diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt index 10b8551677b..a7cf2296ab4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt @@ -65,4 +65,11 @@ internal class CachedLibraries( Cache.Kind.DYNAMIC -> false } } + + val hasDynamicCaches = allCaches.values.any { + when (it.kind) { + Cache.Kind.STATIC -> false + Cache.Kind.DYNAMIC -> true + } + } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecification.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecification.kt index 22a05f89a94..1aaf71d5176 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecification.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecification.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.library.KotlinLibrary interface LlvmModuleSpecification { val isFinal: Boolean fun importsKotlinDeclarationsFromOtherObjectFiles(): Boolean + fun importsKotlinDeclarationsFromOtherSharedLibraries(): Boolean fun containsLibrary(library: KotlinLibrary): Boolean fun containsModule(module: ModuleDescriptor): Boolean fun containsModule(module: IrModuleFragment): Boolean diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt index 5b7fcd53384..76e7645947c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecificationImpl.kt @@ -24,6 +24,9 @@ internal class LlvmModuleSpecificationImpl( override fun importsKotlinDeclarationsFromOtherObjectFiles(): Boolean = cachedLibraries.hasStaticCaches // A bit conservative but still valid. + override fun importsKotlinDeclarationsFromOtherSharedLibraries(): Boolean = + cachedLibraries.hasDynamicCaches // A bit conservative but still valid. + override fun containsLibrary(library: KotlinLibrary): Boolean = if (producingCache) { library in librariesToCache } else { 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 d8b6071f29a..74603105621 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 @@ -12,12 +12,14 @@ import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.allParameters import org.jetbrains.kotlin.backend.konan.ir.isOverridable import org.jetbrains.kotlin.backend.konan.ir.isUnit +import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin 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.CompiledKlibModuleOrigin import org.jetbrains.kotlin.descriptors.konan.CurrentKlibModuleOrigin import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol @@ -60,6 +62,8 @@ internal class ObjCExportCodeGenerator( val referencedSelectors = mutableMapOf() + val externalGlobalInitializers = mutableMapOf() + // TODO: currently bridges don't have any custom `landingpad`s, // so it is correct to use [callAtFunctionScope] here. // However, exception handling probably should be refactored @@ -235,6 +239,14 @@ internal class ObjCExportCodeGenerator( objCTypeAdapters += createTypeAdapter(objCClassForAny, superClass = null) + emitTypeAdapters() + + emitSelectorsHolder() + + emitStaticInitializers() + } + + private fun emitTypeAdapters() { val placedClassAdapters = mutableMapOf() val placedInterfaceAdapters = mutableMapOf() @@ -251,7 +263,12 @@ internal class ObjCExportCodeGenerator( descriptorToAdapter[adapter.objCName] = typeAdapter if (irClass != null) { - setObjCExportTypeInfo(irClass, typeAdapter = typeAdapter) + if (!context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { + setObjCExportTypeInfo(irClass, typeAdapter = typeAdapter) + } else { + // Optimization: avoid generating huge initializers; + // handled with "Kotlin_ObjCExport_initTypeAdapters" below. + } } } @@ -265,14 +282,37 @@ internal class ObjCExportCodeGenerator( val sortedAdaptersPointer = staticData.placeGlobalConstArray("", type, sortedAdapters) // Note: this globals replace runtime globals with weak linkage: - replaceExternalWeakOrCommonGlobal(prefix, sortedAdaptersPointer) - replaceExternalWeakOrCommonGlobal("${prefix}Num", Int32(sortedAdapters.size)) + val origin = context.standardLlvmSymbolsOrigin + replaceExternalWeakOrCommonGlobal(prefix, sortedAdaptersPointer, origin) + replaceExternalWeakOrCommonGlobal("${prefix}Num", Int32(sortedAdapters.size), origin) } } emitSortedAdapters(placedClassAdapters, "Kotlin_ObjCExport_sortedClassAdapters") emitSortedAdapters(placedInterfaceAdapters, "Kotlin_ObjCExport_sortedProtocolAdapters") - emitSelectorsHolder() + + if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { + replaceExternalWeakOrCommonGlobal( + "Kotlin_ObjCExport_initTypeAdapters", + Int1(1), + context.standardLlvmSymbolsOrigin + ) + } + } + + private fun emitStaticInitializers() { + if (externalGlobalInitializers.isEmpty()) return + + val initializer = generateFunction(codegen, functionType(voidType, false), "initObjCExportGlobals") { + externalGlobalInitializers.forEach { (global, value) -> + store(value.llvm, global) + } + ret(null) + } + + LLVMSetLinkage(initializer, LLVMLinkage.LLVMInternalLinkage) + + context.llvm.otherStaticInitializers += initializer } // TODO: consider including this into ObjCExportCodeSpec. @@ -390,13 +430,23 @@ internal class ObjCExportCodeGenerator( } -private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal(name: String, value: ConstValue) { - val global = staticData.placeGlobal(name, value, isExported = true) +private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal( + name: String, + value: ConstValue, + origin: CompiledKlibModuleOrigin +) { + if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { + val global = codegen.importGlobal(name, value.llvmType, origin) + externalGlobalInitializers[global] = value + } else { + context.llvmImports.add(origin) + val global = staticData.placeGlobal(name, value, isExported = true) - if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherObjectFiles()) { - // Note: actually this is required only if global's weak/common definition is in other object file, - // but it is simpler to do this for all globals, considering that all usages can't be removed by DCE anyway. - context.llvm.usedGlobals += global.llvmGlobal + if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherObjectFiles()) { + // Note: actually this is required only if global's weak/common definition is in another object file, + // but it is simpler to do this for all globals, considering that all usages can't be removed by DCE anyway. + context.llvm.usedGlobals += global.llvmGlobal + } } } @@ -421,7 +471,11 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo( if (codegen.isExternal(irClass)) { // Note: this global replaces the external one with common linkage. - replaceExternalWeakOrCommonGlobal(irClass.writableTypeInfoSymbolName, writableTypeInfoValue) + replaceExternalWeakOrCommonGlobal( + irClass.writableTypeInfoSymbolName, + writableTypeInfoValue, + irClass.llvmSymbolOrigin + ) } else { context.llvmDeclarations.forClass(irClass).writableTypeInfoGlobal!!.also { it.setLinkage(LLVMLinkage.LLVMExternalLinkage) @@ -493,7 +547,11 @@ private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() { ).pointer.getElementPtr(0) // Note: this global replaces the weak global defined in runtime. - replaceExternalWeakOrCommonGlobal("Kotlin_ObjCExport_blockToFunctionConverters", ptr) + replaceExternalWeakOrCommonGlobal( + "Kotlin_ObjCExport_blockToFunctionConverters", + ptr, + context.standardLlvmSymbolsOrigin + ) } private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { diff --git a/runtime/src/main/cpp/ObjCExport.mm b/runtime/src/main/cpp/ObjCExport.mm index 086eb55ec12..13283d8ee61 100644 --- a/runtime/src/main/cpp/ObjCExport.mm +++ b/runtime/src/main/cpp/ObjCExport.mm @@ -315,6 +315,8 @@ __attribute__((weak)) int Kotlin_ObjCExport_sortedClassAdaptersNum = 0; __attribute__((weak)) const ObjCTypeAdapter** Kotlin_ObjCExport_sortedProtocolAdapters = nullptr; __attribute__((weak)) int Kotlin_ObjCExport_sortedProtocolAdaptersNum = 0; +__attribute__((weak)) bool Kotlin_ObjCExport_initTypeAdapters = false; + static const ObjCTypeAdapter* findClassAdapter(Class clazz) { return findAdapterByName(class_getName(clazz), Kotlin_ObjCExport_sortedClassAdapters, @@ -416,7 +418,26 @@ static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd); static void checkLoadedOnce(); +static void initTypeAdaptersFrom(const ObjCTypeAdapter** adapters, int count) { + for (int index = 0; index < count; ++index) { + const ObjCTypeAdapter* adapter = adapters[index]; + const TypeInfo* typeInfo = adapter->kotlinTypeInfo; + if (typeInfo != nullptr) { + typeInfo->writableInfo_->objCExport.typeAdapter = adapter; + } + } +} + +static void initTypeAdapters() { + if (!Kotlin_ObjCExport_initTypeAdapters) return; + + initTypeAdaptersFrom(Kotlin_ObjCExport_sortedClassAdapters, Kotlin_ObjCExport_sortedClassAdaptersNum); + initTypeAdaptersFrom(Kotlin_ObjCExport_sortedProtocolAdapters, Kotlin_ObjCExport_sortedProtocolAdaptersNum); +} + static void initializeObjCExport() { + initTypeAdapters(); + SEL toKotlinSelector = @selector(toKotlin:); Method toKotlinMethod = class_getClassMethod([NSObject class], toKotlinSelector); RuntimeAssert(toKotlinMethod != nullptr, "");