Homework 4

This commit is contained in:
Carlos Galindo 2020-01-15 22:34:57 +01:00
commit 72cc3206c4
Signed by: kauron
GPG key ID: 83E68706DEE119A3
125 changed files with 4200 additions and 1636 deletions

View file

@ -0,0 +1,19 @@
package cd.backend;
public enum ExitCode {
OK(0),
INVALID_DOWNCAST(1),
INVALID_ARRAY_STORE(2),
INVALID_ARRAY_BOUNDS(3),
NULL_POINTER(4),
INVALID_ARRAY_SIZE(5),
INFINITE_LOOP(6),
DIVISION_BY_ZERO(7),
INTERNAL_ERROR(22);
public final int value;
private ExitCode(int value) {
this.value = value;
}
}

View file

@ -0,0 +1,176 @@
package cd.backend.codegen;
import cd.Config;
import cd.backend.codegen.RegisterManager.Register;
import java.io.IOException;
import java.io.Writer;
public class AssemblyEmitter {
public Writer out;
public StringBuilder indent = new StringBuilder();
public int counter = 0;
public AssemblyEmitter(Writer out) {
this.out = out;
}
/** Creates an constant operand. */
static String constant(int i) {
return "$" + i;
}
/** Creates an constant operand with the address of a label. */
static String labelAddress(String lbl) {
return "$" + lbl;
}
/** 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 */
static String arrayAddress(Register arrReg, Register idxReg) {
final int offset = Config.SIZEOF_PTR * 2; // one word each in front for
// 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);
}
void increaseIndent(String comment) {
indent.append(" ");
if (comment != null)
emitComment(comment);
}
void decreaseIndent() {
indent.setLength(indent.length() - 2);
}
void emitCommentSection(String name) {
int indentLen = indent.length();
int breakLen = 68 - indentLen - name.length();
StringBuffer sb = new StringBuffer();
sb.append(Config.COMMENT_SEP).append(" ");
for (int i = 0; i < indentLen; i++)
sb.append("_");
sb.append(name);
for (int i = 0; i < breakLen; i++)
sb.append("_");
try {
out.write(sb.toString());
out.write("\n");
} catch (IOException e) {
}
}
void emitComment(String comment) {
emitRaw(Config.COMMENT_SEP + " " + comment);
}
void emit(String op, Register src, String dest) {
emit(op, src.repr, dest);
}
void emit(String op, String src, Register dest) {
emit(op, src, dest.repr);
}
void emit(String op, Register src, Register dest) {
emit(op, src.repr, dest.repr);
}
void emit(String op, String src, String dest) {
emitRaw(String.format("%s %s, %s", op, src, dest));
}
void emit(String op, int src, Register dest) {
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);
}
void emit(String op, Register reg) {
emit(op, reg.repr);
}
void emit(String op, int dest) {
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);
}
void emitMove(Register src, Register dest) {
emitMove(src.repr, dest.repr);
}
void emitMove(String src, Register dest) {
emitMove(src, dest.repr);
}
void emitMove(String src, String dest) {
if (!src.equals(dest))
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);
}
void emitStore(Register src, int destOffset, Register dest) {
emitStore(src.repr, destOffset, dest);
}
void emitStore(String src, int destOffset, Register dest) {
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));
}
String uniqueLabel() {
String labelName = "label" + counter++;
return labelName;
}
void emitLabel(String label) {
try {
out.write(label + ":" + "\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
void emitRaw(String op) {
try {
out.write(indent.toString());
out.write(op);
out.write("\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,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;
}
}

View file

@ -0,0 +1,172 @@
package cd.backend.codegen;
import cd.Config;
import cd.Main;
import cd.backend.codegen.RegisterManager.Register;
import cd.ir.Ast;
import cd.ir.Ast.ClassDecl;
import cd.ir.Symbol.*;
import java.io.Writer;
import java.util.*;
import static cd.Config.MAIN;
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 final Main main;
final AssemblyEmitter emit;
final RegisterManager rm = new RegisterManager();
AstCodeGenerator(Main main, Writer out) {
initMethodData();
main.allTypeSymbols = new ArrayList<>();
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 AstCodeGenerator(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) {
// 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)
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.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 the new Main().main() code to start the program:
// 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);
// 2. Create main variable
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);
}
void initMethodData() {
rm.initRegisters();
}
void emitMethodSuffix(boolean returnNull) {
if (returnNull)
emit.emit("movl", 0, Register.EAX);
emit.emitRaw("leave");
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));
}
}
}

