Skip to content
This repository has been archived by the owner on Aug 10, 2021. It is now read-only.

Map returned Unit to void in more cases when producing framework #2968

Merged
merged 2 commits into from
May 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab

val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)

val interopForeignObjCObject = interopClass("ForeignObjCObject")

// These are possible supertypes of forward declarations - we need to reference them explicitly to force their deserialization.
// TODO: Do it lazily.
val interopCOpaque = symbolTable.referenceClass(context.interopBuiltIns.cOpaque)
Expand Down Expand Up @@ -430,6 +432,11 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
.single()
)

private fun interopClass(name: String) = symbolTable.referenceClass(
context.interopBuiltIns.packageScope
.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BACKEND) as ClassDescriptor
)

val functions = (0 .. KONAN_FUNCTION_INTERFACES_MAX_PARAMETERS)
.map { symbolTable.referenceClass(builtIns.getFunction(it)) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val Kotlin_ObjCExport_AbstractMethodCalled by lazyRtFunction
val Kotlin_ObjCExport_RethrowExceptionAsNSError by lazyRtFunction
val Kotlin_ObjCExport_RethrowNSErrorAsException by lazyRtFunction
val Kotlin_ObjCExport_AllocInstanceWithAssociatedObject by lazyRtFunction

val tlsMode by lazy {
when (target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,16 +188,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
pointerType(runtime.typeInfoType), interfaces)

val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type ->
if (isObjectType(type)) {
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
} else {
null
}
}
val objOffsets = getObjOffsets(bodyType)

val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type,
objOffsets.map { Int32(it.toInt()) })
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type, objOffsets)

val objOffsetsCount = if (irClass.descriptor == context.builtIns.array) {
1 // To mark it as non-leaf.
Expand Down Expand Up @@ -242,6 +235,15 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
exportTypeInfoIfRequired(irClass, irClass.llvmTypeInfoPtr)
}

private fun getObjOffsets(bodyType: LLVMTypeRef): List<Int32> =
getStructElements(bodyType).mapIndexedNotNull { index, type ->
if (isObjectType(type)) {
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
} else {
null
}
}.map { Int32(it.toInt()) }

