Fix DFP when host endian != target endian

Joseph S. Myers joseph@codesourcery.com
Thu Jun 5 17:23:00 GMT 2008


This patch fixes problems with decimal floating point when the host
and target have different endianness.

Recall, as a basis for understanding the patch and the fixes, that
decimal floating-point values are stored in the real.h REAL_VALUE_TYPE
using the _Decimal128 format (as processed by libdecnumber built for
the host, which uses host endianness), and that the encode and decode
methods for floating point formats convert between REAL_VALUE_TYPE and
an array of host longs, where each host long stores 32 bits of the
target value in normal host endianness but the ordering of those longs
is target endianness.

The problems fixed are:

* dfp.c does not reverse the order of the 32-bit parts of the decimal
  floating-point representation when encoding and decoding for the
  target; when libdecnumber produces a representation (in any of the
  three formats), it produces one in host endianness, so the 32-bit
  parts need to have their order reversed if the target endianness
  differs.

* dpd/decimal128Local.h has three macros used in constant folding that
  use FLOAT_WORDS_BIG_ENDIAN.  However, these macros are used on
  host-endian _Decimal128 values stored in REAL_VALUE_TYPE, so they
  need to use the host's endianness, not the target's.

* When libdecnumber is built for the target as part of libgcc, the use
  of -I$(MULTIBUILDTOP)../../libdecnumber means that the host's
  config.h file and gstdint.h files are used, and so it's wrongly
  configured to use host endianness instead of target endianness (for
  the particular multilib being built).  (The problem with using the
  host's gstdint.h is a bit more theoretical; it would arise in
  particular if the host is a system with 64-bit long but without
  <stdint.h> or other headers defining the 64-bit types that
  GCC_HEADER_STDINT can find, so gstdint.h uses long and unsigned long
  for 64-bit types, but the target has 32-bit long.)

* The code for converting 128-bit values between DPD and BID (on the
  host) presumes that the 64-bit halves of the 128-bit value are in
  little-endian order, while the rest of libdecnumber uses host-endian
  DPD.  Thus the null conversions presently used in __host_to_ieee_128
  and __ieee_to_host_128 are unsuitable; the halves of the value need
  swapping on big-endian hosts.

This patch has been tested as follows:

* Tested with cross from i686-pc-linux-gnu to
  powerpc-none-linux-gnuspe, in conjunction with a patch I'll send
  separately to fix target-specific DFP problems for that target,
  where the patches together fix all the DFP test failures.

* Bootstrapped with no regressions on i686-pc-linux-gnu native, to
  verify that native BID is unaffected.

