I am putting up this e-mail for the record. I asked myself if it was "okay for trunk?", and myself answered "If it's not, I quit!"
When merged into the cobolworx test environment, all of our tests pass. When merged into master, the results compile, and check-cobol, such as it is, succeeds. I just pushed it into master. >From a4e0d3376b02b2cae7880038e66f241a4942c488 Mon Sep 17 00:00:00 2001 From: Bob Dubner mailto:rdub...@symas.com Date: Tue, 25 Mar 2025 15:38:38 -0400 Subject: [PATCH] cobol: Changes to eliminate _Float128 from the front end [PR119241] These changes switch _Float128 types to REAL_VALUE_TYPE in the front end. Some __int128 variables and function return values are changed to FIXED_WIDE_INT(128) gcc/cobol PR cobol/119241 * cdf.y: (cdfval_base_t::operator()): Return const. * cdfval.h: (struct cdfval_base_t): Add const cdfval_base_t& operator(). (struct cdfval_t): Add cdfval_t constructor. Change cdf_value definitions. * gcobolspec.cc (lang_specific_driver): Formatting fix. * genapi.cc: Include fold-const.h and realmpfr.h. (initialize_variable_internal): Use real_to_decimal instead of strfromf128. (get_binary_value_from_float): Use wide_int_to_tree instead of build_int_cst_type. (psa_FldLiteralN): Use fold_convert instead of strfromf128, real_from_string and build_real. (parser_display_internal): Rewritten to work on REAL_VALUE_TYPE rather than _Float128. (mh_source_is_literalN): Use FIXED_WIDE_INT(128) rather than __int128, wide_int_to_tree rather than build_int_cst_type, fold_convert rather than build_string_literal. (real_powi10): New function. (binary_initial_from_float128): Change type of last argument from _Float128 to REAL_VALUE_TYPE, process it using real.cc and mpfr APIs. (digits_from_float128): Likewise. (initial_from_float128): Make static. Remove value argument, add local REAL_VALUE_TYPE value variable instead, process it using real.cc and native_encode_expr APIs. (parser_symbol_add): Adjust initial_from_float128 caller. * genapi.h (initial_from_float128): Remove declaration. * genutil.cc (get_power_of_ten): Change return type from __int128 to FIXED_WIDE_INT(128), ditto for retval type, change type of pos from __int128 to unsigned long long. (scale_by_power_of_ten_N): Use wide_int_to_tree instead of build_int_cst_type. Use FIXED_WIDE_INT(128) instead of __int128 as power_of_ten variable type. (copy_little_endian_into_place): Likewise. * genutil.h (get_power_of_ten): Change return type from __int128 to FIXED_WIDE_INT(128). * parse.y (%union): Change type of float128 from _Float128 to REAL_VALUE_TYPE. (string_of): Change argument type from _Float128 to const REAL_VALUE_TYPE &, use real_to_decimal rather than strfromf128. Add another overload with tree argument type. (field: cdf): Use real_zerop rather than comparison against 0.0. (occurs_clause, const_value): Use real_to_integer. (value78): Use build_real and real_to_integer. (data_descr1): Use real_to_integer. (count): Use real_to_integer, real_from_integer and real_identical instead of direct comparison. (value_clause): Use real_from_string3 instead of num_str2i. Use real_identical instead of direct comparison. Use build_real. (allocate): Use real_isneg and real_iszero instead of <= 0 comparison. (move_tgt): Use real_to_integer, real_value_truncate, real_from_integer and real_identical instead of comparison of casts. (cce_expr): Use real_arithmetic and real_convert or real_value_negate instead of direct arithmetics on _Float128. (cce_factor): Use real_from_string3 instead of numstr2i. (literal_refmod_valid): Use real_to_integer. * symbols.cc (symbol_table_t::registers_t::registers_t): Formatting fix. (ERROR_FIELD): Likewise. (extend_66_capacity): Likewise. (cbl_occurs_t::subscript_ok): Use real_to_integer, real_from_integer and real_identical. * symbols.h (cbl_field_data_t::etc_t::value): Change type from _Float128 to tree. (cbl_field_data_t::etc_t::etc_t): Adjust defaulted argument value. (cbl_field_data_t::cbl_field_data_t): Formatting fix. Use etc() rather than etc(0). (cbl_field_data_t::value_of): Change return type from _Float128 to tree. (cbl_field_data_t::operator=): Change return and argument type from _Float128 to tree. (cbl_field_data_t::valify): Use real_from_string, real_value_truncate and build_real. (cbl_field_t::same_as): Use build_zero_cst instead of _Float128(0.0). gcc/testsuite * cobol.dg/literal1.cob: New testcase. * cobol.dg/output1.cob: Likewise Co-authored-by: Richard Biener mailto:rgue...@suse.de Co-authored-by: Jakub Jelinek mailto:ja...@redhat.com Co-authored-by: James K. Lowden mailto:jklow...@cobolworx.com Co-authored-by: Robert Dubner mailto:rdub...@symas.com --- gcc/cobol/cdf.y | 2 +- gcc/cobol/cdfval.h | 16 +- gcc/cobol/gcobolspec.cc | 8 +- gcc/cobol/genapi.cc | 238 +++++++++++++++---------- gcc/cobol/genapi.h | 3 - gcc/cobol/genutil.cc | 26 +-- gcc/cobol/genutil.h | 2 +- gcc/cobol/parse.y | 260 ++++++++++++++-------------- gcc/cobol/symbols.cc | 25 +-- gcc/cobol/symbols.h | 76 ++++---- gcc/testsuite/cobol.dg/data1.cob | 14 ++ gcc/testsuite/cobol.dg/literal1.cob | 14 ++ gcc/testsuite/cobol.dg/output1.cob | 14 ++ 13 files changed, 395 insertions(+), 303 deletions(-) create mode 100644 gcc/testsuite/cobol.dg/data1.cob create mode 100644 gcc/testsuite/cobol.dg/literal1.cob create mode 100644 gcc/testsuite/cobol.dg/output1.cob diff --git a/gcc/cobol/cdf.y b/gcc/cobol/cdf.y index c44ee5ee0723..6392f89d3b13 100644 --- a/gcc/cobol/cdf.y +++ b/gcc/cobol/cdf.y @@ -954,7 +954,7 @@ verify_integer( const YDFLTYPE& loc, const cdfval_base_t& val ) { return true; } -cdfval_base_t& +const cdfval_base_t& cdfval_base_t::operator()( const YDFLTYPE& loc ) { static cdfval_t zero(0); return verify_integer(loc, *this) ? *this : zero; diff --git a/gcc/cobol/cdfval.h b/gcc/cobol/cdfval.h index 4682db8074be..634b5a24c1ae 100644 --- a/gcc/cobol/cdfval.h +++ b/gcc/cobol/cdfval.h @@ -43,7 +43,7 @@ struct cdfval_base_t { bool off; const char *string; int64_t number; - cdfval_base_t& operator()( const YDFLTYPE& loc ); + const cdfval_base_t& operator()( const YDFLTYPE& loc ); }; struct cdf_arg_t { @@ -93,6 +93,14 @@ struct cdfval_t : public cdfval_base_t { cdfval_base_t::string = NULL; cdfval_base_t::number = value; } + explicit cdfval_t( const REAL_VALUE_TYPE& r ) + : lineno(yylineno), filename(cobol_filename()) + { + cdfval_base_t::off = false; + cdfval_base_t::string = NULL; + HOST_WIDE_INT value = real_to_integer(&r); + cdfval_base_t::number = value; + } cdfval_t( const cdfval_base_t& value ) : lineno(yylineno), filename(cobol_filename()) { @@ -104,10 +112,10 @@ struct cdfval_t : public cdfval_base_t { int64_t as_number() const { assert(is_numeric()); return number; } }; -bool -cdf_value( const char name[], cdfval_t value ); - const cdfval_t * cdf_value( const char name[] ); +bool +cdf_value( const char name[], cdfval_t value ); + #endif diff --git a/gcc/cobol/gcobolspec.cc b/gcc/cobol/gcobolspec.cc index c84f4058c59d..63f48aa25287 100644 --- a/gcc/cobol/gcobolspec.cc +++ b/gcc/cobol/gcobolspec.cc @@ -385,8 +385,8 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, case OPT_print_multi_os_directory: case OPT_print_multiarch: case OPT_print_sysroot_headers_suffix: - no_files_error = false; - break; + no_files_error = false; + break; case OPT_v: no_files_error = false; @@ -500,9 +500,9 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options, { const char *ach; if (entry_point) - ach = entry_point; + ach = entry_point; else - ach = decoded_options[i].arg; + ach = decoded_options[i].arg; append_option(OPT_main_, ach, 1); prior_main = false; entry_point = NULL; diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc index 8f4f9b213705..8a58423264e4 100644 --- a/gcc/cobol/genapi.cc +++ b/gcc/cobol/genapi.cc @@ -52,6 +52,8 @@ #include "../../libgcobol/charmaps.h" #include "../../libgcobol/valconv.h" #include "show_parse.h" +#include "fold-const.h" +#include "realmpfr.h" extern int yylineno; @@ -1041,7 +1043,9 @@ initialize_variable_internal( cbl_refer_t refer, default: { char ach[128]; - strfromf128(ach, sizeof(ach), "%.16E", parsed_var->data.value_of()); + real_to_decimal (ach, + TREE_REAL_CST_PTR (parsed_var->data.value_of()), + sizeof(ach), 16, 0); SHOW_PARSE_TEXT(ach); break; } @@ -1296,8 +1300,8 @@ get_binary_value_from_float(tree value, gg_assign(fvalue, gg_multiply(fvalue, gg_float(ftype, - build_int_cst_type(INT, - get_power_of_ten(rdigits))))); + wide_int_to_tree(INT, + get_power_of_ten(rdigits))))); // And we need to throw away any digits to the left of the leftmost digits: // At least, we need to do so in principl. I am deferring this problem until @@ -4025,11 +4029,7 @@ psa_FldLiteralN(struct cbl_field_t *field ) field->literal_decl_node = gg_define_variable(DOUBLE, id_string, vs_static); TREE_READONLY(field->literal_decl_node) = 1; TREE_CONSTANT(field->literal_decl_node) = 1; - char ach[128]; - strfromf128(ach, sizeof(ach), "%.36E", field->data.value_of()); - REAL_VALUE_TYPE real; - real_from_string(&real, ach); - tree initer = build_real (DOUBLE, real); + tree initer = fold_convert (DOUBLE, field->data.value_of()); DECL_INITIAL(field->literal_decl_node) = initer; } @@ -4884,8 +4884,9 @@ parser_display_internal(tree file_descriptor, // We make use of that here char ach[128]; - strfromf128(ach, sizeof(ach), "%.33E", refer.field->data.value_of()); - char *p = strchr(ach, 'E'); + real_to_decimal (ach, TREE_REAL_CST_PTR (refer.field->data.value_of()), + sizeof(ach), 33, 0); + char *p = strchr(ach, 'e'); if( !p ) { // Probably INF -INF NAN or -NAN, so ach has our result @@ -4898,12 +4899,27 @@ parser_display_internal(tree file_descriptor, { // We are going to stick with the E notation, so ach has our result } - else + else if (exp == 0) + { + p[-1] = '\0'; + } + else if (exp < 0) + { + p[-1] = '\0'; + char *q = strchr (ach, '.'); + char dig = q[-1]; + q[-1] = '\0'; + char tem[132]; + snprintf (tem, 132, "%s0.%0*u%c%s", ach, -exp - 1, 0, dig, q + 1); + strcpy (ach, tem); + } + else if (exp > 0) { - int precision = 32 - exp; - char achFormat[24]; - sprintf(achFormat, "%%.%df", precision); - strfromf128(ach, sizeof(ach), achFormat, refer.field->data.value_of()); + p[-1] = '\0'; + char *q = strchr (ach, '.'); + for (int i = 0; i != exp; ++i) + q[i] = q[i + 1]; + q[exp] = '.'; } __gg__remove_trailing_zeroes(ach); } @@ -13864,9 +13880,9 @@ mh_source_is_literalN(cbl_refer_t &destref, Analyzer.Message("Check to see if result fits"); if( destref.field->data.digits ) { - __int128 power_of_ten = get_power_of_ten(destref.field->data.digits); - IF( gg_abs(source), ge_op, build_int_cst_type(calc_type, - power_of_ten) ) + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten(destref.field->data.digits); + IF( gg_abs(source), ge_op, wide_int_to_tree(calc_type, + power_of_ten) ) { gg_assign(size_error, gg_bitwise_or(size_error, integer_one_node)); } @@ -13964,26 +13980,20 @@ mh_source_is_literalN(cbl_refer_t &destref, // The following generated code is the exact equivalent // of the C code: // *(float *)dest = (float)data.value - _Float32 src = (_Float32)sourceref.field->data.value_of(); - tree tsrc = build_string_literal(sizeof(src), (char *)&src); - gg_assign(gg_indirect(gg_cast(build_pointer_type(INT), tdest)), - gg_indirect(gg_cast(build_pointer_type(INT), tsrc ))); + gg_assign(gg_indirect(gg_cast(build_pointer_type(FLOAT), tdest)), + fold_convert (FLOAT, sourceref.field->data.value_of())); break; } case 8: { - _Float64 src = (_Float64)sourceref.field->data.value_of(); - tree tsrc = build_string_literal(sizeof(src), (char *)&src); - gg_assign(gg_indirect(gg_cast(build_pointer_type(LONG), tdest)), - gg_indirect(gg_cast(build_pointer_type(LONG), tsrc ))); + gg_assign(gg_indirect(gg_cast(build_pointer_type(DOUBLE), tdest)), + fold_convert (DOUBLE, sourceref.field->data.value_of())); break; } case 16: { - _Float128 src = (_Float128)sourceref.field->data.value_of(); - tree tsrc = build_string_literal(sizeof(src), (char *)&src); - gg_assign(gg_indirect(gg_cast(build_pointer_type(INT128), tdest)), - gg_indirect(gg_cast(build_pointer_type(INT128), tsrc ))); + gg_assign(gg_indirect(gg_cast(build_pointer_type(FLOAT128), tdest)), + sourceref.field->data.value_of()); break; } } @@ -15226,20 +15236,31 @@ parser_print_string(const char *fmt, const char *ach) gg_printf(fmt, gg_string_literal(ach), NULL_TREE); } +REAL_VALUE_TYPE +real_powi10 (uint32_t x) +{ + REAL_VALUE_TYPE ten, pow10; + real_from_integer (&ten, TYPE_MODE (FLOAT128), 10, SIGNED); + real_powi (&pow10, TYPE_MODE (FLOAT128), &ten, x); + return pow10; +} + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" char * -binary_initial_from_float128(cbl_field_t *field, int rdigits, _Float128 value) +binary_initial_from_float128(cbl_field_t *field, int rdigits, + REAL_VALUE_TYPE value) { // This routine returns an xmalloced buffer designed to replace the // data.initial member of the incoming field char *retval = NULL; - char ach[128] = ""; - // We need to adjust value so that it has no decimal places + // We need to adjust value so that it has no decimal places if( rdigits ) { - value *= get_power_of_ten(rdigits); + REAL_VALUE_TYPE pow10 = real_powi10 (rdigits); + real_arithmetic (&value, MULT_EXPR, &value, &pow10); + real_convert (&value, TYPE_MODE (float128_type_node), &value); } // We need to make sure that the resulting string will fit into // a number with 'digits' digits @@ -15247,52 +15268,47 @@ binary_initial_from_float128(cbl_field_t *field, int rdigits, _Float128 value) // Keep in mind that pure binary types, like BINARY-CHAR, have no digits if( field->data.digits ) { - value = fmodf128(value, (_Float128)get_power_of_ten(field->data.digits)); - } + REAL_VALUE_TYPE pow10 = real_powi10 (field->data.digits); + mpfr_t m0, m1; - // We convert it to a integer string of digits: - strfromf128(ach, sizeof(ach), "%.0f", value); - if( strcmp(ach, "-0") == 0 ) - { - // Yes, negative zero can be a thing. Let's make it go away. - strcpy(ach, "0"); + mpfr_inits2 (REAL_MODE_FORMAT (TYPE_MODE (float128_type_node))->p, + m0, m1, NULL); + mpfr_from_real (m0, &value, MPFR_RNDN); + mpfr_from_real (m1, &pow10, MPFR_RNDN); + mpfr_clear_flags (); + mpfr_fmod (m0, m0, m1, MPFR_RNDN); + real_from_mpfr (&value, m0, + REAL_MODE_FORMAT (TYPE_MODE (float128_type_node)), + MPFR_RNDN); + real_convert (&value, TYPE_MODE (float128_type_node), &value); + mpfr_clears (m0, m1, NULL); } + real_roundeven (&value, TYPE_MODE (float128_type_node), &value); + + bool fail = false; + FIXED_WIDE_INT(128) i + = FIXED_WIDE_INT(128)::from (real_to_integer (&value, &fail, 128), SIGNED); + + /* ??? Use native_encode_* below. */ retval = (char *)xmalloc(field->data.capacity); switch(field->data.capacity) { case 1: - *(signed char *)retval = atoi(ach); + *(signed char *)retval = (signed char)i.slow (); break; case 2: - *(signed short *)retval = atoi(ach); + *(signed short *)retval = (signed short)i.slow (); break; case 4: - *(signed int *)retval = atoi(ach); + *(signed int *)retval = (signed int)i.slow (); break; case 8: - *(signed long *)retval = atol(ach); + *(signed long *)retval = (signed long)i.slow (); break; case 16: - { - __int128 val = 0; - bool negative = false; - for(size_t i=0; i<strlen(ach); i++) - { - if( ach[i] == '-' ) - { - negative = true; - continue; - } - val *= 10; - val += ach[i] & 0x0F; - } - if( negative ) - { - val = -val; - } - *(__int128 *)retval = val; - } + *(unsigned long *)retval = (unsigned long)i.ulow (); + *((signed long *)retval + 1) = (signed long)i.shigh (); break; default: fprintf(stderr, @@ -15308,28 +15324,42 @@ binary_initial_from_float128(cbl_field_t *field, int rdigits, _Float128 value) } #pragma GCC diagnostic pop + static void -digits_from_float128(char *retval, cbl_field_t *field, size_t width, int rdigits, _Float128 value) +digits_from_float128(char *retval, cbl_field_t *field, size_t width, int rdigits, REAL_VALUE_TYPE value) { char ach[128]; // We need to adjust value so that it has no decimal places if( rdigits ) { - value *= get_power_of_ten(rdigits); + REAL_VALUE_TYPE pow10 = real_powi10 (rdigits); + real_arithmetic (&value, MULT_EXPR, &value, &pow10); } // We need to make sure that the resulting string will fit into // a number with 'digits' digits - - value = fmodf128(value, (_Float128)get_power_of_ten(field->data.digits)); + REAL_VALUE_TYPE pow10 = real_powi10 (field->data.digits); + mpfr_t m0, m1; + + mpfr_inits2 (FLOAT_MODE_FORMAT (TYPE_MODE (float128_type_node))->p, m0, m1, + NULL); + mpfr_from_real (m0, &value, MPFR_RNDN); + mpfr_from_real (m1, &pow10, MPFR_RNDN); + mpfr_clear_flags (); + mpfr_fmod (m0, m0, m1, MPFR_RNDN); + real_from_mpfr (&value, m0, + REAL_MODE_FORMAT (TYPE_MODE (float128_type_node)), + MPFR_RNDN); + real_convert (&value, TYPE_MODE (float128_type_node), &value); + mpfr_clears (m0, m1, NULL); + real_roundeven (&value, TYPE_MODE (float128_type_node), &value); + + bool fail = false; + FIXED_WIDE_INT(128) i + = FIXED_WIDE_INT(128)::from (real_to_integer (&value, &fail, 128), SIGNED); // We convert it to a integer string of digits: - strfromf128(ach, sizeof(ach), "%.0f", value); - if( strcmp(ach, "-0") == 0 ) - { - // Yes, negative zero can be a thing. Let's make it go away. - strcpy(ach, "0"); - } + print_dec (i, ach, SIGNED); //fprintf(stderr, "digits_from_float128() %s %f %s ", field->name, (double)value, ach); @@ -15341,8 +15371,8 @@ digits_from_float128(char *retval, cbl_field_t *field, size_t width, int rdigits strcpy(retval + (width-strlen(ach)), ach); } -char * -initial_from_float128(cbl_field_t *field, _Float128 value) +static char * +initial_from_float128(cbl_field_t *field) { Analyze(); // This routine returns an xmalloced buffer that is intended to replace the @@ -15410,10 +15440,16 @@ initial_from_float128(cbl_field_t *field, _Float128 value) { retval = (char *)xmalloc(field->data.capacity); memset(retval, const_char, field->data.capacity); - goto done; + return retval; } } + // ??? Refactoring the cases below that do not need 'value' would + // make this less ugly + REAL_VALUE_TYPE value; + if( field->data.etc_type == cbl_field_data_t::value_e ) + value = TREE_REAL_CST (field->data.value_of ()); + // There is always the infuriating possibility of a P-scaled number if( field->attr & scaled_e ) { @@ -15426,7 +15462,9 @@ initial_from_float128(cbl_field_t *field, _Float128 value) // Our result has no decimal places, and we have to multiply the value // by 10**9 to get the significant bdigits where they belong. - value *= get_power_of_ten(field->data.digits + field->data.rdigits); + REAL_VALUE_TYPE pow10 + = real_powi10 (field->data.digits + field->data.rdigits); + real_arithmetic (&value, MULT_EXPR, &value, &pow10); } else { @@ -15436,7 +15474,8 @@ initial_from_float128(cbl_field_t *field, _Float128 value) // If our caller gave us 123000000, we need to divide // it by 1000000 to line up the 123 with where we want it to go: - value /= get_power_of_ten(-field->data.rdigits); + REAL_VALUE_TYPE pow10 = real_powi10 (-field->data.rdigits); + real_arithmetic (&value, RDIV_EXPR, &value, &pow10); } // Either way, we now have everything aligned for the remainder of the // processing to work: @@ -15473,14 +15512,14 @@ initial_from_float128(cbl_field_t *field, _Float128 value) char ach[128]; bool negative; - if( value < 0 ) + if( real_isneg (&value) ) { - negative = true; - value = -value; + negative = true; + value = real_value_negate (&value); } else { - negative = false; + negative = false; } digits_from_float128(ach, field, field->data.digits, rdigits, value); @@ -15553,14 +15592,14 @@ initial_from_float128(cbl_field_t *field, _Float128 value) char ach[128]; bool negative; - if( value < 0 ) + if( real_isneg (&value) ) { - negative = true; - value = -value; + negative = true; + value = real_value_negate (&value); } else { - negative = false; + negative = false; } // For COMP-6 (flagged by separate_e), the number of required digits is @@ -15664,10 +15703,10 @@ initial_from_float128(cbl_field_t *field, _Float128 value) { // It's not a quoted string, so we use data.value: bool negative; - if( value < 0 ) + if( real_isneg (&value) ) { negative = true; - value = -value; + value = real_value_negate (&value); } else { @@ -15679,13 +15718,14 @@ initial_from_float128(cbl_field_t *field, _Float128 value) memset(retval, 0, field->data.capacity); size_t ndigits = field->data.capacity; - if( (field->attr & blank_zero_e) && value == 0 ) + if( (field->attr & blank_zero_e) && real_iszero (&value) ) { memset(retval, internal_space, field->data.capacity); } else { digits_from_float128(ach, field, ndigits, rdigits, value); + /* ??? This resides in libgcobol valconv.cc. */ __gg__string_to_numeric_edited( retval, ach, field->data.rdigits, @@ -15698,17 +15738,24 @@ initial_from_float128(cbl_field_t *field, _Float128 value) case FldFloat: { + tree tem; retval = (char *)xmalloc(field->data.capacity); switch( field->data.capacity ) { case 4: - *(_Float32 *)retval = (_Float32) value; + value = real_value_truncate (TYPE_MODE (FLOAT), value); + tem = build_real (FLOAT, value); + native_encode_expr (tem, (unsigned char *)retval, 4, 0); break; case 8: - *(_Float64 *)retval = (_Float64) value; + value = real_value_truncate (TYPE_MODE (DOUBLE), value); + tem = build_real (DOUBLE, value); + native_encode_expr (tem, (unsigned char *)retval, 8, 0); break; case 16: - *(_Float128 *)retval = (_Float128) value; + value = real_value_truncate (TYPE_MODE (FLOAT128), value); + tem = build_real (FLOAT128, value); + native_encode_expr (tem, (unsigned char *)retval, 16, 0); break; } break; @@ -15722,7 +15769,6 @@ initial_from_float128(cbl_field_t *field, _Float128 value) default: break; } - done: return retval; } @@ -16839,7 +16885,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) if( new_var->data.initial ) { - new_initial = initial_from_float128(new_var, new_var->data.value_of()); + new_initial = initial_from_float128(new_var); } if( new_initial ) { diff --git a/gcc/cobol/genapi.h b/gcc/cobol/genapi.h index 2c135e8da627..447b62e8357a 100644 --- a/gcc/cobol/genapi.h +++ b/gcc/cobol/genapi.h @@ -569,9 +569,6 @@ void parser_print_long(const char *fmt, long N); // fmt needs to have a %ls in i void parser_print_string(const char *ach); void parser_print_string(const char *fmt, const char *ach); // fmt needs to have a %s in it void parser_set_statement(const char *statement); - -char *initial_from_float128(cbl_field_t *field, _Float128 value); - void parser_set_handled(ec_type_t ec_handled); void parser_set_file_number(int file_number); void parser_exception_clear(); diff --git a/gcc/cobol/genutil.cc b/gcc/cobol/genutil.cc index f8bf7bc34b76..755c87153d70 100644 --- a/gcc/cobol/genutil.cc +++ b/gcc/cobol/genutil.cc @@ -1422,14 +1422,14 @@ get_data_address( cbl_field_t *field, // Ignore pedantic because we know 128-bit computation is not ISO C++14. #pragma GCC diagnostic ignored "-Wpedantic" -__int128 +FIXED_WIDE_INT(128) get_power_of_ten(int n) { // 2** 64 = 1.8E19 // 2**128 = 3.4E38 - __int128 retval = 1; + FIXED_WIDE_INT(128) retval = 1; static const int MAX_POWER = 19 ; - static const __int128 pos[MAX_POWER+1] = + static const unsigned long long pos[MAX_POWER+1] = { 1ULL, // 00 10ULL, // 01 @@ -1500,18 +1500,18 @@ scale_by_power_of_ten_N(tree value, gg_assign(var_decl_rdigits, integer_zero_node); } tree value_type = TREE_TYPE(value); - __int128 power_of_ten = get_power_of_ten(N); - gg_assign(value, gg_multiply(value, build_int_cst_type( value_type, + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten(N); + gg_assign(value, gg_multiply(value, wide_int_to_tree( value_type, power_of_ten))); } if( N < 0 ) { tree value_type = TREE_TYPE(value); - __int128 power_of_ten = get_power_of_ten(-N); + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten(-N); if( check_for_fractional ) { - IF( gg_mod(value, build_int_cst_type( value_type, - power_of_ten)), + IF( gg_mod(value, wide_int_to_tree( value_type, + power_of_ten)), ne_op, gg_cast(value_type, integer_zero_node) ) { @@ -1521,7 +1521,7 @@ scale_by_power_of_ten_N(tree value, gg_assign(var_decl_rdigits, integer_zero_node); ENDIF } - gg_assign(value, gg_divide(value, build_int_cst_type( value_type, + gg_assign(value, gg_divide(value, wide_int_to_tree( value_type, power_of_ten))); } } @@ -1864,12 +1864,12 @@ copy_little_endian_into_place(cbl_field_t *dest, } ENDIF - __int128 power_of_ten = get_power_of_ten( dest->data.digits - - dest->data.rdigits - + rhs_rdigits ); + FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( dest->data.digits + - dest->data.rdigits + + rhs_rdigits ); IF( gg_cast(INT128, abs_value), ge_op, - build_int_cst_type(INT128, power_of_ten) ) + wide_int_to_tree(INT128, power_of_ten) ) { // Flag the size error gg_assign(size_error, integer_one_node); diff --git a/gcc/cobol/genutil.h b/gcc/cobol/genutil.h index b2868f7c1f85..566ce776e7a7 100644 --- a/gcc/cobol/genutil.h +++ b/gcc/cobol/genutil.h @@ -106,7 +106,7 @@ tree get_data_address( cbl_field_t *field, #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" -__int128 get_power_of_ten(int n); +FIXED_WIDE_INT(128) get_power_of_ten(int n); #pragma GCC diagnostic pop void scale_by_power_of_ten_N(tree value, int N, diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y index c436469f570a..bad99528e599 100644 --- a/gcc/cobol/parse.y +++ b/gcc/cobol/parse.y @@ -206,7 +206,7 @@ static data_category_t data_category_of( const cbl_refer_t& refer ); - static _Float128 + static REAL_VALUE_TYPE numstr2i( const char input[], radix_t radix ); struct cbl_field_t; @@ -831,7 +831,7 @@ bool boolean; int number; char *string; - _Float128 float128; // Hope springs eternal: 28 Mar 2023 + REAL_VALUE_TYPE float128; literal_t literal; cbl_field_attr_t field_attr; ec_type_t ec_type; @@ -1333,21 +1333,19 @@ return strlen(lit.data) == lit.len? lit.data : NULL; } - static inline char * string_of( _Float128 cce ) { - static const char empty[] = "", format[] = "%.32E"; + static inline char * string_of( const REAL_VALUE_TYPE &cce ) { char output[64]; - int len = strfromf128 (output, sizeof(output), format, cce); - if( sizeof(output) < size_t(len) ) { - dbgmsg("string_of: value requires %d digits (of %zu)", - len, sizeof(output)); - return xstrdup(empty); - } + real_to_decimal( output, &cce, sizeof(output), 32, 0 ); char decimal = symbol_decimal_point(); std::replace(output, output + strlen(output), '.', decimal); return xstrdup(output); } + static inline char * string_of( tree cce ) { + return string_of (TREE_REAL_CST (cce)); + } + cbl_field_t * new_literal( const literal_t& lit, enum cbl_field_attr_t attr ); @@ -2910,22 +2908,26 @@ fd_clause: record_desc block_desc: BLOCK_kw contains rec_contains chars_recs ; rec_contains: NUMSTR[min] { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = $$.max = n; // fixed length } | NUMSTR[min] TO NUMSTR[max] { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + rn = numstr2i($max.string, $max.radix); + n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@max, "size %s cannot be negative", $max.string); YYERROR; } @@ -2984,26 +2986,32 @@ in_size: IN SIZE ; from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + rn = numstr2i($max.string, $max.radix); + n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $max.string); YYERROR; } $$.max = n; } | NUMSTR[min] TO NUMSTR[max] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } $$.min = n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + rn = numstr2i($max.string, $max.radix); + n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@max, "size %s cannot be negative", $max.string); YYERROR; } @@ -3011,8 +3019,9 @@ from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { } | TO NUMSTR[max] characters { - ssize_t n; - if( (n = numstr2i($max.string, $max.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($max.string, $max.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@max, "size %s cannot be negative", $max.string); YYERROR; } @@ -3021,8 +3030,9 @@ from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { } | FROM NUMSTR[min] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } @@ -3030,8 +3040,9 @@ from_to: FROM NUMSTR[min] TO NUMSTR[max] characters { $$.max = size_t(-1); } | NUMSTR[min] characters { - ssize_t n; - if( (n = numstr2i($min.string, $min.radix)) < 0 ) { + REAL_VALUE_TYPE rn = numstr2i($min.string, $min.radix); + ssize_t n = real_to_integer (&rn); + if( n < 0 ) { error_msg(@min, "size %s cannot be negative", $min.string); YYERROR; } @@ -3104,7 +3115,7 @@ field: cdf // Format data.initial per picture if( 0 == pristine_values.count(field.data.initial) ) { - if( field.data.digits > 0 && field.data.value_of() != 0.0 ) { + if( field.data.digits > 0 && !field.is_zero() ) { char *initial; int rdigits = field.data.rdigits < 0? 1 : field.data.rdigits + 1; @@ -3151,7 +3162,7 @@ occurs_clause: OCCURS cardinal_lb indexed } cbl_occurs_t *occurs = ¤t_field()->occurs; occurs->bounds.lower = - occurs->bounds.upper = $name->data.value_of(); + occurs->bounds.upper = $name->as_integer(); } ; cardinal_lb: cardinal times { @@ -3162,7 +3173,8 @@ cardinal_lb: cardinal times { cardinal: NUMSTR[input] { - $$ = numstr2i( $input.string, $input.radix ); + REAL_VALUE_TYPE rn = numstr2i($input.string, $input.radix); + $$ = real_to_integer (&rn); } ; @@ -3305,9 +3317,9 @@ data_descr: data_descr1 ; const_value: cce_expr - | BYTE_LENGTH of name { $$ = $name->data.capacity; } - | LENGTH of name { $$ = $name->data.capacity; } - | LENGTH_OF of name { $$ = $name->data.capacity; } + | BYTE_LENGTH of name { $name->data.set_real_from_capacity(&$$); } + | LENGTH of name { $name->data.set_real_from_capacity(&$$); } + | LENGTH_OF of name { $name->data.set_real_from_capacity(&$$); } ; value78: literalism @@ -3320,7 +3332,7 @@ value78: literalism | const_value { cbl_field_data_t data = {}; - data = $1; + data = build_real (float128_type_node, $1); $$ = new cbl_field_data_t(data); } | true_false @@ -3349,10 +3361,10 @@ data_descr1: level_name field.attr |= constant_e; if( $is_global ) field.attr |= global_e; field.type = FldLiteralN; - field.data = $const_value; + field.data = build_real (float128_type_node, $const_value); field.data.initial = string_of($const_value); - if( !cdf_value(field.name, static_cast<int64_t>($const_value)) ) { + if( !cdf_value(field.name, cdfval_t($const_value)) ) { error_msg(@1, "%s was defined by CDF", field.name); } } @@ -3411,8 +3423,7 @@ data_descr1: level_name } else { field.type = FldLiteralN; field.data.initial = string_of(field.data.value_of()); - if( !cdf_value(field.name, - static_cast<int64_t>(field.data.value_of())) ) { + if( !cdf_value(field.name, field.as_integer()) ) { yywarn("%s was defined by CDF", field.name); } } @@ -4109,7 +4120,8 @@ nines: NINES count: %empty { $$ = 0; } | '(' NUMSTR ')' { - $$ = numstr2i( $NUMSTR.string, $NUMSTR.radix ); + REAL_VALUE_TYPE rn = numstr2i($NUMSTR.string, $NUMSTR.radix); + $$ = real_to_integer (&rn); if( $$ == 0 ) { error_msg(@2, "'(0)' invalid in PICTURE (ISO 2023 13.18.40.3)"); } @@ -4126,7 +4138,10 @@ count: %empty { $$ = 0; } if( e ) { // verify not floating point with nonzero fraction auto field = cbl_field_of(e); assert(is_literal(field)); - if( field->data.value_of() != size_t(field->data.value_of()) ) { + REAL_VALUE_TYPE vi; + real_from_integer (&vi, VOIDmode, field->as_integer(), SIGNED); + if( !real_identical (TREE_REAL_CST_PTR (field->data.value_of()), + &vi) ) { nmsg++; error_msg(@NAME, "invalid PICTURE count '(%s)'", field->data.initial ); @@ -4315,10 +4330,12 @@ value_clause: VALUE all LITERAL[lit] { | VALUE all cce_expr[value] { cbl_field_t *field = current_field(); auto orig_str = original_number(); - auto orig_val = numstr2i(orig_str, decimal_e); + REAL_VALUE_TYPE orig_val; + real_from_string3 (&orig_val, orig_str, + TYPE_MODE (float128_type_node)); char *initial = NULL; - if( orig_val == $value ) { + if( real_identical (&orig_val, &$value) ) { initial = orig_str; pristine_values.insert(initial); } else { @@ -4330,7 +4347,7 @@ value_clause: VALUE all LITERAL[lit] { std::replace(initial, initial + strlen(initial), '.', decimal); field->data.initial = initial; - field->data = $value; + field->data = build_real (float128_type_node, $value); if( $all ) field_value_all(field); } @@ -5241,7 +5258,8 @@ allocate: ALLOCATE expr[size] CHARACTERS initialized RETURNING scalar[retu { statement_begin(@1, ALLOCATE); if( $size->field->type == FldLiteralN ) { - if( $size->field->data.value_of() <= 0 ) { + auto size = TREE_REAL_CST_PTR ($size->field->data.value_of()); + if( real_isneg(size) || real_iszero(size) ) { error_msg(@size, "size must be greater than 0"); YYERROR; } @@ -6658,10 +6676,18 @@ move_tgt: scalar[tgt] { const auto& field(*$1); static char buf[32]; const char *value_str( name_of($literal) ); - if( is_numeric($1) && - float(field.data.value_of()) == int(field.data.value_of()) ) { - sprintf(buf, "%d", int(field.data.value_of())); - value_str = buf; + if( is_numeric($1) ) + { + REAL_VALUE_TYPE val = TREE_REAL_CST (field.data.value_of()); + int ival = (int)real_to_integer (&val); + val = real_value_truncate (TYPE_MODE (float_type_node), + val); + REAL_VALUE_TYPE rival; + real_from_integer (&rival, VOIDmode, ival, SIGNED); + if( real_identical (&val, &rival) ) { + sprintf(buf, "%d", ival); + value_str = buf; + } } auto litcon = field.name[0] == '_'? "literal" : "constant"; error_msg(@literal, "%s is a %s", value_str, litcon); @@ -6885,27 +6911,35 @@ num_value: scalar // might actually be a string /* ; */ cce_expr: cce_factor - | cce_expr '+' cce_expr { $$ = $1 + $3; } - | cce_expr '-' cce_expr { $$ = $1 - $3; } - | cce_expr '*' cce_expr { $$ = $1 * $3; } - | cce_expr '/' cce_expr { $$ = $1 / $3; } + | cce_expr '+' cce_expr { + real_arithmetic (&$$, PLUS_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } + | cce_expr '-' cce_expr { + real_arithmetic (&$$, MINUS_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } + | cce_expr '*' cce_expr { + real_arithmetic (&$$, MULT_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } + | cce_expr '/' cce_expr { + real_arithmetic (&$$, RDIV_EXPR, &$1, &$3); + real_convert (&$$, TYPE_MODE (float128_type_node), &$$); + } | '+' cce_expr %prec NEG { $$ = $2; } - | '-' cce_expr %prec NEG { $$ = -$2; } + | '-' cce_expr %prec NEG { $$ = real_value_negate (&$2); } | '(' cce_expr ')' { $$ = $2; } ; cce_factor: NUMSTR { - /* - * As of March 2023, glibc printf does not deal with - * __int128_t. The below assertion is not required. It - * serves only remind us we're far short of the precision - * required by ISO. - */ - static_assert( sizeof($$) == sizeof(_Float128), - "quadmath?" ); - static_assert( sizeof($$) == 16, - "long doubles?" ); - $$ = numstr2i($1.string, $1.radix); + /* real_from_string does not allow arbitrary radix. */ + // When DECIMAL IS COMMA, commas act as decimal points. + gcc_assert($1.radix == decimal_e); + auto p = $1.string, pend = p + strlen(p); + std::replace(p, pend, ',', '.'); + real_from_string3( &$$, $1.string, + TYPE_MODE (float128_type_node) ); } ; @@ -10295,17 +10329,10 @@ intrinsic: function_udf } } if( $1 == NUMVAL_F ) { - if( is_literal($r1->field) ) { - _Float128 output __attribute__ ((__unused__)); + if( is_literal($r1->field) && ! is_numeric($r1->field->type) ) { + // The parameter might be literal, but could be "hello". auto input = $r1->field->data.initial; - auto local = xstrdup(input), pend = local; - std::replace(local, local + strlen(local), ',', '.'); - std::remove_if(local, local + strlen(local), isspace); - output = strtof128(local, &pend); - // bad if strtof128 could not convert input - if( *pend != '\0' ) { - error_msg(@r1, "'%s' is not a numeric string", input); - } + error_msg(@r1, "'%s' is not a numeric literal", input); } } if( ! intrinsic_call_1($$, $1, $r1, @r1)) YYERROR; @@ -11459,17 +11486,6 @@ paragraph_reference( const char name[], size_t section ) return p; } -static struct cbl_refer_t * -use_vargs( struct vargs_t *v, struct cbl_refer_t *tgt) { - assert(v); - assert(tgt); - std::copy(v->args.begin(), v->args.end(), tgt); - v->args.clear(); - delete v; - - return tgt; -} - void current_t::repository_add_all() { assert( !programs.empty() ); @@ -12031,46 +12047,45 @@ valid_target( const cbl_refer_t& refer ) { return false; } -static _Float128 +static REAL_VALUE_TYPE numstr2i( const char input[], radix_t radix ) { - _Float128 output = 0.0; - size_t bit, integer = 0; - int erc=0, n=0; + REAL_VALUE_TYPE output; + size_t integer = 0; + int erc=0; switch( radix ) { case decimal_e: { // Use decimal point for comma, just in case. - auto local = xstrdup(input), pend = local; + auto local = xstrdup(input); if( !local ) { erc = -1; break; } std::replace(local, local + strlen(local), ',', '.'); - output = strtof128(local, &pend); - n = pend - local; + real_from_string3 (&output, local, TYPE_MODE (float128_type_node)); } break; case hexadecimal_e: - erc = sscanf(input, "%zx%n", &integer, &n); - output = integer; + erc = sscanf(input, "%zx", &integer); + real_from_integer (&output, VOIDmode, integer, UNSIGNED); break; case boolean_e: for( const char *p = input; *p != '\0'; p++ ) { if( ssize_t(8 * sizeof(integer) - 1) < p - input ) { yywarn("'%s' was accepted as %d", input, integer); - return integer; + break; } switch(*p) { - case '0': bit = 0; break; - case '1': bit = 1; break; + case '0': + case '1': + integer = (integer << (p - input)); + integer |= ((*p) == '0' ? 0 : 1); break; default: yywarn("'%s' was accepted as %d", input, integer); - return integer; + break; } - integer = (integer << (p - input)); - integer |= bit; } - return integer; - break; + real_from_integer (&output, VOIDmode, integer, UNSIGNED); + return output; } - if( erc == -1 || n < int(strlen(input)) ) { + if( erc == -1 ) { yywarn("'%s' was accepted as %lld", input, output); } return output; @@ -12779,28 +12794,6 @@ cbl_field_t::has_subordinate( const cbl_field_t *that ) const { return false; } -bool -cbl_field_t::value_set( _Float128 value ) { - data = value; - char *initial = string_of(data.value_of()); - if( !initial ) return false; - - // Trim trailing zeros. - char *p = initial + strlen(initial); - for( --p; initial <= p; --p ) { - if( *p != '0' ) break; - *p = '\0'; - } - - data.digits = (p - initial) + 1; - p = strchr(initial, '.'); - data.rdigits = p? initial + data.digits - p : 0; - - data.initial = initial; - data.capacity = type_capacity(type, data.digits); - return true; -} - const char * cbl_field_t::value_str() const { if( data.etc_type == cbl_field_data_t::value_e ) @@ -12861,7 +12854,7 @@ literal_refmod_valid( YYLTYPE loc, const cbl_refer_t& r ) { if( ! is_literal(refmod.from->field) ) { if( ! refmod.len ) return true; if( ! is_literal(refmod.len->field) ) return true; - auto edge = refmod.len->field->data.value_of(); + auto edge = refmod.len->field->as_integer(); if( 0 < edge ) { if( --edge < r.field->data.capacity ) return true; } @@ -12875,13 +12868,14 @@ literal_refmod_valid( YYLTYPE loc, const cbl_refer_t& r ) { return false; } - if( refmod.from->field->data.value_of() > 0 ) { - auto edge = refmod.from->field->data.value_of(); + auto edge = refmod.from->field->as_integer(); + if( edge > 0 ) { if( --edge < r.field->data.capacity ) { if( ! refmod.len ) return true; if( ! is_literal(refmod.len->field) ) return true; - if( refmod.len->field->data.value_of() > 0 ) { - edge += refmod.len->field->data.value_of(); + auto len = refmod.len->field->as_integer(); + if( len > 0 ) { + edge += len; if( --edge < r.field->data.capacity ) return true; } // len < 0 or not: 0 < from + len <= capacity @@ -12889,8 +12883,8 @@ literal_refmod_valid( YYLTYPE loc, const cbl_refer_t& r ) { error_msg(loc, "%s(%zu:%zu) out of bounds, " "size is %u", r.field->name, - size_t(refmod.from->field->data.value_of()), - size_t(refmod.len->field->data.value_of()), + size_t(refmod.from->field->as_integer()), + size_t(len), static_cast<unsigned int>(r.field->data.capacity) ); return false; } @@ -12898,7 +12892,7 @@ literal_refmod_valid( YYLTYPE loc, const cbl_refer_t& r ) { // not: 0 < from <= capacity error_msg(loc,"%s(%zu) out of bounds, size is %u", r.field->name, - size_t(refmod.from->field->data.value_of()), + size_t(refmod.from->field->as_integer()), static_cast<unsigned int>(r.field->data.capacity) ); return false; } diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc index b8d785f25319..a4fc82c4ffa7 100644 --- a/gcc/cobol/symbols.cc +++ b/gcc/cobol/symbols.cc @@ -93,7 +93,7 @@ static struct symbol_table_t { exception_condition, very_true, very_false; registers_t() { file_status = linage_counter = return_code = - exception_condition = very_true = very_false = 0; + exception_condition = very_true = very_false = 0; } } registers; @@ -249,10 +249,10 @@ cbl_ffi_arg_t( cbl_ffi_crv_t crv, if( refer && refer != refer->empty() ) delete refer; } -#define ERROR_FIELD(F, ...) \ - do{ \ - auto loc = symbol_field_location(field_index(F)); \ - error_msg(loc, __VA_ARGS__); \ +#define ERROR_FIELD(F, ...) \ + do{ \ + auto loc = symbol_field_location(field_index(F)); \ + error_msg(loc, __VA_ARGS__); \ } while(0) @@ -1646,7 +1646,7 @@ struct capacity_of { static void extend_66_capacity( cbl_field_t *alias ) { static_assert(sizeof(symbol_elem_t*) == sizeof(const char *), - "all pointers must be same size"); + "all pointers must be same size"); assert(alias->data.picture); assert(alias->type == FldGroup); symbol_elem_t *e = symbol_at(alias->parent); @@ -4510,15 +4510,20 @@ cbl_occurs_t::subscript_ok( const cbl_field_t *subscript ) const { // It must be a number. if( subscript->type != FldLiteralN ) return false; - auto sub = subscript->data.value_of(); + // This only gets us int64_t, which is more than adequate for a table subscript + auto sub = real_to_integer (TREE_REAL_CST_PTR (subscript->data.value_of())); + REAL_VALUE_TYPE csub; + real_from_integer (&csub, VOIDmode, sub, SIGNED); - if( sub < 1 || sub != size_t(sub) ) { + if( sub < 1 + || !real_identical (&csub, + TREE_REAL_CST_PTR (subscript->data.value_of())) ) { return false; // zero/fraction invalid } if( bounds.fixed_size() ) { - return sub <= bounds.upper; + return (size_t)sub <= bounds.upper; } - return bounds.lower <= sub && sub <= bounds.upper; + return bounds.lower <= (size_t)sub && (size_t)sub <= bounds.upper; } cbl_file_key_t:: diff --git a/gcc/cobol/symbols.h b/gcc/cobol/symbols.h index fb7b60d9eaaa..91115b714e62 100644 --- a/gcc/cobol/symbols.h +++ b/gcc/cobol/symbols.h @@ -48,21 +48,6 @@ #define PICTURE_MAX 64 -#if ! (__HAVE_FLOAT128 && __GLIBC_USE (IEC_60559_TYPES_EXT)) -static_assert( sizeof(output) == sizeof(long double), "long doubles?" ); - -static inline _Float128 -strtof128 (const char *__restrict __nptr, char **__restrict __endptr) { - return strtold(nptr, endptr); -} - -static inline int -strfromf128 (char *restrict string, size_t size, - const char *restrict format, _Float128 value) { - return strfroml(str, n, format, fp); -} -#endif - extern const char *numed_message; enum cbl_dialect_t { @@ -265,9 +250,9 @@ struct cbl_field_data_t { val88_t() : false_value(NULL), domain(NULL) {} } val88; struct cbl_upsi_mask_t *upsi_mask; - _Float128 value; + tree value; - explicit etc_t( double v = 0.0 ) : value(v) {} + explicit etc_t( tree v = build_zero_cst (float128_type_node)) : value(v) {} } etc; cbl_field_data_t( uint32_t memsize=0, uint32_t capacity=0 ) @@ -278,13 +263,13 @@ struct cbl_field_data_t { , initial(0) , picture(0) , etc_type(value_e) - , etc(0) + , etc() {} cbl_field_data_t( uint32_t memsize, uint32_t capacity, - uint32_t digits, uint32_t rdigits, - const char *initial, - const char *picture = NULL ) + uint32_t digits, uint32_t rdigits, + const char *initial, + const char *picture = NULL ) : memsize(memsize) , capacity(capacity) , digits(digits) @@ -292,7 +277,7 @@ struct cbl_field_data_t { , initial(initial) , picture(picture) , etc_type(value_e) - , etc(0) + , etc() {} cbl_field_data_t( const cbl_field_data_t& that ) { @@ -323,18 +308,21 @@ struct cbl_field_data_t { etc_type = upsi_e; return etc.upsi_mask = mask; } - _Float128 value_of() const { + tree value_of() const { if( etc_type != value_e ) { dbgmsg("%s:%d: type is %s", __func__, __LINE__, etc_type_str()); } -//// assert(etc_type == value_e); return etc.value; } - _Float128& operator=( _Float128 v) { + tree& operator=( tree v) { etc_type = value_e; return etc.value = v; } + void set_real_from_capacity( REAL_VALUE_TYPE *r ) const { + real_from_integer (r, VOIDmode, capacity, SIGNED); + } + time_now_f time_func; uint32_t upsi_mask_derive() const { @@ -356,14 +344,19 @@ struct cbl_field_data_t { std::replace(input.begin(), input.end(), ',', '.'); } - char *pend = NULL; + double d; + int n; + int erc = sscanf(input.c_str(), "%lf%n", &d, &n); - etc.value = strtof128(input.c_str(), &pend); - - if( pend != input.c_str() + len ) { + if( erc < 0 || size_t(n) != input.size() ) { dbgmsg("%s: error: could not interpret '%s' of '%s' as a number", - __func__, pend, initial); + __func__, initial + n, initial); } + + REAL_VALUE_TYPE r; + real_from_string (&r, input.c_str()); + r = real_value_truncate (TYPE_MODE (float128_type_node), r); + etc.value = build_real (float128_type_node, r); return *this; } cbl_field_data_t& valify( const char *input ) { @@ -385,14 +378,14 @@ struct cbl_field_data_t { switch(etc_type) { case value_e: - etc.value = that.etc.value; - break; + etc.value = that.etc.value; + break; case val88_e: - etc.val88 = that.etc.val88; - break; + etc.val88 = that.etc.val88; + break; case upsi_e: - etc.upsi_mask = that.etc.upsi_mask; - break; + etc.upsi_mask = that.etc.upsi_mask; + break; } return *this; } @@ -531,6 +524,10 @@ struct cbl_field_t { || type == FldLiteralN; } + bool is_zero() const { + return real_zerop(data.value_of()); + } + bool rename_level_ok() const { switch( level ) { case 0: @@ -556,7 +553,7 @@ struct cbl_field_t { if( ! (is_typedef || that.type == FldClass) ) { data.initial = NULL; - data = _Float128(0.0); + data = build_zero_cst (float128_type_node); } return *this; } @@ -570,6 +567,10 @@ struct cbl_field_t { return type == FldNumericBinary || type == FldNumericBin5; } + HOST_WIDE_INT as_integer() const { + return real_to_integer( TREE_REAL_CST_PTR (data.value_of()) ); + } + void embiggen( size_t eight=8 ) { assert(gcobol_feature_embiggen() && is_numeric(type) && size() == 4); @@ -595,7 +596,6 @@ struct cbl_field_t { bool has_subordinate( const cbl_field_t *that ) const; const char * internalize(); - bool value_set( _Float128 value ); const char *value_str() const; bool is_key_name() const { return has_attr(record_key_e); } diff --git a/gcc/testsuite/cobol.dg/data1.cob b/gcc/testsuite/cobol.dg/data1.cob new file mode 100644 index 000000000000..5830195e8ac4 --- /dev/null +++ b/gcc/testsuite/cobol.dg/data1.cob @@ -0,0 +1,14 @@ +*> { dg-do run } +*> { dg-output {1.2345678E\+07(\n|\r\n|\r)} } +*> { dg-output {1.2345678E\+07(\n|\r\n|\r)} } + IDENTIFICATION DIVISION. + PROGRAM-ID. data1. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 FLOATLONG FLOAT-LONG VALUE 12345678. + 01 FLOATEXT FLOAT-EXTENDED VALUE 12345678. + PROCEDURE DIVISION. + DISPLAY FLOATLONG + DISPLAY FLOATEXT + GOBACK. + END PROGRAM data1. diff --git a/gcc/testsuite/cobol.dg/literal1.cob b/gcc/testsuite/cobol.dg/literal1.cob new file mode 100644 index 000000000000..43369e00f9ce --- /dev/null +++ b/gcc/testsuite/cobol.dg/literal1.cob @@ -0,0 +1,14 @@ +*> { dg-do run } +*> Make sure we properly round to integer when computing the initial +*> binary representation of a literal +IDENTIFICATION DIVISION. +PROGRAM-ID. literal1. +DATA DIVISION. +WORKING-STORAGE SECTION. + 77 VAR8 PIC 999V9(8) COMP-5 . + 77 VAR555 PIC 999V99999999 COMP-5 VALUE 555.55555555. + PROCEDURE DIVISION. + MOVE 555.55555555 TO VAR8 + ADD 0.00000001 TO VAR555 GIVING VAR8 ROUNDED + IF VAR8 NOT EQUAL TO 555.55555556 STOP RUN ERROR 1. + END PROGRAM literal1. diff --git a/gcc/testsuite/cobol.dg/output1.cob b/gcc/testsuite/cobol.dg/output1.cob new file mode 100644 index 000000000000..9475bde1eff1 --- /dev/null +++ b/gcc/testsuite/cobol.dg/output1.cob @@ -0,0 +1,14 @@ +*> { dg-do run } +*> { dg-output {-0.00012(\n|\r\n|\r)} } +*> { dg-output {0.00012(\n|\r\n|\r)} } +*> { dg-output {1234.66(\n|\r\n|\r)} } +*> { dg-output {-99.8(\n|\r\n|\r)} } +IDENTIFICATION DIVISION. +PROGRAM-ID. output1. +ENVIRONMENT DIVISION. +PROCEDURE DIVISION. + DISPLAY -0.00012 + DISPLAY 0.00012 + DISPLAY 1234.66 + DISPLAY -99.8 + STOP RUN. -- 2.34.1