[Mlir-commits] [clang] [llvm] [mlir] [IR][ModRef] Introduce `errno` memory location (PR #120783)

Antonio Frighetto llvmlistbot at llvm.org
Wed Feb 5 14:46:33 PST 2025


https://github.com/antoniofrighetto updated https://github.com/llvm/llvm-project/pull/120783

>From 8d64748bb7093866ba2900916237f58a203cb511 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Fri, 20 Dec 2024 19:30:59 +0100
Subject: [PATCH 1/2] [IR][ModRef] Introduce `errno` memory location

Model C/C++ `errno` macro by adding a corresponding `errno`
memory location kind to the IR. Preliminary work to separate
`errno` writes from other memory accesses, to the benefit of
alias analyses and optimization correctness.

Previous discussion: https://discourse.llvm.org/t/rfc-modelling-errno-memory-effects/82972.
---
 .../CodeGen/sanitize-metadata-nosanitize.c    | 18 ++---
 clang/test/CodeGenOpenCL/convergent.cl        |  2 +-
 llvm/include/llvm/AsmParser/LLToken.h         |  5 ++
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |  4 ++
 llvm/include/llvm/IR/Function.h               | 20 ++++++
 llvm/include/llvm/IR/InstrTypes.h             | 20 ++++++
 llvm/include/llvm/Support/ModRef.h            | 68 +++++++++++++++++-
 llvm/lib/AsmParser/LLLexer.cpp                |  5 ++
 llvm/lib/AsmParser/LLParser.cpp               | 16 ++++-
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     | 37 ++++++++++
 llvm/lib/IR/Attributes.cpp                    |  3 +
 llvm/lib/IR/Function.cpp                      | 39 ++++++++++
 llvm/lib/IR/Instructions.cpp                  | 39 ++++++++++
 llvm/lib/Support/ModRef.cpp                   |  3 +
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp     | 14 ++++
 .../test/Assembler/memory-attribute-errors.ll |  6 +-
 llvm/test/Assembler/memory-attribute.ll       | 12 ++++
 .../Transforms/FunctionAttrs/argmemonly.ll    | 22 +++---
 .../Transforms/FunctionAttrs/nocapture.ll     | 30 ++++----
 .../FunctionAttrs/read-write-errnomem.ll      | 71 +++++++++++++++++++
 .../FunctionAttrs/read-write-scc.ll           |  4 +-
 .../Transforms/FunctionAttrs/readattrs.ll     |  2 +-
 .../Transforms/FunctionAttrs/writeonly.ll     |  4 +-
 .../InferFunctionAttrs/norecurse_debug.ll     |  2 +-
 .../cfi-nounwind-direct-call.ll               |  2 +-
 .../Transforms/SCCP/ipscp-drop-argmemonly.ll  | 12 ++--
 llvm/unittests/Support/ModRefTest.cpp         |  4 +-
 mlir/test/Target/LLVMIR/llvmir.mlir           | 10 +--
 28 files changed, 413 insertions(+), 61 deletions(-)
 create mode 100644 llvm/test/Transforms/FunctionAttrs/read-write-errnomem.ll

diff --git a/clang/test/CodeGen/sanitize-metadata-nosanitize.c b/clang/test/CodeGen/sanitize-metadata-nosanitize.c
index eabcbd1409fe2b8..ff98d0b21e3f115 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)
+// CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 // CHECK-LABEL: define dso_local void @escape
 // CHECK-SAME: (ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !pcsections [[META2:![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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
 // CHECK-LABEL: define dso_local i32 @normal_function
 // CHECK-SAME: (ptr noundef [[X:%.*]], ptr noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !pcsections [[META4:![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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: disable_sanitizer_instrumentation mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
 // CHECK-LABEL: define dso_local i32 @test_disable_sanitize_instrumentation
 // CHECK-SAME: (ptr noundef [[X:%.*]], ptr 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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
 // CHECK-LABEL: define dso_local i32 @test_no_sanitize_thread
 // CHECK-SAME: (ptr noundef [[X:%.*]], ptr noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] !pcsections [[META14:![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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none)
+// CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: none)
 // CHECK-LABEL: define dso_local i32 @test_no_sanitize_all
 // CHECK-SAME: (ptr noundef [[X:%.*]], ptr noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR3]] !pcsections [[META14]] {
 // 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) "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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: 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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: 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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: 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, errnomem: 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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: 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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: 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 nofree norecurse nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none, errnomem: 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]+]] = !{i32 1, !"wchar_size", i32 4}
diff --git a/clang/test/CodeGenOpenCL/convergent.cl b/clang/test/CodeGenOpenCL/convergent.cl
index 123adba7b40d2c1..d64915205aabf6c 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 nounwind "
+// CHECK: attributes #0 = { nofree noinline norecurse nounwind memory(readwrite, errnomem: none) "
 // CHECK: attributes #1 = { {{[^}]*}}convergent{{[^}]*}} }
 // CHECK: attributes #2 = { {{[^}]*}}convergent{{[^}]*}} }
 // CHECK: attributes #3 = { {{[^}]*}}convergent noduplicate{{[^}]*}} }
diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h
index 300d50c0959b7cd..233fe7f78964b01 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -201,11 +201,16 @@ enum Kind {
   kw_readwrite,
   kw_argmem,
   kw_inaccessiblemem,
+  kw_errnomem,
 
   // Legacy attributes:
   kw_argmemonly,
   kw_inaccessiblememonly,
   kw_inaccessiblemem_or_argmemonly,
+  kw_inaccessiblemem_or_errnomemonly,
+  kw_argmem_or_errnomemonly,
+  kw_inaccessiblemem_or_argmem_or_errnomemonly,
+  kw_errnomemonly,
   kw_nocapture,
 
   // Captures attribute:
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 9eb38c3e4482910..1dd72b479fb93e3 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -789,6 +789,10 @@ enum AttributeKindCodes {
   ATTR_KIND_NO_DIVERGENCE_SOURCE = 100,
   ATTR_KIND_SANITIZE_TYPE = 101,
   ATTR_KIND_CAPTURES = 102,
+  ATTR_KIND_ERRNOMEMONLY = 103,
+  ATTR_KIND_INACCESSIBLEMEM_OR_ERRNOMEMONLY = 104,
+  ATTR_KIND_ARGMEM_OR_ERRNOMEMONLY = 105,
+  ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEM_OR_ERRNOMEMONLY = 106,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index fcd5396ccfdbc87..ef41582bf635351 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -587,11 +587,31 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
   bool onlyAccessesInaccessibleMemory() const;
   void setOnlyAccessesInaccessibleMemory();
 
+  /// Determine if the function may only access errno memory.
+  bool onlyAccessesErrnoMemory() const;
+  void setOnlyAccessesErrnoMemory();
+
   /// Determine if the function may only access memory that is
   ///  either inaccessible from the IR or pointed to by its arguments.
   bool onlyAccessesInaccessibleMemOrArgMem() const;
   void setOnlyAccessesInaccessibleMemOrArgMem();
 
+  /// Determine if the function may only access memory that is
+  ///  either pointed to by its arguments or errno memory.
+  bool onlyAccessesArgMemOrErrnoMem() const;
+  void setOnlyAccessesArgMemOrErrnoMem();
+
+  /// Determine if the function may only access memory that is
+  ///  either inaccessible from the IR or errno memory.
+  bool onlyAccessesInaccessibleMemOrErrnoMem() const;
+  void setOnlyAccessesInaccessibleMemOrErrnoMem();
+
+  /// Determine if the function may only access memory that is
+  ///  either inaccessible from the IR, pointed to by its arguments, or errno
+  ///  memory.
+  bool onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const;
+  void setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem();
+
   /// Determine if the function cannot return.
   bool doesNotReturn() const {
     return hasFnAttribute(Attribute::NoReturn);
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 26be02d4b193de6..5d4e24bc423cd0c 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1915,11 +1915,31 @@ class CallBase : public Instruction {
   bool onlyAccessesInaccessibleMemory() const;
   void setOnlyAccessesInaccessibleMemory();
 
+  /// Determine if the function may only access errno memory.
+  bool onlyAccessesErrnoMemory() const;
+  void setOnlyAccessesErrnoMemory();
+
   /// Determine if the function may only access memory that is
   /// either inaccessible from the IR or pointed to by its arguments.
   bool onlyAccessesInaccessibleMemOrArgMem() const;
   void setOnlyAccessesInaccessibleMemOrArgMem();
 
+  /// Determine if the function may only access memory that is
+  ///  either inaccessible from the IR or errno memory.
+  bool onlyAccessesInaccessibleMemOrErrnoMem() const;
+  void setOnlyAccessesInaccessibleMemOrErrnoMem();
+
+  /// Determine if the function may only access memory that is
+  ///  either pointed to by its arguments or errno memory.
+  bool onlyAccessesArgMemOrErrnoMem() const;
+  void setOnlyAccessesArgMemOrErrnoMem();
+
+  /// Determine if the function may only access memory that is
+  ///  either inaccessible from the IR, pointed to by its arguments, or errno
+  ///  memory.
+  bool onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const;
+  void setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem();
+
   /// Determine if the call cannot return.
   bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); }
   void setDoesNotReturn() { addFnAttr(Attribute::NoReturn); }
diff --git a/llvm/include/llvm/Support/ModRef.h b/llvm/include/llvm/Support/ModRef.h
index a8ce9a8e6e69c47..f54deb911c7c80d 100644
--- a/llvm/include/llvm/Support/ModRef.h
+++ b/llvm/include/llvm/Support/ModRef.h
@@ -61,8 +61,10 @@ enum class IRMemLocation {
   ArgMem = 0,
   /// Memory that is inaccessible via LLVM IR.
   InaccessibleMem = 1,
+  /// Errno memory.
+  ErrnoMem = 2,
   /// Any other memory.
-  Other = 2,
+  Other = 3,
 
   /// Helpers to iterate all locations in the MemoryEffectsBase class.
   First = ArgMem,
@@ -139,6 +141,11 @@ template <typename LocationEnum> class MemoryEffectsBase {
     return MemoryEffectsBase(Location::InaccessibleMem, MR);
   }
 
+  /// Create MemoryEffectsBase that can only access errno memory.
+  static MemoryEffectsBase errnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
+    return MemoryEffectsBase(Location::ErrnoMem, MR);
+  }
+
   /// Create MemoryEffectsBase that can only access inaccessible or argument
   /// memory.
   static MemoryEffectsBase
@@ -149,6 +156,36 @@ template <typename LocationEnum> class MemoryEffectsBase {
     return FRMB;
   }
 
+  /// Create MemoryEffectsBase that can only access inaccessible or errno
+  /// memory.
+  static MemoryEffectsBase
+  inaccessibleOrErrnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
+    MemoryEffectsBase FRMB = none();
+    FRMB.setModRef(Location::ErrnoMem, MR);
+    FRMB.setModRef(Location::InaccessibleMem, MR);
+    return FRMB;
+  }
+
+  /// Create MemoryEffectsBase that can only access argument or errno memory.
+  static MemoryEffectsBase
+  argumentOrErrnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
+    MemoryEffectsBase FRMB = none();
+    FRMB.setModRef(Location::ArgMem, MR);
+    FRMB.setModRef(Location::ErrnoMem, MR);
+    return FRMB;
+  }
+
+  /// Create MemoryEffectsBase that can only access inaccessible, argument or
+  /// errno memory.
+  static MemoryEffectsBase
+  inaccessibleOrArgOrErrnoMemOnly(ModRefInfo MR = ModRefInfo::ModRef) {
+    MemoryEffectsBase FRMB = none();
+    FRMB.setModRef(Location::ArgMem, MR);
+    FRMB.setModRef(Location::ErrnoMem, MR);
+    FRMB.setModRef(Location::InaccessibleMem, MR);
+    return FRMB;
+  }
+
   /// Create MemoryEffectsBase from an encoded integer value (used by memory
   /// attribute).
   static MemoryEffectsBase createFromIntValue(uint32_t Data) {
@@ -212,6 +249,11 @@ template <typename LocationEnum> class MemoryEffectsBase {
     return getWithoutLoc(Location::InaccessibleMem).doesNotAccessMemory();
   }
 
+  /// Whether this function only (at most) accesses errno memory.
+  bool onlyAccessesErrnoMem() const {
+    return getWithoutLoc(Location::ErrnoMem).doesNotAccessMemory();
+  }
+
   /// Whether this function only (at most) accesses argument and inaccessible
   /// memory.
   bool onlyAccessesInaccessibleOrArgMem() const {
@@ -220,6 +262,30 @@ template <typename LocationEnum> class MemoryEffectsBase {
         .doesNotAccessMemory();
   }
 
+  /// Whether this function only (at most) accesses inaccessible and errno
+  /// memory.
+  bool onlyAccessesInaccessibleOrErrnoMem() const {
+    return getWithoutLoc(Location::InaccessibleMem)
+        .getWithoutLoc(Location::ErrnoMem)
+        .doesNotAccessMemory();
+  }
+
+  /// Whether this function only (at most) accesses argument and errno memory.
+  bool onlyAccessesArgumentOrErrnoMem() const {
+    return getWithoutLoc(Location::ArgMem)
+        .getWithoutLoc(Location::ErrnoMem)
+        .doesNotAccessMemory();
+  }
+
+  /// Whether this function only (at most) accesses inaccessible, argument and
+  /// errno memory.
+  bool onlyAccessesInaccessibleOrArgOrErrnoMem() const {
+    return getWithoutLoc(Location::InaccessibleMem)
+        .getWithoutLoc(Location::ArgMem)
+        .getWithoutLoc(Location::ErrnoMem)
+        .doesNotAccessMemory();
+  }
+
   /// Intersect with other MemoryEffectsBase.
   MemoryEffectsBase operator&(MemoryEffectsBase Other) const {
     return MemoryEffectsBase(Data & Other.Data);
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index d2549b79a430582..9e34655241d1386 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -701,9 +701,14 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(readwrite);
   KEYWORD(argmem);
   KEYWORD(inaccessiblemem);
+  KEYWORD(errnomem);
   KEYWORD(argmemonly);
   KEYWORD(inaccessiblememonly);
+  KEYWORD(errnomemonly);
   KEYWORD(inaccessiblemem_or_argmemonly);
+  KEYWORD(inaccessiblemem_or_errnomemonly);
+  KEYWORD(argmem_or_errnomemonly);
+  KEYWORD(inaccessiblemem_or_argmem_or_errnomemonly);
   KEYWORD(nocapture);
   KEYWORD(address_is_null);
   KEYWORD(address);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 642d7cc403610a6..579d2bca4f7426a 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1671,9 +1671,21 @@ static bool upgradeMemoryAttr(MemoryEffects &ME, lltok::Kind Kind) {
   case lltok::kw_inaccessiblememonly:
     ME &= MemoryEffects::inaccessibleMemOnly();
     return true;
+  case lltok::kw_errnomemonly:
+    ME &= MemoryEffects::errnoMemOnly();
+    return true;
   case lltok::kw_inaccessiblemem_or_argmemonly:
     ME &= MemoryEffects::inaccessibleOrArgMemOnly();
     return true;
+  case lltok::kw_inaccessiblemem_or_errnomemonly:
+    ME &= MemoryEffects::inaccessibleOrErrnoMemOnly();
+    return true;
+  case lltok::kw_argmem_or_errnomemonly:
+    ME &= MemoryEffects::argumentOrErrnoMemOnly();
+    return true;
+  case lltok::kw_inaccessiblemem_or_argmem_or_errnomemonly:
+    ME &= MemoryEffects::inaccessibleOrArgOrErrnoMemOnly();
+    return true;
   default:
     return false;
   }
@@ -2497,6 +2509,8 @@ static std::optional<MemoryEffects::Location> keywordToLoc(lltok::Kind Tok) {
     return IRMemLocation::ArgMem;
   case lltok::kw_inaccessiblemem:
     return IRMemLocation::InaccessibleMem;
+  case lltok::kw_errnomem:
+    return IRMemLocation::ErrnoMem;
   default:
     return std::nullopt;
   }
@@ -2545,7 +2559,7 @@ std::optional<MemoryEffects> LLParser::parseMemoryAttr() {
     std::optional<ModRefInfo> MR = keywordToModRef(Lex.getKind());
     if (!MR) {
       if (!Loc)
-        tokError("expected memory location (argmem, inaccessiblemem) "
+        tokError("expected memory location (argmem, inaccessiblemem, errnomem) "
                  "or access kind (none, read, write, readwrite)");
       else
         tokError("expected access kind (none, read, write, readwrite)");
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 1a09e80c4fbb2dd..fcf3999e3efbe99 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -1910,6 +1910,11 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
     return 1ULL << 62;
   case Attribute::NoFree:
     return 1ULL << 63;
+  // 7ULL << 36 is ErrnoMemOnly, which is upgraded separately.
+  // 7ULL << 37 is InaccessibleMemOrErrnoMemOnly, which is upgraded separately.
+  // 7ULL << 38 is ArgumentMemOrErrnoMemOnly, which is upgraded separately.
+  // 7ULL << 39 is InaccessibleMemOrArgOrErrnoMemOnly, which is upgraded
+  // separately.
   default:
     // Other attributes are not supported in the raw format,
     // as we ran out of space.
@@ -1982,6 +1987,26 @@ static void decodeLLVMAttributesForBitcode(AttrBuilder &B,
       Attrs &= ~(1ULL << 53);
       ME &= MemoryEffects::writeOnly();
     }
+    if (Attrs & (7ULL << 36)) {
+      // ErrnoMemOnly
+      Attrs &= ~(7ULL << 36);
+      ME &= MemoryEffects::errnoMemOnly();
+    }
+    if (Attrs & (7ULL << 37)) {
+      // InaccessibleMemOrErrnoMemOnly
+      Attrs &= ~(7ULL << 37);
+      ME &= MemoryEffects::inaccessibleOrErrnoMemOnly();
+    }
+    if (Attrs & (7ULL << 38)) {
+      // ArgumentMemOrErrnoMemOnly
+      Attrs &= ~(7ULL << 38);
+      ME &= MemoryEffects::argumentOrErrnoMemOnly();
+    }
+    if (Attrs & (7ULL << 39)) {
+      // InaccessibleMemOrArgOrErrnoMemOnly
+      Attrs &= ~(7ULL << 39);
+      ME &= MemoryEffects::inaccessibleOrArgOrErrnoMemOnly();
+    }
     if (ME != MemoryEffects::unknown())
       B.addMemoryAttr(ME);
   }
@@ -2293,9 +2318,21 @@ static bool upgradeOldMemoryAttribute(MemoryEffects &ME, uint64_t EncodedKind) {
   case bitc::ATTR_KIND_INACCESSIBLEMEM_ONLY:
     ME &= MemoryEffects::inaccessibleMemOnly();
     return true;
+  case bitc::ATTR_KIND_ERRNOMEMONLY:
+    ME &= MemoryEffects::errnoMemOnly();
+    return true;
   case bitc::ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY:
     ME &= MemoryEffects::inaccessibleOrArgMemOnly();
     return true;
+  case bitc::ATTR_KIND_INACCESSIBLEMEM_OR_ERRNOMEMONLY:
+    ME &= MemoryEffects::inaccessibleOrErrnoMemOnly();
+    return true;
+  case bitc::ATTR_KIND_ARGMEM_OR_ERRNOMEMONLY:
+    ME &= MemoryEffects::argumentOrErrnoMemOnly();
+    return true;
+  case bitc::ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEM_OR_ERRNOMEMONLY:
+    ME &= MemoryEffects::inaccessibleOrArgOrErrnoMemOnly();
+    return true;
   default:
     return false;
   }
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index ef0591ef3174409..8da1dfe914818ce 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -647,6 +647,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
       case IRMemLocation::InaccessibleMem:
         OS << "inaccessiblemem: ";
         break;
+      case IRMemLocation::ErrnoMem:
+        OS << "errnomem: ";
+        break;
       case IRMemLocation::Other:
         llvm_unreachable("This is represented as the default access kind");
       }
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index e6f0d64d071ba67..53e9d310000c1dd 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -922,6 +922,14 @@ void Function::setOnlyAccessesInaccessibleMemory() {
   setMemoryEffects(getMemoryEffects() & MemoryEffects::inaccessibleMemOnly());
 }
 
+/// Determine if the function may only access errno memory.
+bool Function::onlyAccessesErrnoMemory() const {
+  return getMemoryEffects().onlyAccessesErrnoMem();
+}
+void Function::setOnlyAccessesErrnoMemory() {
+  setMemoryEffects(getMemoryEffects() & MemoryEffects::errnoMemOnly());
+}
+
 /// Determine if the function may only access memory that is
 ///  either inaccessible from the IR or pointed to by its arguments.
 bool Function::onlyAccessesInaccessibleMemOrArgMem() const {
@@ -932,6 +940,37 @@ void Function::setOnlyAccessesInaccessibleMemOrArgMem() {
                    MemoryEffects::inaccessibleOrArgMemOnly());
 }
 
+/// Determine if the function may only access memory that is
+///  either inaccessible from the IR or errno memory.
+bool Function::onlyAccessesInaccessibleMemOrErrnoMem() const {
+  return getMemoryEffects().onlyAccessesInaccessibleOrErrnoMem();
+}
+void Function::setOnlyAccessesInaccessibleMemOrErrnoMem() {
+  setMemoryEffects(getMemoryEffects() &
+                   MemoryEffects::inaccessibleOrErrnoMemOnly());
+}
+
+/// Determine if the function may only access memory that is
+///  either pointed to by its arguments or errno memory.
+bool Function::onlyAccessesArgMemOrErrnoMem() const {
+  return getMemoryEffects().onlyAccessesArgumentOrErrnoMem();
+}
+void Function::setOnlyAccessesArgMemOrErrnoMem() {
+  setMemoryEffects(getMemoryEffects() &
+                   MemoryEffects::argumentOrErrnoMemOnly());
+}
+
+/// Determine if the function may only access memory that is
+///  either inaccessible from the IR, pointed to by its arguments or errno
+///  memory.
+bool Function::onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const {
+  return getMemoryEffects().onlyAccessesInaccessibleOrArgOrErrnoMem();
+}
+void Function::setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem() {
+  setMemoryEffects(getMemoryEffects() &
+                   MemoryEffects::inaccessibleOrArgOrErrnoMemOnly());
+}
+
 bool Function::isTargetIntrinsic() const {
   return Intrinsic::isTargetIntrinsic(IntID);
 }
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index 1fdf0eab548fdda..fe835e2def011da 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -682,6 +682,14 @@ void CallBase::setOnlyAccessesInaccessibleMemory() {
   setMemoryEffects(getMemoryEffects() & MemoryEffects::inaccessibleMemOnly());
 }
 
+/// Determine if the function may only access errno memory.
+bool CallBase::onlyAccessesErrnoMemory() const {
+  return getMemoryEffects().onlyAccessesErrnoMem();
+}
+void CallBase::setOnlyAccessesErrnoMemory() {
+  setMemoryEffects(getMemoryEffects() & MemoryEffects::errnoMemOnly());
+}
+
 /// Determine if the function may only access memory that is
 ///  either inaccessible from the IR or pointed to by its arguments.
 bool CallBase::onlyAccessesInaccessibleMemOrArgMem() const {
@@ -692,6 +700,37 @@ void CallBase::setOnlyAccessesInaccessibleMemOrArgMem() {
                    MemoryEffects::inaccessibleOrArgMemOnly());
 }
 
+/// Determine if the function may only access memory that is
+///  either inaccessible from the IR or errno memory.
+bool CallBase::onlyAccessesInaccessibleMemOrErrnoMem() const {
+  return getMemoryEffects().onlyAccessesInaccessibleOrErrnoMem();
+}
+void CallBase::setOnlyAccessesInaccessibleMemOrErrnoMem() {
+  setMemoryEffects(getMemoryEffects() &
+                   MemoryEffects::inaccessibleOrErrnoMemOnly());
+}
+
+/// Determine if the function may only access memory that is
+///  either pointed to by its arguments or errno memory.
+bool CallBase::onlyAccessesArgMemOrErrnoMem() const {
+  return getMemoryEffects().onlyAccessesArgumentOrErrnoMem();
+}
+void CallBase::setOnlyAccessesArgMemOrErrnoMem() {
+  setMemoryEffects(getMemoryEffects() &
+                   MemoryEffects::argumentOrErrnoMemOnly());
+}
+
+/// Determine if the function may only access memory that is
+///  either inaccessible from the IR, pointed to by its arguments or errno
+///  memory.
+bool CallBase::onlyAccessesInaccessibleMemOrArgMemOrErrnoMem() const {
+  return getMemoryEffects().onlyAccessesInaccessibleOrArgOrErrnoMem();
+}
+void CallBase::setOnlyAccessesInaccessibleMemOrArgMemOrErrnoMem() {
+  setMemoryEffects(getMemoryEffects() &
+                   MemoryEffects::inaccessibleOrArgOrErrnoMemOnly());
+}
+
 CaptureInfo CallBase::getCaptureInfo(unsigned OpNo) const {
   if (OpNo < arg_size()) {
     // If the argument is passed byval, the callee does not have access to the
diff --git a/llvm/lib/Support/ModRef.cpp b/llvm/lib/Support/ModRef.cpp
index d3b3dd11171f16d..2bb9bc945bd2e2b 100644
--- a/llvm/lib/Support/ModRef.cpp
+++ b/llvm/lib/Support/ModRef.cpp
@@ -43,6 +43,9 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, MemoryEffects ME) {
     case IRMemLocation::InaccessibleMem:
       OS << "InaccessibleMem: ";
       break;
+    case IRMemLocation::ErrnoMem:
+      OS << "ErrnoMem: ";
+      break;
     case IRMemLocation::Other:
       OS << "Other: ";
       break;
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index cf56f67e4de3f53..45a293003ba36b7 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -128,6 +128,17 @@ static void addLocAccess(MemoryEffects &ME, const MemoryLocation &Loc,
     ME |= MemoryEffects::argMemOnly(MR);
     return;
   }
+  if (isa<CallInst>(UO)) {
+    static constexpr auto ErrnoFnNames = {"__errno_location", "_errno",
+                                          "__errno", "___errno"};
+    auto *Callee = cast<CallInst>(UO)->getCalledFunction();
+    if (Callee && llvm::any_of(ErrnoFnNames, [&](const auto Fn) {
+          return Fn == Callee->getName();
+        })) {
+      ME |= MemoryEffects::errnoMemOnly(MR);
+      return;
+    }
+  }
 
   // If it's not an identified object, it might be an argument.
   if (!isIdentifiedObject(UO))
@@ -210,6 +221,9 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
       if (isa<PseudoProbeInst>(I))
         continue;
 
+      // Merge callee's memory effects into caller's ones, including
+      // inaccessible and errno memory, but excluding argument memory, which is
+      // handled separately.
       ME |= CallME.getWithoutLoc(IRMemLocation::ArgMem);
 
       // If the call accesses captured memory (currently part of "other") and
diff --git a/llvm/test/Assembler/memory-attribute-errors.ll b/llvm/test/Assembler/memory-attribute-errors.ll
index 1fba90362e79b90..2eed11d9465d586 100644
--- a/llvm/test/Assembler/memory-attribute-errors.ll
+++ b/llvm/test/Assembler/memory-attribute-errors.ll
@@ -12,16 +12,16 @@
 ; MISSING-ARGS: error: expected '('
 declare void @fn() memory
 ;--- empty.ll
-; EMPTY: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
+; EMPTY: error: expected memory location (argmem, inaccessiblemem, errnomem) 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) or access kind (none, read, write, readwrite)
+; INVALID-KIND: error: expected memory location (argmem, inaccessiblemem, errnomem) or access kind (none, read, write, readwrite)
 declare void @fn() memory(foo)
 ;--- other.ll
-; OTHER: error: expected memory location (argmem, inaccessiblemem) or access kind (none, read, write, readwrite)
+; OTHER: error: expected memory location (argmem, inaccessiblemem, errnomem) 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 2f7d3980eb378b5..effd4ce7c454836 100644
--- a/llvm/test/Assembler/memory-attribute.ll
+++ b/llvm/test/Assembler/memory-attribute.ll
@@ -40,6 +40,18 @@ declare void @fn_inaccessiblemem_write() memory(inaccessiblemem: write)
 ; CHECK: @fn_inaccessiblemem_readwrite()
 declare void @fn_inaccessiblemem_readwrite() memory(inaccessiblemem: readwrite)
 
+; CHECK: Function Attrs: memory(errnomem: read)
+; CHECK: @fn_errnomem_read()
+declare void @fn_errnomem_read() memory(errnomem: read)
+
+; CHECK: Function Attrs: memory(errnomem: write)
+; CHECK: @fn_errnomem_write()
+declare void @fn_errnomem_write() memory(errnomem: write)
+
+; CHECK: Function Attrs: memory(errnomem: readwrite)
+; CHECK: @fn_errnomem_readwrite()
+declare void @fn_errnomem_readwrite() memory(errnomem: readwrite)
+
 ; CHECK: Function Attrs: memory(read, argmem: readwrite)
 ; CHECK: @fn_read_argmem_readwrite()
 declare void @fn_read_argmem_readwrite() memory(read, argmem: readwrite)
diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
index 42e0e94c1cee3d1..01b4ef43a93f680 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, errnomem: 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i32 @test_read_loaded_ptr
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_memcpy_src_global
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_memcpy_dst_global
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_recursive_argmem_read
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_recursive_argmem_readwrite
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_scc_argmem_read_1
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_scc_argmem_read_2
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i64 @select_different_obj
 ; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR3]] {
 ; FNATTRS-NEXT:  entry:
@@ -580,7 +580,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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i64 @phi_different_obj
 ; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr 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 4a27bcd9853d4af..78459ea2c7400b8 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none, errnomem: 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, errnomem: 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i1 @c7
 ; FNATTRS-SAME: (ptr 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i32 @nc1
 ; FNATTRS-SAME: (ptr [[Q:%.*]], ptr 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i32 @nc1_addrspace
 ; FNATTRS-SAME: (ptr [[Q:%.*]], ptr addrspace(1) 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @nc2
 ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false)
@@ -398,7 +398,7 @@ define void @nc5(ptr %f, ptr %p) {
 
 ; 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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test1_1
 ; FNATTRS-SAME: (ptr readnone captures(none) [[X1_1:%.*]], ptr [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR10:[0-9]+]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = call ptr @test1_2(ptr [[X1_1]], ptr [[Y1_1]], i1 [[C]])
@@ -418,7 +418,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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define ptr @test1_2
 ; FNATTRS-SAME: (ptr readnone captures(none) [[X1_2:%.*]], ptr returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
 ; FNATTRS-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
@@ -450,7 +450,7 @@ f:
 }
 
 define void @test2(ptr %x2) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test2
 ; FNATTRS-SAME: (ptr readnone captures(none) [[X2:%.*]]) #[[ATTR10]] {
 ; FNATTRS-NEXT:    call void @test2(ptr [[X2]])
@@ -470,7 +470,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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test3
 ; FNATTRS-SAME: (ptr readnone captures(none) [[X3:%.*]], ptr readnone captures(none) [[Y3:%.*]], ptr readnone captures(none) [[Z3:%.*]]) #[[ATTR10]] {
 ; FNATTRS-NEXT:    call void @test3(ptr [[Z3]], ptr [[Y3]], ptr [[X3]])
@@ -490,7 +490,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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test4_1
 ; FNATTRS-SAME: (ptr [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = call ptr @test4_2(ptr [[X4_1]], ptr [[X4_1]], ptr [[X4_1]], i1 [[C]])
@@ -510,7 +510,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)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define ptr @test4_2
 ; FNATTRS-SAME: (ptr readnone captures(none) [[X4_2:%.*]], ptr readnone returned [[Y4_2:%.*]], ptr readnone captures(none) [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
 ; FNATTRS-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
@@ -674,7 +674,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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite, errnomem: none)
 ; FNATTRS-LABEL: define void @captureLaunder
 ; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] {
 ; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
@@ -718,7 +718,7 @@ entry:
 
 @g3 = global ptr null
 define void @captureStrip(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @captureStrip
 ; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR1]] {
 ; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]])
diff --git a/llvm/test/Transforms/FunctionAttrs/read-write-errnomem.ll b/llvm/test/Transforms/FunctionAttrs/read-write-errnomem.ll
new file mode 100644
index 000000000000000..97683ad7e646098
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/read-write-errnomem.ll
@@ -0,0 +1,71 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=ATTRIBUTOR %s
+
+define i32 @test_read_errno() {
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(errnomem: read)
+; FNATTRS-LABEL: define i32 @test_read_errno
+; FNATTRS-SAME: () #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT:    [[CALL:%.*]] = tail call ptr @__errno_location() #[[ATTR4:[0-9]+]]
+; FNATTRS-NEXT:    [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; FNATTRS-NEXT:    ret i32 [[ERRNO]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-LABEL: define i32 @test_read_errno
+; ATTRIBUTOR-SAME: () #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[CALL:%.*]] = tail call ptr @__errno_location() #[[ATTR4:[0-9]+]]
+; ATTRIBUTOR-NEXT:    [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[ERRNO]]
+;
+  %call = tail call ptr @__errno_location() #2
+  %errno = load i32, ptr %call
+  ret i32 %errno
+}
+
+define ptr @test_write_errno() {
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(errnomem: write)
+; FNATTRS-LABEL: define noundef ptr @test_write_errno
+; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:    [[CALL:%.*]] = tail call ptr @__errno_location() #[[ATTR4]]
+; FNATTRS-NEXT:    store i32 0, ptr [[CALL]], align 4
+; FNATTRS-NEXT:    ret ptr [[CALL]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define ptr @test_write_errno
+; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[CALL:%.*]] = tail call ptr @__errno_location() #[[ATTR4]]
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT:    ret ptr [[CALL]]
+;
+  %call = tail call ptr @__errno_location() #2
+  store i32 0, ptr %call
+  ret ptr %call
+}
+
+define i32 @test_readwrite_errno() {
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(errnomem: readwrite)
+; FNATTRS-LABEL: define i32 @test_readwrite_errno
+; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
+; FNATTRS-NEXT:    [[CALL:%.*]] = tail call ptr @__errno_location() #[[ATTR4]]
+; FNATTRS-NEXT:    store i32 0, ptr [[CALL]], align 4
+; FNATTRS-NEXT:    [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; FNATTRS-NEXT:    ret i32 [[ERRNO]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define i32 @test_readwrite_errno
+; ATTRIBUTOR-SAME: () #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[CALL:%.*]] = tail call ptr @__errno_location() #[[ATTR4]]
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT:    [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[ERRNO]]
+;
+  %call = tail call ptr @__errno_location() #2
+  store i32 0, ptr %call
+  %errno = load i32, ptr %call
+  ret i32 %errno
+}
+
+declare ptr @__errno_location() #1
+
+attributes #1 = { mustprogress nofree nosync nounwind willreturn memory(none) }
+attributes #2 = { nounwind willreturn memory(none) }
diff --git a/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll b/llvm/test/Transforms/FunctionAttrs/read-write-scc.ll
index be61990fd6278b7..6dd48a04f3145fb 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)
+; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, errnomem: 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)
+; CHECK: Function Attrs: nofree nosync nounwind memory(readwrite, argmem: none, inaccessiblemem: none, errnomem: 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 b24c097ad54d08c..554670479ab8d45 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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define {{[^@]+}}@test2
 ; FNATTRS-SAME: (ptr readnone returned [[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 88c603161369702..fdf9734ebda84f8 100644
--- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll
@@ -44,7 +44,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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funwo
 ; FNATTRS-SAME: (ptr readnone captures(none) [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] {
 ; FNATTRS-NEXT:  nouses-argworn-funwo_entry:
@@ -82,7 +82,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)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none, errnomem: 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/InferFunctionAttrs/norecurse_debug.ll b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll
index c8568272d320fb5..36791ae60fed608 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) }
+; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none, errnomem: 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 2795333effd76ba..8e212040d66515c 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)
+; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: 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 22726e0cac1f12b..5e8c7cd21324d8a 100644
--- a/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll
+++ b/llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll
@@ -11,10 +11,10 @@
 ; Here the pointer argument %arg will be replaced by a constant. We need to
 ; drop argmemonly.
 ;.
-; CHECK: @[[G:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0
+; CHECK: @g = internal global i32 0
 ;.
 define internal void @ptrarg.1(ptr %arg, i32 %val) argmemonly nounwind {
-; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none)
+; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, errnomem: 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)
+; CHECK: Function Attrs: nounwind memory(readwrite, errnomem: 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)
+; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none, errnomem: 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) }
+; CHECK: attributes #[[ATTR0]] = { nounwind memory(readwrite, inaccessiblemem: none, errnomem: none) }
 ; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind memory(argmem: readwrite) }
-; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite) }
+; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite, errnomem: none) }
 ; CHECK: attributes #[[ATTR3:[0-9]+]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) }
 ; CHECK: attributes #[[ATTR4]] = { nounwind }
 ;.
diff --git a/llvm/unittests/Support/ModRefTest.cpp b/llvm/unittests/Support/ModRefTest.cpp
index 35107e50b32db78..9c13908da44bb00 100644
--- a/llvm/unittests/Support/ModRefTest.cpp
+++ b/llvm/unittests/Support/ModRefTest.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/ModRef.h"
-#include "llvm/ADT/SmallString.h"
 #include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 #include <string>
@@ -21,7 +20,8 @@ TEST(ModRefTest, PrintMemoryEffects) {
   std::string S;
   raw_string_ostream OS(S);
   OS << MemoryEffects::none();
-  EXPECT_EQ(S, "ArgMem: NoModRef, InaccessibleMem: NoModRef, Other: NoModRef");
+  EXPECT_EQ(S, "ArgMem: NoModRef, InaccessibleMem: NoModRef, ErrnoMem: "
+               "NoModRef, Other: NoModRef");
 }
 
 } // namespace
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 52aa69f4c481f98..91cfb04e3b4401a 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2347,7 +2347,7 @@ llvm.func @readonly_function(%arg0: !llvm.ptr {llvm.readonly})
 llvm.func @arg_mem_none_func() attributes {
   memory_effects = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite>}
 
-// CHECK: attributes #[[ATTR]] = { memory(readwrite, argmem: none) }
+// CHECK: attributes #[[ATTR]] = { memory(readwrite, argmem: none, errnomem: none) }
 
 // -----
 
@@ -2355,7 +2355,7 @@ llvm.func @arg_mem_none_func() attributes {
 llvm.func @readwrite_func() attributes {
   memory_effects = #llvm.memory_effects<other = readwrite, argMem = readwrite, inaccessibleMem = readwrite>}
 
-// CHECK: attributes #[[ATTR]] = { memory(readwrite) }
+// CHECK: attributes #[[ATTR]] = { memory(readwrite, errnomem: none) }
 
 // -----
 
@@ -2613,11 +2613,11 @@ llvm.func @mem_effects_call() {
 // CHECK: #[[ATTRS_0]]
 // CHECK-SAME: memory(none)
 // CHECK: #[[ATTRS_1]]
-// CHECK-SAME: memory(read, argmem: none, inaccessiblemem: write)
+// CHECK-SAME: memory(read, argmem: none, inaccessiblemem: write, errnomem: none)
 // CHECK: #[[ATTRS_2]]
-// CHECK-SAME: memory(read, inaccessiblemem: write)
+// CHECK-SAME: memory(read, inaccessiblemem: write, errnomem: none)
 // CHECK: #[[ATTRS_3]]
-// CHECK-SAME: memory(readwrite, argmem: read)
+// CHECK-SAME: memory(readwrite, argmem: read, errnomem: none)
 
 // -----
 

>From bf42779ef7cc2b4f4af07f7b13f2b7ad99719e80 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Wed, 5 Feb 2025 23:29:44 +0100
Subject: [PATCH 2/2] !fixup accesses <= sizeof(int) may be aliasing errno

---
 llvm/include/llvm/Analysis/MemoryLocation.h   |  4 ++
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp     |  2 +
 .../Transforms/FunctionAttrs/argmemonly.ll    | 70 +++++++++----------
 llvm/test/Transforms/FunctionAttrs/atomic.ll  |  2 +-
 .../Transforms/FunctionAttrs/initializes.ll   | 60 ++++++++--------
 .../Transforms/FunctionAttrs/nocapture.ll     | 32 ++++-----
 llvm/test/Transforms/FunctionAttrs/nofree.ll  |  2 +-
 llvm/test/Transforms/FunctionAttrs/nosync.ll  | 27 ++++---
 .../Transforms/FunctionAttrs/readattrs.ll     | 22 +++---
 .../Transforms/FunctionAttrs/willreturn.ll    | 26 ++++---
 .../Transforms/FunctionAttrs/writeonly.ll     | 14 ++--
 11 files changed, 136 insertions(+), 125 deletions(-)

diff --git a/llvm/include/llvm/Analysis/MemoryLocation.h b/llvm/include/llvm/Analysis/MemoryLocation.h
index ea29e21bd18f2fa..0c7e9ffe7c9ee28 100644
--- a/llvm/include/llvm/Analysis/MemoryLocation.h
+++ b/llvm/include/llvm/Analysis/MemoryLocation.h
@@ -194,6 +194,10 @@ class LocationSize {
     return hasValue() && getValue() == Other;
   }
 
+  bool operator<=(const LocationSize &Other) const {
+    return Value <= Other.Value;
+  }
+
   bool operator!=(const LocationSize &Other) const { return !(*this == Other); }
 
   bool operator!=(const TypeSize &Other) const { return !(*this == Other); }
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 45a293003ba36b7..a745865f6926cd7 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -126,6 +126,8 @@ static void addLocAccess(MemoryEffects &ME, const MemoryLocation &Loc,
     return;
   if (isa<Argument>(UO)) {
     ME |= MemoryEffects::argMemOnly(MR);
+    if (Loc.Size <= LocationSize::precise(sizeof(int)))
+      ME |= MemoryEffects::errnoMemOnly(MR);
     return;
   }
   if (isa<CallInst>(UO)) {
diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
index 01b4ef43a93f680..cba4ceb20af4c62 100644
--- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
@@ -16,7 +16,7 @@ entry:
 }
 
 define i32 @test_only_read_arg(ptr %ptr) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: define i32 @test_only_read_arg
 ; FNATTRS-SAME: (ptr readonly captures(none) [[PTR:%.*]]) #[[ATTR1:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
@@ -38,7 +38,7 @@ entry:
 define i32 @test_only_read_arg_already_has_argmemonly(ptr %ptr) argmemonly {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
 ; FNATTRS-LABEL: define i32 @test_only_read_arg_already_has_argmemonly
-; FNATTRS-SAME: (ptr readonly captures(none) [[PTR:%.*]]) #[[ATTR1]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[PTR:%.*]]) #[[ATTR2:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
 ; FNATTRS-NEXT:    ret i32 [[L]]
@@ -58,7 +58,7 @@ entry:
 define i32 @test_read_global() {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i32 @test_read_global
-; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
+; FNATTRS-SAME: () #[[ATTR3:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[L:%.*]] = load i32, ptr @g, align 4
 ; FNATTRS-NEXT:    ret i32 [[L]]
@@ -78,7 +78,7 @@ entry:
 define i32 @test_read_loaded_ptr(ptr %ptr) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i32 @test_read_loaded_ptr
-; FNATTRS-SAME: (ptr readonly captures(none) [[PTR:%.*]]) #[[ATTR3:[0-9]+]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[PTR:%.*]]) #[[ATTR4:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[L:%.*]] = load ptr, ptr [[PTR]], align 8
 ; FNATTRS-NEXT:    [[L_2:%.*]] = load i32, ptr [[L]], align 4
@@ -99,9 +99,9 @@ entry:
 }
 
 define void @test_only_write_arg(ptr %ptr) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; FNATTRS-LABEL: define void @test_only_write_arg
-; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 4)) [[PTR:%.*]]) #[[ATTR4:[0-9]+]] {
+; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 4)) [[PTR:%.*]]) #[[ATTR5:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    store i32 0, ptr [[PTR]], align 4
 ; FNATTRS-NEXT:    ret void
@@ -121,7 +121,7 @@ entry:
 define void @test_write_global() {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_write_global
-; FNATTRS-SAME: () #[[ATTR5:[0-9]+]] {
+; FNATTRS-SAME: () #[[ATTR6:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    store i32 0, ptr @g, align 4
 ; FNATTRS-NEXT:    ret void
@@ -154,9 +154,9 @@ entry:
 declare i32 @fn_readnone() readnone
 
 define void @test_call_readnone(ptr %ptr) {
-; FNATTRS: Function Attrs: memory(argmem: write)
+; FNATTRS: Function Attrs: memory(argmem: write, errnomem: write)
 ; FNATTRS-LABEL: define void @test_call_readnone
-; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 4)) [[PTR:%.*]]) #[[ATTR7:[0-9]+]] {
+; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 4)) [[PTR:%.*]]) #[[ATTR8:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[C:%.*]] = call i32 @fn_readnone()
 ; FNATTRS-NEXT:    store i32 [[C]], ptr [[PTR]], align 4
