Implemented fast interface call & check
This commit is contained in:
+2
@@ -267,6 +267,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
|
||||
ClassLayoutBuilder(irClass, this)
|
||||
}
|
||||
|
||||
lateinit var globalHierarchyAnalysisResult: GlobalHierarchyAnalysisResult
|
||||
|
||||
// We serialize untouched descriptor tree and IR.
|
||||
// But we have to wait until the code generation phase,
|
||||
// to dump this information into generated file.
|
||||
|
||||
+150
-8
@@ -81,14 +81,52 @@ 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 interfaceColor: Int) {
|
||||
companion object {
|
||||
val DUMMY = ClassGlobalHierarchyInfo(0, 0)
|
||||
val DUMMY = ClassGlobalHierarchyInfo(0, 0, 0, 0)
|
||||
|
||||
// 32-items table seems like a good threshold.
|
||||
val MAX_BITS_PER_COLOR = 5
|
||||
}
|
||||
}
|
||||
|
||||
internal class GlobalHierarchyAnalysisResult(val bitsPerColor: Int)
|
||||
|
||||
internal class GlobalHierarchyAnalysis(val context: Context) {
|
||||
fun run() {
|
||||
/*
|
||||
* The algorithm for fast interface call and check:
|
||||
* Consider the following graph: the vertices are interfaces and two interfaces are
|
||||
* connected with an edge if there exists a class which inherits both of them.
|
||||
* Now find a proper vertex-coloring of that graph (such that no edge connects vertices of same color).
|
||||
* Assign to each interface a unique id in such a way that its color is stored in the lower bits of its id.
|
||||
* Assuming the number of colors used is reasonably small build then a perfect hash table for each class:
|
||||
* for each interfaceId inherited: itable[interfaceId % size] == interfaceId
|
||||
* Since we store the color in the lower bits the division can be replaced with (interfaceId & (size - 1)).
|
||||
* This is indeed a perfect hash table by construction of the coloring of the interface graph.
|
||||
* Now to perform an interface call store in all itables pointers to vtables of that particular interface.
|
||||
* Interface call: *(itable[interfaceId & (size - 1)].vtable[methodIndex])(...)
|
||||
* Interface check: itable[interfaceId & (size - 1)].id == interfaceId
|
||||
*
|
||||
* Note that we have a fallback to a more conservative version if the size of an itable is too large:
|
||||
* just save all interface ids and vtables in sorted order and find the needed one with the binary search.
|
||||
* We can signal that using the sign bit of the type info's size field:
|
||||
* if (size >= 0) { .. fast path .. }
|
||||
* else binary_search(0, -size)
|
||||
*/
|
||||
val interfaceColors = assignColorsToInterfaces()
|
||||
val maxColor = interfaceColors.values.max() ?: 0
|
||||
var bitsPerColor = 0
|
||||
var x = maxColor
|
||||
while (x > 0) {
|
||||
++bitsPerColor
|
||||
x /= 2
|
||||
}
|
||||
|
||||
val maxInterfaceId = Int.MAX_VALUE shr bitsPerColor
|
||||
val colorCounts = IntArray(maxColor + 1)
|
||||
|
||||
/*
|
||||
* Here's the explanation of what's happening here:
|
||||
* Given a tree we can traverse it with the DFS and save for each vertex two times:
|
||||
@@ -110,9 +148,17 @@ internal class GlobalHierarchyAnalysis(val context: Context) {
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
if (declaration.isInterface)
|
||||
context.getLayoutBuilder(declaration).hierarchyInfo = ClassGlobalHierarchyInfo(0, 0)
|
||||
else {
|
||||
if (declaration.isInterface) {
|
||||
val color = interfaceColors[declaration]!!
|
||||
// Numerate from 1 (reserve 0 for invalid value).
|
||||
val interfaceId = ++colorCounts[color]
|
||||
assert (interfaceId <= maxInterfaceId) {
|
||||
"Unable to assign interface id to ${declaration.name}"
|
||||
}
|
||||
context.getLayoutBuilder(declaration).hierarchyInfo =
|
||||
ClassGlobalHierarchyInfo(0, 0,
|
||||
color or (interfaceId shl bitsPerColor), color)
|
||||
} else {
|
||||
allClasses += declaration
|
||||
if (declaration != root) {
|
||||
val superClass = declaration.getSuperClassNotAny() ?: root
|
||||
@@ -131,10 +177,83 @@ internal class GlobalHierarchyAnalysis(val context: Context) {
|
||||
val enterTime = if (irClass == root) -1 else time
|
||||
immediateInheritors[irClass]?.forEach { dfs(it) }
|
||||
val exitTime = time
|
||||
context.getLayoutBuilder(irClass).hierarchyInfo = ClassGlobalHierarchyInfo(enterTime, exitTime)
|
||||
context.getLayoutBuilder(irClass).hierarchyInfo = ClassGlobalHierarchyInfo(enterTime, exitTime, 0, 0)
|
||||
}
|
||||
|
||||
dfs(root)
|
||||
|
||||
context.globalHierarchyAnalysisResult = GlobalHierarchyAnalysisResult(bitsPerColor)
|
||||
}
|
||||
|
||||
class InterfacesForbiddennessGraph(val nodes: List<IrClass>, val forbidden: List<List<Int>>) {
|
||||
|
||||
fun computeColoringGreedy(): IntArray {
|
||||
val colors = IntArray(nodes.size) { -1 }
|
||||
var numberOfColors = 0
|
||||
val usedColors = BooleanArray(nodes.size)
|
||||
for (v in nodes.indices) {
|
||||
for (c in 0 until numberOfColors)
|
||||
usedColors[c] = false
|
||||
for (u in forbidden[v])
|
||||
if (colors[u] >= 0)
|
||||
usedColors[colors[u]] = true
|
||||
var found = false
|
||||
for (c in 0 until numberOfColors)
|
||||
if (!usedColors[c]) {
|
||||
colors[v] = c
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if (!found)
|
||||
colors[v] = numberOfColors++
|
||||
}
|
||||
return colors
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun build(irModuleFragment: IrModuleFragment): InterfacesForbiddennessGraph {
|
||||
val interfaceIndices = mutableMapOf<IrClass, Int>()
|
||||
val interfaces = mutableListOf<IrClass>()
|
||||
val forbidden = mutableListOf<MutableList<Int>>()
|
||||
irModuleFragment.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
fun registerInterface(iface: IrClass) {
|
||||
interfaceIndices.getOrPut(iface) {
|
||||
forbidden.add(mutableListOf())
|
||||
interfaces.add(iface)
|
||||
interfaces.size - 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
if (declaration.isInterface)
|
||||
registerInterface(declaration)
|
||||
else {
|
||||
val implementedInterfaces = declaration.implementedInterfaces
|
||||
implementedInterfaces.forEach { registerInterface(it) }
|
||||
for (i in 0 until implementedInterfaces.size)
|
||||
for (j in i + 1 until implementedInterfaces.size) {
|
||||
val v = interfaceIndices[implementedInterfaces[i]]!!
|
||||
val u = interfaceIndices[implementedInterfaces[j]]!!
|
||||
forbidden[v].add(u)
|
||||
forbidden[u].add(v)
|
||||
}
|
||||
}
|
||||
super.visitClass(declaration)
|
||||
}
|
||||
})
|
||||
return InterfacesForbiddennessGraph(interfaces, forbidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignColorsToInterfaces(): Map<IrClass, Int> {
|
||||
val graph = InterfacesForbiddennessGraph.build(context.irModule!!)
|
||||
val coloring = graph.computeColoringGreedy()
|
||||
return graph.nodes.mapIndexed { v, irClass -> irClass to coloring[v] }.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +351,30 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
|
||||
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||
}
|
||||
|
||||
val interfaceTableEntries: List<IrSimpleFunction> by lazy {
|
||||
irClass.sortedOverridableOrOverridingMethods
|
||||
.filter { f ->
|
||||
f.isReal || f.overriddenSymbols.any { OverriddenFunctionInfo(f, it.owner).needBridge }
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
data class InterfaceTablePlace(val interfaceId: Int, val methodIndex: Int) {
|
||||
companion object {
|
||||
val INVALID = InterfaceTablePlace(0, -1)
|
||||
}
|
||||
}
|
||||
|
||||
fun itablePlace(function: IrSimpleFunction): InterfaceTablePlace {
|
||||
assert (irClass.isInterface) { "An interface expected but was ${irClass.name}" }
|
||||
val itable = interfaceTableEntries
|
||||
val index = itable.indexOf(function)
|
||||
if (index >= 0)
|
||||
return InterfaceTablePlace(hierarchyInfo.interfaceId, index)
|
||||
val superFunction = function.overriddenSymbols.first().owner
|
||||
return context.getLayoutBuilder(superFunction.parentAsClass).itablePlace(superFunction)
|
||||
}
|
||||
|
||||
/**
|
||||
* All fields of the class instance.
|
||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
||||
@@ -302,8 +445,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) {
|
||||
private val IrClass.sortedOverridableOrOverridingMethods: List<IrSimpleFunction>
|
||||
get() =
|
||||
this.simpleFunctions()
|
||||
.filter { (it.isOverridable || it.overriddenSymbols.isNotEmpty())
|
||||
&& it.bridgeTarget == null }
|
||||
.filter { it.isOverridableOrOverrides && it.bridgeTarget == null }
|
||||
.sortedBy { it.uniqueId }
|
||||
|
||||
private val functionIds = mutableMapOf<IrFunction, Long>()
|
||||
|
||||
+75
-28
@@ -9,6 +9,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.ClassGlobalHierarchyInfo
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -699,21 +700,63 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
return switch
|
||||
}
|
||||
|
||||
fun loadTypeInfo(objPtr: LLVMValueRef): LLVMValueRef {
|
||||
val typeInfoOrMetaPtr = structGep(objPtr, 0 /* typeInfoOrMeta_ */)
|
||||
val typeInfoOrMetaWithFlags = load(typeInfoOrMetaPtr)
|
||||
// Clear two lower bits.
|
||||
val typeInfoOrMetaWithFlagsRaw = ptrToInt(typeInfoOrMetaWithFlags, codegen.intPtrType)
|
||||
val typeInfoOrMetaRaw = and(typeInfoOrMetaWithFlagsRaw, codegen.immTypeInfoMask)
|
||||
val typeInfoOrMeta = intToPtr(typeInfoOrMetaRaw, kTypeInfoPtr)
|
||||
val typeInfoPtrPtr = structGep(typeInfoOrMeta, 0 /* typeInfo */)
|
||||
return load(typeInfoPtrPtr)
|
||||
}
|
||||
|
||||
fun lookupInterfaceTableRecord(typeInfo: LLVMValueRef, interfaceId: Int): LLVMValueRef {
|
||||
val interfaceTableSize = load(structGep(typeInfo, 11 /* interfaceTableSize_ */))
|
||||
val interfaceTable = load(structGep(typeInfo, 12 /* interfaceTable_ */))
|
||||
|
||||
fun fastPath(): LLVMValueRef {
|
||||
// The fastest optimistic version.
|
||||
val interfaceTableIndex = and(interfaceTableSize, Int32(interfaceId).llvm)
|
||||
return gep(interfaceTable, interfaceTableIndex)
|
||||
}
|
||||
|
||||
// See details in ClassLayoutBuilder.
|
||||
return if (context.globalHierarchyAnalysisResult.bitsPerColor <= ClassGlobalHierarchyInfo.MAX_BITS_PER_COLOR
|
||||
&& context.config.produce != CompilerOutputKind.FRAMEWORK) {
|
||||
// All interface tables are small and no unknown interface inheritance.
|
||||
fastPath()
|
||||
} else {
|
||||
val startLocationInfo = position()?.start
|
||||
val fastPathBB = basicBlock("fast_path", startLocationInfo)
|
||||
val slowPathBB = basicBlock("slow_path", startLocationInfo)
|
||||
val takeResBB = basicBlock("take_res", startLocationInfo)
|
||||
condBr(icmpGe(interfaceTableSize, kImmInt32Zero), fastPathBB, slowPathBB)
|
||||
positionAtEnd(takeResBB)
|
||||
val resultPhi = phi(pointerType(runtime.interfaceTableRecordType))
|
||||
appendingTo(fastPathBB) {
|
||||
val fastValue = fastPath()
|
||||
br(takeResBB)
|
||||
addPhiIncoming(resultPhi, currentBlock to fastValue)
|
||||
}
|
||||
appendingTo(slowPathBB) {
|
||||
val actualInterfaceTableSize = sub(kImmInt32Zero, interfaceTableSize) // -interfaceTableSize
|
||||
val slowValue = call(context.llvm.lookupInterfaceTableRecord,
|
||||
listOf(interfaceTable, actualInterfaceTableSize, Int32(interfaceId).llvm))
|
||||
br(takeResBB)
|
||||
addPhiIncoming(resultPhi, currentBlock to slowValue)
|
||||
}
|
||||
resultPhi
|
||||
}
|
||||
}
|
||||
|
||||
fun lookupVirtualImpl(receiver: LLVMValueRef, irFunction: IrFunction): LLVMValueRef {
|
||||
assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr)
|
||||
|
||||
val typeInfoPtr: LLVMValueRef = if (irFunction.getObjCMethodInfo() != null) {
|
||||
val typeInfoPtr: LLVMValueRef = if (irFunction.getObjCMethodInfo() != null)
|
||||
call(context.llvm.getObjCKotlinTypeInfo, listOf(receiver))
|
||||
} else {
|
||||
val typeInfoOrMetaPtr = structGep(receiver, 0 /* typeInfoOrMeta_ */)
|
||||
val typeInfoOrMetaWithFlags = load(typeInfoOrMetaPtr)
|
||||
// Clear two lower bits.
|
||||
val typeInfoOrMetaWithFlagsRaw = ptrToInt(typeInfoOrMetaWithFlags, codegen.intPtrType)
|
||||
val typeInfoOrMetaRaw = and(typeInfoOrMetaWithFlagsRaw, codegen.immTypeInfoMask)
|
||||
val typeInfoOrMeta = intToPtr(typeInfoOrMetaRaw, kTypeInfoPtr)
|
||||
val typeInfoPtrPtr = structGep(typeInfoOrMeta, 0 /* typeInfo */)
|
||||
load(typeInfoPtrPtr)
|
||||
}
|
||||
else
|
||||
loadTypeInfo(receiver)
|
||||
|
||||
assert(typeInfoPtr.type == codegen.kTypeInfoPtr) { LLVMPrintTypeToString(typeInfoPtr.type)!!.toKString() }
|
||||
|
||||
@@ -724,25 +767,29 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
*/
|
||||
val anyMethod = (irFunction as IrSimpleFunction).findOverriddenMethodOfAny()
|
||||
val owner = (anyMethod ?: irFunction).parentAsClass
|
||||
val methodHash = codegen.functionHash(irFunction)
|
||||
|
||||
val llvmMethod = if (!owner.isInterface) {
|
||||
// If this is a virtual method of the class - we can call via vtable.
|
||||
val index = context.getLayoutBuilder(owner).vtableIndex(anyMethod ?: irFunction)
|
||||
val vtablePlace = gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
|
||||
val vtable = bitcast(kInt8PtrPtr, vtablePlace)
|
||||
val slot = gep(vtable, Int32(index).llvm)
|
||||
load(slot)
|
||||
} else {
|
||||
// Otherwise, call by hash.
|
||||
// TODO: optimize by storing interface number in lower bits of 'this' pointer
|
||||
// when passing object as an interface. This way we can use those bits as index
|
||||
// for an additional per-interface vtable.
|
||||
val methodHash = codegen.functionHash(irFunction) // Calculate hash of the method to be invoked
|
||||
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
|
||||
call(context.llvm.lookupOpenMethodFunction, lookupArgs)
|
||||
val llvmMethod = when {
|
||||
!owner.isInterface -> {
|
||||
// If this is a virtual method of the class - we can call via vtable.
|
||||
val index = context.getLayoutBuilder(owner).vtableIndex(anyMethod ?: irFunction)
|
||||
val vtablePlace = gep(typeInfoPtr, Int32(1).llvm) // typeInfoPtr + 1
|
||||
val vtable = bitcast(kInt8PtrPtr, vtablePlace)
|
||||
val slot = gep(vtable, Int32(index).llvm)
|
||||
load(slot)
|
||||
}
|
||||
|
||||
!context.shouldOptimize() -> call(context.llvm.lookupOpenMethodFunction, listOf(typeInfoPtr, methodHash))
|
||||
|
||||
else -> {
|
||||
// Essentially: typeInfo.itable[place(interfaceId)].vtable[method]
|
||||
val itablePlace = context.getLayoutBuilder(owner).itablePlace(irFunction)
|
||||
val interfaceTableRecord = lookupInterfaceTableRecord(typeInfoPtr, itablePlace.interfaceId)
|
||||
load(gep(load(structGep(interfaceTableRecord, 2 /* vtable */)), Int32(itablePlace.methodIndex).llvm))
|
||||
}
|
||||
}
|
||||
val functionPtrType = pointerType(codegen.getLlvmFunctionType(irFunction)) // Construct type of the method to be invoked
|
||||
return bitcast(functionPtrType, llvmMethod) // Cast method address to the type
|
||||
val functionPtrType = pointerType(codegen.getLlvmFunctionType(irFunction))
|
||||
return bitcast(functionPtrType, llvmMethod)
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? {
|
||||
|
||||
+1
@@ -435,6 +435,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
val enterFrameFunction = importModelSpecificRtFunction("EnterFrame")
|
||||
val leaveFrameFunction = importModelSpecificRtFunction("LeaveFrame")
|
||||
val lookupOpenMethodFunction = importRtFunction("LookupOpenMethod")
|
||||
val lookupInterfaceTableRecord = importRtFunction("LookupInterfaceTableRecord")
|
||||
val isInstanceFunction = importRtFunction("IsInstance")
|
||||
val isInstanceOfClassFastFunction = importRtFunction("IsInstanceOfClassFast")
|
||||
val throwExceptionFunction = importRtFunction("ThrowException")
|
||||
|
||||
+16
-6
@@ -1391,13 +1391,23 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
|
||||
val srcObjInfoPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, obj)
|
||||
return if (context.shouldOptimize() && !dstClass.isInterface) {
|
||||
val dstHierarchyInfo = context.getLayoutBuilder(dstClass).hierarchyInfo
|
||||
call(context.llvm.isInstanceOfClassFastFunction,
|
||||
listOf(srcObjInfoPtr, Int32(dstHierarchyInfo.classIdLo).llvm, Int32(dstHierarchyInfo.classIdHi).llvm))
|
||||
|
||||
return if (!context.shouldOptimize()) {
|
||||
call(context.llvm.isInstanceFunction, listOf(srcObjInfoPtr, codegen.typeInfoValue(dstClass)))
|
||||
} else {
|
||||
val dstTypeInfo = codegen.typeInfoValue(dstClass)
|
||||
call(context.llvm.isInstanceFunction, listOf(srcObjInfoPtr, dstTypeInfo))
|
||||
val dstHierarchyInfo = context.getLayoutBuilder(dstClass).hierarchyInfo
|
||||
if (!dstClass.isInterface) {
|
||||
call(context.llvm.isInstanceOfClassFastFunction,
|
||||
listOf(srcObjInfoPtr, Int32(dstHierarchyInfo.classIdLo).llvm, Int32(dstHierarchyInfo.classIdHi).llvm))
|
||||
} else {
|
||||
// Essentially: typeInfo.itable[place(interfaceId)].id == interfaceId
|
||||
val interfaceId = dstHierarchyInfo.interfaceId
|
||||
val typeInfo = functionGenerationContext.loadTypeInfo(srcObjInfoPtr)
|
||||
with(functionGenerationContext) {
|
||||
val interfaceTableRecord = lookupInterfaceTableRecord(typeInfo, interfaceId)
|
||||
icmpEq(load(structGep(interfaceTableRecord, 0 /* id */)), Int32(interfaceId).llvm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -42,6 +42,13 @@ private class ConstGetElementPtr(val pointer: ConstPointer, val index: Int) : Co
|
||||
internal fun ConstPointer.bitcast(toType: LLVMTypeRef) = constPointer(LLVMConstBitCast(this.llvm, toType)!!)
|
||||
|
||||
internal class ConstArray(elementType: LLVMTypeRef?, val elements: List<ConstValue>) : ConstValue {
|
||||
init {
|
||||
elements.forEach {
|
||||
assert(it.llvmType == elementType) {
|
||||
"Expected element type: ${llvmtype2string(elementType)}, actual: ${llvmtype2string(it.llvmType)}"
|
||||
}
|
||||
}
|
||||
}
|
||||
override val llvm = LLVMConstArray(elementType, elements.map { it.llvm }.toCValues(), elements.size)!!
|
||||
}
|
||||
|
||||
@@ -144,6 +151,7 @@ internal val kBoolean = kInt1
|
||||
internal val kInt8Ptr = pointerType(int8Type)
|
||||
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
|
||||
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)!!
|
||||
internal val kImmInt32Zero = Int32(0).llvm
|
||||
internal val kImmInt32One = Int32(1).llvm
|
||||
internal val ContextUtils.kNullObjHeaderPtr: LLVMValueRef
|
||||
get() = LLVMConstNull(this.kObjHeaderPtr)!!
|
||||
|
||||
+110
-5
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
@@ -68,6 +69,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
inner class MethodTableRecord(val nameSignature: LocalHash, methodEntryPoint: ConstPointer?) :
|
||||
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
|
||||
|
||||
inner class InterfaceTableRecord(id: Int32, vtableSize: Int32, vtable: ConstPointer) :
|
||||
Struct(runtime.interfaceTableRecordType, id, vtableSize, vtable)
|
||||
|
||||
private inner class TypeInfo(
|
||||
selfPtr: ConstPointer,
|
||||
extendedInfo: ConstPointer,
|
||||
@@ -79,6 +83,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
interfacesCount: Int,
|
||||
methods: ConstValue,
|
||||
methodsCount: Int,
|
||||
interfaceTableSize: Int,
|
||||
interfaceTable: ConstPointer?,
|
||||
packageName: String?,
|
||||
relativeName: String?,
|
||||
flags: Int,
|
||||
@@ -108,6 +114,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
methods,
|
||||
Int32(methodsCount),
|
||||
|
||||
Int32(interfaceTableSize),
|
||||
interfaceTable,
|
||||
|
||||
kotlinStringLiteral(packageName),
|
||||
kotlinStringLiteral(relativeName),
|
||||
|
||||
@@ -181,13 +190,15 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
val bodyType = llvmDeclarations.bodyType
|
||||
|
||||
val size = getInstanceSize(bodyType, className)
|
||||
val instanceSize = getInstanceSize(bodyType, className)
|
||||
|
||||
val superTypeOrAny = irClass.getSuperClassNotAny() ?: context.ir.symbols.any.owner
|
||||
val superType = if (irClass.isAny()) NullPointer(runtime.typeInfoType)
|
||||
else superTypeOrAny.typeInfoPtr
|
||||
|
||||
val interfaces = irClass.implementedInterfaces.map { it.typeInfoPtr }
|
||||
val implementedInterfaces = irClass.implementedInterfaces
|
||||
|
||||
val interfaces = implementedInterfaces.map { it.typeInfoPtr }
|
||||
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
|
||||
pointerType(runtime.typeInfoType), interfaces)
|
||||
|
||||
@@ -209,6 +220,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
||||
runtime.methodTableRecordType, methods)
|
||||
|
||||
val (interfaceTable, interfaceTableSize) = interfaceTable(irClass)
|
||||
|
||||
val reflectionInfo = getReflectionInfo(irClass)
|
||||
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
|
||||
val hierarchyInfo =
|
||||
@@ -218,15 +231,18 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
val typeInfo = TypeInfo(
|
||||
irClass.typeInfoPtr,
|
||||
makeExtendedInfo(irClass),
|
||||
size,
|
||||
instanceSize,
|
||||
superType,
|
||||
objOffsetsPtr, objOffsetsCount,
|
||||
interfacesPtr, interfaces.size,
|
||||
methodsPtr, methods.size,
|
||||
interfaceTableSize, interfaceTable,
|
||||
reflectionInfo.packageName,
|
||||
reflectionInfo.relativeName,
|
||||
flagsFromClass(irClass),
|
||||
hierarchyInfo.classIdLo,
|
||||
if (irClass.isInterface)
|
||||
hierarchyInfo.interfaceId
|
||||
else hierarchyInfo.classIdLo,
|
||||
llvmDeclarations.writableTypeInfoGlobal?.pointer,
|
||||
associatedObjects = genAssociatedObjects(irClass)
|
||||
)
|
||||
@@ -285,6 +301,78 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
}
|
||||
|
||||
private fun interfaceTable(irClass: IrClass): Pair<ConstPointer?, Int> {
|
||||
val needInterfaceTable = context.shouldOptimize() && !irClass.isInterface
|
||||
&& !irClass.isAbstract() && !irClass.isObjCClass()
|
||||
if (!needInterfaceTable) return Pair(null, 0)
|
||||
// The details are in ClassLayoutBuilder.
|
||||
val interfaceLayouts = irClass.implementedInterfaces.map { context.getLayoutBuilder(it) }
|
||||
val interfaceColors = interfaceLayouts.map { it.hierarchyInfo.interfaceColor }
|
||||
|
||||
// Find the optimal size. It must be a power of 2.
|
||||
var size = 1
|
||||
val maxSize = 1 shl ClassGlobalHierarchyInfo.MAX_BITS_PER_COLOR
|
||||
val used = BooleanArray(maxSize)
|
||||
while (size <= maxSize) {
|
||||
for (i in 0 until size)
|
||||
used[i] = false
|
||||
// Check for collisions.
|
||||
var ok = true
|
||||
for (color in interfaceColors) {
|
||||
val index = color % size
|
||||
if (used[index]) {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
used[index] = true
|
||||
}
|
||||
if (ok) break
|
||||
size *= 2
|
||||
}
|
||||
val conservative = size > maxSize
|
||||
|
||||
val interfaceTableSkeleton = if (conservative) {
|
||||
size = interfaceLayouts.size
|
||||
interfaceLayouts.sortedBy { it.hierarchyInfo.interfaceId }.toTypedArray()
|
||||
} else arrayOfNulls<ClassLayoutBuilder?>(size).also {
|
||||
for (interfaceLayout in interfaceLayouts)
|
||||
it[interfaceLayout.hierarchyInfo.interfaceId % size] = interfaceLayout
|
||||
}
|
||||
|
||||
val methodTableEntries = context.getLayoutBuilder(irClass).methodTableEntries
|
||||
val className = irClass.fqNameForIrSerialization
|
||||
|
||||
val interfaceTableEntries = interfaceTableSkeleton.map { iface ->
|
||||
val interfaceId = iface?.hierarchyInfo?.interfaceId ?: 0
|
||||
InterfaceTableRecord(
|
||||
Int32(interfaceId),
|
||||
Int32(iface?.interfaceTableEntries?.size ?: 0),
|
||||
if (iface == null)
|
||||
NullPointer(kInt8Ptr)
|
||||
else {
|
||||
val vtableEntries = iface.interfaceTableEntries.map { ifaceFunction ->
|
||||
val impl = OverriddenFunctionInfo(
|
||||
methodTableEntries.first { ifaceFunction in it.function.allOverriddenFunctions }.function,
|
||||
ifaceFunction
|
||||
).implementation
|
||||
if (impl == null || context.referencedFunctions?.contains(impl) == false)
|
||||
NullPointer(int8Type)
|
||||
else impl.entryPointAddress
|
||||
}
|
||||
|
||||
staticData.placeGlobalConstArray("kifacevtable:${className}_$interfaceId",
|
||||
kInt8Ptr, vtableEntries
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
return Pair(
|
||||
staticData.placeGlobalConstArray("kifacetable:$className",
|
||||
runtime.interfaceTableRecordType, interfaceTableEntries),
|
||||
if (conservative) -size else (size - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private fun mapRuntimeType(type: LLVMTypeRef): Int =
|
||||
runtimeTypeMap[type] ?: throw Error("Unmapped type: ${llvmtype2string(type)}")
|
||||
|
||||
@@ -399,7 +487,22 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
val typeHierarchyInfo = if (!context.shouldOptimize())
|
||||
ClassGlobalHierarchyInfo.DUMMY
|
||||
else
|
||||
ClassGlobalHierarchyInfo(-1, -1)
|
||||
ClassGlobalHierarchyInfo(-1, -1, 0, 0)
|
||||
|
||||
val interfaceTable = if (!context.shouldOptimize()) null else {
|
||||
val layoutBuilder = context.getLayoutBuilder(irClass)
|
||||
val vtableEntries = layoutBuilder.interfaceTableEntries.map { methodImpls[it]!!.bitcast(int8TypePtr) }
|
||||
val interfaceVTable = staticData.placeGlobalArray("", kInt8Ptr, vtableEntries)
|
||||
staticData.placeGlobalConstArray("",
|
||||
runtime.interfaceTableRecordType, listOf(
|
||||
InterfaceTableRecord(
|
||||
Int32(layoutBuilder.hierarchyInfo.interfaceId),
|
||||
Int32(layoutBuilder.interfaceTableEntries.size),
|
||||
interfaceVTable.pointer.getElementPtr(0)
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
val typeInfoWithVtable = Struct(TypeInfo(
|
||||
selfPtr = result,
|
||||
extendedInfo = NullPointer(runtime.extendedTypeInfoType),
|
||||
@@ -408,6 +511,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
objOffsets = objOffsetsPtr, objOffsetsCount = objOffsetsCount,
|
||||
interfaces = interfacesPtr, interfacesCount = interfaces.size,
|
||||
methods = methodsPtr, methodsCount = methods.size,
|
||||
// 0 is a perfectly valid itable size (since 0 is in form of (2^k - 1).
|
||||
interfaceTableSize = 0, interfaceTable = interfaceTable,
|
||||
packageName = reflectionInfo.packageName,
|
||||
relativeName = reflectionInfo.relativeName,
|
||||
flags = flagsFromClass(irClass) or (if (immutable) TF_IMMUTABLE else 0),
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ class Runtime(bitcodeFile: String) {
|
||||
val extendedTypeInfoType = getStructType("ExtendedTypeInfo")
|
||||
val writableTypeInfoType = getStructTypeOrNull("WritableTypeInfo")
|
||||
val methodTableRecordType = getStructType("MethodTableRecord")
|
||||
val interfaceTableRecordType = getStructType("InterfaceTableRecord")
|
||||
val globalHashType = getStructType("GlobalHash")
|
||||
val associatedObjectTableRecordType = getStructType("AssociatedObjectTableRecord")
|
||||
|
||||
|
||||
+40
-5
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCDataGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -23,6 +24,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.isInterface
|
||||
import org.jetbrains.kotlin.ir.util.isReal
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -317,12 +319,15 @@ internal class ObjCExportCodeGenerator(
|
||||
inner class KotlinToObjCMethodAdapter(
|
||||
selector: String,
|
||||
nameSignature: Long,
|
||||
itablePlace: ClassLayoutBuilder.InterfaceTablePlace,
|
||||
vtableIndex: Int,
|
||||
kotlinImpl: ConstPointer
|
||||
) : Struct(
|
||||
runtime.kotlinToObjCMethodAdapter,
|
||||
staticData.cStringLiteral(selector),
|
||||
Int64(nameSignature),
|
||||
Int32(itablePlace.interfaceId),
|
||||
Int32(itablePlace.methodIndex),
|
||||
Int32(vtableIndex),
|
||||
kotlinImpl
|
||||
)
|
||||
@@ -333,6 +338,7 @@ internal class ObjCExportCodeGenerator(
|
||||
vtable: ConstPointer?,
|
||||
vtableSize: Int,
|
||||
methodTable: List<RTTIGenerator.MethodTableRecord>,
|
||||
itableSize: Int,
|
||||
val objCName: String,
|
||||
directAdapters: List<ObjCToKotlinMethodAdapter>,
|
||||
classAdapters: List<ObjCToKotlinMethodAdapter>,
|
||||
@@ -348,6 +354,8 @@ internal class ObjCExportCodeGenerator(
|
||||
staticData.placeGlobalConstArray("", runtime.methodTableRecordType, methodTable),
|
||||
Int32(methodTable.size),
|
||||
|
||||
Int32(itableSize),
|
||||
|
||||
staticData.cStringLiteral(objCName),
|
||||
|
||||
staticData.placeGlobalConstArray(
|
||||
@@ -842,7 +850,8 @@ private fun ObjCExportCodeGenerator.createReverseAdapter(
|
||||
irFunction: IrFunction,
|
||||
baseMethod: IrFunction,
|
||||
functionName: String,
|
||||
vtableIndex: Int?
|
||||
vtableIndex: Int?,
|
||||
itablePlace: ClassLayoutBuilder.InterfaceTablePlace?
|
||||
): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter {
|
||||
|
||||
val nameSignature = functionName.localHash.value
|
||||
@@ -853,7 +862,10 @@ private fun ObjCExportCodeGenerator.createReverseAdapter(
|
||||
baseMethod
|
||||
).bitcast(int8TypePtr)
|
||||
|
||||
return KotlinToObjCMethodAdapter(selector, nameSignature, vtableIndex ?: -1, kotlinToObjC)
|
||||
return KotlinToObjCMethodAdapter(selector, nameSignature,
|
||||
itablePlace ?: ClassLayoutBuilder.InterfaceTablePlace.INVALID,
|
||||
vtableIndex ?: -1,
|
||||
kotlinToObjC)
|
||||
}
|
||||
|
||||
private fun ObjCExportCodeGenerator.createMethodVirtualAdapter(
|
||||
@@ -917,6 +929,17 @@ private fun ObjCExportCodeGenerator.vtableIndex(irFunction: IrSimpleFunction): I
|
||||
}
|
||||
}
|
||||
|
||||
private fun ObjCExportCodeGenerator.itablePlace(irFunction: IrSimpleFunction): ClassLayoutBuilder.InterfaceTablePlace? {
|
||||
assert(irFunction.isOverridable)
|
||||
val irClass = irFunction.parentAsClass
|
||||
return if (irClass.isInterface && context.shouldOptimize()
|
||||
&& (irFunction.isReal || irFunction.resolveFakeOverrideMaybeAbstract().parent != context.irBuiltIns.anyClass.owner)) {
|
||||
context.getLayoutBuilder(irClass).itablePlace(irFunction)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
|
||||
fileClass: ObjCClassForKotlinFile
|
||||
): ObjCExportCodeGenerator.ObjCTypeAdapter {
|
||||
@@ -930,6 +953,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
|
||||
vtable = null,
|
||||
vtableSize = -1,
|
||||
methodTable = emptyList(),
|
||||
itableSize = -1,
|
||||
objCName = name,
|
||||
directAdapters = emptyList(),
|
||||
classAdapters = adapters,
|
||||
@@ -1003,6 +1027,10 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
|
||||
emptyList()
|
||||
}
|
||||
|
||||
val itableSize = if (irClass.isInterface)
|
||||
context.getLayoutBuilder(irClass).interfaceTableEntries.size
|
||||
else -1
|
||||
|
||||
when (irClass.kind) {
|
||||
ClassKind.OBJECT -> {
|
||||
classAdapters += if (irClass.isUnit()) {
|
||||
@@ -1022,6 +1050,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
|
||||
vtable,
|
||||
vtableSize,
|
||||
methodTable,
|
||||
itableSize,
|
||||
objCName,
|
||||
adapters,
|
||||
classAdapters,
|
||||
@@ -1047,6 +1076,7 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
|
||||
|
||||
val presentVtableBridges = mutableSetOf<Int?>(null)
|
||||
val presentMethodTableBridges = mutableSetOf<String>()
|
||||
val presentItableBridges = mutableSetOf<ClassLayoutBuilder.InterfaceTablePlace?>(null)
|
||||
|
||||
val allOverriddenMethods = method.allOverriddenFunctions
|
||||
|
||||
@@ -1057,16 +1087,20 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
|
||||
inherited.forEach {
|
||||
presentVtableBridges += vtableIndex(it)
|
||||
presentMethodTableBridges += it.functionName
|
||||
presentItableBridges += itablePlace(it)
|
||||
}
|
||||
|
||||
uninherited.forEach {
|
||||
val vtableIndex = vtableIndex(it)
|
||||
val functionName = it.functionName
|
||||
val itablePlace = itablePlace(it)
|
||||
|
||||
if (vtableIndex !in presentVtableBridges || functionName !in presentMethodTableBridges) {
|
||||
if (vtableIndex !in presentVtableBridges || functionName !in presentMethodTableBridges
|
||||
|| itablePlace !in presentItableBridges) {
|
||||
presentVtableBridges += vtableIndex
|
||||
presentMethodTableBridges += functionName
|
||||
result += createReverseAdapter(it, baseMethod, functionName, vtableIndex)
|
||||
presentItableBridges += itablePlace
|
||||
result += createReverseAdapter(it, baseMethod, functionName, vtableIndex, itablePlace)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1088,7 +1122,8 @@ private fun ObjCExportCodeGenerator.nonOverridableAdapter(
|
||||
namer.getSelector(baseMethod),
|
||||
-1,
|
||||
vtableIndex = if (hasSelectorAmbiguity) -2 else -1, // Describes the reason.
|
||||
kotlinImpl = NullPointer(int8Type)
|
||||
kotlinImpl = NullPointer(int8Type),
|
||||
itablePlace = ClassLayoutBuilder.InterfaceTablePlace.INVALID
|
||||
)
|
||||
|
||||
private val ObjCTypeForKotlinType.kotlinMethods: List<ObjCMethodForKotlinMethod>
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ internal class InitializersLowering(val context: CommonBackendContext) : ClassLo
|
||||
object DECLARATION_ORIGIN_ANONYMOUS_INITIALIZER : IrDeclarationOriginImpl("ANONYMOUS_INITIALIZER")
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
if (irClass.isInterface) return
|
||||
InitializersTransformer(irClass).lowerInitializers()
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -529,6 +529,9 @@ internal object DataFlowIR {
|
||||
layoutBuilder.methodTableEntries.forEach {
|
||||
type.itable[it.overriddenFunction.functionName.localHash.value] = mapFunction(it.getImplementation(context)!!)
|
||||
}
|
||||
} else if (irClass.isInterface) {
|
||||
// Warmup interface table so it is computed before DCE.
|
||||
context.getLayoutBuilder(irClass).interfaceTableEntries
|
||||
}
|
||||
return type
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ struct ObjCToKotlinMethodAdapter {
|
||||
struct KotlinToObjCMethodAdapter {
|
||||
const char* selector;
|
||||
MethodNameHash nameSignature;
|
||||
ClassId interfaceId;
|
||||
int itableIndex;
|
||||
int vtableIndex;
|
||||
const void* kotlinImpl;
|
||||
};
|
||||
@@ -64,6 +66,8 @@ struct ObjCTypeAdapter {
|
||||
const MethodTableRecord* kotlinMethodTable;
|
||||
int kotlinMethodTableSize;
|
||||
|
||||
int kotlinItableSize;
|
||||
|
||||
const char* objCName;
|
||||
|
||||
const ObjCToKotlinMethodAdapter* directAdapters;
|
||||
@@ -717,11 +721,70 @@ static id Kotlin_ObjCExport_refToObjC_slowpath(ObjHeader* obj) {
|
||||
return converter(obj);
|
||||
}
|
||||
|
||||
static void buildITable(TypeInfo* result, const KStdOrderedMap<ClassId, KStdVector<VTableElement>>& interfaceVTables) {
|
||||
// Check if can use fast optimistic version - check if the size of the itable could be 2^k and <= 32.
|
||||
bool useFastITable;
|
||||
int itableSize = 1;
|
||||
for (; itableSize <= 32; itableSize <<= 1) {
|
||||
useFastITable = true;
|
||||
bool used[32];
|
||||
memset(used, 0, sizeof(used));
|
||||
for (auto& pair : interfaceVTables) {
|
||||
auto interfaceId = pair.first;
|
||||
auto index = interfaceId & (itableSize - 1);
|
||||
if (used[index]) {
|
||||
useFastITable = false;
|
||||
break;
|
||||
}
|
||||
used[index] = true;
|
||||
}
|
||||
if (useFastITable) break;
|
||||
}
|
||||
if (!useFastITable)
|
||||
itableSize = interfaceVTables.size();
|
||||
|
||||
auto itable_ = konanAllocArray<InterfaceTableRecord>(itableSize);
|
||||
result->interfaceTable_ = itable_;
|
||||
result->interfaceTableSize_ = useFastITable ? itableSize - 1 : -itableSize;
|
||||
|
||||
if (useFastITable) {
|
||||
for (auto& pair : interfaceVTables) {
|
||||
auto interfaceId = pair.first;
|
||||
auto index = interfaceId & (itableSize - 1);
|
||||
itable_[index].id = interfaceId;
|
||||
}
|
||||
} else {
|
||||
// Otherwise: conservative version.
|
||||
// The table will be sorted since we're using KStdOrderedMap.
|
||||
int index = 0;
|
||||
for (auto& pair : interfaceVTables) {
|
||||
auto interfaceId = pair.first;
|
||||
itable_[index++].id = interfaceId;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < itableSize; ++i) {
|
||||
auto interfaceId = itable_[i].id;
|
||||
if (interfaceId == kInvalidInterfaceId) continue;
|
||||
auto interfaceVTableIt = interfaceVTables.find(interfaceId);
|
||||
RuntimeAssert(interfaceVTableIt != interfaceVTables.end(), "");
|
||||
auto const& interfaceVTable = interfaceVTableIt->second;
|
||||
int interfaceVTableSize = interfaceVTable.size();
|
||||
auto interfaceVTable_ = interfaceVTableSize == 0 ? nullptr : konanAllocArray<VTableElement>(interfaceVTableSize);
|
||||
for (int j = 0; j < interfaceVTableSize; ++j)
|
||||
interfaceVTable_[j] = interfaceVTable[j];
|
||||
itable_[i].vtable = interfaceVTable_;
|
||||
itable_[i].vtableSize = interfaceVTableSize;
|
||||
}
|
||||
}
|
||||
|
||||
static const TypeInfo* createTypeInfo(
|
||||
const TypeInfo* superType,
|
||||
const KStdVector<const TypeInfo*>& superInterfaces,
|
||||
const KStdVector<const void*>& vtable,
|
||||
const KStdVector<MethodTableRecord>& methodTable
|
||||
const KStdVector<VTableElement>& vtable,
|
||||
const KStdVector<MethodTableRecord>& methodTable,
|
||||
const KStdOrderedMap<ClassId, KStdVector<VTableElement>>& interfaceVTables,
|
||||
bool itableEqualsSuper
|
||||
) {
|
||||
TypeInfo* result = (TypeInfo*)konanAllocMemory(sizeof(TypeInfo) + vtable.size() * sizeof(void*));
|
||||
result->typeInfo_ = result;
|
||||
@@ -756,6 +819,15 @@ static const TypeInfo* createTypeInfo(
|
||||
|
||||
result->implementedInterfaces_ = implementedInterfaces_;
|
||||
result->implementedInterfacesCount_ = implementedInterfaces.size();
|
||||
const InterfaceTableRecord* superItable = superType->interfaceTable_;
|
||||
if (superItable != nullptr) {
|
||||
if (itableEqualsSuper) {
|
||||
result->interfaceTableSize_ = superType->interfaceTableSize_;
|
||||
result->interfaceTable_ = superType->interfaceTable_;
|
||||
} else {
|
||||
buildITable(result, interfaceVTables);
|
||||
}
|
||||
}
|
||||
|
||||
MethodTableRecord* openMethods_ = konanAllocArray<MethodTableRecord>(methodTable.size());
|
||||
for (int i = 0; i < methodTable.size(); ++i) openMethods_[i] = methodTable[i];
|
||||
@@ -906,6 +978,23 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType) {
|
||||
superMethodTable, superMethodTable + superMethodTableSize
|
||||
);
|
||||
|
||||
InterfaceTableRecord const* superITable = superType->interfaceTable_;
|
||||
KStdOrderedMap<ClassId, KStdVector<VTableElement>> interfaceVTables;
|
||||
if (superITable != nullptr) {
|
||||
int superITableSize = superType->interfaceTableSize_;
|
||||
superITableSize = superITableSize >= 0 ? superITableSize + 1 : -superITableSize;
|
||||
for (int i = 0; i < superITableSize; ++i) {
|
||||
auto& record = superITable[i];
|
||||
auto interfaceId = record.id;
|
||||
if (interfaceId == kInvalidInterfaceId) continue;
|
||||
int vtableSize = record.vtableSize;
|
||||
KStdVector<VTableElement> interfaceVTable(vtableSize);
|
||||
for (int j = 0; j < vtableSize; ++j)
|
||||
interfaceVTable[j] = record.vtable[j];
|
||||
interfaceVTables.emplace(interfaceId, std::move(interfaceVTable));
|
||||
}
|
||||
}
|
||||
|
||||
KStdVector<const TypeInfo*> addedInterfaces = getProtocolsAsInterfaces(clazz);
|
||||
|
||||
KStdVector<const TypeInfo*> supers(
|
||||
@@ -917,6 +1006,16 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType) {
|
||||
supers.push_back(t);
|
||||
}
|
||||
|
||||
auto addToITable = [&interfaceVTables](ClassId interfaceId, int methodIndex, VTableElement entry) {
|
||||
RuntimeAssert(interfaceId != kInvalidInterfaceId, "");
|
||||
auto interfaceVTableIt = interfaceVTables.find(interfaceId);
|
||||
RuntimeAssert(interfaceVTableIt != interfaceVTables.end(), "");
|
||||
auto& interfaceVTable = interfaceVTableIt->second;
|
||||
RuntimeAssert(methodIndex >= 0 && methodIndex < interfaceVTable.size(), "");
|
||||
interfaceVTable[methodIndex] = entry;
|
||||
};
|
||||
|
||||
bool itableEqualsSuper = true;
|
||||
for (const TypeInfo* t : supers) {
|
||||
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(t);
|
||||
if (typeAdapter == nullptr) continue;
|
||||
@@ -927,27 +1026,50 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType) {
|
||||
|
||||
throwIfCantBeOverridden(clazz, adapter);
|
||||
|
||||
itableEqualsSuper = false;
|
||||
insertOrReplace(methodTable, adapter->nameSignature, const_cast<void*>(adapter->kotlinImpl));
|
||||
if (adapter->vtableIndex != -1) vtable[adapter->vtableIndex] = adapter->kotlinImpl;
|
||||
|
||||
if (adapter->itableIndex != -1 && superITable != nullptr)
|
||||
addToITable(adapter->interfaceId, adapter->itableIndex, adapter->kotlinImpl);
|
||||
}
|
||||
}
|
||||
|
||||
for (const TypeInfo* typeInfo : addedInterfaces) {
|
||||
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(typeInfo);
|
||||
|
||||
if (typeAdapter == nullptr) continue;
|
||||
|
||||
if (superITable != nullptr) {
|
||||
auto interfaceId = typeInfo->classId_;
|
||||
int interfaceVTableSize = typeAdapter->kotlinItableSize;
|
||||
RuntimeAssert(interfaceVTableSize >= 0, "");
|
||||
auto interfaceVTablesIt = interfaceVTables.find(interfaceId);
|
||||
if (interfaceVTablesIt == interfaceVTables.end()) {
|
||||
itableEqualsSuper = false;
|
||||
interfaceVTables.emplace(interfaceId, std::move(KStdVector<VTableElement>(interfaceVTableSize)));
|
||||
} else {
|
||||
auto const& interfaceVTable = interfaceVTablesIt->second;
|
||||
RuntimeAssert(interfaceVTable.size() == interfaceVTableSize, "");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < typeAdapter->reverseAdapterNum; ++i) {
|
||||
itableEqualsSuper = false;
|
||||
const KotlinToObjCMethodAdapter* adapter = &typeAdapter->reverseAdapters[i];
|
||||
throwIfCantBeOverridden(clazz, adapter);
|
||||
|
||||
insertOrReplace(methodTable, adapter->nameSignature, const_cast<void*>(adapter->kotlinImpl));
|
||||
RuntimeAssert(adapter->vtableIndex == -1, "");
|
||||
|
||||
if (adapter->itableIndex != -1 && superITable != nullptr)
|
||||
addToITable(adapter->interfaceId, adapter->itableIndex, adapter->kotlinImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider forbidding the class being abstract.
|
||||
|
||||
const TypeInfo* result = createTypeInfo(superType, addedInterfaces, vtable, methodTable);
|
||||
const TypeInfo* result = createTypeInfo(superType, addedInterfaces, vtable, methodTable, interfaceVTables, itableEqualsSuper);
|
||||
|
||||
// TODO: it will probably never be requested, since such a class can't be instantiated in Kotlin.
|
||||
result->writableInfo_->objCExport.objCClass = clazz;
|
||||
|
||||
@@ -56,4 +56,24 @@ void* LookupOpenMethod(const TypeInfo* info, MethodNameHash nameSignature) {
|
||||
|
||||
#endif
|
||||
|
||||
// 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.
|
||||
InterfaceTableRecord const* LookupInterfaceTableRecord(InterfaceTableRecord const* interfaceTable,
|
||||
int interfaceTableSize, ClassId interfaceId) {
|
||||
if (interfaceTableSize <= 8) {
|
||||
// Linear search.
|
||||
int i;
|
||||
for (i = 0; i < interfaceTableSize - 1 && interfaceTable[i].id < interfaceId; ++i);
|
||||
return interfaceTable + i;
|
||||
}
|
||||
int l = 0, r = interfaceTableSize - 1;
|
||||
while (l < r) {
|
||||
int m = (l + r) / 2;
|
||||
if (interfaceTable[m].id < interfaceId)
|
||||
l = m + 1;
|
||||
else r = m;
|
||||
}
|
||||
return interfaceTable + l;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,6 +79,18 @@ struct ExtendedTypeInfo {
|
||||
// TODO: do we want any other info here?
|
||||
};
|
||||
|
||||
typedef void const* VTableElement;
|
||||
|
||||
typedef int32_t ClassId;
|
||||
|
||||
const ClassId kInvalidInterfaceId = 0;
|
||||
|
||||
struct InterfaceTableRecord {
|
||||
ClassId id;
|
||||
uint32_t vtableSize;
|
||||
VTableElement const* vtable;
|
||||
};
|
||||
|
||||
// This struct represents runtime type information and by itself is the compile time
|
||||
// constant.
|
||||
struct TypeInfo {
|
||||
@@ -102,6 +114,8 @@ struct TypeInfo {
|
||||
// Null for abstract classes and interfaces.
|
||||
const MethodTableRecord* openMethods_;
|
||||
uint32_t openMethodsCount_;
|
||||
int32_t interfaceTableSize_;
|
||||
InterfaceTableRecord const* interfaceTable_;
|
||||
|
||||
// String for the fully qualified dot-separated name of the package containing class,
|
||||
// or `null` if the class is local or anonymous.
|
||||
@@ -116,7 +130,7 @@ struct TypeInfo {
|
||||
int32_t flags_;
|
||||
|
||||
// Class id built with the whole class hierarchy taken into account. The details are in ClassLayoutBuilder.
|
||||
int32_t classId_;
|
||||
ClassId classId_;
|
||||
|
||||
#if KONAN_TYPE_INFO_HAS_WRITABLE_PART
|
||||
WritableTypeInfo* writableInfo_;
|
||||
@@ -128,12 +142,12 @@ struct TypeInfo {
|
||||
// vtable starts just after declared contents of the TypeInfo:
|
||||
// void* const vtable_[];
|
||||
#ifdef __cplusplus
|
||||
inline const void* const * vtable() const {
|
||||
return reinterpret_cast<void * const *>(this + 1);
|
||||
inline VTableElement const* vtable() const {
|
||||
return reinterpret_cast<VTableElement const*>(this + 1);
|
||||
}
|
||||
|
||||
inline const void** vtable() {
|
||||
return reinterpret_cast<const void**>(this + 1);
|
||||
inline VTableElement* vtable() {
|
||||
return reinterpret_cast<VTableElement*>(this + 1);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
@@ -149,6 +163,9 @@ extern "C" {
|
||||
// (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,
|
||||
int interfaceTableSize, ClassId interfaceId) RUNTIME_CONST;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
@@ -70,8 +71,10 @@ template<class Value>
|
||||
using KStdUnorderedSet = std::unordered_set<Value,
|
||||
std::hash<Value>, std::equal_to<Value>,
|
||||
KonanAllocator<Value>>;
|
||||
template<class Value, class Compare>
|
||||
template<class Value, class Compare = std::less<Value>>
|
||||
using KStdOrderedSet = std::set<Value, Compare, KonanAllocator<Value>>;
|
||||
template<class Key, class Value, class Compare = std::less<Key>>
|
||||
using KStdOrderedMap = std::map<Key, Value, Compare, KonanAllocator<std::pair<const Key, Value>>>;
|
||||
template<class Value>
|
||||
using KStdVector = std::vector<Value, KonanAllocator<Value>>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user