[llvm] 308ca34 - [InstCombine] Fold `(X | C2) ^ C1 --> (X & ~C2) ^ (C1^C2)`

Roman Lebedev via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 2 14:13:55 PDT 2022


Author: Roman Lebedev
Date: 2022-04-03T00:12:56+03:00
New Revision: 308ca349cbc5fa891b08c525084fc86017bdc498

URL: https://github.com/llvm/llvm-project/commit/308ca349cbc5fa891b08c525084fc86017bdc498
DIFF: https://github.com/llvm/llvm-project/commit/308ca349cbc5fa891b08c525084fc86017bdc498.diff

LOG: [InstCombine] Fold `(X | C2) ^ C1 --> (X & ~C2) ^ (C1^C2)`

These two are equivalent,
and i *think* the `and` form is more-ish canonical.

General proof: https://alive2.llvm.org/ce/z/RrF5s6

If constant on the (outer) `xor` is an `undef`,
the whole lane is dead: https://alive2.llvm.org/ce/z/mu4Sh2

However, if the constant on the (inner) `or` is an `undef`,
we must sanitize it first: https://alive2.llvm.org/ce/z/MHYJL7
I guess, producing a zero `and`-mask is optimal in that case.

alive-tv is happy about the entirety of `xor-of-or.ll`.

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
    llvm/test/Transforms/InstCombine/and.ll
    llvm/test/Transforms/InstCombine/apint-and.ll
    llvm/test/Transforms/InstCombine/demorgan.ll
    llvm/test/Transforms/InstCombine/or-xor.ll
    llvm/test/Transforms/InstCombine/xor-of-or.ll
    llvm/test/Transforms/InstCombine/xor.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index d5f7a1fe813c1..f68787a1535fa 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3566,8 +3566,20 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
   Value *X, *Y;
   Constant *C1;
   if (match(Op1, m_Constant(C1))) {
-    // Use DeMorgan and reassociation to eliminate a 'not' op.
     Constant *C2;
+
+    if (match(Op0, m_OneUse(m_Or(m_Value(X), m_ImmConstant(C2)))) &&
+        match(C1, m_ImmConstant())) {
+      // (X | C2) ^ C1 --> (X & ~C2) ^ (C1^C2)
+      C2 = Constant::replaceUndefsWith(
+          C2, Constant::getAllOnesValue(C2->getType()->getScalarType()));
+      Value *And = Builder.CreateAnd(
+          X, Constant::mergeUndefsWith(ConstantExpr::getNot(C2), C1));
+      return BinaryOperator::CreateXor(
+          And, Constant::mergeUndefsWith(ConstantExpr::getXor(C1, C2), C1));
+    }
+
+    // Use DeMorgan and reassociation to eliminate a 'not' op.
     if (match(Op0, m_OneUse(m_Or(m_Not(m_Value(X)), m_Constant(C2))))) {
       // (~X | C2) ^ C1 --> ((X & ~C2) ^ -1) ^ C1 --> (X & ~C2) ^ ~C1
       Value *And = Builder.CreateAnd(X, ConstantExpr::getNot(C2));

diff  --git a/llvm/test/Transforms/InstCombine/and.ll b/llvm/test/Transforms/InstCombine/and.ll
index b249115de99e7..6c6556afdf5dd 100644
--- a/llvm/test/Transforms/InstCombine/and.ll
+++ b/llvm/test/Transforms/InstCombine/and.ll
@@ -133,8 +133,8 @@ define i32 @test10(i32 %A) {
 
 define i32 @test11(i32 %A, i32* %P) {
 ; CHECK-LABEL: @test11(
-; CHECK-NEXT:    [[B:%.*]] = or i32 [[A:%.*]], 3
-; CHECK-NEXT:    [[C:%.*]] = xor i32 [[B]], 12
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[A:%.*]], -4
+; CHECK-NEXT:    [[C:%.*]] = xor i32 [[TMP1]], 15
 ; CHECK-NEXT:    store i32 [[C]], i32* [[P:%.*]], align 4
 ; CHECK-NEXT:    ret i32 3
 ;

diff  --git a/llvm/test/Transforms/InstCombine/apint-and.ll b/llvm/test/Transforms/InstCombine/apint-and.ll
index 35bf90434406b..793b25e548280 100644
--- a/llvm/test/Transforms/InstCombine/apint-and.ll
+++ b/llvm/test/Transforms/InstCombine/apint-and.ll
@@ -42,8 +42,8 @@ define i1 @test4(i37 %x) {
 
 define i7 @test5(i7 %A, i7* %P) {
 ; CHECK-LABEL: @test5(
-; CHECK-NEXT:    [[B:%.*]] = or i7 [[A:%.*]], 3
-; CHECK-NEXT:    [[C:%.*]] = xor i7 [[B]], 12
+; CHECK-NEXT:    [[TMP1:%.*]] = and i7 [[A:%.*]], -4
+; CHECK-NEXT:    [[C:%.*]] = xor i7 [[TMP1]], 15
 ; CHECK-NEXT:    store i7 [[C]], i7* [[P:%.*]], align 1
 ; CHECK-NEXT:    ret i7 3
 ;
@@ -103,8 +103,8 @@ define i1 @test11(i737 %x) {
 
 define i117 @test12(i117 %A, i117* %P) {
 ; CHECK-LABEL: @test12(
-; CHECK-NEXT:    [[B:%.*]] = or i117 [[A:%.*]], 3
-; CHECK-NEXT:    [[C:%.*]] = xor i117 [[B]], 12
+; CHECK-NEXT:    [[TMP1:%.*]] = and i117 [[A:%.*]], -4
+; CHECK-NEXT:    [[C:%.*]] = xor i117 [[TMP1]], 15
 ; CHECK-NEXT:    store i117 [[C]], i117* [[P:%.*]], align 4
 ; CHECK-NEXT:    ret i117 3
 ;

diff  --git a/llvm/test/Transforms/InstCombine/demorgan.ll b/llvm/test/Transforms/InstCombine/demorgan.ll
index 9ef17bb70c367..bbc24754e9b70 100644
--- a/llvm/test/Transforms/InstCombine/demorgan.ll
+++ b/llvm/test/Transforms/InstCombine/demorgan.ll
@@ -384,8 +384,8 @@ define i32 @demorganize_constant1(i32 %a) {
 
 define i32 @demorganize_constant2(i32 %a) {
 ; CHECK-LABEL: @demorganize_constant2(
-; CHECK-NEXT:    [[AND:%.*]] = or i32 [[A:%.*]], 15
-; CHECK-NEXT:    [[AND1:%.*]] = xor i32 [[AND]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[A:%.*]], -16
+; CHECK-NEXT:    [[AND1:%.*]] = xor i32 [[TMP1]], -16
 ; CHECK-NEXT:    ret i32 [[AND1]]
 ;
   %and = or i32 %a, 15

diff  --git a/llvm/test/Transforms/InstCombine/or-xor.ll b/llvm/test/Transforms/InstCombine/or-xor.ll
index 0784c0742a8f9..79c2141a81796 100644
--- a/llvm/test/Transforms/InstCombine/or-xor.ll
+++ b/llvm/test/Transforms/InstCombine/or-xor.ll
@@ -433,8 +433,8 @@ define i8 @not_or(i8 %x) {
 
 define i8 @not_or_xor(i8 %x) {
 ; CHECK-LABEL: @not_or_xor(
-; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X:%.*]], -8
-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[TMP1]], -13
+; CHECK-NEXT:    [[NOTX:%.*]] = and i8 [[X:%.*]], -8
+; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[NOTX]], -13
 ; CHECK-NEXT:    ret i8 [[XOR]]
 ;
   %notx = xor i8 %x, -1
@@ -445,8 +445,8 @@ define i8 @not_or_xor(i8 %x) {
 
 define i8 @xor_or(i8 %x) {
 ; CHECK-LABEL: @xor_or(
-; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[X:%.*]], 7
-; CHECK-NEXT:    [[OR:%.*]] = xor i8 [[TMP1]], 32
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X:%.*]], -8
+; CHECK-NEXT:    [[OR:%.*]] = xor i8 [[TMP1]], 39
 ; CHECK-NEXT:    ret i8 [[OR]]
 ;
   %xor = xor i8 %x, 32
@@ -456,8 +456,8 @@ define i8 @xor_or(i8 %x) {
 
 define i8 @xor_or2(i8 %x) {
 ; CHECK-LABEL: @xor_or2(
-; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[X:%.*]], 7
-; CHECK-NEXT:    [[OR:%.*]] = xor i8 [[TMP1]], 32
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X:%.*]], -8
+; CHECK-NEXT:    [[OR:%.*]] = xor i8 [[TMP1]], 39
 ; CHECK-NEXT:    ret i8 [[OR]]
 ;
   %xor = xor i8 %x, 33
@@ -467,8 +467,8 @@ define i8 @xor_or2(i8 %x) {
 
 define i8 @xor_or_xor(i8 %x) {
 ; CHECK-LABEL: @xor_or_xor(
-; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[X:%.*]], 7
-; CHECK-NEXT:    [[XOR2:%.*]] = xor i8 [[TMP1]], 44
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X:%.*]], -8
+; CHECK-NEXT:    [[XOR2:%.*]] = xor i8 [[TMP1]], 43
 ; CHECK-NEXT:    ret i8 [[XOR2]]
 ;
   %xor1 = xor i8 %x, 33
@@ -479,8 +479,8 @@ define i8 @xor_or_xor(i8 %x) {
 
 define i8 @or_xor_or(i8 %x) {
 ; CHECK-LABEL: @or_xor_or(
-; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[X:%.*]], 39
-; CHECK-NEXT:    [[OR2:%.*]] = xor i8 [[TMP1]], 8
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X:%.*]], -40
+; CHECK-NEXT:    [[OR2:%.*]] = xor i8 [[TMP1]], 47
 ; CHECK-NEXT:    ret i8 [[OR2]]
 ;
   %or1 = or i8 %x, 33

diff  --git a/llvm/test/Transforms/InstCombine/xor-of-or.ll b/llvm/test/Transforms/InstCombine/xor-of-or.ll
index bfbe250c66c20..71690d2eae7e9 100644
--- a/llvm/test/Transforms/InstCombine/xor-of-or.ll
+++ b/llvm/test/Transforms/InstCombine/xor-of-or.ll
@@ -16,8 +16,8 @@ define i4 @t0(i4 %x, i4 %y, i4 %z) {
 ; If the second operands are immediate constants, we can perform the fold.
 define i4 @t1(i4 %x) {
 ; CHECK-LABEL: @t1(
-; CHECK-NEXT:    [[I0:%.*]] = or i4 [[X:%.*]], -4
-; CHECK-NEXT:    [[I1:%.*]] = xor i4 [[I0]], -6
+; CHECK-NEXT:    [[TMP1:%.*]] = and i4 [[X:%.*]], 3
+; CHECK-NEXT:    [[I1:%.*]] = xor i4 [[TMP1]], 6
 ; CHECK-NEXT:    ret i4 [[I1]]
 ;
   %i0 = or i4 %x, 12   ; 0b1100
@@ -42,8 +42,8 @@ define i4 @t2(i4 %x) {
 ; Splat constants are fine too.
 define <2 x i4> @t3(<2 x i4> %x) {
 ; CHECK-LABEL: @t3(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 -4>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 -6>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 3>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 6>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 12>
@@ -54,8 +54,8 @@ define <2 x i4> @t3(<2 x i4> %x) {
 ; Non-splat constants are fine too.
 define <2 x i4> @t4(<2 x i4> %x) {
 ; CHECK-LABEL: @t4(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 -6>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 -4>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 5>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 6>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 10>
@@ -66,8 +66,8 @@ define <2 x i4> @t4(<2 x i4> %x) {
 ; Partially-undef constants are fine.
 define <2 x i4> @t5(<2 x i4> %x) {
 ; CHECK-LABEL: @t5(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 -4>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 undef>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 undef>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 undef>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 12>
@@ -76,8 +76,8 @@ define <2 x i4> @t5(<2 x i4> %x) {
 }
 define <2 x i4> @t6(<2 x i4> %x) {
 ; CHECK-LABEL: @t6(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 undef>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 -6>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 0>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 5>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 undef>
@@ -86,8 +86,8 @@ define <2 x i4> @t6(<2 x i4> %x) {
 }
 define <2 x i4> @t7(<2 x i4> %x) {
 ; CHECK-LABEL: @t7(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 undef>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 undef>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 undef>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 undef>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 undef>
@@ -98,8 +98,8 @@ define <2 x i4> @t7(<2 x i4> %x) {
 ; Partially-poison constants are fine.
 define <2 x i4> @t8(<2 x i4> %x) {
 ; CHECK-LABEL: @t8(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 -4>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 poison>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 undef>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 poison>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 12>
@@ -108,8 +108,8 @@ define <2 x i4> @t8(<2 x i4> %x) {
 }
 define <2 x i4> @t9(<2 x i4> %x) {
 ; CHECK-LABEL: @t9(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 poison>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 -6>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 0>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 5>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 poison>
@@ -118,8 +118,8 @@ define <2 x i4> @t9(<2 x i4> %x) {
 }
 define <2 x i4> @t10(<2 x i4> %x) {
 ; CHECK-LABEL: @t10(
-; CHECK-NEXT:    [[I0:%.*]] = or <2 x i4> [[X:%.*]], <i4 -4, i4 poison>
-; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[I0]], <i4 -6, i4 poison>
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i4> [[X:%.*]], <i4 3, i4 undef>
+; CHECK-NEXT:    [[I1:%.*]] = xor <2 x i4> [[TMP1]], <i4 6, i4 poison>
 ; CHECK-NEXT:    ret <2 x i4> [[I1]]
 ;
   %i0 = or <2 x i4> %x, <i4 12, i4 poison>

diff  --git a/llvm/test/Transforms/InstCombine/xor.ll b/llvm/test/Transforms/InstCombine/xor.ll
index d06460ab4374a..ec61b372d5cc3 100644
--- a/llvm/test/Transforms/InstCombine/xor.ll
+++ b/llvm/test/Transforms/InstCombine/xor.ll
@@ -623,8 +623,8 @@ define i8 @xor_and_not(i8 %x, i8* %p) {
 ; CHECK-LABEL: @xor_and_not(
 ; CHECK-NEXT:    [[NX:%.*]] = xor i8 [[X:%.*]], -1
 ; CHECK-NEXT:    store i8 [[NX]], i8* [[P:%.*]], align 1
-; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[X]], -43
-; CHECK-NEXT:    [[R:%.*]] = xor i8 [[TMP1]], -32
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X]], 42
+; CHECK-NEXT:    [[R:%.*]] = xor i8 [[TMP1]], 53
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %nx = xor i8 %x, -1


        


More information about the llvm-commits mailing list