[Attributes] Determine attribute properties from TableGen data

Continuing from D105763, this allows placing certain properties
about attributes in the TableGen definition. In particular, we
store whether an attribute applies to fn/param/ret (or a combination
thereof). This information is used by the Verifier, as well as the
ForceFunctionAttrs pass. I also plan to use this in LLParser,
which also duplicates info on which attributes are valid where.

This keeps metadata about attributes in one place, and makes it
more likely that it stays in sync, rather than in various
functions spread across the codebase.

Differential Revision: https://reviews.llvm.org/D105780
This commit is contained in:
Nikita Popov 2021-07-11 16:54:03 +02:00
parent e5e291e135
commit 7ed3e87825
7 changed files with 181 additions and 247 deletions

View File

@ -90,6 +90,10 @@ public:
return Kind >= FirstTypeAttr && Kind <= LastTypeAttr;
}
static bool canUseAsFnAttr(AttrKind Kind);
static bool canUseAsParamAttr(AttrKind Kind);
static bool canUseAsRetAttr(AttrKind Kind);
private:
AttributeImpl *pImpl = nullptr;

View File

@ -10,228 +10,243 @@
//
//===----------------------------------------------------------------------===//
/// Attribute property base class.
class AttrProperty;
/// Can be used as function attribute.
def FnAttr : AttrProperty;
/// Can be used as parameter attribute.
def ParamAttr : AttrProperty;
/// Can be used as return attribute.
def RetAttr : AttrProperty;
/// Attribute base class.
class Attr<string S> {
class Attr<string S, list<AttrProperty> P> {
// String representation of this attribute in the IR.
string AttrString = S;
list<AttrProperty> Properties = P;
}
/// Enum attribute.
class EnumAttr<string S> : Attr<S>;
class EnumAttr<string S, list<AttrProperty> P> : Attr<S, P>;
/// Int attribute.
class IntAttr<string S> : Attr<S>;
/// StringBool attribute.
class StrBoolAttr<string S> : Attr<S>;
class IntAttr<string S, list<AttrProperty> P> : Attr<S, P>;
/// Type attribute.
class TypeAttr<string S> : Attr<S>;
class TypeAttr<string S, list<AttrProperty> P> : Attr<S, P>;
/// StringBool attribute.
class StrBoolAttr<string S> : Attr<S, []>;
/// Target-independent enum attributes.
/// Alignment of parameter (5 bits) stored as log2 of alignment with +1 bias.
/// 0 means unaligned (different from align(1)).
def Alignment : IntAttr<"align">;
def Alignment : IntAttr<"align", [ParamAttr, RetAttr]>;
/// The result of the function is guaranteed to point to a number of bytes that
/// we can determine if we know the value of the function's arguments.
def AllocSize : IntAttr<"allocsize">;
def AllocSize : IntAttr<"allocsize", [FnAttr]>;
/// inline=always.
def AlwaysInline : EnumAttr<"alwaysinline">;
def AlwaysInline : EnumAttr<"alwaysinline", [FnAttr]>;
/// Function can access memory only using pointers based on its arguments.
def ArgMemOnly : EnumAttr<"argmemonly">;
def ArgMemOnly : EnumAttr<"argmemonly", [FnAttr]>;
/// Callee is recognized as a builtin, despite nobuiltin attribute on its
/// declaration.
def Builtin : EnumAttr<"builtin">;
def Builtin : EnumAttr<"builtin", [FnAttr]>;
/// Pass structure by value.
def ByVal : TypeAttr<"byval">;
def ByVal : TypeAttr<"byval", [ParamAttr]>;
/// Mark in-memory ABI type.
def ByRef : TypeAttr<"byref">;
def ByRef : TypeAttr<"byref", [ParamAttr]>;
/// Parameter or return value may not contain uninitialized or poison bits.
def NoUndef : EnumAttr<"noundef">;
def NoUndef : EnumAttr<"noundef", [ParamAttr, RetAttr]>;
/// Marks function as being in a cold path.
def Cold : EnumAttr<"cold">;
def Cold : EnumAttr<"cold", [FnAttr]>;
/// Can only be moved to control-equivalent blocks.
def Convergent : EnumAttr<"convergent">;
def Convergent : EnumAttr<"convergent", [FnAttr]>;
/// Marks function as being in a hot path and frequently called.
def Hot: EnumAttr<"hot">;
def Hot: EnumAttr<"hot", [FnAttr]>;
/// Pointer is known to be dereferenceable.
def Dereferenceable : IntAttr<"dereferenceable">;
def Dereferenceable : IntAttr<"dereferenceable", [ParamAttr, RetAttr]>;
/// Pointer is either null or dereferenceable.
def DereferenceableOrNull : IntAttr<"dereferenceable_or_null">;
def DereferenceableOrNull : IntAttr<"dereferenceable_or_null",
[ParamAttr, RetAttr]>;
/// Function may only access memory that is inaccessible from IR.
def InaccessibleMemOnly : EnumAttr<"inaccessiblememonly">;
def InaccessibleMemOnly : EnumAttr<"inaccessiblememonly", [FnAttr]>;
/// Function may only access memory that is either inaccessible from the IR,
/// or pointed to by its pointer arguments.
def InaccessibleMemOrArgMemOnly : EnumAttr<"inaccessiblemem_or_argmemonly">;
def InaccessibleMemOrArgMemOnly : EnumAttr<"inaccessiblemem_or_argmemonly",
[FnAttr]>;
/// Pass structure in an alloca.
def InAlloca : TypeAttr<"inalloca">;
def InAlloca : TypeAttr<"inalloca", [ParamAttr]>;
/// Source said inlining was desirable.
def InlineHint : EnumAttr<"inlinehint">;
def InlineHint : EnumAttr<"inlinehint", [FnAttr]>;
/// Force argument to be passed in register.
def InReg : EnumAttr<"inreg">;
def InReg : EnumAttr<"inreg", [ParamAttr, RetAttr]>;
/// Build jump-instruction tables and replace refs.
def JumpTable : EnumAttr<"jumptable">;
def JumpTable : EnumAttr<"jumptable", [FnAttr]>;
/// Function must be optimized for size first.
def MinSize : EnumAttr<"minsize">;
def MinSize : EnumAttr<"minsize", [FnAttr]>;
/// Naked function.
def Naked : EnumAttr<"naked">;
def Naked : EnumAttr<"naked", [FnAttr]>;
/// Nested function static chain.
def Nest : EnumAttr<"nest">;
def Nest : EnumAttr<"nest", [ParamAttr]>;
/// Considered to not alias after call.
def NoAlias : EnumAttr<"noalias">;
def NoAlias : EnumAttr<"noalias", [ParamAttr, RetAttr]>;
/// Callee isn't recognized as a builtin.
def NoBuiltin : EnumAttr<"nobuiltin">;
def NoBuiltin : EnumAttr<"nobuiltin", [FnAttr]>;
/// Function cannot enter into caller's translation unit.
def NoCallback : EnumAttr<"nocallback">;
def NoCallback : EnumAttr<"nocallback", [FnAttr]>;
/// Function creates no aliases of pointer.
def NoCapture : EnumAttr<"nocapture">;
def NoCapture : EnumAttr<"nocapture", [ParamAttr]>;
/// Call cannot be duplicated.
def NoDuplicate : EnumAttr<"noduplicate">;
def NoDuplicate : EnumAttr<"noduplicate", [FnAttr]>;
/// Function does not deallocate memory.
def NoFree : EnumAttr<"nofree">;
def NoFree : EnumAttr<"nofree", [FnAttr, ParamAttr]>;
/// Disable implicit floating point insts.
def NoImplicitFloat : EnumAttr<"noimplicitfloat">;
def NoImplicitFloat : EnumAttr<"noimplicitfloat", [FnAttr]>;
/// inline=never.
def NoInline : EnumAttr<"noinline">;
def NoInline : EnumAttr<"noinline", [FnAttr]>;
/// Function is called early and/or often, so lazy binding isn't worthwhile.
def NonLazyBind : EnumAttr<"nonlazybind">;
def NonLazyBind : EnumAttr<"nonlazybind", [FnAttr]>;
/// Disable merging for specified functions or call sites.
def NoMerge : EnumAttr<"nomerge">;
def NoMerge : EnumAttr<"nomerge", [FnAttr]>;
/// Pointer is known to be not null.
def NonNull : EnumAttr<"nonnull">;
def NonNull : EnumAttr<"nonnull", [ParamAttr, RetAttr]>;
/// The function does not recurse.
def NoRecurse : EnumAttr<"norecurse">;
def NoRecurse : EnumAttr<"norecurse", [FnAttr]>;
/// Disable redzone.
def NoRedZone : EnumAttr<"noredzone">;
def NoRedZone : EnumAttr<"noredzone", [FnAttr]>;
/// Mark the function as not returning.
def NoReturn : EnumAttr<"noreturn">;
def NoReturn : EnumAttr<"noreturn", [FnAttr]>;
/// Function does not synchronize.
def NoSync : EnumAttr<"nosync">;
def NoSync : EnumAttr<"nosync", [FnAttr]>;
/// Disable Indirect Branch Tracking.
def NoCfCheck : EnumAttr<"nocf_check">;
def NoCfCheck : EnumAttr<"nocf_check", [FnAttr]>;
/// Function should not be instrumented.
def NoProfile : EnumAttr<"noprofile">;
def NoProfile : EnumAttr<"noprofile", [FnAttr]>;
/// Function doesn't unwind stack.
def NoUnwind : EnumAttr<"nounwind">;
def NoUnwind : EnumAttr<"nounwind", [FnAttr]>;
/// No SanitizeCoverage instrumentation.
def NoSanitizeCoverage : EnumAttr<"nosanitize_coverage">;
def NoSanitizeCoverage : EnumAttr<"nosanitize_coverage", [FnAttr]>;
/// Null pointer in address space zero is valid.
def NullPointerIsValid : EnumAttr<"null_pointer_is_valid">;
def NullPointerIsValid : EnumAttr<"null_pointer_is_valid", [FnAttr]>;
/// Select optimizations for best fuzzing signal.
def OptForFuzzing : EnumAttr<"optforfuzzing">;
def OptForFuzzing : EnumAttr<"optforfuzzing", [FnAttr]>;
/// opt_size.
def OptimizeForSize : EnumAttr<"optsize">;
def OptimizeForSize : EnumAttr<"optsize", [FnAttr]>;
/// Function must not be optimized.
def OptimizeNone : EnumAttr<"optnone">;
def OptimizeNone : EnumAttr<"optnone", [FnAttr]>;
/// Similar to byval but without a copy.
def Preallocated : TypeAttr<"preallocated">;
def Preallocated : TypeAttr<"preallocated", [FnAttr, ParamAttr]>;
/// Function does not access memory.
def ReadNone : EnumAttr<"readnone">;
def ReadNone : EnumAttr<"readnone", [FnAttr, ParamAttr]>;
/// Function only reads from memory.
def ReadOnly : EnumAttr<"readonly">;
def ReadOnly : EnumAttr<"readonly", [FnAttr, ParamAttr]>;
/// Return value is always equal to this argument.
def Returned : EnumAttr<"returned">;
def Returned : EnumAttr<"returned", [ParamAttr]>;
/// Parameter is required to be a trivial constant.
def ImmArg : EnumAttr<"immarg">;
def ImmArg : EnumAttr<"immarg", [ParamAttr]>;
/// Function can return twice.
def ReturnsTwice : EnumAttr<"returns_twice">;
def ReturnsTwice : EnumAttr<"returns_twice", [FnAttr]>;
/// Safe Stack protection.
def SafeStack : EnumAttr<"safestack">;
def SafeStack : EnumAttr<"safestack", [FnAttr]>;
/// Shadow Call Stack protection.
def ShadowCallStack : EnumAttr<"shadowcallstack">;
def ShadowCallStack : EnumAttr<"shadowcallstack", [FnAttr]>;
/// Sign extended before/after call.
def SExt : EnumAttr<"signext">;
def SExt : EnumAttr<"signext", [ParamAttr, RetAttr]>;
/// Alignment of stack for function (3 bits) stored as log2 of alignment with
/// +1 bias 0 means unaligned (different from alignstack=(1)).
def StackAlignment : IntAttr<"alignstack">;
def StackAlignment : IntAttr<"alignstack", [FnAttr, ParamAttr]>;
/// Function can be speculated.
def Speculatable : EnumAttr<"speculatable">;
def Speculatable : EnumAttr<"speculatable", [FnAttr]>;
/// Stack protection.
def StackProtect : EnumAttr<"ssp">;
def StackProtect : EnumAttr<"ssp", [FnAttr]>;
/// Stack protection required.
def StackProtectReq : EnumAttr<"sspreq">;
def StackProtectReq : EnumAttr<"sspreq", [FnAttr]>;
/// Strong Stack protection.
def StackProtectStrong : EnumAttr<"sspstrong">;
def StackProtectStrong : EnumAttr<"sspstrong", [FnAttr]>;
/// Function was called in a scope requiring strict floating point semantics.
def StrictFP : EnumAttr<"strictfp">;
def StrictFP : EnumAttr<"strictfp", [FnAttr]>;
/// Hidden pointer to structure to return.
def StructRet : TypeAttr<"sret">;
def StructRet : TypeAttr<"sret", [ParamAttr]>;
/// AddressSanitizer is on.
def SanitizeAddress : EnumAttr<"sanitize_address">;
def SanitizeAddress : EnumAttr<"sanitize_address", [FnAttr]>;
/// ThreadSanitizer is on.
def SanitizeThread : EnumAttr<"sanitize_thread">;
def SanitizeThread : EnumAttr<"sanitize_thread", [FnAttr]>;
/// MemorySanitizer is on.
def SanitizeMemory : EnumAttr<"sanitize_memory">;
def SanitizeMemory : EnumAttr<"sanitize_memory", [FnAttr]>;
/// HWAddressSanitizer is on.
def SanitizeHWAddress : EnumAttr<"sanitize_hwaddress">;
def SanitizeHWAddress : EnumAttr<"sanitize_hwaddress", [FnAttr]>;
/// MemTagSanitizer is on.
def SanitizeMemTag : EnumAttr<"sanitize_memtag">;
def SanitizeMemTag : EnumAttr<"sanitize_memtag", [FnAttr]>;
/// Speculative Load Hardening is enabled.
///
@ -239,34 +254,35 @@ def SanitizeMemTag : EnumAttr<"sanitize_memtag">;
/// inlining) and a conservative merge strategy where inlining an attributed
/// body will add the attribute to the caller. This ensures that code carrying
/// this attribute will always be lowered with hardening enabled.
def SpeculativeLoadHardening : EnumAttr<"speculative_load_hardening">;
def SpeculativeLoadHardening : EnumAttr<"speculative_load_hardening",
[FnAttr]>;
/// Argument is swift error.
def SwiftError : EnumAttr<"swifterror">;
def SwiftError : EnumAttr<"swifterror", [ParamAttr]>;
/// Argument is swift self/context.
def SwiftSelf : EnumAttr<"swiftself">;
def SwiftSelf : EnumAttr<"swiftself", [ParamAttr]>;
/// Argument is swift async context.
def SwiftAsync : EnumAttr<"swiftasync">;
def SwiftAsync : EnumAttr<"swiftasync", [ParamAttr]>;
/// Function must be in a unwind table.
def UWTable : EnumAttr<"uwtable">;
def UWTable : EnumAttr<"uwtable", [FnAttr]>;
/// Minimum/Maximum vscale value for function.
def VScaleRange : IntAttr<"vscale_range">;
def VScaleRange : IntAttr<"vscale_range", [FnAttr]>;
/// Function always comes back to callsite.
def WillReturn : EnumAttr<"willreturn">;
def WillReturn : EnumAttr<"willreturn", [FnAttr]>;
/// Function only writes to memory.
def WriteOnly : EnumAttr<"writeonly">;
def WriteOnly : EnumAttr<"writeonly", [FnAttr, ParamAttr]>;
/// Zero extended before/after call.
def ZExt : EnumAttr<"zeroext">;
def ZExt : EnumAttr<"zeroext", [ParamAttr, RetAttr]>;
/// Function is required to make Forward Progress.
def MustProgress : EnumAttr<"mustprogress">;
def MustProgress : EnumAttr<"mustprogress", [FnAttr]>;
/// Target-independent string attributes.
def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">;

View File

@ -485,6 +485,35 @@ 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 < sizeof(AttrPropTable) / sizeof(AttrPropTable[0]) &&
"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
//===----------------------------------------------------------------------===//

View File

@ -540,8 +540,7 @@ private:
void verifyTailCCMustTailAttrs(AttrBuilder Attrs, StringRef Context);
void verifyMustTailCall(CallInst &CI);
bool verifyAttributeCount(AttributeList Attrs, unsigned Params);
void verifyAttributeTypes(AttributeSet Attrs, bool IsFunction,
const Value *V);
void verifyAttributeTypes(AttributeSet Attrs, const Value *V);
void verifyParameterAttrs(AttributeSet Attrs, Type *Ty, const Value *V);
void checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr,
const Value *V);
@ -1654,76 +1653,7 @@ void Verifier::visitModuleFlagCGProfileEntry(const MDOperand &MDO) {
"expected an integer constant", Node->getOperand(2));
}
/// Return true if this attribute kind only applies to functions.
static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
switch (Kind) {
case Attribute::NoMerge:
case Attribute::NoReturn:
case Attribute::NoSync:
case Attribute::WillReturn:
case Attribute::NoCallback:
case Attribute::NoCfCheck:
case Attribute::NoUnwind:
case Attribute::NoInline:
case Attribute::NoSanitizeCoverage:
case Attribute::AlwaysInline:
case Attribute::OptimizeForSize:
case Attribute::StackProtect:
case Attribute::StackProtectReq:
case Attribute::StackProtectStrong:
case Attribute::SafeStack:
case Attribute::ShadowCallStack:
case Attribute::NoRedZone:
case Attribute::NoImplicitFloat:
case Attribute::Naked:
case Attribute::InlineHint:
case Attribute::UWTable:
case Attribute::VScaleRange:
case Attribute::NonLazyBind:
case Attribute::ReturnsTwice:
case Attribute::SanitizeAddress:
case Attribute::SanitizeHWAddress:
case Attribute::SanitizeMemTag:
case Attribute::SanitizeThread:
case Attribute::SanitizeMemory:
case Attribute::MinSize:
case Attribute::NoDuplicate:
case Attribute::Builtin:
case Attribute::NoBuiltin:
case Attribute::Cold:
case Attribute::Hot:
case Attribute::OptForFuzzing:
case Attribute::OptimizeNone:
case Attribute::JumpTable:
case Attribute::Convergent:
case Attribute::ArgMemOnly:
case Attribute::NoRecurse:
case Attribute::InaccessibleMemOnly:
case Attribute::InaccessibleMemOrArgMemOnly:
case Attribute::AllocSize:
case Attribute::SpeculativeLoadHardening:
case Attribute::Speculatable:
case Attribute::StrictFP:
case Attribute::NullPointerIsValid:
case Attribute::MustProgress:
case Attribute::NoProfile:
return true;
default:
break;
}
return false;
}
/// Return true if this is a function attribute that can also appear on
/// arguments.
static bool isFuncOrArgAttr(Attribute::AttrKind Kind) {
return Kind == Attribute::ReadOnly || Kind == Attribute::WriteOnly ||
Kind == Attribute::ReadNone || Kind == Attribute::NoFree ||
Kind == Attribute::Preallocated || Kind == Attribute::StackAlignment;
}
void Verifier::verifyAttributeTypes(AttributeSet Attrs, bool IsFunction,
const Value *V) {
void Verifier::verifyAttributeTypes(AttributeSet Attrs, const Value *V) {
for (Attribute A : Attrs) {
if (A.isStringAttribute()) {
@ -1746,20 +1676,6 @@ void Verifier::verifyAttributeTypes(AttributeSet Attrs, bool IsFunction,
V);
return;
}
if (isFuncOnlyAttr(A.getKindAsEnum())) {
if (!IsFunction) {
CheckFailed("Attribute '" + A.getAsString() +
"' only applies to functions!",
V);
return;
}
} else if (IsFunction && !isFuncOrArgAttr(A.getKindAsEnum())) {
CheckFailed("Attribute '" + A.getAsString() +
"' does not apply to functions!",
V);
return;
}
}
}
@ -1770,7 +1686,14 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
if (!Attrs.hasAttributes())
return;
verifyAttributeTypes(Attrs, /*IsFunction=*/false, V);
verifyAttributeTypes(Attrs, V);
for (Attribute Attr : Attrs)
Assert(Attr.isStringAttribute() ||
Attribute::canUseAsParamAttr(Attr.getKindAsEnum()),
"Attribute '" + Attr.getAsString() +
"' does not apply to parameters",
V);
if (Attrs.hasAttribute(Attribute::ImmArg)) {
Assert(Attrs.getNumAttributes() == 1,
@ -1941,29 +1864,13 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
// Verify return value attributes.
AttributeSet RetAttrs = Attrs.getRetAttributes();
Assert((!RetAttrs.hasAttribute(Attribute::ByVal) &&
!RetAttrs.hasAttribute(Attribute::Nest) &&
!RetAttrs.hasAttribute(Attribute::StructRet) &&
!RetAttrs.hasAttribute(Attribute::NoCapture) &&
!RetAttrs.hasAttribute(Attribute::NoFree) &&
!RetAttrs.hasAttribute(Attribute::Returned) &&
!RetAttrs.hasAttribute(Attribute::InAlloca) &&
!RetAttrs.hasAttribute(Attribute::Preallocated) &&
!RetAttrs.hasAttribute(Attribute::ByRef) &&
!RetAttrs.hasAttribute(Attribute::SwiftSelf) &&
!RetAttrs.hasAttribute(Attribute::SwiftAsync) &&
!RetAttrs.hasAttribute(Attribute::SwiftError)),
"Attributes 'byval', 'inalloca', 'preallocated', 'byref', "
"'nest', 'sret', 'nocapture', 'nofree', "
"'returned', 'swiftself', 'swiftasync', and 'swifterror'"
" do not apply to return values!",
V);
Assert((!RetAttrs.hasAttribute(Attribute::ReadOnly) &&
!RetAttrs.hasAttribute(Attribute::WriteOnly) &&
!RetAttrs.hasAttribute(Attribute::ReadNone)),
"Attribute '" + RetAttrs.getAsString() +
"' does not apply to function returns",
V);
for (Attribute RetAttr : RetAttrs)
Assert(RetAttr.isStringAttribute() ||
Attribute::canUseAsRetAttr(RetAttr.getKindAsEnum()),
"Attribute '" + RetAttr.getAsString() +
"' does not apply to function return values",
V);
verifyParameterAttrs(RetAttrs, FT->getReturnType(), V);
// Verify parameter attributes.
@ -2024,7 +1931,13 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
if (!Attrs.hasAttributes(AttributeList::FunctionIndex))
return;
verifyAttributeTypes(Attrs.getFnAttributes(), /*IsFunction=*/true, V);
verifyAttributeTypes(Attrs.getFnAttributes(), V);
for (Attribute FnAttr : Attrs.getFnAttributes())
Assert(FnAttr.isStringAttribute() ||
Attribute::canUseAsFnAttr(FnAttr.getKindAsEnum()),
"Attribute '" + FnAttr.getAsString() +
"' does not apply to functions!",
V);
Assert(!(Attrs.hasFnAttribute(Attribute::ReadNone) &&
Attrs.hasFnAttribute(Attribute::ReadOnly)),
@ -4705,10 +4618,10 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
Assert(ArgCount == 2, "this attribute should have 2 arguments");
Assert(isa<ConstantInt>(Call.getOperand(Elem.Begin + 1)),
"the second argument should be a constant integral value");
} else if (isFuncOnlyAttr(Kind)) {
Assert((ArgCount) == 0, "this attribute has no argument");
} else if (!isFuncOrArgAttr(Kind)) {
} else if (Attribute::canUseAsParamAttr(Kind)) {
Assert((ArgCount) == 1, "this attribute should have one argument");
} else if (Attribute::canUseAsFnAttr(Kind)) {
Assert((ArgCount) == 0, "this attribute has no argument");
}
}
break;

View File

@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/IPO/ForceFunctionAttrs.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
@ -33,51 +32,6 @@ static cl::list<std::string> ForceRemoveAttributes(
"example -force-remove-attribute=foo:noinline. This "
"option can be specified multiple times."));
static Attribute::AttrKind parseAttrKind(StringRef Kind) {
return StringSwitch<Attribute::AttrKind>(Kind)
.Case("alwaysinline", Attribute::AlwaysInline)
.Case("builtin", Attribute::Builtin)
.Case("cold", Attribute::Cold)
.Case("convergent", Attribute::Convergent)
.Case("inlinehint", Attribute::InlineHint)
.Case("jumptable", Attribute::JumpTable)
.Case("minsize", Attribute::MinSize)
.Case("naked", Attribute::Naked)
.Case("nobuiltin", Attribute::NoBuiltin)
.Case("noduplicate", Attribute::NoDuplicate)
.Case("noimplicitfloat", Attribute::NoImplicitFloat)
.Case("noinline", Attribute::NoInline)
.Case("nonlazybind", Attribute::NonLazyBind)
.Case("noredzone", Attribute::NoRedZone)
.Case("noreturn", Attribute::NoReturn)
.Case("nocf_check", Attribute::NoCfCheck)
.Case("norecurse", Attribute::NoRecurse)
.Case("nounwind", Attribute::NoUnwind)
.Case("nosanitize_coverage", Attribute::NoSanitizeCoverage)
.Case("optforfuzzing", Attribute::OptForFuzzing)
.Case("optnone", Attribute::OptimizeNone)
.Case("optsize", Attribute::OptimizeForSize)
.Case("readnone", Attribute::ReadNone)
.Case("readonly", Attribute::ReadOnly)
.Case("argmemonly", Attribute::ArgMemOnly)
.Case("returns_twice", Attribute::ReturnsTwice)
.Case("safestack", Attribute::SafeStack)
.Case("shadowcallstack", Attribute::ShadowCallStack)
.Case("sanitize_address", Attribute::SanitizeAddress)
.Case("sanitize_hwaddress", Attribute::SanitizeHWAddress)
.Case("sanitize_memory", Attribute::SanitizeMemory)
.Case("sanitize_thread", Attribute::SanitizeThread)
.Case("sanitize_memtag", Attribute::SanitizeMemTag)
.Case("speculative_load_hardening", Attribute::SpeculativeLoadHardening)
.Case("ssp", Attribute::StackProtect)
.Case("sspreq", Attribute::StackProtectReq)
.Case("sspstrong", Attribute::StackProtectStrong)
.Case("strictfp", Attribute::StrictFP)
.Case("uwtable", Attribute::UWTable)
.Case("vscale_range", Attribute::VScaleRange)
.Default(Attribute::None);
}
/// If F has any forced attributes given on the command line, add them.
/// If F has any forced remove attributes given on the command line, remove
/// them. When both force and force-remove are given to a function, the latter
@ -88,10 +42,10 @@ static void forceAttributes(Function &F) {
auto KV = StringRef(S).split(':');
if (KV.first != F.getName())
return Kind;
Kind = parseAttrKind(KV.second);
if (Kind == Attribute::None) {
Kind = Attribute::getAttrKindFromName(KV.second);
if (Kind == Attribute::None || !Attribute::canUseAsFnAttr(Kind)) {
LLVM_DEBUG(dbgs() << "ForcedAttribute: " << KV.second
<< " unknown or not handled!\n");
<< " unknown or not a function attribute!\n");
}
return Kind;
};

View File

@ -106,7 +106,7 @@ TEST(VerifierTest, InvalidRetAttribute) {
raw_string_ostream ErrorOS(Error);
EXPECT_TRUE(verifyModule(M, &ErrorOS));
EXPECT_TRUE(StringRef(ErrorOS.str()).startswith(
"Attribute 'uwtable' only applies to functions!"));
"Attribute 'uwtable' does not apply to function return values"));
}
TEST(VerifierTest, CrossModuleRef) {

View File

@ -25,6 +25,7 @@ public:
private:
void emitTargetIndependentNames(raw_ostream &OS);
void emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr);
void emitAttributeProperties(raw_ostream &OF);
RecordKeeper &Records;
};
@ -109,9 +110,26 @@ void Attributes::emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr) {
OS << "#endif\n";
}
void Attributes::emitAttributeProperties(raw_ostream &OS) {
OS << "#ifdef GET_ATTR_PROP_TABLE\n";
OS << "#undef GET_ATTR_PROP_TABLE\n";
OS << "static const uint8_t AttrPropTable[] = {\n";
for (StringRef KindName : {"EnumAttr", "TypeAttr", "IntAttr"}) {
for (auto A : Records.getAllDerivedDefinitions(KindName)) {
OS << "0";
for (Init *P : *A->getValueAsListInit("Properties"))
OS << " | AttributeProperty::" << cast<DefInit>(P)->getDef()->getName();
OS << ",\n";
}
}
OS << "};\n";
OS << "#endif\n";
}
void Attributes::emit(raw_ostream &OS) {
emitTargetIndependentNames(OS);
emitFnAttrCompatCheck(OS, false);
emitAttributeProperties(OS);
}
namespace llvm {