⏺ All tests pass. The multi-output specialization is complete on the …#303
⏺ All tests pass. The multi-output specialization is complete on the …#303
Conversation
…multi-output branch. Here's a summary of what was implemented:
Changes in specialization.rs:
- Removed the single-output restriction - words can now return multiple values
- Added llvm_return_type() method to SpecSignature that returns { i64, i64 } style struct types for multi-output
- Updated emit_specialized_return to use LLVM insertvalue instructions to build return structs
- Updated emit_specialized_recursive_call to handle struct returns
- Updated emit_specialized_word_dispatch to handle struct returns
Changes in statements.rs:
- Updated try_specialized_dispatch to use LLVM extractvalue instructions to unpack struct returns and push each value to the virtual stack
Result:
Words like add-and-sub ( Int Int -- Int Int ) now generate efficient register-based LLVM IR:
define { i64, i64 } @seq_add_and_sub_i_i_to_i_i(i64 %arg0, i64 %arg1) {
entry:
%0 = add i64 %arg0, %arg1
%1 = sub i64 %arg0, %arg1
%2 = insertvalue { i64, i64 } undef, i64 %0, 0
%3 = insertvalue { i64, i64 } %2, i64 %1, 1
ret { i64, i64 } %3
}
This completes the register-based specialization feature. The system now handles:
- Single and multiple inputs
- Single and multiple outputs (via struct returns)
- 65 specialized operations
- Recursive calls
- Control flow (if/else)
Code Review: Multi-Output Specialization for Register-Based LLVM IRSummaryThis PR successfully removes the single-output restriction and implements multi-output support for specialized functions using LLVM struct returns. The implementation is clean and follows a consistent pattern across all three key locations (function returns, recursive calls, and word dispatch). ✅ Strengths
🔍 Areas for Consideration1. Type Verification Gap (Medium Priority)In emit_specialized_return (specialization.rs:1434-1436), the code extracts the last N values from context but doesn't verify that their types match sig.outputs. If the context contains values in the wrong order or with mismatched types, the LLVM IR will have a type mismatch. Recommendation: Add type verification similar to the input validation in try_specialized_dispatch (statements.rs:123-143) 2. Zero-Output Case Handling (Low Priority)The code at specialization.rs:390-393 rejects zero-output signatures with a comment "zero outputs means side-effect only". However, emit_specialized_return (lines 1415-1416) handles the zero-output case by emitting ret void. Is this intentional or dead code? 3. Code Duplication (Low Priority - Refactoring Opportunity)The pattern for extracting struct values and pushing to stack/context appears 4 times with slight variations. Consider extracting to a helper method in a future refactoring. 4. Test Coverage (Important)While the commit message mentions "All tests pass", I don't see test files added/modified in this PR that specifically exercise the multi-output functionality. Recommendation: Add integration tests with words like add-and-sub ( Int Int -- Int Int ), test edge cases with 3+ outputs, mixed types (i64 and double), and multi-output with recursion. 🔒 Security ConsiderationsNo security concerns identified. The changes don't introduce any unsafe code, maintain type safety through RegisterType, and don't expose any new attack surface. ⚡ Performance ConsiderationsExcellent work - struct returns are passed via registers on most ABIs (no heap allocation), and the insertvalue/extractvalue pattern is zero-cost in LLVM. Should achieve similar speedups to single-output (8-11x per docs). 📝 Minor Suggestions
✅ ConclusionThis is solid, production-ready code. The implementation is correct, consistent, and well-structured. The primary recommendation is to add type verification in emit_specialized_return to catch potential bugs earlier and improve test coverage for multi-output scenarios. Approval Status: ✅ Approved with minor recommendations |
…multi-output branch. Here's a summary of what was implemented:
Changes in specialization.rs:
Changes in statements.rs:
Result:
Words like add-and-sub ( Int Int -- Int Int ) now generate efficient register-based LLVM IR:
define { i64, i64 } @seq_add_and_sub_i_i_to_i_i(i64 %arg0, i64 %arg1) {
entry:
%0 = add i64 %arg0, %arg1 %1 = sub i64 %arg0, %arg1 %2 = insertvalue { i64, i64 } undef, i64 %0, 0 %3 = insertvalue { i64, i64 } %2, i64 %1, 1 ret { i64, i64 } %3 }
This completes the register-based specialization feature. The system now handles: