Skip to content

Commit

Permalink
Improve ObjC block dynamic conversion generation when using cache (Je…
Browse files Browse the repository at this point in the history
…tBrains#3507)

Generate conversions along with stdlib to guarantee all synthesized
function classes get covered.
  • Loading branch information
SvyatoslavScherbina authored Oct 30, 2019
1 parent 41f8d5f commit 091de1b
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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}") {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -459,19 +474,12 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo(
objCClass: ConstPointer? = null,
typeAdapter: ConstPointer? = null
) {
if (converter != null) {
assert(converter.llvmType == pointerType(functionType(int8TypePtr, false, codegen.kObjHeaderPtr)))
}

val objCExportAddition = Struct(runtime.typeInfoObjCExportAddition,
converter?.bitcast(int8TypePtr),
objCClass,
typeAdapter
val writableTypeInfoValue = buildWritableTypeInfoValue(
converter = converter,
objCClass = objCClass,
typeAdapter = typeAdapter
)

val writableTypeInfoType = runtime.writableTypeInfoType!!
val writableTypeInfoValue = Struct(writableTypeInfoType, objCExportAddition)

if (codegen.isExternal(irClass)) {
// Note: this global replaces the external one with common linkage.
replaceExternalWeakOrCommonGlobal(
Expand All @@ -480,16 +488,40 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo(
irClass.llvmSymbolOrigin
)
} else {
context.llvmDeclarations.forClass(irClass).writableTypeInfoGlobal!!.also {
it.setLinkage(LLVMLinkage.LLVMExternalLinkage)
}.setInitializer(writableTypeInfoValue)
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)))
}

val objCExportAddition = Struct(runtime.typeInfoObjCExportAddition,
converter?.bitcast(int8TypePtr),
objCClass,
typeAdapter
)

val writableTypeInfoType = runtime.writableTypeInfoType!!
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() {
Expand Down Expand Up @@ -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 }

Expand All @@ -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() {
Expand All @@ -585,10 +613,6 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
emitCollectionConverters()

emitBoxConverters()

emitFunctionConverters()

emitBlockToKotlinFunctionConverters()
}

private fun ObjCExportCodeGenerator.emitCollectionConverters() {
Expand Down Expand Up @@ -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.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
6 changes: 3 additions & 3 deletions runtime/src/main/cpp/ObjCExport.mm
Original file line number Diff line number Diff line change
Expand Up @@ -439,9 +439,9 @@ static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd) {
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);

Expand Down

0 comments on commit 091de1b

Please sign in to comment.