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 @@ -1598,6 +1598,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 @@ -31,6 +31,7 @@ #include "llvm/Analysis/ObjCARCUtil.h" #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/VectorUtils.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/IntrinsicLowering.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -729,6 +730,9 @@ // FIXME: When the target is 64-bit, STRICT_FP_ROUND will be overwritten // as Custom. setOperationAction(ISD::STRICT_FP_ROUND, MVT::f80, Legal); + + if (!Subtarget.useSoftFloat()) + setOperationAction(ISD::IS_FPCLASS, MVT::f80, Custom); } // f128 uses xmm registers, but most operations require libcalls. @@ -22870,6 +22874,248 @@ 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(); + assert(ArgVT == MVT::f80); + auto CNode = cast(Op.getOperand(1)); + unsigned Check = CNode->getZExtValue(); + + // Degenerated cases. + if (Check == 0 || (Check & fcAllFlags) == fcAllFlags) + return DAG.getBoolConstant(true, DL, ResultVT, ArgVT); + + // Some checks can be implemented using float comparisons, if floating point + // exceptions are ignored. + if (Op->getFlags().hasNoFPExcept()) { + if (Check == fcZero) + return DAG.getSetCC(DL, ResultVT, Arg, DAG.getConstantFP(0.0, DL, ArgVT), + ISD::SETOEQ); + // Unordered comparison treats unsupported values as NaNs, which is + // compatible with glibc. + if (Check == fcNan) + return DAG.getSetCC(DL, ResultVT, Arg, Arg, ISD::SETUO); + } + + // Determine classification of the argument using instruction FXAM. + SDValue Test(DAG.getMachineNode(X86::XAM_Fp80, DL, MVT::Glue, Arg), 0); + + // Move FPSW to AX. + SDValue FPSW = + SDValue(DAG.getMachineNode(X86::FNSTSW16r, DL, MVT::i16, Test), 0); + + // 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 fcZero: + case fcSubnormal: + case fcNormal: + case fcInf: + case fcNan: + CCToCompare = ClassCCWithoutSign; + break; + // Some practically important tests can be implemented by checking part of + // condition code bits. + case fcInf | fcNan: // isnan == (C3==0, C2==0, C0==1) + // isinf == (C3==0, C2==1, C0==1) + case fcZero | fcSubnormal: // iszero == (C3==1, C2==0, C0==0) + // issubnormal == (C3==1, C2==1, C0==0) + // C2 can be ignored for these checks. + 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 fcInf | fcNan: + // For this test the bit C2 is cleared, so check for NaN will check for Inf + // also. + ExpectedCC = X86::ClassNaN; + break; + case fcZero: + case fcZero | fcSubnormal: + ExpectedCC = X86::ClassZero; + break; + case fcSubnormal: + ExpectedCC = X86::ClassSubnormal; + break; + case fcNormal: + ExpectedCC = X86::ClassNormal; + break; + case fcInf: + ExpectedCC = X86::ClassInf; + break; + case fcNan: + // For compatibility with glibc treat unsupported formats as NaN. + return DAG.getSetCC(DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassNaN, DL, MVT::i16), + ISD::SETLE); + } + if (ExpectedCC) + return DAG.getSetCC(DL, ResultVT, CCToCompare, + DAG.getConstant(ExpectedCC, DL, MVT::i16), ISD::SETEQ); + + // The general case is implemented as series of checks. + SDValue Res; + unsigned PartialCheck; + + PartialCheck = Check & fcNan; + if (PartialCheck) { + Res = + DAG.getSetCC(DL, ResultVT, ClassCCWithoutSign, + DAG.getConstant(X86::ClassNaN, DL, MVT::i16), ISD::SETEQ); + if (PartialCheck == fcSNan || PartialCheck == 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(ArgVT.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 = StackSlot; + if (ArgVT != MVT::f32) + 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 == 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 & fcInf; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == fcPosInf) { + PartialRes = DAG.getSetCC(DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassInf, DL, MVT::i16), + ISD::SETEQ); + } else if (PartialCheck == 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 & fcNormal; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == fcPosNormal) { + PartialRes = DAG.getSetCC(DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassNormal, DL, MVT::i16), + ISD::SETEQ); + } else if (PartialCheck == 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 & fcSubnormal; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == fcPosSubnormal) { + PartialRes = DAG.getSetCC( + DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassSubnormal, DL, MVT::i16), ISD::SETEQ); + } else if (PartialCheck == 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 & fcZero; + if (PartialCheck) { + SDValue PartialRes; + if (PartialCheck == fcPosZero) { + PartialRes = DAG.getSetCC(DL, ResultVT, FPSW, + DAG.getConstant(X86::ClassZero, DL, MVT::i16), + ISD::SETEQ); + } else if (PartialCheck == 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; + } + + return Res; +} + /// Helper for creating a X86ISD::SETCC node. static SDValue getSETCC(X86::CondCode Cond, SDValue EFLAGS, const SDLoc &dl, SelectionDAG &DAG) { @@ -31761,6 +32007,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/is_fpclass-fp80.ll b/llvm/test/CodeGen/X86/is_fpclass-fp80.ll --- a/llvm/test/CodeGen/X86/is_fpclass-fp80.ll +++ b/llvm/test/CodeGen/X86/is_fpclass-fp80.ll @@ -2,7 +2,7 @@ ; 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) { +define i1 @is_nan_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_nan_f80: ; CHECK-32: # %bb.0: # %entry ; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) @@ -24,12 +24,10 @@ ret i1 %0 } -define i1 @is_nan_f80_strict(x86_fp80 %x) strictfp { -; CHECK-32-LABEL: is_nan_f80_strict: +define i1 @is_nan_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_nan_f80_soft: ; 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 @@ -49,10 +47,9 @@ ; 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-LABEL: is_nan_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx @@ -74,15 +71,83 @@ ret i1 %0 } -define i1 @is_snan_f80(x86_fp80 %x) { +define i1 @is_nan_f80_strict(x86_fp80 %x) #2 { +; CHECK-32-LABEL: is_nan_f80_strict: +; 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: is_nan_f80_strict: +; 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, i32 3) ; "nan" + ret i1 %0 +} + +define i1 @is_snan_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_snan_f80: ; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: subl $28, %esp +; 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: retl +; +; CHECK-64-LABEL: is_snan_f80: +; 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, i32 1) ; "snan" + ret i1 %0 +} + +define i1 @is_snan_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_snan_f80_soft: +; 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 @@ -101,12 +166,10 @@ ; 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-LABEL: is_snan_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx @@ -128,36 +191,91 @@ ret i1 %0 } -define i1 @is_qnan_f80(x86_fp80 %x) { +define i1 @is_qnan_f80(x86_fp80 %x) #0 { ; 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: subl $28, %esp +; 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: 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: 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, i32 2) ; "qnan" + ret i1 %0 +} + +define i1 @is_qnan_f80_soft(x86_fp80 %x) #2 { +; CHECK-32-LABEL: is_qnan_f80_soft: +; CHECK-32: # %bb.0: # %entry +; CHECK-32-NEXT: subl $28, %esp +; 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: 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: 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: retl +; +; CHECK-64-LABEL: is_qnan_f80_soft: +; 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: 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: 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, i32 2) ; "qnan" ret i1 %0 } -define i1 @is_zero_f80(x86_fp80 %x) { +define i1 @is_zero_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_zero_f80: ; CHECK-32: # %bb.0: # %entry ; CHECK-32-NEXT: fldt {{[0-9]+}}(%esp) @@ -186,8 +304,8 @@ ret i1 %0 } -define i1 @is_zero_f80_strict(x86_fp80 %x) strictfp { -; CHECK-32-LABEL: is_zero_f80_strict: +define i1 @is_zero_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_zero_f80_soft: ; CHECK-32: # %bb.0: # %entry ; CHECK-32-NEXT: movzwl {{[0-9]+}}(%esp), %eax ; CHECK-32-NEXT: andl $32767, %eax # imm = 0x7FFF @@ -196,7 +314,7 @@ ; CHECK-32-NEXT: sete %al ; CHECK-32-NEXT: retl ; -; CHECK-64-LABEL: is_zero_f80_strict: +; CHECK-64-LABEL: is_zero_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: andl $32767, %eax # imm = 0x7FFF @@ -208,16 +326,76 @@ ret i1 %0 } -define i1 @is_poszero_f80(x86_fp80 %x) { +define i1 @is_zero_f80_strict(x86_fp80 %x) #2 { +; CHECK-32-LABEL: is_zero_f80_strict: +; 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 $16384, %eax # imm = 0x4000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_zero_f80_strict: +; 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 $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, i32 96) ; 0x60 = "zero" + ret i1 %0 +} + +define i1 @is_poszero_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_poszero_f80: ; 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 $16384, %eax # imm = 0x4000 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_poszero_f80: +; 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 $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, i32 64) ; 0x40 = "+zero" + ret i1 %0 +} + +define i1 @is_poszero_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_poszero_f80_soft: +; 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-LABEL: is_poszero_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: orq {{[0-9]+}}(%rsp), %rax @@ -228,9 +406,38 @@ ret i1 %0 } -define i1 @is_negzero_f80(x86_fp80 %x) { +define i1 @is_negzero_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_negzero_f80: ; 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_negzero_f80: +; 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, i32 32) ; 0x20 = "-zero" + ret i1 %0 +} + +define i1 @is_negzero_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_negzero_f80_soft: +; 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 @@ -238,7 +445,7 @@ ; CHECK-32-NEXT: sete %al ; CHECK-32-NEXT: retl ; -; CHECK-64-LABEL: is_negzero_f80: +; CHECK-64-LABEL: is_negzero_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: xorq $32768, %rax # imm = 0x8000 @@ -250,9 +457,38 @@ ret i1 %0 } -define i1 @is_inf_f80(x86_fp80 %x) { +define i1 @is_inf_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_inf_f80: ; 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: is_inf_f80: +; 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, i32 516) ; 0x204 = "inf" + ret i1 %0 +} + +define i1 @is_inf_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_inf_f80_soft: +; 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 @@ -263,7 +499,7 @@ ; CHECK-32-NEXT: sete %al ; CHECK-32-NEXT: retl ; -; CHECK-64-LABEL: is_inf_f80: +; CHECK-64-LABEL: is_inf_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: notl %eax @@ -278,9 +514,38 @@ ret i1 %0 } -define i1 @is_posinf_f80(x86_fp80 %x) { +define i1 @is_posinf_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_posinf_f80: ; 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_posinf_f80: +; 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, i32 512) ; 0x200 = "+inf" + ret i1 %0 +} + +define i1 @is_posinf_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_posinf_f80_soft: +; 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 @@ -290,7 +555,7 @@ ; CHECK-32-NEXT: sete %al ; CHECK-32-NEXT: retl ; -; CHECK-64-LABEL: is_posinf_f80: +; CHECK-64-LABEL: is_posinf_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: movabsq $-9223372036854775808, %rcx # imm = 0x8000000000000000 @@ -304,9 +569,38 @@ ret i1 %0 } -define i1 @is_neginf_f80(x86_fp80 %x) { +define i1 @is_neginf_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_neginf_f80: ; 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_neginf_f80: +; 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, i32 4) ; "-inf" + ret i1 %0 +} + +define i1 @is_neginf_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_neginf_f80_soft: +; 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 @@ -316,7 +610,7 @@ ; CHECK-32-NEXT: sete %al ; CHECK-32-NEXT: retl ; -; CHECK-64-LABEL: is_neginf_f80: +; CHECK-64-LABEL: is_neginf_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: notl %eax @@ -331,9 +625,38 @@ ret i1 %0 } -define i1 @is_normal_f80(x86_fp80 %x) { +define i1 @is_normal_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_normal_f80: ; 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: is_normal_f80: +; 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, i32 264) ; 0x108 = "normal" + ret i1 %0 +} + +define i1 @is_normal_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_normal_f80_soft: +; 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 @@ -348,7 +671,7 @@ ; CHECK-32-NEXT: # kill: def $al killed $al killed $eax ; CHECK-32-NEXT: retl ; -; CHECK-64-LABEL: is_normal_f80: +; CHECK-64-LABEL: is_normal_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx @@ -365,12 +688,39 @@ ret i1 %0 } -define i1 @is_posnormal_f80(x86_fp80 %x) { +define i1 @is_posnormal_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_posnormal_f80: ; 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 $1024, %eax # imm = 0x400 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_posnormal_f80: +; 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 $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, i32 256) ; 0x100 = "+normal" + ret i1 %0 +} + +define i1 @is_posnormal_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_posnormal_f80_soft: +; 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 @@ -389,10 +739,9 @@ ; 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-LABEL: is_posnormal_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rax ; CHECK-64-NEXT: movswq {{[0-9]+}}(%rsp), %rcx @@ -413,12 +762,39 @@ ret i1 %0 } -define i1 @is_negnormal_f80(x86_fp80 %x) { +define i1 @is_negnormal_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_negnormal_f80: ; 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 $1536, %eax # imm = 0x600 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_negnormal_f80: +; 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 $1536, %eax # imm = 0x600 +; CHECK-64-NEXT: sete %al +; 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_negnormal_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_negnormal_f80_soft: +; 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 @@ -437,10 +813,9 @@ ; 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-LABEL: is_negnormal_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rax ; CHECK-64-NEXT: movswq {{[0-9]+}}(%rsp), %rcx @@ -461,12 +836,39 @@ ret i1 %0 } -define i1 @is_subnormal_f80(x86_fp80 %x) { +define i1 @is_subnormal_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_subnormal_f80: ; 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: is_subnormal_f80: +; 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, i32 144) ; 0x90 = "subnormal" + ret i1 %0 +} + +define i1 @is_subnormal_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_subnormal_f80_soft: +; 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 @@ -482,10 +884,9 @@ ; 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-LABEL: is_subnormal_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx @@ -502,12 +903,39 @@ ret i1 %0 } -define i1 @is_possubnormal_f80(x86_fp80 %x) { +define i1 @is_possubnormal_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_possubnormal_f80: ; 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 %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_possubnormal_f80: +; 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 %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_possubnormal_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_possubnormal_f80_soft: +; 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 @@ -522,10 +950,9 @@ ; 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-LABEL: is_possubnormal_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: movq {{[0-9]+}}(%rsp), %rcx @@ -542,15 +969,40 @@ ret i1 %0 } -define i1 @is_negsubnormal_f80(x86_fp80 %x) { +define i1 @is_negsubnormal_f80(x86_fp80 %x) #0 { ; CHECK-32-LABEL: is_negsubnormal_f80: ; 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 $17920, %eax # imm = 0x4600 +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: retl +; +; CHECK-64-LABEL: is_negsubnormal_f80: +; 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 $17920, %eax # imm = 0x4600 +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 16) ; 0x10 = "-subnormal" + ret i1 %0 +} + +define i1 @is_negsubnormal_f80_soft(x86_fp80 %x) #1 { +; CHECK-32-LABEL: is_negsubnormal_f80_soft: +; 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 @@ -571,12 +1023,10 @@ ; 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-LABEL: is_negsubnormal_f80_soft: ; CHECK-64: # %bb.0: # %entry ; CHECK-64-NEXT: movzwl {{[0-9]+}}(%rsp), %eax ; CHECK-64-NEXT: movswq %ax, %rcx @@ -597,4 +1047,8 @@ ret i1 %0 } +attributes #0 = { nounwind } +attributes #1 = { nounwind "use-soft-float"="true" } +attributes #2 = { nounwind strictfp } + declare i1 @llvm.is.fpclass.f80(x86_fp80, i32)