(see PDF notes)
Consider this C procedure add2
int add2(int x, int y) {
return x + y;
}
Translate this procedure call to MIPS assembly, assuming f is in $s0
int f = add2(3, 4);
Solution:
li $a0, 3
li $a1, 4
jal add2
move $s0, $v0
Now translate the add2 procedure itself to MIPS assembly. Solution:
add2:
add $v0, $a0, $a1
jr $ra
Translate this procedure to MIPS assembly:
int max(int x, int y) {
if (x > y) {
return x;
} else {
return y;
}
}
Solution:
max:
ble $a0, $a1, else # If x is not greater than y, go to else
move $v0, $a0 # We are going to return x
j exit # Go to the procedure exit
else:
move $v0, $a1 # We are going to return y
# Fall through to the procedure exit
exit:
jr $ra # Return from the procedure
And also translate this call to MIPS assembly, assuming f is stored in $s0.
int f = max(5, 15);
Solution:
li $a0, 5
li $a1, 15
jal max
move $s0, $v0
Now let’s look at this non-leaf procedure:
int max3(int x, int y, int z) {
return max(x, max(y, z));
}
Let’s translate the procedure. Solution:
max3:
addi $sp, $sp, -4
sw $ra, 0($sp)
jal max
move $a0, $v0
move $a1, $a2
jal max
lw $ra, 0($sp)
addi $sp, $sp, 4
jr $ra
Work with your groups to translate this non-leaf procedure. It happens to be recursive, but all the same rules still apply!
int fib(int n) {
if (n < 2) {
return n;
} else {
return fib(n-1) + fib(n-2);
}
}
Solution:
fib:
slti $t0, $a0, 2 # Is n less than 2?
beq $t0, $zero, fib_else # If n is not less than 2, go to else
# base case: return n
move $v0, $a0 # Put n in the return register
jr $ra # Return
fib_else:
addi $sp, $sp, -12 # Make space to save ra, n, and fib(n-1)
sw $ra, 8($sp) # Save the return address
sw $a0, 4($sp) # Save n
# recursive case
addi $a0, $a0, -1 # Compute n-1
jal fib # Call fib(n-1)
sw $v0, 0($sp) # Save the result of fib(n-1) on the stack
lw $a0, 4($sp) # Load n from the stack
addi $a0, $a0, -2 # Compute n-2
jal fib # Call fib(n-2)
lw $t0, 0($sp) # Load the result of fib(n-1) from the stack
add $v0, $v0, $t0 # Compute fib(n-1) + fib(n-2)
lw $ra, 8($sp) # Load the return address from the stack
addi $sp, $sp, 12 # Restore the stack
jr $ra # Return
Consider this simple recursive procedure:
int termial(unsigned int n) {
if(n == 0) {
return 0;
} else {
return n + termial(n-1);
}
}
We can rewrite termial to use a helper procedure:
int termial_helper(int n, int sum_so_far) {
if (n == 0) {
return sum_so_far;
} else {
return termial_helper(n-1, n + sum_so_far);
}
}
int termial(int n) {
return termial_helper(n, 0);
}
We can take advantage of the fact that termial_helper is tail-recursive and does not need to return back through each level of recursion; it would be fine if the last level of recursion (the base case) returned all the way back to the original caller. This is something we can do in assembly.
Remember that we use jal to remember where we must return, and jr $ra to return there. If we don’t ever need to come back after a call, we can just use j instead. That will leave $ra unmodified, so we can also skip saving and restoring that register.
termial_helper:
# If n is not zero, go to the else case
bne $a0, $zero, termial_helper_else
# Return sum_so_far
move $v0, $a1
jr $ra
termial_helper_else:
add $a1, $a1, $a0 # Add n to sum_so_far
addi $a0, $a0, -1 # Subtract 1 from n
j termial_helper # Make our recursive tail call