https://github.com/Lurie97 created 
https://github.com/llvm/llvm-project/pull/212696

The FP32 __clc_exp/__clc_exp2 implementations scale the reduced result by 2^p 
using integer bit manipulation (as_int(y) + (p << 23)). That representation 
cannot encode a subnormal, so inputs whose result falls below the smallest 
normal were unconditionally flushed to zero via "x < llim ? 0.0f". On devices 
that report CL_FP_DENORM this loses the correct subnormal result (e.g. erfc(), 
which is built on exp(), failed CTS math_brute_force with the reference 
subnormal vs a returned 0).

Recompute the subnormal range with __clc_ldexp, which does produce subnormals, 
gated on __clc_fp32_subnormals_supported() so flush-to-zero targets keep the 
previous fast path. The ldexp path is restricted to [subnorm_llim, llim); 
inputs below subnorm_llim (including -inf and large-magnitude negatives) still 
flush to zero, since their reduced exponent argument would be ill-formed for 
ldexp.

>From aa9d44de0adb1f21eccda24a7721031ac9ce8594 Mon Sep 17 00:00:00 2001
From: jiajia Qian <[email protected]>
Date: Thu, 30 Jul 2026 14:03:12 +0800
Subject: [PATCH] [libclc] Produce subnormal results for exp/exp2 on FP32

The FP32 __clc_exp/__clc_exp2 implementations scale the reduced result
by 2^p using integer bit manipulation (as_int(y) + (p << 23)). That
representation cannot encode a subnormal, so inputs whose result falls
below the smallest normal were unconditionally flushed to zero via
"x < llim ? 0.0f". On devices that report CL_FP_DENORM this loses the
correct subnormal result (e.g. erfc(), which is built on exp(), failed
CTS math_brute_force with the reference subnormal vs a returned 0).

Recompute the subnormal range with __clc_ldexp, which does produce
subnormals, gated on __clc_fp32_subnormals_supported() so flush-to-zero
targets keep the previous fast path. The ldexp path is restricted to
[subnorm_llim, llim); inputs below subnorm_llim (including -inf and
large-magnitude negatives) still flush to zero, since their reduced
exponent argument would be ill-formed for ldexp.
---
 libclc/clc/lib/generic/math/clc_exp.cl   |  2 ++
 libclc/clc/lib/generic/math/clc_exp.inc  | 15 ++++++++++++++-
 libclc/clc/lib/generic/math/clc_exp2.cl  |  2 ++
 libclc/clc/lib/generic/math/clc_exp2.inc | 15 ++++++++++++++-
 4 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libclc/clc/lib/generic/math/clc_exp.cl 
b/libclc/clc/lib/generic/math/clc_exp.cl
index 1e7174c7541c5..452e85ac23930 100644
--- a/libclc/clc/lib/generic/math/clc_exp.cl
+++ b/libclc/clc/lib/generic/math/clc_exp.cl
@@ -11,7 +11,9 @@
 #include "clc/internal/clc.h"
 #include "clc/math/clc_exp_helper.h"
 #include "clc/math/clc_fma.h"
+#include "clc/math/clc_ldexp.h"
 #include "clc/math/clc_mad.h"
+#include "clc/math/clc_subnormal_config.h"
 #include "clc/math/math.h"
 #include "clc/relational/clc_isnan.h"
 
