backend, runtime: place vtable at the end of TypeInfo
This commit is contained in:
committed by
SvyatoslavScherbina
parent
913916e6dd
commit
648e728d90
+6
-1
@@ -170,6 +170,8 @@ internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
|
||||
return allMethods
|
||||
}
|
||||
|
||||
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|
||||
|
||||
// TODO: optimize
|
||||
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
||||
get() {
|
||||
@@ -190,6 +192,9 @@ val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
||||
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<FunctionDescriptor>
|
||||
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
|
||||
|
||||
+14
-11
@@ -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.
|
||||
|
||||
+5
-4
@@ -1786,15 +1786,16 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): 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 {
|
||||
|
||||
+10
-17
@@ -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<ConstValue>
|
||||
val vtableEntries: List<ConstValue>
|
||||
val methods: List<ConstValue>
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user