Methods in MIPS - Recursive Factorial and Stack Trace

advertisement
Computer Architecture CSC 304
Shai Simonson
MIPS Method Calls, Stack Frames, and Parameter Conventions
Every method should do the following:
1.
2.
3.
4.
Copy input parameters from registers $4-$7, and if necessary off the stack.
Allocate (i.e., push) space for stack frame (aka. activation record). The system stack ($sp) grows into
smaller memory locations, so allocation is done with subtraction. Size is dependent on the number of local
variables, parameters, and callee –saved registers that will be saved.
Push callee registers (aka. preserved registers) onto the stack, if you intend to use these registers in your
method. You will need to restore these registers to their original values before you return to your caller.
Callee-registers include the “s” registers $16-$23 $30, and $ra and $fp.
Set $fp (frame pointer) to $sp + frame size, and put local variables on stack relative to the frame pointer.
Whenever your method invokes another method:
a. Pass first 4 parameters in registers $4-$7 ($a0-$a3), and the rest on the stack.
b. Save caller-saved registers (aka. temporary registers) on the stack, if you expect to use their current
values after the invoked method returns. These include the “a” registers and “t” registers: $4-$15, and
$24-$25.
c. jal label - This stores the address of next line of your method (the return address) in $ra, and jumps to
the address of the method you are invoking, i.e. label.
d. Restore caller registers (if necessary), extract output parameters from the stack, and any return value(s)
from $2 and $3.
5.
6.
7.
8.
Put method’s return value(s) (if any) in $2 and $3.
Restore callee registers (if necessary).
Pop stack frame by adding frame size to $sp.
jr $31 – This jumps back to the line following the jal call in the caller that invoked your method.
A typical stack frame is shown below, where the lower addresses are at the bottom and the stack
is growing in that direction. The stack frame has just been created and so the stack pointer has
just grown by “frame-size” bytes. The frame pointer ($fp) sits at the top of the frame where the
$sp used to be, and is used as a fixed point to reference local variables. This is especially
important in recursive methods. The stack pointer ($sp) sits at the bottom of the frame and may
grow and shrink during the execution of your method as it invokes other methods.
incoming parameters from caller
.
…
$fp  local variables
(This was the old $sp location)
…
saved registers
…
parameter 6
parameter 5
$sp 
Example of Recursive Method with Stack Trace Shown in Class
This is a simple recursive factorial method in C++. Note that it is not tail recursive because
when the recursion returns, the multiplication by n still needs to occur, so the stack will be used
at that point. The equivalent MIPS version appears below the C++. We will do a complete stack
trace in class.
cout << “The factorial of 3is “ << fact(3);
int fact (int n)
{
if (n < 1) return 1;
else return (n* fact(n -1));
}
.data
.asciiz “The factorial of 3 is”
str:
.text
…
calling_method:
addiu $sp, $sp, -32
sw $ra, 20($sp)
sw $fp, 16($sp)
addiu $fp, $sp, 32
li $4, 3
jal fact
A:
# Create the stack frame and save $sp and $fp
# input parameter
# invoke recursive method fact
move $t0, $2 # extract fact method’s return value
# this must be done right now, otherwise
# $2 will be lost because the syscall uses the register
la $a0, str
li $v0, 4
syscall
# print output message
move $a0, $t0
li $v0, 1
syscall
# put the saved output into $4
lw $ra, 20($sp)
lw $fp, 16($sp)
addiu $sp, $sp, 32
jr $ra
# restore $ra
# restore $fp
# pop the stack frame
# return to its caller
# print fact(3), i.e. 6
fact:
recurse:
B:
addiu $sp, $sp, -32
sw $ra, 20($sp)
sw $fp, 16($sp)
addiu $fp, $sp, 32
sw $4, 0($fp)
# Create the stack frame and save $sp and $fp
lw $$t0, 0($fp)
bgtz $t0, recurse
li $2, 1
b close_up
# This loads n in $t0 and checks for the base case.
lw $t1, 0($fp)
addiu $4, $t1, -1
# This computes n-1, using temp register $t1
# and moves n-1 to $4.
# $t0 and $t1 (caller-saved registers) are not saved
# on the stack because the caller does not plan to
# use their values after the call returns
jal fact
# Recursive call
lw $t0, 0($fp)
mul $2, $2, $t0
# Loads n in $t0, and
# Multiplies fact(n-1) by n, and
# Stores result in output register $2.
# This is the local variable n
# If there were other local variables they would
# be at -4($fp), -8($fp), etc.
# 0! = 1
# The previous two lines show that the method is not tail recursive
close_up:
lw $ra, 20($sp)
lw $fp, 16($sp)
addiu $sp, $sp, 32
jr $ra
# restore $ra
# restore $fp
# pop the stack frame
# return to the caller
Trace of CallingMethod for Fact(3)
$sp
$fp
Incoming
$ra
$4
Original
CallingMethod
Fact(3)
1000
968
936
4000
1000
968
-2000
A
--3
Fact(2)
904
936
B
2
Fact(1)
872
904
B
1
Fact(0)
840
872
B
0
--3
2
2
1
1
0
1
B
B
A
2000
--
0
0
0
0
0
1
2
6
6
6
$2
Now it starts popping the stack
Fact(1)
Fact(2)
Fact(3)
CallingMethod
Original
872
904
936
968
1000
904
936
968
1000
4000
CallingMethod
1000
Fact(3)
988
984
968
2000
4000
3
Fact(2)
956
952
936
A
1000
2
Fact(1)
924
920
904
B
968
1
Fact(0)
892
888
872
B
936
0
860
856
840
B
904
Download