763 lines
22 KiB
Java
763 lines
22 KiB
Java
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.Collections;
|
|
import java.util.List;
|
|
|
|
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 {
|
|
|
|
protected RegsNeededVisitor rnv;
|
|
|
|
protected ExprGenerator eg;
|
|
protected StmtGenerator sg;
|
|
|
|
protected final Main main;
|
|
|
|
protected final AssemblyEmitter emit;
|
|
protected final RegisterManager rm = new RegisterManager();
|
|
|
|
protected ExprGeneratorRef egRef;
|
|
protected StmtGeneratorRef sgRef;
|
|
|
|
AstCodeGenerator(Main main, Writer out) {
|
|
initMethodData();
|
|
|
|
this.emit = new AssemblyEmitter(out);
|
|
this.main = main;
|
|
this.rnv = new RegsNeededVisitor();
|
|
|
|
this.eg = new ExprGenerator(this);
|
|
this.sg = new StmtGenerator(this);
|
|
}
|
|
|
|
protected void debug(String format, Object... args) {
|
|
this.main.debug(format, args);
|
|
}
|
|
|
|
public static AstCodeGenerator createCodeGenerator(Main main, Writer 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>
|
|
* <li>Prologue: Generated by {@link #emitPrefix()}. This contains any
|
|
* introductory declarations and the like.
|
|
* <li>Body: Generated by {@link ExprGenerator}. This contains the main
|
|
* method definitions.
|
|
* </ol>
|
|
*/
|
|
public void go(List<? extends ClassDecl> astRoots) {
|
|
for (ClassDecl ast : astRoots) {
|
|
sg.gen(ast);
|
|
}
|
|
}
|
|
|
|
|
|
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\"");
|
|
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);
|
|
|
|
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");
|
|
}
|
|
|
|
// Generate a helper method for checking for null ptrs:
|
|
{
|
|
String oknulllbl = emit.uniqueLabel();
|
|
emit.emitCommentSection(CHECK_NULL + " function");
|
|
emit.emitLabel(CHECK_NULL);
|
|
|
|
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", 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;
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
@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);
|
|
}
|
|
}
|
|
|
|
|
|
@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);
|
|
restoreCalleeSaveRegs();
|
|
emit.emitMove(BASE_REG, STACK_REG);
|
|
emit.emit("pop", BASE_REG);
|
|
emit.emitRaw("ret");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
}
|