compiler-design-eth/src/cd/backend/codegen/ExprGenerator.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;
}
}