[WASM] New VTable format

This commit is contained in:
Igor Yakovlev
2022-03-22 18:43:26 +01:00
parent 8bc5508f5c
commit 8da6ab7a04
22 changed files with 619 additions and 525 deletions
@@ -173,9 +173,6 @@ class WasmSymbols(
val wasmClassId = getInternalFunction("wasmClassId") val wasmClassId = getInternalFunction("wasmClassId")
val wasmInterfaceId = getInternalFunction("wasmInterfaceId") val wasmInterfaceId = getInternalFunction("wasmInterfaceId")
val getVirtualMethodId = getInternalFunction("getVirtualMethodId")
val getInterfaceImplId = getInternalFunction("getInterfaceImplId")
val isInterface = getInternalFunction("isInterface") val isInterface = getInternalFunction("isInterface")
val nullableEquals = getInternalFunction("nullableEquals") val nullableEquals = getInternalFunction("nullableEquals")
@@ -80,6 +80,7 @@ fun compileWasm(
): WasmCompilerResult { ): WasmCompilerResult {
val compiledWasmModule = WasmCompiledModuleFragment(backendContext.irBuiltIns) val compiledWasmModule = WasmCompiledModuleFragment(backendContext.irBuiltIns)
val codeGenerator = WasmModuleFragmentGenerator(backendContext, compiledWasmModule, allowIncompleteImplementations = allowIncompleteImplementations) val codeGenerator = WasmModuleFragmentGenerator(backendContext, compiledWasmModule, allowIncompleteImplementations = allowIncompleteImplementations)
allModules.forEach { codeGenerator.collectInterfaceTables(it) }
allModules.forEach { codeGenerator.generateModule(it) } allModules.forEach { codeGenerator.generateModule(it) }
val linkedModule = compiledWasmModule.linkWasmCompiledFragments() val linkedModule = compiledWasmModule.linkWasmCompiledFragments()
@@ -97,11 +97,8 @@ internal class WasmUsefulDeclarationProcessor(
val isSuperCall = expression.superQualifierSymbol != null val isSuperCall = expression.superQualifierSymbol != null
if (function is IrSimpleFunction && function.isOverridable && !isSuperCall) { if (function is IrSimpleFunction && function.isOverridable && !isSuperCall) {
val klass = function.parentAsClass val klass = function.parentAsClass
if (!klass.isInterface) { if (klass.isInterface) {
context.wasmSymbols.getVirtualMethodId.owner.enqueue(data, "call on class receiver")
} else {
klass.enqueue(data, "receiver class") klass.enqueue(data, "receiver class")
context.wasmSymbols.getInterfaceImplId.owner.enqueue(data, "call on interface receiver")
} }
function.enqueue(data, "method call") function.enqueue(data, "method call")
} }
@@ -134,7 +131,7 @@ internal class WasmUsefulDeclarationProcessor(
} }
private fun IrType.enqueueRuntimeClassOrAny(from: IrDeclaration, info: String): Unit = 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) { private fun IrType.enqueueType(from: IrDeclaration, info: String) {
getInlinedValueTypeIfAny() getInlinedValueTypeIfAny()
@@ -10,9 +10,7 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.common.ir.returnType import org.jetbrains.kotlin.backend.common.ir.returnType
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.WasmSymbols import org.jetbrains.kotlin.backend.wasm.WasmSymbols
import org.jetbrains.kotlin.backend.wasm.utils.getWasmArrayAnnotation import org.jetbrains.kotlin.backend.wasm.utils.*
import org.jetbrains.kotlin.backend.wasm.utils.getWasmOpAnnotation
import org.jetbrains.kotlin.backend.wasm.utils.hasWasmNoOpCastAnnotation
import org.jetbrains.kotlin.backend.wasm.utils.isCanonical import org.jetbrains.kotlin.backend.wasm.utils.isCanonical
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement 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.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.wasm.ir.* import org.jetbrains.kotlin.wasm.ir.*
class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorVoid { class BodyGenerator(
val context: WasmFunctionCodegenContext,
private val hierarchyDisjointUnions: DisjointUnions<IrClassSymbol>
) : IrElementVisitorVoid {
val body: WasmExpressionBuilder = context.bodyGen val body: WasmExpressionBuilder = context.bodyGen
// Shortcuts // Shortcuts
@@ -117,7 +118,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
generateInstanceFieldAccess(field) generateInstanceFieldAccess(field)
} }
} else { } else {
body.buildGetGlobal(context.referenceGlobal(field.symbol)) body.buildGetGlobal(context.referenceGlobalField(field.symbol))
} }
} }
@@ -154,7 +155,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
) )
} else { } else {
generateExpression(expression.value) generateExpression(expression.value)
body.buildSetGlobal(context.referenceGlobal(expression.symbol)) body.buildSetGlobal(context.referenceGlobalField(expression.symbol))
} }
body.buildGetUnit() body.buildGetUnit()
@@ -203,6 +204,14 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
return 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 wasmClassId = context.referenceClassId(klass.symbol)
val irFields: List<IrField> = klass.allFields(backendContext.irBuiltIns) val irFields: List<IrField> = klass.allFields(backendContext.irBuiltIns)
@@ -214,7 +223,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
generateDefaultInitializerForType(context.transformType(field.type), body) generateDefaultInitializerForType(context.transformType(field.type), body)
} }
generateClassRTT(klass.symbol)
body.buildStructNew(wasmGcType) body.buildStructNew(wasmGcType)
generateCall(expression) generateCall(expression)
} }
@@ -236,16 +244,21 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
// Box intrinsic has an additional klass ID argument. // Box intrinsic has an additional klass ID argument.
// Processing it separately // Processing it separately
if (call.symbol == wasmSymbols.boxIntrinsic) { if (call.symbol == wasmSymbols.boxIntrinsic) {
val toType = call.getTypeArgument(0)!! val klass = call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns)
val klass = toType.getRuntimeClass!! val klassSymbol = klass.symbol
val structTypeName = context.referenceGcType(klass.symbol)
val klassId = context.referenceClassId(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 body.buildConstI32(0) // Any::_hashCode
generateExpression(call.getValueArgument(0)!!) generateExpression(call.getValueArgument(0)!!)
generateClassRTT(klass.symbol) body.buildStructNew(context.referenceGcType(klassSymbol))
body.buildStructNew(structTypeName)
return return
} }
@@ -296,20 +309,35 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
val vfSlot = classMetadata.virtualMethods.indexOfFirst { it.function == function } val vfSlot = classMetadata.virtualMethods.indexOfFirst { it.function == function }
// Dispatch receiver should be simple and without side effects at this point // Dispatch receiver should be simple and without side effects at this point
// TODO: Verify // TODO: Verify
generateExpression(call.dispatchReceiver!!) val receiver = call.dispatchReceiver!!
body.buildConstI32(vfSlot) generateExpression(receiver)
body.buildCall(context.referenceFunction(wasmSymbols.getVirtualMethodId))
body.buildCallIndirect( if (!receiver.type.getRuntimeClass(irBuiltIns).isSubclassOf(klass)) {
symbol = context.referenceFunctionType(function.symbol) 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 { } else {
generateExpression(call.dispatchReceiver!!) val symbol = klass.symbol
body.buildConstI32Symbol(context.referenceInterfaceId(klass.symbol)) if (symbol in hierarchyDisjointUnions) {
body.buildCall(context.referenceFunction(wasmSymbols.getInterfaceImplId)) generateExpression(call.dispatchReceiver!!)
body.buildCallIndirect(
tableIdx = WasmSymbolIntWrapper(context.referenceInterfaceTable(function.symbol)), body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(1))
symbol = context.referenceFunctionType(function.symbol)
) 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 { } else {
@@ -322,13 +350,14 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildGetUnit() body.buildGetUnit()
} }
private fun generateTypeRTT(type: IrType) { private fun generateRefCast(type: IrType) {
val klass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass body.buildRefCastStatic(
generateClassRTT(klass) context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol)
)
} }
private fun generateClassRTT(klass: IrClassSymbol) { private fun generateTypeRTT(type: IrType) {
body.buildRttCanon(context.referenceGcType(klass)) body.buildRttCanon(context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol))
} }
// Return true if generated. // Return true if generated.
@@ -356,10 +385,35 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildConstI32Symbol(context.referenceInterfaceId(irInterface.symbol)) 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 -> { wasmSymbols.wasmRefCast -> {
val toType = call.getTypeArgument(0)!! generateRefCast(call.getTypeArgument(0)!!)
generateTypeRTT(toType)
body.buildRefCast()
} }
wasmSymbols.refTest -> { wasmSymbols.refTest -> {
@@ -443,7 +497,8 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
override fun visitReturn(expression: IrReturn) { override fun visitReturn(expression: IrReturn) {
if (expression.returnTargetSymbol.owner.returnType(backendContext) == irBuiltIns.unitType && if (expression.returnTargetSymbol.owner.returnType(backendContext) == irBuiltIns.unitType &&
expression.returnTargetSymbol.owner != unitGetInstance) { expression.returnTargetSymbol.owner != unitGetInstance
) {
generateStatement(expression.value) generateStatement(expression.value)
} else { } else {
generateExpression(expression.value) generateExpression(expression.value)
@@ -499,24 +554,17 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
val label = loop.label val label = loop.label
body.buildLoop(label) body.buildLoop(label) { wasmLoop ->
val wasmLoop = body.numberOfNestedBlocks body.buildBlock("BREAK_$label") { wasmBreakBlock ->
body.buildBlock("CONTINUE_$label") { wasmContinueBlock ->
body.buildBlock("BREAK_$label") context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock)
val wasmBreakBlock = body.numberOfNestedBlocks context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmContinueBlock)
loop.body?.let { generateStatement(it) }
body.buildBlock("CONTINUE_$label") }
val wasmContinueBlock = body.numberOfNestedBlocks generateExpression(loop.condition)
body.buildBrIf(wasmLoop)
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.buildGetUnit() body.buildGetUnit()
} }
@@ -530,24 +578,20 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
val label = loop.label val label = loop.label
body.buildLoop(label) body.buildLoop(label) { wasmLoop ->
val wasmLoop = body.numberOfNestedBlocks body.buildBlock("BREAK_$label") { wasmBreakBlock ->
context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock)
context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmLoop)
body.buildBlock("BREAK_$label") generateExpression(loop.condition)
val wasmBreakBlock = body.numberOfNestedBlocks body.buildInstr(WasmOp.I32_EQZ)
body.buildBrIf(wasmBreakBlock)
context.defineLoopLevel(loop, LoopLabelType.BREAK, wasmBreakBlock) loop.body?.let {
context.defineLoopLevel(loop, LoopLabelType.CONTINUE, wasmLoop) generateStatement(it)
}
generateExpression(loop.condition) body.buildBr(wasmLoop)
body.buildInstr(WasmOp.I32_EQZ) }
body.buildBrIf(wasmBreakBlock)
loop.body?.let {
generateStatement(it)
} }
body.buildBr(wasmLoop)
body.buildEnd()
body.buildEnd()
body.buildGetUnit() body.buildGetUnit()
} }
@@ -564,7 +608,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
} }
// Return true if function is recognized as intrinsic. // 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()) { if (function.hasWasmNoOpCastAnnotation()) {
return true return true
} }
@@ -22,14 +22,18 @@ import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression 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.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.parentOrNull import org.jetbrains.kotlin.name.parentOrNull
import org.jetbrains.kotlin.wasm.ir.* 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<IrClassSymbol>,
) : IrElementVisitorVoid {
// Shortcuts // Shortcuts
private val backendContext: WasmBackendContext = context.backendContext private val backendContext: WasmBackendContext = context.backendContext
@@ -90,13 +94,8 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
) )
context.defineFunctionType(declaration.symbol, wasmFunctionType) context.defineFunctionType(declaration.symbol, wasmFunctionType)
if (declaration is IrSimpleFunction) { if (declaration is IrSimpleFunction && declaration.modality == Modality.ABSTRACT) {
if (declaration.modality == Modality.ABSTRACT) return 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)
}
} }
assert(declaration == declaration.realOverrideTarget) { assert(declaration == declaration.realOverrideTarget) {
@@ -128,7 +127,7 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
} }
val exprGen = functionCodegenContext.bodyGen val exprGen = functionCodegenContext.bodyGen
val bodyBuilder = BodyGenerator(functionCodegenContext) val bodyBuilder = BodyGenerator(functionCodegenContext, hierarchyDisjointUnions)
require(declaration.body is IrBlockBody) { "Only IrBlockBody is supported" } require(declaration.body is IrBlockBody) { "Only IrBlockBody is supported" }
declaration.body?.acceptVoid(bodyBuilder) 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<VirtualMethodMetadata>,
name: String,
superType: WasmSymbolReadOnly<WasmTypeDeclaration>? = 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) { override fun visitClass(declaration: IrClass) {
if (declaration.isAnnotationClass) return if (declaration.isAnnotationClass) return
if (declaration.isExternal) return if (declaration.isExternal) return
val symbol = declaration.symbol val symbol = declaration.symbol
// Handle arrays // Handle arrays
declaration.getWasmArrayAnnotation()?.let { wasmArrayAnnotation -> declaration.getWasmArrayAnnotation()?.let { wasmArrayAnnotation ->
val nameStr = declaration.fqNameWhenAvailable.toString() val nameStr = declaration.fqNameWhenAvailable.toString()
@@ -189,60 +319,43 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
return return
} }
val nameStr = declaration.fqNameWhenAvailable.toString()
if (declaration.isInterface) { if (declaration.isInterface) {
val metadata = InterfaceMetadata(declaration, irBuiltIns) if (symbol in hierarchyDisjointUnions) {
for (method in metadata.methods) { val vtableStruct = createVirtualTableStruct(
val methodSymbol = method.function.symbol methods = context.getInterfaceMetadata(symbol).methods,
val table = WasmTable( name = "$nameStr.itable"
elementType = WasmRefNullType(WasmHeapType.Type(context.referenceFunctionType(methodSymbol)))
) )
context.defineInterfaceMethodTable(methodSymbol, table) context.defineVTableGcType(symbol, vtableStruct)
} }
context.registerInterface(symbol)
} else { } else {
val nameStr = declaration.fqNameWhenAvailable.toString()
val metadata = context.getClassMetadata(symbol) 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<WasmStructFieldDeclaration>()
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 superClass = metadata.superClass
val structType = WasmStructDeclaration( val structType = WasmStructDeclaration(
name = nameStr, name = nameStr,
fields = declaration.allFields(irBuiltIns).map { fields = fields,
WasmStructFieldDeclaration( superType = superClass?.let { context.referenceGcType(superClass.klass.symbol) }
name = it.name.toString(),
type = context.transformFieldType(it.type),
isMutable = true
)
},
superClass?.let { context.referenceGcType(superClass.klass.symbol) }
) )
context.defineGcType(symbol, structType) context.defineGcType(symbol, structType)
context.registerClass(symbol)
context.generateTypeInfo(symbol, binaryDataStruct(metadata)) 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) { for (member in declaration.declarations) {
@@ -251,15 +364,13 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
} }
private fun binaryDataStruct(classMetadata: ClassMetadata): ConstantDataStruct { private fun binaryDataStruct(classMetadata: ClassMetadata): ConstantDataStruct {
val invalidIndex = -1
val fqnShouldBeEmitted = context.backendContext.configuration.languageVersionSettings.getFlag(allowFullyQualifiedNameInKClass) val fqnShouldBeEmitted = context.backendContext.configuration.languageVersionSettings.getFlag(allowFullyQualifiedNameInKClass)
//TODO("FqName for inner classes could be invalid due to topping it out from outer class") //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 packageName = if (fqnShouldBeEmitted) classMetadata.klass.kotlinFqName.parentOrNull()?.asString() ?: "" else ""
val simpleName = classMetadata.klass.kotlinFqName.shortName().asString() val simpleName = classMetadata.klass.kotlinFqName.shortName().asString()
val typeInfo = ConstantDataStruct( val typeInfo = ConstantDataStruct(
"TypeInfo", name = "TypeInfo",
listOf( elements = listOf(
ConstantDataIntField("TypePackageNameLength", packageName.length), ConstantDataIntField("TypePackageNameLength", packageName.length),
ConstantDataIntField("TypePackageNamePtr", context.referenceStringLiteral(packageName)), ConstantDataIntField("TypePackageNamePtr", context.referenceStringLiteral(packageName)),
ConstantDataIntField("TypeNameLength", simpleName.length), ConstantDataIntField("TypeNameLength", simpleName.length),
@@ -272,40 +383,17 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
ConstantDataIntField("SuperTypeId", context.referenceClassId(it.symbol)) ConstantDataIntField("SuperTypeId", context.referenceClassId(it.symbol))
} ?: ConstantDataIntField("SuperTypeId", -1) } ?: ConstantDataIntField("SuperTypeId", -1)
val vtableSizeField = ConstantDataIntField( val typeInfoContent = mutableListOf(typeInfo, superTypeId)
"V-table length", if (!classMetadata.klass.isAbstractOrSealed) {
classMetadata.virtualMethods.size typeInfoContent.add(interfaceTable(classMetadata))
) }
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)
)
return ConstantDataStruct( return ConstantDataStruct(
"Class TypeInfo: ${classMetadata.klass.fqNameWhenAvailable} ", name = "Class TypeInfo: ${classMetadata.klass.fqNameWhenAvailable} ",
listOf( elements = typeInfoContent
typeInfo,
superTypeId,
interfaceTablePtr,
vtableSizeField,
vtableArray,
)
) )
} }
private fun interfaceTable(classMetadata: ClassMetadata): ConstantDataStruct { private fun interfaceTable(classMetadata: ClassMetadata): ConstantDataStruct {
val interfaces = classMetadata.interfaces val interfaces = classMetadata.interfaces
val size = ConstantDataIntField("size", interfaces.size) val size = ConstantDataIntField("size", interfaces.size)
@@ -313,20 +401,10 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
"interfaceIds", "interfaceIds",
interfaces.map { context.referenceInterfaceId(it.symbol) }, interfaces.map { context.referenceInterfaceId(it.symbol) },
) )
val interfaceImplementationIds = ConstantDataIntArray(
"interfaceImplementationId",
interfaces.map {
context.referenceInterfaceImplementationId(InterfaceImplementation(it.symbol, classMetadata.klass.symbol))
},
)
return ConstantDataStruct( return ConstantDataStruct(
"Class interface table: ${classMetadata.klass.fqNameWhenAvailable} ", name = "Class interface table: ${classMetadata.klass.fqNameWhenAvailable} ",
listOf( elements = listOf(size, interfaceIds)
size,
interfaceIds,
interfaceImplementationIds,
)
) )
} }
@@ -357,7 +435,7 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
init = initBody init = initBody
) )
context.defineGlobal(declaration.symbol, global) context.defineGlobalField(declaration.symbol, global)
} }
} }
@@ -44,8 +44,8 @@ class WasmTypeTransformer(
toWasmValueType() toWasmValueType()
} }
fun IrType.toWasmGcRefType(): WasmType = private fun IrType.toWasmGcRefType(): WasmType =
WasmRefNullType(WasmHeapType.Type(context.referenceGcType(getRuntimeClass?.symbol ?: builtIns.anyClass))) WasmRefNullType(WasmHeapType.Type(context.referenceGcType(getRuntimeClass(context.backendContext.irBuiltIns).symbol)))
fun IrType.toBoxedInlineClassType(): WasmType = fun IrType.toBoxedInlineClassType(): WasmType =
toWasmGcRefType() toWasmGcRefType()
@@ -122,8 +122,5 @@ fun isBuiltInWasmRefType(type: IrType): Boolean {
fun isExternalType(type: IrType): Boolean = fun isExternalType(type: IrType): Boolean =
type.erasedUpperBound?.isExternal ?: false type.erasedUpperBound?.isExternal ?: false
val IrType.getRuntimeClass: IrClass? fun IrType.getRuntimeClass(irBuiltIns: IrBuiltIns): IrClass =
get() = erasedUpperBound.let { erasedUpperBound?.takeIf { !it.isInterface } ?: irBuiltIns.anyClass.owner
if (it?.isInterface == true) null
else it
}
@@ -6,13 +6,9 @@
package org.jetbrains.kotlin.backend.wasm.ir2wasm package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext 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.IrField
import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.*
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.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.wasm.ir.* import org.jetbrains.kotlin.wasm.ir.*
@@ -22,17 +18,20 @@ interface WasmBaseCodegenContext {
val scratchMemAddr: WasmSymbol<Int> val scratchMemAddr: WasmSymbol<Int>
fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunction> fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunction>
fun referenceGlobal(irField: IrFieldSymbol): WasmSymbol<WasmGlobal> fun referenceGlobalField(irField: IrFieldSymbol): WasmSymbol<WasmGlobal>
fun referenceGlobalVTable(irClass: IrClassSymbol): WasmSymbol<WasmGlobal>
fun referenceGlobalClassITable(irClass: IrClassSymbol): WasmSymbol<WasmGlobal>
fun referenceGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration> fun referenceGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration>
fun referenceVTableGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration>
fun referenceClassITableGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration>
fun defineClassITableGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration)
fun isAlreadyDefinedClassITableGcType(irClass: IrClassSymbol): Boolean
fun referenceClassITableInterfaceSlot(irClass: IrClassSymbol): WasmSymbol<Int>
fun defineClassITableInterfaceSlot(irClass: IrClassSymbol, slot: Int)
fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType>
fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int> fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int>
fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int> fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int>
fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol<Int>
fun referenceSignatureId(signature: WasmSignature): WasmSymbol<Int>
fun referenceInterfaceTable(irFunction: IrFunctionSymbol): WasmSymbol<WasmTable>
fun referenceStringLiteral(string: String): WasmSymbol<Int> fun referenceStringLiteral(string: String): WasmSymbol<Int>
@@ -47,4 +46,5 @@ interface WasmBaseCodegenContext {
fun getStructFieldRef(field: IrField): WasmSymbol<Int> fun getStructFieldRef(field: IrField): WasmSymbol<Int>
fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata
fun getInterfaceMetadata(irClass: IrClassSymbol): InterfaceMetadata
} }
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.wasm.ir2wasm 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.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
@@ -17,24 +16,30 @@ import org.jetbrains.kotlin.wasm.ir.*
class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val functions = val functions =
ReferencableAndDefinable<IrFunctionSymbol, WasmFunction>() ReferencableAndDefinable<IrFunctionSymbol, WasmFunction>()
val globals = val globalFields =
ReferencableAndDefinable<IrFieldSymbol, WasmGlobal>() ReferencableAndDefinable<IrFieldSymbol, WasmGlobal>()
val globalVTables =
ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
val globalClassITables =
ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
val functionTypes = val functionTypes =
ReferencableAndDefinable<IrFunctionSymbol, WasmFunctionType>() ReferencableAndDefinable<IrFunctionSymbol, WasmFunctionType>()
val gcTypes = val gcTypes =
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>() ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val vTableGcTypes =
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val classITableGcType =
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val classITableInterfaceSlot =
ReferencableAndDefinable<IrClassSymbol, Int>()
val classIds = val classIds =
ReferencableElements<IrClassSymbol, Int>() ReferencableElements<IrClassSymbol, Int>()
val interfaceId = val interfaceId =
ReferencableElements<IrClassSymbol, Int>() ReferencableElements<IrClassSymbol, Int>()
val virtualFunctionId =
ReferencableElements<IrFunctionSymbol, Int>()
val signatureId =
ReferencableElements<WasmSignature, Int>()
val stringLiteralId = val stringLiteralId =
ReferencableElements<String, Int>() ReferencableElements<String, Int>()
val tagFuncType = WasmFunctionType( private val tagFuncType = WasmFunctionType(
listOf( listOf(
WasmRefNullType(WasmHeapType.Type(gcTypes.reference(irBuiltIns.throwableClass))) WasmRefNullType(WasmHeapType.Type(gcTypes.reference(irBuiltIns.throwableClass)))
), ),
@@ -42,34 +47,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
) )
val tag = WasmTag(tagFuncType) val tag = WasmTag(tagFuncType)
val typeInfo = ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>()
val classes = mutableListOf<IrClassSymbol>()
val interfaces = mutableListOf<IrClassSymbol>()
val virtualFunctions = mutableListOf<IrSimpleFunctionSymbol>()
val signatures = LinkedHashSet<WasmSignature>()
val typeInfo =
ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>()
// Wasm table for each method of each interface.
val interfaceMethodTables =
ReferencableAndDefinable<IrFunctionSymbol, WasmTable>()
// Defined class interface tables
val definedClassITableData =
ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>()
// Address of class interface table in linear memory
val referencedClassITableAddresses =
ReferencableElements<IrClassSymbol, Int>()
// Sequential number of an implementation (class, object, etc.) for a particular interface
// Used as index in table for interface method dispatch
val referencedInterfaceImplementationId =
ReferencableElements<InterfaceImplementation, Int>()
val interfaceImplementationsMethods =
LinkedHashMap<InterfaceImplementation, Map<IrFunctionSymbol, WasmSymbol<WasmFunction>?>>()
val exports = mutableListOf<WasmExport<*>>() val exports = mutableListOf<WasmExport<*>>()
@@ -82,7 +60,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val initFunctions = mutableListOf<FunWithPriority>() val initFunctions = mutableListOf<FunWithPriority>()
val scratchMemAddr = WasmSymbol<Int>() val scratchMemAddr = WasmSymbol<Int>()
val scratchMemSizeInBytes = 65_536 private val scratchMemSizeInBytes = 65_536
open class ReferencableElements<Ir, Wasm : Any> { open class ReferencableElements<Ir, Wasm : Any> {
val unbound = mutableMapOf<Ir, WasmSymbol<Wasm>>() val unbound = mutableMapOf<Ir, WasmSymbol<Wasm>>()
@@ -117,9 +95,13 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
fun linkWasmCompiledFragments(): WasmModule { fun linkWasmCompiledFragments(): WasmModule {
bind(functions.unbound, functions.defined) 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(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 // Associate function types to a single canonical function type
val canonicalFunctionTypes = val canonicalFunctionTypes =
@@ -139,13 +121,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
currentDataSectionAddress += typeInfoElement.sizeInBytes currentDataSectionAddress += typeInfoElement.sizeInBytes
} }
val interfaceTableAddresses = mutableMapOf<IrClassSymbol, Int>()
for (typeInfoElement in definedClassITableData.elements) {
val ir = definedClassITableData.wasmToIr.getValue(typeInfoElement)
interfaceTableAddresses[ir] = currentDataSectionAddress
currentDataSectionAddress += typeInfoElement.sizeInBytes
}
val stringDataSectionStart = currentDataSectionAddress val stringDataSectionStart = currentDataSectionAddress
val stringDataSectionBytes = mutableListOf<Byte>() val stringDataSectionBytes = mutableListOf<Byte>()
val stringAddrs = mutableMapOf<String, Int>() val stringAddrs = mutableMapOf<String, Int>()
@@ -163,107 +138,12 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
currentDataSectionAddress += scratchMemSizeInBytes currentDataSectionAddress += scratchMemSizeInBytes
bind(classIds.unbound, klassIds) bind(classIds.unbound, klassIds)
bind(referencedClassITableAddresses.unbound, interfaceTableAddresses)
bind(stringLiteralId.unbound, stringAddrs) bind(stringLiteralId.unbound, stringAddrs)
bindIndices(virtualFunctionId.unbound, virtualFunctions) interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) }
bindIndices(signatureId.unbound, signatures.toList())
bindIndices(interfaceId.unbound, interfaces)
val interfaceImplementationIds = mutableMapOf<InterfaceImplementation, Int>()
val numberOfInterfaceImpls = mutableMapOf<IrClassSymbol, Int>()
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)
val data = typeInfo.buildData(address = { klassIds.getValue(it) }) + val data = typeInfo.buildData(address = { klassIds.getValue(it) }) +
definedClassITableData.buildData(address = { interfaceTableAddresses.getValue(it) }) +
WasmData(WasmDataMode.Active(0, stringDataSectionStart), stringDataSectionBytes.toByteArray()) 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<WasmInstr>()
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<Int, WasmSymbol<WasmFunction>?>()
}
for ((ii: InterfaceImplementation, implId: Int) in interfaceImplementationIds) {
for ((interfaceFunction: IrFunctionSymbol, wasmFunction: WasmSymbol<WasmFunction>?) 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 masterInitFunctionType = WasmFunctionType(emptyList(), emptyList())
val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType)) val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType))
with(WasmIrExpressionBuilder(masterInitFunction.instructions)) { with(WasmIrExpressionBuilder(masterInitFunction.instructions)) {
@@ -273,11 +153,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
} }
exports += WasmExport.Function("__init", masterInitFunction) 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 typeInfoSize = currentDataSectionAddress
val memorySizeInPages = (typeInfoSize / 65_536) + 1 val memorySizeInPages = (typeInfoSize / 65_536) + 1
val memory = WasmMemory(WasmLimits(memorySizeInPages.toUInt(), memorySizeInPages.toUInt())) 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<WasmTypeDeclaration>()
typeDeclarations.addAll(vTableGcTypes.elements)
typeDeclarations.addAll(gcTypes.elements)
typeDeclarations.addAll(classITableGcType.elements.distinct())
typeDeclarations.sortBy(::wasmTypeDeclarationOrderKey)
val globals = mutableListOf<WasmGlobal>()
globals.addAll(globalFields.elements)
globals.addAll(globalVTables.elements)
globals.addAll(globalClassITables.elements.distinct())
val module = WasmModule( val module = WasmModule(
functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType, functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType,
gcTypes = sortedGcTypes, gcTypes = typeDeclarations,
gcTypesInRecursiveGroup = true, gcTypesInRecursiveGroup = true,
importsInOrder = importedFunctions, importsInOrder = importedFunctions,
importedFunctions = importedFunctions, importedFunctions = importedFunctions,
definedFunctions = functions.elements.filterIsInstance<WasmFunction.Defined>() + masterInitFunction, definedFunctions = functions.elements.filterIsInstance<WasmFunction.Defined>() + masterInitFunction,
tables = listOf(table) + interfaceMethodTables.elements, tables = emptyList(),
memories = listOf(memory), memories = listOf(memory),
globals = globals.elements, globals = globals,
exports = exports, exports = exports,
startFunction = null, // Module is initialized via export call startFunction = null, // Module is initialized via export call
elements = listOf(elements) + interfaceTableElements, elements = emptyList(),
data = data, data = data,
tags = listOf(tag) tags = listOf(tag)
) )
@@ -338,18 +222,6 @@ private fun irSymbolDebugDump(symbol: Any?): String =
else -> symbol.toString() else -> symbol.toString()
} }
fun <IrSymbolType> bindIndices(
unbound: Map<IrSymbolType, WasmSymbol<Int>>,
ordered: List<IrSymbolType>
) {
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<IrClassSymbol, ConstantDataElement>.buildData(address: (IrClassSymbol) -> Int): List<WasmData> { inline fun WasmCompiledModuleFragment.ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>.buildData(address: (IrClassSymbol) -> Int): List<WasmData> {
return elements.map { return elements.map {
val id = address(wasmToIr.getValue(it)) val id = address(wasmToIr.getValue(it))
@@ -359,11 +231,6 @@ inline fun WasmCompiledModuleFragment.ReferencableAndDefinable<IrClassSymbol, Co
} }
} }
data class InterfaceImplementation(
val irInterface: IrClassSymbol,
val irClass: IrClassSymbol
)
fun alignUp(x: Int, alignment: Int): Int { fun alignUp(x: Int, alignment: Int): Int {
assert(alignment and (alignment - 1) == 0) { "power of 2 expected" } assert(alignment and (alignment - 1) == 0) { "power of 2 expected" }
return (x + alignment - 1) and (alignment - 1).inv() return (x + alignment - 1) and (alignment - 1).inv()
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.wasm.ir.WasmInstr
import org.jetbrains.kotlin.wasm.ir.WasmLocal import org.jetbrains.kotlin.wasm.ir.WasmLocal
enum class LoopLabelType { BREAK, CONTINUE } enum class LoopLabelType { BREAK, CONTINUE }
enum class SyntheticLocalType { IS_INTERFACE_PARAMETER }
interface WasmFunctionCodegenContext : WasmBaseCodegenContext { interface WasmFunctionCodegenContext : WasmBaseCodegenContext {
val irFunction: IrFunction val irFunction: IrFunction
@@ -20,6 +21,7 @@ interface WasmFunctionCodegenContext : WasmBaseCodegenContext {
fun defineLocal(irValueDeclaration: IrValueSymbol) fun defineLocal(irValueDeclaration: IrValueSymbol)
fun referenceLocal(irValueDeclaration: IrValueSymbol): WasmLocal fun referenceLocal(irValueDeclaration: IrValueSymbol): WasmLocal
fun referenceLocal(index: Int): WasmLocal fun referenceLocal(index: Int): WasmLocal
fun referenceLocal(type: SyntheticLocalType): WasmLocal
fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int) fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int)
fun referenceLoopLevel(irLoop: IrLoop, labelType: LoopLabelType): Int fun referenceLoopLevel(irLoop: IrLoop, labelType: LoopLabelType): Int
@@ -27,6 +27,7 @@ class WasmFunctionCodegenContextImpl(
get() = 0 get() = 0
private val wasmLocals = LinkedHashMap<IrValueSymbol, WasmLocal>() private val wasmLocals = LinkedHashMap<IrValueSymbol, WasmLocal>()
private val wasmSyntheticLocals = LinkedHashMap<SyntheticLocalType, WasmLocal>()
private val loopLevels = LinkedHashMap<Pair<IrLoop, LoopLabelType>, Int>() private val loopLevels = LinkedHashMap<Pair<IrLoop, LoopLabelType>, Int>()
override fun defineLocal(irValueDeclaration: IrValueSymbol) { override fun defineLocal(irValueDeclaration: IrValueSymbol) {
@@ -52,6 +53,17 @@ class WasmFunctionCodegenContextImpl(
return wasmFunction.locals[index] 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) { override fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int) {
val loopKey = Pair(irLoop, labelType) val loopKey = Pair(irLoop, labelType)
assert(loopKey !in loopLevels) { "Redefinition of loop" } assert(loopKey !in loopLevels) { "Redefinition of loop" }
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.backend.wasm.ir2wasm package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.*
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.wasm.ir.* import org.jetbrains.kotlin.wasm.ir.*
/** /**
@@ -16,27 +13,16 @@ import org.jetbrains.kotlin.wasm.ir.*
*/ */
interface WasmModuleCodegenContext : WasmBaseCodegenContext { interface WasmModuleCodegenContext : WasmBaseCodegenContext {
fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) 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 defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration)
fun defineVTableGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration)
fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType)
fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable)
fun addJsFun(importName: String, jsCode: String) fun addJsFun(importName: String, jsCode: String)
fun registerInitFunction(wasmFunction: WasmFunction, priority: String) fun registerInitFunction(wasmFunction: WasmFunction, priority: String)
fun addExport(wasmExport: WasmExport<*>) fun addExport(wasmExport: WasmExport<*>)
fun registerVirtualFunction(irFunction: IrSimpleFunctionSymbol)
fun registerInterface(irInterface: IrClassSymbol)
fun registerClass(irClass: IrClassSymbol)
fun generateTypeInfo(irClass: IrClassSymbol, typeInfo: ConstantDataElement) fun generateTypeInfo(irClass: IrClassSymbol, typeInfo: ConstantDataElement)
fun generateInterfaceTable(irClass: IrClassSymbol, table: ConstantDataElement)
fun registerInterfaceImplementationMethod(
interfaceImplementation: InterfaceImplementation,
table: Map<IrFunctionSymbol, WasmSymbol<WasmFunction>?>,
)
fun referenceInterfaceImplementationId(interfaceImplementation: InterfaceImplementation): WasmSymbol<Int>
fun referenceInterfaceTableAddress(irClass: IrClassSymbol): WasmSymbol<Int>
} }
@@ -6,19 +6,12 @@
package org.jetbrains.kotlin.backend.wasm.ir2wasm package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext 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.IrField
import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.*
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.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType 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.types.isNothing
import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.wasm.ir.* import org.jetbrains.kotlin.wasm.ir.*
@@ -70,10 +63,6 @@ class WasmModuleCodegenContextImpl(
wasmFragment.typeInfo.define(irClass, typeInfo) wasmFragment.typeInfo.define(irClass, typeInfo)
} }
override fun generateInterfaceTable(irClass: IrClassSymbol, table: ConstantDataElement) {
wasmFragment.definedClassITableData.define(irClass, table)
}
override fun registerInitFunction(wasmFunction: WasmFunction, priority: String) { override fun registerInitFunction(wasmFunction: WasmFunction, priority: String) {
wasmFragment.initFunctions += WasmCompiledModuleFragment.FunWithPriority(wasmFunction, priority) wasmFragment.initFunctions += WasmCompiledModuleFragment.FunWithPriority(wasmFunction, priority)
} }
@@ -82,51 +71,34 @@ class WasmModuleCodegenContextImpl(
wasmFragment.exports += wasmExport 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) { override fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) {
wasmFragment.functions.define(irFunction, wasmFunction) wasmFragment.functions.define(irFunction, wasmFunction)
} }
override fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) { override fun defineGlobalField(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) {
wasmFragment.globals.define(irField, 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) { override fun defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) {
wasmFragment.gcTypes.define(irClass, wasmType) wasmFragment.gcTypes.define(irClass, wasmType)
} }
override fun defineVTableGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) {
wasmFragment.vTableGcTypes.define(irClass, wasmType)
}
override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) { override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) {
wasmFragment.functionTypes.define(irFunction, wasmFunctionType) wasmFragment.functionTypes.define(irFunction, wasmFunctionType)
} }
override fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable) {
wasmFragment.interfaceMethodTables.define(irFunction, wasmTable)
}
override fun referenceInterfaceImplementationId(
interfaceImplementation: InterfaceImplementation
): WasmSymbol<Int> =
wasmFragment.referencedInterfaceImplementationId.reference(interfaceImplementation)
override fun registerInterfaceImplementationMethod(
interfaceImplementation: InterfaceImplementation,
table: Map<IrFunctionSymbol, WasmSymbol<WasmFunction>?>
) {
wasmFragment.interfaceImplementationsMethods[interfaceImplementation] = table
}
private val classMetadataCache = mutableMapOf<IrClassSymbol, ClassMetadata>() private val classMetadataCache = mutableMapOf<IrClassSymbol, ClassMetadata>()
override fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata = override fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata =
classMetadataCache.getOrPut(irClass) { classMetadataCache.getOrPut(irClass) {
@@ -139,18 +111,59 @@ class WasmModuleCodegenContextImpl(
) )
} }
private val interfaceMetadataCache = mutableMapOf<IrClassSymbol, InterfaceMetadata>()
override fun getInterfaceMetadata(irClass: IrClassSymbol): InterfaceMetadata =
interfaceMetadataCache.getOrPut(irClass) { InterfaceMetadata(irClass.owner, backendContext.irBuiltIns) }
override fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunction> = override fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunction> =
wasmFragment.functions.reference(irFunction) wasmFragment.functions.reference(irFunction)
override fun referenceGlobal(irField: IrFieldSymbol): WasmSymbol<WasmGlobal> = override fun referenceGlobalField(irField: IrFieldSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.globals.reference(irField) wasmFragment.globalFields.reference(irField)
override fun referenceGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration> { override fun referenceGlobalVTable(irClass: IrClassSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.globalVTables.reference(irClass)
override fun referenceGlobalClassITable(irClass: IrClassSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.globalClassITables.reference(irClass)
private fun referenceNonNothingType(
irClass: IrClassSymbol,
from: WasmCompiledModuleFragment.ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>
): WasmSymbol<WasmTypeDeclaration> {
val type = irClass.defaultType val type = irClass.defaultType
require(!type.isNothing()) { require(!type.isNothing()) {
"Can't reference Nothing type" "Can't reference Nothing type"
} }
return wasmFragment.gcTypes.reference(irClass) return from.reference(irClass)
}
override fun referenceGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration> =
referenceNonNothingType(irClass, wasmFragment.gcTypes)
override fun referenceVTableGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration> =
referenceNonNothingType(irClass, wasmFragment.vTableGcTypes)
override fun referenceClassITableGcType(irClass: IrClassSymbol): WasmSymbol<WasmTypeDeclaration> =
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<Int> {
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<WasmFunctionType> = override fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> =
@@ -159,35 +172,14 @@ class WasmModuleCodegenContextImpl(
override fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int> = override fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int> =
wasmFragment.classIds.reference(irClass) wasmFragment.classIds.reference(irClass)
override fun referenceInterfaceTableAddress(irClass: IrClassSymbol): WasmSymbol<Int> {
if (irClass.owner.modality == Modality.ABSTRACT) return WasmSymbol(-1)
return wasmFragment.referencedClassITableAddresses.reference(irClass)
}
override fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int> { override fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int> {
return wasmFragment.interfaceId.reference(irInterface) return wasmFragment.interfaceId.reference(irInterface)
} }
override fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol<Int> {
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<Int> {
wasmFragment.signatures.add(signature)
return wasmFragment.signatureId.reference(signature)
}
override fun referenceInterfaceTable(irFunction: IrFunctionSymbol): WasmSymbol<WasmTable> {
return wasmFragment.interfaceMethodTables.reference(irFunction)
}
override fun getStructFieldRef(field: IrField): WasmSymbol<Int> { override fun getStructFieldRef(field: IrField): WasmSymbol<Int> {
val klass = field.parentAsClass val klass = field.parentAsClass
val metadata = getClassMetadata(klass.symbol) 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) return WasmSymbol(fieldId)
} }
@@ -195,4 +187,5 @@ class WasmModuleCodegenContextImpl(
wasmFragment.jsFuns += wasmFragment.jsFuns +=
WasmCompiledModuleFragment.JsCodeSnippet(importName = importName, jsCode = jsCode) WasmCompiledModuleFragment.JsCodeSnippet(importName = importName, jsCode = jsCode)
} }
} }
@@ -6,9 +6,16 @@
package org.jetbrains.kotlin.backend.wasm.ir2wasm package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext 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.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 import org.jetbrains.kotlin.ir.visitors.acceptVoid
class WasmModuleFragmentGenerator( class WasmModuleFragmentGenerator(
@@ -16,28 +23,49 @@ class WasmModuleFragmentGenerator(
wasmModuleFragment: WasmCompiledModuleFragment, wasmModuleFragment: WasmCompiledModuleFragment,
allowIncompleteImplementations: Boolean, allowIncompleteImplementations: Boolean,
) { ) {
private val hierarchyDisjointUnions = DisjointUnions<IrClassSymbol>()
private val declarationGenerator = private val declarationGenerator =
DeclarationGenerator( DeclarationGenerator(
WasmModuleCodegenContextImpl( WasmModuleCodegenContextImpl(
backendContext, backendContext,
wasmModuleFragment, 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) { fun generateModule(irModuleFragment: IrModuleFragment) {
acceptVisitor(irModuleFragment, declarationGenerator)
}
private fun acceptVisitor(irModuleFragment: IrModuleFragment, visitor: IrElementVisitorVoid) {
for (irFile in irModuleFragment.files) { 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)
}
} }
@@ -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.at
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext 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.IrElement
import org.jetbrains.kotlin.ir.builders.irSetField import org.jetbrains.kotlin.ir.builders.irSetField
import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.IrField
@@ -101,9 +101,6 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
private val IrType.eraseToClassOrInterface: IrClass private val IrType.eraseToClassOrInterface: IrClass
get() = this.erasedUpperBound ?: builtIns.anyClass.owner get() = this.erasedUpperBound ?: builtIns.anyClass.owner
private val IrType.eraseToClass: IrClass
get() = this.getRuntimeClass ?: builtIns.anyClass.owner
private fun generateTypeCheck( private fun generateTypeCheck(
valueProvider: () -> IrExpression, valueProvider: () -> IrExpression,
toType: IrType toType: IrType
@@ -302,19 +299,16 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
} }
private fun generateIsInterface(argument: IrExpression, toType: IrType): IrExpression { private fun generateIsInterface(argument: IrExpression, toType: IrType): IrExpression {
val interfaceId = builder.irCall(symbols.wasmInterfaceId).apply {
putTypeArgument(0, toType)
}
return builder.irCall(symbols.isInterface).apply { return builder.irCall(symbols.isInterface).apply {
putValueArgument(0, argument) putValueArgument(0, argument)
putValueArgument(1, interfaceId) putTypeArgument(0, toType)
} }
} }
private fun generateIsSubClass(argument: IrExpression, toType: IrType): IrExpression { private fun generateIsSubClass(argument: IrExpression, toType: IrType): IrExpression {
val fromType = argument.type val fromType = argument.type
val fromTypeErased = fromType.eraseToClass val fromTypeErased = fromType.getRuntimeClass(context.irBuiltIns)
val toTypeErased = toType.eraseToClass val toTypeErased = toType.getRuntimeClass(context.irBuiltIns)
if (fromTypeErased.isSubclassOf(toTypeErased)) { if (fromTypeErased.isSubclassOf(toTypeErased)) {
return builder.irTrue() return builder.irTrue()
} }
@@ -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<T> {
private val leafParents = mutableMapOf<T, Node>()
private var dirty: Boolean = false
private inner class Node(var rank: Int = 0, val leafs: MutableList<T> = 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<T>) {
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<T> {
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
}
}
@@ -5,7 +5,9 @@
package org.jetbrains.kotlin.backend.wasm.utils package org.jetbrains.kotlin.backend.wasm.utils
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.expressions.IrTry import org.jetbrains.kotlin.ir.expressions.IrTry
// Backed codegen can only handle try/catch in canonical form. // Backed codegen can only handle try/catch in canonical form.
@@ -16,6 +18,9 @@ import org.jetbrains.kotlin.ir.expressions.IrTry
// ...exprs // ...exprs
// } // }
// no-finally // no-finally
fun IrTry.isCanonical(builtIns: IrBuiltIns) = internal fun IrTry.isCanonical(builtIns: IrBuiltIns) =
catches.singleOrNull()?.catchParameter?.symbol?.owner?.type == builtIns.throwableType && catches.singleOrNull()?.catchParameter?.symbol?.owner?.type == builtIns.throwableType &&
finallyExpression == null finallyExpression == null
internal val IrClass.isAbstractOrSealed
get() = modality == Modality.ABSTRACT || modality == Modality.SEALED
@@ -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_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_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_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_ITABLE_SIZE_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_ITABLE_OFFSET = TYPE_INFO_ITABLE_SIZE_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_VTABLE_OFFSET = TYPE_INFO_VTABLE_LENGTH_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal class TypeInfoData(val typeId: Int, val isInterface: Boolean, val packageName: String, val typeName: String) 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 = internal fun getSuperTypeId(typeInfoPtr: Int): Int =
wasm_i32_load(typeInfoPtr + TYPE_INFO_SUPER_TYPE_OFFSET) wasm_i32_load(typeInfoPtr + TYPE_INFO_SUPER_TYPE_OFFSET)
internal fun getVtablePtr(obj: Any): Int = internal fun isInterfaceById(obj: Any, interfaceId: Int): Boolean {
obj.typeInfo + TYPE_INFO_VTABLE_OFFSET val interfaceListSize = wasm_i32_load(obj.typeInfo + TYPE_INFO_ITABLE_SIZE_OFFSET)
val interfaceListPtr = obj.typeInfo + TYPE_INFO_ITABLE_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)
var interfaceSlot = 0 var interfaceSlot = 0
while (interfaceSlot < interfaceListSize) { while (interfaceSlot < interfaceListSize) {
val supportedInterface = wasm_i32_load(interfaceListPtr + interfaceSlot * TYPE_INFO_ELEMENT_SIZE) val supportedInterface = wasm_i32_load(interfaceListPtr + interfaceSlot * TYPE_INFO_ELEMENT_SIZE)
if (supportedInterface == interfaceId) { if (supportedInterface == interfaceId) {
return wasm_i32_load(interfaceListPtr + interfaceListSize * TYPE_INFO_ELEMENT_SIZE + interfaceSlot * TYPE_INFO_ELEMENT_SIZE) return true
} }
interfaceSlot++ interfaceSlot++
} }
return false
return -1
} }
internal fun isInterface(obj: Any, interfaceId: Int): Boolean { @ExcludedFromCodegen
return getInterfaceImplId(obj, interfaceId) != -1 internal fun <T> isInterface(obj: Any): Boolean =
} implementedAsIntrinsic
@ExcludedFromCodegen @ExcludedFromCodegen
internal fun <T> wasmClassId(): Int = internal fun <T> wasmClassId(): Int =
@@ -7,7 +7,7 @@ package kotlin.reflect.wasm.internal
import kotlin.reflect.* import kotlin.reflect.*
import kotlin.wasm.internal.TypeInfoData import kotlin.wasm.internal.TypeInfoData
import kotlin.wasm.internal.getSuperTypeId import kotlin.wasm.internal.getSuperTypeId
import kotlin.wasm.internal.isInterface import kotlin.wasm.internal.isInterfaceById
internal object NothingKClassImpl : KClass<Nothing> { internal object NothingKClassImpl : KClass<Nothing> {
override val simpleName: String = "Nothing" override val simpleName: String = "Nothing"
@@ -38,7 +38,7 @@ internal class KClassImpl<T : Any>(private val typeData: TypeInfoData) : KClass<
} }
override fun isInstance(value: Any?): Boolean = value?.let { 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 } ?: false
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
@@ -374,6 +374,7 @@ enum class WasmOp(
BR_ON_CAST("br_on_cast", 0xFB_42, listOf(LABEL_IDX)), 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_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_FUNC("ref.is_func", 0xfb50),
REF_IS_DATA("ref.is_data", 0xfb51), REF_IS_DATA("ref.is_data", 0xfb51),
@@ -34,15 +34,19 @@ abstract class WasmExpressionBuilder {
} }
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
fun buildBlock(label: String?, resultType: WasmType? = null) { inline fun buildBlock(label: String?, resultType: WasmType? = null, body: (Int) -> Unit) {
numberOfNestedBlocks++ numberOfNestedBlocks++
buildInstr(WasmOp.BLOCK, WasmImmediate.BlockType.Value(resultType)) buildInstr(WasmOp.BLOCK, WasmImmediate.BlockType.Value(resultType))
body(numberOfNestedBlocks)
buildEnd()
} }
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
fun buildLoop(label: String?, resultType: WasmType? = null) { inline fun buildLoop(label: String?, resultType: WasmType? = null, body: (Int) -> Unit) {
numberOfNestedBlocks++ numberOfNestedBlocks++
buildInstr(WasmOp.LOOP, WasmImmediate.BlockType.Value(resultType)) buildInstr(WasmOp.LOOP, WasmImmediate.BlockType.Value(resultType))
body(numberOfNestedBlocks)
buildEnd()
} }
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
@@ -60,10 +64,21 @@ abstract class WasmExpressionBuilder {
buildInstr(WasmOp.END) buildInstr(WasmOp.END)
} }
fun buildBr(absoluteBlockLevel: Int) {
fun buildBrInstr(brOp: WasmOp, absoluteBlockLevel: Int) {
val relativeLevel = numberOfNestedBlocks - absoluteBlockLevel val relativeLevel = numberOfNestedBlocks - absoluteBlockLevel
assert(relativeLevel >= 0) { "Negative relative block index" } 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<WasmTypeDeclaration>) {
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) { fun buildThrow(tagIdx: Int) {
@@ -81,9 +96,7 @@ abstract class WasmExpressionBuilder {
} }
fun buildBrIf(absoluteBlockLevel: Int) { fun buildBrIf(absoluteBlockLevel: Int) {
val relativeLevel = numberOfNestedBlocks - absoluteBlockLevel buildBrInstr(WasmOp.BR_IF, absoluteBlockLevel)
assert(relativeLevel >= 0) { "Negative relative block index" }
buildInstr(WasmOp.BR_IF, WasmImmediate.LabelIdx(relativeLevel))
} }
fun buildCall(symbol: WasmSymbol<WasmFunction>) { fun buildCall(symbol: WasmSymbol<WasmFunction>) {
@@ -126,7 +139,7 @@ abstract class WasmExpressionBuilder {
} }
fun buildStructNew(struct: WasmSymbol<WasmTypeDeclaration>) { fun buildStructNew(struct: WasmSymbol<WasmTypeDeclaration>) {
buildInstr(WasmOp.STRUCT_NEW_WITH_RTT, WasmImmediate.GcType(struct)) buildInstr(WasmOp.STRUCT_NEW, WasmImmediate.GcType(struct))
} }
fun buildStructSet(struct: WasmSymbol<WasmTypeDeclaration>, fieldId: WasmSymbol<Int>) { fun buildStructSet(struct: WasmSymbol<WasmTypeDeclaration>, fieldId: WasmSymbol<Int>) {
@@ -142,8 +155,8 @@ abstract class WasmExpressionBuilder {
buildInstr(WasmOp.REF_CAST) buildInstr(WasmOp.REF_CAST)
} }
fun buildRefCastStatic(type: WasmSymbolReadOnly<WasmTypeDeclaration>) { fun buildRefCastStatic(toType: WasmSymbolReadOnly<WasmTypeDeclaration>) {
buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(type)) buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(toType))
} }
fun buildRefNull(type: WasmHeapType) { fun buildRefNull(type: WasmHeapType) {
@@ -21,7 +21,6 @@ class WasmIrExpressionBuilder(
} }
} }
override var numberOfNestedBlocks: Int = 0 override var numberOfNestedBlocks: Int = 0
set(value) { set(value) {
assert(value >= 0) { "end without matching block" } assert(value >= 0) { "end without matching block" }