diff --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst --- a/llvm/docs/GlobalISel/GenericOpcode.rst +++ b/llvm/docs/GlobalISel/GenericOpcode.rst @@ -480,6 +480,16 @@ See :ref:`i_intr_llvm_canonicalize`. +G_IS_FPCLASS +^^^^^^^^^^^^ + +GlobalISel-equivalent of the '``llvm.is_fpclass``' intrinsic. + +Checks if the first operand, which must be floating-point scalar or vector, is +of the class specified by the second operand. The third argument specifies +floating-point semantics of the first operand. Returns 1-bit value or vector of +them. + G_FMINNUM ^^^^^^^^^ diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h @@ -406,6 +406,7 @@ LegalizeResult lowerVectorReduction(MachineInstr &MI); LegalizeResult lowerMemcpyInline(MachineInstr &MI); LegalizeResult lowerMemCpyFamily(MachineInstr &MI, unsigned MaxLen = 0); + LegalizeResult lowerIsFPClass(MachineInstr &MI); }; /// Helper function that creates a libcall to the given \p Name using the given diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -620,6 +620,10 @@ /// f64) is allowed. HANDLE_TARGET_OPCODE(G_FCOPYSIGN) +/// Determines if the first operand (which must be a floating-point value or a +/// vector of such) is of the class specified by the second operand. +HANDLE_TARGET_OPCODE(G_IS_FPCLASS) + /// Generic FP canonicalize value. HANDLE_TARGET_OPCODE(G_FCANONICALIZE) diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td --- a/llvm/include/llvm/Target/GenericOpcodes.td +++ b/llvm/include/llvm/Target/GenericOpcodes.td @@ -745,6 +745,13 @@ let hasSideEffects = false; } +// Generic opcode equivalent to the llvm.is_fpclass intrinsic. +def G_IS_FPCLASS: GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type1:$src, unknown:$tst, unknown:$fptype); + let hasSideEffects = false; +} + // FMINNUM/FMAXNUM - Perform floating-point minimum or maximum on two // values. // diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -2269,6 +2269,20 @@ return true; } + case Intrinsic::is_fpclass: { + unsigned Flags = + MachineInstr::copyFlagsFromInstruction(CI) | MachineInstr::NoFPExcept; + Register Src = getOrCreateVReg(*CI.getArgOperand(0)); + unsigned Test = cast(CI.getOperand(1))->getZExtValue(); + const fltSemantics &S = + CI.getArgOperand(0)->getType()->getScalarType()->getFltSemantics(); + MIRBuilder + .buildInstr(TargetOpcode::G_IS_FPCLASS, {getOrCreateVReg(CI)}, {Src}, + Flags) + .addImm(Test) + .addImm(APFloat::SemanticsToEnum(S)); + return true; + } #define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC) \ case Intrinsic::INTRINSIC: #include "llvm/IR/ConstrainedOps.def" diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/GlobalISel/CallLowering.h" #include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h" #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" @@ -3566,6 +3567,8 @@ return lowerMemcpyInline(MI); GISEL_VECREDUCE_CASES_NONSEQ return lowerVectorReduction(MI); + case G_IS_FPCLASS: + return lowerIsFPClass(MI); } } @@ -7874,3 +7877,183 @@ return lowerMemset(MI, Dst, Src, KnownLen, DstAlign, IsVolatile); return UnableToLegalize; } + +LegalizerHelper::LegalizeResult +LegalizerHelper::lowerIsFPClass(MachineInstr &MI) { + Register Dst = MI.getOperand(0).getReg(); + Register Src = MI.getOperand(1).getReg(); + unsigned Test = MI.getOperand(2).getImm(); + auto FloatSem = static_cast(MI.getOperand(3).getImm()); + LLT DstTy = MRI.getType(Dst); + LLT SrcTy = MRI.getType(Src); + + // Some checks may be represented as inversion of simpler check, for example + // "inf|normal|subnormal|zero" => !"nan". + bool IsInverted = false; + if (unsigned InvertedCheck = getInvertedFPClassTest(Test)) { + IsInverted = true; + Test = InvertedCheck; + } + + // Some checks can be implemented using float comparisons, if floating point + // exceptions are ignored. + Function &F = MI.getParent()->getParent()->getFunction(); + if (!F.getAttributes().hasFnAttr(llvm::Attribute::StrictFP)) { + if (Test == fcZero) { + CmpInst::Predicate Predicate = IsInverted ? CmpInst::Predicate::FCMP_UNE + : CmpInst::Predicate::FCMP_OEQ; + auto Zero = MIRBuilder.buildFConstant(SrcTy, 0.0); + MIRBuilder.buildFCmp(Predicate, Dst, Src, Zero); + MI.eraseFromParent(); + return Legalized; + } + if (Test == fcNan) { + CmpInst::Predicate Predicate = IsInverted ? CmpInst::Predicate::FCMP_ORD + : CmpInst::Predicate::FCMP_UNO; + MIRBuilder.buildFCmp(Predicate, Dst, Src, Src); + MI.eraseFromParent(); + return Legalized; + } + } + + // In the general case use integer operations. + unsigned BitSize = SrcTy.getSizeInBits(); + LLT IntTy = LLT::scalar(BitSize); + const fltSemantics &S = APFloat::EnumToSemantics(FloatSem); + + APInt SignBit = APInt::getSignMask(BitSize); + APInt ValueMask = APInt::getSignedMaxValue(BitSize); // All bits but sign. + APInt Inf = APFloat::getInf(S).bitcastToAPInt(); // Exp and int bit. + APInt ExpMask = Inf; + APInt AllOneMantissa = APFloat::getLargest(S).bitcastToAPInt() & ~Inf; + APInt QNaNBitMask = AllOneMantissa & ~(AllOneMantissa.lshr(1)); + + auto ValueMaskV = MIRBuilder.buildConstant(IntTy, ValueMask); + auto SignBitV = MIRBuilder.buildConstant(IntTy, SignBit); + auto ExpMaskV = MIRBuilder.buildConstant(IntTy, ExpMask); + auto ZeroV = MIRBuilder.buildConstant(IntTy, 0); + auto InfV = MIRBuilder.buildConstant(IntTy, Inf); + + APInt InvertionMask = APInt::getAllOnesValue(DstTy.getScalarSizeInBits()); + auto ResultInvertionMask = MIRBuilder.buildConstant(DstTy, InvertionMask); + + Optional Res; + const auto appendResult = [&](MachineInstrBuilder PartialRes) { + if (PartialRes) { + if (Res) + Res = MIRBuilder.buildOr(DstTy, Res.getValue(), PartialRes); + else + Res = PartialRes; + } + }; + + // Split the value into sign bit and absolute value. + auto AbsV = MIRBuilder.buildAnd(IntTy, Src, ValueMaskV); + auto SignV = MIRBuilder.buildICmp(CmpInst::ICMP_SLT, DstTy, Src, ZeroV); + + // Tests that involve more than one class should be processed first. + MachineInstrBuilder PartialRes; + + if ((Test & fcFinite) == fcFinite) { + // finite(V) ==> exp mask > abs(V) + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_SGT, DstTy, ExpMaskV, AbsV); + Test &= ~fcFinite; + } else if ((Test & fcFinite) == fcPosFinite) { + // finite(V) && V > 0 ==> exp mask > V + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_UGT, DstTy, ExpMaskV, Src); + Test &= ~fcPosFinite; + } else if ((Test & fcFinite) == fcNegFinite) { + // finite(V) && V < 0 ==> exp mask > abs(V) && signbit == 1 + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_UGT, DstTy, ExpMaskV, AbsV); + PartialRes = MIRBuilder.buildAnd(DstTy, PartialRes, SignV); + Test &= ~fcNegFinite; + } + appendResult(PartialRes); + + // Check for individual classes. + + if (unsigned PartialCheck = Test & fcZero) { + if (PartialCheck == fcPosZero) + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_EQ, DstTy, Src, ZeroV); + else if (PartialCheck == fcZero) + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_EQ, DstTy, AbsV, ZeroV); + else // fcNegZero + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_EQ, DstTy, Src, SignBitV); + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcInf) { + if (PartialCheck == fcPosInf) + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_EQ, DstTy, Src, InfV); + else if (PartialCheck == fcInf) + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_EQ, DstTy, AbsV, InfV); + else { // fcNegInf + APInt NegInf = APFloat::getInf(S, true).bitcastToAPInt(); + auto NegInfV = MIRBuilder.buildConstant(IntTy, NegInf); + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_EQ, DstTy, Src, NegInfV); + } + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcNan) { + APInt InfWithQnanBit = Inf | QNaNBitMask; + auto InfWithQnanBitV = MIRBuilder.buildConstant(IntTy, InfWithQnanBit); + if (PartialCheck == fcNan) { + // isnan(V) ==> abs(V) > int(inf) + PartialRes = MIRBuilder.buildICmp(CmpInst::ICMP_SGT, DstTy, AbsV, InfV); + } else if (PartialCheck == fcQNan) { + // isquiet(V) ==> unsigned(V) >= (unsigned(Inf) | quiet_bit) + PartialRes = + MIRBuilder.buildICmp(CmpInst::ICMP_SGE, DstTy, AbsV, InfWithQnanBitV); + } else { // fcSNan + // issignaling(V) ==> abs(V) > unsigned(Inf) && + // abs(V) < (unsigned(Inf) | quiet_bit) + auto IsNan = MIRBuilder.buildICmp(CmpInst::ICMP_SGT, DstTy, AbsV, InfV); + auto IsNotQnan = + MIRBuilder.buildICmp(CmpInst::ICMP_SLT, DstTy, AbsV, InfWithQnanBitV); + PartialRes = MIRBuilder.buildAnd(DstTy, IsNan, IsNotQnan); + } + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcSubnormal) { + // issubnormal(V) ==> unsigned(abs(V) - 1) < (all mantissa bits set) + // issubnormal(V) && V>0 ==> unsigned(V - 1) < (all mantissa bits set) + auto V = (PartialCheck == fcPosSubnormal) ? SrcOp(Src) : AbsV; + auto MantissaV = MIRBuilder.buildConstant(IntTy, AllOneMantissa); + auto VMinusOneV = + MIRBuilder.buildSub(IntTy, V, MIRBuilder.buildConstant(IntTy, 1)); + PartialRes = + MIRBuilder.buildICmp(CmpInst::ICMP_ULT, DstTy, VMinusOneV, MantissaV); + if (PartialCheck == fcNegSubnormal) + PartialRes = MIRBuilder.buildAnd(DstTy, PartialRes, SignV); + appendResult(PartialRes); + } + + if (unsigned PartialCheck = Test & fcNormal) { + // isnormal(V) ==> (0 < exp < max_exp) == (unsigned(exp-1) < (max_exp-1)) + APInt ExpLSB = ExpMask & ~(ExpMask.shl(1)); + auto ExpLSBV = MIRBuilder.buildConstant(IntTy, ExpLSB); + auto ExpMinus1 = MIRBuilder.buildSub(IntTy, AbsV, ExpLSBV); + APInt ExpLimit = ExpMask - ExpLSB; + auto ExpLimitV = MIRBuilder.buildConstant(IntTy, ExpLimit); + PartialRes = + MIRBuilder.buildICmp(CmpInst::ICMP_ULT, DstTy, ExpMinus1, ExpLimitV); + if (PartialCheck == fcNegNormal) + PartialRes = MIRBuilder.buildAnd(DstTy, PartialRes, SignV); + else if (PartialCheck == fcPosNormal) { + auto PosSignV = MIRBuilder.buildXor(DstTy, SignV, ResultInvertionMask); + PartialRes = MIRBuilder.buildAnd(DstTy, PartialRes, PosSignV); + } + appendResult(PartialRes); + } + + if (!Res) + MIRBuilder.buildConstant(Dst, IsInverted); + else if (IsInverted) + MIRBuilder.buildXor(Dst, Res.getValue(), ResultInvertionMask); + else + MIRBuilder.buildCopy(Dst, Res.getValue()); + MI.eraseFromParent(); + return Legalized; +} diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp --- a/llvm/lib/CodeGen/MachineVerifier.cpp +++ b/llvm/lib/CodeGen/MachineVerifier.cpp @@ -32,6 +32,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Analysis/EHPersonalities.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/LiveInterval.h" #include "llvm/CodeGen/LiveIntervalCalc.h" #include "llvm/CodeGen/LiveIntervals.h" @@ -1629,6 +1630,29 @@ verifyAllRegOpsScalar(*MI, *MRI); break; } + case TargetOpcode::G_IS_FPCLASS: { + LLT DestTy = MRI->getType(MI->getOperand(0).getReg()); + LLT DestEltTy = DestTy.getScalarType(); + if (DestEltTy != LLT::scalar(1)) { + report("Destination must be a 1-bit scalar or vector of 1-bit elements", + MI); + break; + } + LLT SrcTy = MRI->getType(MI->getOperand(1).getReg()); + LLT SrcEltTy = SrcTy.getScalarType(); + if (!SrcEltTy.isScalar()) { + report("Source must be a scalar or vector of scalars", MI); + break; + } + if (!verifyVectorElementMatch(DestTy, SrcTy, MI)) + break; + int64_t Test = MI->getOperand(2).getImm(); + if (Test < 0 || Test > fcAllFlags) { + report("Incorrect floating-point class set", MI); + break; + } + break; + } default: break; } diff --git a/llvm/lib/Target/X86/X86LegalizerInfo.cpp b/llvm/lib/Target/X86/X86LegalizerInfo.cpp --- a/llvm/lib/Target/X86/X86LegalizerInfo.cpp +++ b/llvm/lib/Target/X86/X86LegalizerInfo.cpp @@ -75,6 +75,7 @@ .scalarize(0) .minScalar(0, LLT::scalar(32)) .libcall(); + getActionDefinitionsBuilder(G_IS_FPCLASS).lower(); auto &LegacyInfo = getLegacyLegalizerInfo(); LegacyInfo.setLegalizeScalarToDifferentSizeStrategy(G_PHI, 0, widen_1); diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir --- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir @@ -484,6 +484,9 @@ # DEBUG-NEXT: G_FCOPYSIGN (opcode {{[0-9]+}}): 2 type indices # DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined # DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined +# DEBUG-NEXT: G_IS_FPCLASS (opcode 174): 2 type indices, 0 imm indices +# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined +# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined # DEBUG-NEXT: G_FCANONICALIZE (opcode {{[0-9]+}}): 1 type index, 0 imm indices # DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined # DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined diff --git a/llvm/test/CodeGen/X86/GlobalISel/is_fpclass.ll b/llvm/test/CodeGen/X86/GlobalISel/is_fpclass.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/GlobalISel/is_fpclass.ll @@ -0,0 +1,468 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64 -global-isel | FileCheck %s -check-prefix=CHECK-64 + +define i1 @isnan_f(float %x) { +; CHECK-64-LABEL: isnan_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomiss %xmm0, %xmm0 +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 3) ; "nan" + ret i1 %0 +} + +define i1 @isnot_nan_f(float %x) { +; CHECK-64-LABEL: isnot_nan_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomiss %xmm0, %xmm0 +; CHECK-64-NEXT: setnp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 1020) ; 0x3fc = "zero|subnormal|normal|inf" + ret i1 %0 +} + +define i1 @issignaling_f(float %x) { +; CHECK-64-LABEL: issignaling_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: andl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: movl $2143289344, %esi # imm = 0x7FC00000 +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: setg %dl +; CHECK-64-NEXT: cmpl %esi, %ecx +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: andb %dl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 1) ; "snan" + ret i1 %0 +} + +define i1 @isquiet_f(float %x) { +; CHECK-64-LABEL: isquiet_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: movl $2143289344, %ecx # imm = 0x7FC00000 +; CHECK-64-NEXT: cmpl %ecx, %eax +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 2) ; "qnan" + ret i1 %0 +} + +define i1 @isinf_f(float %x) { +; CHECK-64-LABEL: isinf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: andl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 516) ; 0x204 = "inf" + ret i1 %0 +} + +define i1 @is_plus_inf_f(float %x) { +; CHECK-64-LABEL: is_plus_inf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 512) ; 0x200 = "+inf" + ret i1 %0 +} + +define i1 @is_minus_inf_f(float %x) { +; CHECK-64-LABEL: is_minus_inf_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $-8388608, %eax # imm = 0xFF800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 4) ; "-inf" + ret i1 %0 +} + +define i1 @isfinite_f(float %x) { +; CHECK-64-LABEL: isfinite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: andl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %ecx, %eax +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 504) ; 0x1f8 = "finite" + ret i1 %0 +} + +define i1 @is_plus_finite_f(float %x) { +; CHECK-64-LABEL: is_plus_finite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: cmpl %ecx, %eax +; CHECK-64-NEXT: seta %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 448) ; 0x1c0 = "+finite" + ret i1 %0 +} + +define i1 @is_minus_finite_f(float %x) { +; CHECK-64-LABEL: is_minus_finite_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: xorl %ecx, %ecx +; CHECK-64-NEXT: movd %xmm0, %edx +; CHECK-64-NEXT: movl %edx, %esi +; CHECK-64-NEXT: andl $2147483647, %esi # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %ecx, %edx +; CHECK-64-NEXT: setl %cl +; CHECK-64-NEXT: cmpl %esi, %eax +; CHECK-64-NEXT: seta %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 56) ; 0x38 = "-finite" + ret i1 %0 +} + +define i1 @isnormal_f(float %x) { +; CHECK-64-LABEL: isnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: subl $8388608, %eax # imm = 0x800000 +; CHECK-64-NEXT: movl $2130706432, %ecx # imm = 0x7F000000 +; CHECK-64-NEXT: cmpl %ecx, %eax +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 264) ; 0x108 = "normal" + ret i1 %0 +} + +define i1 @is_plus_normal_f(float %x) { +; CHECK-64-LABEL: is_plus_normal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorl %eax, %eax +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: movl %ecx, %edx +; CHECK-64-NEXT: andl $2147483647, %edx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: subl $8388608, %edx # imm = 0x800000 +; CHECK-64-NEXT: movl $2130706432, %ecx # imm = 0x7F000000 +; CHECK-64-NEXT: cmpl %ecx, %edx +; CHECK-64-NEXT: setb %cl +; CHECK-64-NEXT: xorb $1, %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 256) ; 0x100 = "+normal" + ret i1 %0 +} + +define i1 @issubnormal_f(float %x) { +; CHECK-64-LABEL: issubnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: movl $8388607, %ecx # imm = 0x7FFFFF +; CHECK-64-NEXT: subl $1, %eax +; CHECK-64-NEXT: cmpl %ecx, %eax +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 144) ; 0x90 = "subnormal" + ret i1 %0 +} + +define i1 @is_plus_subnormal_f(float %x) { +; CHECK-64-LABEL: is_plus_subnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $8388607, %eax # imm = 0x7FFFFF +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: subl $1, %ecx +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 128) ; 0x80 = "+subnormal" + ret i1 %0 +} + +define i1 @is_minus_subnormal_f(float %x) { +; CHECK-64-LABEL: is_minus_subnormal_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorl %eax, %eax +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: movl %ecx, %edx +; CHECK-64-NEXT: andl $2147483647, %edx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: setl %cl +; CHECK-64-NEXT: movl $8388607, %eax # imm = 0x7FFFFF +; CHECK-64-NEXT: subl $1, %edx +; CHECK-64-NEXT: cmpl %eax, %edx +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 16) ; 0x10 = "-subnormal" + ret i1 %0 +} + +define i1 @iszero_f(float %x) { +; CHECK-64-LABEL: iszero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movss {{.*#+}} xmm1 = mem[0],zero,zero,zero +; CHECK-64-NEXT: ucomiss %xmm1, %xmm0 +; CHECK-64-NEXT: sete %cl +; CHECK-64-NEXT: setnp %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 96) ; 0x60 = "zero" + ret i1 %0 +} + +define i1 @is_plus_zero_f(float %x) { +; CHECK-64-LABEL: is_plus_zero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorl %eax, %eax +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 64) ; 0x40 = "+zero" + ret i1 %0 +} + +define i1 @is_minus_zero_f(float %x) { +; CHECK-64-LABEL: is_minus_zero_f: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $-2147483648, %eax # imm = 0x80000000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 32) ; 0x20 = "-zero" + ret i1 %0 +} + + + +define i1 @isnan_f_strictfp(float %x) strictfp { +; CHECK-64-LABEL: isnan_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: andl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 3) ; "nan" + ret i1 %0 +} + +define i1 @isfinite_f_strictfp(float %x) strictfp { +; CHECK-64-LABEL: isfinite_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movl $2139095040, %eax # imm = 0x7F800000 +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: andl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %ecx, %eax +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 504) ; 0x1f8 = "finite" + ret i1 %0 +} + +define i1 @iszero_f_strictfp(float %x) strictfp { +; CHECK-64-LABEL: iszero_f_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: xorl %eax, %eax +; CHECK-64-NEXT: movd %xmm0, %ecx +; CHECK-64-NEXT: andl $2147483647, %ecx # imm = 0x7FFFFFFF +; CHECK-64-NEXT: cmpl %eax, %ecx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 96) ; 0x60 = "zero" + ret i1 %0 +} + + +define i1 @isnan_d(double %x) { +; CHECK-64-LABEL: isnan_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: ucomisd %xmm0, %xmm0 +; CHECK-64-NEXT: setp %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 3) ; "nan" + ret i1 %0 +} + +define i1 @isinf_d(double %x) { +; CHECK-64-LABEL: isinf_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movabsq $9218868437227405312, %rcx # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: movq %xmm0, %rdx +; CHECK-64-NEXT: andq %rax, %rdx +; CHECK-64-NEXT: cmpq %rcx, %rdx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 516) ; 0x204 = "inf" + ret i1 %0 +} + +define i1 @isfinite_d(double %x) { +; CHECK-64-LABEL: isfinite_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movabsq $9218868437227405312, %rcx # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: movq %xmm0, %rdx +; CHECK-64-NEXT: andq %rax, %rdx +; CHECK-64-NEXT: cmpq %rdx, %rcx +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 504) ; 0x1f8 = "finite" + ret i1 %0 +} + +define i1 @isnormal_d(double %x) { +; CHECK-64-LABEL: isnormal_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movq %xmm0, %rcx +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $4503599627370496, %rax # imm = 0x10000000000000 +; CHECK-64-NEXT: subq %rax, %rcx +; CHECK-64-NEXT: movabsq $9214364837600034816, %rax # imm = 0x7FE0000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 264) ; 0x108 = "normal" + ret i1 %0 +} + +define i1 @issubnormal_d(double %x) { +; CHECK-64-LABEL: issubnormal_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movq %xmm0, %rcx +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $4503599627370495, %rax # imm = 0xFFFFFFFFFFFFF +; CHECK-64-NEXT: subq $1, %rcx +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setb %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 144) ; 0x90 = "subnormal" + ret i1 %0 +} + +define i1 @iszero_d(double %x) { +; CHECK-64-LABEL: iszero_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movsd {{.*#+}} xmm1 = mem[0],zero +; CHECK-64-NEXT: ucomisd %xmm1, %xmm0 +; CHECK-64-NEXT: sete %cl +; CHECK-64-NEXT: setnp %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 96) ; 0x60 = "zero" + ret i1 %0 +} + +define i1 @issignaling_d(double %x) { +; CHECK-64-LABEL: issignaling_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movabsq $9218868437227405312, %rcx # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: movq %xmm0, %rdx +; CHECK-64-NEXT: andq %rax, %rdx +; CHECK-64-NEXT: movabsq $9221120237041090560, %rax # imm = 0x7FF8000000000000 +; CHECK-64-NEXT: cmpq %rcx, %rdx +; CHECK-64-NEXT: setg %cl +; CHECK-64-NEXT: cmpq %rax, %rdx +; CHECK-64-NEXT: setl %al +; CHECK-64-NEXT: andb %cl, %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 1) ; "snan" + ret i1 %0 +} + +define i1 @isquiet_d(double %x) { +; CHECK-64-LABEL: isquiet_d: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movq %xmm0, %rcx +; CHECK-64-NEXT: andq %rax, %rcx +; CHECK-64-NEXT: movabsq $9221120237041090560, %rax # imm = 0x7FF8000000000000 +; CHECK-64-NEXT: cmpq %rax, %rcx +; CHECK-64-NEXT: setge %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 2) ; "qnan" + ret i1 %0 +} + +define i1 @isnan_d_strictfp(double %x) strictfp { +; CHECK-64-LABEL: isnan_d_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movabsq $9218868437227405312, %rcx # imm = 0x7FF0000000000000 +; CHECK-64-NEXT: movq %xmm0, %rdx +; CHECK-64-NEXT: andq %rax, %rdx +; CHECK-64-NEXT: cmpq %rcx, %rdx +; CHECK-64-NEXT: setg %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 3) ; "nan" + ret i1 %0 +} + +define i1 @iszero_d_strictfp(double %x) strictfp { +; CHECK-64-LABEL: iszero_d_strictfp: +; CHECK-64: # %bb.0: # %entry +; CHECK-64-NEXT: movabsq $9223372036854775807, %rax # imm = 0x7FFFFFFFFFFFFFFF +; CHECK-64-NEXT: movl $0, %ecx +; CHECK-64-NEXT: movq %xmm0, %rdx +; CHECK-64-NEXT: andq %rax, %rdx +; CHECK-64-NEXT: cmpq %rcx, %rdx +; CHECK-64-NEXT: sete %al +; CHECK-64-NEXT: retq +entry: + %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 96) ; 0x60 = "zero" + ret i1 %0 +} + + +declare i1 @llvm.is.fpclass.f32(float, i32) +declare i1 @llvm.is.fpclass.f64(double, i32)