[K/N][codegen] Refactored interface calls

Removed old impl for debug builds
Fixes https://youtrack.jetbrains.com/issue/KT-44547 as a side effect
This commit is contained in:
Igor Chevdar
2021-05-02 11:28:34 +05:00
parent 3df45f9651
commit 3b3318ab06
15 changed files with 139 additions and 272 deletions
@@ -85,10 +85,9 @@ internal class OverriddenFunctionInfo(
} }
} }
internal class ClassGlobalHierarchyInfo(val classIdLo: Int, val classIdHi: Int, internal class ClassGlobalHierarchyInfo(val classIdLo: Int, val classIdHi: Int, val interfaceId: Int) {
val interfaceId: Int, val interfaceColor: Int) {
companion object { companion object {
val DUMMY = ClassGlobalHierarchyInfo(0, 0, 0, 0) val DUMMY = ClassGlobalHierarchyInfo(0, 0, 0)
// 32-items table seems like a good threshold. // 32-items table seems like a good threshold.
val MAX_BITS_PER_COLOR = 5 val MAX_BITS_PER_COLOR = 5
@@ -160,8 +159,7 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod
"Unable to assign interface id to ${declaration.name}" "Unable to assign interface id to ${declaration.name}"
} }
context.getLayoutBuilder(declaration).hierarchyInfo = context.getLayoutBuilder(declaration).hierarchyInfo =
ClassGlobalHierarchyInfo(0, 0, ClassGlobalHierarchyInfo(0, 0, color or (interfaceId shl bitsPerColor))
color or (interfaceId shl bitsPerColor), color)
} else { } else {
allClasses += declaration allClasses += declaration
if (declaration != root) { if (declaration != root) {
@@ -181,7 +179,7 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod
val enterTime = if (irClass == root) -1 else time val enterTime = if (irClass == root) -1 else time
immediateInheritors[irClass]?.forEach { dfs(it) } immediateInheritors[irClass]?.forEach { dfs(it) }
val exitTime = time val exitTime = time
context.getLayoutBuilder(irClass).hierarchyInfo = ClassGlobalHierarchyInfo(enterTime, exitTime, 0, 0) context.getLayoutBuilder(irClass).hierarchyInfo = ClassGlobalHierarchyInfo(enterTime, exitTime, 0)
} }
dfs(root) dfs(root)
@@ -263,7 +261,7 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod
internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, val isLowered: Boolean) { internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, val isLowered: Boolean) {
val vtableEntries: List<OverriddenFunctionInfo> by lazy { val vtableEntries: List<OverriddenFunctionInfo> by lazy {
assert(!irClass.isInterface) require(!irClass.isInterface)
context.logMultiple { context.logMultiple {
+"" +""
@@ -277,7 +275,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va
context.getLayoutBuilder(superClass).vtableEntries context.getLayoutBuilder(superClass).vtableEntries
} }
val methods = irClass.sortedOverridableOrOverridingMethods val methods = irClass.overridableOrOverridingMethods
val newVtableSlots = mutableListOf<OverriddenFunctionInfo>() val newVtableSlots = mutableListOf<OverriddenFunctionInfo>()
val overridenVtableSlots = mutableMapOf<IrSimpleFunction, OverriddenFunctionInfo>() val overridenVtableSlots = mutableMapOf<IrSimpleFunction, OverriddenFunctionInfo>()
@@ -336,7 +334,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va
+"DONE vTable for ${irClass.render()}" +"DONE vTable for ${irClass.render()}"
} }
inheritedVtableSlots + filteredNewVtableSlots.sortedBy { it.overriddenFunction.uniqueId } inheritedVtableSlots + filteredNewVtableSlots.sortedBy { it.overriddenFunction.uniqueName }
} }
fun vtableIndex(function: IrSimpleFunction): Int { fun vtableIndex(function: IrSimpleFunction): Int {
@@ -346,21 +344,16 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va
return index return index
} }
val methodTableEntries: List<OverriddenFunctionInfo> by lazy { fun overridingOf(function: IrSimpleFunction) =
irClass.sortedOverridableOrOverridingMethods irClass.overridableOrOverridingMethods.firstOrNull { function in it.allOverriddenFunctions }?.let {
.flatMap { method -> method.allOverriddenFunctions.map { OverriddenFunctionInfo(method, it) } } OverriddenFunctionInfo(it, function).getImplementation(context)
.filter { it.canBeCalledVirtually } }
.distinctBy { it.overriddenFunction.uniqueId }
.sortedBy { it.overriddenFunction.uniqueId }
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
val interfaceTableEntries: List<IrSimpleFunction> by lazy { val interfaceVTableEntries: List<IrSimpleFunction> by lazy {
irClass.sortedOverridableOrOverridingMethods require(irClass.isInterface)
.filter { f -> irClass.overridableOrOverridingMethods
f.isReal || f.overriddenSymbols.any { OverriddenFunctionInfo(f, it.owner).needBridge } .filter { f -> f.isReal || f.overriddenSymbols.any { OverriddenFunctionInfo(f, it.owner).needBridge } }
} .sortedBy { it.uniqueName }
.toList()
} }
data class InterfaceTablePlace(val interfaceId: Int, val itableSize: Int, val methodIndex: Int) { data class InterfaceTablePlace(val interfaceId: Int, val itableSize: Int, val methodIndex: Int) {
@@ -369,12 +362,30 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va
} }
} }
val classId: Int get() = when {
irClass.isKotlinObjCClass() -> 0
irClass.isInterface -> {
if (context.ghaEnabled()) {
hierarchyInfo.interfaceId
} else {
localHash(irClass.fqNameForIrSerialization.asString().toByteArray()).toInt()
}
}
else -> {
if (context.ghaEnabled()) {
hierarchyInfo.classIdLo
} else {
0
}
}
}
fun itablePlace(function: IrSimpleFunction): InterfaceTablePlace { fun itablePlace(function: IrSimpleFunction): InterfaceTablePlace {
assert (irClass.isInterface) { "An interface expected but was ${irClass.name}" } require(irClass.isInterface) { "An interface expected but was ${irClass.name}" }
val itable = interfaceTableEntries val interfaceVTable = interfaceVTableEntries
val index = itable.indexOf(function) val index = interfaceVTable.indexOf(function)
if (index >= 0) if (index >= 0)
return InterfaceTablePlace(hierarchyInfo.interfaceId, itable.size, index) return InterfaceTablePlace(classId, interfaceVTable.size, index)
val superFunction = function.overriddenSymbols.first().owner val superFunction = function.overriddenSymbols.first().owner
return context.getLayoutBuilder(superFunction.parentAsClass).itablePlace(superFunction) return context.getLayoutBuilder(superFunction.parentAsClass).itablePlace(superFunction)
} }
@@ -454,13 +465,8 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va
return fields.sortedByDescending{ LLVMStoreSizeOfType(context.llvm.runtime.targetData, it.type.llvmType(context)) } return fields.sortedByDescending{ LLVMStoreSizeOfType(context.llvm.runtime.targetData, it.type.llvmType(context)) }
} }
private val IrClass.sortedOverridableOrOverridingMethods: List<IrSimpleFunction> private val IrClass.overridableOrOverridingMethods: List<IrSimpleFunction>
get() = get() = this.simpleFunctions().filter { it.isOverridableOrOverrides && it.bridgeTarget == null }
this.simpleFunctions()
.filter { it.isOverridableOrOverrides && it.bridgeTarget == null }
.sortedBy { it.uniqueId }
private val functionIds = mutableMapOf<IrFunction, Long>() private val IrFunction.uniqueName get() = computeFunctionName()
private val IrFunction.uniqueId get() = functionIds.getOrPut(this) { computeFunctionName().localHash.value }
} }
@@ -987,8 +987,8 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
} }
fun lookupInterfaceTableRecord(typeInfo: LLVMValueRef, interfaceId: Int): LLVMValueRef { fun lookupInterfaceTableRecord(typeInfo: LLVMValueRef, interfaceId: Int): LLVMValueRef {
val interfaceTableSize = load(structGep(typeInfo, 11 /* interfaceTableSize_ */)) val interfaceTableSize = load(structGep(typeInfo, 9 /* interfaceTableSize_ */))
val interfaceTable = load(structGep(typeInfo, 12 /* interfaceTable_ */)) val interfaceTable = load(structGep(typeInfo, 10 /* interfaceTable_ */))
fun fastPath(): LLVMValueRef { fun fastPath(): LLVMValueRef {
// The fastest optimistic version. // The fastest optimistic version.
@@ -997,7 +997,8 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
} }
// See details in ClassLayoutBuilder. // See details in ClassLayoutBuilder.
return if (context.globalHierarchyAnalysisResult.bitsPerColor <= ClassGlobalHierarchyInfo.MAX_BITS_PER_COLOR return if (context.ghaEnabled()
&& context.globalHierarchyAnalysisResult.bitsPerColor <= ClassGlobalHierarchyInfo.MAX_BITS_PER_COLOR
&& context.config.produce != CompilerOutputKind.FRAMEWORK) { && context.config.produce != CompilerOutputKind.FRAMEWORK) {
// All interface tables are small and no unknown interface inheritance. // All interface tables are small and no unknown interface inheritance.
fastPath() fastPath()
@@ -1042,7 +1043,6 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
*/ */
val anyMethod = (irFunction as IrSimpleFunction).findOverriddenMethodOfAny() val anyMethod = (irFunction as IrSimpleFunction).findOverriddenMethodOfAny()
val owner = (anyMethod ?: irFunction).parentAsClass val owner = (anyMethod ?: irFunction).parentAsClass
val methodHash = codegen.functionHash(irFunction)
val llvmMethod = when { val llvmMethod = when {
!owner.isInterface -> { !owner.isInterface -> {
@@ -1054,8 +1054,6 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
load(slot) load(slot)
} }
!context.ghaEnabled() -> call(context.llvm.lookupOpenMethodFunction, listOf(typeInfoPtr, methodHash))
else -> { else -> {
// Essentially: typeInfo.itable[place(interfaceId)].vtable[method] // Essentially: typeInfo.itable[place(interfaceId)].vtable[method]
val itablePlace = context.getLayoutBuilder(owner).itablePlace(irFunction) val itablePlace = context.getLayoutBuilder(owner).itablePlace(irFunction)
@@ -463,7 +463,6 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val zeroArrayRefsFunction = importRtFunction("ZeroArrayRefs") val zeroArrayRefsFunction = importRtFunction("ZeroArrayRefs")
val enterFrameFunction = importRtFunction("EnterFrame") val enterFrameFunction = importRtFunction("EnterFrame")
val leaveFrameFunction = importRtFunction("LeaveFrame") val leaveFrameFunction = importRtFunction("LeaveFrame")
val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod")
val lookupInterfaceTableRecord = importRtFunction("LookupInterfaceTableRecord") val lookupInterfaceTableRecord = importRtFunction("LookupInterfaceTableRecord")
val isInstanceFunction = importRtFunction("IsInstance") val isInstanceFunction = importRtFunction("IsInstance")
val isInstanceOfClassFastFunction = importRtFunction("IsInstanceOfClassFast") val isInstanceOfClassFastFunction = importRtFunction("IsInstanceOfClassFast")
@@ -82,9 +82,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
return result return result
} }
inner class MethodTableRecord(val nameSignature: LocalHash, methodEntryPoint: ConstPointer?) :
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
inner class InterfaceTableRecord(id: Int32, vtableSize: Int32, vtable: ConstPointer?) : inner class InterfaceTableRecord(id: Int32, vtableSize: Int32, vtable: ConstPointer?) :
Struct(runtime.interfaceTableRecordType, id, vtableSize, vtable) Struct(runtime.interfaceTableRecordType, id, vtableSize, vtable)
@@ -97,8 +94,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
objOffsetsCount: Int, objOffsetsCount: Int,
interfaces: ConstValue, interfaces: ConstValue,
interfacesCount: Int, interfacesCount: Int,
methods: ConstValue,
methodsCount: Int,
interfaceTableSize: Int, interfaceTableSize: Int,
interfaceTable: ConstValue, interfaceTable: ConstValue,
packageName: String?, packageName: String?,
@@ -130,9 +125,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
interfaces, interfaces,
Int32(interfacesCount), Int32(interfacesCount),
methods,
Int32(methodsCount),
Int32(interfaceTableSize), Int32(interfaceTableSize),
interfaceTable, interfaceTable,
@@ -205,20 +197,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
return LLVMStoreSizeOfType(llvmTargetData, classType).toInt() return LLVMStoreSizeOfType(llvmTargetData, classType).toInt()
} }
private fun getClassId(irClass: IrClass): Int {
if (irClass.isKotlinObjCClass()) return 0
val hierarchyInfo = if (context.ghaEnabled()) {
context.getLayoutBuilder(irClass).hierarchyInfo
} else {
ClassGlobalHierarchyInfo.DUMMY
}
return if (irClass.isInterface) {
hierarchyInfo.interfaceId
} else {
hierarchyInfo.classIdLo
}
}
fun generate(irClass: IrClass) { fun generate(irClass: IrClass) {
val className = irClass.fqNameForIrSerialization val className = irClass.fqNameForIrSerialization
@@ -254,16 +232,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
objOffsets.size objOffsets.size
} }
val methods = if (irClass.isAbstract()) { val needInterfaceTable = !irClass.isInterface && !irClass.isAbstract() && !irClass.isObjCClass()
emptyList()
} else {
methodTableRecords(irClass)
}
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
runtime.methodTableRecordType, methods)
val needInterfaceTable = context.ghaEnabled() && !irClass.isInterface
&& !irClass.isAbstract() && !irClass.isObjCClass()
val (interfaceTable, interfaceTableSize) = if (needInterfaceTable) { val (interfaceTable, interfaceTableSize) = if (needInterfaceTable) {
interfaceTableRecords(irClass) interfaceTableRecords(irClass)
} else { } else {
@@ -281,12 +250,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
superType, superType,
objOffsetsPtr, objOffsetsCount, objOffsetsPtr, objOffsetsCount,
interfacesPtr, interfaces.size, interfacesPtr, interfaces.size,
methodsPtr, methods.size,
interfaceTableSize, interfaceTablePtr, interfaceTableSize, interfaceTablePtr,
reflectionInfo.packageName, reflectionInfo.packageName,
reflectionInfo.relativeName, reflectionInfo.relativeName,
flagsFromClass(irClass), flagsFromClass(irClass),
getClassId(irClass), context.getLayoutBuilder(irClass).classId,
llvmDeclarations.writableTypeInfoGlobal?.pointer, llvmDeclarations.writableTypeInfoGlobal?.pointer,
associatedObjects = genAssociatedObjects(irClass) associatedObjects = genAssociatedObjects(irClass)
) )
@@ -326,25 +294,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
return ConstArray(int8TypePtr, vtableEntries) return ConstArray(int8TypePtr, vtableEntries)
} }
fun methodTableRecords(irClass: IrClass): List<MethodTableRecord> {
val functionNames = mutableMapOf<Long, OverriddenFunctionInfo>()
return context.getLayoutBuilder(irClass).methodTableEntries.map {
val functionName = it.overriddenFunction.computeFunctionName()
val nameSignature = functionName.localHash
val previous = functionNames.putIfAbsent(nameSignature.value, it)
if (previous != null)
throw AssertionError("Duplicate method table entry: functionName = '$functionName', hash = '${nameSignature.value}', entry1 = $previous, entry2 = $it")
// TODO: compile-time resolution limits binary compatibility.
val implementation = it.implementation
val methodEntryPoint =
if (implementation == null || context.referencedFunctions?.contains(implementation) == false)
null
else implementation.entryPointAddress
MethodTableRecord(nameSignature, methodEntryPoint)
}.sortedBy { it.nameSignature.value }
}
fun interfaceTableRecords(irClass: IrClass): Pair<List<InterfaceTableRecord>, Int> { fun interfaceTableRecords(irClass: IrClass): Pair<List<InterfaceTableRecord>, Int> {
// The details are in ClassLayoutBuilder. // The details are in ClassLayoutBuilder.
val interfaces = irClass.implementedInterfaces val interfaces = irClass.implementedInterfaces
@@ -356,7 +305,7 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
private fun interfaceTableSkeleton(interfaces: List<IrClass>): Pair<Array<out ClassLayoutBuilder?>, Int> { private fun interfaceTableSkeleton(interfaces: List<IrClass>): Pair<Array<out ClassLayoutBuilder?>, Int> {
val interfaceLayouts = interfaces.map { context.getLayoutBuilder(it) } val interfaceLayouts = interfaces.map { context.getLayoutBuilder(it) }
val interfaceColors = interfaceLayouts.map { it.hierarchyInfo.interfaceColor } val interfaceIds = interfaceLayouts.map { it.classId }
// Find the optimal size. It must be a power of 2. // Find the optimal size. It must be a power of 2.
var size = 1 var size = 1
@@ -367,8 +316,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
used[i] = false used[i] = false
// Check for collisions. // Check for collisions.
var ok = true var ok = true
for (color in interfaceColors) { for (id in interfaceIds) {
val index = color % size val index = id and (size - 1) // This is not an optimization but rather for not to bother with negative numbers.
if (used[index]) { if (used[index]) {
ok = false ok = false
break break
@@ -378,17 +327,25 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
if (ok) break if (ok) break
size *= 2 size *= 2
} }
val conservative = size > maxSize val useFastITable = size <= maxSize
val interfaceTableSkeleton = if (conservative) { val interfaceTableSkeleton = if (useFastITable) {
arrayOfNulls<ClassLayoutBuilder?>(size).also {
for (interfaceLayout in interfaceLayouts)
it[interfaceLayout.classId and (size - 1)] = interfaceLayout
}
} else {
size = interfaceLayouts.size size = interfaceLayouts.size
interfaceLayouts.sortedBy { it.hierarchyInfo.interfaceId }.toTypedArray() val sortedInterfaceLayouts = interfaceLayouts.sortedBy { it.classId }.toTypedArray()
} else arrayOfNulls<ClassLayoutBuilder?>(size).also { for (i in 1 until sortedInterfaceLayouts.size)
for (interfaceLayout in interfaceLayouts) require(sortedInterfaceLayouts[i - 1].classId != sortedInterfaceLayouts[i].classId) {
it[interfaceLayout.hierarchyInfo.interfaceId % size] = interfaceLayout "Different interfaces ${sortedInterfaceLayouts[i - 1].irClass.render()} and ${sortedInterfaceLayouts[i].irClass.render()}" +
" have same class id: ${sortedInterfaceLayouts[i].classId}"
}
sortedInterfaceLayouts
} }
val interfaceTableSize = if (conservative) -size else (size - 1) val interfaceTableSize = if (useFastITable) (size - 1) else -size
return Pair(interfaceTableSkeleton, interfaceTableSize) return Pair(interfaceTableSkeleton, interfaceTableSize)
} }
@@ -396,22 +353,19 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
irClass: IrClass, irClass: IrClass,
interfaceTableSkeleton: Array<out ClassLayoutBuilder?> interfaceTableSkeleton: Array<out ClassLayoutBuilder?>
): List<InterfaceTableRecord> { ): List<InterfaceTableRecord> {
val methodTableEntries = context.getLayoutBuilder(irClass).methodTableEntries val layoutBuilder = context.getLayoutBuilder(irClass)
val className = irClass.fqNameForIrSerialization val className = irClass.fqNameForIrSerialization
return interfaceTableSkeleton.map { iface -> return interfaceTableSkeleton.map { iface ->
val interfaceId = iface?.hierarchyInfo?.interfaceId ?: 0 val interfaceId = iface?.classId ?: 0
InterfaceTableRecord( InterfaceTableRecord(
Int32(interfaceId), Int32(interfaceId),
Int32(iface?.interfaceTableEntries?.size ?: 0), Int32(iface?.interfaceVTableEntries?.size ?: 0),
if (iface == null) if (iface == null)
NullPointer(kInt8Ptr) NullPointer(kInt8Ptr)
else { else {
val vtableEntries = iface.interfaceTableEntries.map { ifaceFunction -> val vtableEntries = iface.interfaceVTableEntries.map { ifaceFunction ->
val impl = OverriddenFunctionInfo( val impl = layoutBuilder.overridingOf(ifaceFunction)
methodTableEntries.first { ifaceFunction in it.function.allOverriddenFunctions }.function,
ifaceFunction
).implementation
if (impl == null || context.referencedFunctions?.contains(impl) == false) if (impl == null || context.referencedFunctions?.contains(impl) == false)
NullPointer(int8Type) NullPointer(int8Type)
else impl.entryPointAddress else impl.entryPointAddress
@@ -542,15 +496,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val objOffsetsPtr = staticData.placeGlobalConstArray("", int32Type, objOffsets) val objOffsetsPtr = staticData.placeGlobalConstArray("", int32Type, objOffsets)
val objOffsetsCount = objOffsets.size val objOffsetsCount = objOffsets.size
val methods = (methodTableRecords(superClass) + methodImpls.map { (method, impl) ->
assert(method.parent == irClass)
MethodTableRecord(method.computeFunctionName().localHash, impl.bitcast(int8TypePtr))
}).sortedBy { it.nameSignature.value }.also {
assert(it.distinctBy { it.nameSignature.value } == it)
}
val methodsPtr = staticData.placeGlobalConstArray("", runtime.methodTableRecordType, methods)
val reflectionInfo = ReflectionInfo(null, null) val reflectionInfo = ReflectionInfo(null, null)
val writableTypeInfoType = runtime.writableTypeInfoType val writableTypeInfoType = runtime.writableTypeInfoType
@@ -568,21 +513,20 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val typeHierarchyInfo = if (!context.ghaEnabled()) val typeHierarchyInfo = if (!context.ghaEnabled())
ClassGlobalHierarchyInfo.DUMMY ClassGlobalHierarchyInfo.DUMMY
else else
ClassGlobalHierarchyInfo(-1, -1, 0, 0) ClassGlobalHierarchyInfo(-1, -1, 0)
// TODO: interfaces (e.g. FunctionN and Function) should have different colors. // TODO: interfaces (e.g. FunctionN and Function) should have different colors.
val (interfaceTableSkeleton, interfaceTableSize) = val (interfaceTableSkeleton, interfaceTableSize) = interfaceTableSkeleton(interfaces)
if (context.ghaEnabled()) interfaceTableSkeleton(interfaces) else Pair(emptyArray(), -1)
val interfaceTable = interfaceTableSkeleton.map { layoutBuilder -> val interfaceTable = interfaceTableSkeleton.map { layoutBuilder ->
if (layoutBuilder == null) { if (layoutBuilder == null) {
InterfaceTableRecord(Int32(0), Int32(0), null) InterfaceTableRecord(Int32(0), Int32(0), null)
} else { } else {
val vtableEntries = layoutBuilder.interfaceTableEntries.map { methodImpls[it]!!.bitcast(int8TypePtr) } val vtableEntries = layoutBuilder.interfaceVTableEntries.map { methodImpls[it]!!.bitcast(int8TypePtr) }
val interfaceVTable = staticData.placeGlobalArray("", kInt8Ptr, vtableEntries) val interfaceVTable = staticData.placeGlobalArray("", kInt8Ptr, vtableEntries)
InterfaceTableRecord( InterfaceTableRecord(
Int32(layoutBuilder.hierarchyInfo.interfaceId), Int32(layoutBuilder.classId),
Int32(layoutBuilder.interfaceTableEntries.size), Int32(layoutBuilder.interfaceVTableEntries.size),
interfaceVTable.pointer.getElementPtr(0) interfaceVTable.pointer.getElementPtr(0)
) )
} }
@@ -596,7 +540,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
superType = superClass.typeInfoPtr, superType = superClass.typeInfoPtr,
objOffsets = objOffsetsPtr, objOffsetsCount = objOffsetsCount, objOffsets = objOffsetsPtr, objOffsetsCount = objOffsetsCount,
interfaces = interfacesPtr, interfacesCount = interfaces.size, interfaces = interfacesPtr, interfacesCount = interfaces.size,
methods = methodsPtr, methodsCount = methods.size,
interfaceTableSize = interfaceTableSize, interfaceTable = interfaceTablePtr, interfaceTableSize = interfaceTableSize, interfaceTable = interfaceTablePtr,
packageName = reflectionInfo.packageName, packageName = reflectionInfo.packageName,
relativeName = reflectionInfo.relativeName, relativeName = reflectionInfo.relativeName,
@@ -26,7 +26,6 @@ class Runtime(bitcodeFile: String) {
val typeInfoType = getStructType("TypeInfo") val typeInfoType = getStructType("TypeInfo")
val extendedTypeInfoType = getStructType("ExtendedTypeInfo") val extendedTypeInfoType = getStructType("ExtendedTypeInfo")
val writableTypeInfoType = getStructTypeOrNull("WritableTypeInfo") val writableTypeInfoType = getStructTypeOrNull("WritableTypeInfo")
val methodTableRecordType = getStructType("MethodTableRecord")
val interfaceTableRecordType = getStructType("InterfaceTableRecord") val interfaceTableRecordType = getStructType("InterfaceTableRecord")
val associatedObjectTableRecordType = getStructType("AssociatedObjectTableRecord") val associatedObjectTableRecordType = getStructType("AssociatedObjectTableRecord")
@@ -438,14 +438,12 @@ internal class ObjCExportCodeGenerator(
inner class KotlinToObjCMethodAdapter( inner class KotlinToObjCMethodAdapter(
selector: String, selector: String,
nameSignature: Long,
itablePlace: ClassLayoutBuilder.InterfaceTablePlace, itablePlace: ClassLayoutBuilder.InterfaceTablePlace,
vtableIndex: Int, vtableIndex: Int,
kotlinImpl: ConstPointer kotlinImpl: ConstPointer
) : Struct( ) : Struct(
runtime.kotlinToObjCMethodAdapter, runtime.kotlinToObjCMethodAdapter,
staticData.cStringLiteral(selector), staticData.cStringLiteral(selector),
Int64(nameSignature),
Int32(itablePlace.interfaceId), Int32(itablePlace.interfaceId),
Int32(itablePlace.itableSize), Int32(itablePlace.itableSize),
Int32(itablePlace.methodIndex), Int32(itablePlace.methodIndex),
@@ -458,7 +456,6 @@ internal class ObjCExportCodeGenerator(
typeInfo: ConstPointer?, typeInfo: ConstPointer?,
vtable: ConstPointer?, vtable: ConstPointer?,
vtableSize: Int, vtableSize: Int,
methodTable: List<RTTIGenerator.MethodTableRecord>,
itable: List<RTTIGenerator.InterfaceTableRecord>, itable: List<RTTIGenerator.InterfaceTableRecord>,
itableSize: Int, itableSize: Int,
val objCName: String, val objCName: String,
@@ -473,9 +470,6 @@ internal class ObjCExportCodeGenerator(
vtable, vtable,
Int32(vtableSize), Int32(vtableSize),
staticData.placeGlobalConstArray("", runtime.methodTableRecordType, methodTable),
Int32(methodTable.size),
staticData.placeGlobalConstArray("", runtime.interfaceTableRecordType, itable), staticData.placeGlobalConstArray("", runtime.interfaceTableRecordType, itable),
Int32(itableSize), Int32(itableSize),
@@ -1147,12 +1141,10 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
private fun ObjCExportCodeGenerator.createReverseAdapter( private fun ObjCExportCodeGenerator.createReverseAdapter(
irFunction: IrFunction, irFunction: IrFunction,
baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>, baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>,
functionName: String,
vtableIndex: Int?, vtableIndex: Int?,
itablePlace: ClassLayoutBuilder.InterfaceTablePlace? itablePlace: ClassLayoutBuilder.InterfaceTablePlace?
): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter { ): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter {
val nameSignature = functionName.localHash.value
val selector = baseMethod.selector val selector = baseMethod.selector
val kotlinToObjC = generateKotlinToObjCBridge( val kotlinToObjC = generateKotlinToObjCBridge(
@@ -1160,7 +1152,7 @@ private fun ObjCExportCodeGenerator.createReverseAdapter(
baseMethod baseMethod
).bitcast(int8TypePtr) ).bitcast(int8TypePtr)
return KotlinToObjCMethodAdapter(selector, nameSignature, return KotlinToObjCMethodAdapter(selector,
itablePlace ?: ClassLayoutBuilder.InterfaceTablePlace.INVALID, itablePlace ?: ClassLayoutBuilder.InterfaceTablePlace.INVALID,
vtableIndex ?: -1, vtableIndex ?: -1,
kotlinToObjC) kotlinToObjC)
@@ -1230,8 +1222,9 @@ private fun ObjCExportCodeGenerator.vtableIndex(irFunction: IrSimpleFunction): I
private fun ObjCExportCodeGenerator.itablePlace(irFunction: IrSimpleFunction): ClassLayoutBuilder.InterfaceTablePlace? { private fun ObjCExportCodeGenerator.itablePlace(irFunction: IrSimpleFunction): ClassLayoutBuilder.InterfaceTablePlace? {
assert(irFunction.isOverridable) assert(irFunction.isOverridable)
val irClass = irFunction.parentAsClass val irClass = irFunction.parentAsClass
return if (irClass.isInterface && context.ghaEnabled() return if (irClass.isInterface
&& (irFunction.isReal || irFunction.resolveFakeOverrideMaybeAbstract().parent != context.irBuiltIns.anyClass.owner)) { && (irFunction.isReal || irFunction.resolveFakeOverrideMaybeAbstract().parent != context.irBuiltIns.anyClass.owner)
) {
context.getLayoutBuilder(irClass).itablePlace(irFunction) context.getLayoutBuilder(irClass).itablePlace(irFunction)
} else { } else {
null null
@@ -1250,7 +1243,6 @@ private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
typeInfo = null, typeInfo = null,
vtable = null, vtable = null,
vtableSize = -1, vtableSize = -1,
methodTable = emptyList(),
itable = emptyList(), itable = emptyList(),
itableSize = -1, itableSize = -1,
objCName = name, objCName = name,
@@ -1333,15 +1325,9 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
null null
} }
val methodTable = if (!irClass.isInterface && irClass.isAbstract()) {
rttiGenerator.methodTableRecords(irClass)
} else {
emptyList()
}
val (itable, itableSize) = when { val (itable, itableSize) = when {
irClass.isInterface -> Pair(emptyList(), context.getLayoutBuilder(irClass).interfaceTableEntries.size) irClass.isInterface -> Pair(emptyList(), context.getLayoutBuilder(irClass).interfaceVTableEntries.size)
irClass.isAbstract() && context.ghaEnabled() -> rttiGenerator.interfaceTableRecords(irClass) irClass.isAbstract() -> rttiGenerator.interfaceTableRecords(irClass)
else -> Pair(emptyList(), -1) else -> Pair(emptyList(), -1)
} }
@@ -1350,7 +1336,6 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
typeInfo, typeInfo,
vtable, vtable,
vtableSize, vtableSize,
methodTable,
itable, itable,
itableSize, itableSize,
objCName, objCName,
@@ -1445,7 +1430,7 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
presentVtableBridges += vtableIndex presentVtableBridges += vtableIndex
presentMethodTableBridges += functionName presentMethodTableBridges += functionName
presentItableBridges += itablePlace presentItableBridges += itablePlace
result += createReverseAdapter(it, baseMethod, functionName, vtableIndex, itablePlace) result += createReverseAdapter(it, baseMethod, vtableIndex, itablePlace)
coveredMethods += it coveredMethods += it
} }
} }
@@ -1466,7 +1451,6 @@ private fun ObjCExportCodeGenerator.nonOverridableAdapter(
hasSelectorAmbiguity: Boolean hasSelectorAmbiguity: Boolean
): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter = KotlinToObjCMethodAdapter( ): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter = KotlinToObjCMethodAdapter(
selector, selector,
-1,
vtableIndex = if (hasSelectorAmbiguity) -2 else -1, // Describes the reason. vtableIndex = if (hasSelectorAmbiguity) -2 else -1, // Describes the reason.
kotlinImpl = NullPointer(int8Type), kotlinImpl = NullPointer(int8Type),
itablePlace = ClassLayoutBuilder.InterfaceTablePlace.INVALID itablePlace = ClassLayoutBuilder.InterfaceTablePlace.INVALID
@@ -182,7 +182,7 @@ internal class CallGraphBuilder(
receiverType.vtable[call.calleeVtableIndex] receiverType.vtable[call.calleeVtableIndex]
is DataFlowIR.Node.ItableCall -> is DataFlowIR.Node.ItableCall ->
receiverType.itable[call.calleeHash]!! receiverType.itable[call.interfaceId]!![call.calleeItableIndex]
else -> error("Unreachable") else -> error("Unreachable")
} }
@@ -785,18 +785,24 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag
symbolTable.mapClassReferenceType(owner) symbolTable.mapClassReferenceType(owner)
} }
} }
if (owner.isInterface) {
val calleeHash = callee.computeFunctionName().localHash.value val isAnyMethod = callee.target.parentAsClass.isAny()
if (owner.isInterface && !isAnyMethod) {
val itablePlace = context.getLayoutBuilder(owner).itablePlace(callee)
DataFlowIR.Node.ItableCall( DataFlowIR.Node.ItableCall(
symbolTable.mapFunction(callee.target), symbolTable.mapFunction(callee.target),
receiverType, receiverType,
calleeHash, itablePlace.interfaceId,
itablePlace.methodIndex,
arguments, arguments,
mapReturnType(value.type, callee.target.returnType), mapReturnType(value.type, callee.target.returnType),
value value
) )
} else { } else {
val vtableIndex = context.getLayoutBuilder(owner).vtableIndex(callee) val vtableIndex = if (isAnyMethod)
context.getLayoutBuilder(context.irBuiltIns.anyClass.owner).vtableIndex(callee.target)
else
context.getLayoutBuilder(owner).vtableIndex(callee)
DataFlowIR.Node.VtableCall( DataFlowIR.Node.VtableCall(
symbolTable.mapFunction(callee.target), symbolTable.mapFunction(callee.target),
receiverType, receiverType,
@@ -6,9 +6,10 @@
package org.jetbrains.kotlin.backend.konan.optimizations package org.jetbrains.kotlin.backend.konan.optimizations
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.descriptors.isBuiltInOperator
import org.jetbrains.kotlin.backend.common.ir.allParameters import org.jetbrains.kotlin.backend.common.ir.allParameters
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.descriptors.OverriddenFunctionInfo
import org.jetbrains.kotlin.backend.konan.descriptors.implementedInterfaces
import org.jetbrains.kotlin.backend.konan.ir.isOverridableOrOverrides import org.jetbrains.kotlin.backend.konan.ir.isOverridableOrOverrides
import org.jetbrains.kotlin.backend.konan.llvm.computeFunctionName import org.jetbrains.kotlin.backend.konan.llvm.computeFunctionName
import org.jetbrains.kotlin.backend.konan.llvm.computeSymbolName import org.jetbrains.kotlin.backend.konan.llvm.computeSymbolName
@@ -64,7 +65,7 @@ internal object DataFlowIR {
: Type(isFinal, isAbstract, primitiveBinaryType, name) { : Type(isFinal, isAbstract, primitiveBinaryType, name) {
val superTypes = mutableListOf<Type>() val superTypes = mutableListOf<Type>()
val vtable = mutableListOf<FunctionSymbol>() val vtable = mutableListOf<FunctionSymbol>()
val itable = mutableMapOf<Long, FunctionSymbol>() val itable = mutableMapOf<Int, List<FunctionSymbol>>()
} }
class Public(val hash: Long, index: Int, isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?, class Public(val hash: Long, index: Int, isFinal: Boolean, isAbstract: Boolean, primitiveBinaryType: PrimitiveBinaryType?,
@@ -244,7 +245,7 @@ internal object DataFlowIR {
arguments: List<Edge>, returnType: Type, irCallSite: IrCall?) arguments: List<Edge>, returnType: Type, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, returnType, irCallSite) : VirtualCall(callee, arguments, receiverType, returnType, irCallSite)
class ItableCall(callee: FunctionSymbol, receiverType: Type, val calleeHash: Long, class ItableCall(callee: FunctionSymbol, receiverType: Type, val interfaceId: Int, val calleeItableIndex: Int,
arguments: List<Edge>, returnType: Type, irCallSite: IrCall?) arguments: List<Edge>, returnType: Type, irCallSite: IrCall?)
: VirtualCall(callee, arguments, receiverType, returnType, irCallSite) : VirtualCall(callee, arguments, receiverType, returnType, irCallSite)
@@ -342,7 +343,7 @@ internal object DataFlowIR {
is Node.ItableCall -> buildString { is Node.ItableCall -> buildString {
appendLine(" INTERFACE CALL ${node.callee}. Return type = ${node.returnType}") appendLine(" INTERFACE CALL ${node.callee}. Return type = ${node.returnType}")
appendLine(" RECEIVER: ${node.receiverType}") appendLine(" RECEIVER: ${node.receiverType}")
append(" METHOD HASH: ${node.calleeHash}") append(" INTERFACE ID: ${node.interfaceId}. ITABLE INDEX: ${node.calleeItableIndex}")
appendList(node.arguments) { appendList(node.arguments) {
append(" ARG #${ids[it.node]!!}") append(" ARG #${ids[it.node]!!}")
appendCastTo(it.castToType) appendCastTo(it.castToType)
@@ -505,12 +506,13 @@ internal object DataFlowIR {
type.vtable += layoutBuilder.vtableEntries.map { type.vtable += layoutBuilder.vtableEntries.map {
mapFunction(it.getImplementation(context)!!) mapFunction(it.getImplementation(context)!!)
} }
layoutBuilder.methodTableEntries.forEach { val interfaces = irClass.implementedInterfaces.map { context.getLayoutBuilder(it) }
type.itable[it.overriddenFunction.computeFunctionName().localHash.value] = mapFunction(it.getImplementation(context)!!) for (iface in interfaces) {
type.itable[iface.classId] = iface.interfaceVTableEntries.map { mapFunction(layoutBuilder.overridingOf(it)!!) }
} }
} else if (irClass.isInterface) { } else if (irClass.isInterface) {
// Warmup interface table so it is computed before DCE. // Warmup interface table so it is computed before DCE.
context.getLayoutBuilder(irClass).interfaceTableEntries context.getLayoutBuilder(irClass).interfaceVTableEntries
} }
return type return type
} }
@@ -79,7 +79,7 @@ internal object Devirtualization {
} + } +
moduleDFG.symbolTable.classMap.values moduleDFG.symbolTable.classMap.values
.filterIsInstance<DataFlowIR.Type.Declared>() .filterIsInstance<DataFlowIR.Type.Declared>()
.flatMap { it.vtable + it.itable.values } .flatMap { it.vtable + it.itable.values.flatten() }
.filterIsInstance<DataFlowIR.FunctionSymbol.Declared>() .filterIsInstance<DataFlowIR.FunctionSymbol.Declared>()
.filter { moduleDFG.functions.containsKey(it) } .filter { moduleDFG.functions.containsKey(it) }
} }
@@ -263,7 +263,7 @@ internal object Devirtualization {
vtable[callSite.calleeVtableIndex] vtable[callSite.calleeVtableIndex]
is DataFlowIR.Node.ItableCall -> is DataFlowIR.Node.ItableCall ->
itable[callSite.calleeHash]!! itable[callSite.interfaceId]!![callSite.calleeItableIndex]
else -> error("Unreachable") else -> error("Unreachable")
} }
@@ -2616,6 +2616,10 @@ task interfaceCallsNCasts_conservativeItable(type: KonanLocalTest) {
source = "codegen/interfaceCallsNCasts/conservativeItable.kt" source = "codegen/interfaceCallsNCasts/conservativeItable.kt"
} }
task interfaceCallsNCasts_functionNameClash(type: KonanLocalTest) {
source = "codegen/interfaceCallsNCasts/functionNameClash.kt"
}
standaloneTest("multiargs") { standaloneTest("multiargs") {
arguments = ["AAA", "BB", "C"] arguments = ["AAA", "BB", "C"]
multiRuns = true multiRuns = true
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package codegen.interfaceCallsNCasts.functionNameClash
import kotlin.test.*
interface I1<T> {
fun foo(x: T): String
}
interface I2<T> {
fun foo(x: T): String
}
class C : I1<String>, I2<Int> {
override fun foo(x: String) = "I1.foo($x)"
override fun foo(x: Int) = "I2.foo($x)"
}
@Test
fun runTest() {
val c = C()
val i1: I1<String> = c
assertEquals("I1.foo(str)", i1.foo("str"))
val i2: I2<Int> = c
assertEquals("I2.foo(42)", i2.foo(42))
}
@@ -52,7 +52,6 @@ struct ObjCToKotlinMethodAdapter {
struct KotlinToObjCMethodAdapter { struct KotlinToObjCMethodAdapter {
const char* selector; const char* selector;
MethodNameHash nameSignature;
ClassId interfaceId; ClassId interfaceId;
int itableSize; int itableSize;
int itableIndex; int itableIndex;
@@ -66,9 +65,6 @@ struct ObjCTypeAdapter {
const void * const * kotlinVtable; const void * const * kotlinVtable;
int kotlinVtableSize; int kotlinVtableSize;
const MethodTableRecord* kotlinMethodTable;
int kotlinMethodTableSize;
const InterfaceTableRecord* kotlinItable; const InterfaceTableRecord* kotlinItable;
int kotlinItableSize; int kotlinItableSize;
@@ -652,7 +648,6 @@ static const TypeInfo* createTypeInfo(
const TypeInfo* superType, const TypeInfo* superType,
const KStdVector<const TypeInfo*>& superInterfaces, const KStdVector<const TypeInfo*>& superInterfaces,
const KStdVector<VTableElement>& vtable, const KStdVector<VTableElement>& vtable,
const KStdVector<MethodTableRecord>& methodTable,
const KStdOrderedMap<ClassId, KStdVector<VTableElement>>& interfaceVTables, const KStdOrderedMap<ClassId, KStdVector<VTableElement>>& interfaceVTables,
const InterfaceTableRecord* superItable, const InterfaceTableRecord* superItable,
int superItableSize, int superItableSize,
@@ -707,12 +702,6 @@ static const TypeInfo* createTypeInfo(
} }
} }
MethodTableRecord* openMethods_ = konanAllocArray<MethodTableRecord>(methodTable.size());
for (size_t i = 0; i < methodTable.size(); ++i) openMethods_[i] = methodTable[i];
result->openMethods_ = openMethods_;
result->openMethodsCount_ = methodTable.size();
result->packageName_ = nullptr; result->packageName_ = nullptr;
result->relativeName_ = nullptr; // TODO: add some info. result->relativeName_ = nullptr; // TODO: add some info.
result->writableInfo_ = (WritableTypeInfo*)konanAllocMemory(sizeof(WritableTypeInfo)); result->writableInfo_ = (WritableTypeInfo*)konanAllocMemory(sizeof(WritableTypeInfo));
@@ -777,22 +766,6 @@ static int getVtableSize(const TypeInfo* typeInfo) {
return -1; return -1;
} }
static void insertOrReplace(KStdVector<MethodTableRecord>& methodTable, MethodNameHash nameSignature, void* entryPoint) {
MethodTableRecord record = {nameSignature, entryPoint};
for (int i = methodTable.size() - 1; i >= 0; --i) {
if (methodTable[i].nameSignature_ == nameSignature) {
methodTable[i].methodEntryPoint_ = entryPoint;
return;
} else if (methodTable[i].nameSignature_ < nameSignature) {
methodTable.insert(methodTable.begin() + (i + 1), record);
return;
}
}
methodTable.insert(methodTable.begin(), record);
}
static void throwIfCantBeOverridden(Class clazz, const KotlinToObjCMethodAdapter* adapter) { static void throwIfCantBeOverridden(Class clazz, const KotlinToObjCMethodAdapter* adapter) {
if (adapter->kotlinImpl == nullptr) { if (adapter->kotlinImpl == nullptr) {
NSString* reason; NSString* reason;
@@ -816,9 +789,6 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
const void * const * superVtable = nullptr; const void * const * superVtable = nullptr;
int superVtableSize = getVtableSize(superType); int superVtableSize = getVtableSize(superType);
const MethodTableRecord* superMethodTable = nullptr;
int superMethodTableSize = 0;
InterfaceTableRecord const* superITable = nullptr; InterfaceTableRecord const* superITable = nullptr;
int superITableSize = 0; int superITableSize = 0;
@@ -828,27 +798,17 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
// And if it is abstract, then vtable and method table are not available from TypeInfo, // And if it is abstract, then vtable and method table are not available from TypeInfo,
// but present in type adapter instead: // but present in type adapter instead:
superVtable = superTypeAdapter->kotlinVtable; superVtable = superTypeAdapter->kotlinVtable;
superMethodTable = superTypeAdapter->kotlinMethodTable;
superMethodTableSize = superTypeAdapter->kotlinMethodTableSize;
superITable = superTypeAdapter->kotlinItable; superITable = superTypeAdapter->kotlinItable;
superITableSize = superTypeAdapter->kotlinItableSize; superITableSize = superTypeAdapter->kotlinItableSize;
} }
if (superVtable == nullptr) superVtable = superType->vtable(); if (superVtable == nullptr) superVtable = superType->vtable();
if (superMethodTable == nullptr) {
superMethodTable = superType->openMethods_;
superMethodTableSize = superType->openMethodsCount_;
}
KStdVector<const void*> vtable( KStdVector<const void*> vtable(
superVtable, superVtable,
superVtable + superVtableSize superVtable + superVtableSize
); );
KStdVector<MethodTableRecord> methodTable(
superMethodTable, superMethodTable + superMethodTableSize
);
if (superITable == nullptr) { if (superITable == nullptr) {
superITable = superType->interfaceTable_; superITable = superType->interfaceTable_;
superITableSize = superType->interfaceTableSize_; superITableSize = superType->interfaceTableSize_;
@@ -915,7 +875,6 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
throwIfCantBeOverridden(clazz, adapter); throwIfCantBeOverridden(clazz, adapter);
itableEqualsSuper = false; itableEqualsSuper = false;
insertOrReplace(methodTable, adapter->nameSignature, const_cast<void*>(adapter->kotlinImpl));
if (adapter->vtableIndex != -1) vtable[adapter->vtableIndex] = adapter->kotlinImpl; if (adapter->vtableIndex != -1) vtable[adapter->vtableIndex] = adapter->kotlinImpl;
if (adapter->itableIndex != -1 && superITable != nullptr) if (adapter->itableIndex != -1 && superITable != nullptr)
@@ -940,7 +899,6 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
const KotlinToObjCMethodAdapter* adapter = &typeAdapter->reverseAdapters[i]; const KotlinToObjCMethodAdapter* adapter = &typeAdapter->reverseAdapters[i];
throwIfCantBeOverridden(clazz, adapter); throwIfCantBeOverridden(clazz, adapter);
insertOrReplace(methodTable, adapter->nameSignature, const_cast<void*>(adapter->kotlinImpl));
RuntimeAssert(adapter->vtableIndex == -1, ""); RuntimeAssert(adapter->vtableIndex == -1, "");
if (adapter->itableIndex != -1 && superITable != nullptr) { if (adapter->itableIndex != -1 && superITable != nullptr) {
@@ -954,9 +912,8 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
// TODO: consider forbidding the class being abstract. // TODO: consider forbidding the class being abstract.
const TypeInfo* result = createTypeInfo(superType, addedInterfaces, vtable, methodTable, const TypeInfo* result = createTypeInfo(superType, addedInterfaces, vtable, interfaceVTables,
interfaceVTables, superITable, superITableSize, itableEqualsSuper, superITable, superITableSize, itableEqualsSuper, fieldsInfo);
fieldsInfo);
// TODO: it will probably never be requested, since such a class can't be instantiated in Kotlin. // TODO: it will probably never be requested, since such a class can't be instantiated in Kotlin.
result->writableInfo_->objCExport.objCClass = clazz; result->writableInfo_->objCExport.objCClass = clazz;
@@ -17,44 +17,7 @@
#include "KAssert.h" #include "KAssert.h"
#include "TypeInfo.h" #include "TypeInfo.h"
// If one shall use binary search when looking up methods and fields.
// TODO: maybe select strategy basing on number of elements.
#define USE_BINARY_SEARCH 1
extern "C" { extern "C" {
#if USE_BINARY_SEARCH
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
int bottom = 0;
int top = info->openMethodsCount_ - 1;
while (bottom <= top) {
int middle = (bottom + top) / 2;
if (info->openMethods_[middle].nameSignature_ < nameSignature)
bottom = middle + 1;
else if (info->openMethods_[middle].nameSignature_ == nameSignature)
return info->openMethods_[middle].methodEntryPoint_;
else
top = middle - 1;
}
RuntimeAssert(false, "Unknown open method");
return nullptr;
}
#else
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
for (int i = 0; i < info->openMethodsCount_; ++i) {
if (info->openMethods_[i].nameSignature_ == nameSignature) {
return info->openMethods_[i].methodEntryPoint_;
}
}
RuntimeAssert(false, "Unknown open method");
return nullptr;
}
#endif
// Seeks for the specified id. In case of failure returns a valid pointer to some record, never returns nullptr. // Seeks for the specified id. In case of failure returns a valid pointer to some record, never returns nullptr.
// It is the caller's responsibility to check if the search has succeeded or not. // It is the caller's responsibility to check if the search has succeeded or not.
@@ -29,17 +29,6 @@ struct WritableTypeInfo;
struct ObjHeader; struct ObjHeader;
struct AssociatedObjectTableRecord; struct AssociatedObjectTableRecord;
// Hash of open method name. Must be unique per class/scope (CityHash64 is being used).
typedef int64_t MethodNameHash;
// An element of sorted by hash in-place array representing methods.
// For systems where introspection is not needed - only open methods are in
// this table.
struct MethodTableRecord {
MethodNameHash nameSignature_;
void* methodEntryPoint_;
};
// Type for runtime representation of Konan object. // Type for runtime representation of Konan object.
// Keep in sync with runtimeTypeMap in RTTIGenerator. // Keep in sync with runtimeTypeMap in RTTIGenerator.
enum Konan_RuntimeType { enum Konan_RuntimeType {
@@ -123,9 +112,6 @@ struct TypeInfo {
int32_t objOffsetsCount_; int32_t objOffsetsCount_;
const TypeInfo* const* implementedInterfaces_; const TypeInfo* const* implementedInterfaces_;
int32_t implementedInterfacesCount_; int32_t implementedInterfacesCount_;
// Null for abstract classes and interfaces.
const MethodTableRecord* openMethods_;
uint32_t openMethodsCount_;
int32_t interfaceTableSize_; int32_t interfaceTableSize_;
InterfaceTableRecord const* interfaceTable_; InterfaceTableRecord const* interfaceTable_;
@@ -181,14 +167,6 @@ struct TypeInfo {
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// Find open method by its hash. Other methods are resolved in compile-time.
// Note, that we use attribute const, which assumes function doesn't
// dereference global memory, while this function does. However, it seems
// to be safe, as actual result of this computation depends only on 'type_info'
// and 'hash' numeric values and doesn't really depends on global memory state
// (as TypeInfo is compile time constant and type info pointers are stable).
void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) RUNTIME_CONST;
InterfaceTableRecord const* LookupInterfaceTableRecord(InterfaceTableRecord const* interfaceTable, InterfaceTableRecord const* LookupInterfaceTableRecord(InterfaceTableRecord const* interfaceTable,
int interfaceTableSize, ClassId interfaceId) RUNTIME_CONST; int interfaceTableSize, ClassId interfaceId) RUNTIME_CONST;