[llvm] [RISCV] Transform fcmp to is.fpclass (PR #120242)

Gergely Futo via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 17 07:02:55 PST 2024


https://github.com/futog created https://github.com/llvm/llvm-project/pull/120242

The `instcombine` pass transforms some `is.fpclass` intrinsics into `fcpm` calls. If a given floating point extension is not available (F/D/Zfinx/Zfinx/Zfh/Zfhmin), these `fcmp` calls are lowered to libcalls.

In these cases, custom lowering of the `is.fpclass` intrinsics in the back-end generates more efficient code.

In the `riscv-codegenprepare` pass, these `fcmp` calls are converted back to `is.fpclass` intrinsics.

>From 0dff7fbbad04e4d711c9beb84e227c81137f2c5d Mon Sep 17 00:00:00 2001
From: Gergely Futo <gergely.futo at hightec-rt.com>
Date: Mon, 16 Dec 2024 09:01:37 +0100
Subject: [PATCH] [RISCV] Transform fcmp to is.fpclass

The `instcombine` pass transforms some `is.fpclass` intrinsics into
`fcpm` calls. If a given floating point extension is not available
(F/D/Zfinx/Zfinx/Zfh/Zfhmin), these `fcmp` calls are lowered to libcalls.

In these cases, custom lowering of the `is.fpclass` intrinsics in the
back-end generates more efficient code.

In the `riscv-codegenprepare` pass, these `fcmp` calls are converted
back to `is.fpclass` intrinsics.
---
 llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp |   39 +
 llvm/test/CodeGen/RISCV/is-fpclass-f32.ll     | 1097 +++++++++++++++++
 llvm/test/CodeGen/RISCV/is-fpclass-f64.ll     |   68 +
 .../RISCV/riscv-codegenprepare-fpclass.ll     |  307 +++++
 4 files changed, 1511 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/is-fpclass-f32.ll
 create mode 100644 llvm/test/CodeGen/RISCV/is-fpclass-f64.ll
 create mode 100644 llvm/test/CodeGen/RISCV/riscv-codegenprepare-fpclass.ll

diff --git a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
index 5be5345cca73a9..9bee2ff2590774 100644
--- a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
@@ -20,11 +20,13 @@
 #include "llvm/CodeGen/TargetPassConfig.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/InstVisitor.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/Pass.h"
+#include "llvm/Transforms/Utils/Local.h"
 
 using namespace llvm;
 
@@ -58,6 +60,7 @@ class RISCVCodeGenPrepare : public FunctionPass,
   bool visitAnd(BinaryOperator &BO);
   bool visitIntrinsicInst(IntrinsicInst &I);
   bool expandVPStrideLoad(IntrinsicInst &I);
+  bool visitFCmpInst(FCmpInst &I);
 };
 
 } // end anonymous namespace
@@ -196,6 +199,42 @@ bool RISCVCodeGenPrepare::expandVPStrideLoad(IntrinsicInst &II) {
   return true;
 }
 
