diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -23244,6 +23244,83 @@ modes. +Floating-Point Test Intrinsics +------------------------------ + +These functions get properties of floating-point values. + + +'``llvm.is.fpclass``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare i1 @llvm.is.fpclass( , i32 ) + declare @llvm.is.fpclass( , i32 ) + +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, which tests to perform. It is an integer value, +each bit in which specifies floating-point class: + ++-------+----------------------+ +| Bit # | floating-point class | ++=======+======================+ +| 0 | Signaling NaN | ++-------+----------------------+ +| 1 | Quiet NaN | ++-------+----------------------+ +| 2 | Negative infinity | ++-------+----------------------+ +| 3 | Negative normal | ++-------+----------------------+ +| 4 | Negative subnormal | ++-------+----------------------+ +| 5 | Negative zero | ++-------+----------------------+ +| 6 | Positive zero | ++-------+----------------------+ +| 7 | Positive subnormal | ++-------+----------------------+ +| 8 | Positive normal | ++-------+----------------------+ +| 9 | Positive infinity | ++-------+----------------------+ + +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 argument ``test`` is a +bit mask where each bit specifies floating-point class to test. For example, the +value 0x108 makes test for normal value, - bits 3 and 8 in it are set, which +means that the function returns ``true`` if ``op`` is a positive or negative +normal value. The function never raises floating-point exceptions. + + General Intrinsics ------------------ diff --git a/llvm/include/llvm/ADT/FloatingPointMode.h b/llvm/include/llvm/ADT/FloatingPointMode.h --- a/llvm/include/llvm/ADT/FloatingPointMode.h +++ b/llvm/include/llvm/ADT/FloatingPointMode.h @@ -7,7 +7,8 @@ //===----------------------------------------------------------------------===// /// /// \file -/// Utilities for dealing with flags related to floating point mode controls. +/// Utilities for dealing with flags related to floating point properties and +/// mode controls. /// //===----------------------------------------------------------------------===/ @@ -193,4 +194,29 @@ } +/// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual +/// test may be an OR combination of basic tests. +enum FPClassTest { + 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 +}; + #endif // LLVM_ADT_FLOATINGPOINTMODE_H diff --git a/llvm/include/llvm/CodeGen/CodeGenCommonISel.h b/llvm/include/llvm/CodeGen/CodeGenCommonISel.h --- a/llvm/include/llvm/CodeGen/CodeGenCommonISel.h +++ b/llvm/include/llvm/CodeGen/CodeGenCommonISel.h @@ -212,6 +212,13 @@ MachineBasicBlock::iterator findSplitPointForStackProtector(MachineBasicBlock *BB, const TargetInstrInfo &TII); +/// Evaluates 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 Test The test as specified in 'is_fpclass' intrinsic invocation. +/// \returns The inverted test, or zero, if inversion does not produce simpler +/// test. +unsigned getInvertedFPClassTest(unsigned Test); } // namespace llvm 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 @@ -485,6 +485,13 @@ /// 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 which specifies + /// test in the same way as intrinsic 'is_fpclass'. + /// 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 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 @@ -4638,6 +4638,16 @@ /// \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 Test 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 Test, + SDNodeFlags Flags, const SDLoc &DL, + 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 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 @@ -729,6 +729,14 @@ def int_set_rounding : DefaultAttrsIntrinsic<[], [llvm_i32_ty]>; } +//===--------------- Floating Point Properties ----------------------------===// +// + +def int_is_fpclass + : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [llvm_anyfloat_ty, llvm_i32_ty], + [IntrNoMem, IntrWillReturn, ImmArg>]>; + //===--------------- Constrained Floating Point Intrinsics ----------------===// // diff --git a/llvm/lib/CodeGen/CodeGenCommonISel.cpp b/llvm/lib/CodeGen/CodeGenCommonISel.cpp --- a/llvm/lib/CodeGen/CodeGenCommonISel.cpp +++ b/llvm/lib/CodeGen/CodeGenCommonISel.cpp @@ -169,3 +169,31 @@ return SplitPoint; } + +unsigned llvm::getInvertedFPClassTest(unsigned Test) { + unsigned InvertedTest = ~Test & fcAllFlags; + switch (InvertedTest) { + default: + break; + case fcNan: + case fcSNan: + case fcQNan: + case fcInf: + case fcPosInf: + case fcNegInf: + case fcNormal: + case fcPosNormal: + case fcNegNormal: + case fcSubnormal: + case fcPosSubnormal: + case fcNegSubnormal: + case fcZero: + case fcPosZero: + case fcNegZero: + case fcFinite: + case fcPosFinite: + case fcNegFinite: + return InvertedTest; + } + return 0; +} 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 @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/ISDOpcodes.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" @@ -1193,6 +1194,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; @@ -3147,6 +3149,15 @@ case ISD::FABS: Results.push_back(ExpandFABS(Node)); break; + case ISD::IS_FPCLASS: { + auto CNode = cast(Node->getOperand(1)); + auto Test = static_cast(CNode->getZExtValue()); + if (SDValue Expanded = + TLI.expandIS_FPCLASS(Node->getValueType(0), Node->getOperand(0), + Test, 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 @@ -262,6 +262,10 @@ case ISD::FSHR: Res = PromoteIntRes_FunnelShift(N); 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. @@ -1167,6 +1171,14 @@ return DAG.getSExtOrTrunc(SetCC, dl, NVT); } +SDValue DAGTypeLegalizer::PromoteIntRes_IS_FPCLASS(SDNode *N) { + SDLoc DL(N); + SDValue Arg = N->getOperand(0); + SDValue Test = N->getOperand(1); + EVT NResVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); + return DAG.getNode(ISD::IS_FPCLASS, DL, NResVT, Arg, Test); +} + SDValue DAGTypeLegalizer::PromoteIntRes_SHL(SDNode *N) { 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 @@ -361,6 +361,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); @@ -783,6 +784,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); @@ -849,6 +851,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_VP_LOAD(VPLoadSDNode *LD, SDValue &Lo, SDValue &Hi); @@ -959,6 +962,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); @@ -984,6 +988,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); SDValue WidenVecOp_VP_REDUCE(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 @@ -66,6 +66,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: @@ -591,6 +592,29 @@ return DAG.getNode(ExtendCode, DL, NVT, Res); } +SDValue DAGTypeLegalizer::ScalarizeVecRes_IS_FPCLASS(SDNode *N) { + SDLoc DL(N); + SDValue Arg = N->getOperand(0); + SDValue Test = 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, Test}, 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. @@ -935,6 +959,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: @@ -1425,6 +1450,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 Test = 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, Test, N->getFlags()); + Hi = DAG.getNode(ISD::IS_FPCLASS, DL, HiVT, ArgHi, Test, N->getFlags()); +} + void DAGTypeLegalizer::SplitVecRes_InregOp(SDNode *N, SDValue &Lo, SDValue &Hi) { SDValue LHSLo, LHSHi; @@ -3654,6 +3692,10 @@ Res = WidenVecRes_FCOPYSIGN(N); break; + case ISD::IS_FPCLASS: + Res = WidenVecRes_IS_FPCLASS(N); + break; + case ISD::FPOWI: Res = WidenVecRes_POWI(N); break; @@ -4332,6 +4374,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)); @@ -5280,6 +5329,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: @@ -5432,6 +5482,34 @@ return DAG.UnrollVectorOp(N); } +SDValue DAGTypeLegalizer::WidenVecOp_IS_FPCLASS(SDNode *N) { + SDLoc DL(N); + EVT ResultVT = N->getValueType(0); + SDValue Test = 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, Test}, 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/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -31,6 +31,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" #include "llvm/CodeGen/GCMetadata.h" #include "llvm/CodeGen/MachineBasicBlock.h" @@ -6415,6 +6416,31 @@ setValue(&I, Res); DAG.setRoot(Res.getValue(0)); 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 Test = cast(I.getArgOperand(1))->getZExtValue(); + 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, Test, Flags, sdl, DAG); + setValue(&I, Result); + return; + } + + SDValue Check = DAG.getTargetConstant(Test, sdl, MVT::i32); + SDValue V = DAG.getNode(ISD::IS_FPCLASS, sdl, DestVT, {Op, Check}, Flags); + setValue(&I, V); + return; + } case Intrinsic::pcmarker: { SDValue Tmp = getValue(I.getArgOperand(0)); DAG.setRoot(DAG.getNode(ISD::PCMARKER, sdl, MVT::Other, getRoot(), Tmp)); 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 @@ -270,6 +270,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 @@ -13,6 +13,7 @@ #include "llvm/CodeGen/TargetLowering.h" #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" @@ -7427,6 +7428,218 @@ return SDValue(); } +SDValue TargetLowering::expandIS_FPCLASS(EVT ResultVT, SDValue Op, + unsigned Test, SDNodeFlags Flags, + const SDLoc &DL, + SelectionDAG &DAG) const { + EVT OperandVT = Op.getValueType(); + assert(OperandVT.isFloatingPoint()); + + // Degenerated cases. + if (Test == 0 || (Test & fcAllFlags) == fcAllFlags) + return DAG.getBoolConstant(true, DL, ResultVT, OperandVT); + + // Some checks may be represented as inversion of simpler check, for example + // "inf|normal|subnormal|zero" => !"nan". + bool IsInverted = false; + if (unsigned InvertedCheck = getInvertedFPClassTest(Test)) { + IsInverted = true; + Test = InvertedCheck; + } + + // Floating-point type properties. + EVT ScalarFloatVT = OperandVT.getScalarType(); + const Type *FloatTy = ScalarFloatVT.getTypeForEVT(*DAG.getContext()); + const llvm::fltSemantics &Semantics = FloatTy->getFltSemantics(); + bool IsF80 = (ScalarFloatVT == MVT::f80); + + // Some checks can be implemented using float comparisons, if floating point + // exceptions are ignored. + if (Flags.hasNoFPExcept() && + isOperationLegalOrCustom(ISD::SETCC, OperandVT.getScalarType())) { + if (Test == fcZero) + return DAG.getSetCC(DL, ResultVT, Op, + DAG.getConstantFP(0.0, DL, OperandVT), + IsInverted ? ISD::SETUNE : ISD::SETOEQ); + if (Test == fcNan) + return DAG.getSetCC(DL, ResultVT, Op, Op, + IsInverted ? ISD::SETO : ISD::SETUO); + } + + // In the general case use integer operations. + unsigned BitSize = OperandVT.getScalarSizeInBits(); + EVT IntVT = EVT::getIntegerVT(*DAG.getContext(), BitSize); + if (OperandVT.isVector()) + IntVT = EVT::getVectorVT(*DAG.getContext(), IntVT, + OperandVT.getVectorElementCount()); + SDValue OpAsInt = DAG.getBitcast(IntVT, Op); + + // Various masks. + APInt SignBit = APInt::getSignMask(BitSize); + APInt ValueMask = APInt::getSignedMaxValue(BitSize); // All bits but sign. + APInt Inf = APFloat::getInf(Semantics).bitcastToAPInt(); // Exp and int bit. + const unsigned ExplicitIntBitInF80 = 63; + APInt ExpMask = Inf; + if (IsF80) + ExpMask.clearBit(ExplicitIntBitInF80); + APInt AllOneMantissa = APFloat::getLargest(Semantics).bitcastToAPInt() & ~Inf; + APInt QNaNBitMask = + APInt::getOneBitSet(BitSize, AllOneMantissa.getActiveBits() - 1); + APInt InvertionMask = APInt::getAllOnesValue(ResultVT.getScalarSizeInBits()); + + SDValue ValueMaskV = DAG.getConstant(ValueMask, DL, IntVT); + SDValue SignBitV = DAG.getConstant(SignBit, DL, IntVT); + SDValue ExpMaskV = DAG.getConstant(ExpMask, DL, IntVT); + SDValue ZeroV = DAG.getConstant(0, DL, IntVT); + SDValue InfV = DAG.getConstant(Inf, DL, IntVT); + SDValue ResultInvertionMask = DAG.getConstant(InvertionMask, DL, ResultVT); + + SDValue Res; + const auto appendResult = [&](SDValue PartialRes) { + if (PartialRes) { + if (Res) + Res = DAG.getNode(ISD::OR, DL, ResultVT, Res, PartialRes); + else + Res = PartialRes; + } + }; + + SDValue IntBitIsSetV; // Explicit integer bit in f80 mantissa is set. + const auto getIntBitIsSet = [&]() -> SDValue { + if (!IntBitIsSetV) { + APInt IntBitMask(BitSize, 0); + IntBitMask.setBit(ExplicitIntBitInF80); + SDValue IntBitMaskV = DAG.getConstant(IntBitMask, DL, IntVT); + SDValue IntBitV = DAG.getNode(ISD::AND, DL, IntVT, OpAsInt, IntBitMaskV); + IntBitIsSetV = DAG.getSetCC(DL, ResultVT, IntBitV, ZeroV, ISD::SETNE); + } + return IntBitIsSetV; + }; + + // Split the value into sign bit and absolute value. + SDValue AbsV = DAG.getNode(ISD::AND, DL, IntVT, OpAsInt, ValueMaskV); + SDValue SignV = DAG.getSetCC(DL, ResultVT, OpAsInt, + DAG.getConstant(0.0, DL, IntVT), ISD::SETLT); + + // Tests that involve more than one class should be processed first. + SDValue PartialRes; + + if (IsF80) + ; // Detect finite numbers of f80 by checking individual classes because + // they have different settings of the explicit integer bit. + else if ((Test & fcFinite) == fcFinite) { + // finite(V) ==> abs(V) < exp_mask + PartialRes = DAG.getSetCC(DL, ResultVT, AbsV, ExpMaskV, ISD::SETLT); + Test &= ~fcFinite; + } else if ((Test & fcFinite) == fcPosFinite) { + // finite(V) && V > 0 ==> V < exp_mask + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, ExpMaskV, ISD::SETULT); + Test &= ~fcPosFinite; + } else if ((Test & fcFinite) == fcNegFinite) { + // finite(V) && V < 0 ==> abs(V) < exp_mask && signbit == 1 + PartialRes = DAG.getSetCC(DL, ResultVT, AbsV, ExpMaskV, ISD::SETLT); + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, SignV); + Test &= ~fcNegFinite; + } + appendResult(PartialRes); + + // Check for individual classes. + + if (unsigned PartialCheck = Test & fcZero) { + if (PartialCheck == fcPosZero) + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, ZeroV, ISD::SETEQ); + else if (PartialCheck == fcZero) + PartialRes = DAG.getSetCC(DL, ResultVT, AbsV, ZeroV, ISD::SETEQ); + else // ISD::fcNegZero + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, SignBitV, ISD::SETEQ); + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcInf) { + if (PartialCheck == fcPosInf) + PartialRes = DAG.getSetCC(DL, ResultVT, OpAsInt, InfV, ISD::SETEQ); + else if (PartialCheck == fcInf) + PartialRes = DAG.getSetCC(DL, ResultVT, AbsV, InfV, 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); + } + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcNan) { + APInt InfWithQnanBit = Inf | QNaNBitMask; + SDValue InfWithQnanBitV = DAG.getConstant(InfWithQnanBit, DL, IntVT); + if (PartialCheck == fcNan) { + // isnan(V) ==> abs(V) > int(inf) + PartialRes = DAG.getSetCC(DL, ResultVT, AbsV, InfV, ISD::SETGT); + if (IsF80) { + // Recognize unsupported values as NaNs for compatibility with glibc. + // In them (exp(V)==0) == int_bit. + SDValue ExpBits = DAG.getNode(ISD::AND, DL, IntVT, AbsV, ExpMaskV); + SDValue ExpIsZero = + DAG.getSetCC(DL, ResultVT, ExpBits, ZeroV, ISD::SETEQ); + SDValue IsPseudo = + DAG.getSetCC(DL, ResultVT, getIntBitIsSet(), ExpIsZero, ISD::SETEQ); + PartialRes = DAG.getNode(ISD::OR, DL, ResultVT, PartialRes, IsPseudo); + } + } else if (PartialCheck == fcQNan) { + // isquiet(V) ==> abs(V) >= (unsigned(Inf) | quiet_bit) + PartialRes = + DAG.getSetCC(DL, ResultVT, AbsV, InfWithQnanBitV, ISD::SETGE); + } else { // ISD::fcSNan + // issignaling(V) ==> abs(V) > unsigned(Inf) && + // abs(V) < (unsigned(Inf) | quiet_bit) + SDValue IsNan = DAG.getSetCC(DL, ResultVT, AbsV, InfV, ISD::SETGT); + SDValue IsNotQnan = + DAG.getSetCC(DL, ResultVT, AbsV, InfWithQnanBitV, ISD::SETLT); + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, IsNan, IsNotQnan); + } + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcSubnormal) { + // issubnormal(V) ==> unsigned(abs(V) - 1) < (all mantissa bits set) + // issubnormal(V) && V>0 ==> unsigned(V - 1) < (all mantissa bits set) + SDValue V = (PartialCheck == fcPosSubnormal) ? OpAsInt : AbsV; + SDValue MantissaV = DAG.getConstant(AllOneMantissa, DL, IntVT); + SDValue VMinusOneV = + DAG.getNode(ISD::SUB, DL, IntVT, V, DAG.getConstant(1, DL, IntVT)); + PartialRes = DAG.getSetCC(DL, ResultVT, VMinusOneV, MantissaV, ISD::SETULT); + if (PartialCheck == fcNegSubnormal) + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, SignV); + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcNormal) { + // 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 ExpMinus1 = DAG.getNode(ISD::SUB, DL, IntVT, AbsV, ExpLSBV); + APInt ExpLimit = ExpMask - ExpLSB; + SDValue ExpLimitV = DAG.getConstant(ExpLimit, DL, IntVT); + PartialRes = DAG.getSetCC(DL, ResultVT, ExpMinus1, ExpLimitV, ISD::SETULT); + if (PartialCheck == fcNegNormal) + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, SignV); + else if (PartialCheck == fcPosNormal) { + SDValue PosSignV = + DAG.getNode(ISD::XOR, DL, ResultVT, SignV, ResultInvertionMask); + PartialRes = DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, PosSignV); + } + if (IsF80) + PartialRes = + DAG.getNode(ISD::AND, DL, ResultVT, PartialRes, getIntBitIsSet()); + appendResult(PartialRes); + } + + if (!Res) + return DAG.getConstant(IsInverted, DL, ResultVT); + if (IsInverted) + Res = DAG.getNode(ISD::XOR, DL, ResultVT, Res, ResultInvertionMask); + return Res; +} + // 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 @@ -777,7 +777,8 @@ ISD::UMULFIX, ISD::UMULFIXSAT, ISD::SDIVFIX, ISD::SDIVFIXSAT, ISD::UDIVFIX, ISD::UDIVFIXSAT, - ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT}, + ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT, + ISD::IS_FPCLASS}, VT, Expand); // Overflow operations default to expand diff --git a/llvm/test/CodeGen/X86/is_fpclass-fp80.ll b/llvm/test/CodeGen/X86/is_fpclass-fp80.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/is_fpclass-fp80.ll @@ -0,0 +1,600 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=i686-linux | FileCheck %s -check-prefix=CHECK-32 +; RUN: llc < %s -mtriple=x86_64-linux | FileCheck %s -check-prefix=CHECK-64 + +define i1 @is_nan_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_nan_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[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: is_nan_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fucompi %st(0), %st +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 3) ; "nan" + ret i1 %0 +} + +define i1 @is_nan_f80_strict(x86_fp80 %x) strictfp { +; CHECK-32-LABEL: is_nan_f80_strict: +; 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: movzwl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: andl $32767, %ecx # imm = 0x7FFF +; CHECK-32-NEXT: xorl %edx, %edx +; CHECK-32-NEXT: cmpl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: movl $-2147483648, %esi # imm = 0x80000000 +; CHECK-32-NEXT: sbbl %eax, %esi +; CHECK-32-NEXT: movl $32767, %esi # imm = 0x7FFF +; CHECK-32-NEXT: sbbl %ecx, %esi +; CHECK-32-NEXT: sbbl %edx, %edx +; CHECK-32-NEXT: setl %dl +; CHECK-32-NEXT: testl %ecx, %ecx +; CHECK-32-NEXT: sete %cl +; CHECK-32-NEXT: shrl $31, %eax +; CHECK-32-NEXT: xorb %cl, %al +; CHECK-32-NEXT: xorb $1, %al +; CHECK-32-NEXT: orb %dl, %al +; CHECK-32-NEXT: # kill: def $al killed $al killed $eax +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_nan_f80_strict: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: movabsq $-9223372036854775808, %rdx # imm = 0x8000000000000000 +; CHECK-64-NEXT: cmpq %rcx, %rdx +; CHECK-64-NEXT: movl $32767, %edx # imm = 0x7FFF +; CHECK-64-NEXT: sbbq %rax, %rdx +; CHECK-64-NEXT: setl %dl +; CHECK-64-NEXT: shrq $63, %rcx +; CHECK-64-NEXT: testq %rax, %rax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: xorb %cl, %al +; CHECK-64-NEXT: xorb $1, %al +; CHECK-64-NEXT: orb %dl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 3) ; "nan" + ret i1 %0 +} + +define i1 @is_snan_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_snan_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: pushl %ebx +; CHECK-32-NEXT: .cfi_def_cfa_offset 8 +; CHECK-32-NEXT: pushl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 12 +; CHECK-32-NEXT: .cfi_offset %esi, -12 +; CHECK-32-NEXT: .cfi_offset %ebx, -8 +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-32-NEXT: xorl %ecx, %ecx +; CHECK-32-NEXT: cmpl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: movl $-2147483648, %esi # imm = 0x80000000 +; CHECK-32-NEXT: sbbl %edx, %esi +; CHECK-32-NEXT: movl $32767, %esi # imm = 0x7FFF +; CHECK-32-NEXT: sbbl %eax, %esi +; CHECK-32-NEXT: movl $0, %esi +; CHECK-32-NEXT: sbbl %esi, %esi +; CHECK-32-NEXT: setl %bl +; CHECK-32-NEXT: cmpl $-1073741824, %edx # imm = 0xC0000000 +; CHECK-32-NEXT: sbbl $32767, %eax # imm = 0x7FFF +; CHECK-32-NEXT: sbbl %ecx, %ecx +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: andb %bl, %al +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 8 +; CHECK-32-NEXT: popl %ebx +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_snan_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: movabsq $-4611686018427387904, %rdx # imm = 0xC000000000000000 +; CHECK-64-NEXT: cmpq %rdx, %rcx +; CHECK-64-NEXT: movq %rax, %rdx +; CHECK-64-NEXT: sbbq $32767, %rdx # imm = 0x7FFF +; CHECK-64-NEXT: setl %dl +; CHECK-64-NEXT: movabsq $-9223372036854775808, %rsi # imm = 0x8000000000000000 +; CHECK-64-NEXT: cmpq %rcx, %rsi +; CHECK-64-NEXT: movl $32767, %ecx # imm = 0x7FFF +; CHECK-64-NEXT: sbbq %rax, %rcx +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: andb %dl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 1) ; "snan" + ret i1 %0 +} + +define i1 @is_qnan_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_qnan_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-32-NEXT: xorl %ecx, %ecx +; CHECK-32-NEXT: movl $-1073741825, %edx # imm = 0xBFFFFFFF +; CHECK-32-NEXT: cmpl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: movl $32767, %edx # imm = 0x7FFF +; CHECK-32-NEXT: sbbl %eax, %edx +; CHECK-32-NEXT: sbbl %ecx, %ecx +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_qnan_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: movabsq $-4611686018427387905, %rcx # imm = 0xBFFFFFFFFFFFFFFF +; CHECK-64-NEXT: cmpq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: movl $32767, %ecx # imm = 0x7FFF +; CHECK-64-NEXT: sbbq %rax, %rcx +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 2) ; "qnan" + ret i1 %0 +} + +define i1 @is_zero_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_zero_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[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: is_zero_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fldz +; CHECK-64-NEXT: fucompi %st(1), %st +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: setnp %cl +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 96) ; 0x60 = "zero" + ret i1 %0 +} + +define i1 @is_zero_f80_strict(x86_fp80 %x) strictfp { +; CHECK-32-LABEL: is_zero_f80_strict: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_zero_f80_strict: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: orq {{[0-9]+}}(%rsp), %rax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 96) ; 0x60 = "zero" + ret i1 %0 +} + +define i1 @is_poszero_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_poszero_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_poszero_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: orq {{[0-9]+}}(%rsp), %rax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 64) ; 0x40 = "+zero" + ret i1 %0 +} + +define i1 @is_negzero_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_negzero_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: xorl $32768, %eax # imm = 0x8000 +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_negzero_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: xorq $32768, %rax # imm = 0x8000 +; CHECK-64-NEXT: orq {{[0-9]+}}(%rsp), %rax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 32) ; 0x20 = "-zero" + ret i1 %0 +} + +define i1 @is_inf_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_inf_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: notl %eax +; CHECK-32-NEXT: movl $-2147483648, %ecx # imm = 0x80000000 +; CHECK-32-NEXT: xorl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl %ecx, %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_inf_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: notl %eax +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: movabsq $-9223372036854775808, %rcx # imm = 0x8000000000000000 +; CHECK-64-NEXT: xorq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: orq %rax, %rcx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 516) ; 0x204 = "inf" + ret i1 %0 +} + +define i1 @is_posinf_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_posinf_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: movl $-2147483648, %ecx # imm = 0x80000000 +; CHECK-32-NEXT: xorl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: xorl $32767, %eax # imm = 0x7FFF +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl %ecx, %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_posinf_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: movabsq $-9223372036854775808, %rcx # imm = 0x8000000000000000 +; CHECK-64-NEXT: xorq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: xorq $32767, %rax # imm = 0x7FFF +; CHECK-64-NEXT: orq %rcx, %rax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 512) ; 0x200 = "+inf" + ret i1 %0 +} + +define i1 @is_neginf_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_neginf_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: xorl $65535, %eax # imm = 0xFFFF +; CHECK-32-NEXT: movl $-2147483648, %ecx # imm = 0x80000000 +; CHECK-32-NEXT: xorl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl %ecx, %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_neginf_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: notl %eax +; CHECK-64-NEXT: movzwl %ax, %eax +; CHECK-64-NEXT: movabsq $-9223372036854775808, %rcx # imm = 0x8000000000000000 +; CHECK-64-NEXT: xorq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: orq %rax, %rcx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 4) ; "-inf" + ret i1 %0 +} + +define i1 @is_normal_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_normal_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: andl $32767, %ecx # imm = 0x7FFF +; CHECK-32-NEXT: decl %ecx +; CHECK-32-NEXT: movzwl %cx, %ecx +; CHECK-32-NEXT: xorl %edx, %edx +; CHECK-32-NEXT: cmpl $32766, %ecx # imm = 0x7FFE +; CHECK-32-NEXT: sbbl %edx, %edx +; CHECK-32-NEXT: setb %cl +; CHECK-32-NEXT: shrl $31, %eax +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: # kill: def $al killed $al killed $eax +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_normal_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: shrq $63, %rcx +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: decl %eax +; CHECK-64-NEXT: movzwl %ax, %eax +; CHECK-64-NEXT: cmpl $32766, %eax # imm = 0x7FFE +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 264) ; 0x108 = "normal" + ret i1 %0 +} + +define i1 @is_posnormal_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_posnormal_f80: +; 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: movzwl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: movswl %dx, %ecx +; CHECK-32-NEXT: sarl $15, %ecx +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: andl $32767, %edx # imm = 0x7FFF +; CHECK-32-NEXT: decl %edx +; CHECK-32-NEXT: movzwl %dx, %edx +; CHECK-32-NEXT: xorl %esi, %esi +; CHECK-32-NEXT: cmpl $32766, %edx # imm = 0x7FFE +; CHECK-32-NEXT: sbbl %esi, %esi +; CHECK-32-NEXT: setb %dl +; CHECK-32-NEXT: testl %ecx, %ecx +; CHECK-32-NEXT: setns %cl +; CHECK-32-NEXT: shrl $31, %eax +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: andb %dl, %al +; CHECK-32-NEXT: # kill: def $al killed $al killed $eax +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_posnormal_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rax +; CHECK-64-NEXT: movswq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: testq %rcx, %rcx +; CHECK-64-NEXT: setns %dl +; CHECK-64-NEXT: andl $32767, %ecx # imm = 0x7FFF +; CHECK-64-NEXT: decl %ecx +; CHECK-64-NEXT: movzwl %cx, %ecx +; CHECK-64-NEXT: cmpl $32766, %ecx # imm = 0x7FFE +; CHECK-64-NEXT: setb %cl +; CHECK-64-NEXT: shrq $63, %rax +; CHECK-64-NEXT: andb %dl, %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: # kill: def $al killed $al killed $rax +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 256) ; 0x100 = "+normal" + ret i1 %0 +} + +define i1 @is_negnormal_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_negnormal_f80: +; 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: movzwl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: movswl %dx, %ecx +; CHECK-32-NEXT: sarl $15, %ecx +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: andl $32767, %edx # imm = 0x7FFF +; CHECK-32-NEXT: decl %edx +; CHECK-32-NEXT: movzwl %dx, %edx +; CHECK-32-NEXT: xorl %esi, %esi +; CHECK-32-NEXT: cmpl $32766, %edx # imm = 0x7FFE +; CHECK-32-NEXT: sbbl %esi, %esi +; CHECK-32-NEXT: setb %dl +; CHECK-32-NEXT: testl %ecx, %ecx +; CHECK-32-NEXT: sets %cl +; CHECK-32-NEXT: shrl $31, %eax +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: andb %dl, %al +; CHECK-32-NEXT: # kill: def $al killed $al killed $eax +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_negnormal_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rax +; CHECK-64-NEXT: movswq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: testq %rcx, %rcx +; CHECK-64-NEXT: sets %dl +; CHECK-64-NEXT: andl $32767, %ecx # imm = 0x7FFF +; CHECK-64-NEXT: decl %ecx +; CHECK-64-NEXT: movzwl %cx, %ecx +; CHECK-64-NEXT: cmpl $32766, %ecx # imm = 0x7FFE +; CHECK-64-NEXT: setb %cl +; CHECK-64-NEXT: shrq $63, %rax +; CHECK-64-NEXT: andb %dl, %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: # kill: def $al killed $al killed $rax +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 8) ; "-normal" + ret i1 %0 +} + +define i1 @is_subnormal_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_subnormal_f80: +; 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), %esi +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-32-NEXT: xorl %edx, %edx +; CHECK-32-NEXT: addl $-1, %esi +; CHECK-32-NEXT: adcl $-1, %ecx +; CHECK-32-NEXT: adcl $-1, %eax +; CHECK-32-NEXT: adcl $-1, %edx +; CHECK-32-NEXT: cmpl $-1, %esi +; CHECK-32-NEXT: sbbl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-32-NEXT: sbbl $0, %eax +; CHECK-32-NEXT: sbbl $0, %edx +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_subnormal_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: addq $-1, %rcx +; CHECK-64-NEXT: adcq $-1, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rdx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: cmpq %rdx, %rcx +; CHECK-64-NEXT: sbbq $0, %rax +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 144) ; 0x90 = "subnormal" + ret i1 %0 +} + +define i1 @is_possubnormal_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_possubnormal_f80: +; 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 {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: addl $-1, %ecx +; CHECK-32-NEXT: adcl $-1, %edx +; CHECK-32-NEXT: adcl $-1, %eax +; CHECK-32-NEXT: movzwl %ax, %eax +; CHECK-32-NEXT: xorl %esi, %esi +; CHECK-32-NEXT: cmpl $-1, %ecx +; CHECK-32-NEXT: sbbl $2147483647, %edx # imm = 0x7FFFFFFF +; CHECK-32-NEXT: sbbl $0, %eax +; CHECK-32-NEXT: sbbl %esi, %esi +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_possubnormal_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx +; CHECK-64-NEXT: addq $-1, %rcx +; CHECK-64-NEXT: adcq $-1, %rax +; CHECK-64-NEXT: movzwl %ax, %eax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rdx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: cmpq %rdx, %rcx +; CHECK-64-NEXT: sbbq $0, %rax +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 128) ; 0x80 = "+subnormal" + ret i1 %0 +} + +define i1 @is_negsubnormal_f80(x86_fp80 %x) { +; CHECK-32-LABEL: is_negsubnormal_f80: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: pushl %edi +; CHECK-32-NEXT: .cfi_def_cfa_offset 8 +; CHECK-32-NEXT: pushl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 12 +; CHECK-32-NEXT: .cfi_offset %esi, -12 +; CHECK-32-NEXT: .cfi_offset %edi, -8 +; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: movswl %cx, %eax +; CHECK-32-NEXT: sarl $15, %eax +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %esi +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %edi +; CHECK-32-NEXT: andl $32767, %ecx # imm = 0x7FFF +; CHECK-32-NEXT: xorl %edx, %edx +; CHECK-32-NEXT: addl $-1, %esi +; CHECK-32-NEXT: adcl $-1, %edi +; CHECK-32-NEXT: adcl $-1, %ecx +; CHECK-32-NEXT: adcl $-1, %edx +; CHECK-32-NEXT: cmpl $-1, %esi +; CHECK-32-NEXT: sbbl $2147483647, %edi # imm = 0x7FFFFFFF +; CHECK-32-NEXT: sbbl $0, %ecx +; CHECK-32-NEXT: sbbl $0, %edx +; CHECK-32-NEXT: setb %cl +; CHECK-32-NEXT: testl %eax, %eax +; CHECK-32-NEXT: sets %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 8 +; CHECK-32-NEXT: popl %edi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_negsubnormal_f80: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax +; CHECK-64-NEXT: movswq %ax, %rcx +; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rdx +; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF +; CHECK-64-NEXT: addq $-1, %rdx +; CHECK-64-NEXT: adcq $-1, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rsi # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: cmpq %rsi, %rdx +; CHECK-64-NEXT: sbbq $0, %rax +; CHECK-64-NEXT: setb %dl +; CHECK-64-NEXT: testq %rcx, %rcx +; CHECK-64-NEXT: sets %al +; CHECK-64-NEXT: andb %dl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 16) ; 0x10 = "-subnormal" + ret i1 %0 +} + +declare i1 @llvm.is.fpclass.f80(x86_fp80, i32) diff --git a/llvm/test/CodeGen/X86/is_fpclass.ll b/llvm/test/CodeGen/X86/is_fpclass.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/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-linux | FileCheck %s -check-prefix=CHECK-32 +; RUN: llc < %s -mtriple=x86_64-linux | 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, i32 3) ; "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, i32 1020) ; 0x3fc = "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, i32 1) ; "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, i32 2) ; "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, i32 516) ; 0x204 = "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, i32 512) ; 0x200 = "+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, i32 4) ; "-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, i32 504) ; 0x1f8 = "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, i32 448) ; 0x1c0 = "+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, i32 56) ; 0x38 = "-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, i32 264) ; 0x108 = "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, i32 256) ; 0x100 = "+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, i32 144) ; 0x90 = "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, i32 128) ; 0x80 = "+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, i32 16) ; 0x10 = "-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, i32 96) ; 0x60 = "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, i32 64) ; 0x40 = "+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, i32 32) ; 0x20 = "-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, i32 3) ; "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, i32 504) ; 0x1f8 = "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, i32 96) ; 0x60 = "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, i32 3) ; "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, i32 516) ; 0x204 = "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, i32 504) ; 0x1f8 = "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, i32 264) ; 0x108 = "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, i32 144) ; 0x90 = "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, i32 96) ; 0x60 = "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, i32 1) ; "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, i32 2) ; "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, i32 3) ; "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, i32 96) ; 0x60 = "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, i32 3) ; "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, i32 3) ; "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, i32 3) ; "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, i32 1020) ; 0x3fc = "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, i32 3) ; "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, i32 3) ; "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, i32 3) ; "nan" + ret <4 x i1> %0 +} + + +declare i1 @llvm.is.fpclass.f32(float, i32) +declare i1 @llvm.is.fpclass.f64(double, i32) +declare <1 x i1> @llvm.is.fpclass.v1f32(<1 x float>, i32) +declare <2 x i1> @llvm.is.fpclass.v2f32(<2 x float>, i32) +declare <4 x i1> @llvm.is.fpclass.v4f32(<4 x float>, i32)