Skip to content

Commit 8b3bf53

Browse files
committed
[ConstraintElim] Support arbitrary incoming values for inductions (llvm#68032)
Support arbitray incoming values for AddRecs by getting the loop predecessor and checking if its SCEV matches the AddRec start. This is done after the existing check, which can help to catch cases where the expression gets simplified by SCEV to either an IR constant or existing value which can be used instead.
1 parent 2409daf commit 8b3bf53

File tree

2 files changed

+162
-7
lines changed

2 files changed

+162
-7
lines changed

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -846,18 +846,18 @@ void State::addInfoForInductions(BasicBlock &BB) {
846846
return;
847847

848848
auto *AR = dyn_cast_or_null<SCEVAddRecExpr>(SE.getSCEV(PN));
849-
if (!AR)
849+
BasicBlock *LoopPred = L->getLoopPredecessor();
850+
if (!AR || !LoopPred)
850851
return;
851852

852853
const SCEV *StartSCEV = AR->getStart();
853854
Value *StartValue = nullptr;
854-
if (auto *C = dyn_cast<SCEVConstant>(StartSCEV))
855+
if (auto *C = dyn_cast<SCEVConstant>(StartSCEV)) {
855856
StartValue = C->getValue();
856-
else if (auto *U = dyn_cast<SCEVUnknown>(StartSCEV))
857-
StartValue = U->getValue();
858-
859-
if (!StartValue)
860-
return;
857+
} else {
858+
StartValue = PN->getIncomingValueForBlock(LoopPred);
859+
assert(SE.getSCEV(StartValue) == StartSCEV && "inconsistent start value");
860+
}
861861

862862
DomTreeNode *DTN = DT.getNode(InLoopSucc);
863863
auto Inc = SE.getMonotonicPredicateType(AR, CmpInst::ICMP_UGT);
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
2+
; RUN: opt -p constraint-elimination -S %s | FileCheck %s
3+
4+
declare void @use(i1)
5+
6+
define void @start_value_of_inner_add_rec_is_add_rec_condition_can_be_simplified(i32 noundef %len) {
7+
; CHECK-LABEL: define void @start_value_of_inner_add_rec_is_add_rec_condition_can_be_simplified(
8+
; CHECK-SAME: i32 noundef [[LEN:%.*]]) {
9+
; CHECK-NEXT: entry:
10+
; CHECK-NEXT: br label [[OUTER_HEADER:%.*]]
11+
; CHECK: outer.header:
12+
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[OUTER_LATCH:%.*]] ]
13+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[LEN]]
14+
; CHECK-NEXT: br i1 [[CMP]], label [[INNER_HEADER:%.*]], label [[EXIT:%.*]]
15+
; CHECK: inner.header:
16+
; CHECK-NEXT: [[K_0:%.*]] = phi i32 [ [[I_0]], [[OUTER_HEADER]] ], [ [[K_INC:%.*]], [[INNER_LATCH:%.*]] ]
17+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i32 [[K_0]], [[LEN]]
18+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[OUTER_LATCH]], label [[INNER_LATCH]]
19+
; CHECK: inner.latch:
20+
; CHECK-NEXT: call void @use(i1 true)
21+
; CHECK-NEXT: [[K_INC]] = add i32 [[K_0]], 1
22+
; CHECK-NEXT: br label [[INNER_HEADER]]
23+
; CHECK: outer.latch:
24+
; CHECK-NEXT: [[I_INC]] = add i32 [[I_0]], 1
25+
; CHECK-NEXT: br label [[OUTER_HEADER]]
26+
; CHECK: exit:
27+
; CHECK-NEXT: ret void
28+
;
29+
entry:
30+
br label %outer.header
31+
32+
outer.header:
33+
%i.0 = phi i32 [ 1, %entry ], [ %i.inc, %outer.latch ]
34+
%cmp = icmp ult i32 %i.0, %len
35+
br i1 %cmp, label %inner.header, label %exit
36+
37+
inner.header:
38+
%k.0 = phi i32 [ %i.0, %outer.header ], [ %k.inc, %inner.latch ]
39+
%cmp2.not = icmp eq i32 %k.0, %len
40+
br i1 %cmp2.not, label %outer.latch, label %inner.latch
41+
42+
inner.latch:
43+
%cmp.not.i = icmp ult i32 %k.0, %len
44+
call void @use(i1 %cmp.not.i)
45+
%k.inc = add i32 %k.0, 1
46+
br label %inner.header
47+
48+
outer.latch:
49+
%i.inc = add i32 %i.0, 1
50+
br label %outer.header
51+
52+
exit:
53+
ret void
54+
}
55+
56+
define void @start_value_of_inner_add_rec_is_add_rec_condition_cannot_be_simplified(i32 noundef %len) {
57+
; CHECK-LABEL: define void @start_value_of_inner_add_rec_is_add_rec_condition_cannot_be_simplified(
58+
; CHECK-SAME: i32 noundef [[LEN:%.*]]) {
59+
; CHECK-NEXT: entry:
60+
; CHECK-NEXT: br label [[OUTER_HEADER:%.*]]
61+
; CHECK: outer.header:
62+
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[OUTER_LATCH:%.*]] ]
63+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[LEN]]
64+
; CHECK-NEXT: br i1 [[CMP]], label [[INNER_HEADER:%.*]], label [[EXIT:%.*]]
65+
; CHECK: inner.header:
66+
; CHECK-NEXT: [[K_0:%.*]] = phi i32 [ [[I_0]], [[OUTER_HEADER]] ], [ [[K_INC:%.*]], [[INNER_LATCH:%.*]] ]
67+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i32 [[K_0]], [[LEN]]
68+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[OUTER_LATCH]], label [[INNER_LATCH]]
69+
; CHECK: inner.latch:
70+
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i32 [[K_0]], 3
71+
; CHECK-NEXT: call void @use(i1 [[CMP_NOT_I]])
72+
; CHECK-NEXT: [[K_INC]] = add i32 [[K_0]], 1
73+
; CHECK-NEXT: br label [[INNER_HEADER]]
74+
; CHECK: outer.latch:
75+
; CHECK-NEXT: [[I_INC]] = add i32 [[I_0]], 1
76+
; CHECK-NEXT: br label [[OUTER_HEADER]]
77+
; CHECK: exit:
78+
; CHECK-NEXT: ret void
79+
;
80+
entry:
81+
br label %outer.header
82+
83+
outer.header:
84+
%i.0 = phi i32 [ 1, %entry ], [ %i.inc, %outer.latch ]
85+
%cmp = icmp ult i32 %i.0, %len
86+
br i1 %cmp, label %inner.header, label %exit
87+
88+
inner.header:
89+
%k.0 = phi i32 [ %i.0, %outer.header ], [ %k.inc, %inner.latch ]
90+
%cmp2.not = icmp eq i32 %k.0, %len
91+
br i1 %cmp2.not, label %outer.latch, label %inner.latch
92+
93+
inner.latch:
94+
%cmp.not.i = icmp ult i32 %k.0, 3
95+
call void @use(i1 %cmp.not.i)
96+
%k.inc = add i32 %k.0, 1
97+
br label %inner.header
98+
99+
outer.latch:
100+
%i.inc = add i32 %i.0, 1
101+
br label %outer.header
102+
103+
exit:
104+
ret void
105+
}
106+
define void @inner_add_rec_decreasing(i32 noundef %len) {
107+
; CHECK-LABEL: define void @inner_add_rec_decreasing(
108+
; CHECK-SAME: i32 noundef [[LEN:%.*]]) {
109+
; CHECK-NEXT: entry:
110+
; CHECK-NEXT: br label [[OUTER_HEADER:%.*]]
111+
; CHECK: outer.header:
112+
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[OUTER_LATCH:%.*]] ]
113+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[I_0]], [[LEN]]
114+
; CHECK-NEXT: br i1 [[CMP]], label [[INNER_HEADER:%.*]], label [[EXIT:%.*]]
115+
; CHECK: inner.header:
116+
; CHECK-NEXT: [[K_0:%.*]] = phi i32 [ [[I_0]], [[OUTER_HEADER]] ], [ [[K_DEC:%.*]], [[INNER_LATCH:%.*]] ]
117+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i32 [[K_0]], 0
118+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[OUTER_LATCH]], label [[INNER_LATCH]]
119+
; CHECK: inner.latch:
120+
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i32 [[K_0]], [[LEN]]
121+
; CHECK-NEXT: call void @use(i1 [[CMP_NOT_I]])
122+
; CHECK-NEXT: [[K_DEC]] = add i32 [[K_0]], -1
123+
; CHECK-NEXT: br label [[INNER_HEADER]]
124+
; CHECK: outer.latch:
125+
; CHECK-NEXT: [[I_INC]] = add i32 [[I_0]], 1
126+
; CHECK-NEXT: br label [[OUTER_HEADER]]
127+
; CHECK: exit:
128+
; CHECK-NEXT: ret void
129+
;
130+
entry:
131+
br label %outer.header
132+
133+
outer.header:
134+
%i.0 = phi i32 [ 1, %entry ], [ %i.inc, %outer.latch ]
135+
%cmp = icmp ult i32 %i.0, %len
136+
br i1 %cmp, label %inner.header, label %exit
137+
138+
inner.header:
139+
%k.0 = phi i32 [ %i.0, %outer.header ], [ %k.dec, %inner.latch ]
140+
%cmp2.not = icmp eq i32 %k.0, 0
141+
br i1 %cmp2.not, label %outer.latch, label %inner.latch
142+
143+
inner.latch:
144+
%cmp.not.i = icmp ult i32 %k.0, %len
145+
call void @use(i1 %cmp.not.i)
146+
%k.dec = add i32 %k.0, -1
147+
br label %inner.header
148+
149+
outer.latch:
150+
%i.inc = add i32 %i.0, 1
151+
br label %outer.header
152+
153+
exit:
154+
ret void
155+
}

0 commit comments

Comments
 (0)