Run-time storage layout: focus on compilation, not interpretation

n Plan how and where to keep data at run-time CSE401: Storage Layout n Representation of n int, bool, etc. n arrays, records, etc. n procedures Larry Ruzzo n Placement of Spring 2001 n global variables n local variables Slides by Chambers, Eggers, Notkin, Ruzzo, and others n parameters © W.L. Ruzzo and UW CSE, 1994-2001 n results

1 2

Data layout of scalars Based on machine representation Data layout of aggregates

n Aggregate scalars together Integer Use hardware representation n (2, 4, and/or 8 bytes of memory, maybe Different compilers make different decisions aligned) n Decisions are sometimes machine dependent n Note that through the discussion of the front-end, Bool 1 byte or word we never mentioned the target machine Char 1-2 bytes or word n We didn’t in interpretation, either Pointer Use hardware representation n But now it’s going to start to come up constantly (2, 4, or 8 bytes, maybe two words if n Necessarily, some of what we will say will be segmented machine) "typical", not universal.

3 4

Layout of records Layout of arrays

n Concatenate layout r : record n Repeated layout of s : array [5] of b : bool; record; of fields i : int; element type i : int; n Respect alignment m : record n Respect alignment of : char; restrictions b : bool; element type end; c : char; n Respect field order, if end n How is the length of required by language j : int; end; the array handled? n Why might a language choose to do this or not do this? n Respect contiguity?

5 6

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 1 Layout of multi-dimensional arrays Dynamically sized arrays

n Recursively apply a : array [3] of n Arrays whose length is a : array of s : array [5] of determined at run-time record; layout rule to record; n Different values of the same i : int; subarray first i : int; array type can have different c : char; c : char; lengths end; n This leads to row- end; n Can store length implicitly major layout in array n Alternative: column- n Where? How much space? major layout n Dynamically sized arrays require pointer indirection n Most famous n Each variable must have example: FORTRAN fixed, statically known size

7 8

Dope vectors String representation

n PL/1 handled arrays differently, in particular n A string ≈ an array of characters storage of the length n So, can use array layout rule for strings n It used something called a dope vector, which n Pascal, C strings: statically determined length was a record consisting of n Layout like array with statically determined length n A pointer to the array n n The length of the array Other languages: strings have dynamically n Subscript bounds for each dimension determined length n Arrays could change locations in memory and n Layout like array with dynamically determined size quite easily length n Alternative: special end-of-string char (e.g., \0)

9 10

Storage allocation strategies Parts of run-time memory

n Given layout of , where in n Code/Read-only data area memory to allocate space for each instance? stack n Shared across processes running same program n Key issue: what is the lifetime (dynamic n Static data area extent) of a variable/data structure? n Can start out initialized or n Whole execution of program (e.g., global zeroed variables) n Heap heap ⇒ Static allocation n Can expand upwards through (e.g. sbrk) system call n Execution of a procedure activation (e.g., locals) static data n Stack ⇒ Stack allocation n Expands/contracts downwards n Variable (dynamically allocated data) automatically code/RO data ⇒ Heap allocation 11 12

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 2 Static allocation Stack allocation

n Statically allocate variables/data structures n Stack-allocate variables/data structures with with global lifetime LIFO lifetime n Machine code n Data doesn’t outlive previously allocated data on n Compile-time constant scalars, strings, arrays, etc. the same stack n Global variables n Stack-allocate procedure activation records n static locals in C, all variables in FORTRAN n A stack-allocated activation record = a stack frame n Compiler uses symbolic addresses n Frame includes formals, locals, temps n And housekeeping: static link, dynamic link, … n Linker assigns exact address, patches compiled code n Fast to allocate and deallocate storage n Good memory locality

13 14

Stack allocation II Constraints on stack allocation

n What about procedure P() { n No references proc foo(x:int): proctype(int):int; int x; proc bar(y:int):int; variables local to to stack- begin for(int i=0; i<10; i++){ nested scopes allocated data return x + y; double x; allowed after end bar; within one … begin returns return bar; procedure? } n This is violated end foo; for(int j=0; j<10; j++){ by general double y; var f:proctype(int):int; first-class var g:proctype(int):int; … functions } f := foo(3); g := foo(4); } output := f(5); output := g(6);

15 16

Constraints on stack allocation Heap allocation

