You are on page 1of 43

Functions and The Stack

Anthony J Souza
San Francisco State University
April 2017
Readings and Topics
• Topics
• The Stack
• Function call and return
• Parameter handling
• Register User Conventions
• Readings
• Patterson and Hennessy
• 2.8 and 2.13
The Stack
• Is a last-in-first-out data structure. This is also known as LIFO
• This means when pushing items onto the stack, the last item pushed
is at the top. The first item pushed is the bottom.
• Push  add an item to the top of the stack.
• Pop  remove an item from the top of the stack.
• For example, add 13, 23, and 33 to the stack

top 33
top 23 23
top 13 13 13

add 13 add 23 add 33


Stack In Memory
• Stack: is basically an array of 32-bit words
• Register $29 ($sp) contains the address of the top item in the stack
• Stacks in most machines grow upwards, from high addresses to low
addresses.
• Push  subtract from $sp
0x400000
• Pop  add to $sp Program text

• How memory Space is usually allocated: 0x10010000


Static data and heap

0x7FFFFFFC Stack
Pushing 32-bit word onto the stack
Before:
$sp 0x7fffc6c0 0x7fff c6b8
addi $sp, $sp, -4
sw $23, ($sp) 0x7fff c6bc Empty slot
$23 0x12345678 0x7fff c6c0 Old top item
After:
$sp 0x7fffc6bc 0x7fff c6b8 Empty slot

0x7fff c6bc 0x12345678


$23 0x12345678 0x7fff c6c0 Old top item

• Alternate way of pushing values:


sw $23, -4($sp)
addi $sp, $sp, -4
Popping 32-bit word onto the stack
$sp 0x7fffc6bc 0x7fff c6b8
addi $sp, $sp, 4
lw $19, -4($sp) 0x7fff c6bc Empty slot
$19 0x7fff c6c0 0x89abcdef

$sp 0x7fffc6c0 0x7fff c6b8 Empty slot

0x7fff c6bc Empty slot


$19 0x89abcdef 0x7fff c6c0 Empty slot

• Alternate way of pushing values:


lw $19, ($sp)
addi $sp, $sp, 4
To push $11, $12, $13 onto the stack, in order
Before:
$sp 0x7fff c6c0

$11 0x13
$12 0x23
$13 0x33 0x7fff c6bc
0x7fff c6c0 Old item
sw $11, -4($sp)
sw $12, -8($sp)
sw $13, -12($sp)
addi $sp, $sp, -12
To push $11, $12, $13 onto the stack, in order
After:
$sp 0x7fff c6c0 Empty slot
0x7fff c6b0
Empty slot
$11 0x13 0x7fff c6b4
Empty slot
$12 0x23 0x7fff c6b8

$13 0x33 0x7fff c6bc 0x13

0x7fff c6c0 Old item


sw $11, -4($sp)
sw $12, -8($sp)
sw $13, -12($sp)
addi $sp, $sp, -12
To push $11, $12, $13 onto the stack, in order
After:
$sp 0x7fff c6c0 Empty slot
0x7fff c6b0
Empty slot
$11 0x13 0x7fff c6b4
0x23
$12 0x23 0x7fff c6b8

$13 0x33 0x7fff c6bc 0x13

0x7fff c6c0 Old item


sw $11, -4($sp)
sw $12, -8($sp)
sw $13, -12($sp)
addi $sp, $sp, -12
To push $11, $12, $13 onto the stack, in order
After:
$sp 0x7fff c6c0 Empty slot
0x7fff c6b0
0x33
$11 0x13 0x7fff c6b4
0x23
$12 0x23 0x7fff c6b8

$13 0x33 0x7fff c6bc 0x13

0x7fff c6c0 Old item


sw $11, -4($sp)
sw $12, -8($sp)
sw $13, -12($sp)
addi $sp, $sp, -12
To push $11, $12, $13 onto the stack, in order
After:
$sp 0x7fff c6b4 Empty slot
0x7fff c6b0
0x33
$11 0x13 0x7fff c6b4
0x23
$12 0x23 0x7fff c6b8

