diff options
Diffstat (limited to 'fpu')
| -rw-r--r-- | fpu/softfloat-parts.c.inc | 57 | ||||
| -rw-r--r-- | fpu/softfloat-specialize.c.inc | 12 | ||||
| -rw-r--r-- | fpu/softfloat.c | 114 |
3 files changed, 154 insertions, 29 deletions
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 41d4b17e41..db3e1f393d 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -19,7 +19,7 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s) { switch (a->cls) { case float_class_snan: - float_raise(float_flag_invalid, s); + float_raise(float_flag_invalid | float_flag_invalid_snan, s); if (s->default_nan_mode) { parts_default_nan(a, s); } else { @@ -40,7 +40,7 @@ static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b, float_status *s) { if (is_snan(a->cls) || is_snan(b->cls)) { - float_raise(float_flag_invalid, s); + float_raise(float_flag_invalid | float_flag_invalid_snan, s); } if (s->default_nan_mode) { @@ -68,7 +68,7 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, int which; if (unlikely(abc_mask & float_cmask_snan)) { - float_raise(float_flag_invalid, s); + float_raise(float_flag_invalid | float_flag_invalid_snan, s); } which = pickNaNMulAdd(a->cls, b->cls, c->cls, @@ -354,7 +354,7 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b, return a; } /* Inf - Inf */ - float_raise(float_flag_invalid, s); + float_raise(float_flag_invalid | float_flag_invalid_isi, s); parts_default_nan(a, s); return a; } @@ -423,7 +423,7 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b, /* Inf * Zero == NaN */ if (unlikely(ab_mask == float_cmask_infzero)) { - float_raise(float_flag_invalid, s); + float_raise(float_flag_invalid | float_flag_invalid_imz, s); parts_default_nan(a, s); return a; } @@ -489,11 +489,13 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, if (unlikely(ab_mask != float_cmask_normal)) { if (unlikely(ab_mask == float_cmask_infzero)) { + float_raise(float_flag_invalid | float_flag_invalid_imz, s); goto d_nan; } if (ab_mask & float_cmask_inf) { if (c->cls == float_class_inf && a->sign != c->sign) { + float_raise(float_flag_invalid | float_flag_invalid_isi, s); goto d_nan; } goto return_inf; @@ -566,7 +568,6 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b, goto finish_sign; d_nan: - float_raise(float_flag_invalid, s); parts_default_nan(a, s); return a; } @@ -589,11 +590,13 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, } /* 0/0 or Inf/Inf => NaN */ - if (unlikely(ab_mask == float_cmask_zero) || - unlikely(ab_mask == float_cmask_inf)) { - float_raise(float_flag_invalid, s); - parts_default_nan(a, s); - return a; + if (unlikely(ab_mask == float_cmask_zero)) { + float_raise(float_flag_invalid | float_flag_invalid_zdz, s); + goto d_nan; + } + if (unlikely(ab_mask == float_cmask_inf)) { + float_raise(float_flag_invalid | float_flag_invalid_idi, s); + goto d_nan; } /* All the NaN cases */ @@ -624,6 +627,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b, float_raise(float_flag_divbyzero, s); a->cls = float_class_inf; return a; + + d_nan: + parts_default_nan(a, s); + return a; } /* @@ -862,7 +869,7 @@ static void partsN(sqrt)(FloatPartsN *a, float_status *status, return; d_nan: - float_raise(float_flag_invalid, status); + float_raise(float_flag_invalid | float_flag_invalid_sqrt, status); parts_default_nan(a, status); } @@ -1042,13 +1049,15 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode, switch (p->cls) { case float_class_snan: + flags |= float_flag_invalid_snan; + /* fall through */ case float_class_qnan: - flags = float_flag_invalid; + flags |= float_flag_invalid; r = max; break; case float_class_inf: - flags = float_flag_invalid; + flags = float_flag_invalid | float_flag_invalid_cvti; r = p->sign ? min : max; break; @@ -1070,11 +1079,11 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode, if (r <= -(uint64_t)min) { r = -r; } else { - flags = float_flag_invalid; + flags = float_flag_invalid | float_flag_invalid_cvti; r = min; } } else if (r > max) { - flags = float_flag_invalid; + flags = float_flag_invalid | float_flag_invalid_cvti; r = max; } break; @@ -1107,13 +1116,15 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode, switch (p->cls) { case float_class_snan: + flags |= float_flag_invalid_snan; + /* fall through */ case float_class_qnan: - flags = float_flag_invalid; + flags |= float_flag_invalid; r = max; break; case float_class_inf: - flags = float_flag_invalid; + flags = float_flag_invalid | float_flag_invalid_cvti; r = p->sign ? 0 : max; break; @@ -1131,15 +1142,15 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode, } if (p->sign) { - flags = float_flag_invalid; + flags = float_flag_invalid | float_flag_invalid_cvti; r = 0; } else if (p->exp > DECOMPOSED_BINARY_POINT) { - flags = float_flag_invalid; + flags = float_flag_invalid | float_flag_invalid_cvti; r = max; } else { r = p->frac_hi >> (DECOMPOSED_BINARY_POINT - p->exp); if (r > max) { - flags = float_flag_invalid; + flags = float_flag_invalid | float_flag_invalid_cvti; r = max; } } @@ -1334,7 +1345,9 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b, } if (unlikely(ab_mask & float_cmask_anynan)) { - if (!is_quiet || (ab_mask & float_cmask_snan)) { + if (ab_mask & float_cmask_snan) { + float_raise(float_flag_invalid | float_flag_invalid_snan, s); + } else if (!is_quiet) { float_raise(float_flag_invalid, s); } return float_relation_unordered; diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index f2ad0f335e..943e3301d2 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -506,7 +506,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * the default NaN */ if (infzero && is_qnan(c_cls)) { - float_raise(float_flag_invalid, status); + float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 3; } @@ -533,7 +533,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * case sets InvalidOp and returns the default NaN */ if (infzero) { - float_raise(float_flag_invalid, status); + float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 3; } /* Prefer sNaN over qNaN, in the a, b, c order. */ @@ -556,7 +556,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * case sets InvalidOp and returns the input value 'c' */ if (infzero) { - float_raise(float_flag_invalid, status); + float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 2; } /* Prefer sNaN over qNaN, in the c, a, b order. */ @@ -580,7 +580,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * a default NaN */ if (infzero) { - float_raise(float_flag_invalid, status); + float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 2; } @@ -597,7 +597,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, #elif defined(TARGET_RISCV) /* For RISC-V, InvalidOp is set when multiplicands are Inf and zero */ if (infzero) { - float_raise(float_flag_invalid, status); + float_raise(float_flag_invalid | float_flag_invalid_imz, status); } return 3; /* default NaN */ #elif defined(TARGET_XTENSA) @@ -606,7 +606,7 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, * an input NaN if we have one (ie c). */ if (infzero) { - float_raise(float_flag_invalid, status); + float_raise(float_flag_invalid | float_flag_invalid_imz, status); return 2; } if (status->use_first_nan) { diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 9a28720d82..7f524d4377 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1693,6 +1693,50 @@ static float64 float64_round_pack_canonical(FloatParts64 *p, return float64_pack_raw(p); } +static float64 float64r32_round_pack_canonical(FloatParts64 *p, + float_status *s) +{ + parts_uncanon(p, s, &float32_params); + + /* + * In parts_uncanon, we placed the fraction for float32 at the lsb. + * We need to adjust the fraction higher so that the least N bits are + * zero, and the fraction is adjacent to the float64 implicit bit. + */ + switch (p->cls) { + case float_class_normal: + if (unlikely(p->exp == 0)) { + /* + * The result is denormal for float32, but can be represented + * in normalized form for float64. Adjust, per canonicalize. + */ + int shift = frac_normalize(p); + p->exp = (float32_params.frac_shift - + float32_params.exp_bias - shift + 1 + + float64_params.exp_bias); + frac_shr(p, float64_params.frac_shift); + } else { + frac_shl(p, float32_params.frac_shift - float64_params.frac_shift); + p->exp += float64_params.exp_bias - float32_params.exp_bias; + } + break; + case float_class_snan: + case float_class_qnan: + frac_shl(p, float32_params.frac_shift - float64_params.frac_shift); + p->exp = float64_params.exp_max; + break; + case float_class_inf: + p->exp = float64_params.exp_max; + break; + case float_class_zero: + break; + default: + g_assert_not_reached(); + } + + return float64_pack_raw(p); +} + static void float128_unpack_canonical(FloatParts128 *p, float128 f, float_status *s) { @@ -1938,6 +1982,28 @@ float64_sub(float64 a, float64 b, float_status *s) return float64_addsub(a, b, s, hard_f64_sub, soft_f64_sub); } +static float64 float64r32_addsub(float64 a, float64 b, float_status *status, + bool subtract) +{ + FloatParts64 pa, pb, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + pr = parts_addsub(&pa, &pb, status, subtract); + + return float64r32_round_pack_canonical(pr, status); +} + +float64 float64r32_add(float64 a, float64 b, float_status *status) +{ + return float64r32_addsub(a, b, status, false); +} + +float64 float64r32_sub(float64 a, float64 b, float_status *status) +{ + return float64r32_addsub(a, b, status, true); +} + static bfloat16 QEMU_FLATTEN bfloat16_addsub(bfloat16 a, bfloat16 b, float_status *status, bool subtract) { @@ -2069,6 +2135,17 @@ float64_mul(float64 a, float64 b, float_status *s) f64_is_zon2, f64_addsubmul_post); } +float64 float64r32_mul(float64 a, float64 b, float_status *status) +{ + FloatParts64 pa, pb, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + pr = parts_mul(&pa, &pb, status); + + return float64r32_round_pack_canonical(pr, status); +} + bfloat16 QEMU_FLATTEN bfloat16_mul(bfloat16 a, bfloat16 b, float_status *status) { @@ -2296,6 +2373,19 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s); } +float64 float64r32_muladd(float64 a, float64 b, float64 c, + int flags, float_status *status) +{ + FloatParts64 pa, pb, pc, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + float64_unpack_canonical(&pc, c, status); + pr = parts_muladd(&pa, &pb, &pc, flags, status); + + return float64r32_round_pack_canonical(pr, status); +} + bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c, int flags, float_status *status) { @@ -2419,6 +2509,17 @@ float64_div(float64 a, float64 b, float_status *s) f64_div_pre, f64_div_post); } +float64 float64r32_div(float64 a, float64 b, float_status *status) +{ + FloatParts64 pa, pb, *pr; + + float64_unpack_canonical(&pa, a, status); + float64_unpack_canonical(&pb, b, status); + pr = parts_div(&pa, &pb, status); + + return float64r32_round_pack_canonical(pr, status); +} + bfloat16 QEMU_FLATTEN bfloat16_div(bfloat16 a, bfloat16 b, float_status *status) { @@ -2543,8 +2644,10 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status) static void parts_float_to_ahp(FloatParts64 *a, float_status *s) { switch (a->cls) { - case float_class_qnan: case float_class_snan: + float_raise(float_flag_invalid_snan, s); + /* fall through */ + case float_class_qnan: /* * There is no NaN in the destination format. Raise Invalid * and return a zero with the sign of the input NaN. @@ -4283,6 +4386,15 @@ float64 QEMU_FLATTEN float64_sqrt(float64 xa, float_status *s) return soft_f64_sqrt(ua.s, s); } +float64 float64r32_sqrt(float64 a, float_status *status) +{ + FloatParts64 p; + + float64_unpack_canonical(&p, a, status); + parts_sqrt(&p, status, &float64_params); + return float64r32_round_pack_canonical(&p, status); +} + bfloat16 QEMU_FLATTEN bfloat16_sqrt(bfloat16 a, float_status *status) { FloatParts64 p; |