[llvm] [SelectionDAG] Use `KnownBits` to determine if an operand may be NaN. (PR #188606)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 1 10:53:27 PDT 2026


https://github.com/zGoldthorpe updated https://github.com/llvm/llvm-project/pull/188606

>From f0fb561bb4e75fa1b5733359923f28d6fad08483 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Wed, 25 Mar 2026 15:37:09 -0500
Subject: [PATCH 01/14] [SelectionDAG] Use `KnownBits` to determine if an
 operand may be NaN.

---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 23 +++++++++++++++++++
 .../CodeGen/AMDGPU/fneg-modifier-casting.ll   |  4 ++--
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 028c9955a6bb1..eaf24fa8bdf06 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6193,6 +6193,29 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
                                                Depth);
     }
 
+    // Try to infer NaN from known bits, but only for detecting signaling or
+    // nonsignaling NaNs
+    if (!SNaN) {
+      EVT VT = Op.getValueType().getScalarType();
+      const unsigned Mantissa = VT == MVT::f16    ? 10
+                                : VT == MVT::f32  ? 23
+                                : VT == MVT::f64  ? 52
+                                : VT == MVT::f128 ? 112
+                                                  : 0;
+      const unsigned Exponent = VT == MVT::f16    ? 5
+                                : VT == MVT::f32  ? 8
+                                : VT == MVT::f64  ? 11
+                                : VT == MVT::f128 ? 15
+                                                  : 0;
+
+      if (Mantissa) {
+        KnownBits Known = computeKnownBits(Op, DemandedElts);
+        KnownBits KnownMan = Known.extractBits(Mantissa, 0);
+        KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
+        if (!KnownExp.getMaxValue().isAllOnes() || KnownMan.isZero())
+          return true;
+      }
+    }
     return false;
   }
 }
