diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -1112,6 +1112,23 @@ return BinaryOperator::CreateNSWNeg( Builder.CreateSDiv(X, Y, I.getName(), I.isExact())); + // X / abs(X) --> X < 0 ? -1 : 1 + // abs(X) / X --> X < 0 ? -1 : 1 + Value *LHS, *RHS; + if (((isa(Op1) && + matchSelectPattern(Op1, LHS, RHS).Flavor == + SelectPatternFlavor::SPF_ABS && + LHS == Op0) || + (isa(Op0) && + matchSelectPattern(Op0, LHS, RHS).Flavor == + SelectPatternFlavor::SPF_ABS && + LHS == Op1)) && cast(RHS)->hasNoSignedWrap()) { + Value *AllOnes = Constant::getAllOnesValue(I.getType()); + Value *Cond = Builder.CreateICmpSGT(LHS, AllOnes); + return SelectInst::Create(Cond, ConstantInt::get(I.getType(), 1), + AllOnes); + } + // 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(I.getType()->getScalarSizeInBits())); diff --git a/llvm/test/Transforms/InstCombine/sdiv-abs.ll b/llvm/test/Transforms/InstCombine/sdiv-abs.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/sdiv-abs.ll @@ -0,0 +1,123 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare void @use(i32) + +define i32 @sdiv_abs(i32 %x) { +; CHECK-LABEL: @sdiv_abs( +; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[TMP1]], i32 1, i32 -1 +; CHECK-NEXT: ret i32 [[DIV]] +; + %cmp = icmp slt i32 %x, 0 + %sub = sub nsw i32 0, %x + %cond = select i1 %cmp, i32 %sub, i32 %x + %div = sdiv i32 %x, %cond + ret i32 %div +} + +define i32 @sdiv_abs2(i32 %x) { +; CHECK-LABEL: @sdiv_abs2( +; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[TMP1]], i32 1, i32 -1 +; CHECK-NEXT: ret i32 [[DIV]] +; + %cmp = icmp slt i32 %x, 0 + %sub = sub nsw i32 0, %x + %cond = select i1 %cmp, i32 %sub, i32 %x + %div = sdiv i32 %cond, %x + ret i32 %div +} + +define i32 @sdiv_abs_extra_use(i32 %x) { +; CHECK-LABEL: @sdiv_abs_extra_use( +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X:%.*]], 0 +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 0, [[X]] +; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 [[X]] +; CHECK-NEXT: call void @use(i32 [[COND]]) +; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[TMP1]], i32 1, i32 -1 +; CHECK-NEXT: ret i32 [[DIV]] +; + %cmp = icmp slt i32 %x, 0 + %sub = sub nsw i32 0, %x + %cond = select i1 %cmp, i32 %sub, i32 %x + call void @use(i32 %cond) + %div = sdiv i32 %x, %cond + ret i32 %div +} + +define i32 @sdiv_extra_use_abs(i32 %x) { +; CHECK-LABEL: @sdiv_extra_use_abs( +; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[TMP1]], i32 1, i32 -1 +; CHECK-NEXT: call void @use(i32 [[DIV]]) +; CHECK-NEXT: ret i32 [[DIV]] +; + %cmp = icmp slt i32 %x, 0 + %sub = sub nsw i32 0, %x + %cond = select i1 %cmp, i32 %sub, i32 %x + %div = sdiv i32 %x, %cond + call void @use(i32 %div) + ret i32 %div +} + + +define <4 x i32> @sdiv_abs_vec(<4 x i32> %x) { +; CHECK-LABEL: @sdiv_abs_vec( +; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt <4 x i32> [[X:%.*]], +; CHECK-NEXT: [[DIV:%.*]] = select <4 x i1> [[TMP1]], <4 x i32> , <4 x i32> +; CHECK-NEXT: ret <4 x i32> [[DIV]] +; + %cmp = icmp slt <4 x i32> %x, zeroinitializer + %sub = sub nsw <4 x i32> zeroinitializer, %x + %cond = select <4 x i1> %cmp, <4 x i32> %sub, <4 x i32> %x + %div = sdiv <4 x i32> %x, %cond + ret <4 x i32> %div +} + + +define <4 x i32> @sdiv_abs_vec2(<4 x i32> %x) { +; CHECK-LABEL: @sdiv_abs_vec2( +; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt <4 x i32> [[X:%.*]], +; CHECK-NEXT: [[DIV:%.*]] = select <4 x i1> [[TMP1]], <4 x i32> , <4 x i32> +; CHECK-NEXT: ret <4 x i32> [[DIV]] +; + %cmp = icmp slt <4 x i32> %x, zeroinitializer + %sub = sub nsw <4 x i32> zeroinitializer, %x + %cond = select <4 x i1> %cmp, <4 x i32> %sub, <4 x i32> %x + %div = sdiv <4 x i32> %cond, %x + ret <4 x i32> %div +} + +; Negative tests + +define i32 @sdiv_abs_no_nsw(i32 %x) { +; CHECK-LABEL: @sdiv_abs_no_nsw( +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X:%.*]], 0 +; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[X]] +; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 [[X]] +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[X]], [[COND]] +; CHECK-NEXT: ret i32 [[DIV]] +; + %cmp = icmp slt i32 %x, 0 + %sub = sub i32 0, %x + %cond = select i1 %cmp, i32 %sub, i32 %x + %div = sdiv i32 %x, %cond + ret i32 %div +} + +define i32 @sdiv_abs2_no_nsw(i32 %x) { +; CHECK-LABEL: @sdiv_abs2_no_nsw( +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X:%.*]], 0 +; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[X]] +; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 [[X]] +; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[COND]], [[X]] +; CHECK-NEXT: ret i32 [[DIV]] +; + %cmp = icmp slt i32 %x, 0 + %sub = sub i32 0, %x + %cond = select i1 %cmp, i32 %sub, i32 %x + %div = sdiv i32 %cond, %x + ret i32 %div +}