Skip to content

Commit 03954ed

Browse files
committed
[DependenceAnalysis] Fix incorrect analysis of wrapping AddRec expressions
Fixes GitHub issue #148435 where {false,+,true} patterns reported "da analyze - none!" instead of correct "da analyze - output [*]!". The issue occurs when AddRec expressions in narrow types create cyclic patterns (e.g., {false,+,true} in i1 arithmetic: 0,1,0,1,0,1...) that violate SIV analysis assumptions of linear, non-wrapping recurrences. The fix detects potential wrapping by checking if step × iteration_count exceeds the type's representable range, then classifies such expressions as NonLinear for conservative analysis.
1 parent c993c0b commit 03954ed

File tree

12 files changed

+228
-18
lines changed

12 files changed

+228
-18
lines changed

llvm/include/llvm/Analysis/ScalarEvolution.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,12 @@ class ScalarEvolution {
13371337
/// sharpen it.
13381338
LLVM_ABI void setNoWrapFlags(SCEVAddRecExpr *AddRec, SCEV::NoWrapFlags Flags);
13391339

1340+
/// Check if this AddRec expression may wrap.
1341+
/// Wrapping AddRecs create cyclic patterns that violate linearity
1342+
/// assumptions. Returns true if definitely wraps, false if definitely safe,
1343+
/// nullopt if unknown.
1344+
LLVM_ABI std::optional<bool> mayAddRecWrap(const SCEVAddRecExpr *AddRec);
1345+
13401346
class LoopGuards {
13411347
DenseMap<const SCEV *, const SCEV *> RewriteMap;
13421348
bool PreserveNUW = false;

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,6 +2256,34 @@ bool DependenceInfo::testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level,
22562256
assert(CurLoop == DstAddRec->getLoop() &&
22572257
"both loops in SIV should be same");
22582258
Level = mapSrcLoop(CurLoop);
2259+
2260+
// Check if either AddRec may wrap, which would invalidate SIV analysis
2261+
auto addWrapPredicate = [&](const SCEVAddRecExpr *AR, const char *Name) {
2262+
std::optional<bool> MayWrap = SE->mayAddRecWrap(AR);
2263+
if (MayWrap == true) {
2264+
LLVM_DEBUG(dbgs() << "\tSIV test skipped due to wrapping " << Name
2265+
<< " AddRec\n");
2266+
return false;
2267+
}
2268+
if (!MayWrap.has_value()) {
2269+
// Unknown wrapping - add runtime predicate.
2270+
auto WrapFlags = static_cast<SCEVWrapPredicate::IncrementWrapFlags>(
2271+
SCEVWrapPredicate::IncrementNUSW |
2272+
SCEVWrapPredicate::IncrementNSSW);
2273+
const SCEVPredicate *WrapPred = SE->getWrapPredicate(AR, WrapFlags);
2274+
const_cast<DependenceInfo *>(this)->Assumptions.push_back(WrapPred);
2275+
LLVM_DEBUG(dbgs() << "\t Added runtime wrap assumption for " << Name
2276+
<< ": " << *AR << "\n");
2277+
}
2278+
return true;
2279+
};
2280+
2281+
if (!addWrapPredicate(SrcAddRec, "Src") ||
2282+
!addWrapPredicate(DstAddRec, "Dst")) {
2283+
// Conservative - cannot prove independence.
2284+
return false;
2285+
}
2286+
22592287
bool disproven;
22602288
if (SrcCoeff == DstCoeff)
22612289
disproven = strongSIVtest(SrcCoeff, SrcConst, DstConst, CurLoop, Level,

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6502,8 +6502,37 @@ void ScalarEvolution::setNoWrapFlags(SCEVAddRecExpr *AddRec,
65026502
}
65036503
}
65046504

6505-
ConstantRange ScalarEvolution::
6506-
getRangeForUnknownRecurrence(const SCEVUnknown *U) {
6505+
std::optional<bool>
6506+
ScalarEvolution::mayAddRecWrap(const SCEVAddRecExpr *AddRec) {
6507+
if (AddRec->hasNoSelfWrap())
6508+
return false;
6509+
6510+
// For simple constant step cases, do explicit wrapping check.
6511+
const SCEV *Step = AddRec->getStepRecurrence(*this);
6512+
const SCEVConstant *ConstStep = dyn_cast<SCEVConstant>(Step);
6513+
if (ConstStep) {
6514+
const Loop *Loop = AddRec->getLoop();
6515+
if (hasLoopInvariantBackedgeTakenCount(Loop)) {
6516+
const SCEV *BTC = getBackedgeTakenCount(Loop);
6517+
const SCEVConstant *ConstBTC = dyn_cast<SCEVConstant>(BTC);
6518+
if (ConstBTC) {
6519+
// Check if step * iterations would exceed type range.
6520+
APInt StepVal = ConstStep->getAPInt();
6521+
APInt BTCVal = ConstBTC->getAPInt();
6522+
unsigned BitWidth = AddRec->getType()->getScalarSizeInBits();
6523+
6524+
bool Overflow = false;
6525+
APInt Product = StepVal.zext(64).umul_ov(BTCVal.zext(64), Overflow);
6526+
6527+
return Overflow || Product.getZExtValue() >= (1ULL << BitWidth);
6528+
}
6529+
}
6530+
}
6531+
return std::nullopt;
6532+
}
6533+
6534+
ConstantRange
6535+
ScalarEvolution::getRangeForUnknownRecurrence(const SCEVUnknown *U) {
65076536
const DataLayout &DL = getDataLayout();
65086537

65096538
unsigned BitWidth = getTypeSizeInBits(U->getType());

llvm/test/Analysis/DDG/basic-a.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ for.end: ; preds = %test1.for.body, %en
9898

9999
; CHECK: Node Address:[[N2]]:single-instruction
100100
; CHECK-NEXT: Instructions:
101-
; CHECK-NEXT: %inc = add i64 %i.02, 1
101+
; CHECK-NEXT: %inc = add nsw i64 %i.02, 1
102102
; CHECK-NEXT: Edges:
103103
; CHECK-NEXT: [def-use] to [[N1]]
104104
; CHECK-NEXT: --- end of nodes in pi-block ---
@@ -168,7 +168,7 @@ test2.for.body: ; preds = %entry, %test2
168168
%add = fadd float %0, %1
169169
%arrayidx2 = getelementptr inbounds float, ptr %a, i64 %i.02
170170
store float %add, ptr %arrayidx2, align 4
171-
%inc = add i64 %i.02, 1
171+
%inc = add nsw i64 %i.02, 1
172172
%exitcond = icmp ne i64 %inc, %n
173173
br i1 %exitcond, label %test2.for.body, label %for.end
174174

llvm/test/Analysis/DDG/basic-b.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
; CHECK: Node Address:[[N3]]:single-instruction
1414
; CHECK-NEXT: Instructions:
15-
; CHECK-NEXT: %inc = add i64 %i.02, 1
15+
; CHECK-NEXT: %inc = add nsw i64 %i.02, 1
1616
; CHECK-NEXT: Edges:
1717
; CHECK-NEXT: [def-use] to [[N2]]
1818
; CHECK-NEXT:--- end of nodes in pi-block ---
@@ -95,7 +95,7 @@ test1.for.body: ; preds = %entry, %test1
9595
%add = fadd float %0, %1
9696
%arrayidx3 = getelementptr inbounds float, ptr %a, i64 %i.02
9797
store float %add, ptr %arrayidx3, align 4
98-
%inc = add i64 %i.02, 1
98+
%inc = add nsw i64 %i.02, 1
9999
%cmp = icmp ult i64 %inc, %sub
100100
br i1 %cmp, label %test1.for.body, label %for.end
101101

@@ -116,7 +116,7 @@ for.end: ; preds = %test1.for.body, %en
116116

117117
; CHECK: Node Address:[[N3]]:single-instruction
118118
; CHECK-NEXT: Instructions:
119-
; CHECK-NEXT: %inc = add i64 %i.02, 1
119+
; CHECK-NEXT: %inc = add nsw i64 %i.02, 1
120120
; CHECK-NEXT: Edges:
121121
; CHECK-NEXT: [def-use] to [[N2]]
122122
; CHECK-NEXT:--- end of nodes in pi-block ---
@@ -188,7 +188,7 @@ test2.for.body: ; preds = %entry, %test2
188188
%add = fadd float %0, %1
189189
%arrayidx3 = getelementptr inbounds float, ptr %a, i64 %i.02
190190
store float %add, ptr %arrayidx3, align 4
191-
%inc = add i64 %i.02, 1
191+
%inc = add nsw i64 %i.02, 1
192192
%cmp = icmp ult i64 %inc, %sub
193193
br i1 %cmp, label %test2.for.body, label %for.end
194194

llvm/test/Analysis/DependenceAnalysis/DADelin.ll

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,12 @@ define void @t7(i32 %n, i32 %m, i32 %o, ptr nocapture %A) {
417417
; CHECK-NEXT: da analyze - none!
418418
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx, align 4 --> Dst: store i32 %add12, ptr %arrayidx2, align 4
419419
; CHECK-NEXT: da analyze - consistent anti [1 0 0]!
420+
; CHECK-NEXT: Runtime Assumptions:
421+
; CHECK-NEXT: {-1,+,1}<%for.cond1.preheader> Added Flags: <nusw><nssw>
420422
; CHECK-NEXT: Src: store i32 %add12, ptr %arrayidx2, align 4 --> Dst: store i32 %add12, ptr %arrayidx2, align 4
421423
; CHECK-NEXT: da analyze - none!
424+
; CHECK-NEXT: Runtime Assumptions:
425+
; CHECK-NEXT: {-1,+,1}<%for.cond1.preheader> Added Flags: <nusw><nssw>
422426
;
423427
entry:
424428
%cmp49 = icmp sgt i32 %n, 0
@@ -442,17 +446,18 @@ for.cond5.preheader: ; preds = %for.cond.cleanup7,
442446
br i1 %cmp645, label %for.body8.lr.ph, label %for.cond.cleanup7
443447

444448
for.body8.lr.ph: ; preds = %for.cond5.preheader
445-
%mul944 = add i32 %j.048, %mul
446-
%add = mul i32 %mul944, %o
449+
%mul944 = add nsw i32 %j.048, %mul
450+
%add = mul nsw i32 %mul944, %o
447451
br label %for.body8
448452

449453
for.body8: ; preds = %for.body8, %for.body8.lr.ph
450454
%k.046 = phi i32 [ 0, %for.body8.lr.ph ], [ %inc, %for.body8 ]
451-
%add11 = add nsw i32 %k.046, %add
455+
456+
%add11 = add nsw i32 %k.046, %add
452457
%arrayidx = getelementptr inbounds i32, ptr %A, i32 %add11
453458
%0 = load i32, ptr %arrayidx, align 4
454459
%add12 = add nsw i32 %0, 1
455-
%mo = mul i32 %m, %o
460+
%mo = mul nsw i32 %m, %o
456461
%add111 = sub nsw i32 %add11, %mo
457462
%arrayidx2 = getelementptr inbounds i32, ptr %A, i32 %add111
458463
store i32 %add12, ptr %arrayidx2, align 4
@@ -649,8 +654,14 @@ define void @coeff_may_negative(ptr %a, i32 %k) {
649654
; CHECK-NEXT: da analyze - none!
650655
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
651656
; CHECK-NEXT: da analyze - output [*|<]!
657+
; CHECK-NEXT: Runtime Assumptions:
658+
; CHECK-NEXT: {%a,+,%k}<%loop> Added Flags: <nusw><nssw>
659+
; CHECK-NEXT: {(%k + %a),+,%k}<%loop> Added Flags: <nusw><nssw>
652660
; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
653661
; CHECK-NEXT: da analyze - none!
662+
; CHECK-NEXT: Runtime Assumptions:
663+
; CHECK-NEXT: {%a,+,%k}<%loop> Added Flags: <nusw><nssw>
664+
; CHECK-NEXT: {(%k + %a),+,%k}<%loop> Added Flags: <nusw><nssw>
654665
;
655666
entry:
656667
br label %loop
@@ -688,11 +699,24 @@ define void @coeff_positive(ptr %a, i32 %k) {
688699
; CHECK-NEXT: da analyze - none!
689700
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
690701
; CHECK-NEXT: da analyze - output [*|<]!
702+
; CHECK-NEXT: Runtime Assumptions:
703+
; CHECK-NEXT: {%a,+,%k}<%loop> Added Flags: <nusw><nssw>
704+
; CHECK-NEXT: {(%k + %a),+,%k}<%loop> Added Flags: <nusw><nssw>
691705
; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
692706
; CHECK-NEXT: da analyze - none!
707+
; CHECK-NEXT: Runtime Assumptions:
708+
; CHECK-NEXT: {%a,+,%k}<%loop> Added Flags: <nusw><nssw>
709+
; CHECK-NEXT: {(%k + %a),+,%k}<%loop> Added Flags: <nusw><nssw>
693710
;
711+
694712
entry:
695-
br label %loop
713+
714+
715+
716+
717+
718+
719+
br label %loop
696720

697721
loop:
698722
%i = phi i32 [ 0, %entry ], [ %i.next, %loop ]

llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,26 @@ define void @test(ptr %A, ptr %B, i1 %arg, i32 %n, i32 %m) #0 align 2 {
4343
; CHECK-NEXT: Runtime Assumptions:
4444
; CHECK-NEXT: Equal predicate: (zext i7 (4 * (trunc i32 %v1 to i7) * (1 + (trunc i32 %n to i7))) to i32) == 0
4545
; CHECK-NEXT: Equal predicate: (8 * (zext i4 (trunc i32 %v1 to i4) to i32))<nuw><nsw> == 0
46+
; CHECK-NEXT: {(80640 + (4 * (1 + %n) * %v1) + %A),+,(8 * %v1)}<%bb13> Added Flags: <nusw><nssw>
4647
; CHECK-NEXT: Src: %v27 = load <32 x i32>, ptr %v25, align 256 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
4748
; CHECK-NEXT: da analyze - input [* S S|<]!
4849
; CHECK-NEXT: Runtime Assumptions:
4950
; CHECK-NEXT: Equal predicate: (zext i7 (4 * (trunc i32 %v1 to i7) * (1 + (trunc i32 %n to i7))) to i32) == 0
5051
; CHECK-NEXT: Equal predicate: (8 * (zext i4 (trunc i32 %v1 to i4) to i32))<nuw><nsw> == 0
52+
; CHECK-NEXT: {(80640 + (4 * (1 + %n) * %v1) + %A),+,(8 * %v1)}<%bb13> Added Flags: <nusw><nssw>
53+
; CHECK-NEXT: {(126720 + (128 * %m) + %A),+,256}<%bb13> Added Flags: <nusw><nssw>
5154
; CHECK-NEXT: Src: %v32 = load <32 x i32>, ptr %v30, align 128 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
5255
; CHECK-NEXT: da analyze - consistent input [0 S S]!
56+
; CHECK-NEXT: Runtime Assumptions:
57+
; CHECK-NEXT: Equal predicate: (zext i7 (4 * (trunc i32 %v1 to i7) * (1 + (trunc i32 %n to i7))) to i32) == 0
58+
; CHECK-NEXT: Equal predicate: (8 * (zext i4 (trunc i32 %v1 to i4) to i32))<nuw><nsw> == 0
59+
; CHECK-NEXT: {(80640 + (4 * (1 + %n) * %v1) + %A),+,(8 * %v1)}<%bb13> Added Flags: <nusw><nssw>
60+
; CHECK-NEXT: {(126720 + (128 * %m) + %A),+,256}<%bb13> Added Flags: <nusw><nssw>
5361
; CHECK-NEXT: Runtime Assumptions:
5462
; CHECK-NEXT: Equal predicate: (zext i7 (4 * (trunc i32 %v1 to i7) * (1 + (trunc i32 %n to i7))) to i32) == 0
5563
; CHECK-NEXT: Equal predicate: (8 * (zext i4 (trunc i32 %v1 to i4) to i32))<nuw><nsw> == 0
64+
; CHECK-NEXT: {(80640 + (4 * (1 + %n) * %v1) + %A),+,(8 * %v1)}<%bb13> Added Flags: <nusw><nssw>
65+
; CHECK-NEXT: {(126720 + (128 * %m) + %A),+,256}<%bb13> Added Flags: <nusw><nssw>
5666
;
5767
entry:
5868
%v1 = load i32, ptr %B, align 4

llvm/test/Analysis/DependenceAnalysis/PR148435.ll

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
; expressions contain AddRec after propagation, which can happen when
77
; constraints simplify the expressions to non-AddRec forms.
88

9-
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"
10-
target triple = "aarch64-unknown-linux-gnu"
11-
129
define void @_Z1cb(ptr %a) {
1310
; CHECK-LABEL: '_Z1cb'
1411
; CHECK-NEXT: Src: store i8 0, ptr %arrayidx9, align 1 --> Dst: store i8 0, ptr %arrayidx9, align 1
@@ -75,8 +72,7 @@ for.end10: ; preds = %for.cond
7572
define void @f1(ptr %a) {
7673
; CHECK-LABEL: 'f1'
7774
; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
78-
; CHECK-NEXT: da analyze - none!
79-
; Note: the second patch for PR148435 modifies the above CHECK to correct "output [*]".
75+
; CHECK-NEXT: da analyze - consistent output [*]!
8076
;
8177
entry:
8278
br label %loop

llvm/test/Analysis/DependenceAnalysis/SimpleSIVNoValidityCheck.ll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,14 @@ define void @t2(i32 signext %n, i32 signext %m, ptr %a) {
8181
; CHECK-NEXT: da analyze - none!
8282
; CHECK-NEXT: Src: %21 = load i32, ptr %arrayidx28, align 4 --> Dst: store i32 %21, ptr %arrayidx38, align 4
8383
; CHECK-NEXT: da analyze - consistent anti [1 -2 0 -3 2]!
84+
; CHECK-NEXT: Runtime Assumptions:
85+
; CHECK-NEXT: {0,+,1}<%for.body12> Added Flags: <nusw><nssw>
86+
; CHECK-NEXT: {3,+,1}<%for.body12> Added Flags: <nusw><nssw>
8487
; CHECK-NEXT: Src: store i32 %21, ptr %arrayidx38, align 4 --> Dst: store i32 %21, ptr %arrayidx38, align 4
8588
; CHECK-NEXT: da analyze - none!
89+
; CHECK-NEXT: Runtime Assumptions:
90+
; CHECK-NEXT: {0,+,1}<%for.body12> Added Flags: <nusw><nssw>
91+
; CHECK-NEXT: {3,+,1}<%for.body12> Added Flags: <nusw><nssw>
8692
;
8793
; LIN-LABEL: 't2'
8894
; LIN-NEXT: Src: %21 = load i32, ptr %arrayidx28, align 4 --> Dst: %21 = load i32, ptr %arrayidx28, align 4
@@ -211,8 +217,14 @@ define void @t3(i64 %n, i64 %m, i64 %lb, ptr %a) {
211217
; CHECK-NEXT: da analyze - none!
212218
; CHECK-NEXT: Src: %2 = load i32, ptr %arrayidx6, align 4 --> Dst: store i32 %2, ptr %arrayidx8, align 4
213219
; CHECK-NEXT: da analyze - consistent anti [1 -2]!
220+
; CHECK-NEXT: Runtime Assumptions:
221+
; CHECK-NEXT: {(-2 + %lb),+,1}<%for.body4> Added Flags: <nusw><nssw>
222+
; CHECK-NEXT: {%lb,+,1}<%for.body4> Added Flags: <nusw><nssw>
214223
; CHECK-NEXT: Src: store i32 %2, ptr %arrayidx8, align 4 --> Dst: store i32 %2, ptr %arrayidx8, align 4
215224
; CHECK-NEXT: da analyze - none!
225+
; CHECK-NEXT: Runtime Assumptions:
226+
; CHECK-NEXT: {(-2 + %lb),+,1}<%for.body4> Added Flags: <nusw><nssw>
227+
; CHECK-NEXT: {%lb,+,1}<%for.body4> Added Flags: <nusw><nssw>
216228
;
217229
; LIN-LABEL: 't3'
218230
; LIN-NEXT: Src: %2 = load i32, ptr %arrayidx6, align 4 --> Dst: %2 = load i32, ptr %arrayidx6, align 4
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -disable-output "-passes=print<da>" 2>&1 | FileCheck %s
3+
4+
; Test case for bug #148435 - SIV test assertion failure
5+
; This test ensures that testSIV handles the case where neither Src nor Dst
6+
; expressions contain AddRec after propagation, which can happen when
7+
; constraints simplify the expressions to non-AddRec forms.
8+
9+
define void @f(ptr %a) {
10+
; CHECK-LABEL: 'f'
11+
; CHECK-NEXT: Src: store i8 42, ptr %idx, align 1 --> Dst: store i8 42, ptr %idx, align 1
12+
; CHECK-NEXT: da analyze - consistent output [* *]!
13+
;
14+
entry:
15+
br label %loop.i.header
16+
17+
loop.i.header:
18+
%i = phi i64 [ 0, %entry ], [ %i.next, %loop.i.latch ]
19+
%and.i = and i64 %i, 1
20+
br label %loop.j
21+
22+
loop.j:
23+
%j = phi i64 [ 0, %loop.i.header ], [ %j.next, %loop.j ]
24+
%and.j = and i64 %j, 1
25+
%idx = getelementptr [2 x [2 x i8]], ptr %a, i64 0, i64 %and.i, i64 %and.j
26+
store i8 42, ptr %idx
27+
%j.next = add i64 %j, 1
28+
%exitcond.j = icmp eq i64 %j.next, 100
29+
br i1 %exitcond.j, label %loop.i.latch, label %loop.j
30+
31+
loop.i.latch:
32+
%i.next = add i64 %i, 1
33+
%exitcond.i = icmp eq i64 %i.next, 100
34+
br i1 %exitcond.i, label %exit, label %loop.i.header
35+
36+
exit:
37+
ret void
38+
}

0 commit comments

Comments
 (0)