Functions calling functions
call f jumps to f and puts the return address in ra; ret jumps back to it. But if f calls another function, ra gets overwritten — and so might the registers you were using. The calling convention is the agreement that makes this safe.
- Arguments in a0–a7, results in a0/a1.
- t and a registers may be changed by any call (caller-saved).
- s registers, sp and your ra must look unchanged when you return (callee-saved) — save them on the stack first.
A function that calls another
square: mul a0, a0, a0 ret # sum_squares(a0 = x, a1 = y) -> x*x + y*y sum_squares: addi sp, sp, -12 # make room for three words sw ra, 8(sp) # our return address — call will overwrite ra sw s0, 4(sp) # s registers belong to our caller: save them sw s1, 0(sp) mv s0, a1 # keep y safe across the call call square # a0 = x*x mv s1, a0 mv a0, s0 call square # a0 = y*y add a0, a0, s1 lw s1, 0(sp) # restore everything we saved… lw s0, 4(sp) lw ra, 8(sp) addi sp, sp, 12 # …and give the stack space back ret