Skip to content

Commit 75daf22

Browse files
committed
[InstCombine] Fold dependent IVs
Fold `iv = phi(start, iv.next = iv2.next + start)` where `iv2 = phi(iv2.start, iv2.next = iv2 + iv2.step)` to `iv = iv2 + start` removing one induction variable from the loop. Proof: https://alive2.llvm.org/ce/z/hfmwgf
1 parent 1789dc2 commit 75daf22

File tree

2 files changed

+72
-36
lines changed

2 files changed

+72
-36
lines changed

llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,51 @@ static Value *simplifyUsingControlFlow(InstCombiner &Self, PHINode &PN,
13781378
return nullptr;
13791379
}
13801380

1381+
// Fold iv = phi(start, iv.next = iv2.next op start)
1382+
// where iv2 = phi(iv2.start, iv2.next = iv2 + iv2.step)
1383+
// and iv2.start op start = start
1384+
// to iv = iv2 op start
1385+
// Currently limited to op == add/gep and iv2.start == 0.
1386+
static Value *foldDependentIVs(PHINode &PN, IRBuilderBase &Builder) {
1387+
BasicBlock *BB = PN.getParent();
1388+
if (PN.getNumIncomingValues() != 2)
1389+
return nullptr;
1390+
1391+
Value *Start;
1392+
Instruction *IvNext;
1393+
BinaryOperator *Iv2Next;
1394+
auto MatchOuterIV = [&](Value *V1, Value *V2) {
1395+
if (match(V2, m_c_Add(m_Specific(V1), m_BinOp(Iv2Next))) ||
1396+
match(V2, m_GEP(m_Specific(V1), m_BinOp(Iv2Next)))) {
1397+
Start = V1;
1398+
IvNext = cast<Instruction>(V2);
1399+
return true;
1400+
}
1401+
return false;
1402+
};
1403+
1404+
if (!MatchOuterIV(PN.getIncomingValue(0), PN.getIncomingValue(1)) &&
1405+
!MatchOuterIV(PN.getIncomingValue(1), PN.getIncomingValue(0)))
1406+
return nullptr;
1407+
1408+
PHINode *Iv2;
1409+
Value *Iv2Start, *Iv2Step;
1410+
if (!matchSimpleRecurrence(Iv2Next, Iv2, Iv2Start, Iv2Step) ||
1411+
Iv2->getParent() != BB || !match(Iv2Start, m_Zero()))
1412+
return nullptr;
1413+
1414+
Builder.SetInsertPoint(&*BB, BB->getFirstInsertionPt());
1415+
if (Start->getType()->isPtrOrPtrVectorTy()) {
1416+
auto *GEP = cast<GEPOperator>(IvNext);
1417+
return Builder.CreateGEP(GEP->getSourceElementType(), Start, Iv2, "",
1418+
cast<GEPOperator>(IvNext)->isInBounds());
1419+
}
1420+
1421+
auto *OBO = cast<OverflowingBinaryOperator>(IvNext);
1422+
return Builder.CreateAdd(Start, Iv2, "", OBO->hasNoUnsignedWrap(),
1423+
OBO->hasNoSignedWrap());
1424+
}
1425+
13811426
// PHINode simplification
13821427
//
13831428
Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
@@ -1595,5 +1640,8 @@ Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
15951640
if (auto *V = simplifyUsingControlFlow(*this, PN, DT))
15961641
return replaceInstUsesWith(PN, V);
15971642

1643+
if (Value *Res = foldDependentIVs(PN, Builder))
1644+
return replaceInstUsesWith(PN, Res);
1645+
15981646
return nullptr;
15991647
}

