OSDN Git Service

* config/rs6000/darwin-ldouble.c: Build file for SOFT_FLOAT.
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / darwin-ldouble.c
index 8faf3a5..8ac69f2 100644 (file)
@@ -1,5 +1,6 @@
 /* 128-bit long double support routines for Darwin.
-   Copyright (C) 1993, 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 1993, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -48,7 +49,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 
    This code currently assumes big-endian.  */
 
-#if !_SOFT_FLOAT && (defined (__MACH__) || defined (__powerpc64__) || defined (_AIX))
+#if ((!defined (__NO_FPRS__) || defined (_SOFT_FLOAT)) \
+     && !defined (__LITTLE_ENDIAN__) \
+     && (defined (__MACH__) || defined (__powerpc__) || defined (_AIX)))
 
 #define fabs(x) __builtin_fabs(x)
 #define isless(x, y) __builtin_isless (x, y)
@@ -58,16 +61,22 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 
 #define nonfinite(a) unlikely (! isless (fabs (a), inf ()))
 
+/* Define ALIASNAME as a strong alias for NAME.  */
+# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
+# define _strong_alias(name, aliasname) \
+  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
+
 /* 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.  */
 
-extern long double __gcc_qadd (double, double, double, double);
-extern long double __gcc_qsub (double, double, double, double);
-extern long double __gcc_qmul (double, double, double, double);
-extern long double __gcc_qdiv (double, double, double, double);
+long double __gcc_qadd (double, double, double, double);
+long double __gcc_qsub (double, double, double, double);
+long double __gcc_qmul (double, double, double, double);
+long double __gcc_qdiv (double, double, double, double);
 
-#if defined __ELF__ && defined SHARED
+#if defined __ELF__ && defined SHARED \
+    && (defined __powerpc64__ || !(defined __linux__ || defined __gnu_hurd__))
 /* Provide definitions of the old symbol names to satisfy apps and
    shared libs built against an older libgcc.  To access the _xlq
    symbols an explicit version reference is needed, so these won't
@@ -115,8 +124,12 @@ __gcc_qadd (double a, double aa, double c, double cc)
     {
       q = a - z;
       zz = q + c + (a - (q + z)) + aa + cc;
-      xh = z + zz;
 
+      /* Keep -0 result.  */
+      if (zz == 0.0)
+       return z;
+
+      xh = z + zz;
       if (nonfinite (xh))
        return xh;
 
@@ -132,6 +145,10 @@ __gcc_qsub (double a, double b, double c, double d)
   return __gcc_qadd (a, b, -c, -d);
 }
 
+#ifdef _SOFT_FLOAT
+static double fmsub (double, double, double);
+#endif
+
 long double
 __gcc_qmul (double a, double b, double c, double d)
 {
@@ -147,7 +164,11 @@ __gcc_qmul (double a, double b, double c, double d)
   /* Sum terms of two highest orders. */
   
   /* Use fused multiply-add to get low part of a * c.  */
+#ifndef _SOFT_FLOAT
   asm ("fmsub %0,%1,%2,%3" : "=f"(tau) : "f"(a), "f"(c), "f"(t));
+#else
+  tau = fmsub (a, c, t);
+#endif
   v = a*d;
   w = b*c;
   tau += v + w;            /* Add in other second-order terms.  */
@@ -180,7 +201,11 @@ __gcc_qdiv (double a, double b, double c, double d)
                           numerically necessary.  */
   
   /* Use fused multiply-add to get low part of c * t.   */
+#ifndef _SOFT_FLOAT
   asm ("fmsub %0,%1,%2,%3" : "=f"(sigma) : "f"(c), "f"(t), "f"(s));
+#else
+  sigma = fmsub (c, t, s);
+#endif
   v = a - s;
   
   tau = ((v-sigma)+w)/c;   /* Correction to t.  */
@@ -194,4 +219,220 @@ __gcc_qdiv (double a, double b, double c, double d)
   return z.ldval;
 }
 
+#ifdef _SOFT_FLOAT
+
+long double __gcc_qneg (double, double);
+int __gcc_qeq (double, double, double, double);
+int __gcc_qne (double, double, double, double);
+int __gcc_qge (double, double, double, double);
+int __gcc_qle (double, double, double, double);
+int __gcc_qunord (double, double, double, double);
+long double __gcc_stoq (float);
+long double __gcc_dtoq (double);
+float __gcc_qtos (double, double);
+double __gcc_qtod (double, double);
+int __gcc_qtoi (double, double);
+unsigned int __gcc_qtou (double, double);
+long double __gcc_itoq (int);
+long double __gcc_utoq (unsigned int);
+
+extern int __eqdf2 (double, double);
+extern int __ledf2 (double, double);
+extern int __gedf2 (double, double);
+extern int __unorddf2 (double, double);
+
+/* Negate 'long double' value and return the result.   */
+long double
+__gcc_qneg (double a, double aa)
+{
+  longDblUnion x;
+
+  x.dval[0] = -a;
+  x.dval[1] = -aa;
+  return x.ldval;
+}
+
+/* Compare two 'long double' values for equality.  */
+int
+__gcc_qeq (double a, double aa, double c, double cc)
+{
+  if (__eqdf2 (a, c) == 0)
+    return __eqdf2 (aa, cc);
+  return 1;
+}
+
+strong_alias (__gcc_qeq, __gcc_qne);
+
+/* Compare two 'long double' values for less than or equal.  */
+int
+__gcc_qle (double a, double aa, double c, double cc)
+{
+  if (__eqdf2 (a, c) == 0)
+    return __ledf2 (aa, cc);
+  return __ledf2 (a, c);
+}
+
+strong_alias (__gcc_qle, __gcc_qlt);
+
+/* Compare two 'long double' values for greater than or equal.  */
+int
+__gcc_qge (double a, double aa, double c, double cc)
+{
+  if (__eqdf2 (a, c) == 0)
+    return __gedf2 (aa, cc);
+  return __gedf2 (a, c);
+}
+
+strong_alias (__gcc_qge, __gcc_qgt);
+
+/* Compare two 'long double' values for unordered.  */
+int
+__gcc_qunord (double a, double aa, double c, double cc)
+{
+  if (__eqdf2 (a, c) == 0)
+    return __unorddf2 (aa, cc);
+  return __unorddf2 (a, c);
+}
+
+/* Convert single to long double.  */
+long double
+__gcc_stoq (float a)
+{
+  longDblUnion x;
+
+  x.dval[0] = (double) a;
+  x.dval[1] = 0.0;
+
+  return x.ldval;
+}
+
+/* Convert double to long double.  */
+long double
+__gcc_dtoq (double a)
+{
+  longDblUnion x;
+
+  x.dval[0] = a;
+  x.dval[1] = 0.0;
+
+  return x.ldval;
+}
+
+/* Convert long double to single.  */
+float
+__gcc_qtos (double a, double aa __attribute__ ((__unused__)))
+{
+  return (float) a;
+}
+
+/* Convert long double to double.  */
+double
+__gcc_qtod (double a, double aa __attribute__ ((__unused__)))
+{
+  return a;
+}
+
+/* Convert long double to int.  */
+int
+__gcc_qtoi (double a, double aa)
+{
+  double z = a + aa;
+  return (int) z;
+}
+
+/* Convert long double to unsigned int.  */
+unsigned int
+__gcc_qtou (double a, double aa)
+{
+  double z = a + aa;
+  return (unsigned int) z;
+}
+
+/* Convert int to long double.  */
+long double
+__gcc_itoq (int a)
+{
+  return __gcc_dtoq ((double) a);
+}
+
+/* Convert unsigned int to long double.  */
+long double
+__gcc_utoq (unsigned int a)
+{
+  return __gcc_dtoq ((double) a);
+}
+
+#include "config/soft-fp/soft-fp.h"
+#include "config/soft-fp/double.h"
+#include "config/soft-fp/quad.h"
+
+/* Compute floating point multiply-subtract with higher (quad) precision.  */
+static double
+fmsub (double a, double b, double c)
+{
+    FP_DECL_EX;
+    FP_DECL_D(A);
+    FP_DECL_D(B);
+    FP_DECL_D(C);
+    FP_DECL_Q(X);
+    FP_DECL_Q(Y);
+    FP_DECL_Q(Z);
+    FP_DECL_Q(U);
+    FP_DECL_Q(V);
+    FP_DECL_D(R);
+    double r;
+    long double u, v, x, y, z;
+
+    FP_INIT_ROUNDMODE;
+    FP_UNPACK_RAW_D (A, a);
+    FP_UNPACK_RAW_D (B, b);
+    FP_UNPACK_RAW_D (C, c);
+
+    /* Extend double to quad.  */
+#if (2 * _FP_W_TYPE_SIZE) < _FP_FRACBITS_Q
+    FP_EXTEND(Q,D,4,2,X,A);
+    FP_EXTEND(Q,D,4,2,Y,B);
+    FP_EXTEND(Q,D,4,2,Z,C);
+#else
+    FP_EXTEND(Q,D,2,1,X,A);
+    FP_EXTEND(Q,D,2,1,Y,B);
+    FP_EXTEND(Q,D,2,1,Z,C);
+#endif
+    FP_PACK_RAW_Q(x,X);
+    FP_PACK_RAW_Q(y,Y);
+    FP_PACK_RAW_Q(z,Z);
+    FP_HANDLE_EXCEPTIONS;
+
+    /* Multiply.  */
+    FP_INIT_ROUNDMODE;
+    FP_UNPACK_Q(X,x);
+    FP_UNPACK_Q(Y,y);
+    FP_MUL_Q(U,X,Y);
+    FP_PACK_Q(u,U);
+    FP_HANDLE_EXCEPTIONS;
+
+    /* Subtract.  */
+    FP_INIT_ROUNDMODE;
+    FP_UNPACK_SEMIRAW_Q(U,u);
+    FP_UNPACK_SEMIRAW_Q(Z,z);
+    FP_SUB_Q(V,U,Z);
+    FP_PACK_SEMIRAW_Q(v,V);
+    FP_HANDLE_EXCEPTIONS;
+
+    /* Truncate quad to double.  */
+    FP_INIT_ROUNDMODE;
+    FP_UNPACK_SEMIRAW_Q(V,v);
+#if (2 * _FP_W_TYPE_SIZE) < _FP_FRACBITS_Q
+    FP_TRUNC(D,Q,2,4,R,V);
+#else
+    FP_TRUNC(D,Q,1,2,R,V);
+#endif
+    FP_PACK_SEMIRAW_D(r,R);
+    FP_HANDLE_EXCEPTIONS;
+
+    return r;
+}
+
+#endif
+
 #endif