n Also violated if proc foo (x:int): *int; n For data with unknown lifetime var y:int; pointers to locals begin n new/malloc to allocate space are allowed y := x * 2; n delete/free/garbage collection to deallocate return &y; end foo; n Heap-allocate activation records of first-class functions var w,z:*int; n Relatively expensive to manage z := foo(3); w := foo(4); n Can have dangling reference, storage leaks n Garbage collection reduces (but may not output := *z; output := *w; eliminate) these classes of errors

17 18

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 3 Stack frame layout Key property

n Need space for n All data in stack frame is at a fixed, statically n Formals computed offset from the FP n Locals n This makes it easy to generate fast code to n Various housekeeping data access the data in the stack frame n Dynamic link (pointer to caller's stack frame) n Static link (pointer to lexically enclosing stack frame) n And even lexically enclosing stack frames n Return address, saved registers, … n Can compute these offsets solely from the n Dedicate registers to support stack access symbol tables n FP - frame pointer: ptr to start of stack frame (fixed) n Based also on the chosen layout approach n SP - stack pointer: ptr to end of stack (can move)

19 20

Stack Layout Accessing locals

n If a local is in the same stack frame then low high

stack grows down t := *(fp + local_offset) addresses addresses n If in lexically-enclosing stack frame t := *(fp + static_link_offset) t := *(t + local_offset) n If farther away

...... t := *(fp + static_link_offset) arg 1 formal 1 N-1 local arg N local 1 local formal N-1 formal local N local formal N arg N-1 static link static

dynamic link t := *(t + static_link_offset) return address saved registers callee's link static

...caller's frame...... caller's …

t := *(t + local_offset) one stack frame stack one SP FP 21 22

At compile-time… Calling conventions

n …need to calculate n Define responsibilities of caller and callee n To make sure the stack frame is properly set up n Difference in nesting depth of use and definition and torn down n Some things can only be done by the caller n Offset of local in defining stack frame n Other things can only be done by the callee n Some can be done by either n So, we need a protocol

23 24

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 4 PL/0 calling sequence PL/0 return sequence

n Caller n Callee n Callee n Caller n Evaluate actual args n Save return address on stack n Deallocate space for n Deallocate space n Order? n Save caller’s frame pointer local, other data for callee’s static n Push onto stack (dynamic link) on stack sp := sp + size_of_locals n Order? n Save any other registers that + other_data link, args

n might be needed by caller n Alternative: First k n Restore caller’s frame sp := fp args in registers n Allocates space for locals, pointer, return address & n Continue execution n Push callee's static link other data other regs, all without n Or in register? sp := sp – size_of_locals in caller after call Before or after stack – other_data losing addresses of stuff arguments? n Locals stored in what order? still needed in stack n Execute call instruction n Set up new frame pointer n Execute return instruction n Hardware puts return (fp := sp) address in a register n Start executing callee’s code

25 26

Accessing callee procedures similar to accessing locals Some questions

n Call to procedure declared in same scope: n Return values? static_link := fp n call p Local, variable-sized, arrays proc P(int n) { n Call to procedure in lexically-enclosing scope: static_link := *(fp + static_link_offset) var x array[1 .. n] of int; call p var y array[-5 .. 2*n] of array[1 .. n] int; n If farther away … t := *(fp + static_link_offset) t := *(t + static_link_offset) } … n static_link := *(t + static_link_offset) Max length of dynamic-link chain? call p n Max length of static-link chain?

27 28

Exercise: apply to this example What do these mean?

module M; proc P(int a); proc Q(int a,int b); var x:int; begin int c; proc P(y:int); output := a; begin proc Q(y:int); output := a+1; c := a; begin R(x+y);end Q; a := a+1; a := b; proc R(z:int); output := a; b := c; begin P(x+y+z);end R; end; end; begin Q(x+y);end P; begin int i=2; int i=2; j=3; x := 1; P(2); P(i); output i; Q(i,j); end M. P(2); output 2;

29 30

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 5 Parameter passing Parameter passing modes

n When passing args, need to support right semantics n call-by-value n Issue #1: when is argument expression evaluated? n Before call? n call-by-sharing n If and when needed by callee? n call-by-reference n Issue #2: what happens if callee assigns to formal? n Is this visible to the caller? If so, when? n call-by-value-result n What happens with aliasing among arguments and lexically visible variables? n call-by-name n Different choices lead to n n different representations for passed arguments and call-by-need n different code to access formals n …

31 32

Call-by-value Call-by-reference

n Assignment to var a : int; n Assignment to formal var a : int; formal doesn’t proc foo(x:int,y:int); changes actual value proc foo(x:int,y:int); affect caller’s begin in caller begin n x := x + 1; value x := x + 1; Immediately n y := y + a; y := y + a; Actual must be lvalue n end foo; Implementation: end foo; n Implementation: pass pass copy of pointer to actual a := 2; argument value n Efficient for big data a := 2; structures(?) foo(a,a); n Trivial for scalars foo(a,a); n References to formal output := a; n Inefficient for output := a; must do extra aggregates(?) dereference

33 34

Big immutable data for example, a constant string Call-by-value-result

var a : int; n Suppose language has call-by-value n Assignment to formal copies final value back proc semantics foo(x:int,y:int); to caller on return begin n But, it's expensive to pass by-value n “copy-in, copy-out” x := x + 1; y := y + a; n Could implement as call-by-reference n Implement as call-by- value with copy back end foo; n Since you can’t assign to the data, you when procedure returns don’t care a := 2; n More efficient than call- foo(a,a); n Let the compiler decide? by-reference output := a; n For scalars? n For arrays?

35 36

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 6 Call-by-result Ada: in, out, in out

var a : int; n proc foo(x:int,y:int); Programmer selects intent begin n Compiler decides which mechanism is x := x + 1; y := y + a; more efficient end foo; n Program’s meaning “shouldn’t” depend a := 2; on which is chosen foo(a,a); output := a;

37 38

Call-by-name, call-by-need Call-by-name

n Variations on lazy evaluation n Replace each use of a proc square(x); int x; n parameter in the callee, by Only evaluate argument expression if and when begin needed by callee the text of the actual x := x * x n Supports very cool programming tricks parameter, but in the end; caller's context n Somewhat hard to implement efficiently in traditional compilers n This implies reevaluation of square(A[i]); the actual every time the n Thunks formal parameter is used n Largely incompatible with side-effects n And evaluation of the actual n So more common in purely functional languages might return different values like Haskell and Miranda each time n But did appear first in Algol-60 39 40

A classic problem: Jensen’s device a procedure to swap two elements

n How to implement the int proc sum(j,lo,hi,Aj); proc swap(int a,int b); n int x, y; equivalent of a math int j, lo, hi, Aj, s; int temp; x = 2; Σ begin begin y = 5; formula like 0≤i ≤n A2i s := 0; temp := a; for j := lo to hi do a := b; swap(x, y); sum(i,0,n,A[2*i])? s := s + Aj; b := temp; end; end; n int j, z[10]; n Pass by-reference or return s; j = 2; by-value do not work, end; z[2] = 5; since they can only swap(j, z[j]); pass one element of A n So: Jensen’s device

41 42

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 7 Call-by-name advantages Call-by-name disadvantages

n Textual substitution is a simple, clear n Repeatedly evaluating arguments can semantic model be inefficient n There are some useful applications, like n Pass-by-name precludes some standard Jensen's device procedures from being implemented n Argument expressions are evaluated n Pass-by-name is difficult to implement lazily

43 44

thunks Parameters and compiling

n Call-by-name arguments are compiled to n There is an intimate link between the thunks, special parameter-less procedures semantics of a programming language and n One gives value of actual, appropriately evaluated the mechanisms used for parameter passing in caller’s environment n Maybe more than other programming n Other gives l-value, again in caller's environment language constructs, the connection is n Thunks are passed into the called procedure extremely strong between implementation and called to evaluate the argument and language semantics in this area whenever necessary

45 46

PL/0 storage allocation PL/0 storage allocation

n How and when it is decided how big a stack frame void SymTabScope::allocateSpace() { will be? _localsSize = 0; _formalsSize = 0; n It’s necessary that the frame always be the same size for every invocation of a given procedure for (int i = 0; i < _symbols->length(); i++) n Also, how and when is it decided exactly where in a { stack frame specific data will be? _symbols->fetch(i)->allocateSpace(this); } n Some pieces are decided a priori (such as the return address) for (int j = 0; j < _children->length(); j++) n Others must be decided during compile-time, such as local { variables (since the number and size can’t be known _children->fetch(j)->allocateSpace(); beforehand) } n This is all done during the storage allocation phase }

47 48

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 8 int SymTabScope::allocateFormal(int size) { int offset = _formalsSize; _formalsSize += size; return offset; }

int SymTabScope::allocateLocal(int size) { int offset = _localsSize; _localsSize += size; return offset; }

void VarSTE::allocateSpace(SymTabScope* s) { int size = _type->size(); _offset = s->allocateLocal(size); }

49

CSE 401, © W. L. Ruzzo and UW CSE, 1994-2001 9