diff options
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) { |