diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -13601,16 +13601,17 @@ A fixed point number represents a real data type for a number that has a fixed number of digits after a radix point (equivalent to the decimal point '.'). -The number of digits after the radix point is referred as the ``scale``. These +The number of digits after the radix point is referred as the `scale`. These are useful for representing fractional values to a specific precision. The following intrinsics perform fixed point arithmetic operations on 2 operands of the same scale, specified as the third argument. -The `llvm.*mul.fix` family of intrinsic functions represents a multiplication +The ``llvm.*mul.fix`` family of intrinsic functions represents a multiplication of fixed point numbers through scaled integers. Therefore, fixed point -multplication can be represented as +multiplication can be represented as + +.. code-block:: llvm -:: %result = call i4 @llvm.smul.fix.i4(i4 %a, i4 %b, i32 %scale) ; Expands to @@ -13621,6 +13622,22 @@ %r = ashr i8 %mul, i8 %scale2 ; this is for a target rounding down towards negative infinity %result = trunc i8 %r to i4 +The ``llvm.*div.fix`` family of intrinsic functions represents a division of +fixed point numbers through scaled integers. Fixed point division can be +represented as: + +.. code-block:: llvm + + %result call i4 @llvm.sdiv.fix.i4(i4 %a, i4 %b, i32 %scale) + + ; Expands to + %a2 = sext i4 %a to i8 + %b2 = sext i4 %b to i8 + %scale2 = trunc i32 %scale to i8 + %a3 = shl i8 %a2, %scale2 + %r = sdiv i8 %a3, %b2 ; this is for a target rounding towards zero + %result = trunc i8 %r to i4 + For each of these functions, if the result cannot be represented exactly with the provided scale, the result is rounded. Rounding is unspecified since preferred rounding may vary for different targets. Rounding is specified @@ -13889,6 +13906,126 @@ %res = call i4 @llvm.umul.fix.sat.i4(i4 2, i4 4, i32 1) ; %res = 4 (1 x 2 = 2) +'``llvm.sdiv.fix.*``' Intrinsics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax +""""""" + +This is an overloaded intrinsic. You can use ``llvm.sdiv.fix`` +on any integer bit width or vectors of integers. + +:: + + declare i16 @llvm.sdiv.fix.i16(i16 %a, i16 %b, i32 %scale) + declare i32 @llvm.sdiv.fix.i32(i32 %a, i32 %b, i32 %scale) + declare i64 @llvm.sdiv.fix.i64(i64 %a, i64 %b, i32 %scale) + declare <4 x i32> @llvm.sdiv.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale) + +Overview +""""""""" + +The '``llvm.sdiv.fix``' family of intrinsic functions perform signed +fixed point division on 2 arguments of the same scale. + +Arguments +"""""""""" + +The arguments (%a and %b) and the result may be of integer types of any bit +width, but they must have the same bit width. The arguments may also work with +int vectors of the same length and int size. ``%a`` and ``%b`` are the two +values that will undergo signed fixed point division. The argument +``%scale`` represents the scale of both operands, and must be a constant +integer. + +Semantics: +"""""""""" + +This operation performs fixed point division on the 2 arguments of a +specified scale. The result will also be returned in the same scale specified +in the third argument. + +If the result value cannot be precisely represented in the given scale, the +value is rounded up or down to the closest representable value. The rounding +direction is unspecified. + +It is undefined behavior if the result value does not fit within the range of +the fixed point type. + + +Examples +""""""""" + +.. code-block:: llvm + + %res = call i4 @llvm.sdiv.fix.i4(i4 6, i4 2, i32 0) ; %res = 3 (6 / 2 = 3) + %res = call i4 @llvm.sdiv.fix.i4(i4 6, i4 4, i32 1) ; %res = 3 (3 / 2 = 1.5) + %res = call i4 @llvm.sdiv.fix.i4(i4 3, i4 -2, i32 1) ; %res = -3 (1.5 / -1 = -1.5) + + ; The result in the following could be rounded up to 1 or down to 0.5 + %res = call i4 @llvm.sdiv.fix.i4(i4 3, i4 4, i32 1) ; %res = 2 (or 1) (1.5 / 2 = 0.75) + + +'``llvm.udiv.fix.*``' Intrinsics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax +""""""" + +This is an overloaded intrinsic. You can use ``llvm.udiv.fix`` +on any integer bit width or vectors of integers. + +:: + + declare i16 @llvm.udiv.fix.i16(i16 %a, i16 %b, i32 %scale) + declare i32 @llvm.udiv.fix.i32(i32 %a, i32 %b, i32 %scale) + declare i64 @llvm.udiv.fix.i64(i64 %a, i64 %b, i32 %scale) + declare <4 x i32> @llvm.udiv.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale) + +Overview +""""""""" + +The '``llvm.udiv.fix``' family of intrinsic functions perform unsigned +fixed point division on 2 arguments of the same scale. + +Arguments +"""""""""" + +The arguments (%a and %b) and the result may be of integer types of any bit +width, but they must have the same bit width. The arguments may also work with +int vectors of the same length and int size. ``%a`` and ``%b`` are the two +values that will undergo unsigned fixed point division. The argument +``%scale`` represents the scale of both operands, and must be a constant +integer. + +Semantics: +"""""""""" + +This operation performs fixed point division on the 2 arguments of a +specified scale. The result will also be returned in the same scale specified +in the third argument. + +If the result value cannot be precisely represented in the given scale, the +value is rounded up or down to the closest representable value. The rounding +direction is unspecified. + +It is undefined behavior if the result value does not fit within the range of +the fixed point type. + + +Examples +""""""""" + +.. code-block:: llvm + + %res = call i4 @llvm.udiv.fix.i4(i4 6, i4 2, i32 0) ; %res = 3 (6 / 2 = 3) + %res = call i4 @llvm.udiv.fix.i4(i4 6, i4 4, i32 1) ; %res = 3 (3 / 2 = 1.5) + %res = call i4 @llvm.udiv.fix.i4(i4 1, i4 -8, i32 4) ; %res = 2 (0.0625 / 0.5 = 0.125) + + ; The result in the following could be rounded up to 1 or down to 0.5 + %res = call i4 @llvm.udiv.fix.i4(i4 3, i4 4, i32 1) ; %res = 2 (or 1) (1.5 / 2 = 0.75) + + Specialised Arithmetic Intrinsics --------------------------------- diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -283,6 +283,12 @@ /// bits of the first 2 operands. SMULFIXSAT, UMULFIXSAT, + /// RESULT = [US]DIVFIX(LHS, RHS, SCALE) - Perform fixed point division on + /// 2 integers with the same width and scale. SCALE represents the scale + /// of both operands as fixed point numbers. This SCALE parameter must be a + /// constant integer. + SDIVFIX, UDIVFIX, + /// Simple binary floating point operators. FADD, FSUB, FMUL, FDIV, FREM, diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -925,6 +925,8 @@ case ISD::SMULFIXSAT: case ISD::UMULFIX: case ISD::UMULFIXSAT: + case ISD::SDIVFIX: + case ISD::UDIVFIX: Supported = isSupportedFixedPointOperation(Op, VT, Scale); break; } @@ -4169,6 +4171,14 @@ /// method accepts integers as its arguments. SDValue expandFixedPointMul(SDNode *Node, SelectionDAG &DAG) const; + /// Method for building the DAG expansion of ISD::[US]DIVFIX. This + /// method accepts integers as its arguments. + /// Note: This method may fail if the division could not be performed + /// within the type. Clients must retry with a wider type if this happens. + SDValue expandFixedPointDiv(unsigned Opcode, const SDLoc &dl, + SDValue LHS, SDValue RHS, + unsigned Scale, SelectionDAG &DAG) const; + /// Method for building the DAG expansion of ISD::U(ADD|SUB)O. Expansion /// always suceeds and populates the Result and Overflow arguments. void expandUADDSUBO(SDNode *Node, SDValue &Result, SDValue &Overflow, diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -914,6 +914,14 @@ [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty], [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative, ImmArg<2>]>; +def int_sdiv_fix : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty], + [IntrNoMem, ImmArg<2>]>; + +def int_udiv_fix : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty], + [IntrNoMem, ImmArg<2>]>; + //===------------------- Fixed Point Saturation Arithmetic Intrinsics ----------------===// // def int_smul_fix_sat : Intrinsic<[llvm_anyint_ty], diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td --- a/llvm/include/llvm/Target/TargetSelectionDAG.td +++ b/llvm/include/llvm/Target/TargetSelectionDAG.td @@ -124,7 +124,7 @@ def SDTIntBinHiLoOp : SDTypeProfile<2, 2, [ // mulhi, mullo, sdivrem, udivrem SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>,SDTCisInt<0> ]>; -def SDTIntScaledBinOp : SDTypeProfile<1, 3, [ // smulfix, umulfix +def SDTIntScaledBinOp : SDTypeProfile<1, 3, [ // smulfix, sdivfix, etc SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisInt<0>, SDTCisInt<3> ]>; @@ -400,6 +400,8 @@ def smulfixsat : SDNode<"ISD::SMULFIXSAT", SDTIntScaledBinOp, [SDNPCommutative]>; def umulfix : SDNode<"ISD::UMULFIX" , SDTIntScaledBinOp, [SDNPCommutative]>; def umulfixsat : SDNode<"ISD::UMULFIXSAT", SDTIntScaledBinOp, [SDNPCommutative]>; +def sdivfix : SDNode<"ISD::SDIVFIX" , SDTIntScaledBinOp>; +def udivfix : SDNode<"ISD::UDIVFIX" , SDTIntScaledBinOp>; def sext_inreg : SDNode<"ISD::SIGN_EXTEND_INREG", SDTExtInreg>; def sext_invec : SDNode<"ISD::SIGN_EXTEND_VECTOR_INREG", SDTExtInvec>; diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -1125,7 +1125,9 @@ case ISD::SMULFIX: case ISD::SMULFIXSAT: case ISD::UMULFIX: - case ISD::UMULFIXSAT: { + case ISD::UMULFIXSAT: + case ISD::SDIVFIX: + case ISD::UDIVFIX: { unsigned Scale = Node->getConstantOperandVal(2); Action = TLI.getFixedPointOperationAction(Node->getOpcode(), Node->getValueType(0), Scale); @@ -3340,6 +3342,24 @@ case ISD::UMULFIXSAT: Results.push_back(TLI.expandFixedPointMul(Node, DAG)); break; + case ISD::SDIVFIX: + case ISD::UDIVFIX: + if (SDValue V = TLI.expandFixedPointDiv(Node->getOpcode(), SDLoc(Node), + Node->getOperand(0), + Node->getOperand(1), + Node->getConstantOperandVal(2), + DAG)) { + Results.push_back(V); + break; + } + // FIXME: We might want to retry here with a wider type if we fail, if that + // type is legal. + // FIXME: Technically, so long as we only have sdivfixes where BW+Scale is + // <= 128 (which is the case for all of the default Embedded-C types), + // we will only get here with types and scales that we could always expand + // if we were allowed to generate libcalls to division functions of illegal + // type. But we cannot do that. + llvm_unreachable("Cannot expand DIVFIX!"); case ISD::ADDCARRY: case ISD::SUBCARRY: { SDValue LHS = Node->getOperand(0); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -158,6 +158,9 @@ case ISD::UMULFIX: case ISD::UMULFIXSAT: Res = PromoteIntRes_MULFIX(N); break; + case ISD::SDIVFIX: + case ISD::UDIVFIX: Res = PromoteIntRes_DIVFIX(N); break; + case ISD::ABS: Res = PromoteIntRes_ABS(N); break; case ISD::ATOMIC_LOAD: @@ -762,6 +765,71 @@ N->getOperand(2)); } +static SDValue earlyExpandDIVFIX(SDNode *N, SDValue LHS, SDValue RHS, + unsigned Scale, const TargetLowering &TLI, + SelectionDAG &DAG) { + EVT VT = LHS.getValueType(); + bool Signed = N->getOpcode() == ISD::SDIVFIX; + + SDLoc dl(N); + // See if we can perform the division in this type without widening. + if (SDValue V = TLI.expandFixedPointDiv(N->getOpcode(), dl, LHS, RHS, Scale, + DAG)) + return V; + + // If that didn't work, double the type width and try again. That must work, + // or something is wrong. + EVT WideVT = EVT::getIntegerVT(*DAG.getContext(), + VT.getScalarSizeInBits() * 2); + if (Signed) { + LHS = DAG.getSExtOrTrunc(LHS, dl, WideVT); + RHS = DAG.getSExtOrTrunc(RHS, dl, WideVT); + } else { + LHS = DAG.getZExtOrTrunc(LHS, dl, WideVT); + RHS = DAG.getZExtOrTrunc(RHS, dl, WideVT); + } + + // TODO: Saturation. + + SDValue Res = TLI.expandFixedPointDiv(N->getOpcode(), dl, LHS, RHS, Scale, + DAG); + assert(Res && "Expanding DIVFIX with wide type failed?"); + return DAG.getZExtOrTrunc(Res, dl, VT); +} + +SDValue DAGTypeLegalizer::PromoteIntRes_DIVFIX(SDNode *N) { + SDLoc dl(N); + SDValue Op1Promoted, Op2Promoted; + bool Signed = N->getOpcode() == ISD::SDIVFIX; + if (Signed) { + Op1Promoted = SExtPromotedInteger(N->getOperand(0)); + Op2Promoted = SExtPromotedInteger(N->getOperand(1)); + } else { + Op1Promoted = ZExtPromotedInteger(N->getOperand(0)); + Op2Promoted = ZExtPromotedInteger(N->getOperand(1)); + } + EVT PromotedType = Op1Promoted.getValueType(); + unsigned Scale = N->getConstantOperandVal(2); + + SDValue Res; + // If the type is already legal and the operation is legal in that type, we + // should not early expand. + if (TLI.isTypeLegal(PromotedType)) { + TargetLowering::LegalizeAction Action = + TLI.getFixedPointOperationAction(N->getOpcode(), PromotedType, Scale); + if (Action == TargetLowering::Legal || Action == TargetLowering::Custom) + Res = DAG.getNode(N->getOpcode(), dl, PromotedType, Op1Promoted, + Op2Promoted, N->getOperand(2)); + } + + if (!Res) + Res = earlyExpandDIVFIX(N, Op1Promoted, Op2Promoted, Scale, TLI, DAG); + + // TODO: Saturation. + + return Res; +} + SDValue DAGTypeLegalizer::PromoteIntRes_SADDSUBO(SDNode *N, unsigned ResNo) { if (ResNo == 1) return PromoteIntRes_Overflow(N); @@ -1208,7 +1276,9 @@ case ISD::SMULFIX: case ISD::SMULFIXSAT: case ISD::UMULFIX: - case ISD::UMULFIXSAT: Res = PromoteIntOp_MULFIX(N); break; + case ISD::UMULFIXSAT: + case ISD::SDIVFIX: + case ISD::UDIVFIX: Res = PromoteIntOp_FIX(N); break; case ISD::FPOWI: Res = PromoteIntOp_FPOWI(N); break; @@ -1583,7 +1653,7 @@ return SDValue(DAG.UpdateNodeOperands(N, LHS, RHS, Carry), 0); } -SDValue DAGTypeLegalizer::PromoteIntOp_MULFIX(SDNode *N) { +SDValue DAGTypeLegalizer::PromoteIntOp_FIX(SDNode *N) { SDValue Op2 = ZExtPromotedInteger(N->getOperand(2)); return SDValue( DAG.UpdateNodeOperands(N, N->getOperand(0), N->getOperand(1), Op2), 0); @@ -1793,6 +1863,9 @@ case ISD::UMULFIX: case ISD::UMULFIXSAT: ExpandIntRes_MULFIX(N, Lo, Hi); break; + case ISD::SDIVFIX: + case ISD::UDIVFIX: ExpandIntRes_DIVFIX(N, Lo, Hi); break; + case ISD::VECREDUCE_ADD: case ISD::VECREDUCE_MUL: case ISD::VECREDUCE_AND: @@ -3080,6 +3153,13 @@ Lo = DAG.getSelect(dl, NVT, SatMin, NVTZero, Lo); } +void DAGTypeLegalizer::ExpandIntRes_DIVFIX(SDNode *N, SDValue &Lo, + SDValue &Hi) { + SDValue Res = earlyExpandDIVFIX(N, N->getOperand(0), N->getOperand(1), + N->getConstantOperandVal(2), TLI, DAG); + SplitInteger(Res, Lo, Hi); +} + void DAGTypeLegalizer::ExpandIntRes_SADDSUBO(SDNode *Node, SDValue &Lo, SDValue &Hi) { SDValue LHS = Node->getOperand(0); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h @@ -337,6 +337,7 @@ SDValue PromoteIntRes_XMULO(SDNode *N, unsigned ResNo); SDValue PromoteIntRes_ADDSUBSAT(SDNode *N); SDValue PromoteIntRes_MULFIX(SDNode *N); + SDValue PromoteIntRes_DIVFIX(SDNode *N); SDValue PromoteIntRes_FLT_ROUNDS(SDNode *N); SDValue PromoteIntRes_VECREDUCE(SDNode *N); SDValue PromoteIntRes_ABS(SDNode *N); @@ -373,7 +374,7 @@ SDValue PromoteIntOp_ADDSUBCARRY(SDNode *N, unsigned OpNo); SDValue PromoteIntOp_FRAMERETURNADDR(SDNode *N); SDValue PromoteIntOp_PREFETCH(SDNode *N, unsigned OpNo); - SDValue PromoteIntOp_MULFIX(SDNode *N); + SDValue PromoteIntOp_FIX(SDNode *N); SDValue PromoteIntOp_FPOWI(SDNode *N); SDValue PromoteIntOp_VECREDUCE(SDNode *N); @@ -435,6 +436,7 @@ void ExpandIntRes_XMULO (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandIntRes_ADDSUBSAT (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandIntRes_MULFIX (SDNode *N, SDValue &Lo, SDValue &Hi); + void ExpandIntRes_DIVFIX (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandIntRes_ATOMIC_LOAD (SDNode *N, SDValue &Lo, SDValue &Hi); void ExpandIntRes_VECREDUCE (SDNode *N, SDValue &Lo, SDValue &Hi); @@ -690,7 +692,7 @@ SDValue ScalarizeVecRes_UNDEF(SDNode *N); SDValue ScalarizeVecRes_VECTOR_SHUFFLE(SDNode *N); - SDValue ScalarizeVecRes_MULFIX(SDNode *N); + SDValue ScalarizeVecRes_FIX(SDNode *N); // Vector Operand Scalarization: <1 x ty> -> ty. bool ScalarizeVectorOperand(SDNode *N, unsigned OpNo); @@ -732,7 +734,7 @@ void SplitVecRes_OverflowOp(SDNode *N, unsigned ResNo, SDValue &Lo, SDValue &Hi); - void SplitVecRes_MULFIX(SDNode *N, SDValue &Lo, SDValue &Hi); + void SplitVecRes_FIX(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_BITCAST(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_BUILD_VECTOR(SDNode *N, SDValue &Lo, SDValue &Hi); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp @@ -146,6 +146,7 @@ SDValue ExpandMULO(SDValue Op); SDValue ExpandAddSubSat(SDValue Op); SDValue ExpandFixedPointMul(SDValue Op); + SDValue ExpandFixedPointDiv(SDValue Op); SDValue ExpandStrictFPOp(SDValue Op); /// Implements vector promotion. @@ -454,7 +455,9 @@ case ISD::SMULFIX: case ISD::SMULFIXSAT: case ISD::UMULFIX: - case ISD::UMULFIXSAT: { + case ISD::UMULFIXSAT: + case ISD::SDIVFIX: + case ISD::UDIVFIX: { unsigned Scale = Node->getConstantOperandVal(2); Action = TLI.getFixedPointOperationAction(Node->getOpcode(), Node->getValueType(0), Scale); @@ -838,6 +841,9 @@ // targets? This should probably be investigated. And if we still prefer to // unroll an explanation could be helpful. return DAG.UnrollVectorOp(Op.getNode()); + case ISD::SDIVFIX: + case ISD::UDIVFIX: + return ExpandFixedPointDiv(Op); case ISD::STRICT_FADD: case ISD::STRICT_FSUB: case ISD::STRICT_FMUL: @@ -1365,6 +1371,14 @@ return DAG.UnrollVectorOp(Op.getNode()); } +SDValue VectorLegalizer::ExpandFixedPointDiv(SDValue Op) { + SDNode *N = Op.getNode(); + if (SDValue Expanded = TLI.expandFixedPointDiv(N->getOpcode(), SDLoc(N), + N->getOperand(0), N->getOperand(1), N->getConstantOperandVal(2), DAG)) + return Expanded; + return DAG.UnrollVectorOp(N); +} + SDValue VectorLegalizer::ExpandStrictFPOp(SDValue Op) { EVT VT = Op.getValueType(); EVT EltVT = VT.getVectorElementType(); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp @@ -187,7 +187,9 @@ case ISD::SMULFIXSAT: case ISD::UMULFIX: case ISD::UMULFIXSAT: - R = ScalarizeVecRes_MULFIX(N); + case ISD::SDIVFIX: + case ISD::UDIVFIX: + R = ScalarizeVecRes_FIX(N); break; } @@ -211,7 +213,7 @@ Op0.getValueType(), Op0, Op1, Op2); } -SDValue DAGTypeLegalizer::ScalarizeVecRes_MULFIX(SDNode *N) { +SDValue DAGTypeLegalizer::ScalarizeVecRes_FIX(SDNode *N) { SDValue Op0 = GetScalarizedVector(N->getOperand(0)); SDValue Op1 = GetScalarizedVector(N->getOperand(1)); SDValue Op2 = N->getOperand(2); @@ -1002,7 +1004,9 @@ case ISD::SMULFIXSAT: case ISD::UMULFIX: case ISD::UMULFIXSAT: - SplitVecRes_MULFIX(N, Lo, Hi); + case ISD::SDIVFIX: + case ISD::UDIVFIX: + SplitVecRes_FIX(N, Lo, Hi); break; } @@ -1041,7 +1045,7 @@ Op0Hi, Op1Hi, Op2Hi); } -void DAGTypeLegalizer::SplitVecRes_MULFIX(SDNode *N, SDValue &Lo, SDValue &Hi) { +void DAGTypeLegalizer::SplitVecRes_FIX(SDNode *N, SDValue &Lo, SDValue &Hi) { SDValue LHSLo, LHSHi; GetSplitVector(N->getOperand(0), LHSLo, LHSHi); SDValue RHSLo, RHSHi; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5386,6 +5386,60 @@ return DAG.getNode(ISD::FPOWI, DL, LHS.getValueType(), LHS, RHS); } +static SDValue expandDivFix(unsigned Opcode, const SDLoc &DL, + SDValue LHS, SDValue RHS, SDValue Scale, + SelectionDAG &DAG, const TargetLowering &TLI) { + EVT VT = LHS.getValueType(); + bool Signed = Opcode == ISD::SDIVFIX; + LLVMContext &Ctx = *DAG.getContext(); + + // If the type is legal but the operation isn't, this node might survive all + // the way to operation legalization. If we end up there and we do not have + // the ability to widen the type (if VT*2 is not legal), we cannot expand the + // node. + + // Coax the legalizer into expanding the node during type legalization instead + // by bumping the size by one bit. This will force it to Promote, enabling the + // early expansion and avoiding the need to expand later. + + // We don't have to do this if Scale is 0; that can always be expanded. + + // FIXME: We wouldn't have to do this (or any of the early + // expansion/promotion) if it was possible to expand a libcall of an + // illegal type during operation legalization. But it's not, so things + // get a bit hacky. + unsigned ScaleInt = cast(Scale)->getZExtValue(); + if (ScaleInt > 0 && + (TLI.isTypeLegal(VT) || + (VT.isVector() && TLI.isTypeLegal(VT.getVectorElementType())))) { + TargetLowering::LegalizeAction Action = TLI.getFixedPointOperationAction( + Opcode, VT, ScaleInt); + if (Action != TargetLowering::Legal && Action != TargetLowering::Custom) { + EVT PromVT; + if (VT.isScalarInteger()) + PromVT = EVT::getIntegerVT(Ctx, VT.getSizeInBits() + 1); + else if (VT.isVector()) { + PromVT = VT.getVectorElementType(); + PromVT = EVT::getIntegerVT(Ctx, PromVT.getSizeInBits() + 1); + PromVT = EVT::getVectorVT(Ctx, PromVT, VT.getVectorElementCount()); + } else + llvm_unreachable("Wrong VT for DIVFIX?"); + if (Signed) { + LHS = DAG.getSExtOrTrunc(LHS, DL, PromVT); + RHS = DAG.getSExtOrTrunc(RHS, DL, PromVT); + } else { + LHS = DAG.getZExtOrTrunc(LHS, DL, PromVT); + RHS = DAG.getZExtOrTrunc(RHS, DL, PromVT); + } + // TODO: Saturation. + SDValue Res = DAG.getNode(Opcode, DL, PromVT, LHS, RHS, Scale); + return DAG.getZExtOrTrunc(Res, DL, VT); + } + } + + return DAG.getNode(Opcode, DL, VT, LHS, RHS, Scale); +} + // getUnderlyingArgRegs - Find underlying registers used for a truncated, // bitcasted, or split argument. Returns a list of static void @@ -5621,6 +5675,14 @@ return ISD::SMULFIX; case Intrinsic::umul_fix: return ISD::UMULFIX; + case Intrinsic::smul_fix_sat: + return ISD::SMULFIXSAT; + case Intrinsic::umul_fix_sat: + return ISD::UMULFIXSAT; + case Intrinsic::sdiv_fix: + return ISD::SDIVFIX; + case Intrinsic::udiv_fix: + return ISD::UDIVFIX; default: llvm_unreachable("Unhandled fixed point intrinsic"); } @@ -6311,7 +6373,9 @@ return; } case Intrinsic::smul_fix: - case Intrinsic::umul_fix: { + case Intrinsic::umul_fix: + case Intrinsic::smul_fix_sat: + case Intrinsic::umul_fix_sat: { SDValue Op1 = getValue(I.getArgOperand(0)); SDValue Op2 = getValue(I.getArgOperand(1)); SDValue Op3 = getValue(I.getArgOperand(2)); @@ -6319,20 +6383,13 @@ Op1.getValueType(), Op1, Op2, Op3)); return; } - case Intrinsic::smul_fix_sat: { - SDValue Op1 = getValue(I.getArgOperand(0)); - SDValue Op2 = getValue(I.getArgOperand(1)); - SDValue Op3 = getValue(I.getArgOperand(2)); - setValue(&I, DAG.getNode(ISD::SMULFIXSAT, sdl, Op1.getValueType(), Op1, Op2, - Op3)); - return; - } - case Intrinsic::umul_fix_sat: { + case Intrinsic::sdiv_fix: + case Intrinsic::udiv_fix: { SDValue Op1 = getValue(I.getArgOperand(0)); SDValue Op2 = getValue(I.getArgOperand(1)); SDValue Op3 = getValue(I.getArgOperand(2)); - setValue(&I, DAG.getNode(ISD::UMULFIXSAT, sdl, Op1.getValueType(), Op1, Op2, - Op3)); + setValue(&I, expandDivFix(FixedPointIntrinsicToOpcode(Intrinsic), sdl, + Op1, Op2, Op3, DAG, TLI)); return; } case Intrinsic::stacksave: { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -308,6 +308,9 @@ case ISD::UMULFIX: return "umulfix"; case ISD::UMULFIXSAT: return "umulfixsat"; + case ISD::SDIVFIX: return "sdivfix"; + case ISD::UDIVFIX: return "udivfix"; + // Conversion operators. case ISD::SIGN_EXTEND: return "sign_extend"; case ISD::ZERO_EXTEND: return "zero_extend"; diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -7099,6 +7099,84 @@ return Result; } +SDValue +TargetLowering::expandFixedPointDiv(unsigned Opcode, const SDLoc &dl, + SDValue LHS, SDValue RHS, + unsigned Scale, SelectionDAG &DAG) const { + assert((Opcode == ISD::SDIVFIX || + Opcode == ISD::UDIVFIX) && + "Expected a fixed point division opcode"); + + EVT VT = LHS.getValueType(); + bool Signed = Opcode == ISD::SDIVFIX; + EVT BoolVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT); + + // If there is enough room in the type to upscale the LHS or downscale the + // RHS before the division, we can perform it in this type without having to + // resize. For signed operations, the LHS headroom is the number of + // redundant sign bits, and for unsigned ones it is the number of zeroes. + // The headroom for the RHS is the number of trailing zeroes. + unsigned LHSLead = Signed ? DAG.ComputeNumSignBits(LHS) - 1 + : DAG.computeKnownBits(LHS).countMinLeadingZeros(); + unsigned RHSTrail = DAG.computeKnownBits(RHS).countMinTrailingZeros(); + + if (LHSLead + RHSTrail < Scale) + return SDValue(); + + unsigned LHSShift = std::min(LHSLead, Scale); + unsigned RHSShift = Scale - LHSShift; + + // At this point, we know that if we shift the LHS up by LHSShift and the + // RHS down by RHSShift, we can emit a regular division with a final scaling + // factor of Scale. + + EVT ShiftTy = getShiftAmountTy(VT, DAG.getDataLayout()); + if (LHSShift) + LHS = DAG.getNode(ISD::SHL, dl, VT, LHS, + DAG.getConstant(LHSShift, dl, ShiftTy)); + if (RHSShift) + RHS = DAG.getNode(Signed ? ISD::SRA : ISD::SRL, dl, VT, RHS, + DAG.getConstant(RHSShift, dl, ShiftTy)); + + SDValue Quot; + if (Signed) { + // For signed operations, if the resulting quotient is negative and the + // remainder is nonzero, subtract 1 from the quotient to round towards + // negative infinity. + SDValue Rem; + // FIXME: Ideally we would always produce an SDIVREM here, but if the + // type isn't legal, SDIVREM cannot be expanded. There is no reason why + // we couldn't just form a libcall, but the type legalizer doesn't do it. + if (isTypeLegal(VT) && + isOperationLegalOrCustom(ISD::SDIVREM, VT)) { + Quot = DAG.getNode(ISD::SDIVREM, dl, + DAG.getVTList(VT, VT), + LHS, RHS); + Rem = Quot.getValue(1); + Quot = Quot.getValue(0); + } else { + Quot = DAG.getNode(ISD::SDIV, dl, VT, + LHS, RHS); + Rem = DAG.getNode(ISD::SREM, dl, VT, + LHS, RHS); + } + SDValue Zero = DAG.getConstant(0, dl, VT); + SDValue RemZero = DAG.getSetCC(dl, BoolVT, Rem, Zero, ISD::SETEQ); + SDValue QuotNeg = DAG.getSetCC(dl, BoolVT, Quot, Zero, ISD::SETLT); + SDValue Sub1 = DAG.getNode(ISD::SUB, dl, VT, Quot, + DAG.getConstant(1, dl, VT)); + Quot = DAG.getSelect(dl, VT, + DAG.getNode(ISD::AND, dl, BoolVT, RemZero, QuotNeg), + Sub1, Quot); + } else + Quot = DAG.getNode(ISD::UDIV, dl, VT, + LHS, RHS); + + // TODO: Saturation. + + return Quot; +} + void TargetLowering::expandUADDSUBO( SDNode *Node, SDValue &Result, SDValue &Overflow, SelectionDAG &DAG) const { SDLoc dl(Node); diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -654,6 +654,8 @@ setOperationAction(ISD::SMULFIXSAT, VT, Expand); setOperationAction(ISD::UMULFIX, VT, Expand); setOperationAction(ISD::UMULFIXSAT, VT, Expand); + setOperationAction(ISD::SDIVFIX, VT, Expand); + setOperationAction(ISD::UDIVFIX, VT, Expand); // Overflow operations default to expand setOperationAction(ISD::SADDO, VT, Expand); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -4697,28 +4697,32 @@ case Intrinsic::smul_fix: case Intrinsic::smul_fix_sat: case Intrinsic::umul_fix: - case Intrinsic::umul_fix_sat: { + case Intrinsic::umul_fix_sat: + case Intrinsic::sdiv_fix: + case Intrinsic::udiv_fix: { Value *Op1 = Call.getArgOperand(0); Value *Op2 = Call.getArgOperand(1); Assert(Op1->getType()->isIntOrIntVectorTy(), - "first operand of [us]mul_fix[_sat] must be an int type or vector " - "of ints"); + "first operand of [us][mul|div]_fix[_sat] must be an int type or " + "vector of ints"); Assert(Op2->getType()->isIntOrIntVectorTy(), - "second operand of [us]mul_fix_[sat] must be an int type or vector " - "of ints"); + "second operand of [us][mul|div]_fix[_sat] must be an int type or " + "vector of ints"); auto *Op3 = cast(Call.getArgOperand(2)); Assert(Op3->getType()->getBitWidth() <= 32, - "third argument of [us]mul_fix[_sat] must fit within 32 bits"); + "third argument of [us][mul|div]_fix[_sat] must fit within 32 bits"); - if (ID == Intrinsic::smul_fix || ID == Intrinsic::smul_fix_sat) { + if (ID == Intrinsic::smul_fix || ID == Intrinsic::smul_fix_sat || + ID == Intrinsic::sdiv_fix) { Assert( Op3->getZExtValue() < Op1->getType()->getScalarSizeInBits(), - "the scale of smul_fix[_sat] must be less than the width of the operands"); + "the scale of s[mul|div]_fix[_sat] must be less than the width of " + "the operands"); } else { Assert(Op3->getZExtValue() <= Op1->getType()->getScalarSizeInBits(), - "the scale of umul_fix[_sat] must be less than or equal to the width of " - "the operands"); + "the scale of u[mul|div]_fix[_sat] must be less than or equal " + "to the width of the operands"); } break; } diff --git a/llvm/test/CodeGen/X86/sdiv_fix.ll b/llvm/test/CodeGen/X86/sdiv_fix.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/sdiv_fix.ll @@ -0,0 +1,645 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-linux | FileCheck %s --check-prefix=X64 +; RUN: llc < %s -mtriple=i686 -mattr=cmov | FileCheck %s --check-prefix=X86 + +declare i4 @llvm.sdiv.fix.i4 (i4, i4, i32) +declare i15 @llvm.sdiv.fix.i15 (i15, i15, i32) +declare i16 @llvm.sdiv.fix.i16 (i16, i16, i32) +declare i18 @llvm.sdiv.fix.i18 (i18, i18, i32) +declare i64 @llvm.sdiv.fix.i64 (i64, i64, i32) +declare <4 x i32> @llvm.sdiv.fix.v4i32(<4 x i32>, <4 x i32>, i32) + +define i16 @func(i16 %x, i16 %y) nounwind { +; X64-LABEL: func: +; X64: # %bb.0: +; X64-NEXT: movswl %si, %ecx +; X64-NEXT: movswl %di, %eax +; X64-NEXT: shll $7, %eax +; X64-NEXT: cltd +; X64-NEXT: idivl %ecx +; X64-NEXT: # kill: def $eax killed $eax def $rax +; X64-NEXT: leal -1(%rax), %esi +; X64-NEXT: testl %eax, %eax +; X64-NEXT: sets %cl +; X64-NEXT: testl %edx, %edx +; X64-NEXT: sete %dl +; X64-NEXT: testb %cl, %dl +; X64-NEXT: cmovnel %esi, %eax +; X64-NEXT: # kill: def $ax killed $ax killed $rax +; X64-NEXT: retq +; +; X86-LABEL: func: +; X86: # %bb.0: +; X86-NEXT: pushl %ebx +; X86-NEXT: movswl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movswl {{[0-9]+}}(%esp), %eax +; X86-NEXT: shll $7, %eax +; X86-NEXT: cltd +; X86-NEXT: idivl %ecx +; X86-NEXT: leal -1(%eax), %ecx +; X86-NEXT: testl %eax, %eax +; X86-NEXT: sets %bl +; X86-NEXT: testl %edx, %edx +; X86-NEXT: sete %dl +; X86-NEXT: testb %bl, %dl +; X86-NEXT: cmovnel %ecx, %eax +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: popl %ebx +; X86-NEXT: retl + %tmp = call i16 @llvm.sdiv.fix.i16(i16 %x, i16 %y, i32 7) + ret i16 %tmp +} + +define i16 @func2(i8 %x, i8 %y) nounwind { +; X64-LABEL: func2: +; X64: # %bb.0: +; X64-NEXT: movsbl %dil, %eax +; X64-NEXT: movsbl %sil, %ecx +; X64-NEXT: movswl %cx, %ecx +; X64-NEXT: cwtl +; X64-NEXT: shll $14, %eax +; X64-NEXT: cltd +; X64-NEXT: idivl %ecx +; X64-NEXT: # kill: def $eax killed $eax def $rax +; X64-NEXT: leal -1(%rax), %ecx +; X64-NEXT: testl %eax, %eax +; X64-NEXT: sets %sil +; X64-NEXT: testl %edx, %edx +; X64-NEXT: sete %dl +; X64-NEXT: testb %sil, %dl +; X64-NEXT: cmovel %eax, %ecx +; X64-NEXT: addl %ecx, %ecx +; X64-NEXT: movswl %cx, %eax +; X64-NEXT: shrl %eax +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: retq +; +; X86-LABEL: func2: +; X86: # %bb.0: +; X86-NEXT: pushl %ebx +; X86-NEXT: movsbl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movsbl {{[0-9]+}}(%esp), %eax +; X86-NEXT: shll $14, %eax +; X86-NEXT: cltd +; X86-NEXT: idivl %ecx +; X86-NEXT: leal -1(%eax), %ecx +; X86-NEXT: testl %eax, %eax +; X86-NEXT: sets %bl +; X86-NEXT: testl %edx, %edx +; X86-NEXT: sete %dl +; X86-NEXT: testb %bl, %dl +; X86-NEXT: cmovel %eax, %ecx +; X86-NEXT: addl %ecx, %ecx +; X86-NEXT: movswl %cx, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: popl %ebx +; X86-NEXT: retl + %x2 = sext i8 %x to i15 + %y2 = sext i8 %y to i15 + %tmp = call i15 @llvm.sdiv.fix.i15(i15 %x2, i15 %y2, i32 14) + %tmp2 = sext i15 %tmp to i16 + ret i16 %tmp2 +} + +define i16 @func3(i15 %x, i8 %y) nounwind { +; X64-LABEL: func3: +; X64: # %bb.0: +; X64-NEXT: # kill: def $edi killed $edi def $rdi +; X64-NEXT: shll $8, %esi +; X64-NEXT: movswl %si, %ecx +; X64-NEXT: leal (%rdi,%rdi), %eax +; X64-NEXT: shrl $4, %ecx +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: cwtd +; X64-NEXT: idivw %cx +; X64-NEXT: # kill: def $ax killed $ax def $rax +; X64-NEXT: leal -1(%rax), %ecx +; X64-NEXT: testw %ax, %ax +; X64-NEXT: sets %sil +; X64-NEXT: testw %dx, %dx +; X64-NEXT: sete %dl +; X64-NEXT: testb %sil, %dl +; X64-NEXT: cmovel %eax, %ecx +; X64-NEXT: addl %ecx, %ecx +; X64-NEXT: movswl %cx, %eax +; X64-NEXT: shrl %eax +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: retq +; +; X86-LABEL: func3: +; X86: # %bb.0: +; X86-NEXT: pushl %ebx +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: shll $8, %ecx +; X86-NEXT: movswl %cx, %ecx +; X86-NEXT: addl %eax, %eax +; X86-NEXT: shrl $4, %ecx +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: cwtd +; X86-NEXT: idivw %cx +; X86-NEXT: # kill: def $ax killed $ax def $eax +; X86-NEXT: leal -1(%eax), %ecx +; X86-NEXT: testw %ax, %ax +; X86-NEXT: sets %bl +; X86-NEXT: testw %dx, %dx +; X86-NEXT: sete %dl +; X86-NEXT: testb %bl, %dl +; X86-NEXT: cmovel %eax, %ecx +; X86-NEXT: addl %ecx, %ecx +; X86-NEXT: movswl %cx, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: popl %ebx +; X86-NEXT: retl + %y2 = sext i8 %y to i15 + %y3 = shl i15 %y2, 7 + %tmp = call i15 @llvm.sdiv.fix.i15(i15 %x, i15 %y3, i32 4) + %tmp2 = sext i15 %tmp to i16 + ret i16 %tmp2 +} + +define i4 @func4(i4 %x, i4 %y) nounwind { +; X64-LABEL: func4: +; X64: # %bb.0: +; X64-NEXT: shlb $4, %sil +; X64-NEXT: sarb $4, %sil +; X64-NEXT: shlb $4, %dil +; X64-NEXT: sarb $4, %dil +; X64-NEXT: shlb $2, %dil +; X64-NEXT: movsbl %dil, %eax +; X64-NEXT: idivb %sil +; X64-NEXT: movsbl %ah, %esi +; X64-NEXT: movzbl %al, %edx +; X64-NEXT: leal -1(%rdx), %eax +; X64-NEXT: movzbl %al, %eax +; X64-NEXT: testb %dl, %dl +; X64-NEXT: sets %dil +; X64-NEXT: testb %sil, %sil +; X64-NEXT: sete %cl +; X64-NEXT: testb %dil, %cl +; X64-NEXT: cmovel %edx, %eax +; X64-NEXT: # kill: def $al killed $al killed $eax +; X64-NEXT: retq +; +; X86-LABEL: func4: +; X86: # %bb.0: +; X86-NEXT: pushl %esi +; X86-NEXT: movb {{[0-9]+}}(%esp), %cl +; X86-NEXT: shlb $4, %cl +; X86-NEXT: sarb $4, %cl +; X86-NEXT: movb {{[0-9]+}}(%esp), %al +; X86-NEXT: shlb $4, %al +; X86-NEXT: sarb $4, %al +; X86-NEXT: shlb $2, %al +; X86-NEXT: cbtw +; X86-NEXT: idivb %cl +; X86-NEXT: movl %eax, %ecx +; X86-NEXT: movsbl %ah, %edx +; X86-NEXT: movzbl %al, %esi +; X86-NEXT: decb %al +; X86-NEXT: movzbl %al, %eax +; X86-NEXT: testb %cl, %cl +; X86-NEXT: sets %cl +; X86-NEXT: testb %dl, %dl +; X86-NEXT: sete %dl +; X86-NEXT: testb %cl, %dl +; X86-NEXT: cmovel %esi, %eax +; X86-NEXT: # kill: def $al killed $al killed $eax +; X86-NEXT: popl %esi +; X86-NEXT: retl + %tmp = call i4 @llvm.sdiv.fix.i4(i4 %x, i4 %y, i32 2) + ret i4 %tmp +} + +define i64 @func5(i64 %x, i64 %y) nounwind { +; X64-LABEL: func5: +; X64: # %bb.0: +; X64-NEXT: pushq %rbp +; X64-NEXT: pushq %r15 +; X64-NEXT: pushq %r14 +; X64-NEXT: pushq %r13 +; X64-NEXT: pushq %r12 +; X64-NEXT: pushq %rbx +; X64-NEXT: pushq %rax +; X64-NEXT: movq %rsi, %r14 +; X64-NEXT: movq %rdi, %rbx +; X64-NEXT: movq %rdi, %r15 +; X64-NEXT: sarq $63, %r15 +; X64-NEXT: shldq $31, %rdi, %r15 +; X64-NEXT: shlq $31, %rbx +; X64-NEXT: movq %rsi, %r12 +; X64-NEXT: sarq $63, %r12 +; X64-NEXT: movq %rbx, %rdi +; X64-NEXT: movq %r15, %rsi +; X64-NEXT: movq %r14, %rdx +; X64-NEXT: movq %r12, %rcx +; X64-NEXT: callq __divti3 +; X64-NEXT: movq %rax, %r13 +; X64-NEXT: decq %rax +; X64-NEXT: movq %rax, (%rsp) # 8-byte Spill +; X64-NEXT: testq %rdx, %rdx +; X64-NEXT: sets %bpl +; X64-NEXT: movq %rbx, %rdi +; X64-NEXT: movq %r15, %rsi +; X64-NEXT: movq %r14, %rdx +; X64-NEXT: movq %r12, %rcx +; X64-NEXT: callq __modti3 +; X64-NEXT: orq %rax, %rdx +; X64-NEXT: sete %al +; X64-NEXT: testb %bpl, %al +; X64-NEXT: cmovneq (%rsp), %r13 # 8-byte Folded Reload +; X64-NEXT: movq %r13, %rax +; X64-NEXT: addq $8, %rsp +; X64-NEXT: popq %rbx +; X64-NEXT: popq %r12 +; X64-NEXT: popq %r13 +; X64-NEXT: popq %r14 +; X64-NEXT: popq %r15 +; X64-NEXT: popq %rbp +; X64-NEXT: retq +; +; X86-LABEL: func5: +; X86: # %bb.0: +; X86-NEXT: pushl %ebp +; X86-NEXT: movl %esp, %ebp +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %esi +; X86-NEXT: andl $-8, %esp +; X86-NEXT: subl $64, %esp +; X86-NEXT: movl 8(%ebp), %ecx +; X86-NEXT: movl 12(%ebp), %edx +; X86-NEXT: movl 20(%ebp), %ebx +; X86-NEXT: sarl $31, %ebx +; X86-NEXT: movl %edx, %eax +; X86-NEXT: shldl $31, %ecx, %eax +; X86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: shll $31, %ecx +; X86-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %edx, %edi +; X86-NEXT: sarl $31, %edi +; X86-NEXT: movl %edi, %esi +; X86-NEXT: shldl $31, %edx, %edi +; X86-NEXT: leal {{[0-9]+}}(%esp), %edx +; X86-NEXT: rorl %esi +; X86-NEXT: movl %esi, (%esp) # 4-byte Spill +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl 20(%ebp) +; X86-NEXT: pushl 16(%ebp) +; X86-NEXT: pushl %esi +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %eax +; X86-NEXT: pushl %ecx +; X86-NEXT: pushl %edx +; X86-NEXT: calll __divti3 +; X86-NEXT: addl $32, %esp +; X86-NEXT: leal {{[0-9]+}}(%esp), %eax +; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl {{[0-9]+}}(%esp), %esi +; X86-NEXT: movl %esi, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: subl $1, %ecx +; X86-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: sbbl $0, %esi +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl 20(%ebp) +; X86-NEXT: pushl 16(%ebp) +; X86-NEXT: pushl {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: pushl %edi +; X86-NEXT: pushl {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: pushl {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: pushl %eax +; X86-NEXT: calll __modti3 +; X86-NEXT: addl $32, %esp +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: orl {{[0-9]+}}(%esp), %eax +; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: orl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: orl %eax, %ecx +; X86-NEXT: sete %al +; X86-NEXT: cmpl $0, {{[0-9]+}}(%esp) +; X86-NEXT: sets %cl +; X86-NEXT: testb %cl, %al +; X86-NEXT: cmovel {{[-0-9]+}}(%e{{[sb]}}p), %esi # 4-byte Folded Reload +; X86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload +; X86-NEXT: cmovel {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Folded Reload +; X86-NEXT: movl %esi, %edx +; X86-NEXT: leal -12(%ebp), %esp +; X86-NEXT: popl %esi +; X86-NEXT: popl %edi +; X86-NEXT: popl %ebx +; X86-NEXT: popl %ebp +; X86-NEXT: retl + %tmp = call i64 @llvm.sdiv.fix.i64(i64 %x, i64 %y, i32 31) + ret i64 %tmp +} + +define i18 @func6(i16 %x, i16 %y) nounwind { +; X64-LABEL: func6: +; X64: # %bb.0: +; X64-NEXT: movswl %di, %eax +; X64-NEXT: movswl %si, %ecx +; X64-NEXT: shll $7, %eax +; X64-NEXT: cltd +; X64-NEXT: idivl %ecx +; X64-NEXT: # kill: def $eax killed $eax def $rax +; X64-NEXT: leal -1(%rax), %esi +; X64-NEXT: testl %eax, %eax +; X64-NEXT: sets %cl +; X64-NEXT: testl %edx, %edx +; X64-NEXT: sete %dl +; X64-NEXT: testb %cl, %dl +; X64-NEXT: cmovnel %esi, %eax +; X64-NEXT: # kill: def $eax killed $eax killed $rax +; X64-NEXT: retq +; +; X86-LABEL: func6: +; X86: # %bb.0: +; X86-NEXT: pushl %ebx +; X86-NEXT: movswl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movswl {{[0-9]+}}(%esp), %eax +; X86-NEXT: shll $7, %eax +; X86-NEXT: cltd +; X86-NEXT: idivl %ecx +; X86-NEXT: leal -1(%eax), %ecx +; X86-NEXT: testl %eax, %eax +; X86-NEXT: sets %bl +; X86-NEXT: testl %edx, %edx +; X86-NEXT: sete %dl +; X86-NEXT: testb %bl, %dl +; X86-NEXT: cmovnel %ecx, %eax +; X86-NEXT: popl %ebx +; X86-NEXT: retl + %x2 = sext i16 %x to i18 + %y2 = sext i16 %y to i18 + %tmp = call i18 @llvm.sdiv.fix.i18(i18 %x2, i18 %y2, i32 7) + ret i18 %tmp +} + +define <4 x i32> @vec(<4 x i32> %x, <4 x i32> %y) nounwind { +; X64-LABEL: vec: +; X64: # %bb.0: +; X64-NEXT: pxor %xmm2, %xmm2 +; X64-NEXT: pcmpgtd %xmm1, %xmm2 +; X64-NEXT: pshufd {{.*#+}} xmm3 = xmm1[2,3,0,1] +; X64-NEXT: punpckldq {{.*#+}} xmm1 = xmm1[0],xmm2[0],xmm1[1],xmm2[1] +; X64-NEXT: movq %xmm1, %rcx +; X64-NEXT: pxor %xmm2, %xmm2 +; X64-NEXT: pcmpgtd %xmm0, %xmm2 +; X64-NEXT: pshufd {{.*#+}} xmm4 = xmm0[2,3,0,1] +; X64-NEXT: punpckldq {{.*#+}} xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1] +; X64-NEXT: psllq $31, %xmm0 +; X64-NEXT: movq %xmm0, %rax +; X64-NEXT: cqto +; X64-NEXT: idivq %rcx +; X64-NEXT: movq %rax, %r8 +; X64-NEXT: movq %rdx, %r10 +; X64-NEXT: pshufd {{.*#+}} xmm1 = xmm1[2,3,0,1] +; X64-NEXT: movq %xmm1, %rcx +; X64-NEXT: pshufd {{.*#+}} xmm0 = xmm0[2,3,0,1] +; X64-NEXT: movq %xmm0, %rax +; X64-NEXT: cqto +; X64-NEXT: idivq %rcx +; X64-NEXT: movq %rax, %rdi +; X64-NEXT: movq %rdx, %rcx +; X64-NEXT: pxor %xmm0, %xmm0 +; X64-NEXT: pcmpgtd %xmm3, %xmm0 +; X64-NEXT: punpckldq {{.*#+}} xmm3 = xmm3[0],xmm0[0],xmm3[1],xmm0[1] +; X64-NEXT: movq %xmm3, %rsi +; X64-NEXT: pxor %xmm0, %xmm0 +; X64-NEXT: pcmpgtd %xmm4, %xmm0 +; X64-NEXT: punpckldq {{.*#+}} xmm4 = xmm4[0],xmm0[0],xmm4[1],xmm0[1] +; X64-NEXT: psllq $31, %xmm4 +; X64-NEXT: movq %xmm4, %rax +; X64-NEXT: cqto +; X64-NEXT: idivq %rsi +; X64-NEXT: movq %rax, %r9 +; X64-NEXT: movq %rdx, %r11 +; X64-NEXT: pshufd {{.*#+}} xmm0 = xmm3[2,3,0,1] +; X64-NEXT: movq %xmm0, %rsi +; X64-NEXT: pshufd {{.*#+}} xmm0 = xmm4[2,3,0,1] +; X64-NEXT: movq %xmm0, %rax +; X64-NEXT: cqto +; X64-NEXT: idivq %rsi +; X64-NEXT: movq %r10, %xmm1 +; X64-NEXT: movq %rcx, %xmm2 +; X64-NEXT: movq %r8, %xmm0 +; X64-NEXT: movq %rdi, %xmm3 +; X64-NEXT: pxor %xmm4, %xmm4 +; X64-NEXT: punpcklqdq {{.*#+}} xmm1 = xmm1[0],xmm2[0] +; X64-NEXT: pcmpeqd %xmm4, %xmm1 +; X64-NEXT: pshufd {{.*#+}} xmm2 = xmm1[1,0,3,2] +; X64-NEXT: punpcklqdq {{.*#+}} xmm0 = xmm0[0],xmm3[0] +; X64-NEXT: movdqa {{.*#+}} xmm3 = [2147483648,2147483648] +; X64-NEXT: movdqa %xmm0, %xmm5 +; X64-NEXT: pxor %xmm3, %xmm5 +; X64-NEXT: movdqa %xmm3, %xmm6 +; X64-NEXT: pcmpgtd %xmm5, %xmm6 +; X64-NEXT: pshufd {{.*#+}} xmm7 = xmm6[0,0,2,2] +; X64-NEXT: pcmpeqd %xmm3, %xmm5 +; X64-NEXT: pshufd {{.*#+}} xmm5 = xmm5[1,1,3,3] +; X64-NEXT: pand %xmm7, %xmm5 +; X64-NEXT: pshufd {{.*#+}} xmm6 = xmm6[1,1,3,3] +; X64-NEXT: por %xmm5, %xmm6 +; X64-NEXT: pand %xmm2, %xmm6 +; X64-NEXT: pand %xmm1, %xmm6 +; X64-NEXT: movdqa %xmm6, %xmm1 +; X64-NEXT: pandn %xmm0, %xmm1 +; X64-NEXT: pcmpeqd %xmm8, %xmm8 +; X64-NEXT: paddq %xmm8, %xmm0 +; X64-NEXT: pand %xmm6, %xmm0 +; X64-NEXT: por %xmm1, %xmm0 +; X64-NEXT: movq %r11, %xmm1 +; X64-NEXT: movq %rdx, %xmm5 +; X64-NEXT: punpcklqdq {{.*#+}} xmm1 = xmm1[0],xmm5[0] +; X64-NEXT: pcmpeqd %xmm4, %xmm1 +; X64-NEXT: pshufd {{.*#+}} xmm4 = xmm1[1,0,3,2] +; X64-NEXT: movq %r9, %xmm5 +; X64-NEXT: movq %rax, %xmm6 +; X64-NEXT: punpcklqdq {{.*#+}} xmm5 = xmm5[0],xmm6[0] +; X64-NEXT: movdqa %xmm5, %xmm6 +; X64-NEXT: pxor %xmm3, %xmm6 +; X64-NEXT: movdqa %xmm3, %xmm7 +; X64-NEXT: pcmpgtd %xmm6, %xmm7 +; X64-NEXT: pshufd {{.*#+}} xmm2 = xmm7[0,0,2,2] +; X64-NEXT: pcmpeqd %xmm3, %xmm6 +; X64-NEXT: pshufd {{.*#+}} xmm3 = xmm6[1,1,3,3] +; X64-NEXT: pand %xmm2, %xmm3 +; X64-NEXT: pshufd {{.*#+}} xmm2 = xmm7[1,1,3,3] +; X64-NEXT: por %xmm3, %xmm2 +; X64-NEXT: pand %xmm4, %xmm2 +; X64-NEXT: pand %xmm1, %xmm2 +; X64-NEXT: movdqa %xmm2, %xmm1 +; X64-NEXT: pandn %xmm5, %xmm1 +; X64-NEXT: paddq %xmm8, %xmm5 +; X64-NEXT: pand %xmm2, %xmm5 +; X64-NEXT: por %xmm1, %xmm5 +; X64-NEXT: shufps {{.*#+}} xmm0 = xmm0[0,2],xmm5[0,2] +; X64-NEXT: retq +; +; X86-LABEL: vec: +; X86: # %bb.0: +; X86-NEXT: pushl %ebp +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %esi +; X86-NEXT: subl $56, %esp +; X86-NEXT: movl {{[0-9]+}}(%esp), %esi +; X86-NEXT: movl {{[0-9]+}}(%esp), %ebx +; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: movl %eax, %ebp +; X86-NEXT: movl %eax, %edi +; X86-NEXT: sarl $31, %ebp +; X86-NEXT: movl %ebp, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %ecx, %eax +; X86-NEXT: shll $31, %eax +; X86-NEXT: movl %eax, %edx +; X86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %ecx, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: andl $-2147483648, %ecx # imm = 0x80000000 +; X86-NEXT: orl %eax, %ecx +; X86-NEXT: movl %ecx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: pushl %ebp +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %ecx +; X86-NEXT: pushl %edx +; X86-NEXT: calll __divdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movl %ecx, %ebp +; X86-NEXT: sarl $31, %ebp +; X86-NEXT: movl %ebx, %edi +; X86-NEXT: shll $31, %edi +; X86-NEXT: movl %ebx, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: andl $-2147483648, %ebx # imm = 0x80000000 +; X86-NEXT: orl %eax, %ebx +; X86-NEXT: pushl %ebp +; X86-NEXT: pushl %ecx +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl %edi +; X86-NEXT: calll __moddi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: pushl %ebp +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl %edi +; X86-NEXT: calll __divdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movl %ecx, %edi +; X86-NEXT: sarl $31, %edi +; X86-NEXT: movl %esi, %ebx +; X86-NEXT: shll $31, %ebx +; X86-NEXT: movl %esi, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: andl $-2147483648, %esi # imm = 0x80000000 +; X86-NEXT: orl %eax, %esi +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %ecx +; X86-NEXT: movl %ecx, %ebp +; X86-NEXT: pushl %esi +; X86-NEXT: pushl %ebx +; X86-NEXT: calll __moddi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %ebp +; X86-NEXT: pushl %esi +; X86-NEXT: pushl %ebx +; X86-NEXT: calll __divdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl %edx, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; X86-NEXT: movl {{[0-9]+}}(%esp), %edx +; X86-NEXT: movl %edx, %edi +; X86-NEXT: sarl $31, %edi +; X86-NEXT: movl {{[0-9]+}}(%esp), %esi +; X86-NEXT: movl %esi, %ebp +; X86-NEXT: shll $31, %ebp +; X86-NEXT: movl %esi, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: andl $-2147483648, %esi # imm = 0x80000000 +; X86-NEXT: orl %eax, %esi +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %edx +; X86-NEXT: pushl %esi +; X86-NEXT: pushl %ebp +; X86-NEXT: calll __moddi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, (%esp) # 4-byte Spill +; X86-NEXT: movl %edx, %ebx +; X86-NEXT: pushl %edi +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl %esi +; X86-NEXT: pushl %ebp +; X86-NEXT: calll __divdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: testl %edx, %edx +; X86-NEXT: sets %cl +; X86-NEXT: orl (%esp), %ebx # 4-byte Folded Reload +; X86-NEXT: sete %dl +; X86-NEXT: testb %cl, %dl +; X86-NEXT: leal -1(%eax), %ecx +; X86-NEXT: cmovel %eax, %ecx +; X86-NEXT: movl %ecx, (%esp) # 4-byte Spill +; X86-NEXT: cmpl $0, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: sets %al +; X86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload +; X86-NEXT: orl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Folded Reload +; X86-NEXT: sete %cl +; X86-NEXT: testb %al, %cl +; X86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload +; X86-NEXT: leal -1(%eax), %edi +; X86-NEXT: cmovel %eax, %edi +; X86-NEXT: cmpl $0, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: sets %al +; X86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload +; X86-NEXT: orl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Folded Reload +; X86-NEXT: sete %cl +; X86-NEXT: testb %al, %cl +; X86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload +; X86-NEXT: leal -1(%eax), %ebx +; X86-NEXT: cmovel %eax, %ebx +; X86-NEXT: cmpl $0, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %esi # 4-byte Reload +; X86-NEXT: leal -1(%esi), %ebp +; X86-NEXT: sets {{[-0-9]+}}(%e{{[sb]}}p) # 1-byte Folded Spill +; X86-NEXT: pushl {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: pushl {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Folded Reload +; X86-NEXT: calll __moddi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: orl %eax, %edx +; X86-NEXT: sete %al +; X86-NEXT: testb %al, {{[-0-9]+}}(%e{{[sb]}}p) # 1-byte Folded Reload +; X86-NEXT: cmovel %esi, %ebp +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: movl %ebp, 12(%eax) +; X86-NEXT: movl %ebx, 8(%eax) +; X86-NEXT: movl %edi, 4(%eax) +; X86-NEXT: movl (%esp), %ecx # 4-byte Reload +; X86-NEXT: movl %ecx, (%eax) +; X86-NEXT: addl $56, %esp +; X86-NEXT: popl %esi +; X86-NEXT: popl %edi +; X86-NEXT: popl %ebx +; X86-NEXT: popl %ebp +; X86-NEXT: retl $4 + %tmp = call <4 x i32> @llvm.sdiv.fix.v4i32(<4 x i32> %x, <4 x i32> %y, i32 31) + ret <4 x i32> %tmp +} diff --git a/llvm/test/CodeGen/X86/udiv_fix.ll b/llvm/test/CodeGen/X86/udiv_fix.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/udiv_fix.ll @@ -0,0 +1,344 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-linux | FileCheck %s --check-prefix=X64 +; RUN: llc < %s -mtriple=i686 -mattr=cmov | FileCheck %s --check-prefix=X86 + +declare i4 @llvm.udiv.fix.i4 (i4, i4, i32) +declare i15 @llvm.udiv.fix.i15 (i15, i15, i32) +declare i16 @llvm.udiv.fix.i16 (i16, i16, i32) +declare i18 @llvm.udiv.fix.i18 (i18, i18, i32) +declare i64 @llvm.udiv.fix.i64 (i64, i64, i32) +declare <4 x i32> @llvm.udiv.fix.v4i32(<4 x i32>, <4 x i32>, i32) + +define i16 @func(i16 %x, i16 %y) nounwind { +; X64-LABEL: func: +; X64: # %bb.0: +; X64-NEXT: movzwl %si, %ecx +; X64-NEXT: movzwl %di, %eax +; X64-NEXT: shll $7, %eax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divl %ecx +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: retq +; +; X86-LABEL: func: +; X86: # %bb.0: +; X86-NEXT: movzwl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; X86-NEXT: shll $7, %eax +; X86-NEXT: xorl %edx, %edx +; X86-NEXT: divl %ecx +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: retl + %tmp = call i16 @llvm.udiv.fix.i16(i16 %x, i16 %y, i32 7) + ret i16 %tmp +} + +define i16 @func2(i8 %x, i8 %y) nounwind { +; X64-LABEL: func2: +; X64: # %bb.0: +; X64-NEXT: movsbl %dil, %eax +; X64-NEXT: andl $32767, %eax # imm = 0x7FFF +; X64-NEXT: movsbl %sil, %ecx +; X64-NEXT: andl $32767, %ecx # imm = 0x7FFF +; X64-NEXT: shll $14, %eax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divl %ecx +; X64-NEXT: addl %eax, %eax +; X64-NEXT: cwtl +; X64-NEXT: shrl %eax +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: retq +; +; X86-LABEL: func2: +; X86: # %bb.0: +; X86-NEXT: movsbl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: andl $32767, %ecx # imm = 0x7FFF +; X86-NEXT: movsbl {{[0-9]+}}(%esp), %eax +; X86-NEXT: andl $32767, %eax # imm = 0x7FFF +; X86-NEXT: shll $14, %eax +; X86-NEXT: xorl %edx, %edx +; X86-NEXT: divl %ecx +; X86-NEXT: addl %eax, %eax +; X86-NEXT: cwtl +; X86-NEXT: shrl %eax +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: retl + %x2 = sext i8 %x to i15 + %y2 = sext i8 %y to i15 + %tmp = call i15 @llvm.udiv.fix.i15(i15 %x2, i15 %y2, i32 14) + %tmp2 = sext i15 %tmp to i16 + ret i16 %tmp2 +} + +define i16 @func3(i15 %x, i8 %y) nounwind { +; X64-LABEL: func3: +; X64: # %bb.0: +; X64-NEXT: # kill: def $edi killed $edi def $rdi +; X64-NEXT: leal (%rdi,%rdi), %eax +; X64-NEXT: movzbl %sil, %ecx +; X64-NEXT: shll $4, %ecx +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divw %cx +; X64-NEXT: # kill: def $ax killed $ax def $eax +; X64-NEXT: addl %eax, %eax +; X64-NEXT: cwtl +; X64-NEXT: shrl %eax +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: retq +; +; X86-LABEL: func3: +; X86: # %bb.0: +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: addl %eax, %eax +; X86-NEXT: movzbl %cl, %ecx +; X86-NEXT: shll $4, %ecx +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: xorl %edx, %edx +; X86-NEXT: divw %cx +; X86-NEXT: # kill: def $ax killed $ax def $eax +; X86-NEXT: addl %eax, %eax +; X86-NEXT: cwtl +; X86-NEXT: shrl %eax +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: retl + %y2 = sext i8 %y to i15 + %y3 = shl i15 %y2, 7 + %tmp = call i15 @llvm.udiv.fix.i15(i15 %x, i15 %y3, i32 4) + %tmp2 = sext i15 %tmp to i16 + ret i16 %tmp2 +} + +define i4 @func4(i4 %x, i4 %y) nounwind { +; X64-LABEL: func4: +; X64: # %bb.0: +; X64-NEXT: andb $15, %sil +; X64-NEXT: andb $15, %dil +; X64-NEXT: shlb $2, %dil +; X64-NEXT: movzbl %dil, %eax +; X64-NEXT: divb %sil +; X64-NEXT: retq +; +; X86-LABEL: func4: +; X86: # %bb.0: +; X86-NEXT: movb {{[0-9]+}}(%esp), %cl +; X86-NEXT: andb $15, %cl +; X86-NEXT: movb {{[0-9]+}}(%esp), %al +; X86-NEXT: andb $15, %al +; X86-NEXT: shlb $2, %al +; X86-NEXT: movzbl %al, %eax +; X86-NEXT: divb %cl +; X86-NEXT: retl + %tmp = call i4 @llvm.udiv.fix.i4(i4 %x, i4 %y, i32 2) + ret i4 %tmp +} + +define i64 @func5(i64 %x, i64 %y) nounwind { +; X64-LABEL: func5: +; X64: # %bb.0: +; X64-NEXT: pushq %rax +; X64-NEXT: movq %rsi, %rdx +; X64-NEXT: movq %rdi, %rsi +; X64-NEXT: shlq $31, %rdi +; X64-NEXT: shrq $33, %rsi +; X64-NEXT: xorl %ecx, %ecx +; X64-NEXT: callq __udivti3 +; X64-NEXT: popq %rcx +; X64-NEXT: retq +; +; X86-LABEL: func5: +; X86: # %bb.0: +; X86-NEXT: pushl %ebp +; X86-NEXT: movl %esp, %ebp +; X86-NEXT: pushl %esi +; X86-NEXT: andl $-8, %esp +; X86-NEXT: subl $24, %esp +; X86-NEXT: movl 8(%ebp), %eax +; X86-NEXT: movl 12(%ebp), %ecx +; X86-NEXT: movl %ecx, %edx +; X86-NEXT: shrl %edx +; X86-NEXT: shldl $31, %eax, %ecx +; X86-NEXT: shll $31, %eax +; X86-NEXT: movl %esp, %esi +; X86-NEXT: pushl $0 +; X86-NEXT: pushl $0 +; X86-NEXT: pushl 20(%ebp) +; X86-NEXT: pushl 16(%ebp) +; X86-NEXT: pushl $0 +; X86-NEXT: pushl %edx +; X86-NEXT: pushl %ecx +; X86-NEXT: pushl %eax +; X86-NEXT: pushl %esi +; X86-NEXT: calll __udivti3 +; X86-NEXT: addl $32, %esp +; X86-NEXT: movl (%esp), %eax +; X86-NEXT: movl {{[0-9]+}}(%esp), %edx +; X86-NEXT: leal -4(%ebp), %esp +; X86-NEXT: popl %esi +; X86-NEXT: popl %ebp +; X86-NEXT: retl + %tmp = call i64 @llvm.udiv.fix.i64(i64 %x, i64 %y, i32 31) + ret i64 %tmp +} + +define i18 @func6(i16 %x, i16 %y) nounwind { +; X64-LABEL: func6: +; X64: # %bb.0: +; X64-NEXT: movswl %di, %eax +; X64-NEXT: andl $262143, %eax # imm = 0x3FFFF +; X64-NEXT: movswl %si, %ecx +; X64-NEXT: andl $262143, %ecx # imm = 0x3FFFF +; X64-NEXT: shll $7, %eax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divl %ecx +; X64-NEXT: retq +; +; X86-LABEL: func6: +; X86: # %bb.0: +; X86-NEXT: movswl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: andl $262143, %ecx # imm = 0x3FFFF +; X86-NEXT: movswl {{[0-9]+}}(%esp), %eax +; X86-NEXT: andl $262143, %eax # imm = 0x3FFFF +; X86-NEXT: shll $7, %eax +; X86-NEXT: xorl %edx, %edx +; X86-NEXT: divl %ecx +; X86-NEXT: retl + %x2 = sext i16 %x to i18 + %y2 = sext i16 %y to i18 + %tmp = call i18 @llvm.udiv.fix.i18(i18 %x2, i18 %y2, i32 7) + ret i18 %tmp +} + +define i16 @func7(i16 %x, i16 %y) nounwind { +; X64-LABEL: func7: +; X64: # %bb.0: +; X64-NEXT: movl %edi, %eax +; X64-NEXT: shll $16, %eax +; X64-NEXT: movzwl %si, %ecx +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divl %ecx +; X64-NEXT: # kill: def $ax killed $ax killed $eax +; X64-NEXT: retq +; +; X86-LABEL: func7: +; X86: # %bb.0: +; X86-NEXT: movzwl {{[0-9]+}}(%esp), %ecx +; X86-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; X86-NEXT: shll $16, %eax +; X86-NEXT: xorl %edx, %edx +; X86-NEXT: divl %ecx +; X86-NEXT: # kill: def $ax killed $ax killed $eax +; X86-NEXT: retl + %tmp = call i16 @llvm.udiv.fix.i16(i16 %x, i16 %y, i32 16) + ret i16 %tmp +} + +define <4 x i32> @vec(<4 x i32> %x, <4 x i32> %y) nounwind { +; X64-LABEL: vec: +; X64: # %bb.0: +; X64-NEXT: pxor %xmm2, %xmm2 +; X64-NEXT: movdqa %xmm1, %xmm4 +; X64-NEXT: punpckhdq {{.*#+}} xmm4 = xmm4[2],xmm2[2],xmm4[3],xmm2[3] +; X64-NEXT: movq %xmm4, %rcx +; X64-NEXT: movdqa %xmm0, %xmm5 +; X64-NEXT: punpckhdq {{.*#+}} xmm5 = xmm5[2],xmm2[2],xmm5[3],xmm2[3] +; X64-NEXT: psllq $31, %xmm5 +; X64-NEXT: movq %xmm5, %rax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divq %rcx +; X64-NEXT: movq %rax, %xmm3 +; X64-NEXT: pshufd {{.*#+}} xmm4 = xmm4[2,3,0,1] +; X64-NEXT: movq %xmm4, %rcx +; X64-NEXT: pshufd {{.*#+}} xmm4 = xmm5[2,3,0,1] +; X64-NEXT: movq %xmm4, %rax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divq %rcx +; X64-NEXT: movq %rax, %xmm4 +; X64-NEXT: punpcklqdq {{.*#+}} xmm3 = xmm3[0],xmm4[0] +; X64-NEXT: punpckldq {{.*#+}} xmm1 = xmm1[0],xmm2[0],xmm1[1],xmm2[1] +; X64-NEXT: movq %xmm1, %rcx +; X64-NEXT: punpckldq {{.*#+}} xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1] +; X64-NEXT: psllq $31, %xmm0 +; X64-NEXT: movq %xmm0, %rax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divq %rcx +; X64-NEXT: movq %rax, %xmm2 +; X64-NEXT: pshufd {{.*#+}} xmm1 = xmm1[2,3,0,1] +; X64-NEXT: movq %xmm1, %rcx +; X64-NEXT: pshufd {{.*#+}} xmm0 = xmm0[2,3,0,1] +; X64-NEXT: movq %xmm0, %rax +; X64-NEXT: xorl %edx, %edx +; X64-NEXT: divq %rcx +; X64-NEXT: movq %rax, %xmm0 +; X64-NEXT: punpcklqdq {{.*#+}} xmm2 = xmm2[0],xmm0[0] +; X64-NEXT: shufps {{.*#+}} xmm2 = xmm2[0,2],xmm3[0,2] +; X64-NEXT: movaps %xmm2, %xmm0 +; X64-NEXT: retq +; +; X86-LABEL: vec: +; X86: # %bb.0: +; X86-NEXT: pushl %ebp +; X86-NEXT: pushl %ebx +; X86-NEXT: pushl %edi +; X86-NEXT: pushl %esi +; X86-NEXT: pushl %eax +; X86-NEXT: movl {{[0-9]+}}(%esp), %esi +; X86-NEXT: movl {{[0-9]+}}(%esp), %edi +; X86-NEXT: movl {{[0-9]+}}(%esp), %ebp +; X86-NEXT: movl {{[0-9]+}}(%esp), %ebx +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: movl %eax, %ecx +; X86-NEXT: shrl %ecx +; X86-NEXT: shll $31, %eax +; X86-NEXT: pushl $0 +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl %ecx +; X86-NEXT: pushl %eax +; X86-NEXT: calll __udivdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, (%esp) # 4-byte Spill +; X86-NEXT: movl %ebx, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: shll $31, %ebx +; X86-NEXT: pushl $0 +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl %eax +; X86-NEXT: pushl %ebx +; X86-NEXT: calll __udivdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, %ebx +; X86-NEXT: movl %ebp, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: shll $31, %ebp +; X86-NEXT: pushl $0 +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl %eax +; X86-NEXT: pushl %ebp +; X86-NEXT: calll __udivdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, %ebp +; X86-NEXT: movl %edi, %eax +; X86-NEXT: shrl %eax +; X86-NEXT: shll $31, %edi +; X86-NEXT: pushl $0 +; X86-NEXT: pushl {{[0-9]+}}(%esp) +; X86-NEXT: pushl %eax +; X86-NEXT: pushl %edi +; X86-NEXT: calll __udivdi3 +; X86-NEXT: addl $16, %esp +; X86-NEXT: movl %eax, 12(%esi) +; X86-NEXT: movl %ebp, 8(%esi) +; X86-NEXT: movl %ebx, 4(%esi) +; X86-NEXT: movl (%esp), %eax # 4-byte Reload +; X86-NEXT: movl %eax, (%esi) +; X86-NEXT: movl %esi, %eax +; X86-NEXT: addl $4, %esp +; X86-NEXT: popl %esi +; X86-NEXT: popl %edi +; X86-NEXT: popl %ebx +; X86-NEXT: popl %ebp +; X86-NEXT: retl $4 + %tmp = call <4 x i32> @llvm.udiv.fix.v4i32(<4 x i32> %x, <4 x i32> %y, i32 31) + ret <4 x i32> %tmp +}