CSC362 2004S, Class 29: Stack Frames, Continued Admin: * Thoughts on yesterday's lab? * Homework due! * Questions on project, part 3? * Quiz! Overview: * Handling scope * Dynamic * Static chains * Static tables public class PC { public static void main(String[] args) { CharStream ch = new CharStream(args[0]); Tokens tokens = new PascalTokenizer(ch); Node tree = PascalParser.parse(tokens); TypeChecker.check(tree); IntCode code = CodeGenerator.generate(tree); CodeImprover.improve(code); RegisterAllocator.allocate(code); ... } } How should we represent the types? * Associate a "type" characteristic with many (or perhaps all) nodes * How do we represent scope when generating code? Keep track of nesting levels. Observation: * We know where global variables are stored (in fixed locations) * We know where variables local to the current procedure are stored (as offsets on the stack frame) * How can we access variables that belong to our enclosing scopes? Question: How can we provide bar with access to foo's a? procedure foo; var a: integer; procedure bar; begin a := 2; ... end; procedure zebra; var a: integer; begin bar; ... end; begin ... end; Which a does bar set to 2? foo's a or zebra's a? * Static scoping: The procedure that encloses bar (foo's) * Dynamic scoping: The most recently *defined* a at run time; Walk up the stack to check "call path" (zebra's) * Which is preferable? * Dynamic: 'Easier': You don't know how far back to look * Static: Easier to write a syntax for; Easier for the programmer * In Pascal: Statically scoped Question: How can we provide bar with access to foo's a? * Arjun claims "You don't know how far back on the stack the stack foo's a is" Davis: The callee has the address of the caller's stack frame so ... * Climb up the stack until you find 'an a' (dynamic) * Climb up the stack until you find foo and use it's a (static) Strategy Davis-2 seems slow: You may have to climb up the stack a lot Strategy Davis-2 requires us to know which procedure each stack frame belongs to Strategy Davis-1 has the problem that you don't have the names of the variables on the stack (Not a problem in Davis-2, since we know where in a foo stack frame a belongs) Josh's strategy: Don't nest your procedures Advantage: An easy solution Disadvantage: Not Pascal Choed's strategy: Copy all the local variables from stack frame to stack frame * Uses a *lot* of space * Requires a lot of computation for copying Paul's strategy: Pass a pointer to the base of a parent frame * How do we implement this * Sibling calling sibling or self: Easy: Just copy your parent pointer * Parent calling child: Pass your own frame pointer * Child calls parent, aunt, uncle: Figure out the parent's parent pointer * Child calling ancestor: Similar to the parent * Ancestors cannot call non-immediate descendants How do we access a non-local variable? * Follow the parent pointer an appropriate number of levels up the stack and offset from there Another strategy (implemented in the i86 call operation) * Put pointers to *all* ancestors in the stack frame An Example /An Example/ function factorial(n: integer): integer; var tmp: integer; begin if (n = 0) then factorial := 1 else begin tmp := factorial(n-1); factorial := n * tmp; end end; The frame The code