[InstCombine] fold abs(X)/X to cmp+select
authorSanjay Patel <spatel@rotateright.com>
Sun, 16 Aug 2020 19:42:06 +0000 (15:42 -0400)
committerSanjay Patel <spatel@rotateright.com>
Mon, 17 Aug 2020 12:01:28 +0000 (08:01 -0400)
The backend can convert the select-of-constants to
bit-hack shift+logic if desirable.

https://alive2.llvm.org/ce/z/pgJT6E

  define i8 @src(i8 %x) {
  %0:
    %a = abs i8 %x, 1
    %d = sdiv i8 %x, %a
    ret i8 %d
  }
  =>
  define i8 @tgt(i8 %x) {
  %0:
    %cond = icmp sgt i8 %x, 255
    %r = select i1 %cond, i8 1, i8 255
    ret i8 %r
  }
  Transformation seems to be correct!

llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll

index fb8b640..ec610b4 100644 (file)
@@ -1179,6 +1179,16 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
     return BinaryOperator::CreateNSWNeg(
         Builder.CreateSDiv(X, Y, I.getName(), I.isExact()));
 
+  // abs(X) / X --> X > -1 ? 1 : -1
+  // X / abs(X) --> X > -1 ? 1 : -1
+  if (match(&I, m_c_BinOp(
+                    m_OneUse(m_Intrinsic<Intrinsic::abs>(m_Value(X), m_One())),
+                    m_Deferred(X)))) {
+    Constant *NegOne = ConstantInt::getAllOnesValue(Ty);
+    Value *Cond = Builder.CreateICmpSGT(X, NegOne);
+    return SelectInst::Create(Cond, ConstantInt::get(Ty, 1), NegOne);
+  }
+
   // If the sign bits of both operands are zero (i.e. we can prove they are
   // unsigned inputs), turn this into a udiv.
   APInt Mask(APInt::getSignMask(Ty->getScalarSizeInBits()));
index 52f7b1b..b2a7a0d 100644 (file)
@@ -96,8 +96,8 @@ define i64 @test_sdiv_canonicalize_constexpr(i64 %L1) {
 
 define i32 @sdiv_abs_nsw(i32 %x) {
 ; CHECK-LABEL: @sdiv_abs_nsw(
-; CHECK-NEXT:    [[A:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 true)
-; CHECK-NEXT:    [[R:%.*]] = sdiv i32 [[A]], [[X]]
+; CHECK-NEXT:    [[DOTINV:%.*]] = icmp sgt i32 [[X:%.*]], -1
+; CHECK-NEXT:    [[R:%.*]] = select i1 [[DOTINV]], i32 1, i32 -1
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
   %a = call i32 @llvm.abs.i32(i32 %x, i1 true)
@@ -107,8 +107,8 @@ define i32 @sdiv_abs_nsw(i32 %x) {
 
 define <4 x i32> @sdiv_abs_nsw_vec(<4 x i32> %x) {
 ; CHECK-LABEL: @sdiv_abs_nsw_vec(
-; CHECK-NEXT:    [[A:%.*]] = call <4 x i32> @llvm.abs.v4i32(<4 x i32> [[X:%.*]], i1 true)
-; CHECK-NEXT:    [[R:%.*]] = sdiv <4 x i32> [[X]], [[A]]
+; CHECK-NEXT:    [[DOTINV:%.*]] = icmp sgt <4 x i32> [[X:%.*]], <i32 -1, i32 -1, i32 -1, i32 -1>
+; CHECK-NEXT:    [[R:%.*]] = select <4 x i1> [[DOTINV]], <4 x i32> <i32 1, i32 1, i32 1, i32 1>, <4 x i32> <i32 -1, i32 -1, i32 -1, i32 -1>
 ; CHECK-NEXT:    ret <4 x i32> [[R]]
 ;
   %a = call <4 x i32> @llvm.abs.v4i32(<4 x i32> %x, i1 true)
@@ -116,6 +116,8 @@ define <4 x i32> @sdiv_abs_nsw_vec(<4 x i32> %x) {
   ret <4 x i32> %r
 }
 
+; Negative test - requires poison int min (nsw)
+
 define i32 @sdiv_abs(i32 %x) {
 ; CHECK-LABEL: @sdiv_abs(
 ; CHECK-NEXT:    [[A:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 false)
@@ -127,6 +129,8 @@ define i32 @sdiv_abs(i32 %x) {
   ret i32 %r
 }
 
+; Negative test
+
 define i32 @sdiv_abs_extra_use(i32 %x) {
 ; CHECK-LABEL: @sdiv_abs_extra_use(
 ; CHECK-NEXT:    [[A:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 true)