From 7bab964916314efb6dd1f54828f9332ee412f682 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 22 Aug 2023 12:28:53 -0700 Subject: [PATCH] libgcc/m68k: Fixes for soft float 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 --- libgcc/config/m68k/fpgnulib.c | 155 +++++++++++++++++++++++++++------- libgcc/config/m68k/lb1sf68.S | 20 +++-- 2 files changed, 137 insertions(+), 38 deletions(-) diff --git a/libgcc/config/m68k/fpgnulib.c b/libgcc/config/m68k/fpgnulib.c index fe41edf26aa03..d7388d06b0d20 100644 --- a/libgcc/config/m68k/fpgnulib.c +++ b/libgcc/config/m68k/fpgnulib.c @@ -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)) @@ -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; @@ -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; @@ -432,6 +448,24 @@ __extenddfxf2 (double d) } exp = EXPD (dl) - EXCESSD + EXCESSX; + dl.l.upper &= MANTDMASK; + + /* 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; + ldl.l.upper |= exp << 16; ldl.l.middle = HIDDENX; /* 31-20: # mantissa bits in ldl.l.middle - # mantissa bits in dl.l.upper */ @@ -464,9 +498,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); @@ -511,10 +574,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. */ diff --git a/libgcc/config/m68k/lb1sf68.S b/libgcc/config/m68k/lb1sf68.S index 8ba85c53656d9..e12888bd7f893 100644 --- a/libgcc/config/m68k/lb1sf68.S +++ b/libgcc/config/m68k/lb1sf68.S @@ -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 @@ -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 @@ -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 @@ -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: @@ -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: @@ -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 @@ -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 @@ -2548,7 +2553,7 @@ Lround$to$minus: .globl SYM (_fpCCR) .globl $_exception_handler -QUIET_NaN = 0xffffffff +QUIET_NaN = 0x7fffffff SIGNL_NaN = 0x7f800001 INFINITY = 0x7f800000 @@ -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 @@ -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