From: Robert Dubner <[email protected]>
Date: Wed, 24 Jun 2026 22:19:10 -0400
Subject: [PATCH] cobol: Improve MOVE BINARY/COMP-5 to PACKED-DECIMAL and
NUMERIC-DISPLAY
These are more efficient algorithms for converting binary values to
packed-decimal and zoned decimal byte strings. There is also an
improved routine that generates GENERIC to do COBOL rounding during
such moves.
gcc/cobol/ChangeLog:
* gengen.cc (gg_abs): Use fold_build1().
* genutil.cc (scale_and_round): Remove function.
(round_this_value): New function for rounding.
* genutil.h (scale_and_round): Remove function.
(round_this_value): New declaration.
* move.cc (cobol_wider_type_with_x_signedness): New function.
(mh_binary_to_numdisp): Faster routine.
(mh_binary_to_packed): Faster routine.
(move_helper): Use the faster routines.
libgcobol/ChangeLog:
* charmaps.cc (__gg__miconverter): Use table instead of a map.
(__gg__get_charmap): Likewise.
* charmaps.h (ebcdic_zero): New constant.
(ebcdic_plus): Likewise.
(ebcdic_minus): Likewise.
* encodings.h (enum cbl_encoding_t): Guardrail for table.
(ASCII_e): Remove trailing spaces.
* stringbin.cc (string_from_combined): Improved routine.
(defined): Likewise.
(FALLTHROUGH): Likewise.
(uint_to_8_digits): Improve speed.
(__gg__binary_to_string_ascii): Likewise.
(binary_to_string): Likewise.
(__gg__binary_to_string_ebcdic): Likewise.
(__gg__binary_to_string_encoded): Likewise.
(packed_from_combined): Likewise.
(__gg__binary_to_packed): Likewise.
(__gg__packed_to_binary): Likewise.
gcc/testsuite/ChangeLog:
* cobol.dg/group1/simple-classes.cob:
* cobol.dg/group1/simple-if.cob:
* cobol.dg/group2/Rounding_from_BINARY_signable_and_negative.cob:
New test.
* cobol.dg/group2/Rounding_from_BINARY_signable_and_negative.out:
New test.
* cobol.dg/group2/Rounding_from_BINARY_signable_and_positive.cob:
New test.
* cobol.dg/group2/Rounding_from_BINARY_signable_and_positive.out:
New test.
* cobol.dg/group2/Rounding_from_BINARY_unsignable.cob: New test.
* cobol.dg/group2/Rounding_from_BINARY_unsignable.out: New test.
---
gcc/cobol/gengen.cc | 2 +-
gcc/cobol/genutil.cc | 403 ++++++++++---
gcc/cobol/genutil.h | 9 +-
gcc/cobol/move.cc | 537 ++++++++++++++++++
...ding_from_BINARY_signable_and_negative.cob | 62 ++
...ding_from_BINARY_signable_and_negative.out | 9 +
...ding_from_BINARY_signable_and_positive.cob | 62 ++
...ding_from_BINARY_signable_and_positive.out | 9 +
.../Rounding_from_BINARY_unsignable.cob | 62 ++
.../Rounding_from_BINARY_unsignable.out | 9 +
libgcobol/charmaps.cc | 18 +-
libgcobol/charmaps.h | 3 +
libgcobol/encodings.h | 7 +-
libgcobol/stringbin.cc | 395 ++++++-------
14 files changed, 1273 insertions(+), 314 deletions(-)
create mode 100644
gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative.c
ob
create mode 100644
gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative.o
ut
create mode 100644
gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive.c
ob
create mode 100644
gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive.o
ut
create mode 100644
gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.cob
create mode 100644
gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.out
diff --git a/gcc/cobol/gengen.cc b/gcc/cobol/gengen.cc
index 3700de01590..03f79c56692 100644
--- a/gcc/cobol/gengen.cc
+++ b/gcc/cobol/gengen.cc
@@ -1594,7 +1594,7 @@ gg_bitwise_not(tree var)
tree
gg_abs(tree var)
{
- return build1(ABS_EXPR, TREE_TYPE(var), var);
+ return fold_build1(ABS_EXPR, TREE_TYPE(var), var);
}
static tree
diff --git a/gcc/cobol/genutil.cc b/gcc/cobol/genutil.cc
index 799a17e1bb7..142e0bb02e2 100644
--- a/gcc/cobol/genutil.cc
+++ b/gcc/cobol/genutil.cc
@@ -1760,71 +1760,6 @@ scale_by_power_of_ten(tree value,
return retval;
}
-void
-scale_and_round(tree value,
- int value_rdigits,
- bool target_is_signable,
- int target_rdigits,
- cbl_round_t rounded)
- {
- if( !target_is_signable )
- {
- // The target has to be positive, so take the absolute value of the
input
- gg_assign(value, gg_abs(value));
- }
-
- if( target_rdigits >= value_rdigits )
- {
- // The value doesn't have enough rdigits. All we need to do is
multiply it
- // by a power of ten to get it right:
- scale_by_power_of_ten_N(value,
- target_rdigits - value_rdigits);
- }
- else
- {
- // The value has too few rdigits.
- switch(rounded)
- {
- case nearest_away_from_zero_e:
- {
- // This is rounding away from zero
-
- // We want to adjust value so that the extra digit is in the
units
- // place:
- scale_by_power_of_ten_N(value,
- target_rdigits - value_rdigits + 1);
- // Add five to the result:
- IF( value, lt_op, gg_cast(TREE_TYPE(value), integer_zero_node) )
- {
- gg_assign(value,
- gg_add( value,
- build_int_cst_type(TREE_TYPE(value), -5)));
- }
- ELSE
- {
- gg_assign(value,
- gg_add( value,
- build_int_cst_type(TREE_TYPE(value), +5)));
- }
- // And now get rid of the lowest decimal digit
- scale_by_power_of_ten_N(value, -1);
-
- break;
- }
-
- case truncation_e:
- {
- // Without rounding, just scale the result
- scale_by_power_of_ten_N(value, target_rdigits - value_rdigits);
- break;
- }
- default:
- abort();
- break;
- }
- }
- }
-
void
hex_dump(tree data, size_t bytes)
{
@@ -3493,3 +3428,341 @@ attribute_bit_set(struct cbl_field_t *var,
cbl_field_attr_t bits)
build_int_cst_type(SIZE_T, bits)));
}
+tree
+round_this_value( tree &value,
+ tree pot,
+ cbl_round_t rounded,
+ tree size_error)
+ {
+ tree retval = gg_define_variable(INT);
+ // We are rounding value by dividing it by 'pot', which is a power of
ten.
+ // We will decide how to round it by looking at the remainder.
+
+ // Return zero when the returned value is zero. We use this to avoid
+ // negative zero flags in numeric-display and packed-decimal
reprentatios
+ // when there have been truncations.
+ tree type = TREE_TYPE(value);
+ if( rounded == truncation_e )
+ {
+ // This is the simplest and most common case.
+ gg_assign(value, gg_divide(value, pot));
+ IF( value, eq_op, build_int_cst_type(type, 0) )
+ {
+ gg_assign(retval, integer_zero_node);
+ }
+ ELSE
+ {
+ gg_assign(retval, integer_one_node);
+ }
+ ENDIF
+ return retval;
+ }
+ // With truncation out of the way, we actually have to do some work.
+ bool signable = !TYPE_UNSIGNED(type);
+
+ // Let's calculate rem = abs(value) % pot. So, if the POT is, say,
1000,
+ // the remainder will be between 000 and 999 inclusive.
+ tree rem = gg_define_variable(type);
+ gg_assign(rem, gg_mod(gg_abs(value), pot));
+ gg_assign(value, gg_divide(value, pot));
+
+ // We often need the halfway point, that is, 500 when POT is 1000
+ tree half = gg_define_variable(type);
+ gg_assign(half, gg_divide(pot, build_int_cst_type(type, 2)));
+
+ tree zero = build_int_cst_type(type, 0);
+
+ switch(rounded)
+ {
+ case away_from_zero_e:
+ {
+ /* "If the AWAY-FROM-ZERO phrase is specified and the arithmetic
value
+ cannot be exactly represented in the resultant identifier, the
+ arithmetic value is rounded to the nearest value farther from
zero
+ that can be represented in the resultant identifier." */
+ if( signable )
+ {
+ IF( value, ge_op, zero )
+ {
+ // The value is positive, so if there is a remainder, increment
it
+ IF( rem, gt_op, zero )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ ELSE
+ {
+ // The value is negative, so if there is a remainder, decrement
it
+ IF( rem, gt_op, zero )
+ {
+ gg_decrement(value);
+ }
+ ELSE {} ENDIF
+ }
+ ENDIF
+ }
+ else
+ {
+ // The value is positive, so if there is a remainder, increment
it
+ IF( rem, gt_op, zero )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ break;
+ }
+
+ case nearest_away_from_zero_e:
+ {
+ /* "If the NEAREST-AWAY-FROM-ZERO phrase is specified or implied
and the
+ arithmetic value cannot be exactly represented in the resultant
+ identifier, the arithmetic value is rounded to the nearest value
that
+ can be represented in the resultant identifier. If two such
values are
+ equally near, the value farther from zero is chosen."
+
+ This is rounding like you learned in grade school,
+ */
+ if( signable )
+ {
+ IF( value, ge_op, zero )
+ {
+ // The value is positive, so if remainder >= 5, increment it
+ IF( rem, ge_op, half )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ ELSE
+ {
+ // The value is negative, so if remainder < 5, decrement it
+ IF( rem, ge_op, half )
+ {
+ gg_decrement(value);
+ }
+ ELSE {} ENDIF
+ }
+ ENDIF
+ }
+ else
+ {
+ // The value is positive, so if remainder >= 5, increment it
+ IF( rem, ge_op, half )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ break;
+ }
+
+ case nearest_even_e:
+ {
+ /* "If the NEAREST-EVEN phrase is specified and the arithmetic
value
+ cannot be exactly represented in the resultant identifier, the
+ arithmetic value is rounded to the nearest value that can be
+ represented in the resultant identifier. If two such values are
+ equally near, the value whose rightmost digit is even is chosen.
+ NOTE: This method is sometimes known as 'banker's rounding'." */
+ if( signable )
+ {
+ IF( rem, eq_op, half )
+ {
+ // This is the money shot, exactly half-way.
+ IF( value, ge_op, zero )
+ {
+ gg_increment(value);
+ gg_assign(value,
+ gg_bitwise_and(value,
+
gg_bitwise_not(build_int_cst_type(type,
+
1))));
+ }
+ ELSE
+ {
+ gg_assign(value, gg_negate(value));
+ gg_increment(value);
+ gg_assign(value,
+ gg_bitwise_and(value,
+
gg_bitwise_not(build_int_cst_type(type,
+
1))));
+ gg_assign(value, gg_negate(value));
+ }
+ ENDIF
+ }
+ ELSE
+ {
+ // The signable value has a remainder that is not exactly 5
+ IF( value, ge_op, zero )
+ {
+ // The value is positive, so if remainder >= 5, increment it
+ IF( rem, ge_op, half )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ ELSE
+ {
+ // The value is negative, so if remainder < 5, decrement it
+ IF( rem, ge_op, half )
+ {
+ gg_decrement(value);
+ }
+ ELSE {} ENDIF
+ }
+ ENDIF
+ }
+ ENDIF
+ }
+ else
+ {
+ // The value is not signable, hence it is positive:
+ IF( rem, eq_op, half )
+ {
+ // This is the money shot, exactly half-way.
+ // Make value a multiple of 2
+ gg_increment(value);
+ gg_assign(value,
+ gg_bitwise_and(value,
+
gg_bitwise_not(build_int_cst_type(type,
+
1))));
+ }
+ ELSE
+ {
+ // The value is positive, so if remainder > 5, increment it
+ IF( rem, gt_op, half )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ ENDIF
+ }
+ break;
+ }
+
+ case nearest_toward_zero_e:
+ {
+ /* "If the NEAREST-TOWARD-ZERO phrase is specified and the
arithmetic
+ value cannot be exactly represented in the resultant identifier,
the
+ arithmetic value is rounded to the nearest value that can be
+ represented in the resultant identifier. If two such values are
+ equally near, the value nearest to zero is chosen." */
+ if( signable )
+ {
+ IF( value, ge_op, zero )
+ {
+ // The value is positive, so if remainder > 5, increment it
+ IF( rem, gt_op, half )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ ELSE
+ {
+ // The value is negative, so if remainder < 5, decrement it
+ IF( rem, gt_op, half )
+ {
+ gg_decrement(value);
+ }
+ ELSE {} ENDIF
+ }
+ ENDIF
+ }
+ else
+ {
+ // The value is positive, so if remainder > 5, increment it
+ IF( rem, gt_op, half )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ break;
+ }
+ case prohibited_e:
+ {
+ /* "If the PROHIBITED phrase is specified, and the arithmetic value
+ cannot be represented exactly in the resultant identifier, the
+ EC-SIZE-TRUNCATION exception condition is set to exist, the size
error
+ condition exists, and the content of the resultant identifier is
+ unchanged." */
+ const cbl_enabled_exceptions_t&
+ enabled_exceptions(
cdf_enabled_exceptions() );
+ IF( rem, ne_op, zero )
+ {
+ if( size_error )
+ {
+ gg_assign(size_error, integer_one_node);
+ }
+ else if( enabled_exceptions.match(ec_size_truncation_e) )
+ {
+ set_exception_code(ec_size_truncation_e);
+ }
+ }
+ ELSE
+ {
+ }
+ ENDIF
+ break;
+ }
+
+ case toward_greater_e:
+ {
+ /* "If the TOWARD-GREATER phrase is specified, and the arithmetic
value
+ cannot be represented exactly in the resultant identifier, the
+ arithmetic value is rounded to the nearest larger value that can
be
+ represented in the resultant identifier." */
+ IF( value, ge_op, zero )
+ {
+ // The value is positive, so if remainder != 0, increment it
+ IF( rem, ne_op, zero )
+ {
+ gg_increment(value);
+ }
+ ELSE {} ENDIF
+ }
+ ELSE {} ENDIF
+ break;
+ }
+
+ case toward_lesser_e:
+ {
+ /* "If the TOWARD-LESS phrase is specified, and the arithmetic
value
+ cannot be represented exactly in the resultant identifier, the
+ arithmetic value is rounded to the nearest smaller value that
+ can be represented in the resultant identifier." */
+ IF( value, lt_op, zero )
+ {
+ // The value is negative, so if remainder != 0 , decrement it
+ IF( rem, ne_op, zero )
+ {
+ gg_decrement(value);
+ }
+ ELSE {} ENDIF
+ }
+ ELSE {} ENDIF
+ break;
+ }
+
+ case truncation_e:
+ {
+ /* We do nothing.*/
+ gg_assign(value, gg_divide(value, pot));
+ break;
+ }
+ }
+ IF( value, eq_op, build_int_cst_type(type, 0) )
+ {
+ gg_assign(retval, integer_zero_node);
+ }
+ ELSE
+ {
+ gg_assign(retval, integer_one_node);
+ }
+ ENDIF
+ return retval;
+ }
diff --git a/gcc/cobol/genutil.h b/gcc/cobol/genutil.h
index e9ec2630718..985b3a52d51 100644
--- a/gcc/cobol/genutil.h
+++ b/gcc/cobol/genutil.h
@@ -90,11 +90,10 @@ void scale_by_power_of_ten_N(tree value,
tree scale_by_power_of_ten(tree value,
tree N,
bool check_for_fractional = false);
-void scale_and_round(tree value,
- int value_rdigits,
- bool target_is_signable,
- int target_rdigits,
- cbl_round_t rounded);
+tree round_this_value( tree &value,
+ tree power_of_ten,
+ cbl_round_t rounded,
+ tree size_error);
void hex_dump(tree data, size_t bytes);
void set_exception_code_func(ec_type_t ec,
int line,
diff --git a/gcc/cobol/move.cc b/gcc/cobol/move.cc
index 4050c49e8d2..d57ae6b20ce 100644
--- a/gcc/cobol/move.cc
+++ b/gcc/cobol/move.cc
@@ -1268,6 +1268,527 @@ mh_numeric_display( const cbl_refer_t &destref,
return moved;
}
+static tree
+cobol_wider_type_with_x_signedness (tree s_type, tree x_type)
+ {
+ gcc_assert (INTEGRAL_TYPE_P (s_type));
+ gcc_assert (INTEGRAL_TYPE_P (x_type));
+
+ unsigned int s_prec = TYPE_PRECISION (s_type);
+ unsigned int x_prec = TYPE_PRECISION (x_type);
+
+ unsigned int w_prec = MAX (s_prec, x_prec);
+ int unsignedp = TYPE_UNSIGNED (x_type);
+
+ return build_nonstandard_integer_type (w_prec, unsignedp);
+ }
+
+static bool
+mh_binary_to_numdisp(const cbl_refer_t &destref,
+ const cbl_refer_t &sourceref,
+ cbl_round_t rounded,
+ tree size_error)
+ {
+ bool moved = false;
+
+ charmap_t *charmap_dest =
+ __gg__get_charmap(destref.field->codeset.encoding);
+ if( destref.field->type == FldNumericDisplay
+ && !(destref.field->attr & scaled_e)
+ && !(sourceref.field->attr & scaled_e)
+ && charmap_dest->stride() == 1
+ && ( sourceref.field->type == FldNumericBinary
+ || sourceref.field->type == FldNumericBin5
+ || sourceref.field->type == FldLiteralN
+ || sourceref.field->type == FldIndex
+ || sourceref.field->type == FldPointer ) )
+ {
+ tree plus = build_int_cst_type(UCHAR,
+ charmap_dest->is_like_ebcdic()
+ ? ebcdic_plus : ascii_plus );
+ tree minus = build_int_cst_type(UCHAR,
+ charmap_dest->is_like_ebcdic()
+ ? ebcdic_minus : ascii_minus );
+ tree dest_location;
+ get_location(dest_location, destref);
+
+ tree s_type = tree_type_from_refer(sourceref);
+ tree d_type = tree_type_from_refer(destref);
+
+ // Our working type is the larger of the source and destination
types.
+ tree work_type = cobol_wider_type_with_x_signedness(d_type, s_type);
+
+ tree value ;
+ get_binary_value(value, sourceref, work_type);
+
+ tree negative = gg_define_variable(INT);
+ gg_assign(negative, integer_zero_node);
+
+ tree sign_location = NULL_TREE;
+
+ if( destref.field->attr & signable_e )
+ {
+ sign_location = gg_define_variable(UCHAR_P);
+ if( (destref.field->attr & separate_e)
+ && (destref.field->attr & leading_e ) )
+ {
+ // separate and leading
+ gg_assign(sign_location, dest_location);
+ gg_increment(dest_location);
+ }
+ else if( (destref.field->attr & separate_e)
+ && !(destref.field->attr & leading_e ) )
+ {
+ // separate and trailing
+ gg_assign(sign_location, gg_add(dest_location,
+ build_int_cst_type(SIZE_T,
+
destref.field->data.capacity()-1)));
+ }
+ else if( !(destref.field->attr & separate_e)
+ && (destref.field->attr & leading_e ) )
+ {
+ // internal and leading
+ gg_assign(sign_location, dest_location);
+ }
+ else
+ {
+ // internal and trailing
+ gg_assign(sign_location, gg_add(dest_location,
+ build_int_cst_type(SIZE_T,
+
destref.field->data.capacity()-1)));
+ }
+ }
+
+ if( (sourceref.field->attr & signable_e)
+ && (destref.field->attr & signable_e) )
+ {
+ // Both source and dest are signable, which means we have to
preserve
+ // the source sign and apply it, eventually, to the target.
+ IF( value, lt_op, gg_cast(work_type, integer_zero_node) )
+ {
+ gg_assign(negative, integer_one_node);
+ }
+ ELSE {} ENDIF
+ }
+
+ // At this point we have to align the source and destination value
rdigits.
+
+ if( !(sourceref.field->attr & intermediate_e) )
+ {
+ // Because the source is not intermediate, we can work with the
compile-
+ // time values.
+ int source_rdigits = sourceref.field->data.rdigits;
+ int dest_rdigits = destref.field->data.rdigits;
+ int nshift = source_rdigits - dest_rdigits;
+ if(nshift < 0)
+ {
+ // We need to multiply the source by 10^(-nshift) to line them
up.
+ FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( -nshift );
+ gg_assign(value, gg_multiply(value,
+ wide_int_to_tree(work_type,
+ power_of_ten)));
+ }
+ else if(nshift > 0)
+ {
+ // We need to divide the source by 10^(nshift) to line them up.
+ // This is a potential rounding situation.
+ FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( nshift );
+ tree pot = wide_int_to_tree(work_type, power_of_ten);
+ gg_assign(negative,
+ gg_bitwise_and( negative,
+ round_this_value(value,
+ pot,
+ rounded,
+ size_error)));
+ }
+ }
+ else
+ {
+ // Source is intermediate; we need to use the dynamic source
rdigits
+ // Because the source is not intermediate, we can work with the
compile-
+ // time values.
+ tree source_rdigits = gg_define_variable(INT);
+ tree dest_rdigits;
+ tree nshift = gg_define_variable(INT);
+
+ gg_assign(source_rdigits,
+ gg_cast(INT,
+ member(sourceref.field->var_decl_node,
+ "rdigits")));
+ dest_rdigits = build_int_cst_type(INT,
destref.field->data.rdigits);
+ gg_assign(nshift, gg_subtract(source_rdigits, dest_rdigits));
+ tree power_of_ten = gg_define_variable(work_type);
+ IF( nshift, lt_op, integer_zero_node )
+ {
+ // We need to multiply the source by 10^(-nshift) to line them
up.
+ gg_assign(power_of_ten,
+ gg_cast(work_type,
+ gg_call_expr(INT128,
+ "__gg__power_of_ten",
+ gg_negate(nshift),
+ NULL_TREE)));
+ gg_assign(value, gg_multiply(value, power_of_ten));
+ }
+ ELSE
+ {
+ IF( nshift, gt_op, integer_zero_node )
+ {
+ // We need to divide the source by 10^(nshift) to line them up.
+ // This is a potential rounding situation.
+ gg_assign(power_of_ten,
+ gg_cast(work_type,
+ gg_call_expr(INT128,
+ "__gg__power_of_ten",
+ nshift,
+ NULL_TREE)));
+ gg_assign(negative,
+ gg_bitwise_and( negative,
+ round_this_value(value,
+ power_of_ten,
+ rounded,
+ size_error)));
+ }
+ ELSE
+ {
+ }
+ ENDIF
+ }
+ ENDIF
+ }
+
+ // At this point, value is lined up with the destination.
+
+ // Make it positive
+
+ if( !TYPE_UNSIGNED(work_type) )
+ {
+ gg_assign(value, gg_abs(value));
+ }
+
+ if( size_error )
+ {
+ // We need to see if is too big to fit
+ FIXED_WIDE_INT(128) power_of_ten =
+
get_power_of_ten(destref.field->data.digits);
+ tree pot = wide_int_to_tree(work_type, power_of_ten);
+ IF( gg_divide(value, pot),
+ ne_op,
+ gg_cast(work_type, integer_zero_node) )
+ {
+ // The value is too big; flag it:
+ gg_assign(size_error, integer_one_node);
+ }
+ ELSE
+ {
+ }
+ ENDIF
+ }
+
+ if( charmap_dest->is_like_ebcdic() )
+ {
+ gg_call(INT,
+ "__gg__binary_to_string_ebcdic",
+ dest_location,
+ build_int_cst_type(INT, destref.field->data.digits),
+ gg_cast(INT128, value),
+ NULL_TREE);
+ }
+ else
+ {
+ gg_call(INT,
+ "__gg__binary_to_string_ascii",
+ dest_location,
+ build_int_cst_type(INT, destref.field->data.digits),
+ gg_cast(INT128, value),
+ NULL_TREE);
+ }
+
+ if( (sourceref.field->attr & signable_e )
+ && (destref.field->attr & signable_e ) )
+ {
+ IF( negative, ne_op, integer_zero_node )
+ {
+ if( destref.field->attr & separate_e )
+ {
+ // We flag the separate as negative
+ gg_assign(gg_indirect(sign_location), minus);
+ }
+ else
+ {
+ if( charmap_dest->is_like_ebcdic() )
+ {
+ gg_assign(gg_indirect(sign_location),
+ gg_bitwise_and(gg_indirect(sign_location),
+ build_int_cst_type(UCHAR, 0xDF)));
+ }
+ else
+ {
+ gg_assign(gg_indirect(sign_location),
+ gg_bitwise_or(gg_indirect(sign_location),
+ build_int_cst_type(UCHAR, 0x70)));
+ }
+ }
+ }
+ ELSE
+ {
+ // The result is positive
+ if( destref.field->attr & separate_e )
+ {
+ // We flag the separate as negative
+ gg_assign(gg_indirect(sign_location), plus);
+ }
+ }
+ ENDIF
+ }
+ else if( (destref.field->attr & signable_e )
+ && (destref.field->attr & separate_e ) )
+ {
+ // The source is not signed, but the destination is signable and
+ // separate:
+ gg_assign(gg_indirect(sign_location), plus);
+ }
+
+ moved = true;
+ }
+
+ return moved;
+ }
+
+static bool
+mh_binary_to_packed(const cbl_refer_t &destref,
+ const cbl_refer_t &sourceref,
+ cbl_round_t rounded,
+ tree size_error)
+ {
+ bool moved = false;
+
+ if( destref.field->type == FldPacked
+ && !(destref.field->attr & scaled_e)
+ && !(sourceref.field->attr & scaled_e)
+ && ( sourceref.field->type == FldNumericBinary
+ || sourceref.field->type == FldNumericBin5
+ || sourceref.field->type == FldLiteralN
+ || sourceref.field->type == FldIndex
+ || sourceref.field->type == FldPointer ) )
+ {
+ tree dest_location;
+ get_location(dest_location, destref);
+
+ tree s_type = tree_type_from_refer(sourceref);
+ tree d_type = tree_type_from_refer(destref);
+
+ // Our working type is the larger of the source and destination
types.
+ tree work_type = cobol_wider_type_with_x_signedness(d_type, s_type);
+
+ tree value ;
+ get_binary_value(value, sourceref, work_type);
+
+ tree negative = gg_define_variable(INT);
+ gg_assign(negative, integer_zero_node);
+
+ if( (sourceref.field->attr & signable_e)
+ && (destref.field->attr & signable_e) )
+ {
+ // Both source and dest are signable, which means we have to
preserve
+ // the source sign and apply it, eventually, to the target.
+ IF( value, lt_op, gg_cast(work_type, integer_zero_node) )
+ {
+ gg_assign(negative, integer_one_node);
+ }
+ ELSE
+ {
+ gg_assign(negative, integer_zero_node);
+ }
+ ENDIF
+ }
+
+ // At this point we have to align the source and destination value
rdigits.
+
+ if( !(sourceref.field->attr & intermediate_e) )
+ {
+ // Because the source is not intermediate, we can work with the
compile-
+ // time values.
+ int source_rdigits = sourceref.field->data.rdigits;
+ int dest_rdigits = destref.field->data.rdigits;
+ int nshift = source_rdigits - dest_rdigits;
+ if(nshift < 0)
+ {
+ // We need to multiply the source by 10^(-nshift) to line them
up.
+ FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten( -nshift );
+ gg_assign(value, gg_multiply(value,
+ wide_int_to_tree(work_type,
+ power_of_ten)));
+ }
+ else if(nshift > 0)
+ {
+ // We need to divide the source by 10^(nshift) to line them up.
+ // This is a potential rounding situation.
+ FIXED_WIDE_INT(128) power_of_ten = get_power_of_ten(nshift);
+ tree pot = wide_int_to_tree(work_type, power_of_ten);
+ gg_assign(negative,
+ gg_bitwise_and( negative,
+ round_this_value(value,
+ pot,
+ rounded,
+ size_error)));
+ }
+ }
+ else
+ {
+ // Source is intermediate; we need to use the dynamic source
rdigits
+ // Because the source is not intermediate, we can work with the
compile-
+ // time values.
+ tree source_rdigits = gg_define_variable(INT);
+ tree dest_rdigits;
+ tree nshift = gg_define_variable(INT);
+
+ gg_assign(source_rdigits,
+ gg_cast(INT,
+ member(sourceref.field->var_decl_node,
+ "rdigits")));
+ dest_rdigits = build_int_cst_type(INT,
destref.field->data.rdigits);
+ gg_assign(nshift, gg_subtract(source_rdigits, dest_rdigits));
+ tree power_of_ten = gg_define_variable(work_type);
+ IF( nshift, lt_op, integer_zero_node )
+ {
+ // We need to multiply the source by 10^(-nshift) to line them
up.
+ gg_assign(power_of_ten,
+ gg_cast(work_type,
+ gg_call_expr(INT128,
+ "__gg__power_of_ten",
+ gg_negate(nshift),
+ NULL_TREE)));
+ gg_assign(value, gg_multiply(value, power_of_ten));
+ }
+ ELSE
+ {
+ IF( nshift, gt_op, integer_zero_node )
+ {
+ // We need to divide the source by 10^(nshift) to line them up.
+ // This is a potential rounding situation.
+ gg_assign(power_of_ten,
+ gg_cast(work_type,
+ gg_call_expr(INT128,
+ "__gg__power_of_ten",
+ nshift,
+ NULL_TREE)));
+ // At this point, value is ten times as big as the final value,
so
+ // we are set up to round it:
+ gg_assign(negative,
+ gg_bitwise_and( negative,
+ round_this_value(value,
+ power_of_ten,
+ rounded,
+ size_error)));
+ }
+ ELSE
+ {
+ }
+ ENDIF
+ }
+ ENDIF
+ }
+
+ // At this point, value is lined up with the destination.
+
+ // Make it positive
+
+ if( !TYPE_UNSIGNED(work_type) )
+ {
+ gg_assign(value, gg_abs(value));
+ }
+
+ if( size_error )
+ {
+ // We need to see if is too big to fit
+ FIXED_WIDE_INT(128) power_of_ten =
+
get_power_of_ten(destref.field->data.digits);
+ tree pot = wide_int_to_tree(work_type, power_of_ten);
+ IF( gg_divide(value, pot),
+ ne_op,
+ gg_cast(work_type, integer_zero_node) )
+ {
+ // The value is too big; flag it:
+ gg_assign(size_error, integer_one_node);
+ }
+ ELSE
+ {
+ }
+ ENDIF
+ }
+
+ // We are now ready to convert the binary to the packed byte string.
+
+ int ndigits;
+ if( !(destref.field->attr & packed_no_sign_e) )
+ {
+ // This is ordinary packed. We need to multiply the value by ten
to
+ // make room for the sign nybble.
+ gg_assign(value, gg_multiply(value, build_int_cst_type(work_type,
10)));
+ ndigits = destref.field->data.digits+1;
+ }
+ else
+ {
+ ndigits = destref.field->data.digits;
+ }
+
+ gg_call(INT,
+ "__gg__binary_to_packed",
+ dest_location,
+ build_int_cst_type(INT, ndigits),
+ gg_cast(INT128, value),
+ NULL_TREE);
+
+ if( !(destref.field->attr & packed_no_sign_e) )
+ {
+ tree sign_loc = gg_add(dest_location,
+ build_int_cst_type(SIZE_T,
+
destref.field->data.capacity()-1));
+ if( !(destref.field->attr & signable_e) )
+ {
+ // This is an unsigned packed decimal.
+ gg_assign(gg_indirect(sign_loc),
+ gg_bitwise_or(gg_indirect(sign_loc),
+ build_int_cst_type(UCHAR, 0x0F)));
+ }
+ else
+ {
+ // It is signable
+ if( !(sourceref.field->attr & signable_e) )
+ {
+ // The source wasn't signable, so the destination has to be
positive.
+ gg_assign(gg_indirect(sign_loc),
+ gg_bitwise_or(gg_indirect(sign_loc),
+ build_int_cst_type(UCHAR, 0x0C)));
+ }
+ else
+ {
+ // The source was signable, so we have to transfer the sign
+ IF( negative, ne_op, integer_one_node )
+ {
+ // The result is non-negative
+ gg_assign(gg_indirect(sign_loc),
+ gg_bitwise_or(gg_indirect(sign_loc),
+ build_int_cst_type(UCHAR, 0x0C)));
+ }
+ ELSE
+ {
+ // The result is negative
+ gg_assign(gg_indirect(sign_loc),
+ gg_bitwise_or(gg_indirect(sign_loc),
+ build_int_cst_type(UCHAR, 0x0D)));
+ }
+ ENDIF
+ }
+ }
+ }
+
+ moved = true;
+ }
+
+ return moved;
+ }
+
static void
copy_little_endian_into_place(cbl_field_t *dest,
tree dest_offset,
@@ -2952,6 +3473,22 @@ move_helper(tree size_error, // This is an
INT
size_error);
}
+ if( !moved )
+ {
+ moved = mh_binary_to_numdisp(destref,
+ sourceref,
+ rounded,
+ size_error);
+ }
+
+ if( !moved )
+ {
+ moved = mh_binary_to_packed(destref,
+ sourceref,
+ rounded,
+ size_error);
+ }
+
if( !moved )
{
SHOW_PARSE1
diff --git
a/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative
.cob
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative
.cob
new file mode 100644
index 00000000000..afa6eb9b51c
--- /dev/null
+++
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative
.cob
@@ -0,0 +1,62 @@
+ *> { dg-do run }
+ *> { dg-output-file
"group2/Rounding_from_BINARY_signable_and_negative.out" }
+ identification division.
+ program-id. prog.
+ data division.
+ working-storage section.
+ 77 v0 pic s9999v9 binary value -1234.0 .
+ 77 v1 pic s9999v9 binary value -1234.1 .
+ 77 v5 pic s9999v9 binary value -1234.5 .
+ 77 v9 pic s9999v9 binary value -1234.9 .
+ 77 d0 pic s9999 display .
+ 77 d1 pic s9999 display .
+ 77 d5 pic s9999 display .
+ 77 d9 pic s9999 display .
+
+ procedure division.
+ display "-1234.0 -1234.1 -1234.5 -1234.9"
+ compute d0 = v0
+ compute d1 = v1
+ compute d5 = v5
+ compute d9 = v9
+ display d0 " " d1 " " d5 " " d9 " " "truncated"
+
+ compute d0 ROUNDED MODE AWAY-FROM-ZERO = v0
+ compute d1 ROUNDED MODE AWAY-FROM-ZERO = v1
+ compute d5 ROUNDED MODE AWAY-FROM-ZERO = v5
+ compute d9 ROUNDED MODE AWAY-FROM-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " " "away-from-zero"
+
+ compute d0 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v0
+ compute d1 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v1
+ compute d5 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v5
+ compute d9 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " "
"nearest-away-from-zero"
+
+ compute d0 ROUNDED MODE NEAREST-EVEN = v0
+ compute d1 ROUNDED MODE NEAREST-EVEN = v1
+ compute d5 ROUNDED MODE NEAREST-EVEN = v5
+ compute d9 ROUNDED MODE NEAREST-EVEN = v9
+ display d0 " " d1 " " d5 " " d9 " " "nearest-even"
+
+ compute d0 ROUNDED MODE NEAREST-TOWARD-ZERO = v0
+ compute d1 ROUNDED MODE NEAREST-TOWARD-ZERO = v1
+ compute d5 ROUNDED MODE NEAREST-TOWARD-ZERO = v5
+ compute d9 ROUNDED MODE NEAREST-TOWARD-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " "
"nearest-toward-zero"
+
+ compute d0 ROUNDED MODE TOWARD-GREATER = v0
+ compute d1 ROUNDED MODE TOWARD-GREATER = v1
+ compute d5 ROUNDED MODE TOWARD-GREATER = v5
+ compute d9 ROUNDED MODE TOWARD-GREATER = v9
+ display d0 " " d1 " " d5 " " d9 " " "toward-greater"
+
+ compute d0 ROUNDED MODE TOWARD-LESSER = v0
+ compute d1 ROUNDED MODE TOWARD-LESSER = v1
+ compute d5 ROUNDED MODE TOWARD-LESSER = v5
+ compute d9 ROUNDED MODE TOWARD-LESSER = v9
+ display d0 " " d1 " " d5 " " d9 " " "toward-lesser"
+
+ goback.
+ end program prog.
+
diff --git
a/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative
.out
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative
.out
new file mode 100644
index 00000000000..682fc1b1bbe
--- /dev/null
+++
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_negative
.out
@@ -0,0 +1,9 @@
+-1234.0 -1234.1 -1234.5 -1234.9
+-1234 -1234 -1234 -1234 truncated
+-1234 -1235 -1235 -1235 away-from-zero
+-1234 -1234 -1235 -1235 nearest-away-from-zero
+-1234 -1234 -1234 -1235 nearest-even
+-1234 -1234 -1234 -1235 nearest-toward-zero
+-1234 -1234 -1234 -1234 toward-greater
+-1234 -1235 -1235 -1235 toward-lesser
+
diff --git
a/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive
.cob
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive
.cob
new file mode 100644
index 00000000000..3cdae70c838
--- /dev/null
+++
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive
.cob
@@ -0,0 +1,62 @@
+ *> { dg-do run }
+ *> { dg-output-file
"group2/Rounding_from_BINARY_signable_and_positive.out" }
+ identification division.
+ program-id. prog.
+ data division.
+ working-storage section.
+ 77 v0 pic s9999v9 binary value 1234.0 .
+ 77 v1 pic s9999v9 binary value 1234.1 .
+ 77 v5 pic s9999v9 binary value 1234.5 .
+ 77 v9 pic s9999v9 binary value 1234.9 .
+ 77 d0 pic s9999 display .
+ 77 d1 pic s9999 display .
+ 77 d5 pic s9999 display .
+ 77 d9 pic s9999 display .
+
+ procedure division.
+ display "+1234.0 +1234.1 +1234.5 +1234.9"
+ compute d0 = v0
+ compute d1 = v1
+ compute d5 = v5
+ compute d9 = v9
+ display d0 " " d1 " " d5 " " d9 " " "truncated"
+
+ compute d0 ROUNDED MODE AWAY-FROM-ZERO = v0
+ compute d1 ROUNDED MODE AWAY-FROM-ZERO = v1
+ compute d5 ROUNDED MODE AWAY-FROM-ZERO = v5
+ compute d9 ROUNDED MODE AWAY-FROM-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " " "away-from-zero"
+
+ compute d0 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v0
+ compute d1 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v1
+ compute d5 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v5
+ compute d9 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " "
"nearest-away-from-zero"
+
+ compute d0 ROUNDED MODE NEAREST-EVEN = v0
+ compute d1 ROUNDED MODE NEAREST-EVEN = v1
+ compute d5 ROUNDED MODE NEAREST-EVEN = v5
+ compute d9 ROUNDED MODE NEAREST-EVEN = v9
+ display d0 " " d1 " " d5 " " d9 " " "nearest-even"
+
+ compute d0 ROUNDED MODE NEAREST-TOWARD-ZERO = v0
+ compute d1 ROUNDED MODE NEAREST-TOWARD-ZERO = v1
+ compute d5 ROUNDED MODE NEAREST-TOWARD-ZERO = v5
+ compute d9 ROUNDED MODE NEAREST-TOWARD-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " "
"nearest-toward-zero"
+
+ compute d0 ROUNDED MODE TOWARD-GREATER = v0
+ compute d1 ROUNDED MODE TOWARD-GREATER = v1
+ compute d5 ROUNDED MODE TOWARD-GREATER = v5
+ compute d9 ROUNDED MODE TOWARD-GREATER = v9
+ display d0 " " d1 " " d5 " " d9 " " "toward-greater"
+
+ compute d0 ROUNDED MODE TOWARD-LESSER = v0
+ compute d1 ROUNDED MODE TOWARD-LESSER = v1
+ compute d5 ROUNDED MODE TOWARD-LESSER = v5
+ compute d9 ROUNDED MODE TOWARD-LESSER = v9
+ display d0 " " d1 " " d5 " " d9 " " "toward-lesser"
+
+ goback.
+ end program prog.
+
diff --git
a/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive
.out
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive
.out
new file mode 100644
index 00000000000..c173c7aeee1
--- /dev/null
+++
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_signable_and_positive
.out
@@ -0,0 +1,9 @@
++1234.0 +1234.1 +1234.5 +1234.9
++1234 +1234 +1234 +1234 truncated
++1234 +1235 +1235 +1235 away-from-zero
++1234 +1234 +1235 +1235 nearest-away-from-zero
++1234 +1234 +1234 +1235 nearest-even
++1234 +1234 +1234 +1235 nearest-toward-zero
++1234 +1235 +1235 +1235 toward-greater
++1234 +1234 +1234 +1234 toward-lesser
+
diff --git
a/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.cob
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.cob
new file mode 100644
index 00000000000..ee02333bce4
--- /dev/null
+++ b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.cob
@@ -0,0 +1,62 @@
+ *> { dg-do run }
+ *> { dg-output-file "group2/Rounding_from_BINARY_unsignable.out" }
+ identification division.
+ program-id. prog.
+ data division.
+ working-storage section.
+ 77 v0 pic 9999v9 binary value 1234.0 .
+ 77 v1 pic 9999v9 binary value 1234.1 .
+ 77 v5 pic 9999v9 binary value 1234.5 .
+ 77 v9 pic 9999v9 binary value 1234.9 .
+ 77 d0 pic s9999 display .
+ 77 d1 pic s9999 display .
+ 77 d5 pic s9999 display .
+ 77 d9 pic s9999 display .
+
+ procedure division.
+ display " 1234.0 1234.1 1234.5 1234.9"
+ compute d0 = v0
+ compute d1 = v1
+ compute d5 = v5
+ compute d9 = v9
+ display d0 " " d1 " " d5 " " d9 " " "truncated"
+
+ compute d0 ROUNDED MODE AWAY-FROM-ZERO = v0
+ compute d1 ROUNDED MODE AWAY-FROM-ZERO = v1
+ compute d5 ROUNDED MODE AWAY-FROM-ZERO = v5
+ compute d9 ROUNDED MODE AWAY-FROM-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " " "away-from-zero"
+
+ compute d0 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v0
+ compute d1 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v1
+ compute d5 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v5
+ compute d9 ROUNDED MODE NEAREST-AWAY-FROM-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " "
"nearest-away-from-zero"
+
+ compute d0 ROUNDED MODE NEAREST-EVEN = v0
+ compute d1 ROUNDED MODE NEAREST-EVEN = v1
+ compute d5 ROUNDED MODE NEAREST-EVEN = v5
+ compute d9 ROUNDED MODE NEAREST-EVEN = v9
+ display d0 " " d1 " " d5 " " d9 " " "nearest-even"
+
+ compute d0 ROUNDED MODE NEAREST-TOWARD-ZERO = v0
+ compute d1 ROUNDED MODE NEAREST-TOWARD-ZERO = v1
+ compute d5 ROUNDED MODE NEAREST-TOWARD-ZERO = v5
+ compute d9 ROUNDED MODE NEAREST-TOWARD-ZERO = v9
+ display d0 " " d1 " " d5 " " d9 " "
"nearest-toward-zero"
+
+ compute d0 ROUNDED MODE TOWARD-GREATER = v0
+ compute d1 ROUNDED MODE TOWARD-GREATER = v1
+ compute d5 ROUNDED MODE TOWARD-GREATER = v5
+ compute d9 ROUNDED MODE TOWARD-GREATER = v9
+ display d0 " " d1 " " d5 " " d9 " " "toward-greater"
+
+ compute d0 ROUNDED MODE TOWARD-LESSER = v0
+ compute d1 ROUNDED MODE TOWARD-LESSER = v1
+ compute d5 ROUNDED MODE TOWARD-LESSER = v5
+ compute d9 ROUNDED MODE TOWARD-LESSER = v9
+ display d0 " " d1 " " d5 " " d9 " " "toward-lesser"
+
+ goback.
+ end program prog.
+
diff --git
a/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.out
b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.out
new file mode 100644
index 00000000000..0a1ff4754b0
--- /dev/null
+++ b/gcc/testsuite/cobol.dg/group2/Rounding_from_BINARY_unsignable.out
@@ -0,0 +1,9 @@
+ 1234.0 1234.1 1234.5 1234.9
++1234 +1234 +1234 +1234 truncated
++1234 +1235 +1235 +1235 away-from-zero
++1234 +1234 +1235 +1235 nearest-away-from-zero
++1234 +1234 +1234 +1235 nearest-even
++1234 +1234 +1234 +1235 nearest-toward-zero
++1234 +1235 +1235 +1235 toward-greater
++1234 +1234 +1234 +1234 toward-lesser
+
diff --git a/libgcobol/charmaps.cc b/libgcobol/charmaps.cc
index 6bce28c211c..8f28f85c8e5 100644
--- a/libgcobol/charmaps.cc
+++ b/libgcobol/charmaps.cc
@@ -1715,11 +1715,13 @@ __gg__miconverter( cbl_encoding_t from,
return retval;
}
-typedef std::unordered_map<cbl_encoding_t, charmap_t *,
cbl_encoding_t_hash>
-cbl_encoding_charmap_map;
+// I switched to this wasteful table when I learned that
unordered_map.find(),
+// fast though it is, at something like 23 nanoseconds was annoyingly
longer
+// than some of my efficient MOVE conversion routines.
-static
-cbl_encoding_charmap_map map_of_encodings;
+// Using 1500 is, I suppose, sloppy. Right now the biggest entry in the
+// cbl_encodings_t enum is about 1,150.
+static charmap_t *maps_weve_seen[iconv_LAST] = {};
charmap_t *
__gg__get_charmap(cbl_encoding_t encoding)
@@ -1741,15 +1743,15 @@ __gg__get_charmap(cbl_encoding_t encoding)
}
charmap_t *retval;
- cbl_encoding_charmap_map::const_iterator it =
map_of_encodings.find(encoding);
- if( it != map_of_encodings.end() )
+
+ if( maps_weve_seen[encoding] )
{
- retval = it->second;
+ retval = maps_weve_seen[encoding];
}
else
{
retval = new charmap_t(encoding);
- map_of_encodings[encoding] = retval;
+ maps_weve_seen[encoding] = retval;
}
return retval;
}
diff --git a/libgcobol/charmaps.h b/libgcobol/charmaps.h
index 3fb1bd067d5..a16caa57ea8 100644
--- a/libgcobol/charmaps.h
+++ b/libgcobol/charmaps.h
@@ -263,6 +263,9 @@ enum
#define ascii_ff ((uint8_t)('\f'))
#define ascii_newline ((uint8_t)('\n'))
#define ascii_return ((uint8_t)('\r'))
+#define ebcdic_zero ((uint8_t)(0xF0))
+#define ebcdic_plus ((uint8_t)(0x4E))
+#define ebcdic_minus ((uint8_t)(0x60))
extern unsigned char __gg__data_space[1] ;
extern unsigned char __gg__data_low_values[1] ;
diff --git a/libgcobol/encodings.h b/libgcobol/encodings.h
index cf46d83981a..e7de8793f4a 100644
--- a/libgcobol/encodings.h
+++ b/libgcobol/encodings.h
@@ -33,8 +33,10 @@
#include <type_traits>
+// These values explicitly start at zero. The final entry is used for
sizing
+// a lookup array.
enum cbl_encoding_t {
- no_encoding_e,
+ no_encoding_e = 0,
custom_encoding_e,
iconv_1026_e,
iconv_1046_e,
@@ -1195,6 +1197,7 @@ enum cbl_encoding_t {
iconv_WIN_SAMI_2_e,
iconv_WS2_e,
iconv_YU_e,
+ iconv_LAST, // This must be the last one. It's used for array sizing
};
static inline bool
@@ -1202,7 +1205,7 @@ valid_encoding( cbl_encoding_t enc ) {
return enc <= iconv_YU_e;
}
-#define ASCII_e iconv_ASCII_e
+#define ASCII_e iconv_ASCII_e
#define CP1252_e iconv_CP1252_e
#define EBCDIC_e iconv_CP1140_e
#define UTF8_e iconv_UTF_8_e
diff --git a/libgcobol/stringbin.cc b/libgcobol/stringbin.cc
index 8597338c57d..acf2fff02a0 100644
--- a/libgcobol/stringbin.cc
+++ b/libgcobol/stringbin.cc
@@ -128,148 +128,128 @@ static char zero_char;
typedef struct
{
- int start;
int run;
union
{
unsigned __int128 val128;
uint64_t val64;
- uint32_t val32;
- uint16_t val16;
- uint8_t val8;
};
} COMBINED;
-static
-void
-string_from_combined(const COMBINED &combined)
+#if defined(__cplusplus) && __cplusplus >= 201703L
+# define FALLTHROUGH [[fallthrough]]
+#elif defined(__GNUC__) && __GNUC__ >= 7
+# define FALLTHROUGH __attribute__((fallthrough))
+#else
+# define FALLTHROUGH ((void)0)
+#endif
+
+static const unsigned char digits2[100][2] =
{
- COMBINED left;
- COMBINED right;
+ {0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,6},{0,7},{0,8},{0,9},
+ {1,0},{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},{1,7},{1,8},{1,9},
+ {2,0},{2,1},{2,2},{2,3},{2,4},{2,5},{2,6},{2,7},{2,8},{2,9},
+ {3,0},{3,1},{3,2},{3,3},{3,4},{3,5},{3,6},{3,7},{3,8},{3,9},
+ {4,0},{4,1},{4,2},{4,3},{4,4},{4,5},{4,6},{4,7},{4,8},{4,9},
+ {5,0},{5,1},{5,2},{5,3},{5,4},{5,5},{5,6},{5,7},{5,8},{5,9},
+ {6,0},{6,1},{6,2},{6,3},{6,4},{6,5},{6,6},{6,7},{6,8},{6,9},
+ {7,0},{7,1},{7,2},{7,3},{7,4},{7,5},{7,6},{7,7},{7,8},{7,9},
+ {8,0},{8,1},{8,2},{8,3},{8,4},{8,5},{8,6},{8,7},{8,8},{8,9},
+ {9,0},{9,1},{9,2},{9,3},{9,4},{9,5},{9,6},{9,7},{9,8},{9,9}
+ };
- uint16_t v16;
+static void
+uint_to_8_digits(unsigned int a, unsigned char *ach, int n)
+ {
+ unsigned int x;
- switch(combined.run)
+ switch(n)
{
- case 1:
- // We know that val8 is a single digit
- combined_string[combined.start] = combined.val8 + zero_char;
- break;
-
- case 2:
- // We know that val8 has two digits
- combined_string[combined.start] = digit_high[combined.val8] +
zero_char;
- combined_string[combined.start+1] = digit_low [combined.val8] +
zero_char;
- break;
-
- case 3:
- // We know that val16 has three digits.
- v16 = combined.val16;
- combined_string[combined.start] = v16 / 100 + zero_char;
- v16 %= 100;
- combined_string[combined.start+1] = v16 / 10 + zero_char;
- combined_string[combined.start+2] = v16 % 10 + zero_char;
- break;
-
- case 4:
- // We know that val16 has four digits:
- v16 = combined.val16;
- combined_string[combined.start] = v16 / 1000 + zero_char;
- v16 %= 1000;
- combined_string[combined.start+1] = v16 / 100 + zero_char;
- v16 %= 100;
- combined_string[combined.start+2] = v16 / 10 + zero_char;
- combined_string[combined.start+3] = v16 % 10 + zero_char;
- break;
-
- case 5:
- case 6:
- case 7:
case 8:
- // We know that val32 can be treated as two 4-digit pieces
- left.start = combined.start;
- left.run = combined.run - 4;
- left.val16 = combined.val32 / 10000;
+ x = a % 100;
+ ach[6] = digits2[x][0];
+ ach[7] = digits2[x][1];
+ a /= 100;
+ FALLTHROUGH;
- right.start = combined.start+left.run;
- right.run = 4;
- right.val16 = combined.val32 % 10000;
-
- string_from_combined(left);
- string_from_combined(right);
- break;
-
- case 9:
- // We break val32 into a 1-digit piece, and an 8-digit piece:
- left.start = combined.start;
- left.run = combined.run - 8;
- left.val32 = combined.val32 / 100000000;
-
- right.start = combined.start+left.run;
- right.run = 8;
- right.val32 = combined.val32 % 100000000;
+ case 7:
+ case 6:
+ x = a % 100;
+ ach[4] = digits2[x][0];
+ ach[5] = digits2[x][1];
+ a /= 100;
+ FALLTHROUGH;
- string_from_combined(left);
- string_from_combined(right);
- break;
+ case 5:
+ case 4:
+ x = a % 100;
+ ach[2] = digits2[x][0];
+ ach[3] = digits2[x][1];
+ a /= 100;
+ FALLTHROUGH;
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- case 16:
- case 17:
- case 18:
- // We know we can treat val64 as two 9-digit pieces:
- left.start = combined.start;
- left.run = combined.run - 9;
- left.val32 = combined.val64 / 1000000000;
-
- right.start = combined.start+left.run;
- right.run = 9;
- right.val32 = combined.val64 % 1000000000;
-
- string_from_combined(left);
- string_from_combined(right);
+ case 3:
+ case 2:
+ x = a % 100;
+ ach[0] = digits2[x][0];
+ ach[1] = digits2[x][1];
+ FALLTHROUGH;
+ default:
break;
+ }
+ }
- case 19:
- // We split off the bottom nine digits
- left.start = combined.start;
- left.run = combined.run - 9;
- left.val64 = combined.val64 / 1000000000;
-
- right.start = combined.start+left.run;
- right.run = 9;
- right.val32 = combined.val64 % 1000000000;
-
- string_from_combined(left);
- string_from_combined(right);
- break;
+static
+void
+string_from_combined(const COMBINED &combined)
+ {
+ int ndigits = combined.run;
+ unsigned __int128 value = combined.val128;
- default:
- // For twenty or more digits we peel eighteen digits at a time off
the
- // right side:
- left.start = combined.start;
- left.run = combined.run - 18;
- left.val128 = combined.val128 / 1000000000000000000ULL;
-
- right.start = combined.start+left.run;
- right.run = 18;
- right.val64 = combined.val128 % 1000000000000000000ULL;
-
- string_from_combined(left);
- string_from_combined(right);
- break;
+ if( ndigits & 0x01 )
+ {
+ combined_string[ndigits-1] = value%10;
+ value /= 10;
+ ndigits -= 1;
+ }
+ while(ndigits >= 8)
+ {
+ unsigned int val = value % 100000000;
+ uint_to_8_digits(val,
+ reinterpret_cast<unsigned char *>(combined_string +
ndigits-8), 8);
+ value /= 100000000;
+ ndigits -= 8;
+ }
+ if( ndigits )
+ {
+ const unsigned int pots[8] =
+ {
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ };
+
+ unsigned int val = value % pots[ndigits];
+ uint_to_8_digits(val,
+ reinterpret_cast<unsigned char *>(combined_string),
ndigits);
+ value /= 100000000;
+ }
+ char *p = combined_string;
+ const char *pend = p + combined.run;
+ while(p < pend)
+ {
+ *p++ += zero_char;
}
}
-bool
-__gg__binary_to_string_ascii(char *result, int digits, __int128 value)
+static bool
+binary_to_string(char *result, int digits, __int128 value)
{
- zero_char = ascii_zero;
bool retval; // True means the value was too big to fit into digits
if( digits < 39 )
{
@@ -291,7 +271,6 @@ __gg__binary_to_string_ascii(char *result, int digits,
__int128 value)
// mask off the bottom digits to avoid garbage when value is too
large
value %= mask;
- combined.start = 0;
combined.run = digits;
combined.val128 = value;
string_from_combined(combined);
@@ -307,7 +286,6 @@ __gg__binary_to_string_ascii(char *result, int digits,
__int128 value)
// number of digits:
retval = false;
- combined.start = 0;
combined.run = digits;
combined.val128 = value;
string_from_combined(combined);
@@ -316,6 +294,22 @@ __gg__binary_to_string_ascii(char *result, int
digits, __int128 value)
return retval;
}
+extern "C"
+bool
+__gg__binary_to_string_ascii(char *result, int digits, __int128 value)
+ {
+ zero_char = ascii_zero;
+ return binary_to_string(result, digits, value);
+ }
+
+extern "C"
+bool
+__gg__binary_to_string_ebcdic(char *result, int digits, __int128 value)
+ {
+ zero_char = ebcdic_zero;
+ return binary_to_string(result, digits, value);
+ }
+
bool
__gg__binary_to_string_encoded( char *result,
size_t digits,
@@ -325,7 +319,10 @@ __gg__binary_to_string_encoded( char *result,
// A non-zero retval means the number was too big to fit into the
desired
// number of digits.
- zero_char = ascii_0;
+ const charmap_t *charmap = __gg__get_charmap(encoding);
+ int stride = charmap->stride();
+
+ zero_char = charmap->is_like_ebcdic() ? ebcdic_zero : ascii_0;
// Note that this routine does not terminate the generated string with
a
// NUL. This routine is sometimes used to generate a NumericDisplay
string
@@ -343,22 +340,29 @@ __gg__binary_to_string_encoded( char *result,
// mask off the bottom digits to avoid garbage when value is too large
value %= mask;
- combined.start = 0;
combined.run = digits;
combined.val128 = value;
string_from_combined(combined);
- size_t converted_bytes;
- const char *converted = __gg__iconverter(DEFAULT_SOURCE_ENCODING,
- encoding,
- combined_string,
- digits,
- &converted_bytes);
- memcpy(result, converted, converted_bytes);
+ if( stride == 1 )
+ {
+ memcpy(result, combined_string, digits);
+ }
+ else
+ {
+ char *p = combined_string;
+ const char *pend = p + digits;
+ char *d = result;
+ while(p < pend)
+ {
+ *d++ = *p++;
+ memset(d, 0, stride-1);
+ d += stride-1;
+ }
+ }
return retval;
}
-static
-void
+static void
packed_from_combined(const COMBINED &combined)
{
/* The combined.value must be positive at this point.
@@ -383,108 +387,33 @@ packed_from_combined(const COMBINED &combined)
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
} ;
- COMBINED left;
- COMBINED right;
+ char *d = combined_string + combined.run;
- switch(combined.run)
+ if( combined.run > 9)
{
- case 1:
- // We know that val8 has two digits.
- combined_string[combined.start] = bin2pd[combined.val8];
- break;
-
- case 2:
- // We know that val16 has four digits.
- combined_string[combined.start ] = bin2pd[combined.val16/100];
- combined_string[combined.start+1] = bin2pd[combined.val16%100];
- break;
-
- case 3:
- case 4:
- // We know that val32 can hold up to eight digits. Break it in
half.
- left.start = combined.start;
- left.run = combined.run - 2;
- left.val16 = combined.val32 / 10000;
-
- right.start = combined.start+left.run;
- right.run = 2;
- right.val16 = combined.val32 % 10000;
-
- packed_from_combined(left);
- packed_from_combined(right);
- break;
-
- case 5:
- case 6:
- case 7:
- case 8:
- // We know that val64 is holding up to 18 digits. Break it into
two
- // eight-digit places that can each go into a val23
- left.start = combined.start;
- left.run = combined.run - 4;
- left.val32 = combined.val64 / 100000000;
-
- right.start = combined.start+left.run;
- right.run = 4;
- right.val32 = combined.val64 % 100000000;
-
- packed_from_combined(left);
- packed_from_combined(right);
- break;
-
- case 9:
- // We know that val64 is holding 17 or 18 digits. Break off the
- // bottom eight.
- left.start = combined.start;
- left.run = combined.run - 4;
- left.val64 = combined.val64 / 100000000;
-
- right.start = combined.start+left.run;
- right.run = 4;
- right.val32 = combined.val64 % 100000000;
-
- packed_from_combined(left);
- packed_from_combined(right);
- break;
-
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- case 16:
- case 17:
- case 18:
- // We know that val64 is holding between 18 and 36 digits. Break
it
- // two val64:
-
- left.start = combined.start;
- left.run = combined.run - 9;
- left.val64 = combined.val128 / 1000000000000000000ULL;
-
- right.start = combined.start+left.run;
- right.run = 9;
- right.val64 = combined.val128 % 1000000000000000000ULL;
-
- packed_from_combined(left);
- packed_from_combined(right);
- break;
-
- default:
- // For twenty or more digits we peel eighteen digits at a time off
the
- // right side:
- left.start = combined.start;
- left.run = combined.run - 9;
- left.val128 = combined.val128 / 1000000000000000000ULL;
-
- right.start = combined.start+left.run;
- right.run = 9;
- right.val64 = combined.val128 % 1000000000000000000ULL;
-
- packed_from_combined(left);
- packed_from_combined(right);
- break;
+ // Stage 1: pull from int128 until the top half is zero.
+ __int128 value128 = combined.val128;
+ while(value128>>64)
+ {
+ *(--d) = bin2pd[value128%100];
+ value128 /= 100;
+ }
+ // Stage 2: Keep going with the 64-bit bottom half.
+ uint64_t value64 = value128;
+ while(d > combined_string)
+ {
+ *(--d) = bin2pd[value64%100];
+ value64 /= 100;
+ }
+ }
+ else
+ {
+ uint64_t value = combined.val64;
+ while(d > combined_string)
+ {
+ *(--d) = bin2pd[value%100];
+ value /= 100;
+ }
}
}
@@ -497,7 +426,6 @@ __gg__binary_to_packed( unsigned char *result,
size_t length = (digits+1)/2;
COMBINED combined;
- combined.start = 0;
combined.run = length;
combined.val128 = value;
packed_from_combined(combined);
@@ -671,3 +599,4 @@ __gg__packed_to_binary(const unsigned char *psz,
}
return retval;
}
+
--
2.34.1