[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 refIsNull = getInternalFunction("wasm_ref_is_null")
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 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"))
val wasmDataRefType by lazy { wasmDataRefClass.defaultType }
val wasmAnyRefClass = getIrClass(FqName("kotlin.wasm.internal.reftypes.anyref"))
private val externalInterfaceClass = getIrClass(FqName("kotlin.wasm.internal.ExternalInterfaceType"))
val externalInterfaceType by lazy { externalInterfaceClass.defaultType }
@@ -45,7 +45,7 @@ internal class WasmUsefulDeclarationProcessor(
}
context.wasmSymbols.wasmClassId,
context.wasmSymbols.wasmInterfaceId,
context.wasmSymbols.wasmRefCast,
context.wasmSymbols.refCast,
context.wasmSymbols.refTest -> {
call.getTypeArgument(0)?.getClass()?.enqueue(from, "generic intrinsic ${call.symbol.owner.name}")
true
@@ -63,8 +63,8 @@ internal class WasmUsefulDeclarationProcessor(
val op = WasmOp.valueOf(opString)
when (op.immediates.size) {
0 -> {
if (op == WasmOp.REF_TEST) {
call.getTypeArgument(0)?.enqueueRuntimeClassOrAny(from, "REF_TEST")
if (op == WasmOp.REF_TEST || op == WasmOp.REF_TEST_STATIC) {
call.getTypeArgument(0)?.enqueueRuntimeClassOrAny(from, "REF_TEST/REF_TEST_STATIC")
}
}
1 -> {
@@ -14,6 +14,7 @@ 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
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.isDispatchReceiver
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
@@ -312,9 +313,8 @@ class BodyGenerator(
val receiver = call.dispatchReceiver!!
generateExpression(receiver)
if (!receiver.type.getRuntimeClass(irBuiltIns).isSubclassOf(klass)) {
body.buildRefCastStatic(toType = context.referenceGcType(klass.symbol))
}
//TODO: check why it could be needed
generateRefCast(receiver.type, klass.defaultType)
body.buildStructGet(context.referenceGcType(klass.symbol), WasmSymbol(0))
body.buildStructGet(context.referenceVTableGcType(klass.symbol), WasmSymbol(vfSlot))
@@ -350,14 +350,31 @@ class BodyGenerator(
body.buildGetUnit()
}
private fun generateRefCast(type: IrType) {
body.buildRefCastStatic(
context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol)
)
private fun generateRefCast(fromType: IrType, toType: IrType) {
if (!isDownCastAlwaysSuccessInRuntime(fromType, toType)) {
body.buildRefCastStatic(
toType = context.referenceGcType(toType.getRuntimeClass(irBuiltIns).symbol)
)
}
}
private fun generateTypeRTT(type: IrType) {
body.buildRttCanon(context.referenceGcType(type.getRuntimeClass(irBuiltIns).symbol))
private fun generateRefTest(fromType: IrType, toType: IrType) {
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.
@@ -412,14 +429,11 @@ class BodyGenerator(
}
}
wasmSymbols.wasmRefCast -> {
generateRefCast(call.getTypeArgument(0)!!)
}
wasmSymbols.refTest -> {
val toType = call.getTypeArgument(0)!!
generateTypeRTT(toType)
body.buildInstr(WasmOp.REF_TEST)
wasmSymbols.refCast -> {
generateRefCast(
fromType = call.getValueArgument(0)!!.type,
toType = call.getTypeArgument(0)!!
)
}
wasmSymbols.unboxIntrinsic -> {
@@ -441,8 +455,7 @@ class BodyGenerator(
val klass: IrClass = backendContext.inlineClassesUtils.getInlinedClass(toType)!!
val field = getInlineClassBackingField(klass)
generateTypeRTT(toType)
body.buildRefCast()
generateRefCast(fromType, toType)
generateInstanceFieldAccess(field)
}
@@ -616,21 +629,22 @@ class BodyGenerator(
val opString = function.getWasmOpAnnotation()
if (opString != null) {
val op = WasmOp.valueOf(opString)
var immediates = emptyArray<WasmImmediate>()
when (op.immediates.size) {
0 -> {
when (op) {
WasmOp.REF_TEST -> {
val toIrType = call.getTypeArgument(0)!!
// ref.test takes RTT as a second operand
generateTypeRTT(toIrType)
WasmOp.REF_TEST, WasmOp.REF_TEST_STATIC -> {
generateRefTest(
fromType = call.getValueArgument(0)!!.type,
toType = call.getTypeArgument(0)!!
)
}
else -> {
body.buildInstr(op)
}
}
}
1 -> {
immediates = arrayOf(
val immediates = arrayOf(
when (val imm = op.immediates[0]) {
WasmImmediateKind.MEM_ARG ->
WasmImmediate.MemArg(0u, 0u)
@@ -643,11 +657,11 @@ class BodyGenerator(
error("Immediate $imm is unsupported")
}
)
body.buildInstr(op, *immediates)
}
else ->
error("Op $opString is unsupported")
}
body.buildInstr(op, *immediates)
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.lower.createIrBuilder
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.irImplicitCast
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
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.IrGetValue
import org.jetbrains.kotlin.ir.types.isAny
@@ -580,7 +580,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
) : InteropTypeAdapter {
override val fromType: IrType = context.wasmSymbols.wasmDataRefType
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.putTypeArgument(0, toType)
return call
@@ -225,6 +225,7 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
if (fromClass.isSubclassOf(toClass)) {
return value
}
if (toType.isNothing()) {
// Casting to nothing is unreachable...
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)
putValueArgument(0, value)
}
@@ -150,15 +150,14 @@ abstract class WasmExpressionBuilder {
)
}
fun buildRefCast() {
buildInstr(WasmOp.REF_CAST)
}
fun buildRefCastStatic(toType: WasmSymbolReadOnly<WasmTypeDeclaration>) {
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) {
buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type)))
}