CS453 Colorado State University ============================================== Register Allocation ============================================== ------------------------ Register Allocation for Locals (1) Do a pass through method to see how many callee-saved registers are used for method calls. (2) Map locals to left-over callee-saved registers. If a callee-saved register is mapped to a local than it is considered used by the method. (3) In method prologue, push all used callee-saved registers on the stack. (4) In IdExp nodes, check if a variable has been allocated a register. If yes then just push value from register(s) to the stack. If no then load value from stack base+offset and then push value to the stack. (5) In assignment statements, check if a variable has been allocated to a register. If yes, then pop right hand side expression from stack into the registers assigned to the variable. If no, pop right hand side expression from stack and the store into stack base+offset for var. (6) In method epilogue, pop all used callee-saved registers in reverse order of stores in prologue. ------------------------ Register Allocation for Parameters Still need to do steps (1) through (6) above. The only difference is that in the method prologue you will also have to move the incoming parameter values into the registers allocated for those parameters instead of putting them in a stack base+offset. ------------------------ Register Allocation for Expressions - Currently we generate AVR code that evaluates expressions on a stack. - Can we bound the space needed on a stack to evaluate expressions? - do some example expressions this.foo(a+b, c-d-e, f - (g - (h-i)) ); - Yes, by looking at static shape of all expression trees. Height of those trees is the max number of temps. What is height to the right? - Can we implement the stack with registers? - Look at examples again and try it. - NO, there is a fundamental difference between registers and arrays because registers are statically indexed. - We can assign temp ids, or temp "variables", to the result of each expression. Temps that are not live at the same time can be assigned the same register. Another way of doing this is assigning the same temp id to expressions that are not live at the same time. A value is live at runtime if it has not yet been consumed by any computation that plans to read it. -> When is an expression no longer live? Implementing register allocation for expressions (1) Do a pass through method to see how many callee-saved registers are used for method calls. --> (1b) Could combine the above pass with a pass that maps expressions to temp ids. (2) Map params and locals to left-over callee-saved registers. If a callee-saved register is mapped to a local then it is considered used by the method. --> (2a) Map temporary ids for expressions to callee-saved registers. If a callee-saved register is mapped to a temp id then it is considered used by the method. Compose the map of expressions to temp ids and the map of temp ids to registers to map expressions to registers. (3) In method prologue, push all used callee-saved registers on the stack. Also copy all incoming params to allocated register or memory location. (4) In IdExp nodes, check if a variable has been allocated a register. If yes then just push value from register(s) to the stack. If no then load value from stack base+offset and then push value to the stack. --> (4b) In all AST nodes that have a child that is an expression, check if expression has been allocated a register. If so use value in register, if not then pop from stack into a register. --> (4c) In all expression AST nodes. Store the result of the expression in the allocated register if there is one or push the value onto the stack. (5) In assignment statements, check if a variable has been allocated to a register. If yes, then pop right hand side expression from stack into the registers assigned to the variable. If no, pop right hand side expression from stack and the store into stack base+offset for var. (6) In method epilogue, pop all used callee-saved registers in reverse order of stores in prologue. ------------------------ Some suggested helper routines // This routine generates the code that will load the // given variable into the backup register and its // pair or will indicate what register was allocated // for this variable. // If the variable being loaded is only a one byte // type, then only the lo bit register will be non-null. private RegPair genLoadVar(VarSTE varste, int backupReg) { RegPair retval = this.getVarReg(varste); if (retval == null) { retval = new RegPair("r"+backupReg,"r"+(backupReg+1)); // Generate code to load var value into backup reg out.println(); if (varste.getType().getAVRTypeSize()==2) { out.println(" # load a two byte variable from base+offset"); out.println(" ldd "+retval.hibitReg+", " + varste.getBase() + " + " + (varste.getOffset()+1)); out.println(" ldd "+retval.lobitReg+", " + varste.getBase() + " + " + varste.getOffset()); } else { out.println(" # load a one byte variable from base+offset"); out.println(" ldd "+retval.lobitReg+", " + varste.getBase() + " + " + varste.getOffset()); } } else { out.println(" # var "+varste.getName()+" is in "+retval.lobitReg); } return retval; } // This routine generates the code that will load the // given expression into the backup register and its // pair or will indicate what register was allocated // for this expression. // If the expression being loaded is only a one byte // type, then only the lo bit register will be non-null. private RegPair genLoadExp(Node node, int backupReg) { RegPair retval = this.getExpReg(node); if (retval == null) { retval = new RegPair("r"+backupReg,"r"+(backupReg+1)); // Generate code to load value into backup reg if (this.mCurrentST.getExpType(node).getAVRTypeSize()==2) { out.println(" # load a two byte expression off stack"); out.println(" pop "+retval.lobitReg); out.println(" pop "+retval.hibitReg); } else { out.println(" # load a one byte expression off stack"); out.println(" pop "+retval.lobitReg); } } return retval; } /** * This routine generates the avr code that stores * the value(s) in the srcReg into the registers * assigned to the given expression node or onto the stack * if the expression node has not been assigned to a register. * This routine will use the type of the expression to determine * whether the value is 1 byte or two. * * @param node expression node in AST * @param srcReg register pair where value current lives */ private void genStoreExp(Node node, RegPair srcReg) { RegPair regpair = this.getExpReg(node); // If the expression does not have a reg // then push srcReg values onto the stack. if (regpair == null) { // Generate code to push value onto stack if (this.mCurrentST.getExpType(node).getAVRTypeSize()==2) { out.println(" # push two byte expression onto stack"); out.println(" push "+srcReg.hibitReg); out.println(" push "+srcReg.lobitReg); } else { out.println(" # push one byte expression onto stack"); out.println(" push "+srcReg.lobitReg); } // Otherwise, move the values into the expr register(s) } else if (! regpair.lobitReg.equals(srcReg.lobitReg)) { this.genMoveReg(regpair, srcReg); } } ------------------------ mstrout@cs.colostate.edu, 4/21/11