diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 20754f6c8cf..55f402afc0f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -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. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index 8febefa3080..39c6a683bfa 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -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, val forbidden: List>) { + + 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() + val interfaces = mutableListOf() + val forbidden = mutableListOf>() + 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 { + 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 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 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() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 487335823ec..e5ca9c45569 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -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? { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 52a7776e788..f14f6d3ed98 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -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") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index b67541b162d..67d3afef39e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1391,13 +1391,23 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map) : 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)!! diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index 9bf627b91ad..46da5682160 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -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 { + 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(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), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt index 7b5a778f4cc..a666fd2cc92 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt @@ -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") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 23cfedcb039..02277967480 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -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, + itableSize: Int, val objCName: String, directAdapters: List, classAdapters: List, @@ -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(null) val presentMethodTableBridges = mutableSetOf() + val presentItableBridges = mutableSetOf(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 diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt index cfc5f0671ae..9bbec52279f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InitializersLowering.kt @@ -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() } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt index 127ff702edf..76ff452cced 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt @@ -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 } diff --git a/runtime/src/main/cpp/ObjCExport.mm b/runtime/src/main/cpp/ObjCExport.mm index 2bcb0eac365..086eb55ec12 100644 --- a/runtime/src/main/cpp/ObjCExport.mm +++ b/runtime/src/main/cpp/ObjCExport.mm @@ -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>& 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(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(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& superInterfaces, - const KStdVector& vtable, - const KStdVector& methodTable + const KStdVector& vtable, + const KStdVector& methodTable, + const KStdOrderedMap>& 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(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> 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 interfaceVTable(vtableSize); + for (int j = 0; j < vtableSize; ++j) + interfaceVTable[j] = record.vtable[j]; + interfaceVTables.emplace(interfaceId, std::move(interfaceVTable)); + } + } + KStdVector addedInterfaces = getProtocolsAsInterfaces(clazz); KStdVector 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(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(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(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; diff --git a/runtime/src/main/cpp/TypeInfo.cpp b/runtime/src/main/cpp/TypeInfo.cpp index 60262ea6072..3e226a0add6 100644 --- a/runtime/src/main/cpp/TypeInfo.cpp +++ b/runtime/src/main/cpp/TypeInfo.cpp @@ -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; +} + } diff --git a/runtime/src/main/cpp/TypeInfo.h b/runtime/src/main/cpp/TypeInfo.h index 437f2faadb7..866349a0224 100644 --- a/runtime/src/main/cpp/TypeInfo.h +++ b/runtime/src/main/cpp/TypeInfo.h @@ -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(this + 1); + inline VTableElement const* vtable() const { + return reinterpret_cast(this + 1); } - inline const void** vtable() { - return reinterpret_cast(this + 1); + inline VTableElement* vtable() { + return reinterpret_cast(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 diff --git a/runtime/src/main/cpp/Types.h b/runtime/src/main/cpp/Types.h index 5ced20ab1cc..8aa17aed174 100644 --- a/runtime/src/main/cpp/Types.h +++ b/runtime/src/main/cpp/Types.h @@ -25,6 +25,7 @@ #endif #include +#include #include #include #include @@ -70,8 +71,10 @@ template using KStdUnorderedSet = std::unordered_set, std::equal_to, KonanAllocator>; -template +template> using KStdOrderedSet = std::set>; +template> +using KStdOrderedMap = std::map>>; template using KStdVector = std::vector>;