Implement virtual invoke via vtables. (#181)

Small refactoring to share vtable computation code.
This commit is contained in:
Nikolay Igotti
2017-01-16 15:25:36 +03:00
committed by GitHub
parent 56093ecbe0
commit 913916e6dd
8 changed files with 102 additions and 60 deletions
@@ -1,6 +1,8 @@
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -149,3 +151,56 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean
}
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
get() {
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
// (includes declarations from supers)
val functions = contributedDescriptors.filterIsInstance<FunctionDescriptor>()
val properties = contributedDescriptors.filterIsInstance<PropertyDescriptor>()
val getters = properties.mapNotNull { it.getter }
val setters = properties.mapNotNull { it.setter }
val allMethods = (functions + getters + setters).sortedBy {
// TODO: use local hash instead, but it needs major refactoring.
it.functionName.hashCode()
}
return allMethods
}
// TODO: optimize
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
get() {
assert(!this.isInterface)
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(this)) {
emptyList()
} else {
this.getSuperClassOrAny().vtableEntries
}
val methods = this.сontributedMethods
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
methods.single { OverridingUtil.overrides(it, superMethod) }
}
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
}
fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
if (functionDescriptor == function.original) return index
}
throw Error(function.toString() + " not in vtable of " + this.toString())
}
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
get() {
assert (this.modality != Modality.ABSTRACT)
return this.сontributedMethods.filter { it.isOverridableOrOverrides }
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
@@ -1784,14 +1784,29 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
//-------------------------------------------------------------------------//
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
val thisI8PtrPtr = codegen.bitcast(kInt8PtrPtr, args[0]) // Cast "this (i8*)" to i8**.
val typeInfoI8Ptr = codegen.load(thisI8PtrPtr) // Load TypeInfo address.
val typeInfoPtr = codegen.bitcast(codegen.kTypeInfoPtr, typeInfoI8Ptr) // Cast TypeInfo (i8*) to TypeInfo*.
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
val llvmMethod = call(context.llvm.lookupOpenMethodFunction,
lookupArgs) // Get method ptr to be invoked
val typeInfoPtrPtr = LLVMBuildStructGEP(codegen.builder, args[0], 0 /* type_info */, "")!!
val typeInfoPtr = codegen.load(typeInfoPtrPtr)
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 slot = codegen.gep(vtable, Int32(index).llvm)
codegen.load(slot)
} else {
// Otherwise, call via hashtable.
// TODO: optimize by storing interface number in lower bits of 'this' pointer
// when passing object as an interface. This way we can use those bits as index
// for an additional per-interface vtable.
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
call(context.llvm.lookupOpenMethodFunction,
lookupArgs)
}
val functionPtrType = pointerType(codegen.getLlvmFunctionType(descriptor)) // Construct type of the method to be invoked
val function = codegen.bitcast(functionPtrType, llvmMethod) // Cast method address to the type
return call(descriptor, function, args) // Invoke the method
@@ -1,13 +1,15 @@
package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
@@ -94,4 +96,4 @@ internal val ClassDescriptor.typeInfoSymbolName: String
get() = "ktype:" + this.fqNameSafe.toString()
internal val PropertyDescriptor.symbolName:String
get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}"
get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}"
@@ -4,16 +4,16 @@ 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.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
@@ -71,46 +71,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
return classType
}
// TODO: optimize
private fun getVtableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
assert (!classDesc.isInterface)
val superVtableEntries = if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(classDesc)) {
emptyList()
} else {
getVtableEntries(classDesc.getSuperClassOrAny())
}
val methods = classDesc.getContributedMethods() // TODO: ensure order is well-defined
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
methods.single { OverridingUtil.overrides(it, superMethod) }
}
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
}
private fun getMethodTableEntries(classDesc: ClassDescriptor): List<FunctionDescriptor> {
assert (classDesc.modality != Modality.ABSTRACT)
return classDesc.getContributedMethods().filter { it.isOverridableOrOverrides }
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
private fun ClassDescriptor.getContributedMethods(): List<FunctionDescriptor> {
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
// (includes declarations from supers)
val functions = contributedDescriptors.filterIsInstance<FunctionDescriptor>()
val properties = contributedDescriptors.filterIsInstance<PropertyDescriptor>()
val getters = properties.mapNotNull { it.getter }
val setters = properties.mapNotNull { it.setter }
val allMethods = functions + getters + setters
return allMethods
}
private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) {
val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo"))
if (annot != null) {
@@ -186,9 +146,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
if (classDesc.modality != Modality.ABSTRACT) {
// TODO: compile-time resolution limits binary compatibility
vtable = getVtableEntries(classDesc).map { it.implementation.entryPointAddress }
vtable = classDesc.vtableEntries.map { it.implementation.entryPointAddress }
methods = getMethodTableEntries(classDesc).map {
methods = classDesc.methodTableEntries.map {
val nameSignature = it.functionName.localHash
// TODO: compile-time resolution limits binary compatibility
val methodEntryPoint = it.implementation.entryPointAddress