[PATCH v5 4/9] fpu: Introduce FloatFmtExpMaxKind

Richard Henderson posted 9 patches 1 month, 2 weeks ago
Maintainers: Aurelien Jarno <aurelien@aurel32.net>, Peter Maydell <peter.maydell@linaro.org>, "Alex Bennée" <alex.bennee@linaro.org>, Paolo Bonzini <pbonzini@redhat.com>, Richard Henderson <richard.henderson@linaro.org>, Eduardo Habkost <eduardo@habkost.net>
[PATCH v5 4/9] fpu: Introduce FloatFmtExpMaxKind
Posted by Richard Henderson 1 month, 2 weeks ago
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 8740975348..8d8e576757 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 79b56014ab..455bbf281e 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
Re: [PATCH v5 4/9] fpu: Introduce FloatFmtExpMaxKind
Posted by Chao Liu 1 month, 2 weeks ago
On Mon, Feb 23, 2026 at 07:21:49PM +1100, Richard Henderson wrote:
> 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 8740975348..8d8e576757 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 79b56014ab..455bbf281e 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
> 

Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>

Thanks,
Chao