diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt index 99bb6c714a4..724e322df41 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt @@ -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) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt index dedc2c64d55..280fcd00f9b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt @@ -38,15 +38,30 @@ private class CallsChecker(val context: Context, goodFunctions: List) { 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) { 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) { 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 -> { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt index b2a2e8c1564..8beb577db1f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InlineClasses.kt @@ -52,6 +52,12 @@ internal inline fun KotlinType.unwrapToPrimitiveOrReference( ifReference: (type: KotlinType) -> R ): R = KotlinTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference) +internal inline fun 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() { +internal object IrTypeInlineClassesSupport : InlineClassesSupport() { override fun isNullable(type: IrType): Boolean = type.containsNull() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt index f312d340cd8..8b5df8be322 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt @@ -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() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index c9c7e571bf1..94e03e418b5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -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>, - val argsNumber: Int, val success: LLVMBasicBlockRef) + data class FunctionInvokeInformation( + val invokeInstruction: LLVMValueRef, + val llvmFunction: LLVMValueRef, + val rargs: CValues>, + val argsNumber: Int, + val success: LLVMBasicBlockRef, + val attributeProvider: LlvmFunctionAttributeProvider? + ) private val invokeInstructions = mutableListOf() /** * 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, + 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, 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, - 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()) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 0e268215670..362fe0533d8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -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(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(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 { + ) = object : ReadOnlyProperty { - 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) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt index 956bd586866..2a5002f5b53 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt @@ -454,25 +454,30 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv private fun FunctionGenerationContext.emitObjCGetMessenger(args: List, 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 = "" )!! diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index d64a7f764be..27f5ab2cd24 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -712,7 +712,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map, - resultLifetime: Lifetime): LLVMValueRef { - val llvmFunction = codegen.llvmFunction(function.target) - return call(function, llvmFunction, args, resultLifetime) + fun callDirect(function: IrFunction, args: List, resultLifetime: Lifetime): LLVMValueRef { + val functionDeclarations = codegen.llvmFunction(function.target) + return call(function, functionDeclarations, args, resultLifetime) } //-------------------------------------------------------------------------// - fun callVirtual(function: IrFunction, args: List, - 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, 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, + private fun call(function: IrFunction, llvmCallable: LlvmCallable, args: List, resultLifetime: Lifetime): LLVMValueRef { check(!function.isTypedIntrinsic) @@ -2620,7 +2618,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map @@ -2629,16 +2627,18 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map, - resultLifetime: Lifetime = Lifetime.IRRELEVANT, - exceptionHandler: ExceptionHandler = currentCodeContext.exceptionHandler): LLVMValueRef { + private fun call( + function: LlvmCallable, args: List, + resultLifetime: Lifetime = Lifetime.IRRELEVANT, + exceptionHandler: ExceptionHandler = currentCodeContext.exceptionHandler, + ): LLVMValueRef { return functionGenerationContext.call(function, args, resultLifetime, exceptionHandler) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt index 2390e0e102c..dda4bc7b035 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinObjCClassInfoGenerator.kt @@ -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 ) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt index 44fcea358dd..643d5e2c0d8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmAttributes.kt @@ -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(). +sealed class LlvmParameterAttribute(private val llvmAttributeName: String) : LlvmAttribute { + + override fun asAttributeKindId(): LLVMAttributeKindId = llvmAttributeKindIdCache.getOrPut(this) { + getLlvmAttributeKindId(llvmAttributeName) + } + + companion object { + private val llvmAttributeKindIdCache = mutableMapOf() + } + + 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() + } + + object NoUnwind : LlvmFunctionAttribute("nounwind") + object NoReturn : LlvmFunctionAttribute("noreturn") + object NoInline : LlvmFunctionAttribute("noinline") +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmCallable.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmCallable.kt new file mode 100644 index 00000000000..c4c692f3d08 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmCallable.kt @@ -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) + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 79b656c05ac..4e59cd8acdf 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -34,8 +34,13 @@ enum class UniqueKind(val llvmName: String) { } internal class LlvmDeclarations(private val unique: Map) { - 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( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmFunctionPrototype.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmFunctionPrototype.kt new file mode 100644 index 00000000000..1fe02374e79 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmFunctionPrototype.kt @@ -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> 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> by lazy { + memScoped { + val result = mutableListOf>() + for (index in LLVMAttributeFunctionIndex..paramsCount) { + val count = LLVMGetAttributeCountAtIndex(externalFunction, index) + val attributesBuffer = allocArray(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) { + attributes.forEach { attribute -> + val llvmAttributeRef = createLlvmEnumAttribute(context, attribute.asAttributeKindId()) + LLVMAddCallSiteAttribute(callSite, index, llvmAttributeRef) + } +} + +private fun addDeclarationAttributesAtIndex(context: LLVMContextRef, function: LLVMValueRef, index: Int, attributes: List) { + 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 = emptyList(), + val isVararg: Boolean = false, + val functionAttributes: List = 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 = emptyList(), + functionAttributes: List = 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 = + mutableListOf().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) + } + } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmParamType.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmParamType.kt new file mode 100644 index 00000000000..d15d51b85df --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmParamType.kt @@ -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 = emptyList()) + +/** + * A bit better readability for cases when [LlvmParamType] represents return type. + */ +typealias LlvmRetType = LlvmParamType + +internal fun RuntimeAware.getLlvmFunctionParameterTypes(function: IrFunction): List { + 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 { + // 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() + }, + ) +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt index 5a04d3f66e3..8909309c6d1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt @@ -17,7 +17,7 @@ interface RuntimeAware { class Runtime(bitcodeFile: String) { val llvmModule: LLVMModuleRef = parseBitcodeFile(bitcodeFile) val calculatedLLVMTypes: MutableMap = HashMap() - val addedLLVMExternalFunctions: MutableMap = HashMap() + val addedLLVMExternalFunctions: MutableMap = HashMap() internal fun getStructTypeOrNull(name: String) = LLVMGetTypeByName(llvmModule, "struct.$name") internal fun getStructType(name: String) = getStructTypeOrNull(name) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageInstrumentation.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageInstrumentation.kt index 28ac78e36ed..c4aa38abea7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageInstrumentation.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageInstrumentation.kt @@ -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)!! } } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageWriter.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageWriter.kt index 886754ed2b8..61cdca3e25f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageWriter.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/LLVMCoverageWriter.kt @@ -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)!! diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt index 0c3992ad5ed..27e0e42b3b5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCCodeGenerator.kt @@ -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 { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt index 5e37c33f87f..2872b9d8d75 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/BlockPointerSupport.kt @@ -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 -) \ No newline at end of file +private val ObjCExportCodeGeneratorBase.retainBlock: LlvmCallable + get() { + val functionProto = LlvmFunctionProto( + "objc_retainBlock", + LlvmRetType(int8TypePtr), + listOf(LlvmParamType(int8TypePtr)), + origin = CurrentKlibModuleOrigin + ) + return context.llvm.externalFunction(functionProto) + } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index ef5948cceef..bf058a6504e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -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( - 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, + 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, 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, 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 = 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() } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt index ff524a98873..38c5f84bcd7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt @@ -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 = 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"),