CS212 Spring 2002 3/6/2002 Lecture 7: Introduction to Part 3 ------------------------------------------------------------------------------- [0] Announcements: + floppy disks only accepted in lecture + remember to submit online in time + late assignments will be graded with penalty + reading: Chapters 5,7 ------------------------------------------------------------------------------- [1] Summary: + sam-code for procedural portions of a langauge (Bali) + compiler converts instructions into sam-code, assuming everything in main ------------------------------------------------------------------------------- [2] Overview: + what if statements involve multiple functions? + what if statements call other functions for expressions? + Part 3 involves: - writing Bali functions in sam-code ("the stack") - extending the compiler from Part 2 to handle control structures in main + Part 4? - compile functions into sam-code - maybe Bali++ ("the heap") From CS211 notes: Memory map for modern languages: +-------------------------------------------+ | | | +--------------+ +--------------+ | | | static area | | program area | | | +--------------+ +--------------+ | | | | +--------------+ +--------------+ | | | stack | | heap | | | +--------------+ +--------------+ | | | +-------------------------------------------+ ------------------------------------------------------------------------------- [3] Functions: + mathematical: mapping, like f:domain->coDomain + practical: "useful" math, like sqrt(4), sin(x) + programming: tasks, like doSomething(stuff) + SaM: activation record? Note: what's the difference between routine, procedure, function, method, subroutine, subfunction? see http://foldoc.doc.ic.ac.uk/foldoc ------------------------------------------------------------------------------- [4] Activation Record: + ACTIVATION RECORD (lifted from http://foldoc.doc.ic.ac.uk/foldoc) "A data structure containing the variables belonging to one particular scope (e.g. a procedure body), as well as links to other activation records. Activation records are usually created (on the stack) on entry to a block and destroyed on exit." Other terms: DATA FRAME, STACK FRAME, FRAME (these tend to be interchanged) + CALL STACK/"THE STACK": collection of frames + Why stacks and frames in the stack? reminder of organization of memory map (see also "Ur-Java" notes from CS211): - Program area: sam-code - Stack: activation records for functions + Frame: a portion of the stack for a function - think of each frame as a rectangular "box" with segments for vars/params and other values - executing a function means pushing a frame onto the CALL STACK (or just STACK, as we've been saying) - each frame holds the info need for execution - the frame is erased when function is finished + Process: - each function invocation gets a frame (built collaboratively with caller and callee) - frame contains params, local vars, and book-keeping info - new invocations go on top of old ones (and thus, build the call stack) (a function X that calls Y puts the frame for Y above X) ------------------------------------------------------------------------------- [5] Depiction of Frame: + example function: int dis(int x, int y) { // I added parameter types, which are not in Bali bool a, b; // statements; // return something; } + drawing a frame: - need technique for knowing which function is currently being executed - each function has its own "stuff" (things like variables) which must be stored in the stack relative to their frame - parameters/variables need to get storage locations - bookkeeping: + return address: location to return when function is done + FBR (frame based register): discussed in [6] + depiction: +----+ SP ---> 7 | | temporaries: pushed and popped values for computations +----+ 6 | | local variable +----+ 5 | | local variable +----+ 4 | | return address +----+ 3 | | function parameter +----+ 2 | | function parameter +----+ FBR ---> 1 | | saved frame based register +----+ 0 | | rv: space for return variable +----+ ------------------------------------------------------------------------------- [6] Frame Based Register: + what "part" of sam-code did relative addressing? FBR! + FBR: Frame Based Register - register that always points to the bottom of the topmost frame on the stack * so, rv is actually the top of the previous frame ! * - provides means to address parameters/variables in relative fashion (relates to the idea of the scope of local vars: they "live" in a function, which means they're addressed according to the frame in which they're stored) + What's in FBR? - The location in the frame that the FBR points to contains the contents of the FBR before the topmost frame was constructed. - When the function invocation returns, the value of the FBR is restored from that location. + default value: starts at 0 (as with many other, but not all, registers) ------------------------------------------------------------------------------- [7] Example of Building a Frame: + Notes: - CALLER: the portion of code invoking a function - CALLEE: the function being called - invoking a function will build a frame - when the function is completed, the frame is dismantled and a return value is left on top of the previous frame + Assume a function call f(10,20) where f(int x,int y) { return x+y; } (1) Allocate space for the return value of function call: - PUSHIMM 0 or ADDSP 1 +---+ | | <-- SP +----+ +---+ | <-+- the current value of the FBR | 0 | +----+ +---+ (2) Save the current value of FBR on the stack: - the default initial value is 0 in general - use PUSHFBR to push the current value of FBR on top of the stack - the SAVED FBR lets SaM restore the "old" (or previous) FBR when the current callee is finished (the saved FBR is the caller's FBR) +---+ | | <-- SP +---+ FBR | F | F is an integer +---+ +---+ | F | | 0 | +---+ +---+ (3) Evaluate each argument that's supplied to the function f: - push each value as it's computed - our example has PUSHIMM 10 and PUSHIMM 20 +----+ SP --> | | 4+v the "+v" means that there are +----+ cells in the stack below the current frame | 20 | 3+v +----+ new frame being built | 10 | 2+v ^ +----+ | | F | 1+v | +----+ ------------------------------------ RV --> | 0 | 0+v | +----+ V previous frame (4) Set the FBR to the "bottom" of the partially constructed frame: - the RV slot is technically part of the CALLER's frame - the new value of FBR should be the address of the location on the stack where the FBR was saved in Step (2) - to find the new FBR, ( SP - (# of Params+1) ): PUSHSP // push SP value to top PUSHIMM 3 // push 1+number of params SUB // ( SP - (# of Params+1) ) POPFBR // pop the top value and store in FBR (FBR <- Vtop) +----+ SP --> | | calling POPFBR causes FBR <- SP-3 +----+ | 3 | FBR +----+ +------+ | SP | | SP-3 | +----+ +------+ | 20 | +----+ new frame being built | 10 | ^ +----+ | | F | | +----+ ------------------------------------ RV --> | 0 | | +----+ V previous frame (5) Jump to the callee: - push the address of the instruction to which to return when the invocation ends - assume that first instruction of each function is labeled with the name of the function - use JSR label, as in JSR f + JSR: Jump to subroutine + JSR x: push PC+1; PC<-x (translation: go to address of function, save return address) (going to the function's address means that SaM goes to the (first instruction below the label x in your sam-code) +----+ SP --> | | +----+ RA --> | f | return address: each sam-code instruction has a number +----+ the PC register keeps track of that number | 20 | +----+ new frame being built | 10 | ^ +----+ | | F | | +----+ ------------------------------------ RV --> | 0 | | +----+ V previous frame (6) Execute the callee code and return to caller when done: // label of first command is function name label: // allocate local variables (params already taken care of!) ADDSP n // // code in body // // return code: // store return value: STOREOFF -1 // pop off local variables: ADDSP -n // return to caller JUMPIND ------------------------------------------------------------------------- reminder of STOREOFF: STOREOFF x <- V[x+FBR]<-V[top] (puts top value in stack in rv slot at the top of the PREVIOUS frame -- you could almost say "bottom of frame", but that's technically incorrect) JUMPIND: go back to return address (f) and pop it off the stack ------------------------------------------------------------------------- sam-code? ========= PUSHOFF 1 // retrieve the "10" for x PUSHOFF 2 // retrieve the "20" for y ADD // x+y STOREOFF -1 // return x+y +----+ SP --> | | +----+ | 20 | +----+ new frame being built | 10 | ^ +----+ | | F | | +----+ ------------------------------------ RV --> | 30 | | +----+ V previous frame (7) Remove remnants of newly created frame: - pop off parameters - restore the FBR (POPFBR, which moves the FBR to the location that the saved FBR cell stores) - only the return value remains ADDSP -2 // remove params POPFBR // FBR<-V[top] (FBR now points to "old" FBR value) +----+ | RV --> | 30 | | +----+ | previous frame V FBR --> somewhere down here, pointing to bottom of previous frame ------------------------------------------------------------------------------- [8] Complete sam-code for the example in [7] (w/o main) PUSHIMM 0 // leave space for rv PUSHFBR // push the current value of FBR PUSHIMM 10 // push param PUSHIMM 20 // push param PUSHSP ---- PUSHIMM 3 \_____ set up FBR: FBR now points to bottom of current frame SUB / POPFBR ---- JSR f // jump to function (jump to callee) ADDSP -2 // remove params POPFBR // restore FBR (the FBR gets its previous value) f: // code for callee (the function that gets called) // ADDSP n not needed because no local vars besides formal params PUSHOFF 1 // get x PUSHOFF 2 // get y STOREOFF -1 // store x+y in rv JUMPIND // return to caller ------------------------------------------------------------------------------- [9] Generic sam-code: PROGRAM: // loading starts at address 0 PUSHIMM 0 PUSHFBR PUSHIMM 1 POPFBR JSR main POPFBR STOP // code for all other functions, starting with main FUNCTION: // label of first command is function name label: // allocate local variables (params already taken care of!) ADDSP n // code in body // return code (see next segment): STOREOFF -1 // pop off local variables: ADDSP -n // return to caller JUMPIND RETURN CODE (giving a bit more detail): // evaluate return value // store return value into "bottom" slot STOREOFF -1 // takes into account that the FBR is 1 "higher" than rv // pop off local variables: ADDSP -n JUMPIND FUNCTION INVOCATION (calling function f): // make space for rv and saved FBR PUSHIMM 0 // or ADDSP 1 PUSHFBR // push params // set up new FBR for n params: PUSHSP PUSHIMM n+1 SUB POPFBR // call function f JSR f // pop params and restore "old" FBR (move FBR to previous saved FBR) ADDSP -n POPFBR -------------------------------------------------------------------------------