* The .s files for all the gcc.dg/dfp tests verified to be identical
  for cross-compilers to i586-pc-linux-gnu from i686-pc-linux-gnu and
  sparc-sun-solaris2.8, to verify that identical results are now
  generated for BID from both big and little endian hosts (they
  weren't before).

OK to commit?  What do people think about putting this in 4.3.2 as a
wrong-code fix?

gcc:
2008-06-05  Joseph Myers  <joseph@codesourcery.com>

	* dfp.c (WORDS_BIGENDIAN): Define to 0 if not defined.
	(encode_decimal64, decode_decimal64, encode_decimal128,
	decode_decimal128): Reverse order of 32-bit parts of value if host
	and target endianness differ.

libdecnumber:
2008-06-05  Joseph Myers  <joseph@codesourcery.com>

	* dconfig.h: New.
	* decContext.c, decExcept.c, decExcept.h, decLibrary.c,
	decNumber.c, decNumberLocal.h, decRound.c, dpd/decimal128.c,
	dpd/decimal32.c, dpd/decimal64.c: Include dconfig.h not config.h.
	* dpd/decimal128Local.h (decimal128SetSign, decimal128ClearSign,
	decimal128FlipSign): Use WORDS_BIGENDIAN not
	FLOAT_WORDS_BIG_ENDIAN.
	* bid/host-ieee128.c: Include dconfig.h.
	(__host_to_ieee_128, __ieee_to_host_128): Swap 64-bit halves of
	value if WORDS_BIGENDIAN.

libgcc:
2008-06-05  Joseph Myers  <joseph@codesourcery.com>

	* Makefile.in (DECNUMINC): Remove
	-I$(MULTIBUILDTOP)../../libdecnumber.
	* gstdint.h: New.

Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in	(revision 136352)
+++ libgcc/Makefile.in	(working copy)
@@ -191,8 +191,7 @@
 DECNUMINC = -I$(srcdir)/config/libbid -DENABLE_DECIMAL_BID_FORMAT
 else
 DECNUMINC = -I$(srcdir)/../libdecnumber/$(enable_decimal_float) \
-	    -I$(srcdir)/../libdecnumber \
-	    -I$(MULTIBUILDTOP)../../libdecnumber
+	    -I$(srcdir)/../libdecnumber
 endif
 else
 DECNUMINC =
Index: libgcc/gstdint.h
===================================================================
--- libgcc/gstdint.h	(revision 0)
+++ libgcc/gstdint.h	(revision 0)
@@ -0,0 +1,6 @@
+/* This header is only for use of libdecnumber built as part of
+   libgcc.  The targets supported for decimal floating point have
+   <stdint.h>; libdecnumber uses GCC_HEADER_STDINT only for the sake
+   of the host.  */
+
+#include <stdint.h>
Index: libdecnumber/decRound.c
===================================================================
--- libdecnumber/decRound.c	(revision 136352)
+++ libdecnumber/decRound.c	(working copy)
@@ -28,7 +28,7 @@
    Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.  */
 
-#include "config.h"
+#include "dconfig.h"
 #include "decContext.h"
 #include "decRound.h"
 
Index: libdecnumber/decNumber.c
===================================================================
--- libdecnumber/decNumber.c	(revision 136352)
+++ libdecnumber/decNumber.c	(working copy)
@@ -170,7 +170,7 @@
 #include <stdio.h>		   /* for printf [if needed] */
 #include <string.h>		   /* for strcpy */
 #include <ctype.h>		   /* for lower */
-#include "config.h"		   /* for GCC definitions */
+#include "dconfig.h"		   /* for GCC definitions */
 #include "decNumber.h"		   /* base number library */
 #include "decNumberLocal.h"	   /* decNumber local types, etc. */
 
Index: libdecnumber/decExcept.c
===================================================================
--- libdecnumber/decExcept.c	(revision 136352)
+++ libdecnumber/decExcept.c	(working copy)
@@ -28,7 +28,7 @@
    02110-1301, USA.  */
 
 #include <fenv.h>
-#include "config.h"
+#include "dconfig.h"
 #include "decContext.h"
 #include "decExcept.h"
 
Index: libdecnumber/decExcept.h
===================================================================
--- libdecnumber/decExcept.h	(revision 136352)
+++ libdecnumber/decExcept.h	(working copy)
@@ -28,7 +28,7 @@
    02110-1301, USA.  */
 
 #include <fenv.h>
-#include "config.h"
+#include "dconfig.h"
 #include "decContext.h"
 
 #define DFP_EXCEPTIONS_ENABLED 1
Index: libdecnumber/bid/host-ieee128.c
===================================================================
--- libdecnumber/bid/host-ieee128.c	(revision 136352)
+++ libdecnumber/bid/host-ieee128.c	(working copy)
@@ -29,6 +29,7 @@
 
 #include <string.h>
 
+#include "dconfig.h"
 #include "bid-dpd.h"
 #include "decimal128.h"
 
@@ -38,11 +39,21 @@
 void
 __host_to_ieee_128 (_Decimal128 in, decimal128 *out)
 {
+#if WORDS_BIGENDIAN
+  memcpy ((char *) out, (char *) &in + 8, 8);
+  memcpy ((char *) out + 8, (char *) &in, 8);
+#else
   memcpy ((char *) out, (char *) &in, 16);
+#endif
 }
 
 void
 __ieee_to_host_128 (decimal128 in, _Decimal128 *out)
 {
+#if WORDS_BIGENDIAN
+  memcpy ((char *) out, (char *) &in + 8, 8);
+  memcpy ((char *) out + 8, (char *) &in, 8);
+#else
   memcpy ((char *) out, (char *) &in, 16);
+#endif
 }
Index: libdecnumber/decLibrary.c
===================================================================
--- libdecnumber/decLibrary.c	(revision 136352)
+++ libdecnumber/decLibrary.c	(working copy)
@@ -27,7 +27,7 @@
    Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.  */
 
-#include "config.h"
+#include "dconfig.h"
 #include "decContext.h"
 #include "decimal128.h"
 #include "decimal64.h"
Index: libdecnumber/decNumberLocal.h
===================================================================
--- libdecnumber/decNumberLocal.h	(revision 136352)
+++ libdecnumber/decNumberLocal.h	(working copy)
@@ -44,7 +44,7 @@
 
   #include <stdlib.h>	      /* for abs			      */
   #include <string.h>	      /* for memset, strcpy		      */
-  #include "config.h"	      /* for WORDS_BIGENDIAN                  */
+  #include "dconfig.h"	      /* for WORDS_BIGENDIAN                  */
 
   /* Conditional code flag -- set this to match hardware platform     */
   /* 1=little-endian, 0=big-endian	                              */
Index: libdecnumber/decContext.c
===================================================================
--- libdecnumber/decContext.c	(revision 136352)
+++ libdecnumber/decContext.c	(working copy)
@@ -37,7 +37,7 @@
 
 #include <string.h>	      /* for strcmp */
 #include <stdio.h>	      /* for printf if DECCHECK */
-#include "config.h"	      /* for GCC definitions */
+#include "dconfig.h"	      /* for GCC definitions */
 #include "decContext.h"	      /* context and base types */
 #include "decNumberLocal.h"   /* decNumber local types, etc. */
 
Index: libdecnumber/dconfig.h
===================================================================
--- libdecnumber/dconfig.h	(revision 0)
+++ libdecnumber/dconfig.h	(revision 0)
@@ -0,0 +1,52 @@
+/* Configure decNumber for either host or target.
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2, or (at your option) any later
+   version.
+
+   In addition to the permissions in the GNU General Public License,
+   the Free Software Foundation gives you unlimited permission to link
+   the compiled version of this file into combinations with other
+   programs, and to distribute those combinations without any
+   restriction coming from the use of this file.  (The General Public
+   License restrictions do apply in other respects; for example, they
+   cover modification of the file, and distribution when not linked
+   into a combine executable.)
+
+   GCC 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 General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#ifdef IN_LIBGCC2
+
+#include "tconfig.h"
+#include "coretypes.h"
+#include "tm.h"
+
+#ifndef LIBGCC2_WORDS_BIG_ENDIAN
+#define LIBGCC2_WORDS_BIG_ENDIAN WORDS_BIG_ENDIAN
+#endif
+
+#ifndef LIBGCC2_FLOAT_WORDS_BIG_ENDIAN
+#define LIBGCC2_FLOAT_WORDS_BIG_ENDIAN LIBGCC2_WORDS_BIG_ENDIAN
+#endif
+
+#if LIBGCC2_FLOAT_WORDS_BIG_ENDIAN
+#define WORDS_BIGENDIAN 1
+#endif
+
+#else
+
+#include "config.h"
+
+#endif
Index: libdecnumber/dpd/decimal32.c
===================================================================
--- libdecnumber/dpd/decimal32.c	(revision 136352)
+++ libdecnumber/dpd/decimal32.c	(working copy)
@@ -42,7 +42,7 @@
 #include <string.h>	      /* [for memset/memcpy] */
 #include <stdio.h>	      /* [for printf] */
 
-#include "config.h"	      /* GCC definitions */
+#include "dconfig.h"	      /* GCC definitions */
 #define	 DECNUMDIGITS  7      /* make decNumbers with space for 7 */
 #include "decNumber.h"	      /* base number library */
 #include "decNumberLocal.h"   /* decNumber local types, etc. */
Index: libdecnumber/dpd/decimal64.c
===================================================================
--- libdecnumber/dpd/decimal64.c	(revision 136352)
+++ libdecnumber/dpd/decimal64.c	(working copy)
@@ -42,7 +42,7 @@
 #include <string.h>	      /* [for memset/memcpy] */
 #include <stdio.h>	      /* [for printf] */
 
-#include "config.h"	      /* GCC definitions */
+#include "dconfig.h"	      /* GCC definitions */
 #define	 DECNUMDIGITS 16      /* make decNumbers with space for 16 */
 #include "decNumber.h"	      /* base number library */
 #include "decNumberLocal.h"   /* decNumber local types, etc. */
Index: libdecnumber/dpd/decimal128.c
===================================================================
--- libdecnumber/dpd/decimal128.c	(revision 136352)
+++ libdecnumber/dpd/decimal128.c	(working copy)
@@ -42,7 +42,7 @@
 #include <string.h>	      /* [for memset/memcpy] */
 #include <stdio.h>	      /* [for printf] */
 
-#include "config.h"	      /* GCC definitions */
+#include "dconfig.h"	      /* GCC definitions */
 #define	 DECNUMDIGITS 34      /* make decNumbers with space for 34 */
 #include "decNumber.h"	      /* base number library */
 #include "decNumberLocal.h"   /* decNumber local types, etc. */
Index: libdecnumber/dpd/decimal128Local.h
===================================================================
--- libdecnumber/dpd/decimal128Local.h	(revision 136352)
+++ libdecnumber/dpd/decimal128Local.h	(working copy)
@@ -34,14 +34,14 @@
 
 /* Set sign; this assumes the sign was previously zero.  */
 #define decimal128SetSign(d,b) \
-  { (d)->bytes[FLOAT_WORDS_BIG_ENDIAN ? 0 : 15] |= ((unsigned) (b) << 7); }
+  { (d)->bytes[WORDS_BIGENDIAN ? 0 : 15] |= ((unsigned) (b) << 7); }
 
 /* Clear sign.  */
 #define decimal128ClearSign(d) \
-  { (d)->bytes[FLOAT_WORDS_BIG_ENDIAN ? 0 : 15] &= ~0x80; }
+  { (d)->bytes[WORDS_BIGENDIAN ? 0 : 15] &= ~0x80; }
 
 /* Flip sign.  */
 #define decimal128FlipSign(d) \
-  { (d)->bytes[FLOAT_WORDS_BIG_ENDIAN ? 0 : 15] ^= 0x80; }
+  { (d)->bytes[WORDS_BIGENDIAN ? 0 : 15] ^= 0x80; }
 
 #endif
Index: gcc/dfp.c
===================================================================
--- gcc/dfp.c	(revision 136352)
+++ gcc/dfp.c	(working copy)
@@ -36,6 +36,10 @@
 #include "decimal32.h"
 #include "decNumber.h"
 
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN 0
+#endif
+
 /* Initialize R (a real with the decimal flag set) from DN.  Can
    utilize status passed in via CONTEXT, if a previous operation had
    interesting status.  */
@@ -173,8 +177,16 @@
   decimal_to_decnumber (r, &dn);
   decimal64FromNumber (&d64, &dn, &set);
 
-  buf[0] = *(uint32_t *) &d64.bytes[0];
-  buf[1] = *(uint32_t *) &d64.bytes[4];
+  if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN)
+    {
+      buf[0] = *(uint32_t *) &d64.bytes[0];
+      buf[1] = *(uint32_t *) &d64.bytes[4];
+    }
+  else
+    {
+      buf[0] = *(uint32_t *) &d64.bytes[4];
+      buf[1] = *(uint32_t *) &d64.bytes[0];
+    }
 }
 
 /* Decode an IEEE 754R decimal64 type into a real.  */
