Skip to content

Commit 234f778

Browse files
committed
[ConstraintElim] Support decrementing inductions with step -1. (llvm#68644)
Extend the logic in addInfoForInductions to support decrementing inductions with a step of -1. Fixes llvm#64881.
1 parent 8b3bf53 commit 234f778

File tree

4 files changed

+281
-10
lines changed

4 files changed

+281
-10
lines changed

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,26 @@ void State::addInfoForInductions(BasicBlock &BB) {
876876
else
877877
return;
878878

879+
// Handle negative steps.
880+
if (StepOffset.isNegative()) {
881+
// TODO: Extend to allow steps > -1.
882+
if (!(-StepOffset).isOne())
883+
return;
884+
885+
// AR may wrap.
886+
// Add StartValue >= PN conditional on B <= StartValue which guarantees that
887+
// the loop exits before wrapping with a step of -1.
888+
WorkList.push_back(FactOrCheck::getConditionFact(
889+
DTN, CmpInst::ICMP_UGE, StartValue, PN,
890+
ConditionTy(CmpInst::ICMP_ULE, B, StartValue)));
891+
// Add PN > B conditional on B <= StartValue which guarantees that the loop
892+
// exits when reaching B with a step of -1.
893+
WorkList.push_back(FactOrCheck::getConditionFact(
894+
DTN, CmpInst::ICMP_UGT, PN, B,
895+
ConditionTy(CmpInst::ICMP_ULE, B, StartValue)));
896+
return;
897+
}
898+
879899
// Make sure AR either steps by 1 or that the value we compare against is a
880900
// GEP based on the same start value and all offsets are a multiple of the
881901
// step size, to guarantee that the induction will reach the value.
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
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+
declare void @llvm.assume(i1)
6+
7+
define void @add_rec_decreasing_cond_true_constant(i8 noundef %len) {
8+
; CHECK-LABEL: define void @add_rec_decreasing_cond_true_constant(
9+
; CHECK-SAME: i8 noundef [[LEN:%.*]]) {
10+
; CHECK-NEXT: entry:
11+
; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
12+
; CHECK: loop.header:
13+
; CHECK-NEXT: [[K_0:%.*]] = phi i8 [ 4, [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
14+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[K_0]], 0
15+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
16+
; CHECK: loop.latch:
17+
; CHECK-NEXT: call void @use(i1 true)
18+
; CHECK-NEXT: [[K_DEC]] = add i8 [[K_0]], -1
19+
; CHECK-NEXT: br label [[LOOP_HEADER]]
20+
; CHECK: exit:
21+
; CHECK-NEXT: ret void
22+
;
23+
entry:
24+
br label %loop.header
25+
26+
loop.header:
27+
%k.0 = phi i8 [ 4, %entry], [ %k.dec, %loop.latch ]
28+
%cmp2.not = icmp eq i8 %k.0, 0
29+
br i1 %cmp2.not, label %exit, label %loop.latch
30+
31+
loop.latch:
32+
%cmp.not.i = icmp ult i8 %k.0, 5
33+
call void @use(i1 %cmp.not.i)
34+
%k.dec = add i8 %k.0, -1
35+
br label %loop.header
36+
37+
exit:
38+
ret void
39+
}
40+
41+
define void @add_rec_decreasing_cond_not_true_constant(i8 noundef %len) {
42+
; CHECK-LABEL: define void @add_rec_decreasing_cond_not_true_constant(
43+
; CHECK-SAME: i8 noundef [[LEN:%.*]]) {
44+
; CHECK-NEXT: entry:
45+
; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
46+
; CHECK: loop.header:
47+
; CHECK-NEXT: [[K_0:%.*]] = phi i8 [ 4, [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
48+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[K_0]], 0
49+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
50+
; CHECK: loop.latch:
51+
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], 4
52+
; CHECK-NEXT: call void @use(i1 [[CMP_NOT_I]])
53+
; CHECK-NEXT: [[K_DEC]] = add i8 [[K_0]], -1
54+
; CHECK-NEXT: br label [[LOOP_HEADER]]
55+
; CHECK: exit:
56+
; CHECK-NEXT: ret void
57+
;
58+
entry:
59+
br label %loop.header
60+
61+
loop.header:
62+
%k.0 = phi i8 [ 4, %entry], [ %k.dec, %loop.latch ]
63+
%cmp2.not = icmp eq i8 %k.0, 0
64+
br i1 %cmp2.not, label %exit, label %loop.latch
65+
66+
loop.latch:
67+
%cmp.not.i = icmp ult i8 %k.0, 4
68+
call void @use(i1 %cmp.not.i)
69+
%k.dec = add i8 %k.0, -1
70+
br label %loop.header
71+
72+
exit:
73+
ret void
74+
}
75+
76+
define void @add_rec_decreasing_cond_true_start_signed_positive(i8 noundef %start) {
77+
; CHECK-LABEL: define void @add_rec_decreasing_cond_true_start_signed_positive(
78+
; CHECK-SAME: i8 noundef [[START:%.*]]) {
79+
; CHECK-NEXT: entry:
80+
; CHECK-NEXT: [[PRECOND:%.*]] = icmp sge i8 [[START]], 1
81+
; CHECK-NEXT: call void @llvm.assume(i1 [[PRECOND]])
82+
; CHECK-NEXT: [[START_1:%.*]] = add i8 [[START]], -1
83+
; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
84+
; CHECK: loop.header:
85+
; CHECK-NEXT: [[K_0:%.*]] = phi i8 [ [[START_1]], [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
86+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[K_0]], 0
87+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
88+
; CHECK: loop.latch:
89+
; CHECK-NEXT: call void @use(i1 true)
90+
; CHECK-NEXT: [[K_DEC]] = add i8 [[K_0]], -1
91+
; CHECK-NEXT: br label [[LOOP_HEADER]]
92+
; CHECK: exit:
93+
; CHECK-NEXT: ret void
94+
;
95+
entry:
96+
%precond = icmp sge i8 %start, 1
97+
call void @llvm.assume(i1 %precond)
98+
%start.1 = add i8 %start, -1
99+
br label %loop.header
100+
101+
loop.header:
102+
%k.0 = phi i8 [ %start.1, %entry], [ %k.dec, %loop.latch ]
103+
%cmp2.not = icmp eq i8 %k.0, 0
104+
br i1 %cmp2.not, label %exit, label %loop.latch
105+
106+
loop.latch:
107+
%cmp.not.i = icmp ult i8 %k.0, %start
108+
call void @use(i1 %cmp.not.i)
109+
%k.dec = add i8 %k.0, -1
110+
br label %loop.header
111+
112+
exit:
113+
ret void
114+
}
115+
116+
define void @add_rec_decreasing_cond_not_true_start_signed_positive(i8 noundef %start) {
117+
; CHECK-LABEL: define void @add_rec_decreasing_cond_not_true_start_signed_positive(
118+
; CHECK-SAME: i8 noundef [[START:%.*]]) {
119+
; CHECK-NEXT: entry:
120+
; CHECK-NEXT: [[PRECOND:%.*]] = icmp sge i8 [[START]], 1
121+
; CHECK-NEXT: call void @llvm.assume(i1 [[PRECOND]])
122+
; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
123+
; CHECK: loop.header:
124+
; CHECK-NEXT: [[K_0:%.*]] = phi i8 [ [[START]], [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
125+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[K_0]], 0
126+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
127+
; CHECK: loop.latch:
128+
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], [[START]]
129+
; CHECK-NEXT: call void @use(i1 [[CMP_NOT_I]])
130+
; CHECK-NEXT: [[K_DEC]] = add i8 [[K_0]], -1
131+
; CHECK-NEXT: br label [[LOOP_HEADER]]
132+
; CHECK: exit:
133+
; CHECK-NEXT: ret void
134+
;
135+
entry:
136+
%precond = icmp sge i8 %start, 1
137+
call void @llvm.assume(i1 %precond)
138+
br label %loop.header
139+
140+
loop.header:
141+
%k.0 = phi i8 [ %start, %entry], [ %k.dec, %loop.latch ]
142+
%cmp2.not = icmp eq i8 %k.0, 0
143+
br i1 %cmp2.not, label %exit, label %loop.latch
144+
145+
loop.latch:
146+
%cmp.not.i = icmp ult i8 %k.0, %start
147+
call void @use(i1 %cmp.not.i)
148+
%k.dec = add i8 %k.0, -1
149+
br label %loop.header
150+
151+
exit:
152+
ret void
153+
}
154+
155+
define void @add_rec_decreasing_add_rec_positive_to_negative(i8 noundef %len) {
156+
; CHECK-LABEL: define void @add_rec_decreasing_add_rec_positive_to_negative(
157+
; CHECK-SAME: i8 noundef [[LEN:%.*]]) {
158+
; CHECK-NEXT: entry:
159+
; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
160+
; CHECK: loop.header:
161+
; CHECK-NEXT: [[K_0:%.*]] = phi i8 [ 4, [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
162+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[K_0]], -2
163+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
164+
; CHECK: loop.latch:
165+
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], 5
166+
; CHECK-NEXT: call void @use(i1 [[CMP_NOT_I]])
167+
; CHECK-NEXT: [[K_DEC]] = add i8 [[K_0]], -1
168+
; CHECK-NEXT: br label [[LOOP_HEADER]]
169+
; CHECK: exit:
170+
; CHECK-NEXT: ret void
171+
;
172+
entry:
173+
br label %loop.header
174+
175+
loop.header:
176+
%k.0 = phi i8 [ 4, %entry], [ %k.dec, %loop.latch ]
177+
%cmp2.not = icmp eq i8 %k.0, -2
178+
br i1 %cmp2.not, label %exit, label %loop.latch
179+
180+
loop.latch:
181+
%cmp.not.i = icmp ult i8 %k.0, 5
182+
call void @use(i1 %cmp.not.i)
183+
%k.dec = add i8 %k.0, -1
184+
br label %loop.header
185+
186+
exit:
187+
ret void
188+
}
189+
190+
define void @add_rec_decreasing_2_cond_true_constant(i8 noundef %len) {
191+
; CHECK-LABEL: define void @add_rec_decreasing_2_cond_true_constant(
192+
; CHECK-SAME: i8 noundef [[LEN:%.*]]) {
193+
; CHECK-NEXT: entry:
194+
; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
195+
; CHECK: loop.header:
196+
; CHECK-NEXT: [[K_0:%.*]] = phi i8 [ 4, [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
197+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[K_0]], 0
198+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
199+
; CHECK: loop.latch:
200+
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], 5
201+
; CHECK-NEXT: call void @use(i1 [[CMP_NOT_I]])
202+
; CHECK-NEXT: [[K_DEC]] = add i8 [[K_0]], -2
203+
; CHECK-NEXT: br label [[LOOP_HEADER]]
204+
; CHECK: exit:
205+
; CHECK-NEXT: ret void
206+
;
207+
entry:
208+
br label %loop.header
209+
210+
loop.header:
211+
%k.0 = phi i8 [ 4, %entry], [ %k.dec, %loop.latch ]
212+
%cmp2.not = icmp eq i8 %k.0, 0
213+
br i1 %cmp2.not, label %exit, label %loop.latch
214+
215+
loop.latch:
216+
%cmp.not.i = icmp ult i8 %k.0, 5
217+
call void @use(i1 %cmp.not.i)
218+
%k.dec = add i8 %k.0, -2
219+
br label %loop.header
220+
221+
exit:
222+
ret void
223+
}
224+
225+
define void @add_rec_decreasing_2_cond_not_true_constant(i8 noundef %len) {
226+
; CHECK-LABEL: define void @add_rec_decreasing_2_cond_not_true_constant(
227+
; CHECK-SAME: i8 noundef [[LEN:%.*]]) {
228+
; CHECK-NEXT: entry:
229+
; CHECK-NEXT: br label [[LOOP_HEADER:%.*]]
230+
; CHECK: loop.header:
231+
; CHECK-NEXT: [[K_0:%.*]] = phi i8 [ 5, [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
232+
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i8 [[K_0]], 0
233+
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
234+
; CHECK: loop.latch:
235+
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], 5
236+
; CHECK-NEXT: call void @use(i1 [[CMP_NOT_I]])
237+
; CHECK-NEXT: [[K_DEC]] = add i8 [[K_0]], -2
238+
; CHECK-NEXT: br label [[LOOP_HEADER]]
239+
; CHECK: exit:
240+
; CHECK-NEXT: ret void
241+
;
242+
entry:
243+
br label %loop.header
244+
245+
loop.header:
246+
%k.0 = phi i8 [ 5, %entry], [ %k.dec, %loop.latch ]
247+
%cmp2.not = icmp eq i8 %k.0, 0
248+
br i1 %cmp2.not, label %exit, label %loop.latch
249+
250+
loop.latch:
251+
%cmp.not.i = icmp ult i8 %k.0, 5
252+
call void @use(i1 %cmp.not.i)
253+
%k.dec = add i8 %k.0, -2
254+
br label %loop.header
255+
256+
exit:
257+
ret void
258+
}

llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-nested-loops.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ define void @inner_add_rec_decreasing(i32 noundef %len) {
117117
; CHECK-NEXT: [[CMP2_NOT:%.*]] = icmp eq i32 [[K_0]], 0
118118
; CHECK-NEXT: br i1 [[CMP2_NOT]], label [[OUTER_LATCH]], label [[INNER_LATCH]]
119119
; 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]])
120+
; CHECK-NEXT: call void @use(i1 true)
122121
; CHECK-NEXT: [[K_DEC]] = add i32 [[K_0]], -1
123122
; CHECK-NEXT: br label [[INNER_HEADER]]
124123
; CHECK: outer.latch:

llvm/test/Transforms/PhaseOrdering/loop-access-checks.ll

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ declare void @abort()
270270

271271
define void @monkey(ptr noundef %arr, i32 noundef %len) {
272272
; CHECK-LABEL: define void @monkey
273-
; CHECK-SAME: (ptr nocapture noundef [[ARR:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr {
273+
; CHECK-SAME: (ptr nocapture noundef [[ARR:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
274274
; CHECK-NEXT: entry:
275275
; CHECK-NEXT: [[CMP8:%.*]] = icmp ugt i32 [[LEN]], 1
276276
; CHECK-NEXT: br i1 [[CMP8]], label [[FOR_BODY4_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]]
@@ -284,13 +284,7 @@ define void @monkey(ptr noundef %arr, i32 noundef %len) {
284284
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[INC]], [[LEN]]
285285
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY4_PREHEADER]], label [[FOR_COND_CLEANUP]]
286286
; CHECK: for.body4:
287-
; CHECK-NEXT: [[K_07:%.*]] = phi i32 [ [[DEC:%.*]], [[AT_EXIT:%.*]] ], [ [[I_09]], [[FOR_BODY4_PREHEADER]] ]
288-
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i32 [[K_07]], [[LEN]]
289-
; CHECK-NEXT: br i1 [[CMP_NOT_I]], label [[AT_EXIT]], label [[IF_THEN_I:%.*]]
290-
; CHECK: if.then.i:
291-
; CHECK-NEXT: tail call void @abort()
292-
; CHECK-NEXT: unreachable
293-
; CHECK: at.exit:
287+
; CHECK-NEXT: [[K_07:%.*]] = phi i32 [ [[DEC:%.*]], [[FOR_BODY4]] ], [ [[I_09]], [[FOR_BODY4_PREHEADER]] ]
294288
; CHECK-NEXT: [[IDX_EXT_I:%.*]] = zext i32 [[K_07]] to i64
295289
; CHECK-NEXT: [[ADD_PTR_I:%.*]] = getelementptr inbounds i32, ptr [[ARR]], i64 [[IDX_EXT_I]]
296290
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ADD_PTR_I]], align 4

0 commit comments

Comments
 (0)