summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2005-09-23 16:04:10 +0000
committerIulian Dragos <jaguarul@gmail.com>2005-09-23 16:04:10 +0000
commit4a72b68fe32818b2532308536a91a0e573590ab9 (patch)
tree223b23cf6a9684f66efab76906a7f81957dfb3ed
parent7554cbeb65687e131d88b70c951bc8b285ed2f3d (diff)
downloadscala-4a72b68fe32818b2532308536a91a0e573590ab9.tar.gz
scala-4a72b68fe32818b2532308536a91a0e573590ab9.tar.bz2
scala-4a72b68fe32818b2532308536a91a0e573590ab9.zip
Various bug fixes.
-rwxr-xr-xsources/scala/tools/nsc/Global.scala26
-rw-r--r--sources/scala/tools/nsc/backend/ScalaPrimitives.scala127
-rw-r--r--sources/scala/tools/nsc/backend/icode/BasicBlocks.scala46
-rw-r--r--sources/scala/tools/nsc/backend/icode/Checkers.scala239
-rw-r--r--sources/scala/tools/nsc/backend/icode/GenICode.scala297
-rw-r--r--sources/scala/tools/nsc/backend/icode/ICodes.scala1
-rw-r--r--sources/scala/tools/nsc/backend/icode/Opcodes.scala41
-rw-r--r--sources/scala/tools/nsc/backend/icode/Printers.scala9
-rw-r--r--sources/scala/tools/nsc/backend/icode/TypeKinds.scala12
9 files changed, 567 insertions, 231 deletions
diff --git a/sources/scala/tools/nsc/Global.scala b/sources/scala/tools/nsc/Global.scala
index 117b0271e1..8a2715eb01 100755
--- a/sources/scala/tools/nsc/Global.scala
+++ b/sources/scala/tools/nsc/Global.scala
@@ -153,6 +153,7 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
var globalPhase: Phase = NoPhase;
+
abstract class GlobalPhase(prev: Phase) extends Phase(prev) {
def run: unit = units foreach applyPhase;
def apply(unit: CompilationUnit): unit;
@@ -254,8 +255,8 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
flatten,
constructors,
mixin,
- if (settings.Xshowicode.value) genicode
- else sampleTransform);
+ genicode,
+ sampleTransform);
val parserPhase = syntaxAnalyzer.newPhase(NoPhase);
val firstPhase = parserPhase;
@@ -345,7 +346,7 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
globalPhase = globalPhase.next;
if (settings.check contains globalPhase.name) {
phase = globalPhase;
- if (globalPhase.name == "terminal" && settings.Xshowicode.value)
+ if (globalPhase.name == "terminal")
icodeChecker.checkICodes;
else
checker.checkTrees;
@@ -355,7 +356,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 (settings.Xshowicode.value) writeICode();
if (reporter.errors() == 0) {
for (val Pair(sym, pickled) <- symData.elements.toList) {
@@ -451,8 +452,21 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
}
}
- private def printICode(): Unit = {
+ private def writeICode(): Unit = {
val printer = new icodePrinter.TextPrinter(new PrintWriter(System.out, true));
- icodes.classes.foreach(printer.printClass);
+ icodes.classes.foreach((cls) => {
+ val file = getFile(cls.symbol, ".icode");
+ try {
+ val stream = new FileOutputStream(file);
+ printer.setWriter(new PrintWriter(stream, true));
+ printer.printClass(cls);
+ informProgress("wrote " + file);
+ } catch {
+ case ex: IOException =>
+ if (settings.debug.value) ex.printStackTrace();
+ error("could not write file " + file);
+ }
+
+ });
}
}
diff --git a/sources/scala/tools/nsc/backend/ScalaPrimitives.scala b/sources/scala/tools/nsc/backend/ScalaPrimitives.scala
index 077e06e320..d03ffdb484 100644
--- a/sources/scala/tools/nsc/backend/ScalaPrimitives.scala
+++ b/sources/scala/tools/nsc/backend/ScalaPrimitives.scala
@@ -200,7 +200,7 @@ abstract class ScalaPrimitives {
private var primitives: Map[Symbol, Int] = _;
/** Initialize the primitive map */
- def initPrimitives: Unit = {
+ def init: Unit = {
primitives = new HashMap();
// scala.Any
@@ -254,7 +254,7 @@ abstract class ScalaPrimitives {
addPrimitives(BooleanClass, nme.ADD, CONCAT);
// scala.Byte
- addPrimitives(ByteClass, nme.coerce, COERCE);
+ addCoercions(ByteClass);
addPrimitives(ByteClass, nme.EQ, EQ);
addPrimitives(ByteClass, nme.NE, NE);
addPrimitives(ByteClass, nme.equals_, EQUALS);
@@ -278,7 +278,7 @@ abstract class ScalaPrimitives {
addPrimitives(ByteClass, nme.ASR, ASR);
// scala.Short
- addPrimitives(ShortClass, nme.coerce, COERCE);
+ addCoercions(ShortClass);
addPrimitives(ShortClass, nme.EQ, EQ);
addPrimitives(ShortClass, nme.NE, NE);
addPrimitives(ShortClass, nme.equals_, EQUALS);
@@ -302,7 +302,7 @@ abstract class ScalaPrimitives {
addPrimitives(ShortClass, nme.ASR, ASR);
// scala.Char
- addPrimitives(CharClass, nme.coerce, COERCE);
+ addCoercions(CharClass);
addPrimitives(CharClass, nme.EQ, EQ);
addPrimitives(CharClass, nme.NE, NE);
addPrimitives(CharClass, nme.equals_, EQUALS);
@@ -326,7 +326,7 @@ abstract class ScalaPrimitives {
addPrimitives(CharClass, nme.ASR, ASR);
// scala.Int
- addPrimitives(IntClass, nme.coerce, COERCE);
+ addCoercions(IntClass);
addPrimitives(IntClass, nme.EQ, EQ);
addPrimitives(IntClass, nme.NE, NE);
addPrimitives(IntClass, nme.equals_, EQUALS);
@@ -350,7 +350,7 @@ abstract class ScalaPrimitives {
addPrimitives(IntClass, nme.ASR, ASR);
// scala.Long
- addPrimitives(LongClass, nme.coerce, COERCE);
+ addCoercions(LongClass);
addPrimitives(LongClass, nme.EQ, EQ);
addPrimitives(LongClass, nme.NE, NE);
addPrimitives(LongClass, nme.equals_, EQUALS);
@@ -374,7 +374,7 @@ abstract class ScalaPrimitives {
addPrimitives(LongClass, nme.ASR, ASR);
// scala.Float
- addPrimitives(FloatClass, nme.coerce, COERCE);
+ addCoercion(FloatClass);
addPrimitives(FloatClass, nme.EQ, EQ);
addPrimitives(FloatClass, nme.NE, NE);
addPrimitives(FloatClass, nme.equals_, EQUALS);
@@ -391,7 +391,6 @@ abstract class ScalaPrimitives {
addPrimitives(FloatClass, nme.GE, GE);
// scala.Double
- addPrimitives(DoubleClass, nme.coerce, COERCE);
addPrimitives(DoubleClass, nme.EQ, EQ);
addPrimitives(DoubleClass, nme.NE, NE);
addPrimitives(DoubleClass, nme.equals_, EQUALS);
@@ -423,12 +422,121 @@ abstract class ScalaPrimitives {
sym.info match {
case OverloadedType(pre, alternatives) =>
log("Adding " + alternatives.length + " overloads for " + sym.fullNameString);
- alternatives foreach ((s) => addPrimitive(s, code));
+ code match {
+ case SUB =>
+ alternatives foreach ((s) =>
+ if (s.info.paramTypes.length == 0)
+ addPrimitive(s, NEG); // unary
+ else
+ addPrimitive(s, code));
+
+ case ADD =>
+ alternatives foreach ((s) =>
+ if (s.info.paramTypes.length == 0)
+ addPrimitive(s, POS); // unary
+ else if (s.info.paramTypes.head == definitions.StringClass.tpe)
+ addPrimitive(s, CONCAT); // string concatenation
+ else
+ addPrimitive(s, code));
+
+ case _ =>
+ alternatives foreach ((s) => addPrimitive(s, code));
+ }
case _ =>
addPrimitive(sym, code);
}
+ }
+
+ def addCoercion(cls: Symbol) = {
+ assert(cls == FloatClass,
+ "Only scala.Double has non-overloaded 'coerce'");
+ val method = cls.info.member(nme.coerce);
+ addPrimitive(method, F2D);
+ }
+ def addCoercions(cls: Symbol): Unit = {
+ val OverloadedType(_, coercions) = cls.info.member(nme.coerce).info;
+ if (cls == ByteClass)
+ coercions foreach ((m) =>
+ if (m.info.resultType == ShortClass.tpe)
+ addPrimitive(m, B2S)
+ else if (m.info.resultType == IntClass.tpe)
+ addPrimitive(m, B2I)
+ else if (m.info.resultType == LongClass.tpe)
+ addPrimitive(m, B2L)
+ else if (m.info.resultType == FloatClass.tpe)
+ addPrimitive(m, B2F)
+ else if (m.info.resultType == DoubleClass.tpe)
+ addPrimitive(m, B2D)
+ else
+ abort("Unknown coercion method: " + m.info)
+ )
+ else if (cls == ShortClass)
+ coercions foreach ((m) =>
+ if (m.info.resultType == IntClass.tpe)
+ addPrimitive(m, S2I)
+ else if (m.info.resultType == LongClass.tpe)
+ addPrimitive(m, S2L)
+ else if (m.info.resultType == FloatClass.tpe)
+ addPrimitive(m, S2F)
+ else if (m.info.resultType == DoubleClass.tpe)
+ addPrimitive(m, S2D)
+ else
+ abort("Unknown coercion method: " + m.fullNameString)
+ )
+ else if (cls == CharClass)
+ coercions foreach ((m) =>
+ if (m.info.resultType == IntClass.tpe)
+ addPrimitive(m, C2I)
+ else if (m.info.resultType == LongClass.tpe)
+ addPrimitive(m, C2L)
+ else if (m.info.resultType == FloatClass.tpe)
+ addPrimitive(m, C2F)
+ else if (m.info.resultType == DoubleClass.tpe)
+ addPrimitive(m, C2D)
+ else
+ abort("Unknown coercion method: " + m.fullNameString)
+ )
+ else if (cls == IntClass)
+ coercions foreach ((m) =>
+ if (m.info.resultType == LongClass.tpe)
+ addPrimitive(m, I2L)
+ else if (m.info.resultType == FloatClass.tpe)
+ addPrimitive(m, I2F)
+ else if (m.info.resultType == DoubleClass.tpe)
+ addPrimitive(m, I2D)
+ else
+ abort("Unknown coercion method: " + m.fullNameString)
+ )
+ else if (cls == LongClass)
+ coercions foreach ((m) =>
+ if (m.info.resultType == FloatClass.tpe)
+ addPrimitive(m, L2F)
+ else if (m.info.resultType == DoubleClass.tpe)
+ addPrimitive(m, L2D)
+ else
+ abort("Unknown coercion method: " + m.fullNameString)
+ )
+ else if (cls == FloatClass)
+ coercions foreach ((m) =>
+ if (m.info.resultType == DoubleClass.tpe)
+ addPrimitive(m, F2D)
+ else
+ abort("Unknown coercion method: " + m.fullNameString)
+ )
+ else
+ abort("Unknown value type: " + cls.fullNameString);
+ }
+
+ def isCoercion(code: Int): Boolean = code match {
+ case B2S | B2I | B2L | B2F | B2D |
+ S2I | S2L | S2F | S2D |
+ C2I | C2L | C2F | C2D |
+ I2L | I2F | I2D |
+ L2F | L2D |
+ F2D => true;
+ case _ => false;
}
/** Check whether the given operation code is an array operation. */
@@ -503,7 +611,6 @@ abstract class ScalaPrimitives {
primitives(sym);
}
-
/**
* Return the primitive code of the given operation. If the
* operation is an array get/set, we inspect the type of the receiver
diff --git a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
index 8746d2f320..558a983586 100644
--- a/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
+++ b/sources/scala/tools/nsc/backend/icode/BasicBlocks.scala
@@ -35,10 +35,12 @@ trait BasicBlocks: ICodes {
var preds: List[BasicBlock] = null;
- /** ICode instructions */
+ /** ICode instructions, used as temporary storage while emitting code.
+ * Once closed is called, only the `instrs' array should be used.
+ */
private var instructionList: List[Instruction] = Nil;
- private var lastInstruction: Instruction = null;
+ private var _lastInstruction: Instruction = null;
private var closed: boolean = false;
@@ -49,12 +51,22 @@ trait BasicBlocks: ICodes {
/** 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);
+ /** Apply a function to all the instructions of the block. */
+ def traverse(f: Instruction => unit) = {
+ assert(closed, "Traversing an open block!: ");
+ instrs foreach f;
+ }
+
+ def traverseBackwards(f: Instruction => Unit) = {
+ var i = instrs.length - 1;
+ while (i >= 0) {
+ f(instrs(i));
+ i = i - 1
+ }
+ }
/** The number of instructions in this basic block so far. */
- def size: Int = instructionList.length;
+ def size: Int = instrs.length;
/** Initialize the stack of the block, must be done before evaluation
* the type stack */
@@ -87,7 +99,7 @@ trait BasicBlocks: ICodes {
var i = 0;
var changed = false;
- while (i < instrs.length) {
+ while (i < instrs.length && !changed) {
if (instrs(i) == oldInstr) {
instrs(i) = newInstr;
changed = true;
@@ -113,22 +125,22 @@ trait BasicBlocks: ICodes {
def emit(instr: Instruction) = {
assert (!closed, "BasicBlock closed.");
instructionList = instr :: instructionList;
- lastInstruction = instr;
+ _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);
+ instrs = toInstructionArray(instructionList.reverse);
}
+ /** Return the last instruction of this basic block. */
+ def lastInstruction =
+ if (closed)
+ instrs(instrs.length - 1)
+ else
+ instructionList.head;
+
/** Convert the list to an array */
def toInstructionArray(l: List[Instruction]): Array[Instruction] = {
var array = new Array[Instruction](l.length);
@@ -140,8 +152,6 @@ trait BasicBlocks: ICodes {
def isClosed = closed;
- def getLastInstruction = lastInstruction;
-
def successors : List[BasicBlock] = // here order will count
lastInstruction match {
case JUMP (where) => List(where);
diff --git a/sources/scala/tools/nsc/backend/icode/Checkers.scala b/sources/scala/tools/nsc/backend/icode/Checkers.scala
index 062780a7b0..da37f4e525 100644
--- a/sources/scala/tools/nsc/backend/icode/Checkers.scala
+++ b/sources/scala/tools/nsc/backend/icode/Checkers.scala
@@ -26,10 +26,19 @@ abstract class Checkers {
* and the number and type of arguments match the declared type of
* the method.
*
- * - for object creation: the class exists and can be instantiated.
+ * - for object creation: the constructor can be called.
*
* - for load/stores: the field/local/param exists and the type
* of the value matches that of the target.
+ *
+ * For a control flow graph it checks that type stacks at entry to
+ * each basic block 'agree':
+ *
+ * - they have the same length
+ * - there exists a lub for all types at the same position in stacks.
+ *
+ * TODO: Better checks for MONITOR_ENTER/EXIT
+ * Better checks for local var initializations
*/
class ICodeChecker {
import icodes._;
@@ -101,6 +110,11 @@ abstract class Checkers {
in(bl) = (preds map out.apply) reduceLeft meet2;
}
+
+ private var typeStack: TypeStack = null;
+ private var instruction: Instruction = null;
+ private var basicBlock: BasicBlock = null;
+
/**
* Check the basic block to be type correct and return the
* produced type stack.
@@ -108,82 +122,87 @@ abstract class Checkers {
def check(b: BasicBlock, initial: TypeStack): TypeStack = {
var stack = new TypeStack(initial);
- def checkStack(len: Int) =
- if (stack.length < len)
- ICodeChecker.this.error("Expected at least " + len + " elements on the stack", stack);
- else
- ();
-
- def checkLocal(local: Symbol) =
- method.lookupLocal(local.name) match {
- case None => error(" " + local + " is not defined in method " + method);
- case _ => ()
- }
-
- def checkField(obj: TypeKind, field: Symbol) =
- obj match {
- case REFERENCE(sym) =>
- if (sym.info.member(field.name) == NoSymbol)
- error(" " + field + " is not defined in class " + clasz);
- case _ =>
- error(" expected reference type, but " + obj + " found");
- }
-
- /** Checks that tpe is a subtype of one of the allowed types */
- def checkType(tpe: TypeKind, allowed: TypeKind*) =
- if (isOneOf(tpe, allowed: _*))
- ()
- else
- error(tpe.toString() + " is not one of: " + allowed);
-
- /** Checks that the 2 topmost elements on stack are of the
- * kind TypeKind.
- */
- def checkBinop(kind: TypeKind) = {
- val Pair(a, b) = stack.pop2;
- checkType(a, kind);
- checkType(b, kind);
- }
-
- /** Check that arguments on the stack match method params. */
- def checkMethodArgs(method: Symbol) = {
- val params = method.info.paramTypes;
- checkStack(params.length);
- params.reverse.foreach( (tpe) => checkType(stack.pop, toTypeKind(tpe)));
- }
-
- /** Checks that the object on top of the stack has a method
- * 'method' and that it is callable from the current method.
- * The object is popped from the stack.
- */
- def checkMethod(method: Symbol) =
- stack.pop match {
- case REFERENCE(sym) =>
- checkBool(sym.info.member(method.name) != NoSymbol,
- "Method " + method + " does not exist in " + sym.fullNameString);
- if (method hasFlag Flags.PRIVATE)
- checkBool(method.owner == clasz.symbol,
- "Cannot call private method of " + method.owner.fullNameString
- + " from " + clasz.symbol.fullNameString);
- else if (method hasFlag Flags.PROTECTED)
- checkBool(clasz.symbol isSubClass method.owner,
- "Cannot call protected method of " + method.owner.fullNameString
- + " from " + clasz.symbol.fullNameString);
- case t =>
- error("Not a reference type: " + t);
- }
-
- def checkBool(cond: Boolean, msg: String) =
- if (cond) () else error(msg);
-
- def error(msg: String): Unit = ICodeChecker.this.error(msg, stack);
+ this.typeStack = stack;
+ this.basicBlock = b;
def typeError(k1: TypeKind, k2: TypeKind): Unit =
error(" expected: " + k1 + " but " + k2 + " found");
b traverse (instr => {
- def error(msg: String): Unit = ICodeChecker.this.error(instr.toString() + ": " + msg, stack);
+ def checkStack(len: Int) =
+ if (stack.length < len)
+ ICodeChecker.this.error("Expected at least " + len + " elements on the stack", stack);
+ else
+ ();
+
+ def checkLocal(local: Symbol) =
+ method.lookupLocal(local.name) match {
+ case None => error(" " + local + " is not defined in method " + method);
+ case _ => ()
+ }
+
+ def checkField(obj: TypeKind, field: Symbol) =
+ obj match {
+ case REFERENCE(sym) =>
+ if (sym.info.member(field.name) == NoSymbol)
+ error(" " + field + " is not defined in class " + clasz);
+ case _ =>
+ error(" expected reference type, but " + obj + " found");
+ }
+
+ /** Checks that tpe is a subtype of one of the allowed types */
+ def checkType(tpe: TypeKind, allowed: TypeKind*) =
+ if (isOneOf(tpe, allowed: _*))
+ ()
+ else
+ error(tpe.toString() + " is not one of: " + allowed);
+
+ /** Checks that the 2 topmost elements on stack are of the
+ * kind TypeKind.
+ */
+ def checkBinop(kind: TypeKind) = {
+ val Pair(a, b) = stack.pop2;
+ checkType(a, kind);
+ checkType(b, kind);
+ }
+
+ /** Check that arguments on the stack match method params. */
+ def checkMethodArgs(method: Symbol) = {
+ val params = method.info.paramTypes;
+ checkStack(params.length);
+ params.reverse.foreach( (tpe) => checkType(stack.pop, toTypeKind(tpe)));
+ }
+
+ /** Checks that the object passed as receiver has a method
+ * 'method' and that it is callable from the current method.
+ */
+ def checkMethod(receiver: TypeKind, method: Symbol) =
+ receiver match {
+ case REFERENCE(sym) =>
+ checkBool(sym.info.member(method.name) != NoSymbol,
+ "Method " + method + " does not exist in " + sym.fullNameString);
+ if (method hasFlag Flags.PRIVATE)
+ checkBool(method.owner == clasz.symbol,
+ "Cannot call private method of " + method.owner.fullNameString
+ + " from " + clasz.symbol.fullNameString);
+ else if (method hasFlag Flags.PROTECTED)
+ checkBool(clasz.symbol isSubClass method.owner,
+ "Cannot call protected method of " + method.owner.fullNameString
+ + " from " + clasz.symbol.fullNameString);
+
+ case ARRAY(_) =>
+ checkBool(receiver.toType.member(method.name) != NoSymbol,
+ "Method " + method + " does not exist in " + receiver);
+
+ case t =>
+ error("Not a reference type: " + t);
+ }
+
+ def checkBool(cond: Boolean, msg: String) =
+ if (cond) () else error(msg);
+
+ this.instruction = instr;
log("PC: " + instr);
log("stack: " + stack);
@@ -199,8 +218,9 @@ abstract class Checkers {
checkStack(2);
stack.pop2 match {
case Pair(INT, ARRAY(elem)) =>
- if (kind != elem)
+ if (!(elem <:< kind))
typeError(kind, elem);
+ stack.push(elem);
case Pair(a, b) =>
error(" expected and INT and a array reference, but " +
a + ", " + b + " found");
@@ -222,14 +242,17 @@ abstract class Checkers {
}
stack.push(toTypeKind(field.info));
+ case LOAD_MODULE(module) =>
+ stack.push(toTypeKind(module.tpe));
+
case STORE_ARRAY_ITEM(kind) =>
checkStack(3);
stack.pop3 match {
- case Triple(ARRAY(elem), INT, k) =>
+ case Triple(k, INT, ARRAY(elem)) =>
if (!(k <:< kind))
typeError(kind, k);
- if (kind != elem)
- typeError(kind, elem);
+ if (!(k <:< elem))
+ typeError(elem, k);
case Triple(a, b, c) =>
error(" expected and array reference, and int and " + kind +
" but " + a + ", " + b + ", " + c + " found");
@@ -284,7 +307,10 @@ abstract class Checkers {
case Arithmetic(op, kind) =>
checkType(kind, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE);
- checkBinop(kind);
+ if (op == NOT)
+ checkType(stack.pop, kind)
+ else
+ checkBinop(kind);
stack push kind;
case Logical(op, kind) =>
@@ -322,17 +348,10 @@ abstract class Checkers {
case CALL_METHOD(method, style) =>
style match {
- case NewInstance =>
- checkStack(1);
- checkBool(method.name == nme.CONSTRUCTOR,
- "NewInstance invoke style to non-constructor method");
- checkMethodArgs(method);
- checkMethod(method);
-
case Dynamic =>
checkStack(1 + method.info.paramTypes.length);
checkMethodArgs(method);
- checkMethod(method);
+ checkMethod(stack.pop, method);
stack.push(toTypeKind(method.info.resultType));
case Static(onInstance) =>
@@ -341,7 +360,7 @@ abstract class Checkers {
checkBool(method.hasFlag(Flags.PRIVATE) || method.isConstructor,
"Static call to non-private method.");
checkMethodArgs(method);
- checkMethod(method);
+ checkMethod(stack.pop, method);
stack.push(toTypeKind(method.info.resultType));
} else {
checkStack(method.info.paramTypes.length);
@@ -350,15 +369,23 @@ abstract class Checkers {
}
case SuperCall(mixin) =>
- // we're not supposed to have such things!
- //error("Invalid SuperCall");
+ checkStack(1 + method.info.paramTypes.length);
+ checkMethodArgs(method);
+ checkMethod(stack.pop, method);
+ stack.push(toTypeKind(method.info.resultType));
+
}
- case NEW(clasz) =>
- stack.push(REFERENCE(clasz));
+ case NEW(ctor) =>
+ checkBool(ctor.isConstructor,
+ "'new' call to non-constructor method");
+ checkMethodArgs(ctor);
+ checkMethod(REFERENCE(ctor.owner), ctor);
+ stack.push(REFERENCE(ctor.owner));
case CREATE_ARRAY(elem) =>
checkStack(1);
+ checkType(stack.pop, INT);
stack.push(ARRAY(elem));
case IS_INSTANCE(tpe) =>
@@ -371,7 +398,7 @@ abstract class Checkers {
case SWITCH(tags, labels) =>
checkType(stack.pop, INT);
- checkBool(tags.length == labels.length + 1,
+ checkBool(tags.length == labels.length - 1,
"The number of tags and labels does not coincide.");
checkBool(labels forall (b => code.blocks contains b),
"Switch target cannot be found in code.");
@@ -398,7 +425,7 @@ abstract class Checkers {
case THROW() =>
val thrown = stack.pop;
- checkBool(thrown.toType <:<definitions.ThrowableClass.info,
+ checkBool(thrown.toType <:< definitions.ThrowableClass.tpe,
"Element on top of stack should implement 'Throwable': " + thrown);
case DROP(kind) =>
@@ -410,8 +437,15 @@ abstract class Checkers {
stack.push(top);
stack.push(top);
- case MONITOR_ENTER() => ();
- case MONITOR_EXIT() => ();
+ case MONITOR_ENTER() =>
+ checkStack(1);
+ checkBool(stack.pop.isReferenceType,
+ "MONITOR_ENTER on non-reference type");
+
+ case MONITOR_EXIT() =>
+ checkStack(1);
+ checkBool(stack.pop.isReferenceType,
+ "MONITOR_EXIT on non-reference type");
case _ => abort("Unknown instruction: " + instr);
}
@@ -421,8 +455,25 @@ abstract class Checkers {
//////////////// Error reporting /////////////////////////
- def error(msg: String): Unit =
+ def error(msg: String): Unit = {
+ System.out.println(method.toString() + " in block: " + basicBlock.label);
+ printLastIntructions;
+
Checkers.this.global.error("ICode checker: " + method + ": " + msg);
+ }
+
+ /** Prints the last 4 instructions. */
+ def printLastIntructions = {
+ var printed = 0;
+ var buf: List[Instruction] = Nil;
+
+ basicBlock.traverseBackwards( (i) =>
+ if (i == instruction || (printed > 0 && printed < 3)) {
+ buf = i :: buf;
+ printed = printed + 1;
+ });
+ buf foreach System.out.println;
+ }
def error(msg: String, stack: TypeStack): Unit = {
error(msg + "\n type stack: " + stack);
diff --git a/sources/scala/tools/nsc/backend/icode/GenICode.scala b/sources/scala/tools/nsc/backend/icode/GenICode.scala
index f22df8743b..d749adcfb8 100644
--- a/sources/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/sources/scala/tools/nsc/backend/icode/GenICode.scala
@@ -13,11 +13,11 @@ import scala.tools.nsc.symtab._;
/**
* TODO:
- * - conditionals as values
* - record line number info
* - exception handling
- * - array creation
- * - switch
+ * - synchronized blocks should add exception handlers that guarantee
+ * monitor releases in case of exceptions (like Java)?
+ * - switches with alternatives
*/
abstract class GenICode extends SubComponent {
import global._;
@@ -29,20 +29,28 @@ abstract class GenICode extends SubComponent {
override def newPhase(prev: Phase) = new ICodePhase(prev);
class ICodePhase(prev: Phase) extends StdPhase(prev) {
-// import Primitives._;
override def name = "genicode";
override def description = "Generate ICode from the AST";
var unit: CompilationUnit = _;
- scalaPrimitives.initPrimitives;
-
+ // We assume definitions are alread initialized
val STRING = REFERENCE(definitions.StringClass);
- val ANY_REF_CLASS = REFERENCE(definitions.AnyRefClass);
+
+ // this depends on the backend! should be changed.
+ val ANY_REF_CLASS = REFERENCE(definitions.ObjectClass);
+
+ val SCALA_ALL = REFERENCE(definitions.AllClass);
+ 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);
@@ -67,6 +75,7 @@ abstract class GenICode extends SubComponent {
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;
@@ -75,7 +84,9 @@ abstract class GenICode extends SubComponent {
// !! modules should be eliminated by refcheck... or not?
case ModuleDef(mods, name, impl) =>
+ 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;
@@ -95,6 +106,7 @@ abstract class GenICode extends SubComponent {
else
toTypeKind(ctx1.method.symbol.info.resultType));
ctx1.bb.emit(RETURN());
+ ctx1.bb.close;
ctx1;
case Template(parents, body) =>
@@ -144,7 +156,7 @@ abstract class GenICode extends SubComponent {
*/
private def genStat(tree: Tree, ctx: Context): Context = tree match {
case Assign(lhs @ Select(_, _), rhs) =>
- if (lhs.symbol.isStatic) {
+ if (isStaticSymbol(lhs.symbol)) {
val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info));
ctx1.bb.emit(STORE_FIELD(lhs.symbol, true));
ctx1
@@ -156,8 +168,8 @@ abstract class GenICode extends SubComponent {
}
case Assign(lhs, rhs) =>
- assert(ctx.method.locals.contains(lhs.symbol),
- "Assignment to inexistent local: " + lhs.symbol);
+// 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));
ctx1.bb.emit(STORE_LOCAL(lhs.symbol, lhs.symbol.isValueParameter));
ctx1
@@ -193,15 +205,15 @@ abstract class GenICode extends SubComponent {
assert(resKind.isNumericType | resKind == BOOL,
resKind.toString() + " is not a numeric or boolean type [operation: " + fun.symbol + "]");
- ctx1 = genLoad(larg, ctx1, resKind);
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)));
case scalaPrimitives.NOT => ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(NOT, resKind)));
- case _ => abort("Unknown unary operation: " + fun.symbol);
+ case _ => abort("Unknown unary operation: " + fun.symbol.fullNameString + " code: " + code);
}
generatedType = resKind;
@@ -212,6 +224,7 @@ abstract class GenICode extends SubComponent {
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);
@@ -339,12 +352,13 @@ abstract class GenICode extends SubComponent {
genLoad(rhs, ctx1, toTypeKind(tree.symbol.info.resultType));
case ValDef(_, _, _, rhs) =>
- assert(rhs != EmptyTree,
- "Uninitialized variable " + tree + " at: " + unit.position(tree.pos));
+ if (rhs == EmptyTree)
+ log("Uninitialized variable " + tree + " at: " + unit.position(tree.pos));
val sym = tree.symbol;
ctx.method.addLocal(sym);
val ctx1 = genLoad(rhs, ctx, toTypeKind(sym.info));
- ctx1.bb.emit(STORE_LOCAL(sym, false));
+ if (rhs != EmptyTree)
+ ctx1.bb.emit(STORE_LOCAL(sym, false));
generatedType = UNIT;
ctx1
@@ -358,12 +372,16 @@ abstract class GenICode extends SubComponent {
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 ctx1 = genLoad(expr, ctx, expectedType);
ctx1.bb.emit(RETURN());
+ // although strange, 'return' does not necessarily close a block
+ //ctx1.bb.close;
ctx1
case Try(block, catches, finalizer) =>
@@ -379,24 +397,13 @@ abstract class GenICode extends SubComponent {
ctx1
case Throw(expr) =>
- val ctx1 = genLoad(expr, ctx, expectedType);
+ val ctx1 = genLoad(expr, ctx, THROWABLE);
ctx1.bb.emit(THROW());
+ generatedType = SCALA_ALL;
ctx;
case New(tpt) =>
- val sym = tree.tpe.symbol;
- generatedType = toTypeKind(tpt.tpe);
- generatedType match {
- case ARRAY(elem) =>
- ctx.bb.emit(CREATE_ARRAY(elem));
- case REFERENCE(cls) =>
- ctx.bb.emit(NEW(cls));
- assert(sym == cls,
- "Symbol " + sym.fullNameString + "is different than " + tpt);
- case _ =>
- abort("Cannot instantiate " + tpt + "of kind: " + generatedType);
- }
- ctx;
+ abort("Unexpected New");
case Apply(TypeApply(fun, targs), _) =>
val sym = fun.symbol;
@@ -407,12 +414,14 @@ abstract class GenICode extends SubComponent {
generatedType = BOOL;
} else if (sym == definitions.Object_asInstanceOf) {
ctx1.bb.emit(CHECK_CAST(targs.head.tpe));
- generatedType = REFERENCE(targs.head.tpe.symbol);
+ generatedType = toTypeKind(targs.head.tpe);
} else
abort("Unexpected type application " + fun + "[sym: " + sym + "]");
ctx1
+ // super call
case Apply(fun @ Select(Super(_, mixin), _), args) =>
+ log("Call to super: " + tree);
val invokeStyle =
if (fun.symbol.isConstructor) Static(true) else SuperCall(mixin);
@@ -423,6 +432,25 @@ abstract class GenICode extends SubComponent {
generatedType = toTypeKind(fun.symbol.info.resultType);
ctx1
+ // 'new' constructor call
+ case Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) =>
+ val ctor = fun.symbol;
+ assert(fun != null && ctor.isClassConstructor,
+ "'new' call to non-constructor: " + tree);
+ var ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx);
+ generatedType = toTypeKind(tpt.tpe);
+ generatedType match {
+ case ARRAY(elem) =>
+ ctx1.bb.emit(CREATE_ARRAY(elem));
+ case REFERENCE(cls) =>
+ ctx1.bb.emit(NEW(ctor));
+ assert(ctor.owner == cls,
+ "Symbol " + ctor.owner.fullNameString + "is different than " + tpt);
+ case _ =>
+ abort("Cannot instantiate " + tpt + "of kind: " + generatedType);
+ }
+ ctx1
+
case Apply(fun, args) =>
val sym = fun.symbol;
@@ -460,7 +488,9 @@ abstract class GenICode extends SubComponent {
generatedType = STRING;
} else if (scalaPrimitives.isArrayOp(code)) {
ctx1 = genArrayOp(tree, ctx1, code);
- } else if (scalaPrimitives.isLogicalOp(code)) {
+ } else if (scalaPrimitives.isLogicalOp(code) ||
+ scalaPrimitives.isComparisonOp(code)) {
+
val trueCtx = ctx1.newBlock;
val falseCtx = ctx1.newBlock;
val afterCtx = ctx1.newBlock;
@@ -473,16 +503,24 @@ abstract class GenICode extends SubComponent {
falseCtx.bb.emit(JUMP(afterCtx.bb));
falseCtx.bb.close;
ctx1 = afterCtx;
+ } else if (code == scalaPrimitives.SYNCHRONIZED) {
+ ctx1 = genLoadQualifier(fun, ctx1);
+ ctx1.bb.emit(MONITOR_ENTER());
+ ctx1 = genLoad(args.head, ctx1, toTypeKind(tree.tpe.resultType));
+ ctx1 = genLoadQualifier(fun, ctx1);
+ ctx1.bb.emit(MONITOR_EXIT());
+ } 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
+ log("Gen CALL_METHOD with sym: " + sym + " isStaticSymbol: " + isStaticSymbol(sym));
var invokeStyle =
- if (sym.isClassConstructor)
- NewInstance;
- else if (sym.isStatic)
+ if (isStaticSymbol(sym))
Static(false)
else if (sym hasFlag Flags.PRIVATE)
Static(true)
@@ -493,8 +531,7 @@ abstract class GenICode extends SubComponent {
genLoadQualifier(fun, ctx);
else
ctx;
- if (sym.isClassConstructor)
- ctx1.bb.emit(DUP(toTypeKind(fun.symbol.info.resultType))); // hack!
+
ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx1);
ctx1.bb.emit(CALL_METHOD(sym, invokeStyle));
@@ -503,32 +540,48 @@ abstract class GenICode extends SubComponent {
}
case This(qual) =>
- assert(tree.symbol == ctx.clazz.symbol,
+ 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);
- ctx.bb.emit(THIS(ctx.clazz.symbol));
- generatedType = REFERENCE(ctx.clazz.symbol);
+ if (tree.symbol.isModuleClass && tree.symbol != ctx.clazz.symbol) {
+ ctx.bb.emit(LOAD_MODULE(tree.symbol));
+ generatedType = REFERENCE(tree.symbol);
+ } else {
+ ctx.bb.emit(THIS(ctx.clazz.symbol));
+ 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));
+ ctx.bb.emit(LOAD_MODULE(tree.symbol));
+ ctx
+
case Select(qualifier, selector) =>
val sym = tree.symbol;
val generatedType = toTypeKind(sym.info);
- if (sym.isStatic) {
+ if (sym.isModule) {
+ ctx.bb.emit(LOAD_MODULE(sym));
+ ctx
+ } else if (isStaticSymbol(sym)) {
ctx.bb.emit(LOAD_FIELD(sym, true));
ctx
} else {
- val ctx1 = genLoadQualifier(tree, ctx); // !! attention
+ val ctx1 = genLoadQualifier(tree, ctx);
ctx1.bb.emit(LOAD_FIELD(sym, false));
ctx1
}
case Ident(name) =>
- if (tree.symbol.isModule)
- abort("Modules are not handled yet");
- else {
-// assert(ctx.method.locals contains tree.symbol, "Unkown local " + tree.symbol + "[" + name + "]");
- ctx.bb.emit(LOAD_LOCAL(tree.symbol, tree.symbol.isValueParameter ));
+ if (!tree.symbol.isPackage) {
+ if (tree.symbol.isModule)
+ ctx.bb.emit(LOAD_MODULE(tree.symbol));
+ else
+ ctx.bb.emit(LOAD_LOCAL(tree.symbol, tree.symbol.isValueParameter));
generatedType = toTypeKind(tree.symbol.info);
}
ctx
@@ -570,6 +623,44 @@ abstract class GenICode extends SubComponent {
}
ctx1
+ case Match(selector, cases) =>
+ 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));
+ 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));
+ 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));
+ ctx1.bb.close;
+ afterCtx
+
case EmptyTree => ctx;
case _ => abort("Unexpected tree in genLoad: " + tree + " at: " + unit.position(tree.pos));
@@ -587,12 +678,16 @@ abstract class GenICode extends SubComponent {
/** 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);
- }
+ 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.
@@ -633,13 +728,45 @@ abstract class GenICode extends SubComponent {
if (scalaPrimitives.isPrimitive(fun))
scalaPrimitives.getPrimitive(fun) match {
- case EQUALS | HASHCODE | TOSTRING | COERCE => false;
+ case EQUALS | HASHCODE | TOSTRING => false;
case _ => true;
}
else
false;
}
+ def genCoercion(tree: Tree, ctx: Context, code: Int) = {
+ import scalaPrimitives._;
+ code match {
+ case B2S => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, SHORT)));
+ case B2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, INT)));
+ case B2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, LONG)));
+ case B2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, FLOAT)));
+ case B2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(BYTE, DOUBLE)));
+
+ case S2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, INT)));
+ case S2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, LONG)));
+ case S2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, FLOAT)));
+ case S2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(SHORT, DOUBLE)));
+
+ case C2I => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, INT)));
+ case C2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, LONG)));
+ case C2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, FLOAT)));
+ case C2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(CHAR, DOUBLE)));
+
+ case I2L => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, LONG)));
+ case I2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, FLOAT)));
+ case I2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, DOUBLE)));
+
+ case L2F => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, FLOAT)));
+ case L2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(LONG, DOUBLE)));
+
+ case F2D => ctx.bb.emit(CALL_PRIMITIVE(Conversion(FLOAT, DOUBLE)));
+
+ case _ => abort("Unknown coercion primitive: " + code);
+ }
+ }
+
/** Generate string concatenation. */
def genStringConcat(tree: Tree, ctx: Context): Context = {
val Apply(Select(larg, _), rarg) = tree;
@@ -688,7 +815,27 @@ abstract class GenICode extends SubComponent {
private def genCond(tree: Tree,
ctx: Context,
thenCtx: Context,
- elseCtx: Context): Unit = {
+ 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));
+ ctx1.bb.close;
+ }
+
log("Entering genCond with tree: " + tree);
tree match {
@@ -696,51 +843,43 @@ abstract class GenICode extends SubComponent {
if isPrimitive(fun.symbol) =>
assert(args.length <= 1,
"Too many arguments for primitive function: " + fun.symbol);
-
- val Select(leftArg, _) = fun;
- var kind: TypeKind = toTypeKind(leftArg.tpe);
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) &&
- (kind.isReferenceType)) {
- genEqEqPrimitive(leftArg, args.head, ctx, thenCtx, elseCtx);
+ } else if ((code == scalaPrimitives.EQ ||
+ code == scalaPrimitives.NE)) {
+ val Select(leftArg, _) = fun;
+ if (toTypeKind(leftArg.tpe).isReferenceType)
+ genEqEqPrimitive(leftArg, args.head, ctx, thenCtx, elseCtx);
+ else
+ genComparisonOp(leftArg, args.head, code);
} else if (scalaPrimitives.isComparisonOp(code)) {
-
- 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);
- };
-
- kind = getMaxType(leftArg.tpe :: (args map (.tpe)));
- var ctx1 = genLoad(leftArg, ctx, kind);
- ctx1 = genLoad(args.head, ctx1, kind);
- ctx1.bb.emit(CJUMP(thenCtx.bb, elseCtx.bb, op, kind));
- ctx1.bb.close;
- } else
+ 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, kind));
+ ctx1.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL));
ctx1.bb.close;
}
+ }
case _ =>
var ctx1 = genLoad(tree, ctx, BOOL);
diff --git a/sources/scala/tools/nsc/backend/icode/ICodes.scala b/sources/scala/tools/nsc/backend/icode/ICodes.scala
index b9a37dae7f..bcbd8f37d8 100644
--- a/sources/scala/tools/nsc/backend/icode/ICodes.scala
+++ b/sources/scala/tools/nsc/backend/icode/ICodes.scala
@@ -19,6 +19,7 @@ abstract class ICodes extends AnyRef
with TypeKinds
with ExceptionHandlers
with Primitives
+ with Linearizers
{
val global: Global;
diff --git a/sources/scala/tools/nsc/backend/icode/Opcodes.scala b/sources/scala/tools/nsc/backend/icode/Opcodes.scala
index 0916a7c5a6..7f744da83f 100644
--- a/sources/scala/tools/nsc/backend/icode/Opcodes.scala
+++ b/sources/scala/tools/nsc/backend/icode/Opcodes.scala
@@ -18,6 +18,7 @@ import scala.tools.nsc.ast._;
case LOAD_ARRAY_ITEM(kind) =>
case LOAD_LOCAL(local, isArg) =>
case LOAD_FIELD(field, isStatic) =>
+ case LOAD_MODULE(module) =>
case STORE_ARRAY_ITEM(kind) =>
case STORE_LOCAL(local, isArg) =>
case STORE_FIELD(field, isStatic) =>
@@ -47,7 +48,7 @@ import scala.tools.nsc.ast._;
* in the source files.
*/
abstract class Opcodes: ICodes {
- import global.{Symbol, Type, Name, Constant};
+ import global.{Symbol, NoSymbol, Type, Name, Constant};
/** This class represents an instruction of the intermediate code.
* Each case subclass will represent a specific operation.
@@ -97,7 +98,7 @@ abstract class Opcodes: ICodes {
*/
case class LOAD_ARRAY_ITEM(kind: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
- override def toString(): String = "LOAD_ARRAY_ITEM";
+ override def toString(): String = "LOAD_ARRAY_ITEM (" + kind + ")";
override def consumed = 2;
override def produced = 1;
@@ -129,13 +130,24 @@ abstract class Opcodes: ICodes {
override def produced = 1;
}
+ case class LOAD_MODULE(module: Symbol) extends Instruction {
+ assert(module != NoSymbol,
+ "Invalid module symbol");
+ /** Returns a string representation of this instruction */
+ override def toString(): String =
+ "LOAD_MODULE " + module.toString();
+
+ override def consumed = 0;
+ 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(kind: TypeKind) extends Instruction {
/** Returns a string representation of this instruction */
- override def toString(): String = "STORE_ARRAY_ITEM";
+ override def toString(): String = "STORE_ARRAY_ITEM (" + kind + ")";
override def consumed = 3;
override def produced = 0;
@@ -193,10 +205,6 @@ abstract class Opcodes: ICodes {
* Stack: ...:ref:arg1:arg2:...:argn
* ->: ...:result
*
- * STYLE: new - unused by jvm
- * Stack: ...:arg1:arg2:...:argn
- * ->: ...:ref
- *
* STYLE: static(StaticClass)
* Stack: ...:arg1:arg2:...:argn
* ->: ...:result
@@ -220,13 +228,13 @@ abstract class Opcodes: ICodes {
override def produced = 1;
}
- /** Create a new instance of a class.
- * Stack: ...:
+ /** Create a new instance of a class through the specified constructor
+ * Stack: ...:arg1:arg2:...:argn
* ->: ...:ref
*/
- case class NEW(clasz: Symbol) extends Instruction {
+ case class NEW(ctor: Symbol) extends Instruction {
/** Returns a string representation of this instruction */
- override def toString(): String = "NEW "+clasz.toString();
+ override def toString(): String = "NEW "+ctor.fullNameString;
override def consumed = 0;
override def produced = 1;
@@ -277,7 +285,7 @@ abstract class Opcodes: ICodes {
* an array of ints, any of which will trigger the jump to the corresponding label.
* labels should contain an extra label, which is the 'default' jump.
*/
- case class SWITCH(tags: Array[Array[int]], labels: List[BasicBlock]) extends Instruction {
+ case class SWITCH(tags: List[List[Int]], labels: List[BasicBlock]) extends Instruction {
/** Returns a string representation of this instruction */
override def toString(): String ="SWITCH ...";
@@ -408,11 +416,6 @@ abstract class Opcodes: ICodes {
/** 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 {
@@ -430,13 +433,12 @@ abstract class Opcodes: ICodes {
def hasInstance: Boolean = this match {
case Dynamic => true;
case Static(onInstance) => onInstance;
- case NewInstance => true;
+ case SuperCall(_) => true;
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";
@@ -444,7 +446,6 @@ abstract class Opcodes: ICodes {
}
}
- case object NewInstance extends InvokeStyle;
case object Dynamic extends InvokeStyle;
/**
diff --git a/sources/scala/tools/nsc/backend/icode/Printers.scala b/sources/scala/tools/nsc/backend/icode/Printers.scala
index 5a7bb46ab5..717af7ef73 100644
--- a/sources/scala/tools/nsc/backend/icode/Printers.scala
+++ b/sources/scala/tools/nsc/backend/icode/Printers.scala
@@ -15,11 +15,15 @@ abstract class Printers {
import global.icodes.opcodes._;
import global.icodes._;
- class TextPrinter(out: PrintWriter) {
+ class TextPrinter(writer: PrintWriter) {
var margin = 0;
+ var out = writer;
+ val linearizer = new NormalLinearizer();
final val TAB = 2;
+ def setWriter(w: PrintWriter) = (out = w);
+
def indent = margin = margin + TAB;
def undent = margin = margin - TAB;
@@ -84,7 +88,8 @@ abstract class Printers {
}
def printCode(code: Code): Unit = {
- code traverse printBlock;
+// code traverse printBlock;
+ linearizer.linearize(code) foreach printBlock;
}
def printBlock(bb: BasicBlock): Unit = {
diff --git a/sources/scala/tools/nsc/backend/icode/TypeKinds.scala b/sources/scala/tools/nsc/backend/icode/TypeKinds.scala
index 51bfe8d4f9..88f2223bc8 100644
--- a/sources/scala/tools/nsc/backend/icode/TypeKinds.scala
+++ b/sources/scala/tools/nsc/backend/icode/TypeKinds.scala
@@ -210,6 +210,8 @@ abstract class TypeKinds: ICodes {
case class REFERENCE(cls: Symbol) extends TypeKind {
assert(cls != null,
"REFERENCE to null class symbol.");
+ assert(cls != definitions.ArrayClass,
+ "REFERENCE to Array is not allowed, should be ARRAY[..] instead");
override def toString(): String =
"REFERENCE(" + cls.fullNameString + ")";
@@ -228,7 +230,9 @@ abstract class TypeKinds: ICodes {
/** Checks subtyping relationship. */
override def <:<(other: TypeKind): Boolean =
- other match {
+ if (cls == definitions.AllClass)
+ true
+ else other match {
case REFERENCE(cls2) =>
cls.tpe <:< cls2.tpe;
case _ => false;
@@ -267,6 +271,10 @@ abstract class TypeKinds: ICodes {
other match {
case ARRAY(elem2) =>
elem <:< elem2;
+ case REFERENCE(sym) =>
+ sym == definitions.AnyRefClass ||
+ sym == definitions.ObjectClass; // TODO: platform dependent!
+
case _ => false;
}
@@ -303,7 +311,7 @@ abstract class TypeKinds: ICodes {
REFERENCE(sym);
}
- case _ => abort("Unknown type");
+ case _ => abort("Unknown type: " + t);
}
/** A map from scala primitive Types to ICode TypeKinds */