Skip to content

Commit

Permalink
fpu: Handle m68k extended precision denormals properly
Browse files Browse the repository at this point in the history
Motorola treats denormals with explicit integer bit set as
having unbiased exponent 0, unlike Intel which treats it as
having unbiased exponent 1 (more like all other IEEE formats
that have no explicit integer bit).

Add a flag on FloatFmt to differentiate the behaviour.

Reported-by: Keith Packard <[email protected]>
Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Signed-off-by: Richard Henderson <[email protected]>
  • Loading branch information
rth7680 committed Sep 16, 2023
1 parent 00f9ef8 commit 7224606
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 5 deletions.
7 changes: 4 additions & 3 deletions fpu/softfloat-parts.c.inc
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
} else {
int shift = frac_normalize(p);
p->cls = float_class_normal;
p->exp = fmt->frac_shift - fmt->exp_bias - shift + 1;
p->exp = fmt->frac_shift - fmt->exp_bias
- shift + !fmt->m68k_denormal;
}
} else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) {
p->cls = float_class_normal;
Expand Down Expand Up @@ -256,7 +257,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
is_tiny = !frac_addi(&discard, p, inc);
}

frac_shrjam(p, 1 - exp);
frac_shrjam(p, !fmt->m68k_denormal - exp);

if (p->frac_lo & round_mask) {
/* Need to recompute round-to-even/round-to-odd. */
Expand Down Expand Up @@ -287,7 +288,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
p->frac_lo &= ~round_mask;
}

exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) != 0;
exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal;
frac_shr(p, frac_shift);

if (is_tiny && (flags & float_flag_inexact)) {
Expand Down
9 changes: 8 additions & 1 deletion fpu/softfloat.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ 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
*/
typedef struct {
int exp_size;
Expand All @@ -526,6 +527,7 @@ typedef struct {
int frac_size;
int frac_shift;
bool arm_althp;
bool m68k_denormal;
uint64_t round_mask;
} FloatFmt;

Expand Down Expand Up @@ -576,7 +578,12 @@ static const FloatFmt float128_params = {
static const FloatFmt floatx80_params[3] = {
[floatx80_precision_s] = { FLOATX80_PARAMS(23) },
[floatx80_precision_d] = { FLOATX80_PARAMS(52) },
[floatx80_precision_x] = { FLOATX80_PARAMS(64) },
[floatx80_precision_x] = {
FLOATX80_PARAMS(64),
#ifdef TARGET_M68K
.m68k_denormal = true,
#endif
},
};

/* Unpack a float to parts, but do not canonicalize. */
Expand Down
2 changes: 1 addition & 1 deletion tests/tcg/m68k/Makefile.target
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#

VPATH += $(SRC_PATH)/tests/tcg/m68k
TESTS += trap
TESTS += trap denormal

# On m68k Linux supports 4k and 8k pages (but 8k is currently broken)
EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192
53 changes: 53 additions & 0 deletions tests/tcg/m68k/denormal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Test m68k extended double denormals.
*/

#include <stdio.h>
#include <stdint.h>

#define TEST(X, Y) { X, Y, X * Y }

static volatile long double test[][3] = {
TEST(0x1p+16383l, 0x1p-16446l),
TEST(0x1.1p-8223l, 0x1.1p-8224l),
TEST(1.0l, 0x1p-16383l),
};

#undef TEST

static void dump_ld(const char *label, long double ld)
{
union {
long double d;
struct {
uint32_t exp:16;
uint32_t space:16;
uint32_t h;
uint32_t l;
};
} u;

u.d = ld;
printf("%12s: % -27La 0x%04x 0x%08x 0x%08x\n", label, u.d, u.exp, u.h, u.l);
}

int main(void)
{
int i, n = sizeof(test) / sizeof(test[0]), err = 0;

for (i = 0; i < n; ++i) {
long double x = test[i][0];
long double y = test[i][1];
long double build_mul = test[i][2];
long double runtime_mul = x * y;

if (runtime_mul != build_mul) {
dump_ld("x", x);
dump_ld("y", y);
dump_ld("build_mul", build_mul);
dump_ld("runtime_mul", runtime_mul);
err = 1;
}
}
return err;
}

0 comments on commit 7224606

Please sign in to comment.