Skip to content

Commit

Permalink
[backport] Refactor the ClosureOptimizer, run ProdCons only once per …
Browse files Browse the repository at this point in the history
…method

Refactor and clean up the ClosureOptimzier. The goal of this change
is to run the ProdCons analyzer only once per method, instead of
once per closure instantiation.

Bootstrapping scala with `-Yopt-inline-heuristics:everything` revealed
that ProdCons can take a very long time on large methods, for example

```
[quick.compiler] scala/tools/nsc/backend/jvm/BCodeBodyBuilder$PlainBodyBuilder#genArithmeticOp - analysis: 1 spans, 17755ms
[quick.compiler] scala/tools/nsc/typechecker/SuperAccessors$SuperAccTransformer#transform - analysis: 1 spans, 28024ms
[quick.compiler] scala/tools/nsc/backend/jvm/BCodeBodyBuilder$PlainBodyBuilder#genInvokeDynamicLambda - analysis: 1 spans, 22100ms
```

With this change and enough time and space (-Xmx6000m), bootstrapping
scala succeeds in this test mode.
  • Loading branch information
lrytz committed Jul 23, 2015
1 parent f5e7276 commit a1d471f
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ object BackendReporting {
* Used in `rewriteClosureApplyInvocations` when a closure apply callsite cannot be rewritten
* to the closure body method.
*/
trait RewriteClosureApplyToClosureBodyFailed extends OptimizerWarning {
sealed trait RewriteClosureApplyToClosureBodyFailed extends OptimizerWarning {
def pos: Position

override def emitWarning(settings: ScalaSettings): Boolean = this match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,20 @@ import scala.collection.convert.decorateAsScala._
* copying operations.
*/
class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName) {

/* Timers for benchmarking ProdCons
import scala.reflect.internal.util.Statistics._
import ProdConsAnalyzer._
val analyzerTimer = newSubTimer(classInternalName + "#" + methodNode.name + " - analysis", prodConsAnalyzerTimer)
val consumersTimer = newSubTimer(classInternalName + "#" + methodNode.name + " - consumers", prodConsAnalyzerTimer)
*/

val analyzer = new Analyzer(new InitialProducerSourceInterpreter)

// val start = analyzerTimer.start()
analyzer.analyze(classInternalName, methodNode)
// analyzerTimer.stop(start)
// println(analyzerTimer.line)

def frameAt(insn: AbstractInsnNode) = analyzer.frameAt(insn, methodNode)

Expand Down Expand Up @@ -392,6 +404,7 @@ class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName)

/** For each instruction, a set of potential consumers of the produced values. */
private lazy val _consumersOfOutputsFrom: Map[AbstractInsnNode, Vector[Set[AbstractInsnNode]]] = {
// val start = consumersTimer.start()
var res = Map.empty[AbstractInsnNode, Vector[Set[AbstractInsnNode]]]
for {
insn <- methodNode.instructions.iterator.asScala
Expand All @@ -404,13 +417,20 @@ class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName)
val outputIndex = producedSlots.indexOf(i)
res = res.updated(producer, currentConsumers.updated(outputIndex, currentConsumers(outputIndex) + insn))
}
// consumersTimer.stop(start)
// println(consumersTimer.line)
res
}

private val _initialProducersCache: mutable.AnyRefMap[(AbstractInsnNode, Int), Set[AbstractInsnNode]] = mutable.AnyRefMap.empty
private val _ultimateConsumersCache: mutable.AnyRefMap[(AbstractInsnNode, Int), Set[AbstractInsnNode]] = mutable.AnyRefMap.empty
}

object ProdConsAnalyzer {
import scala.reflect.internal.util.Statistics._
val prodConsAnalyzerTimer = newTimer("Time in ProdConsAnalyzer", "jvm")
}

/**
* A class for pseudo-instructions representing the initial producers of local values that have
* no producer instruction in the method:
Expand Down

0 comments on commit a1d471f

Please sign in to comment.