Skip to content

Conversation

@antoniofrighetto
Copy link
Contributor

Following up on #110484, it should be possible to drop callCaptureBefore, whose last user is MemCpyOpt, while retaining its additional precision by encapsulating it into a dedicated CapturesBeforeAnalysis. This uses PointerMayBeCapturedBefore and is more precise than EarliestEscapeAnalysis, though more expensive. Also allow users to query them in chain when needed.

…alysis (NFCI)

Following up on llvm#110484, it should be possible to drop
`callCaptureBefore`, whose last user is MemCpyOpt, while
retaining its additional precision by encapsulating it
into a dedicated CapturesBeforeAnalysis. This uses
`PointerMayBeCapturedBefore` and is more precise than
EarliestEscapeAnalysis, though more expensive. Also allow
users to query them in chain when needed.
@llvmbot llvmbot added llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Jan 7, 2026
@llvmbot
Copy link
Member

llvmbot commented Jan 7, 2026

@llvm/pr-subscribers-llvm-analysis

Author: Antonio Frighetto (antoniofrighetto)

Changes

Following up on #110484, it should be possible to drop callCaptureBefore, whose last user is MemCpyOpt, while retaining its additional precision by encapsulating it into a dedicated CapturesBeforeAnalysis. This uses PointerMayBeCapturedBefore and is more precise than EarliestEscapeAnalysis, though more expensive. Also allow users to query them in chain when needed.


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

4 Files Affected:

  • (modified) llvm/include/llvm/Analysis/AliasAnalysis.h (+31-16)
  • (modified) llvm/lib/Analysis/AliasAnalysis.cpp (-65)
  • (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+26)
  • (modified) llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp (+9-8)
diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h
index 878b7e7a1fb3b..0bdfa797e670c 100644
--- a/llvm/include/llvm/Analysis/AliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/AliasAnalysis.h
@@ -198,6 +198,37 @@ class LLVM_ABI EarliestEscapeAnalysis final : public CaptureAnalysis {
   void removeInstruction(Instruction *I);
 };
 
+/// Context-sensitive CaptureAnalysis provider, which precisely determines
+/// whether an object is captured before a specific instruction using
+/// PointerMayBeCapturedBefore.
+class LLVM_ABI CapturesBeforeAnalysis final : public CaptureAnalysis {
+  const DominatorTree &DT;
+  const LoopInfo *LI;
+
+public:
+  CapturesBeforeAnalysis(const DominatorTree &DT, const LoopInfo *LI = nullptr)
+      : DT(DT), LI(LI) {}
+
+  CaptureComponents getCapturesBefore(const Value *Object, const Instruction *I,
+                                      bool OrAt) override;
+};
+
+/// A CaptureAnalysis provider that chains EarliestEscapeAnalysis and
+/// CapturesBeforeAnalysis. It favours the former, cached but approximate;
+/// falling back to the latter, precise but expensive, when needed.
+class LLVM_ABI ChainedCaptureAnalysis final : public CaptureAnalysis {
+  EarliestEscapeAnalysis &EEA;
+  CapturesBeforeAnalysis &CBA;
+
+public:
+  ChainedCaptureAnalysis(EarliestEscapeAnalysis &EEA,
+                         CapturesBeforeAnalysis &CBA)
+      : EEA(EEA), CBA(CBA) {}
+
+  CaptureComponents getCapturesBefore(const Value *Object, const Instruction *I,
+                                      bool OrAt) override;
+};
+
 /// Cache key for BasicAA results. It only includes the pointer and size from
 /// MemoryLocation, as BasicAA is AATags independent. Additionally, it includes
 /// the value of MayBeCrossIteration, which may affect BasicAA results.
