diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt index ceb33cfd9fb..af0654bd0c4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.descriptors.konan.CurrentKlibModuleOrigin import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.util.OperatorNameConventions -internal fun ObjCExportCodeGenerator.generateBlockToKotlinFunctionConverter( +internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter( bridge: BlockPointerBridge ): LLVMValueRef { val irInterface = symbols.functionN(bridge.numberOfParameters).owner @@ -133,9 +133,7 @@ private val BlockPointerBridge.blockInvokeLlvmType: LLVMTypeRef private val BlockPointerBridge.nameSuffix: String get() = numberOfParameters.toString() + if (returnsVoid) "V" else "" -internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjCExportCodeGenerator) { - private val codegen get() = objCExportCodeGenerator.codegen - +internal class BlockAdapterToFunctionGenerator(private val codegen: CodeGenerator) { private val kRefSharedHolderType = LLVMGetTypeByName(codegen.runtime.llvmModule, "class.KRefSharedHolder")!! private val blockLiteralType = structType( @@ -220,7 +218,7 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC - private fun ObjCExportCodeGenerator.generateInvoke(bridge: BlockPointerBridge): ConstPointer { + private fun ObjCExportCodeGeneratorBase.generateInvoke(bridge: BlockPointerBridge): ConstPointer { val numberOfParameters = bridge.numberOfParameters val result = generateFunction(codegen, bridge.blockInvokeLlvmType, "invokeBlock${bridge.nameSuffix}") { @@ -255,7 +253,7 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC return constPointer(result) } - fun ObjCExportCodeGenerator.generateConvertFunctionToBlock(bridge: BlockPointerBridge): LLVMValueRef { + fun ObjCExportCodeGeneratorBase.generateConvertFunctionToBlock(bridge: BlockPointerBridge): LLVMValueRef { val blockDescriptor = codegen.staticData.placeGlobal( "", generateDescriptorForBlockAdapterToFunction(bridge) @@ -311,7 +309,7 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC } } -private val ObjCExportCodeGenerator.retainBlock get() = context.llvm.externalFunction( +private val ObjCExportCodeGeneratorBase.retainBlock get() = context.llvm.externalFunction( "objc_retainBlock", functionType(int8TypePtr, false, int8TypePtr), CurrentKlibModuleOrigin 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 b5bc498200c..36fd88a3476 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 @@ -34,14 +34,8 @@ internal fun TypeBridge.makeNothing() = when (this) { is ValueTypeBridge -> LLVMConstNull(this.objCValueType.llvmType)!! } -internal class ObjCExportCodeGenerator( - codegen: CodeGenerator, - val namer: ObjCExportNamer, - val mapper: ObjCExportMapper -) : ObjCCodeGenerator(codegen) { - +internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCodeGenerator(codegen) { val symbols get() = context.ir.symbols - val runtime get() = codegen.runtime val staticData get() = codegen.staticData @@ -57,10 +51,6 @@ 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 @@ -77,6 +67,55 @@ internal class ObjCExportCodeGenerator( return call(function, args, resultLifetime, ExceptionHandler.Caller) } + fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef) = + callFromBridge(context.llvm.Kotlin_ObjCExport_refToObjC, listOf(value)) + + fun FunctionGenerationContext.objCReferenceToKotlin(value: LLVMValueRef, resultLifetime: Lifetime) = + callFromBridge(context.llvm.Kotlin_ObjCExport_refFromObjC, listOf(value), resultLifetime) + + private val blockToKotlinFunctionConverterCache = mutableMapOf() + + internal fun blockToKotlinFunctionConverter(bridge: BlockPointerBridge): LLVMValueRef = + blockToKotlinFunctionConverterCache.getOrPut(bridge) { + generateBlockToKotlinFunctionConverter(bridge) + } + + private val blockAdapterToFunctionGenerator = BlockAdapterToFunctionGenerator(this.codegen) + private val functionToBlockConverterCache = mutableMapOf() + + internal fun kotlinFunctionToBlockConverter(bridge: BlockPointerBridge): LLVMValueRef = + functionToBlockConverterCache.getOrPut(bridge) { + blockAdapterToFunctionGenerator.run { + generateConvertFunctionToBlock(bridge) + } + } +} + +internal class ObjCExportBlockCodeGenerator(codegen: CodeGenerator) : ObjCExportCodeGeneratorBase(codegen) { + init { + // Must be generated along with stdlib: + // 1. Enumerates [BuiltInFictitiousFunctionIrClassFactory] built classes, which may be incomplete otherwise. + // 2. Modifies stdlib global initializers. + // 3. Defines runtime-declared globals. + require(context.producedLlvmModuleContainsStdlib) + } + + fun generate() { + emitFunctionConverters() + emitBlockToKotlinFunctionConverters() + } +} + +internal class ObjCExportCodeGenerator( + codegen: CodeGenerator, + val namer: ObjCExportNamer, + val mapper: ObjCExportMapper +) : ObjCExportCodeGeneratorBase(codegen) { + + val referencedSelectors = mutableMapOf() + + val externalGlobalInitializers = mutableMapOf() + fun FunctionGenerationContext.genSendMessage( returnType: LLVMTypeRef, receiver: LLVMValueRef, @@ -119,12 +158,6 @@ internal class ObjCExportCodeGenerator( ObjCValueType.FLOAT, ObjCValueType.DOUBLE, ObjCValueType.POINTER -> value } - fun FunctionGenerationContext.kotlinReferenceToObjC(value: LLVMValueRef) = - callFromBridge(context.llvm.Kotlin_ObjCExport_refToObjC, listOf(value)) - - fun FunctionGenerationContext.objCReferenceToKotlin(value: LLVMValueRef, resultLifetime: Lifetime) = - callFromBridge(context.llvm.Kotlin_ObjCExport_refFromObjC, listOf(value), resultLifetime) - private fun FunctionGenerationContext.objCBlockPointerToKotlin( value: LLVMValueRef, typeBridge: BlockPointerBridge, @@ -135,29 +168,11 @@ internal class ObjCExportCodeGenerator( resultLifetime ) - private val blockToKotlinFunctionConverterCache = mutableMapOf() - - internal fun blockToKotlinFunctionConverter(bridge: BlockPointerBridge): LLVMValueRef = - blockToKotlinFunctionConverterCache.getOrPut(bridge) { - generateBlockToKotlinFunctionConverter(bridge) - } - private fun FunctionGenerationContext.kotlinFunctionToObjCBlockPointer( typeBridge: BlockPointerBridge, value: LLVMValueRef ) = callFromBridge(kotlinFunctionToBlockConverter(typeBridge), listOf(value)) - private val blockAdapterToFunctionGenerator = BlockAdapterToFunctionGenerator(this) - - private val functionToBlockConverterCache = mutableMapOf() - - internal fun kotlinFunctionToBlockConverter(bridge: BlockPointerBridge): LLVMValueRef = - functionToBlockConverterCache.getOrPut(bridge) { - blockAdapterToFunctionGenerator.run { - generateConvertFunctionToBlock(bridge) - } - } - fun FunctionGenerationContext.kotlinToObjC( value: LLVMValueRef, typeBridge: TypeBridge @@ -459,6 +474,36 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo( objCClass: ConstPointer? = null, typeAdapter: ConstPointer? = null ) { + val writableTypeInfoValue = buildWritableTypeInfoValue( + converter = converter, + objCClass = objCClass, + typeAdapter = typeAdapter + ) + + if (codegen.isExternal(irClass)) { + // Note: this global replaces the external one with common linkage. + replaceExternalWeakOrCommonGlobal( + irClass.writableTypeInfoSymbolName, + writableTypeInfoValue, + irClass.llvmSymbolOrigin + ) + } else { + setOwnWritableTypeInfo(irClass, writableTypeInfoValue) + } +} + +private fun ObjCExportCodeGeneratorBase.setOwnWritableTypeInfo(irClass: IrClass, writableTypeInfoValue: Struct) { + require(!codegen.isExternal(irClass)) + val writeableTypeInfoGlobal = context.llvmDeclarations.forClass(irClass).writableTypeInfoGlobal!! + writeableTypeInfoGlobal.setLinkage(LLVMLinkage.LLVMExternalLinkage) + writeableTypeInfoGlobal.setInitializer(writableTypeInfoValue) +} + +private fun ObjCExportCodeGeneratorBase.buildWritableTypeInfoValue( + converter: ConstPointer? = null, + objCClass: ConstPointer? = null, + typeAdapter: ConstPointer? = null +): Struct { if (converter != null) { assert(converter.llvmType == pointerType(functionType(int8TypePtr, false, codegen.kObjHeaderPtr))) } @@ -470,26 +515,13 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo( ) val writableTypeInfoType = runtime.writableTypeInfoType!! - val writableTypeInfoValue = Struct(writableTypeInfoType, objCExportAddition) - - if (codegen.isExternal(irClass)) { - // Note: this global replaces the external one with common linkage. - replaceExternalWeakOrCommonGlobal( - irClass.writableTypeInfoSymbolName, - writableTypeInfoValue, - irClass.llvmSymbolOrigin - ) - } else { - context.llvmDeclarations.forClass(irClass).writableTypeInfoGlobal!!.also { - it.setLinkage(LLVMLinkage.LLVMExternalLinkage) - }.setInitializer(writableTypeInfoValue) - } + return Struct(writableTypeInfoType, objCExportAddition) } private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr) -private val ObjCExportCodeGenerator.objCToKotlinFunctionType: LLVMTypeRef +private val ObjCExportCodeGeneratorBase.objCToKotlinFunctionType: LLVMTypeRef get() = functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr) private fun ObjCExportCodeGenerator.emitBoxConverters() { @@ -534,14 +566,18 @@ private fun ObjCExportCodeGenerator.emitBoxConverter( setObjCExportTypeInfo(boxClass, constPointer(converter)) } -private fun ObjCExportCodeGenerator.emitFunctionConverters() { +private fun ObjCExportBlockCodeGenerator.emitFunctionConverters() { + require(context.producedLlvmModuleContainsStdlib) context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.forEach { functionClass -> val converter = kotlinFunctionToBlockConverter(BlockPointerBridge(functionClass.arity, returnsVoid = false)) - setObjCExportTypeInfo(functionClass.irClass, constPointer(converter)) + + val writableTypeInfoValue = buildWritableTypeInfoValue(converter = constPointer(converter)) + setOwnWritableTypeInfo(functionClass.irClass, writableTypeInfoValue) } } -private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() { +private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() { + require(context.producedLlvmModuleContainsStdlib) val functionClassesByArity = context.ir.symbols.functionIrClassFactory.builtFunctionNClasses.associateBy { it.arity } @@ -563,17 +599,9 @@ private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() { converters ).pointer.getElementPtr(0) - // Note: replacing weak globals defined in runtime. - replaceExternalWeakOrCommonGlobal( - "Kotlin_ObjCExport_blockToFunctionConverters", - ptr, - context.standardLlvmSymbolsOrigin - ) - replaceExternalWeakOrCommonGlobal( - "Kotlin_ObjCExport_blockToFunctionConverters_size", - Int32(count), - context.standardLlvmSymbolsOrigin - ) + // Note: defining globals declared in runtime. + staticData.placeGlobal("Kotlin_ObjCExport_blockToFunctionConverters", ptr, isExported = true) + staticData.placeGlobal("Kotlin_ObjCExport_blockToFunctionConverters_size", Int32(count), isExported = true) } private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { @@ -585,10 +613,6 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { emitCollectionConverters() emitBoxConverters() - - emitFunctionConverters() - - emitBlockToKotlinFunctionConverters() } private fun ObjCExportCodeGenerator.emitCollectionConverters() { @@ -1470,4 +1494,4 @@ internal fun Context.is64BitLong(): Boolean = when (val target = this.config.tar is KonanTarget.ZEPHYR, KonanTarget.IOS_ARM32 -> false KonanTarget.WATCHOS_X64 -> error("Target $target is not supported.") -} \ No newline at end of file +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt index 25538e53cb2..b02cd9b408d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.getExportedDependencies import org.jetbrains.kotlin.backend.konan.llvm.CodeGenerator +import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportBlockCodeGenerator import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerator import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -75,6 +76,10 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { internal fun generate(codegen: CodeGenerator) { if (!target.family.isAppleFamily) return + if (context.producedLlvmModuleContainsStdlib) { + ObjCExportBlockCodeGenerator(codegen).generate() + } + if (!context.config.produce.isFinalBinary) return // TODO: emit RTTI to the same modules as classes belong to. val mapper = exportedInterface?.mapper ?: ObjCExportMapper() diff --git a/runtime/src/main/cpp/ObjCExport.mm b/runtime/src/main/cpp/ObjCExport.mm index c54905c8f01..bab6022f322 100644 --- a/runtime/src/main/cpp/ObjCExport.mm +++ b/runtime/src/main/cpp/ObjCExport.mm @@ -439,9 +439,9 @@ static const char* getBlockEncoding(id block) { reinterpret_cast(literal->descriptor)->signature; } -// Note: replaced by compiler in appropriate compilation modes. -__attribute__((weak)) convertReferenceFromObjC* Kotlin_ObjCExport_blockToFunctionConverters = nullptr; -__attribute__((weak)) int Kotlin_ObjCExport_blockToFunctionConverters_size = 0; +// Note: defined by compiler. +extern "C" convertReferenceFromObjC* Kotlin_ObjCExport_blockToFunctionConverters; +extern "C" int Kotlin_ObjCExport_blockToFunctionConverters_size; extern "C" id objc_retainBlock(id self);