[llvm] [InstCombine] Convert logical and/or with `icmp samesign` into bitwise ops (PR #116983)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 20 06:44:07 PST 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/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](https://github.com/dtcxzyw/llvm-opt-benchmark/pull/1724#discussion_r1849663863) to unblock https://github.com/llvm/llvm-project/pull/112742.


>From 3dec6fee0b966db002b3800e29af0cd3bd67f477 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 20 Nov 2024 21:50:39 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 .../Transforms/InstCombine/logical-select.ll  | 121 ++++++++++++++++++
 1 file changed, 121 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/logical-select.ll b/llvm/test/Transforms/InstCombine/logical-select.ll
index 1b6e816d2e624e..b212a92a244f90 100644
--- a/llvm/test/Transforms/InstCombine/logical-select.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select.ll
@@ -1521,3 +1521,124 @@ bb:
   %and2 = or i1 %and1, %cmp
   ret i1 %and2
 }
+
+define i1 @test_logical_and_icmp_samesign(i8 %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i8 [[X]], 11
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %cmp1 = icmp ne i8 %x, 9
+  %cmp2 = icmp samesign ult i8 %x, 11
+  %and = select i1 %cmp1, i1 %cmp2, i1 false
+  ret i1 %and
+}
+
+define i1 @test_logical_or_icmp_samesign(i8 %x) {
+; CHECK-LABEL: @test_logical_or_icmp_samesign(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], -9
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i8 [[X]], -11
+; CHECK-NEXT:    [[OR:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %cmp1 = icmp eq i8 %x, -9
+  %cmp2 = icmp samesign ult i8 %x, -11
+  %or = select i1 %cmp1, i1 true, i1 %cmp2
+  ret i1 %or
+}
+
+define i1 @test_double_logical_and_icmp_samesign1(i1 %cond, i32 %y) {
+; CHECK-LABEL: @test_double_logical_and_icmp_samesign1(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[Y:%.*]], 5
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 false
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i32 [[Y]], 4
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[SEL1]], i1 [[CMP2]], i1 false
+; CHECK-NEXT:    ret i1 [[SEL2]]
+;
+  %cmp1 = icmp ne i32 %y, 5
+  %sel1 = select i1 %cond, i1 %cmp1, i1 false
+  %cmp2 = icmp samesign ult i32 %y, 4
+  %sel2 = select i1 %sel1, i1 %cmp2, i1 false
+  ret i1 %sel2
+}
+
+define i1 @test_double_logical_and_icmp_samesign2(i1 %cond, i32 %y) {
+; CHECK-LABEL: @test_double_logical_and_icmp_samesign2(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp samesign ugt i32 [[Y:%.*]], 65535
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 false
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i32 [[Y]], 1114112
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[SEL1]], i1 [[CMP2]], i1 false
+; CHECK-NEXT:    ret i1 [[SEL2]]
+;
+  %cmp1 = icmp samesign ugt i32 %y, 65535
+  %sel1 = select i1 %cond, i1 %cmp1, i1 false
+  %cmp2 = icmp samesign ult i32 %y, 1114112
+  %sel2 = select i1 %sel1, i1 %cmp2, i1 false
+  ret i1 %sel2
+}
+
+define <2 x i1> @test_logical_and_icmp_samesign_vec(<2 x i8> %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_vec(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9)
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
+; CHECK-NEXT:    [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[AND]]
+;
+  %cmp1 = icmp ne <2 x i8> %x, splat(i8 9)
+  %cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11)
+  %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
+  ret <2 x i1> %and
+}
+
+define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_cond(<2 x i8> %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_cond(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], <i8 9, i8 poison>
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
+; CHECK-NEXT:    [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[AND]]
+;
+  %cmp1 = icmp ne <2 x i8> %x, <i8 9, i8 poison>
+  %cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11)
+  %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
+  ret <2 x i1> %and
+}
+
+define i1 @test_logical_and_icmp_samesign_do_not_imply(i8 %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_do_not_imply(
+; CHECK-NEXT:    [[AND:%.*]] = icmp ult i8 [[X:%.*]], 11
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %cmp1 = icmp ne i8 %x, -9
+  %cmp2 = icmp samesign ult i8 %x, 11
+  %and = select i1 %cmp1, i1 %cmp2, i1 false
+  ret i1 %and
+}
+
+define i1 @test_logical_and_icmp_no_samesign(i8 %x) {
+; CHECK-LABEL: @test_logical_and_icmp_no_samesign(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ult i8 [[X]], 11
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %cmp1 = icmp ne i8 %x, 9
+  %cmp2 = icmp ult i8 %x, 11
+  %and = select i1 %cmp1, i1 %cmp2, i1 false
+  ret i1 %and
+}
+
+; Negative tests
+
+define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_tv(<2 x i8> %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_tv(
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9)
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], <i8 11, i8 poison>
+; CHECK-NEXT:    [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[AND]]
+;
+  %cmp1 = icmp ne <2 x i8> %x, splat(i8 9)
+  %cmp2 = icmp samesign ult <2 x i8> %x, <i8 11, i8 poison>
+  %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
+  ret <2 x i1> %and
+}

>From 25d7b618953ced55283ee27bf43b8b1ffda40fba Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 20 Nov 2024 22:25:17 +0800
Subject: [PATCH 2/2] [InstCombine] Convert logical and/or with icmp samesign
 into bitwise ops

---
 .../InstCombine/InstCombineSelect.cpp         | 41 +++++++++++++++++--
 .../Transforms/InstCombine/logical-select.ll  | 21 ++++------
 2 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 010b77548c152a..6f0ec66420a51d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3115,6 +3115,39 @@ static Instruction *foldNestedSelects(SelectInst &OuterSelVal,
                             !IsAndVariant ? SelInner : InnerSel.FalseVal);
 }
 
