about summary refs log tree commit diff
path: root/sysdeps/powerpc
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>2006-03-16 11:47:24 +0000
committerRoland McGrath <roland@gnu.org>2006-03-16 11:47:24 +0000
commit5c68d401698a58cf7da150d9cce769fa6679ba5f (patch)
tree0f77818c4e9204e385b3cb1777e9171b75a7949e /sysdeps/powerpc
parent671ca699ddfee826a74a32590d369672b8039321 (diff)
downloadglibc-5c68d401698a58cf7da150d9cce769fa6679ba5f.tar.gz
glibc-5c68d401698a58cf7da150d9cce769fa6679ba5f.tar.xz
glibc-5c68d401698a58cf7da150d9cce769fa6679ba5f.zip
[BZ #2423]
2006-03-07  Jakub Jelinek  <jakub@redhat.com>
	[BZ #2423]
	* math/libm-test.inc [TEST_LDOUBLE] (ceil_test, floor_test, rint_test,
	round_test, trunc_test): Only run some of the new tests if
	LDBL_MANT_DIG > 100.

2006-03-03  Steven Munroe  <sjmunroe@us.ibm.com>
	    Alan Modra  <amodra@bigpond.net.au>

	* sysdeps/powerpc/fpu/fenv_libc.h (__fegetround, __fesetround):
	Define inline implementations.
	* sysdeps/powerpc/fpu/fegetround.c: Use __fegetround.
	* sysdeps/powerpc/fpu/fesetround.c: Use __fesetround.

	* sysdeps/powerpc/fpu/math_ldbl.h: New file.

	[BZ #2423]
	* math/libm-test.inc [TEST_LDOUBLE] (ceil_test, floor_test, rint_test,
	round_test, trunc_test): Add new tests.
	* sysdeps/ieee754/ldbl-128ibm/math_ldbl.h
	(EXTRACT_IBM_EXTENDED_MANTISSA, INSERT_IBM_EXTENDED_MANTISSA):
	Removed, replaced with ...
	(ldbl_extract_mantissa, ldbl_insert_mantissa, ldbl_pack, ldbl_unpack,
	ldbl_canonicalise, ldbl_nearbyint): New functions.
	* sysdeps/ieee754/ldbl-128ibm/e_fmodl.c (__ieee754_fmodl): Replace
	EXTRACT_IBM_EXTENDED_MANTISSA and INSERT_IBM_EXTENDED_MANTISSA
	with ldbl_extract_mantissa and ldbl_insert_mantissa.
	* sysdeps/ieee754/ldbl-128ibm/e_rem_pio2l.c (__ieee754_rem_pio2l):
	Replace EXTRACT_IBM_EXTENDED_MANTISSA with ldbl_extract_mantissa.
	(ldbl_extract_mantissa, ldbl_insert_mantissa): New inline functions.
	* sysdeps/ieee754/ldbl-128ibm/s_ceill.c (__ceill): Handle rounding
	that spans doubles in IBM long double format.
	* sysdeps/ieee754/ldbl-128ibm/s_floorl.c: Likewise.
	* sysdeps/ieee754/ldbl-128ibm/s_rintl.c: Likewise.
	* sysdeps/ieee754/ldbl-128ibm/s_roundl.c: Likewise.
	* sysdeps/ieee754/ldbl-128ibm/s_truncl.c: Likewise.
	* sysdeps/powerpc/powerpc64/fpu/s_rintl.S: File removed.
Diffstat (limited to 'sysdeps/powerpc')
-rw-r--r--sysdeps/powerpc/fpu/fegetround.c4
-rw-r--r--sysdeps/powerpc/fpu/fenv_libc.h37
-rw-r--r--sysdeps/powerpc/fpu/fesetround.c18
-rw-r--r--sysdeps/powerpc/fpu/math_ldbl.h189
-rw-r--r--sysdeps/powerpc/powerpc64/fpu/s_rintl.S113
5 files changed, 230 insertions, 131 deletions
diff --git a/sysdeps/powerpc/fpu/fegetround.c b/sysdeps/powerpc/fpu/fegetround.c
index ae521fdf10..93663ad546 100644
--- a/sysdeps/powerpc/fpu/fegetround.c
+++ b/sysdeps/powerpc/fpu/fegetround.c
@@ -23,7 +23,5 @@
 int
 fegetround (void)
 {
-  int result;
-  asm ("mcrfs 7,7 ; mfcr %0" : "=r"(result) : : "cr7"); \
-  return result & 3;
+  return __fegetround();
 }
diff --git a/sysdeps/powerpc/fpu/fenv_libc.h b/sysdeps/powerpc/fpu/fenv_libc.h
index 7ae12a7d2b..fd5fc0c767 100644
--- a/sysdeps/powerpc/fpu/fenv_libc.h
+++ b/sysdeps/powerpc/fpu/fenv_libc.h
@@ -1,5 +1,5 @@
 /* Internal libc stuff for floating point environment routines.
-   Copyright (C) 1997 Free Software Foundation, Inc.
+   Copyright (C) 1997, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -54,6 +54,41 @@ typedef union
   unsigned int l[2];
 } fenv_union_t;
 
+
+static inline int
+__fegetround (void)
+{
+  int result;
+  asm volatile ("mcrfs 7,7\n\t"
+		"mfcr  %0" : "=r"(result) : : "cr7");
+  return result & 3;
+}
+#define fegetround() __fegetround()
+
+static inline int
+__fesetround (int round)
+{
+  if ((unsigned int) round < 2)
+    {
+       asm volatile ("mtfsb0 30");
+       if ((unsigned int) round == 0)
+         asm volatile ("mtfsb0 31");
+       else
+         asm volatile ("mtfsb1 31");
+    }
+  else
+    {
+       asm volatile ("mtfsb1 30");
+       if ((unsigned int) round == 2)
+         asm volatile ("mtfsb0 31");
+       else
+         asm volatile ("mtfsb1 31");
+    }
+
+  return 0;
+}
+#define fesetround(mode) __fesetround(mode)
+
 /* Definitions of all the FPSCR bit numbers */
 enum {
   FPSCR_FX = 0,    /* exception summary */
diff --git a/sysdeps/powerpc/fpu/fesetround.c b/sysdeps/powerpc/fpu/fesetround.c
index 67518d0df4..a7efa3bbb0 100644
--- a/sysdeps/powerpc/fpu/fesetround.c
+++ b/sysdeps/powerpc/fpu/fesetround.c
@@ -1,5 +1,5 @@
 /* Set current rounding direction.
-   Copyright (C) 1997, 2005 Free Software Foundation, Inc.
+   Copyright (C) 1997, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
 
@@ -20,23 +20,13 @@
 
 #include <fenv_libc.h>
 
+#undef fesetround
 int
 fesetround (int round)
 {
-  fenv_union_t u;
-
   if ((unsigned int) round > 3)
     return 1;
-
-  /* Get the current state.  */
-  u.fenv = fegetenv_register ();
-
-  /* Set the relevant bits.  */
-  u.l[1] = (u.l[1] & ~3)  |  (round & 3);
-
-  /* Put the new state in effect.  */
-  fesetenv_register (u.fenv);
-
-  return 0;
+  else
+    return __fesetround(round);
 }
 libm_hidden_def (fesetround)
diff --git a/sysdeps/powerpc/fpu/math_ldbl.h b/sysdeps/powerpc/fpu/math_ldbl.h
new file mode 100644
index 0000000000..6cd6d0bdfe
--- /dev/null
+++ b/sysdeps/powerpc/fpu/math_ldbl.h
@@ -0,0 +1,189 @@
+#ifndef _MATH_PRIVATE_H_
+#error "Never use <math_ldbl.h> directly; include <math_private.h> instead."
+#endif
+
+#include <sysdeps/ieee754/ldbl-128/math_ldbl.h>
+#include <ieee754.h>
+  
+static inline void
+ldbl_extract_mantissa (int64_t *hi64, u_int64_t *lo64, int *exp, long double x)
+{
+  /* We have 105 bits of mantissa plus one implicit digit.  Since
+     106 bits are representable we use the first implicit digit for
+     the number before the decimal point and the second implicit bit
+     as bit 53 of the mantissa.  */
+  unsigned long long hi, lo;
+  int ediff;
+  union ibm_extended_long_double eldbl;
+  eldbl.d = x;
+  *exp = eldbl.ieee.exponent - IBM_EXTENDED_LONG_DOUBLE_BIAS;
+
+  lo = ((long long)eldbl.ieee.mantissa2 << 32) | eldbl.ieee.mantissa3;
+  hi = ((long long)eldbl.ieee.mantissa0 << 32) | eldbl.ieee.mantissa1;
+  /* If the lower double is not a denomal or zero then set the hidden
+     53rd bit.  */
+  if (eldbl.ieee.exponent2 > 0x001)
+    {
+      lo |= (1ULL << 52);
+      lo = lo << 7; /* pre-shift lo to match ieee854.  */
+      /* The lower double is normalized separately from the upper.  We
+	 may need to adjust the lower manitissa to reflect this.  */
+      ediff = eldbl.ieee.exponent - eldbl.ieee.exponent2;
+      if (ediff > 53)
+	lo = lo >> (ediff-53);
+    }
+  hi |= (1ULL << 52);
+  
+  if ((eldbl.ieee.negative != eldbl.ieee.negative2)
+      && ((eldbl.ieee.exponent2 != 0) && (lo != 0LL)))
+    {
+      hi--;
+      lo = (1ULL << 60) - lo;
+      if (hi < (1ULL << 52))
+	{
+	  /* we have a borrow from the hidden bit, so shift left 1.  */
+	  hi = (hi << 1) | (lo >> 59);
+	  lo = 0xfffffffffffffffLL & (lo << 1);
+	  *exp = *exp - 1;
+	}
+    }
+  *lo64 = (hi << 60) | lo;
+  *hi64 = hi >> 4;
+}
+
+static inline long double
+ldbl_insert_mantissa (int sign, int exp, int64_t hi64, u_int64_t lo64)
+{
+  union ibm_extended_long_double u;
+  unsigned long hidden2, lzcount;
+  unsigned long long hi, lo;
+
+  u.ieee.negative = sign;
+  u.ieee.negative2 = sign;
+  u.ieee.exponent = exp + IBM_EXTENDED_LONG_DOUBLE_BIAS;
+  u.ieee.exponent2 = exp-53 + IBM_EXTENDED_LONG_DOUBLE_BIAS;
+  /* Expect 113 bits (112 bits + hidden) right justified in two longs.
+     The low order 53 bits (52 + hidden) go into the lower double */ 
+  lo = (lo64 >> 7)& ((1ULL << 53) - 1);
+  hidden2 = (lo64 >> 59) &  1ULL;
+  /* The high order 53 bits (52 + hidden) go into the upper double */
+  hi = (lo64 >> 60) & ((1ULL << 11) - 1);
+  hi |= (hi64 << 4);
+
+  if (lo != 0LL)
+    {
+      /* hidden2 bit of low double controls rounding of the high double.
+	 If hidden2 is '1' then round up hi and adjust lo (2nd mantissa)
+	 plus change the sign of the low double to compensate.  */
+      if (hidden2)
+	{
+	  hi++;
+	  u.ieee.negative2 = !sign;
+	  lo = (1ULL << 53) - lo;
+	}
+      /* The hidden bit of the lo mantissa is zero so we need to
+	 normalize the it for the low double.  Shift it left until the
+	 hidden bit is '1' then adjust the 2nd exponent accordingly.  */ 
+
+      if (sizeof (lo) == sizeof (long))
+	lzcount = __builtin_clzl (lo);
+      else if ((lo >> 32) != 0)
+	lzcount = __builtin_clzl ((long) (lo >> 32));
+      else
+	lzcount = __builtin_clzl ((long) lo) + 32;
+      lzcount = lzcount - 11;
+      if (lzcount > 0)
+	{
+	  int expnt2 = u.ieee.exponent2 - lzcount;
+	  if (expnt2 >= 1)
+	    {
+	      /* Not denormal.  Normalize and set low exponent.  */
+	      lo = lo << lzcount;
+	      u.ieee.exponent2 = expnt2;
+	    }
+	  else
+	    {
+	      /* Is denormal.  */
+	      lo = lo << (lzcount + expnt2);
+	      u.ieee.exponent2 = 0;
+	    }
+	}
+    }
+  else
+    {
+      u.ieee.negative2 = 0;
+      u.ieee.exponent2 = 0;
+    }
+
+  u.ieee.mantissa3 = lo & ((1ULL << 32) - 1);
+  u.ieee.mantissa2 = (lo >> 32) & ((1ULL << 20) - 1);
+  u.ieee.mantissa1 = hi & ((1ULL << 32) - 1);
+  u.ieee.mantissa0 = (hi >> 32) & ((1ULL << 20) - 1);
+  return u.d;
+}
+  
+/* gcc generates disgusting code to pack and unpack long doubles.
+   This tells gcc that pack/unpack is really a nop.  We use fr1/fr2
+   because those are the regs used to pass/return a single
+   long double arg.  */
+static inline long double
+ldbl_pack (double a, double aa)
+{
+  register long double x __asm__ ("fr1");
+  register double xh __asm__ ("fr1");
+  register double xl __asm__ ("fr2");
+  xh = a;
+  xl = aa;
+  __asm__ ("" : "=f" (x) : "f" (xh), "f" (xl));
+  return x;
+}
+
+static inline void
+ldbl_unpack (long double l, double *a, double *aa)
+{
+  register long double x __asm__ ("fr1");
+  register double xh __asm__ ("fr1");
+  register double xl __asm__ ("fr2");
+  x = l;
+  __asm__ ("" : "=f" (xh), "=f" (xl) : "f" (x));
+  *a = xh;
+  *aa = xl;
+}
+
+
+/* Convert a finite long double to canonical form.
+   Does not handle +/-Inf properly.  */
+static inline void
+ldbl_canonicalize (double *a, double *aa)
+{
+  double xh, xl;
+
+  xh = *a + *aa;
+  xl = (*a - xh) + *aa;
+  *a = xh;
+  *aa = xl;
+}
+
+/* Simple inline nearbyint (double) function .
+   Only works in the default rounding mode
+   but is useful in long double rounding functions.  */
+static inline double
+ldbl_nearbyint (double a)
+{
+  double two52 = 0x10000000000000LL;
+
+  if (__builtin_expect ((__builtin_fabs (a) < two52), 1))
+    {
+      if (__builtin_expect ((a > 0.0), 1))
+	{
+	  a += two52;
+	  a -= two52;
+	}
+      else if (__builtin_expect ((a < 0.0), 1))
+	{
+	  a = two52 - a;
+	  a = -(a - two52);
+	}
+    }
+  return a;
+}
diff --git a/sysdeps/powerpc/powerpc64/fpu/s_rintl.S b/sysdeps/powerpc/powerpc64/fpu/s_rintl.S
deleted file mode 100644
index 2ca2d4481f..0000000000
--- a/sysdeps/powerpc/powerpc64/fpu/s_rintl.S
+++ /dev/null
@@ -1,113 +0,0 @@
-/* Round to int long double floating-point values.
-   IBM extended format long double version.
-   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-/* This has been coded in assembler because GCC makes such a mess of it
-   when it's coded in C.  */
-
-#include <sysdep.h>
-#include <math_ldbl_opt.h>
-
-	.section	".toc","aw"
-.LC0:	/* 2**52 */
-	.tc FD_43300000_0[TC],0x4330000000000000
-	.section	".text"
-
-ENTRY (__rintl)
-	lfd	fp13,.LC0@toc(2)
-	fabs	fp0,fp1
-	fsub	fp12,fp13,fp13	/* generate 0.0  */
-	fabs	fp9,fp2
-	fcmpu	cr7,fp0,fp13	/* if (fabs(x) > TWO52)  */
-	fcmpu	cr6,fp1,fp12	/* if (x > 0.0)  */
-	bnl-	cr7,.L2
-	fmr	fp2,fp12
-	bng-	cr6,.L1
-	fadd	fp1,fp1,fp13	/* x+= TWO52;  */
-	fsub	fp1,fp1,fp13	/* x-= TWO52;  */
-	fabs	fp1,fp1		/* if (x == 0.0)  */
-	blr			/* x = 0.0; */
-.L1:
-	bnllr-	cr6		/* if (x < 0.0)  */
-	fsub	fp1,fp1,fp13	/* x-= TWO52;  */
-	fadd	fp1,fp1,fp13	/* x+= TWO52;  */
-	fnabs	fp1,fp1		/* if (x == 0.0)  */
-	blr			/* x = -0.0; */
-
-/* The high double is > TWO52 so we need to round the low double and
-   perhaps the high double.  In this case we have to round the low
-   double and handle any adjustment to the high double that may be
-   caused by rounding (up).  This is complicated by the fact that the
-   high double may already be rounded and the low double may have the
-   opposite sign to compensate.This gets a bit tricky so we use the
-   following algorithm:
-
-   tau = floor(x_high/TWO52);
-   x0 = x_high - tau;
-   x1 = x_low + tau;
-   r1 = rint(x1);
-   y_high = x0 + r1;
-   y_low = x0 - y_high + r1;
-   return y;  */
-.L2:
-	fcmpu	cr7,fp9,fp13	/* if (|x_low| > TWO52)  */
-	fcmpu	cr0,fp9,fp12	/* || (|x_low| == 0.0)  */
-	fcmpu	cr5,fp2,fp12	/* if (x_low > 0.0)  */
-	bgelr-	cr7		/*   return x;	*/
-	beqlr-  cr0
-	fdiv	fp8,fp1,fp13	/* x_high/TWO52  */
-	
-	bng-	cr6,.L6		/* if (x > 0.0)  */
-	fctidz	fp0,fp8
-	fcfid	fp8,fp0		/* tau = floor(x_high/TWO52);  */
-	fadd	fp8,fp8,fp8	/* tau++; Make tau even  */
-	bng	cr5,.L4		/* if (x_low > 0.0)  */
-	fmr	fp3,fp1
-	fmr	fp4,fp2
-	b	.L5
-.L4:				/* if (x_low < 0.0)  */
-	fsub	fp3,fp1,fp8	/* x0 = x_high - tau;  */
-	fadd	fp4,fp2,fp8	/* x1 = x_low + tau;  */
-.L5:
-	fadd	fp5,fp4,fp13	/* r1 = x1 + TWO52;  */
-	fsub	fp5,fp5,fp13	/* r1 = r1 - TWO52;  */
-	b	.L9
-.L6:				/* if (x < 0.0)  */
-	fctidz	fp0,fp8
-	fcfid	fp8,fp0		/* tau = floor(x_high/TWO52);  */
-	fadd	fp8,fp8,fp8	/* tau++; Make tau even  */	
-	bnl	cr5,.L7		/* if (x_low < 0.0)  */
-	fmr	fp3,fp1
-	fmr	fp4,fp2
-	b	.L8
-.L7:				/* if (x_low > 0.0)  */
-	fsub	fp3,fp1,fp8	/* x0 = x_high - tau;  */
-	fadd	fp4,fp2,fp8	/* x1 = x_low + tau;  */
-.L8:
-	fsub	fp5,fp13,fp4	/* r1 = TWO52 - x1;  */
-	fsub	fp0,fp5,fp13	/* r1 = - (r1 - TWO52);  */
-	fneg	fp5,fp0
-.L9:
-	fadd	fp1,fp3,fp5	/* y_high = x0 + r1;  */
-	fsub	fp2,fp3,fp1	/* y_low = x0 - y_high + r1;  */
-	fadd	fp2,fp2,fp5
-	blr
-END (__rintl)
-
-long_double_symbol (libm, __rintl, rintl)