jal, jr and the calling convention

jal puts the return address in a register instead of on the stack, which is cheap until a subroutine calls something. The arguments in $a0 to $a3, the answer in $v0, who has to preserve what, and a recursive program drawn frame by frame.

jal label writes the address of the next instruction into $ra and jumps to the label. jr $ra jumps back to it. That pair is the whole of calling and returning on MIPS, and it touches no memory at all: on the M68K a bsr pushes the return address and an rts pops it, and here it goes in a register.

A subroutine that calls nothing

The agreement in this course is the standard one: arguments arrive in $a0 to $a3, the answer leaves in $v0.

$s0 comes out at 30. Step through it and watch $ra: it is 0 until the jal, then 00400008, the address of the move that follows the call, and the jr $ra puts the pc back there.

The li $v0, 10 and syscall before triple: are what stop the program. Take them out and it walks into the subroutine, runs it, and the jr $ra jumps back to the move it has already done, round and round until the Playground gives up.

triple is a leaf: it calls nothing, so $ra is safe for as long as it runs and the subroutine needs no stack, no saving and no prologue. Most small subroutines are leaves, and that is what makes a register return address worth having.

There is only one $ra

A subroutine that calls something else has a problem: the jal inside it overwrites $ra with a new return address, and the one it needed is gone. So a subroutine that is not a leaf saves $ra on the stack on the way in and loads it back on the way out.

$s0 comes out at 20. Take the two $ra lines out and run it again: the second jal doubled overwrites $ra with an address inside quadruple, so the jr $ra at the end returns into the middle of quadruple instead of into main, and the program loops.

Those four lines around the body are the prologue and the epilogue, and they are what a C compiler writes for every function that calls another one.

Who preserves what

The convention names two groups of registers, and the whole point of the split is that a caller and a subroutine written by two different people still work together.

registerswho keeps them
$t0 to $t9, $a0 to $a3, $v0, $v1the caller, if it still wants them
$s0 to $s7, $sp, $fp, $rathe subroutine, before it returns

So a subroutine may write any $t register it likes without telling anyone, and must put back any $s register it touches. A caller holding something in $t3 across a call has to save it itself.

$t1 comes out at 999 and $s1 at 222. The subroutine destroyed the caller's $t0 and was entitled to; it destroyed the caller's $s0 too, and put it back, which is why main still has its 222.

Nothing in the machine enforces any of this. It is what the comment above the label says, and the reason to follow the standard one rather than invent your own is that every other MIPS program does.

Arguments past the fourth

Four registers hold four arguments. A fifth goes on the stack, and the caller puts it there and takes it back off.

$s0 comes out at 21, which is 1 to 6 added up. While sum6 runs, $sp is at 0x7FFFEFF4 and the stack holds:

addressvaluereached aswhat it is
0x7FFFEFF4🟢 000000050($sp)e
0x7FFFEFF8000000064($sp)f

jal pushes nothing, which is why 0($sp) inside the subroutine is the argument and not a return address. The catch is that $sp moves: push anything inside sum6 and every offset above changes, and that is what $fp exists for. move $fp, $sp at the top of a subroutine gives you a pointer that stays still while $sp moves, and then the arguments are at 0($fp) and 4($fp) whatever else the subroutine does. $fp is a saved register, so a subroutine that uses it saves the caller's copy first.

Recursion needs nothing new

A subroutine that calls itself gets a fresh frame at a fresh address every time, because every prologue subtracts from wherever $sp happens to be. The same 0($sp) in the source is a different address in every call.

$s0 comes out at 00000078, which is 120. At the deepest point, with $a0 down to 1, the stack holds five frames of eight bytes each:

addressvaluewhat it is
0x7FFFEFD4🟢 00000001n of the innermost call
0x7FFFEFD800400034its return address, inside factorial
0x7FFFEFDC00000002n of the call before it
0x7FFFEFE000400034
0x7FFFEFE400000003
0x7FFFEFE800400034
0x7FFFEFEC00000004
0x7FFFEFF000400034
0x7FFFEFF400000005n of the first call
0x7FFFEFF800400008its return address, inside main

Four of the five return addresses are the same 00400034, the lw $a0, 0($sp) after the recursive jal, and the outermost one points into main. Nothing had to be reserved and nothing had to be named: the stack pointer chose all ten addresses.

The lw $a0, 0($sp) after the call is there because $a0 is a caller-saved register and the recursive call destroyed it. Saving it in the prologue and reloading it afterwards is this subroutine being its own caller.

The editor's Call stack tab lists the calls that are open, which for a run stopped part way through this program is factorial five times over.

Calling an address

jalr $t0 jumps to the address in $t0 and writes the return address into $ra, which is how a program calls a function pointer or a routine out of a table. la $t0, triple and jalr $t0 do what jal triple does, with the address worked out while the program runs.

Your turn

Write a subroutine called with jal that squares the number in $a0 and leaves the answer in $v0, and a caller that copies that answer into $s0 before ending the program. The test starts $a0 at 7, so $s0 comes out at 49.

The copy is not busywork. $v0 is both the register a subroutine answers in and the register the syscall number goes in, so the li $v0, 10 that ends a program destroys the answer, and anything you still want has to be moved out of $v0 first.

Show solution

The second one hands you a caller that calls add_two with 20 and 22 on the stack. Write the body, which must leave 42 in $v0 without moving $sp.

Show solution