Skip to content
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

[InstCombine] Simplify and/or by replacing operands with constants #77231

Merged
merged 3 commits into from
Jan 31, 2024

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Jan 7, 2024

This patch tries to simplify X | Y by replacing occurrences of Y in X with 0. Similarly, it tries to simplify X & Y by replacing occurrences of Y in X with -1.
See also #76572 (review).
Alive2: https://alive2.llvm.org/ce/z/cNjDTR
Note:

  1. I am not sure that it is correct to decompose (A ^ B) | Y into ((A | Y) ^ (B | Y)) | Y if Y may be an undef (See the alive2 proof).
  2. As the current implementation is too conservative in the one-use checks, I cannot remove other existing hard-coded simplifications if they involves more than two instructions (e.g, A & ~(A ^ B) --> A & B).

Compile-time impact: http://llvm-compile-time-tracker.com/compare.php?from=a085402ef54379758e6c996dbaedfcb92ad222b5&to=9d655c6685865ffce0ad336fed81228f3071bd03&stat=instructions%3Au

stage1-O3 stage1-ReleaseThinLTO stage1-ReleaseLTO-g stage1-O0-g stage2-O3 stage2-O0-g stage2-clang
+0.01% -0.00% +0.00% -0.02% +0.01% +0.02% -0.01%

Fixes #76554.
It is an alternative to #76572.

@llvmbot
Copy link
Collaborator

llvmbot commented Jan 7, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

Changes

This patch tries to simplify X | Y by replacing occurrences of Y in X with 0. Similarly, it tries to simplify X & Y by replacing occurrences of Y in X with -1.
See also #76572 (review).
Alive2: https://alive2.llvm.org/ce/z/cNjDTR
Note:

  1. I am not sure that it is correct to decompose (A ^ B) | Y into ((A | Y) ^ (B | Y)) | Y if Y may be an undef (See the alive2 proof).
  2. As the current implementation is too conservative in the one-use checks, I cannot remove other existing hard-coded simplifications if they involves more than two instructions (e.g, A & ~(A ^ B) --> A & B).

Fixes #76554.
It is an alternative to #76572.


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

4 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+65-11)
  • (modified) llvm/test/Transforms/InstCombine/and-or-icmps.ll (+5-9)
  • (modified) llvm/test/Transforms/InstCombine/and-or-not.ll (+3-11)
  • (modified) llvm/test/Transforms/InstCombine/or.ll (+111)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index c03f50d75814d8..dc350b29bc20b5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2159,6 +2159,61 @@ Instruction *InstCombinerImpl::foldBinOpOfDisplacedShifts(BinaryOperator &I) {
   return BinaryOperator::Create(ShiftOp, NewC, ShAmt);
 }
 
