Newer
Older
zweic / sources / zweic / Generator.scala
@glproj03 glproj03 on 3 Feb 2006 13 KB added theirasm
/*  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


}