From 49fdeb73ee3499852bd670b4520c82951837173f Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Thu, 21 Oct 2021 14:30:09 +0700 Subject: [PATCH] [K/N] Make default LLVM attributes target-specific Unfortunately, the world of native development is diverse, and we can't generate one bitcode that is correct on every platform. What is important to us is that AAPCS64 and Windows x64 extend <32 bit arguments on callee side, so we can't use zext/sext args for them. --- .../jetbrains/kotlin/backend/konan/Context.kt | 16 +++ .../kotlin/backend/konan/TargetAbiInfo.kt | 99 +++++++++++++++++++ .../backend/konan/llvm/BinaryInterface.kt | 2 +- .../kotlin/backend/konan/llvm/ContextUtils.kt | 4 + .../backend/konan/llvm/LlvmParamType.kt | 46 ++------- 5 files changed, 130 insertions(+), 37 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt 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