[llvm] [InstCombine] Simplify and/or by replacing operands with constants (PR #77231)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 30 09:06:17 PST 2024


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/77231

>From c37fae17d4e4c90e66a79641cef48886576b4815 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 22 Jan 2024 10:58:27 +0800
Subject: [PATCH 1/3] [InstCombine] Add pre-commit tests for PR76554. NFC.

---
 .../test/Transforms/InstCombine/and-xor-or.ll |  30 +++++
 llvm/test/Transforms/InstCombine/or.ll        | 117 ++++++++++++++++++
 2 files changed, 147 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/and-xor-or.ll b/llvm/test/Transforms/InstCombine/and-xor-or.ll
index 69a7890bee22f..ca0ba9206ffde 100644
--- a/llvm/test/Transforms/InstCombine/and-xor-or.ll
+++ b/llvm/test/Transforms/InstCombine/and-xor-or.ll
@@ -3,6 +3,7 @@
 
 declare void @use(i32)
 declare void @use_i8(i8)
+declare void @use_i1(i1)
 
 ; a & (a ^ b) --> a & ~b
 
@@ -4780,3 +4781,32 @@ define i32 @canonicalize_logic_first_constexpr_nuw(i32 %x) {
   %r = and i32 %a, -10
   ret i32 %r
 }
+
+define i1 @test_and_xor_freely_invertable(i32 %x, i32 %y, i1 %z) {
+; CHECK-LABEL: define {{[^@]+}}@test_and_xor_freely_invertable
+; CHECK-SAME: (i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[Z:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sle i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP]], [[Z]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %cmp = icmp sgt i32 %x, %y
+  %xor = xor i1 %cmp, %z
+  %and = and i1 %xor, %z
+  ret i1 %and
+}
+
+define i1 @test_and_xor_freely_invertable_multiuse(i32 %x, i32 %y, i1 %z) {
+; CHECK-LABEL: define {{[^@]+}}@test_and_xor_freely_invertable_multiuse
+; CHECK-SAME: (i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[Z:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[X]], [[Y]]
+; CHECK-NEXT:    call void @use_i1(i1 [[CMP]])
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[CMP]], true
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[TMP1]], [[Z]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %cmp = icmp sgt i32 %x, %y
+  call void @use_i1(i1 %cmp)
+  %xor = xor i1 %cmp, %z
+  %and = and i1 %xor, %z
+  ret i1 %and
+}
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index 573a11599141a..b710f3afaa42c 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1777,3 +1777,120 @@ 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:    [[A:%.*]] = and i32 [[X:%.*]], -2147483648
+; CHECK-NEXT:    [[B:%.*]] = xor i32 [[A]], -2147483648
+; CHECK-NEXT:    [[C:%.*]] = and i32 [[B]], [[Y:%.*]]
+; CHECK-NEXT:    [[D:%.*]] = or i32 [[C]], [[A]]
+; 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:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[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:    [[XOR:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[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:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[CC]], [[XOR]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[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:    [[XOR:%.*]] = xor i32 [[AA]], [[B:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AA]], [[AND]]
+; 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:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[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
+}

>From c8b3af5f0ed80caa9e471f110d2408f9e5b3a85f Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 22 Jan 2024 11:01:06 +0800
Subject: [PATCH 2/3] [InstCombine] Simplify and/or by replacing operands with
 constants

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 72 ++++++++++++++++---
 .../Transforms/InstCombine/and-or-icmps.ll    | 14 ++--
 .../test/Transforms/InstCombine/and-or-not.ll | 14 +---
 llvm/test/Transforms/InstCombine/or.ll        | 30 ++++----
 4 files changed, 81 insertions(+), 49 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 5fd944a859ef0..f9571819bfc02 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2179,6 +2179,57 @@ foldBitwiseLogicWithIntrinsics(BinaryOperator &I,
   }
 }
 
+// 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.
+// Return the simplified result of X if successful, and nullptr otherwise.
+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 *RHS;
+  if (match(X, m_c_And(m_Specific(Y), m_Value(RHS)))) {
+    return IsAnd ? RHS : Constant::getNullValue(X->getType());
+  } else if (match(X, m_c_Or(m_Specific(Y), m_Value(RHS)))) {
+    return IsAnd ? Constant::getAllOnesValue(X->getType()) : RHS;
+  } else if (match(X, m_c_Xor(m_Specific(Y), m_Value(RHS)))) {
+    if (IsAnd) {
+      if (X->hasOneUse())
+        return IC.Builder.CreateNot(RHS);
+
+      if (Value *NotRHS =
+              IC.getFreelyInverted(RHS, RHS->hasOneUse(), &IC.Builder))
+        return NotRHS;
+    } else
+      return RHS;
+  }
+
+  // FIXME: Is it correct to decompose xor if Y may be undef?
+  Value *Op0, *Op1;
+  if (match(X, m_BitwiseLogic(m_Value(Op0), m_Value(Op1))))
+    return RecursivelyReplaceUses(cast<BinaryOperator>(X)->getOpcode(), 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.
@@ -2505,13 +2556,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);
@@ -2708,6 +2752,11 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
     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;
 }
 
@@ -3423,10 +3472,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))) &&
@@ -3911,6 +3956,11 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
   if (Instruction *Res = foldBitwiseLogicWithIntrinsics(I, Builder))
     return Res;
 
+  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 91ecf24760259..b43a65d5b8e3b 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 ca093eba1b568..2e351c30ea1f7 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 b710f3afaa42c..0450255135f02 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1781,10 +1781,8 @@ if.else:
 ; Tests from PR76554
 define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
 ; CHECK-LABEL: @test_or_and_xor_constant(
-; CHECK-NEXT:    [[A:%.*]] = and i32 [[X:%.*]], -2147483648
-; CHECK-NEXT:    [[B:%.*]] = xor i32 [[A]], -2147483648
-; CHECK-NEXT:    [[C:%.*]] = and i32 [[B]], [[Y:%.*]]
-; CHECK-NEXT:    [[D:%.*]] = or i32 [[C]], [[A]]
+; CHECK-NEXT:    [[A1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[D:%.*]] = and i32 [[A1]], -2147483648
 ; CHECK-NEXT:    ret i32 [[D]]
 ;
   %a = and i32 %x, -2147483648
@@ -1796,9 +1794,8 @@ define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
 
 define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
 ; CHECK-LABEL: @test_or_and_xor(
-; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %xor = xor i32 %a, %b
@@ -1809,9 +1806,8 @@ define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
 
 define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
 ; CHECK-LABEL: @test_or_and_xor_commuted1(
-; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %xor = xor i32 %b, %a
@@ -1823,9 +1819,8 @@ define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
 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:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[CC]], [[XOR]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[CC]], [[B:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %cc = mul i32 %c, %c
@@ -1838,9 +1833,8 @@ define i32 @test_or_and_xor_commuted2(i32 %a, i32 %b, i32 %c) {
 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:    [[XOR:%.*]] = xor i32 [[AA]], [[B:%.*]]
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AA]], [[AND]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AA]], [[TMP1]]
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %aa = mul i32 %a, %a
@@ -1854,8 +1848,8 @@ 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:    [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], [[A]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[B]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[TMP1]], [[A]]
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %xor = xor i32 %a, %b

>From e48497498b2c2ac38101db2a2d722a37f85eed2e Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 31 Jan 2024 00:49:36 +0800
Subject: [PATCH 3/3] fixup! [InstCombine] Simplify and/or by replacing
 operands with constants

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 38 ++++++++-----------
 .../Transforms/InstCombine/and-or-icmps.ll    |  2 +-
 .../test/Transforms/InstCombine/and-xor-or.ll |  2 +-
 llvm/test/Transforms/InstCombine/or-xor.ll    | 26 ++++++-------
 llvm/test/Transforms/InstCombine/or.ll        | 17 ++++++++-
 5 files changed, 45 insertions(+), 40 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index f9571819bfc02..1cfa797be2207 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2188,23 +2188,6 @@ static Value *simplifyAndOrWithOpReplaced(Value *X, Value *Y, bool IsAnd,
   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 *RHS;
   if (match(X, m_c_And(m_Specific(Y), m_Value(RHS)))) {
     return IsAnd ? RHS : Constant::getNullValue(X->getType());
@@ -2222,11 +2205,20 @@ static Value *simplifyAndOrWithOpReplaced(Value *X, Value *Y, bool IsAnd,
       return RHS;
   }
 
-  // FIXME: Is it correct to decompose xor if Y may be undef?
+  // Replace uses of Y in X recursively.
   Value *Op0, *Op1;
-  if (match(X, m_BitwiseLogic(m_Value(Op0), m_Value(Op1))))
-    return RecursivelyReplaceUses(cast<BinaryOperator>(X)->getOpcode(), Op0,
-                                  Op1);
+  if (Depth < 2 && match(X, m_BitwiseLogic(m_Value(Op0), m_Value(Op1)))) {
+    // 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(cast<BinaryOperator>(X)->getOpcode(),
+                                  NewOp0 ? NewOp0 : Op0, NewOp1 ? NewOp1 : Op1);
+  }
   return nullptr;
 }
 
@@ -2753,7 +2745,7 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
     return Res;
 
   if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ true, *this))
-    return BinaryOperator::CreateAnd(Op1, V);
+    return BinaryOperator::CreateAnd(V, Op1);
   if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ true, *this))
     return BinaryOperator::CreateAnd(Op0, V);
 
@@ -3957,7 +3949,7 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     return Res;
 
   if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ false, *this))