diff --git a/llvm/test/CodeGen/AMDGPU/fneg-modifier-casting.ll b/llvm/test/CodeGen/AMDGPU/fneg-modifier-casting.ll
index 9b44acd5c0716..64431cb31ea6e 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-modifier-casting.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-modifier-casting.ll
@@ -1680,7 +1680,7 @@ define amdgpu_kernel void @fnge_select_f32_multi_use_regression(float %.i2369) {
 ; GCN-NEXT:    s_waitcnt lgkmcnt(0)
 ; GCN-NEXT:    v_cmp_nlt_f32_e64 s[0:1], s0, 0
 ; GCN-NEXT:    v_cndmask_b32_e64 v0, 0, 1, s[0:1]
-; GCN-NEXT:    v_cmp_nge_f32_e32 vcc, 0, v0
+; GCN-NEXT:    v_cmp_lt_f32_e32 vcc, 0, v0
 ; GCN-NEXT:    v_cndmask_b32_e32 v1, 0, v0, vcc
 ; GCN-NEXT:    v_mul_f32_e64 v0, -v0, v1
 ; GCN-NEXT:    v_cmp_le_f32_e32 vcc, 0, v0
@@ -1694,7 +1694,7 @@ define amdgpu_kernel void @fnge_select_f32_multi_use_regression(float %.i2369) {
 ; GFX11-NEXT:    v_cmp_nlt_f32_e64 s0, s0, 0
 ; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX11-NEXT:    v_cndmask_b32_e64 v0, 0, 1, s0
-; GFX11-NEXT:    v_cmp_nge_f32_e32 vcc_lo, 0, v0
+; GFX11-NEXT:    v_cmp_lt_f32_e32 vcc_lo, 0, v0
 ; GFX11-NEXT:    v_cndmask_b32_e32 v1, 0, v0, vcc_lo
 ; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
 ; GFX11-NEXT:    v_mul_f32_e64 v0, -v0, v1

>From fcfd5a757ab1f3e64c4a68d92e99642b3c242107 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Wed, 25 Mar 2026 16:43:52 -0500
Subject: [PATCH 02/14] Use `fltSemantics`

---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 40 +++++++++++--------
 1 file changed, 24 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index eaf24fa8bdf06..bb76e08894e20 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6196,24 +6196,32 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
     // Try to infer NaN from known bits, but only for detecting signaling or
     // nonsignaling NaNs
     if (!SNaN) {
-      EVT VT = Op.getValueType().getScalarType();
-      const unsigned Mantissa = VT == MVT::f16    ? 10
-                                : VT == MVT::f32  ? 23
-                                : VT == MVT::f64  ? 52
-                                : VT == MVT::f128 ? 112
-                                                  : 0;
-      const unsigned Exponent = VT == MVT::f16    ? 5
-                                : VT == MVT::f32  ? 8
-                                : VT == MVT::f64  ? 11
-                                : VT == MVT::f128 ? 15
-                                                  : 0;
-
-      if (Mantissa) {
-        KnownBits Known = computeKnownBits(Op, DemandedElts);
-        KnownBits KnownMan = Known.extractBits(Mantissa, 0);
-        KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
+      const fltSemantics &FltSem = Op.getValueType().getFltSemantics();
+      const KnownBits Known = computeKnownBits(Op, DemandedElts);
+      const unsigned Mantissa = FltSem.precision - 1;
+      const unsigned Exponent = FltSem.sizeInBits - FltSem.precision;
+      const KnownBits KnownMan = Known.extractBits(Mantissa, 0);
+      const KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
+
+      switch (FltSem.nanEncoding) {
+      default:
+        break;
+      case fltNanEncoding::IEEE: {
         if (!KnownExp.getMaxValue().isAllOnes() || KnownMan.isZero())
           return true;
+        break;
+      }
+      case fltNanEncoding::AllOnes: {
+        if (!KnownExp.getMaxValue().isAllOnes() ||
+            !KnownMan.getMaxValue().isAllOnes())
+          return true;
+        break;
+      }
+      case fltNanEncoding::NegativeZero:
+        if (Known.Zero.isSignBitSet() || !KnownExp.isZero() ||
+            !KnownMan.isZero())
+          return true;
+        break;
       }
     }
     return false;

>From e427b07c5e1422108478cbf57dc84a61715861ad Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Wed, 25 Mar 2026 17:34:12 -0500
Subject: [PATCH 03/14] Remove unnecessary `default` block.

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

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index bb76e08894e20..872efa20c3587 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6204,8 +6204,6 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
       const KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
 
       switch (FltSem.nanEncoding) {
-      default:
-        break;
       case fltNanEncoding::IEEE: {
         if (!KnownExp.getMaxValue().isAllOnes() || KnownMan.isZero())
           return true;

>From f45aaee4ecf5b8323d1370290f32fce601b24980 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Thu, 26 Mar 2026 09:45:23 -0500
Subject: [PATCH 04/14] Added guards and removed consts.

---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 53 ++++++++++---------
 1 file changed, 28 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 872efa20c3587..53cee1970f739 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6195,31 +6195,34 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
 
     // Try to infer NaN from known bits, but only for detecting signaling or
     // nonsignaling NaNs
-    if (!SNaN) {
-      const fltSemantics &FltSem = Op.getValueType().getFltSemantics();
-      const KnownBits Known = computeKnownBits(Op, DemandedElts);
-      const unsigned Mantissa = FltSem.precision - 1;
-      const unsigned Exponent = FltSem.sizeInBits - FltSem.precision;
-      const KnownBits KnownMan = Known.extractBits(Mantissa, 0);
-      const KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
-
-      switch (FltSem.nanEncoding) {
-      case fltNanEncoding::IEEE: {
-        if (!KnownExp.getMaxValue().isAllOnes() || KnownMan.isZero())
-          return true;
-        break;
-      }
-      case fltNanEncoding::AllOnes: {
-        if (!KnownExp.getMaxValue().isAllOnes() ||
-            !KnownMan.getMaxValue().isAllOnes())
-          return true;
-        break;
-      }
-      case fltNanEncoding::NegativeZero:
-        if (Known.Zero.isSignBitSet() || !KnownExp.isZero() ||
-            !KnownMan.isZero())
-          return true;
-        break;
+    EVT VT = Op.getValueType();
+    if (!SNaN && VT.isFloatingPoint()) {
+      const fltSemantics &FltSem = VT.getFltSemantics();
+      if (FltSem.precision > 0) {
+        KnownBits Known = computeKnownBits(Op, DemandedElts);
+        unsigned Mantissa = FltSem.precision - 1;
+        unsigned Exponent = FltSem.sizeInBits - FltSem.precision;
+        KnownBits KnownMan = Known.extractBits(Mantissa, 0);
+        KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
+
+        switch (FltSem.nanEncoding) {
+        case fltNanEncoding::IEEE: {
+          if (!KnownExp.getMaxValue().isAllOnes() || KnownMan.isZero())
+            return true;
+          break;
+        }
+        case fltNanEncoding::AllOnes: {
+          if (!KnownExp.getMaxValue().isAllOnes() ||
+              !KnownMan.getMaxValue().isAllOnes())
+            return true;
+          break;
+        }
+        case fltNanEncoding::NegativeZero:
+          if (Known.Zero.isSignBitSet() || !KnownExp.isZero() ||
+              !KnownMan.isZero())
+            return true;
+          break;
+        }
       }
     }
     return false;

>From 9992ad8298ea442ecece1a8282b4f60ab13570c9 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Thu, 26 Mar 2026 09:53:17 -0500
Subject: [PATCH 05/14] Remove unnecessary parentheses.

---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 53cee1970f739..bbe8cd5fc41af 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6206,17 +6206,15 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
         KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
 
         switch (FltSem.nanEncoding) {
-        case fltNanEncoding::IEEE: {
+        case fltNanEncoding::IEEE:
           if (!KnownExp.getMaxValue().isAllOnes() || KnownMan.isZero())
             return true;
           break;
-        }
-        case fltNanEncoding::AllOnes: {
+        case fltNanEncoding::AllOnes:
           if (!KnownExp.getMaxValue().isAllOnes() ||
               !KnownMan.getMaxValue().isAllOnes())
             return true;
           break;
-        }
         case fltNanEncoding::NegativeZero:
           if (Known.Zero.isSignBitSet() || !KnownExp.isZero() ||
               !KnownMan.isZero())

>From 6e602684db39f3744f93aba36b89a3edaab15a9e Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Thu, 26 Mar 2026 11:02:24 -0500
Subject: [PATCH 06/14] Corrected `NegativeZero` logic error.

---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index bbe8cd5fc41af..d3895c447182e 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6216,8 +6216,8 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
             return true;
           break;
         case fltNanEncoding::NegativeZero:
-          if (Known.Zero.isSignBitSet() || !KnownExp.isZero() ||
-              !KnownMan.isZero())
+          if (Known.Zero.isSignBitSet() || !KnownExp.getMinValue().isZero() ||
+              !KnownMan.getMinValue().isZero())
             return true;
           break;
         }

>From cafb465f538c6cf0313f9677ef057aba80c0aa6f Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Mon, 30 Mar 2026 13:50:07 -0500
Subject: [PATCH 07/14] Moved `KnownBits` usage into `ISD::BITCAST`

---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index cb269d73d793e..e53ca6c2c8a8d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6323,13 +6323,7 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
       return true;
     return isKnownNeverNaN(Op.getOperand(0), DemandedElts, SNaN, Depth + 1);
   }
-  default:
-    if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN ||
-        Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) {
-      return TLI->isKnownNeverNaNForTargetNode(Op, DemandedElts, *this, SNaN,
-                                               Depth);
-    }
-
+  case ISD::BITCAST: {
     // Try to infer NaN from known bits, but only for detecting signaling or
     // nonsignaling NaNs
     EVT VT = Op.getValueType();
@@ -6362,6 +6356,14 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
     }
     return false;
   }
+  default:
+    if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN ||
+        Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) {
+      return TLI->isKnownNeverNaNForTargetNode(Op, DemandedElts, *this, SNaN,
+                                               Depth);
+    }
+    return false;
+  }
 }
 
 bool SelectionDAG::isKnownNeverZeroFloat(SDValue Op) const {

>From 22da0a57b394c6c0c8bf43587cc7f52eabe28ed7 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Mon, 30 Mar 2026 14:27:18 -0500
Subject: [PATCH 08/14] Use `KnownFPClass::bitcast`

---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 28 ++-----------------
 1 file changed, 3 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index e53ca6c2c8a8d..8cf4ebecd1b33 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6328,31 +6328,9 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
     // nonsignaling NaNs
     EVT VT = Op.getValueType();
     if (!SNaN && VT.isFloatingPoint()) {
-      const fltSemantics &FltSem = VT.getFltSemantics();
-      if (FltSem.precision > 0) {
-        KnownBits Known = computeKnownBits(Op, DemandedElts);
-        unsigned Mantissa = FltSem.precision - 1;
-        unsigned Exponent = FltSem.sizeInBits - FltSem.precision;
-        KnownBits KnownMan = Known.extractBits(Mantissa, 0);
-        KnownBits KnownExp = Known.extractBits(Exponent, Mantissa);
-
-        switch (FltSem.nanEncoding) {
-        case fltNanEncoding::IEEE:
-          if (!KnownExp.getMaxValue().isAllOnes() || KnownMan.isZero())
-            return true;
-          break;
-        case fltNanEncoding::AllOnes:
-          if (!KnownExp.getMaxValue().isAllOnes() ||
-              !KnownMan.getMaxValue().isAllOnes())
-            return true;
-          break;
-        case fltNanEncoding::NegativeZero:
-          if (Known.Zero.isSignBitSet() || !KnownExp.getMinValue().isZero() ||
-              !KnownMan.getMinValue().isZero())
-            return true;
-          break;
-        }
-      }
+      KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
+      const auto FPClass = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);
+      return FPClass.isKnownNeverNaN();
     }
     return false;
   }

