diff --git a/run.sh b/run.sh index f1b231f6ac71a60726de66e7d05c7cdc12dc6574..61a640d6437c511e95c450be64a75590ebb1b26f 100755 --- a/run.sh +++ b/run.sh @@ -8,6 +8,8 @@ classPath="chocopy-ref.jar:target/assignment.jar" if [[ "$task" = codegen ]]; then java -cp "$classPath" chocopy.ChocoPy --pass=rrs --out "$output" "$input" +elif [[ "$task" = codegen-ref ]]; then + java -cp "$classPath" chocopy.ChocoPy --pass=rrr --out "$output" "$input" elif [[ "$task" = ass ]]; then java -cp "$classPath" chocopy.ChocoPy --pass=..s --run --dir src/test/data/pa3/sample/ --test | tee /tmp/cs164_ass.log # Should pass all positive test, else return error_code=2 diff --git a/src/main/java/chocopy/pa3/CodeGenImpl.java b/src/main/java/chocopy/pa3/CodeGenImpl.java index 08cb23f6a73eef7d1ec60f379c2a368c1970c03a..43acc8fe92ba7d6387496eae4255af03fc90b016 100644 --- a/src/main/java/chocopy/pa3/CodeGenImpl.java +++ b/src/main/java/chocopy/pa3/CodeGenImpl.java @@ -26,6 +26,47 @@ import static chocopy.common.codegen.RiscVBackend.Register.*; * importance is knowing what all the SymbolInfo classes contain. */ +/* Recolic func manage logic: +IMPORTANT!!! NEVER DELETE THESE COMMENTS + +emitPush(arg1) +emitPush(arg2) +emitCall("someFunc") +emitPop(null) +emitPop(null) + +$someFunc: + +-------- emitFunctionBegin ------ +emitPush(RA) // save My RA +emitPush(FP) // save parent FP // that's a good fucking design +emitMv(FP, SP+2*WORD) // load My FP +emitJ($someFunc$saveReg) +$someFunc$saveRegContinue: +-------- emitFunctionBegin ------ + +some code... +using S1... +using S2... +calling others... +... +MV A0, returnValue // IMPORTANT! + +-------- emitFunctionEnd ------ +emitMv(SP, FP-4*WORD) // 2 WORD for FP and RA, and 2 WORD for register S1 and S2. +emitPop(S2) +emitPop(S1) +emitPop(FP) // restore parent's FP +emitPop(RA) // restore My RA +emitJR(RA) // return! +$someFunc$saveReg: +emitPush(S1) +emitPush(S2) +emitJ($someFunc$saveRegContinue) +-------- emitFunctionEnd ------ + */ + + public class CodeGenImpl extends CodeGenBase { private class BetterRsicVBackend { @@ -47,30 +88,37 @@ public class CodeGenImpl extends CodeGenBase { backend.emitLW(reg, SP, 0, comment); backend.emitADDI(SP, SP, backend.getWordSize(), comment); } - public void emitSavedFPCall(Label calledLabel, String 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 return address of parent."); + emitPush(RA, "backup my return address."); emitPush(FP, "backup parent fp."); backend.emitADDI(FP, SP, 2 * backend.getWordSize(), "Change FP to apply new frame."); - // jal will set RA register properly. - backend.emitJAL(calledLabel, "Call it!"); - - backend.emitADDI(SP, FP, -2 * backend.getWordSize(), "Revert all local variables on this dying frame."); - emitPop(FP, "restore parent FP."); - emitPop(RA, "restore parent return address."); + backend.emitJ(new Label("$" + funcName + "$saveReg"), "Jump to save callee-saved registers"); + backend.emitLocalLabel(new Label("$" + funcName + "$saveRegContinue"), "Begin real function codes:"); } - public void emitCall(Label calledLabel, String comment) { - // Arguments should be already pushed to stack. - emitNoop(comment); - backend.emitJAL(calledLabel, "Call it!"); - } - public void emitRet(String comment) { - // All local variable should already be poped. + public void emitFunctionEnd(String funcName, registerManager regMgr, String comment) { // Return value should be in A0 + List<RiscVBackend.Register> savedRegs = regMgr.registerToSaveAndRestoreInFunc; + emitNoop(comment); - backend.emitJR(RA, "Return to caller."); + 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."); + emitPop(RA, "restore my return address."); + 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) { + 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); @@ -163,29 +211,58 @@ public class CodeGenImpl extends CodeGenBase { // --- pop S0 ; pop S1 --- // --- restore FP, return --- private class registerManager { - private Map<RiscVBackend.Register, Boolean> usageMap; // true if the reg is in use. + 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() { - usageMap = new LinkedHashMap<>(); - usageMap.put(T0, false); - usageMap.put(T1, false); - usageMap.put(T2, false); - usageMap.put(T3, false); - usageMap.put(T4, false); - usageMap.put(T5, false); - usageMap.put(T6, false); - usageMap.put(A1, false); - usageMap.put(A2, false); - usageMap.put(A3, false); - usageMap.put(A4, false); - usageMap.put(A5, false); - usageMap.put(A6, false); - usageMap.put(A7, false); - // usageMap.put(A0, false); // used as return. not managed + callerSavedUsageMap = new LinkedHashMap<>(); + calleeSavedUsageMap = new LinkedHashMap<>(); + + callerSavedUsageMap.put(T0, false); + callerSavedUsageMap.put(T1, false); + callerSavedUsageMap.put(T2, false); + callerSavedUsageMap.put(T3, false); + callerSavedUsageMap.put(T4, false); + callerSavedUsageMap.put(T5, false); + callerSavedUsageMap.put(T6, false); + callerSavedUsageMap.put(A1, false); + callerSavedUsageMap.put(A2, false); + callerSavedUsageMap.put(A3, false); + callerSavedUsageMap.put(A4, false); + callerSavedUsageMap.put(A5, false); + callerSavedUsageMap.put(A6, false); + callerSavedUsageMap.put(A7, false); + // callerSavedUsageMap.put(A0, false); // used as return. not managed + + calleeSavedUsageMap.put(S1, false); + calleeSavedUsageMap.put(S2, false); + calleeSavedUsageMap.put(S3, false); + calleeSavedUsageMap.put(S4, false); + calleeSavedUsageMap.put(S5, false); + calleeSavedUsageMap.put(S6, false); + calleeSavedUsageMap.put(S7, false); + calleeSavedUsageMap.put(S8, false); + calleeSavedUsageMap.put(S9, false); + + registerToSaveAndRestoreInFunc = new ArrayList<>(); + } + + // 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) { + return reg; + } + } + throw new RuntimeException("Too many variable, not enough register."); } - public RiscVBackend.Register borrowOne() { - for(RiscVBackend.Register reg : usageMap.keySet()) { - if(usageMap.put(reg, true) == false) { + public RiscVBackend.Register borrowOnePersist() { + for(RiscVBackend.Register reg : calleeSavedUsageMap.keySet()) { + if(calleeSavedUsageMap.put(reg, true) == false) { + if(!registerToSaveAndRestoreInFunc.contains(reg)) + registerToSaveAndRestoreInFunc.add(reg); return reg; } } @@ -193,9 +270,24 @@ public class CodeGenImpl extends CodeGenBase { } public void returnOne(RiscVBackend.Register reg) { - boolean old = usageMap.put(reg, false); - assert old == true; + if(calleeSavedUsageMap.containsKey(reg)) + assert calleeSavedUsageMap.put(reg, false); + else + assert callerSavedUsageMap.put(reg, false); } + + /* + public void generateCodeToBackupReg() { + for(RiscVBackend.Register reg : registerToSaveAndRestoreInFunc) { + betterBackend.emitPush(reg, "Backup used callee saved register in this func."); + } + } + public void generateCodeToRestoreReg() { + for(RiscVBackend.Register reg : registerToSaveAndRestoreInFunc) { + betterBackend.emitPop(reg, "Restore backuped callee saved registers."); + } + } + */ } /** A code generator emitting instructions to BACKEND. */ @@ -254,11 +346,14 @@ public class CodeGenImpl extends CodeGenBase { backend.emitGlobalLabel(funcInfo.getCodeLabel()); StmtAnalyzer stmtAnalyzer = new StmtAnalyzer(funcInfo); + betterBackend.emitFunctionBegin(funcInfo.getFuncName(), "BEGIN FUNCTION"); + for (Stmt stmt : funcInfo.getStatements()) { stmt.dispatch(stmtAnalyzer); } backend.emitMV(A0, ZERO, "Returning None implicitly"); + betterBackend.emitFunctionEnd(funcInfo.getFuncName(), stmtAnalyzer.regMgr, "BEGIN FUNCTION"); backend.emitLocalLabel(stmtAnalyzer.epilogue, "Epilogue"); // FIXME: {... reset fp etc. ...} @@ -345,15 +440,9 @@ public class CodeGenImpl extends CodeGenBase { SymbolInfo called = sym.get(node.function.name); if(called instanceof FuncInfo) { FuncInfo func = (FuncInfo) called; - if(func.getFuncName() != "print") { - throw new RuntimeException("Not implemented: emitCall doesn't allow calling user-defined function, because RA will be messed up."); - // TODO: analyze the reference compiler, to see why $print use 0(SP) as arg0, and how to call it with RA saved. - // https://piazza.com/class/jr5kaiy0u71499?cid=234 - } // TODO: Push the FUCKING arg0 (outter function frame ptr) List<RiscVBackend.Register> args_reg = new ArrayList<>(); for(Expr expr : node.args) { - // TODO: See register allocator TODOS. RiscVBackend.Register result = expr.dispatch(this); if(result == null) { throw new RuntimeException("NotImplemented: Expression " + expr.getClass().getName() + " returns null register."); @@ -380,14 +469,20 @@ public class CodeGenImpl extends CodeGenBase { @Override public RiscVBackend.Register analyze(BinaryExpr node) { - RiscVBackend.Register leftRes = node.left.dispatch(this); - RiscVBackend.Register rightRes = node.right.dispatch(this); - if(leftRes == null || rightRes == null) + RiscVBackend.Register leftReturnReg = node.left.dispatch(this); + 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) return null; switch(node.operator) { case "+": if(node.left.getInferredType().equals(SymbolType.INT_TYPE)) { - RiscVBackend.Register savedLeftAddr = regMgr.borrowOne(); + RiscVBackend.Register savedLeftAddr = regMgr.borrowOneTmp(); backend.emitMV(savedLeftAddr, leftRes, "Backup reg leftRes"); backend.emitLW(leftRes, leftRes, 3 * backend.getWordSize(), "Operator+ Fetch left int result"); backend.emitLW(rightRes, rightRes, 3 * backend.getWordSize(), "Operator+ Fetch right int result"); @@ -407,7 +502,7 @@ public class CodeGenImpl extends CodeGenBase { } case "-": if(node.left.getInferredType().equals(SymbolType.INT_TYPE)) { - RiscVBackend.Register savedLeftAddr = regMgr.borrowOne(); + RiscVBackend.Register savedLeftAddr = regMgr.borrowOneTmp(); backend.emitMV(savedLeftAddr, leftRes, "Backup reg leftRes"); backend.emitLW(leftRes, leftRes, 3 * backend.getWordSize(), "Operator- Fetch left int result"); backend.emitLW(rightRes, rightRes, 3 * backend.getWordSize(), "Operator- Fetch right int result"); @@ -419,7 +514,7 @@ public class CodeGenImpl extends CodeGenBase { } case "*": if(node.left.getInferredType().equals(SymbolType.INT_TYPE)) { - RiscVBackend.Register savedLeftAddr = regMgr.borrowOne(); + RiscVBackend.Register savedLeftAddr = regMgr.borrowOneTmp(); backend.emitMV(savedLeftAddr, leftRes, "Backup reg leftRes"); backend.emitLW(leftRes, leftRes, 3 * backend.getWordSize(), "Operator* Fetch left int result"); backend.emitLW(rightRes, rightRes, 3 * backend.getWordSize(), "Operator* Fetch right int result"); @@ -438,7 +533,7 @@ public class CodeGenImpl extends CodeGenBase { SymbolInfo id = sym.get(node.name); if(id instanceof GlobalVarInfo) { GlobalVarInfo globalVarInfo = (GlobalVarInfo) id; - RiscVBackend.Register tmpReg = regMgr.borrowOne(); + RiscVBackend.Register tmpReg = regMgr.borrowOneTmp(); backend.emitLA(tmpReg, globalVarInfo.getLabel(), "Load address of the global var."); return tmpReg; } @@ -449,21 +544,21 @@ public class CodeGenImpl extends CodeGenBase { @Override public RiscVBackend.Register analyze(IntegerLiteral node) { // emitConstant(node, ValueType.INT_TYPE, "Set constant int literal."); - RiscVBackend.Register tmpReg = regMgr.borrowOne(); + 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.borrowOne(); + 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.borrowOne(); + RiscVBackend.Register tmpReg = regMgr.borrowOneTmp(); betterBackend.emitPushStrVal(tmpReg, node.value, "push string literal."); return tmpReg; }