lecture08 assembly language
TRANSCRIPT
COMPUTER ORGANIZATION
AND ASSEMBLY LANGUAGE
Lecture 8
Functions and Recursive function calls in
MIPS
What have we learned
So far
Write any program in MIPS
Today
More Examples of Function Calls
2
Steps for Making a Function Call
1) Save necessary values onto stack
2) Assign argument(s), if any
3) jal call
4) Restore values from stack
3
Structure of a Function
1) Save necessary values onto stack
2) Function body
3) Set return value(s) in $v0 (and $v1), if any
4) Restore values from stack
5) jr $ra
4
Example Function
sumSquare:
addi $sp,$sp,-8 # space on stack
sw $ra, 4($sp) # save ret addr
sw $a1, 0($sp) # save y
add $a1,$a0,$zero # mult(x,x)
jal mult # call mult
lw $a1, 0($sp) # restore y
add $v0,$v0,$a1 # mult()+y
lw $ra, 4($sp) # get ret addr
addi $sp,$sp,8 # restore stack
jr $ra
mult: ...
int sumSquare(int x, int y) {
return mult(x,x)+ y;
}
“push”
“pop”
5
Rules for Function Calls
Called with a jal instruction, returns with a jr $ra
Accepts up to 4 arguments in $a0, $a1, $a2 and $a3
Return value is always in $v0 (and if necessary in
$v1)
Must follow register conventions
even in functions that only you will call!
6
Other Registers
$at: may be used by the assembler at any time;
unsafe to use
$k0-$k1: may be used by the OS at any time; unsafe
to use
$gp, $fp: don’t worry about them
Feel free to read up on $gp and $fp in Appendix A, but you
can write perfectly good MIPS code without them.
7
Basic Structure of a Function
entry_label:addi $sp,$sp, -framesizesw $ra, framesize-4($sp) # save $rasave other regs if need be
...
restore other regs if need belw $ra, framesize-4($sp) # restore $raaddi $sp,$sp, framesizejr $ra
Epilogue
Prologue
Body (call other functions…)
ra
memory
8
Register Conventions – Saved Registers
$0: No Change. Always 0.
$s0-$s7: Restore if you change. Very important, that’s
why they’re called saved registers. If the callee changes
these in any way, it must restore the original values before
returning.
$sp: Restore if you change. The stack pointer must point
to the same place before and after the jal call, or else the
caller won’t be able to restore values from the stack.
HINT -- All saved registers start with S!
9
Register Conventions – Volatile
Registers
$ra: Can Change. The jal call itself will change this
register. Caller needs to save on stack if nested call.
$v0-$v1: Can Change. These will contain the new
returned values.
$a0-$a3: Can change. These are volatile argument
registers. Caller needs to save if they’ll need them after the
call.
$t0-$t9: Can change. That’s why they’re called
temporary: any procedure may change them at any time.
Caller needs to save if they’ll need them afterwards.
10
Register Conventions
What do these conventions mean?
If function R calls function E, then function R must
save any temporary registers that it may be using
onto the stack before making a jal call.
Function E must save any S (saved) registers it
intends to use before garbling up their values
Remember: CalleR/calleE need to save only
temporary/saved registers they are using, not all
registers.
11
Requirements for Functions
Pass arguments to the function
$a0, $a1, $a2, $a3
Get results from the function
$v0, $v1
Can call from anywhere
jal
Can always return back
jr
Nested and Recursive Functions
Save $ra on stack
Saving and Restoring Registers
Register Conventions
Functions with more than 4 parameters
Pass them on the stack
12
Nested Procedures
Leaf procedures do not call other procedures.
What happens to return addresses with nested procedures?
int rt_1 (int i) {
if (i == 0) return 0;
elsereturn rt_2(i-1);
}
caller: jal rt_1
next: . . .
rt_1: bne $a0, $zero, to_2add $v0, $zero, $zerojr $ra
to_2: addi $a0, $a0, -1jal rt_2jr $ra
rt_2: . . .
13
Nested Procedures Outcome
caller: jal rt_1next: . . .
rt_1: bne $a0, $zero, to_2add $v0, $zero, $zerojr $ra
to_2: addi $a0, $a0, -1jal rt_2jr $ra
rt_2: . . .
• On the call to rt_1, the return address (next in the caller routine) gets stored in $ra. What happens to the value in $ra (when i != 0) when rt_1 makes a call to rt_2?
int rt_1 (int i)
{
if (i == 0)
return 0;
else
return rt_2(i-1);
}
14
caller rt addr
caller rt addr
Saving the Return Address, Part
1Nested procedures (i passed in $a0, return value in $v0)
high addr
$sp
low addr
$a0 value
$rart_2
old TOS
rt_1: bne $a0, $zero, to_2
add $v0, $zero, $zero
jr $ra
to_2: addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
addi $a0, $a0, -1
jal rt_2
rt_2: lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
jr $ra
• Save the return address (and arguments) on the stack
int rt_1 (int i)
{
if (i == 0)
return 0;
else
return rt_2(i-1);
}
$a0
$rt_2 $a0 value
$a0 value $a0 value - 1
15
Compiling a Recursive Procedure
Calculating factorial:int fact (int n) {if (n < 1) return 1;else return (n * fact (n-1));
}
Recursive procedure (one that calls itself!)
fact (0) = 1
fact (1) = 1 * 1 = 1
fact (2) = 2 * 1 * 1 = 2
fact (3) = 3 * 2 * 1 * 1 = 6
fact (4) = 4 * 3 * 2 * 1 * 1 = 24
. . .
Assume n is passed in $a0; result returned in $v0
16
Compiling a Recursive Procedure
fact: addi $sp, $sp, -8 #adjust stack pointer
sw $ra, 4($sp) #save return address
sw $a0, 0($sp) #save argument n
slt $t0, $a0, 1 #test for n < 1
beq $t0, $zero, L1 #if n >=1, go to L1
addi $v0, $zero, 1 #else return 1 in $v0
addi $sp, $sp, 8 #adjust stack pointer
jr $ra #return to caller
L1: addi $a0, $a0, -1 #n >=1, so decrement n
jal fact #call fact with (n-1)
#this is where fact returns
bk_f: lw $a0, 0($sp) #restore argument n
lw $ra, 4($sp) #restore return address
addi $sp, $sp, 8 #adjust stack pointer
mul $v0, $a0, $v0 #$v0 = n * fact(n-1)
jr $ra #return to caller
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
17
1
caller rt addr
caller rt addr
A Look at the Stack for $a0 =
2 $sp
$ra
$a0
$v0
$a0 = 2
2
bk_f
old TOS
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
fact: addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slt $t0, $a0, 1
beq $t0, $zero, L1
addi $v0, $zero, 1
addi $sp, $sp, 8
jr $ra
L1: addi $a0, $a0, -1
jal fact
bk_f: lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
mul $v0, $a0, $v0
jr $ra
18
1
caller rt addr
A Look at the Stack for $a0 =
2
$sp
$ra
$a0
$v0
$a0 = 2
bk_f
old TOS
• Stack state after execution of first encounter with the jalinstruction (second call to fact routine with $a0 now holding 1)– saved return address to caller
routine (i.e., location in the main routine where first call to fact is made) on the stack
– saved original value of $a0 on the stack
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
19
1
bk_f
A Look at the Stack for $a0 =
2
$sp
$ra
$a0
$v0
$a0 = 1
0
bk_f
old TOS
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
caller rt addr
$a0 = 2fact: addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slt $t0, $a0, 1
beq $t0, $zero, L1
addi $v0, $zero, 1
addi $sp, $sp, 8
jr $ra
L1: addi $a0, $a0, -1
jal fact
bk_f: lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
mul $v0, $a0, $v0
jr $ra
20
caller rt addr
A Look at the Stack for $a0 =
2
$ra
$a0
$v0
$a0 = 2
0
bk_f
old TOS
$sp$a0 = 1
bk_f
bk_f
• Stack state after execution of second encounter with the jal instruction (third call to fact routine with $a0 now holding 0)– saved return address of
instruction in caller routine (instruction after jal) on the stack
– saved previous value of $a0on the stack
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
21
1
bk_f
A Look at the Stack for $a0 =
2
$sp
$ra
$a0
$v0
$a0 = 1
0
bk_f
old TOS
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
caller rt addr
$a0 = 2fact: addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slt $t0, $a0, 1
beq $t0, $zero, L1
addi $v0, $zero, 1
addi $sp, $sp, 8
jr $ra
L1: addi $a0, $a0, -1
jal fact
bk_f: lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
mul $v0, $a0, $v0
jr $ra
bk_f
$a0 = 0
22
1
bk_f
A Look at the Stack for $a0 =
2
$sp
$ra
$a0
$v0
$a0 = 1
0
bk_f
old TOS
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
caller rt addr
$a0 = 2 • Stack state after execution of first encounter with the first jr instruction ($v0initialized to 1)– stack pointer updated to
point to third call to fact
23
1
A Look at the Stack for $a0 =
2
$sp
$ra
$a0
$v0
0
bk_f
old TOS
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
caller rt addr
$a0 = 2fact: addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slt $t0, $a0, 1
beq $t0, $zero, L1
addi $v0, $zero, 1
addi $sp, $sp, 8
jr $ra
L1: addi $a0, $a0, -1
jal fact
bk_f: lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
mul $v0, $a0, $v0
jr $ra
bk_f
$a0 = 1
1
1*1 = 1
24
A Look at the Stack for $a0 =
2
$sp
$ra
$a0
$v0
bk_f
old TOS
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
caller rt addr
$a0 = 2
1
1
• Stack state after execution of first encounter with the second jr instruction (return from fact routine after updating $v0 to 1 * 1)
– return address to caller routine (bk_f in fact routine) restored to $ra from the stack
– previous value of $a0restored from the stack
– stack pointer updated to point to second call to fact
25
1
A Look at the Stack for $a0 =
2
$sp
$ra
$a0
$v0
1
bk_f
old TOS
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
caller rt addr
$a0 = 2fact: addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slt $t0, $a0, 1
beq $t0, $zero, L1
addi $v0, $zero, 1
addi $sp, $sp, 8
jr $ra
L1: addi $a0, $a0, -1
jal fact
bk_f: lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
mul $v0, $a0, $v0
jr $ra
2
2*1 = 2
caller rt addr
26
A Look at the Stack for $a0 =
2 $sp
$ra
$a0
$v0
old TOS
2
2
caller rt addr
• Stack state after execution of second encounter with the second jr instruction (return from fact routine after updating $v0 to 1 * 1 * 2)
– return address to caller routine (main routine) restored to $rafrom the stack
– original value of $a0 restored from the stack
– stack pointer updated to point to first call to fact
int fact (int n)
{
if (n < 1)
return 1;
else
return (n * fact (n-1));
}
27
Summary
Requirements for functions
Register Conventions
Nested function calls
Recursive function calls
28