/* 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 }