diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2016-01-24 10:19:25 +0100 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2016-01-24 13:02:08 +0100 |
commit | 1a9e649e34cda4306537d74ab425c33d5f6a77db (patch) | |
tree | 389a254831541bd8a02a3b03642249d3691c5dd2 /src/compiler | |
parent | 790ed59d9fe5ca396984b8a5135b7a091e4224f7 (diff) | |
download | scala-1a9e649e34cda4306537d74ab425c33d5f6a77db.tar.gz scala-1a9e649e34cda4306537d74ab425c33d5f6a77db.tar.bz2 scala-1a9e649e34cda4306537d74ab425c33d5f6a77db.zip |
Jump optimizations may enable more push-pop elimination
Jump optimization replaces an unnecessary conditional jump, e.g.
`IFNULL l; l: ...` by `POP`, which enables further push-pop elimination.
Also introduces a `-YoptTrace` flag that traces the progress of the
bytecode as it goes through local optimizations.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala | 34 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 2 |
2 files changed, 34 insertions, 2 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 cd4d5be197..7a1dcd9ec6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -104,6 +104,7 @@ import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ * + enables UPSTREAM * - unreachable code (`GOTO a; a: GOTO b; b: ...`, the first jump is changed to `GOTO b`, the second becomes unreachable) * - store-load pairs (a `GOTO l; l: ...` is removed between store and load) + * - push-pop (`IFNULL l; l: ...` is replaced by `POP`) * * * The following cleanup optimizations don't enable any upstream optimizations, so they can be @@ -228,6 +229,16 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { // 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 currentTrace: String = null + def traceIfChanged(optName: String): Unit = if (compilerSettings.YoptTrace.value) { + val after = AsmUtils.textify(method) + if (currentTrace != after) { + println(s"after $optName") + println(after) + } + currentTrace = after + } + /** * Runs the optimizations that depend on each other in a loop until reaching a fixpoint. See * comment in class [[LocalOpt]]. @@ -239,14 +250,18 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { requestDCE: Boolean, requestBoxUnbox: Boolean, requestStaleStores: Boolean, + requestPushPop: Boolean, requestStoreLoad: Boolean, firstIteration: Boolean, maxRecursion: Int = 10): (Boolean, Boolean) = { if (maxRecursion == 0) return (false, false) + traceIfChanged("beforeMethodOpt") + // NULLNESS OPTIMIZATIONS val runNullness = compilerSettings.YoptNullnessTracking && requestNullness val nullnessOptChanged = runNullness && nullnessOptimizations(method, ownerClassName) + traceIfChanged("nullness") // UNREACHABLE CODE // Both AliasingAnalyzer (used in copyProp) and ProdConsAnalyzer (used in eliminateStaleStores, @@ -255,54 +270,65 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { compilerSettings.YoptBoxUnbox || compilerSettings.YoptCopyPropagation val (codeRemoved, liveLabels) = if (runDCE) removeUnreachableCodeImpl(method, ownerClassName) else (false, Set.empty[LabelNode]) + traceIfChanged("dce") // BOX-UNBOX val runBoxUnbox = compilerSettings.YoptBoxUnbox && (requestBoxUnbox || nullnessOptChanged) val boxUnboxChanged = runBoxUnbox && boxUnboxElimination(method, ownerClassName) + traceIfChanged("boxUnbox") // COPY PROPAGATION val runCopyProp = compilerSettings.YoptCopyPropagation && (firstIteration || boxUnboxChanged) val copyPropChanged = runCopyProp && copyPropagation(method, ownerClassName) + traceIfChanged("copyProp") // STALE STORES val runStaleStores = compilerSettings.YoptCopyPropagation && (requestStaleStores || nullnessOptChanged || codeRemoved || boxUnboxChanged || copyPropChanged) val storesRemoved = runStaleStores && eliminateStaleStores(method, ownerClassName) + traceIfChanged("staleStores") // REDUNDANT CASTS val runRedundantCasts = compilerSettings.YoptRedundantCasts && (firstIteration || boxUnboxChanged) val castRemoved = runRedundantCasts && eliminateRedundantCasts(method, ownerClassName) + traceIfChanged("redundantCasts") // PUSH-POP - val runPushPop = compilerSettings.YoptCopyPropagation && (firstIteration || storesRemoved || castRemoved) + val runPushPop = compilerSettings.YoptCopyPropagation && (requestPushPop || firstIteration || storesRemoved || castRemoved) val pushPopRemoved = runPushPop && eliminatePushPop(method, ownerClassName) + traceIfChanged("pushPop") // STORE-LOAD PAIRS val runStoreLoad = compilerSettings.YoptCopyPropagation && (requestStoreLoad || boxUnboxChanged || copyPropChanged || pushPopRemoved) val storeLoadRemoved = runStoreLoad && eliminateStoreLoad(method) + traceIfChanged("storeLoadPairs") // STALE HANDLERS val removedHandlers = if (runDCE) removeEmptyExceptionHandlers(method) else Set.empty[TryCatchBlockNode] val handlersRemoved = removedHandlers.nonEmpty val liveHandlerRemoved = removedHandlers.exists(h => liveLabels(h.start)) + traceIfChanged("staleHandlers") // SIMPLIFY JUMPS // almost all of the above optimizations enable simplifying more jumps, so we just run it in every iteration val runSimplifyJumps = compilerSettings.YoptSimplifyJumps val jumpsChanged = runSimplifyJumps && simplifyJumps(method) + traceIfChanged("simplifyJumps") // See doc comment in the beginning of this file (optimizations marked UPSTREAM) val runNullnessAgain = boxUnboxChanged val runDCEAgain = liveHandlerRemoved || jumpsChanged val runBoxUnboxAgain = boxUnboxChanged || castRemoved || pushPopRemoved || liveHandlerRemoved val runStaleStoresAgain = pushPopRemoved + val runPushPopAgain = jumpsChanged val runStoreLoadAgain = jumpsChanged - val runAgain = runNullnessAgain || runDCEAgain || runBoxUnboxAgain || pushPopRemoved || runStaleStoresAgain || runStoreLoadAgain + val runAgain = runNullnessAgain || runDCEAgain || runBoxUnboxAgain || pushPopRemoved || runStaleStoresAgain || runPushPopAgain || runStoreLoadAgain val downstreamRequireEliminateUnusedLocals = runAgain && removalRound( requestNullness = runNullnessAgain, requestDCE = runDCEAgain, requestBoxUnbox = runBoxUnboxAgain, requestStaleStores = runStaleStoresAgain, + requestPushPop = runPushPopAgain, requestStoreLoad = runStoreLoadAgain, firstIteration = false, maxRecursion = maxRecursion - 1)._2 @@ -327,6 +353,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { requestDCE = true, requestBoxUnbox = true, requestStaleStores = true, + requestPushPop = true, requestStoreLoad = true, firstIteration = true) if (compilerSettings.YoptUnreachableCode) unreachableCodeEliminated += method @@ -338,10 +365,13 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { if (compilerSettings.YoptCompactLocals) compactLocalVariables(method) // also removes unused else if (requireEliminateUnusedLocals) removeUnusedLocalVariableNodes(method)() // (*) else false + traceIfChanged("localVariables") val lineNumbersRemoved = if (compilerSettings.YoptUnreachableCode) removeEmptyLineNumbers(method) else false + traceIfChanged("lineNumbers") val labelsRemoved = if (compilerSettings.YoptUnreachableCode) removeEmptyLabelNodes(method) else false + traceIfChanged("labels") // assert that local variable annotations are empty (we don't emit them) - otherwise we'd have // to eliminate those covering an empty range, similar to removeUnusedLocalVariableNodes. diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index f64fe437e4..1bb1943c0c 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -322,6 +322,8 @@ trait ScalaSettings extends AbsScalaSettings def YoptWarningNoInlineMissingBytecode = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingBytecode) def YoptWarningNoInlineMissingScalaInlineInfoAttr = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingScalaInlineInfoAttr) + val YoptTrace = BooleanSetting("-YoptTrace", "trace optimizer progress") + private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." object YstatisticsPhases extends MultiChoiceEnumeration { val parser, typer, patmat, erasure, cleanup, jvm = Value } |