Open Johan-Mi opened 10 months ago
(just in case an idea is helpful:) You could implicitly declare a list and use that as a stack to pass return values. So the code above code would de-sugar like:
let n = f(1, 2) + f(3, 4)
# =>
block f(a, b) {
push(_stack, a + b)
}
f(1, 2)
f(3, 4)
let n = get(_stack, len(_stack) - 1) + get(_stack, len(_stack))
pop(_stack)
pop(_stack)
And then I think you get recursion for free, because each function cleans up after all its internal calls like the native call-stack,
fn fib(_ n: Num) -> Num {
if n < 2 {
n
} else {
fib(n - 1) + fib(n - 2)
}
}
let a = f(3)
# =>
block fib(n) {
if n < 2 {
push(_stack, n)
} else {
fib(n - 1)
fib(n - 2)
# clobbering here is fine because it's never held across a call, storing it just makes the indices easier to think about
let _temp = get(_stack, len(_stack) - 1) + get(_stack, len(_stack))
pop(_stack)
pop(_stack)
push(_stack, n)
}
}
fib(3)
let a = get(_stack, len(_stack))
pop(_stack)
Presumably there's a performance cost over just using a variable for functions that are only ever called once in an expression but perhaps an optimisation could notice that case and switch calling conventions when possible.
One idea would be to use variables by default, perform a flattening step to put all sub-expressions into variables (that might get optimized away later if deemed unnecessary) and then do liveness analysis to detect clobbering and switch to the stack approach.
Another problem: what if two scripts call a function at the same time? I really don't know what to do about that.
The MIR-based codegen has unintentionally fixed non-recursive non-parallel clobbering since it currently puts every expression in a new variable.
This will, however, break again when I implement linearity analysis.
Here's a really dumb idea: custom block parameters can't be clobbered, so the compiler could use a Lisp-like implementation of let
that desugars variables into immediately invoked functions:
(let ((a 1))
(use a))
; =>
((lambda (a) (use a)) 1)
If an expression contains multiple calls to the same function, the return variable gets clobbered:
Then there's recursion but that's too much of a pain for me to even think about.