blob: 37abb41ee6602ab409000d987f327e540f173c3e [file] [edit]
//===- RuntimeLibcalls.cpp - Interface for runtime libcalls -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/RuntimeLibcalls.h"
#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/SystemLibraries.h"
#include "llvm/IR/Type.h"
#include "llvm/TargetParser/ARMTargetParser.h"
#define DEBUG_TYPE "runtime-libcalls-info"
using namespace llvm;
using namespace RTLIB;
#define GET_RUNTIME_LIBCALLS_INFO
#define GET_INIT_RUNTIME_LIBCALL_NAMES
#define GET_INIT_RUNTIME_LIBCALL_SIGNATURES
#define GET_RUNTIME_LIBCALL_MASKED_VECTOR_LIBCALL_MAPPING
#define GET_SET_TARGET_RUNTIME_LIBCALL_SETS
#define DEFINE_GET_LOOKUP_LIBCALL_IMPL_NAME
#define GET_RUNTIME_LIBCALL_INTRINSIC_TO_LIBCALL
#include "llvm/IR/RuntimeLibcalls.inc"
static RuntimeLibrary getVectorRuntimeLibrary(VectorLibrary VecLib) {
switch (VecLib) {
case VectorLibrary::NoLibrary:
break;
case VectorLibrary::Accelerate:
case VectorLibrary::DarwinLibSystemM:
case VectorLibrary::LIBMVEC:
case VectorLibrary::MASSV:
case VectorLibrary::SVML:
return RuntimeLibrary::UnknownLibrary;
case VectorLibrary::SLEEFGNUABI:
return RuntimeLibrary::sleefgnuabi;
case VectorLibrary::ArmPL:
return RuntimeLibrary::armpl;
case VectorLibrary::AMDLIBM:
return RuntimeLibrary::amdlibm;
}
llvm_unreachable("no RuntimeLibrary for VectorLibrary");
}
RuntimeLibcallsInfo::RuntimeLibcallsInfo(const Triple &TT,
ExceptionHandling ExceptionModel,
FloatABI::ABIType FloatABI,
StringRef ABIName,
VectorLibrary VecLib) {
// Only an unspecified model resolves to the triple default; None is left as
// an explicit disable.
if (ExceptionModel == ExceptionHandling::Default)
ExceptionModel = TT.getDefaultExceptionHandling();
initLibcalls(TT, ExceptionModel, FloatABI, ABIName,
TT.getDefaultLongDoubleFormat());
if (VecLib != VectorLibrary::NoLibrary) {
setAvailableLibraryLibcalls(getVectorRuntimeLibrary(VecLib), TT,
ExceptionModel, FloatABI, ABIName,
TT.getDefaultLongDoubleFormat());
}
}
// TODO: Consider the remaining module flags.
RuntimeLibcallsInfo::RuntimeLibcallsInfo(const Module &M, StringRef ABIName,
VectorLibrary VecLib)
: RuntimeLibcallsInfo(M.getTargetTriple(), M.getExceptionModel(),
M.getFloatABI(), ABIName, VecLib) {}
bool RuntimeLibcallsInfo::isLibraryAvailable(RuntimeLibrary Library) const {
// TODO: Drive this from module-level state (e.g. the linked runtime). For now
// every named library is reported as available.
return true;
}
/// Set default libcall names. If a target wants to opt-out of a libcall it
/// should be placed here.
void RuntimeLibcallsInfo::initLibcalls(const Triple &TT,
ExceptionHandling ExceptionModel,
FloatABI::ABIType FloatABI,
StringRef ABIName,
LongDoubleFormat LongDoubleFormat) {
setTargetRuntimeLibcallSets(TT, ExceptionModel, FloatABI, ABIName,
LongDoubleFormat);
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
iota_range<RTLIB::LibcallImpl>
RuntimeLibcallsInfo::libcallImplNameHit(uint16_t NameOffsetEntry,
uint16_t StrOffset) {
int NumAliases = 1;
for (uint16_t Entry : ArrayRef(RuntimeLibcallNameOffsetTable)
.drop_front(NameOffsetEntry + 1)) {
if (Entry != StrOffset)
break;
++NumAliases;
}
RTLIB::LibcallImpl ImplStart = static_cast<RTLIB::LibcallImpl>(
&RuntimeLibcallNameOffsetTable[NameOffsetEntry] -
&RuntimeLibcallNameOffsetTable[0]);
return enum_seq(ImplStart,
static_cast<RTLIB::LibcallImpl>(ImplStart + NumAliases));
}
bool RuntimeLibcallsInfo::isAAPCS_ABI(const Triple &TT, StringRef ABIName) {
const ARM::ARMABI TargetABI = ARM::computeTargetABI(TT, ABIName);
return TargetABI == ARM::ARM_ABI_AAPCS || TargetABI == ARM::ARM_ABI_AAPCS16;
}
/// TODO: There is really no guarantee that sizeof(size_t) is equal to the index
/// size of the default address space. This matches TargetLibraryInfo and should
/// be kept in sync.
static IntegerType *getSizeTType(LLVMContext &Ctx, const DataLayout &DL) {
return DL.getIndexType(Ctx, /*AddressSpace=*/0);
}
// TODO: NoCallback probably unsafe in general
static constexpr Attribute::AttrKind CommonFnAttrs[] = {
Attribute::NoCallback, Attribute::NoFree, Attribute::NoSync,
Attribute::NoUnwind, Attribute::WillReturn};
static std::pair<Type *, Attribute> convertToIRTypeAndAttr(FuncArgTypeID ID,
LLVMContext &Ctx,
const DataLayout &DL,
unsigned IntBits) {
// FIXME: Use the llvm/ABI library to get accurate IR types and attributes.
switch (ID) {
case Void:
return {Type::getVoidTy(Ctx), Attribute()};
case Bool:
return {IntegerType::get(Ctx, 1), Attribute::get(Ctx, Attribute::ZExt)};
case Int16:
case UInt16:
return {IntegerType::get(Ctx, 16), Attribute()};
case Int32:
case UInt32:
return {IntegerType::get(Ctx, 32), Attribute()};
case Int:
case UInt:
case IntPlus:
case UIntPlus:
case Long:
case ULong:
case IntX:
case UIntX:
return {IntegerType::get(Ctx, IntBits), Attribute()};
case Int64:
case UInt64:
case LLong:
case ULLong:
return {IntegerType::get(Ctx, 64), Attribute()};
case SizeT:
case SSizeT:
return {getSizeTType(Ctx, DL), Attribute()};
case Flt:
case Floating:
return {Type::getFloatTy(Ctx), Attribute()};
case Dbl:
case LDbl:
return {Type::getDoubleTy(Ctx), Attribute()};
case Ptr:
return {PointerType::get(Ctx, 0), Attribute()};
case V2F32:
return {FixedVectorType::get(Type::getFloatTy(Ctx), 2), Attribute()};
case V4F32:
return {FixedVectorType::get(Type::getFloatTy(Ctx), 4), Attribute()};
case V8F32:
return {FixedVectorType::get(Type::getFloatTy(Ctx), 8), Attribute()};
case V16F32:
return {FixedVectorType::get(Type::getFloatTy(Ctx), 16), Attribute()};
case NXV4F32:
return {ScalableVectorType::get(Type::getFloatTy(Ctx), 4), Attribute()};
case V2F64:
return {FixedVectorType::get(Type::getDoubleTy(Ctx), 2), Attribute()};
case V4F64:
return {FixedVectorType::get(Type::getDoubleTy(Ctx), 4), Attribute()};
case V8F64:
return {FixedVectorType::get(Type::getDoubleTy(Ctx), 8), Attribute()};
case NXV2F64:
return {ScalableVectorType::get(Type::getDoubleTy(Ctx), 2), Attribute()};
case NXV2I1:
return {ScalableVectorType::get(Type::getInt1Ty(Ctx), 2), Attribute()};
case NXV4I1:
return {ScalableVectorType::get(Type::getInt1Ty(Ctx), 4), Attribute()};
case V2I32:
return {FixedVectorType::get(Type::getInt32Ty(Ctx), 2), Attribute()};
case V4I32:
return {FixedVectorType::get(Type::getInt32Ty(Ctx), 4), Attribute()};
case V8I32:
return {FixedVectorType::get(Type::getInt32Ty(Ctx), 8), Attribute()};
case V16I32:
return {FixedVectorType::get(Type::getInt32Ty(Ctx), 16), Attribute()};
case NXV2I32:
return {ScalableVectorType::get(Type::getInt32Ty(Ctx), 2), Attribute()};
case NXV4I32:
return {ScalableVectorType::get(Type::getInt32Ty(Ctx), 4), Attribute()};
default:
return {};
}
}
std::pair<FunctionType *, AttributeList>
RuntimeLibcallsInfo::getDefaultFunctionTy(
LLVMContext &Ctx, const Triple &TT, const DataLayout &DL,
RTLIB::LibcallImpl LibcallImpl) const {
Libcall LC = getLibcallFromImpl(LibcallImpl);
const FuncArgTypeID *ProtoTypes = &SignatureTable[SignatureOffset[LC]];
if (ProtoTypes[0] == NoFuncArgType)
return {};
unsigned IntBits = getIntSize(TT);
AttributeList Attrs;
auto [RetTy, RetAttr] =
convertToIRTypeAndAttr(ProtoTypes[0], Ctx, DL, IntBits);
if (RetAttr.isValid())
Attrs = Attrs.addRetAttribute(Ctx, RetAttr);
Type *LastTy = RetTy, *ArgTy;
Attribute LastAttr = RetAttr, ArgAttr;
SmallVector<Type *, 4> ArgTys;
bool IsVarArg = false;
unsigned Idx = 1;
for (FuncArgTypeID TyID = ProtoTypes[Idx]; TyID != NoFuncArgType;
TyID = ProtoTypes[++Idx]) {
if (TyID == Ellip) {
// The ellipsis ends the protoype list so it must be followed by
// NoFuncArgType.
assert(ProtoTypes[Idx + 1] == NoFuncArgType);
IsVarArg = true;
break;
}
if (TyID == Same) {
ArgTy = LastTy;
ArgAttr = LastAttr;
} else {
std::tie(ArgTy, ArgAttr) =
convertToIRTypeAndAttr(ProtoTypes[Idx], Ctx, DL, IntBits);
LastTy = ArgTy;
LastAttr = ArgAttr;
}
ArgTys.push_back(ArgTy);
if (ArgAttr.isValid())
Attrs = Attrs.addParamAttribute(Ctx, Idx - 1, ArgAttr);
}
if (RetTy->isVectorTy()) {
AttrBuilder FuncAttrBuilder(Ctx);
for (Attribute::AttrKind Attr : CommonFnAttrs)
FuncAttrBuilder.addAttribute(Attr);
Attrs = Attrs.addFnAttributes(Ctx, FuncAttrBuilder);
}
return {FunctionType::get(RetTy, ArgTys, IsVarArg), Attrs};
}
std::pair<FunctionType *, AttributeList>
RuntimeLibcallsInfo::getFunctionTy(LLVMContext &Ctx, const Triple &TT,
const DataLayout &DL,
RTLIB::LibcallImpl LibcallImpl) const {
static constexpr Attribute::AttrKind MemoryFnAttrs[] = {
Attribute::NoUnwind, Attribute::WillReturn};
static constexpr Attribute::AttrKind CommonPtrArgAttrs[] = {
Attribute::NoAlias, Attribute::WriteOnly, Attribute::NonNull};
switch (LibcallImpl) {
case RTLIB::impl___sincos_stret:
case RTLIB::impl___sincosf_stret: {
if (!darwinHasSinCosStret(TT)) // Non-darwin currently unexpected
return {};
Type *ScalarTy = LibcallImpl == RTLIB::impl___sincosf_stret
? Type::getFloatTy(Ctx)
: Type::getDoubleTy(Ctx);
AttrBuilder FuncAttrBuilder(Ctx);
for (Attribute::AttrKind Attr : CommonFnAttrs)
FuncAttrBuilder.addAttribute(Attr);
const bool UseSret =
TT.isX86_32() || ((TT.isARM() || TT.isThumb()) &&
ARM::computeTargetABI(TT) == ARM::ARM_ABI_APCS);
FuncAttrBuilder.addMemoryAttr(MemoryEffects::argumentOrErrnoMemOnly(
UseSret ? ModRefInfo::Mod : ModRefInfo::NoModRef, ModRefInfo::Mod));
AttributeList Attrs;
Attrs = Attrs.addFnAttributes(Ctx, FuncAttrBuilder);
if (UseSret) {
AttrBuilder AttrBuilder(Ctx);
StructType *StructTy = StructType::get(ScalarTy, ScalarTy);
AttrBuilder.addStructRetAttr(StructTy);
AttrBuilder.addAlignmentAttr(DL.getABITypeAlign(StructTy));
FunctionType *FuncTy = FunctionType::get(
Type::getVoidTy(Ctx), {DL.getAllocaPtrType(Ctx), ScalarTy}, false);
return {FuncTy, Attrs.addParamAttributes(Ctx, 0, AttrBuilder)};
}
Type *RetTy =
LibcallImpl == RTLIB::impl___sincosf_stret && TT.isX86_64()
? static_cast<Type *>(FixedVectorType::get(ScalarTy, 2))
: static_cast<Type *>(StructType::get(ScalarTy, ScalarTy));
return {FunctionType::get(RetTy, {ScalarTy}, false), Attrs};
}
case RTLIB::impl_malloc:
case RTLIB::impl_calloc: {
AttrBuilder FuncAttrBuilder(Ctx);
for (Attribute::AttrKind Attr : MemoryFnAttrs)
FuncAttrBuilder.addAttribute(Attr);
FuncAttrBuilder.addAttribute(Attribute::NoFree);
AllocFnKind AllocKind = AllocFnKind::Alloc;
if (LibcallImpl == RTLIB::impl_malloc)
AllocKind |= AllocFnKind::Uninitialized;
// TODO: Set memory attribute
FuncAttrBuilder.addAllocKindAttr(AllocKind);
FuncAttrBuilder.addAttribute("alloc-family", "malloc");
FuncAttrBuilder.addAllocSizeAttr(0, LibcallImpl == RTLIB::impl_malloc
? std::nullopt
: std::make_optional(1));
AttributeList Attrs;
Attrs = Attrs.addFnAttributes(Ctx, FuncAttrBuilder);
{
AttrBuilder ArgAttrBuilder(Ctx);
for (Attribute::AttrKind AK : CommonPtrArgAttrs)
ArgAttrBuilder.addAttribute(AK);
Attrs = Attrs.addRetAttribute(Ctx, Attribute::NoUndef);
Attrs = Attrs.addRetAttribute(Ctx, Attribute::NoAlias);
Attrs = Attrs.addParamAttribute(Ctx, 0, Attribute::NoUndef);
if (LibcallImpl == RTLIB::impl_calloc)
Attrs = Attrs.addParamAttribute(Ctx, 1, Attribute::NoUndef);
}
IntegerType *SizeT = getSizeTType(Ctx, DL);
PointerType *PtrTy = PointerType::get(Ctx, 0);
SmallVector<Type *, 2> ArgTys = {SizeT};
if (LibcallImpl == RTLIB::impl_calloc)
ArgTys.push_back(SizeT);
return {FunctionType::get(PtrTy, ArgTys, false), Attrs};
}
case RTLIB::impl_free: {
// TODO: Set memory attribute
AttrBuilder FuncAttrBuilder(Ctx);
for (Attribute::AttrKind Attr : MemoryFnAttrs)
FuncAttrBuilder.addAttribute(Attr);
FuncAttrBuilder.addAllocKindAttr(AllocFnKind::Free);
FuncAttrBuilder.addAttribute("alloc-family", "malloc");
AttributeList Attrs;
Attrs = Attrs.addFnAttributes(Ctx, FuncAttrBuilder);
{
AttrBuilder ArgAttrBuilder(Ctx);
ArgAttrBuilder.addAttribute(Attribute::NoUndef);
ArgAttrBuilder.addAttribute(Attribute::AllocatedPointer);
ArgAttrBuilder.addCapturesAttr(CaptureInfo::none());
Attrs = Attrs.addParamAttributes(Ctx, 0, ArgAttrBuilder);
}
return {FunctionType::get(Type::getVoidTy(Ctx), {PointerType::get(Ctx, 0)},
false),
Attrs};
}
case RTLIB::impl___aeabi_idivmod:
case RTLIB::impl___aeabi_uidivmod:
case RTLIB::impl___aeabi_ldivmod:
case RTLIB::impl___aeabi_uldivmod:
case RTLIB::impl___rt_sdiv:
case RTLIB::impl___rt_udiv:
case RTLIB::impl___rt_sdiv64:
case RTLIB::impl___rt_udiv64: {
// The ARM AEABI (__aeabi_*divmod) and Windows (__rt_*div*) divmod functions
// return both values modeled as an inreg { iN, iN } struct (quotient,
// remainder). The __rt_*div* cases pass the arguments in opposite order,
// though this doesn't affect the declaration.
bool IsSigned;
unsigned Bits;
switch (LibcallImpl) {
case RTLIB::impl___aeabi_idivmod:
case RTLIB::impl___rt_sdiv:
IsSigned = true;
Bits = 32;
break;
case RTLIB::impl___aeabi_uidivmod:
case RTLIB::impl___rt_udiv:
IsSigned = false;
Bits = 32;
break;
case RTLIB::impl___aeabi_ldivmod:
case RTLIB::impl___rt_sdiv64:
IsSigned = true;
Bits = 64;
break;
case RTLIB::impl___aeabi_uldivmod:
case RTLIB::impl___rt_udiv64:
IsSigned = false;
Bits = 64;
break;
default:
llvm_unreachable("unexpected divmod libcall");
}
Type *IntTy = IntegerType::get(Ctx, Bits);
StructType *RetTy = StructType::get(IntTy, IntTy);
FunctionType *FuncTy = FunctionType::get(RetTy, {IntTy, IntTy}, false);
AttrBuilder FuncAttrBuilder(Ctx);
for (Attribute::AttrKind Attr : CommonFnAttrs)
FuncAttrBuilder.addAttribute(Attr);
FuncAttrBuilder.addMemoryAttr(MemoryEffects::none());
AttributeList Attrs;
Attrs = Attrs.addFnAttributes(Ctx, FuncAttrBuilder);
Attribute::AttrKind ExtKind = IsSigned ? Attribute::SExt : Attribute::ZExt;
Attrs = Attrs.addRetAttribute(Ctx, Attribute::InReg);
Attrs = Attrs.addParamAttribute(Ctx, 0, ExtKind);
Attrs = Attrs.addParamAttribute(Ctx, 1, ExtKind);
return {FuncTy, Attrs};
}
case RTLIB::impl_sqrtf:
case RTLIB::impl_sqrt: {
AttrBuilder FuncAttrBuilder(Ctx);
for (Attribute::AttrKind Attr : CommonFnAttrs)
FuncAttrBuilder.addAttribute(Attr);
FuncAttrBuilder.addMemoryAttr(MemoryEffects::errnoMemOnly(ModRefInfo::Mod));
AttributeList Attrs;
Attrs = Attrs.addFnAttributes(Ctx, FuncAttrBuilder);
Type *ScalarTy = LibcallImpl == RTLIB::impl_sqrtf ? Type::getFloatTy(Ctx)
: Type::getDoubleTy(Ctx);
FunctionType *FuncTy = FunctionType::get(ScalarTy, {ScalarTy}, false);
Attrs = Attrs.addRetAttribute(
Ctx, Attribute::getWithNoFPClass(Ctx, fcNegInf | fcNegSubnormal |
fcNegNormal));
return {FuncTy, Attrs};
}
default:
break;
}
auto [FuncTy, Attrs] = getDefaultFunctionTy(Ctx, TT, DL, LibcallImpl);
if (!FuncTy || FuncTy->getNumParams() == 0 ||
!FuncTy->getParamType(0)->isVectorTy())
return {FuncTy, Attrs};
Type *VecTy = FuncTy->getParamType(0);
AttrBuilder ArgAttrBuilder(Ctx);
for (Attribute::AttrKind AK : CommonPtrArgAttrs)
ArgAttrBuilder.addAttribute(AK);
ArgAttrBuilder.addAlignmentAttr(DL.getABITypeAlign(VecTy));
bool HasPtrArg = false;
for (auto [Idx, ParamTy] : enumerate(FuncTy->params())) {
if (ParamTy->isPointerTy()) {
Attrs = Attrs.addParamAttributes(Ctx, Idx, ArgAttrBuilder);
HasPtrArg = true;
}
}
if (HasPtrArg) {
AttrBuilder FuncAttrBuilder(Ctx);
for (Attribute::AttrKind Attr : CommonFnAttrs)
FuncAttrBuilder.addAttribute(Attr);
FuncAttrBuilder.addMemoryAttr(MemoryEffects::argMemOnly(ModRefInfo::Mod));
Attrs = Attrs.addFnAttributes(Ctx, FuncAttrBuilder);
}
return {FuncTy, Attrs};
}