@@ -531,22 +562,6 @@ class AAResults {
   LLVM_ABI ModRefInfo getModRefInfo(const Instruction *I1,
                                     const Instruction *I2);
 
-  /// Return information about whether a particular call site modifies
-  /// or reads the specified memory location \p MemLoc before instruction \p I
-  /// in a BasicBlock.
-  ModRefInfo callCapturesBefore(const Instruction *I,
-                                const MemoryLocation &MemLoc,
-                                DominatorTree *DT) {
-    SimpleAAQueryInfo AAQIP(*this);
-    return callCapturesBefore(I, MemLoc, DT, AAQIP);
-  }
-
-  /// A convenience wrapper to synthesize a memory location.
-  ModRefInfo callCapturesBefore(const Instruction *I, const Value *P,
-                                LocationSize Size, DominatorTree *DT) {
-    return callCapturesBefore(I, MemoryLocation(P, Size), DT);
-  }
-
   /// @}
   //===--------------------------------------------------------------------===//
   /// \name Higher level methods for querying mod/ref information.
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index fd2f7c1ea9c8d..fd34204eff003 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -616,71 +616,6 @@ ModRefInfo AAResults::getModRefInfo(const Instruction *I,
   }
 }
 
-/// Return information about whether a particular call site modifies
-/// or reads the specified memory location \p MemLoc before instruction \p I
-/// in a BasicBlock.
-/// FIXME: this is really just shoring-up a deficiency in alias analysis.
-/// BasicAA isn't willing to spend linear time determining whether an alloca
-/// was captured before or after this particular call, while we are. However,
-/// with a smarter AA in place, this test is just wasting compile time.
-ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
-                                         const MemoryLocation &MemLoc,
-                                         DominatorTree *DT,
-                                         AAQueryInfo &AAQI) {
-  if (!DT)
-    return ModRefInfo::ModRef;
-
-  const Value *Object = getUnderlyingObject(MemLoc.Ptr);
-  if (!isIdentifiedFunctionLocal(Object))
-    return ModRefInfo::ModRef;
-
-  const auto *Call = dyn_cast<CallBase>(I);
-  if (!Call || Call == Object)
-    return ModRefInfo::ModRef;
-
-  if (capturesAnything(PointerMayBeCapturedBefore(
-          Object, /* ReturnCaptures */ true, I, DT,
-          /* include Object */ true, CaptureComponents::Provenance)))
-    return ModRefInfo::ModRef;
-
-  unsigned ArgNo = 0;
-  ModRefInfo R = ModRefInfo::NoModRef;
-  // Set flag only if no May found and all operands processed.
-  for (auto CI = Call->data_operands_begin(), CE = Call->data_operands_end();
-       CI != CE; ++CI, ++ArgNo) {
-    // Only look at the no-capture or byval pointer arguments.  If this
-    // pointer were passed to arguments that were neither of these, then it
-    // couldn't be no-capture.
-    if (!(*CI)->getType()->isPointerTy())
-      continue;
-
-    // Make sure we still check captures(ret: address, provenance) and
-    // captures(address) arguments, as these wouldn't be treated as a capture
-    // at the call-site.
-    CaptureInfo Captures = Call->getCaptureInfo(ArgNo);
-    if (capturesAnyProvenance(Captures.getOtherComponents()))
-      continue;
-
-    AliasResult AR =
-        alias(MemoryLocation::getBeforeOrAfter(*CI),
-              MemoryLocation::getBeforeOrAfter(Object), AAQI, Call);
-    // If this is a no-capture pointer argument, see if we can tell that it
-    // is impossible to alias the pointer we're checking.  If not, we have to
-    // assume that the call could touch the pointer, even though it doesn't
-    // escape.
-    if (AR == AliasResult::NoAlias)
-      continue;
-    if (Call->doesNotAccessMemory(ArgNo))
-      continue;
-    if (Call->onlyReadsMemory(ArgNo)) {
-      R = ModRefInfo::Ref;
-      continue;
-    }
-    return ModRefInfo::ModRef;
-  }
-  return R;
-}
-
 /// canBasicBlockModify - Return true if it is possible for execution of the
 /// specified basic block to modify the location Loc.
 ///
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 1d5f9ac465808..f085d720ba597 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -270,6 +270,32 @@ void EarliestEscapeAnalysis::removeInstruction(Instruction *I) {
   }
 }
 
