[llvm] [DAG] Add ISD::FSQRT handling to computeKnownFPClass (PR #213620)

Varad Rahul Kamthe via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 3 01:48:27 PDT 2026


https://github.com/varadk27 created https://github.com/llvm/llvm-project/pull/213620

Fixes #189583

Ports the ISD::FSQRT case from ValueTracking.cpp's computeKnownFPClass to the SelectionDAG version. sqrt can never produce a negative normal or -infinity; with nnan set, it also can't produce NaN.

Related to #195405, which attempted this earlier but stalled on review.
Unlike that attempt, this does not treat nsz as proof that the result can't be negative zero, since nsz is a fast-math hint rather than a guarantee -- doing so risks promoting a value to poison.

Added RISC-V codegen tests (scalar + scalable-vector) covering nnan, neginf, negnormal, nsz+negzero, and posinf cases. check-llvm-codegen-riscv and check-llvm both pass.

>From bda910d3fe47bc71309f2ddddca4d8f8a8d8a008 Mon Sep 17 00:00:00 2001
From: Varad Kamthe <varadk.2704 at gmail.com>
Date: Mon, 3 Aug 2026 14:05:20 +0530
Subject: [PATCH] [DAG] Add ISD::FSQRT handling to computeKnownFPClass

Ports the ISD::FSQRT case from ValueTracking.cpp's computeKnownFPClass
to the SelectionDAG version, per #189583.

sqrt can never produce a negative normal or -infinity; with nnan set,
it also can't produce NaN. Unlike an earlier attempt (#195405), this
does not treat nsz as proof that the result can't be negative zero,
since nsz is a fast-math hint rather than a guarantee -- doing so risks
promoting a value to poison.

Fixes #189583
---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp |  19 +++
 .../CodeGen/RISCV/fold-is-fpclass-sqrt.ll     | 124 ++++++++++++++++++
 2 files changed, 143 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/fold-is-fpclass-sqrt.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 683cf2517b2f5..21f2727927e60 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6191,6 +6191,25 @@ KnownFPClass SelectionDAG::computeKnownFPClass(SDValue Op,
     Known.fneg();
     break;
   }
+  case ISD::FSQRT: {
+    FPClassTest InterestedSrcs = InterestedClasses;
+    if (InterestedClasses & fcNan)
+      InterestedSrcs |= KnownFPClass::OrderedLessThanZeroMask;
+
+    KnownFPClass KnownSrc = computeKnownFPClass(Op.getOperand(0), DemandedElts,
+                                                InterestedSrcs, Depth + 1);
+
+    const fltSemantics &FltSem =
+        Op.getValueType().getScalarType().getFltSemantics();
+    DenormalMode Mode = getMachineFunction().getDenormalMode(FltSem);
+
+    Known = KnownFPClass::sqrt(KnownSrc, Mode);
+
+    if (Op->getFlags().hasNoNaNs())
+      Known.knownNot(fcNan);
+
+    break;
+  }
   case ISD::BUILD_VECTOR: {
     assert(!VT.isScalableVector());
     bool First = true;
diff --git a/llvm/test/CodeGen/RISCV/fold-is-fpclass-sqrt.ll b/llvm/test/CodeGen/RISCV/fold-is-fpclass-sqrt.ll
new file mode 100644
index 0000000000000..9472259c62cc9
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/fold-is-fpclass-sqrt.ll
@@ -0,0 +1,124 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -mattr=+f,+d,+v -target-abi=lp64d < %s | FileCheck %s
+
+define i1 @sqrt_nnan_check_f32(float %a0) {
+; CHECK-LABEL: sqrt_nnan_check_f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 0
+; CHECK-NEXT:    ret
+  %sqr = call nnan float @llvm.sqrt.f32(float %a0)
+  %res = tail call i1 @llvm.is.fpclass.f32(float %sqr, i32 3)
+  ret i1 %res
+}
+
+define i1 @sqrt_neginf_f32(float %a0) {
+; CHECK-LABEL: sqrt_neginf_f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 0
+; CHECK-NEXT:    ret
+  %sqr = call float @llvm.sqrt.f32(float %a0)
+  %res = tail call i1 @llvm.is.fpclass.f32(float %sqr, i32 4)
+  ret i1 %res
+}
+
+define i1 @sqrt_negnormal_f32(float %a0) {
+; CHECK-LABEL: sqrt_negnormal_f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 0
+; CHECK-NEXT:    ret
+  %sqr = call float @llvm.sqrt.f32(float %a0)
+  %res = tail call i1 @llvm.is.fpclass.f32(float %sqr, i32 8)
+  ret i1 %res
+}
+
+define i1 @sqrt_nsz_negzero_f32(float %a0) {
+; CHECK-LABEL: sqrt_nsz_negzero_f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    fsqrt.s fa5, fa0
+; CHECK-NEXT:    fclass.s a0, fa5
+; CHECK-NEXT:    slli a0, a0, 60
+; CHECK-NEXT:    srli a0, a0, 63
+; CHECK-NEXT:    ret
+  %sqr = call nsz float @llvm.sqrt.f32(float %a0)
+  %res = tail call i1 @llvm.is.fpclass.f32(float %sqr, i32 32)
+  ret i1 %res
+}
+
+define i1 @sqrt_posinf_f32(float %a0) {
+; CHECK-LABEL: sqrt_posinf_f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    fsqrt.s fa5, fa0
+; CHECK-NEXT:    fclass.s a0, fa5
+; CHECK-NEXT:    slli a0, a0, 56
+; CHECK-NEXT:    srli a0, a0, 63
+; CHECK-NEXT:    ret
+  %sqr = call float @llvm.sqrt.f32(float %a0)
+  %res = tail call i1 @llvm.is.fpclass.f32(float %sqr, i32 512)
+  ret i1 %res
+}
+
+declare float @llvm.sqrt.f32(float)
+declare i1 @llvm.is.fpclass.f32(float, i32)
+
+declare <vscale x 2 x float> @llvm.sqrt.nxv2f32(<vscale x 2 x float>)
+declare <vscale x 2 x i1> @llvm.is.fpclass.nxv2f32(<vscale x 2 x float>, i32)
+
+define <vscale x 2 x i1> @vsqrt_nnan_check_nxv2f32(<vscale x 2 x float> %a0) {
+; CHECK-LABEL: vsqrt_nnan_check_nxv2f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e8, mf4, ta, ma
+; CHECK-NEXT:    vmclr.m v0
+; CHECK-NEXT:    ret
+  %sqr = call nnan <vscale x 2 x float> @llvm.sqrt.nxv2f32(<vscale x 2 x float> %a0)
+  %res = tail call <vscale x 2 x i1> @llvm.is.fpclass.nxv2f32(<vscale x 2 x float> %sqr, i32 3)
+  ret <vscale x 2 x i1> %res
+}
+
+define <vscale x 2 x i1> @vsqrt_neginf_nxv2f32(<vscale x 2 x float> %a0) {
+; CHECK-LABEL: vsqrt_neginf_nxv2f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e8, mf4, ta, ma
+; CHECK-NEXT:    vmclr.m v0
+; CHECK-NEXT:    ret
+  %sqr = call <vscale x 2 x float> @llvm.sqrt.nxv2f32(<vscale x 2 x float> %a0)
+  %res = tail call <vscale x 2 x i1> @llvm.is.fpclass.nxv2f32(<vscale x 2 x float> %sqr, i32 4)
+  ret <vscale x 2 x i1> %res
+}
+
+define <vscale x 2 x i1> @vsqrt_negnormal_nxv2f32(<vscale x 2 x float> %a0) {
+; CHECK-LABEL: vsqrt_negnormal_nxv2f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e8, mf4, ta, ma
+; CHECK-NEXT:    vmclr.m v0
+; CHECK-NEXT:    ret
+  %sqr = call <vscale x 2 x float> @llvm.sqrt.nxv2f32(<vscale x 2 x float> %a0)
+  %res = tail call <vscale x 2 x i1> @llvm.is.fpclass.nxv2f32(<vscale x 2 x float> %sqr, i32 8)
+  ret <vscale x 2 x i1> %res
+}
+
+define <vscale x 2 x i1> @vsqrt_nsz_negzero_nxv2f32(<vscale x 2 x float> %a0) {
+; CHECK-LABEL: vsqrt_nsz_negzero_nxv2f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfsqrt.v v8, v8
+; CHECK-NEXT:    vfclass.v v8, v8
+; CHECK-NEXT:    vmseq.vi v0, v8, 8
+; CHECK-NEXT:    ret
+  %sqr = call nsz <vscale x 2 x float> @llvm.sqrt.nxv2f32(<vscale x 2 x float> %a0)
+  %res = tail call <vscale x 2 x i1> @llvm.is.fpclass.nxv2f32(<vscale x 2 x float> %sqr, i32 32)
+  ret <vscale x 2 x i1> %res
+}
+
+define <vscale x 2 x i1> @vsqrt_posinf_nxv2f32(<vscale x 2 x float> %a0) {
+; CHECK-LABEL: vsqrt_posinf_nxv2f32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfsqrt.v v8, v8
+; CHECK-NEXT:    vfclass.v v8, v8
+; CHECK-NEXT:    li a0, 128
+; CHECK-NEXT:    vmseq.vx v0, v8, a0
+; CHECK-NEXT:    ret
+  %sqr = call <vscale x 2 x float> @llvm.sqrt.nxv2f32(<vscale x 2 x float> %a0)
+  %res = tail call <vscale x 2 x i1> @llvm.is.fpclass.nxv2f32(<vscale x 2 x float> %sqr, i32 512)
+  ret <vscale x 2 x i1> %res
+}



More information about the llvm-commits mailing list