[Wasm] Add an option to dump reachability info to file

This commit is contained in:
Zalim Bashorov
2023-04-21 12:55:30 +02:00
parent 7486a11bd1
commit 62b21ac078
6 changed files with 98 additions and 16 deletions
@@ -294,6 +294,20 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
field = value
}
@Argument(
value = "-Xir-dce-dump-reachability-info-to-file",
valueDescription = "<path>",
description = "Dump declarations' reachability info collected during performing DCE to a file. " +
"The format will be chosen automatically based on the file extension. " +
"Supported output formats include JSON for .json, JS const initialized with a plain object containing information for .js, " +
"and plain text for all other file types."
)
var irDceDumpReachabilityInfoToFile: String? = null
set(value) {
checkFrozen()
field = value
}
@Argument(value = "-Xir-property-lazy-initialization", description = "Perform lazy initialization for properties")
var irPropertyLazyInitialization = true
set(value) {
@@ -723,6 +723,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
configuration.put(JSConfigurationKeys.PRINT_REACHABILITY_INFO, arguments.irDcePrintReachabilityInfo)
configuration.put(JSConfigurationKeys.FAKE_OVERRIDE_VALIDATOR, arguments.fakeOverrideValidator)
configuration.putIfNotNull(JSConfigurationKeys.DUMP_REACHABILITY_INFO_TO_FILE, arguments.irDceDumpReachabilityInfoToFile)
configuration.setupPartialLinkageConfig(
mode = arguments.partialLinkageMode,
@@ -16,11 +16,13 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import java.io.File
import java.util.*
abstract class UsefulDeclarationProcessor(
private val printReachabilityInfo: Boolean,
protected val removeUnusedAssociatedObjects: Boolean
protected val removeUnusedAssociatedObjects: Boolean,
private val dumpReachabilityInfoToFile: String? = null
) {
abstract val context: JsCommonBackendContext
@@ -85,12 +87,7 @@ abstract class UsefulDeclarationProcessor(
description: String?,
isContagiousOverridableDeclaration: Boolean,
) {
if (!printReachabilityInfo) return
val fromFqn = (from as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "<unknown>"
val toFqn = (to as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "<unknown>"
val comment = (description ?: "") + (if (isContagiousOverridableDeclaration) "[CONTAGIOUS!]" else "")
val info = "\"$fromFqn\" -> \"$toFqn\"" + (if (comment.isBlank()) "" else " // $comment")
reachabilityInfo.add(info)
reachabilityInfos?.add(ReachabilityInfo(from, to, description, isContagiousOverridableDeclaration))
}
protected fun IrDeclaration.enqueue(
@@ -133,11 +130,12 @@ abstract class UsefulDeclarationProcessor(
//
// The collection must be a subset of [result] set.
private val contagiousReachableDeclarations = hashSetOf<IrOverridableDeclaration<*>>()
protected val constructedClasses = hashSetOf<IrClass>()
private val reachabilityInfo: MutableSet<String> = if (printReachabilityInfo) linkedSetOf() else Collections.emptySet()
protected val constructedClasses = linkedSetOf<IrClass>()
private val reachabilityInfos =
if (printReachabilityInfo || dumpReachabilityInfoToFile != null) mutableListOf<ReachabilityInfo>() else null
private val queue = ArrayDeque<IrDeclaration>()
protected val result = hashSetOf<IrDeclaration>()
protected val classesWithObjectAssociations = hashSetOf<IrClass>()
protected val classesWithObjectAssociations = linkedSetOf<IrClass>()
val usefulPolyfilledDeclarations = hashSetOf<IrDeclaration>()
@@ -268,10 +266,70 @@ abstract class UsefulDeclarationProcessor(
}
}
if (printReachabilityInfo) {
reachabilityInfo.forEach(::println)
if (reachabilityInfos != null) {
if (printReachabilityInfo) {
println(transformToDotLikeString(reachabilityInfos))
}
if (dumpReachabilityInfoToFile != null) {
val out = File(dumpReachabilityInfoToFile)
val stringify = when (out.extension) {
"json" -> ::transformToJsonString
"js" -> ::transformToJsConstDeclaration
else -> ::transformToDotLikeString
}
out.writeText(stringify(reachabilityInfos))
}
}
return result
}
}
}
private data class ReachabilityInfo(
val source: IrDeclaration,
val target: IrDeclaration,
val description: String?,
val isTargetContagious: Boolean
)
private fun transformToStringBy(
reachabilityInfos: List<ReachabilityInfo>,
separator: String,
transformer: (sourceFqn: String, targetFqn: String, description: String, isTargetContagious: Boolean) -> String
): String {
fun IrDeclaration.fqnOrUnknown() = (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "<unknown>"
return reachabilityInfos
.map {
transformer(it.source.fqnOrUnknown(), it.target.fqnOrUnknown(), it.description ?: "", it.isTargetContagious)
}
.distinct()
.joinToString(separator)
}
private fun transformToDotLikeString(reachabilityInfos: List<ReachabilityInfo>): String {
return transformToStringBy(reachabilityInfos, "\n") { sourceFqn, targetFqn, description, isTargetContagious ->
val comment = description + (if (isTargetContagious) "[CONTAGIOUS!]" else "")
val info = "\"$sourceFqn\" -> \"$targetFqn\"" + (if (comment.isBlank()) "" else " // $comment")
info
}
}
private fun transformToJsonString(reachabilityInfos: List<ReachabilityInfo>): String {
return "[\n" + transformToStringBy(reachabilityInfos, ",\n") { sourceFqn, targetFqn, description, isTargetContagious ->
"""
| {
| "source" : "$sourceFqn",
| "target" : "$targetFqn",
| "description" : "$description",
| "isTargetContagious" : $isTargetContagious
| }""".trimMargin()
} + "\n]"
}
private fun transformToJsConstDeclaration(reachabilityInfos: List<ReachabilityInfo>): String {
return "const kotlinReachabilityInfos = " + transformToJsonString(reachabilityInfos) + ";"
}
@@ -20,9 +20,14 @@ fun eliminateDeadDeclarations(modules: List<IrModuleFragment>, context: WasmBack
context.configuration.getBoolean(JSConfigurationKeys.PRINT_REACHABILITY_INFO) ||
java.lang.Boolean.getBoolean("kotlin.wasm.dce.print.reachability.info")
val dumpReachabilityInfoToFile: String? =
context.configuration.get(JSConfigurationKeys.DUMP_REACHABILITY_INFO_TO_FILE)
?: System.getProperty("kotlin.wasm.dce.dump.reachability.info.to.file")
val usefulDeclarations = WasmUsefulDeclarationProcessor(
context = context,
printReachabilityInfo = printReachabilityInfo
printReachabilityInfo = printReachabilityInfo,
dumpReachabilityInfoToFile
).collectDeclarations(rootDeclarations = buildRoots(modules, context))
val remover = WasmUselessDeclarationsRemover(usefulDeclarations)
@@ -18,8 +18,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
internal class WasmUsefulDeclarationProcessor(
override val context: WasmBackendContext,
printReachabilityInfo: Boolean
) : UsefulDeclarationProcessor(printReachabilityInfo, removeUnusedAssociatedObjects = false) {
printReachabilityInfo: Boolean,
dumpReachabilityInfoToFile: String?
) : UsefulDeclarationProcessor(printReachabilityInfo, removeUnusedAssociatedObjects = false, dumpReachabilityInfoToFile) {
private val unitGetInstance: IrSimpleFunction = context.findUnitGetInstanceFunction()
@@ -94,6 +94,9 @@ public class JSConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> PRINT_REACHABILITY_INFO =
CompilerConfigurationKey.create("print declarations' reachability info during performing DCE");
public static final CompilerConfigurationKey<String> DUMP_REACHABILITY_INFO_TO_FILE =
CompilerConfigurationKey.create("dump declarations' reachability info to file during performing DCE");
public static final CompilerConfigurationKey<Boolean> FAKE_OVERRIDE_VALIDATOR =
CompilerConfigurationKey.create("IR fake override validator");