All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8}
@ 2026-02-05 11:16 Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 1/9] fpu/softfloat: Refactor IEEE format NaN classification to share code Richard Henderson
                   ` (8 more replies)
  0 siblings, 9 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou

Hiya,

This is the form of what I was talking about wrt spliting up the
patches and introduced FloatFmtExpMaxKind, as I suggested during
review of v3.  Since I've got no test case for these formats, I
haven't been able to do more.  Please test.

r~

Max Chou (8):
  fpu/softfloat: Refactor IEEE format NaN classification to share code
  fpu/softfloat: Refactor floatx80 format NaN classification to share
    code
  target/i386: Fix pseudo-NaN handling in FPATAN/FYL2XP1/FYL2X helpers
  fpu: Add overflow_raises_invalid to FloatFmt
  fpu: Add saturate parameter to parts_uncanon
  fpu: Add conversion routines for OCP FP8 E5M2
  fpu: Add conversion routines for OCP FP8 E4M3
  fpu: Add conversion routines for OCP FP4 E2M1

Richard Henderson (1):
  fpu: Introduce FloatFmtExpMaxKind

 include/fpu/softfloat-types.h  |   7 +
 include/fpu/softfloat.h        |  18 ++-
 fpu/softfloat.c                | 211 ++++++++++++++++++++++++----
 target/i386/tcg/fpu_helper.c   |  30 ++--
 fpu/softfloat-parts.c.inc      | 152 ++++++++++++++------
 fpu/softfloat-specialize.c.inc | 245 +++++++++++++--------------------
 6 files changed, 429 insertions(+), 234 deletions(-)

-- 
2.43.0



^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 1/9] fpu/softfloat: Refactor IEEE format NaN classification to share code
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 2/9] fpu/softfloat: Refactor floatx80 " Richard Henderson
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou, Chao Liu

From: Max Chou <max.chou@sifive.com>

The floatN_is_[quiet|signaling]_nan functions for following formats
(float16, bfloat16, float32, float64, float128) contain duplicated
logic that should be shared.
This commit introduces
[float16|bfloat16|float32|float64|float128]_nan_is_snan that determine
if a NaN is signaling.

Suggested-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Max Chou <max.chou@sifive.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-ID: <20260204051756.667397-3-max.chou@sifive.com>
---
 fpu/softfloat-specialize.c.inc | 192 ++++++++++++++-------------------
 1 file changed, 80 insertions(+), 112 deletions(-)

diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc
index ba4fa08b7b..7d2515c1fa 100644
--- a/fpu/softfloat-specialize.c.inc
+++ b/fpu/softfloat-specialize.c.inc
@@ -226,6 +226,19 @@ floatx80 floatx80_default_inf(bool zSign, float_status *status)
     return packFloatx80(zSign, 0x7fff, z ? 0 : (1ULL << 63));
 }
 
+/*----------------------------------------------------------------------------
+| Determine if a float16 NaN is signaling NaN.
+*----------------------------------------------------------------------------*/
+
+static bool float16_nan_is_snan(float16 a, float_status *status)
+{
+    if (no_signaling_nans(status)) {
+        return false;
+    }
+    bool frac_msb_is_one = (a >> 9) & 1;
+    return frac_msb_is_one == snan_bit_is_one(status);
+}
+
 /*----------------------------------------------------------------------------
 | Returns 1 if the half-precision floating-point value `a' is a quiet
 | NaN; otherwise returns 0.
@@ -233,36 +246,7 @@ floatx80 floatx80_default_inf(bool zSign, float_status *status)
 
 bool float16_is_quiet_nan(float16 a_, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return float16_is_any_nan(a_);
-    } else {
-        uint16_t a = float16_val(a_);
-        if (snan_bit_is_one(status)) {
-            return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF);
-        } else {
-
-            return ((a >> 9) & 0x3F) == 0x3F;
-        }
-    }
-}
-
-/*----------------------------------------------------------------------------
-| Returns 1 if the bfloat16 value `a' is a quiet
-| NaN; otherwise returns 0.
-*----------------------------------------------------------------------------*/
-
-bool bfloat16_is_quiet_nan(bfloat16 a_, float_status *status)
-{
-    if (no_signaling_nans(status)) {
-        return bfloat16_is_any_nan(a_);
-    } else {
-        uint16_t a = a_;
-        if (snan_bit_is_one(status)) {
-            return (((a >> 6) & 0x1FF) == 0x1FE) && (a & 0x3F);
-        } else {
-            return ((a >> 6) & 0x1FF) == 0x1FF;
-        }
-    }
+    return float16_is_any_nan(a_) && !float16_nan_is_snan(a_, status);
 }
 
 /*----------------------------------------------------------------------------
@@ -272,35 +256,51 @@ bool bfloat16_is_quiet_nan(bfloat16 a_, float_status *status)
 
 bool float16_is_signaling_nan(float16 a_, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return 0;
-    } else {
-        uint16_t a = float16_val(a_);
-        if (snan_bit_is_one(status)) {
-            return ((a >> 9) & 0x3F) == 0x3F;
-        } else {
-            return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF);
-        }
-    }
+    return float16_is_any_nan(a_) && float16_nan_is_snan(a_, status);
 }
 
 /*----------------------------------------------------------------------------
-| Returns 1 if the bfloat16 value `a' is a signaling
-| NaN; otherwise returns 0.
+| Determine if a bfloat16 NaN is signaling NaN.
+*----------------------------------------------------------------------------*/
+
+static bool bfloat16_nan_is_snan(bfloat16 a, float_status *status)
+{
+    if (no_signaling_nans(status)) {
+        return false;
+    }
+    bool frac_msb_is_one = (a >> 6) & 1;
+    return frac_msb_is_one == snan_bit_is_one(status);
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the bfloat16 value `a' is a quiet NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+bool bfloat16_is_quiet_nan(bfloat16 a_, float_status *status)
+{
+    return bfloat16_is_any_nan(a_) && !bfloat16_nan_is_snan(a_, status);
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the bfloat16 value `a' is a signaling NaN; otherwise returns 0.
 *----------------------------------------------------------------------------*/
 
 bool bfloat16_is_signaling_nan(bfloat16 a_, float_status *status)
+{
+    return bfloat16_is_any_nan(a_) && bfloat16_nan_is_snan(a_, status);
+}
+
+/*----------------------------------------------------------------------------
+| Determine if a float32 NaN is signaling NaN.
+*----------------------------------------------------------------------------*/
+
+static bool float32_nan_is_snan(float32 a, float_status *status)
 {
     if (no_signaling_nans(status)) {
-        return 0;
-    } else {
-        uint16_t a = a_;
-        if (snan_bit_is_one(status)) {
-            return ((a >> 6) & 0x1FF) == 0x1FF;
-        } else {
-            return (((a >> 6) & 0x1FF) == 0x1FE) && (a & 0x3F);
-        }
+        return false;
     }
+    bool frac_msb_is_one = (a >> 22) & 1;
+    return frac_msb_is_one == snan_bit_is_one(status);
 }
 
 /*----------------------------------------------------------------------------
@@ -310,16 +310,7 @@ bool bfloat16_is_signaling_nan(bfloat16 a_, float_status *status)
 
 bool float32_is_quiet_nan(float32 a_, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return float32_is_any_nan(a_);
-    } else {
-        uint32_t a = float32_val(a_);
-        if (snan_bit_is_one(status)) {
-            return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF);
-        } else {
-            return ((uint32_t)(a << 1) >= 0xFF800000);
-        }
-    }
+    return float32_is_any_nan(a_) && !float32_nan_is_snan(a_, status);
 }
 
 /*----------------------------------------------------------------------------
@@ -328,17 +319,21 @@ bool float32_is_quiet_nan(float32 a_, float_status *status)
 *----------------------------------------------------------------------------*/
 
 bool float32_is_signaling_nan(float32 a_, float_status *status)
+{
+    return float32_is_any_nan(a_) && float32_nan_is_snan(a_, status);
+}
+
+/*----------------------------------------------------------------------------
+| Determine if a float64 NaN is signaling NaN.
+*----------------------------------------------------------------------------*/
+
+static bool float64_nan_is_snan(float64 a, float_status *status)
 {
     if (no_signaling_nans(status)) {
-        return 0;
-    } else {
-        uint32_t a = float32_val(a_);
-        if (snan_bit_is_one(status)) {
-            return ((uint32_t)(a << 1) >= 0xFF800000);
-        } else {
-            return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF);
-        }
+        return false;
     }
+    bool frac_msb_is_one = (a >> 51) & 1;
+    return frac_msb_is_one == snan_bit_is_one(status);
 }
 
 /*----------------------------------------------------------------------------
@@ -348,17 +343,7 @@ bool float32_is_signaling_nan(float32 a_, float_status *status)
 
 bool float64_is_quiet_nan(float64 a_, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return float64_is_any_nan(a_);
-    } else {
-        uint64_t a = float64_val(a_);
-        if (snan_bit_is_one(status)) {
-            return (((a >> 51) & 0xFFF) == 0xFFE)
-                && (a & 0x0007FFFFFFFFFFFFULL);
-        } else {
-            return ((a << 1) >= 0xFFF0000000000000ULL);
-        }
-    }
+    return float64_is_any_nan(a_) && !float64_nan_is_snan(a_, status);
 }
 
 /*----------------------------------------------------------------------------
@@ -368,17 +353,7 @@ bool float64_is_quiet_nan(float64 a_, float_status *status)
 
 bool float64_is_signaling_nan(float64 a_, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return 0;
-    } else {
-        uint64_t a = float64_val(a_);
-        if (snan_bit_is_one(status)) {
-            return ((a << 1) >= 0xFFF0000000000000ULL);
-        } else {
-            return (((a >> 51) & 0xFFF) == 0xFFE)
-                && (a & UINT64_C(0x0007FFFFFFFFFFFF));
-        }
-    }
+    return float64_is_any_nan(a_) && float64_nan_is_snan(a_, status);
 }
 
 /*----------------------------------------------------------------------------
@@ -444,6 +419,19 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status)
     return a;
 }
 
+/*----------------------------------------------------------------------------
+| Determine if a float128 NaN is signaling NaN.
+*----------------------------------------------------------------------------*/
+
+static bool float128_nan_is_snan(float128 a, float_status *status)
+{
+    if (no_signaling_nans(status)) {
+        return false;
+    }
+    bool frac_msb_is_one = (a.high >> 47) & 1;
+    return frac_msb_is_one == snan_bit_is_one(status);
+}
+
 /*----------------------------------------------------------------------------
 | Returns 1 if the quadruple-precision floating-point value `a' is a quiet
 | NaN; otherwise returns 0.
@@ -451,17 +439,7 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status)
 
 bool float128_is_quiet_nan(float128 a, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return float128_is_any_nan(a);
-    } else {
-        if (snan_bit_is_one(status)) {
-            return (((a.high >> 47) & 0xFFFF) == 0xFFFE)
-                && (a.low || (a.high & 0x00007FFFFFFFFFFFULL));
-        } else {
-            return ((a.high << 1) >= 0xFFFF000000000000ULL)
-                && (a.low || (a.high & 0x0000FFFFFFFFFFFFULL));
-        }
-    }
+    return float128_is_any_nan(a) && !float128_nan_is_snan(a, status);
 }
 
 /*----------------------------------------------------------------------------
@@ -471,15 +449,5 @@ bool float128_is_quiet_nan(float128 a, float_status *status)
 
 bool float128_is_signaling_nan(float128 a, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return 0;
-    } else {
-        if (snan_bit_is_one(status)) {
-            return ((a.high << 1) >= 0xFFFF000000000000ULL)
-                && (a.low || (a.high & 0x0000FFFFFFFFFFFFULL));
-        } else {
-            return (((a.high >> 47) & 0xFFFF) == 0xFFFE)
-                && (a.low || (a.high & UINT64_C(0x00007FFFFFFFFFFF)));
-        }
-    }
+    return float128_is_any_nan(a) && float128_nan_is_snan(a, status);
 }
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 2/9] fpu/softfloat: Refactor floatx80 format NaN classification to share code
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 1/9] fpu/softfloat: Refactor IEEE format NaN classification to share code Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 3/9] target/i386: Fix pseudo-NaN handling in FPATAN/FYL2XP1/FYL2X helpers Richard Henderson
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou, Chao Liu

From: Max Chou <max.chou@sifive.com>

The floatx80_is_[quiet|signaling]_nan functions contain duplicated
logic that should be shared.
This commit introduces floatx80_nan_is_snan helper function that
determine if a NaN is signaling and change the return type of
floatx80_is_[signaling|quiet]_nan to bool.

Suggested-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Max Chou <max.chou@sifive.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-ID: <20260204051756.667397-4-max.chou@sifive.com>
---
 include/fpu/softfloat.h        |  4 +--
 fpu/softfloat-specialize.c.inc | 53 ++++++++++++----------------------
 2 files changed, 21 insertions(+), 36 deletions(-)

diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index c18ab2cb60..ac6a392375 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -978,8 +978,8 @@ floatx80 floatx80_rem(floatx80, floatx80, float_status *status);
 floatx80 floatx80_sqrt(floatx80, float_status *status);
 FloatRelation floatx80_compare(floatx80, floatx80, float_status *status);
 FloatRelation floatx80_compare_quiet(floatx80, floatx80, float_status *status);
-int floatx80_is_quiet_nan(floatx80, float_status *status);
-int floatx80_is_signaling_nan(floatx80, float_status *status);
+bool floatx80_is_quiet_nan(floatx80, float_status *status);
+bool floatx80_is_signaling_nan(floatx80, float_status *status);
 floatx80 floatx80_silence_nan(floatx80, float_status *status);
 floatx80 floatx80_scalbn(floatx80, int, float_status *status);
 
diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc
index 7d2515c1fa..9ed968c79b 100644
--- a/fpu/softfloat-specialize.c.inc
+++ b/fpu/softfloat-specialize.c.inc
@@ -357,53 +357,38 @@ bool float64_is_signaling_nan(float64 a_, float_status *status)
 }
 
 /*----------------------------------------------------------------------------
-| Returns 1 if the extended double-precision floating-point value `a' is a
-| quiet NaN; otherwise returns 0. This slightly differs from the same
-| function for other types as floatx80 has an explicit bit.
+| Determine if a floatx80 NaN is signaling NaN.
+| The MSB of frac differs from the same function for other types as floatx80
+| has an explicit bit.
 *----------------------------------------------------------------------------*/
 
