From 62b21ac078046fabd87b9c4141ee55a020e2cc8a Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Fri, 21 Apr 2023 12:55:30 +0200 Subject: [PATCH] [Wasm] Add an option to dump reachability info to file --- .../common/arguments/K2JSCompilerArguments.kt | 14 ++++ .../jetbrains/kotlin/cli/js/K2JsIrCompiler.kt | 1 + .../js/dce/UsefulDeclarationProcessor.kt | 84 ++++++++++++++++--- .../jetbrains/kotlin/backend/wasm/dce/Dce.kt | 7 +- .../dce/WasmUsefulDeclarationProcessor.kt | 5 +- .../kotlin/js/config/JSConfigurationKeys.java | 3 + 6 files changed, 98 insertions(+), 16 deletions(-) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt index 6b096cdb8a9..7ffbc340bc2 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt @@ -294,6 +294,20 @@ class K2JSCompilerArguments : CommonCompilerArguments() { field = value } + @Argument( + value = "-Xir-dce-dump-reachability-info-to-file", + valueDescription = "", + 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) { diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt index 38e286a8446..ef4b7d583dd 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt @@ -723,6 +723,7 @@ class K2JsIrCompiler : CLICompiler() { 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, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt index 7a8aef82149..a689785e897 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt @@ -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() ?: "" - val toFqn = (to as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "" - 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>() - protected val constructedClasses = hashSetOf() - private val reachabilityInfo: MutableSet = if (printReachabilityInfo) linkedSetOf() else Collections.emptySet() + protected val constructedClasses = linkedSetOf() + private val reachabilityInfos = + if (printReachabilityInfo || dumpReachabilityInfoToFile != null) mutableListOf() else null private val queue = ArrayDeque() protected val result = hashSetOf() - protected val classesWithObjectAssociations = hashSetOf() + protected val classesWithObjectAssociations = linkedSetOf() val usefulPolyfilledDeclarations = hashSetOf() @@ -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 } -} \ No newline at end of file +} + +private data class ReachabilityInfo( + val source: IrDeclaration, + val target: IrDeclaration, + val description: String?, + val isTargetContagious: Boolean +) + +private fun transformToStringBy( + reachabilityInfos: List, + separator: String, + transformer: (sourceFqn: String, targetFqn: String, description: String, isTargetContagious: Boolean) -> String +): String { + fun IrDeclaration.fqnOrUnknown() = (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "" + + return reachabilityInfos + .map { + transformer(it.source.fqnOrUnknown(), it.target.fqnOrUnknown(), it.description ?: "", it.isTargetContagious) + } + .distinct() + .joinToString(separator) +} + +private fun transformToDotLikeString(reachabilityInfos: List): 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): 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): String { + return "const kotlinReachabilityInfos = " + transformToJsonString(reachabilityInfos) + ";" +} diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/Dce.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/Dce.kt index 80acf7aedeb..0c044d54bcd 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/Dce.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/Dce.kt @@ -20,9 +20,14 @@ fun eliminateDeadDeclarations(modules: List, 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) diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt index 9665cff507b..0740ca5f5ef 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt @@ -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() diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java index cb747671042..068d9606830 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java @@ -94,6 +94,9 @@ public class JSConfigurationKeys { public static final CompilerConfigurationKey PRINT_REACHABILITY_INFO = CompilerConfigurationKey.create("print declarations' reachability info during performing DCE"); + public static final CompilerConfigurationKey DUMP_REACHABILITY_INFO_TO_FILE = + CompilerConfigurationKey.create("dump declarations' reachability info to file during performing DCE"); + public static final CompilerConfigurationKey FAKE_OVERRIDE_VALIDATOR = CompilerConfigurationKey.create("IR fake override validator");