+// The 'fcmp uno/ord/oeq/une/ueq/one/ogt/oge/olt/ole x, 0.0' instructions are
+// equivalent to an FP class test. If the fcmp instruction would be custom
+// lowered or lowered to a libcall, use the is.fpclass intrinsic instead, which
+// is lowered by the back-end without a libcall.
+//
+// This basically reverts the transformations of
+// InstCombinerImpl::foldIntrinsicIsFPClass.
+bool RISCVCodeGenPrepare::visitFCmpInst(FCmpInst &Fcmp) {
+  const auto *TLI = ST->getTargetLowering();
+  const EVT VT = TLI->getValueType(*DL, Fcmp.getOperand(0)->getType());
+  const int ISDOpcode = TLI->InstructionOpcodeToISD(Fcmp.getOpcode());
+
+  auto LegalizeTypeAction = TLI->getTypeAction(Fcmp.getContext(), VT);
+  auto OperationAction = TLI->getOperationAction(ISDOpcode, VT);
+  if ((LegalizeTypeAction != TargetLoweringBase::TypeSoftenFloat &&
+       LegalizeTypeAction != TargetLoweringBase::TypeSoftPromoteHalf) ||
+      OperationAction == TargetLowering::Custom)
+    return false;
+
+  auto [ClassVal, ClassTest] =
+      fcmpToClassTest(Fcmp.getPredicate(), *Fcmp.getParent()->getParent(),
+                      Fcmp.getOperand(0), Fcmp.getOperand(1));
+
+  // FIXME: For some conditions (e.g ole, olt, oge, ogt) the output is quite
+  //        verbose compared to the libcall. Should we do the tranformation
+  //        only if we are optimizing for speed?
+  if (!ClassVal)
+    return false;
+
+  IRBuilder<> Builder(&Fcmp);
+  Value *IsFPClass = Builder.createIsFPClass(ClassVal, ClassTest);
+  Fcmp.replaceAllUsesWith(IsFPClass);
+  RecursivelyDeleteTriviallyDeadInstructions(&Fcmp);
+  return true;
+}
+
 bool RISCVCodeGenPrepare::runOnFunction(Function &F) {
   if (skipFunction(F))
     return false;
diff --git a/llvm/test/CodeGen/RISCV/is-fpclass-f32.ll b/llvm/test/CodeGen/RISCV/is-fpclass-f32.ll
new file mode 100644
index 00000000000000..dd918ba7a1a8d7
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/is-fpclass-f32.ll
@@ -0,0 +1,1097 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=riscv32 -mattr=+f -verify-machineinstrs -target-abi=ilp32f < %s \
+; RUN:   | FileCheck -check-prefix=RV32IF %s
+; RUN: llc -mtriple=riscv32 -mattr=+zfinx -verify-machineinstrs -target-abi=ilp32 < %s \
+; RUN:   | FileCheck -check-prefix=RV32IZFINX %s
+; RUN: llc -mtriple=riscv32 -mattr=+d -verify-machineinstrs -target-abi=ilp32f < %s \
+; RUN:   | FileCheck -check-prefix=RV32IF %s
+; RUN: llc -mtriple=riscv64 -mattr=+f -verify-machineinstrs -target-abi=lp64f < %s \
+; RUN:   | FileCheck -check-prefix=RV64IF %s
+; RUN: llc -mtriple=riscv64 -mattr=+zfinx -verify-machineinstrs -target-abi=lp64 < %s \
+; RUN:   | FileCheck -check-prefix=RV64IZFINX %s
+; RUN: llc -mtriple=riscv64 -mattr=+d -verify-machineinstrs -target-abi=lp64d < %s \
+; RUN:   | FileCheck -check-prefix=RV64IF %s
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefix=RV64I %s
+
+declare i1 @llvm.is.fpclass.f32(float, i32)
+
+define i1 @fpclass(float %x) {
+; RV32IF-LABEL: fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 927
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 927
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 927
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 927
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a1, a0, 1
+; RV32I-NEXT:    lui a2, 2048
+; RV32I-NEXT:    slti a0, a0, 0
+; RV32I-NEXT:    lui a3, 522240
+; RV32I-NEXT:    lui a4, 1046528
+; RV32I-NEXT:    srli a1, a1, 1
+; RV32I-NEXT:    addi a2, a2, -1
+; RV32I-NEXT:    addi a5, a1, -1
+; RV32I-NEXT:    sltu a2, a5, a2
+; RV32I-NEXT:    xor a5, a1, a3
+; RV32I-NEXT:    slt a3, a3, a1
+; RV32I-NEXT:    add a4, a1, a4
+; RV32I-NEXT:    seqz a1, a1
+; RV32I-NEXT:    seqz a5, a5
+; RV32I-NEXT:    srli a4, a4, 24
+; RV32I-NEXT:    and a2, a2, a0
+; RV32I-NEXT:    or a1, a1, a5
+; RV32I-NEXT:    sltiu a4, a4, 127
+; RV32I-NEXT:    or a1, a1, a2
+; RV32I-NEXT:    or a1, a1, a3
+; RV32I-NEXT:    and a0, a4, a0
+; RV32I-NEXT:    or a0, a1, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a1, a0
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a2, 2048
+; RV64I-NEXT:    lui a3, 522240
+; RV64I-NEXT:    lui a4, 1046528
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    addiw a2, a2, -1
+; RV64I-NEXT:    slti a1, a1, 0
+; RV64I-NEXT:    addi a5, a0, -1
+; RV64I-NEXT:    sltu a2, a5, a2
+; RV64I-NEXT:    xor a5, a0, a3
+; RV64I-NEXT:    slt a3, a3, a0
+; RV64I-NEXT:    add a4, a0, a4
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    seqz a5, a5
+; RV64I-NEXT:    srliw a4, a4, 24
+; RV64I-NEXT:    and a2, a2, a1
+; RV64I-NEXT:    or a0, a0, a5
+; RV64I-NEXT:    sltiu a4, a4, 127
+; RV64I-NEXT:    or a0, a0, a2
+; RV64I-NEXT:    or a0, a0, a3
+; RV64I-NEXT:    and a1, a4, a1
+; RV64I-NEXT:    or a0, a0, a1
+; RV64I-NEXT:    ret
+  %cmp = call i1 @llvm.is.fpclass.f32(float %x, i32 639)
+  ret i1 %cmp
+}
+
+define i1 @is_nan_fpclass(float %x) {
+; RV32IF-LABEL: is_nan_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 768
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_nan_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 768
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_nan_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 768
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_nan_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 768
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_nan_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    lui a1, 522240
+; RV32I-NEXT:    slt a0, a1, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_nan_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    lui a1, 522240
+; RV64I-NEXT:    slt a0, a1, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 3)  ; nan
+  ret i1 %1
+}
+
+define i1 @is_qnan_fpclass(float %x) {
+; RV32IF-LABEL: is_qnan_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    srli a0, a0, 9
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_qnan_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    srli a0, a0, 9
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_qnan_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    srli a0, a0, 9
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_qnan_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    srli a0, a0, 9
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_qnan_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    lui a1, 523264
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    addi a1, a1, -1
+; RV32I-NEXT:    slt a0, a1, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_qnan_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a1, 523264
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    addiw a1, a1, -1
+; RV64I-NEXT:    slt a0, a1, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 2)  ; qnan
+  ret i1 %1
+}
+
+define i1 @is_snan_fpclass(float %x) {
+; RV32IF-LABEL: is_snan_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    slli a0, a0, 23
+; RV32IF-NEXT:    srli a0, a0, 31
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_snan_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    slli a0, a0, 23
+; RV32IZFINX-NEXT:    srli a0, a0, 31
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_snan_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    slli a0, a0, 55
+; RV64IF-NEXT:    srli a0, a0, 63
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_snan_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    slli a0, a0, 55
+; RV64IZFINX-NEXT:    srli a0, a0, 63
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_snan_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    lui a1, 523264
+; RV32I-NEXT:    lui a2, 522240
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    slt a1, a0, a1
+; RV32I-NEXT:    slt a0, a2, a0
+; RV32I-NEXT:    and a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_snan_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a1, 523264
+; RV64I-NEXT:    lui a2, 522240
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    slt a1, a0, a1
+; RV64I-NEXT:    slt a0, a2, a0
+; RV64I-NEXT:    and a0, a0, a1
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 1)  ; snan
+  ret i1 %1
+}
+
+define i1 @is_inf_fpclass(float %x) {
+; RV32IF-LABEL: is_inf_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 129
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_inf_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 129
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_inf_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 129
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_inf_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 129
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_inf_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    lui a1, 522240
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_inf_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    lui a1, 522240
+; RV64I-NEXT:    xor a0, a0, a1
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 516)  ; 0x204 = "inf"
+  ret i1 %1
+}
+
+define i1 @is_posinf_fpclass(float %x) {
+; RV32IF-LABEL: is_posinf_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    slli a0, a0, 24
+; RV32IF-NEXT:    srli a0, a0, 31
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_posinf_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    slli a0, a0, 24
+; RV32IZFINX-NEXT:    srli a0, a0, 31
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_posinf_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    slli a0, a0, 56
+; RV64IF-NEXT:    srli a0, a0, 63
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_posinf_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    slli a0, a0, 56
+; RV64IZFINX-NEXT:    srli a0, a0, 63
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_posinf_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 522240
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_posinf_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a0, a0
+; RV64I-NEXT:    lui a1, 522240
+; RV64I-NEXT:    xor a0, a0, a1
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 512)  ; 0x200 = "+inf"
+  ret i1 %1
+}
+
+define i1 @is_neginf_fpclass(float %x) {
+; RV32IF-LABEL: is_neginf_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 1
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_neginf_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 1
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_neginf_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 1
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_neginf_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 1
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_neginf_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 1046528
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_neginf_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a0, a0
+; RV64I-NEXT:    lui a1, 1046528
+; RV64I-NEXT:    xor a0, a0, a1
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 4)  ; "-inf"
+  ret i1 %1
+}
+
+define i1 @is_finite_fpclass(float %x) {
+; RV32IF-LABEL: is_finite_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 126
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_finite_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 126
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_finite_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 126
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_finite_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 126
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_finite_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    lui a1, 522240
+; RV32I-NEXT:    slt a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_finite_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    lui a1, 522240
+; RV64I-NEXT:    slt a0, a0, a1
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 504)  ; 0x1f8 = "finite"
+  ret i1 %1
+}
+
+define i1 @is_posfinite_fpclass(float %x) {
+; RV32IF-LABEL: is_posfinite_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 112
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_posfinite_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 112
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_posfinite_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 112
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_posfinite_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 112
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_posfinite_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    srli a0, a0, 23
+; RV32I-NEXT:    sltiu a0, a0, 255
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_posfinite_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    srliw a0, a0, 23
+; RV64I-NEXT:    sltiu a0, a0, 255
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 448)  ; 0x1c0 = "+finite"
+  ret i1 %1
+}
+
+define i1 @is_negfinite_fpclass(float %x) {
+; RV32IF-LABEL: is_negfinite_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 14
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_negfinite_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 14
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_negfinite_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 14
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_negfinite_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 14
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_negfinite_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a1, a0, 1
+; RV32I-NEXT:    lui a2, 522240
+; RV32I-NEXT:    srli a1, a1, 1
+; RV32I-NEXT:    slt a1, a1, a2
+; RV32I-NEXT:    slti a0, a0, 0
+; RV32I-NEXT:    and a0, a1, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_negfinite_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a1, a0
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a2, 522240
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    slt a0, a0, a2
+; RV64I-NEXT:    slti a1, a1, 0
+; RV64I-NEXT:    and a0, a0, a1
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 56)  ; 0x38 = "-finite"
+  ret i1 %1
+}
+
+define i1 @is_notfinite_fpclass(float %x) {
+; RV32IF-LABEL: is_notfinite_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 897
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_notfinite_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 897
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_notfinite_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 897
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_notfinite_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 897
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_notfinite_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    lui a1, 522240
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    addi a1, a1, -1
+; RV32I-NEXT:    slt a0, a1, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_notfinite_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a1, 522240
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    addiw a1, a1, -1
+; RV64I-NEXT:    slt a0, a1, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 519)  ; ox207 = "inf|nan"
+  ret i1 %1
+}
+
+define i1 @is_zero_fpclass(float %x) {
+; RV32IF-LABEL: is_zero_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 24
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_zero_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 24
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_zero_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 24
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_zero_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 24
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_zero_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_zero_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 96) ; ox60 = "zero"
+  ret i1 %1
+}
+
+define i1 @is_notzero_fpclass(float %x) {
+; RV32IF-LABEL: is_notzero_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 999
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_notzero_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 999
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_notzero_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 999
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_notzero_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 999
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_notzero_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    snez a0, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_notzero_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    snez a0, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 927) ; ox39f = "~zero"
+  ret i1 %1
+}
+
+define i1 @is_zero_nan_fpclass(float %x) {
+; RV32IF-LABEL: is_zero_nan_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 792
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_zero_nan_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 792
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_zero_nan_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 792
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_zero_nan_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 792
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_zero_nan_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    lui a1, 522240
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    slt a1, a1, a0
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_zero_nan_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a1, 522240
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    slt a1, a1, a0
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    or a0, a0, a1
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 99) ; ox63 = "zero|nan"
+  ret i1 %1
+}
+
+define i1 @is_not_zero_nan_fpclass(float %x) {
+; RV32IF-LABEL: is_not_zero_nan_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 231
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_not_zero_nan_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 231
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_not_zero_nan_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 231
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_not_zero_nan_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 231
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_not_zero_nan_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a0, a0, 1
+; RV32I-NEXT:    lui a1, 522240
+; RV32I-NEXT:    srli a0, a0, 1
+; RV32I-NEXT:    addi a1, a1, 1
+; RV32I-NEXT:    slt a1, a0, a1
+; RV32I-NEXT:    snez a0, a0
+; RV32I-NEXT:    and a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_not_zero_nan_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a1, 522240
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    addiw a1, a1, 1
+; RV64I-NEXT:    slt a1, a0, a1
+; RV64I-NEXT:    snez a0, a0
+; RV64I-NEXT:    and a0, a0, a1
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 924) ; ox39c = "~(zero|nan)"
+  ret i1 %1
+}
+
+define i1 @is_possubnormal_posnormal_posinf_fpclass(float %x) {
+; RV32IF-LABEL: is_possubnormal_posnormal_posinf_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 224
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_possubnormal_posnormal_posinf_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 224
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_possubnormal_posnormal_posinf_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 224
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_possubnormal_posnormal_posinf_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 224
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_possubnormal_posnormal_posinf_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi a1, a0, -1
+; RV32I-NEXT:    lui a2, 2048
+; RV32I-NEXT:    slli a3, a0, 1
+; RV32I-NEXT:    addi a2, a2, -1
+; RV32I-NEXT:    sltu a1, a1, a2
+; RV32I-NEXT:    lui a2, 1046528
+; RV32I-NEXT:    srli a3, a3, 1
+; RV32I-NEXT:    add a2, a3, a2
+; RV32I-NEXT:    lui a3, 522240
+; RV32I-NEXT:    xor a3, a0, a3
+; RV32I-NEXT:    slti a0, a0, 0
+; RV32I-NEXT:    seqz a3, a3
+; RV32I-NEXT:    or a1, a1, a3
+; RV32I-NEXT:    srli a2, a2, 24
+; RV32I-NEXT:    sltiu a2, a2, 127
+; RV32I-NEXT:    xori a0, a0, 1
+; RV32I-NEXT:    and a0, a2, a0
+; RV32I-NEXT:    or a0, a1, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_possubnormal_posnormal_posinf_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a1, a0
+; RV64I-NEXT:    addiw a2, a0, -1
+; RV64I-NEXT:    lui a3, 2048
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    addiw a3, a3, -1
+; RV64I-NEXT:    sltu a2, a2, a3
+; RV64I-NEXT:    lui a3, 1046528
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    add a0, a0, a3
+; RV64I-NEXT:    lui a3, 522240
+; RV64I-NEXT:    xor a3, a1, a3
+; RV64I-NEXT:    slti a1, a1, 0
+; RV64I-NEXT:    seqz a3, a3
+; RV64I-NEXT:    or a2, a2, a3
+; RV64I-NEXT:    srliw a0, a0, 24
+; RV64I-NEXT:    sltiu a0, a0, 127
+; RV64I-NEXT:    xori a1, a1, 1
+; RV64I-NEXT:    and a0, a0, a1
+; RV64I-NEXT:    or a0, a2, a0
+; RV64I-NEXT:    ret
+    %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 896) ; ox380 = "+subnormal|+normal|+inf"
+    ret i1 %1
+}
+
+define i1 @is_positive_negzero_fpclass(float %x) {
+; RV32IF-LABEL: is_positive_negzero_fpclass:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 248
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_positive_negzero_fpclass:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 248
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_positive_negzero_fpclass:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 248
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_positive_negzero_fpclass:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 248
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_positive_negzero_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi a1, a0, -1
+; RV32I-NEXT:    lui a2, 2048
+; RV32I-NEXT:    lui a3, 522240
+; RV32I-NEXT:    slli a4, a0, 1
+; RV32I-NEXT:    addi a2, a2, -1
+; RV32I-NEXT:    sltu a1, a1, a2
+; RV32I-NEXT:    lui a2, 1046528
+; RV32I-NEXT:    xor a3, a0, a3
+; RV32I-NEXT:    slti a0, a0, 0
+; RV32I-NEXT:    srli a4, a4, 1
+; RV32I-NEXT:    seqz a3, a3
+; RV32I-NEXT:    add a2, a4, a2
+; RV32I-NEXT:    seqz a4, a4
+; RV32I-NEXT:    or a3, a4, a3
+; RV32I-NEXT:    srli a2, a2, 24
+; RV32I-NEXT:    or a1, a3, a1
+; RV32I-NEXT:    sltiu a2, a2, 127
+; RV32I-NEXT:    xori a0, a0, 1
+; RV32I-NEXT:    and a0, a2, a0
+; RV32I-NEXT:    or a0, a1, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_positive_negzero_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a1, a0
+; RV64I-NEXT:    addiw a2, a0, -1
+; RV64I-NEXT:    lui a3, 2048
+; RV64I-NEXT:    lui a4, 522240
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    addiw a3, a3, -1
+; RV64I-NEXT:    sltu a2, a2, a3
+; RV64I-NEXT:    lui a3, 1046528
+; RV64I-NEXT:    xor a4, a1, a4
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    slti a1, a1, 0
+; RV64I-NEXT:    seqz a4, a4
+; RV64I-NEXT:    add a3, a0, a3
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    or a0, a0, a4
+; RV64I-NEXT:    srliw a3, a3, 24
+; RV64I-NEXT:    or a0, a0, a2
+; RV64I-NEXT:    sltiu a2, a3, 127
+; RV64I-NEXT:    xori a1, a1, 1
+; RV64I-NEXT:    and a1, a2, a1
+; RV64I-NEXT:    or a0, a0, a1
+; RV64I-NEXT:    ret
+    %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 992) ; ox3e0 = "positive|-zero"
+    ret i1 %1
+}
+
+define i1 @is_negsubnormal_negnormal_neginf(float %x) {
+; RV32IF-LABEL: is_negsubnormal_negnormal_neginf:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 7
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_negsubnormal_negnormal_neginf:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 7
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_negsubnormal_negnormal_neginf:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 7
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_negsubnormal_negnormal_neginf:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 7
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_negsubnormal_negnormal_neginf:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a1, a0, 1
+; RV32I-NEXT:    lui a2, 2048
+; RV32I-NEXT:    slti a3, a0, 0
+; RV32I-NEXT:    lui a4, 1046528
+; RV32I-NEXT:    srli a1, a1, 1
+; RV32I-NEXT:    addi a2, a2, -1
+; RV32I-NEXT:    xor a0, a0, a4
+; RV32I-NEXT:    add a4, a1, a4
+; RV32I-NEXT:    addi a1, a1, -1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    sltu a1, a1, a2
+; RV32I-NEXT:    srli a4, a4, 24
+; RV32I-NEXT:    and a1, a1, a3
+; RV32I-NEXT:    sltiu a2, a4, 127
+; RV32I-NEXT:    or a0, a1, a0
+; RV32I-NEXT:    and a2, a2, a3
+; RV32I-NEXT:    or a0, a0, a2
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_negsubnormal_negnormal_neginf:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a1, a0
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a2, 2048
+; RV64I-NEXT:    lui a3, 1046528
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    addiw a2, a2, -1
+; RV64I-NEXT:    slti a4, a1, 0
+; RV64I-NEXT:    xor a1, a1, a3
+; RV64I-NEXT:    add a3, a0, a3
+; RV64I-NEXT:    addi a0, a0, -1
+; RV64I-NEXT:    seqz a1, a1
+; RV64I-NEXT:    sltu a0, a0, a2
+; RV64I-NEXT:    srliw a2, a3, 24
+; RV64I-NEXT:    and a0, a0, a4
+; RV64I-NEXT:    sltiu a2, a2, 127
+; RV64I-NEXT:    or a0, a0, a1
+; RV64I-NEXT:    and a2, a2, a4
+; RV64I-NEXT:    or a0, a0, a2
+; RV64I-NEXT:    ret
+    %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 28) ; ox1c = "-subnormal|-normal|-inf"
+    ret i1 %1
+}
+
+define i1 @is_negative_poszero(float %x) {
+; RV32IF-LABEL: is_negative_poszero:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    fclass.s a0, fa0
+; RV32IF-NEXT:    andi a0, a0, 31
+; RV32IF-NEXT:    snez a0, a0
+; RV32IF-NEXT:    ret
+;
+; RV32IZFINX-LABEL: is_negative_poszero:
+; RV32IZFINX:       # %bb.0:
+; RV32IZFINX-NEXT:    fclass.s a0, a0
+; RV32IZFINX-NEXT:    andi a0, a0, 31
+; RV32IZFINX-NEXT:    snez a0, a0
+; RV32IZFINX-NEXT:    ret
+;
+; RV64IF-LABEL: is_negative_poszero:
+; RV64IF:       # %bb.0:
+; RV64IF-NEXT:    fclass.s a0, fa0
+; RV64IF-NEXT:    andi a0, a0, 31
+; RV64IF-NEXT:    snez a0, a0
+; RV64IF-NEXT:    ret
+;
+; RV64IZFINX-LABEL: is_negative_poszero:
+; RV64IZFINX:       # %bb.0:
+; RV64IZFINX-NEXT:    fclass.s a0, a0
+; RV64IZFINX-NEXT:    andi a0, a0, 31
+; RV64IZFINX-NEXT:    snez a0, a0
+; RV64IZFINX-NEXT:    ret
+;
+; RV32I-LABEL: is_negative_poszero:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a1, a0, 1
+; RV32I-NEXT:    lui a2, 2048
+; RV32I-NEXT:    slti a3, a0, 0
+; RV32I-NEXT:    lui a4, 1046528
+; RV32I-NEXT:    srli a1, a1, 1
+; RV32I-NEXT:    addi a2, a2, -1
+; RV32I-NEXT:    xor a0, a0, a4
+; RV32I-NEXT:    addi a5, a1, -1
+; RV32I-NEXT:    add a4, a1, a4
+; RV32I-NEXT:    seqz a1, a1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    sltu a2, a5, a2
+; RV32I-NEXT:    or a0, a1, a0
+; RV32I-NEXT:    srli a4, a4, 24
+; RV32I-NEXT:    and a2, a2, a3
+; RV32I-NEXT:    sltiu a1, a4, 127
+; RV32I-NEXT:    or a0, a0, a2
+; RV32I-NEXT:    and a1, a1, a3
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: is_negative_poszero:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    sext.w a1, a0
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    lui a2, 2048
+; RV64I-NEXT:    lui a3, 1046528
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    addiw a2, a2, -1
+; RV64I-NEXT:    slti a4, a1, 0
+; RV64I-NEXT:    xor a1, a1, a3
+; RV64I-NEXT:    addi a5, a0, -1
+; RV64I-NEXT:    add a3, a0, a3
+; RV64I-NEXT:    seqz a0, a0
+; RV64I-NEXT:    seqz a1, a1
+; RV64I-NEXT:    sltu a2, a5, a2
+; RV64I-NEXT:    or a0, a0, a1
+; RV64I-NEXT:    srliw a1, a3, 24
+; RV64I-NEXT:    and a2, a2, a4
+; RV64I-NEXT:    sltiu a1, a1, 127
+; RV64I-NEXT:    or a0, a0, a2
+; RV64I-NEXT:    and a1, a1, a4
+; RV64I-NEXT:    or a0, a0, a1
+; RV64I-NEXT:    ret
+    %1 = call i1 @llvm.is.fpclass.f32(float %x, i32 124) ; ox1c = "negative|+zero"
+    ret i1 %1
+}
diff --git a/llvm/test/CodeGen/RISCV/is-fpclass-f64.ll b/llvm/test/CodeGen/RISCV/is-fpclass-f64.ll
new file mode 100644
index 00000000000000..8ce041eb3b6589
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/is-fpclass-f64.ll
@@ -0,0 +1,68 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=riscv32 -mattr=+d -verify-machineinstrs -target-abi=ilp32d < %s \
+; RUN:   | FileCheck -check-prefixes=RV32IFD %s
+; RUN: llc -mtriple=riscv64 -mattr=+d -verify-machineinstrs -target-abi=lp64d < %s \
+; RUN:   | FileCheck -check-prefixes=RV64IFD %s
+; RUN: llc -mtriple=riscv32 -mattr=+zdinx -verify-machineinstrs -target-abi=ilp32 < %s \
+; RUN:   | FileCheck -check-prefix=RV32IZFINXZDINX %s
+; RUN: llc -mtriple=riscv64 -mattr=+zdinx -verify-machineinstrs -target-abi=lp64 < %s\
+; RUN:   | FileCheck -check-prefix=RV64IZFINXZDINX %s
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck -check-prefix=RV64I %s
+
+
+declare i1 @llvm.is.fpclass.f64(double, i32)
+
+define i1 @isnan_d_fpclass(double %x) {
+; RV32IFD-LABEL: isnan_d_fpclass:
+; RV32IFD:       # %bb.0:
+; RV32IFD-NEXT:    fclass.d a0, fa0
+; RV32IFD-NEXT:    andi a0, a0, 768
+; RV32IFD-NEXT:    snez a0, a0
+; RV32IFD-NEXT:    ret
+;
+; RV64IFD-LABEL: isnan_d_fpclass:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    fclass.d a0, fa0
+; RV64IFD-NEXT:    andi a0, a0, 768
+; RV64IFD-NEXT:    snez a0, a0
+; RV64IFD-NEXT:    ret
+;
+; RV32IZFINXZDINX-LABEL: isnan_d_fpclass:
+; RV32IZFINXZDINX:       # %bb.0:
+; RV32IZFINXZDINX-NEXT:    fclass.d a0, a0
+; RV32IZFINXZDINX-NEXT:    andi a0, a0, 768
+; RV32IZFINXZDINX-NEXT:    snez a0, a0
+; RV32IZFINXZDINX-NEXT:    ret
+;
+; RV64IZFINXZDINX-LABEL: isnan_d_fpclass:
+; RV64IZFINXZDINX:       # %bb.0:
+; RV64IZFINXZDINX-NEXT:    fclass.d a0, a0
+; RV64IZFINXZDINX-NEXT:    andi a0, a0, 768
+; RV64IZFINXZDINX-NEXT:    snez a0, a0
+; RV64IZFINXZDINX-NEXT:    ret
+;
+; RV32I-LABEL: isnan_d_fpclass:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    slli a1, a1, 1
+; RV32I-NEXT:    srli a1, a1, 1
+; RV32I-NEXT:    lui a2, 524032
+; RV32I-NEXT:    beq a1, a2, .LBB0_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    slt a0, a2, a1
+; RV32I-NEXT:    ret
+; RV32I-NEXT:  .LBB0_2:
+; RV32I-NEXT:    snez a0, a0
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: isnan_d_fpclass:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 1
+; RV64I-NEXT:    li a1, 2047
+; RV64I-NEXT:    srli a0, a0, 1
+; RV64I-NEXT:    slli a1, a1, 52
+; RV64I-NEXT:    slt a0, a1, a0
+; RV64I-NEXT:    ret
+  %1 = call i1 @llvm.is.fpclass.f64(double %x, i32 3)  ; nan
+  ret i1 %1
+}
diff --git a/llvm/test/CodeGen/RISCV/riscv-codegenprepare-fpclass.ll b/llvm/test/CodeGen/RISCV/riscv-codegenprepare-fpclass.ll
new file mode 100644
index 00000000000000..d4728e6cf8a5e1
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/riscv-codegenprepare-fpclass.ll
@@ -0,0 +1,307 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -riscv-codegenprepare -S -mtriple=riscv32 -mattr=-f,-d          < %s | FileCheck %s --check-prefixes=RV32I
+; RUN: opt -riscv-codegenprepare -S -mtriple=riscv32 -mattr=+f,-d          < %s | FileCheck %s --check-prefixes=RV32IF
+; RUN: opt -riscv-codegenprepare -S -mtriple=riscv32 -mattr=+zfinx,-zdinx  < %s | FileCheck %s --check-prefixes=RV32IZFINX
+
+; RUN: opt -riscv-codegenprepare -S -mtriple=riscv32 -mattr=+d             < %s | FileCheck %s --check-prefixes=RV32ID
+; RUN: opt -riscv-codegenprepare -S -mtriple=riscv32 -mattr=+zdinx         < %s | FileCheck %s --check-prefixes=RV32IZDINX
+
+define i1 @test_is_nan(double %arg) {
+; RV32I-LABEL: define i1 @test_is_nan(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 3)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_is_nan(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 3)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_is_nan(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 3)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_is_nan(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp uno double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_is_nan(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp uno double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp uno double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_not_is_nan(double %arg) {
+; RV32I-LABEL: define i1 @test_not_is_nan(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 1020)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_not_is_nan(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 1020)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_not_is_nan(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 1020)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_not_is_nan(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp ord double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_not_is_nan(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp ord double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp ord double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_is_zero(double %arg) {
+; RV32I-LABEL: define i1 @test_is_zero(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 96)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_is_zero(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 96)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_is_zero(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 96)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_is_zero(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp oeq double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_is_zero(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp oeq double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp oeq double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_not_is_zero(double %arg) {
+; RV32I-LABEL: define i1 @test_not_is_zero(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 927)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_not_is_zero(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 927)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_not_is_zero(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 927)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_not_is_zero(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp une double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_not_is_zero(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp une double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp une double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_is_zero_or_nan(double %arg) {
+; RV32I-LABEL: define i1 @test_is_zero_or_nan(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 99)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_is_zero_or_nan(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 99)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_is_zero_or_nan(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 99)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_is_zero_or_nan(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp ueq double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_is_zero_or_nan(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp ueq double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp ueq double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_not_is_zero_or_nan(double %arg) {
+; RV32I-LABEL: define i1 @test_not_is_zero_or_nan(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 924)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_not_is_zero_or_nan(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 924)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_not_is_zero_or_nan(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 924)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_not_is_zero_or_nan(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp one double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_not_is_zero_or_nan(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp one double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp one double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_is_possubnormal_or_posnormal_or_posinf(double %arg) {
+; RV32I-LABEL: define i1 @test_is_possubnormal_or_posnormal_or_posinf(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 896)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_is_possubnormal_or_posnormal_or_posinf(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 896)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_is_possubnormal_or_posnormal_or_posinf(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 896)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_is_possubnormal_or_posnormal_or_posinf(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp ogt double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_is_possubnormal_or_posnormal_or_posinf(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp ogt double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp ogt double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_is_positive_or_negzero(double %arg) {
+; RV32I-LABEL: define i1 @test_is_positive_or_negzero(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 992)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_is_positive_or_negzero(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 992)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_is_positive_or_negzero(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 992)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_is_positive_or_negzero(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp oge double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_is_positive_or_negzero(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp oge double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp oge double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_is_negsubnormal_or_negnormal_or_neginf(double %arg) {
+; RV32I-LABEL: define i1 @test_is_negsubnormal_or_negnormal_or_neginf(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 28)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_is_negsubnormal_or_negnormal_or_neginf(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 28)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_is_negsubnormal_or_negnormal_or_neginf(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 28)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_is_negsubnormal_or_negnormal_or_neginf(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp olt double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_is_negsubnormal_or_negnormal_or_neginf(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp olt double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp olt double %arg, 0.000000e+00
+  ret i1 %1
+}
+
+define i1 @test_is_negative_or_poszero(double %arg) {
+; RV32I-LABEL: define i1 @test_is_negative_or_poszero(
+; RV32I-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32I-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 124)
+; RV32I-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IF-LABEL: define i1 @test_is_negative_or_poszero(
+; RV32IF-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IF-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 124)
+; RV32IF-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZFINX-LABEL: define i1 @test_is_negative_or_poszero(
+; RV32IZFINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZFINX-NEXT:    [[TMP1:%.*]] = call i1 @llvm.is.fpclass.f64(double [[ARG]], i32 124)
+; RV32IZFINX-NEXT:    ret i1 [[TMP1]]
+;
+; RV32ID-LABEL: define i1 @test_is_negative_or_poszero(
+; RV32ID-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32ID-NEXT:    [[TMP1:%.*]] = fcmp ole double [[ARG]], 0.000000e+00
+; RV32ID-NEXT:    ret i1 [[TMP1]]
+;
+; RV32IZDINX-LABEL: define i1 @test_is_negative_or_poszero(
+; RV32IZDINX-SAME: double [[ARG:%.*]]) #[[ATTR0]] {
+; RV32IZDINX-NEXT:    [[TMP1:%.*]] = fcmp ole double [[ARG]], 0.000000e+00
+; RV32IZDINX-NEXT:    ret i1 [[TMP1]]
+;
+  %1 = fcmp ole double %arg, 0.000000e+00
+  ret i1 %1
+}



More information about the llvm-commits mailing list