Skip to content

[InstCombine] Reduce nested logical operator if poison is implied #86823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 10, 2024

Conversation

XChy
Copy link
Member

@XChy XChy commented Mar 27, 2024

Fixes #76623
Alive2 proof: https://alive2.llvm.org/ce/z/gX6znJ (I'm not sure how to write a proof for such transform, maybe there are mistakes)

In most cases, icmp(a, C1) && (other_cond && icmp(a, C2)) will be reduced to icmp(a, C1) & (other_cond && icmp(a, C2)), since latter icmp always implies the poison of the former. After reduction, it's easier to simplify the icmp chain.
Similarly, this patch does the same thing for (A && B) && C --> A && (B & C). Maybe we could constraint such reduction only on icmps if there is regression in benchmarks.

@XChy XChy requested review from dtcxzyw and goldsteinn March 27, 2024 16:10
@XChy XChy requested a review from nikic as a code owner March 27, 2024 16:10
@llvmbot
Copy link
Member

llvmbot commented Mar 27, 2024

@llvm/pr-subscribers-llvm-transforms

Author: XChy (XChy)

Changes

Fixes #76623
Alive2 proof: https://alive2.llvm.org/ce/z/gX6znJ (I'm not sure how to write a proof for such transform, maybe there are mistakes)

In most cases, icmp(a, C1) && (other_cond && icmp(a, C2)) will be reduced to icmp(a, C1) && (other_cond && icmp(a, C2)), since latter icmp always implies the poison of the former. After reduction, it's easier to simplify the icmp chain.
Similarly, we do the same thing for (A && B) && C --> A && (B & C). Maybe we could constraint such reduction only on icmps if there is regression in benchmarks.


Full diff: https://github.com/llvm/llvm-project/pull/86823.diff

3 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+16)
  • (modified) llvm/test/Transforms/InstCombine/and-or-icmps.ll (+60)
  • (modified) llvm/test/Transforms/InstCombine/logical-select.ll (+165)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 9ab2bd8f70aa15..211c116f5a347d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2985,6 +2985,14 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
       return BinaryOperator::CreateOr(CondVal, FalseVal);
     }
 
