[Wasm] Support Wasm GC milestone 5

This commit is contained in:
Svyatoslav Kuzmich
2022-02-16 15:01:31 +03:00
parent c018270462
commit d1c81eb6ba
25 changed files with 140 additions and 115 deletions
@@ -45,7 +45,8 @@ internal class WasmUsefulDeclarationProcessor(
}
context.wasmSymbols.wasmClassId,
context.wasmSymbols.wasmInterfaceId,
context.wasmSymbols.wasmRefCast -> {
context.wasmSymbols.wasmRefCast,
context.wasmSymbols.refTest -> {
call.getTypeArgument(0)?.getClass()?.enqueue(from, "generic intrinsic ${call.symbol.owner.name}")
true
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
@@ -213,7 +214,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
generateDefaultInitializerForType(context.transformType(field.type), body)
}
body.buildGetGlobal(context.referenceClassRTT(klass.symbol))
generateClassRTT(klass.symbol)
body.buildStructNew(wasmGcType)
generateCall(expression)
}
@@ -243,7 +244,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildConstI32Symbol(klassId)
body.buildConstI32(0) // Any::_hashCode
generateExpression(call.getValueArgument(0)!!)
body.buildGetGlobal(context.referenceClassRTT(klass.symbol))
generateClassRTT(klass.symbol)
body.buildStructNew(structTypeName)
return
}
@@ -322,8 +323,12 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
}
private fun generateTypeRTT(type: IrType) {
val rtClass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass
body.buildGetGlobal(context.referenceClassRTT(rtClass))
val klass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass
generateClassRTT(klass)
}
private fun generateClassRTT(klass: IrClassSymbol) {
body.buildRttCanon(context.referenceGcType(klass))
}
// Return true if generated.
@@ -357,6 +362,12 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildRefCast()
}
wasmSymbols.refTest -> {
val toType = call.getTypeArgument(0)!!
generateTypeRTT(toType)
body.buildInstr(WasmOp.REF_TEST)
}
wasmSymbols.unboxIntrinsic -> {
val fromType = call.getTypeArgument(0)!!
@@ -581,6 +592,9 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
WasmImmediate.MemArg(0u, 0u)
WasmImmediateKind.STRUCT_TYPE_IDX ->
WasmImmediate.GcType(context.referenceGcType(function.dispatchReceiverParameter!!.type.classOrNull!!))
WasmImmediateKind.TYPE_IDX ->
WasmImmediate.TypeIdx(context.referenceGcType(function.dispatchReceiverParameter!!.type.classOrNull!!))
else ->
error("Immediate $imm is unsupported")
}
@@ -85,7 +85,6 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
val wasmFunctionType =
WasmFunctionType(
name = watName,
parameterTypes = irParameters.map { context.transformValueParameterType(it) },
resultTypes = listOfNotNull(resultType)
)
@@ -104,17 +103,19 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
"Sanity check that $declaration is a real function that can be used in calls"
}
val functionTypeSymbol = context.referenceFunctionType(declaration.symbol)
if (importedName != null) {
// Imported functions don't have bodies. Declaring the signature:
context.defineFunction(
declaration.symbol,
WasmFunction.Imported(watName, wasmFunctionType, importedName)
WasmFunction.Imported(watName, functionTypeSymbol, importedName)
)
// TODO: Support re-export of imported functions.
return
}
val function = WasmFunction.Defined(watName, wasmFunctionType)
val function = WasmFunction.Defined(watName, functionTypeSymbol)
val functionCodegenContext = WasmFunctionCodegenContextImpl(
declaration,
function,
@@ -200,6 +201,8 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
context.registerInterface(symbol)
} else {
val nameStr = declaration.fqNameWhenAvailable.toString()
val metadata = context.getClassMetadata(symbol)
val superClass = metadata.superClass
val structType = WasmStructDeclaration(
name = nameStr,
fields = declaration.allFields(irBuiltIns).map {
@@ -208,40 +211,11 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
type = context.transformFieldType(it.type),
isMutable = true
)
}
},
superClass?.let { context.referenceGcType(superClass.klass.symbol) }
)
context.defineGcType(symbol, structType)
var depth = 0
val metadata = context.getClassMetadata(symbol)
var subMetadata = metadata
while (true) {
subMetadata = subMetadata.superClass ?: break
depth++
}
val initBody = mutableListOf<WasmInstr>()
val wasmExpressionGenerator = WasmIrExpressionBuilder(initBody)
val wasmGcType = context.referenceGcType(symbol)
val superClass = metadata.superClass
if (superClass != null) {
val superRTT = context.referenceClassRTT(superClass.klass.symbol)
wasmExpressionGenerator.buildGetGlobal(superRTT)
wasmExpressionGenerator.buildRttSub(wasmGcType)
} else {
wasmExpressionGenerator.buildRttCanon(wasmGcType)
}
val rtt = WasmGlobal(
name = "rtt_of_$nameStr",
isMutable = false,
type = WasmRtt(depth, WasmSymbol(structType)),
init = initBody
)
context.defineRTT(symbol, rtt)
context.registerClass(symbol)
context.generateTypeInfo(symbol, binaryDataStruct(metadata))
@@ -394,7 +368,7 @@ fun generateDefaultInitializerForType(type: WasmType, g: WasmExpressionBuilder)
WasmF32 -> g.buildConstF32(0f)
WasmF64 -> g.buildConstF64(0.0)
is WasmRefNullType -> g.buildRefNull(type.heapType)
is WasmExternRef, is WasmAnyRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
is WasmAnyRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
WasmUnreachableType -> error("Unreachable type can't be initialized")
else -> error("Unknown value type ${type.name}")
}
@@ -82,11 +82,11 @@ class WasmTypeTransformer(
WasmF64
builtIns.nothingNType ->
WasmExternRef
WasmAnyRef
// Value will not be created. Just using a random Wasm type.
builtIns.nothingType ->
WasmExternRef
WasmAnyRef
symbols.voidType ->
error("Void type can't be used as a value")
@@ -29,7 +29,6 @@ interface WasmBaseCodegenContext {
fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int>
fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int>
fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol<Int>
fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal>
fun referenceSignatureId(signature: WasmSignature): WasmSymbol<Int>
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.wasm.ir2wasm
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
@@ -35,11 +34,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val stringLiteralId =
ReferencableElements<String, Int>()
val runtimeTypes =
ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
val tagFuncType = WasmFunctionType(
"ex_handling_tag",
listOf(
WasmRefNullType(WasmHeapType.Type(gcTypes.reference(irBuiltIns.throwableClass)))
),
@@ -123,9 +118,18 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
fun linkWasmCompiledFragments(): WasmModule {
bind(functions.unbound, functions.defined)
bind(globals.unbound, globals.defined)
bind(functionTypes.unbound, functionTypes.defined)
bind(gcTypes.unbound, gcTypes.defined)
bind(runtimeTypes.unbound, runtimeTypes.defined)
// Associate function types to a single canonical function type
val canonicalFunctionTypes =
functionTypes.elements.associateWithTo(LinkedHashMap()) { it }
functionTypes.unbound.forEach { (irSymbol, wasmSymbol) ->
if (irSymbol !in functionTypes.defined)
error("Can't link symbol ${irSymbolDebugDump(irSymbol)}")
wasmSymbol.bind(canonicalFunctionTypes.getValue(functionTypes.defined.getValue(irSymbol)))
}
val klassIds = mutableMapOf<IrClassSymbol, Int>()
var currentDataSectionAddress = 0
@@ -260,8 +264,8 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
)
}
val masterInitFunctionType = WasmFunctionType("__init_t", emptyList(), emptyList())
val masterInitFunction = WasmFunction.Defined("__init", masterInitFunctionType)
val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList())
val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType))
with(WasmIrExpressionBuilder(masterInitFunction.instructions)) {
initFunctions.sortedBy { it.priority }.forEach {
buildCall(WasmSymbol(it.function))
@@ -283,18 +287,28 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val importedFunctions = functions.elements.filterIsInstance<WasmFunction.Imported>()
// Sorting by depth for a valid init order
val sortedRttGlobals = runtimeTypes.elements.sortedBy { (it.type as WasmRtt).depth }
fun wasmTypeDeclarationOrderKey(declaration: WasmTypeDeclaration): Int {
return when (declaration) {
is WasmArrayDeclaration -> 0
is WasmFunctionType -> 0
is WasmStructDeclaration ->
// Subtype depth
declaration.superType?.let { wasmTypeDeclarationOrderKey(it.owner) + 1 } ?: 0
}
}
val sortedGcTypes = gcTypes.elements.sortedBy(::wasmTypeDeclarationOrderKey)
val module = WasmModule(
functionTypes = functionTypes.elements + tagFuncType + masterInitFunctionType,
gcTypes = gcTypes.elements,
functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType,
gcTypes = sortedGcTypes,
gcTypesInRecursiveGroup = true,
importsInOrder = importedFunctions,
importedFunctions = importedFunctions,
definedFunctions = functions.elements.filterIsInstance<WasmFunction.Defined>() + masterInitFunction,
tables = listOf(table) + interfaceMethodTables.elements,
memories = listOf(memory),
globals = globals.elements + sortedRttGlobals,
globals = globals.elements,
exports = exports,
startFunction = null, // Module is initialized via export call
elements = listOf(elements) + interfaceTableElements,
@@ -18,7 +18,6 @@ interface WasmModuleCodegenContext : WasmBaseCodegenContext {
fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction)
fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal)
fun defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration)
fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal)
fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType)
fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable)
fun addJsFun(importName: String, jsCode: String)
@@ -106,10 +106,6 @@ class WasmModuleCodegenContextImpl(
wasmFragment.gcTypes.define(irClass, wasmType)
}
override fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
wasmFragment.runtimeTypes.define(irClass, wasmGlobal)
}
override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) {
wasmFragment.functionTypes.define(irFunction, wasmFunctionType)
}
@@ -157,9 +153,6 @@ class WasmModuleCodegenContextImpl(
return wasmFragment.gcTypes.reference(irClass)
}
override fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.runtimeTypes.reference(irClass)
override fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> =
wasmFragment.functionTypes.reference(irFunction)
@@ -51,9 +51,6 @@ class GenericReturnTypeLowering(val context: WasmBackendContext) : FileLoweringP
val function: IrSimpleFunction =
call.symbol.owner as? IrSimpleFunction ?: return call
if (!function.realOverrideTarget.returnType.isTypeParameter())
return call
val erasedReturnType: IrType =
function.realOverrideTarget.returnType.eraseUpperBoundType()
@@ -72,11 +69,6 @@ class GenericReturnTypeLowering(val context: WasmBackendContext) : FileLoweringP
)
context.createIrBuilder(scopeOwnerSymbol).apply {
if (call.type.isUnit()) {
return irComposite(call) {
+newCall
}
}
return irImplicitCast(newCall, call.type)
}
}