7.2 Storage Organization
Computer Science 332 Compiler Construction 7.2-7.3 Storage Organization / Allocation Strategies
• Runtime storage consists of – Generated target code – Data objects • Static: stored in static data area • Dynamic : stored on heap – Stack to keep track of procedure activations • Procedure call sequence: 1.Status (PC, register contents) is saved on stack 2.Called procedure executes and returns 3.Register values & PC are restored from stack
7.2 Storage Organization • Difference between static and dynamic data is most obvious in a language like C/C++: – static: #import int a [5000]; main(){ ... } – dynamic: void foo() { int size = get_size(); int *a = new int [size]; } Won't matter to us in our “toy ML” compiler
7.2 Storage Organization High
Stack
– As procedures are called, stack “grows” downward. – As dynamic storage is allocated, heap grows
Heap Static Data Low
Code
upward. – NOTE: This is the opposite of what the book has!
Activation Records • Activation record (a.k.a. frame, a.k.a. stack frame) stores info needed to execute a procedure. • A.r. is pushed when proc. is called, popped when it returns. • A.r. typically contains 1.Local data 2.Machine status (PC, registers) 3.Actual parameters (may be passed in registers) 4.Returned value (may be passed in registers)
Compile-Time Layout of Local Data • Local data declaration (e.g., int i; char c;) corresponds to allocating more space on top of stack. • Data are typically aligned in segments corresponding to addressing constraints of processor – e.g., 4-byte segments for most current (32-bit) processors. • Works well for 4-byte types like int, but for others it can be wasteful: char a, b, c; allocates 12 bytes when only 3 are needed; rest is “padding”. • Can avoid doing this by not padding, then generating extra code to “unpack” the data (probably unnecessary nowadays).
7.3 Storage-Allocation Strategies
Calling Sequences
• Static Allocation (e.g., FORTRAN, global arrays in C/C++) : irrelevant to our project • Dynamic Allocation (e.g., C/C++, Java) : ditto • Stack Allocation: necessary for implementing functions in ML – Special register top_sp ($sp in MIPS) marks top of stack – Activation records are allocated (deallocated) at run-time by incrementing (decrementing) value of this register (b/c stack grows downward).
• Call sequence allocates activation record and enters info into its fields 1. Caller evaluates actuals 2.Caller stores return address and old value of top_sp, then increments top_sp. 3.Callee saves register values and other status info 4.Callee initializes its local data and begins execution
Calling Sequences • Return sequence restores state of machine so calling proc. can continue execution 1.Callee places return value next to activation record of caller (or in a special register). 2.Using info in status field, callee restores top_sp and other registers, and jumps to a return address in caller's code ($ra in MIPS) 3.Although top_sp has been decremented, caller can copy returned value into its own activation record and use it.
fact:
L1:
sub $sp, $sp, 8 sw $ra, 4($sp) sw $a0, 0($sp) slt $t0, $a0, 1 beq $t0, $zero, L1 add $v0, $zero, 1 add $sp, $sp, 8 jr $ra sub $a0, $a0, 1 jal fact lw $a0, 0($sp) lw $ra, 4($sp) add $sp, $sp, 8 mul $v0, $a0, $v0 jr $ra
# # # # # # # # # # # # # # #
adjust stack for 2 items save the return address save the argument n test for n < 1 if n >=1, goto L1 return val = 1 pop 2 items off stack return to after jal n >= 1: arg gets (n-1) recur with (n-1) return : restore arg n restore return address pop 2 items return val = n*fact(n-1) return to caller
Concrete Example MIPS for factorial function in C (Patterson & Hennessy p. 137): int fact(int n) { return n == 0 ? 1 : n * fact(n-1); }
fact(2)
$a0 : 2
Stack
$sp
sub $sp, $sp, 8
# adjust stack for 2 items
sub $sp, $sp, 8 sw $ra, 4($sp)
# adjust stack for 2 items # save the return address
$sp $a0 : 2
$a0 : 2
2 $ra
Stack
$ra
$a0 : 2
Stack
sub $sp, $sp, 8 sw $ra, 4($sp) sw $a0, 0($sp)
$sp
# adjust stack for 2 items # save the return address # save the argument n
sub sw sw slt
$sp $a0 : 2
Stack
$sp, $ra, $a0, $t0,
$sp, 8 4($sp) 0($sp) $a0, 1
2 $ra
Stack
# # # #
adjust stack for 2 items save the return address save the argument n test for n < 1
$sp
L1:
sub sw sw slt beq ... sub jal
$sp, $ra, $a0, $t0, $t0,
$sp, 8 4($sp) 0($sp) $a0, 1 $zero, L1
$a0, $a0, 1 fact
# # # # #
adjust stack for 2 items save the return address save the argument n test for n < 1 if n >=1, goto L1
sub $sp, $sp, 8
# adjust stack for 2 items
# n >= 1: arg gets (n-1) # recur with (n-1)
$sp
$a0 : 1
2 $ra
$sp $a0 : 1
Stack
sub $sp, $sp, 8 sw $ra, 4($sp)
# adjust stack for 2 items # save the return address
$sp 2 $ra
Stack
Stack
sub $sp, $sp, 8 sw $ra, 4($sp) sw $a0, 0($sp)
1 $ra
$ra
$a0 : 1
2 $ra
$a0 : 1
2 $ra
Stack
# adjust stack for 2 items # save the return address # save the argument n
$sp
L1:
sub sw sw slt beq ... sub jal
$sp, $ra, $a0, $t0, $t0,
$sp, 8 4($sp) 0($sp) $a0, 1 $zero, L1
$a0, $a0, 1 fact
1 $ra
$a0 : 0
# # # # #
adjust stack for 2 items save the return address save the argument n test for n < 1 if n >=1, goto L1
# n >= 1: arg gets (n-1) # recur with (n-1)
1 $ra
$a0 : 0
Stack
# adjust stack for 2 items # save the return address
$sp
2 $ra
Stack
2 $ra
Stack
sub $sp, $sp, 8 sw $ra, 4($sp) sw $a0, 0($sp)
0 $ra 1 $ra
$ra 1 $ra
$a0 : 0
# adjust stack for 2 items
$sp
$sp
2 $ra
sub $sp, $sp, 8 sw $ra, 4($sp)
sub $sp, $sp, 8
$a0 : 0
2 $ra
Stack
# adjust stack for 2 items # save the return address # save the argument n
$sp
sub $sp, sw $ra, sw $a0, slt $t0, beq $t0, add $v0, add $sp, jr $ra
$sp, 8 # adjust stack for 2 items 4($sp) # save the return address 0($sp) # save the argument n $a0, 1 # test for n < 1 $zero, L1 # if n >=1, goto L1 $zero, 1 # return val = 1 $sp, 8 # pop 2 items off stack # return to after jal
1 $ra
$a0 : 0 $v0 : 1
lw $a0, 0($sp) lw $ra, 4($sp) add $sp, $sp, 8
$a0 : 1 $v0 : 1
$sp
2 $ra
# return : restore arg n # restore return address # pop 2 items
Stack
# return : restore arg n
1 $ra
$a0 : 1 $v0 : 1
Stack
2 $ra
lw $a0, 0($sp)
lw $a0, 0($sp) lw $ra, 4($sp) add $sp, $sp, 8 mul $v0, $a0, $v0 jr $ra
$sp $a0 : 1 $v0 : 1
$sp
2 $ra
Stack
# # # # #
return : restore arg n restore return address pop 2 items return val = n*fact(n-1) return to caller
2 $ra
Stack
$sp
lw $a0, 0($sp)
$a0 : 2 $v0 : 1
lw $a0, 0($sp) lw $ra, 4($sp) add $sp, $sp, 8 mul $v0, $a0, $v0 jr $ra
$a0 : 2 $v0 : 2
# return : restore arg n
2 $ra
$a0 : 2 $v0 : 1
return : restore arg n restore return address pop 2 items return val = n*fact(n-1) return to caller
$sp Stack
# return : restore arg n # restore return address # pop 2 items
$sp
Stack
# # # # #
lw $a0, 0($sp) lw $ra, 4($sp) add $sp, $sp, 8
$sp Stack