Add intrinsics for saturating float to int casts

This patch adds support for the fptoui.sat and fptosi.sat intrinsics,
which provide basically the same functionality as the existing fptoui
and fptosi instructions, but will saturate (or return 0 for NaN) on
values unrepresentable in the target type, instead of returning
poison. Related mailing list discussion can be found at:
https://groups.google.com/d/msg/llvm-dev/cgDFaBmCnDQ/CZAIMj4IBAAJ

The intrinsics have overloaded source and result type and support
vector operands:

    i32 @llvm.fptoui.sat.i32.f32(float %f)
    i100 @llvm.fptoui.sat.i100.f64(double %f)
    <4 x i32> @llvm.fptoui.sat.v4i32.v4f16(half %f)
    // etc

On the SelectionDAG layer two new ISD opcodes are added,
FP_TO_UINT_SAT and FP_TO_SINT_SAT. These opcodes have two operands
and one result. The second operand is an integer constant specifying
the scalar saturation width. The idea here is that initially the
second operand and the scalar width of the result type are the same,
but they may change during type legalization. For example:

    i19 @llvm.fptsi.sat.i19.f32(float %f)
    // builds
    i19 fp_to_sint_sat f, 19
    // type legalizes (through integer result promotion)
    i32 fp_to_sint_sat f, 19

I went for this approach, because saturated conversion does not
compose well. There is no good way of "adjusting" a saturating
conversion to i32 into one to i19 short of saturating twice.
Specifying the saturation width separately allows directly saturating
to the correct width.

There are two baseline expansions for the fp_to_xint_sat opcodes. If
the integer bounds can be exactly represented in the float type and
fminnum/fmaxnum are legal, we can expand to something like:

    f = fmaxnum f, FP(MIN)
    f = fminnum f, FP(MAX)
    i = fptoxi f
    i = select f uo f, 0, i # unnecessary if unsigned as 0 = MIN

If the bounds cannot be exactly represented, we expand to something
like this instead:

    i = fptoxi f
    i = select f ult FP(MIN), MIN, i
    i = select f ogt FP(MAX), MAX, i
    i = select f uo f, 0, i # unnecessary if unsigned as 0 = MIN

It should be noted that this expansion assumes a non-trapping fptoxi.

Initial tests are for AArch64, x86_64 and ARM. This exercises all of
the scalar and vector legalization. ARM is included to test float
softening.

Original patch by @nikic and @ebevhan (based on D54696).

Differential Revision: https://reviews.llvm.org/D54749
This commit is contained in:
Bjorn Pettersson 2020-12-17 21:33:32 +01:00
parent 324d96b637
commit a89d751fb4
22 changed files with 18535 additions and 2 deletions

View File

@ -16426,6 +16426,120 @@ Examples:
%a = load i16, i16* @x, align 2
%res = call float @llvm.convert.from.fp16(i16 %a)
Saturating floating-point to integer conversions
------------------------------------------------
The ``fptoui`` and ``fptosi`` instructions return a
:ref:`poison value <poisonvalues>` if the rounded-towards-zero value is not
representable by the result type. These intrinsics provide an alternative
conversion, which will saturate towards the smallest and largest representable
integer values instead.
'``llvm.fptoui.sat.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax:
"""""""
This is an overloaded intrinsic. You can use ``llvm.fptoui.sat`` on any
floating-point argument type and any integer result type, or vectors thereof.
Not all targets may support all types, however.
::
declare i32 @llvm.fptoui.sat.i32.f32(float %f)
declare i19 @llvm.fptoui.sat.i19.f64(double %f)
declare <4 x i100> @llvm.fptoui.sat.v4i100.v4f128(<4 x fp128> %f)
Overview:
"""""""""
This intrinsic converts the argument into an unsigned integer using saturating
semantics.
Arguments:
""""""""""
The argument may be any floating-point or vector of floating-point type. The
return value may be any integer or vector of integer type. The number of vector
elements in argument and return must be the same.
Semantics:
""""""""""
The conversion to integer is performed subject to the following rules:
- If the argument is any NaN, zero is returned.
- If the argument is smaller than zero (this includes negative infinity),
zero is returned.
- If the argument is larger than the largest representable unsigned integer of
the result type (this includes positive infinity), the largest representable
unsigned integer is returned.
- Otherwise, the result of rounding the argument towards zero is returned.
Example:
""""""""
.. code-block:: text
%a = call i8 @llvm.fptoui.sat.i8.f32(float 123.9) ; yields i8: 123
%b = call i8 @llvm.fptoui.sat.i8.f32(float -5.7) ; yields i8: 0
%c = call i8 @llvm.fptoui.sat.i8.f32(float 377.0) ; yields i8: 255
%d = call i8 @llvm.fptoui.sat.i8.f32(float 0xFFF8000000000000) ; yields i8: 0
'``llvm.fptosi.sat.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax:
"""""""
This is an overloaded intrinsic. You can use ``llvm.fptosi.sat`` on any
floating-point argument type and any integer result type, or vectors thereof.
Not all targets may support all types, however.
::
declare i32 @llvm.fptosi.sat.i32.f32(float %f)
declare i19 @llvm.fptosi.sat.i19.f64(double %f)
declare <4 x i100> @llvm.fptosi.sat.v4i100.v4f128(<4 x fp128> %f)
Overview:
"""""""""
This intrinsic converts the argument into a signed integer using saturating
semantics.
Arguments:
""""""""""
The argument may be any floating-point or vector of floating-point type. The
return value may be any integer or vector of integer type. The number of vector
elements in argument and return must be the same.
Semantics:
""""""""""
The conversion to integer is performed subject to the following rules:
- If the argument is any NaN, zero is returned.
- If the argument is smaller than the smallest representable signed integer of
the result type (this includes negative infinity), the smallest
representable signed integer is returned.
- If the argument is larger than the largest representable signed integer of
the result type (this includes positive infinity), the largest representable
signed integer is returned.
- Otherwise, the result of rounding the argument towards zero is returned.
Example:
""""""""
.. code-block:: text
%a = call i8 @llvm.fptosi.sat.i8.f32(float 23.9) ; yields i8: 23
%b = call i8 @llvm.fptosi.sat.i8.f32(float -130.8) ; yields i8: -128
%c = call i8 @llvm.fptosi.sat.i8.f32(float 999.0) ; yields i8: 127
%d = call i8 @llvm.fptosi.sat.i8.f32(float 0xFFF8000000000000) ; yields i8: 0
.. _dbg_intrinsics:
Debugger Intrinsics

View File

@ -734,6 +734,21 @@ enum NodeType {
FP_TO_SINT,
FP_TO_UINT,
/// FP_TO_[US]INT_SAT - Convert floating point value in operand 0 to a
/// signed or unsigned integer type with the bit width given in operand 1 with
/// the following semantics:
///
/// * If the value is NaN, zero is returned.
/// * If the value is larger/smaller than the largest/smallest integer,
/// the largest/smallest integer is returned (saturation).
/// * Otherwise the result of rounding the value towards zero is returned.
///
/// The width given in operand 1 must be equal to, or smaller than, the scalar
/// result type width. It may end up being smaller than the result witdh as a
/// result of integer type legalization.
FP_TO_SINT_SAT,
FP_TO_UINT_SAT,
/// X = FP_ROUND(Y, TRUNC) - Rounding 'Y' from a larger floating point type
/// down to the precision of the destination VT. TRUNC is a flag, which is
/// always an integer that is zero or one. If TRUNC is 0, this is a

View File

@ -4367,6 +4367,11 @@ public:
/// Expand fminnum/fmaxnum into fminnum_ieee/fmaxnum_ieee with quieted inputs.
SDValue expandFMINNUM_FMAXNUM(SDNode *N, SelectionDAG &DAG) const;
/// Expand FP_TO_[US]INT_SAT into FP_TO_[US]INT and selects or min/max.
/// \param N Node to expand
/// \returns The expansion result
SDValue expandFP_TO_INT_SAT(SDNode *N, SelectionDAG &DAG) const;
/// Expand CTPOP nodes. Expands vector/scalar CTPOP nodes,
/// vector nodes can only succeed if all operations are legal/custom.
/// \param N Node to expand

View File

@ -1293,6 +1293,12 @@ def int_convert_to_fp16 : DefaultAttrsIntrinsic<[llvm_i16_ty], [llvm_anyfloat_
def int_convert_from_fp16 : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_i16_ty]>;
}
// Saturating floating point to integer intrinsics
let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrWillReturn] in {
def int_fptoui_sat : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty]>;
def int_fptosi_sat : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty]>;
}
// Clear cache intrinsic, default to ignore (ie. emit nothing)
// maps to void __clear_cache() on supporting platforms
def int_clear_cache : Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty],

View File