@@ -181,7 +181,7 @@ declare i32 @fn_argmemonly(ptr) argmemonly
 define i32 @test_call_argmemonly(ptr %ptr) {
 ; FNATTRS: Function Attrs: memory(argmem: readwrite)
 ; FNATTRS-LABEL: define i32 @test_call_argmemonly
-; FNATTRS-SAME: (ptr [[PTR:%.*]]) #[[ATTR8:[0-9]+]] {
+; FNATTRS-SAME: (ptr [[PTR:%.*]]) #[[ATTR9:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR]])
 ; FNATTRS-NEXT:    ret i32 [[C]]
@@ -199,7 +199,7 @@ entry:
 }
 
 define i32 @test_call_fn_where_argmemonly_can_be_inferred(ptr %ptr) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: define i32 @test_call_fn_where_argmemonly_can_be_inferred
 ; FNATTRS-SAME: (ptr readonly captures(none) [[PTR:%.*]]) #[[ATTR1]] {
 ; FNATTRS-NEXT:  entry:
@@ -221,7 +221,7 @@ entry:
 define void @test_memcpy_argonly(ptr %dst, ptr %src) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; FNATTRS-LABEL: define void @test_memcpy_argonly
-; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 32)) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR9:[0-9]+]] {
+; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 32)) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR10:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC]], i64 32, i1 false)
 ; FNATTRS-NEXT:    ret void
