Skip to content

Commit

Permalink
libgcc/m68k: Fixes for soft float
Browse files Browse the repository at this point in the history
Check for non-zero denorm in __adddf3. Need to check both the upper and
lower 32-bit chunks of a 64-bit float for a non-zero value when
checking to see if the value is -0.

Fix __addsf3 when the sum exponent is exactly 0xff to ensure that
produces infinity and not nan.

Handle converting NaN/inf values between formats.

Handle underflow and overflow when truncating.

Write a replacement for __fixxfsi so that it does not raise extra
exceptions during an extra conversion from long double to double.

Return correctly signed zero on float and double divide underflow

Return positive qNaN instead of negative.

Signed-off-by: Keith Packard <keithp@keithp.com>
  • Loading branch information
keith-packard committed Aug 23, 2023
1 parent d18296e commit 3393fcc
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 39 deletions.
162 changes: 130 additions & 32 deletions libgcc/config/m68k/fpgnulib.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#define SIGNBIT 0x80000000L
#define HIDDEN (1L << 23L)
#define SIGN(fp) ((fp) & SIGNBIT)
#define EXPMASK 0xFFL
#define EXP(fp) (((fp) >> 23L) & 0xFF)
#define MANT(fp) (((fp) & 0x7FFFFFL) | HIDDEN)
#define PACK(s,e,m) ((s) | ((e) << 23L) | (m))
Expand Down Expand Up @@ -262,6 +263,9 @@ __extendsfdf2 (float a1)
mant &= ~HIDDEN;
}
exp = exp - EXCESS + EXCESSD;
/* Handle inf and NaN */
if (exp == EXPMASK - EXCESS + EXCESSD)
exp = EXPDMASK;
dl.l.upper |= exp << 20;
dl.l.upper |= mant >> 3;
dl.l.lower = mant << 29;
Expand Down Expand Up @@ -295,40 +299,52 @@ __truncdfsf2 (double a1)
/* shift double mantissa 6 bits so we can round */
sticky |= mant & ((1 << 6) - 1);
mant >>= 6;

/* Check for underflow and denormals. */
if (exp <= 0)
if (exp == EXPDMASK - EXCESSD + EXCESS)
{
exp = EXPMASK;
mant = mant >> 1 | (mant & 1) | !!sticky;
}
else
{
if (exp < -24)
/* Check for underflow and denormals. */
if (exp <= 0)
{
sticky |= mant;
mant = 0;
if (exp < -24)
{
sticky |= mant;
mant = 0;
}
else
{
sticky |= mant & ((1 << (1 - exp)) - 1);
mant >>= 1 - exp;
}
exp = 0;
}
else

/* now round */
shift = 1;
if ((mant & 1) && (sticky || (mant & 2)))
{
sticky |= mant & ((1 << (1 - exp)) - 1);
mant >>= 1 - exp;
}
exp = 0;
}

/* now round */
shift = 1;
if ((mant & 1) && (sticky || (mant & 2)))
{
int rounding = exp ? 2 : 1;
int rounding = exp ? 2 : 1;

mant += 1;
mant += 1;

/* did the round overflow? */
if (mant >= (HIDDEN << rounding))
/* did the round overflow? */
if (mant >= (HIDDEN << rounding))
{
exp++;
shift = rounding;
}
}
/* shift down */
mant >>= shift;
if (exp >= EXPMASK)
{
exp++;
shift = rounding;
exp = EXPMASK;
mant = 0;
}
}
/* shift down */
mant >>= shift;

mant &= ~HIDDEN;

Expand Down Expand Up @@ -432,8 +448,31 @@ __extenddfxf2 (double d)
}

exp = EXPD (dl) - EXCESSD + EXCESSX;
ldl.l.upper |= exp << 16;
dl.l.upper &= MANTDMASK;
ldl.l.middle = HIDDENX;

/* Recover from a denorm. */
if (exp == -EXCESSD + EXCESSX)
{
exp++;
while ((dl.l.upper & HIDDEND) == 0)
{
exp--;
dl.l.upper = (dl.l.upper << 1) | (dl.l.lower >> 31);
dl.l.lower = dl.l.lower << 1;
}
}

/* Handle inf and NaN */
else if (exp == EXPDMASK - EXCESSD + EXCESSX)
{
exp = EXPXMASK;
/* No hidden one bit for INF */
if (dl.l.upper == 0 && dl.l.lower == 0)
ldl.l.middle = 0;
}

ldl.l.upper |= exp << 16;
/* 31-20: # mantissa bits in ldl.l.middle - # mantissa bits in dl.l.upper */
ldl.l.middle |= (dl.l.upper & MANTDMASK) << (31 - 20);
/* 1+20: explicit-integer-bit + # mantissa bits in dl.l.upper */
Expand Down Expand Up @@ -464,9 +503,38 @@ __truncxfdf2 (long double ld)
}

exp = EXPX (ldl) - EXCESSX + EXCESSD;
/* ??? quick and dirty: keep `exp' sane */
if (exp >= EXPDMASK)
exp = EXPDMASK - 1;
/* Check for underflow and denormals. */
if (exp <= 0)
{
if (exp < -53)
{
ldl.l.middle = 0;
ldl.l.lower = 0;
}
else if (exp < -30)
{
ldl.l.lower = (ldl.l.middle & MANTXMASK) >> ((1 - exp) - 32);
ldl.l.middle &= ~MANTXMASK;
}
else
{
ldl.l.lower >>= 1 - exp;
ldl.l.lower |= (ldl.l.middle & MANTXMASK) << (32 - (1 - exp));
ldl.l.middle = (ldl.l.middle & ~MANTXMASK) | (ldl.l.middle & MANTXMASK >> (1 - exp));
}
exp = 0;
}
else if (exp == EXPXMASK - EXCESSX + EXCESSD)
{
exp = EXPDMASK;
ldl.l.middle |= ldl.l.lower;
}
else if (exp >= EXPDMASK)
{
exp = EXPDMASK;
ldl.l.middle = 0;
ldl.l.lower = 0;
}
dl.l.upper |= exp << (32 - (EXPDBITS + 1));
/* +1-1: add one for sign bit, but take one off for explicit-integer-bit */
dl.l.upper |= (ldl.l.middle & MANTXMASK) >> (EXPDBITS + 1 - 1);
Expand Down Expand Up @@ -511,10 +579,40 @@ __floatunsixf (unsigned long l)

