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 f8e6f1156a6..bcf3cd1b2b9 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 @@ -173,9 +173,6 @@ class WasmSymbols( val wasmClassId = getInternalFunction("wasmClassId") val wasmInterfaceId = getInternalFunction("wasmInterfaceId") - val getVirtualMethodId = getInternalFunction("getVirtualMethodId") - val getInterfaceImplId = getInternalFunction("getInterfaceImplId") - val isInterface = getInternalFunction("isInterface") val nullableEquals = getInternalFunction("nullableEquals") diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt index fcf73716f3e..fac4a0eb5d2 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt @@ -80,6 +80,7 @@ fun compileWasm( ): WasmCompilerResult { val compiledWasmModule = WasmCompiledModuleFragment(backendContext.irBuiltIns) val codeGenerator = WasmModuleFragmentGenerator(backendContext, compiledWasmModule, allowIncompleteImplementations = allowIncompleteImplementations) + allModules.forEach { codeGenerator.collectInterfaceTables(it) } allModules.forEach { codeGenerator.generateModule(it) } val linkedModule = compiledWasmModule.linkWasmCompiledFragments() 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 ac690f68ff1..ee7fdd2e9be 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 @@ -97,11 +97,8 @@ internal class WasmUsefulDeclarationProcessor( val isSuperCall = expression.superQualifierSymbol != null if (function is IrSimpleFunction && function.isOverridable && !isSuperCall) { val klass = function.parentAsClass - if (!klass.isInterface) { - context.wasmSymbols.getVirtualMethodId.owner.enqueue(data, "call on class receiver") - } else { + if (klass.isInterface) { klass.enqueue(data, "receiver class") - context.wasmSymbols.getInterfaceImplId.owner.enqueue(data, "call on interface receiver") } function.enqueue(data, "method call") } @@ -134,7 +131,7 @@ internal class WasmUsefulDeclarationProcessor( } private fun IrType.enqueueRuntimeClassOrAny(from: IrDeclaration, info: String): Unit = - (this.getRuntimeClass ?: context.wasmSymbols.any.owner).enqueue(from, info, isContagious = false) + this.getRuntimeClass(context.irBuiltIns).enqueue(from, info, isContagious = false) private fun IrType.enqueueType(from: IrDeclaration, info: String) { getInlinedValueTypeIfAny() 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 5acc0a6f0ac..393093b31a6 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 @@ -10,9 +10,7 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm import org.jetbrains.kotlin.backend.common.ir.returnType import org.jetbrains.kotlin.backend.wasm.WasmBackendContext import org.jetbrains.kotlin.backend.wasm.WasmSymbols -import org.jetbrains.kotlin.backend.wasm.utils.getWasmArrayAnnotation -import org.jetbrains.kotlin.backend.wasm.utils.getWasmOpAnnotation -import org.jetbrains.kotlin.backend.wasm.utils.hasWasmNoOpCastAnnotation +import org.jetbrains.kotlin.backend.wasm.utils.* import org.jetbrains.kotlin.backend.wasm.utils.isCanonical import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrElement @@ -29,7 +27,10 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.wasm.ir.* -class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorVoid { +class BodyGenerator( + val context: WasmFunctionCodegenContext, + private val hierarchyDisjointUnions: DisjointUnions +) : IrElementVisitorVoid { val body: WasmExpressionBuilder = context.bodyGen // Shortcuts @@ -117,7 +118,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV generateInstanceFieldAccess(field) } } else { - body.buildGetGlobal(context.referenceGlobal(field.symbol)) + body.buildGetGlobal(context.referenceGlobalField(field.symbol)) } } @@ -154,7 +155,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV ) } else { generateExpression(expression.value) - body.buildSetGlobal(context.referenceGlobal(expression.symbol)) + body.buildSetGlobal(context.referenceGlobalField(expression.symbol)) } body.buildGetUnit() @@ -203,6 +204,14 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV return } + //ClassITable and VTable load + body.buildGetGlobal(context.referenceGlobalVTable(klass.symbol)) + if (klass.hasInterfaceForClass()) { + body.buildGetGlobal(context.referenceGlobalClassITable(klass.symbol)) + } else { + body.buildRefNull(WasmHeapType.Simple.Data) + } + val wasmClassId = context.referenceClassId(klass.symbol) val irFields: List = klass.allFields(backendContext.irBuiltIns) @@ -214,7 +223,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV generateDefaultInitializerForType(context.transformType(field.type), body) } - generateClassRTT(klass.symbol) body.buildStructNew(wasmGcType) generateCall(expression) } @@ -236,16 +244,21 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV // Box intrinsic has an additional klass ID argument. // Processing it separately if (call.symbol == wasmSymbols.boxIntrinsic) { - val toType = call.getTypeArgument(0)!! - val klass = toType.getRuntimeClass!! - val structTypeName = context.referenceGcType(klass.symbol) - val klassId = context.referenceClassId(klass.symbol) + val klass = call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns) + val klassSymbol = klass.symbol - body.buildConstI32Symbol(klassId) + //ClassITable and VTable load + body.buildGetGlobal(context.referenceGlobalVTable(klassSymbol)) + if (klass.hasInterfaceForClass()) { + body.buildGetGlobal(context.referenceGlobalClassITable(klassSymbol)) + } else { + body.buildRefNull(WasmHeapType.Simple.Data) + } + + body.buildConstI32Symbol(context.referenceClassId(klassSymbol)) body.buildConstI32(0) // Any::_hashCode generateExpression(call.getValueArgument(0)!!) - generateClassRTT(klass.symbol) - body.buildStructNew(structTypeName) + body.buildStructNew(context.referenceGcType(klassSymbol)) return } @@ -296,20 +309,35 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV val vfSlot = classMetadata.virtualMethods.indexOfFirst { it.function == function } // Dispatch receiver should be simple and without side effects at this point // TODO: Verify - generateExpression(call.dispatchReceiver!!) - body.buildConstI32(vfSlot) - body.buildCall(context.referenceFunction(wasmSymbols.getVirtualMethodId)) - body.buildCallIndirect( - symbol = context.referenceFunctionType(function.symbol) - ) + val receiver = call.dispatchReceiver!! + generateExpression(receiver) + + if (!receiver.type.getRuntimeClass(irBuiltIns).isSubclassOf(klass)) { + body.buildRefCastStatic(toType = context.referenceGcType(klass.symbol)) + } + + body.buildStructGet(context.referenceGcType(klass.symbol), WasmSymbol(0)) + body.buildStructGet(context.referenceVTableGcType(klass.symbol), WasmSymbol(vfSlot)) + body.buildInstr(WasmOp.CALL_REF) } else { - generateExpression(call.dispatchReceiver!!) - body.buildConstI32Symbol(context.referenceInterfaceId(klass.symbol)) - body.buildCall(context.referenceFunction(wasmSymbols.getInterfaceImplId)) - body.buildCallIndirect( - tableIdx = WasmSymbolIntWrapper(context.referenceInterfaceTable(function.symbol)), - symbol = context.referenceFunctionType(function.symbol) - ) + val symbol = klass.symbol + if (symbol in hierarchyDisjointUnions) { + generateExpression(call.dispatchReceiver!!) + + body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(1)) + + val classITableReference = context.referenceClassITableGcType(symbol) + body.buildRefCastStatic(classITableReference) + body.buildStructGet(classITableReference, context.referenceClassITableInterfaceSlot(symbol)) + + val vfSlot = context.getInterfaceMetadata(symbol).methods + .indexOfFirst { it.function == function } + + body.buildStructGet(context.referenceVTableGcType(symbol), WasmSymbol(vfSlot)) + body.buildInstr(WasmOp.CALL_REF) + } else { + body.buildUnreachable() + } } } else { @@ -322,13 +350,14 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV body.buildGetUnit() } - private fun generateTypeRTT(type: IrType) { - val klass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass - generateClassRTT(klass) + private fun generateRefCast(type: IrType) { + body.buildRefCastStatic( + context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol) + ) } - private fun generateClassRTT(klass: IrClassSymbol) { - body.buildRttCanon(context.referenceGcType(klass)) + private fun generateTypeRTT(type: IrType) { + body.buildRttCanon(context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol)) } // Return true if generated. @@ -356,10 +385,35 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV body.buildConstI32Symbol(context.referenceInterfaceId(irInterface.symbol)) } + wasmSymbols.isInterface -> { + val irInterface = call.getTypeArgument(0)!!.getClass() + ?: error("No interface given for wasmInterfaceId intrinsic") + assert(irInterface.isInterface) + if (irInterface.symbol in hierarchyDisjointUnions) { + val classITable = context.referenceClassITableGcType(irInterface.symbol) + val parameterLocal = context.referenceLocal(SyntheticLocalType.IS_INTERFACE_PARAMETER) + body.buildSetLocal(parameterLocal) + body.buildBlock("isInterface", WasmI32) { outerLabel -> + body.buildBlock("isInterface", WasmRefNullType(WasmHeapType.Simple.Data)) { innerLabel -> + body.buildGetLocal(parameterLocal) + body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(1)) + body.buildBrInstr(WasmOp.BR_ON_CAST_STATIC_FAIL, innerLabel, classITable) + body.buildStructGet(classITable, context.referenceClassITableInterfaceSlot(irInterface.symbol)) + body.buildInstr(WasmOp.REF_IS_NULL) + body.buildInstr(WasmOp.I32_EQZ) + body.buildBr(outerLabel) + } + body.buildDrop() + body.buildConstI32(0) + } + } else { + body.buildDrop() + body.buildConstI32(0) + } + } + wasmSymbols.wasmRefCast -> { - val toType = call.getTypeArgument(0)!! - generateTypeRTT(toType) - body.buildRefCast() + generateRefCast(call.getTypeArgument(0)!!) } wasmSymbols.refTest -> { @@ -443,7 +497,8 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV override fun visitReturn(expression: IrReturn) { if (expression.returnTargetSymbol.owner.returnType(backendContext) == irBuiltIns.unitType && - expression.returnTargetSymbol.owner != unitGetInstance) { + expression.returnTargetSymbol.owner != unitGetInstance + ) { generateStatement(expression.value) } else { generateExpression(expression.value) @@ -499,24 +554,17 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV val label = loop.label - body.buildLoop(label) - val wasmLoop = body.numberOfNestedBlocks - - body.buildBlock("BREAK_$label") - val wasmBreakBlock = body.numberOfNestedBlocks - - body.buildBlock("CONTINUE_$label") - val wasmContinueBlock = body.numberOfNestedBlocks - - context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock) - context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmContinueBlock) - - loop.body?.let { generateStatement(it) } - body.buildEnd() - generateExpression(loop.condition) - body.buildBrIf(wasmLoop) - body.buildEnd() - body.buildEnd() + body.buildLoop(label) { wasmLoop -> + body.buildBlock("BREAK_$label") { wasmBreakBlock -> + body.buildBlock("CONTINUE_$label") { wasmContinueBlock -> + context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock) + context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmContinueBlock) + loop.body?.let { generateStatement(it) } + } + generateExpression(loop.condition) + body.buildBrIf(wasmLoop) + } + } body.buildGetUnit() } @@ -530,24 +578,20 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV val label = loop.label - body.buildLoop(label) - val wasmLoop = body.numberOfNestedBlocks + body.buildLoop(label) { wasmLoop -> + body.buildBlock("BREAK_$label") { wasmBreakBlock -> + context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock) + context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmLoop) - body.buildBlock("BREAK_$label") - val wasmBreakBlock = body.numberOfNestedBlocks - - context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock) - context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmLoop) - - generateExpression(loop.condition) - body.buildInstr(WasmOp.I32_EQZ) - body.buildBrIf(wasmBreakBlock) - loop.body?.let { - generateStatement(it) + generateExpression(loop.condition) + body.buildInstr(WasmOp.I32_EQZ) + body.buildBrIf(wasmBreakBlock) + loop.body?.let { + generateStatement(it) + } + body.buildBr(wasmLoop) + } } - body.buildBr(wasmLoop) - body.buildEnd() - body.buildEnd() body.buildGetUnit() } @@ -564,7 +608,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV } // Return true if function is recognized as intrinsic. - fun tryToGenerateWasmOpIntrinsicCall(call: IrFunctionAccessExpression, function: IrFunction): Boolean { + private fun tryToGenerateWasmOpIntrinsicCall(call: IrFunctionAccessExpression, function: IrFunction): Boolean { if (function.hasWasmNoOpCastAnnotation()) { return true } 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 7866babbe11..a1bc8a7725e 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 @@ -22,14 +22,18 @@ import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.name.parentOrNull import org.jetbrains.kotlin.wasm.ir.* -class DeclarationGenerator(val context: WasmModuleCodegenContext, private val allowIncompleteImplementations: Boolean) : IrElementVisitorVoid { +class DeclarationGenerator( + val context: WasmModuleCodegenContext, + private val allowIncompleteImplementations: Boolean, + private val hierarchyDisjointUnions: DisjointUnions, +) : IrElementVisitorVoid { // Shortcuts private val backendContext: WasmBackendContext = context.backendContext @@ -90,13 +94,8 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al ) context.defineFunctionType(declaration.symbol, wasmFunctionType) - if (declaration is IrSimpleFunction) { - if (declaration.modality == Modality.ABSTRACT) return - if (declaration.isOverridableOrOverrides) { - // Register function as virtual, meaning this function - // will be stored Wasm table and could be called indirectly. - context.registerVirtualFunction(declaration.symbol) - } + if (declaration is IrSimpleFunction && declaration.modality == Modality.ABSTRACT) { + return } assert(declaration == declaration.realOverrideTarget) { @@ -128,7 +127,7 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al } val exprGen = functionCodegenContext.bodyGen - val bodyBuilder = BodyGenerator(functionCodegenContext) + val bodyBuilder = BodyGenerator(functionCodegenContext, hierarchyDisjointUnions) require(declaration.body is IrBlockBody) { "Only IrBlockBody is supported" } declaration.body?.acceptVoid(bodyBuilder) @@ -167,12 +166,143 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al } } + private fun createDeclarationByInterface(iFace: IrClassSymbol) { + if (context.isAlreadyDefinedClassITableGcType(iFace)) return + if (iFace !in hierarchyDisjointUnions) return + + val iFacesUnion = hierarchyDisjointUnions[iFace] + + val fields = iFacesUnion.mapIndexed { index, unionIFace -> + context.defineClassITableInterfaceSlot(unionIFace, index) + WasmStructFieldDeclaration( + name = "${unionIFace.owner.fqNameWhenAvailable.toString()}.itable", + type = WasmRefNullType(WasmHeapType.Type(context.referenceVTableGcType(unionIFace))), + isMutable = false + ) + } + + val struct = WasmStructDeclaration( + name = "classITable", + fields = fields, + superType = null + ) + + iFacesUnion.forEach { + context.defineClassITableGcType(it, struct) + } + } + + private fun createVirtualTableStruct( + methods: List, + name: String, + superType: WasmSymbolReadOnly? = null, + ): WasmStructDeclaration { + val tableFields = methods.map { + WasmStructFieldDeclaration( + name = it.signature.name.asString(), + type = WasmRefNullType(WasmHeapType.Type(context.referenceFunctionType(it.function.symbol))), + isMutable = false + ) + } + + return WasmStructDeclaration( + name = name, + fields = tableFields, + superType = superType, + ) + } + + private fun createVTable(metadata: ClassMetadata) { + val klass = metadata.klass + val symbol = klass.symbol + val vtableName = "${klass.fqNameWhenAvailable}.vtable" + val vtableStruct = createVirtualTableStruct( + metadata.virtualMethods, + vtableName, + superType = metadata.superClass?.klass?.symbol?.let(context::referenceVTableGcType) + ) + context.defineVTableGcType(metadata.klass.symbol, vtableStruct) + + if (klass.isAbstractOrSealed) return + + val vTableTypeReference = context.referenceVTableGcType(symbol) + val vTableRefGcType = WasmRefType(WasmHeapType.Type(vTableTypeReference)) + + val initVTableGlobal = buildWasmExpression { + metadata.virtualMethods.forEachIndexed { i, method -> + if (method.function.modality != Modality.ABSTRACT) { + buildInstr(WasmOp.REF_FUNC, WasmImmediate.FuncIdx(context.referenceFunction(method.function.symbol))) + } else { + check(allowIncompleteImplementations) { + "Cannot find class implementation of method ${method.signature} in class ${klass.fqNameWhenAvailable}" + } + //This erased by DCE so abstract version appeared in non-abstract class + buildRefNull(vtableStruct.fields[i].type.getHeapType()) + } + } + buildStructNew(vTableTypeReference) + } + context.defineGlobalVTable( + irClass = symbol, + wasmGlobal = WasmGlobal(vtableName, vTableRefGcType, false, initVTableGlobal) + ) + } + + private fun createClassITable(metadata: ClassMetadata) { + val klass = metadata.klass + if (klass.isAbstractOrSealed) return + val supportedInterface = metadata.interfaces.firstOrNull()?.symbol ?: return + + createDeclarationByInterface(supportedInterface) + + val classInterfaceType = context.referenceClassITableGcType(supportedInterface) + val interfaceList = hierarchyDisjointUnions[supportedInterface] + + val initITableGlobal = buildWasmExpression { + for (iFace in interfaceList) { + val iFaceVTableGcType = context.referenceVTableGcType(iFace) + val iFaceVTableGcNullHeapType = WasmHeapType.Type(iFaceVTableGcType) + + if (!metadata.interfaces.contains(iFace.owner)) { + buildRefNull(iFaceVTableGcNullHeapType) + continue + } + + for (method in context.getInterfaceMetadata(iFace).methods) { + val classMethod: VirtualMethodMetadata? = metadata.virtualMethods + .find { it.signature == method.signature && it.function.modality != Modality.ABSTRACT } // TODO: Use map + + if (classMethod == null && !allowIncompleteImplementations) { + error("Cannot find interface implementation of method ${method.signature} in class ${klass.fqNameWhenAvailable}") + } + + if (classMethod != null) { + val functionTypeReference = context.referenceFunction(classMethod.function.symbol) + buildInstr(WasmOp.REF_FUNC, WasmImmediate.FuncIdx(functionTypeReference)) + } else { + //This erased by DCE so abstract version appeared in non-abstract class + buildRefNull(WasmHeapType.Type(context.referenceFunctionType(method.function.symbol))) + } + } + buildStructNew(iFaceVTableGcType) + } + buildStructNew(classInterfaceType) + } + + val wasmClassIFaceGlobal = WasmGlobal( + name = "${klass.fqNameWhenAvailable.toString()}.classITable", + type = WasmRefType(WasmHeapType.Type(classInterfaceType)), + isMutable = false, + init = initITableGlobal + ) + context.defineGlobalClassITable(klass.symbol, wasmClassIFaceGlobal) + } + override fun visitClass(declaration: IrClass) { if (declaration.isAnnotationClass) return if (declaration.isExternal) return val symbol = declaration.symbol - // Handle arrays declaration.getWasmArrayAnnotation()?.let { wasmArrayAnnotation -> val nameStr = declaration.fqNameWhenAvailable.toString() @@ -189,60 +319,43 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al return } + val nameStr = declaration.fqNameWhenAvailable.toString() + if (declaration.isInterface) { - val metadata = InterfaceMetadata(declaration, irBuiltIns) - for (method in metadata.methods) { - val methodSymbol = method.function.symbol - val table = WasmTable( - elementType = WasmRefNullType(WasmHeapType.Type(context.referenceFunctionType(methodSymbol))) + if (symbol in hierarchyDisjointUnions) { + val vtableStruct = createVirtualTableStruct( + methods = context.getInterfaceMetadata(symbol).methods, + name = "$nameStr.itable" ) - context.defineInterfaceMethodTable(methodSymbol, table) + context.defineVTableGcType(symbol, vtableStruct) } - context.registerInterface(symbol) } else { - val nameStr = declaration.fqNameWhenAvailable.toString() val metadata = context.getClassMetadata(symbol) + + createVTable(metadata) + createClassITable(metadata) + + val vtableRefGcType = WasmRefType(WasmHeapType.Type(context.referenceVTableGcType(symbol))) + val classITableRefGcType = WasmRefNullType(WasmHeapType.Simple.Data) + val fields = mutableListOf() + fields.add(WasmStructFieldDeclaration("vtable", vtableRefGcType, false)) + fields.add(WasmStructFieldDeclaration("itable", classITableRefGcType, false)) + declaration.allFields(irBuiltIns).mapTo(fields) { + WasmStructFieldDeclaration( + name = it.name.toString(), + type = context.transformFieldType(it.type), + isMutable = true + ) + } + val superClass = metadata.superClass val structType = WasmStructDeclaration( name = nameStr, - fields = declaration.allFields(irBuiltIns).map { - WasmStructFieldDeclaration( - name = it.name.toString(), - type = context.transformFieldType(it.type), - isMutable = true - ) - }, - superClass?.let { context.referenceGcType(superClass.klass.symbol) } + fields = fields, + superType = superClass?.let { context.referenceGcType(superClass.klass.symbol) } ) - context.defineGcType(symbol, structType) - context.registerClass(symbol) context.generateTypeInfo(symbol, binaryDataStruct(metadata)) - - // New type info model - if (declaration.modality != Modality.ABSTRACT) { - context.generateInterfaceTable(symbol, interfaceTable(metadata)) - for (i in metadata.interfaces) { - val interfaceImplementation = InterfaceImplementation(i.symbol, declaration.symbol) - // TODO: Cache it - val interfaceMetadata = InterfaceMetadata(i, irBuiltIns) - val table = interfaceMetadata.methods.associate { method -> - val classMethod: VirtualMethodMetadata? = metadata.virtualMethods - .find { it.signature == method.signature && it.function.modality != Modality.ABSTRACT } // TODO: Use map - - if (classMethod == null && !allowIncompleteImplementations) { - error("Cannot find class implementation of method ${method.signature} in class ${declaration.fqNameWhenAvailable}") - } - val matchedMethod = classMethod?.let { context.referenceFunction(it.function.symbol) } - method.function.symbol as IrFunctionSymbol to matchedMethod - } - - context.registerInterfaceImplementationMethod( - interfaceImplementation, - table - ) - } - } } for (member in declaration.declarations) { @@ -251,15 +364,13 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al } private fun binaryDataStruct(classMetadata: ClassMetadata): ConstantDataStruct { - val invalidIndex = -1 - val fqnShouldBeEmitted = context.backendContext.configuration.languageVersionSettings.getFlag(allowFullyQualifiedNameInKClass) //TODO("FqName for inner classes could be invalid due to topping it out from outer class") val packageName = if (fqnShouldBeEmitted) classMetadata.klass.kotlinFqName.parentOrNull()?.asString() ?: "" else "" val simpleName = classMetadata.klass.kotlinFqName.shortName().asString() val typeInfo = ConstantDataStruct( - "TypeInfo", - listOf( + name = "TypeInfo", + elements = listOf( ConstantDataIntField("TypePackageNameLength", packageName.length), ConstantDataIntField("TypePackageNamePtr", context.referenceStringLiteral(packageName)), ConstantDataIntField("TypeNameLength", simpleName.length), @@ -272,40 +383,17 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al ConstantDataIntField("SuperTypeId", context.referenceClassId(it.symbol)) } ?: ConstantDataIntField("SuperTypeId", -1) - val vtableSizeField = ConstantDataIntField( - "V-table length", - classMetadata.virtualMethods.size - ) - - val vtableArray = ConstantDataIntArray( - "V-table", - classMetadata.virtualMethods.map { - if (it.function.modality == Modality.ABSTRACT) { - WasmSymbol(invalidIndex) - } else { - context.referenceVirtualFunctionId(it.function.symbol) - } - } - ) - - val interfaceTablePtr = ConstantDataIntField( - "interfaceTablePtr", - context.referenceInterfaceTableAddress(classMetadata.klass.symbol) - ) + val typeInfoContent = mutableListOf(typeInfo, superTypeId) + if (!classMetadata.klass.isAbstractOrSealed) { + typeInfoContent.add(interfaceTable(classMetadata)) + } return ConstantDataStruct( - "Class TypeInfo: ${classMetadata.klass.fqNameWhenAvailable} ", - listOf( - typeInfo, - superTypeId, - interfaceTablePtr, - vtableSizeField, - vtableArray, - ) + name = "Class TypeInfo: ${classMetadata.klass.fqNameWhenAvailable} ", + elements = typeInfoContent ) } - private fun interfaceTable(classMetadata: ClassMetadata): ConstantDataStruct { val interfaces = classMetadata.interfaces val size = ConstantDataIntField("size", interfaces.size) @@ -313,20 +401,10 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al "interfaceIds", interfaces.map { context.referenceInterfaceId(it.symbol) }, ) - val interfaceImplementationIds = ConstantDataIntArray( - "interfaceImplementationId", - interfaces.map { - context.referenceInterfaceImplementationId(InterfaceImplementation(it.symbol, classMetadata.klass.symbol)) - }, - ) return ConstantDataStruct( - "Class interface table: ${classMetadata.klass.fqNameWhenAvailable} ", - listOf( - size, - interfaceIds, - interfaceImplementationIds, - ) + name = "Class interface table: ${classMetadata.klass.fqNameWhenAvailable} ", + elements = listOf(size, interfaceIds) ) } @@ -357,7 +435,7 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al init = initBody ) - context.defineGlobal(declaration.symbol, global) + context.defineGlobalField(declaration.symbol, global) } } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt index b3bb08ffdd0..e7fecc2d104 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt @@ -44,8 +44,8 @@ class WasmTypeTransformer( toWasmValueType() } - fun IrType.toWasmGcRefType(): WasmType = - WasmRefNullType(WasmHeapType.Type(context.referenceGcType(getRuntimeClass?.symbol ?: builtIns.anyClass))) + private fun IrType.toWasmGcRefType(): WasmType = + WasmRefNullType(WasmHeapType.Type(context.referenceGcType(getRuntimeClass(context.backendContext.irBuiltIns).symbol))) fun IrType.toBoxedInlineClassType(): WasmType = toWasmGcRefType() @@ -122,8 +122,5 @@ fun isBuiltInWasmRefType(type: IrType): Boolean { fun isExternalType(type: IrType): Boolean = type.erasedUpperBound?.isExternal ?: false -val IrType.getRuntimeClass: IrClass? - get() = erasedUpperBound.let { - if (it?.isInterface == true) null - else it - } +fun IrType.getRuntimeClass(irBuiltIns: IrBuiltIns): IrClass = + erasedUpperBound?.takeIf { !it.isInterface } ?: irBuiltIns.anyClass.owner \ No newline at end of file diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt index 97e78c593be..1c772c862cd 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt @@ -6,13 +6,9 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm import org.jetbrains.kotlin.backend.wasm.WasmBackendContext -import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.IrValueParameter -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.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.wasm.ir.* @@ -22,17 +18,20 @@ interface WasmBaseCodegenContext { val scratchMemAddr: WasmSymbol fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol - fun referenceGlobal(irField: IrFieldSymbol): WasmSymbol + fun referenceGlobalField(irField: IrFieldSymbol): WasmSymbol + fun referenceGlobalVTable(irClass: IrClassSymbol): WasmSymbol + fun referenceGlobalClassITable(irClass: IrClassSymbol): WasmSymbol fun referenceGcType(irClass: IrClassSymbol): WasmSymbol + fun referenceVTableGcType(irClass: IrClassSymbol): WasmSymbol + fun referenceClassITableGcType(irClass: IrClassSymbol): WasmSymbol + fun defineClassITableGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) + fun isAlreadyDefinedClassITableGcType(irClass: IrClassSymbol): Boolean + fun referenceClassITableInterfaceSlot(irClass: IrClassSymbol): WasmSymbol + fun defineClassITableInterfaceSlot(irClass: IrClassSymbol, slot: Int) fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol fun referenceClassId(irClass: IrClassSymbol): WasmSymbol fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol - fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol - - fun referenceSignatureId(signature: WasmSignature): WasmSymbol - - fun referenceInterfaceTable(irFunction: IrFunctionSymbol): WasmSymbol fun referenceStringLiteral(string: String): WasmSymbol @@ -47,4 +46,5 @@ interface WasmBaseCodegenContext { fun getStructFieldRef(field: IrField): WasmSymbol fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata + fun getInterfaceMetadata(irClass: IrClassSymbol): InterfaceMetadata } \ 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 c7b48e45a77..4928bea6eba 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 @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm -import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment @@ -17,24 +16,30 @@ import org.jetbrains.kotlin.wasm.ir.* class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { val functions = ReferencableAndDefinable() - val globals = + val globalFields = ReferencableAndDefinable() + val globalVTables = + ReferencableAndDefinable() + val globalClassITables = + ReferencableAndDefinable() val functionTypes = ReferencableAndDefinable() val gcTypes = ReferencableAndDefinable() + val vTableGcTypes = + ReferencableAndDefinable() + val classITableGcType = + ReferencableAndDefinable() + val classITableInterfaceSlot = + ReferencableAndDefinable() val classIds = ReferencableElements() val interfaceId = ReferencableElements() - val virtualFunctionId = - ReferencableElements() - val signatureId = - ReferencableElements() val stringLiteralId = ReferencableElements() - val tagFuncType = WasmFunctionType( + private val tagFuncType = WasmFunctionType( listOf( WasmRefNullType(WasmHeapType.Type(gcTypes.reference(irBuiltIns.throwableClass))) ), @@ -42,34 +47,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { ) val tag = WasmTag(tagFuncType) - - val classes = mutableListOf() - val interfaces = mutableListOf() - val virtualFunctions = mutableListOf() - val signatures = LinkedHashSet() - - val typeInfo = - ReferencableAndDefinable() - - // Wasm table for each method of each interface. - val interfaceMethodTables = - ReferencableAndDefinable() - - // Defined class interface tables - val definedClassITableData = - ReferencableAndDefinable() - - // Address of class interface table in linear memory - val referencedClassITableAddresses = - ReferencableElements() - - // Sequential number of an implementation (class, object, etc.) for a particular interface - // Used as index in table for interface method dispatch - val referencedInterfaceImplementationId = - ReferencableElements() - - val interfaceImplementationsMethods = - LinkedHashMap?>>() + val typeInfo = ReferencableAndDefinable() val exports = mutableListOf>() @@ -82,7 +60,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { val initFunctions = mutableListOf() val scratchMemAddr = WasmSymbol() - val scratchMemSizeInBytes = 65_536 + private val scratchMemSizeInBytes = 65_536 open class ReferencableElements { val unbound = mutableMapOf>() @@ -117,9 +95,13 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { @OptIn(ExperimentalUnsignedTypes::class) fun linkWasmCompiledFragments(): WasmModule { bind(functions.unbound, functions.defined) - bind(globals.unbound, globals.defined) - + bind(globalFields.unbound, globalFields.defined) + bind(globalVTables.unbound, globalVTables.defined) bind(gcTypes.unbound, gcTypes.defined) + bind(vTableGcTypes.unbound, vTableGcTypes.defined) + bind(classITableGcType.unbound, classITableGcType.defined) + bind(classITableInterfaceSlot.unbound, classITableInterfaceSlot.defined) + bind(globalClassITables.unbound, globalClassITables.defined) // Associate function types to a single canonical function type val canonicalFunctionTypes = @@ -139,13 +121,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { currentDataSectionAddress += typeInfoElement.sizeInBytes } - val interfaceTableAddresses = mutableMapOf() - for (typeInfoElement in definedClassITableData.elements) { - val ir = definedClassITableData.wasmToIr.getValue(typeInfoElement) - interfaceTableAddresses[ir] = currentDataSectionAddress - currentDataSectionAddress += typeInfoElement.sizeInBytes - } - val stringDataSectionStart = currentDataSectionAddress val stringDataSectionBytes = mutableListOf() val stringAddrs = mutableMapOf() @@ -163,107 +138,12 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { currentDataSectionAddress += scratchMemSizeInBytes bind(classIds.unbound, klassIds) - bind(referencedClassITableAddresses.unbound, interfaceTableAddresses) bind(stringLiteralId.unbound, stringAddrs) - bindIndices(virtualFunctionId.unbound, virtualFunctions) - bindIndices(signatureId.unbound, signatures.toList()) - bindIndices(interfaceId.unbound, interfaces) - - val interfaceImplementationIds = mutableMapOf() - val numberOfInterfaceImpls = mutableMapOf() - for (interfaceImplementation in interfaceImplementationsMethods.keys) { - val prev = numberOfInterfaceImpls.getOrPut(interfaceImplementation.irInterface) { 0 } - interfaceImplementationIds[interfaceImplementation] = prev - numberOfInterfaceImpls[interfaceImplementation.irInterface] = prev + 1 - } - - bind(referencedInterfaceImplementationId.unbound, interfaceImplementationIds) - bind(interfaceMethodTables.unbound, interfaceMethodTables.defined) + interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) } val data = typeInfo.buildData(address = { klassIds.getValue(it) }) + - definedClassITableData.buildData(address = { interfaceTableAddresses.getValue(it) }) + WasmData(WasmDataMode.Active(0, stringDataSectionStart), stringDataSectionBytes.toByteArray()) - val logTypeInfo = false - if (logTypeInfo) { - println("Signatures: ") - for ((index, signature: WasmSignature) in signatures.withIndex()) { - println(" -- $index $signature") - } - - println("Interfaces: ") - for ((index, iface: IrClassSymbol) in interfaces.withIndex()) { - println(" -- $index ${iface.owner.fqNameWhenAvailable}") - } - - println("Interfaces implementations: ") - for ((interfaceImpl, index: Int) in interfaceImplementationIds) { - println( - " -- $index" + - " Interface: ${interfaceImpl.irInterface.owner.fqNameWhenAvailable}" + - " Class: ${interfaceImpl.irClass.owner.fqNameWhenAvailable}" - ) - } - - - println("Virtual functions: ") - for ((index, vf: IrSimpleFunctionSymbol) in virtualFunctions.withIndex()) { - println(" -- $index ${vf.owner.fqNameWhenAvailable}") - } - - println( - ConstantDataStruct("typeInfo", typeInfo.elements).dump("", 0) - ) - } - - val table = WasmTable( - limits = WasmLimits(virtualFunctions.size.toUInt(), virtualFunctions.size.toUInt()), - elementType = WasmFuncRef, - ) - - val offsetExpr = mutableListOf() - WasmIrExpressionBuilder(offsetExpr).buildConstI32(0) - - val elements = WasmElement( - WasmFuncRef, - values = virtualFunctions.map { - WasmTable.Value.Function(functions.defined.getValue(it)) - }, - WasmElement.Mode.Active(table, offsetExpr) - ) - - val interfaceTableElementsLists = interfaceMethodTables.defined.keys.associateWith { - mutableMapOf?>() - } - - for ((ii: InterfaceImplementation, implId: Int) in interfaceImplementationIds) { - for ((interfaceFunction: IrFunctionSymbol, wasmFunction: WasmSymbol?) in interfaceImplementationsMethods[ii]!!) { - interfaceTableElementsLists[interfaceFunction]!![implId] = wasmFunction - } - } - - val interfaceTableElements = interfaceTableElementsLists.map { (interfaceFunction, methods) -> - val methodTable = interfaceMethodTables.defined[interfaceFunction]!! - val type = methodTable.elementType - val functions = MutableList(methods.size) { idx -> - val wasmFunc = methods[idx] - val expression = buildWasmExpression { - if (wasmFunc != null) { - buildInstr(WasmOp.REF_FUNC, WasmImmediate.FuncIdx(wasmFunc)) - } else { - //DCE could remove implementation from class, so we should to put a stub into method implementations table - buildRefNull(type.getHeapType()) - } - } - WasmTable.Value.Expression(expression) - } - WasmElement( - type, - values = functions, - WasmElement.Mode.Active(methodTable, offsetExpr) - ) - } - val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList()) val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType)) with(WasmIrExpressionBuilder(masterInitFunction.instructions)) { @@ -273,11 +153,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { } exports += WasmExport.Function("__init", masterInitFunction) - interfaceMethodTables.defined.forEach { (function, table) -> - val size = interfaceTableElementsLists[function]!!.size.toUInt() - table.limits = WasmLimits(size, size) - } - val typeInfoSize = currentDataSectionAddress val memorySizeInPages = (typeInfoSize / 65_536) + 1 val memory = WasmMemory(WasmLimits(memorySizeInPages.toUInt(), memorySizeInPages.toUInt())) @@ -297,21 +172,30 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { } } - val sortedGcTypes = gcTypes.elements.sortedBy(::wasmTypeDeclarationOrderKey) + val typeDeclarations = mutableListOf() + typeDeclarations.addAll(vTableGcTypes.elements) + typeDeclarations.addAll(gcTypes.elements) + typeDeclarations.addAll(classITableGcType.elements.distinct()) + typeDeclarations.sortBy(::wasmTypeDeclarationOrderKey) + + val globals = mutableListOf() + globals.addAll(globalFields.elements) + globals.addAll(globalVTables.elements) + globals.addAll(globalClassITables.elements.distinct()) val module = WasmModule( functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType, - gcTypes = sortedGcTypes, + gcTypes = typeDeclarations, gcTypesInRecursiveGroup = true, importsInOrder = importedFunctions, importedFunctions = importedFunctions, definedFunctions = functions.elements.filterIsInstance() + masterInitFunction, - tables = listOf(table) + interfaceMethodTables.elements, + tables = emptyList(), memories = listOf(memory), - globals = globals.elements, + globals = globals, exports = exports, startFunction = null, // Module is initialized via export call - elements = listOf(elements) + interfaceTableElements, + elements = emptyList(), data = data, tags = listOf(tag) ) @@ -338,18 +222,6 @@ private fun irSymbolDebugDump(symbol: Any?): String = else -> symbol.toString() } -fun bindIndices( - unbound: Map>, - ordered: List -) { - unbound.forEach { (irSymbol, wasmSymbol) -> - val index = ordered.indexOf(irSymbol) - if (index == -1) - error("Can't link symbol with indices ${irSymbolDebugDump(irSymbol)}") - wasmSymbol.bind(index) - } -} - inline fun WasmCompiledModuleFragment.ReferencableAndDefinable.buildData(address: (IrClassSymbol) -> Int): List { return elements.map { val id = address(wasmToIr.getValue(it)) @@ -359,11 +231,6 @@ inline fun WasmCompiledModuleFragment.ReferencableAndDefinable() + private val wasmSyntheticLocals = LinkedHashMap() private val loopLevels = LinkedHashMap, Int>() override fun defineLocal(irValueDeclaration: IrValueSymbol) { @@ -52,6 +53,17 @@ class WasmFunctionCodegenContextImpl( return wasmFunction.locals[index] } + override fun referenceLocal(type: SyntheticLocalType): WasmLocal = wasmSyntheticLocals.getOrPut(type) { + WasmLocal( + wasmFunction.locals.size, + type.name, + WasmRefNullType(WasmHeapType.Type(referenceGcType(backendContext.irBuiltIns.anyClass))), + isParameter = false + ).also { + wasmFunction.locals += it + } + } + override fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int) { val loopKey = Pair(irLoop, labelType) assert(loopKey !in loopLevels) { "Redefinition of loop" } 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 a3803c3f619..967e6c1ba99 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 @@ -5,10 +5,7 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm -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.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.wasm.ir.* /** @@ -16,27 +13,16 @@ import org.jetbrains.kotlin.wasm.ir.* */ interface WasmModuleCodegenContext : WasmBaseCodegenContext { fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) - fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) + fun defineGlobalField(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) + fun defineGlobalVTable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) + fun defineGlobalClassITable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) fun defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) + fun defineVTableGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) - fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable) fun addJsFun(importName: String, jsCode: String) fun registerInitFunction(wasmFunction: WasmFunction, priority: String) fun addExport(wasmExport: WasmExport<*>) - fun registerVirtualFunction(irFunction: IrSimpleFunctionSymbol) - fun registerInterface(irInterface: IrClassSymbol) - fun registerClass(irClass: IrClassSymbol) - fun generateTypeInfo(irClass: IrClassSymbol, typeInfo: ConstantDataElement) - fun generateInterfaceTable(irClass: IrClassSymbol, table: ConstantDataElement) - - fun registerInterfaceImplementationMethod( - interfaceImplementation: InterfaceImplementation, - table: Map?>, - ) - - fun referenceInterfaceImplementationId(interfaceImplementation: InterfaceImplementation): WasmSymbol - fun referenceInterfaceTableAddress(irClass: IrClassSymbol): WasmSymbol } \ No newline at end of file diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt index 7c32ae92140..0fc696c0980 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt @@ -6,19 +6,12 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm import org.jetbrains.kotlin.backend.wasm.WasmBackendContext -import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature -import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.IrValueParameter -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.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.defaultType -import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.isNothing -import org.jetbrains.kotlin.ir.util.isFunction import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.wasm.ir.* @@ -70,10 +63,6 @@ class WasmModuleCodegenContextImpl( wasmFragment.typeInfo.define(irClass, typeInfo) } - override fun generateInterfaceTable(irClass: IrClassSymbol, table: ConstantDataElement) { - wasmFragment.definedClassITableData.define(irClass, table) - } - override fun registerInitFunction(wasmFunction: WasmFunction, priority: String) { wasmFragment.initFunctions += WasmCompiledModuleFragment.FunWithPriority(wasmFunction, priority) } @@ -82,51 +71,34 @@ class WasmModuleCodegenContextImpl( wasmFragment.exports += wasmExport } - override fun registerVirtualFunction(irFunction: IrSimpleFunctionSymbol) { - wasmFragment.virtualFunctions += irFunction - } - - override fun registerInterface(irInterface: IrClassSymbol) { - wasmFragment.interfaces += irInterface - } - - override fun registerClass(irClass: IrClassSymbol) { - wasmFragment.classes += irClass - } - override fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) { wasmFragment.functions.define(irFunction, wasmFunction) } - override fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) { - wasmFragment.globals.define(irField, wasmGlobal) + override fun defineGlobalField(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) { + wasmFragment.globalFields.define(irField, wasmGlobal) + } + + override fun defineGlobalVTable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) { + wasmFragment.globalVTables.define(irClass, wasmGlobal) + } + + override fun defineGlobalClassITable(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) { + wasmFragment.globalClassITables.define(irClass, wasmGlobal) } override fun defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) { wasmFragment.gcTypes.define(irClass, wasmType) } + override fun defineVTableGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) { + wasmFragment.vTableGcTypes.define(irClass, wasmType) + } + override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) { wasmFragment.functionTypes.define(irFunction, wasmFunctionType) } - override fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable) { - wasmFragment.interfaceMethodTables.define(irFunction, wasmTable) - } - - override fun referenceInterfaceImplementationId( - interfaceImplementation: InterfaceImplementation - ): WasmSymbol = - wasmFragment.referencedInterfaceImplementationId.reference(interfaceImplementation) - - - override fun registerInterfaceImplementationMethod( - interfaceImplementation: InterfaceImplementation, - table: Map?> - ) { - wasmFragment.interfaceImplementationsMethods[interfaceImplementation] = table - } - private val classMetadataCache = mutableMapOf() override fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata = classMetadataCache.getOrPut(irClass) { @@ -139,18 +111,59 @@ class WasmModuleCodegenContextImpl( ) } + private val interfaceMetadataCache = mutableMapOf() + override fun getInterfaceMetadata(irClass: IrClassSymbol): InterfaceMetadata = + interfaceMetadataCache.getOrPut(irClass) { InterfaceMetadata(irClass.owner, backendContext.irBuiltIns) } + override fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol = wasmFragment.functions.reference(irFunction) - override fun referenceGlobal(irField: IrFieldSymbol): WasmSymbol = - wasmFragment.globals.reference(irField) + override fun referenceGlobalField(irField: IrFieldSymbol): WasmSymbol = + wasmFragment.globalFields.reference(irField) - override fun referenceGcType(irClass: IrClassSymbol): WasmSymbol { + override fun referenceGlobalVTable(irClass: IrClassSymbol): WasmSymbol = + wasmFragment.globalVTables.reference(irClass) + + override fun referenceGlobalClassITable(irClass: IrClassSymbol): WasmSymbol = + wasmFragment.globalClassITables.reference(irClass) + + private fun referenceNonNothingType( + irClass: IrClassSymbol, + from: WasmCompiledModuleFragment.ReferencableAndDefinable + ): WasmSymbol { val type = irClass.defaultType require(!type.isNothing()) { "Can't reference Nothing type" } - return wasmFragment.gcTypes.reference(irClass) + return from.reference(irClass) + } + + override fun referenceGcType(irClass: IrClassSymbol): WasmSymbol = + referenceNonNothingType(irClass, wasmFragment.gcTypes) + + override fun referenceVTableGcType(irClass: IrClassSymbol): WasmSymbol = + referenceNonNothingType(irClass, wasmFragment.vTableGcTypes) + + override fun referenceClassITableGcType(irClass: IrClassSymbol): WasmSymbol = + referenceNonNothingType(irClass, wasmFragment.classITableGcType) + + override fun defineClassITableGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) { + wasmFragment.classITableGcType.define(irClass, wasmType) + } + + override fun isAlreadyDefinedClassITableGcType(irClass: IrClassSymbol): Boolean = + wasmFragment.classITableGcType.defined.keys.contains(irClass) + + override fun referenceClassITableInterfaceSlot(irClass: IrClassSymbol): WasmSymbol { + val type = irClass.defaultType + require(!type.isNothing()) { + "Can't reference Nothing type" + } + return wasmFragment.classITableInterfaceSlot.reference(irClass) + } + + override fun defineClassITableInterfaceSlot(irClass: IrClassSymbol, slot: Int) { + wasmFragment.classITableInterfaceSlot.define(irClass, slot) } override fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol = @@ -159,35 +172,14 @@ class WasmModuleCodegenContextImpl( override fun referenceClassId(irClass: IrClassSymbol): WasmSymbol = wasmFragment.classIds.reference(irClass) - override fun referenceInterfaceTableAddress(irClass: IrClassSymbol): WasmSymbol { - if (irClass.owner.modality == Modality.ABSTRACT) return WasmSymbol(-1) - return wasmFragment.referencedClassITableAddresses.reference(irClass) - } - - override fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol { return wasmFragment.interfaceId.reference(irInterface) } - override fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol { - if (irFunction.owner.modality == Modality.ABSTRACT) - error("Abstract functions are not stored in table") - return wasmFragment.virtualFunctionId.reference(irFunction) - } - - override fun referenceSignatureId(signature: WasmSignature): WasmSymbol { - wasmFragment.signatures.add(signature) - return wasmFragment.signatureId.reference(signature) - } - - override fun referenceInterfaceTable(irFunction: IrFunctionSymbol): WasmSymbol { - return wasmFragment.interfaceMethodTables.reference(irFunction) - } - override fun getStructFieldRef(field: IrField): WasmSymbol { val klass = field.parentAsClass val metadata = getClassMetadata(klass.symbol) - val fieldId = metadata.fields.indexOf(field) + val fieldId = metadata.fields.indexOf(field) + 2 //Implicit vtable and vtable field return WasmSymbol(fieldId) } @@ -195,4 +187,5 @@ class WasmModuleCodegenContextImpl( wasmFragment.jsFuns += WasmCompiledModuleFragment.JsCodeSnippet(importName = importName, jsCode = jsCode) } -} \ No newline at end of file +} + 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 507e21fc7fc..f068b9a4cb5 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 @@ -6,9 +6,16 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm import org.jetbrains.kotlin.backend.wasm.WasmBackendContext -import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.backend.wasm.utils.DisjointUnions +import org.jetbrains.kotlin.backend.wasm.utils.getWasmArrayAnnotation +import org.jetbrains.kotlin.backend.wasm.utils.isAbstractOrSealed +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.declarations.IrPackageFragment +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.util.isAnnotationClass +import org.jetbrains.kotlin.ir.util.isInterface +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid class WasmModuleFragmentGenerator( @@ -16,28 +23,49 @@ class WasmModuleFragmentGenerator( wasmModuleFragment: WasmCompiledModuleFragment, allowIncompleteImplementations: Boolean, ) { + private val hierarchyDisjointUnions = DisjointUnions() + private val declarationGenerator = DeclarationGenerator( WasmModuleCodegenContextImpl( backendContext, wasmModuleFragment, ), - allowIncompleteImplementations + allowIncompleteImplementations, + hierarchyDisjointUnions, ) + private val interfaceCollector = object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { } + + override fun visitClass(declaration: IrClass) { + if (declaration.isAnnotationClass) return + if (declaration.isExternal) return + if (declaration.getWasmArrayAnnotation() != null) return + if (declaration.isInterface) return + if (declaration.isAbstractOrSealed) return + + val classMetadata = declarationGenerator.context.getClassMetadata(declaration.symbol) + if (classMetadata.interfaces.isNotEmpty()) { + hierarchyDisjointUnions.addUnion(classMetadata.interfaces.map { it.symbol }) + } + } + } + + fun collectInterfaceTables(irModuleFragment: IrModuleFragment) { + acceptVisitor(irModuleFragment, interfaceCollector) + hierarchyDisjointUnions.compress() + } + fun generateModule(irModuleFragment: IrModuleFragment) { + acceptVisitor(irModuleFragment, declarationGenerator) + } + + private fun acceptVisitor(irModuleFragment: IrModuleFragment, visitor: IrElementVisitorVoid) { for (irFile in irModuleFragment.files) { - generatePackageFragment(irFile) + for (irDeclaration in irFile.declarations) { + irDeclaration.acceptVoid(visitor) + } } } - - fun generatePackageFragment(irPackageFragment: IrPackageFragment) { - for (irDeclaration in irPackageFragment.declarations) { - generateDeclaration(irDeclaration) - } - } - - fun generateDeclaration(irDeclaration: IrDeclaration) { - irDeclaration.acceptVoid(declarationGenerator) - } } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/FieldInitializersLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/FieldInitializersLowering.kt index 75f7c4f0b94..0f687f970ec 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/FieldInitializersLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/FieldInitializersLowering.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.at import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.wasm.WasmBackendContext -import org.jetbrains.kotlin.backend.wasm.ir2wasm.getRuntimeClass import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.irSetField import org.jetbrains.kotlin.ir.declarations.IrField diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt index fc857aa1787..72d38341ded 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt @@ -101,9 +101,6 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme private val IrType.eraseToClassOrInterface: IrClass get() = this.erasedUpperBound ?: builtIns.anyClass.owner - private val IrType.eraseToClass: IrClass - get() = this.getRuntimeClass ?: builtIns.anyClass.owner - private fun generateTypeCheck( valueProvider: () -> IrExpression, toType: IrType @@ -302,19 +299,16 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme } private fun generateIsInterface(argument: IrExpression, toType: IrType): IrExpression { - val interfaceId = builder.irCall(symbols.wasmInterfaceId).apply { - putTypeArgument(0, toType) - } return builder.irCall(symbols.isInterface).apply { putValueArgument(0, argument) - putValueArgument(1, interfaceId) + putTypeArgument(0, toType) } } private fun generateIsSubClass(argument: IrExpression, toType: IrType): IrExpression { val fromType = argument.type - val fromTypeErased = fromType.eraseToClass - val toTypeErased = toType.eraseToClass + val fromTypeErased = fromType.getRuntimeClass(context.irBuiltIns) + val toTypeErased = toType.getRuntimeClass(context.irBuiltIns) if (fromTypeErased.isSubclassOf(toTypeErased)) { return builder.irTrue() } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/DisjointUnions.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/DisjointUnions.kt new file mode 100644 index 00000000000..bee12ebd951 --- /dev/null +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/DisjointUnions.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2010-2022 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.utils + +class DisjointUnions { + private val leafParents = mutableMapOf() + private var dirty: Boolean = false + + private inner class Node(var rank: Int = 0, val leafs: MutableList = mutableListOf()) { + var parent: Node? = null + override fun toString(): String = "${if (parent == null) "ROOT " else ""}Node with ${leafs.size} leafs and $rank rank" + } + + private fun findRoot(node: T): Node? = + leafParents[node]?.let { findRoot(it) } + + private fun findRoot(node: Node, pathWeight: Int = 0): Node { + val strictParent = node.parent ?: return node + val currentWeight = pathWeight + node.leafs.size + val foundRoot = findRoot(strictParent, currentWeight) + + if (foundRoot != node) { + val leafs = node.leafs + node.rank -= currentWeight + check(node.rank >= 0) + foundRoot.leafs.addAll(leafs) + leafs.clear() + node.parent = foundRoot + } + + return foundRoot + } + + private fun addToRoot(leaf: T, root: Node) { + leafParents[leaf] = root + root.rank++ + root.leafs.add(leaf) + } + + private fun mergeRoots(root1: Node, root2: Node): Node { + if (root1 == root2) return root1 + require(root1.parent == null && root2.parent == null) { "Merge is possible only for root nodes" } + if (root2.parent == root1) return root1 + if (root1.parent == root2) return root2 + + val rootToMove: Node + val newParentRoot: Node + if (root1.rank > root2.rank) { + rootToMove = root2 + newParentRoot = root1 + } else { + rootToMove = root1 + newParentRoot = root2 + } + + rootToMove.parent = newParentRoot + + val leafs = rootToMove.leafs + newParentRoot.rank += leafs.size + rootToMove.rank -= leafs.size + check(rootToMove.rank >= 0) + newParentRoot.leafs.addAll(leafs) + leafs.clear() + + return newParentRoot + } + + fun addUnion(elements: List) { + var currentRoot: Node? = null + dirty = true + for (leaf in elements) { + val strictRoot = leafParents[leaf] + if (strictRoot == null) { + currentRoot = currentRoot?.let(::findRoot) ?: Node() + addToRoot(leaf, currentRoot) + } else { + val leafRoot = findRoot(strictRoot) + currentRoot = if (currentRoot != null) mergeRoots(currentRoot, leafRoot) else leafRoot + } + } + } + + fun compress() { + if (dirty) { + leafParents.keys.forEach(::findRoot) + dirty = false + } + } + + operator fun contains(element: T): Boolean = + leafParents.containsKey(element) + + operator fun get(element: T): List { + require(!dirty) { "Call compress before getting union" } + val root = findRoot(element) + require(root != null) { "Element not contains in any union" } + check(root.rank == root.leafs.size) { "Invalid tree state after compress" } + return root.leafs + } +} \ No newline at end of file diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/Utils.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/Utils.kt index 501e9bc3363..f595826ec2b 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/Utils.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/Utils.kt @@ -5,7 +5,9 @@ package org.jetbrains.kotlin.backend.wasm.utils +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.expressions.IrTry // Backed codegen can only handle try/catch in canonical form. @@ -16,6 +18,9 @@ import org.jetbrains.kotlin.ir.expressions.IrTry // ...exprs // } // no-finally -fun IrTry.isCanonical(builtIns: IrBuiltIns) = +internal fun IrTry.isCanonical(builtIns: IrBuiltIns) = catches.singleOrNull()?.catchParameter?.symbol?.owner?.type == builtIns.throwableType && - finallyExpression == null \ No newline at end of file + finallyExpression == null + +internal val IrClass.isAbstractOrSealed + get() = modality == Modality.ABSTRACT || modality == Modality.SEALED \ No newline at end of file diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt index 4e4b3274ef2..b4005e2b4a3 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt @@ -14,9 +14,8 @@ internal const val TYPE_INFO_TYPE_PACKAGE_NAME_PRT_OFFSET = TYPE_INFO_TYPE_PACKA internal const val TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET = TYPE_INFO_TYPE_PACKAGE_NAME_PRT_OFFSET + TYPE_INFO_ELEMENT_SIZE internal const val TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET = TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET + TYPE_INFO_ELEMENT_SIZE internal const val TYPE_INFO_SUPER_TYPE_OFFSET = TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET + TYPE_INFO_ELEMENT_SIZE -internal const val TYPE_INFO_ITABLE_PTR_OFFSET = TYPE_INFO_SUPER_TYPE_OFFSET + TYPE_INFO_ELEMENT_SIZE -internal const val TYPE_INFO_VTABLE_LENGTH_OFFSET = TYPE_INFO_ITABLE_PTR_OFFSET + TYPE_INFO_ELEMENT_SIZE -internal const val TYPE_INFO_VTABLE_OFFSET = TYPE_INFO_VTABLE_LENGTH_OFFSET + TYPE_INFO_ELEMENT_SIZE +internal const val TYPE_INFO_ITABLE_SIZE_OFFSET = TYPE_INFO_SUPER_TYPE_OFFSET + TYPE_INFO_ELEMENT_SIZE +internal const val TYPE_INFO_ITABLE_OFFSET = TYPE_INFO_ITABLE_SIZE_OFFSET + TYPE_INFO_ELEMENT_SIZE internal class TypeInfoData(val typeId: Int, val isInterface: Boolean, val packageName: String, val typeName: String) @@ -33,45 +32,24 @@ internal fun getTypeInfoTypeDataByPtr(typeInfoPtr: Int): TypeInfoData { internal fun getSuperTypeId(typeInfoPtr: Int): Int = wasm_i32_load(typeInfoPtr + TYPE_INFO_SUPER_TYPE_OFFSET) -internal fun getVtablePtr(obj: Any): Int = - obj.typeInfo + TYPE_INFO_VTABLE_OFFSET - -internal fun getVtableLength(obj: Any): Int = - wasm_i32_load(obj.typeInfo + TYPE_INFO_VTABLE_LENGTH_OFFSET) - -internal fun getItablePtr(obj: Any): Int = - wasm_i32_load(obj.typeInfo + TYPE_INFO_ITABLE_PTR_OFFSET) - -internal fun getInterfaceListLength(itablePtr: Int): Int = - wasm_i32_load(itablePtr + TYPE_INFO_VTABLE_LENGTH_OFFSET) - -internal fun getVirtualMethodId(obj: Any, virtualFunctionSlot: Int): Int { - val vtablePtr = getVtablePtr(obj) - val methodIdPtr = vtablePtr + virtualFunctionSlot * TYPE_INFO_ELEMENT_SIZE - return wasm_i32_load(methodIdPtr) -} - -// Returns -1 if obj does not implement interface -internal fun getInterfaceImplId(obj: Any, interfaceId: Int): Int { - val interfaceListSizePtr = getItablePtr(obj) - val interfaceListPtr = interfaceListSizePtr + TYPE_INFO_ELEMENT_SIZE - val interfaceListSize = wasm_i32_load(interfaceListSizePtr) +internal fun isInterfaceById(obj: Any, interfaceId: Int): Boolean { + val interfaceListSize = wasm_i32_load(obj.typeInfo + TYPE_INFO_ITABLE_SIZE_OFFSET) + val interfaceListPtr = obj.typeInfo + TYPE_INFO_ITABLE_OFFSET var interfaceSlot = 0 while (interfaceSlot < interfaceListSize) { val supportedInterface = wasm_i32_load(interfaceListPtr + interfaceSlot * TYPE_INFO_ELEMENT_SIZE) if (supportedInterface == interfaceId) { - return wasm_i32_load(interfaceListPtr + interfaceListSize * TYPE_INFO_ELEMENT_SIZE + interfaceSlot * TYPE_INFO_ELEMENT_SIZE) + return true } interfaceSlot++ } - - return -1 + return false } -internal fun isInterface(obj: Any, interfaceId: Int): Boolean { - return getInterfaceImplId(obj, interfaceId) != -1 -} +@ExcludedFromCodegen +internal fun isInterface(obj: Any): Boolean = + implementedAsIntrinsic @ExcludedFromCodegen internal fun wasmClassId(): Int = diff --git a/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt b/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt index 086d1bf7494..9cd5012b1af 100644 --- a/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt +++ b/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt @@ -7,7 +7,7 @@ package kotlin.reflect.wasm.internal import kotlin.reflect.* import kotlin.wasm.internal.TypeInfoData import kotlin.wasm.internal.getSuperTypeId -import kotlin.wasm.internal.isInterface +import kotlin.wasm.internal.isInterfaceById internal object NothingKClassImpl : KClass { override val simpleName: String = "Nothing" @@ -38,7 +38,7 @@ internal class KClassImpl(private val typeData: TypeInfoData) : KClass< } override fun isInstance(value: Any?): Boolean = value?.let { - if (typeData.isInterface) isInterface(it, typeData.typeId) else checkSuperTypeInstance(it) + if (typeData.isInterface) isInterfaceById(it, typeData.typeId) else checkSuperTypeInstance(it) } ?: false override fun equals(other: Any?): Boolean = diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt index 1e549e4115b..1622121aef9 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt @@ -374,6 +374,7 @@ enum class WasmOp( BR_ON_CAST("br_on_cast", 0xFB_42, listOf(LABEL_IDX)), BR_ON_CAST_FAIL("br_on_cast_fail", 0xfb43, listOf(LABEL_IDX)), + BR_ON_CAST_STATIC_FAIL("br_on_cast_static_fail", 0xfb47, listOf(LABEL_IDX, STRUCT_TYPE_IDX)), REF_IS_FUNC("ref.is_func", 0xfb50), REF_IS_DATA("ref.is_data", 0xfb51), diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt index 6eacbfe3a4d..548941732ff 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt @@ -34,15 +34,19 @@ abstract class WasmExpressionBuilder { } @Suppress("UNUSED_PARAMETER") - fun buildBlock(label: String?, resultType: WasmType? = null) { + inline fun buildBlock(label: String?, resultType: WasmType? = null, body: (Int) -> Unit) { numberOfNestedBlocks++ buildInstr(WasmOp.BLOCK, WasmImmediate.BlockType.Value(resultType)) + body(numberOfNestedBlocks) + buildEnd() } @Suppress("UNUSED_PARAMETER") - fun buildLoop(label: String?, resultType: WasmType? = null) { + inline fun buildLoop(label: String?, resultType: WasmType? = null, body: (Int) -> Unit) { numberOfNestedBlocks++ buildInstr(WasmOp.LOOP, WasmImmediate.BlockType.Value(resultType)) + body(numberOfNestedBlocks) + buildEnd() } @Suppress("UNUSED_PARAMETER") @@ -60,10 +64,21 @@ abstract class WasmExpressionBuilder { buildInstr(WasmOp.END) } - fun buildBr(absoluteBlockLevel: Int) { + + fun buildBrInstr(brOp: WasmOp, absoluteBlockLevel: Int) { val relativeLevel = numberOfNestedBlocks - absoluteBlockLevel assert(relativeLevel >= 0) { "Negative relative block index" } - buildInstr(WasmOp.BR, WasmImmediate.LabelIdx(relativeLevel)) + buildInstr(brOp, WasmImmediate.LabelIdx(relativeLevel)) + } + + fun buildBrInstr(brOp: WasmOp, absoluteBlockLevel: Int, symbol: WasmSymbolReadOnly) { + val relativeLevel = numberOfNestedBlocks - absoluteBlockLevel + assert(relativeLevel >= 0) { "Negative relative block index" } + buildInstr(brOp, WasmImmediate.LabelIdx(relativeLevel), WasmImmediate.TypeIdx(symbol)) + } + + fun buildBr(absoluteBlockLevel: Int) { + buildBrInstr(WasmOp.BR, absoluteBlockLevel) } fun buildThrow(tagIdx: Int) { @@ -81,9 +96,7 @@ abstract class WasmExpressionBuilder { } fun buildBrIf(absoluteBlockLevel: Int) { - val relativeLevel = numberOfNestedBlocks - absoluteBlockLevel - assert(relativeLevel >= 0) { "Negative relative block index" } - buildInstr(WasmOp.BR_IF, WasmImmediate.LabelIdx(relativeLevel)) + buildBrInstr(WasmOp.BR_IF, absoluteBlockLevel) } fun buildCall(symbol: WasmSymbol) { @@ -126,7 +139,7 @@ abstract class WasmExpressionBuilder { } fun buildStructNew(struct: WasmSymbol) { - buildInstr(WasmOp.STRUCT_NEW_WITH_RTT, WasmImmediate.GcType(struct)) + buildInstr(WasmOp.STRUCT_NEW, WasmImmediate.GcType(struct)) } fun buildStructSet(struct: WasmSymbol, fieldId: WasmSymbol) { @@ -142,8 +155,8 @@ abstract class WasmExpressionBuilder { buildInstr(WasmOp.REF_CAST) } - fun buildRefCastStatic(type: WasmSymbolReadOnly) { - buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(type)) + fun buildRefCastStatic(toType: WasmSymbolReadOnly) { + buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(toType)) } fun buildRefNull(type: WasmHeapType) { diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmIrExpressionBuilder.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmIrExpressionBuilder.kt index f1e1f652ce0..0d82e853e13 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmIrExpressionBuilder.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmIrExpressionBuilder.kt @@ -21,7 +21,6 @@ class WasmIrExpressionBuilder( } } - override var numberOfNestedBlocks: Int = 0 set(value) { assert(value >= 0) { "end without matching block" }