+// Try to simplify X | Y by replacing occurrences of Y in X with 0.
+// Similarly, simplify X & Y by replacing occurrences of Y in X with -1.
+static Value *simplifyAndOrWithOpReplaced(Value *X, Value *Y, bool IsAnd,
+                                          InstCombinerImpl &IC,
+                                          unsigned Depth = 0) {
+  if (isa<Constant>(X) || X == Y)
+    return nullptr;
+
+  auto RecursivelyReplaceUses = [&](Instruction::BinaryOps Opcode, Value *Op0,
+                                    Value *Op1) -> Value * {
+    if (Depth == 2)
+      return nullptr;
+
+    // TODO: Relax the one-use constraint to clean up existing hard-coded
+    // simplifications.
+    if (!X->hasOneUse())
+      return nullptr;
+    Value *NewOp0 = simplifyAndOrWithOpReplaced(Op0, Y, IsAnd, IC, Depth + 1);
+    Value *NewOp1 = simplifyAndOrWithOpReplaced(Op1, Y, IsAnd, IC, Depth + 1);
+    if (!NewOp0 && !NewOp1)
+      return nullptr;
+    return IC.Builder.CreateBinOp(Opcode, NewOp0 ? NewOp0 : Op0,
+                                  NewOp1 ? NewOp1 : Op1);
+  };
+
+  Value *Op0, *Op1;
+  if (match(X, m_And(m_Value(Op0), m_Value(Op1)))) {
+    if (Op0 == Y || Op1 == Y)
+      return IsAnd ? (Op0 == Y ? Op1 : Op0)
+                   : Constant::getNullValue(X->getType());
+    return RecursivelyReplaceUses(Instruction::And, Op0, Op1);
+  } else if (match(X, m_Or(m_Value(Op0), m_Value(Op1)))) {
+    if (Op0 == Y || Op1 == Y)
+      return IsAnd ? Constant::getAllOnesValue(X->getType())
+                   : (Op0 == Y ? Op1 : Op0);
+    return RecursivelyReplaceUses(Instruction::Or, Op0, Op1);
+  } else if (match(X, m_Xor(m_Value(Op0), m_Value(Op1)))) {
+    if (Op0 == Y || Op1 == Y) {
+      Value *V = Op0 == Y ? Op1 : Op0;
+      if (IsAnd) {
+        if (Value *NotV =
+                simplifyXorInst(V, Constant::getAllOnesValue(V->getType()),
+                                IC.getSimplifyQuery()))
+          return NotV;
+        if (X->hasOneUse())
+          return IC.Builder.CreateNot(V);
+      } else
+        return V;
+    }
+    // FIXME: Is it correct to decompose xor if Y may be undef?
+    return RecursivelyReplaceUses(Instruction::Xor, Op0, Op1);
+  }
+  return nullptr;
+}
+
 // FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
 // here. We should standardize that construct where it is needed or choose some
 // other way to ensure that commutated variants of patterns are not missed.
@@ -2488,13 +2543,6 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
 
   {
     Value *A, *B, *C;
-    // A & (A ^ B) --> A & ~B
-    if (match(Op1, m_OneUse(m_c_Xor(m_Specific(Op0), m_Value(B)))))
-      return BinaryOperator::CreateAnd(Op0, Builder.CreateNot(B));
-    // (A ^ B) & A --> A & ~B
-    if (match(Op0, m_OneUse(m_c_Xor(m_Specific(Op1), m_Value(B)))))
-      return BinaryOperator::CreateAnd(Op1, Builder.CreateNot(B));
-
     // A & ~(A ^ B) --> A & B
     if (match(Op1, m_Not(m_c_Xor(m_Specific(Op0), m_Value(B)))))
       return BinaryOperator::CreateAnd(Op0, B);
@@ -2688,6 +2736,11 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
     return Res;
 
+  if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ true, *this))
+    return BinaryOperator::CreateAnd(Op1, V);
+  if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ true, *this))
+    return BinaryOperator::CreateAnd(Op0, V);
+
   return nullptr;
 }
 
@@ -3399,10 +3452,6 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     return BinaryOperator::CreateMul(X, IncrementY);
   }
 
-  // X | (X ^ Y) --> X | Y (4 commuted patterns)
-  if (match(&I, m_c_Or(m_Value(X), m_c_Xor(m_Deferred(X), m_Value(Y)))))
-    return BinaryOperator::CreateOr(X, Y);
-
   // (A & C) | (B & D)
   Value *A, *B, *C, *D;
   if (match(Op0, m_And(m_Value(A), m_Value(C))) &&
@@ -3884,6 +3933,11 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
       return BinaryOperator::CreateAnd(X, ConstantInt::get(Ty, *C1 | *C2));
   }
 