@@ -245,7 +245,7 @@ declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
 define void @test_memcpy_src_global(ptr %dst) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_memcpy_src_global
-; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 32)) [[DST:%.*]]) #[[ATTR11:[0-9]+]] {
+; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 32)) [[DST:%.*]]) #[[ATTR12:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr @arr, i64 32, i1 false)
 ; FNATTRS-NEXT:    ret void
@@ -265,7 +265,7 @@ entry:
 define void @test_memcpy_dst_global(ptr %src) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define void @test_memcpy_dst_global
-; FNATTRS-SAME: (ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR11]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR12]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr [[SRC]], i64 32, i1 false)
 ; FNATTRS-NEXT:    ret void
@@ -283,7 +283,7 @@ entry:
 }
 
 define i32 @test_read_arg_access_alloca(ptr %ptr) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: define i32 @test_read_arg_access_alloca
 ; FNATTRS-SAME: (ptr readonly captures(none) [[PTR:%.*]]) #[[ATTR1]] {
 ; FNATTRS-NEXT:  entry:
@@ -316,7 +316,7 @@ declare void @fn_inaccessiblememonly() inaccessiblememonly
 define void @test_inaccessiblememonly() {
 ; FNATTRS: Function Attrs: memory(inaccessiblemem: readwrite)
 ; FNATTRS-LABEL: define void @test_inaccessiblememonly
-; FNATTRS-SAME: () #[[ATTR12:[0-9]+]] {
+; FNATTRS-SAME: () #[[ATTR13:[0-9]+]] {
 ; FNATTRS-NEXT:    call void @fn_inaccessiblememonly()
 ; FNATTRS-NEXT:    ret void
 ;
@@ -333,8 +333,8 @@ define void @test_inaccessiblememonly() {
 define void @test_inaccessiblememonly_readonly() {
 ; FNATTRS: Function Attrs: nofree memory(inaccessiblemem: read)
 ; FNATTRS-LABEL: define void @test_inaccessiblememonly_readonly
-; FNATTRS-SAME: () #[[ATTR13:[0-9]+]] {
-; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR19:[0-9]+]]
+; FNATTRS-SAME: () #[[ATTR14:[0-9]+]] {
+; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR20:[0-9]+]]
 ; FNATTRS-NEXT:    ret void
 ;
 ; ATTRIBUTOR: Function Attrs: nosync memory(inaccessiblemem: read)
