This is the mail archive of the
fortran@gcc.gnu.org
mailing list for the GNU Fortran project.
Re: [libgfortran] I/O of 128 bits integers
- From: FX Coudert <fxcoudert at gmail dot com>
- To: gfortran <fortran at gcc dot gnu dot org>
- Date: Thu, 26 May 2005 08:16:09 +0200
- Subject: Re: [libgfortran] I/O of 128 bits integers
This time, the patch is really attached. Sorry.
---------------------------------------------------------
Hi all,
Attached patch is an attempt at handling I/O of 128bits integers, and
any other types of integer in the future.
The libgfortran code relied on int64_t being the largest integer kind:
many parameters were passed as int64_t before being cast into their
final type. This is obviously wrong on some platforms, such as x86_64.
So, I propose we #define GFC_INTEGER_LARGEST to the largest integer kind
available, and use this instead of int64_t.
The question is: how do we know, when compiling the library, which is
the largest available integer kind for the target?
To do some testing, I defined GFC_INTEGER_LARGEST to be __int128_t on
x86_64 and int64_t everywhere else. With that, the patch bootstraps and
is regtested on i686-linux and x86_64-linux. On the latter, I can now do:
$ uname -mo
x86_64 GNU/Linux
$ cat a.f90
character(len=60) c
integer(kind=16) i, j
print *, bit_size(0_16)
print *, huge(0_16)
j = 2**(bit_size(0_16)-1) - 2**7
write (c,*) j
read (c,*) i
if (i /= j) call abort
write (c,'(I60)') j
read (c,'(I60)') i
if (i /= j) call abort
end
$ gfc a.f90 && ./a.out
128
170141183460469231731687303715884105727
$
I am waiting for comments on the general idea, the coding-style and
choice of name for GFC_INTEGER_LARGEST, as well as idea for a general
way to determine this type at compile-time. When everything is settled,
I will submit a proper patch (with ChangeLog entries and all).
Thanks,
FX
Index: libgfortran/libgfortran.h
===================================================================
RCS file: /cvsroot/gcc/gcc/libgfortran/libgfortran.h,v
retrieving revision 1.24
diff -p -u -r1.24 libgfortran.h
--- libgfortran/libgfortran.h 30 Apr 2005 20:51:29 -0000 1.24
+++ libgfortran/libgfortran.h 25 May 2005 21:10:52 -0000
@@ -212,6 +212,14 @@ typedef double GFC_REAL_8;
typedef complex float GFC_COMPLEX_4;
typedef complex double GFC_COMPLEX_8;
+#ifdef __x86_64__
+#define GFC_INTEGER_LARGEST __int128_t
+#define GFC_UINTEGER_LARGEST __uint128_t
+#else
+#define GFC_INTEGER_LARGEST int64_t
+#define GFC_UINTEGER_LARGEST uint64_t
+#endif
+
/* The following two definitions must be consistent with the types used
by the compiler. */
/* The type used of array indices, amongst other things. */
@@ -384,10 +392,10 @@ internal_proto(get_args);
/* error.c */
-extern char *gfc_itoa (int64_t);
+extern char *gfc_itoa (GFC_INTEGER_LARGEST);
internal_proto(gfc_itoa);
-extern char *xtoa (uint64_t);
+extern char *xtoa (GFC_UINTEGER_LARGEST);
internal_proto(xtoa);
extern void os_error (const char *) __attribute__ ((noreturn));
Index: libgfortran/io/io.h
===================================================================
RCS file: /cvsroot/gcc/gcc/libgfortran/io/io.h,v
retrieving revision 1.20
diff -p -u -r1.20 io.h
--- libgfortran/io/io.h 15 May 2005 12:49:40 -0000 1.20
+++ libgfortran/io/io.h 25 May 2005 21:10:52 -0000
@@ -562,10 +562,10 @@ internal_proto(next_record);
/* read.c */
-extern void set_integer (void *, int64_t, int);
+extern void set_integer (void *, GFC_INTEGER_LARGEST, int);
internal_proto(set_integer);
-extern uint64_t max_value (int, int);
+extern GFC_UINTEGER_LARGEST max_value (int, int);
internal_proto(max_value);
extern int convert_real (void *, const char *, int);
Index: libgfortran/io/list_read.c
===================================================================
RCS file: /cvsroot/gcc/gcc/libgfortran/io/list_read.c,v
retrieving revision 1.22
diff -p -u -r1.22 list_read.c
--- libgfortran/io/list_read.c 17 May 2005 16:54:51 -0000 1.22
+++ libgfortran/io/list_read.c 25 May 2005 21:10:52 -0000
@@ -344,7 +344,7 @@ convert_integer (int length, int negativ
{
char c, *buffer, message[100];
int m;
- int64_t v, max, max10;
+ GFC_INTEGER_LARGEST v, max, max10;
buffer = saved_string;
v = 0;
Index: libgfortran/io/read.c
===================================================================
RCS file: /cvsroot/gcc/gcc/libgfortran/io/read.c,v
retrieving revision 1.9
diff -p -u -r1.9 read.c
--- libgfortran/io/read.c 10 May 2005 08:34:58 -0000 1.9
+++ libgfortran/io/read.c 25 May 2005 21:10:52 -0000
@@ -43,10 +43,15 @@ Boston, MA 02111-1307, USA. */
* actually place the value into memory. */
void
-set_integer (void *dest, int64_t value, int length)
+set_integer (void *dest, GFC_INTEGER_LARGEST value, int length)
{
switch (length)
{
+#ifdef __x86_64__
+ case 16:
+ *((__int128_t *) dest) = value;
+ break;
+#endif
case 8:
*((int64_t *) dest) = value;
break;
@@ -68,13 +73,23 @@ set_integer (void *dest, int64_t value,
/* max_value()-- Given a length (kind), return the maximum signed or
* unsigned value */
-uint64_t
+GFC_UINTEGER_LARGEST
max_value (int length, int signed_flag)
{
- uint64_t value;
+ GFC_UINTEGER_LARGEST value;
+ int n;
switch (length)
{
+#ifdef __x86_64__
+ case 16:
+ value = 1;
+ for (n = 1; n < 4 * length; n++)
+ value = (value << 2) + 3;
+ if (! signed_flag)
+ value = 2*value+1;
+ break;
+#endif
case 8:
value = signed_flag ? 0x7fffffffffffffff : 0xffffffffffffffff;
break;
@@ -164,11 +179,11 @@ read_l (fnode * f, char *dest, int lengt
{
case 't':
case 'T':
- set_integer (dest, 1, length);
+ set_integer (dest, (GFC_INTEGER_LARGEST) 1, length);
break;
case 'f':
case 'F':
- set_integer (dest, 0, length);
+ set_integer (dest, (GFC_INTEGER_LARGEST) 0, length);
break;
default:
bad:
@@ -263,8 +278,9 @@ next_char (char **p, int *w)
void
read_decimal (fnode * f, char *dest, int length)
{
- unsigned value, maxv, maxv_10;
- int v, w, negative;
+ GFC_UINTEGER_LARGEST value, maxv, maxv_10;
+ GFC_INTEGER_LARGEST v;
+ int w, negative;
char c, *p;
w = f->u.w;
@@ -275,7 +291,7 @@ read_decimal (fnode * f, char *dest, int
p = eat_leading_spaces (&w, p);
if (w == 0)
{
- set_integer (dest, 0, length);
+ set_integer (dest, (GFC_INTEGER_LARGEST) 0, length);
return;
}
@@ -324,7 +340,7 @@ read_decimal (fnode * f, char *dest, int
value += c;
}
- v = (signed int) value;
+ v = value;
if (negative)
v = -v;
@@ -350,8 +366,9 @@ read_decimal (fnode * f, char *dest, int
void
read_radix (fnode * f, char *dest, int length, int radix)
{
- unsigned value, maxv, maxv_r;
- int v, w, negative;
+ GFC_UINTEGER_LARGEST value, maxv, maxv_r;
+ GFC_INTEGER_LARGEST v;
+ int w, negative;
char c, *p;
w = f->u.w;
@@ -362,7 +379,7 @@ read_radix (fnode * f, char *dest, int l
p = eat_leading_spaces (&w, p);
if (w == 0)
{
- set_integer (dest, 0, length);
+ set_integer (dest, (GFC_INTEGER_LARGEST) 0, length);
return;
}
@@ -460,7 +477,7 @@ read_radix (fnode * f, char *dest, int l
value += c;
}
- v = (signed int) value;
+ v = value;
if (negative)
v = -v;
Index: libgfortran/io/write.c
===================================================================
RCS file: /cvsroot/gcc/gcc/libgfortran/io/write.c,v
retrieving revision 1.38
diff -p -u -r1.38 write.c
--- libgfortran/io/write.c 22 May 2005 21:17:42 -0000 1.38
+++ libgfortran/io/write.c 25 May 2005 21:10:55 -0000
@@ -69,10 +69,10 @@ write_a (fnode * f, const char *source,
}
}
-static int64_t
+static GFC_INTEGER_LARGEST
extract_int (const void *p, int len)
{
- int64_t i = 0;
+ GFC_INTEGER_LARGEST i = 0;
if (p == NULL)
return i;
@@ -91,6 +91,11 @@ extract_int (const void *p, int len)
case 8:
i = *((const int64_t *) p);
break;
+#ifdef __x86_64__
+ case 16:
+ i = *((const __int128_t *) p);
+ break;
+#endif
default:
internal_error ("bad integer kind");
}
@@ -673,7 +678,7 @@ void
write_l (fnode * f, char *source, int len)
{
char *p;
- int64_t n;
+ GFC_INTEGER_LARGEST n;
p = write_block (f->u.w);
if (p == NULL)
@@ -756,10 +761,11 @@ write_float (fnode *f, const char *sourc
static void
-write_int (fnode *f, const char *source, int len, char *(*conv) (uint64_t))
+write_int (fnode *f, const char *source, int len,
+ char *(*conv) (GFC_UINTEGER_LARGEST))
{
uint32_t ns =0;
- uint64_t n = 0;
+ GFC_UINTEGER_LARGEST n = 0;
int w, m, digits, nzero, nblank;
char *p, *q;
@@ -842,9 +848,10 @@ write_int (fnode *f, const char *source,
}
static void
-write_decimal (fnode *f, const char *source, int len, char *(*conv) (int64_t))
+write_decimal (fnode *f, const char *source, int len,
+ char *(*conv) (GFC_INTEGER_LARGEST))
{
- int64_t n = 0;
+ GFC_INTEGER_LARGEST n = 0;
int w, m, digits, nsign, nzero, nblank;
char *p, *q;
sign_t sign;
@@ -930,7 +937,7 @@ write_decimal (fnode *f, const char *sou
/* Convert unsigned octal to ascii. */
static char *
-otoa (uint64_t n)
+otoa (GFC_UINTEGER_LARGEST n)
{
char *p;
@@ -958,7 +965,7 @@ otoa (uint64_t n)
/* Convert unsigned binary to ascii. */
static char *
-btoa (uint64_t n)
+btoa (GFC_UINTEGER_LARGEST n)
{
char *p;
Index: libgfortran/runtime/error.c
===================================================================
RCS file: /cvsroot/gcc/gcc/libgfortran/runtime/error.c,v
retrieving revision 1.9
diff -p -u -r1.9 error.c
--- libgfortran/runtime/error.c 12 Jan 2005 21:27:31 -0000 1.9
+++ libgfortran/runtime/error.c 25 May 2005 21:10:55 -0000
@@ -69,11 +69,11 @@ static char buffer[32]; /* buffer for i
/* Returns a pointer to a static buffer. */
char *
-gfc_itoa (int64_t n)
+gfc_itoa (GFC_INTEGER_LARGEST n)
{
int negative;
char *p;
- uint64_t t;
+ GFC_UINTEGER_LARGEST t;
if (n == 0)
{
@@ -109,7 +109,7 @@ gfc_itoa (int64_t n)
* static buffer. */
char *
-xtoa (uint64_t n)
+xtoa (GFC_UINTEGER_LARGEST n)
{
int digit;
char *p;