/* 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()+"<<"); code.emit(LSHI, r, r, 16, "<< 16"); code.emit(ORIU, r, ZERO, lowBits, value.toString()); } else { code.emit(ORIU, r, ZERO, lowBits, value.toString()); } } 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); 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(r3); code.freeRegister(r2); code.freeRegister(r1); // populate VMTs genTmp { classHeader => genTmp { methodAddr => for ( val ClassDef(cname, _, members) <- classes ) { emitLoadConstant(classHeader, cname.sym.offset); for ( val m <- cname.sym.asInstanceOf[ClassSymbol].allMethods ) { emitLoadConstant(methodAddr, m.address); code.emit(STW, methodAddr, classHeader, m.offset); } } } } // 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); code.emit(SUBI, SP, SP, code.getFrameSize() - (4+4+4*params.length) ); code.decFrameSize(code.getFrameSize()); // fs = 0 code.incFrameSize(4+4+4*params.length); // fs = params + this + lnk code.emit(POP, LNK, SP, 4); code.decFrameSize(4); // fs = params + this code.emit(SUBI, SP, SP, params.length*4+4); 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); genCond(cond, endwhile, false); stats.foreach(gen); 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); 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 _ => //Console.println("gen __________________________________" + tree); error("gen: " + tree); } // def gen(Tree): Unit def genLoad(tree: Expr, targetReg: Int): Unit = { 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 store1 = code.getLabel(); val end = code.getLabel(); genCond(expr, store1, false); emitLoadConstant(targetReg, 0); code.emit(BEQ, ZERO, end); code.anchorLabel(store1); emitLoadConstant(targetReg, 1); code.anchorLabel(end); case Operators.NEG => genLoad(expr, targetReg); code.emit(SUB, targetReg, ZERO, targetReg, "-"); } case Binop(op, left, right) => genTmp { tmpLeft => genLoad(left, tmpLeft); genLoad(right, targetReg); op match { case Operators.ADD => code.emit(ADD, targetReg, tmpLeft, targetReg, "+"); case Operators.SUB => code.emit(SUB, targetReg, tmpLeft, targetReg, "-"); case Operators.MUL => code.emit(MUL, targetReg, tmpLeft, targetReg, "*"); case Operators.DIV => code.emit(DIV, targetReg, tmpLeft, targetReg, "/"); case Operators.MOD => code.emit(MOD, targetReg, tmpLeft, targetReg, "%"); case _ => val store0 = code.getLabel(); val end = code.getLabel(); genCond(tree, store0, false); 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); code.emit(SUBI, SP, SP, fs-code.getFrameSize()); code.decFrameSize(code.getFrameSize()); code.incFrameSize(fs); case Ident(name) => if ( name.name == "this" ) { code.emit(LDW, targetReg, SP, code.getFrameSize(), name.name); } else { code.emit(LDW, targetReg, SP, code.getFrameSize()-(name.sym.offset), name.name); } case New(name, args) => val af = name.sym.asInstanceOf[ClassSymbol].allFields; genTmp { tmpReg => emitLoadConstant(tmpReg, 4+af.length*4); code.emit(SYSCALL, targetReg, tmpReg, SYS_GC_ALLOC, " allocate memory for instance of '"+name+"'"); /* 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(PSH, i, SP, 4); 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); code.emit(PSH, targetReg, SP, 4); code.incFrameSize(4); // put args on stack genTmp { tmpReg => for ( val a <- args ) { genLoad(a, tmpReg); code.emit(PSH, tmpReg, SP, 4); code.incFrameSize(4); } } //load method address code.emit(LDW, targetReg, targetReg, method.sym.offset); // check receiver.method() != null val okLabel2 = code.getLabel(); code.emit(BNE, targetReg, okLabel2); emitLoadConstant(targetReg, 1); code.emit(SYSCALL, targetReg, ZERO, SYS_EXIT); code.anchorLabel(okLabel2); //set return address code.emit(SUBI, LNK, SP, 4, "store return address"); //call function code.emit(RET, targetReg); //store return value code.emit(ADD, targetReg, ZERO, RES); 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); code.decFrameSize(4); } case If(cond, thenp, elsep) => val elseLabel = code.getLabel(); val afterLabel = code.getLabel(); genCond(cond, elseLabel, false); 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 => genTmp { tmpRight => genLoad(left, tmpLeft); genLoad(right, tmpRight); op match { // TODO do all this with CMP !!! case Operators.EQ => code.emit(CMP, tmpLeft, tmpLeft, tmpRight, ""); if ( when ) { code.emit(BNE, tmpLeft, targetLabel); } else { code.emit(BEQ, tmpLeft, targetLabel); } case Operators.NE => code.emit(CMP, tmpLeft, tmpLeft, tmpRight, ""); if ( when ) { code.emit(BEQ, tmpLeft, targetLabel); } else { code.emit(BNE, tmpLeft, targetLabel); } case Operators.ADD => code.emit(ADD, tmpLeft, tmpLeft, tmpRight); if ( when ) { code.emit(BNE, tmpLeft, targetLabel); } else { code.emit(BEQ, tmpLeft, targetLabel); } case Operators.SUB => code.emit(CMP, tmpLeft, tmpLeft, tmpRight); if ( when ) { code.emit(BNE, tmpLeft, targetLabel); } else { code.emit(BEQ, tmpLeft, targetLabel); } case Operators.MUL => code.emit(MUL, tmpLeft, tmpLeft, tmpRight); if ( when ) { code.emit(BNE, tmpLeft, targetLabel); } else { code.emit(BEQ, tmpLeft, targetLabel); } case Operators.DIV => code.emit(DIV, tmpLeft, tmpLeft, tmpRight); if ( when ) { code.emit(BNE, tmpLeft, targetLabel); } else { code.emit(BEQ, tmpLeft, targetLabel); } case Operators.MOD => code.emit(MOD, 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 }