[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 wasmInterfaceId = getInternalFunction("wasmInterfaceId")
val getVirtualMethodId = getInternalFunction("getVirtualMethodId")
val getInterfaceImplId = getInternalFunction("getInterfaceImplId")
val isInterface = getInternalFunction("isInterface")
val nullableEquals = getInternalFunction("nullableEquals")
@@ -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()
@@ -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()
@@ -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<IrClassSymbol>
) : 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<IrField> = 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
}
@@ -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<IrClassSymbol>,
) : 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<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) {
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<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 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)
}
}
@@ -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
@@ -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<Int>
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 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 referenceClassId(irClass: 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>
@@ -47,4 +46,5 @@ interface WasmBaseCodegenContext {
fun getStructFieldRef(field: IrField): WasmSymbol<Int>
fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata
fun getInterfaceMetadata(irClass: IrClassSymbol): InterfaceMetadata
}
@@ -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<IrFunctionSymbol, WasmFunction>()
val globals =
val globalFields =
ReferencableAndDefinable<IrFieldSymbol, WasmGlobal>()
val globalVTables =
ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
val globalClassITables =
ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
val functionTypes =
ReferencableAndDefinable<IrFunctionSymbol, WasmFunctionType>()
val gcTypes =
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val vTableGcTypes =
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val classITableGcType =
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val classITableInterfaceSlot =
ReferencableAndDefinable<IrClassSymbol, Int>()
val classIds =
ReferencableElements<IrClassSymbol, Int>()
val interfaceId =
ReferencableElements<IrClassSymbol, Int>()
val virtualFunctionId =
ReferencableElements<IrFunctionSymbol, Int>()
val signatureId =
ReferencableElements<WasmSignature, Int>()
val stringLiteralId =
ReferencableElements<String, Int>()
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<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 typeInfo = ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>()
val exports = mutableListOf<WasmExport<*>>()
@@ -82,7 +60,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val initFunctions = mutableListOf<FunWithPriority>()
val scratchMemAddr = WasmSymbol<Int>()
val scratchMemSizeInBytes = 65_536
private val scratchMemSizeInBytes = 65_536
open class ReferencableElements<Ir, Wasm : Any> {
val unbound = mutableMapOf<Ir, WasmSymbol<Wasm>>()
@@ -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<IrClassSymbol, Int>()
for (typeInfoElement in definedClassITableData.elements) {
val ir = definedClassITableData.wasmToIr.getValue(typeInfoElement)
interfaceTableAddresses[ir] = currentDataSectionAddress
currentDataSectionAddress += typeInfoElement.sizeInBytes
}
val stringDataSectionStart = currentDataSectionAddress
val stringDataSectionBytes = mutableListOf<Byte>()
val stringAddrs = mutableMapOf<String, Int>()
@@ -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<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)
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<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 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<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(
functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType,
gcTypes = sortedGcTypes,
gcTypes = typeDeclarations,
gcTypesInRecursiveGroup = true,
importsInOrder = importedFunctions,
importedFunctions = importedFunctions,
definedFunctions = functions.elements.filterIsInstance<WasmFunction.Defined>() + 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 <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> {
return elements.map {
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 {
assert(alignment and (alignment - 1) == 0) { "power of 2 expected" }
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
enum class LoopLabelType { BREAK, CONTINUE }
enum class SyntheticLocalType { IS_INTERFACE_PARAMETER }
interface WasmFunctionCodegenContext : WasmBaseCodegenContext {
val irFunction: IrFunction
@@ -20,6 +21,7 @@ interface WasmFunctionCodegenContext : WasmBaseCodegenContext {
fun defineLocal(irValueDeclaration: IrValueSymbol)
fun referenceLocal(irValueDeclaration: IrValueSymbol): WasmLocal
fun referenceLocal(index: Int): WasmLocal
fun referenceLocal(type: SyntheticLocalType): WasmLocal
fun defineLoopLevel(irLoop: IrLoop, labelType: LoopLabelType, level: Int)
fun referenceLoopLevel(irLoop: IrLoop, labelType: LoopLabelType): Int
@@ -27,6 +27,7 @@ class WasmFunctionCodegenContextImpl(
get() = 0
private val wasmLocals = LinkedHashMap<IrValueSymbol, WasmLocal>()
private val wasmSyntheticLocals = LinkedHashMap<SyntheticLocalType, WasmLocal>()
private val loopLevels = LinkedHashMap<Pair<IrLoop, LoopLabelType>, 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" }
@@ -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<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
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<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>()
override fun getClassMetadata(irClass: IrClassSymbol): ClassMetadata =
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> =
wasmFragment.functions.reference(irFunction)
override fun referenceGlobal(irField: IrFieldSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.globals.reference(irField)
override fun referenceGlobalField(irField: IrFieldSymbol): WasmSymbol<WasmGlobal> =
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
require(!type.isNothing()) {
"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> =
@@ -159,35 +172,14 @@ class WasmModuleCodegenContextImpl(
override fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int> =
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> {
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> {
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)
}
}
}
@@ -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<IrClassSymbol>()
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)
}
}
@@ -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
@@ -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()
}
@@ -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
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
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_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 <T> isInterface(obj: Any): Boolean =
implementedAsIntrinsic
@ExcludedFromCodegen
internal fun <T> wasmClassId(): Int =
@@ -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<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 {
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 =
@@ -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),
@@ -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<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) {
@@ -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<WasmFunction>) {
@@ -126,7 +139,7 @@ abstract class WasmExpressionBuilder {
}
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>) {
@@ -142,8 +155,8 @@ abstract class WasmExpressionBuilder {
buildInstr(WasmOp.REF_CAST)
}
fun buildRefCastStatic(type: WasmSymbolReadOnly<WasmTypeDeclaration>) {
buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(type))
fun buildRefCastStatic(toType: WasmSymbolReadOnly<WasmTypeDeclaration>) {
buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(toType))
}
fun buildRefNull(type: WasmHeapType) {
@@ -21,7 +21,6 @@ class WasmIrExpressionBuilder(
}
}
override var numberOfNestedBlocks: Int = 0
set(value) {
assert(value >= 0) { "end without matching block" }