[clang] [compiler-rt] [llvm] [TSan] Use EscapeAnalysis to eliminate redundant instrumentation (PR #192945)

via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 20 04:25:27 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Alexey Paznikov (apaznikov)

<details>
<summary>Changes</summary>

**Depends on #<!-- -->169896**

## Summary
This PR integrates the new `EscapeAnalysis` pass (introduced in #<!-- -->169896) into ThreadSanitizer to identify and eliminate redundant memory access instrumentation.

By querying the analysis, TSan determines whether a memory allocation (stack or heap) remains thread-local. Accesses to such "non-escaping" objects cannot participate in data races and are therefore safe to exclude from runtime checks.

*(Note for reviewers: This is a stacked PR. Please review the core analysis implementation in #<!-- -->169896 first. This PR focuses on the direct use of the analysis inside TSan and the resulting overhead reductions.)*

## Impact
*   **Runtime Performance:** Eliminating checks for thread-local data significantly reduces overhead, especially in functions with heavy usage of temporary buffers or local aggregates.
*   **Memory Overhead:** By not instrumenting local allocations, TSan avoids allocating shadow memory for them. This leads to a measurable reduction in memory consumption.

## Motivation & Potential Impact
This work is based on our research [1] into optimizing dynamic race detectors. Our experiments show that Escape Analysis (EA) is highly effective for specific workloads and complementary to other techniques.

**Runtime Speedup (EA Only):**
In the paper evaluation, EA alone reaches:
*   **SQLite:** 1.17x speedup.
*   **FFmpeg:** 1.05x speedup.
*   **MySQL Select:** 1.04x speedup.
*   **MySQL Write-only:** 1.04x speedup.
*   **Selected Chromium benchmarks:** 1.04x on Parser: HTML5 Render, 1.14x on Paint: Transform Changes, 1.15x on SVG: SvgCubics, and 1.18x on Image Decoder: WebP. Speedometer 3.1 is neutral at 1.00x.

**Memory Overhead Reduction:**
A key advantage of EA is reducing the memory footprint of the sanitizer by preventing shadow memory allocation for thread-local objects.
In the full optimization suite, where EA is the primary driver of the memory savings, the paper reports:
*   **MySQL:** 6.0% reduction.
*   **FFmpeg:** 5.5% reduction.
*   **Chromium:** 4.8% reduction.
*   **SQLite:** 4.4% reduction.
*   **Memcached:** 0.5% reduction.
*   **Redis:** essentially neutral.

**Compilation Overhead:**
This specific **intra-procedural** implementation is lightweight and is expected to have a **negligible impact** on compilation time.

## Usage
The optimization is currently enabled by default, but can be toggled via:
**Flag:** `-mllvm -tsan-use-escape-analysis`

## Attribution
**Implementation:**
This patch was implemented by **Alexey Paznikov**.

**Research & Algorithm Design:**
The underlying algorithms and performance validation were conducted by the research team: **Alexey Paznikov**, **Andrey Kogutenko**, **Yaroslav Osipov**, **Michael Schwarz**, and **Umang Mathur**.

This work is part of our broader effort on reducing TSan overhead [1].

[1] Preprint: https://arxiv.org/abs/2512.05555

---

Patch is 96.89 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/192945.diff


11 Files Affected:

- (added) llvm/include/llvm/Analysis/EscapeAnalysis.h (+199) 
- (modified) llvm/lib/Analysis/CMakeLists.txt (+1) 
- (added) llvm/lib/Analysis/EscapeAnalysis.cpp (+757) 
- (modified) llvm/lib/Passes/PassBuilder.cpp (+1) 
- (modified) llvm/lib/Passes/PassRegistry.def (+2) 
- (modified) llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp (+92-20) 
- (added) llvm/test/Analysis/EscapeAnalysis/escape-analysis.ll (+1062) 
- (added) llvm/test/Analysis/EscapeAnalysis/invalidation.ll (+22) 
- (added) llvm/test/Analysis/EscapeAnalysis/worklist-limit.ll (+25) 
- (modified) llvm/test/Instrumentation/ThreadSanitizer/capture-no-omit.ll (+1-1) 
- (added) llvm/test/Instrumentation/ThreadSanitizer/escape-analysis-tsan.ll (+250) 


``````````diff
diff --git a/llvm/include/llvm/Analysis/EscapeAnalysis.h b/llvm/include/llvm/Analysis/EscapeAnalysis.h
new file mode 100644
index 0000000000000..dd4effbb209b8
--- /dev/null
+++ b/llvm/include/llvm/Analysis/EscapeAnalysis.h
@@ -0,0 +1,199 @@
+//===- EscapeAnalysis.h - Intraprocedural Escape Analysis -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the interface for a simple, conservative intraprocedural
+// escape analysis. It is designed as a helper utility for other passes, like
+// ThreadSanitizer, to determine if an allocation escapes the context of its
+// containing function.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ANALYSIS_ESCAPEANALYSIS_H
+#define LLVM_ANALYSIS_ESCAPEANALYSIS_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/MemorySSA.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+/// Find underlying base objects for a pointer possibly produced by loads.
+///
+/// This routine walks backwards through MemorySSA clobbering definitions of
+/// simple loads to find stores that defined the loaded pointer values, and
+/// collects their base objects. Additionally, it attempts ValueTracking
+/// `getUnderlyingObjects` to peel pointer casts/GEPs/phis where profitable.
+///
+/// Collected "base" objects are:
+///  - `AllocaInst` (stack, base=stack)
+///  - `Argument` (function argument, base=arg)
+///  - `GlobalVariable` and `GlobalAlias` (base=global|alias)
+///  - `ConstantPointerNull` (base=null)
+///  - Results of known heap-allocating calls (e.g. `malloc`, `calloc`,
+///    `realloc`, `aligned_alloc`, `strdup`, or C\+\+ `new`) when recognized
+///    via `TargetLibraryInfo` (base=heap).
+///
+/// If the walk encounters an unrecognized defining write, a non-simple store,
+/// a memintrinsic as a defining write, or the step budget is exceeded, the
+/// analysis conservatively treats the current value as a terminal non-base
+/// and marks the result as incomplete.
+///
+/// Contract and guarantees:
+///  - If `MSSA` is null, the analysis immediately returns with
+///    `*IsComplete == false` (if provided).
+///  - If `TLI` is null, heap allocations cannot be recognized; terminals that
+///    are calls are treated as non-bases and lead to `*IsComplete == false`.
+///  - `Result` is a set of terminal values observed (may include non-bases if
+///    the analysis is incomplete). Use `*IsComplete` to know if all are bases.
+///  - `MaxSteps` is a per-query safety valve limiting the combined number of
+///    processed worklist nodes. When exceeded, the analysis bails out and
+///    sets `*IsComplete == false`.
+LLVM_ABI void getUnderlyingObjectsThroughLoads(
+    const Value *Ptr, MemorySSA *MSSA, SmallPtrSetImpl<const Value *> &Result,
+    const TargetLibraryInfo *TLI = nullptr, LoopInfo *LI = nullptr,
+    bool *IsComplete = nullptr, unsigned MaxSteps = 10000);
+
+/// Detect heap allocations. Complements isAllocationFn() by checking
+/// library functions directly when attributes might be missing.
+LLVM_ABI bool isHeapAllocation(const CallBase *CB,
+                               const TargetLibraryInfo &TLI);
+
+/// EscapeAnalysisInfo - This class implements the actual backward dataflow
+/// analysis for a function; queries are per allocation site.
+///
+/// This is a lightweight, intraprocedural and conservative analysis intended
+/// to help instrumentation passes (e.g. ThreadSanitizer) skip objects that do
+/// not escape the function scope. The main query is \c isEscaping(Value&),
+/// which answers whether an allocation site (alloca/malloc-like) may escape
+/// the current function. Results are memoized per underlying object.
+struct EscapeAnalysisInfo {
+  /// Constructs an escape analysis utility for a given function.
+  /// Requires a FunctionAnalysisManager to obtain other analyses like AA.
+  EscapeAnalysisInfo(Function &F, FunctionAnalysisManager &FAM) : F(F) {
+    TLI = &FAM.getResult<TargetLibraryAnalysis>(F);
+    MSSA = &FAM.getResult<MemorySSAAnalysis>(F).getMSSA();
+    LI = &FAM.getResult<LoopAnalysis>(F);
+  }
+  ~EscapeAnalysisInfo() = default;
+
+  /// Return true if \p Alloc may escape the function.
+  /// \param Alloc - Must be an allocation site (AllocaInst or heap allocation
+  ///                call). Passing GEPs/bitcasts is not supported; use the base
+  ///                allocation.
+  /// \returns true if the allocation escapes or if \p Alloc is not an
+  /// allocation site.
+  LLVM_ABI bool isEscaping(const Value &Alloc);
+
+  /// Print escape information for all allocations in the function
+  LLVM_ABI void print(raw_ostream &OS);
+
+  LLVM_ABI bool invalidate(Function &Fn, const PreservedAnalyses &PA,
+                           FunctionAnalysisManager::Invalidator &Inv);
+
+private:
+  Function &F;
+  DenseMap<const Value *, bool> Cache;
+
+  TargetLibraryInfo *TLI = nullptr;
+  MemorySSA *MSSA = nullptr;
+  LoopInfo *LI = nullptr;
+
+  /// Checks whether a base location is externally visible (thus escapes).
+  static bool isExternalObject(const Value *Base);
+
+  /// Custom CaptureTracker for escape analysis
+  class EscapeCaptureTracker : public CaptureTracker {
+  public:
+    EscapeCaptureTracker(EscapeAnalysisInfo &EAI,
+                         const SmallPtrSet<const Value *, 32> &ProcessingSet,
+                         bool &SawCycle)
+        : EAI(EAI), ProcessingSet(ProcessingSet), SawCycle(SawCycle) {}
+
+    void tooManyUses() override { Escaped = true; }
+    bool shouldExplore(const Use *U) override;
+    Action captured(const Use *U, UseCaptureInfo CI) override;
+    bool hasEscaped() const { return Escaped; }
+
+  private:
+    EscapeAnalysisInfo &EAI;
+    SmallPtrSet<const Value *, 32> ProcessingSet;
+    bool &SawCycle;
+    bool Escaped = false;
+
+    /// Analyze if storing to destination causes escape
+    bool doesStoreDestEscape(const Value *Dest);
+
+    /// Get indices of pointer-typed arguments that are marked 'nocapture'
+    SmallVector<unsigned, 8>
+    getNoCapturePointerArgIndices(const CallBase *CB) const;
+
+    /// Check if any of the 'nocapture' arguments can reach the query object
+    bool canEscapeViaNocaptureArgs(
+        const CallBase &CB, ArrayRef<unsigned> NoCapPtrArgs,
+        SmallPtrSetImpl<const Value *> &StorePtrOpndBases) const;
+
+    /// Check if the given clobber stems from StartMDef
+    bool stemsFromStartStore(MemoryUseOrDef *MUOD, const MemoryDef *StartMDef,
+                             MemoryLocation Loc, bool &IsComplete,
+                             MemorySSAWalker *Walker) const;
+
+    /// Walk MemorySSA forward from StartStore and:
+    ///  - collect pointer-typed Loads that may read bytes written by StartStore
+    ///  - detect calls that may export those bytes via nocapture pointer args
+    /// Sets ContentMayEscape if any call may export the bytes.
+    SmallVector<const LoadInst *, 32>
+    findStoreReadersAndExports(const StoreInst *StartStore,
+                               bool &ContentMayEscape, bool &IsComplete);
+
+    /// Analyze whether the pointer value stored by `Store` can escape
+    bool doesStoredPointerEscapeViaLoads(const StoreInst *Store);
+  };
+
+  /// Solve escape for a single allocation site using backward dataflow.
+  ///
+  /// If \p SawCycle is provided, it is set when the query encounters a
+  /// backedge into the current \p ProcessingSet. This does not by itself mean
+  /// the object escapes; it only marks a negative result as provisional so it
+  /// is not memoized before the whole local SCC is resolved.
+  bool solveEscapeFor(const Value &Ptr,
+                      SmallPtrSet<const Value *, 32> &ProcessingSet,
+                      bool *SawCycle = nullptr);
+
+  /// Helper function to detect allocation sites (malloc/new-like)
+  /// Returns true if V is an Alloca or a call to a known heap alloc function.
+  bool isAllocationSite(const Value *V);
+};
+
+/// EscapeAnalysisInfo wrapper for the new pass manager.
+class EscapeAnalysis : public AnalysisInfoMixin<EscapeAnalysis> {
+  friend AnalysisInfoMixin<EscapeAnalysis>;
+  static AnalysisKey Key;
+
+public:
+  using Result = EscapeAnalysisInfo;
+  static Result run(Function &F, FunctionAnalysisManager &FAM);
+};
+
+/// Printer pass for the \c EscapeAnalysis results.
+class EscapeAnalysisPrinterPass
+    : public PassInfoMixin<EscapeAnalysisPrinterPass> {
+  raw_ostream &OS;
+
+public:
+  explicit EscapeAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {}
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) const;
+  static bool isRequired() { return true; }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_ANALYSIS_ESCAPEANALYSIS_H
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index f3586c66cb056..219dfe2998ca3 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -78,6 +78,7 @@ add_llvm_component_library(LLVMAnalysis
   DXILResource.cpp
   DXILMetadataAnalysis.cpp
   EphemeralValuesCache.cpp
+  EscapeAnalysis.cpp
   FloatingPointPredicateUtils.cpp
   FunctionPropertiesAnalysis.cpp
   GlobalsModRef.cpp
diff --git a/llvm/lib/Analysis/EscapeAnalysis.cpp b/llvm/lib/Analysis/EscapeAnalysis.cpp
new file mode 100644
index 0000000000000..ba51b1c1a0341
--- /dev/null
+++ b/llvm/lib/Analysis/EscapeAnalysis.cpp
@@ -0,0 +1,757 @@
+//===- EscapeAnalysis.cpp - Intraprocedural Escape Analysis Implementation ===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the EscapeAnalysis helper class. It uses a worklist-
+// based, backward dataflow analysis to determine if an allocation can escape.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/EscapeAnalysis.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/MemoryBuiltins.h"
+#include "llvm/Analysis/MemorySSA.h"
+#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "escape-analysis"
+
+using namespace llvm;
+
+STATISTIC(NumAllocationsAnalyzed, "Number of allocation sites analyzed");
+STATISTIC(NumAllocationsEscaped, "Number of allocation sites found to escape");
+
+/// Per-allocation worklist cap (safety valve). If the number of processed
+/// worklist nodes exceeds this limit, the analysis bails out conservatively and
+/// considers the allocation as escaping.
+static cl::opt<unsigned> WorklistLimit(
+    "escape-analysis-worklist-limit", cl::init(1000), cl::Hidden,
+    cl::desc("Max number of worklist nodes processed per allocation; "
+             "if exceeded, assume the allocation escapes"));
+
+// getUnderlyingObjects(..., MaxLookup = 0) is assumed to mean "unbounded".
+// If upstream changes semantics, this must be revisited.
+static constexpr unsigned VTMaxLookup = 0;
+
+//===----------------------------------------------------------------------===//
+// File-local MemorySSA utilities
+//===----------------------------------------------------------------------===//
+
+namespace llvm {
+/// Add P to Worklist if it doesn't exist in Seen
+template <typename PtrT, typename SetT, typename WorklistT>
+static bool tryEnqueueIfNew(PtrT *P, SetT &Seen, WorklistT &Worklist) {
+  if (P && Seen.insert(P).second) {
+    Worklist.push_back(P);
+    return true;
+  }
+  return false;
+}
+
+/// Add incoming unvisited MemoryAccesses of a MemoryPhi to MAWorkList.
+static void appendIncomingMAs(const MemoryPhi *MPhi,
+                              SmallPtrSetImpl<MemoryAccess *> &VisitedMA,
+                              SmallVectorImpl<MemoryAccess *> &MAWorkList,
+                              MemoryLocation Loc, MemorySSAWalker *Walker,
+                              bool &IsComplete) {
+  for (unsigned Idx = 0, N = MPhi->getNumIncomingValues(); Idx != N; ++Idx) {
+    MemoryAccess *InMA = MPhi->getIncomingValue(Idx);
+    MemoryAccess *EdgeCl = Walker->getClobberingMemoryAccess(InMA, Loc);
+    if (!EdgeCl) {
+      IsComplete = false;
+      continue;
+    }
+    tryEnqueueIfNew(EdgeCl, VisitedMA, MAWorkList);
+  }
+}
+
+enum class EdgeWalkStep { Recurse, SkipSuccessors, Stop };
+
+/// Walk edge clobbering definitions starting from Start MemoryAccess.
+template <typename VisitT>
+static void walkEdgeClobbers(MemoryAccess *Start, MemorySSAWalker *Walker,
+                             MemoryLocation Loc, unsigned Limit,
+                             const VisitT &Visit, bool &IsComplete) {
+  IsComplete = true;
+  if (!Start) {
+    IsComplete = false;
+    return;
+  }
+
+  SmallVector<MemoryAccess *, 32> MAWorklist;
+  SmallPtrSet<MemoryAccess *, 32> MAVisited;
+  tryEnqueueIfNew(Start, MAVisited, MAWorklist);
+  unsigned Steps = 0;
+
+  while (!MAWorklist.empty()) {
+    if (++Steps > Limit) {
+      IsComplete = false;
+      return;
+    }
+
+    MemoryAccess *MA = MAWorklist.pop_back_val();
+
+    const EdgeWalkStep Act = Visit(MA);
+    if (Act == EdgeWalkStep::Stop)
+      return;
+    if (Act == EdgeWalkStep::SkipSuccessors)
+      continue;
+
+    if (auto *MDef = dyn_cast<MemoryDef>(MA)) {
+      MemoryAccess *EdgeCl = Walker->getClobberingMemoryAccess(MDef, Loc);
+      if (!EdgeCl) {
+        IsComplete = false;
+        return;
+      }
+      tryEnqueueIfNew(EdgeCl, MAVisited, MAWorklist);
+    } else if (const auto *MPhi = dyn_cast<MemoryPhi>(MA)) {
+      appendIncomingMAs(MPhi, MAVisited, MAWorklist, Loc, Walker, IsComplete);
+      if (!IsComplete)
+        return;
+    } else {
+      llvm_unreachable("Unexpected MemoryAccess kind");
+    }
+  }
+}
+
+/// Try to use ValueTracking to find underlying objects.
+static bool tryValueTracking(const Value *V, LoopInfo *LI,
+                             SmallVectorImpl<const Value *> &Work,
+                             SmallPtrSetImpl<const Value *> &Enqueued) {
+  SmallVector<const Value *, 4> Bases;
+  if (!V->getType()->isPointerTy())
+    return false; // Only pointers have underlying objects.
+
+  getUnderlyingObjects(V, Bases, LI, VTMaxLookup);
+
+  if (Bases.empty() || (Bases.size() == 1 && Bases[0] == V))
+    return false;
+
+  for (const Value *B : Bases)
+    tryEnqueueIfNew(B, Enqueued, Work);
+  return true;
+}
+
+bool isHeapAllocation(const CallBase *CB, const TargetLibraryInfo &TLI) {
+  // Try standard path first (works for C++ new and modern IR with allockind)
+  if (isAllocationFn(CB, &TLI) || isNewLikeFn(CB, &TLI))
+    return true;
+
+  // Fallback: check directly via TLI for malloc/calloc/etc
+  const Function *Callee = CB->getCalledFunction();
+  if (!Callee || !Callee->getReturnType()->isPointerTy())
+    return false;
+
+  LibFunc Func;
+  if (!TLI.getLibFunc(*Callee, Func) || !TLI.has(Func))
+    return false;
+
+  // List of known heap allocation functions from libc
+  switch (Func) {
+  case LibFunc_malloc:
+  case LibFunc_calloc:
+  case LibFunc_realloc:
+  case LibFunc_reallocf:
+  case LibFunc_reallocarray:
+  case LibFunc_valloc:
+  case LibFunc_pvalloc:
+  case LibFunc_aligned_alloc:
+  case LibFunc_memalign:
+  case LibFunc_vec_malloc:
+  case LibFunc_vec_calloc:
+  case LibFunc_vec_realloc:
+  case LibFunc_strdup:
+  case LibFunc_strndup:
+    return true;
+  default:
+    return false;
+  }
+}
+
+void getUnderlyingObjectsThroughLoads(const Value *Ptr, MemorySSA *MSSA,
+                                      SmallPtrSetImpl<const Value *> &Result,
+                                      const TargetLibraryInfo *TLI,
+                                      LoopInfo *LI, bool *IsComplete,
+                                      unsigned MaxSteps) {
+  LLVM_DEBUG(dbgs() << "getUnderlyingObjectsThroughLoads: " << Ptr->getName()
+                    << "\n");
+
+  if (!Ptr->getType()->isPointerTy()) {
+    LLVM_DEBUG(dbgs() << "Input is not a pointer: " << *Ptr << "\n");
+    return; // Only pointers have underlying objects.
+  }
+
+  if (!MSSA) {
+    LLVM_DEBUG(dbgs() << "MSSA is null, marking analysis as incomplete\n");
+    if (IsComplete)
+      *IsComplete = false;
+    return;
+  }
+
+  auto addTerminal = [&](const Value *Term) {
+    if (!Term || !Term->getType()->isPointerTy())
+      return;
+    bool IsBase = isa<AllocaInst>(Term) || isa<Argument>(Term) ||
+                  isa<GlobalVariable>(Term) || isa<GlobalAlias>(Term) ||
+                  isa<ConstantPointerNull>(Term);
+    if (!IsBase && TLI) { // Check if it's heap allocation call
+      if (const auto *CB = dyn_cast<CallBase>(Term))
+        IsBase = isHeapAllocation(CB, *TLI);
+    }
+    LLVM_DEBUG(dbgs() << "Mark terminal: " << *Term
+                      << " IsBase=" << (IsBase ? "yes" : "no") << "\n");
+    Result.insert(Term);
+    if (IsComplete && !IsBase) {
+      *IsComplete = false;
+      LLVM_DEBUG(dbgs() << "Marking incomplete due to non-base\n");
+    }
+  };
+
+  SmallPtrSet<const Value *, 32> ValueTrackingSeen;
+  SmallPtrSet<const Value *, 32> Seen;
+  SmallVector<const Value *, 32> Worklist;
+
+  auto bail = [&]() {
+    if (IsComplete)
+      *IsComplete = false;
+    for (const Value *WV : Worklist)
+      addTerminal(WV);
+  };
+
+  tryEnqueueIfNew(Ptr, Seen, Worklist);
+
+  unsigned Step = 0;
+  if (IsComplete)
+    *IsComplete = true;
+
+  MemorySSAWalker *Walker = MSSA->getSkipSelfWalker();
+
+  while (!Worklist.empty()) {
+    const Value *CurrPtr = Worklist.pop_back_val();
+
+    // Safety valve: if we exceed MaxSteps, bail out conservatively.
+    if (++Step > MaxSteps) {
+      LLVM_DEBUG(dbgs() << "MaxSteps exceeded at: " << *CurrPtr << "\n");
+      addTerminal(CurrPtr);
+      bail();
+      return;
+    }
+
+    // Try ValueTracking first (only once per value)
+    if (!isa<LoadInst>(CurrPtr) && ValueTrackingSeen.insert(CurrPtr).second &&
+        tryValueTracking(CurrPtr, LI, Worklist, Seen))
+      continue; // Successfully expanded via ValueTracking;
+
+    const auto *Load = dyn_cast<LoadInst>(CurrPtr);
+    if (!Load || !Load->isSimple()) {
+      addTerminal(CurrPtr);
+      continue;
+    }
+
+    // Use MemorySSA's API to get the clobbering MemoryAccess.
+    MemoryAccess *Clobber = Walker->getClobberingMemoryAccess(Load);
+    const auto LoadLoc = MemoryLocation::get(Load);
+
+    // Local accumulators for Load
+    SmallVector<const Value *, 8> LocalWorklist;
+    SmallPtrSet<const Value *, 8> LocalSeen;
+
+    LocalSeen.insert(Load);
+    bool Fallback = false;
+    bool MAWalkComplete = false;
+    // Limit MemorySSA walk to half of the budget
+    const unsigned MAIterationLimit = std::max(1u, MaxSteps / 2);
+
+    walkEdgeClobbers(
+        Clobber, Walker, LoadLoc, MAIterationLimit,
+        [&](MemoryAccess *MA) -> EdgeWalkStep {
+          if (MSSA->isLiveOnEntryDef(MA)) {
+            LLVM_DEBUG(dbgs() << "LiveOnEntryDef reached, fallback\n");
+            Fallback = true;
+            return EdgeWalkStep::Stop;
+          }
+
+          if (const auto *MDef = dyn_cast<MemoryDef>(MA)) {
+            const Instruction *I = MDef->getMemoryInst();
+            assert(I && "MemoryDef must have an instruction");
+
+            if (const auto *Store = dyn_cast<StoreInst>(I)) {
+              if (!Store->isSimple()) {
+                Fallback = true;
+                return EdgeWalkStep::Stop;
+              }
+              const Value *SV = Store->getValueOperand();
+              if (SV->getType()->isPointerTy()) {
+                tryEnqueueIfNew(SV, LocalSeen, LocalWorklist);
+                // Reached defining store for LoadLoc — stop this path here.
+                return EdgeWalkStep::SkipSuccessors;
+              }
+              LLVM_DEBUG(dbgs() << "Non-pointer store: " << *Store << "\n");
+              Fal...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/192945


More information about the cfe-commits mailing list