View file

@ -0,0 +1,613 @@
package cd.backend.codegen;
import cd.backend.ExitCode;
import cd.backend.codegen.AstCodeGenerator.*;
import cd.backend.codegen.RegisterManager.Register;
import cd.ir.Ast.*;
import cd.ir.ExprVisitor;
import cd.ir.Symbol.ClassSymbol;
import cd.ir.Symbol.MethodSymbol;
import cd.ir.Symbol.PrimitiveTypeSymbol;
import cd.ir.Symbol.VariableSymbol;
import cd.util.debug.AstOneLine;
import java.util.*;
import static cd.Config.SCANF;
import static cd.backend.codegen.AstCodeGenerator.*;
import static cd.backend.codegen.RegisterManager.BASE_REG;
import static cd.backend.codegen.RegisterManager.CALLER_SAVE;
import static cd.backend.codegen.RegisterManager.STACK_REG;
/**
* Generates code to evaluate expressions. After emitting the code, returns a
* String which indicates the register where the result can be found.
*/
class ExprGenerator extends ExprVisitor<Register,Location> {
private final AstCodeGenerator cg;
ExprGenerator(AstCodeGenerator astCodeGenerator) {
cg = astCodeGenerator;
}
public Register gen(Expr ast) {
return visit(ast, null);
}
@Override
public Register visit(Expr ast, Location 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);
} finally {
cg.emit.decreaseIndent();
}
}
@Override
public Register binaryOp(BinaryOp ast, Location arg) {
// Simplistic HW1 implementation that does
// not care if it runs out of registers
int leftRN = cg.rnv.calc(ast.left());
int rightRN = cg.rnv.calc(ast.right());
Register leftReg, rightReg;
if (leftRN > rightRN) {
leftReg = visit(ast.left(), arg);
rightReg = visit(ast.right(), arg);
} else {
rightReg = visit(ast.right(), arg);
leftReg = visit(ast.left(), arg);
}
cg.debug("Binary Op: %s (%s,%s)", ast, leftReg, rightReg);
switch (ast.operator) {
case B_TIMES:
cg.emit.emit("imul", rightReg, leftReg);
break;
case B_PLUS:
cg.emit.emit("add", rightReg, leftReg);
break;
case B_MINUS:
cg.emit.emit("sub", rightReg, leftReg);
break;
case B_DIV:
case B_MOD:
// Check division by 0
String beginDivision = cg.emit.uniqueLabel();
cg.emit.emit("cmp", 0, rightReg);
cg.emit.emit("jne", beginDivision);
Interrupts.exit(cg, ExitCode.DIVISION_BY_ZERO);
cg.emit.emitLabel(beginDivision);
// Save EAX, EBX, and EDX to the stack if they are not used
// in this subtree (but are used elsewhere). We will be
// changing them.
List<Register> dontBother = Arrays.asList(rightReg, leftReg);
Register[] affected = { Register.EAX, Register.EBX, Register.EDX };
for (Register s : affected)
if (!dontBother.contains(s) && cg.rm.isInUse(s))
cg.emit.emit("pushl", s);
// Move the LHS (numerator) into eax
// Move the RHS (denominator) into ebx
cg.emit.emit("pushl", rightReg);
cg.emit.emit("pushl", leftReg);
cg.emit.emit("popl", Register.EAX);
cg.emit.emit("popl", Register.EBX);
cg.emit.emitRaw("cltd"); // sign-extend %eax into %edx
cg.emit.emit("idivl", Register.EBX); // division, result into edx:eax
// Move the result into the LHS, and pop off anything we saved
if (ast.operator == BinaryOp.BOp.B_DIV)
cg.emit.emitMove(Register.EAX, leftReg);
else
cg.emit.emitMove(Register.EDX, leftReg);
for (int i = affected.length - 1; i >= 0; i--) {
Register s = affected[i];
if (!dontBother.contains(s) && cg.rm.isInUse(s))
cg.emit.emit("popl", s);
}
break;
case B_AND:
cg.emit.emit("and", rightReg, leftReg);
break;
case B_OR:
cg.emit.emit("or", rightReg, leftReg);
break;
case B_EQUAL: // a == b <--> ! (a != b)
cg.emit.emit("xor", rightReg, leftReg);
// if 'leftReg'==0, set leftReg to '1'
String equal = cg.emit.uniqueLabel();
String end = cg.emit.uniqueLabel();
cg.emit.emit("cmp", 0, leftReg);
cg.emit.emit("je", equal);
cg.emit.emitMove(AssemblyEmitter.constant(0), leftReg);
cg.emit.emit("jmp", end);
cg.emit.emitLabel(equal);
cg.emit.emitMove(TRUE, leftReg);
cg.emit.emitLabel(end);
break;
case B_NOT_EQUAL:
String skipTrue = cg.emit.uniqueLabel();
cg.emit.emit("xor", rightReg, leftReg);
cg.emit.emit("cmp", 0, leftReg);
cg.emit.emit("je", skipTrue);
cg.emit.emitMove(TRUE, leftReg);
cg.emit.emitLabel(skipTrue);
break;
default: // Comparison operations
String endLabel = cg.emit.uniqueLabel();
cg.emit.emit("cmp", rightReg, leftReg);
// leftReg - rightReg
cg.emit.emitMove(TRUE, leftReg);
switch (ast.operator) {
case B_LESS_THAN:
cg.emit.emit("jl", endLabel);
break;
case B_LESS_OR_EQUAL:
cg.emit.emit("jle", endLabel);
break;
case B_GREATER_THAN:
cg.emit.emit("jg", endLabel);
break;
case B_GREATER_OR_EQUAL:
cg.emit.emit("jge", endLabel);
break;
default:
throw new RuntimeException("A binary operation wasn't implemented");
}
cg.emit.emitMove(FALSE, leftReg);
cg.emit.emitLabel(endLabel);
}
cg.rm.releaseRegister(rightReg);
return leftReg;
}
@Override
public Register booleanConst(BooleanConst ast, Location arg) {
Register reg = cg.rm.getRegister();
cg.emit.emitMove(ast.value ? TRUE : FALSE, reg);
return reg;
}
@Override
public Register builtInRead(BuiltInRead ast, Location arg) {
Register reg = cg.rm.getRegister();
cg.emit.emit("sub", 16, STACK_REG);
cg.emit.emit("leal", AssemblyEmitter.registerOffset(8, STACK_REG), reg);
cg.emit.emitStore(reg, 4, STACK_REG);
cg.emit.emitStore("$STR_D", 0, STACK_REG);
cg.emit.emit("call", SCANF);
cg.emit.emitLoad(8, STACK_REG, reg);
cg.emit.emit("add", 16, STACK_REG);
return reg;
}
/**
* A Cast from one type to another: {@code (typeName)arg}
*/
@Override
public Register cast(Cast ast, Location arg) {
// 1. Obtain a register with the desired type's address
Register desiredType = cg.rm.getRegister();
cg.emit.emit("lea", Label.type(ast.type), desiredType); // lea copies the label's address to the reg
// 2. Get a reference to the object to be casted, and push a copy for later
Register runtimeType = visit(ast.arg(), arg);
cg.emit.emit("push", runtimeType);
// 3. Go to the type's vtable
cg.emit.emitLoad(0, runtimeType, runtimeType);
// 4. Runtime type check: recursively go to superType until
String matchLabel = cg.emit.uniqueLabel();
String checkSuperTypeLabel = cg.emit.uniqueLabel();
cg.emit.emitLabel(checkSuperTypeLabel);
cg.emit.emit("cmpl", desiredType, runtimeType);
cg.emit.emit("je", matchLabel); // 4.1 It matches: ok
cg.emit.emitLoad(0, runtimeType, runtimeType); // Go to superclass
cg.emit.emit("cmp", 0, runtimeType); // 4.2 null is reached (super type of Object): error
cg.emit.emit("jne", checkSuperTypeLabel);
Interrupts.exit(cg, ExitCode.INVALID_DOWNCAST);
cg.emit.emitLabel(matchLabel);
cg.rm.releaseRegister(desiredType);
// 5. Recover pointer to object and return it
cg.emit.emit("pop", runtimeType);
return runtimeType;
}
@Override
public Register index(Index ast, Location arg) {
boolean obtainReference = arg.isObtainReference();
String invalidBoundLabel = cg.emit.uniqueLabel();
String validBoundLabel = cg.emit.uniqueLabel();
String checkBoundsLabel = cg.emit.uniqueLabel();
// Obtain pointer to array and index to be accessed
Register pointer = visit(ast.left(), arg);
Register index = visit(ast.right(), arg);
// check for null pointer
cg.emit.emit("cmp", 0, pointer);
cg.emit.emit("jne", checkBoundsLabel); // 0 != array
Interrupts.exit(cg, ExitCode.NULL_POINTER);
// check if index>=0 and index<arraySize
Register arraySizeReg = cg.rm.getRegister();
cg.emit.emitLabel(checkBoundsLabel);
cg.emit.emitLoad(1 * VAR_SIZE, pointer, arraySizeReg);
cg.emit.emit("cmp", 0, index);
cg.emit.emit("jl", invalidBoundLabel); // 0 > index
cg.emit.emit("cmp", index, arraySizeReg);
cg.emit.emit("jg", validBoundLabel); // index < array.length
cg.emit.emitLabel(invalidBoundLabel);
Interrupts.exit(cg, ExitCode.INVALID_ARRAY_BOUNDS);
cg.rm.releaseRegister(arraySizeReg);
// return array element (base + 2 * VAR_SIZE + index * VAR_SIZE)
cg.emit.emitLabel(validBoundLabel);
cg.emit.emit("imul", VAR_SIZE, index);
cg.emit.emit("add", pointer, index);
cg.rm.releaseRegister(pointer);
if (!obtainReference)
cg.emit.emitLoad(2 * VAR_SIZE, index, index);
else
cg.emit.emit("add", 2 * VAR_SIZE, index);
return index;
}
@Override
public Register intConst(IntConst ast, Location arg) {
Register reg = cg.rm.getRegister();
cg.emit.emitMove(ast.value, reg);
return reg;
}
@Override
public Register field(Field ast, Location arg) {
boolean obtainReference = arg.isObtainReference();
String accessFieldLabel = cg.emit.uniqueLabel();
Register pointer = visit(ast.arg(), arg);
cg.emit.emit("cmp", 0, pointer);
cg.emit.emit("jne", accessFieldLabel);
Interrupts.exit(cg, ExitCode.NULL_POINTER);
cg.emit.emitLabel(accessFieldLabel);
String foundFieldLabel = cg.emit.uniqueLabel();
String lookForFieldLabel = cg.emit.uniqueLabel();
cg.emit.emit("add", VAR_SIZE, pointer);
cg.emit.emitLabel(lookForFieldLabel);
cg.emit.emit("cmpl", ast.sym.hashCode(), AssemblyEmitter.registerOffset(0, pointer));
cg.emit.emit("je", foundFieldLabel);
cg.emit.emit("add", 2 * VAR_SIZE, pointer);
cg.emit.emit("jmp", lookForFieldLabel);
cg.emit.emitLabel(foundFieldLabel);
if (obtainReference)
cg.emit.emit("add", VAR_SIZE, pointer);
else
cg.emit.emitLoad(VAR_SIZE, pointer, pointer);
return pointer;
}
/**
* Array structure:
* <ul>
* <li>Type of array (for casts, assignments and equalities)</li>
* <li>Size of array (N)</li>
* <li>element 0 or pointer to element 0</li>
* <li>...</li>
* <li>element N or pointer to element N</li>
* </ul>
* The type of the elements is stored in the vtable for the array (in order to
* save space). <br/>
*
* The pointer that references the array points to the type of the array.
* The reason for this is so that all reference types have at pointer + 0
* the type, for dynamic type comparisons for assignments, casts and ==/!= operators
*/
@Override
public Register newArray(NewArray ast, Location arg) {
String validArraySizeLabel = cg.emit.uniqueLabel();
// Check size of array is positive
Register arraySizeReg = visit(ast.arg(), arg);
cg.emit.emit("cmp", 0, arraySizeReg);
cg.emit.emit("jns", validArraySizeLabel); // size >= 0
Interrupts.exit(cg, ExitCode.INVALID_ARRAY_SIZE);
// Reserve for length + 2 variables
cg.emit.emitLabel(validArraySizeLabel);
cg.emit.emit("push", arraySizeReg);
cg.emit.emit("add", 2, arraySizeReg);
Register arrayPointerReg = calloc(arraySizeReg);
// Store overhead information
// Type reference
Register arrayTypeReg = cg.rm.getRegister();
cg.emit.emit("lea", Label.type(ast.type), arrayTypeReg);
cg.emit.emitStore(arrayTypeReg, 0 * VAR_SIZE, arrayPointerReg);
cg.rm.releaseRegister(arrayTypeReg);
// Number of elements
Register numElemReg = cg.rm.getRegister();
cg.emit.emit("pop", numElemReg);
cg.emit.emitStore(numElemReg, 1 * VAR_SIZE, arrayPointerReg);
cg.rm.releaseRegister(numElemReg);
return arrayPointerReg;
}
/**
* Structure of reference type objects (except arrays)
* <ul>
* <li>Pointer to type vtable</li>
* <li>hashCode0, field0</li>
* <li>...</li>
* <li>hashCodeN, fieldN</li>
* </ul>
* The object pointer points to the first element, and every element
* is of VAR_SIZE
*/
@Override
public Register newObject(NewObject ast, Location arg) {
ClassSymbol sym = (ClassSymbol) ast.type;
// Obtain from type size of allocated object and allocate memory
int size = 1;
ClassSymbol auxSym = sym;
while (auxSym != ClassSymbol.objectType) {
size += auxSym.fields.size() * 2;
auxSym = auxSym.superClass;
}
Register sizeReg = cg.rm.getRegister();
cg.emit.emitMove(size, sizeReg);
Register pointer = calloc(sizeReg);
// Store the pointer to the type vtable
Register auxReg = cg.rm.getRegister();
cg.emit.emit("lea", Label.type(sym), auxReg);
cg.emit.emitStore(auxReg, 0, pointer);
cg.emit.emit("push", pointer); // Save the pointer to the beginning to return later
cg.rm.releaseRegister(auxReg);
// Store the hashes for the fields' variable symbols
cg.emit.emit("add", VAR_SIZE, pointer);
auxSym = sym;
while (auxSym != ClassSymbol.objectType) {
for (VariableSymbol field : auxSym.fields.values()) {
cg.emit.emitStore(field.hashCode(), 0, pointer);
cg.emit.emit("add", 2 * VAR_SIZE, pointer);
}
auxSym = auxSym.superClass;
}
// Recover the initial address and return
cg.emit.emit("pop", pointer);
return pointer;
}
@Override
public Register nullConst(NullConst ast, Location arg) {
Register reg = cg.rm.getRegister();
cg.emit.emitMove(0, reg);
return reg;
}
@Override
public Register thisRef(ThisRef ast, Location arg) {
Register register = cg.rm.getRegister();
cg.emit.emitLoad(2 * VAR_SIZE, BASE_REG, register);
return register;
}
/**
* implementation of x86 calling convention according to:
* http://unixwiz.net/techtips/win32-callconv-asm.html
* following the __cdecl calling convention
*/
@Override
public Register methodCall(MethodCallExpr ast, Location arg) {
// 0. Save registers to stack
List<Register> callerSaved = new ArrayList<>();
for (Register reg : CALLER_SAVE) {
if (cg.rm.isInUse(reg)) {
callerSaved.add(0, reg);
cg.emit.emit("push", Register.EAX);
}
}
// 1. Evaluate all the arguments left-to-right (according to Java's spec)
// and push them to the stack in right-to-left order. We will push them ltr
// and then swap them so that the order is reversed
for (Expr allArgument : ast.allArguments()) {
Register argumentRegister = visit(allArgument, arg);
cg.emit.emit("push", argumentRegister);
cg.rm.releaseRegister(argumentRegister);
}
for (int i = 0; i < ast.allArguments().size() / 2; i++) {
int offset1 = i * VAR_SIZE;
int offset2 = (ast.allArguments().size() - 1 - i) * VAR_SIZE;
Register aux1 = cg.rm.getRegister();
Register aux2 = cg.rm.getRegister();
cg.emit.emitLoad(offset1, STACK_REG, aux1);
cg.emit.emitLoad(offset2, STACK_REG, aux2);
cg.emit.emitStore(aux1, offset2, STACK_REG);
cg.emit.emitStore(aux2, offset1, STACK_REG);
cg.rm.releaseRegister(aux1);
cg.rm.releaseRegister(aux2);
}
// 2. Call function
// 2.1. Search for the method pointer using the hashCode
Register thisRef = cg.rm.getRegister();
cg.emit.emitLoad(0, STACK_REG, thisRef);
int hashCode = ast.methodName.hashCode();
String lookForMethod = cg.emit.uniqueLabel();
String methodFound = cg.emit.uniqueLabel();
cg.emit.emitLoad(0, thisRef, thisRef); // Go to type vtable
cg.emit.emit("add", 1 * VAR_SIZE, thisRef); // Skip the reference to superType
cg.emit.emitLabel(lookForMethod);
cg.emit.emit("cmpl", hashCode, AssemblyEmitter.registerOffset(0, thisRef));
cg.emit.emit("je", methodFound); // hashCode == methodName.hashCode
cg.emit.emit("add", 2 * VAR_SIZE, thisRef); // Go to next hashCode
cg.emit.emit("jmp", lookForMethod);
cg.emit.emitLabel(methodFound);
// 2.2. Call the function
cg.emit.emit("call", AssemblyEmitter.registerOffset(VAR_SIZE, thisRef));
cg.rm.releaseRegister(thisRef);
// 3. Pop arguments from stack
cg.emit.emit("add", VAR_SIZE * ast.allArguments().size(), STACK_REG);
// 4. Return result to caller
Register result = null;
if (ast.sym.returnType != PrimitiveTypeSymbol.voidType) {
result = cg.rm.getRegister();
cg.emit.emitMove(Register.EAX, result);
}
// 5. Restore registers
for (Register reg : callerSaved) {
if (cg.rm.isInUse(reg))
cg.emit.emit("pop", Register.EAX);
else
cg.emit.emit("add", 4, Register.ESP); // Don't overwrite
}
return result;
}
@Override
public Register unaryOp(UnaryOp ast, Location arg) {
Register argReg = visit(ast.arg(), arg);
switch (ast.operator) {
case U_PLUS:
break;
case U_MINUS:
cg.emit.emit("negl", argReg);
break;
case U_BOOL_NOT:
cg.emit.emit("negl", argReg);
cg.emit.emit("incl", argReg);
break;
}
return argReg;
}
/**
* Obtains the value or reference to a variable described by a Var node
* and a VariableSymbol.
*/
@Override
public Register var(Var ast, Location arg) {
boolean obtainReference = arg.isObtainReference();
Register register = cg.rm.getRegister();
cg.emit.emitMove(BASE_REG, register);
int offset;
switch (ast.sym.kind) {
case PARAM:
offset = 3 + arg.methodSym().parameters.indexOf(ast.sym);
break;
case LOCAL:
offset = -(1 + positionOfLocal(arg.methodSym(), ast.name));
break;
case FIELD:
String foundFieldLabel = cg.emit.uniqueLabel();
String lookForFieldLabel = cg.emit.uniqueLabel();
cg.emit.emitLoad(2 * VAR_SIZE, register, register);
cg.emit.emit("add", VAR_SIZE, register);
cg.emit.emitLabel(lookForFieldLabel);
cg.emit.emit("cmpl", ast.sym.hashCode(), AssemblyEmitter.registerOffset(0, register));
cg.emit.emit("je", foundFieldLabel);
cg.emit.emit("add", 2 * VAR_SIZE, register);
cg.emit.emit("jmp", lookForFieldLabel);
cg.emit.emitLabel(foundFieldLabel);
offset = 1; // The next element will be the reference we want
break;
default:
throw new RuntimeException("VariableSymbol Kind option not implemented");
}
if (obtainReference)
cg.emit.emit("add", offset * VAR_SIZE, register);
else
cg.emit.emitLoad(offset * VAR_SIZE, register, register);
return register;
}
private int positionOfLocal(MethodSymbol sym, String name) {
List<String> locals = new ArrayList<>(sym.locals.keySet());
locals.sort(Comparator.comparing(o -> o));
for (int i = 0; i < locals.size(); i++)
if (locals.get(i).equals(name))
return i;
throw new RuntimeException("The variable could not be found, and should be there");
}
/**
* Helper function that calls calloc to obtain memory for reference type variables
* @param numberOfElements Size in bytes, stored in a register
* @return A register with the first address of the memory allocated
*/
private Register calloc(Register numberOfElements) {
cg.emit.emitComment("calloc begin arg=" + numberOfElements);
String callocOk = cg.emit.uniqueLabel();
boolean eaxInUse = cg.rm.isInUse(Register.EAX);
if (eaxInUse && Register.EAX != numberOfElements)
cg.emit.emit("push", Register.EAX);
// push arguments to the stack, then call calloc
// the size of allocated elements is always four bytes!
cg.emit.emit("push", AssemblyEmitter.constant(VAR_SIZE));
cg.emit.emit("push", numberOfElements);
cg.rm.releaseRegister(numberOfElements);
cg.emit.emit("call", "calloc");
cg.emit.emit("add", 8, STACK_REG);
// Check for null pointer (if calloc fails)
cg.emit.emit("cmp", 0, Register.EAX);
cg.emit.emit("jne", callocOk);
Interrupts.exit(cg, ExitCode.INTERNAL_ERROR);
cg.emit.emitLabel(callocOk);
Register result = cg.rm.getRegister();
cg.emit.emitMove(Register.EAX, result);
// pop EAX if needed
if (eaxInUse && Register.EAX != numberOfElements)
cg.emit.emit("pop", Register.EAX);
cg.emit.emitComment("calloc end");
return result;
}
}