>From f3972f7153226481b696e922acb6cf7e6c1434b7 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Mon, 30 Mar 2026 17:01:13 -0500
Subject: [PATCH 09/14] Added scalar and vector tests.

---
 .../AArch64/AArch64SelectionDAGTest.cpp       | 41 +++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
index 12b7763274f6c..18f104a849437 100644
--- a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
+++ b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
@@ -1576,6 +1576,47 @@ TEST_F(AArch64SelectionDAGTest, KnownNeverZero_Select) {
   EXPECT_TRUE(DAG->isKnownNeverZero(VSelect4444, DemandAll));
 }
 
+TEST_F(AArch64SelectionDAGTest, KnownNeverNaN_Bitcast) {
+  SDLoc Loc;
+
+  auto UnknownI32 = DAG->getRegister(1, MVT::i32);
+  auto FiniteMask = DAG->getConstant(0x3fff'ffff, Loc, MVT::i32);
+  auto NeverNaNI32 =
+      DAG->getNode(ISD::AND, Loc, MVT::i32, FiniteMask, UnknownI32);
+
+  auto NeverNaNF32 = DAG->getBitcast(MVT::f32, NeverNaNI32);
+  EXPECT_TRUE(DAG->isKnownNeverNaN(NeverNaNF32));
+
+  auto UnknownF32 = DAG->getBitcast(MVT::f32, UnknownI32);
+  EXPECT_FALSE(DAG->isKnownNeverNaN(UnknownF32));
+
+  auto NeverNaNV2I32 = DAG->getSplat(MVT::v2i32, Loc, NeverNaNI32);
+  auto NeverNaNV2F32 = DAG->getBitcast(MVT::v2f32, NeverNaNV2I32);
+  EXPECT_TRUE(DAG->isKnownNeverNaN(NeverNaNV2F32));
+
+  auto PartialNaNV2I32 =
+      DAG->getBuildVector(MVT::v2i32, Loc, {UnknownI32, NeverNaNI32});
+  auto PartialNaNV2F32 = DAG->getBitcast(MVT::v2f32, PartialNaNV2I32);
+  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNV2F32));
+  APInt DemandLo(2, 1);
+  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNV2F32, DemandLo));
+  APInt DemandHi(2, 2);
+  EXPECT_TRUE(DAG->isKnownNeverNaN(PartialNaNV2F32, DemandHi));
+
+  auto PartialNaNAsF64 = DAG->getBitcast(MVT::f64, PartialNaNV2I32);
+  EXPECT_TRUE(DAG->isKnownNeverNaN(PartialNaNAsF64));
+
+  auto UnknownI64 = DAG->getRegister(2, MVT::i64);
+  auto Lo32NeverNaNMask =
+      DAG->getConstant(0xffff'ffff'3fff'ffff, Loc, MVT::i64);
+  auto PartialNaNPack64 =
+      DAG->getNode(ISD::AND, Loc, MVT::i64, Lo32NeverNaNMask, UnknownI64);
+  auto PartialNaNUnpackV2F32 = DAG->getBitcast(MVT::v2f32, PartialNaNPack64);
+  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNUnpackV2F32));
+  EXPECT_TRUE(DAG->isKnownNeverNaN(PartialNaNUnpackV2F32, DemandLo));
+  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNUnpackV2F32, DemandHi));
+}
+
 // tests for SelectionDAG::computeKnownFPClass
 TEST_F(AArch64SelectionDAGTest, ComputeKnownFPClass_ConstantScalar) {
   SDLoc Loc;

>From 4a036c7e4a4854edb937aa12f7fd1e7c73f845c8 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Mon, 30 Mar 2026 17:26:53 -0500
Subject: [PATCH 10/14] Move logic into `computeKnownFPClass`

---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 21 +++++++++----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index b87836233bc33..58e7b9daafa1b 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6112,6 +6112,16 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
     }
     break;
   }