-    return BinaryOperator::CreateOr(Op1, V);
+    return BinaryOperator::CreateOr(V, Op1);
   if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ false, *this))
     return BinaryOperator::CreateOr(Op0, V);
 
diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
index b43a65d5b8e3b..c8d348df5f427 100644
--- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
@@ -370,7 +370,7 @@ define void @simplify_before_foldAndOfICmps(ptr %p) {
 ; CHECK-NEXT:    [[B11:%.*]] = zext i1 [[TMP1]] to i16
 ; CHECK-NEXT:    [[C10:%.*]] = icmp ugt i16 [[L7]], [[B11]]
 ; CHECK-NEXT:    [[C7:%.*]] = icmp slt i16 [[L7]], 0
-; CHECK-NEXT:    [[C3:%.*]] = and i1 [[C10]], [[C7]]
+; CHECK-NEXT:    [[C3:%.*]] = and i1 [[C7]], [[C10]]
 ; CHECK-NEXT:    [[TMP2:%.*]] = xor i1 [[C10]], true
 ; CHECK-NEXT:    [[C18:%.*]] = or i1 [[C7]], [[TMP2]]
 ; CHECK-NEXT:    [[TMP3:%.*]] = sext i1 [[C3]] to i64
diff --git a/llvm/test/Transforms/InstCombine/and-xor-or.ll b/llvm/test/Transforms/InstCombine/and-xor-or.ll
index ca0ba9206ffde..d072dc15cbb2c 100644
--- a/llvm/test/Transforms/InstCombine/and-xor-or.ll
+++ b/llvm/test/Transforms/InstCombine/and-xor-or.ll
@@ -113,7 +113,7 @@ define i32 @and_xor_not_common_op_extrause(i32 %a, i32 %b, ptr %dst) {
 ; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], ptr [[DST:%.*]]) {
 ; CHECK-NEXT:    [[B2:%.*]] = xor i32 [[B]], -1
 ; CHECK-NEXT:    store i32 [[B2]], ptr [[DST]], align 4
-; CHECK-NEXT:    [[T4:%.*]] = and i32 [[A]], [[B]]
+; CHECK-NEXT:    [[T4:%.*]] = and i32 [[B]], [[A]]
 ; CHECK-NEXT:    ret i32 [[T4]]
 ;
   %b2 = xor i32 %b, -1
diff --git a/llvm/test/Transforms/InstCombine/or-xor.ll b/llvm/test/Transforms/InstCombine/or-xor.ll
index 361aab6c21e27..443a0898df0d7 100644
--- a/llvm/test/Transforms/InstCombine/or-xor.ll
+++ b/llvm/test/Transforms/InstCombine/or-xor.ll
@@ -156,7 +156,7 @@ define i16 @test5_extra_use_not_xor(i16 %x, i16 %y, ptr %dst_not, ptr %dst_xor)
 
 define i8 @xor_common_op_commute0(i8 %x, i8 %y) {
 ; CHECK-LABEL: @xor_common_op_commute0(
-; CHECK-NEXT:    [[Z:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[Z:%.*]] = or i8 [[Y:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    ret i8 [[Z]]
 ;
   %xor = xor i8 %x, %y
@@ -168,7 +168,7 @@ define i8 @xor_common_op_commute1(i8 %x, i8 %y) {
 ; CHECK-LABEL: @xor_common_op_commute1(
 ; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    call void @use(i8 [[XOR]])
-; CHECK-NEXT:    [[Z:%.*]] = or i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[Z:%.*]] = or i8 [[Y]], [[X]]
 ; CHECK-NEXT:    ret i8 [[Z]]
 ;
   %xor = xor i8 %y, %x
@@ -1059,7 +1059,7 @@ define i8 @or_nand_xor_common_op_commute3_use3(i8 %x, i8 %y, i8 %z) {
 ; (a ^ 4) & (a ^ ~4) -> -1
 define i32 @PR75692_1(i32 %x) {
 ; CHECK-LABEL: @PR75692_1(
-; CHECK-NEXT:  ret i32 -1
+; CHECK-NEXT:    ret i32 -1
 ;
   %t2 = xor i32 %x, 4
   %t3 = xor i32 %x, -5
@@ -1069,11 +1069,11 @@ define i32 @PR75692_1(i32 %x) {
 
 ; (a ^ 4) & (a ^ 3) is not -1
 define i32 @PR75692_2(i32 %x) {
-; CHECK-LABEL: @PR75692_2
-; CHECK-NEXT:  %t2 = xor i32 %x, 4
-; CHECK-NEXT:  %t3 = xor i32 %x, -4
-; CHECK-NEXT:  %t4 = or i32 %t2, %t3
-; CHECK-NEXT:  ret i32 %t4
+; CHECK-LABEL: @PR75692_2(
+; CHECK-NEXT:    [[T2:%.*]] = xor i32 [[X:%.*]], 4
+; CHECK-NEXT:    [[T3:%.*]] = xor i32 [[X]], -4
+; CHECK-NEXT:    [[T4:%.*]] = or i32 [[T2]], [[T3]]
+; CHECK-NEXT:    ret i32 [[T4]]
 ;
   %t2 = xor i32 %x, 4
   %t3 = xor i32 %x, -4
@@ -1083,11 +1083,11 @@ define i32 @PR75692_2(i32 %x) {
 
 ; (a ^ 4) & (b ^ ~4) is not -1, since a != b is possible
 define i32 @PR75692_3(i32 %x, i32 %y) {
-; CHECK-LABEL: @PR75692_3
-; CHECK-NEXT:  %t2 = xor i32 %x, 4
-; CHECK-NEXT:  %t3 = xor i32 %y, -5
-; CHECK-NEXT:  %t4 = or i32 %t2, %t3
-; CHECK-NEXT:  ret i32 %t4
+; CHECK-LABEL: @PR75692_3(
+; CHECK-NEXT:    [[T2:%.*]] = xor i32 [[X:%.*]], 4
+; CHECK-NEXT:    [[T3:%.*]] = xor i32 [[Y:%.*]], -5
+; CHECK-NEXT:    [[T4:%.*]] = or i32 [[T2]], [[T3]]
+; CHECK-NEXT:    ret i32 [[T4]]
 ;
   %t2 = xor i32 %x, 4
   %t3 = xor i32 %y, -5
diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll
index 0450255135f02..2238b6bcc5653 100644
--- a/llvm/test/Transforms/InstCombine/or.ll
+++ b/llvm/test/Transforms/InstCombine/or.ll
@@ -1781,8 +1781,8 @@ if.else:
 ; 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:    [[TMP1:%.*]] = or i32 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[D:%.*]] = and i32 [[TMP1]], -2147483648
 ; CHECK-NEXT:    ret i32 [[D]]
 ;
   %a = and i32 %x, -2147483648
@@ -1888,3 +1888,16 @@ define i32 @test_or_and_xor_multiuse2(i32 %a, i32 %b, i32 %c) {
   %or = or i32 %and, %a
   ret i32 %or
 }
+
+define i32 @test_or_add_xor(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @test_or_add_xor(
+; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[XOR]], [[C:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[ADD]], [[A]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %xor = xor i32 %a, %b
+  %add = add i32 %xor, %c
+  %or = or i32 %add, %a
+  ret i32 %or
+}



More information about the llvm-commits mailing list