Calling convention 2026-10-24
The agreement between a caller and a subroutine on argument and result placement, processor register preservation, and call stack organization. Callee-saved registers retain their incoming values across a call; caller-saved registers must be saved by the caller if their values are still needed afterwards.
Use the conventional integer calling convention:
$a0 holds the argument and $v0 holds the result. The function is recursive, so every call must preserve its own return address and original argument. The first recursive result must also survive the second call; a caller-saved register alone would not protect it.This implementation reserves a 32-byte stack frame, keeping the stack pointer eight-byte aligned. Offsets 0–15 are available as the outgoing argument area, 16 stores the original argument, 20 stores the first result, 24 is padding, and 28 stores the return address. It changes no callee-saved register. Assemble in classic MIPS delayed-branch mode with instruction reordering disabled, such as
.set noreorder.foo:
addiu $sp,$sp,-32 # allocate 32-byte aligned frame
sw $ra,28($sp) # save caller return address
sw $a0,16($sp) # save original n
beq $a0,$zero,foo_base
sll $zero,$zero,0
addiu $t0,$zero,1
beq $a0,$t0,foo_base
sll $zero,$zero,0
addiu $t0,$zero,2
beq $a0,$t0,foo_base
sll $zero,$zero,0
addiu $a0,$a0,-1
jal foo
sll $zero,$zero,0
sw $v0,20($sp) # preserve foo(n-1) across next call
lw $a0,16($sp) # recover original n
sll $zero,$zero,0
addiu $a0,$a0,-3
jal foo
sll $zero,$zero,0
lw $t0,20($sp)
sll $zero,$zero,0
addu $v0,$v0,$t0 # combine the two recursive results
beq $zero,$zero,foo_return
sll $zero,$zero,0
foo_base:
addu $v0,$a0,$zero # base case result is n
foo_return:
lw $ra,28($sp)
sll $zero,$zero,0
addiu $sp,$sp,32 # release the frame
jr $ra
sll $zero,$zero,0All displayed operation mnemonics denote single machine instructions. In particular,
sll $zero,$zero,0 is the actual harmless shift encoding often abbreviated by the pseudoinstruction nop; addu $v0,$a0,$zero performs a copy without the move pseudoinstruction. Each branch or jump has an explicit harmless branch delay slot, and harmless shifts also separate loads from their subsequent uses. The base cases return the original argument; other calls return the sum of the two recursive values. Every return restores the saved return address and exactly undoes the stack allocation.[ref]MIPS32 Architecture For Programmers Volume II by MIPS Technologies. Revision 2.00, ADDIU and ADDU, pp. 37–38; JAL and JR, pp. 139 and 144
Use
r0 for the argument and result. With 32-bit integers and pointers, the three fields have byte offsets 0, 4 and 8. Assume an acyclic finite binary tree, no concurrent mutation, a valid aligned call stack, and no signed integer overflow in any evaluated sum. These are representation and arithmetic assumptions, rather than properties of arbitrary C programming language implementations..syntax unified
.cpu cortex-m0
.thumb
.global sum_rec
.thumb_func
sum_rec:
cmp r0, #0
beq .Lempty
push {r4, r5, r6, lr} @ 16-byte frame; r6 is alignment padding
movs r4, r0 @ retain this invocation's node
ldr r5, [r4, #0] @ running value begins with its label
ldr r0, [r4, #4]
bl sum_rec @ r0 = left subtree sum
adds r5, r5, r0
ldr r0, [r4, #8]
bl sum_rec @ r0 = right subtree sum
adds r0, r0, r5
pop {r4, r5, r6, pc} @ restore caller state and return
.Lempty:
movs r0, #0
bx lrThe node and partial sum are held in callee-saved registers
r4,r5. Each recursive invocation saves its caller's values before replacing them, so a child returns with the parent's values intact. Saving lr preserves the return address across the two bl instructions. Caller-saved registers may be overwritten by a child; no parent value is left live only in them. Pushing four words maintains eight-byte stack alignment at recursive calls.[ref]Procedure Call Standard for the Arm Architecture by ARM Limited. AAPCS32, base standard: core-register roles, stack constraints and result return
Correctness follows by structural induction. An empty subtree returns zero. For a nonempty subtree, assume both recursive calls return their respective label sums. The final addition then combines the node's label with exactly those two sums, so
r0 returns the sum of all labels. The instruction forms and field offsets fit the short load and arithmetic encodings.[ref]Thumb 16-bit instruction set quick reference by Arm. Move, Add, Load, Push/Pop and Branch tables