aboutsummaryrefslogtreecommitdiff
path: root/fpu/softfloat.c
diff options
context:
space:
mode:
Diffstat (limited to 'fpu/softfloat.c')
-rw-r--r--fpu/softfloat.c66
1 files changed, 60 insertions, 6 deletions
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 26f3a8d..f4fed9b 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -404,12 +404,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 +424,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 +466,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.
*
@@ -1729,6 +1749,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
@@ -1817,6 +1838,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;
@@ -2696,6 +2718,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;
@@ -2710,6 +2735,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)
@@ -2717,6 +2745,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) \
@@ -2729,12 +2760,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;
}
}
@@ -2749,6 +2789,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)
@@ -3218,6 +3261,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;
}
@@ -3645,6 +3689,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) {
@@ -4386,7 +4431,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;
@@ -4436,7 +4485,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;
@@ -5231,6 +5284,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);
@@ -5240,9 +5295,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);