diff options
author | Iulian Dragos <jaguarul@gmail.com> | 2007-10-08 14:34:59 +0000 |
---|---|---|
committer | Iulian Dragos <jaguarul@gmail.com> | 2007-10-08 14:34:59 +0000 |
commit | c4181f656d306a986549ef990a1f531313bee420 (patch) | |
tree | b2d4e4cd6ca20ed36e359bfaab4d25f0aa9e2d3c /src/compiler | |
parent | 9ce1dd8d50095a64a68bc86d5f5a856209eaf1f2 (diff) | |
download | scala-c4181f656d306a986549ef990a1f531313bee420.tar.gz scala-c4181f656d306a986549ef990a1f531313bee420.tar.bz2 scala-c4181f656d306a986549ef990a1f531313bee420.zip |
Improved/refactored parts of the optimization p...
Improved/refactored parts of the optimization phases, removed option
Ybytecode-read (enabled now by -optimise).
Diffstat (limited to 'src/compiler')
9 files changed, 175 insertions, 77 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 4037a0a5c7..45a68c5a16 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -17,7 +17,7 @@ import scala.tools.nsc.util.{ClassPath, SourceFile, BatchSourceFile} import scala.collection.mutable.{HashSet, HashMap, ListBuffer} import symtab._ -import symtab.classfile.{PickleBuffer, Pickler, ICodeReader} +import symtab.classfile.{PickleBuffer, Pickler} import util.Statistics import plugins.Plugins import ast._ @@ -72,10 +72,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends Trees val global: Global.this.type = Global.this } - object icodeReader extends ICodeReader { - val global: Global.this.type = Global.this - } - object analysis extends TypeFlowAnalysis { val global: Global.this.type = Global.this } diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index dc4afa22c0..46d0c9ab0a 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -12,6 +12,7 @@ import java.io.PrintWriter import scala.collection.mutable.HashMap import scala.tools.nsc.symtab._ import analysis.{Liveness, ReachingDefinitions} +import scala.tools.nsc.symtab.classfile.ICodeReader /** Glue together ICode parts. * @@ -27,6 +28,7 @@ abstract class ICodes extends AnyRef with Primitives with Linearizers with Printers + with Repository { val global: Global @@ -74,6 +76,10 @@ abstract class ICodes extends AnyRef settings.Xdce.value = true } + object icodeReader extends ICodeReader { + lazy val global: ICodes.this.global.type = ICodes.this.global + } + /** A phase which works on icode. */ abstract class ICodePhase(prev: Phase) extends global.GlobalPhase(prev) { override def erasedTypes = true diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala index 27342deb85..79e1764a31 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala @@ -192,6 +192,52 @@ abstract class ReachingDefinitions { IState(locals, stack) } + /** Return the instructions that produced the 'm' elements on the stack, below given 'depth'. + * for instance, findefs(bb, idx, 1, 1) returns the instructions that might have produced the + * value found below the topmost element of the stack. + */ + def findDefs(bb: BasicBlock, idx: Int, m: Int, depth: Int): List[(BasicBlock, Int)] = if (idx > 0) { + assert(bb.isClosed) + var instrs = bb.getArray + var res: List[(BasicBlock, Int)] = Nil + var i = idx + var n = m + var d = depth + // "I look for who produced the 'n' elements below the 'd' topmost slots of the stack" + while (n > 0 && i > 0) { + i -= 1 + val prod = instrs(i).produced + if (prod > d) { + res = (bb, i) :: res + n = n - (prod - d) + if (bb(i) != LOAD_EXCEPTION) + d = instrs(i).consumed + } else { + d -= prod + d += instrs(i).consumed + } + } + + if (n > 0) { + val stack = this.in(bb).stack + assert(stack.length >= n, "entry stack is too small, expected: " + n + " found: " + stack) + stack.drop(d).take(n) foreach { defs => + res = defs.toList ::: res + } + } + res + } else { + val stack = this.in(bb).stack + assert(stack.length >= m, "entry stack is too small, expected: " + m + " found: " + stack) + stack.take(m) flatMap (_.toList) + } + + /** Return the definitions that produced the topmost 'm' elements on the stack, + * and that reach the instruction at index 'idx' in basic block 'bb'. + */ + def findDefs(bb: BasicBlock, idx: Int, m: Int): List[(BasicBlock, Int)] = + findDefs(bb, idx, m, 0) + override def toString: String = { val sb = new compat.StringBuilder sb.append("rdef: \n") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index da9a38eef7..f793346d66 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -1348,7 +1348,7 @@ abstract class GenJVM extends SubComponent { && !sym.enclClass.hasFlag(Flags.INTERFACE) && !sym.isClassConstructor) ACC_FINAL else 0) jf = jf | (if (isStaticSymbol(sym)) ACC_STATIC else 0) - jf = jf | (if (sym hasFlag Flags.SYNTHETIC) ACC_SYNTHETIC else 0) + jf = jf | (if (sym hasFlag Flags.ACCESSOR) ACC_SYNTHETIC else 0) jf } diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index de00968145..3bcb9e98bf 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -105,7 +105,7 @@ abstract class DeadCodeElimination extends SubComponent { case CALL_METHOD(m1, SuperCall(_)) => worklist += ((bb, idx)) // super calls to constructor case DROP(_) => - val necessary = findDefs(bb, idx, 1) exists { p => + val necessary = rdef.findDefs(bb, idx, 1) exists { p => val (bb1, idx1) = p bb1(idx1) match { case CALL_METHOD(m1, _) if isSideEffecting(m1) => true @@ -152,7 +152,7 @@ abstract class DeadCodeElimination extends SubComponent { () case _ => - for ((bb1, idx1) <- findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) { + for ((bb1, idx1) <- rdef.findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) { log("\tAdding " + bb1(idx1)) worklist += ((bb1, idx1)) } @@ -210,7 +210,8 @@ abstract class DeadCodeElimination extends SubComponent { for ((i, idx) <- bb.toList.zipWithIndex) { if (!useful(bb)(idx)) { for ((consumedType, depth) <- i.consumedTypes.reverse.zipWithIndex) { - val defs = findDefs(bb, idx, i.consumed, depth) + log("Finding definitions of: " + i + "\n\t" + consumedType + " at depth: " + depth) + val defs = rdef.findDefs(bb, idx, 1, depth) for (d <- defs) { if (!compensations.isDefinedAt(d)) compensations(d) = List(DROP(consumedType)) @@ -246,52 +247,6 @@ abstract class DeadCodeElimination extends SubComponent { abort("could not find init in: " + method) } - /** Return the instructions that produced the 'm' elements on the stack, below given 'depth'. - * for instance, findefs(bb, idx, 1, 1) returns the instructions that might have produced the - * value found below the topmost element of the stack. - */ - def findDefs(bb: BasicBlock, idx: Int, m: Int, depth: Int): List[(BasicBlock, Int)] = if (idx > 0) { - assert(bb.isClosed) - var instrs = bb.getArray - var res: List[(BasicBlock, Int)] = Nil - var i = idx - var n = m - var d = 0 - // "I look for who produced the 'n' elements below the 'd' topmost slots of the stack" - while (n > 0 && i > 0) { - i -= 1 - val prod = instrs(i).produced - if (prod > d) { - res = (bb, i) :: res - n = n - (prod - d) - if (bb(i) != LOAD_EXCEPTION) - d = instrs(i).consumed - } else { - d -= prod - d += instrs(i).consumed - } - } - - if (n > 0) { - val stack = rdef.in(bb).stack - assert(stack.length >= n, "entry stack is too small, expected: " + n + " found: " + stack) - stack.drop(d).take(n) foreach { defs => - res = defs.toList ::: res - } - } - res - } else { - val stack = rdef.in(bb).stack - assert(stack.length >= m, "entry stack is too small, expected: " + m + " found: " + stack) - stack.take(m) flatMap (_.toList) - } - - /** Return the definitions that produced the topmost 'm' elements on the stack, - * and that reach the instruction at index 'idx' in basic block 'bb'. - */ - def findDefs(bb: BasicBlock, idx: Int, m: Int): List[(BasicBlock, Int)] = - findDefs(bb, idx, m, 0) - /** Is 'sym' a side-effecting method? TODO: proper analysis. */ private def isSideEffecting(sym: Symbol): Boolean = { !(sym.isGetter // for testing only diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index c7d33a7693..8dabbd3173 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -309,16 +309,21 @@ abstract class Inliners extends SubComponent { log("\tlooked up method: " + concreteMethod.fullNameString) } + if (receiver == definitions.PredefModule.moduleClass) { + log("loading predef") + icodes.icode(receiver, true) + } if (settings.debug.value) log("Treating " + i - + "\n\tclasses.contains: " + classes.contains(receiver) + + "\n\treceiver: " + receiver + + "\n\ticodes.available: " + icodes.available(receiver) + "\n\tconcreteMethod.isFinal: " + concreteMethod.isFinal); - if ( classes.contains(receiver) + if ( icodes.available(receiver) && (isClosureClass(receiver) || concreteMethod.isFinal || receiver.isFinal)) { - classes(receiver).lookupMethod(concreteMethod) match { + icodes.icode(receiver).get.lookupMethod(concreteMethod) match { case Some(inc) => if (inc.symbol != m.symbol && (inlinedMethods(inc.symbol) < 2) diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 13180aade2..c9dc436b6d 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -301,6 +301,7 @@ trait Definitions { var PatternWildcard: Symbol = _ // boxed classes + lazy val BoxesUtilityClass = getModule("scala.runtime.BoxesUtility") lazy val BoxedArrayClass = getClass("scala.runtime.BoxedArray") lazy val BoxedAnyArrayClass = getClass("scala.runtime.BoxedAnyArray") lazy val BoxedObjectArrayClass = getClass("scala.runtime.BoxedObjectArray") diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index d9300c8a17..66ef759e79 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -263,9 +263,10 @@ abstract class ClassfileParser { c = sigToType(null, name) values(index) = c } else { - val sym = if (name.endsWith("$")) definitions.getModule(name.subName(0, name.length - 1)) + val sym = classNameToSymbol(name) + /*if (name.endsWith("$")) definitions.getModule(name.subName(0, name.length - 1)) else if (name.endsWith("$class")) definitions.getModule(name) - else definitions.getClass(name) + else definitions.getClass(name)*/ values(index) = sym c = sym.tpe } @@ -403,7 +404,7 @@ abstract class ClassfileParser { val jflags = in.nextChar var sflags = transFlags(jflags) if ((sflags & FINAL) == 0) sflags = sflags | MUTABLE - if ((sflags & PRIVATE) != 0 && !global.settings.XbytecodeRead.value) { + if ((sflags & PRIVATE) != 0 && !global.settings.XO.value) { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) @@ -420,14 +421,14 @@ abstract class ClassfileParser { def parseMethod() { val jflags = in.nextChar var sflags = transFlags(jflags) - if ((jflags & JAVA_ACC_PRIVATE) != 0 && !global.settings.XbytecodeRead.value) { + if ((jflags & JAVA_ACC_PRIVATE) != 0 && !global.settings.XO.value) { val name = pool.getName(in.nextChar) if (name == nme.CONSTRUCTOR) sawPrivateConstructor = true in.skip(2); skipAttributes() } else { if ((jflags & JAVA_ACC_BRIDGE) != 0) sflags = sflags | BRIDGE //PRIVATE - if ((sflags & PRIVATE) != 0 && !global.settings.XbytecodeRead.value) { + if ((sflags & PRIVATE) != 0 && global.settings.XO.value) { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) @@ -478,6 +479,7 @@ abstract class ClassfileParser { case BOOL_TAG => definitions.BooleanClass.tpe case 'L' => { val classSym = classNameToSymbol(subName(c => ((c == ';') || (c == '<')))) + assert(!classSym.hasFlag(OVERLOADED), classSym.alternatives) val existentials = new ListBuffer[Symbol]() val tpe: Type = if (sig(index) == '<') { assert(sym != null) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index eedee0a048..9dce5031ea 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -45,7 +45,7 @@ abstract class ICodeReader extends ClassfileParser { var sym = cls isScalaModule = cls.isModule && !cls.hasFlag(JAVA) log("Reading class: " + cls + " isScalaModule?: " + isScalaModule) - val name = cls.fullNameString(java.io.File.separatorChar) + (if (isScalaModule) "$" else "") + val name = cls.fullNameString(java.io.File.separatorChar) + (if (sym.hasFlag(MODULE)) "$" else "") val entry = classPath.root.find(name, false) if (entry ne null) { classFile = entry.classFile @@ -53,7 +53,6 @@ abstract class ICodeReader extends ClassfileParser { //sym = cls.linkedClassOfModule assert(classFile ne null, "No classfile for " + cls) - println("sym = " + sym) // for (s <- cls.info.members) // Console.println("" + s + ": " + s.tpe) this.instanceCode = new IClass(sym) @@ -94,12 +93,12 @@ abstract class ICodeReader extends ClassfileParser { } override def parseField(): Unit = { - val (jflags, sym) = parseMember() + val (jflags, sym) = parseMember(true) getCode(jflags).addField(new IField(sym)) skipAttributes() } - private def parseMember(): (Int, Symbol) = { + private def parseMember(field: Boolean): (Int, Symbol) = { val jflags = in.nextChar val name = pool.getName(in.nextChar) var tpe = pool.getType(in.nextChar) @@ -113,19 +112,55 @@ abstract class ICodeReader extends ClassfileParser { if ("<clinit>" == name.toString) (jflags, NoSymbol) else { - var sym = getOwner(jflags).info.member(name).suchThat(old => old.tpe =:= tpe); + val owner = getOwner(jflags) + var sym = owner.info.member(name).suchThat(old => sameType(old.tpe, tpe)); if (sym == NoSymbol) - sym = getOwner(jflags).info.member(newTermName(name.toString + nme.LOCAL_SUFFIX)).suchThat(old => old.tpe =:= tpe); - if (sym == NoSymbol) - Console.println("Could not find symbol for " + name + ": " + tpe); + sym = owner.info.member(newTermName(name.toString + nme.LOCAL_SUFFIX)).suchThat(old => old.tpe =:= tpe); + if (sym == NoSymbol) { + log("Could not find symbol for " + name + ": " + tpe/* + " in " + owner.info.decls*/) + log(owner.info.member(name).tpe + " : " + tpe) + if (field) + sym = owner.newValue(owner.pos, name).setInfo(tpe).setFlag(MUTABLE | javaToScalaFlags(jflags)) + else + sym = owner.newMethod(owner.pos, name).setInfo(tpe).setFlag(javaToScalaFlags(jflags)) + owner.info.decls.enter(sym) + log("added " + sym + ": " + sym.tpe) + } (jflags, sym) } } + private def javaToScalaFlags(flags: Int): Long = { + import ch.epfl.lamp.fjbg.JAccessFlags._ + + var res = 0L + if ((flags & ACC_PRIVATE) == 1) res |= Flags.PRIVATE + if ((flags & ACC_PROTECTED) == 1) res |= Flags.PROTECTED + if ((flags & ACC_FINAL) == 1) res |= Flags.FINAL + if ((flags & ACC_ABSTRACT) == 1) res |= Flags.DEFERRED + if ((flags & ACC_SYNTHETIC) == 1) res |= Flags.SYNTHETIC + + res + } + + /** Checks if tp1 is the same type as tp2, modulo implict methods. + * We don't care about the distinction between implcit and explicit + * methods as this point, and we can't get back the information from + * bytecode anyway. + */ + private def sameType(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { + case (MethodType(args1, resTpe1), MethodType(args2, resTpe2)) => + if (tp1.isInstanceOf[ImplicitMethodType] || tp2.isInstanceOf[ImplicitMethodType]) { + MethodType(args1, resTpe1) =:= MethodType(args2, resTpe2) + } else + tp1 =:= tp2 + case _ => tp1 =:= tp2 + } + override def parseMethod() { - val (jflags, sym) = parseMember() + val (jflags, sym) = parseMember(false) if (sym != NoSymbol) { - Console.println("Parsing method " + sym.fullNameString + ": " + sym.tpe); + log("Parsing method " + sym.fullNameString + ": " + sym.tpe); this.method = new IMethod(sym); getCode(jflags).addMethod(this.method) if ((jflags & JAVA_ACC_NATIVE) != 0) @@ -154,10 +189,16 @@ abstract class ICodeReader extends ClassfileParser { definitions.AllClass else if (name == nullName) definitions.AllRefClass - else if (name.endsWith("$")) + else if (name.endsWith("$class")) { + val iface = definitions.getClass(name.subName(0, name.length - "$class".length)) + log("forcing " + iface) + iface.info // force the mixin type-transformer + definitions.getClass(name) + } else if (name.endsWith("$")) definitions.getModule(name.subName(0, name.length - 1)) else definitions.getClass(name) + //super.classNameToSymbol(name) if (sym.isModule) sym.moduleClass else @@ -478,7 +519,12 @@ abstract class ICodeReader extends ClassfileParser { code.emit(CALL_METHOD(m, style)) case JVM.invokestatic => val m = pool.getMemberSymbol(in.nextChar, true); size += 2 - code.emit(CALL_METHOD(m, Static(false))) + if (isBox(m)) + code.emit(BOX(toTypeKind(m.info.paramTypes.head))) + else if (isUnbox(m)) + code.emit(UNBOX(toTypeKind(m.info.resultType))) + else + code.emit(CALL_METHOD(m, Static(false))) case JVM.new_ => code.emit(NEW(REFERENCE(pool.getClassSymbol(in.nextChar)))) @@ -574,12 +620,22 @@ abstract class ICodeReader extends ClassfileParser { code.toBasicBlock assert(method.code ne null) + // reverse parameters, as they were prepended during code generation + method.params = method.params.reverse if (code.containsDUPX) { - code.resolveDups } + if (code.containsNEW) code.resolveNEWs } + /** TODO: move in Definitions and remove obsolete isBox/isUnbox found there. */ + def isBox(m: Symbol): Boolean = + (m.owner == definitions.BoxesUtilityClass.moduleClass + && m.name.startsWith("boxTo")) + def isUnbox(m: Symbol): Boolean = + (m.owner == definitions.BoxesUtilityClass.moduleClass + && m.name.startsWith("unboxTo")) + /** Return the icode class that should include members with the given flags. * There are two possible classes, the static part and the instance part. */ @@ -592,12 +648,14 @@ abstract class ICodeReader extends ClassfileParser { var locals: Map[Int, List[(Local, TypeKind)]] = new HashMap() var containsDUPX = false + var containsNEW = false def emit(i: Instruction) = { -// Console.println(i); instrs += (pc, i) if (i.isInstanceOf[DupX]) containsDUPX = true + if (i.isInstanceOf[opcodes.NEW]) + containsNEW = true } /** Break this linear code in basic block representation @@ -852,6 +910,35 @@ abstract class ICodeReader extends ClassfileParser { } } + /** Recover def-use chains for NEW and initializers. */ + def resolveNEWs { + import opcodes._ + + val rdef = new reachingDefinitions.ReachingDefinitionsAnalysis; + rdef.init(method) + rdef.run + + for (bb <- method.code.blocks) { + var info = rdef.in(bb) + for ((i, idx) <- bb.toList.zipWithIndex) i match { + case CALL_METHOD(m, Static(true)) if m.isClassConstructor => + val defs = rdef.findDefs(bb, idx, 1, m.info.paramTypes.length) + //println("ctor: " + i + " found defs: " + defs) + assert(defs.length == 1) + val (bb1, idx1) = defs.head + var producer = bb1(idx1) + while (producer.isInstanceOf[DUP]) { + val (bb2, idx2) = rdef.findDefs(bb1, idx1, 1).head + producer = bb2(idx2) + } + assert(producer.isInstanceOf[NEW], producer) + producer.asInstanceOf[NEW].init = i.asInstanceOf[CALL_METHOD] + case _ => + } + } + + } + /** Return the local at given index, with the given type. */ def getLocal(idx: Int, kind: TypeKind): Local = { assert(idx < maxLocals, "Index too large for local variable."); |