@@ -190,8 +202,16 @@
   decContextDefault (&set, DEC_INIT_DECIMAL128);
   set.traps = 0;
 
-  *((uint32_t *) &d64.bytes[0]) = (uint32_t) buf[0];
-  *((uint32_t *) &d64.bytes[4]) = (uint32_t) buf[1];
+  if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN)
+    {
+      *((uint32_t *) &d64.bytes[0]) = (uint32_t) buf[0];
+      *((uint32_t *) &d64.bytes[4]) = (uint32_t) buf[1];
+    }
+  else
+    {
+      *((uint32_t *) &d64.bytes[0]) = (uint32_t) buf[1];
+      *((uint32_t *) &d64.bytes[4]) = (uint32_t) buf[0];
+    }
 
   decimal64ToNumber (&d64, &dn);
   decimal_from_decnumber (r, &dn, &set); 
@@ -213,10 +233,20 @@
   decimal_to_decnumber (r, &dn);
   decimal128FromNumber (&d128, &dn, &set);
 
-  buf[0] = *(uint32_t *) &d128.bytes[0];
-  buf[1] = *(uint32_t *) &d128.bytes[4];
-  buf[2] = *(uint32_t *) &d128.bytes[8];
-  buf[3] = *(uint32_t *) &d128.bytes[12];
+  if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN)
+    {
+      buf[0] = *(uint32_t *) &d128.bytes[0];
+      buf[1] = *(uint32_t *) &d128.bytes[4];
+      buf[2] = *(uint32_t *) &d128.bytes[8];
+      buf[3] = *(uint32_t *) &d128.bytes[12];
+    }
+  else
+    {
+      buf[0] = *(uint32_t *) &d128.bytes[12];
+      buf[1] = *(uint32_t *) &d128.bytes[8];
+      buf[2] = *(uint32_t *) &d128.bytes[4];
+      buf[3] = *(uint32_t *) &d128.bytes[0];
+    }
 }
 
 /* Decode an IEEE 754R decimal128 type into a real.  */