$13 0x33 0x7fff c6bc 0x13

0x7fff c6c0 Old item


sw $11, -4($sp)
sw $12, -8($sp)
sw $13, -12($sp)
addi $sp, $sp, -12
To Pop 0x13, 0x23, 0x33 from the stack, in order
Before:
$sp 0x7fff c6b4 0x7fff c6b0

0x7fff c6b4 0x33


$11
0x7fff c6b8 0x23
$12
$13 0x7fff c6bc 0x13

0x7fff c6c0 Old item


addi $sp, $sp, 12
lw $13, -12($sp)
lw $12, -8($sp)
lw $11, -4($sp)
To Pop 0x13, 0x23, 0x33 from the stack, in order
Before:
$sp 0x7fff c6c0 0x7fff c6b0

0x7fff c6b4 0x33


$11
0x7fff c6b8 0x23
$12
$13 0x7fff c6bc 0x13

0x7fff c6c0 Old item


addi $sp, $sp, 12
lw $13, -12($sp)
lw $12, -8($sp)
lw $11, -4($sp)
To Pop 0x13, 0x23, 0x33 from the stack, in order
Before:
$sp 0x7fff c6c0 0x7fff c6b0

0x7fff c6b4 Now empty


$11
0x7fff c6b8 0x23
$12
$13 0x33 0x7fff c6bc 0x13

0x7fff c6c0 Old item


addi $sp, $sp, 12
lw $13, -12($sp)
lw $12, -8($sp)
lw $11, -4($sp)
To Pop 0x13, 0x23, 0x33 from the stack, in order
Before:
$sp 0x7fff c6c0 0x7fff c6b0

0x7fff c6b4 empty


$11
0x7fff c6b8 Now empty
$12 0x23
$13 0x33 0x7fff c6bc 0x13

0x7fff c6c0 Old item


addi $sp, $sp, 12
lw $13, -12($sp)
lw $12, -8($sp)
lw $11, -4($sp)
To Pop 0x13, 0x23, 0x33 from the stack, in order
Before:
$sp 0x7fff c6c0 0x7fff c6b0

0x7fff c6b4 empty


$11 0x13
0x7fff c6b8 empty
$12 0x23
$13 0x33 0x7fff c6bc Now empty

0x7fff c6c0 Old item


addi $sp, $sp, 12
lw $13, -12($sp)
lw $12, -8($sp)
lw $11, -4($sp)
Basic Mechanics of Functions
• C/C++ program: compute 𝑥 𝑝 , 𝑦 𝑞
int pow( int, int); Function prototype:
int pow(int, int);

int main(){ With the prototype for pow(),


int x=2,p=3,y=2,q=4; a caller has all the information
needed to call pow()
int result, result1;
result = pow(x,p);
arg0
result1 = pow(y,q);
} Pow() result

arg1
Basic Mechanics of Functions
• No need to see the code for pow()
• Caller code (main) and callee function (pow) can be written
independently
• Caller and callee communicate through arguments and return values
defined in prototype
• Arguments:
• Placeholders for copies of data
• Caller puts copies of its variables in argument placeholders
• Callee function puts return value in another placeholder
• Registers are used for arguments and return values.
Register Use Convention
• Set of rules on how to use registers so software modules can
communicate with each other properly
• Register use conventions are part of the call conventions of a system.
• In MIPS:
• First four arguments in $a0-$a3 ($4-$7)
• Return values in $v0, $v1 ($2, $3)
• Caller (main) puts correct values in $a0-$a3 before calling function
• Callee (pow) puts return value in $v0, $v1 before returning.
Control flow of main and pow()
int pow(int b, int e){
int res = 1;
for(int i = 0; i < e; i++){
res = res * b; Each function call must
} remember the return
return res; address(where to
} return to when
int main(){
function is done).
int x=2,p=3,y=2,q=4;
int result, result1;
result = pow(x,p);
result1 = pow(y,q);
}
To call function pow in MIPS
• To call a function in MIPS, a assembly programmer must do the
following:
1. Put arguments into $a0-$a3 registers.
2. Call function via jal instruction.
3. When pow returns, copy return value from v0 into result variable.
• When jal is executed, return address is saved to $ra($31) and then
jumps to label.
• Return address  address after the jal instruction that we want to
return to when function returns. (PC + 4)
• In the case of multiple function calls, i.e. a function calls another
function, each function is responsible for saving its return address if it
calls other functions.
Function call Instruction
Jump and Link:
jal label
$31 (or $ra) = return address
go to label [or, PC = address of label]
Jump register:
jr R [R is any register]
PC = contents of R
To return from a function:
jr $ra
C++ prototype for pow()
int pow(int arg0, int arg1)

