[llvm] [LAA] Fix inverted bounds for negative-step AddRecs with unknown end. (PR #218388)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 6 12:34:12 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/218388
>From e06f64063cd23c89aebc67be03f6981320fb4cfa Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sat, 22 Aug 2026 19:43:29 +0100
Subject: [PATCH 1/3] [LAA] Fix inverted bounds for negative-step AddRecs with
unknown end.
Currently we use an conservative upper bound (-1) for runtime checks when
evaluating the AddRec may wrap.
This does not properly account for negative steps. In that case, we need
to use null as conservative lower bound.
One complication is that the null pointer may not be zero, so we need to
check for that, and bail out completely if that's the case. We then must
clean up any pointers partially added.
---
.../llvm/Analysis/LoopAccessAnalysis.h | 5 +-
llvm/lib/Analysis/LoopAccessAnalysis.cpp | 99 +++++++++++--------
.../all-ones-null-pointer.ll | 13 +--
...bolic-max-backedge-taken-count-may-wrap.ll | 22 +----
.../negative-step-deref-off-by-eltsize.ll | 4 +-
...ter-dependence-analysis-forked-pointers.ll | 11 +--
.../LoopAccessAnalysis/symbolic-stride.ll | 2 +-
7 files changed, 75 insertions(+), 81 deletions(-)
diff --git a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h
index 392321448c895..c885adce7ac87 100644
--- a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h
+++ b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h
@@ -556,8 +556,9 @@ class RuntimePointerChecking {
/// We need \p PSE in order to compute the SCEV expression of the pointer
/// according to the assumptions that we've made during the analysis.
/// The method might also version the pointer stride according to \p Strides,
- /// and add new predicates to \p PSE.
- LLVM_ABI void insert(Loop *Lp, Value *Ptr, const SCEV *PtrExpr,
+ /// and add new predicates to \p PSE. Returns false without inserting anything
+ /// if the bounds of \p PtrExpr cannot be computed.
+ LLVM_ABI bool insert(Loop *Lp, Value *Ptr, const SCEV *PtrExpr,
Type *AccessTy, bool WritePtr, unsigned DepSetId,
unsigned ASId, PredicatedScalarEvolution &PSE,
bool NeedsFreeze);
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 9b407b1ca4729..0f7673816796e 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -330,6 +330,22 @@ static bool evaluatePtrAddRecAtMaxBTCWillNotWrap(
return SE.isKnownPredicate(CmpInst::ICMP_ULE, MaxOffset, DerefBytesSCEV);
}
+/// Return the lowest address of pointer type \p PtrTy, i.e. a null pointer.
+/// Returns nullptr if it cannot be used as a lower bound.
+static const SCEV *getLowestAddress(Type *PtrTy, ScalarEvolution &SE,
+ const DataLayout &DL) {
+ if (!DL.getNullPtrValue(PtrTy->getPointerAddressSpace()).isZero())
+ return nullptr;
+ return SE.getSCEV(Constant::getNullValue(PtrTy));
+}
+
+/// Return the highest address of pointer type \p PtrTy.
+static const SCEV *getHighestAddress(Type *PtrTy, ScalarEvolution &SE,
+ const DataLayout &DL) {
+ return SE.getSCEV(ConstantExpr::getIntToPtr(
+ Constant::getAllOnesValue(DL.getIndexType(PtrTy)), PtrTy));
+}
+
std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
const Loop *Lp, const SCEV *PtrExpr, Type *AccessTy, const SCEV *BTC,
const SCEV *MaxBTC, ScalarEvolution *SE,
@@ -363,52 +379,51 @@ std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
PtrBoundsPair = &Iter->second;
}
+ // ScStart is the lowest accessed address; ScEnd is the highest one plus the
+ // size of the accessed element.
const SCEV *ScStart;
const SCEV *ScEnd;
auto &DL = Lp->getHeader()->getDataLayout();
if (SE->isLoopInvariant(PtrExpr, Lp)) {
- ScStart = ScEnd = PtrExpr;
+ ScStart = PtrExpr;
+ ScEnd = SE->getAddExpr(PtrExpr, EltSizeSCEV);
} else if (auto *AR = dyn_cast<SCEVAddRecExpr>(PtrExpr)) {
- ScStart = AR->getStart();
- if (!isa<SCEVCouldNotCompute>(BTC))
+ const SCEV *Step = AR->getStepRecurrence(*SE);
+ // The address of the last accessed element, if it can be computed
+ // precisely.
+ const SCEV *LastAddr = nullptr;
+ if (!isa<SCEVCouldNotCompute>(BTC)) {
// Evaluating AR at an exact BTC is safe: LAA separately checks that
// accesses cannot wrap in the loop. If evaluating AR at BTC wraps, then
// the loop either triggers UB when executing a memory access with a
// poison pointer or the wrapping/poisoned pointer is not used.
- ScEnd = AR->evaluateAtIteration(BTC, *SE);
- else {
- // Evaluating AR at MaxBTC may wrap and create an expression that is less
- // than the start of the AddRec due to wrapping (for example consider
- // MaxBTC = -2). If that's the case, set ScEnd to -(EltSize + 1). ScEnd
- // will get incremented by EltSize before returning, so this effectively
- // sets ScEnd to the maximum unsigned value for the type. Note that LAA
- // separately checks that accesses cannot not wrap, so unsigned max
- // represents an upper bound.
- if (evaluatePtrAddRecAtMaxBTCWillNotWrap(AR, MaxBTC, EltSizeSCEV, *SE, DL,
- DT, AC, LoopGuards)) {
- ScEnd = AR->evaluateAtIteration(MaxBTC, *SE);
- } else {
- ScEnd = SE->getAddExpr(
- SE->getNegativeSCEV(EltSizeSCEV),
- SE->getSCEV(ConstantExpr::getIntToPtr(
- ConstantInt::getAllOnesValue(EltSizeSCEV->getType()),
- AR->getType())));
- }
+ LastAddr = AR->evaluateAtIteration(BTC, *SE);
+ } else if (evaluatePtrAddRecAtMaxBTCWillNotWrap(
+ AR, MaxBTC, EltSizeSCEV, *SE, DL, DT, AC, LoopGuards)) {
+ LastAddr = AR->evaluateAtIteration(MaxBTC, *SE);
}
- const SCEV *Step = AR->getStepRecurrence(*SE);
-
- // For expressions with negative step, the upper bound is ScStart and the
- // lower bound is ScEnd.
- if (const auto *CStep = dyn_cast<SCEVConstant>(Step)) {
- if (CStep->getValue()->isNegative())
- std::swap(ScStart, ScEnd);
+ const SCEV *Start = AR->getStart();
+ Type *PtrTy = AR->getType();
+ if (SE->isKnownNegative(Step)) {
+ ScStart = LastAddr ? LastAddr : getLowestAddress(PtrTy, *SE, DL);
+ if (!ScStart)
+ return {SE->getCouldNotCompute(), SE->getCouldNotCompute()};
+ ScEnd = SE->getAddExpr(Start, EltSizeSCEV);
+ } else if (SE->isKnownNonNegative(Step)) {
+ ScStart = Start;
+ // The highest address for the type saturates; adding EltSize to it would
+ // wrap to the start of the address space.
+ ScEnd = LastAddr ? SE->getAddExpr(LastAddr, EltSizeSCEV)
+ : getHighestAddress(PtrTy, *SE, DL);
} else {
+ if (!LastAddr)
+ return {SE->getCouldNotCompute(), SE->getCouldNotCompute()};
// Fallback case: the step is not constant, but we can still
// get the upper and lower bounds of the interval by using min/max
// expressions.
- ScStart = SE->getUMinExpr(ScStart, ScEnd);
- ScEnd = SE->getUMaxExpr(AR->getStart(), ScEnd);
+ ScStart = SE->getUMinExpr(Start, LastAddr);
+ ScEnd = SE->getAddExpr(SE->getUMaxExpr(Start, LastAddr), EltSizeSCEV);
}
} else
return {SE->getCouldNotCompute(), SE->getCouldNotCompute()};
@@ -416,9 +431,6 @@ std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
assert(SE->isLoopInvariant(ScStart, Lp) && "ScStart needs to be invariant");
assert(SE->isLoopInvariant(ScEnd, Lp) && "ScEnd needs to be invariant");
- // Add the size of the pointed element to ScEnd.
- ScEnd = SE->getAddExpr(ScEnd, EltSizeSCEV);
-
std::pair<const SCEV *, const SCEV *> Res = {ScStart, ScEnd};
if (PointerBounds)
*PtrBoundsPair = Res;
@@ -427,7 +439,7 @@ std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
/// Calculate Start and End points of memory access using
/// getStartAndEndForAccess.
-void RuntimePointerChecking::insert(Loop *Lp, Value *Ptr, const SCEV *PtrExpr,
+bool RuntimePointerChecking::insert(Loop *Lp, Value *Ptr, const SCEV *PtrExpr,
Type *AccessTy, bool WritePtr,
unsigned DepSetId, unsigned ASId,
PredicatedScalarEvolution &PSE,
@@ -437,11 +449,11 @@ void RuntimePointerChecking::insert(Loop *Lp, Value *Ptr, const SCEV *PtrExpr,
const auto &[ScStart, ScEnd] = getStartAndEndForAccess(
Lp, PtrExpr, AccessTy, BTC, SymbolicMaxBTC, PSE.getSE(),
&DC.getPointerBounds(), DC.getDT(), DC.getAC(), LoopGuards);
- assert(!isa<SCEVCouldNotCompute>(ScStart) &&
- !isa<SCEVCouldNotCompute>(ScEnd) &&
- "must be able to compute both start and end expressions");
+ if (isa<SCEVCouldNotCompute>(ScStart) || isa<SCEVCouldNotCompute>(ScEnd))
+ return false;
Pointers.emplace_back(Ptr, ScStart, ScEnd, WritePtr, DepSetId, ASId, PtrExpr,
NeedsFreeze);
+ return true;
}
bool RuntimePointerChecking::tryToCreateDiffCheck(
@@ -1343,6 +1355,10 @@ bool AccessAnalysis::createCheckForAccess(
}
PSE.addPredicates(Predicates);
+ // Remember the number of pointers inserted so far, to remove the pointers of
+ // this access again if the bounds of any of them cannot be computed, to avoid
+ // partial inserts.
+ unsigned NumPointers = RtCheck.Pointers.size();
for (const auto &[PtrExpr, NeedsFreeze] : RTCheckPtrs) {
// The id of the dependence set.
unsigned DepId;
@@ -1358,8 +1374,11 @@ bool AccessAnalysis::createCheckForAccess(
DepId = RunningDepId++;
bool IsWrite = Access.getInt();
- RtCheck.insert(TheLoop, Ptr, PtrExpr, AccessTy, IsWrite, DepId, ASId, PSE,
- NeedsFreeze);
+ if (!RtCheck.insert(TheLoop, Ptr, PtrExpr, AccessTy, IsWrite, DepId, ASId,
+ PSE, NeedsFreeze)) {
+ RtCheck.Pointers.truncate(NumPointers);
+ return false;
+ }
LLVM_DEBUG(dbgs() << "LAA: Found a runtime check ptr:" << *Ptr << '\n');
}
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll b/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll
index f58c7cf587ff6..031c4c7a6c4d3 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll
@@ -10,21 +10,10 @@ target datalayout = "po1:64:64"
define void @negative_step_all_ones_null(ptr addrspace(1) %P, ptr addrspace(1) %S) {
; CHECK-LABEL: 'negative_step_all_ones_null'
; CHECK-NEXT: loop:
-; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Report: cannot identify array bounds
; CHECK-NEXT: Dependences:
; CHECK-NEXT: Run-time memory checks:
-; CHECK-NEXT: Check 0:
-; CHECK-NEXT: Comparing group GRP0:
-; CHECK-NEXT: %ptr.iv = phi ptr addrspace(1) [ %P, %entry ], [ %ptr.iv.next, %loop ]
-; CHECK-NEXT: Against group GRP1:
-; CHECK-NEXT: ptr addrspace(1) %S
; CHECK-NEXT: Grouped accesses:
-; CHECK-NEXT: Group GRP0:
-; CHECK-NEXT: (Low: (-4 + inttoptr (i64 -1 to ptr addrspace(1)))<nsw> High: (4 + %P))
-; CHECK-NEXT: Member: {%P,+,-4}<nw><%loop>
-; CHECK-NEXT: Group GRP1:
-; CHECK-NEXT: (Low: %S High: (4 + %S))
-; CHECK-NEXT: Member: %S
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
; CHECK-NEXT: SCEV assumptions:
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll b/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll
index ebbb435bc2264..232302dc922b2 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/evaluate-at-symbolic-max-backedge-taken-count-may-wrap.ll
@@ -181,7 +181,6 @@ exit:
; The pointer AddRec has a negative step, so its start is the highest accessed
; address and it is the *lowest* accessed address that is unknown when
; evaluating at the symbolic max BTC may wrap.
-; FIXME: Currently the bounds are incorrect.
define void @symbolic_max_btc_may_wrap_negative_step(ptr %P, ptr %S) {
; CHECK-LABEL: 'symbolic_max_btc_may_wrap_negative_step'
; CHECK-NEXT: loop:
@@ -195,7 +194,7 @@ define void @symbolic_max_btc_may_wrap_negative_step(ptr %P, ptr %S) {
; CHECK-NEXT: ptr %S
; CHECK-NEXT: Grouped accesses:
; CHECK-NEXT: Group GRP0:
-; CHECK-NEXT: (Low: (-4 + inttoptr (i32 -1 to ptr))<nsw> High: (4 + %P))
+; CHECK-NEXT: (Low: null High: (4 + %P))
; CHECK-NEXT: Member: {%P,+,-4}<nw><%loop>
; CHECK-NEXT: Group GRP1:
; CHECK-NEXT: (Low: %S High: (4 + %S))
@@ -225,7 +224,6 @@ exit:
; Same as @symbolic_max_btc_may_wrap_negative_step, but with a non-constant
; step that is known to be negative.
-; FIXME: Currently the bounds are incorrect.
define void @symbolic_max_btc_may_wrap_non_constant_negative_step(ptr %P, ptr %S, i32 %step) {
; CHECK-LABEL: 'symbolic_max_btc_may_wrap_non_constant_negative_step'
; CHECK-NEXT: loop:
@@ -239,7 +237,7 @@ define void @symbolic_max_btc_may_wrap_non_constant_negative_step(ptr %P, ptr %S
; CHECK-NEXT: ptr %S
; CHECK-NEXT: Grouped accesses:
; CHECK-NEXT: Group GRP0:
-; CHECK-NEXT: (Low: ((-4 + inttoptr (i32 -1 to ptr))<nsw> umin %P) High: (4 + ((-4 + inttoptr (i32 -1 to ptr))<nsw> umax %P))<nsw>)
+; CHECK-NEXT: (Low: null High: (4 + %P))
; CHECK-NEXT: Member: {%P,+,(-1 + (-1 * (zext i16 (trunc i32 %step to i16) to i32))<nsw>)<nsw>}<nw><%loop>
; CHECK-NEXT: Group GRP1:
; CHECK-NEXT: (Low: %S High: (4 + %S))
@@ -272,7 +270,6 @@ exit:
; Same as @symbolic_max_btc_may_wrap_negative_step, but with a non-constant
; step that is known to be non-negative, so the start is the lowest accessed
; address and only the upper bound has to be widened.
-; FIXME: Currently the bounds are incorrect.
define void @symbolic_max_btc_may_wrap_non_constant_non_negative_step(ptr %P, ptr %S, i32 %step) {
; CHECK-LABEL: 'symbolic_max_btc_may_wrap_non_constant_non_negative_step'
; CHECK-NEXT: loop:
@@ -286,7 +283,7 @@ define void @symbolic_max_btc_may_wrap_non_constant_non_negative_step(ptr %P, pt
; CHECK-NEXT: ptr %S
; CHECK-NEXT: Grouped accesses:
; CHECK-NEXT: Group GRP0:
-; CHECK-NEXT: (Low: ((-4 + inttoptr (i32 -1 to ptr))<nsw> umin %P) High: (4 + ((-4 + inttoptr (i32 -1 to ptr))<nsw> umax %P))<nsw>)
+; CHECK-NEXT: (Low: %P High: inttoptr (i32 -1 to ptr))
; CHECK-NEXT: Member: {%P,+,(zext i16 (trunc i32 %step to i16) to i32)}<nuw><%loop>
; CHECK-NEXT: Group GRP1:
; CHECK-NEXT: (Low: %S High: (4 + %S))
@@ -321,21 +318,10 @@ exit:
define void @symbolic_max_btc_may_wrap_unknown_step_direction(ptr %P, ptr %S, i32 %step.a, i32 %step.b) {
; CHECK-LABEL: 'symbolic_max_btc_may_wrap_unknown_step_direction'
; CHECK-NEXT: loop:
-; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Report: cannot identify array bounds
; CHECK-NEXT: Dependences:
; CHECK-NEXT: Run-time memory checks:
-; CHECK-NEXT: Check 0:
-; CHECK-NEXT: Comparing group GRP0:
-; CHECK-NEXT: %ptr.iv = phi ptr [ %P, %entry ], [ %ptr.iv.next, %loop ]
-; CHECK-NEXT: Against group GRP1:
-; CHECK-NEXT: ptr %S
; CHECK-NEXT: Grouped accesses:
-; CHECK-NEXT: Group GRP0:
-; CHECK-NEXT: (Low: ((-4 + inttoptr (i32 -1 to ptr))<nsw> umin %P) High: (4 + ((-4 + inttoptr (i32 -1 to ptr))<nsw> umax %P))<nsw>)
-; CHECK-NEXT: Member: {%P,+,(%step.a + %step.b)}<nw><%loop>
-; CHECK-NEXT: Group GRP1:
-; CHECK-NEXT: (Low: %S High: (4 + %S))
-; CHECK-NEXT: Member: %S
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
; CHECK-NEXT: SCEV assumptions:
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/negative-step-deref-off-by-eltsize.ll b/llvm/test/Analysis/LoopAccessAnalysis/negative-step-deref-off-by-eltsize.ll
index 1a53fba949749..b9f1857e40137 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/negative-step-deref-off-by-eltsize.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/negative-step-deref-off-by-eltsize.ll
@@ -28,10 +28,10 @@ define void @reverse_reaches_base(ptr dereferenceable(16) %A, ptr dereferenceabl
; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
; CHECK-NEXT: Grouped accesses:
; CHECK-NEXT: Group GRP0:
-; CHECK-NEXT: (Low: (-4 + inttoptr (i64 -1 to ptr))<nsw> High: (16 + %B)<nuw>)
+; CHECK-NEXT: (Low: null High: (16 + %B)<nuw>)
; CHECK-NEXT: Member: {(12 + %B)<nuw>,+,-4}<nw><%loop>
; CHECK-NEXT: Group GRP1:
-; CHECK-NEXT: (Low: (-4 + inttoptr (i64 -1 to ptr))<nsw> High: (16 + %A)<nuw>)
+; CHECK-NEXT: (Low: null High: (16 + %A)<nuw>)
; CHECK-NEXT: Member: {(12 + %A)<nuw>,+,-4}<nw><%loop>
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-after-dependence-analysis-forked-pointers.ll b/llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-after-dependence-analysis-forked-pointers.ll
index ad0467fc6b734..19c52c7dfa88b 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-after-dependence-analysis-forked-pointers.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/retry-runtime-checks-after-dependence-analysis-forked-pointers.ll
@@ -253,15 +253,14 @@ exit:
define void @forked_ptr_with_uncomputable_bounds(ptr %P, ptr %S, i32 %step.a, i32 %step.b, i1 %c) {
; CHECK-LABEL: 'forked_ptr_with_uncomputable_bounds'
; CHECK-NEXT: loop:
-; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT: Unsafe indirect dependence.
+; CHECK-NEXT: Report: cannot identify array bounds
; CHECK-NEXT: Dependences:
-; CHECK-NEXT: IndirectUnsafe:
-; CHECK-NEXT: %l = load i32, ptr %S, align 4 ->
-; CHECK-NEXT: store i32 %l, ptr %select, align 4
-; CHECK-EMPTY:
; CHECK-NEXT: Run-time memory checks:
; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %S High: (4 + %S))
+; CHECK-NEXT: Member: %S
+; CHECK-NEXT: Generated run-time checks are incomplete
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
; CHECK-NEXT: SCEV assumptions:
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/symbolic-stride.ll b/llvm/test/Analysis/LoopAccessAnalysis/symbolic-stride.ll
index c6ce3fbdf678f..f4aff75582c02 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/symbolic-stride.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/symbolic-stride.ll
@@ -521,7 +521,7 @@ define void @unknown_stride_equalto_zext_tc(i16 zeroext %N, ptr %A, ptr %B, i32
; CHECK-NEXT: (Low: %A High: (4 + %A))
; CHECK-NEXT: Member: %A
; CHECK-NEXT: Group GRP1:
-; CHECK-NEXT: (Low: (((2 * (sext i32 %j to i64))<nsw> + %B) umin ((2 * (sext i32 %j to i64))<nsw> + (2 * (zext i32 (-1 + (zext i16 %N to i32))<nsw> to i64) * (zext i16 %N to i64)) + %B)) High: (2 + (((2 * (sext i32 %j to i64))<nsw> + %B) umax ((2 * (sext i32 %j to i64))<nsw> + (2 * (zext i32 (-1 + (zext i16 %N to i32))<nsw> to i64) * (zext i16 %N to i64)) + %B))))
+; CHECK-NEXT: (Low: ((2 * (sext i32 %j to i64))<nsw> + %B) High: (2 + (2 * (sext i32 %j to i64))<nsw> + (2 * (zext i32 (-1 + (zext i16 %N to i32))<nsw> to i64) * (zext i16 %N to i64)) + %B))
; CHECK-NEXT: Member: {((2 * (sext i32 %j to i64))<nsw> + %B),+,(2 * (zext i16 %N to i64))<nuw><nsw>}<nw><%loop>
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
>From 13dfb8de9e18e9c54bfb9d045d3fa3fc4627952b Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 24 Aug 2026 16:54:10 +0100
Subject: [PATCH 2/3] !fixup use inttoptr 0 unconditionally
---
llvm/lib/Analysis/LoopAccessAnalysis.cpp | 31 ++++++-------------
.../all-ones-null-pointer.ll | 17 ++++++++--
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index 0f7673816796e..246f728e26cf5 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -330,22 +330,6 @@ static bool evaluatePtrAddRecAtMaxBTCWillNotWrap(
return SE.isKnownPredicate(CmpInst::ICMP_ULE, MaxOffset, DerefBytesSCEV);
}
-/// Return the lowest address of pointer type \p PtrTy, i.e. a null pointer.
-/// Returns nullptr if it cannot be used as a lower bound.
-static const SCEV *getLowestAddress(Type *PtrTy, ScalarEvolution &SE,
- const DataLayout &DL) {
- if (!DL.getNullPtrValue(PtrTy->getPointerAddressSpace()).isZero())
- return nullptr;
- return SE.getSCEV(Constant::getNullValue(PtrTy));
-}
-
-/// Return the highest address of pointer type \p PtrTy.
-static const SCEV *getHighestAddress(Type *PtrTy, ScalarEvolution &SE,
- const DataLayout &DL) {
- return SE.getSCEV(ConstantExpr::getIntToPtr(
- Constant::getAllOnesValue(DL.getIndexType(PtrTy)), PtrTy));
-}
-
std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
const Loop *Lp, const SCEV *PtrExpr, Type *AccessTy, const SCEV *BTC,
const SCEV *MaxBTC, ScalarEvolution *SE,
@@ -406,16 +390,21 @@ std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
const SCEV *Start = AR->getStart();
Type *PtrTy = AR->getType();
if (SE->isKnownNegative(Step)) {
- ScStart = LastAddr ? LastAddr : getLowestAddress(PtrTy, *SE, DL);
- if (!ScStart)
- return {SE->getCouldNotCompute(), SE->getCouldNotCompute()};
+ ScStart =
+ LastAddr
+ ? LastAddr
+ : SE->getSCEV(ConstantExpr::getIntToPtr(
+ Constant::getNullValue(DL.getIndexType(PtrTy)), PtrTy));
ScEnd = SE->getAddExpr(Start, EltSizeSCEV);
} else if (SE->isKnownNonNegative(Step)) {
ScStart = Start;
// The highest address for the type saturates; adding EltSize to it would
// wrap to the start of the address space.
- ScEnd = LastAddr ? SE->getAddExpr(LastAddr, EltSizeSCEV)
- : getHighestAddress(PtrTy, *SE, DL);
+ ScEnd =
+ LastAddr
+ ? SE->getAddExpr(LastAddr, EltSizeSCEV)
+ : SE->getSCEV(ConstantExpr::getIntToPtr(
+ Constant::getAllOnesValue(DL.getIndexType(PtrTy)), PtrTy));
} else {
if (!LastAddr)
return {SE->getCouldNotCompute(), SE->getCouldNotCompute()};
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll b/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll
index 031c4c7a6c4d3..4d11865d2f7ec 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/all-ones-null-pointer.ll
@@ -5,15 +5,26 @@ target datalayout = "po1:64:64"
; Tests with an address space that has an all-ones nullptr.
-; The step is negative and evaluating at the symbolic max BTC may wrap, so the
-; lowest accessed address is unknown and no runtime checks can be formed.
+; The step is negative and evaluating at the symbolic max BTC may wrap, so we
+; need a zero pointer as lower bound.
define void @negative_step_all_ones_null(ptr addrspace(1) %P, ptr addrspace(1) %S) {
; CHECK-LABEL: 'negative_step_all_ones_null'
; CHECK-NEXT: loop:
-; CHECK-NEXT: Report: cannot identify array bounds
+; CHECK-NEXT: Memory dependences are safe with run-time checks
; CHECK-NEXT: Dependences:
; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %ptr.iv = phi ptr addrspace(1) [ %P, %entry ], [ %ptr.iv.next, %loop ]
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: ptr addrspace(1) %S
; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: null High: (4 + %P))
+; CHECK-NEXT: Member: {%P,+,-4}<nw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: %S High: (4 + %S))
+; CHECK-NEXT: Member: %S
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
; CHECK-NEXT: SCEV assumptions:
>From e499509df44a64116b6966d0ec2095bf1a36f2c2 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 2 Sep 2026 12:54:22 +0100
Subject: [PATCH 3/3] !fixup handle non-affine monotonic case
---
llvm/lib/Analysis/LoopAccessAnalysis.cpp | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index c83055a731193..2d81fb270df63 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -355,12 +355,14 @@ static bool isKnownNonDecreasingInLoop(const SCEV *S, const Loop *L,
///
/// If the offset is provably monotonically non-decreasing the accessed range is
/// bounded by the offset's value at the first iteration (via
-/// SplitIntoInitAndPostInc) and last iteration (via getSCEVAtScope)
+/// SplitIntoInitAndPostInc) and last iteration (via getSCEVAtScope). The
+/// returned range is half-open: \p EltSizeSCEV is added to the address of the
+/// last accessed element to form the end.
///
/// Returns {nullptr, nullptr} if no such bound can be formed.
static std::pair<const SCEV *, const SCEV *>
getNonAffineMonotonicBounds(const Loop *Lp, const SCEV *PtrExpr,
- ScalarEvolution *SE) {
+ const SCEV *EltSizeSCEV, ScalarEvolution *SE) {
const auto *PtrAdd = dyn_cast<SCEVAddExpr>(PtrExpr);
if (!PtrAdd || !PtrAdd->hasNoUnsignedWrap())
return {nullptr, nullptr};
@@ -382,7 +384,8 @@ getNonAffineMonotonicBounds(const Loop *Lp, const SCEV *PtrExpr,
!SE->isLoopInvariant(OffStart, Lp) || !SE->isLoopInvariant(OffEnd, Lp))
return {nullptr, nullptr};
- return {SE->getAddExpr(Base, OffStart), SE->getAddExpr(Base, OffEnd)};
+ return {SE->getAddExpr(Base, OffStart),
+ SE->getAddExpr(Base, OffEnd, EltSizeSCEV)};
}
std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
@@ -472,7 +475,8 @@ std::pair<const SCEV *, const SCEV *> llvm::getStartAndEndForAccess(
} else {
// The pointer is loop-variant but not an affine AddRec. Try to form a
// tight bound for a monotonic offset (see getNonAffineMonotonicBounds).
- std::tie(ScStart, ScEnd) = getNonAffineMonotonicBounds(Lp, PtrExpr, SE);
+ std::tie(ScStart, ScEnd) =
+ getNonAffineMonotonicBounds(Lp, PtrExpr, EltSizeSCEV, SE);
if (!ScStart)
return {SE->getCouldNotCompute(), SE->getCouldNotCompute()};
}
@@ -1350,6 +1354,7 @@ bool AccessAnalysis::createCheckForAccess(RuntimePointerChecking &RtCheck,
unsigned ASId, bool Assume) {
Value *Ptr = Access.getPointer();
ScalarEvolution *SE = PSE.getSE();
+ const DataLayout &DL = TheLoop->getHeader()->getDataLayout();
assert(SE->isSCEVable(Ptr->getType()) && "Value is not SCEVable!");
SmallVector<PointerIntPair<const SCEV *, 1, bool>> RTCheckPtrs;
@@ -1385,8 +1390,11 @@ bool AccessAnalysis::createCheckForAccess(RuntimePointerChecking &RtCheck,
AR = PSE.getAsAddRec(Ptr, &Predicates);
if (!AR || !AR->isAffine()) {
// Check if bounds for non-affine monotonic expressions can be formed.
+ const SCEV *EltSizeSCEV = SE->getStoreSizeOfExpr(
+ DL.getIndexType(P.getPointer()->getType()), AccessTy);
if (!Assume ||
- !getNonAffineMonotonicBounds(TheLoop, P.getPointer(), SE).first)
+ !getNonAffineMonotonicBounds(TheLoop, P.getPointer(), EltSizeSCEV, SE)
+ .first)
return false;
continue;
}
More information about the llvm-commits
mailing list