diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h b/llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h --- a/llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h +++ b/llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h @@ -107,6 +107,25 @@ COND_INVALID }; + /// Bits of x87 FPU Status Register. + enum FPSWFlag { + // Condition code. + C0 = 0x0100, + C1 = 0x0200, + C2 = 0x0400, + C3 = 0x4000, + + // Bits set by FXAM. + AllCondCodeBits = C0 | C1 | C2 | C3, + AllClassBits = C0 | C2 | C3, + ClassNaN = C0, + ClassInf = C0 | C2, + ClassNormal = C2, + ClassZero = C3, + ClassSubnormal = C2 | C3, + Negative = C1 + }; + // The classification for the first instruction in macro fusion. enum class FirstMacroFusionInstKind { // TEST diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h --- a/llvm/lib/Target/X86/X86ISelLowering.h +++ b/llvm/lib/Target/X86/X86ISelLowering.h @@ -1569,6 +1569,7 @@ SDValue lowerFaddFsub(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFP_EXTEND(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFP_ROUND(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerIS_FPCLASS(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -718,6 +718,7 @@ setOperationAction(ISD::LLROUND, MVT::f80, Expand); setOperationAction(ISD::LRINT, MVT::f80, Custom); setOperationAction(ISD::LLRINT, MVT::f80, Custom); + setOperationAction(ISD::IS_FPCLASS, MVT::f80, Custom); // Handle constrained floating-point operations of scalar. setOperationAction(ISD::STRICT_FADD , MVT::f80, Legal); @@ -22691,6 +22692,269 @@ return Res; } +SDValue X86TargetLowering::lowerIS_FPCLASS(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + MVT ResultVT = Op.getSimpleValueType(); + SDValue Arg = Op.getOperand(0); + MVT ArgVT = Arg.getSimpleValueType(); + auto CNode = cast(Op.getOperand(1)); + unsigned Check = CNode->getZExtValue(); + + if (Check == ISD::fcNan) { + // If exceptions are ignored, use unordered comparison. It treats + // unsupported values as NaNs, which is compatible with glibc. + if (ArgVT == MVT::f80 && Op->getFlags().hasNoFPExcept()) + return DAG.getSetCC(DL, ResultVT, Arg, Arg, ISD::CondCode::SETUNE); + } + + // Determine classification of the argument using instruction FXAM. + unsigned Opc; + switch (ArgVT.SimpleTy) { + default: + llvm_unreachable("Unexpected type!"); + case MVT::f32: + Opc = X86::XAM_Fp32; + break; + case MVT::f64: + Opc = X86::XAM_Fp64; + break; + case MVT::f80: + Opc = X86::XAM_Fp80; + break; + } + SDValue Test(DAG.getMachineNode(Opc, DL, MVT::Glue, Arg), 0); + + // Move FPSW to AX. + SDValue FPSW = + SDValue(DAG.getMachineNode(X86::FNSTSW16r, DL, MVT::i16, Test), 0); + + // Recognize the case when the FP class test is an inversion of a simpler + // test, like "inf|normal|subnormal|zero" == !"nan". + bool IsInverted = false; + if (unsigned InvertedCheck = checkInvertedFPClass(Check)) { + IsInverted = true; + Check = InvertedCheck; + } + + // Mask irrelevant bits in FPSW. + SDValue ClassCCWithoutSign = + DAG.getNode(ISD::AND, DL, MVT::i16, FPSW, + DAG.getConstant(X86::AllClassBits, DL, MVT::i16)); + FPSW = DAG.getNode(ISD::AND, DL, MVT::i16, FPSW, + DAG.getConstant(X86::AllCondCodeBits, DL, MVT::i16)); + + // Process the simple cases, when only one compare is required for + // classification. + SDValue CCToCompare; + switch (Check) { + default: + break; + case ISD::fcZero: + case ISD::fcSubnormal: + case ISD::fcNormal: + case ISD::fcInf: + case ISD::fcNan: + CCToCompare = ClassCCWithoutSign; + break; + case ISD::fcFinite: + // isfinite == !(isnan || isinf) + case ISD::fcInf | ISD::fcNan: + // isnan == (C3 == 0, C2 == 0, C0 == 1) + // isinf == (C3 == 0, C2 == 1, C0 == 1) + case ISD::fcZero | ISD::fcSubnormal: + // iszero == (C3 == 1, C2 == 0, C0 == 0) + // issubnormal == (C3 == 1, C2 == 1, C0 == 0) + CCToCompare = DAG.getNode(ISD::AND, DL, MVT::i16, FPSW, + DAG.getConstant(X86::C3 | X86::C0, DL, MVT::i16)); + break; + } + unsigned ExpectedCC = 0; + switch (Check) { + default: + break; + case ISD::fcFinite: + // isfinite == !(isnan || isinf) + IsInverted = !IsInverted; + LLVM_FALLTHROUGH; + case ISD::fcInf | ISD::fcNan: + // For this test the bit C2 is cleared, so check for NaN will check for Inf + // also. + ExpectedCC = X86::ClassNaN; + break; + case ISD::fcZero: + case ISD::fcZero | ISD::fcSubnormal: + ExpectedCC = X86::ClassZero; + break; + case ISD::fcSubnormal: + ExpectedCC = X86::ClassSubnormal; + break; + case ISD::fcNormal: + ExpectedCC = X86::ClassNormal; + break; + case ISD::fcInf: + ExpectedCC = X86::ClassInf; + break; + case ISD::fcNan: + // For compatibility with glibc treat unsupported formats as NaN. + return DAG.getSetCC(DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassNaN, DL, MVT::i16), + IsInverted ? ISD::SETGT : ISD::SETLE); + } + if (ExpectedCC) + return DAG.getSetCC(DL, ResultVT, CCToCompare, + DAG.getConstant(ExpectedCC, DL, MVT::i16), + IsInverted ? ISD::SETNE : ISD::SETEQ); + assert(!CCToCompare); + + // The general case is implemented as series of checks. + SDValue Res; + unsigned PartialCheck; + + PartialCheck = Check & ISD::fcNan; + if (PartialCheck) { + Res = + DAG.getSetCC(DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassNaN, DL, MVT::i16), ISD::SETEQ); + if (PartialCheck == ISD::fcSNan || PartialCheck == ISD::fcQNan) { + // FXAM does not provide information whether a NaN value is signaling, so + // to distinguish between signaling and quiet NaNs we need to analyze + // mantissa. + + // Store FP value to stack. + SDValue Chain = DAG.getEntryNode(); + MachineFunction &MF = DAG.getMachineFunction(); + Align StackAlign = Subtarget.getFrameLowering()->getStackAlign(); + unsigned SSFISize = + alignTo(Arg.getValueType().getStoreSize(), StackAlign); + int SSFI = + MF.getFrameInfo().CreateStackObject(SSFISize, StackAlign, false); + auto PtrVT = getPointerTy(MF.getDataLayout()); + SDValue StackSlot = DAG.getFrameIndex(SSFI, PtrVT); + MachineMemOperand *StoreMMO = MF.getMachineMemOperand( + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), SSFI), + MachineMemOperand::MOStore, SSFISize, StackAlign); + + Chain = DAG.getStore(Chain, DL, Arg, StackSlot, StoreMMO); + + // Load the high half of the mantissa. + SDValue OffsetSlot = + DAG.getMemBasePlusOffset(StackSlot, TypeSize::Fixed(4), DL); + SDValue AsInt = DAG.getLoad( + MVT::i32, DL, Chain, OffsetSlot, + MachinePointerInfo::getFixedStack(MF, SSFI).getWithOffset(4)); + + // Check the quiet NaN bit. + APInt QNaNBitMask(32, 0x40000000); + SDValue QNaNBitMaskV = DAG.getConstant(QNaNBitMask, DL, MVT::i32); + SDValue QNaNBitV = + DAG.getNode(ISD::AND, DL, MVT::i32, AsInt, QNaNBitMaskV); + ISD::CondCode CC = (PartialCheck == ISD::fcSNan) ? ISD::CondCode::SETEQ + : ISD::CondCode::SETNE; + SDValue QNaNBitTest = DAG.getSetCC(DL, ResultVT, QNaNBitV, + DAG.getConstant(0, DL, MVT::i32), CC); + Res = DAG.getNode(ISD::AND, DL, ResultVT, Res, QNaNBitTest); + } + } + + PartialCheck = Check & ISD::fcInf; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == ISD::fcPosInf) { + PartialRes = DAG.getSetCC(DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassInf, DL, MVT::i16), + ISD::SETEQ); + } else if (PartialCheck == ISD::fcNegInf) { + PartialRes = DAG.getSetCC( + DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassInf | X86::Negative, DL, MVT::i16), + ISD::SETEQ); + } else { + PartialRes = DAG.getSetCC(DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassInf, DL, MVT::i16), + ISD::SETEQ); + } + if (Res) + Res = DAG.getNode(ISD::OR, DL, ResultVT, Res, PartialRes); + else + Res = PartialRes; + } + + PartialCheck = Check & ISD::fcNormal; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == ISD::fcPosNormal) { + PartialRes = DAG.getSetCC(DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassNormal, DL, MVT::i16), + ISD::SETEQ); + } else if (PartialCheck == ISD::fcNegNormal) { + PartialRes = DAG.getSetCC( + DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassNormal | X86::Negative, DL, MVT::i16), + ISD::SETEQ); + } else { + PartialRes = DAG.getSetCC(DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassNormal, DL, MVT::i16), + ISD::SETEQ); + } + if (Res) + Res = DAG.getNode(ISD::OR, DL, ResultVT, Res, PartialRes); + else + Res = PartialRes; + } + + PartialCheck = Check & ISD::fcSubnormal; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == ISD::fcPosSubnormal) { + PartialRes = DAG.getSetCC( + DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassSubnormal, DL, MVT::i16), ISD::SETEQ); + } else if (PartialCheck == ISD::fcNegSubnormal) { + PartialRes = DAG.getSetCC( + DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassSubnormal | X86::Negative, DL, MVT::i16), + ISD::SETEQ); + } else { + PartialRes = DAG.getSetCC( + DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassSubnormal, DL, MVT::i16), ISD::SETEQ); + } + if (Res) + Res = DAG.getNode(ISD::OR, DL, ResultVT, Res, PartialRes); + else + Res = PartialRes; + } + + PartialCheck = Check & ISD::fcZero; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == ISD::fcPosZero) { + PartialRes = DAG.getSetCC(DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassZero, DL, MVT::i16), + ISD::SETEQ); + } else if (PartialCheck == ISD::fcNegZero) { + PartialRes = DAG.getSetCC( + DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassZero | X86::Negative, DL, MVT::i16), + ISD::SETEQ); + } else { + PartialRes = DAG.getSetCC(DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassZero, DL, MVT::i16), + ISD::SETEQ); + } + if (Res) + Res = DAG.getNode(ISD::OR, DL, ResultVT, Res, PartialRes); + else + Res = PartialRes; + } + + if (IsInverted) + Res = DAG.getSetCC(DL, ResultVT, Res, DAG.getConstant(0, DL, ResultVT), + ISD::SETEQ); + return Res; +} + /// Helper for creating a X86ISD::SETCC node. static SDValue getSETCC(X86::CondCode Cond, SDValue EFLAGS, const SDLoc &dl, SelectionDAG &DAG) { @@ -31235,6 +31499,7 @@ case ISD::FNEG: return LowerFABSorFNEG(Op, DAG); case ISD::FCOPYSIGN: return LowerFCOPYSIGN(Op, DAG); case ISD::FGETSIGN: return LowerFGETSIGN(Op, DAG); + case ISD::IS_FPCLASS: return lowerIS_FPCLASS(Op, DAG); case ISD::LRINT: case ISD::LLRINT: return LowerLRINT_LLRINT(Op, DAG); case ISD::SETCC: diff --git a/llvm/test/CodeGen/X86/x86-is_fpclass-fp80.ll b/llvm/test/CodeGen/X86/x86-is_fpclass-fp80.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/x86-is_fpclass-fp80.ll @@ -0,0 +1,497 @@ +; 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(x86_fp80 %x) { +; CHECK-32-LABEL: isnan_f: +; 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 %cl +; CHECK-32-NEXT: setne %al +; CHECK-32-NEXT: orb %cl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fucompi %st(0), %st +; CHECK-64-NEXT: setp %cl +; CHECK-64-NEXT: setne %al +; CHECK-64-NEXT: orb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"nan") + ret i1 %0 +} + +define i1 @isnot_nan_f(x86_fp80 %x) { +; CHECK-32-LABEL: isnot_nan_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $257, %eax # imm = 0x101 +; CHECK-32-NEXT: setge %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnot_nan_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $257, %eax # imm = 0x101 +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"zero|subnormal|normal|inf") + ret i1 %0 +} + +define i1 @issignaling_f(x86_fp80 %x) { +; CHECK-32-LABEL: issignaling_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: subl $28, %esp +; CHECK-32-NEXT: .cfi_def_cfa_offset 32 +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fld %st(0) +; CHECK-32-NEXT: fstpt (%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-32-NEXT: sete %cl +; CHECK-32-NEXT: testl $1073741824, {{[0-9]+}}(%esp) # imm = 0x40000000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: addl $28, %esp +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issignaling_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fld %st(0) +; CHECK-64-NEXT: fstpt -{{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-64-NEXT: sete %cl +; CHECK-64-NEXT: testl $1073741824, -{{[0-9]+}}(%rsp) # imm = 0x40000000 +; 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, metadata !"snan") + ret i1 %0 +} + +define i1 @isnot_signaling_f(x86_fp80 %x) { +; CHECK-32-LABEL: isnot_signaling_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: subl $28, %esp +; CHECK-32-NEXT: .cfi_def_cfa_offset 32 +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fld %st(0) +; CHECK-32-NEXT: fstpt (%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: testl $1073741824, {{[0-9]+}}(%esp) # imm = 0x40000000 +; CHECK-32-NEXT: sete %cl +; CHECK-32-NEXT: testb %cl, %al +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: addl $28, %esp +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnot_signaling_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fld %st(0) +; CHECK-64-NEXT: fstpt -{{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: testl $1073741824, -{{[0-9]+}}(%rsp) # imm = 0x40000000 +; CHECK-64-NEXT: sete %cl +; CHECK-64-NEXT: testb %cl, %al +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"zero|subnormal|normal|inf|qnan") + ret i1 %0 +} + +define i1 @isquiet_f(x86_fp80 %x) { +; CHECK-32-LABEL: isquiet_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: subl $28, %esp +; CHECK-32-NEXT: .cfi_def_cfa_offset 32 +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fld %st(0) +; CHECK-32-NEXT: fstpt (%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-32-NEXT: sete %cl +; CHECK-32-NEXT: testl $1073741824, {{[0-9]+}}(%esp) # imm = 0x40000000 +; CHECK-32-NEXT: setne %al +; CHECK-32-NEXT: andb %cl, %al +; CHECK-32-NEXT: addl $28, %esp +; CHECK-32-NEXT: .cfi_def_cfa_offset 4 +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isquiet_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fld %st(0) +; CHECK-64-NEXT: fstpt -{{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-64-NEXT: sete %cl +; CHECK-64-NEXT: testl $1073741824, -{{[0-9]+}}(%rsp) # imm = 0x40000000 +; CHECK-64-NEXT: setne %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"qnan") + ret i1 %0 +} + +define i1 @isinf_f(x86_fp80 %x) { +; CHECK-32-LABEL: isinf_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $1280, %eax # imm = 0x500 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isinf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $1280, %eax # imm = 0x500 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"inf") + ret i1 %0 +} + +define i1 @is_plus_inf_f(x86_fp80 %x) { +; CHECK-32-LABEL: is_plus_inf_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-32-NEXT: cmpl $1280, %eax # imm = 0x500 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_plus_inf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-64-NEXT: cmpl $1280, %eax # imm = 0x500 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"+inf") + ret i1 %0 +} + +define i1 @is_minus_inf_f(x86_fp80 %x) { +; CHECK-32-LABEL: is_minus_inf_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-32-NEXT: cmpl $1792, %eax # imm = 0x700 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_minus_inf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-64-NEXT: cmpl $1792, %eax # imm = 0x700 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"-inf") + ret i1 %0 +} + +define i1 @isfinite_f(x86_fp80 %x) { +; CHECK-32-LABEL: isfinite_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $16640, %eax # imm = 0x4100 +; CHECK-32-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-32-NEXT: setne %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isfinite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $16640, %eax # imm = 0x4100 +; CHECK-64-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-64-NEXT: setne %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"finite") + ret i1 %0 +} + +define i1 @is_plus_finite_f(x86_fp80 %x) { +; CHECK-32-LABEL: is_plus_finite_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-32-NEXT: cmpl $17408, %eax # imm = 0x4400 +; CHECK-32-NEXT: sete %cl +; CHECK-32-NEXT: cmpl $1024, %eax # imm = 0x400 +; CHECK-32-NEXT: sete %dl +; CHECK-32-NEXT: cmpl $16384, %eax # imm = 0x4000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: orb %cl, %al +; CHECK-32-NEXT: orb %dl, %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_plus_finite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-64-NEXT: cmpl $17408, %eax # imm = 0x4400 +; CHECK-64-NEXT: sete %cl +; CHECK-64-NEXT: cmpl $1024, %eax # imm = 0x400 +; CHECK-64-NEXT: sete %dl +; CHECK-64-NEXT: cmpl $16384, %eax # imm = 0x4000 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: orb %cl, %al +; CHECK-64-NEXT: orb %dl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"+finite") + ret i1 %0 +} + +define i1 @isnormal_f(x86_fp80 %x) { +; CHECK-32-LABEL: isnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $1024, %eax # imm = 0x400 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $1024, %eax # imm = 0x400 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"normal") + ret i1 %0 +} + +define i1 @issubnormal_f(x86_fp80 %x) { +; CHECK-32-LABEL: issubnormal_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $17408, %eax # imm = 0x4400 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: issubnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $17408, %eax # imm = 0x4400 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"subnormal") + ret i1 %0 +} + +define i1 @iszero_f(x86_fp80 %x) { +; CHECK-32-LABEL: iszero_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $16384, %eax # imm = 0x4000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: iszero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $16384, %eax # imm = 0x4000 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"zero") + ret i1 %0 +} + +define i1 @is_minus_zero_f(x86_fp80 %x) { +; CHECK-32-LABEL: is_minus_zero_f: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-32-NEXT: cmpl $16896, %eax # imm = 0x4200 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_minus_zero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $18176, %eax # imm = 0x4700 +; CHECK-64-NEXT: cmpl $16896, %eax # imm = 0x4200 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"-zero") + ret i1 %0 +} + + + +define i1 @isnan_f_strictfp(x86_fp80 %x) strictfp { +; CHECK-32-LABEL: isnan_f_strictfp: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fxam +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: fstp %st(0) +; CHECK-32-NEXT: wait +; CHECK-32-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-32-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-32-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-32-NEXT: setle %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: isnan_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: fldt {{[0-9]+}}(%rsp) +; CHECK-64-NEXT: fxam +; CHECK-64-NEXT: fnstsw %ax +; CHECK-64-NEXT: fstp %st(0) +; CHECK-64-NEXT: wait +; CHECK-64-NEXT: # kill: def $ax killed $ax def $eax +; CHECK-64-NEXT: andl $17664, %eax # imm = 0x4500 +; CHECK-64-NEXT: cmpl $256, %eax # imm = 0x100 +; CHECK-64-NEXT: setle %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, metadata !"nan") + ret i1 %0 +} + +declare i1 @llvm.is.fpclass.f80(x86_fp80, metadata)