[llvm] 10c3df7 - [Instcombine] Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 7 07:52:16 PST 2022


Author: chenglin.bi
Date: 2022-12-07T23:52:07+08:00
New Revision: 10c3df728cb6d445b6ab9e2d62487e4ecf55693b

URL: https://github.com/llvm/llvm-project/commit/10c3df728cb6d445b6ab9e2d62487e4ecf55693b
DIFF: https://github.com/llvm/llvm-project/commit/10c3df728cb6d445b6ab9e2d62487e4ecf55693b.diff

LOG: [Instcombine] Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)

~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
https://alive2.llvm.org/ce/z/JHN2p4

Reviewed By: spatel

Differential Revision: https://reviews.llvm.org/D139299

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
    llvm/test/Transforms/InstCombine/xor2.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 032021b5806b5..5fc8fe9acbfda 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3477,14 +3477,8 @@ static Instruction *visitMaskedMerge(BinaryOperator &I,
 //   (~x) ^ y
 // or into
 //   x ^ (~y)
-static Instruction *sinkNotIntoXor(BinaryOperator &I,
+static Instruction *sinkNotIntoXor(BinaryOperator &I, Value *X, Value *Y,
                                    InstCombiner::BuilderTy &Builder) {
-  Value *X, *Y;
-  // FIXME: one-use check is not needed in general, but currently we are unable
-  // to fold 'not' into 'icmp', if that 'icmp' has multiple uses. (D35182)
-  if (!match(&I, m_Not(m_OneUse(m_Xor(m_Value(X), m_Value(Y))))))
-    return nullptr;
-
   // We only want to do the transform if it is free to do.
   if (InstCombiner::isFreeToInvert(X, X->hasOneUse())) {
     // Ok, good.
@@ -3497,6 +3491,41 @@ static Instruction *sinkNotIntoXor(BinaryOperator &I,
   return BinaryOperator::CreateXor(NotX, Y, I.getName() + ".demorgan");
 }
 
+static Instruction *foldNotXor(BinaryOperator &I,
+                               InstCombiner::BuilderTy &Builder) {
+  Value *X, *Y;
+  // FIXME: one-use check is not needed in general, but currently we are unable
+  // to fold 'not' into 'icmp', if that 'icmp' has multiple uses. (D35182)
+  if (!match(&I, m_Not(m_OneUse(m_Xor(m_Value(X), m_Value(Y))))))
+    return nullptr;
+
+  if (Instruction *NewXor = sinkNotIntoXor(I, X, Y, Builder))
+    return NewXor;
+
+  auto hasCommonOperand = [](Value *A, Value *B, Value *C, Value *D) {
+    return A == C || A == D || B == C || B == D;
+  };
+
+  Value *A, *B, *C, *D;
+  // Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
+  // 4 commuted variants
+  if (match(X, m_And(m_Value(A), m_Value(B))) &&
+      match(Y, m_Or(m_Value(C), m_Value(D))) && hasCommonOperand(A, B, C, D)) {
+    Value *NotY = Builder.CreateNot(Y);
+    return BinaryOperator::CreateOr(X, NotY);
+  };
+
+  // Canonicalize ~((A | ?) ^ (A & B)) -> (A & B) | ~(A | ?)
+  // 4 commuted variants
+  if (match(Y, m_And(m_Value(A), m_Value(B))) &&
+      match(X, m_Or(m_Value(C), m_Value(D))) && hasCommonOperand(A, B, C, D)) {
+    Value *NotX = Builder.CreateNot(X);
+    return BinaryOperator::CreateOr(Y, NotX);
+  };
+
+  return nullptr;
+}
+
 /// Canonicalize a shifty way to code absolute value to the more common pattern
 /// that uses negation and select.
 static Instruction *canonicalizeAbs(BinaryOperator &Xor,
@@ -3741,7 +3770,7 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) {
     }
   }
 
-  if (Instruction *NewXor = sinkNotIntoXor(I, Builder))
+  if (Instruction *NewXor = foldNotXor(I, Builder))
     return NewXor;
 
   return nullptr;

diff  --git a/llvm/test/Transforms/InstCombine/xor2.ll b/llvm/test/Transforms/InstCombine/xor2.ll
index 3871e1f1fbe4e..06fd740954b99 100644
--- a/llvm/test/Transforms/InstCombine/xor2.ll
+++ b/llvm/test/Transforms/InstCombine/xor2.ll
@@ -520,14 +520,14 @@ define i8 @test16(i8 %A, i8 %B) {
   ret i8 %res
 }
 
-; TODO: Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
+; Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
 
 define i3 @not_xor_to_or_not1(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @not_xor_to_or_not1(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[NOT]]
 ;
   %or = or i3 %b, %c
@@ -541,8 +541,8 @@ define i3 @not_xor_to_or_not2(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @not_xor_to_or_not2(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[NOT]]
 ;
   %or = or i3 %c, %b
@@ -556,8 +556,8 @@ define i3 @not_xor_to_or_not3(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @not_xor_to_or_not3(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[NOT]]
 ;
   %or = or i3 %c, %b
@@ -571,8 +571,8 @@ define i3 @not_xor_to_or_not4(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @not_xor_to_or_not4(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[NOT]]
 ;
   %or = or i3 %b, %c
@@ -586,8 +586,8 @@ define <3 x i5> @not_xor_to_or_not_vector(<3 x i5> %a, <3 x i5> %b, <3 x i5> %c)
 ; CHECK-LABEL: @not_xor_to_or_not_vector(
 ; CHECK-NEXT:    [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor <3 x i5> [[OR]], [[AND]]
-; CHECK-NEXT:    [[NOT:%.*]] = xor <3 x i5> [[XOR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT:    [[NOT:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret <3 x i5> [[NOT]]
 ;
   %or = or <3 x i5> %b, %c
@@ -601,8 +601,8 @@ define <3 x i5> @not_xor_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3 x
 ; CHECK-LABEL: @not_xor_to_or_not_vector_poison(
 ; CHECK-NEXT:    [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor <3 x i5> [[OR]], [[AND]]
-; CHECK-NEXT:    [[NOT:%.*]] = xor <3 x i5> [[XOR]], <i5 poison, i5 -1, i5 -1>
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT:    [[NOT:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret <3 x i5> [[NOT]]
 ;
   %or = or <3 x i5> %b, %c
@@ -612,6 +612,8 @@ define <3 x i5> @not_xor_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3 x
   ret <3 x i5> %not
 }
 
+; negative test : not one use
+
 define i3 @not_xor_to_or_not_2use(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @not_xor_to_or_not_2use(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
@@ -629,14 +631,14 @@ define i3 @not_xor_to_or_not_2use(i3 %a, i3 %b, i3 %c) {
   ret i3 %not
 }
 
-; TODO: Canonicalize ~(A & B) ^ (A | ?) -> (A & B) | ~(A | ?)
+; Canonicalize ~(A & B) ^ (A | ?) -> (A & B) | ~(A | ?)
 
 define i3 @xor_notand_to_or_not1(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @xor_notand_to_or_not1(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[XOR]]
 ;
   %or = or i3 %b, %c
@@ -650,8 +652,8 @@ define i3 @xor_notand_to_or_not2(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @xor_notand_to_or_not2(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[XOR]]
 ;
   %or = or i3 %c, %b
@@ -665,8 +667,8 @@ define i3 @xor_notand_to_or_not3(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @xor_notand_to_or_not3(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[XOR]]
 ;
   %or = or i3 %c, %b
@@ -680,8 +682,8 @@ define i3 @xor_notand_to_or_not4(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @xor_notand_to_or_not4(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT:    [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i3 [[XOR]]
 ;
   %or = or i3 %b, %c
@@ -695,8 +697,8 @@ define <3 x i5> @xor_notand_to_or_not_vector(<3 x i5> %a, <3 x i5> %b, <3 x i5>
 ; CHECK-LABEL: @xor_notand_to_or_not_vector(
 ; CHECK-NEXT:    [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[TMP1:%.*]] = xor <3 x i5> [[AND]], [[OR]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor <3 x i5> [[TMP1]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT:    [[XOR:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret <3 x i5> [[XOR]]
 ;
   %or = or <3 x i5> %b, %c
@@ -710,8 +712,8 @@ define <3 x i5> @xor_notand_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3
 ; CHECK-LABEL: @xor_notand_to_or_not_vector_poison(
 ; CHECK-NEXT:    [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT:    [[TMP1:%.*]] = xor <3 x i5> [[AND]], [[OR]]
-; CHECK-NEXT:    [[XOR:%.*]] = xor <3 x i5> [[TMP1]], <i5 -1, i5 poison, i5 -1>
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT:    [[XOR:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret <3 x i5> [[XOR]]
 ;
   %or = or <3 x i5> %b, %c
@@ -721,6 +723,8 @@ define <3 x i5> @xor_notand_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3
   ret <3 x i5> %xor
 }
 
+; negative test : not one use
+
 define i3 @xor_notand_to_or_not_2use(i3 %a, i3 %b, i3 %c) {
 ; CHECK-LABEL: @xor_notand_to_or_not_2use(
 ; CHECK-NEXT:    [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]


        


More information about the llvm-commits mailing list