View file

@ -0,0 +1,19 @@
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);
}
}

View file

@ -0,0 +1,42 @@
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;
}
}

View file

@ -0,0 +1,130 @@
package cd.backend.codegen;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Simple class that manages the set of currently used
* and unused registers
*/
public class RegisterManager {
private List<Register> registers = new ArrayList<Register>();
// lists of register to save by the callee and the caller
public static final Register CALLEE_SAVE[] = new Register[]{Register.ESI,
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};
// special purpose registers
public static final Register BASE_REG = Register.EBP;
public static final Register STACK_REG = Register.ESP;
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(
"%esi", null), EDI("%edi", null), EBP("%ebp", null), ESP(
"%esp", null);
public final String repr;
private final ByteRegister lowByteVersion;
private Register(String repr, ByteRegister bv) {
this.repr = repr;
this.lowByteVersion = bv;
}
@Override
public String toString() {
return repr;
}
/**
* determines if this register has an 8bit version
*/
public boolean hasLowByteVersion() {
return lowByteVersion != null;
}
/**
* Given a register like {@code %eax} returns {@code %al}, but doesn't
* work for {@code %esi} and {@code %edi}!
*/
public ByteRegister lowByteVersion() {
assert hasLowByteVersion();
return lowByteVersion;
}
}
public enum ByteRegister {
EAX("%al"), EBX("%bl"), ECX("%cl"), EDX("%dl");
public final String repr;
private ByteRegister(String repr) {
this.repr = repr;
}
@Override
public String toString() {
return repr;
}
}
/**
* Reset all general purpose registers to free
*/
public void initRegisters() {
registers.clear();
registers.addAll(Arrays.asList(GPR));
}
/**
* returns a free register and marks it as used
*/
public Register getRegister() {
int last = registers.size() - 1;
if (last < 0)
throw new AssemblyFailedException(
"Program requires too many registers");
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);
}
/**
* Returns whether the register is currently non-free
*/
public boolean isInUse(Register reg) {
return !registers.contains(reg);
}
/**
* returns the number of free registers
*/
public int availableRegisters() {
return registers.size();
}
}