@@ -232,10 +262,20 @@
   decContextDefault (&set, DEC_INIT_DECIMAL128);
   set.traps = 0;
 
-  *((uint32_t *) &d128.bytes[0])  = (uint32_t) buf[0];
-  *((uint32_t *) &d128.bytes[4])  = (uint32_t) buf[1];
-  *((uint32_t *) &d128.bytes[8])  = (uint32_t) buf[2];
-  *((uint32_t *) &d128.bytes[12]) = (uint32_t) buf[3];
+  if (WORDS_BIGENDIAN == FLOAT_WORDS_BIG_ENDIAN)
+    {
+      *((uint32_t *) &d128.bytes[0])  = (uint32_t) buf[0];
+      *((uint32_t *) &d128.bytes[4])  = (uint32_t) buf[1];
+      *((uint32_t *) &d128.bytes[8])  = (uint32_t) buf[2];
+      *((uint32_t *) &d128.bytes[12]) = (uint32_t) buf[3];
+    }
+  else
+    {
+      *((uint32_t *) &d128.bytes[0])  = (uint32_t) buf[3];
+      *((uint32_t *) &d128.bytes[4])  = (uint32_t) buf[2];
+      *((uint32_t *) &d128.bytes[8])  = (uint32_t) buf[1];
+      *((uint32_t *) &d128.bytes[12]) = (uint32_t) buf[0];
+    }
 
   decimal128ToNumber (&d128, &dn);
   decimal_from_decnumber (r, &dn, &set); 

-- 
Joseph S. Myers
joseph@codesourcery.com



More information about the Gcc-patches mailing list