@ -164,6 +164,9 @@ def SDTIntToFPOp : SDTypeProfile<1, 1, [ // [su]int_to_fp
def SDTFPToIntOp : SDTypeProfile<1, 1, [ // fp_to_[su]int
SDTCisInt<0>, SDTCisFP<1>, SDTCisSameNumEltsAs<0, 1>
]>;
def SDTFPToIntSatOp : SDTypeProfile<1, 2, [ // fp_to_[su]int_sat
SDTCisInt<0>, SDTCisFP<1>, SDTCisInt<2>, SDTCisSameNumEltsAs<0, 1>
]>;
def SDTExtInreg : SDTypeProfile<1, 2, [ // sext_inreg
SDTCisSameAs<0, 1>, SDTCisInt<0>, SDTCisVT<2, OtherVT>,
SDTCisVTSmallerThanOp<2, 1>
@ -486,6 +489,8 @@ def sint_to_fp : SDNode<"ISD::SINT_TO_FP" , SDTIntToFPOp>;
def uint_to_fp : SDNode<"ISD::UINT_TO_FP" , SDTIntToFPOp>;
def fp_to_sint : SDNode<"ISD::FP_TO_SINT" , SDTFPToIntOp>;
def fp_to_uint : SDNode<"ISD::FP_TO_UINT" , SDTFPToIntOp>;
def fp_to_sint_sat : SDNode<"ISD::FP_TO_SINT_SAT" , SDTFPToIntSatOp>;
def fp_to_uint_sat : SDNode<"ISD::FP_TO_UINT_SAT" , SDTFPToIntSatOp>;
def f16_to_fp : SDNode<"ISD::FP16_TO_FP" , SDTIntToFPOp>;
def fp_to_f16 : SDNode<"ISD::FP_TO_FP16" , SDTFPToIntOp>;

View File

@ -179,6 +179,7 @@ private:
SmallVectorImpl<SDValue> &Results);
void PromoteLegalFP_TO_INT(SDNode *N, const SDLoc &dl,
SmallVectorImpl<SDValue> &Results);
SDValue PromoteLegalFP_TO_INT_SAT(SDNode *Node, const SDLoc &dl);
SDValue ExpandBITREVERSE(SDValue Op, const SDLoc &dl);
SDValue ExpandBSWAP(SDValue Op, const SDLoc &dl);
@ -1136,10 +1137,11 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
case ISD::SSUBSAT:
case ISD::USUBSAT:
case ISD::SSHLSAT:
case ISD::USHLSAT: {
case ISD::USHLSAT:
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0));
break;
}
case ISD::SMULFIX:
case ISD::SMULFIXSAT:
case ISD::UMULFIX:
@ -2736,6 +2738,30 @@ void SelectionDAGLegalize::PromoteLegalFP_TO_INT(SDNode *N, const SDLoc &dl,
Results.push_back(Operation.getValue(1));
}
/// Promote FP_TO_*INT_SAT operation to a larger result type. At this point
/// the result and operand types are legal and there must be a legal
/// FP_TO_*INT_SAT operation for a larger result type.
SDValue SelectionDAGLegalize::PromoteLegalFP_TO_INT_SAT(SDNode *Node,
const SDLoc &dl) {
unsigned Opcode = Node->getOpcode();
// Scan for the appropriate larger type to use.
EVT NewOutTy = Node->getValueType(0);
while (true) {
NewOutTy = (MVT::SimpleValueType)(NewOutTy.getSimpleVT().SimpleTy + 1);
assert(NewOutTy.isInteger() && "Ran out of possibilities!");
if (TLI.isOperationLegalOrCustom(Opcode, NewOutTy))
break;
}
// Saturation width is determined by second operand, so we don't have to
// perform any fixup and can directly truncate the result.
SDValue Result = DAG.getNode(Opcode, dl, NewOutTy, Node->getOperand(0),
Node->getOperand(1));
return DAG.getNode(ISD::TRUNCATE, dl, Node->getValueType(0), Result);
}
/// Legalize a BITREVERSE scalar/vector operation as a series of mask + shifts.
SDValue SelectionDAGLegalize::ExpandBITREVERSE(SDValue Op, const SDLoc &dl) {
EVT VT = Op.getValueType();
@ -3167,6 +3193,10 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
return true;
}
break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Results.push_back(TLI.expandFP_TO_INT_SAT(Node, DAG));
break;
case ISD::VAARG:
Results.push_back(DAG.expandVAArg(Node));
Results.push_back(Results[0].getValue(1));
@ -4642,6 +4672,10 @@ void SelectionDAGLegalize::PromoteNode(SDNode *Node) {
case ISD::STRICT_FP_TO_SINT:
PromoteLegalFP_TO_INT(Node, dl, Results);
break;
case ISD::FP_TO_UINT_SAT:
case ISD::FP_TO_SINT_SAT:
Results.push_back(PromoteLegalFP_TO_INT_SAT(Node, dl));
break;
case ISD::UINT_TO_FP:
case ISD::STRICT_UINT_TO_FP:
case ISD::SINT_TO_FP:

View File

@ -819,6 +819,9 @@ bool DAGTypeLegalizer::SoftenFloatOperand(SDNode *N, unsigned OpNo) {
case ISD::STRICT_FP_TO_UINT:
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT: Res = SoftenFloatOp_FP_TO_XINT(N); break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Res = SoftenFloatOp_FP_TO_XINT_SAT(N); break;
case ISD::STRICT_LROUND:
case ISD::LROUND: Res = SoftenFloatOp_LROUND(N); break;
case ISD::STRICT_LLROUND:
@ -954,6 +957,11 @@ SDValue DAGTypeLegalizer::SoftenFloatOp_FP_TO_XINT(SDNode *N) {
return SDValue();
}
SDValue DAGTypeLegalizer::SoftenFloatOp_FP_TO_XINT_SAT(SDNode *N) {
SDValue Res = TLI.expandFP_TO_INT_SAT(N, DAG);
return Res;
}
SDValue DAGTypeLegalizer::SoftenFloatOp_SELECT_CC(SDNode *N) {
SDValue NewLHS = N->getOperand(0), NewRHS = N->getOperand(1);
ISD::CondCode CCCode = cast<CondCodeSDNode>(N->getOperand(4))->get();
@ -2060,6 +2068,9 @@ bool DAGTypeLegalizer::PromoteFloatOperand(SDNode *N, unsigned OpNo) {
case ISD::FCOPYSIGN: R = PromoteFloatOp_FCOPYSIGN(N, OpNo); break;
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT: R = PromoteFloatOp_FP_TO_XINT(N, OpNo); break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
R = PromoteFloatOp_FP_TO_XINT_SAT(N, OpNo); break;
case ISD::FP_EXTEND: R = PromoteFloatOp_FP_EXTEND(N, OpNo); break;
case ISD::SELECT_CC: R = PromoteFloatOp_SELECT_CC(N, OpNo); break;
case ISD::SETCC: R = PromoteFloatOp_SETCC(N, OpNo); break;
@ -2103,6 +2114,13 @@ SDValue DAGTypeLegalizer::PromoteFloatOp_FP_TO_XINT(SDNode *N, unsigned OpNo) {
return DAG.getNode(N->getOpcode(), SDLoc(N), N->getValueType(0), Op);
}
SDValue DAGTypeLegalizer::PromoteFloatOp_FP_TO_XINT_SAT(SDNode *N,
unsigned OpNo) {
SDValue Op = GetPromotedFloat(N->getOperand(0));
return DAG.getNode(N->getOpcode(), SDLoc(N), N->getValueType(0), Op,
N->getOperand(1));
}
SDValue DAGTypeLegalizer::PromoteFloatOp_FP_EXTEND(SDNode *N, unsigned OpNo) {
SDValue Op = GetPromotedFloat(N->getOperand(0));
EVT VT = N->getValueType(0);
@ -2846,6 +2864,9 @@ bool DAGTypeLegalizer::SoftPromoteHalfOperand(SDNode *N, unsigned OpNo) {
case ISD::FCOPYSIGN: Res = SoftPromoteHalfOp_FCOPYSIGN(N, OpNo); break;
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT: Res = SoftPromoteHalfOp_FP_TO_XINT(N); break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Res = SoftPromoteHalfOp_FP_TO_XINT_SAT(N); break;
case ISD::STRICT_FP_EXTEND:
case ISD::FP_EXTEND: Res = SoftPromoteHalfOp_FP_EXTEND(N); break;
case ISD::SELECT_CC: Res = SoftPromoteHalfOp_SELECT_CC(N, OpNo); break;
@ -2915,6 +2936,20 @@ SDValue DAGTypeLegalizer::SoftPromoteHalfOp_FP_TO_XINT(SDNode *N) {
return DAG.getNode(N->getOpcode(), dl, N->getValueType(0), Res);
}
SDValue DAGTypeLegalizer::SoftPromoteHalfOp_FP_TO_XINT_SAT(SDNode *N) {
SDValue Op = N->getOperand(0);
SDLoc dl(N);
EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), Op.getValueType());
Op = GetSoftPromotedHalf(Op);
SDValue Res = DAG.getNode(ISD::FP16_TO_FP, dl, NVT, Op);
return DAG.getNode(N->getOpcode(), dl, N->getValueType(0), Res,
N->getOperand(1));
}
SDValue DAGTypeLegalizer::SoftPromoteHalfOp_SELECT_CC(SDNode *N,
unsigned OpNo) {
assert(OpNo == 0 && "Can only soften the comparison values");

View File

@ -123,6 +123,10 @@ void DAGTypeLegalizer::PromoteIntegerResult(SDNode *N, unsigned ResNo) {
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT: Res = PromoteIntRes_FP_TO_XINT(N); break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Res = PromoteIntRes_FP_TO_XINT_SAT(N); break;
case ISD::FP_TO_FP16: Res = PromoteIntRes_FP_TO_FP16(N); break;
case ISD::FLT_ROUNDS_: Res = PromoteIntRes_FLT_ROUNDS(N); break;
@ -596,6 +600,14 @@ SDValue DAGTypeLegalizer::PromoteIntRes_FP_TO_XINT(SDNode *N) {
DAG.getValueType(N->getValueType(0).getScalarType()));
}
SDValue DAGTypeLegalizer::PromoteIntRes_FP_TO_XINT_SAT(SDNode *N) {
// Promote the result type, while keeping the original width in Op1.
EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0));
SDLoc dl(N);
return DAG.getNode(N->getOpcode(), dl, NVT, N->getOperand(0),
N->getOperand(1));
}
SDValue DAGTypeLegalizer::PromoteIntRes_FP_TO_FP16(SDNode *N) {
EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0));
SDLoc dl(N);
@ -2045,6 +2057,8 @@ void DAGTypeLegalizer::ExpandIntegerResult(SDNode *N, unsigned ResNo) {
case ISD::FP_TO_SINT: ExpandIntRes_FP_TO_SINT(N, Lo, Hi); break;
case ISD::STRICT_FP_TO_UINT:
case ISD::FP_TO_UINT: ExpandIntRes_FP_TO_UINT(N, Lo, Hi); break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT: ExpandIntRes_FP_TO_XINT_SAT(N, Lo, Hi); break;
case ISD::STRICT_LLROUND:
case ISD::STRICT_LLRINT:
case ISD::LLROUND:
@ -3050,6 +3064,12 @@ void DAGTypeLegalizer::ExpandIntRes_FP_TO_UINT(SDNode *N, SDValue &Lo,
ReplaceValueWith(SDValue(N, 1), Tmp.second);
}
void DAGTypeLegalizer::ExpandIntRes_FP_TO_XINT_SAT(SDNode *N, SDValue &Lo,
SDValue &Hi) {
SDValue Res = TLI.expandFP_TO_INT_SAT(N, DAG);
SplitInteger(Res, Lo, Hi);
}
void DAGTypeLegalizer::ExpandIntRes_LLROUND_LLRINT(SDNode *N, SDValue &Lo,
SDValue &Hi) {
SDValue Op = N->getOperand(N->isStrictFPOpcode() ? 1 : 0);

View File

@ -315,6 +315,7 @@ private:
SDValue PromoteIntRes_CTTZ(SDNode *N);
SDValue PromoteIntRes_EXTRACT_VECTOR_ELT(SDNode *N);
SDValue PromoteIntRes_FP_TO_XINT(SDNode *N);
SDValue PromoteIntRes_FP_TO_XINT_SAT(SDNode *N);
SDValue PromoteIntRes_FP_TO_FP16(SDNode *N);
SDValue PromoteIntRes_FREEZE(SDNode *N);
SDValue PromoteIntRes_INT_EXTEND(SDNode *N);
@ -424,6 +425,7 @@ private:
void ExpandIntRes_FLT_ROUNDS (SDNode *N, SDValue &Lo, SDValue &Hi);
void ExpandIntRes_FP_TO_SINT (SDNode *N, SDValue &Lo, SDValue &Hi);
void ExpandIntRes_FP_TO_UINT (SDNode *N, SDValue &Lo, SDValue &Hi);
void ExpandIntRes_FP_TO_XINT_SAT (SDNode *N, SDValue &Lo, SDValue &Hi);
void ExpandIntRes_LLROUND_LLRINT (SDNode *N, SDValue &Lo, SDValue &Hi);
void ExpandIntRes_Logical (SDNode *N, SDValue &Lo, SDValue &Hi);
@ -561,6 +563,7 @@ private:
SDValue SoftenFloatOp_BR_CC(SDNode *N);
SDValue SoftenFloatOp_FP_ROUND(SDNode *N);
SDValue SoftenFloatOp_FP_TO_XINT(SDNode *N);
SDValue SoftenFloatOp_FP_TO_XINT_SAT(SDNode *N);
SDValue SoftenFloatOp_LROUND(SDNode *N);
SDValue SoftenFloatOp_LLROUND(SDNode *N);
SDValue SoftenFloatOp_LRINT(SDNode *N);
@ -678,6 +681,7 @@ private:
SDValue PromoteFloatOp_FCOPYSIGN(SDNode *N, unsigned OpNo);
SDValue PromoteFloatOp_FP_EXTEND(SDNode *N, unsigned OpNo);
SDValue PromoteFloatOp_FP_TO_XINT(SDNode *N, unsigned OpNo);
SDValue PromoteFloatOp_FP_TO_XINT_SAT(SDNode *N, unsigned OpNo);
SDValue PromoteFloatOp_STORE(SDNode *N, unsigned OpNo);
SDValue PromoteFloatOp_SELECT_CC(SDNode *N, unsigned OpNo);
SDValue PromoteFloatOp_SETCC(SDNode *N, unsigned OpNo);
@ -717,6 +721,7 @@ private:
SDValue SoftPromoteHalfOp_FCOPYSIGN(SDNode *N, unsigned OpNo);
SDValue SoftPromoteHalfOp_FP_EXTEND(SDNode *N);
SDValue SoftPromoteHalfOp_FP_TO_XINT(SDNode *N);
SDValue SoftPromoteHalfOp_FP_TO_XINT_SAT(SDNode *N);
SDValue SoftPromoteHalfOp_SETCC(SDNode *N);
SDValue SoftPromoteHalfOp_SELECT_CC(SDNode *N, unsigned OpNo);
SDValue SoftPromoteHalfOp_STORE(SDNode *N, unsigned OpNo);
@ -761,6 +766,7 @@ private:
SDValue ScalarizeVecRes_SETCC(SDNode *N);
SDValue ScalarizeVecRes_UNDEF(SDNode *N);
SDValue ScalarizeVecRes_VECTOR_SHUFFLE(SDNode *N);
SDValue ScalarizeVecRes_FP_TO_XINT_SAT(SDNode *N);
SDValue ScalarizeVecRes_FIX(SDNode *N);
@ -830,6 +836,7 @@ private:
void SplitVecRes_VECTOR_SHUFFLE(ShuffleVectorSDNode *N, SDValue &Lo,
SDValue &Hi);
void SplitVecRes_VAARG(SDNode *N, SDValue &Lo, SDValue &Hi);
void SplitVecRes_FP_TO_XINT_SAT(SDNode *N, SDValue &Lo, SDValue &Hi);
// Vector Operand Splitting: <128 x ty> -> 2 x <64 x ty>.
bool SplitVectorOperand(SDNode *N, unsigned OpNo);
@ -852,6 +859,7 @@ private:
SDValue SplitVecOp_VSETCC(SDNode *N);
SDValue SplitVecOp_FP_ROUND(SDNode *N);
SDValue SplitVecOp_FCOPYSIGN(SDNode *N);
SDValue SplitVecOp_FP_TO_XINT_SAT(SDNode *N);
//===--------------------------------------------------------------------===//
// Vector Widening Support: LegalizeVectorTypes.cpp
@ -900,6 +908,7 @@ private:
SDValue WidenVecRes_OverflowOp(SDNode *N, unsigned ResNo);
SDValue WidenVecRes_Convert(SDNode *N);
SDValue WidenVecRes_Convert_StrictFP(SDNode *N);
SDValue WidenVecRes_FP_TO_XINT_SAT(SDNode *N);
SDValue WidenVecRes_FCOPYSIGN(SDNode *N);
SDValue WidenVecRes_POWI(SDNode *N);
SDValue WidenVecRes_Unary(SDNode *N);
@ -921,6 +930,7 @@ private:
SDValue WidenVecOp_VSELECT(SDNode *N);
SDValue WidenVecOp_Convert(SDNode *N);
SDValue WidenVecOp_FP_TO_XINT_SAT(SDNode *N);
SDValue WidenVecOp_FCOPYSIGN(SDNode *N);
SDValue WidenVecOp_VECREDUCE(SDNode *N);
SDValue WidenVecOp_VECREDUCE_SEQ(SDNode *N);

View File

@ -455,6 +455,8 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) {
case ISD::USUBSAT:
case ISD::SSHLSAT:
case ISD::USHLSAT:
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0));
break;
case ISD::SMULFIX:

View File

@ -162,6 +162,11 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) {
R = ScalarizeVecRes_StrictFPOp(N);
break;
case ISD::FP_TO_UINT_SAT:
case ISD::FP_TO_SINT_SAT:
R = ScalarizeVecRes_FP_TO_XINT_SAT(N);
break;
case ISD::UADDO:
case ISD::SADDO:
case ISD::USUBO:
@ -516,6 +521,23 @@ SDValue DAGTypeLegalizer::ScalarizeVecRes_VECTOR_SHUFFLE(SDNode *N) {
return GetScalarizedVector(N->getOperand(Op));
}
SDValue DAGTypeLegalizer::ScalarizeVecRes_FP_TO_XINT_SAT(SDNode *N) {
SDValue Src = N->getOperand(0);
EVT SrcVT = Src.getValueType();
SDLoc dl(N);
// Handle case where result is scalarized but operand is not
if (getTypeAction(SrcVT) == TargetLowering::TypeScalarizeVector)
Src = GetScalarizedVector(Src);
else
Src = DAG.getNode(
ISD::EXTRACT_VECTOR_ELT, dl, SrcVT.getVectorElementType(), Src,
DAG.getConstant(0, dl, TLI.getVectorIdxTy(DAG.getDataLayout())));
EVT DstVT = N->getValueType(0).getVectorElementType();
return DAG.getNode(N->getOpcode(), dl, DstVT, Src, N->getOperand(1));
}
SDValue DAGTypeLegalizer::ScalarizeVecRes_SETCC(SDNode *N) {
assert(N->getValueType(0).isVector() &&
N->getOperand(0).getValueType().isVector() &&
@ -1015,6 +1037,11 @@ void DAGTypeLegalizer::SplitVectorResult(SDNode *N, unsigned ResNo) {
SplitVecRes_StrictFPOp(N, Lo, Hi);
break;
case ISD::FP_TO_UINT_SAT:
case ISD::FP_TO_SINT_SAT:
SplitVecRes_FP_TO_XINT_SAT(N, Lo, Hi);
break;
case ISD::UADDO:
case ISD::SADDO:
case ISD::USUBO:
@ -2032,6 +2059,22 @@ void DAGTypeLegalizer::SplitVecRes_VAARG(SDNode *N, SDValue &Lo, SDValue &Hi) {
ReplaceValueWith(SDValue(N, 1), Chain);
}
void DAGTypeLegalizer::SplitVecRes_FP_TO_XINT_SAT(SDNode *N, SDValue &Lo,
SDValue &Hi) {
EVT DstVTLo, DstVTHi;
std::tie(DstVTLo, DstVTHi) = DAG.GetSplitDestVTs(N->getValueType(0));
SDLoc dl(N);
SDValue SrcLo, SrcHi;
EVT SrcVT = N->getOperand(0).getValueType();
if (getTypeAction(SrcVT) == TargetLowering::TypeSplitVector)
GetSplitVector(N->getOperand(0), SrcLo, SrcHi);
else
std::tie(SrcLo, SrcHi) = DAG.SplitVectorOperand(N, 0);
Lo = DAG.getNode(N->getOpcode(), dl, DstVTLo, SrcLo, N->getOperand(1));
Hi = DAG.getNode(N->getOpcode(), dl, DstVTHi, SrcHi, N->getOperand(1));
}
//===----------------------------------------------------------------------===//
// Operand Vector Splitting
@ -2096,6 +2139,10 @@ bool DAGTypeLegalizer::SplitVectorOperand(SDNode *N, unsigned OpNo) {
else
Res = SplitVecOp_UnaryOp(N);
break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Res = SplitVecOp_FP_TO_XINT_SAT(N);
break;
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT:
case ISD::STRICT_FP_TO_SINT:
@ -2842,6 +2889,22 @@ SDValue DAGTypeLegalizer::SplitVecOp_FCOPYSIGN(SDNode *N) {
return DAG.UnrollVectorOp(N, N->getValueType(0).getVectorNumElements());
}
SDValue DAGTypeLegalizer::SplitVecOp_FP_TO_XINT_SAT(SDNode *N) {
EVT ResVT = N->getValueType(0);
SDValue Lo, Hi;
SDLoc dl(N);
GetSplitVector(N->getOperand(0), Lo, Hi);
EVT InVT = Lo.getValueType();
EVT NewResVT =
EVT::getVectorVT(*DAG.getContext(), ResVT.getVectorElementType(),
InVT.getVectorElementCount());
Lo = DAG.getNode(N->getOpcode(), dl, NewResVT, Lo, N->getOperand(1));
Hi = DAG.getNode(N->getOpcode(), dl, NewResVT, Hi, N->getOperand(1));
return DAG.getNode(ISD::CONCAT_VECTORS, dl, ResVT, Lo, Hi);
}
//===----------------------------------------------------------------------===//
// Result Vector Widening
@ -2986,6 +3049,11 @@ void DAGTypeLegalizer::WidenVectorResult(SDNode *N, unsigned ResNo) {
Res = WidenVecRes_Convert(N);
break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Res = WidenVecRes_FP_TO_XINT_SAT(N);
break;
case ISD::FABS:
case ISD::FCEIL:
case ISD::FCOS:
@ -3495,6 +3563,27 @@ SDValue DAGTypeLegalizer::WidenVecRes_Convert(SDNode *N) {
return DAG.getBuildVector(WidenVT, DL, Ops);
}
SDValue DAGTypeLegalizer::WidenVecRes_FP_TO_XINT_SAT(SDNode *N) {
SDLoc dl(N);
EVT WidenVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0));
ElementCount WidenNumElts = WidenVT.getVectorElementCount();
SDValue Src = N->getOperand(0);
EVT SrcVT = Src.getValueType();
// Also widen the input.
if (getTypeAction(SrcVT) == TargetLowering::TypeWidenVector) {
Src = GetWidenedVector(Src);
SrcVT = Src.getValueType();
}
// Input and output not widened to the same size, give up.
if (WidenNumElts != SrcVT.getVectorElementCount())
return DAG.UnrollVectorOp(N, WidenNumElts.getKnownMinValue());
return DAG.getNode(N->getOpcode(), dl, WidenVT, Src, N->getOperand(1));
}
SDValue DAGTypeLegalizer::WidenVecRes_Convert_StrictFP(SDNode *N) {
SDValue InOp = N->getOperand(1);
SDLoc DL(N);
@ -4413,6 +4502,11 @@ bool DAGTypeLegalizer::WidenVectorOperand(SDNode *N, unsigned OpNo) {
Res = WidenVecOp_Convert(N);
break;
case ISD::FP_TO_SINT_SAT:
case ISD::FP_TO_UINT_SAT:
Res = WidenVecOp_FP_TO_XINT_SAT(N);
break;
case ISD::VECREDUCE_FADD:
case ISD::VECREDUCE_FMUL:
case ISD::VECREDUCE_ADD:
@ -4586,6 +4680,28 @@ SDValue DAGTypeLegalizer::WidenVecOp_Convert(SDNode *N) {
return DAG.getBuildVector(VT, dl, Ops);
}
SDValue DAGTypeLegalizer::WidenVecOp_FP_TO_XINT_SAT(SDNode *N) {
EVT DstVT = N->getValueType(0);
SDValue Src = GetWidenedVector(N->getOperand(0));
EVT SrcVT = Src.getValueType();
ElementCount WideNumElts = SrcVT.getVectorElementCount();
SDLoc dl(N);
// See if a widened result type would be legal, if so widen the node.
EVT WideDstVT = EVT::getVectorVT(*DAG.getContext(),
DstVT.getVectorElementType(), WideNumElts);
if (TLI.isTypeLegal(WideDstVT)) {
SDValue Res =
DAG.getNode(N->getOpcode(), dl, WideDstVT, Src, N->getOperand(1));
return DAG.getNode(
ISD::EXTRACT_SUBVECTOR, dl, DstVT, Res,
DAG.getConstant(0, dl, TLI.getVectorIdxTy(DAG.getDataLayout())));
}
// Give up and unroll.
return DAG.UnrollVectorOp(N);
}
SDValue DAGTypeLegalizer::WidenVecOp_BITCAST(SDNode *N) {
EVT VT = N->getValueType(0);
SDValue InOp = GetWidenedVector(N->getOperand(0));

View File

@ -6183,6 +6183,20 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
DAG.getNode(ISD::BITCAST, sdl, MVT::f16,
getValue(I.getArgOperand(0)))));
return;
case Intrinsic::fptosi_sat: {
EVT Type = TLI.getValueType(DAG.getDataLayout(), I.getType());
SDValue SatW = DAG.getConstant(Type.getScalarSizeInBits(), sdl, MVT::i32);
setValue(&I, DAG.getNode(ISD::FP_TO_SINT_SAT, sdl, Type,
getValue(I.getArgOperand(0)), SatW));
return;
}
case Intrinsic::fptoui_sat: {
EVT Type = TLI.getValueType(DAG.getDataLayout(), I.getType());
SDValue SatW = DAG.getConstant(Type.getScalarSizeInBits(), sdl, MVT::i32);
setValue(&I, DAG.getNode(ISD::FP_TO_UINT_SAT, sdl, Type,
getValue(I.getArgOperand(0)), SatW));
return;
}
case Intrinsic::pcmarker: {
SDValue Tmp = getValue(I.getArgOperand(0));
DAG.setRoot(DAG.getNode(ISD::PCMARKER, sdl, MVT::Other, getRoot(), Tmp));

View File

@ -348,6 +348,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
case ISD::STRICT_FP_TO_SINT: return "strict_fp_to_sint";
case ISD::FP_TO_UINT: return "fp_to_uint";
case ISD::STRICT_FP_TO_UINT: return "strict_fp_to_uint";
case ISD::FP_TO_SINT_SAT: return "fp_to_sint_sat";
case ISD::FP_TO_UINT_SAT: return "fp_to_uint_sat";
case ISD::BITCAST: return "bitcast";
case ISD::ADDRSPACECAST: return "addrspacecast";
case ISD::FP16_TO_FP: return "fp16_to_fp";

View File

@ -8193,3 +8193,105 @@ bool TargetLowering::expandREM(SDNode *Node, SDValue &Result,
}
return false;
}
SDValue TargetLowering::expandFP_TO_INT_SAT(SDNode *Node,
SelectionDAG &DAG) const {
bool IsSigned = Node->getOpcode() == ISD::FP_TO_SINT_SAT;
SDLoc dl(SDValue(Node, 0));
SDValue Src = Node->getOperand(0);
// DstVT is the result type, while SatVT is the size to which we saturate
EVT SrcVT = Src.getValueType();
EVT DstVT = Node->getValueType(0);
unsigned SatWidth = Node->getConstantOperandVal(1);
unsigned DstWidth = DstVT.getScalarSizeInBits();
assert(SatWidth <= DstWidth &&
"Expected saturation width smaller than result width");
// Determine minimum and maximum integer values and their corresponding
// floating-point values.
APInt MinInt, MaxInt;
if (IsSigned) {
MinInt = APInt::getSignedMinValue(SatWidth).sextOrSelf(DstWidth);
MaxInt = APInt::getSignedMaxValue(SatWidth).sextOrSelf(DstWidth);
} else {
MinInt = APInt::getMinValue(SatWidth).zextOrSelf(DstWidth);
MaxInt = APInt::getMaxValue(SatWidth).zextOrSelf(DstWidth);
}
// We cannot risk emitting FP_TO_XINT nodes with a source VT of f16, as
// libcall emission cannot handle this. Large result types will fail.
if (SrcVT == MVT::f16) {
Src = DAG.getNode(ISD::FP_EXTEND, dl, MVT::f32, Src);
SrcVT = Src.getValueType();
}
APFloat MinFloat(DAG.EVTToAPFloatSemantics(SrcVT));
APFloat MaxFloat(DAG.EVTToAPFloatSemantics(SrcVT));
APFloat::opStatus MinStatus =
MinFloat.convertFromAPInt(MinInt, IsSigned, APFloat::rmTowardZero);
APFloat::opStatus MaxStatus =
MaxFloat.convertFromAPInt(MaxInt, IsSigned, APFloat::rmTowardZero);
bool AreExactFloatBounds = !(MinStatus & APFloat::opStatus::opInexact) &&
!(MaxStatus & APFloat::opStatus::opInexact);
SDValue MinFloatNode = DAG.getConstantFP(MinFloat, dl, SrcVT);
SDValue MaxFloatNode = DAG.getConstantFP(MaxFloat, dl, SrcVT);
// If the integer bounds are exactly representable as floats and min/max are
// legal, emit a min+max+fptoi sequence. Otherwise we have to use a sequence
// of comparisons and selects.
bool MinMaxLegal = isOperationLegal(ISD::FMINNUM, SrcVT) &&
isOperationLegal(ISD::FMAXNUM, SrcVT);
if (AreExactFloatBounds && MinMaxLegal) {
SDValue Clamped = Src;
// Clamp Src by MinFloat from below. If Src is NaN the result is MinFloat.
Clamped = DAG.getNode(ISD::FMAXNUM, dl, SrcVT, Clamped, MinFloatNode);
// Clamp by MaxFloat from above. NaN cannot occur.
Clamped = DAG.getNode(ISD::FMINNUM, dl, SrcVT, Clamped, MaxFloatNode);
// Convert clamped value to integer.
SDValue FpToInt = DAG.getNode(IsSigned ? ISD::FP_TO_SINT : ISD::FP_TO_UINT,
dl, DstVT, Clamped);
// In the unsigned case we're done, because we mapped NaN to MinFloat,
// which will cast to zero.
if (!IsSigned)
return FpToInt;
// Otherwise, select 0 if Src is NaN.
SDValue ZeroInt = DAG.getConstant(0, dl, DstVT);
return DAG.getSelectCC(dl, Src, Src, ZeroInt, FpToInt,
ISD::CondCode::SETUO);
}
SDValue MinIntNode = DAG.getConstant(MinInt, dl, DstVT);
SDValue MaxIntNode = DAG.getConstant(MaxInt, dl, DstVT);
// Result of direct conversion. The assumption here is that the operation is
// non-trapping and it's fine to apply it to an out-of-range value if we
// select it away later.
SDValue FpToInt =
DAG.getNode(IsSigned ? ISD::FP_TO_SINT : ISD::FP_TO_UINT, dl, DstVT, Src);
SDValue Select = FpToInt;
// If Src ULT MinFloat, select MinInt. In particular, this also selects
// MinInt if Src is NaN.
Select = DAG.getSelectCC(dl, Src, MinFloatNode, MinIntNode, Select,
ISD::CondCode::SETULT);
// If Src OGT MaxFloat, select MaxInt.
Select = DAG.getSelectCC(dl, Src, MaxFloatNode, MaxIntNode, Select,
ISD::CondCode::SETOGT);
// In the unsigned case we are done, because we mapped NaN to MinInt, which
// is already zero.
if (!IsSigned)
return Select;
// Otherwise, select 0 if Src is NaN.
SDValue ZeroInt = DAG.getConstant(0, dl, DstVT);
return DAG.getSelectCC(dl, Src, Src, ZeroInt, Select, ISD::CondCode::SETUO);
}

View File

@ -779,6 +779,8 @@ void TargetLoweringBase::initActions() {
setOperationAction(ISD::SDIVFIXSAT, VT, Expand);
setOperationAction(ISD::UDIVFIX, VT, Expand);
setOperationAction(ISD::UDIVFIXSAT, VT, Expand);
setOperationAction(ISD::FP_TO_SINT_SAT, VT, Expand);
setOperationAction(ISD::FP_TO_UINT_SAT, VT, Expand);
// Overflow operations default to expand
setOperationAction(ISD::SADDO, VT, Expand);

View File

@ -0,0 +1,676 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=aarch64 < %s | FileCheck %s
;
; 32-bit float to signed integer
;
declare i1 @llvm.fptosi.sat.i1.f32 (float)
declare i8 @llvm.fptosi.sat.i8.f32 (float)
declare i13 @llvm.fptosi.sat.i13.f32 (float)
declare i16 @llvm.fptosi.sat.i16.f32 (float)
declare i19 @llvm.fptosi.sat.i19.f32 (float)
declare i32 @llvm.fptosi.sat.i32.f32 (float)
declare i50 @llvm.fptosi.sat.i50.f32 (float)
declare i64 @llvm.fptosi.sat.i64.f32 (float)
declare i100 @llvm.fptosi.sat.i100.f32(float)
declare i128 @llvm.fptosi.sat.i128.f32(float)
define i1 @test_signed_i1_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i1_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: fmov s1, #-1.00000000
; CHECK-NEXT: fmov s2, wzr
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w8, wzr, w8, vs
; CHECK-NEXT: and w0, w8, #0x1
; CHECK-NEXT: ret
%x = call i1 @llvm.fptosi.sat.i1.f32(float %f)
ret i1 %x
}
define i8 @test_signed_i8_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i8_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-1023410176
; CHECK-NEXT: mov w9, #1123942400
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i8 @llvm.fptosi.sat.i8.f32(float %f)
ret i8 %x
}
define i13 @test_signed_i13_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i13_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-981467136
; CHECK-NEXT: mov w9, #61440
; CHECK-NEXT: movk w9, #17791, lsl #16
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i13 @llvm.fptosi.sat.i13.f32(float %f)
ret i13 %x
}
define i16 @test_signed_i16_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i16_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-956301312
; CHECK-NEXT: mov w9, #65024
; CHECK-NEXT: movk w9, #18175, lsl #16
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i16 @llvm.fptosi.sat.i16.f32(float %f)
ret i16 %x
}
define i19 @test_signed_i19_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i19_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-931135488
; CHECK-NEXT: mov w9, #65472
; CHECK-NEXT: movk w9, #18559, lsl #16
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i19 @llvm.fptosi.sat.i19.f32(float %f)
ret i19 %x
}
define i32 @test_signed_i32_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i32_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w9, #-822083584
; CHECK-NEXT: mov w11, #1325400063
; CHECK-NEXT: fmov s1, w9
; CHECK-NEXT: fcvtzs w8, s0
; CHECK-NEXT: mov w10, #-2147483648
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: fmov s1, w11
; CHECK-NEXT: mov w12, #2147483647
; CHECK-NEXT: csel w8, w10, w8, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csel w8, w12, w8, gt
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i32 @llvm.fptosi.sat.i32.f32(float %f)
ret i32 %x
}
define i50 @test_signed_i50_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i50_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w9, #-671088640
; CHECK-NEXT: mov w11, #1476395007
; CHECK-NEXT: fmov s1, w9
; CHECK-NEXT: fcvtzs x8, s0
; CHECK-NEXT: mov x10, #-562949953421312
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: fmov s1, w11
; CHECK-NEXT: mov x12, #562949953421311
; CHECK-NEXT: csel x8, x10, x8, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csel x8, x12, x8, gt
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel x0, xzr, x8, vs
; CHECK-NEXT: ret
%x = call i50 @llvm.fptosi.sat.i50.f32(float %f)
ret i50 %x
}
define i64 @test_signed_i64_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i64_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w9, #-553648128
; CHECK-NEXT: mov w11, #1593835519
; CHECK-NEXT: fmov s1, w9
; CHECK-NEXT: fcvtzs x8, s0
; CHECK-NEXT: mov x10, #-9223372036854775808
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: fmov s1, w11
; CHECK-NEXT: mov x12, #9223372036854775807
; CHECK-NEXT: csel x8, x10, x8, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csel x8, x12, x8, gt
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel x0, xzr, x8, vs
; CHECK-NEXT: ret
%x = call i64 @llvm.fptosi.sat.i64.f32(float %f)
ret i64 %x
}
define i100 @test_signed_i100_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i100_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixsfti
; CHECK-NEXT: mov w8, #-251658240
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov w8, #1895825407
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov x8, #-34359738368
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: csel x8, x8, x1, lt
; CHECK-NEXT: mov x9, #34359738367
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: csinv x9, x10, xzr, le
; CHECK-NEXT: fcmp s8, s8
; CHECK-NEXT: csel x0, xzr, x9, vs
; CHECK-NEXT: csel x1, xzr, x8, vs
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i100 @llvm.fptosi.sat.i100.f32(float %f)
ret i100 %x
}
define i128 @test_signed_i128_f32(float %f) nounwind {
; CHECK-LABEL: test_signed_i128_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixsfti
; CHECK-NEXT: mov w8, #-16777216
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov w8, #2130706431
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov x8, #-9223372036854775808
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: csel x8, x8, x1, lt
; CHECK-NEXT: mov x9, #9223372036854775807
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: csinv x9, x10, xzr, le
; CHECK-NEXT: fcmp s8, s8
; CHECK-NEXT: csel x0, xzr, x9, vs
; CHECK-NEXT: csel x1, xzr, x8, vs
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i128 @llvm.fptosi.sat.i128.f32(float %f)
ret i128 %x
}
;
; 64-bit float to signed integer
;
declare i1 @llvm.fptosi.sat.i1.f64 (double)
declare i8 @llvm.fptosi.sat.i8.f64 (double)
declare i13 @llvm.fptosi.sat.i13.f64 (double)
declare i16 @llvm.fptosi.sat.i16.f64 (double)
declare i19 @llvm.fptosi.sat.i19.f64 (double)
declare i32 @llvm.fptosi.sat.i32.f64 (double)
declare i50 @llvm.fptosi.sat.i50.f64 (double)
declare i64 @llvm.fptosi.sat.i64.f64 (double)
declare i100 @llvm.fptosi.sat.i100.f64(double)
declare i128 @llvm.fptosi.sat.i128.f64(double)
define i1 @test_signed_i1_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i1_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: fmov d1, #-1.00000000
; CHECK-NEXT: fmov d2, xzr
; CHECK-NEXT: fmaxnm d1, d0, d1
; CHECK-NEXT: fminnm d1, d1, d2
; CHECK-NEXT: fcvtzs w8, d1
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel w8, wzr, w8, vs
; CHECK-NEXT: and w0, w8, #0x1
; CHECK-NEXT: ret
%x = call i1 @llvm.fptosi.sat.i1.f64(double %f)
ret i1 %x
}
define i8 @test_signed_i8_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i8_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #-4584664420663164928
; CHECK-NEXT: mov x9, #211106232532992
; CHECK-NEXT: movk x9, #16479, lsl #48
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fmaxnm d1, d0, d1
; CHECK-NEXT: fmov d2, x9
; CHECK-NEXT: fminnm d1, d1, d2
; CHECK-NEXT: fcvtzs w8, d1
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i8 @llvm.fptosi.sat.i8.f64(double %f)
ret i8 %x
}
define i13 @test_signed_i13_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i13_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #-4562146422526312448
; CHECK-NEXT: mov x9, #279275953455104
; CHECK-NEXT: movk x9, #16559, lsl #48
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fmaxnm d1, d0, d1
; CHECK-NEXT: fmov d2, x9
; CHECK-NEXT: fminnm d1, d1, d2
; CHECK-NEXT: fcvtzs w8, d1
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i13 @llvm.fptosi.sat.i13.f64(double %f)
ret i13 %x
}
define i16 @test_signed_i16_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i16_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #-4548635623644200960
; CHECK-NEXT: mov x9, #281200098803712
; CHECK-NEXT: movk x9, #16607, lsl #48
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fmaxnm d1, d0, d1
; CHECK-NEXT: fmov d2, x9
; CHECK-NEXT: fminnm d1, d1, d2
; CHECK-NEXT: fcvtzs w8, d1
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i16 @llvm.fptosi.sat.i16.f64(double %f)
ret i16 %x
}
define i19 @test_signed_i19_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i19_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #-4535124824762089472
; CHECK-NEXT: mov x9, #281440616972288
; CHECK-NEXT: movk x9, #16655, lsl #48
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fmaxnm d1, d0, d1
; CHECK-NEXT: fmov d2, x9
; CHECK-NEXT: fminnm d1, d1, d2
; CHECK-NEXT: fcvtzs w8, d1
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i19 @llvm.fptosi.sat.i19.f64(double %f)
ret i19 %x
}
define i32 @test_signed_i32_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i32_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #-4476578029606273024
; CHECK-NEXT: mov x9, #281474972516352
; CHECK-NEXT: movk x9, #16863, lsl #48
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fmaxnm d1, d0, d1
; CHECK-NEXT: fmov d2, x9
; CHECK-NEXT: fminnm d1, d1, d2
; CHECK-NEXT: fcvtzs w8, d1
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i32 @llvm.fptosi.sat.i32.f64(double %f)
ret i32 %x
}
define i50 @test_signed_i50_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i50_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #-4395513236313604096
; CHECK-NEXT: mov x9, #-16
; CHECK-NEXT: movk x9, #17151, lsl #48
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fmaxnm d1, d0, d1
; CHECK-NEXT: fmov d2, x9
; CHECK-NEXT: fminnm d1, d1, d2
; CHECK-NEXT: fcvtzs x8, d1
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel x0, xzr, x8, vs
; CHECK-NEXT: ret
%x = call i50 @llvm.fptosi.sat.i50.f64(double %f)
ret i50 %x
}
define i64 @test_signed_i64_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i64_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x9, #-4332462841530417152
; CHECK-NEXT: mov x11, #4890909195324358655
; CHECK-NEXT: fmov d1, x9
; CHECK-NEXT: fcvtzs x8, d0
; CHECK-NEXT: mov x10, #-9223372036854775808
; CHECK-NEXT: fcmp d0, d1
; CHECK-NEXT: fmov d1, x11
; CHECK-NEXT: mov x12, #9223372036854775807
; CHECK-NEXT: csel x8, x10, x8, lt
; CHECK-NEXT: fcmp d0, d1
; CHECK-NEXT: csel x8, x12, x8, gt
; CHECK-NEXT: fcmp d0, d0
; CHECK-NEXT: csel x0, xzr, x8, vs
; CHECK-NEXT: ret
%x = call i64 @llvm.fptosi.sat.i64.f64(double %f)
ret i64 %x
}
define i100 @test_signed_i100_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i100_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixdfti
; CHECK-NEXT: mov x8, #-4170333254945079296
; CHECK-NEXT: fmov d0, x8
; CHECK-NEXT: mov x8, #5053038781909696511
; CHECK-NEXT: fcmp d8, d0
; CHECK-NEXT: fmov d0, x8
; CHECK-NEXT: mov x8, #-34359738368
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: csel x8, x8, x1, lt
; CHECK-NEXT: mov x9, #34359738367
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp d8, d0
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: csinv x9, x10, xzr, le
; CHECK-NEXT: fcmp d8, d8
; CHECK-NEXT: csel x0, xzr, x9, vs
; CHECK-NEXT: csel x1, xzr, x8, vs
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i100 @llvm.fptosi.sat.i100.f64(double %f)
ret i100 %x
}
define i128 @test_signed_i128_f64(double %f) nounwind {
; CHECK-LABEL: test_signed_i128_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixdfti
; CHECK-NEXT: mov x8, #-4044232465378705408
; CHECK-NEXT: fmov d0, x8
; CHECK-NEXT: mov x8, #5179139571476070399
; CHECK-NEXT: fcmp d8, d0
; CHECK-NEXT: fmov d0, x8
; CHECK-NEXT: mov x8, #-9223372036854775808
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: csel x8, x8, x1, lt
; CHECK-NEXT: mov x9, #9223372036854775807
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp d8, d0
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: csinv x9, x10, xzr, le
; CHECK-NEXT: fcmp d8, d8
; CHECK-NEXT: csel x0, xzr, x9, vs
; CHECK-NEXT: csel x1, xzr, x8, vs
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i128 @llvm.fptosi.sat.i128.f64(double %f)
ret i128 %x
}
;
; 16-bit float to signed integer
;
declare i1 @llvm.fptosi.sat.i1.f16 (half)
declare i8 @llvm.fptosi.sat.i8.f16 (half)
declare i13 @llvm.fptosi.sat.i13.f16 (half)
declare i16 @llvm.fptosi.sat.i16.f16 (half)
declare i19 @llvm.fptosi.sat.i19.f16 (half)
declare i32 @llvm.fptosi.sat.i32.f16 (half)
declare i50 @llvm.fptosi.sat.i50.f16 (half)
declare i64 @llvm.fptosi.sat.i64.f16 (half)
declare i100 @llvm.fptosi.sat.i100.f16(half)
declare i128 @llvm.fptosi.sat.i128.f16(half)
define i1 @test_signed_i1_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i1_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, #-1.00000000
; CHECK-NEXT: fmov s2, wzr
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w8, wzr, w8, vs
; CHECK-NEXT: and w0, w8, #0x1
; CHECK-NEXT: ret
%x = call i1 @llvm.fptosi.sat.i1.f16(half %f)
ret i1 %x
}
define i8 @test_signed_i8_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i8_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-1023410176
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: mov w9, #1123942400
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i8 @llvm.fptosi.sat.i8.f16(half %f)
ret i8 %x
}
define i13 @test_signed_i13_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i13_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-981467136
; CHECK-NEXT: mov w9, #61440
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: movk w9, #17791, lsl #16
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i13 @llvm.fptosi.sat.i13.f16(half %f)
ret i13 %x
}
define i16 @test_signed_i16_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i16_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-956301312
; CHECK-NEXT: mov w9, #65024
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: movk w9, #18175, lsl #16
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i16 @llvm.fptosi.sat.i16.f16(half %f)
ret i16 %x
}
define i19 @test_signed_i19_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i19_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-931135488
; CHECK-NEXT: mov w9, #65472
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: movk w9, #18559, lsl #16
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fmaxnm s1, s0, s1
; CHECK-NEXT: fmov s2, w9
; CHECK-NEXT: fminnm s1, s1, s2
; CHECK-NEXT: fcvtzs w8, s1
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i19 @llvm.fptosi.sat.i19.f16(half %f)
ret i19 %x
}
define i32 @test_signed_i32_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i32_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-822083584
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: mov w8, #1325400063
; CHECK-NEXT: mov w9, #-2147483648
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fcvtzs w8, s0
; CHECK-NEXT: csel w8, w9, w8, lt
; CHECK-NEXT: mov w9, #2147483647
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csel w8, w9, w8, gt
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel w0, wzr, w8, vs
; CHECK-NEXT: ret
%x = call i32 @llvm.fptosi.sat.i32.f16(half %f)
ret i32 %x
}
define i50 @test_signed_i50_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i50_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-671088640
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: mov w8, #1476395007
; CHECK-NEXT: mov x9, #-562949953421312
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fcvtzs x8, s0
; CHECK-NEXT: csel x8, x9, x8, lt
; CHECK-NEXT: mov x9, #562949953421311
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel x0, xzr, x8, vs
; CHECK-NEXT: ret
%x = call i50 @llvm.fptosi.sat.i50.f16(half %f)
ret i50 %x
}
define i64 @test_signed_i64_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i64_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #-553648128
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: mov w8, #1593835519
; CHECK-NEXT: mov x9, #-9223372036854775808
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fcvtzs x8, s0
; CHECK-NEXT: csel x8, x9, x8, lt
; CHECK-NEXT: mov x9, #9223372036854775807
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: fcmp s0, s0
; CHECK-NEXT: csel x0, xzr, x8, vs
; CHECK-NEXT: ret
%x = call i64 @llvm.fptosi.sat.i64.f16(half %f)
ret i64 %x
}
define i100 @test_signed_i100_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i100_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: fcvt s8, h0
; CHECK-NEXT: mov v0.16b, v8.16b
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: bl __fixsfti
; CHECK-NEXT: mov w8, #-251658240
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov w8, #1895825407
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov x8, #-34359738368
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: csel x8, x8, x1, lt
; CHECK-NEXT: mov x9, #34359738367
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: csinv x9, x10, xzr, le
; CHECK-NEXT: fcmp s8, s8
; CHECK-NEXT: csel x0, xzr, x9, vs
; CHECK-NEXT: csel x1, xzr, x8, vs
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i100 @llvm.fptosi.sat.i100.f16(half %f)
ret i100 %x
}
define i128 @test_signed_i128_f16(half %f) nounwind {
; CHECK-LABEL: test_signed_i128_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: fcvt s8, h0
; CHECK-NEXT: mov v0.16b, v8.16b
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: bl __fixsfti
; CHECK-NEXT: mov w8, #-16777216
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov w8, #2130706431
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov x8, #-9223372036854775808
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: csel x8, x8, x1, lt
; CHECK-NEXT: mov x9, #9223372036854775807
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csel x8, x9, x8, gt
; CHECK-NEXT: csinv x9, x10, xzr, le
; CHECK-NEXT: fcmp s8, s8
; CHECK-NEXT: csel x0, xzr, x9, vs
; CHECK-NEXT: csel x1, xzr, x8, vs
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i128 @llvm.fptosi.sat.i128.f16(half %f)
ret i128 %x
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,549 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=aarch64 < %s | FileCheck %s
;
; 32-bit float to unsigned integer
;
declare i1 @llvm.fptoui.sat.i1.f32 (float)
declare i8 @llvm.fptoui.sat.i8.f32 (float)
declare i13 @llvm.fptoui.sat.i13.f32 (float)
declare i16 @llvm.fptoui.sat.i16.f32 (float)
declare i19 @llvm.fptoui.sat.i19.f32 (float)
declare i32 @llvm.fptoui.sat.i32.f32 (float)
declare i50 @llvm.fptoui.sat.i50.f32 (float)
declare i64 @llvm.fptoui.sat.i64.f32 (float)
declare i100 @llvm.fptoui.sat.i100.f32(float)
declare i128 @llvm.fptoui.sat.i128.f32(float)
define i1 @test_unsigned_i1_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i1_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, #1.00000000
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w8, s0
; CHECK-NEXT: and w0, w8, #0x1
; CHECK-NEXT: ret
%x = call i1 @llvm.fptoui.sat.i1.f32(float %f)
ret i1 %x
}
define i8 @test_unsigned_i8_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i8_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: mov w8, #1132396544
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i8 @llvm.fptoui.sat.i8.f32(float %f)
ret i8 %x
}
define i13 @test_unsigned_i13_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i13_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #63488
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: movk w8, #17919, lsl #16
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i13 @llvm.fptoui.sat.i13.f32(float %f)
ret i13 %x
}
define i16 @test_unsigned_i16_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i16_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #65280
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: movk w8, #18303, lsl #16
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i16 @llvm.fptoui.sat.i16.f32(float %f)
ret i16 %x
}
define i19 @test_unsigned_i19_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i19_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #65504
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: movk w8, #18687, lsl #16
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i19 @llvm.fptoui.sat.i19.f32(float %f)
ret i19 %x
}
define i32 @test_unsigned_i32_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i32_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w9, #1333788671
; CHECK-NEXT: fcvtzu w8, s0
; CHECK-NEXT: fcmp s0, #0.0
; CHECK-NEXT: fmov s1, w9
; CHECK-NEXT: csel w8, wzr, w8, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csinv w0, w8, wzr, le
; CHECK-NEXT: ret
%x = call i32 @llvm.fptoui.sat.i32.f32(float %f)
ret i32 %x
}
define i50 @test_unsigned_i50_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i50_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w9, #1484783615
; CHECK-NEXT: fcvtzu x8, s0
; CHECK-NEXT: fcmp s0, #0.0
; CHECK-NEXT: fmov s1, w9
; CHECK-NEXT: csel x8, xzr, x8, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: mov x9, #1125899906842623
; CHECK-NEXT: csel x0, x9, x8, gt
; CHECK-NEXT: ret
%x = call i50 @llvm.fptoui.sat.i50.f32(float %f)
ret i50 %x
}
define i64 @test_unsigned_i64_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i64_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w9, #1602224127
; CHECK-NEXT: fcvtzu x8, s0
; CHECK-NEXT: fcmp s0, #0.0
; CHECK-NEXT: fmov s1, w9
; CHECK-NEXT: csel x8, xzr, x8, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csinv x0, x8, xzr, le
; CHECK-NEXT: ret
%x = call i64 @llvm.fptoui.sat.i64.f32(float %f)
ret i64 %x
}
define i100 @test_unsigned_i100_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i100_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixunssfti
; CHECK-NEXT: mov w8, #1904214015
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: fcmp s8, #0.0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov x9, #68719476735
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: csel x11, xzr, x1, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csel x1, x9, x11, gt
; CHECK-NEXT: csinv x0, x10, xzr, le
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i100 @llvm.fptoui.sat.i100.f32(float %f)
ret i100 %x
}
define i128 @test_unsigned_i128_f32(float %f) nounwind {
; CHECK-LABEL: test_unsigned_i128_f32:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixunssfti
; CHECK-NEXT: mov w8, #2139095039
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: fcmp s8, #0.0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: csel x9, xzr, x1, lt
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csinv x0, x10, xzr, le
; CHECK-NEXT: csinv x1, x9, xzr, le
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i128 @llvm.fptoui.sat.i128.f32(float %f)
ret i128 %x
}
;
; 64-bit float to unsigned integer
;
declare i1 @llvm.fptoui.sat.i1.f64 (double)
declare i8 @llvm.fptoui.sat.i8.f64 (double)
declare i13 @llvm.fptoui.sat.i13.f64 (double)
declare i16 @llvm.fptoui.sat.i16.f64 (double)
declare i19 @llvm.fptoui.sat.i19.f64 (double)
declare i32 @llvm.fptoui.sat.i32.f64 (double)
declare i50 @llvm.fptoui.sat.i50.f64 (double)
declare i64 @llvm.fptoui.sat.i64.f64 (double)
declare i100 @llvm.fptoui.sat.i100.f64(double)
declare i128 @llvm.fptoui.sat.i128.f64(double)
define i1 @test_unsigned_i1_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i1_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: fmov d1, xzr
; CHECK-NEXT: fmaxnm d0, d0, d1
; CHECK-NEXT: fmov d1, #1.00000000
; CHECK-NEXT: fminnm d0, d0, d1
; CHECK-NEXT: fcvtzu w8, d0
; CHECK-NEXT: and w0, w8, #0x1
; CHECK-NEXT: ret
%x = call i1 @llvm.fptoui.sat.i1.f64(double %f)
ret i1 %x
}
define i8 @test_unsigned_i8_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i8_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #246290604621824
; CHECK-NEXT: fmov d1, xzr
; CHECK-NEXT: movk x8, #16495, lsl #48
; CHECK-NEXT: fmaxnm d0, d0, d1
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fminnm d0, d0, d1
; CHECK-NEXT: fcvtzu w0, d0
; CHECK-NEXT: ret
%x = call i8 @llvm.fptoui.sat.i8.f64(double %f)
ret i8 %x
}
define i13 @test_unsigned_i13_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i13_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #280375465082880
; CHECK-NEXT: fmov d1, xzr
; CHECK-NEXT: movk x8, #16575, lsl #48
; CHECK-NEXT: fmaxnm d0, d0, d1
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fminnm d0, d0, d1
; CHECK-NEXT: fcvtzu w0, d0
; CHECK-NEXT: ret
%x = call i13 @llvm.fptoui.sat.i13.f64(double %f)
ret i13 %x
}
define i16 @test_unsigned_i16_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i16_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #281337537757184
; CHECK-NEXT: fmov d1, xzr
; CHECK-NEXT: movk x8, #16623, lsl #48
; CHECK-NEXT: fmaxnm d0, d0, d1
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fminnm d0, d0, d1
; CHECK-NEXT: fcvtzu w0, d0
; CHECK-NEXT: ret
%x = call i16 @llvm.fptoui.sat.i16.f64(double %f)
ret i16 %x
}
define i19 @test_unsigned_i19_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i19_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #281457796841472
; CHECK-NEXT: fmov d1, xzr
; CHECK-NEXT: movk x8, #16671, lsl #48
; CHECK-NEXT: fmaxnm d0, d0, d1
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fminnm d0, d0, d1
; CHECK-NEXT: fcvtzu w0, d0
; CHECK-NEXT: ret
%x = call i19 @llvm.fptoui.sat.i19.f64(double %f)
ret i19 %x
}
define i32 @test_unsigned_i32_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i32_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #281474974613504
; CHECK-NEXT: fmov d1, xzr
; CHECK-NEXT: movk x8, #16879, lsl #48
; CHECK-NEXT: fmaxnm d0, d0, d1
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fminnm d0, d0, d1
; CHECK-NEXT: fcvtzu w0, d0
; CHECK-NEXT: ret
%x = call i32 @llvm.fptoui.sat.i32.f64(double %f)
ret i32 %x
}
define i50 @test_unsigned_i50_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i50_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #-8
; CHECK-NEXT: fmov d1, xzr
; CHECK-NEXT: movk x8, #17167, lsl #48
; CHECK-NEXT: fmaxnm d0, d0, d1
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fminnm d0, d0, d1
; CHECK-NEXT: fcvtzu x0, d0
; CHECK-NEXT: ret
%x = call i50 @llvm.fptoui.sat.i50.f64(double %f)
ret i50 %x
}
define i64 @test_unsigned_i64_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i64_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x9, #4895412794951729151
; CHECK-NEXT: fcvtzu x8, d0
; CHECK-NEXT: fcmp d0, #0.0
; CHECK-NEXT: fmov d1, x9
; CHECK-NEXT: csel x8, xzr, x8, lt
; CHECK-NEXT: fcmp d0, d1
; CHECK-NEXT: csinv x0, x8, xzr, le
; CHECK-NEXT: ret
%x = call i64 @llvm.fptoui.sat.i64.f64(double %f)
ret i64 %x
}
define i100 @test_unsigned_i100_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i100_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixunsdfti
; CHECK-NEXT: mov x8, #5057542381537067007
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: fcmp d8, #0.0
; CHECK-NEXT: fmov d0, x8
; CHECK-NEXT: mov x9, #68719476735
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: csel x11, xzr, x1, lt
; CHECK-NEXT: fcmp d8, d0
; CHECK-NEXT: csel x1, x9, x11, gt
; CHECK-NEXT: csinv x0, x10, xzr, le
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i100 @llvm.fptoui.sat.i100.f64(double %f)
ret i100 %x
}
define i128 @test_unsigned_i128_f64(double %f) nounwind {
; CHECK-LABEL: test_unsigned_i128_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: mov v8.16b, v0.16b
; CHECK-NEXT: bl __fixunsdfti
; CHECK-NEXT: mov x8, #5183643171103440895
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: fcmp d8, #0.0
; CHECK-NEXT: fmov d0, x8
; CHECK-NEXT: csel x9, xzr, x1, lt
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp d8, d0
; CHECK-NEXT: csinv x0, x10, xzr, le
; CHECK-NEXT: csinv x1, x9, xzr, le
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i128 @llvm.fptoui.sat.i128.f64(double %f)
ret i128 %x
}
;
; 16-bit float to unsigned integer
;
declare i1 @llvm.fptoui.sat.i1.f16 (half)
declare i8 @llvm.fptoui.sat.i8.f16 (half)
declare i13 @llvm.fptoui.sat.i13.f16 (half)
declare i16 @llvm.fptoui.sat.i16.f16 (half)
declare i19 @llvm.fptoui.sat.i19.f16 (half)
declare i32 @llvm.fptoui.sat.i32.f16 (half)
declare i50 @llvm.fptoui.sat.i50.f16 (half)
declare i64 @llvm.fptoui.sat.i64.f16 (half)
declare i100 @llvm.fptoui.sat.i100.f16(half)
declare i128 @llvm.fptoui.sat.i128.f16(half)
define i1 @test_unsigned_i1_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i1_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, #1.00000000
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w8, s0
; CHECK-NEXT: and w0, w8, #0x1
; CHECK-NEXT: ret
%x = call i1 @llvm.fptoui.sat.i1.f16(half %f)
ret i1 %x
}
define i8 @test_unsigned_i8_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i8_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: mov w8, #1132396544
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i8 @llvm.fptoui.sat.i8.f16(half %f)
ret i8 %x
}
define i13 @test_unsigned_i13_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i13_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #63488
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: movk w8, #17919, lsl #16
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i13 @llvm.fptoui.sat.i13.f16(half %f)
ret i13 %x
}
define i16 @test_unsigned_i16_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i16_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #65280
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: movk w8, #18303, lsl #16
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i16 @llvm.fptoui.sat.i16.f16(half %f)
ret i16 %x
}
define i19 @test_unsigned_i19_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i19_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #65504
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: fmov s1, wzr
; CHECK-NEXT: movk w8, #18687, lsl #16
; CHECK-NEXT: fmaxnm s0, s0, s1
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: fminnm s0, s0, s1
; CHECK-NEXT: fcvtzu w0, s0
; CHECK-NEXT: ret
%x = call i19 @llvm.fptoui.sat.i19.f16(half %f)
ret i19 %x
}
define i32 @test_unsigned_i32_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i32_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: mov w8, #1333788671
; CHECK-NEXT: fcvtzu w9, s0
; CHECK-NEXT: fcmp s0, #0.0
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: csel w8, wzr, w9, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csinv w0, w8, wzr, le
; CHECK-NEXT: ret
%x = call i32 @llvm.fptoui.sat.i32.f16(half %f)
ret i32 %x
}
define i50 @test_unsigned_i50_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i50_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: mov w8, #1484783615
; CHECK-NEXT: fcvtzu x9, s0
; CHECK-NEXT: fcmp s0, #0.0
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: csel x8, xzr, x9, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: mov x9, #1125899906842623
; CHECK-NEXT: csel x0, x9, x8, gt
; CHECK-NEXT: ret
%x = call i50 @llvm.fptoui.sat.i50.f16(half %f)
ret i50 %x
}
define i64 @test_unsigned_i64_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i64_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: fcvt s0, h0
; CHECK-NEXT: mov w8, #1602224127
; CHECK-NEXT: fcvtzu x9, s0
; CHECK-NEXT: fcmp s0, #0.0
; CHECK-NEXT: fmov s1, w8
; CHECK-NEXT: csel x8, xzr, x9, lt
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: csinv x0, x8, xzr, le
; CHECK-NEXT: ret
%x = call i64 @llvm.fptoui.sat.i64.f16(half %f)
ret i64 %x
}
define i100 @test_unsigned_i100_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i100_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: fcvt s8, h0
; CHECK-NEXT: mov v0.16b, v8.16b
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: bl __fixunssfti
; CHECK-NEXT: mov w8, #1904214015
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: fcmp s8, #0.0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: mov x9, #68719476735
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: csel x11, xzr, x1, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csel x1, x9, x11, gt
; CHECK-NEXT: csinv x0, x10, xzr, le
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i100 @llvm.fptoui.sat.i100.f16(half %f)
ret i100 %x
}
define i128 @test_unsigned_i128_f16(half %f) nounwind {
; CHECK-LABEL: test_unsigned_i128_f16:
; CHECK: // %bb.0:
; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: fcvt s8, h0
; CHECK-NEXT: mov v0.16b, v8.16b
; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill
; CHECK-NEXT: bl __fixunssfti
; CHECK-NEXT: mov w8, #2139095039
; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload
; CHECK-NEXT: fcmp s8, #0.0
; CHECK-NEXT: fmov s0, w8
; CHECK-NEXT: csel x9, xzr, x1, lt
; CHECK-NEXT: csel x10, xzr, x0, lt
; CHECK-NEXT: fcmp s8, s0
; CHECK-NEXT: csinv x0, x10, xzr, le
; CHECK-NEXT: csinv x1, x9, xzr, le
; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
%x = call i128 @llvm.fptoui.sat.i128.f16(half %f)
ret i128 %x
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff