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

Reply via email to