[llvm] [InstCombine] Convert logical and/or with `icmp samesign` into bitwise ops (PR #116983)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 20 06:44:42 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Yingwei Zheng (dtcxzyw)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/116983.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+37-4)
- (modified) llvm/test/Transforms/InstCombine/logical-select.ll (+118)
``````````diff
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 1b6e816d2e624e..050a53406a9c59 100644
--- a/llvm/test/Transforms/InstCombine/logical-select.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select.ll
@@ -1521,3 +1521,121 @@ 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:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; 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:%.*]] = or i1 [[CMP1]], [[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: [[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: [[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
+ %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:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
+; 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:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
+; 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
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/116983
More information about the llvm-commits
mailing list