-int floatx80_is_quiet_nan(floatx80 a, float_status *status)
+static bool floatx80_nan_is_snan(floatx80 a, float_status *status)
 {
     if (no_signaling_nans(status)) {
-        return floatx80_is_any_nan(a);
-    } else {
-        if (snan_bit_is_one(status)) {
-            uint64_t aLow;
-
-            aLow = a.low & ~0x4000000000000000ULL;
-            return ((a.high & 0x7FFF) == 0x7FFF)
-                && (aLow << 1)
-                && (a.low == aLow);
-        } else {
-            return ((a.high & 0x7FFF) == 0x7FFF)
-                && (UINT64_C(0x8000000000000000) <= ((uint64_t)(a.low << 1)));
-        }
+        return false;
     }
+    bool frac_msb_is_one = (a.low >> 62) & 1;
+    return frac_msb_is_one == snan_bit_is_one(status);
 }
 
 /*----------------------------------------------------------------------------
 | Returns 1 if the extended double-precision floating-point value `a' is a
-| signaling NaN; otherwise returns 0. This slightly differs from the same
-| function for other types as floatx80 has an explicit bit.
+| quiet NaN; otherwise returns 0.
 *----------------------------------------------------------------------------*/
 
-int floatx80_is_signaling_nan(floatx80 a, float_status *status)
+bool floatx80_is_quiet_nan(floatx80 a, float_status *status)
 {
-    if (no_signaling_nans(status)) {
-        return 0;
-    } else {
-        if (snan_bit_is_one(status)) {
-            return ((a.high & 0x7FFF) == 0x7FFF)
-                && ((a.low << 1) >= 0x8000000000000000ULL);
-        } else {
-            uint64_t aLow;
+    return floatx80_is_any_nan(a) && !floatx80_nan_is_snan(a, status);
+}
 
-            aLow = a.low & ~UINT64_C(0x4000000000000000);
-            return ((a.high & 0x7FFF) == 0x7FFF)
-                && (uint64_t)(aLow << 1)
-                && (a.low == aLow);
-        }
-    }
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is a
+| signaling NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+bool floatx80_is_signaling_nan(floatx80 a, float_status *status)
+{
+    return floatx80_is_any_nan(a) && floatx80_nan_is_snan(a, status);
 }
 
 /*----------------------------------------------------------------------------
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 3/9] target/i386: Fix pseudo-NaN handling in FPATAN/FYL2XP1/FYL2X helpers
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 1/9] fpu/softfloat: Refactor IEEE format NaN classification to share code Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 2/9] fpu/softfloat: Refactor floatx80 " Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 4/9] fpu: Introduce FloatFmtExpMaxKind Richard Henderson
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou, Chao Liu

From: Max Chou <max.chou@sifive.com>

According to Intel's x87 FPU specification (Table 8-10, Vol. 1), arithmetic
operations on operands in unsupported formats (including pseudo-NaNs) must
return the QNaN floating-point indefinite value.

The helper functions for FPATAN, FYL2XP1, and FYL2X incorrectly check for
signaling NaN before checking for invalid encodings. This causes pseudo-NaNs
to be treated as valid signaling NaNs and silenced, rather than being
rejected as unsupported formats.

Reorder the checks to test floatx80_invalid_encoding before
floatx80_is_signaling_nan, matching the correct behavior already
implemented in helper_fscale.

Signed-off-by: Max Chou <max.chou@sifive.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-ID: <20260204051756.667397-5-max.chou@sifive.com>
---
 target/i386/tcg/fpu_helper.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c
index b3b23823fd..37c83ded38 100644
--- a/target/i386/tcg/fpu_helper.c
+++ b/target/i386/tcg/fpu_helper.c
@@ -1377,16 +1377,16 @@ void helper_fpatan(CPUX86State *env)
     int32_t arg1_exp = extractFloatx80Exp(ST1);
     bool arg1_sign = extractFloatx80Sign(ST1);
 
-    if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
+    if (floatx80_invalid_encoding(ST0, &env->fp_status) ||
+        floatx80_invalid_encoding(ST1, &env->fp_status)) {
+        float_raise(float_flag_invalid, &env->fp_status);
+        ST1 = floatx80_default_nan(&env->fp_status);
+    } else if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
         float_raise(float_flag_invalid, &env->fp_status);
         ST1 = floatx80_silence_nan(ST0, &env->fp_status);
     } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) {
         float_raise(float_flag_invalid, &env->fp_status);
         ST1 = floatx80_silence_nan(ST1, &env->fp_status);
-    } else if (floatx80_invalid_encoding(ST0, &env->fp_status) ||
-               floatx80_invalid_encoding(ST1, &env->fp_status)) {
-        float_raise(float_flag_invalid, &env->fp_status);
-        ST1 = floatx80_default_nan(&env->fp_status);
     } else if (floatx80_is_any_nan(ST0)) {
         ST1 = ST0;
     } else if (floatx80_is_any_nan(ST1)) {
@@ -2061,16 +2061,16 @@ void helper_fyl2xp1(CPUX86State *env)
     int32_t arg1_exp = extractFloatx80Exp(ST1);
     bool arg1_sign = extractFloatx80Sign(ST1);
 
-    if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
+    if (floatx80_invalid_encoding(ST0, &env->fp_status) ||
+        floatx80_invalid_encoding(ST1, &env->fp_status)) {
+        float_raise(float_flag_invalid, &env->fp_status);
+        ST1 = floatx80_default_nan(&env->fp_status);
+    } else if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
         float_raise(float_flag_invalid, &env->fp_status);
         ST1 = floatx80_silence_nan(ST0, &env->fp_status);
     } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) {
         float_raise(float_flag_invalid, &env->fp_status);
         ST1 = floatx80_silence_nan(ST1, &env->fp_status);
-    } else if (floatx80_invalid_encoding(ST0, &env->fp_status) ||
-               floatx80_invalid_encoding(ST1, &env->fp_status)) {
-        float_raise(float_flag_invalid, &env->fp_status);
-        ST1 = floatx80_default_nan(&env->fp_status);
     } else if (floatx80_is_any_nan(ST0)) {
         ST1 = ST0;
     } else if (floatx80_is_any_nan(ST1)) {
@@ -2159,16 +2159,16 @@ void helper_fyl2x(CPUX86State *env)
     int32_t arg1_exp = extractFloatx80Exp(ST1);
     bool arg1_sign = extractFloatx80Sign(ST1);
 
-    if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
+    if (floatx80_invalid_encoding(ST0, &env->fp_status) ||
+        floatx80_invalid_encoding(ST1, &env->fp_status)) {
+        float_raise(float_flag_invalid, &env->fp_status);
+        ST1 = floatx80_default_nan(&env->fp_status);
+    } else if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
         float_raise(float_flag_invalid, &env->fp_status);
         ST1 = floatx80_silence_nan(ST0, &env->fp_status);
     } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) {
         float_raise(float_flag_invalid, &env->fp_status);
         ST1 = floatx80_silence_nan(ST1, &env->fp_status);
-    } else if (floatx80_invalid_encoding(ST0, &env->fp_status) ||
-               floatx80_invalid_encoding(ST1, &env->fp_status)) {
-        float_raise(float_flag_invalid, &env->fp_status);
-        ST1 = floatx80_default_nan(&env->fp_status);
     } else if (floatx80_is_any_nan(ST0)) {
         ST1 = ST0;
     } else if (floatx80_is_any_nan(ST1)) {
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 4/9] fpu: Introduce FloatFmtExpMaxKind
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
                   ` (2 preceding siblings ...)
  2026-02-05 11:16 ` [PATCH v4 3/9] target/i386: Fix pseudo-NaN handling in FPATAN/FYL2XP1/FYL2X helpers Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 5/9] fpu: Add overflow_raises_invalid to FloatFmt Richard Henderson
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou

Generalize arm_althp to indicate how exp==max should
be handled for the format.  Reorganize canonicalize
and uncanon_normal to use a switch statement, allowing
more cases to be added trivially.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 fpu/softfloat.c           | 17 ++++++--
 fpu/softfloat-parts.c.inc | 92 ++++++++++++++++++++++++---------------
 2 files changed, 69 insertions(+), 40 deletions(-)

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 8094358c2e..18098c4cdd 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -522,7 +522,16 @@ typedef struct {
 #define DECOMPOSED_BINARY_POINT    63
 #define DECOMPOSED_IMPLICIT_BIT    (1ull << DECOMPOSED_BINARY_POINT)
 
-/* Structure holding all of the relevant parameters for a format.
+/* Format-specific handling of exp == exp_max */
+typedef enum __attribute__((__packed__)) {
+    /* exp==max, frac==0 ? infinity : nan; this is ieee standard. */
+    float_expmax_ieee,
+    /* exp==max is a normal number; no infinity or nan representation. */
+    float_expmax_normal,
+} FloatFmtExpMaxKind;
+
+/*
+ * Structure holding all of the relevant parameters for a format.
  *   exp_size: the size of the exponent field
  *   exp_bias: the offset applied to the exponent field
  *   exp_max: the maximum normalised exponent
@@ -531,7 +540,7 @@ typedef struct {
  * The following are computed based the size of fraction
  *   round_mask: bits below lsb which must be rounded
  * The following optional modifiers are available:
- *   arm_althp: handle ARM Alternative Half Precision
+ *   exp_max_kind: affects how exp == exp_max is interpreted
  *   has_explicit_bit: has an explicit integer bit; this affects whether
  *   the float_status floatx80_behaviour handling applies
  */
