Tail Call Optimization¶
Current Status (2026-01-26)¶
Detection: ✅ Implemented Transformation: 🚧 Planned
The Onion compiler now includes a tail call detection system that identifies tail-recursive methods during compilation.
How It Works¶
Detection Phase¶
The compiler analyzes methods to identify tail-recursive calls:
- Tail Position Analysis: Checks if the last statement (or statements in control flow branches) is a self-call
- Recursive Search: Searches through
StatementBlockandIfStatementnodes to find tail calls - Method Matching: Verifies that the call target matches the current method (same name, class, and parameter types)
Supported Patterns¶
The detector recognizes tail recursion in:
- Direct tail calls:
return method(args) - Conditional branches: Both
thenandelsebranches of if statements - Nested blocks: Recursive search through statement blocks
Example¶
def factorial(n: Int, acc: Int): Int {
if (n <= 1) {
return acc
}
return factorial(n - 1, n * acc) // ✅ Detected as tail call
}
Viewing Detected Tail Calls¶
Compile with the --verbose flag to see which methods are tail-recursive:
Output:
[TCO] Detected tail-recursive method: YourClass.factorial
[TCO] Note: Tail call optimization is not yet fully implemented
Implementation Details¶
File Location¶
- Source:
src/main/scala/onion/compiler/optimization/TailCallOptimization.scala - Pipeline Integration: Added between
TypingandCodeGenerationphases
Compiler Pipeline¶
Parsing → Rewriting → Typing → [TailCallOptimization] → MutualRecursionOptimization → TypedAstCodeGeneration
Future Work¶
Planned Transformations¶
The transformation phase (converting tail recursion to loops) requires:
- Local Variable Allocation: Properly allocating temporary variables using
LocalFrame - Parameter Rewriting: Converting parameter references to temporary variable references throughout the method body
- Loop Construction: Wrapping the method body in a
while(true)loop - Tail Call Replacement: Replacing tail calls with variable assignments + continue
Example Transformation (Planned)¶
// Original
def factorial(n: Int, acc: Int): Int {
if (n <= 1) {
return acc
}
return factorial(n - 1, n * acc)
}
Would be transformed to (conceptually):
def factorial(n: Int, acc: Int): Int {
var n_temp: Int = n
var acc_temp: Int = acc
while (true) {
if (n_temp <= 1) {
return acc_temp
}
val n_next = n_temp - 1
val acc_next = n_temp * acc_temp
n_temp = n_next
acc_temp = acc_next
// loop continues
}
}
Testing¶
Test files are located in src/test/run/:
- tail_recursion_factorial.on - Factorial with tail recursion
- tail_recursion_simple.on - Simple countdown example
- tail_recursion_direct.on - Direct infinite recursion (for testing)
Contributing¶
If you'd like to contribute to implementing the transformation phase:
- Understand
TypedASTnode structure (especiallyLocalFrame,RefLocal,SetLocal) - Study
LocalVarContextinsrc/main/scala/onion/compiler/backend/asm/LocalVarContext.scala - Implement recursive statement rewriting to replace parameter references
- Add comprehensive tests for various tail-recursive patterns
References¶
- Tail Call Optimization (Wikipedia)
- Related implementation: Scala's
@tailrecannotation - Similar optimization in functional languages: Haskell, Scheme, OCaml