[K/N] LLVM attributes at call-sites

(I messed up commit history and all changes had to be squashed. Sorry.)

The commit is originated from KT-48591. The root of the problem is
following: we unconditionally added sext to all value parameters of
imported functions. To fix this problem we have to pass zext/sext
depending on the parameter type.

To make things right and future-proof, we decided to refactor
a significant part of IR to bitcode translator: when generating callsite
compiler "looks" at callee and applies its attributes to the callsite.
There are several sources of attributes:
1. External LLVM functions from runtime: just copy-paste attributes.
2. Direct IR calls: infer attributes from the declaration.
3. Virtual calls: use available declaration, so almost the same as prev.
4. External declarations functions: manual labor!

Specifying attributes at all callsites (including Kotlin->Kotlin) makes
things uniform and a little easier to implement.
All in all, the delta is significant, but comprehensible. In some places
I managed to make it seamless by changing declaration type and its
usages types simultaneously. In others (ObjCExport), unfortunately,
things got a little messy.

#KT-48722 fixed
This commit is contained in:
Sergey Bogolepov
2021-09-08 14:03:34 +07:00
committed by Space
parent a13329fc65
commit cf3296c94c
21 changed files with 702 additions and 335 deletions
@@ -212,21 +212,22 @@ private class ExportedElement(val kind: ElementKind,
val function = declaration as FunctionDescriptor val function = declaration as FunctionDescriptor
val irFunction = irSymbol.owner as IrFunction val irFunction = irSymbol.owner as IrFunction
cname = "_konan_function_${owner.nextFunctionIndex()}" cname = "_konan_function_${owner.nextFunctionIndex()}"
val llvmFunction = owner.codegen.llvmFunction(irFunction) val llvmCallable = owner.codegen.llvmFunction(irFunction)
// If function is virtual, we need to resolve receiver properly. // If function is virtual, we need to resolve receiver properly.
val bridge = if (!DescriptorUtils.isTopLevelDeclaration(function) && val bridge = if (!DescriptorUtils.isTopLevelDeclaration(function) &&
irFunction.isOverridable) { irFunction.isOverridable) {
// We need LLVMGetElementType() as otherwise type is function pointer. generateFunction(owner.codegen, llvmCallable.functionType, cname) {
generateFunction(owner.codegen, LLVMGetElementType(llvmFunction.type)!!, cname) {
val receiver = param(0) val receiver = param(0)
val numParams = LLVMCountParams(llvmFunction) val numParams = LLVMCountParams(llvmCallable.llvmValue)
val args = (0 .. numParams - 1).map { index -> param(index) } val args = (0..numParams - 1).map { index -> param(index) }
val callee = lookupVirtualImpl(receiver, irFunction) val callee = lookupVirtualImpl(receiver, irFunction)
callee.attributeProvider.addFunctionAttributes(this.function)
val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true) val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true)
ret(result) ret(result)
} }
} else { } else {
LLVMAddAlias(context.llvmModule, llvmFunction.type, llvmFunction, cname)!! val aliasType = pointerType(llvmCallable.functionType)
LLVMAddAlias(context.llvmModule, aliasType, llvmCallable.llvmValue, cname)!!
} }
LLVMSetLinkage(bridge, LLVMLinkage.LLVMExternalLinkage) LLVMSetLinkage(bridge, LLVMLinkage.LLVMExternalLinkage)
} }
@@ -38,15 +38,30 @@ private class CallsChecker(val context: Context, goodFunctions: List<String>) {
return false return false
} }
private fun externalFunction(name: String, type: LLVMTypeRef) =
context.llvm.externalFunction(name, type, context.stdlibModule.llvmSymbolOrigin)
private fun moduleFunction(name: String) = private fun moduleFunction(name: String) =
LLVMGetNamedFunction(context.llvmModule, name) ?: throw IllegalStateException("$name function is not available") LLVMGetNamedFunction(context.llvmModule, name) ?: throw IllegalStateException("$name function is not available")
val getMethodImpl = externalFunction("class_getMethodImplementation", functionType(pointerType(functionType(voidType, false)), false, int8TypePtr, int8TypePtr)) val getMethodImpl = context.llvm.externalFunction(LlvmFunctionProto(
val getClass = externalFunction("object_getClass", functionType(int8TypePtr, false, int8TypePtr)) "class_getMethodImplementation",
val getSuperClass = externalFunction("class_getSuperclass", functionType(int8TypePtr, false, int8TypePtr)) LlvmRetType(pointerType(functionType(voidType, false))),
listOf(LlvmParamType(int8TypePtr), LlvmParamType(int8TypePtr)),
origin = context.stdlibModule.llvmSymbolOrigin)
)
val getClass = context.llvm.externalFunction(LlvmFunctionProto(
"object_getClass",
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
origin = context.stdlibModule.llvmSymbolOrigin)
)
val getSuperClass = context.llvm.externalFunction(LlvmFunctionProto(
"class_getSuperclass",
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
origin = context.stdlibModule.llvmSymbolOrigin)
)
val checkerFunction = moduleFunction("Kotlin_mm_checkStateAtExternalFunctionCall") val checkerFunction = moduleFunction("Kotlin_mm_checkStateAtExternalFunctionCall")
private data class ExternalCallInfo(val name: String?, val calledPtr: LLVMValueRef) private data class ExternalCallInfo(val name: String?, val calledPtr: LLVMValueRef)
@@ -102,10 +117,10 @@ private class CallsChecker(val context: Context, goodFunctions: List<String>) {
callSiteDescription = "$functionName (over objc_msgSend)" callSiteDescription = "$functionName (over objc_msgSend)"
calledName = null calledName = null
val firstArgI8Ptr = LLVMBuildBitCast(builder, LLVMGetArgOperand(call, 0), int8TypePtr, "") val firstArgI8Ptr = LLVMBuildBitCast(builder, LLVMGetArgOperand(call, 0), int8TypePtr, "")
val firstArgClassPtr = LLVMBuildCall(builder, getClass, listOf(firstArgI8Ptr).toCValues(), 1, "") val firstArgClassPtr = LLVMBuildCall(builder, getClass.llvmValue, listOf(firstArgI8Ptr).toCValues(), 1, "")
val isNil = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, firstArgI8Ptr, LLVMConstNull(int8TypePtr), "") val isNil = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, firstArgI8Ptr, LLVMConstNull(int8TypePtr), "")
val selector = LLVMGetArgOperand(call, 1) val selector = LLVMGetArgOperand(call, 1)
val calledPtrLlvmIfNotNilFunPtr = LLVMBuildCall(builder, getMethodImpl, listOf(firstArgClassPtr, selector).toCValues(), 2, "") val calledPtrLlvmIfNotNilFunPtr = LLVMBuildCall(builder, getMethodImpl.llvmValue, listOf(firstArgClassPtr, selector).toCValues(), 2, "")
val calledPtrLlvmIfNotNil = LLVMBuildBitCast(builder, calledPtrLlvmIfNotNilFunPtr, int8TypePtr, "") val calledPtrLlvmIfNotNil = LLVMBuildBitCast(builder, calledPtrLlvmIfNotNilFunPtr, int8TypePtr, "")
val calledPtrLlvmIfNil = LLVMConstIntToPtr(Int64(MSG_SEND_TO_NULL).llvm, int8TypePtr) val calledPtrLlvmIfNil = LLVMConstIntToPtr(Int64(MSG_SEND_TO_NULL).llvm, int8TypePtr)
calledPtrLlvm = LLVMBuildSelect(builder, isNil, calledPtrLlvmIfNil, calledPtrLlvmIfNotNil, "") calledPtrLlvm = LLVMBuildSelect(builder, isNil, calledPtrLlvmIfNil, calledPtrLlvmIfNotNil, "")
@@ -117,8 +132,8 @@ private class CallsChecker(val context: Context, goodFunctions: List<String>) {
val superStruct = LLVMGetArgOperand(call, 0) val superStruct = LLVMGetArgOperand(call, 0)
val superClassPtrPtr = LLVMBuildGEP(builder, superStruct, listOf(Int32(0).llvm, Int32(1).llvm).toCValues(), 2, "") val superClassPtrPtr = LLVMBuildGEP(builder, superStruct, listOf(Int32(0).llvm, Int32(1).llvm).toCValues(), 2, "")
val superClassPtr = LLVMBuildLoad(builder, superClassPtrPtr, "") val superClassPtr = LLVMBuildLoad(builder, superClassPtrPtr, "")
val classPtr = LLVMBuildCall(builder, getSuperClass, listOf(superClassPtr).toCValues(), 1, "") val classPtr = LLVMBuildCall(builder, getSuperClass.llvmValue, listOf(superClassPtr).toCValues(), 1, "")
val calledPtrLlvmFunPtr = LLVMBuildCall(builder, getMethodImpl, listOf(classPtr, LLVMGetArgOperand(call, 1)).toCValues(), 2, "") val calledPtrLlvmFunPtr = LLVMBuildCall(builder, getMethodImpl.llvmValue, listOf(classPtr, LLVMGetArgOperand(call, 1)).toCValues(), 2, "")
calledPtrLlvm = LLVMBuildBitCast(builder, calledPtrLlvmFunPtr, int8TypePtr, "") calledPtrLlvm = LLVMBuildBitCast(builder, calledPtrLlvmFunPtr, int8TypePtr, "")
} }
else -> { else -> {
@@ -52,6 +52,12 @@ internal inline fun <R> KotlinType.unwrapToPrimitiveOrReference(
ifReference: (type: KotlinType) -> R ifReference: (type: KotlinType) -> R
): R = KotlinTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference) ): R = KotlinTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference)
internal inline fun <R> IrType.unwrapToPrimitiveOrReference(
eachInlinedClass: (inlinedClass: IrClass, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: IrType) -> R
): R = IrTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference)
// TODO: consider renaming to `isReference`. // TODO: consider renaming to `isReference`.
fun KotlinType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null fun KotlinType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
fun IrType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null fun IrType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
@@ -263,7 +269,7 @@ internal object KotlinTypeInlineClassesSupport : InlineClassesSupport<ClassDescr
override fun isTopLevelClass(clazz: ClassDescriptor): Boolean = clazz.containingDeclaration is PackageFragmentDescriptor override fun isTopLevelClass(clazz: ClassDescriptor): Boolean = clazz.containingDeclaration is PackageFragmentDescriptor
} }
private object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() { internal object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() {
override fun isNullable(type: IrType): Boolean = type.containsNull() override fun isNullable(type: IrType): Boolean = type.containsNull()
@@ -129,19 +129,11 @@ private fun String.replaceSpecialSymbols() =
fun IrDeclaration.isExported() = KonanBinaryInterface.isExported(this) fun IrDeclaration.isExported() = KonanBinaryInterface.isExported(this)
// TODO: bring here dependencies of this method? // TODO: bring here dependencies of this method?
internal fun RuntimeAware.getLlvmFunctionType(function: IrFunction): LLVMTypeRef { internal fun RuntimeAware.getLlvmFunctionType(function: IrFunction): LLVMTypeRef = functionType(
val returnType = when { returnType = getLlvmFunctionReturnType(function).llvmType,
function is IrConstructor -> voidType isVarArg = false,
function.isSuspend -> kObjHeaderPtr // Suspend functions return Any?. paramTypes = getLlvmFunctionParameterTypes(function).map { it.llvmType }
else -> getLLVMReturnType(function.returnType) )
}
val paramTypes = ArrayList(function.allParameters.map { getLLVMType(it.type) })
if (function.isSuspend)
paramTypes.add(kObjHeaderPtr) // Suspend functions have implicit parameter of type Continuation<>.
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
return functionType(returnType, isVarArg = false, paramTypes = paramTypes.toTypedArray())
}
internal val IrClass.typeInfoHasVtableAttached: Boolean internal val IrClass.typeInfoHasVtableAttached: Boolean
get() = !this.isAbstract() && !this.isExternalObjCClass() get() = !this.isAbstract() && !this.isExternalObjCClass()
@@ -48,9 +48,13 @@ internal fun IrClass.hasConstStateAndNoSideEffects(context: Context): Boolean {
} }
internal class CodeGenerator(override val context: Context) : ContextUtils { internal class CodeGenerator(override val context: Context) : ContextUtils {
fun llvmFunction(function: IrFunction): LlvmCallable =
llvmFunctionOrNull(function)
?: error("no function ${function.name} in ${function.file.fqName}")
fun llvmFunctionOrNull(function: IrFunction): LlvmCallable? =
function.llvmFunctionOrNull
fun llvmFunction(function: IrFunction): LLVMValueRef = llvmFunctionOrNull(function) ?: error("no function ${function.name} in ${function.file.fqName}")
fun llvmFunctionOrNull(function: IrFunction): LLVMValueRef? = function.llvmFunctionOrNull
val intPtrType = LLVMIntPtrTypeInContext(llvmContext, llvmTargetData)!! val intPtrType = LLVMIntPtrTypeInContext(llvmContext, llvmTargetData)!!
internal val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!! internal val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!!
internal val immThreeIntPtrType = LLVMConstInt(intPtrType, 3, 1)!! internal val immThreeIntPtrType = LLVMConstInt(intPtrType, 3, 1)!!
@@ -63,10 +67,10 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
fun param(fn: IrFunction, i: Int): LLVMValueRef { fun param(fn: IrFunction, i: Int): LLVMValueRef {
assert(i >= 0 && i < countParams(fn)) assert(i >= 0 && i < countParams(fn))
return LLVMGetParam(fn.llvmFunction, i)!! return LLVMGetParam(fn.llvmFunction.llvmValue, i)!!
} }
private fun countParams(fn: IrFunction) = LLVMCountParams(fn.llvmFunction) private fun countParams(fn: IrFunction) = LLVMCountParams(fn.llvmFunction.llvmValue)
fun functionEntryPointAddress(function: IrFunction) = function.entryPointAddress.llvm fun functionEntryPointAddress(function: IrFunction) = function.entryPointAddress.llvm
fun functionHash(function: IrFunction): LLVMValueRef = function.computeFunctionName().localHash.llvm fun functionHash(function: IrFunction): LLVMValueRef = function.computeFunctionName().localHash.llvm
@@ -128,7 +132,7 @@ internal inline fun generateFunction(
endLocation: LocationInfo?, endLocation: LocationInfo?,
code: FunctionGenerationContext.() -> Unit code: FunctionGenerationContext.() -> Unit
) { ) {
val llvmFunction = codegen.llvmFunction(function) val llvmFunction = codegen.llvmFunction(function).llvmValue
val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE
@@ -473,15 +477,21 @@ internal abstract class FunctionGenerationContext(
val stackLocalsManager = StackLocalsManagerImpl(this, stackLocalsInitBb) val stackLocalsManager = StackLocalsManagerImpl(this, stackLocalsInitBb)
data class FunctionInvokeInformation(val invokeInstruction: LLVMValueRef, val llvmFunction: LLVMValueRef, val rargs: CValues<CPointerVar<LLVMOpaqueValue>>, data class FunctionInvokeInformation(
val argsNumber: Int, val success: LLVMBasicBlockRef) val invokeInstruction: LLVMValueRef,
val llvmFunction: LLVMValueRef,
val rargs: CValues<CPointerVar<LLVMOpaqueValue>>,
val argsNumber: Int,
val success: LLVMBasicBlockRef,
val attributeProvider: LlvmFunctionAttributeProvider?
)
private val invokeInstructions = mutableListOf<FunctionInvokeInformation>() private val invokeInstructions = mutableListOf<FunctionInvokeInformation>()
/** /**
* TODO: consider merging this with [ExceptionHandler]. * TODO: consider merging this with [ExceptionHandler].
*/ */
var forwardingForeignExceptionsTerminatedWith: LLVMValueRef? = null var forwardingForeignExceptionsTerminatedWith: LlvmCallable? = null
// Whether the generating function needs to initialize Kotlin runtime before execution. Useful for interop bridges, // Whether the generating function needs to initialize Kotlin runtime before execution. Useful for interop bridges,
// for example. // for example.
@@ -647,10 +657,18 @@ internal abstract class FunctionGenerationContext(
Int32(size).llvm, Int32(size).llvm,
Int1(isVolatile).llvm)) Int1(isVolatile).llvm))
fun call(llvmCallable: LlvmCallable, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler = ExceptionHandler.None,
verbatim: Boolean = false): LLVMValueRef =
call(llvmCallable.llvmValue, args, resultLifetime, exceptionHandler, verbatim, llvmCallable.attributeProvider)
fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT, resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler = ExceptionHandler.None, exceptionHandler: ExceptionHandler = ExceptionHandler.None,
verbatim: Boolean = false): LLVMValueRef { verbatim: Boolean = false,
attributeProvider: LlvmFunctionAttributeProvider? = null
): LLVMValueRef {
val callArgs = if (verbatim || !isObjectReturn(llvmFunction.type)) { val callArgs = if (verbatim || !isObjectReturn(llvmFunction.type)) {
args args
} else { } else {
@@ -677,15 +695,18 @@ internal abstract class FunctionGenerationContext(
} }
args + resultSlot args + resultSlot
} }
return callRaw(llvmFunction, callArgs, exceptionHandler) return callRaw(llvmFunction, callArgs, exceptionHandler, attributeProvider)
} }
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
exceptionHandler: ExceptionHandler): LLVMValueRef { exceptionHandler: ExceptionHandler,
attributeProvider: LlvmFunctionAttributeProvider?): LLVMValueRef {
val rargs = args.toCValues() val rargs = args.toCValues()
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ && if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
isFunctionNoUnwind(llvmFunction)) { isFunctionNoUnwind(llvmFunction)) {
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!! return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!.also {
attributeProvider?.addCallSiteAttributes(it)
}
} else { } else {
val unwind = when (exceptionHandler) { val unwind = when (exceptionHandler) {
ExceptionHandler.Caller -> cleanupLandingpad ExceptionHandler.Caller -> cleanupLandingpad
@@ -706,11 +727,12 @@ internal abstract class FunctionGenerationContext(
val endLocation = position?.end val endLocation = position?.end
val success = basicBlock("call_success", endLocation) val success = basicBlock("call_success", endLocation)
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, unwind, "")!! val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, unwind, "")!!
attributeProvider?.addCallSiteAttributes(result)
// Store invoke instruction and its success block in reverse order. // Store invoke instruction and its success block in reverse order.
// Reverse order allows save arguments valid during all work with invokes // Reverse order allows save arguments valid during all work with invokes
// because other invokes processed before can be inside arguments list. // because other invokes processed before can be inside arguments list.
if (exceptionHandler == ExceptionHandler.Caller) if (exceptionHandler == ExceptionHandler.Caller)
invokeInstructions.add(0, FunctionInvokeInformation(result, llvmFunction, rargs, args.size, success)) invokeInstructions.add(0, FunctionInvokeInformation(result, llvmFunction, rargs, args.size, success, attributeProvider))
positionAtEnd(success) positionAtEnd(success)
return result return result
@@ -867,7 +889,7 @@ internal abstract class FunctionGenerationContext(
LLVMBuildExtractValue(builder, aggregate, index, name)!! LLVMBuildExtractValue(builder, aggregate, index, name)!!
fun gxxLandingpad(numClauses: Int, name: String = ""): LLVMValueRef { fun gxxLandingpad(numClauses: Int, name: String = ""): LLVMValueRef {
val personalityFunction = context.llvm.gxxPersonalityFunction val personalityFunction = context.llvm.gxxPersonalityFunction.llvmValue
// Type of `landingpad` instruction result (depends on personality function): // Type of `landingpad` instruction result (depends on personality function):
val landingpadType = structType(int8TypePtr, int32Type) val landingpadType = structType(int8TypePtr, int32Type)
@@ -1134,7 +1156,7 @@ internal abstract class FunctionGenerationContext(
} }
} }
fun lookupVirtualImpl(receiver: LLVMValueRef, irFunction: IrFunction): LLVMValueRef { fun lookupVirtualImpl(receiver: LLVMValueRef, irFunction: IrFunction): LlvmCallable {
assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr) assert(LLVMTypeOf(receiver) == codegen.kObjHeaderPtr)
val typeInfoPtr: LLVMValueRef = if (irFunction.getObjCMethodInfo() != null) val typeInfoPtr: LLVMValueRef = if (irFunction.getObjCMethodInfo() != null)
@@ -1170,7 +1192,10 @@ internal abstract class FunctionGenerationContext(
} }
} }
val functionPtrType = pointerType(codegen.getLlvmFunctionType(irFunction)) val functionPtrType = pointerType(codegen.getLlvmFunctionType(irFunction))
return bitcast(functionPtrType, llvmMethod) return LlvmCallable(
bitcast(functionPtrType, llvmMethod),
LlvmFunctionSignature(irFunction, this)
)
} }
private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? { private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? {
@@ -1268,7 +1293,7 @@ internal abstract class FunctionGenerationContext(
positionAtEnd(bbInit) positionAtEnd(bbInit)
val typeInfo = codegen.typeInfoForAllocation(irClass) val typeInfo = codegen.typeInfoForAllocation(irClass)
val defaultConstructor = irClass.constructors.single { it.valueParameters.size == 0 } val defaultConstructor = irClass.constructors.single { it.valueParameters.size == 0 }
val ctor = codegen.llvmFunction(defaultConstructor) val ctor = codegen.llvmFunction(defaultConstructor).llvmValue
val initFunction = val initFunction =
if (storageKind == ObjectStorageKind.SHARED && context.config.threadsAreAllowed) { if (storageKind == ObjectStorageKind.SHARED && context.config.threadsAreAllowed) {
context.llvm.initSingletonFunction context.llvm.initSingletonFunction
@@ -1296,14 +1321,14 @@ internal abstract class FunctionGenerationContext(
val getterId = loweredEnum.entriesMap[enumEntry.name]!!.getterId val getterId = loweredEnum.entriesMap[enumEntry.name]!!.getterId
val values = call( val values = call(
loweredEnum.valuesGetter.llvmFunction, loweredEnum.valuesGetter.llvmFunction.llvmValue,
emptyList(), emptyList(),
Lifetime.ARGUMENT, Lifetime.ARGUMENT,
exceptionHandler exceptionHandler
) )
return call( return call(
loweredEnum.itemGetterSymbol.owner.llvmFunction, loweredEnum.itemGetterSymbol.owner.llvmFunction.llvmValue,
listOf(values, Int32(getterId).llvm), listOf(values, Int32(getterId).llvm),
Lifetime.GLOBAL, Lifetime.GLOBAL,
exceptionHandler exceptionHandler
@@ -1321,12 +1346,12 @@ internal abstract class FunctionGenerationContext(
val name = irClass.descriptor.getExternalObjCMetaClassBinaryName() val name = irClass.descriptor.getExternalObjCMetaClassBinaryName()
val objCClass = getObjCClass(name, llvmSymbolOrigin) val objCClass = getObjCClass(name, llvmSymbolOrigin)
val getClass = context.llvm.externalFunction( val getClass = context.llvm.externalFunction(LlvmFunctionProto(
"object_getClass", "object_getClass",
functionType(int8TypePtr, false, int8TypePtr), LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
origin = context.standardLlvmSymbolsOrigin origin = context.standardLlvmSymbolsOrigin
) ))
call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler) call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler)
} else { } else {
getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin) getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin)
@@ -1490,7 +1515,8 @@ internal abstract class FunctionGenerationContext(
invokeInstructions.forEach { functionInvokeInfo -> invokeInstructions.forEach { functionInvokeInfo ->
positionBefore(functionInvokeInfo.invokeInstruction) positionBefore(functionInvokeInfo.invokeInstruction)
val newResult = LLVMBuildCall(builder, functionInvokeInfo.llvmFunction, functionInvokeInfo.rargs, val newResult = LLVMBuildCall(builder, functionInvokeInfo.llvmFunction, functionInvokeInfo.rargs,
functionInvokeInfo.argsNumber, "") functionInvokeInfo.argsNumber, "")!!
functionInvokeInfo.attributeProvider?.addCallSiteAttributes(newResult)
// Have to generate `br` instruction because of current scheme of debug info. // Have to generate `br` instruction because of current scheme of debug info.
br(functionInvokeInfo.success) br(functionInvokeInfo.success)
LLVMReplaceAllUsesWith(functionInvokeInfo.invokeInstruction, newResult) LLVMReplaceAllUsesWith(functionInvokeInfo.invokeInstruction, newResult)
@@ -1531,7 +1557,7 @@ internal abstract class FunctionGenerationContext(
handleEpilogueForExperimentalMM(context.llvm.Kotlin_mm_safePointFunctionEpilogue) handleEpilogueForExperimentalMM(context.llvm.Kotlin_mm_safePointFunctionEpilogue)
} }
private fun handleEpilogueForExperimentalMM(safePointFunction: LLVMValueRef) { private fun handleEpilogueForExperimentalMM(safePointFunction: LlvmCallable) {
if (context.memoryModel == MemoryModel.EXPERIMENTAL) { if (context.memoryModel == MemoryModel.EXPERIMENTAL) {
if (!forbidRuntime) { if (!forbidRuntime) {
call(safePointFunction, emptyList()) call(safePointFunction, emptyList())
@@ -5,9 +5,6 @@
package org.jetbrains.kotlin.backend.konan.llvm package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString import kotlinx.cinterop.toKString
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.konan.CachedLibraries import org.jetbrains.kotlin.backend.konan.CachedLibraries
@@ -170,19 +167,22 @@ internal interface ContextUtils : RuntimeAware {
* LLVM function generated from the Kotlin function. * LLVM function generated from the Kotlin function.
* It may be declared as external function prototype. * It may be declared as external function prototype.
*/ */
val IrFunction.llvmFunction: LLVMValueRef val IrFunction.llvmFunction: LlvmCallable
get() = llvmFunctionOrNull get() = llvmFunctionOrNull
?: error("$name in ${file.name}/${parent.fqNameForIrSerialization}") ?: error("$name in ${file.name}/${parent.fqNameForIrSerialization}")
val IrFunction.llvmFunctionOrNull: LLVMValueRef? val IrFunction.llvmFunctionOrNull: LlvmCallable?
get() { get() {
assert(this.isReal) assert(this.isReal) {
this.computeFullName()
}
return if (isExternal(this)) { return if (isExternal(this)) {
runtime.addedLLVMExternalFunctions.getOrPut(this) { context.llvm.externalFunction(this.computeSymbolName(), getLlvmFunctionType(this), runtime.addedLLVMExternalFunctions.getOrPut(this) {
origin = this.llvmSymbolOrigin) } val proto = LlvmFunctionProto(this, this.computeSymbolName(), this@ContextUtils)
context.llvm.externalFunction(proto)
}
} else { } else {
context.llvmDeclarations.forFunctionOrNull(this)?.llvmFunction context.llvmDeclarations.forFunctionOrNull(this)
} }
} }
@@ -191,7 +191,7 @@ internal interface ContextUtils : RuntimeAware {
*/ */
val IrFunction.entryPointAddress: ConstPointer val IrFunction.entryPointAddress: ConstPointer
get() { get() {
val result = LLVMConstBitCast(this.llvmFunction, int8TypePtr)!! val result = LLVMConstBitCast(this.llvmFunction.llvmValue, int8TypePtr)!!
return constPointer(result) return constPointer(result)
} }
@@ -256,19 +256,21 @@ internal class InitializersGenerationState {
internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : RuntimeAware { internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : RuntimeAware {
private fun importFunction(name: String, otherModule: LLVMModuleRef): LLVMValueRef { private fun importFunction(name: String, otherModule: LLVMModuleRef): LlvmCallable {
if (LLVMGetNamedFunction(llvmModule, name) != null) { if (LLVMGetNamedFunction(llvmModule, name) != null) {
throw IllegalArgumentException("function $name already exists") throw IllegalArgumentException("function $name already exists")
} }
val externalFunction = LLVMGetNamedFunction(otherModule, name) ?: throw Error("function $name not found") val externalFunction = LLVMGetNamedFunction(otherModule, name) ?: throw Error("function $name not found")
val attributesCopier = LlvmFunctionAttributeProvider.copyFromExternal(externalFunction)
val functionType = getFunctionType(externalFunction) val functionType = getFunctionType(externalFunction)
val function = LLVMAddFunction(llvmModule, name, functionType)!! val function = LLVMAddFunction(llvmModule, name, functionType)!!
copyFunctionAttributes(externalFunction, function) attributesCopier.addFunctionAttributes(function)
return function return LlvmCallable(function, attributesCopier)
} }
private fun importGlobal(name: String, otherModule: LLVMModuleRef): LLVMValueRef { private fun importGlobal(name: String, otherModule: LLVMModuleRef): LLVMValueRef {
@@ -283,74 +285,37 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
return global return global
} }
private fun copyFunctionAttributes(source: LLVMValueRef, destination: LLVMValueRef) { private fun importMemset(): LlvmCallable {
// TODO: consider parameter attributes
val attributeIndex = LLVMAttributeFunctionIndex
val count = LLVMGetAttributeCountAtIndex(source, attributeIndex)
memScoped {
val attributes = allocArray<LLVMAttributeRefVar>(count)
LLVMGetAttributesAtIndex(source, attributeIndex, attributes)
(0 until count).forEach {
LLVMAddAttributeAtIndex(destination, attributeIndex, attributes[it])
}
}
}
private fun importMemset(): LLVMValueRef {
val functionType = functionType(voidType, false, int8TypePtr, int8Type, int32Type, int1Type) val functionType = functionType(voidType, false, int8TypePtr, int8Type, int32Type, int1Type)
return llvmIntrinsic("llvm.memset.p0i8.i32", functionType) return llvmIntrinsic("llvm.memset.p0i8.i32", functionType)
} }
private fun llvmIntrinsic(name: String, type: LLVMTypeRef, vararg attributes: String): LLVMValueRef { private fun llvmIntrinsic(name: String, type: LLVMTypeRef, vararg attributes: String): LlvmCallable {
val result = LLVMAddFunction(llvmModule, name, type)!! val result = LLVMAddFunction(llvmModule, name, type)!!
attributes.forEach { attributes.forEach {
val kindId = getLlvmAttributeKindId(it) val kindId = getLlvmAttributeKindId(it)
addLlvmFunctionEnumAttribute(result, kindId) addLlvmFunctionEnumAttribute(result, kindId)
} }
return result return LlvmCallable(result, LlvmFunctionAttributeProvider.copyFromExternal(result))
} }
internal fun externalFunction( internal fun externalFunction(llvmFunctionProto: LlvmFunctionProto): LlvmCallable {
name: String, this.imports.add(llvmFunctionProto.origin, onlyBitcode = llvmFunctionProto.independent)
type: LLVMTypeRef, val found = LLVMGetNamedFunction(llvmModule, llvmFunctionProto.name)
origin: CompiledKlibModuleOrigin,
independent: Boolean = false
): LLVMValueRef {
this.imports.add(origin, onlyBitcode = independent)
val found = LLVMGetNamedFunction(llvmModule, name)
if (found != null) { if (found != null) {
assert(getFunctionType(found) == type) { assert(getFunctionType(found) == llvmFunctionProto.llvmFunctionType) {
"Expected: ${LLVMPrintTypeToString(type)!!.toKString()} " + "Expected: ${LLVMPrintTypeToString(llvmFunctionProto.llvmFunctionType)!!.toKString()} " +
"found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}" "found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}"
} }
assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage) assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
return found return LlvmCallable(found, llvmFunctionProto)
} else { } else {
// As exported functions are written in C++ they assume sign extension for promoted types - val function = addLlvmFunctionWithDefaultAttributes(context, llvmModule, llvmFunctionProto.name, llvmFunctionProto.llvmFunctionType)
// mention that in attributes. llvmFunctionProto.addFunctionAttributes(function)
val function = addLlvmFunctionWithDefaultAttributes(context, llvmModule, name, type) return LlvmCallable(function, llvmFunctionProto)
return memScoped {
val paramCount = LLVMCountParamTypes(type)
val paramTypes = allocArray<LLVMTypeRefVar>(paramCount)
LLVMGetParamTypes(type, paramTypes)
(0 until paramCount).forEach { index ->
val paramType = paramTypes[index]
addFunctionSignext(function, index + 1, paramType)
}
val returnType = LLVMGetReturnType(type)
addFunctionSignext(function, 0, returnType)
function
}
} }
} }
private fun externalNounwindFunction(name: String, type: LLVMTypeRef, origin: CompiledKlibModuleOrigin): LLVMValueRef {
val function = externalFunction(name, type, origin)
setFunctionNoUnwind(function)
return function
}
val imports get() = context.llvmImports val imports get() = context.llvmImports
class ImportsImpl(private val context: Context) : LlvmImports { class ImportsImpl(private val context: Context) : LlvmImports {
@@ -563,27 +528,35 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
else -> "__gxx_personality_v0" else -> "__gxx_personality_v0"
} }
val cxxStdTerminate = externalNounwindFunction( val cxxStdTerminate = externalFunction(LlvmFunctionProto(
"_ZSt9terminatev", // mangled C++ 'std::terminate' "_ZSt9terminatev", // mangled C++ 'std::terminate'
functionType(voidType, false), returnType = LlvmRetType(voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
origin = context.standardLlvmSymbolsOrigin origin = context.standardLlvmSymbolsOrigin
) ))
val gxxPersonalityFunction = externalNounwindFunction( val gxxPersonalityFunction = externalFunction(LlvmFunctionProto(
personalityFunctionName, personalityFunctionName,
functionType(int32Type, true), returnType = LlvmRetType(int32Type),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
isVararg = true,
origin = context.standardLlvmSymbolsOrigin origin = context.standardLlvmSymbolsOrigin
) ))
val cxaBeginCatchFunction = externalNounwindFunction(
val cxaBeginCatchFunction = externalFunction(LlvmFunctionProto(
"__cxa_begin_catch", "__cxa_begin_catch",
functionType(int8TypePtr, false, int8TypePtr), returnType = LlvmRetType(int8TypePtr),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
parameterTypes = listOf(LlvmParamType(int8TypePtr)),
origin = context.standardLlvmSymbolsOrigin origin = context.standardLlvmSymbolsOrigin
) ))
val cxaEndCatchFunction = externalNounwindFunction(
val cxaEndCatchFunction = externalFunction(LlvmFunctionProto(
"__cxa_end_catch", "__cxa_end_catch",
functionType(voidType, false), returnType = LlvmRetType(voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
origin = context.standardLlvmSymbolsOrigin origin = context.standardLlvmSymbolsOrigin
) ))
val memsetFunction = importMemset() val memsetFunction = importMemset()
//val memcpyFunction = importMemcpy() //val memcpyFunction = importMemcpy()
@@ -612,11 +585,11 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
private object lazyRtFunction { private object lazyRtFunction {
operator fun provideDelegate( operator fun provideDelegate(
thisRef: Llvm, property: KProperty<*> thisRef: Llvm, property: KProperty<*>
) = object : ReadOnlyProperty<Llvm, LLVMValueRef> { ) = object : ReadOnlyProperty<Llvm, LlvmCallable> {
val value by lazy { thisRef.importRtFunction(property.name) } val value: LlvmCallable by lazy { thisRef.importRtFunction(property.name) }
override fun getValue(thisRef: Llvm, property: KProperty<*>): LLVMValueRef = value override fun getValue(thisRef: Llvm, property: KProperty<*>): LlvmCallable = value
} }
} }
@@ -639,14 +612,14 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
* Width of NSInteger in bits. * Width of NSInteger in bits.
*/ */
val nsIntegerTypeWidth: Long by lazy { val nsIntegerTypeWidth: Long by lazy {
getSizeOfReturnTypeInBits(Kotlin_ObjCExport_NSIntegerTypeProvider) getSizeOfReturnTypeInBits(Kotlin_ObjCExport_NSIntegerTypeProvider.llvmValue)
} }
/** /**
* Width of C long type in bits. * Width of C long type in bits.
*/ */
val longTypeWidth: Long by lazy { val longTypeWidth: Long by lazy {
getSizeOfReturnTypeInBits(Kotlin_longTypeProvider) getSizeOfReturnTypeInBits(Kotlin_longTypeProvider.llvmValue)
} }
} }
@@ -454,25 +454,30 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
private fun FunctionGenerationContext.emitObjCGetMessenger(args: List<LLVMValueRef>, isStret: Boolean): LLVMValueRef { private fun FunctionGenerationContext.emitObjCGetMessenger(args: List<LLVMValueRef>, isStret: Boolean): LLVMValueRef {
val messengerNameSuffix = if (isStret) "_stret" else "" val messengerNameSuffix = if (isStret) "_stret" else ""
val functionType = functionType(int8TypePtr, true, int8TypePtr, int8TypePtr) val functionReturnType = LlvmRetType(int8TypePtr)
val functionParameterTypes = listOf(LlvmParamType(int8TypePtr), LlvmParamType(int8TypePtr))
val libobjc = context.standardLlvmSymbolsOrigin val libobjc = context.standardLlvmSymbolsOrigin
val normalMessenger = context.llvm.externalFunction( val normalMessenger = context.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSend$messengerNameSuffix", "objc_msgSend$messengerNameSuffix",
functionType, functionReturnType,
functionParameterTypes,
isVararg = true,
origin = libobjc origin = libobjc
) ))
val superMessenger = context.llvm.externalFunction( val superMessenger = context.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSendSuper$messengerNameSuffix", "objc_msgSendSuper$messengerNameSuffix",
functionType, functionReturnType,
functionParameterTypes,
isVararg = true,
origin = libobjc origin = libobjc
) ))
val superClass = args.single() val superClass = args.single()
val messenger = LLVMBuildSelect(builder, val messenger = LLVMBuildSelect(builder,
If = icmpEq(superClass, kNullInt8Ptr), If = icmpEq(superClass, kNullInt8Ptr),
Then = normalMessenger, Then = normalMessenger.llvmValue,
Else = superMessenger, Else = superMessenger.llvmValue,
Name = "" Name = ""
)!! )!!
@@ -712,7 +712,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val llvmFunction: LLVMValueRef) : InnerScopeImpl() { val llvmFunction: LLVMValueRef) : InnerScopeImpl() {
constructor(declaration: IrFunction, functionGenerationContext: FunctionGenerationContext) : constructor(declaration: IrFunction, functionGenerationContext: FunctionGenerationContext) :
this(functionGenerationContext, declaration, codegen.llvmFunction(declaration)) this(functionGenerationContext, declaration, codegen.llvmFunction(declaration).llvmValue)
constructor(llvmFunction: LLVMValueRef, functionGenerationContext: FunctionGenerationContext) : constructor(llvmFunction: LLVMValueRef, functionGenerationContext: FunctionGenerationContext) :
this(functionGenerationContext, null, llvmFunction) this(functionGenerationContext, null, llvmFunction)
@@ -857,7 +857,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
if (declaration.retainAnnotation(context.config.target)) { if (declaration.retainAnnotation(context.config.target)) {
context.llvm.usedFunctions.add(codegen.llvmFunction(declaration)) context.llvm.usedFunctions.add(codegen.llvmFunction(declaration).llvmValue)
} }
if (context.shouldVerifyBitCode()) if (context.shouldVerifyBitCode())
@@ -1645,12 +1645,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
} else { } else {
// e.g. ObjCObject, ObjCObjectBase etc. // e.g. ObjCObject, ObjCObjectBase etc.
if (dstClass.isObjCMetaClass()) { if (dstClass.isObjCMetaClass()) {
val isClass = context.llvm.externalFunction( val isClassProto = LlvmFunctionProto(
"object_isClass", "object_isClass",
functionType(int8Type, false, int8TypePtr), LlvmRetType(int8Type),
context.standardLlvmSymbolsOrigin listOf(LlvmParamType(int8TypePtr)),
origin = context.standardLlvmSymbolsOrigin
) )
val isClass = context.llvm.externalFunction(isClassProto)
call(isClass, listOf(objCObject)).let { call(isClass, listOf(objCObject)).let {
functionGenerationContext.icmpNe(it, Int8(0).llvm) functionGenerationContext.icmpNe(it, Int8(0).llvm)
} }
@@ -2180,15 +2181,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
if (codegen.isExternal(this) && !KonanBinaryInterface.isExported(this)) if (codegen.isExternal(this) && !KonanBinaryInterface.isExported(this))
null null
else else
codegen.llvmFunctionOrNull(this) codegen.llvmFunctionOrNull(this)?.llvmValue
return if (!isReifiedInline && functionLlvmValue != null) { return if (!isReifiedInline && functionLlvmValue != null) {
context.debugInfo.subprograms.getOrPut(functionLlvmValue) { context.debugInfo.subprograms.getOrPut(functionLlvmValue) {
memScoped { memScoped {
val subroutineType = subroutineType(context, codegen.llvmTargetData) val subroutineType = subroutineType(context, codegen.llvmTargetData)
val llvmFunnction = codegen.llvmFunction(this@scope) val llvmFunction = codegen.llvmFunction(this@scope).llvmValue
diFunctionScope(name.asString(), llvmFunnction.name!!, startLine, subroutineType).also { diFunctionScope(name.asString(), llvmFunction.name!!, startLine, subroutineType).also {
if (!this@scope.isInline) if (!this@scope.isInline)
DIFunctionAddSubprogram(llvmFunnction, it) DIFunctionAddSubprogram(llvmFunction, it)
} }
} }
} as DIScopeOpaqueRef } as DIScopeOpaqueRef
@@ -2374,7 +2375,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private fun evaluateFileGlobalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) { private fun evaluateFileGlobalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
val statePtr = getGlobalInitStateFor(fileInitializer.parent as IrFile) val statePtr = getGlobalInitStateFor(fileInitializer.parent as IrFile)
val initializerPtr = with(codegen) { fileInitializer.llvmFunction } val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
val bbInit = basicBlock("label_init", null) val bbInit = basicBlock("label_init", null)
val bbExit = basicBlock("label_continue", null) val bbExit = basicBlock("label_continue", null)
@@ -2396,7 +2397,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val globalStatePtr = getGlobalInitStateFor(fileInitializer.parent as IrFile) val globalStatePtr = getGlobalInitStateFor(fileInitializer.parent as IrFile)
val localState = getThreadLocalInitStateFor(fileInitializer.parent as IrFile) val localState = getThreadLocalInitStateFor(fileInitializer.parent as IrFile)
val localStatePtr = localState.getAddress(functionGenerationContext) val localStatePtr = localState.getAddress(functionGenerationContext)
val initializerPtr = with(codegen) { fileInitializer.llvmFunction } val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
val bbInit = basicBlock("label_init", null) val bbInit = basicBlock("label_init", null)
val bbCheckLocalState = basicBlock("label_check_local", null) val bbCheckLocalState = basicBlock("label_check_local", null)
@@ -2422,7 +2423,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private fun evaluateFileStandaloneThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) { private fun evaluateFileStandaloneThreadLocalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
val state = getThreadLocalInitStateFor(fileInitializer.parent as IrFile) val state = getThreadLocalInitStateFor(fileInitializer.parent as IrFile)
val statePtr = state.getAddress(functionGenerationContext) val statePtr = state.getAddress(functionGenerationContext)
val initializerPtr = with(codegen) { fileInitializer.llvmFunction } val initializerPtr = with(codegen) { fileInitializer.llvmFunction.llvmValue }
val bbInit = basicBlock("label_init", null) val bbInit = basicBlock("label_init", null)
val bbExit = basicBlock("label_continue", null) val bbExit = basicBlock("label_continue", null)
@@ -2494,12 +2495,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val annotation = irClass.annotations.findAnnotation(externalObjCClassFqName)!! val annotation = irClass.annotations.findAnnotation(externalObjCClassFqName)!!
val protocolGetterName = annotation.getAnnotationStringValue("protocolGetter") val protocolGetterName = annotation.getAnnotationStringValue("protocolGetter")
val protocolGetter = context.llvm.externalFunction( val protocolGetterProto = LlvmFunctionProto(
protocolGetterName, protocolGetterName,
functionType(int8TypePtr, false), LlvmRetType(int8TypePtr),
irClass.llvmSymbolOrigin, origin = irClass.llvmSymbolOrigin,
independent = true // Protocol is header-only declaration. independent = true // Protocol is header-only declaration.
) )
val protocolGetter = context.llvm.externalFunction(protocolGetterProto)
return call(protocolGetter, emptyList()) return call(protocolGetter, emptyList())
} }
@@ -2571,20 +2573,16 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
fun callDirect(function: IrFunction, args: List<LLVMValueRef>, fun callDirect(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef {
resultLifetime: Lifetime): LLVMValueRef { val functionDeclarations = codegen.llvmFunction(function.target)
val llvmFunction = codegen.llvmFunction(function.target) return call(function, functionDeclarations, args, resultLifetime)
return call(function, llvmFunction, args, resultLifetime)
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
fun callVirtual(function: IrFunction, args: List<LLVMValueRef>, fun callVirtual(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef {
resultLifetime: Lifetime): LLVMValueRef { val functionDeclarations = functionGenerationContext.lookupVirtualImpl(args.first(), function)
return call(function, functionDeclarations, args, resultLifetime)
val llvmFunction = functionGenerationContext.lookupVirtualImpl(args.first(), function)
return call(function, llvmFunction, args, resultLifetime) // Invoke the method
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -2602,7 +2600,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
return result return result
} }
private fun call(function: IrFunction, llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, private fun call(function: IrFunction, llvmCallable: LlvmCallable, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef { resultLifetime: Lifetime): LLVMValueRef {
check(!function.isTypedIntrinsic) check(!function.isTypedIntrinsic)
@@ -2620,7 +2618,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.switchThreadState(ThreadState.Native) functionGenerationContext.switchThreadState(ThreadState.Native)
} }
val result = call(llvmFunction, args, resultLifetime, exceptionHandler) val result = call(llvmCallable, args, resultLifetime, exceptionHandler)
when { when {
!function.isSuspend && function.returnType.isNothing() -> !function.isSuspend && function.returnType.isNothing() ->
@@ -2629,16 +2627,18 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.switchThreadState(ThreadState.Runnable) functionGenerationContext.switchThreadState(ThreadState.Runnable)
} }
if (LLVMGetReturnType(getFunctionType(llvmFunction)) == voidType) { if (llvmCallable.returnType == voidType) {
return codegen.theUnitInstanceRef.llvm return codegen.theUnitInstanceRef.llvm
} }
return result return result
} }
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>, private fun call(
resultLifetime: Lifetime = Lifetime.IRRELEVANT, function: LlvmCallable, args: List<LLVMValueRef>,
exceptionHandler: ExceptionHandler = currentCodeContext.exceptionHandler): LLVMValueRef { resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler = currentCodeContext.exceptionHandler,
): LLVMValueRef {
return functionGenerationContext.call(function, args, resultLifetime, exceptionHandler) return functionGenerationContext.call(function, args, resultLifetime, exceptionHandler)
} }
@@ -87,7 +87,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
.distinctBy { it.selector } .distinctBy { it.selector }
allInitMethodsInfo.mapTo(this) { allInitMethodsInfo.mapTo(this) {
ObjCMethodDesc(it.selector, it.encoding, context.llvm.missingInitImp) ObjCMethodDesc(it.selector, it.encoding, context.llvm.missingInitImp.llvmValue)
} }
} }
@@ -126,7 +126,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
ObjCMethodDesc( ObjCMethodDesc(
annotation.getAnnotationStringValue("selector"), annotation.getAnnotationStringValue("selector"),
annotation.getAnnotationStringValue("encoding"), annotation.getAnnotationStringValue("encoding"),
it.llvmFunction it.llvmFunction.llvmValue
) )
} }
@@ -48,27 +48,6 @@ private fun addTargetCpuAndFeaturesAttributes(context: Context, llvmFunction: LL
} }
} }
internal fun addLlvmAttributesForKotlinFunction(context: Context, irFunction: IrFunction, llvmFunction: LLVMValueRef) {
if (irFunction.returnType.isNothing()) {
setFunctionNoReturn(llvmFunction)
}
if (mustNotInline(context, irFunction)) {
setFunctionNoInline(llvmFunction)
}
}
private fun mustNotInline(context: Context, irFunction: IrFunction): Boolean {
if (context.shouldContainLocationDebugInfo()) {
if (irFunction is IrConstructor && irFunction.isPrimary && irFunction.returnType.isThrowable()) {
// To simplify skipping this constructor when scanning call stack in Kotlin_getCurrentStackTrace.
return true
}
}
return false
}
private fun shouldEnforceFramePointer(context: Context): Boolean { private fun shouldEnforceFramePointer(context: Context): Boolean {
// TODO: do we still need it? // TODO: do we still need it?
if (!context.shouldOptimize()) { if (!context.shouldOptimize()) {
@@ -99,3 +78,38 @@ private fun enforceFramePointer(llvmFunction: LLVMValueRef, context: Context) {
LLVMAddTargetDependentFunctionAttr(llvmFunction, "frame-pointer", fpKind) LLVMAddTargetDependentFunctionAttr(llvmFunction, "frame-pointer", fpKind)
} }
interface LlvmAttribute {
fun asAttributeKindId(): LLVMAttributeKindId
}
// We use sealed class instead of enum because there are attributes with parameters
// that we might want to use later. For example, align(<n>).
sealed class LlvmParameterAttribute(private val llvmAttributeName: String) : LlvmAttribute {
override fun asAttributeKindId(): LLVMAttributeKindId = llvmAttributeKindIdCache.getOrPut(this) {
getLlvmAttributeKindId(llvmAttributeName)
}
companion object {
private val llvmAttributeKindIdCache = mutableMapOf<LlvmParameterAttribute, LLVMAttributeKindId>()
}
object SignExt : LlvmParameterAttribute("signext")
object ZeroExt : LlvmParameterAttribute("zeroext")
}
sealed class LlvmFunctionAttribute(private val llvmAttributeName: String) : LlvmAttribute {
override fun asAttributeKindId(): LLVMAttributeKindId = llvmAttributeKindIdCache.getOrPut(this) {
getLlvmAttributeKindId(llvmAttributeName)
}
companion object {
private val llvmAttributeKindIdCache = mutableMapOf<LlvmFunctionAttribute, LLVMAttributeKindId>()
}
object NoUnwind : LlvmFunctionAttribute("nounwind")
object NoReturn : LlvmFunctionAttribute("noreturn")
object NoInline : LlvmFunctionAttribute("noinline")
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMGetReturnType
import llvm.LLVMTypeRef
import llvm.LLVMValueRef
/**
* Wrapper around LLVM value of functional type.
*/
class LlvmCallable(val llvmValue: LLVMValueRef, val attributeProvider: LlvmFunctionAttributeProvider) {
val returnType: LLVMTypeRef by lazy {
LLVMGetReturnType(functionType)!!
}
val functionType: LLVMTypeRef by lazy {
getFunctionType(llvmValue)
}
}
@@ -34,8 +34,13 @@ enum class UniqueKind(val llvmName: String) {
} }
internal class LlvmDeclarations(private val unique: Map<UniqueKind, UniqueLlvmDeclarations>) { internal class LlvmDeclarations(private val unique: Map<UniqueKind, UniqueLlvmDeclarations>) {
fun forFunction(function: IrFunction) = forFunctionOrNull(function) ?: with(function){error("$name in $file/${parent.fqNameForIrSerialization}")} fun forFunction(function: IrFunction): LlvmCallable =
fun forFunctionOrNull(function: IrFunction) = (function.metadata as? CodegenFunctionMetadata)?.llvm forFunctionOrNull(function) ?: with(function) {
error("$name in $file/${parent.fqNameForIrSerialization}")
}
fun forFunctionOrNull(function: IrFunction): LlvmCallable? =
(function.metadata as? CodegenFunctionMetadata)?.llvm
fun forClass(irClass: IrClass) = (irClass.metadata as? CodegenClassMetadata)?.llvm ?: fun forClass(irClass: IrClass) = (irClass.metadata as? CodegenClassMetadata)?.llvm ?:
error(irClass.descriptor.toString()) error(irClass.descriptor.toString())
@@ -68,8 +73,6 @@ internal class KotlinObjCClassLlvmDeclarations(
val bodyOffsetGlobal: StaticData.Global val bodyOffsetGlobal: StaticData.Global
) )
internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef)
internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef) internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef)
internal class StaticFieldLlvmDeclarations(val storageAddressAccess: AddressAccess) internal class StaticFieldLlvmDeclarations(val storageAddressAccess: AddressAccess)
@@ -338,8 +341,6 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
if (!declaration.isReal) return if (!declaration.isReal) return
val llvmFunctionType = getLlvmFunctionType(declaration)
if ((declaration is IrConstructor && declaration.isObjCConstructor)) { if ((declaration is IrConstructor && declaration.isObjCConstructor)) {
return return
} }
@@ -351,11 +352,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|| (declaration.isAccessor && declaration.isFromInteropLibrary()) || (declaration.isAccessor && declaration.isFromInteropLibrary())
|| declaration.annotations.hasAnnotation(RuntimeNames.cCall)) return || declaration.annotations.hasAnnotation(RuntimeNames.cCall)) return
context.llvm.externalFunction(declaration.computeSymbolName(), llvmFunctionType, val proto = LlvmFunctionProto(declaration, declaration.computeSymbolName(), this)
// Assume that `external fun` is defined in native libs attached to this module: context.llvm.externalFunction(proto)
origin = declaration.llvmSymbolOrigin,
independent = declaration.hasAnnotation(RuntimeNames.independent)
)
} else { } else {
val symbolName = if (declaration.isExported()) { val symbolName = if (declaration.isExported()) {
declaration.computeSymbolName().also { declaration.computeSymbolName().also {
@@ -369,21 +367,22 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
} else { } else {
"kfun:" + qualifyInternalName(declaration) "kfun:" + qualifyInternalName(declaration)
} }
val proto = LlvmFunctionProto(declaration, symbolName, this)
addLlvmFunctionWithDefaultAttributes( val llvmFunction = addLlvmFunctionWithDefaultAttributes(
context, context,
context.llvmModule!!, context.llvmModule!!,
symbolName, symbolName,
llvmFunctionType proto.llvmFunctionType
).also { ).also {
addLlvmAttributesForKotlinFunction(context, declaration, it) proto.addFunctionAttributes(it)
} }
LlvmCallable(llvmFunction, proto)
} }
declaration.metadata = CodegenFunctionMetadata( declaration.metadata = CodegenFunctionMetadata(
declaration.metadata?.name, declaration.metadata?.name,
declaration.konanLibrary, declaration.konanLibrary,
FunctionLlvmDeclarations(llvmFunction) llvmFunction
) )
} }
} }
@@ -400,7 +399,7 @@ internal class CodegenClassMetadata(irClass: IrClass)
private class CodegenFunctionMetadata( private class CodegenFunctionMetadata(
name: Name?, name: Name?,
konanLibrary: KotlinLibrary?, konanLibrary: KotlinLibrary?,
val llvm: FunctionLlvmDeclarations val llvm: LlvmCallable
) : KonanMetadata(name, konanLibrary), MetadataSource.Function ) : KonanMetadata(name, konanLibrary), MetadataSource.Function
private class CodegenInstanceFieldMetadata( private class CodegenInstanceFieldMetadata(
@@ -0,0 +1,200 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.isThrowable
/**
* Add attributes to LLVM function declaration and its invocation.
*/
interface LlvmFunctionAttributeProvider {
fun addCallSiteAttributes(callSite: LLVMValueRef)
fun addFunctionAttributes(function: LLVMValueRef)
companion object {
fun makeEmpty(): LlvmFunctionAttributeProvider =
DummyLlvmFunctionAttributeProvider
fun copyFromExternal(externalFunction: LLVMValueRef): LlvmFunctionAttributeProvider =
LlvmFunctionAttributesCopier(externalFunction)
}
}
private object DummyLlvmFunctionAttributeProvider : LlvmFunctionAttributeProvider {
override fun addCallSiteAttributes(callSite: LLVMValueRef) {}
override fun addFunctionAttributes(function: LLVMValueRef) {}
}
/**
* Copies attributes from the given [externalFunction].
* This is useful when the [externalFunction] is declared in the another LLVM module,
* and we want to create an external declaration for it.
*/
private class LlvmFunctionAttributesCopier(private val externalFunction: LLVMValueRef) : LlvmFunctionAttributeProvider {
private val paramsCount: Int by lazy { LLVMCountParams(externalFunction) }
private val attributesForCallSite: List<List<LLVMAttributeRef>> by lazy {
attributesForFunctionDeclaration.map {
// We don't need attributes like correctly-rounded-divide-sqrt-fp-math or less-precise-fpmad at callsites.
// So let's take only enum and integer attributes, should be enough to generate correct calls.
it.filter {
// This function is actually more like "is enum OR int attribute".
LLVMIsEnumAttribute(it) != 0
}
}
}
private val attributesForFunctionDeclaration: List<List<LLVMAttributeRef>> by lazy {
memScoped {
val result = mutableListOf<List<LLVMAttributeRef>>()
for (index in LLVMAttributeFunctionIndex..paramsCount) {
val count = LLVMGetAttributeCountAtIndex(externalFunction, index)
val attributesBuffer = allocArray<LLVMAttributeRefVar>(count)
LLVMGetAttributesAtIndex(externalFunction, index, attributesBuffer)
result += (0 until count).map { attributesBuffer[it]!! }
}
result
}
}
override fun addCallSiteAttributes(callSite: LLVMValueRef) {
attributesForCallSite.withIndex().forEach { (listIndex, attributeList) ->
attributeList.forEach { attributeRef ->
LLVMAddCallSiteAttribute(callSite, LLVMAttributeFunctionIndex + listIndex, attributeRef)
}
}
}
override fun addFunctionAttributes(function: LLVMValueRef) {
attributesForFunctionDeclaration.withIndex().forEach { (listIndex, attributeList) ->
attributeList.forEach { attributeRef ->
LLVMAddAttributeAtIndex(function, LLVMAttributeFunctionIndex + listIndex, attributeRef)
}
}
}
}
private fun addCallSiteAttributesAtIndex(context: LLVMContextRef, callSite: LLVMValueRef, index: Int, attributes: List<LlvmAttribute>) {
attributes.forEach { attribute ->
val llvmAttributeRef = createLlvmEnumAttribute(context, attribute.asAttributeKindId())
LLVMAddCallSiteAttribute(callSite, index, llvmAttributeRef)
}
}
private fun addDeclarationAttributesAtIndex(context: LLVMContextRef, function: LLVMValueRef, index: Int, attributes: List<LlvmAttribute>) {
attributes.forEach { attribute ->
val llvmAttributeRef = createLlvmEnumAttribute(context, attribute.asAttributeKindId())
LLVMAddAttributeAtIndex(function, index, llvmAttributeRef)
}
}
/**
* LLVM function's signature, enriched with attributes.
*/
internal open class LlvmFunctionSignature(
val returnType: LlvmRetType,
val parameterTypes: List<LlvmParamType> = emptyList(),
val isVararg: Boolean = false,
val functionAttributes: List<LlvmFunctionAttribute> = emptyList(),
) : LlvmFunctionAttributeProvider {
constructor(irFunction: IrFunction, contextUtils: ContextUtils) : this(
returnType = contextUtils.getLlvmFunctionReturnType(irFunction),
parameterTypes = contextUtils.getLlvmFunctionParameterTypes(irFunction),
functionAttributes = inferFunctionAttributes(contextUtils, irFunction),
isVararg = false,
)
val llvmFunctionType by lazy {
functionType(returnType.llvmType, isVararg, parameterTypes.map { it.llvmType })
}
override fun addCallSiteAttributes(callSite: LLVMValueRef) {
val caller = LLVMGetBasicBlockParent(LLVMGetInstructionParent(callSite))
val llvmContext = LLVMGetModuleContext(LLVMGetGlobalParent(caller))!!
addCallSiteAttributesAtIndex(llvmContext, callSite, LLVMAttributeFunctionIndex, functionAttributes)
addCallSiteAttributesAtIndex(llvmContext, callSite, LLVMAttributeReturnIndex, returnType.attributes)
repeat(parameterTypes.count()) {
addCallSiteAttributesAtIndex(llvmContext, callSite, it + 1, parameterTypes[it].attributes)
}
}
override fun addFunctionAttributes(function: LLVMValueRef) {
val llvmContext = LLVMGetModuleContext(LLVMGetGlobalParent(function))!!
addDeclarationAttributesAtIndex(llvmContext, function, LLVMAttributeFunctionIndex, functionAttributes)
addDeclarationAttributesAtIndex(llvmContext, function, LLVMAttributeReturnIndex, returnType.attributes)
repeat(parameterTypes.count()) {
addDeclarationAttributesAtIndex(llvmContext, function, it + 1, parameterTypes[it].attributes)
}
}
}
/**
* Prototype of a LLVM function that is not tied to a specific LLVM module.
*/
internal class LlvmFunctionProto(
val name: String,
returnType: LlvmRetType,
parameterTypes: List<LlvmParamType> = emptyList(),
functionAttributes: List<LlvmFunctionAttribute> = emptyList(),
val origin: CompiledKlibModuleOrigin,
isVararg: Boolean = false,
val independent: Boolean = false,
) : LlvmFunctionSignature(returnType, parameterTypes, isVararg, functionAttributes) {
constructor(
name: String,
signature: LlvmFunctionSignature,
origin: CompiledKlibModuleOrigin,
independent: Boolean = false,
) : this(name, signature.returnType, signature.parameterTypes, signature.functionAttributes, origin, signature.isVararg, independent)
constructor(irFunction: IrFunction, symbolName: String, contextUtils: ContextUtils) : this(
name = symbolName,
returnType = contextUtils.getLlvmFunctionReturnType(irFunction),
parameterTypes = contextUtils.getLlvmFunctionParameterTypes(irFunction),
functionAttributes = inferFunctionAttributes(contextUtils, irFunction),
origin = irFunction.llvmSymbolOrigin,
independent = irFunction.hasAnnotation(RuntimeNames.independent)
)
}
private fun mustNotInline(context: Context, irFunction: IrFunction): Boolean {
if (context.shouldContainLocationDebugInfo()) {
if (irFunction is IrConstructor && irFunction.isPrimary && irFunction.returnType.isThrowable()) {
// To simplify skipping this constructor when scanning call stack in Kotlin_getCurrentStackTrace.
return true
}
}
return false
}
private fun inferFunctionAttributes(contextUtils: ContextUtils, irFunction: IrFunction): List<LlvmFunctionAttribute> =
mutableListOf<LlvmFunctionAttribute>().apply {
// suspend function can return value in case of COROUTINE_SUSPENDED.
if (irFunction.returnType.isNothing() && !irFunction.isSuspend) {
add(LlvmFunctionAttribute.NoReturn)
}
if (mustNotInline(contextUtils.context, irFunction)) {
add(LlvmFunctionAttribute.NoInline)
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.common.ir.allParameters
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.unwrapToPrimitiveOrReference
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.classId
import org.jetbrains.kotlin.ir.util.isSuspend
/**
* LLVM function's parameter type with its attributes.
*/
class LlvmParamType(val llvmType: LLVMTypeRef, val attributes: List<LlvmParameterAttribute> = emptyList())
/**
* A bit better readability for cases when [LlvmParamType] represents return type.
*/
typealias LlvmRetType = LlvmParamType
internal fun RuntimeAware.getLlvmFunctionParameterTypes(function: IrFunction): List<LlvmParamType> {
val returnType = getLlvmFunctionReturnType(function).llvmType
val paramTypes = ArrayList(function.allParameters.map { LlvmParamType(getLLVMType(it.type), defaultParameterAttributesForIrType(it.type)) })
if (function.isSuspend)
paramTypes.add(LlvmParamType(kObjHeaderPtr)) // Suspend functions have implicit parameter of type Continuation<>.
if (isObjectType(returnType))
paramTypes.add(LlvmParamType(kObjHeaderPtrPtr))
return paramTypes
}
internal fun RuntimeAware.getLlvmFunctionReturnType(function: IrFunction): LlvmRetType {
val returnType = when {
function is IrConstructor -> LlvmParamType(voidType)
function.isSuspend -> LlvmParamType(kObjHeaderPtr) // Suspend functions return Any?.
else -> LlvmParamType(getLLVMReturnType(function.returnType), defaultParameterAttributesForIrType(function.returnType))
}
return returnType
}
// Note: Probably, this function should become target-dependent in the future.
private fun defaultParameterAttributesForIrType(irType: IrType): List<LlvmParameterAttribute> {
// TODO: We perform type unwrapping twice: one to get the underlying type, and then this one.
// Unwrapping is not cheap, so it might affect compilation time.
return irType.unwrapToPrimitiveOrReference(
eachInlinedClass = { inlinedClass, _ ->
when (inlinedClass.classId) {
UnsignedType.UBYTE.classId -> return listOf(LlvmParameterAttribute.ZeroExt)
UnsignedType.USHORT.classId -> return listOf(LlvmParameterAttribute.ZeroExt)
}
},
ifPrimitive = { primitiveType, _ ->
when (primitiveType) {
KonanPrimitiveType.BOOLEAN -> listOf(LlvmParameterAttribute.ZeroExt)
KonanPrimitiveType.CHAR -> listOf(LlvmParameterAttribute.ZeroExt)
KonanPrimitiveType.BYTE -> listOf(LlvmParameterAttribute.SignExt)
KonanPrimitiveType.SHORT -> listOf(LlvmParameterAttribute.SignExt)
KonanPrimitiveType.INT -> emptyList()
KonanPrimitiveType.LONG -> emptyList()
KonanPrimitiveType.FLOAT -> emptyList()
KonanPrimitiveType.DOUBLE -> emptyList()
KonanPrimitiveType.NON_NULL_NATIVE_PTR -> emptyList()
KonanPrimitiveType.VECTOR128 -> emptyList()
}
},
ifReference = {
return listOf()
},
)
}
@@ -17,7 +17,7 @@ interface RuntimeAware {
class Runtime(bitcodeFile: String) { class Runtime(bitcodeFile: String) {
val llvmModule: LLVMModuleRef = parseBitcodeFile(bitcodeFile) val llvmModule: LLVMModuleRef = parseBitcodeFile(bitcodeFile)
val calculatedLLVMTypes: MutableMap<IrType, LLVMTypeRef> = HashMap() val calculatedLLVMTypes: MutableMap<IrType, LLVMTypeRef> = HashMap()
val addedLLVMExternalFunctions: MutableMap<IrFunction, LLVMValueRef> = HashMap() val addedLLVMExternalFunctions: MutableMap<IrFunction, LlvmCallable> = HashMap()
internal fun getStructTypeOrNull(name: String) = LLVMGetTypeByName(llvmModule, "struct.$name") internal fun getStructTypeOrNull(name: String) = LLVMGetTypeByName(llvmModule, "struct.$name")
internal fun getStructType(name: String) = getStructTypeOrNull(name) internal fun getStructType(name: String) = getStructTypeOrNull(name)
@@ -46,8 +46,8 @@ internal class LLVMCoverageInstrumentation(
// Each profiled function should have a global with its name in a specific format. // Each profiled function should have a global with its name in a specific format.
private fun createFunctionNameGlobal(function: IrFunction): LLVMValueRef { private fun createFunctionNameGlobal(function: IrFunction): LLVMValueRef {
val name = function.llvmFunction.name val name = function.llvmFunction.llvmValue.name
val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction, name)!! val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction.llvmValue, name)!!
return LLVMConstBitCast(pgoFunctionName, int8TypePtr)!! return LLVMConstBitCast(pgoFunctionName, int8TypePtr)!!
} }
} }
@@ -54,7 +54,7 @@ internal class LLVMCoverageWriter(
fileIds.toCValues(), fileIds.size.signExtend(), fileIds.toCValues(), fileIds.size.signExtend(),
regions.toCValues(), regions.size.signExtend()) regions.toCValues(), regions.size.signExtend())
val functionName = context.llvmDeclarations.forFunction(functionRegions.function).llvmFunction.name val functionName = context.llvmDeclarations.forFunction(functionRegions.function).llvmValue.name
val functionMappingRecord = LLVMAddFunctionMappingRecord(LLVMGetModuleContext(context.llvmModule), val functionMappingRecord = LLVMAddFunctionMappingRecord(LLVMGetModuleContext(context.llvmModule),
functionName, functionRegions.structuralHash, functionCoverage)!! functionName, functionRegions.structuralHash, functionCoverage)!!
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.konan.llvm.objc package org.jetbrains.kotlin.backend.konan.llvm.objc
import llvm.LLVMTypeRef
import llvm.LLVMValueRef import llvm.LLVMValueRef
import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.*
@@ -22,44 +21,54 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
} }
private val objcMsgSend = constPointer( private val objcMsgSend = constPointer(
context.llvm.externalFunction( context.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSend", "objc_msgSend",
functionType(int8TypePtr, true, int8TypePtr, int8TypePtr), LlvmRetType(int8TypePtr),
context.stdlibModule.llvmSymbolOrigin listOf(LlvmParamType(int8TypePtr), LlvmParamType(int8TypePtr)),
) isVararg = true,
origin = context.stdlibModule.llvmSymbolOrigin
)).llvmValue
) )
val objcRelease = context.llvm.externalFunction( val objcRelease = run {
"objc_release", val proto = LlvmFunctionProto(
functionType(voidType, false, int8TypePtr), "objc_release",
context.stdlibModule.llvmSymbolOrigin LlvmRetType(voidType),
) listOf(LlvmParamType(int8TypePtr)),
origin = context.stdlibModule.llvmSymbolOrigin
)
context.llvm.externalFunction(proto)
}
val objcAlloc = context.llvm.externalFunction( val objcAlloc = context.llvm.externalFunction(LlvmFunctionProto(
"objc_alloc", "objc_alloc",
functionType(int8TypePtr, false, int8TypePtr), LlvmRetType(int8TypePtr),
context.stdlibModule.llvmSymbolOrigin listOf(LlvmParamType(int8TypePtr)),
) origin = context.stdlibModule.llvmSymbolOrigin
))
val objcAutorelease = context.llvm.externalFunction( val objcAutorelease = context.llvm.externalFunction(LlvmFunctionProto(
"llvm.objc.autorelease", "llvm.objc.autorelease",
functionType(int8TypePtr, false, int8TypePtr), LlvmRetType(int8TypePtr),
context.stdlibModule.llvmSymbolOrigin listOf(LlvmParamType(int8TypePtr)),
).also { listOf(LlvmFunctionAttribute.NoUnwind),
setFunctionNoUnwind(it) origin = context.stdlibModule.llvmSymbolOrigin
} ))
val objcAutoreleaseReturnValue = context.llvm.externalFunction( val objcAutoreleaseReturnValue = context.llvm.externalFunction(LlvmFunctionProto(
"llvm.objc.autoreleaseReturnValue", "llvm.objc.autoreleaseReturnValue",
functionType(int8TypePtr, false, int8TypePtr), LlvmRetType(int8TypePtr),
context.stdlibModule.llvmSymbolOrigin listOf(LlvmParamType(int8TypePtr)),
).also { listOf(LlvmFunctionAttribute.NoUnwind),
setFunctionNoUnwind(it) origin = context.stdlibModule.llvmSymbolOrigin
} ))
// TODO: this doesn't support stret. // TODO: this doesn't support stret.
fun msgSender(functionType: LLVMTypeRef): LLVMValueRef = fun msgSender(functionType: LlvmFunctionSignature): LlvmCallable =
objcMsgSend.bitcast(pointerType(functionType)).llvm LlvmCallable(
objcMsgSend.bitcast(pointerType(functionType.llvmFunctionType)).llvm,
functionType
)
} }
internal fun FunctionGenerationContext.genObjCSelector(selector: String): LLVMValueRef { internal fun FunctionGenerationContext.genObjCSelector(selector: String): LLVMValueRef {
@@ -31,7 +31,7 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
} }
val invokeImpl = functionGenerator( val invokeImpl = functionGenerator(
codegen.getLlvmFunctionType(invokeMethod), LlvmFunctionSignature(invokeMethod, codegen),
"invokeFunction${bridge.nameSuffix}" "invokeFunction${bridge.nameSuffix}"
).generate { ).generate {
val args = (0 until bridge.numberOfParameters).map { index -> val args = (0 until bridge.numberOfParameters).map { index ->
@@ -69,9 +69,8 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
bodyType, bodyType,
immutable = true immutable = true
) )
return functionGenerator( return functionGenerator(
functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr), LlvmFunctionSignature(LlvmRetType(codegen.kObjHeaderPtr), listOf(LlvmParamType(int8TypePtr), LlvmParamType(codegen.kObjHeaderPtrPtr))),
"convertBlock${bridge.nameSuffix}" "convertBlock${bridge.nameSuffix}"
).generate { ).generate {
val blockPtr = param(0) val blockPtr = param(0)
@@ -108,7 +107,7 @@ private fun FunctionGenerationContext.loadBlockInvoke(
val blockLiteralType = codegen.runtime.getStructType("Block_literal_1") val blockLiteralType = codegen.runtime.getStructType("Block_literal_1")
val invokePtr = structGep(bitcast(pointerType(blockLiteralType), blockPtr), 3) val invokePtr = structGep(bitcast(pointerType(blockLiteralType), blockPtr), 3)
return bitcast(pointerType(bridge.blockType.blockInvokeLlvmType), load(invokePtr)) return bitcast(pointerType(bridge.blockType.blockInvokeLlvmType.llvmFunctionType), load(invokePtr))
} }
private fun FunctionGenerationContext.allocInstanceWithAssociatedObject( private fun FunctionGenerationContext.allocInstanceWithAssociatedObject(
@@ -129,11 +128,10 @@ private val BlockPointerBridge.blockType: BlockType
*/ */
internal data class BlockType(val numberOfParameters: Int, val returnsVoid: Boolean) internal data class BlockType(val numberOfParameters: Int, val returnsVoid: Boolean)
private val BlockType.blockInvokeLlvmType: LLVMTypeRef private val BlockType.blockInvokeLlvmType: LlvmFunctionSignature
get() = functionType( get() = LlvmFunctionSignature(
if (returnsVoid) voidType else int8TypePtr, LlvmRetType(if (returnsVoid) voidType else int8TypePtr),
false, (0..numberOfParameters).map { LlvmParamType(int8TypePtr) }
(0..numberOfParameters).map { int8TypePtr }
) )
private val BlockPointerBridge.nameSuffix: String private val BlockPointerBridge.nameSuffix: String
@@ -268,11 +266,8 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
val invokeMethod = context.ir.symbols.functionN(numberOfParameters).owner.simpleFunctions() val invokeMethod = context.ir.symbols.functionN(numberOfParameters).owner.simpleFunctions()
.single { it.name == OperatorNameConventions.INVOKE } .single { it.name == OperatorNameConventions.INVOKE }
val llvmDeclarations = lookupVirtualImpl(kotlinFunction, invokeMethod)
val callee = lookupVirtualImpl(kotlinFunction, invokeMethod) val result = callFromBridge(llvmDeclarations, listOf(kotlinFunction) + kotlinArguments, Lifetime.ARGUMENT)
val result = callFromBridge(callee, listOf(kotlinFunction) + kotlinArguments, Lifetime.ARGUMENT)
if (bridge.returnsVoid) { if (bridge.returnsVoid) {
ret(null) ret(null)
} else { } else {
@@ -293,7 +288,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
) )
return functionGenerator( return functionGenerator(
functionType(int8TypePtr, false, codegen.kObjHeaderPtr), LlvmFunctionSignature(LlvmRetType(int8TypePtr), listOf(LlvmParamType(codegen.kObjHeaderPtr))),
convertName convertName
).generate { ).generate {
val kotlinRef = param(0) val kotlinRef = param(0)
@@ -335,8 +330,13 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
} }
} }
private val ObjCExportCodeGeneratorBase.retainBlock get() = context.llvm.externalFunction( private val ObjCExportCodeGeneratorBase.retainBlock: LlvmCallable
"objc_retainBlock", get() {
functionType(int8TypePtr, false, int8TypePtr), val functionProto = LlvmFunctionProto(
CurrentKlibModuleOrigin "objc_retainBlock",
) LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
origin = CurrentKlibModuleOrigin
)
return context.llvm.externalFunction(functionProto)
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.konan.llvm.objcexport package org.jetbrains.kotlin.backend.konan.llvm.objcexport
import kotlinx.cinterop.toKString
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.common.ir.allParameters import org.jetbrains.kotlin.backend.common.ir.allParameters
import org.jetbrains.kotlin.backend.common.ir.allParametersCount import org.jetbrains.kotlin.backend.common.ir.allParametersCount
@@ -86,19 +87,24 @@ internal class ObjCExportFunctionGenerationContext(
} }
internal class ObjCExportFunctionGenerationContextBuilder( internal class ObjCExportFunctionGenerationContextBuilder(
functionType: LLVMTypeRef, functionType: LlvmFunctionSignature,
functionName: String, functionName: String,
val objCExportCodegen: ObjCExportCodeGeneratorBase val objCExportCodegen: ObjCExportCodeGeneratorBase
) : FunctionGenerationContextBuilder<ObjCExportFunctionGenerationContext>( ) : FunctionGenerationContextBuilder<ObjCExportFunctionGenerationContext>(
functionType, functionType.llvmFunctionType,
functionName, functionName,
objCExportCodegen.codegen objCExportCodegen.codegen
) { ) {
// Not a very pleasant way to provide attributes to the generated function.
init {
functionType.addFunctionAttributes(function)
}
override fun build() = ObjCExportFunctionGenerationContext(this) override fun build() = ObjCExportFunctionGenerationContext(this)
} }
internal inline fun ObjCExportCodeGeneratorBase.functionGenerator( internal inline fun ObjCExportCodeGeneratorBase.functionGenerator(
functionType: LLVMTypeRef, functionType: LlvmFunctionSignature,
functionName: String, functionName: String,
configure: ObjCExportFunctionGenerationContextBuilder.() -> Unit = {} configure: ObjCExportFunctionGenerationContextBuilder.() -> Unit = {}
): ObjCExportFunctionGenerationContextBuilder = ObjCExportFunctionGenerationContextBuilder( ): ObjCExportFunctionGenerationContextBuilder = ObjCExportFunctionGenerationContextBuilder(
@@ -114,29 +120,42 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
val rttiGenerator = RTTIGenerator(context) val rttiGenerator = RTTIGenerator(context)
private val objcTerminate: LLVMValueRef by lazy { private val objcTerminate: LlvmCallable by lazy {
context.llvm.externalFunction( context.llvm.externalFunction(LlvmFunctionProto(
"objc_terminate", "objc_terminate",
functionType(voidType, false), LlvmRetType(voidType),
CurrentKlibModuleOrigin functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
).also { origin = CurrentKlibModuleOrigin
setFunctionNoUnwind(it) ))
}
} }
fun dispose() { fun dispose() {
rttiGenerator.dispose() rttiGenerator.dispose()
} }
fun FunctionGenerationContext.callFromBridge(
llvmFunction: LLVMValueRef,
args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
toNative: Boolean = false,
): LLVMValueRef {
val llvmDeclarations = LlvmCallable(
llvmFunction,
// llvmFunction could be a function pointer here, and we can't infer attributes from it.
LlvmFunctionAttributeProvider.makeEmpty()
)
return callFromBridge(llvmDeclarations, args, resultLifetime, toNative)
}
// TODO: currently bridges don't have any custom `landingpad`s, // TODO: currently bridges don't have any custom `landingpad`s,
// so it is correct to use [callAtFunctionScope] here. // so it is correct to use [callAtFunctionScope] here.
// However, exception handling probably should be refactored // However, exception handling probably should be refactored
// (e.g. moved from `IrToBitcode.kt` to [FunctionGenerationContext]). // (e.g. moved from `IrToBitcode.kt` to [FunctionGenerationContext]).
fun FunctionGenerationContext.callFromBridge( fun FunctionGenerationContext.callFromBridge(
function: LLVMValueRef, function: LlvmCallable,
args: List<LLVMValueRef>, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT, resultLifetime: Lifetime = Lifetime.IRRELEVANT,
toNative: Boolean = false toNative: Boolean = false,
): LLVMValueRef { ): LLVMValueRef {
// TODO: it is required only for Kotlin-to-Objective-C bridges. // TODO: it is required only for Kotlin-to-Objective-C bridges.
@@ -220,19 +239,18 @@ internal class ObjCExportCodeGenerator(
} }
fun FunctionGenerationContext.genSendMessage( fun FunctionGenerationContext.genSendMessage(
returnType: LLVMTypeRef, returnType: LlvmParamType,
parameterTypes: List<LlvmParamType>,
receiver: LLVMValueRef, receiver: LLVMValueRef,
selector: String, selector: String,
switchToNative: Boolean, switchToNative: Boolean,
vararg args: LLVMValueRef, vararg args: LLVMValueRef,
): LLVMValueRef { ): LLVMValueRef {
val objcMsgSendType = functionType( val objcMsgSendType = LlvmFunctionSignature(
returnType, returnType,
false, listOf(LlvmParamType(int8TypePtr), LlvmParamType(int8TypePtr)) + parameterTypes
listOf(int8TypePtr, int8TypePtr) + args.map { it.type }
) )
return callFromBridge(msgSender(objcMsgSendType), listOf(receiver, genSelector(selector)) + args, toNative = switchToNative) return callFromBridge(msgSender(objcMsgSendType), listOf(receiver, genSelector(selector)) + args, toNative = switchToNative)
} }
@@ -662,7 +680,11 @@ private fun ObjCExportCodeGeneratorBase.buildWritableTypeInfoValue(
typeAdapter: ConstPointer? = null typeAdapter: ConstPointer? = null
): Struct { ): Struct {
if (convertToRetained != null) { if (convertToRetained != null) {
assert(convertToRetained.llvmType == pointerType(functionType(int8TypePtr, false, codegen.kObjHeaderPtr))) val expectedType = pointerType(functionType(int8TypePtr, false, codegen.kObjHeaderPtr))
assert(convertToRetained.llvmType == expectedType) {
"Expected: ${LLVMPrintTypeToString(expectedType)!!.toKString()} " +
"found: ${LLVMPrintTypeToString(convertToRetained.llvmType)!!.toKString()}"
}
} }
val objCExportAddition = Struct(runtime.typeInfoObjCExportAddition, val objCExportAddition = Struct(runtime.typeInfoObjCExportAddition,
@@ -675,8 +697,8 @@ private fun ObjCExportCodeGeneratorBase.buildWritableTypeInfoValue(
return Struct(writableTypeInfoType, objCExportAddition) return Struct(writableTypeInfoType, objCExportAddition)
} }
private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LlvmFunctionSignature
get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr) get() = LlvmFunctionSignature(LlvmRetType(int8TypePtr), listOf(LlvmParamType(codegen.kObjHeaderPtr)), isVararg = false)
private val ObjCExportCodeGeneratorBase.objCToKotlinFunctionType: LLVMTypeRef private val ObjCExportCodeGeneratorBase.objCToKotlinFunctionType: LLVMTypeRef
get() = functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr) get() = functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr)
@@ -714,11 +736,14 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
) )
val value = kotlinToObjC(kotlinValue, objCValueType) val value = kotlinToObjC(kotlinValue, objCValueType)
val valueParameterTypes: List<LlvmParamType> = listOf(
LlvmParamType(value.type, objCValueType.defaultParameterAttributes)
)
val nsNumberSubclass = genGetLinkedClass(namer.numberBoxName(boxClass.classId!!).binaryName) val nsNumberSubclass = genGetLinkedClass(namer.numberBoxName(boxClass.classId!!).binaryName)
val switchToNative = false // We consider these methods fast enough. val switchToNative = false // We consider these methods fast enough.
val instance = callFromBridge(objcAlloc, listOf(nsNumberSubclass), toNative = switchToNative) val instance = callFromBridge(objcAlloc, listOf(nsNumberSubclass), toNative = switchToNative)
ret(genSendMessage(int8TypePtr, instance, nsNumberInitSelector, switchToNative, value)) val returnType = LlvmRetType(int8TypePtr)
ret(genSendMessage(returnType, valueParameterTypes, instance, nsNumberInitSelector, switchToNative, value))
} }
LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage) LLVMSetLinkage(converter, LLVMLinkage.LLVMPrivateLinkage)
@@ -782,7 +807,7 @@ private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() {
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
setObjCExportTypeInfo( setObjCExportTypeInfo(
symbols.string.owner, symbols.string.owner,
constPointer(context.llvm.Kotlin_ObjCExport_CreateRetainedNSStringFromKString) constPointer(context.llvm.Kotlin_ObjCExport_CreateRetainedNSStringFromKString.llvmValue)
) )
emitCollectionConverters() emitCollectionConverters()
@@ -792,11 +817,11 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
private fun ObjCExportCodeGenerator.emitCollectionConverters() { private fun ObjCExportCodeGenerator.emitCollectionConverters() {
fun importConverter(name: String): ConstPointer = constPointer(context.llvm.externalFunction( fun importConverter(name: String): ConstPointer = constPointer(context.llvm.externalFunction(LlvmFunctionProto(
name, name,
kotlinToObjCFunctionType, kotlinToObjCFunctionType,
CurrentKlibModuleOrigin origin = CurrentKlibModuleOrigin
)) )).llvmValue)
setObjCExportTypeInfo( setObjCExportTypeInfo(
symbols.list.owner, symbols.list.owner,
@@ -840,7 +865,8 @@ private inline fun ObjCExportCodeGenerator.generateObjCImpBy(
debugInfo: Boolean = false, debugInfo: Boolean = false,
genBody: ObjCExportFunctionGenerationContext.() -> Unit genBody: ObjCExportFunctionGenerationContext.() -> Unit
): LLVMValueRef { ): LLVMValueRef {
val result = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") { val functionType = objCFunctionType(context, methodBridge)
val result = functionGenerator(functionType, "objc2kotlin") {
if (debugInfo) { if (debugInfo) {
this.setupBridgeDebugInfo() this.setupBridgeDebugInfo()
} }
@@ -882,13 +908,12 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
listOf(param(0), codegen.typeInfoValue(target.parent as IrClass)) listOf(param(0), codegen.typeInfoValue(target.parent as IrClass))
) )
} }
val llvmTarget = if (!isVirtual) { val llvmCallable = if (!isVirtual) {
codegen.llvmFunction(target) codegen.llvmFunction(target)
} else { } else {
lookupVirtualImpl(args.first(), target) lookupVirtualImpl(args.first(), target)
} }
call(llvmCallable, args, resultLifetime, exceptionHandler)
call(llvmTarget, args, resultLifetime, exceptionHandler)
} }
} }
@@ -967,7 +992,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
// Release init receiver, as required by convention. // Release init receiver, as required by convention.
callFromBridge(objcRelease, listOf(param(0)), toNative = true) callFromBridge(objcRelease, listOf(param(0)), toNative = true)
} }
Zero(returnType.objCType(context)).llvm Zero(returnType.toLlvmRetType(context).llvmType).llvm
} }
} }
@@ -1104,7 +1129,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
val objcMsgSend = msgSender(objCFunctionType(context, methodBridge)) val objcMsgSend = msgSender(objCFunctionType(context, methodBridge))
val functionType = codegen.getLlvmFunctionType(irFunction) val functionType = LlvmFunctionSignature(irFunction, codegen)
val result = functionGenerator(functionType, "kotlin2objc").generate { val result = functionGenerator(functionType, "kotlin2objc").generate {
var errorOutPtr: LLVMValueRef? = null var errorOutPtr: LLVMValueRef? = null
@@ -1614,7 +1639,8 @@ private inline fun ObjCExportCodeGenerator.generateObjCToKotlinSyntheticGetter(
MethodBridgeReceiver.Static, valueParameters = emptyList() MethodBridgeReceiver.Static, valueParameters = emptyList()
) )
val imp = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") { val functionType = objCFunctionType(context, methodBridge)
val imp = functionGenerator(functionType, "objc2kotlin") {
switchToRunnable = true switchToRunnable = true
}.generate { }.generate {
block() block()
@@ -1700,12 +1726,10 @@ private fun ObjCExportCodeGenerator.createThrowableAsErrorAdapter(): ObjCExportC
return objCToKotlinMethodAdapter(selector, methodBridge, imp) return objCToKotlinMethodAdapter(selector, methodBridge, imp)
} }
private fun objCFunctionType(context: Context, methodBridge: MethodBridge): LLVMTypeRef { private fun objCFunctionType(context: Context, methodBridge: MethodBridge): LlvmFunctionSignature {
val paramTypes = methodBridge.paramBridges.map { it.objCType } val paramTypes = methodBridge.paramBridges.map { it.toLlvmParamType() }
val returnType = methodBridge.returnBridge.toLlvmRetType(context)
val returnType = methodBridge.returnBridge.objCType(context) return LlvmFunctionSignature(returnType, paramTypes, isVararg = false)
return functionType(returnType, false, *(paramTypes.toTypedArray()))
} }
private val ObjCValueType.llvmType: LLVMTypeRef get() = when (this) { private val ObjCValueType.llvmType: LLVMTypeRef get() = when (this) {
@@ -1724,31 +1748,31 @@ private val ObjCValueType.llvmType: LLVMTypeRef get() = when (this) {
ObjCValueType.POINTER -> kInt8Ptr ObjCValueType.POINTER -> kInt8Ptr
} }
private val MethodBridgeParameter.objCType: LLVMTypeRef get() = when (this) { private fun MethodBridgeParameter.toLlvmParamType(): LlvmParamType = when (this) {
is MethodBridgeValueParameter.Mapped -> this.bridge.objCType is MethodBridgeValueParameter.Mapped -> this.bridge.toLlvmParamType()
is MethodBridgeReceiver -> ReferenceBridge.objCType is MethodBridgeReceiver -> ReferenceBridge.toLlvmParamType()
MethodBridgeSelector -> int8TypePtr MethodBridgeSelector -> LlvmParamType(int8TypePtr)
MethodBridgeValueParameter.ErrorOutParameter -> pointerType(ReferenceBridge.objCType) MethodBridgeValueParameter.ErrorOutParameter -> LlvmParamType(pointerType(ReferenceBridge.toLlvmParamType().llvmType))
MethodBridgeValueParameter.SuspendCompletion -> int8TypePtr MethodBridgeValueParameter.SuspendCompletion -> LlvmParamType(int8TypePtr)
} }
private fun MethodBridge.ReturnValue.objCType(context: Context): LLVMTypeRef { private fun MethodBridge.ReturnValue.toLlvmRetType(
return when (this) { context: Context
MethodBridge.ReturnValue.Suspend, ): LlvmRetType = when (this) {
MethodBridge.ReturnValue.Void -> voidType MethodBridge.ReturnValue.Suspend,
MethodBridge.ReturnValue.HashCode -> if (context.is64BitNSInteger()) int64Type else int32Type MethodBridge.ReturnValue.Void -> LlvmRetType(voidType)
is MethodBridge.ReturnValue.Mapped -> this.bridge.objCType MethodBridge.ReturnValue.HashCode -> LlvmRetType(if (context.is64BitNSInteger()) int64Type else int32Type)
MethodBridge.ReturnValue.WithError.Success -> ObjCValueType.BOOL.llvmType is MethodBridge.ReturnValue.Mapped -> this.bridge.toLlvmParamType()
MethodBridge.ReturnValue.WithError.Success -> ValueTypeBridge(ObjCValueType.BOOL).toLlvmParamType()
MethodBridge.ReturnValue.Instance.InitResult, MethodBridge.ReturnValue.Instance.InitResult,
MethodBridge.ReturnValue.Instance.FactoryResult -> ReferenceBridge.objCType MethodBridge.ReturnValue.Instance.FactoryResult -> ReferenceBridge.toLlvmParamType()
is MethodBridge.ReturnValue.WithError.ZeroForError -> this.successBridge.objCType(context) is MethodBridge.ReturnValue.WithError.ZeroForError -> this.successBridge.toLlvmRetType(context)
}
} }
private val TypeBridge.objCType: LLVMTypeRef get() = when (this) { private fun TypeBridge.toLlvmParamType(): LlvmParamType = when (this) {
is ReferenceBridge, is BlockPointerBridge -> int8TypePtr is ReferenceBridge, is BlockPointerBridge -> LlvmParamType(int8TypePtr)
is ValueTypeBridge -> this.objCValueType.llvmType is ValueTypeBridge -> LlvmParamType(this.objCValueType.llvmType, this.objCValueType.defaultParameterAttributes)
} }
internal fun ObjCExportCodeGenerator.getEncoding(methodBridge: MethodBridge): String { internal fun ObjCExportCodeGenerator.getEncoding(methodBridge: MethodBridge): String {
@@ -1758,7 +1782,7 @@ internal fun ObjCExportCodeGenerator.getEncoding(methodBridge: MethodBridge): St
methodBridge.paramBridges.forEach { methodBridge.paramBridges.forEach {
append(it.objCEncoding) append(it.objCEncoding)
append(paramOffset) append(paramOffset)
paramOffset += LLVMStoreSizeOfType(runtime.targetData, it.objCType).toInt() paramOffset += LLVMStoreSizeOfType(runtime.targetData, it.toLlvmParamType().llvmType).toInt()
} }
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.konan.objcexport package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.llvm.LlvmParameterAttribute
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -153,15 +154,16 @@ object ObjCVoidType : ObjCType() {
override fun render(attrsAndName: String) = "void".withAttrsAndName(attrsAndName) override fun render(attrsAndName: String) = "void".withAttrsAndName(attrsAndName)
} }
internal enum class ObjCValueType(val encoding: String) { internal enum class ObjCValueType(val encoding: String, val defaultParameterAttributes: List<LlvmParameterAttribute> = emptyList()) {
BOOL("c"), BOOL("c", listOf(LlvmParameterAttribute.SignExt)),
UNICHAR("S"), UNICHAR("S", listOf(LlvmParameterAttribute.ZeroExt)),
CHAR("c"), // TODO: Switch to explicit SIGNED_CHAR
SHORT("s"), CHAR("c", listOf(LlvmParameterAttribute.SignExt)),
SHORT("s", listOf(LlvmParameterAttribute.SignExt)),
INT("i"), INT("i"),
LONG_LONG("q"), LONG_LONG("q"),
UNSIGNED_CHAR("C"), UNSIGNED_CHAR("C", listOf(LlvmParameterAttribute.ZeroExt)),
UNSIGNED_SHORT("S"), UNSIGNED_SHORT("S", listOf(LlvmParameterAttribute.ZeroExt)),
UNSIGNED_INT("I"), UNSIGNED_INT("I"),
UNSIGNED_LONG_LONG("Q"), UNSIGNED_LONG_LONG("Q"),
FLOAT("f"), FLOAT("f"),