From 648e728d90c54eef033c8eb22eca5994de5f5cb9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 16 Jan 2017 17:42:12 +0700 Subject: [PATCH] backend, runtime: place vtable at the end of TypeInfo --- .../konan/descriptors/DescriptorUtils.kt | 7 ++++- .../kotlin/backend/konan/llvm/ContextUtils.kt | 25 +++++++++-------- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 9 ++++--- .../backend/konan/llvm/RTTIGenerator.kt | 27 +++++++------------ runtime/src/main/cpp/TypeInfo.h | 6 ++--- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 968d34cc5e5..cbc1a30bf48 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -170,6 +170,8 @@ internal val ClassDescriptor.сontributedMethods: List return allMethods } +fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT + // TODO: optimize val ClassDescriptor.vtableEntries: List get() { @@ -190,6 +192,9 @@ val ClassDescriptor.vtableEntries: List return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable } } +val ClassDescriptor.vtableSize: Int + get() = if (this.isAbstract()) 0 else this.vtableEntries.size + fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int { this.vtableEntries.forEachIndexed { index, functionDescriptor -> if (functionDescriptor == function.original) return index @@ -199,7 +204,7 @@ fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int { val ClassDescriptor.methodTableEntries: List get() { - assert (this.modality != Modality.ABSTRACT) + assert (!this.isAbstract()) return this.сontributedMethods.filter { it.isOverridableOrOverrides } // TODO: probably method table should contain all accessible methods to improve binary compatibility diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 2609c23de9d..a792110a4d7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -7,6 +7,7 @@ import org.jetbrains.kotlin.backend.konan.Distribution import org.jetbrains.kotlin.backend.konan.hash.GlobalHash import org.jetbrains.kotlin.backend.konan.KonanConfigKeys import org.jetbrains.kotlin.backend.konan.descriptors.backingField +import org.jetbrains.kotlin.backend.konan.descriptors.vtableSize import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor @@ -114,22 +115,24 @@ internal interface ContextUtils { return constValue(result) } + /** + * Pointer to struct { TypeInfo, vtable }. + */ + val ClassDescriptor.typeInfoWithVtable: ConstPointer + get() { + val type = structType(runtime.typeInfoType, LLVMArrayType(int8TypePtr, this.vtableSize)!!) + return constPointer(externalGlobal(this.typeInfoSymbolName, type)) + } + + val ClassDescriptor.typeInfoPtr: ConstPointer + get() = typeInfoWithVtable.getElementPtr(0) + /** * Pointer to type info for given class. * It may be declared as pointer to external variable. */ val ClassDescriptor.llvmTypeInfoPtr: LLVMValueRef - get() { - val module = context.llvmModule - val globalName = this.typeInfoSymbolName - val globalPtr = LLVMGetNamedGlobal(module, globalName) ?: - LLVMAddGlobal(module, runtime.typeInfoType, globalName)!! - - return globalPtr - } - - val ClassDescriptor.typeInfoPtr: ConstPointer - get() = constPointer(this.llvmTypeInfoPtr) + get() = typeInfoPtr.llvm /** * Pointer to type info for this type, or `null` if the type doesn't have corresponding type info. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index b6c8f3de539..2e7f3275c1c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1786,15 +1786,16 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid fun callVirtual(descriptor: FunctionDescriptor, args: List): LLVMValueRef { val typeInfoPtrPtr = LLVMBuildStructGEP(codegen.builder, args[0], 0 /* type_info */, "")!! val typeInfoPtr = codegen.load(typeInfoPtrPtr) + assert (typeInfoPtr.type == codegen.kTypeInfoPtr) val owner = descriptor.containingDeclaration as ClassDescriptor val llvmMethod = if (!owner.isInterface) { // If this is a virtual method of the class - we can call via vtable. val index = owner.vtableIndex(descriptor) - val vtablePtr = LLVMBuildStructGEP(codegen.builder, typeInfoPtr, 7 /* vtable */, "")!! - val vtable = codegen.load(vtablePtr) - // TODO: those two loads are bad, we shall rework vtable to follow TypeInfo in memory, so - // this code shall just take end address of TypeInfo and use numbered slot from there. + + val vtablePlace = codegen.gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1 + val vtable = codegen.bitcast(kInt8PtrPtr, vtablePlace) + val slot = codegen.gep(vtable, Int32(index).llvm) codegen.load(slot) } else { 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 fc15959cc1b..f50e23e5858 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 @@ -4,11 +4,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.descriptors.methodTableEntries -import org.jetbrains.kotlin.backend.konan.descriptors.vtableEntries -import org.jetbrains.kotlin.backend.konan.descriptors.implementation -import org.jetbrains.kotlin.backend.konan.descriptors.implementedInterfaces -import org.jetbrains.kotlin.backend.konan.descriptors.isInterface +import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.FqName @@ -31,7 +27,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val objOffsetsCount: Int, val interfaces: ConstValue, val interfacesCount: Int, - val vtable: ConstValue, val methods: ConstValue, val methodsCount: Int, val fields: ConstValue, @@ -50,8 +45,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { interfaces, Int32(interfacesCount), - vtable, - methods, Int32(methodsCount), @@ -141,12 +134,12 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className", runtime.fieldTableRecordType, fields) - val vtable: List + val vtableEntries: List val methods: List - if (classDesc.modality != Modality.ABSTRACT) { + if (!classDesc.isAbstract()) { // TODO: compile-time resolution limits binary compatibility - vtable = classDesc.vtableEntries.map { it.implementation.entryPointAddress } + vtableEntries = classDesc.vtableEntries.map { it.implementation.entryPointAddress } methods = classDesc.methodTableEntries.map { val nameSignature = it.functionName.localHash @@ -155,12 +148,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { MethodTableRecord(nameSignature, methodEntryPoint) }.sortedBy { it.nameSignature.value } } else { - vtable = emptyList() + vtableEntries = emptyList() methods = emptyList() } + assert (vtableEntries.size == classDesc.vtableSize) - val vtablePtr = staticData.placeGlobalConstArray("kvtable:$className", pointerType(int8Type), vtable) + val vtable = ConstArray(int8TypePtr, vtableEntries) val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className", runtime.methodTableRecordType, methods) @@ -169,15 +163,14 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils { superType, objOffsetsPtr, objOffsets.size, interfacesPtr, interfaces.size, - vtablePtr, methodsPtr, methods.size, fieldsPtr, if (classDesc.isInterface) -1 else fields.size) - val typeInfoGlobal = classDesc.llvmTypeInfoPtr // TODO: it is a hack - LLVMSetInitializer(typeInfoGlobal, typeInfo.llvm) + val typeInfoGlobal = classDesc.typeInfoWithVtable.llvm // TODO: it is a hack + LLVMSetInitializer(typeInfoGlobal, Struct(typeInfo, vtable).llvm) LLVMSetGlobalConstant(typeInfoGlobal, 1) - exportTypeInfoIfRequired(classDesc, typeInfoGlobal) + exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr) } } diff --git a/runtime/src/main/cpp/TypeInfo.h b/runtime/src/main/cpp/TypeInfo.h index 92f25ee478d..e34a6c0b225 100644 --- a/runtime/src/main/cpp/TypeInfo.h +++ b/runtime/src/main/cpp/TypeInfo.h @@ -34,14 +34,14 @@ struct TypeInfo { const TypeInfo* const* implementedInterfaces_; int32_t implementedInterfacesCount_; // Null for abstract classes and interfaces. - // TODO: place vtable at the end of TypeInfo to eliminate the indirection. - void* const* vtable_; - // Null for abstract classes and interfaces. const MethodTableRecord* openMethods_; uint32_t openMethodsCount_; const FieldTableRecord* fields_; // Is negative to mark an interface. int32_t fieldsCount_; + + // vtable starts just after declared contents of the TypeInfo: + // void* const vtable_[]; }; #ifdef __cplusplus