Skip to content
Snippets Groups Projects
Verified Commit afecd941 authored by Recolic Keghart's avatar Recolic Keghart
Browse files

merged

parents 2948129d a2074e51
No related branches found
No related tags found
1 merge request!1merge init works
Pipeline #295 passed with warnings with stages
in 24 minutes and 15 seconds
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");
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment