Stack arguments and a stack frame

Two arguments pushed by the caller, a local that survives a call inside a frame fp points at, and a subroutine that gives back the register it borrowed.

sum_of_squares(a, b) takes its two arguments on the stack, calls a second subroutine twice to square them, and returns their sum in a0. It needs a local variable to hold the first square while the second call runs, and that local lives on the stack too, in a frame the subroutine builds for itself.

A subroutine with its arguments in registers passed everything in a0 and a1 and kept nothing. That works until a subroutine has to hold something across a call, because there is only one ra and the call is free to destroy any temporary it likes.

You need to know: the "The stack and sp" lecture and the "jal, ret and the calling convention" lecture. What is new here is fp as a frame pointer, it stays still while sp keeps moving, so 0(fp) names the same argument from the first instruction to the last.

There is no link here and no unlk, and no push and no pop either. The M68K builds and takes down a frame with one instruction each; on RISC-V the prologue is an addi that moves sp down and a sw for everything the subroutine promised to give back, and the epilogue is the same lines the other way round.

While the second jal square is running, the stack looks like this, with 🟢 on the stack pointer:

addressvaluereached aswhat it is
0x7FFFEFDC🟢 000000090(sp)the local, a * a
0x7FFFEFE0000000004(sp)room the frame asked for and did not use
0x7FFFEFE4000000008(sp)the caller's fp
0x7FFFEFE80040007012(sp)the return address into main
0x7FFFEFEC000000030(fp)a
0x7FFFEFF0000000044(fp)b

Type 7FFFEFD0 in the memory panel after running and those words are still lying there, since popping moves a pointer and erases nothing.

Both frames are sixteen bytes for two or three words, because the ABI asks the stack pointer to move in multiples of 16 and sp starts at 0x7FFFEFFC, so every frame keeps the alignment the one before it had. The 4(sp) in the middle of the table is room nothing was put in.

The arguments are the two words the caller pushed and nothing sits between them and the frame, because jal pushed no return address: sum_of_squares saved its own. addi fp, sp, 16 is what makes them reachable by a name that does not move, and fp is s0, a saved register, so the caller's copy goes on the stack first.

square keeps to a smaller agreement of its own. It borrows s1, which is a saved register, so it puts the caller's value back before returning; t0 in main it destroys freely, and that is why main reads the answer out of a0 and not out of anything it was holding.

a0 and s2 both come out at 00000019, which is 25, from 9 plus 16.

Try changing addi sp, sp, 16 in main to addi sp, sp, 8. The answer is still right, and sp ends at 7FFFEFF4 instead of 7FFFEFFC: eight bytes of stack the program will never get back, which in a loop is how a program runs out of it.