package compiler.asmcode;
import java.util.LinkedList;
import compiler.frames.Frame;
import compiler.frames.Label;
import compiler.frames.Temp;
import compiler.imcode.ImcBINOP;
import compiler.imcode.ImcCALL;
import compiler.imcode.ImcCJUMP;
import compiler.imcode.ImcCONST;
import compiler.imcode.ImcEXP;
import compiler.imcode.ImcExpr;
import compiler.imcode.ImcJUMP;
import compiler.imcode.ImcLABEL;
import compiler.imcode.ImcMEM;
import compiler.imcode.ImcMOVE;
import compiler.imcode.ImcNAME;
import compiler.imcode.ImcSEQ;
import compiler.imcode.ImcStmt;
import compiler.imcode.ImcTEMP;
public class AsmCodeCreator
{
public LinkedList<AsmInstr> instructions = new LinkedList<AsmInstr>();
private Frame callFrame = null;
public AsmCodeCreator(Frame chunkFrame)
{
callFrame = chunkFrame;
}
public void munch(ImcStmt stmt)
{
/** ImcSEQ */
if (stmt instanceof ImcSEQ)
{
for (ImcStmt statement : ((ImcSEQ)stmt).stmts)
{
munch(statement);
}
}
/** ImcLABEL */
else if(stmt instanceof ImcLABEL)
{
ImcLABEL label = (ImcLABEL)stmt;
instructions.add(new AsmLABEL(label.label.name() + ":", label.label));
}
/** ImcMOVE */
else if(stmt instanceof ImcMOVE)
{
munch((ImcMOVE)stmt);
}
/** ImcEXP */
else if (stmt instanceof ImcEXP)
{
munch(((ImcEXP)stmt).expr);
}
/** ImcJUMP */
else if (stmt instanceof ImcJUMP)
{
munch((ImcJUMP)stmt);
}
else if (stmt instanceof ImcCJUMP)
{
munch((ImcCJUMP)stmt);
}
else
{
System.err.println(stmt.getClass().toString());
}
};
/**
* Munch expression
*/
private Temp munch(ImcExpr expr)
{
/** TEMP */
if (expr instanceof ImcTEMP)
{
return ((ImcTEMP)expr).temp;
}
/** CONST */
if (expr instanceof ImcCONST)
{
return munch((ImcCONST)expr);
}
/** MEM */
else if (expr instanceof ImcMEM)
{
return munch((ImcMEM)expr);
}
/** BINOP */
else if (expr instanceof ImcBINOP)
{
return munch((ImcBINOP)expr);
}
/** CALL */
else if (expr instanceof ImcCALL)
{
return munch((ImcCALL)expr);
}
/** NAME */
else if (expr instanceof ImcNAME)
{
// Name as an expression, load label
Temp val = new Temp();
LinkedList<Temp> defs = new LinkedList<Temp>();
defs.add(val);
LinkedList<Label> lbls = new LinkedList<Label>();
lbls.add(((ImcNAME)expr).label);
instructions.add(new AsmOPER("ldr `d0, =" + lbls.get(0).name(), defs, new LinkedList<Temp>(), lbls));
return val;
}
else
{
System.err.println(" -- " + expr.getClass().toString());
}
return new Temp();
};
/**
* Munch MOVE
*/
private void munch(ImcMOVE move)
{
// MOV
if (move.src instanceof ImcTEMP && move.dst instanceof ImcTEMP)
{
// Create move command
instructions.add(new AsmMOVE("mov `d0, [`s0]", ((ImcTEMP)move.src).temp, ((ImcTEMP)move.dst).temp));
}
// LDR or MOV const
else if (move.dst instanceof ImcTEMP && move.src instanceof ImcCONST)
{
// Create MOV command if the operand is less than 4096 since ARMv5 can't have bigger immediate operands
int constValue = ((ImcCONST)move.src).value;
// Defs
LinkedList<Temp> def = new LinkedList<Temp>();
def.add(((ImcTEMP)move.dst).temp);
if (constValue < 4096)
{
instructions.add(new AsmOPER("mov `d0, #" + constValue, def, new LinkedList<Temp>()));
}
else
{
instructions.add(new AsmOPER("ldr `d0, =" + constValue, def, new LinkedList<Temp>()));
}
}
else if (move.dst instanceof ImcTEMP && move.src instanceof ImcExpr)
{
Temp val = munch(move.src);
instructions.add(new AsmMOVE("mov `d0, [`s0]", ((ImcTEMP)move.dst).temp, val));
}
// STR
else if (move.dst instanceof ImcMEM)
{
Temp value = munch((ImcExpr)move.src);
ImcMEM mem = (ImcMEM)move.dst;
// Check if it's a label or a register with offset
// NAME
if (mem.expr instanceof ImcNAME)
{
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(value);
instructions.add(new AsmOPER("str `s0, =" + ((ImcNAME)mem.expr).label.name(), new LinkedList<Temp>(), src));
}
else if (mem.expr instanceof ImcBINOP && ((ImcBINOP)mem.expr).rimc instanceof ImcCONST)
{
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(value);
src.add(munch(((ImcBINOP)mem.expr).limc));
int offset = ((ImcCONST)((ImcBINOP)mem.expr).rimc).value;
instructions.add(new AsmOPER("str `s0, [`s1, #" + offset + "]", new LinkedList<Temp>(), src));
}
// Address that evaluates
else if (((ImcBINOP)mem.expr).rimc instanceof ImcCONST)
{
Temp addr = munch((ImcExpr)mem.expr);
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(addr);
src.add(munch(((ImcBINOP)mem.expr).limc));
int offset = ((ImcCONST)((ImcBINOP)mem.expr).rimc).value;
instructions.add(new AsmOPER("str `s0, [`s1, #" + offset + "]", new LinkedList<Temp>(), src));
}
}
else
{
System.err.println("!! " + move.src.getClass().toString());
}
}
/**
* Munch JUMP
*/
private void munch(ImcJUMP jump)
{
LinkedList<Label> lbls = new LinkedList<Label>();
lbls.add(jump.label);
instructions.add(new AsmOPER("b " + jump.label.name(), new LinkedList<Temp>(), new LinkedList<Temp>(), lbls));
}
/**
* Munch CJUMP
*/
private void munch(ImcCJUMP jump)
{
Temp conditionResult = munch(jump.cond);
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(conditionResult);
// Test if condition is true
instructions.add(new AsmOPER("tst `s0, #1", new LinkedList<Temp>(), src));
// TRUE
LinkedList<Label> lbls = new LinkedList<Label>();
lbls.add(jump.trueLabel);
instructions.add(new AsmOPER("bne " + jump.trueLabel.name(), new LinkedList<Temp>(), src, lbls));
// FALSE
lbls = new LinkedList<Label>();
lbls.add(jump.falseLabel);
instructions.add(new AsmOPER("beq " + jump.falseLabel.name(), new LinkedList<Temp>(), src, lbls));
}
/**
* Munch CALL
*/
private Temp munch(ImcCALL call)
{
// Save static link on current stack pointer location
// TODO: check validity
LinkedList<Temp> uses = new LinkedList<Temp>();
uses.add(callFrame.FP);
addComment("Store static link");
instructions.add(new AsmOPER("str `s0, [sp]", new LinkedList<Temp>(), uses));
addComment("Store arguments");
int offset = 4;
for (ImcExpr arg : call.args)
{
Temp val = munch(arg);
// Store on stack (r14 is stack pointer) and increment it (!)
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(val);
instructions.add(new AsmOPER("str `s0, [sp, #" + offset + "]", new LinkedList<Temp>(), src));
offset += 4;
}
// JUMP
LinkedList<Label> lbls = new LinkedList<Label>();
lbls.add(call.label);
// Store registers
instructions.add(new AsmOPER("b " + call.label.name(), new LinkedList<Temp>(), new LinkedList<Temp>(), lbls));
return new Temp();
}
/**
* Munch BINOP
*/
private Temp munch(ImcBINOP binop)
{
switch(binop.op)
{
case ImcBINOP.ADD:
case ImcBINOP.SUB:
case ImcBINOP.MUL:
case ImcBINOP.DIV:
return munchArithmetic(binop);
case ImcBINOP.EQU:
case ImcBINOP.NEQ:
case ImcBINOP.LEQ:
case ImcBINOP.LTH:
case ImcBINOP.GEQ:
case ImcBINOP.GTH:
case ImcBINOP.AND:
case ImcBINOP.OR:
return munchLogical(binop);
default:
System.err.println("Unimplemented BINOP operation " + binop.op);
return new Temp();
}
}
/**
* Munch CONST
*/
private Temp munch(ImcCONST constant)
{
// Load constant to a register and return it
Temp constreg = new Temp();
int val = constant.value;
LinkedList<Temp> dst = new LinkedList<Temp>();
dst.add(constreg);
if (val < 4096)
{
instructions.add(new AsmOPER("mov `d0, #" + val, dst, new LinkedList<Temp>()));
}
else
{
instructions.add(new AsmOPER("ldr `d0, =" + val, dst, new LinkedList<Temp>()));
}
return constreg;
}
/**
* Munch MEM
*/
private Temp munch(ImcMEM mem)
{
// Create LDR command depending on the parameters
Temp dest = new Temp();
LinkedList<Temp> defs = new LinkedList<Temp>();
defs.add(dest);
if (mem.expr instanceof ImcNAME)
{
Label label = ((ImcNAME)mem.expr).label;
LinkedList<Label> lbls = new LinkedList<Label>();
lbls.add(label);
instructions.add(new AsmOPER("ldr `d0, =" + label.name(), defs, new LinkedList<Temp>(), lbls));
}
else if (mem.expr instanceof ImcBINOP && ((ImcBINOP)mem.expr).rimc instanceof ImcCONST)
{
Temp left = munch(((ImcBINOP)mem.expr).limc);
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(left);
int offset = ((ImcCONST)((ImcBINOP)mem.expr).rimc).value;
instructions.add(new AsmOPER("ldr `d0, [`s0, #" + offset + "]", defs, src));
}
else
{
Temp addr = munch(mem.expr);
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(addr);
instructions.add(new AsmOPER("ldr, `d0, [`s0]", defs, src));
}
return dest;
}
/**
* Handles arithmetic BINOP operations
*/
private Temp munchArithmetic(ImcBINOP binop)
{
// Check if perhaps we have two constant values, convert them to single LDR/MOV order instead
if (binop.limc instanceof ImcCONST && binop.rimc instanceof ImcCONST)
{
// Calculate operation result
int val1 = ((ImcCONST)binop.limc).value;
int val2 = ((ImcCONST)binop.rimc).value;
int result = 0;
switch(binop.op)
{
case ImcBINOP.ADD:
result = val1 + val2;
break;
case ImcBINOP.SUB:
result = val1 - val2;
break;
case ImcBINOP.MUL:
result = val1 * val2;
break;
case ImcBINOP.DIV:
result = val1 / val2;
break;
}
LinkedList<Temp> def = new LinkedList<Temp>();
def.add(new Temp());
if (result < 4096)
{
instructions.add(new AsmOPER("mov `d0, #" + result, def, new LinkedList<Temp>()));
}
else
{
instructions.add(new AsmOPER("ldr `d0, =" + result, def, new LinkedList<Temp>()));
}
return def.getFirst();
}
// Check for immediate values
else
{
String command = "";
// Evaulate left and right sides
Temp val1 = munch((ImcExpr)binop.limc);
Temp val2 = munch((ImcExpr)binop.rimc);
switch(binop.op)
{
case ImcBINOP.ADD:
command = "add";
break;
case ImcBINOP.SUB:
command = "sub";
break;
case ImcBINOP.MUL:
command = "mul";
break;
case ImcBINOP.DIV:
return inlineDivAlgorithm(val1, val2);
}
Temp result = new Temp();
LinkedList<Temp> defs = new LinkedList<Temp>();
defs.add(result);
LinkedList<Temp> srcs = new LinkedList<Temp>();
srcs.add(val1);
srcs.add(val2);
instructions.add(new AsmOPER(command + " `d0, `s0, `s1", defs, srcs));
return result;
}
}
// Inserts an inlined division algorithm
private Temp inlineDivAlgorithm(Temp val1, Temp val2)
{
LinkedList<Temp> uses = new LinkedList<Temp>();
LinkedList<Temp> defs = new LinkedList<Temp>();
Temp result = new Temp();
defs.add(result);
Temp tmp = new Temp();
// Clear result register for accumulation
// MOV R0, #0
instructions.add(new AsmOPER("mov `d0, #0", defs, new LinkedList<Temp>()));
defs = new LinkedList<Temp>();
defs.add(tmp);
// Put bit 1 in temp register
// MOV R3, #1
instructions.add(new AsmOPER("mov `d0, #1", defs, new LinkedList<Temp>()));
// Start label
Label start = Label.newLabel();
instructions.add(new AsmLABEL(start.name() + ":", start));
// CMP R2, R1
uses.add(val1);
uses.add(val2);
instructions.add(new AsmOPER("cmp `s0, `s1", new LinkedList<Temp>(), uses));
// MOVLS R2, R2, LSL #1
defs = new LinkedList<Temp>();
defs.add(val2);
uses = new LinkedList<Temp>();
uses.add(val2);
instructions.add(new AsmOPER("movls `d0, `s0, lsl #1", defs, uses));
// MOVLS R3, R3, LSL #1
defs = new LinkedList<Temp>();
defs.add(tmp);
uses = new LinkedList<Temp>();
uses.add(tmp);
instructions.add(new AsmOPER("movls `d0, `s0, lsl #1", defs, uses));
// BLS start
LinkedList<Label> lbls = new LinkedList<Label>();
lbls.add(start);
instructions.add(new AsmOPER("bls " + start.name(), new LinkedList<Temp>(), new LinkedList<Temp>(), lbls));
// Next label
Label next = Label.newLabel();
instructions.add(new AsmLABEL(next.name() + ":", next));
// CMP R1, R2
uses = new LinkedList<Temp>();
uses.add(val1);
uses.add(val2);
instructions.add(new AsmOPER("cmp `s0, `s1", new LinkedList<Temp>(), uses));
// SUBCS R1, R1, R2
defs = new LinkedList<Temp>();
defs.add(val1);
instructions.add(new AsmOPER("subcs `d0, `s0, `s1", defs, uses));
// ADDCS R0, R0, R3
defs = new LinkedList<Temp>();
defs.add(result);
uses = new LinkedList<Temp>();
uses.add(result);
uses.add(tmp);
instructions.add(new AsmOPER("addcs `d0, `s0, `s1", defs, uses));
// MOVS R3, R3, LSR #1
defs = new LinkedList<Temp>();
defs.add(tmp);
uses = new LinkedList<Temp>();
uses.add(tmp);
instructions.add(new AsmOPER("movs `d0, `s0, LSR #1", defs, uses));
// MOVCC R2, R2, LSR #1
defs = new LinkedList<Temp>();
defs.add(val2);
uses = new LinkedList<Temp>();
uses.add(val2);
instructions.add(new AsmOPER("movcc `d0, `s0, LSR #1", defs, uses));
// BCC next
lbls = new LinkedList<Label>();
lbls.add(next);
instructions.add(new AsmOPER("bcc " + next.name(), new LinkedList<Temp>(), new LinkedList<Temp>(), lbls));
// Done
return result;
}
private Temp munchLogical(ImcBINOP binop)
{
Temp result = new Temp();
LinkedList<Temp> dst = new LinkedList<Temp>();
dst.add(result);
Temp left = munch(binop.limc);
Temp right = munch(binop.rimc);
LinkedList<Temp> src = new LinkedList<Temp>();
src.add(left);
src.add(right);
if (binop.op == ImcBINOP.AND)
{
instructions.add(new AsmOPER("and `d0, `s0, `s1", dst, src));
return result;
}
else if (binop.op == ImcBINOP.OR)
{
instructions.add(new AsmOPER("orr `d0, `s0, `s1", dst, src));
return result;
}
// Compare operators
instructions.add(new AsmOPER("cmp `s0, `s1", new LinkedList<Temp>(), src));
// Determine correct pair of conditional execution postfixes for the condition
String truePf = "";
String falsePf = "";
switch(binop.op)
{
case ImcBINOP.EQU:
truePf = "eq";
falsePf = "ne";
break;
case ImcBINOP.NEQ:
truePf = "ne";
falsePf = "eq";
break;
case ImcBINOP.GTH:
truePf = "gt";
falsePf = "le";
break;
case ImcBINOP.GEQ:
truePf = "ge";
falsePf = "lt";
break;
case ImcBINOP.LEQ:
truePf = "le";
falsePf = "gt";
break;
case ImcBINOP.LTH:
truePf = "lt";
falsePf = "ge";
break;
default:
System.err.println("Unimplemented logical BINOP operator " + binop.op);
}
// Add value setter instructions with correct postfixes
instructions.add(new AsmOPER("mov" + truePf + " `d0, #0", dst, new LinkedList<Temp>()));
instructions.add(new AsmOPER("mov" + falsePf + " `d0, #1", dst, new LinkedList<Temp>()));
return result;
}
private void addComment(String comment)
{
instructions.add(new AsmOPER("; " + comment, new LinkedList<Temp>(), new LinkedList<Temp>()));
}
}