From 17fdaccccfad9b143e4aadbcdda7f645de127153 Mon Sep 17 00:00:00 2001 From: Arthur Eubanks <aeubanks@google.com> Date: Tue, 5 Apr 2022 14:51:53 -0700 Subject: [PATCH] [CaptureTracking] Ignore ephemeral values when determining pointer escapeness Ephemeral values cannot cause a pointer to escape. No change in compile time: https://llvm-compile-time-tracker.com/compare.php?from=4371710085ba1c376a094948b806ddd3b88319de&to=c5ddbcc4866f38026737762ee8d7b9b00395d4f4&stat=instructions This partially fixes some regressions caused by more calls to `__builtin_assume` (D122397). Reviewed By: asbirlea Differential Revision: https://reviews.llvm.org/D123162 --- llvm/include/llvm/Analysis/CaptureTracking.h | 7 +++++ llvm/lib/Analysis/CaptureTracking.cpp | 28 +++++++++++++++---- .../Scalar/DeadStoreElimination.cpp | 27 +++++++++++++----- .../Transforms/DeadStoreElimination/assume.ll | 1 - 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/llvm/include/llvm/Analysis/CaptureTracking.h b/llvm/include/llvm/Analysis/CaptureTracking.h index 42d58f374606..17059da7c993 100644 --- a/llvm/include/llvm/Analysis/CaptureTracking.h +++ b/llvm/include/llvm/Analysis/CaptureTracking.h @@ -25,6 +25,7 @@ namespace llvm { class DominatorTree; class LoopInfo; class Function; + template <typename T> class SmallPtrSetImpl; /// getDefaultMaxUsesToExploreForCaptureTracking - Return default value of /// the maximal number of uses to explore before giving up. It is used by @@ -41,8 +42,14 @@ namespace llvm { /// MaxUsesToExplore specifies how many uses the analysis should explore for /// one value before giving up due too "too many uses". If MaxUsesToExplore /// is zero, a default value is assumed. + bool PointerMayBeCaptured(const Value *V, bool ReturnCaptures, + bool StoreCaptures, unsigned MaxUsesToExplore = 0); + + /// Variant of the above function which accepts a set of Values that are + /// ephemeral and cannot cause pointers to escape. bool PointerMayBeCaptured(const Value *V, bool ReturnCaptures, bool StoreCaptures, + const SmallPtrSetImpl<const Value *> &EphValues, unsigned MaxUsesToExplore = 0); /// PointerMayBeCapturedBefore - Return true if this pointer value may be diff --git a/llvm/lib/Analysis/CaptureTracking.cpp b/llvm/lib/Analysis/CaptureTracking.cpp index 9d5bfef10191..774567b0eb74 100644 --- a/llvm/lib/Analysis/CaptureTracking.cpp +++ b/llvm/lib/Analysis/CaptureTracking.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/CaptureTracking.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" @@ -74,8 +75,10 @@ bool CaptureTracker::isDereferenceableOrNull(Value *O, const DataLayout &DL) { namespace { struct SimpleCaptureTracker : public CaptureTracker { - explicit SimpleCaptureTracker(bool ReturnCaptures) - : ReturnCaptures(ReturnCaptures) {} + explicit SimpleCaptureTracker( + + const SmallPtrSetImpl<const Value *> &EphValues, bool ReturnCaptures) + : EphValues(EphValues), ReturnCaptures(ReturnCaptures) {} void tooManyUses() override { Captured = true; } @@ -83,10 +86,15 @@ namespace { if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures) return false; + if (EphValues.contains(U->getUser())) + return false; + Captured = true; return true; } + const SmallPtrSetImpl<const Value *> &EphValues; + bool ReturnCaptures; bool Captured = false; @@ -212,8 +220,18 @@ namespace { /// counts as capturing it or not. The boolean StoreCaptures specified whether /// storing the value (or part of it) into memory anywhere automatically /// counts as capturing it or not. -bool llvm::PointerMayBeCaptured(const Value *V, - bool ReturnCaptures, bool StoreCaptures, +bool llvm::PointerMayBeCaptured(const Value *V, bool ReturnCaptures, + bool StoreCaptures, unsigned MaxUsesToExplore) { + SmallPtrSet<const Value *, 1> Empty; + return PointerMayBeCaptured(V, ReturnCaptures, StoreCaptures, Empty, + MaxUsesToExplore); +} + +/// Variant of the above function which accepts a set of Values that are +/// ephemeral and cannot cause pointers to escape. +bool llvm::PointerMayBeCaptured(const Value *V, bool ReturnCaptures, + bool StoreCaptures, + const SmallPtrSetImpl<const Value *> &EphValues, unsigned MaxUsesToExplore) { assert(!isa<GlobalValue>(V) && "It doesn't make sense to ask whether a global is captured."); @@ -224,7 +242,7 @@ bool llvm::PointerMayBeCaptured(const Value *V, // take advantage of this. (void)StoreCaptures; - SimpleCaptureTracker SCT(ReturnCaptures); + SimpleCaptureTracker SCT(EphValues, ReturnCaptures); PointerMayBeCaptured(V, &SCT, MaxUsesToExplore); if (SCT.Captured) ++NumCaptured; diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp index 2c9fec88885b..d10927013748 100644 --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -38,7 +38,9 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/CaptureTracking.h" +#include "llvm/Analysis/CodeMetrics.h" #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/MemoryBuiltins.h" @@ -762,6 +764,9 @@ struct DSEState { // Post-order numbers for each basic block. Used to figure out if memory // accesses are executed before another access. DenseMap<BasicBlock *, unsigned> PostOrderNumbers; + // Values that are only used with assumes. Used to refine pointer escape + // analysis. + SmallPtrSet<const Value *, 32> EphValues; /// Keep track of instructions (partly) overlapping with killing MemoryDefs per /// basic block. @@ -776,8 +781,8 @@ struct DSEState { DSEState &operator=(const DSEState &) = delete; DSEState(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, DominatorTree &DT, - PostDominatorTree &PDT, const TargetLibraryInfo &TLI, - const LoopInfo &LI) + PostDominatorTree &PDT, AssumptionCache &AC, + const TargetLibraryInfo &TLI, const LoopInfo &LI) : F(F), AA(AA), EI(DT, LI), BatchAA(AA, &EI), MSSA(MSSA), DT(DT), PDT(PDT), TLI(TLI), DL(F.getParent()->getDataLayout()), LI(LI) { // Collect blocks with throwing instructions not modeled in MemorySSA and @@ -809,6 +814,8 @@ struct DSEState { AnyUnreachableExit = any_of(PDT.roots(), [](const BasicBlock *E) { return isa<UnreachableInst>(E->getTerminator()); }); + + CodeMetrics::collectEphemeralValues(&F, &AC, EphValues); } /// Return 'OW_Complete' if a store to the 'KillingLoc' location (by \p @@ -955,7 +962,7 @@ struct DSEState { if (!isInvisibleToCallerOnUnwind(V)) { I.first->second = false; } else if (isNoAliasCall(V)) { - I.first->second = !PointerMayBeCaptured(V, true, false); + I.first->second = !PointerMayBeCaptured(V, true, false, EphValues); } } return I.first->second; @@ -974,7 +981,7 @@ struct DSEState { // with the killing MemoryDef. But we refrain from doing so for now to // limit compile-time and this does not cause any changes to the number // of stores removed on a large test set in practice. - I.first->second = PointerMayBeCaptured(V, false, true); + I.first->second = PointerMayBeCaptured(V, false, true, EphValues); return !I.first->second; } @@ -1929,12 +1936,13 @@ struct DSEState { static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, DominatorTree &DT, PostDominatorTree &PDT, + AssumptionCache &AC, const TargetLibraryInfo &TLI, const LoopInfo &LI) { bool MadeChange = false; MSSA.ensureOptimizedUses(); - DSEState State(F, AA, MSSA, DT, PDT, TLI, LI); + DSEState State(F, AA, MSSA, DT, PDT, AC, TLI, LI); // For each store: for (unsigned I = 0; I < State.MemDefs.size(); I++) { MemoryDef *KillingDef = State.MemDefs[I]; @@ -2114,9 +2122,10 @@ PreservedAnalyses DSEPass::run(Function &F, FunctionAnalysisManager &AM) { DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F); MemorySSA &MSSA = AM.getResult<MemorySSAAnalysis>(F).getMSSA(); PostDominatorTree &PDT = AM.getResult<PostDominatorTreeAnalysis>(F); + AssumptionCache &AC = AM.getResult<AssumptionAnalysis>(F); LoopInfo &LI = AM.getResult<LoopAnalysis>(F); - bool Changed = eliminateDeadStores(F, AA, MSSA, DT, PDT, TLI, LI); + bool Changed = eliminateDeadStores(F, AA, MSSA, DT, PDT, AC, TLI, LI); #ifdef LLVM_ENABLE_STATS if (AreStatisticsEnabled()) @@ -2156,9 +2165,11 @@ public: MemorySSA &MSSA = getAnalysis<MemorySSAWrapperPass>().getMSSA(); PostDominatorTree &PDT = getAnalysis<PostDominatorTreeWrapperPass>().getPostDomTree(); + AssumptionCache &AC = + getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F); LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); - bool Changed = eliminateDeadStores(F, AA, MSSA, DT, PDT, TLI, LI); + bool Changed = eliminateDeadStores(F, AA, MSSA, DT, PDT, AC, TLI, LI); #ifdef LLVM_ENABLE_STATS if (AreStatisticsEnabled()) @@ -2182,6 +2193,7 @@ public: AU.addPreserved<MemorySSAWrapperPass>(); AU.addRequired<LoopInfoWrapperPass>(); AU.addPreserved<LoopInfoWrapperPass>(); + AU.addRequired<AssumptionCacheTracker>(); } }; @@ -2199,6 +2211,7 @@ INITIALIZE_PASS_DEPENDENCY(MemorySSAWrapperPass) INITIALIZE_PASS_DEPENDENCY(MemoryDependenceWrapperPass) INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) INITIALIZE_PASS_END(DSELegacyPass, "dse", "Dead Store Elimination", false, false) diff --git a/llvm/test/Transforms/DeadStoreElimination/assume.ll b/llvm/test/Transforms/DeadStoreElimination/assume.ll index f7559dcbff39..c131ae904bf5 100644 --- a/llvm/test/Transforms/DeadStoreElimination/assume.ll +++ b/llvm/test/Transforms/DeadStoreElimination/assume.ll @@ -8,7 +8,6 @@ define void @f() { ; CHECK-NEXT: [[TMP1:%.*]] = call noalias i8* @_Znwm(i64 32) ; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i8* [[TMP1]], @global ; CHECK-NEXT: call void @llvm.assume(i1 [[TMP2]]) -; CHECK-NEXT: store i8 0, i8* [[TMP1]], align 1 ; CHECK-NEXT: ret void ; %tmp1 = call noalias i8* @_Znwm(i64 32) -- GitLab