This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[patch, libgfortran] PR41711 Z format does not support writing KIND=10 reals
- From: Jerry DeLisle <jvdelisle at verizon dot net>
- To: gfortran <fortran at gcc dot gnu dot org>, gcc patches <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 29 Oct 2009 09:01:13 -0700
- Subject: [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);
+ }
}