@@ -542,7 +551,7 @@ typedef struct {
     int exp_max;
     int frac_size;
     int frac_shift;
-    bool arm_althp;
+    FloatFmtExpMaxKind exp_max_kind;
     bool has_explicit_bit;
     uint64_t round_mask;
 } FloatFmt;
@@ -566,7 +575,7 @@ static const FloatFmt float16_params = {
 
 static const FloatFmt float16_params_ahp = {
     FLOAT_PARAMS(5, 10),
-    .arm_althp = true
+    .exp_max_kind = float_expmax_normal,
 };
 
 static const FloatFmt bfloat16_params = {
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc
index 5e0438fc0b..d3c9c7a913 100644
--- a/fpu/softfloat-parts.c.inc
+++ b/fpu/softfloat-parts.c.inc
@@ -227,18 +227,30 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
             p->exp = fmt->frac_shift - fmt->exp_bias
                    - shift + !has_pseudo_denormals;
         }
-    } else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) {
-        p->cls = float_class_normal;
-        p->exp -= fmt->exp_bias;
-        frac_shl(p, fmt->frac_shift);
-        p->frac_hi |= DECOMPOSED_IMPLICIT_BIT;
-    } else if (likely(frac_eqz(p))) {
-        p->cls = float_class_inf;
-    } else {
-        frac_shl(p, fmt->frac_shift);
-        p->cls = (parts_is_snan_frac(p->frac_hi, status)
-                  ? float_class_snan : float_class_qnan);
+        return;
     }
