[Wasm] Replace ClassId and InterfaceId with single TypeId

This commit is contained in:
Igor Yakovlev
2023-03-30 20:27:47 +02:00
committed by Space Team
parent 5a46cb1c40
commit 49beec33b4
13 changed files with 103 additions and 86 deletions
@@ -63,6 +63,7 @@ class WasmSymbols(
override val primitiveClassesObject = getInternalClass("PrimitiveClasses") override val primitiveClassesObject = getInternalClass("PrimitiveClasses")
override val kTypeClass: IrClassSymbol = getIrClass(FqName("kotlin.reflect.KClass")) override val kTypeClass: IrClassSymbol = getIrClass(FqName("kotlin.reflect.KClass"))
val getTypeInfoTypeDataByPtr: IrSimpleFunctionSymbol = getInternalFunction("getTypeInfoTypeDataByPtr")
val wasmTypeInfoData: IrClassSymbol = getInternalClass("TypeInfoData") val wasmTypeInfoData: IrClassSymbol = getInternalClass("TypeInfoData")
} }
@@ -208,8 +209,7 @@ class WasmSymbols(
val suiteFun = maybeGetFunction("suite", kotlinTestPackage) val suiteFun = maybeGetFunction("suite", kotlinTestPackage)
val startUnitTests = maybeGetFunction("startUnitTests", kotlinTestPackage) val startUnitTests = maybeGetFunction("startUnitTests", kotlinTestPackage)
val wasmClassId = getInternalFunction("wasmClassId") val wasmTypeId = getInternalFunction("wasmTypeId")
val wasmInterfaceId = getInternalFunction("wasmInterfaceId")
val wasmIsInterface = getInternalFunction("wasmIsInterface") val wasmIsInterface = getInternalFunction("wasmIsInterface")
@@ -56,8 +56,7 @@ internal class WasmUsefulDeclarationProcessor(
true true
} }
context.wasmSymbols.wasmClassId, context.wasmSymbols.wasmTypeId,
context.wasmSymbols.wasmInterfaceId,
context.wasmSymbols.refCastNull, context.wasmSymbols.refCastNull,
context.wasmSymbols.refTest, context.wasmSymbols.refTest,
context.wasmSymbols.boxIntrinsic, context.wasmSymbols.boxIntrinsic,
@@ -317,7 +317,7 @@ class BodyGenerator(
body.buildRefNull(WasmHeapType.Simple.Data, location) body.buildRefNull(WasmHeapType.Simple.Data, location)
} }
body.buildConstI32Symbol(context.referenceClassId(klassSymbol), location) body.buildConstI32Symbol(context.referenceTypeId(klassSymbol), location)
body.buildConstI32(0, location) // Any::_hashCode body.buildConstI32(0, location) // Any::_hashCode
body.commentGroupEnd() body.commentGroupEnd()
} }
@@ -519,23 +519,15 @@ class BodyGenerator(
val location = call.getSourceLocation() val location = call.getSourceLocation()
when (function.symbol) { when (function.symbol) {
wasmSymbols.wasmClassId -> { wasmSymbols.wasmTypeId -> {
val klass = call.getTypeArgument(0)!!.getClass() val klass = call.getTypeArgument(0)!!.getClass()
?: error("No class given for wasmClassId intrinsic") ?: error("No class given for wasmTypeId intrinsic")
assert(!klass.isInterface) body.buildConstI32Symbol(context.referenceTypeId(klass.symbol), location)
body.buildConstI32Symbol(context.referenceClassId(klass.symbol), location)
}
wasmSymbols.wasmInterfaceId -> {
val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic")
assert(irInterface.isInterface)
body.buildConstI32Symbol(context.referenceInterfaceId(irInterface.symbol), location)
} }
wasmSymbols.wasmIsInterface -> { wasmSymbols.wasmIsInterface -> {
val irInterface = call.getTypeArgument(0)!!.getClass() val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic") ?: error("No interface given for wasmIsInterface intrinsic")
assert(irInterface.isInterface) assert(irInterface.isInterface)
if (irInterface.symbol in hierarchyDisjointUnions) { if (irInterface.symbol in hierarchyDisjointUnions) {
val classITable = context.referenceClassITableGcType(irInterface.symbol) val classITable = context.referenceClassITableGcType(irInterface.symbol)
@@ -416,7 +416,7 @@ class DeclarationGenerator(
val superClass = classMetadata.klass.getSuperClass(context.backendContext.irBuiltIns) val superClass = classMetadata.klass.getSuperClass(context.backendContext.irBuiltIns)
val superTypeId = superClass?.let { val superTypeId = superClass?.let {
ConstantDataIntField("SuperTypeId", context.referenceClassId(it.symbol)) ConstantDataIntField("SuperTypeId", context.referenceTypeId(it.symbol))
} ?: ConstantDataIntField("SuperTypeId", -1) } ?: ConstantDataIntField("SuperTypeId", -1)
val typeInfoContent = mutableListOf(typeInfo, superTypeId) val typeInfoContent = mutableListOf(typeInfo, superTypeId)
@@ -435,7 +435,7 @@ class DeclarationGenerator(
val size = ConstantDataIntField("size", interfaces.size) val size = ConstantDataIntField("size", interfaces.size)
val interfaceIds = ConstantDataIntArray( val interfaceIds = ConstantDataIntArray(
"interfaceIds", "interfaceIds",
interfaces.map { context.referenceInterfaceId(it.symbol) }, interfaces.map { context.referenceTypeId(it.symbol) },
) )
return ConstantDataStruct( return ConstantDataStruct(
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.getPackageFragment import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.wasm.ir.* import org.jetbrains.kotlin.wasm.ir.*
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation
@@ -33,9 +34,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>() ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val classITableInterfaceSlot = val classITableInterfaceSlot =
ReferencableAndDefinable<IrClassSymbol, Int>() ReferencableAndDefinable<IrClassSymbol, Int>()
val classIds = val typeIds =
ReferencableElements<IrClassSymbol, Int>()
val interfaceId =
ReferencableElements<IrClassSymbol, Int>() ReferencableElements<IrClassSymbol, Int>()
val stringLiteralAddress = val stringLiteralAddress =
ReferencableElements<String, Int>() ReferencableElements<String, Int>()
@@ -119,20 +118,20 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
wasmSymbol.bind(canonicalFunctionTypes.getValue(functionTypes.defined.getValue(irSymbol))) wasmSymbol.bind(canonicalFunctionTypes.getValue(functionTypes.defined.getValue(irSymbol)))
} }
val klassIds = mutableMapOf<IrClassSymbol, Int>()
var currentDataSectionAddress = 0 var currentDataSectionAddress = 0
for (typeInfoElement in typeInfo.elements) { var interfaceId = 0
val ir = typeInfo.wasmToIr.getValue(typeInfoElement) typeIds.unbound.forEach { (klassSymbol, wasmSymbol) ->
klassIds[ir] = currentDataSectionAddress if (klassSymbol.owner.isInterface) {
currentDataSectionAddress += typeInfoElement.sizeInBytes interfaceId--
wasmSymbol.bind(interfaceId)
} else {
wasmSymbol.bind(currentDataSectionAddress)
currentDataSectionAddress += typeInfo.defined.getValue(klassSymbol).sizeInBytes
}
} }
currentDataSectionAddress = alignUp(currentDataSectionAddress, INT_SIZE_BYTES) currentDataSectionAddress = alignUp(currentDataSectionAddress, INT_SIZE_BYTES)
scratchMemAddr.bind(currentDataSectionAddress) scratchMemAddr.bind(currentDataSectionAddress)
bind(classIds.unbound, klassIds)
interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) }
val stringDataSectionBytes = mutableListOf<Byte>() val stringDataSectionBytes = mutableListOf<Byte>()
var stringDataSectionStart = 0 var stringDataSectionStart = 0
var stringLiteralCount = 0 var stringLiteralCount = 0
@@ -160,9 +159,23 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val constData = ConstantDataIntegerArray("constant_array", constantArraySegment.first, integerSize) val constData = ConstantDataIntegerArray("constant_array", constantArraySegment.first, integerSize)
data.add(WasmData(WasmDataMode.Passive, constData.toBytes())) data.add(WasmData(WasmDataMode.Passive, constData.toBytes()))
} }
typeInfo.buildData(data, address = { klassIds.getValue(it) })
val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList()) typeIds.unbound.forEach { (klassSymbol, typeId) ->
if (!klassSymbol.owner.isInterface) {
val instructions = mutableListOf<WasmInstr>()
WasmIrExpressionBuilder(instructions).buildConstI32(
typeId.owner,
SourceLocation.NoLocation("Compile time data per class")
)
val typeData = WasmData(
WasmDataMode.Active(0, instructions),
typeInfo.defined.getValue(klassSymbol).toBytes()
)
data.add(typeData)
}
}
val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList())
val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType)) val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType))
with(WasmIrExpressionBuilder(masterInitFunction.instructions)) { with(WasmIrExpressionBuilder(masterInitFunction.instructions)) {
initFunctions.sortedBy { it.priority }.forEach { initFunctions.sortedBy { it.priority }.forEach {
@@ -250,18 +263,6 @@ private fun irSymbolDebugDump(symbol: Any?): String =
else -> symbol.toString() else -> symbol.toString()
} }
inline fun WasmCompiledModuleFragment.ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>.buildData(
into: MutableList<WasmData>,
address: (IrClassSymbol) -> Int
) {
elements.mapTo(into) {
val id = address(wasmToIr.getValue(it))
val offset = mutableListOf<WasmInstr>()
WasmIrExpressionBuilder(offset).buildConstI32(id, SourceLocation.NoLocation("Compile time data per class"))
WasmData(WasmDataMode.Active(0, offset), it.toBytes())
}
}
fun alignUp(x: Int, alignment: Int): Int { fun alignUp(x: Int, alignment: Int): Int {
assert(alignment and (alignment - 1) == 0) { "power of 2 expected" } assert(alignment and (alignment - 1) == 0) { "power of 2 expected" }
return (x + alignment - 1) and (alignment - 1).inv() return (x + alignment - 1) and (alignment - 1).inv()
@@ -177,12 +177,8 @@ class WasmModuleCodegenContext(
fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> = fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> =
wasmFragment.functionTypes.reference(irFunction) wasmFragment.functionTypes.reference(irFunction)
fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int> = fun referenceTypeId(irClass: IrClassSymbol): WasmSymbol<Int> =
wasmFragment.classIds.reference(irClass) wasmFragment.typeIds.reference(irClass)
fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int> {
return wasmFragment.interfaceId.reference(irInterface)
}
fun getStructFieldRef(field: IrField): WasmSymbol<Int> { fun getStructFieldRef(field: IrField): WasmSymbol<Int> {
val klass = field.parentAsClass val klass = field.parentAsClass
@@ -90,19 +90,15 @@ private fun IrBuilderWithScope.createAssociatedObjectAdd(
0, 0,
irGet(wasmSymbols.initAssociatedObjects.owner.valueParameters[0]) irGet(wasmSymbols.initAssociatedObjects.owner.valueParameters[0])
) )
val classIdGetter = when (targetClass.owner.isInterface) {
true -> wasmSymbols.wasmInterfaceId
false -> wasmSymbols.wasmClassId
}
addCall.putValueArgument( addCall.putValueArgument(
1, 1,
irCall(classIdGetter, irBuiltIns.intType).also { irCall(wasmSymbols.wasmTypeId, irBuiltIns.intType).also {
it.putTypeArgument(0, targetClass.defaultType) it.putTypeArgument(0, targetClass.defaultType)
} }
) )
addCall.putValueArgument( addCall.putValueArgument(
2, 2,
irCall(wasmSymbols.wasmClassId, irBuiltIns.intType).also { irCall(wasmSymbols.wasmTypeId, irBuiltIns.intType).also {
it.putTypeArgument(0, keyAnnotation.defaultType) it.putTypeArgument(0, keyAnnotation.defaultType)
} }
) )
@@ -150,26 +150,31 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
return irCall(call, newSymbol, argumentsAsReceivers = true) return irCall(call, newSymbol, argumentsAsReceivers = true)
} }
symbols.reflectionSymbols.getClassData -> { symbols.reflectionSymbols.getClassData -> {
val infoDataCtor = symbols.reflectionSymbols.wasmTypeInfoData.constructors.first()
val type = call.getTypeArgument(0)!! val type = call.getTypeArgument(0)!!
val isInterface = type.isInterface() val klass = type.classOrNull?.owner ?: error("Invalid type")
val fqName = type.classFqName!!
val fqnShouldBeEmitted =
context.configuration.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
val packageName = if (fqnShouldBeEmitted) fqName.parentOrNull()?.asString() ?: "" else ""
val typeName = fqName.shortName().asString()
return with(builder) { val typeId = builder.irCall(symbols.wasmTypeId).also {
val wasmIdGetter = if (type.isInterface()) symbols.wasmInterfaceId else symbols.wasmClassId it.putTypeArgument(0, type)
val typeId = irCall(wasmIdGetter).also { }
it.putTypeArgument(0, type)
}
irCallConstructor(infoDataCtor, emptyList()).also { if (!klass.isInterface) {
return builder.irCall(context.wasmSymbols.reflectionSymbols.getTypeInfoTypeDataByPtr).also {
it.putValueArgument(0, typeId) it.putValueArgument(0, typeId)
it.putValueArgument(1, isInterface.toIrConst(context.irBuiltIns.booleanType)) }
it.putValueArgument(2, packageName.toIrConst(context.irBuiltIns.stringType)) } else {
it.putValueArgument(3, typeName.toIrConst(context.irBuiltIns.stringType)) val infoDataCtor = symbols.reflectionSymbols.wasmTypeInfoData.constructors.first()
val fqName = type.classFqName!!
val fqnShouldBeEmitted =
context.configuration.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
val packageName = if (fqnShouldBeEmitted) fqName.parentOrNull()?.asString() ?: "" else ""
val typeName = fqName.shortName().asString()
return with(builder) {
irCallConstructor(infoDataCtor, emptyList()).also {
it.putValueArgument(0, typeId)
it.putValueArgument(1, packageName.toIrConst(context.irBuiltIns.stringType))
it.putValueArgument(2, typeName.toIrConst(context.irBuiltIns.stringType))
}
} }
} }
} }
@@ -19,7 +19,10 @@ internal const val TYPE_INFO_SUPER_TYPE_OFFSET = TYPE_INFO_TYPE_SIMPLE_NAME_PRT_
internal const val TYPE_INFO_ITABLE_SIZE_OFFSET = TYPE_INFO_SUPER_TYPE_OFFSET + TYPE_INFO_ELEMENT_SIZE internal const val TYPE_INFO_ITABLE_SIZE_OFFSET = TYPE_INFO_SUPER_TYPE_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_ITABLE_OFFSET = TYPE_INFO_ITABLE_SIZE_OFFSET + TYPE_INFO_ELEMENT_SIZE internal const val TYPE_INFO_ITABLE_OFFSET = TYPE_INFO_ITABLE_SIZE_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal class TypeInfoData(val typeId: Int, val isInterface: Boolean, val packageName: String, val typeName: String) internal class TypeInfoData(val typeId: Int, val packageName: String, val typeName: String)
internal val TypeInfoData.isInterfaceType
get() = typeId < 0
internal fun getTypeInfoTypeDataByPtr(typeInfoPtr: Int): TypeInfoData { internal fun getTypeInfoTypeDataByPtr(typeInfoPtr: Int): TypeInfoData {
val fqNameLength = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_PACKAGE_NAME_LENGTH_OFFSET) val fqNameLength = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_PACKAGE_NAME_LENGTH_OFFSET)
@@ -30,7 +33,7 @@ internal fun getTypeInfoTypeDataByPtr(typeInfoPtr: Int): TypeInfoData {
val simpleNamePtr = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET) val simpleNamePtr = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET)
val packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength) val packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength)
val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength) val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength)
return TypeInfoData(typeInfoPtr, isInterface = false, packageName, simpleName) return TypeInfoData(typeInfoPtr, packageName, simpleName)
} }
internal fun getSuperTypeId(typeInfoPtr: Int): Int = internal fun getSuperTypeId(typeInfoPtr: Int): Int =
@@ -57,11 +60,7 @@ internal fun <T> wasmIsInterface(obj: Any): Boolean =
implementedAsIntrinsic implementedAsIntrinsic
@ExcludedFromCodegen @ExcludedFromCodegen
internal fun <T> wasmClassId(): Int = internal fun <T> wasmTypeId(): Int =
implementedAsIntrinsic
@ExcludedFromCodegen
internal fun <T> wasmInterfaceId(): Int =
implementedAsIntrinsic implementedAsIntrinsic
@ExcludedFromCodegen @ExcludedFromCodegen
@@ -5,7 +5,7 @@
package kotlin.reflect package kotlin.reflect
import kotlin.wasm.internal.wasmClassId import kotlin.wasm.internal.wasmTypeId
import kotlin.wasm.internal.findAssociatedObject import kotlin.wasm.internal.findAssociatedObject
/** /**
@@ -40,4 +40,4 @@ public annotation class AssociatedObjectKey
@ExperimentalAssociatedObjects @ExperimentalAssociatedObjects
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
public inline fun <reified T : Annotation> KClass<*>.findAssociatedObject(): Any? = public inline fun <reified T : Annotation> KClass<*>.findAssociatedObject(): Any? =
findAssociatedObject(this, wasmClassId<T>()) findAssociatedObject(this, wasmTypeId<T>())
@@ -8,6 +8,7 @@ import kotlin.reflect.*
import kotlin.wasm.internal.TypeInfoData import kotlin.wasm.internal.TypeInfoData
import kotlin.wasm.internal.getSuperTypeId import kotlin.wasm.internal.getSuperTypeId
import kotlin.wasm.internal.isInterfaceById import kotlin.wasm.internal.isInterfaceById
import kotlin.wasm.internal.isInterfaceType
internal object NothingKClassImpl : KClass<Nothing> { internal object NothingKClassImpl : KClass<Nothing> {
override val simpleName: String = "Nothing" override val simpleName: String = "Nothing"
@@ -37,12 +38,16 @@ internal class KClassImpl<T : Any>(internal val typeData: TypeInfoData) : KClass
return false return false
} }
override fun isInstance(value: Any?): Boolean = value?.let { override fun isInstance(value: Any?): Boolean {
if (typeData.isInterface) isInterfaceById(it, typeData.typeId) else checkSuperTypeInstance(it) if (value !is Any) return false
} ?: false return when (typeData.isInterfaceType) {
true -> isInterfaceById(value, typeData.typeId)
false -> checkSuperTypeInstance(value)
}
}
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
(this === other) || (other is KClassImpl<*> && other.typeData.isInterface == typeData.isInterface && other.typeData.typeId == typeData.typeId) (this === other) || (other is KClassImpl<*> && other.typeData.typeId == typeData.typeId)
override fun hashCode(): Int = typeData.typeId override fun hashCode(): Int = typeData.typeId
@@ -33,6 +33,7 @@ fun main(args: Array<String>) {
) )
) )
model("box/jsQualifier/", pattern = jsTranslatorTestPattern, targetBackend = TargetBackend.WASM) model("box/jsQualifier/", pattern = jsTranslatorTestPattern, targetBackend = TargetBackend.WASM)
model("box/reflection/", pattern = "^(findAssociatedObject(InSeparatedFile)?)\\.kt$", targetBackend = TargetBackend.WASM)
} }
testClass<AbstractJsTranslatorUnitWasmTest> { testClass<AbstractJsTranslatorUnitWasmTest> {
@@ -363,4 +363,27 @@ public class JsTranslatorWasmTestGenerated extends AbstractJsTranslatorWasmTest
runTest("js/js.translator/testData/box/jsQualifier/simple.kt"); runTest("js/js.translator/testData/box/jsQualifier/simple.kt");
} }
} }
@TestMetadata("js/js.translator/testData/box/reflection")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Reflection extends AbstractJsTranslatorWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInReflection() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/reflection"), Pattern.compile("^(findAssociatedObject(InSeparatedFile)?)\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("findAssociatedObject.kt")
public void testFindAssociatedObject() throws Exception {
runTest("js/js.translator/testData/box/reflection/findAssociatedObject.kt");
}
@TestMetadata("findAssociatedObjectInSeparatedFile.kt")
public void testFindAssociatedObjectInSeparatedFile() throws Exception {
runTest("js/js.translator/testData/box/reflection/findAssociatedObjectInSeparatedFile.kt");
}
}
} }