[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 irFunction = irSymbol.owner as IrFunction
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.
val bridge = if (!DescriptorUtils.isTopLevelDeclaration(function) &&
irFunction.isOverridable) {
// We need LLVMGetElementType() as otherwise type is function pointer.
generateFunction(owner.codegen, LLVMGetElementType(llvmFunction.type)!!, cname) {
generateFunction(owner.codegen, llvmCallable.functionType, cname) {
val receiver = param(0)
val numParams = LLVMCountParams(llvmFunction)
val args = (0 .. numParams - 1).map { index -> param(index) }
val numParams = LLVMCountParams(llvmCallable.llvmValue)
val args = (0..numParams - 1).map { index -> param(index) }
val callee = lookupVirtualImpl(receiver, irFunction)
callee.attributeProvider.addFunctionAttributes(this.function)
val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true)
ret(result)
}
} else {
LLVMAddAlias(context.llvmModule, llvmFunction.type, llvmFunction, cname)!!
val aliasType = pointerType(llvmCallable.functionType)
LLVMAddAlias(context.llvmModule, aliasType, llvmCallable.llvmValue, cname)!!
}
LLVMSetLinkage(bridge, LLVMLinkage.LLVMExternalLinkage)
}
@@ -38,15 +38,30 @@ private class CallsChecker(val context: Context, goodFunctions: List<String>) {
return false
}
private fun externalFunction(name: String, type: LLVMTypeRef) =
context.llvm.externalFunction(name, type, context.stdlibModule.llvmSymbolOrigin)
private fun moduleFunction(name: String) =
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 getClass = externalFunction("object_getClass", functionType(int8TypePtr, false, int8TypePtr))
val getSuperClass = externalFunction("class_getSuperclass", functionType(int8TypePtr, false, int8TypePtr))
val getMethodImpl = context.llvm.externalFunction(LlvmFunctionProto(
"class_getMethodImplementation",
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")
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)"
calledName = null
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 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 calledPtrLlvmIfNil = LLVMConstIntToPtr(Int64(MSG_SEND_TO_NULL).llvm, int8TypePtr)
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 superClassPtrPtr = LLVMBuildGEP(builder, superStruct, listOf(Int32(0).llvm, Int32(1).llvm).toCValues(), 2, "")
val superClassPtr = LLVMBuildLoad(builder, superClassPtrPtr, "")
val classPtr = LLVMBuildCall(builder, getSuperClass, listOf(superClassPtr).toCValues(), 1, "")
val calledPtrLlvmFunPtr = LLVMBuildCall(builder, getMethodImpl, listOf(classPtr, LLVMGetArgOperand(call, 1)).toCValues(), 2, "")
val classPtr = LLVMBuildCall(builder, getSuperClass.llvmValue, listOf(superClassPtr).toCValues(), 1, "")
val calledPtrLlvmFunPtr = LLVMBuildCall(builder, getMethodImpl.llvmValue, listOf(classPtr, LLVMGetArgOperand(call, 1)).toCValues(), 2, "")
calledPtrLlvm = LLVMBuildBitCast(builder, calledPtrLlvmFunPtr, int8TypePtr, "")
}
else -> {
@@ -52,6 +52,12 @@ internal inline fun <R> KotlinType.unwrapToPrimitiveOrReference(
ifReference: (type: KotlinType) -> R
): 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`.
fun KotlinType.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
}
private object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() {
internal object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() {
override fun isNullable(type: IrType): Boolean = type.containsNull()
@@ -129,19 +129,11 @@ private fun String.replaceSpecialSymbols() =
fun IrDeclaration.isExported() = KonanBinaryInterface.isExported(this)
// TODO: bring here dependencies of this method?
internal fun RuntimeAware.getLlvmFunctionType(function: IrFunction): LLVMTypeRef {
val returnType = when {
function is IrConstructor -> voidType
function.isSuspend -> kObjHeaderPtr // Suspend functions return Any?.
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 fun RuntimeAware.getLlvmFunctionType(function: IrFunction): LLVMTypeRef = functionType(
returnType = getLlvmFunctionReturnType(function).llvmType,
isVarArg = false,
paramTypes = getLlvmFunctionParameterTypes(function).map { it.llvmType }
)
internal val IrClass.typeInfoHasVtableAttached: Boolean
get() = !this.isAbstract() && !this.isExternalObjCClass()
@@ -48,9 +48,13 @@ internal fun IrClass.hasConstStateAndNoSideEffects(context: Context): Boolean {
}
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)!!
internal val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 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 {
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 functionHash(function: IrFunction): LLVMValueRef = function.computeFunctionName().localHash.llvm
@@ -128,7 +132,7 @@ internal inline fun generateFunction(
endLocation: LocationInfo?,
code: FunctionGenerationContext.() -> Unit
) {
val llvmFunction = codegen.llvmFunction(function)
val llvmFunction = codegen.llvmFunction(function).llvmValue
val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE
@@ -473,15 +477,21 @@ internal abstract class FunctionGenerationContext(
val stackLocalsManager = StackLocalsManagerImpl(this, stackLocalsInitBb)
data class FunctionInvokeInformation(val invokeInstruction: LLVMValueRef, val llvmFunction: LLVMValueRef, val rargs: CValues<CPointerVar<LLVMOpaqueValue>>,
val argsNumber: Int, val success: LLVMBasicBlockRef)
data class FunctionInvokeInformation(
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>()
/**
* 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,
// for example.
@@ -647,10 +657,18 @@ internal abstract class FunctionGenerationContext(
Int32(size).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>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler = ExceptionHandler.None,
verbatim: Boolean = false): LLVMValueRef {
verbatim: Boolean = false,
attributeProvider: LlvmFunctionAttributeProvider? = null
): LLVMValueRef {
val callArgs = if (verbatim || !isObjectReturn(llvmFunction.type)) {
args
} else {
@@ -677,15 +695,18 @@ internal abstract class FunctionGenerationContext(
}
args + resultSlot
}
return callRaw(llvmFunction, callArgs, exceptionHandler)
return callRaw(llvmFunction, callArgs, exceptionHandler, attributeProvider)
}
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
exceptionHandler: ExceptionHandler): LLVMValueRef {
exceptionHandler: ExceptionHandler,
attributeProvider: LlvmFunctionAttributeProvider?): LLVMValueRef {
val rargs = args.toCValues()
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
isFunctionNoUnwind(llvmFunction)) {
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!.also {
attributeProvider?.addCallSiteAttributes(it)
}
} else {
val unwind = when (exceptionHandler) {
ExceptionHandler.Caller -> cleanupLandingpad
@@ -706,11 +727,12 @@ internal abstract class FunctionGenerationContext(
val endLocation = position?.end
val success = basicBlock("call_success", endLocation)
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, unwind, "")!!
attributeProvider?.addCallSiteAttributes(result)
// Store invoke instruction and its success block in reverse order.
// Reverse order allows save arguments valid during all work with invokes
// because other invokes processed before can be inside arguments list.
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)
return result
@@ -867,7 +889,7 @@ internal abstract class FunctionGenerationContext(
LLVMBuildExtractValue(builder, aggregate, index, name)!!
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):
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)
val typeInfoPtr: LLVMValueRef = if (irFunction.getObjCMethodInfo() != null)
@@ -1170,7 +1192,10 @@ internal abstract class FunctionGenerationContext(
}
}
val functionPtrType = pointerType(codegen.getLlvmFunctionType(irFunction))
return bitcast(functionPtrType, llvmMethod)
return LlvmCallable(
bitcast(functionPtrType, llvmMethod),
LlvmFunctionSignature(irFunction, this)
)
}
private fun IrSimpleFunction.findOverriddenMethodOfAny(): IrSimpleFunction? {
@@ -1268,7 +1293,7 @@ internal abstract class FunctionGenerationContext(
positionAtEnd(bbInit)
val typeInfo = codegen.typeInfoForAllocation(irClass)
val defaultConstructor = irClass.constructors.single { it.valueParameters.size == 0 }
val ctor = codegen.llvmFunction(defaultConstructor)
val ctor = codegen.llvmFunction(defaultConstructor).llvmValue
val initFunction =
if (storageKind == ObjectStorageKind.SHARED && context.config.threadsAreAllowed) {
context.llvm.initSingletonFunction
@@ -1296,14 +1321,14 @@ internal abstract class FunctionGenerationContext(
val getterId = loweredEnum.entriesMap[enumEntry.name]!!.getterId
val values = call(
loweredEnum.valuesGetter.llvmFunction,
loweredEnum.valuesGetter.llvmFunction.llvmValue,
emptyList(),
Lifetime.ARGUMENT,
exceptionHandler
)
return call(
loweredEnum.itemGetterSymbol.owner.llvmFunction,
loweredEnum.itemGetterSymbol.owner.llvmFunction.llvmValue,
listOf(values, Int32(getterId).llvm),
Lifetime.GLOBAL,
exceptionHandler
@@ -1321,12 +1346,12 @@ internal abstract class FunctionGenerationContext(
val name = irClass.descriptor.getExternalObjCMetaClassBinaryName()
val objCClass = getObjCClass(name, llvmSymbolOrigin)
val getClass = context.llvm.externalFunction(
val getClass = context.llvm.externalFunction(LlvmFunctionProto(
"object_getClass",
functionType(int8TypePtr, false, int8TypePtr),
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
origin = context.standardLlvmSymbolsOrigin
)
))
call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler)
} else {
getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin)
@@ -1490,7 +1515,8 @@ internal abstract class FunctionGenerationContext(
invokeInstructions.forEach { functionInvokeInfo ->
positionBefore(functionInvokeInfo.invokeInstruction)
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.
br(functionInvokeInfo.success)
LLVMReplaceAllUsesWith(functionInvokeInfo.invokeInstruction, newResult)
@@ -1531,7 +1557,7 @@ internal abstract class FunctionGenerationContext(
handleEpilogueForExperimentalMM(context.llvm.Kotlin_mm_safePointFunctionEpilogue)
}
private fun handleEpilogueForExperimentalMM(safePointFunction: LLVMValueRef) {
private fun handleEpilogueForExperimentalMM(safePointFunction: LlvmCallable) {
if (context.memoryModel == MemoryModel.EXPERIMENTAL) {
if (!forbidRuntime) {
call(safePointFunction, emptyList())
@@ -5,9 +5,6 @@
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import llvm.*
import org.jetbrains.kotlin.backend.konan.CachedLibraries
@@ -170,19 +167,22 @@ internal interface ContextUtils : RuntimeAware {
* LLVM function generated from the Kotlin function.
* It may be declared as external function prototype.
*/
val IrFunction.llvmFunction: LLVMValueRef
val IrFunction.llvmFunction: LlvmCallable
get() = llvmFunctionOrNull
?: error("$name in ${file.name}/${parent.fqNameForIrSerialization}")
val IrFunction.llvmFunctionOrNull: LLVMValueRef?
val IrFunction.llvmFunctionOrNull: LlvmCallable?
get() {
assert(this.isReal)
assert(this.isReal) {
this.computeFullName()
}
return if (isExternal(this)) {
runtime.addedLLVMExternalFunctions.getOrPut(this) { context.llvm.externalFunction(this.computeSymbolName(), getLlvmFunctionType(this),
origin = this.llvmSymbolOrigin) }
runtime.addedLLVMExternalFunctions.getOrPut(this) {
val proto = LlvmFunctionProto(this, this.computeSymbolName(), this@ContextUtils)
context.llvm.externalFunction(proto)
}
} else {
context.llvmDeclarations.forFunctionOrNull(this)?.llvmFunction
context.llvmDeclarations.forFunctionOrNull(this)
}
}
@@ -191,7 +191,7 @@ internal interface ContextUtils : RuntimeAware {
*/
val IrFunction.entryPointAddress: ConstPointer
get() {
val result = LLVMConstBitCast(this.llvmFunction, int8TypePtr)!!
val result = LLVMConstBitCast(this.llvmFunction.llvmValue, int8TypePtr)!!
return constPointer(result)
}
@@ -256,19 +256,21 @@ internal class InitializersGenerationState {
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) {
throw IllegalArgumentException("function $name already exists")
}
val externalFunction = LLVMGetNamedFunction(otherModule, name) ?: throw Error("function $name not found")
val attributesCopier = LlvmFunctionAttributeProvider.copyFromExternal(externalFunction)
val functionType = getFunctionType(externalFunction)
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 {
@@ -283,74 +285,37 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
return global
}
private fun copyFunctionAttributes(source: LLVMValueRef, destination: LLVMValueRef) {
// 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 {
private fun importMemset(): LlvmCallable {
val functionType = functionType(voidType, false, int8TypePtr, int8Type, int32Type, int1Type)
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)!!
attributes.forEach {
val kindId = getLlvmAttributeKindId(it)
addLlvmFunctionEnumAttribute(result, kindId)
}
return result
return LlvmCallable(result, LlvmFunctionAttributeProvider.copyFromExternal(result))
}
internal fun externalFunction(
name: String,
type: LLVMTypeRef,
origin: CompiledKlibModuleOrigin,
independent: Boolean = false
): LLVMValueRef {
this.imports.add(origin, onlyBitcode = independent)
val found = LLVMGetNamedFunction(llvmModule, name)
internal fun externalFunction(llvmFunctionProto: LlvmFunctionProto): LlvmCallable {
this.imports.add(llvmFunctionProto.origin, onlyBitcode = llvmFunctionProto.independent)
val found = LLVMGetNamedFunction(llvmModule, llvmFunctionProto.name)
if (found != null) {
assert(getFunctionType(found) == type) {
"Expected: ${LLVMPrintTypeToString(type)!!.toKString()} " +
assert(getFunctionType(found) == llvmFunctionProto.llvmFunctionType) {
"Expected: ${LLVMPrintTypeToString(llvmFunctionProto.llvmFunctionType)!!.toKString()} " +
"found: ${LLVMPrintTypeToString(getFunctionType(found))!!.toKString()}"
}
assert(LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
return found
return LlvmCallable(found, llvmFunctionProto)
} else {
// As exported functions are written in C++ they assume sign extension for promoted types -
// mention that in attributes.
val function = addLlvmFunctionWithDefaultAttributes(context, llvmModule, name, type)
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
}
val function = addLlvmFunctionWithDefaultAttributes(context, llvmModule, llvmFunctionProto.name, llvmFunctionProto.llvmFunctionType)
llvmFunctionProto.addFunctionAttributes(function)
return LlvmCallable(function, llvmFunctionProto)
}
}
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
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"
}
val cxxStdTerminate = externalNounwindFunction(
val cxxStdTerminate = externalFunction(LlvmFunctionProto(
"_ZSt9terminatev", // mangled C++ 'std::terminate'
functionType(voidType, false),
returnType = LlvmRetType(voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
origin = context.standardLlvmSymbolsOrigin
)
))
val gxxPersonalityFunction = externalNounwindFunction(
val gxxPersonalityFunction = externalFunction(LlvmFunctionProto(
personalityFunctionName,
functionType(int32Type, true),
returnType = LlvmRetType(int32Type),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
isVararg = true,
origin = context.standardLlvmSymbolsOrigin
)
val cxaBeginCatchFunction = externalNounwindFunction(
))
val cxaBeginCatchFunction = externalFunction(LlvmFunctionProto(
"__cxa_begin_catch",
functionType(int8TypePtr, false, int8TypePtr),
returnType = LlvmRetType(int8TypePtr),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
parameterTypes = listOf(LlvmParamType(int8TypePtr)),
origin = context.standardLlvmSymbolsOrigin
)
val cxaEndCatchFunction = externalNounwindFunction(
))
val cxaEndCatchFunction = externalFunction(LlvmFunctionProto(
"__cxa_end_catch",
functionType(voidType, false),
returnType = LlvmRetType(voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
origin = context.standardLlvmSymbolsOrigin
)
))
val memsetFunction = importMemset()
//val memcpyFunction = importMemcpy()
@@ -612,11 +585,11 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
private object lazyRtFunction {
operator fun provideDelegate(
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.
*/
val nsIntegerTypeWidth: Long by lazy {
getSizeOfReturnTypeInBits(Kotlin_ObjCExport_NSIntegerTypeProvider)
getSizeOfReturnTypeInBits(Kotlin_ObjCExport_NSIntegerTypeProvider.llvmValue)
}
/**
* Width of C long type in bits.
*/
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 {
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 normalMessenger = context.llvm.externalFunction(
val normalMessenger = context.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSend$messengerNameSuffix",
functionType,
functionReturnType,
functionParameterTypes,
isVararg = true,
origin = libobjc
)
val superMessenger = context.llvm.externalFunction(
))
val superMessenger = context.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSendSuper$messengerNameSuffix",
functionType,
functionReturnType,
functionParameterTypes,
isVararg = true,
origin = libobjc
)
))
val superClass = args.single()
val messenger = LLVMBuildSelect(builder,
If = icmpEq(superClass, kNullInt8Ptr),
Then = normalMessenger,
Else = superMessenger,
Then = normalMessenger.llvmValue,
Else = superMessenger.llvmValue,
Name = ""
)!!
@@ -712,7 +712,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val llvmFunction: LLVMValueRef) : InnerScopeImpl() {
constructor(declaration: IrFunction, functionGenerationContext: FunctionGenerationContext) :
this(functionGenerationContext, declaration, codegen.llvmFunction(declaration))
this(functionGenerationContext, declaration, codegen.llvmFunction(declaration).llvmValue)
constructor(llvmFunction: LLVMValueRef, functionGenerationContext: FunctionGenerationContext) :
this(functionGenerationContext, null, llvmFunction)
@@ -857,7 +857,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
if (declaration.retainAnnotation(context.config.target)) {
context.llvm.usedFunctions.add(codegen.llvmFunction(declaration))
context.llvm.usedFunctions.add(codegen.llvmFunction(declaration).llvmValue)
}
if (context.shouldVerifyBitCode())
@@ -1645,12 +1645,13 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
} else {
// e.g. ObjCObject, ObjCObjectBase etc.
if (dstClass.isObjCMetaClass()) {
val isClass = context.llvm.externalFunction(
val isClassProto = LlvmFunctionProto(
"object_isClass",
functionType(int8Type, false, int8TypePtr),
context.standardLlvmSymbolsOrigin
LlvmRetType(int8Type),
listOf(LlvmParamType(int8TypePtr)),
origin = context.standardLlvmSymbolsOrigin
)
val isClass = context.llvm.externalFunction(isClassProto)
call(isClass, listOf(objCObject)).let {
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))
null
else
codegen.llvmFunctionOrNull(this)
codegen.llvmFunctionOrNull(this)?.llvmValue
return if (!isReifiedInline && functionLlvmValue != null) {
context.debugInfo.subprograms.getOrPut(functionLlvmValue) {
memScoped {
val subroutineType = subroutineType(context, codegen.llvmTargetData)
val llvmFunnction = codegen.llvmFunction(this@scope)
diFunctionScope(name.asString(), llvmFunnction.name!!, startLine, subroutineType).also {
val llvmFunction = codegen.llvmFunction(this@scope).llvmValue
diFunctionScope(name.asString(), llvmFunction.name!!, startLine, subroutineType).also {
if (!this@scope.isInline)
DIFunctionAddSubprogram(llvmFunnction, it)
DIFunctionAddSubprogram(llvmFunction, it)
}
}
} as DIScopeOpaqueRef
@@ -2374,7 +2375,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private fun evaluateFileGlobalInitializerCall(fileInitializer: IrFunction) = with(functionGenerationContext) {
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 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 localState = getThreadLocalInitStateFor(fileInitializer.parent as IrFile)
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 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) {
val state = getThreadLocalInitStateFor(fileInitializer.parent as IrFile)
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 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 protocolGetterName = annotation.getAnnotationStringValue("protocolGetter")
val protocolGetter = context.llvm.externalFunction(
val protocolGetterProto = LlvmFunctionProto(
protocolGetterName,
functionType(int8TypePtr, false),
irClass.llvmSymbolOrigin,
LlvmRetType(int8TypePtr),
origin = irClass.llvmSymbolOrigin,
independent = true // Protocol is header-only declaration.
)
val protocolGetter = context.llvm.externalFunction(protocolGetterProto)
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>,
resultLifetime: Lifetime): LLVMValueRef {
val llvmFunction = codegen.llvmFunction(function.target)
return call(function, llvmFunction, args, resultLifetime)
fun callDirect(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef {
val functionDeclarations = codegen.llvmFunction(function.target)
return call(function, functionDeclarations, args, resultLifetime)
}
//-------------------------------------------------------------------------//
fun callVirtual(function: IrFunction, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
val llvmFunction = functionGenerationContext.lookupVirtualImpl(args.first(), function)
return call(function, llvmFunction, args, resultLifetime) // Invoke the method
fun callVirtual(function: IrFunction, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef {
val functionDeclarations = functionGenerationContext.lookupVirtualImpl(args.first(), function)
return call(function, functionDeclarations, args, resultLifetime)
}
//-------------------------------------------------------------------------//
@@ -2602,7 +2600,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
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 {
check(!function.isTypedIntrinsic)
@@ -2620,7 +2618,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.switchThreadState(ThreadState.Native)
}
val result = call(llvmFunction, args, resultLifetime, exceptionHandler)
val result = call(llvmCallable, args, resultLifetime, exceptionHandler)
when {
!function.isSuspend && function.returnType.isNothing() ->
@@ -2629,16 +2627,18 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.switchThreadState(ThreadState.Runnable)
}
if (LLVMGetReturnType(getFunctionType(llvmFunction)) == voidType) {
if (llvmCallable.returnType == voidType) {
return codegen.theUnitInstanceRef.llvm
}
return result
}
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler = currentCodeContext.exceptionHandler): LLVMValueRef {
private fun call(
function: LlvmCallable, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
exceptionHandler: ExceptionHandler = currentCodeContext.exceptionHandler,
): LLVMValueRef {
return functionGenerationContext.call(function, args, resultLifetime, exceptionHandler)
}
@@ -87,7 +87,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
.distinctBy { it.selector }
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(
annotation.getAnnotationStringValue("selector"),
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 {
// TODO: do we still need it?
if (!context.shouldOptimize()) {
@@ -99,3 +78,38 @@ private fun enforceFramePointer(llvmFunction: LLVMValueRef, context: Context) {
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>) {
fun forFunction(function: IrFunction) = forFunctionOrNull(function) ?: with(function){error("$name in $file/${parent.fqNameForIrSerialization}")}
fun forFunctionOrNull(function: IrFunction) = (function.metadata as? CodegenFunctionMetadata)?.llvm
fun forFunction(function: IrFunction): LlvmCallable =
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 ?:
error(irClass.descriptor.toString())
@@ -68,8 +73,6 @@ internal class KotlinObjCClassLlvmDeclarations(
val bodyOffsetGlobal: StaticData.Global
)
internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef)
internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef)
internal class StaticFieldLlvmDeclarations(val storageAddressAccess: AddressAccess)
@@ -338,8 +341,6 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
if (!declaration.isReal) return
val llvmFunctionType = getLlvmFunctionType(declaration)
if ((declaration is IrConstructor && declaration.isObjCConstructor)) {
return
}
@@ -351,11 +352,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|| (declaration.isAccessor && declaration.isFromInteropLibrary())
|| declaration.annotations.hasAnnotation(RuntimeNames.cCall)) return
context.llvm.externalFunction(declaration.computeSymbolName(), llvmFunctionType,
// Assume that `external fun` is defined in native libs attached to this module:
origin = declaration.llvmSymbolOrigin,
independent = declaration.hasAnnotation(RuntimeNames.independent)
)
val proto = LlvmFunctionProto(declaration, declaration.computeSymbolName(), this)
context.llvm.externalFunction(proto)
} else {
val symbolName = if (declaration.isExported()) {
declaration.computeSymbolName().also {
@@ -369,21 +367,22 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
} else {
"kfun:" + qualifyInternalName(declaration)
}
addLlvmFunctionWithDefaultAttributes(
val proto = LlvmFunctionProto(declaration, symbolName, this)
val llvmFunction = addLlvmFunctionWithDefaultAttributes(
context,
context.llvmModule!!,
symbolName,
llvmFunctionType
proto.llvmFunctionType
).also {
addLlvmAttributesForKotlinFunction(context, declaration, it)
proto.addFunctionAttributes(it)
}
LlvmCallable(llvmFunction, proto)
}
declaration.metadata = CodegenFunctionMetadata(
declaration.metadata?.name,
declaration.konanLibrary,
FunctionLlvmDeclarations(llvmFunction)
llvmFunction
)
}
}
@@ -400,7 +399,7 @@ internal class CodegenClassMetadata(irClass: IrClass)
private class CodegenFunctionMetadata(
name: Name?,
konanLibrary: KotlinLibrary?,
val llvm: FunctionLlvmDeclarations
val llvm: LlvmCallable
) : KonanMetadata(name, konanLibrary), MetadataSource.Function
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) {
val llvmModule: LLVMModuleRef = parseBitcodeFile(bitcodeFile)
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 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.
private fun createFunctionNameGlobal(function: IrFunction): LLVMValueRef {
val name = function.llvmFunction.name
val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction, name)!!
val name = function.llvmFunction.llvmValue.name
val pgoFunctionName = LLVMCreatePGOFunctionNameVar(function.llvmFunction.llvmValue, name)!!
return LLVMConstBitCast(pgoFunctionName, int8TypePtr)!!
}
}
@@ -54,7 +54,7 @@ internal class LLVMCoverageWriter(
fileIds.toCValues(), fileIds.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),
functionName, functionRegions.structuralHash, functionCoverage)!!
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.konan.llvm.objc
import llvm.LLVMTypeRef
import llvm.LLVMValueRef
import org.jetbrains.kotlin.backend.konan.llvm.*
@@ -22,44 +21,54 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
}
private val objcMsgSend = constPointer(
context.llvm.externalFunction(
context.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSend",
functionType(int8TypePtr, true, int8TypePtr, int8TypePtr),
context.stdlibModule.llvmSymbolOrigin
)
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr), LlvmParamType(int8TypePtr)),
isVararg = true,
origin = context.stdlibModule.llvmSymbolOrigin
)).llvmValue
)
val objcRelease = context.llvm.externalFunction(
"objc_release",
functionType(voidType, false, int8TypePtr),
context.stdlibModule.llvmSymbolOrigin
)
val objcRelease = run {
val proto = LlvmFunctionProto(
"objc_release",
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",
functionType(int8TypePtr, false, int8TypePtr),
context.stdlibModule.llvmSymbolOrigin
)
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
origin = context.stdlibModule.llvmSymbolOrigin
))
val objcAutorelease = context.llvm.externalFunction(
val objcAutorelease = context.llvm.externalFunction(LlvmFunctionProto(
"llvm.objc.autorelease",
functionType(int8TypePtr, false, int8TypePtr),
context.stdlibModule.llvmSymbolOrigin
).also {
setFunctionNoUnwind(it)
}
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
listOf(LlvmFunctionAttribute.NoUnwind),
origin = context.stdlibModule.llvmSymbolOrigin
))
val objcAutoreleaseReturnValue = context.llvm.externalFunction(
val objcAutoreleaseReturnValue = context.llvm.externalFunction(LlvmFunctionProto(
"llvm.objc.autoreleaseReturnValue",
functionType(int8TypePtr, false, int8TypePtr),
context.stdlibModule.llvmSymbolOrigin
).also {
setFunctionNoUnwind(it)
}
LlvmRetType(int8TypePtr),
listOf(LlvmParamType(int8TypePtr)),
listOf(LlvmFunctionAttribute.NoUnwind),
origin = context.stdlibModule.llvmSymbolOrigin
))
// TODO: this doesn't support stret.
fun msgSender(functionType: LLVMTypeRef): LLVMValueRef =
objcMsgSend.bitcast(pointerType(functionType)).llvm
fun msgSender(functionType: LlvmFunctionSignature): LlvmCallable =
LlvmCallable(
objcMsgSend.bitcast(pointerType(functionType.llvmFunctionType)).llvm,
functionType
)
}
internal fun FunctionGenerationContext.genObjCSelector(selector: String): LLVMValueRef {
@@ -31,7 +31,7 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
}
val invokeImpl = functionGenerator(
codegen.getLlvmFunctionType(invokeMethod),
LlvmFunctionSignature(invokeMethod, codegen),
"invokeFunction${bridge.nameSuffix}"
).generate {
val args = (0 until bridge.numberOfParameters).map { index ->
@@ -69,9 +69,8 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
bodyType,
immutable = true
)
return functionGenerator(
functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr),
LlvmFunctionSignature(LlvmRetType(codegen.kObjHeaderPtr), listOf(LlvmParamType(int8TypePtr), LlvmParamType(codegen.kObjHeaderPtrPtr))),
"convertBlock${bridge.nameSuffix}"
).generate {
val blockPtr = param(0)
@@ -108,7 +107,7 @@ private fun FunctionGenerationContext.loadBlockInvoke(
val blockLiteralType = codegen.runtime.getStructType("Block_literal_1")
val invokePtr = structGep(bitcast(pointerType(blockLiteralType), blockPtr), 3)
return bitcast(pointerType(bridge.blockType.blockInvokeLlvmType), load(invokePtr))
return bitcast(pointerType(bridge.blockType.blockInvokeLlvmType.llvmFunctionType), load(invokePtr))
}
private fun FunctionGenerationContext.allocInstanceWithAssociatedObject(
@@ -129,11 +128,10 @@ private val BlockPointerBridge.blockType: BlockType
*/
internal data class BlockType(val numberOfParameters: Int, val returnsVoid: Boolean)
private val BlockType.blockInvokeLlvmType: LLVMTypeRef
get() = functionType(
if (returnsVoid) voidType else int8TypePtr,
false,
(0..numberOfParameters).map { int8TypePtr }
private val BlockType.blockInvokeLlvmType: LlvmFunctionSignature
get() = LlvmFunctionSignature(
LlvmRetType(if (returnsVoid) voidType else int8TypePtr),
(0..numberOfParameters).map { LlvmParamType(int8TypePtr) }
)
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()
.single { it.name == OperatorNameConventions.INVOKE }
val callee = lookupVirtualImpl(kotlinFunction, invokeMethod)
val result = callFromBridge(callee, listOf(kotlinFunction) + kotlinArguments, Lifetime.ARGUMENT)
val llvmDeclarations = lookupVirtualImpl(kotlinFunction, invokeMethod)
val result = callFromBridge(llvmDeclarations, listOf(kotlinFunction) + kotlinArguments, Lifetime.ARGUMENT)
if (bridge.returnsVoid) {
ret(null)
} else {
@@ -293,7 +288,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
)
return functionGenerator(
functionType(int8TypePtr, false, codegen.kObjHeaderPtr),
LlvmFunctionSignature(LlvmRetType(int8TypePtr), listOf(LlvmParamType(codegen.kObjHeaderPtr))),
convertName
).generate {
val kotlinRef = param(0)
@@ -335,8 +330,13 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
}
}
private val ObjCExportCodeGeneratorBase.retainBlock get() = context.llvm.externalFunction(
"objc_retainBlock",
functionType(int8TypePtr, false, int8TypePtr),
CurrentKlibModuleOrigin
)
private val ObjCExportCodeGeneratorBase.retainBlock: LlvmCallable
get() {
val functionProto = LlvmFunctionProto(
"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
import kotlinx.cinterop.toKString
import llvm.*
import org.jetbrains.kotlin.backend.common.ir.allParameters
import org.jetbrains.kotlin.backend.common.ir.allParametersCount
@@ -86,19 +87,24 @@ internal class ObjCExportFunctionGenerationContext(
}
internal class ObjCExportFunctionGenerationContextBuilder(
functionType: LLVMTypeRef,
functionType: LlvmFunctionSignature,
functionName: String,
val objCExportCodegen: ObjCExportCodeGeneratorBase
) : FunctionGenerationContextBuilder<ObjCExportFunctionGenerationContext>(
functionType,
functionType.llvmFunctionType,
functionName,
objCExportCodegen.codegen
) {
// Not a very pleasant way to provide attributes to the generated function.
init {
functionType.addFunctionAttributes(function)
}
override fun build() = ObjCExportFunctionGenerationContext(this)
}
internal inline fun ObjCExportCodeGeneratorBase.functionGenerator(
functionType: LLVMTypeRef,
functionType: LlvmFunctionSignature,
functionName: String,
configure: ObjCExportFunctionGenerationContextBuilder.() -> Unit = {}
): ObjCExportFunctionGenerationContextBuilder = ObjCExportFunctionGenerationContextBuilder(
@@ -114,29 +120,42 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
val rttiGenerator = RTTIGenerator(context)
private val objcTerminate: LLVMValueRef by lazy {
context.llvm.externalFunction(
private val objcTerminate: LlvmCallable by lazy {
context.llvm.externalFunction(LlvmFunctionProto(
"objc_terminate",
functionType(voidType, false),
CurrentKlibModuleOrigin
).also {
setFunctionNoUnwind(it)
}
LlvmRetType(voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
origin = CurrentKlibModuleOrigin
))
}
fun 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,
// so it is correct to use [callAtFunctionScope] here.
// However, exception handling probably should be refactored
// (e.g. moved from `IrToBitcode.kt` to [FunctionGenerationContext]).
fun FunctionGenerationContext.callFromBridge(
function: LLVMValueRef,
function: LlvmCallable,
args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
toNative: Boolean = false
toNative: Boolean = false,
): LLVMValueRef {
// TODO: it is required only for Kotlin-to-Objective-C bridges.
@@ -220,19 +239,18 @@ internal class ObjCExportCodeGenerator(
}
fun FunctionGenerationContext.genSendMessage(
returnType: LLVMTypeRef,
returnType: LlvmParamType,
parameterTypes: List<LlvmParamType>,
receiver: LLVMValueRef,
selector: String,
switchToNative: Boolean,
vararg args: LLVMValueRef,
): LLVMValueRef {
val objcMsgSendType = functionType(
val objcMsgSendType = LlvmFunctionSignature(
returnType,
false,
listOf(int8TypePtr, int8TypePtr) + args.map { it.type }
listOf(LlvmParamType(int8TypePtr), LlvmParamType(int8TypePtr)) + parameterTypes
)
return callFromBridge(msgSender(objcMsgSendType), listOf(receiver, genSelector(selector)) + args, toNative = switchToNative)
}
@@ -662,7 +680,11 @@ private fun ObjCExportCodeGeneratorBase.buildWritableTypeInfoValue(
typeAdapter: ConstPointer? = null
): Struct {
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,
@@ -675,8 +697,8 @@ private fun ObjCExportCodeGeneratorBase.buildWritableTypeInfoValue(
return Struct(writableTypeInfoType, objCExportAddition)
}
private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef
get() = functionType(int8TypePtr, false, codegen.kObjHeaderPtr)
private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LlvmFunctionSignature
get() = LlvmFunctionSignature(LlvmRetType(int8TypePtr), listOf(LlvmParamType(codegen.kObjHeaderPtr)), isVararg = false)
private val ObjCExportCodeGeneratorBase.objCToKotlinFunctionType: LLVMTypeRef
get() = functionType(codegen.kObjHeaderPtr, false, int8TypePtr, codegen.kObjHeaderPtrPtr)
@@ -714,11 +736,14 @@ private fun ObjCExportCodeGenerator.emitBoxConverter(
)
val value = kotlinToObjC(kotlinValue, objCValueType)
val valueParameterTypes: List<LlvmParamType> = listOf(
LlvmParamType(value.type, objCValueType.defaultParameterAttributes)
)
val nsNumberSubclass = genGetLinkedClass(namer.numberBoxName(boxClass.classId!!).binaryName)
val switchToNative = false // We consider these methods fast enough.
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)
@@ -782,7 +807,7 @@ private fun ObjCExportBlockCodeGenerator.emitBlockToKotlinFunctionConverters() {
private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
setObjCExportTypeInfo(
symbols.string.owner,
constPointer(context.llvm.Kotlin_ObjCExport_CreateRetainedNSStringFromKString)
constPointer(context.llvm.Kotlin_ObjCExport_CreateRetainedNSStringFromKString.llvmValue)
)
emitCollectionConverters()
@@ -792,11 +817,11 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
private fun ObjCExportCodeGenerator.emitCollectionConverters() {
fun importConverter(name: String): ConstPointer = constPointer(context.llvm.externalFunction(
fun importConverter(name: String): ConstPointer = constPointer(context.llvm.externalFunction(LlvmFunctionProto(
name,
kotlinToObjCFunctionType,
CurrentKlibModuleOrigin
))
origin = CurrentKlibModuleOrigin
)).llvmValue)
setObjCExportTypeInfo(
symbols.list.owner,
@@ -840,7 +865,8 @@ private inline fun ObjCExportCodeGenerator.generateObjCImpBy(
debugInfo: Boolean = false,
genBody: ObjCExportFunctionGenerationContext.() -> Unit
): LLVMValueRef {
val result = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") {
val functionType = objCFunctionType(context, methodBridge)
val result = functionGenerator(functionType, "objc2kotlin") {
if (debugInfo) {
this.setupBridgeDebugInfo()
}
@@ -882,13 +908,12 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
listOf(param(0), codegen.typeInfoValue(target.parent as IrClass))
)
}
val llvmTarget = if (!isVirtual) {
val llvmCallable = if (!isVirtual) {
codegen.llvmFunction(target)
} else {
lookupVirtualImpl(args.first(), target)
}
call(llvmTarget, args, resultLifetime, exceptionHandler)
call(llvmCallable, args, resultLifetime, exceptionHandler)
}
}
@@ -967,7 +992,7 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
// Release init receiver, as required by convention.
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 functionType = codegen.getLlvmFunctionType(irFunction)
val functionType = LlvmFunctionSignature(irFunction, codegen)
val result = functionGenerator(functionType, "kotlin2objc").generate {
var errorOutPtr: LLVMValueRef? = null
@@ -1614,7 +1639,8 @@ private inline fun ObjCExportCodeGenerator.generateObjCToKotlinSyntheticGetter(
MethodBridgeReceiver.Static, valueParameters = emptyList()
)
val imp = functionGenerator(objCFunctionType(context, methodBridge), "objc2kotlin") {
val functionType = objCFunctionType(context, methodBridge)
val imp = functionGenerator(functionType, "objc2kotlin") {
switchToRunnable = true
}.generate {
block()
@@ -1700,12 +1726,10 @@ private fun ObjCExportCodeGenerator.createThrowableAsErrorAdapter(): ObjCExportC
return objCToKotlinMethodAdapter(selector, methodBridge, imp)
}
private fun objCFunctionType(context: Context, methodBridge: MethodBridge): LLVMTypeRef {
val paramTypes = methodBridge.paramBridges.map { it.objCType }
val returnType = methodBridge.returnBridge.objCType(context)
return functionType(returnType, false, *(paramTypes.toTypedArray()))
private fun objCFunctionType(context: Context, methodBridge: MethodBridge): LlvmFunctionSignature {
val paramTypes = methodBridge.paramBridges.map { it.toLlvmParamType() }
val returnType = methodBridge.returnBridge.toLlvmRetType(context)
return LlvmFunctionSignature(returnType, paramTypes, isVararg = false)
}
private val ObjCValueType.llvmType: LLVMTypeRef get() = when (this) {
@@ -1724,31 +1748,31 @@ private val ObjCValueType.llvmType: LLVMTypeRef get() = when (this) {
ObjCValueType.POINTER -> kInt8Ptr
}
private val MethodBridgeParameter.objCType: LLVMTypeRef get() = when (this) {
is MethodBridgeValueParameter.Mapped -> this.bridge.objCType
is MethodBridgeReceiver -> ReferenceBridge.objCType
MethodBridgeSelector -> int8TypePtr
MethodBridgeValueParameter.ErrorOutParameter -> pointerType(ReferenceBridge.objCType)
MethodBridgeValueParameter.SuspendCompletion -> int8TypePtr
private fun MethodBridgeParameter.toLlvmParamType(): LlvmParamType = when (this) {
is MethodBridgeValueParameter.Mapped -> this.bridge.toLlvmParamType()
is MethodBridgeReceiver -> ReferenceBridge.toLlvmParamType()
MethodBridgeSelector -> LlvmParamType(int8TypePtr)
MethodBridgeValueParameter.ErrorOutParameter -> LlvmParamType(pointerType(ReferenceBridge.toLlvmParamType().llvmType))
MethodBridgeValueParameter.SuspendCompletion -> LlvmParamType(int8TypePtr)
}
private fun MethodBridge.ReturnValue.objCType(context: Context): LLVMTypeRef {
return when (this) {
MethodBridge.ReturnValue.Suspend,
MethodBridge.ReturnValue.Void -> voidType
MethodBridge.ReturnValue.HashCode -> if (context.is64BitNSInteger()) int64Type else int32Type
is MethodBridge.ReturnValue.Mapped -> this.bridge.objCType
MethodBridge.ReturnValue.WithError.Success -> ObjCValueType.BOOL.llvmType
private fun MethodBridge.ReturnValue.toLlvmRetType(
context: Context
): LlvmRetType = when (this) {
MethodBridge.ReturnValue.Suspend,
MethodBridge.ReturnValue.Void -> LlvmRetType(voidType)
MethodBridge.ReturnValue.HashCode -> LlvmRetType(if (context.is64BitNSInteger()) int64Type else int32Type)
is MethodBridge.ReturnValue.Mapped -> this.bridge.toLlvmParamType()
MethodBridge.ReturnValue.WithError.Success -> ValueTypeBridge(ObjCValueType.BOOL).toLlvmParamType()
MethodBridge.ReturnValue.Instance.InitResult,
MethodBridge.ReturnValue.Instance.FactoryResult -> ReferenceBridge.objCType
is MethodBridge.ReturnValue.WithError.ZeroForError -> this.successBridge.objCType(context)
}
MethodBridge.ReturnValue.Instance.InitResult,
MethodBridge.ReturnValue.Instance.FactoryResult -> ReferenceBridge.toLlvmParamType()
is MethodBridge.ReturnValue.WithError.ZeroForError -> this.successBridge.toLlvmRetType(context)
}
private val TypeBridge.objCType: LLVMTypeRef get() = when (this) {
is ReferenceBridge, is BlockPointerBridge -> int8TypePtr
is ValueTypeBridge -> this.objCValueType.llvmType
private fun TypeBridge.toLlvmParamType(): LlvmParamType = when (this) {
is ReferenceBridge, is BlockPointerBridge -> LlvmParamType(int8TypePtr)
is ValueTypeBridge -> LlvmParamType(this.objCValueType.llvmType, this.objCValueType.defaultParameterAttributes)
}
internal fun ObjCExportCodeGenerator.getEncoding(methodBridge: MethodBridge): String {
@@ -1758,7 +1782,7 @@ internal fun ObjCExportCodeGenerator.getEncoding(methodBridge: MethodBridge): St
methodBridge.paramBridges.forEach {
append(it.objCEncoding)
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
import org.jetbrains.kotlin.backend.konan.llvm.LlvmParameterAttribute
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.Variance
@@ -153,15 +154,16 @@ object ObjCVoidType : ObjCType() {
override fun render(attrsAndName: String) = "void".withAttrsAndName(attrsAndName)
}
internal enum class ObjCValueType(val encoding: String) {
BOOL("c"),
UNICHAR("S"),
CHAR("c"),
SHORT("s"),
internal enum class ObjCValueType(val encoding: String, val defaultParameterAttributes: List<LlvmParameterAttribute> = emptyList()) {
BOOL("c", listOf(LlvmParameterAttribute.SignExt)),
UNICHAR("S", listOf(LlvmParameterAttribute.ZeroExt)),
// TODO: Switch to explicit SIGNED_CHAR
CHAR("c", listOf(LlvmParameterAttribute.SignExt)),
SHORT("s", listOf(LlvmParameterAttribute.SignExt)),
INT("i"),
LONG_LONG("q"),
UNSIGNED_CHAR("C"),
UNSIGNED_SHORT("S"),
UNSIGNED_CHAR("C", listOf(LlvmParameterAttribute.ZeroExt)),
UNSIGNED_SHORT("S", listOf(LlvmParameterAttribute.ZeroExt)),
UNSIGNED_INT("I"),
UNSIGNED_LONG_LONG("Q"),
FLOAT("f"),