[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 kTypeClass: IrClassSymbol = getIrClass(FqName("kotlin.reflect.KClass"))
val getTypeInfoTypeDataByPtr: IrSimpleFunctionSymbol = getInternalFunction("getTypeInfoTypeDataByPtr")
val wasmTypeInfoData: IrClassSymbol = getInternalClass("TypeInfoData")
}
@@ -208,8 +209,7 @@ class WasmSymbols(
val suiteFun = maybeGetFunction("suite", kotlinTestPackage)
val startUnitTests = maybeGetFunction("startUnitTests", kotlinTestPackage)
val wasmClassId = getInternalFunction("wasmClassId")
val wasmInterfaceId = getInternalFunction("wasmInterfaceId")
val wasmTypeId = getInternalFunction("wasmTypeId")
val wasmIsInterface = getInternalFunction("wasmIsInterface")
@@ -56,8 +56,7 @@ internal class WasmUsefulDeclarationProcessor(
true
}
context.wasmSymbols.wasmClassId,
context.wasmSymbols.wasmInterfaceId,
context.wasmSymbols.wasmTypeId,
context.wasmSymbols.refCastNull,
context.wasmSymbols.refTest,
context.wasmSymbols.boxIntrinsic,
@@ -317,7 +317,7 @@ class BodyGenerator(
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.commentGroupEnd()
}
@@ -519,23 +519,15 @@ class BodyGenerator(
val location = call.getSourceLocation()
when (function.symbol) {
wasmSymbols.wasmClassId -> {
wasmSymbols.wasmTypeId -> {
val klass = call.getTypeArgument(0)!!.getClass()
?: error("No class given for wasmClassId intrinsic")
assert(!klass.isInterface)
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)
?: error("No class given for wasmTypeId intrinsic")
body.buildConstI32Symbol(context.referenceTypeId(klass.symbol), location)
}
wasmSymbols.wasmIsInterface -> {
val irInterface = call.getTypeArgument(0)!!.getClass()
?: error("No interface given for wasmInterfaceId intrinsic")
?: error("No interface given for wasmIsInterface intrinsic")
assert(irInterface.isInterface)
if (irInterface.symbol in hierarchyDisjointUnions) {
val classITable = context.referenceClassITableGcType(irInterface.symbol)
@@ -416,7 +416,7 @@ class DeclarationGenerator(
val superClass = classMetadata.klass.getSuperClass(context.backendContext.irBuiltIns)
val superTypeId = superClass?.let {
ConstantDataIntField("SuperTypeId", context.referenceClassId(it.symbol))
ConstantDataIntField("SuperTypeId", context.referenceTypeId(it.symbol))
} ?: ConstantDataIntField("SuperTypeId", -1)
val typeInfoContent = mutableListOf(typeInfo, superTypeId)
@@ -435,7 +435,7 @@ class DeclarationGenerator(
val size = ConstantDataIntField("size", interfaces.size)
val interfaceIds = ConstantDataIntArray(
"interfaceIds",
interfaces.map { context.referenceInterfaceId(it.symbol) },
interfaces.map { context.referenceTypeId(it.symbol) },
)
return ConstantDataStruct(
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
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.source.location.SourceLocation
@@ -33,9 +34,7 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
ReferencableAndDefinable<IrClassSymbol, WasmTypeDeclaration>()
val classITableInterfaceSlot =
ReferencableAndDefinable<IrClassSymbol, Int>()
val classIds =
ReferencableElements<IrClassSymbol, Int>()
val interfaceId =
val typeIds =
ReferencableElements<IrClassSymbol, Int>()
val stringLiteralAddress =
ReferencableElements<String, Int>()
@@ -119,20 +118,20 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
wasmSymbol.bind(canonicalFunctionTypes.getValue(functionTypes.defined.getValue(irSymbol)))
}
val klassIds = mutableMapOf<IrClassSymbol, Int>()
var currentDataSectionAddress = 0
for (typeInfoElement in typeInfo.elements) {
val ir = typeInfo.wasmToIr.getValue(typeInfoElement)
klassIds[ir] = currentDataSectionAddress
currentDataSectionAddress += typeInfoElement.sizeInBytes
var interfaceId = 0
typeIds.unbound.forEach { (klassSymbol, wasmSymbol) ->
if (klassSymbol.owner.isInterface) {
interfaceId--
wasmSymbol.bind(interfaceId)
} else {
wasmSymbol.bind(currentDataSectionAddress)
currentDataSectionAddress += typeInfo.defined.getValue(klassSymbol).sizeInBytes
}
}
currentDataSectionAddress = alignUp(currentDataSectionAddress, INT_SIZE_BYTES)
scratchMemAddr.bind(currentDataSectionAddress)
bind(classIds.unbound, klassIds)
interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) }
val stringDataSectionBytes = mutableListOf<Byte>()
var stringDataSectionStart = 0
var stringLiteralCount = 0
@@ -160,9 +159,23 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val constData = ConstantDataIntegerArray("constant_array", constantArraySegment.first, integerSize)
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))
with(WasmIrExpressionBuilder(masterInitFunction.instructions)) {
initFunctions.sortedBy { it.priority }.forEach {
@@ -250,18 +263,6 @@ private fun irSymbolDebugDump(symbol: Any?): String =
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 {
assert(alignment and (alignment - 1) == 0) { "power of 2 expected" }
return (x + alignment - 1) and (alignment - 1).inv()
@@ -177,12 +177,8 @@ class WasmModuleCodegenContext(
fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> =
wasmFragment.functionTypes.reference(irFunction)
fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int> =
wasmFragment.classIds.reference(irClass)
fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int> {
return wasmFragment.interfaceId.reference(irInterface)
}
fun referenceTypeId(irClass: IrClassSymbol): WasmSymbol<Int> =
wasmFragment.typeIds.reference(irClass)
fun getStructFieldRef(field: IrField): WasmSymbol<Int> {
val klass = field.parentAsClass
@@ -90,19 +90,15 @@ private fun IrBuilderWithScope.createAssociatedObjectAdd(
0,
irGet(wasmSymbols.initAssociatedObjects.owner.valueParameters[0])
)
val classIdGetter = when (targetClass.owner.isInterface) {
true -> wasmSymbols.wasmInterfaceId
false -> wasmSymbols.wasmClassId
}
addCall.putValueArgument(
1,
irCall(classIdGetter, irBuiltIns.intType).also {
irCall(wasmSymbols.wasmTypeId, irBuiltIns.intType).also {
it.putTypeArgument(0, targetClass.defaultType)
}
)
addCall.putValueArgument(
2,
irCall(wasmSymbols.wasmClassId, irBuiltIns.intType).also {
irCall(wasmSymbols.wasmTypeId, irBuiltIns.intType).also {
it.putTypeArgument(0, keyAnnotation.defaultType)
}
)
@@ -150,26 +150,31 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
return irCall(call, newSymbol, argumentsAsReceivers = true)
}
symbols.reflectionSymbols.getClassData -> {
val infoDataCtor = symbols.reflectionSymbols.wasmTypeInfoData.constructors.first()
val type = call.getTypeArgument(0)!!
val isInterface = type.isInterface()
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()
val klass = type.classOrNull?.owner ?: error("Invalid type")
return with(builder) {
val wasmIdGetter = if (type.isInterface()) symbols.wasmInterfaceId else symbols.wasmClassId
val typeId = irCall(wasmIdGetter).also {
it.putTypeArgument(0, type)
}
val typeId = builder.irCall(symbols.wasmTypeId).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(1, isInterface.toIrConst(context.irBuiltIns.booleanType))
it.putValueArgument(2, packageName.toIrConst(context.irBuiltIns.stringType))
it.putValueArgument(3, typeName.toIrConst(context.irBuiltIns.stringType))
}
} else {
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_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 {
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 packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength)
val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength)
return TypeInfoData(typeInfoPtr, isInterface = false, packageName, simpleName)
return TypeInfoData(typeInfoPtr, packageName, simpleName)
}
internal fun getSuperTypeId(typeInfoPtr: Int): Int =
@@ -57,11 +60,7 @@ internal fun <T> wasmIsInterface(obj: Any): Boolean =
implementedAsIntrinsic
@ExcludedFromCodegen
internal fun <T> wasmClassId(): Int =
implementedAsIntrinsic
@ExcludedFromCodegen
internal fun <T> wasmInterfaceId(): Int =
internal fun <T> wasmTypeId(): Int =
implementedAsIntrinsic
@ExcludedFromCodegen
@@ -5,7 +5,7 @@
package kotlin.reflect
import kotlin.wasm.internal.wasmClassId
import kotlin.wasm.internal.wasmTypeId
import kotlin.wasm.internal.findAssociatedObject
/**
@@ -40,4 +40,4 @@ public annotation class AssociatedObjectKey
@ExperimentalAssociatedObjects
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
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.getSuperTypeId
import kotlin.wasm.internal.isInterfaceById
import kotlin.wasm.internal.isInterfaceType
internal object NothingKClassImpl : KClass<Nothing> {
override val simpleName: String = "Nothing"
@@ -37,12 +38,16 @@ internal class KClassImpl<T : Any>(internal val typeData: TypeInfoData) : KClass
return false
}
override fun isInstance(value: Any?): Boolean = value?.let {
if (typeData.isInterface) isInterfaceById(it, typeData.typeId) else checkSuperTypeInstance(it)
} ?: false
override fun isInstance(value: Any?): Boolean {
if (value !is Any) return false
return when (typeData.isInterfaceType) {
true -> isInterfaceById(value, typeData.typeId)
false -> checkSuperTypeInstance(value)
}
}
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
@@ -33,6 +33,7 @@ fun main(args: Array<String>) {
)
)
model("box/jsQualifier/", pattern = jsTranslatorTestPattern, targetBackend = TargetBackend.WASM)
model("box/reflection/", pattern = "^(findAssociatedObject(InSeparatedFile)?)\\.kt$", targetBackend = TargetBackend.WASM)
}
testClass<AbstractJsTranslatorUnitWasmTest> {
@@ -363,4 +363,27 @@ public class JsTranslatorWasmTestGenerated extends AbstractJsTranslatorWasmTest
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");
}
}
}