diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/opt/Inliners.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/opt/Inliners.scala | 190 |
1 files changed, 116 insertions, 74 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index e3d21011d1..66f802f74f 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -62,6 +62,15 @@ abstract class Inliners extends SubComponent { override def apply(c: IClass) { inliner analyzeClass c } + + override def run() { + try { + super.run() + } finally { + inliner.NonPublicRefs.usesNonPublics.clear() + inliner.recentTFAs.clear + } + } } def isBottomType(sym: Symbol) = sym == NullClass || sym == NothingClass @@ -79,17 +88,10 @@ abstract class Inliners extends SubComponent { val Private, Protected, Public = Value /** Cache whether a method calls private members. */ - val usesNonPublics: mutable.Map[IMethod, Value] = perRunCaches.newMap() + val usesNonPublics = mutable.Map.empty[IMethod, Value] } import NonPublicRefs._ - /* fresh name counter */ - val fresh = perRunCaches.newMap[String, Int]() withDefaultValue 0 - def freshName(s: String): TermName = { - fresh(s) += 1 - newTermName(s + fresh(s)) - } - private def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass private def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass @@ -97,27 +99,60 @@ abstract class Inliners extends SubComponent { private var currentIClazz: IClass = _ private def warn(pos: Position, msg: String) = currentIClazz.cunit.warning(pos, msg) + val recentTFAs = mutable.Map.empty[Symbol, Tuple2[Boolean, analysis.MethodTFA]] + private def getRecentTFA(incm: IMethod): (Boolean, analysis.MethodTFA) = { + + def containsRETURN(blocks: List[BasicBlock]) = blocks exists { bb => bb.lastInstruction.isInstanceOf[RETURN] } + + val opt = recentTFAs.get(incm.symbol) + if(opt.isDefined) { + // FYI val cachedBBs = opt.get._2.in.keySet + // FYI assert(incm.blocks.toSet == cachedBBs) + // incm.code.touched plays no role here + return opt.get + } + + val hasRETURN = containsRETURN(incm.code.blocksList) || (incm.exh exists { eh => containsRETURN(eh.blocks) }) + var a: analysis.MethodTFA = null + if(hasRETURN) { a = new analysis.MethodTFA(incm); a.run } + + if(hasInline(incm.symbol)) { recentTFAs.put(incm.symbol, (hasRETURN, a)) } + + (hasRETURN, a) + } + def analyzeClass(cls: IClass): Unit = if (settings.inline.value) { debuglog("Analyzing " + cls) this.currentIClazz = cls - cls.methods filterNot (_.symbol.isConstructor) foreach analyzeMethod + val ms = cls.methods filterNot { _.symbol.isConstructor } + ms foreach { im => + if(hasInline(im.symbol)) { + log("Not inlining into " + im.symbol.originalName.decode + " because it is marked @inline.") + } else if(im.hasCode && !im.symbol.isBridge) { + analyzeMethod(im) + } + } } - val tfa = new analysis.MethodTFA() + val tfa = new analysis.MTFAGrowable() tfa.stat = global.opt.printStats - - // how many times have we already inlined this method here? - private val inlinedMethodCount = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0 + val staleOut = new mutable.ListBuffer[BasicBlock] + val splicedBlocks = mutable.Set.empty[BasicBlock] + val staleIn = mutable.Set.empty[BasicBlock] def analyzeMethod(m: IMethod): Unit = { - var sizeBeforeInlining = if (m.hasCode) m.code.blockCount else 0 - var instrBeforeInlining = if (m.hasCode) m.code.instructionCount else 0 + var sizeBeforeInlining = m.code.blockCount + var instrBeforeInlining = m.code.instructionCount var retry = false var count = 0 - fresh.clear() - inlinedMethodCount.clear() + + // fresh name counter + val fresh = mutable.HashMap.empty[String, Int] withDefaultValue 0 + // how many times have we already inlined this method here? + val inlinedMethodCount = mutable.HashMap.empty[Symbol, Int] withDefaultValue 0 + val caller = new IMethodInfo(m) var info: tfa.lattice.Elem = null @@ -136,17 +171,20 @@ abstract class Inliners extends SubComponent { warn(i.pos, "Could not inline required method %s because %s.".format(msym.originalName.decode, reason)) } - if (shouldLoadImplFor(concreteMethod, receiver)) { + def isAvailable = icodes available concreteMethod.enclClass + + if (!isAvailable && shouldLoadImplFor(concreteMethod, receiver)) { // Until r22824 this line was: // icodes.icode(concreteMethod.enclClass, true) // - // Changing it to the below was the proximate cause for SI-3882: + // Changing it to + // icodes.load(concreteMethod.enclClass) + // was the proximate cause for SI-3882: // error: Illegal index: 0 overlaps List((variable par1,LONG)) // error: Illegal index: 0 overlaps List((variable par1,LONG)) icodes.load(concreteMethod.enclClass) } - def isAvailable = icodes available concreteMethod.enclClass def isCandidate = ( isClosureClass(receiver) || concreteMethod.isEffectivelyFinal @@ -169,7 +207,7 @@ abstract class Inliners extends SubComponent { lookupIMethod(concreteMethod, receiver) match { case Some(callee) => val inc = new IMethodInfo(callee) - val pair = new CallerCalleeInfo(caller, inc) + val pair = new CallerCalleeInfo(caller, inc, fresh, inlinedMethodCount) if (pair isStampedForInlining info.stack) { retry = true @@ -186,6 +224,7 @@ abstract class Inliners extends SubComponent { * might have changed after the inlining. */ usesNonPublics -= m + recentTFAs -= m.symbol } else { if (settings.debug.value) @@ -208,34 +247,35 @@ abstract class Inliners extends SubComponent { import scala.util.control.Breaks._ do { retry = false - if (caller.inline) { - log("Not inlining into " + caller.sym.originalName.decode + " because it is marked @inline.") - } - else if (caller.m.hasCode) { - log("Analyzing " + m + " count " + count + " with " + caller.length + " blocks") - tfa init m - tfa.run - caller.m.linearizedBlocks() foreach { bb => - info = tfa in bb - - breakable { - for (i <- bb) { - i match { - // Dynamic == normal invocations - // Static(true) == calls to private members - case CALL_METHOD(msym, Dynamic | Static(true)) if !msym.isConstructor => - if (analyzeInc(msym, i, bb)) - break - case _ => () - } - info = tfa.interpret(info, i) + log("Analyzing " + m + " count " + count + " with " + caller.length + " blocks") + tfa.reinit(m, staleOut.toList, splicedBlocks, staleIn) + tfa.run + staleOut.clear() + splicedBlocks.clear() + staleIn.clear() + + caller.m.linearizedBlocks() foreach { bb => + info = tfa in bb + + breakable { + for (i <- bb) { + i match { + // Dynamic == normal invocations + // Static(true) == calls to private members + case CALL_METHOD(msym, Dynamic | Static(true)) if !msym.isConstructor => + if (analyzeInc(msym, i, bb)) { + break + } + case _ => () } + info = tfa.interpret(info, i) } } - if (tfa.stat) - log(m.symbol.fullName + " iterations: " + tfa.iterations + " (size: " + caller.length + ")") } + + if (tfa.stat) + log(m.symbol.fullName + " iterations: " + tfa.iterations + " (size: " + caller.length + ")") } while (retry && count < MAX_INLINE_RETRY) @@ -304,11 +344,10 @@ abstract class Inliners extends SubComponent { def inline = hasInline(sym) def noinline = hasNoInline(sym) - def numInlined = inlinedMethodCount(sym) def isBridge = sym.isBridge def isInClosure = isClosureClass(owner) - def isHigherOrder = isHigherOrderMethod(sym) + val isHigherOrder = isHigherOrderMethod(sym) def isMonadic = isMonadicMethod(sym) def handlers = m.exh @@ -317,7 +356,7 @@ abstract class Inliners extends SubComponent { def length = blocks.length def openBlocks = blocks filterNot (_.closed) def instructions = m.code.instructions - def linearized = linearizer linearize m + // def linearized = linearizer linearize m def isSmall = (length <= SMALL_METHOD_SIZE) && blocks(0).length < 10 def isLarge = length > MAX_INLINE_SIZE @@ -336,21 +375,29 @@ abstract class Inliners extends SubComponent { def addHandlers(exhs: List[ExceptionHandler]) = m.exh = exhs ::: m.exh } - class CallerCalleeInfo(val caller: IMethodInfo, val inc: IMethodInfo) { + class CallerCalleeInfo(val caller: IMethodInfo, val inc: IMethodInfo, fresh: mutable.Map[String, Int], inlinedMethodCount: collection.Map[Symbol, Int]) { def isLargeSum = caller.length + inc.length - 1 > SMALL_METHOD_SIZE + private def freshName(s: String): TermName = { + fresh(s) += 1 + newTermName(s + fresh(s)) + } + /** Inline 'inc' into 'caller' at the given block and instruction. * The instruction must be a CALL_METHOD. */ def doInline(block: BasicBlock, instr: Instruction) { + + staleOut += block + val targetPos = instr.pos log("Inlining " + inc.m + " in " + caller.m + " at pos: " + posToStr(targetPos)) def blockEmit(i: Instruction) = block.emit(i, targetPos) def newLocal(baseName: String, kind: TypeKind) = - new Local(caller.sym.newVariable(targetPos, freshName(baseName)), kind, false) + new Local(caller.sym.newVariable(freshName(baseName), targetPos), kind, false) - val a = new analysis.MethodTFA(inc.m) + val (hasRETURN, a) = getRecentTFA(inc.m) /* The exception handlers that are active at the current block. */ val activeHandlers = caller.handlers filter (_ covered block) @@ -379,7 +426,7 @@ abstract class Inliners extends SubComponent { case x => newLocal("$retVal", x) } - val inlinedLocals = perRunCaches.newMap[Local, Local]() + val inlinedLocals = mutable.HashMap.empty[Local, Local] /** Add a new block in the current context. */ def newBlock() = { @@ -400,7 +447,7 @@ abstract class Inliners extends SubComponent { /** alfa-rename `l` in caller's context. */ def dupLocal(l: Local): Local = { - val sym = caller.sym.newVariable(l.sym.pos, freshName(l.sym.name.toString)) + val sym = caller.sym.newVariable(freshName(l.sym.name.toString), l.sym.pos) // sym.setInfo(l.sym.tpe) val dupped = new Local(sym, l.kind, false) inlinedLocals(l) = dupped @@ -461,9 +508,6 @@ abstract class Inliners extends SubComponent { inlinedBlock(b).varsInScope ++= (b.varsInScope map inlinedLocals) } - // analyse callee - a.run - // re-emit the instructions before the call block.open block.clear @@ -478,8 +522,9 @@ abstract class Inliners extends SubComponent { block.close // duplicate the other blocks in the callee - inc.m.linearizedBlocks() foreach { bb => - var info = a in bb + val calleeLin = inc.m.linearizedBlocks() + calleeLin foreach { bb => + var info = if(hasRETURN) (a in bb) else null def emitInlined(i: Instruction) = inlinedBlock(bb).emit(i, targetPos) def emitDrops(toDrop: Int) = info.stack.types drop toDrop foreach (t => emitInlined(DROP(t))) @@ -495,7 +540,7 @@ abstract class Inliners extends SubComponent { case _ => () } emitInlined(map(i)) - info = a.interpret(info, i) + info = if(hasRETURN) a.interpret(info, i) else null } inlinedBlock(bb).close } @@ -503,6 +548,9 @@ abstract class Inliners extends SubComponent { afterBlock emit instrAfter afterBlock.close + staleIn += afterBlock + splicedBlocks ++= (calleeLin map inlinedBlock) + // add exception handlers of the callee caller addHandlers (inc.handlers map translateExh) assert(pending.isEmpty, "Pending NEW elements: " + pending) @@ -520,7 +568,7 @@ abstract class Inliners extends SubComponent { | isSafeToInline: %s | shouldInline: %s """.stripMargin.format( - inc.m, sameSymbols, inc.numInlined < 2, + inc.m, sameSymbols, inlinedMethodCount(inc.sym) < 2, inc.m.hasCode, isSafeToInline(stack), shouldInline ) ) @@ -617,28 +665,22 @@ abstract class Inliners extends SubComponent { var score = 0 - // better not inline inside closures, but hope that the closure itself - // is repeatedly inlined - if (caller.isInClosure) score -= 2 + // better not inline inside closures, but hope that the closure itself is repeatedly inlined + if (caller.isInClosure) score -= 2 else if (caller.inlinedCalls < 1) score -= 1 // only monadic methods can trigger the first inline - if (inc.isSmall) - score += 1 + if (inc.isSmall) score += 1; + if (inc.isLarge) score -= 1; if (caller.isSmall && isLargeSum) { score -= 1 debuglog("shouldInline: score decreased to " + score + " because small " + caller + " would become large") } - if (inc.isLarge) - score -= 1 - if (inc.isMonadic) - score += 3 - else if (inc.isHigherOrder) - score += 1 - if (inc.isInClosure) - score += 2 - if (inc.numInlined > 2) - score -= 2 + if (inc.isMonadic) score += 3 + else if (inc.isHigherOrder) score += 1 + + if (inc.isInClosure) score += 2; + if (inlinedMethodCount(inc.sym) > 2) score -= 2; log("shouldInline(" + inc.m + ") score: " + score) |