CSC448: Code Generation: Run-Time Environment V [6/14] |
What if recursive procedure calls are permitted?
A stack allows fast allocation and deallocation of activation records.
The position of local variables is not known at compile-time.
Generated code expects to find local variables at a fixed offset from the current activation record (in a C-like language).
On x86:
ebp
(Stack-Frame Base Pointer) register
points to the current activation record.
-4(%ebp)
refers to the memory
location that is 4 bytes (or one word = one 32 bit
integer) below the area pointed to by the base pointer.
The following code moves the integer constant 3 into that
location:
movl $3, -4(%ebp)
esp
(Stack Pointer) register points to
the top element of the stack.
Procedures and functions communicate by passing arguments and return values.
There are several calling conventions in common use on x86, but we will use the following convention:
eax
. There are other general purpose
registers: ebx
, ecx
,
edx
.
A diagram:
Location | Value |
---|---|
16(%ebp)
|
argument 3 |
12(%ebp)
|
argument 2 |
8(%ebp)
|
argument 1 |
4(%ebp)
|
return address |
0(%ebp)
|
old ebp
|
-4(%ebp)
|
local variable 1 |
-8(%ebp)
|
local variable 2 |
-12(%ebp)
|
local variable 3 |
Global variables are stored in a fixed location that can be referenced via an assembly language symbol (with the same name as the global variable).
For example, f(1,2,3)
might compile to:
pushl $3 // push parameter onto stack pushl $2 // push parameter onto stack pushl $1 // push parameter onto stack call f // store address of next instruction on stack, jump to f (set eip) addl $12, %esp // deallocate space for actual parameters
Callee executes the preamble:
pushl %ebp // store old base pointer movl %esp, %ebp // set new base pointer subl $12, %esp // allocate space for local variables (three in this case)
Callee executes the postamble:
movl %ebp, %esp // deallocate space for local variables pop %ebp // restore old base pointer ret // pop address of instruction from stack and jump there (set eip)
Note that the activation record of the caller grows temporarily before the call (as arguments are pushed onto the stack), and shrinks after the call (when the stack pointer is moved upwards).
push = decrement esp then put data at new esp. pop = get data from esp then increment esp.
The arguments are reversed to allow procedures or functions with a variable number of arguments.
Suppose main(argv,argc){x}
calls f(a,b,c){y,z}
calls
g(d,e){w}
.
0x1000 argc (parameter of main) 0x0FFC argv (parameter of main) 0x0FF8 0x???? = %eip for os/runtime system (return address from main) +-> 0x0FF4 0x???? = %ebp for os/runtime system (return frame from main) | 0x0FF0 x (local variable of main) | | 0x0FEC c (parameter of f) | 0x0FE8 b (parameter of f) | 0x0FE4 a (parameter of f) | 0x0FE0 0x???? = %eip for main (return address from f) +-- 0x0FDC <-+ 0x0FF4 = %ebp for main (return frame from f) 0x0FD8 | y (local variable of f) 0x0FD4 | z (local variable of f) | 0x0FD0 | e (parameter of g) 0x0FCC | d (parameter of g) 0x0FC8 | 0x???? = %eip for f (return address from g) +---> 0x0FC4 --+ 0x0FDC = %ebp for f (return frame from g) | +-> 0x0FC0 w (local variable of g | | | +-- %esp = 0x0FC0 +---- %ebp = 0x0FC4 e = 12(%ebp) (parameters are positive) d = 8(%ebp) (parameters are positive) w = -4(%ebp) (local variables are negative)
Variables indexed w.r.t. %ebp
.
End of allocated space determined by %esp
.