+    if (unlikely(p->exp == fmt->exp_max)) {
+        switch (fmt->exp_max_kind) {
+        case float_expmax_ieee:
+            if (likely(frac_eqz(p))) {
+                p->cls = float_class_inf;
+            } else {
+                frac_shl(p, fmt->frac_shift);
+                p->cls = (parts_is_snan_frac(p->frac_hi, status)
+                          ? float_class_snan : float_class_qnan);
+            }
+            return;
+        case float_expmax_normal:
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    }
+
+    p->cls = float_class_normal;
+    p->exp -= fmt->exp_bias;
+    frac_shl(p, fmt->frac_shift);
+    p->frac_hi |= DECOMPOSED_IMPLICIT_BIT;
 }
 
 /*
@@ -314,29 +326,37 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
             p->frac_lo &= ~round_mask;
         }
 
-        if (fmt->arm_althp) {
-            /* ARM Alt HP eschews Inf and NaN for a wider exponent.  */
-            if (unlikely(exp > exp_max)) {
-                /* Overflow.  Return the maximum normal.  */
-                flags = float_flag_invalid;
-                exp = exp_max;
-                frac_allones(p);
-                p->frac_lo &= ~round_mask;
-            }
-        } else if (unlikely(exp >= exp_max)) {
-            flags |= float_flag_overflow;
-            if (s->rebias_overflow) {
-                exp -= fmt->exp_re_bias;
-            } else if (overflow_norm) {
-                flags |= float_flag_inexact;
-                exp = exp_max - 1;
-                frac_allones(p);
-                p->frac_lo &= ~round_mask;
-            } else {
-                flags |= float_flag_inexact;
-                p->cls = float_class_inf;
-                exp = exp_max;
-                frac_clear(p);
+        if (unlikely(exp >= exp_max)) {
+            switch (fmt->exp_max_kind) {
+            case float_expmax_ieee:
+                flags |= float_flag_overflow;
+                if (s->rebias_overflow) {
+                    exp -= fmt->exp_re_bias;
+                } else if (overflow_norm) {
+                    flags |= float_flag_inexact;
+                    exp = exp_max - 1;
+                    frac_allones(p);
+                    p->frac_lo &= ~round_mask;
+                } else {
+                    flags |= float_flag_inexact;
+                    p->cls = float_class_inf;
+                    exp = exp_max;
+                    frac_clear(p);
+                }
+                break;
+
+            case float_expmax_normal:
+                if (unlikely(exp > exp_max)) {
+                    /* Overflow.  Return the maximum normal.  */
+                    flags = float_flag_invalid;
+                    exp = exp_max;
+                    frac_allones(p);
+                    p->frac_lo &= ~round_mask;
+                }
+                break;
+
+            default:
+                g_assert_not_reached();
             }
         }
         frac_shr(p, frac_shift);
@@ -434,13 +454,13 @@ static void partsN(uncanon)(FloatPartsN *p, float_status *s,
             frac_clear(p);
             return;
         case float_class_inf:
-            g_assert(!fmt->arm_althp);
+            assert(fmt->exp_max_kind == float_expmax_ieee);
             p->exp = fmt->exp_max;
             frac_clear(p);
             return;
         case float_class_qnan:
         case float_class_snan:
-            g_assert(!fmt->arm_althp);
+            assert(fmt->exp_max_kind != float_expmax_normal);
             p->exp = fmt->exp_max;
             frac_shr(p, fmt->frac_shift);
             return;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 5/9] fpu: Add overflow_raises_invalid to FloatFmt
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
                   ` (3 preceding siblings ...)
  2026-02-05 11:16 ` [PATCH v4 4/9] fpu: Introduce FloatFmtExpMaxKind Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 6/9] fpu: Add saturate parameter to parts_uncanon Richard Henderson
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou

From: Max Chou <max.chou@sifive.com>

ARM Alt HP raises different exceptions on overflow than is
standard for IEEE when saturating a value.  Add a flag to
control this effect.

Signed-off-by: Max Chou <max.chou@sifive.com>
[rth: Split out of a larger patch]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 fpu/softfloat.c           | 6 +++++-
 fpu/softfloat-parts.c.inc | 4 +++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 18098c4cdd..209eb59b48 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -542,7 +542,9 @@ typedef enum __attribute__((__packed__)) {
  * The following optional modifiers are available:
  *   exp_max_kind: affects how exp == exp_max is interpreted
  *   has_explicit_bit: has an explicit integer bit; this affects whether
- *   the float_status floatx80_behaviour handling applies
+ *       the float_status floatx80_behaviour handling applies
+ *   overflow_raises_invalid: for float_expmax_normal, raise invalid
+ *       instead of overflow.
  */
 typedef struct {
     int exp_size;
@@ -553,6 +555,7 @@ typedef struct {
     int frac_shift;
     FloatFmtExpMaxKind exp_max_kind;
     bool has_explicit_bit;
+    bool overflow_raises_invalid;
     uint64_t round_mask;
 } FloatFmt;
 
@@ -576,6 +579,7 @@ static const FloatFmt float16_params = {
 static const FloatFmt float16_params_ahp = {
     FLOAT_PARAMS(5, 10),
     .exp_max_kind = float_expmax_normal,
+    .overflow_raises_invalid = true,
 };
 
 static const FloatFmt bfloat16_params = {
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc
index d3c9c7a913..eada83f7a4 100644
--- a/fpu/softfloat-parts.c.inc
+++ b/fpu/softfloat-parts.c.inc
@@ -348,7 +348,9 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
             case float_expmax_normal:
                 if (unlikely(exp > exp_max)) {
                     /* Overflow.  Return the maximum normal.  */
-                    flags = float_flag_invalid;
+                    flags = (fmt->overflow_raises_invalid
+                             ? float_flag_invalid
+                             : float_flag_overflow | float_flag_inexact);
                     exp = exp_max;
                     frac_allones(p);
                     p->frac_lo &= ~round_mask;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 6/9] fpu: Add saturate parameter to parts_uncanon
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
                   ` (4 preceding siblings ...)
  2026-02-05 11:16 ` [PATCH v4 5/9] fpu: Add overflow_raises_invalid to FloatFmt Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 7/9] fpu: Add conversion routines for OCP FP8 E5M2 Richard Henderson
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou

From: Max Chou <max.chou@sifive.com>

The OCP FP8 conversion operations have a parameter to control
saturate vs overflow.  Add a parameter, currently always false.

Signed-off-by: Max Chou <max.chou@sifive.com>
[rth: Split out of a larger patch]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 fpu/softfloat.c           | 44 +++++++++++++++++++--------------------
 fpu/softfloat-parts.c.inc | 15 +++++++------
 2 files changed, 31 insertions(+), 28 deletions(-)

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 209eb59b48..a637d28fd3 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -771,20 +771,20 @@ static void parts128_canonicalize(FloatParts128 *p, float_status *status,
     PARTS_GENERIC_64_128(canonicalize, A)(A, S, F)
 
 static void parts64_uncanon_normal(FloatParts64 *p, float_status *status,
-                                   const FloatFmt *fmt);
+                                   const FloatFmt *fmt, bool saturate);
 static void parts128_uncanon_normal(FloatParts128 *p, float_status *status,
-                                    const FloatFmt *fmt);
+                                    const FloatFmt *fmt, bool saturate);
 
-#define parts_uncanon_normal(A, S, F) \
-    PARTS_GENERIC_64_128(uncanon_normal, A)(A, S, F)
+#define parts_uncanon_normal(A, S, F, X) \
+    PARTS_GENERIC_64_128(uncanon_normal, A)(A, S, F, X)
 
 static void parts64_uncanon(FloatParts64 *p, float_status *status,
-                            const FloatFmt *fmt);
+                            const FloatFmt *fmt, bool saturate);
 static void parts128_uncanon(FloatParts128 *p, float_status *status,
-                             const FloatFmt *fmt);
+                             const FloatFmt *fmt, bool saturate);
 
-#define parts_uncanon(A, S, F) \
-    PARTS_GENERIC_64_128(uncanon, A)(A, S, F)
+#define parts_uncanon(A, S, F, X) \
+    PARTS_GENERIC_64_128(uncanon, A)(A, S, F, X)
 
 static void parts64_add_normal(FloatParts64 *a, FloatParts64 *b);
 static void parts128_add_normal(FloatParts128 *a, FloatParts128 *b);
@@ -1699,7 +1699,7 @@ static float16 float16a_round_pack_canonical(FloatParts64 *p,
                                              float_status *s,
                                              const FloatFmt *params)
 {
-    parts_uncanon(p, s, params);
+    parts_uncanon(p, s, params, false);
     return float16_pack_raw(p);
 }
 
@@ -1712,7 +1712,7 @@ static float16 float16_round_pack_canonical(FloatParts64 *p,
 static bfloat16 bfloat16_round_pack_canonical(FloatParts64 *p,
                                               float_status *s)
 {
-    parts_uncanon(p, s, &bfloat16_params);
+    parts_uncanon(p, s, &bfloat16_params, false);
     return bfloat16_pack_raw(p);
 }
 
@@ -1726,7 +1726,7 @@ static void float32_unpack_canonical(FloatParts64 *p, float32 f,
 static float32 float32_round_pack_canonical(FloatParts64 *p,
                                             float_status *s)
 {
-    parts_uncanon(p, s, &float32_params);
+    parts_uncanon(p, s, &float32_params, false);
     return float32_pack_raw(p);
 }
 
@@ -1740,7 +1740,7 @@ static void float64_unpack_canonical(FloatParts64 *p, float64 f,
 static float64 float64_round_pack_canonical(FloatParts64 *p,
                                             float_status *s)
 {
-    parts_uncanon(p, s, &float64_params);
+    parts_uncanon(p, s, &float64_params, false);
     return float64_pack_raw(p);
 }
 
@@ -1789,7 +1789,7 @@ static float64 float64r32_pack_raw(FloatParts64 *p)
 static float64 float64r32_round_pack_canonical(FloatParts64 *p,
                                                float_status *s)
 {
-    parts_uncanon(p, s, &float32_params);
+    parts_uncanon(p, s, &float32_params, false);
     return float64r32_pack_raw(p);
 }
 
@@ -1803,7 +1803,7 @@ static void float128_unpack_canonical(FloatParts128 *p, float128 f,
 static float128 float128_round_pack_canonical(FloatParts128 *p,
                                               float_status *s)
 {
-    parts_uncanon(p, s, &float128_params);
+    parts_uncanon(p, s, &float128_params, false);
     return float128_pack_raw(p);
 }
 
@@ -1851,7 +1851,7 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p,
     case float_class_normal:
     case float_class_denormal:
         if (s->floatx80_rounding_precision == floatx80_precision_x) {
-            parts_uncanon_normal(p, s, fmt);
+            parts_uncanon_normal(p, s, fmt, false);
             frac = p->frac_hi;
             exp = p->exp;
         } else {
@@ -1860,7 +1860,7 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p,
             p64.sign = p->sign;
             p64.exp = p->exp;
             frac_truncjam(&p64, p);
-            parts_uncanon_normal(&p64, s, fmt);
+            parts_uncanon_normal(&p64, s, fmt, false);
             frac = p64.frac;
             exp = p64.exp;
         }
@@ -2258,7 +2258,7 @@ float16_muladd_scalbn(float16 a, float16 b, float16 c,
     pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
 
     /* Round before applying negate result. */
-    parts_uncanon(pr, status, &float16_params);
+    parts_uncanon(pr, status, &float16_params, false);
     if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
         pr->sign ^= 1;
     }
@@ -2283,7 +2283,7 @@ float32_muladd_scalbn(float32 a, float32 b, float32 c,
     pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
 
     /* Round before applying negate result. */
-    parts_uncanon(pr, status, &float32_params);
+    parts_uncanon(pr, status, &float32_params, false);
     if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
         pr->sign ^= 1;
     }
@@ -2302,7 +2302,7 @@ float64_muladd_scalbn(float64 a, float64 b, float64 c,
     pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
 
     /* Round before applying negate result. */
-    parts_uncanon(pr, status, &float64_params);
+    parts_uncanon(pr, status, &float64_params, false);
     if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
         pr->sign ^= 1;
     }
@@ -2461,7 +2461,7 @@ float64 float64r32_muladd(float64 a, float64 b, float64 c,
     pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
 
     /* Round before applying negate result. */
-    parts_uncanon(pr, status, &float32_params);
+    parts_uncanon(pr, status, &float32_params, false);
     if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
         pr->sign ^= 1;
     }
@@ -2479,7 +2479,7 @@ bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c,
     pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
 
     /* Round before applying negate result. */
-    parts_uncanon(pr, status, &bfloat16_params);
+    parts_uncanon(pr, status, &bfloat16_params, false);
     if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
         pr->sign ^= 1;
     }
@@ -2497,7 +2497,7 @@ float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c,
     pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
 
     /* Round before applying negate result. */
-    parts_uncanon(pr, status, &float128_params);
+    parts_uncanon(pr, status, &float128_params, false);
     if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) {
         pr->sign ^= 1;
     }
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc
index eada83f7a4..1e1bbb14fd 100644
--- a/fpu/softfloat-parts.c.inc
+++ b/fpu/softfloat-parts.c.inc
@@ -258,9 +258,12 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
  * are FRAC_SHIFT bits that may require rounding at the bottom of the
  * fraction; these bits will be removed. The exponent will be biased
  * by EXP_BIAS and must be bounded by [EXP_MAX-1, 0].
+ *
+ * The saturate parameter controls saturation behavior for formats that
+ * support it -- when true, overflow produces max normal instead of infinity.
  */
 static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
-                                   const FloatFmt *fmt)
+                                   const FloatFmt *fmt, bool saturate)
 {
     const int exp_max = fmt->exp_max;
     const int frac_shift = fmt->frac_shift;
@@ -269,7 +272,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
     const uint64_t frac_lsbm1 = round_mask ^ (round_mask >> 1);
     const uint64_t roundeven_mask = round_mask | frac_lsb;
     uint64_t inc;
-    bool overflow_norm = false;
+    bool overflow_norm = saturate;
     int exp, flags = 0;
 
     switch (s->float_rounding_mode) {
@@ -294,11 +297,11 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
         break;
     case float_round_up:
         inc = p->sign ? 0 : round_mask;
-        overflow_norm = p->sign;
+        overflow_norm |= p->sign;
         break;
     case float_round_down:
         inc = p->sign ? round_mask : 0;
-        overflow_norm = !p->sign;
+        overflow_norm |= !p->sign;
         break;
     case float_round_to_odd:
         overflow_norm = true;
@@ -445,10 +448,10 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
 }
 
 static void partsN(uncanon)(FloatPartsN *p, float_status *s,
-                            const FloatFmt *fmt)
+                            const FloatFmt *fmt, bool saturate)
 {
     if (likely(is_anynorm(p->cls))) {
-        parts_uncanon_normal(p, s, fmt);
+        parts_uncanon_normal(p, s, fmt, saturate);
     } else {
         switch (p->cls) {
         case float_class_zero:
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 7/9] fpu: Add conversion routines for OCP FP8 E5M2
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
                   ` (5 preceding siblings ...)
  2026-02-05 11:16 ` [PATCH v4 6/9] fpu: Add saturate parameter to parts_uncanon Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3 Richard Henderson
  2026-02-05 11:16 ` [PATCH v4 9/9] fpu: Add conversion routines for OCP FP4 E2M1 Richard Henderson
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou

From: Max Chou <max.chou@sifive.com>

Signed-off-by: Max Chou <max.chou@sifive.com>
[rth: Split out of a larger patch]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 include/fpu/softfloat-types.h |  5 ++++
 include/fpu/softfloat.h       |  8 +++++
 fpu/softfloat.c               | 56 +++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)

diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h
index 8f82fdfc97..be7e2de6e3 100644
--- a/include/fpu/softfloat-types.h
+++ b/include/fpu/softfloat-types.h
@@ -119,6 +119,11 @@ typedef struct {
  */
 typedef uint16_t bfloat16;
 
+/*
+ * Open Compute Project (OCP) Microscaling Formats
+ */
+typedef uint8_t float8_e5m2;
+
 /*
  * Software IEC/IEEE floating-point underflow tininess-detection mode.
  */
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index ac6a392375..8f80726658 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -189,6 +189,14 @@ float128 int128_to_float128(Int128, float_status *status);
 float128 uint64_to_float128(uint64_t, float_status *status);
 float128 uint128_to_float128(Int128, float_status *status);
 
+/*----------------------------------------------------------------------------
+| OCP FP8 conversion routines.
+*----------------------------------------------------------------------------*/
+
+bfloat16 float8_e5m2_to_bfloat16(float8_e5m2, float_status *status);
+float8_e5m2 bfloat16_to_float8_e5m2(bfloat16, bool sat, float_status *status);
+float8_e5m2 float32_to_float8_e5m2(float32, bool sat, float_status *status);
+
 /*----------------------------------------------------------------------------
 | Software half-precision conversion routines.
 *----------------------------------------------------------------------------*/
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index a637d28fd3..ed259657d8 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -572,6 +572,10 @@ typedef struct {
     .frac_shift     = (-F - 1) & 63,                    \
     .round_mask     = (1ull << ((-F - 1) & 63)) - 1
 
+static const FloatFmt float8_e5m2_params = {
+    FLOAT_PARAMS(5, 2)
+};
+
 static const FloatFmt float16_params = {
     FLOAT_PARAMS(5, 10)
 };
@@ -627,6 +631,11 @@ static void unpack_raw64(FloatParts64 *r, const FloatFmt *fmt, uint64_t raw)
     };
 }
 
+static void QEMU_FLATTEN float8_e5m2_unpack_raw(FloatParts64 *p, float8_e5m2 f)
+{
+    unpack_raw64(p, &float8_e5m2_params, f);
+}
+
 static void QEMU_FLATTEN float16_unpack_raw(FloatParts64 *p, float16 f)
 {
     unpack_raw64(p, &float16_params, f);
@@ -684,6 +693,11 @@ static uint64_t pack_raw64(const FloatParts64 *p, const FloatFmt *fmt)
     return ret;
 }
 
+static float8_e5m2 QEMU_FLATTEN float8_e5m2_pack_raw(const FloatParts64 *p)
+{
+    return pack_raw64(p, &float8_e5m2_params);
+}
+
 static float16 QEMU_FLATTEN float16_pack_raw(const FloatParts64 *p)
 {
     return make_float16(pack_raw64(p, &float16_params));
@@ -1675,6 +1689,13 @@ static const uint16_t rsqrt_tab[128] = {
  * Pack/unpack routines with a specific FloatFmt.
  */
 
+static void float8_e5m2_unpack_canonical(FloatParts64 *p, float8_e5m2 f,
+                                         float_status *s)
+{
+    float8_e5m2_unpack_raw(p, f);
+    parts_canonicalize(p, s, &float8_e5m2_params);
+}
+
 static void float16a_unpack_canonical(FloatParts64 *p, float16 f,
                                       float_status *s, const FloatFmt *params)
 {
@@ -1695,6 +1716,14 @@ static void bfloat16_unpack_canonical(FloatParts64 *p, bfloat16 f,
     parts_canonicalize(p, s, &bfloat16_params);
 }
 
+static float8_e5m2 float8_e5m2_round_pack_canonical(FloatParts64 *p,
+                                                    float_status *s,
+                                                    bool saturate)
+{
+    parts_uncanon(p, s, &float8_e5m2_params, saturate);
+    return float8_e5m2_pack_raw(p);
+}
+
 static float16 float16a_round_pack_canonical(FloatParts64 *p,
                                              float_status *s,
                                              const FloatFmt *params)
@@ -2836,6 +2865,15 @@ static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b,
     }
 }
 
+bfloat16 float8_e5m2_to_bfloat16(float8_e5m2 a, float_status *s)
+{
+    FloatParts64 p;
+
+    float8_e5m2_unpack_canonical(&p, a, s);
+    parts_float_to_float(&p, s);
+    return bfloat16_round_pack_canonical(&p, s);
+}
+
 float32 float16_to_float32(float16 a, bool ieee, float_status *s)
 {
     const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp;
@@ -2856,6 +2894,15 @@ float64 float16_to_float64(float16 a, bool ieee, float_status *s)
     return float64_round_pack_canonical(&p, s);
 }
 
+float8_e5m2 float32_to_float8_e5m2(float32 a, bool saturate, float_status *s)
+{
+    FloatParts64 p;
+
+    float32_unpack_canonical(&p, a, s);
+    parts_float_to_float(&p, s);
+    return float8_e5m2_round_pack_canonical(&p, s, saturate);
+}
+
 float16 float32_to_float16(float32 a, bool ieee, float_status *s)
 {
     FloatParts64 p;
@@ -2923,6 +2970,15 @@ float32 float64_to_float32(float64 a, float_status *s)
     return float32_round_pack_canonical(&p, s);
 }
 
+float8_e5m2 bfloat16_to_float8_e5m2(bfloat16 a, bool saturate, float_status *s)
+{
+    FloatParts64 p;
+
+    bfloat16_unpack_canonical(&p, a, s);
+    parts_float_to_float(&p, s);
+    return float8_e5m2_round_pack_canonical(&p, s, saturate);
+}
+
 float32 bfloat16_to_float32(bfloat16 a, float_status *s)
 {
     FloatParts64 p;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
                   ` (6 preceding siblings ...)
  2026-02-05 11:16 ` [PATCH v4 7/9] fpu: Add conversion routines for OCP FP8 E5M2 Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  2026-02-16  4:22   ` Max Chou
  2026-02-05 11:16 ` [PATCH v4 9/9] fpu: Add conversion routines for OCP FP4 E2M1 Richard Henderson
  8 siblings, 1 reply; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou

From: Max Chou <max.chou@sifive.com>

Signed-off-by: Max Chou <max.chou@sifive.com>
[rth: Split out of a larger patch; adjust overflow detection.]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 include/fpu/softfloat-types.h |  1 +
 include/fpu/softfloat.h       |  4 +++
 fpu/softfloat.c               | 62 +++++++++++++++++++++++++++++++++++
 fpu/softfloat-parts.c.inc     | 45 +++++++++++++++++++++++--
 4 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h
index be7e2de6e3..9c84a101e5 100644
--- a/include/fpu/softfloat-types.h
+++ b/include/fpu/softfloat-types.h
@@ -122,6 +122,7 @@ typedef uint16_t bfloat16;
 /*
  * Open Compute Project (OCP) Microscaling Formats
  */
+typedef uint8_t float8_e4m3;
 typedef uint8_t float8_e5m2;
 
 /*
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index 8f80726658..e45260f52d 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -193,6 +193,10 @@ float128 uint128_to_float128(Int128, float_status *status);
 | OCP FP8 conversion routines.
 *----------------------------------------------------------------------------*/
 
+bfloat16 float8_e4m3_to_bfloat16(float8_e4m3, float_status *status);
+float8_e4m3 bfloat16_to_float8_e4m3(bfloat16, bool sat, float_status *status);
+float8_e4m3 float32_to_float8_e4m3(float32, bool sat, float_status *status);
+
 bfloat16 float8_e5m2_to_bfloat16(float8_e5m2, float_status *status);
 float8_e5m2 bfloat16_to_float8_e5m2(bfloat16, bool sat, float_status *status);
 float8_e5m2 float32_to_float8_e5m2(float32, bool sat, float_status *status);
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index ed259657d8..9e31ddd85a 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -528,6 +528,8 @@ typedef enum __attribute__((__packed__)) {
     float_expmax_ieee,
     /* exp==max is a normal number; no infinity or nan representation. */
     float_expmax_normal,
+    /* exp==max, frac==max ? nan : normal; no infinity representation. */
+    float_expmax_e4m3,
 } FloatFmtExpMaxKind;
 
 /*
@@ -572,6 +574,14 @@ typedef struct {
     .frac_shift     = (-F - 1) & 63,                    \
     .round_mask     = (1ull << ((-F - 1) & 63)) - 1
 
+static const FloatFmt float8_e4m3_params = {
+    FLOAT_PARAMS(4, 3),
+    .exp_max_kind = float_expmax_e4m3
+};
+
+/* 110 << frac_shift, with the implicit bit set */
+#define E4M3_NORMAL_FRAC_MAX  0xe000000000000000ull
+
 static const FloatFmt float8_e5m2_params = {
     FLOAT_PARAMS(5, 2)
 };
@@ -631,6 +641,11 @@ static void unpack_raw64(FloatParts64 *r, const FloatFmt *fmt, uint64_t raw)
     };
 }
 
+static void QEMU_FLATTEN float8_e4m3_unpack_raw(FloatParts64 *p, float8_e4m3 f)
+{
+    unpack_raw64(p, &float8_e4m3_params, f);
+}
+
 static void QEMU_FLATTEN float8_e5m2_unpack_raw(FloatParts64 *p, float8_e5m2 f)
 {
     unpack_raw64(p, &float8_e5m2_params, f);
@@ -693,6 +708,11 @@ static uint64_t pack_raw64(const FloatParts64 *p, const FloatFmt *fmt)
     return ret;
 }
 
+static float8_e4m3 QEMU_FLATTEN float8_e4m3_pack_raw(const FloatParts64 *p)
+{
+    return pack_raw64(p, &float8_e4m3_params);
+}
+
 static float8_e5m2 QEMU_FLATTEN float8_e5m2_pack_raw(const FloatParts64 *p)
 {
     return pack_raw64(p, &float8_e5m2_params);
@@ -1689,6 +1709,13 @@ static const uint16_t rsqrt_tab[128] = {
  * Pack/unpack routines with a specific FloatFmt.
  */
 
+static void float8_e4m3_unpack_canonical(FloatParts64 *p, float8_e4m3 f,
+                                         float_status *s)
+{
+    float8_e4m3_unpack_raw(p, f);
+    parts_canonicalize(p, s, &float8_e4m3_params);
+}
+
 static void float8_e5m2_unpack_canonical(FloatParts64 *p, float8_e5m2 f,
                                          float_status *s)
 {
@@ -1716,6 +1743,14 @@ static void bfloat16_unpack_canonical(FloatParts64 *p, bfloat16 f,
     parts_canonicalize(p, s, &bfloat16_params);
 }
 
+static float8_e4m3 float8_e4m3_round_pack_canonical(FloatParts64 *p,
+                                                    float_status *s,
+                                                    bool saturate)
+{
+    parts_uncanon(p, s, &float8_e4m3_params, saturate);
+    return float8_e4m3_pack_raw(p);
+}
+
 static float8_e5m2 float8_e5m2_round_pack_canonical(FloatParts64 *p,
                                                     float_status *s,
                                                     bool saturate)
@@ -2865,6 +2900,15 @@ static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b,
     }
 }
 
+bfloat16 float8_e4m3_to_bfloat16(float8_e4m3 a, float_status *s)
+{
+    FloatParts64 p;
+
+    float8_e4m3_unpack_canonical(&p, a, s);
+    parts_float_to_float(&p, s);
+    return bfloat16_round_pack_canonical(&p, s);
+}
+
 bfloat16 float8_e5m2_to_bfloat16(float8_e5m2 a, float_status *s)
 {
     FloatParts64 p;
@@ -2894,6 +2938,15 @@ float64 float16_to_float64(float16 a, bool ieee, float_status *s)
     return float64_round_pack_canonical(&p, s);
 }
 
+float8_e4m3 float32_to_float8_e4m3(float32 a, bool saturate, float_status *s)
+{
+    FloatParts64 p;
+
+    float32_unpack_canonical(&p, a, s);
+    parts_float_to_float(&p, s);
+    return float8_e4m3_round_pack_canonical(&p, s, saturate);
+}
+
 float8_e5m2 float32_to_float8_e5m2(float32 a, bool saturate, float_status *s)
 {
     FloatParts64 p;
@@ -2970,6 +3023,15 @@ float32 float64_to_float32(float64 a, float_status *s)
     return float32_round_pack_canonical(&p, s);
 }
 
+float8_e4m3 bfloat16_to_float8_e4m3(bfloat16 a, bool saturate, float_status *s)
+{
+    FloatParts64 p;
+
+    bfloat16_unpack_canonical(&p, a, s);
+    parts_float_to_float(&p, s);
+    return float8_e4m3_round_pack_canonical(&p, s, saturate);
+}
+
 float8_e5m2 bfloat16_to_float8_e5m2(bfloat16 a, bool saturate, float_status *s)
 {
     FloatParts64 p;
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc
index 1e1bbb14fd..a45873c51f 100644
--- a/fpu/softfloat-parts.c.inc
+++ b/fpu/softfloat-parts.c.inc
@@ -242,6 +242,15 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
             return;
         case float_expmax_normal:
             break;
+        case float_expmax_e4m3:
+            if (p->frac_hi == 0b111) {
+                frac_shl(p, fmt->frac_shift);
+                p->cls = (parts_is_snan_frac(p->frac_hi, status)
+                          ? float_class_snan : float_class_qnan);
+                return;
+            }
+            /* otherwise normal */
+            break;
         default:
             g_assert_not_reached();
         }
@@ -262,6 +271,21 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
  * The saturate parameter controls saturation behavior for formats that
  * support it -- when true, overflow produces max normal instead of infinity.
  */
+
+/* Helper for uncanon_normal and uncanon, for FP8 E4M3. */
+static void partsN(uncanon_e4m3_overflow)(FloatPartsN *p, float_status *s,
+                                          const FloatFmt *fmt, bool saturate)
+{
+    assert(N == 64);
+    float_raise(float_flag_overflow | float_flag_inexact, s);
+    if (saturate) {
+        p->exp = fmt->exp_max;
+        p->frac_hi = E4M3_NORMAL_FRAC_MAX;
+    } else {
+        parts_default_nan(p, s);
+    }
+}
+
 static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
                                    const FloatFmt *fmt, bool saturate)
 {
@@ -360,6 +384,12 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
                 }
                 break;
 
+            case float_expmax_e4m3:
+                if (exp > exp_max || p->frac_hi > E4M3_NORMAL_FRAC_MAX) {
+                    partsN(uncanon_e4m3_overflow)(p, s, fmt, overflow_norm);
+                }
+                break;
+
             default:
                 g_assert_not_reached();
             }
@@ -459,9 +489,18 @@ static void partsN(uncanon)(FloatPartsN *p, float_status *s,
             frac_clear(p);
             return;
         case float_class_inf:
-            assert(fmt->exp_max_kind == float_expmax_ieee);
-            p->exp = fmt->exp_max;
-            frac_clear(p);
+            switch (fmt->exp_max_kind) {
+            case float_expmax_ieee:
+                p->exp = fmt->exp_max;
+                frac_clear(p);
+                break;
+            case float_expmax_e4m3:
+                partsN(uncanon_e4m3_overflow)(p, s, fmt, saturate);
+                break;
+            case float_expmax_normal:
+            default:
+                g_assert_not_reached();
+            }
             return;
         case float_class_qnan:
         case float_class_snan:
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 9/9] fpu: Add conversion routines for OCP FP4 E2M1
  2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
                   ` (7 preceding siblings ...)
  2026-02-05 11:16 ` [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3 Richard Henderson
@ 2026-02-05 11:16 ` Richard Henderson
  8 siblings, 0 replies; 16+ messages in thread
From: Richard Henderson @ 2026-02-05 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: max.chou

From: Max Chou <max.chou@sifive.com>

Signed-off-by: Max Chou <max.chou@sifive.com>
[rth: Update for exp_max_kind]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 include/fpu/softfloat-types.h |  1 +
 include/fpu/softfloat.h       |  4 +++-
 fpu/softfloat.c               | 26 ++++++++++++++++++++++++++
 3 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h
index 9c84a101e5..ead146c22d 100644
--- a/include/fpu/softfloat-types.h
+++ b/include/fpu/softfloat-types.h
@@ -122,6 +122,7 @@ typedef uint16_t bfloat16;
 /*
  * Open Compute Project (OCP) Microscaling Formats
  */
+typedef uint8_t float4_e2m1;
 typedef uint8_t float8_e4m3;
 typedef uint8_t float8_e5m2;
 
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index e45260f52d..1580d956d5 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -190,9 +190,11 @@ float128 uint64_to_float128(uint64_t, float_status *status);
 float128 uint128_to_float128(Int128, float_status *status);
 
 /*----------------------------------------------------------------------------
-| OCP FP8 conversion routines.
+| OCP FP{4,8} conversion routines.
 *----------------------------------------------------------------------------*/
 
+float8_e4m3 float4_e2m1_to_float8_e4m3(float4_e2m1, float_status *status);
+
 bfloat16 float8_e4m3_to_bfloat16(float8_e4m3, float_status *status);
 float8_e4m3 bfloat16_to_float8_e4m3(bfloat16, bool sat, float_status *status);
 float8_e4m3 float32_to_float8_e4m3(float32, bool sat, float_status *status);
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 9e31ddd85a..a8ba3cbdd1 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -574,6 +574,11 @@ typedef struct {
     .frac_shift     = (-F - 1) & 63,                    \
     .round_mask     = (1ull << ((-F - 1) & 63)) - 1
 
+static const FloatFmt float4_e2m1_params = {
+    FLOAT_PARAMS(2, 1),
+    .exp_max_kind = float_expmax_normal,
+};
+
 static const FloatFmt float8_e4m3_params = {
     FLOAT_PARAMS(4, 3),
     .exp_max_kind = float_expmax_e4m3
@@ -641,6 +646,11 @@ static void unpack_raw64(FloatParts64 *r, const FloatFmt *fmt, uint64_t raw)
     };
 }
 
+static void QEMU_FLATTEN float4_e2m1_unpack_raw(FloatParts64 *p, float4_e2m1 f)
+{
+    unpack_raw64(p, &float4_e2m1_params, f);
+}
+
 static void QEMU_FLATTEN float8_e4m3_unpack_raw(FloatParts64 *p, float8_e4m3 f)
 {
     unpack_raw64(p, &float8_e4m3_params, f);
@@ -1709,6 +1719,13 @@ static const uint16_t rsqrt_tab[128] = {
  * Pack/unpack routines with a specific FloatFmt.
  */
 
+static void float4_e2m1_unpack_canonical(FloatParts64 *p, float4_e2m1 f,
+                                         float_status *s)
+{
+    float4_e2m1_unpack_raw(p, f);
+    parts_canonicalize(p, s, &float4_e2m1_params);
+}
+
 static void float8_e4m3_unpack_canonical(FloatParts64 *p, float8_e4m3 f,
                                          float_status *s)
 {
@@ -2900,6 +2917,15 @@ static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b,
     }
 }
 
+float8_e4m3 float4_e2m1_to_float8_e4m3(float4_e2m1 a, float_status *s)
+{
+    FloatParts64 p;
+
+    float4_e2m1_unpack_canonical(&p, a, s);
+    parts_float_to_float(&p, s);
+    return float8_e4m3_round_pack_canonical(&p, s, false);
+}
+
 bfloat16 float8_e4m3_to_bfloat16(float8_e4m3 a, float_status *s)
 {
     FloatParts64 p;
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3
  2026-02-05 11:16 ` [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3 Richard Henderson
@ 2026-02-16  4:22   ` Max Chou
  2026-02-16  5:04     ` Richard Henderson
  2026-02-16  5:30     ` Richard Henderson
  0 siblings, 2 replies; 16+ messages in thread
From: Max Chou @ 2026-02-16  4:22 UTC (permalink / raw)
  To: Richard Henderson; +Cc: qemu-devel

On 2026-02-05 21:16, Richard Henderson wrote:
> index 1e1bbb14fd..a45873c51f 100644
> --- a/fpu/softfloat-parts.c.inc
> +++ b/fpu/softfloat-parts.c.inc
> @@ -360,6 +384,12 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
>                  }
>                  break;
>  
> +            case float_expmax_e4m3:
> +                if (exp > exp_max || p->frac_hi > E4M3_NORMAL_FRAC_MAX) {
> +                    partsN(uncanon_e4m3_overflow)(p, s, fmt, overflow_norm);
We need to sync local exp here liked:
+                      exp = exp_max;

> +                }
> +                break;
> +

> @@ -459,9 +489,18 @@ static void partsN(uncanon)(FloatPartsN *p, float_status *s,
>              frac_clear(p);
>              return;
>          case float_class_inf:
> -            assert(fmt->exp_max_kind == float_expmax_ieee);
> -            p->exp = fmt->exp_max;
> -            frac_clear(p);
> +            switch (fmt->exp_max_kind) {
> +            case float_expmax_ieee:
> +                p->exp = fmt->exp_max;
> +                frac_clear(p);
> +                break;

Thanks for Chao's notification. Here I missed the saturate checking at v3.
We need following checking flow for E5M2 when when float_expmax_ieee.
+                if (saturate) {
+                    p->exp = fmt->exp_max - 1;
+                    frac_allones(p);
+                } else {
+                    p->exp = fmt->exp_max;
+                    frac_clear(p);
+                }

> +            case float_expmax_e4m3:
> +                partsN(uncanon_e4m3_overflow)(p, s, fmt, saturate);
And we need to shift frac to packed format liked:
+                frac_shr(p, fmt->frac_shift);

> +                break;
> +            case float_expmax_normal:
> +            default:
> +                g_assert_not_reached();
> +            }

Thank you Richard for this v4 patchset.
I can pass Chao's test and other test with the fix modifications
suggested here.
Could you please let me know if you’re okay with the softfloat OCP FP
conversion v4 patchset containing the OCP FP classification functions?
(e.g. float8_[e4m3|e5m2]_is_[zero|normal|signaling_nan|quiet_nan], etc.)
If not, I could provide another patchset for these OCP FP classification
funtions.

Thanks,
rnax


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3
  2026-02-16  4:22   ` Max Chou
@ 2026-02-16  5:04     ` Richard Henderson
  2026-02-20  4:39       ` Max Chou
  2026-02-16  5:30     ` Richard Henderson
  1 sibling, 1 reply; 16+ messages in thread
From: Richard Henderson @ 2026-02-16  5:04 UTC (permalink / raw)
  To: Max Chou; +Cc: qemu-devel

On 2/16/26 14:22, Max Chou wrote:
> Could you please let me know if you’re okay with the softfloat OCP FP
> conversion v4 patchset containing the OCP FP classification functions?
> (e.g. float8_[e4m3|e5m2]_is_[zero|normal|signaling_nan|quiet_nan], etc.)
> If not, I could provide another patchset for these OCP FP classification
> funtions.

Do you use those in target/riscv somewhere?
I didn't include them simply because they seemed useless.


r~


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3
  2026-02-16  4:22   ` Max Chou
  2026-02-16  5:04     ` Richard Henderson
@ 2026-02-16  5:30     ` Richard Henderson
  2026-02-20  5:44       ` Max Chou
  1 sibling, 1 reply; 16+ messages in thread
From: Richard Henderson @ 2026-02-16  5:30 UTC (permalink / raw)
  To: Max Chou; +Cc: qemu-devel

On 2/16/26 14:22, Max Chou wrote:
>> @@ -459,9 +489,18 @@ static void partsN(uncanon)(FloatPartsN *p, float_status *s,
>>               frac_clear(p);
>>               return;
>>           case float_class_inf:
>> -            assert(fmt->exp_max_kind == float_expmax_ieee);
>> -            p->exp = fmt->exp_max;
>> -            frac_clear(p);
>> +            switch (fmt->exp_max_kind) {
>> +            case float_expmax_ieee:
>> +                p->exp = fmt->exp_max;
>> +                frac_clear(p);
>> +                break;
> 
> Thanks for Chao's notification. Here I missed the saturate checking at v3.
> We need following checking flow for E5M2 when when float_expmax_ieee.
> +                if (saturate) {
> +                    p->exp = fmt->exp_max - 1;
> +                    frac_allones(p);
> +                } else {
> +                    p->exp = fmt->exp_max;
> +                    frac_clear(p);
> +                }

It doesn't feel right doing this here, because true overflow is handled elsewhere.  This 
is re-packing a FloatParts for a given FloatFmt.

Perhaps parts64_float_to_float() would be the proper place for this, as that's the 
conversion operation, and recognizing this overflow feels like part of a conversion.


r~


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3
  2026-02-16  5:04     ` Richard Henderson
@ 2026-02-20  4:39       ` Max Chou
  0 siblings, 0 replies; 16+ messages in thread
From: Max Chou @ 2026-02-20  4:39 UTC (permalink / raw)
  To: Richard Henderson; +Cc: qemu-devel

On 2026-02-16 15:04, Richard Henderson wrote:
> Do you use those in target/riscv somewhere?
> I didn't include them simply because they seemed useless.
> 

No,the target/riscv part does not use these classification functions yet.
Got it.

Thanks
rnax

> 
> r~


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3
  2026-02-16  5:30     ` Richard Henderson
@ 2026-02-20  5:44       ` Max Chou
  2026-02-20  6:33         ` Chao Liu
  0 siblings, 1 reply; 16+ messages in thread
From: Max Chou @ 2026-02-20  5:44 UTC (permalink / raw)
  To: Richard Henderson; +Cc: qemu-devel

On 2026-02-16 15:30, Richard Henderson wrote:
> It doesn't feel right doing this here, because true overflow is handled
> elsewhere.  This is re-packing a FloatParts for a given FloatFmt.
> 
> Perhaps parts64_float_to_float() would be the proper place for this, as
> that's the conversion operation, and recognizing this overflow feels like
> part of a conversion.

Hmm, I agree with you. Maybe we could add a new float_to_e5m2 similar to
float_to_ahp and replacing the parts_float_to_float calls in
float32_to_float8_e5m2/bfloat16_to_float8_e5m2.

+static void parts_float_to_e5m2(FloatParts64 *a, float_status *s,
+                                bool saturate)
+{
+    switch (a->cls) {
+    case float_class_snan:
+    case float_class_qnan:
+        parts_return_nan(a, s);
+        break;
+    case float_class_inf:
+        if (saturate) {
+            float_raise(float_flag_invalid, s);
+            a->cls = float_class_normal;
+            a->exp = float8_e5m2_params.exp_max - 1;
+            a->frac = MAKE_64BIT_MASK(float8_e5m2_params.frac_shift,
+                                      float8_e5m2_params.frac_size + 1);
+        }
+        break;
+    case float_class_denormal:
+        float_raise(float_flag_input_denormal_used, s);
+        break;
+    case float_class_normal:
+    case float_class_zero:
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}

Thanks for the feedbacks,
rnax


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3
  2026-02-20  5:44       ` Max Chou
@ 2026-02-20  6:33         ` Chao Liu
  0 siblings, 0 replies; 16+ messages in thread
From: Chao Liu @ 2026-02-20  6:33 UTC (permalink / raw)
  To: Max Chou; +Cc: Richard Henderson, qemu-devel

On Fri, Feb 20, 2026 at 01:44:27PM +0800, Max Chou wrote:
> On 2026-02-16 15:30, Richard Henderson wrote:
> > It doesn't feel right doing this here, because true overflow is handled
> > elsewhere.  This is re-packing a FloatParts for a given FloatFmt.
> > 
> > Perhaps parts64_float_to_float() would be the proper place for this, as
> > that's the conversion operation, and recognizing this overflow feels like
> > part of a conversion.
> 
> Hmm, I agree with you. Maybe we could add a new float_to_e5m2 similar to
> float_to_ahp and replacing the parts_float_to_float calls in
> float32_to_float8_e5m2/bfloat16_to_float8_e5m2.
> 
> +static void parts_float_to_e5m2(FloatParts64 *a, float_status *s,
> +                                bool saturate)
> +{
> +    switch (a->cls) {
> +    case float_class_snan:
> +    case float_class_qnan:
> +        parts_return_nan(a, s);
> +        break;
> +    case float_class_inf:
> +        if (saturate) {
> +            float_raise(float_flag_invalid, s);
> +            a->cls = float_class_normal;
> +            a->exp = float8_e5m2_params.exp_max - 1;
> +            a->frac = MAKE_64BIT_MASK(float8_e5m2_params.frac_shift,
> +                                      float8_e5m2_params.frac_size + 1);
> +        }
> +        break;
> +    case float_class_denormal:
> +        float_raise(float_flag_input_denormal_used, s);
> +        break;
> +    case float_class_normal:
> +    case float_class_zero:
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }
> +}
> 
Hi rnax,

This approach looks good to me. Adding a dedicated parts_float_to_e5m2
following the float_to_ahp pattern makes sense -- it keeps the infinity
saturation logic in the conversion path where it belongs, rather than in
the generic uncanon_sat repacking.

Thanks,
Chao

> Thanks for the feedbacks,
> rnax
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-02-20  6:44 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-05 11:16 [PATCH v4 0/9] fpu: Add conversion routines for OCP FP{4,8} Richard Henderson
2026-02-05 11:16 ` [PATCH v4 1/9] fpu/softfloat: Refactor IEEE format NaN classification to share code Richard Henderson
2026-02-05 11:16 ` [PATCH v4 2/9] fpu/softfloat: Refactor floatx80 " Richard Henderson
2026-02-05 11:16 ` [PATCH v4 3/9] target/i386: Fix pseudo-NaN handling in FPATAN/FYL2XP1/FYL2X helpers Richard Henderson
2026-02-05 11:16 ` [PATCH v4 4/9] fpu: Introduce FloatFmtExpMaxKind Richard Henderson
2026-02-05 11:16 ` [PATCH v4 5/9] fpu: Add overflow_raises_invalid to FloatFmt Richard Henderson
2026-02-05 11:16 ` [PATCH v4 6/9] fpu: Add saturate parameter to parts_uncanon Richard Henderson
2026-02-05 11:16 ` [PATCH v4 7/9] fpu: Add conversion routines for OCP FP8 E5M2 Richard Henderson
2026-02-05 11:16 ` [PATCH v4 8/9] fpu: Add conversion routines for OCP FP8 E4M3 Richard Henderson
2026-02-16  4:22   ` Max Chou
2026-02-16  5:04     ` Richard Henderson
2026-02-20  4:39       ` Max Chou
2026-02-16  5:30     ` Richard Henderson
2026-02-20  5:44       ` Max Chou
2026-02-20  6:33         ` Chao Liu
2026-02-05 11:16 ` [PATCH v4 9/9] fpu: Add conversion routines for OCP FP4 E2M1 Richard Henderson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.