Homework 2
This commit is contained in:
parent
12f678a924
commit
bf60a078d7
64 changed files with 4786 additions and 1185 deletions
|
@ -1,164 +0,0 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import cd.Config;
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
|
||||
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, String dest) {
|
||||
emitRaw(op + " " + dest);
|
||||
}
|
||||
|
||||
void emit(String op) {
|
||||
emitRaw(op);
|
||||
}
|
||||
|
||||
void emit(String op, Register reg) {
|
||||
emit(op, reg.repr);
|
||||
}
|
||||
|
||||
void emit(String op, int dest) {
|
||||
emit(op, 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 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 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.Main;
|
||||
import cd.ir.Ast.ClassDecl;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
|
||||
public class AstCodeGenerator {
|
||||
|
||||
protected ExprGenerator eg;
|
||||
protected StmtGenerator sg;
|
||||
|
||||
protected final Main main;
|
||||
|
||||
protected final AssemblyEmitter emit;
|
||||
protected final RegisterManager rm = new RegisterManager();
|
||||
|
||||
AstCodeGenerator(Main main, Writer out) {
|
||||
this.emit = new AssemblyEmitter(out);
|
||||
this.main = main;
|
||||
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) {
|
||||
for (ClassDecl ast : astRoots) {
|
||||
sg.gen(ast);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void initMethodData() {
|
||||
}
|
||||
}
|
|
@ -1,307 +0,0 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
import cd.ir.Ast.*;
|
||||
import cd.ir.Ast.BinaryOp.BOp;
|
||||
import cd.ir.ExprVisitor;
|
||||
import cd.util.debug.AstOneLine;
|
||||
|
||||
/**
|
||||
* 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, Void> {
|
||||
protected final AstCodeGenerator cg;
|
||||
|
||||
ExprGenerator(AstCodeGenerator astCodeGenerator) {
|
||||
cg = astCodeGenerator;
|
||||
}
|
||||
|
||||
public Register gen(Expr ast) {
|
||||
return visit(ast, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register visit(Expr ast, Void arg) {
|
||||
try {
|
||||
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
|
||||
return super.visit(ast, null);
|
||||
} finally {
|
||||
cg.emit.decreaseIndent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register binaryOp(BinaryOp ast, Void arg) {
|
||||
String op;
|
||||
if (ast.operator == BOp.B_TIMES)
|
||||
op = "imul";
|
||||
else if (ast.operator == BOp.B_PLUS)
|
||||
op = "add";
|
||||
else if (ast.operator == BOp.B_MINUS)
|
||||
op = "sub";
|
||||
else if (ast.operator == BOp.B_DIV)
|
||||
op = "div";
|
||||
else
|
||||
throw new RuntimeException("Not required (optional)");
|
||||
op += "l"; // long = 4B = 32b
|
||||
|
||||
Register left, right;
|
||||
int leftNeeded = ast.left().registersNeeded();
|
||||
int rightNeeded = ast.right().registersNeeded();
|
||||
if (leftNeeded > rightNeeded) {
|
||||
left = cg.eg.visit(ast.left(), arg);
|
||||
right = cg.eg.visit(ast.right(), arg);
|
||||
} else {
|
||||
right = cg.eg.visit(ast.right(), arg);
|
||||
left = cg.eg.visit(ast.left(), arg);
|
||||
}
|
||||
|
||||
if (ast.operator == BOp.B_DIV) {
|
||||
// The dividend (left) will be replaced with the quotient
|
||||
// So free the divisor register (right) and return the quotient (left)
|
||||
division(left, right);
|
||||
cg.rm.releaseRegister(right);
|
||||
return left;
|
||||
} else {
|
||||
// All operators take the form OP A, B --> B = B OP A --> B is left, A is right
|
||||
// Therefore the result is stored in B (left) and A (right) can be freed
|
||||
cg.emit.emit(op, right, left);
|
||||
cg.rm.releaseRegister(right);
|
||||
return left;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Division with divide by zero check incorporated
|
||||
*/
|
||||
private void division(Register dividend, Register divisor) {
|
||||
division(dividend, divisor, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special implementation for divisions, as it requires specific registers to be used
|
||||
* @param dividend Maximum 32bit (even though the division is implemented as 64bit)
|
||||
* @param divisor Maximum 32bit
|
||||
* @param checkZero Whether to generate code that checks for division by zero or not
|
||||
* @return The quotient will be in place of the dividend and the remainder in the divisor
|
||||
*/
|
||||
protected void division(Register dividend, Register divisor, boolean checkZero) {
|
||||
if (checkZero) {
|
||||
String beginDivision = cg.emit.uniqueLabel();
|
||||
cg.emit.emit("cmp", 0, divisor);
|
||||
cg.emit.emit("jne", beginDivision);
|
||||
// Exit with error code in case of div/0
|
||||
Interrupts.exit(cg, Interrupts.EXIT_DIV_0);
|
||||
cg.emit.emitLabel(beginDivision);
|
||||
}
|
||||
|
||||
// D = d * q + r (dividend, divisor, quotient, remainder)
|
||||
// EDX:EAX = d * EAX + EDX (before and after the div operation)
|
||||
|
||||
// Take care of register use: move EAX to memory if necessary
|
||||
// The stack is not needed because the division operator is not recursive
|
||||
// After the division is performed, move the data back to dividend and divisor
|
||||
Register d, D = Register.EAX;
|
||||
if (divisor.equals(D)) {
|
||||
d = cg.rm.getRegister();
|
||||
cg.emit.emitMove(divisor, d);
|
||||
cg.emit.emitMove(dividend, Register.EAX);
|
||||
} else {
|
||||
if (!dividend.equals(D) && cg.rm.isInUse(D))
|
||||
cg.emit.emitMove(D, "(" + Interrupts.Mem.EAX.pos + ")");
|
||||
cg.emit.emitMove(dividend, D);
|
||||
if (divisor.equals(Register.EDX)) {
|
||||
d = cg.rm.getRegister();
|
||||
cg.emit.emitMove(divisor, d);
|
||||
}
|
||||
}
|
||||
|
||||
// Division
|
||||
cg.emit.emit("cdq");
|
||||
cg.emit.emit("idiv", divisor);
|
||||
cg.emit.emitMove(Register.EAX, dividend);
|
||||
cg.emit.emitMove(Register.EDX, divisor);
|
||||
|
||||
Register q = Register.EAX, r = Register.EDX;
|
||||
if (divisor.equals(D)) {
|
||||
cg.emit.emitMove(q, dividend);
|
||||
cg.emit.emitMove(r, divisor);
|
||||
} else {
|
||||
if (!dividend.equals(D) && cg.rm.isInUse(D))
|
||||
cg.emit.emitMove(Interrupts.Mem.EAX.pos, D);
|
||||
cg.emit.emitMove(dividend, D);
|
||||
if (!divisor.equals(r)) {
|
||||
cg.emit.emitMove(r, divisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register booleanConst(BooleanConst ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register builtInRead(BuiltInRead ast, Void arg) {
|
||||
// Holds the first digit and then is used to accumulate the total value
|
||||
Register value = cg.rm.getRegister();
|
||||
// Used to mark for positive (0) or negative (1) value
|
||||
Register negative = cg.rm.getRegister();
|
||||
// Holds the next digit read, once the first has been read
|
||||
Register nextDigit = cg.rm.getRegister();
|
||||
|
||||
// Labels for the read structure
|
||||
String removeWhitespace = cg.emit.uniqueLabel();
|
||||
String negativeFirstDigit = cg.emit.uniqueLabel();
|
||||
String inputError = cg.emit.uniqueLabel();
|
||||
String moreDigits = cg.emit.uniqueLabel();
|
||||
String endRead = cg.emit.uniqueLabel();
|
||||
String checkSign = cg.emit.uniqueLabel();
|
||||
|
||||
// By default, variable is positive (negative == 0)
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(0), negative);
|
||||
|
||||
// Skip whitespace ( \t\n\r) at the beginning
|
||||
cg.emit.emitLabel(removeWhitespace);
|
||||
Interrupts.readChar(cg, value);
|
||||
cg.emit.emit("cmp", '\n', value);
|
||||
cg.emit.emit("je", removeWhitespace);
|
||||
cg.emit.emit("cmp", ' ', value);
|
||||
cg.emit.emit("je", removeWhitespace);
|
||||
cg.emit.emit("cmp", '\t', value);
|
||||
cg.emit.emit("je", removeWhitespace);
|
||||
cg.emit.emit("cmp", '\r', value);
|
||||
cg.emit.emit("je", removeWhitespace);
|
||||
|
||||
// Recognize first digit, if negative read another digit to place at value
|
||||
cg.emit.emit("cmp", '-', value);
|
||||
cg.emit.emit("je", negativeFirstDigit);
|
||||
charToIntOrJump(value, inputError);
|
||||
cg.emit.emit("jmp", moreDigits);
|
||||
|
||||
// Negative first digit
|
||||
cg.emit.emitLabel(negativeFirstDigit);
|
||||
Interrupts.readChar(cg, value);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(1), negative);
|
||||
charToIntOrJump(value, inputError);
|
||||
|
||||
// All other digits:
|
||||
// while ( (nextDigit = read()).isDigit())
|
||||
// value = value * 10 + nextDigit
|
||||
cg.emit.emitLabel(moreDigits);
|
||||
Interrupts.readChar(cg, nextDigit);
|
||||
charToIntOrJump(nextDigit, checkSign);
|
||||
cg.emit.emit("imul", 10, value);
|
||||
cg.emit.emit("add", nextDigit, value);
|
||||
cg.emit.emit("jmp", moreDigits);
|
||||
|
||||
// No number can be read (non-digit first or following the '-')
|
||||
cg.emit.emitLabel(inputError);
|
||||
Interrupts.exit(cg, Interrupts.EXIT_INPUT_MISMATCH);
|
||||
|
||||
// Sign check and end
|
||||
cg.emit.emitLabel(checkSign);
|
||||
cg.emit.emit("cmp", 0, negative);
|
||||
cg.emit.emit("je", endRead);
|
||||
cg.emit.emit("negl", value);
|
||||
cg.emit.emitLabel(endRead);
|
||||
|
||||
// Register bookkeeping and return read value
|
||||
cg.rm.releaseRegister(negative);
|
||||
cg.rm.releaseRegister(nextDigit);
|
||||
return value;
|
||||
}
|
||||
|
||||
private void charToIntOrJump(Register reg, String jumpLabel) {
|
||||
cg.emit.emit("cmp", '0', reg);
|
||||
cg.emit.emit("jl", jumpLabel);
|
||||
cg.emit.emit("cmp", '9', reg);
|
||||
cg.emit.emit("jg", jumpLabel);
|
||||
cg.emit.emit("sub", '0', reg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register cast(Cast ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register index(Index ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register intConst(IntConst ast, Void arg) {
|
||||
Register r = cg.rm.getRegister();
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(ast.value), r);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register field(Field ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register newArray(NewArray ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register newObject(NewObject ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register nullConst(NullConst ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register thisRef(ThisRef ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register unaryOp(UnaryOp ast, Void arg) {
|
||||
Register aux = cg.eg.visit(ast.arg(), arg);
|
||||
switch (ast.operator) {
|
||||
case U_PLUS:
|
||||
return aux;
|
||||
case U_MINUS:
|
||||
cg.emit.emit("negl", aux);
|
||||
return aux;
|
||||
case U_BOOL_NOT:
|
||||
cg.emit.emit("notl", aux);
|
||||
return aux;
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register var(Var ast, Void arg) {
|
||||
Register r = cg.rm.getRegister();
|
||||
cg.emit.emitMove(ast.getLabel(), r);
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
|
||||
import static cd.backend.codegen.Interrupts.Mem.BUFFER;
|
||||
|
||||
public class Interrupts {
|
||||
|
||||
protected static final int EXIT_OK = 0, EXIT_DIV_0 = 7, EXIT_INPUT_MISMATCH = 8;
|
||||
protected static final int STDIN = 0, STDOUT = 1, STDERR = 2;
|
||||
protected static final int I_EXIT = 1, I_READ = 3, I_WRITE = 4;
|
||||
|
||||
protected enum Mem {
|
||||
BUFFER("io_buffer"), EAX("eax");
|
||||
|
||||
String pos;
|
||||
Mem(String pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write auxiliary data variables needed for I/O and division
|
||||
* @param cg AstCodeGenerator to print instructions
|
||||
*/
|
||||
protected static void reserveMemory(AstCodeGenerator cg) {
|
||||
for (Mem m : Mem.values()) {
|
||||
cg.emit.emitLabel(m.pos);
|
||||
cg.emit.emitConstantData("0");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single character passed as parameter
|
||||
* @param cg AstCodeGenerator to print instructions
|
||||
* @param i Character code in ASCII (or single casted character)
|
||||
*/
|
||||
protected static void printChar(AstCodeGenerator cg, int i) {
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(i), BUFFER.pos);
|
||||
printChar(cg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single character from the BUFFER memory pos to stdout
|
||||
* @param cg AstCodeGenerator to print instructions
|
||||
*/
|
||||
protected static void printChar(AstCodeGenerator cg) {
|
||||
Register[] needed = {Register.EAX, Register.EBX, Register.ECX, Register.EDX};
|
||||
for (int i = 0; i < needed.length; i++)
|
||||
if (cg.rm.isInUse(needed[i]))
|
||||
cg.emit.emit("push", needed[i]);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(I_WRITE), Register.EAX);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(STDOUT), Register.EBX);
|
||||
cg.emit.emitMove("$" + BUFFER.pos, Register.ECX);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(1), Register.EDX);
|
||||
cg.emit.emit("int", 0x80);
|
||||
for (int i = needed.length - 1; i >= 0; i--)
|
||||
if (cg.rm.isInUse(needed[i]))
|
||||
cg.emit.emit("pop", needed[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single character from stdin and places it in the
|
||||
* memory pos BUFFER
|
||||
*/
|
||||
protected static void readChar(AstCodeGenerator cg, Register destination) {
|
||||
Register[] needed = {Register.EAX, Register.EBX, Register.ECX, Register.EDX};
|
||||
for (int i = 0; i < needed.length; i++)
|
||||
if (cg.rm.isInUse(needed[i]))
|
||||
cg.emit.emit("push", needed[i]);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(I_READ), Register.EAX);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(STDIN), Register.EBX);
|
||||
cg.emit.emitMove("$" + BUFFER.pos, Register.ECX);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(1), Register.EDX);
|
||||
cg.emit.emit("int", 0x80);
|
||||
for (int i = needed.length - 1; i >= 0; i--)
|
||||
if (cg.rm.isInUse(needed[i]))
|
||||
cg.emit.emit("pop", needed[i]);
|
||||
cg.emit.emitMove(BUFFER.pos, destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, int exitCode) {
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(I_EXIT), Register.EAX);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(exitCode), Register.EBX);
|
||||
cg.emit.emit("int", 0x80);
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* marks a currently used register as free
|
||||
*/
|
||||
public void releaseRegister(Register reg) {
|
||||
assert !registers.contains(reg);
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
package cd.backend.codegen;
|
||||
|
||||
import cd.backend.codegen.RegisterManager.Register;
|
||||
import cd.ir.Ast;
|
||||
import cd.ir.Ast.*;
|
||||
import cd.ir.AstVisitor;
|
||||
import cd.util.debug.AstOneLine;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Generates code to process statements and declarations.
|
||||
*/
|
||||
class StmtGenerator extends AstVisitor<Register, Void> {
|
||||
protected final AstCodeGenerator cg;
|
||||
|
||||
StmtGenerator(AstCodeGenerator astCodeGenerator) {
|
||||
cg = astCodeGenerator;
|
||||
}
|
||||
|
||||
public void gen(Ast ast) {
|
||||
visit(ast, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register visit(Ast ast, Void arg) {
|
||||
try {
|
||||
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
|
||||
return super.visit(ast, arg);
|
||||
} finally {
|
||||
cg.emit.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a variable in the data segment. This method was implemented because it was necessary
|
||||
* to create the data segment
|
||||
* @param ast Variable declaration node
|
||||
* @param arg Extra arguments
|
||||
* @return null
|
||||
*/
|
||||
@Override
|
||||
public Register varDecl(Ast.VarDecl ast, Void arg) {
|
||||
cg.emit.emitLabel(ast.getLabel());
|
||||
cg.emit.emitConstantData("0");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register methodCall(MethodCall ast, Void dummy) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register methodDecl(MethodDecl ast, Void arg) {
|
||||
// Prologue: in future version a more complex prologue that
|
||||
// takes care of parameters and return values is necessary
|
||||
// Method code
|
||||
cg.rm.initRegisters();
|
||||
cg.emit.emitRaw(".section .data");
|
||||
Interrupts.reserveMemory(cg);
|
||||
// Variables
|
||||
cg.sg.visit(ast.decls(), arg);
|
||||
cg.emit.emitRaw(".section .text");
|
||||
cg.emit.emitRaw(".globl " + ast.name);
|
||||
cg.emit.emitLabel(ast.name);
|
||||
Register result = cg.sg.visit(ast.body(), arg);
|
||||
// Epilogue: not needed in the simple HW1
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register ifElse(IfElse ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register whileLoop(WhileLoop ast, Void arg) {
|
||||
{
|
||||
throw new RuntimeException("Not required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register assign(Assign ast, Void arg) {
|
||||
// Because we only handle very simple programs in HW1,
|
||||
// you can just emit the prologue here!
|
||||
Register value = cg.eg.visit(ast.right(), arg);
|
||||
Ast.Var variable = (Var) ast.left();
|
||||
cg.emit.emitMove(value, variable.getLabel());
|
||||
cg.rm.releaseRegister(value);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register builtInWrite(BuiltInWrite ast, Void arg) {
|
||||
String printNonZero = cg.emit.uniqueLabel();
|
||||
String end = cg.emit.uniqueLabel();
|
||||
String printPositive = cg.emit.uniqueLabel();
|
||||
String stackLoop = cg.emit.uniqueLabel();
|
||||
String printLoop = cg.emit.uniqueLabel();
|
||||
|
||||
Register value = cg.eg.visit(ast.arg(), arg);
|
||||
// Begin: decide if print 0 or print number
|
||||
cg.emit.emit("cmp", 0, value);
|
||||
cg.emit.emit("jne", printNonZero); // value != 0
|
||||
|
||||
// Print 0
|
||||
Interrupts.printChar(cg, '0'); // print 0
|
||||
cg.emit.emit("jmp", end);
|
||||
|
||||
// Not 0: positive or negative?
|
||||
cg.emit.emitLabel(printNonZero);
|
||||
cg.emit.emit("jg", printPositive); // value > 0
|
||||
|
||||
// Number is negative, print '-' then number (with sign changed)
|
||||
Interrupts.printChar(cg, '-');
|
||||
cg.emit.emit("negl", value);
|
||||
|
||||
// Print number: extract and print all digits of number
|
||||
cg.emit.emitLabel(printPositive);
|
||||
|
||||
// In order to avoid using EAX and EDX, we are going to guarantee
|
||||
// that there will be at least 2 other registers available
|
||||
List<Register> auxRegs = Arrays.asList(Register.EDI, Register.ESI);
|
||||
List<Register> divRegs = Arrays.asList(Register.EAX, Register.EDX);
|
||||
Map<Register, Boolean> isPushed = new HashMap<>();
|
||||
boolean usingAuxRegs = cg.rm.availableRegisters() < 4;
|
||||
if (usingAuxRegs)
|
||||
for (int i = 0; i < auxRegs.size(); i++)
|
||||
isPushed.put(auxRegs.get(i), cg.rm.isInUse(auxRegs.get(i)));
|
||||
|
||||
// Free EAX and EDX for div operand use
|
||||
isPushed.put(Register.EAX, cg.rm.isInUse(Register.EAX) && !value.equals(Register.EAX));
|
||||
isPushed.put(Register.EDX, cg.rm.isInUse(Register.EDX));
|
||||
|
||||
// Push all elements
|
||||
for (Register r : isPushed.keySet())
|
||||
if (isPushed.get(r))
|
||||
cg.emit.emit("push", r);
|
||||
|
||||
// Force counter and divisor to not be EDX/EAX, as they need to coexist
|
||||
Register counter = cg.rm.getRegister();
|
||||
Register divisor = cg.rm.getRegister();
|
||||
while (divRegs.contains(counter)) {
|
||||
Register aux = cg.rm.getRegister();
|
||||
cg.rm.releaseRegister(counter);
|
||||
counter = aux;
|
||||
}
|
||||
while (divRegs.contains(divisor)) {
|
||||
Register aux = cg.rm.getRegister();
|
||||
cg.rm.releaseRegister(divisor);
|
||||
divisor = aux;
|
||||
}
|
||||
|
||||
// Divide by 10 to extract the remainders and push them to the stack
|
||||
// Registers used: EAX is the remaining digits (initially the value)
|
||||
// in div it is used for the lower half of the dividend and for quotient
|
||||
// divisor
|
||||
// EDX is used as the high half of the dividend and as remainder
|
||||
cg.emit.emitMove(value, Register.EAX);
|
||||
cg.rm.releaseRegister(value);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(0), counter);
|
||||
|
||||
cg.emit.emitLabel(stackLoop);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(0), Register.EDX);
|
||||
cg.emit.emitMove(AssemblyEmitter.constant(10), divisor);
|
||||
cg.emit.emit("div", divisor);
|
||||
cg.emit.emit("add", '0', Register.EDX);
|
||||
cg.emit.emit("push", Register.EDX);
|
||||
cg.emit.emit("inc", counter);
|
||||
cg.emit.emit("cmp", 0, Register.EAX);
|
||||
cg.emit.emit("je", printLoop);
|
||||
cg.emit.emit("jmp", stackLoop);
|
||||
// Release divisor register
|
||||
cg.rm.releaseRegister(divisor);
|
||||
|
||||
// Print digits from the stack
|
||||
cg.emit.emitLabel(printLoop);
|
||||
cg.emit.emit("cmp", 0, counter);
|
||||
cg.emit.emit("jz", end);
|
||||
cg.emit.emit("dec", counter);
|
||||
cg.emit.emit("pop", Interrupts.Mem.BUFFER.pos);
|
||||
Interrupts.printChar(cg);
|
||||
cg.emit.emit("jmp", printLoop);
|
||||
cg.emit.emitLabel(end);
|
||||
|
||||
cg.rm.releaseRegister(counter);
|
||||
|
||||
// Restore original registers
|
||||
for (Register r : isPushed.keySet())
|
||||
if (isPushed.get(r))
|
||||
cg.emit.emit("pop", r);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Register builtInWriteln(BuiltInWriteln ast, Void arg) {
|
||||
Interrupts.printChar(cg, '\n');
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue