llvm-project/llvm/lib/IR/Attributes.cpp
Nikita Popov 6b8ed78719 [IR] Add writable attribute
This adds a writable attribute, which in conjunction with
dereferenceable(N) states that a spurious store of N bytes is
introduced on function entry. This implies that this many bytes
are writable without trapping or introducing data races. See
https://llvm.org/docs/Atomics.html#optimization-outside-atomic for
why the second point is important.

This attribute can be added to sret arguments. I believe Rust will
also be able to use it for by-value (moved) arguments. Rust likely
won't be able to use it for &mut arguments (tree borrows does not
appear to allow spurious stores).

In this patch the new attribute is only used by LICM scalar promotion.
However, the actual motivation for this is to fix a correctness issue
in call slot optimization, which needs this attribute to avoid
optimization regressions.

Followup to the discussion on D157499.

Differential Revision: https://reviews.llvm.org/D158081
2023-11-01 10:46:31 +01:00

2251 lines
72 KiB
C++

//===- Attributes.cpp - Implement AttributesList --------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// \file
// This file implements the Attribute, AttributeImpl, AttrBuilder,
// AttributeListImpl, and AttributeList classes.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Attributes.h"
#include "AttributeImpl.h"
#include "LLVMContextImpl.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/AttributeMask.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ModRef.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
using namespace llvm;
//===----------------------------------------------------------------------===//
// Attribute Construction Methods
//===----------------------------------------------------------------------===//
// allocsize has two integer arguments, but because they're both 32 bits, we can
// pack them into one 64-bit value, at the cost of making said value
// nonsensical.
//
// In order to do this, we need to reserve one value of the second (optional)
// allocsize argument to signify "not present."
static const unsigned AllocSizeNumElemsNotPresent = -1;
static uint64_t packAllocSizeArgs(unsigned ElemSizeArg,
const std::optional<unsigned> &NumElemsArg) {
assert((!NumElemsArg || *NumElemsArg != AllocSizeNumElemsNotPresent) &&
"Attempting to pack a reserved value");
return uint64_t(ElemSizeArg) << 32 |
NumElemsArg.value_or(AllocSizeNumElemsNotPresent);
}
static std::pair<unsigned, std::optional<unsigned>>
unpackAllocSizeArgs(uint64_t Num) {
unsigned NumElems = Num & std::numeric_limits<unsigned>::max();
unsigned ElemSizeArg = Num >> 32;
std::optional<unsigned> NumElemsArg;
if (NumElems != AllocSizeNumElemsNotPresent)
NumElemsArg = NumElems;
return std::make_pair(ElemSizeArg, NumElemsArg);
}
static uint64_t packVScaleRangeArgs(unsigned MinValue,
std::optional<unsigned> MaxValue) {
return uint64_t(MinValue) << 32 | MaxValue.value_or(0);
}
static std::pair<unsigned, std::optional<unsigned>>
unpackVScaleRangeArgs(uint64_t Value) {
unsigned MaxValue = Value & std::numeric_limits<unsigned>::max();
unsigned MinValue = Value >> 32;
return std::make_pair(MinValue,
MaxValue > 0 ? MaxValue : std::optional<unsigned>());
}
Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
uint64_t Val) {
bool IsIntAttr = Attribute::isIntAttrKind(Kind);
assert((IsIntAttr || Attribute::isEnumAttrKind(Kind)) &&
"Not an enum or int attribute");
LLVMContextImpl *pImpl = Context.pImpl;
FoldingSetNodeID ID;
ID.AddInteger(Kind);
if (IsIntAttr)
ID.AddInteger(Val);
else
assert(Val == 0 && "Value must be zero for enum attributes");
void *InsertPoint;
AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint);
if (!PA) {
// If we didn't find any existing attributes of the same shape then create a
// new one and insert it.
if (!IsIntAttr)
PA = new (pImpl->Alloc) EnumAttributeImpl(Kind);
else
PA = new (pImpl->Alloc) IntAttributeImpl(Kind, Val);
pImpl->AttrsSet.InsertNode(PA, InsertPoint);
}
// Return the Attribute that we found or created.
return Attribute(PA);
}
Attribute Attribute::get(LLVMContext &Context, StringRef Kind, StringRef Val) {
LLVMContextImpl *pImpl = Context.pImpl;
FoldingSetNodeID ID;
ID.AddString(Kind);
if (!Val.empty()) ID.AddString(Val);
void *InsertPoint;
AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint);
if (!PA) {
// If we didn't find any existing attributes of the same shape then create a
// new one and insert it.
void *Mem =
pImpl->Alloc.Allocate(StringAttributeImpl::totalSizeToAlloc(Kind, Val),
alignof(StringAttributeImpl));
PA = new (Mem) StringAttributeImpl(Kind, Val);
pImpl->AttrsSet.InsertNode(PA, InsertPoint);
}
// Return the Attribute that we found or created.
return Attribute(PA);
}
Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
Type *Ty) {
assert(Attribute::isTypeAttrKind(Kind) && "Not a type attribute");
LLVMContextImpl *pImpl = Context.pImpl;
FoldingSetNodeID ID;
ID.AddInteger(Kind);
ID.AddPointer(Ty);
void *InsertPoint;
AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint);
if (!PA) {
// If we didn't find any existing attributes of the same shape then create a
// new one and insert it.
PA = new (pImpl->Alloc) TypeAttributeImpl(Kind, Ty);
pImpl->AttrsSet.InsertNode(PA, InsertPoint);
}
// Return the Attribute that we found or created.
return Attribute(PA);
}
Attribute Attribute::getWithAlignment(LLVMContext &Context, Align A) {
assert(A <= llvm::Value::MaximumAlignment && "Alignment too large.");
return get(Context, Alignment, A.value());
}
Attribute Attribute::getWithStackAlignment(LLVMContext &Context, Align A) {
assert(A <= 0x100 && "Alignment too large.");
return get(Context, StackAlignment, A.value());
}
Attribute Attribute::getWithDereferenceableBytes(LLVMContext &Context,
uint64_t Bytes) {
assert(Bytes && "Bytes must be non-zero.");
return get(Context, Dereferenceable, Bytes);
}
Attribute Attribute::getWithDereferenceableOrNullBytes(LLVMContext &Context,
uint64_t Bytes) {
assert(Bytes && "Bytes must be non-zero.");
return get(Context, DereferenceableOrNull, Bytes);
}
Attribute Attribute::getWithByValType(LLVMContext &Context, Type *Ty) {
return get(Context, ByVal, Ty);
}
Attribute Attribute::getWithStructRetType(LLVMContext &Context, Type *Ty) {
return get(Context, StructRet, Ty);
}
Attribute Attribute::getWithByRefType(LLVMContext &Context, Type *Ty) {
return get(Context, ByRef, Ty);
}
Attribute Attribute::getWithPreallocatedType(LLVMContext &Context, Type *Ty) {
return get(Context, Preallocated, Ty);
}
Attribute Attribute::getWithInAllocaType(LLVMContext &Context, Type *Ty) {
return get(Context, InAlloca, Ty);
}
Attribute Attribute::getWithUWTableKind(LLVMContext &Context,
UWTableKind Kind) {
return get(Context, UWTable, uint64_t(Kind));
}
Attribute Attribute::getWithMemoryEffects(LLVMContext &Context,
MemoryEffects ME) {
return get(Context, Memory, ME.toIntValue());
}
Attribute Attribute::getWithNoFPClass(LLVMContext &Context,
FPClassTest ClassMask) {
return get(Context, NoFPClass, ClassMask);
}
Attribute
Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg,
const std::optional<unsigned> &NumElemsArg) {
assert(!(ElemSizeArg == 0 && NumElemsArg && *NumElemsArg == 0) &&
"Invalid allocsize arguments -- given allocsize(0, 0)");
return get(Context, AllocSize, packAllocSizeArgs(ElemSizeArg, NumElemsArg));
}
Attribute Attribute::getWithVScaleRangeArgs(LLVMContext &Context,
unsigned MinValue,
unsigned MaxValue) {
return get(Context, VScaleRange, packVScaleRangeArgs(MinValue, MaxValue));
}
Attribute::AttrKind Attribute::getAttrKindFromName(StringRef AttrName) {
return StringSwitch<Attribute::AttrKind>(AttrName)
#define GET_ATTR_NAMES
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \
.Case(#DISPLAY_NAME, Attribute::ENUM_NAME)
#include "llvm/IR/Attributes.inc"
.Default(Attribute::None);
}
StringRef Attribute::getNameFromAttrKind(Attribute::AttrKind AttrKind) {
switch (AttrKind) {
#define GET_ATTR_NAMES
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \
case Attribute::ENUM_NAME: \
return #DISPLAY_NAME;
#include "llvm/IR/Attributes.inc"
case Attribute::None:
return "none";
default:
llvm_unreachable("invalid Kind");
}
}
bool Attribute::isExistingAttribute(StringRef Name) {
return StringSwitch<bool>(Name)
#define GET_ATTR_NAMES
#define ATTRIBUTE_ALL(ENUM_NAME, DISPLAY_NAME) .Case(#DISPLAY_NAME, true)
#include "llvm/IR/Attributes.inc"
.Default(false);
}
//===----------------------------------------------------------------------===//
// Attribute Accessor Methods
//===----------------------------------------------------------------------===//
bool Attribute::isEnumAttribute() const {
return pImpl && pImpl->isEnumAttribute();
}
bool Attribute::isIntAttribute() const {
return pImpl && pImpl->isIntAttribute();
}
bool Attribute::isStringAttribute() const {
return pImpl && pImpl->isStringAttribute();
}
bool Attribute::isTypeAttribute() const {
return pImpl && pImpl->isTypeAttribute();
}
Attribute::AttrKind Attribute::getKindAsEnum() const {
if (!pImpl) return None;
assert((isEnumAttribute() || isIntAttribute() || isTypeAttribute()) &&
"Invalid attribute type to get the kind as an enum!");
return pImpl->getKindAsEnum();
}
uint64_t Attribute::getValueAsInt() const {
if (!pImpl) return 0;
assert(isIntAttribute() &&
"Expected the attribute to be an integer attribute!");
return pImpl->getValueAsInt();
}
bool Attribute::getValueAsBool() const {
if (!pImpl) return false;
assert(isStringAttribute() &&
"Expected the attribute to be a string attribute!");
return pImpl->getValueAsBool();
}
StringRef Attribute::getKindAsString() const {
if (!pImpl) return {};
assert(isStringAttribute() &&
"Invalid attribute type to get the kind as a string!");
return pImpl->getKindAsString();
}
StringRef Attribute::getValueAsString() const {
if (!pImpl) return {};
assert(isStringAttribute() &&
"Invalid attribute type to get the value as a string!");
return pImpl->getValueAsString();
}
Type *Attribute::getValueAsType() const {
if (!pImpl) return {};
assert(isTypeAttribute() &&
"Invalid attribute type to get the value as a type!");
return pImpl->getValueAsType();
}
bool Attribute::hasAttribute(AttrKind Kind) const {
return (pImpl && pImpl->hasAttribute(Kind)) || (!pImpl && Kind == None);
}
bool Attribute::hasAttribute(StringRef Kind) const {
if (!isStringAttribute()) return false;
return pImpl && pImpl->hasAttribute(Kind);
}
MaybeAlign Attribute::getAlignment() const {
assert(hasAttribute(Attribute::Alignment) &&
"Trying to get alignment from non-alignment attribute!");
return MaybeAlign(pImpl->getValueAsInt());
}
MaybeAlign Attribute::getStackAlignment() const {
assert(hasAttribute(Attribute::StackAlignment) &&
"Trying to get alignment from non-alignment attribute!");
return MaybeAlign(pImpl->getValueAsInt());
}
uint64_t Attribute::getDereferenceableBytes() const {
assert(hasAttribute(Attribute::Dereferenceable) &&
"Trying to get dereferenceable bytes from "
"non-dereferenceable attribute!");
return pImpl->getValueAsInt();
}
uint64_t Attribute::getDereferenceableOrNullBytes() const {
assert(hasAttribute(Attribute::DereferenceableOrNull) &&
"Trying to get dereferenceable bytes from "
"non-dereferenceable attribute!");
return pImpl->getValueAsInt();
}
std::pair<unsigned, std::optional<unsigned>>
Attribute::getAllocSizeArgs() const {
assert(hasAttribute(Attribute::AllocSize) &&
"Trying to get allocsize args from non-allocsize attribute");
return unpackAllocSizeArgs(pImpl->getValueAsInt());
}
unsigned Attribute::getVScaleRangeMin() const {
assert(hasAttribute(Attribute::VScaleRange) &&
"Trying to get vscale args from non-vscale attribute");
return unpackVScaleRangeArgs(pImpl->getValueAsInt()).first;
}
std::optional<unsigned> Attribute::getVScaleRangeMax() const {
assert(hasAttribute(Attribute::VScaleRange) &&
"Trying to get vscale args from non-vscale attribute");
return unpackVScaleRangeArgs(pImpl->getValueAsInt()).second;
}
UWTableKind Attribute::getUWTableKind() const {
assert(hasAttribute(Attribute::UWTable) &&
"Trying to get unwind table kind from non-uwtable attribute");
return UWTableKind(pImpl->getValueAsInt());
}
AllocFnKind Attribute::getAllocKind() const {
assert(hasAttribute(Attribute::AllocKind) &&
"Trying to get allockind value from non-allockind attribute");
return AllocFnKind(pImpl->getValueAsInt());
}
MemoryEffects Attribute::getMemoryEffects() const {
assert(hasAttribute(Attribute::Memory) &&
"Can only call getMemoryEffects() on memory attribute");
return MemoryEffects::createFromIntValue(pImpl->getValueAsInt());
}
FPClassTest Attribute::getNoFPClass() const {
assert(hasAttribute(Attribute::NoFPClass) &&
"Can only call getNoFPClass() on nofpclass attribute");
return static_cast<FPClassTest>(pImpl->getValueAsInt());
}
static const char *getModRefStr(ModRefInfo MR) {
switch (MR) {
case ModRefInfo::NoModRef:
return "none";
case ModRefInfo::Ref:
return "read";
case ModRefInfo::Mod:
return "write";
case ModRefInfo::ModRef:
return "readwrite";
}
llvm_unreachable("Invalid ModRefInfo");
}
std::string Attribute::getAsString(bool InAttrGrp) const {
if (!pImpl) return {};
if (isEnumAttribute())
return getNameFromAttrKind(getKindAsEnum()).str();
if (isTypeAttribute()) {
std::string Result = getNameFromAttrKind(getKindAsEnum()).str();
Result += '(';
raw_string_ostream OS(Result);
getValueAsType()->print(OS, false, true);
OS.flush();
Result += ')';
return Result;
}
// FIXME: These should be output like this:
//
// align=4
// alignstack=8
//
if (hasAttribute(Attribute::Alignment))
return (InAttrGrp ? "align=" + Twine(getValueAsInt())
: "align " + Twine(getValueAsInt()))
.str();
auto AttrWithBytesToString = [&](const char *Name) {
return (InAttrGrp ? Name + ("=" + Twine(getValueAsInt()))
: Name + ("(" + Twine(getValueAsInt())) + ")")
.str();
};
if (hasAttribute(Attribute::StackAlignment))
return AttrWithBytesToString("alignstack");
if (hasAttribute(Attribute::Dereferenceable))
return AttrWithBytesToString("dereferenceable");
if (hasAttribute(Attribute::DereferenceableOrNull))
return AttrWithBytesToString("dereferenceable_or_null");
if (hasAttribute(Attribute::AllocSize)) {
unsigned ElemSize;
std::optional<unsigned> NumElems;
std::tie(ElemSize, NumElems) = getAllocSizeArgs();
return (NumElems
? "allocsize(" + Twine(ElemSize) + "," + Twine(*NumElems) + ")"
: "allocsize(" + Twine(ElemSize) + ")")
.str();
}
if (hasAttribute(Attribute::VScaleRange)) {
unsigned MinValue = getVScaleRangeMin();
std::optional<unsigned> MaxValue = getVScaleRangeMax();
return ("vscale_range(" + Twine(MinValue) + "," +
Twine(MaxValue.value_or(0)) + ")")
.str();
}
if (hasAttribute(Attribute::UWTable)) {
UWTableKind Kind = getUWTableKind();
if (Kind != UWTableKind::None) {
return Kind == UWTableKind::Default
? "uwtable"
: ("uwtable(" +
Twine(Kind == UWTableKind::Sync ? "sync" : "async") + ")")
.str();
}
}
if (hasAttribute(Attribute::AllocKind)) {
AllocFnKind Kind = getAllocKind();
SmallVector<StringRef> parts;
if ((Kind & AllocFnKind::Alloc) != AllocFnKind::Unknown)
parts.push_back("alloc");
if ((Kind & AllocFnKind::Realloc) != AllocFnKind::Unknown)
parts.push_back("realloc");
if ((Kind & AllocFnKind::Free) != AllocFnKind::Unknown)
parts.push_back("free");
if ((Kind & AllocFnKind::Uninitialized) != AllocFnKind::Unknown)
parts.push_back("uninitialized");
if ((Kind & AllocFnKind::Zeroed) != AllocFnKind::Unknown)
parts.push_back("zeroed");
if ((Kind & AllocFnKind::Aligned) != AllocFnKind::Unknown)
parts.push_back("aligned");
return ("allockind(\"" +
Twine(llvm::join(parts.begin(), parts.end(), ",")) + "\")")
.str();
}
if (hasAttribute(Attribute::Memory)) {
std::string Result;
raw_string_ostream OS(Result);
bool First = true;
OS << "memory(";
MemoryEffects ME = getMemoryEffects();
// Print access kind for "other" as the default access kind. This way it
// will apply to any new location kinds that get split out of "other".
ModRefInfo OtherMR = ME.getModRef(IRMemLocation::Other);
if (OtherMR != ModRefInfo::NoModRef || ME.getModRef() == OtherMR) {
First = false;
OS << getModRefStr(OtherMR);
}
for (auto Loc : MemoryEffects::locations()) {
ModRefInfo MR = ME.getModRef(Loc);
if (MR == OtherMR)
continue;
if (!First)
OS << ", ";
First = false;
switch (Loc) {
case IRMemLocation::ArgMem:
OS << "argmem: ";
break;
case IRMemLocation::InaccessibleMem:
OS << "inaccessiblemem: ";
break;
case IRMemLocation::Other:
llvm_unreachable("This is represented as the default access kind");
}
OS << getModRefStr(MR);
}
OS << ")";
OS.flush();
return Result;
}
if (hasAttribute(Attribute::NoFPClass)) {
std::string Result = "nofpclass";
raw_string_ostream OS(Result);
OS << getNoFPClass();
return Result;
}
// Convert target-dependent attributes to strings of the form:
//
// "kind"
// "kind" = "value"
//
if (isStringAttribute()) {
std::string Result;
{
raw_string_ostream OS(Result);
OS << '"' << getKindAsString() << '"';
// Since some attribute strings contain special characters that cannot be
// printable, those have to be escaped to make the attribute value
// printable as is. e.g. "\01__gnu_mcount_nc"
const auto &AttrVal = pImpl->getValueAsString();
if (!AttrVal.empty()) {
OS << "=\"";
printEscapedString(AttrVal, OS);
OS << "\"";
}
}
return Result;
}
llvm_unreachable("Unknown attribute");
}
bool Attribute::hasParentContext(LLVMContext &C) const {
assert(isValid() && "invalid Attribute doesn't refer to any context");
FoldingSetNodeID ID;
pImpl->Profile(ID);
void *Unused;
return C.pImpl->AttrsSet.FindNodeOrInsertPos(ID, Unused) == pImpl;
}
bool Attribute::operator<(Attribute A) const {
if (!pImpl && !A.pImpl) return false;
if (!pImpl) return true;
if (!A.pImpl) return false;
return *pImpl < *A.pImpl;
}
void Attribute::Profile(FoldingSetNodeID &ID) const {
ID.AddPointer(pImpl);
}
enum AttributeProperty {
FnAttr = (1 << 0),
ParamAttr = (1 << 1),
RetAttr = (1 << 2),
};
#define GET_ATTR_PROP_TABLE
#include "llvm/IR/Attributes.inc"
static bool hasAttributeProperty(Attribute::AttrKind Kind,
AttributeProperty Prop) {
unsigned Index = Kind - 1;
assert(Index < std::size(AttrPropTable) && "Invalid attribute kind");
return AttrPropTable[Index] & Prop;
}
bool Attribute::canUseAsFnAttr(AttrKind Kind) {
return hasAttributeProperty(Kind, AttributeProperty::FnAttr);
}
bool Attribute::canUseAsParamAttr(AttrKind Kind) {
return hasAttributeProperty(Kind, AttributeProperty::ParamAttr);
}
bool Attribute::canUseAsRetAttr(AttrKind Kind) {
return hasAttributeProperty(Kind, AttributeProperty::RetAttr);
}
//===----------------------------------------------------------------------===//
// AttributeImpl Definition
//===----------------------------------------------------------------------===//
bool AttributeImpl::hasAttribute(Attribute::AttrKind A) const {
if (isStringAttribute()) return false;
return getKindAsEnum() == A;
}
bool AttributeImpl::hasAttribute(StringRef Kind) const {
if (!isStringAttribute()) return false;
return getKindAsString() == Kind;
}
Attribute::AttrKind AttributeImpl::getKindAsEnum() const {
assert(isEnumAttribute() || isIntAttribute() || isTypeAttribute());
return static_cast<const EnumAttributeImpl *>(this)->getEnumKind();
}
uint64_t AttributeImpl::getValueAsInt() const {
assert(isIntAttribute());
return static_cast<const IntAttributeImpl *>(this)->getValue();
}
bool AttributeImpl::getValueAsBool() const {
assert(getValueAsString().empty() || getValueAsString() == "false" || getValueAsString() == "true");
return getValueAsString() == "true";
}
StringRef AttributeImpl::getKindAsString() const {
assert(isStringAttribute());
return static_cast<const StringAttributeImpl *>(this)->getStringKind();
}
StringRef AttributeImpl::getValueAsString() const {
assert(isStringAttribute());
return static_cast<const StringAttributeImpl *>(this)->getStringValue();
}
Type *AttributeImpl::getValueAsType() const {
assert(isTypeAttribute());
return static_cast<const TypeAttributeImpl *>(this)->getTypeValue();
}
bool AttributeImpl::operator<(const AttributeImpl &AI) const {
if (this == &AI)
return false;
// This sorts the attributes with Attribute::AttrKinds coming first (sorted
// relative to their enum value) and then strings.
if (!isStringAttribute()) {
if (AI.isStringAttribute())
return true;
if (getKindAsEnum() != AI.getKindAsEnum())
return getKindAsEnum() < AI.getKindAsEnum();
assert(!AI.isEnumAttribute() && "Non-unique attribute");
assert(!AI.isTypeAttribute() && "Comparison of types would be unstable");
// TODO: Is this actually needed?
assert(AI.isIntAttribute() && "Only possibility left");
return getValueAsInt() < AI.getValueAsInt();
}
if (!AI.isStringAttribute())
return false;
if (getKindAsString() == AI.getKindAsString())
return getValueAsString() < AI.getValueAsString();
return getKindAsString() < AI.getKindAsString();
}
//===----------------------------------------------------------------------===//
// AttributeSet Definition
//===----------------------------------------------------------------------===//
AttributeSet AttributeSet::get(LLVMContext &C, const AttrBuilder &B) {
return AttributeSet(AttributeSetNode::get(C, B));
}
AttributeSet AttributeSet::get(LLVMContext &C, ArrayRef<Attribute> Attrs) {
return AttributeSet(AttributeSetNode::get(C, Attrs));
}
AttributeSet AttributeSet::addAttribute(LLVMContext &C,
Attribute::AttrKind Kind) const {
if (hasAttribute(Kind)) return *this;
AttrBuilder B(C);
B.addAttribute(Kind);
return addAttributes(C, AttributeSet::get(C, B));
}
AttributeSet AttributeSet::addAttribute(LLVMContext &C, StringRef Kind,
StringRef Value) const {
AttrBuilder B(C);
B.addAttribute(Kind, Value);
return addAttributes(C, AttributeSet::get(C, B));
}
AttributeSet AttributeSet::addAttributes(LLVMContext &C,
const AttributeSet AS) const {
if (!hasAttributes())
return AS;
if (!AS.hasAttributes())
return *this;
AttrBuilder B(C, *this);
B.merge(AttrBuilder(C, AS));
return get(C, B);
}
AttributeSet AttributeSet::removeAttribute(LLVMContext &C,
Attribute::AttrKind Kind) const {
if (!hasAttribute(Kind)) return *this;
AttrBuilder B(C, *this);
B.removeAttribute(Kind);
return get(C, B);
}
AttributeSet AttributeSet::removeAttribute(LLVMContext &C,
StringRef Kind) const {
if (!hasAttribute(Kind)) return *this;
AttrBuilder B(C, *this);
B.removeAttribute(Kind);
return get(C, B);
}
AttributeSet AttributeSet::removeAttributes(LLVMContext &C,
const AttributeMask &Attrs) const {
AttrBuilder B(C, *this);
// If there is nothing to remove, directly return the original set.
if (!B.overlaps(Attrs))
return *this;
B.remove(Attrs);
return get(C, B);
}
unsigned AttributeSet::getNumAttributes() const {
return SetNode ? SetNode->getNumAttributes() : 0;
}
bool AttributeSet::hasAttribute(Attribute::AttrKind Kind) const {
return SetNode ? SetNode->hasAttribute(Kind) : false;
}
bool AttributeSet::hasAttribute(StringRef Kind) const {
return SetNode ? SetNode->hasAttribute(Kind) : false;
}
Attribute AttributeSet::getAttribute(Attribute::AttrKind Kind) const {
return SetNode ? SetNode->getAttribute(Kind) : Attribute();
}
Attribute AttributeSet::getAttribute(StringRef Kind) const {
return SetNode ? SetNode->getAttribute(Kind) : Attribute();
}
MaybeAlign AttributeSet::getAlignment() const {
return SetNode ? SetNode->getAlignment() : std::nullopt;
}
MaybeAlign AttributeSet::getStackAlignment() const {
return SetNode ? SetNode->getStackAlignment() : std::nullopt;
}
uint64_t AttributeSet::getDereferenceableBytes() const {
return SetNode ? SetNode->getDereferenceableBytes() : 0;
}
uint64_t AttributeSet::getDereferenceableOrNullBytes() const {
return SetNode ? SetNode->getDereferenceableOrNullBytes() : 0;
}
Type *AttributeSet::getByRefType() const {
return SetNode ? SetNode->getAttributeType(Attribute::ByRef) : nullptr;
}
Type *AttributeSet::getByValType() const {
return SetNode ? SetNode->getAttributeType(Attribute::ByVal) : nullptr;
}
Type *AttributeSet::getStructRetType() const {
return SetNode ? SetNode->getAttributeType(Attribute::StructRet) : nullptr;
}
Type *AttributeSet::getPreallocatedType() const {
return SetNode ? SetNode->getAttributeType(Attribute::Preallocated) : nullptr;
}
Type *AttributeSet::getInAllocaType() const {
return SetNode ? SetNode->getAttributeType(Attribute::InAlloca) : nullptr;
}
Type *AttributeSet::getElementType() const {
return SetNode ? SetNode->getAttributeType(Attribute::ElementType) : nullptr;
}
std::optional<std::pair<unsigned, std::optional<unsigned>>>
AttributeSet::getAllocSizeArgs() const {
if (SetNode)
return SetNode->getAllocSizeArgs();
return std::nullopt;
}
unsigned AttributeSet::getVScaleRangeMin() const {
return SetNode ? SetNode->getVScaleRangeMin() : 1;
}
std::optional<unsigned> AttributeSet::getVScaleRangeMax() const {
return SetNode ? SetNode->getVScaleRangeMax() : std::nullopt;
}
UWTableKind AttributeSet::getUWTableKind() const {
return SetNode ? SetNode->getUWTableKind() : UWTableKind::None;
}
AllocFnKind AttributeSet::getAllocKind() const {
return SetNode ? SetNode->getAllocKind() : AllocFnKind::Unknown;
}
MemoryEffects AttributeSet::getMemoryEffects() const {
return SetNode ? SetNode->getMemoryEffects() : MemoryEffects::unknown();
}
FPClassTest AttributeSet::getNoFPClass() const {
return SetNode ? SetNode->getNoFPClass() : fcNone;
}
std::string AttributeSet::getAsString(bool InAttrGrp) const {
return SetNode ? SetNode->getAsString(InAttrGrp) : "";
}
bool AttributeSet::hasParentContext(LLVMContext &C) const {
assert(hasAttributes() && "empty AttributeSet doesn't refer to any context");
FoldingSetNodeID ID;
SetNode->Profile(ID);
void *Unused;
return C.pImpl->AttrsSetNodes.FindNodeOrInsertPos(ID, Unused) == SetNode;
}
AttributeSet::iterator AttributeSet::begin() const {
return SetNode ? SetNode->begin() : nullptr;
}
AttributeSet::iterator AttributeSet::end() const {
return SetNode ? SetNode->end() : nullptr;
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void AttributeSet::dump() const {
dbgs() << "AS =\n";
dbgs() << " { ";
dbgs() << getAsString(true) << " }\n";
}
#endif
//===----------------------------------------------------------------------===//
// AttributeSetNode Definition
//===----------------------------------------------------------------------===//
AttributeSetNode::AttributeSetNode(ArrayRef<Attribute> Attrs)
: NumAttrs(Attrs.size()) {
// There's memory after the node where we can store the entries in.
llvm::copy(Attrs, getTrailingObjects<Attribute>());
for (const auto &I : *this) {
if (I.isStringAttribute())
StringAttrs.insert({ I.getKindAsString(), I });
else
AvailableAttrs.addAttribute(I.getKindAsEnum());
}
}
AttributeSetNode *AttributeSetNode::get(LLVMContext &C,
ArrayRef<Attribute> Attrs) {
SmallVector<Attribute, 8> SortedAttrs(Attrs.begin(), Attrs.end());
llvm::sort(SortedAttrs);
return getSorted(C, SortedAttrs);
}
AttributeSetNode *AttributeSetNode::getSorted(LLVMContext &C,
ArrayRef<Attribute> SortedAttrs) {
if (SortedAttrs.empty())
return nullptr;
// Build a key to look up the existing attributes.
LLVMContextImpl *pImpl = C.pImpl;
FoldingSetNodeID ID;
assert(llvm::is_sorted(SortedAttrs) && "Expected sorted attributes!");
for (const auto &Attr : SortedAttrs)
Attr.Profile(ID);
void *InsertPoint;
AttributeSetNode *PA =
pImpl->AttrsSetNodes.FindNodeOrInsertPos(ID, InsertPoint);
// If we didn't find any existing attributes of the same shape then create a
// new one and insert it.
if (!PA) {
// Coallocate entries after the AttributeSetNode itself.
void *Mem = ::operator new(totalSizeToAlloc<Attribute>(SortedAttrs.size()));
PA = new (Mem) AttributeSetNode(SortedAttrs);
pImpl->AttrsSetNodes.InsertNode(PA, InsertPoint);
}
// Return the AttributeSetNode that we found or created.
return PA;
}
AttributeSetNode *AttributeSetNode::get(LLVMContext &C, const AttrBuilder &B) {
return getSorted(C, B.attrs());
}
bool AttributeSetNode::hasAttribute(StringRef Kind) const {
return StringAttrs.count(Kind);
}
std::optional<Attribute>
AttributeSetNode::findEnumAttribute(Attribute::AttrKind Kind) const {
// Do a quick presence check.
if (!hasAttribute(Kind))
return std::nullopt;
// Attributes in a set are sorted by enum value, followed by string
// attributes. Binary search the one we want.
const Attribute *I =
std::lower_bound(begin(), end() - StringAttrs.size(), Kind,
[](Attribute A, Attribute::AttrKind Kind) {
return A.getKindAsEnum() < Kind;
});
assert(I != end() && I->hasAttribute(Kind) && "Presence check failed?");
return *I;
}
Attribute AttributeSetNode::getAttribute(Attribute::AttrKind Kind) const {
if (auto A = findEnumAttribute(Kind))
return *A;
return {};
}
Attribute AttributeSetNode::getAttribute(StringRef Kind) const {
return StringAttrs.lookup(Kind);
}
MaybeAlign AttributeSetNode::getAlignment() const {
if (auto A = findEnumAttribute(Attribute::Alignment))
return A->getAlignment();
return std::nullopt;
}
MaybeAlign AttributeSetNode::getStackAlignment() const {
if (auto A = findEnumAttribute(Attribute::StackAlignment))
return A->getStackAlignment();
return std::nullopt;
}
Type *AttributeSetNode::getAttributeType(Attribute::AttrKind Kind) const {
if (auto A = findEnumAttribute(Kind))
return A->getValueAsType();
return nullptr;
}
uint64_t AttributeSetNode::getDereferenceableBytes() const {
if (auto A = findEnumAttribute(Attribute::Dereferenceable))
return A->getDereferenceableBytes();
return 0;
}
uint64_t AttributeSetNode::getDereferenceableOrNullBytes() const {
if (auto A = findEnumAttribute(Attribute::DereferenceableOrNull))
return A->getDereferenceableOrNullBytes();
return 0;
}
std::optional<std::pair<unsigned, std::optional<unsigned>>>
AttributeSetNode::getAllocSizeArgs() const {
if (auto A = findEnumAttribute(Attribute::AllocSize))
return A->getAllocSizeArgs();
return std::nullopt;
}
unsigned AttributeSetNode::getVScaleRangeMin() const {
if (auto A = findEnumAttribute(Attribute::VScaleRange))
return A->getVScaleRangeMin();
return 1;
}
std::optional<unsigned> AttributeSetNode::getVScaleRangeMax() const {
if (auto A = findEnumAttribute(Attribute::VScaleRange))
return A->getVScaleRangeMax();
return std::nullopt;
}
UWTableKind AttributeSetNode::getUWTableKind() const {
if (auto A = findEnumAttribute(Attribute::UWTable))
return A->getUWTableKind();
return UWTableKind::None;
}
AllocFnKind AttributeSetNode::getAllocKind() const {
if (auto A = findEnumAttribute(Attribute::AllocKind))
return A->getAllocKind();
return AllocFnKind::Unknown;
}
MemoryEffects AttributeSetNode::getMemoryEffects() const {
if (auto A = findEnumAttribute(Attribute::Memory))
return A->getMemoryEffects();
return MemoryEffects::unknown();
}
FPClassTest AttributeSetNode::getNoFPClass() const {
if (auto A = findEnumAttribute(Attribute::NoFPClass))
return A->getNoFPClass();
return fcNone;
}
std::string AttributeSetNode::getAsString(bool InAttrGrp) const {
std::string Str;
for (iterator I = begin(), E = end(); I != E; ++I) {
if (I != begin())
Str += ' ';
Str += I->getAsString(InAttrGrp);
}
return Str;
}
//===----------------------------------------------------------------------===//
// AttributeListImpl Definition
//===----------------------------------------------------------------------===//
/// Map from AttributeList index to the internal array index. Adding one happens
/// to work, because -1 wraps around to 0.
static unsigned attrIdxToArrayIdx(unsigned Index) {
return Index + 1;
}
AttributeListImpl::AttributeListImpl(ArrayRef<AttributeSet> Sets)
: NumAttrSets(Sets.size()) {
assert(!Sets.empty() && "pointless AttributeListImpl");
// There's memory after the node where we can store the entries in.
llvm::copy(Sets, getTrailingObjects<AttributeSet>());
// Initialize AvailableFunctionAttrs and AvailableSomewhereAttrs
// summary bitsets.
for (const auto &I : Sets[attrIdxToArrayIdx(AttributeList::FunctionIndex)])
if (!I.isStringAttribute())
AvailableFunctionAttrs.addAttribute(I.getKindAsEnum());
for (const auto &Set : Sets)
for (const auto &I : Set)
if (!I.isStringAttribute())
AvailableSomewhereAttrs.addAttribute(I.getKindAsEnum());
}
void AttributeListImpl::Profile(FoldingSetNodeID &ID) const {
Profile(ID, ArrayRef(begin(), end()));
}
void AttributeListImpl::Profile(FoldingSetNodeID &ID,
ArrayRef<AttributeSet> Sets) {
for (const auto &Set : Sets)
ID.AddPointer(Set.SetNode);
}
bool AttributeListImpl::hasAttrSomewhere(Attribute::AttrKind Kind,
unsigned *Index) const {
if (!AvailableSomewhereAttrs.hasAttribute(Kind))
return false;
if (Index) {
for (unsigned I = 0, E = NumAttrSets; I != E; ++I) {
if (begin()[I].hasAttribute(Kind)) {
*Index = I - 1;
break;
}
}
}
return true;
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void AttributeListImpl::dump() const {
AttributeList(const_cast<AttributeListImpl *>(this)).dump();
}
#endif
//===----------------------------------------------------------------------===//
// AttributeList Construction and Mutation Methods
//===----------------------------------------------------------------------===//
AttributeList AttributeList::getImpl(LLVMContext &C,
ArrayRef<AttributeSet> AttrSets) {
assert(!AttrSets.empty() && "pointless AttributeListImpl");
LLVMContextImpl *pImpl = C.pImpl;
FoldingSetNodeID ID;
AttributeListImpl::Profile(ID, AttrSets);
void *InsertPoint;
AttributeListImpl *PA =
pImpl->AttrsLists.FindNodeOrInsertPos(ID, InsertPoint);
// If we didn't find any existing attributes of the same shape then
// create a new one and insert it.
if (!PA) {
// Coallocate entries after the AttributeListImpl itself.
void *Mem = pImpl->Alloc.Allocate(
AttributeListImpl::totalSizeToAlloc<AttributeSet>(AttrSets.size()),
alignof(AttributeListImpl));
PA = new (Mem) AttributeListImpl(AttrSets);
pImpl->AttrsLists.InsertNode(PA, InsertPoint);
}
// Return the AttributesList that we found or created.
return AttributeList(PA);
}
AttributeList
AttributeList::get(LLVMContext &C,
ArrayRef<std::pair<unsigned, Attribute>> Attrs) {
// If there are no attributes then return a null AttributesList pointer.
if (Attrs.empty())
return {};
assert(llvm::is_sorted(Attrs, llvm::less_first()) &&
"Misordered Attributes list!");
assert(llvm::all_of(Attrs,
[](const std::pair<unsigned, Attribute> &Pair) {
return Pair.second.isValid();
}) &&
"Pointless attribute!");
// Create a vector if (unsigned, AttributeSetNode*) pairs from the attributes
// list.
SmallVector<std::pair<unsigned, AttributeSet>, 8> AttrPairVec;
for (ArrayRef<std::pair<unsigned, Attribute>>::iterator I = Attrs.begin(),
E = Attrs.end(); I != E; ) {
unsigned Index = I->first;
SmallVector<Attribute, 4> AttrVec;
while (I != E && I->first == Index) {
AttrVec.push_back(I->second);
++I;
}
AttrPairVec.emplace_back(Index, AttributeSet::get(C, AttrVec));
}
return get(C, AttrPairVec);
}
AttributeList
AttributeList::get(LLVMContext &C,
ArrayRef<std::pair<unsigned, AttributeSet>> Attrs) {
// If there are no attributes then return a null AttributesList pointer.
if (Attrs.empty())
return {};
assert(llvm::is_sorted(Attrs, llvm::less_first()) &&
"Misordered Attributes list!");
assert(llvm::none_of(Attrs,
[](const std::pair<unsigned, AttributeSet> &Pair) {
return !Pair.second.hasAttributes();
}) &&
"Pointless attribute!");
unsigned MaxIndex = Attrs.back().first;
// If the MaxIndex is FunctionIndex and there are other indices in front
// of it, we need to use the largest of those to get the right size.
if (MaxIndex == FunctionIndex && Attrs.size() > 1)
MaxIndex = Attrs[Attrs.size() - 2].first;
SmallVector<AttributeSet, 4> AttrVec(attrIdxToArrayIdx(MaxIndex) + 1);
for (const auto &Pair : Attrs)
AttrVec[attrIdxToArrayIdx(Pair.first)] = Pair.second;
return getImpl(C, AttrVec);
}
AttributeList AttributeList::get(LLVMContext &C, AttributeSet FnAttrs,
AttributeSet RetAttrs,
ArrayRef<AttributeSet> ArgAttrs) {
// Scan from the end to find the last argument with attributes. Most
// arguments don't have attributes, so it's nice if we can have fewer unique
// AttributeListImpls by dropping empty attribute sets at the end of the list.
unsigned NumSets = 0;
for (size_t I = ArgAttrs.size(); I != 0; --I) {
if (ArgAttrs[I - 1].hasAttributes()) {
NumSets = I + 2;
break;
}
}
if (NumSets == 0) {
// Check function and return attributes if we didn't have argument
// attributes.
if (RetAttrs.hasAttributes())
NumSets = 2;
else if (FnAttrs.hasAttributes())
NumSets = 1;
}
// If all attribute sets were empty, we can use the empty attribute list.
if (NumSets == 0)
return {};
SmallVector<AttributeSet, 8> AttrSets;
AttrSets.reserve(NumSets);
// If we have any attributes, we always have function attributes.
AttrSets.push_back(FnAttrs);
if (NumSets > 1)
AttrSets.push_back(RetAttrs);
if (NumSets > 2) {
// Drop the empty argument attribute sets at the end.
ArgAttrs = ArgAttrs.take_front(NumSets - 2);
llvm::append_range(AttrSets, ArgAttrs);
}
return getImpl(C, AttrSets);
}
AttributeList AttributeList::get(LLVMContext &C, unsigned Index,
AttributeSet Attrs) {
if (!Attrs.hasAttributes())
return {};
Index = attrIdxToArrayIdx(Index);
SmallVector<AttributeSet, 8> AttrSets(Index + 1);
AttrSets[Index] = Attrs;
return getImpl(C, AttrSets);
}
AttributeList AttributeList::get(LLVMContext &C, unsigned Index,
const AttrBuilder &B) {
return get(C, Index, AttributeSet::get(C, B));
}
AttributeList AttributeList::get(LLVMContext &C, unsigned Index,
ArrayRef<Attribute::AttrKind> Kinds) {
SmallVector<std::pair<unsigned, Attribute>, 8> Attrs;
for (const auto K : Kinds)
Attrs.emplace_back(Index, Attribute::get(C, K));
return get(C, Attrs);
}
AttributeList AttributeList::get(LLVMContext &C, unsigned Index,
ArrayRef<Attribute::AttrKind> Kinds,
ArrayRef<uint64_t> Values) {
assert(Kinds.size() == Values.size() && "Mismatched attribute values.");
SmallVector<std::pair<unsigned, Attribute>, 8> Attrs;
auto VI = Values.begin();
for (const auto K : Kinds)
Attrs.emplace_back(Index, Attribute::get(C, K, *VI++));
return get(C, Attrs);
}
AttributeList AttributeList::get(LLVMContext &C, unsigned Index,
ArrayRef<StringRef> Kinds) {
SmallVector<std::pair<unsigned, Attribute>, 8> Attrs;
for (const auto &K : Kinds)
Attrs.emplace_back(Index, Attribute::get(C, K));
return get(C, Attrs);
}
AttributeList AttributeList::get(LLVMContext &C,
ArrayRef<AttributeList> Attrs) {
if (Attrs.empty())
return {};
if (Attrs.size() == 1)
return Attrs[0];
unsigned MaxSize = 0;
for (const auto &List : Attrs)
MaxSize = std::max(MaxSize, List.getNumAttrSets());
// If every list was empty, there is no point in merging the lists.
if (MaxSize == 0)
return {};
SmallVector<AttributeSet, 8> NewAttrSets(MaxSize);
for (unsigned I = 0; I < MaxSize; ++I) {
AttrBuilder CurBuilder(C);
for (const auto &List : Attrs)
CurBuilder.merge(AttrBuilder(C, List.getAttributes(I - 1)));
NewAttrSets[I] = AttributeSet::get(C, CurBuilder);
}
return getImpl(C, NewAttrSets);
}
AttributeList
AttributeList::addAttributeAtIndex(LLVMContext &C, unsigned Index,
Attribute::AttrKind Kind) const {
AttributeSet Attrs = getAttributes(Index);
if (Attrs.hasAttribute(Kind))
return *this;
// TODO: Insert at correct position and avoid sort.
SmallVector<Attribute, 8> NewAttrs(Attrs.begin(), Attrs.end());
NewAttrs.push_back(Attribute::get(C, Kind));
return setAttributesAtIndex(C, Index, AttributeSet::get(C, NewAttrs));
}
AttributeList AttributeList::addAttributeAtIndex(LLVMContext &C, unsigned Index,
StringRef Kind,
StringRef Value) const {
AttrBuilder B(C);
B.addAttribute(Kind, Value);
return addAttributesAtIndex(C, Index, B);
}
AttributeList AttributeList::addAttributeAtIndex(LLVMContext &C, unsigned Index,
Attribute A) const {
AttrBuilder B(C);
B.addAttribute(A);
return addAttributesAtIndex(C, Index, B);
}
AttributeList AttributeList::setAttributesAtIndex(LLVMContext &C,
unsigned Index,
AttributeSet Attrs) const {
Index = attrIdxToArrayIdx(Index);
SmallVector<AttributeSet, 4> AttrSets(this->begin(), this->end());
if (Index >= AttrSets.size())
AttrSets.resize(Index + 1);
AttrSets[Index] = Attrs;
// Remove trailing empty attribute sets.
while (!AttrSets.empty() && !AttrSets.back().hasAttributes())
AttrSets.pop_back();
if (AttrSets.empty())
return {};
return AttributeList::getImpl(C, AttrSets);
}
AttributeList AttributeList::addAttributesAtIndex(LLVMContext &C,
unsigned Index,
const AttrBuilder &B) const {
if (!B.hasAttributes())
return *this;
if (!pImpl)
return AttributeList::get(C, {{Index, AttributeSet::get(C, B)}});
AttrBuilder Merged(C, getAttributes(Index));
Merged.merge(B);
return setAttributesAtIndex(C, Index, AttributeSet::get(C, Merged));
}
AttributeList AttributeList::addParamAttribute(LLVMContext &C,
ArrayRef<unsigned> ArgNos,
Attribute A) const {
assert(llvm::is_sorted(ArgNos));
SmallVector<AttributeSet, 4> AttrSets(this->begin(), this->end());
unsigned MaxIndex = attrIdxToArrayIdx(ArgNos.back() + FirstArgIndex);
if (MaxIndex >= AttrSets.size())
AttrSets.resize(MaxIndex + 1);
for (unsigned ArgNo : ArgNos) {
unsigned Index = attrIdxToArrayIdx(ArgNo + FirstArgIndex);
AttrBuilder B(C, AttrSets[Index]);
B.addAttribute(A);
AttrSets[Index] = AttributeSet::get(C, B);
}
return getImpl(C, AttrSets);
}
AttributeList
AttributeList::removeAttributeAtIndex(LLVMContext &C, unsigned Index,
Attribute::AttrKind Kind) const {
AttributeSet Attrs = getAttributes(Index);
AttributeSet NewAttrs = Attrs.removeAttribute(C, Kind);
if (Attrs == NewAttrs)
return *this;
return setAttributesAtIndex(C, Index, NewAttrs);
}
AttributeList AttributeList::removeAttributeAtIndex(LLVMContext &C,
unsigned Index,
StringRef Kind) const {
AttributeSet Attrs = getAttributes(Index);
AttributeSet NewAttrs = Attrs.removeAttribute(C, Kind);
if (Attrs == NewAttrs)
return *this;
return setAttributesAtIndex(C, Index, NewAttrs);
}
AttributeList AttributeList::removeAttributesAtIndex(
LLVMContext &C, unsigned Index, const AttributeMask &AttrsToRemove) const {
AttributeSet Attrs = getAttributes(Index);
AttributeSet NewAttrs = Attrs.removeAttributes(C, AttrsToRemove);
// If nothing was removed, return the original list.
if (Attrs == NewAttrs)
return *this;
return setAttributesAtIndex(C, Index, NewAttrs);
}
AttributeList
AttributeList::removeAttributesAtIndex(LLVMContext &C,
unsigned WithoutIndex) const {
if (!pImpl)
return {};
if (attrIdxToArrayIdx(WithoutIndex) >= getNumAttrSets())
return *this;
return setAttributesAtIndex(C, WithoutIndex, AttributeSet());
}
AttributeList AttributeList::addDereferenceableRetAttr(LLVMContext &C,
uint64_t Bytes) const {
AttrBuilder B(C);
B.addDereferenceableAttr(Bytes);
return addRetAttributes(C, B);
}
AttributeList AttributeList::addDereferenceableParamAttr(LLVMContext &C,
unsigned Index,
uint64_t Bytes) const {
AttrBuilder B(C);
B.addDereferenceableAttr(Bytes);
return addParamAttributes(C, Index, B);
}
AttributeList
AttributeList::addDereferenceableOrNullParamAttr(LLVMContext &C, unsigned Index,
uint64_t Bytes) const {
AttrBuilder B(C);
B.addDereferenceableOrNullAttr(Bytes);
return addParamAttributes(C, Index, B);
}
AttributeList AttributeList::addAllocSizeParamAttr(
LLVMContext &C, unsigned Index, unsigned ElemSizeArg,
const std::optional<unsigned> &NumElemsArg) {
AttrBuilder B(C);
B.addAllocSizeAttr(ElemSizeArg, NumElemsArg);
return addParamAttributes(C, Index, B);
}
//===----------------------------------------------------------------------===//
// AttributeList Accessor Methods
//===----------------------------------------------------------------------===//
AttributeSet AttributeList::getParamAttrs(unsigned ArgNo) const {
return getAttributes(ArgNo + FirstArgIndex);
}
AttributeSet AttributeList::getRetAttrs() const {
return getAttributes(ReturnIndex);
}
AttributeSet AttributeList::getFnAttrs() const {
return getAttributes(FunctionIndex);
}
bool AttributeList::hasAttributeAtIndex(unsigned Index,
Attribute::AttrKind Kind) const {
return getAttributes(Index).hasAttribute(Kind);
}
bool AttributeList::hasAttributeAtIndex(unsigned Index, StringRef Kind) const {
return getAttributes(Index).hasAttribute(Kind);
}
bool AttributeList::hasAttributesAtIndex(unsigned Index) const {
return getAttributes(Index).hasAttributes();
}
bool AttributeList::hasFnAttr(Attribute::AttrKind Kind) const {
return pImpl && pImpl->hasFnAttribute(Kind);
}
bool AttributeList::hasFnAttr(StringRef Kind) const {
return hasAttributeAtIndex(AttributeList::FunctionIndex, Kind);
}
bool AttributeList::hasAttrSomewhere(Attribute::AttrKind Attr,
unsigned *Index) const {
return pImpl && pImpl->hasAttrSomewhere(Attr, Index);
}
Attribute AttributeList::getAttributeAtIndex(unsigned Index,
Attribute::AttrKind Kind) const {
return getAttributes(Index).getAttribute(Kind);
}
Attribute AttributeList::getAttributeAtIndex(unsigned Index,
StringRef Kind) const {
return getAttributes(Index).getAttribute(Kind);
}
MaybeAlign AttributeList::getRetAlignment() const {
return getAttributes(ReturnIndex).getAlignment();
}
MaybeAlign AttributeList::getParamAlignment(unsigned ArgNo) const {
return getAttributes(ArgNo + FirstArgIndex).getAlignment();
}
MaybeAlign AttributeList::getParamStackAlignment(unsigned ArgNo) const {
return getAttributes(ArgNo + FirstArgIndex).getStackAlignment();
}
Type *AttributeList::getParamByValType(unsigned Index) const {
return getAttributes(Index+FirstArgIndex).getByValType();
}
Type *AttributeList::getParamStructRetType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getStructRetType();
}
Type *AttributeList::getParamByRefType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getByRefType();
}
Type *AttributeList::getParamPreallocatedType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getPreallocatedType();
}
Type *AttributeList::getParamInAllocaType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getInAllocaType();
}
Type *AttributeList::getParamElementType(unsigned Index) const {
return getAttributes(Index + FirstArgIndex).getElementType();
}
MaybeAlign AttributeList::getFnStackAlignment() const {
return getFnAttrs().getStackAlignment();
}
MaybeAlign AttributeList::getRetStackAlignment() const {
return getRetAttrs().getStackAlignment();
}
uint64_t AttributeList::getRetDereferenceableBytes() const {
return getRetAttrs().getDereferenceableBytes();
}
uint64_t AttributeList::getParamDereferenceableBytes(unsigned Index) const {
return getParamAttrs(Index).getDereferenceableBytes();
}
uint64_t AttributeList::getRetDereferenceableOrNullBytes() const {
return getRetAttrs().getDereferenceableOrNullBytes();
}
uint64_t
AttributeList::getParamDereferenceableOrNullBytes(unsigned Index) const {
return getParamAttrs(Index).getDereferenceableOrNullBytes();
}
FPClassTest AttributeList::getRetNoFPClass() const {
return getRetAttrs().getNoFPClass();
}
FPClassTest AttributeList::getParamNoFPClass(unsigned Index) const {
return getParamAttrs(Index).getNoFPClass();
}
UWTableKind AttributeList::getUWTableKind() const {
return getFnAttrs().getUWTableKind();
}
AllocFnKind AttributeList::getAllocKind() const {
return getFnAttrs().getAllocKind();
}
MemoryEffects AttributeList::getMemoryEffects() const {
return getFnAttrs().getMemoryEffects();
}
std::string AttributeList::getAsString(unsigned Index, bool InAttrGrp) const {
return getAttributes(Index).getAsString(InAttrGrp);
}
AttributeSet AttributeList::getAttributes(unsigned Index) const {
Index = attrIdxToArrayIdx(Index);
if (!pImpl || Index >= getNumAttrSets())
return {};
return pImpl->begin()[Index];
}
bool AttributeList::hasParentContext(LLVMContext &C) const {
assert(!isEmpty() && "an empty attribute list has no parent context");
FoldingSetNodeID ID;
pImpl->Profile(ID);
void *Unused;
return C.pImpl->AttrsLists.FindNodeOrInsertPos(ID, Unused) == pImpl;
}
AttributeList::iterator AttributeList::begin() const {
return pImpl ? pImpl->begin() : nullptr;
}
AttributeList::iterator AttributeList::end() const {
return pImpl ? pImpl->end() : nullptr;
}
//===----------------------------------------------------------------------===//
// AttributeList Introspection Methods
//===----------------------------------------------------------------------===//
unsigned AttributeList::getNumAttrSets() const {
return pImpl ? pImpl->NumAttrSets : 0;
}
void AttributeList::print(raw_ostream &O) const {
O << "AttributeList[\n";
for (unsigned i : indexes()) {
if (!getAttributes(i).hasAttributes())
continue;
O << " { ";
switch (i) {
case AttrIndex::ReturnIndex:
O << "return";
break;
case AttrIndex::FunctionIndex:
O << "function";
break;
default:
O << "arg(" << i - AttrIndex::FirstArgIndex << ")";
}
O << " => " << getAsString(i) << " }\n";
}
O << "]\n";
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void AttributeList::dump() const { print(dbgs()); }
#endif
//===----------------------------------------------------------------------===//
// AttrBuilder Method Implementations
//===----------------------------------------------------------------------===//
AttrBuilder::AttrBuilder(LLVMContext &Ctx, AttributeSet AS) : Ctx(Ctx) {
append_range(Attrs, AS);
assert(is_sorted(Attrs) && "AttributeSet should be sorted");
}
void AttrBuilder::clear() { Attrs.clear(); }
/// Attribute comparator that only compares attribute keys. Enum attributes are
/// sorted before string attributes.
struct AttributeComparator {
bool operator()(Attribute A0, Attribute A1) const {
bool A0IsString = A0.isStringAttribute();
bool A1IsString = A1.isStringAttribute();
if (A0IsString) {
if (A1IsString)
return A0.getKindAsString() < A1.getKindAsString();
else
return false;
}
if (A1IsString)
return true;
return A0.getKindAsEnum() < A1.getKindAsEnum();
}
bool operator()(Attribute A0, Attribute::AttrKind Kind) const {
if (A0.isStringAttribute())
return false;
return A0.getKindAsEnum() < Kind;
}
bool operator()(Attribute A0, StringRef Kind) const {
if (A0.isStringAttribute())
return A0.getKindAsString() < Kind;
return true;
}
};
template <typename K>
static void addAttributeImpl(SmallVectorImpl<Attribute> &Attrs, K Kind,
Attribute Attr) {
auto It = lower_bound(Attrs, Kind, AttributeComparator());
if (It != Attrs.end() && It->hasAttribute(Kind))
std::swap(*It, Attr);
else
Attrs.insert(It, Attr);
}
AttrBuilder &AttrBuilder::addAttribute(Attribute Attr) {
if (Attr.isStringAttribute())
addAttributeImpl(Attrs, Attr.getKindAsString(), Attr);
else
addAttributeImpl(Attrs, Attr.getKindAsEnum(), Attr);
return *this;
}
AttrBuilder &AttrBuilder::addAttribute(Attribute::AttrKind Kind) {
addAttributeImpl(Attrs, Kind, Attribute::get(Ctx, Kind));
return *this;
}
AttrBuilder &AttrBuilder::addAttribute(StringRef A, StringRef V) {
addAttributeImpl(Attrs, A, Attribute::get(Ctx, A, V));
return *this;
}
AttrBuilder &AttrBuilder::removeAttribute(Attribute::AttrKind Val) {
assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!");
auto It = lower_bound(Attrs, Val, AttributeComparator());
if (It != Attrs.end() && It->hasAttribute(Val))
Attrs.erase(It);
return *this;
}
AttrBuilder &AttrBuilder::removeAttribute(StringRef A) {
auto It = lower_bound(Attrs, A, AttributeComparator());
if (It != Attrs.end() && It->hasAttribute(A))
Attrs.erase(It);
return *this;
}
std::optional<uint64_t>
AttrBuilder::getRawIntAttr(Attribute::AttrKind Kind) const {
assert(Attribute::isIntAttrKind(Kind) && "Not an int attribute");
Attribute A = getAttribute(Kind);
if (A.isValid())
return A.getValueAsInt();
return std::nullopt;
}
AttrBuilder &AttrBuilder::addRawIntAttr(Attribute::AttrKind Kind,
uint64_t Value) {
return addAttribute(Attribute::get(Ctx, Kind, Value));
}
std::optional<std::pair<unsigned, std::optional<unsigned>>>
AttrBuilder::getAllocSizeArgs() const {
Attribute A = getAttribute(Attribute::AllocSize);
if (A.isValid())
return A.getAllocSizeArgs();
return std::nullopt;
}
AttrBuilder &AttrBuilder::addAlignmentAttr(MaybeAlign Align) {
if (!Align)
return *this;
assert(*Align <= llvm::Value::MaximumAlignment && "Alignment too large.");
return addRawIntAttr(Attribute::Alignment, Align->value());
}
AttrBuilder &AttrBuilder::addStackAlignmentAttr(MaybeAlign Align) {
// Default alignment, allow the target to define how to align it.
if (!Align)
return *this;
assert(*Align <= 0x100 && "Alignment too large.");
return addRawIntAttr(Attribute::StackAlignment, Align->value());
}
AttrBuilder &AttrBuilder::addDereferenceableAttr(uint64_t Bytes) {
if (Bytes == 0) return *this;
return addRawIntAttr(Attribute::Dereferenceable, Bytes);
}
AttrBuilder &AttrBuilder::addDereferenceableOrNullAttr(uint64_t Bytes) {
if (Bytes == 0)
return *this;
return addRawIntAttr(Attribute::DereferenceableOrNull, Bytes);
}
AttrBuilder &
AttrBuilder::addAllocSizeAttr(unsigned ElemSize,
const std::optional<unsigned> &NumElems) {
return addAllocSizeAttrFromRawRepr(packAllocSizeArgs(ElemSize, NumElems));
}
AttrBuilder &AttrBuilder::addAllocSizeAttrFromRawRepr(uint64_t RawArgs) {
// (0, 0) is our "not present" value, so we need to check for it here.
assert(RawArgs && "Invalid allocsize arguments -- given allocsize(0, 0)");
return addRawIntAttr(Attribute::AllocSize, RawArgs);
}
AttrBuilder &AttrBuilder::addVScaleRangeAttr(unsigned MinValue,
std::optional<unsigned> MaxValue) {
return addVScaleRangeAttrFromRawRepr(packVScaleRangeArgs(MinValue, MaxValue));
}
AttrBuilder &AttrBuilder::addVScaleRangeAttrFromRawRepr(uint64_t RawArgs) {
// (0, 0) is not present hence ignore this case
if (RawArgs == 0)
return *this;
return addRawIntAttr(Attribute::VScaleRange, RawArgs);
}
AttrBuilder &AttrBuilder::addUWTableAttr(UWTableKind Kind) {
if (Kind == UWTableKind::None)
return *this;
return addRawIntAttr(Attribute::UWTable, uint64_t(Kind));
}
AttrBuilder &AttrBuilder::addMemoryAttr(MemoryEffects ME) {
return addRawIntAttr(Attribute::Memory, ME.toIntValue());
}
AttrBuilder &AttrBuilder::addNoFPClassAttr(FPClassTest Mask) {
if (Mask == fcNone)
return *this;
return addRawIntAttr(Attribute::NoFPClass, Mask);
}
AttrBuilder &AttrBuilder::addAllocKindAttr(AllocFnKind Kind) {
return addRawIntAttr(Attribute::AllocKind, static_cast<uint64_t>(Kind));
}
Type *AttrBuilder::getTypeAttr(Attribute::AttrKind Kind) const {
assert(Attribute::isTypeAttrKind(Kind) && "Not a type attribute");
Attribute A = getAttribute(Kind);
return A.isValid() ? A.getValueAsType() : nullptr;
}
AttrBuilder &AttrBuilder::addTypeAttr(Attribute::AttrKind Kind, Type *Ty) {
return addAttribute(Attribute::get(Ctx, Kind, Ty));
}
AttrBuilder &AttrBuilder::addByValAttr(Type *Ty) {
return addTypeAttr(Attribute::ByVal, Ty);
}
AttrBuilder &AttrBuilder::addStructRetAttr(Type *Ty) {
return addTypeAttr(Attribute::StructRet, Ty);
}
AttrBuilder &AttrBuilder::addByRefAttr(Type *Ty) {
return addTypeAttr(Attribute::ByRef, Ty);
}
AttrBuilder &AttrBuilder::addPreallocatedAttr(Type *Ty) {
return addTypeAttr(Attribute::Preallocated, Ty);
}
AttrBuilder &AttrBuilder::addInAllocaAttr(Type *Ty) {
return addTypeAttr(Attribute::InAlloca, Ty);
}
AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
// TODO: Could make this O(n) as we're merging two sorted lists.
for (const auto &I : B.attrs())
addAttribute(I);
return *this;
}
AttrBuilder &AttrBuilder::remove(const AttributeMask &AM) {
erase_if(Attrs, [&](Attribute A) { return AM.contains(A); });
return *this;
}
bool AttrBuilder::overlaps(const AttributeMask &AM) const {
return any_of(Attrs, [&](Attribute A) { return AM.contains(A); });
}
Attribute AttrBuilder::getAttribute(Attribute::AttrKind A) const {
assert((unsigned)A < Attribute::EndAttrKinds && "Attribute out of range!");
auto It = lower_bound(Attrs, A, AttributeComparator());
if (It != Attrs.end() && It->hasAttribute(A))
return *It;
return {};
}
Attribute AttrBuilder::getAttribute(StringRef A) const {
auto It = lower_bound(Attrs, A, AttributeComparator());
if (It != Attrs.end() && It->hasAttribute(A))
return *It;
return {};
}
bool AttrBuilder::contains(Attribute::AttrKind A) const {
return getAttribute(A).isValid();
}
bool AttrBuilder::contains(StringRef A) const {
return getAttribute(A).isValid();
}
bool AttrBuilder::operator==(const AttrBuilder &B) const {
return Attrs == B.Attrs;
}
//===----------------------------------------------------------------------===//
// AttributeFuncs Function Defintions
//===----------------------------------------------------------------------===//
/// Returns true if this is a type legal for the 'nofpclass' attribute. This
/// follows the same type rules as FPMathOperator.
///
/// TODO: Consider relaxing to any FP type struct fields.
bool AttributeFuncs::isNoFPClassCompatibleType(Type *Ty) {
while (ArrayType *ArrTy = dyn_cast<ArrayType>(Ty))
Ty = ArrTy->getElementType();
return Ty->isFPOrFPVectorTy();
}
/// Which attributes cannot be applied to a type.
AttributeMask AttributeFuncs::typeIncompatible(Type *Ty,
AttributeSafetyKind ASK) {
AttributeMask Incompatible;
if (!Ty->isIntegerTy()) {
// Attributes that only apply to integers.
if (ASK & ASK_SAFE_TO_DROP)
Incompatible.addAttribute(Attribute::AllocAlign);
if (ASK & ASK_UNSAFE_TO_DROP)
Incompatible.addAttribute(Attribute::SExt).addAttribute(Attribute::ZExt);
}
if (!Ty->isPointerTy()) {
// Attributes that only apply to pointers.
if (ASK & ASK_SAFE_TO_DROP)
Incompatible.addAttribute(Attribute::NoAlias)
.addAttribute(Attribute::NoCapture)
.addAttribute(Attribute::NonNull)
.addAttribute(Attribute::ReadNone)
.addAttribute(Attribute::ReadOnly)
.addAttribute(Attribute::Dereferenceable)
.addAttribute(Attribute::DereferenceableOrNull)
.addAttribute(Attribute::Writable);
if (ASK & ASK_UNSAFE_TO_DROP)
Incompatible.addAttribute(Attribute::Nest)
.addAttribute(Attribute::SwiftError)
.addAttribute(Attribute::Preallocated)
.addAttribute(Attribute::InAlloca)
.addAttribute(Attribute::ByVal)
.addAttribute(Attribute::StructRet)
.addAttribute(Attribute::ByRef)
.addAttribute(Attribute::ElementType)
.addAttribute(Attribute::AllocatedPointer);
}
// Attributes that only apply to pointers or vectors of pointers.
if (!Ty->isPtrOrPtrVectorTy()) {
if (ASK & ASK_SAFE_TO_DROP)
Incompatible.addAttribute(Attribute::Alignment);
}
if (ASK & ASK_SAFE_TO_DROP) {
if (!isNoFPClassCompatibleType(Ty))
Incompatible.addAttribute(Attribute::NoFPClass);
}
// Some attributes can apply to all "values" but there are no `void` values.
if (Ty->isVoidTy()) {
if (ASK & ASK_SAFE_TO_DROP)
Incompatible.addAttribute(Attribute::NoUndef);
}
return Incompatible;
}
AttributeMask AttributeFuncs::getUBImplyingAttributes() {
AttributeMask AM;
AM.addAttribute(Attribute::NoUndef);
AM.addAttribute(Attribute::Dereferenceable);
AM.addAttribute(Attribute::DereferenceableOrNull);
return AM;
}
/// Callees with dynamic denormal modes are compatible with any caller mode.
static bool denormModeCompatible(DenormalMode CallerMode,
DenormalMode CalleeMode) {
if (CallerMode == CalleeMode || CalleeMode == DenormalMode::getDynamic())
return true;
// If they don't exactly match, it's OK if the mismatched component is
// dynamic.
if (CalleeMode.Input == CallerMode.Input &&
CalleeMode.Output == DenormalMode::Dynamic)
return true;
if (CalleeMode.Output == CallerMode.Output &&
CalleeMode.Input == DenormalMode::Dynamic)
return true;
return false;
}
static bool checkDenormMode(const Function &Caller, const Function &Callee) {
DenormalMode CallerMode = Caller.getDenormalModeRaw();
DenormalMode CalleeMode = Callee.getDenormalModeRaw();
if (denormModeCompatible(CallerMode, CalleeMode)) {
DenormalMode CallerModeF32 = Caller.getDenormalModeF32Raw();
DenormalMode CalleeModeF32 = Callee.getDenormalModeF32Raw();
if (CallerModeF32 == DenormalMode::getInvalid())
CallerModeF32 = CallerMode;
if (CalleeModeF32 == DenormalMode::getInvalid())
CalleeModeF32 = CalleeMode;
return denormModeCompatible(CallerModeF32, CalleeModeF32);
}
return false;
}
template<typename AttrClass>
static bool isEqual(const Function &Caller, const Function &Callee) {
return Caller.getFnAttribute(AttrClass::getKind()) ==
Callee.getFnAttribute(AttrClass::getKind());
}
/// Compute the logical AND of the attributes of the caller and the
/// callee.
///
/// This function sets the caller's attribute to false if the callee's attribute
/// is false.
template<typename AttrClass>
static void setAND(Function &Caller, const Function &Callee) {
if (AttrClass::isSet(Caller, AttrClass::getKind()) &&
!AttrClass::isSet(Callee, AttrClass::getKind()))
AttrClass::set(Caller, AttrClass::getKind(), false);
}
/// Compute the logical OR of the attributes of the caller and the
/// callee.
///
/// This function sets the caller's attribute to true if the callee's attribute
/// is true.
template<typename AttrClass>
static void setOR(Function &Caller, const Function &Callee) {
if (!AttrClass::isSet(Caller, AttrClass::getKind()) &&
AttrClass::isSet(Callee, AttrClass::getKind()))
AttrClass::set(Caller, AttrClass::getKind(), true);
}
/// If the inlined function had a higher stack protection level than the
/// calling function, then bump up the caller's stack protection level.
static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) {
// If the calling function has *no* stack protection level (e.g. it was built
// with Clang's -fno-stack-protector or no_stack_protector attribute), don't
// change it as that could change the program's semantics.
if (!Caller.hasStackProtectorFnAttr())
return;
// If upgrading the SSP attribute, clear out the old SSP Attributes first.
// Having multiple SSP attributes doesn't actually hurt, but it adds useless
// clutter to the IR.
AttributeMask OldSSPAttr;
OldSSPAttr.addAttribute(Attribute::StackProtect)
.addAttribute(Attribute::StackProtectStrong)
.addAttribute(Attribute::StackProtectReq);
if (Callee.hasFnAttribute(Attribute::StackProtectReq)) {
Caller.removeFnAttrs(OldSSPAttr);
Caller.addFnAttr(Attribute::StackProtectReq);
} else if (Callee.hasFnAttribute(Attribute::StackProtectStrong) &&
!Caller.hasFnAttribute(Attribute::StackProtectReq)) {
Caller.removeFnAttrs(OldSSPAttr);
Caller.addFnAttr(Attribute::StackProtectStrong);
} else if (Callee.hasFnAttribute(Attribute::StackProtect) &&
!Caller.hasFnAttribute(Attribute::StackProtectReq) &&
!Caller.hasFnAttribute(Attribute::StackProtectStrong))
Caller.addFnAttr(Attribute::StackProtect);
}
/// If the inlined function required stack probes, then ensure that
/// the calling function has those too.
static void adjustCallerStackProbes(Function &Caller, const Function &Callee) {
if (!Caller.hasFnAttribute("probe-stack") &&
Callee.hasFnAttribute("probe-stack")) {
Caller.addFnAttr(Callee.getFnAttribute("probe-stack"));
}
}
/// If the inlined function defines the size of guard region
/// on the stack, then ensure that the calling function defines a guard region
/// that is no larger.
static void
adjustCallerStackProbeSize(Function &Caller, const Function &Callee) {
Attribute CalleeAttr = Callee.getFnAttribute("stack-probe-size");
if (CalleeAttr.isValid()) {
Attribute CallerAttr = Caller.getFnAttribute("stack-probe-size");
if (CallerAttr.isValid()) {
uint64_t CallerStackProbeSize, CalleeStackProbeSize;
CallerAttr.getValueAsString().getAsInteger(0, CallerStackProbeSize);
CalleeAttr.getValueAsString().getAsInteger(0, CalleeStackProbeSize);
if (CallerStackProbeSize > CalleeStackProbeSize) {
Caller.addFnAttr(CalleeAttr);
}
} else {
Caller.addFnAttr(CalleeAttr);
}
}
}
/// If the inlined function defines a min legal vector width, then ensure
/// the calling function has the same or larger min legal vector width. If the
/// caller has the attribute, but the callee doesn't, we need to remove the
/// attribute from the caller since we can't make any guarantees about the
/// caller's requirements.
/// This function is called after the inlining decision has been made so we have
/// to merge the attribute this way. Heuristics that would use
/// min-legal-vector-width to determine inline compatibility would need to be
/// handled as part of inline cost analysis.
static void
adjustMinLegalVectorWidth(Function &Caller, const Function &Callee) {
Attribute CallerAttr = Caller.getFnAttribute("min-legal-vector-width");
if (CallerAttr.isValid()) {
Attribute CalleeAttr = Callee.getFnAttribute("min-legal-vector-width");
if (CalleeAttr.isValid()) {
uint64_t CallerVectorWidth, CalleeVectorWidth;
CallerAttr.getValueAsString().getAsInteger(0, CallerVectorWidth);
CalleeAttr.getValueAsString().getAsInteger(0, CalleeVectorWidth);
if (CallerVectorWidth < CalleeVectorWidth)
Caller.addFnAttr(CalleeAttr);
} else {
// If the callee doesn't have the attribute then we don't know anything
// and must drop the attribute from the caller.
Caller.removeFnAttr("min-legal-vector-width");
}
}
}
/// If the inlined function has null_pointer_is_valid attribute,
/// set this attribute in the caller post inlining.
static void
adjustNullPointerValidAttr(Function &Caller, const Function &Callee) {
if (Callee.nullPointerIsDefined() && !Caller.nullPointerIsDefined()) {
Caller.addFnAttr(Attribute::NullPointerIsValid);
}
}
struct EnumAttr {
static bool isSet(const Function &Fn,
Attribute::AttrKind Kind) {
return Fn.hasFnAttribute(Kind);
}
static void set(Function &Fn,
Attribute::AttrKind Kind, bool Val) {
if (Val)
Fn.addFnAttr(Kind);
else
Fn.removeFnAttr(Kind);
}
};
struct StrBoolAttr {
static bool isSet(const Function &Fn,
StringRef Kind) {
auto A = Fn.getFnAttribute(Kind);
return A.getValueAsString().equals("true");
}
static void set(Function &Fn,
StringRef Kind, bool Val) {
Fn.addFnAttr(Kind, Val ? "true" : "false");
}
};
#define GET_ATTR_NAMES
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \
struct ENUM_NAME##Attr : EnumAttr { \
static enum Attribute::AttrKind getKind() { \
return llvm::Attribute::ENUM_NAME; \
} \
};
#define ATTRIBUTE_STRBOOL(ENUM_NAME, DISPLAY_NAME) \
struct ENUM_NAME##Attr : StrBoolAttr { \
static StringRef getKind() { return #DISPLAY_NAME; } \
};
#include "llvm/IR/Attributes.inc"
#define GET_ATTR_COMPAT_FUNC
#include "llvm/IR/Attributes.inc"
bool AttributeFuncs::areInlineCompatible(const Function &Caller,
const Function &Callee) {
return hasCompatibleFnAttrs(Caller, Callee);
}
bool AttributeFuncs::areOutlineCompatible(const Function &A,
const Function &B) {
return hasCompatibleFnAttrs(A, B);
}
void AttributeFuncs::mergeAttributesForInlining(Function &Caller,
const Function &Callee) {
mergeFnAttrs(Caller, Callee);
}
void AttributeFuncs::mergeAttributesForOutlining(Function &Base,
const Function &ToMerge) {
// We merge functions so that they meet the most general case.
// For example, if the NoNansFPMathAttr is set in one function, but not in
// the other, in the merged function we can say that the NoNansFPMathAttr
// is not set.
// However if we have the SpeculativeLoadHardeningAttr set true in one
// function, but not the other, we make sure that the function retains
// that aspect in the merged function.
mergeFnAttrs(Base, ToMerge);
}
void AttributeFuncs::updateMinLegalVectorWidthAttr(Function &Fn,
uint64_t Width) {
Attribute Attr = Fn.getFnAttribute("min-legal-vector-width");
if (Attr.isValid()) {
uint64_t OldWidth;
Attr.getValueAsString().getAsInteger(0, OldWidth);
if (Width > OldWidth)
Fn.addFnAttr("min-legal-vector-width", llvm::utostr(Width));
}
}