/* * File: * irtframe.c * Author: * Samuel A. Rebelsky, based on code from Andrew Appel * Version: * 0.5 of 16 December 1998 * Description: * A very simple implementation of frames for a ``straight-line'' * IRT code used as a project in CSC362 1998 Fall at Grinnell College. * Frame structure * M[FP]: Return address * M[FP-1]: Pointer to previous frame * M[FP-...]: Parameters * History: * 07 December 1998 (version 0.3): * Got rid of the dependency on assem.h * 12 December 1998 (version 0.4) * Added computation of the view shift (only when needed) * to procEntryExit1. * Filled in details for a lot of functions. * 16 December 1998 (version 0.5) * Filled in details of procEntryExit3. * Added the slconcat function as a utility. */ /*********************************************************** * Headers * ***********/ #include #include "util.h" #include "symbol.h" #include "absyn.h" #include "temp.h" #include "tree.h" #include "frame.h" #include "irtframe.h" /*********************************************************** * Constants * *************/ /* * The size of words (in bytes). * Green book, p. 159. */ const int F_wordSize = 4; /*********************************************************** * Globals * ***********/ /*********************************************************** * Local Functions * *******************/ /* * Build an access list, given the head and tail. */ F_accessList F_AccessList(F_access head, F_accessList tail) { F_accessList list = checked_malloc(sizeof(struct F_accessList_)); list->head = head; list->tail = tail; return list; } /* F_AccessList(F_access, F_AccessList) */ /* * Add the formals. Done as a separate function so that I can build * the list in the correct order (which requires putting the first thing * in the list of escapes at the front, which suggests that I do recursion). */ static void addFormals(F_frame frame, U_boolList formals) { /* Here's the lovely new variable. */ F_access newformal; /* Are we done? */ if (formals == NULL) { return; } /* Allocate the lovely new variable. */ newformal = F_allocLocal(frame, formals->head); /* Recurse */ addFormals(frame, formals->tail); /* Add the head to the list of formals. */ frame->formals = F_AccessList(newformal, frame->formals); } /* addFormals() */ /* * Allocate the frame. This involves * a. storing the old frame pointer in the current stack frame * b. copying the stack pointer to the frame pointer * c. updating the frame pointer * We could also attempt to check whether we've overflowed memory, * but let's leave that for another time. */ static T_stmList allocFrame(F_frame frame) { return T_StmList(T_Move(T_Mem(T_Binop(T_minus,T_Temp(F_SP()),T_Const(1))), T_Temp(F_FP())), T_StmList(T_Move(T_Temp(F_FP()), T_Temp(F_SP())), T_StmList(T_Move(T_Temp(F_SP()), T_Binop(T_minus, T_Temp(F_SP()), T_Const(frame->allocated))), NULL))); } /* allocFrame */ /* * Concatenate two statement lists. Destructively updates the * first statement list. */ static T_stmList slconcat(T_stmList s1, T_stmList s2) { T_stmList orig = s1; if (s1 == NULL) return s2; /* Keep going until we reach the end of the list. */ while (s1->tail != NULL) { s1 = s1->tail; } /* Now add the other list */ s1->tail = s2; /* And we're done. */ return orig; } /* slconcat() */ /* * Create a new access for a variable stored in a register. */ static F_access inreg(F_frame frame) { /* Allocate the access and set its kind. */ F_access reg = (F_access) checked_malloc(sizeof(struct F_access_)); reg->kind = F_inRegister; /* Fill in the new temporary. */ reg->u.reg = Temp_newtemp(); /* And that's it. */ return reg; } /* inreg(void) */ /* * Create a new access for a variable stored in the frame. */ static F_access inframe(F_frame frame) { /* Allocate the access and set its kind. */ F_access var = (F_access) checked_malloc(sizeof(struct F_access_)); var->kind = F_inFrame; /* Determine the offset in the current frame (in bytes). Note that frames grow downward, so we use a negative offset. */ var->u.offset = -1*(frame->allocated); /* Update the amount of space allocated in the current frame. */ frame->allocated += 1; /* And that's it. */ return var; } /* inframe(F_frame) */ /* * Restore the stack at the end of the function call. This involves * a. updating the stack pointer to the frame pointer * b. retrieving the frame pointer from memory. */ static T_stmList restoreStack(F_frame frame) { return T_StmList(T_Move(T_Temp(F_FP()), T_Mem(T_Binop(T_minus,T_Temp(F_FP()),T_Const(1)))), T_StmList(T_Move(T_Temp(F_SP()), T_Temp(F_FP())), (T_stmList) NULL)); } /* restoreStack() */ /* * Restore the key registers (return address, etc.) */ static T_stm restoreRegs() { /* The return address is stored in the first word of the frame. */ return T_Move(T_Temp(F_RA()), T_Mem(T_Temp(F_FP()))); } /* restoreRegs() */ /* * Save the key registers (return address, etc.) */ static T_stm saveRegs() { /* The return address is stored in the first word of the frame. */ return T_Move(T_Mem(T_Temp(F_FP())), T_Temp(F_RA())); } /* saveRegs() */ /* * Compute the view shift. The formalnum parameter gives the * number of the next formal (starting with 0). */ static T_stm viewShift(F_accessList formals, int formalnum) { if (formals == NULL) return T_Exp(T_Const(111)); return T_Seq(T_Move(F_Exp(formals->head, T_Temp(F_FP())), T_Temp(F_Arg(formalnum))), viewShift(formals->tail, formalnum+1)); } // viewShift /*********************************************************** * Exported Functions * **********************/ /* * Create a new frame, given information about the parameters to * the function. The booleans indicate whether or not the parameters * "escape" (must be stored in the frame, rather than in registers). * Green book, p. 136. */ F_frame F_newFrame(Temp_label name, U_boolList formals) { /* Allocate that puppy! */ F_frame newf = (F_frame) checked_malloc(sizeof(struct F_frame_)); /* Fill in the fields. */ /* Fill in the name. Thankfully, it's been supplied for us. We might also use Temp_newLabel(). */ newf->name = name; /* How many words are initially allocated? Let's do one for the return address and one for the old frame pointer.. */ newf->allocated = 2; /* How many formals do we have? Initially, none. */ newf->formals = NULL; /* A useless view shift, but it may be useful to have something. */ /* Add the formals. */ addFormals(newf, formals); /* And that's it. */ return newf; } /* F_newFrame */ /* * Get the label associated with the frame. Used when calling the * function. (It's not clear that this really belongs here, but * that's how Appel designed it.) * Green book, p. 136. */ Temp_label F_name(F_frame f) { return f->name; } /* F_name */ /* * Get the locations associated with the parameters. Presumably, * this is only used by the stuff that manipulates the body of the * corresponding function. * Green book, p. 136. */ F_accessList F_formals(F_frame f) { return f->formals; } /* F_formals */ /* * Allocate a new local storage location, which might be a register * or space on the frame (or ....). In doing so, indicate whether it * must be stored in the frame. * Green book, p. 136. */ F_access F_allocLocal(F_frame f, bool escapes) { if (escapes) { return inframe(f); } else { return inreg(f); } } /* F_allocLocal */ /* * The standard registers on the machine. Should only be needed * for register allocation and therefore not implemented. * Appears in Green book, p. 268. Defined earlier p. 267. */ Temp_tempList F_registers(void) { return NULL; } /* F_registers */ /* * Compute the appropriate IRT for an access. This might be a TEMP, * or it might be a memory location. Requires the qualified base of * the frame, which is computed at a different level (by translate.c). * Green book, p. 159. */ T_exp F_Exp(F_access acc, T_exp framePtr) { /* If it's a register, just return the register. */ if (acc->kind == F_inRegister) { return T_Temp(acc->u.reg); } /* If it's an offset, return the memory location given at that particular offset from the frame pointer). */ else { return T_Mem(T_Binop(T_minus, framePtr, T_Const(acc->u.offset))); } } /* F_Exp */ /* * Get the frame pointer. * Green book, p. 159. */ Temp_temp F_FP(void) { return Temp_numtemp(3); } /* F_FP */ /* * Get the return-address register. * Green book, p. 268. Defined earlier? */ Temp_temp F_RA(void) { return Temp_numtemp(7); } /* F_RA */ /* * Get the return-value register. * Green book, p. 172. */ Temp_temp F_RV(void) { return Temp_numtemp(6); } /* F_RV */ /* * Get the stack pointer. * Green book, p. 268. Defined earlier? */ Temp_temp F_SP(void) { return Temp_numtemp(2); } /* F_SP */ /* * Some special temporary? Perhaps register 0? * Green book, p. 268. Defined earlier? */ Temp_temp F_ZERO(void) { return Temp_numtemp(0); } /* F_ZERO */ /* * Call an external function (one of the built-in functions). * Green book, p. 168. */ T_exp F_externalCall(string s, T_expList args) { return T_Call(T_Name(Temp_namedlabel(s)), args); } /* F_externalCall */ /* * Create the IRT for the start and end of the frame. This includes * saving parameters and saving registers. The associated statement * is the body of the function (I hope). * Green book, p. 172 and 267. */ T_stm F_procEntryExit1(F_frame frame, T_stm stm) { #ifdef SIMPLE return T_Seq(viewShift(frame->formals), stm); #else return T_Seq(viewShift(frame->formals, 0), /* p. 171, step 4. */ T_Seq(saveRegs(), /* p. 171, step 5. */ T_Seq(stm, /* p. 171, steps 6. and 7. */ restoreRegs()))); /* p. 171, step 8. */ #endif } /* F_procEntryExit1 */ /* * Add ``sink'' instructions to the function body so that the register * allocator can determine which registers are live at procedure exit. * Note that this does not seem to rely on the current frame. * Green book, p. 215. * * Because we're working with IRT ``statements'' rather than assmebly code * statements, this returns a T_stmList. */ T_stmList F_procEntryExit2(T_stmList body) { return body; } /* F_procEntryExit2 */ /* * Create assembly code for the start and the end of the frame. * Handles sizing the frame. * Green book, p. 269. * * Because we're working with IRT ``statements'' rather than assmebly code * statements, this returns a T_stmList. * * Note that this is not yet implemented, but some of the framework * is now present. */ T_stmList F_procEntryExit3(F_frame frame, T_stmList body) { return slconcat(T_StmList(T_Label(frame->name), /* p. 171, step 2. */ (T_stmList) NULL), slconcat(allocFrame(frame), /* p. 171, step 3. */ slconcat(body, /* p. 171, steps 4-8. */ slconcat(restoreStack(frame), /* p. 171, step 9. */ T_StmList(T_Return(), /* p. 171, step 10. */ (T_stmList) NULL) )))); } /* F_procEntryExit3 */ /* * Create a new fragment corresponding to a string. * Green book, p. 172. */ F_frag F_StringFrag(Temp_label label, string str) { F_frag newfrag = checked_malloc(sizeof (*newfrag)); newfrag->kind = F_stringFrag; newfrag->u.stringg.label = label; newfrag->u.stringg.str = str; return newfrag; } /* F_StringFrag */ /* * Create a new fragment corresopnding to a procedure body. * Green book, p. 173. */ F_frag F_ProcFrag(T_stm body, F_frame frame) { F_frag newfrag = checked_malloc(sizeof (*newfrag)); newfrag->kind = F_procFrag; newfrag->u.proc.body = body; newfrag->u.proc.frame = frame; return newfrag; } /* F_ProcFrag */ /* * Create a new list of fragments. */ F_fragList F_FragList(F_frag head, F_fragList tail) { F_fragList newlist = checked_malloc(sizeof (*newlist)); newlist->head = head; newlist->tail = tail; return newlist; } /* F_FragList */ /*********************************************************** * Special Functions * *********************/ /* * The following are functions that I added to ease creation of * ``straight-line'' IRT code. They may not be appropriate for * all implementations. They may also be moved to irtframe.h. */ /* * Get the program counter. */ Temp_temp F_PC(void) { return Temp_numtemp(1); } /* * Get the heap pointer. */ Temp_temp F_HP(void) { return Temp_numtemp(4); } /* * Get the accumulator. */ Temp_temp F_ACC(void) { return Temp_numtemp(5); } /* * Get an argument register. Argument registers are numbered 0 through 9. */ Temp_temp F_Arg(int num) { return Temp_numtemp(10+num); }