Skip to content

Commit a6fefc8

Browse files
authored
[InstCombine] Convert logical and/or with icmp samesign into bitwise ops (#116983)
See the following case: ``` define i1 @test_logical_and_icmp_samesign(i8 %x) { %cmp1 = icmp ne i8 %x, 9 %cmp2 = icmp samesign ult i8 %x, 11 %and = select i1 %cmp1, i1 %cmp2, i1 false ret i1 %and } ``` Currently we cannot convert this logical and into a bitwise and due to the `samesign` flag. But if `%cmp2` evaluates to `poison`, we can infer that `%cmp1` is either `poison` or `true` (`samesign` violation indicates that X is negative). Therefore, `%and` still evaluates to `poison`. This patch converts a logical and into a bitwise and iff TV is poison implies that Cond is either poison or true. Likewise, we convert a logical or into a bitwise or iff FV is poison implies that Cond is either poison or false. Note: 1. This logic is implemented in InstCombine. Not sure whether it is profitable to move it into ValueTracking and call `impliesPoison(TV/FV, Sel)` instead. 2. We only handle the case that `ValAssumedPoison` is `icmp samesign pred X, C1` and `V` is `icmp pred X, C2`. There are no suitable variants for `isImpliedCondition` to pass the fact that X is [non-]negative. Alive2: https://alive2.llvm.org/ce/z/eorFfa Motivation: fix [a major regression](dtcxzyw/llvm-opt-benchmark#1724 (comment)) to unblock #112742.
1 parent 7c07863 commit a6fefc8

File tree

2 files changed

+155
-4
lines changed

2 files changed

+155
-4
lines changed

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3112,6 +3112,39 @@ static Instruction *foldNestedSelects(SelectInst &OuterSelVal,
31123112
!IsAndVariant ? SelInner : InnerSel.FalseVal);
31133113
}
31143114

3115+
/// Return true if V is poison or \p Expected given that ValAssumedPoison is
3116+
/// already poison. For example, if ValAssumedPoison is `icmp samesign X, 10`
3117+
/// and V is `icmp ne X, 5`, impliesPoisonOrCond returns true.
3118+
static bool impliesPoisonOrCond(const Value *ValAssumedPoison, const Value *V,
3119+
bool Expected) {
3120+
if (impliesPoison(ValAssumedPoison, V))
3121+
return true;
3122+
3123+
// Handle the case that ValAssumedPoison is `icmp samesign pred X, C1` and V
3124+
// is `icmp pred X, C2`, where C1 is well-defined.
3125+
if (auto *ICmp = dyn_cast<ICmpInst>(ValAssumedPoison)) {
3126+
Value *LHS = ICmp->getOperand(0);
3127+
const APInt *RHSC1;
3128+
const APInt *RHSC2;
3129+
ICmpInst::Predicate Pred;
3130+
if (ICmp->hasSameSign() &&
3131+
match(ICmp->getOperand(1), m_APIntForbidPoison(RHSC1)) &&
3132+
match(V, m_ICmp(Pred, m_Specific(LHS), m_APIntAllowPoison(RHSC2)))) {
3133+
unsigned BitWidth = RHSC1->getBitWidth();
3134+
ConstantRange CRX =
3135+
RHSC1->isNonNegative()
3136+
? ConstantRange(APInt::getSignedMinValue(BitWidth),
3137+
APInt::getZero(BitWidth))
3138+
: ConstantRange(APInt::getZero(BitWidth),
3139+
APInt::getSignedMinValue(BitWidth));
3140+
return CRX.icmp(Expected ? Pred : ICmpInst::getInversePredicate(Pred),
3141+
*RHSC2);
3142+
}
3143+
}
3144+
3145+
return false;
3146+
}
3147+
31153148
Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
31163149
Value *CondVal = SI.getCondition();
31173150
Value *TrueVal = SI.getTrueValue();
@@ -3133,13 +3166,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
31333166
// checks whether folding it does not convert a well-defined value into
31343167
// poison.
31353168
if (match(TrueVal, m_One())) {
3136-
if (impliesPoison(FalseVal, CondVal)) {
3169+
if (impliesPoisonOrCond(FalseVal, CondVal, /*Expected=*/false)) {
31373170
// Change: A = select B, true, C --> A = or B, C
31383171
return BinaryOperator::CreateOr(CondVal, FalseVal);
31393172
}
31403173

31413174
if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_One(), m_Value(B)))) &&
3142-
impliesPoison(FalseVal, B)) {
3175+
impliesPoisonOrCond(FalseVal, B, /*Expected=*/false)) {
31433176
// (A || B) || C --> A || (B | C)
31443177
return replaceInstUsesWith(
31453178
SI, Builder.CreateLogicalOr(A, Builder.CreateOr(B, FalseVal)));
@@ -3175,13 +3208,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
31753208
}
31763209

