Tom Kelliher, CS 220
Oct. 24, 2011
SPIM programs due Wednesday. Refer to homework handout for what to turn in, and how.
Continuation of the function call mechanism.
Consider the following code fragment:
int min(int, int);
int factorial(int);
void main(void)
{
int i = 3;
int j = 5;
int k;
k = min(i + 3, j);
/* Current values of i, j, k? */
k = factorial(5);
}
int min(int a, int b)
{
int i;
if (a < b)
i = a;
else
i = b;
b = 0;
return i;
}
int factorial(int n)
{
if (n <= 1)
return 1;
else
return n * factorial(n - 1);
}
Notes:
factorial() managed?
Scratch-pad storage on top of the stack.
factorial uses $s0 and has a local
copy of n (inefficient).
Example code snippets:
lw $s0, 4($fp) sw $s0, -12($fp) sw $s0, 0($sp) sub $sp, $sp, 4
$v[0-1] -- function results.
$a[0-3] -- function arguments.
$t[0-9] -- caller save registers. (Except for two global
pointers, ignore these registers.)
$s[0-7] -- callee save registers.
$sp -- top of stack pointer, pointing to first free
location on execution stack.
$fp -- frame pointer, pointing to base of current frame.
$ra -- return address.
Program parameters.
$ra (among others) and executes
jr $ra.
Use this sequence. Assume caller is calling callee.
Caller:
(C: last pushed first.)
jal callee
Callee:
$sp.
Number of words needed:
$fp and $ra.
$sn register used.
$fp, $ra, callee save), using
$sp as base register.
$fp by adding frame size to $sp and storing.
$fp as
base register.
$vn registers.
$sp as base register.
$sp.
jr $ra
Caller:
#include <stdio.h>
int getnum(void);
int factorial(int n);
int main()
{
int value;
printf("Enter 0 to exit.\n");
value = getnum();
while (value != 0)
{
printf("Factorial: %d\n", factorial(value));
value = getnum();
}
return 0;
}
int getnum(void)
{
int num;
printf("Number: ");
scanf("%d", &num);
return num;
}
int factorial(int n)
{
if (n <= 1)
return 1;
return n * factorial(n - 1);
}
# factorial.s --- A recursive SPIM program. Demonstrates function
# call and return.
.data
prompt: .asciiz "Number: "
nl: .asciiz "\n"
instr: .asciiz "Enter 0 to exit.\n"
response: .asciiz "Factorial: "
######################################################################
# main
######################################################################
.text
.globl main
main:
sub $sp, $sp, 16 # Push frame & save registers.
sw $fp, 16($sp)
sw $ra, 12($sp)
sw $s0, 8($sp)
add $fp, $sp, 16
li $v0, 4 # Print instruction.
la $a0, instr
syscall
jal getnum # Get a number from keyboard.
sw $v0, -12($fp) # Store locally.
move $s0, $v0
while1:
beqz $s0, endwhile1 # Compute until 0 entered.
li $v0, 4 # Print response prompt.
la $a0, response
syscall
move $a0, $s0 # Pass argument
jal factorial # Call
move $a0, $v0 # Copy return value to print it
li $v0, 1
syscall
li $v0, 4 # Prepare to read next number.
la $a0, nl
syscall
jal getnum
sw $v0, -12($fp) # Store number just read.
move $s0, $v0
b while1
endwhile1:
lw $s0, 8($sp) # Restore registers and pop frame.
lw $ra, 12($sp)
lw $fp, 16($sp)
add $sp, $sp, 16
li $v0, 0
jr $ra # Exit.
######################################################################
# getnum --- returns integer read from keyboard through $v0.
######################################################################
.text
getnum:
sub $sp, $sp, 12 # push frame & save registers.
sw $fp, 12($sp)
sw $ra, 8($sp)
add $fp, $sp, 12
li $v0, 4
la $a0, prompt
syscall
li $v0, 5
syscall # Value read left in $v0.
sw $v0, -8($fp)
lw $ra, 8($sp) # restore registers & pop frame.
lw $fp, 12($sp)
add $sp, $sp, 12
jr $ra
######################################################################
# factorial --- compute factorial of $a0 and return result through
# $v0
######################################################################
.text
factorial:
sub $sp, $sp, 16
sw $fp, 16($sp)
sw $ra, 12($sp)
sw $s0, 8($sp)
add $fp, $sp, 16
sw $a0, -12($fp) # Our value n.
move $s0, $a0
bgt $s0, 1, recurse # Base case. Return 1
li $v0, 1
lw $s0, 8($sp)
lw $ra, 12($sp)
lw $fp, 16($sp)
add $sp, $sp, 16
jr $ra
recurse: sub $a0, $a0, 1 # Recursive call. Compute (n-1)!
jal factorial
mul $v0, $v0, $s0 # n * (n-1)!
lw $s0, 8($sp)
lw $ra, 12($sp)
lw $fp, 16($sp)
add $sp, $sp, 16
jr $ra
gcd() function for
gcd.c/gcd.s.
Notes for exercise:
i and j are in $a0 and
$a1 as gcd() begins
execution. How long can you leave them there?
$s0 and $s1? Why?