diff --git a/libclc/clc/lib/generic/math/clc_exp.inc 
b/libclc/clc/lib/generic/math/clc_exp.inc
index 5057bf8034e92..aa20412880e2e 100644
--- a/libclc/clc/lib/generic/math/clc_exp.inc
+++ b/libclc/clc/lib/generic/math/clc_exp.inc
@@ -44,8 +44,21 @@ _CLC_OVERLOAD _CLC_DEF __CLC_GENTYPE __clc_exp(__CLC_GENTYPE 
x) {
   const __CLC_GENTYPE ulim = 0x1.62e430p+6f;
   // ln(smallest_normal) = -87.33654475055310898657
   const __CLC_GENTYPE llim = -0x1.5d589ep+6f;
+  // ln(smallest_subnormal) = ln(2^-149) = -103.27892990343184
+  const __CLC_GENTYPE subnorm_llim = -0x1.9d1dap+6f;
 
-  r = x < llim ? 0.0f : r;
+  // The integer scaling above (as_int(y) + (p << 23)) cannot represent
+  // subnormal results, so inputs in [subnorm_llim, llim) whose result is a
+  // subnormal are flushed to zero by the plain "x < llim ? 0" path below.
+  // When subnormals are supported, recompute those via __clc_ldexp so the
+  // subnormal result is preserved. Inputs below subnorm_llim (including -inf)
+  // genuinely underflow to zero and must not go through ldexp, whose exponent
+  // argument would be ill-formed for such extreme inputs.
+  r = x < llim ? (__CLC_GENTYPE)0.0f : r;
+  if (__clc_fp32_subnormals_supported()) {
+    __CLC_GENTYPE sub = __clc_ldexp(y, p);
+    r = (x < llim & x >= subnorm_llim) ? sub : r;
+  }
   r = x < ulim ? r : __CLC_AS_GENTYPE((__CLC_UINTN)0x7f800000);
   return __clc_isnan(x) ? x : r;
 }
diff --git a/libclc/clc/lib/generic/math/clc_exp2.cl 
b/libclc/clc/lib/generic/math/clc_exp2.cl
index 0516963ab620f..baa3beb2ef3f2 100644
--- a/libclc/clc/lib/generic/math/clc_exp2.cl
+++ b/libclc/clc/lib/generic/math/clc_exp2.cl
@@ -11,8 +11,10 @@
 #include "clc/internal/clc.h"
 #include "clc/math/clc_exp_helper.h"
 #include "clc/math/clc_fma.h"
+#include "clc/math/clc_ldexp.h"
 #include "clc/math/clc_mad.h"
 #include "clc/math/clc_rint.h"
+#include "clc/math/clc_subnormal_config.h"
 #include "clc/math/math.h"
 #include "clc/relational/clc_isnan.h"
 
diff --git a/libclc/clc/lib/generic/math/clc_exp2.inc 
b/libclc/clc/lib/generic/math/clc_exp2.inc
index 6da361a43ed4c..906386189f316 100644
--- a/libclc/clc/lib/generic/math/clc_exp2.inc
+++ b/libclc/clc/lib/generic/math/clc_exp2.inc
@@ -41,8 +41,21 @@ _CLC_OVERLOAD _CLC_DEF __CLC_GENTYPE 
__clc_exp2(__CLC_GENTYPE x) {
 
   const __CLC_GENTYPE ulim = 128.0f;
   const __CLC_GENTYPE llim = -126.0f;
+  // smallest subnormal is 2^-149
+  const __CLC_GENTYPE subnorm_llim = -149.0f;
 
-  r = x < llim ? 0.0f : r;
+  // The integer scaling above (as_int(y) + (p << 23)) cannot represent
+  // subnormal results, so inputs in [subnorm_llim, llim) whose result is a
+  // subnormal are flushed to zero by the plain "x < llim ? 0" path below.
+  // When subnormals are supported, recompute those via __clc_ldexp so the
+  // subnormal result is preserved. Inputs below subnorm_llim (including -inf)
+  // genuinely underflow to zero and must not go through ldexp, whose exponent
+  // argument would be ill-formed for such extreme inputs.
+  r = x < llim ? (__CLC_GENTYPE)0.0f : r;
+  if (__clc_fp32_subnormals_supported()) {
+    __CLC_GENTYPE sub = __clc_ldexp(y, p);
+    r = (x < llim & x >= subnorm_llim) ? sub : r;
+  }
   r = x < ulim ? r : __CLC_AS_FLOATN((__CLC_UINTN)0x7f800000);
   return __clc_isnan(x) ? x : r;
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to