[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:
+41
-35
@@ -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 }
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-6
@@ -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)
|
||||||
|
|||||||
-1
@@ -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")
|
||||||
|
|||||||
+30
-87
@@ -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,
|
||||||
|
|||||||
-1
@@ -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")
|
||||||
|
|
||||||
|
|||||||
+7
-23
@@ -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
|
||||||
|
|||||||
+1
-1
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -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,
|
||||||
|
|||||||
+10
-8
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user