+/// Return true if V is poison or \p Expected given that ValAssumedPoison is
+/// already poison. For example, if ValAssumedPoison is `icmp samesign X, 10`
+/// and V is `icmp ne X, 5`, impliesPoisonOrCond returns true.
+static bool impliesPoisonOrCond(const Value *ValAssumedPoison, const Value *V,
+                                bool Expected) {
+  if (impliesPoison(ValAssumedPoison, V))
+    return true;
+
+  // Handle the case that ValAssumedPoison is `icmp samesign pred X, C1` and V
+  // is `icmp pred X, C2`, where C1 is well-defined.
+  if (auto *ICmp = dyn_cast<ICmpInst>(ValAssumedPoison)) {
+    Value *LHS = ICmp->getOperand(0);
+    const APInt *RHSC1;
+    const APInt *RHSC2;
+    ICmpInst::Predicate Pred;
+    if (ICmp->hasSameSign() &&
+        match(ICmp->getOperand(1), m_APIntForbidPoison(RHSC1)) &&
+        match(V, m_ICmp(Pred, m_Specific(LHS), m_APIntAllowPoison(RHSC2)))) {
+      unsigned BitWidth = RHSC1->getBitWidth();
+      ConstantRange CRX =
+          RHSC1->isNonNegative()
+              ? ConstantRange(APInt::getSignedMinValue(BitWidth),
+                              APInt::getZero(BitWidth))
+              : ConstantRange(APInt::getZero(BitWidth),
+                              APInt::getSignedMinValue(BitWidth));
+      return CRX.icmp(Expected ? Pred : ICmpInst::getInversePredicate(Pred),
+                      *RHSC2);
+    }
+  }
+
+  return false;
+}
+
 Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
   Value *CondVal = SI.getCondition();
   Value *TrueVal = SI.getTrueValue();
@@ -3136,13 +3169,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
   // checks whether folding it does not convert a well-defined value into
   // poison.
   if (match(TrueVal, m_One())) {
-    if (impliesPoison(FalseVal, CondVal)) {
+    if (impliesPoisonOrCond(FalseVal, CondVal, /*Expected=*/false)) {
       // Change: A = select B, true, C --> A = or B, C
       return BinaryOperator::CreateOr(CondVal, FalseVal);
     }
 
     if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_One(), m_Value(B)))) &&
