compiler-design-eth/src/cd/backend/codegen/StmtGenerator.java

370 lines
10 KiB
Java

package cd.backend.codegen;
import cd.Config;
import cd.backend.codegen.RegisterManager.Register;
import cd.ir.Ast;
import cd.ir.Ast.*;
import cd.ir.AstVisitor;
import cd.ir.CompileTimeEvaluator;
import cd.ir.ExprVisitor;
import cd.ir.Symbol.MethodSymbol;
import cd.ir.Symbol.PrimitiveTypeSymbol;
import cd.util.Pair;
import cd.util.debug.AstOneLine;
import java.util.List;
import static cd.backend.codegen.AssemblyEmitter.arrayAddress;
import static cd.backend.codegen.RegisterManager.BASE_REG;
import static cd.backend.codegen.RegisterManager.STACK_REG;
/**
* Generates code to process statements and declarations.
*/
class StmtGenerator extends AstVisitor<Register, Void> {
protected final AstCodeGenerator cg;
StmtGenerator(AstCodeGenerator astCodeGenerator) {
cg = astCodeGenerator;
}
public void gen(Ast ast) {
visit(ast, null);
}
@Override
public Register visit(Ast ast, Void arg) {
try {
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
return super.visit(ast, arg);
} finally {
cg.emit.decreaseIndent();
}
}
public Register methodCall(MethodSymbol sym, List<Expr> allArguments) {
throw new RuntimeException("Not required");
}
}
/*
* StmtGenerator with the reference solution
*/
class StmtGeneratorRef extends StmtGenerator {
/* cg and cgRef are the same instance. cgRef simply
* provides a wider interface */
protected final AstCodeGeneratorRef cgRef;
StmtGeneratorRef(AstCodeGeneratorRef astCodeGenerator) {
super(astCodeGenerator);
this.cgRef = astCodeGenerator;
}
@Override
public Register methodCall(MethodSymbol mthSymbol, List<Expr> allArgs) {
// Push the arguments and the method prefix (caller save register,
// and padding) onto the stack.
// Note that the space for the arguments is not already reserved,
// so we just push them in the Java left-to-right order.
//
// After each iteration of the following loop, reg holds the
// register used for the previous argument.
int padding = cgRef.emitCallPrefix(null, allArgs.size());
Register reg = null;
for (int i = 0; i < allArgs.size(); i++) {
if (reg != null) {
cgRef.rm.releaseRegister(reg);
}
reg = cgRef.eg.gen(allArgs.get(i));
cgRef.push(reg.repr);
}
// Since "this" is the first parameter that push
// we have to get it back to resolve the method call
cgRef.emit.emitComment("Load \"this\" pointer");
cgRef.emit.emitLoad((allArgs.size() - 1) * Config.SIZEOF_PTR, STACK_REG, reg);
// Check for a null receiver
int cnPadding = cgRef.emitCallPrefix(null, 1);
cgRef.push(reg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, cnPadding);
// Load the address of the method to call into "reg"
// and call it indirectly.
cgRef.emit.emitLoad(0, reg, reg);
int mthdoffset = 4 + mthSymbol.vtableIndex * Config.SIZEOF_PTR;
cgRef.emit.emitLoad(mthdoffset, reg, reg);
cgRef.emit.emit("call", "*" + reg);
cgRef.emitCallSuffix(reg, allArgs.size(), padding);
if (mthSymbol.returnType == PrimitiveTypeSymbol.voidType) {
cgRef.rm.releaseRegister(reg);
return null;
}
return reg;
}
@Override
public Register methodCall(MethodCall ast, Void dummy) {
Register reg = cgRef.eg.gen(ast.getMethodCallExpr());
if (reg != null)
cgRef.rm.releaseRegister(reg);
return reg;
}
@Override
public Register classDecl(ClassDecl ast, Void arg) {
// Emit each method:
cgRef.emit.emitCommentSection("Class " + ast.name);
return visitChildren(ast, arg);
}
@Override
public Register methodDecl(MethodDecl ast, Void arg) {
cgRef.emitMethodPrefix(ast);
gen(ast.body());
cgRef.emitMethodSuffix(false);
return null;
}
@Override
public Register ifElse(IfElse ast, Void arg) {
String falseLbl = cgRef.emit.uniqueLabel();
String doneLbl = cgRef.emit.uniqueLabel();
cgRef.genJumpIfFalse(ast.condition(), falseLbl);
gen(ast.then());
cgRef.emit.emit("jmp", doneLbl);
cgRef.emit.emitLabel(falseLbl);
gen(ast.otherwise());
cgRef.emit.emitLabel(doneLbl);
return null;
}
@Override
public Register whileLoop(WhileLoop ast, Void arg) {
String nextLbl = cgRef.emit.uniqueLabel();
String doneLbl = cgRef.emit.uniqueLabel();
cgRef.emit.emitLabel(nextLbl);
cgRef.genJumpIfFalse(ast.condition(), doneLbl);
gen(ast.body());
cgRef.emit.emit("jmp", nextLbl);
cgRef.emit.emitLabel(doneLbl);
return null;
}
@Override
public Register assign(Assign ast, Void arg) {
class AssignVisitor extends ExprVisitor<Void, Expr> {
@Override
public Void var(Var ast, Expr right) {
final Register rhsReg = cgRef.eg.gen(right);
cgRef.emit.emitStore(rhsReg, ast.sym.offset, BASE_REG);
cgRef.rm.releaseRegister(rhsReg);
return null;
}
@Override
public Void field(Field ast, Expr right) {
final Register rhsReg = cgRef.eg.gen(right);
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.arg());
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(regs.b.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
cgRef.emit.emitStore(regs.a, ast.sym.offset, regs.b);
cgRef.rm.releaseRegister(regs.b);
cgRef.rm.releaseRegister(regs.a);
return null;
}
@Override
public Void index(Index ast, Expr right) {
Register rhsReg = cgRef.egRef.gen(right);
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.left());
rhsReg = regs.a;
Register arrReg = regs.b;
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
regs = cgRef.egRef.genPushing(arrReg, ast.right());
arrReg = regs.a;
Register idxReg = regs.b;
// Check array bounds
padding = cgRef.emitCallPrefix(null, 2);
cgRef.push(idxReg.repr);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_ARRAY_BOUNDS);
cgRef.emitCallSuffix(null, 2, padding);
cgRef.emit.emitMove(rhsReg, arrayAddress(arrReg, idxReg));
cgRef.rm.releaseRegister(arrReg);
cgRef.rm.releaseRegister(idxReg);
cgRef.rm.releaseRegister(rhsReg);
return null;
}
@Override
protected Void dfltExpr(Expr ast, Expr arg) {
throw new RuntimeException("Store to unexpected lvalue " + ast);
}
}
new AssignVisitor().visit(ast.left(), ast.right());
return null;
}
@Override
public Register builtInWrite(BuiltInWrite ast, Void arg) {
Register reg = cgRef.eg.gen(ast.arg());
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(reg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.PRINT_INTEGER);
cgRef.emitCallSuffix(null, 1, padding);
cgRef.rm.releaseRegister(reg);
return null;
}
@Override
public Register builtInWriteln(BuiltInWriteln ast, Void arg) {
int padding = cgRef.emitCallPrefix(null, 0);
cgRef.emit.emit("call", AstCodeGeneratorRef.PRINT_NEW_LINE);
cgRef.emitCallSuffix(null, 0, padding);
return null;
}
@Override
public Register returnStmt(ReturnStmt ast, Void arg) {
if (ast.arg() != null) {
Register reg = cgRef.eg.gen(ast.arg());
cgRef.emit.emitMove(reg, "%eax");
cgRef.emitMethodSuffix(false);
cgRef.rm.releaseRegister(reg);
} else {
cgRef.emitMethodSuffix(true); // no return value -- return NULL as
// a default (required for main())
}
return null;
}
}
class StmtGeneratorNop90 extends StmtGeneratorRef {
protected final CfgCodeGenerator cfgCg;
protected CompileTimeEvaluator cte;
StmtGeneratorNop90(AstCodeGeneratorNop90 astCodeGenerator, CfgCodeGenerator cfgCodeGenerator) {
super(astCodeGenerator);
this.cfgCg = cfgCodeGenerator;
cte = new CompileTimeEvaluator();
}
@Override
public Register assign(Assign ast, Void arg) {
/*
if (ast.left() instanceof Ast.Var) {
Ast.Var var = (Ast.Var) ast.left();
VariableSymbol sym = var.sym;
MaybeC<List<Assign>> maybeStateList = cfgCg.unusedAssignmentsState.get(sym);
List<Ast.Assign> stateList = maybeStateList.getValue();
if (stateList.contains(ast)) {
return null;
}
}
*/
class AssignVisitor extends ExprVisitor<Void, Expr> {
@Override
public Void var(Var ast, Expr right) {
final Register rhsReg = cgRef.eg.gen(right);
cgRef.emit.emitStore(rhsReg, ast.sym.offset, BASE_REG);
cgRef.rm.releaseRegister(rhsReg);
return null;
}
@Override
public Void field(Field ast, Expr right) {
final Register rhsReg = cgRef.eg.gen(right);
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.arg());
if (!cfgCg.check.isNotNull(ast.arg())) {
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(regs.b.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
}
cgRef.emit.emitStore(regs.a, ast.sym.offset, regs.b);
cgRef.rm.releaseRegister(regs.b);
cgRef.rm.releaseRegister(regs.a);
return null;
}
@Override
public Void index(Index ast, Expr right) {
boolean isNotNull = cfgCg.check.isNotNull(ast.left());
boolean emitBoundCheck = cfgCg.check.checkArrayBound(ast);
Register rhsReg = cgRef.egRef.gen(right);
Pair<Register> regs = cgRef.egRef.genPushing(rhsReg, ast.left());
rhsReg = regs.a;
Register arrReg = regs.b;
if (!isNotNull) {
int padding = cgRef.emitCallPrefix(null, 1);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_NULL);
cgRef.emitCallSuffix(null, 1, padding);
}
regs = cgRef.egRef.genPushing(arrReg, ast.right());
arrReg = regs.a;
Register idxReg = regs.b;
// Check array bounds
if (emitBoundCheck) {
int padding = cgRef.emitCallPrefix(null, 2);
cgRef.push(idxReg.repr);
cgRef.push(arrReg.repr);
cgRef.emit.emit("call", AstCodeGeneratorRef.CHECK_ARRAY_BOUNDS);
cgRef.emitCallSuffix(null, 2, padding);
}
cgRef.emit.emitMove(rhsReg, arrayAddress(arrReg, idxReg));
cgRef.rm.releaseRegister(arrReg);
cgRef.rm.releaseRegister(idxReg);
cgRef.rm.releaseRegister(rhsReg);
return null;
}
@Override
protected Void dfltExpr(Expr ast, Expr arg) {
throw new RuntimeException("Store to unexpected lvalue " + ast);
}
}
new AssignVisitor().visit(ast.left(), ast.right());
return null;
}
}