+CaptureComponents
+CapturesBeforeAnalysis::getCapturesBefore(const Value *Object,
+                                          const Instruction *I, bool OrAt) {
+  assert(I && "CapturesBeforeAnalysis requires a context instruction.");
+  if (!isIdentifiedFunctionLocal(Object))
+    return CaptureComponents::Provenance;
+
+  return PointerMayBeCapturedBefore(
+      Object, /* ReturnCaptures */ true, I, &DT, /* IncludeI */ true,
+      CaptureComponents::Provenance, capturesAnything, LI);
+}
+
+CaptureComponents
+ChainedCaptureAnalysis::getCapturesBefore(const Value *Object,
+                                          const Instruction *I, bool OrAt) {
+  CaptureComponents CC = EEA.getCapturesBefore(Object, I, OrAt);
+  if (!capturesAnyProvenance(CC))
+    return CC;
+
+  // If we still happen not to have the context instruction, we cannot provide
+  // more precision than EarliestEscapeAnalysis.
+  if (!I)
+    return CC;
+  return CBA.getCapturesBefore(Object, I, OrAt);
+}
+
 //===----------------------------------------------------------------------===//
 // GetElementPtr Instruction Decomposition and Analysis
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
index f054b21f2e9dd..5f0e90eed747f 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -1048,14 +1048,15 @@ bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad,
   // In addition to knowing that the call does not access src in some
   // unexpected manner, for example via a global, which we deduce from
   // the use analysis, we also need to know that it does not sneakily
-  // access dest.  We rely on AA to figure this out for us.
-  MemoryLocation DestWithSrcSize(cpyDest, LocationSize::precise(srcSize));
-  ModRefInfo MR = BAA.getModRefInfo(C, DestWithSrcSize);
-  // If necessary, perform additional analysis.
-  if (isModOrRefSet(MR))
-    MR = BAA.callCapturesBefore(C, DestWithSrcSize, DT);
-  if (isModOrRefSet(MR))
-    return false;
+  // access dest. We rely on AA with CapturesBeforeAnalysis to figure this out.
+  {
+    MemoryLocation DestWithSrcSize(cpyDest, LocationSize::precise(srcSize));
+    CapturesBeforeAnalysis CBA(*DT);
+    ChainedCaptureAnalysis CCA(*EEA, CBA);
+    BatchAAResults BAAWithCCA(*AA, &CCA);
+    if (isModOrRefSet(BAAWithCCA.getModRefInfo(C, DestWithSrcSize)))
+      return false;
+  }
 
   // We can't create address space casts here because we don't know if they're
   // safe for the target.

@llvmbot
Copy link
Member

llvmbot commented Jan 7, 2026

@llvm/pr-subscribers-llvm-transforms

Author: Antonio Frighetto (antoniofrighetto)

Changes

Following up on #110484, it should be possible to drop callCaptureBefore, whose last user is MemCpyOpt, while retaining its additional precision by encapsulating it into a dedicated CapturesBeforeAnalysis. This uses PointerMayBeCapturedBefore and is more precise than EarliestEscapeAnalysis, though more expensive. Also allow users to query them in chain when needed.


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

4 Files Affected:

  • (modified) llvm/include/llvm/Analysis/AliasAnalysis.h (+31-16)
  • (modified) llvm/lib/Analysis/AliasAnalysis.cpp (-65)
  • (modified) llvm/lib/Analysis/BasicAliasAnalysis.cpp (+26)
  • (modified) llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp (+9-8)
diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h
index 878b7e7a1fb3b..0bdfa797e670c 100644
--- a/llvm/include/llvm/Analysis/AliasAnalysis.h
+++ b/llvm/include/llvm/Analysis/AliasAnalysis.h
@@ -198,6 +198,37 @@ class LLVM_ABI EarliestEscapeAnalysis final : public CaptureAnalysis {
   void removeInstruction(Instruction *I);
 };
 
