[llvm] [DAG] Add basic ISD::IS_FPCLASS constant/identity folds (PR #189944)

Simon Pilgrim via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 1 05:38:20 PDT 2026


https://github.com/RKSimon updated https://github.com/llvm/llvm-project/pull/189944

>From 1c979c0a00fae5d9c3b0ccec2b2d1e54289b9a6b Mon Sep 17 00:00:00 2001
From: Simon Pilgrim <llvm-dev at redking.me.uk>
Date: Wed, 1 Apr 2026 12:48:21 +0100
Subject: [PATCH 1/4] [DAG] Add basic ISD::IS_FPCLASS constant/identity folds

Attempts to match middle-end implementation in InstructionSimplify/foldIntrinsicIsFPClass

Fixes #189919
---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 28 +++++++++++++++++++
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 12 ++++++++
 llvm/test/CodeGen/RISCV/combine-is_fpclass.ll | 18 +++---------
 3 files changed, 44 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 6ad5df3d3272c..1d485ed6060ba 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -489,6 +489,7 @@ namespace {
     SDValue visitANY_EXTEND(SDNode *N);
     SDValue visitAssertExt(SDNode *N);
     SDValue visitAssertAlign(SDNode *N);
+    SDValue visitIS_FPCLASS(SDNode *N);
     SDValue visitSIGN_EXTEND_INREG(SDNode *N);
     SDValue visitEXTEND_VECTOR_INREG(SDNode *N);
     SDValue visitTRUNCATE(SDNode *N);
@@ -2009,6 +2010,7 @@ SDValue DAGCombiner::visit(SDNode *N) {
   case ISD::AssertSext:
   case ISD::AssertZext:         return visitAssertExt(N);
   case ISD::AssertAlign:        return visitAssertAlign(N);
+  case ISD::IS_FPCLASS:         return visitIS_FPCLASS(N);
   case ISD::SIGN_EXTEND_INREG:  return visitSIGN_EXTEND_INREG(N);
   case ISD::SIGN_EXTEND_VECTOR_INREG:
   case ISD::ZERO_EXTEND_VECTOR_INREG:
@@ -16012,6 +16014,32 @@ SDValue DAGCombiner::visitAssertAlign(SDNode *N) {
   return SDValue();
 }
 
+SDValue DAGCombiner::visitIS_FPCLASS(SDNode *N) {
+  SDValue Src = N->getOperand(0);
+  FPClassTest Mask = static_cast<FPClassTest>(N->getConstantOperandVal(1));
+  EVT VT = N->getValueType(0);
+  SDLoc DL(N);
+
+  KnownFPClass Known = DAG.computeKnownFPClass(Src, Mask);
+
+  // Clear test bits we know must be false from the source value.
+  // fp_class (nnan x), qnan|snan|other -> fp_class (nnan x), other
+  // fp_class (ninf x), ninf|pinf|other -> fp_class (ninf x), other
+  if ((Mask & Known.KnownFPClasses) != Mask)
+    return DAG.getNode(
+        ISD::IS_FPCLASS, DL, VT, Src,
+        DAG.getTargetConstant(Mask & Known.KnownFPClasses, DL, MVT::i32),
+        N->getFlags());
+
+  // If none of the tests which can return false are possible, fold to true.
+  // fp_class (nnan x), ~(qnan|snan) -> true
+  // fp_class (ninf x), ~(ninf|pinf) -> true
+  if (Mask == Known.KnownFPClasses)
+    return DAG.getBoolConstant(true, DL, VT, Src.getValueType());
+
+  return SDValue();
+}
+
 /// If the result of a load is shifted/masked/truncated to an effectively
 /// narrower type, try to transform the load to a narrower type and/or
 /// use an extending load.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 3716de880cce3..f60a006c51e82 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -8290,6 +8290,18 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, EVT VT,
            N2.getOpcode() == ISD::TargetConstant && "Invalid FP_ROUND!");
     if (N1.getValueType() == VT) return N1;  // noop conversion.
     break;
+  case ISD::IS_FPCLASS: {
+    assert(N1.getValueType().isFloatingPoint() &&
+           "IS_FPCLASS is used for a non-floating type");
+    assert(isa<ConstantSDNode>(N2) && "FPClassTest is not Constant");
+    FPClassTest Mask = static_cast<FPClassTest>(N2->getAsZExtVal());
+    // If all tests are made, it doesn't matter what the value is.
+    if ((Mask & fcAllFlags) == fcAllFlags)
+      return getBoolConstant(true, DL, VT, N1.getValueType());
+    if ((Mask & fcAllFlags) == 0)
+      return getBoolConstant(false, DL, VT, N1.getValueType());
+    break;
+  }
   case ISD::AssertNoFPClass: {
     assert(N1.getValueType().isFloatingPoint() &&
            "AssertNoFPClass is used for a non-floating type");
diff --git a/llvm/test/CodeGen/RISCV/combine-is_fpclass.ll b/llvm/test/CodeGen/RISCV/combine-is_fpclass.ll
index 07c22ecf130d0..0292df415a655 100644
--- a/llvm/test/CodeGen/RISCV/combine-is_fpclass.ll
+++ b/llvm/test/CodeGen/RISCV/combine-is_fpclass.ll
@@ -4,10 +4,7 @@
 define i1 @isneginf_constant_f32() nounwind {
 ; CHECK-LABEL: isneginf_constant_f32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    lui a0, 260096
-; CHECK-NEXT:    fmv.w.x fa5, a0
-; CHECK-NEXT:    fclass.s a0, fa5
-; CHECK-NEXT:    andi a0, a0, 1
+; CHECK-NEXT:    li a0, 0
 ; CHECK-NEXT:    ret
   %f = tail call i1 @llvm.is.fpclass.f32(float 1.0, i32 4)  ; 0x4 = "neginf"
   ret i1 %f
@@ -16,16 +13,9 @@ define i1 @isneginf_constant_f32() nounwind {
 define i8 @iszero_constant_v4f32() nounwind {
 ; CHECK-LABEL: iszero_constant_v4f32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    lui a0, %hi(.LCPI1_0)
-; CHECK-NEXT:    addi a0, a0, %lo(.LCPI1_0)
-; CHECK-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
-; CHECK-NEXT:    vle32.v v8, (a0)
-; CHECK-NEXT:    vfclass.v v8, v8
-; CHECK-NEXT:    li a0, 24
-; CHECK-NEXT:    vand.vx v8, v8, a0
-; CHECK-NEXT:    vmsne.vi v10, v8, 0
-; CHECK-NEXT:    vsetvli zero, zero, e8, mf2, ta, ma
-; CHECK-NEXT:    vmv.x.s a0, v10
+; CHECK-NEXT:    vsetivli zero, 8, e8, mf2, ta, ma
+; CHECK-NEXT:    vmclr.m v8
+; CHECK-NEXT:    vmv.x.s a0, v8
 ; CHECK-NEXT:    ret
   %f = tail call <8 x i1> @llvm.is.fpclass.v4f32(<8 x float> <float 1.0, float 2.0, float 3.0, float 4.0, float -1.0, float -2.0, float -3.0, float -4.0>, i32 96)  ; 0x60 = "zero"
   %r = bitcast <8 x i1> %f to i8

>From d73086a45755703537226836532f194ad9e7f3c2 Mon Sep 17 00:00:00 2001
From: Simon Pilgrim <llvm-dev at redking.me.uk>
Date: Wed, 1 Apr 2026 13:08:05 +0100
Subject: [PATCH 2/4] visitIS_FPCLASS - remove folding to true until we have
 test coverage

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 1d485ed6060ba..664868d9d11c5 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -16031,12 +16031,6 @@ SDValue DAGCombiner::visitIS_FPCLASS(SDNode *N) {
         DAG.getTargetConstant(Mask & Known.KnownFPClasses, DL, MVT::i32),
         N->getFlags());
 
-  // If none of the tests which can return false are possible, fold to true.
-  // fp_class (nnan x), ~(qnan|snan) -> true
-  // fp_class (ninf x), ~(ninf|pinf) -> true
-  if (Mask == Known.KnownFPClasses)
-    return DAG.getBoolConstant(true, DL, VT, Src.getValueType());
-
   return SDValue();
 }
 

>From 753aaf444a1ae06cf54cd2e6a7a19b123e1f2002 Mon Sep 17 00:00:00 2001
From: Simon Pilgrim <llvm-dev at redking.me.uk>
Date: Wed, 1 Apr 2026 13:16:52 +0100
Subject: [PATCH 3/4] Regenerate LoongArch isnon/isany fpclass tests

---
 llvm/test/CodeGen/LoongArch/is_fpclass_f32.ll | 10 ++--------
 llvm/test/CodeGen/LoongArch/is_fpclass_f64.ll | 20 ++++---------------
 2 files changed, 6 insertions(+), 24 deletions(-)

diff --git a/llvm/test/CodeGen/LoongArch/is_fpclass_f32.ll b/llvm/test/CodeGen/LoongArch/is_fpclass_f32.ll
index af128fa52e1d2..fdbea762a1111 100644
--- a/llvm/test/CodeGen/LoongArch/is_fpclass_f32.ll
+++ b/llvm/test/CodeGen/LoongArch/is_fpclass_f32.ll
@@ -447,10 +447,7 @@ entry:
 define i1 @isnone_f(float %x) {
 ; CHECK-LABEL: isnone_f:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    fclass.s $fa0, $fa0
-; CHECK-NEXT:    movfr2gr.s $a0, $fa0
-; CHECK-NEXT:    andi $a0, $a0, 0
-; CHECK-NEXT:    sltu $a0, $zero, $a0
+; CHECK-NEXT:    move $a0, $zero
 ; CHECK-NEXT:    ret
 entry:
   %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 0)
@@ -460,10 +457,7 @@ entry:
 define i1 @isany_f(float %x) {
 ; CHECK-LABEL: isany_f:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    fclass.s $fa0, $fa0
-; CHECK-NEXT:    movfr2gr.s $a0, $fa0
-; CHECK-NEXT:    andi $a0, $a0, 1023
-; CHECK-NEXT:    sltu $a0, $zero, $a0
+; CHECK-NEXT:    ori $a0, $zero, 1
 ; CHECK-NEXT:    ret
 entry:
   %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 1023)
diff --git a/llvm/test/CodeGen/LoongArch/is_fpclass_f64.ll b/llvm/test/CodeGen/LoongArch/is_fpclass_f64.ll
index e58f81a08e4c5..d10e21ccbf705 100644
--- a/llvm/test/CodeGen/LoongArch/is_fpclass_f64.ll
+++ b/llvm/test/CodeGen/LoongArch/is_fpclass_f64.ll
@@ -719,18 +719,12 @@ entry:
 define i1 @isnone_d(double %x) {
 ; CHECK32-LABEL: isnone_d:
 ; CHECK32:       # %bb.0: # %entry
-; CHECK32-NEXT:    fclass.d $fa0, $fa0
-; CHECK32-NEXT:    movfr2gr.s $a0, $fa0
-; CHECK32-NEXT:    andi $a0, $a0, 0
-; CHECK32-NEXT:    sltu $a0, $zero, $a0
+; CHECK32-NEXT:    move $a0, $zero
 ; CHECK32-NEXT:    ret
 ;
 ; CHECK64-LABEL: isnone_d:
 ; CHECK64:       # %bb.0: # %entry
-; CHECK64-NEXT:    fclass.d $fa0, $fa0
-; CHECK64-NEXT:    movfr2gr.d $a0, $fa0
-; CHECK64-NEXT:    andi $a0, $a0, 0
-; CHECK64-NEXT:    sltu $a0, $zero, $a0
+; CHECK64-NEXT:    move $a0, $zero
 ; CHECK64-NEXT:    ret
 entry:
   %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 0)
@@ -740,18 +734,12 @@ entry:
 define i1 @isany_d(double %x) {
 ; CHECK32-LABEL: isany_d:
 ; CHECK32:       # %bb.0: # %entry
-; CHECK32-NEXT:    fclass.d $fa0, $fa0
-; CHECK32-NEXT:    movfr2gr.s $a0, $fa0
-; CHECK32-NEXT:    andi $a0, $a0, 1023
-; CHECK32-NEXT:    sltu $a0, $zero, $a0
+; CHECK32-NEXT:    ori $a0, $zero, 1
 ; CHECK32-NEXT:    ret
 ;
 ; CHECK64-LABEL: isany_d:
 ; CHECK64:       # %bb.0: # %entry
-; CHECK64-NEXT:    fclass.d $fa0, $fa0
-; CHECK64-NEXT:    movfr2gr.d $a0, $fa0
-; CHECK64-NEXT:    andi $a0, $a0, 1023
-; CHECK64-NEXT:    sltu $a0, $zero, $a0
+; CHECK64-NEXT:    ori $a0, $zero, 1
 ; CHECK64-NEXT:    ret
 entry:
   %0 = tail call i1 @llvm.is.fpclass.f64(double %x, i32 1023)

>From f7f519f47a489b55fe84f3359d5f815232395ab3 Mon Sep 17 00:00:00 2001
From: Simon Pilgrim <llvm-dev at redking.me.uk>
Date: Wed, 1 Apr 2026 13:38:03 +0100
Subject: [PATCH 4/4] braces

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 664868d9d11c5..dbca440a0d199 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -16025,11 +16025,12 @@ SDValue DAGCombiner::visitIS_FPCLASS(SDNode *N) {
   // Clear test bits we know must be false from the source value.
   // fp_class (nnan x), qnan|snan|other -> fp_class (nnan x), other
   // fp_class (ninf x), ninf|pinf|other -> fp_class (ninf x), other
-  if ((Mask & Known.KnownFPClasses) != Mask)
+  if ((Mask & Known.KnownFPClasses) != Mask) {
     return DAG.getNode(
         ISD::IS_FPCLASS, DL, VT, Src,
         DAG.getTargetConstant(Mask & Known.KnownFPClasses, DL, MVT::i32),
         N->getFlags());
+  }
 
   return SDValue();
 }



More information about the llvm-commits mailing list