[Wasm] Require location for buildConstI32 and fix all usages

This commit is contained in:
Zalim Bashorov
2022-12-06 20:34:47 +01:00
parent cdc1d66b1f
commit f2f4c63720
6 changed files with 147 additions and 127 deletions
@@ -13,6 +13,7 @@ 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.lower.PrimaryConstructorLowering
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.getSourceLocation
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
@@ -97,9 +98,11 @@ class BodyGenerator(
val constantArrayId = context.referenceConstantArray(resource)
body.buildConstI32(0)
body.buildConstI32(irVararg.elements.size)
body.buildInstr(WasmOp.ARRAY_NEW_DATA, wasmArrayType, WasmImmediate.DataIdx(constantArrayId))
withLocation(irVararg.getSourceLocation()) {
body.buildConstI32(0, location)
body.buildConstI32(irVararg.elements.size, location)
body.buildInstr(WasmOp.ARRAY_NEW_DATA, wasmArrayType, WasmImmediate.DataIdx(constantArrayId))
}
return true
}
@@ -125,7 +128,7 @@ class BodyGenerator(
check(wasmArrayType != null)
generateAnyParameters(arrayClass.symbol)
generateAnyParameters(arrayClass.symbol, expression.getSourceLocation())
if (!tryGenerateConstVarargArray(expression, wasmArrayType)) tryGenerateVarargArray(expression, wasmArrayType)
body.buildStructNew(context.referenceGcType(expression.type.getRuntimeClass(irBuiltIns).symbol))
}
@@ -274,7 +277,7 @@ class BodyGenerator(
}
if (expression.symbol.owner.hasWasmPrimitiveConstructorAnnotation()) {
generateAnyParameters(klassSymbol)
generateAnyParameters(klassSymbol, expression.getSourceLocation())
for (i in 0 until expression.valueArgumentsCount) {
generateExpression(expression.getValueArgument(i)!!)
}
@@ -287,7 +290,7 @@ class BodyGenerator(
generateCall(expression)
}
private fun generateAnyParameters(klassSymbol: IrClassSymbol) {
private fun generateAnyParameters(klassSymbol: IrClassSymbol, location: SourceLocation) {
//ClassITable and VTable load
body.commentGroupStart { "Any parameters" }
body.buildGetGlobal(context.referenceGlobalVTable(klassSymbol))
@@ -298,7 +301,7 @@ class BodyGenerator(
}
body.buildConstI32Symbol(context.referenceClassId(klassSymbol))
body.buildConstI32(0) // Any::_hashCode
body.buildConstI32(0, location) // Any::_hashCode
body.commentGroupEnd()
}
@@ -311,7 +314,7 @@ class BodyGenerator(
body.buildGetLocal(thisParameter)
body.buildInstr(WasmOp.REF_IS_NULL)
body.buildIf("this_init")
generateAnyParameters(parentClass.symbol)
generateAnyParameters(parentClass.symbol, SourceLocation.NoLocation("Constructor preamble"))
val irFields: List<IrField> = parentClass.allFields(backendContext.irBuiltIns)
irFields.forEachIndexed { index, field ->
if (index > 1) {
@@ -339,7 +342,7 @@ class BodyGenerator(
private fun generateBox(expression: IrExpression, type: IrType) {
val klassSymbol = type.getRuntimeClass(irBuiltIns).symbol
generateAnyParameters(klassSymbol)
generateAnyParameters(klassSymbol, expression.getSourceLocation())
generateExpression(expression)
body.buildStructNew(context.referenceGcType(klassSymbol))
body.commentPreviousInstr { "box" }
@@ -459,14 +462,14 @@ class BodyGenerator(
}
}
private fun generateRefTest(fromType: IrType, toType: IrType) {
private fun generateRefTest(fromType: IrType, toType: IrType, location: SourceLocation) {
if (!isDownCastAlwaysSuccessInRuntime(fromType, toType)) {
body.buildRefTestStatic(
toType = context.referenceGcType(toType.getRuntimeClass(irBuiltIns).symbol)
)
} else {
body.buildDrop()
body.buildConstI32(1)
body.buildConstI32(1, location)
}
}
@@ -488,109 +491,115 @@ class BodyGenerator(
return true
}
when (function.symbol) {
wasmSymbols.wasmClassId -> {
val klass = call.getTypeArgument(0)!!.getClass()
?: error("No class given for wasmClassId intrinsic")
assert(!klass.isInterface)
body.buildConstI32Symbol(context.referenceClassId(klass.symbol))
}
withLocation(call.getSourceLocation()) {
when (function.symbol) {
wasmSymbols.wasmClassId -> {
val klass = call.getTypeArgument(0)!!.getClass()
?: error("No class given for wasmClassId intrinsic")
assert(!klass.isInterface)
body.buildConstI32Symbol(context.referenceClassId(klass.symbol))
}
wasmSymbols.wasmInterfaceId -> {
val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic")
assert(irInterface.isInterface)
body.buildConstI32Symbol(context.referenceInterfaceId(irInterface.symbol))
}
wasmSymbols.wasmInterfaceId -> {
val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic")
assert(irInterface.isInterface)
body.buildConstI32Symbol(context.referenceInterfaceId(irInterface.symbol))
}
wasmSymbols.wasmIsInterface -> {
val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic")
assert(irInterface.isInterface)
if (irInterface.symbol in hierarchyDisjointUnions) {
val classITable = context.referenceClassITableGcType(irInterface.symbol)
val parameterLocal = functionContext.referenceLocal(SyntheticLocalType.IS_INTERFACE_PARAMETER)
body.buildSetLocal(parameterLocal)
body.buildBlock("isInterface", WasmI32) { outerLabel ->
body.buildBlock("isInterface", WasmRefNullType(WasmHeapType.Simple.Data)) { innerLabel ->
body.buildGetLocal(parameterLocal)
body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(1))
body.buildBrInstr(WasmOp.BR_ON_CAST_FAIL_DEPRECATED, innerLabel, classITable)
body.buildStructGet(classITable, context.referenceClassITableInterfaceSlot(irInterface.symbol))
body.buildInstr(WasmOp.REF_IS_NULL)
body.buildInstr(WasmOp.I32_EQZ)
body.buildBr(outerLabel)
wasmSymbols.wasmIsInterface -> {
val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic")
assert(irInterface.isInterface)
if (irInterface.symbol in hierarchyDisjointUnions) {
val classITable = context.referenceClassITableGcType(irInterface.symbol)
val parameterLocal = functionContext.referenceLocal(SyntheticLocalType.IS_INTERFACE_PARAMETER)
body.buildSetLocal(parameterLocal)
body.buildBlock("isInterface", WasmI32) { outerLabel ->
body.buildBlock("isInterface", WasmRefNullType(WasmHeapType.Simple.Data)) { innerLabel ->
body.buildGetLocal(parameterLocal)
body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(1))
body.buildBrInstr(WasmOp.BR_ON_CAST_FAIL_DEPRECATED, innerLabel, classITable)
body.buildStructGet(classITable, context.referenceClassITableInterfaceSlot(irInterface.symbol))
body.buildInstr(WasmOp.REF_IS_NULL)
body.buildInstr(WasmOp.I32_EQZ)
body.buildBr(outerLabel)
}
body.buildDrop()
body.buildConstI32(0, location)
}
} else {
body.buildDrop()
body.buildConstI32(0)
body.buildConstI32(0, location)
}
} else {
body.buildDrop()
body.buildConstI32(0)
}
}
wasmSymbols.refCastNull -> {
generateRefNullCast(
fromType = call.getValueArgument(0)!!.type,
toType = call.getTypeArgument(0)!!
)
}
wasmSymbols.refTest -> {
generateRefTest(
fromType = call.getValueArgument(0)!!.type,
toType = call.getTypeArgument(0)!!
)
}
wasmSymbols.unboxIntrinsic -> {
val fromType = call.getTypeArgument(0)!!
if (fromType.isNothing()) {
body.buildUnreachable()
// TODO: Investigate why?
return true
}
// Workaround test codegen/box/elvis/nullNullOk.kt
if (fromType.makeNotNull().isNothing()) {
body.buildUnreachable()
return true
wasmSymbols.refCastNull -> {
generateRefNullCast(
fromType = call.getValueArgument(0)!!.type,
toType = call.getTypeArgument(0)!!
)
}
val toType = call.getTypeArgument(1)!!
val klass: IrClass = backendContext.inlineClassesUtils.getInlinedClass(toType)!!
val field = getInlineClassBackingField(klass)
wasmSymbols.refTest -> {
generateRefTest(
fromType = call.getValueArgument(0)!!.type,
toType = call.getTypeArgument(0)!!,
location
)
}
generateRefNullCast(fromType, toType)
generateInstanceFieldAccess(field)
}
wasmSymbols.unboxIntrinsic -> {
val fromType = call.getTypeArgument(0)!!
wasmSymbols.unsafeGetScratchRawMemory -> {
body.buildConstI32Symbol(context.scratchMemAddr)
}
if (fromType.isNothing()) {
body.buildUnreachable()
// TODO: Investigate why?
return true
}
wasmSymbols.wasmArrayCopy -> {
val immediate = WasmImmediate.GcType(
context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol)
)
body.buildInstr(WasmOp.ARRAY_COPY, immediate, immediate)
}
// Workaround test codegen/box/elvis/nullNullOk.kt
if (fromType.makeNotNull().isNothing()) {
body.buildUnreachable()
return true
}
wasmSymbols.stringGetPoolSize -> {
body.buildConstI32Symbol(context.stringPoolSize)
}
val toType = call.getTypeArgument(1)!!
val klass: IrClass = backendContext.inlineClassesUtils.getInlinedClass(toType)!!
val field = getInlineClassBackingField(klass)
wasmSymbols.wasmArrayNewData0 -> {
val arrayGcType = WasmImmediate.GcType(
context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol)
)
body.buildInstr(WasmOp.ARRAY_NEW_DATA, arrayGcType, WasmImmediate.DataIdx(0))
}
generateRefNullCast(fromType, toType)
generateInstanceFieldAccess(field)
}
else -> {
return false
wasmSymbols.unsafeGetScratchRawMemory -> {
body.buildConstI32Symbol(context.scratchMemAddr)
}
wasmSymbols.wasmArrayCopy -> {
val immediate = WasmImmediate.GcType(
context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol)
)
body.buildInstr(WasmOp.ARRAY_COPY, immediate, immediate)
}
wasmSymbols.stringGetPoolSize -> {
body.buildConstI32Symbol(context.stringPoolSize)
}
wasmSymbols.wasmArrayNewData0 -> {
val arrayGcType = WasmImmediate.GcType(
context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol)
)
body.buildInstr(WasmOp.ARRAY_NEW_DATA, arrayGcType, WasmImmediate.DataIdx(0))
}
else -> {
return false
}
}
}
return true
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.parentOrNull
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.wasm.ir.source.location.withLocation
import org.jetbrains.kotlin.wasm.ir.source.location.withNoLocation
class DeclarationGenerator(
val context: WasmModuleCodegenContext,
@@ -472,19 +473,22 @@ class DeclarationGenerator(
}
}
fun generateDefaultInitializerForType(type: WasmType, g: WasmExpressionBuilder) = when (type) {
WasmI32 -> g.buildConstI32(0)
WasmI64 -> g.buildConstI64(0)
WasmF32 -> g.buildConstF32(0f)
WasmF64 -> g.buildConstF64(0.0)
is WasmRefNullType -> g.buildRefNull(type.heapType)
is WasmRefNullNoneType -> g.buildRefNull(WasmHeapType.Simple.NullNone)
is WasmRefNullExternrefType -> g.buildRefNull(WasmHeapType.Simple.NullNoExtern)
is WasmAnyRef -> g.buildRefNull(WasmHeapType.Simple.Any)
is WasmExternRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
WasmUnreachableType -> error("Unreachable type can't be initialized")
else -> error("Unknown value type ${type.name}")
}
fun generateDefaultInitializerForType(type: WasmType, g: WasmExpressionBuilder) =
withNoLocation("Default initializer, usually don't require location") {
when (type) {
WasmI32 -> g.buildConstI32(0, location)
WasmI64 -> g.buildConstI64(0)
WasmF32 -> g.buildConstF32(0f)
WasmF64 -> g.buildConstF64(0.0)
is WasmRefNullType -> g.buildRefNull(type.heapType)
is WasmRefNullNoneType -> g.buildRefNull(WasmHeapType.Simple.NullNone)
is WasmRefNullExternrefType -> g.buildRefNull(WasmHeapType.Simple.NullNoExtern)
is WasmAnyRef -> g.buildRefNull(WasmHeapType.Simple.Any)
is WasmExternRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
WasmUnreachableType -> error("Unreachable type can't be initialized")
else -> error("Unknown value type ${type.name}")
}
}
fun IrFunction.getEffectiveValueParameters(): List<IrValueParameter> {
val implicitThis = if (this is IrConstructor) parentAsClass.thisReceiver!! else null
@@ -502,12 +506,12 @@ fun generateConstExpression(expression: IrConst<*>, body: WasmExpressionBuilder,
val bottomType = if (expression.type.getClass()?.isExternal == true) WasmRefNullExternrefType else WasmRefNullNoneType
body.buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(bottomType))
}
is IrConstKind.Boolean -> body.buildConstI32(if (kind.valueOf(expression)) 1 else 0)
is IrConstKind.Byte -> body.buildConstI32(kind.valueOf(expression).toInt())
is IrConstKind.Short -> body.buildConstI32(kind.valueOf(expression).toInt())
is IrConstKind.Int -> body.buildConstI32(kind.valueOf(expression))
is IrConstKind.Boolean -> body.buildConstI32(if (kind.valueOf(expression)) 1 else 0, location)
is IrConstKind.Byte -> body.buildConstI32(kind.valueOf(expression).toInt(), location)
is IrConstKind.Short -> body.buildConstI32(kind.valueOf(expression).toInt(), location)
is IrConstKind.Int -> body.buildConstI32(kind.valueOf(expression), location)
is IrConstKind.Long -> body.buildConstI64(kind.valueOf(expression))
is IrConstKind.Char -> body.buildConstI32(kind.valueOf(expression).code)
is IrConstKind.Char -> body.buildConstI32(kind.valueOf(expression).code, location)
is IrConstKind.Float -> body.buildConstF32(kind.valueOf(expression))
is IrConstKind.Double -> body.buildConstF64(kind.valueOf(expression))
is IrConstKind.String -> {
@@ -516,7 +520,7 @@ fun generateConstExpression(expression: IrConst<*>, body: WasmExpressionBuilder,
body.commentGroupStart { "const string: \"$stringValue\"" }
body.buildConstI32Symbol(literalPoolId)
body.buildConstI32Symbol(literalAddress)
body.buildConstI32(stringValue.length)
body.buildConstI32(stringValue.length, location)
body.buildCall(context.referenceFunction(context.backendContext.wasmSymbols.stringGetLiteral), location)
body.commentGroupEnd()
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.isElseBranch
import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation
private class ExtractedWhenCondition<T>(val condition: IrCall, val const: IrConst<T>)
private class ExtractedWhenBranch<T>(val conditions: List<ExtractedWhenCondition<T>>, val expression: IrExpression)
@@ -134,11 +135,13 @@ private fun BodyGenerator.createBinaryTable(selectorLocal: WasmLocal, intBranche
intBranches.flatMapIndexedTo(sortedCaseToBranchIndex) { index, branch -> branch.conditions.map { it.const.value to index } }
sortedCaseToBranchIndex.sortBy { it.first }
val location = SourceLocation.NoLocation("When's binary search infra")
val thenBody = { result: Int ->
body.buildConstI32(result)
body.buildConstI32(result, location)
}
val elseBody: () -> Unit = {
body.buildConstI32(intBranches.size)
body.buildConstI32(intBranches.size, location)
}
createBinaryTable(selectorLocal, WasmI32, sortedCaseToBranchIndex, 0, sortedCaseToBranchIndex.size, thenBody, elseBody)
}
@@ -240,11 +243,13 @@ private fun <T> BodyGenerator.createBinaryTable(
thenBody: (T) -> Unit,
elseBody: () -> Unit
) {
val location = SourceLocation.NoLocation("When's binary search infra")
val size = toExcl - fromIncl
if (size == 1) {
val (case, result) = sortedCases[fromIncl]
body.buildGetLocal(selectorLocal)
body.buildConstI32(case)
body.buildConstI32(case, location)
body.buildInstr(WasmOp.I32_EQ)
body.buildIf("binary_tree_branch", resultType)
thenBody(result)
@@ -257,7 +262,7 @@ private fun <T> BodyGenerator.createBinaryTable(
val border = fromIncl + size / 2
body.buildGetLocal(selectorLocal)
body.buildConstI32(sortedCases[border].first)
body.buildConstI32(sortedCases[border].first, location)
body.buildInstr(WasmOp.I32_LT_S)
body.buildIf("binary_tree_node", resultType)
createBinaryTable(selectorLocal, resultType, sortedCases, fromIncl, border, thenBody, elseBody)
@@ -299,6 +304,8 @@ private fun BodyGenerator.genTableIntSwitch(
brTable: List<Int>,
expectedType: IrType,
) {
val location = SourceLocation.NoLocation("When's binary search infra")
val baseBlockIndex = body.numberOfNestedBlocks
//expressions + else branch + br_table
repeat(branches.size + 2) {
@@ -308,7 +315,7 @@ private fun BodyGenerator.genTableIntSwitch(
resultType?.let { generateDefaultInitializerForType(it, body) } //stub value
body.buildGetLocal(selectorLocal)
if (shift != 0) {
body.buildConstI32(shift)
body.buildConstI32(shift, location)
body.buildInstr(WasmOp.I32_SUB)
}
body.buildInstr(
@@ -257,7 +257,7 @@ inline fun WasmCompiledModuleFragment.ReferencableAndDefinable<IrClassSymbol, Co
elements.mapTo(into) {
val id = address(wasmToIr.getValue(it))
val offset = mutableListOf<WasmInstr>()
WasmIrExpressionBuilder(offset).buildConstI32(id)
WasmIrExpressionBuilder(offset).buildConstI32(id, SourceLocation.NoLocation("Compile time data per class"))
WasmData(WasmDataMode.Active(0, offset), it.toBytes())
}
}
@@ -65,8 +65,8 @@ sealed class WasmDataMode {
val memoryIdx: Int,
val offset: MutableList<WasmInstr>
) : WasmDataMode() {
constructor(memoryIdx: Int, offset: Int) : this(memoryIdx, mutableListOf<WasmInstr>().also<MutableList<WasmInstr>> {
WasmIrExpressionBuilder(it).buildConstI32(offset)
constructor(memoryIdx: Int, offset: Int, location: SourceLocation) : this(memoryIdx, mutableListOf<WasmInstr>().also<MutableList<WasmInstr>> {
WasmIrExpressionBuilder(it).buildConstI32(offset, location)
})
}
@@ -16,7 +16,7 @@ abstract class WasmExpressionBuilder {
abstract var numberOfNestedBlocks: Int
fun buildConstI32(value: Int, location: SourceLocation = SourceLocation.TBDLocation) {
fun buildConstI32(value: Int, location: SourceLocation) {
buildInstr(WasmOp.I32_CONST, location, WasmImmediate.ConstI32(value))
}