+/// Context-sensitive CaptureAnalysis provider, which precisely determines
+/// whether an object is captured before a specific instruction using
+/// PointerMayBeCapturedBefore.
+class LLVM_ABI CapturesBeforeAnalysis final : public CaptureAnalysis {
+  const DominatorTree &DT;
+  const LoopInfo *LI;
+
+public:
+  CapturesBeforeAnalysis(const DominatorTree &DT, const LoopInfo *LI = nullptr)
+      : DT(DT), LI(LI) {}
+
+  CaptureComponents getCapturesBefore(const Value *Object, const Instruction *I,
+                                      bool OrAt) override;
+};
+
+/// A CaptureAnalysis provider that chains EarliestEscapeAnalysis and
+/// CapturesBeforeAnalysis. It favours the former, cached but approximate;
+/// falling back to the latter, precise but expensive, when needed.
+class LLVM_ABI ChainedCaptureAnalysis final : public CaptureAnalysis {
+  EarliestEscapeAnalysis &EEA;
+  CapturesBeforeAnalysis &CBA;
+
+public:
+  ChainedCaptureAnalysis(EarliestEscapeAnalysis &EEA,
+                         CapturesBeforeAnalysis &CBA)
+      : EEA(EEA), CBA(CBA) {}
+
+  CaptureComponents getCapturesBefore(const Value *Object, const Instruction *I,
+                                      bool OrAt) override;
+};
+
 /// Cache key for BasicAA results. It only includes the pointer and size from
 /// MemoryLocation, as BasicAA is AATags independent. Additionally, it includes
 /// the value of MayBeCrossIteration, which may affect BasicAA results.
@@ -531,22 +562,6 @@ class AAResults {
   LLVM_ABI ModRefInfo getModRefInfo(const Instruction *I1,
                                     const Instruction *I2);
 
-  /// Return information about whether a particular call site modifies
-  /// or reads the specified memory location \p MemLoc before instruction \p I
-  /// in a BasicBlock.
-  ModRefInfo callCapturesBefore(const Instruction *I,
-                                const MemoryLocation &MemLoc,
-                                DominatorTree *DT) {
-    SimpleAAQueryInfo AAQIP(*this);
-    return callCapturesBefore(I, MemLoc, DT, AAQIP);
-  }
-
-  /// A convenience wrapper to synthesize a memory location.
-  ModRefInfo callCapturesBefore(const Instruction *I, const Value *P,
-                                LocationSize Size, DominatorTree *DT) {
-    return callCapturesBefore(I, MemoryLocation(P, Size), DT);
-  }
-
   /// @}
   //===--------------------------------------------------------------------===//
   /// \name Higher level methods for querying mod/ref information.
diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index fd2f7c1ea9c8d..fd34204eff003 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -616,71 +616,6 @@ ModRefInfo AAResults::getModRefInfo(const Instruction *I,
   }
 }
 
-/// Return information about whether a particular call site modifies
-/// or reads the specified memory location \p MemLoc before instruction \p I
-/// in a BasicBlock.
-/// FIXME: this is really just shoring-up a deficiency in alias analysis.
-/// BasicAA isn't willing to spend linear time determining whether an alloca
-/// was captured before or after this particular call, while we are. However,
-/// with a smarter AA in place, this test is just wasting compile time.
-ModRefInfo AAResults::callCapturesBefore(const Instruction *I,
-                                         const MemoryLocation &MemLoc,
-                                         DominatorTree *DT,
-                                         AAQueryInfo &AAQI) {
-  if (!DT)
-    return ModRefInfo::ModRef;
-
-  const Value *Object = getUnderlyingObject(MemLoc.Ptr);
-  if (!isIdentifiedFunctionLocal(Object))
-    return ModRefInfo::ModRef;
-
-  const auto *Call = dyn_cast<CallBase>(I);
-  if (!Call || Call == Object)
-    return ModRefInfo::ModRef;
-
-  if (capturesAnything(PointerMayBeCapturedBefore(
-          Object, /* ReturnCaptures */ true, I, DT,
-          /* include Object */ true, CaptureComponents::Provenance)))
-    return ModRefInfo::ModRef;
-
-  unsigned ArgNo = 0;
-  ModRefInfo R = ModRefInfo::NoModRef;
-  // Set flag only if no May found and all operands processed.
-  for (auto CI = Call->data_operands_begin(), CE = Call->data_operands_end();
-       CI != CE; ++CI, ++ArgNo) {
-    // Only look at the no-capture or byval pointer arguments.  If this
-    // pointer were passed to arguments that were neither of these, then it
-    // couldn't be no-capture.
-    if (!(*CI)->getType()->isPointerTy())
-      continue;
-
-    // Make sure we still check captures(ret: address, provenance) and
-    // captures(address) arguments, as these wouldn't be treated as a capture
-    // at the call-site.
-    CaptureInfo Captures = Call->getCaptureInfo(ArgNo);
-    if (capturesAnyProvenance(Captures.getOtherComponents()))
-      continue;
-
-    AliasResult AR =
-        alias(MemoryLocation::getBeforeOrAfter(*CI),
-              MemoryLocation::getBeforeOrAfter(Object), AAQI, Call);
-    // If this is a no-capture pointer argument, see if we can tell that it
-    // is impossible to alias the pointer we're checking.  If not, we have to
-    // assume that the call could touch the pointer, even though it doesn't
-    // escape.
-    if (AR == AliasResult::NoAlias)
-      continue;
-    if (Call->doesNotAccessMemory(ArgNo))
-      continue;
-    if (Call->onlyReadsMemory(ArgNo)) {
-      R = ModRefInfo::Ref;
-      continue;
-    }
-    return ModRefInfo::ModRef;
-  }
-  return R;
-}
-
 /// canBasicBlockModify - Return true if it is possible for execution of the
 /// specified basic block to modify the location Loc.
 ///
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 1d5f9ac465808..f085d720ba597 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -270,6 +270,32 @@ void EarliestEscapeAnalysis::removeInstruction(Instruction *I) {
   }
 }
 