/* convert a long double to an int */
long
__fixxfsi (long double ld)
__fixxfsi (long double a)
{
long foo = __fixdfsi ((double) ld);
return foo;
union long_double_long ldl;
long exp;
long l;

ldl.ld = a;

exp = EXPX(ldl);
if (exp == 0 && ldl.l.middle == 0 && ldl.l.lower == 0)
return 0;

exp = exp - EXCESSX - 64;

if (exp > 0)
{
/* Return largest integer. */
return SIGNX (ldl) ? 0x80000000L : 0x7fffffffL;
}

if (exp <= -64)
return 0;

if (exp <= -32)
{
ldl.l.lower = ldl.l.middle >> (-exp - 32);
}
else if (exp < 0)
{
ldl.l.lower = ldl.l.lower >> -exp;
ldl.l.lower |= ldl.l.middle << (32 + exp);
}

return SIGNX(ldl) ? -ldl.l.lower : ldl.l.lower;
}

/* The remaining provide crude math support by working in double precision. */
Expand Down
20 changes: 13 additions & 7 deletions libgcc/config/m68k/lb1sf68.S
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ SYM (__modsi3):
.globl SYM (_fpCCR)
.globl $_exception_handler

QUIET_NaN = 0xffffffff
QUIET_NaN = 0x7fffffff

D_MAX_EXP = 0x07ff
D_BIAS = 1022
Expand Down Expand Up @@ -700,9 +700,10 @@ Ld$overflow:
PICJUMP $_exception_handler

Ld$underflow:
| Return 0 and set the exception flags
| Return a properly signed 0 and set the exception flags
movel IMM (0),d0
movel d0,d1
orl d7,d0
movew IMM (INEXACT_RESULT+UNDERFLOW),d7
moveq IMM (DOUBLE_FLOAT),d6
PICJUMP $_exception_handler
Expand All @@ -711,6 +712,7 @@ Ld$inop:
| Return a quiet NaN and set the exception flags
movel IMM (QUIET_NaN),d0
movel d0,d1
bset IMM (31),d1
movew IMM (INEXACT_RESULT+INVALID_OPERATION),d7
moveq IMM (DOUBLE_FLOAT),d6
PICJUMP $_exception_handler
Expand Down Expand Up @@ -1383,6 +1385,8 @@ Ladddf$a:
bge 2f |
movel d0,d0 | check for zero, since we don't '
bne Ladddf$ret | want to return -0 by mistake
movel d1,d1 |
bne Ladddf$ret |
bclr IMM (31),d7 |
bra Ladddf$ret |
2:
Expand Down Expand Up @@ -2080,6 +2084,7 @@ Ldivdf$b$nf:
| If d2 == 0x7ff00000 we have to check d3.
tstl d3 |
bne Ld$inop | if d3 <> 0, b is NaN
movel a0,d7 | put a's sign
bra Ld$underflow | else b is +/-INFINITY, so signal underflow

Ldivdf$a$nf:
Expand All @@ -2090,8 +2095,7 @@ Ldivdf$a$nf:
| If a is INFINITY we have to check b
cmpl d7,d2 | compare b with INFINITY
bge Ld$inop | if b is NaN or INFINITY return NaN
tstl d3 |
bne Ld$inop |
movl a0,d7 | restore sign bit to d7
bra Ld$overflow | else return overflow

| If a number is denormalized we put an exponent of 1 but do not put the
Expand Down Expand Up @@ -2186,6 +2190,7 @@ Lround$exit:
#endif
beq 2f | if not loop back
bra 1b |
movel a0,d7
bra Ld$underflow | safety check, shouldn't execute '
2: orl d6,d2 | this is a trick so we don't lose '
orl d7,d3 | the bits which were flushed right
Expand Down Expand Up @@ -2548,7 +2553,7 @@ Lround$to$minus:
.globl SYM (_fpCCR)
.globl $_exception_handler

QUIET_NaN = 0xffffffff
QUIET_NaN = 0x7fffffff
SIGNL_NaN = 0x7f800001
INFINITY = 0x7f800000

Expand Down Expand Up @@ -2614,8 +2619,9 @@ Lf$overflow:
PICJUMP $_exception_handler

Lf$underflow:
| Return 0 and set the exception flags
| Return a properly signed 0 and set the exception flags
moveq IMM (0),d0
orl d7,d0
moveq IMM (INEXACT_RESULT+UNDERFLOW),d7
moveq IMM (SINGLE_FLOAT),d6
PICJUMP $_exception_handler
Expand Down Expand Up @@ -2936,7 +2942,7 @@ Laddsf$4:
#else
cmpl IMM (0xff),d2
#endif
bhi 1f
bge 1f
bclr IMM (FLT_MANT_DIG-1),d0
#ifndef __mcoldfire__
lslw IMM (7),d2
Expand Down

0 comments on commit 3393fcc

Please sign in to comment.