[llvm] d1a6f92 - [InstCombine] Fold `(~x) | y` --> `~(x & (~y))` iff it is free to do so

Roman Lebedev via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 22 06:24:55 PST 2021


Author: Roman Lebedev
Date: 2021-01-22T17:23:54+03:00
New Revision: d1a6f92fd545726aab0784e2dcfb193ce185c418

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

LOG: [InstCombine] Fold `(~x) | y` --> `~(x & (~y))` iff it is free to do so

Iff we know we can get rid of the inversions in the new pattern,
we can thus get rid of the inversion in the old pattern,
this decreasing instruction count.

Note that we could position this transformation as just hoisting
of the `not` (still, iff y is freely negatible), but the test changes
show a number of regressions, so let's not do that.

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
    llvm/lib/Transforms/InstCombine/InstCombineInternal.h
    llvm/test/Transforms/InstCombine/sink-not-into-another-hand-of-or.ll
    llvm/test/Transforms/SimplifyCFG/merge-cond-stores.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 944e7c4b1325..68c4156af2c4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1984,7 +1984,7 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   }
 
   // (~x) & y  -->  ~(x | (~y))  iff that gets rid of inversions
-  if (sinkNotIntoOtherHandOfAnd(I))
+  if (sinkNotIntoOtherHandOfAndOrOr(I))
     return &I;
 
   return nullptr;
@@ -2867,6 +2867,10 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     }
   }
 
+  // (~x) | y  -->  ~(x & (~y))  iff that gets rid of inversions
+  if (sinkNotIntoOtherHandOfAndOrOr(I))
+    return &I;
+
   return nullptr;
 }
 
@@ -3094,16 +3098,19 @@ static Instruction *sinkNotIntoXor(BinaryOperator &I,
 }
 
 // Transform
-//   z = (~x) & y
+//   z = (~x) &/| y
 // into:
-//   z = ~(x | (~y))
+//   z = ~(x |/& (~y))
 // iff y is free to invert and all uses of z can be freely updated.
