Improve ObjC block dynamic conversion generation when using cache (#3507)
Generate conversions along with stdlib to guarantee all synthesized function classes get covered.
This commit is contained in:
committed by
GitHub
parent
41f8d5fc07
commit
091de1b8bc
+5
-7
@@ -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
|
||||
|
||||
+93
-69
@@ -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<String, MethodBridge>()
|
||||
|
||||
val externalGlobalInitializers = mutableMapOf<LLVMValueRef, ConstValue>()
|
||||
|
||||
// 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<BlockPointerBridge, LLVMValueRef>()
|
||||
|
||||
internal fun blockToKotlinFunctionConverter(bridge: BlockPointerBridge): LLVMValueRef =
|
||||
blockToKotlinFunctionConverterCache.getOrPut(bridge) {
|
||||
generateBlockToKotlinFunctionConverter(bridge)
|
||||
}
|
||||
|
||||
private val blockAdapterToFunctionGenerator = BlockAdapterToFunctionGenerator(this.codegen)
|
||||
private val functionToBlockConverterCache = mutableMapOf<BlockPointerBridge, LLVMValueRef>()
|
||||
|
||||
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<String, MethodBridge>()
|
||||
|
||||
val externalGlobalInitializers = mutableMapOf<LLVMValueRef, ConstValue>()
|
||||
|
||||
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<BlockPointerBridge, LLVMValueRef>()
|
||||
|
||||
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<BlockPointerBridge, LLVMValueRef>()
|
||||
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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()
|
||||
|
||||
@@ -439,9 +439,9 @@ static const char* getBlockEncoding(id block) {
|
||||
reinterpret_cast<struct Block_descriptor_1_without_helpers*>(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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user