[InstCombine] Fold signbit test of a pow2 or zero
authorJun Zhang <jun@junz.org>
Sat, 25 Feb 2023 06:23:01 +0000 (14:23 +0800)
committerJun Zhang <jun@junz.org>
Tue, 28 Feb 2023 07:52:13 +0000 (15:52 +0800)
(X & X) <  0 --> X == MinSignedC
(X & X) > -1 --> X != MinSignedC

Alive2: https://alive2.llvm.org/ce/z/_J5q3S
Closes: https://github.com/llvm/llvm-project/issues/60957

Signed-off-by: Jun Zhang <jun@junz.org>
Differential Revision: https://reviews.llvm.org/D144777

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
llvm/test/Transforms/InstCombine/fold-signbit-test-power2.ll
llvm/test/Transforms/InstCombine/minmax-of-xor-x.ll

index c8c24c7..2300455 100644 (file)
@@ -1832,6 +1832,15 @@ Instruction *InstCombinerImpl::foldICmpAndConstant(ICmpInst &Cmp,
       auto NewPred = TrueIfNeg ? CmpInst::ICMP_EQ : CmpInst::ICMP_NE;
       return new ICmpInst(NewPred, X, ConstantInt::getNullValue(X->getType()));
     }
+    // (X & X) <  0 --> X == MinSignedC
+    // (X & X) > -1 --> X != MinSignedC
+    if (match(And, m_c_And(m_Neg(m_Value(X)), m_Deferred(X)))) {
+      Constant *MinSignedC = ConstantInt::get(
+          X->getType(),
+          APInt::getSignedMinValue(X->getType()->getScalarSizeInBits()));
+      auto NewPred = TrueIfNeg ? CmpInst::ICMP_EQ : CmpInst::ICMP_NE;
+      return new ICmpInst(NewPred, X, MinSignedC);
+    }
   }
 
   // TODO: These all require that Y is constant too, so refactor with the above.
