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 af0654bd0c4..0ca13e435dc 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 @@ -110,7 +110,7 @@ private fun FunctionGenerationContext.loadBlockInvoke( val blockLiteralType = codegen.runtime.getStructType("Block_literal_1") val invokePtr = structGep(bitcast(pointerType(blockLiteralType), blockPtr), 3) - return bitcast(pointerType(bridge.blockInvokeLlvmType), load(invokePtr)) + return bitcast(pointerType(bridge.blockType.blockInvokeLlvmType), load(invokePtr)) } private fun FunctionGenerationContext.allocInstanceWithAssociatedObject( @@ -123,7 +123,15 @@ private fun FunctionGenerationContext.allocInstanceWithAssociatedObject( resultLifetime ) -private val BlockPointerBridge.blockInvokeLlvmType: LLVMTypeRef +private val BlockPointerBridge.blockType: BlockType + get() = BlockType(numberOfParameters = this.numberOfParameters, returnsVoid = this.returnsVoid) + +/** + * Type of block having [numberOfParameters] reference-typed parameters and reference- or void-typed return value. + */ +internal data class BlockType(val numberOfParameters: Int, val returnsVoid: Boolean) + +private val BlockType.blockInvokeLlvmType: LLVMTypeRef get() = functionType( if (returnsVoid) voidType else int8TypePtr, false, @@ -133,7 +141,7 @@ private val BlockPointerBridge.blockInvokeLlvmType: LLVMTypeRef private val BlockPointerBridge.nameSuffix: String get() = numberOfParameters.toString() + if (returnsVoid) "V" else "" -internal class BlockAdapterToFunctionGenerator(private val codegen: CodeGenerator) { +internal class BlockGenerator(private val codegen: CodeGenerator) { private val kRefSharedHolderType = LLVMGetTypeByName(codegen.runtime.llvmModule, "class.KRefSharedHolder")!! private val blockLiteralType = structType( @@ -188,11 +196,11 @@ internal class BlockAdapterToFunctionGenerator(private val codegen: CodeGenerato fun org.jetbrains.kotlin.backend.konan.Context.LongInt(value: Long) = if (is64BitLong()) Int64(value) else Int32(value.toInt()) - private fun generateDescriptorForBlockAdapterToFunction(bridge: BlockPointerBridge): ConstValue { - val numberOfParameters = bridge.numberOfParameters + private fun generateDescriptorForBlock(blockType: BlockType): ConstValue { + val numberOfParameters = blockType.numberOfParameters val signature = buildString { - append(if (bridge.returnsVoid) 'v' else '@') + append(if (blockType.returnsVoid) 'v' else '@') val pointerSize = codegen.runtime.pointerSize append(pointerSize * (numberOfParameters + 1)) @@ -217,35 +225,23 @@ internal class BlockAdapterToFunctionGenerator(private val codegen: CodeGenerato } - - private fun ObjCExportCodeGeneratorBase.generateInvoke(bridge: BlockPointerBridge): ConstPointer { - val numberOfParameters = bridge.numberOfParameters - - val result = generateFunction(codegen, bridge.blockInvokeLlvmType, "invokeBlock${bridge.nameSuffix}") { + private fun ObjCExportCodeGeneratorBase.generateInvoke( + blockType: BlockType, + invokeName: String, + genBody: FunctionGenerationContext.(LLVMValueRef, List) -> Unit + ): ConstPointer { + val result = generateFunction(codegen, blockType.blockInvokeLlvmType, invokeName) { val blockPtr = bitcast(pointerType(blockLiteralType), param(0)) - val kotlinFunction = call( + val kotlinObject = call( context.llvm.kRefSharedHolderRef, listOf(structGep(blockPtr, 1)), exceptionHandler = ExceptionHandler.Caller, verbatim = true ) - val args = (1 .. numberOfParameters).map { index -> - objCReferenceToKotlin(param(index), Lifetime.ARGUMENT) - } + val arguments = (1 .. blockType.numberOfParameters).map { index -> param(index) } - val invokeMethod = context.ir.symbols.functionN(numberOfParameters).owner.simpleFunctions() - .single { it.name == OperatorNameConventions.INVOKE } - - val callee = lookupVirtualImpl(kotlinFunction, invokeMethod) - - val result = callFromBridge(callee, listOf(kotlinFunction) + args, Lifetime.ARGUMENT) - - if (bridge.returnsVoid) { - ret(null) - } else { - ret(kotlinReferenceToObjC(result)) - } + genBody(kotlinObject, arguments) }.also { LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage) } @@ -253,16 +249,48 @@ internal class BlockAdapterToFunctionGenerator(private val codegen: CodeGenerato return constPointer(result) } - fun ObjCExportCodeGeneratorBase.generateConvertFunctionToBlock(bridge: BlockPointerBridge): LLVMValueRef { + fun ObjCExportCodeGeneratorBase.generateConvertFunctionToBlock( + bridge: BlockPointerBridge + ): LLVMValueRef { + return generateWrapKotlinObjectToBlock( + bridge.blockType, + convertName = "convertFunction${bridge.nameSuffix}", + invokeName = "invokeBlock${bridge.nameSuffix}" + ) { kotlinFunction, arguments -> + val numberOfParameters = bridge.numberOfParameters + + val kotlinArguments = arguments.map { objCReferenceToKotlin(it, Lifetime.ARGUMENT) } + + val invokeMethod = context.ir.symbols.functionN(numberOfParameters).owner.simpleFunctions() + .single { it.name == OperatorNameConventions.INVOKE } + + val callee = lookupVirtualImpl(kotlinFunction, invokeMethod) + + val result = callFromBridge(callee, listOf(kotlinFunction) + kotlinArguments, Lifetime.ARGUMENT) + + if (bridge.returnsVoid) { + ret(null) + } else { + ret(kotlinReferenceToObjC(result)) + } + } + } + + internal fun ObjCExportCodeGeneratorBase.generateWrapKotlinObjectToBlock( + blockType: BlockType, + convertName: String, + invokeName: String, + genBlockBody: FunctionGenerationContext.(LLVMValueRef, List) -> Unit + ): LLVMValueRef { val blockDescriptor = codegen.staticData.placeGlobal( "", - generateDescriptorForBlockAdapterToFunction(bridge) + generateDescriptorForBlock(blockType) ) return generateFunction( codegen, functionType(int8TypePtr, false, codegen.kObjHeaderPtr), - "convertFunction${bridge.nameSuffix}" + convertName ) { val kotlinRef = param(0) ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) { @@ -279,7 +307,7 @@ internal class BlockAdapterToFunctionGenerator(private val codegen: CodeGenerato val reserved = Int32(0).llvm val invokeType = pointerType(functionType(voidType, true, int8TypePtr)) - val invoke = generateInvoke(bridge).bitcast(invokeType).llvm + val invoke = generateInvoke(blockType, invokeName, genBlockBody).bitcast(invokeType).llvm val descriptor = blockDescriptor.llvmGlobal val blockOnStack = alloca(blockLiteralType) 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 fdbe67b02ad..d7070acab8d 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 @@ -86,12 +86,12 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo generateBlockToKotlinFunctionConverter(bridge) } - private val blockAdapterToFunctionGenerator = BlockAdapterToFunctionGenerator(this.codegen) + private val blockGenerator = BlockGenerator(this.codegen) private val functionToBlockConverterCache = mutableMapOf() internal fun kotlinFunctionToBlockConverter(bridge: BlockPointerBridge): LLVMValueRef = functionToBlockConverterCache.getOrPut(bridge) { - blockAdapterToFunctionGenerator.run { + blockGenerator.run { generateConvertFunctionToBlock(bridge) } }