leaningtech
Published in

leaningtech

Fantastic tail calls, and how to implement them.

Think about your memory: recycle your frame!

Let’s refresh our memory…

  • The caller: factorial 0, the first instance of factorial.
  • The callee: factorial 1, the first tail-recursive call to factorial.
push $arg0          \
push $arg... | caller’s frame (factorial 0)
push $argN |
call $factorial /
push %rbp \
mov %rsp, %rbp | callee’s frame (factorial 1)
push $arg0           \
push $arg... | caller’s frame (factorial 0)
push $argN |
call $factorial /
push %rbp \
mov %rsp, %rbp |
... | callee’s frame (factorial 1)
mov %rbp, %rsp |
pop %rbp |
ret /
push $arg0    \
push $arg... | caller’s frame (factorial 0)
push $argN |
call fib /
push %rbp \
mov %rsp, %rbp |
... | callee’s frame (factorial 1)
mov %rbp, %rsp |
pop %rbp |
ret /
mov %rbp, %rsp \
pop %rbp | caller’s frame (factorial 0)
ret /

How it can be done in WebAssembly: return_call

Figure 1. The stack at C (factorial 1) call site.
Figure 2. The stack after rearrangement, before tail calling into C (factorial 1).
Figure 3. Comparison of the stack in a deeply recursive routine.

Arguments vs. Return values

Figure 4. Stack at B call site. A reserved space for B’s return values
Figure 5. Stack at different stages of the A -> B -> C tail call chain. C’s number of arguments modified the stack size of A. Even if the return count was preserved with C’s signature, A expects the return values at incorrect offsets when C returns.
Figure 6. Same depiction as in Fig. 5, with offsets that allow A to properly collect return values.

The next steps

--

--

Leaning Technologies' Blog - everything WebVM, Cheerp, CheerpJ, CheerpX, compile-to-WebAssembly and WebAssembly virtualization

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store