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 7ffbc340bc2..0d9d47b1d44 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 @@ -308,6 +308,20 @@ class K2JSCompilerArguments : CommonCompilerArguments() { field = value } + @Argument( + value = "-Xir-dump-declaration-ir-sizes-to-file", + valueDescription = "", + description = "Dump the IR size of each declaration to a file. " + + "The format will be chosen automatically depending 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 irDceDumpDeclarationIrSizesToFile: 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 ef4b7d583dd..b8072a7f8db 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 @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity +import org.jetbrains.kotlin.ir.backend.js.dce.dumpDeclarationIrSizesIfNeed import org.jetbrains.kotlin.ir.backend.js.ic.* import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputsBuilt import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer @@ -354,6 +355,8 @@ class K2JsIrCompiler : CLICompiler() { eliminateDeadDeclarations(allModules, backendContext) } + dumpDeclarationIrSizesIfNeed(arguments.irDceDumpDeclarationIrSizesToFile, allModules) + val generateSourceMaps = configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP) val res = compileWasm( 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 3eb55129d10..9fb145c3577 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 @@ -299,11 +299,9 @@ private fun transformToStringBy( 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) + transformer(it.source.fqNameForDceDump(), it.target.fqNameForDceDump(), it.description, it.isTargetContagious) } .distinct() .joinToString(separator) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/utils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/utils.kt new file mode 100644 index 00000000000..9c0f466445a --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/utils.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.dce + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.backend.js.lower.PrimaryConstructorLowering +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.util.dumpKotlinLike +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import java.io.File + +internal fun IrDeclaration.fqNameForDceDump(): String { + // TODO: sanitize names + val fqn = (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "" + val signature = when (this is IrFunction) { + true -> this.valueParameters.joinToString(prefix = "(", postfix = ")") { it.type.dumpKotlinLike() } + else -> "" + } + val synthetic = when (this.origin == PrimaryConstructorLowering.SYNTHETIC_PRIMARY_CONSTRUCTOR) { + true -> "[synthetic]" + else -> "" + } + + return (fqn + signature + synthetic) +} + +fun dumpDeclarationIrSizesIfNeed(path: String?, allModules: List) { + if (path == null) return + + val declarations = linkedSetOf() + + allModules.forEach { + it.acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitDeclaration(declaration: IrDeclarationBase) { + when (declaration) { + is IrFunction, + is IrProperty, + is IrField, + is IrAnonymousInitializer -> { + declarations.add(declaration) + } + } + + super.visitDeclaration(declaration) + } + }) + } + + val out = File(path) + val (prefix, postfix, separator, indent) = when (out.extension) { + "json" -> listOf("{\n", "\n}", ",\n", " ") + "js" -> listOf("const kotlinDeclarationsSize = {\n", "\n};\n", ",\n", " ") + else -> listOf("", "", "\n", "") + } + + val value = declarations.joinToString(separator, prefix, postfix) { + val fqn = it.fqNameForDceDump() + val size = it.dumpKotlinLike().length + "$indent\"$fqn\" : $size" + } + + out.writeText(value) +} diff --git a/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/BasicWasmBoxTest.kt b/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/BasicWasmBoxTest.kt index 1e278117166..97f74e04cef 100644 --- a/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/BasicWasmBoxTest.kt +++ b/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/BasicWasmBoxTest.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.ir.backend.js.dce.dumpDeclarationIrSizesIfNeed import org.jetbrains.kotlin.ir.backend.js.prepareAnalyzedSourceModule import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.js.config.JSConfigurationKeys @@ -37,7 +38,6 @@ import org.jetbrains.kotlin.test.* import org.jetbrains.kotlin.test.util.KtTestUtil import java.io.Closeable import java.io.File -import kotlin.math.abs abstract class BasicWasmBoxTest( private val pathToTestDir: String, @@ -167,6 +167,8 @@ abstract class BasicWasmBoxTest( eliminateDeadDeclarations(allModules, backendContext) + dumpDeclarationIrSizesIfNeed(System.getProperty("kotlin.wasm.dump.declaration.ir.size.to.file"), allModules) + val compilerResultWithDCE = compileWasm( allModules = allModules, backendContext = backendContext,