207 lines
5.9 KiB
Java
207 lines
5.9 KiB
Java
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;
|
|
}
|
|
}
|