diff options
author | Paul Phillips <paulp@improving.org> | 2011-12-20 14:45:11 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-01-03 10:57:06 -0800 |
commit | 45a26ce3102f54e4f72165aef42fab7f73681c1d (patch) | |
tree | d13f32bce0859bb162d96b844fbdc10a368d1df6 /src | |
parent | 471a63a9e6618f25feb3a5266c38ad98ea2a5ac7 (diff) | |
download | scala-45a26ce3102f54e4f72165aef42fab7f73681c1d.tar.gz scala-45a26ce3102f54e4f72165aef42fab7f73681c1d.tar.bz2 scala-45a26ce3102f54e4f72165aef42fab7f73681c1d.zip |
Optimization/robustness in the backend.
Trying to help icode/jvm get through their business a bit faster, and
also make things more robust. Less null, more distinguished objects.
More encapsulation, fewer public vars. Helping updateSuccessorList()
to avoid assembling a list of successors on every call only to find
most of the time that it's the same list it already had (e.g. compiling
quick.lib it says "59076 calls, 690 requiring allocation" rather than
59076 calls, 59076 requiring allocation.)
Diffstat (limited to 'src')
18 files changed, 279 insertions, 198 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index 56e05cdc04..4ab0eb0129 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -3,13 +3,12 @@ * @author Martin Odersky */ - package scala.tools.nsc package backend package icode import scala.collection.{ mutable, immutable } -import mutable.{ ArrayBuffer } +import mutable.{ ListBuffer, ArrayBuffer } import util.{ Position, NoPosition } import backend.icode.analysis.ProgramPoint @@ -17,23 +16,83 @@ trait BasicBlocks { self: ICodes => import opcodes._ - import global.{ settings, log, nme } + import global.{ ifDebug, settings, log, nme } import nme.isExceptionResultName + + object NoBasicBlock extends BasicBlock(-1, null) /** 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 (modulo exceptions). */ - class BasicBlock(val label: Int, val method: IMethod) - extends AnyRef - with ProgramPoint[BasicBlock] - with Seq[Instruction] { + class BasicBlock(val label: Int, val method: IMethod) extends ProgramPoint[BasicBlock] { + outer => import BBFlags._ def code = method.code + private final class SuccessorList() { + private var successors: List[BasicBlock] = Nil + private def updateConserve() { + var lb: ListBuffer[BasicBlock] = null + var matches = 0 + var remaining = successors + + def addBlock(bb: BasicBlock) { + if (matches < 0) + lb += bb + else if (remaining.isEmpty || bb != remaining.head) { + lb = ListBuffer[BasicBlock]() ++= (successors take matches) += bb + matches = -1 + } + else { + matches += 1 + remaining = remaining.tail + } + } + + // exceptionSuccessors + method.exh foreach { handler => + if (handler covers outer) + addBlock(handler.startBlock) + } + // directSuccessors + val direct = directSuccessors + direct foreach addBlock + + /** Return a list of successors for 'b' that come from exception handlers + * covering b's (non-exceptional) successors. These exception handlers + * might not cover 'b' itself. This situation corresponds to an + * exception being thrown as the first thing of one of b's successors. + */ + method.exh foreach { handler => + direct foreach { block => + if (handler covers block) + addBlock(handler.startBlock) + } + } + // Blocks did not align: create a new list. + if (matches < 0) + successors = lb.toList + // Blocks aligned, but more blocks remain. Take a prefix of the list. + else if (remaining.nonEmpty) + successors = successors take matches + // Otherwise the list is unchanged, leave it alone. + } + + /** This is called millions of times: it is performance sensitive. */ + def updateSuccs() { + if (isEmpty) { + if (successors.nonEmpty) + successors = Nil + } + else updateConserve() + } + def toList = successors + } + /** Flags of this basic block. */ private var flags: Int = 0 @@ -76,20 +135,23 @@ trait BasicBlocks { setFlag(DIRTYSUCCS | DIRTYPREDS) /** Cached predecessors. */ - var preds: List[BasicBlock] = null + var preds: List[BasicBlock] = Nil /** Local variables that are in scope at entry of this basic block. Used * for debugging information. */ - var varsInScope: mutable.Set[Local] = new mutable.LinkedHashSet() + val varsInScope: mutable.Set[Local] = new mutable.LinkedHashSet() /** 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 instrs: Array[Instruction] = _ - override def toList: List[Instruction] = + + def take(n: Int): Seq[Instruction] = + if (closed) instrs take n else instructionList takeRight n reverse + + def toList: List[Instruction] = if (closed) instrs.toList else instructionList.reverse /** Return an iterator over the instructions in this basic block. */ @@ -117,17 +179,37 @@ trait BasicBlocks { } /** Apply a function to all the instructions of the block. */ - override def foreach[U](f: Instruction => U) = { - // !!! This appears to change behavior if I try to avoid the implicit - // conversion and traverse the array directly, which presumably means it - // is dependent on some mutation which is taking place during traversal. - // Please eliminate this if humanly possible. + final def foreach[U](f: Instruction => U) = { if (!closed) dumpMethodAndAbort(method, this) else instrs foreach f + + // !!! If I replace "instrs foreach f" with the following: + // var i = 0 + // val len = instrs.length + // while (i < len) { + // f(instrs(i)) + // i += 1 + // } + // + // Then when compiling under -optimise, quick.plugins fails as follows: + // + // quick.plugins: + // [mkdir] Created dir: /scratch/trunk6/build/quick/classes/continuations-plugin + // [scalacfork] Compiling 5 files to /scratch/trunk6/build/quick/classes/continuations-plugin + // [scalacfork] error: java.lang.VerifyError: (class: scala/tools/nsc/typechecker/Implicits$ImplicitSearch, method: typedImplicit0 signature: (Lscala/tools/nsc/typechecker/Implicits$ImplicitInfo;Z)Lscala/tools/nsc/typechecker/Implicits$SearchResult;) Incompatible object argument for function call + // [scalacfork] at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:67) + // [scalacfork] at scala.tools.nsc.Global$$anon$1.inferImplicit(Global.scala:419) + // [scalacfork] at scala.tools.nsc.typechecker.Typers$Typer.wrapImplicit$1(Typers.scala:170) + // [scalacfork] at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:174) + // [scalacfork] at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:963) + // [scalacfork] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:4378) + // + // This is bad and should be understood/eliminated. } /** The number of instructions in this basic block so far. */ def length = if (closed) instrs.length else instructionList.length + def size = length /** Return the n-th instruction. */ def apply(n: Int): Instruction = @@ -208,7 +290,7 @@ trait BasicBlocks { */ def removeLastInstruction() { if (closed) - removeInstructionsAt(size) + removeInstructionsAt(length) else { instructionList = instructionList.tail code.touched = true @@ -321,10 +403,11 @@ trait BasicBlocks { def clear() { instructionList = Nil instrs = null - preds = null + preds = Nil } - override def isEmpty = instructionList.isEmpty + final def isEmpty = instructionList.isEmpty + final def nonEmpty = !isEmpty /** Enter ignore mode: new 'emit'ted instructions will not be * added to this basic block. It makes the generation of THROW @@ -341,33 +424,33 @@ trait BasicBlocks { /** Return the last instruction of this basic block. */ def lastInstruction = - if (closed) instrs.last + if (closed) instrs(instrs.length - 1) else instructionList.head def firstInstruction = if (closed) instrs(0) else instructionList.last + def exceptionSuccessors: List[BasicBlock] = + exceptionSuccessorsForBlock(this) + def exceptionSuccessorsForBlock(block: BasicBlock): List[BasicBlock] = method.exh collect { case x if x covers block => x.startBlock } /** Cached value of successors. Must be recomputed whenever a block in the current method is changed. */ - private var succs: List[BasicBlock] = Nil - private def updateSuccs() { - resetFlag(DIRTYSUCCS) - succs = - if (isEmpty) Nil - else exceptionSuccessors ++ directSuccessors ++ indirectExceptionSuccessors - } + private val succs = new SuccessorList - def successors : List[BasicBlock] = { - if (touched) updateSuccs() - succs + def successors: List[BasicBlock] = { + if (touched) { + succs.updateSuccs() + resetFlag(DIRTYSUCCS) + } + succs.toList } def directSuccessors: List[BasicBlock] = if (isEmpty) Nil else lastInstruction match { - case JUMP(whereto) => List(whereto) + case JUMP(whereto) => whereto :: Nil case CJUMP(succ, fail, _, _) => fail :: succ :: Nil case CZJUMP(succ, fail, _, _) => fail :: succ :: Nil case SWITCH(_, labels) => labels @@ -379,17 +462,6 @@ trait BasicBlocks { else Nil } - def exceptionSuccessors: List[BasicBlock] = - exceptionSuccessorsForBlock(this) - - /** Return a list of successors for 'b' that come from exception handlers - * covering b's (non-exceptional) successors. These exception handlers - * might not cover 'b' itself. This situation corresponds to an - * exception being thrown as the first thing of one of b's successors. - */ - def indirectExceptionSuccessors: List[BasicBlock] = - directSuccessors flatMap exceptionSuccessorsForBlock distinct - /** Returns the predecessors of this block. */ def predecessors: List[BasicBlock] = { if (hasFlag(DIRTYPREDS)) { diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 3f0a0fac1a..2e34db09eb 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -102,7 +102,7 @@ abstract class GenICode extends SubComponent { case DefDef(mods, name, tparams, vparamss, tpt, rhs) => debuglog("Entering method " + name) val m = new IMethod(tree.symbol) - m.sourceFile = unit.source.toString() + m.sourceFile = unit.source m.returnType = if (tree.symbol.isConstructor) UNIT else toTypeKind(tree.symbol.info.resultType) ctx.clazz.addMethod(m) @@ -1716,7 +1716,7 @@ abstract class GenICode extends SubComponent { do { changed = false n += 1 - method.code.blocks foreach prune0 + method.blocks foreach prune0 } while (changed) debuglog("Prune fixpoint reached in " + n + " iterations."); @@ -1924,7 +1924,7 @@ abstract class GenICode extends SubComponent { val ctx1 = new Context(this) setMethod(m) ctx1.labels = mutable.HashMap() ctx1.method.code = new Code(m) - ctx1.bb = ctx1.method.code.startBlock + ctx1.bb = ctx1.method.startBlock ctx1.defdef = d ctx1.scope = EmptyScope ctx1.enterScope @@ -1932,11 +1932,12 @@ abstract class GenICode extends SubComponent { } /** Return a new context for a new basic block. */ - def newBlock: Context = { + def newBlock(): Context = { val block = method.code.newBlock handlers foreach (_ addCoveredBlock block) currentExceptionHandlers foreach (_ addBlock block) - block.varsInScope = mutable.HashSet() ++= scope.varsInScope + block.varsInScope.clear() + block.varsInScope ++= scope.varsInScope new Context(this) setBasicBlock block } diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index 7a0017944b..631b71d83a 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -84,15 +84,16 @@ abstract class ICodes extends AnyRef def checkValid(m: IMethod) { // always slightly dicey to iterate over mutable structures - val bs = m.code.blocks.toList - for (b <- bs ; if !b.closed) { - // Something is leaving open/empty blocks around (see SI-4840) so - // let's not kill the deal unless it's nonempty. - if (b.isEmpty) { - log("!!! Found open but empty block while inlining " + m + ": removing from block list.") - m.code removeBlock b + m foreachBlock { b => + if (!b.closed) { + // Something is leaving open/empty blocks around (see SI-4840) so + // let's not kill the deal unless it's nonempty. + if (b.isEmpty) { + log("!!! Found open but empty block while inlining " + m + ": removing from block list.") + m.code removeBlock b + } + else dumpMethodAndAbort(m, b) } - else dumpMethodAndAbort(m, b) } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala index 1978a23d90..f71c8de449 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Linearizers.scala @@ -36,7 +36,7 @@ trait Linearizers { var blocks: List[BasicBlock] = Nil def linearize(m: IMethod): List[BasicBlock] = { - val b = m.code.startBlock; + val b = m.startBlock; blocks = Nil; run { @@ -106,7 +106,7 @@ trait Linearizers { def linearize(m: IMethod): List[BasicBlock] = { blocks = Nil; - dfs(m.code.startBlock); + dfs(m.startBlock); m.exh foreach (b => dfs(b.startBlock)); blocks.reverse @@ -150,14 +150,14 @@ trait Linearizers { added.clear; m.exh foreach (b => rpo(b.startBlock)); - rpo(m.code.startBlock); + rpo(m.startBlock); // if the start block has predecessors, it won't be the first one // in the linearization, so we need to enforce it here - if (m.code.startBlock.predecessors eq Nil) + if (m.startBlock.predecessors eq Nil) blocks else - m.code.startBlock :: (blocks.filterNot(_ == m.code.startBlock)) + m.startBlock :: (blocks.filterNot(_ == m.startBlock)) } def linearizeAt(m: IMethod, start: BasicBlock): List[BasicBlock] = { @@ -195,7 +195,7 @@ trait Linearizers { * the last instruction being a jump). */ class DumpLinearizer extends Linearizer { - def linearize(m: IMethod): List[BasicBlock] = m.code.blocks.toList + def linearize(m: IMethod): List[BasicBlock] = m.blocks def linearizeAt(m: IMethod, start: BasicBlock): List[BasicBlock] = sys.error("not implemented") } @@ -250,7 +250,7 @@ trait Linearizers { * @param frozen blocks can't be moved (fist block of a method, blocks directly following a try-catch) */ def groupBlocks(method: IMethod, blocks: List[BasicBlock], handlers: List[ExceptionHandler], frozen: mutable.HashSet[BasicBlock]) = { - assert(blocks.head == method.code.startBlock, method) + assert(blocks.head == method.startBlock, method) // blocks before the try, and blocks for the try val beforeAndTry = new ListBuffer[BasicBlock]() diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index b2cc4cdd5b..4ef6766262 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -3,13 +3,13 @@ * @author Martin Odersky */ - package scala.tools.nsc package backend package icode import java.io.PrintWriter import scala.collection.{ mutable, immutable } +import util.{ SourceFile, NoSourceFile } import symtab.Flags.{ DEFERRED } trait ReferenceEquality { @@ -17,29 +17,34 @@ trait ReferenceEquality { override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] } -trait Members { self: ICodes => +trait Members { + self: ICodes => + import global._ + + object NoCode extends Code(null, "NoCode") { + override def blocksList: List[BasicBlock] = Nil + } /** * This class represents the intermediate code of a method or * other multi-block piece of code, like exception handlers. */ - class Code(method: IMethod) { - private[this] val name = method.symbol.decodedName.toString.intern + class Code(method: IMethod, name: String) { + def this(method: IMethod) = this(method, method.symbol.decodedName.toString.intern) /** The set of all blocks */ val blocks = mutable.ListBuffer[BasicBlock]() /** The start block of the method */ - var startBlock: BasicBlock = null - - /** The stack produced by this method */ - var producedStack: TypeStack = null + var startBlock: BasicBlock = NoBasicBlock private var currentLabel: Int = 0 private var _touched = false - def blockCount = blocks.size - def instructionCount = blocks map (_.length) sum + def blocksList: List[BasicBlock] = blocks.toList + def instructions = blocksList flatMap (_.iterator) + def blockCount = blocks.size + def instructionCount = blocks map (_.length) sum def touched = _touched def touched_=(b: Boolean): Unit = { @@ -82,7 +87,7 @@ trait Members { self: ICodes => /* Create a new block and append it to the list */ - def newBlock: BasicBlock = { + def newBlock(): BasicBlock = { touched = true val block = new BasicBlock(nextLabel, method); blocks += block; @@ -133,6 +138,8 @@ trait Members { self: ICodes => /** Represent a field in ICode */ class IField(val symbol: Symbol) extends IMember { } + + object NoIMethod extends IMethod(NoSymbol) { } /** * Represents a method in ICode. Local variables contain @@ -145,12 +152,22 @@ trait Members { self: ICodes => * finished (GenICode does that). */ class IMethod(val symbol: Symbol) extends IMember { - var code: Code = null + var code: Code = NoCode + + def newBlock() = code.newBlock + def startBlock = code.startBlock + def lastBlock = blocks.last + def blocks = code.blocksList + def linearizedBlocks(lin: Linearizer = self.linearizer): List[BasicBlock] = lin linearize this + + def foreachBlock[U](f: BasicBlock => U): Unit = blocks foreach f + def foreachInstr[U](f: Instruction => U): Unit = foreachBlock(_.toList foreach f) + var native = false /** The list of exception handlers, ordered from innermost to outermost. */ var exh: List[ExceptionHandler] = Nil - var sourceFile: String = _ + var sourceFile: SourceFile = NoSourceFile var returnType: TypeKind = _ var recursive: Boolean = false @@ -161,7 +178,8 @@ trait Members { self: ICodes => /** method parameters */ var params: List[Local] = Nil - def hasCode = code != null + // TODO - see how null is stil arriving here + def hasCode = (code ne NoCode) && (code ne null) def setCode(code: Code): IMethod = { this.code = code; this @@ -199,12 +217,14 @@ trait Members { self: ICodes => import opcodes._ def checkLocals(): Unit = { - def localsSet = code.blocks.flatten collect { - case LOAD_LOCAL(l) => l - case STORE_LOCAL(l) => l - } toSet + def localsSet = (code.blocks flatMap { bb => + bb.iterator collect { + case LOAD_LOCAL(l) => l + case STORE_LOCAL(l) => l + } + }).toSet - if (code != null) { + if (hasCode) { log("[checking locals of " + this + "]") locals filterNot localsSet foreach { l => log("Local " + l + " is not declared in " + this) @@ -218,7 +238,7 @@ trait Members { self: ICodes => * * This method should be most effective after heavy inlining. */ - def normalize(): Unit = if (this.code ne null) { + def normalize(): Unit = if (this.hasCode) { val nextBlock: mutable.Map[BasicBlock, BasicBlock] = mutable.HashMap.empty for (b <- code.blocks.toList if b.successors.length == 1; diff --git a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala index ff4abbb757..4ea253d29d 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala @@ -82,7 +82,7 @@ trait Printers { self: ICodes => if (!m.isAbstractMethod) { println(" {") println("locals: " + m.locals.mkString("", ", ", "")) - println("startBlock: " + m.code.startBlock) + println("startBlock: " + m.startBlock) println("blocks: " + m.code.blocks.mkString("[", ",", "]")) println lin.linearize(m) foreach printBlock diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala index 45ab7ae43c..ba4b250303 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala @@ -21,6 +21,8 @@ trait TypeStacks { * stack of the ICode. */ type Rep = List[TypeKind] + + object NoTypeStack extends TypeStack(Nil) { } class TypeStack(var types: Rep) { if (types.nonEmpty) diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala index 5af5b05682..229bbceb36 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala @@ -194,9 +194,9 @@ abstract class CopyPropagation { this.method = m init { - worklist += m.code.startBlock + worklist += m.startBlock worklist ++= (m.exh map (_.startBlock)) - m.code.blocks.foreach { b => + m foreachBlock { b => in(b) = lattice.bottom out(b) = lattice.bottom assert(out.contains(b)) @@ -207,21 +207,21 @@ abstract class CopyPropagation { } // first block is special: it's not bottom, but a precisely defined state with no bindings - in(m.code.startBlock) = new lattice.State(lattice.emptyBinding, Nil); + in(m.startBlock) = new lattice.State(lattice.emptyBinding, Nil); } } override def run() { forwardAnalysis(blockTransfer) if (settings.debug.value) { - linearizer.linearize(method).foreach(b => if (b != method.code.startBlock) + linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(in(b) != lattice.bottom, "Block " + b + " in " + this.method + " has input equal to bottom -- not visited?")); } } def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = - b.foldLeft(in)(interpret) + b.iterator.foldLeft(in)(interpret) import opcodes._ @@ -520,8 +520,8 @@ abstract class CopyPropagation { */ private def getBindingsForPrimaryCtor(in: copyLattice.State, ctor: Symbol): mutable.Map[Symbol, Value] = { val paramAccessors = ctor.owner.constrParamAccessors; - var values = in.stack.take(1 + ctor.info.paramTypes.length).reverse.drop(1); - val bindings = mutable.HashMap[Symbol, Value]() + var values = in.stack.take(1 + ctor.info.paramTypes.length).reverse.drop(1); + val bindings = mutable.HashMap[Symbol, Value]() debuglog("getBindings for: " + ctor + " acc: " + paramAccessors) @@ -562,13 +562,12 @@ abstract class CopyPropagation { final def isPureMethod(m: Symbol): Boolean = m.isGetter // abstract getters are still pure, as we 'know' - final override def toString(): String = { - var res = "" - for (b <- this.method.code.blocks.toList) - res = (res + "\nIN(" + b.label + "):\t Bindings: " + in(b).bindings + - "\nIN(" + b.label +"):\t Stack: " + in(b).stack) + "\n"; - res - } + final override def toString() = ( + method.blocks map { b => + "\nIN(%s):\t Bindings: %s".format(b.label, in(b).bindings) + + "\nIN(%s):\t Stack: %s".format(b.label, in(b).stack) + } mkString + ) } /* class CopyAnalysis */ } diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala index c656219dc8..49f5b51d51 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala @@ -33,10 +33,9 @@ abstract class Liveness { final class LivenessAnalysis extends DataFlowAnalysis[livenessLattice.type] { type P = BasicBlock - val lattice = livenessLattice - var method: IMethod = _ - - val gen: mutable.Map[BasicBlock, Set[Local]] = perRunCaches.newMap() + val lattice = livenessLattice + var method: IMethod = _ + val gen: mutable.Map[BasicBlock, Set[Local]] = perRunCaches.newMap() val kill: mutable.Map[BasicBlock, Set[Local]] = perRunCaches.newMap() def init(m: IMethod) { @@ -44,14 +43,15 @@ abstract class Liveness { gen.clear() kill.clear() - for (b <- m.code.blocks; (g, k) = genAndKill(b)) { + m foreachBlock { b => + val (g, k) = genAndKill(b) gen += (b -> g) kill += (b -> k) } init { - worklist ++= m.code.blocks.toList - m.code.blocks.foreach { b => + m foreachBlock { b => + worklist += b in(b) = lattice.bottom out(b) = lattice.bottom } @@ -75,7 +75,7 @@ abstract class Liveness { override def run() { backwardAnalysis(blockTransfer) if (settings.debug.value) { - linearizer.linearize(method).foreach(b => if (b != method.code.startBlock) + linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(lattice.bottom != in(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited?")); } @@ -89,29 +89,14 @@ abstract class Liveness { * liveness *before* the given instruction `i`. */ def interpret(out: lattice.Elem, i: Instruction): lattice.Elem = { - var in = out - - if (settings.debug.value) { - log("- " + i) - log("out: " + out) - log("\n") - } - + debuglog("- " + i + "\nout: " + out + "\n") i match { - case LOAD_LOCAL(l) => in += l - case STORE_LOCAL(l) => in -= l - case _ => - () - } - in - } /* def interpret */ - - override def toString(): String = { - val buf = new StringBuilder() - for (b <- method.code.blocks.toList) { - buf.append("\nlive-in(" + b + ")=" + in(b) + "\nlive-out(" + b + ")=" + out(b)); + case LOAD_LOCAL(l) => out + l + case STORE_LOCAL(l) => out - l + case _ => out } - buf.toString() } + override def toString() = + method.blocks map (b => "\nlive-in(%s)=%s\nlive-out(%s)=%s".format(b, in(b), b, out(b))) mkString } /* Liveness analysis */ } 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 eac714f999..c06bd2e097 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala @@ -78,7 +78,10 @@ abstract class ReachingDefinitions { drops.clear() outStack.clear() - for (b <- m.code.blocks.toList; (g, k) = genAndKill(b); (d, st) = dropsAndGen(b)) { + m foreachBlock { b => + val (g, k) = genAndKill(b) + val (d, st) = dropsAndGen(b) + gen += (b -> g) kill += (b -> k) drops += (b -> d) @@ -86,8 +89,8 @@ abstract class ReachingDefinitions { } init { - worklist ++= m.code.blocks.toList - m.code.blocks.foreach { b => + m foreachBlock { b => + worklist += b in(b) = lattice.bottom out(b) = lattice.bottom } @@ -141,7 +144,7 @@ abstract class ReachingDefinitions { override def run() { forwardAnalysis(blockTransfer) if (settings.debug.value) { - linearizer.linearize(method).foreach(b => if (b != method.code.startBlock) + linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(lattice.bottom != in(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? " + in(b) + ": bot: " + lattice.bottom diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala index a34d269cc9..937b0bdc3d 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala @@ -110,16 +110,16 @@ abstract class TypeFlowAnalysis { this.method = m //typeFlowLattice.lubs = 0 init { - worklist += m.code.startBlock + worklist += m.startBlock worklist ++= (m.exh map (_.startBlock)) - m.code.blocks.foreach { b => + m foreachBlock { b => in(b) = typeFlowLattice.bottom out(b) = typeFlowLattice.bottom } // start block has var bindings for each of its parameters val entryBindings = new VarBinding ++= (m.params map (p => ((p, p.kind)))) - in(m.code.startBlock) = lattice.IState(entryBindings, typeStackLattice.bottom) + in(m.startBlock) = lattice.IState(entryBindings, typeStackLattice.bottom) m.exh foreach { e => in(e.startBlock) = lattice.IState(in(e.startBlock).vars, typeStackLattice.exceptionHandlerStack) @@ -132,16 +132,18 @@ abstract class TypeFlowAnalysis { if (this.method == null || this.method.symbol != m.symbol) init(m) else reinit { - for (b <- m.code.blocks; if !in.isDefinedAt(b)) { - for (p <- b.predecessors) { - if (out.isDefinedAt(p)) { - in(b) = out(p) - worklist += p - } -/* else - in(b) = typeFlowLattice.bottom -*/ } - out(b) = typeFlowLattice.bottom + m foreachBlock { b => + if (!in.contains(b)) { + for (p <- b.predecessors) { + if (out.isDefinedAt(p)) { + in(b) = out(p) + worklist += p + } + /* else + in(b) = typeFlowLattice.bottom + */ } + out(b) = typeFlowLattice.bottom + } } for (handler <- m.exh) { val start = handler.startBlock @@ -164,7 +166,7 @@ abstract class TypeFlowAnalysis { forwardAnalysis(blockTransfer) val t = timer.stop if (settings.debug.value) { - linearizer.linearize(method).foreach(b => if (b != method.code.startBlock) + linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(visited.contains(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited)); } @@ -174,7 +176,7 @@ abstract class TypeFlowAnalysis { } def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = { - b.foldLeft(in)(interpret) + b.iterator.foldLeft(in)(interpret) } /** The flow function of a given basic block. */ /* var flowFun: immutable.Map[BasicBlock, TransferFunction] = new immutable.HashMap */ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index a2c4f16bf1..ff98537907 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -3,7 +3,6 @@ * @author Iulian Dragos */ - package scala.tools.nsc package backend.jvm @@ -13,6 +12,7 @@ import scala.collection.{ mutable, immutable } import scala.reflect.internal.pickling.{ PickleFormat, PickleBuffer } import scala.tools.reflect.SigParser import scala.tools.nsc.symtab._ +import scala.tools.nsc.util.{ SourceFile, NoSourceFile } import scala.reflect.internal.ClassfileConstants._ import ch.epfl.lamp.fjbg._ import JAccessFlags._ @@ -355,7 +355,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with if (isTopLevelModule(c.symbol)) { if (c.symbol.companionClass == NoSymbol) - generateMirrorClass(c.symbol, c.cunit.source.toString) + generateMirrorClass(c.symbol, c.cunit.source) else log("No mirror class for module with linked class: " + c.symbol.fullName) @@ -925,8 +925,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with mopt match { case Some(m) => - val oldLastBlock = m.code.blocks.last - val lastBlock = m.code.newBlock + val oldLastBlock = m.lastBlock + val lastBlock = m.newBlock() oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) if (isStaticModule(clasz.symbol)) { @@ -1079,7 +1079,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with * generated if there is no companion class: if there is, an attempt will * instead be made to add the forwarder methods to the companion class. */ - def generateMirrorClass(clasz: Symbol, sourceFile: String) { + def generateMirrorClass(clasz: Symbol, sourceFile: SourceFile) { import JAccessFlags._ val moduleName = javaName(clasz) // + "$" val mirrorName = moduleName.substring(0, moduleName.length() - 1) @@ -1087,7 +1087,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with mirrorName, JAVA_LANG_OBJECT.getName, JClass.NO_INTERFACES, - sourceFile) + "" + sourceFile) log("Dumping mirror class for '%s'".format(mirrorClass.getName)) addForwarders(mirrorClass, clasz) diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index e208f5a8da..bf59d80cdd 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -1898,8 +1898,8 @@ abstract class GenMSIL extends SubComponent { val sc = iclass.lookupStaticCtor if (sc.isDefined) { val m = sc.get - val oldLastBlock = m.code.blocks.last - val lastBlock = m.code.newBlock + val oldLastBlock = m.lastBlock + val lastBlock = m.newBlock() oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) // call object's private ctor from static ctor lastBlock.emit(CIL_NEWOBJ(iclass.symbol.primaryConstructor)) diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index 3e921cf472..e8abee7d06 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -94,12 +94,12 @@ abstract class ClosureElimination extends SubComponent { import copyPropagation._ /* Some embryonic copy propagation. */ - def analyzeMethod(m: IMethod): Unit = try {if (m.code ne null) { + def analyzeMethod(m: IMethod): Unit = try {if (m.hasCode) { log("Analyzing " + m) cpp.init(m) cpp.run - for (bb <- linearizer.linearize(m)) { + m.linearizedBlocks() foreach { bb => var info = cpp.in(bb) debuglog("Cpp info at entry to block " + bb + ": " + info) @@ -201,28 +201,25 @@ abstract class ClosureElimination extends SubComponent { /** Peephole optimization. */ abstract class PeepholeOpt { - private var method: IMethod = null + private var method: IMethod = NoIMethod /** Concrete implementations will perform their optimizations here */ def peep(bb: BasicBlock, i1: Instruction, i2: Instruction): Option[List[Instruction]] var liveness: global.icodes.liveness.LivenessAnalysis = null - def apply(m: IMethod): Unit = if (m.code ne null) { + def apply(m: IMethod): Unit = if (m.hasCode) { method = m liveness = new global.icodes.liveness.LivenessAnalysis liveness.init(m) liveness.run - for (b <- m.code.blocks) - transformBlock(b) + m foreachBlock transformBlock } def transformBlock(b: BasicBlock): Unit = if (b.size >= 2) { - var newInstructions: List[Instruction] = Nil - - newInstructions = b.toList - + var newInstructions: List[Instruction] = b.toList var redo = false + do { var h = newInstructions.head var t = newInstructions.tail diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 64df3b4636..5fc7329955 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -72,7 +72,7 @@ abstract class DeadCodeElimination extends SubComponent { val dropOf: mutable.Map[(BasicBlock, Int), List[(BasicBlock, Int)]] = perRunCaches.newMap() def dieCodeDie(m: IMethod) { - if (m.code ne null) { + if (m.hasCode) { log("dead code elimination on " + m); dropOf.clear() m.code.blocks.clear() @@ -90,12 +90,12 @@ abstract class DeadCodeElimination extends SubComponent { } /** collect reaching definitions and initial useful instructions for this method. */ - def collectRDef(m: IMethod): Unit = if (m.code ne null) { + def collectRDef(m: IMethod): Unit = if (m.hasCode) { defs = immutable.HashMap.empty; worklist.clear(); useful.clear(); rdef.init(m); rdef.run; - for (bb <- m.code.blocks.toList) { + m foreachBlock { bb => useful(bb) = new mutable.BitSet(bb.size) var rd = rdef.in(bb); for (Pair(i, idx) <- bb.toList.zipWithIndex) { @@ -184,7 +184,7 @@ abstract class DeadCodeElimination extends SubComponent { def sweep(m: IMethod) { val compensations = computeCompensations(m) - for (bb <- m.code.blocks.toList) { + m foreachBlock { bb => // Console.println("** Sweeping block " + bb + " **") val oldInstr = bb.toList bb.open @@ -223,7 +223,7 @@ abstract class DeadCodeElimination extends SubComponent { private def computeCompensations(m: IMethod): mutable.Map[(BasicBlock, Int), List[Instruction]] = { val compensations: mutable.Map[(BasicBlock, Int), List[Instruction]] = new mutable.HashMap - for (bb <- m.code.blocks) { + m foreachBlock { bb => assert(bb.closed, "Open block in computeCompensations") for ((i, idx) <- bb.toList.zipWithIndex) { if (!useful(bb)(idx)) { diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala index 1d971beae4..a37a3406a8 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala @@ -79,7 +79,7 @@ abstract class InlineExceptionHandlers extends SubComponent { /* Type Flow Analysis */ private val tfa: analysis.MethodTFA = new analysis.MethodTFA() private var tfaCache: Map[Int, tfa.lattice.Elem] = Map.empty - private var analyzedMethod: IMethod = null + private var analyzedMethod: IMethod = NoIMethod /* Blocks that need to be analyzed */ private var todoBlocks: List[BasicBlock] = Nil @@ -110,7 +110,7 @@ abstract class InlineExceptionHandlers extends SubComponent { * inlined blocks, so worst case scenario we double the size of the code */ private def applyMethod(method: IMethod): Unit = { - if (method.code ne null) { + if (method.hasCode) { // create the list of starting blocks todoBlocks = global.icodes.linearizer.linearize(method) @@ -127,7 +127,7 @@ abstract class InlineExceptionHandlers extends SubComponent { todoBlocks = Nil // Type flow analysis cleanup - analyzedMethod = null + analyzedMethod = NoIMethod tfaCache = Map.empty //TODO: Need a way to clear tfa structures } @@ -151,7 +151,7 @@ abstract class InlineExceptionHandlers extends SubComponent { * - we change the THROW exception to the new Clear stack + JUMP code */ for { - (instr @ THROW(clazz), index) <- bblock.zipWithIndex + (instr @ THROW(clazz), index) <- bblock.iterator.zipWithIndex // Decide if any handler fits this exception // If not, then nothing to do, we cannot determine statically which handler will catch the exception (handler, caughtException) <- findExceptionHandler(toTypeKind(clazz.tpe), bblock.exceptionSuccessors) @@ -181,7 +181,7 @@ abstract class InlineExceptionHandlers extends SubComponent { if (!canReplaceHandler) { currentClass.cunit.warning(NoPosition, "Unable to inline the exception handler inside incorrect" + - " block:\n" + bblock.mkString("\n") + "\nwith stack: " + typeInfo + " just " + + " block:\n" + bblock.iterator.mkString("\n") + "\nwith stack: " + typeInfo + " just " + "before instruction index " + index) } else { @@ -261,13 +261,13 @@ abstract class InlineExceptionHandlers extends SubComponent { private def getTypesAtBlockEntry(bblock: BasicBlock): tfa.lattice.Elem = { // lazily perform tfa, because it's expensive // cache results by block label, as rewriting the code messes up the block's hashCode - if (analyzedMethod eq null) { + if (analyzedMethod eq NoIMethod) { analyzedMethod = bblock.method tfa.init(bblock.method) tfa.run log(" performed tfa on method: " + bblock.method) - for (block <- bblock.method.code.blocks.sortBy(_.label)) + for (block <- bblock.method.blocks.sortBy(_.label)) tfaCache += block.label -> tfa.in(block) } @@ -360,7 +360,7 @@ abstract class InlineExceptionHandlers extends SubComponent { val caughtException = toTypeKind(caughtClass.tpe) // copy the exception handler code once again, dropping the LOAD_EXCEPTION val copy = handler.code.newBlock - copy.emitOnly(handler drop dropCount: _*) + copy.emitOnly(handler.iterator drop dropCount toSeq: _*) // extend the handlers of the handler to the copy for (parentHandler <- handler.method.exh ; if parentHandler covers handler) { @@ -382,7 +382,7 @@ abstract class InlineExceptionHandlers extends SubComponent { case _ => currentClass.cunit.warning(NoPosition, "Unable to inline the exception handler due to incorrect format:\n" + - handler.mkString("\n")) + handler.iterator.mkString("\n")) None } } diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index d56a0740d9..e01c42ad87 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -9,6 +9,7 @@ package backend.opt import scala.collection.mutable import scala.tools.nsc.symtab._ +import scala.tools.nsc.util.{ NoSourceFile } /** * @author Iulian Dragos @@ -111,8 +112,8 @@ abstract class Inliners extends SubComponent { private val inlinedMethodCount = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0 def analyzeMethod(m: IMethod): Unit = { - var sizeBeforeInlining = if (m.code ne null) m.code.blockCount else 0 - var instrBeforeInlining = if (m.code ne null) m.code.instructionCount else 0 + var sizeBeforeInlining = if (m.hasCode) m.code.blockCount else 0 + var instrBeforeInlining = if (m.hasCode) m.code.instructionCount else 0 var retry = false var count = 0 fresh.clear() @@ -210,11 +211,11 @@ abstract class Inliners extends SubComponent { if (caller.inline) { log("Not inlining into " + caller.sym.originalName.decode + " because it is marked @inline.") } - else if (caller.hasCode) { + else if (caller.m.hasCode) { log("Analyzing " + m + " count " + count + " with " + caller.length + " blocks") tfa init m tfa.run - caller.linearized foreach { bb => + caller.m.linearizedBlocks() foreach { bb => info = tfa in bb breakable { @@ -311,18 +312,16 @@ abstract class Inliners extends SubComponent { def isMonadic = isMonadicMethod(sym) def handlers = m.exh - def blocks = if (m.code eq null) sys.error("blocks = null + " + m) else m.code.blocks + def blocks = m.blocks def locals = m.locals def length = blocks.length def openBlocks = blocks filterNot (_.closed) - def instructions = blocks.flatten + def instructions = m.code.instructions def linearized = linearizer linearize m def isSmall = (length <= SMALL_METHOD_SIZE) && blocks(0).length < 10 def isLarge = length > MAX_INLINE_SIZE def isRecursive = m.recursive - def hasCode = m.code != null - def hasSourceFile = m.sourceFile != null def hasHandlers = handlers.nonEmpty def hasNonFinalizerHandler = handlers exists { case _: Finalizer => true @@ -457,7 +456,7 @@ abstract class Inliners extends SubComponent { if (retVal ne null) caller addLocal retVal - inc.blocks foreach { b => + inc.m foreachBlock { b => inlinedBlock += (b -> newBlock()) inlinedBlock(b).varsInScope ++= (b.varsInScope map inlinedLocals) } @@ -475,11 +474,11 @@ abstract class Inliners extends SubComponent { blockEmit(STORE_LOCAL(inlinedThis)) // jump to the start block of the callee - blockEmit(JUMP(inlinedBlock(inc.m.code.startBlock))) + blockEmit(JUMP(inlinedBlock(inc.m.startBlock))) block.close // duplicate the other blocks in the callee - linearizer linearize inc.m foreach { bb => + inc.m.linearizedBlocks() foreach { bb => var info = a in bb def emitInlined(i: Instruction) = inlinedBlock(bb).emit(i, targetPos) def emitDrops(toDrop: Int) = info.stack.types drop toDrop foreach (t => emitInlined(DROP(t))) @@ -511,23 +510,23 @@ abstract class Inliners extends SubComponent { } def isStampedForInlining(stack: TypeStack) = - !sameSymbols && inc.hasCode && shouldInline && isSafeToInline(stack) + !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stack) def logFailure(stack: TypeStack) = log( """|inline failed for %s: | pair.sameSymbols: %s | inc.numInlined < 2: %s - | inc.hasCode: %s + | inc.m.hasCode: %s | isSafeToInline: %s | shouldInline: %s """.stripMargin.format( inc.m, sameSymbols, inc.numInlined < 2, - inc.hasCode, isSafeToInline(stack), shouldInline + inc.m.hasCode, isSafeToInline(stack), shouldInline ) ) def failureReason(stack: TypeStack) = - if (!inc.hasCode) "bytecode was unavailable" + if (!inc.m.hasCode) "bytecode was unavailable" else if (!isSafeToInline(stack)) "it is unsafe (target may reference private fields)" else "of a bug (run with -Ylog:inline -Ydebug for more information)" @@ -550,14 +549,14 @@ abstract class Inliners extends SubComponent { */ def isSafeToInline(stack: TypeStack): Boolean = { def makePublic(f: Symbol): Boolean = - inc.hasSourceFile && (f.isSynthetic || f.isParamAccessor) && { + (inc.m.sourceFile ne NoSourceFile) && (f.isSynthetic || f.isParamAccessor) && { debuglog("Making not-private symbol out of synthetic: " + f) f setNotFlag Flags.PRIVATE true } - if (!inc.hasCode || inc.isRecursive) + if (!inc.m.hasCode || inc.isRecursive) return false val accessNeeded = usesNonPublics.getOrElseUpdate(inc.m, { @@ -610,7 +609,7 @@ abstract class Inliners extends SubComponent { * - it's good to inline closures functions. * - it's bad (useless) to inline inside bridge methods */ - private def neverInline = caller.isBridge || !inc.hasCode || inc.noinline + private def neverInline = caller.isBridge || !inc.m.hasCode || inc.noinline private def alwaysInline = inc.inline def shouldInline: Boolean = !neverInline && (alwaysInline || { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index a203b8a78b..abc1dd387c 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -25,7 +25,7 @@ abstract class ICodeReader extends ClassfileParser { var instanceCode: IClass = null // the ICode class for the current symbol var staticCode: IClass = null // the ICode class static members - var method: IMethod = _ // the current IMethod + var method: IMethod = NoIMethod // the current IMethod val nothingName = newTermName(SCALA_NOTHING) val nullName = newTermName(SCALA_NULL) @@ -629,7 +629,7 @@ abstract class ICodeReader extends ClassfileParser { skipAttributes() code.toBasicBlock - assert(method.code ne null) + assert(method.hasCode, method) // reverse parameters, as they were prepended during code generation method.params = method.params.reverse @@ -692,7 +692,7 @@ abstract class ICodeReader extends ClassfileParser { mutable.Map(jmpTargets.toSeq map (_ -> code.newBlock): _*) val blocks = makeBasicBlocks - var otherBlock: BasicBlock = null + var otherBlock: BasicBlock = NoBasicBlock var disableJmpTarget = false for ((pc, instr) <- instrs.iterator) { |