-bool InstCombinerImpl::sinkNotIntoOtherHandOfAnd(BinaryOperator &I) {
+bool InstCombinerImpl::sinkNotIntoOtherHandOfAndOrOr(BinaryOperator &I) {
   Instruction::BinaryOps NewOpc;
   switch (I.getOpcode()) {
   case Instruction::And:
     NewOpc = Instruction::Or;
     break;
+  case Instruction::Or:
+    NewOpc = Instruction::And;
+    break;
   default:
     return false;
   };

diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index c56b31bd227b..5e466c5e13e7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -105,7 +105,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   Value *simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1, bool Inverted);
   Instruction *visitAnd(BinaryOperator &I);
   Instruction *visitOr(BinaryOperator &I);
-  bool sinkNotIntoOtherHandOfAnd(BinaryOperator &I);
+  bool sinkNotIntoOtherHandOfAndOrOr(BinaryOperator &I);
   Instruction *visitXor(BinaryOperator &I);
   Instruction *visitShl(BinaryOperator &I);
   Value *reassociateShiftAmtsOfTwoSameDirectionShifts(

diff  --git a/llvm/test/Transforms/InstCombine/sink-not-into-another-hand-of-or.ll b/llvm/test/Transforms/InstCombine/sink-not-into-another-hand-of-or.ll
index 57ef48725ead..d40acc37ca73 100644
--- a/llvm/test/Transforms/InstCombine/sink-not-into-another-hand-of-or.ll
+++ b/llvm/test/Transforms/InstCombine/sink-not-into-another-hand-of-or.ll
@@ -12,10 +12,9 @@ declare void @use1(i1)
 ; Most basic positive test
 define i32 @t0(i1 %i0, i32 %v0, i32 %v1, i32 %v2, i32 %v3) {
 ; CHECK-LABEL: @t0(
-; CHECK-NEXT:    [[I1:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]]
-; CHECK-NEXT:    [[I2:%.*]] = xor i1 [[I0:%.*]], true
-; CHECK-NEXT:    [[I3:%.*]] = or i1 [[I1]], [[I2]]
-; CHECK-NEXT:    [[I4:%.*]] = select i1 [[I3]], i32 [[V2:%.*]], i32 [[V3:%.*]]
+; CHECK-NEXT:    [[I1:%.*]] = icmp ne i32 [[V0:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i1 [[I1]], [[I0:%.*]]
+; CHECK-NEXT:    [[I4:%.*]] = select i1 [[TMP1]], i32 [[V3:%.*]], i32 [[V2:%.*]]
 ; CHECK-NEXT:    ret i32 [[I4]]
 ;
   %i1 = icmp eq i32 %v0, %v1
@@ -27,11 +26,10 @@ define i32 @t0(i1 %i0, i32 %v0, i32 %v1, i32 %v2, i32 %v3) {
 define i32 @t1(i32 %v0, i32 %v1, i32 %v2, i32 %v3, i32 %v4, i32 %v5) {
 ; CHECK-LABEL: @t1(
 ; CHECK-NEXT:    [[I0:%.*]] = icmp eq i32 [[V0:%.*]], [[V1:%.*]]
-; CHECK-NEXT:    [[I1:%.*]] = icmp eq i32 [[V2:%.*]], [[V3:%.*]]
+; CHECK-NEXT:    [[I1:%.*]] = icmp ne i32 [[V2:%.*]], [[V3:%.*]]
 ; CHECK-NEXT:    call void @use1(i1 [[I0]])
-; CHECK-NEXT:    [[I2:%.*]] = xor i1 [[I0]], true
-; CHECK-NEXT:    [[I3:%.*]] = or i1 [[I1]], [[I2]]
-; CHECK-NEXT:    [[I4:%.*]] = select i1 [[I3]], i32 [[V4:%.*]], i32 [[V5:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i1 [[I0]], [[I1]]
+; CHECK-NEXT:    [[I4:%.*]] = select i1 [[TMP1]], i32 [[V5:%.*]], i32 [[V4:%.*]]
 ; CHECK-NEXT:    ret i32 [[I4]]
 ;
   %i0 = icmp eq i32 %v0, %v1

diff  --git a/llvm/test/Transforms/SimplifyCFG/merge-cond-stores.ll b/llvm/test/Transforms/SimplifyCFG/merge-cond-stores.ll
index 3736a1d03677..921d26387aa2 100644
--- a/llvm/test/Transforms/SimplifyCFG/merge-cond-stores.ll
+++ b/llvm/test/Transforms/SimplifyCFG/merge-cond-stores.ll
@@ -5,14 +5,12 @@
 define void @test_simple(i32* %p, i32 %a, i32 %b) {
 ; CHECK-LABEL: @test_simple(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[X1:%.*]] = icmp ne i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[X2:%.*]] = icmp eq i32 [[B:%.*]], 0
-; CHECK-NEXT:    [[TMP0:%.*]] = xor i1 [[X2]], true
-; CHECK-NEXT:    [[TMP1:%.*]] = or i1 [[X1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; CHECK-NEXT:    [[TMP0:%.*]] = or i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[TMP1]], label [[TMP3:%.*]], label [[TMP2:%.*]]
 ; CHECK:       2:
-; CHECK-NEXT:    [[NOT_X2:%.*]] = xor i1 [[X2]], true
-; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = zext i1 [[NOT_X2]] to i32
+; CHECK-NEXT:    [[X2:%.*]] = icmp ne i32 [[B]], 0
+; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = zext i1 [[X2]] to i32
 ; CHECK-NEXT:    store i32 [[SPEC_SELECT]], i32* [[P:%.*]], align 4
 ; CHECK-NEXT:    br label [[TMP3]]
 ; CHECK:       3:
@@ -78,21 +76,20 @@ define void @test_recursive(i32* %p, i32 %a, i32 %b, i32 %c, i32 %d) {
 ; CHECK-LABEL: @test_recursive(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[TMP0:%.*]] = or i32 [[B:%.*]], [[A:%.*]]
-; CHECK-NEXT:    [[X4:%.*]] = icmp eq i32 [[D:%.*]], 0
 ; CHECK-NEXT:    [[TMP1:%.*]] = or i32 [[TMP0]], [[C:%.*]]
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0
-; CHECK-NEXT:    [[TMP3:%.*]] = xor i1 [[X4]], true
-; CHECK-NEXT:    [[TMP4:%.*]] = or i1 [[TMP2]], [[TMP3]]
-; CHECK-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; CHECK:       5:
+; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[TMP1]], [[D:%.*]]
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0
+; CHECK-NEXT:    br i1 [[TMP3]], label [[TMP5:%.*]], label [[TMP4:%.*]]
+; CHECK:       4:
+; CHECK-NEXT:    [[X4:%.*]] = icmp eq i32 [[D]], 0
 ; CHECK-NEXT:    [[X3:%.*]] = icmp eq i32 [[C]], 0
 ; CHECK-NEXT:    [[X2:%.*]] = icmp ne i32 [[B]], 0
 ; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = zext i1 [[X2]] to i32
 ; CHECK-NEXT:    [[SPEC_SELECT1:%.*]] = select i1 [[X3]], i32 [[SPEC_SELECT]], i32 2
 ; CHECK-NEXT:    [[SPEC_SELECT2:%.*]] = select i1 [[X4]], i32 [[SPEC_SELECT1]], i32 3
 ; CHECK-NEXT:    store i32 [[SPEC_SELECT2]], i32* [[P:%.*]], align 4
-; CHECK-NEXT:    br label [[TMP6]]
-; CHECK:       6:
+; CHECK-NEXT:    br label [[TMP5]]
+; CHECK:       5:
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -383,14 +380,12 @@ define void @test_outer_if(i32* %p, i32 %a, i32 %b, i32 %c) {
 ; CHECK-NEXT:    [[X3:%.*]] = icmp eq i32 [[C:%.*]], 0
 ; CHECK-NEXT:    br i1 [[X3]], label [[END:%.*]], label [[CONTINUE:%.*]]
 ; CHECK:       continue:
-; CHECK-NEXT:    [[X1:%.*]] = icmp ne i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[X2:%.*]] = icmp eq i32 [[B:%.*]], 0
-; CHECK-NEXT:    [[TMP0:%.*]] = xor i1 [[X2]], true
-; CHECK-NEXT:    [[TMP1:%.*]] = or i1 [[X1]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[END]]
+; CHECK-NEXT:    [[TMP0:%.*]] = or i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[TMP1]], label [[END]], label [[TMP2:%.*]]
 ; CHECK:       2:
-; CHECK-NEXT:    [[NOT_X2:%.*]] = xor i1 [[X2]], true
-; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = zext i1 [[NOT_X2]] to i32
+; CHECK-NEXT:    [[X2:%.*]] = icmp ne i32 [[B]], 0
+; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = zext i1 [[X2]] to i32
 ; CHECK-NEXT:    store i32 [[SPEC_SELECT]], i32* [[P:%.*]], align 4
 ; CHECK-NEXT:    br label [[END]]
 ; CHECK:       end:


        


More information about the llvm-commits mailing list