[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