diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index c30ec76349f..292c9c6b4a4 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -50,6 +50,8 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.konan.library.KonanLibraryLayout +import org.jetbrains.kotlin.konan.target.Architecture +import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.util.disposeNativeMemoryAllocator import org.jetbrains.kotlin.library.SerializedIrModule import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal @@ -505,6 +507,20 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { val inlineFunctionBodies = mutableListOf() val classFields = mutableListOf() + + val targetAbiInfo: TargetAbiInfo by lazy { + when { + config.target == KonanTarget.MINGW_X64 -> { + WindowsX64TargetAbiInfo() + } + !config.target.family.isAppleFamily && config.target.architecture == Architecture.ARM64 -> { + AAPCS64TargetAbiInfo() + } + else -> { + DefaultTargetAbiInfo() + } + } + } } private fun MemberScope.getContributedClassifier(name: String) = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt new file mode 100644 index 00000000000..91a8eadd7c3 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt @@ -0,0 +1,99 @@ +/* + * 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 + +import org.jetbrains.kotlin.backend.konan.llvm.LlvmParameterAttribute +import org.jetbrains.kotlin.builtins.UnsignedType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.classId + +// TODO: We have [org.jetbrains.kotlin.native.interop.gen.ObjCAbiInfo] which does similar thing. +// Consider unifying these classes. +// Also there are ABI-specific pieces in codegen and ObjCExport. It makes sense to move them here as well. +/** + * Encapsulates ABI-specific logic for code generation. + * + * Similar Clang classes: + * * [ABIInfo](https://github.com/llvm/llvm-project/blob/2a36f29fce91b6242ebd926d7c08381c31138d2c/clang/lib/CodeGen/ABIInfo.h#L50) + * * [TargetCodeGenInfo](https://github.com/llvm/llvm-project/blob/2831a317b689c7f005a29f008a8e4c24485c0711/clang/lib/CodeGen/TargetInfo.h#L45) + */ +sealed interface TargetAbiInfo { + fun defaultParameterAttributesForIrType(irType: IrType): List +} + +class ExtendOnCallerSideTargetAbiInfo : TargetAbiInfo { + private val typeAttributesCache = mutableMapOf>() + + override 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 typeAttributesCache.getOrPut(irType) { + 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 emptyList() + }, + ) + } + } +} + +sealed class ExtendOnCalleeSideTargetAbiInfo(private val shouldZeroExtBoolean: Boolean) : TargetAbiInfo { + private val typeAttributesCache = mutableMapOf>() + + override fun defaultParameterAttributesForIrType(irType: IrType): List { + return typeAttributesCache.getOrPut(irType) { + if (shouldZeroExtBoolean && irType.computePrimitiveBinaryTypeOrNull() == PrimitiveBinaryType.BOOLEAN) { + listOf(LlvmParameterAttribute.ZeroExt) + } else { + emptyList() + } + } + } +} + +/** + * Procedure Call Standard for the Arm 64-bit Architecture. + * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055a/IHI0055A_aapcs64.pdf + * + * Note that Apple uses different its own variant called DarwinPCS. + */ +class AAPCS64TargetAbiInfo : ExtendOnCalleeSideTargetAbiInfo(shouldZeroExtBoolean = false) + +/** + * Windows x64 ABI. + * https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention + * + * Regarding zero-extension of boolean type: I wasn't able to find any documentation about it, + * so we follow Clang's behavior here, which in turn follows MSVC. + * https://github.com/llvm/llvm-project/blob/1fdec59bffc11ae37eb51a1b9869f0696bfd5312/clang/lib/CodeGen/TargetInfo.cpp#L4234 + */ +class WindowsX64TargetAbiInfo : ExtendOnCalleeSideTargetAbiInfo(shouldZeroExtBoolean = true) + +/** + * "Generic" ABI info that is applicable for the most of our current targets. + * In time will be replaced by more specific [TargetAbiInfo] inheritors. + */ +typealias DefaultTargetAbiInfo = ExtendOnCallerSideTargetAbiInfo \ No newline at end of file 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 8b5df8be322..388f423aaae 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,7 +129,7 @@ private fun String.replaceSpecialSymbols() = fun IrDeclaration.isExported() = KonanBinaryInterface.isExported(this) // TODO: bring here dependencies of this method? -internal fun RuntimeAware.getLlvmFunctionType(function: IrFunction): LLVMTypeRef = functionType( +internal fun ContextUtils.getLlvmFunctionType(function: IrFunction): LLVMTypeRef = functionType( returnType = getLlvmFunctionReturnType(function).llvmType, isVarArg = false, paramTypes = getLlvmFunctionParameterTypes(function).map { it.llvmType } 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 362fe0533d8..75d1526684b 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 @@ -10,6 +10,7 @@ import llvm.* import org.jetbrains.kotlin.backend.konan.CachedLibraries import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.TargetAbiInfo import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin import org.jetbrains.kotlin.descriptors.konan.CurrentKlibModuleOrigin @@ -144,6 +145,9 @@ internal interface ContextUtils : RuntimeAware { override val runtime: Runtime get() = context.llvm.runtime + val argumentAbiInfo: TargetAbiInfo + get() = context.targetAbiInfo + /** * Describes the target platform. * 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 index d15d51b85df..9f27d8e1a54 100644 --- 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 @@ -26,9 +26,11 @@ class LlvmParamType(val llvmType: LLVMTypeRef, val attributes: List { +internal fun ContextUtils.getLlvmFunctionParameterTypes(function: IrFunction): List { val returnType = getLlvmFunctionReturnType(function).llvmType - val paramTypes = ArrayList(function.allParameters.map { LlvmParamType(getLLVMType(it.type), defaultParameterAttributesForIrType(it.type)) }) + val paramTypes = ArrayList(function.allParameters.map { + LlvmParamType(getLLVMType(it.type), argumentAbiInfo.defaultParameterAttributesForIrType(it.type)) + }) if (function.isSuspend) paramTypes.add(LlvmParamType(kObjHeaderPtr)) // Suspend functions have implicit parameter of type Continuation<>. if (isObjectType(returnType)) @@ -37,42 +39,14 @@ internal fun RuntimeAware.getLlvmFunctionParameterTypes(function: IrFunction): L return paramTypes } -internal fun RuntimeAware.getLlvmFunctionReturnType(function: IrFunction): LlvmRetType { +internal fun ContextUtils.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)) + else -> LlvmParamType( + getLLVMReturnType(function.returnType), + argumentAbiInfo.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() - }, - ) -} +} \ No newline at end of file