softfloat: add APIs to handle alternative sNaN propagation for fmax/fmin

For "fmax/fmin ft0, ft1, ft2" and if one of the inputs is sNaN,

  The original logic:
    Return NaN and set invalid flag if ft1 == sNaN || ft2 == sNan.

  The alternative path:
    Set invalid flag if ft1 == sNaN || ft2 == sNaN.
    Return NaN only if ft1 == NaN && ft2 == NaN.

The IEEE 754 spec allows both implementation and some architecture such
as riscv choose different defintions in two spec versions.
(riscv-spec-v2.2 use original version, riscv-spec-20191213 changes to
 alternative)

Signed-off-by: Chih-Min Chao <chihmin.chao@sifive.com>
Signed-off-by: Frank Chang <frank.chang@sifive.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20211021160847.2748577-2-frank.chang@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Chih-Min Chao 2021-10-22 00:08:45 +08:00 committed by Alistair Francis
parent 50d1608764
commit 0e9030376e
3 changed files with 46 additions and 8 deletions

View file

@ -1219,14 +1219,35 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b,
if (unlikely(ab_mask & float_cmask_anynan)) {
/*
* For minnum/maxnum, if one operand is a QNaN, and the other
* For minNum/maxNum (IEEE 754-2008)
* or minimumNumber/maximumNumber (IEEE 754-2019),
* if one operand is a QNaN, and the other
* operand is numerical, then return numerical argument.
*/
if ((flags & minmax_isnum)
if ((flags & (minmax_isnum | minmax_isnumber))
&& !(ab_mask & float_cmask_snan)
&& (ab_mask & ~float_cmask_qnan)) {
return is_nan(a->cls) ? b : a;
}
/*
* In IEEE 754-2019, minNum, maxNum, minNumMag and maxNumMag
* are removed and replaced with minimum, minimumNumber, maximum
* and maximumNumber.
* minimumNumber/maximumNumber behavior for SNaN is changed to:
* If both operands are NaNs, a QNaN is returned.
* If either operand is a SNaN,
* an invalid operation exception is signaled,
* but unless both operands are NaNs,
* the SNaN is otherwise ignored and not converted to a QNaN.
*/
if ((flags & minmax_isnumber)
&& (ab_mask & float_cmask_snan)
&& (ab_mask & ~float_cmask_anynan)) {
float_raise(float_flag_invalid, s);
return is_nan(a->cls) ? b : a;
}
return parts_pick_nan(a, b, s);
}