View file

@ -0,0 +1,138 @@
package cd.backend.codegen;
import cd.ir.Ast;
import cd.ir.Ast.*;
import cd.ir.AstVisitor;
import java.util.HashMap;
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. */
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>();
/**
* Override visit() so as to memorize the results and avoid
* unnecessary computation
*/
@Override
public Integer visit(Ast ast, Void arg) {
if (memo.containsKey(ast))
return memo.get(ast);
Integer res = ast.accept(this, null);
memo.put(ast, res);
return res;
}
@Override
protected Integer dflt(Ast ast, Void arg) {
// For a non-expression, it suffices to find the
// maximum registers used by any individual expression.
int maxReg = 0;
for (Ast a : ast.children()) {
maxReg = Math.max(calc(a), maxReg);
}
return maxReg;
}
@Override
protected Integer dfltExpr(Expr ast, Void arg) {
throw new RuntimeException("Should never be used");
}
@Override
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 overall = min(ifLeftFirst, ifRightFirst);
return overall;
}
@Override
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;
}
@Override
public Integer builtInRead(BuiltInRead ast, Void arg) {
return 1;
}
@Override
public Integer cast(Cast ast, Void arg) {
return calc(ast.arg());
}
@Override
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());
}
@Override
public Integer intConst(IntConst ast, Void arg) {
return 1;
}
@Override
public Integer newArray(NewArray ast, Void arg) {
return calc(ast.arg());
}
@Override
public Integer newObject(NewObject ast, Void arg) {
return 1;
}
@Override
public Integer nullConst(NullConst ast, Void arg) {
return 1;
}
@Override
public Integer thisRef(ThisRef ast, Void arg) {
return 1;
}
@Override
public Integer methodCall(MethodCallExpr ast, Void arg) {
int max = 1;
for (Expr argex : ast.allArguments()) {
int needed = calc(argex);
if (needed > max)
max = needed;
}
return max;
}
@Override
public Integer unaryOp(UnaryOp ast, Void arg) {
return calc(ast.arg());
}
@Override
public Integer var(Var ast, Void arg) {
return 1;
}
}

