https://github.com/rjodinchr created 
https://github.com/llvm/llvm-project/pull/233791

`__clc_sw_fma` previously assumed all non-zero inputs were normal floats 
(extracting biased exponent `0` as `-127` with the implicit leading bit 
`0x800000` set) and unconditionally flushed results with exponent `<= -127` to 
zero. On devices that support fp32 subnormals and use software fma, this caused 
`fma`, `expm1`, and `erf` to fail the CTS.

Normalize non-zero subnormal inputs using `__clc_clz` and, when fp32 subnormals 
are supported (`!__clc_denormals_are_zero_fp32()`), right-shift the significand 
before rounding for exponents in `[-150, -127]` so subnormal outputs and 
subnormal-to-normal rounding overflows are rounded to nearest-even.

>From aed077ba203b15df301e81a637641ea74c7f138c Mon Sep 17 00:00:00 2001
From: Romaric Jodin <[email protected]>
Date: Tue, 20 Oct 2026 11:13:49 +0200
Subject: [PATCH] [libclc] Support fp32 subnormal inputs and outputs in SPIR-V
 sw fma

`__clc_sw_fma` previously assumed all non-zero inputs were normal
floats (extracting biased exponent `0` as `-127` with the implicit
leading bit `0x800000` set) and unconditionally flushed results with
exponent `<= -127` to zero. On devices that support fp32 subnormals
and use software fma, this caused `fma`, `expm1`, and `erf` to fail
the CTS.

Normalize non-zero subnormal inputs using `__clc_clz` and, when fp32
subnormals are supported (`!__clc_denormals_are_zero_fp32()`),
right-shift the significand before rounding for exponents in
`[-150, -127]` so subnormal outputs and subnormal-to-normal rounding
overflows are rounded to nearest-even.

Change-Id: I7368ef462b16bdc5f0c05704052cb9e2feb86ea2
---
 libclc/clc/lib/spirv/math/clc_sw_fma.cl | 50 +++++++++++++++++--------
 1 file changed, 35 insertions(+), 15 deletions(-)

diff --git a/libclc/clc/lib/spirv/math/clc_sw_fma.cl 
b/libclc/clc/lib/spirv/math/clc_sw_fma.cl
index e73f53e6bcd769..fae76e62050eb8 100644
--- a/libclc/clc/lib/spirv/math/clc_sw_fma.cl
+++ b/libclc/clc/lib/spirv/math/clc_sw_fma.cl
@@ -142,16 +142,24 @@ _CLC_DEF _CLC_OVERLOAD float __clc_sw_fma(float a, float 
b, float c) {
 
   struct fp st_a, st_b, st_c;
 
-  st_a.exponent = a == .0f ? 0 : ((__clc_as_uint(a) & 0x7f800000) >> 23) - 127;
-  st_b.exponent = b == .0f ? 0 : ((__clc_as_uint(b) & 0x7f800000) >> 23) - 127;
-  st_c.exponent = c == .0f ? 0 : ((__clc_as_uint(c) & 0x7f800000) >> 23) - 127;
+  uint ua = __clc_as_uint(a) & 0x7fffffff;
+  uint ub = __clc_as_uint(b) & 0x7fffffff;
+  uint uc = __clc_as_uint(c) & 0x7fffffff;
+
+  uint sa = ua < 0x800000u ? __clc_clz(ua) - 8 : 0;
+  uint sb = ub < 0x800000u ? __clc_clz(ub) - 8 : 0;
+  uint sc = uc < 0x800000u ? __clc_clz(uc) - 8 : 0;
+
+  st_a.exponent = ua < 0x800000u ? -126 - (int)sa : (int)(ua >> 23) - 127;
+  st_b.exponent = ub < 0x800000u ? -126 - (int)sb : (int)(ub >> 23) - 127;
+  st_c.exponent = uc < 0x800000u ? -126 - (int)sc : (int)(uc >> 23) - 127;
 
   st_a.mantissa =
-      u2_set_u(a == .0f ? 0 : (__clc_as_uint(a) & 0x7fffff) | 0x800000);
+      u2_set_u(ua < 0x800000u ? (ua << sa) : (ua & 0x7fffff) | 0x800000);
   st_b.mantissa =
-      u2_set_u(b == .0f ? 0 : (__clc_as_uint(b) & 0x7fffff) | 0x800000);
+      u2_set_u(ub < 0x800000u ? (ub << sb) : (ub & 0x7fffff) | 0x800000);
   st_c.mantissa =
-      u2_set_u(c == .0f ? 0 : (__clc_as_uint(c) & 0x7fffff) | 0x800000);
+      u2_set_u(uc < 0x800000u ? (uc << sc) : (uc & 0x7fffff) | 0x800000);
 
   st_a.sign = __clc_as_uint(a) & 0x80000000;
   st_b.sign = __clc_as_uint(b) & 0x80000000;
@@ -214,6 +222,10 @@ _CLC_DEF _CLC_OVERLOAD float __clc_sw_fma(float a, float 
b, float c) {
     st_fma.sign = st_mul.sign ^ 0x80000000;
   }
 
+  if (u2_zero(st_fma.mantissa)) {
+    return 0.0f;
+  }
+
   // detect overflow/underflow
   int overflow_bits = 3 - u2_clz(st_fma.mantissa);
 
@@ -226,6 +238,19 @@ _CLC_DEF _CLC_OVERLOAD float __clc_sw_fma(float a, float 
b, float c) {
     overflow_bits = 0;
   }
 
+  if (st_fma.exponent <= -127) {
+    if (__clc_denormals_are_zero_fp32() || st_fma.exponent < -150) {
+      return __clc_as_float(st_fma.sign);
+    }
+    int sub_shift = -126 - st_fma.exponent;
+    uint2 sub_mask = u2_add(u2_sll(u2_set_u(1), sub_shift),
+                            u2_set(0xffffffff, 0xffffffff));
+    cutoff_bits =
+        u2_or(cutoff_bits, !u2_zero(u2_and(st_fma.mantissa, sub_mask)));
+    st_fma.mantissa = u2_srl(st_fma.mantissa, sub_shift);
+    st_fma.exponent = -127;
+  }
+
   // rounding
   uint2 trunc_mask = u2_add(u2_sll(u2_set_u(1), C_ADJUST + overflow_bits),
                             u2_set(0xffffffff, 0xffffffff));
@@ -245,26 +270,21 @@ _CLC_DEF _CLC_OVERLOAD float __clc_sw_fma(float a, float 
b, float c) {
   // Shift mantissa back to bit 23
   st_fma.mantissa = u2_srl(st_fma.mantissa, C_ADJUST + overflow_bits);
 
+  if (st_fma.exponent == -127) {
+    return __clc_as_float(st_fma.sign | (uint)st_fma.mantissa.lo);
+  }
+
   // Detect rounding overflow
   if (u2_gt(st_fma.mantissa, u2_set_u(0xffffff))) {
     ++st_fma.exponent;
     st_fma.mantissa = u2_srl(st_fma.mantissa, 1);
   }
 
-  if (u2_zero(st_fma.mantissa)) {
-    return 0.0f;
-  }
-
   // Flating point range limit
   if (st_fma.exponent > 127) {
     return __clc_as_float(__clc_as_uint(INFINITY) | st_fma.sign);
   }
 
-  // Flush denormals
-  if (st_fma.exponent <= -127) {
-    return __clc_as_float(st_fma.sign);
-  }
-
   return __clc_as_float(st_fma.sign | ((st_fma.exponent + 127) << 23) |
                         ((uint)st_fma.mantissa.lo & 0x7fffff));
 }

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

Reply via email to