MIPS comment header for pow():


# int pow(int arg0, int arg1)
# a0 arg0
# a1 arg1
# v0 result
C++ Main Program – Calling pow
• In C++ main program
Result = pow(x,p):
Mean:
• Copy x into placeholder arg0(a0)
• Copy p into placeholder arg1(a1)
• Call pow()
• When pow() returns, copy return value from placeholder into variable
result
C++ Main Program – Calling pow
• In MIPS , “compiler” decides:
#x $s0
#p $s1
# result $s2 Note that we do not
care how pow is written,
we just know its label
C++: and what arguments it
result = pow(x, p); takes. And we assume
MIPS: pow will put the return
value into v0.
add $a0, $s0, $0
add $a1, $s1, $0
jal pow
add $s2, $v0, $0
Pow function
int pow(int arg0, int arg1)
{
int product = 1, i;
for (i=0; i<arg1; i++) {
product = arg0 * product;
}
return product;
}
• Keep in mind pow can be written independently of main.
• Main and pow communicate through arguments and return values.
• Modularity: organize code into independent modules.
Translate pow into MIPS
• Pow() does not call any other functions
• Therefore pow is considered a leaf.
• MIPS RULE:
• For leaf functions, all local variables are allocated to $t? registers.
• Some cases, we can allocate return variable directly to v0, instead of using a t
register and then copying it before we return.
# i $t0
# product $v0
Translate pow into MIPS
• Pow() does not call any other
functions
• Therefore pow is considered # i $t0
a leaf. # product $v0
• MIPS RULE: pow: li $v0, 1
• For leaf functions, all local li $t0, 0,
variables are allocated to $t? bge $t0, $a1, exit
registers. loop: mul $v0, $a0, $v0
• Some cases, we can allocate
return variable directly to v0,
addi $t0, $t0, 1
instead of using a t register blt $t0, $a1, loop
and then copying it before we exit: jr $ra
return.
Examples 4.1
• http://unixlab.sfsu.edu/~whsu/csc256/PROGS/4.1.cpp
• http://unixlab.sfsu.edu/~whsu/csc256/PROGS/4.1.s
Nested Function Call
• C++ program:
• Compute polynomial 𝑥 4 + 𝑥 3 + 1, for x=2 to 4
• Sample code for using poly:
int main()
{
int i;
int result;
for (i=2; i<=4; i++) {
result = poly(i);
cout << result << endl;
}
}
Nested Function Call
• The poly function makes call to pow
int poly(int arg) • Note that main, poly, and
pow are completely modular.
{
• Poly’s return address will be
int temp1,result; in $ra
temp1 = pow(arg,4); • When poly calls pow, pow’s
result = pow(arg,3); return address is stored in
result = temp1 + result + 1; $ra, overwriting poly’s return
address
return result;
• If this is the case, how does
} poly know where to return
to?
Nested Function Call
• The poly function makes call to pow
int poly(int arg) • Note that main, poly, and
pow are completely modular.
{
• Poly’s return address will be
int temp1,result; in $ra
temp1 = pow(arg,4); • When poly calls pow, pow’s
result = pow(arg,3); return address is stored in
result = temp1 + result + 1; $ra, overwriting poly’s return
address
return result;
• If this is the case, how does
} poly know where to return
to? We will be saving the
value of $ra onto the stack.
Nested Functions
• Given that return addresses must be saved on the stack when executing
nested function calls.
• Our poly function starts out looking like the following:
poly:
addi $sp, $sp, -4
sw $ra, ($sp)

