/* zweic -- a compiler for zwei
*
* Stephane Micheloud & LAMP
*
* $Id$
*/
package zweic;
class Generator(analyzer: Analyzer) {
import RISC._;
import scala.collection.mutable.{Map, HashMap};
val code = new Code();
private def genTmp(gen: Int => Unit) = {
val tmpReg = code.getRegister();
gen(tmpReg);
code.freeRegister(tmpReg);
}
def emitLoadConstant(r: Int, value: Int) = {
val highBits = value >> 16;
val lowBits = value & 0xFFFF;
if (highBits != 0) {
code.emit(ORIU, r, ZERO, highBits, value.toString()+" high Bits");
code.emit(LSHI, r, r, 16, "<<");
}
if (lowBits != 0) {
code.emit(ORIU, r, ZERO, lowBits, value.toString());
}
if (highBits == 0 && lowBits == 0) {
code.emit(ORIU, r, ZERO, ZERO, "0");
}
}
def gen(tree: Tree): Unit = tree match {
case Program(classes, main) =>
val init = code.getLabel();
code.emit(BEQ,ZERO,init);
// go initialize the VMTs
var offset = code.pc();
for ( val c <- classes ) {
c.name.sym.offset = offset;
offset = offset + c.name.sym.asInstanceOf[ClassSymbol].allMethods.length*4;
//XXX: no +4 for empty top entry
for ( val m <- c.name.sym.asInstanceOf[ClassSymbol].allMethods ) {
code.emit(ADD, ZERO, ZERO, ZERO, c.name.name + "::" + m.name);
}
}
// generate class and function definitions
classes.foreach(gen);
//TODO: what was that??? its writing some stuff in fromt of the program...
//TODO: allocation what????
//genTmp { tmpReg =>
// emitLoadConstant(tmpReg, offset);
// code.emit(SYSCALL, tmpReg, tmpReg, SYS_GC_ALLOC);
// }
// generate main expression
val start = code.getLabel();
code.anchorLabel(start);
genTmp { tmpReg => genLoad(main, tmpReg) }
// exit
code.emit(RET, ZERO);
// the following code is placed here because the space that
// it occupies can be used after initialization.
code.anchorLabel(init);
// initialise stack pointer
code.emit(SYSCALL, SP, 0, SYS_GET_TOTAL_MEM_SIZE);
// initialise garbage collector:
// the heap starts at init and
// its size is 2/3 * (total_mem_size - init) words
val r1 = code.getRegister();
val r2 = code.getRegister();
val r3 = code.getRegister();
emitLoadConstant(r1,init.getAnchor());
code.emit(SUB,r2,SP,r1);
code.emit(DIVIU,r2,r2,3*WORD_SIZE);
code.emit(LSHI,r2,r2,1); // r2 now contains the size of the heap
emitLoadConstant(r3,SP << 27);
code.emit(ADD, r2, r2, r3);
code.emit(SYSCALL, r1, r2, SYS_GC_INIT);
code.freeRegister(r1);
code.freeRegister(r2);
code.freeRegister(r3);
// populate VMTs
genTmp { classHeader =>
for ( val ClassDef(cname, _, members) <- classes ) {
emitLoadConstant(classHeader, cname.sym.offset);
for ( val m <- cname.sym.asInstanceOf[ClassSymbol].allMethods ) {
//TODO: why use LNK here? don't we use a normal register???
emitLoadConstant(LNK, m.address-4);
//TODO: this +4 could be somewhere else....
code.emit(STW, LNK, classHeader, m.offset,
cname.name + "::" + m.name);
}
}
}
// jump to main expression
code.emit(BEQ, ZERO, start);
case ClassDef(_, _, members) =>
members.foreach(gen);
//TODO: case FieldDecl(_, _) => ;
case FieldDecl(_) => ;
case method @ MethodDef(name, params, _, body) =>
val oldFrameSize = code.getFrameSize();
code.decFrameSize(oldFrameSize); // fs = 0
code.incFrameSize(params.length*4+4); // fs = params + this
code.emit(PSH, LNK, SP, 4, "def "+ method.self+"::"+name);
code.incFrameSize(4); // fs = params + this + lnk
name.sym.asInstanceOf[MethodSymbol].address = code.pc();
code.getRegister(RES);
genLoad(body, RES); // fs = params + this + lnk + ...
code.freeRegister(RES);
//TODO FP
code.emit(POP, LNK, SP, code.getFrameSize(), "free arguments on stack " + params.length + "plus framesize " + code.getFrameSize());
code.decFrameSize(code.getFrameSize()); // fs = 0
code.incFrameSize(oldFrameSize); // fs = oldframesize
code.emit(RET, LNK);
case While(cond, stats) =>
genTmp { tmpReg =>
val startwhile = code.getLabel();
val endwhile = code.getLabel();
val fs = code.getFrameSize();
code.anchorLabel(startwhile);
//TODO free targetregister
genCond(cond, endwhile, false);
stats.foreach(gen);
if (fs-code.getFrameSize() != 0) {
code.emit(SUBI, SP, SP, fs-code.getFrameSize());
}
code.decFrameSize(code.getFrameSize());
code.incFrameSize(fs);
code.emit(BEQ, ZERO, startwhile);
code.anchorLabel(endwhile);
}
case Var(varname, _, init) =>
genTmp { tmpReg =>
genLoad(init, tmpReg);
varname.sym.offset = code.getFrameSize() + 4;
code.emit(PSH, tmpReg, SP, 4, "var " + varname.name);
code.incFrameSize(4);
}
case Set(name, expr) =>
genTmp { tmpReg =>
genLoad(expr, tmpReg);
code.emit(STW, tmpReg, SP, code.getFrameSize()-name.sym.offset);
}
case Do(expr) =>
genTmp { tmpReg =>
genLoad(expr, tmpReg);
}
case PrintInt(expr) =>
genTmp { tmpReg =>
genLoad(expr, tmpReg);
code.emit(SYSCALL, tmpReg, ZERO, SYS_IO_WR_INT, "printInt");
code.emit(SYSCALL, ZERO, ZERO, SYS_IO_FLUSH, "flush");
}
case PrintChar(expr) =>
genTmp { tempReg =>
genLoad(expr, tempReg);
code.emit(SYSCALL, tempReg, ZERO, SYS_IO_WR_CHR, "printChar");
// TODO: do we flush all day long?
code.emit(SYSCALL, ZERO, ZERO, SYS_IO_FLUSH, "flush");
}
case _ =>
error("gen: " + tree);
} // def gen(Tree): Unit
def genLoad(tree: Expr, targetReg: Int): Unit = {
//TODO: what the hell is that supposed to be???
//def caseDefault(tree: Expr): Unit = genLoad(
// new If(tree, new IntLit(1), new IntLit(0)),
// targetReg);
assert(code.usedRegisters()(targetReg),
"invariant violated: target register R"
+ targetReg + " not allocated");
tree match {
case NullLit() =>
code.emit(ORIU, targetReg, ZERO, 0, "null");
case IntLit(value) =>
emitLoadConstant(targetReg, value)
case ReadInt() =>
code.emit(SYSCALL, targetReg, ZERO, SYS_IO_RD_INT, "readInt")
case ReadChar() =>
code.emit(SYSCALL, targetReg, ZERO, SYS_IO_RD_CHR, "readChar")
case Unop(op, expr) =>
op match {
case Operators.NOT =>
val storeTrue = code.getLabel();
val stored = code.getLabel();
code.freeRegister(targetReg);
genCond(expr, storeTrue, false);
code.getRegister(targetReg);
emitLoadConstant(targetReg, 0);
code.emit(BEQ, ZERO, stored);
code.anchorLabel(storeTrue);
emitLoadConstant(targetReg, 1);
code.anchorLabel(stored);
case Operators.NEG =>
genLoad(expr, targetReg);
code.emit(SUB, targetReg, ZERO, targetReg, "-");
}
case Binop(op, left, right) =>
//TODO: move gemTmp into each operator...
genLoad(left, targetReg);
genTmp { tmpRight =>
genLoad(right, tmpRight);
op match {
case Operators.ADD =>
code.emit(ADD, targetReg, targetReg, tmpRight, "+");
case Operators.SUB =>
code.emit(SUB, targetReg, targetReg, tmpRight, "-");
case Operators.MUL =>
code.emit(MUL, targetReg, targetReg, tmpRight, "*");
case Operators.DIV =>
code.emit(DIV, targetReg, targetReg, tmpRight, "/");
case Operators.MOD =>
code.emit(MOD, targetReg, targetReg, tmpRight, "%");
case _ =>
val store0 = code.getLabel();
val end = code.getLabel();
code.freeRegister(targetReg);
genCond(tree, store0, false);
code.getRegister(targetReg);
emitLoadConstant(targetReg, 1);
code.emit(BEQ, ZERO, end);
code.anchorLabel(store0);
emitLoadConstant(targetReg, 0);
code.anchorLabel(end);
}
}
case Block(stats, main) =>
//TODO: is it really needed to do code.getFrameSize() and code.setFrameSize()
val fs = code.getFrameSize();
//code.freeRegister(targetReg);
stats.foreach(gen);
//code.getRegister(targetReg);
genLoad(main, targetReg);
//only reset SP if it has changed
if (code.getFrameSize() - fs != 0) {
code.emit(ADDI, SP, SP, code.getFrameSize()-fs, "end of block SP reset");
}
code.decFrameSize(code.getFrameSize());
code.incFrameSize(fs);
case Ident(name) =>
if ( name.name == "this" ) {
code.emit(LDW, targetReg, SP, code.getFrameSize()-4, name.name + " FS:" + code.getFrameSize());
} else {
code.emit(LDW, targetReg, SP, code.getFrameSize() - name.sym.offset,
name.name )//+ " offset: "+ name.sym.offset + " FS:" + code.getFrameSize());
}
case New(name, args) =>
val af = name.sym.asInstanceOf[ClassSymbol].allFields;
emitLoadConstant(targetReg, 4+af.length*4);
code.emit(SYSCALL, targetReg, targetReg, SYS_GC_ALLOC, "new "+name);
genTmp { tmpReg =>
/* pointer to class header */
emitLoadConstant(tmpReg, name.sym.offset);
code.emit(STW, tmpReg, targetReg, 0);
/* class variables */
for ( val x <- args.zip(af) ) {
genLoad(x._1, tmpReg);
code.emit(STW, tmpReg, targetReg, x._2.offset+4);
}
}
case Select(prefix, selector) =>
genLoad(prefix, targetReg);
val okLabel = code.getLabel();
// check prefix != null
code.emit(BNE, targetReg, okLabel);
emitLoadConstant(targetReg, 1);
code.emit(SYSCALL, targetReg, ZERO, SYS_EXIT);
code.anchorLabel(okLabel);
code.emit(LDW, targetReg, targetReg, selector.sym.offset+4);
case Call(receiver, method, args) =>
// put all registers on stack
val used = code.usedRegisters();
var freed:List[Int] = Nil;
for ( val i <- Iterator.range(0, used.length)) {
if ( used(i) && i != targetReg ) {
//code.emit(ADD,ZERO,ZERO,ZERO,targetReg + " tmp " + i);
code.emit(PSH, i, SP, 4, "save registers");
code.incFrameSize(4);
code.freeRegister(i);
freed = i::freed;
}
}
// get 'receiver' and put it as 'this' on the stack
genLoad(receiver, targetReg);
// check receiver != null
val okLabel = code.getLabel();
code.emit(BNE, targetReg, okLabel);
emitLoadConstant(targetReg, 1);
code.emit(SYSCALL, targetReg, ZERO, SYS_EXIT);
code.anchorLabel(okLabel);
//put 'this' on stack
code.emit(PSH, targetReg, SP, 4);
code.incFrameSize(4);
//load vmt pos
code.emit(LDW, targetReg, targetReg, 0);
//load method address
//code.emit(LDW, targetReg, thisReg, method.sym.offset);
// put args on stack
genTmp { tmpReg =>
//val tmpReg = code.getRegister();
//load method address
code.emit(LDW, tmpReg, targetReg, method.sym.offset);
for ( val a <- args ) {
genLoad(a, targetReg);
code.emit(PSH, targetReg, SP, 4);
code.incFrameSize(4);
}
//save current position + 8 (after RET)
code.emit(ORIU, LNK, ZERO, code.pc() + 8, "call " +
method.sym.name.toString() + " offset:" + method.sym.offset.toString());
//code.freeRegister(tmpReg);
//call function
code.emit(RET, tmpReg);
}
if (targetReg!=RES) {
code.emit(ADDI, targetReg, RES, 0, "store return value to target reg");
}
code.decFrameSize(args.length*4+4); // fs = oldframesize - params - this
// retrieve all registers from stack
for ( val j <- freed ) {
code.getRegister(j);
code.emit(POP, j, SP, 4, "restore registers");
code.decFrameSize(4);
}
case If(cond, thenp, elsep) =>
val elseLabel = code.getLabel();
val afterLabel = code.getLabel();
code.freeRegister(targetReg);
genCond(cond, elseLabel, false);
code.getRegister(targetReg);
genLoad(thenp, targetReg);
code.emit(BEQ, ZERO, afterLabel);
code.anchorLabel(elseLabel);
genLoad(elsep, targetReg);
code.anchorLabel(afterLabel);
case _ =>
Console.println("LoadGen________________");
// ... à compléter ...
}
} // genLoad
/**
* Generates code for conditions, that is constructs
* which jump somewhere on the result of a test.
*/
private def genCond(tree: Expr, targetLabel: code.Label, when: Boolean): Unit = {
tree match {
case Binop(op, left, right) => {
genTmp { tmpLeft =>
genLoad(left, tmpLeft);
genTmp { tmpRight =>
genLoad(right, tmpRight);
op match {
// TODO do all this with CMP !!!
case Operators.EQ =>
code.emit(CMP, tmpLeft, tmpLeft, tmpRight, "==");
if ( when ) {
code.emit(BEQ, tmpLeft, targetLabel);
} else {
code.emit(BNE, tmpLeft, targetLabel);
}
case Operators.NE =>
code.emit(CMP, tmpLeft, tmpLeft, tmpRight, "!=");
if ( when ) {
code.emit(BNE, tmpLeft, targetLabel);
} else {
code.emit(BEQ, tmpLeft, targetLabel);
}
case Operators.LT =>
code.emit(CMP, tmpLeft, tmpLeft, tmpRight);
if ( when ) {
code.emit(BLT, tmpLeft, targetLabel);
} else {
code.emit(BGE, tmpLeft, targetLabel);
}
case Operators.LE =>
code.emit(CMP, tmpLeft, tmpLeft, tmpRight);
if ( when ) {
code.emit(BLE, tmpLeft, targetLabel);
} else {
code.emit(BGT, tmpLeft, targetLabel);
}
case Operators.GT =>
code.emit(CMP, tmpLeft, tmpLeft, tmpRight);
if ( when ) {
code.emit(BGT, tmpLeft, targetLabel);
} else {
code.emit(BLE, tmpLeft, targetLabel);
}
case Operators.GE =>
code.emit(CMP, tmpLeft, tmpLeft, tmpRight);
if ( when ) {
code.emit(BGE, tmpLeft, targetLabel);
} else {
code.emit(BLT, tmpLeft, targetLabel);
}
case Operators.AND =>
code.emit(AND, tmpLeft, tmpLeft, tmpRight);
if ( when ) {
code.emit(BNE, tmpLeft, targetLabel);
} else {
code.emit(BEQ, tmpLeft, targetLabel);
}
case _ =>
genLoad(tree, tmpLeft);
if ( when ) {
code.emit(BNE, tmpLeft, targetLabel);
} else {
code.emit(BEQ, tmpLeft, targetLabel);
}
}
}
}
}
case Unop(op, expr) => {
op match {
case Operators.NOT =>
genCond(expr, targetLabel, !when);
case Operators.NEG =>
genCond(expr, targetLabel, when);
}
}
case _ =>
genTmp { tmpReg =>
genLoad(tree, tmpReg);
if ( when ) {
code.emit(BNE, tmpReg, targetLabel);
} else {
code.emit(BEQ, tmpReg, targetLabel);
}
}
}
} // genCond
}