+  if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ false, *this))
+    return BinaryOperator::CreateOr(Op1, V);
+  if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ false, *this))
+    return BinaryOperator::CreateOr(Op0, V);
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
index 91ecf24760259b..b43a65d5b8e3b2 100644
--- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
@@ -369,16 +369,12 @@ define void @simplify_before_foldAndOfICmps(ptr %p) {
 ; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i16 [[L7]], -1
 ; CHECK-NEXT:    [[B11:%.*]] = zext i1 [[TMP1]] to i16
 ; CHECK-NEXT:    [[C10:%.*]] = icmp ugt i16 [[L7]], [[B11]]
-; CHECK-NEXT:    [[C5:%.*]] = icmp slt i16 [[L7]], 1
 ; CHECK-NEXT:    [[C7:%.*]] = icmp slt i16 [[L7]], 0
-; CHECK-NEXT:    [[B15:%.*]] = xor i1 [[C7]], [[C10]]
-; CHECK-NEXT:    [[C6:%.*]] = xor i1 [[B15]], true
-; CHECK-NEXT:    [[TMP2:%.*]] = and i1 [[C5]], [[C6]]
-; CHECK-NEXT:    [[C3:%.*]] = and i1 [[TMP2]], [[C10]]
-; CHECK-NEXT:    [[TMP3:%.*]] = xor i1 [[C10]], true
-; CHECK-NEXT:    [[C18:%.*]] = or i1 [[C7]], [[TMP3]]
-; CHECK-NEXT:    [[TMP4:%.*]] = sext i1 [[C3]] to i64
-; CHECK-NEXT:    [[G26:%.*]] = getelementptr i1, ptr null, i64 [[TMP4]]
+; CHECK-NEXT:    [[C3:%.*]] = and i1 [[C10]], [[C7]]
+; CHECK-NEXT:    [[TMP2:%.*]] = xor i1 [[C10]], true
+; CHECK-NEXT:    [[C18:%.*]] = or i1 [[C7]], [[TMP2]]
+; CHECK-NEXT:    [[TMP3:%.*]] = sext i1 [[C3]] to i64
+; CHECK-NEXT:    [[G26:%.*]] = getelementptr i1, ptr null, i64 [[TMP3]]
 ; CHECK-NEXT:    store i16 [[L7]], ptr [[P:%.*]], align 2
 ; CHECK-NEXT:    store i1 [[C18]], ptr [[P]], align 1
 ; CHECK-NEXT:    store ptr [[G26]], ptr [[P]], align 8
diff --git a/llvm/test/Transforms/InstCombine/and-or-not.ll b/llvm/test/Transforms/InstCombine/and-or-not.ll
index ca093eba1b5688..2e351c30ea1f7b 100644
--- a/llvm/test/Transforms/InstCombine/and-or-not.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-not.ll
@@ -761,11 +761,7 @@ define i4 @simplify_and_common_op_use1(i4 %x, i4 %y, i4 %z)  {
 define i4 @simplify_and_common_op_use2(i4 %x, i4 %y, i4 %z)  {
 ; CHECK-LABEL: @simplify_and_common_op_use2(
 ; CHECK-NEXT:    call void @use(i4 [[Y:%.*]])
-; CHECK-NEXT:    [[TMP1:%.*]] = or i4 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT:    [[XYZ:%.*]] = or i4 [[TMP1]], [[Y]]
-; CHECK-NEXT:    [[NOT_XYZ:%.*]] = xor i4 [[XYZ]], -1
-; CHECK-NEXT:    [[R:%.*]] = and i4 [[NOT_XYZ]], [[X]]
-; CHECK-NEXT:    ret i4 [[R]]
+; CHECK-NEXT:    ret i4 0
 ;
   %xy = or i4 %y, %x
   call void @use(i4 %y)
@@ -779,12 +775,8 @@ define i4 @simplify_and_common_op_use2(i4 %x, i4 %y, i4 %z)  {
 
 define i4 @simplify_and_common_op_use3(i4 %x, i4 %y, i4 %z)  {
 ; CHECK-LABEL: @simplify_and_common_op_use3(
-; CHECK-NEXT:    [[XY:%.*]] = or i4 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[XYZ:%.*]] = or i4 [[XY]], [[Z:%.*]]
-; CHECK-NEXT:    call void @use(i4 [[Z]])
-; CHECK-NEXT:    [[NOT_XYZ:%.*]] = xor i4 [[XYZ]], -1
-; CHECK-NEXT:    [[R:%.*]] = and i4 [[NOT_XYZ]], [[X]]
-; CHECK-NEXT:    ret i4 [[R]]
+; CHECK-NEXT:    call void @use(i4 [[Z:%.*]])
+; CHECK-NEXT:    ret i4 0
 ;
   %xy = or i4 %x, %y
   %xyz = or i4 %xy, %z
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index 573a11599141a7..0450255135f028 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1777,3 +1777,114 @@ if.then:
 if.else:
   ret i32 0
 }
+
+; Tests from PR76554
+define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
+; CHECK-LABEL: @test_or_and_xor_constant(
+; CHECK-NEXT:    [[A1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[D:%.*]] = and i32 [[A1]], -2147483648
+; CHECK-NEXT:    ret i32 [[D]]
+;
+  %a = and i32 %x, -2147483648
+  %b = xor i32 %a, -2147483648
+  %c = and i32 %b, %y
+  %d = or i32 %c, %a
+  ret i32 %d
+}
+
+define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor(
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %xor = xor i32 %a, %b
+  %and = and i32 %xor, %c
+  %or = or i32 %and, %a
+  ret i32 %or
+}
+
+define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_commuted1(
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %xor = xor i32 %b, %a
+  %and = and i32 %xor, %c
+  %or = or i32 %and, %a
+  ret i32 %or
+}
+
+define i32 @test_or_and_xor_commuted2(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_commuted2(
+; CHECK-NEXT:    [[CC:%.*]] = mul i32 [[C:%.*]], [[C]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[CC]], [[B:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %cc = mul i32 %c, %c
+  %xor = xor i32 %a, %b
+  %and = and i32 %cc, %xor
+  %or = or i32 %and, %a
+  ret i32 %or
+}
+
+define i32 @test_or_and_xor_commuted3(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_commuted3(
+; CHECK-NEXT:    [[AA:%.*]] = mul i32 [[A:%.*]], [[A]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AA]], [[TMP1]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %aa = mul i32 %a, %a
+  %xor = xor i32 %aa, %b
+  %and = and i32 %xor, %c
+  %or = or i32 %aa, %and
+  ret i32 %or
+}
+
+define i32 @test_or_and_xor_multiuse1(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_multiuse1(
+; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    call void @use(i32 [[XOR]])
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %xor = xor i32 %a, %b
+  call void @use(i32 %xor)
+  %and = and i32 %xor, %c
+  %or = or i32 %and, %a
+  ret i32 %or
+}
+
+; Negative tests
+
+define i32 @test_or_and_xor_mismatched_op(i32 %a, i32 %b, i32 %c, i32 %d) {
+; CHECK-LABEL: @test_or_and_xor_mismatched_op(
+; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[D:%.*]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %xor = xor i32 %a, %b
+  %and = and i32 %xor, %c
+  %or = or i32 %and, %d
+  ret i32 %or
+}
+
+define i32 @test_or_and_xor_multiuse2(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_and_xor_multiuse2(
+; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT:    call void @use(i32 [[AND]])
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %xor = xor i32 %a, %b
+  %and = and i32 %xor, %c
+  call void @use(i32 %and)
+  %or = or i32 %and, %a
+  ret i32 %or
+}

@XChy
Copy link
Member

XChy commented Jan 16, 2024

Could you check whether this patch simplifies (or partially) the example like https://alive2.llvm.org/ce/z/7MHnzm? It's a piece of unreduced real-world IR. It seems to be covered by the X & Y case here. If so, I could post a issue for it without reduction.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Jan 16, 2024

Could you check whether this patch simplifies (or partially) the example like https://alive2.llvm.org/ce/z/7MHnzm? It's a piece of unreduced real-world IR. It seems to be covered by the X & Y case here. If so, I could post a issue for it without reduction.

Is it extracted from qemu/virtio.c:qvirtio_set_features?

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Jan 16, 2024

Could you check whether this patch simplifies (or partially) the example like https://alive2.llvm.org/ce/z/7MHnzm? It's a piece of unreduced real-world IR. It seems to be covered by the X & Y case here. If so, I could post a issue for it without reduction.

Optimized result:

define dso_local noundef zeroext i1 @src(i64 noundef %features) local_unnamed_addr {
for.inc.1310:
  %svq_features.1.3 = and i64 %features, -7784628225
  %and.5 = and i64 %features, 8589934592
  %or.5 = xor i64 %and.5, 8589934592
  %spec.select13.4 = or i64 %svq_features.1.3, %or.5
  %spec.select13.5 = or disjoint i64 %spec.select13.4, 4294967296
  %and4.6 = and i64 %features, 17179869184
  %tobool5.not.6 = icmp eq i64 %and4.6, 0
  %and8.6 = and i64 %spec.select13.5, -20669530113
  %svq_features.1.6 = select i1 %tobool5.not.6, i64 %spec.select13.5, i64 %and8.6
  %and4.13 = and i64 %svq_features.1.6, 2199023255552
  %tobool5.not.13 = icmp eq i64 %and4.13, 0
  br i1 %tobool5.not.13, label %for.inc.13, label %if.then11

for.inc.13:                                       ; preds = %for.inc.1310
  %0 = and i64 %features, 16374562816
  %narrow = icmp eq i64 %0, 12884901888
  %and4.7 = and i64 %svq_features.1.6, 34359738368
  %i2 = or disjoint i64 %and4.7, %and4.6
  %i3 = icmp eq i64 %i2, 0
  %1 = and i64 %svq_features.1.6, 2130303778816
  %i8 = icmp eq i64 %1, 0
  %i9 = select i1 %i8, i1 %i3, i1 false
  %narrow31 = and i1 %narrow, %i9
  br i1 %narrow31, label %if.end12, label %if.then11

if.then11:                                        ; preds = %for.inc.13, %for.inc.1310
  %svq_features.2.13 = and i64 %svq_features.1.6, -4367176433665
  tail call void @use(i64 noundef %svq_features.2.13)
  br label %if.end12

if.end12:                                         ; preds = %if.then11, %for.inc.13
  %tobool1022 = phi i1 [ false, %if.then11 ], [ true, %for.inc.13 ]
  ret i1 %tobool1022
}

@dtcxzyw dtcxzyw force-pushed the perf/simplify-and-or-replace branch from 9d655c6 to 4980f1e Compare January 17, 2024 02:57
@XChy
Copy link
Member

XChy commented Jan 17, 2024

Could you check whether this patch simplifies (or partially) the example like https://alive2.llvm.org/ce/z/7MHnzm? It's a piece of unreduced real-world IR. It seems to be covered by the X & Y case here. If so, I could post a issue for it without reduction.

Is it extracted from qemu/virtio.c:qvirtio_set_features?

Thanks! It's extracted from https://github.com/qemu/qemu/blob/7425b6277f12e82952cede1f531bfc689bf77fb1/hw/virtio/vhost-shadow-virtqueue.c#L27. It seems that it has been optimized partially now.

@nikic
Copy link
Contributor

nikic commented Jan 22, 2024

I am not sure that it is correct to decompose (A ^ B) | Y into ((A | Y) ^ (B | Y)) | Y if Y may be an undef (See the alive2 proof).

I don't really understand how this is related to your patch.

As the current implementation is too conservative in the one-use checks, I cannot remove other existing hard-coded simplifications if they involves more than two instructions (e.g, A & ~(A ^ B) --> A & B).

I think there are quite a few patterns you could drop that are currently missing one-use checks. For example

// B | ((B | C) & A) -> B | (A & C)
if (match(Op1, m_c_And(m_c_Or(m_Specific(Op0), m_Value(C)), m_Value(A))))
return BinaryOperator::CreateOr(Op0, Builder.CreateAnd(A, C));
and some of the above fails to check one-use on the and.

But it's fine to do this in separate step, with extra test coverage for multi-use if necessary.

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, thanks!

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp Outdated Show resolved Hide resolved
llvm/test/Transforms/InstCombine/or.ll Show resolved Hide resolved
@dtcxzyw dtcxzyw force-pushed the perf/simplify-and-or-replace branch from ca75191 to e484974 Compare January 30, 2024 17:06
@dtcxzyw dtcxzyw merged commit f2816ff into llvm:main Jan 31, 2024
3 of 4 checks passed
@dtcxzyw dtcxzyw deleted the perf/simplify-and-or-replace branch January 31, 2024 06:30
dtcxzyw added a commit that referenced this pull request Feb 7, 2024
This patch removes some bitwise folds that fail to check the one-use
constraint on the operands.
See also the comments
#77231 (comment).
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.

missed optimization: ((a ^ b) & c) | a -> a | (b & c)
5 participants