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,31 @@ return BinaryOperator::CreateNSWNeg( Builder.CreateSDiv(X, Y, I.getName(), I.isExact())); + // X / (select C, X, -X) -> select C ? 1 : -1 + // (select C, X, -X) / X -> select C ? 1 : -1 + Value *Cond; + if ((match(Op0, m_Select(m_Value(Cond), m_Value(X), + m_NSWSub(m_Zero(), m_Deferred(X)))) && + Op1 == X) || + (match(Op1, m_Select(m_Value(Cond), m_Value(X), + m_NSWSub(m_Zero(), m_Deferred(X)))) && + Op0 == X)) { + return SelectInst::Create(Cond, ConstantInt::get(I.getType(), 1), + Constant::getAllOnesValue(I.getType())); + } + + // X / (select C, -X, X) -> select C ? -1 : 1 + // (select C, -X, X) / X -> select C ? -1 : 1 + if ((match(Op0, m_Select(m_Value(Cond), m_NSWSub(m_Zero(), m_Value(X)), + m_Deferred(X))) && + Op1 == X) || + (match(Op1, m_Select(m_Value(Cond), m_NSWSub(m_Zero(), m_Value(X)), + m_Deferred(X))) && + Op0 == X)) { + return SelectInst::Create(Cond, Constant::getAllOnesValue(I.getType()), + ConstantInt::get(I.getType(), 1)); + } + // 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-select.ll b/llvm/test/Transforms/InstCombine/sdiv-select.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/sdiv-select.ll @@ -0,0 +1,187 @@ +; 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: [[CMP_INV:%.*]] = icmp sgt i32 [[X:%.*]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[CMP_INV]], 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: [[CMP_INV:%.*]] = icmp sgt i32 [[X:%.*]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[CMP_INV]], 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_abs3(i32 %x) { +; CHECK-LABEL: @sdiv_abs3( +; CHECK-NEXT: [[CMP_INV:%.*]] = icmp sgt i32 [[X:%.*]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[CMP_INV]], 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 %x, i32 %sub + %div = sdiv i32 %x, %cond + ret i32 %div +} + +define i32 @sdiv_eq(i32 %x) { +; CHECK-LABEL: @sdiv_eq( +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 4 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[CMP]], i32 -1, i32 1 +; CHECK-NEXT: ret i32 [[DIV]] +; + %cmp = icmp eq i32 %x, 4 + %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_eq2(i32 %x, i32 %y) { +; CHECK-LABEL: @sdiv_eq2( +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[Y:%.*]], 0 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[TOBOOL]], i32 -1, i32 1 +; CHECK-NEXT: ret i32 [[DIV]] +; + %tobool = icmp eq i32 %y, 0 + %div = select i1 %tobool, i32 -1, i32 1 + 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: [[DIV:%.*]] = select i1 [[CMP]], 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: [[CMP_INV:%.*]] = icmp sgt i32 [[X:%.*]], -1 +; CHECK-NEXT: [[DIV:%.*]] = select i1 [[CMP_INV]], 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: [[CMP_INV:%.*]] = icmp sgt <4 x i32> [[X:%.*]], +; CHECK-NEXT: [[DIV:%.*]] = select <4 x i1> [[CMP_INV]], <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: [[CMP_INV:%.*]] = icmp sgt <4 x i32> [[X:%.*]], +; CHECK-NEXT: [[DIV:%.*]] = select <4 x i1> [[CMP_INV]], <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 +} + +define <4 x i32> @sdiv_abs_vec3(<4 x i32> %x) { +; CHECK-LABEL: @sdiv_abs_vec3( +; CHECK-NEXT: [[CMP_INV:%.*]] = icmp sgt <4 x i32> [[X:%.*]], +; CHECK-NEXT: [[DIV:%.*]] = select <4 x i1> [[CMP_INV]], <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> %x, <4 x i32> %sub + %div = sdiv <4 x i32> %cond, %x + ret <4 x i32> %div +} + +define <4 x i32> @sdiv_eq_vec(<4 x i32> %x) { +; CHECK-LABEL: @sdiv_eq_vec( +; CHECK-NEXT: [[CMP:%.*]] = icmp eq <4 x i32> [[X:%.*]], +; CHECK-NEXT: [[DIV:%.*]] = select <4 x i1> [[CMP]], <4 x i32> , <4 x i32> +; CHECK-NEXT: ret <4 x i32> [[DIV]] +; + %cmp = icmp eq <4 x i32> %x, + %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 +} + + +; 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 +}