Homework 1

This commit is contained in:
Carlos Galindo 2020-01-15 22:18:07 +01:00
commit 12f678a924
Signed by: kauron
GPG key ID: 83E68706DEE119A3
43 changed files with 3703 additions and 0 deletions

View file

@ -0,0 +1,164 @@
package cd.backend.codegen;
import java.io.IOException;
import java.io.Writer;
import cd.Config;
import cd.backend.codegen.RegisterManager.Register;
public class AssemblyEmitter {
public Writer out;
public StringBuilder indent = new StringBuilder();
public int counter = 0;
public AssemblyEmitter(Writer out) {
this.out = out;
}
/** Creates an constant operand. */
static String constant(int i) {
return "$" + i;
}
/** Creates an constant operand with the address of a label. */
static String labelAddress(String lbl) {
return "$" + lbl;
}
/** Creates an operand relative to another operand. */
static String registerOffset(int offset, Register reg) {
return String.format("%d(%s)", offset, reg);
}
/** Creates an operand addressing an item in an array */
static String arrayAddress(Register arrReg, Register idxReg) {
final int offset = Config.SIZEOF_PTR * 2; // one word each in front for
// vptr and length
final int mul = Config.SIZEOF_PTR; // assume all arrays of 4-byte elem
return String.format("%d(%s,%s,%d)", offset, arrReg, idxReg, mul);
}
void increaseIndent(String comment) {
indent.append(" ");
if (comment != null)
emitComment(comment);
}
void decreaseIndent() {
indent.setLength(indent.length() - 2);
}
void emitCommentSection(String name) {
int indentLen = indent.length();
int breakLen = 68 - indentLen - name.length();
StringBuffer sb = new StringBuffer();
sb.append(Config.COMMENT_SEP).append(" ");
for (int i = 0; i < indentLen; i++)
sb.append("_");
sb.append(name);
for (int i = 0; i < breakLen; i++)
sb.append("_");
try {
out.write(sb.toString());
out.write("\n");
} catch (IOException e) {
}
}
void emitComment(String comment) {
emitRaw(Config.COMMENT_SEP + " " + comment);
}
void emit(String op, Register src, String dest) {
emit(op, src.repr, dest);
}
void emit(String op, String src, Register dest) {
emit(op, src, dest.repr);
}
void emit(String op, Register src, Register dest) {
emit(op, src.repr, dest.repr);
}
void emit(String op, String src, String dest) {
emitRaw(String.format("%s %s, %s", op, src, dest));
}
void emit(String op, int src, Register dest) {
emit(op, constant(src), dest);
}
void emit(String op, String dest) {
emitRaw(op + " " + dest);
}
void emit(String op) {
emitRaw(op);
}
void emit(String op, Register reg) {
emit(op, reg.repr);
}
void emit(String op, int dest) {
emit(op, constant(dest));
}
void emitMove(Register src, String dest) {
emitMove(src.repr, dest);
}
void emitMove(Register src, Register dest) {
emitMove(src.repr, dest.repr);
}
void emitMove(String src, Register dest) {
emitMove(src, dest.repr);
}
void emitMove(String src, String dest) {
if (!src.equals(dest))
emit("movl", src, dest);
}
void emitLoad(int srcOffset, Register src, Register dest) {
emitMove(registerOffset(srcOffset, src), dest.repr);
}
void emitStore(Register src, int destOffset, Register dest) {
emitStore(src.repr, destOffset, dest);
}
void emitStore(String src, int destOffset, Register dest) {
emitMove(src, registerOffset(destOffset, dest));
}
void emitConstantData(String data) {
emitRaw(String.format("%s %s", Config.DOT_INT, data));
}
String uniqueLabel() {
String labelName = "label" + counter++;
return labelName;
}
void emitLabel(String label) {
try {
out.write(label + ":" + "\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
void emitRaw(String op) {
try {
out.write(indent.toString());
out.write(op);
out.write("\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,14 @@
package cd.backend.codegen;
public class AssemblyFailedException extends RuntimeException {
private static final long serialVersionUID = -5658502514441032016L;
public final String assemblerOutput;
public AssemblyFailedException(
String assemblerOutput) {
super("Executing assembler failed.\n"
+ "Output:\n"
+ assemblerOutput);
this.assemblerOutput = assemblerOutput;
}
}

View file

@ -0,0 +1,58 @@
package cd.backend.codegen;
import cd.Main;
import cd.ir.Ast.ClassDecl;
import java.io.Writer;
import java.util.List;
public class AstCodeGenerator {
protected ExprGenerator eg;
protected StmtGenerator sg;
protected final Main main;
protected final AssemblyEmitter emit;
protected final RegisterManager rm = new RegisterManager();
AstCodeGenerator(Main main, Writer out) {
this.emit = new AssemblyEmitter(out);
this.main = main;
this.eg = new ExprGenerator(this);
this.sg = new StmtGenerator(this);
}
protected void debug(String format, Object... args) {
this.main.debug(format, args);
}
public static AstCodeGenerator createCodeGenerator(Main main, Writer out) {
return new AstCodeGenerator(main, out);
}
/**
* Main method. Causes us to emit x86 assembly corresponding to {@code ast}
* into {@code file}. Throws a {@link RuntimeException} should any I/O error
* occur.
*
* <p>
* The generated file will be divided into two sections:
* <ol>
* <li>Prologue: Generated by {@link #emitPrefix()}. This contains any
* introductory declarations and the like.
* <li>Body: Generated by {@link ExprGenerator}. This contains the main
* method definitions.
* </ol>
*/
public void go(List<? extends ClassDecl> astRoots) {
for (ClassDecl ast : astRoots) {
sg.gen(ast);
}
}
protected void initMethodData() {
}
}

View 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;
}
}

View file

@ -0,0 +1,97 @@
package cd.backend.codegen;
import cd.backend.codegen.RegisterManager.Register;
import static cd.backend.codegen.Interrupts.Mem.BUFFER;
public class Interrupts {
protected static final int EXIT_OK = 0, EXIT_DIV_0 = 7, EXIT_INPUT_MISMATCH = 8;
protected static final int STDIN = 0, STDOUT = 1, STDERR = 2;
protected static final int I_EXIT = 1, I_READ = 3, I_WRITE = 4;
protected enum Mem {
BUFFER("io_buffer"), EAX("eax");
String pos;
Mem(String pos) {
this.pos = pos;
}
@Override
public String toString() {
return pos;
}
}
/**
* Write auxiliary data variables needed for I/O and division
* @param cg AstCodeGenerator to print instructions
*/
protected static void reserveMemory(AstCodeGenerator cg) {
for (Mem m : Mem.values()) {
cg.emit.emitLabel(m.pos);
cg.emit.emitConstantData("0");
}
}
/**
* Writes a single character passed as parameter
* @param cg AstCodeGenerator to print instructions
* @param i Character code in ASCII (or single casted character)
*/
protected static void printChar(AstCodeGenerator cg, int i) {
cg.emit.emitMove(AssemblyEmitter.constant(i), BUFFER.pos);
printChar(cg);
}
/**
* Writes a single character from the BUFFER memory pos to stdout
* @param cg AstCodeGenerator to print instructions
*/
protected static void printChar(AstCodeGenerator cg) {
Register[] needed = {Register.EAX, Register.EBX, Register.ECX, Register.EDX};
for (int i = 0; i < needed.length; i++)
if (cg.rm.isInUse(needed[i]))
cg.emit.emit("push", needed[i]);
cg.emit.emitMove(AssemblyEmitter.constant(I_WRITE), Register.EAX);
cg.emit.emitMove(AssemblyEmitter.constant(STDOUT), Register.EBX);
cg.emit.emitMove("$" + BUFFER.pos, Register.ECX);
cg.emit.emitMove(AssemblyEmitter.constant(1), Register.EDX);
cg.emit.emit("int", 0x80);
for (int i = needed.length - 1; i >= 0; i--)
if (cg.rm.isInUse(needed[i]))
cg.emit.emit("pop", needed[i]);
}
/**
* Reads a single character from stdin and places it in the
* memory pos BUFFER
*/
protected static void readChar(AstCodeGenerator cg, Register destination) {
Register[] needed = {Register.EAX, Register.EBX, Register.ECX, Register.EDX};
for (int i = 0; i < needed.length; i++)
if (cg.rm.isInUse(needed[i]))
cg.emit.emit("push", needed[i]);
cg.emit.emitMove(AssemblyEmitter.constant(I_READ), Register.EAX);
cg.emit.emitMove(AssemblyEmitter.constant(STDIN), Register.EBX);
cg.emit.emitMove("$" + BUFFER.pos, Register.ECX);
cg.emit.emitMove(AssemblyEmitter.constant(1), Register.EDX);
cg.emit.emit("int", 0x80);
for (int i = needed.length - 1; i >= 0; i--)
if (cg.rm.isInUse(needed[i]))
cg.emit.emit("pop", needed[i]);
cg.emit.emitMove(BUFFER.pos, destination);
}
/**
* Generates a exit interrupt with the code provided
* @param cg AstCodeGenerator to print instructions
* @param exitCode Number to use as exit code (can use constants in this class)
*/
protected static void exit(AstCodeGenerator cg, int exitCode) {
cg.emit.emitMove(AssemblyEmitter.constant(I_EXIT), Register.EAX);
cg.emit.emitMove(AssemblyEmitter.constant(exitCode), Register.EBX);
cg.emit.emit("int", 0x80);
}
}

View file

@ -0,0 +1,123 @@
package cd.backend.codegen;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Simple class that manages the set of currently used
* and unused registers
*/
public class RegisterManager {
private List<Register> registers = new ArrayList<Register>();
// lists of register to save by the callee and the caller
public static final Register CALLEE_SAVE[] = new Register[]{Register.ESI,
Register.EDI, Register.EBX};
public static final Register CALLER_SAVE[] = new Register[]{Register.EAX,
Register.ECX, Register.EDX};
// list of general purpose registers
public static final Register GPR[] = new Register[]{Register.EAX, Register.EBX,
Register.ECX, Register.EDX, Register.ESI, Register.EDI};
// special purpose registers
public static final Register BASE_REG = Register.EBP;
public static final Register STACK_REG = Register.ESP;
public static final int SIZEOF_REG = 4;
public enum Register {
EAX("%eax", ByteRegister.EAX), EBX("%ebx", ByteRegister.EBX), ECX(
"%ecx", ByteRegister.ECX), EDX("%edx", ByteRegister.EDX), ESI(
"%esi", null), EDI("%edi", null), EBP("%ebp", null), ESP(
"%esp", null);
public final String repr;
private final ByteRegister lowByteVersion;
private Register(String repr, ByteRegister bv) {
this.repr = repr;
this.lowByteVersion = bv;
}
@Override
public String toString() {
return repr;
}
/**
* determines if this register has an 8bit version
*/
public boolean hasLowByteVersion() {
return lowByteVersion != null;
}
/**
* Given a register like {@code %eax} returns {@code %al}, but doesn't
* work for {@code %esi} and {@code %edi}!
*/
public ByteRegister lowByteVersion() {
assert hasLowByteVersion();
return lowByteVersion;
}
}
public enum ByteRegister {
EAX("%al"), EBX("%bl"), ECX("%cl"), EDX("%dl");
public final String repr;
private ByteRegister(String repr) {
this.repr = repr;
}
@Override
public String toString() {
return repr;
}
}
/**
* Reset all general purpose registers to free
*/
public void initRegisters() {
registers.clear();
registers.addAll(Arrays.asList(GPR));
}
/**
* returns a free register and marks it as used
*/
public Register getRegister() {
int last = registers.size() - 1;
if (last < 0)
throw new AssemblyFailedException(
"Program requires too many registers");
return registers.remove(last);
}
/**
* marks a currently used register as free
*/
public void releaseRegister(Register reg) {
assert !registers.contains(reg);
registers.add(reg);
}
/**
* Returns whether the register is currently non-free
*/
public boolean isInUse(Register reg) {
return !registers.contains(reg);
}
/**
* returns the number of free registers
*/
public int availableRegisters() {
return registers.size();
}
}

View file

@ -0,0 +1,206 @@
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;
}
}