CSC362.L01 2004S, Class 37: Translating Procedure Calls Admin * Sorry about yesterday. * Deal with the groups. * Questions on translation? * Variable declarations: Can we treat them differently? Yes. Must we treat them differently? No. So they can be on a "base stack frame"? Yes. * If we decide to go with an extra stack frame, do we have to do some tricky stuff to keep track of what's going on? (E.g., an extra real global variable that keeps track of the start of globals, or passing the start of globals as "the parent"?) Yes. * Can we have a global BOS (bottom of stack, not Logan airport)? * Do stacks grow from high memory to low or from low memory to high? Nevermind. Assume they grow from high addresses to low addresses. * Can you write imod, too? Yes. * Whose type checker do we use? Fight about it. * What if we don't have a working type checker? Ask Arjun/Choed/Oge for theirs or Patty/Ananta/Shihan for theirs. * What are the best ways to give procedures access to variables in enclosing scopes? * Memory efficient but painful and time inefficient: Pass a frame-of-parent pointer and follow those pointers up the appropriate number of levels. * Memory inefficient but easier and time efficient: Determine the maximum level of procedure nesting and provide that many "ancestor pointers" (parent, grandparent, great-grandparent, ..., globals) in each stack frame. Overview: * Format of stack frames. * Allocating space for locals and parameters. * Division of responsibilities (between caller and callee). * Function/procedure initialization. * Function/procedure cleanup. * Caller responsibilities. Stack frames: * previous frame pointer * parent/ancestor frame pointer(s) * return location * return value * parameters and locals * temporaries * perhaps other stuff * How do we arrange the frames for easy use? * Right before a procedure call 832 +-----+ <- fp | | 814 +-----+ <- sp | | * Goal (while we are executing the called procedure): 832 +-----+ | | 814 +-----+ <- fp |REST.| |STUFF| +-----+ <- sp * Goal (after we have finished the called procedure): 832 +-----+ <- fp | | 814 +-----+ <- sp | | * How do we eaily arrange this? * Use registers? * Put the old (caller's) frame pointer next on the stack 832 +-----+ | | 814 +-----+ fp: |oldfp| |STUFF| |param| sp: |STUFF| +-----+ * How do we restore the old frame and stack pointers? MOVE fp -> sp # Move the stack pointer to the frame pointer MOVE MEM[sp] -> fp # Move the old frame pointer to the frame pointer CALLEE INITIALIZE: PUSH fp MOVE sp -> fp CLEANUP: MOVE fp -> sp # Move the stack pointer to the frame pointer POP fp # Move the old frame pointer to the frame pointer CALLER: PREPARATION-FOR-CALL: Push parameters Push room for the return value Push other stuff Push RETURN-FROM-CALL JUMP CALLEE RETURN-FROM-CALL Grab the return value (so we don't forget it) - Copy to a temporary Pop everything we pushed Advantages? It's what our compiler seems to do. Can make variable-parameter procedures easier to handle. Side note: There are many ways to arrange the stack frame. This is one that we're developing today. You can use others. * Additional idea: Have parameters be "above" frame pointer. * Who is responsible for arranging what part of the frame? Side note: If you move the "Save SP and FP/Restore SP and FP" to the caller, you can simplify some things (e.g., Side note: You can decide to return a value in a designated register rather than on the stack. How do you figure out where the parameters and local variables are? * If we've used the "parameters above fp" technique |paramn| .. |param1| |param0| |retadd| fp: |oldfp | vs. |param0| |param1| .. |paramn| |retadd| fp: |oldfp | Advantage of second: You can push them as they appear. Advantage of first? Easier for variable-arity procedures. If you make the first choice and have parameters above the frame pointer, then the ith parameter is at something like MEM[fp-1-i*sizeofvalues] Local variables: * Traverse the declaration and keep track of how much space you have allocated on the stack. At each declaration, note the current offset (space allocated the stack), store in symbol table, and increment the offset.