package chocopy.pa3; import chocopy.common.analysis.AbstractNodeAnalyzer; import chocopy.common.analysis.SymbolTable; import chocopy.common.astnodes.ReturnStmt; import chocopy.common.astnodes.Stmt; import chocopy.common.codegen.*; import java.util.List; import static chocopy.common.codegen.RiscVBackend.Register.*; /** * This is where the main implementation of PA3 will live. * *
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. * *
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. */ public class CodeGenImpl extends CodeGenBase { /** A code generator emitting instructions to BACKEND. */ public CodeGenImpl(RiscVBackend backend) { super(backend); } /** Operation on None. */ private final Label errorNone = new Label("error.None"); /** Division by zero. */ private final Label errorDiv = new Label("error.Div"); /** Index out of bounds. */ private final Label errorOob = new Label("error.OOB"); /** * Emits the top level of the program. * *
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. * *
You only need to generate code for statements.
*
* @param statements top level statements
*/
protected void emitTopLevel(List 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 function `foo`, you only emit `foo`'s code for `foo` and only emit `bar`'s
* code for `bar`.
*/
protected void emitUserDefinedFunction(FuncInfo funcInfo) {
backend.emitGlobalLabel(funcInfo.getCodeLabel());
StmtAnalyzer stmtAnalyzer = new StmtAnalyzer(funcInfo);
for (Stmt stmt : funcInfo.getStatements()) {
stmt.dispatch(stmtAnalyzer);
}
backend.emitMV(A0, ZERO, "Returning None implicitly");
backend.emitLocalLabel(stmtAnalyzer.epilogue, "Epilogue");
// FIXME: {... reset fp etc. ...}
backend.emitJR(RA, "Return to caller");
}
/** An analyzer that encapsulates code generation for statements. */
private class StmtAnalyzer extends AbstractNodeAnalyzer This method is called after emitting the top level and the function bodies for each
* function.
*
* 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.
*
* 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.
*
* 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");
*/
protected void emitCustomCode() {
emitErrorFunc(errorNone, "Operation on None");
emitErrorFunc(errorDiv, "Division by zero");
emitErrorFunc(errorOob, "Index out of bounds");
}
/** 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");
backend.emitADDI(
A1, A1, getAttrOffset(strClass, "__str__"), "Load address of attribute __str__");
backend.emitJ(abortLabel, "Abort");
}
}