softfloat: Move muladd_floats to softfloat-parts.c.inc

Rename to parts$N_muladd.
Implement float128_muladd with FloatParts128.

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson
2021-05-16 07:13:51 -05:00
parent aca845275a
commit dedd123c56
6 changed files with 342 additions and 214 deletions
+126
View File
@@ -413,3 +413,129 @@ static FloatPartsN *partsN(mul)(FloatPartsN *a, FloatPartsN *b,
a->sign = sign;
return a;
}
/*
* Returns the result of multiplying the floating-point values `a' and
* `b' then adding 'c', with no intermediate rounding step after the
* multiplication. The operation is performed according to the
* IEC/IEEE Standard for Binary Floating-Point Arithmetic 754-2008.
* The flags argument allows the caller to select negation of the
* addend, the intermediate product, or the final result. (The
* difference between this and having the caller do a separate
* negation is that negating externally will flip the sign bit on NaNs.)
*
* 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)
{
int ab_mask, abc_mask;
FloatPartsW p_widen, c_widen;
ab_mask = float_cmask(a->cls) | float_cmask(b->cls);
abc_mask = float_cmask(c->cls) | ab_mask;
/*
* It is implementation-defined whether the cases of (0,inf,qnan)
* and (inf,0,qnan) raise InvalidOperation or not (and what QNaN
* they return if they do), so we have to hand this information
* off to the target-specific pick-a-NaN routine.
*/
if (unlikely(abc_mask & float_cmask_anynan)) {
return parts_pick_nan_muladd(a, b, c, s, ab_mask, abc_mask);
}
if (flags & float_muladd_negate_c) {
c->sign ^= 1;
}
/* Compute the sign of the product into A. */
a->sign ^= b->sign;
if (flags & float_muladd_negate_product) {
a->sign ^= 1;
}
if (unlikely(ab_mask != float_cmask_normal)) {
if (unlikely(ab_mask == float_cmask_infzero)) {
goto d_nan;
}
if (ab_mask & float_cmask_inf) {
if (c->cls == float_class_inf && a->sign != c->sign) {
goto d_nan;
}
goto return_inf;
}
g_assert(ab_mask & float_cmask_zero);
if (c->cls == float_class_normal) {
*a = *c;
goto return_normal;
}
if (c->cls == float_class_zero) {
if (a->sign != c->sign) {
goto return_sub_zero;
}
goto return_zero;
}
g_assert(c->cls == float_class_inf);
}
if (unlikely(c->cls == float_class_inf)) {
a->sign = c->sign;
goto return_inf;
}
/* Perform the multiplication step. */
p_widen.sign = a->sign;
p_widen.exp = a->exp + b->exp + 1;
frac_mulw(&p_widen, a, b);
if (!(p_widen.frac_hi & DECOMPOSED_IMPLICIT_BIT)) {
frac_add(&p_widen, &p_widen, &p_widen);
p_widen.exp -= 1;
}
/* Perform the addition step. */
if (c->cls != float_class_zero) {
/* Zero-extend C to less significant bits. */
frac_widen(&c_widen, c);
c_widen.exp = c->exp;
if (a->sign == c->sign) {
parts_add_normal(&p_widen, &c_widen);
} else if (!parts_sub_normal(&p_widen, &c_widen)) {
goto return_sub_zero;
}
}
/* Narrow with sticky bit, for proper rounding later. */
frac_truncjam(a, &p_widen);
a->sign = p_widen.sign;
a->exp = p_widen.exp;
return_normal:
if (flags & float_muladd_halve_result) {
a->exp -= 1;
}
finish_sign:
if (flags & float_muladd_negate_result) {
a->sign ^= 1;
}
return a;
return_sub_zero:
a->sign = s->float_rounding_mode == float_round_down;
return_zero:
a->cls = float_class_zero;
goto finish_sign;
return_inf:
a->cls = float_class_inf;
goto finish_sign;
d_nan:
float_raise(float_flag_invalid, s);
parts_default_nan(a, s);
return a;
}
+197 -209
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -1197,6 +1197,8 @@ float128 float128_round_to_int(float128, float_status *status);
float128 float128_add(float128, float128, float_status *status);
float128 float128_sub(float128, float128, float_status *status);
float128 float128_mul(float128, float128, float_status *status);
float128 float128_muladd(float128, float128, float128, int,
float_status *status);
float128 float128_div(float128, float128, float_status *status);
float128 float128_rem(float128, float128, float_status *status);
float128 float128_sqrt(float128, float_status *status);
+4 -4
View File
@@ -386,7 +386,7 @@ static void bench(enum precision prec, enum op op, int n_ops, bool no_neg)
for (i = 0; i < OPS_PER_ITER; i++) {
float128 a = ops[0].f128;
float128 b = ops[1].f128;
/* float128 c = ops[2].f128; */
float128 c = ops[2].f128;
switch (op) {
case OP_ADD:
@@ -401,9 +401,9 @@ static void bench(enum precision prec, enum op op, int n_ops, bool no_neg)
case OP_DIV:
res.f128 = float128_div(a, b, &soft_status);
break;
/* case OP_FMA: */
/* res.f128 = float128_muladd(a, b, c, 0, &soft_status); */
/* break; */
case OP_FMA:
res.f128 = float128_muladd(a, b, c, 0, &soft_status);
break;
case OP_SQRT:
res.f128 = float128_sqrt(a, &soft_status);
break;
+1 -1
View File
@@ -717,7 +717,7 @@ static void do_testfloat(int op, int rmode, bool exact)
test_abz_f128(true_abz_f128M, subj_abz_f128M);
break;
case F128_MULADD:
not_implemented();
test_abcz_f128(slow_f128M_mulAdd, qemu_f128M_mulAdd);
break;
case F128_SQRT:
test_az_f128(slow_f128M_sqrt, qemu_f128M_sqrt);
+12
View File
@@ -574,6 +574,18 @@ WRAP_MULADD(qemu_f32_mulAdd, float32_muladd, float32)
WRAP_MULADD(qemu_f64_mulAdd, float64_muladd, float64)
#undef WRAP_MULADD
static void qemu_f128M_mulAdd(const float128_t *ap, const float128_t *bp,
const float128_t *cp, float128_t *res)
{
float128 a, b, c, ret;
a = soft_to_qemu128(*ap);
b = soft_to_qemu128(*bp);
c = soft_to_qemu128(*cp);
ret = float128_muladd(a, b, c, 0, &qsf);
*res = qemu_to_soft128(ret);
}
#define WRAP_CMP16(name, func, retcond) \
static bool name(float16_t a, float16_t b) \
{ \