[Wasm] Multimodule compilation
diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2WasmCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2WasmCompilerArguments.kt
index b794ac1..6a25ed7 100644
--- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2WasmCompilerArguments.kt
+++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2WasmCompilerArguments.kt
@@ -38,6 +38,13 @@
field = value
}
+ @Argument(value = "-Xwasm-multimodule-mode", description = "set multimodule compilation mode.")
+ var wasmMultimoduleCompilationMode: String? = null
+ set(value) {
+ checkFrozen()
+ field = value
+ }
+
@Argument(value = "-Xwasm-generate-wat", description = "Generate a .wat file.")
var wasmGenerateWat = false
set(value) {
diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmBackendPipelinePhase.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmBackendPipelinePhase.kt
index 8ebe60a..7f82f62 100644
--- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmBackendPipelinePhase.kt
+++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmBackendPipelinePhase.kt
@@ -11,8 +11,10 @@
import org.jetbrains.kotlin.backend.wasm.dce.eliminateDeadDeclarations
import org.jetbrains.kotlin.backend.wasm.ic.IrFactoryImplForWasmIC
import org.jetbrains.kotlin.backend.wasm.ic.WasmModuleArtifact
+import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCompiledFileFragment
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleFragmentGenerator
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleMetadataCache
+import org.jetbrains.kotlin.backend.wasm.ir2wasm.getAllReferencedDeclarations
import org.jetbrains.kotlin.backend.wasm.writeCompilationResult
import org.jetbrains.kotlin.cli.common.perfManager
import org.jetbrains.kotlin.cli.js.IcCachesArtifacts
@@ -21,13 +23,19 @@
import org.jetbrains.kotlin.cli.pipeline.web.WebBackendPipelinePhase
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.moduleName
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
import org.jetbrains.kotlin.ir.backend.js.WholeWorldStageController
import org.jetbrains.kotlin.ir.backend.js.dce.DceDumpNameCache
import org.jetbrains.kotlin.ir.backend.js.dce.dumpDeclarationIrSizesIfNeed
import org.jetbrains.kotlin.ir.backend.js.loadIr
+import org.jetbrains.kotlin.ir.backend.js.loadIrForMultimoduleMode
+import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
+import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.js.config.*
+import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.platform.wasm.WasmMultimoduleMode
import org.jetbrains.kotlin.wasm.config.WasmConfigurationKeys
import java.io.File
@@ -125,6 +133,41 @@
wasmDebug: Boolean,
generateDwarf: Boolean
): WasmCompilerResult {
+ val wasmMultimoduleCompilationMode = configuration.get(WasmConfigurationKeys.WASM_MULTIMODULE_MODE) ?: WasmMultimoduleMode.NONE
+ return when (wasmMultimoduleCompilationMode) {
+ WasmMultimoduleMode.NONE -> compileNormalMode(
+ configuration = configuration,
+ module = module,
+ outputName = outputName,
+ outputDir = outputDir,
+ propertyLazyInitialization = propertyLazyInitialization,
+ dce = dce,
+ dceDumpDeclarationIrSizesToFile = dceDumpDeclarationIrSizesToFile,
+ wasmDebug = wasmDebug,
+ generateDwarf = generateDwarf,
+ )
+ WasmMultimoduleMode.SLAVE, WasmMultimoduleMode.MASTER -> compileMultimoduleMode(
+ configuration = configuration,
+ module = module,
+ outputName = outputName,
+ outputDir = outputDir,
+ propertyLazyInitialization = propertyLazyInitialization,
+ isMaster = wasmMultimoduleCompilationMode == WasmMultimoduleMode.MASTER
+ )
+ }
+ }
+
+ internal fun compileNormalMode(
+ configuration: CompilerConfiguration,
+ module: ModulesStructure,
+ outputName: String,
+ outputDir: File,
+ propertyLazyInitialization: Boolean,
+ dce: Boolean,
+ dceDumpDeclarationIrSizesToFile: String?,
+ wasmDebug: Boolean,
+ generateDwarf: Boolean
+ ): WasmCompilerResult {
val performanceManager = configuration.perfManager
val generateDts = configuration.getBoolean(JSConfigurationKeys.GENERATE_DTS)
val generateSourceMaps = configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)
@@ -165,7 +208,9 @@
irFactory,
allowIncompleteImplementations = dce,
skipCommentInstructions = !generateWat,
+ useStringPool = true
)
+
val wasmCompiledFileFragments = allModules.map { codeGenerator.generateModuleAsSingleFileFragment(it) }
val res = compileWasm(
@@ -195,4 +240,123 @@
return res
}
+
+ private fun compileMultimoduleMode(
+ configuration: CompilerConfiguration,
+ module: ModulesStructure,
+ outputName: String,
+ outputDir: File,
+ propertyLazyInitialization: Boolean,
+ isMaster: Boolean,
+ ): WasmCompilerResult {
+ val performanceManager = configuration.perfManager
+
+ val irFactory = IrFactoryImplForWasmIC(WholeWorldStageController())
+
+ val masterDeserializer: (JsIrLinker, ModuleDescriptor, KotlinLibrary) -> IrModuleFragment
+ val slaveDeserializer: (JsIrLinker, ModuleDescriptor, KotlinLibrary) -> IrModuleFragment
+ if (isMaster) {
+ masterDeserializer = JsIrLinker::deserializeFullModule
+ slaveDeserializer = JsIrLinker::deserializeOnlyHeaderModule
+ } else {
+ masterDeserializer = JsIrLinker::deserializeHeadersWithInlineBodies
+ slaveDeserializer = JsIrLinker::deserializeFullModule
+ }
+
+ val irModuleInfo = loadIrForMultimoduleMode(
+ depsDescriptors = module,
+ irFactory = irFactory,
+ masterDeserializer = masterDeserializer,
+ slaveDeserializer = slaveDeserializer,
+ )
+
+ //Hack - pre-load functional interfaces in case if IrLoader cut its count (KT-71039)
+ if (isMaster) {
+ repeat(25) {
+ irModuleInfo.bultins.functionN(it)
+ irModuleInfo.bultins.suspendFunctionN(it)
+ irModuleInfo.bultins.kFunctionN(it)
+ irModuleInfo.bultins.kSuspendFunctionN(it)
+ }
+ }
+
+ val (allModules, backendContext, typeScriptFragment) = compileToLoweredIr(
+ irModuleInfo,
+ module.mainModule,
+ configuration,
+ performanceManager,
+ exportedDeclarations = setOf(FqName("main")),
+ generateTypeScriptFragment = false,
+ propertyLazyInitialization = propertyLazyInitialization,
+ isIncremental = true,
+ )
+
+ performanceManager?.notifyIRGenerationStarted()
+
+ val generateWat = configuration.get(WasmConfigurationKeys.WASM_GENERATE_WAT, false)
+
+ val wasmModuleMetadataCache = WasmModuleMetadataCache(backendContext)
+ val codeGenerator = WasmModuleFragmentGenerator(
+ backendContext,
+ wasmModuleMetadataCache,
+ irFactory,
+ allowIncompleteImplementations = false,
+ skipCommentInstructions = !generateWat,
+ useStringPool = isMaster
+ )
+
+ val slaveModule = allModules.last()
+ val masterModules = allModules.dropLast(1)
+ val wasmCompiledFileFragments = mutableListOf<WasmCompiledFileFragment>()
+ val masterModuleName: String?
+ if (isMaster) {
+ masterModules.mapTo(wasmCompiledFileFragments) {
+ codeGenerator.generateModuleAsSingleFileFragmentWithIECExport(it)
+ }
+ masterModuleName = null
+ } else {
+ masterModuleName = "$outputName.master"
+ val slaveModuleFileFragment = codeGenerator.generateModuleAsSingleFileFragment(slaveModule)
+
+ val importedDeclarations = getAllReferencedDeclarations(slaveModuleFileFragment)
+ masterModules.mapTo(wasmCompiledFileFragments) {
+ codeGenerator.generateModuleAsSingleFileFragmentWithIECImport(it, masterModuleName, importedDeclarations)
+ }
+ wasmCompiledFileFragments.add(slaveModuleFileFragment)
+ }
+
+ val moduleName = if (isMaster) "${slaveModule.name.asString()}.master" else slaveModule.name.asString()
+ val outputFileName = if (isMaster) "${outputName}_master" else outputName
+
+ val res = compileWasm(
+ wasmCompiledFileFragments = wasmCompiledFileFragments,
+ moduleName = moduleName,
+ configuration = configuration,
+ typeScriptFragment = typeScriptFragment,
+ baseFileName = outputFileName,
+ emitNameSection = false,
+ generateWat = generateWat,
+ generateDwarf = false,
+ generateSourceMaps = false,
+ useDebuggerCustomFormatters = false,
+ moduleImportName = masterModuleName,
+ initializeUnit = isMaster,
+ exportThrowableTag = isMaster,
+ initializeStringPool = isMaster,
+ )
+
+ performanceManager?.notifyIRGenerationFinished()
+ performanceManager?.notifyGenerationFinished()
+
+ writeCompilationResult(
+ result = res,
+ dir = outputDir,
+ fileNameBase = outputFileName,
+ useDebuggerCustomFormatters = false
+ )
+
+ performanceManager?.notifyIRTranslationFinished()
+
+ return res
+ }
}
diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmConfigurationUpdater.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmConfigurationUpdater.kt
index b306209..39511ac 100644
--- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmConfigurationUpdater.kt
+++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/pipeline/web/wasm/WasmConfigurationUpdater.kt
@@ -16,6 +16,7 @@
import org.jetbrains.kotlin.config.phaseConfig
import org.jetbrains.kotlin.js.config.propertyLazyInitialization
import org.jetbrains.kotlin.js.config.wasmCompilation
+import org.jetbrains.kotlin.platform.wasm.WasmMultimoduleMode
import org.jetbrains.kotlin.platform.wasm.WasmTarget
import org.jetbrains.kotlin.wasm.config.WasmConfigurationKeys
@@ -50,6 +51,10 @@
configuration.put(WasmConfigurationKeys.WASM_USE_JS_TAG, arguments.wasmUseJsTag ?: arguments.wasmUseNewExceptionProposal)
configuration.put(WasmConfigurationKeys.WASM_GENERATE_DWARF, arguments.generateDwarf)
configuration.put(WasmConfigurationKeys.WASM_FORCE_DEBUG_FRIENDLY_COMPILATION, arguments.forceDebugFriendlyCompilation)
+ configuration.put(
+ WasmConfigurationKeys.WASM_MULTIMODULE_MODE,
+ arguments.wasmMultimoduleCompilationMode?.let { WasmMultimoduleMode.fromName(it) } ?: WasmMultimoduleMode.NONE
+ )
configuration.putIfNotNull(WasmConfigurationKeys.WASM_TARGET, arguments.wasmTarget?.let(WasmTarget::fromName))
configuration.putIfNotNull(WasmConfigurationKeys.DCE_DUMP_DECLARATION_IR_SIZES_TO_FILE, arguments.irDceDumpDeclarationIrSizesToFile)
configuration.propertyLazyInitialization = arguments.irPropertyLazyInitialization
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
index b32de78..8399e4b 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
@@ -231,6 +231,7 @@
val unboxIntrinsic: IrSimpleFunctionSymbol = getInternalFunction("unboxIntrinsic")
val stringGetLiteral = getFunction("stringLiteral", StandardNames.BUILT_INS_PACKAGE_FQ_NAME)
+ val createString = getFunction("createString", StandardNames.BUILT_INS_PACKAGE_FQ_NAME)
val stringGetPoolSize = getInternalFunction("stringGetPoolSize")
val testFun = maybeGetFunction("test", kotlinTestPackageFqName)
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compilerWithIC.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compilerWithIC.kt
index 03a9a57..2d6e1e5 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compilerWithIC.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compilerWithIC.kt
@@ -67,6 +67,7 @@
allowIncompleteImplementations,
if (safeFragmentTags) "${irFile.module.name.asString()}${irFile.path}" else null,
skipCommentInstructions = skipCommentInstructions,
+ useStringPool = true
)
)
}
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
index dfdd250..5fdee3b 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
@@ -38,6 +38,8 @@
private val functionContext: WasmFunctionCodegenContext,
private val wasmModuleMetadataCache: WasmModuleMetadataCache,
private val wasmModuleTypeTransformer: WasmModuleTypeTransformer,
+ private val useStringPool: Boolean,
+ private val inlineUnitGetter: Boolean,
) : IrVisitorVoid() {
val body: WasmExpressionBuilder = functionContext.bodyGen
@@ -49,10 +51,17 @@
private val unitInstanceField by lazy { backendContext.findUnitInstanceField() }
fun WasmExpressionBuilder.buildGetUnit() {
- buildGetGlobal(
- wasmFileCodegenContext.referenceGlobalField(unitInstanceField.symbol),
- SourceLocation.NoLocation("GET_UNIT")
- )
+ if (inlineUnitGetter) {
+ buildGetGlobal(
+ wasmFileCodegenContext.referenceGlobalField(unitInstanceField.symbol),
+ SourceLocation.NoLocation("GET_UNIT")
+ )
+ } else {
+ buildCall(
+ wasmFileCodegenContext.referenceFunction(unitGetInstance.symbol),
+ SourceLocation.NoLocation("GET_UNIT")
+ )
+ }
}
fun getStructFieldRef(field: IrField): WasmSymbol<Int> {
@@ -443,7 +452,7 @@
}
override fun visitConst(expression: IrConst): Unit =
- generateConstExpression(expression, body, wasmFileCodegenContext, backendContext, expression.getSourceLocation())
+ generateConstExpression(expression, body, wasmFileCodegenContext, backendContext, expression.getSourceLocation(), useStringPool)
override fun visitGetField(expression: IrGetField) {
val field: IrField = expression.symbol.owner
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt
index 1e0ac3f..9c23bc2 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt
@@ -36,6 +36,8 @@
private val wasmModuleMetadataCache: WasmModuleMetadataCache,
private val allowIncompleteImplementations: Boolean,
private val skipCommentInstructions: Boolean,
+ private val useStringPool: Boolean = true,
+ private val inlineUnitGetter: Boolean = true,
) : IrVisitorVoid() {
// Shortcuts
@@ -96,8 +98,9 @@
"Sanity check that $declaration is a real function that can be used in calls"
}
- val functionTypeSymbol = wasmFileCodegenContext.referenceFunctionType(declaration.symbol)
+ if (wasmFileCodegenContext.handleFunctionWithImport(declaration.symbol)) return
+ val functionTypeSymbol = wasmFileCodegenContext.referenceFunctionType(declaration.symbol)
val wasmImportModule = declaration.getWasmImportDescriptor()
val jsCode = declaration.getJsFunAnnotation()
@@ -159,6 +162,8 @@
functionCodegenContext,
wasmModuleMetadataCache,
wasmModuleTypeTransformer,
+ useStringPool,
+ inlineUnitGetter,
)
val declarationBody = declaration.body
@@ -311,6 +316,8 @@
if (klass.isAbstractOrSealed) return
+ if (wasmFileCodegenContext.handleVTableWithImport(symbol)) return
+
val vTableTypeReference = wasmFileCodegenContext.referenceVTableGcType(symbol)
val vTableRefGcType = WasmRefType(WasmHeapType.Type(vTableTypeReference))
@@ -365,16 +372,7 @@
val symbol = klass.symbol
val superType = klass.getSuperClass(irBuiltIns)?.symbol
- val fqnShouldBeEmitted = backendContext.configuration.languageVersionSettings.getFlag(allowFullyQualifiedNameInKClass)
- val qualifier =
- if (fqnShouldBeEmitted) {
- (klass.originalFqName ?: klass.kotlinFqName).parentOrNull()?.asString() ?: ""
- } else {
- ""
- }
- val simpleName = klass.name.asString()
- val (packageNameAddress, packageNamePoolId) = wasmFileCodegenContext.referenceStringLiteralAddressAndId(qualifier)
- val (simpleNameAddress, simpleNamePoolId) = wasmFileCodegenContext.referenceStringLiteralAddressAndId(simpleName)
+ if (wasmFileCodegenContext.handleRTTIWithImport(symbol, superType)) return
val location = SourceLocation.NoLocation("Create instance of rtti struct")
val initRttiGlobal = buildWasmExpression {
@@ -385,13 +383,33 @@
buildRefNull(WasmHeapType.Simple.None, location)
}
- buildConstI32Symbol(packageNameAddress, location)
- buildConstI32(qualifier.length, location)
- buildConstI32Symbol(packageNamePoolId, location)
+ if (useStringPool) {
+ val fqnShouldBeEmitted = backendContext.configuration.languageVersionSettings.getFlag(allowFullyQualifiedNameInKClass)
+ val qualifier =
+ if (fqnShouldBeEmitted) {
+ (klass.originalFqName ?: klass.kotlinFqName).parentOrNull()?.asString() ?: ""
+ } else {
+ ""
+ }
+ val simpleName = klass.name.asString()
+ val (packageNameAddress, packageNamePoolId) = wasmFileCodegenContext.referenceStringLiteralAddressAndId(qualifier)
+ val (simpleNameAddress, simpleNamePoolId) = wasmFileCodegenContext.referenceStringLiteralAddressAndId(simpleName)
- buildConstI32Symbol(simpleNameAddress, location)
- buildConstI32(simpleName.length, location)
- buildConstI32Symbol(simpleNamePoolId, location)
+ buildConstI32Symbol(packageNameAddress, location)
+ buildConstI32(qualifier.length, location)
+ buildConstI32Symbol(packageNamePoolId, location)
+
+ buildConstI32Symbol(simpleNameAddress, location)
+ buildConstI32(simpleName.length, location)
+ buildConstI32Symbol(simpleNamePoolId, location)
+ } else {
+ buildConstI32(-1, location)
+ buildConstI32(-1, location)
+ buildConstI32(-1, location)
+ buildConstI32(-1, location)
+ buildConstI32(-1, location)
+ buildConstI32(-1, location)
+ }
buildConstI64(wasmFileCodegenContext.referenceTypeId(symbol), location)
@@ -413,6 +431,8 @@
if (klass.isAbstractOrSealed) return
if (!klass.hasInterfaceSuperClass()) return
+ if (wasmFileCodegenContext.handleClassITableWithImport(klass.symbol)) return
+
val location = SourceLocation.NoLocation("Create instance of itable struct")
val initITableGlobal = buildWasmExpression {
@@ -535,7 +555,6 @@
override fun visitField(declaration: IrField) {
// Member fields are generated as part of struct type
if (!declaration.isStatic) return
-
val wasmType = wasmModuleTypeTransformer.transformType(declaration.type)
val initBody = mutableListOf<WasmInstr>()
@@ -549,7 +568,8 @@
wasmExpressionGenerator,
wasmFileCodegenContext,
backendContext,
- initValue.getSourceLocation(declaration.symbol, declaration.fileOrNull)
+ initValue.getSourceLocation(declaration.symbol, declaration.fileOrNull),
+ useStringPool,
)
} else {
val stubFunction = WasmFunction.Defined("static_fun_stub", WasmSymbol())
@@ -567,6 +587,8 @@
functionCodegenContext,
wasmModuleMetadataCache,
wasmModuleTypeTransformer,
+ useStringPool,
+ inlineUnitGetter,
)
bodyGenerator.generateExpression(initValue)
wasmFileCodegenContext.addFieldInitializer(
@@ -625,7 +647,8 @@
body: WasmExpressionBuilder,
context: WasmFileCodegenContext,
backendContext: WasmBackendContext,
- location: SourceLocation
+ location: SourceLocation,
+ useStringPool: Boolean,
) =
when (val kind = expression.kind) {
is IrConstKind.Null -> {
@@ -643,12 +666,28 @@
is IrConstKind.Double -> body.buildConstF64(expression.value as Double, location)
is IrConstKind.String -> {
val stringValue = expression.value as String
- val (literalAddress, literalPoolId) = context.referenceStringLiteralAddressAndId(stringValue)
+
body.commentGroupStart { "const string: \"$stringValue\"" }
- body.buildConstI32Symbol(literalPoolId, location)
- body.buildConstI32Symbol(literalAddress, location)
- body.buildConstI32(stringValue.length, location)
- body.buildCall(context.referenceFunction(backendContext.wasmSymbols.stringGetLiteral), location)
+
+ val (literalAddress, literalPoolId) = context.referenceStringLiteralAddressAndId(stringValue)
+ if (useStringPool) {
+ body.buildConstI32Symbol(literalPoolId, location)
+ body.buildConstI32Symbol(literalAddress, location)
+ body.buildConstI32(stringValue.length, location)
+ body.buildCall(context.referenceFunction(backendContext.wasmSymbols.stringGetLiteral), location)
+ } else {
+ body.buildConstI32Symbol(literalAddress, location)
+ body.buildConstI32(stringValue.length, location)
+
+ val createString = backendContext.wasmSymbols.createString
+ val wasmCharArrayType = createString.owner.parameters[0].type
+ val arrayGcType = WasmImmediate.GcType(
+ context.referenceGcType(wasmCharArrayType.getRuntimeClass(backendContext.irBuiltIns).symbol)
+ )
+ body.buildInstr(WasmOp.ARRAY_NEW_DATA, location, arrayGcType, WasmImmediate.DataIdx(0))
+ body.buildCall(context.referenceFunction(createString), location)
+ }
+
body.commentGroupEnd()
}
}
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/RecursiveTypesUtils.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/RecursiveTypesUtils.kt
index c573b9e..0cc7f2e 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/RecursiveTypesUtils.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/RecursiveTypesUtils.kt
@@ -10,7 +10,7 @@
import org.jetbrains.kotlin.utils.yieldIfNotNull
import org.jetbrains.kotlin.wasm.ir.*
-typealias RecursiveTypeGroup = List<WasmTypeDeclaration>
+typealias RecursiveTypeGroup = MutableList<WasmTypeDeclaration>
private fun WasmType.toTypeDeclaration(): WasmTypeDeclaration? {
val heapType = when (val type = this) {
@@ -54,17 +54,10 @@
}
-fun createRecursiveTypeGroups(types: Sequence<WasmTypeDeclaration>): List<RecursiveTypeGroup> {
+fun createRecursiveTypeGroups(types: Sequence<WasmTypeDeclaration>): MutableList<RecursiveTypeGroup> {
val componentFinder = StronglyConnectedComponents(::dependencyTypes)
types.forEach(componentFinder::visit)
-
- val components = componentFinder.findComponents()
-
- components.forEach { component ->
- component.sortBy(::wasmTypeDeclarationOrderKey)
- }
-
- return components
+ return componentFinder.findComponents()
}
private fun typeFingerprint(type: WasmType, currentHash: Hash128Bits, visited: MutableSet<WasmTypeDeclaration>): Hash128Bits {
@@ -129,31 +122,62 @@
WasmStructRef,
)
-internal fun encodeIndex(index: Int): List<WasmStructFieldDeclaration> {
+internal fun encodeIndex(index: UInt): List<WasmStructFieldDeclaration> {
var current = index
val result = mutableListOf<WasmStructFieldDeclaration>()
//i31 type is not used by kotlin/wasm, so mixin index would never clash with regular signature
result.add(WasmStructFieldDeclaration("", WasmI31Ref, false))
- while (current != 0) {
- result.add(WasmStructFieldDeclaration("", indexes[current % 10], false))
- current /= 10
+ while (current != 0U) {
+ result.add(WasmStructFieldDeclaration("", indexes[(current % 10U).toInt()], false))
+ current /= 10U
}
return result
}
-internal fun addMixInGroup(group: RecursiveTypeGroup, mixInIndexesForGroups: MutableMap<Hash128Bits, Int>): RecursiveTypeGroup {
- val firstDeclaration = group.firstOrNull() ?: return group
+internal fun canonicalSort(group: RecursiveTypeGroup, stableSort: Boolean) {
+ if (group.size == 1) return
+ if (stableSort) {
+ group.sortWith(WasmTypeDeclaratorByFingerprint())
+ }
+ group.sortBy(::wasmTypeDeclarationOrderKey)
+}
- val fingerprint = wasmDeclarationFingerprint(firstDeclaration, Hash128Bits(), visited = mutableSetOf())
+private class WasmTypeDeclaratorByFingerprint : Comparator<WasmTypeDeclaration> {
+ private val fingerprintCache = mutableMapOf<WasmTypeDeclaration, Hash128Bits>()
- val groupIndex = mixInIndexesForGroups[fingerprint]
- if (groupIndex != null) {
- val nextIndex = groupIndex + 1
- mixInIndexesForGroups[fingerprint] = nextIndex
- val mixIn = WasmStructDeclaration("mixin_$nextIndex", encodeIndex(nextIndex), null, true)
- return group + mixIn
- } else {
- mixInIndexesForGroups[fingerprint] = 0
- return group
+ private fun getFingerprint(type: WasmTypeDeclaration) = fingerprintCache.getOrPut(type) {
+ wasmDeclarationFingerprint(type, Hash128Bits(), visited = mutableSetOf())
+ }
+
+ private fun diff(x: ULong, y: ULong): Int {
+ if (x == y) return 0
+ return if (x > y) {
+ val diff = x - y
+ if (diff > Int.MAX_VALUE.toUInt()) {
+ Int.MAX_VALUE
+ } else {
+ (x - y).toInt()
+ }
+ } else {
+ val diff = y - x
+ if (diff > Int.MAX_VALUE.toUInt()) {
+ Int.MIN_VALUE
+ } else {
+ -diff.toInt()
+ }
+ }
+ }
+
+ override fun compare(
+ o1: WasmTypeDeclaration,
+ o2: WasmTypeDeclaration,
+ ): Int {
+ val o1Hash = getFingerprint(o1)
+ val o2Hash = getFingerprint(o2)
+ return if (o1Hash.highBytes == o2Hash.highBytes) {
+ diff(o1Hash.lowBytes, o2Hash.lowBytes)
+ } else {
+ diff(o1Hash.highBytes, o2Hash.highBytes)
+ }
}
}
\ No newline at end of file
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt
index 927edb9..9740ca4 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt
@@ -21,6 +21,7 @@
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation
+import kotlin.math.exp
class BuiltinIdSignatures(
val throwable: IdSignature?,
@@ -153,11 +154,16 @@
return definedFunctions to importedFunctions
}
- private fun createAndExportServiceFunctions(definedFunctions: MutableList<WasmFunction.Defined>, exports: MutableList<WasmExport<*>>) {
- val fieldInitializerFunction = createFieldInitializerFunction()
+ private fun createAndExportServiceFunctions(
+ definedFunctions: MutableList<WasmFunction.Defined>,
+ exports: MutableList<WasmExport<*>>,
+ initializeUnit: Boolean,
+ initializeStringPool: Boolean,
+ ) {
+ val fieldInitializerFunction = createFieldInitializerFunction(initializeStringPool)
definedFunctions.add(fieldInitializerFunction)
- val masterInitFunction = createAndExportMasterInitFunction(fieldInitializerFunction)
+ val masterInitFunction = createAndExportMasterInitFunction(fieldInitializerFunction, initializeUnit)
exports.add(WasmExport.Function("_initialize", masterInitFunction))
definedFunctions.add(masterInitFunction)
@@ -168,7 +174,7 @@
}
}
- fun linkWasmCompiledFragments(): WasmModule {
+ fun linkWasmCompiledFragments(moduleImportName: String?, initializeUnit: Boolean, exportThrowableTag: Boolean, initializeStringPool: Boolean): WasmModule {
// TODO: Implement optimal ir linkage KT-71040
bindUnboundSymbols()
val canonicalFunctionTypes = bindUnboundFunctionTypes()
@@ -184,16 +190,13 @@
val exports = mutableListOf<WasmExport<*>>()
wasmCompiledFileFragments.flatMapTo(exports) { it.exports }
- val memory = createAndExportMemory(exports)
+ val memories = createAndExportMemory(exports, moduleImportName)
+ val (importedMemories, definedMemories) = memories.partition { it.importPair != null }
- createAndExportServiceFunctions(definedFunctions, exports)
+ createAndExportServiceFunctions(definedFunctions, exports, initializeUnit, initializeStringPool)
- val throwableDeclaration = tryFindBuiltInType { it.throwable }
- ?: compilationException("kotlin.Throwable is not found in fragments", null)
-
- val tags = getTags(throwableDeclaration)
+ val tags = getTags(exports, moduleImportName, exportThrowableTag)
val (importedTags, definedTags) = tags.partition { it.importPair != null }
- val importsInOrder = importedFunctions + importedTags
val additionalTypes = mutableListOf<WasmTypeDeclaration>()
additionalTypes.add(parameterlessNoReturnFunctionType)
@@ -202,6 +205,13 @@
val syntheticTypes = mutableListOf<WasmTypeDeclaration>()
createAndBindSpecialITableTypes(syntheticTypes)
val globals = getGlobals(syntheticTypes)
+ val (importedGlobals, definedGlobals) = globals.partition { it.importPair != null }
+
+ val importsInOrder = mutableListOf<WasmNamedModuleField>()
+ importsInOrder.addAll(importedFunctions)
+ importsInOrder.addAll(importedTags)
+ importsInOrder.addAll(importedGlobals)
+ importsInOrder.addAll(importedMemories)
val recursiveTypeGroups = getTypes(syntheticTypes, canonicalFunctionTypes, additionalTypes)
@@ -209,17 +219,19 @@
recGroups = recursiveTypeGroups,
importsInOrder = importsInOrder,
importedFunctions = importedFunctions,
+ importedMemories = importedMemories,
definedFunctions = definedFunctions,
+ importedTags = importedTags.filterNot { it.importPair?.moduleName == "intrinsics" },
tables = emptyList(),
- memories = listOf(memory),
- globals = globals,
+ memories = definedMemories,
+ globals = definedGlobals,
+ importedGlobals = importedGlobals,
exports = exports,
startFunction = null, // Module is initialized via export call
elements = emptyList(),
data = data,
dataCount = true,
- tags = definedTags,
- importedTags = importedTags,
+ tags = definedTags
).apply { calculateIds() }
}
@@ -329,34 +341,43 @@
return syntheticTypes
}
- private fun getTags(throwableDeclaration: WasmTypeDeclaration): List<WasmTag> {
+ private fun getTags(exports: MutableList<WasmExport<*>>, moduleImportName: String?, exportThrowableTag: Boolean): List<WasmTag> {
+ if (generateTrapsInsteadOfExceptions) return emptyList()
+
+ val throwableDeclaration = tryFindBuiltInType { it.throwable }
+ ?: compilationException("kotlin.Throwable is not found in fragments", null)
val tagFuncType = WasmRefNullType(WasmHeapType.Type(WasmSymbol(throwableDeclaration)))
val throwableTagFuncType = WasmFunctionType(
parameterTypes = listOf(tagFuncType),
resultTypes = emptyList()
)
+
+ val throwableImport = moduleImportName?.let {
+ WasmImportDescriptor(moduleImportName, WasmSymbol("tag_throwable"))
+ }
+ val throwableTag = WasmTag(throwableTagFuncType, throwableImport)
+ if (exportThrowableTag) {
+ exports.add(WasmExport.Tag("tag_throwable", throwableTag))
+ }
+
+ wasmCompiledFileFragments.forEach {
+ it.throwableTagIndex?.bind(0)
+ }
+
+ if (!itsPossibleToCatchJsErrorSeparately) return listOf(throwableTag)
+
val jsExceptionTagFuncType = WasmFunctionType(
parameterTypes = listOf(WasmExternRef),
resultTypes = emptyList()
)
- val tags = listOfNotNull(
- runIf(!generateTrapsInsteadOfExceptions && itsPossibleToCatchJsErrorSeparately) {
- WasmTag(jsExceptionTagFuncType, WasmImportDescriptor("intrinsics", WasmSymbol("js_error_tag")))
- },
- runIf(!generateTrapsInsteadOfExceptions) { WasmTag(throwableTagFuncType) }
- )
- val throwableTagIndex = tags.indexOfFirst { it.type === throwableTagFuncType }
+ val jsErrorTagImport = WasmImportDescriptor("intrinsics", WasmSymbol("js_error_tag"))
+ val jsErrorTag = WasmTag(jsExceptionTagFuncType, jsErrorTagImport)
wasmCompiledFileFragments.forEach {
- it.throwableTagIndex?.bind(throwableTagIndex)
+ it.jsExceptionTagIndex?.bind(1)
}
- val jsExceptionTagIndex = tags.indexOfFirst { it.type === jsExceptionTagFuncType }
- wasmCompiledFileFragments.forEach {
- it.jsExceptionTagIndex?.bind(jsExceptionTagIndex)
- }
-
- return tags
+ return listOf(throwableTag, jsErrorTag)
}
private fun getTypes(
@@ -365,30 +386,42 @@
additionalTypes: List<WasmTypeDeclaration>,
): List<RecursiveTypeGroup> {
val gcTypes = wasmCompiledFileFragments.flatMap { it.gcTypes.elements }
+ val vTableGcTypes = wasmCompiledFileFragments.flatMap { it.vTableGcTypes.elements }
val recGroupTypes = sequence {
yieldAll(additionalRecGroupTypes)
yieldAll(gcTypes)
- yieldAll(wasmCompiledFileFragments.asSequence().flatMap { it.vTableGcTypes.elements })
+ yieldAll(vTableGcTypes)
yieldAll(canonicalFunctionTypes.values)
}
val recursiveGroups = createRecursiveTypeGroups(recGroupTypes)
- val mixInIndexesForGroups = mutableMapOf<Hash128Bits, Int>()
- val groupsWithMixIns = mutableListOf<RecursiveTypeGroup>()
+ recursiveGroups.forEach { group ->
+ if (group.singleOrNull() is WasmArrayDeclaration) {
+ return@forEach
+ }
- recursiveGroups.mapTo(groupsWithMixIns) { group ->
- if (group.any { it in gcTypes } && group.singleOrNull() !is WasmArrayDeclaration) {
- addMixInGroup(group, mixInIndexesForGroups)
- } else {
- group
+ val needMixIn = group.any { it in gcTypes }
+ val needStableSort = needMixIn || group.any { it in vTableGcTypes }
+
+ canonicalSort(group, needStableSort)
+
+ if (needMixIn) {
+ val firstIdSignature = group.firstOrNull { it is WasmStructDeclaration }?.let { firstStruct ->
+ wasmCompiledFileFragments.firstNotNullOfOrNull {
+ it.gcTypes.wasmToIr[firstStruct] ?: it.vTableGcTypes.wasmToIr[firstStruct]
+ }
+ }
+ if (firstIdSignature != null) {
+ val mixIn = WasmStructDeclaration("mixin_type", encodeIndex(firstIdSignature.toString().hashCode().toUInt()), null, true)
+ group.add(mixIn)
+ }
}
}
- additionalTypes.forEach { groupsWithMixIns.add(listOf(it)) }
-
- return groupsWithMixIns
+ additionalTypes.forEach { recursiveGroups.add(mutableListOf(it)) }
+ return recursiveGroups
}
private fun getGlobals(additionalTypes: MutableList<WasmTypeDeclaration>) = mutableListOf<WasmGlobal>().apply {
@@ -400,24 +433,26 @@
createRttiTypeAndProcessRttiGlobals(this, additionalTypes)
}
- private fun createAndExportMemory(exports: MutableList<WasmExport<*>>): WasmMemory {
+ private fun createAndExportMemory(exports: MutableList<WasmExport<*>>, moduleImportName: String?): List<WasmMemory> {
val memorySizeInPages = 0
- val memory = WasmMemory(WasmLimits(memorySizeInPages.toUInt(), null /* "unlimited" */))
+ val importPair = moduleImportName?.let { WasmImportDescriptor(it, WasmSymbol("memory")) }
+ val memory = WasmMemory(WasmLimits(memorySizeInPages.toUInt(), null/* "unlimited" */), importPair)
// Need to export the memory in order to pass complex objects to the host language.
// Export name "memory" is a WASI ABI convention.
val exportMemory = WasmExport.Memory("memory", memory)
exports.add(exportMemory)
- return memory
+ return listOf(memory)
}
- private fun createAndExportMasterInitFunction(fieldInitializerFunction: WasmFunction): WasmFunction.Defined {
- val unitGetInstance = tryFindBuiltInFunction { it.unitGetInstance }
- ?: compilationException("kotlin.Unit_getInstance is not file in fragments", null)
-
+ private fun createAndExportMasterInitFunction(fieldInitializerFunction: WasmFunction, initializeUnit: Boolean,): WasmFunction.Defined {
val masterInitFunction = WasmFunction.Defined("_initialize", WasmSymbol(parameterlessNoReturnFunctionType))
with(WasmExpressionBuilder(masterInitFunction.instructions)) {
- buildCall(WasmSymbol(unitGetInstance), serviceCodeLocation)
+ if (initializeUnit) {
+ val unitGetInstance = tryFindBuiltInFunction { it.unitGetInstance }
+ ?: compilationException("kotlin.Unit_getInstance is not file in fragments", null)
+ buildCall(WasmSymbol(unitGetInstance), serviceCodeLocation)
+ }
buildCall(WasmSymbol(fieldInitializerFunction), serviceCodeLocation)
wasmCompiledFileFragments.forEach { fragment ->
fragment.mainFunctionWrappers.forEach { signature ->
@@ -491,7 +526,7 @@
return startUnitTestsFunction
}
- private fun createFieldInitializerFunction(): WasmFunction.Defined {
+ private fun createFieldInitializerFunction(initializeStringPool: Boolean): WasmFunction.Defined {
val fieldInitializerFunction = WasmFunction.Defined("_fieldInitialize", WasmSymbol(parameterlessNoReturnFunctionType))
with(WasmExpressionBuilder(fieldInitializerFunction.instructions)) {
var stringPoolInitializer: Pair<FieldInitializer, WasmSymbol<WasmGlobal>>? = null
@@ -511,10 +546,12 @@
}
}
}
- stringPoolInitializer?.let {
- expression.add(0, WasmInstrWithoutLocation(WasmOp.GLOBAL_SET, listOf(WasmImmediate.GlobalIdx(it.second))))
- expression.addAll(0, it.first.instructions)
- } ?: compilationException("stringPool initializer not found!", type = null)
+ if (initializeStringPool) {
+ stringPoolInitializer?.let {
+ expression.add(0, WasmInstrWithoutLocation(WasmOp.GLOBAL_SET, listOf(WasmImmediate.GlobalIdx(it.second))))
+ expression.addAll(0, it.first.instructions)
+ } ?: compilationException("stringPool initializer not found!", type = null)
+ }
}
return fieldInitializerFunction
}
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmFileCodegenContextWithExport.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmFileCodegenContextWithExport.kt
new file mode 100644
index 0000000..8cee765
--- /dev/null
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmFileCodegenContextWithExport.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010-2025 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.backend.wasm.ir2wasm
+
+import org.jetbrains.kotlin.ir.declarations.IdSignatureRetriever
+import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithVisibility
+import org.jetbrains.kotlin.ir.overrides.isEffectivelyPrivate
+import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
+import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
+import org.jetbrains.kotlin.wasm.ir.WasmExport
+import org.jetbrains.kotlin.wasm.ir.WasmFunction
+import org.jetbrains.kotlin.wasm.ir.WasmGlobal
+
+class WasmFileCodegenContextWithExport(
+ wasmFileFragment: WasmCompiledFileFragment,
+ idSignatureRetriever: IdSignatureRetriever,
+) : WasmFileCodegenContext(wasmFileFragment, idSignatureRetriever) {
+ override fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) {
+ super.defineFunction(irFunction, wasmFunction)
+ val owner = irFunction.owner
+ if (owner.isEffectivelyPrivate()) return
+ val signature = idSignatureRetriever.declarationSignature(owner)
+ addExport(
+ WasmExport.Function(
+ field = wasmFunction,
+ name = "$FunctionImportPrefix$signature"
+ )
+ )
+ }
+
+ override fun defineGlobalVTable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
+ super.defineGlobalVTable(irClass, wasmGlobal)
+ exportDeclarationGlobal(irClass.owner, TypeGlobalImportPrefix.VTABLE, wasmGlobal)
+ }
+
+ override fun defineGlobalClassITable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
+ super.defineGlobalClassITable(irClass, wasmGlobal)
+ exportDeclarationGlobal(irClass.owner, TypeGlobalImportPrefix.ITABLE, wasmGlobal)
+ }
+
+ override fun defineRttiGlobal(global: WasmGlobal, irClass: IrClassSymbol, irSuperClass: IrClassSymbol?) {
+ super.defineRttiGlobal(global, irClass, irSuperClass)
+ exportDeclarationGlobal(irClass.owner, TypeGlobalImportPrefix.RTTI, global)
+ }
+
+ private fun exportDeclarationGlobal(declaration: IrDeclarationWithVisibility, prefix: TypeGlobalImportPrefix, global: WasmGlobal) {
+ if (declaration.isEffectivelyPrivate()) return
+ val signature = idSignatureRetriever.declarationSignature(declaration)
+ addExport(
+ WasmExport.Global(
+ name = "${prefix.prefix}$signature",
+ field = global
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmFileCodegenContextWithImport.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmFileCodegenContextWithImport.kt
new file mode 100644
index 0000000..9dd06ad
--- /dev/null
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmFileCodegenContextWithImport.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2010-2025 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.backend.wasm.ir2wasm
+
+/*
+ * Copyright 2010-2020 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.
+ */
+
+import org.jetbrains.kotlin.ir.declarations.IdSignatureRetriever
+import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
+import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
+import org.jetbrains.kotlin.ir.util.IdSignature
+import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
+import org.jetbrains.kotlin.wasm.ir.*
+
+fun getAllReferencedDeclarations(wasmCompiledFileFragment: WasmCompiledFileFragment): Set<IdSignature> {
+ val signatures = mutableSetOf<IdSignature>()
+ signatures.addAll(wasmCompiledFileFragment.functions.unbound.keys)
+ signatures.addAll(wasmCompiledFileFragment.globalFields.unbound.keys)
+ signatures.addAll(wasmCompiledFileFragment.globalVTables.unbound.keys)
+ signatures.addAll(wasmCompiledFileFragment.globalClassITables.unbound.keys)
+ wasmCompiledFileFragment.rttiElements?.let {
+ signatures.addAll(it.globalReferences.unbound.keys)
+ }
+ return signatures
+}
+
+class WasmFileCodegenContextWithImport(
+ wasmFileFragment: WasmCompiledFileFragment,
+ idSignatureRetriever: IdSignatureRetriever,
+ private val moduleName: String,
+ private val importDeclarations: Set<IdSignature>,
+) : WasmFileCodegenContext(wasmFileFragment, idSignatureRetriever) {
+ override fun handleFunctionWithImport(declaration: IrFunctionSymbol): Boolean {
+ val signature = idSignatureRetriever.declarationSignature(declaration.owner)
+ if (signature !in importDeclarations) return true
+ val functionTypeSymbol = referenceFunctionType(declaration)
+ defineFunction(
+ declaration,
+ WasmFunction.Imported(
+ name = declaration.owner.fqNameWhenAvailable.toString(),
+ type = functionTypeSymbol,
+ importPair = WasmImportDescriptor(moduleName, WasmSymbol("$FunctionImportPrefix$signature"))
+ )
+ )
+ return true
+ }
+
+ override fun handleVTableWithImport(declaration: IrClassSymbol): Boolean {
+ val signature = idSignatureRetriever.declarationSignature(declaration.owner)
+ if (signature !in importDeclarations) return true
+ val global = WasmGlobal(
+ name = "<classVTable>",
+ type = WasmRefType(WasmHeapType.Type(referenceVTableGcType(declaration))),
+ isMutable = false,
+ init = emptyList(),
+ importPair = WasmImportDescriptor(moduleName, WasmSymbol("${TypeGlobalImportPrefix.VTABLE.prefix}$signature"))
+ )
+ defineGlobalVTable(irClass = declaration, wasmGlobal = global)
+ return true
+ }
+
+ override fun handleClassITableWithImport(declaration: IrClassSymbol): Boolean {
+ val signature = idSignatureRetriever.declarationSignature(declaration.owner)
+ if (signature !in importDeclarations) return true
+ val global = WasmGlobal(
+ name = "<classITable>",
+ type = WasmRefType(WasmHeapType.Type(interfaceTableTypes.wasmAnyArrayType)),
+ isMutable = false,
+ init = emptyList(),
+ importPair = WasmImportDescriptor(moduleName, WasmSymbol("${TypeGlobalImportPrefix.ITABLE.prefix}$signature"))
+ )
+ defineGlobalClassITable(irClass = declaration, wasmGlobal = global)
+ return true
+ }
+
+ override fun handleRTTIWithImport(declaration: IrClassSymbol, superType: IrClassSymbol?): Boolean {
+ val signature = idSignatureRetriever.declarationSignature(declaration.owner)
+ if (signature !in importDeclarations) return true
+ val rttiGlobal = WasmGlobal(
+ name = "${declaration.owner.fqNameWhenAvailable}_rtti",
+ type = WasmRefType(WasmHeapType.Type(rttiType)),
+ isMutable = false,
+ init = emptyList(),
+ importPair = WasmImportDescriptor(moduleName, WasmSymbol("${TypeGlobalImportPrefix.RTTI.prefix}$signature"))
+ )
+ defineRttiGlobal(global = rttiGlobal, irClass = declaration, irSuperClass = superType)
+ return true
+ }
+}
\ No newline at end of file
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt
index 5eb3335..7591cca 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt
@@ -10,15 +10,32 @@
import org.jetbrains.kotlin.ir.declarations.IdSignatureRetriever
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
-import org.jetbrains.kotlin.ir.symbols.*
+import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
+import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
+import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
+import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.wasm.ir.*
-class WasmFileCodegenContext(
+
+enum class TypeGlobalImportPrefix(val prefix: String) {
+ VTABLE("vtable_"),
+ ITABLE("itable_"),
+ RTTI("rtti_"),
+}
+
+const val FunctionImportPrefix = "func_"
+
+open class WasmFileCodegenContext(
private val wasmFileFragment: WasmCompiledFileFragment,
- private val idSignatureRetriever: IdSignatureRetriever,
+ protected val idSignatureRetriever: IdSignatureRetriever,
) {
+ open fun handleFunctionWithImport(declaration: IrFunctionSymbol): Boolean = false
+ open fun handleVTableWithImport(declaration: IrClassSymbol): Boolean = false
+ open fun handleClassITableWithImport(declaration: IrClassSymbol): Boolean = false
+ open fun handleRTTIWithImport(declaration: IrClassSymbol, superType: IrClassSymbol?): Boolean = false
+
private fun IrSymbol.getReferenceKey(): IdSignature =
idSignatureRetriever.declarationSignature(this.owner as IrDeclaration)!!
@@ -38,7 +55,7 @@
private fun IrClassSymbol.getSignature(): IdSignature =
idSignatureRetriever.declarationSignature(this.owner)!!
- fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) {
+ open fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) {
wasmFileFragment.functions.define(irFunction.getReferenceKey(), wasmFunction)
}
@@ -46,11 +63,11 @@
wasmFileFragment.globalFields.define(irField.getReferenceKey(), wasmGlobal)
}
- fun defineGlobalVTable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
+ open fun defineGlobalVTable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
wasmFileFragment.globalVTables.define(irClass.getReferenceKey(), wasmGlobal)
}
- fun defineGlobalClassITable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
+ open fun defineGlobalClassITable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
wasmFileFragment.globalClassITables.define(irClass.getReferenceKey(), wasmGlobal)
}
@@ -179,7 +196,7 @@
val rttiType: WasmSymbol<WasmStructDeclaration> get() = rttiElements.rttiType
- fun defineRttiGlobal(global: WasmGlobal, irClass: IrClassSymbol, irSuperClass: IrClassSymbol?) {
+ open fun defineRttiGlobal(global: WasmGlobal, irClass: IrClassSymbol, irSuperClass: IrClassSymbol?) {
rttiElements.globals.add(
RttiGlobal(
global = global,
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleFragmentGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleFragmentGenerator.kt
index a022fb6..0d0b459 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleFragmentGenerator.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleFragmentGenerator.kt
@@ -10,6 +10,7 @@
import org.jetbrains.kotlin.ir.backend.js.utils.findUnitGetInstanceFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
+import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -19,12 +20,39 @@
private val idSignatureRetriever: IdSignatureRetriever,
private val allowIncompleteImplementations: Boolean,
private val skipCommentInstructions: Boolean,
+ private val useStringPool: Boolean,
) {
- fun generateModuleAsSingleFileFragment(irModuleFragment: IrModuleFragment): WasmCompiledFileFragment {
+ fun generateModuleAsSingleFileFragment(
+ irModuleFragment: IrModuleFragment,
+ ): WasmCompiledFileFragment {
val wasmFileFragment = WasmCompiledFileFragment(fragmentTag = null)
val wasmFileCodegenContext = WasmFileCodegenContext(wasmFileFragment, idSignatureRetriever)
- val wasmModuleTypeTransformer = WasmModuleTypeTransformer(backendContext, wasmFileCodegenContext)
+ generate(irModuleFragment, wasmFileCodegenContext)
+ return wasmFileFragment
+ }
+ fun generateModuleAsSingleFileFragmentWithIECImport(
+ irModuleFragment: IrModuleFragment,
+ moduleName: String,
+ importDeclarations: Set<IdSignature>,
+ ): WasmCompiledFileFragment {
+ val wasmFileFragment = WasmCompiledFileFragment(fragmentTag = null)
+ val wasmFileCodegenContext = WasmFileCodegenContextWithImport(wasmFileFragment, idSignatureRetriever, moduleName, importDeclarations)
+ generate(irModuleFragment, wasmFileCodegenContext)
+ return wasmFileFragment
+ }
+
+ fun generateModuleAsSingleFileFragmentWithIECExport(
+ irModuleFragment: IrModuleFragment,
+ ): WasmCompiledFileFragment {
+ val wasmFileFragment = WasmCompiledFileFragment(fragmentTag = null)
+ val wasmFileCodegenContext = WasmFileCodegenContextWithExport(wasmFileFragment, idSignatureRetriever)
+ generate(irModuleFragment, wasmFileCodegenContext)
+ return wasmFileFragment
+ }
+
+ private fun generate(irModuleFragment: IrModuleFragment, wasmFileCodegenContext: WasmFileCodegenContext) {
+ val wasmModuleTypeTransformer = WasmModuleTypeTransformer(backendContext, wasmFileCodegenContext)
for (irFile in irModuleFragment.files) {
compileIrFile(
irFile,
@@ -34,9 +62,9 @@
wasmFileCodegenContext,
wasmModuleTypeTransformer,
skipCommentInstructions,
+ useStringPool,
)
}
- return wasmFileFragment
}
}
@@ -48,6 +76,7 @@
allowIncompleteImplementations: Boolean,
fragmentTag: String?,
skipCommentInstructions: Boolean,
+ useStringPool: Boolean,
): WasmCompiledFileFragment {
val wasmFileFragment = WasmCompiledFileFragment(fragmentTag)
val wasmFileCodegenContext = WasmFileCodegenContext(wasmFileFragment, idSignatureRetriever)
@@ -60,6 +89,7 @@
wasmFileCodegenContext,
wasmModuleTypeTransformer,
skipCommentInstructions,
+ useStringPool,
)
return wasmFileFragment
}
@@ -72,6 +102,7 @@
wasmFileCodegenContext: WasmFileCodegenContext,
wasmModuleTypeTransformer: WasmModuleTypeTransformer,
skipCommentInstructions: Boolean,
+ useStringPool: Boolean,
) {
val generator = DeclarationGenerator(
backendContext,
@@ -80,6 +111,7 @@
wasmModuleMetadataCache,
allowIncompleteImplementations,
skipCommentInstructions,
+ useStringPool,
)
for (irDeclaration in irFile.declarations) {
irDeclaration.acceptVoid(generator)
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/StronglyConnectedComponents.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/StronglyConnectedComponents.kt
index 4126693..790fb3c 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/StronglyConnectedComponents.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/StronglyConnectedComponents.kt
@@ -23,7 +23,7 @@
}
}
- fun findComponents(): List<MutableList<T>> {
+ fun findComponents(): MutableList<MutableList<T>> {
visited.clear()
val result = mutableListOf<MutableList<T>>()
while (stack.isNotEmpty()) {
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/wasmCompiler.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/wasmCompiler.kt
index 9187b35..0bb077c 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/wasmCompiler.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/wasmCompiler.kt
@@ -69,6 +69,7 @@
exportedDeclarations: Set<FqName> = emptySet(),
generateTypeScriptFragment: Boolean,
propertyLazyInitialization: Boolean,
+ isIncremental: Boolean = false,
): LoweredIrWithExtraArtifacts {
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, irLinker) = irModuleInfo
@@ -78,7 +79,14 @@
}
val moduleDescriptor = moduleFragment.descriptor
- val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, propertyLazyInitialization, configuration)
+ val context = WasmBackendContext(
+ module = moduleDescriptor,
+ irBuiltIns = irBuiltIns,
+ symbolTable = symbolTable,
+ irModuleFragment = moduleFragment,
+ propertyLazyInitialization = propertyLazyInitialization,
+ configuration = configuration,
+ )
// Create stubs
ExternalDependenciesGenerator(symbolTable, listOf(irLinker)).generateUnboundSymbolsAsDependencies()
@@ -107,7 +115,7 @@
allModules,
context,
context.irFactory.stageController as WholeWorldStageController,
- isIncremental = false,
+ isIncremental = isIncremental,
)
performanceManager?.notifyIRLoweringFinished()
@@ -147,7 +155,11 @@
generateWat: Boolean = false,
generateSourceMaps: Boolean = false,
useDebuggerCustomFormatters: Boolean = false,
- generateDwarf: Boolean = false
+ generateDwarf: Boolean = false,
+ moduleImportName: String? = null,
+ initializeUnit: Boolean = true,
+ exportThrowableTag: Boolean = false,
+ initializeStringPool: Boolean = true,
): WasmCompilerResult {
val useJsTag = configuration.getBoolean(WasmConfigurationKeys.WASM_USE_JS_TAG)
val isWasmJsTarget = configuration.get(WasmConfigurationKeys.WASM_TARGET) != WasmTarget.WASI
@@ -158,7 +170,7 @@
isWasmJsTarget && useJsTag,
)
- val linkedModule = wasmCompiledModuleFragment.linkWasmCompiledFragments()
+ val linkedModule = wasmCompiledModuleFragment.linkWasmCompiledFragments(moduleImportName, initializeUnit, exportThrowableTag, initializeStringPool)
val dwarfGeneratorForBinary = runIf(generateDwarf) {
DwarfGenerator()
@@ -206,6 +218,9 @@
jsFuns.addAll(fragment.jsFuns.values)
jsModuleAndQualifierReferences.addAll(fragment.jsModuleAndQualifierReferences)
}
+ if (moduleImportName != null) {
+ jsModuleImports.add(moduleImportName)
+ }
jsUninstantiatedWrapper = generateAsyncJsWrapper(
jsModuleImports,
diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt
index bb94ab5..f45e502 100644
--- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt
+++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt
@@ -17,6 +17,7 @@
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
+import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageSupportForLinker
import org.jetbrains.kotlin.backend.common.linkage.partial.createPartialLinkageSupportForLinker
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker
import org.jetbrains.kotlin.backend.common.serialization.*
@@ -41,6 +42,7 @@
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.descriptors.IrDescriptorBasedFunctionFactory
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
+import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageConfig
import org.jetbrains.kotlin.ir.linkage.partial.partialLinkageConfig
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
@@ -214,6 +216,92 @@
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
+fun loadIrForMultimoduleMode(
+ depsDescriptors: ModulesStructure,
+ irFactory: IrFactory,
+ masterDeserializer: (JsIrLinker, ModuleDescriptor, KotlinLibrary) -> IrModuleFragment,
+ slaveDeserializer: (JsIrLinker, ModuleDescriptor, KotlinLibrary) -> IrModuleFragment,
+): IrModuleInfo {
+ val mainModule = depsDescriptors.mainModule
+ val configuration = depsDescriptors.compilerConfiguration
+ val allDependencies = depsDescriptors.allDependencies
+ val messageCollector = configuration.messageCollector
+
+ val signaturer = IdSignatureDescriptor(JsManglerDesc)
+ val symbolTable = SymbolTable(signaturer, irFactory)
+
+ check(mainModule is MainModule.Klib)
+
+ val mainPath = File(mainModule.libPath).canonicalPath
+ val mainModuleLibA = allDependencies.find { it.libraryFile.canonicalPath == mainPath }
+ ?: error("No module with ${mainModule.libPath} found")
+ val moduleDescriptor = depsDescriptors.getModuleDescriptor(mainModuleLibA)
+ val sortedDependencies = sortDependencies(depsDescriptors.moduleDependencies)
+ val friendModules = mapOf(mainModuleLibA.uniqueName to depsDescriptors.friendDependencies.map { it.uniqueName })
+
+ val typeTranslator = TypeTranslatorImpl(symbolTable, configuration.languageVersionSettings, moduleDescriptor)
+ val irBuiltIns = IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
+
+ val irLinker = JsIrLinker(
+ currentModule = null,
+ messageCollector = messageCollector,
+ builtIns = irBuiltIns,
+ symbolTable = symbolTable,
+ partialLinkageSupport = createPartialLinkageSupportForLinker(
+ partialLinkageConfig = PartialLinkageConfig.DEFAULT,
+ builtIns = irBuiltIns,
+ messageCollector = messageCollector
+ ),
+ icData = null,
+ friendModules = friendModules
+ )
+
+ val stdlibDependency = sortedDependencies.first()
+ val mainDependency = sortedDependencies.last()
+
+ val deserializedFragments = mutableListOf<IrModuleFragment>()
+
+ val deserializedStdlib = masterDeserializer(
+ irLinker,
+ depsDescriptors.getModuleDescriptor(stdlibDependency),
+ stdlibDependency
+ )
+ deserializedFragments.add(deserializedStdlib)
+
+ sortedDependencies.forEach { klib ->
+ if (klib != stdlibDependency && klib != mainDependency) {
+ deserializedFragments.add(
+ masterDeserializer(irLinker, depsDescriptors.getModuleDescriptor(klib), klib)
+ )
+ }
+ }
+
+ val deserializedMain = slaveDeserializer(irLinker, depsDescriptors.getModuleDescriptor(mainDependency), mainDependency)
+ deserializedFragments.add(deserializedMain)
+
+ irBuiltIns.functionFactory = IrDescriptorBasedFunctionFactory(
+ irBuiltIns = irBuiltIns,
+ symbolTable = symbolTable,
+ typeTranslator = typeTranslator,
+ getPackageFragment = FunctionTypeInterfacePackages().makePackageAccessor(deserializedStdlib),
+ referenceFunctionsWhenKFunctionAreReferenced = true
+ )
+
+ irLinker.init(null)
+ ExternalDependenciesGenerator(symbolTable, listOf(irLinker)).generateUnboundSymbolsAsDependencies()
+ irLinker.postProcess(inOrAfterLinkageStep = true)
+
+ return IrModuleInfo(
+ deserializedMain,
+ deserializedFragments,
+ irBuiltIns,
+ symbolTable,
+ irLinker,
+ emptyMap()
+ )
+}
+
+@OptIn(ObsoleteDescriptorBasedAPI::class)
fun getIrModuleInfoForKlib(
moduleDescriptor: ModuleDescriptor,
sortedDependencies: Collection<KotlinLibrary>,
diff --git a/libraries/stdlib/wasm/builtins/kotlin/String.kt b/libraries/stdlib/wasm/builtins/kotlin/String.kt
index ab94495..4b0d147 100644
--- a/libraries/stdlib/wasm/builtins/kotlin/String.kt
+++ b/libraries/stdlib/wasm/builtins/kotlin/String.kt
@@ -133,11 +133,14 @@
}
}
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun WasmCharArray.createString(): String =
+internal fun WasmCharArray.createString(): String =
String(null, this.len(), this)
internal fun stringLiteral(poolId: Int, startAddress: Int, length: Int): String {
+ if (poolId == -1) {
+ return ""
+ }
+
val cached = stringPool[poolId]
if (cached !== null) {
return cached
diff --git a/wasm/wasm.config/src/org/jetbrains/kotlin/platform/wasm/MultimoduleMode.kt b/wasm/wasm.config/src/org/jetbrains/kotlin/platform/wasm/MultimoduleMode.kt
new file mode 100644
index 0000000..a853f81
--- /dev/null
+++ b/wasm/wasm.config/src/org/jetbrains/kotlin/platform/wasm/MultimoduleMode.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2010-2025 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.platform.wasm
+
+enum class WasmMultimoduleMode(val alias: String) {
+ NONE("none"),
+ MASTER("master"),
+ SLAVE("slave");
+
+ companion object {
+ fun fromName(name: String): WasmMultimoduleMode? = WasmMultimoduleMode.entries.firstOrNull { it.alias == name }
+ }
+}
\ No newline at end of file
diff --git a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/config/WasmConfigurationKeys.java b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/config/WasmConfigurationKeys.java
index 3fa6777..0e4844e 100644
--- a/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/config/WasmConfigurationKeys.java
+++ b/wasm/wasm.frontend/src/org/jetbrains/kotlin/wasm/config/WasmConfigurationKeys.java
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.wasm.config;
import org.jetbrains.kotlin.config.CompilerConfigurationKey;
+import org.jetbrains.kotlin.platform.wasm.WasmMultimoduleMode;
import org.jetbrains.kotlin.platform.wasm.WasmTarget;
public class WasmConfigurationKeys {
@@ -41,4 +42,7 @@
public static final CompilerConfigurationKey<Boolean> WASM_FORCE_DEBUG_FRIENDLY_COMPILATION =
CompilerConfigurationKey.create("avoid optimizations that can break debugging.");
+
+ public static final CompilerConfigurationKey<WasmMultimoduleMode> WASM_MULTIMODULE_MODE =
+ CompilerConfigurationKey.create("set multimodule compilation mode.");
}
diff --git a/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/converters/WasmLoweringFacade.kt b/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/converters/WasmLoweringFacade.kt
index 27bbeea..14ebba1 100644
--- a/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/converters/WasmLoweringFacade.kt
+++ b/wasm/wasm.tests/test/org/jetbrains/kotlin/wasm/test/converters/WasmLoweringFacade.kt
@@ -89,6 +89,7 @@
moduleInfo.symbolTable.irFactory as IrFactoryImplForWasmIC,
allowIncompleteImplementations = false,
skipCommentInstructions = !generateWat,
+ useStringPool = true,
)
val wasmCompiledFileFragments = allModules.map { codeGenerator.generateModuleAsSingleFileFragment(it) }
@@ -116,6 +117,7 @@
moduleInfo.symbolTable.irFactory as IrFactoryImplForWasmIC,
allowIncompleteImplementations = true,
skipCommentInstructions = !generateWat,
+ useStringPool = true,
)
val wasmCompiledFileFragmentsDce = allModules.map { codeGeneratorDce.generateModuleAsSingleFileFragment(it) }