[llvm-branch-commits] [llvm] [GVN] Replace SCEV address recovery with GEP peeling (PR #215234)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Aug 10 03:16:18 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Madhur Amilkanthwar (madhur13490)
<details>
<summary>Changes</summary>
Avoid introducing ScalarEvolution into GVN by teaching PHITransAddr to
recover affine-equivalent select-arm addresses from nested GEPs.
---
Full diff: https://github.com/llvm/llvm-project/pull/215234.diff
7 Files Affected:
- (modified) llvm/include/llvm/Analysis/MemoryDependenceAnalysis.h (-12)
- (modified) llvm/include/llvm/Analysis/PHITransAddr.h (+6)
- (modified) llvm/include/llvm/Transforms/Scalar/GVN.h (-3)
- (modified) llvm/lib/Analysis/MemoryDependenceAnalysis.cpp (-81)
- (modified) llvm/lib/Analysis/PHITransAddr.cpp (+89-17)
- (modified) llvm/lib/Transforms/Scalar/GVN.cpp (-6)
- (renamed) llvm/test/Transforms/GVN/phitrans-gep-select-load-address.ll (+5-11)
``````````diff
diff --git a/llvm/include/llvm/Analysis/MemoryDependenceAnalysis.h b/llvm/include/llvm/Analysis/MemoryDependenceAnalysis.h
index 98e7f84855b4a..459c8aeb5ab5b 100644
--- a/llvm/include/llvm/Analysis/MemoryDependenceAnalysis.h
+++ b/llvm/include/llvm/Analysis/MemoryDependenceAnalysis.h
@@ -32,7 +32,6 @@ namespace llvm {
class AssumptionCache;
class DominatorTree;
class PHITransAddr;
-class ScalarEvolution;
/// A memory dependence query can return one of three different answers.
class MemDepResult {
@@ -383,12 +382,6 @@ class MemoryDependenceResults {
PredIteratorCache PredCache;
EarliestEscapeAnalysis EEA;
- /// Optional, opt-in ScalarEvolution used only to recover select-dependent
- /// load addresses that are affine-equal (but not syntactically identical) to
- /// an existing pointer. Null unless a client (currently GVN) sets it, so all
- /// other MemDep users are unaffected.
- ScalarEvolution *SE = nullptr;
-
unsigned DefaultBlockScanLimit;
/// Offsets to dependant clobber loads.
@@ -402,11 +395,6 @@ class MemoryDependenceResults {
: AA(AA), AC(AC), TLI(TLI), DT(DT), EEA(DT),
DefaultBlockScanLimit(DefaultBlockScanLimit) {}
- /// Opt in to SCEV-based recovery of affine-equal select-dependent
- /// addresses. Passing null (the default) preserves the syntactic-only
- /// behavior.
- void setScalarEvolution(ScalarEvolution *S) { SE = S; }
-
/// Handle invalidation in the new PM.
LLVM_ABI bool invalidate(Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &Inv);
diff --git a/llvm/include/llvm/Analysis/PHITransAddr.h b/llvm/include/llvm/Analysis/PHITransAddr.h
index 90df01269057f..2a45539a70156 100644
--- a/llvm/include/llvm/Analysis/PHITransAddr.h
+++ b/llvm/include/llvm/Analysis/PHITransAddr.h
@@ -146,6 +146,12 @@ class PHITransAddr {
LLVM_ABI bool verify() const;
private:
+ /// Recover an available address when select-arm translation only fails
+ /// because a constant index delta is folded into a nested i8 GEP offset.
+ Value *findAvailableSelectArmAddr(Value *Cond, BasicBlock *CurBB,
+ BasicBlock *PredBB, const DominatorTree *DT,
+ bool CondVal) const;
+
Value *translateSubExpr(Value *V, BasicBlock *CurBB, BasicBlock *PredBB,
const DominatorTree *DT, Value *Cond = nullptr,
bool CondVal = false);
diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h
index 554d66deecf1c..0275c01b28020 100644
--- a/llvm/include/llvm/Transforms/Scalar/GVN.h
+++ b/llvm/include/llvm/Transforms/Scalar/GVN.h
@@ -49,7 +49,6 @@ class GetElementPtrInst;
class ImplicitControlFlowTracking;
class LoadInst;
class LoopInfo;
-class ScalarEvolution;
class MemDepResult;
class MemoryAccess;
class MemoryDependenceResults;
@@ -261,8 +260,6 @@ class GVNPass : public OptionalPassInfoMixin<GVNPass> {
LoopInfo *LI = nullptr;
AAResults *AA = nullptr;
MemorySSAUpdater *MSSAU = nullptr;
- // Prototype: SCEV handed to MemDep for affine select-address recovery.
- ScalarEvolution *SE = nullptr;
ValueTable VN;
diff --git a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
index 7cf1c7f35879b..6fda89af4867c 100644
--- a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
@@ -25,8 +25,6 @@
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/MemoryLocation.h"
#include "llvm/Analysis/PHITransAddr.h"
-#include "llvm/Analysis/ScalarEvolution.h"
-#include "llvm/Analysis/ScalarEvolutionExpressions.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
@@ -1058,73 +1056,6 @@ MemoryDependenceResults::lookupNonLocalPointerDepVisited(BasicBlock *BB) const {
return NonLocalPointerDepVisited[BB->getNumber()].first;
}
-// When the syntactic PHI translation of a select-dependent load address fails
-// for one arm, try to recover an equivalent, already-existing address with
-// SCEV. The arm's address is the original address \p A with the recurrence
-// phi (whose \p PredBB incoming is a select on \p Cond) replaced by the value
-// chosen by \p CondVal. If SCEV proves that address equals the SCEV of an
-// existing pointer that is loaded from and dominates \p PredBB's terminator,
-// that pointer is returned so the load-PRE-through-select path can reuse it.
-// This closes the affine-address gap (different index with compensating
-// offsets) that the syntactic operand match cannot see. Returns null on
-// failure.
-static Value *recoverSelectArmAddr(Value *A, Value *Cond, BasicBlock *PredBB,
- bool CondVal, ScalarEvolution &SE,
- const DominatorTree &DT) {
- if (!A || !SE.isSCEVable(A->getType()))
- return nullptr;
-
- const SCEV *S = SE.getSCEV(A);
-
- // Locate the recurrence phi in the address whose PredBB incoming is a select
- // on Cond, and the arm chosen by CondVal.
- PHINode *RecPhi = nullptr;
- Value *Arm = nullptr;
- SCEVExprContains(S, [&](const SCEV *Sub) {
- if (RecPhi)
- return true;
- auto *U = dyn_cast<SCEVUnknown>(Sub);
- if (!U)
- return false;
- auto *PN = dyn_cast<PHINode>(U->getValue());
- if (!PN || PN->getBasicBlockIndex(PredBB) < 0)
- return false;
- auto *SI = dyn_cast<SelectInst>(PN->getIncomingValueForBlock(PredBB));
- if (!SI || SI->getCondition() != Cond)
- return false;
- RecPhi = PN;
- Arm = CondVal ? SI->getTrueValue() : SI->getFalseValue();
- return true;
- });
- if (!RecPhi || !Arm || !SE.isSCEVable(Arm->getType()))
- return nullptr;
-
- // The "keep" arm reproduces the original address exactly.
- if (Arm == RecPhi)
- return A;
-
- ValueToSCEVMapTy Map;
- Map[RecPhi] = SE.getSCEV(Arm);
- const SCEV *Target = SCEVParameterRewriter::rewrite(S, SE, Map);
- if (Target == S)
- return nullptr;
-
- // Return an existing loaded-from pointer with a matching address SCEV.
- for (BasicBlock *BB = PredBB; BB; BB = BB->getSinglePredecessor())
- for (Instruction &I : *BB) {
- auto *LD = dyn_cast<LoadInst>(&I);
- if (!LD)
- continue;
- Value *Ptr = LD->getPointerOperand();
- if (!SE.isSCEVable(Ptr->getType()) ||
- !DT.dominates(LD, PredBB->getTerminator()))
- continue;
- if (SE.getSCEV(Ptr) == Target)
- return Ptr;
- }
- return nullptr;
-}
-
/// Perform a dependency query based on pointer/pointeesize starting at the end
/// of StartBB.
///
@@ -1447,18 +1378,6 @@ bool MemoryDependenceResults::getNonLocalPointerDepFromBB(
if (Value *Cond = PredPointer.getSelectCondition()) {
SelectAddr::SelectAddrs SelAddrs =
PHITransAddr(Pointer).translateValue(BB, Pred, &DT, Cond);
- // If a side failed the syntactic match, try to recover an existing
- // affine-equal address with SCEV (opt-in via setScalarEvolution).
- if (SE && (!SelAddrs.first || !SelAddrs.second)) {
- Value *A = Pointer.getAddr();
- if (!SelAddrs.first)
- SelAddrs.first = recoverSelectArmAddr(A, Cond, Pred,
- /*CondVal=*/true, *SE, DT);
- if (!SelAddrs.second)
- SelAddrs.second =
- recoverSelectArmAddr(A, Cond, Pred,
- /*CondVal=*/false, *SE, DT);
- }
if (SelAddrs.first && SelAddrs.second) {
Result.push_back(NonLocalDepResult(Pred, MemDepResult::getSelect(),
SelectAddr(Cond, SelAddrs)));
diff --git a/llvm/lib/Analysis/PHITransAddr.cpp b/llvm/lib/Analysis/PHITransAddr.cpp
index 03b931a2f0587..203a2d4050d00 100644
--- a/llvm/lib/Analysis/PHITransAddr.cpp
+++ b/llvm/lib/Analysis/PHITransAddr.cpp
@@ -15,12 +15,15 @@
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
+#include "llvm/IR/PatternMatch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
+using namespace llvm::PatternMatch;
static cl::opt<bool> EnableAddPhiTranslation(
"gvn-add-phi-translation", cl::init(false), cl::Hidden,
@@ -37,6 +40,31 @@ static bool canPHITrans(Instruction *Inst) {
return false;
}
+/// Return an existing GEP of \p SrcTy over \p Ops that dominates \p PredBB.
+static GetElementPtrInst *findAvailableGEP(Type *ResultTy, Type *SrcTy,
+ ArrayRef<Value *> Ops,
+ BasicBlock *CurBB,
+ BasicBlock *PredBB,
+ const DominatorTree *DT) {
+ assert(!Ops.empty() && "GEP needs a pointer operand");
+ Value *Ptr = Ops[0];
+ if (isa<ConstantData>(Ptr))
+ return nullptr;
+
+ for (User *U : Ptr->users()) {
+ auto *GEPI = dyn_cast<GetElementPtrInst>(U);
+ if (!GEPI || GEPI->getType() != ResultTy ||
+ GEPI->getSourceElementType() != SrcTy ||
+ GEPI->getNumOperands() != Ops.size() ||
+ GEPI->getParent()->getParent() != CurBB->getParent() ||
+ (DT && !DT->dominates(GEPI->getParent(), PredBB)))
+ continue;
+ if (std::equal(Ops.begin(), Ops.end(), GEPI->op_begin()))
+ return GEPI;
+ }
+ return nullptr;
+}
+
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void PHITransAddr::dump() const {
if (!Addr) {
@@ -236,22 +264,8 @@ Value *PHITransAddr::translateSubExpr(Value *V, BasicBlock *CurBB,
}
// Scan to see if we have this GEP available.
- Value *APHIOp = GEPOps[0];
- if (isa<ConstantData>(APHIOp))
- return nullptr;
-
- for (User *U : APHIOp->users()) {
- if (GetElementPtrInst *GEPI = dyn_cast<GetElementPtrInst>(U))
- if (GEPI->getType() == GEP->getType() &&
- GEPI->getSourceElementType() == GEP->getSourceElementType() &&
- GEPI->getNumOperands() == GEPOps.size() &&
- GEPI->getParent()->getParent() == CurBB->getParent() &&
- (!DT || DT->dominates(GEPI->getParent(), PredBB))) {
- if (std::equal(GEPOps.begin(), GEPOps.end(), GEPI->op_begin()))
- return GEPI;
- }
- }
- return nullptr;
+ return findAvailableGEP(GEP->getType(), GEP->getSourceElementType(), GEPOps,
+ CurBB, PredBB, DT);
}
// Handle add with a constant RHS.
@@ -347,12 +361,70 @@ SelectAddr::SelectAddrs PHITransAddr::translateValue(BasicBlock *CurBB,
// Work on a copy so that the original address state is preserved and the
// other side can be translated independently.
PHITransAddr Tmp(*this);
- return Tmp.translateSubExpr(Tmp.Addr, CurBB, PredBB, DT, Cond, CondVal);
+ if (Value *V =
+ Tmp.translateSubExpr(Tmp.Addr, CurBB, PredBB, DT, Cond, CondVal))
+ return V;
+ // Syntactic match misses when a constant index delta is folded into an i8
+ // offset. Recover the affine-equal available pointer instead.
+ return findAvailableSelectArmAddr(Cond, CurBB, PredBB, DT, CondVal);
};
return {TranslateSide(/*CondVal=*/true), TranslateSide(/*CondVal=*/false)};
}
+Value *PHITransAddr::findAvailableSelectArmAddr(Value *Cond, BasicBlock *CurBB,
+ BasicBlock *PredBB,
+ const DominatorTree *DT,
+ bool CondVal) const {
+ // Addr must be: gep i8, (gep Ty, Base, Index), Off.
+ auto *Outer = dyn_cast<GetElementPtrInst>(Addr);
+ if (!Outer || Outer->getNumIndices() != 1 ||
+ !Outer->getSourceElementType()->isIntegerTy(8))
+ return nullptr;
+ auto *OffCI = dyn_cast<ConstantInt>(Outer->getOperand(1));
+ auto *Inner = dyn_cast<GetElementPtrInst>(Outer->getPointerOperand());
+ if (!OffCI || !Inner || Inner->getNumIndices() != 1)
+ return nullptr;
+
+ auto *RecPhi = dyn_cast<PHINode>(Inner->getOperand(1));
+ if (!RecPhi || RecPhi->getParent() != CurBB)
+ return nullptr;
+ auto *SI = dyn_cast<SelectInst>(RecPhi->getIncomingValueForBlock(PredBB));
+ if (!SI || SI->getCondition() != Cond)
+ return nullptr;
+
+ Value *Arm = CondVal ? SI->getTrueValue() : SI->getFalseValue();
+ if (Arm == RecPhi)
+ return Addr;
+
+ // Fold add(IV, C) into the outer byte offset and look for that GEP.
+ Value *IV = nullptr;
+ const APInt *C = nullptr;
+ if (!match(Arm, m_c_Add(m_Value(IV), m_APInt(C))))
+ return nullptr;
+
+ Type *Ty = Inner->getSourceElementType();
+ TypeSize ElemSize = DL.getTypeAllocSize(Ty);
+ if (ElemSize.isScalable())
+ return nullptr;
+
+ unsigned BitWidth = OffCI->getBitWidth();
+ APInt TargetOff =
+ OffCI->getValue() +
+ C->sextOrTrunc(BitWidth) * APInt(BitWidth, ElemSize.getFixedValue());
+
+ Value *InnerOps[] = {Inner->getPointerOperand(), IV};
+ auto *InnerAvail =
+ findAvailableGEP(Inner->getType(), Ty, InnerOps, CurBB, PredBB, DT);
+ if (!InnerAvail)
+ return nullptr;
+
+ Value *OuterOps[] = {InnerAvail,
+ ConstantInt::get(Outer->getContext(), TargetOff)};
+ return findAvailableGEP(Outer->getType(), Outer->getSourceElementType(),
+ OuterOps, CurBB, PredBB, DT);
+}
+
Value *PHITransAddr::getSelectCondition() const {
for (Instruction *I : InstInputs)
if (auto *SI = dyn_cast<SelectInst>(I))
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 2c5725a7817dd..517d33bfba103 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -41,7 +41,6 @@
#include "llvm/Analysis/MemorySSAUpdater.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/PHITransAddr.h"
-#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Attributes.h"
@@ -886,11 +885,6 @@ PreservedAnalyses GVNPass::run(Function &F, FunctionAnalysisManager &AM) {
MSSA = &AM.getResult<MemorySSAAnalysis>(F);
}
auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
- // Hand SCEV to MemDep so its select-address translation can recover
- // affine-equal addresses (reuses the load-PRE-through-select path).
- SE = &AM.getResult<ScalarEvolutionAnalysis>(F);
- if (MemDep)
- MemDep->setScalarEvolution(SE);
bool Changed = runImpl(F, AC, DT, TLI, AA, MemDep, LI, &ORE,
MSSA ? &MSSA->getMSSA() : nullptr);
if (!Changed)
diff --git a/llvm/test/Transforms/GVN/scev-select-load-address.ll b/llvm/test/Transforms/GVN/phitrans-gep-select-load-address.ll
similarity index 79%
rename from llvm/test/Transforms/GVN/scev-select-load-address.ll
rename to llvm/test/Transforms/GVN/phitrans-gep-select-load-address.ll
index 2786b6759fc10..854a232cbcbf5 100644
--- a/llvm/test/Transforms/GVN/scev-select-load-address.ll
+++ b/llvm/test/Transforms/GVN/phitrans-gep-select-load-address.ll
@@ -4,17 +4,11 @@
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-linux-gnu"
-; Down-counting floating-point argmin after IndVarSimplify has widened the index
-; recurrence to i64, as it reaches GVN in the real pipeline. Each iteration
-; reloads a[minidx] to compare against the scanned element. minidx is the latch
-; select of the previous iteration, so the reload address is select-dependent.
-; The address is a typed [4 x i8] GEP composed with a byte-offset i8 GEP, so the
-; select arm is affine-equal to the scanned-element pointer but not syntactically
-; identical: upstream's syntactic PHITransAddr match fails on that arm. MemDep
-; rewrites the recurrence phi with the selected arm's SCEV, proves the reloaded
-; address equals a previously loaded pointer, and forwards the value. The
-; a[minidx] reload should be eliminated and replaced by a running-minimum phi
-; fed by the value select.
+; Down-counting FP argmin after IndVarSimplify widening. The reload of
+; a[minidx] uses gep([4 x i8], ...) + gep(i8, Off), so the select update arm
+; is affine-equal to the scanned pointer but not syntactically identical.
+; PHITransAddr folds the constant index delta into the byte offset and GVN
+; eliminates the reload.
define i32 @fp_argmin_decreasing(ptr %a, i32 %start, i64 %tc0) {
; CHECK-LABEL: @fp_argmin_decreasing(
; CHECK-NEXT: entry:
``````````
</details>
https://github.com/llvm/llvm-project/pull/215234
More information about the llvm-branch-commits
mailing list