[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.wasmClassId,
context.wasmSymbols.wasmInterfaceId, context.wasmSymbols.wasmInterfaceId,
context.wasmSymbols.wasmRefCast -> { context.wasmSymbols.wasmRefCast,
context.wasmSymbols.refTest -> {
call.getTypeArgument(0)?.getClass()?.enqueue(from, "generic intrinsic ${call.symbol.owner.name}") call.getTypeArgument(0)?.getClass()?.enqueue(from, "generic intrinsic ${call.symbol.owner.name}")
true 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.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
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
@@ -213,7 +214,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
generateDefaultInitializerForType(context.transformType(field.type), body) generateDefaultInitializerForType(context.transformType(field.type), body)
} }
body.buildGetGlobal(context.referenceClassRTT(klass.symbol)) generateClassRTT(klass.symbol)
body.buildStructNew(wasmGcType) body.buildStructNew(wasmGcType)
generateCall(expression) generateCall(expression)
} }
@@ -243,7 +244,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildConstI32Symbol(klassId) body.buildConstI32Symbol(klassId)
body.buildConstI32(0) // Any::_hashCode body.buildConstI32(0) // Any::_hashCode
generateExpression(call.getValueArgument(0)!!) generateExpression(call.getValueArgument(0)!!)
body.buildGetGlobal(context.referenceClassRTT(klass.symbol)) generateClassRTT(klass.symbol)
body.buildStructNew(structTypeName) body.buildStructNew(structTypeName)
return return
} }
@@ -322,8 +323,12 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
} }
private fun generateTypeRTT(type: IrType) { private fun generateTypeRTT(type: IrType) {
val rtClass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass val klass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass
body.buildGetGlobal(context.referenceClassRTT(rtClass)) generateClassRTT(klass)
}
private fun generateClassRTT(klass: IrClassSymbol) {
body.buildRttCanon(context.referenceGcType(klass))
} }
// Return true if generated. // Return true if generated.
@@ -357,6 +362,12 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildRefCast() body.buildRefCast()
} }
wasmSymbols.refTest -> {
val toType = call.getTypeArgument(0)!!
generateTypeRTT(toType)
body.buildInstr(WasmOp.REF_TEST)
}
wasmSymbols.unboxIntrinsic -> { wasmSymbols.unboxIntrinsic -> {
val fromType = call.getTypeArgument(0)!! val fromType = call.getTypeArgument(0)!!
@@ -581,6 +592,9 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
WasmImmediate.MemArg(0u, 0u) WasmImmediate.MemArg(0u, 0u)
WasmImmediateKind.STRUCT_TYPE_IDX -> WasmImmediateKind.STRUCT_TYPE_IDX ->
WasmImmediate.GcType(context.referenceGcType(function.dispatchReceiverParameter!!.type.classOrNull!!)) WasmImmediate.GcType(context.referenceGcType(function.dispatchReceiverParameter!!.type.classOrNull!!))
WasmImmediateKind.TYPE_IDX ->
WasmImmediate.TypeIdx(context.referenceGcType(function.dispatchReceiverParameter!!.type.classOrNull!!))
else -> else ->
error("Immediate $imm is unsupported") error("Immediate $imm is unsupported")
} }
@@ -85,7 +85,6 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
val wasmFunctionType = val wasmFunctionType =
WasmFunctionType( WasmFunctionType(
name = watName,
parameterTypes = irParameters.map { context.transformValueParameterType(it) }, parameterTypes = irParameters.map { context.transformValueParameterType(it) },
resultTypes = listOfNotNull(resultType) 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" "Sanity check that $declaration is a real function that can be used in calls"
} }
val functionTypeSymbol = context.referenceFunctionType(declaration.symbol)
if (importedName != null) { if (importedName != null) {
// Imported functions don't have bodies. Declaring the signature: // Imported functions don't have bodies. Declaring the signature:
context.defineFunction( context.defineFunction(
declaration.symbol, declaration.symbol,
WasmFunction.Imported(watName, wasmFunctionType, importedName) WasmFunction.Imported(watName, functionTypeSymbol, importedName)
) )
// TODO: Support re-export of imported functions. // TODO: Support re-export of imported functions.
return return
} }
val function = WasmFunction.Defined(watName, wasmFunctionType) val function = WasmFunction.Defined(watName, functionTypeSymbol)
val functionCodegenContext = WasmFunctionCodegenContextImpl( val functionCodegenContext = WasmFunctionCodegenContextImpl(
declaration, declaration,
function, function,
@@ -200,6 +201,8 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
context.registerInterface(symbol) context.registerInterface(symbol)
} else { } else {
val nameStr = declaration.fqNameWhenAvailable.toString() val nameStr = declaration.fqNameWhenAvailable.toString()
val metadata = context.getClassMetadata(symbol)
val superClass = metadata.superClass
val structType = WasmStructDeclaration( val structType = WasmStructDeclaration(
name = nameStr, name = nameStr,
fields = declaration.allFields(irBuiltIns).map { fields = declaration.allFields(irBuiltIns).map {
@@ -208,40 +211,11 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext, private val al
type = context.transformFieldType(it.type), type = context.transformFieldType(it.type),
isMutable = true isMutable = true
) )
} },
superClass?.let { context.referenceGcType(superClass.klass.symbol) }
) )
context.defineGcType(symbol, structType) 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.registerClass(symbol)
context.generateTypeInfo(symbol, binaryDataStruct(metadata)) context.generateTypeInfo(symbol, binaryDataStruct(metadata))
@@ -394,7 +368,7 @@ fun generateDefaultInitializerForType(type: WasmType, g: WasmExpressionBuilder)
WasmF32 -> g.buildConstF32(0f) WasmF32 -> g.buildConstF32(0f)
WasmF64 -> g.buildConstF64(0.0) WasmF64 -> g.buildConstF64(0.0)
is WasmRefNullType -> g.buildRefNull(type.heapType) 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") WasmUnreachableType -> error("Unreachable type can't be initialized")
else -> error("Unknown value type ${type.name}") else -> error("Unknown value type ${type.name}")
} }
@@ -82,11 +82,11 @@ class WasmTypeTransformer(
WasmF64 WasmF64
builtIns.nothingNType -> builtIns.nothingNType ->
WasmExternRef WasmAnyRef
// Value will not be created. Just using a random Wasm type. // Value will not be created. Just using a random Wasm type.
builtIns.nothingType -> builtIns.nothingType ->
WasmExternRef WasmAnyRef
symbols.voidType -> symbols.voidType ->
error("Void type can't be used as a value") error("Void type can't be used as a value")
@@ -29,7 +29,6 @@ interface WasmBaseCodegenContext {
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 referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol<Int>
fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal>
fun referenceSignatureId(signature: WasmSignature): WasmSymbol<Int> fun referenceSignatureId(signature: WasmSignature): WasmSymbol<Int>
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.wasm.ir2wasm 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.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
@@ -35,11 +34,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val stringLiteralId = val stringLiteralId =
ReferencableElements<String, Int>() ReferencableElements<String, Int>()
val runtimeTypes =
ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
val tagFuncType = WasmFunctionType( val tagFuncType = WasmFunctionType(
"ex_handling_tag",
listOf( listOf(
WasmRefNullType(WasmHeapType.Type(gcTypes.reference(irBuiltIns.throwableClass))) WasmRefNullType(WasmHeapType.Type(gcTypes.reference(irBuiltIns.throwableClass)))
), ),
@@ -123,9 +118,18 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
fun linkWasmCompiledFragments(): WasmModule { fun linkWasmCompiledFragments(): WasmModule {
bind(functions.unbound, functions.defined) bind(functions.unbound, functions.defined)
bind(globals.unbound, globals.defined) bind(globals.unbound, globals.defined)
bind(functionTypes.unbound, functionTypes.defined)
bind(gcTypes.unbound, gcTypes.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>() val klassIds = mutableMapOf<IrClassSymbol, Int>()
var currentDataSectionAddress = 0 var currentDataSectionAddress = 0
@@ -260,8 +264,8 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
) )
} }
val masterInitFunctionType = WasmFunctionType("__init_t", emptyList(), emptyList()) val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList())
val masterInitFunction = WasmFunction.Defined("__init", masterInitFunctionType) val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType))
with(WasmIrExpressionBuilder(masterInitFunction.instructions)) { with(WasmIrExpressionBuilder(masterInitFunction.instructions)) {
initFunctions.sortedBy { it.priority }.forEach { initFunctions.sortedBy { it.priority }.forEach {
buildCall(WasmSymbol(it.function)) buildCall(WasmSymbol(it.function))
@@ -283,18 +287,28 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val importedFunctions = functions.elements.filterIsInstance<WasmFunction.Imported>() val importedFunctions = functions.elements.filterIsInstance<WasmFunction.Imported>()
// Sorting by depth for a valid init order fun wasmTypeDeclarationOrderKey(declaration: WasmTypeDeclaration): Int {
val sortedRttGlobals = runtimeTypes.elements.sortedBy { (it.type as WasmRtt).depth } 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( val module = WasmModule(
functionTypes = functionTypes.elements + tagFuncType + masterInitFunctionType, functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType,
gcTypes = gcTypes.elements, gcTypes = sortedGcTypes,
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 = listOf(table) + interfaceMethodTables.elements,
memories = listOf(memory), memories = listOf(memory),
globals = globals.elements + sortedRttGlobals, globals = globals.elements,
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 = listOf(elements) + interfaceTableElements,
@@ -18,7 +18,6 @@ interface WasmModuleCodegenContext : WasmBaseCodegenContext {
fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction) fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction)
fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal) fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal)
fun defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration) fun defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration)
fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal)
fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType)
fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable) fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable)
fun addJsFun(importName: String, jsCode: String) fun addJsFun(importName: String, jsCode: String)
@@ -106,10 +106,6 @@ class WasmModuleCodegenContextImpl(
wasmFragment.gcTypes.define(irClass, wasmType) wasmFragment.gcTypes.define(irClass, wasmType)
} }
override fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
wasmFragment.runtimeTypes.define(irClass, wasmGlobal)
}
override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) { override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) {
wasmFragment.functionTypes.define(irFunction, wasmFunctionType) wasmFragment.functionTypes.define(irFunction, wasmFunctionType)
} }
@@ -157,9 +153,6 @@ class WasmModuleCodegenContextImpl(
return wasmFragment.gcTypes.reference(irClass) return wasmFragment.gcTypes.reference(irClass)
} }
override fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal> =
wasmFragment.runtimeTypes.reference(irClass)
override fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> = override fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> =
wasmFragment.functionTypes.reference(irFunction) wasmFragment.functionTypes.reference(irFunction)
@@ -51,9 +51,6 @@ class GenericReturnTypeLowering(val context: WasmBackendContext) : FileLoweringP
val function: IrSimpleFunction = val function: IrSimpleFunction =
call.symbol.owner as? IrSimpleFunction ?: return call call.symbol.owner as? IrSimpleFunction ?: return call
if (!function.realOverrideTarget.returnType.isTypeParameter())
return call
val erasedReturnType: IrType = val erasedReturnType: IrType =
function.realOverrideTarget.returnType.eraseUpperBoundType() function.realOverrideTarget.returnType.eraseUpperBoundType()
@@ -72,11 +69,6 @@ class GenericReturnTypeLowering(val context: WasmBackendContext) : FileLoweringP
) )
context.createIrBuilder(scopeOwnerSymbol).apply { context.createIrBuilder(scopeOwnerSymbol).apply {
if (call.type.isUnit()) {
return irComposite(call) {
+newCall
}
}
return irImplicitCast(newCall, call.type) return irImplicitCast(newCall, call.type)
} }
} }
@@ -1,7 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: FAKE_OVERRIDE_ISSUES
// On wasm this will produce conflicting return types, foo will return Any but we will try to interpret it as String.
// Before wasm native strings this worked by chance because we added unbox intrinsic for strings.
open class Foo { open class Foo {
open fun foo(x: CharSequence = "O"): CharSequence = x open fun foo(x: CharSequence = "O"): CharSequence = x
@@ -1,7 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: FAKE_OVERRIDE_ISSUES
// On wasm this will produce conflicting return types, foo will return Any but we will try to interpret it as String.
// Before wasm native strings this worked by chance because we added unbox intrinsic for strings.
open class Foo { open class Foo {
open fun foo(x: CharSequence = "O"): CharSequence = x open fun foo(x: CharSequence = "O"): CharSequence = x
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: WASM
fun <T> myRun(action: () -> T): T = action() fun <T> myRun(action: () -> T): T = action()
fun foo(): String = "foo" fun foo(): String = "foo"
@@ -1,3 +1,5 @@
// V8 fail: https://bugs.chromium.org/p/v8/issues/detail?id=12834
// IGNORE_BACKEND: WASM
// Char issues // Char issues
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR
+1 -1
View File
@@ -158,7 +158,7 @@ val v8osString = when (currentOsType) {
} }
val v8edition = "rel" // rel or dbg val v8edition = "rel" // rel or dbg
val v8version = "9.2.212" val v8version = "10.2.9"
val v8fileName = "v8-${v8osString}-${v8edition}-${v8version}" val v8fileName = "v8-${v8osString}-${v8edition}-${v8version}"
val v8url = "https://storage.googleapis.com/chromium-v8/official/canary/$v8fileName.zip" val v8url = "https://storage.googleapis.com/chromium-v8/official/canary/$v8fileName.zip"
@@ -180,7 +180,6 @@ abstract class BasicWasmBoxTest(
File(dir, "test.js").writeText(testJs) File(dir, "test.js").writeText(testJs)
ExternalTool(System.getProperty("javascript.engine.path.V8")) ExternalTool(System.getProperty("javascript.engine.path.V8"))
.run( .run(
"--experimental-wasm-typed-funcref",
"--experimental-wasm-gc", "--experimental-wasm-gc",
"--experimental-wasm-eh", "--experimental-wasm-eh",
*jsFilesBefore.map { File(it).absolutePath }.toTypedArray(), *jsFilesBefore.map { File(it).absolutePath }.toTypedArray(),
@@ -1,3 +1,6 @@
// V8 fail: https://bugs.chromium.org/p/v8/issues/detail?id=12834
// IGNORE_BACKEND: WASM
// EXPECTED_REACHABLE_NODES: 1281 // EXPECTED_REACHABLE_NODES: 1281
// FILE: main.kt // FILE: main.kt
+3
View File
@@ -1,3 +1,6 @@
// V8 fail: https://bugs.chromium.org/p/v8/issues/detail?id=12834
// IGNORE_BACKEND: WASM
// EXPECTED_REACHABLE_NODES: 1283 // EXPECTED_REACHABLE_NODES: 1283
package foo package foo
@@ -1,3 +1,6 @@
// V8 fail: https://bugs.chromium.org/p/v8/issues/detail?id=12834
// IGNORE_BACKEND: WASM
// EXPECTED_REACHABLE_NODES: 1284 // EXPECTED_REACHABLE_NODES: 1284
package foo package foo
@@ -9,6 +9,7 @@ package org.jetbrains.kotlin.wasm.ir
class WasmModule( class WasmModule(
val functionTypes: List<WasmFunctionType> = emptyList(), val functionTypes: List<WasmFunctionType> = emptyList(),
val gcTypes: List<WasmTypeDeclaration> = emptyList(), val gcTypes: List<WasmTypeDeclaration> = emptyList(),
val gcTypesInRecursiveGroup: Boolean,
val importsInOrder: List<WasmNamedModuleField> = emptyList(), val importsInOrder: List<WasmNamedModuleField> = emptyList(),
val importedFunctions: List<WasmFunction.Imported> = emptyList(), val importedFunctions: List<WasmFunction.Imported> = emptyList(),
@@ -38,18 +39,18 @@ sealed class WasmNamedModuleField {
sealed class WasmFunction( sealed class WasmFunction(
override val name: String, override val name: String,
val type: WasmFunctionType val type: WasmSymbolReadOnly<WasmFunctionType>
) : WasmNamedModuleField() { ) : WasmNamedModuleField() {
class Defined( class Defined(
name: String, name: String,
type: WasmFunctionType, type: WasmSymbolReadOnly<WasmFunctionType>,
val locals: MutableList<WasmLocal> = mutableListOf(), val locals: MutableList<WasmLocal> = mutableListOf(),
val instructions: MutableList<WasmInstr> = mutableListOf() val instructions: MutableList<WasmInstr> = mutableListOf()
) : WasmFunction(name, type) ) : WasmFunction(name, type)
class Imported( class Imported(
name: String, name: String,
type: WasmFunctionType, type: WasmSymbolReadOnly<WasmFunctionType>,
val importPair: WasmImportPair val importPair: WasmImportPair
) : WasmFunction(name, type) ) : WasmFunction(name, type)
} }
@@ -146,15 +147,15 @@ sealed class WasmTypeDeclaration(
override val name: String override val name: String
) : WasmNamedModuleField() ) : WasmNamedModuleField()
class WasmFunctionType( data class WasmFunctionType(
name: String,
val parameterTypes: List<WasmType>, val parameterTypes: List<WasmType>,
val resultTypes: List<WasmType> val resultTypes: List<WasmType>
) : WasmTypeDeclaration(name) ) : WasmTypeDeclaration("")
class WasmStructDeclaration( class WasmStructDeclaration(
name: String, name: String,
val fields: List<WasmStructFieldDeclaration> val fields: List<WasmStructFieldDeclaration>,
val superType: WasmSymbolReadOnly<WasmTypeDeclaration>?
) : WasmTypeDeclaration(name) ) : WasmTypeDeclaration(name)
class WasmArrayDeclaration( class WasmArrayDeclaration(
@@ -343,6 +343,8 @@ enum class WasmOp(
// GC // GC
STRUCT_NEW_WITH_RTT("struct.new_with_rtt", 0xFB_01, STRUCT_TYPE_IDX), STRUCT_NEW_WITH_RTT("struct.new_with_rtt", 0xFB_01, STRUCT_TYPE_IDX),
STRUCT_NEW_DEFAULT_WITH_RTT("struct.new_default_with_rtt", 0xFB_02, STRUCT_TYPE_IDX), STRUCT_NEW_DEFAULT_WITH_RTT("struct.new_default_with_rtt", 0xFB_02, STRUCT_TYPE_IDX),
STRUCT_NEW("struct.new", 0xFB_07, STRUCT_TYPE_IDX),
STRUCT_NEW_DEFAULT("struct.new_default", 0xFB_08, STRUCT_TYPE_IDX),
STRUCT_GET("struct.get", 0xFB_03, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)), STRUCT_GET("struct.get", 0xFB_03, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)),
STRUCT_GET_S("struct.get_s", 0xFB_04, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)), STRUCT_GET_S("struct.get_s", 0xFB_04, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)),
STRUCT_GET_U("struct.get_u", 0xFB_05, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)), STRUCT_GET_U("struct.get_u", 0xFB_05, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)),
@@ -350,6 +352,8 @@ enum class WasmOp(
ARRAY_NEW_WITH_RTT("array.new_with_rtt", 0xFB_11, STRUCT_TYPE_IDX), ARRAY_NEW_WITH_RTT("array.new_with_rtt", 0xFB_11, STRUCT_TYPE_IDX),
ARRAY_NEW_DEFAULT_WITH_RTT("array.new_default_with_rtt", 0xFB_12, STRUCT_TYPE_IDX), ARRAY_NEW_DEFAULT_WITH_RTT("array.new_default_with_rtt", 0xFB_12, STRUCT_TYPE_IDX),
ARRAY_NEW("array.new", 0xFB_1B, STRUCT_TYPE_IDX),
ARRAY_NEW_DEFAULT("array.new_default", 0xFB_1C, STRUCT_TYPE_IDX),
ARRAY_GET("array.get", 0xFB_13, listOf(STRUCT_TYPE_IDX)), ARRAY_GET("array.get", 0xFB_13, listOf(STRUCT_TYPE_IDX)),
ARRAY_GET_S("array.get_s", 0xFB_14, listOf(STRUCT_TYPE_IDX)), ARRAY_GET_S("array.get_s", 0xFB_14, listOf(STRUCT_TYPE_IDX)),
ARRAY_GET_U("array.get_u", 0xFB_15, listOf(STRUCT_TYPE_IDX)), ARRAY_GET_U("array.get_u", 0xFB_15, listOf(STRUCT_TYPE_IDX)),
@@ -362,9 +366,10 @@ enum class WasmOp(
RTT_CANON("rtt.canon", 0xFB_30, TYPE_IDX), RTT_CANON("rtt.canon", 0xFB_30, TYPE_IDX),
RTT_SUB("rtt.sub", 0xFB_31, TYPE_IDX),
REF_TEST("ref.test", 0xFB_40), REF_TEST("ref.test", 0xFB_40),
REF_TEST_STATIC("ref.test_static", 0xFB_44, STRUCT_TYPE_IDX),
REF_CAST("ref.cast", 0xFB_41), REF_CAST("ref.cast", 0xFB_41),
REF_CAST_STATIC("ref.cast_static", 0xFB_45, STRUCT_TYPE_IDX),
BR_ON_CAST("br_on_cast", 0xFB_42, listOf(LABEL_IDX)), BR_ON_CAST("br_on_cast", 0xFB_42, listOf(LABEL_IDX)),
@@ -26,18 +26,18 @@ object WasmExternRef : WasmType("externref", -0x11)
object WasmAnyRef : WasmType("anyref", -0x12) object WasmAnyRef : WasmType("anyref", -0x12)
object WasmEqRef : WasmType("eqref", -0x13) object WasmEqRef : WasmType("eqref", -0x13)
class WasmRefNullType(val heapType: WasmHeapType) : WasmType("ref null", -0x14) data class WasmRefNullType(val heapType: WasmHeapType) : WasmType("ref null", -0x14)
class WasmRefType(val heapType: WasmHeapType) : WasmType("ref", -0x15) data class WasmRefType(val heapType: WasmHeapType) : WasmType("ref", -0x15)
@Suppress("unused") @Suppress("unused")
object WasmI31Ref : WasmType("i31ref", -0x16) object WasmI31Ref : WasmType("i31ref", -0x16)
class WasmRtt(val depth: Int, val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmType("rtt", -0x17) data class WasmRtt(val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmType("rtt", -0x18)
@Suppress("unused") @Suppress("unused")
object WasmDataRef : WasmType("dataref", -0x19) object WasmDataRef : WasmType("dataref", -0x19)
sealed class WasmHeapType { sealed class WasmHeapType {
class Type(val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmHeapType() { data class Type(val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmHeapType() {
override fun toString(): String { override fun toString(): String {
return "Type:$type" return "Type:$type"
} }
@@ -46,6 +46,7 @@ sealed class WasmHeapType {
sealed class Simple(val name: String, val code: Byte) : WasmHeapType() { sealed class Simple(val name: String, val code: Byte) : WasmHeapType() {
object Func : Simple("func", -0x10) object Func : Simple("func", -0x10)
object Extern : Simple("extern", -0x11) object Extern : Simple("extern", -0x11)
object Any : Simple("any", -0x12)
object Eq : Simple("eq", -0x13) object Eq : Simple("eq", -0x13)
@Suppress("unused") @Suppress("unused")
@@ -70,7 +71,8 @@ fun WasmType.getHeapType(): WasmHeapType =
is WasmRefType -> heapType is WasmRefType -> heapType
is WasmRefNullType -> heapType is WasmRefNullType -> heapType
is WasmEqRef -> WasmHeapType.Simple.Eq is WasmEqRef -> WasmHeapType.Simple.Eq
is WasmExternRef -> WasmHeapType.Simple.Extern is WasmAnyRef -> WasmHeapType.Simple.Any
is WasmFuncRef -> WasmHeapType.Simple.Func is WasmFuncRef -> WasmHeapType.Simple.Func
is WasmExternRef -> WasmHeapType.Simple.Extern
else -> error("Unknown heap type for type $this") else -> error("Unknown heap type for type $this")
} }
@@ -142,12 +142,12 @@ abstract class WasmExpressionBuilder {
buildInstr(WasmOp.REF_CAST) buildInstr(WasmOp.REF_CAST)
} }
fun buildRefNull(type: WasmHeapType) { fun buildRefCastStatic(type: WasmSymbolReadOnly<WasmTypeDeclaration>) {
buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type))) buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(type))
} }
fun buildRttSub(decl: WasmSymbol<WasmTypeDeclaration>) { fun buildRefNull(type: WasmHeapType) {
buildInstr(WasmOp.RTT_SUB, WasmImmediate.TypeIdx(decl)) buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type)))
} }
fun buildRttCanon(decl: WasmSymbol<WasmTypeDeclaration>) { fun buildRttCanon(decl: WasmSymbol<WasmTypeDeclaration>) {
@@ -97,7 +97,7 @@ class WasmBinaryToIR(val b: MyByteReader) {
val type = functionTypes[b.readVarUInt32AsInt()] val type = functionTypes[b.readVarUInt32AsInt()]
importedFunctions += WasmFunction.Imported( importedFunctions += WasmFunction.Imported(
name = "", name = "",
type = type, type = WasmSymbol(type),
importPair = importPair, importPair = importPair,
).also { importsInOrder.add(it) } ).also { importsInOrder.add(it) }
} }
@@ -141,7 +141,7 @@ class WasmBinaryToIR(val b: MyByteReader) {
definedFunctions.add( definedFunctions.add(
WasmFunction.Defined( WasmFunction.Defined(
"", "",
functionType, WasmSymbol(functionType),
locals = functionType.parameterTypes.mapIndexed { index, wasmType -> locals = functionType.parameterTypes.mapIndexed { index, wasmType ->
WasmLocal(index, "", wasmType, true) WasmLocal(index, "", wasmType, true)
}.toMutableList() }.toMutableList()
@@ -330,6 +330,7 @@ class WasmBinaryToIR(val b: MyByteReader) {
return WasmModule( return WasmModule(
functionTypes = functionTypes, functionTypes = functionTypes,
gcTypes = gcTypes, gcTypes = gcTypes,
gcTypesInRecursiveGroup = false,
importsInOrder = importsInOrder, importsInOrder = importsInOrder,
importedFunctions = importedFunctions, importedFunctions = importedFunctions,
importedMemories = importedMemories, importedMemories = importedMemories,
@@ -450,7 +451,7 @@ class WasmBinaryToIR(val b: MyByteReader) {
(-0x20).toByte() -> { (-0x20).toByte() -> {
val types = mapVector { readValueType() } val types = mapVector { readValueType() }
val returnTypes = mapVector { readValueType() } val returnTypes = mapVector { readValueType() }
return WasmFunctionType("", types, returnTypes) return WasmFunctionType(types, returnTypes)
} }
else -> TODO() else -> TODO()
@@ -466,8 +467,8 @@ class WasmBinaryToIR(val b: MyByteReader) {
WasmI8, WasmI8,
WasmI16, WasmI16,
WasmFuncRef, WasmFuncRef,
WasmExternRef,
WasmAnyRef, WasmAnyRef,
WasmExternRef,
WasmEqRef WasmEqRef
).associateBy { it.code } ).associateBy { it.code }
@@ -21,13 +21,17 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod
with(module) { with(module) {
// type section // type section
appendSection(1u) { appendSection(1u) {
if (module.gcTypesInRecursiveGroup) {
appendVectorSize(1)
b.writeByte(0x4f)
}
appendVectorSize(functionTypes.size + gcTypes.size) appendVectorSize(functionTypes.size + gcTypes.size)
functionTypes.forEach { appendFunctionTypeDeclaration(it) } functionTypes.forEach { appendFunctionTypeDeclaration(it) }
gcTypes.forEach { gcTypes.forEach {
when (it) { when (it) {
is WasmStructDeclaration -> appendStructTypeDeclaration(it) is WasmStructDeclaration -> appendStructTypeDeclaration(it)
is WasmArrayDeclaration -> appendArrayTypeDeclaration(it) is WasmArrayDeclaration -> appendArrayTypeDeclaration(it)
is WasmFunctionType -> {} is WasmFunctionType -> error("Function type in GC types")
} }
} }
} }
@@ -277,6 +281,12 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod
} }
private fun appendStructTypeDeclaration(type: WasmStructDeclaration) { private fun appendStructTypeDeclaration(type: WasmStructDeclaration) {
val superType = type.superType
if (superType != null) {
b.writeVarInt7(-0x30)
appendVectorSize(1)
appendModuleFieldReference(superType.owner)
}
b.writeVarInt7(-0x21) b.writeVarInt7(-0x21)
b.writeVarUInt32(type.fields.size) b.writeVarUInt32(type.fields.size)
type.fields.forEach { type.fields.forEach {
@@ -290,7 +300,7 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod
} }
val WasmFunctionType.index: Int val WasmFunctionType.index: Int
get() = module.functionTypes.indexOf(this) get() = id!!
private fun appendLimits(limits: WasmLimits) { private fun appendLimits(limits: WasmLimits) {
b.writeVarUInt1(limits.maxSize != null) b.writeVarUInt1(limits.maxSize != null)
@@ -303,11 +313,11 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod
b.writeString(function.importPair.moduleName) b.writeString(function.importPair.moduleName)
b.writeString(function.importPair.declarationName) b.writeString(function.importPair.declarationName)
b.writeByte(0) // Function external kind. b.writeByte(0) // Function external kind.
b.writeVarUInt32(function.type.index) b.writeVarUInt32(function.type.owner.index)
} }
private fun appendDefinedFunction(function: WasmFunction.Defined) { private fun appendDefinedFunction(function: WasmFunction.Defined) {
b.writeVarUInt32(function.type.index) b.writeVarUInt32(function.type.owner.index)
} }
private fun appendTable(table: WasmTable) { private fun appendTable(table: WasmTable) {
@@ -483,7 +493,6 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod
appendHeapType(type.heapType) appendHeapType(type.heapType)
} }
if (type is WasmRtt) { if (type is WasmRtt) {
b.writeVarUInt32(type.depth)
appendModuleFieldReference(type.type.owner) appendModuleFieldReference(type.type.owner)
} }
} }
@@ -246,12 +246,26 @@ class WasmIrToText : SExpressionBuilder() {
} }
} }
private inline fun maybeSubType(superType: WasmTypeDeclaration?, body: () -> Unit) {
if (superType != null) {
sameLineList("sub") {
appendModuleFieldReference(superType)
body()
}
} else {
body()
}
}
private fun appendStructTypeDeclaration(type: WasmStructDeclaration) { private fun appendStructTypeDeclaration(type: WasmStructDeclaration) {
newLineList("type") { newLineList("type") {
appendModuleFieldReference(type) appendModuleFieldReference(type)
sameLineList("struct") { maybeSubType(type.superType?.owner) {
type.fields.forEach { sameLineList("struct") {
appendStructField(it) type.fields.forEach {
appendStructField(it)
}
} }
} }
} }
@@ -287,9 +301,9 @@ class WasmIrToText : SExpressionBuilder() {
appendModuleFieldReference(function) appendModuleFieldReference(function)
sameLineList("type") { appendModuleFieldReference(function.type) } sameLineList("type") { appendModuleFieldReference(function.type) }
function.locals.forEach { if (it.isParameter) appendLocal(it) } function.locals.forEach { if (it.isParameter) appendLocal(it) }
if (function.type.resultTypes.isNotEmpty()) { if (function.type.owner.resultTypes.isNotEmpty()) {
sameLineList("result") { sameLineList("result") {
function.type.resultTypes.forEach { appendType(it) } function.type.owner.resultTypes.forEach { appendType(it) }
} }
} }
function.locals.forEach { if (!it.isParameter) appendLocal(it) } function.locals.forEach { if (!it.isParameter) appendLocal(it) }
@@ -438,6 +452,7 @@ class WasmIrToText : SExpressionBuilder() {
fun appendReferencedType(type: WasmType) { fun appendReferencedType(type: WasmType) {
when (type) { when (type) {
is WasmFuncRef -> appendElement("func") is WasmFuncRef -> appendElement("func")
is WasmAnyRef -> appendElement("any")
is WasmExternRef -> appendElement("extern") is WasmExternRef -> appendElement("extern")
else -> TODO() else -> TODO()
} }
@@ -457,7 +472,6 @@ class WasmIrToText : SExpressionBuilder() {
is WasmRtt -> is WasmRtt ->
sameLineList("rtt") { sameLineList("rtt") {
appendElement(type.depth.toString())
appendModuleFieldReference(type.type.owner) appendModuleFieldReference(type.type.owner)
} }
@@ -489,6 +503,10 @@ class WasmIrToText : SExpressionBuilder() {
if (id != 0) appendElement(id.toString()) if (id != 0) appendElement(id.toString())
} }
fun appendModuleFieldReference(field: WasmSymbolReadOnly<WasmNamedModuleField>) {
appendModuleFieldReference(field.owner)
}
fun appendModuleFieldReference(field: WasmNamedModuleField) { fun appendModuleFieldReference(field: WasmNamedModuleField) {
val id = field.id val id = field.id
?: error("${field::class} ${field.name} ID is unlinked") ?: error("${field::class} ${field.name} ID is unlinked")