+    if (match(CondVal, m_LogicalOr(m_Value(A), m_Value(B))) &&
+        isa<SelectInst>(CondVal) && impliesPoison(FalseVal, B)) {
+      // (A || B) || C --> A || (B | C)
+      return replaceInstUsesWith(
+          SI, Builder.CreateLogicalOp(Instruction::Or, A,
+                                      Builder.CreateOr(B, FalseVal)));
+    }
+
     if (auto *LHS = dyn_cast<FCmpInst>(CondVal))
       if (auto *RHS = dyn_cast<FCmpInst>(FalseVal))
         if (Value *V = foldLogicOfFCmps(LHS, RHS, /*IsAnd*/ false,
@@ -3026,6 +3034,14 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
       return BinaryOperator::CreateAnd(CondVal, TrueVal);
     }
 
+    if (match(CondVal, m_LogicalAnd(m_Value(A), m_Value(B))) &&
+        isa<SelectInst>(CondVal) && impliesPoison(TrueVal, B)) {
+      // (A && B) && C --> A && (B & C)
+      return replaceInstUsesWith(
+          SI, Builder.CreateLogicalOp(Instruction::And, A,
+                                      Builder.CreateAnd(B, TrueVal)));
+    }
+
     if (auto *LHS = dyn_cast<FCmpInst>(CondVal))
       if (auto *RHS = dyn_cast<FCmpInst>(TrueVal))
         if (Value *V = foldLogicOfFCmps(LHS, RHS, /*IsAnd*/ true,
diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
index c8d348df5f427c..63b11d0c0bc086 100644
--- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
@@ -3038,3 +3038,63 @@ define i32 @icmp_slt_0_or_icmp_add_1_sge_100_i32_fail(i32 %x) {
   %D = or i32 %C, %B
   ret i32 %D
 }
+
+define i1 @logical_and_icmps1(i32 %a, i1 %other_cond) {
+; CHECK-LABEL: @logical_and_icmps1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ult i32 [[A:%.*]], 10086
+; CHECK-NEXT:    [[RET2:%.*]] = select i1 [[RET1:%.*]], i1 [[CMP3]], i1 false
+; CHECK-NEXT:    ret i1 [[RET2]]
+;
+entry:
+  %cmp1 = icmp sgt i32 %a, -1
+  %logical_and = select i1 %other_cond, i1 %cmp1, i1 false
+  %cmp2 = icmp slt i32 %a, 10086
+  %ret = select i1 %logical_and, i1 %cmp2, i1 false
+  ret i1 %ret
+}
+
+define i1 @logical_and_icmps2(i32 %a, i1 %other_cond) {
+; CHECK-LABEL: @logical_and_icmps2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cmp1 = icmp slt i32 %a, -1
+  %logical_and = select i1 %other_cond, i1 %cmp1, i1 false
+  %cmp2 = icmp eq i32 %a, 10086
+  %ret = select i1 %logical_and, i1 %cmp2, i1 false
+  ret i1 %ret
+}
+
+define <4 x i1> @logical_and_icmps_vec1(<4 x i32> %a, <4 x i1> %other_cond) {
+; CHECK-LABEL: @logical_and_icmps_vec1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ult <4 x i32> [[A:%.*]], <i32 10086, i32 10086, i32 10086, i32 10086>
+; CHECK-NEXT:    [[RET2:%.*]] = select <4 x i1> [[RET1:%.*]], <4 x i1> [[CMP3]], <4 x i1> zeroinitializer
+; CHECK-NEXT:    ret <4 x i1> [[RET2]]
+;
+entry:
+  %cmp1 = icmp sgt <4 x i32> %a, <i32 -1, i32 -1, i32 -1, i32 -1 >
+  %logical_and = select <4 x i1> %other_cond, <4 x i1> %cmp1, <4 x i1> zeroinitializer
+  %cmp2 = icmp slt <4 x i32> %a, <i32  10086, i32 10086, i32 10086, i32 10086 >
+  %ret = select <4 x i1> %logical_and, <4 x i1> %cmp2, <4 x i1> zeroinitializer
+  ret <4 x i1> %ret
+}
+
+define i1 @logical_and_icmps_fail1(i32 %a, i32 %b, i1 %other_cond) {
+; CHECK-LABEL: @logical_and_icmps_fail1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[A:%.*]], -1
+; CHECK-NEXT:    [[LOGICAL_AND:%.*]] = select i1 [[OTHER_COND:%.*]], i1 [[CMP1]], i1 false
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp slt i32 [[A]], [[B:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[LOGICAL_AND]], i1 [[CMP2]], i1 false
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+entry:
+  %cmp1 = icmp sgt i32 %a, -1
+  %logical_and = select i1 %other_cond, i1 %cmp1, i1 false
+  %cmp2 = icmp slt i32 %a, %b
+  %ret = select i1 %logical_and, i1 %cmp2, i1 false
+  ret i1 %ret
+}
diff --git a/llvm/test/Transforms/InstCombine/logical-select.ll b/llvm/test/Transforms/InstCombine/logical-select.ll
index f0ea09c0884744..834773544a5546 100644
--- a/llvm/test/Transforms/InstCombine/logical-select.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select.ll
@@ -1303,3 +1303,168 @@ define i1 @logical_or_and_with_common_not_op_variant5(i1 %a) {
   %or = select i1 %a, i1 true, i1 %and
   ret i1 %or
 }
+
+define i1 @reduce_logical_and1(i1 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @reduce_logical_and1(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], [[B]]
+; CHECK-NEXT:    [[TMP0:%.*]] = and i1 [[CMP1]], [[CMP]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[A:%.*]], i1 [[TMP0]], i1 false
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, %b
+  %and1 = select i1 %a, i1 %cmp1, i1 false
+  %and2 = select i1 %and1, i1 %cmp, i1 false
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_and2(i1 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: @reduce_logical_and2(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[TMP0:%.*]] = xor i1 [[C:%.*]], true
+; CHECK-NEXT:    [[B:%.*]] = and i1 [[TMP0]], [[B1:%.*]]
+; CHECK-NEXT:    [[AND3:%.*]] = select i1 [[AND2:%.*]], i1 [[B]], i1 false
+; CHECK-NEXT:    ret i1 [[AND3]]
+;
+bb:
+  %or = xor i1 %c, %b
+  %and1 = select i1 %a, i1 %or, i1 false
+  %and2 = select i1 %and1, i1 %b, i1 false
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_and3(i1 %a, i32 %b, i32 noundef %c) {
+; CHECK-LABEL: @reduce_logical_and3(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], [[B]]
+; CHECK-NEXT:    [[TMP0:%.*]] = and i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[A:%.*]], i1 [[TMP0]], i1 false
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, %b
+  %and1 = select i1 %a, i1 %cmp, i1 false
+  %and2 = select i1 %and1, i1 %cmp1, i1 false
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_or1(i1 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @reduce_logical_or1(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], [[B]]
+; CHECK-NEXT:    [[TMP0:%.*]] = or i1 [[CMP1]], [[CMP]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[TMP0]]
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, %b
+  %and1 = select i1 %a, i1 true, i1 %cmp1
+  %and2 = select i1 %and1, i1 true, i1 %cmp
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_or2(i1 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: @reduce_logical_or2(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[B:%.*]] = or i1 [[C:%.*]], [[B1:%.*]]
+; CHECK-NEXT:    [[AND3:%.*]] = select i1 [[AND2:%.*]], i1 true, i1 [[B]]
+; CHECK-NEXT:    ret i1 [[AND3]]
+;
+bb:
+  %or = xor i1 %c, %b
+  %and1 = select i1 %a, i1 true, i1 %or
+  %and2 = select i1 %and1, i1 true, i1 %b
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_or3(i1 %a, i32 %b, i32 noundef %c) {
+; CHECK-LABEL: @reduce_logical_or3(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], [[B]]
+; CHECK-NEXT:    [[TMP0:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[TMP0]]
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, %b
+  %and1 = select i1 %a, i1 true, i1 %cmp
+  %and2 = select i1 %and1, i1 true, i1 %cmp1
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_and_fail1(i1 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @reduce_logical_and_fail1(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], [[B]]
+; CHECK-NEXT:    [[AND1:%.*]] = select i1 [[A:%.*]], i1 [[CMP]], i1 false
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[AND1]], i1 [[CMP1]], i1 false
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, %b
+  %and1 = select i1 %a, i1 %cmp, i1 false
+  %and2 = select i1 %and1, i1 %cmp1, i1 false
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_and_fail2(i1 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @reduce_logical_and_fail2(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], 7
+; CHECK-NEXT:    [[AND1:%.*]] = select i1 [[A:%.*]], i1 [[CMP]], i1 false
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[AND1]], i1 [[CMP1]], i1 false
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, 7
+  %and1 = select i1 %a, i1 %cmp, i1 false
+  %and2 = select i1 %and1, i1 %cmp1, i1 false
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_or_fail1(i1 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @reduce_logical_or_fail1(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], [[B]]
+; CHECK-NEXT:    [[AND1:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[CMP]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[AND1]], i1 true, i1 [[CMP1]]
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, %b
+  %and1 = select i1 %a, i1 true, i1 %cmp
+  %and2 = select i1 %and1, i1 true, i1 %cmp1
+  ret i1 %and2
+}
+
+define i1 @reduce_logical_or_fail2(i1 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @reduce_logical_or_fail2(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[B:%.*]], 6
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sgt i32 [[C:%.*]], 7
+; CHECK-NEXT:    [[AND1:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[CMP]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[AND1]], i1 true, i1 [[CMP1]]
+; CHECK-NEXT:    ret i1 [[AND2]]
+;
+bb:
+  %cmp = icmp slt i32 %b, 6
+  %cmp1 = icmp sgt i32 %c, 7
+  %and1 = select i1 %a, i1 true, i1 %cmp
+  %and2 = select i1 %and1, i1 true, i1 %cmp1
+  ret i1 %and2
+}

@XChy XChy force-pushed the reassociate-logical-and branch from 8a8c008 to 9579807 Compare March 27, 2024 17:02
dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Mar 27, 2024
@@ -2985,6 +2985,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
return BinaryOperator::CreateOr(CondVal, FalseVal);
}

if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_One(), m_Value(B)))) &&
impliesPoison(FalseVal, B)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check the compile time impact?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikic could you test the compile time impact of this patch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gently ping. @nikic

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. There is some small compile-time impact, but I think this is an important fold to have.

@XChy XChy merged commit 313a33b into llvm:main Apr 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[InstCombine] Missed optimization: fold other_cond && (a s> -1) && a s< b to other_cond && a u< b when b is a specific constant
4 participants