Skip to content

Commit 4a074f3

Browse files
authored
[InstCombine] Extend Phi-Icmp use to include or (#67682)
In InstCombinePHI currently the only use of PHI as an Icmp is being checked as a requirement to reduce a value if isKnownNonZero. However this can be extended to include or(icmp) . This is always true as OR only adds bits and we are checking against 0.
1 parent 15f179e commit 4a074f3

File tree

2 files changed

+333
-13
lines changed

2 files changed

+333
-13
lines changed

llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,18 +1444,37 @@ Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
14441444
PHIUser->user_back() == &PN) {
14451445
return replaceInstUsesWith(PN, PoisonValue::get(PN.getType()));
14461446
}
1447-
// When a PHI is used only to be compared with zero, it is safe to replace
1448-
// an incoming value proved as known nonzero with any non-zero constant.
1449-
// For example, in the code below, the incoming value %v can be replaced
1450-
// with any non-zero constant based on the fact that the PHI is only used to
1451-
// be compared with zero and %v is a known non-zero value:
1452-
// %v = select %cond, 1, 2
1453-
// %p = phi [%v, BB] ...
1454-
// icmp eq, %p, 0
1455-
auto *CmpInst = dyn_cast<ICmpInst>(PHIUser);
1456-
// FIXME: To be simple, handle only integer type for now.
1457-
if (CmpInst && isa<IntegerType>(PN.getType()) && CmpInst->isEquality() &&
1458-
match(CmpInst->getOperand(1), m_Zero())) {
1447+
}
1448+
1449+
// When a PHI is used only to be compared with zero, it is safe to replace
1450+
// an incoming value proved as known nonzero with any non-zero constant.
1451+
// For example, in the code below, the incoming value %v can be replaced
1452+
// with any non-zero constant based on the fact that the PHI is only used to
1453+
// be compared with zero and %v is a known non-zero value:
1454+
// %v = select %cond, 1, 2
1455+
// %p = phi [%v, BB] ...
1456+
// icmp eq, %p, 0
1457+
// FIXME: To be simple, handle only integer type for now.
1458+
// This handles a small number of uses to keep the complexity down, and an
1459+
// icmp(or(phi)) can equally be replaced with any non-zero constant as the
1460+
// "or" will only add bits.
1461+
if (!PN.hasNUsesOrMore(3)) {
1462+
bool AllUsesOfPhiEndsInCmp = all_of(PN.users(), [&PN](User *U) {
1463+
auto *CmpInst = dyn_cast<ICmpInst>(U);
1464+
if (!CmpInst) {
1465+
// This is always correct as OR only add bits and we are checking
1466+
// against 0.
1467+
if (U->hasOneUse() && match(U, m_c_Or(m_Specific(&PN), m_Value())))
1468+
CmpInst = dyn_cast<ICmpInst>(U->user_back());
1469+
}
1470+
if (!CmpInst || !isa<IntegerType>(PN.getType()) ||
1471+
!CmpInst->isEquality() || !match(CmpInst->getOperand(1), m_Zero())) {
1472+
return false;
1473+
}
1474+
return true;
1475+
});
1476+
// All uses of PHI results in a compare with zero.
1477+
if (AllUsesOfPhiEndsInCmp) {
14591478
ConstantInt *NonZeroConst = nullptr;
14601479
bool MadeChange = false;
14611480
for (unsigned I = 0, E = PN.getNumIncomingValues(); I != E; ++I) {
@@ -1464,7 +1483,6 @@ Instruction *InstCombinerImpl::visitPHINode(PHINode &PN) {
14641483
if (isKnownNonZero(VA, DL, 0, &AC, CtxI, &DT)) {
14651484
if (!NonZeroConst)
14661485
NonZeroConst = getAnyNonZeroConstInt(PN);
1467-
14681486
if (NonZeroConst != VA) {
14691487
replaceOperand(PN, I, NonZeroConst);
14701488
MadeChange = true;

llvm/test/Transforms/InstCombine/phi.ll

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,308 @@ if.end: ; preds = %entry, %if.then
13401340
ret i1 %cmp1
13411341
}
13421342

1343+
define i1 @phi_knownnonzero_eq_oricmp(i32 %n, i32 %s, ptr %P, i32 %val) {
1344+
; CHECK-LABEL: @phi_knownnonzero_eq_oricmp(
1345+
; CHECK-NEXT: entry:
1346+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1347+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1348+
; CHECK: if.then:
1349+
; CHECK-NEXT: br label [[IF_END]]
1350+
; CHECK: if.end:
1351+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1352+
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
1353+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ORPHI]], 0
1354+
; CHECK-NEXT: ret i1 [[CMP1]]
1355+
;
1356+
entry:
1357+
%tobool = icmp slt i32 %n, %s
1358+
br i1 %tobool, label %if.end, label %if.then
1359+
1360+
if.then:
1361+
%load = load i32, ptr %P
1362+
%cmp = icmp eq i32 %n, %load
1363+
%sel = select i1 %cmp, i32 1, i32 2
1364+
br label %if.end
1365+
1366+
if.end:
1367+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1368+
%orphi = or i32 %phi, %val
1369+
%cmp1 = icmp eq i32 %orphi, 0
1370+
ret i1 %cmp1
1371+
}
1372+
1373+
define i1 @phi_knownnonzero_eq_oricmp_commuted(i32 %n, i32 %s, ptr %P, i32 %val) {
1374+
; CHECK-LABEL: @phi_knownnonzero_eq_oricmp_commuted(
1375+
; CHECK-NEXT: entry:
1376+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1377+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1378+
; CHECK: if.then:
1379+
; CHECK-NEXT: br label [[IF_END]]
1380+
; CHECK: if.end:
1381+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1382+
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
1383+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ORPHI]], 0
1384+
; CHECK-NEXT: ret i1 [[CMP1]]
1385+
;
1386+
entry:
1387+
%tobool = icmp slt i32 %n, %s
1388+
br i1 %tobool, label %if.end, label %if.then
1389+
1390+
if.then:
1391+
%load = load i32, ptr %P
1392+
%cmp = icmp eq i32 %n, %load
1393+
%sel = select i1 %cmp, i32 1, i32 2
1394+
br label %if.end
1395+
1396+
if.end:
1397+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1398+
%orphi = or i32 %val, %phi
1399+
%cmp1 = icmp eq i32 %orphi, 0
1400+
ret i1 %cmp1
1401+
}
1402+
1403+
define i1 @phi_knownnonzero_ne_oricmp(i32 %n, i32 %s, ptr %P, i32 %val) {
1404+
; CHECK-LABEL: @phi_knownnonzero_ne_oricmp(
1405+
; CHECK-NEXT: entry:
1406+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1407+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1408+
; CHECK: if.then:
1409+
; CHECK-NEXT: br label [[IF_END]]
1410+
; CHECK: if.end:
1411+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1412+
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
1413+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ORPHI]], 0
1414+
; CHECK-NEXT: ret i1 [[CMP1]]
1415+
;
1416+
entry:
1417+
%tobool = icmp slt i32 %n, %s
1418+
br i1 %tobool, label %if.end, label %if.then
1419+
1420+
if.then:
1421+
%load = load i32, ptr %P
1422+
%cmp = icmp eq i32 %n, %load
1423+
%sel = select i1 %cmp, i32 1, i32 2
1424+
br label %if.end
1425+
1426+
if.end:
1427+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1428+
%orphi = or i32 %phi, %val
1429+
%cmp1 = icmp ne i32 %orphi, 0
1430+
ret i1 %cmp1
1431+
}
1432+
1433+
define i1 @phi_knownnonzero_ne_oricmp_commuted(i32 %n, i32 %s, ptr %P, i32 %val) {
1434+
; CHECK-LABEL: @phi_knownnonzero_ne_oricmp_commuted(
1435+
; CHECK-NEXT: entry:
1436+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1437+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1438+
; CHECK: if.then:
1439+
; CHECK-NEXT: br label [[IF_END]]
1440+
; CHECK: if.end:
1441+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1442+
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
1443+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ORPHI]], 0
1444+
; CHECK-NEXT: ret i1 [[CMP1]]
1445+
;
1446+
entry:
1447+
%tobool = icmp slt i32 %n, %s
1448+
br i1 %tobool, label %if.end, label %if.then
1449+
1450+
if.then:
1451+
%load = load i32, ptr %P
1452+
%cmp = icmp eq i32 %n, %load
1453+
%sel = select i1 %cmp, i32 1, i32 2
1454+
br label %if.end
1455+
1456+
if.end:
1457+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1458+
%orphi = or i32 %val, %phi
1459+
%cmp1 = icmp ne i32 %orphi, 0
1460+
ret i1 %cmp1
1461+
}
1462+
1463+
define i1 @phi_knownnonzero_eq_multiuse_oricmp(i32 %n, i32 %s, ptr %P, i32 %val) {
1464+
; CHECK-LABEL: @phi_knownnonzero_eq_multiuse_oricmp(
1465+
; CHECK-NEXT: entry:
1466+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1467+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1468+
; CHECK: if.then:
1469+
; CHECK-NEXT: br label [[IF_END]]
1470+
; CHECK: if.end:
1471+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1472+
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
1473+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ORPHI]], 0
1474+
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
1475+
; CHECK: next:
1476+
; CHECK-NEXT: [[BOOL2:%.*]] = icmp eq i32 [[PHI]], 0
1477+
; CHECK-NEXT: br label [[CLEANUP]]
1478+
; CHECK: cleanup:
1479+
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
1480+
; CHECK-NEXT: ret i1 [[FINAL]]
1481+
;
1482+
entry:
1483+
%tobool = icmp slt i32 %n, %s
1484+
br i1 %tobool, label %if.end, label %if.then
1485+
1486+
if.then:
1487+
%load = load i32, ptr %P
1488+
%cmp = icmp eq i32 %n, %load
1489+
%sel = select i1 %cmp, i32 1, i32 2
1490+
br label %if.end
1491+
1492+
if.end:
1493+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1494+
%orphi = or i32 %phi, %val
1495+
%cmp1 = icmp eq i32 %orphi, 0
1496+
br i1 %cmp1, label %next, label %cleanup
1497+
1498+
next:
1499+
%bool2 = icmp eq i32 %phi, 0
1500+
br label %cleanup
1501+
1502+
cleanup:
1503+
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
1504+
ret i1 %final
1505+
}
1506+
1507+
define i1 @phi_knownnonzero_ne_multiuse_oricmp_commuted(i32 %n, i32 %s, ptr %P, i32 %val) {
1508+
; CHECK-LABEL: @phi_knownnonzero_ne_multiuse_oricmp_commuted(
1509+
; CHECK-NEXT: entry:
1510+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1511+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1512+
; CHECK: if.then:
1513+
; CHECK-NEXT: br label [[IF_END]]
1514+
; CHECK: if.end:
1515+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 1, [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1516+
; CHECK-NEXT: [[ORPHI:%.*]] = or i32 [[PHI]], [[VAL:%.*]]
1517+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ORPHI]], 0
1518+
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
1519+
; CHECK: next:
1520+
; CHECK-NEXT: [[BOOL2:%.*]] = icmp ne i32 [[PHI]], 0
1521+
; CHECK-NEXT: br label [[CLEANUP]]
1522+
; CHECK: cleanup:
1523+
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
1524+
; CHECK-NEXT: ret i1 [[FINAL]]
1525+
;
1526+
entry:
1527+
%tobool = icmp slt i32 %n, %s
1528+
br i1 %tobool, label %if.end, label %if.then
1529+
1530+
if.then:
1531+
%load = load i32, ptr %P
1532+
%cmp = icmp eq i32 %n, %load
1533+
%sel = select i1 %cmp, i32 1, i32 2
1534+
br label %if.end
1535+
1536+
if.end:
1537+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1538+
%orphi = or i32 %val, %phi
1539+
%cmp1 = icmp ne i32 %orphi, 0
1540+
br i1 %cmp1, label %next, label %cleanup
1541+
1542+
next:
1543+
%bool2 = icmp ne i32 %phi, 0
1544+
br label %cleanup
1545+
1546+
cleanup:
1547+
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
1548+
ret i1 %final
1549+
}
1550+
1551+
define i1 @phi_knownnonzero_eq_multiuse_andicmp(i32 %n, i32 %s, ptr %P, i32 %val) {
1552+
; CHECK-LABEL: @phi_knownnonzero_eq_multiuse_andicmp(
1553+
; CHECK-NEXT: entry:
1554+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1555+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1556+
; CHECK: if.then:
1557+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[P:%.*]], align 4
1558+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LOAD]], [[N]]
1559+
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 2
1560+
; CHECK-NEXT: br label [[IF_END]]
1561+
; CHECK: if.end:
1562+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[SEL]], [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1563+
; CHECK-NEXT: [[ANDPHI:%.*]] = and i32 [[PHI]], [[VAL:%.*]]
1564+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[ANDPHI]], 0
1565+
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
1566+
; CHECK: next:
1567+
; CHECK-NEXT: [[BOOL2:%.*]] = icmp eq i32 [[PHI]], 0
1568+
; CHECK-NEXT: br label [[CLEANUP]]
1569+
; CHECK: cleanup:
1570+
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
1571+
; CHECK-NEXT: ret i1 [[FINAL]]
1572+
;
1573+
entry:
1574+
%tobool = icmp slt i32 %n, %s
1575+
br i1 %tobool, label %if.end, label %if.then
1576+
1577+
if.then:
1578+
%load = load i32, ptr %P
1579+
%cmp = icmp eq i32 %n, %load
1580+
%sel = select i1 %cmp, i32 1, i32 2
1581+
br label %if.end
1582+
1583+
if.end:
1584+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1585+
%andphi = and i32 %phi, %val
1586+
%cmp1 = icmp eq i32 %andphi, 0
1587+
br i1 %cmp1, label %next, label %cleanup
1588+
1589+
next:
1590+
%bool2 = icmp eq i32 %phi, 0
1591+
br label %cleanup
1592+
1593+
cleanup:
1594+
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
1595+
ret i1 %final
1596+
}
1597+
1598+
define i1 @phi_knownnonzero_ne_multiuse_andicmp(i32 %n, i32 %s, ptr %P, i32 %val) {
1599+
; CHECK-LABEL: @phi_knownnonzero_ne_multiuse_andicmp(
1600+
; CHECK-NEXT: entry:
1601+
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp slt i32 [[N:%.*]], [[S:%.*]]
1602+
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
1603+
; CHECK: if.then:
1604+
; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[P:%.*]], align 4
1605+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LOAD]], [[N]]
1606+
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 2
1607+
; CHECK-NEXT: br label [[IF_END]]
1608+
; CHECK: if.end:
1609+
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[SEL]], [[IF_THEN]] ], [ [[N]], [[ENTRY:%.*]] ]
1610+
; CHECK-NEXT: [[ANDPHI:%.*]] = and i32 [[PHI]], [[VAL:%.*]]
1611+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[ANDPHI]], 0
1612+
; CHECK-NEXT: br i1 [[CMP1]], label [[NEXT:%.*]], label [[CLEANUP:%.*]]
1613+
; CHECK: next:
1614+
; CHECK-NEXT: [[BOOL2:%.*]] = icmp ne i32 [[PHI]], 0
1615+
; CHECK-NEXT: br label [[CLEANUP]]
1616+
; CHECK: cleanup:
1617+
; CHECK-NEXT: [[FINAL:%.*]] = phi i1 [ [[CMP1]], [[IF_END]] ], [ [[BOOL2]], [[NEXT]] ]
1618+
; CHECK-NEXT: ret i1 [[FINAL]]
1619+
;
1620+
entry:
1621+
%tobool = icmp slt i32 %n, %s
1622+
br i1 %tobool, label %if.end, label %if.then
1623+
1624+
if.then:
1625+
%load = load i32, ptr %P
1626+
%cmp = icmp eq i32 %n, %load
1627+
%sel = select i1 %cmp, i32 1, i32 2
1628+
br label %if.end
1629+
1630+
if.end:
1631+
%phi = phi i32 [ %sel, %if.then ], [ %n, %entry ]
1632+
%andphi = and i32 %phi, %val
1633+
%cmp1 = icmp ne i32 %andphi, 0
1634+
br i1 %cmp1, label %next, label %cleanup
1635+
1636+
next:
1637+
%bool2 = icmp ne i32 %phi, 0
1638+
br label %cleanup
1639+
1640+
cleanup:
1641+
%final = phi i1 [ %cmp1, %if.end ], [ %bool2, %next ]
1642+
ret i1 %final
1643+
}
1644+
13431645
; This would crash trying to delete an instruction (conv)
13441646
; that still had uses because the user (the phi) was not
13451647
; updated to remove a use from an unreachable block (g.exit).

0 commit comments

Comments
 (0)