390 likes | 457 Views
Honors Compilers. Addressing of Local Variables Mar 19 th , 2002. Procedure frame. Assume stack builds down Parameters stored by caller Return point stored by callee Saved regs stored by callee Old frame ptr stored by callee FRAME PTR local variables STACK PTR .
E N D
Honors Compilers Addressing of Local Variables Mar 19th, 2002
Procedure frame • Assume stack builds down • Parameters stored by callerReturn point stored by calleeSaved regs stored by calleeOld frame ptr stored by calleeFRAME PTR local variables STACK PTR
Passing Parameters • Parameters can be passed three ways • In registers • On the stack, addressed by stack ptr on entry • In memory, referenced by an argument ptr • Cannot pass all parameters in registers • So use method 2 or 3 as a backup
Addressing Parameters • Parameters in registers • Either keep them in the register and use there • Or store as a local variable • Parameters on stack • Address using the frame pointer • Parameters referenced by arg ptr • Reference via arg ptr, possibly have to save arg ptr as a local variable if not leaf procedure
Local Variables • If a local variable is of fixed size, it can be addressed directly using a fixed offset from the frame pointer or stack pointer • For a variable of dynamic size (e.g. a local array with dynamic bounds), we use a hidden pointer
Addressing a Variable Length Object • Parameters Return point Old frame ptr frame ptr Fixed part of stack Dynamic object Dynamic object stack ptr • For each dynamic object we put a pointer into the fixed part of the stack. This pointer can be addressed as a normal fixed length local variable.
Variables in Blocks • Consider simple case {int a,b,c ….. } {int a,d,e ….. } • First, the clash of a’s is a compile time issue, not a run-time issue • For example, rename to a1, a2 • Now just include a1,b,c,a2,d,e in parent frame and address of parent frame ptr • Can overlap the sets of variables
Variables in Blocks (continued) • Consider this case {int a[10000]; ….} {int b[10000]; ….} lots more code here • Now do we really want to have 10000 int’s allocated throughout the procedure? • Perhaps not
Variables in Blocks (continued) • So in this case {int a[10000]; ….} we could consider using a different technique Allocate *int in enclosing frame (call it __a) On block entry: Save stack pointer in some local variable Decrement stack pointer by 10000 Copy stack ptr to __a Address indirectly through __a On block exit Restore stack ptr from saved location
Up Level Addressing • PROC outer = (INT x) INT : ( INT a; INT b; PROC inner = (INT y) INT : ( INT c; … c := a + b; …) …) • How do we address a and b from inside inner
Stack Frames • Inner could be recursive Stack frame for outer (has a and b) Stack frame for inner Stack frame for inner Stack frame for inner Stack frame for inner Stack frame for inner (references a,b) • How does inner frame find the outer frame? • If we know frame ptr of outer, we can address off that, in the usua fashion.
A Horrible Solution • The Saved frame pointer links each frame to the previous one. • Mark each frame with e.g. name of procedure it is for • Trace back frame pointer links until you get to the frame you need outer. • Then get the variable you want
Some Observations • Label each procedure with its static nesting level (in our example, outer = 1, inner = 2) • You can only see one stack frame at most at each static level • The one you can see is the nearest one in the dynamic call chain. • The one you can see always statically encloses you at some level.
More Observations • A level N procedure can call • Another level N procedure • A procedure at level 1 .. N-1 • This is an uplevel call • A procedure at level N+1 • This is a downlevel call • You cannot call a level N+2 procedure
The Uplevel Addressing Problem • If you are an N level procedure, you can reference your own variables at level N • The variables of the immediately enclosing N-1 level procedure • The variables of the immediately enclosing N-2 level procedure • Etc.
How to Find Up Level Variables • For a level N procedure • We need the address of the level N frame • That’s the local frame pointer, no problem • The address of the nearest enclosing N-1 level frame (statically enclosing us) • The address of the nearest enclosing N-2 level frame (statically enclosing us)
One Solution, Static Links • Pass an extra param on every call • Called the static link • If you are calling a level N procedure, this is the address of the nearest level N-1 procedure frame • This static link is stored in the local stack frame at a fixed offset that everyone knows.
How to Use The Static Link • To address your own variables, use the frame pointer. • To address at N-1, use the static link • To address at N-2, use the static link to locate the N-1 frame, and then load its static link to locate the N-2 frame • Etc.
How to Set The Static Link • Assume you are a level N procedure • To call a level N+1 procedure, pass your frame pointer • To call a level N procedure, pass a copy of the static link that was given to you siblings must share the same static parent) • To call a level Q procedure (Q < N), find the address of the level Q-1 frame and pass that (see previous slide).
More on Static Links • When calling a global procedure (level 1) • No need to pass a static link • In practice many calls are to global procedures • The expression that fetches a level Q static link can be recognized as a CSE • And for example, if many variables are referenced at level 3, the level 3 frame pointer can end up in a local register.
Another Solution: Global Display • Maintain a global table • Length of table = max static nesting level of program (can set a reasonable max/measure) • Within a level N procedure, entries 1-N are defined, Nth entry is copy of frame pointer. • Entry Q is frame pointer for statically enclosing level Q procedure = most recently called level Q procedure
Using the Global Display • To access local variables in level Q procedure: • Simply use the Q’th entry of the global display as the frame pointer
Maintaining The Global Display • Nothing needs to be done by caller • No additional parameter passed • The called level N procedure on entry: • Establish frame pointer as usual • Save N’th level entry of global display • Set N’th level entry to copy of frame ptr • The called level N procedure on exit • Restore N’th level entry of global display
Notes on Global Display • In a tasking program, you need a separate global display for each task (not needed with static link method) • Global display can be stored in registers • But that’s not a very good use of registers, better solution is to simply let frequently referenced entries from display migrate into registers as needed.
Optimizing Global Display • If a procedure has no nested procedures, then no one will ever need its entry in the display, so don’t bother to set it. And of course that means no need to restore. • The invariant then becomes • N’th entry of global display contains the frame pointer of the most recently called procedure that contains at least one nested procedure. • So overhead only for calling a procedure with at least one nested procedure.
Local Displays • Classical Algol method • Within each stack frame keep a local display consisting of entries 1 to N of a global display value.
Using a Local Display • To access variables in the current level N procedure, use frame pointer as usual, which is the same as level N of the local display. • To access variables in level Q (Q < N) procedure, access Qth entry of local display.
Maintaining Local Display • When entering a level N procedure, you are being called from a procedure at level N-1 or higher (never from N-2). • That means that the callers stack frame always has the N-1 entries you need for your local display, so copy them. • Establish a frame pointer as usual, and store as the N’th entry of the local display.
Procedure Parameters • Go back to our Inner/Outer example • Suppose that both inner and outer are recursive • Suppose that both inner and outer pass procedure parameters • A given procedure value is not just a code address, but also the environment at the time that it is passed.
More on Procedure Values • Basically a procedure value is a pointer to the code of the procedure, as well as the status of the invocation stack for any up level references it might have. • Form of procedure value depends on the way that up level referencing is handled.
Storing a Procedure Value • For static links, we just need the address of the procedure frame, since the static links from that frame can recreate the necessary up level addressing information. • For local display, the procedure frame is also sufficient, since the entire up level addressing information is stored in frame. • For global display, need at least entries 1..N-1 of the global display to be stored
Comparison of Methods • Local Display • Never used any more • Clearly dominated by other methods • Too bad no one told Intel • X86 implements local display in hardware • Enter x,4 means call a 4th level procedure and includes copying the local display from the stack frame of the caller to the stack frame of the called function.s
Static Links vs Global Display • Static Links • No overhead for calling global procedure • Efficient representation of procedure value • Global Display • No overhead for calling procedure with no nested procedures • Awkward representation of procedure value
Static Links vs Global Display • Global display probably more efficient • Only large procedures have nested procedures inside • All leaf procedures have no nested procedures • But mess with procedure parameters nasty • Ada is carefully designed so that no call to a procedure parameter ever modifies the display, so that displays need not be kept.
Closures • This entire model assumes that stack frames are created on entry, and destroyed on exit from a procedure in a stack like manner (hence the name) • This works for almost everything, but there is one case which is a problem
Upward funarg problem • Suppose that a procedure outer has a return type that is itself a procedure • Suppose this procedure outer has a nested procedure, inner • Suppose that outer returns inner as the returned result • Suppose that inner references a variable of outer
An Example • MODE INTP = PROC (REAL) REAL;PROC compose = (INTP a, b) INTP : (REAL q) REAL : a (b (q));INTP sqrtsin = compose (sqrt, sin);REAL result = sqrtsin (0.345); • The body of sqrtsin references local parameters a, b from an enclosing proc • By the time we call sqrtsin, these parameters are long gone
Closures in Algol-68 • Are not implemented in full generality • In particular cannot have upward funargs • Allows simple use of stack frames
How to Implement Closures • Allocate stack frames on heap • Don’t remove stack frames on leaving a procedure • That way things stay around and are available to reference when needed • Need general garbage collection to clean up stack frames eventually