Newer
Older
zweic / sources / zweic / Generator.scala
@glproj03 glproj03 on 6 Feb 2006 13 KB Added impossible test case
/*  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


}