[Wasm] Add an option to dump reachability info to file
This commit is contained in:
+14
@@ -294,6 +294,20 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
|||||||
field = value
|
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")
|
@Argument(value = "-Xir-property-lazy-initialization", description = "Perform lazy initialization for properties")
|
||||||
var irPropertyLazyInitialization = true
|
var irPropertyLazyInitialization = true
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|||||||
@@ -723,6 +723,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
|||||||
|
|
||||||
configuration.put(JSConfigurationKeys.PRINT_REACHABILITY_INFO, arguments.irDcePrintReachabilityInfo)
|
configuration.put(JSConfigurationKeys.PRINT_REACHABILITY_INFO, arguments.irDcePrintReachabilityInfo)
|
||||||
configuration.put(JSConfigurationKeys.FAKE_OVERRIDE_VALIDATOR, arguments.fakeOverrideValidator)
|
configuration.put(JSConfigurationKeys.FAKE_OVERRIDE_VALIDATOR, arguments.fakeOverrideValidator)
|
||||||
|
configuration.putIfNotNull(JSConfigurationKeys.DUMP_REACHABILITY_INFO_TO_FILE, arguments.irDceDumpReachabilityInfoToFile)
|
||||||
|
|
||||||
configuration.setupPartialLinkageConfig(
|
configuration.setupPartialLinkageConfig(
|
||||||
mode = arguments.partialLinkageMode,
|
mode = arguments.partialLinkageMode,
|
||||||
|
|||||||
+71
-13
@@ -16,11 +16,13 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
|||||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class UsefulDeclarationProcessor(
|
abstract class UsefulDeclarationProcessor(
|
||||||
private val printReachabilityInfo: Boolean,
|
private val printReachabilityInfo: Boolean,
|
||||||
protected val removeUnusedAssociatedObjects: Boolean
|
protected val removeUnusedAssociatedObjects: Boolean,
|
||||||
|
private val dumpReachabilityInfoToFile: String? = null
|
||||||
) {
|
) {
|
||||||
abstract val context: JsCommonBackendContext
|
abstract val context: JsCommonBackendContext
|
||||||
|
|
||||||
@@ -85,12 +87,7 @@ abstract class UsefulDeclarationProcessor(
|
|||||||
description: String?,
|
description: String?,
|
||||||
isContagiousOverridableDeclaration: Boolean,
|
isContagiousOverridableDeclaration: Boolean,
|
||||||
) {
|
) {
|
||||||
if (!printReachabilityInfo) return
|
reachabilityInfos?.add(ReachabilityInfo(from, to, description, isContagiousOverridableDeclaration))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun IrDeclaration.enqueue(
|
protected fun IrDeclaration.enqueue(
|
||||||
@@ -133,11 +130,12 @@ abstract class UsefulDeclarationProcessor(
|
|||||||
//
|
//
|
||||||
// The collection must be a subset of [result] set.
|
// The collection must be a subset of [result] set.
|
||||||
private val contagiousReachableDeclarations = hashSetOf<IrOverridableDeclaration<*>>()
|
private val contagiousReachableDeclarations = hashSetOf<IrOverridableDeclaration<*>>()
|
||||||
protected val constructedClasses = hashSetOf<IrClass>()
|
protected val constructedClasses = linkedSetOf<IrClass>()
|
||||||
private val reachabilityInfo: MutableSet<String> = if (printReachabilityInfo) linkedSetOf() else Collections.emptySet()
|
private val reachabilityInfos =
|
||||||
|
if (printReachabilityInfo || dumpReachabilityInfoToFile != null) mutableListOf<ReachabilityInfo>() else null
|
||||||
private val queue = ArrayDeque<IrDeclaration>()
|
private val queue = ArrayDeque<IrDeclaration>()
|
||||||
protected val result = hashSetOf<IrDeclaration>()
|
protected val result = hashSetOf<IrDeclaration>()
|
||||||
protected val classesWithObjectAssociations = hashSetOf<IrClass>()
|
protected val classesWithObjectAssociations = linkedSetOf<IrClass>()
|
||||||
|
|
||||||
val usefulPolyfilledDeclarations = hashSetOf<IrDeclaration>()
|
val usefulPolyfilledDeclarations = hashSetOf<IrDeclaration>()
|
||||||
|
|
||||||
@@ -268,10 +266,70 @@ abstract class UsefulDeclarationProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (printReachabilityInfo) {
|
if (reachabilityInfos != null) {
|
||||||
reachabilityInfo.forEach(::println)
|
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
|
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) ||
|
context.configuration.getBoolean(JSConfigurationKeys.PRINT_REACHABILITY_INFO) ||
|
||||||
java.lang.Boolean.getBoolean("kotlin.wasm.dce.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(
|
val usefulDeclarations = WasmUsefulDeclarationProcessor(
|
||||||
context = context,
|
context = context,
|
||||||
printReachabilityInfo = printReachabilityInfo
|
printReachabilityInfo = printReachabilityInfo,
|
||||||
|
dumpReachabilityInfoToFile
|
||||||
).collectDeclarations(rootDeclarations = buildRoots(modules, context))
|
).collectDeclarations(rootDeclarations = buildRoots(modules, context))
|
||||||
|
|
||||||
val remover = WasmUselessDeclarationsRemover(usefulDeclarations)
|
val remover = WasmUselessDeclarationsRemover(usefulDeclarations)
|
||||||
|
|||||||
+3
-2
@@ -18,8 +18,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
|||||||
|
|
||||||
internal class WasmUsefulDeclarationProcessor(
|
internal class WasmUsefulDeclarationProcessor(
|
||||||
override val context: WasmBackendContext,
|
override val context: WasmBackendContext,
|
||||||
printReachabilityInfo: Boolean
|
printReachabilityInfo: Boolean,
|
||||||
) : UsefulDeclarationProcessor(printReachabilityInfo, removeUnusedAssociatedObjects = false) {
|
dumpReachabilityInfoToFile: String?
|
||||||
|
) : UsefulDeclarationProcessor(printReachabilityInfo, removeUnusedAssociatedObjects = false, dumpReachabilityInfoToFile) {
|
||||||
|
|
||||||
private val unitGetInstance: IrSimpleFunction = context.findUnitGetInstanceFunction()
|
private val unitGetInstance: IrSimpleFunction = context.findUnitGetInstanceFunction()
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ public class JSConfigurationKeys {
|
|||||||
public static final CompilerConfigurationKey<Boolean> PRINT_REACHABILITY_INFO =
|
public static final CompilerConfigurationKey<Boolean> PRINT_REACHABILITY_INFO =
|
||||||
CompilerConfigurationKey.create("print declarations' reachability info during performing DCE");
|
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 =
|
public static final CompilerConfigurationKey<Boolean> FAKE_OVERRIDE_VALIDATOR =
|
||||||
CompilerConfigurationKey.create("IR fake override validator");
|
CompilerConfigurationKey.create("IR fake override validator");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user