-        impliesPoison(FalseVal, B)) {
+        impliesPoisonOrCond(FalseVal, B, /*Expected=*/false)) {
       // (A || B) || C --> A || (B | C)
       return replaceInstUsesWith(
           SI, Builder.CreateLogicalOr(A, Builder.CreateOr(B, FalseVal)));
@@ -3178,13 +3211,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
   }
 
   if (match(FalseVal, m_Zero())) {
-    if (impliesPoison(TrueVal, CondVal)) {
+    if (impliesPoisonOrCond(TrueVal, CondVal, /*Expected=*/true)) {
       // Change: A = select B, C, false --> A = and B, C
       return BinaryOperator::CreateAnd(CondVal, TrueVal);
     }
 
     if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_Value(B), m_Zero()))) &&
-        impliesPoison(TrueVal, B)) {
+        impliesPoisonOrCond(TrueVal, B, /*Expected=*/true)) {
       // (A && B) && C --> A && (B & C)
       return replaceInstUsesWith(
           SI, Builder.CreateLogicalAnd(A, Builder.CreateAnd(B, TrueVal)));
diff --git a/llvm/test/Transforms/InstCombine/logical-select.ll b/llvm/test/Transforms/InstCombine/logical-select.ll
index b212a92a244f90..050a53406a9c59 100644
--- a/llvm/test/Transforms/InstCombine/logical-select.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select.ll
@@ -1526,7 +1526,7 @@ define i1 @test_logical_and_icmp_samesign(i8 %x) {
 ; CHECK-LABEL: @test_logical_and_icmp_samesign(
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i8 [[X]], 11
-; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]]
 ; CHECK-NEXT:    ret i1 [[AND]]
 ;
   %cmp1 = icmp ne i8 %x, 9
@@ -1539,7 +1539,7 @@ define i1 @test_logical_or_icmp_samesign(i8 %x) {
 ; CHECK-LABEL: @test_logical_or_icmp_samesign(
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], -9
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i8 [[X]], -11
-; CHECK-NEXT:    [[OR:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]]
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP1]], [[CMP2]]
 ; CHECK-NEXT:    ret i1 [[OR]]
 ;
   %cmp1 = icmp eq i8 %x, -9
@@ -1550,10 +1550,8 @@ define i1 @test_logical_or_icmp_samesign(i8 %x) {
 
 define i1 @test_double_logical_and_icmp_samesign1(i1 %cond, i32 %y) {
 ; CHECK-LABEL: @test_double_logical_and_icmp_samesign1(
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[Y:%.*]], 5
-; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 false
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i32 [[Y]], 4
-; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[SEL1]], i1 [[CMP2]], i1 false
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i32 [[Y:%.*]], 4
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false
 ; CHECK-NEXT:    ret i1 [[SEL2]]
 ;
   %cmp1 = icmp ne i32 %y, 5
@@ -1565,10 +1563,9 @@ define i1 @test_double_logical_and_icmp_samesign1(i1 %cond, i32 %y) {
 
 define i1 @test_double_logical_and_icmp_samesign2(i1 %cond, i32 %y) {
 ; CHECK-LABEL: @test_double_logical_and_icmp_samesign2(
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp samesign ugt i32 [[Y:%.*]], 65535
-; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND:%.*]], i1 [[CMP1]], i1 false
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult i32 [[Y]], 1114112
-; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[SEL1]], i1 [[CMP2]], i1 false
+; CHECK-NEXT:    [[TMP1:%.*]] = add i32 [[Y:%.*]], -65536
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ult i32 [[TMP1]], 1048576
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false
 ; CHECK-NEXT:    ret i1 [[SEL2]]
 ;
   %cmp1 = icmp samesign ugt i32 %y, 65535
@@ -1582,7 +1579,7 @@ define <2 x i1> @test_logical_and_icmp_samesign_vec(<2 x i8> %x) {
 ; CHECK-LABEL: @test_logical_and_icmp_samesign_vec(
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9)
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
-; CHECK-NEXT:    [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
 ; CHECK-NEXT:    ret <2 x i1> [[AND]]
 ;
   %cmp1 = icmp ne <2 x i8> %x, splat(i8 9)
@@ -1595,7 +1592,7 @@ define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_cond(<2 x i8> %x
 ; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_cond(
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], <i8 9, i8 poison>
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
-; CHECK-NEXT:    [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
 ; CHECK-NEXT:    ret <2 x i1> [[AND]]
 ;
   %cmp1 = icmp ne <2 x i8> %x, <i8 9, i8 poison>



More information about the llvm-commits mailing list