Skip to content

Commit 4715789

Browse files
[InstCombinePHI] Enhance PHI CSE to remove redundant phis
Enhanced PHI CSE to eliminate redundant PHIs, which could clean up the IR and open up opportunities for other passes such as loop vectorization.
1 parent 7b10e97 commit 4715789

File tree

2 files changed

+145
-5
lines changed

2 files changed

+145
-5
lines changed

llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,11 +1621,90 @@ Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
16211621
// Note that even though we've just canonicalized this PHI, due to the
16221622
// worklist visitation order, there are no guarantess that *every* PHI
16231623
// has been canonicalized, so we can't just compare operands ranges.
1624-
if (!PN.isIdenticalToWhenDefined(&IdenticalPN))
1625-
continue;
1626-
// Just use that PHI instead then.
1627-
++NumPHICSEs;
1628-
return replaceInstUsesWith(PN, &IdenticalPN);
1624+
if (PN.isIdenticalToWhenDefined(&IdenticalPN)) {
1625+
// Just use that PHI instead then.
1626+
++NumPHICSEs;
1627+
return replaceInstUsesWith(PN, &IdenticalPN);
1628+
}
1629+
1630+
// Look for the following pattern and do PHI CSE to clean up the
1631+
// redundant %phi. Here %phi, %1 and %phi.next perform the same
1632+
// functionality as %identicalPhi and hence %phi can be eliminated.
1633+
//
1634+
// BB1:
1635+
// %identicalPhi = phi [ X, %BB0 ], [ %identicalPhi.next, %BB1 ]
1636+
// %phi = phi [ X, %BB0 ], [ %phi.next, %BB1 ]
1637+
// ...
1638+
// %identicalPhi.next = select %cmp, %val, %identicalPhi
1639+
// %1 = select %cmp2, %identicalPhi, float %phi
1640+
// %phi.next = select %cmp, %val, %1
1641+
//
1642+
// Prove that %phi and %identicalPhi are the same by induction:
1643+
//
1644+
// Base case: Both %phi and %identicalPhi are equal on entry to the loop.
1645+
// Inductive case:
1646+
// Suppose %phi and %identicalPhi are equal at iteration i.
1647+
// We look at their values at iteration i+1 which are %phi.next and
1648+
// %identicalPhi.next. They would have become different only when %cmp is
1649+
// false and the corresponding values %1 and %identicalPhi differ.
1650+
//
1651+
// The only condition when %1 and %identicalPh could differ is when %cmp2
1652+
// is false and %1 is %phi, which contradicts our inductive hypothesis
1653+
// that %phi and %identicalPhi are equal. Thus %phi and %identicalPhi are
1654+
// always equal at iteration i+1.
1655+
1656+
if (PN.getNumIncomingValues() == 2 && PN.getNumUses() == 1) {
1657+
unsigned diffVals = 0;
1658+
unsigned diffValIdx = 0;
1659+
// Check that only the backedge incoming value is different.
1660+
for (unsigned i = 0; i < 2; i++) {
1661+
if (PN.getIncomingValue(i) != IdenticalPN.getIncomingValue(i)) {
1662+
diffVals++;
1663+
diffValIdx = i;
1664+
}
1665+
}
1666+
BasicBlock *CurBB = PN.getParent();
1667+
if (diffVals == 2 || PN.getIncomingBlock(diffValIdx) != CurBB)
1668+
continue;
1669+
// Now check that the backedge incoming values are two select
1670+
// instructions that are in the same BB, and have the same condition,
1671+
// true value.
1672+
auto *Val = PN.getIncomingValue(diffValIdx);
1673+
auto *IdenticalVal = IdenticalPN.getIncomingValue(diffValIdx);
1674+
if (!isa<SelectInst>(Val) || !isa<SelectInst>(IdenticalVal))
1675+
continue;
1676+
1677+
auto *SI = cast<SelectInst>(Val);
1678+
auto *IdenticalSI = cast<SelectInst>(IdenticalVal);
1679+
if (SI->getParent() != CurBB || IdenticalSI->getParent() != CurBB)
1680+
continue;
1681+
if (SI->getCondition() != IdenticalSI->getCondition() ||
1682+
SI->getTrueValue() != IdenticalSI->getTrueValue())
1683+
continue;
1684+
1685+
// Now check that the false values, i.e., %1 and %identicalPhi,
1686+
// are essentially the same value within the same BB.
1687+
auto SameSelAndPhi = [&](SelectInst *SI, PHINode *IdenticalPN,
1688+
PHINode *PN) {
1689+
if (SI->getTrueValue() == IdenticalPN) {
1690+
return SI->getFalseValue() == PN;
1691+
}
1692+
return false;
1693+
};
1694+
auto *FalseVal = SI->getFalseValue();
1695+
auto *IdenticalSIFalseVal =
1696+
dyn_cast<PHINode>(IdenticalSI->getFalseValue());
1697+
if (!isa<SelectInst>(FalseVal) || !IdenticalSIFalseVal ||
1698+
IdenticalSIFalseVal != &IdenticalPN)
1699+
continue;
1700+
auto *FalseValSI = cast<SelectInst>(FalseVal);
1701+
if (FalseValSI->getParent() != CurBB ||
1702+
!SameSelAndPhi(FalseValSI, &IdenticalPN, &PN))
1703+
continue;
1704+
1705+
++NumPHICSEs;
1706+
return replaceInstUsesWith(PN, &IdenticalPN);
1707+
}
16291708
}
16301709

