Homework 1
This commit is contained in:
parent
49bca7f856
commit
12f678a924
43 changed files with 3703 additions and 0 deletions
307
src/cd/backend/codegen/ExprGenerator.java
Normal file
307
src/cd/backend/codegen/ExprGenerator.java
Normal file
|
@ -0,0 +1,307 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue