From c1e55850cf08c6d0d648fd9cf93359b3254d1f0b Mon Sep 17 00:00:00 2001 From: Geoffrey Keating Date: Sat, 31 Jul 2004 01:40:18 +0000 Subject: [PATCH] rs6000.c (legitimate_lo_sum_address_p): Permit non-offsettable addresses even for DImode. 2004-07-30 Geoffrey Keating * config/rs6000/rs6000.c (legitimate_lo_sum_address_p): Permit non-offsettable addresses even for DImode. (rs6000_split_multireg_move): Cope with non-offsettable addresses being moved into multiple GPRs. * config/rs6000/rs6000.c (RS6000_DEFAULT_LONG_DOUBLE_SIZE): Default to 64. (rs6000_override_options): Use RS6000_DEFAULT_LONG_DOUBLE_SIZE. * config/rs6000/darwin.h (RS6000_DEFAULT_LONG_DOUBLE_SIZE): Define to 128. * config/rs6000/darwin-ldouble.c (isless): New macro. (inf): New macro. (nonfinite): New macro. (FPKINF): Delete. (_xlqadd): Completely rewrite. (_xlqmul): Correct overflow handling. (_xlqdiv): Correct overflow handling. * config/rs6000/darwin-ldouble-format: New file. Index: testsuite/ChangeLog 2004-07-30 Geoffrey Keating * gcc.dg/darwin-longdouble.c: New file. From-SVN: r85371 --- gcc/ChangeLog | 21 ++++ gcc/config/rs6000/darwin-ldouble-format | 84 ++++++++++++++++ gcc/config/rs6000/darwin-ldouble.c | 103 ++++++++------------ gcc/config/rs6000/darwin.h | 13 ++- gcc/config/rs6000/rs6000.c | 34 ++++++- gcc/testsuite/ChangeLog | 4 + gcc/testsuite/gcc.dg/darwin-longdouble.c | 117 +++++++++++++++++++++++ 7 files changed, 304 insertions(+), 72 deletions(-) create mode 100644 gcc/config/rs6000/darwin-ldouble-format create mode 100644 gcc/testsuite/gcc.dg/darwin-longdouble.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9535b8cef84..0120ce46d75 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +2004-07-30 Geoffrey Keating + + * config/rs6000/rs6000.c (legitimate_lo_sum_address_p): Permit + non-offsettable addresses even for DImode. + (rs6000_split_multireg_move): Cope with non-offsettable addresses + being moved into multiple GPRs. + + * config/rs6000/rs6000.c (RS6000_DEFAULT_LONG_DOUBLE_SIZE): Default + to 64. + (rs6000_override_options): Use RS6000_DEFAULT_LONG_DOUBLE_SIZE. + * config/rs6000/darwin.h (RS6000_DEFAULT_LONG_DOUBLE_SIZE): Define + to 128. + * config/rs6000/darwin-ldouble.c (isless): New macro. + (inf): New macro. + (nonfinite): New macro. + (FPKINF): Delete. + (_xlqadd): Completely rewrite. + (_xlqmul): Correct overflow handling. + (_xlqdiv): Correct overflow handling. + * config/rs6000/darwin-ldouble-format: New file. + 2004-07-30 Roger Sayle Richard Henderson diff --git a/gcc/config/rs6000/darwin-ldouble-format b/gcc/config/rs6000/darwin-ldouble-format new file mode 100644 index 00000000000..0012a332d71 --- /dev/null +++ b/gcc/config/rs6000/darwin-ldouble-format @@ -0,0 +1,84 @@ +Long double format +================== + + Each long double is made up of two IEEE doubles. The value of the +long double is the sum of the values of the two parts (except for +-0.0). The most significant part is required to be the value of the +long double rounded to the nearest double, as specified by IEEE. For +Inf values, the least significant part is required to be one of +0.0 +or -0.0. No other requirements are made; so, for example, 1.0 may be +represented as (1.0, +0.0) or (1.0, -0.0), and the low part of a NaN +is don't-care. + +Classification +-------------- + +A long double can represent any value of the form + s * 2^e * sum(k=0...105: f_k * 2^(-k)) +where 's' is +1 or -1, 'e' is between 1022 and -968 inclusive, f_0 is +1, and f_k for k>0 is 0 or 1. These are the 'normal' long doubles. + +A long double can also represent any value of the form + s * 2^-968 * sum(k=0...105: f_k * 2^(-k)) +where 's' is +1 or -1, f_0 is 0, and f_k for k>0 is 0 or 1. These are +the 'subnormal' long doubles. + +There are four long doubles that represent zero, two that represent ++0.0 and two that represent -0.0. The sign of the high part is the +sign of the long double, and the sign of the low part is ignored. + +Likewise, there are four long doubles that represent infinities, two +for +Inf and two for -Inf. + +Each NaN, quiet or signalling, that can be represented as a 'double' +can be represented as a 'long double'. In fact, there are 2^64 +equivalent representations for each one. + +There are certain other valid long doubles where both parts are +nonzero but the low part represents a value which has a bit set below +2^(e-105). These, together with the subnormal long doubles, make up +the denormal long doubles. + +Many possible long double bit patterns are not valid long doubles. +These do not represent any value. + +Limits +------ + +The maximum representable long double is 2^1024-2^918. The smallest +*normal* positive long double is 2^-968. The smallest denormalised +positive long double is 2^-1074 (this is the same as for 'double'). + +Conversions +----------- + +A double can be converted to a long double by adding a zero low part. + +A long double can be converted to a double by removing the low part. + +Comparisons +----------- + +Two long doubles can be compared by comparing the high parts, and if +those compare equal, comparing the low parts. + +Arithmetic +---------- + +The unary negate operation operates by negating the low and high parts. + +An absolute or absolute-negate operation must be done by comparing +against zero and negating if necessary. + +Addition and subtraction are performed using library routines. They +are not at present performed perfectly accurately, the result produced +will be within 1ulp of the range generated by adding or subtracting +1ulp from the input values, where a 'ulp' is 2^(e-106) given the +exponent 'e'. In the presence of cancellation, this may be +arbitrarily inaccurate. Subtraction is done by negation and addition. + +Multiplication is also performed using a library routine. Its result +will be within 2ulp of the correct result. + +Division is also performed using a library routine. Its result will +be within 3ulp of the correct result. diff --git a/gcc/config/rs6000/darwin-ldouble.c b/gcc/config/rs6000/darwin-ldouble.c index 58786075011..91c00281585 100644 --- a/gcc/config/rs6000/darwin-ldouble.c +++ b/gcc/config/rs6000/darwin-ldouble.c @@ -51,9 +51,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #if !_SOFT_FLOAT && (defined (__MACH__) || defined (__powerpc64__)) #define fabs(x) __builtin_fabs(x) +#define isless(x, y) __builtin_isless (x, y) +#define inf() __builtin_inf() #define unlikely(x) __builtin_expect ((x), 0) +#define nonfinite(a) unlikely (! isless (fabs (a), inf ())) + /* All these routines actually take two long doubles as parameters, but GCC currently generates poor code when a union is used to turn a long double into a pair of doubles. */ @@ -69,66 +73,40 @@ typedef union double dval[2]; } longDblUnion; -static const double FPKINF = 1.0/0.0; - /* Add two 'long double' values and return the result. */ long double -_xlqadd (double a, double b, double c, double d) +_xlqadd (double a, double aa, double c, double cc) { - longDblUnion z; - double t, tau, u, FPR_zero, FPR_PosInf; - - FPR_zero = 0.0; - FPR_PosInf = FPKINF; - - if (unlikely (a != a) || unlikely (c != c)) - return a + c; /* NaN result. */ + longDblUnion x; + double z, q, zz, xh; - /* Ordered operands are arranged in order of their magnitudes. */ + z = a + c; - /* Switch inputs if |(c,d)| > |(a,b)|. */ - if (fabs (c) > fabs (a)) + if (nonfinite (z)) { - t = a; - tau = b; - a = c; - b = d; - c = t; - d = tau; + z = cc + aa + c + a; + if (nonfinite (z)) + return z; + x.dval[0] = z; /* Will always be DBL_MAX. */ + zz = aa + cc; + if (fabs(a) > fabs(c)) + x.dval[1] = a - z + c + zz; + else + x.dval[1] = c - z + a + zz; } - - /* b <- second largest magnitude double. */ - if (fabs (c) > fabs (b)) + else { - t = b; - b = c; - c = t; - } + q = a - z; + zz = q + c + (a - (q + z)) + aa + cc; + xh = z + zz; - /* Thanks to commutativity, sum is invariant w.r.t. the next - conditional exchange. */ - tau = d + c; + if (nonfinite (xh)) + return xh; - /* Order the smallest magnitude doubles. */ - if (fabs (d) > fabs (c)) - { - t = c; - c = d; - d = t; + x.dval[0] = xh; + x.dval[1] = z - xh + zz; } - - t = (tau + b) + a; /* Sum values in ascending magnitude order. */ - - /* Infinite or zero result. */ - if (unlikely (t == FPR_zero) || unlikely (fabs (t) == FPR_PosInf)) - return t; - - /* Usual case. */ - tau = (((a-t) + b) + c) + d; - u = t + tau; - z.dval[0] = u; /* Final fixup for long double result. */ - z.dval[1] = (t - u) + tau; - return z.ldval; + return x.ldval; } long double @@ -141,21 +119,17 @@ long double _xlqmul (double a, double b, double c, double d) { longDblUnion z; - double t, tau, u, v, w, FPR_zero, FPR_PosInf; + double t, tau, u, v, w; - FPR_zero = 0.0; - FPR_PosInf = FPKINF; - t = a * c; /* Highest order double term. */ - if (unlikely (t != t) || unlikely (t == FPR_zero) - || unlikely (fabs (t) == FPR_PosInf)) + if (unlikely (t == 0) /* Preserve -0. */ + || nonfinite (t)) return t; - /* Finite nonzero result requires summing of terms of two highest - orders. */ + /* Sum terms of two highest orders. */ - /* Use fused multiply-add to get low part of a * c. */ + /* Use fused multiply-add to get low part of a * c. */ asm ("fmsub %0,%1,%2,%3" : "=f"(tau) : "f"(a), "f"(c), "f"(t)); v = a*d; w = b*c; @@ -163,6 +137,8 @@ _xlqmul (double a, double b, double c, double d) u = t + tau; /* Construct long double result. */ + if (nonfinite (u)) + return u; z.dval[0] = u; z.dval[1] = (t - u) + tau; return z.ldval; @@ -172,15 +148,12 @@ long double _xlqdiv (double a, double b, double c, double d) { longDblUnion z; - double s, sigma, t, tau, u, v, w, FPR_zero, FPR_PosInf; - - FPR_zero = 0.0; - FPR_PosInf = FPKINF; + double s, sigma, t, tau, u, v, w; t = a / c; /* highest order double term */ - if (unlikely (t != t) || unlikely (t == FPR_zero) - || unlikely (fabs (t) == FPR_PosInf)) + if (unlikely (t == 0) /* Preserve -0. */ + || nonfinite (t)) return t; /* Finite nonzero result requires corrections to the highest order term. */ @@ -197,6 +170,8 @@ _xlqdiv (double a, double b, double c, double d) u = t + tau; /* Construct long double result. */ + if (nonfinite (u)) + return u; z.dval[0] = u; z.dval[1] = (t - u) + tau; return z.ldval; diff --git a/gcc/config/rs6000/darwin.h b/gcc/config/rs6000/darwin.h index e09e86b892d..1f891ac561e 100644 --- a/gcc/config/rs6000/darwin.h +++ b/gcc/config/rs6000/darwin.h @@ -68,7 +68,7 @@ /* The Darwin ABI always includes AltiVec, can't be (validly) turned off. */ -#define SUBTARGET_OVERRIDE_OPTIONS \ +#define SUBTARGET_OVERRIDE_OPTIONS \ do { \ rs6000_altivec_abi = 1; \ rs6000_altivec_vrsave = 1; \ @@ -87,12 +87,19 @@ do { \ flag_pic = 2; \ } \ } \ -}while(0) +} while(0) + +/* Darwin has 128-bit long double support in libc in 10.4 and later. + Default to 128-bit long doubles even on earlier platforms for ABI + consistency; arithmetic will work even if libc and libm support is + not available. */ + +#define RS6000_DEFAULT_LONG_DOUBLE_SIZE 128 + /* We want -fPIC by default, unless we're using -static to compile for the kernel or some such. */ - #define CC1_SPEC "\ %{gused: -g -feliminate-unused-debug-symbols % 32 - && !(TARGET_HARD_FLOAT && TARGET_FPRS && mode == DFmode)) + if (GET_MODE_BITSIZE (mode) > 64) return false; return CONSTANT_P (x); @@ -11054,7 +11060,7 @@ rs6000_split_multireg_move (rtx dst, rtx src) int j = -1; bool used_update = false; - if (GET_CODE (src) == MEM && INT_REGNO_P (reg)) + if (MEM_P (src) && INT_REGNO_P (reg)) { rtx breg; @@ -11071,6 +11077,15 @@ rs6000_split_multireg_move (rtx dst, rtx src) : gen_adddi3 (breg, breg, delta_rtx)); src = gen_rtx_MEM (mode, breg); } + else if (! offsettable_memref_p (src)) + { + rtx newsrc, basereg; + basereg = gen_rtx_REG (Pmode, reg); + emit_insn (gen_rtx_SET (VOIDmode, basereg, XEXP (src, 0))); + newsrc = gen_rtx_MEM (GET_MODE (src), basereg); + MEM_COPY_ATTRIBUTES (newsrc, src); + src = newsrc; + } /* We have now address involving an base register only. If we use one of the registers to address memory, @@ -11118,6 +11133,15 @@ rs6000_split_multireg_move (rtx dst, rtx src) : gen_adddi3 (breg, breg, delta_rtx)); dst = gen_rtx_MEM (mode, breg); } + else if (! offsettable_memref_p (dst)) + { + rtx newdst, basereg; + basereg = gen_rtx_REG (Pmode, reg); + emit_insn (gen_rtx_SET (VOIDmode, basereg, XEXP (dst, 0))); + newdst = gen_rtx_MEM (GET_MODE (dst), basereg); + MEM_COPY_ATTRIBUTES (newdst, dst); + dst = newdst; + } } for (i = 0; i < nregs; i++) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7280c2da478..8bb22cd6325 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2004-07-30 Geoffrey Keating + + * gcc.dg/darwin-longdouble.c: New file. + 2004-07-30 Richard Henderson * gfortran.fortran-torture/execute/intrinsic_rrspacing.f90: Fix diff --git a/gcc/testsuite/gcc.dg/darwin-longdouble.c b/gcc/testsuite/gcc.dg/darwin-longdouble.c new file mode 100644 index 00000000000..54f090ddff6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/darwin-longdouble.c @@ -0,0 +1,117 @@ +/* { dg-do run { target powerpc*-*-darwin* } } */ +/* { dg-options "" } */ +/* No options so 'long long' can be used. */ + +#include + +typedef unsigned long long uint64_t; +typedef uint64_t ldbits[2]; + +union ldu +{ + ldbits lb; + long double ld; +}; + +static const struct { + ldbits a; + ldbits b; + ldbits result; +} single_tests[] = { + /* Test of values that add to near +Inf. */ + { { 0x7FEFFFFFFFFFFFFFLL, 0xFC88000000000000LL }, + { 0x7C94000000000000LL, 0x0000000000000000LL }, + { 0x7FEFFFFFFFFFFFFFLL, 0x7C80000000000000LL } }, + { { 0x7FEFFFFFFFFFFFFFLL, 0x7C8FFFFFFFFFFFFFLL }, + { 0x792FFFFFFFFFFFFFLL, 0x0000000000000000LL }, + { 0x7FEFFFFFFFFFFFFFLL, 0x7C8FFFFFFFFFFFFFLL } }, + { { 0x7FEFFFFFFFFFFFFFLL, 0x7C8FFFFFFFFFFFFFLL }, + { 0x7930000000000000LL, 0xF5DFFFFFFFFFFFFFLL }, + /* correct result is: { 0x7FEFFFFFFFFFFFFFLL, 0x7C8FFFFFFFFFFFFFLL } */ + { 0x7FF0000000000000LL, 0x0000000000000000LL } }, + /* Test of values that add to +Inf. */ + { { 0x7FEFFFFFFFFFFFFFLL, 0x7C8FFFFFFFFFFFFFLL }, + { 0x7930000000000000LL, 0x0000000000000000LL }, + { 0x7FF0000000000000LL, 0x0000000000000000LL } }, + /* Tests of Inf addition. */ + { { 0x7FF0000000000000LL, 0x0000000000000000LL }, + { 0x0000000000000000LL, 0x0000000000000000LL }, + { 0x7FF0000000000000LL, 0x0000000000000000LL } }, + { { 0x7FF0000000000000LL, 0x0000000000000000LL }, + { 0x7FF0000000000000LL, 0x0000000000000000LL }, + { 0x7FF0000000000000LL, 0x0000000000000000LL } }, + /* Test of Inf addition producing NaN. */ + { { 0x7FF0000000000000LL, 0x0000000000000000LL }, + { 0xFFF0000000000000LL, 0x0000000000000000LL }, + { 0x7FF8000000000000LL, 0x0000000000000000LL } }, + /* Tests of NaN addition. */ + { { 0x7FF8000000000000LL, 0x0000000000000000LL }, + { 0x0000000000000000LL, 0x0000000000000000LL }, + { 0x7FF8000000000000LL, 0x7FF8000000000000LL } }, + { { 0x7FF8000000000000LL, 0x0000000000000000LL }, + { 0x7FF0000000000000LL, 0x0000000000000000LL }, + { 0x7FF8000000000000LL, 0x7FF8000000000000LL } }, + /* Addition of positive integers, with interesting rounding properties. */ + { { 0x4690000000000000LL, 0x4330000000000000LL }, + { 0x4650000000000009LL, 0xC2FFFFFFFFFFFFF2LL }, + /* correct result is: { 0x4691000000000001LL, 0xC32C000000000000LL } */ + { 0x4691000000000001LL, 0xc32bfffffffffffeLL } }, + { { 0x4690000000000000LL, 0x4330000000000000LL }, + { 0x4650000000000008LL, 0x42F0000000000010LL }, + { 0x4691000000000001LL, 0xC32E000000000000LL } }, + { { 0x469FFFFFFFFFFFFFLL, 0x433FFFFFFFFFFFFFLL }, + { 0x4340000000000000LL, 0x3FF0000000000000LL }, + { 0x46A0000000000000LL, 0x0000000000000000LL } }, + { { 0x469FFFFFFFFFFFFFLL, 0x433FFFFFFFFFFFFFLL }, + { 0x4340000000000000LL, 0x0000000000000000LL }, + { 0x46A0000000000000LL, 0xBFF0000000000000LL } }, + /* Subtraction of integers, with cancellation. */ + { { 0x4690000000000000LL, 0x4330000000000000LL }, + { 0xC690000000000000LL, 0xC330000000000000LL }, + { 0x0000000000000000LL, 0x0000000000000000LL } }, + { { 0x4690000000000000LL, 0x4330000000000000LL }, + { 0xC330000000000000LL, 0x0000000000000000LL }, + { 0x4690000000000000LL, 0x0000000000000000LL } }, + { { 0x4690000000000000LL, 0x4330000000000000LL }, + { 0xC330000000000000LL, 0x3FA0000000000000LL }, + { 0x4690000000000000LL, 0x3FA0000000000000LL } }, + { { 0x4690000000000000LL, 0x4330000000000000LL }, + { 0xC690000000000000LL, 0x3FA0000000000000LL }, + /* correct result is: { 0x4330000000000000LL, 0x3FA0000000000000LL } */ + { 0x4330000000000000LL, 0x0000000000000000LL } } +}; + +static int fail = 0; + +static void +run_single_tests (void) +{ + size_t i; + for (i = 0; i < sizeof (single_tests) / sizeof (single_tests[0]); i++) + { + union ldu a, b, result, expected; + memcpy (a.lb, single_tests[i].a, sizeof (ldbits)); + memcpy (b.lb, single_tests[i].b, sizeof (ldbits)); + memcpy (expected.lb, single_tests[i].result, sizeof (ldbits)); + result.ld = a.ld + b.ld; + if (memcmp (result.lb, expected.lb, + result.ld == result.ld ? sizeof (ldbits) : sizeof (double)) + != 0) + { + printf ("FAIL: %016llx %016llx + %016llx %016llx\n", + a.lb[0], a.lb[1], b.lb[0], b.lb[1]); + printf (" = %016llx %016llx not %016llx %016llx\n", + result.lb[0], result.lb[1], expected.lb[0], expected.lb[1]); + fail = 1; + } + } +} + +int main(void) +{ + run_single_tests(); + if (fail) + abort (); + else + exit (0); +} -- 2.30.2