[llvm] 170a903 - Intrinsic for checking floating point class

Serge Pavlov via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 25 23:20:11 PDT 2022


Author: Serge Pavlov
Date: 2022-04-26T13:09:16+07:00
New Revision: 170a903144905bcc8d009dadccb33238a4ae9b78

URL: https://github.com/llvm/llvm-project/commit/170a903144905bcc8d009dadccb33238a4ae9b78
DIFF: https://github.com/llvm/llvm-project/commit/170a903144905bcc8d009dadccb33238a4ae9b78.diff

LOG: Intrinsic for checking floating point class

This change introduces a new intrinsic, `llvm.is.fpclass`, which checks
if the provided floating-point number belongs to any of the the specified
value classes. The intrinsic implements the checks made by C standard
library functions `isnan`, `isinf`, `isfinite`, `isnormal`, `issubnormal`,
`issignaling` and corresponding IEEE-754 operations.

The primary motivation for this intrinsic is the support of strict FP
mode. In this mode using compare instructions or other FP operations is
not possible, because if the value is a signaling NaN, floating-point
exception `Invalid` is raised, but the aforementioned functions must
never raise exceptions.

Currently there are two solutions for this problem, both are
implemented partially. One of them is using integer operations to
implement the check. It was implemented in https://reviews.llvm.org/D95948
for `isnan`. It solves the problem of exceptions, but offers one
solution for all targets, although some can do the check in more
efficient way.

The other, implemented in https://reviews.llvm.org/D96568, introduced a
hook 'clang::TargetCodeGenInfo::testFPKind', which injects a target
specific code into IR to implement `isnan` and some other functions. It is
convenient for targets that have dedicated instruction to determine FP data
class. However using target-specific intrinsic complicates analysis and can
prevent some optimizations.

A special intrinsic for value class checks allows representing data class
tests with enough flexibility. During IR transformations it represents the
check in target-independent way and saves it from undesired transformations.
In the instruction selector it allows efficient lowering depending on the
used target and mode.

This implementation is an extended variant of `llvm.isnan` introduced
in https://reviews.llvm.org/D104854. It is limited to minimal intrinsic
support. Target-specific treatment will be implemented in separate
patches.

Differential Revision: https://reviews.llvm.org/D112025

Added: 
    llvm/test/CodeGen/X86/is_fpclass-fp80.ll
    llvm/test/CodeGen/X86/is_fpclass.ll

Modified: 
    llvm/docs/LangRef.rst
    llvm/include/llvm/ADT/FloatingPointMode.h
    llvm/include/llvm/CodeGen/CodeGenCommonISel.h
    llvm/include/llvm/CodeGen/ISDOpcodes.h
    llvm/include/llvm/CodeGen/TargetLowering.h
    llvm/include/llvm/IR/Intrinsics.td
    llvm/lib/CodeGen/CodeGenCommonISel.cpp
    llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
    llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
    llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
    llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
    llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
    llvm/lib/CodeGen/TargetLoweringBase.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 3b023dfa1c6e8..d880fbd321b6c 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -23244,6 +23244,83 @@ return any value and uses platform-independent representation of IEEE rounding
 modes.
 
 
+Floating-Point Test Intrinsics
+------------------------------
+
+These functions get properties of floating-point values.
+
+
+'``llvm.is.fpclass``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+      declare i1 @llvm.is.fpclass(<fptype> <op>, i32 <test>)
+      declare <N x i1> @llvm.is.fpclass(<vector-fptype> <op>, i32 <test>)
+
+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 <t_integer>`).
+
+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 <t_floating>` or :ref:`vector <t_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 <t_integer>` 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
index 9cc69b8a83442..59ccea1f9d441 100644
--- 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 @@ void DenormalMode::print(raw_ostream &OS) const {
 
 }
 
+/// 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
index dc4b9e29e14c7..ce278468dffca 100644
--- a/llvm/include/llvm/CodeGen/CodeGenCommonISel.h
+++ b/llvm/include/llvm/CodeGen/CodeGenCommonISel.h
@@ -212,6 +212,13 @@ class StackProtectorDescriptor {
 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
index beb99b2ff5cf2..ea1d3170acba2 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -485,6 +485,13 @@ enum NodeType {
   /// 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
index 4f09b9a6da57e..63dc943254a59 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -4638,6 +4638,16 @@ class TargetLowering : public TargetLoweringBase {
   /// \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
index 29ce056056776..72ff1444b28dc 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -729,6 +729,14 @@ let IntrProperties = [IntrInaccessibleMemOnly, IntrWillReturn] in {
   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<ArgIndex<1>>]>;
+
 //===--------------- Constrained Floating Point Intrinsics ----------------===//
 //
 

diff  --git a/llvm/lib/CodeGen/CodeGenCommonISel.cpp b/llvm/lib/CodeGen/CodeGenCommonISel.cpp
index 88b79d28175fd..8f185a161bd0b 100644
--- a/llvm/lib/CodeGen/CodeGenCommonISel.cpp
+++ b/llvm/lib/CodeGen/CodeGenCommonISel.cpp
@@ -169,3 +169,31 @@ llvm::findSplitPointForStackProtector(MachineBasicBlock *BB,
 
   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
index e1353c5ba464f..64f2c4c571d40 100644
--- 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 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
   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 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
   case ISD::FABS:
     Results.push_back(ExpandFABS(Node));
     break;
+  case ISD::IS_FPCLASS: {
+    auto CNode = cast<ConstantSDNode>(Node->getOperand(1));
+    auto Test = static_cast<FPClassTest>(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
index 52cb2fd2ff9df..a1ddb02563e3b 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
@@ -262,6 +262,10 @@ void DAGTypeLegalizer::PromoteIntegerResult(SDNode *N, unsigned ResNo) {
   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 @@ SDValue DAGTypeLegalizer::PromoteIntRes_SETCC(SDNode *N) {
   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
index 865b9e98a2d94..5642908961bf6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -361,6 +361,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   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 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   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 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   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 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   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 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
   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
index c0b546c12c7a8..a7e54817fb60d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -66,6 +66,7 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) {
   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 @@ SDValue DAGTypeLegalizer::ScalarizeVecRes_SETCC(SDNode *N) {
   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 
diff erent 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 @@ void DAGTypeLegalizer::SplitVectorResult(SDNode *N, unsigned ResNo) {
   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 @@ void DAGTypeLegalizer::SplitVecRes_FCOPYSIGN(SDNode *N, SDValue &Lo,
   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 @@ void DAGTypeLegalizer::WidenVectorResult(SDNode *N, unsigned ResNo) {
     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 @@ SDValue DAGTypeLegalizer::WidenVecRes_FCOPYSIGN(SDNode *N) {
   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 @@ bool DAGTypeLegalizer::WidenVectorOperand(SDNode *N, unsigned OpNo) {
   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 @@ SDValue DAGTypeLegalizer::WidenVecOp_FCOPYSIGN(SDNode *N) {
   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
index 49431af877703..d6b1915605c17 100644
--- 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 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
     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<ConstantInt>(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
index 874073bc456a1..60a3c02e38cf1 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -270,6 +270,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
   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
index 1182e60f37a6f..6fddd1c963f0b 100644
--- 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 @@ SDValue TargetLowering::expandFMINNUM_FMAXNUM(SDNode *Node,
   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 
diff erent 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
index ffefce59b46f1..323c41c7b23b2 100644
--- a/llvm/lib/CodeGen/TargetLoweringBase.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp
@@ -777,7 +777,8 @@ void TargetLoweringBase::initActions() {
                         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
index 0000000000000..17569234abd0f
--- /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
index 0000000000000..3929141f69221
--- /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)


        


More information about the llvm-commits mailing list