aboutsummaryrefslogtreecommitdiff
path: root/fpu
diff options
context:
space:
mode:
Diffstat (limited to 'fpu')
-rw-r--r--fpu/meson.build2
-rw-r--r--fpu/softfloat-parts.c.inc327
-rw-r--r--fpu/softfloat-specialize.c.inc460
-rw-r--r--fpu/softfloat.c177
4 files changed, 413 insertions, 553 deletions
diff --git a/fpu/meson.build b/fpu/meson.build
index 1a9992d..646c76f 100644
--- a/fpu/meson.build
+++ b/fpu/meson.build
@@ -1 +1 @@
-specific_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c'))
+common_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c'))
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc
index a44649f..171bfd0 100644
--- a/fpu/softfloat-parts.c.inc
+++ b/fpu/softfloat-parts.c.inc
@@ -39,65 +39,152 @@ static void partsN(return_nan)(FloatPartsN *a, float_status *s)
static FloatPartsN *partsN(pick_nan)(FloatPartsN *a, FloatPartsN *b,
float_status *s)
{
+ bool have_snan = false;
+ FloatPartsN *ret;
+ int cmp;
+
if (is_snan(a->cls) || is_snan(b->cls)) {
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
+ have_snan = true;
}
if (s->default_nan_mode) {
parts_default_nan(a, s);
- } else {
- int cmp = frac_cmp(a, b);
- if (cmp == 0) {
- cmp = a->sign < b->sign;
- }
+ return a;
+ }
- if (pickNaN(a->cls, b->cls, cmp > 0, s)) {
- a = b;
+ switch (s->float_2nan_prop_rule) {
+ case float_2nan_prop_s_ab:
+ if (have_snan) {
+ ret = is_snan(a->cls) ? a : b;
+ break;
+ }
+ /* fall through */
+ case float_2nan_prop_ab:
+ ret = is_nan(a->cls) ? a : b;
+ break;
+ case float_2nan_prop_s_ba:
+ if (have_snan) {
+ ret = is_snan(b->cls) ? b : a;
+ break;
}
+ /* fall through */
+ case float_2nan_prop_ba:
+ ret = is_nan(b->cls) ? b : a;
+ break;
+ case float_2nan_prop_x87:
+ /*
+ * This implements x87 NaN propagation rules:
+ * SNaN + QNaN => return the QNaN
+ * two SNaNs => return the one with the larger significand, silenced
+ * two QNaNs => return the one with the larger significand
+ * SNaN and a non-NaN => return the SNaN, silenced
+ * QNaN and a non-NaN => return the QNaN
+ *
+ * If we get down to comparing significands and they are the same,
+ * return the NaN with the positive sign bit (if any).
+ */
if (is_snan(a->cls)) {
- parts_silence_nan(a, s);
+ if (!is_snan(b->cls)) {
+ ret = is_qnan(b->cls) ? b : a;
+ break;
+ }
+ } else if (is_qnan(a->cls)) {
+ if (is_snan(b->cls) || !is_qnan(b->cls)) {
+ ret = a;
+ break;
+ }
+ } else {
+ ret = b;
+ break;
+ }
+ cmp = frac_cmp(a, b);
+ if (cmp == 0) {
+ cmp = a->sign < b->sign;
}
+ ret = cmp > 0 ? a : b;
+ break;
+ default:
+ g_assert_not_reached();
}
- return a;
+
+ if (is_snan(ret->cls)) {
+ parts_silence_nan(ret, s);
+ }
+ return ret;
}
static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b,
FloatPartsN *c, float_status *s,
int ab_mask, int abc_mask)
{
- int which;
+ bool infzero = (ab_mask == float_cmask_infzero);
+ bool have_snan = (abc_mask & float_cmask_snan);
+ FloatPartsN *ret;
- if (unlikely(abc_mask & float_cmask_snan)) {
+ if (unlikely(have_snan)) {
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
}
- which = pickNaNMulAdd(a->cls, b->cls, c->cls,
- ab_mask == float_cmask_infzero, s);
+ if (infzero &&
+ !(s->float_infzeronan_rule & float_infzeronan_suppress_invalid)) {
+ /* This is (0 * inf) + NaN or (inf * 0) + NaN */
+ float_raise(float_flag_invalid | float_flag_invalid_imz, s);
+ }
- if (s->default_nan_mode || which == 3) {
+ if (s->default_nan_mode) {
/*
- * Note that this check is after pickNaNMulAdd so that function
- * has an opportunity to set the Invalid flag for infzero.
+ * We guarantee not to require the target to tell us how to
+ * pick a NaN if we're always returning the default NaN.
+ * But if we're not in default-NaN mode then the target must
+ * specify.
*/
- parts_default_nan(a, s);
- return a;
+ goto default_nan;
+ } else if (infzero) {
+ /*
+ * Inf * 0 + NaN -- some implementations return the
+ * default NaN here, and some return the input NaN.
+ */
+ switch (s->float_infzeronan_rule & ~float_infzeronan_suppress_invalid) {
+ case float_infzeronan_dnan_never:
+ break;
+ case float_infzeronan_dnan_always:
+ goto default_nan;
+ case float_infzeronan_dnan_if_qnan:
+ if (is_qnan(c->cls)) {
+ goto default_nan;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ ret = c;
+ } else {
+ FloatPartsN *val[R_3NAN_1ST_MASK + 1] = { a, b, c };
+ Float3NaNPropRule rule = s->float_3nan_prop_rule;
+
+ assert(rule != float_3nan_prop_none);
+ if (have_snan && (rule & R_3NAN_SNAN_MASK)) {
+ /* We have at least one SNaN input and should prefer it */
+ do {
+ ret = val[rule & R_3NAN_1ST_MASK];
+ rule >>= R_3NAN_1ST_LENGTH;
+ } while (!is_snan(ret->cls));
+ } else {
+ do {
+ ret = val[rule & R_3NAN_1ST_MASK];
+ rule >>= R_3NAN_1ST_LENGTH;
+ } while (!is_nan(ret->cls));
+ }
}
- switch (which) {
- case 0:
- break;
- case 1:
- a = b;
- break;
- case 2:
- a = c;
- break;
- default:
- g_assert_not_reached();
- }
- if (is_snan(a->cls)) {
- parts_silence_nan(a, s);
+ if (is_snan(ret->cls)) {
+ parts_silence_nan(ret, s);
}
+ return ret;
+
+ default_nan:
+ parts_default_nan(a, s);
return a;
}
@@ -108,18 +195,37 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b,
static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
const FloatFmt *fmt)
{
+ /*
+ * It's target-dependent how to handle the case of exponent 0
+ * and Integer bit set. Intel calls these "pseudodenormals",
+ * and treats them as if the integer bit was 0, and never
+ * produces them on output. This is the default behaviour for QEMU.
+ * For m68k, the integer bit is considered validly part of the
+ * input value when the exponent is 0, and may be 0 or 1,
+ * giving extra range. They may also be generated as outputs.
+ * (The m68k manual actually calls these values part of the
+ * normalized number range, not the denormalized number range,
+ * but that distinction is not important for us, because
+ * m68k doesn't care about the input_denormal_used status flag.)
+ * floatx80_pseudo_denormal_valid selects the m68k behaviour,
+ * which changes both how we canonicalize such a value and
+ * how we uncanonicalize results.
+ */
+ bool has_pseudo_denormals = fmt->has_explicit_bit &&
+ (status->floatx80_behaviour & floatx80_pseudo_denormal_valid);
+
if (unlikely(p->exp == 0)) {
if (likely(frac_eqz(p))) {
p->cls = float_class_zero;
} else if (status->flush_inputs_to_zero) {
- float_raise(float_flag_input_denormal, status);
+ float_raise(float_flag_input_denormal_flushed, status);
p->cls = float_class_zero;
frac_clear(p);
} else {
int shift = frac_normalize(p);
- p->cls = float_class_normal;
+ p->cls = float_class_denormal;
p->exp = fmt->frac_shift - fmt->exp_bias
- - shift + !fmt->m68k_denormal;
+ - shift + !has_pseudo_denormals;
}
} else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) {
p->cls = float_class_normal;
@@ -155,6 +261,9 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
int exp, flags = 0;
switch (s->float_rounding_mode) {
+ case float_round_nearest_even_max:
+ overflow_norm = true;
+ /* fall through */
case float_round_nearest_even:
if (N > 64 && frac_lsb == 0) {
inc = ((p->frac_hi & 1) || (p->frac_lo & round_mask) != frac_lsbm1
@@ -244,20 +353,23 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
p->frac_lo &= ~round_mask;
}
frac_shr(p, frac_shift);
- } else if (s->flush_to_zero) {
- flags |= float_flag_output_denormal;
+ } else if (s->flush_to_zero &&
+ s->ftz_detection == float_ftz_before_rounding) {
+ flags |= float_flag_output_denormal_flushed;
p->cls = float_class_zero;
exp = 0;
frac_clear(p);
} else {
bool is_tiny = s->tininess_before_rounding || exp < 0;
+ bool has_pseudo_denormals = fmt->has_explicit_bit &&
+ (s->floatx80_behaviour & floatx80_pseudo_denormal_valid);
if (!is_tiny) {
FloatPartsN discard;
is_tiny = !frac_addi(&discard, p, inc);
}
- frac_shrjam(p, !fmt->m68k_denormal - exp);
+ frac_shrjam(p, !has_pseudo_denormals - exp);
if (p->frac_lo & round_mask) {
/* Need to recompute round-to-even/round-to-odd. */
@@ -288,14 +400,22 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
p->frac_lo &= ~round_mask;
}
- exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal;
+ exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !has_pseudo_denormals;
frac_shr(p, frac_shift);
- if (is_tiny && (flags & float_flag_inexact)) {
- flags |= float_flag_underflow;
- }
- if (exp == 0 && frac_eqz(p)) {
- p->cls = float_class_zero;
+ if (is_tiny) {
+ if (s->flush_to_zero) {
+ assert(s->ftz_detection == float_ftz_after_rounding);
+ flags |= float_flag_output_denormal_flushed;
+ p->cls = float_class_zero;
+ exp = 0;
+ frac_clear(p);
+ } else if (flags & float_flag_inexact) {
+ flags |= float_flag_underflow;
+ }
+ if (exp == 0 && frac_eqz(p)) {
+ p->cls = float_class_zero;
+ }
}
}
p->exp = exp;
@@ -305,7 +425,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
static void partsN(uncanon)(FloatPartsN *p, float_status *s,
const FloatFmt *fmt)
{
- if (likely(p->cls == float_class_normal)) {
+ if (likely(is_anynorm(p->cls))) {
parts_uncanon_normal(p, s, fmt);
} else {
switch (p->cls) {
@@ -343,9 +463,18 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b,
bool b_sign = b->sign ^ subtract;
int ab_mask = float_cmask(a->cls) | float_cmask(b->cls);
+ /*
+ * For addition and subtraction, we will consume an
+ * input denormal unless the other input is a NaN.
+ */
+ if ((ab_mask & (float_cmask_denormal | float_cmask_anynan)) ==
+ float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
if (a->sign != b_sign) {
/* Subtraction */
- if (likely(ab_mask == float_cmask_normal)) {
+ if (likely(cmask_is_only_normals(ab_mask))) {
if (parts_sub_normal(a, b)) {
return a;
}
@@ -378,7 +507,7 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b,
}
} else {
/* Addition */
- if (likely(ab_mask == float_cmask_normal)) {
+ if (likely(cmask_is_only_normals(ab_mask))) {
parts_add_normal(a, b);
return a;
}
@@ -398,12 +527,12 @@ static FloatPartsN *partsN(addsub)(FloatPartsN *a, FloatPartsN *b,
}
if (b->cls == float_class_zero) {
- g_assert(a->cls == float_class_normal);
+ g_assert(is_anynorm(a->cls));
return a;
}
g_assert(a->cls == float_class_zero);
- g_assert(b->cls == float_class_normal);
+ g_assert(is_anynorm(b->cls));
return_b:
b->sign = b_sign;
return b;
@@ -423,9 +552,13 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b,
int ab_mask = float_cmask(a->cls) | float_cmask(b->cls);
bool sign = a->sign ^ b->sign;
- if (likely(ab_mask == float_cmask_normal)) {
+ if (likely(cmask_is_only_normals(ab_mask))) {
FloatPartsW tmp;
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
frac_mulw(&tmp, a, b);
frac_truncjam(a, &tmp);
@@ -451,6 +584,10 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b,
}
/* Multiply by 0 or Inf */
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
if (ab_mask & float_cmask_inf) {
a->cls = float_class_inf;
a->sign = sign;
@@ -476,8 +613,9 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b,
* Requires A and C extracted into a double-sized structure to provide the
* extra space for the widening multiply.
*/
-static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
- FloatPartsN *c, int flags, float_status *s)
+static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b,
+ FloatPartsN *c, int scale,
+ int flags, float_status *s)
{
int ab_mask, abc_mask;
FloatPartsW p_widen, c_widen;
@@ -505,7 +643,7 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
a->sign ^= 1;
}
- if (unlikely(ab_mask != float_cmask_normal)) {
+ if (unlikely(!cmask_is_only_normals(ab_mask))) {
if (unlikely(ab_mask == float_cmask_infzero)) {
float_raise(float_flag_invalid | float_flag_invalid_imz, s);
goto d_nan;
@@ -520,12 +658,14 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
}
g_assert(ab_mask & float_cmask_zero);
- if (c->cls == float_class_normal) {
+ if (is_anynorm(c->cls)) {
*a = *c;
goto return_normal;
}
if (c->cls == float_class_zero) {
- if (a->sign != c->sign) {
+ if (flags & float_muladd_suppress_add_product_zero) {
+ a->sign = c->sign;
+ } else if (a->sign != c->sign) {
goto return_sub_zero;
}
goto return_zero;
@@ -566,13 +706,21 @@ static FloatPartsN *partsN(muladd)(FloatPartsN *a, FloatPartsN *b,
a->exp = p_widen.exp;
return_normal:
- if (flags & float_muladd_halve_result) {
- a->exp -= 1;
- }
+ a->exp += scale;
finish_sign:
if (flags & float_muladd_negate_result) {
a->sign ^= 1;
}
+
+ /*
+ * All result types except for "return the default NaN
+ * because this is an Invalid Operation" go through here;
+ * this matches the set of cases where we consumed a
+ * denormal input.
+ */
+ if (abc_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
return a;
return_sub_zero:
@@ -601,7 +749,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b,
int ab_mask = float_cmask(a->cls) | float_cmask(b->cls);
bool sign = a->sign ^ b->sign;
- if (likely(ab_mask == float_cmask_normal)) {
+ if (likely(cmask_is_only_normals(ab_mask))) {
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
a->sign = sign;
a->exp -= b->exp + frac_div(a, b);
return a;
@@ -622,6 +773,10 @@ static FloatPartsN *partsN(div)(FloatPartsN *a, FloatPartsN *b,
return parts_pick_nan(a, b, s);
}
+ if ((ab_mask & float_cmask_denormal) && b->cls != float_class_zero) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
a->sign = sign;
/* Inf / X */
@@ -659,7 +814,10 @@ static FloatPartsN *partsN(modrem)(FloatPartsN *a, FloatPartsN *b,
{
int ab_mask = float_cmask(a->cls) | float_cmask(b->cls);
- if (likely(ab_mask == float_cmask_normal)) {
+ if (likely(cmask_is_only_normals(ab_mask))) {
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
frac_modrem(a, b, mod_quot);
return a;
}
@@ -680,6 +838,10 @@ static FloatPartsN *partsN(modrem)(FloatPartsN *a, FloatPartsN *b,
return a;
}
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
/* N % Inf; 0 % N */
g_assert(b->cls == float_class_inf || a->cls == float_class_zero);
return a;
@@ -709,6 +871,12 @@ static void partsN(sqrt)(FloatPartsN *a, float_status *status,
if (unlikely(a->cls != float_class_normal)) {
switch (a->cls) {
+ case float_class_denormal:
+ if (!a->sign) {
+ /* -ve denormal will be InvalidOperation */
+ float_raise(float_flag_input_denormal_used, status);
+ }
+ break;
case float_class_snan:
case float_class_qnan:
parts_return_nan(a, status);
@@ -1039,6 +1207,7 @@ static void partsN(round_to_int)(FloatPartsN *a, FloatRoundMode rmode,
case float_class_inf:
break;
case float_class_normal:
+ case float_class_denormal:
if (parts_round_to_int_normal(a, rmode, scale, fmt->frac_size)) {
float_raise(float_flag_inexact, s);
}
@@ -1083,6 +1252,7 @@ static int64_t partsN(float_to_sint)(FloatPartsN *p, FloatRoundMode rmode,
return 0;
case float_class_normal:
+ case float_class_denormal:
/* TODO: N - 2 is frac_size for rounding; could use input fmt. */
if (parts_round_to_int_normal(p, rmode, scale, N - 2)) {
flags = float_flag_inexact;
@@ -1150,6 +1320,7 @@ static uint64_t partsN(float_to_uint)(FloatPartsN *p, FloatRoundMode rmode,
return 0;
case float_class_normal:
+ case float_class_denormal:
/* TODO: N - 2 is frac_size for rounding; could use input fmt. */
if (parts_round_to_int_normal(p, rmode, scale, N - 2)) {
flags = float_flag_inexact;
@@ -1213,6 +1384,7 @@ static int64_t partsN(float_to_sint_modulo)(FloatPartsN *p,
return 0;
case float_class_normal:
+ case float_class_denormal:
/* TODO: N - 2 is frac_size for rounding; could use input fmt. */
if (parts_round_to_int_normal(p, rmode, 0, N - 2)) {
flags = float_flag_inexact;
@@ -1334,6 +1506,9 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b,
if ((flags & (minmax_isnum | minmax_isnumber))
&& !(ab_mask & float_cmask_snan)
&& (ab_mask & ~float_cmask_qnan)) {
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
return is_nan(a->cls) ? b : a;
}
@@ -1358,12 +1533,17 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b,
return parts_pick_nan(a, b, s);
}
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
a_exp = a->exp;
b_exp = b->exp;
- if (unlikely(ab_mask != float_cmask_normal)) {
+ if (unlikely(!cmask_is_only_normals(ab_mask))) {
switch (a->cls) {
case float_class_normal:
+ case float_class_denormal:
break;
case float_class_inf:
a_exp = INT16_MAX;
@@ -1373,10 +1553,10 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b,
break;
default:
g_assert_not_reached();
- break;
}
switch (b->cls) {
case float_class_normal:
+ case float_class_denormal:
break;
case float_class_inf:
b_exp = INT16_MAX;
@@ -1386,7 +1566,6 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b,
break;
default:
g_assert_not_reached();
- break;
}
}
@@ -1424,9 +1603,13 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b,
{
int ab_mask = float_cmask(a->cls) | float_cmask(b->cls);
- if (likely(ab_mask == float_cmask_normal)) {
+ if (likely(cmask_is_only_normals(ab_mask))) {
FloatRelation cmp;
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
if (a->sign != b->sign) {
goto a_sign;
}
@@ -1452,6 +1635,10 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b,
return float_relation_unordered;
}
+ if (ab_mask & float_cmask_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
+
if (ab_mask & float_cmask_zero) {
if (ab_mask == float_cmask_zero) {
return float_relation_equal;
@@ -1491,6 +1678,9 @@ static void partsN(scalbn)(FloatPartsN *a, int n, float_status *s)
case float_class_zero:
case float_class_inf:
break;
+ case float_class_denormal:
+ float_raise(float_flag_input_denormal_used, s);
+ /* fall through */
case float_class_normal:
a->exp += MIN(MAX(n, -0x10000), 0x10000);
break;
@@ -1510,6 +1700,12 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt)
if (unlikely(a->cls != float_class_normal)) {
switch (a->cls) {
+ case float_class_denormal:
+ if (!a->sign) {
+ /* -ve denormal will be InvalidOperation */
+ float_raise(float_flag_input_denormal_used, s);
+ }
+ break;
case float_class_snan:
case float_class_qnan:
parts_return_nan(a, s);
@@ -1526,9 +1722,8 @@ static void partsN(log2)(FloatPartsN *a, float_status *s, const FloatFmt *fmt)
}
return;
default:
- break;
+ g_assert_not_reached();
}
- g_assert_not_reached();
}
if (unlikely(a->sign)) {
goto d_nan;
diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc
index 8f3b97d..ba4fa08 100644
--- a/fpu/softfloat-specialize.c.inc
+++ b/fpu/softfloat-specialize.c.inc
@@ -85,11 +85,7 @@ this code that are retained.
*/
static inline bool no_signaling_nans(float_status *status)
{
-#if defined(TARGET_XTENSA)
return status->no_signaling_nans;
-#else
- return false;
-#endif
}
/* Define how the architecture discriminates signaling NaNs.
@@ -97,17 +93,10 @@ static inline bool no_signaling_nans(float_status *status)
* In IEEE 754-1985 this was implementation defined, but in IEEE 754-2008
* the msb must be zero. MIPS is (so far) unique in supporting both the
* 2008 revision and backward compatibility with their original choice.
- * Thus for MIPS we must make the choice at runtime.
*/
static inline bool snan_bit_is_one(float_status *status)
{
-#if defined(TARGET_MIPS)
return status->snan_bit_is_one;
-#elif defined(TARGET_HPPA) || defined(TARGET_SH4)
- return 1;
-#else
- return 0;
-#endif
}
/*----------------------------------------------------------------------------
@@ -133,35 +122,17 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status)
{
bool sign = 0;
uint64_t frac;
+ uint8_t dnan_pattern = status->default_nan_pattern;
-#if defined(TARGET_SPARC) || defined(TARGET_M68K)
- /* !snan_bit_is_one, set all bits */
- frac = (1ULL << DECOMPOSED_BINARY_POINT) - 1;
-#elif defined(TARGET_I386) || defined(TARGET_X86_64) \
- || defined(TARGET_MICROBLAZE)
- /* !snan_bit_is_one, set sign and msb */
- frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1);
- sign = 1;
-#elif defined(TARGET_HPPA)
- /* snan_bit_is_one, set msb-1. */
- frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2);
-#elif defined(TARGET_HEXAGON)
- sign = 1;
- frac = ~0ULL;
-#else
+ assert(dnan_pattern != 0);
+
+ sign = dnan_pattern >> 7;
/*
- * This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V,
- * S390, SH4, TriCore, and Xtensa. Our other supported targets,
- * such CRIS, do not have floating-point.
+ * Place default_nan_pattern [6:0] into bits [62:56],
+ * and replecate bit [0] down into [55:0]
*/
- if (snan_bit_is_one(status)) {
- /* set all bits other than msb */
- frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1;
- } else {
- /* set msb */
- frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1);
- }
-#endif
+ frac = deposit64(0, DECOMPOSED_BINARY_POINT - 7, 7, dnan_pattern);
+ frac = deposit64(frac, 0, DECOMPOSED_BINARY_POINT - 7, -(dnan_pattern & 1));
*p = (FloatParts64) {
.cls = float_class_qnan,
@@ -227,17 +198,17 @@ static void parts128_silence_nan(FloatParts128 *p, float_status *status)
floatx80 floatx80_default_nan(float_status *status)
{
floatx80 r;
+ /*
+ * Extrapolate from the choices made by parts64_default_nan to fill
+ * in the floatx80 format. We assume that floatx80's explicit
+ * integer bit is always set (this is true for i386 and m68k,
+ * which are the only real users of this format).
+ */
+ FloatParts64 p64;
+ parts64_default_nan(&p64, status);
- /* None of the targets that have snan_bit_is_one use floatx80. */
- assert(!snan_bit_is_one(status));
-#if defined(TARGET_M68K)
- r.low = UINT64_C(0xFFFFFFFFFFFFFFFF);
- r.high = 0x7FFF;
-#else
- /* X86 */
- r.low = UINT64_C(0xC000000000000000);
- r.high = 0xFFFF;
-#endif
+ r.high = 0x7FFF | (p64.sign << 15);
+ r.low = (1ULL << DECOMPOSED_BINARY_POINT) | p64.frac;
return r;
}
@@ -245,15 +216,15 @@ floatx80 floatx80_default_nan(float_status *status)
| The pattern for a default generated extended double-precision inf.
*----------------------------------------------------------------------------*/
-#define floatx80_infinity_high 0x7FFF
-#if defined(TARGET_M68K)
-#define floatx80_infinity_low UINT64_C(0x0000000000000000)
-#else
-#define floatx80_infinity_low UINT64_C(0x8000000000000000)
-#endif
-
-const floatx80 floatx80_infinity
- = make_floatx80_init(floatx80_infinity_high, floatx80_infinity_low);
+floatx80 floatx80_default_inf(bool zSign, float_status *status)
+{
+ /*
+ * Whether the Integer bit is set in the default Infinity is
+ * target dependent.
+ */
+ bool z = status->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero;
+ return packFloatx80(zSign, 0x7fff, z ? 0 : (1ULL << 63));
+}
/*----------------------------------------------------------------------------
| Returns 1 if the half-precision floating-point value `a' is a quiet
@@ -371,331 +342,6 @@ bool float32_is_signaling_nan(float32 a_, float_status *status)
}
/*----------------------------------------------------------------------------
-| Select which NaN to propagate for a two-input operation.
-| IEEE754 doesn't specify all the details of this, so the
-| algorithm is target-specific.
-| The routine is passed various bits of information about the
-| two NaNs and should return 0 to select NaN a and 1 for NaN b.
-| Note that signalling NaNs are always squashed to quiet NaNs
-| by the caller, by calling floatXX_silence_nan() before
-| returning them.
-|
-| aIsLargerSignificand is only valid if both a and b are NaNs
-| of some kind, and is true if a has the larger significand,
-| or if both a and b have the same significand but a is
-| positive but b is negative. It is only needed for the x87
-| tie-break rule.
-*----------------------------------------------------------------------------*/
-
-static int pickNaN(FloatClass a_cls, FloatClass b_cls,
- bool aIsLargerSignificand, float_status *status)
-{
-#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) || \
- defined(TARGET_LOONGARCH64) || defined(TARGET_S390X)
- /* ARM mandated NaN propagation rules (see FPProcessNaNs()), take
- * the first of:
- * 1. A if it is signaling
- * 2. B if it is signaling
- * 3. A (quiet)
- * 4. B (quiet)
- * A signaling NaN is always quietened before returning it.
- */
- /* According to MIPS specifications, if one of the two operands is
- * a sNaN, a new qNaN has to be generated. This is done in
- * floatXX_silence_nan(). For qNaN inputs the specifications
- * says: "When possible, this QNaN result is one of the operand QNaN
- * values." In practice it seems that most implementations choose
- * the first operand if both operands are qNaN. In short this gives
- * the following rules:
- * 1. A if it is signaling
- * 2. B if it is signaling
- * 3. A (quiet)
- * 4. B (quiet)
- * A signaling NaN is always silenced before returning it.
- */
- if (is_snan(a_cls)) {
- return 0;
- } else if (is_snan(b_cls)) {
- return 1;
- } else if (is_qnan(a_cls)) {
- return 0;
- } else {
- return 1;
- }
-#elif defined(TARGET_PPC) || defined(TARGET_M68K)
- /* PowerPC propagation rules:
- * 1. A if it sNaN or qNaN
- * 2. B if it sNaN or qNaN
- * A signaling NaN is always silenced before returning it.
- */
- /* M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL
- * 3.4 FLOATING-POINT INSTRUCTION DETAILS
- * If either operand, but not both operands, of an operation is a
- * nonsignaling NaN, then that NaN is returned as the result. If both
- * operands are nonsignaling NaNs, then the destination operand
- * nonsignaling NaN is returned as the result.
- * If either operand to an operation is a signaling NaN (SNaN), then the
- * SNaN bit is set in the FPSR EXC byte. If the SNaN exception enable bit
- * is set in the FPCR ENABLE byte, then the exception is taken and the
- * destination is not modified. If the SNaN exception enable bit is not
- * set, setting the SNaN bit in the operand to a one converts the SNaN to
- * a nonsignaling NaN. The operation then continues as described in the
- * preceding paragraph for nonsignaling NaNs.
- */
- if (is_nan(a_cls)) {
- return 0;
- } else {
- return 1;
- }
-#elif defined(TARGET_SPARC)
- /* Prefer SNaN over QNaN, order B then A. */
- if (is_snan(b_cls)) {
- return 1;
- } else if (is_snan(a_cls)) {
- return 0;
- } else if (is_qnan(b_cls)) {
- return 1;
- } else {
- return 0;
- }
-#elif defined(TARGET_XTENSA)
- /*
- * Xtensa has two NaN propagation modes.
- * Which one is active is controlled by float_status::use_first_nan.
- */
- if (status->use_first_nan) {
- if (is_nan(a_cls)) {
- return 0;
- } else {
- return 1;
- }
- } else {
- if (is_nan(b_cls)) {
- return 1;
- } else {
- return 0;
- }
- }
-#else
- /* This implements x87 NaN propagation rules:
- * SNaN + QNaN => return the QNaN
- * two SNaNs => return the one with the larger significand, silenced
- * two QNaNs => return the one with the larger significand
- * SNaN and a non-NaN => return the SNaN, silenced
- * QNaN and a non-NaN => return the QNaN
- *
- * If we get down to comparing significands and they are the same,
- * return the NaN with the positive sign bit (if any).
- */
- if (is_snan(a_cls)) {
- if (is_snan(b_cls)) {
- return aIsLargerSignificand ? 0 : 1;
- }
- return is_qnan(b_cls) ? 1 : 0;
- } else if (is_qnan(a_cls)) {
- if (is_snan(b_cls) || !is_qnan(b_cls)) {
- return 0;
- } else {
- return aIsLargerSignificand ? 0 : 1;
- }
- } else {
- return 1;
- }
-#endif
-}
-
-/*----------------------------------------------------------------------------
-| Select which NaN to propagate for a three-input operation.
-| For the moment we assume that no CPU needs the 'larger significand'
-| information.
-| Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN
-*----------------------------------------------------------------------------*/
-static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
- bool infzero, float_status *status)
-{
-#if defined(TARGET_ARM)
- /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns
- * the default NaN
- */
- if (infzero && is_qnan(c_cls)) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- return 3;
- }
-
- /* This looks different from the ARM ARM pseudocode, because the ARM ARM
- * puts the operands to a fused mac operation (a*b)+c in the order c,a,b.
- */
- if (is_snan(c_cls)) {
- return 2;
- } else if (is_snan(a_cls)) {
- return 0;
- } else if (is_snan(b_cls)) {
- return 1;
- } else if (is_qnan(c_cls)) {
- return 2;
- } else if (is_qnan(a_cls)) {
- return 0;
- } else {
- return 1;
- }
-#elif defined(TARGET_MIPS)
- if (snan_bit_is_one(status)) {
- /*
- * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan)
- * case sets InvalidOp and returns the default NaN
- */
- if (infzero) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- return 3;
- }
- /* Prefer sNaN over qNaN, in the a, b, c order. */
- if (is_snan(a_cls)) {
- return 0;
- } else if (is_snan(b_cls)) {
- return 1;
- } else if (is_snan(c_cls)) {
- return 2;
- } else if (is_qnan(a_cls)) {
- return 0;
- } else if (is_qnan(b_cls)) {
- return 1;
- } else {
- return 2;
- }
- } else {
- /*
- * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan)
- * case sets InvalidOp and returns the input value 'c'
- */
- if (infzero) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- return 2;
- }
- /* Prefer sNaN over qNaN, in the c, a, b order. */
- if (is_snan(c_cls)) {
- return 2;
- } else if (is_snan(a_cls)) {
- return 0;
- } else if (is_snan(b_cls)) {
- return 1;
- } else if (is_qnan(c_cls)) {
- return 2;
- } else if (is_qnan(a_cls)) {
- return 0;
- } else {
- return 1;
- }
- }
-#elif defined(TARGET_LOONGARCH64)
- /*
- * For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan)
- * case sets InvalidOp and returns the input value 'c'
- */
- if (infzero) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- return 2;
- }
- /* Prefer sNaN over qNaN, in the c, a, b order. */
- if (is_snan(c_cls)) {
- return 2;
- } else if (is_snan(a_cls)) {
- return 0;
- } else if (is_snan(b_cls)) {
- return 1;
- } else if (is_qnan(c_cls)) {
- return 2;
- } else if (is_qnan(a_cls)) {
- return 0;
- } else {
- return 1;
- }
-#elif defined(TARGET_PPC)
- /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer
- * to return an input NaN if we have one (ie c) rather than generating
- * a default NaN
- */
- if (infzero) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- return 2;
- }
-
- /* If fRA is a NaN return it; otherwise if fRB is a NaN return it;
- * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB
- */
- if (is_nan(a_cls)) {
- return 0;
- } else if (is_nan(c_cls)) {
- return 2;
- } else {
- return 1;
- }
-#elif defined(TARGET_RISCV)
- /* For RISC-V, InvalidOp is set when multiplicands are Inf and zero */
- if (infzero) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- }
- return 3; /* default NaN */
-#elif defined(TARGET_SPARC)
- /* For (inf,0,nan) return c. */
- if (infzero) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- return 2;
- }
- /* Prefer SNaN over QNaN, order C, B, A. */
- if (is_snan(c_cls)) {
- return 2;
- } else if (is_snan(b_cls)) {
- return 1;
- } else if (is_snan(a_cls)) {
- return 0;
- } else if (is_qnan(c_cls)) {
- return 2;
- } else if (is_qnan(b_cls)) {
- return 1;
- } else {
- return 0;
- }
-#elif defined(TARGET_XTENSA)
- /*
- * For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns
- * an input NaN if we have one (ie c).
- */
- if (infzero) {
- float_raise(float_flag_invalid | float_flag_invalid_imz, status);
- return 2;
- }
- if (status->use_first_nan) {
- if (is_nan(a_cls)) {
- return 0;
- } else if (is_nan(b_cls)) {
- return 1;
- } else {
- return 2;
- }
- } else {
- if (is_nan(c_cls)) {
- return 2;
- } else if (is_nan(b_cls)) {
- return 1;
- } else {
- return 0;
- }
- }
-#else
- /* A default implementation: prefer a to b to c.
- * This is unlikely to actually match any real implementation.
- */
- if (is_nan(a_cls)) {
- return 0;
- } else if (is_nan(b_cls)) {
- return 1;
- } else {
- return 2;
- }
-#endif
-}
-
-/*----------------------------------------------------------------------------
| Returns 1 if the double-precision floating-point value `a' is a quiet
| NaN; otherwise returns 0.
*----------------------------------------------------------------------------*/
@@ -799,58 +445,6 @@ floatx80 floatx80_silence_nan(floatx80 a, float_status *status)
}
/*----------------------------------------------------------------------------
-| Takes two extended double-precision floating-point values `a' and `b', one
-| of which is a NaN, and returns the appropriate NaN result. If either `a' or
-| `b' is a signaling NaN, the invalid exception is raised.
-*----------------------------------------------------------------------------*/
-
-floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status)
-{
- bool aIsLargerSignificand;
- FloatClass a_cls, b_cls;
-
- /* This is not complete, but is good enough for pickNaN. */
- a_cls = (!floatx80_is_any_nan(a)
- ? float_class_normal
- : floatx80_is_signaling_nan(a, status)
- ? float_class_snan
- : float_class_qnan);
- b_cls = (!floatx80_is_any_nan(b)
- ? float_class_normal
- : floatx80_is_signaling_nan(b, status)
- ? float_class_snan
- : float_class_qnan);
-
- if (is_snan(a_cls) || is_snan(b_cls)) {
- float_raise(float_flag_invalid, status);
- }
-
- if (status->default_nan_mode) {
- return floatx80_default_nan(status);
- }
-
- if (a.low < b.low) {
- aIsLargerSignificand = 0;
- } else if (b.low < a.low) {
- aIsLargerSignificand = 1;
- } else {
- aIsLargerSignificand = (a.high < b.high) ? 1 : 0;
- }
-
- if (pickNaN(a_cls, b_cls, aIsLargerSignificand, status)) {
- if (is_snan(b_cls)) {
- return floatx80_silence_nan(b, status);
- }
- return b;
- } else {
- if (is_snan(a_cls)) {
- return floatx80_silence_nan(a, status);
- }
- return a;
- }
-}
-
-/*----------------------------------------------------------------------------
| Returns 1 if the quadruple-precision floating-point value `a' is a quiet
| NaN; otherwise returns 0.
*----------------------------------------------------------------------------*/
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 027a8e5..34c962d 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -79,9 +79,6 @@ this code that are retained.
* version 2 or later. See the COPYING file in the top-level directory.
*/
-/* softfloat (and in particular the code in softfloat-specialize.h) is
- * target-dependent and needs the TARGET_* macros.
- */
#include "qemu/osdep.h"
#include <math.h>
#include "qemu/bitops.h"
@@ -132,7 +129,7 @@ this code that are retained.
if (unlikely(soft_t ## _is_denormal(*a))) { \
*a = soft_t ## _set_sign(soft_t ## _zero, \
soft_t ## _is_neg(*a)); \
- float_raise(float_flag_input_denormal, s); \
+ float_raise(float_flag_input_denormal_flushed, s); \
} \
}
@@ -220,11 +217,9 @@ GEN_INPUT_FLUSH3(float64_input_flush3, float64)
* the use of hardfloat, since hardfloat relies on the inexact flag being
* already set.
*/
-#if defined(TARGET_PPC) || defined(__FAST_MATH__)
# if defined(__FAST_MATH__)
# warning disabling hardfloat due to -ffast-math: hardfloat requires an exact \
IEEE implementation
-# endif
# define QEMU_NO_HARDFLOAT 1
# define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN
#else
@@ -404,12 +399,16 @@ float64_gen2(float64 xa, float64 xb, float_status *s,
/*
* Classify a floating point number. Everything above float_class_qnan
* is a NaN so cls >= float_class_qnan is any NaN.
+ *
+ * Note that we canonicalize denormals, so most code should treat
+ * class_normal and class_denormal identically.
*/
typedef enum __attribute__ ((__packed__)) {
float_class_unclassified,
float_class_zero,
float_class_normal,
+ float_class_denormal, /* input was a non-squashed denormal */
float_class_inf,
float_class_qnan, /* all NaNs from here */
float_class_snan,
@@ -420,12 +419,14 @@ typedef enum __attribute__ ((__packed__)) {
enum {
float_cmask_zero = float_cmask(float_class_zero),
float_cmask_normal = float_cmask(float_class_normal),
+ float_cmask_denormal = float_cmask(float_class_denormal),
float_cmask_inf = float_cmask(float_class_inf),
float_cmask_qnan = float_cmask(float_class_qnan),
float_cmask_snan = float_cmask(float_class_snan),
float_cmask_infzero = float_cmask_zero | float_cmask_inf,
float_cmask_anynan = float_cmask_qnan | float_cmask_snan,
+ float_cmask_anynorm = float_cmask_normal | float_cmask_denormal,
};
/* Flags for parts_minmax. */
@@ -460,6 +461,20 @@ static inline __attribute__((unused)) bool is_qnan(FloatClass c)
}
/*
+ * Return true if the float_cmask has only normals in it
+ * (including input denormals that were canonicalized)
+ */
+static inline bool cmask_is_only_normals(int cmask)
+{
+ return !(cmask & ~float_cmask_anynorm);
+}
+
+static inline bool is_anynorm(FloatClass c)
+{
+ return float_cmask(c) & float_cmask_anynorm;
+}
+
+/*
* Structure holding all of the decomposed parts of a float.
* The exponent is unbiased and the fraction is normalized.
*
@@ -517,7 +532,8 @@ typedef struct {
* round_mask: bits below lsb which must be rounded
* The following optional modifiers are available:
* arm_althp: handle ARM Alternative Half Precision
- * m68k_denormal: explicit integer bit for extended precision may be 1
+ * has_explicit_bit: has an explicit integer bit; this affects whether
+ * the float_status floatx80_behaviour handling applies
*/
typedef struct {
int exp_size;
@@ -527,7 +543,7 @@ typedef struct {
int frac_size;
int frac_shift;
bool arm_althp;
- bool m68k_denormal;
+ bool has_explicit_bit;
uint64_t round_mask;
} FloatFmt;
@@ -580,9 +596,7 @@ static const FloatFmt floatx80_params[3] = {
[floatx80_precision_d] = { FLOATX80_PARAMS(52) },
[floatx80_precision_x] = {
FLOATX80_PARAMS(64),
-#ifdef TARGET_M68K
- .m68k_denormal = true,
-#endif
+ .has_explicit_bit = true,
},
};
@@ -789,15 +803,15 @@ static FloatParts128 *parts128_mul(FloatParts128 *a, FloatParts128 *b,
#define parts_mul(A, B, S) \
PARTS_GENERIC_64_128(mul, A)(A, B, S)
-static FloatParts64 *parts64_muladd(FloatParts64 *a, FloatParts64 *b,
- FloatParts64 *c, int flags,
- float_status *s);
-static FloatParts128 *parts128_muladd(FloatParts128 *a, FloatParts128 *b,
- FloatParts128 *c, int flags,
- float_status *s);
+static FloatParts64 *parts64_muladd_scalbn(FloatParts64 *a, FloatParts64 *b,
+ FloatParts64 *c, int scale,
+ int flags, float_status *s);
+static FloatParts128 *parts128_muladd_scalbn(FloatParts128 *a, FloatParts128 *b,
+ FloatParts128 *c, int scale,
+ int flags, float_status *s);
-#define parts_muladd(A, B, C, Z, S) \
- PARTS_GENERIC_64_128(muladd, A)(A, B, C, Z, S)
+#define parts_muladd_scalbn(A, B, C, Z, Y, S) \
+ PARTS_GENERIC_64_128(muladd_scalbn, A)(A, B, C, Z, Y, S)
static FloatParts64 *parts64_div(FloatParts64 *a, FloatParts64 *b,
float_status *s);
@@ -1729,6 +1743,7 @@ static float64 float64r32_round_pack_canonical(FloatParts64 *p,
*/
switch (p->cls) {
case float_class_normal:
+ case float_class_denormal:
if (unlikely(p->exp == 0)) {
/*
* The result is denormal for float32, but can be represented
@@ -1789,7 +1804,7 @@ static bool floatx80_unpack_canonical(FloatParts128 *p, floatx80 f,
g_assert_not_reached();
}
- if (unlikely(floatx80_invalid_encoding(f))) {
+ if (unlikely(floatx80_invalid_encoding(f, s))) {
float_raise(float_flag_invalid, s);
return false;
}
@@ -1817,6 +1832,7 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p,
switch (p->cls) {
case float_class_normal:
+ case float_class_denormal:
if (s->floatx80_rounding_precision == floatx80_precision_x) {
parts_uncanon_normal(p, s, fmt);
frac = p->frac_hi;
@@ -1838,7 +1854,8 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p,
case float_class_inf:
/* x86 and m68k differ in the setting of the integer bit. */
- frac = floatx80_infinity_low;
+ frac = s->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero ?
+ 0 : (1ULL << 63);
exp = fmt->exp_max;
break;
@@ -2212,43 +2229,50 @@ floatx80_mul(floatx80 a, floatx80 b, float_status *status)
* Fused multiply-add
*/
-float16 QEMU_FLATTEN float16_muladd(float16 a, float16 b, float16 c,
- int flags, float_status *status)
+float16 QEMU_FLATTEN
+float16_muladd_scalbn(float16 a, float16 b, float16 c,
+ int scale, int flags, float_status *status)
{
FloatParts64 pa, pb, pc, *pr;
float16_unpack_canonical(&pa, a, status);
float16_unpack_canonical(&pb, b, status);
float16_unpack_canonical(&pc, c, status);
- pr = parts_muladd(&pa, &pb, &pc, flags, status);
+ pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
return float16_round_pack_canonical(pr, status);
}
-static float32 QEMU_SOFTFLOAT_ATTR
-soft_f32_muladd(float32 a, float32 b, float32 c, int flags,
- float_status *status)
+float16 float16_muladd(float16 a, float16 b, float16 c,
+ int flags, float_status *status)
+{
+ return float16_muladd_scalbn(a, b, c, 0, flags, status);
+}
+
+float32 QEMU_SOFTFLOAT_ATTR
+float32_muladd_scalbn(float32 a, float32 b, float32 c,
+ int scale, int flags, float_status *status)
{
FloatParts64 pa, pb, pc, *pr;
float32_unpack_canonical(&pa, a, status);
float32_unpack_canonical(&pb, b, status);
float32_unpack_canonical(&pc, c, status);
- pr = parts_muladd(&pa, &pb, &pc, flags, status);
+ pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
return float32_round_pack_canonical(pr, status);
}
-static float64 QEMU_SOFTFLOAT_ATTR
-soft_f64_muladd(float64 a, float64 b, float64 c, int flags,
- float_status *status)
+float64 QEMU_SOFTFLOAT_ATTR
+float64_muladd_scalbn(float64 a, float64 b, float64 c,
+ int scale, 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);
+ pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status);
return float64_round_pack_canonical(pr, status);
}
@@ -2267,7 +2291,7 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s)
if (unlikely(!can_use_fpu(s))) {
goto soft;
}
- if (unlikely(flags & float_muladd_halve_result)) {
+ if (unlikely(flags & float_muladd_suppress_add_product_zero)) {
goto soft;
}
@@ -2323,7 +2347,7 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s)
return ur.s;
soft:
- return soft_f32_muladd(ua.s, ub.s, uc.s, flags, s);
+ return float32_muladd_scalbn(ua.s, ub.s, uc.s, 0, flags, s);
}
float64 QEMU_FLATTEN
@@ -2338,9 +2362,6 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s)
if (unlikely(!can_use_fpu(s))) {
goto soft;
}
- if (unlikely(flags & float_muladd_halve_result)) {
- goto soft;
- }
float64_input_flush3(&ua.s, &ub.s, &uc.s, s);
if (unlikely(!f64_is_zon3(ua, ub, uc))) {
@@ -2394,7 +2415,7 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s)
return ur.s;
soft:
- return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s);
+ return float64_muladd_scalbn(ua.s, ub.s, uc.s, 0, flags, s);
}
float64 float64r32_muladd(float64 a, float64 b, float64 c,
@@ -2405,7 +2426,7 @@ float64 float64r32_muladd(float64 a, float64 b, float64 c,
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);
+ pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
return float64r32_round_pack_canonical(pr, status);
}
@@ -2418,7 +2439,7 @@ bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c,
bfloat16_unpack_canonical(&pa, a, status);
bfloat16_unpack_canonical(&pb, b, status);
bfloat16_unpack_canonical(&pc, c, status);
- pr = parts_muladd(&pa, &pb, &pc, flags, status);
+ pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
return bfloat16_round_pack_canonical(pr, status);
}
@@ -2431,7 +2452,7 @@ float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c,
float128_unpack_canonical(&pa, a, status);
float128_unpack_canonical(&pb, b, status);
float128_unpack_canonical(&pc, c, status);
- pr = parts_muladd(&pa, &pb, &pc, flags, status);
+ pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status);
return float128_round_pack_canonical(pr, status);
}
@@ -2692,6 +2713,9 @@ static void parts_float_to_ahp(FloatParts64 *a, float_status *s)
float16_params_ahp.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;
@@ -2706,6 +2730,9 @@ static void parts64_float_to_float(FloatParts64 *a, float_status *s)
if (is_nan(a->cls)) {
parts_return_nan(a, s);
}
+ if (a->cls == float_class_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
}
static void parts128_float_to_float(FloatParts128 *a, float_status *s)
@@ -2713,6 +2740,9 @@ static void parts128_float_to_float(FloatParts128 *a, float_status *s)
if (is_nan(a->cls)) {
parts_return_nan(a, s);
}
+ if (a->cls == float_class_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
}
#define parts_float_to_float(P, S) \
@@ -2725,12 +2755,21 @@ static void parts_float_to_float_narrow(FloatParts64 *a, FloatParts128 *b,
a->sign = b->sign;
a->exp = b->exp;
- if (a->cls == float_class_normal) {
+ switch (a->cls) {
+ case float_class_denormal:
+ float_raise(float_flag_input_denormal_used, s);
+ /* fall through */
+ case float_class_normal:
frac_truncjam(a, b);
- } else if (is_nan(a->cls)) {
+ break;
+ case float_class_snan:
+ case float_class_qnan:
/* Discard the low bits of the NaN. */
a->frac = b->frac_hi;
parts_return_nan(a, s);
+ break;
+ default:
+ break;
}
}
@@ -2745,6 +2784,9 @@ static void parts_float_to_float_widen(FloatParts128 *a, FloatParts64 *b,
if (is_nan(a->cls)) {
parts_return_nan(a, s);
}
+ if (a->cls == float_class_denormal) {
+ float_raise(float_flag_input_denormal_used, s);
+ }
}
float32 float16_to_float32(float16 a, bool ieee, float_status *s)
@@ -3214,6 +3256,7 @@ static Int128 float128_to_int128_scalbn(float128 a, FloatRoundMode rmode,
return int128_zero();
case float_class_normal:
+ case float_class_denormal:
if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) {
flags = float_flag_inexact;
}
@@ -3641,6 +3684,7 @@ static Int128 float128_to_uint128_scalbn(float128 a, FloatRoundMode rmode,
return int128_zero();
case float_class_normal:
+ case float_class_denormal:
if (parts_round_to_int_normal(&p, rmode, scale, 128 - 2)) {
flags = float_flag_inexact;
if (p.cls == float_class_zero) {
@@ -4382,7 +4426,11 @@ float32_hs_compare(float32 xa, float32 xb, float_status *s, bool is_quiet)
goto soft;
}
- float32_input_flush2(&ua.s, &ub.s, s);
+ if (unlikely(float32_is_denormal(ua.s) || float32_is_denormal(ub.s))) {
+ /* We may need to set the input_denormal_used flag */
+ goto soft;
+ }
+
if (isgreaterequal(ua.h, ub.h)) {
if (isgreater(ua.h, ub.h)) {
return float_relation_greater;
@@ -4432,7 +4480,11 @@ float64_hs_compare(float64 xa, float64 xb, float_status *s, bool is_quiet)
goto soft;
}
- float64_input_flush2(&ua.s, &ub.s, s);
+ if (unlikely(float64_is_denormal(ua.s) || float64_is_denormal(ub.s))) {
+ /* We may need to set the input_denormal_used flag */
+ goto soft;
+ }
+
if (isgreaterequal(ua.h, ub.h)) {
if (isgreater(ua.h, ub.h)) {
return float_relation_greater;
@@ -4844,7 +4896,7 @@ float128 float128_silence_nan(float128 a, float_status *status)
static bool parts_squash_denormal(FloatParts64 p, float_status *status)
{
if (p.exp == 0 && p.frac != 0) {
- float_raise(float_flag_input_denormal, status);
+ float_raise(float_flag_input_denormal_flushed, status);
return true;
}
@@ -4921,6 +4973,25 @@ void normalizeFloatx80Subnormal(uint64_t aSig, int32_t *zExpPtr,
}
/*----------------------------------------------------------------------------
+| Takes two extended double-precision floating-point values `a' and `b', one
+| of which is a NaN, and returns the appropriate NaN result. If either `a' or
+| `b' is a signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status)
+{
+ FloatParts128 pa, pb, *pr;
+
+ if (!floatx80_unpack_canonical(&pa, a, status) ||
+ !floatx80_unpack_canonical(&pb, b, status)) {
+ return floatx80_default_nan(status);
+ }
+
+ pr = parts_pick_nan(&pa, &pb, status);
+ return floatx80_round_pack_canonical(pr, status);
+}
+
+/*----------------------------------------------------------------------------
| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
| and extended significand formed by the concatenation of `zSig0' and `zSig1',
| and returns the proper extended double-precision floating-point value
@@ -4994,7 +5065,7 @@ floatx80 roundAndPackFloatx80(FloatX80RoundPrec roundingPrecision, bool zSign,
}
if ( zExp <= 0 ) {
if (status->flush_to_zero) {
- float_raise(float_flag_output_denormal, status);
+ float_raise(float_flag_output_denormal_flushed, status);
return packFloatx80(zSign, 0, 0);
}
isTiny = status->tininess_before_rounding
@@ -5068,9 +5139,7 @@ floatx80 roundAndPackFloatx80(FloatX80RoundPrec roundingPrecision, bool zSign,
) {
return packFloatx80( zSign, 0x7FFE, ~ roundMask );
}
- return packFloatx80(zSign,
- floatx80_infinity_high,
- floatx80_infinity_low);
+ return floatx80_default_inf(zSign, status);
}
if ( zExp <= 0 ) {
isTiny = status->tininess_before_rounding
@@ -5208,6 +5277,8 @@ float32 float32_exp2(float32 a, float_status *status)
float32_unpack_canonical(&xp, a, status);
if (unlikely(xp.cls != float_class_normal)) {
switch (xp.cls) {
+ case float_class_denormal:
+ break;
case float_class_snan:
case float_class_qnan:
parts_return_nan(&xp, status);
@@ -5217,9 +5288,8 @@ float32 float32_exp2(float32 a, float_status *status)
case float_class_zero:
return float32_one;
default:
- break;
+ g_assert_not_reached();
}
- g_assert_not_reached();
}
float_raise(float_flag_inexact, status);
@@ -5230,8 +5300,9 @@ float32 float32_exp2(float32 a, float_status *status)
float64_unpack_canonical(&rp, float64_one, status);
for (i = 0 ; i < 15 ; i++) {
+
float64_unpack_canonical(&tp, float32_exp2_coefficients[i], status);
- rp = *parts_muladd(&tp, &xnp, &rp, 0, status);
+ rp = *parts_muladd_scalbn(&tp, &xnp, &rp, 0, 0, status);
xnp = *parts_mul(&xnp, &xp, status);
}