https://github.com/spavloff updated https://github.com/llvm/llvm-project/pull/226826
>From 43f30af4420b573ab220d2d8d8be22821fd16708 Mon Sep 17 00:00:00 2001 From: Serge Pavlov <[email protected]> Date: Fri, 25 Sep 2026 18:55:05 +0700 Subject: [PATCH 1/2] [IR] Memory effects for floating-point operations Floating-point operations in a strictfp function have side effects, which are modeled using memory effects in the form of a read-write access to "inaccessible memory". This helps maintain strictfp semantics but may hinder optimizations. Floating-point operations may depend on rounding mode or not - this fact may be used to reorder them in a more optimal way. Similarly, functions that control floating-point environment (like `set_rounding`, `set_fpmode` etc.) also have more specific access than generic read-write. Also, "inaccessible memory" is used in cases other than FP operation, this results in unnecessary restrictions. This change implements two new memory location to use instead of the access to "inaccessible memory". The "fpcontrol" location is used to represent access to floating-point control modes, of which only rounding mode is currently supported. The other location, "fpstatus", represents access to floating-point exceptions. Together they replace the use of "inaccessible memory". The new memory locations are not supported in all cases. The Attributor does not support them now, as these locations do not exactly represent memory. Writing to a floating-point control register inside a function does not mean that the function itself changes some floating-point control mode. Supporting these locations in the Attributor requires additional considerations and, probably, new attributes. The meaning of `MemoryEffects::onlyAccessesInaccessibleMem` is changed in this commit. Now this method checks not only the "inaccessible memory" but also floating-point environment. This change agrees with the current usage of the method in the compiler sources and will hopefully reduce the risk on misuse. This change only introduces the new memory locations. Their use in the functions that access the floating-point environment will be implemented in another commit. This MR was obtained from https://github.com/llvm/llvm-project/pull/221754 by keeping only the changes required to introduce the new memory locations only. --- llvm/include/llvm/AsmParser/LLToken.h | 2 + llvm/include/llvm/Support/ModRef.h | 76 +++++++++++++++++-- llvm/include/llvm/Transforms/IPO/Attributor.h | 4 +- llvm/lib/Analysis/AliasAnalysis.cpp | 2 + llvm/lib/Analysis/BasicAliasAnalysis.cpp | 7 +- llvm/lib/Analysis/GlobalsModRef.cpp | 2 +- llvm/lib/AsmParser/LLLexer.cpp | 2 + llvm/lib/AsmParser/LLParser.cpp | 9 ++- llvm/lib/IR/Attributes.cpp | 40 +++++++--- llvm/lib/Support/ModRef.cpp | 6 ++ llvm/lib/Transforms/IPO/Attributor.cpp | 4 +- .../Transforms/IPO/AttributorAttributes.cpp | 8 +- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 2 +- .../Instrumentation/AddressSanitizer.cpp | 2 + .../test/Assembler/memory-attribute-errors.ll | 6 +- llvm/test/Assembler/memory-attribute.ll | 30 ++++++++ llvm/unittests/Support/ModRefTest.cpp | 3 +- 17 files changed, 172 insertions(+), 33 deletions(-) diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index 5624410497ac8..7ce7004e39df1 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -212,6 +212,8 @@ enum Kind { kw_target_mem0, kw_target_mem1, kw_errnomem, + kw_fpcontrol, + kw_fpstatus, // Legacy attributes: kw_argmemonly, diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h index 83091c617f629..91bcebff7ee08 100644 --- a/llvm/include/llvm/Support/ModRef.h +++ b/llvm/include/llvm/Support/ModRef.h @@ -69,10 +69,15 @@ enum class IRMemLocation { /// Represents target specific state. TargetMem0 = 4, TargetMem1 = 5, + /// Floating-point control modes. + FPControl = 6, + /// Floating-point status bits. + FPStatus = 7, /// Helpers to iterate all locations in the MemoryEffectsBase class. First = ArgMem, - Last = TargetMem1, + LastMem = TargetMem1, + Last = FPStatus, }; template <typename LocationEnum> class MemoryEffectsBase { @@ -102,11 +107,22 @@ template <typename LocationEnum> class MemoryEffectsBase { return enum_seq_inclusive(Location::First, Location::Last, force_iteration_on_noniterable_enum); } + /// Returns iterator over all supported memory location kinds, without + /// floating-point registers. + static auto mem_locations() { + return enum_seq_inclusive(Location::First, Location::LastMem, + force_iteration_on_noniterable_enum); + } /// Returns iterator over all target location kinds static auto targetMemLocations() { return enum_seq_inclusive(Location::TargetMem0, Location::TargetMem1, force_iteration_on_noniterable_enum); } + /// Returns iterator over floating-point state locations. + static auto fpenvLocations() { + return enum_seq_inclusive(Location::FPControl, Location::FPStatus, + force_iteration_on_noniterable_enum); + } /// Create MemoryEffectsBase that can access only the given location with the /// given ModRefInfo. @@ -119,24 +135,42 @@ template <typename LocationEnum> class MemoryEffectsBase { setModRef(Loc, MR); } + /// Create MemoryEffectsBase that can access any location with the given + /// ModRefInfo, excluding floating-point locations. + /// The boolean argument value is not used, it only allows to distinguish + /// between constructors. + MemoryEffectsBase(ModRefInfo MR, bool NoFPEnvMarker) { + (void)NoFPEnvMarker; + for (Location Loc : mem_locations()) + setModRef(Loc, MR); + } + /// Create MemoryEffectsBase that can read and write any memory. static MemoryEffectsBase unknown() { return MemoryEffectsBase(ModRefInfo::ModRef); } + /// Create MemoryEffectsBase that can read and write any memory except + /// floating-point registers. + static MemoryEffectsBase unknown_mem() { + return MemoryEffectsBase(ModRefInfo::ModRef, /*NoFPEnvMarker*/ true); + } + /// Create MemoryEffectsBase that cannot read or write any memory. static MemoryEffectsBase none() { return MemoryEffectsBase(ModRefInfo::NoModRef); } - /// Create MemoryEffectsBase that can read any memory. + /// Create MemoryEffectsBase that can read any memory excluding floating-point + /// registers. static MemoryEffectsBase readOnly() { - return MemoryEffectsBase(ModRefInfo::Ref); + return MemoryEffectsBase(ModRefInfo::Ref, /*NoFPEnvMarker*/ true); } - /// Create MemoryEffectsBase that can write any memory. + /// Create MemoryEffectsBase that can write any memory excluding + /// floating-point registers. static MemoryEffectsBase writeOnly() { - return MemoryEffectsBase(ModRefInfo::Mod); + return MemoryEffectsBase(ModRefInfo::Mod, /*NoFPEnvMarker*/ true); } /// Create MemoryEffectsBase that can only access argument memory. @@ -160,6 +194,14 @@ template <typename LocationEnum> class MemoryEffectsBase { return MemoryEffectsBase(Location::Other, MR); } + /// Create MemoryEffectsBase that can only access floating-point state + /// register. + static MemoryEffectsBase + fpenvironmentOnly(ModRefInfo MR = ModRefInfo::ModRef) { + return MemoryEffectsBase(Location::FPControl, MR) | + MemoryEffectsBase(Location::FPStatus, MR); + } + /// Create MemoryEffectsBase that can only access inaccessible or argument /// memory. static MemoryEffectsBase @@ -261,9 +303,13 @@ template <typename LocationEnum> class MemoryEffectsBase { return isModOrRefSet(getModRef(Location::ArgMem)); } - /// Whether this function only (at most) accesses inaccessible memory. + /// Whether this function only (at most) accesses memory that cannot be + /// addressed by any pointer. bool onlyAccessesInaccessibleMem() const { - return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory(); + return getWithoutLoc(Location::InaccessibleMem) + .getWithoutLoc(Location::FPControl) + .getWithoutLoc(Location::FPStatus) + .doesNotAccessMemory(); } /// Whether this function only (at most) accesses inaccessible or target @@ -295,6 +341,22 @@ template <typename LocationEnum> class MemoryEffectsBase { return true; } + /// Whether location represents floating-point environment. + static bool isFPEnvMemLoc(IRMemLocation Loc) { + for (auto L : fpenvLocations()) + if (Loc == L) + return true; + return false; + } + + /// Whether all floating-point locations are not accessed. + bool doesNotAccessFPEnv() const { + for (auto L : fpenvLocations()) + if (getModRef(L) != ModRefInfo::NoModRef) + return false; + return true; + } + /// Whether this function only (at most) accesses errno memory. bool onlyAccessesErrnoMem() const { return getWithoutLoc(Location::ErrnoMem).doesNotAccessMemory(); diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h index 066883afafb95..300ab39cac410 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -3554,7 +3554,7 @@ struct AANoSync SmallVector<Attribute, 2> Attrs; A.getAttrs(IRP, {Attribute::Memory}, Attrs, IgnoreSubsumingPositions); - MemoryEffects ME = MemoryEffects::unknown(); + MemoryEffects ME = MemoryEffects::unknown_mem(); for (const Attribute &Attr : Attrs) ME &= Attr.getMemoryEffects(); @@ -3771,7 +3771,7 @@ struct AAWillReturn A.getAttrs(IRP, {Attribute::Memory}, Attrs, /* IgnoreSubsumingPositions */ false); - MemoryEffects ME = MemoryEffects::unknown(); + MemoryEffects ME = MemoryEffects::unknown_mem(); for (const Attribute &Attr : Attrs) ME &= Attr.getMemoryEffects(); return ME.onlyReadsMemory(); diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp index f2b73c974a35c..05b784482d8ed 100644 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -263,6 +263,8 @@ getModRefInfoInaccessibleAndTargetMemLoc(const MemoryEffects CallUse, }; addModRefInfoForLoc(IRMemLocation::InaccessibleMem); + for (auto Loc : MemoryEffects::fpenvLocations()) + addModRefInfoForLoc(Loc); for (auto Loc : MemoryEffects::targetMemLocations()) addModRefInfoForLoc(Loc); return Result; diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index fd9a7bb935170..72cf1ac8dfb80 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -971,9 +971,12 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call, return ModRefInfo::Mod; // We can completely ignore inaccessible memory here, because MemoryLocations - // can only reference accessible memory. + // can only reference accessible memory. The same is true for floating-point + // registers. auto ME = AAQI.AAR.getMemoryEffects(Call, AAQI) - .getWithoutLoc(IRMemLocation::InaccessibleMem); + .getWithoutLoc(IRMemLocation::InaccessibleMem) + .getWithoutLoc(IRMemLocation::FPControl) + .getWithoutLoc(IRMemLocation::FPStatus); if (ME.doesNotAccessMemory()) return ModRefInfo::NoModRef; diff --git a/llvm/lib/Analysis/GlobalsModRef.cpp b/llvm/lib/Analysis/GlobalsModRef.cpp index 059ebed787c6b..eb2ba3eba80e6 100644 --- a/llvm/lib/Analysis/GlobalsModRef.cpp +++ b/llvm/lib/Analysis/GlobalsModRef.cpp @@ -237,7 +237,7 @@ void GlobalsAAResult::DeletionCallbackHandle::deleted() { MemoryEffects GlobalsAAResult::getMemoryEffects(const Function *F) { if (FunctionInfo *FI = getFunctionInfo(F)) - return MemoryEffects(FI->getModRefInfo()); + return MemoryEffects(FI->getModRefInfo(), /*NoFPEnvMarker*/ true); return MemoryEffects::unknown(); } diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 97362c614091c..0c36736e248ec 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -727,6 +727,8 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(target_mem); KEYWORD(inaccessiblemem); KEYWORD(errnomem); + KEYWORD(fpcontrol); + KEYWORD(fpstatus); KEYWORD(argmemonly); KEYWORD(inaccessiblememonly); KEYWORD(inaccessiblemem_or_argmemonly); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 408d79fe3402f..02d523d783044 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -2760,6 +2760,10 @@ static SmallVector<MemoryEffects::Location, 2> keywordToLoc(lltok::Kind Tok) { return {Loc::InaccessibleMem}; case lltok::kw_errnomem: return {Loc::ErrnoMem}; + case lltok::kw_fpcontrol: + return {Loc::FPControl}; + case lltok::kw_fpstatus: + return {Loc::FPStatus}; case lltok::kw_target_mem0: return {Loc::TargetMem0}; case lltok::kw_target_mem1: @@ -2835,7 +2839,8 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() { std::optional<ModRefInfo> MR = keywordToModRef(Lex.getKind()); if (!MR) { if (Locs.empty()) - tokError("expected memory location (argmem, inaccessiblemem, errnomem) " + tokError("expected memory location (argmem, inaccessiblemem, errnomem, " + "fpcontrol, fpstatus) " "or access kind (none, read, write, readwrite)"); else tokError("expected access kind (none, read, write, readwrite)"); @@ -2860,7 +2865,7 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() { tokError("default access kind must be specified first"); return std::nullopt; } - ME = MemoryEffects(*MR); + ME = MemoryEffects(*MR, /*NoFPEnvMarker*/ false); } if (EatIfPresent(lltok::rparen)) diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 7f5f5f34af389..313d38afe3989 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -644,13 +644,31 @@ std::string Attribute::getAsString(bool InAttrGrp) const { OS << getModRefStr(OtherMR); } - bool TargetPrintedForAll = false; + bool SkipLocGroup = false; + IRMemLocation LastToSkip; for (auto Loc : MemoryEffects::locations()) { ModRefInfo MR = ME.getModRef(Loc); - if (MR == OtherMR) + if (MR == OtherMR && !MemoryEffects::isFPEnvMemLoc(Loc)) continue; - if (!First && !TargetPrintedForAll) + if (SkipLocGroup) { + if (Loc == LastToSkip) + SkipLocGroup = false; + continue; + } + + if (MemoryEffects::isFPEnvMemLoc(Loc)) { + // If access to a floating-point location is absent, do not print it. + if (ME.doesNotAccessFPEnv()) { + SkipLocGroup = true; + LastToSkip = IRMemLocation::FPStatus; + continue; + } + if (ME.getModRef(Loc) == ModRefInfo::NoModRef) + continue; + } + + if (!First && !SkipLocGroup) OS << ", "; First = false; @@ -658,12 +676,10 @@ std::string Attribute::getAsString(bool InAttrGrp) const { // If more targets are added it should do something like: // memory(target_mem:read, target_mem3:none, target_mem5:write). if (ME.isTargetMemLoc(Loc) && ME.isTargetMemLocSameForAll()) { - if (!TargetPrintedForAll) { - OS << "target_mem: "; - OS << getModRefStr(MR); - TargetPrintedForAll = true; - } - // Only works when target memories are last to be listed in Location. + OS << "target_mem: "; + OS << getModRefStr(MR); + SkipLocGroup = true; + LastToSkip = IRMemLocation::TargetMem1; continue; } @@ -685,6 +701,12 @@ std::string Attribute::getAsString(bool InAttrGrp) const { case IRMemLocation::TargetMem1: OS << "target_mem1: "; break; + case IRMemLocation::FPControl: + OS << "fpcontrol: "; + break; + case IRMemLocation::FPStatus: + OS << "fpstatus: "; + break; } OS << getModRefStr(MR); } diff --git a/llvm/lib/Support/ModRef.cpp b/llvm/lib/Support/ModRef.cpp index 1083c72902c0b..43dbe9d068df0 100644 --- a/llvm/lib/Support/ModRef.cpp +++ b/llvm/lib/Support/ModRef.cpp @@ -55,6 +55,12 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, MemoryEffects ME) { case IRMemLocation::TargetMem1: OS << "TargetMem1: "; break; + case IRMemLocation::FPControl: + OS << "FPControl: "; + break; + case IRMemLocation::FPStatus: + OS << "FPStatus: "; + break; } OS << ME.getModRef(Loc); }); diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index 338240b2f8087..f89e00398c297 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -3116,7 +3116,9 @@ ChangeStatus Attributor::rewriteFunctionSignatures( return !T->isPtrOrPtrVectorTy() || NewFn->hasParamAttribute(ArgNo, Attribute::ReadNone); })) { - NewFn->setMemoryEffects(ME - MemoryEffects::argMemOnly()); + ME -= MemoryEffects::argMemOnly(); + ME -= MemoryEffects::fpenvironmentOnly(); + NewFn->setMemoryEffects(ME); } // Since we have now created the new function, splice the body of the old diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp index c2a896be0064d..6e181603318cc 100644 --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -8196,7 +8196,7 @@ struct AAMemoryBehaviorFunction final : public AAMemoryBehaviorImpl { // we could determine read/write per location. This would also have the // benefit of only one place trying to manifest the memory attribute. Function &F = cast<Function>(getAnchorValue()); - MemoryEffects ME = MemoryEffects::unknown(); + MemoryEffects ME = MemoryEffects::unknown_mem(); if (isAssumedReadNone()) ME = MemoryEffects::none(); else if (isAssumedReadOnly()) @@ -8234,7 +8234,7 @@ struct AAMemoryBehaviorCallSite final ChangeStatus manifest(Attributor &A) override { // TODO: Deduplicate this with AAMemoryBehaviorFunction. CallBase &CB = cast<CallBase>(getAnchorValue()); - MemoryEffects ME = MemoryEffects::unknown(); + MemoryEffects ME = MemoryEffects::unknown_mem(); if (isAssumedReadNone()) ME = MemoryEffects::none(); else if (isAssumedReadOnly()) @@ -8558,7 +8558,7 @@ struct AAMemoryLocationImpl : public AAMemoryLocation { State.addKnownBits(inverseLocation(NO_ARGUMENT_MEM, true, true)); else { // Remove location information, only keep read/write info. - ME = MemoryEffects(ME.getModRef()); + ME = MemoryEffects(ME.getModRef(), /*NoFPEnvMarker*/ true); A.manifestAttrs(IRP, Attribute::getWithMemoryEffects( IRP.getAnchorValue().getContext(), ME), @@ -8572,7 +8572,7 @@ struct AAMemoryLocationImpl : public AAMemoryLocation { NO_INACCESSIBLE_MEM | NO_ARGUMENT_MEM, true, true)); else { // Remove location information, only keep read/write info. - ME = MemoryEffects(ME.getModRef()); + ME = MemoryEffects(ME.getModRef(), /*NoFPEnvMarker*/ true); A.manifestAttrs(IRP, Attribute::getWithMemoryEffects( IRP.getAnchorValue().getContext(), ME), diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index a713ead683476..cb7e9e32a0bc6 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -285,7 +285,7 @@ static void addMemoryAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter, ME |= FnME; RecursiveArgME |= FnRecursiveArgME; // Reached bottom of the lattice, we will not be able to improve the result. - if (ME == MemoryEffects::unknown()) + if (ME == MemoryEffects::unknown_mem()) return; } diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 3e9b0b3d39c47..d9b143676e163 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -655,6 +655,8 @@ void llvm::removeASanIncompatibleFnAttributes(Function &F, bool ReadsArgMem) { // memory. if (!F.getMemoryEffects() .getWithoutLoc(IRMemLocation::InaccessibleMem) + .getWithoutLoc(IRMemLocation::FPControl) + .getWithoutLoc(IRMemLocation::FPStatus) .doesNotAccessMemory() && !isModAndRefSet(F.getMemoryEffects().getModRef(IRMemLocation::Other))) { F.setMemoryEffects(F.getMemoryEffects() | diff --git a/llvm/test/Assembler/memory-attribute-errors.ll b/llvm/test/Assembler/memory-attribute-errors.ll index cf8690e07678e..f581247be185a 100644 --- a/llvm/test/Assembler/memory-attribute-errors.ll +++ b/llvm/test/Assembler/memory-attribute-errors.ll @@ -13,16 +13,16 @@ ; MISSING-ARGS: error: expected '(' declare void @fn() memory ;--- empty.ll -; EMPTY: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite) +; EMPTY: error: expected memory location (argmem, inaccessiblemem, errnomem, fpcontrol, fpstatus) or access kind (none, read, write, readwrite) declare void @fn() memory() ;--- unterminated.ll ; UNTERMINATED: error: unterminated memory attribute declare void @fn() memory(read ;--- invalid-kind.ll -; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite) +; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem, errnomem, fpcontrol, fpstatus) or access kind (none, read, write, readwrite) declare void @fn() memory(foo) ;--- other.ll -; OTHER: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite) +; OTHER: error: expected memory location (argmem, inaccessiblemem, errnomem, fpcontrol, fpstatus) or access kind (none, read, write, readwrite) declare void @fn() memory(other: read) ;--- missing-colon.ll ; MISSING-COLON: error: expected ':' after location diff --git a/llvm/test/Assembler/memory-attribute.ll b/llvm/test/Assembler/memory-attribute.ll index fd98384d90405..614ee14d5586d 100644 --- a/llvm/test/Assembler/memory-attribute.ll +++ b/llvm/test/Assembler/memory-attribute.ll @@ -148,3 +148,33 @@ declare void @fn_write_target_mem_read_write() ; CHECK: @fn_all_readwrite declare void @fn_all_readwrite() memory(readwrite, target_mem: read) + +; CHECK: Function Attrs: memory(fpcontrol: read) +; CHECK: @fn_read_fpcontrol +declare void @fn_read_fpcontrol() + memory(fpcontrol: read) + +; CHECK: Function Attrs: memory(fpcontrol: write) +; CHECK: @fn_write_fpcontrol +declare void @fn_write_fpcontrol() + memory(fpcontrol: write) + +; CHECK: Function Attrs: memory(fpstatus: read) +; CHECK: @fn_read_fpstatus +declare void @fn_read_fpstatus() + memory(fpstatus: read) + +; CHECK: Function Attrs: memory(fpstatus: readwrite) +; CHECK: @fn_readwrite_fpstatus +declare void @fn_readwrite_fpstatus() + memory(fpstatus: readwrite) + +; CHECK: Function Attrs: memory(fpcontrol: read, fpstatus: readwrite) +; CHECK: @fn_read_fpcontrol_readwrite_fpstatus +declare void @fn_read_fpcontrol_readwrite_fpstatus() + memory(fpcontrol: read, fpstatus: readwrite) + +; CHECK: Function Attrs: memory(write, fpcontrol: read) +; CHECK: @fn_read_fpcontrol_write_mem +declare void @fn_read_fpcontrol_write_mem() + memory(write, fpcontrol: read) diff --git a/llvm/unittests/Support/ModRefTest.cpp b/llvm/unittests/Support/ModRefTest.cpp index 128501bf2d957..bbb1048729453 100644 --- a/llvm/unittests/Support/ModRefTest.cpp +++ b/llvm/unittests/Support/ModRefTest.cpp @@ -21,7 +21,8 @@ TEST(ModRefTest, PrintMemoryEffects) { raw_string_ostream OS(S); OS << MemoryEffects::none(); EXPECT_EQ(S, "ArgMem: NoModRef, InaccessibleMem: NoModRef, ErrnoMem: " - "NoModRef, Other: NoModRef, TargetMem0: NoModRef, TargetMem1: NoModRef"); + "NoModRef, Other: NoModRef, TargetMem0: NoModRef, " + "TargetMem1: NoModRef, FPControl: NoModRef, FPStatus: NoModRef"); } } // namespace >From 1f4be034e4c08d744cde85383f238070dab2effa Mon Sep 17 00:00:00 2001 From: Serge Pavlov <[email protected]> Date: Fri, 9 Oct 2026 12:15:25 +0700 Subject: [PATCH 2/2] Update PR according to review notes - Now the FP locations are treated as other kinds of memory, - Add new tests, - Removed changes not necessary for adding the new locations, - Some functions are renamed, - Small enhancements in parsing the new memory location. --- .../CodeGen/sanitize-metadata-nosanitize.c | 18 ++-- clang/test/CodeGenOpenCL/convergent.cl | 2 +- llvm/include/llvm/AsmParser/LLToken.h | 1 + llvm/include/llvm/Support/ModRef.h | 72 +++++++--------- llvm/include/llvm/Transforms/IPO/Attributor.h | 4 +- llvm/lib/Analysis/AliasAnalysis.cpp | 15 ++-- llvm/lib/Analysis/BasicAliasAnalysis.cpp | 7 +- llvm/lib/Analysis/GlobalsModRef.cpp | 2 +- llvm/lib/AsmParser/LLLexer.cpp | 1 + llvm/lib/AsmParser/LLParser.cpp | 31 +++++-- llvm/lib/IR/Attributes.cpp | 25 +++--- llvm/lib/Transforms/IPO/Attributor.cpp | 4 +- .../Transforms/IPO/AttributorAttributes.cpp | 8 +- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 2 +- .../Instrumentation/AddressSanitizer.cpp | 2 - .../test/Assembler/memory-attribute-errors.ll | 3 + llvm/test/Assembler/memory-attribute.ll | 20 +++++ llvm/test/Bitcode/memory-attribute-upgrade.ll | 4 +- .../Transforms/FunctionAttrs/argmemonly.ll | 22 ++--- .../Transforms/FunctionAttrs/nocapture.ll | 36 ++++---- .../FunctionAttrs/read-write-scc.ll | 4 +- .../Transforms/FunctionAttrs/readattrs.ll | 2 +- .../Transforms/FunctionAttrs/writeonly.ll | 4 +- .../GlobalOpt/disable-globals-aa.ll | 8 +- .../InferFunctionAttrs/norecurse_debug.ll | 2 +- .../cfi-nounwind-direct-call.ll | 2 +- .../Transforms/SCCP/ipscp-drop-argmemonly.ll | 10 +-- llvm/unittests/Analysis/AliasAnalysisTest.cpp | 83 +++++++++++++++++++ llvm/unittests/IR/AttributesTest.cpp | 78 +++++++++++++++++ 29 files changed, 329 insertions(+), 143 deletions(-) diff --git a/clang/test/CodeGen/sanitize-metadata-nosanitize.c b/clang/test/CodeGen/sanitize-metadata-nosanitize.c index d92ff26065ba3..d66b32548de70 100644 --- a/clang/test/CodeGen/sanitize-metadata-nosanitize.c +++ b/clang/test/CodeGen/sanitize-metadata-nosanitize.c @@ -10,7 +10,7 @@ // CHECK: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_covered2.module_ctor, ptr @__sanitizer_metadata_covered2.module_ctor }, { i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_atomics2.module_ctor, ptr @__sanitizer_metadata_atomics2.module_ctor }] // CHECK: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_covered2.module_dtor, ptr @__sanitizer_metadata_covered2.module_dtor }, { i32, ptr, ptr } { i32 2, ptr @__sanitizer_metadata_atomics2.module_dtor, ptr @__sanitizer_metadata_atomics2.module_dtor }] //. -// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) // CHECK-LABEL: define dso_local void @escape( // CHECK-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !pcsections [[META6:![0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] @@ -21,7 +21,7 @@ __attribute__((noinline, not_tail_called)) void escape(const volatile void *p) { sink = p; } -// CHECK: Function Attrs: mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) +// CHECK: Function Attrs: mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) // CHECK-LABEL: define dso_local i32 @normal_function( // CHECK-SAME: ptr noundef [[X:%.*]], ptr nofree noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !pcsections [[META8:![0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] @@ -38,7 +38,7 @@ int normal_function(int *x, int *y) { return *y; } -// CHECK: Function Attrs: disable_sanitizer_instrumentation mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) +// CHECK: Function Attrs: disable_sanitizer_instrumentation mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) // CHECK-LABEL: define dso_local i32 @test_disable_sanitize_instrumentation( // CHECK-SAME: ptr noundef [[X:%.*]], ptr nofree noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] @@ -55,7 +55,7 @@ __attribute__((disable_sanitizer_instrumentation)) int test_disable_sanitize_ins return *y; } -// CHECK: Function Attrs: mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) +// CHECK: Function Attrs: mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) // CHECK-LABEL: define dso_local i32 @test_no_sanitize_thread( // CHECK-SAME: ptr noundef [[X:%.*]], ptr nofree noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] !pcsections [[META15:![0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] @@ -72,7 +72,7 @@ __attribute__((no_sanitize("thread"))) int test_no_sanitize_thread(int *x, int * return *y; } -// CHECK: Function Attrs: mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) +// CHECK: Function Attrs: mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) // CHECK-LABEL: define dso_local i32 @test_no_sanitize_all( // CHECK-SAME: ptr noundef [[X:%.*]], ptr nofree noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR3]] !pcsections [[META15]] { // CHECK-NEXT: [[ENTRY:.*:]] @@ -89,10 +89,10 @@ __attribute__((no_sanitize("all"))) int test_no_sanitize_all(int *x, int *y) { return *y; } //. -// CHECK: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } -// CHECK: attributes #[[ATTR1]] = { mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } -// CHECK: attributes #[[ATTR2]] = { disable_sanitizer_instrumentation mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } -// CHECK: attributes #[[ATTR3]] = { mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "no_sanitize_thread" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #[[ATTR1]] = { mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #[[ATTR2]] = { disable_sanitizer_instrumentation mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #[[ATTR3]] = { mustprogress norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) "min-legal-vector-width"="0" "no-trapping-math"="true" "no_sanitize_thread" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } // CHECK: attributes #[[ATTR4:[0-9]+]] = { nounwind "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } //. // CHECK: [[META0:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"} diff --git a/clang/test/CodeGenOpenCL/convergent.cl b/clang/test/CodeGenOpenCL/convergent.cl index 5217b3acd081e..6bc315690b3b4 100644 --- a/clang/test/CodeGenOpenCL/convergent.cl +++ b/clang/test/CodeGenOpenCL/convergent.cl @@ -133,7 +133,7 @@ kernel void assume_convergent_asm() __asm__ volatile("s_barrier"); } -// CHECK: attributes #0 = { nofree noinline norecurse nosync nounwind memory(readwrite, target_mem: none) +// CHECK: attributes #0 = { nofree noinline norecurse nosync nounwind memory(readwrite, target_mem: none, fpenv: none) // CHECK: attributes #1 = { convergent norecurse nounwind{{[^}]*}} } // CHECK: attributes #2 = { convergent nounwind{{[^}]*}} } // CHECK: attributes #3 = { convergent noduplicate nounwind{{[^}]*}} } diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index 7ce7004e39df1..3b26a68ec5d4c 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -212,6 +212,7 @@ enum Kind { kw_target_mem0, kw_target_mem1, kw_errnomem, + kw_fpenv, kw_fpcontrol, kw_fpstatus, diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h index 91bcebff7ee08..c98e7cc5204e2 100644 --- a/llvm/include/llvm/Support/ModRef.h +++ b/llvm/include/llvm/Support/ModRef.h @@ -107,12 +107,6 @@ template <typename LocationEnum> class MemoryEffectsBase { return enum_seq_inclusive(Location::First, Location::Last, force_iteration_on_noniterable_enum); } - /// Returns iterator over all supported memory location kinds, without - /// floating-point registers. - static auto mem_locations() { - return enum_seq_inclusive(Location::First, Location::LastMem, - force_iteration_on_noniterable_enum); - } /// Returns iterator over all target location kinds static auto targetMemLocations() { return enum_seq_inclusive(Location::TargetMem0, Location::TargetMem1, @@ -135,42 +129,24 @@ template <typename LocationEnum> class MemoryEffectsBase { setModRef(Loc, MR); } - /// Create MemoryEffectsBase that can access any location with the given - /// ModRefInfo, excluding floating-point locations. - /// The boolean argument value is not used, it only allows to distinguish - /// between constructors. - MemoryEffectsBase(ModRefInfo MR, bool NoFPEnvMarker) { - (void)NoFPEnvMarker; - for (Location Loc : mem_locations()) - setModRef(Loc, MR); - } - /// Create MemoryEffectsBase that can read and write any memory. static MemoryEffectsBase unknown() { return MemoryEffectsBase(ModRefInfo::ModRef); } - /// Create MemoryEffectsBase that can read and write any memory except - /// floating-point registers. - static MemoryEffectsBase unknown_mem() { - return MemoryEffectsBase(ModRefInfo::ModRef, /*NoFPEnvMarker*/ true); - } - /// Create MemoryEffectsBase that cannot read or write any memory. static MemoryEffectsBase none() { return MemoryEffectsBase(ModRefInfo::NoModRef); } - /// Create MemoryEffectsBase that can read any memory excluding floating-point - /// registers. + /// Create MemoryEffectsBase that can read any memory. static MemoryEffectsBase readOnly() { - return MemoryEffectsBase(ModRefInfo::Ref, /*NoFPEnvMarker*/ true); + return MemoryEffectsBase(ModRefInfo::Ref); } - /// Create MemoryEffectsBase that can write any memory excluding - /// floating-point registers. + /// Create MemoryEffectsBase that can write any memory. static MemoryEffectsBase writeOnly() { - return MemoryEffectsBase(ModRefInfo::Mod, /*NoFPEnvMarker*/ true); + return MemoryEffectsBase(ModRefInfo::Mod); } /// Create MemoryEffectsBase that can only access argument memory. @@ -196,10 +172,28 @@ template <typename LocationEnum> class MemoryEffectsBase { /// Create MemoryEffectsBase that can only access floating-point state /// register. - static MemoryEffectsBase - fpenvironmentOnly(ModRefInfo MR = ModRefInfo::ModRef) { - return MemoryEffectsBase(Location::FPControl, MR) | - MemoryEffectsBase(Location::FPStatus, MR); + static MemoryEffectsBase fpenvOnly(ModRefInfo ControlMR = ModRefInfo::ModRef, + ModRefInfo StatusMR = ModRefInfo::ModRef) { + MemoryEffectsBase Result = none(); + Result.setModRef(Location::FPControl, ControlMR); + Result.setModRef(Location::FPStatus, StatusMR); + return Result; + } + + /// Create MemoryEffectsBase that can only access floating-point + /// control modes. + static MemoryEffectsBase fpcontrolOnly(ModRefInfo MR = ModRefInfo::ModRef) { + MemoryEffectsBase Result = none(); + Result.setModRef(Location::FPControl, MR); + return Result; + } + + /// Create MemoryEffectsBase that can only access floating-point + /// status bits. + static MemoryEffectsBase fpstatusOnly(ModRefInfo MR = ModRefInfo::ModRef) { + MemoryEffectsBase Result = none(); + Result.setModRef(Location::FPStatus, MR); + return Result; } /// Create MemoryEffectsBase that can only access inaccessible or argument @@ -303,21 +297,19 @@ template <typename LocationEnum> class MemoryEffectsBase { return isModOrRefSet(getModRef(Location::ArgMem)); } - /// Whether this function only (at most) accesses memory that cannot be - /// addressed by any pointer. + /// Whether this function only (at most) accesses inaccessible memory. bool onlyAccessesInaccessibleMem() const { - return getWithoutLoc(Location::InaccessibleMem) - .getWithoutLoc(Location::FPControl) - .getWithoutLoc(Location::FPStatus) - .doesNotAccessMemory(); + return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory(); } /// Whether this function only (at most) accesses inaccessible or target - /// memory. - bool onlyAccessesInaccessibleOrTargetMem() const { + /// memory or floating-point environment. + bool onlyAccessesNonaddressableMem() const { MemoryEffectsBase ME = *this; for (auto Loc : MemoryEffectsBase::targetMemLocations()) ME &= ME.getWithoutLoc(Loc); + for (auto Loc : MemoryEffectsBase::fpenvLocations()) + ME &= ME.getWithoutLoc(Loc); return ME.getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory(); } diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h index 300ab39cac410..066883afafb95 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -3554,7 +3554,7 @@ struct AANoSync SmallVector<Attribute, 2> Attrs; A.getAttrs(IRP, {Attribute::Memory}, Attrs, IgnoreSubsumingPositions); - MemoryEffects ME = MemoryEffects::unknown_mem(); + MemoryEffects ME = MemoryEffects::unknown(); for (const Attribute &Attr : Attrs) ME &= Attr.getMemoryEffects(); @@ -3771,7 +3771,7 @@ struct AAWillReturn A.getAttrs(IRP, {Attribute::Memory}, Attrs, /* IgnoreSubsumingPositions */ false); - MemoryEffects ME = MemoryEffects::unknown_mem(); + MemoryEffects ME = MemoryEffects::unknown(); for (const Attribute &Attr : Attrs) ME &= Attr.getMemoryEffects(); return ME.onlyReadsMemory(); diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp index 05b784482d8ed..90e860a6133a1 100644 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -245,9 +245,8 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call, return Result; } -ModRefInfo -getModRefInfoInaccessibleAndTargetMemLoc(const MemoryEffects CallUse, - const MemoryEffects CallDef) { +ModRefInfo getModRefInfoNonaddressableMemLoc(const MemoryEffects CallUse, + const MemoryEffects CallDef) { ModRefInfo Result = ModRefInfo::NoModRef; auto addModRefInfoForLoc = [&](IRMemLocation L) { @@ -374,11 +373,11 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call1, return R; } - // If only Inaccessible and Target Memory Location have set ModRefInfo - // then check the relation between the same locations. - if (Call1B.onlyAccessesInaccessibleOrTargetMem() && - Call2B.onlyAccessesInaccessibleOrTargetMem()) - return getModRefInfoInaccessibleAndTargetMemLoc(Call1B, Call2B); + // If only Inaccessible, Target and floating-point Memory Location have + // set ModRefInfo then check the relation between the same locations. + if (Call1B.onlyAccessesNonaddressableMem() && + Call2B.onlyAccessesNonaddressableMem()) + return getModRefInfoNonaddressableMemLoc(Call1B, Call2B); return Result; } diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index 72cf1ac8dfb80..fd9a7bb935170 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -971,12 +971,9 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call, return ModRefInfo::Mod; // We can completely ignore inaccessible memory here, because MemoryLocations - // can only reference accessible memory. The same is true for floating-point - // registers. + // can only reference accessible memory. auto ME = AAQI.AAR.getMemoryEffects(Call, AAQI) - .getWithoutLoc(IRMemLocation::InaccessibleMem) - .getWithoutLoc(IRMemLocation::FPControl) - .getWithoutLoc(IRMemLocation::FPStatus); + .getWithoutLoc(IRMemLocation::InaccessibleMem); if (ME.doesNotAccessMemory()) return ModRefInfo::NoModRef; diff --git a/llvm/lib/Analysis/GlobalsModRef.cpp b/llvm/lib/Analysis/GlobalsModRef.cpp index eb2ba3eba80e6..059ebed787c6b 100644 --- a/llvm/lib/Analysis/GlobalsModRef.cpp +++ b/llvm/lib/Analysis/GlobalsModRef.cpp @@ -237,7 +237,7 @@ void GlobalsAAResult::DeletionCallbackHandle::deleted() { MemoryEffects GlobalsAAResult::getMemoryEffects(const Function *F) { if (FunctionInfo *FI = getFunctionInfo(F)) - return MemoryEffects(FI->getModRefInfo(), /*NoFPEnvMarker*/ true); + return MemoryEffects(FI->getModRefInfo()); return MemoryEffects::unknown(); } diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 0c36736e248ec..bb48a40772b53 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -729,6 +729,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(errnomem); KEYWORD(fpcontrol); KEYWORD(fpstatus); + KEYWORD(fpenv); KEYWORD(argmemonly); KEYWORD(inaccessiblememonly); KEYWORD(inaccessiblemem_or_argmemonly); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 02d523d783044..88a625791add8 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -2760,10 +2760,6 @@ static SmallVector<MemoryEffects::Location, 2> keywordToLoc(lltok::Kind Tok) { return {Loc::InaccessibleMem}; case lltok::kw_errnomem: return {Loc::ErrnoMem}; - case lltok::kw_fpcontrol: - return {Loc::FPControl}; - case lltok::kw_fpstatus: - return {Loc::FPStatus}; case lltok::kw_target_mem0: return {Loc::TargetMem0}; case lltok::kw_target_mem1: @@ -2774,6 +2770,16 @@ static SmallVector<MemoryEffects::Location, 2> keywordToLoc(lltok::Kind Tok) { Targets.push_back(Loc); return Targets; } + case lltok::kw_fpcontrol: + return {Loc::FPControl}; + case lltok::kw_fpstatus: + return {Loc::FPStatus}; + case lltok::kw_fpenv: { + SmallVector<MemoryEffects::Location, 2> FPLocs; + for (auto Loc : MemoryEffects::fpenvLocations()) + FPLocs.push_back(Loc); + return FPLocs; + } default: return {}; } @@ -2826,6 +2832,7 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() { bool SeenLoc = false; bool SeenTargetLoc = false; + bool SeenFPEnvLoc = false; do { SmallVector<IRMemLocation, 2> Locs = keywordToLoc(Lex.getKind()); if (!Locs.empty()) { @@ -2850,22 +2857,30 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() { Lex.Lex(); if (!Locs.empty()) { SeenLoc = true; + bool IsTargetLoc = ME.isTargetMemLoc(Locs.front()); + bool IsFPEnvLoc = ME.isFPEnvMemLoc(Locs.front()); for (IRMemLocation Loc : Locs) { ME = ME.getWithModRef(Loc, *MR); - if (ME.isTargetMemLoc(Loc) && Locs.size() == 1) + if (IsTargetLoc && Locs.size() == 1) SeenTargetLoc = true; + else if (IsFPEnvLoc && Locs.size() == 1) + SeenFPEnvLoc = true; } - if (Locs.size() > 1 && SeenTargetLoc) { + if (IsTargetLoc && Locs.size() > 1 && SeenTargetLoc) { tokError("target memory default access kind must be specified first"); return std::nullopt; } - + if (IsFPEnvLoc && Locs.size() > 1 && SeenFPEnvLoc) { + tokError("floating-point environment default access kind " + "must be specified first"); + return std::nullopt; + } } else { if (SeenLoc) { tokError("default access kind must be specified first"); return std::nullopt; } - ME = MemoryEffects(*MR, /*NoFPEnvMarker*/ false); + ME = MemoryEffects(*MR); } if (EatIfPresent(lltok::rparen)) diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 313d38afe3989..ef09e3fa01453 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -646,9 +646,13 @@ std::string Attribute::getAsString(bool InAttrGrp) const { bool SkipLocGroup = false; IRMemLocation LastToSkip; + const auto isFPEnvMemLocSameForAll = [](MemoryEffects ME) -> bool { + return ME.getModRef(IRMemLocation::FPControl) == + ME.getModRef(IRMemLocation::FPStatus); + }; for (auto Loc : MemoryEffects::locations()) { ModRefInfo MR = ME.getModRef(Loc); - if (MR == OtherMR && !MemoryEffects::isFPEnvMemLoc(Loc)) + if (MR == OtherMR) continue; if (SkipLocGroup) { @@ -657,21 +661,18 @@ std::string Attribute::getAsString(bool InAttrGrp) const { continue; } - if (MemoryEffects::isFPEnvMemLoc(Loc)) { - // If access to a floating-point location is absent, do not print it. - if (ME.doesNotAccessFPEnv()) { - SkipLocGroup = true; - LastToSkip = IRMemLocation::FPStatus; - continue; - } - if (ME.getModRef(Loc) == ModRefInfo::NoModRef) - continue; - } - if (!First && !SkipLocGroup) OS << ", "; First = false; + if (MemoryEffects::isFPEnvMemLoc(Loc) && isFPEnvMemLocSameForAll(ME)) { + OS << "fpenv: "; + OS << getModRefStr(MR); + SkipLocGroup = true; + LastToSkip = IRMemLocation::FPStatus; + continue; + } + // isTargetMemLocSameForAll is fine for target location < 3 // If more targets are added it should do something like: // memory(target_mem:read, target_mem3:none, target_mem5:write). diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index f89e00398c297..338240b2f8087 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -3116,9 +3116,7 @@ ChangeStatus Attributor::rewriteFunctionSignatures( return !T->isPtrOrPtrVectorTy() || NewFn->hasParamAttribute(ArgNo, Attribute::ReadNone); })) { - ME -= MemoryEffects::argMemOnly(); - ME -= MemoryEffects::fpenvironmentOnly(); - NewFn->setMemoryEffects(ME); + NewFn->setMemoryEffects(ME - MemoryEffects::argMemOnly()); } // Since we have now created the new function, splice the body of the old diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp index 6e181603318cc..c2a896be0064d 100644 --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -8196,7 +8196,7 @@ struct AAMemoryBehaviorFunction final : public AAMemoryBehaviorImpl { // we could determine read/write per location. This would also have the // benefit of only one place trying to manifest the memory attribute. Function &F = cast<Function>(getAnchorValue()); - MemoryEffects ME = MemoryEffects::unknown_mem(); + MemoryEffects ME = MemoryEffects::unknown(); if (isAssumedReadNone()) ME = MemoryEffects::none(); else if (isAssumedReadOnly()) @@ -8234,7 +8234,7 @@ struct AAMemoryBehaviorCallSite final ChangeStatus manifest(Attributor &A) override { // TODO: Deduplicate this with AAMemoryBehaviorFunction. CallBase &CB = cast<CallBase>(getAnchorValue()); - MemoryEffects ME = MemoryEffects::unknown_mem(); + MemoryEffects ME = MemoryEffects::unknown(); if (isAssumedReadNone()) ME = MemoryEffects::none(); else if (isAssumedReadOnly()) @@ -8558,7 +8558,7 @@ struct AAMemoryLocationImpl : public AAMemoryLocation { State.addKnownBits(inverseLocation(NO_ARGUMENT_MEM, true, true)); else { // Remove location information, only keep read/write info. - ME = MemoryEffects(ME.getModRef(), /*NoFPEnvMarker*/ true); + ME = MemoryEffects(ME.getModRef()); A.manifestAttrs(IRP, Attribute::getWithMemoryEffects( IRP.getAnchorValue().getContext(), ME), @@ -8572,7 +8572,7 @@ struct AAMemoryLocationImpl : public AAMemoryLocation { NO_INACCESSIBLE_MEM | NO_ARGUMENT_MEM, true, true)); else { // Remove location information, only keep read/write info. - ME = MemoryEffects(ME.getModRef(), /*NoFPEnvMarker*/ true); + ME = MemoryEffects(ME.getModRef()); A.manifestAttrs(IRP, Attribute::getWithMemoryEffects( IRP.getAnchorValue().getContext(), ME), diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index cb7e9e32a0bc6..a713ead683476 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -285,7 +285,7 @@ static void addMemoryAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter, ME |= FnME; RecursiveArgME |= FnRecursiveArgME; // Reached bottom of the lattice, we will not be able to improve the result. - if (ME == MemoryEffects::unknown_mem()) + if (ME == MemoryEffects::unknown()) return; } diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index d9b143676e163..3e9b0b3d39c47 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -655,8 +655,6 @@ void llvm::removeASanIncompatibleFnAttributes(Function &F, bool ReadsArgMem) { // memory. if (!F.getMemoryEffects() .getWithoutLoc(IRMemLocation::InaccessibleMem) - .getWithoutLoc(IRMemLocation::FPControl) - .getWithoutLoc(IRMemLocation::FPStatus) .doesNotAccessMemory() && !isModAndRefSet(F.getMemoryEffects().getModRef(IRMemLocation::Other))) { F.setMemoryEffects(F.getMemoryEffects() | diff --git a/llvm/test/Assembler/memory-attribute-errors.ll b/llvm/test/Assembler/memory-attribute-errors.ll index f581247be185a..d35fada4fbd6d 100644 --- a/llvm/test/Assembler/memory-attribute-errors.ll +++ b/llvm/test/Assembler/memory-attribute-errors.ll @@ -36,3 +36,6 @@ declare void @fn() memory(argmem: read, write) ;--- default-after-target-loc.ll ; DEFAULT-AFTER-TARGET-LOC: error: target memory default access kind must be specified first declare void @fn() memory(target_mem0: read, target_mem:write) +;--- default-after-fpenv-loc.ll +; DEFAULT-AFTER-FPENV-LOC: error: floating-point environment default access kind must be specified first +declare void @fn() memory(fccontrol: read, fpenv:write) diff --git a/llvm/test/Assembler/memory-attribute.ll b/llvm/test/Assembler/memory-attribute.ll index 614ee14d5586d..a4701dc5318d0 100644 --- a/llvm/test/Assembler/memory-attribute.ll +++ b/llvm/test/Assembler/memory-attribute.ll @@ -169,6 +169,11 @@ declare void @fn_read_fpstatus() declare void @fn_readwrite_fpstatus() memory(fpstatus: readwrite) +; CHECK: Function Attrs: memory(fpenv: readwrite) +; CHECK: @fn_readwrite_fpenv +declare void @fn_readwrite_fpenv() + memory(fpenv: readwrite) + ; CHECK: Function Attrs: memory(fpcontrol: read, fpstatus: readwrite) ; CHECK: @fn_read_fpcontrol_readwrite_fpstatus declare void @fn_read_fpcontrol_readwrite_fpstatus() @@ -178,3 +183,18 @@ declare void @fn_read_fpcontrol_readwrite_fpstatus() ; CHECK: @fn_read_fpcontrol_write_mem declare void @fn_read_fpcontrol_write_mem() memory(write, fpcontrol: read) + +; CHECK: Function Attrs: memory(write, target_mem: readwrite, fpcontrol: read) +; CHECK: @fn_read_fpcontrol_rw_target_write_mem +declare void @fn_read_fpcontrol_rw_target_write_mem() + memory(write, target_mem: readwrite, fpcontrol: read) + +; CHECK: Function Attrs: memory(write, target_mem0: read, fpenv: read) +; CHECK: @fn_read_fpenv_read_target0_write_mem +declare void @fn_read_fpenv_read_target0_write_mem() + memory(write, target_mem0: read, fpenv: read) + +; CHECK: Function Attrs: memory(write, target_mem: readwrite, fpenv: read) +; CHECK: @fn_read_fpenv_rw_target_write_mem +declare void @fn_read_fpenv_rw_target_write_mem() + memory(write, target_mem: readwrite, fpenv: read) diff --git a/llvm/test/Bitcode/memory-attribute-upgrade.ll b/llvm/test/Bitcode/memory-attribute-upgrade.ll index 2d8eb12eaccf7..42d7e011e69aa 100644 --- a/llvm/test/Bitcode/memory-attribute-upgrade.ll +++ b/llvm/test/Bitcode/memory-attribute-upgrade.ll @@ -1,7 +1,7 @@ ; RUN: llvm-dis < %S/Inputs/memory-attribute-upgrade.bc | FileCheck %s -; CHECK: ; Function Attrs: memory(write, argmem: read, target_mem: none) +; CHECK: ; Function Attrs: memory(write, argmem: read, target_mem: none, fpenv: none) ; CHECK-NEXT: define void @test_any_write_argmem_read(ptr %p) -; CHECK: ; Function Attrs: memory(read, argmem: readwrite, inaccessiblemem: none, target_mem: none) +; CHECK: ; Function Attrs: memory(read, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; CHECK-NEXT: define void @test_any_read_argmem_readwrite(ptr %p) diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll index b1a44c94fd046..e60325eb2663f 100644 --- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll @@ -56,7 +56,7 @@ entry: } define i32 @test_read_global() { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i32 @test_read_global ; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] { ; FNATTRS-NEXT: entry: @@ -76,7 +76,7 @@ entry: } define i32 @test_read_loaded_ptr(ptr %ptr) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i32 @test_read_loaded_ptr ; FNATTRS-SAME: (ptr nofree readonly captures(none) [[PTR:%.*]]) #[[ATTR3:[0-9]+]] { ; FNATTRS-NEXT: entry: @@ -119,7 +119,7 @@ entry: } define void @test_write_global() { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test_write_global ; FNATTRS-SAME: () #[[ATTR5:[0-9]+]] { ; FNATTRS-NEXT: entry: @@ -243,7 +243,7 @@ declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1) @arr = global [32 x i8] zeroinitializer define void @test_memcpy_src_global(ptr %dst) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test_memcpy_src_global ; FNATTRS-SAME: (ptr nofree writeonly captures(none) initializes((0, 32)) [[DST:%.*]]) #[[ATTR11:[0-9]+]] { ; FNATTRS-NEXT: entry: @@ -263,7 +263,7 @@ entry: } define void @test_memcpy_dst_global(ptr %src) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test_memcpy_dst_global ; FNATTRS-SAME: (ptr nofree readonly captures(none) [[SRC:%.*]]) #[[ATTR11]] { ; FNATTRS-NEXT: entry: @@ -388,7 +388,7 @@ define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) { } define void @test_recursive_argmem_read(ptr %p) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test_recursive_argmem_read ; FNATTRS-SAME: (ptr nofree readonly captures(none) [[P:%.*]]) #[[ATTR16:[0-9]+]] { ; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 @@ -408,7 +408,7 @@ define void @test_recursive_argmem_read(ptr %p) { } define void @test_recursive_argmem_readwrite(ptr %p) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(readwrite, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test_recursive_argmem_readwrite ; FNATTRS-SAME: (ptr nofree captures(none) [[P:%.*]]) #[[ATTR17:[0-9]+]] { ; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 @@ -454,7 +454,7 @@ define void @test_recursive_argmem_read_alloca(ptr %p) { } define void @test_scc_argmem_read_1(ptr %p) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test_scc_argmem_read_1 ; FNATTRS-SAME: (ptr nofree readonly captures(none) [[P:%.*]]) #[[ATTR16]] { ; FNATTRS-NEXT: [[PVAL:%.*]] = load ptr, ptr [[P]], align 8 @@ -474,7 +474,7 @@ define void @test_scc_argmem_read_1(ptr %p) { } define void @test_scc_argmem_read_2(ptr %p) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test_scc_argmem_read_2 ; FNATTRS-SAME: (ptr nofree readonly captures(none) [[P:%.*]]) #[[ATTR16]] { ; FNATTRS-NEXT: call void @test_scc_argmem_read_1(ptr [[P]]) @@ -518,7 +518,7 @@ entry: ; FIXME: This could be `memory(argmem: read)`. define i64 @select_different_obj(i1 %c, ptr %p, ptr %p2) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i64 @select_different_obj ; FNATTRS-SAME: (i1 [[C:%.*]], ptr nofree readonly captures(none) [[P:%.*]], ptr nofree readonly captures(none) [[P2:%.*]]) #[[ATTR3]] { ; FNATTRS-NEXT: entry: @@ -567,7 +567,7 @@ join: ; FIXME: This could be `memory(argmem: read)`. define i64 @phi_different_obj(i1 %c, ptr %p, ptr %p2) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i64 @phi_different_obj ; FNATTRS-SAME: (i1 [[C:%.*]], ptr nofree readonly captures(none) [[P:%.*]], ptr nofree readonly captures(none) [[P2:%.*]]) #[[ATTR3]] { ; FNATTRS-NEXT: entry: diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll index fc5daa18cd3c8..a7c235ed81d5e 100644 --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -20,7 +20,7 @@ define ptr @c1(ptr %q) { ; It would also be acceptable to mark %q as readnone. Update @c3 too. define void @c2(ptr %q) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @c2 ; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR1:[0-9]+]] { ; FNATTRS-NEXT: store ptr [[Q]], ptr @g, align 8 @@ -37,7 +37,7 @@ define void @c2(ptr %q) { } define void @c3(ptr %q) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @c3 ; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR2:[0-9]+]] { ; FNATTRS-NEXT: call void @c2(ptr [[Q]]) @@ -127,7 +127,7 @@ l1: @lookup_table = global [2 x i1] [ i1 0, i1 1 ] define i1 @c5(ptr %q, i32 %bitno) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i1 @c5 ; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR3:[0-9]+]] { ; FNATTRS-NEXT: [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32 @@ -222,7 +222,7 @@ define ptr @lookup_bit(ptr %q, i32 %bitno) readnone nounwind { } define i1 @c7(ptr %q, i32 %bitno) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i1 @c7 ; FNATTRS-SAME: (ptr nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR6:[0-9]+]] { ; FNATTRS-NEXT: [[PTR:%.*]] = call ptr @lookup_bit(ptr [[Q]], i32 [[BITNO]]) @@ -243,7 +243,7 @@ define i1 @c7(ptr %q, i32 %bitno) { define i32 @nc1(ptr %q, ptr %p, i1 %b) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i32 @nc1 ; FNATTRS-SAME: (ptr [[Q:%.*]], ptr nofree captures(none) [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7:[0-9]+]] { ; FNATTRS-NEXT: e: @@ -284,7 +284,7 @@ l: } define i32 @nc1_addrspace(ptr %q, ptr addrspace(1) %p, i1 %b) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define i32 @nc1_addrspace ; FNATTRS-SAME: (ptr [[Q:%.*]], ptr addrspace(1) nofree captures(none) [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7]] { ; FNATTRS-NEXT: e: @@ -328,7 +328,7 @@ l: } define void @nc2(ptr %p, ptr %q) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @nc2 ; FNATTRS-SAME: (ptr nofree captures(none) [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] { ; FNATTRS-NEXT: [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false) @@ -468,7 +468,7 @@ define void @self_readonly_nounwind_willreturn(ptr %p) readonly nounwind willret ; It would be acceptable to add readnone to %y1_1 and %y1_2. define void @test1_1(ptr %x1_1, ptr %y1_1, i1 %c) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test1_1 ; FNATTRS-SAME: (ptr nofree readnone captures(none) [[X1_1:%.*]], ptr [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR12:[0-9]+]] { ; FNATTRS-NEXT: [[TMP1:%.*]] = call ptr @test1_2(ptr [[X1_1]], ptr [[Y1_1]], i1 [[C]]) @@ -488,7 +488,7 @@ define void @test1_1(ptr %x1_1, ptr %y1_1, i1 %c) { } define ptr @test1_2(ptr %x1_2, ptr %y1_2, i1 %c) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define ptr @test1_2 ; FNATTRS-SAME: (ptr nofree readnone captures(none) [[X1_2:%.*]], ptr returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR12]] { ; FNATTRS-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] @@ -520,7 +520,7 @@ f: } define void @test2(ptr %x2) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test2 ; FNATTRS-SAME: (ptr nofree readnone captures(none) [[X2:%.*]]) #[[ATTR12]] { ; FNATTRS-NEXT: call void @test2(ptr [[X2]]) @@ -540,7 +540,7 @@ define void @test2(ptr %x2) { } define void @test3(ptr %x3, ptr %y3, ptr %z3) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test3 ; FNATTRS-SAME: (ptr nofree readnone captures(none) [[X3:%.*]], ptr nofree readnone captures(none) [[Y3:%.*]], ptr nofree readnone captures(none) [[Z3:%.*]]) #[[ATTR12]] { ; FNATTRS-NEXT: call void @test3(ptr [[Z3]], ptr [[Y3]], ptr [[X3]]) @@ -560,7 +560,7 @@ define void @test3(ptr %x3, ptr %y3, ptr %z3) { } define void @test4_1(ptr %x4_1, i1 %c) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @test4_1 ; FNATTRS-SAME: (ptr [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR12]] { ; FNATTRS-NEXT: [[TMP1:%.*]] = call ptr @test4_2(ptr [[X4_1]], ptr [[X4_1]], ptr [[X4_1]], i1 [[C]]) @@ -580,7 +580,7 @@ define void @test4_1(ptr %x4_1, i1 %c) { } define ptr @test4_2(ptr %x4_2, ptr %y4_2, ptr %z4_2, i1 %c) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define ptr @test4_2 ; FNATTRS-SAME: (ptr nofree readnone captures(none) [[X4_2:%.*]], ptr nofree readnone returned captures(ret: address, provenance) [[Y4_2:%.*]], ptr nofree readnone captures(none) [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR12]] { ; FNATTRS-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] @@ -744,7 +744,7 @@ entry: @g2 = global ptr null define void @captureLaunder(ptr %p) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @captureLaunder ; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR16:[0-9]+]] { ; FNATTRS-NEXT: [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) @@ -1042,7 +1042,7 @@ define i64 @captures_not_ret_only(ptr %p) { ;; Unlike ptrtoint, ptrtoaddr only captures the address define i64 @captures_ptrtoaddr_stored(ptr %p) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define noundef i64 @captures_ptrtoaddr_stored ; FNATTRS-SAME: (ptr nofree readnone captures(address) [[P:%.*]]) #[[ATTR1]] { ; FNATTRS-NEXT: [[INT:%.*]] = ptrtoaddr ptr [[P]] to i64 @@ -1145,7 +1145,7 @@ define ptr @captures_used_ret(ptr %p) { ; Make sure this is does not produce captures(ret: ...). We need to take the ; return capture components into account when handling argument SCCs. define ptr @scc_capture_via_ret(i1 %c, ptr %p) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define ptr @scc_capture_via_ret ; FNATTRS-SAME: (i1 [[C:%.*]], ptr [[P:%.*]]) #[[ATTR12]] { ; FNATTRS-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]] @@ -1247,7 +1247,7 @@ define void @dont_increase_existing_captures_scc2(ptr %p) { } define void @addr_only_scc(ptr %p) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @addr_only_scc ; FNATTRS-SAME: (ptr nofree readonly captures(address_is_null) [[P:%.*]]) #[[ATTR19:[0-9]+]] { ; FNATTRS-NEXT: [[V:%.*]] = load i8, ptr [[P]], align 1 @@ -1270,7 +1270,7 @@ define void @addr_only_scc(ptr %p) { } define void @addr_only_scc2(ptr %p) { -; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define void @addr_only_scc2 ; FNATTRS-SAME: (ptr nofree readonly captures(address_is_null) [[P:%.*]]) #[[ATTR19]] { ; FNATTRS-NEXT: [[CMP:%.*]] = icmp ne ptr [[P]], null diff --git a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll index 36baf6be754c9..75582608ead10 100644 --- a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll +++ b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll @@ -4,7 +4,7 @@ @i = global i32 0 define void @foo() { -; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, target_mem: none) +; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; CHECK-LABEL: define {{[^@]+}}@foo ; CHECK-SAME: () #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: store i32 1, ptr @i, align 4 @@ -17,7 +17,7 @@ define void @foo() { } define void @bar() { -; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, target_mem: none) +; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; CHECK-LABEL: define {{[^@]+}}@bar ; CHECK-SAME: () #[[ATTR0]] { ; CHECK-NEXT: [[I:%.*]] = load i32, ptr @i, align 4 diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll index fbd7b7436ce6d..dcc0703b9ea62 100644 --- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -33,7 +33,7 @@ define void @test1_2(ptr %x1_2, ptr %y1_2, ptr %z1_2) { ; TODO: Missing with attributor-light: argmem: none, inaccessiblemem: none define ptr @test2(ptr %p) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define {{[^@]+}}@test2 ; FNATTRS-SAME: (ptr nofree readnone returned captures(ret: address, provenance) [[P:%.*]]) #[[ATTR0:[0-9]+]] { ; FNATTRS-NEXT: store i32 0, ptr @x, align 4 diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll index 9d6fa0ef0efe8..4a64cfeec3bdf 100644 --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -38,7 +38,7 @@ nouses-argworn-funro_entry: @d-ccc = internal global %_type_of_d-ccc <{ ptr null, i8 1, i8 13, i8 0, i8 -127 }>, align 8 define void @nouses-argworn-funwo(ptr writeonly %.aaa) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funwo ; FNATTRS-SAME: (ptr nofree readnone captures(none) [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] { ; FNATTRS-NEXT: nouses-argworn-funwo_entry: @@ -76,7 +76,7 @@ define void @test_store(ptr %p) { @G = external global ptr define i8 @test_store_capture(ptr %p) { -; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none, target_mem: none) +; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none, target_mem: none, fpenv: none) ; FNATTRS-LABEL: define {{[^@]+}}@test_store_capture ; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] { ; FNATTRS-NEXT: store ptr [[P]], ptr @G, align 8 diff --git a/llvm/test/Transforms/GlobalOpt/disable-globals-aa.ll b/llvm/test/Transforms/GlobalOpt/disable-globals-aa.ll index df620416154d7..a6136b37e440b 100644 --- a/llvm/test/Transforms/GlobalOpt/disable-globals-aa.ll +++ b/llvm/test/Transforms/GlobalOpt/disable-globals-aa.ll @@ -44,11 +44,11 @@ define i8 @dummy() !prof !0 { !0 = !{!"function_entry_count", i32 10} ;. -; WITHGLOBALSAA: attributes #[[ATTR0:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none, target_mem: none) } -; WITHGLOBALSAA: attributes #[[ATTR1:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none) } +; WITHGLOBALSAA: attributes #[[ATTR0:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none, target_mem: none, fpenv: none) } +; WITHGLOBALSAA: attributes #[[ATTR1:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) } ;. -; NOGLOBALSAA: attributes #[[ATTR0:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none) } -; NOGLOBALSAA: attributes #[[ATTR1:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none) } +; NOGLOBALSAA: attributes #[[ATTR0:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) } +; NOGLOBALSAA: attributes #[[ATTR1:[0-9]+]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) } ;. ; WITHGLOBALSAA: [[META0:![0-9]+]] = !{!"function_entry_count", i32 10} ; WITHGLOBALSAA: [[PROF1]] = !{!"unknown", !"globalopt"} diff --git a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll index 0a0fdc0d4d42a..05c5311aec779 100644 --- a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll +++ b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll @@ -52,5 +52,5 @@ attributes #1 = { nounwind readnone speculatable } !28 = !DILocation(line: 9, column: 18, scope: !2) !29 = !DILocation(line: 10, column: 1, scope: !2) -; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none, target_mem: none) } +; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none, target_mem: none, fpenv: none) } ; CHECK-NOT: foo.coefficient1 diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll b/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll index c626a5f4d59c9..e0ea8e8e62c42 100644 --- a/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll +++ b/llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll @@ -117,7 +117,7 @@ attributes #6 = { noreturn nounwind } ; CHECK-NEXT: ret i32 [[DOT]] ; ; -; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none) +; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(write, argmem: none, inaccessiblemem: none, target_mem: none, fpenv: none) ; CHECK-LABEL: define dso_local noundef range(i32 0, 2) i32 @_Z10call_catchi ; CHECK-SAME: (i32 noundef [[NUM:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !type [[META4]] !type [[META5]] !type [[META6]] { ; CHECK-NEXT: entry: diff --git a/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll b/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll index 4e7597e930207..6eb3e276eb252 100644 --- a/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll +++ b/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll @@ -14,7 +14,7 @@ ; CHECK: @[[G:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0 ;. define internal void @ptrarg.1(ptr %arg, i32 %val) argmemonly nounwind { -; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, target_mem: none) +; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; CHECK-LABEL: @ptrarg.1( ; CHECK-NEXT: store i32 10, ptr @g, align 4 ; CHECK-NEXT: ret void @@ -62,7 +62,7 @@ define void @caller.2(ptr %ptr) { ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop inaccessiblemem_or_argmemonly. define internal void @ptrarg.3(ptr %arg, i32 %val) inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: nounwind memory(readwrite, target_mem: none) +; CHECK: Function Attrs: nounwind memory(readwrite, target_mem: none, fpenv: none) ; CHECK-LABEL: @ptrarg.3( ; CHECK-NEXT: store i32 10, ptr @g, align 4 ; CHECK-NEXT: ret void @@ -110,7 +110,7 @@ define void @caller.4(ptr %ptr) { ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop inaccessiblemem_or_argmemonly. define internal void @ptrarg.5(ptr %arg, i32 %val) argmemonly inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, target_mem: none) +; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) ; CHECK-LABEL: @ptrarg.5( ; CHECK-NEXT: store i32 10, ptr @g, align 4 ; CHECK-NEXT: ret void @@ -163,9 +163,9 @@ define i32 @caller.6.cs.attributes(i32 %n) { } ;. -; CHECK: attributes #[[ATTR0]] = { nounwind memory(readwrite, inaccessiblemem: none, target_mem: none) } +; CHECK: attributes #[[ATTR0]] = { nounwind memory(readwrite, inaccessiblemem: none, target_mem: none, fpenv: none) } ; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind memory(argmem: readwrite) } -; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite, target_mem: none) } +; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite, target_mem: none, fpenv: none) } ; CHECK: attributes #[[ATTR3:[0-9]+]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) } ; CHECK: attributes #[[ATTR4]] = { nounwind } ;. diff --git a/llvm/unittests/Analysis/AliasAnalysisTest.cpp b/llvm/unittests/Analysis/AliasAnalysisTest.cpp index 7be74310e1ae7..d80b4ad241637 100644 --- a/llvm/unittests/Analysis/AliasAnalysisTest.cpp +++ b/llvm/unittests/Analysis/AliasAnalysisTest.cpp @@ -13,6 +13,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" @@ -221,6 +222,88 @@ TEST_F(AliasAnalysisTest, getModRefInfo) { EXPECT_EQ(AA.getModRefInfo(Foo, Foo), ModRefInfo::ModRef); } +TEST_F(AliasAnalysisTest, getFPModRefInfo) { + IRBuilder<> Builder(C); + auto PtrType = PointerType::get(C, 0); + auto FloatType = Type::getFloatTy(C); + + // Setup function. + auto *FTy = + FunctionType::get(Type::getVoidTy(C), {PtrType, FloatType}, false); + auto *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); + auto *BB = BasicBlock::Create(C, "entry", F); + Builder.SetInsertPoint(BB); + auto *Addr = F->getArg(0); + auto *Val = F->getArg(1); + + auto *Store = Builder.CreateStore(Val, Addr); + auto *Load = Builder.CreateLoad(FloatType, Addr); + + auto *FuncTy = FunctionType::get(Type::getVoidTy(C), {}, false); + auto *F1 = Function::Create(FuncTy, Function::ExternalLinkage, "f1", &M); + auto *F2 = Function::Create(FuncTy, Function::ExternalLinkage, "f2", &M); + F2->setDoesNotAccessMemory(); + auto *F3 = Function::Create(FuncTy, Function::ExternalLinkage, "f3", &M); + F3->setOnlyWritesMemory(); + + auto *CallF1 = Builder.CreateCall(F1); + auto *CallF2 = Builder.CreateCall(F2); + auto *CallF3 = Builder.CreateCall(F3); + + // Function that only reads FP control modes. + auto *FReadC = Function::Create(FuncTy, Function::ExternalLinkage, "rc", &M); + FReadC->setMemoryEffects( + MemoryEffects::fpenvOnly(ModRefInfo::Ref, ModRefInfo::NoModRef)); + auto *ReadFPC = Builder.CreateCall(FReadC); + + // Function that only writes FP control modes. + auto *FWriteC = Function::Create(FuncTy, Function::ExternalLinkage, "wc", &M); + FWriteC->setMemoryEffects( + MemoryEffects::fpenvOnly(ModRefInfo::Mod, ModRefInfo::NoModRef)); + auto *WriteFPC = Builder.CreateCall(FWriteC); + + // Function that only reads/writes FP status. + auto *FRWS = Function::Create(FuncTy, Function::ExternalLinkage, "rws", &M); + FRWS->setMemoryEffects( + MemoryEffects::fpenvOnly(ModRefInfo::NoModRef, ModRefInfo::ModRef)); + auto *ReadWriteFPS = Builder.CreateCall(FRWS); + + // Function that only reads/writes FP status and reads FP control modes. + auto *FFPOp = Function::Create(FuncTy, Function::ExternalLinkage, "fpop", &M); + FFPOp->setMemoryEffects( + MemoryEffects::fpenvOnly(ModRefInfo::Ref, ModRefInfo::ModRef)); + auto *FPOp = Builder.CreateCall(FFPOp); + + ReturnInst::Create(C, nullptr, BB); + + auto &AA = getAAResults(*F); + + EXPECT_EQ(AA.getModRefInfo(ReadFPC, Load), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(ReadFPC, Store), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, Load), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, Store), ModRefInfo::NoModRef); + + EXPECT_EQ(AA.getModRefInfo(ReadFPC, CallF1), ModRefInfo::Ref); + EXPECT_EQ(AA.getModRefInfo(ReadFPC, CallF2), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(ReadFPC, CallF3), ModRefInfo::Ref); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, CallF1), ModRefInfo::Mod); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, CallF2), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, CallF3), ModRefInfo::Mod); + + EXPECT_EQ(AA.getModRefInfo(ReadFPC, WriteFPC), ModRefInfo::Ref); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, ReadFPC), ModRefInfo::Mod); + EXPECT_EQ(AA.getModRefInfo(ReadFPC, ReadFPC), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, WriteFPC), ModRefInfo::Mod); + + EXPECT_EQ(AA.getModRefInfo(ReadFPC, ReadWriteFPS), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, ReadWriteFPS), ModRefInfo::NoModRef); + + EXPECT_EQ(AA.getModRefInfo(ReadFPC, FPOp), ModRefInfo::NoModRef); + EXPECT_EQ(AA.getModRefInfo(WriteFPC, FPOp), ModRefInfo::Mod); + EXPECT_EQ(AA.getModRefInfo(ReadWriteFPS, FPOp), ModRefInfo::ModRef); + EXPECT_EQ(AA.getModRefInfo(FPOp, FPOp), ModRefInfo::ModRef); +} + static Instruction *getInstructionByName(Function &F, StringRef Name) { for (auto &I : instructions(F)) if (I.getName() == Name) diff --git a/llvm/unittests/IR/AttributesTest.cpp b/llvm/unittests/IR/AttributesTest.cpp index 5e795ef771713..4eedb0941bd5c 100644 --- a/llvm/unittests/IR/AttributesTest.cpp +++ b/llvm/unittests/IR/AttributesTest.cpp @@ -818,4 +818,82 @@ TEST(Attributes, ListIntersect) { ASSERT_TRUE(Res->hasParamAttr(3, Attribute::ByVal)); } +TEST(Attributes, AsString) { + LLVMContext C; + Attribute Attr; + + { + MemoryEffects ME = MemoryEffects::none(); + for (auto Loc : MemoryEffects::targetMemLocations()) + ME |= MemoryEffects(Loc, ModRefInfo::ModRef); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(target_mem: readwrite)", Attr.getAsString()); + } + { + MemoryEffects ME = + MemoryEffects(IRMemLocation::TargetMem0, ModRefInfo::ModRef) | + MemoryEffects(IRMemLocation::TargetMem1, ModRefInfo::Ref); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(target_mem0: readwrite, target_mem1: read)", + Attr.getAsString()); + } + { + MemoryEffects ME = MemoryEffects::otherMemOnly(ModRefInfo::Ref); + for (auto Loc : MemoryEffects::targetMemLocations()) + ME |= MemoryEffects(Loc, ModRefInfo::Ref); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(read, argmem: none, inaccessiblemem: none, " + "errnomem: none, fpenv: none)", + Attr.getAsString()); + } + { + MemoryEffects ME = + MemoryEffects::otherMemOnly(ModRefInfo::Ref) | + MemoryEffects(IRMemLocation::TargetMem0, ModRefInfo::ModRef) | + MemoryEffects(IRMemLocation::TargetMem1, ModRefInfo::Ref); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(read, argmem: none, inaccessiblemem: none, " + "errnomem: none, target_mem0: readwrite, fpenv: none)", + Attr.getAsString()); + } + + { + MemoryEffects ME = MemoryEffects::fpcontrolOnly(); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(fpcontrol: readwrite)", Attr.getAsString()); + } + { + MemoryEffects ME = MemoryEffects::fpcontrolOnly(ModRefInfo::Ref); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(fpcontrol: read)", Attr.getAsString()); + } + { + MemoryEffects ME = MemoryEffects::otherMemOnly(ModRefInfo::Ref) | + MemoryEffects::fpcontrolOnly(ModRefInfo::Ref); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(read, argmem: none, inaccessiblemem: none, " + "errnomem: none, target_mem: none, fpstatus: none)", + Attr.getAsString()); + } + { + MemoryEffects ME = MemoryEffects::otherMemOnly(ModRefInfo::Ref) | + MemoryEffects::fpcontrolOnly(ModRefInfo::Ref) | + MemoryEffects::fpstatusOnly(ModRefInfo::Ref); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(read, argmem: none, inaccessiblemem: none, " + "errnomem: none, target_mem: none)", + Attr.getAsString()); + } + { + MemoryEffects ME = MemoryEffects::otherMemOnly(ModRefInfo::Mod) | + MemoryEffects::fpcontrolOnly(ModRefInfo::Ref) | + MemoryEffects::fpstatusOnly(ModRefInfo::ModRef); + Attr = Attribute::get(C, Attribute::Memory, ME.toIntValue()); + EXPECT_EQ("memory(write, argmem: none, inaccessiblemem: none, " + "errnomem: none, target_mem: none, fpcontrol: read, " + "fpstatus: readwrite)", + Attr.getAsString()); + } +} + } // end anonymous namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
