diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2015-01-19 15:16:23 +0100 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2015-03-11 12:53:35 -0700 |
commit | 37c91654433a12249ae125b9454ba17cef103327 (patch) | |
tree | a9613f68be766bdfb79a542451b9588fe038b563 | |
parent | ea10434ff3bf24ac61dd4f65edcf931b7e988c0a (diff) | |
download | scala-37c91654433a12249ae125b9454ba17cef103327.tar.gz scala-37c91654433a12249ae125b9454ba17cef103327.tar.bz2 scala-37c91654433a12249ae125b9454ba17cef103327.zip |
After removing a live handler, need to re-run unreachableCode
Building the call graph and running the inliner require all code to
be reachable (in terms of ASM's data flow analysis / interpreter).
After removing unreachable code, empty exception handlers need to be
removed for correctness (see comment in LocalOpt.methodOptimizations).
Removing a live exception handler renders its handler unreachable and
therefore requires running another round of removing unreachable code.
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala | 33 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 4 |
2 files changed, 22 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala index 8c5a31658c..f6cfc5598b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -65,17 +65,25 @@ class LocalOpt(settings: ScalaSettings) { * * This implementation only removes instructions that are unreachable for an ASM analyzer / * interpreter. This ensures that future analyses will not produce `null` frames. The inliner - * depends on this property. + * and call graph builder depend on this property. */ def minimalRemoveUnreachableCode(method: MethodNode, ownerClassName: InternalName): Boolean = { if (method.instructions.size == 0) return false // fast path for abstract methods - val (codeRemoved, _) = removeUnreachableCodeImpl(method, ownerClassName) - if (codeRemoved) { - // Required for correctness, see comment in class LocalOpt - removeEmptyExceptionHandlers(method) - removeUnusedLocalVariableNodes(method)() + // For correctness, after removing unreachable code, we have to eliminate empty exception + // handlers, see scaladoc of def methodOptimizations. Removing an live handler may render more + // code unreachable and therefore requires running another round. + def removalRound(): Boolean = { + val (codeRemoved, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName) + if (codeRemoved) { + val liveHandlerRemoved = removeEmptyExceptionHandlers(method).exists(h => liveLabels(h.start)) + if (liveHandlerRemoved) removalRound() + } + codeRemoved } + + val codeRemoved = removalRound() + if (codeRemoved) removeUnusedLocalVariableNodes(method)() codeRemoved } @@ -134,9 +142,7 @@ class LocalOpt(settings: ScalaSettings) { // This triggers "ClassFormatError: Illegal exception table range in class file C". Similar // for local variables in dead blocks. Maybe that's a bug in the ASM framework. - var recurse = true - var codeHandlersOrJumpsChanged = false - while (recurse) { + def removalRound(): Boolean = { // unreachable-code, empty-handlers and simplify-jumps run until reaching a fixpoint (see doc on class LocalOpt) val (codeRemoved, handlersRemoved, liveHandlerRemoved) = if (settings.YoptUnreachableCode) { val (codeRemoved, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName) @@ -148,12 +154,15 @@ class LocalOpt(settings: ScalaSettings) { val jumpsChanged = if (settings.YoptSimplifyJumps) simplifyJumps(method) else false - codeHandlersOrJumpsChanged ||= (codeRemoved || handlersRemoved || jumpsChanged) + // Eliminating live handlers and simplifying jump instructions may render more code + // unreachable, so we need to run another round. + if (liveHandlerRemoved || jumpsChanged) removalRound() - // The doc comment of class LocalOpt explains why we recurse if jumpsChanged || liveHandlerRemoved - recurse = settings.YoptRecurseUnreachableJumps && (jumpsChanged || liveHandlerRemoved) + codeRemoved || handlersRemoved || jumpsChanged } + val codeHandlersOrJumpsChanged = removalRound() + // (*) Removing stale local variable descriptors is required for correctness of unreachable-code val localsRemoved = if (settings.YoptCompactLocals) compactLocalVariables(method) // also removes unused diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 9674b4cfae..43b634eee1 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -218,7 +218,6 @@ trait ScalaSettings extends AbsScalaSettings object YoptChoices extends MultiChoiceEnumeration { val unreachableCode = Choice("unreachable-code", "Eliminate unreachable code, exception handlers protecting no instructions, debug information of eliminated variables.") val simplifyJumps = Choice("simplify-jumps", "Simplify branching instructions, eliminate unnecessary ones.") - val recurseUnreachableJumps = Choice("recurse-unreachable-jumps", "Recursively apply unreachable-code and simplify-jumps (if enabled) until reaching a fixpoint.") val emptyLineNumbers = Choice("empty-line-numbers", "Eliminate unnecessary line number information.") val emptyLabels = Choice("empty-labels", "Eliminate and collapse redundant labels in the bytecode.") val compactLocals = Choice("compact-locals", "Eliminate empty slots in the sequence of local variables.") @@ -230,7 +229,7 @@ trait ScalaSettings extends AbsScalaSettings private val defaultChoices = List(unreachableCode) val lDefault = Choice("l:default", "Enable default optimizations: "+ defaultChoices.mkString(","), expandsTo = defaultChoices) - private val methodChoices = List(unreachableCode, simplifyJumps, recurseUnreachableJumps, emptyLineNumbers, emptyLabels, compactLocals) + private val methodChoices = List(unreachableCode, simplifyJumps, emptyLineNumbers, emptyLabels, compactLocals) val lMethod = Choice("l:method", "Enable intra-method optimizations: "+ methodChoices.mkString(","), expandsTo = methodChoices) private val projectChoices = List(lMethod, inlineProject) @@ -249,7 +248,6 @@ trait ScalaSettings extends AbsScalaSettings def YoptNone = Yopt.isSetByUser && Yopt.value.isEmpty def YoptUnreachableCode = !Yopt.isSetByUser || Yopt.contains(YoptChoices.unreachableCode) def YoptSimplifyJumps = Yopt.contains(YoptChoices.simplifyJumps) - def YoptRecurseUnreachableJumps = Yopt.contains(YoptChoices.recurseUnreachableJumps) def YoptEmptyLineNumbers = Yopt.contains(YoptChoices.emptyLineNumbers) def YoptEmptyLabels = Yopt.contains(YoptChoices.emptyLabels) def YoptCompactLocals = Yopt.contains(YoptChoices.compactLocals) |