diff --git a/llvm/include/llvm/ADT/FloatingPointMode.h b/llvm/include/llvm/ADT/FloatingPointMode.h index 639d931ef88fe..2802841f3fc34 100644 --- a/llvm/include/llvm/ADT/FloatingPointMode.h +++ b/llvm/include/llvm/ADT/FloatingPointMode.h @@ -279,6 +279,29 @@ FPClassTest unknown_sign(FPClassTest Mask); /// Write a human readable form of \p Mask to \p OS raw_ostream &operator<<(raw_ostream &OS, FPClassTest Mask); +/// Returns a true value if if this FPClassTest can be performed with an ordered +/// fcmp to 0, and a false value if it's an unordered fcmp to 0. Returns +/// std::nullopt if it cannot be performed as a compare with 0. +__attribute__((unused)) static std::optional +isFPTestPossibleAsFCmpWithZero( + FPClassTest Test, DenormalMode::DenormalModeKind MFdenormalModeKind, + bool IsDenormalModeInputZero) { + FPClassTest OrderedMask = Test & ~fcNan; + FPClassTest NanTest = Test & fcNan; + bool IsOrdered = NanTest == fcNone; + bool IsUnordered = NanTest == fcNan; + + // Skip cases that are testing for only a qnan or snan. + if (!IsOrdered && !IsUnordered) + return std::nullopt; + + if (OrderedMask == fcZero && MFdenormalModeKind == DenormalMode::IEEE) + return IsOrdered; + if (OrderedMask == (fcZero | fcSubnormal) && IsDenormalModeInputZero) + return IsOrdered; + return std::nullopt; +} + } // namespace llvm #endif // LLVM_ADT_FLOATINGPOINTMODE_H diff --git a/llvm/lib/CodeGen/LowLevelTypeUtils.cpp b/llvm/lib/CodeGen/LowLevelTypeUtils.cpp index 936c9fbb2fff0..226119384140e 100644 --- a/llvm/lib/CodeGen/LowLevelTypeUtils.cpp +++ b/llvm/lib/CodeGen/LowLevelTypeUtils.cpp @@ -80,6 +80,8 @@ const llvm::fltSemantics &llvm::getFltSemanticForLLT(LLT Ty) { return APFloat::IEEEsingle(); case 64: return APFloat::IEEEdouble(); + case 80: + return APFloat::x87DoubleExtended(); case 128: return APFloat::IEEEquad(); } diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 5308593b5c988..08a996076bab9 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -8783,7 +8783,7 @@ SDValue TargetLowering::expandIS_FPCLASS(EVT ResultVT, SDValue Op, OrderedFPTestMask = FPTestMask; const bool IsOrdered = FPTestMask == OrderedFPTestMask; - + const MachineFunction &CurMF = DAG.getMachineFunction(); if (std::optional IsCmp0 = isFCmpEqualZero(FPTestMask, Semantics, DAG.getMachineFunction()); IsCmp0 && (isCondCodeLegalOrCustom( diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp index ba9fa254a477a..53f99f95b134b 100644 --- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp +++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp @@ -13,6 +13,7 @@ #include "X86LegalizerInfo.h" #include "X86Subtarget.h" #include "X86TargetMachine.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" @@ -21,6 +22,7 @@ #include "llvm/CodeGen/ValueTypes.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Type.h" +#include using namespace llvm; using namespace TargetOpcode; @@ -631,6 +633,15 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI, G_STACKSAVE, G_STACKRESTORE}).lower(); + getActionDefinitionsBuilder(G_IS_FPCLASS) + .legalFor(HasSSE1 || UseX87, {s8, s32}) + .legalFor(HasSSE2 || UseX87, {s8, s64}) + .legalFor(UseX87, {s8, s80}) + .custom(); + // TODO: Remove when G_FABS PR is merged + getActionDefinitionsBuilder(G_FABS).legalFor(UseX87, {s80}).lower(); + + getActionDefinitionsBuilder(G_BITCAST).custom(); // fp intrinsics getActionDefinitionsBuilder(G_INTRINSIC_ROUNDEVEN) .scalarize(0) @@ -660,6 +671,10 @@ bool X86LegalizerInfo::legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, return legalizeFPTOUI(MI, MRI, Helper); case TargetOpcode::G_UITOFP: return legalizeUITOFP(MI, MRI, Helper); + case TargetOpcode::G_IS_FPCLASS: + return expandIS_FPCLASS(MI, MRI, Helper); + case TargetOpcode::G_BITCAST: + return legalizeBitcast(MI, MRI, Helper); } llvm_unreachable("expected switch to return"); } @@ -758,3 +773,253 @@ bool X86LegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper, MachineInstr &MI) const { return true; } + +bool X86LegalizerInfo::expandFPClassTestForF32OrF64( + MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const { + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs(); + FPClassTest Test = static_cast(MI.getOperand(2).getImm()); + assert(!SrcTy.isVector() && "G_IS_FPCLASS does not support vectors yet"); + const fltSemantics &Semantics = getFltSemanticForLLT(SrcTy.getScalarType()); + + // Some checks may be represented as inversion of simpler check, for example + // "inf|normal|subnormal|zero" => !"nan". + bool IsInverted = false; + + if (FPClassTest InvertedCheck = invertFPClassTestIfSimpler(Test, false)) { + Test = InvertedCheck; + IsInverted = true; + } + + // In the general case use integer operations. + unsigned BitSize = SrcTy.getScalarSizeInBits(); + LLT IntVT = LLT::scalar(BitSize); + MachineInstrBuilder OpAsInt = MIRBuilder.buildBitcast(IntVT, SrcReg); + + // Various Mask + APInt SignMask = APInt::getSignMask(BitSize); + APInt ValueMask = APInt::getSignedMaxValue(BitSize); + APInt Inf = APFloat::getInf(Semantics).bitcastToAPInt(); + APInt InfPlus1 = Inf + 1; + APInt ExpMask = Inf; + APInt AllOneMantissa = APFloat::getLargest(Semantics).bitcastToAPInt() & ~Inf; + APInt QNaNBitMask = + APInt::getOneBitSet(BitSize, AllOneMantissa.getActiveBits() - 1); + APInt InvertionMask = APInt::getAllOnes(DstTy.getScalarSizeInBits()); + + auto ValueMaskV = MIRBuilder.buildConstant(IntVT, ValueMask); + auto SignBitV = MIRBuilder.buildConstant(IntVT, SignMask); + auto ExpMaskV = MIRBuilder.buildConstant(IntVT, ExpMask); + auto ZeroV = MIRBuilder.buildConstant(IntVT, 0); + auto InfV = MIRBuilder.buildConstant(IntVT, Inf); + auto InfPlus1V = MIRBuilder.buildConstant(IntVT, InfPlus1); + auto ResultInvertedV = MIRBuilder.buildConstant(DstTy, InvertionMask); + + MachineInstrBuilder Res; + const auto appendResult = [&](MachineInstrBuilder &PartialRes) { + if (PartialRes.getInstr()) { + if (Res.getInstr()) { + Res = MIRBuilder.buildOr(DstTy, Res, PartialRes); + } else { + Res = PartialRes; + } + } + }; + // Split the value into sign bit and absolute value. + auto AbsV = MIRBuilder.buildAnd(IntVT, OpAsInt, ValueMaskV); + auto SignVDestReg = MRI.createGenericVirtualRegister(LLT::scalar(1)); + auto SignV = + MIRBuilder.buildICmp(CmpInst::ICMP_SLT, SignVDestReg, OpAsInt, ZeroV); + + // Tests that involve more than one class should be processed first. + MachineInstrBuilder PartialRes; + + if ((Test & fcFinite) == fcFinite) { + // finite(V) ==> abs(V) < exp_mask + PartialRes = MIRBuilder.buildICmp( + IsInverted ? CmpInst::ICMP_SGE : CmpInst::ICMP_SLT, + MRI.createGenericVirtualRegister(LLT::scalar(1)), AbsV, ExpMaskV); + Test &= ~fcFinite; + } else if ((Test & fcFinite) == fcPosFinite) { + // finite(V) && V > 0 ==> V < exp_mask + PartialRes = MIRBuilder.buildICmp( + IsInverted ? CmpInst::ICMP_UGE : CmpInst::ICMP_ULT, + MRI.createGenericVirtualRegister(LLT::scalar(1)), OpAsInt, ExpMaskV); + Test &= ~fcPosFinite; + } else if ((Test & fcFinite) == fcNegFinite) { + // finite(V) && V < 0 ==> abs(V) < exp_mask && signbit == 1 + auto PartialResPart = MIRBuilder.buildICmp( + CmpInst::ICMP_SLT, MRI.createGenericVirtualRegister(LLT::scalar(1)), + AbsV, ExpMaskV); + PartialRes = MIRBuilder.buildAnd(LLT::scalar(1), PartialResPart, SignV); + Test &= ~fcNegFinite; + } + appendResult(PartialRes); + + if (FPClassTest PartialCheck = Test & (fcZero | fcSubnormal)) { + // fcZero | fcSubnormal => test all exponent bits are 0 + // TODO: Handle sign bit specific cases + if (PartialCheck == (fcZero | fcSubnormal)) { + auto ExpBits = MIRBuilder.buildAnd(IntVT, OpAsInt, ExpMaskV); + auto ExpIsZero = MIRBuilder.buildICmp( + CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), + ExpBits, ZeroV); + appendResult(ExpIsZero); + Test &= ~PartialCheck & fcAllFlags; + } + } + + // Check for individual classes. + if (unsigned PartialCheck = Test & fcZero) { + if (PartialCheck == fcPosZero) + PartialRes = MIRBuilder.buildICmp( + CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), + OpAsInt, ZeroV); + else if (PartialCheck == fcZero) + PartialRes = MIRBuilder.buildICmp( + CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), + AbsV, ZeroV); + else // ISD::fcNegZero + PartialRes = MIRBuilder.buildICmp( + CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), + OpAsInt, SignBitV); + appendResult(PartialRes); + } + if (unsigned PartialCheck = Test & fcSubnormal) { + assert("Not Supported yet!"); + } + if (unsigned PartialCheck = Test & fcInf) { + if (PartialCheck == fcPosInf) + PartialRes = MIRBuilder.buildICmp( + IsInverted ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, + MRI.createGenericVirtualRegister(LLT::scalar(1)), OpAsInt, InfV); + else if (PartialCheck == fcInf) + PartialRes = MIRBuilder.buildICmp( + IsInverted ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, + MRI.createGenericVirtualRegister(LLT::scalar(1)), AbsV, InfV); + else { // ISD::fcNegInf + APInt NegInf = APFloat::getInf(Semantics, true).bitcastToAPInt(); + auto NegInfV = MIRBuilder.buildConstant(IntVT, NegInf); + PartialRes = MIRBuilder.buildICmp( + IsInverted ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ, + MRI.createGenericVirtualRegister(LLT::scalar(1)), OpAsInt, NegInfV); + } + MIRBuilder.buildCopy(DstReg, PartialRes); + MI.eraseFromParent(); + return true; + } + if (unsigned PartialCheck = Test & fcNan) { + APInt InfWithQnanBit = Inf | QNaNBitMask; + auto InfWithQnanBitV = MIRBuilder.buildConstant(IntVT, InfWithQnanBit); + if (PartialCheck == fcNan) { + // isnan(V) ==> abs(V) > int(inf) + auto AbsDstReg = MRI.createGenericVirtualRegister(LLT::scalar(BitSize)); + auto FAbsV = MIRBuilder.buildCopy(AbsDstReg, SrcReg); + auto InfVDstReg = MRI.createGenericVirtualRegister(LLT::scalar(BitSize)); + PartialRes = MIRBuilder.buildFCmp( + CmpInst::FCMP_UEQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), + FAbsV, FAbsV); + } else if (PartialCheck == fcQNan) { + // isquiet(V) ==> abs(V) >= (unsigned(Inf) | quiet_bit) + PartialRes = MIRBuilder.buildICmp( + IsInverted ? CmpInst::ICMP_SLT : CmpInst::ICMP_SGE, + MRI.createGenericVirtualRegister(LLT::scalar(1)), AbsV, + InfWithQnanBitV); + + } else { // ISD::fcSNan + // issignaling(V) ==> abs(V) > unsigned(Inf) && + // abs(V) < (unsigned(Inf) | quiet_bit) + auto IsNotQnan = MIRBuilder.buildICmp( + CmpInst::ICMP_SLT, MRI.createGenericVirtualRegister(LLT::scalar(1)), + AbsV, InfWithQnanBitV); + auto IsNan = MIRBuilder.buildICmp( + CmpInst::ICMP_SGE, MRI.createGenericVirtualRegister(LLT::scalar(1)), + AbsV, InfPlus1V); + PartialRes = MIRBuilder.buildAnd(LLT::scalar(1), IsNan, IsNotQnan); + } + MIRBuilder.buildCopy(DstReg, PartialRes); + MI.eraseFromParent(); + return true; + } + if (unsigned PartialCheck = Test & fcNormal) { + assert("Not Supported yet!"); + } + if (unsigned PartialCheck = Test & fcSubnormal) { + // subnormal(V) ==> abs(V) < exp_mask && signbit == 0 + auto ExpBits = MIRBuilder.buildAnd(IntVT, OpAsInt, ExpMaskV); + auto ExpIsZero = MIRBuilder.buildICmp( + CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), + ExpBits, ZeroV); + auto SignBit = MIRBuilder.buildICmp( + CmpInst::ICMP_EQ, MRI.createGenericVirtualRegister(LLT::scalar(1)), + SignV, ZeroV); + PartialRes = MIRBuilder.buildAnd(LLT::scalar(1), ExpIsZero, SignBit); + appendResult(PartialRes); + } + if (!Res.getInstr()) { + Res = MIRBuilder.buildConstant(LLT::scalar(1), IsInverted); + MIRBuilder.buildCopy(DstReg, Res); + MI.eraseFromParent(); + return true; + } + + MIRBuilder.buildCopy(DstReg, Res); + MI.eraseFromParent(); + return true; +} + +bool X86LegalizerInfo::expandFPClassTestForF80(MachineInstr &MI, + MachineRegisterInfo &MRI, + LegalizerHelper &Helper) const { + + return false; +} + +bool X86LegalizerInfo::expandIS_FPCLASS(MachineInstr &MI, + MachineRegisterInfo &MRI, + LegalizerHelper &Helper) const { + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs(); + assert(!SrcTy.isVector() && "G_IS_FPCLASS does not support vectors yet"); + + FPClassTest Mask = static_cast(MI.getOperand(2).getImm()); + if (Mask == fcNone) { + MIRBuilder.buildConstant(DstReg, 0); + MI.eraseFromParent(); + return true; + } + if (Mask == fcAllFlags) { + MIRBuilder.buildConstant(DstReg, 1); + MI.eraseFromParent(); + return true; + } + bool IsF80 = (SrcTy == LLT::scalar(80)); + const fltSemantics &Semantics = getFltSemanticForLLT(SrcTy.getScalarType()); + const MachineFunction &CurMF = *MI.getMF(); + // For f32/f64/f80 if NoFpException is set, we can use the FCMP + // Some checks can be implemented using float comparisons, if floating point + // exceptions are ignored. + if (MI.getFlag(MachineInstr::NoFPExcept)) { + assert("NoFpException is not supported!"); + } + + if (IsF80) + return expandFPClassTestForF80(MI, MRI, Helper); + return expandFPClassTestForF32OrF64(MI, MRI, Helper); +} + +bool X86LegalizerInfo::legalizeBitcast(MachineInstr &MI, + MachineRegisterInfo &MRI, + LegalizerHelper &Helper) const { + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs(); + assert(!SrcTy.isVector() && "G_BITCAST does not support vectors yet"); + bool isCopy = + (SrcTy == DstTy) || (SrcTy.getSizeInBits() == DstTy.getSizeInBits()); + if (isCopy) { + MIRBuilder.buildCopy(DstReg, SrcReg); + MI.eraseFromParent(); + return true; + } + return Helper.lowerBitcast(MI) == LegalizerHelper::LegalizeResult::Legalized; +} diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h index 39bd9892e2f16..12fd5d19993e4 100644 --- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h +++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h @@ -45,6 +45,17 @@ class X86LegalizerInfo : public LegalizerInfo { bool legalizeUITOFP(MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper &Helper) const; + + bool legalizeBitcast(MachineInstr &MI, MachineRegisterInfo &MRI, + LegalizerHelper &Helper) const; + + bool expandIS_FPCLASS(MachineInstr &MI, MachineRegisterInfo &MRI, + LegalizerHelper &Helper) const; + + bool expandFPClassTestForF32OrF64(MachineInstr &MI, MachineRegisterInfo &MRI, + LegalizerHelper &Helper) const; + bool expandFPClassTestForF80(MachineInstr &MI, MachineRegisterInfo &MRI, + LegalizerHelper &Helper) const; }; } // namespace llvm #endif diff --git a/llvm/test/CodeGen/X86/fpclass.ll b/llvm/test/CodeGen/X86/fpclass.ll new file mode 100644 index 0000000000000..b351716187448 --- /dev/null +++ b/llvm/test/CodeGen/X86/fpclass.ll @@ -0,0 +1,440 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc < %s -mtriple=i686-linux | FileCheck %s -check-prefixes=X86 +; RUN: llc < %s -mtriple=x86_64-linux | FileCheck %s -check-prefixes=X64 +; RUN: llc < %s -mtriple=x86_64-linux -global-isel | FileCheck %s -check-prefixes=GISEL + +; FIXME: This is only for code review until all changes are pushed. +; we will reuse isfpclass.ll and isfpclass-f80.ll + +define i1 @is_fcNone_f32(float %x) nounwind { +; X86-LABEL: is_fcNone_f32: +; X86: # %bb.0: # %entry +; X86-NEXT: xorl %eax, %eax +; X86-NEXT: retl +; +; X64-LABEL: is_fcNone_f32: +; X64: # %bb.0: # %entry +; X64-NEXT: xorl %eax, %eax +; X64-NEXT: retq +; +; GISEL-LABEL: is_fcNone_f32: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: xorl %eax, %eax +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 0) + ret i1 %0 +} + +define i1 @is_fcAllFlags_f32(float %x) nounwind { +; X86-LABEL: is_fcAllFlags_f32: +; X86: # %bb.0: # %entry +; X86-NEXT: movb $1, %al +; X86-NEXT: retl +; +; X64-LABEL: is_fcAllFlags_f32: +; X64: # %bb.0: # %entry +; X64-NEXT: movb $1, %al +; X64-NEXT: retq +; +; GISEL-LABEL: is_fcAllFlags_f32: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movb $1, %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 1023) + ret i1 %0 +} + +define i1 @issignaling_f(float %x) { +; X86-LABEL: issignaling_f: +; X86: # %bb.0: +; X86-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; X86-NEXT: andl {{[0-9]+}}(%esp), %eax +; X86-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; X86-NEXT: setl %cl +; X86-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; X86-NEXT: setge %al +; X86-NEXT: andb %cl, %al +; X86-NEXT: retl +; +; X64-LABEL: issignaling_f: +; X64: # %bb.0: +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; X64-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; X64-NEXT: setl %cl +; X64-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; X64-NEXT: setge %al +; X64-NEXT: andb %cl, %al +; X64-NEXT: retq +; +; GISEL-LABEL: issignaling_f: +; GISEL: # %bb.0: +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; GISEL-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; GISEL-NEXT: setl %cl +; GISEL-NEXT: cmpl $2139095041, %eax # imm = 0x7F800001 +; GISEL-NEXT: setge %al +; GISEL-NEXT: andb %cl, %al +; GISEL-NEXT: retq + %a0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 1) ; "snan" + ret i1 %a0 +} + + define i1 @isquiet_f(float %x) { +; X86-LABEL: isquiet_f: +; X86: # %bb.0: # %entry +; X86-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; X86-NEXT: andl {{[0-9]+}}(%esp), %eax +; X86-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; X86-NEXT: setge %al +; X86-NEXT: retl +; +; X64-LABEL: isquiet_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; X64-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; X64-NEXT: setge %al +; X64-NEXT: retq +; +; GISEL-LABEL: isquiet_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; GISEL-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; GISEL-NEXT: setge %al +; GISEL-NEXT: retq + entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 2) ; "qnan" + ret i1 %0 +} + +define i1 @not_isquiet_f(float %x) { +; X86-LABEL: not_isquiet_f: +; X86: # %bb.0: # %entry +; X86-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; X86-NEXT: andl {{[0-9]+}}(%esp), %eax +; X86-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; X86-NEXT: setl %al +; X86-NEXT: retl +; +; X64-LABEL: not_isquiet_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; X64-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; X64-NEXT: setl %al +; X64-NEXT: retq +; +; GISEL-LABEL: not_isquiet_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; GISEL-NEXT: cmpl $2143289344, %eax # imm = 0x7FC00000 +; GISEL-NEXT: setl %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 1021) ; ~"qnan" + ret i1 %0 +} + +define i1 @isinf_f(float %x) { +; X86-LABEL: isinf_f: +; X86: # %bb.0: # %entry +; X86-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; X86-NEXT: andl {{[0-9]+}}(%esp), %eax +; X86-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X86-NEXT: sete %al +; X86-NEXT: retl +; +; X64-LABEL: isinf_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; X64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X64-NEXT: sete %al +; X64-NEXT: retq +; +; GISEL-LABEL: isinf_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; GISEL-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; GISEL-NEXT: sete %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 516) ; 0x204 = "inf" + ret i1 %0 +} + +define i1 @not_isinf_f(float %x) { +; X86-LABEL: not_isinf_f: +; X86: # %bb.0: # %entry +; X86-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; X86-NEXT: andl {{[0-9]+}}(%esp), %eax +; X86-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X86-NEXT: setne %al +; X86-NEXT: retl +; +; X64-LABEL: not_isinf_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; X64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X64-NEXT: setne %al +; X64-NEXT: retq +; +; GISEL-LABEL: not_isinf_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; GISEL-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; GISEL-NEXT: setne %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 507) ; ~0x204 = "~inf" + ret i1 %0 +} + + + +define i1 @is_plus_inf_f(float %x) { +; X86-LABEL: is_plus_inf_f: +; X86: # %bb.0: # %entry +; X86-NEXT: cmpl $2139095040, {{[0-9]+}}(%esp) # imm = 0x7F800000 +; X86-NEXT: sete %al +; X86-NEXT: retl +; +; X64-LABEL: is_plus_inf_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X64-NEXT: sete %al +; X64-NEXT: retq +; +; GISEL-LABEL: is_plus_inf_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; GISEL-NEXT: sete %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 512) ; 0x200 = "+inf" + ret i1 %0 +} + +define i1 @is_minus_inf_f(float %x) { +; X86-LABEL: is_minus_inf_f: +; X86: # %bb.0: # %entry +; X86-NEXT: cmpl $-8388608, {{[0-9]+}}(%esp) # imm = 0xFF800000 +; X86-NEXT: sete %al +; X86-NEXT: retl +; +; X64-LABEL: is_minus_inf_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: cmpl $-8388608, %eax # imm = 0xFF800000 +; X64-NEXT: sete %al +; X64-NEXT: retq +; +; GISEL-LABEL: is_minus_inf_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: cmpl $-8388608, %eax # imm = 0xFF800000 +; GISEL-NEXT: sete %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 4) ; "-inf" + ret i1 %0 +} + +define i1 @not_is_minus_inf_f(float %x) { +; X86-LABEL: not_is_minus_inf_f: +; X86: # %bb.0: # %entry +; X86-NEXT: cmpl $-8388608, {{[0-9]+}}(%esp) # imm = 0xFF800000 +; X86-NEXT: setne %al +; X86-NEXT: retl +; +; X64-LABEL: not_is_minus_inf_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: cmpl $-8388608, %eax # imm = 0xFF800000 +; X64-NEXT: setne %al +; X64-NEXT: retq +; +; GISEL-LABEL: not_is_minus_inf_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: cmpl $-8388608, %eax # imm = 0xFF800000 +; GISEL-NEXT: setne %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 1019) ; ~"-inf" + ret i1 %0 +} + +define i1 @isfinite_f(float %x) { +; X86-LABEL: isfinite_f: +; X86: # %bb.0: # %entry +; X86-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; X86-NEXT: andl {{[0-9]+}}(%esp), %eax +; X86-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X86-NEXT: setl %al +; X86-NEXT: retl +; +; X64-LABEL: isfinite_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; X64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X64-NEXT: setl %al +; X64-NEXT: retq +; +; GISEL-LABEL: isfinite_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; GISEL-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; GISEL-NEXT: setl %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 504) ; 0x1f8 = "finite" + ret i1 %0 +} + +define i1 @not_isfinite_f(float %x) { +; X86-LABEL: not_isfinite_f: +; X86: # %bb.0: # %entry +; X86-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF +; X86-NEXT: andl {{[0-9]+}}(%esp), %eax +; X86-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X86-NEXT: setge %al +; X86-NEXT: retl +; +; X64-LABEL: not_isfinite_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; X64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X64-NEXT: setge %al +; X64-NEXT: retq +; +; GISEL-LABEL: not_isfinite_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; GISEL-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; GISEL-NEXT: setge %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 519) ; ~0x1f8 = "~finite" + ret i1 %0 +} + +define i1 @is_plus_finite_f(float %x) { +; X86-LABEL: is_plus_finite_f: +; X86: # %bb.0: # %entry +; X86-NEXT: cmpl $2139095040, {{[0-9]+}}(%esp) # imm = 0x7F800000 +; X86-NEXT: setb %al +; X86-NEXT: retl +; +; X64-LABEL: is_plus_finite_f: +; X64: # %bb.0: # %entry +; X64-NEXT: movd %xmm0, %eax +; X64-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; X64-NEXT: setb %al +; X64-NEXT: retq +; +; GISEL-LABEL: is_plus_finite_f: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movd %xmm0, %eax +; GISEL-NEXT: cmpl $2139095040, %eax # imm = 0x7F800000 +; GISEL-NEXT: setb %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 448) ; 0x1c0 = "+finite" + ret i1 %0 +} + +define i1 @is_fcNone_f64(double %x) nounwind { +; X86-LABEL: is_fcNone_f64: +; X86: # %bb.0: # %entry +; X86-NEXT: xorl %eax, %eax +; X86-NEXT: retl +; +; X64-LABEL: is_fcNone_f64: +; X64: # %bb.0: # %entry +; X64-NEXT: xorl %eax, %eax +; X64-NEXT: retq +; +; GISEL-LABEL: is_fcNone_f64: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: xorl %eax, %eax +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 0) + ret i1 %0 +} + +define i1 @is_fcAllFlags_f64(double %x) nounwind { +; X86-LABEL: is_fcAllFlags_f64: +; X86: # %bb.0: # %entry +; X86-NEXT: movb $1, %al +; X86-NEXT: retl +; +; X64-LABEL: is_fcAllFlags_f64: +; X64: # %bb.0: # %entry +; X64-NEXT: movb $1, %al +; X64-NEXT: retq +; +; GISEL-LABEL: is_fcAllFlags_f64: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movb $1, %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 1023) + ret i1 %0 +} + +define i1 @is_fcNone_f80(x86_fp80 %x) nounwind { +; X86-LABEL: is_fcNone_f80: +; X86: # %bb.0: # %entry +; X86-NEXT: xorl %eax, %eax +; X86-NEXT: retl +; +; X64-LABEL: is_fcNone_f80: +; X64: # %bb.0: # %entry +; X64-NEXT: xorl %eax, %eax +; X64-NEXT: retq +; +; GISEL-LABEL: is_fcNone_f80: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: xorl %eax, %eax +; GISEL-NEXT: retq +entry: +%0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 0) +ret i1 %0 +} + +define i1 @is_fcAllFlags_f80(x86_fp80 %x) nounwind { +; X86-LABEL: is_fcAllFlags_f80: +; X86: # %bb.0: # %entry +; X86-NEXT: movb $1, %al +; X86-NEXT: retl +; +; X64-LABEL: is_fcAllFlags_f80: +; X64: # %bb.0: # %entry +; X64-NEXT: movb $1, %al +; X64-NEXT: retq +; +; GISEL-LABEL: is_fcAllFlags_f80: +; GISEL: # %bb.0: # %entry +; GISEL-NEXT: movb $1, %al +; GISEL-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f80(x86_fp80 %x, i32 1023) + ret i1 %0 +}