| //===- 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/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::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()); | |
| } | |
| 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(MemoryEffects::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 MemoryEffects::ArgMem: | |
| OS << "argmem: "; | |
| break; | |
| case MemoryEffects::InaccessibleMem: | |
| OS << "inaccessiblemem: "; | |
| break; | |
| case MemoryEffects::Other: | |
| llvm_unreachable("This is represented as the default access kind"); | |
| } | |
| OS << getModRefStr(MR); | |
| } | |
| OS << ")"; | |
| OS.flush(); | |
| 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(); | |
| } | |
| 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(); | |
| } | |
| 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(); | |
| } | |
| 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::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 | |
| //===----------------------------------------------------------------------===// | |
| /// 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); | |
| 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); | |
| } | |
| // 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; | |
| } | |
| 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)); | |
| } | |
| } |