Upload
gabriela-houser
View
214
Download
0
Embed Size (px)
Citation preview
Based on Mike Feeley’s original slides; Modified by George Tsiknis
Unit 11Local Variables, Parameters
and the Stack
Relevant Information CPSC 213 Companion
- 2.8 (all subsections) (Optional) Textbook
- 3.7
Last Updated: 7/28/2014 12:14 PM
Learning Outcomes At the end of this unit you should be able to:
translate java methods that contain local variables and parameters into sm213 assembly and vice versa
translate C functions that contain local variables and parameters into sm213 assembly and vice versa
describe at least two ways that compilers deal with local variables and list their advantages and disadvantages
describe at least two ways that compilers deal with function/method parameters and arguments and list their advantages and disadvantages
Unit 11 2
Unit 11 3
Local Variables of
Functions and Methods
Can local variables be static? Consider the procedures on the
right. Can local0 and local1 be allocated statically?A. YesB. Yes, but only if the function is
not recursiveC. Yes, but only for specific
functions with specific properties
D. No, no local variable can be allocated statically
E. I don’t know what you are talking about
Unit 11 4
void b () { int local0 = 0; int local1 = 1;. . .}
void foo () { b ();}
void bar () { b ();}
Unit 11 5
Local Variables of a Procedure
Scopeare part of the local scope (local
environment) of a procedureaccessible only within that procedure
Lifetimeallocated when procedure startsde-allocated (freed) when procedure
returnsexist only when procedure/method is runningeach call has its own instances
Need to allocate and deallocate a procedure’s environment at each procedure activation
void b () { int local0 = 0; int local1 = 1;}
void foo () { b ();}
Unit 11 6
Local Variables of a Procedure (cont’)
Activation execution of a procedure starts when procedure is called and
ends when it returns there can be many activations of a
single procedure alive at once Activation Frame
memory that stores an activation’s state including its locals and arguments
Should we allocate Activation Frames from the Heap? call malloc() to create frame on
procedure call and call free() on procedure return?
void b () { int local0 = 0; int local1 = 1;}
void foo () { b ();}
l0: 0l1: 1a0: ?a1: ?
Heap isn’t Best for Activation Frames Order of frame allocation and deallocation is special
frames are de-allocated in the reverse order in which they are allocated We can thus build a very simple allocator for frames
lets start by reserving a BIG chunk of memory for all the frames assuming you know the address of this chunk how would you allocate and free frames?
What data structure should we use to store the frames?
What restriction do we place on lifetime of local variables and arguments?
7
12345678
Activation Frames
Explicit Allocation in Heap2837461
5 Requires more complicated and thus more costly allocation and deallocation to avoid fragmentation.
Simple, cheap allocation. Just add or subtract from a pointer.
Unit 11 8
The Runtime Stack Every (thread in a) program is given a part of
memory called the runtime stack A call to a method pushes onto the stack an
activation frame with the method’s local scope local variables saved registers any storage needed just for this call
The compiler generates code (as part of a method's code) to allocate a frame when a method is called
(size is known)– may allocate space for one variable at a time or all
space at once to deallocate the frame when a method returns to access local variables within the frame
(constant offsets) Stack usually starts at the highest address
and grows towards lower addressesStack Bottom
Stack TopCurrent Framer5
Stack Pointer (SP)
Gro
ws
Up
Unit 11 9
The Memory Segments STATIC
instructions constants static variables
HEAP all dynamically allocated objects
created by– new– malloc
STACK all local scopes of the methods
invoked but not returned yet
address
0
max
Unit 11 10
Accessing Local VariablesSimilar to object or struct member variables : Compiler sets their layout Compiler stores all local variables together Accessing local variables is similar to accessing
object or struct member variablesneed the address of the start of the local scopeeach variable will be determined by a constant offset
Compilers typically use a register to hold the address of the top of the current stack frameusually it is called the stack pointer in our machine we use r5 as the stack pointer
Locating local variables within a frameuse the stack pointer (r5 ) and constant offsets
Unit 11 11
Accessing Local Variables: Example 1
public class A { public static void f () { int m = 0; int n = 1; . . . } ... void main() { A.f (); ...
void f () { int m = 0; int n = 1; . . .}
void main(){ f();...
Java C
So, since r5 will point to the activation frame when f is called, to initialize m and n, f has to do:
ld $0x0, r0 # r0 = 0st r0, 0x0(r5) # m = 0ld $0x1, r0 # r0 = 1st r0, 0x4(r5) # n = 1
Unit 11 12
Example 2.address 0x100start: ld $0x400000, r5 # initialize
stack # pointer
.address 0x200main: gpc $6 r6 # r6 = pc+6
j f # call f () halt
.address 0x300f: deca r5 # create space for n
deca r5 # create space for m ld $0x0, r0 # r0 = 0 st r0, 0x0(r5) # m = 0 ld $0x1, r0 # r0 = 1 st r0, 0x4(r5) # n = 1 inca r5 # remove m inca r5 # remove n j 0x0(r6) # return
void f () { int m = 0; int n = 1;}
void main(){ f();}
main’s frame
r5
n
m
stack
0
1
Decision on Local Variables Local variables are only needed during the execution
of the function Therefore we can
either store them on the stack as part of the function’s frame (as we did so far)
or keep them in registers, whenever it is possible If we keep them in registers, we can still save them
on the stack when we run out of registers. Compiler can decide which method to use for each
function For instance, if function has few locals (1-3), store them in
registers; otherwise store them on the stack We’ll try to keep the local variables in registers
until we run out of registers. Unit 11 13
1
Unit 11 14
Saving the Return Address A procedure g may call another procedure f
main sets in r6 a return address before it calls g g needs to set in r6 a new return address before it
calls f when f returns, g needs the address that WAS in
r6 to return If a procedure calls another procedure it
needs to save current r6 on the stack Optimizing procedure code:
At the beginning: If a procedure calls another procedure, save r6 on the stack
Before a call: compute new return address and put it in r6
Before returning: restore r6 to the value on the stack and release its space.
void f() { int m = 0; int n = 1;}
void g(){ f(); ...}
void main(){ g() ...}
Unit 11 15
Example 3: Return-Address Saving & Locals in Registers
.address 0x200g: deca r5 # create space for
r6st r6, 0x0(r5) # save r6 to stackgpc $6, r6 # r6 = pc
+ 6j f # call f()
20c: ld $x, r1st r0, 0(r1) # x = f()ld 0x0(r5), r6 # restore r6 from
stackinca r5 # remove r6 spacej 0x0(r6) # return
.address 0x300f: ld $0x2, r0 # r0 = m = 2
ld $0x3, r1 # r1 = n = 3add r1, r0 # r0 = return valuej 0x0(r6) # return
int x;
void f() { int m = 2; int n = 3; return m+n;}
void g(){ x = f();}
stack
r5
main’s frame
1000C
10008
10000
1000C10008
r6
500
500
20c500
1000C
r0
25
Unit 11 16
Return Value
In C and Java functions/methods return a single value
Traditionally return value is left in register 0 (%eax in IA32)
Our machine also uses r0 for it Normal procedure:
function moves return value to r0 before it returnscaller gets the value from r0
2, 3
Unit 11 17
Argumentsof
Procedures and Methods
Unit 11 18
Function/Method Arguments Formal arguments (or parameters) are part of
method’s scope Are different than local variables
values are supplied at the call ( actual arguments ) by the caller
Two ways to pass arguments through registers (good for small number of arguments) on the stack (more general)
When stack is used calling procedure pushes the actual arguments onto the
stack arguments are pushed in the reverse order ( from right to
left), so first argument is at the top of the stack on return, calling procedure pops the arguments
We’ll always pass arguments on the stack.
Register Usage Convention r0-r3 are caller-save registers:
function may use them freely, but if it calls other functions, their values may be changed after a call.
Use them for values that do not need to be preserved across function calls.
r4-r7 are callee-save registers: function may use them, but must save their values on
the stack upon entering, and restore their values before returning.
Note that r5 is an exception: It should not be changed and used for anything else, except as stack pointer.
Note that you have to pop registers from the stack in the reverse order in which they were pushed
Unit 11 19
Unit 11 20
Responsibilities of a Function/Method
At the beginning Save on stack any of
registers r4-r7 if you plan to use them
Specifically, save r6 on the stack if function calls another
Before it returns Set the return value in r0 Restore saved registers Restore r6
Before calling another function
Set callee’s arguments on the stack (in reverse order)
Set the return address in r6
After the call to another function
Pop the called function’s arguments from the stack
Get return value from r0
Unit 11 21
Example 4: Putting all Together# bar’s code (reg use: mr0, nr1) bar: ld 0x0(r5), r0 # r0 = i (holds
m)inc r0 # r0 = m = i+1
ld 0x4(r5), r1 # r1 = j (holds n)
inc r1 # r1 = = n = j+1
# leave result in r0add r1, r0# r0 = m+n
# returnj 0x0(r6) #return
int bar(int i, int j) { int m = i+1; int n = j+1; rerurn m+n;}void foo() { int k; k = bar(8, 10);}
r5
j
i
stack
8008
800c
8004
8000
10
8
r0 20
Unit 11 22
Example 4: Putting all Together# set ret addr and call bar
gpc $6, r6 # r6 = ret. addr
j bar
# remove arg’s
inca r5 # remove i
inca r5 # remove j
# restore and remove r6
ld 0x0(r5), r6 # restore r6
inca r5 # remove r6
# return value is in 0
# asume that this is k
#just return
j 0x0(r6) #return
# foo’s code
foo:# save r6deca r5 #space for r6st r6, 0x0(r5) # save r6
# prepare for calling bar(8,10)
# set actual argumentsdeca r5 # space for jld $0xa, r1st r1, 0x0(r5) # j = 10
deca r5 # space for ild $0x8, r1st r1, 0x0(r5) # i = 8
r6
r5
500
8014
8010
800c
8008
r6
j
i8
10
500
stack
frame of caller of foo
610
610
Unit 11 23
The First Activation Frame The first procedure that is executed by any program is
main(). So at the bottom of every stack is the frame for main. How is the stack pointer set initially? How is main called?
There is an OS operation _start() or crt0() or crt1() that its code is executed first sets the stack pointer to (largest memory address +1) calls main
_start() for our sm213 Machine’s with 4 MB memory:
# _start ()_start: ld 0x400000, r5 # initialize stack pointer
# to the base of stackgpc $6, r6 # r6 = pcjmp main # goto main()halt
4, 5, 6
APPENDIXExample 4 with Local Variables Stored on the Stack
Unit 11 24
Unit 11 25
Example 4: Putting all Together# bar’s code # set local var’s
deca r5 # space for ndeca r5 # space for m
# do calculationsld 0x8(r5), r0 # r0 = iinc r0 # r0 = i+1st r0, 0x0(r5) # m = i+1 ld 0xc(r5), r1 # r1 = jinc r1 # r1 = j+1st r1, 0x4(r5) # n = j+1
# leave result in r0add r1, r0# r0 = m+n
# remove local var’s inca r5 # remove minca r5 # remove n
# returnj 0x0(r6) #return
int bar(int i, int j) { int m = i+1; int n = j+1; rerurn m+n;}void foo() { int k; k = bar(8, 10);}
r5
j
i
n
m
stack
8008
800c
8004
8000
10
8
11
9
r0 20
7f f f
Unit 11 26
Example 4: Putting all Together# set return address and call bar
gpc $6, r6 # r6 = ret. addr
j bar
# perform after call tasks
# remove arg’s
inca r5 # remove i
inca r5 # remove j
# restore and remove r6
ld 0x0(r5), r6 # restore r6
inca r5 # remove r6
# set k to the return value
st r0, 0x0(r5)
# prepare to return
inca r5 # remove k
j 0x0(r6) #return
# foo’s code
# set local var’s
foo: deca r5 # space for k
# prepare for calling bar(8,10)# save r6 deca r5 #space for r6
st r6, 0x0(r5) # save r6
# set actual argumentsdeca r5 # space for jld $0xa, r1st r1, 0x0(r5) # j = 10
deca r5 # space for ild $0x8, r1st r1, 0x0(r5) # i = 8
r6
r5
500
8018
8014
8010
800c
8008
k
r6
j
i8
10
?
500
stack
frame of caller of foo
610
610
20