diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/icode/GenICode.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 1672 |
1 files changed, 1672 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala new file mode 100644 index 0000000000..a7940cd66a --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -0,0 +1,1672 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.icode; + +import scala.collection.mutable.{Map, HashMap}; +import scala.tools.nsc.symtab._; + + +/** + * TODO: + * - switches with alternatives + */ +abstract class GenICode extends SubComponent { + import global._; + import icodes._; + import icodes.opcodes._; + + val phaseName = "icode"; + + override def newPhase(prev: Phase) = new ICodePhase(prev); + + class ICodePhase(prev: Phase) extends StdPhase(prev) { + override def name = "icode"; + + override def description = "Generate ICode from the AST"; + + var unit: CompilationUnit = _; + + // We assume definitions are alread initialized + val STRING = REFERENCE(definitions.StringClass); + + // this depends on the backend! should be changed. + val ANY_REF_CLASS = REFERENCE(definitions.ObjectClass); + + val SCALA_ALL = REFERENCE(definitions.AllClass); + val SCALA_ALLREF = REFERENCE(definitions.AllRefClass); + val THROWABLE = REFERENCE(definitions.ThrowableClass); + + /////////////////////////////////////////////////////////// + + override def run: Unit = { + scalaPrimitives.init; + super.run + } + + override def apply(unit: CompilationUnit): Unit = { + this.unit = unit; + log("Generating icode for " + unit); + gen(unit.body); + } + + def gen(tree: Tree): Context = gen(tree, new Context()); + + def gen(trees: List[Tree], ctx: Context): Context = { + var ctx1 = ctx; + for (val t <- trees) + ctx1 = gen(t, ctx1); + + ctx1 + } + + /////////////////// Code generation /////////////////////// + + def gen(tree: Tree, ctx: Context): Context = tree match { + case EmptyTree => ctx; + + case PackageDef(name, stats) => gen(stats, ctx setPackage name); + + case ClassDef(mods, name, tparams, tpt, impl) => + log("Generating class: " + tree.symbol.fullNameString); + ctx setClass (new IClass(tree.symbol) setCompilationUnit unit); + addClassFields(ctx, tree.symbol); + classes = ctx.clazz :: classes; + gen(impl, ctx); + ctx setClass null; + + // !! modules should be eliminated by refcheck... or not? + case ModuleDef(mods, name, impl) => + abort("Modules should not reach backend!"); + + log("Generating module: " + tree.symbol.fullNameString); + ctx setClass (new IClass(tree.symbol) setCompilationUnit unit); + addClassFields(ctx, tree.symbol); + classes = ctx.clazz :: classes; + gen(impl, ctx); + ctx setClass null; + + case ValDef(mods, name, tpt, rhs) => ctx; // we use the symbol to add fields + + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + if (settings.debug.value) + log("Entering method " + name); + val m = new IMethod(tree.symbol); + m.sourceFile = unit.source.toString(); + m.returnType = if (tree.symbol.isConstructor) UNIT + else toTypeKind(tree.symbol.info.resultType); + ctx.clazz.addMethod(m); + + var ctx1 = ctx.enterMethod(m, tree.asInstanceOf[DefDef]); + addMethodParams(ctx1, vparamss); + + if (!m.isDeferred) { + ctx1 = genLoad(rhs, ctx1, m.returnType); + + // reverse the order of the local variables, to match the source-order + m.locals = m.locals.reverse; + + rhs match { + case Block(_, Return(_)) => (); + case Return(_) => (); + case _ => if (ctx1.bb.isEmpty) + ctx1.bb.emit(RETURN(m.returnType), rhs.pos); + else + ctx1.bb.emit(RETURN(m.returnType)); + } + ctx1.bb.close; + prune(ctx1.method); + } else + ctx1.method.setCode(null); + ctx1; + + case Template(parents, body) => + gen(body, ctx); + + case _ => + abort("Illegal tree in gen: " + tree); + } + + private def genStat(trees: List[Tree], ctx: Context): Context = { + var currentCtx = ctx; + + for (val t <- trees) + currentCtx = genStat(t, currentCtx); + + currentCtx + } + + /** + * Generate code for the given tree. The trees should contain statements + * and not produce any value. Use genLoad for expressions which leave + * a value on top of the stack. + * + * @return a new context. This is necessary for control flow instructions + * which may change the current basic block. + */ + private def genStat(tree: Tree, ctx: Context): Context = tree match { + case Assign(lhs @ Select(_, _), rhs) => + if (isStaticSymbol(lhs.symbol)) { + val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info)); + ctx1.bb.emit(STORE_FIELD(lhs.symbol, true), tree.pos); + ctx1 + } else { + var ctx1 = genLoadQualifier(lhs, ctx); + ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info)); + ctx1.bb.emit(STORE_FIELD(lhs.symbol, false), tree.pos); + ctx1 + } + + case Assign(lhs, rhs) => +// assert(ctx.method.locals.contains(lhs.symbol) | ctx.clazz.fields.contains(lhs.symbol), +// "Assignment to inexistent local or field: " + lhs.symbol); + val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info)); + val Some(l) = ctx.method.lookupLocal(lhs.symbol); + ctx1.bb.emit(STORE_LOCAL(l, lhs.symbol.isValueParameter), tree.pos); + ctx1 + + case _ => + if (settings.debug.value) + log("Passing " + tree + " to genLoad"); + genLoad(tree, ctx, UNIT); + } + + /** + * Generate code for trees that produce values on the stack + * + * @param tree The tree to be translated + * @param ctx The current context + * @param expectedType The type of the value to be generated on top of the + * stack. + * @return The new context. The only thing that may change is the current + * basic block (as the labels map is mutable). + */ + private def genLoad(tree: Tree, ctx: Context, expectedType: TypeKind): Context = { + var generatedType = expectedType; + + /** + * Generate code for primitive arithmetic operations. + */ + def genArithmeticOp(tree: Tree, ctx: Context, code: Int): Context = { + val Apply(fun @ Select(larg, _), args) = tree; + var ctx1 = ctx; + var resKind = toTypeKind(larg.tpe); + + assert(args.length <= 1, + "Too many arguments for primitive function: " + fun.symbol); + assert(resKind.isNumericType | resKind == BOOL, + resKind.toString() + " is not a numeric or boolean type [operation: " + fun.symbol + "]"); + + args match { + // unary operation + case Nil => + ctx1 = genLoad(larg, ctx1, resKind); + code match { + case scalaPrimitives.POS => (); // nothing + case scalaPrimitives.NEG => ctx1.bb.emit(CALL_PRIMITIVE(Negation(resKind)), larg.pos); + case scalaPrimitives.NOT => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(NOT, resKind)), larg.pos); + case _ => abort("Unknown unary operation: " + fun.symbol.fullNameString + " code: " + code); + } + generatedType = resKind; + + // binary operation + case rarg :: Nil => + resKind = getMaxType(larg.tpe :: rarg.tpe :: Nil); + if (scalaPrimitives.isShiftOp(code) || scalaPrimitives.isBitwiseOp(code)) + assert(resKind.isIntType | resKind == BOOL, + resKind.toString() + " incompatible with arithmetic modulo operation: " + ctx1); + + ctx1 = genLoad(larg, ctx1, resKind); + ctx1 = genLoad(rarg, + ctx1, // check .NET size of shift arguments! + if (scalaPrimitives.isShiftOp(code)) INT else resKind); + + generatedType = resKind; + code match { + case scalaPrimitives.ADD => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(ADD, resKind)), tree.pos); + case scalaPrimitives.SUB => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(SUB, resKind)), tree.pos); + case scalaPrimitives.MUL => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(MUL, resKind)), tree.pos); + case scalaPrimitives.DIV => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(DIV, resKind)), tree.pos); + case scalaPrimitives.MOD => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(REM, resKind)), tree.pos); + case scalaPrimitives.OR => ctx1.bb.emit(CALL_PRIMITIVE(Logical(OR, resKind)), tree.pos); + case scalaPrimitives.XOR => ctx1.bb.emit(CALL_PRIMITIVE(Logical(XOR, resKind)), tree.pos); + case scalaPrimitives.AND => ctx1.bb.emit(CALL_PRIMITIVE(Logical(AND, resKind)), tree.pos); + case scalaPrimitives.LSL => ctx1.bb.emit(CALL_PRIMITIVE(Shift(LSL, resKind)), tree.pos); + generatedType = resKind; + case scalaPrimitives.LSR => ctx1.bb.emit(CALL_PRIMITIVE(Shift(LSR, resKind)), tree.pos); + generatedType = resKind; + case scalaPrimitives.ASR => ctx1.bb.emit(CALL_PRIMITIVE(Shift(ASR, resKind)), tree.pos); + generatedType = resKind; + case _ => abort("Unknown primitive: " + fun.symbol + "[" + code + "]"); + } + + case _ => abort("Too many arguments for primitive function: " + tree); + } + ctx1 + } + + /** Generate primitive array operations. */ + def genArrayOp(tree: Tree, ctx: Context, code: Int): Context = { + import scalaPrimitives._; + val Apply(Select(arrayObj, _), args) = tree; + val k = toTypeKind(arrayObj.tpe); + val ARRAY(elem) = k; + var ctx1 = genLoad(arrayObj, ctx, k); + + if (scalaPrimitives.isArrayGet(code)) { + // load argument on stack + assert(args.length == 1, + "Too many arguments for array get operation: " + tree); + ctx1 = genLoad(args.head, ctx1, INT); + generatedType = elem; + } else if (scalaPrimitives.isArraySet(code)) { + assert(args.length == 2, + "Too many arguments for array set operation: " + tree); + ctx1 = genLoad(args.head, ctx1, INT); + ctx1 = genLoad(args.tail.head, ctx1, toTypeKind(args.tail.head.tpe)); + // the following line should really be here, but because of bugs in erasure + // we pretend we generate whatever type is expected from us. + //generatedType = UNIT; + } else + generatedType = INT; + + code match { + case ZARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(BOOL)), tree.pos); + case BARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(BYTE)), tree.pos); + case SARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(SHORT)), tree.pos); + case CARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(CHAR)), tree.pos); + case IARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(INT)), tree.pos); + case LARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(LONG)), tree.pos); + case FARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(FLOAT)), tree.pos); + case DARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(DOUBLE)), tree.pos); + case OARRAY_LENGTH => + ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(ANY_REF_CLASS)), tree.pos); + + case ZARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(BOOL), tree.pos); + case BARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(BYTE), tree.pos); + case SARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(SHORT), tree.pos); + case CARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(CHAR), tree.pos); + case IARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(INT), tree.pos); + case LARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(LONG), tree.pos); + case FARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(FLOAT), tree.pos); + case DARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(DOUBLE), tree.pos); + case OARRAY_GET => + ctx1.bb.emit(LOAD_ARRAY_ITEM(ANY_REF_CLASS), tree.pos); + + case ZARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(BOOL), tree.pos); + case BARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(BYTE), tree.pos); + case SARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(SHORT), tree.pos); + case CARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(CHAR), tree.pos); + case IARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(INT), tree.pos); + case LARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(LONG), tree.pos); + case FARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(FLOAT), tree.pos); + case DARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(DOUBLE), tree.pos); + case OARRAY_SET => + ctx1.bb.emit(STORE_ARRAY_ITEM(ANY_REF_CLASS), tree.pos); + + case _ => + abort("Unknown operation on arrays: " + tree + " code: " + code); + } + ctx1 + } + + // genLoad + val resCtx: Context = tree match { + case LabelDef(name, params, rhs) => + val ctx1 = ctx.newBlock; + ctx1.labels.get(tree.symbol) match { + case Some(label) => + label.anchor(ctx1.bb); + label.patch(ctx.method.code); + + case None => + ctx1.labels += tree.symbol -> (new Label(tree.symbol) anchor ctx1.bb setParams (params map (.symbol))); + ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info)))); + if (settings.debug.value) + log("Adding label " + tree.symbol); + } + +// if (!isTailCallLabel(tree.asInstanceOf[LabelDef], ctx)) { +// log("Non-tail call label found (" + tree.symbol + "), initializing arguments to default values."); +// genLoadLabelArguments(params map { p => zeroOf(toTypeKind(p.symbol.tpe)) }, +// ctx1.labels(tree.symbol), +// ctx); +// } + + ctx.bb.emit(JUMP(ctx1.bb), tree.pos); + ctx.bb.close; + genLoad(rhs, ctx1, expectedType /*toTypeKind(tree.symbol.info.resultType)*/); + + case ValDef(_, _, _, rhs) => + val sym = tree.symbol; + val local = new Local(sym, toTypeKind(sym.info)); + ctx.method.addLocal(local); + + if (rhs == EmptyTree) { + if (settings.debug.value) + log("Uninitialized variable " + tree + " at: " + unit.position(tree.pos)); + ctx.bb.emit(getZeroOf(local.kind)); + } + + var ctx1 = ctx; + if (rhs != EmptyTree) + ctx1 = genLoad(rhs, ctx, local.kind); + + ctx1.bb.emit(STORE_LOCAL(local, false), tree.pos); + generatedType = UNIT; + ctx1 + + case If(cond, thenp, elsep) => + var thenCtx = ctx.newBlock; + var elseCtx = ctx.newBlock; + val contCtx = ctx.newBlock; + genCond(cond, ctx, thenCtx, elseCtx); + val ifKind = toTypeKind(tree.tpe); + + val thenKind = toTypeKind(thenp.tpe); + val elseKind = if (elsep == EmptyTree) UNIT else toTypeKind(elsep.tpe); + + generatedType = ifKind; + + // we need to drop unneeded results, if one branch gives + // unit and the other gives something on the stack, because + // the type of 'if' is scala.Any, and its erasure would be Object. + // But unboxed units are not Objects... + if (thenKind == UNIT || elseKind == UNIT) { + if (settings.debug.value) + log("Will drop result from an if branch"); + thenCtx = genLoad(thenp, thenCtx, UNIT); + elseCtx = genLoad(elsep, elseCtx, UNIT); + assert(expectedType == UNIT, "I produce UNIT in a context where " + expectedType + " is expected!"); + generatedType = UNIT; + } else { + thenCtx = genLoad(thenp, thenCtx, ifKind); + elseCtx = genLoad(elsep, elseCtx, ifKind); + } + + thenCtx.bb.emit(JUMP(contCtx.bb)); + thenCtx.bb.close; + elseCtx.bb.emit(JUMP(contCtx.bb)); + elseCtx.bb.close; + + contCtx; + + case Return(expr) => + val returnedKind = toTypeKind(expr.tpe); + val ctx1 = genLoad(expr, ctx, returnedKind); + ctx1.bb.emit(RETURN(returnedKind), tree.pos); + ctx1.bb.enterIgnoreMode; + generatedType = expectedType; + ctx1 + + case Try(block, catches, finalizer) => + val kind = toTypeKind(tree.tpe); + var handlers = for (val CaseDef(pat, _, body) <- catches.reverse) + yield pat match { + case Typed(Ident(nme.WILDCARD), tpt) => Pair(tpt.tpe.symbol, { + ctx: Context => + ctx.bb.emit(DROP(REFERENCE(tpt.tpe.symbol))); + val ctx1 = genLoad(finalizer, ctx, UNIT); + genLoad(body, ctx1, kind); + }) + + case Ident(nme.WILDCARD) => Pair(definitions.ThrowableClass, { + ctx: Context => + ctx.bb.emit(DROP(REFERENCE(definitions.ThrowableClass))); + val ctx1 = genLoad(finalizer, ctx, UNIT); + genLoad(body, ctx1, kind) + }) + + case Bind(name, _) => + val exception = new Local(pat.symbol, toTypeKind(pat.symbol.tpe)); + ctx.method.addLocal(exception); + + Pair(pat.symbol.tpe.symbol, { + ctx: Context => + ctx.bb.emit(STORE_LOCAL(exception, false), pat.pos); + val ctx1 = genLoad(finalizer, ctx, UNIT); + genLoad(body, ctx1, kind) + }) + } + + if (finalizer != EmptyTree) + handlers = Pair(NoSymbol, { + ctx: Context => + val ctx1 = genLoad(finalizer, ctx, UNIT); + ctx1.bb.emit(THROW()); + ctx1 + }) :: handlers; + + ctx.Try( + bodyCtx => { + generatedType = toTypeKind(block.tpe); + val ctx1 = genLoad(block, bodyCtx, generatedType); + genLoad(finalizer, ctx1, UNIT) + }, + handlers) + + case Throw(expr) => + val ctx1 = genLoad(expr, ctx, THROWABLE); + ctx1.bb.emit(THROW(), tree.pos); + ctx1.bb.enterIgnoreMode; + generatedType = SCALA_ALL; + ctx1; + + case New(tpt) => + abort("Unexpected New"); + + case Apply(TypeApply(fun, targs), _) => + val sym = fun.symbol; + var ctx1 = ctx; + var cast = false; + + if (sym == definitions.Object_isInstanceOf) + cast = false + else if (sym == definitions.Object_asInstanceOf) + cast = true + else + abort("Unexpected type application " + fun + "[sym: " + sym + "]"); + + val Select(obj, _) = fun; + val l = toTypeKind(obj.tpe); + val r = toTypeKind(targs.head.tpe); + + ctx1 = genLoadQualifier(fun, ctx); + + if (l.isValueType && r.isValueType) + genConversion(l, r, ctx1, cast) + else if (l.isValueType) { + ctx1.bb.emit(DROP(l), fun.pos); + if (cast) { + ctx1.bb.emit(NEW(REFERENCE(definitions.getClass("ClassCastException")))); + ctx1.bb.emit(DUP(ANY_REF_CLASS)); + ctx1.bb.emit(THROW()); + } else + ctx1.bb.emit(CONSTANT(Constant(false))) + } + else if (r.isValueType /*|| r.isArrayType */) + genBoxedConversion(l, r, ctx1, cast) + else + genCast(l, r, ctx1, cast); + + generatedType = if (cast) r else BOOL; + ctx1 + + // 'super' call: Note: since constructors are supposed to + // return an instance of what they construct, we have to take + // special care. On JVM they are 'void', and Scala forbids (syntactically) + // to call super constructors explicitly and/or use their 'returned' value. + // therefore, we can ignore this fact, and generate code that leaves nothing + // on the stack (contrary to what the type in the AST says). + case Apply(fun @ Select(Super(_, mixin), _), args) => + if (settings.debug.value) + log("Call to super: " + tree); + val invokeStyle = SuperCall(mixin); +// if (fun.symbol.isConstructor) Static(true) else SuperCall(mixin); + + ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos); + val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx); + + ctx1.bb.emit(CALL_METHOD(fun.symbol, invokeStyle), tree.pos); + generatedType = if (fun.symbol.isConstructor) + UNIT + else + toTypeKind(fun.symbol.info.resultType); + ctx1 + + // 'new' constructor call: Note: since constructors are + // thought to return an instance of what they construct, + // we have to 'simulate' it by DUPlicating the freshly created + // instance (on JVM, <init> methods return VOID). + case Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) => + val ctor = fun.symbol; + assert(ctor.isClassConstructor, + "'new' call to non-constructor: " + tree); + + generatedType = toTypeKind(tpt.tpe); + assert(generatedType.isReferenceType || generatedType.isArrayType, + "Non reference type cannot be instantiated: " + generatedType); + + var ctx1 = ctx; + + generatedType match { + case ARRAY(elem) => + ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx); + ctx1.bb.emit(CREATE_ARRAY(elem), tree.pos); + + case REFERENCE(cls) => + assert(ctor.owner == cls, + "Symbol " + ctor.owner.fullNameString + "is different than " + tpt); + ctx1.bb.emit(NEW(generatedType), tree.pos); + ctx1.bb.emit(DUP(generatedType)); + ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx); + + ctx1.bb.emit(CALL_METHOD(ctor, Static(true)), tree.pos); + + case _ => + abort("Cannot instantiate " + tpt + "of kind: " + generatedType); + } + ctx1 + + case Apply(fun, args) => + val sym = fun.symbol; + + if (sym.isLabel) { // jump to a label + val label = ctx.labels.get(sym) match { + case Some(l) => l; + + // it is a forward jump, scan for labels + case None => + log("Performing scan for label because of forward jump."); + scanForLabels(ctx.defdef, ctx); + ctx.labels.get(sym) match { + case Some(l) => + log("Found label: " + l); + l + case _ => abort("Unknown label target: " + sym + " at: " + unit.position(fun.pos) + ": ctx: " + ctx); + } + } + val ctx1 = genLoadLabelArguments(args, label, ctx); + if (label.anchored) + ctx1.bb.emit(JUMP(label.block), tree.pos); + else + ctx1.bb.emit(PJUMP(label), tree.pos); + + ctx1.bb.close; + ctx1.newBlock; + } else if (isPrimitive(fun.symbol)) { // primitive method call + val Select(receiver, _) = fun; + + val code = scalaPrimitives.getPrimitive(fun.symbol, receiver.tpe); + var ctx1 = ctx; + + if (scalaPrimitives.isArithmeticOp(code)) { + ctx1 = genArithmeticOp(tree, ctx1, code); + } else if (code == scalaPrimitives.CONCAT) { + ctx1 = genStringConcat(tree, ctx1); + generatedType = STRING; + } else if (scalaPrimitives.isArrayOp(code)) { + ctx1 = genArrayOp(tree, ctx1, code); + } else if (scalaPrimitives.isLogicalOp(code) || + scalaPrimitives.isComparisonOp(code)) { + + val trueCtx = ctx1.newBlock; + val falseCtx = ctx1.newBlock; + val afterCtx = ctx1.newBlock; + if (settings.debug.value) + log("Passing " + tree + " to genCond"); + genCond(tree, ctx1, trueCtx, falseCtx); + trueCtx.bb.emit(CONSTANT(Constant(true)), tree.pos); + trueCtx.bb.emit(JUMP(afterCtx.bb)); + trueCtx.bb.close; + falseCtx.bb.emit(CONSTANT(Constant(false)), tree.pos); + falseCtx.bb.emit(JUMP(afterCtx.bb)); + falseCtx.bb.close; + generatedType = BOOL; + ctx1 = afterCtx; + } else if (code == scalaPrimitives.SYNCHRONIZED) { + val monitor = new Local(ctx.method.symbol.newVariable(tree.pos, unit.fresh.newName("monitor")).setInfo(definitions.ObjectClass.tpe), + ANY_REF_CLASS); + ctx.method.addLocal(monitor); + + ctx1 = genLoadQualifier(fun, ctx1); + ctx1.bb.emit(DUP(ANY_REF_CLASS)); + ctx1.bb.emit(STORE_LOCAL(monitor, false)); + ctx1.bb.emit(MONITOR_ENTER(), tree.pos); + + if (settings.debug.value) + log("synchronized block start"); + ctx1 = ctx1.Try( + bodyCtx => { + val ctx1 = genLoad(args.head, bodyCtx, toTypeKind(tree.tpe.resultType)); + ctx1.bb.emit(LOAD_LOCAL(monitor, false)); + ctx1.bb.emit(MONITOR_EXIT(), tree.pos); + ctx1 + }, List( + Pair(NoSymbol, exhCtx => { + exhCtx.bb.emit(LOAD_LOCAL(monitor, false)); + exhCtx.bb.emit(MONITOR_EXIT(), tree.pos); + exhCtx.bb.emit(THROW()); + exhCtx + }))); + if (settings.debug.value) + log("synchronized block end with block " + ctx1.bb + " closed=" + ctx1.bb.isClosed); + } else if (scalaPrimitives.isCoercion(code)) { + ctx1 = genLoad(receiver, ctx1, toTypeKind(receiver.tpe)); + genCoercion(tree, ctx1, code); + } else + abort("Primitive operation not handled yet: " + + fun.symbol.fullNameString + "(" + fun.symbol.simpleName + ") " + + " at: " + unit.position(tree.pos)); + ctx1 + } else { // normal method call + if (settings.debug.value) + log("Gen CALL_METHOD with sym: " + sym + " isStaticSymbol: " + isStaticSymbol(sym)); + var invokeStyle = + if (isStaticSymbol(sym)) + Static(false) + else if (sym.hasFlag(Flags.PRIVATE) || sym.isClassConstructor) + Static(true) + else + Dynamic; + + var ctx1 = if (invokeStyle.hasInstance) + genLoadQualifier(fun, ctx); + else + ctx; + + ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx1); + + ctx1.bb.emit(CALL_METHOD(sym, invokeStyle), tree.pos); + generatedType = if (sym.isClassConstructor) UNIT else toTypeKind(sym.info.resultType); + ctx1 + } + + case This(qual) => + assert(tree.symbol == ctx.clazz.symbol || tree.symbol.isModuleClass, + "Trying to access the this of another class: " + + "tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol); + if (tree.symbol.isModuleClass && tree.symbol != ctx.clazz.symbol) { + if (settings.debug.value) + log("LOAD_MODULE from 'This': " + tree.symbol); + ctx.bb.emit(LOAD_MODULE(tree.symbol), tree.pos); + generatedType = REFERENCE(tree.symbol); + } else { + ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos); + generatedType = REFERENCE(ctx.clazz.symbol); + } + ctx; + + case Select(Ident(nme.EMPTY_PACKAGE_NAME), module) => + assert(tree.symbol.isModule, + "Selection of non-module from empty package: " + tree.toString() + + " sym: " + tree.symbol + + " at: " + unit.position(tree.pos)); + if (settings.debug.value) + log("LOAD_MODULE from Select(<emptypackage>): " + tree.symbol); + ctx.bb.emit(LOAD_MODULE(tree.symbol), tree.pos); + ctx + + case Select(qualifier, selector) => + val sym = tree.symbol; + generatedType = toTypeKind(sym.info); + + if (sym.isModule) { + if (settings.debug.value) + log("LOAD_MODULE from Select(qualifier, selector): " + sym); + ctx.bb.emit(LOAD_MODULE(sym), tree.pos); + ctx + } else if (isStaticSymbol(sym)) { + ctx.bb.emit(LOAD_FIELD(sym, true), tree.pos); + ctx + } else { + val ctx1 = genLoadQualifier(tree, ctx); + ctx1.bb.emit(LOAD_FIELD(sym, false), tree.pos); + ctx1 + } + + case Ident(name) => + if (!tree.symbol.isPackage) { + if (tree.symbol.isModule) { + if (settings.debug.value) + log("LOAD_MODULE from Ident(name): " + tree.symbol); + ctx.bb.emit(LOAD_MODULE(tree.symbol), tree.pos); + generatedType = toTypeKind(tree.symbol.info); + } else { + val Some(l) = ctx.method.lookupLocal(tree.symbol); + ctx.bb.emit(LOAD_LOCAL(l, tree.symbol.isValueParameter), tree.pos); + generatedType = l.kind; + } + } + ctx + + case Literal(value) => + if (value.tag != UnitTag) + ctx.bb.emit(CONSTANT(value), tree.pos); + generatedType = toTypeKind(value.tpe); + ctx + + case Block(stats, expr) => + assert(!(ctx.method eq null), "Block outside method"); + val ctx1 = genStat(stats, ctx); + genLoad(expr, ctx1, expectedType); + + case Typed(expr, _) => + genLoad(expr, ctx, expectedType); + + case Assign(_, _) => + generatedType = UNIT; + genStat(tree, ctx); + + case ArrayValue(tpt @ TypeTree(), elems) => + var ctx1 = ctx; + val elmKind = toTypeKind(tpt.tpe); + generatedType = ARRAY(elmKind); + + ctx1.bb.emit(CONSTANT(new Constant(elems.length)), tree.pos); + ctx1.bb.emit(CREATE_ARRAY(elmKind)); + // inline array literals + var i = 0; + while (i < elems.length) { + ctx1.bb.emit(DUP(generatedType), tree.pos); + ctx1.bb.emit(CONSTANT(new Constant(i))); + ctx1 = genLoad(elems(i), ctx1, elmKind); + ctx1.bb.emit(STORE_ARRAY_ITEM(elmKind)); + i = i + 1; + } + ctx1 + + case Match(selector, cases) => + if (settings.debug.value) + log("Generating SWITCH statement."); + var ctx1 = genLoad(selector, ctx, INT); + val afterCtx = ctx1.newBlock; + var caseCtx: Context = null; + val kind = toTypeKind(tree.tpe); + + var targets: List[BasicBlock] = Nil; + var tags: List[Int] = Nil; + var default: BasicBlock = afterCtx.bb; + + for (val caze <- cases) + caze match { + case CaseDef(Literal(value), EmptyTree, body) => + tags = value.intValue :: tags; + val tmpCtx = ctx1.newBlock; + targets = tmpCtx.bb :: targets; + + caseCtx = genLoad(body, tmpCtx , kind); + caseCtx.bb.emit(JUMP(afterCtx.bb), caze.pos); + caseCtx.bb.close; + + case CaseDef(Ident(nme.WILDCARD), EmptyTree, body) => + val tmpCtx = ctx1.newBlock; + default = tmpCtx.bb; + + caseCtx = genLoad(body, tmpCtx , kind); + caseCtx.bb.emit(JUMP(afterCtx.bb), caze.pos); + caseCtx.bb.close; + + case _ => abort("Invalid case statement in switch-like pattern match: " + + tree + " at: " + unit.position(tree.pos)); + } + ctx1.bb.emit(SWITCH(tags.reverse map (x => List(x)), + (default :: targets).reverse), tree.pos); + ctx1.bb.close; + afterCtx + + case EmptyTree => ctx; + + case _ => abort("Unexpected tree in genLoad: " + tree + " at: " + unit.position(tree.pos)); + } + + // emit conversion + if (generatedType != expectedType) + adapt(generatedType, expectedType, resCtx, tree); + + resCtx; + } + + private def adapt(from: TypeKind, to: TypeKind, ctx: Context, tree: Tree): Unit = { + if (!(from <:< to) && !(from == SCALA_ALLREF && to == SCALA_ALL)) { + to match { + case UNIT => + ctx.bb.emit(DROP(from), tree.pos); + if (settings.debug.value) + log("Dropped an " + from); + + case _ => + assert(from != UNIT, "Can't convert from UNIT to " + to + + tree + " at: " + unit.position(tree.pos)); + ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), tree.pos); + } + } else if (from == SCALA_ALL) { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(getZeroOf(ctx.method.returnType)); + ctx.bb.emit(RETURN(ctx.method.returnType)); + ctx.bb.enterIgnoreMode; + } else if (from == SCALA_ALLREF) { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(CONSTANT(Constant(null))); + } + } + + /** Load the qualifier of `tree' on top of the stack. */ + private def genLoadQualifier(tree: Tree, ctx: Context): Context = + tree match { + case Select(qualifier, _) => + genLoad(qualifier, ctx, ANY_REF_CLASS); // !! + case _ => + abort("Unknown qualifier " + tree); + } + + /** Is this symbol static in the Java sense? */ + def isStaticSymbol(s: Symbol): Boolean = + s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass; + + /** + * Generate code that loads args into label parameters. + */ + private def genLoadLabelArguments(args: List[Tree], label: Label, ctx: Context): Context = { + assert(args.length == label.params.length, + "Wrong number of arguments in call to label " + label.symbol); + var ctx1 = ctx; + var arg = args; + var param = label.params; + + // store arguments in reverse order on the stack + while (arg != Nil) { + val Some(l) = ctx.method.lookupLocal(param.head); + ctx1 = genLoad(arg.head, ctx1, l.kind); + arg = arg.tail; + param = param.tail; + } + + // store arguments in the right variables + arg = args.reverse; param = label.params.reverse; + while (arg != Nil) { + val Some(l) = ctx.method.lookupLocal(param.head); + ctx1.bb.emit(STORE_LOCAL(l, param.head.isValueParameter), arg.head.pos); + arg = arg.tail; + param = param.tail; + } + + ctx1 + } + + private def genLoadArguments(args: List[Tree], tpes: List[Type], ctx: Context): Context = { + assert(args.length == tpes.length, "Wrong number of arguments in call " + ctx); + + var ctx1 = ctx; + var arg = args; + var tpe = tpes; + while (arg != Nil) { + ctx1 = genLoad(arg.head, ctx1, toTypeKind(tpe.head)); + arg = arg.tail; + tpe = tpe.tail; + } + ctx1 + } + + def genConversion(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = { + if (cast) + ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to))); + else { + ctx.bb.emit(DROP(from)); + ctx.bb.emit(CONSTANT(Constant(from == to))); + } + } + + /** Generate a conversion from a reference type to a value type, like in + * Any -> Array[Int] or Any -> Int + */ + def genBoxedConversion(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = { + assert(to.isValueType || to.isArrayType, "Expecting conversion to value type: " + to); + + val boxedCls = to match { + case ARRAY(ARRAY(_)) | ARRAY(REFERENCE(_)) => + definitions.BoxedObjectArrayClass; + case ARRAY(elem) => + definitions.boxedArrayClass(elem.toType.symbol) + case _ => + definitions.boxedClass(to.toType.symbol); + } + + if (cast) { + ctx.bb.emit(CHECK_CAST(REFERENCE(boxedCls))); + ctx.bb.emit(CONSTANT(Constant(definitions.signature(to.toType)))); + ctx.bb.emit(CALL_METHOD(definitions.getMember(boxedCls, "unbox"), + Dynamic)); + } else { + ctx.bb.emit(IS_INSTANCE(REFERENCE(boxedCls))); + } + } + + def genCast(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = { + if (cast) + ctx.bb.emit(CHECK_CAST(to)); + else + ctx.bb.emit(IS_INSTANCE(to)); + } + + def zeroOf(k: TypeKind): Tree = k match { + case UNIT => Literal(()); + case BOOL => Literal(false); + case BYTE => Literal(0: Byte); + case SHORT => Literal(0: Short); + case CHAR => Literal(0: Char); + case INT => Literal(0: Int); + case LONG => Literal(0: Long); + case FLOAT => Literal(0.0f); + case DOUBLE => Literal(0.0d); + case REFERENCE(cls) => Literal(null: Any); + case ARRAY(elem) => Literal(null: Any); + } + + def getZeroOf(k: TypeKind): Instruction = k match { + case UNIT => CONSTANT(Constant(())); + case BOOL => CONSTANT(Constant(false)); + case BYTE => CONSTANT(Constant(0: Byte)); + case SHORT => CONSTANT(Constant(0: Short)); + case CHAR => CONSTANT(Constant(0: Char)); + case INT => CONSTANT(Constant(0: Int)); + case LONG => CONSTANT(Constant(0: Long)); + case FLOAT => CONSTANT(Constant(0.0f)); + case DOUBLE => CONSTANT(Constant(0.0d)); + case REFERENCE(cls) => CONSTANT(Constant(null: Any)); + case ARRAY(elem) => CONSTANT(Constant(null: Any)); + } + + + /** Is the given symbol a primitive operation? */ + def isPrimitive(fun: Symbol): Boolean = { + import scalaPrimitives._; + + if (scalaPrimitives.isPrimitive(fun)) + scalaPrimitives.getPrimitive(fun) match { + case EQUALS | HASHCODE | TOSTRING => false; + case _ => true; + } + else + false; + } + + def genCoercion(tree: Tree, ctx: Context, code: Int) = { + import scalaPrimitives._; + code match { + case B2B => (); + case B2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, CHAR)), tree.pos); + case B2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, SHORT)), tree.pos); + case B2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, INT)), tree.pos); + case B2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, LONG)), tree.pos); + case B2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, FLOAT)), tree.pos); + case B2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, DOUBLE)), tree.pos); + + case S2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, BYTE)), tree.pos); + case S2S => (); + case S2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, CHAR)), tree.pos); + case S2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, INT)), tree.pos); + case S2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, LONG)), tree.pos); + case S2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, FLOAT)), tree.pos); + case S2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, DOUBLE)), tree.pos); + + case C2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, BYTE)), tree.pos); + case C2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, SHORT)), tree.pos); + case C2C => (); + case C2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, INT)), tree.pos); + case C2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, LONG)), tree.pos); + case C2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, FLOAT)), tree.pos); + case C2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, DOUBLE)), tree.pos); + + case I2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, BYTE)), tree.pos); + case I2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, SHORT)), tree.pos); + case I2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, CHAR)), tree.pos); + case I2I => (); + case I2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, LONG)), tree.pos); + case I2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, FLOAT)), tree.pos); + case I2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, DOUBLE)), tree.pos); + + case L2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, BYTE)), tree.pos); + case L2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, SHORT)), tree.pos); + case L2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, CHAR)), tree.pos); + case L2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, INT)), tree.pos); + case L2L => (); + case L2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, FLOAT)), tree.pos); + case L2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, DOUBLE)), tree.pos); + + case F2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, BYTE)), tree.pos); + case F2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, SHORT)), tree.pos); + case F2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, CHAR)), tree.pos); + case F2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, INT)), tree.pos); + case F2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, LONG)), tree.pos); + case F2F => (); + case F2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, DOUBLE)), tree.pos); + + case D2B => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, BYTE)), tree.pos); + case D2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, SHORT)), tree.pos); + case D2C => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, CHAR)), tree.pos); + case D2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, INT)), tree.pos); + case D2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, LONG)), tree.pos); + case D2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(DOUBLE, FLOAT)), tree.pos); + case D2D => (); + + case _ => abort("Unknown coercion primitive: " + code); + } + } + + /** Generate string concatenation. */ + def genStringConcat(tree: Tree, ctx: Context): Context = { + val Apply(Select(larg, _), rarg) = tree; + var ctx1 = ctx; + + assert(rarg.length == 1, + "Too many parameters for string concatenation"); + + val concatenations = liftStringConcat(tree); + if (settings.debug.value) + log("Lifted string concatenations for " + tree + "\n to: " + concatenations); + + ctx1.bb.emit(CALL_PRIMITIVE(StartConcat), tree.pos); + for (val elem <- concatenations) { + val kind = toTypeKind(elem.tpe); + ctx1 = genLoad(elem, ctx1, kind); + ctx1.bb.emit(CALL_PRIMITIVE(StringConcat(kind)), elem.pos); + } + ctx1.bb.emit(CALL_PRIMITIVE(EndConcat), tree.pos); + + ctx1; + } + + /** + * Returns a list of trees that each should be concatenated, from + * left to right. It turns a chained call like "a".+("b").+("c") into + * a list of arguments. + */ + def liftStringConcat(tree: Tree): List[Tree] = tree match { + case Apply(fun @ Select(larg, method), rarg) => + if (isPrimitive(fun.symbol) && + scalaPrimitives.getPrimitive(fun.symbol) == scalaPrimitives.CONCAT) + liftStringConcat(larg) ::: rarg + else + List(tree); + case _ => + List(tree); + } + + + /** + * Traverse the tree and store label stubs in the contxt. This is + * necessary to handle forward jumps, because at a label application + * with arguments, the symbols of the corresponding LabelDef parameters + * are not yet known. + * + * Since it is expensive to traverse each method twice, this method is called + * only when forward jumps really happen, and then it re-traverses the whole + * method, scanning for LabelDefs. + * + * TODO: restrict the scanning to smaller subtrees than the whole method. + * It is sufficient to scan the trees of the innermost enclosing block. + */ + private def scanForLabels(tree: Tree, ctx: Context): Unit = + new Traverser() { + override def traverse(tree: Tree): Unit = tree match { + + case LabelDef(name, params, rhs) => + if (!ctx.labels.contains(tree.symbol)) { + ctx.labels += tree.symbol -> (new Label(tree.symbol) setParams(params map (.symbol))); + ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info)))); + } + super.traverse(rhs); + + case _ => super.traverse(tree); + } + } traverse(tree); + + /** + * Generate code for conditional expressions. The two basic blocks + * represent the continuation in case of success/failure of the + * test. + */ + private def genCond(tree: Tree, + ctx: Context, + thenCtx: Context, + elseCtx: Context): Unit = + { + def genComparisonOp(l: Tree, r: Tree, code: Int): Unit = { + val op: TestOp = code match { + case scalaPrimitives.LT => LT; + case scalaPrimitives.LE => LE; + case scalaPrimitives.GT => GT; + case scalaPrimitives.GE => GE; + case scalaPrimitives.ID | scalaPrimitives.EQ => EQ; + case scalaPrimitives.NI | scalaPrimitives.NE => NE; + + case _ => abort("Unknown comparison primitive: " + code); + }; + + val kind = getMaxType(l.tpe :: r.tpe :: Nil); + var ctx1 = genLoad(l, ctx, kind); + ctx1 = genLoad(r, ctx1, kind); + ctx1.bb.emit(CJUMP(thenCtx.bb, elseCtx.bb, op, kind), r.pos); + ctx1.bb.close; + } + + if (settings.debug.value) + log("Entering genCond with tree: " + tree); + + tree match { + case Apply(fun, args) + if isPrimitive(fun.symbol) => + assert(args.length <= 1, + "Too many arguments for primitive function: " + fun.symbol); + val code = scalaPrimitives.getPrimitive(fun.symbol); + + if (code == scalaPrimitives.ZNOT) { + val Select(leftArg, _) = fun; + genCond(leftArg, ctx, elseCtx, thenCtx); + } else if ((code == scalaPrimitives.EQ || + code == scalaPrimitives.NE)) { + val Select(leftArg, _) = fun; + if (toTypeKind(leftArg.tpe).isReferenceType) { + if (code == scalaPrimitives.EQ) + genEqEqPrimitive(leftArg, args.head, ctx, thenCtx, elseCtx); + else + genEqEqPrimitive(leftArg, args.head, ctx, elseCtx, thenCtx); + } + else + genComparisonOp(leftArg, args.head, code); + } else if (scalaPrimitives.isComparisonOp(code)) { + val Select(leftArg, _) = fun; + genComparisonOp(leftArg, args.head, code); + } else { + code match { + case scalaPrimitives.ZAND => + val Select(leftArg, _) = fun; + + val ctxInterm = ctx.newBlock; + genCond(leftArg, ctx, ctxInterm, elseCtx); + genCond(args.head, ctxInterm, thenCtx, elseCtx); + + case scalaPrimitives.ZOR => + val Select(leftArg, _) = fun; + + val ctxInterm = ctx.newBlock; + genCond(leftArg, ctx, thenCtx, ctxInterm); + genCond(args.head, ctxInterm, thenCtx, elseCtx); + + case _ => + var ctx1 = genLoad(tree, ctx, BOOL); + ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL), tree.pos); + ctx1.bb.close; + } + } + + case _ => + var ctx1 = genLoad(tree, ctx, BOOL); + ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL), tree.pos); + ctx1.bb.close; + } + } + + val eqEqTemp: Name = "eqEqTemp$"; + + + /** + * Generate the "==" code for object references. It is equivalent of + * if (l == null) then r == null else l.equals(r); + */ + def genEqEqPrimitive(l: Tree, r: Tree, ctx: Context, thenCtx: Context, elseCtx: Context): Unit = { + var eqEqTempVar: Symbol = null; + var eqEqTempLocal: Local = null; + + ctx.method.lookupLocal(eqEqTemp) match { + case Some(local) => eqEqTempVar = local.sym; eqEqTempLocal = local; + case None => + eqEqTempVar = ctx.method.symbol.newVariable(l.pos, eqEqTemp); + eqEqTempVar.setInfo(definitions.AnyRefClass.typeConstructor); + eqEqTempLocal = new Local(eqEqTempVar, REFERENCE(definitions.AnyRefClass)); + ctx.method.addLocal(eqEqTempLocal); + } + + var ctx1 = genLoad(l, ctx, ANY_REF_CLASS); + ctx1 = genLoad(r, ctx1, ANY_REF_CLASS); + val tmpNullCtx = ctx1.newBlock; + val tmpNonNullCtx = ctx1.newBlock; + ctx1.bb.emit(STORE_LOCAL(eqEqTempLocal, false), l.pos); + ctx1.bb.emit(DUP(ANY_REF_CLASS)); + ctx1.bb.emit(CZJUMP(tmpNullCtx.bb, tmpNonNullCtx.bb, EQ, ANY_REF_CLASS)); + ctx1.bb.close; + + tmpNullCtx.bb.emit(DROP(ANY_REF_CLASS), l.pos); // type of AnyRef + tmpNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal, false)); + tmpNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, EQ, ANY_REF_CLASS)); + tmpNullCtx.bb.close; + + tmpNonNullCtx.bb.emit(LOAD_LOCAL(eqEqTempLocal, false), l.pos); + tmpNonNullCtx.bb.emit(CALL_METHOD(definitions.Object_equals, Dynamic)); + tmpNonNullCtx.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)); + tmpNonNullCtx.bb.close; + } + + /** + * Add all fields of the given class symbol to the current ICode + * class. + */ + private def addClassFields(ctx: Context, cls: Symbol): Unit = { + assert(ctx.clazz.symbol eq cls, + "Classes are not the same: " + ctx.clazz.symbol + ", " + cls); + + for (val f <- cls.info.decls.elements) + if (!f.isMethod && f.isTerm) + ctx.clazz.addField(new IField(f)); + } + + /** + * Add parameters to the current ICode method. It is assumed the methods + * have been uncurried, so the list of lists contains just one list. + */ + private def addMethodParams(ctx: Context, vparamss: List[List[ValDef]]): Unit = + vparamss match { + case Nil => () + + case vparams :: Nil => + for (val p <- vparams) + ctx.method.addParam(new Local(p.symbol, toTypeKind(p.symbol.info))); + ctx.method.params = ctx.method.params.reverse; + + case _ => + abort("Malformed parameter list: " + vparamss); + } + + /** + * If the block consists of a single unconditional jump, prune + * it by replacing the instructions in the predecessor to jump + * directly to the JUMP target of the block. + */ + def prune(method: IMethod) = { + var changed = false; + var n = 0; + + def prune0(block: BasicBlock): Unit = { + val optCont = block.lastInstruction match { + case JUMP(b) if (b != block) => Some(b); + case _ => None + } + if (block.size == 1 && optCont != None) { + val Some(cont) = optCont; + val pred = block.predecessors; + log("Preds: " + pred + " of " + block); + pred foreach { p => + p.lastInstruction match { + case CJUMP(succ, fail, cond, kind) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + if (block == succ) + CJUMP(cont, fail, cond, kind) + else if (block == fail) + CJUMP(succ, cont, cond, kind) + else + abort("Could not find block in preds")); + + case CZJUMP(succ, fail, cond, kind) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + if (block == succ) + CZJUMP(cont, fail, cond, kind) + else if (block == fail) + CZJUMP(succ, cont, cond, kind) + else + abort("Could not find block in preds")); + + case JUMP(b) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, JUMP(cont)); + + case SWITCH(tags, labels) => + if (settings.debug.value) + log("Pruning empty if branch."); + changed = true; + p.replaceInstruction(p.lastInstruction, + SWITCH(tags, labels map (l => if (l == block) cont else l))); + } + } + if (changed) + method.code.removeBlock(block); + } + } + + do { + changed = false; + n = n + 1; + method.code traverse prune0; + } while (changed); + + if (settings.debug.value) + log("Prune fixpoint reached in " + n + " iterations."); + } + + def getMaxType(ts: List[Type]): TypeKind = { + def maxType(a: TypeKind, b: TypeKind): TypeKind = + a maxType b; + + val kinds = ts map toTypeKind; + kinds reduceLeft maxType; + } + + /** Check weather a given label definition is introduced by the tail call phase + * It is considered to be so if all value parameters of the label are the + * same as the value parameters of the current method. + */ + def isTailCallLabel(tree: LabelDef, ctx: Context) = ( + tree.params.length == ctx.defdef.vparamss.head && + List.forall2(tree.params, ctx.defdef.vparamss.head) + { (x, y) => x.symbol == y.symbol } + ); + + + /////////////////////// Context //////////////////////////////// + + + /** + * The Context class keeps information relative to the current state + * in code generation + */ + class Context { + + /** The current package. */ + var packg: Name = _; + + /** The current class. */ + var clazz: IClass = _; + + /** The current method. */ + var method: IMethod = _; + + /** The current basic block. */ + var bb: BasicBlock = _; + + /** Map from label symbols to label objects. */ + var labels: HashMap[Symbol, Label] = new HashMap(); + + /** Current method definition. */ + var defdef: DefDef = _; + + /** current exception handlers */ + var handlers: List[ExceptionHandler] = Nil; + + var handlerCount = 0; + + override def toString(): String = { + val buf = new StringBuffer(); + buf.append("\tpackage: ").append(packg).append('\n'); + buf.append("\tclazz: ").append(clazz).append('\n'); + buf.append("\tmethod: ").append(method).append('\n'); + buf.append("\tbb: ").append(bb).append('\n'); + buf.append("\tlabels: ").append(labels).append('\n'); + buf.append("\texception handlers: ").append(handlers).append('\n'); + buf.toString() + } + + + def this(other: Context) = { + this(); + this.packg = other.packg; + this.clazz = other.clazz; + this.method = other.method; + this.bb = other.bb; + this.labels = other.labels; + this.defdef = other.defdef; + this.handlers = other.handlers; + this.handlerCount = other.handlerCount; + } + + def setPackage(p: Name): this.type = { + this.packg = p; + this + } + + def setClass(c: IClass): this.type = { + this.clazz = c; + this + } + + def setMethod(m: IMethod): this.type = { + this.method = m; + this + } + + def setBasicBlock(b: BasicBlock): this.type = { + this.bb = b; + this + } + + /** Prepare a new context upon entry into a method */ + def enterMethod(m: IMethod, d: DefDef): Context = { + val ctx1 = new Context(this) setMethod(m); + ctx1.labels = new HashMap(); + ctx1.method.code = new Code(m.symbol.simpleName.toString()); + ctx1.bb = ctx1.method.code.startBlock; + ctx1.defdef = d; + ctx1 + } + + /** Return a new context for a new basic block. */ + def newBlock: Context = { + val block = method.code.newBlock; + handlers foreach (h => h addBlock block); + new Context(this) setBasicBlock block; + } + + /** Create a new exception handler and adds it in the list + * of current exception handlers. + */ + def newHandler(cls: Symbol): ExceptionHandler = { + handlerCount = handlerCount + 1; + val exh = new ExceptionHandler(method, "" + handlerCount, cls); + method.addHandler(exh); + handlers = exh :: handlers; + if (settings.debug.value) + log("added handler: " + exh); + + exh + } + + /** Return a new context for generating code for the given + * exception handler. + */ + def enterHandler(exh: ExceptionHandler): Context = { + val ctx = newBlock; + exh.setStartBlock(ctx.bb); + ctx + } + + def exitHandler(exh: ExceptionHandler): Unit = { + assert(handlerCount > 0 && handlers.head == exh, + "Wrong nesting of exception handlers." + this + " for " + exh); + handlerCount = handlerCount - 1; + handlers = handlers.tail; + if (settings.debug.value) + log("removed handler: " + exh); + + } + + /** Clone the current context */ + def dup: Context = new Context(this); + + /** + * Generate exception handlers for the body. Body is evaluated + * with a context where all the handlers are active. Handlers are + * evaluated in the 'outer' context. + * + * It returns the resulting context, with the same active handlers as + * before the call. Use it like: + * + * <code> ctx.Try( ctx => { + * ctx.bb.emit(...) // protected block + * }, Pair(definitions.ThrowableClass, + * ctx => { + * ctx.bb.emit(...); // exception handler + * }), Pair(AnotherExceptionClass, + * ctx => {... + * } ))</code> + */ + def Try(body: Context => Context, + handlers: List[Pair[Symbol, (Context => Context)]]) = { + val outerCtx = this.dup; + val afterCtx = outerCtx.newBlock; + + val exhs = handlers.map { handler => + val exh = this.newHandler(handler._1); + val ctx1 = handler._2(outerCtx.enterHandler(exh)); + ctx1.bb.emit(JUMP(afterCtx.bb)); + ctx1.bb.close; + exh + } + val bodyCtx = this.newBlock; + + val finalCtx = body(bodyCtx); + + outerCtx.bb.emit(JUMP(bodyCtx.bb)); + outerCtx.bb.close; + + exhs.reverse foreach finalCtx.exitHandler; + + finalCtx.bb.emit(JUMP(afterCtx.bb)); + finalCtx.bb.close; + + afterCtx + } + } + } + + /** + * Represent a label in the current method code. In order + * to support forward jumps, labels can be created without + * having a deisgnated target block. They can later be attached + * by calling `anchor'. + */ + class Label(val symbol: Symbol) { + var anchored = false; + var block: BasicBlock = _; + var params: List[Symbol] = _; + + private var toPatch: List[Instruction] = Nil; + + /** Fix this label to the given basic block. */ + def anchor(b: BasicBlock): Label = { + assert(!anchored, "Cannot anchor an already anchored label!"); + anchored = true; + this.block = b; + this + } + + def setParams(p: List[Symbol]): Label = { + assert(params == null, "Cannot set label parameters twice!"); + params = p; + this + } + + /** Add an instruction that refers to this label. */ + def addCallingInstruction(i: Instruction) = + toPatch = i :: toPatch; + + /** + * Patch the code by replacing pseudo call instructions with + * jumps to the given basic block. + */ + def patch(code: Code): Unit = { + def substMap: Map[Instruction, Instruction] = { + val map = new HashMap[Instruction, Instruction](); + + toPatch foreach (i => map += i -> patch(i)); + map + } + + val map = substMap; + code traverse (.subst(map)); + } + + /** + * Return the patched instruction. If the given instruction + * jumps to this label, replace it with the basic block. Otherwise, + * return the same instruction. Conditional jumps have more than one + * label, so they are replaced only if all labels are anchored. + */ + def patch(instr: Instruction): Instruction = { + assert(anchored, "Cannot patch until this label is anchored: " + this); + + instr match { + case PJUMP(self) + if (self == this) => JUMP(block); + + case PCJUMP(self, failure, cond, kind) + if (self == this && failure.anchored) => + CJUMP(block, failure.block, cond, kind); + + case PCJUMP(success, self, cond, kind) + if (self == this && success.anchored) => + CJUMP(success.block, block, cond, kind); + + case PCZJUMP(self, failure, cond, kind) + if (self == this && failure.anchored) => + CZJUMP(block, failure.block, cond, kind); + + case PCZJUMP(success, self, cond, kind) + if (self == this && success.anchored) => + CZJUMP(success.block, block, cond, kind); + + case _ => instr; + } + } + } + + ///////////////// Fake instructions ////////////////////////// + + /** + * Pseudo jump: it takes a Label instead of a basick block. + * It is used temporarily during code generation. It is replaced + * by a real JUMP instruction when all labels are resolved. + */ + abstract class PseudoJUMP(label: Label) extends Instruction { + override def toString(): String ="PJUMP " + label.symbol.simpleName; + + override def consumed = 0; + override def produced = 0; + + // register with the given label + if (!label.anchored) + label.addCallingInstruction(this); + } + + case class PJUMP(where: Label) extends PseudoJUMP(where); + + case class PCJUMP(success: Label, failure: Label, cond: TestOp, kind: TypeKind) + extends PseudoJUMP(success) { + + if (!failure.anchored) + failure.addCallingInstruction(this); + } + + case class PCZJUMP(success: Label, failure: Label, cond: TestOp, kind: TypeKind) + extends PseudoJUMP(success) { + + if (!failure.anchored) + failure.addCallingInstruction(this); + } + +} + |