summaryrefslogtreecommitdiff
path: root/sources
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2005-07-25 09:30:48 +0000
committerIulian Dragos <jaguarul@gmail.com>2005-07-25 09:30:48 +0000
commit5cf7d39061bdf671311c17ba139186af53a1d5aa (patch)
tree46e5a5f878de5c992db6aaa67fc23fbfa9d17759 /sources
parent3e90b7175a58a1b50801b3ad7cf0bb22da2146b2 (diff)
downloadscala-5cf7d39061bdf671311c17ba139186af53a1d5aa.tar.gz
scala-5cf7d39061bdf671311c17ba139186af53a1d5aa.tar.bz2
scala-5cf7d39061bdf671311c17ba139186af53a1d5aa.zip
Added experimental support for ICode.
Diffstat (limited to 'sources')
-rwxr-xr-xsources/scala/tools/nsc/Global.scala26
-rw-r--r--sources/scala/tools/nsc/Settings.scala1
-rw-r--r--sources/scala/tools/nsc/backend/icode/BasicBlocks.scala169
-rw-r--r--sources/scala/tools/nsc/backend/icode/GenICode.scala638
-rw-r--r--sources/scala/tools/nsc/backend/icode/ICodes.scala22
-rw-r--r--sources/scala/tools/nsc/backend/icode/Members.scala166
-rw-r--r--sources/scala/tools/nsc/backend/icode/Opcodes.scala419
-rw-r--r--sources/scala/tools/nsc/backend/icode/Primitives.scala234
-rw-r--r--sources/scala/tools/nsc/backend/icode/Printers.scala97
-rw-r--r--sources/scala/tools/nsc/backend/icode/TypeKind.scala79
-rw-r--r--sources/scala/tools/nsc/backend/icode/TypeStacks.scala131
11 files changed, 1981 insertions, 1 deletions
diff --git a/sources/scala/tools/nsc/Global.scala b/sources/scala/tools/nsc/Global.scala
index 06898a1297..4d63ec2a78 100755
--- a/sources/scala/tools/nsc/Global.scala
+++ b/sources/scala/tools/nsc/Global.scala
@@ -18,8 +18,12 @@ import ast.parser._;
import typechecker._;
import matching.TransMatcher;
import transform._;
+import backend.icode.{ICodes, GenICode};
-class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable with Trees with CompilationUnits {
+class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
+ with Trees
+ with CompilationUnits
+ with ICodes {
// sub-components --------------------------------------------------
@@ -64,6 +68,8 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
def log(msg: Object): unit =
if (settings.log contains phase.name) inform("[log " + phase + "] " + msg);
+ def abort(msg: String) = throw new Error(msg);
+
// file interface -------------------------------------------------------
private val reader: SourceReader = {
@@ -167,6 +173,14 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
val global: Global.this.type = Global.this;
}
+ object genicode extends GenICode {
+ val global: Global.this.type = Global.this;
+ }
+
+ object icodePrinter extends backend.icode.Printers {
+ val global: Global.this.type = Global.this;
+ }
+
def phaseDescriptors: List[SubComponent] = List(
analyzer.namerFactory, // needs to be first
analyzer.typerFactory, // needs to be second
@@ -193,6 +207,10 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
}
}
+ // temporary: icode is turned on explicitely
+ if (settings.Xshowicode.value)
+ p = genicode.newPhase(p);
+
val terminalPhase = new GlobalPhase(p) {
def name = "terminal";
def apply(unit: CompilationUnit): unit = {}
@@ -256,6 +274,7 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
}
if (settings.Xshowcls.value != "") showDef(newTermName(settings.Xshowcls.value), false);
if (settings.Xshowobj.value != "") showDef(newTermName(settings.Xshowobj.value), true);
+ if (settings.Xshowicode.value) printICode();
if (reporter.errors() == 0) {
for (val Pair(sym, pickled) <- symData.elements.toList) {
@@ -351,6 +370,11 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
}
}
+ private def printICode(): Unit = {
+ val printer = new icodePrinter.TextPrinter(new PrintWriter(System.out, true));
+ classes.foreach(printer.printClass);
+ }
+
private def informStatistics = {
inform("#identifiers : " + analyzer.idcnt);
inform("#selections : " + analyzer.selcnt);
diff --git a/sources/scala/tools/nsc/Settings.scala b/sources/scala/tools/nsc/Settings.scala
index e9bb2cbea8..e8496fc683 100644
--- a/sources/scala/tools/nsc/Settings.scala
+++ b/sources/scala/tools/nsc/Settings.scala
@@ -47,6 +47,7 @@ class Settings(error: String => unit) {
val Xshowcls = StringSetting ("-Xshowcls", "class", "Show class info", "");
val Xshowobj = StringSetting ("-Xshowobj", "object", "Show object info", "");
+ val Xshowicode = BooleanSetting("-Xshowicode", "Print the generated ICode");
/** A list of all settings */
def allSettings: List[Setting] = allsettings.reverse;
diff --git a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
new file mode 100644
index 0000000000..ffe9d7cd33
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
@@ -0,0 +1,169 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+// $Id$
+
+package scala.tools.nsc.backend.icode;
+
+import scala.tools.nsc.ast._;
+import scala.collection.mutable.Map;
+
+trait BasicBlocks: Global {
+ import opcodes._;
+
+ /** This class represents a basic block. Each
+ * basic block contains a list of instructions that are
+ * either executed all, or none. No jumps
+ * to/from the "middle" of the basic block are allowed.
+ */
+ class BasicBlock (theLabel: int) {
+
+ /** The type stack at the begining of the block */
+ var initialStack : TypeStack = null;
+
+ /** The label of the block */
+ val label = theLabel;
+
+ /** The substitute variables of the block
+ * in the case of a recursive method */
+ var substituteVars : List[Symbol] = null;
+
+ /** The stack at the end of the block */
+ var endStack : TypeStack = null;
+
+
+ /** ICode instructions */
+ private var instructionList: List[Instruction] = Nil;
+
+ private var lastInstruction: Instruction = null;
+
+ private var closed: boolean = false;
+
+ private var instrs: Array[Instruction] = _;
+
+ // public:
+
+ /** Compute an hashCode for the block */
+ override def hashCode() = label;
+
+ /** Apply a function to all the instructions of the block*/
+ def traverse(f: Instruction => unit) =
+ instructionList.reverse.foreach(f);
+
+ /** The number of instructions in this basic block so far. */
+ def size: Int = instructionList.length;
+
+ /** Initialize the stack of the block, must be done before evaluation
+ * the type stack */
+ def initStack(stack : TypeStack) = {
+ if (initialStack == null) {
+ initialStack = stack;
+ endStack = null;
+ }
+ }
+
+ ///////////////////// Substitutions ///////////////////////
+
+ /**
+ * Replace the instruction at the given position. Used by labels when
+ * they are anchored.
+ */
+ def replaceInstruction(pos: Int, instr: Instruction): Boolean = {
+ assert(closed, "Instructions can be replaced only after the basic block is closed");
+
+ instrs(pos) = instr;
+ true
+ }
+
+ /**
+ * Replace the given instruction with the new one.
+ * Returns `true' if it actually changed something.
+ */
+ def replaceInstruction(oldInstr: Instruction, newInstr: Instruction): Boolean = {
+ assert(closed, "Instructions can be replaced only after the basic block is closed");
+
+ var i = 0;
+ var changed = false;
+ while (i < instrs.length) {
+ if (instrs(i) == oldInstr) {
+ instrs(i) = newInstr;
+ changed = true;
+ }
+ i = i + 1;
+ }
+ changed
+ }
+
+ /** Replace all instructions found in the map. */
+ def subst(map: Map[Instruction, Instruction]) = {
+ var i = 0;
+ while (i < instrs.length) {
+ map get instrs(i) match {
+ case Some(instr) => replaceInstruction(i, instr);
+ case None => ();
+ }
+ i = i + 1;
+ }
+ }
+
+ /** Add a new instruction at the end of the block */
+ def emit(instr: Instruction) = {
+ assert (!closed, "BasicBlock closed.");
+ instructionList = instr :: instructionList;
+ lastInstruction = instr;
+ }
+
+ /** Compute the type stack of the block */
+ def typeBlock = {
+ assert(initialStack != null, "Stack not initialized");
+ endStack = initialStack;
+ traverse((ic : Instruction) => endStack = endStack.eval(ic));
+ }
+
+ /** Close the block */
+ def close = {
+ closed = true;
+ instrs = toInstructionArray(instructionList);
+ }
+
+ /** Convert the list to an array */
+ def toInstructionArray(l: List[Instruction]): Array[Instruction] = {
+ var array = new Array[Instruction](l.length);
+ var i: Int = 0;
+
+ l foreach (x => { array(i) = x; i = i + 1 });
+ array
+ }
+
+ def isClosed = closed;
+
+ def getLastInstruction = lastInstruction;
+
+ def successors : List[BasicBlock] = // here order will count
+ lastInstruction match {
+ case JUMP (where) => List(where);
+ case CJUMP(success, failure, _) => failure::success::Nil;
+ case CZJUMP(success, failure, _) => failure::success::Nil;
+ case SWITCH(_,labels) => labels;
+ case RETURN() => Nil;
+ case _ =>
+ abort("The last instruction is not a control flow instruction");
+ }
+
+ // Instead of it, rather use a printer
+ def print() : unit = print(System.out);
+
+ def print(out: java.io.PrintStream) : unit = {
+ out.println("block #"+label+" :");
+ instructionList.reverse.foreach(
+ (i: Instruction) => out.println(" "+i));
+ out.print("Successors: ");
+ successors.foreach((x: BasicBlock) => out.print(" "+x.label.toString()));
+ out.println();
+ }
+
+ }
+
+}
diff --git a/sources/scala/tools/nsc/backend/icode/GenICode.scala b/sources/scala/tools/nsc/backend/icode/GenICode.scala
new file mode 100644
index 0000000000..727202ca72
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/GenICode.scala
@@ -0,0 +1,638 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+// $Id$
+
+package scala.tools.nsc.backend.icode;
+
+import scala.collection.mutable.{Map, HashMap};
+
+abstract class GenICode extends SubComponent {
+ import global._;
+ import opcodes._;
+
+ val phaseName = "genicode";
+
+ override def newPhase(prev: Phase) = new ICodePhase(prev);
+
+ class ICodePhase(prev: Phase) extends StdPhase(prev) {
+ import Primitives._;
+
+ override def description = "Generate ICode from the AST";
+
+ var unit: CompilationUnit = _;
+
+ /** A map from scala primitive Types to ICode TypeKinds */
+ private var primitiveTypeMap: Map[Symbol, TypeKind] = null;
+
+ private var primitiveMethods: Map[Symbol, Primitive] = null;
+
+ initPrimitiveTypeMap;
+
+ override def apply(unit: CompilationUnit): Unit = {
+ this.unit = 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
+ }
+
+ 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) =>
+ 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) =>
+ ctx setClass (new IClass(tree.symbol) setCompilationUnit unit);
+ 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) =>
+ log("Entering method " + name);
+ val m = new IMethod(tree.symbol);
+ ctx.clazz.addMethod(m);
+ var ctx1 = ctx.enterMethod(m, tree.asInstanceOf[DefDef]);
+ addMethodParams(ctx1, vparamss);
+ ctx1 = genLoad(rhs,
+ ctx1,
+ toTypeKind(ctx1.method.symbol.info.resultType));
+ ctx1.bb.emit(RETURN());
+ ctx1;
+
+ case Template(parents, body) =>
+ gen(body, ctx);
+
+ case Block(stats, expr) =>
+ abort("Block is not allowed in gen " + tree);
+
+
+/* case AbsTypeDef(mods, name, lo, hi) => (eliminated by erasure)
+ case AliasTypeDef(mods, name, tparams, rhs) => (eliminated by erasure)
+ case Import(expr, selectors) => (eliminated by typecheck)
+ case Attributed(attribute, definition) => (eliminated by typecheck)
+ case DocDef(comment, definition) => (eliminated by typecheck)
+ case CaseDef(pat, guard, body) => (eliminated by transmatch)
+ case Sequence(trees) => (eliminated by transmatch)
+ case Alternative(trees) => (eliminated by transmatch)
+ case Star(elem) => (eliminated by transmatch)
+ case Bind(name, body) => (eliminated by transmatch)
+ case ArrayValue(elemtpt, trees) => (introduced by uncurry)
+ case Function(vparams, body) => (eliminated by typecheck)
+ case Typed(expr, tpt) => (eliminated by erasure)
+ case TypeApply(fun, args) =>
+ case SingletonTypeTree(ref) => (eliminated by typecheck)
+ case SelectFromTypeTree(qualifier, selector) => (eliminated by typecheck)
+ case CompoundTypeTree(templ: Template) => (eliminated by typecheck)
+ case AppliedTypeTree(tpt, args) => (eliminated by typecheck)
+*/
+ }
+
+ 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 (lhs.symbol.isStatic) {
+ val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info));
+ ctx1.bb.emit(STORE_FIELD(lhs.symbol, true));
+ ctx1
+ } else {
+ var ctx1 = genLoadQualifier(lhs, ctx);
+ ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info));
+ ctx1.bb.emit(STORE_FIELD(lhs.symbol, false));
+ ctx1
+ }
+
+ case Assign(lhs, rhs) =>
+ assert(ctx.method.locals.contains(lhs.symbol),
+ "Assignment to inexistent local: " + lhs.symbol);
+ val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info));
+ ctx1.bb.emit(STORE_LOCAL(lhs.symbol, lhs.symbol.isValueParameter));
+ ctx1
+
+ case _ =>
+ log("Passing " + tree + " to genLoad");
+ genLoad(tree, ctx, UNIT);
+ }
+
+ private def genLoad(tree: Tree, ctx: Context, expectedType: TypeKind): Context = {
+ tree match {
+ case LabelDef(name, params, rhs) =>
+ ctx.method.addLocals(params map (.symbol));
+ val ctx1 = ctx.newBlock;
+ ctx1.labels.get(tree.symbol) match {
+ case Some(label) => label.anchor(ctx1.bb);
+
+ case None =>
+ ctx.labels += tree.symbol -> (new Label(tree.symbol) anchor ctx1.bb setParams (params map (.symbol)));
+ }
+ genLoad(rhs, ctx1, toTypeKind(tree.symbol.info.resultType));
+
+ case ValDef(_, _, _, rhs) =>
+ val sym = tree.symbol;
+ ctx.method.addLocal(sym);
+ val ctx1 = genLoad(rhs, ctx, toTypeKind(sym.info));
+ ctx1.bb.emit(STORE_LOCAL(sym, false));
+ ctx1
+
+ case If(cond, thenp, elsep) =>
+ var thenCtx = ctx.newBlock;
+ var elseCtx = ctx.newBlock;
+ val contCtx = ctx.newBlock;
+ genCond(cond, ctx, thenCtx, elseCtx);
+ thenCtx = genLoad(thenp, thenCtx, toTypeKind(tree.tpe));
+ elseCtx = genLoad(elsep, elseCtx, toTypeKind(tree.tpe));
+ thenCtx.bb.emit(JUMP(contCtx.bb));
+ elseCtx.bb.emit(JUMP(contCtx.bb));
+ contCtx;
+
+ case Return(expr) =>
+ val ctx1 = genLoad(expr, ctx, expectedType);
+ ctx1.bb.emit(RETURN());
+ ctx1
+
+ case Try(block, catches, finalizer) =>
+ abort("Try blocks not handled yet");
+
+ case Throw(expr) =>
+ val ctx1 = genLoad(expr, ctx, expectedType);
+ ctx1.bb.emit(THROW());
+ ctx;
+
+ case New(tpt) =>
+ ctx.bb.emit(NEW(tree.symbol));
+ ctx;
+
+ case Apply(TypeApply(fun, targs), _) =>
+ val sym = fun.symbol;
+ val ctx1 = genLoadQualifier(fun, ctx);
+
+ if (sym == definitions.Any_isInstanceOfErased)
+ ctx1.bb.emit(IS_INSTANCE(targs.head.tpe));
+ else if (sym == definitions.Any_asInstanceOfErased)
+ ctx1.bb.emit(CHECK_CAST(targs.head.tpe));
+ else
+ abort("Unexpected type application");
+ ctx1
+
+ case Apply(fun @ Select(Super(_, mixin), _), args) =>
+ val invokeStyle = SuperCall(mixin);
+ val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx);
+
+ ctx1.bb.emit(CALL_METHOD(fun.symbol, invokeStyle));
+ ctx1
+
+
+ case Apply(fun, args) =>
+ val sym = fun.symbol;
+ if (sym.isLabel) {
+
+ val label = ctx.labels.get(sym) match {
+ case Some(l) => l;
+
+ // it is a forward jump, scan for labels
+ case None =>
+ scanForLabels(ctx.defdef, ctx);
+ ctx.labels.get(sym) match {
+ case Some(l) => l;
+ case _ => abort("Unknown label target: " + sym);
+ }
+ }
+ val ctx1 = genLoadLabelArguments(args, label, ctx);
+ if (label.anchored)
+ ctx1.bb.emit(JUMP(label.block));
+ else
+ ctx1.bb.emit(PJUMP(label));
+
+ ctx1.bb.close;
+ ctx1.newBlock;
+ } else if (isPrimitive(fun.symbol)) {
+
+ abort("Primitive operations not handled yet");
+
+ } else {
+ // normal method call
+
+ var invokeStyle =
+ if (sym.isConstructor)
+ NewInstance;
+ else if (sym.isStatic)
+ Static(false)
+ else
+ Dynamic;
+
+ var ctx1 = genLoadQualifier(fun, ctx);
+ ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx1);
+
+ ctx1.bb.emit(CALL_METHOD(sym, invokeStyle));
+ ctx1
+ }
+
+ case This(qual) =>
+ assert(tree.symbol == ctx.clazz.symbol,
+ "Trying to access the this of another class: " +
+ "tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol);
+ ctx.bb.emit(THIS(ctx.clazz.symbol));
+ ctx;
+
+ case Select(qualifier, selector) =>
+ val sym = tree.symbol;
+ if (sym.isStatic) {
+ ctx.bb.emit(LOAD_FIELD(sym, true));
+ ctx
+ } else {
+ val ctx1 = genLoadQualifier(tree, ctx); // !! attention
+ ctx1.bb.emit(LOAD_FIELD(sym, false));
+ ctx1
+ }
+
+ case Ident(name) =>
+ if (tree.symbol.isModule)
+ abort("Modules are not handled yet");
+ else
+ ctx.bb.emit(LOAD_LOCAL(tree.symbol, tree.symbol.isValueParameter ));
+ ctx
+
+ case Literal(value) =>
+ ctx.bb.emit(CONSTANT(value));
+ ctx
+
+ case Block(stats, expr) =>
+ log("Entering block");
+ assert(!(ctx.method eq null), "Block outside method");
+ val ctx1 = genStat(stats, ctx);
+ genLoad(expr, ctx1, expectedType);
+
+ case EmptyTree => ctx;
+
+ case Assign(_, _) => genStat(tree, ctx);
+
+ case _ => abort("Unexpected tree in genLoad: " + tree);
+ }
+ }
+
+ /** 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, REF);
+ case _ =>
+ abort("Unknown qualifier " + tree);
+ }
+
+ /**
+ * 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;
+
+ while (arg != Nil) {
+ ctx1 = genLoad(arg.head, ctx1, toTypeKind(param.head.info));
+ ctx1.bb.emit(STORE_LOCAL(param.head, param.head.isValueParameter));
+ 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
+ }
+
+ /** Is the given symbol a primitive operation? */
+ def isPrimitive(fun: Symbol): Boolean = false;
+
+ /**
+ * 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.
+ *
+ * Attention! Might be expensive to traverse each method twice! An
+ * optimization would be to make it lazily, only if forward jumps
+ * really happen.
+ */
+ private def scanForLabels(tree: Tree, ctx: Context): Unit =
+ new Traverser() {
+ override def traverse(tree: Tree): Unit = tree match {
+
+ case LabelDef(name, params, _) =>
+ ctx.labels += tree.symbol -> (new Label(tree.symbol) setParams(params map (.symbol)));
+
+ 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 = {
+
+
+ }
+
+ /**
+ * 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(p.symbol);
+
+ case _ =>
+ abort("Malformed parameter list: " + vparamss);
+ }
+
+ def toTypeKind(t: Type): TypeKind = t match {
+ case ThisType(_) => REF;
+
+ case SingleType(pre, sym) =>
+ primitiveTypeMap get sym match {
+ case Some(k) => k;
+ case None => REF;
+ }
+
+ case ConstantType(value) =>
+ toTypeKind(value.tpe);
+
+ case TypeRef(_, sym, _) => REF;
+ case _ => abort("Unknown type");
+ }
+
+ /** Initialize the map from scala primitive types to ICode types */
+ private def initPrimitiveTypeMap = {
+ log("Initializing primitive map");
+ primitiveTypeMap = new HashMap();
+ primitiveTypeMap += definitions.UnitClass -> UNIT;
+ primitiveTypeMap += definitions.BooleanClass -> BOOL;
+ primitiveTypeMap += definitions.ByteClass -> I1;
+ primitiveTypeMap += definitions.ShortClass -> I2;
+ primitiveTypeMap += definitions.CharClass -> I2;
+ primitiveTypeMap += definitions.IntClass -> I4;
+ primitiveTypeMap += definitions.LongClass -> I8;
+ primitiveTypeMap += definitions.FloatClass -> R4;
+ primitiveTypeMap += definitions.DoubleClass -> R8;
+ primitiveTypeMap += definitions.StringClass -> STRING;
+ }
+
+ /////////////////////// 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 = _;
+
+ /** TODO: add a current exception handler */
+
+ 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;
+ }
+
+ 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 =
+ new Context(this) setBasicBlock (method.code.newBlock);
+ }
+
+ /**
+ * 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!");
+ 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)
+ if (self == this && failure.anchored) =>
+ CJUMP(block, failure.block, cond);
+
+ case PCJUMP(success, self, cond)
+ if (self == this && success.anchored) =>
+ CJUMP(success.block, block, cond);
+
+ case PCZJUMP(self, failure, cond)
+ if (self == this && failure.anchored) =>
+ CZJUMP(block, failure.block, cond);
+
+ case PCZJUMP(success, self, cond)
+ if (self == this && success.anchored) =>
+ CZJUMP(success.block, block, cond);
+
+ 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.
+ */
+ 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)
+ extends PseudoJUMP(success) {
+
+ if (!failure.anchored)
+ failure.addCallingInstruction(this);
+ }
+
+ case class PCZJUMP(success: Label, failure: Label, cond: TestOp)
+ extends PseudoJUMP(success) {
+
+ if (!failure.anchored)
+ failure.addCallingInstruction(this);
+ }
+
+ }
+}
+
diff --git a/sources/scala/tools/nsc/backend/icode/ICodes.scala b/sources/scala/tools/nsc/backend/icode/ICodes.scala
new file mode 100644
index 0000000000..14406bce1a
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/ICodes.scala
@@ -0,0 +1,22 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+// $Id$
+
+package scala.tools.nsc.backend.icode;
+
+/** Glue together ICode parts.
+ */
+abstract class ICodes: Global extends AnyRef
+ with Members
+ with BasicBlocks
+ with Opcodes
+ with TypeStacks
+{
+ import opcodes._;
+
+ /** The ICode representation of classes */
+ var classes: List[IClass] = Nil;
+}
+
diff --git a/sources/scala/tools/nsc/backend/icode/Members.scala b/sources/scala/tools/nsc/backend/icode/Members.scala
new file mode 100644
index 0000000000..7dac967952
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/Members.scala
@@ -0,0 +1,166 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+// $Id$
+
+package scala.tools.nsc.backend.icode;
+
+import scala.collection.mutable.HashMap;
+import scala.collection.mutable.HashSet;
+
+trait Members: Global {
+
+ /** This class represents the intermediate code of a method.
+ */
+ class Code(label: String) {
+
+ /** The set of all blocks */
+ val blocks: HashSet[BasicBlock] = new HashSet;
+
+ /** The start block of the method */
+ var startBlock: BasicBlock = null;
+
+ /** The stack produced by this method */
+ var producedStack: TypeStack = null;
+
+ private var currentLabel: int = 0;
+ private var aTreeLabels: HashMap[Symbol, BasicBlock] = new HashMap;
+
+ // Constructor code
+ startBlock = newBlock;
+ startBlock.initStack(new TypeStack);
+
+ /** Apply a function to all basic blocks, for side-effects. */
+ def traverse(f: BasicBlock => Unit) =
+ traverseFeedBack((bb: BasicBlock, hm: HashMap[BasicBlock, boolean]) => f(bb));
+
+ /* This method applies the given function to each basic block. */
+ def traverseFeedBack(f: (BasicBlock, HashMap[BasicBlock, Boolean]) => Unit) = {
+ val visited : HashMap[BasicBlock, Boolean] = new HashMap;
+ visited ++= blocks.elements.map(x => Pair(x, false));
+
+ var blockToVisit : List[BasicBlock] = startBlock::Nil;
+
+ while (!blockToVisit.isEmpty) {
+ blockToVisit match {
+ case b::xs =>
+ if (!visited(b)) {
+ f(b, visited);
+ blockToVisit = b.successors ::: xs;
+ visited += b -> true;
+ } else
+ blockToVisit = xs;
+ }
+ }
+ }
+
+ /** This methods returns a string representation of the ICode */
+ override def toString() : String = "ICode '" + label + "'";
+
+ /** This method print the code */
+ def print() : unit = print(System.out);
+
+ def print(out: java.io.PrintStream) : unit = {
+ traverse((bb: BasicBlock) => {
+ out.println("Block #" + bb.label);
+ out.println("Substituable variables : ");
+ if (bb.substituteVars != null)
+ bb.substituteVars.foreach(out.print);
+ else
+ out.println(" {Empty} ");
+ out.println("Instructions:");
+ bb.traverse((ici: Instruction) =>
+ out.println(" "+ici.toString()));
+ out.print ("Successors: ");
+ bb.successors.foreach((bb: BasicBlock) => out.print(bb.label+", "));
+ out.println (""); // ?? Del
+ out.println ();
+ });
+ }
+
+ def logType = {
+ log ("// Typing " + toString());
+ traverse( (bb: BasicBlock) => {
+ log ("Typing block #" + bb.label);
+ var typer = new TypeStack;
+ bb.traverse((ic: Instruction) => {
+ typer = typer.eval(ic);
+ log(ic.toString()+" -> "+typer.toString());
+ });
+
+ });
+ }
+
+ /* Compute a unique new label */
+ def nextLabel = {
+ currentLabel = currentLabel + 1;
+ currentLabel;
+ }
+
+ /* Create a new block and append it to the list
+ */
+ def newBlock : BasicBlock = {
+ val block = new BasicBlock(nextLabel);
+ blocks += block;
+ block;
+ }
+ }
+
+ /** Represent a class in ICode */
+ class IClass(val symbol: Symbol) {
+ var fields: List[IField] = Nil;
+ var methods: List[IMethod] = Nil;
+ var cunit: CompilationUnit = _;
+
+ def addField(f: IField): this.type = {
+ fields = f :: fields;
+ this
+ }
+
+ def addMethod(m: IMethod): this.type = {
+ methods = m :: methods;
+ this
+ }
+
+ def setCompilationUnit(unit: CompilationUnit): this.type = {
+ this.cunit = unit;
+ this
+ }
+ }
+
+ /** Represent a field in ICode */
+ class IField(val symbol: Symbol) {
+ }
+
+ /** Represent a method in ICode */
+ class IMethod(val symbol: Symbol) {
+ var code: Code = null;
+
+ /** local variables and method parameters */
+ var locals: List[Symbol] = Nil;
+
+ /** method parameters */
+ var params: List[Symbol] = Nil;
+
+ def setCode(code: Code): IMethod = {
+ this.code = code;
+ this
+ }
+
+ def addLocal(sym: Symbol): Unit =
+ if (!(locals contains sym))
+ locals = sym :: locals;
+
+ def addLocals(ls: List[Symbol]): Unit =
+ ls foreach addLocal;
+
+ def addParam(sym: Symbol): Unit =
+ if (!(params contains sym))
+ params = sym :: params;
+
+ def addParams(as: List[Symbol]): Unit =
+ as.foreach( (a: Symbol) => { addParam(a); addLocal(a); } )
+ }
+}
diff --git a/sources/scala/tools/nsc/backend/icode/Opcodes.scala b/sources/scala/tools/nsc/backend/icode/Opcodes.scala
new file mode 100644
index 0000000000..0313bd8cc8
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/Opcodes.scala
@@ -0,0 +1,419 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+// $Id$
+
+
+package scala.tools.nsc.backend.icode;
+
+import scala.tools.nsc.ast._;
+import scala.tools.nsc.backend.icode.Primitives._;
+
+
+/**
+ * The ICode intermediate representation. It is a stack-based
+ * representation, very close to the JVM and .NET. It uses the
+ * erased types of Scala and references Symbols to refer named entities
+ * in the source files.
+ */
+abstract class Opcodes: Global {
+
+ /** This class represents an instruction of the intermediate code.
+ * Each case subclass will represent a specific operation.
+ */
+ abstract class Instruction {
+
+ /** This abstract method returns the number of used elements on the stack */
+ def consumed : Int = 0;
+
+ /** This abstract method returns the number of produced elements on the stack */
+ def produced : Int = 0;
+
+ /** This method returns the difference of size of the stack when the instruction is used */
+ def difference = produced-consumed;
+ }
+
+ object opcodes {
+
+ /** Loads the "this" references on top of the stack.
+ * Stack: ...
+ * ->: ...:ref
+ */
+ case class THIS(clasz: Symbol) extends Instruction {
+ /** Returns a string representation of this constant */
+ override def toString(): String = "THIS";
+
+ override def consumed = 0;
+ override def produced = 1;
+ }
+
+ /** Loads a constant on the stack.
+ * Stack: ...
+ * ->: ...:constant
+ */
+ case class CONSTANT(constant: Constant) extends Instruction{
+ /** Returns a string representation of this constant */
+ override def toString(): String = "CONSTANT ("+constant.toString()+")";
+
+ override def consumed = 0;
+ override def produced = 1;
+ }
+
+ /** Loads an element of an array. The array and the index should
+ * be on top of the stack.
+ * Stack: ...:array[a](Ref):index(Int)
+ * ->: ...:element(a)
+ */
+ case class LOAD_ARRAY_ITEM() extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String = "LOAD_ARRAY_ITEM";
+
+ override def consumed = 2;
+ override def produced = 1;
+ }
+
+ /** Load a local variable on the stack. It can be a method argument.
+ * Stack: ...
+ * ->: ...:value
+ */
+ case class LOAD_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String = "LOAD_LOCAL "+local.toString(); //+isArgument?" (argument)":"";
+
+ override def consumed = 0;
+ override def produced = 1;
+ }
+
+ /** Load a field on the stack. The object to which it refers should be
+ * on the stack.
+ * Stack: ...:ref
+ * ->: ...:value
+ */
+ case class LOAD_FIELD(field: Symbol, isStatic: boolean) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String = "LOAD_FIELD "+field.toString(); //+isStatic?" (static)":"";
+
+ override def consumed = 1;
+ override def produced = 1;
+ }
+
+ /** Store a value into an array at a specified index.
+ * Stack: ...:array[a](Ref):index(Int):value(a)
+ * ->: ...
+ */
+ case class STORE_ARRAY_ITEM() extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String = "STORE_ARRAY_ITEM";
+
+ override def consumed = 3;
+ override def produced = 0;
+ }
+
+ /** Store a value into a local variable. It can be an argument.
+ * Stack: ...:value
+ * ->: ...
+ */
+ case class STORE_LOCAL(local: Symbol, isArgument: boolean) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String = "STORE_LOCAL "+local.toString(); //+isArgument?" (argument)":"";
+
+ override def consumed = 1;
+ override def produced = 0;
+ }
+
+ /** Store a value into a field.
+ * Stack: ...:ref:value
+ * ->: ...
+ */
+ case class STORE_FIELD(field: Symbol, isStatic: boolean) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String = "STORE_FIELD "+field.toString(); //+isStatic?" (static)":"";
+
+ override def consumed = 2;
+ override def produced = 0;
+ }
+
+ /** Call a primitive function.
+ * Stack: ...:arg1:arg2:...:argn
+ * ->: ...:result
+ */
+ case class CALL_PRIMITIVE(primitive: Primitive) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="CALL "+primitive.toString();
+
+ override def consumed = primitive match {
+ case (Negation(_)) => 1;
+ case (Test(_,_,true)) => 1;
+ case (Test(_,_,false)) => 2;
+ case (Comparison(_,_)) => 2;
+ case (Arithmetic(_,_)) => 2;
+ case (Logical(_,_)) => 2;
+ case (Shift(_,_)) => 2;
+ case (Conversion(_,_)) => 1;
+ case (ArrayLength(_)) => 1;
+ case (StringConcat(_,_)) => 2;
+ }
+ override def produced = 1;
+ }
+
+ /** This class represents a CALL_METHOD instruction
+ * STYLE: dynamic / static(StaticInstance)
+ * Stack: ...:ref:arg1:arg2:...:argn
+ * ->: ...:result
+ *
+ * STYLE: new - unused by jvm
+ * Stack: ...:arg1:arg2:...:argn
+ * ->: ...:ref
+ *
+ * STYLE: static(StaticClass)
+ * Stack: ...:arg1:arg2:...:argn
+ * ->: ...:result
+ *
+ */
+ case class CALL_METHOD(method: Symbol, style: InvokeStyle) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="CALL_METHOD "+method.toString()+" ("+style.toString()+")";
+
+ override def consumed = {
+ var result = method.tpe.paramTypes.length;
+ result = result + (style match {
+ case Dynamic => 1
+ case Static(true) => 1
+ case _ => 0
+ });
+
+ result;
+ }
+ override def produced = 1;
+ }
+
+ /** Create a new instance of a class.
+ * Stack: ...:
+ * ->: ...:ref
+ */
+ case class NEW(clasz: Symbol) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String = "NEW "+clasz.toString();
+
+ override def consumed = 0;
+ override def produced = 1;
+ }
+
+
+ /** This class represents a CREATE_ARRAY instruction
+ * Stack: ...:size(int)
+ * ->: ...:arrayref
+ */
+ case class CREATE_ARRAY(element: Type) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="CREATE_ARRAY "+element.toString();
+
+ override def consumed = 1;
+ override def produced = 1;
+ }
+
+ /** This class represents a IS_INSTANCE instruction
+ * Stack: ...:ref
+ * ->: ...:result(boolean)
+ */
+ case class IS_INSTANCE(typ: Type) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="IS_INSTANCE "+typ.toString();
+
+ override def consumed = 1;
+ override def produced = 1;
+ }
+
+ /** This class represents a CHECK_CAST instruction
+ * Stack: ...:ref(oldtype)
+ * ->: ...:ref(typ <=: oldtype)
+ */
+ case class CHECK_CAST(typ: Type) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="CHECK_CAST "+typ.toString();
+
+ override def consumed = 1;
+ override def produced = 1;
+ }
+
+ /** This class represents a SWITCH instruction
+ * Stack: ...:index(int)
+ * ->: ...:
+ */
+ case class SWITCH(tags: Array[Array[int]], labels: List[BasicBlock]) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="SWITCH ...";
+
+ override def consumed = 1;
+ override def produced = 0;
+ }
+
+ /** This class represents a JUMP instruction
+ * Stack: ...
+ * ->: ...
+ */
+ case class JUMP(where: BasicBlock) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="JUMP "+where.label;
+
+ override def consumed = 0;
+ override def produced = 0;
+ }
+
+ /** This class represents a CJUMP instruction
+ * It compares the two values on the stack with the 'cond' test operator
+ * Stack: ...:value1:value2
+ * ->: ...
+ */
+ case class CJUMP(successBlock: BasicBlock,
+ failureBlock: BasicBlock,
+ cond: TestOp) extends Instruction {
+
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="CJUMP "+cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label;
+
+ override def consumed = 2;
+ override def produced = 0;
+ }
+
+ /** This class represents a CZJUMP instruction
+ * It compares the one value on the stack and zero with the 'cond' test operator
+ * Stack: ...:value:
+ * ->: ...
+ */
+ case class CZJUMP(successBlock: BasicBlock, failureBlock: BasicBlock, cond: TestOp) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="CZJUMP "+cond.toString()+" ? "+successBlock.label+" : "+failureBlock.label;
+
+ override def consumed = 1;
+ override def produced = 0;
+ }
+
+
+ /** This class represents a RETURN instruction
+ * Stack: ...
+ * ->: ...
+ */
+ case class RETURN() extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="RETURN";
+
+ override def consumed = 0;
+ override def produced = 0;
+ }
+
+ /** This class represents a THROW instruction
+ * Stack: ...:Throwable(Ref)
+ * ->: ...:
+ */
+ case class THROW() extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="THROW";
+
+ override def consumed = 1;
+ override def produced = 0;
+ }
+
+ /** This class represents a DROP instruction
+ * Stack: ...:something
+ * ->: ...
+ */
+ case class DROP (typ: Type) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="DROP "+typ.toString();
+
+ override def consumed = 1;
+ override def produced = 0;
+ }
+
+ /** This class represents a DUP instruction
+ * Stack: ...:something
+ * ->: ...:something:something
+ */
+ case class DUP (typ: Type) extends Instruction {
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="DUP";
+
+ override def consumed = 1;
+ override def produced = 2;
+ }
+
+ /** This class represents a MONITOR_ENTER instruction
+ * Stack: ...:object(ref)
+ * ->: ...:
+ */
+ case class MONITOR_ENTER() extends Instruction {
+
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="MONITOR_ENTER";
+
+ override def consumed = 1;
+ override def produced = 0;
+ }
+
+ /** This class represents a MONITOR_EXIT instruction
+ * Stack: ...:object(ref)
+ * ->: ...:
+ */
+ case class MONITOR_EXIT() extends Instruction {
+
+ /** Returns a string representation of this instruction */
+ override def toString(): String ="MONITOR_EXIT";
+
+ override def consumed = 1;
+ override def produced = 0;
+ }
+
+ /** This class represents a method invocation style. */
+ trait InvokeStyle {
+ /** Is this a new object creation? */
+ def isNew: Boolean = this match {
+ case NewInstance => true;
+ case _ => false;
+ }
+
+ /** Is this a dynamic method call? */
+ def isDynamic: Boolean = this match {
+ case Dynamic => true;
+ case _ => false;
+ }
+
+ /** Is this a static method call? */
+ def isStatic: Boolean = this match {
+ case Static(_) => true;
+ case _ => false;
+ }
+
+ /** Is this an instance method call? */
+ def hasInstance: Boolean = this match {
+ case Dynamic => true;
+ case Static(onInstance) => onInstance;
+ case _ => false;
+ }
+
+ /** Returns a string representation of this style. */
+ override def toString(): String = this match {
+ case NewInstance => "new";
+ case Dynamic => "dynamic";
+ case Static(false) => "static-class";
+ case Static(true) => "static-instance";
+ case SuperCall(mixin) => "super(" + mixin + ")";
+ }
+ }
+
+ case object NewInstance extends InvokeStyle;
+ case object Dynamic extends InvokeStyle;
+
+ /**
+ * Special invoke. Static(true) is used for constructor,
+ * priavate and super calls.
+ */
+ case class Static(onInstance: Boolean) extends InvokeStyle;
+
+ /** Call through super[mixin]. */
+ case class SuperCall(mixin: Name) extends InvokeStyle;
+
+ }
+}
diff --git a/sources/scala/tools/nsc/backend/icode/Primitives.scala b/sources/scala/tools/nsc/backend/icode/Primitives.scala
new file mode 100644
index 0000000000..3fc765069e
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/Primitives.scala
@@ -0,0 +1,234 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+\* */
+
+// $Id$
+
+package scala.tools.nsc.backend.icode;
+
+import java.io.PrintWriter;
+
+object Primitives {
+
+ /** This class represents a primitive operation. */
+ class Primitive {
+
+ /** Returns a string representation of this primitive. */
+ override def toString(): String = {
+// val x: StringBuffer = new StringBuffer();
+// new PrimitivePrinter(new PrintWriter(x)).printPrimitive(this);
+// x.toString();
+ ""
+ }
+ }
+
+
+ // type : (type) => type
+ // range: type <- { BOOL, Ix, Ux, Rx }
+ // jvm : {i, l, f, d}neg
+ case class Negation(kind: TypeKind) extends Primitive;
+
+ // type : zero ? (type) => BOOL : (type,type) => BOOL
+ // range: type <- { BOOL, Ix, Ux, Rx, REF }
+ // jvm : if{eq, ne, lt, ge, le, gt}, if{null, nonnull}
+ // if_icmp{eq, ne, lt, ge, le, gt}, if_acmp{eq,ne}
+ case class Test(op: TestOp, kind: TypeKind, zero: boolean) extends Primitive;
+
+ // type : (type,type) => I4
+ // range: type <- { Ix, Ux, Rx }
+ // jvm : lcmp, {f, d}cmp{l, g}
+ case class Comparison(op: ComparisonOp, kind: TypeKind) extends Primitive;
+
+ // type : (type,type) => type
+ // range: type <- { Ix, Ux, Rx }
+ // jvm : {i, l, f, d}{add, sub, mul, div, rem}
+ case class Arithmetic(op: ArithmeticOp, kind: TypeKind) extends Primitive;
+
+ // type : (type,type) => type
+ // range: type <- { BOOL, Ix, Ux }
+ // jvm : {i, l}{and, or, xor}
+ case class Logical(op: LogicalOp, kind: TypeKind) extends Primitive;
+
+ // type : (type,I4) => type
+ // range: type <- { Ix, Ux }
+ // jvm : {i, l}{shl, ushl, shr}
+ case class Shift(op: ShiftOp, kind: TypeKind) extends Primitive;
+
+ // type : (src) => dst
+ // range: src,dst <- { Ix, Ux, Rx }
+ // jvm : i2{l, f, d}, l2{i, f, d}, f2{i, l, d}, d2{i, l, f}, i2{b, c, s}
+ case class Conversion(src: TypeKind, dst: TypeKind) extends Primitive;
+
+ // type : (Array[REF]) => I4
+ // range: type <- { BOOL, Ix, Ux, Rx, REF }
+ // jvm : arraylength
+ case class ArrayLength(kind: TypeKind) extends Primitive;
+
+ // type : (lf,rg) => STR
+ // range: lf,rg <- { BOOL, Ix, Ux, Rx, REF, STR }
+ // jvm : -
+ case class StringConcat(lf: TypeKind, rg: TypeKind) extends Primitive;
+
+
+ /** Pretty printer for primitives */
+ class PrimitivePrinter(out: PrintWriter) {
+
+ def print(s: String): PrimitivePrinter = {
+ out.print(s);
+ this
+ }
+
+ def print(o: AnyRef): PrimitivePrinter = print(o.toString());
+
+ def printPrimitive(prim: Primitive) = prim match {
+ case Negation(kind) =>
+ print("!");
+
+ case Test(op, kind, zero) =>
+ print(op).print(kind);
+
+ case Comparison(op, kind) =>
+ print(op).print("(").print(kind);
+
+ }
+ }
+
+ /** This class represents a comparison operation. */
+ class ComparisonOp {
+
+ /** Returns a string representation of this operation. */
+ override def toString(): String = this match {
+ case CMPL => "CMPL";
+ case CMP => "CMP";
+ case CMPG => "CMPG";
+ case _ => throw new RuntimeException("ComparisonOp unknown case");
+ }
+ }
+
+ /** A comparison operation with -1 default for NaNs */
+ case object CMPL extends ComparisonOp;
+
+ /** A comparison operation with no default for NaNs */
+ case object CMP extends ComparisonOp;
+
+ /** A comparison operation with +1 default for NaNs */
+ case object CMPG extends ComparisonOp;
+
+
+ /** This class represents a test operation. */
+ class TestOp {
+
+ /** Returns the negation of this operation. */
+ def negate(): TestOp = this match {
+ case EQ => NE;
+ case NE => EQ;
+ case LT => GE;
+ case GE => LT;
+ case LE => GT;
+ case GT => LE;
+ case _ => throw new RuntimeException("TestOp unknown case");
+ }
+
+ /** Returns a string representation of this operation. */
+ override def toString(): String = this match {
+ case EQ => "EQ";
+ case NE => "NE";
+ case LT => "LT";
+ case GE => "GE";
+ case LE => "LE";
+ case GT => "GT";
+ case _ => throw new RuntimeException("TestOp unknown case");
+ }
+ }
+ /** An equality test */
+ case object EQ extends TestOp;
+
+ /** A non-equality test */
+ case object NE extends TestOp;
+
+ /** A less-than test */
+ case object LT extends TestOp;
+
+ /** A greater-than-or-equal test */
+ case object GE extends TestOp;
+
+ /** A less-than-or-equal test */
+ case object LE extends TestOp;
+
+ /** A greater-than test */
+ case object GT extends TestOp;
+
+ /** This class represents an arithmetic operation. */
+ class ArithmeticOp {
+
+ /** Returns a string representation of this operation. */
+ override def toString(): String = this match {
+ case ADD => "ADD";
+ case SUB => "SUB";
+ case MUL => "MUL";
+ case DIV => "DIV";
+ case REM => "REM";
+ case _ => throw new RuntimeException("ArithmeticOp unknown case");
+ }
+ }
+
+ /** An arithmetic addition operation */
+ case object ADD extends ArithmeticOp;
+
+ /** An arithmetic subtraction operation */
+ case object SUB extends ArithmeticOp;
+
+ /** An arithmetic multiplication operation */
+ case object MUL extends ArithmeticOp;
+
+ /** An arithmetic division operation */
+ case object DIV extends ArithmeticOp;
+
+ /** An arithmetic remainder operation */
+ case object REM extends ArithmeticOp;
+
+ /** This class represents a shift operation. */
+ class ShiftOp {
+
+ /** Returns a string representation of this operation. */
+ override def toString(): String = this match {
+ case LSL => "LSL";
+ case ASR => "ASR";
+ case LSR => "LSR";
+ case _ => throw new RuntimeException("ShitOp unknown case");
+ }
+ }
+
+ /** A logical shift to the left */
+ case object LSL extends ShiftOp;
+
+ /** An arithmetic shift to the right */
+ case object ASR extends ShiftOp;
+
+ /** A logical shift to the right */
+ case object LSR extends ShiftOp;
+
+ /** This class represents a logical operation. */
+ class LogicalOp {
+
+ /** Returns a string representation of this operation. */
+ override def toString(): String = this match {
+ case AND => return "AND";
+ case OR => return "OR";
+ case XOR => return "XOR";
+ case _ => throw new RuntimeException("LogicalOp unknown case");
+ }
+ }
+
+ /** A bitwise AND operation */
+ case object AND extends LogicalOp;
+
+ /** A bitwise OR operation */
+ case object OR extends LogicalOp;
+
+ /** A bitwise XOR operation */
+ case object XOR extends LogicalOp;
+}
+
diff --git a/sources/scala/tools/nsc/backend/icode/Printers.scala b/sources/scala/tools/nsc/backend/icode/Printers.scala
new file mode 100644
index 0000000000..4b174847ae
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/Printers.scala
@@ -0,0 +1,97 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc.backend.icode;
+
+import java.io.PrintWriter;
+
+abstract class Printers {
+ val global: Global;
+ import global._;
+ import opcodes._;
+
+ class TextPrinter(out: PrintWriter) {
+ var margin = 0;
+
+ final val TAB = 2;
+
+ def indent = margin = margin + TAB;
+ def undent = margin = margin - TAB;
+
+ def print(s: String) = out.print(s);
+ def print(o: Any): Unit = print(o.toString());
+
+ def println(s: String): Unit = {
+ print(s);
+ println
+ }
+
+ def println = {
+ out.println();
+ var i = 0;
+ while (i < margin) {
+ print(" ");
+ i = i + 1;
+ }
+ }
+
+ def printList[a](l: List[a], sep: String): Unit = l match {
+ case Nil => ();
+ case x :: Nil => print(x);
+ case x :: xs => print(x); print(sep); printList(xs, sep);
+ }
+
+ def printList[a](pr: a => Unit)(l: List[a], sep: String): Unit = l match {
+ case Nil => ();
+ case x :: Nil => pr(x);
+ case x :: xs => pr(x); print(sep); printList(pr)(xs, sep);
+ }
+
+
+ def printClass(cls: IClass): Unit = {
+ print(cls.symbol.toString()); print(" extends ");
+ printList(cls.symbol.info.parents, ", ");
+ indent; println(" {");
+ println("// fields:");
+ cls.fields.foreach(printField); println;
+ println("// methods");
+ cls.methods.foreach(printMethod);
+ undent; println;
+ println("}");
+ }
+
+ def printField(f: IField): Unit = {
+ print(f.symbol.keyString); print(" ");
+ print(f.symbol.nameString); print(": ");
+ println(f.symbol.info.toString());
+ }
+
+ def printMethod(m: IMethod): Unit = {
+ print("def "); print(m.symbol.name);
+ print("("); printList(printParam)(m.params.reverse, ", "); print(")");
+ print(": "); print(m.symbol.info.resultType); println(" {");
+ printCode(m.code);
+ println("}");
+ }
+
+ def printParam(p: Symbol): Unit = {
+ print(p.name); print(": "); print(p.info);
+ }
+
+ def printCode(code: Code): Unit = {
+ code traverse printBlock;
+ }
+
+ def printBlock(bb: BasicBlock): Unit = {
+ print(bb.label); print(": "); indent; println;
+ bb traverse printInstruction;
+ undent; println;
+ }
+
+ def printInstruction(i: Instruction): Unit = {
+ println(i.toString());
+ }
+ }
+}
diff --git a/sources/scala/tools/nsc/backend/icode/TypeKind.scala b/sources/scala/tools/nsc/backend/icode/TypeKind.scala
new file mode 100644
index 0000000000..dae8820264
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/TypeKind.scala
@@ -0,0 +1,79 @@
+
+// $Id$
+
+package scala.tools.nsc.backend.icode;
+
+/** This class represents a type kind. Type kinds
+ * represent the types that the VM know (or the ICode
+ * view of what VMs know.
+ */
+class TypeKind {
+
+ /** Returns a string representation of this type kind. */
+ override def toString(): String = this match {
+ case UNIT => "UNIT";
+ case BOOL => "BOOL";
+ case U1 => "U1";
+ case U2 => "U2";
+ case U4 => "U4";
+ case U8 => "U8";
+ case I1 => "I1";
+ case I2 => "I2";
+ case I4 => "I4";
+ case I8 => "I8";
+ case R4 => "R4";
+ case R8 => "R8";
+ case REF => "REF";
+ case STRING => "STR";
+ case NULL => "NULL";
+ case ZERO => "ZERO";
+ }
+}
+
+/** The unit value */
+case object UNIT extends TypeKind;
+
+/** A boolean value */
+case object BOOL extends TypeKind;
+
+/** A 1-byte unsigned integer */
+case object U1 extends TypeKind;
+
+/** A 2-byte unsigned integer */
+case object U2 extends TypeKind;
+
+/** A 4-byte unsigned integer */
+case object U4 extends TypeKind;
+
+/** An 8-byte unsigned integer */
+case object U8 extends TypeKind;
+
+/** A 1-byte signed integer */
+case object I1 extends TypeKind;
+
+/** A 2-byte signed integer */
+case object I2 extends TypeKind;
+
+/** A 4-byte signed integer */
+case object I4 extends TypeKind;
+
+/** An 8-byte signed integer */
+case object I8 extends TypeKind;
+
+/** A 4-byte floating point number */
+case object R4 extends TypeKind;
+
+/** An 8-byte floating point number */
+case object R8 extends TypeKind;
+
+/** An object reference */
+case object REF extends TypeKind;
+
+/** A string reference */
+case object STRING extends TypeKind;
+
+/** The null reference */
+case object NULL extends TypeKind;
+
+/** The zero value */
+case object ZERO extends TypeKind;
diff --git a/sources/scala/tools/nsc/backend/icode/TypeStacks.scala b/sources/scala/tools/nsc/backend/icode/TypeStacks.scala
new file mode 100644
index 0000000000..6398636517
--- /dev/null
+++ b/sources/scala/tools/nsc/backend/icode/TypeStacks.scala
@@ -0,0 +1,131 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+// $Id$
+
+package scala.tools.nsc.backend.icode;
+
+import scala.tools.nsc.backend.icode.Primitives;
+
+trait TypeStacks: Global {
+ import opcodes._;
+
+ /* This class simulates the type of the opperand
+ * stack of the ICode.
+ */
+ class TypeStack {
+
+ private var stack : List[Type] = Nil;
+// private val definitions = global.definitions;
+ // TODO:
+ // private val typer = definitions.atyper; // !!! Using atree typer !
+
+ private def this(stack : List[Type]) = {
+ this();
+ this.stack = stack;
+ }
+
+ /** Simulate the effects of an Instruction
+ * on the stack. It returns the new stack
+ */
+ def eval(instr: Instruction) : TypeStack = {
+ log(instr.toString());
+ assert(stack.length >= instr.consumed, "Invalid instruction :"+instr+" for stack "+toString());
+
+ val result = new TypeStack(erase(production(instr)):::stack.drop(instr.consumed)); /// !!! Opt
+ log("\t-> "+result.toString());
+ result;
+
+ }
+
+ // ##################################################
+ // Public methods - Operation on the stack
+
+ /* Push a new type */
+ def push(t: Type) = stack = t::stack;
+
+ /* Returns the topmost type of the stack */
+ def head = stack.head;
+
+ /* Returns the stack without the topmost element */
+ def tail = stack.tail;
+
+ /* Is the stack empty ? */
+ def isEmpty = stack.isEmpty;
+
+ /* Compute what type(s) the instruction produce on the stack */
+ private def production(instr: Instruction) : List[Type] = instr match {
+ case THIS(clasz) => clasz.thisType :: Nil;
+
+ // TODO:
+ // case CONSTANT(aConstant) => typer.computeType(aConstant)::Nil;
+
+ // TODO:
+ // case LOAD_ARRAY_ITEM() => typer.getArrayElementType(stack.tail.head)::Nil;
+
+ case LOAD_LOCAL(local, _) => local.tpe :: Nil;
+
+ case LOAD_FIELD(field, _) => stack.head.memberType(field) :: Nil;
+
+ case STORE_ARRAY_ITEM() => Nil;
+
+ case STORE_LOCAL(_,_) => Nil;
+
+ case STORE_FIELD(_,_) => Nil;
+
+ // TODO:
+ // case CALL_PRIMITIVE(p) => typer.computeType(p)::Nil;
+
+ case CALL_METHOD(method, style) => style match {
+ case NewInstance => method.owner.thisType :: Nil;
+ case _ => method.tpe.resultType :: Nil;
+ }
+ case NEW(clasz) => clasz.tpe :: Nil;
+
+ case CREATE_ARRAY(element) =>
+ appliedType(definitions.ArrayClass.typeConstructor,
+ List(element)) :: Nil;
+
+ case IS_INSTANCE(_) => definitions.BooleanClass.tpe :: Nil;
+
+ case CHECK_CAST(typ) => typ::Nil;
+
+ case SWITCH(_,_) => Nil;
+
+ case JUMP(_) => Nil;
+
+ case CJUMP(_,_,_) => Nil;
+
+ case CZJUMP(_,_,_) => Nil;
+
+ case RETURN() => Nil;
+
+ case THROW() => Nil;
+
+ case DROP(_) => Nil;
+
+ case DUP(_) => stack.head::stack.head::Nil;
+
+ case MONITOR_ENTER() => Nil;
+
+ case MONITOR_EXIT() => Nil;
+ }
+
+ /* This method kill all the produced *Unit* elements */
+ // !!! Hack
+ private def erase(production : List[Type]) = production.filter(
+ (t: Type) => !(t == definitions.UnitClass.tpe)
+ );
+
+ /* This method returns a String representation of the stack */
+ override def toString() = {
+ var ret : String = "";
+ stack.foreach((t: Type) => ret=(t.toString()+"::")+ret);
+ ret;
+
+ }
+ }
+
+}