From e6b6787d01e9ea6338b5b51c6e3ba1b903876b3a Mon Sep 17 00:00:00 2001 From: Sanjay Patel Date: Sun, 16 Aug 2020 15:42:06 -0400 Subject: [PATCH] [InstCombine] fold abs(X)/X to cmp+select 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 | 10 ++++++++++ llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll | 12 ++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp index fb8b640..ec610b4 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -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(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())); diff --git a/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll b/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll index 52f7b1b..b2a7a0d 100644 --- a/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll +++ b/llvm/test/Transforms/InstCombine/sdiv-canonicalize.ll @@ -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:%.*]], +; CHECK-NEXT: [[R:%.*]] = select <4 x i1> [[DOTINV]], <4 x i32> , <4 x i32> ; 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) -- 2.7.4