+CaptureComponents
+CapturesBeforeAnalysis::getCapturesBefore(const Value *Object,
+                                          const Instruction *I, bool OrAt) {
+  assert(I && "CapturesBeforeAnalysis requires a context instruction.");
+  if (!isIdentifiedFunctionLocal(Object))
+    return CaptureComponents::Provenance;
+
+  return PointerMayBeCapturedBefore(
+      Object, /* ReturnCaptures */ true, I, &DT, /* IncludeI */ true,
+      CaptureComponents::Provenance, capturesAnything, LI);
+}
+
+CaptureComponents
+ChainedCaptureAnalysis::getCapturesBefore(const Value *Object,
+                                          const Instruction *I, bool OrAt) {
+  CaptureComponents CC = EEA.getCapturesBefore(Object, I, OrAt);
+  if (!capturesAnyProvenance(CC))
+    return CC;
+
+  // If we still happen not to have the context instruction, we cannot provide
+  // more precision than EarliestEscapeAnalysis.
+  if (!I)
+    return CC;
+  return CBA.getCapturesBefore(Object, I, OrAt);
+}
+
 //===----------------------------------------------------------------------===//
 // GetElementPtr Instruction Decomposition and Analysis
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
index f054b21f2e9dd..5f0e90eed747f 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -1048,14 +1048,15 @@ bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad,
   // In addition to knowing that the call does not access src in some
   // unexpected manner, for example via a global, which we deduce from
   // the use analysis, we also need to know that it does not sneakily
-  // access dest.  We rely on AA to figure this out for us.
-  MemoryLocation DestWithSrcSize(cpyDest, LocationSize::precise(srcSize));
-  ModRefInfo MR = BAA.getModRefInfo(C, DestWithSrcSize);
-  // If necessary, perform additional analysis.
-  if (isModOrRefSet(MR))
-    MR = BAA.callCapturesBefore(C, DestWithSrcSize, DT);
-  if (isModOrRefSet(MR))
-    return false;
+  // access dest. We rely on AA with CapturesBeforeAnalysis to figure this out.
+  {
+    MemoryLocation DestWithSrcSize(cpyDest, LocationSize::precise(srcSize));
+    CapturesBeforeAnalysis CBA(*DT);
+    ChainedCaptureAnalysis CCA(*EEA, CBA);
+    BatchAAResults BAAWithCCA(*AA, &CCA);
+    if (isModOrRefSet(BAAWithCCA.getModRefInfo(C, DestWithSrcSize)))
+      return false;
+  }
 
   // We can't create address space casts here because we don't know if they're
   // safe for the target.

@antoniofrighetto
Copy link
Contributor Author

Kind ping on direction.

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:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants