https://gcc.gnu.org/g:b44e7c167c3d05d175f84423707241ba7847bb77
commit b44e7c167c3d05d175f84423707241ba7847bb77 Author: Thomas Koenig <tkoe...@gcc.gnu.org> Date: Sun Jul 28 14:29:42 2024 +0200 Implement decimal list-directed I/O. Diff: --- gcc/fortran/gfortran.h | 10 +- gcc/fortran/primary.cc | 3 +- gcc/fortran/simplify.cc | 4 +- gcc/fortran/trans-io.cc | 19 ++++ gcc/testsuite/gfortran.dg/unsigned_1.f90 | 7 +- gcc/testsuite/gfortran.dg/unsigned_2.f90 | 20 ++++ libgfortran/gfortran.map | 2 + libgfortran/io/io.h | 6 ++ libgfortran/io/list_read.c | 92 +++++++++++++++-- libgfortran/io/read.c | 78 ++++++++++++++ libgfortran/io/transfer.c | 20 ++++ libgfortran/io/write.c | 171 +++++++++++++++++++++++++++++++ libgfortran/libgfortran.h | 14 ++- libgfortran/mk-kinds-h.sh | 1 + 14 files changed, 426 insertions(+), 21 deletions(-) diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index d7bbcf6cdcde..1e3262f97bf2 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -677,7 +677,6 @@ enum gfc_isym_id GFC_ISYM_STOPPED_IMAGES, GFC_ISYM_STORAGE_SIZE, GFC_ISYM_STRIDE, - GFC_ISYM_SU_KIND, GFC_ISYM_SUM, GFC_ISYM_SYMLINK, GFC_ISYM_SYMLNK, @@ -706,7 +705,12 @@ enum gfc_isym_id GFC_ISYM_Y0, GFC_ISYM_Y1, GFC_ISYM_YN, - GFC_ISYM_YN2 + GFC_ISYM_YN2, + + /* Add this at the end, so maybe the module format + remains compatible. */ + GFC_ISYM_SU_KIND + }; enum init_local_logical @@ -4108,7 +4112,7 @@ void gfc_convert_mpz_to_signed (mpz_t, int); gfc_expr *gfc_simplify_ieee_functions (gfc_expr *); bool gfc_is_constant_array_expr (gfc_expr *); bool gfc_is_size_zero_array (gfc_expr *); -void gfc_convert_mpz_to_unsigned (mpz_t, int); +void gfc_convert_mpz_to_unsigned (mpz_t, int, bool check = true); /* trans-array.cc */ diff --git a/gcc/fortran/primary.cc b/gcc/fortran/primary.cc index c1aa0bc77c91..63b0bcf6784d 100644 --- a/gcc/fortran/primary.cc +++ b/gcc/fortran/primary.cc @@ -229,7 +229,8 @@ convert_unsigned (const char *buffer, int kind, int radix, locus *where) mpz_set_str (e->value.integer, t, radix); k = gfc_validate_kind (BT_UNSIGNED, kind, false); - gfc_convert_mpz_to_unsigned (e->value.integer, gfc_unsigned_kinds[k].bit_size); + gfc_convert_mpz_to_unsigned (e->value.integer, gfc_unsigned_kinds[k].bit_size, + false); return e; } diff --git a/gcc/fortran/simplify.cc b/gcc/fortran/simplify.cc index b96f5ee713e3..a8c9397edf97 100644 --- a/gcc/fortran/simplify.cc +++ b/gcc/fortran/simplify.cc @@ -148,7 +148,7 @@ get_kind (bt type, gfc_expr *k, const char *name, int default_kind) be accomplished by masking out the high bits. */ void -gfc_convert_mpz_to_unsigned (mpz_t x, int bitsize) +gfc_convert_mpz_to_unsigned (mpz_t x, int bitsize, bool check) { mpz_t mask; @@ -171,7 +171,7 @@ gfc_convert_mpz_to_unsigned (mpz_t x, int bitsize) { /* Confirm that no bits above the signed range are set if we are doing range checking. */ - if (flag_range_check != 0) + if (check && flag_range_check != 0) gcc_assert (mpz_scan1 (x, bitsize-1) == ULONG_MAX); } } diff --git a/gcc/fortran/trans-io.cc b/gcc/fortran/trans-io.cc index 7ab82fa2f5b1..e9e67a0d6b81 100644 --- a/gcc/fortran/trans-io.cc +++ b/gcc/fortran/trans-io.cc @@ -117,6 +117,8 @@ enum iocall IOCALL_WRITE_DONE, IOCALL_X_INTEGER, IOCALL_X_INTEGER_WRITE, + IOCALL_X_UNSIGNED, + IOCALL_X_UNSIGNED_WRITE, IOCALL_X_LOGICAL, IOCALL_X_LOGICAL_WRITE, IOCALL_X_CHARACTER, @@ -335,6 +337,14 @@ gfc_build_io_library_fndecls (void) get_identifier (PREFIX("transfer_integer_write")), ". w R . ", void_type_node, 3, dt_parm_type, pvoid_type_node, gfc_int4_type_node); + iocall[IOCALL_X_UNSIGNED] = gfc_build_library_function_decl_with_spec ( + get_identifier (PREFIX("transfer_unsigned")), ". w W . ", + void_type_node, 3, dt_parm_type, pvoid_type_node, gfc_int4_type_node); + + iocall[IOCALL_X_UNSIGNED_WRITE] = gfc_build_library_function_decl_with_spec ( + get_identifier (PREFIX("transfer_unsigned_write")), ". w R . ", + void_type_node, 3, dt_parm_type, pvoid_type_node, gfc_int4_type_node); + iocall[IOCALL_X_LOGICAL] = gfc_build_library_function_decl_with_spec ( get_identifier (PREFIX("transfer_logical")), ". w W . ", void_type_node, 3, dt_parm_type, pvoid_type_node, gfc_int4_type_node); @@ -2341,6 +2351,15 @@ transfer_expr (gfc_se * se, gfc_typespec * ts, tree addr_expr, break; + case BT_UNSIGNED: + arg2 = build_int_cst (unsigned_type_node, kind); + if (last_dt == READ) + function = iocall[IOCALL_X_UNSIGNED]; + else + function = iocall[IOCALL_X_UNSIGNED_WRITE]; + + break; + case BT_REAL: arg2 = build_int_cst (integer_type_node, kind); if (last_dt == READ) diff --git a/gcc/testsuite/gfortran.dg/unsigned_1.f90 b/gcc/testsuite/gfortran.dg/unsigned_1.f90 index a5f110aa0ab5..ed1a6eed937e 100644 --- a/gcc/testsuite/gfortran.dg/unsigned_1.f90 +++ b/gcc/testsuite/gfortran.dg/unsigned_1.f90 @@ -1,11 +1,16 @@ ! { dg-do run } ! { dg-options "-funsigned" } -! Test basic assignment, arithmetic and a condition. +! Test some arithmetic ans selected_unsigned_kind. program memain unsigned :: u, v + integer, parameter :: u1 = selected_unsigned_kind(2), & + u2 = selected_unsigned_kind(4), & + u4 = selected_unsigned_kind(6), & + u8 = selected_unsigned_kind(10) u = 1u v = 42u if (u + v /= 43u) then stop 1 end if + if (u1 /= 1 .or. u2 /= 2 .or. u4 /= 4 .or. u8 /= 8) stop 2 end program memain diff --git a/gcc/testsuite/gfortran.dg/unsigned_2.f90 b/gcc/testsuite/gfortran.dg/unsigned_2.f90 new file mode 100644 index 000000000000..e55e0f51a6df --- /dev/null +++ b/gcc/testsuite/gfortran.dg/unsigned_2.f90 @@ -0,0 +1,20 @@ +! { dg-do run } +! { dg-additional-options -funsigned } +! Test some list-directed I/O +program main + implicit none + unsigned :: uw, ur, vr + unsigned(kind=8) :: u8 + uw = 10u + open (10, status="scratch") + write (10,*) uw,-1 + rewind 10 + read (10,*) ur,vr + if (ur /= 10u .or. vr /= 4294967295u) stop 1 + rewind 10 + write (10,*) 17179869184u_8 + rewind 10 + read (10,*) u8 + if (u8 /= 17179869184u_8) stop 2 +end program main + diff --git a/libgfortran/gfortran.map b/libgfortran/gfortran.map index 82f8f3c5e9ca..e71cbcf23768 100644 --- a/libgfortran/gfortran.map +++ b/libgfortran/gfortran.map @@ -1775,4 +1775,6 @@ GFORTRAN_15 { global: _gfortran_internal_pack_class; _gfortran_internal_unpack_class; + _gfortran_transfer_unsigned; + _gfortran_transfer_unsigned_write; } GFORTRAN_14; diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h index 1c23676cc4c1..32e2b825ed5b 100644 --- a/libgfortran/io/io.h +++ b/libgfortran/io/io.h @@ -861,9 +861,15 @@ internal_proto (transfer_array_inner); extern void set_integer (void *, GFC_INTEGER_LARGEST, int); internal_proto(set_integer); +extern void set_unsigned (void *, GFC_UINTEGER_LARGEST, int); +internal_proto(set_unsigned); + extern GFC_UINTEGER_LARGEST si_max (int); internal_proto(si_max); +extern GFC_UINTEGER_LARGEST us_max (int); +internal_proto(us_max); + extern int convert_real (st_parameter_dt *, void *, const char *, int); internal_proto(convert_real); diff --git a/libgfortran/io/list_read.c b/libgfortran/io/list_read.c index 96b2efe854f5..9e3fffe56143 100644 --- a/libgfortran/io/list_read.c +++ b/libgfortran/io/list_read.c @@ -722,6 +722,65 @@ convert_integer (st_parameter_dt *dtp, int length, int negative) return 1; } +/* Same as above, but for unsigneds, where we do not need overflow checks, + except on the repeat count. */ + +static int +convert_unsigned (st_parameter_dt *dtp, int length, int negative) +{ + char c, *buffer, message[IOMSG_LEN]; + GFC_UINTEGER_LARGEST v, value; + GFC_UINTEGER_8 max; + int m; + + buffer = dtp->u.p.saved_string; + max = length == -1 ? 0 : MAX_REPEAT; + + for (;;) + { + c = *buffer++; + if (c == '\0') + break; + c -= '0'; + v += c; + if (length == -1 && v > max) + goto overflow; + } + + m = 0; + + if (length == -1) + { + if (negative) + value = -v; + else + value = v; + + value = value & us_max (length); + set_unsigned (dtp->u.p.value, value, length); + } + else + { + dtp->u.p.repeat_count = v; + + if (dtp->u.p.repeat_count == 0) + { + snprintf (message, IOMSG_LEN, "Zero repeat count in item %d of list input", + dtp->u.p.item_count); + + generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + m = 1; + } + } + free_saved (dtp); + return m; + + overflow: + snprintf (message, IOMSG_LEN, "Repeat count overflow in item %d of list input", + dtp->u.p.item_count); + + return 1; +} /* Parse a repeat count for logical and complex values which cannot begin with a digit. Returns nonzero if we are done, zero if we @@ -990,7 +1049,7 @@ read_logical (st_parameter_dt *dtp, int length) used for repeat counts. */ static void -read_integer (st_parameter_dt *dtp, int length) +read_integer (st_parameter_dt *dtp, int length, bt type) { char message[IOMSG_LEN]; int c, negative; @@ -1055,8 +1114,16 @@ read_integer (st_parameter_dt *dtp, int length) } repeat: - if (convert_integer (dtp, -1, 0)) - return; + if (type == BT_INTEGER) + { + if (convert_integer (dtp, -1, 0)) + return; + } + else + { + if (convert_unsigned (dtp, -1, 0)) + return; + } /* Get the real integer. */ @@ -1127,8 +1194,13 @@ read_integer (st_parameter_dt *dtp, int length) else if (c != '\n') eat_line (dtp); - snprintf (message, IOMSG_LEN, "Bad integer for item %d in list input", + if (type == BT_INTEGER) + snprintf (message, IOMSG_LEN, "Bad integer for item %d in list input", dtp->u.p.item_count); + else + snprintf (message, IOMSG_LEN, "Bad unsigned for item %d in list input", + dtp->u.p.item_count); + free_line (dtp); generate_error (&dtp->common, LIBERROR_READ_VALUE, message); @@ -1139,17 +1211,16 @@ read_integer (st_parameter_dt *dtp, int length) eat_separator (dtp); push_char (dtp, '\0'); - if (convert_integer (dtp, length, negative)) + if (convert_integer (dtp, length, negative)) /* XXX */ { free_saved (dtp); return; } free_saved (dtp); - dtp->u.p.saved_type = BT_INTEGER; + dtp->u.p.saved_type = type; } - /* Read a character variable. */ static void @@ -2224,7 +2295,8 @@ list_formatted_read_scalar (st_parameter_dt *dtp, bt type, void *p, switch (type) { case BT_INTEGER: - read_integer (dtp, kind); + case BT_UNSIGNED: + read_integer (dtp, kind, type); break; case BT_LOGICAL: read_logical (dtp, kind); @@ -2318,6 +2390,7 @@ list_formatted_read_scalar (st_parameter_dt *dtp, bt type, void *p, break; case BT_INTEGER: + case BT_UNSIGNED: case BT_LOGICAL: memcpy (p, dtp->u.p.value, size); break; @@ -3029,7 +3102,8 @@ nml_read_obj (st_parameter_dt *dtp, namelist_info *nl, index_type offset, switch (nl->type) { case BT_INTEGER: - read_integer (dtp, len); + case BT_UNSIGNED: + read_integer (dtp, len, nl->type); break; case BT_LOGICAL: diff --git a/libgfortran/io/read.c b/libgfortran/io/read.c index 7a9e341d7d80..2fb39392fc99 100644 --- a/libgfortran/io/read.c +++ b/libgfortran/io/read.c @@ -92,6 +92,62 @@ set_integer (void *dest, GFC_INTEGER_LARGEST value, int length) } } +/* set_integer()-- All of the integer assignments come here to + actually place the value into memory. */ + +void +set_unsigned (void *dest, GFC_UINTEGER_LARGEST value, int length) +{ + NOTE ("set_integer: %lld %p", (long long int) value, dest); + switch (length) + { +#ifdef HAVE_GFC_UINTEGER_16 +#ifdef HAVE_GFC_REAL_17 + case 17: + { + GFC_UINTEGER_16 tmp = value; + memcpy (dest, (void *) &tmp, 16); + } + break; +#endif +/* length=10 comes about for kind=10 real/complex BOZ, cf. PR41711. */ + case 10: + case 16: + { + GFC_UINTEGER_16 tmp = value; + memcpy (dest, (void *) &tmp, length); + } + break; +#endif + case 8: + { + GFC_UINTEGER_8 tmp = value; + memcpy (dest, (void *) &tmp, length); + } + break; + case 4: + { + GFC_UINTEGER_4 tmp = value; + memcpy (dest, (void *) &tmp, length); + } + break; + case 2: + { + GFC_UINTEGER_2 tmp = value; + memcpy (dest, (void *) &tmp, length); + } + break; + case 1: + { + GFC_UINTEGER_1 tmp = value; + memcpy (dest, (void *) &tmp, length); + } + break; + default: + internal_error (NULL, "Bad integer kind"); + } +} + /* Max signed value of size give by length argument. */ @@ -132,6 +188,28 @@ si_max (int length) } } +GFC_UINTEGER_LARGEST +us_max (int length) +{ + switch (length) + { +#ifdef HAVE_GFC_UINTEGER_16 + case 17: + case 16: + return GFC_UINTEGER_16_HUGE; +#endif + case 8: + return GFC_UINTEGER_8_HUGE; + case 4: + return GFC_UINTEGER_4_HUGE; + case 2: + return GFC_UINTEGER_2_HUGE; + case 1: + return GFC_UINTEGER_1_HUGE; + default: + internal_error (NULL, "Bad unsigned kind"); + } +} /* convert_real()-- Convert a character representation of a floating point number to the machine number. Returns nonzero if there is an diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c index a86099d46f56..741dbd9cc981 100644 --- a/libgfortran/io/transfer.c +++ b/libgfortran/io/transfer.c @@ -56,6 +56,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see transfer_complex transfer_real128 transfer_complex128 + transfer_unsigned and for WRITE @@ -67,6 +68,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see transfer_complex_write transfer_real128_write transfer_complex128_write + transfer_unsigned_write These subroutines do not return status. The *128 functions are in the file transfer128.c. @@ -82,6 +84,12 @@ export_proto(transfer_integer); extern void transfer_integer_write (st_parameter_dt *, void *, int); export_proto(transfer_integer_write); +extern void transfer_unsigned (st_parameter_dt *, void *, int); +export_proto(transfer_unsigned); + +extern void transfer_unsigned_write (st_parameter_dt *, void *, int); +export_proto(transfer_unsigned_write); + extern void transfer_real (st_parameter_dt *, void *, int); export_proto(transfer_real); @@ -2608,6 +2616,18 @@ transfer_integer_write (st_parameter_dt *dtp, void *p, int kind) transfer_integer (dtp, p, kind); } +void +transfer_unsigned (st_parameter_dt *dtp, void *p, int kind) +{ + wrap_scalar_transfer (dtp, BT_UNSIGNED, p, kind, kind, 1); +} + +void +transfer_unsigned_write (st_parameter_dt *dtp, void *p, int kind) +{ + transfer_unsigned (dtp, p, kind); +} + void transfer_real (st_parameter_dt *dtp, void *p, int kind) { diff --git a/libgfortran/io/write.c b/libgfortran/io/write.c index 91d1da2007ae..0f9600f5f1fe 100644 --- a/libgfortran/io/write.c +++ b/libgfortran/io/write.c @@ -949,7 +949,134 @@ write_decimal (st_parameter_dt *dtp, const fnode *f, const char *source, return; } +/* Same as above, but somewhat simpler because we only treat unsigned + numbers. */ +static void +write_decimal_unsigned (st_parameter_dt *dtp, const fnode *f, + const char *source, int len) +{ + GFC_UINTEGER_LARGEST n = 0; + int w, m, digits, nsign, nzero, nblank; + char *p; + const char *q; + sign_t sign; + char itoa_buf[GFC_BTOA_BUF_SIZE]; + + w = f->u.integer.w; + m = f->format == FMT_G ? -1 : f->u.integer.m; + + n = extract_uint (source, len); + + /* Special case: */ + if (m == 0 && n == 0) + { + if (w == 0) + w = 1; + + p = write_block (dtp, w); + if (p == NULL) + return; + + if (unlikely (is_char4_unit (dtp))) + { + gfc_char4_t *p4 = (gfc_char4_t *) p; + memset4 (p4, ' ', w); + } + else + memset (p, ' ', w); + goto done; + } + + /* Just in case somebody wants a + sign. */ + sign = calculate_sign (dtp, false); + nsign = sign == S_NONE ? 0 : 1; + + q = gfc_itoa (n, itoa_buf, sizeof (itoa_buf)); + digits = strlen (q); + + /* Select a width if none was specified. The idea here is to always + print something. */ + if (w == DEFAULT_WIDTH) + w = default_width_for_integer (len); + + if (w == 0) + w = ((digits < m) ? m : digits) + nsign; + + p = write_block (dtp, w); + if (p == NULL) + return; + + nzero = 0; + if (digits < m) + nzero = m - digits; + + /* See if things will work. */ + + nblank = w - (nsign + nzero + digits); + + if (unlikely (is_char4_unit (dtp))) + { + gfc_char4_t *p4 = (gfc_char4_t *)p; + if (nblank < 0) + { + memset4 (p4, '*', w); + goto done; + } + + if (!dtp->u.p.namelist_mode) + { + memset4 (p4, ' ', nblank); + p4 += nblank; + } + + if (sign == S_PLUS) + *p4++ = '+'; + + memset4 (p4, '0', nzero); + p4 += nzero; + + memcpy4 (p4, q, digits); + + if (dtp->u.p.namelist_mode) + { + p4 += digits; + memset4 (p4, ' ', nblank); + } + + return; + } + + if (nblank < 0) + { + star_fill (p, w); + goto done; + } + + if (!dtp->u.p.namelist_mode) + { + memset (p, ' ', nblank); + p += nblank; + } + + if (sign == S_PLUS) + *p++ = '+'; + + memset (p, '0', nzero); + p += nzero; + + memcpy (p, q, digits); + + if (dtp->u.p.namelist_mode) + { + p += digits; + memset (p, ' ', nblank); + } + + done: + return; + +} /* Convert hexadecimal to ASCII. */ static const char * @@ -1404,6 +1531,47 @@ write_integer (st_parameter_dt *dtp, const char *source, int kind) write_decimal (dtp, &f, source, kind); } +/* Write a list-directed unsigned value. We use the same formatting + as for integer. */ + +static void +write_unsigned (st_parameter_dt *dtp, const char *source, int kind) +{ + int width; + fnode f; + + switch (kind) + { + case 1: + width = 4; + break; + + case 2: + width = 6; + break; + + case 4: + width = 11; + break; + + case 8: + width = 20; + break; + + case 16: + width = 40; + break; + + default: + width = 0; + break; + } + f.u.integer.w = width; + f.u.integer.m = -1; + f.format = FMT_NONE; + write_decimal_unsigned (dtp, &f, source, kind); +} + /* Write a list-directed string. We have to worry about delimiting the strings if the file has been opened in that mode. */ @@ -1942,6 +2110,9 @@ list_formatted_write_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, case BT_INTEGER: write_integer (dtp, p, kind); break; + case BT_UNSIGNED: + write_unsigned (dtp, p, kind); + break; case BT_LOGICAL: write_logical (dtp, p, kind); break; diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h index effa3732c185..faf57a33358c 100644 --- a/libgfortran/libgfortran.h +++ b/libgfortran/libgfortran.h @@ -307,6 +307,15 @@ typedef GFC_UINTEGER_4 gfc_char4_t; (GFC_INTEGER_16)((((GFC_UINTEGER_16)1) << 127) - 1) #endif +#define GFC_UINTEGER_1_HUGE ((GFC_UINTEGER_1) -1) +#define GFC_UINTEGER_2_HUGE ((GFC_UINTEGER_2) -1) +#define GFC_UINTEGER_4_HUGE ((GFC_UINTEGER_4) -1) +#define GFC_UINTEGER_8_HUGE ((GFC_UINTEGER_8) -1) +#ifdef HAVE_GFC_UINTEGER_16 +#define GFC_UINTEGER_16_HUGE ((GFC_UINTEGER_16) -1) +#endif + + /* M{IN,AX}{LOC,VAL} need also infinities and NaNs if supported. */ #if __FLT_HAS_INFINITY__ @@ -2042,9 +2051,4 @@ extern int __snprintfieee128 (char *, size_t, const char *, ...) #endif -/* We always have these. */ - -#define HAVE_GFC_UINTEGER_1 1 -#define HAVE_GFC_UINTEGER_4 1 - #endif /* LIBGFOR_H */ diff --git a/libgfortran/mk-kinds-h.sh b/libgfortran/mk-kinds-h.sh index 0e0ec195875a..647b3b6eadb5 100755 --- a/libgfortran/mk-kinds-h.sh +++ b/libgfortran/mk-kinds-h.sh @@ -38,6 +38,7 @@ for k in $possible_integer_kinds; do echo "typedef GFC_INTEGER_${k} GFC_LOGICAL_${k};" echo "#define HAVE_GFC_LOGICAL_${k}" echo "#define HAVE_GFC_INTEGER_${k}" + echo "#define HAVE_GFC_UINTEGER_${k}" echo "" fi rm -f tmp$$.*