#do logic for poly function.

lw $ra, ($sp)
addi $sp, $sp, 4
jr $ra
Nested Functions
• When writing poly, we don’t see code from other functions.
• Compilers work in a similar way when they compile functions.
• From C++ code:
temp1 = pow(arg,4);
result = pow(arg,3);
result = temp1 + result + 1;
• What register should temp be in?
Nested Functions
• When writing poly, we don’t see code from other functions.
• Compilers work in a similar way when they compile functions.
• From C++ code:
temp1 = pow(arg,4);
result = pow(arg,3);
result = temp1 + result + 1;
• What register should temp be in?
• Temp needs to be in a safe register. Its value must be preserved
across functions calls. Meaning pow will not modify it.
• The $s? registers needs to used. (called saved registers).
Nested Functions
• Register use conventions: set of rules that determine how registers are used in a system.
• In general, a function is written/translated independently from other functions.
• Suppose we are writing a caller function; it calls a callee function.
• What happens to registers after the callee returns?
caller:
$s0 = 13
$a0 = 63
$v0 = -3
$t0 = 23
jal callee #callee()

$s0 = 13
$a0 = ??
$v0 = ??
$t0 = ??
Nested Functions
• MIPS register use conventions states:
• $a?, $v?, $t? registers not preserved across functions calls
• $ra is not preserved as well.
• $s? registers are preserved across function calls
• RULE:
• Suppose we are writing/translating function F().
• If $s? is used as a local var in F():
• The old value of $s? must first be saved on the stack
• Before the function returns it must restore all $s0? Registers that were
saved.
Nested Functions
• Looking back out our poly function, we will allocate temp1 to an $s? register, $s1

poly:
addi $sp, $sp, -8
sw $s1, ($sp)
sw $ra, 4($sp)

# do some work

lw $ra, 4($sp)
lw $s1, ($sp
addi $sp, $sp, 8
jr $ra
Nested Functions - Scopes
• There are various arguments floating around:
• poly’s argument arg,
• pow’s argument arg0, and arg1
• Note that all arguments share argument registers $a0-$a3
• MIPS prototype and comment header for poly:
# int poly(int arg)
# a0 arg
# v0 result
• Note from C++ code:
temp1 = pow(arg,4);
result = pow(arg,3);
• arg(in $a0 is used twice, once by poly and once by pow
• $a0 needs to be the same after the call to pow
• But register use conventions says $a0 must be assumed to contain garbage after call to pow(),
meaning not preserved across functions call!.
• Therefor, save a copy of arg in $s
Nested Function Calls poly:addi $sp, $sp, -12
• For nested function calls, sw $s1, ($sp)
often need to make a copy of sw $s0, 4($sp)
sw $ra, 8($sp)
each argument.
add $s0, $a0, $0
• In our example this is arg for li $a1, 4
the function poly. jal pow
• Then we can use a0 for pow’s move $s1, $v0
first argument. li $a1, 3
jal pow
• Now we can translate poly: add $t0, $v0, $s1
addi $v0, $t0, 1
lw $ra, 8($sp)
lw $s0, 4($sp)
lw $s1, ($sp)
addi $sp, $sp, 12
jr $ra
Nested Function Calls
• Complete source code at:
• http://unixlab.sfsu.edu/~whsu/csc256/PROGS/4.2.cpp
• http://unixlab.sfsu.edu/~whsu/csc256/PROGS/4.2.s
Summary MIPS Register Use Conventions
• Set of rules agreed on by software developers to make
sure software modules can communicate properly
• $a0-$a3 are arguments
• $v0-$v1 are return values
• $a?, $t?, $v? are not preserved across function calls.
• Must assume $a? $v? change contents after function
calls
• $t? are temporaries; these are also not preserved
across function calls.
Summary MIPS Register Use Conventions
• It is the responsibility of the caller to save $a?, $v?,
and $t?, if needed. [caller-saved]
• $s? are saved across function calls.
• It is the responsibility of the callee to save and restore
$s? registers that it uses [callee-saved]
• A compiler will choose the correct type of register to
use for each local variable.

You might also like