backend, runtime: place vtable at the end of TypeInfo

This commit is contained in:
Svyatoslav Scherbina
2017-01-16 17:42:12 +07:00
committed by SvyatoslavScherbina
parent 913916e6dd
commit 648e728d90
5 changed files with 38 additions and 36 deletions
@@ -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
@@ -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.
@@ -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 {
@@ -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)
}
}
+3 -3
View File
@@ -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