[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.
This commit is contained in:
Sergey Bogolepov
2021-10-21 14:30:09 +07:00
committed by Space
parent fdd020eab6
commit 49fdeb73ee
5 changed files with 130 additions and 37 deletions
@@ -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<SerializedInlineFunctionReference>()
val classFields = mutableListOf<SerializedClassFields>()
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) =
@@ -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<LlvmParameterAttribute>
}
class ExtendOnCallerSideTargetAbiInfo : TargetAbiInfo {
private val typeAttributesCache = mutableMapOf<IrType, List<LlvmParameterAttribute>>()
override fun defaultParameterAttributesForIrType(irType: IrType): List<LlvmParameterAttribute> {
// TODO: We perform type unwrapping twice: one to get the underlying type, and then this one.
// Unwrapping is not cheap, so it might affect compilation time.
return 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<IrType, List<LlvmParameterAttribute>>()
override fun defaultParameterAttributesForIrType(irType: IrType): List<LlvmParameterAttribute> {
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
@@ -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 }
@@ -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.
*
@@ -26,9 +26,11 @@ class LlvmParamType(val llvmType: LLVMTypeRef, val attributes: List<LlvmParamete
*/
typealias LlvmRetType = LlvmParamType
internal fun RuntimeAware.getLlvmFunctionParameterTypes(function: IrFunction): List<LlvmParamType> {
internal fun ContextUtils.getLlvmFunctionParameterTypes(function: IrFunction): List<LlvmParamType> {
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<LlvmParameterAttribute> {
// TODO: We perform type unwrapping twice: one to get the underlying type, and then this one.
// Unwrapping is not cheap, so it might affect compilation time.
return irType.unwrapToPrimitiveOrReference(
eachInlinedClass = { inlinedClass, _ ->
when (inlinedClass.classId) {
UnsignedType.UBYTE.classId -> return listOf(LlvmParameterAttribute.ZeroExt)
UnsignedType.USHORT.classId -> return listOf(LlvmParameterAttribute.ZeroExt)
}
},
ifPrimitive = { primitiveType, _ ->
when (primitiveType) {
KonanPrimitiveType.BOOLEAN -> listOf(LlvmParameterAttribute.ZeroExt)
KonanPrimitiveType.CHAR -> listOf(LlvmParameterAttribute.ZeroExt)
KonanPrimitiveType.BYTE -> listOf(LlvmParameterAttribute.SignExt)
KonanPrimitiveType.SHORT -> listOf(LlvmParameterAttribute.SignExt)
KonanPrimitiveType.INT -> emptyList()
KonanPrimitiveType.LONG -> emptyList()
KonanPrimitiveType.FLOAT -> emptyList()
KonanPrimitiveType.DOUBLE -> emptyList()
KonanPrimitiveType.NON_NULL_NATIVE_PTR -> emptyList()
KonanPrimitiveType.VECTOR128 -> emptyList()
}
},
ifReference = {
return listOf()
},
)
}
}