@samitouri / QOSamiQemu / commits / 27e989f99c

fpu: Add conversion routines for OCP FP8 E4M3

Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.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>

Max Chou committed Feb 5, 2026 at 20:45 UTC 27e989f99c87adc522fc436e1ef3c9cfbdf30d61
4 files changed +109 -3
fpu/softfloat-parts.c.inc
+42 -3
@@ -242,6 +242,15 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
242 return;
243 case float_expmax_normal:
244 break;
245 + case float_expmax_e4m3:
246 + if (p->frac_hi == 0b111) {
247 + frac_shl(p, fmt->frac_shift);
248 + p->cls = (parts_is_snan_frac(p->frac_hi, status)
249 + ? float_class_snan : float_class_qnan);
250 + return;
251 + }
252 + /* otherwise normal */
253 + break;
254 default:
255 g_assert_not_reached();
256 }
@@ -262,6 +271,21 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
271 * The saturate parameter controls saturation behavior for formats that
272 * support it -- when true, overflow produces max normal instead of infinity.
273 */
274 +
275 +/* Helper for uncanon_normal and uncanon, for FP8 E4M3. */
276 +static void partsN(uncanon_e4m3_overflow)(FloatPartsN *p, float_status *s,
277 + const FloatFmt *fmt, bool saturate)
278 +{
279 + assert(N == 64);
280 + float_raise(float_flag_overflow | float_flag_inexact, s);
281 + if (saturate) {
282 + p->exp = fmt->exp_max;
283 + p->frac_hi = E4M3_NORMAL_FRAC_MAX;
284 + } else {
285 + parts_default_nan(p, s);
286 + }
287 +}
288 +
289 static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
290 const FloatFmt *fmt, bool saturate)
291 {
@@ -360,6 +384,12 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
384 }
385 break;
386
387 + case float_expmax_e4m3:
388 + if (exp > exp_max || p->frac_hi > E4M3_NORMAL_FRAC_MAX) {
389 + partsN(uncanon_e4m3_overflow)(p, s, fmt, overflow_norm);
390 + }
391 + break;
392 +
393 default:
394 g_assert_not_reached();
395 }
@@ -459,9 +489,18 @@ static void partsN(uncanon)(FloatPartsN *p, float_status *s,
489 frac_clear(p);
490 return;
491 case float_class_inf:
462 - assert(fmt->exp_max_kind == float_expmax_ieee);
463 - p->exp = fmt->exp_max;
464 - frac_clear(p);
492 + switch (fmt->exp_max_kind) {
493 + case float_expmax_ieee:
494 + p->exp = fmt->exp_max;
495 + frac_clear(p);
496 + break;
497 + case float_expmax_e4m3:
498 + partsN(uncanon_e4m3_overflow)(p, s, fmt, saturate);
499 + break;
500 + case float_expmax_normal:
501 + default:
502 + g_assert_not_reached();
503 + }
504 return;
505 case float_class_qnan:
506 case float_class_snan:
fpu/softfloat.c
+62
@@ -528,6 +528,8 @@ typedef enum __attribute__((__packed__)) {
528 float_expmax_ieee,
529 /* exp==max is a normal number; no infinity or nan representation. */
530 float_expmax_normal,
531 + /* exp==max, frac==max ? nan : normal; no infinity representation. */
532 + float_expmax_e4m3,
533 } FloatFmtExpMaxKind;
534
535 /*
@@ -572,6 +574,14 @@ typedef struct {
574 .frac_shift = (-F - 1) & 63, \
575 .round_mask = (1ull << ((-F - 1) & 63)) - 1
576
577 +static const FloatFmt float8_e4m3_params = {
578 + FLOAT_PARAMS(4, 3),
579 + .exp_max_kind = float_expmax_e4m3
580 +};
581 +
582 +/* 110 << frac_shift, with the implicit bit set */
583 +#define E4M3_NORMAL_FRAC_MAX 0xe000000000000000ull
584 +
585 static const FloatFmt float8_e5m2_params = {
586 FLOAT_PARAMS(5, 2)
587 };
@@ -631,6 +641,11 @@ static void unpack_raw64(FloatParts64 *r, const FloatFmt *fmt, uint64_t raw)
641 };
642 }
643
644 +static void QEMU_FLATTEN float8_e4m3_unpack_raw(FloatParts64 *p, float8_e4m3 f)
645 +{
646 + unpack_raw64(p, &float8_e4m3_params, f);
647 +}
648 +
649 static void QEMU_FLATTEN float8_e5m2_unpack_raw(FloatParts64 *p, float8_e5m2 f)
650 {
651 unpack_raw64(p, &float8_e5m2_params, f);
@@ -693,6 +708,11 @@ static uint64_t pack_raw64(const FloatParts64 *p, const FloatFmt *fmt)
708 return ret;
709 }
710
711 +static float8_e4m3 QEMU_FLATTEN float8_e4m3_pack_raw(const FloatParts64 *p)
712 +{
713 + return pack_raw64(p, &float8_e4m3_params);
714 +}
715 +
716 static float8_e5m2 QEMU_FLATTEN float8_e5m2_pack_raw(const FloatParts64 *p)
717 {
718 return pack_raw64(p, &float8_e5m2_params);
@@ -1689,6 +1709,13 @@ static const uint16_t rsqrt_tab[128] = {
1709 * Pack/unpack routines with a specific FloatFmt.
1710 */
1711
1712 +static void float8_e4m3_unpack_canonical(FloatParts64 *p, float8_e4m3 f,
1713 + float_status *s)
1714 +{
1715 + float8_e4m3_unpack_raw(p, f);
1716 + parts_canonicalize(p, s, &float8_e4m3_params);
1717 +}
1718 +
1719 static void float8_e5m2_unpack_canonical(FloatParts64 *p, float8_e5m2 f,
1720 float_status *s)
1721 {
@@ -1716,6 +1743,14 @@ static void bfloat16_unpack_canonical(FloatParts64 *p, bfloat16 f,
1743 parts_canonicalize(p, s, &bfloat16_params);
1744 }
1745
1746 +static float8_e4m3 float8_e4m3_round_pack_canonical(FloatParts64 *p,
1747 + float_status *s,
1748 + bool saturate)
1749 +{
1750 + parts_uncanon(p, s, &float8_e4m3_params, saturate);
1751 + return float8_e4m3_pack_raw(p);
1752 +}
1753 +
1754 static float8_e5m2 float8_e5m2_round_pack_canonical(FloatParts64 *p,
1755 float_status *s,
1756 bool saturate)
@@ -2894,6 +2929,15 @@ static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b,
2929 }
2930 }
2931
2932 +bfloat16 float8_e4m3_to_bfloat16(float8_e4m3 a, float_status *s)
2933 +{
2934 + FloatParts64 p;
2935 +
2936 + float8_e4m3_unpack_canonical(&p, a, s);
2937 + parts_float_to_float(&p, s);
2938 + return bfloat16_round_pack_canonical(&p, s);
2939 +}
2940 +
2941 bfloat16 float8_e5m2_to_bfloat16(float8_e5m2 a, float_status *s)
2942 {
2943 FloatParts64 p;
@@ -2923,6 +2967,15 @@ float64 float16_to_float64(float16 a, bool ieee, float_status *s)
2967 return float64_round_pack_canonical(&p, s);
2968 }
2969
2970 +float8_e4m3 float32_to_float8_e4m3(float32 a, bool saturate, float_status *s)
2971 +{
2972 + FloatParts64 p;
2973 +
2974 + float32_unpack_canonical(&p, a, s);
2975 + parts_float_to_float(&p, s);
2976 + return float8_e4m3_round_pack_canonical(&p, s, saturate);
2977 +}
2978 +
2979 float8_e5m2 float32_to_float8_e5m2(float32 a, bool saturate, float_status *s)
2980 {
2981 FloatParts64 p;
@@ -2999,6 +3052,15 @@ float32 float64_to_float32(float64 a, float_status *s)
3052 return float32_round_pack_canonical(&p, s);
3053 }
3054
3055 +float8_e4m3 bfloat16_to_float8_e4m3(bfloat16 a, bool saturate, float_status *s)
3056 +{
3057 + FloatParts64 p;
3058 +
3059 + bfloat16_unpack_canonical(&p, a, s);
3060 + parts_float_to_float(&p, s);
3061 + return float8_e4m3_round_pack_canonical(&p, s, saturate);
3062 +}
3063 +
3064 float8_e5m2 bfloat16_to_float8_e5m2(bfloat16 a, bool saturate, float_status *s)
3065 {
3066 FloatParts64 p;
include/fpu/softfloat-types.h
+1
@@ -122,6 +122,7 @@ typedef uint16_t bfloat16;
122 /*
123 * Open Compute Project (OCP) Microscaling Formats
124 */
125 +typedef uint8_t float8_e4m3;
126 typedef uint8_t float8_e5m2;
127
128 /*
include/fpu/softfloat.h
+4
@@ -193,6 +193,10 @@ float128 uint128_to_float128(Int128, float_status *status);
193 | OCP FP8 conversion routines.
194 *----------------------------------------------------------------------------*/
195
196 +bfloat16 float8_e4m3_to_bfloat16(float8_e4m3, float_status *status);
197 +float8_e4m3 bfloat16_to_float8_e4m3(bfloat16, bool sat, float_status *status);
198 +float8_e4m3 float32_to_float8_e4m3(float32, bool sat, float_status *status);
199 +
200 bfloat16 float8_e5m2_to_bfloat16(float8_e5m2, float_status *status);
201 float8_e5m2 bfloat16_to_float8_e5m2(bfloat16, bool sat, float_status *status);
202 float8_e5m2 float32_to_float8_e5m2(float32, bool sat, float_status *status);