Homework B (benchmarks)
This commit is contained in:
parent
72cc3206c4
commit
76fbabdf53
141 changed files with 7540 additions and 2032 deletions
|
@ -10,10 +10,10 @@ public enum ExitCode {
|
|||
INFINITE_LOOP(6),
|
||||
DIVISION_BY_ZERO(7),
|
||||
INTERNAL_ERROR(22);
|
||||
|
||||
|
||||
public final int value;
|
||||
|
||||
private ExitCode(int value) {
|
||||
this.value = value;
|
||||
private ExitCode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -15,25 +15,33 @@ public class AssemblyEmitter {
|
|||
this.out = out;
|
||||
}
|
||||
|
||||
/** Creates an constant operand. */
|
||||
/**
|
||||
* Creates an constant operand.
|
||||
*/
|
||||
static String constant(int i) {
|
||||
return "$" + i;
|
||||
}
|
||||
|
||||
/** Creates an constant operand with the address of a label. */
|
||||
/**
|
||||
* Creates an constant operand with the address of a label.
|
||||
*/
|
||||
static String labelAddress(String lbl) {
|
||||
return "$" + lbl;
|
||||
}
|
||||
|
||||
/** Creates an operand relative to another operand. */
|
||||
/**
|
||||
* Creates an operand relative to another operand.
|
||||
*/
|
||||
static String registerOffset(int offset, Register reg) {
|
||||
return String.format("%d(%s)", offset, reg);
|
||||
}
|
||||
|
||||
/** Creates an operand addressing an item in an array */
|
||||
/**
|
||||
* Creates an operand addressing an item in an array
|
||||
*/
|
||||
static String arrayAddress(Register arrReg, Register idxReg) {
|
||||
final int offset = Config.SIZEOF_PTR * 2; // one word each in front for
|
||||
// vptr and length
|
||||
// vptr and length
|
||||
final int mul = Config.SIZEOF_PTR; // assume all arrays of 4-byte elem
|
||||
return String.format("%d(%s,%s,%d)", offset, arrReg, idxReg, mul);
|
||||
}
|
||||
|
@ -90,10 +98,6 @@ public class AssemblyEmitter {
|
|||
emit(op, constant(src), dest);
|
||||
}
|
||||
|
||||
void emit(String op, int src, String dest) {
|
||||
emit(op, constant(src), dest);
|
||||
}
|
||||
|
||||
void emit(String op, String dest) {
|
||||
emitRaw(op + " " + dest);
|
||||
}
|
||||
|
@ -106,10 +110,6 @@ public class AssemblyEmitter {
|
|||
emit(op, constant(dest));
|
||||
}
|
||||
|
||||
void emit(String op, int src, int dest) {
|
||||
emit(op, constant(src), constant(dest));
|
||||
}
|
||||
|
||||
void emitMove(Register src, String dest) {
|
||||
emitMove(src.repr, dest);
|
||||
}
|
||||
|
@ -127,10 +127,6 @@ public class AssemblyEmitter {
|
|||
emit("movl", src, dest);
|
||||
}
|
||||
|
||||
void emitMove(int src, Register dest) {
|
||||
emitMove(constant(src), dest);
|
||||
}
|
||||
|
||||
void emitLoad(int srcOffset, Register src, Register dest) {
|
||||
emitMove(registerOffset(srcOffset, src), dest.repr);
|
||||
}
|
||||
|
@ -143,10 +139,6 @@ public class AssemblyEmitter {
|
|||
emitMove(src, registerOffset(destOffset, dest));
|
||||
}
|
||||
|
||||
void emitStore(int src, int destOffset, Register dest) {
|
||||
emitStore(constant(src), destOffset, dest);
|
||||
}
|
||||
|
||||
void emitConstantData(String data) {
|
||||
emitRaw(String.format("%s %s", Config.DOT_INT, data));
|
||||
}
|
||||
|
|
|
@ -2,13 +2,14 @@ package cd.backend.codegen;
|
|||
|
||||
public class AssemblyFailedException extends RuntimeException {
|
||||
private static final long serialVersionUID = -5658502514441032016L;
|
||||
|
||||
|
||||
public final String assemblerOutput;
|
||||
|
||||
public AssemblyFailedException(
|
||||
String assemblerOutput) {
|
||||
super("Executing assembler failed.\n"
|
||||
+ "Output:\n"
|
||||
+ assemblerOutput);
|
||||
this.assemblerOutput = assemblerOutput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,43 +2,42 @@ package cd.backend.codegen;
|
|||
|
||||
import cd.Config;
|
||||
import cd.Main;
|
||||
import cd.backend.ExitCode;
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
import cd.ir.Ast;
|
||||
import cd.ir.Ast.ClassDecl;
|
||||
import cd.ir.Ast.Expr;
|
||||
import cd.ir.Ast.MethodDecl;
|
||||
import cd.ir.Symbol.*;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static cd.Config.MAIN;
|
||||
import static cd.Config.*;
|
||||
import static cd.backend.codegen.AssemblyEmitter.constant;
|
||||
import static cd.backend.codegen.AssemblyEmitter.registerOffset;
|
||||
import static cd.backend.codegen.RegisterManager.BASE_REG;
|
||||
import static cd.backend.codegen.RegisterManager.STACK_REG;
|
||||
|
||||
public class AstCodeGenerator {
|
||||
/** Constant representing the boolean TRUE as integer */
|
||||
static final int TRUE = 1;
|
||||
/** Constant representing the boolean FALSE as integer */
|
||||
static final int FALSE = 0;
|
||||
/** Size of any variable in assembly
|
||||
* Primitive variables take up 4 bytes (booleans are integers)
|
||||
* Reference variables are a 4 byte pointer
|
||||
*/
|
||||
static final int VAR_SIZE = 4;
|
||||
|
||||
RegsNeededVisitor rnv;
|
||||
|
||||
ExprGenerator eg;
|
||||
StmtGenerator sg;
|
||||
|
||||
protected RegsNeededVisitor rnv;
|
||||
|
||||
protected ExprGenerator eg;
|
||||
protected StmtGenerator sg;
|
||||
|
||||
protected final Main main;
|
||||
|
||||
final AssemblyEmitter emit;
|
||||
final RegisterManager rm = new RegisterManager();
|
||||
|
||||
protected final AssemblyEmitter emit;
|
||||
protected final RegisterManager rm = new RegisterManager();
|
||||
|
||||
protected ExprGeneratorRef egRef;
|
||||
protected StmtGeneratorRef sgRef;
|
||||
|
||||
AstCodeGenerator(Main main, Writer out) {
|
||||
initMethodData();
|
||||
main.allTypeSymbols = new ArrayList<>();
|
||||
|
||||
|
||||
this.emit = new AssemblyEmitter(out);
|
||||
this.main = main;
|
||||
this.rnv = new RegsNeededVisitor();
|
||||
|
@ -52,14 +51,15 @@ public class AstCodeGenerator {
|
|||
}
|
||||
|
||||
public static AstCodeGenerator createCodeGenerator(Main main, Writer out) {
|
||||
return new AstCodeGenerator(main, out);
|
||||
return new AstCodeGeneratorRef(main, out);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main method. Causes us to emit x86 assembly corresponding to {@code ast}
|
||||
* into {@code file}. Throws a {@link RuntimeException} should any I/O error
|
||||
* occur.
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* The generated file will be divided into two sections:
|
||||
* <ol>
|
||||
|
@ -70,103 +70,693 @@ public class AstCodeGenerator {
|
|||
* </ol>
|
||||
*/
|
||||
public void go(List<? extends ClassDecl> astRoots) {
|
||||
// Find main type and assign to main.mainType
|
||||
// Add array types to the types list to lookup later
|
||||
for (ClassDecl decl : astRoots) {
|
||||
if (decl.name.equals("Main")) {
|
||||
main.mainType = decl.sym;
|
||||
}
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(decl.sym));
|
||||
}
|
||||
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(PrimitiveTypeSymbol.intType));
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(PrimitiveTypeSymbol.booleanType));
|
||||
main.allTypeSymbols.add(new ArrayTypeSymbol(ClassSymbol.objectType));
|
||||
|
||||
emitPrefix();
|
||||
for (ClassDecl ast : astRoots) {
|
||||
sg.gen(ast);
|
||||
}
|
||||
}
|
||||
|
||||
private void emitPrefix() {
|
||||
// Emit some useful string constants (copied from old HW1 method declaration)
|
||||
|
||||
protected void initMethodData() {
|
||||
rm.initRegisters();
|
||||
}
|
||||
|
||||
|
||||
protected void emitMethodSuffix(boolean returnNull) {
|
||||
if (returnNull)
|
||||
emit.emit("movl", "$0", Register.EAX);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
}
|
||||
|
||||
class AstCodeGeneratorRef extends AstCodeGenerator {
|
||||
/**
|
||||
* The address of the this ptr relative to the BP. Note that the this ptr is
|
||||
* always the first argument. Default offset value is 8 but this can change
|
||||
* depending on the number of parameters pushed on the stack.
|
||||
*/
|
||||
protected int THIS_OFFSET = 8;
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$CheckCast() helper function we generate.
|
||||
*/
|
||||
static final String CHECK_CAST = "Javali$CheckCast";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$CheckNull() helper function we generate.
|
||||
*/
|
||||
static final String CHECK_NULL = "Javali$CheckNull";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$CheckNonZero() helper function we generate.
|
||||
*/
|
||||
static final String CHECK_NON_ZERO = "Javali$CheckNonZero";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$CheckArraySize() helper function we generate.
|
||||
*/
|
||||
static final String CHECK_ARRAY_SIZE = "Javali$CheckArraySize";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$CheckArrayBounds() helper function we
|
||||
* generate.
|
||||
*/
|
||||
static final String CHECK_ARRAY_BOUNDS = "Javali$CheckArrayBounds";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$Alloc() helper function we generate.
|
||||
*/
|
||||
static final String ALLOC = "Javali$Alloc";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$PrintNewLine() helper function we generate.
|
||||
*/
|
||||
static final String PRINT_NEW_LINE = "Javali$PrintNewLine";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$PrintInteger() helper function we generate.
|
||||
*/
|
||||
static final String PRINT_INTEGER = "Javali$PrintInteger";
|
||||
|
||||
/**
|
||||
* Name of the internal Javali$ReadInteger() helper function we generate.
|
||||
*/
|
||||
static final String READ_INTEGER = "Javali$ReadInteger";
|
||||
|
||||
public AstCodeGeneratorRef(Main main, Writer out) {
|
||||
super(main, out);
|
||||
|
||||
this.egRef = new ExprGeneratorRef(this);
|
||||
this.eg = this.egRef;
|
||||
this.sgRef = new StmtGeneratorRef(this);
|
||||
this.sg = this.sgRef;
|
||||
}
|
||||
|
||||
|
||||
protected void emitPrefix(List<? extends ClassDecl> astRoots) {
|
||||
// compute method and field offsets
|
||||
for (ClassDecl ast : astRoots) {
|
||||
computeFieldOffsets(ast.sym);
|
||||
computeVtableOffsets(ast.sym);
|
||||
}
|
||||
|
||||
// emit vtables
|
||||
for (TypeSymbol ts : main.allTypeSymbols)
|
||||
emitVtable(ts);
|
||||
|
||||
// Emit some useful string constants and static data:
|
||||
emit.emitRaw(Config.DATA_STR_SECTION);
|
||||
emit.emitLabel("STR_NL");
|
||||
emit.emitRaw(Config.DOT_STRING + " \"\\n\"");
|
||||
emit.emitLabel("STR_D");
|
||||
emit.emitRaw(Config.DOT_STRING + " \"%d\"");
|
||||
|
||||
// Define Object, Object[], int, int[], boolean and boolean[]
|
||||
List<TypeSymbol> elementTypes = new ArrayList<>();
|
||||
elementTypes.add(ClassSymbol.objectType);
|
||||
elementTypes.add(PrimitiveTypeSymbol.intType);
|
||||
elementTypes.add(PrimitiveTypeSymbol.booleanType);
|
||||
emit.emitLabel("STR_F");
|
||||
emit.emitRaw(Config.DOT_STRING + " \"%.5f\"");
|
||||
emit.emitLabel("SCANF_STR_F");
|
||||
emit.emitRaw(Config.DOT_STRING + " \"%f\"");
|
||||
emit.emitRaw(Config.DATA_INT_SECTION);
|
||||
for (TypeSymbol type : elementTypes) {
|
||||
// type vtable
|
||||
emit.emitLabel(Label.type(type));
|
||||
emit.emitConstantData("0"); // Supertype (null)
|
||||
// array vtable
|
||||
emit.emitLabel(Label.type(new ArrayTypeSymbol(type)));
|
||||
emit.emitConstantData(Label.type(ClassSymbol.objectType)); // Supertype
|
||||
emit.emitConstantData(Label.type(type)); // Element type
|
||||
|
||||
emit.emitRaw(Config.TEXT_SECTION);
|
||||
|
||||
// Generate a helper method for checking casts:
|
||||
// It takes first a vtable and second an object ptr.
|
||||
{
|
||||
Register obj = RegisterManager.CALLER_SAVE[0];
|
||||
Register cls = RegisterManager.CALLER_SAVE[1];
|
||||
String looplbl = emit.uniqueLabel();
|
||||
String donelbl = emit.uniqueLabel();
|
||||
String faillbl = emit.uniqueLabel();
|
||||
emit.emitCommentSection(CHECK_CAST + " function");
|
||||
emit.emitLabel(CHECK_CAST);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emitLoad(SIZEOF_PTR * 2, BASE_REG, cls);
|
||||
emit.emitLoad(SIZEOF_PTR * 3, BASE_REG, obj);
|
||||
emit.emit("cmpl", constant(0), obj);
|
||||
emit.emit("je", donelbl); // allow null objects to pass
|
||||
emit.emitLoad(0, obj, obj); // load vtbl of object
|
||||
emit.emitLabel(looplbl);
|
||||
emit.emit("cmpl", obj, cls);
|
||||
emit.emit("je", donelbl);
|
||||
emit.emit("cmpl", constant(0), obj);
|
||||
emit.emit("je", faillbl);
|
||||
emit.emitLoad(0, obj, obj); // load parent vtable
|
||||
emit.emit("jmp", looplbl);
|
||||
emit.emitLabel(faillbl);
|
||||
emit.emitStore(constant(ExitCode.INVALID_DOWNCAST.value), 0, STACK_REG);
|
||||
emit.emit("call", Config.EXIT);
|
||||
emit.emitLabel(donelbl);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Emit the new Main().main() code to start the program:
|
||||
// Generate a helper method for checking for null ptrs:
|
||||
{
|
||||
String oknulllbl = emit.uniqueLabel();
|
||||
emit.emitCommentSection(CHECK_NULL + " function");
|
||||
emit.emitLabel(CHECK_NULL);
|
||||
|
||||
// 1. Enter TEXT and start the program
|
||||
emit.emitRaw(Config.TEXT_SECTION);
|
||||
emit.emit(".globl", MAIN);
|
||||
emit.emitLabel(MAIN);
|
||||
// 1.1. Prepare first frame
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
// 2. Create main variable
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emit("cmpl", constant(0), registerOffset(SIZEOF_PTR * 2, BASE_REG));
|
||||
emit.emit("jne", oknulllbl);
|
||||
emit.emitStore(constant(ExitCode.NULL_POINTER.value), 0, STACK_REG);
|
||||
emit.emit("call", Config.EXIT);
|
||||
emit.emitLabel(oknulllbl);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Generate a helper method for checking that we don't divide by zero:
|
||||
{
|
||||
String oknzlbl = emit.uniqueLabel();
|
||||
emit.emitCommentSection(CHECK_NON_ZERO + " function");
|
||||
emit.emitLabel(CHECK_NON_ZERO);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emit("cmpl", constant(0), registerOffset(SIZEOF_PTR * 2, BASE_REG));
|
||||
emit.emit("jne", oknzlbl);
|
||||
emit.emitStore(constant(ExitCode.DIVISION_BY_ZERO.value), 0, STACK_REG);
|
||||
emit.emit("call", Config.EXIT);
|
||||
emit.emitLabel(oknzlbl);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Generate a helper method for checking array size:
|
||||
{
|
||||
String okunqlbl = emit.uniqueLabel();
|
||||
emit.emitCommentSection(CHECK_ARRAY_SIZE + " function");
|
||||
emit.emitLabel(CHECK_ARRAY_SIZE);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emit("cmpl", constant(0), registerOffset(SIZEOF_PTR * 2, BASE_REG));
|
||||
emit.emit("jge", okunqlbl);
|
||||
emit.emitStore(constant(ExitCode.INVALID_ARRAY_SIZE.value), 0, STACK_REG);
|
||||
emit.emit("call", Config.EXIT);
|
||||
emit.emitLabel(okunqlbl);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Generate a helper method for checking array bounds:
|
||||
{
|
||||
Register arr = RegisterManager.CALLER_SAVE[0];
|
||||
Register idx = RegisterManager.CALLER_SAVE[1];
|
||||
String faillbl = emit.uniqueLabel();
|
||||
emit.emitCommentSection(CHECK_ARRAY_BOUNDS + " function");
|
||||
emit.emitLabel(CHECK_ARRAY_BOUNDS);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emitLoad(SIZEOF_PTR * 3, BASE_REG, idx);
|
||||
emit.emitLoad(SIZEOF_PTR * 2, BASE_REG, arr);
|
||||
emit.emit("cmpl", constant(0), idx); // idx < 0
|
||||
emit.emit("jl", faillbl);
|
||||
emit.emit("cmpl", registerOffset(Config.SIZEOF_PTR, arr), idx); // idx >= len
|
||||
emit.emit("jge", faillbl);
|
||||
// done
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
// fail
|
||||
emit.emitLabel(faillbl);
|
||||
emit.emitStore(constant(ExitCode.INVALID_ARRAY_BOUNDS.value), 0, STACK_REG);
|
||||
emit.emit("call", Config.EXIT);
|
||||
|
||||
}
|
||||
|
||||
// Generate a helper method for allocating objects/arrays
|
||||
{
|
||||
Register size = RegisterManager.CALLER_SAVE[0];
|
||||
emit.emitCommentSection(ALLOC + " function");
|
||||
emit.emitLabel(ALLOC);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emitLoad(8, BASE_REG, size);
|
||||
emit.emitStore(size, 0, STACK_REG);
|
||||
emit.emitStore(constant(1), 4, STACK_REG);
|
||||
emit.emit("call", Config.CALLOC);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Generate a helper method for printing a new line
|
||||
{
|
||||
emit.emitCommentSection(PRINT_NEW_LINE + " function");
|
||||
emit.emitLabel(PRINT_NEW_LINE);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emitStore("$STR_NL", 0, STACK_REG);
|
||||
emit.emit("call", Config.PRINTF);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Generate a helper method for printing an integer
|
||||
{
|
||||
Register temp = RegisterManager.CALLER_SAVE[0];
|
||||
emit.emitCommentSection(PRINT_INTEGER + " function");
|
||||
emit.emitLabel(PRINT_INTEGER);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emitLoad(8, BASE_REG, temp);
|
||||
emit.emitStore(temp, 4, STACK_REG);
|
||||
emit.emitStore("$STR_D", 0, STACK_REG);
|
||||
emit.emit("call", Config.PRINTF);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Generate a helper method for reading an integer
|
||||
{
|
||||
Register number = RegisterManager.CALLER_SAVE[0];
|
||||
emit.emitCommentSection(READ_INTEGER + " function");
|
||||
emit.emitLabel(READ_INTEGER);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", constant(-16), STACK_REG);
|
||||
emit.emit("sub", constant(16), STACK_REG);
|
||||
emit.emit("leal", registerOffset(8, STACK_REG), number);
|
||||
emit.emitStore(number, 4, STACK_REG);
|
||||
emit.emitStore("$STR_D", 0, STACK_REG);
|
||||
emit.emit("call", SCANF);
|
||||
emit.emitLoad(8, STACK_REG, Register.EAX);
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
// Generate AST for main() method:
|
||||
// new Main().main()
|
||||
Ast.NewObject newMain = new Ast.NewObject("Main");
|
||||
newMain.type = main.mainType;
|
||||
Register mainLocation = eg.visit(newMain, null);
|
||||
|
||||
// 3. Call main()
|
||||
emit.emit("push", mainLocation);
|
||||
for (ClassSymbol sym = main.mainType; sym != ClassSymbol.objectType; sym = sym.superClass)
|
||||
if (sym.methods.getOrDefault("main", null) != null) {
|
||||
emit.emit("call", Label.method(sym, sym.methods.get("main")));
|
||||
break;
|
||||
}
|
||||
emitMethodSuffix(true);
|
||||
Ast.MethodCallExpr mce = new Ast.MethodCallExpr(newMain, "main", Collections.<Expr>emptyList());
|
||||
Ast.MethodCall callMain = new Ast.MethodCall(mce);
|
||||
mce.sym = main.mainType.getMethod("main");
|
||||
|
||||
// Emit the main() method:
|
||||
// new Main().main();
|
||||
emit.emitCommentSection("main() function");
|
||||
emit.emitRaw(".globl " + MAIN);
|
||||
emit.emitLabel(MAIN);
|
||||
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(8), STACK_REG);
|
||||
|
||||
emit.emit("and", -16, STACK_REG);
|
||||
sg.gen(callMain);
|
||||
emit.emit("movl", constant(ExitCode.OK.value), Register.EAX); // normal termination:
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
|
||||
}
|
||||
|
||||
void initMethodData() {
|
||||
rm.initRegisters();
|
||||
@Override
|
||||
public void go(List<? extends ClassDecl> astRoots) {
|
||||
emitPrefix(astRoots);
|
||||
super.go(astRoots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the vtable offset for each method defined in the class
|
||||
* {@code sym}.
|
||||
*/
|
||||
|
||||
protected int computeVtableOffsets(ClassSymbol sym) {
|
||||
|
||||
if (sym == null)
|
||||
return 0;
|
||||
|
||||
if (sym.totalMethods != -1)
|
||||
return sym.totalMethods;
|
||||
|
||||
int index = computeVtableOffsets(sym.superClass);
|
||||
for (MethodSymbol ms : sym.methods.values()) {
|
||||
assert ms.vtableIndex == -1;
|
||||
if (ms.overrides != null)
|
||||
ms.vtableIndex = ms.overrides.vtableIndex;
|
||||
else
|
||||
ms.vtableIndex = index++;
|
||||
}
|
||||
sym.totalMethods = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the offset for each field.
|
||||
*/
|
||||
|
||||
protected int computeFieldOffsets(ClassSymbol sym) {
|
||||
if (sym == null)
|
||||
return 0;
|
||||
|
||||
if (sym.totalFields != -1)
|
||||
return sym.totalFields;
|
||||
|
||||
int index = computeFieldOffsets(sym.superClass);
|
||||
for (VariableSymbol fs : sym.fields.values()) {
|
||||
assert fs.offset == -1;
|
||||
// compute offset in bytes; note that 0 is the vtable
|
||||
fs.offset = (index * SIZEOF_PTR) + SIZEOF_PTR;
|
||||
index++;
|
||||
}
|
||||
sym.totalFields = index;
|
||||
sym.sizeof = (sym.totalFields + 1) * Config.SIZEOF_PTR;
|
||||
return index;
|
||||
}
|
||||
|
||||
private void collectVtable(MethodSymbol[] vtable, ClassSymbol sym) {
|
||||
if (sym.superClass != null)
|
||||
collectVtable(vtable, sym.superClass);
|
||||
for (MethodSymbol ms : sym.methods.values())
|
||||
vtable[ms.vtableIndex] = ms;
|
||||
}
|
||||
|
||||
protected void emitVtable(TypeSymbol ts) {
|
||||
if (ts instanceof ClassSymbol) {
|
||||
ClassSymbol cs = (ClassSymbol) ts;
|
||||
|
||||
// Collect the vtable:
|
||||
MethodSymbol[] vtable = new MethodSymbol[cs.totalMethods];
|
||||
collectVtable(vtable, cs);
|
||||
|
||||
// Emit vtable for this class:
|
||||
emit.emitLabel(vtable(cs));
|
||||
if (cs.superClass != null)
|
||||
emit.emitConstantData(vtable(cs.superClass));
|
||||
else
|
||||
emit.emitConstantData("0");
|
||||
for (int i = 0; i < cs.totalMethods; i++)
|
||||
emit.emitConstantData(methodLabel(vtable[i]));
|
||||
} else if (ts instanceof ArrayTypeSymbol) {
|
||||
ArrayTypeSymbol as = (ArrayTypeSymbol) ts;
|
||||
emit.emitLabel(vtable(as));
|
||||
emit.emitConstantData(vtable(ClassSymbol.objectType));
|
||||
}
|
||||
}
|
||||
|
||||
protected String vtable(TypeSymbol ts) {
|
||||
if (ts instanceof ClassSymbol) {
|
||||
return "vtable_" + ((ClassSymbol) ts).name;
|
||||
} else if (ts instanceof ArrayTypeSymbol) {
|
||||
return "vtablearr_" + ((ArrayTypeSymbol) ts).elementType.name;
|
||||
} else {
|
||||
throw new RuntimeException("No vtable for " + ts.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void emitMethodSuffix(boolean returnNull) {
|
||||
@Override
|
||||
protected void initMethodData() {
|
||||
THIS_OFFSET = 8;
|
||||
bytes = 0;
|
||||
super.initMethodData();
|
||||
}
|
||||
|
||||
protected int padding(int numberOfParameters) {
|
||||
int padding = (bytes + numberOfParameters * Config.SIZEOF_PTR + 15) & 0xFFFFFFF0;
|
||||
return padding - bytes - numberOfParameters * Config.SIZEOF_PTR;
|
||||
}
|
||||
|
||||
protected void push(int padding) {
|
||||
if (padding > 0) {
|
||||
emit.emit("sub", padding, STACK_REG);
|
||||
bytes += padding;
|
||||
}
|
||||
}
|
||||
|
||||
protected void pop(int padding) {
|
||||
if (padding > 0) {
|
||||
emit.emit("add", padding, STACK_REG);
|
||||
bytes -= padding;
|
||||
}
|
||||
assert bytes >= 0;
|
||||
}
|
||||
|
||||
protected void push(String reg) {
|
||||
emit.emit("push", reg);
|
||||
bytes += Config.SIZEOF_PTR;
|
||||
}
|
||||
|
||||
protected void pop(String reg) {
|
||||
emit.emit("pop", reg);
|
||||
bytes -= Config.SIZEOF_PTR;
|
||||
assert bytes >= 0;
|
||||
}
|
||||
|
||||
protected void restoreCalleeSaveRegs() {
|
||||
for (int reg = RegisterManager.CALLEE_SAVE.length - 1; reg >= 0; reg--) {
|
||||
emit.emit("pop", RegisterManager.CALLEE_SAVE[reg]);
|
||||
}
|
||||
}
|
||||
|
||||
protected void storeCalleeSaveRegs() {
|
||||
bytes = 0;
|
||||
for (int reg = 0; reg < RegisterManager.CALLEE_SAVE.length; reg++) {
|
||||
emit.emit("push", RegisterManager.CALLEE_SAVE[reg]);
|
||||
bytes += Config.SIZEOF_PTR;
|
||||
}
|
||||
}
|
||||
|
||||
protected void restoreCallerSaveRegs(Register res) {
|
||||
for (int reg = RegisterManager.CALLER_SAVE.length - 1; reg >= 0; reg--) {
|
||||
if (!rm.isInUse(RegisterManager.CALLER_SAVE[reg]))
|
||||
continue; // not in use
|
||||
if (RegisterManager.CALLER_SAVE[reg].equals(res))
|
||||
continue; // contains our result
|
||||
pop(RegisterManager.CALLER_SAVE[reg].repr);
|
||||
}
|
||||
}
|
||||
|
||||
protected void storeCallerSaveRegs(Register res) {
|
||||
for (int reg = 0; reg < RegisterManager.CALLER_SAVE.length; reg++) {
|
||||
if (!rm.isInUse(RegisterManager.CALLER_SAVE[reg]))
|
||||
continue; // not in use
|
||||
if (RegisterManager.CALLER_SAVE[reg].equals(res))
|
||||
continue; // will contain our result
|
||||
push(RegisterManager.CALLER_SAVE[reg].repr);
|
||||
}
|
||||
}
|
||||
|
||||
protected int emitCallPrefix(Register res, int numberOfParameters) {
|
||||
storeCallerSaveRegs(res);
|
||||
int padding = padding(numberOfParameters);
|
||||
push(padding);
|
||||
return padding;
|
||||
}
|
||||
|
||||
protected void emitCallSuffix(Register res, int numberOfParameters,
|
||||
int padding) {
|
||||
pop(numberOfParameters * Config.SIZEOF_PTR + padding);
|
||||
if (res != null) {
|
||||
emit.emitMove(Register.EAX, res);
|
||||
}
|
||||
restoreCallerSaveRegs(res);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates code which evaluates {@code ast} and branches to {@code lbl} if
|
||||
* the value generated for {@code ast} is false.
|
||||
*/
|
||||
|
||||
protected void genJumpIfFalse(Expr ast, String lbl) {
|
||||
// A better way to implement this would be with a separate
|
||||
// visitor.
|
||||
Register reg = eg.gen(ast);
|
||||
emit.emit("cmpl", "$0", reg);
|
||||
emit.emit("je", lbl);
|
||||
rm.releaseRegister(reg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to store the temporaries. We grow our stack dynamically, we allocate
|
||||
* "temporary" values on this stack during method execution. Values can be
|
||||
* stored and retrieved using {@link #push(String)} and {@link #pop(String)}
|
||||
* , which use the program stack.
|
||||
*/
|
||||
|
||||
protected int bytes = 0;
|
||||
|
||||
protected String methodLabel(MethodSymbol msym) {
|
||||
return "meth_" + msym.owner.name + "_" + msym.name;
|
||||
}
|
||||
|
||||
protected void emitMethodPrefix(MethodDecl ast) {
|
||||
|
||||
// Emit the label for the method:
|
||||
emit.emitRaw(Config.TEXT_SECTION);
|
||||
emit.emitCommentSection(String.format("Method %s.%s", ast.sym.owner.name,
|
||||
ast.name));
|
||||
emit.emitRaw(".globl " + methodLabel(ast.sym));
|
||||
emit.emitLabel(methodLabel(ast.sym));
|
||||
|
||||
// Compute the size and layout of the stack frame. Our
|
||||
// frame looks like (the numbers are relative to our ebp):
|
||||
//
|
||||
// (caller's locals)
|
||||
// (padding)
|
||||
// arg 0 (this ptr)
|
||||
// ...
|
||||
// 12 arg N - 1
|
||||
// 8 arg N
|
||||
// 4 linkage ptr (return address)
|
||||
// 0 saved ebp
|
||||
// -4 locals
|
||||
// (callee's arguments + temporaries)
|
||||
//
|
||||
// We allocate on the stack during the course of
|
||||
// a function call using push(...) and pop(...) instructions.
|
||||
//
|
||||
// Stack slots fall into several
|
||||
// categories:
|
||||
// - "Linkage": overhead for function calls.
|
||||
// This includes the return address and saved ebp.
|
||||
// - locals: these store the value of user-declared local
|
||||
// variables.
|
||||
// - temporaries: these are stack slots used to store
|
||||
// values during expression evaluation when we run out
|
||||
// of registers, saving caller-saved registers, and
|
||||
// other miscellaneous purposes.
|
||||
// - padding: only there to ensure the stack size is a multiple
|
||||
// of 16.
|
||||
// - arguments: values we will pass to functions being
|
||||
// invoked.
|
||||
//
|
||||
// We calculate all address relative to the base pointer.
|
||||
|
||||
// Initialize method-specific data
|
||||
initMethodData();
|
||||
|
||||
// Assign parameter offsets:
|
||||
// As shown above, these start from 8.
|
||||
// Being able to evaluate parameters like in Java
|
||||
// with left-to-right evaluation order they result
|
||||
// on the stack in reversed order.
|
||||
// The "this" parameter is the first pushed on the stack
|
||||
// thus receiving higher offset.
|
||||
int paramOffset = Config.SIZEOF_PTR * 2;
|
||||
for (int i = ast.sym.parameters.size() - 1; i >= 0; i--) {
|
||||
final VariableSymbol param = ast.sym.parameters.get(i);
|
||||
assert param.offset == -1;
|
||||
param.offset = paramOffset;
|
||||
paramOffset += Config.SIZEOF_PTR;
|
||||
}
|
||||
THIS_OFFSET = paramOffset;
|
||||
paramOffset += Config.SIZEOF_PTR;
|
||||
|
||||
// First few slots are reserved for caller save regs:
|
||||
int localSlot = RegisterManager.CALLER_SAVE.length * RegisterManager.SIZEOF_REG;
|
||||
|
||||
// Assign local variable offsets:
|
||||
emit.emitComment(String.format("%-10s Offset", "Variable"));
|
||||
for (VariableSymbol local : ast.sym.locals.values()) {
|
||||
assert local.offset == -1;
|
||||
local.offset = -localSlot;
|
||||
localSlot += Config.SIZEOF_PTR;
|
||||
emit.emitComment(String.format("%-10s %d", local, local.offset));
|
||||
}
|
||||
|
||||
// Round up stack size to make it a multiple of 16.
|
||||
// The actual amount passed to the enter instruction is 8
|
||||
// less, however, because it wants to know the amount
|
||||
// in addition to the linkage ptr and saved ebp.
|
||||
int implicit = Config.SIZEOF_PTR * 2;
|
||||
int stackSize = (implicit + localSlot + 15) & 0xFFFFFFF0;
|
||||
stackSize -= implicit;
|
||||
|
||||
emit.emitComment(String.format("implicit=%d localSlot=%d sum=%d", implicit,
|
||||
localSlot, implicit + localSlot));
|
||||
|
||||
|
||||
// emit.emitRaw(String.format("enter $%d, $0", stackSize));
|
||||
emit.emit("push", BASE_REG);
|
||||
emit.emitMove(STACK_REG, BASE_REG);
|
||||
emit.emit("sub", constant(stackSize), STACK_REG);
|
||||
|
||||
emit.emit("and", -16, STACK_REG);
|
||||
|
||||
storeCalleeSaveRegs();
|
||||
|
||||
// zero-initialize locals
|
||||
for (VariableSymbol local : ast.sym.locals.values()) {
|
||||
emit.emitMove(constant(0), registerOffset(local.offset, BASE_REG));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void emitMethodSuffix(boolean returnNull) {
|
||||
if (returnNull)
|
||||
emit.emit("movl", 0, Register.EAX);
|
||||
emit.emitRaw("leave");
|
||||
emit.emit("movl", "$0", Register.EAX);
|
||||
restoreCalleeSaveRegs();
|
||||
emit.emitMove(BASE_REG, STACK_REG);
|
||||
emit.emit("pop", BASE_REG);
|
||||
emit.emitRaw("ret");
|
||||
}
|
||||
|
||||
static class Label {
|
||||
static String type(TypeSymbol symbol) {
|
||||
if (symbol instanceof ClassSymbol)
|
||||
return String.format("type_%s", symbol);
|
||||
else if (symbol instanceof ArrayTypeSymbol)
|
||||
return String.format("array_%s", ((ArrayTypeSymbol) symbol).elementType);
|
||||
else if (symbol instanceof PrimitiveTypeSymbol)
|
||||
return String.format("primive_%s", symbol);
|
||||
throw new RuntimeException("Unimplemented type symbol");
|
||||
}
|
||||
|
||||
static String method(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
|
||||
return String.format("method_%s_%s", classSymbol.name, methodSymbol.name);
|
||||
}
|
||||
|
||||
static String returnMethod(ClassSymbol classSymbol, MethodSymbol methodSymbol) {
|
||||
return String.format("return_%s", method(classSymbol, methodSymbol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AstCodeGeneratorNop90 extends AstCodeGeneratorRef {
|
||||
public AstCodeGeneratorNop90(Main main, Writer out, CfgCodeGenerator cfgCg) {
|
||||
super(main, out);
|
||||
this.egRef = new ExprGeneratorNop90(this, cfgCg);
|
||||
this.eg = this.egRef;
|
||||
this.sgRef = new StmtGeneratorNop90(this, cfgCg);
|
||||
this.sg = this.sgRef;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
130
src/cd/backend/codegen/CfgCodeGenerator.java
Normal file
130
src/cd/backend/codegen/CfgCodeGenerator.java
Normal file
|
@ -0,0 +1,130 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.Main;
|
||||
import cd.ir.Ast.ClassDecl;
|
||||
import cd.ir.Ast.MethodDecl;
|
||||
import cd.ir.Ast.Stmt;
|
||||
import cd.ir.Ast;
|
||||
import cd.ir.AstVisitor;
|
||||
import cd.ir.BasicBlock;
|
||||
import cd.ir.ControlFlowGraph;
|
||||
import cd.ir.Symbol.PrimitiveTypeSymbol;
|
||||
import cd.ir.Symbol.TypeSymbol;
|
||||
import cd.ir.Symbol.VariableSymbol;
|
||||
import cd.transform.analysis.ArraySizeAnalysis;
|
||||
import cd.transform.analysis.AssignmentAnalysis;
|
||||
import cd.transform.analysis.DynamicTypeAnalysis;
|
||||
import cd.transform.analysis.MaybeC;
|
||||
import cd.transform.analysis.NullAnalysis;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CfgCodeGenerator {
|
||||
|
||||
public final Main main;
|
||||
private final AstCodeGeneratorRef cg;
|
||||
public final CompileTimeChecks check;
|
||||
|
||||
protected Map<VariableSymbol, MaybeC<Integer>> arraySizeState;
|
||||
protected Map<VariableSymbol, MaybeC<Boolean>> nullState;
|
||||
protected Map<VariableSymbol, MaybeC<TypeSymbol>> dynamicTypeState;
|
||||
//protected Map<VariableSymbol, MaybeC<List<Ast.Assign>>> unusedAssignmentsState;
|
||||
|
||||
public CfgCodeGenerator(Main main, Writer out) {
|
||||
this.main = main;
|
||||
cg = new AstCodeGeneratorNop90(main, out, this);
|
||||
check = new CompileTimeChecks(cg, this);
|
||||
}
|
||||
|
||||
public void go(List<? extends ClassDecl> astRoots) {
|
||||
cg.emitPrefix(astRoots);
|
||||
for (ClassDecl cdecl : astRoots)
|
||||
new CfgStmtVisitor().visit(cdecl, null);
|
||||
}
|
||||
|
||||
private class CfgStmtVisitor extends AstVisitor<Void, Void> {
|
||||
|
||||
@Override
|
||||
public Void classDecl(ClassDecl ast, Void arg) {
|
||||
cg.emit.emitCommentSection("Class " + ast.name);
|
||||
cg.emit.increaseIndent("");
|
||||
super.classDecl(ast, arg);
|
||||
cg.emit.decreaseIndent();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void methodDecl(MethodDecl ast, Void arg) {
|
||||
cg.emitMethodPrefix(ast);
|
||||
|
||||
ControlFlowGraph cfg = ast.cfg;
|
||||
assert cfg != null;
|
||||
|
||||
Map<BasicBlock, String> labels = new HashMap<BasicBlock, String>();
|
||||
for (BasicBlock blk : cfg.allBlocks)
|
||||
labels.put(blk, cg.emit.uniqueLabel());
|
||||
String exitLabel = cg.emit.uniqueLabel();
|
||||
|
||||
cg.emit.emit("jmp", labels.get(cfg.start));
|
||||
|
||||
// Run analysis here to have the relevant results to this cfg object
|
||||
ArraySizeAnalysis arraySizeAnalysis = new ArraySizeAnalysis(cfg);
|
||||
NullAnalysis nullAnalysis = new NullAnalysis(cfg);
|
||||
DynamicTypeAnalysis dynamicTypeAnalysis = new DynamicTypeAnalysis(cfg);
|
||||
//AssignmentAnalysis assignmentAnalysis = new AssignmentAnalysis(cfg);
|
||||
|
||||
for (BasicBlock blk : cfg.allBlocks) {
|
||||
arraySizeState = arraySizeAnalysis.inStateOf(blk);
|
||||
nullState = nullAnalysis.inStateOf(blk);
|
||||
dynamicTypeState = dynamicTypeAnalysis.inStateOf(blk);
|
||||
//unusedAssignmentsState = assignmentAnalysis.inStateOf(blk);
|
||||
assert nullState != null;
|
||||
assert arraySizeState != null;
|
||||
assert dynamicTypeState != null;
|
||||
//assert unusedAssignmentsState != null;
|
||||
|
||||
cg.emit.emitCommentSection("Basic block " + blk.index);
|
||||
cg.emit.emitLabel(labels.get(blk));
|
||||
|
||||
for (Stmt stmt : blk.stmts) {
|
||||
cg.sg.gen(stmt);
|
||||
//assignmentAnalysis.transferStmt(stmt, unusedAssignmentsState);
|
||||
arraySizeAnalysis.transferStmt(stmt, arraySizeState);
|
||||
nullAnalysis.transferStmt(stmt, nullState);
|
||||
dynamicTypeAnalysis.transferStmt(stmt, dynamicTypeState);
|
||||
}
|
||||
|
||||
if (blk == cfg.end) {
|
||||
cg.emit.emitComment(String.format("Return"));
|
||||
assert blk.successors.size() == 0;
|
||||
cg.emit.emit("jmp", exitLabel);
|
||||
} else if (blk.condition != null) {
|
||||
assert blk.successors.size() == 2;
|
||||
cg.emit.emitComment(String.format(
|
||||
"Exit to block %d if true, block %d if false",
|
||||
blk.trueSuccessor().index, blk.falseSuccessor().index));
|
||||
cg.genJumpIfFalse(blk.condition, labels.get(blk.falseSuccessor()));
|
||||
cg.emit.emit("jmp", labels.get(blk.trueSuccessor()));
|
||||
} else {
|
||||
cg.emit.emitComment(String.format(
|
||||
"Exit to block %d", blk.successors.get(0).index));
|
||||
assert blk.successors.size() == 1;
|
||||
cg.emit.emit("jmp", labels.get(blk.successors.get(0)));
|
||||
}
|
||||
}
|
||||
|
||||
cg.emit.emitLabel(exitLabel);
|
||||
if (ast.sym.returnType.equals(PrimitiveTypeSymbol.voidType))
|
||||
cg.emitMethodSuffix(true);
|
||||
else
|
||||
cg.emitMethodSuffix(true);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
106
src/cd/backend/codegen/CompileTimeChecks.java
Executable file
106
src/cd/backend/codegen/CompileTimeChecks.java
Executable file
|
@ -0,0 +1,106 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.Config;
|
||||
import cd.backend.ExitCode;
|
||||
import cd.ir.Ast;
|
||||
import cd.ir.Ast.*;
|
||||
import cd.ir.CompileTimeEvaluator;
|
||||
import cd.ir.Symbol.VariableSymbol;
|
||||
import cd.transform.analysis.MaybeC;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static cd.backend.codegen.AssemblyEmitter.constant;
|
||||
import static cd.backend.codegen.RegisterManager.STACK_REG;
|
||||
|
||||
/**
|
||||
* Class with simple static checks mimicking the runtime system with the functions to check
|
||||
* possible errors at runtime. The purpose is to skip unnecessary calls to those functions
|
||||
*/
|
||||
public class CompileTimeChecks {
|
||||
protected final CfgCodeGenerator cfgCg;
|
||||
protected final AstCodeGeneratorRef cgRef;
|
||||
protected CompileTimeEvaluator cte;
|
||||
|
||||
|
||||
public CompileTimeChecks(AstCodeGeneratorRef cg, CfgCodeGenerator cfgCodeGenerator) {
|
||||
this.cfgCg = cfgCodeGenerator;
|
||||
this.cgRef = cg;
|
||||
cte = new CompileTimeEvaluator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that extracts the VariableSymbol from a Var or Field node.
|
||||
* <br/> If the node is not one of those, the node must be a Index
|
||||
*/
|
||||
private VariableSymbol symbolOf(Expr expr) {
|
||||
if (expr instanceof Var)
|
||||
return ((Var) expr).sym;
|
||||
if (expr instanceof Field)
|
||||
return ((Field) expr).sym;
|
||||
// assert expr instanceof Index // only for local checks
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/** Returns whether a NewArray expression needs to check dynamically for the
|
||||
* validity of the index (>=0). In case that it is known but invalid, a call
|
||||
* to exit is done with the appropriate error code.
|
||||
*/
|
||||
public boolean checkArraySize(Ast.NewArray expr) {
|
||||
Optional<Integer> arraySize = cte.calc(expr.arg());
|
||||
if (!arraySize.isPresent()) {
|
||||
return true;
|
||||
}
|
||||
int arraySizeValue = arraySize.get();
|
||||
if (arraySizeValue < 0) {
|
||||
cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_SIZE.value), 0, STACK_REG);
|
||||
cgRef.emit.emit("call", Config.EXIT);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Returns whether a run-time check for the bounds of an array index is necessary.
|
||||
* <br/> A check is only necessary if the index expression cannot be evaluated in
|
||||
* compile time or the size of the array is unknown.
|
||||
* <br/> If the value is known and it is invalid, an unconditional call to exit is
|
||||
* performed with the appropriate exit code
|
||||
*/
|
||||
public boolean checkArrayBound(Index expr) {
|
||||
Optional<Integer> index = cte.calc(expr.right());
|
||||
if (!index.isPresent()) {
|
||||
return true;
|
||||
}
|
||||
VariableSymbol symbol = symbolOf(expr.left());
|
||||
if (!cfgCg.arraySizeState.containsKey(symbol)) {
|
||||
return true;
|
||||
}
|
||||
int indexValue = index.get();
|
||||
MaybeC<Integer> bound = cfgCg.arraySizeState.get(symbol);
|
||||
if (bound.isNotConstant()) {
|
||||
return true;
|
||||
}
|
||||
int boundValue = bound.getValue();
|
||||
if (indexValue < 0 || indexValue > (boundValue - 1)) {
|
||||
cgRef.emit.emitStore(constant(ExitCode.INVALID_ARRAY_BOUNDS.value), 0, STACK_REG);
|
||||
cgRef.emit.emit("call", Config.EXIT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true only when it is impossible for the expression (Field or Var) to be null.
|
||||
* <br/> Currently doesn't check indexes.
|
||||
*/
|
||||
public boolean isNotNull(Expr expr) {
|
||||
if (expr instanceof ThisRef) {
|
||||
return false;
|
||||
} else {
|
||||
VariableSymbol symbol = symbolOf(expr);
|
||||
MaybeC<Boolean> isNull = cfgCg.nullState.get(symbol);
|
||||
return isNull != null && isNull.isConstant() && !isNull.getValue();
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,19 +0,0 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.backend.ExitCode;
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
|
||||
public class Interrupts {
|
||||
protected static final int INTERRUPT_EXIT = 1;
|
||||
|
||||
/**
|
||||
* Generates a exit interrupt with the code provided
|
||||
* @param cg AstCodeGenerator to print instructions
|
||||
* @param exitCode Number to use as exit code (can use constants in this class)
|
||||
*/
|
||||
protected static void exit(AstCodeGenerator cg, ExitCode exitCode) {
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(INTERRUPT_EXIT), Register.EAX);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(exitCode.value), Register.EBX);
|
||||
cg.emit.emit("int", 0x80);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.ir.Symbol.ClassSymbol;
|
||||
import cd.ir.Symbol.MethodSymbol;
|
||||
|
||||
public class Location {
|
||||
private ClassSymbol classSymbol;
|
||||
private MethodSymbol methodSymbol = null;
|
||||
private boolean obtainReference = false;
|
||||
|
||||
public Location (ClassSymbol sym) {
|
||||
classSymbol = sym;
|
||||
}
|
||||
|
||||
public ClassSymbol classSym() {
|
||||
return classSymbol;
|
||||
}
|
||||
|
||||
public MethodSymbol methodSym() {
|
||||
assert methodSymbol != null;
|
||||
return methodSymbol;
|
||||
}
|
||||
|
||||
public void enterMethod(MethodSymbol sym) {
|
||||
methodSymbol = sym;
|
||||
}
|
||||
|
||||
public void leaveMethod() {
|
||||
methodSymbol = null;
|
||||
}
|
||||
|
||||
public boolean isObtainReference() {
|
||||
boolean aux = obtainReference;
|
||||
obtainReference = false;
|
||||
return aux;
|
||||
}
|
||||
|
||||
public Location obtainReference() {
|
||||
obtainReference = true;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -16,10 +16,10 @@ public class RegisterManager {
|
|||
Register.EDI, Register.EBX};
|
||||
public static final Register CALLER_SAVE[] = new Register[]{Register.EAX,
|
||||
Register.ECX, Register.EDX};
|
||||
|
||||
|
||||
// list of general purpose registers
|
||||
public static final Register GPR[] = new Register[]{Register.EAX, Register.EBX,
|
||||
Register.ECX, Register.EDX, Register.ESI, Register.EDI};
|
||||
Register.ECX, Register.EDX, Register.ESI, Register.EDI};
|
||||
|
||||
// special purpose registers
|
||||
public static final Register BASE_REG = Register.EBP;
|
||||
|
@ -27,7 +27,7 @@ public class RegisterManager {
|
|||
|
||||
public static final int SIZEOF_REG = 4;
|
||||
|
||||
|
||||
|
||||
public enum Register {
|
||||
EAX("%eax", ByteRegister.EAX), EBX("%ebx", ByteRegister.EBX), ECX(
|
||||
"%ecx", ByteRegister.ECX), EDX("%edx", ByteRegister.EDX), ESI(
|
||||
|
@ -99,18 +99,11 @@ public class RegisterManager {
|
|||
return registers.remove(last);
|
||||
}
|
||||
|
||||
public void useRegister(Register reg) {
|
||||
assert registers.contains(reg);
|
||||
assert reg != null;
|
||||
registers.remove(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* marks a currently used register as free
|
||||
*/
|
||||
public void releaseRegister(Register reg) {
|
||||
assert !registers.contains(reg);
|
||||
assert reg != null;
|
||||
registers.add(reg);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,18 +10,19 @@ import java.util.Map;
|
|||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
/**
|
||||
* Determines the maximum number of registers
|
||||
* required to execute one subtree. */
|
||||
/**
|
||||
* Determines the maximum number of registers
|
||||
* required to execute one subtree.
|
||||
*/
|
||||
public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
||||
|
||||
|
||||
public int calc(Ast ast) {
|
||||
return visit(ast, null);
|
||||
}
|
||||
|
||||
private Map<Ast,Integer> memo = new HashMap<Ast, Integer>();
|
||||
|
||||
/**
|
||||
|
||||
private Map<Ast, Integer> memo = new HashMap<Ast, Integer>();
|
||||
|
||||
/**
|
||||
* Override visit() so as to memorize the results and avoid
|
||||
* unnecessary computation
|
||||
*/
|
||||
|
@ -31,9 +32,9 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
|||
return memo.get(ast);
|
||||
Integer res = ast.accept(this, null);
|
||||
memo.put(ast, res);
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Integer dflt(Ast ast, Void arg) {
|
||||
// For a non-expression, it suffices to find the
|
||||
|
@ -54,8 +55,8 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
|||
public Integer binaryOp(BinaryOp ast, Void arg) {
|
||||
int left = calc(ast.left());
|
||||
int right = calc(ast.right());
|
||||
int ifLeftFirst = max(left, right+1);
|
||||
int ifRightFirst = max(left+1, right);
|
||||
int ifLeftFirst = max(left, right + 1);
|
||||
int ifRightFirst = max(left + 1, right);
|
||||
int overall = min(ifLeftFirst, ifRightFirst);
|
||||
return overall;
|
||||
}
|
||||
|
@ -64,7 +65,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
|||
public Integer assign(Assign ast, Void arg) {
|
||||
return max(calc(ast.left()) + 1, calc(ast.right()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer booleanConst(BooleanConst ast, Void arg) {
|
||||
return 1;
|
||||
|
@ -74,7 +75,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
|||
public Integer builtInRead(BuiltInRead ast, Void arg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer cast(Cast ast, Void arg) {
|
||||
return calc(ast.arg());
|
||||
|
@ -84,7 +85,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
|||
public Integer index(Index ast, Void arg) {
|
||||
return max(calc(ast.left()), calc(ast.right()) + 1);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer field(Field ast, Void arg) {
|
||||
return calc(ast.arg());
|
||||
|
@ -114,7 +115,7 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
|||
public Integer thisRef(ThisRef ast, Void arg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer methodCall(MethodCallExpr ast, Void arg) {
|
||||
int max = 1;
|
||||
|
@ -134,5 +135,5 @@ public class RegsNeededVisitor extends AstVisitor<Integer, Void> {
|
|||
@Override
|
||||
public Integer var(Var ast, Void arg) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,31 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.Config;
|
||||
import cd.backend.ExitCode;
|
||||
import cd.backend.codegen.AstCodeGenerator.Label;
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
import cd.frontend.semantic.ReturnCheckerVisitor;
|
||||
import cd.ir.Ast;
|
||||
import cd.ir.Ast.*;
|
||||
import cd.ir.AstVisitor;
|
||||
import cd.ir.Symbol;
|
||||
import cd.ir.Symbol.ClassSymbol;
|
||||
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.*;
|
||||
import java.util.List;
|
||||
|
||||
import static cd.backend.codegen.AstCodeGenerator.VAR_SIZE;
|
||||
import static cd.backend.codegen.AstCodeGenerator.TRUE;
|
||||
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,Location> {
|
||||
class StmtGenerator extends AstVisitor<Register, Void> {
|
||||
protected final AstCodeGenerator cg;
|
||||
|
||||
StmtGenerator(AstCodeGenerator astCodeGenerator) {
|
||||
cg = astCodeGenerator;
|
||||
|
||||
}
|
||||
|
||||
public void gen(Ast ast) {
|
||||
|
@ -36,253 +33,337 @@ class StmtGenerator extends AstVisitor<Register,Location> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Register visit(Ast ast, Location arg) {
|
||||
public Register visit(Ast ast, Void arg) {
|
||||
try {
|
||||
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
|
||||
if (cg.rnv.calc(ast) > cg.rm.availableRegisters()) {
|
||||
Deque<Register> pushed = new ArrayDeque<>();
|
||||
for (Register r : RegisterManager.GPR) {
|
||||
if (cg.rm.isInUse(r)) {
|
||||
cg.emit.emit("push", r);
|
||||
pushed.push(r);
|
||||
cg.rm.releaseRegister(r);
|
||||
}
|
||||
}
|
||||
Register result = super.visit(ast, arg);
|
||||
for (Register r : pushed)
|
||||
cg.rm.useRegister(r);
|
||||
Register finalResult = cg.rm.getRegister();
|
||||
cg.emit.emitMove(result, finalResult);
|
||||
for (Register r = pushed.pop(); !pushed.isEmpty(); r = pushed.pop())
|
||||
cg.emit.emit("pop", r);
|
||||
return finalResult;
|
||||
} else return super.visit(ast, arg);
|
||||
return super.visit(ast, arg);
|
||||
} finally {
|
||||
cg.emit.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register methodCall(MethodCall ast, Location arg) {
|
||||
Register result = cg.eg.visit(ast.getMethodCallExpr(), arg);
|
||||
if (result != null)
|
||||
cg.rm.releaseRegister(result);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Register methodCall(MethodSymbol sym, List<Expr> allArguments) {
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
|
||||
/**
|
||||
* vtable structure for a class type: pointer to superclass, and methods, each with 2 entries, name's hashCode and
|
||||
* pointer to method execution
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 classDecl(ClassDecl ast, Location arg) {
|
||||
cg.emit.emitRaw(Config.DATA_INT_SECTION);
|
||||
// Emit vtable for class
|
||||
cg.emit.emitLabel(Label.type(ast.sym)); // Label
|
||||
cg.emit.emitConstantData(Label.type(ast.sym.superClass)); // Superclass
|
||||
// Methods (don't write those that are overridden twice)
|
||||
Set<String> generated = new HashSet<>();
|
||||
ClassSymbol currSym = ast.sym;
|
||||
while (currSym != ClassSymbol.objectType) {
|
||||
ClassSymbol finalCurrSym = currSym;
|
||||
currSym.methods.values().forEach(o -> {
|
||||
if (!generated.add(o.name)) return;
|
||||
cg.emit.emitConstantData(String.valueOf(o.name.hashCode()));
|
||||
cg.emit.emitConstantData(Label.method(finalCurrSym, o));
|
||||
});
|
||||
currSym = currSym.superClass;
|
||||
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);
|
||||
}
|
||||
// End of class vtable
|
||||
// Array vtable
|
||||
boolean found = false;
|
||||
for (Symbol.TypeSymbol type : cg.main.allTypeSymbols) {
|
||||
if (type instanceof Symbol.ArrayTypeSymbol) {
|
||||
Symbol.ArrayTypeSymbol aType = (Symbol.ArrayTypeSymbol) type;
|
||||
if (aType.elementType == ast.sym) {
|
||||
cg.emit.emitLabel(Label.type(aType)); // Label
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
throw new RuntimeException("The array type could not be found");
|
||||
cg.emit.emitConstantData(Label.type(ClassSymbol.objectType)); // Supertype
|
||||
cg.emit.emitConstantData(Label.type(ast.sym)); // Type of elements
|
||||
// End of array vtable
|
||||
cg.emit.emitRaw(Config.TEXT_SECTION);
|
||||
|
||||
// Method bodies
|
||||
for (Ast method : ast.methods())
|
||||
visit(method, new Location(ast.sym));
|
||||
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
class AssignVisitor extends ExprVisitor<Void, Expr> {
|
||||
|
||||
@Override
|
||||
public Register methodDecl(MethodDecl ast, Location arg) {
|
||||
// Bookkeeping for framework
|
||||
arg.enterMethod(ast.sym);
|
||||
cg.initMethodData();
|
||||
@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;
|
||||
}
|
||||
|
||||
// Begin method
|
||||
cg.emit.emitLabel(Label.method(arg.classSym(), ast.sym));
|
||||
@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;
|
||||
}
|
||||
|
||||
// 1. Save and update the base register, reserve space for local variables
|
||||
cg.emit.emit("enter", ast.sym.locals.size() * VAR_SIZE, 0);
|
||||
for (int i = 0; i < ast.sym.locals.size(); i++)
|
||||
cg.emit.emitStore(0, -(i + 1) * VAR_SIZE, BASE_REG);
|
||||
@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);
|
||||
|
||||
// 2. Save CPU registers
|
||||
Register[] regs = RegisterManager.CALLEE_SAVE;
|
||||
for (int i = 0; i < regs.length; i++)
|
||||
cg.emit.emit("push", regs[i]);
|
||||
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);
|
||||
}
|
||||
|
||||
// 3. Run the code contained in the function
|
||||
Register returnReg = visit(ast.body(), arg);
|
||||
cg.emit.emitLabel(Label.returnMethod(arg.classSym(), ast.sym));
|
||||
regs = cgRef.egRef.genPushing(arrReg, ast.right());
|
||||
arrReg = regs.a;
|
||||
Register idxReg = regs.b;
|
||||
|
||||
// 4. Restore saved registers (if there is a mismatch, the stack will be corrupted)
|
||||
for (int i = regs.length - 1; i >= 0; i--)
|
||||
cg.emit.emit("pop", regs[i]);
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 5 and 6: Restore the old base pointer and return from the function
|
||||
cg.emitMethodSuffix(returnReg == null);
|
||||
cgRef.emit.emitMove(rhsReg, arrayAddress(arrReg, idxReg));
|
||||
cgRef.rm.releaseRegister(arrReg);
|
||||
cgRef.rm.releaseRegister(idxReg);
|
||||
cgRef.rm.releaseRegister(rhsReg);
|
||||
|
||||
// Framework bookkeeping
|
||||
arg.leaveMethod();
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void dfltExpr(Expr ast, Expr arg) {
|
||||
throw new RuntimeException("Store to unexpected lvalue " + ast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register ifElse(IfElse ast, Location arg) {
|
||||
// Emit condition check
|
||||
Register conditionRegister = cg.eg.visit(ast.condition(), arg);
|
||||
// If both blocks are empty, no more code need be generated, and the condition can be ignored
|
||||
if (ast.then().children().isEmpty() && ast.otherwise().children().isEmpty()) {
|
||||
// Generate the condition and ignore the result
|
||||
cg.rm.releaseRegister(conditionRegister);
|
||||
return null;
|
||||
}
|
||||
String notIfLabel = cg.emit.uniqueLabel();
|
||||
String endLabel = cg.emit.uniqueLabel();
|
||||
|
||||
cg.emit.emit("cmp", TRUE, conditionRegister);
|
||||
cg.rm.releaseRegister(conditionRegister);
|
||||
if (!ast.then().children().isEmpty()) {
|
||||
cg.emit.emit("jne", notIfLabel);
|
||||
visit(ast.then(), arg);
|
||||
// If there is no otherwise, the jump instruction makes no sense
|
||||
// as the next code executed will be the next instruction
|
||||
if (!ast.otherwise().children().isEmpty())
|
||||
cg.emit.emit("jmp", endLabel);
|
||||
} else {
|
||||
// No if, therefore the else follows the condition immediately
|
||||
cg.emit.emit("je", endLabel);
|
||||
}
|
||||
|
||||
cg.emit.emitLabel(notIfLabel);
|
||||
// Emit otherwise
|
||||
visit(ast.otherwise(), arg);
|
||||
cg.emit.emitLabel(endLabel);
|
||||
|
||||
// Check if the ifElse ast node contains a return statement
|
||||
ReturnCheckerVisitor rc = new ReturnCheckerVisitor();
|
||||
if (rc.ifElse(ast, null)) {
|
||||
return Register.EAX;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register whileLoop(WhileLoop ast, Location arg) {
|
||||
String conditionLabel = cg.emit.uniqueLabel();
|
||||
|
||||
cg.emit.emitLabel(conditionLabel);
|
||||
Register conditionRegister = cg.eg.visit(ast.condition(), arg);
|
||||
cg.emit.emit("cmp", TRUE, conditionRegister);
|
||||
cg.rm.releaseRegister(conditionRegister);
|
||||
if (ast.body().children().isEmpty()) {
|
||||
// Jump structure can be easier if there is no body
|
||||
cg.emit.emit("je", conditionLabel);
|
||||
} else {
|
||||
String endLabel = cg.emit.uniqueLabel();
|
||||
// Emit jumps, labels and body
|
||||
cg.emit.emit("jne", endLabel);
|
||||
visit(ast.body(), arg);
|
||||
cg.emit.emit("jmp", conditionLabel);
|
||||
cg.emit.emitLabel(endLabel);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register assign(Assign ast, Location arg) {
|
||||
Register value = cg.eg.visit(ast.right(), arg);
|
||||
|
||||
// If the type is a reference, visiting the lhs will yield its address
|
||||
// else, the lhs will yield the current value. We need a visitor that
|
||||
// returns a pointer to the position of the variable/field in memory
|
||||
// for primitive types.
|
||||
Register pointer = cg.eg.visit(ast.left(), arg.obtainReference());
|
||||
if (ast.left().type.isReferenceType()) {
|
||||
// Check null pointer
|
||||
String validPointerLabel = cg.emit.uniqueLabel();
|
||||
cg.emit.emit("cmp", 0, pointer);
|
||||
cg.emit.emit("jne", validPointerLabel);
|
||||
Interrupts.exit(cg, ExitCode.NULL_POINTER);
|
||||
cg.emit.emitLabel(validPointerLabel);
|
||||
}
|
||||
cg.emit.emitStore(value, 0, pointer);
|
||||
cg.rm.releaseRegister(pointer);
|
||||
cg.rm.releaseRegister(value);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register builtInWrite(BuiltInWrite ast, Location arg) {
|
||||
Register reg = cg.eg.visit(ast.arg(), arg);
|
||||
cg.emit.emit("sub", 16, STACK_REG);
|
||||
cg.emit.emitStore(reg, 4, STACK_REG);
|
||||
cg.emit.emitStore("$STR_D", 0, STACK_REG);
|
||||
cg.emit.emit("call", Config.PRINTF);
|
||||
cg.emit.emit("add", 16, STACK_REG);
|
||||
cg.rm.releaseRegister(reg);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register builtInWriteln(BuiltInWriteln ast, Location arg) {
|
||||
cg.emit.emit("sub", 16, STACK_REG);
|
||||
cg.emit.emitStore("$STR_NL", 0, STACK_REG);
|
||||
cg.emit.emit("call", Config.PRINTF);
|
||||
cg.emit.emit("add", 16, STACK_REG);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register returnStmt(ReturnStmt ast, Location arg) {
|
||||
if (ast.arg() != null) {
|
||||
Register retReg = cg.eg.visit(ast.arg(), arg);
|
||||
cg.emit.emitMove(retReg, Register.EAX);
|
||||
cg.rm.releaseRegister(retReg);
|
||||
}
|
||||
cg.emit.emit("jmp", Label.returnMethod(arg.classSym(), arg.methodSym()));
|
||||
return Register.EAX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register seq(Seq ast, Location arg) {
|
||||
for (Ast instruction : ast.children()) {
|
||||
Register res = visit(instruction, arg);
|
||||
if (res != null)
|
||||
return res; // don't generate instructions after a return
|
||||
}
|
||||
new AssignVisitor().visit(ast.left(), ast.right());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue