Skip to content

Conversation

antoniofrighetto
Copy link
Contributor

Ensure alias analyses mask out errnomem location, refining the resulting modref info, when the given access/location does not alias errno. This may occur either when TBAA proves there is no alias with errno (e.g., float TBAA for the same root would be disjoint with the int-only compatible TBAA node for errno); or if the memory access size is larger than the integer size, or when the underlying object is a potentially-escaping alloca.

Related PR: #125258.

Previous discussion: https://discourse.llvm.org/t/rfc-modelling-errno-memory-effects/82972.

@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Sep 8, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 8, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Antonio Frighetto (antoniofrighetto)

Changes

Ensure alias analyses mask out errnomem location, refining the resulting modref info, when the given access/location does not alias errno. This may occur either when TBAA proves there is no alias with errno (e.g., float TBAA for the same root would be disjoint with the int-only compatible TBAA node for errno); or if the memory access size is larger than the integer size, or when the underlying object is a potentially-escaping alloca.

Related PR: #125258.

Previous discussion: https://discourse.llvm.org/t/rfc-modelling-errno-memory-effects/82972.


Full diff: https://github.com/llvm/llvm-project/pull/157495.diff

8 Files Affected:

  • (modified) llvm/include/llvm/Analysis/AliasAnalysis.h (+14)
  • (modified) llvm/include/llvm/Analysis/BasicAliasAnalysis.h (+2)
  • (modified) llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h (+1)
  • (modified) llvm/lib/Analysis/AliasAnalysis.cpp (+12)
  • (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+21-1)
  • (modified) llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp (+18)
  • (modified) llvm/test/Transforms/EarlyCSE/writeonly.ll (+1-1)
  • (added) llvm/test/Transforms/InstCombine/may-alias-errno.ll (+115)
diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h
index b7d1251aeb723..1681079054b8b 100644
--- a/llvm/include/llvm/Analysis/AliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/AliasAnalysis.h
@@ -585,6 +585,7 @@ class AAResults {
   LLVM_ABI AliasResult alias(const MemoryLocation &LocA,
                              const MemoryLocation &LocB, AAQueryInfo &AAQI,
                              const Instruction *CtxI = nullptr);
+  LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M);
 
   LLVM_ABI ModRefInfo getModRefInfoMask(const MemoryLocation &Loc,
                                         AAQueryInfo &AAQI,
@@ -744,6 +745,11 @@ class LLVM_ABI AAResults::Concept {
                             const MemoryLocation &LocB, AAQueryInfo &AAQI,
                             const Instruction *CtxI) = 0;
 
+  /// Returns an AliasResult indicating whether a specific memory location
+  /// aliases errno.
+  virtual AliasResult aliasErrno(const MemoryLocation &Loc,
+                                 const Module *M) = 0;
+
   /// @}
   //===--------------------------------------------------------------------===//
   /// \name Simple mod/ref information
@@ -805,6 +811,10 @@ template <typename AAResultT> class AAResults::Model final : public Concept {
     return Result.alias(LocA, LocB, AAQI, CtxI);
   }
 
+  AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M) override {
+    return Result.aliasErrno(Loc, M);
+  }
+
   ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
                                bool IgnoreLocals) override {
     return Result.getModRefInfoMask(Loc, AAQI, IgnoreLocals);
@@ -860,6 +870,10 @@ class AAResultBase {
     return AliasResult::MayAlias;
   }
 
+  AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M) {
+    return AliasResult::MayAlias;
+  }
+
   ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
                                bool IgnoreLocals) {
     return ModRefInfo::ModRef;
diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
index 6f37a086e323b..31875e59207b3 100644
--- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
@@ -73,6 +73,8 @@ class BasicAAResult : public AAResultBase {
                              const MemoryLocation &LocB, AAQueryInfo &AAQI,
                              const Instruction *CtxI);
 
+  LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M);
+
   LLVM_ABI ModRefInfo getModRefInfo(const CallBase *Call,
                                     const MemoryLocation &Loc,
                                     AAQueryInfo &AAQI);
diff --git a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
index 77edbe8527aae..38f9fc718824f 100644
--- a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
@@ -50,6 +50,7 @@ class TypeBasedAAResult : public AAResultBase {
   LLVM_ABI AliasResult alias(const MemoryLocation &LocA,
                              const MemoryLocation &LocB, AAQueryInfo &AAQI,
                              const Instruction *CtxI);
+  LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M);
   LLVM_ABI ModRefInfo getModRefInfoMask(const MemoryLocation &Loc,
                                         AAQueryInfo &AAQI, bool IgnoreLocals);
 
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 3ec009ca4adde..f2dc25fa5dbf5 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -148,6 +148,18 @@ AliasResult AAResults::alias(const MemoryLocation &LocA,
   return Result;
 }
 
+AliasResult AAResults::aliasErrno(const MemoryLocation &Loc, const Module *M) {
+  AliasResult Result = AliasResult::MayAlias;
+
+  for (const auto &AA : AAs) {
+    Result = AA->aliasErrno(Loc, M);
+    if (Result != AliasResult::MayAlias)
+      break;
+  }
+
+  return Result;
+}
+
 ModRefInfo AAResults::getModRefInfoMask(const MemoryLocation &Loc,
                                         bool IgnoreLocals) {
   SimpleAAQueryInfo AAQIP(*this);
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index de37c391cf254..376981da201d4 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -951,7 +951,8 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
     return ModRefInfo::NoModRef;
 
   ModRefInfo ArgMR = ME.getModRef(IRMemLocation::ArgMem);
-  ModRefInfo OtherMR = ME.getWithoutLoc(IRMemLocation::ArgMem).getModRef();
+  ModRefInfo ErrnoMR = ME.getModRef(IRMemLocation::ErrnoMem);
+  ModRefInfo OtherMR = ME.getModRef(IRMemLocation::Other);
 
   // An identified function-local object that does not escape can only be
   // accessed via call arguments. Reduce OtherMR (which includes accesses to
@@ -997,6 +998,18 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
   }
 
   ModRefInfo Result = ArgMR | OtherMR;
+
+  // Refine writes to errno memory. We can safely exclude the errno location if
+  // the given memory location is an alloca, the size of the memory access is
+  // larger than the integer size, or if TBAA proves it does not alias errno.
+  if ((ErrnoMR | Result) != Result) {
+    if (ErrnoMR == ModRefInfo::Mod && !isa<AllocaInst>(Object) &&
+        AAQI.AAR.aliasErrno(Loc, Call->getModule()) != AliasResult::NoAlias) {
+      // Preconditions above are not met, this memory location may alias errno.
+      Result |= ErrnoMR;
+    }
+  }
+
   if (!isModAndRefSet(Result))
     return Result;
 
@@ -1851,6 +1864,13 @@ AliasResult BasicAAResult::aliasCheckRecursive(
   return AliasResult::MayAlias;
 }
 
+AliasResult BasicAAResult::aliasErrno(const MemoryLocation &Loc,
+                                      const Module *M) {
+  bool DoesNotAlias =
+      Loc.Size.hasValue() && Loc.Size.getValue() * 8 > TLI.getIntSize();
+  return DoesNotAlias ? AliasResult::NoAlias : AliasResult::MayAlias;
+}
+
 /// Check whether two Values can be considered equivalent.
 ///
 /// If the values may come from different cycle iterations, this will also
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index 7025b8354564a..9cd1bb4f540ab 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -115,6 +115,7 @@
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Casting.h"
@@ -385,6 +386,23 @@ AliasResult TypeBasedAAResult::alias(const MemoryLocation &LocA,
   return AliasResult::NoAlias;
 }
 
+AliasResult TypeBasedAAResult::aliasErrno(const MemoryLocation &Loc,
+                                          const Module *M) {
+  if (!shouldUseTBAA())
+    return AliasResult::MayAlias;
+
+  const auto *N = Loc.AATags.TBAA;
+  if (!N)
+    return AliasResult::MayAlias;
+
+  const auto *ErrnoTBAAMD = M->getNamedMetadata("llvm.errno.tbaa");
+  if (!ErrnoTBAAMD || any_of(ErrnoTBAAMD->operands(), [&](const auto *Node) {
+        return Aliases(N, Node);
+      }))
+    return AliasResult::MayAlias;
+  return AliasResult::NoAlias;
+}
+
 ModRefInfo TypeBasedAAResult::getModRefInfoMask(const MemoryLocation &Loc,
                                                 AAQueryInfo &AAQI,
                                                 bool IgnoreLocals) {
diff --git a/llvm/test/Transforms/EarlyCSE/writeonly.ll b/llvm/test/Transforms/EarlyCSE/writeonly.ll
index c09b913f9ff2b..8614af2a80c59 100644
--- a/llvm/test/Transforms/EarlyCSE/writeonly.ll
+++ b/llvm/test/Transforms/EarlyCSE/writeonly.ll
@@ -17,7 +17,7 @@ define void @test() {
   ret void
 }
 
-declare void @writeonly_void() memory(write)
+declare void @writeonly_void() memory(write, errnomem: none)
 
 ; Can CSE writeonly calls, including non-nounwind/willreturn.
 define void @writeonly_cse() {
diff --git a/llvm/test/Transforms/InstCombine/may-alias-errno.ll b/llvm/test/Transforms/InstCombine/may-alias-errno.ll
new file mode 100644
index 0000000000000..71d1e723a6fad
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/may-alias-errno.ll
@@ -0,0 +1,115 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+; sinf clobbering errno, but %p cannot alias errno per C/C++ strict aliasing rules via TBAA.
+; Can do constant store-to-load forwarding.
+define float @does_not_alias_errno(ptr %p, float %f) {
+; CHECK-LABEL: define float @does_not_alias_errno(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[P]], align 4, !tbaa [[TBAA4:![0-9]+]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+entry:
+  store float 0.000000e+00, ptr %p, align 4, !tbaa !4
+  %call = call float @sinf(float %f)
+  %0 = load float, ptr %p, align 4, !tbaa !4
+  ret float %0
+}
+
+; sinf clobbering errno, but %p is alloca memory, wich can never aliases errno.
+; Can do constant store-to-load forwarding.
+define float @does_not_alias_errno_2(float %f) {
+; CHECK-LABEL: define float @does_not_alias_errno_2(
+; CHECK-SAME: float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[P:%.*]] = alloca float, align 4
+; CHECK-NEXT:    call void @escape(ptr nonnull [[P]])
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[P]], align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+entry:
+  %p = alloca float
+  call void @escape(ptr %p)
+  store float 0.0, ptr %p
+  call float @sinf(float %f)
+  %v = load float, ptr %p
+  ret float %v
+}
+
+; sinf clobbering errno, but %p is memory accessed w/ size larger than errno.
+; Can do constant store-to-load forwarding.
+define double @does_not_alias_errno_3(ptr %p, float %f) {
+; CHECK-LABEL: define double @does_not_alias_errno_3(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @escape(ptr [[P]])
+; CHECK-NEXT:    store double 0.000000e+00, ptr [[P]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    ret double 0.000000e+00
+;
+entry:
+  call void @escape(ptr %p)
+  store double 0.0, ptr %p
+  call float @sinf(float %f)
+  %v = load double, ptr %p
+  ret double %v
+}
+
+; sinf clobbering errno, unknown TBAA info, %p may alias errno.
+; Cannot do constant store-to-load forwarding.
+define float @may_alias_errno(ptr %p, float %f) {
+; CHECK-LABEL: define float @may_alias_errno(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[P]], align 4
+; CHECK-NEXT:    [[CALL:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr [[P]], align 4
+; CHECK-NEXT:    ret float [[TMP0]]
+;
+entry:
+  store float 0.000000e+00, ptr %p, align 4
+  %call = call float @sinf(float %f)
+  %0 = load float, ptr %p, align 4
+  ret float %0
+}
+
+; sinf clobbering errno, %p, a integer pointer, may alias errno.
+; Cannot do constant store-to-load forwarding.
+define i32 @may_alias_errno_2(ptr %p, float %f) {
+; CHECK-LABEL: define i32 @may_alias_errno_2(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store i32 0, ptr [[P]], align 4, !tbaa [[TBAA0:![0-9]+]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[P]], align 4, !tbaa [[TBAA0]]
+; CHECK-NEXT:    ret i32 [[TMP0]]
+;
+entry:
+  store i32 0, ptr %p, align 4, !tbaa !0
+  %call = call float @sinf(float %f)
+  %0 = load i32, ptr %p, align 4, !tbaa !0
+  ret i32 %0
+}
+
+declare float @sinf(float) memory(errnomem: write)
+declare void @escape(ptr %p)
+
+!llvm.errno.tbaa = !{!0}
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"int", !2, i64 0}
+!2 = !{!"omnipotent char", !3, i64 0}
+!3 = !{!"Simple C/C++ TBAA"}
+!4 = !{!5, !5, i64 0}
+!5 = !{!"float", !2, i64 0}
+;.
+; CHECK: [[TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
+; CHECK: [[META1]] = !{!"int", [[META2:![0-9]+]], i64 0}
+; CHECK: [[META2]] = !{!"omnipotent char", [[META3:![0-9]+]], i64 0}
+; CHECK: [[META3]] = !{!"Simple C/C++ TBAA"}
+; CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0}
+; CHECK: [[META5]] = !{!"float", [[META2]], i64 0}
+;.

@llvmbot
Copy link
Member

llvmbot commented Sep 8, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Antonio Frighetto (antoniofrighetto)

Changes

Ensure alias analyses mask out errnomem location, refining the resulting modref info, when the given access/location does not alias errno. This may occur either when TBAA proves there is no alias with errno (e.g., float TBAA for the same root would be disjoint with the int-only compatible TBAA node for errno); or if the memory access size is larger than the integer size, or when the underlying object is a potentially-escaping alloca.

Related PR: #125258.

Previous discussion: https://discourse.llvm.org/t/rfc-modelling-errno-memory-effects/82972.


Full diff: https://github.com/llvm/llvm-project/pull/157495.diff

8 Files Affected:

  • (modified) llvm/include/llvm/Analysis/AliasAnalysis.h (+14)
  • (modified) llvm/include/llvm/Analysis/BasicAliasAnalysis.h (+2)
  • (modified) llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h (+1)
  • (modified) llvm/lib/Analysis/AliasAnalysis.cpp (+12)
  • (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+21-1)
  • (modified) llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp (+18)
  • (modified) llvm/test/Transforms/EarlyCSE/writeonly.ll (+1-1)
  • (added) llvm/test/Transforms/InstCombine/may-alias-errno.ll (+115)
diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h
index b7d1251aeb723..1681079054b8b 100644
--- a/llvm/include/llvm/Analysis/AliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/AliasAnalysis.h
@@ -585,6 +585,7 @@ class AAResults {
   LLVM_ABI AliasResult alias(const MemoryLocation &LocA,
                              const MemoryLocation &LocB, AAQueryInfo &AAQI,
                              const Instruction *CtxI = nullptr);
+  LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M);
 
   LLVM_ABI ModRefInfo getModRefInfoMask(const MemoryLocation &Loc,
                                         AAQueryInfo &AAQI,
@@ -744,6 +745,11 @@ class LLVM_ABI AAResults::Concept {
                             const MemoryLocation &LocB, AAQueryInfo &AAQI,
                             const Instruction *CtxI) = 0;
 
+  /// Returns an AliasResult indicating whether a specific memory location
+  /// aliases errno.
+  virtual AliasResult aliasErrno(const MemoryLocation &Loc,
+                                 const Module *M) = 0;
+
   /// @}
   //===--------------------------------------------------------------------===//
   /// \name Simple mod/ref information
@@ -805,6 +811,10 @@ template <typename AAResultT> class AAResults::Model final : public Concept {
     return Result.alias(LocA, LocB, AAQI, CtxI);
   }
 
+  AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M) override {
+    return Result.aliasErrno(Loc, M);
+  }
+
   ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
                                bool IgnoreLocals) override {
     return Result.getModRefInfoMask(Loc, AAQI, IgnoreLocals);
@@ -860,6 +870,10 @@ class AAResultBase {
     return AliasResult::MayAlias;
   }
 
+  AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M) {
+    return AliasResult::MayAlias;
+  }
+
   ModRefInfo getModRefInfoMask(const MemoryLocation &Loc, AAQueryInfo &AAQI,
                                bool IgnoreLocals) {
     return ModRefInfo::ModRef;
diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
index 6f37a086e323b..31875e59207b3 100644
--- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h
@@ -73,6 +73,8 @@ class BasicAAResult : public AAResultBase {
                              const MemoryLocation &LocB, AAQueryInfo &AAQI,
                              const Instruction *CtxI);
 
+  LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M);
+
   LLVM_ABI ModRefInfo getModRefInfo(const CallBase *Call,
                                     const MemoryLocation &Loc,
                                     AAQueryInfo &AAQI);
diff --git a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
index 77edbe8527aae..38f9fc718824f 100644
--- a/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/TypeBasedAliasAnalysis.h
@@ -50,6 +50,7 @@ class TypeBasedAAResult : public AAResultBase {
   LLVM_ABI AliasResult alias(const MemoryLocation &LocA,
                              const MemoryLocation &LocB, AAQueryInfo &AAQI,
                              const Instruction *CtxI);
+  LLVM_ABI AliasResult aliasErrno(const MemoryLocation &Loc, const Module *M);
   LLVM_ABI ModRefInfo getModRefInfoMask(const MemoryLocation &Loc,
                                         AAQueryInfo &AAQI, bool IgnoreLocals);
 
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 3ec009ca4adde..f2dc25fa5dbf5 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -148,6 +148,18 @@ AliasResult AAResults::alias(const MemoryLocation &LocA,
   return Result;
 }
 
+AliasResult AAResults::aliasErrno(const MemoryLocation &Loc, const Module *M) {
+  AliasResult Result = AliasResult::MayAlias;
+
+  for (const auto &AA : AAs) {
+    Result = AA->aliasErrno(Loc, M);
+    if (Result != AliasResult::MayAlias)
+      break;
+  }
+
+  return Result;
+}
+
 ModRefInfo AAResults::getModRefInfoMask(const MemoryLocation &Loc,
                                         bool IgnoreLocals) {
   SimpleAAQueryInfo AAQIP(*this);
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index de37c391cf254..376981da201d4 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -951,7 +951,8 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
     return ModRefInfo::NoModRef;
 
   ModRefInfo ArgMR = ME.getModRef(IRMemLocation::ArgMem);
-  ModRefInfo OtherMR = ME.getWithoutLoc(IRMemLocation::ArgMem).getModRef();
+  ModRefInfo ErrnoMR = ME.getModRef(IRMemLocation::ErrnoMem);
+  ModRefInfo OtherMR = ME.getModRef(IRMemLocation::Other);
 
   // An identified function-local object that does not escape can only be
   // accessed via call arguments. Reduce OtherMR (which includes accesses to
@@ -997,6 +998,18 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call,
   }
 
   ModRefInfo Result = ArgMR | OtherMR;
+
+  // Refine writes to errno memory. We can safely exclude the errno location if
+  // the given memory location is an alloca, the size of the memory access is
+  // larger than the integer size, or if TBAA proves it does not alias errno.
+  if ((ErrnoMR | Result) != Result) {
+    if (ErrnoMR == ModRefInfo::Mod && !isa<AllocaInst>(Object) &&
+        AAQI.AAR.aliasErrno(Loc, Call->getModule()) != AliasResult::NoAlias) {
+      // Preconditions above are not met, this memory location may alias errno.
+      Result |= ErrnoMR;
+    }
+  }
+
   if (!isModAndRefSet(Result))
     return Result;
 
@@ -1851,6 +1864,13 @@ AliasResult BasicAAResult::aliasCheckRecursive(
   return AliasResult::MayAlias;
 }
 
+AliasResult BasicAAResult::aliasErrno(const MemoryLocation &Loc,
+                                      const Module *M) {
+  bool DoesNotAlias =
+      Loc.Size.hasValue() && Loc.Size.getValue() * 8 > TLI.getIntSize();
+  return DoesNotAlias ? AliasResult::NoAlias : AliasResult::MayAlias;
+}
+
 /// Check whether two Values can be considered equivalent.
 ///
 /// If the values may come from different cycle iterations, this will also
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index 7025b8354564a..9cd1bb4f540ab 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -115,6 +115,7 @@
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Casting.h"
@@ -385,6 +386,23 @@ AliasResult TypeBasedAAResult::alias(const MemoryLocation &LocA,
   return AliasResult::NoAlias;
 }
 
+AliasResult TypeBasedAAResult::aliasErrno(const MemoryLocation &Loc,
+                                          const Module *M) {
+  if (!shouldUseTBAA())
+    return AliasResult::MayAlias;
+
+  const auto *N = Loc.AATags.TBAA;
+  if (!N)
+    return AliasResult::MayAlias;
+
+  const auto *ErrnoTBAAMD = M->getNamedMetadata("llvm.errno.tbaa");
+  if (!ErrnoTBAAMD || any_of(ErrnoTBAAMD->operands(), [&](const auto *Node) {
+        return Aliases(N, Node);
+      }))
+    return AliasResult::MayAlias;
+  return AliasResult::NoAlias;
+}
+
 ModRefInfo TypeBasedAAResult::getModRefInfoMask(const MemoryLocation &Loc,
                                                 AAQueryInfo &AAQI,
                                                 bool IgnoreLocals) {
diff --git a/llvm/test/Transforms/EarlyCSE/writeonly.ll b/llvm/test/Transforms/EarlyCSE/writeonly.ll
index c09b913f9ff2b..8614af2a80c59 100644
--- a/llvm/test/Transforms/EarlyCSE/writeonly.ll
+++ b/llvm/test/Transforms/EarlyCSE/writeonly.ll
@@ -17,7 +17,7 @@ define void @test() {
   ret void
 }
 
-declare void @writeonly_void() memory(write)
+declare void @writeonly_void() memory(write, errnomem: none)
 
 ; Can CSE writeonly calls, including non-nounwind/willreturn.
 define void @writeonly_cse() {
diff --git a/llvm/test/Transforms/InstCombine/may-alias-errno.ll b/llvm/test/Transforms/InstCombine/may-alias-errno.ll
new file mode 100644
index 0000000000000..71d1e723a6fad
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/may-alias-errno.ll
@@ -0,0 +1,115 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+; sinf clobbering errno, but %p cannot alias errno per C/C++ strict aliasing rules via TBAA.
+; Can do constant store-to-load forwarding.
+define float @does_not_alias_errno(ptr %p, float %f) {
+; CHECK-LABEL: define float @does_not_alias_errno(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[P]], align 4, !tbaa [[TBAA4:![0-9]+]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+entry:
+  store float 0.000000e+00, ptr %p, align 4, !tbaa !4
+  %call = call float @sinf(float %f)
+  %0 = load float, ptr %p, align 4, !tbaa !4
+  ret float %0
+}
+
+; sinf clobbering errno, but %p is alloca memory, wich can never aliases errno.
+; Can do constant store-to-load forwarding.
+define float @does_not_alias_errno_2(float %f) {
+; CHECK-LABEL: define float @does_not_alias_errno_2(
+; CHECK-SAME: float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[P:%.*]] = alloca float, align 4
+; CHECK-NEXT:    call void @escape(ptr nonnull [[P]])
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[P]], align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    ret float 0.000000e+00
+;
+entry:
+  %p = alloca float
+  call void @escape(ptr %p)
+  store float 0.0, ptr %p
+  call float @sinf(float %f)
+  %v = load float, ptr %p
+  ret float %v
+}
+
+; sinf clobbering errno, but %p is memory accessed w/ size larger than errno.
+; Can do constant store-to-load forwarding.
+define double @does_not_alias_errno_3(ptr %p, float %f) {
+; CHECK-LABEL: define double @does_not_alias_errno_3(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @escape(ptr [[P]])
+; CHECK-NEXT:    store double 0.000000e+00, ptr [[P]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    ret double 0.000000e+00
+;
+entry:
+  call void @escape(ptr %p)
+  store double 0.0, ptr %p
+  call float @sinf(float %f)
+  %v = load double, ptr %p
+  ret double %v
+}
+
+; sinf clobbering errno, unknown TBAA info, %p may alias errno.
+; Cannot do constant store-to-load forwarding.
+define float @may_alias_errno(ptr %p, float %f) {
+; CHECK-LABEL: define float @may_alias_errno(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[P]], align 4
+; CHECK-NEXT:    [[CALL:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr [[P]], align 4
+; CHECK-NEXT:    ret float [[TMP0]]
+;
+entry:
+  store float 0.000000e+00, ptr %p, align 4
+  %call = call float @sinf(float %f)
+  %0 = load float, ptr %p, align 4
+  ret float %0
+}
+
+; sinf clobbering errno, %p, a integer pointer, may alias errno.
+; Cannot do constant store-to-load forwarding.
+define i32 @may_alias_errno_2(ptr %p, float %f) {
+; CHECK-LABEL: define i32 @may_alias_errno_2(
+; CHECK-SAME: ptr [[P:%.*]], float [[F:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store i32 0, ptr [[P]], align 4, !tbaa [[TBAA0:![0-9]+]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[P]], align 4, !tbaa [[TBAA0]]
+; CHECK-NEXT:    ret i32 [[TMP0]]
+;
+entry:
+  store i32 0, ptr %p, align 4, !tbaa !0
+  %call = call float @sinf(float %f)
+  %0 = load i32, ptr %p, align 4, !tbaa !0
+  ret i32 %0
+}
+
+declare float @sinf(float) memory(errnomem: write)
+declare void @escape(ptr %p)
+
+!llvm.errno.tbaa = !{!0}
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"int", !2, i64 0}
+!2 = !{!"omnipotent char", !3, i64 0}
+!3 = !{!"Simple C/C++ TBAA"}
+!4 = !{!5, !5, i64 0}
+!5 = !{!"float", !2, i64 0}
+;.
+; CHECK: [[TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
+; CHECK: [[META1]] = !{!"int", [[META2:![0-9]+]], i64 0}
+; CHECK: [[META2]] = !{!"omnipotent char", [[META3:![0-9]+]], i64 0}
+; CHECK: [[META3]] = !{!"Simple C/C++ TBAA"}
+; CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0}
+; CHECK: [[META5]] = !{!"float", [[META2]], i64 0}
+;.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the isModOrRefSet() check is needed, it is already covered by (ErrnoMR | Result) != Result.

Also I think the isIdentifiedFunctionLocal() check belongs inside aliasErrno(). This will repeat the getUnderlyingObject() call, but from a quick test (with this commit: 60cab1e) this doesn't seem to affect compile-time, so I think it's fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Kept it there for compile-time concerns, moved it in aliasErrno.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use a function other than cosf for this. It's confusing, as the actual cosf wouldn't have these memory effects...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most of this comment belongs into aliasErrno() now.

Ensure alias analyses mask out `errnomem` location, refining the
resulting modref info, when the given access/location does not
alias errno. This may occur either when TBAA proves there is no
alias with errno (e.g., float TBAA for the same root would be
disjoint with the int-only compatible TBAA node for errno); or
if the memory access size is larger than the integer size, or
when the underlying object is a potentially-escaping alloca.

Previous discussion: https://discourse.llvm.org/t/rfc-modelling-errno-memory-effects/82972.
@antoniofrighetto antoniofrighetto merged commit f9f62ef into llvm:main Sep 16, 2025
5 checks passed
@madhur13490
Copy link
Contributor

This change has caused massive failure in our CI. Many test-suites are failing with the below assert:

LLVM ERROR: Cannot implicitly convert a scalable size to a fixed-width size in `TypeSize::operator ScalarTy()`
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.

#3 0x00004000000608dc (linux-vdso.so.1+0x8dc)                                                                                                                                                                                                     
   6   │  #4 0x00004000004af200 __pthread_kill_implementation ./nptl/pthread_kill.c:44:76                                                                                                                                                                   
   7   │  #5 0x000040000046a67c gsignal ./signal/../sysdeps/posix/raise.c:27:6                                                                                                                                                                              
   8   │  #6 0x0000400000457130 abort ./stdlib/abort.c:81:7                                                                                                                                                                                                 
   9   │  #7 0x00000000008b135c (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x8b135c)                                                                                                                                                            
  10   │  #8 0x00000000008b13f0 llvm::reportFatalInternalError(llvm::StringRef) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x8b13f0)                                                                                                            
  11   │  #9 0x0000000002939348 (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x2939348)                                                                                                                                                           
  12   │ #10 0x000000000291cadc llvm::AAResults::aliasErrno(llvm::MemoryLocation const&, llvm::Module const*) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x291cadc)                                                                             
  13   │ #11 0x00000000029384f0 llvm::BasicAAResult::getModRefInfo(llvm::CallBase const*, llvm::MemoryLocation const&, llvm::AAQueryInfo&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x29384f0)                                                
  14   │ #12 0x000000000291e6ec llvm::AAResults::getModRefInfo(llvm::Instruction const*, std::optional const&, llvm::AAQueryInfo&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x291e6ec)                                  
  15   │ #13 0x0000000002b054d4 llvm::MemoryDependenceResults::getSimplePointerDependencyFrom(llvm::MemoryLocation const&, bool, llvm::ilist_iterator_w_bit                                                                                    
                   │ , false, false>, llvm::BasicBlock*, llvm::Instruction*, unsigned int*, llvm::BatchAAResults&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x2b054d4)                                                                                    
  16   │ #14 0x0000000002b0a43c llvm::MemoryDependenceResults::getPointerDependencyFrom(llvm::MemoryLocation const&, bool, llvm::ilist_iterator_w_bits, fals
       │ e, false>, llvm::BasicBlock*, llvm::Instruction*, unsigned int*, llvm::BatchAAResults&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x2b0a43c)                                                                                          
  17   │ #15 0x0000000002b0a600 llvm::MemoryDependenceResults::getNonLocalInfoForBlock(llvm::Instruction*, llvm::MemoryLocation const&, bool, llvm::BasicBlock*, std::vector>*, unsigned int,
       │  llvm::BatchAAResults&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x2b0a600)                                                                                                                                                          
  18   │ #16 0x0000000002b0bbb0 llvm::MemoryDependenceResults::getNonLocalPointerDepFromBB(llvm::Instruction*, llvm::PHITransAddr const&, llvm::MemoryLocation const&, bool, llvm::BasicBlock*, llvm::SmallVectorImpl&, llvm::Small
       │ DenseMap, llvm::detail::DenseMapPair>&, bool, bool) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x2b0bbb0)           
  19   │ #17 0x0000000002b0d7bc llvm::MemoryDependenceResults::getNonLocalPointerDependency(llvm::Instruction*, llvm::SmallVectorImpl&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x2b0d7bc)                          
  20   │ #18 0x000000000234ae28 llvm::GVNPass::processNonLocalLoad(llvm::LoadInst*) (.part.0) GVN.cpp:0:0                                                                                                                                                   
  21   │ #19 0x000000000234d3d4 llvm::GVNPass::processInstruction(llvm::Instruction*) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x234d3d4)                                                                                                     
  22   │ #20 0x000000000234dcac llvm::GVNPass::processBlock(llvm::BasicBlock*) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x234dcac)                                                                                                            
  23   │ #21 0x000000000234e1cc llvm::GVNPass::runImpl(llvm::Function&, llvm::AssumptionCache&, llvm::DominatorTree&, llvm::TargetLibraryInfo const&, llvm::AAResults&, llvm::MemoryDependenceResults*, llvm::LoopInfo&, llvm::OptimizationRemarkEmitter*, l
       │ lvm::MemorySSA*) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x234e1cc)                                                                                                                                                                 
  24   │ #22 0x000000000234ed1c llvm::GVNPass::run(llvm::Function&, llvm::AnalysisManager&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x234ed1c)                                                                               
  25   │ #23 0x00000000011b5168 llvm::detail::PassModel>::run(llvm::Function&, llvm::AnalysisManager&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x11b5168
       │ )                                                                                                                                                                                                                                                  
  26   │ #24 0x000000000327a6d8 llvm::PassManager>::run(llvm::Function&, llvm::AnalysisManager&) (/work/llvm/LA/main/20250916104529-0648c518/bin/ld.lld+0x327a6d8)                                                                                                                                                                                           

To reproduce, please run LLVM test-suite from the standard make commands.

@nikic
Copy link
Contributor

nikic commented Sep 17, 2025

@madhur13490 Are you still seeing this after 0996696? That should have fixed it.

@asb
Copy link
Contributor

asb commented Sep 17, 2025

I've also just bisected a failure to this https://lab.llvm.org/buildbot/#/builders/214/builds/622 - the run containing 0996696 hasn't completed yet but has got past the point it should have failed at, so it appears to have fixed the issue.

@nikic
Copy link
Contributor

nikic commented Sep 17, 2025

We should probably actually remove that implicit conversion at some point -- I filed #159290 to track.

@madhur13490
Copy link
Contributor

Ok, that is a pretty recent change. I synced this morning and was working on that build. Resync + build seems to fix the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants