diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -21943,6 +21943,64 @@ modes. +Floating Point Test Intrinsics +------------------------------ + +These functions get properties of floating point values. + + +'``llvm.is.fclass``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare i1 @llvm.is.fclass( , metadata ) + declare @llvm.is.fclass( , metadata ) + +Overview: +""""""""" + +The '``llvm.is.fclass``' intrinsic returns a boolean value or vector of boolean +values depending on whether the first argument satisfies the test specified by +the second argument. + +If the first argument is a floating-point scalar, then the result type is a +boolean (:ref:`i1 `). + +If the first argument is a floating-point vector, then the result type is a +vector of boolean with the same number of elements as the first argument. + +Arguments: +"""""""""" + +The first argument to the '``llvm.is.fclass``' intrinsic must be +:ref:`floating-point ` or :ref:`vector ` +of floating-point values. + +The second argument specifies the kind of test to perform. Possible tests are: +- "``zero``": yields ``true`` if the value is a zero. +- "``subnormal``": yields ``true`` if the value is subnormal. +- "``normal``": yields ``true`` if the value is normal (neither zero, subnormal, + infinite, nor NaN). +- "``finite``": yields ``true`` if the value is finite (not an infinity or NaN). +- "``inf``": yields ``true`` if the value is an infinity. +- "``nan``": yields ``true`` if the value is a NAN. +- "``signaling``": yields ``true`` if the value is a signaling NAN. +- "``quiet``": yields ``true`` if the value is a quietNAN. + +Semantics: +"""""""""" + +The function checks if ``op`` belongs to the floating point class specified by +``test``. If ``op`` is a vector, then the check is made element by element. Each +check yields an :ref:`i1 ` result, which is ``true``, if the element +value satisfies the specified test. The function never raises floating point +exceptions. + + General Intrinsics ------------------ diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -17,6 +17,8 @@ namespace llvm { +class Value; + /// ISD namespace - This namespace contains an enum which represents all of the /// SelectionDAG node types and value types. /// @@ -482,6 +484,12 @@ /// Returns platform specific canonical encoding of a floating point number. FCANONICALIZE, + /// Performs a check of floating point class property, defined by IEEE-754. + /// The first operand is the floating point value to check. The second operand + /// specifies the checked property and is a TargetConstant with value from + /// FClassCheck. Returns boolean value. + IS_FCLASS, + /// BUILD_VECTOR(ELT0, ELT1, ELT2, ELT3,...) - Return a fixed-width vector /// with the specified, possibly variable, elements. The types of the /// operands must match the vector element type, except that integer types @@ -1447,6 +1455,23 @@ /// SETCC_INVALID if it is not possible to represent the resultant comparison. CondCode getSetCCAndOperation(CondCode Op1, CondCode Op2, EVT Type); +/// Floating point class tests, supported in IS_FCLASS node. +enum FClassCheck { + FC_ZERO, + FC_SUBNORMAL, + FC_NORMAL, + FC_FINITE, + FC_INF, + FC_NAN, + FC_SIGNALING, + FC_QUIET, + FC_BAD +}; + +/// Convert floating point class test, which must be a metadata argument as +/// specified in invocations of `is_fclass`, into corresponding numeric code. +FClassCheck getFClassCheck(const Value *V); + } // namespace ISD } // namespace llvm diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -4445,6 +4445,11 @@ /// \returns The expansion result SDValue expandFP_TO_INT_SAT(SDNode *N, SelectionDAG &DAG) const; + /// Expand is_fclass depending on function attributes. + SDValue expandIS_FCLASS(EVT ResultVT, SDValue Op, ISD::FClassCheck Check, + SDNodeFlags Flags, const SDLoc &DL, + SelectionDAG &DAG) const; + /// Expand CTPOP nodes. Expands vector/scalar CTPOP nodes, /// vector nodes can only succeed if all operations are legal/custom. /// \param N Node to expand diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -717,6 +717,14 @@ def int_set_rounding : DefaultAttrsIntrinsic<[], [llvm_i32_ty]>; } +//===--------------- Floating Point Test Intrinsics -----------------------===// +// + +def int_is_fclass + : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [llvm_anyfloat_ty, llvm_metadata_ty], + [IntrNoMem, IntrWillReturn]>; + //===--------------- Constrained Floating Point Intrinsics ----------------===// // diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -1187,6 +1187,7 @@ case ISD::VECREDUCE_UMIN: case ISD::VECREDUCE_FMAX: case ISD::VECREDUCE_FMIN: + case ISD::IS_FCLASS: Action = TLI.getOperationAction( Node->getOpcode(), Node->getOperand(0).getValueType()); break; @@ -3139,6 +3140,15 @@ case ISD::FABS: Results.push_back(ExpandFABS(Node)); break; + case ISD::IS_FCLASS: { + auto CNode = cast(Node->getOperand(1)); + auto Check = static_cast(CNode->getZExtValue()); + if (SDValue Expanded = + TLI.expandIS_FCLASS(Node->getValueType(0), Node->getOperand(0), + Check, Node->getFlags(), SDLoc(Node), DAG)) + Results.push_back(Expanded); + break; + } case ISD::SMIN: case ISD::SMAX: case ISD::UMIN: diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -286,6 +286,10 @@ case ISD::VP_LSHR: Res = PromoteIntRes_SRL(N, /*IsVP*/ true); break; + + case ISD::IS_FCLASS: + Res = PromoteIntRes_IS_FCLASS(N); + break; } // If the result is null then the sub-method took care of registering it. @@ -1156,6 +1160,14 @@ return DAG.getSExtOrTrunc(SetCC, dl, NVT); } +SDValue DAGTypeLegalizer::PromoteIntRes_IS_FCLASS(SDNode *N) { + SDLoc DL(N); + SDValue Arg = N->getOperand(0); + SDValue Check = N->getOperand(1); + EVT NResVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); + return DAG.getNode(ISD::IS_FCLASS, DL, NResVT, Arg, Check); +} + SDValue DAGTypeLegalizer::PromoteIntRes_SHL(SDNode *N, bool IsVP) { SDValue LHS = GetPromotedInteger(N->getOperand(0)); SDValue RHS = N->getOperand(1); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h @@ -363,6 +363,7 @@ SDValue PromoteIntRes_ABS(SDNode *N); SDValue PromoteIntRes_Rotate(SDNode *N); SDValue PromoteIntRes_FunnelShift(SDNode *N); + SDValue PromoteIntRes_IS_FCLASS(SDNode *N); // Integer Operand Promotion. bool PromoteIntegerOperand(SDNode *N, unsigned OpNo); @@ -785,6 +786,7 @@ SDValue ScalarizeVecRes_UNDEF(SDNode *N); SDValue ScalarizeVecRes_VECTOR_SHUFFLE(SDNode *N); SDValue ScalarizeVecRes_FP_TO_XINT_SAT(SDNode *N); + SDValue ScalarizeVecRes_IS_FCLASS(SDNode *N); SDValue ScalarizeVecRes_FIX(SDNode *N); @@ -845,6 +847,7 @@ void SplitVecRes_INSERT_SUBVECTOR(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_FPOWI(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_FCOPYSIGN(SDNode *N, SDValue &Lo, SDValue &Hi); + void SplitVecRes_IS_FCLASS(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_INSERT_VECTOR_ELT(SDNode *N, SDValue &Lo, SDValue &Hi); void SplitVecRes_LOAD(LoadSDNode *LD, SDValue &Lo, SDValue &Hi); void SplitVecRes_MLOAD(MaskedLoadSDNode *MLD, SDValue &Lo, SDValue &Hi); @@ -931,6 +934,7 @@ SDValue WidenVecRes_Convert_StrictFP(SDNode *N); SDValue WidenVecRes_FP_TO_XINT_SAT(SDNode *N); SDValue WidenVecRes_FCOPYSIGN(SDNode *N); + SDValue WidenVecRes_IS_FCLASS(SDNode *N); SDValue WidenVecRes_POWI(SDNode *N); SDValue WidenVecRes_Unary(SDNode *N); SDValue WidenVecRes_InregOp(SDNode *N); @@ -954,6 +958,7 @@ SDValue WidenVecOp_Convert(SDNode *N); SDValue WidenVecOp_FP_TO_XINT_SAT(SDNode *N); SDValue WidenVecOp_FCOPYSIGN(SDNode *N); + SDValue WidenVecOp_IS_FCLASS(SDNode *N); SDValue WidenVecOp_VECREDUCE(SDNode *N); SDValue WidenVecOp_VECREDUCE_SEQ(SDNode *N); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp @@ -64,6 +64,7 @@ case ISD::SETCC: R = ScalarizeVecRes_SETCC(N); break; case ISD::UNDEF: R = ScalarizeVecRes_UNDEF(N); break; case ISD::VECTOR_SHUFFLE: R = ScalarizeVecRes_VECTOR_SHUFFLE(N); break; + case ISD::IS_FCLASS: R = ScalarizeVecRes_IS_FCLASS(N); break; case ISD::ANY_EXTEND_VECTOR_INREG: case ISD::SIGN_EXTEND_VECTOR_INREG: case ISD::ZERO_EXTEND_VECTOR_INREG: @@ -582,6 +583,29 @@ return DAG.getNode(ExtendCode, DL, NVT, Res); } +SDValue DAGTypeLegalizer::ScalarizeVecRes_IS_FCLASS(SDNode *N) { + SDLoc DL(N); + SDValue Arg = N->getOperand(0); + SDValue Check = N->getOperand(1); + EVT ArgVT = Arg.getValueType(); + EVT ResultVT = N->getValueType(0).getVectorElementType(); + + if (getTypeAction(ArgVT) == TargetLowering::TypeScalarizeVector) { + Arg = GetScalarizedVector(Arg); + } else { + EVT VT = ArgVT.getVectorElementType(); + Arg = DAG.getNode(ISD::EXTRACT_VECTOR_ELT, DL, VT, Arg, + DAG.getVectorIdxConstant(0, DL)); + } + + SDValue Res = + DAG.getNode(ISD::IS_FCLASS, DL, MVT::i1, {Arg, Check}, N->getFlags()); + // Vectors may have a different boolean contents to scalars. Promote the + // value appropriately. + ISD::NodeType ExtendCode = + TargetLowering::getExtendForContent(TLI.getBooleanContents(ArgVT)); + return DAG.getNode(ExtendCode, DL, ResultVT, Res); +} //===----------------------------------------------------------------------===// // Operand Vector Scalarization <1 x ty> -> ty. @@ -924,6 +948,7 @@ case ISD::INSERT_SUBVECTOR: SplitVecRes_INSERT_SUBVECTOR(N, Lo, Hi); break; case ISD::FPOWI: SplitVecRes_FPOWI(N, Lo, Hi); break; case ISD::FCOPYSIGN: SplitVecRes_FCOPYSIGN(N, Lo, Hi); break; + case ISD::IS_FCLASS: SplitVecRes_IS_FCLASS(N, Lo, Hi); break; case ISD::INSERT_VECTOR_ELT: SplitVecRes_INSERT_VECTOR_ELT(N, Lo, Hi); break; case ISD::SPLAT_VECTOR: case ISD::SCALAR_TO_VECTOR: @@ -1413,6 +1438,19 @@ Hi = DAG.getNode(ISD::FCOPYSIGN, DL, LHSHi.getValueType(), LHSHi, RHSHi); } +void DAGTypeLegalizer::SplitVecRes_IS_FCLASS(SDNode *N, SDValue &Lo, + SDValue &Hi) { + SDLoc DL(N); + SDValue ArgLo, ArgHi; + SDValue Check = N->getOperand(1); + GetSplitVector(N->getOperand(0), ArgLo, ArgHi); + EVT LoVT, HiVT; + std::tie(LoVT, HiVT) = DAG.GetSplitDestVTs(N->getValueType(0)); + + Lo = DAG.getNode(ISD::IS_FCLASS, DL, LoVT, ArgLo, Check, N->getFlags()); + Hi = DAG.getNode(ISD::IS_FCLASS, DL, HiVT, ArgHi, Check, N->getFlags()); +} + void DAGTypeLegalizer::SplitVecRes_InregOp(SDNode *N, SDValue &Lo, SDValue &Hi) { SDValue LHSLo, LHSHi; @@ -3132,6 +3170,10 @@ Res = WidenVecRes_FCOPYSIGN(N); break; + case ISD::IS_FCLASS: + Res = WidenVecRes_IS_FCLASS(N); + break; + case ISD::FPOWI: Res = WidenVecRes_POWI(N); break; @@ -3834,6 +3876,13 @@ return DAG.UnrollVectorOp(N, WidenVT.getVectorNumElements()); } +SDValue DAGTypeLegalizer::WidenVecRes_IS_FCLASS(SDNode *N) { + EVT WidenVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); + SDValue Arg = GetWidenedVector(N->getOperand(0)); + return DAG.getNode(N->getOpcode(), SDLoc(N), WidenVT, {Arg, N->getOperand(1)}, + N->getFlags()); +} + SDValue DAGTypeLegalizer::WidenVecRes_POWI(SDNode *N) { EVT WidenVT = TLI.getTypeToTransformTo(*DAG.getContext(), N->getValueType(0)); SDValue InOp = GetWidenedVector(N->getOperand(0)); @@ -4675,6 +4724,7 @@ case ISD::STRICT_FSETCCS: Res = WidenVecOp_STRICT_FSETCC(N); break; case ISD::VSELECT: Res = WidenVecOp_VSELECT(N); break; case ISD::FCOPYSIGN: Res = WidenVecOp_FCOPYSIGN(N); break; + case ISD::IS_FCLASS: Res = WidenVecOp_IS_FCLASS(N); break; case ISD::ANY_EXTEND: case ISD::SIGN_EXTEND: @@ -4810,6 +4860,34 @@ return DAG.UnrollVectorOp(N); } +SDValue DAGTypeLegalizer::WidenVecOp_IS_FCLASS(SDNode *N) { + SDLoc DL(N); + EVT ResultVT = N->getValueType(0); + SDValue Check = N->getOperand(1); + SDValue WideArg = GetWidenedVector(N->getOperand(0)); + + // Process this node similarly to SETCC. + EVT WideResultVT = getSetCCResultType(WideArg.getValueType()); + if (ResultVT.getScalarType() == MVT::i1) + WideResultVT = EVT::getVectorVT(*DAG.getContext(), MVT::i1, + WideResultVT.getVectorNumElements()); + + SDValue WideNode = DAG.getNode(ISD::IS_FCLASS, DL, WideResultVT, + {WideArg, Check}, N->getFlags()); + + // Extract the needed results from the result vector. + EVT ResVT = + EVT::getVectorVT(*DAG.getContext(), WideResultVT.getVectorElementType(), + ResultVT.getVectorNumElements()); + SDValue CC = DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ResVT, WideNode, + DAG.getVectorIdxConstant(0, DL)); + + EVT OpVT = N->getOperand(0).getValueType(); + ISD::NodeType ExtendCode = + TargetLowering::getExtendForContent(TLI.getBooleanContents(OpVT)); + return DAG.getNode(ExtendCode, DL, ResultVT, CC); +} + SDValue DAGTypeLegalizer::WidenVecOp_Convert(SDNode *N) { // Since the result is legal and the input is illegal. EVT VT = N->getValueType(0); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -570,6 +570,22 @@ return Result; } +ISD::FClassCheck ISD::getFClassCheck(const llvm::Value *V) { + Metadata *MD = cast(V)->getMetadata(); + if (!MD || !isa(MD)) + return ISD::FClassCheck::FC_BAD; + return StringSwitch(cast(MD)->getString()) + .Case("zero", ISD::FClassCheck::FC_ZERO) + .Case("subnormal", ISD::FClassCheck::FC_SUBNORMAL) + .Case("normal", ISD::FClassCheck::FC_NORMAL) + .Case("finite", ISD::FClassCheck::FC_FINITE) + .Case("inf", ISD::FClassCheck::FC_INF) + .Case("nan", ISD::FClassCheck::FC_NAN) + .Case("signaling", ISD::FClassCheck::FC_SIGNALING) + .Case("quiet", ISD::FClassCheck::FC_QUIET) + .Default(ISD::FClassCheck::FC_BAD); +} + //===----------------------------------------------------------------------===// // SDNode Profile Support //===----------------------------------------------------------------------===// diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6401,6 +6401,32 @@ DAG.setRoot(DAG.getNode(ISD::PCMARKER, sdl, MVT::Other, getRoot(), Tmp)); return; } + case Intrinsic::is_fclass: { + const DataLayout DLayout = DAG.getDataLayout(); + EVT DestVT = TLI.getValueType(DLayout, I.getType()); + EVT ArgVT = TLI.getValueType(DLayout, I.getArgOperand(0)->getType()); + ISD::FClassCheck FC = ISD::getFClassCheck(I.getArgOperand(1)); + assert(FC != ISD::FClassCheck::FC_BAD); + 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_FCLASS 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_FCLASS, ArgVT)) { + SDValue Result = TLI.expandIS_FCLASS(DestVT, Op, FC, Flags, sdl, DAG); + setValue(&I, Result); + return; + } + + SDValue Check = DAG.getTargetConstant(FC, sdl, MVT::i32); + SDValue V = DAG.getNode(ISD::IS_FCLASS, sdl, DestVT, {Op, Check}, Flags); + setValue(&I, V); + return; + } case Intrinsic::readcyclecounter: { SDValue Op = getRoot(); Res = DAG.getNode(ISD::READCYCLECOUNTER, sdl, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -267,6 +267,7 @@ case ISD::FCOPYSIGN: return "fcopysign"; case ISD::FGETSIGN: return "fgetsign"; case ISD::FCANONICALIZE: return "fcanonicalize"; + case ISD::IS_FCLASS: return "is_fclass"; case ISD::FPOW: return "fpow"; case ISD::STRICT_FPOW: return "strict_fpow"; case ISD::SMIN: return "smin"; diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -6980,6 +6980,93 @@ return SDValue(); } +SDValue TargetLowering::expandIS_FCLASS(EVT ResultVT, SDValue Op, + ISD::FClassCheck Check, + SDNodeFlags Flags, const SDLoc &DL, + SelectionDAG &DAG) const { + EVT OperandVT = Op.getValueType(); + assert(OperandVT.isFloatingPoint()); + + // Some checks can be implemented using float comparisons, if floating point + // exceptions are ignored. + if (Flags.hasNoFPExcept() && + isOperationLegalOrCustom(ISD::SETCC, OperandVT.getScalarType())) { + if (Check == ISD::FC_ZERO) + return DAG.getSetCC(DL, ResultVT, Op, + DAG.getConstantFP(0.0, DL, OperandVT), ISD::SETOEQ); + if (Check == ISD::FC_NAN) + return DAG.getSetCC(DL, ResultVT, Op, Op, ISD::SETUO); + } + + // In the general case use integer operations. They also allow to avoid traps + // if the argument is SNaN. + unsigned BitSize = OperandVT.getScalarSizeInBits(); + EVT IntVT = OperandVT.changeTypeToInteger(); + SDValue OpAsInt = DAG.getBitcast(IntVT, Op); + + // Clear sign bit + APInt AndMask = APInt::getSignedMaxValue(BitSize); + SDValue AndMaskV = DAG.getConstant(AndMask, DL, IntVT); + SDValue AbsV = DAG.getNode(ISD::AND, DL, IntVT, OpAsInt, AndMaskV); + + if (Check == ISD::FC_ZERO) + return DAG.getSetCC(DL, ResultVT, AbsV, DAG.getConstant(0, DL, IntVT), + ISD::SETEQ); + + EVT ScalarFloatVT = OperandVT.getScalarType(); + const Type *FloatTy = ScalarFloatVT.getTypeForEVT(*DAG.getContext()); + const llvm::fltSemantics &Semantics = FloatTy->getFltSemantics(); + + // isnan(V) ==> exp mask < abs(V) + // isinf(V) ==> exp mask == abs(V) + // isfinite(V) ==> exp mask > abs(V) + APInt ExpMask = APFloat::getInf(Semantics).bitcastToAPInt(); + SDValue ExpMaskV = DAG.getConstant(ExpMask, DL, IntVT); + if (Check == ISD::FC_NAN) + return DAG.getSetCC(DL, ResultVT, ExpMaskV, AbsV, ISD::SETLT); + if (Check == ISD::FC_INF) + return DAG.getSetCC(DL, ResultVT, ExpMaskV, AbsV, ISD::SETEQ); + if (Check == ISD::FC_FINITE) + return DAG.getSetCC(DL, ResultVT, ExpMaskV, AbsV, ISD::SETGT); + + APInt AllOneMantissa = APFloat::getLargest(Semantics).bitcastToAPInt(); + AllOneMantissa &= ~ExpMask; + if (Check == ISD::FC_SUBNORMAL) { + // issubnormal(V) ==> unsigned(abs(V) - 1) < (all mantissa bits set) + SDValue MantissaV = DAG.getConstant(AllOneMantissa, DL, IntVT); + SDValue MinusOneV = + DAG.getNode(ISD::SUB, DL, IntVT, AbsV, DAG.getConstant(1, DL, IntVT)); + return DAG.getSetCC(DL, ResultVT, MinusOneV, MantissaV, ISD::SETULT); + } + + if (Check == ISD::FC_NORMAL) { + // isnormal(V) == (0 < exp < max_exp) == (unsigned(exp - 1) < (max_exp - 1)) + APInt LSBExpBit = ExpMask & ~(ExpMask.shl(1)); + SDValue LSBExpBitV = DAG.getConstant(LSBExpBit, DL, IntVT); + SDValue DecExp = DAG.getNode(ISD::SUB, DL, IntVT, AbsV, LSBExpBitV); + APInt ExpLimit = ExpMask - LSBExpBit; + SDValue ExpLimitV = DAG.getConstant(ExpLimit, DL, IntVT); + return DAG.getSetCC(DL, ResultVT, DecExp, ExpLimitV, ISD::SETULT); + } + + APInt QNaNBitMask = AllOneMantissa & ~(AllOneMantissa.lshr(1)); + APInt ExpWithQNaNBit = ExpMask | QNaNBitMask; + SDValue ExpWithQNaNBitV = DAG.getConstant(ExpWithQNaNBit, DL, IntVT); + if (Check == ISD::FC_SIGNALING) { + // issignaling(V) ==> abs(V) xor quiet_bit > (exp_mask | quiet_bit) + SDValue QNaNBitMaskV = DAG.getConstant(QNaNBitMask, DL, IntVT); + SDValue FlippedQNaNV = DAG.getNode(ISD::XOR, DL, IntVT, AbsV, QNaNBitMaskV); + return DAG.getSetCC(DL, ResultVT, FlippedQNaNV, ExpWithQNaNBitV, + ISD::SETGT); + } + + // isquiet(V) ==> unsigned(V) >= exp_mask | quiet_bit + if (Check == ISD::FC_QUIET) + return DAG.getSetCC(DL, ResultVT, AbsV, ExpWithQNaNBitV, ISD::SETGE); + + return SDValue(); +} + bool TargetLowering::expandCTPOP(SDNode *Node, SDValue &Result, SelectionDAG &DAG) const { SDLoc dl(Node); diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -763,6 +763,7 @@ // These operations default to expand. setOperationAction(ISD::FGETSIGN, VT, Expand); + setOperationAction(ISD::IS_FCLASS, VT, Expand); setOperationAction(ISD::CONCAT_VECTORS, VT, Expand); setOperationAction(ISD::FMINNUM, VT, Expand); setOperationAction(ISD::FMAXNUM, VT, Expand); diff --git a/llvm/test/CodeGen/X86/x86-is_fclass.ll b/llvm/test/CodeGen/X86/x86-is_fclass.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/x86-is_fclass.ll @@ -0,0 +1,800 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=i686 | FileCheck %s -check-prefix=CHECK-32 +; RUN: llc < %s -mtriple=x86_64 | FileCheck %s -check-prefix=CHECK-64 + +define i1 @isnan_f(float %x) { +; CHECK-32-LABEL: isnan_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomiss %xmm0, %xmm0 +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"nan") + 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.fclass.f32(float %x, metadata !"inf") + ret i1 %0 +} + +define i1 @isfinite_f(float %x) { +; CHECK-32-LABEL: isfinite_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isfinite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"finite") + ret i1 %0 +} + +define i1 @isnormal_f(float %x) { +; CHECK-32-LABEL: isnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: addl $-8388608, %eax # imm = 0xFF800000 +; CHECK-32-NEXT: cmpl $2130706432, %eax # imm = 0x7F000000 +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: addl $-8388608, %eax # imm = 0xFF800000 +; CHECK-64-NEXT: cmpl $2130706432, %eax # imm = 0x7F000000 +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"normal") + ret i1 %0 +} + +define i1 @issubnormal_f(float %x) { +; CHECK-32-LABEL: issubnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: decl %eax +; CHECK-32-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issubnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: decl %eax +; CHECK-64-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"subnormal") + ret i1 %0 +} + +define i1 @iszero_f(float %x) { +; CHECK-32-LABEL: iszero_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fldz +; CHECK-32-NEXT: fucompp +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %cl +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorps %xmm1, %xmm1 +; CHECK-64-NEXT: cmpeqss %xmm0, %xmm1 +; CHECK-64-NEXT: movd %xmm1, %eax +; CHECK-64-NEXT: andl $1, %eax +; CHECK-64-NEXT: # kill: def $al killed $al killed $eax +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"zero") + 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: xorl $4194304, %eax # imm = 0x400000 +; CHECK-32-NEXT: cmpl $2143289345, %eax # imm = 0x7FC00001 +; CHECK-32-NEXT: setge %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: xorl $4194304, %eax # imm = 0x400000 +; CHECK-64-NEXT: cmpl $2143289345, %eax # imm = 0x7FC00001 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"signaling") + 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.fclass.f32(float %x, metadata !"quiet") + 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.fclass.f32(float %x, metadata !"nan") + ret i1 %0 +} + +define i1 @isinf_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: isinf_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: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isinf_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: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"inf") + 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.fclass.f32(float %x, metadata !"finite") + ret i1 %0 +} + +define i1 @isnormal_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: isnormal_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: 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_strictfp: +; 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.fclass.f32(float %x, metadata !"normal") + ret i1 %0 +} + +define i1 @issubnormal_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: issubnormal_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: decl %eax +; CHECK-32-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issubnormal_f_strictfp: +; 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.fclass.f32(float %x, metadata !"subnormal") + 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.fclass.f32(float %x, metadata !"zero") + ret i1 %0 +} + +define i1 @issignaling_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: issignaling_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: xorl $4194304, %eax # imm = 0x400000 +; CHECK-32-NEXT: cmpl $2143289345, %eax # imm = 0x7FC00001 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issignaling_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: xorl $4194304, %eax # imm = 0x400000 +; CHECK-64-NEXT: cmpl $2143289345, %eax # imm = 0x7FC00001 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"signaling") + ret i1 %0 +} + +define i1 @isquiet_f_strictfp(float %x) strictfp { +; CHECK-32-LABEL: isquiet_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 $2143289344, %eax # imm = 0x7FC00000 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isquiet_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 $2143289344, %eax # imm = 0x7FC00000 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f32(float %x, metadata !"quiet") + 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.fclass.f64(double %x, metadata !"nan") + ret i1 %0 +} + +define i1 @isinf_d(double %x) { +; CHECK-32-LABEL: isinf_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: xorl $2146435072, %eax # imm = 0x7FF00000 +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isinf_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9218868437227405312, %rax # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f64(double %x, metadata !"inf") + ret i1 %0 +} + +define i1 @isfinite_d(double %x) { +; CHECK-32-LABEL: isfinite_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2146435072, %eax # imm = 0x7FF00000 +; CHECK-32-NEXT: setl %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isfinite_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9218868437227405312, %rax # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f64(double %x, metadata !"finite") + ret i1 %0 +} + +define i1 @isnormal_d(double %x) { +; CHECK-32-LABEL: isnormal_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: addl $-1048576, %eax # imm = 0xFFF00000 +; CHECK-32-NEXT: shrl $21, %eax +; CHECK-32-NEXT: cmpl $1023, %eax # imm = 0x3FF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnormal_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $-4503599627370496, %rax # imm = 0xFFF0000000000000 +; CHECK-64-NEXT: addq %rcx, %rax +; CHECK-64-NEXT: shrq $53, %rax +; CHECK-64-NEXT: cmpl $1023, %eax # imm = 0x3FF +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f64(double %x, metadata !"normal") + ret i1 %0 +} + +define i1 @issubnormal_d(double %x) { +; CHECK-32-LABEL: issubnormal_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: movl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: addl $-1, %eax +; CHECK-32-NEXT: adcl $-1, %ecx +; CHECK-32-NEXT: cmpl $-1, %eax +; CHECK-32-NEXT: sbbl $1048575, %ecx # imm = 0xFFFFF +; CHECK-32-NEXT: setb %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issubnormal_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: decq %rcx +; CHECK-64-NEXT: movabsq $4503599627370495, %rax # imm = 0xFFFFFFFFFFFFF +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f64(double %x, metadata !"subnormal") + ret i1 %0 +} + +define i1 @iszero_d(double %x) { +; CHECK-32-LABEL: iszero_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldl {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fldz +; CHECK-32-NEXT: fucompp +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %cl +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorpd %xmm1, %xmm1 +; CHECK-64-NEXT: cmpeqsd %xmm0, %xmm1 +; CHECK-64-NEXT: movq %xmm1, %rax +; CHECK-64-NEXT: andl $1, %eax +; CHECK-64-NEXT: # kill: def $al killed $al killed $rax +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f64(double %x, metadata !"zero") + ret i1 %0 +} + +define i1 @issignaling_d(double %x) { +; CHECK-32-LABEL: issignaling_d: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: xorl $524288, %eax # imm = 0x80000 +; CHECK-32-NEXT: xorl %ecx, %ecx +; CHECK-32-NEXT: cmpl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: movl $2146959360, %ecx # imm = 0x7FF80000 +; CHECK-32-NEXT: sbbl %eax, %ecx +; CHECK-32-NEXT: setl %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 $2251799813685248, %rax # imm = 0x8000000000000 +; CHECK-64-NEXT: xorq %rcx, %rax +; CHECK-64-NEXT: movabsq $9221120237041090560, %rcx # imm = 0x7FF8000000000000 +; CHECK-64-NEXT: cmpq %rcx, %rax +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f64(double %x, metadata !"signaling") + 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.fclass.f64(double %x, metadata !"quiet") + 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.fclass.f64(double %x, metadata !"nan") + ret i1 %0 +} + +define i1 @iszero_d_strictfp(double %x) strictfp { +; CHECK-32-LABEL: iszero_d_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: orl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_d_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movq %xmm0, %rax +; CHECK-64-NEXT: shlq $1, %rax +; CHECK-64-NEXT: testq %rax, %rax +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fclass.f64(double %x, metadata !"zero") + ret i1 %0 +} + + + +define <1 x i1> @isnan_v1f(<1 x float> %x) { +; CHECK-32-LABEL: isnan_v1f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_v1f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomiss %xmm0, %xmm0 +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call <1 x i1> @llvm.is.fclass.v1f32(<1 x float> %x, metadata !"nan") + ret <1 x i1> %0 +} + +define <1 x i1> @isnan_v1f_strictfp(<1 x float> %x) strictfp { +; CHECK-32-LABEL: isnan_v1f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_v1f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call <1 x i1> @llvm.is.fclass.v1f32(<1 x float> %x, metadata !"nan") + ret <1 x i1> %0 +} + +define <2 x i1> @isnan_v2f(<2 x float> %x) { +; CHECK-32-LABEL: isnan_v2f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %cl +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dl +; CHECK-32-NEXT: movl %ecx, %eax +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_v2f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: cmpunordps %xmm0, %xmm0 +; CHECK-64-NEXT: shufps {{.*#+}} xmm0 = xmm0[0,1,1,3] +; CHECK-64-NEXT: retq +entry: + %0 = tail call <2 x i1> @llvm.is.fclass.v2f32(<2 x float> %x, metadata !"nan") + 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.fclass.v2f32(<2 x float> %x, metadata !"nan") + ret <2 x i1> %0 +} + +define <4 x i1> @isnan_v4f(<4 x float> %x) { +; CHECK-32-LABEL: isnan_v4f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dh +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dl +; CHECK-32-NEXT: addb %dl, %dl +; CHECK-32-NEXT: orb %dh, %dl +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %dh +; CHECK-32-NEXT: fucomp %st(0) +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setp %al +; CHECK-32-NEXT: addb %al, %al +; CHECK-32-NEXT: orb %dh, %al +; CHECK-32-NEXT: shlb $2, %al +; CHECK-32-NEXT: orb %dl, %al +; CHECK-32-NEXT: movb %al, (%ecx) +; CHECK-32-NEXT: movl %ecx, %eax +; CHECK-32-NEXT: retl $4 +; +; CHECK-64-LABEL: isnan_v4f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: cmpunordps %xmm0, %xmm0 +; CHECK-64-NEXT: retq +entry: + %0 = tail call <4 x i1> @llvm.is.fclass.v4f32(<4 x float> %x, metadata !"nan") + ret <4 x i1> %0 +} + +define <4 x i1> @isnan_v4f_strictfp(<4 x float> %x) strictfp { +; CHECK-32-LABEL: isnan_v4f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: pushl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 8 +; CHECK-32-NEXT: .cfi_offset %esi, -8 +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %eax +; CHECK-32-NEXT: movl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %edx +; CHECK-32-NEXT: andl %ecx, %edx +; CHECK-32-NEXT: cmpl $2139095041, %edx # imm = 0x7F800001 +; CHECK-32-NEXT: setge %dh +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %esi +; CHECK-32-NEXT: andl %ecx, %esi +; CHECK-32-NEXT: cmpl $2139095041, %esi # imm = 0x7F800001 +; CHECK-32-NEXT: setge %dl +; CHECK-32-NEXT: addb %dl, %dl +; CHECK-32-NEXT: orb %dh, %dl +; CHECK-32-NEXT: movl {{[0-9]+}}(%esp), %esi +; CHECK-32-NEXT: andl %ecx, %esi +; CHECK-32-NEXT: cmpl $2139095041, %esi # imm = 0x7F800001 +; CHECK-32-NEXT: setge %dh +; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %ecx +; CHECK-32-NEXT: cmpl $2139095041, %ecx # imm = 0x7F800001 +; CHECK-32-NEXT: setge %cl +; CHECK-32-NEXT: addb %cl, %cl +; CHECK-32-NEXT: orb %dh, %cl +; CHECK-32-NEXT: shlb $2, %cl +; CHECK-32-NEXT: orb %dl, %cl +; CHECK-32-NEXT: movb %cl, (%eax) +; CHECK-32-NEXT: popl %esi +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl $4 +; +; CHECK-64-LABEL: isnan_v4f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: pand {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-64-NEXT: pcmpgtd {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0 +; CHECK-64-NEXT: retq +entry: + %0 = tail call <4 x i1> @llvm.is.fclass.v4f32(<4 x float> %x, metadata !"nan") + ret <4 x i1> %0 +} + + +declare i1 @llvm.is.fclass.f32(float, metadata) +declare i1 @llvm.is.fclass.f64(double, metadata) +declare <1 x i1> @llvm.is.fclass.v1f32(<1 x float>, metadata) +declare <2 x i1> @llvm.is.fclass.v2f32(<2 x float>, metadata) +declare <4 x i1> @llvm.is.fclass.v4f32(<4 x float>, metadata)