This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch, libgfortran] PR41711 Z format does not support writing KIND=10 reals


Hello,

I will commit the attached patch which has been reviewed and OK'ed by Steve Kargl off list.

Special thanks to Dominique for testing on PPC big endian system.

Our curent BOZ conversion routines rely on mapping reals into the GFC_UINTEGER_LARGEST integer and then doing the conversions. It turns out that some systems do not have an integer type large enough to map a real(10) or real(16). For example, i686 has integer(8) and real(10). x86_64 does have integer(16) so it can support direct conversion of real(10).

To resolve this, I have re-factored the code some and added three new functions that will handle the conversions of large kinds by using byte manipulations.

After this patch goes in, I will focus on the read side of the issue. Test cases will be added after I get the read working.

Regression tested on i686 and x86_64.

Regards,

Jerry


2009-10-29 Jerry DeLisle <jvdelisle@gcc.gnu.org>


	PR libgfortran/41711
	* libgfortran.h: Define larger sizes for BOZ conversion buffers.
	* io/write.c (extract_uint): Include case where size is 10 if integer
	is large enough. (write_int): Rename to write_boz. (write_boz): Factor
	out extract_uint and delete the conversion function.
	(btoa_big): New binary conversion function.
	(otoa_big): New octal conversion function.
	(ztoa_big): New hexidecimal conversion function.
	(write_b): Modify to use new function.
	(write_o): Likewise.
	(write_z): Likewise.
Index: libgfortran.h
===================================================================
--- libgfortran.h	(revision 153571)
+++ libgfortran.h	(working copy)
@@ -672,10 +672,18 @@ internal_proto(show_backtrace);
 
 /* error.c */
 
+#if defined(HAVE_GFC_REAL_16)
+#define GFC_LARGEST_BUF (sizeof (GFC_REAL_16))
+#elif defined(HAVE_GFC_REAL_10)
+#define GFC_LARGEST_BUF (sizeof (GFC_REAL_10))
+#else
+#define GFC_LARGEST_BUF (sizeof (GFC_INTEGER_LARGEST))
+#endif
+
 #define GFC_ITOA_BUF_SIZE (sizeof (GFC_INTEGER_LARGEST) * 3 + 2)
-#define GFC_XTOA_BUF_SIZE (sizeof (GFC_UINTEGER_LARGEST) * 2 + 1)
-#define GFC_OTOA_BUF_SIZE (sizeof (GFC_INTEGER_LARGEST) * 3 + 1)
-#define GFC_BTOA_BUF_SIZE (sizeof (GFC_INTEGER_LARGEST) * 8 + 1)
+#define GFC_XTOA_BUF_SIZE (GFC_LARGEST_BUF * 2 + 1)
+#define GFC_OTOA_BUF_SIZE (GFC_LARGEST_BUF * 3 + 1)
+#define GFC_BTOA_BUF_SIZE (GFC_LARGEST_BUF * 8 + 1)
 
 extern void sys_exit (int) __attribute__ ((noreturn));
 internal_proto(sys_exit);
Index: io/write.c
===================================================================
--- io/write.c	(revision 153571)
+++ io/write.c	(working copy)
@@ -446,9 +446,10 @@ extract_uint (const void *p, int len)
       }
       break;
 #ifdef HAVE_GFC_INTEGER_16
+    case 10:
     case 16:
       {
-	GFC_INTEGER_16 tmp;
+	GFC_INTEGER_16 tmp = 0;
 	memcpy ((void *) &tmp, p, len);
 	i = (GFC_UINTEGER_16) tmp;
       }
@@ -482,20 +483,14 @@ write_l (st_parameter_dt *dtp, const fnode *f, cha
 
 
 static void
-write_int (st_parameter_dt *dtp, const fnode *f, const char *source, int len,
-           const char *(*conv) (GFC_UINTEGER_LARGEST, char *, size_t))
+write_boz (st_parameter_dt *dtp, const fnode *f, const char *q, int n)
 {
-  GFC_UINTEGER_LARGEST n = 0;
   int w, m, digits, nzero, nblank;
   char *p;
-  const char *q;
-  char itoa_buf[GFC_BTOA_BUF_SIZE];
 
   w = f->u.integer.w;
   m = f->u.integer.m;
 
-  n = extract_uint (source, len);
-
   /* Special case:  */
 
   if (m == 0 && n == 0)
@@ -511,7 +506,6 @@ static void
       goto done;
     }
 
-  q = conv (n, itoa_buf, sizeof (itoa_buf));
   digits = strlen (q);
 
   /* Select a width if none was specified.  The idea here is to always
@@ -538,7 +532,6 @@ static void
       goto done;
     }
 
-
   if (!dtp->u.p.no_leading_blank)
     {
       memset (p, ' ', nblank);
@@ -706,7 +699,203 @@ btoa (GFC_UINTEGER_LARGEST n, char *buffer, size_t
   return p;
 }
 
+/* The following three functions, btoa_big, otoa_big, and ztoa_big, are needed
+   to convert large reals with kind sizes that exceed the largest integer type
+   available on certain platforms.  In these cases, byte by byte conversion is
+   performed. Endianess is taken into account.  */
 
+/* Conversion to binary.  */
+
+static const char *
+btoa_big (const char *s, char *buffer, int len, GFC_UINTEGER_LARGEST *n)
+{
+  char *q;
+  int i, j;
+  
+  q = buffer;
+  if (big_endian)
+    {
+      const char *p = s;
+      for (i = 0; i < len; i++)
+	{
+	  char c = *p;
+
+	  /* Test for zero. Needed by write_boz later.  */
+	  if (*p != 0)
+	    *n = 1;
+
+	  for (j = 0; j < 8; j++)
+	    {
+	      *q++ = (c & 128) ? '1' : '0';
+	      c <<= 1;
+	    }
+	  p++;
+	}
+    }
+  else
+    {
+      const char *p = s + len - 1;
+      for (i = 0; i < len; i++)
+	{
+	  char c = *p;
+
+	  /* Test for zero. Needed by write_boz later.  */
+	  if (*p != 0)
+	    *n = 1;
+
+	  for (j = 0; j < 8; j++)
+	    {
+	      *q++ = (c & 128) ? '1' : '0';
+	      c <<= 1;
+	    }
+	  p--;
+	}
+    }
+
+  *q = '\0';
+
+  if (*n == 0)
+    return "0";
+
+  /* Move past any leading zeros.  */  
+  while (*buffer == '0')
+    buffer++;
+
+  return buffer;
+
+}
+
+/* Conversion to octal.  */
+
+static const char *
+otoa_big (const char *s, char *buffer, int len, GFC_UINTEGER_LARGEST *n)
+{
+  char *q;
+  int i, j, k;
+  uint8_t octet;
+
+  q = buffer + GFC_OTOA_BUF_SIZE - 1;
+  *q = '\0';
+  i = k = octet = 0;
+
+  if (big_endian)
+    {
+      const char *p = s + len - 1;
+      char c = *p;
+      while (i < len)
+	{
+	  /* Test for zero. Needed by write_boz later.  */
+	  if (*p != 0)
+	    *n = 1;
+
+	  for (j = 0; j < 3 && i < len; j++)
+	    {
+	      octet |= (c & 1) << j;
+	      c >>= 1;
+	      if (++k > 7)
+	        {
+		  i++;
+		  k = 0;
+		  c = *--p;
+		}
+	    }
+	  *--q = '0' + octet;
+	  octet = 0;
+	}
+    }
+  else
+    {
+      const char *p = s;
+      char c = *p;
+      while (i < len)
+	{
+	  /* Test for zero. Needed by write_boz later.  */
+	  if (*p != 0)
+	    *n = 1;
+
+	  for (j = 0; j < 3 && i < len; j++)
+	    {
+	      octet |= (c & 1) << j;
+	      c >>= 1;
+	      if (++k > 7)
+	        {
+		  i++;
+		  k = 0;
+		  c = *++p;
+		}
+	    }
+	  *--q = '0' + octet;
+	  octet = 0;
+	}
+    }
+
+  if (*n == 0)
+    return "0";
+
+  /* Move past any leading zeros.  */  
+  while (*q == '0')
+    q++;
+
+  return q;
+}
+
+/* Conversion to hexidecimal.  */
+
+static const char *
+ztoa_big (const char *s, char *buffer, int len, GFC_UINTEGER_LARGEST *n)
+{
+  static char a[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+  char *q;
+  uint8_t h, l;
+  int i;
+  
+  q = buffer;
+  
+  if (big_endian)
+    {
+      const char *p = s;
+      for (i = 0; i < len; i++)
+	{
+	  /* Test for zero. Needed by write_boz later.  */
+	  if (*p != 0)
+	    *n = 1;
+
+	  h = (*p >> 4) & 0x0F;
+	  l = *p++ & 0x0F;
+	  *q++ = a[h];
+	  *q++ = a[l];
+	}
+    }
+  else
+    {
+      const char *p = s + len - 1;
+      for (i = 0; i < len; i++)
+	{
+	  /* Test for zero. Needed by write_boz later.  */
+	  if (*p != 0)
+	    *n = 1;
+
+	  h = (*p >> 4) & 0x0F;
+	  l = *p-- & 0x0F;
+	  *q++ = a[h];
+	  *q++ = a[l];
+	}
+    }
+
+  *q = '\0';
+  
+  if (*n == 0)
+    return "0";
+    
+  /* Move past any leading zeros.  */  
+  while (*buffer == '0')
+    buffer++;
+
+  return buffer;
+}
+
 /* gfc_itoa()-- Integer to decimal conversion.
    The itoa function is a widespread non-standard extension to standard
    C, often declared in <stdlib.h>.  Even though the itoa defined here
@@ -757,22 +946,64 @@ write_i (st_parameter_dt *dtp, const fnode *f, con
 
 
 void
-write_b (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
+write_b (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
 {
-  write_int (dtp, f, p, len, btoa);
+  const char *p;
+  char itoa_buf[GFC_BTOA_BUF_SIZE];
+  GFC_UINTEGER_LARGEST n = 0;
+
+  if (len > (int) sizeof (GFC_UINTEGER_LARGEST))
+    {
+      p = btoa_big (source, itoa_buf, len, &n);
+      write_boz (dtp, f, p, n);
+    }
+  else
+    {
+      n = extract_uint (source, len);
+      p = btoa (n, itoa_buf, sizeof (itoa_buf));
+      write_boz (dtp, f, p, n);
+    }
 }
 
 
 void
-write_o (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
+write_o (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
 {
-  write_int (dtp, f, p, len, otoa);
+  const char *p;
+  char itoa_buf[GFC_OTOA_BUF_SIZE];
+  GFC_UINTEGER_LARGEST n = 0;
+  
+  if (len > (int) sizeof (GFC_UINTEGER_LARGEST))
+    {
+      p = otoa_big (source, itoa_buf, len, &n);
+      write_boz (dtp, f, p, n);
+    }
+  else
+    {
+      n = extract_uint (source, len);
+      p = otoa (n, itoa_buf, sizeof (itoa_buf));
+      write_boz (dtp, f, p, n);
+    }
 }
 
 void
-write_z (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
+write_z (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
 {
-  write_int (dtp, f, p, len, gfc_xtoa);
+  const char *p;
+  char itoa_buf[GFC_XTOA_BUF_SIZE];
+  GFC_UINTEGER_LARGEST n = 0;
+
+  if (len > (int) sizeof (GFC_UINTEGER_LARGEST))
+    {
+      p = ztoa_big (source, itoa_buf, len, &n);
+      write_boz (dtp, f, p, n);
+    }
+  else
+    {
+      n = extract_uint (source, len);
+      p = gfc_xtoa (n, itoa_buf, sizeof (itoa_buf));
+      write_boz (dtp, f, p, n);
+    }
 }
 
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]