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 <stdio.h> 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