From 526e99891a928346f842c87bb9825c18b54bf5d3 Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Sat, 9 Apr 2022 19:42:28 +0200 Subject: [PATCH] [WASM] Refcast and Reftest refactorings --- .../kotlin/backend/wasm/WasmSymbols.kt | 6 +- .../dce/WasmUsefulDeclarationProcessor.kt | 6 +- .../backend/wasm/ir2wasm/BodyGenerator.kt | 66 +++++++++++-------- ...eVirtualDispatchReceiverParametersTypes.kt | 3 + .../wasm/lower/JsInteropFunctionsLowering.kt | 2 +- .../wasm/lower/WasmTypeOperatorLowering.kt | 3 +- .../kotlin/wasm/ir/WasmExpressionBuilder.kt | 9 ++- 7 files changed, 57 insertions(+), 38 deletions(-) diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt index bcf3cd1b2b9..a7ca8cc596c 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt @@ -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 } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt index ee7fdd2e9be..a48f2b924b6 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt @@ -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 -> { diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt index 1014f3e87cf..53614c0b91a 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt @@ -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() 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 } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/EraseVirtualDispatchReceiverParametersTypes.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/EraseVirtualDispatchReceiverParametersTypes.kt index 9837817fe59..d3393d2c57f 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/EraseVirtualDispatchReceiverParametersTypes.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/EraseVirtualDispatchReceiverParametersTypes.kt @@ -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 diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt index 40afc88f771..239868facb0 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt @@ -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 diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt index 72d38341ded..81320f363bf 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt @@ -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) } diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt index 548941732ff..e11dc356568 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt @@ -150,15 +150,14 @@ abstract class WasmExpressionBuilder { ) } - - fun buildRefCast() { - buildInstr(WasmOp.REF_CAST) - } - fun buildRefCastStatic(toType: WasmSymbolReadOnly) { buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(toType)) } + fun buildRefTestStatic(toType: WasmSymbolReadOnly) { + buildInstr(WasmOp.REF_TEST_STATIC, WasmImmediate.TypeIdx(toType)) + } + fun buildRefNull(type: WasmHeapType) { buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type))) }