308 lines
8.4 KiB
Java
308 lines
8.4 KiB
Java
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;
|
|
}
|
|
|
|
}
|