diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -21943,6 +21943,78 @@ modes. +Floating Point Test Intrinsics +------------------------------ + +These functions get properties of floating point values. + + +'``llvm.is.fpclass``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare i1 @llvm.is.fpclass( , metadata ) + declare @llvm.is.fpclass( , metadata ) + +Overview: +""""""""" + +The '``llvm.is.fpclass``' intrinsic returns a boolean value or vector of boolean +values depending on whether the first argument satisfies the test specified by +the second argument. + +If the first argument is a floating-point scalar, then the result type is a +boolean (:ref:`i1 `). + +If the first argument is a floating-point vector, then the result type is a +vector of boolean with the same number of elements as the first argument. + +Arguments: +"""""""""" + +The first argument to the '``llvm.is.fpclass``' intrinsic must be +:ref:`floating-point ` or :ref:`vector ` +of floating-point values. + +The second argument specifies the test to perform. It may represent a single +data class name. Such test is string with one of the following values: +- "``zero``": yields ``true`` if the value is a zero. +- "``+zero``": yields ``true`` if the value is a positive zero. +- "``-zero``": yields ``true`` if the value is a negative zero. +- "``subnormal``": yields ``true`` if the value is subnormal. +- "``+subnormal``": yields ``true`` if the value is positive subnormal. +- "``-subnormal``": yields ``true`` if the value is negative subnormal. +- "``normal``": yields ``true`` if the value is normal (neither zero, subnormal, + infinite, nor NaN). +- "``+normal``": yields ``true`` if the value is positive normal. +- "``-normal``": yields ``true`` if the value is positive normal. +- "``finite``": yields ``true`` if the value is finite (not an infinity or NaN). +- "``+finite``": yields ``true`` if the value is positive finite. +- "``-finite``": yields ``true`` if the value is negatitive finite. +- "``inf``": yields ``true`` if the value is an infinity. +- "``+inf``": yields ``true`` if the value is a positive infinity. +- "``-inf``": yields ``true`` if the value is a negative infinity. +- "``nan``": yields ``true`` if the value is a NAN. +- "``snan``": yields ``true`` if the value is a signaling NAN. +- "``qnan``": yields ``true`` if the value is a quietNAN. +The test aslo can be specified as a OR-ed combination of data classed, like +"zero|subnormal|+inf". In this case a number satisfies the test if its data +class is any of the combination. + +Semantics: +"""""""""" + +The function checks if ``op`` belongs to any of the floating point classes +specified by ``test``. If ``op`` is a vector, then the check is made element by +element. Each check yields an :ref:`i1 ` result, which is ``true``, +if the element value satisfies the specified test. The function never raises +floating point exceptions. + + General 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 @@ -17,6 +17,8 @@ namespace llvm { +class Value; + /// ISD namespace - This namespace contains an enum which represents all of the /// SelectionDAG node types and value types. /// @@ -482,6 +484,12 @@ /// Returns platform specific canonical encoding of a floating point number. FCANONICALIZE, + /// Performs a check of floating point class property, defined by IEEE-754. + /// The first operand is the floating point value to check. The second operand + /// specifies the checked property and is a TargetConstant with value obtained + /// from flags in FPClassCheck. Returns boolean value. + IS_FPCLASS, + /// BUILD_VECTOR(ELT0, ELT1, ELT2, ELT3,...) - Return a fixed-width vector /// with the specified, possibly variable, elements. The types of the /// operands must match the vector element type, except that integer types @@ -1447,6 +1455,36 @@ /// SETCC_INVALID if it is not possible to represent the resultant comparison. CondCode getSetCCAndOperation(CondCode Op1, CondCode Op2, EVT Type); +/// Floating point class tests, supported by IS_FPCLASS node. Actual test may be +/// an OR combination of these tests. +enum FPClassCheck { + fcBad = 0, + fcSNan = 0x0001, + fcQNan = 0x0002, + fcNegInf = 0x0004, + fcNegNormal = 0x0008, + fcNegSubnormal = 0x0010, + fcNegZero = 0x0020, + fcPosZero = 0x0040, + fcPosSubnormal = 0x0080, + fcPosNormal = 0x0100, + fcPosInf = 0x0200, + + fcNan = fcSNan | fcQNan, + fcInf = fcPosInf | fcNegInf, + fcNormal = fcPosNormal | fcNegNormal, + fcSubnormal = fcPosSubnormal | fcNegSubnormal, + fcZero = fcPosZero | fcNegZero, + fcPosFinite = fcPosNormal | fcPosSubnormal | fcPosZero, + fcNegFinite = fcNegNormal | fcNegSubnormal | fcNegZero, + fcFinite = fcPosFinite | fcNegFinite, + fcAllFlags = fcNan | fcInf | fcFinite +}; + +/// Convert floating point class test, which must be a metadata argument as +/// specified in invocations of `is_fpclass`, into corresponding numeric code. +unsigned getFPClassCheck(const Value *V); + } // namespace ISD } // namespace llvm 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 @@ -4443,6 +4443,24 @@ /// \returns The expansion result SDValue expandFP_TO_INT_SAT(SDNode *N, SelectionDAG &DAG) const; + /// Expand check for floating point class. + /// \param ResultVT The type of intrinsic call result + /// \param Op The tested value + /// \param Check The test to perform + /// \param Flags The optimization flags + /// \returns The expansion result or SDValue() if it fails. + SDValue expandIS_FPCLASS(EVT ResultVT, SDValue Op, unsigned Check, + SDNodeFlags Flags, const SDLoc &DL, + SelectionDAG &DAG) const; + + /// Check if the specified FP class test is an inversion of a simpler test. + /// An example is the test "inf|normal|subnormal|zero", which is an inversion + /// of "nan". + /// \param Check The test as specified in IS_FPCLASS node. + /// \returns The inverted test, or zero, if inversion does not produce simpler + /// test. + unsigned checkInvertedFPClass(unsigned Check) 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 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 @@ -717,6 +717,14 @@ def int_set_rounding : DefaultAttrsIntrinsic<[], [llvm_i32_ty]>; } +//===--------------- Floating Point Test Intrinsics -----------------------===// +// + +def int_is_fpclass + : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [llvm_anyfloat_ty, llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; + //===--------------- Constrained Floating Point Intrinsics ----------------===// // 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 @@ -1187,6 +1187,7 @@ case ISD::VECREDUCE_UMIN: case ISD::VECREDUCE_FMAX: case ISD::VECREDUCE_FMIN: + case ISD::IS_FPCLASS: Action = TLI.getOperationAction( Node->getOpcode(), Node->getOperand(0).getValueType()); break; @@ -3139,6 +3140,15 @@ case ISD::FABS: Results.push_back(ExpandFABS(Node)); break; + case ISD::IS_FPCLASS: { + auto CNode = cast(Node->getOperand(1)); + auto Check = static_cast(CNode->getZExtValue()); + if (SDValue Expanded = + TLI.expandIS_FPCLASS(Node->getValueType(0), Node->getOperand(0), + Check, Node->getFlags(), SDLoc(Node), DAG)) + Results.push_back(Expanded); + break; + } case ISD::SMIN: case ISD::SMAX: case ISD::UMIN: 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 @@ -286,6 +286,10 @@ case ISD::VP_LSHR: Res = PromoteIntRes_SRL(N, /*IsVP*/ true); break; + + case ISD::IS_FPCLASS: + Res = PromoteIntRes_IS_FPCLASS(N); + break; } // If the result is null then the sub-method took care of registering it. @@ -1156,6 +1160,14 @@ return DAG.getSExtOrTrunc(SetCC, dl, NVT); } +SDValue DAGTypeLegalizer::PromoteIntRes_IS_FPCLASS(SDNode *N) { + SDLoc DL(N); + SDValue Arg = N->getOperand(0); + SDValue Check = N->getOperand(1); + EVT NResVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); + return DAG.getNode(ISD::IS_FPCLASS, DL, NResVT, Arg, Check); +} + SDValue DAGTypeLegalizer::PromoteIntRes_SHL(SDNode *N, bool IsVP) { SDValue LHS = GetPromotedInteger(N->getOperand(0)); SDValue RHS = N->getOperand(1); 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 @@ -363,6 +363,7 @@ SDValue PromoteIntRes_ABS(SDNode *N); SDValue PromoteIntRes_Rotate(SDNode *N); SDValue PromoteIntRes_FunnelShift(SDNode *N); + SDValue PromoteIntRes_IS_FPCLASS(SDNode *N); // Integer Operand Promotion. bool PromoteIntegerOperand(SDNode *N, unsigned OpNo); @@ -785,6 +786,7 @@ SDValue ScalarizeVecRes_UNDEF(SDNode *N); SDValue ScalarizeVecRes_VECTOR_SHUFFLE(SDNode *N); SDValue ScalarizeVecRes_FP_TO_XINT_SAT(SDNode *N); + SDValue ScalarizeVecRes_IS_FPCLASS(SDNode *N); SDValue ScalarizeVecRes_FIX(SDNode *N); @@ -845,6 +847,7 @@ void SplitVecRes_INSERT_SUBVECTOR(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_FPOWI(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_FCOPYSIGN(SDNode *N, SDValue &Lo, SDValue &Hi); + void SplitVecRes_IS_FPCLASS(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_INSERT_VECTOR_ELT(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_LOAD(LoadSDNode *LD, SDValue &Lo, SDValue &Hi); void SplitVecRes_MLOAD(MaskedLoadSDNode *MLD, SDValue &Lo, SDValue &Hi); @@ -931,6 +934,7 @@ SDValue WidenVecRes_Convert_StrictFP(SDNode *N); SDValue WidenVecRes_FP_TO_XINT_SAT(SDNode *N); SDValue WidenVecRes_FCOPYSIGN(SDNode *N); + SDValue WidenVecRes_IS_FPCLASS(SDNode *N); SDValue WidenVecRes_POWI(SDNode *N); SDValue WidenVecRes_Unary(SDNode *N); SDValue WidenVecRes_InregOp(SDNode *N); @@ -954,6 +958,7 @@ SDValue WidenVecOp_Convert(SDNode *N); SDValue WidenVecOp_FP_TO_XINT_SAT(SDNode *N); SDValue WidenVecOp_FCOPYSIGN(SDNode *N); + SDValue WidenVecOp_IS_FPCLASS(SDNode *N); SDValue WidenVecOp_VECREDUCE(SDNode *N); SDValue WidenVecOp_VECREDUCE_SEQ(SDNode *N); 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 @@ -64,6 +64,7 @@ case ISD::SETCC: R = ScalarizeVecRes_SETCC(N); break; case ISD::UNDEF: R = ScalarizeVecRes_UNDEF(N); break; case ISD::VECTOR_SHUFFLE: R = ScalarizeVecRes_VECTOR_SHUFFLE(N); break; + case ISD::IS_FPCLASS: R = ScalarizeVecRes_IS_FPCLASS(N); break; case ISD::ANY_EXTEND_VECTOR_INREG: case ISD::SIGN_EXTEND_VECTOR_INREG: case ISD::ZERO_EXTEND_VECTOR_INREG: @@ -582,6 +583,29 @@ return DAG.getNode(ExtendCode, DL, NVT, Res); } +SDValue DAGTypeLegalizer::ScalarizeVecRes_IS_FPCLASS(SDNode *N) { + SDLoc DL(N); + SDValue Arg = N->getOperand(0); + SDValue Check = N->getOperand(1); + EVT ArgVT = Arg.getValueType(); + EVT ResultVT = N->getValueType(0).getVectorElementType(); + + if (getTypeAction(ArgVT) == TargetLowering::TypeScalarizeVector) { + Arg = GetScalarizedVector(Arg); + } else { + EVT VT = ArgVT.getVectorElementType(); + Arg = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, VT, Arg, + DAG.getVectorIdxConstant(0, DL)); + } + + SDValue Res = + DAG.getNode(ISD::IS_FPCLASS, DL, MVT::i1, {Arg, Check}, N->getFlags()); + // Vectors may have a different boolean contents to scalars. Promote the + // value appropriately. + ISD::NodeType ExtendCode = + TargetLowering::getExtendForContent(TLI.getBooleanContents(ArgVT)); + return DAG.getNode(ExtendCode, DL, ResultVT, Res); +} //===----------------------------------------------------------------------===// // Operand Vector Scalarization <1 x ty> -> ty. @@ -924,6 +948,7 @@ case ISD::INSERT_SUBVECTOR: SplitVecRes_INSERT_SUBVECTOR(N, Lo, Hi); break; case ISD::FPOWI: SplitVecRes_FPOWI(N, Lo, Hi); break; case ISD::FCOPYSIGN: SplitVecRes_FCOPYSIGN(N, Lo, Hi); break; + case ISD::IS_FPCLASS: SplitVecRes_IS_FPCLASS(N, Lo, Hi); break; case ISD::INSERT_VECTOR_ELT: SplitVecRes_INSERT_VECTOR_ELT(N, Lo, Hi); break; case ISD::SPLAT_VECTOR: case ISD::SCALAR_TO_VECTOR: @@ -1413,6 +1438,19 @@ Hi = DAG.getNode(ISD::FCOPYSIGN, DL, LHSHi.getValueType(), LHSHi, RHSHi); } +void DAGTypeLegalizer::SplitVecRes_IS_FPCLASS(SDNode *N, SDValue &Lo, + SDValue &Hi) { + SDLoc DL(N); + SDValue ArgLo, ArgHi; + SDValue Check = N->getOperand(1); + GetSplitVector(N->getOperand(0), ArgLo, ArgHi); + EVT LoVT, HiVT; + std::tie(LoVT, HiVT) = DAG.GetSplitDestVTs(N->getValueType(0)); + + Lo = DAG.getNode(ISD::IS_FPCLASS, DL, LoVT, ArgLo, Check, N->getFlags()); + Hi = DAG.getNode(ISD::IS_FPCLASS, DL, HiVT, ArgHi, Check, N->getFlags()); +} + void DAGTypeLegalizer::SplitVecRes_InregOp(SDNode *N, SDValue &Lo, SDValue &Hi) { SDValue LHSLo, LHSHi; @@ -3132,6 +3170,10 @@ Res = WidenVecRes_FCOPYSIGN(N); break; + case ISD::IS_FPCLASS: + Res = WidenVecRes_IS_FPCLASS(N); + break; + case ISD::FPOWI: Res = WidenVecRes_POWI(N); break; @@ -3834,6 +3876,13 @@ return DAG.UnrollVectorOp(N, WidenVT.getVectorNumElements()); } +SDValue DAGTypeLegalizer::WidenVecRes_IS_FPCLASS(SDNode *N) { + EVT WidenVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); + SDValue Arg = GetWidenedVector(N->getOperand(0)); + return DAG.getNode(N->getOpcode(), SDLoc(N), WidenVT, {Arg, N->getOperand(1)}, + N->getFlags()); +} + SDValue DAGTypeLegalizer::WidenVecRes_POWI(SDNode *N) { EVT WidenVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); SDValue InOp = GetWidenedVector(N->getOperand(0)); @@ -4675,6 +4724,7 @@ case ISD::STRICT_FSETCCS: Res = WidenVecOp_STRICT_FSETCC(N); break; case ISD::VSELECT: Res = WidenVecOp_VSELECT(N); break; case ISD::FCOPYSIGN: Res = WidenVecOp_FCOPYSIGN(N); break; + case ISD::IS_FPCLASS: Res = WidenVecOp_IS_FPCLASS(N); break; case ISD::ANY_EXTEND: case ISD::SIGN_EXTEND: @@ -4810,6 +4860,34 @@ return DAG.UnrollVectorOp(N); } +SDValue DAGTypeLegalizer::WidenVecOp_IS_FPCLASS(SDNode *N) { + SDLoc DL(N); + EVT ResultVT = N->getValueType(0); + SDValue Check = N->getOperand(1); + SDValue WideArg = GetWidenedVector(N->getOperand(0)); + + // Process this node similarly to SETCC. + EVT WideResultVT = getSetCCResultType(WideArg.getValueType()); + if (ResultVT.getScalarType() == MVT::i1) + WideResultVT = EVT::getVectorVT(*DAG.getContext(), MVT::i1, + WideResultVT.getVectorNumElements()); + + SDValue WideNode = DAG.getNode(ISD::IS_FPCLASS, DL, WideResultVT, + {WideArg, Check}, N->getFlags()); + + // Extract the needed results from the result vector. + EVT ResVT = + EVT::getVectorVT(*DAG.getContext(), WideResultVT.getVectorElementType(), + ResultVT.getVectorNumElements()); + SDValue CC = DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ResVT, WideNode, + DAG.getVectorIdxConstant(0, DL)); + + EVT OpVT = N->getOperand(0).getValueType(); + ISD::NodeType ExtendCode = + TargetLowering::getExtendForContent(TLI.getBooleanContents(OpVT)); + return DAG.getNode(ExtendCode, DL, ResultVT, CC); +} + SDValue DAGTypeLegalizer::WidenVecOp_Convert(SDNode *N) { // Since the result is legal and the input is illegal. EVT VT = N->getValueType(0); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -570,6 +570,41 @@ return Result; } +unsigned ISD::getFPClassCheck(const llvm::Value *V) { + Metadata *MD = cast(V)->getMetadata(); + if (!MD || !isa(MD)) + return ISD::FPClassCheck::fcBad; + StringRef Val = cast(MD)->getString(); + unsigned Res = 0; + do { + StringRef Flag; + std::tie(Flag, Val) = Val.split("|"); + unsigned FlagVal = StringSwitch(Flag) + .Case("+zero", fcPosZero) + .Case("-zero", fcNegZero) + .Case("zero", fcZero) + .Case("+subnormal", fcPosSubnormal) + .Case("-subnormal", fcNegSubnormal) + .Case("subnormal", fcSubnormal) + .Case("+normal", fcPosNormal) + .Case("-normal", fcNegNormal) + .Case("normal", fcNormal) + .Case("+finite", fcPosFinite) + .Case("-finite", fcNegFinite) + .Case("finite", fcFinite) + .Case("+inf", fcPosInf) + .Case("-inf", fcNegInf) + .Case("inf", fcInf) + .Case("qnan", fcQNan) + .Case("snan", fcSNan) + .Case("nan", fcNan) + .Default(fcBad); + assert(FlagVal != fcBad && "Invalid fpclass test"); + Res |= FlagVal; + } while (!Val.empty()); + return Res; +} + //===----------------------------------------------------------------------===// // SDNode Profile Support //===----------------------------------------------------------------------===// 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 @@ -6401,6 +6401,32 @@ DAG.setRoot(DAG.getNode(ISD::PCMARKER, sdl, MVT::Other, getRoot(), Tmp)); return; } + case Intrinsic::is_fpclass: { + const DataLayout DLayout = DAG.getDataLayout(); + EVT DestVT = TLI.getValueType(DLayout, I.getType()); + EVT ArgVT = TLI.getValueType(DLayout, I.getArgOperand(0)->getType()); + unsigned FC = ISD::getFPClassCheck(I.getArgOperand(1)); + assert(FC != ISD::fcBad); + MachineFunction &MF = DAG.getMachineFunction(); + const Function &F = MF.getFunction(); + SDValue Op = getValue(I.getArgOperand(0)); + SDNodeFlags Flags; + Flags.setNoFPExcept( + !F.getAttributes().hasFnAttr(llvm::Attribute::StrictFP)); + // If ISD::IS_FPCLASS should be expanded, do it right now, because the + // expansion can use illegal types. Making expansion early allows + // legalizing these types prior to selection. + if (!TLI.isOperationLegalOrCustom(ISD::IS_FPCLASS, ArgVT)) { + SDValue Result = TLI.expandIS_FPCLASS(DestVT, Op, FC, Flags, sdl, DAG); + setValue(&I, Result); + return; + } + + SDValue Check = DAG.getTargetConstant(FC, sdl, MVT::i32); + SDValue V = DAG.getNode(ISD::IS_FPCLASS, sdl, DestVT, {Op, Check}, Flags); + setValue(&I, V); + return; + } case Intrinsic::readcyclecounter: { SDValue Op = getRoot(); Res = DAG.getNode(ISD::READCYCLECOUNTER, sdl, 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 @@ -267,6 +267,7 @@ case ISD::FCOPYSIGN: return "fcopysign"; case ISD::FGETSIGN: return "fgetsign"; case ISD::FCANONICALIZE: return "fcanonicalize"; + case ISD::IS_FPCLASS: return "is_fpclass"; case ISD::FPOW: return "fpow"; case ISD::STRICT_FPOW: return "strict_fpow"; case ISD::SMIN: return "smin"; 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 @@ -6980,6 +6980,219 @@ return SDValue(); } +SDValue TargetLowering::expandIS_FPCLASS(EVT ResultVT, SDValue Op, + unsigned Check, SDNodeFlags Flags, + const SDLoc &DL, + SelectionDAG &DAG) const { + EVT OperandVT = Op.getValueType(); + assert(OperandVT.isFloatingPoint()); + + APInt InvertMask = APInt::getAllOnesValue(ResultVT.getScalarSizeInBits()); + SDValue InvertMaskV = DAG.getConstant(InvertMask, DL, ResultVT); + + bool IsInverted = false; + if (unsigned InvertedCheck = checkInvertedFPClass(Check)) { + IsInverted = true; + Check = InvertedCheck; + } + + // Some checks can be implemented using float comparisons, if floating point + // exceptions are ignored. + if (Flags.hasNoFPExcept() && + isOperationLegalOrCustom(ISD::SETCC, OperandVT.getScalarType())) { + if (Check == ISD::fcZero) + return DAG.getSetCC(DL, ResultVT, Op, + DAG.getConstantFP(0.0, DL, OperandVT), + IsInverted ? ISD::SETUNE : ISD::SETOEQ); + if (Check == ISD::fcNan) + return DAG.getSetCC(DL, ResultVT, Op, Op, + IsInverted ? ISD::SETO : ISD::SETUO); + } + + // In the general case use integer operations. They also allow avoiding + // floating point exceptions. + unsigned BitSize = OperandVT.getScalarSizeInBits(); + EVT IntVT = OperandVT.changeTypeToInteger(); + SDValue OpAsInt = DAG.getBitcast(IntVT, Op); + + EVT ScalarFloatVT = OperandVT.getScalarType(); + const Type *FloatTy = ScalarFloatVT.getTypeForEVT(*DAG.getContext()); + const llvm::fltSemantics &Semantics = FloatTy->getFltSemantics(); + + APInt SignMask = APInt::getSignedMaxValue(BitSize); + APInt SignBit = APInt::getSignMask(BitSize); + APInt ExpMask = APFloat::getInf(Semantics).bitcastToAPInt(); + APInt AllOneMantissa = + APFloat::getLargest(Semantics).bitcastToAPInt() & ~ExpMask; + APInt QNaNBitMask = AllOneMantissa & ~(AllOneMantissa.lshr(1)); + + SDValue SignMaskV = DAG.getConstant(SignMask, DL, IntVT); + SDValue SignBitV = DAG.getConstant(SignBit, DL, IntVT); + SDValue ExpMaskV = DAG.getConstant(ExpMask, DL, IntVT); + + SDValue Res; + const auto CombineResult = [&](SDValue V) -> bool { + if (!V) + return false; + if (IsInverted) { + assert(!Res); + Res = DAG.getNode(ISD::XOR, DL, ResultVT, V, InvertMaskV); + return true; + } + if (Res) + Res = DAG.getNode(ISD::OR, DL, ResultVT, Res, V); + else + Res = V; + return false; + }; + + // Split the value into sign bit and absolute value. + SDValue AbsV = DAG.getNode(ISD::AND, DL, IntVT, OpAsInt, SignMaskV); + SDValue SignV = DAG.getSetCC(DL, ResultVT, OpAsInt, + DAG.getConstant(0.0, DL, IntVT), ISD::SETLT); + + // The tests that involve more than one data classes should be processed + // first. + SDValue PartialRes; + + unsigned PartialCheck = Check & ISD::fcFinite; + if (PartialCheck == ISD::fcFinite) { + // finite(V) ==> exp mask > abs(V) + PartialRes = DAG.getSetCC(DL, ResultVT, ExpMaskV, AbsV, ISD::SETGT); + Check &= ~ISD::fcFinite; + } else if (PartialCheck == ISD::fcPosFinite) { + // finite(V) && V > 0 ==> exp mask > V + PartialRes = DAG.getSetCC(DL, ResultVT, ExpMaskV, OpAsInt, ISD::SETUGT); + Check &= ~ISD::fcPosFinite; + } else if (PartialCheck == ISD::fcNegFinite) { + // finite(V) && V < 0 ==> exp mask > abs(V) && signbit == 1 + PartialRes = DAG.getSetCC(DL, ResultVT, ExpMaskV, AbsV, ISD::SETGT); + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, SignV); + Check &= ~ISD::fcNegFinite; + } + if (CombineResult(PartialRes)) + return Res; + + // Check for individual classes. + + PartialCheck = Check & ISD::fcZero; + if (PartialCheck) { + if (PartialCheck == ISD::fcPosZero) + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, + DAG.getConstant(0, DL, IntVT), ISD::SETEQ); + else if (PartialCheck == ISD::fcZero) + PartialRes = DAG.getSetCC(DL, ResultVT, AbsV, + DAG.getConstant(0, DL, IntVT), ISD::SETEQ); + else // ISD::fcNegZero + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, SignBitV, ISD::SETEQ); + if (CombineResult(PartialRes)) + return Res; + } + + PartialCheck = Check & ISD::fcInf; + if (PartialCheck) { + if (PartialCheck == ISD::fcPosInf) + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, ExpMaskV, ISD::SETEQ); + else if (PartialCheck == ISD::fcInf) + PartialRes = DAG.getSetCC(DL, ResultVT, ExpMaskV, AbsV, ISD::SETEQ); + else { // ISD::fcNegInf + APInt NegInf = APFloat::getInf(Semantics, true).bitcastToAPInt(); + SDValue NegInfV = DAG.getConstant(NegInf, DL, IntVT); + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, NegInfV, ISD::SETEQ); + } + if (CombineResult(PartialRes)) + return Res; + } + + PartialCheck = Check & ISD::fcNan; + if (PartialCheck) { + APInt ExpWithQNaNBit = ExpMask | QNaNBitMask; + SDValue ExpWithQNaNBitV = DAG.getConstant(ExpWithQNaNBit, DL, IntVT); + if (PartialCheck == ISD::fcNan) + // isnan(V) ==> exp mask < abs(V) + PartialRes = DAG.getSetCC(DL, ResultVT, ExpMaskV, AbsV, ISD::SETLT); + else if (PartialCheck == ISD::fcQNan) + // isquiet(V) ==> unsigned(V) >= exp_mask | quiet_bit + PartialRes = + DAG.getSetCC(DL, ResultVT, AbsV, ExpWithQNaNBitV, ISD::SETGE); + else { // ISD::fcSNan + // issignaling(V) ==> abs(V) > exp_mask && + // abs(V) < (exp_mask | quiet_bit) + SDValue IsNaN = DAG.getSetCC(DL, ResultVT, AbsV, ExpMaskV, ISD::SETGT); + SDValue IsNotQNaN = + DAG.getSetCC(DL, ResultVT, AbsV, ExpWithQNaNBitV, ISD::SETLT); + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, IsNaN, IsNotQNaN); + } + if (CombineResult(PartialRes)) + return Res; + } + + PartialCheck = Check & ISD::fcSubnormal; + if (PartialCheck) { + // issubnormal(V) ==> unsigned(abs(V) - 1) < (all mantissa bits set) + // issubnormal(V) && V>0 ==> unsigned(V - 1) < (all mantissa bits set) + SDValue V = (PartialCheck == ISD::fcPosSubnormal) ? OpAsInt : AbsV; + SDValue MantissaV = DAG.getConstant(AllOneMantissa, DL, IntVT); + SDValue MinusOneV = + DAG.getNode(ISD::SUB, DL, IntVT, V, DAG.getConstant(1, DL, IntVT)); + PartialRes = DAG.getSetCC(DL, ResultVT, MinusOneV, MantissaV, ISD::SETULT); + if (PartialCheck == ISD::fcNegSubnormal) + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, SignV); + if (CombineResult(PartialRes)) + return Res; + } + + PartialCheck = Check & ISD::fcNormal; + if (PartialCheck) { + // isnormal(V) ==> (0 < exp < max_exp) == (unsigned(exp-1) < (max_exp-1)) + APInt ExpLSB = ExpMask & ~(ExpMask.shl(1)); + SDValue ExpLSBV = DAG.getConstant(ExpLSB, DL, IntVT); + SDValue DecExp = DAG.getNode(ISD::SUB, DL, IntVT, AbsV, ExpLSBV); + APInt ExpLimit = ExpMask - ExpLSB; + SDValue ExpLimitV = DAG.getConstant(ExpLimit, DL, IntVT); + PartialRes = DAG.getSetCC(DL, ResultVT, DecExp, ExpLimitV, ISD::SETULT); + if (PartialCheck == ISD::fcNegNormal) + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, SignV); + else if (PartialCheck == ISD::fcPosNormal) { + SDValue PosSignV = + DAG.getNode(ISD::XOR, DL, ResultVT, SignV, InvertMaskV); + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, PosSignV); + } + if (CombineResult(PartialRes)) + return Res; + } + + return Res; +} + +unsigned TargetLowering::checkInvertedFPClass(unsigned Test) const { + unsigned InvertedTest = ~Test & ISD::fcAllFlags; + switch (InvertedTest) { + default: + break; + case ISD::fcNan: + case ISD::fcSNan: + case ISD::fcQNan: + case ISD::fcInf: + case ISD::fcPosInf: + case ISD::fcNegInf: + case ISD::fcNormal: + case ISD::fcPosNormal: + case ISD::fcNegNormal: + case ISD::fcSubnormal: + case ISD::fcPosSubnormal: + case ISD::fcNegSubnormal: + case ISD::fcZero: + case ISD::fcPosZero: + case ISD::fcNegZero: + case ISD::fcFinite: + case ISD::fcPosFinite: + case ISD::fcNegFinite: + return InvertedTest; + } + return 0; +} + // Only expand vector types if we have the appropriate vector bit operations. static bool canExpandVectorCTPOP(const TargetLowering &TLI, EVT VT) { assert(VT.isVector() && "Expected vector type"); 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 @@ -763,6 +763,7 @@ // These operations default to expand. setOperationAction(ISD::FGETSIGN, VT, Expand); + setOperationAction(ISD::IS_FPCLASS, VT, Expand); setOperationAction(ISD::CONCAT_VECTORS, VT, Expand); setOperationAction(ISD::FMINNUM, VT, Expand); setOperationAction(ISD::FMAXNUM, VT, Expand); diff --git a/llvm/test/CodeGen/X86/x86-is_fpclass.ll b/llvm/test/CodeGen/X86/x86-is_fpclass.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/x86-is_fpclass.ll @@ -0,0 +1,943 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=i686 | FileCheck %s -check-prefix=CHECK-32 +; RUN: llc < %s -mtriple=x86_64 | FileCheck %s -check-prefix=CHECK-64 + +define i1 @isnan_f(float %x) { +; CHECK-32-LABEL: isnan_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomiss %xmm0, %xmm0 +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"nan") + ret i1 %0 +} + +define i1 @isnot_nan_f(float %x) { +; CHECK-32-LABEL: isnot_nan_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnot_nan_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomiss %xmm0, %xmm0 +; CHECK-64-NEXT: setnp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"zero|subnormal|normal|inf") + ret i1 %0 +} + +define i1 @issignaling_f(float %x) { +; CHECK-32-LABEL: issignaling_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; CHECK-32-NEXT: setl %cl +; CHECK-32-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issignaling_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; CHECK-64-NEXT: setl %cl +; CHECK-64-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"snan") + ret i1 %0 +} + +define i1 @isquiet_f(float %x) { +; CHECK-32-LABEL: isquiet_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isquiet_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"qnan") + ret i1 %0 +} + +define i1 @isinf_f(float %x) { +; CHECK-32-LABEL: isinf_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isinf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"inf") + ret i1 %0 +} + +define i1 @is_plus_inf_f(float %x) { +; CHECK-32-LABEL: is_plus_inf_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: cmpl $2139095040, {{[0-9]+}}(%esp) # imm = 0x7F800000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_plus_inf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"+inf") + ret i1 %0 +} + +define i1 @is_minus_inf_f(float %x) { +; CHECK-32-LABEL: is_minus_inf_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: cmpl $-8388608, {{[0-9]+}}(%esp) # imm = 0xFF800000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_minus_inf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: cmpl $-8388608, %eax # imm = 0xFF800000 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"-inf") + ret i1 %0 +} + +define i1 @isfinite_f(float %x) { +; CHECK-32-LABEL: isfinite_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isfinite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"finite") + ret i1 %0 +} + +define i1 @is_plus_finite_f(float %x) { +; CHECK-32-LABEL: is_plus_finite_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: cmpl $2139095040, {{[0-9]+}}(%esp) # imm = 0x7F800000 +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_plus_finite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"+finite") + ret i1 %0 +} + +define i1 @is_minus_finite_f(float %x) { +; CHECK-32-LABEL: is_minus_finite_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: testl %eax, %eax +; CHECK-32-NEXT: sets %cl +; CHECK-32-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_minus_finite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: testl %eax, %eax +; CHECK-64-NEXT: sets %cl +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"-finite") + ret i1 %0 +} + +define i1 @isnormal_f(float %x) { +; CHECK-32-LABEL: isnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: addl $-8388608, %eax # imm = 0xFF800000 +; CHECK-32-NEXT: cmpl $2130706432, %eax # imm = 0x7F000000 +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: addl $-8388608, %eax # imm = 0xFF800000 +; CHECK-64-NEXT: cmpl $2130706432, %eax # imm = 0x7F000000 +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"normal") + ret i1 %0 +} + +define i1 @is_plus_normal_f(float %x) { +; CHECK-32-LABEL: is_plus_normal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: testl %eax, %eax +; CHECK-32-NEXT: setns %cl +; CHECK-32-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: addl $-8388608, %eax # imm = 0xFF800000 +; CHECK-32-NEXT: cmpl $2130706432, %eax # imm = 0x7F000000 +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_plus_normal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: testl %eax, %eax +; CHECK-64-NEXT: setns %cl +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: addl $-8388608, %eax # imm = 0xFF800000 +; CHECK-64-NEXT: cmpl $2130706432, %eax # imm = 0x7F000000 +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"+normal") + ret i1 %0 +} + +define i1 @issubnormal_f(float %x) { +; CHECK-32-LABEL: issubnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: decl %eax +; CHECK-32-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issubnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: decl %eax +; CHECK-64-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"subnormal") + ret i1 %0 +} + +define i1 @is_plus_subnormal_f(float %x) { +; CHECK-32-LABEL: is_plus_subnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: decl %eax +; CHECK-32-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_plus_subnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: decl %eax +; CHECK-64-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"+subnormal") + ret i1 %0 +} + +define i1 @is_minus_subnormal_f(float %x) { +; CHECK-32-LABEL: is_minus_subnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: testl %eax, %eax +; CHECK-32-NEXT: sets %cl +; CHECK-32-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: decl %eax +; CHECK-32-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_minus_subnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: testl %eax, %eax +; CHECK-64-NEXT: sets %cl +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: decl %eax +; CHECK-64-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"-subnormal") + ret i1 %0 +} + +define i1 @iszero_f(float %x) { +; CHECK-32-LABEL: iszero_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fldz +; CHECK-32-NEXT: fucompp +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %cl +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorps %xmm1, %xmm1 +; CHECK-64-NEXT: cmpeqss %xmm0, %xmm1 +; CHECK-64-NEXT: movd %xmm1, %eax +; CHECK-64-NEXT: andl $1, %eax +; CHECK-64-NEXT: # kill: def $al killed $al killed $eax +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"zero") + ret i1 %0 +} + +define i1 @is_plus_zero_f(float %x) { +; CHECK-32-LABEL: is_plus_zero_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: cmpl $0, {{[0-9]+}}(%esp) +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_plus_zero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: testl %eax, %eax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"+zero") + ret i1 %0 +} + +define i1 @is_minus_zero_f(float %x) { +; CHECK-32-LABEL: is_minus_zero_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: cmpl $-2147483648, {{[0-9]+}}(%esp) # imm = 0x80000000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_minus_zero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: cmpl $-2147483648, %eax # imm = 0x80000000 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"-zero") + ret i1 %0 +} + + + +define i1 @isnan_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: isnan_f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"nan") + ret i1 %0 +} + +define i1 @isfinite_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: isfinite_f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isfinite_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"finite") + ret i1 %0 +} + +define i1 @iszero_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: iszero_f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: testl $2147483647, {{[0-9]+}}(%esp) # imm = 0x7FFFFFFF +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: testl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, metadata !"zero") + ret i1 %0 +} + + +define i1 @isnan_d(double %x) { +; CHECK-32-LABEL: isnan_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldl {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomisd %xmm0, %xmm0 +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"nan") + ret i1 %0 +} + +define i1 @isinf_d(double %x) { +; CHECK-32-LABEL: isinf_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: xorl $2146435072, %eax # imm = 0x7FF00000 +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isinf_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9218868437227405312, %rax # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"inf") + ret i1 %0 +} + +define i1 @isfinite_d(double %x) { +; CHECK-32-LABEL: isfinite_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2146435072, %eax # imm = 0x7FF00000 +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isfinite_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9218868437227405312, %rax # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"finite") + ret i1 %0 +} + +define i1 @isnormal_d(double %x) { +; CHECK-32-LABEL: isnormal_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: addl $-1048576, %eax # imm = 0xFFF00000 +; CHECK-32-NEXT: shrl $21, %eax +; CHECK-32-NEXT: cmpl $1023, %eax # imm = 0x3FF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnormal_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $-4503599627370496, %rax # imm = 0xFFF0000000000000 +; CHECK-64-NEXT: addq %rcx, %rax +; CHECK-64-NEXT: shrq $53, %rax +; CHECK-64-NEXT: cmpl $1023, %eax # imm = 0x3FF +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"normal") + ret i1 %0 +} + +define i1 @issubnormal_d(double %x) { +; CHECK-32-LABEL: issubnormal_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: movl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: addl $-1, %eax +; CHECK-32-NEXT: adcl $-1, %ecx +; CHECK-32-NEXT: cmpl $-1, %eax +; CHECK-32-NEXT: sbbl $1048575, %ecx # imm = 0xFFFFF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issubnormal_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: decq %rcx +; CHECK-64-NEXT: movabsq $4503599627370495, %rax # imm = 0xFFFFFFFFFFFFF +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"subnormal") + ret i1 %0 +} + +define i1 @iszero_d(double %x) { +; CHECK-32-LABEL: iszero_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldl {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fldz +; CHECK-32-NEXT: fucompp +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %cl +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorpd %xmm1, %xmm1 +; CHECK-64-NEXT: cmpeqsd %xmm0, %xmm1 +; CHECK-64-NEXT: movq %xmm1, %rax +; CHECK-64-NEXT: andl $1, %eax +; CHECK-64-NEXT: # kill: def $al killed $al killed $rax +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"zero") + ret i1 %0 +} + +define i1 @issignaling_d(double %x) { +; CHECK-32-LABEL: issignaling_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: xorl %ecx, %ecx +; CHECK-32-NEXT: cmpl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: movl $2146435072, %ecx # imm = 0x7FF00000 +; CHECK-32-NEXT: sbbl %eax, %ecx +; CHECK-32-NEXT: setl %cl +; CHECK-32-NEXT: cmpl $2146959360, %eax # imm = 0x7FF80000 +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issignaling_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9221120237041090560, %rax # imm = 0x7FF8000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setl %dl +; CHECK-64-NEXT: movabsq $9218868437227405312, %rax # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: andb %dl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"snan") + ret i1 %0 +} + +define i1 @isquiet_d(double %x) { +; CHECK-32-LABEL: isquiet_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2146959360, %eax # imm = 0x7FF80000 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isquiet_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9221120237041090559, %rax # imm = 0x7FF7FFFFFFFFFFFF +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"qnan") + ret i1 %0 +} + +define i1 @isnan_d_strictfp(double %x) strictfp { +; CHECK-32-LABEL: isnan_d_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: xorl %ecx, %ecx +; CHECK-32-NEXT: cmpl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: movl $2146435072, %ecx # imm = 0x7FF00000 +; CHECK-32-NEXT: sbbl %eax, %ecx +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_d_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9218868437227405312, %rax # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"nan") + ret i1 %0 +} + +define i1 @iszero_d_strictfp(double %x) strictfp { +; CHECK-32-LABEL: iszero_d_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_d_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: shlq $1, %rax +; CHECK-64-NEXT: testq %rax, %rax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, metadata !"zero") + ret i1 %0 +} + + + +define <1 x i1> @isnan_v1f(<1 x float> %x) { +; CHECK-32-LABEL: isnan_v1f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_v1f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomiss %xmm0, %xmm0 +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call <1 x i1> @llvm.is.fpclass.v1f32(<1 x float> %x, metadata !"nan") + ret <1 x i1> %0 +} + +define <1 x i1> @isnan_v1f_strictfp(<1 x float> %x) strictfp { +; CHECK-32-LABEL: isnan_v1f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_v1f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call <1 x i1> @llvm.is.fpclass.v1f32(<1 x float> %x, metadata !"nan") + ret <1 x i1> %0 +} + +define <2 x i1> @isnan_v2f(<2 x float> %x) { +; CHECK-32-LABEL: isnan_v2f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %cl +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dl +; CHECK-32-NEXT: movl %ecx, %eax +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_v2f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: cmpunordps %xmm0, %xmm0 +; CHECK-64-NEXT: shufps {{.*#+}} xmm0 = xmm0[0,1,1,3] +; CHECK-64-NEXT: retq +entry: + %0 = tail call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, metadata !"nan") + ret <2 x i1> %0 +} + + +define <2 x i1> @isnot_nan_v2f(<2 x float> %x) { +; CHECK-32-LABEL: isnot_nan_v2f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %cl +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %dl +; CHECK-32-NEXT: movl %ecx, %eax +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnot_nan_v2f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: cmpordps %xmm0, %xmm0 +; CHECK-64-NEXT: shufps {{.*#+}} xmm0 = xmm0[0,1,1,3] +; CHECK-64-NEXT: retq +entry: + %0 = tail call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, metadata !"zero|subnormal|normal|inf") + ret <2 x i1> %0 +} + +define <2 x i1> @isnan_v2f_strictfp(<2 x float> %x) strictfp { +; CHECK-32-LABEL: isnan_v2f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: andl %ecx, %eax +; CHECK-32-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: cmpl $2139095041, %ecx # imm = 0x7F800001 +; CHECK-32-NEXT: setge %dl +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_v2f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: shufps {{.*#+}} xmm0 = xmm0[0,1,1,3] +; CHECK-64-NEXT: andps {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-64-NEXT: pcmpgtd {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-64-NEXT: retq +entry: + %0 = tail call <2 x i1> @llvm.is.fpclass.v2f32(<2 x float> %x, metadata !"nan") + ret <2 x i1> %0 +} + +define <4 x i1> @isnan_v4f(<4 x float> %x) { +; CHECK-32-LABEL: isnan_v4f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dh +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dl +; CHECK-32-NEXT: addb %dl, %dl +; CHECK-32-NEXT: orb %dh, %dl +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dh +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %al +; CHECK-32-NEXT: addb %al, %al +; CHECK-32-NEXT: orb %dh, %al +; CHECK-32-NEXT: shlb $2, %al +; CHECK-32-NEXT: orb %dl, %al +; CHECK-32-NEXT: movb %al, (%ecx) +; CHECK-32-NEXT: movl %ecx, %eax +; CHECK-32-NEXT: retl $4 +; +; CHECK-64-LABEL: isnan_v4f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: cmpunordps %xmm0, %xmm0 +; CHECK-64-NEXT: retq +entry: + %0 = tail call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> %x, metadata !"nan") + ret <4 x i1> %0 +} + +define <4 x i1> @isnan_v4f_strictfp(<4 x float> %x) strictfp { +; CHECK-32-LABEL: isnan_v4f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: pushl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 8 +; CHECK-32-NEXT: .cfi_offset %esi, -8 +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: movl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: andl %ecx, %edx +; CHECK-32-NEXT: cmpl $2139095041, %edx # imm = 0x7F800001 +; CHECK-32-NEXT: setge %dh +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %esi +; CHECK-32-NEXT: andl %ecx, %esi +; CHECK-32-NEXT: cmpl $2139095041, %esi # imm = 0x7F800001 +; CHECK-32-NEXT: setge %dl +; CHECK-32-NEXT: addb %dl, %dl +; CHECK-32-NEXT: orb %dh, %dl +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %esi +; CHECK-32-NEXT: andl %ecx, %esi +; CHECK-32-NEXT: cmpl $2139095041, %esi # imm = 0x7F800001 +; CHECK-32-NEXT: setge %dh +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: cmpl $2139095041, %ecx # imm = 0x7F800001 +; CHECK-32-NEXT: setge %cl +; CHECK-32-NEXT: addb %cl, %cl +; CHECK-32-NEXT: orb %dh, %cl +; CHECK-32-NEXT: shlb $2, %cl +; CHECK-32-NEXT: orb %dl, %cl +; CHECK-32-NEXT: movb %cl, (%eax) +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl $4 +; +; CHECK-64-LABEL: isnan_v4f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: pand {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-64-NEXT: pcmpgtd {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-64-NEXT: retq +entry: + %0 = tail call <4 x i1> @llvm.is.fpclass.v4f32(<4 x float> %x, metadata !"nan") + ret <4 x i1> %0 +} + + +declare i1 @llvm.is.fpclass.f32(float, metadata) +declare i1 @llvm.is.fpclass.f64(double, metadata) +declare <1 x i1> @llvm.is.fpclass.v1f32(<1 x float>, metadata) +declare <2 x i1> @llvm.is.fpclass.v2f32(<2 x float>, metadata) +declare <4 x i1> @llvm.is.fpclass.v4f32(<4 x float>, metadata)