[WASM] Refcast and Reftest refactorings

This commit is contained in:
Igor Yakovlev
2022-04-09 19:42:28 +02:00
parent 1eaa266a95
commit 526e99891a
7 changed files with 57 additions and 38 deletions
@@ -154,9 +154,9 @@ class WasmSymbols(
val refEq = getInternalFunction("wasm_ref_eq") val refEq = getInternalFunction("wasm_ref_eq")
val refIsNull = getInternalFunction("wasm_ref_is_null") val refIsNull = getInternalFunction("wasm_ref_is_null")
val refTest = getInternalFunction("wasm_ref_test") val refTest = getInternalFunction("wasm_ref_test")
val intToLong = getInternalFunction("wasm_i64_extend_i32_s") val refCast = getInternalFunction("wasm_ref_cast")
val wasmRefCast = getInternalFunction("wasm_ref_cast") val intToLong = getInternalFunction("wasm_i64_extend_i32_s")
val rangeCheck = getInternalFunction("rangeCheck") val rangeCheck = getInternalFunction("rangeCheck")
val assertFuncs = findFunctions(kotlinTopLevelPackage.memberScope, Name.identifier("assert")).map { symbolTable.referenceSimpleFunction(it) } val assertFuncs = findFunctions(kotlinTopLevelPackage.memberScope, Name.identifier("assert")).map { symbolTable.referenceSimpleFunction(it) }
@@ -250,6 +250,8 @@ class WasmSymbols(
private val wasmDataRefClass = getIrClass(FqName("kotlin.wasm.internal.reftypes.dataref")) private val wasmDataRefClass = getIrClass(FqName("kotlin.wasm.internal.reftypes.dataref"))
val wasmDataRefType by lazy { wasmDataRefClass.defaultType } val wasmDataRefType by lazy { wasmDataRefClass.defaultType }
val wasmAnyRefClass = getIrClass(FqName("kotlin.wasm.internal.reftypes.anyref"))
private val externalInterfaceClass = getIrClass(FqName("kotlin.wasm.internal.ExternalInterfaceType")) private val externalInterfaceClass = getIrClass(FqName("kotlin.wasm.internal.ExternalInterfaceType"))
val externalInterfaceType by lazy { externalInterfaceClass.defaultType } val externalInterfaceType by lazy { externalInterfaceClass.defaultType }
@@ -45,7 +45,7 @@ internal class WasmUsefulDeclarationProcessor(
} }
context.wasmSymbols.wasmClassId, context.wasmSymbols.wasmClassId,
context.wasmSymbols.wasmInterfaceId, context.wasmSymbols.wasmInterfaceId,
context.wasmSymbols.wasmRefCast, context.wasmSymbols.refCast,
context.wasmSymbols.refTest -> { 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
@@ -63,8 +63,8 @@ internal class WasmUsefulDeclarationProcessor(
val op = WasmOp.valueOf(opString) val op = WasmOp.valueOf(opString)
when (op.immediates.size) { when (op.immediates.size) {
0 -> { 0 -> {
if (op == WasmOp.REF_TEST) { if (op == WasmOp.REF_TEST || op == WasmOp.REF_TEST_STATIC) {
call.getTypeArgument(0)?.enqueueRuntimeClassOrAny(from, "REF_TEST") call.getTypeArgument(0)?.enqueueRuntimeClassOrAny(from, "REF_TEST/REF_TEST_STATIC")
} }
} }
1 -> { 1 -> {
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.wasm.utils.*
import org.jetbrains.kotlin.backend.wasm.utils.isCanonical import org.jetbrains.kotlin.backend.wasm.utils.isCanonical
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.utils.erasedUpperBound
import org.jetbrains.kotlin.ir.backend.js.utils.findUnitGetInstanceFunction import org.jetbrains.kotlin.ir.backend.js.utils.findUnitGetInstanceFunction
import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver 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
@@ -312,9 +313,8 @@ class BodyGenerator(
val receiver = call.dispatchReceiver!! val receiver = call.dispatchReceiver!!
generateExpression(receiver) generateExpression(receiver)
if (!receiver.type.getRuntimeClass(irBuiltIns).isSubclassOf(klass)) { //TODO: check why it could be needed
body.buildRefCastStatic(toType = context.referenceGcType(klass.symbol)) generateRefCast(receiver.type, klass.defaultType)
}
body.buildStructGet(context.referenceGcType(klass.symbol), WasmSymbol(0)) body.buildStructGet(context.referenceGcType(klass.symbol), WasmSymbol(0))
body.buildStructGet(context.referenceVTableGcType(klass.symbol), WasmSymbol(vfSlot)) body.buildStructGet(context.referenceVTableGcType(klass.symbol), WasmSymbol(vfSlot))
@@ -350,14 +350,31 @@ class BodyGenerator(
body.buildGetUnit() body.buildGetUnit()
} }
private fun generateRefCast(type: IrType) { private fun generateRefCast(fromType: IrType, toType: IrType) {
body.buildRefCastStatic( if (!isDownCastAlwaysSuccessInRuntime(fromType, toType)) {
context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol) body.buildRefCastStatic(
) toType = context.referenceGcType(toType.getRuntimeClass(irBuiltIns).symbol)
)
}
} }
private fun generateTypeRTT(type: IrType) { private fun generateRefTest(fromType: IrType, toType: IrType) {
body.buildRttCanon(context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol)) if (!isDownCastAlwaysSuccessInRuntime(fromType, toType)) {
body.buildRefTestStatic(
toType = context.referenceGcType(toType.getRuntimeClass(irBuiltIns).symbol)
)
} else {
body.buildDrop()
body.buildConstI32(1)
}
}
private fun isDownCastAlwaysSuccessInRuntime(fromType: IrType, toType: IrType): Boolean {
val upperBound = fromType.erasedUpperBound
if (upperBound != null && upperBound.symbol.isSubtypeOfClass(backendContext.wasmSymbols.wasmAnyRefClass)) {
return false
}
return fromType.getRuntimeClass(irBuiltIns).isSubclassOf(toType.getRuntimeClass(irBuiltIns))
} }
// Return true if generated. // Return true if generated.
@@ -412,14 +429,11 @@ class BodyGenerator(
} }
} }
wasmSymbols.wasmRefCast -> { wasmSymbols.refCast -> {
generateRefCast(call.getTypeArgument(0)!!) generateRefCast(
} fromType = call.getValueArgument(0)!!.type,
toType = call.getTypeArgument(0)!!
wasmSymbols.refTest -> { )
val toType = call.getTypeArgument(0)!!
generateTypeRTT(toType)
body.buildInstr(WasmOp.REF_TEST)
} }
wasmSymbols.unboxIntrinsic -> { wasmSymbols.unboxIntrinsic -> {
@@ -441,8 +455,7 @@ class BodyGenerator(
val klass: IrClass = backendContext.inlineClassesUtils.getInlinedClass(toType)!! val klass: IrClass = backendContext.inlineClassesUtils.getInlinedClass(toType)!!
val field = getInlineClassBackingField(klass) val field = getInlineClassBackingField(klass)
generateTypeRTT(toType) generateRefCast(fromType, toType)
body.buildRefCast()
generateInstanceFieldAccess(field) generateInstanceFieldAccess(field)
} }
@@ -616,21 +629,22 @@ class BodyGenerator(
val opString = function.getWasmOpAnnotation() val opString = function.getWasmOpAnnotation()
if (opString != null) { if (opString != null) {
val op = WasmOp.valueOf(opString) val op = WasmOp.valueOf(opString)
var immediates = emptyArray<WasmImmediate>()
when (op.immediates.size) { when (op.immediates.size) {
0 -> { 0 -> {
when (op) { when (op) {
WasmOp.REF_TEST -> { WasmOp.REF_TEST, WasmOp.REF_TEST_STATIC -> {
val toIrType = call.getTypeArgument(0)!! generateRefTest(
// ref.test takes RTT as a second operand fromType = call.getValueArgument(0)!!.type,
generateTypeRTT(toIrType) toType = call.getTypeArgument(0)!!
)
} }
else -> { else -> {
body.buildInstr(op)
} }
} }
} }
1 -> { 1 -> {
immediates = arrayOf( val immediates = arrayOf(
when (val imm = op.immediates[0]) { when (val imm = op.immediates[0]) {
WasmImmediateKind.MEM_ARG -> WasmImmediateKind.MEM_ARG ->
WasmImmediate.MemArg(0u, 0u) WasmImmediate.MemArg(0u, 0u)
@@ -643,11 +657,11 @@ class BodyGenerator(
error("Immediate $imm is unsupported") error("Immediate $imm is unsupported")
} }
) )
body.buildInstr(op, *immediates)
} }
else -> else ->
error("Op $opString is unsupported") error("Op $opString is unsupported")
} }
body.buildInstr(op, *immediates)
return true return true
} }
@@ -9,11 +9,14 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.buildStatement
import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irImplicitCast import org.jetbrains.kotlin.ir.builders.irImplicitCast
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.types.isAny import org.jetbrains.kotlin.ir.types.isAny
@@ -580,7 +580,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
) : InteropTypeAdapter { ) : InteropTypeAdapter {
override val fromType: IrType = context.wasmSymbols.wasmDataRefType override val fromType: IrType = context.wasmSymbols.wasmDataRefType
override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression { override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
val call = builder.irCall(context.wasmSymbols.wasmRefCast) val call = builder.irCall(context.wasmSymbols.refCast)
call.putValueArgument(0, expression) call.putValueArgument(0, expression)
call.putTypeArgument(0, toType) call.putTypeArgument(0, toType)
return call return call
@@ -225,6 +225,7 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
if (fromClass.isSubclassOf(toClass)) { if (fromClass.isSubclassOf(toClass)) {
return value return value
} }
if (toType.isNothing()) { if (toType.isNothing()) {
// Casting to nothing is unreachable... // Casting to nothing is unreachable...
return builder.irComposite(resultType = context.irBuiltIns.nothingType) { return builder.irComposite(resultType = context.irBuiltIns.nothingType) {
@@ -233,7 +234,7 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
} }
} }
return builder.irCall(symbols.wasmRefCast, type = toType).apply { return builder.irCall(symbols.refCast, type = toType).apply {
putTypeArgument(0, toType) putTypeArgument(0, toType)
putValueArgument(0, value) putValueArgument(0, value)
} }
@@ -150,15 +150,14 @@ abstract class WasmExpressionBuilder {
) )
} }
fun buildRefCast() {
buildInstr(WasmOp.REF_CAST)
}
fun buildRefCastStatic(toType: WasmSymbolReadOnly<WasmTypeDeclaration>) { fun buildRefCastStatic(toType: WasmSymbolReadOnly<WasmTypeDeclaration>) {
buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(toType)) buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(toType))
} }
fun buildRefTestStatic(toType: WasmSymbolReadOnly<WasmTypeDeclaration>) {
buildInstr(WasmOp.REF_TEST_STATIC, WasmImmediate.TypeIdx(toType))
}
fun buildRefNull(type: WasmHeapType) { fun buildRefNull(type: WasmHeapType) {
buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type))) buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type)))
} }