[WASM] Refcast and Reftest refactorings
This commit is contained in:
@@ -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 }
|
||||
|
||||
|
||||
+3
-3
@@ -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 -> {
|
||||
|
||||
+40
-26
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+2
-1
@@ -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)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user