fun vtable(irClass: IrClass): ConstArray {
// TODO: compile-time resolution limits binary compatibility.
val vtableEntries = context.getVtableBuilder(irClass).vtableEntries.map {
Expand Down Expand Up @@ -314,11 +316,12 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fun generateSyntheticInterfaceImpl(
irClass: IrClass,
methodImpls: Map<IrFunction, ConstPointer>,
bodyType: LLVMTypeRef,
immutable: Boolean = false
): ConstPointer {
assert(irClass.isInterface)

val size = LLVMStoreSizeOfType(llvmTargetData, kObjHeader).toInt()
val size = LLVMStoreSizeOfType(llvmTargetData, bodyType).toInt()

val superClass = context.ir.symbols.any.owner

Expand All @@ -328,8 +331,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
pointerType(runtime.typeInfoType), interfaces)

assert(superClass.declarations.all { it !is IrProperty && it !is IrField })
val objOffsetsPtr = NullPointer(int32Type)
val objOffsetsCount = 0

val objOffsets = getObjOffsets(bodyType)
val objOffsetsPtr = staticData.placeGlobalConstArray("", int32Type, objOffsets)
val objOffsetsCount = objOffsets.size

val methods = (methodTableRecords(superClass) + methodImpls.map { (method, impl) ->
assert(method.parent == irClass)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,133 @@

package org.jetbrains.kotlin.backend.konan.llvm.objcexport

import llvm.LLVMLinkage
import llvm.LLVMSetLinkage
import llvm.LLVMStoreSizeOfType
import llvm.LLVMValueRef
import llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.objcexport.BlockPointerBridge
import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.util.OperatorNameConventions

internal fun ObjCExportCodeGenerator.generateKotlinFunctionImpl(invokeMethod: IrSimpleFunction): ConstPointer {
// TODO: consider also overriding methods of `Any`.

val numberOfParameters = invokeMethod.valueParameters.size
internal fun ObjCExportCodeGenerator.generateBlockToKotlinFunctionConverter(
bridge: BlockPointerBridge
): LLVMValueRef {
val irInterface = symbols.functions[bridge.numberOfParameters].owner
val invokeMethod = irInterface.declarations.filterIsInstance<IrSimpleFunction>()
.single { it.name == OperatorNameConventions.INVOKE }

// Note: we can store Objective-C block pointer as associated object of Kotlin function object itself,
// but only if it is equivalent to its dynamic translation result. If block returns void, then it's not like that:
val useSeparateHolder = bridge.returnsVoid

val bodyType = if (useSeparateHolder) {
structType(codegen.kObjHeader, codegen.kObjHeaderPtr)
} else {
structType(codegen.kObjHeader)
}

val function = generateFunction(
val invokeImpl = generateFunction(
codegen,
codegen.getLlvmFunctionType(invokeMethod),
"invokeFunction$numberOfParameters"
"invokeFunction${bridge.nameSuffix}"
) {
val args = (0 until numberOfParameters).map { index -> kotlinReferenceToObjC(param(index + 1)) }
val args = (0 until bridge.numberOfParameters).map { index ->
kotlinReferenceToObjC(param(index + 1))
}

val rawBlockPtr = callFromBridge(context.llvm.Kotlin_ObjCExport_GetAssociatedObject, listOf(param(0)))
val thisRef = param(0)
val associatedObjectHolder = if (useSeparateHolder) {
val bodyPtr = bitcast(pointerType(bodyType), thisRef)
loadSlot(structGep(bodyPtr, 1), isVar = false)
} else {
thisRef
}
val blockPtr = callFromBridge(
context.llvm.Kotlin_ObjCExport_GetAssociatedObject,
listOf(associatedObjectHolder)
)

val blockLiteralType = codegen.runtime.getStructType("Block_literal_1")
val blockPtr = bitcast(pointerType(blockLiteralType), rawBlockPtr)
val invokePtr = structGep(blockPtr, 3)
val invoke = loadBlockInvoke(blockPtr, bridge)
val result = callFromBridge(invoke, listOf(blockPtr) + args)

val blockInvokeType = functionType(int8TypePtr, false, (0 .. numberOfParameters).map { int8TypePtr })
val kotlinResult = if (bridge.returnsVoid) {
theUnitInstanceRef.llvm
} else {
objCReferenceToKotlin(result, Lifetime.RETURN_VALUE)
}
ret(kotlinResult)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
}

val invoke = bitcast(pointerType(blockInvokeType), load(invokePtr))
val result = callFromBridge(invoke, listOf(rawBlockPtr) + args)
val typeInfo = rttiGenerator.generateSyntheticInterfaceImpl(
irInterface,
mapOf(invokeMethod to constPointer(invokeImpl)),
bodyType,
immutable = true
)

return generateFunction(
codegen,
functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr),
""
) {
val blockPtr = param(0)
ifThen(icmpEq(blockPtr, kNullInt8Ptr)) {
ret(kNullObjHeaderPtr)
}

val retainedBlockPtr = callFromBridge(retainBlock, listOf(blockPtr))

// TODO: support void-as-Unit.
ret(objCReferenceToKotlin(result, Lifetime.RETURN_VALUE))
val result = if (useSeparateHolder) {
val result = allocInstance(typeInfo.llvm, Lifetime.RETURN_VALUE)
val bodyPtr = bitcast(pointerType(bodyType), result)
val holder = allocInstanceWithAssociatedObject(
symbols.interopForeignObjCObject.owner.typeInfoPtr,
retainedBlockPtr,
Lifetime.ARGUMENT
)
storeHeapRef(holder, structGep(bodyPtr, 1))
result
} else {
allocInstanceWithAssociatedObject(typeInfo, retainedBlockPtr, Lifetime.RETURN_VALUE)
}

ret(result)
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
}
}

return constPointer(function)
private fun FunctionGenerationContext.loadBlockInvoke(
blockPtr: LLVMValueRef,
bridge: BlockPointerBridge
): LLVMValueRef {
val blockLiteralType = codegen.runtime.getStructType("Block_literal_1")
val invokePtr = structGep(bitcast(pointerType(blockLiteralType), blockPtr), 3)

return bitcast(pointerType(bridge.blockInvokeLlvmType), load(invokePtr))
}

private fun FunctionGenerationContext.allocInstanceWithAssociatedObject(
typeInfo: ConstPointer,
associatedObject: LLVMValueRef,
resultLifetime: Lifetime
): LLVMValueRef = call(
context.llvm.Kotlin_ObjCExport_AllocInstanceWithAssociatedObject,
listOf(typeInfo.llvm, associatedObject),
resultLifetime
)

private val BlockPointerBridge.blockInvokeLlvmType: LLVMTypeRef
get() = functionType(
if (returnsVoid) voidType else int8TypePtr,
false,
(0..numberOfParameters).map { int8TypePtr }
)

private val BlockPointerBridge.nameSuffix: String
get() = numberOfParameters.toString() + if (returnsVoid) "V" else ""

internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjCExportCodeGenerator) {
private val codegen get() = objCExportCodeGenerator.codegen

Expand Down Expand Up @@ -94,9 +180,11 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
fun org.jetbrains.kotlin.backend.konan.Context.LongInt(value: Long) =
if (is64Bit()) Int64(value) else Int32(value.toInt())

private fun generateDescriptorForBlockAdapterToFunction(numberOfParameters: Int): ConstValue {
private fun generateDescriptorForBlockAdapterToFunction(bridge: BlockPointerBridge): ConstValue {
val numberOfParameters = bridge.numberOfParameters

val signature = buildString {
append('@')
append(if (bridge.returnsVoid) 'v' else '@')
val pointerSize = codegen.runtime.pointerSize
append(pointerSize * (numberOfParameters + 1))

Expand Down Expand Up @@ -132,14 +220,10 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
)
}

private fun ObjCExportCodeGenerator.generateInvoke(numberOfParameters: Int): ConstPointer {
val functionType = functionType(
int8TypePtr,
false,
(0 .. numberOfParameters).map { int8TypePtr }
)
private fun ObjCExportCodeGenerator.generateInvoke(bridge: BlockPointerBridge): ConstPointer {
val numberOfParameters = bridge.numberOfParameters

val result = generateFunction(codegen, functionType, "invokeBlock$numberOfParameters") {
val result = generateFunction(codegen, bridge.blockInvokeLlvmType, "invokeBlock${bridge.nameSuffix}") {
val blockPtr = bitcast(pointerType(blockLiteralType), param(0))
val kotlinFunction = loadSlot(structGep(blockPtr, 1), isVar = false)

Expand All @@ -154,25 +238,34 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC

val result = callFromBridge(callee, listOf(kotlinFunction) + args, Lifetime.ARGUMENT)

ret(kotlinReferenceToObjC(result))
if (bridge.returnsVoid) {
ret(null)
} else {
ret(kotlinReferenceToObjC(result))
}
}.also {
LLVMSetLinkage(it, LLVMLinkage.LLVMInternalLinkage)
}

return constPointer(result)
}

fun ObjCExportCodeGenerator.generateConvertFunctionToBlock(numberOfParameters: Int): LLVMValueRef {
fun ObjCExportCodeGenerator.generateConvertFunctionToBlock(bridge: BlockPointerBridge): LLVMValueRef {
val blockDescriptor = codegen.staticData.placeGlobal(
"",
generateDescriptorForBlockAdapterToFunction(numberOfParameters)
generateDescriptorForBlockAdapterToFunction(bridge)
)

return generateFunction(
codegen,
functionType(int8TypePtr, false, codegen.kObjHeaderPtr),
"convertFunction$numberOfParameters"
"convertFunction${bridge.nameSuffix}"
) {
val kotlinRef = param(0)
ifThen(icmpEq(kotlinRef, kNullObjHeaderPtr)) {
ret(kNullInt8Ptr)
}

val isa = codegen.importGlobal(
"_NSConcreteStackBlock",
int8TypePtr,
Expand All @@ -183,7 +276,7 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
val reserved = Int32(0).llvm

val invokeType = pointerType(functionType(voidType, true, int8TypePtr))
val invoke = generateInvoke(numberOfParameters).bitcast(invokeType).llvm
val invoke = generateInvoke(bridge).bitcast(invokeType).llvm
val descriptor = blockDescriptor.llvmGlobal

val blockOnStack = alloca(blockLiteralType)
Expand All @@ -197,13 +290,7 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
}

// Note: it is the slot in the block located on stack, so no need to manage it properly:
storeRefUnsafe(param(0), slot)

val retainBlock = context.llvm.externalFunction(
"objc_retainBlock",
functionType(int8TypePtr, false, int8TypePtr),
CurrentKonanModuleOrigin
)
storeRefUnsafe(kotlinRef, slot)

val copiedBlock = callFromBridge(retainBlock, listOf(bitcast(int8TypePtr, blockOnStack)))

Expand All @@ -219,3 +306,9 @@ internal class BlockAdapterToFunctionGenerator(val objCExportCodeGenerator: ObjC
}
}
}

private val ObjCExportCodeGenerator.retainBlock get() = context.llvm.externalFunction(
"objc_retainBlock",
functionType(int8TypePtr, false, int8TypePtr),
CurrentKonanModuleOrigin
)
Loading