Skip to content

Commit

Permalink
Mark reachable labels with a flag in label.status instead of a hash set
Browse files Browse the repository at this point in the history
Add a flag in label.status to mark reachable labels in
`removeUnreachableCodeImpl`. This improves performance.
  • Loading branch information
lrytz committed Sep 13, 2017
1 parent 4813f3b commit 6e3c5fc
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 12 deletions.
18 changes: 18 additions & 0 deletions src/compiler/scala/tools/asm/LabelAccess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package scala.tools.asm;

/**
* Temporary class to allow access to the package-private status field of class Label.
*/
public class LabelAccess {
public static boolean isLabelFlagSet(Label l, int f) {
return (l.status & f) != 0;
}

public static void setLabelFlag(Label l, int f) {
l.status |= f;
}

public static void clearLabelFlag(Label l, int f) {
l.status &= ~f;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import scala.tools.asm
import scala.tools.asm.Opcodes._
import scala.tools.asm.tree._
import scala.tools.asm.tree.analysis._
import scala.tools.asm.{Handle, Type}
import scala.tools.asm.{Handle, Label, LabelAccess, Type}
import scala.tools.nsc.backend.jvm.BTypes._
import scala.tools.nsc.backend.jvm.GenBCode._
import scala.tools.nsc.backend.jvm.analysis.BackendUtils._
Expand Down Expand Up @@ -574,6 +574,11 @@ object BackendUtils {
def setDceDone(method: MethodNode) = method.access |= ACC_DCE_DONE
def clearDceDone(method: MethodNode) = method.access &= ~ACC_DCE_DONE

private val LABEL_REACHABLE_STATUS = 0x1000000
def isLabelReachable(label: LabelNode) = LabelAccess.isLabelFlagSet(label.getLabel, LABEL_REACHABLE_STATUS)
def setLabelReachable(label: LabelNode) = LabelAccess.setLabelFlag(label.getLabel, LABEL_REACHABLE_STATUS)
def clearLabelReachable(label: LabelNode) = LabelAccess.clearLabelFlag(label.getLabel, LABEL_REACHABLE_STATUS)

abstract class NestedClassesCollector[T] extends GenericSignatureVisitor {
val innerClasses = mutable.Set.empty[T]

Expand Down
29 changes: 18 additions & 11 deletions src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,13 @@ abstract class LocalOpt {
// 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 (insnsRemoved, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName)
val insnsRemoved = removeUnreachableCodeImpl(method, ownerClassName)
if (insnsRemoved) {
val liveHandlerRemoved = removeEmptyExceptionHandlers(method).exists(h => liveLabels(h.start))
val liveHandlerRemoved = removeEmptyExceptionHandlers(method).exists(h => BackendUtils.isLabelReachable(h.start))
if (liveHandlerRemoved) removalRound()
}
// Note that `removeUnreachableCodeImpl` adds `LABEL_REACHABLE_STATUS` to label.status fields. We don't clean up
// this flag here (in `minimalRemoveUnreachableCode`), we rely on that being done later in `methodOptimizations`.
insnsRemoved
}

Expand Down Expand Up @@ -285,7 +287,7 @@ abstract class LocalOpt {
val runDCE = (compilerSettings.optUnreachableCode && (requestDCE || nullnessOptChanged)) ||
compilerSettings.optBoxUnbox ||
compilerSettings.optCopyPropagation
val (codeRemoved, liveLabels) = if (runDCE) removeUnreachableCodeImpl(method, ownerClassName) else (false, Set.empty[LabelNode])
val codeRemoved = if (runDCE) removeUnreachableCodeImpl(method, ownerClassName) else false
traceIfChanged("dce")

// BOX-UNBOX
Expand Down Expand Up @@ -321,7 +323,7 @@ abstract class LocalOpt {
// STALE HANDLERS
val removedHandlers = if (runDCE) removeEmptyExceptionHandlers(method) else Set.empty[TryCatchBlockNode]
val handlersRemoved = removedHandlers.nonEmpty
val liveHandlerRemoved = removedHandlers.exists(h => liveLabels(h.start))
val liveHandlerRemoved = removedHandlers.exists(h => BackendUtils.isLabelReachable(h.start))
traceIfChanged("staleHandlers")

// SIMPLIFY JUMPS
Expand Down Expand Up @@ -496,16 +498,16 @@ abstract class LocalOpt {
}

/**
* Removes unreachable basic blocks.
* Removes unreachable basic blocks, returns `true` if instructions were removed.
*
* @return A set containing eliminated instructions, and a set containing all live label nodes.
* When this method returns, each `labelNode.getLabel` has a status set whether the label is live
* or not. This can be queried using `BackendUtils.isLabelReachable`.
*/
def removeUnreachableCodeImpl(method: MethodNode, ownerClassName: InternalName): (Boolean, Set[LabelNode]) = {
def removeUnreachableCodeImpl(method: MethodNode, ownerClassName: InternalName): Boolean = {
val a = new AsmAnalyzer(method, ownerClassName)
val frames = a.analyzer.getFrames

var i = 0
var liveLabels = Set.empty[LabelNode]
var changed = false
var maxLocals = parametersSize(method)
var maxStack = 0
Expand All @@ -518,7 +520,7 @@ abstract class LocalOpt {
insn match {
case l: LabelNode =>
// label nodes are not removed: they might be referenced for example in a LocalVariableNode
if (isLive) liveLabels += l
if (isLive) BackendUtils.setLabelReachable(l) else BackendUtils.clearLabelReachable(l)

case v: VarInsnNode if isLive =>
val longSize = if (isSize2LoadOrStore(v.getOpcode)) 1 else 0
Expand All @@ -544,7 +546,7 @@ abstract class LocalOpt {
}
method.maxLocals = maxLocals
method.maxStack = maxStack
(changed, liveLabels)
changed
}

/**
Expand Down Expand Up @@ -724,6 +726,9 @@ object LocalOptImpls {
/**
* Removes LineNumberNodes that don't describe any executable instructions.
*
* As a side-effect, this traversal removes the `LABEL_REACHABLE_STATUS` flag from all label's
* `status` fields.
*
* This method expects (and asserts) that the `start` label of each LineNumberNode is the
* lexically preceding label declaration.
*/
Expand All @@ -740,7 +745,9 @@ object LocalOptImpls {
var previousLabel: LabelNode = null
while (iterator.hasNext) {
iterator.next match {
case label: LabelNode => previousLabel = label
case label: LabelNode =>
BackendUtils.clearLabelReachable(label)
previousLabel = label
case line: LineNumberNode if isEmpty(line) =>
assert(line.start == previousLabel)
iterator.remove()
Expand Down

0 comments on commit 6e3c5fc

Please sign in to comment.