JS DCE: use less LinkedHashSets

This commit is contained in:
Anton Bannykh
2021-01-28 14:05:09 +03:00
parent 3c0b226344
commit bbc6d2b993
6 changed files with 44 additions and 17 deletions
@@ -78,11 +78,10 @@ class K2JSDce : CLITool<K2JSDceArguments>() {
val dceResult = DeadCodeElimination.run(files, includedDeclarations, logConsumer) val dceResult = DeadCodeElimination.run(files, includedDeclarations, logConsumer)
if (dceResult.status == DeadCodeEliminationStatus.FAILED) return ExitCode.COMPILATION_ERROR if (dceResult.status == DeadCodeEliminationStatus.FAILED) return ExitCode.COMPILATION_ERROR
val nodes = dceResult.reachableNodes.filterTo(mutableSetOf()) { it.reachable }
val reachabilitySeverity = if (arguments.printReachabilityInfo) CompilerMessageSeverity.INFO else CompilerMessageSeverity.LOGGING val reachabilitySeverity = if (arguments.printReachabilityInfo) CompilerMessageSeverity.INFO else CompilerMessageSeverity.LOGGING
messageCollector.report(reachabilitySeverity, "") messageCollector.report(reachabilitySeverity, "")
for (node in nodes.extractRoots()) { for (node in dceResult.reachableNodes.extractReachableRoots(dceResult.context!!)) {
printTree( printTree(
node, { messageCollector.report(reachabilitySeverity, it) }, node, { messageCollector.report(reachabilitySeverity, it) },
printNestedMembers = false, showLocations = true printNestedMembers = false, showLocations = true
@@ -126,6 +126,14 @@ class Context {
} }
} }
private var currentColor = 1.toByte()
fun clearVisited() {
currentColor++
}
fun visit(n: Node) = n.visit(currentColor)
class Node private constructor(val localName: JsName?, qualifier: Qualifier?) { class Node private constructor(val localName: JsName?, qualifier: Qualifier?) {
private var _dependenciesImpl: MutableSet<Node>? = null private var _dependenciesImpl: MutableSet<Node>? = null
private var _expressionsImpl: MutableSet<JsExpression>? = null private var _expressionsImpl: MutableSet<JsExpression>? = null
@@ -178,6 +186,14 @@ class Context {
var qualifier: Qualifier? = qualifier var qualifier: Qualifier? = qualifier
private set private set
private var color: Byte = 0
fun visit(c: Byte): Boolean {
val result = color != c
color = c
return result
}
val memberNames: Set<String> get() = original._membersImpl?.keys ?: emptySet() val memberNames: Set<String> get() = original._membersImpl?.keys ?: emptySet()
constructor(localName: JsName? = null) : this(localName, null) constructor(localName: JsName? = null) : this(localName, null)
@@ -42,11 +42,14 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit
val moduleMapping = mutableMapOf<JsBlock, String>() val moduleMapping = mutableMapOf<JsBlock, String>()
private val reachableNames = mutableSetOf<String>() private val reachableNames = mutableSetOf<String>()
var reachableNodes = setOf<Node>() var reachableNodes: Iterable<Node> = setOf()
private set private set
var context: Context? = null
fun apply(root: JsNode) { fun apply(root: JsNode) {
val context = Context() val context = Context()
this.context = context
val topLevelVars = collectDefinedNames(root) val topLevelVars = collectDefinedNames(root)
context.addNodesForLocalVars(topLevelVars) context.addNodesForLocalVars(topLevelVars)
@@ -107,7 +110,7 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit
block block
} }
if (hasErrors) return DeadCodeEliminationResult(emptySet(), DeadCodeEliminationStatus.FAILED) if (hasErrors) return DeadCodeEliminationResult(dce.context, emptySet(), DeadCodeEliminationStatus.FAILED)
program.globalBlock.statements += blocks program.globalBlock.statements += blocks
program.globalBlock.fixForwardNameReferences() program.globalBlock.fixForwardNameReferences()
@@ -139,7 +142,7 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit
} }
} }
return DeadCodeEliminationResult(dce.reachableNodes, DeadCodeEliminationStatus.OK) return DeadCodeEliminationResult(dce.context, dce.reachableNodes, DeadCodeEliminationStatus.OK)
} }
private class Reporter(private val fileName: String, private val logConsumer: (DCELogLevel, String) -> Unit) : ErrorReporter { private class Reporter(private val fileName: String, private val logConsumer: (DCELogLevel, String) -> Unit) : ErrorReporter {
@@ -16,4 +16,4 @@
package org.jetbrains.kotlin.js.dce package org.jetbrains.kotlin.js.dce
class DeadCodeEliminationResult(val reachableNodes: Set<Context.Node>, val status: DeadCodeEliminationStatus) class DeadCodeEliminationResult(val context: Context?, val reachableNodes: Iterable<Context.Node>, val status: DeadCodeEliminationStatus)
@@ -29,11 +29,15 @@ class ReachabilityTracker(
private val CALL_FUNCTIONS = setOf("call", "apply") private val CALL_FUNCTIONS = setOf("call", "apply")
} }
init {
context.clearVisited()
}
private var currentNodeWithLocation: JsNode? = null private var currentNodeWithLocation: JsNode? = null
private var depth = 0 private var depth = 0
private val reachableNodesImpl = mutableSetOf<Node>() private val reachableNodesImpl = mutableListOf<Node>()
val reachableNodes: Set<Node> get() = reachableNodesImpl val reachableNodes: Iterable<Node> get() = reachableNodesImpl
override fun visit(x: JsVars.JsVar) { override fun visit(x: JsVars.JsVar) {
if (shouldTraverse(x)) { if (shouldTraverse(x)) {
@@ -137,7 +141,9 @@ class ReachabilityTracker(
fun reach(node: Node) { fun reach(node: Node) {
if (node.reachable) return if (node.reachable) return
node.reachable = true node.reachable = true
reachableNodesImpl += node if (context.visit(node)) {
reachableNodesImpl += node
}
reachDeclaration(node) reachDeclaration(node)
@@ -192,7 +198,9 @@ class ReachabilityTracker(
} }
else if (!node.declarationReachable) { else if (!node.declarationReachable) {
node.declarationReachable = true node.declarationReachable = true
reachableNodesImpl += node if (context.visit(node)) {
reachableNodesImpl += node
}
node.original.qualifier?.parent?.let { node.original.qualifier?.parent?.let {
reportAndNest("reach-decl: parent $it", null) { reportAndNest("reach-decl: parent $it", null) {
@@ -73,20 +73,21 @@ fun JsLocation.asString(): String {
return "$simpleFileName:${startLine + 1}" return "$simpleFileName:${startLine + 1}"
} }
fun Set<Node>.extractRoots(): Set<Node> { fun Iterable<Node>.extractReachableRoots(context: Context): Iterable<Node> {
val result = mutableSetOf<Node>() context.clearVisited()
val visited = mutableSetOf<Node>()
forEach { it.original.extractRootsImpl(result, visited) } val result = mutableListOf<Node>()
forEach { if (it.reachable) it.original.extractRootsImpl(result, context) }
return result return result
} }
private fun Node.extractRootsImpl(target: MutableSet<Node>, visited: MutableSet<Node>) { private fun Node.extractRootsImpl(target: MutableList<Node>, context: Context) {
if (!visited.add(original)) return if (!context.visit(original)) return
val qualifier = original.qualifier val qualifier = original.qualifier
if (qualifier == null) { if (qualifier == null) {
target += original target += original
} }
else { else {
qualifier.parent.extractRootsImpl(target, visited) qualifier.parent.extractRootsImpl(target, context)
} }
} }