@@ -348,11 +348,11 @@ define void @test_inaccessiblememonly_readonly() {
 }
 
 define void @test_inaccessibleorargmemonly_readonly(ptr %arg) {
-; FNATTRS: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read)
+; FNATTRS: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read, errnomem: read)
 ; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readonly
-; FNATTRS-SAME: (ptr readonly captures(none) [[ARG:%.*]]) #[[ATTR14:[0-9]+]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[ARG:%.*]]) #[[ATTR15:[0-9]+]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4
-; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR19]]
+; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR20]]
 ; FNATTRS-NEXT:    ret void
 ;
 ; ATTRIBUTOR: Function Attrs: nosync memory(argmem: read, inaccessiblemem: read)
@@ -368,11 +368,11 @@ define void @test_inaccessibleorargmemonly_readonly(ptr %arg) {
 }
 
 define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) {
-; FNATTRS: Function Attrs: memory(argmem: write, inaccessiblemem: read)
+; FNATTRS: Function Attrs: memory(argmem: write, inaccessiblemem: read, errnomem: write)
 ; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readwrite
-; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 4)) [[ARG:%.*]]) #[[ATTR15:[0-9]+]] {
+; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 4)) [[ARG:%.*]]) #[[ATTR16:[0-9]+]] {
 ; FNATTRS-NEXT:    store i32 0, ptr [[ARG]], align 4
-; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR19]]
+; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR20]]
 ; FNATTRS-NEXT:    ret void
 ;
 ; ATTRIBUTOR: Function Attrs: nosync memory(argmem: readwrite, inaccessiblemem: readwrite)
@@ -390,7 +390,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, errnomem: none)
 ; FNATTRS-LABEL: define void @test_recursive_argmem_read
-; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR16:[0-9]+]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR17:[0-9]+]] {
 ; FNATTRS-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
 ; FNATTRS-NEXT:    call void @test_recursive_argmem_read(ptr [[PVAL]])
 ; FNATTRS-NEXT:    ret void
@@ -408,9 +408,9 @@ 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, errnomem: none)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(readwrite, inaccessiblemem: none, errnomem: write)
 ; FNATTRS-LABEL: define void @test_recursive_argmem_readwrite
-; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR17:[0-9]+]] {
+; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR18:[0-9]+]] {
 ; FNATTRS-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
 ; FNATTRS-NEXT:    store i32 0, ptr [[P]], align 4
 ; FNATTRS-NEXT:    call void @test_recursive_argmem_readwrite(ptr [[PVAL]])
@@ -431,9 +431,9 @@ define void @test_recursive_argmem_readwrite(ptr %p) {
 }
 
 define void @test_recursive_argmem_read_alloca(ptr %p) {
-; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: define void @test_recursive_argmem_read_alloca
-; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR18:[0-9]+]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR19:[0-9]+]] {
 ; FNATTRS-NEXT:    [[A:%.*]] = alloca ptr, align 8
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P]], align 4
 ; FNATTRS-NEXT:    call void @test_recursive_argmem_read_alloca(ptr [[A]])
@@ -456,7 +456,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, errnomem: none)
 ; FNATTRS-LABEL: define void @test_scc_argmem_read_1
-; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR16]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR17]] {
 ; FNATTRS-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
 ; FNATTRS-NEXT:    call void @test_scc_argmem_read_2(ptr [[PVAL]])
 ; FNATTRS-NEXT:    ret void
@@ -476,7 +476,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, errnomem: none)
 ; FNATTRS-LABEL: define void @test_scc_argmem_read_2
-; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR16]] {
+; FNATTRS-SAME: (ptr readonly captures(none) [[P:%.*]]) #[[ATTR17]] {
 ; FNATTRS-NEXT:    call void @test_scc_argmem_read_1(ptr [[P]])
 ; FNATTRS-NEXT:    ret void
 ;
@@ -493,7 +493,7 @@ define void @test_scc_argmem_read_2(ptr %p) {
 define i64 @select_same_obj(i1 %c, ptr %p, i64 %x) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
 ; FNATTRS-LABEL: define i64 @select_same_obj
-; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], i64 [[X:%.*]]) #[[ATTR1]] {
+; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], i64 [[X:%.*]]) #[[ATTR2]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[P2:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
 ; FNATTRS-NEXT:    [[P3:%.*]] = select i1 [[C]], ptr [[P]], ptr [[P2]]
@@ -520,7 +520,7 @@ entry:
 define i64 @select_different_obj(i1 %c, ptr %p, ptr %p2) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i64 @select_different_obj
-; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR3]] {
+; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR4]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[P3:%.*]] = select i1 [[C]], ptr [[P]], ptr [[P2]]
 ; FNATTRS-NEXT:    [[R:%.*]] = load i64, ptr [[P3]], align 4
@@ -543,7 +543,7 @@ entry:
 define i64 @phi_same_obj(i1 %c, ptr %p, i64 %x) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
 ; FNATTRS-LABEL: define i64 @phi_same_obj
-; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], i64 [[X:%.*]]) #[[ATTR1]] {
+; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], i64 [[X:%.*]]) #[[ATTR2]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[P2:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
 ; FNATTRS-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[JOIN:%.*]]
@@ -582,7 +582,7 @@ join:
 define i64 @phi_different_obj(i1 %c, ptr %p, ptr %p2) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
 ; FNATTRS-LABEL: define i64 @phi_different_obj
-; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR3]] {
+; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR4]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    br i1 [[C]], label [[IF:%.*]], label [[JOIN:%.*]]
 ; FNATTRS:       if:
diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll
index 8635f2bbdc49802..9121264dacc96c2 100644
--- a/llvm/test/Transforms/FunctionAttrs/atomic.ll
+++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll
@@ -21,7 +21,7 @@ entry:
 
 ; A function with an Acquire load is not readonly.
 define i32 @test2(ptr %x) uwtable ssp {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn memory(argmem: readwrite) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind ssp willreturn memory(argmem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @test2(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[R:%.*]] = load atomic i32, ptr [[X:%.*]] seq_cst, align 4
diff --git a/llvm/test/Transforms/FunctionAttrs/initializes.ll b/llvm/test/Transforms/FunctionAttrs/initializes.ll
index 861c61d683ae0ad..d5c2e99a83f44b4 100644
--- a/llvm/test/Transforms/FunctionAttrs/initializes.ll
+++ b/llvm/test/Transforms/FunctionAttrs/initializes.ll
@@ -63,7 +63,7 @@ define void @store_pointer_to_itself(ptr %p) {
 }
 
 define void @load_before_store(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; CHECK-LABEL: define void @load_before_store(
 ; CHECK-SAME: ptr captures(none) [[P:%.*]]) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:    [[A:%.*]] = load i32, ptr [[P]], align 4
@@ -76,9 +76,9 @@ define void @load_before_store(ptr %p) {
 }
 
 define void @partial_load_before_store(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, errnomem: read)
 ; CHECK-LABEL: define void @partial_load_before_store(
-; CHECK-SAME: ptr captures(none) initializes((4, 8)) [[P:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr captures(none) initializes((4, 8)) [[P:%.*]]) #[[ATTR2:[0-9]+]] {
 ; CHECK-NEXT:    [[A:%.*]] = load i32, ptr [[P]], align 4
 ; CHECK-NEXT:    store i64 123, ptr [[P]], align 4
 ; CHECK-NEXT:    ret void
@@ -115,9 +115,9 @@ define void @call_clobber_after_store(ptr %p) {
 }
 
 define void @store_offset(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; CHECK-LABEL: define void @store_offset(
-; CHECK-SAME: ptr writeonly captures(none) initializes((8, 12)) [[P:%.*]]) #[[ATTR0]] {
+; CHECK-SAME: ptr writeonly captures(none) initializes((8, 12)) [[P:%.*]]) #[[ATTR3:[0-9]+]] {
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 8
 ; CHECK-NEXT:    store i32 123, ptr [[G]], align 4
 ; CHECK-NEXT:    ret void
@@ -128,9 +128,9 @@ define void @store_offset(ptr %p) {
 }
 
 define void @store_volatile(ptr %p) {
-; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite)
 ; CHECK-LABEL: define void @store_volatile(
-; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] {
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 8
 ; CHECK-NEXT:    store volatile i32 123, ptr [[G]], align 4
 ; CHECK-NEXT:    ret void
@@ -141,9 +141,9 @@ define void @store_volatile(ptr %p) {
 }
 
 define void @merge_store_ranges(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; CHECK-LABEL: define void @merge_store_ranges(
-; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8)) [[P:%.*]]) #[[ATTR0]] {
+; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8)) [[P:%.*]]) #[[ATTR3]] {
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 4
 ; CHECK-NEXT:    store i32 123, ptr [[G]], align 4
 ; CHECK-NEXT:    store i32 123, ptr [[P]], align 4
@@ -156,9 +156,9 @@ define void @merge_store_ranges(ptr %p) {
 }
 
 define void @partially_overlapping_stores_branches(ptr %p, i1 %i) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, errnomem: read)
 ; CHECK-LABEL: define void @partially_overlapping_stores_branches(
-; CHECK-SAME: ptr captures(none) initializes((4, 8)) [[P:%.*]], i1 [[I:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr captures(none) initializes((4, 8)) [[P:%.*]], i1 [[I:%.*]]) #[[ATTR2]] {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[A:%.*]] = load i32, ptr [[P]], align 4
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 4
@@ -384,7 +384,7 @@ define void @call_initializes_escape_bundle(ptr %p) {
 define void @access_bundle() {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define void @access_bundle(
-; CHECK-SAME: ) #[[ATTR3:[0-9]+]] {
+; CHECK-SAME: ) #[[ATTR5:[0-9]+]] {
 ; CHECK-NEXT:    [[SINK:%.*]] = alloca i64, align 8
 ; CHECK-NEXT:    store i64 123, ptr [[SINK]], align 4
 ; CHECK-NEXT:    ret void
@@ -397,7 +397,7 @@ define void @access_bundle() {
 define void @call_operand_bundle(ptr %p) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
 ; CHECK-LABEL: define void @call_operand_bundle(
-; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR6:[0-9]+]] {
 ; CHECK-NEXT:    call void @access_bundle() [ "unknown"(ptr [[P]]) ]
 ; CHECK-NEXT:    ret void
 ;
@@ -445,7 +445,7 @@ define void @memset_neg(ptr %p) {
 define void @memset_volatile(ptr %p) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: write)
 ; CHECK-LABEL: define void @memset_volatile(
-; CHECK-SAME: ptr writeonly [[P:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-SAME: ptr writeonly [[P:%.*]]) #[[ATTR7:[0-9]+]] {
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[P]], i8 2, i64 9, i1 true)
 ; CHECK-NEXT:    ret void
 ;
@@ -469,7 +469,7 @@ declare void @llvm.memcpy(ptr, ptr, i64 ,i1)
 define void @memcpy(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memcpy(
-; CHECK-SAME: ptr writeonly captures(none) initializes((0, 9)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) initializes((0, 9)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR8:[0-9]+]] {
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 false)
 ; CHECK-NEXT:    ret void
 ;
@@ -480,7 +480,7 @@ define void @memcpy(ptr %p, ptr %p2) {
 define void @memcpy_volatile(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memcpy_volatile(
-; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR6:[0-9]+]] {
+; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR9:[0-9]+]] {
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true)
 ; CHECK-NEXT:    ret void
 ;
@@ -491,7 +491,7 @@ define void @memcpy_volatile(ptr %p, ptr %p2) {
 define void @memcpy_offset(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memcpy_offset(
-; CHECK-SAME: ptr writeonly captures(none) initializes((3, 12)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) initializes((3, 12)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 3
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 9, i1 false)
 ; CHECK-NEXT:    ret void
@@ -504,7 +504,7 @@ define void @memcpy_offset(ptr %p, ptr %p2) {
 define void @memcpy_src(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memcpy_src(
-; CHECK-SAME: ptr captures(none) initializes((96, 128)) [[P:%.*]], ptr captures(none) initializes((0, 96)) [[P2:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr captures(none) initializes((96, 128)) [[P:%.*]], ptr captures(none) initializes((0, 96)) [[P2:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[P2]], ptr [[P]], i64 96, i1 false)
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 64
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 64, i1 false)
@@ -519,7 +519,7 @@ define void @memcpy_src(ptr %p, ptr %p2) {
 define void @memcpy_non_constant(ptr %p, ptr %p2, i64 %i) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memcpy_non_constant(
-; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]], i64 [[I:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]], i64 [[I:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 [[I]], i1 false)
 ; CHECK-NEXT:    ret void
 ;
@@ -532,7 +532,7 @@ declare void @llvm.memmove(ptr, ptr, i64 ,i1)
 define void @memmove(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memmove(
-; CHECK-SAME: ptr writeonly captures(none) initializes((0, 9)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) initializes((0, 9)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 false)
 ; CHECK-NEXT:    ret void
 ;
@@ -543,7 +543,7 @@ define void @memmove(ptr %p, ptr %p2) {
 define void @memmove_volatile(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memmove_volatile(
-; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR6]] {
+; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR9]] {
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true)
 ; CHECK-NEXT:    ret void
 ;
@@ -554,7 +554,7 @@ define void @memmove_volatile(ptr %p, ptr %p2) {
 define void @memmove_offset(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memmove_offset(
-; CHECK-SAME: ptr writeonly captures(none) initializes((3, 12)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) initializes((3, 12)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 3
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 9, i1 false)
 ; CHECK-NEXT:    ret void
@@ -567,7 +567,7 @@ define void @memmove_offset(ptr %p, ptr %p2) {
 define void @memmove_src(ptr %p, ptr %p2) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memmove_src(
-; CHECK-SAME: ptr captures(none) initializes((96, 128)) [[P:%.*]], ptr captures(none) initializes((0, 96)) [[P2:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr captures(none) initializes((96, 128)) [[P:%.*]], ptr captures(none) initializes((0, 96)) [[P2:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[P2]], ptr [[P]], i64 96, i1 false)
 ; CHECK-NEXT:    [[G:%.*]] = getelementptr i8, ptr [[P]], i64 64
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 64, i1 false)
@@ -582,7 +582,7 @@ define void @memmove_src(ptr %p, ptr %p2) {
 define void @memmove_non_constant(ptr %p, ptr %p2, i64 %i) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memmove_non_constant(
-; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]], i64 [[I:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]], i64 [[I:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 [[I]], i1 false)
 ; CHECK-NEXT:    ret void
 ;
@@ -591,9 +591,9 @@ define void @memmove_non_constant(ptr %p, ptr %p2, i64 %i) {
 }
 
 define void @callee_byval(ptr byval(i32) %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; CHECK-LABEL: define void @callee_byval(
-; CHECK-SAME: ptr writeonly byval(i32) captures(none) initializes((0, 4)) [[P:%.*]]) #[[ATTR0]] {
+; CHECK-SAME: ptr writeonly byval(i32) captures(none) initializes((0, 4)) [[P:%.*]]) #[[ATTR3]] {
 ; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
 ; CHECK-NEXT:    ret void
 ;
@@ -602,9 +602,9 @@ define void @callee_byval(ptr byval(i32) %p) {
 }
 
 define void @caller_byval(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; CHECK-LABEL: define void @caller_byval(
-; CHECK-SAME: ptr readonly captures(none) [[P:%.*]]) #[[ATTR0]] {
+; CHECK-SAME: ptr readonly captures(none) [[P:%.*]]) #[[ATTR3]] {
 ; CHECK-NEXT:    call void @callee_byval(ptr byval(i32) [[P]])
 ; CHECK-NEXT:    ret void
 ;
@@ -615,7 +615,7 @@ define void @caller_byval(ptr %p) {
 define void @memset_offset_0_size_0(ptr %dst, ptr %src) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memset_offset_0_size_0(
-; CHECK-SAME: ptr writeonly captures(none) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[DST]], ptr [[SRC]], i64 0, i1 false)
 ; CHECK-NEXT:    ret void
 ;
@@ -626,7 +626,7 @@ define void @memset_offset_0_size_0(ptr %dst, ptr %src) {
 define void @memset_offset_1_size_0(ptr %dst, ptr %src) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
 ; CHECK-LABEL: define void @memset_offset_1_size_0(
-; CHECK-SAME: ptr writeonly captures(none) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR1]] {
+; CHECK-SAME: ptr writeonly captures(none) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR8]] {
 ; CHECK-NEXT:    [[DST_1:%.*]] = getelementptr inbounds i8, ptr [[DST]], i64 1
 ; CHECK-NEXT:    call void @llvm.memmove.p0.p0.i64(ptr [[DST_1]], ptr [[SRC]], i64 0, i1 false)
 ; CHECK-NEXT:    ret void
diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
index 78459ea2c7400b8..e9f46369c2fb65f 100644
--- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -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, errnomem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: write)
 ; FNATTRS-LABEL: define i32 @nc1
 ; FNATTRS-SAME: (ptr [[Q:%.*]], ptr 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, errnomem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: write)
 ; FNATTRS-LABEL: define i32 @nc1_addrspace
 ; FNATTRS-SAME: (ptr [[Q:%.*]], ptr addrspace(1) 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, errnomem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none, errnomem: write)
 ; FNATTRS-LABEL: define void @nc2
 ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false)
@@ -576,7 +576,7 @@ define void @test6_2(ptr %x6_2, ptr %y6_2, ptr %z6_2) {
 }
 
 define void @test_cmpxchg(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; FNATTRS-LABEL: define void @test_cmpxchg
 ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR11:[0-9]+]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4
@@ -595,7 +595,7 @@ define void @test_cmpxchg(ptr %p) {
 define void @test_cmpxchg_ptr(ptr %p, ptr %q) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
 ; FNATTRS-LABEL: define void @test_cmpxchg_ptr
-; FNATTRS-SAME: (ptr captures(none) [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR11]] {
+; FNATTRS-SAME: (ptr captures(none) [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR12:[0-9]+]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8
 ; FNATTRS-NEXT:    ret void
 ;
@@ -610,7 +610,7 @@ define void @test_cmpxchg_ptr(ptr %p, ptr %q) {
 }
 
 define void @test_atomicrmw(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; FNATTRS-LABEL: define void @test_atomicrmw
 ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR11]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4
@@ -627,9 +627,9 @@ define void @test_atomicrmw(ptr %p) {
 }
 
 define void @test_volatile(ptr %x) {
-; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite)
 ; FNATTRS-LABEL: define void @test_volatile
-; FNATTRS-SAME: (ptr [[X:%.*]]) #[[ATTR12:[0-9]+]] {
+; FNATTRS-SAME: (ptr [[X:%.*]]) #[[ATTR13:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1
 ; FNATTRS-NEXT:    store volatile i32 0, ptr [[GEP]], align 4
@@ -650,9 +650,9 @@ entry:
 }
 
 define void @nocaptureLaunder(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, inaccessiblemem: readwrite)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, inaccessiblemem: readwrite, errnomem: write)
 ; FNATTRS-LABEL: define void @nocaptureLaunder
-; FNATTRS-SAME: (ptr writeonly captures(none) [[P:%.*]]) #[[ATTR13:[0-9]+]] {
+; FNATTRS-SAME: (ptr writeonly captures(none) [[P:%.*]]) #[[ATTR14:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
 ; FNATTRS-NEXT:    store i8 42, ptr [[B]], align 1
@@ -676,7 +676,7 @@ entry:
 define void @captureLaunder(ptr %p) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite, errnomem: none)
 ; FNATTRS-LABEL: define void @captureLaunder
-; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] {
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR15:[0-9]+]] {
 ; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
 ; FNATTRS-NEXT:    store ptr [[B]], ptr @g2, align 8
 ; FNATTRS-NEXT:    ret void
@@ -694,9 +694,9 @@ define void @captureLaunder(ptr %p) {
 }
 
 define void @nocaptureStrip(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; FNATTRS-LABEL: define void @nocaptureStrip
-; FNATTRS-SAME: (ptr writeonly captures(none) [[P:%.*]]) #[[ATTR15:[0-9]+]] {
+; FNATTRS-SAME: (ptr writeonly captures(none) [[P:%.*]]) #[[ATTR16:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]])
 ; FNATTRS-NEXT:    store i8 42, ptr [[B]], align 1
@@ -831,7 +831,7 @@ define i1 @nocaptureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x)
 define i1 @captureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x) null_pointer_is_valid {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none)
 ; FNATTRS-LABEL: define noundef i1 @captureDereferenceableOrNullICmp
-; FNATTRS-SAME: (ptr readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR16:[0-9]+]] {
+; FNATTRS-SAME: (ptr readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR17:[0-9]+]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
 ; FNATTRS-NEXT:    ret i1 [[TMP1]]
 ;
@@ -886,8 +886,8 @@ define void @recurse_fptr(ptr %f, ptr %p) {
 define void @readnone_indirec(ptr %f, ptr %p) {
 ; FNATTRS: Function Attrs: nofree nosync memory(none)
 ; FNATTRS-LABEL: define void @readnone_indirec
-; FNATTRS-SAME: (ptr readonly captures(none) [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR17:[0-9]+]] {
-; FNATTRS-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR20:[0-9]+]]
+; FNATTRS-SAME: (ptr readonly captures(none) [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] {
+; FNATTRS-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR21:[0-9]+]]
 ; FNATTRS-NEXT:    ret void
 ;
 ; ATTRIBUTOR: Function Attrs: nosync memory(none)
diff --git a/llvm/test/Transforms/FunctionAttrs/nofree.ll b/llvm/test/Transforms/FunctionAttrs/nofree.ll
index 16711890db802eb..5789c015905c097 100644
--- a/llvm/test/Transforms/FunctionAttrs/nofree.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nofree.ll
@@ -33,7 +33,7 @@ entry:
 declare void @free(ptr nocapture) local_unnamed_addr #2
 
 define i32 @_Z4foo3Pi(ptr nocapture readonly %a) local_unnamed_addr #3 {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read, errnomem: read) uwtable
 ; CHECK-LABEL: @_Z4foo3Pi(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[A:%.*]], align 4
diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll
index de5398f17ce51d4..334ac56175b5051 100644
--- a/llvm/test/Transforms/FunctionAttrs/nosync.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll
@@ -48,7 +48,7 @@ define i32 @test4(i32 %a, i32 %b) {
 
 ; negative case - explicit sync
 define void @test5(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; CHECK-LABEL: @test5(
 ; CHECK-NEXT:    store atomic i8 0, ptr [[P:%.*]] seq_cst, align 1
 ; CHECK-NEXT:    ret void
@@ -59,7 +59,7 @@ define void @test5(ptr %p) {
 
 ; negative case - explicit sync
 define i8 @test6(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; CHECK-LABEL: @test6(
 ; CHECK-NEXT:    [[V:%.*]] = load atomic i8, ptr [[P:%.*]] seq_cst, align 1
 ; CHECK-NEXT:    ret i8 [[V]]
@@ -70,7 +70,7 @@ define i8 @test6(ptr %p) {
 
 ; negative case - explicit sync
 define void @test7(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; CHECK-LABEL: @test7(
 ; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P:%.*]], i8 0 seq_cst, align 1
 ; CHECK-NEXT:    ret void
@@ -103,7 +103,7 @@ define void @test9(ptr %p) {
 
 ; atomic load with monotonic ordering
 define i32 @load_monotonic(ptr nocapture readonly %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @load_monotonic(
 ; CHECK-NEXT:    [[TMP2:%.*]] = load atomic i32, ptr [[TMP0:%.*]] monotonic, align 4
 ; CHECK-NEXT:    ret i32 [[TMP2]]
@@ -114,7 +114,7 @@ define i32 @load_monotonic(ptr nocapture readonly %0) norecurse nounwind uwtable
 
 ; atomic store with monotonic ordering.
 define void @store_monotonic(ptr nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @store_monotonic(
 ; CHECK-NEXT:    store atomic i32 10, ptr [[TMP0:%.*]] monotonic, align 4
 ; CHECK-NEXT:    ret void
@@ -126,7 +126,7 @@ define void @store_monotonic(ptr nocapture %0) norecurse nounwind uwtable {
 ; negative, should not deduce nosync
 ; atomic load with acquire ordering.
 define i32 @load_acquire(ptr nocapture readonly %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @load_acquire(
 ; CHECK-NEXT:    [[TMP2:%.*]] = load atomic i32, ptr [[TMP0:%.*]] acquire, align 4
 ; CHECK-NEXT:    ret i32 [[TMP2]]
@@ -136,7 +136,7 @@ define i32 @load_acquire(ptr nocapture readonly %0) norecurse nounwind uwtable {
 }
 
 define i32 @load_unordered(ptr nocapture readonly %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read, errnomem: read) uwtable
 ; CHECK-LABEL: @load_unordered(
 ; CHECK-NEXT:    [[TMP2:%.*]] = load atomic i32, ptr [[TMP0:%.*]] unordered, align 4
 ; CHECK-NEXT:    ret i32 [[TMP2]]
@@ -147,7 +147,7 @@ define i32 @load_unordered(ptr nocapture readonly %0) norecurse nounwind uwtable
 
 ; atomic store with unordered ordering.
 define void @store_unordered(ptr nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write) uwtable
 ; CHECK-LABEL: @store_unordered(
 ; CHECK-NEXT:    store atomic i32 10, ptr [[TMP0:%.*]] unordered, align 4
 ; CHECK-NEXT:    ret void
@@ -160,7 +160,7 @@ define void @store_unordered(ptr nocapture %0) norecurse nounwind uwtable {
 ; negative, should not deduce nosync
 ; atomic load with release ordering
 define void @load_release(ptr nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) uwtable
+; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @load_release(
 ; CHECK-NEXT:    store atomic volatile i32 10, ptr [[TMP0:%.*]] release, align 4
 ; CHECK-NEXT:    ret void
@@ -171,7 +171,7 @@ define void @load_release(ptr nocapture %0) norecurse nounwind uwtable {
 
 ; negative volatile, relaxed atomic
 define void @load_volatile_release(ptr nocapture %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) uwtable
+; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @load_volatile_release(
 ; CHECK-NEXT:    store atomic volatile i32 10, ptr [[TMP0:%.*]] release, align 4
 ; CHECK-NEXT:    ret void
@@ -182,7 +182,7 @@ define void @load_volatile_release(ptr nocapture %0) norecurse nounwind uwtable
 
 ; volatile store.
 define void @volatile_store(ptr %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) uwtable
+; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @volatile_store(
 ; CHECK-NEXT:    store volatile i32 14, ptr [[TMP0:%.*]], align 4
 ; CHECK-NEXT:    ret void
@@ -194,7 +194,7 @@ define void @volatile_store(ptr %0) norecurse nounwind uwtable {
 ; negative, should not deduce nosync
 ; volatile load.
 define i32 @volatile_load(ptr %0) norecurse nounwind uwtable {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) uwtable
+; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite) uwtable
 ; CHECK-LABEL: @volatile_load(
 ; CHECK-NEXT:    [[TMP2:%.*]] = load volatile i32, ptr [[TMP0:%.*]], align 4
 ; CHECK-NEXT:    ret i32 [[TMP2]]
@@ -204,7 +204,6 @@ define i32 @volatile_load(ptr %0) norecurse nounwind uwtable {
 }
 
 ; CHECK: Function Attrs: noinline nosync nounwind uwtable
-; CHECK-NEXT: declare void @nosync_function()
 declare void @nosync_function() noinline nounwind uwtable nosync
 
 define void @call_nosync_function() nounwind uwtable noinline {
@@ -218,7 +217,6 @@ define void @call_nosync_function() nounwind uwtable noinline {
 }
 
 ; CHECK: Function Attrs: noinline nounwind uwtable
-; CHECK-NEXT: declare void @might_sync()
 declare void @might_sync() noinline nounwind uwtable
 
 define void @call_might_sync() nounwind uwtable noinline {
@@ -280,7 +278,6 @@ define void @convergent_readnone(){
 }
 
 ; CHECK: Function Attrs: nounwind
-; CHECK-NEXT: declare void @llvm.x86.sse2.clflush(ptr)
 declare void @llvm.x86.sse2.clflush(ptr)
 @a = common global i32 0, align 4
 
diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
index 554670479ab8d45..8148bbf5ccb4ce5 100644
--- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll
+++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -218,9 +218,9 @@ entry:
 }
 
 define void @test8_2(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; FNATTRS-LABEL: define {{[^@]+}}@test8_2
-; FNATTRS-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR4]] {
+; FNATTRS-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR6:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[CALL:%.*]] = call ptr @test8_1(ptr [[P]])
 ; FNATTRS-NEXT:    store i32 10, ptr [[CALL]], align 4
@@ -253,7 +253,7 @@ declare void @llvm.masked.scatter.v4i32.v4p0(<4 x i32>%val, <4 x ptr>, i32, <4 x
 define void @test9(<4 x ptr> %ptrs, <4 x i32>%val) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
 ; FNATTRS-LABEL: define {{[^@]+}}@test9
-; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR7:[0-9]+]] {
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR8:[0-9]+]] {
 ; FNATTRS-NEXT:    call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>)
 ; FNATTRS-NEXT:    ret void
 ;
@@ -277,7 +277,7 @@ declare <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr>, i32, <4 x i1>, <4 x
 define <4 x i32> @test10(<4 x ptr> %ptrs) {
 ; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read)
 ; FNATTRS-LABEL: define {{[^@]+}}@test10
-; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] {
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR10:[0-9]+]] {
 ; FNATTRS-NEXT:    [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>, <4 x i32> undef)
 ; FNATTRS-NEXT:    ret <4 x i32> [[RES]]
 ;
@@ -301,7 +301,7 @@ declare <4 x i32> @test11_1(<4 x ptr>) argmemonly nounwind readonly
 define <4 x i32> @test11_2(<4 x ptr> %ptrs) {
 ; FNATTRS: Function Attrs: nofree nounwind memory(argmem: read)
 ; FNATTRS-LABEL: define {{[^@]+}}@test11_2
-; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] {
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR12:[0-9]+]] {
 ; FNATTRS-NEXT:    [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]])
 ; FNATTRS-NEXT:    ret <4 x i32> [[RES]]
 ;
@@ -325,7 +325,7 @@ declare <4 x i32> @test12_1(<4 x ptr>) argmemonly nounwind
 define <4 x i32> @test12_2(<4 x ptr> %ptrs) {
 ; FNATTRS: Function Attrs: nounwind memory(argmem: readwrite)
 ; FNATTRS-LABEL: define {{[^@]+}}@test12_2
-; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR12:[0-9]+]] {
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR13:[0-9]+]] {
 ; FNATTRS-NEXT:    [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]])
 ; FNATTRS-NEXT:    ret <4 x i32> [[RES]]
 ;
@@ -346,9 +346,9 @@ define <4 x i32> @test12_2(<4 x ptr> %ptrs) {
 }
 
 define i32 @volatile_load(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite)
 ; FNATTRS-LABEL: define {{[^@]+}}@volatile_load
-; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR13:[0-9]+]] {
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] {
 ; FNATTRS-NEXT:    [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4
 ; FNATTRS-NEXT:    ret i32 [[LOAD]]
 ;
@@ -570,7 +570,7 @@ define void @fptr_test2c(ptr %p, ptr %f) {
 define void @alloca_recphi() {
 ; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(none)
 ; FNATTRS-LABEL: define {{[^@]+}}@alloca_recphi
-; FNATTRS-SAME: () #[[ATTR14:[0-9]+]] {
+; FNATTRS-SAME: () #[[ATTR15:[0-9]+]] {
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    [[A:%.*]] = alloca [8 x i32], align 4
 ; FNATTRS-NEXT:    [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8
@@ -721,9 +721,9 @@ define void @op_bundle_readonly_unknown(ptr %p) {
 }
 
 define i32 @writable_readonly(ptr writable dereferenceable(4) %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: define {{[^@]+}}@writable_readonly
-; FNATTRS-SAME: (ptr readonly captures(none) dereferenceable(4) [[P:%.*]]) #[[ATTR15:[0-9]+]] {
+; FNATTRS-SAME: (ptr readonly captures(none) dereferenceable(4) [[P:%.*]]) #[[ATTR16:[0-9]+]] {
 ; FNATTRS-NEXT:    [[V:%.*]] = load i32, ptr [[P]], align 4
 ; FNATTRS-NEXT:    ret i32 [[V]]
 ;
diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll
index dc0280288b4fe3b..f846d5c7a7ea3cc 100644
--- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll
+++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll
@@ -25,7 +25,7 @@ while.body:
 }
 
 define i32 @mustprogress_load(ptr %ptr) mustprogress {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: @mustprogress_load(
 ; FNATTRS-NEXT:  entry:
 ; FNATTRS-NEXT:    br label [[WHILE_BODY:%.*]]
@@ -50,13 +50,21 @@ while.body:
 }
 
 define void @mustprogress_store(ptr %ptr) mustprogress {
-; COMMON: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write)
-; COMMON-LABEL: @mustprogress_store(
-; COMMON-NEXT:  entry:
-; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
-; COMMON:       while.body:
-; COMMON-NEXT:    store i32 0, ptr [[PTR:%.*]], align 4
-; COMMON-NEXT:    br label [[WHILE_BODY]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write, errnomem: write)
+; FNATTRS-LABEL: @mustprogress_store(
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    br label [[WHILE_BODY:%.*]]
+; FNATTRS:       while.body:
+; FNATTRS-NEXT:    store i32 0, ptr [[PTR:%.*]], align 4
+; FNATTRS-NEXT:    br label [[WHILE_BODY]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write)
+; ATTRIBUTOR-LABEL: @mustprogress_store(
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY:%.*]]
+; ATTRIBUTOR:       while.body:
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[PTR:%.*]], align 4
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY]]
 ;
 entry:
   br label %while.body
@@ -79,7 +87,7 @@ define void @mustprogress_call_unknown_fn() mustprogress {
 }
 
 define i32 @mustprogress_call_known_functions(ptr %ptr) mustprogress {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: @mustprogress_call_known_functions(
 ; FNATTRS-NEXT:    call void @mustprogress_readnone()
 ; FNATTRS-NEXT:    [[R:%.*]] = call i32 @mustprogress_load(ptr [[PTR:%.*]])
diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll
index fdf9734ebda84f8..8af4c7ef819f894 100644
--- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll
@@ -20,7 +20,7 @@ nouses-argworn-funrn_entry:
 }
 
 define void @nouses-argworn-funro(ptr writeonly %.aaa, ptr %.bbb) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read, errnomem: read)
 ; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funro
 ; FNATTRS-SAME: (ptr readnone captures(none) [[DOTAAA:%.*]], ptr readonly captures(none) [[DOTBBB:%.*]]) #[[ATTR1:[0-9]+]] {
 ; FNATTRS-NEXT:  nouses-argworn-funro_entry:
@@ -64,7 +64,7 @@ nouses-argworn-funwo_entry:
 }
 
 define void @test_store(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; FNATTRS-LABEL: define {{[^@]+}}@test_store
 ; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 1)) [[P:%.*]]) #[[ATTR3:[0-9]+]] {
 ; FNATTRS-NEXT:    store i8 0, ptr [[P]], align 1
@@ -105,7 +105,7 @@ define i8 @test_store_capture(ptr %p) {
 }
 
 define void @test_addressing(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; FNATTRS-LABEL: define {{[^@]+}}@test_addressing
 ; FNATTRS-SAME: (ptr writeonly captures(none) initializes((8, 12)) [[P:%.*]]) #[[ATTR3]] {
 ; FNATTRS-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 8
@@ -125,7 +125,7 @@ define void @test_addressing(ptr %p) {
 }
 
 define void @test_readwrite(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; FNATTRS-LABEL: define {{[^@]+}}@test_readwrite
 ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR5:[0-9]+]] {
 ; FNATTRS-NEXT:    [[V:%.*]] = load i8, ptr [[P]], align 1
@@ -145,7 +145,7 @@ define void @test_readwrite(ptr %p) {
 }
 
 define void @test_volatile(ptr %p) {
-; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite, errnomem: readwrite)
 ; FNATTRS-LABEL: define {{[^@]+}}@test_volatile
 ; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR6:[0-9]+]] {
 ; FNATTRS-NEXT:    store volatile i8 0, ptr [[P]], align 1
@@ -162,7 +162,7 @@ define void @test_volatile(ptr %p) {
 }
 
 define void @test_atomicrmw(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, errnomem: readwrite)
 ; FNATTRS-LABEL: define {{[^@]+}}@test_atomicrmw
 ; FNATTRS-SAME: (ptr captures(none) [[P:%.*]]) #[[ATTR7:[0-9]+]] {
 ; FNATTRS-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1
@@ -179,7 +179,7 @@ define void @test_atomicrmw(ptr %p) {
 }
 
 define void @test_ptrmask(ptr %p) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write, errnomem: write)
 ; FNATTRS-LABEL: define {{[^@]+}}@test_ptrmask
 ; FNATTRS-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR3]] {
 ; FNATTRS-NEXT:    [[MASK:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[P]], i64 -5)



More information about the Mlir-commits mailing list