From 46edc3595d03fa64423e46d77b304f06740a0a60 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Fri, 17 May 2019 14:51:56 +0300 Subject: [PATCH] Merged different RTTI-related structure builders This includes vTable, fields and associated objects --- .../jetbrains/kotlin/backend/konan/Context.kt | 6 +- ...tablesBuilder.kt => ClassLayoutBuilder.kt} | 78 +++++++++++++++++-- .../backend/konan/llvm/CodeGenerator.kt | 2 +- .../backend/konan/llvm/LlvmDeclarations.kt | 56 +------------ .../backend/konan/llvm/RTTIGenerator.kt | 49 ++---------- .../backend/konan/llvm/StaticObjects.kt | 2 +- .../objcexport/ObjCExportCodeGenerator.kt | 4 +- .../backend/konan/optimizations/DFGBuilder.kt | 3 +- .../backend/konan/optimizations/DataFlowIR.kt | 6 +- 9 files changed, 92 insertions(+), 114 deletions(-) rename backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/{ClassVtablesBuilder.kt => ClassLayoutBuilder.kt} (72%) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 11b6b8afdea..8f398c386ba 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -257,10 +257,10 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { KonanReflectionTypes(moduleDescriptor, KonanFqNames.internalPackageName) } - private val vtableBuilders = mutableMapOf() + val layoutBuilders = mutableMapOf() - fun getVtableBuilder(classDescriptor: IrClass) = vtableBuilders.getOrPut(classDescriptor) { - ClassVtablesBuilder(classDescriptor, this) + fun getLayoutBuilder(irClass: IrClass) = layoutBuilders.getOrPut(irClass) { + ClassLayoutBuilder(irClass, this) } // We serialize untouched descriptor tree and inline IR bodies diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassVtablesBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt similarity index 72% rename from backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassVtablesBuilder.kt rename to backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index 9335d26fba1..19b0383ab67 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassVtablesBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -12,11 +12,10 @@ import org.jetbrains.kotlin.backend.konan.llvm.functionName import org.jetbrains.kotlin.backend.konan.llvm.localHash import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction -import org.jetbrains.kotlin.ir.util.isInterface -import org.jetbrains.kotlin.ir.util.overrides +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrClassReference +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.FqName internal class OverriddenFunctionInfo( val function: IrSimpleFunction, @@ -78,7 +77,7 @@ internal class OverriddenFunctionInfo( } } -internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) { +internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { private val DEBUG = 0 private inline fun DEBUG_OUTPUT(severity: Int, block: () -> Unit) { @@ -98,7 +97,7 @@ internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) { emptyList() } else { val superClass = irClass.getSuperClassNotAny() ?: context.ir.symbols.any.owner - context.getVtableBuilder(superClass).vtableEntries + context.getLayoutBuilder(superClass).vtableEntries } val methods = irClass.sortedOverridableOrOverridingMethods @@ -172,6 +171,71 @@ internal class ClassVtablesBuilder(val irClass: IrClass, val context: Context) { // TODO: probably method table should contain all accessible methods to improve binary compatibility } + /** + * All fields of the class instance. + * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. + */ + val fields: List by lazy { + val superClass = irClass.getSuperClassNotAny() // TODO: what if Any has fields? + val superFields = if (superClass != null) context.getLayoutBuilder(superClass).fields else emptyList() + + superFields + getDeclaredFields() + } + + val associatedObjects by lazy { + val result = mutableMapOf() + + irClass.annotations.forEach { + val irFile = irClass.getContainingFile() + + val annotationClass = (it.symbol.owner as? IrConstructor)?.constructedClass + ?: error(irFile, it, "unexpected annotation") + + if (annotationClass.hasAnnotation(RuntimeNames.associatedObjectKey)) { + val argument = it.getValueArgument(0) + + val irClassReference = argument as? IrClassReference + ?: error(irFile, argument, "unexpected annotation argument") + + val associatedObject = irClassReference.symbol.owner + + if (associatedObject !is IrClass || !associatedObject.isObject) { + error(irFile, irClassReference, "argument is not a singleton") + } + + if (annotationClass in result) { + error( + irFile, + it, + "duplicate value for ${annotationClass.name}, previous was ${result[annotationClass]?.name}" + ) + } + + result[annotationClass] = associatedObject + } + } + + result + } + + /** + * Fields declared in the class. + */ + private fun getDeclaredFields(): List { + val fields = irClass.declarations.mapNotNull { + when (it) { + is IrField -> it.takeIf { it.isReal } + is IrProperty -> it.takeIf { it.isReal }?.backingField + else -> null + } + } + + if (irClass.hasAnnotation(FqName.fromSegments(listOf("kotlin", "native", "internal", "NoReorderFields")))) + return fields + + return fields.sortedBy { it.fqNameForIrSerialization.localHash.value } + } + private val IrClass.sortedOverridableOrOverridingMethods: List get() = this.simpleFunctions() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 78aadcd80c1..72e4983de7c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -722,7 +722,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, val llvmMethod = if (!owner.isInterface) { // If this is a virtual method of the class - we can call via vtable. - val index = context.getVtableBuilder(owner).vtableIndex(anyMethod ?: irFunction) + val index = context.getLayoutBuilder(owner).vtableIndex(anyMethod ?: irFunction) val vtablePlace = gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1 val vtable = bitcast(kInt8PtrPtr, vtablePlace) val slot = gep(vtable, Int32(index).llvm) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 1d9b21ee09d..06b3cb06b06 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -65,7 +65,6 @@ internal class LlvmDeclarations( internal class ClassLlvmDeclarations( val bodyType: LLVMTypeRef, - val fields: List, // TODO: it is not an LLVM declaration. val typeInfoGlobal: StaticData.Global, val writableTypeInfoGlobal: StaticData.Global?, val typeInfo: ConstPointer, @@ -88,53 +87,6 @@ internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef) internal class UniqueLlvmDeclarations(val pointer: ConstPointer) -// TODO: rework getFields and getDeclaredFields. - -/** - * All fields of the class instance. - * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. - */ -internal fun ContextUtils.getFields(irClass: IrClass) = context.getFields(irClass) - -internal fun Context.getFields(irClass: IrClass): List { - val superClass = irClass.getSuperClassNotAny() // TODO: what if Any has fields? - val superFields = if (superClass != null) getFields(superClass) else emptyList() - - return superFields + getDeclaredFields(irClass) -} - -/** - * Fields declared in the class. - */ -private fun Context.getDeclaredFields(irClass: IrClass): List { - // TODO: Here's what is going on here: - // The existence of a backing field for a property is only described in the IR, - // but not in the PropertyDescriptor. - // - // We mark serialized properties with a Konan protobuf extension bit, - // so it is present in DeserializedPropertyDescriptor. - // - // In this function we check the presence of the backing field - // two ways: first we check IR, then we check the protobuf extension. - - val fields = irClass.declarations.mapNotNull { - when (it) { - is IrField -> it.takeIf { it.isReal } - is IrProperty -> it.takeIf { it.isReal }?.konanBackingField - else -> null - } - } - // TODO: hack over missed parents in deserialized fields/property declarations. - fields.forEach{it.parent = irClass} - - if (irClass.hasAnnotation(FqName.fromSegments(listOf("kotlin", "native", "internal", "NoReorderFields")))) - return fields - - return fields.sortedBy { - it.fqNameForIrSerialization.localHash.value - } -} - private fun ContextUtils.createClassBodyType(name: String, fields: List): LLVMTypeRef { val fieldTypes = listOf(runtime.objHeaderType) + fields.map { getLLVMType(it.type) } // TODO: consider adding synthetic ObjHeader field to Any. @@ -213,7 +165,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : private fun createClassDeclarations(declaration: IrClass): ClassLlvmDeclarations { val internalName = qualifyInternalName(declaration) - val fields = getFields(declaration) + val fields = context.getLayoutBuilder(declaration).fields val bodyType = createClassBodyType("kclassbody:$internalName", fields) val typeInfoPtr: ConstPointer @@ -232,7 +184,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : val typeInfoWithVtableType = structType( runtime.typeInfoType, - LLVMArrayType(int8TypePtr, context.getVtableBuilder(declaration).vtableEntries.size)!! + LLVMArrayType(int8TypePtr, context.getLayoutBuilder(declaration).vtableEntries.size)!! ) typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false) @@ -290,7 +242,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : it.setZeroInitializer() } - return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr, + return ClassLlvmDeclarations(bodyType, typeInfoGlobal, writableTypeInfoGlobal, typeInfoPtr, singletonDeclarations, objCDeclarations) } @@ -367,7 +319,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : if (containingClass != null) { val classDeclarations = this.classes[containingClass] ?: error(containingClass.descriptor.toString()) - val allFields = classDeclarations.fields + val allFields = context.getLayoutBuilder(containingClass).fields this.fields[declaration] = FieldLlvmDeclarations( allFields.indexOf(declaration) + 1, // First field is ObjHeader. classDeclarations.bodyType diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index 4f19eb88451..d4251d95dda 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -14,7 +14,6 @@ import org.jetbrains.kotlin.backend.konan.ir.isAnonymousObject import org.jetbrains.kotlin.backend.konan.ir.isLocal import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.konan.KonanAbiVersion @@ -47,7 +46,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { private fun checkAcyclicClass(irClass: IrClass): Boolean = when { irClass.symbol == context.ir.symbols.array -> false irClass.isArray -> true - context.llvmDeclarations.forClass(irClass).fields.all { checkAcyclicFieldType(it.type) } -> true + context.getLayoutBuilder(irClass).fields.all { checkAcyclicFieldType(it.type) } -> true else -> false } @@ -248,7 +247,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { fun vtable(irClass: IrClass): ConstArray { // TODO: compile-time resolution limits binary compatibility. - val vtableEntries = context.getVtableBuilder(irClass).vtableEntries.map { + val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map { val implementation = it.implementation if (implementation == null || implementation.isExternalObjCClassMethod()) { NullPointer(int8Type) @@ -261,7 +260,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { fun methodTableRecords(irClass: IrClass): List { val functionNames = mutableMapOf() - return context.getVtableBuilder(irClass).methodTableEntries.map { + return context.getLayoutBuilder(irClass).methodTableEntries.map { val functionName = it.overriddenFunction.functionName val nameSignature = functionName.localHash val previous = functionNames.putIfAbsent(nameSignature.value, it) @@ -295,11 +294,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { NullPointer(int32Type), NullPointer(int8Type), NullPointer(kInt8Ptr)) } else { data class FieldRecord(val offset: Int, val type: Int, val name: String) - val fields = getStructElements(bodyType).drop(1).mapIndexedNotNull { index, type -> + val fields = getStructElements(bodyType).drop(1).mapIndexed { index, type -> FieldRecord( LLVMOffsetOfElement(llvmTargetData, bodyType, index + 1).toInt(), mapRuntimeType(type), - llvmDeclarations.fields[index].name.asString()) + context.getLayoutBuilder(irClass).fields[index].name.asString()) } val offsetsPtr = staticData.placeGlobalConstArray("kextoff:$className", int32Type, fields.map { Int32(it.offset) }) @@ -315,7 +314,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { } private fun genAssociatedObjects(irClass: IrClass): ConstPointer? { - val associatedObjects = getAssociatedObjects(irClass) + val associatedObjects = context.getLayoutBuilder(irClass).associatedObjects if (associatedObjects.isEmpty()) { return null } @@ -425,42 +424,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { } } -private fun getAssociatedObjects(irClass: IrClass): Map { - val result = mutableMapOf() - - irClass.annotations.forEach { - val irFile = irClass.getContainingFile() - - val annotationClass = (it.symbol.owner as? IrConstructor)?.constructedClass - ?: error(irFile, it, "unexpected annotation") - - if (annotationClass.hasAnnotation(RuntimeNames.associatedObjectKey)) { - val argument = it.getValueArgument(0) - - val irClassReference = argument as? IrClassReference - ?: error(irFile, argument, "unexpected annotation argument") - - val associatedObject = irClassReference.symbol.owner - - if (associatedObject !is IrClass || !associatedObject.isObject) { - error(irFile, irClassReference, "argument is not a singleton") - } - - if (annotationClass in result) { - error( - irFile, - it, - "duplicate value for ${annotationClass.name}, previous was ${result[annotationClass]?.name}" - ) - } - - result[annotationClass] = associatedObject - } - } - - return result -} - // Keep in sync with Konan_TypeFlags in TypeInfo.h. private const val TF_IMMUTABLE = 1 private const val TF_ACYCLIC = 2 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt index 5eb62ce5b30..0856d38823f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticObjects.kt @@ -102,7 +102,7 @@ internal fun StaticData.createConstArrayList(array: ConstPointer, length: Int): // Now sort these values according to the order of fields returned by getFields() // to match the sorting order of the real ArrayList(). val sorted = linkedMapOf() - getFields(arrayListClass).forEach { + context.getLayoutBuilder(arrayListClass).fields.forEach { val fqName = it.fqNameForIrSerialization.asString() sorted.put(fqName, arrayListFields[fqName]!!) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 064e8dcbf84..5f17efd1eaf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -901,7 +901,7 @@ private fun ObjCExportCodeGenerator.vtableIndex(irFunction: IrSimpleFunction): I return if (irClass.isInterface) { null } else { - context.getVtableBuilder(irClass).vtableIndex(irFunction) + context.getLayoutBuilder(irClass).vtableIndex(irFunction) } } @@ -974,7 +974,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapter( val vtableSize = if (irClass.kind == ClassKind.INTERFACE) { -1 } else { - context.getVtableBuilder(irClass).vtableEntries.size + context.getLayoutBuilder(irClass).vtableEntries.size } val vtable = if (!irClass.isInterface && !irClass.typeInfoHasVtableAttached) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index 7317ddd8d66..d2b8fbae644 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -651,8 +651,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag value ) } else { - val vTableBuilder = context.getVtableBuilder(owner) - val vtableIndex = vTableBuilder.vtableIndex(callee) + val vtableIndex = context.getLayoutBuilder(owner).vtableIndex(callee) DataFlowIR.Node.VtableCall( symbolTable.mapFunction(callee.target), receiverType, diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt index 2dafd7e9896..d40c38fc9e4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt @@ -530,9 +530,9 @@ internal object DataFlowIR { type.superTypes += irClass.superTypes.map { mapClassReferenceType(it.getClass()!!) } if (!isAbstract) { - val vtableBuilder = context.getVtableBuilder(irClass) - type.vtable += vtableBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) } - vtableBuilder.methodTableEntries.forEach { + val layoutBuilder = context.getLayoutBuilder(irClass) + type.vtable += layoutBuilder.vtableEntries.map { mapFunction(it.getImplementation(context)!!) } + layoutBuilder.methodTableEntries.forEach { type.itable[it.overriddenFunction.functionName.localHash.value] = mapFunction(it.getImplementation(context)!!) } }