View file

@ -0,0 +1,288 @@
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.Symbol.MethodSymbol;
import cd.util.debug.AstOneLine;
import java.util.*;
import static cd.backend.codegen.AstCodeGenerator.VAR_SIZE;
import static cd.backend.codegen.AstCodeGenerator.TRUE;
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> {
protected final AstCodeGenerator cg;
StmtGenerator(AstCodeGenerator astCodeGenerator) {
cg = astCodeGenerator;
}
public void gen(Ast ast) {
visit(ast, null);
}
@Override
public Register visit(Ast ast, Location 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);
} 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
*/
@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;
}
// 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;
}
}
}
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;
}
@Override
public Register methodDecl(MethodDecl ast, Location arg) {
// Bookkeeping for framework
arg.enterMethod(ast.sym);
cg.initMethodData();
// Begin method
cg.emit.emitLabel(Label.method(arg.classSym(), ast.sym));
// 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);
// 2. Save CPU registers
Register[] regs = RegisterManager.CALLEE_SAVE;
for (int i = 0; i < regs.length; i++)
cg.emit.emit("push", regs[i]);
// 3. Run the code contained in the function
Register returnReg = visit(ast.body(), arg);
cg.emit.emitLabel(Label.returnMethod(arg.classSym(), ast.sym));
// 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]);
// 5 and 6: Restore the old base pointer and return from the function
cg.emitMethodSuffix(returnReg == null);
// Framework bookkeeping
arg.leaveMethod();
return null;
}
@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
}
return null;
}
}