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