llvm/test/Transforms/InstCombine/dependent-ivs.ll

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ define void @int_iv_nuw(i64 %base, i64 %end) {
77
; CHECK-NEXT: entry:
88
; CHECK-NEXT: br label [[LOOP:%.*]]
99
; CHECK: loop:
10-
; CHECK-NEXT: [[IV2:%.*]] = phi i64 [ [[IV2_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
11-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
10+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
11+
; CHECK-NEXT: [[IV2:%.*]] = add nuw i64 [[IV]], [[BASE]]
1212
; CHECK-NEXT: call void @use.i64(i64 [[IV2]])
1313
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
14-
; CHECK-NEXT: [[IV2_NEXT]] = add nuw i64 [[IV_NEXT]], [[BASE]]
1514
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
1615
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
1716
; CHECK: exit:
@@ -39,11 +38,10 @@ define void @int_iv_nsw(i64 %base, i64 %end) {
3938
; CHECK-NEXT: entry:
4039
; CHECK-NEXT: br label [[LOOP:%.*]]
4140
; CHECK: loop:
42-
; CHECK-NEXT: [[IV2:%.*]] = phi i64 [ [[IV2_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
43-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
41+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
42+
; CHECK-NEXT: [[IV2:%.*]] = add nsw i64 [[IV]], [[BASE]]
4443
; CHECK-NEXT: call void @use.i64(i64 [[IV2]])
4544
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
46-
; CHECK-NEXT: [[IV2_NEXT]] = add nsw i64 [[IV_NEXT]], [[BASE]]
4745
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
4846
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
4947
; CHECK: exit:
@@ -72,11 +70,10 @@ define void @int_iv_commuted_add(i64 %base, i64 %end) {
7270
; CHECK-NEXT: [[BASE2:%.*]] = mul i64 [[BASE]], 42
7371
; CHECK-NEXT: br label [[LOOP:%.*]]
7472
; CHECK: loop:
75-
; CHECK-NEXT: [[IV2:%.*]] = phi i64 [ [[IV2_NEXT:%.*]], [[LOOP]] ], [ [[BASE2]], [[ENTRY:%.*]] ]
76-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
73+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
74+
; CHECK-NEXT: [[IV2:%.*]] = add i64 [[BASE2]], [[IV]]
7775
; CHECK-NEXT: call void @use.i64(i64 [[IV2]])
7876
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
79-
; CHECK-NEXT: [[IV2_NEXT]] = add i64 [[BASE2]], [[IV_NEXT]]
8077
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
8178
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
8279
; CHECK: exit:
@@ -105,11 +102,10 @@ define void @int_iv_commuted_phi1(i64 %base, i64 %end) {
105102
; CHECK-NEXT: entry:
106103
; CHECK-NEXT: br label [[LOOP:%.*]]
107104
; CHECK: loop:
108-
; CHECK-NEXT: [[IV2:%.*]] = phi i64 [ [[BASE]], [[ENTRY:%.*]] ], [ [[IV2_NEXT:%.*]], [[LOOP]] ]
109-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
105+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
106+
; CHECK-NEXT: [[IV2:%.*]] = add i64 [[IV]], [[BASE]]
110107
; CHECK-NEXT: call void @use.i64(i64 [[IV2]])
111108
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
112-
; CHECK-NEXT: [[IV2_NEXT]] = add i64 [[IV_NEXT]], [[BASE]]
113109
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
114110
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
115111
; CHECK: exit:
@@ -137,11 +133,10 @@ define void @int_iv_commuted_phi2(i64 %base, i64 %end) {
137133
; CHECK-NEXT: entry:
138134
; CHECK-NEXT: br label [[LOOP:%.*]]
139135
; CHECK: loop:
140-
; CHECK-NEXT: [[IV2:%.*]] = phi i64 [ [[IV2_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
141-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
136+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
137+
; CHECK-NEXT: [[IV2:%.*]] = add i64 [[IV]], [[BASE]]
142138
; CHECK-NEXT: call void @use.i64(i64 [[IV2]])
143139
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
144-
; CHECK-NEXT: [[IV2_NEXT]] = add i64 [[IV_NEXT]], [[BASE]]
145140
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
146141
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
147142
; CHECK: exit:
@@ -169,11 +164,10 @@ define void @int_iv_vector(<2 x i64> %base) {
169164
; CHECK-NEXT: entry:
170165
; CHECK-NEXT: br label [[LOOP:%.*]]
171166
; CHECK: loop:
172-
; CHECK-NEXT: [[IV2:%.*]] = phi <2 x i64> [ [[IV2_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
173-
; CHECK-NEXT: [[IV:%.*]] = phi <2 x i64> [ [[IV_NEXT:%.*]], [[LOOP]] ], [ zeroinitializer, [[ENTRY]] ]
167+
; CHECK-NEXT: [[IV:%.*]] = phi <2 x i64> [ [[IV_NEXT:%.*]], [[LOOP]] ], [ zeroinitializer, [[ENTRY:%.*]] ]
168+
; CHECK-NEXT: [[IV2:%.*]] = add <2 x i64> [[IV]], [[BASE]]
174169
; CHECK-NEXT: call void @use.v2i64(<2 x i64> [[IV2]])
175170
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw <2 x i64> [[IV]], <i64 4, i64 4>
176-
; CHECK-NEXT: [[IV2_NEXT]] = add <2 x i64> [[IV_NEXT]], [[BASE]]
177171
; CHECK-NEXT: [[CMP:%.*]] = call i1 @get.i1()
178172
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
179173
; CHECK: exit:
@@ -201,12 +195,11 @@ define void @int_iv_loop_variant_step(i64 %base, i64 %end) {
201195
; CHECK-NEXT: entry:
202196
; CHECK-NEXT: br label [[LOOP:%.*]]
203197
; CHECK: loop:
204-
; CHECK-NEXT: [[IV2:%.*]] = phi i64 [ [[IV2_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
205-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
198+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
199+
; CHECK-NEXT: [[IV2:%.*]] = add nuw i64 [[IV]], [[BASE]]
206200
; CHECK-NEXT: call void @use.i64(i64 [[IV2]])
207201
; CHECK-NEXT: [[STEP:%.*]] = call i64 @get.i64()
208202
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], [[STEP]]
209-
; CHECK-NEXT: [[IV2_NEXT]] = add nuw i64 [[IV_NEXT]], [[BASE]]
210203
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
211204
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
212205
; CHECK: exit:
@@ -235,11 +228,10 @@ define void @ptr_iv_inbounds(ptr %base, i64 %end) {
235228
; CHECK-NEXT: entry:
236229
; CHECK-NEXT: br label [[LOOP:%.*]]
237230
; CHECK: loop:
238-
; CHECK-NEXT: [[IV_PTR:%.*]] = phi ptr [ [[IV_PTR_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
239-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
231+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
232+
; CHECK-NEXT: [[IV_PTR:%.*]] = getelementptr inbounds i8, ptr [[BASE]], i64 [[IV]]
240233
; CHECK-NEXT: call void @use.p0(ptr [[IV_PTR]])
241234
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
242-
; CHECK-NEXT: [[IV_PTR_NEXT]] = getelementptr inbounds i8, ptr [[BASE]], i64 [[IV_NEXT]]
243235
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
244236
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
245237
; CHECK: exit:
@@ -267,11 +259,10 @@ define void @ptr_iv_no_inbounds(ptr %base, i64 %end) {
267259
; CHECK-NEXT: entry:
268260
; CHECK-NEXT: br label [[LOOP:%.*]]
269261
; CHECK: loop:
270-
; CHECK-NEXT: [[IV_PTR:%.*]] = phi ptr [ [[IV_PTR_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
271-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
262+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
263+
; CHECK-NEXT: [[IV_PTR:%.*]] = getelementptr i8, ptr [[BASE]], i64 [[IV]]
272264
; CHECK-NEXT: call void @use.p0(ptr [[IV_PTR]])
273265
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
274-
; CHECK-NEXT: [[IV_PTR_NEXT]] = getelementptr i8, ptr [[BASE]], i64 [[IV_NEXT]]
275266
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
276267
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
277268
; CHECK: exit:
@@ -299,11 +290,10 @@ define void @ptr_iv_non_i8_type(ptr %base, i64 %end) {
299290
; CHECK-NEXT: entry:
300291
; CHECK-NEXT: br label [[LOOP:%.*]]
301292
; CHECK: loop:
302-
; CHECK-NEXT: [[IV_PTR:%.*]] = phi ptr [ [[IV_PTR_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
303-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
293+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
294+
; CHECK-NEXT: [[IV_PTR:%.*]] = getelementptr i32, ptr [[BASE]], i64 [[IV]]
304295
; CHECK-NEXT: call void @use.p0(ptr [[IV_PTR]])
305296
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
306-
; CHECK-NEXT: [[IV_PTR_NEXT]] = getelementptr i32, ptr [[BASE]], i64 [[IV_NEXT]]
307297
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
308298
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
309299
; CHECK: exit:
@@ -331,11 +321,10 @@ define void @ptr_iv_vector(<2 x ptr> %base, i64 %end) {
331321
; CHECK-NEXT: entry:
332322
; CHECK-NEXT: br label [[LOOP:%.*]]
333323
; CHECK: loop:
334-
; CHECK-NEXT: [[IV_PTR:%.*]] = phi <2 x ptr> [ [[IV_PTR_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
335-
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY]] ]
324+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[ENTRY:%.*]] ]
325+
; CHECK-NEXT: [[IV_PTR:%.*]] = getelementptr inbounds i8, <2 x ptr> [[BASE]], i64 [[IV]]
336326
; CHECK-NEXT: call void @use.v2p0(<2 x ptr> [[IV_PTR]])
337327
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 4
338-
; CHECK-NEXT: [[IV_PTR_NEXT]] = getelementptr inbounds i8, <2 x ptr> [[BASE]], i64 [[IV_NEXT]]
339328
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
340329
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
341330
; CHECK: exit:
@@ -363,11 +352,10 @@ define void @ptr_iv_vector2(<2 x ptr> %base) {
363352
; CHECK-NEXT: entry:
364353
; CHECK-NEXT: br label [[LOOP:%.*]]
365354
; CHECK: loop:
366-
; CHECK-NEXT: [[IV_PTR:%.*]] = phi <2 x ptr> [ [[IV_PTR_NEXT:%.*]], [[LOOP]] ], [ [[BASE]], [[ENTRY:%.*]] ]
367-
; CHECK-NEXT: [[IV:%.*]] = phi <2 x i64> [ [[IV_NEXT:%.*]], [[LOOP]] ], [ zeroinitializer, [[ENTRY]] ]
355+
; CHECK-NEXT: [[IV:%.*]] = phi <2 x i64> [ [[IV_NEXT:%.*]], [[LOOP]] ], [ zeroinitializer, [[ENTRY:%.*]] ]
356+
; CHECK-NEXT: [[IV_PTR:%.*]] = getelementptr i8, <2 x ptr> [[BASE]], <2 x i64> [[IV]]
368357
; CHECK-NEXT: call void @use.v2p0(<2 x ptr> [[IV_PTR]])
369358
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw <2 x i64> [[IV]], <i64 4, i64 4>
370-
; CHECK-NEXT: [[IV_PTR_NEXT]] = getelementptr i8, <2 x ptr> [[BASE]], <2 x i64> [[IV_NEXT]]
371359
; CHECK-NEXT: [[CMP:%.*]] = call i1 @get.i1()
372360
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
373361
; CHECK: exit:

0 commit comments

Comments
 (0)