31773210
if (match(FalseVal, m_Zero())) {
3178-
if (impliesPoison(TrueVal, CondVal)) {
3211+
if (impliesPoisonOrCond(TrueVal, CondVal, /*Expected=*/true)) {
31793212
// Change: A = select B, C, false --> A = and B, C
31803213
return BinaryOperator::CreateAnd(CondVal, TrueVal);
31813214
}
31823215

31833216
if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_Value(B), m_Zero()))) &&
3184-
impliesPoison(TrueVal, B)) {
3217+
impliesPoisonOrCond(TrueVal, B, /*Expected=*/true)) {
31853218
// (A && B) && C --> A && (B & C)
31863219
return replaceInstUsesWith(
31873220
SI, Builder.CreateLogicalAnd(A, Builder.CreateAnd(B, TrueVal)));

llvm/test/Transforms/InstCombine/logical-select.ll

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,3 +1521,121 @@ bb:
15211521
%and2 = or i1 %and1, %cmp
15221522
ret i1 %and2
15231523
}
1524+
1525+
define i1 @test_logical_and_icmp_samesign(i8 %x) {
1526+
; CHECK-LABEL: @test_logical_and_icmp_samesign(
1527+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9
1528+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i8 [[X]], 11
1529+
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]]
1530+
; CHECK-NEXT: ret i1 [[AND]]
1531+
;
1532+
%cmp1 = icmp ne i8 %x, 9
1533+
%cmp2 = icmp samesign ult i8 %x, 11
1534+
%and = select i1 %cmp1, i1 %cmp2, i1 false
1535+
ret i1 %and
1536+
}
1537+
1538+
define i1 @test_logical_or_icmp_samesign(i8 %x) {
1539+
; CHECK-LABEL: @test_logical_or_icmp_samesign(
1540+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], -9
1541+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i8 [[X]], -11
1542+
; CHECK-NEXT: [[OR:%.*]] = or i1 [[CMP1]], [[CMP2]]
1543+
; CHECK-NEXT: ret i1 [[OR]]
1544+
;
1545+
%cmp1 = icmp eq i8 %x, -9
1546+
%cmp2 = icmp samesign ult i8 %x, -11
1547+
%or = select i1 %cmp1, i1 true, i1 %cmp2
1548+
ret i1 %or
1549+
}
1550+
1551+
define i1 @test_double_logical_and_icmp_samesign1(i1 %cond, i32 %y) {
1552+
; CHECK-LABEL: @test_double_logical_and_icmp_samesign1(
1553+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i32 [[Y:%.*]], 4
1554+
; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false
1555+
; CHECK-NEXT: ret i1 [[SEL2]]
1556+
;
1557+
%cmp1 = icmp ne i32 %y, 5
1558+
%sel1 = select i1 %cond, i1 %cmp1, i1 false
1559+
%cmp2 = icmp samesign ult i32 %y, 4
1560+
%sel2 = select i1 %sel1, i1 %cmp2, i1 false
1561+
ret i1 %sel2
1562+
}
1563+
1564+
define i1 @test_double_logical_and_icmp_samesign2(i1 %cond, i32 %y) {
1565+
; CHECK-LABEL: @test_double_logical_and_icmp_samesign2(
1566+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[Y:%.*]], -65536
1567+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[TMP1]], 1048576
1568+
; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false
1569+
; CHECK-NEXT: ret i1 [[SEL2]]
1570+
;
1571+
%cmp1 = icmp samesign ugt i32 %y, 65535
1572+
%sel1 = select i1 %cond, i1 %cmp1, i1 false
1573+
%cmp2 = icmp samesign ult i32 %y, 1114112
1574+
%sel2 = select i1 %sel1, i1 %cmp2, i1 false
1575+
ret i1 %sel2
1576+
}
1577+
1578+
define <2 x i1> @test_logical_and_icmp_samesign_vec(<2 x i8> %x) {
1579+
; CHECK-LABEL: @test_logical_and_icmp_samesign_vec(
1580+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9)
1581+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
1582+
; CHECK-NEXT: [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
1583+
; CHECK-NEXT: ret <2 x i1> [[AND]]
1584+
;
1585+
%cmp1 = icmp ne <2 x i8> %x, splat(i8 9)
1586+
%cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11)
1587+
%and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
1588+
ret <2 x i1> %and
1589+
}
1590+
1591+
define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_cond(<2 x i8> %x) {
1592+
; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_cond(
1593+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], <i8 9, i8 poison>
1594+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
1595+
; CHECK-NEXT: [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
1596+
; CHECK-NEXT: ret <2 x i1> [[AND]]
1597+
;
1598+
%cmp1 = icmp ne <2 x i8> %x, <i8 9, i8 poison>
1599+
%cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11)
1600+
%and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
1601+
ret <2 x i1> %and
1602+
}
1603+
1604+
define i1 @test_logical_and_icmp_samesign_do_not_imply(i8 %x) {
1605+
; CHECK-LABEL: @test_logical_and_icmp_samesign_do_not_imply(
1606+
; CHECK-NEXT: [[AND:%.*]] = icmp ult i8 [[X:%.*]], 11
1607+
; CHECK-NEXT: ret i1 [[AND]]
1608+
;
1609+
%cmp1 = icmp ne i8 %x, -9
1610+
%cmp2 = icmp samesign ult i8 %x, 11
1611+
%and = select i1 %cmp1, i1 %cmp2, i1 false
1612+
ret i1 %and
1613+
}
1614+
1615+
define i1 @test_logical_and_icmp_no_samesign(i8 %x) {
1616+
; CHECK-LABEL: @test_logical_and_icmp_no_samesign(
1617+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9
1618+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 [[X]], 11
1619+
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]]
1620+
; CHECK-NEXT: ret i1 [[AND]]
1621+
;
1622+
%cmp1 = icmp ne i8 %x, 9
1623+
%cmp2 = icmp ult i8 %x, 11
1624+
%and = select i1 %cmp1, i1 %cmp2, i1 false
1625+
ret i1 %and
1626+
}
1627+
1628+
; Negative tests
1629+
1630+
define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_tv(<2 x i8> %x) {
1631+
; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_tv(
1632+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9)
1633+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], <i8 11, i8 poison>
1634+
; CHECK-NEXT: [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer
1635+
; CHECK-NEXT: ret <2 x i1> [[AND]]
1636+
;
1637+
%cmp1 = icmp ne <2 x i8> %x, splat(i8 9)
1638+
%cmp2 = icmp samesign ult <2 x i8> %x, <i8 11, i8 poison>
1639+
%and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
1640+
ret <2 x i1> %and
1641+
}

0 commit comments

Comments
 (0)