index 8c51209..f502466 100644 (file)
@@ -1,56 +1,60 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
-; icmp slt (and X, -X), 0 --> icmp eq (X, MinSignC)
-define i1 @pow2_or_zero1(i8 %x) {
-; CHECK-LABEL: @pow2_or_zero1(
-; CHECK-NEXT:    [[NEG:%.*]] = sub i8 0, [[X:%.*]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and i8 [[NEG]], [[X]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[POW2_OR_ZERO]], 0
+declare void @use(i8)
+declare void @use_i1(i1)
+declare void @use_i1_vec(<2 x i1>)
+
+; (X & -X) < 0 --> X == MinSignC
+; (X & X) > -1 --> X != MinSignC
+
+define i1 @pow2_or_zero_is_negative(i8 %x) {
+; CHECK-LABEL: @pow2_or_zero_is_negative(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X:%.*]], -128
+; CHECK-NEXT:    [[CMP_2:%.*]] = icmp eq i8 [[X]], -128
+; CHECK-NEXT:    call void @use_i1(i1 [[CMP_2]])
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %neg = sub i8 0, %x
   %pow2_or_zero = and i8 %x, %neg
   %cmp = icmp slt i8 %pow2_or_zero, 0
+  %cmp.2 = icmp ugt i8 %pow2_or_zero, 127
+  call void @use_i1(i1 %cmp.2)
   ret i1 %cmp
 }
 
-; icmp slt (and -X, X), 0 --> icmp eq (X, MinSignC)
-define i1 @pow2_or_zero1_commute(i8 %A) {
-; CHECK-LABEL: @pow2_or_zero1_commute(
-; CHECK-NEXT:    [[X:%.*]] = sdiv i8 42, [[A:%.*]]
-; CHECK-NEXT:    [[NEG:%.*]] = sub nsw i8 0, [[X]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and i8 [[X]], [[NEG]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[POW2_OR_ZERO]], 0
+define i1 @pow2_or_zero_is_negative_commute(i8 %A) {
+; CHECK-LABEL: @pow2_or_zero_is_negative_commute(
+; CHECK-NEXT:    [[X:%.*]] = mul i8 [[A:%.*]], 42
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], -128
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
-  %x = sdiv i8 42, %A ; thwart complexity-based canonicalization
+  %x = mul i8 42, %A ; thwart complexity-based canonicalization
   %neg = sub i8 0, %x
   %pow2_or_zero = and i8 %neg, %x
   %cmp = icmp slt i8 %pow2_or_zero, 0
   ret i1 %cmp
 }
 
-define <2 x i1> @pow2_or_zero1_vec(<2 x i8> %x) {
-; CHECK-LABEL: @pow2_or_zero1_vec(
-; CHECK-NEXT:    [[NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and <2 x i8> [[NEG]], [[X]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt <2 x i8> [[POW2_OR_ZERO]], zeroinitializer
+define <2 x i1> @pow2_or_zero_is_negative_vec(<2 x i8> %x) {
+; CHECK-LABEL: @pow2_or_zero_is_negative_vec(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X:%.*]], <i8 -128, i8 -128>
+; CHECK-NEXT:    [[CMP_2:%.*]] = icmp eq <2 x i8> [[X]], <i8 -128, i8 -128>
+; CHECK-NEXT:    call void @use_i1_vec(<2 x i1> [[CMP_2]])
 ; CHECK-NEXT:    ret <2 x i1> [[CMP]]
 ;
   %neg = sub <2 x i8> <i8 0, i8 0>, %x
   %pow2_or_zero = and <2 x i8> %x, %neg
   %cmp = icmp slt <2 x i8> %pow2_or_zero, <i8 0, i8 0>
+  %cmp.2 = icmp ugt <2 x i8> %pow2_or_zero, <i8 127, i8 127>
+  call void @use_i1_vec(<2 x i1> %cmp.2)
   ret <2 x i1> %cmp
 }
 
-
-define <2 x i1> @pow2_or_zero1_vec_commute(<2 x i8> %A) {
-; CHECK-LABEL: @pow2_or_zero1_vec_commute(
+define <2 x i1> @pow2_or_zero_is_negative_vec_commute(<2 x i8> %A) {
+; CHECK-LABEL: @pow2_or_zero_is_negative_vec_commute(
 ; CHECK-NEXT:    [[X:%.*]] = mul <2 x i8> [[A:%.*]], <i8 42, i8 42>
-; CHECK-NEXT:    [[NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and <2 x i8> [[X]], [[NEG]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt <2 x i8> [[POW2_OR_ZERO]], zeroinitializer
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X]], <i8 -128, i8 -128>
 ; CHECK-NEXT:    ret <2 x i1> [[CMP]]
 ;
   %x = mul <2 x i8> <i8 42, i8 42>, %A ; thwart complexity-based canonicalization
@@ -60,27 +64,25 @@ define <2 x i1> @pow2_or_zero1_vec_commute(<2 x i8> %A) {
   ret <2 x i1> %cmp
 }
 
-; icmp sgt (and X, -X), -1 --> icmp ne (X, MinSignC)
-define i1 @pow2_or_zero2(i8 %x) {
-; CHECK-LABEL: @pow2_or_zero2(
-; CHECK-NEXT:    [[NEG:%.*]] = sub i8 0, [[X:%.*]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and i8 [[NEG]], [[X]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[POW2_OR_ZERO]], -1
+define i1 @pow2_or_zero_is_not_negative(i8 %x) {
+; CHECK-LABEL: @pow2_or_zero_is_not_negative(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X:%.*]], -128
+; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ne i8 [[X]], -128
+; CHECK-NEXT:    call void @use_i1(i1 [[CMP_2]])
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %neg = sub i8 0, %x
   %pow2_or_zero = and i8 %x, %neg
   %cmp = icmp sgt i8 %pow2_or_zero, -1
+  %cmp.2 = icmp ult i8 %pow2_or_zero, -128
+  call void @use_i1(i1 %cmp.2)
   ret i1 %cmp
 }
 
-; icmp sgt (and -X, X), -1 --> icmp ne (X, MinSignC)
-define i1 @pow2_or_zero2_commute(i8 %A) {
-; CHECK-LABEL: @pow2_or_zero2_commute(
+define i1 @pow2_or_zero_is_not_negative_commute(i8 %A) {
+; CHECK-LABEL: @pow2_or_zero_is_not_negative_commute(
 ; CHECK-NEXT:    [[X:%.*]] = mul i8 [[A:%.*]], 42
-; CHECK-NEXT:    [[NEG:%.*]] = sub i8 0, [[X]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and i8 [[X]], [[NEG]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[POW2_OR_ZERO]], -1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X]], -128
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %x = mul i8 42, %A ; thwart complexity-based canonicalization
@@ -90,25 +92,25 @@ define i1 @pow2_or_zero2_commute(i8 %A) {
   ret i1 %cmp
 }
 
-define <2 x i1> @pow2_or_zero2_vec(<2 x i8> %x) {
-; CHECK-LABEL: @pow2_or_zero2_vec(
-; CHECK-NEXT:    [[NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and <2 x i8> [[NEG]], [[X]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt <2 x i8> [[POW2_OR_ZERO]], <i8 -1, i8 -1>
+define <2 x i1> @pow2_or_zero_is_not_negative_vec(<2 x i8> %x) {
+; CHECK-LABEL: @pow2_or_zero_is_not_negative_vec(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i8> [[X:%.*]], <i8 -128, i8 -128>
+; CHECK-NEXT:    [[CMP_2:%.*]] = icmp ne <2 x i8> [[X]], <i8 -128, i8 -128>
+; CHECK-NEXT:    call void @use_i1_vec(<2 x i1> [[CMP_2]])
 ; CHECK-NEXT:    ret <2 x i1> [[CMP]]
 ;
   %neg = sub <2 x i8> <i8 0, i8 0>, %x
   %pow2_or_zero = and <2 x i8> %x, %neg
   %cmp = icmp sgt <2 x i8> %pow2_or_zero, <i8 -1, i8 -1>
+  %cmp.2 = icmp ult <2 x i8> %pow2_or_zero, <i8 -128, i8 -128>
+  call void @use_i1_vec(<2 x i1> %cmp.2)
   ret <2 x i1> %cmp
 }
 
-define <2 x i1> @pow2_or_zero2_vec_commute(<2 x i8> %A) {
-; CHECK-LABEL: @pow2_or_zero2_vec_commute(
+define <2 x i1> @pow2_or_zero_is_not_negative_vec_commute(<2 x i8> %A) {
+; CHECK-LABEL: @pow2_or_zero_is_not_negative_vec_commute(
 ; CHECK-NEXT:    [[X:%.*]] = mul <2 x i8> [[A:%.*]], <i8 42, i8 42>
-; CHECK-NEXT:    [[NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X]]
-; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and <2 x i8> [[X]], [[NEG]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt <2 x i8> [[POW2_OR_ZERO]], <i8 -1, i8 -1>
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i8> [[X]], <i8 -128, i8 -128>
 ; CHECK-NEXT:    ret <2 x i1> [[CMP]]
 ;
   %x = mul <2 x i8> <i8 42, i8 42>, %A ; thwart complexity-based canonicalization
@@ -117,3 +119,20 @@ define <2 x i1> @pow2_or_zero2_vec_commute(<2 x i8> %A) {
   %cmp = icmp sgt <2 x i8> %pow2_or_zero, <i8 -1, i8 -1>
   ret <2 x i1> %cmp
 }
+
+define i1 @pow2_or_zero_is_negative_extra_use(i8 %x) {
+; CHECK-LABEL: @pow2_or_zero_is_negative_extra_use(
+; CHECK-NEXT:    [[NEG:%.*]] = sub i8 0, [[X:%.*]]
+; CHECK-NEXT:    call void @use(i8 [[NEG]])
+; CHECK-NEXT:    [[POW2_OR_ZERO:%.*]] = and i8 [[NEG]], [[X]]
+; CHECK-NEXT:    call void @use(i8 [[POW2_OR_ZERO]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %neg = sub i8 0, %x
+  call void @use(i8 %neg)
+  %pow2_or_zero = and i8 %x, %neg
+  call void @use(i8 %pow2_or_zero)
+  %cmp = icmp slt i8 %pow2_or_zero, 0
+  ret i1 %cmp
+}
index ff3093c..daf58a3 100644 (file)
@@ -134,12 +134,13 @@ define <2 x i8> @smin_xor_pow2_unk(<2 x i8> %x, <2 x i8> %y) {
 
 define i8 @smax_xor_pow2_neg(i8 %x, i8 %y) {
 ; CHECK-LABEL: @smax_xor_pow2_neg(
-; CHECK-NEXT:    [[NY:%.*]] = sub i8 0, [[Y:%.*]]
-; CHECK-NEXT:    [[YP2:%.*]] = and i8 [[NY]], [[Y]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[YP2]], 0
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[Y:%.*]], -128
 ; CHECK-NEXT:    br i1 [[CMP]], label [[NEG:%.*]], label [[POS:%.*]]
 ; CHECK:       neg:
-; CHECK-NEXT:    [[R:%.*]] = and i8 [[X:%.*]], 127
+; CHECK-NEXT:    [[NY:%.*]] = sub i8 0, [[Y]]
+; CHECK-NEXT:    [[YP2:%.*]] = and i8 [[NY]], [[Y]]
+; CHECK-NEXT:    [[X_XOR:%.*]] = xor i8 [[YP2]], [[X:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.smax.i8(i8 [[X]], i8 [[X_XOR]])
 ; CHECK-NEXT:    ret i8 [[R]]
 ; CHECK:       pos:
 ; CHECK-NEXT:    call void @barrier()