diff --git a/src/main/java/chocopy/pa3/CodeGenImpl.java b/src/main/java/chocopy/pa3/CodeGenImpl.java index 3334f8d22e2c130342566046101bba54f065fc2e..b2919bc2ddcefb8f435c0b08f4ee60d1e2327582 100644 --- a/src/main/java/chocopy/pa3/CodeGenImpl.java +++ b/src/main/java/chocopy/pa3/CodeGenImpl.java @@ -1,13 +1,11 @@ package chocopy.pa3; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.*; import chocopy.common.analysis.SymbolTable; import chocopy.common.analysis.AbstractNodeAnalyzer; import chocopy.common.analysis.types.SymbolType; -import chocopy.common.analysis.types.ValueType; import chocopy.common.astnodes.*; import chocopy.common.codegen.*; @@ -15,12 +13,12 @@ import static chocopy.common.codegen.RiscVBackend.Register.*; /** * This is where the main implementation of PA3 will live. - * + * <p> * A large part of the functionality has already been implemented * in the base class, CodeGenBase. Make sure to read through that * class, since you will want to use many of its fields * and utility methods in this class when emitting code. - * + * <p> * Also read the PDF spec for details on what the base class does and * what APIs it exposes for its sub-class (this one). Of particular * importance is knowing what all the SymbolInfo classes contain. @@ -71,27 +69,33 @@ emitJ($someFunc$saveRegContinue) public class CodeGenImpl extends CodeGenBase { private class BetterRsicVBackend { private final RiscVBackend backend; + BetterRsicVBackend(RiscVBackend _backend) { backend = _backend; } + public void emitNoop(String comment) { - if(comment != null) { + if (comment != null) { backend.emitMV(ZERO, ZERO, "Noop: " + comment); } } + public void emitPush(RiscVBackend.Register reg, String comment) { backend.emitADDI(SP, SP, -1 * backend.getWordSize(), comment); backend.emitSW(reg, SP, 0, comment); } + public void emitPop(RiscVBackend.Register reg, String comment) { - if(reg != null) + if (reg != null) backend.emitLW(reg, SP, 0, comment); backend.emitADDI(SP, SP, backend.getWordSize(), comment); } + public void emitCall(Label calledLabel, String comment) { // Arguments should be already pushed to stack. backend.emitJAL(calledLabel, comment); } + public void emitFunctionBegin(String funcName, String comment) { emitNoop(comment); emitPush(RA, "backup my return address."); @@ -101,13 +105,14 @@ public class CodeGenImpl extends CodeGenBase { backend.emitJ(new Label("$" + funcName + "$saveReg"), "Jump to save callee-saved registers"); backend.emitLocalLabel(new Label("$" + funcName + "$saveRegContinue"), "Begin real function codes:"); } + public void emitFunctionEnd(String funcName, registerManager regMgr, String comment) { // Return value should be in A0 List<RiscVBackend.Register> savedRegs = regMgr.registerToSaveAndRestoreInFunc; emitNoop(comment); - backend.emitADDI(SP, FP, -(2+savedRegs.size()) * backend.getWordSize(), "Revert all local variables on this dying frame."); - for (int i = savedRegs.size(); i --> 0; ) { + backend.emitADDI(SP, FP, -(2 + savedRegs.size()) * backend.getWordSize(), "Revert all local variables on this dying frame."); + for (int i = savedRegs.size(); i-- > 0; ) { emitPop(savedRegs.get(i), "restore callee-saved reg."); } emitPop(FP, "restore parent FP."); @@ -115,11 +120,12 @@ public class CodeGenImpl extends CodeGenBase { backend.emitJR(RA, "Real return!"); backend.emitLocalLabel(new Label("$" + funcName + "$saveReg"), "Save callee-saved registers before use."); - for(int i = 0; i < savedRegs.size(); ++i) { + for (int i = 0; i < savedRegs.size(); ++i) { emitPush(savedRegs.get(i), "save callee-saved reg."); } backend.emitJ(new Label("$" + funcName + "$saveRegContinue"), "go back to execute real function code."); } + public void emitPushIntVal(RiscVBackend.Register tmpReg, Integer val, String comment) { emitNoop(comment); backend.emitLI(tmpReg, val, "INT VAL"); @@ -132,6 +138,7 @@ public class CodeGenImpl extends CodeGenBase { emitPush(tmpReg, null); backend.emitMV(tmpReg, SP, "Return INT address."); } + public void emitPushBoolVal(RiscVBackend.Register tmpReg, Boolean val, String comment) { emitNoop(comment); backend.emitLI(tmpReg, val ? 1 : 0, "BOOL VAL"); @@ -144,18 +151,19 @@ public class CodeGenImpl extends CodeGenBase { emitPush(tmpReg, null); backend.emitMV(tmpReg, SP, "Return BOOL address."); } + public void emitPushStrVal(RiscVBackend.Register tmpReg, String val, String comment) { emitNoop(comment); - byte [] bytes = val.getBytes(StandardCharsets.US_ASCII); + byte[] bytes = val.getBytes(StandardCharsets.US_ASCII); int dataLen = bytes.length / backend.getWordSize() + 1; // Equals to ceil((len+1) DIVIDE WORD_SIZE) - for(int cter = 0; cter < dataLen; ++cter) { - int myRangeBegin = (dataLen-cter-1) * backend.getWordSize(); + for (int cter = 0; cter < dataLen; ++cter) { + int myRangeBegin = (dataLen - cter - 1) * backend.getWordSize(); int myRangeEnd = Math.min(myRangeBegin + backend.getWordSize(), bytes.length); int curr = 0; assert backend.getWordSize() == 4; // curr should be 4 byte = int String debug_pushed_str = ""; - for(int shift = 0; shift < myRangeEnd - myRangeBegin; ++shift) { + for (int shift = 0; shift < myRangeEnd - myRangeBegin; ++shift) { curr += bytes[myRangeBegin + shift] << (shift * 8); debug_pushed_str += Integer.toString(bytes[myRangeBegin + shift]) + " "; } @@ -215,6 +223,7 @@ public class CodeGenImpl extends CodeGenBase { private Map<RiscVBackend.Register, Boolean> callerSavedUsageMap; // true if the reg is in use. private Map<RiscVBackend.Register, Boolean> calleeSavedUsageMap; // true if the reg is in use. public List<RiscVBackend.Register> registerToSaveAndRestoreInFunc; + registerManager() { callerSavedUsageMap = new LinkedHashMap<>(); calleeSavedUsageMap = new LinkedHashMap<>(); @@ -251,8 +260,8 @@ public class CodeGenImpl extends CodeGenBase { // For converience: we call caller-saved register as `temp register`, // call callee-saved register as `persist register` or `saved register`, because they should be saved before the function. public RiscVBackend.Register borrowOneTmp() { - for(RiscVBackend.Register reg : callerSavedUsageMap.keySet()) { - if(callerSavedUsageMap.put(reg, true) == false) { + for (RiscVBackend.Register reg : callerSavedUsageMap.keySet()) { + if (callerSavedUsageMap.put(reg, true) == false) { return reg; } } @@ -260,9 +269,9 @@ public class CodeGenImpl extends CodeGenBase { } public RiscVBackend.Register borrowOnePersist() { - for(RiscVBackend.Register reg : calleeSavedUsageMap.keySet()) { - if(calleeSavedUsageMap.put(reg, true) == false) { - if(!registerToSaveAndRestoreInFunc.contains(reg)) + for (RiscVBackend.Register reg : calleeSavedUsageMap.keySet()) { + if (calleeSavedUsageMap.put(reg, true) == false) { + if (!registerToSaveAndRestoreInFunc.contains(reg)) registerToSaveAndRestoreInFunc.add(reg); return reg; } @@ -271,7 +280,7 @@ public class CodeGenImpl extends CodeGenBase { } public void returnOne(RiscVBackend.Register reg) { - if(calleeSavedUsageMap.containsKey(reg)) + if (calleeSavedUsageMap.containsKey(reg)) assert calleeSavedUsageMap.put(reg, false); else assert callerSavedUsageMap.put(reg, false); @@ -291,7 +300,9 @@ public class CodeGenImpl extends CodeGenBase { */ } - /** A code generator emitting instructions to BACKEND. */ + /** + * A code generator emitting instructions to BACKEND. + */ public CodeGenImpl(RiscVBackend backend) { super(backend); betterBackend = new BetterRsicVBackend(backend); @@ -299,21 +310,27 @@ public class CodeGenImpl extends CodeGenBase { private BetterRsicVBackend betterBackend; - /** Operation on None. */ + /** + * Operation on None. + */ private final Label errorNone = new Label("error.None"); - /** Division by zero. */ + /** + * Division by zero. + */ private final Label errorDiv = new Label("error.Div"); - /** Index out of bounds. */ + /** + * Index out of bounds. + */ private final Label errorOob = new Label("error.OOB"); /** * Emits the top level of the program. - * + * <p> * This method is invoked exactly once, and is surrounded * by some boilerplate code that: (1) initializes the heap * before the top-level begins and (2) exits after the top-level * ends. - * + * <p> * You only need to generate code for statements. * * @param statements top level statements @@ -321,12 +338,11 @@ public class CodeGenImpl extends CodeGenBase { protected void emitTopLevel(List<Stmt> statements) { StmtAnalyzer stmtAnalyzer = new StmtAnalyzer(null); backend.emitADDI(SP, SP, -2 * backend.getWordSize(), - "Saved FP and saved RA (unused at top level)."); + "Saved FP and saved RA (unused at top level)."); backend.emitSW(ZERO, SP, 0, "Top saved FP is 0."); backend.emitSW(ZERO, SP, 4, "Top saved RA is 0."); backend.emitADDI(FP, SP, 2 * backend.getWordSize(), - "Set FP to previous SP."); - + "Set FP to previous SP."); for (Stmt stmt : statements) { stmt.dispatch(stmtAnalyzer); } @@ -336,7 +352,7 @@ public class CodeGenImpl extends CodeGenBase { /** * Emits the code for a function described by FUNCINFO. - * + * <p> * This method is invoked once per function and method definition. * At the code generation stage, nested functions are emitted as * separate functions of their own. So if function `bar` is nested within @@ -359,10 +375,14 @@ public class CodeGenImpl extends CodeGenBase { backend.emitLocalLabel(stmtAnalyzer.epilogue, "Epilogue"); // FIXME: {... reset fp etc. ...} + backend.emitLW(RA, FP, 0, "Restore RA"); + backend.emitLW(FP, FP, 4, "Restore old FP"); backend.emitJR(RA, "Return to caller"); } - /** An analyzer that encapsulates code generation for statments. */ + /** + * An analyzer that encapsulates code generation for statments. + */ private class StmtAnalyzer extends AbstractNodeAnalyzer<RiscVBackend.Register> { /* * The symbol table has all the info you need to determine @@ -395,20 +415,26 @@ public class CodeGenImpl extends CodeGenBase { * appropriate info for the var that is currently in scope. */ - /** Symbol table for my statements. */ + /** + * Symbol table for my statements. + */ private SymbolTable<SymbolInfo> sym; - /** Label of code that exits from procedure. */ + /** + * Label of code that exits from procedure. + */ protected Label epilogue; - /** The descriptor for the current function, or null at the top - * level. */ + /** + * The descriptor for the current function, or null at the top + * level. + */ private FuncInfo funcInfo; private registerManager regMgr = new registerManager(); /* This method allocates object on the heap using the prototype - * Return register containing address */ + * Return register containing address */ private RiscVBackend.Register allocPrototype(SymbolType typ, RiscVBackend.Register objReg) { RiscVBackend.Register storeAddr = regMgr.borrowOneTmp(); // Convention: if typ is INT_TYPE then objReg contains the literal value, not a pointer. @@ -425,8 +451,10 @@ public class CodeGenImpl extends CodeGenBase { return null; } - /** An analyzer for the function described by FUNCINFO0, which is null - * for the top level. */ + /** + * An analyzer for the function described by FUNCINFO0, which is null + * for the top level. + */ StmtAnalyzer(FuncInfo funcInfo0) { funcInfo = funcInfo0; if (funcInfo == null) { @@ -445,14 +473,35 @@ public class CodeGenImpl extends CodeGenBase { // This is here just to demonstrate how to emit a // RISC-V instruction. RiscVBackend.Register returnValReg = node.value.dispatch(this); - if(returnValReg == null) + if (returnValReg == null) return null; backend.emitMV(A0, returnValReg, "put return value!"); regMgr.returnOne(returnValReg); return null; // Statement always return null. } - // FIXME: More, of course. + @Override + public RiscVBackend.Register analyze(BooleanLiteral literal) { + /* Push boolean literal onto stack, incrementing stack pointer*/ + RiscVBackend.Register res = regMgr.borrowOneTmp(); + backend.emitLA(res, constants.getBoolConstant(literal.value), "Load boolean literal"); + return res; + } + + @Override + public RiscVBackend.Register analyze(IntegerLiteral literal) { + RiscVBackend.Register res = regMgr.borrowOneTmp(); + backend.emitLA(res, constants.getIntConstant(literal.value), "Load boolean literal"); + return res; + } + + @Override + public RiscVBackend.Register analyze(StringLiteral literal) { + RiscVBackend.Register res = regMgr.borrowOneTmp(); + backend.emitLA(res, constants.getStrConstant(literal.value), "Load string literal"); + return res; + } + @Override public RiscVBackend.Register analyze(ExprStmt node) { node.expr.dispatch(this); @@ -462,30 +511,29 @@ public class CodeGenImpl extends CodeGenBase { @Override public RiscVBackend.Register analyze(CallExpr node) { SymbolInfo called = sym.get(node.function.name); - if(called instanceof FuncInfo) { + if (called instanceof FuncInfo) { FuncInfo func = (FuncInfo) called; // TODO: Push the FUCKING arg0 (outter function frame ptr) List<RiscVBackend.Register> args_reg = new ArrayList<>(); - for(Expr expr : node.args) { + for (Expr expr : node.args) { RiscVBackend.Register result = expr.dispatch(this); - if(result == null) { + if (result == null) { throw new RuntimeException("NotImplemented: Expression " + expr.getClass().getName() + " returns null register."); } args_reg.add(result); } - for(RiscVBackend.Register reg : args_reg) { + for (RiscVBackend.Register reg : args_reg) { betterBackend.emitPush(reg, "Push function arguments."); regMgr.returnOne(reg); } betterBackend.emitCall(func.getCodeLabel(), null); - for(Object reg : args_reg) + for (Object reg : args_reg) betterBackend.emitPop(null, "Pop function arguments."); return A0; // Function return value is always in A0. - } - else if(called instanceof ClassInfo) { + } else if (called instanceof ClassInfo) { // TODO: Fuck the fucking framework. } return null; @@ -494,18 +542,18 @@ public class CodeGenImpl extends CodeGenBase { @Override public RiscVBackend.Register analyze(BinaryExpr node) { RiscVBackend.Register leftReturnReg = node.left.dispatch(this); - if(leftReturnReg == null) return null; + if (leftReturnReg == null) return null; RiscVBackend.Register leftRes = regMgr.borrowOnePersist(); backend.emitMV(leftRes, leftReturnReg, "Move left operand result to a callee saved register"); regMgr.returnOne(leftReturnReg); // because right operand may overwrite temp registers. RiscVBackend.Register rightReturnReg = node.right.dispatch(this); RiscVBackend.Register rightRes = rightReturnReg; - if(rightReturnReg == null) + if (rightReturnReg == null) return null; - switch(node.operator) { + switch (node.operator) { case "+": - if(node.left.getInferredType().equals(SymbolType.INT_TYPE)) { + if (node.left.getInferredType().equals(SymbolType.INT_TYPE)) { RiscVBackend.Register savedLeftAddr = regMgr.borrowOneTmp(); backend.emitMV(savedLeftAddr, leftRes, "Backup reg leftRes"); backend.emitLW(leftRes, leftRes, 3 * backend.getWordSize(), "Operator+ Fetch left int result"); @@ -516,16 +564,16 @@ public class CodeGenImpl extends CodeGenBase { regMgr.returnOne(leftRes); return savedLeftAddr; } - if(node.left.getInferredType().isListType()) { + if (node.left.getInferredType().isListType()) { // TODO: Merge two list. throw new RuntimeException("NOTIMPLEMENTED: ListMerge"); } - if(node.left.getInferredType().equals(SymbolType.STR_TYPE)) { + if (node.left.getInferredType().equals(SymbolType.STR_TYPE)) { // TODO: Merge two str. throw new RuntimeException("NOTIMPLEMENTED: StrMerge"); } case "-": - if(node.left.getInferredType().equals(SymbolType.INT_TYPE)) { + if (node.left.getInferredType().equals(SymbolType.INT_TYPE)) { RiscVBackend.Register savedLeftAddr = regMgr.borrowOneTmp(); backend.emitMV(savedLeftAddr, leftRes, "Backup reg leftRes"); backend.emitLW(leftRes, leftRes, 3 * backend.getWordSize(), "Operator- Fetch left int result"); @@ -537,7 +585,7 @@ public class CodeGenImpl extends CodeGenBase { return savedLeftAddr; } case "*": - if(node.left.getInferredType().equals(SymbolType.INT_TYPE)) { + if (node.left.getInferredType().equals(SymbolType.INT_TYPE)) { RiscVBackend.Register savedLeftAddr = regMgr.borrowOneTmp(); backend.emitMV(savedLeftAddr, leftRes, "Backup reg leftRes"); backend.emitLW(leftRes, leftRes, 3 * backend.getWordSize(), "Operator* Fetch left int result"); @@ -558,10 +606,10 @@ public class CodeGenImpl extends CodeGenBase { switch (node.operator) { case "-": - RiscVBackend.Register savedOpAddr = regMgr.borrowOneTmp(); RiscVBackend.Register savedResult = regMgr.borrowOnePersist(); backend.emitMV(savedOpAddr, operandReg, "Save operand addr in register"); + // TODO FIXME NOOOOO! YOU"RE DOINT THINGS WRONGLY. I'll complete the merge and fix it later. if (node.operand instanceof IntegerLiteral) { backend.emitLW(operandReg, operandReg, 3 * backend.getWordSize(), "Load operand value"); } else if (node.operand instanceof Identifier) { @@ -572,69 +620,46 @@ public class CodeGenImpl extends CodeGenBase { regMgr.returnOne(operandReg); regMgr.returnOne(savedOpAddr); regMgr.returnOne(savedResult); - return heapObject; } return null; } + @Override public RiscVBackend.Register analyze(Identifier node) { SymbolInfo id = sym.get(node.name); - if(id instanceof GlobalVarInfo) { + if (id instanceof GlobalVarInfo) { GlobalVarInfo globalVarInfo = (GlobalVarInfo) id; RiscVBackend.Register tmpReg = regMgr.borrowOneTmp(); backend.emitLA(tmpReg, globalVarInfo.getLabel(), "Load address of the global var."); - return tmpReg; + return tmpReg; // FIXME: The global var contains a bare int rather than a INT OBJECT> } // FIXME: not implemented return null; } - - @Override - public RiscVBackend.Register analyze(IntegerLiteral node) { - // emitConstant(node, ValueType.INT_TYPE, "Set constant int literal."); - RiscVBackend.Register tmpReg = regMgr.borrowOneTmp(); - betterBackend.emitPushIntVal(tmpReg, node.value, "Push int literal"); - return tmpReg; - } - - @Override - public RiscVBackend.Register analyze(BooleanLiteral node) { - RiscVBackend.Register tmpReg = regMgr.borrowOneTmp(); - betterBackend.emitPushBoolVal(tmpReg, node.value, "Push bool literal"); - return tmpReg; - } - - @Override - public RiscVBackend.Register analyze(StringLiteral node) { - RiscVBackend.Register tmpReg = regMgr.borrowOneTmp(); - betterBackend.emitPushStrVal(tmpReg, node.value, "push string literal."); - return tmpReg; - } } /** * Emits custom code in the CODE segment. - * + * <p> * This method is called after emitting the top level and the * function bodies for each function. - * + * <p> * You can use this method to emit anything you want outside of the * top level or functions, e.g. custom routines that you may want to * call from within your code to do common tasks. This is not strictly * needed. You might not modify this at all and still complete * the assignment. - * + * <p> * To start you off, here is an implementation of three routines that * will be commonly needed from within the code you will generate * for statements. - * + * <p> * The routines are error handlers for operations on None, index out * of bounds, and division by zero. They never return to their caller. * Just jump to one of these routines to throw an error and * exit the program. For example, to throw an OOB error: - * backend.emitJ(errorOob, "Go to out-of-bounds error and abort"); - * + * backend.emitJ(errorOob, "Go to out-of-bounds error and abort"); */ protected void emitCustomCode() { emitErrorFunc(errorNone, "Operation on None"); @@ -642,14 +667,16 @@ public class CodeGenImpl extends CodeGenBase { emitErrorFunc(errorOob, "Index out of bounds"); } - /** Emit an error routine labeled ERRLABEL that aborts with message MSG. */ + /** + * Emit an error routine labeled ERRLABEL that aborts with message MSG. + */ private void emitErrorFunc(Label errLabel, String msg) { backend.emitGlobalLabel(errLabel); backend.emitLI(A0, ERROR_NONE, "Exit code for: " + msg); backend.emitLA(A1, constants.getStrConstant(msg), - "Load error message as str"); + "Load error message as str"); backend.emitADDI(A1, A1, getAttrOffset(strClass, "__str__"), - "Load address of attribute __str__"); + "Load address of attribute __str__"); backend.emitJ(abortLabel, "Abort"); } }