16311710
// If this is an integer PHI and we know that it has an illegal type, see if
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt < %s -S -passes=instcombine | FileCheck %s
3+
@A = extern_weak global float, align 4
4+
5+
; %phi.to.remove acts the same as %v1, and can be eliminated with PHI CSE.
6+
define void @enhanced_phi_cse(ptr %m, ptr %n, i32 %count) {
7+
; CHECK-LABEL: @enhanced_phi_cse(
8+
; CHECK-NEXT: entry:
9+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
10+
; CHECK: for.body:
11+
; CHECK-NEXT: [[V0:%.*]] = phi float [ 0x4415AF1D80000000, [[ENTRY:%.*]] ], [ [[V0_1:%.*]], [[FOR_BODY]] ]
12+
; CHECK-NEXT: [[V1:%.*]] = phi float [ 0xC415AF1D80000000, [[ENTRY]] ], [ [[V1_1:%.*]], [[FOR_BODY]] ]
13+
; CHECK-NEXT: [[I:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[INC_I:%.*]], [[FOR_BODY]] ]
14+
; CHECK-NEXT: [[Q:%.*]] = phi ptr [ [[M:%.*]], [[ENTRY]] ], [ [[Q_NEXT:%.*]], [[FOR_BODY]] ]
15+
; CHECK-NEXT: [[C:%.*]] = phi ptr [ [[N:%.*]], [[ENTRY]] ], [ [[C_NEXT:%.*]], [[FOR_BODY]] ]
16+
; CHECK-NEXT: [[Q_LOAD:%.*]] = load float, ptr [[Q]], align 4
17+
; CHECK-NEXT: [[C_LOAD:%.*]] = load float, ptr [[C]], align 4
18+
; CHECK-NEXT: [[SUB:%.*]] = fsub float [[Q_LOAD]], [[C_LOAD]]
19+
; CHECK-NEXT: [[CMP1:%.*]] = fcmp olt float [[SUB]], [[V0]]
20+
; CHECK-NEXT: [[V0_1]] = select i1 [[CMP1]], float [[SUB]], float [[V0]]
21+
; CHECK-NEXT: [[CMP2:%.*]] = fcmp ogt float [[SUB]], [[V1]]
22+
; CHECK-NEXT: [[V1_1]] = select i1 [[CMP2]], float [[SUB]], float [[V1]]
23+
; CHECK-NEXT: [[INC_I]] = add nuw nsw i32 [[I]], 1
24+
; CHECK-NEXT: [[Q_NEXT]] = getelementptr inbounds nuw i8, ptr [[Q]], i64 4
25+
; CHECK-NEXT: [[C_NEXT]] = getelementptr inbounds nuw i8, ptr [[C]], i64 4
26+
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[INC_I]], [[COUNT:%.*]]
27+
; CHECK-NEXT: br i1 [[EXITCOND]], label [[EXIT:%.*]], label [[FOR_BODY]]
28+
; CHECK: exit:
29+
; CHECK-NEXT: store float [[V1_1]], ptr @A, align 4
30+
; CHECK-NEXT: ret void
31+
;
32+
entry:
33+
br label %for.body
34+
35+
for.body: ; preds = %entry, %for.body
36+
%v0 = phi float [ 0x4415AF1D80000000, %entry ], [ %v0.1, %for.body ]
37+
%v1 = phi float [ 0xC415AF1D80000000, %entry ], [ %v1.1, %for.body ]
38+
%phi.to.remove = phi float [ 0xC415AF1D80000000, %entry ], [ %phi.to.remove.next, %for.body ]
39+
%i = phi i32 [ 0, %entry ], [ %inc.i, %for.body ]
40+
%q = phi ptr [ %m, %entry ], [ %q.next, %for.body ]
41+
%c = phi ptr [ %n, %entry ], [ %c.next, %for.body ]
42+
%q.load = load float, ptr %q
43+
%c.load = load float, ptr %c
44+
%sub = fsub float %q.load, %c.load
45+
%cmp1 = fcmp olt float %sub, %v0
46+
%v0.1 = select i1 %cmp1, float %sub, float %v0
47+
%same.as.v1 = select i1 %cmp1, float %v1, float %phi.to.remove
48+
%cmp2 = fcmp ogt float %sub, %same.as.v1
49+
%v1.1 = select i1 %cmp2, float %sub, float %v1
50+
%phi.to.remove.next = select i1 %cmp2, float %sub, float %same.as.v1
51+
%inc.i = add nuw nsw i32 %i, 1
52+
%q.next = getelementptr inbounds i8, ptr %q, i64 4
53+
%c.next = getelementptr inbounds i8, ptr %c, i64 4
54+
%exitcond = icmp eq i32 %inc.i, %count
55+
br i1 %exitcond, label %exit, label %for.body
56+
57+
exit:
58+
%vl.1.lcssa = phi float [ %v1.1, %for.body ]
59+
store float %vl.1.lcssa, ptr @A
60+
ret void
61+
}

0 commit comments

Comments
 (0)