Every call gets its own frame
A recursive function calls itself — so each call needs its *own* saved ra and its own copy of n. That's exactly what the stack gives you: every call pushes a stack frame and pops it on return. It's the same stack that overflows when JavaScript recursion goes too deep.
# fib(a0 = n) -> a0 = fib(n), with fib(0) = 0 and fib(1) = 1 fib: li t0, 2 blt a0, t0, base # n < 2: the answer is n itself addi sp, sp, -12 sw ra, 8(sp) sw s0, 4(sp) sw s1, 0(sp) mv s0, a0 addi a0, s0, -1 call fib # fib(n - 1) mv s1, a0 addi a0, s0, -2 call fib # fib(n - 2) add a0, a0, s1 lw s1, 0(sp) lw s0, 4(sp) lw ra, 8(sp) addi sp, sp, 12 base: ret
What goes in fib's frame
Each fib call needs three things after its first recursive call returns: its own return address, its own n (to compute n − 2), and the result of fib(n − 1) while it computes fib(n − 2). That's three words, 12 bytes. Every active call has its own 12-byte frame, so nothing one call saves is overwritten by another.
fib: li t0, 2 blt a0, t0, base addi sp, sp, -12 sw ra, 8(sp) sw s0, 4(sp) sw s1, 0(sp) mv s0, a0 # n addi a0, s0, -1 call fib # fib(n - 1) mv s1, a0 addi a0, s0, -2 call fib # fib(n - 2) add a0, a0, s1 lw s1, 0(sp) lw s0, 4(sp) lw ra, 8(sp) addi sp, sp, 12 base: ret