+  case ISD::BITCAST: {
+    // Try to infer NaN from known bits, but only for detecting signaling or
+    // nonsignaling NaNs
+    EVT VT = Op.getValueType();
+    if (VT.isFloatingPoint()) {
+      KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
+      Known = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);
+    }
+    break;
+  }
   default:
     if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN ||
         Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) {
@@ -6317,17 +6327,6 @@ bool SelectionDAG::isKnownNeverNaN(SDValue Op, const APInt &DemandedElts,
       return true;
     return isKnownNeverNaN(Op.getOperand(0), DemandedElts, SNaN, Depth + 1);
   }
-  case ISD::BITCAST: {
-    // Try to infer NaN from known bits, but only for detecting signaling or
-    // nonsignaling NaNs
-    EVT VT = Op.getValueType();
-    if (!SNaN && VT.isFloatingPoint()) {
-      KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
-      const auto FPClass = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);
-      return FPClass.isKnownNeverNaN();
-    }
-    return false;
-  }
   default:
     if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN ||
         Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) {

>From 04d231629a1bbe44d1c5caf6876f367884001db7 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Tue, 31 Mar 2026 09:18:31 -0500
Subject: [PATCH 11/14] Remove unnecessary recomputation of `VT`

---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 58e7b9daafa1b..2dec1873de864 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6076,7 +6076,7 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
   if (Op.getOpcode() == ISD::UNDEF)
     return Known;
 
-  [[maybe_unused]] EVT VT = Op.getValueType();
+  EVT VT = Op.getValueType();
   assert((!VT.isFixedLengthVector() ||
           DemandedElts.getBitWidth() == VT.getVectorNumElements()) &&
          "Unexpected vector size");
@@ -6115,7 +6115,6 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
   case ISD::BITCAST: {
     // Try to infer NaN from known bits, but only for detecting signaling or
     // nonsignaling NaNs
-    EVT VT = Op.getValueType();
     if (VT.isFloatingPoint()) {
       KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
       Known = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);

>From 9df080c874722be978443282ea3b6e237b7642d3 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Tue, 31 Mar 2026 14:31:08 -0500
Subject: [PATCH 12/14] Restrict to elementwise bitcasts

---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 2dec1873de864..b7273fd599350 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6113,8 +6113,19 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
     break;
   }
   case ISD::BITCAST: {
-    // Try to infer NaN from known bits, but only for detecting signaling or
-    // nonsignaling NaNs
+    // FIXME: It should not be necessary to check for an elementwise bitcast.
+    // If a bitcast is not elementwise between vector / scalar types,
+    // computeKnownBits already splices the known bits of the source elements
+    // appropriately so as to line up with the bits of the result's demanded
+    // elements.
+    EVT SrcVT = Op.getOperand(0).getValueType();
+    if (VT.isScalableVector() || SrcVT.isScalableVector())
+      break;
+    unsigned VTNumElts = VT.isVector() ? VT.getVectorNumElements() : 1;
+    unsigned SrcVTNumElts = SrcVT.isVector() ? SrcVT.getVectorNumElements() : 1;
+    if (VTNumElts != SrcVTNumElts)
+      break;
+
     if (VT.isFloatingPoint()) {
       KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
       Known = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);

>From 71e2fc0e17e3106c7549f6a997da54c57e597f9b Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Tue, 31 Mar 2026 14:34:02 -0500
Subject: [PATCH 13/14] Create `KnownFPClass` test coverage for bitcasts.

---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp |  1 +
 .../AArch64/AArch64SelectionDAGTest.cpp       | 98 ++++++++++++-------
 2 files changed, 63 insertions(+), 36 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index b7273fd599350..f734227a4135d 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6126,6 +6126,7 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
     if (VTNumElts != SrcVTNumElts)
       break;
 
+    // FIXME: assert VT.isFloatingPoint() at top level of function.
     if (VT.isFloatingPoint()) {
       KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
       Known = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);
diff --git a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
index 18f104a849437..b144060afe925 100644
--- a/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
+++ b/llvm/unittests/Target/AArch64/AArch64SelectionDAGTest.cpp
@@ -1576,45 +1576,71 @@ TEST_F(AArch64SelectionDAGTest, KnownNeverZero_Select) {
   EXPECT_TRUE(DAG->isKnownNeverZero(VSelect4444, DemandAll));
 }
 
-TEST_F(AArch64SelectionDAGTest, KnownNeverNaN_Bitcast) {
+TEST_F(AArch64SelectionDAGTest, KnownFPClass_Bitcast) {
   SDLoc Loc;
-
-  auto UnknownI32 = DAG->getRegister(1, MVT::i32);
-  auto FiniteMask = DAG->getConstant(0x3fff'ffff, Loc, MVT::i32);
-  auto NeverNaNI32 =
-      DAG->getNode(ISD::AND, Loc, MVT::i32, FiniteMask, UnknownI32);
-
-  auto NeverNaNF32 = DAG->getBitcast(MVT::f32, NeverNaNI32);
-  EXPECT_TRUE(DAG->isKnownNeverNaN(NeverNaNF32));
-
-  auto UnknownF32 = DAG->getBitcast(MVT::f32, UnknownI32);
-  EXPECT_FALSE(DAG->isKnownNeverNaN(UnknownF32));
-
-  auto NeverNaNV2I32 = DAG->getSplat(MVT::v2i32, Loc, NeverNaNI32);
-  auto NeverNaNV2F32 = DAG->getBitcast(MVT::v2f32, NeverNaNV2I32);
-  EXPECT_TRUE(DAG->isKnownNeverNaN(NeverNaNV2F32));
-
-  auto PartialNaNV2I32 =
-      DAG->getBuildVector(MVT::v2i32, Loc, {UnknownI32, NeverNaNI32});
-  auto PartialNaNV2F32 = DAG->getBitcast(MVT::v2f32, PartialNaNV2I32);
-  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNV2F32));
+  SDValue Cond = DAG->getRegister(1, MVT::i1);
+
+  SDValue ConstZeroI32 = DAG->getConstant(0x0000'0000, Loc, MVT::i32);
+  SDValue ConstFiniteI32 = DAG->getConstant(0xbfff'ffff, Loc, MVT::i32);
+  SDValue AlwaysFiniteI32 =
+      DAG->getSelect(Loc, MVT::i32, Cond, ConstZeroI32, ConstFiniteI32);
+  SDValue AlwaysFiniteF32 = DAG->getBitcast(MVT::f32, AlwaysFiniteI32);
+  KnownFPClass AlwaysFiniteFPClass =
+      DAG->computeKnownFPClass(AlwaysFiniteF32, fcAllFlags);
+  EXPECT_TRUE(AlwaysFiniteFPClass.isKnownNeverInfOrNaN());
+
+  SDValue ConstNegInfI32 = DAG->getConstant(0xff80'0000, Loc, MVT::i32);
+  SDValue NeverNaNI32 =
+      DAG->getSelect(Loc, MVT::i32, Cond, ConstZeroI32, ConstNegInfI32);
+  SDValue NeverNaNF32 = DAG->getBitcast(MVT::f32, NeverNaNI32);
+  KnownFPClass NeverNaNFPClass =
+      DAG->computeKnownFPClass(NeverNaNF32, fcAllFlags);
+  EXPECT_FALSE(NeverNaNFPClass.isKnownNeverInfinity());
+  EXPECT_TRUE(NeverNaNFPClass.isKnownNeverNaN());
+
+  SDValue ConstPosNaNI32 = DAG->getConstant(0x7f80'0001, Loc, MVT::i32);
+  SDValue MaybeNaNI32 =
+      DAG->getSelect(Loc, MVT::i32, Cond, ConstPosNaNI32, ConstNegInfI32);
+  SDValue MaybeNaNF32 = DAG->getBitcast(MVT::f32, MaybeNaNI32);
+  KnownFPClass MaybeNaNFPClass =
+      DAG->computeKnownFPClass(MaybeNaNF32, fcAllFlags);
+  EXPECT_FALSE(MaybeNaNFPClass.isKnownNeverNaN());
+  EXPECT_FALSE(MaybeNaNFPClass.isKnownAlwaysNaN());
+
+  SDValue ConstNegNaNI32 = DAG->getConstant(0xffff'ffff, Loc, MVT::i32);
+  SDValue AlwaysNaNI32 =
+      DAG->getSelect(Loc, MVT::i32, Cond, ConstPosNaNI32, ConstNegNaNI32);
+  SDValue AlwaysNaNF32 = DAG->getBitcast(MVT::f32, AlwaysNaNI32);
+  KnownFPClass AlwaysNaNFPClass =
+      DAG->computeKnownFPClass(AlwaysNaNF32, fcAllFlags);
+  EXPECT_TRUE(AlwaysNaNFPClass.isKnownAlwaysNaN());
+
+  SDValue AlwaysFiniteSplat = DAG->getSplat(MVT::v2i32, Loc, AlwaysFiniteI32);
+  SDValue AlwaysFiniteV2F32 = DAG->getBitcast(MVT::v2f32, AlwaysFiniteSplat);
+  KnownFPClass AlwaysFiniteV2FPClass =
+      DAG->computeKnownFPClass(AlwaysFiniteV2F32, fcAllFlags);
+  EXPECT_TRUE(AlwaysFiniteV2FPClass.isKnownNeverInfOrNaN());
+
+  SDValue UpperAlwaysNaN =
+      DAG->getBuildVector(MVT::v2i32, Loc, {NeverNaNI32, AlwaysNaNI32});
+  SDValue UpperAlwaysNaNV2F32 = DAG->getBitcast(MVT::v2f32, UpperAlwaysNaN);
+  KnownFPClass UpperAlwaysNaNFullFPClass =
+      DAG->computeKnownFPClass(UpperAlwaysNaNV2F32, fcAllFlags);
+  EXPECT_FALSE(UpperAlwaysNaNFullFPClass.isKnownNeverNaN());
   APInt DemandLo(2, 1);
-  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNV2F32, DemandLo));
+  KnownFPClass UpperAlwaysNaNLoFPClass =
+      DAG->computeKnownFPClass(UpperAlwaysNaNV2F32, DemandLo, fcAllFlags);
+  EXPECT_TRUE(UpperAlwaysNaNLoFPClass.isKnownNeverNaN());
   APInt DemandHi(2, 2);
-  EXPECT_TRUE(DAG->isKnownNeverNaN(PartialNaNV2F32, DemandHi));
-
-  auto PartialNaNAsF64 = DAG->getBitcast(MVT::f64, PartialNaNV2I32);
-  EXPECT_TRUE(DAG->isKnownNeverNaN(PartialNaNAsF64));
-
-  auto UnknownI64 = DAG->getRegister(2, MVT::i64);
-  auto Lo32NeverNaNMask =
-      DAG->getConstant(0xffff'ffff'3fff'ffff, Loc, MVT::i64);
-  auto PartialNaNPack64 =
-      DAG->getNode(ISD::AND, Loc, MVT::i64, Lo32NeverNaNMask, UnknownI64);
-  auto PartialNaNUnpackV2F32 = DAG->getBitcast(MVT::v2f32, PartialNaNPack64);
-  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNUnpackV2F32));
-  EXPECT_TRUE(DAG->isKnownNeverNaN(PartialNaNUnpackV2F32, DemandLo));
-  EXPECT_FALSE(DAG->isKnownNeverNaN(PartialNaNUnpackV2F32, DemandHi));
+  KnownFPClass UpperAlwaysNaNHiFPClass =
+      DAG->computeKnownFPClass(UpperAlwaysNaNV2F32, DemandHi, fcAllFlags);
+  EXPECT_TRUE(UpperAlwaysNaNHiFPClass.isKnownAlwaysNaN());
+
+  SDValue UpperAlwaysNaNAsF64 = DAG->getBitcast(MVT::f64, UpperAlwaysNaN);
+  KnownFPClass UpperAlwaysNaNAsF64FPClass =
+      DAG->computeKnownFPClass(UpperAlwaysNaNAsF64, fcAllFlags);
+  EXPECT_FALSE(UpperAlwaysNaNAsF64FPClass.isKnownNeverNaN());
+  EXPECT_FALSE(UpperAlwaysNaNAsF64FPClass.isKnownAlwaysNaN());
 }
 
 // tests for SelectionDAG::computeKnownFPClass

>From 06fceac4b8c99cb057474c54c1b6f06a040f8268 Mon Sep 17 00:00:00 2001
From: Zach Goldthorpe <Zach.Goldthorpe at amd.com>
Date: Wed, 1 Apr 2026 12:46:44 -0500
Subject: [PATCH 14/14] Remove unnecessary FP check.

---
 llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 081456bd97c00..4d0ec32b1bfca 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6127,11 +6127,8 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
     if (VTNumElts != SrcVTNumElts)
       break;
 
-    // FIXME: assert VT.isFloatingPoint() at top level of function.
-    if (VT.isFloatingPoint()) {
-      KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
-      Known = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);
-    }
+    KnownBits Bits = computeKnownBits(Op, DemandedElts, Depth + 1);
+    Known = KnownFPClass::bitcast(VT.getFltSemantics(), Bits);
     break;
   }
   default:



More information about the llvm-commits mailing list