https://gcc.gnu.org/g:33e26a071f9daea57cb0d170d75d9fdf406040f1
commit r16-3184-g33e26a071f9daea57cb0d170d75d9fdf406040f1 Author: Robert Dubner <rdub...@symas.com> Date: Wed Aug 13 11:17:05 2025 -0400 cobol: Implement and use faster __gg__packed_to_binary() routine. The new routine uses table lookups more effectively, and avoids __int128 arithmetic until necessary. gcc/cobol/ChangeLog: * genutil.cc (get_binary_value): Use the new routine. libgcobol/ChangeLog: * libgcobol.cc (get_binary_value_local): Use the new routine. * stringbin.cc (int_from_string): Removed. (__gg__packed_to_binary): Implement new routine. * stringbin.h (__gg__packed_to_binary): Likewise. Diff: --- gcc/cobol/genutil.cc | 60 ++++--------- libgcobol/libgcobol.cc | 49 +---------- libgcobol/stringbin.cc | 223 ++++++++++++++++++++++++------------------------- libgcobol/stringbin.h | 5 ++ 4 files changed, 134 insertions(+), 203 deletions(-) diff --git a/gcc/cobol/genutil.cc b/gcc/cobol/genutil.cc index e131d15d5361..3682b107c5ad 100644 --- a/gcc/cobol/genutil.cc +++ b/gcc/cobol/genutil.cc @@ -755,10 +755,6 @@ get_binary_value( tree value, static tree pointer = gg_define_variable( UCHAR_P, "..gbv_pointer", vs_file_static); - static tree pend = gg_define_variable(UCHAR_P, - "..gbv_pend", - vs_file_static); - switch(field->type) { case FldLiteralN: @@ -945,7 +941,9 @@ get_binary_value( tree value, vs_file_static); if( field->attr & signable_e ) { - IF( gg_array_value(gg_cast(build_pointer_type(SCHAR), source)), lt_op, gg_cast(SCHAR, integer_zero_node) ) + IF( gg_array_value(gg_cast(build_pointer_type(SCHAR), source)), + lt_op, + gg_cast(SCHAR, integer_zero_node) ) { gg_assign(extension, build_int_cst_type(UCHAR, 0xFF)); } @@ -1028,45 +1026,23 @@ get_binary_value( tree value, case FldPacked: { - // Zero out the destination: - gg_assign(value, gg_cast(TREE_TYPE(value), integer_zero_node)); - gg_assign(pointer, get_data_address(field, field_offset)); - gg_assign(pend, - gg_add(pointer, - build_int_cst_type(SIZE_T, field->data.capacity-1))); - - // Convert all but the last byte of the packed decimal sequence - WHILE( pointer, lt_op, pend ) - { - // Convert the first nybble - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - gg_assign(value, gg_add(value, gg_cast(TREE_TYPE(value), gg_rshift(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst(UINT, 4))))); - - // Convert the second nybble - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - gg_assign(value, gg_add(value, gg_cast(TREE_TYPE(value), gg_bitwise_and(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst_type(UCHAR, 0xF))))); - gg_increment(pointer); - } - WEND - - // This is the final byte: - gg_assign(value, gg_multiply(value, build_int_cst_type(TREE_TYPE(value), 10))); - gg_assign(value, gg_add(value, gg_cast(TREE_TYPE(value), gg_rshift(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst(UINT, 4))))); - - IF( gg_bitwise_and(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst_type(UCHAR, 0xF)), eq_op, build_int_cst_type(UCHAR, 0x0D) ) - { - gg_assign(value, gg_negate(value)); - } - ELSE + if( rdigits ) { - IF( gg_bitwise_and(gg_get_indirect_reference(pointer, NULL_TREE), build_int_cst_type(UCHAR, 0xF)), eq_op, build_int_cst_type(UCHAR, 0x0B) ) - { - gg_assign(value, gg_negate(value)); - } - ELSE - ENDIF + gg_assign(rdigits, + build_int_cst_type( TREE_TYPE(rdigits), + get_scaled_rdigits(field))); } - ENDIF + tree dest_type = TREE_TYPE(value); + + gg_assign(value, + gg_cast(dest_type, + gg_call_expr(INT128, + "__gg__packed_to_binary", + get_data_address( field, + field_offset), + build_int_cst_type(INT, + field->data.capacity), + NULL_TREE))); break; } diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc index f41434f9f3a0..427625366810 100644 --- a/libgcobol/libgcobol.cc +++ b/libgcobol/libgcobol.cc @@ -2149,52 +2149,9 @@ get_binary_value_local( int *rdigits, case FldPacked: { - static const unsigned char dp2bin[160] = - { - // This may not be the weirdest table I've ever created, but it is - // certainly a contender. Given the packed decimal byte 0x23, it - // returns the equivalent decimal value of 23. - 00, 01, 02, 03, 04, 05, 06, 07, 8, 9, 0, 0, 0, 0, 0, 0, // 0x00 - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, // 0x10 - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, // 0x20 - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, // 0x30 - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, // 0x40 - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0, // 0x50 - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0, // 0x60 - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, // 0x70 - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, // 0x80 - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0, 0, 0, 0, 0, 0, // 0x90 - }; - - if( resolved_var->attr & packed_no_sign_e ) - { - // This is packed decimal without a sign nybble - retval = 0; - for(size_t i=0; i<resolved_var->capacity; i++) - { - retval *= 100; - retval += dp2bin[resolved_location[i]]; - } - } - else - { - // This is packed decimal with a final sign nybble - retval = 0; - size_t imputed_length = (resolved_var->digits + 2)/2; - for(size_t i=0; i<imputed_length-1; i++) - { - retval *= 100; - retval += dp2bin[resolved_location[i]]; - } - retval *= 10; - retval += resolved_location[imputed_length-1]>>4; - if( (resolved_location[imputed_length-1]&0x0F) == 0x0D - || (resolved_location[imputed_length-1]&0x0F) == 0x0B ) - { - retval = -retval; - } - } - *rdigits = resolved_var->rdigits; + *rdigits = resolved_var->rdigits; + retval = __gg__packed_to_binary(resolved_location, + resolved_length); break; } } diff --git a/libgcobol/stringbin.cc b/libgcobol/stringbin.cc index 2e88cdf1fb5e..63976cf4964d 100644 --- a/libgcobol/stringbin.cc +++ b/libgcobol/stringbin.cc @@ -476,121 +476,6 @@ __gg__binary_to_packed( unsigned char *result, memcpy(result, combined_string, length); } - -unsigned __int128 -int_from_string(const char *psz, int n) - { - /* This is a generalized converter */ - - - /* We are assuming that 64-bit arithmetic is faster than 128-bit arithmetic, - and so we build up a 128-bit result in three 64-bit pieces, and assemble - them at the end. */ - - static const uint8_t lookup[] = - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,0,0,0,0,0, - 10,11,12,13,14,15,16,17,18,19, 0,0,0,0,0,0, - 20,21,22,23,24,25,26,27,28,29, 0,0,0,0,0,0, - 30,31,32,33,34,35,36,37,38,39, 0,0,0,0,0,0, - 40,41,42,43,44,45,46,47,48,49, 0,0,0,0,0,0, - 50,51,52,53,54,55,56,57,58,59, 0,0,0,0,0,0, - 60,61,62,63,64,65,66,67,68,69, 0,0,0,0,0,0, - 70,71,72,73,74,75,76,77,78,79, 0,0,0,0,0,0, - 80,81,82,83,84,85,86,87,88,89, 0,0,0,0,0,0, - 90,91,92,93,94,95,96,97,98,99, 0,0,0,0,0,0, - }; - - unsigned __int128 retval; - - uint64_t top = 0; - uint64_t middle = 0; - uint64_t bottom = 0; - - int count_bottom; - int count_middle; - int count_top; - - // Digits 1 through 18 come from the bottom: - if( n <= 18 ) - { - count_bottom = n; - count_middle = 0; - count_top = 0; - } - else if( n<= 36 ) - { - count_bottom = 18; - count_middle = n - 18; - count_top = 0; - } - else - { - count_bottom = 18; - count_middle = 18; - count_top = n - 36; - } - - if( n & 1 ) - { - // We are dealing with an odd number of digits - if( count_top ) - { - top = *psz++ & 0x0f; - count_top -= 1; - } - else if( count_middle ) - { - middle = *psz++ & 0x0f; - count_middle -= 1; - } - else - { - bottom = *psz++ & 0x0f; - count_bottom -= 1; - } - } - - uint8_t add_me; - - while( count_top ) - { - add_me = *psz++ << 4; - add_me += *psz++ & 0xF; - top *= 100 ; - top += lookup[add_me]; - count_top -= 2; - } - - while( count_middle ) - { - add_me = *psz++ << 4; - add_me += *psz++ & 0xF; - middle *= 100 ; - middle += lookup[add_me]; - count_middle -= 2; - } - - while( count_bottom ) - { - add_me = *psz++ << 4; - add_me += *psz++ & 0xF; - bottom *= 100 ; - bottom += lookup[add_me]; - count_bottom -= 2; - } - - retval = top; - retval *= 1000000000000000000ULL; // 10E18 - - retval += middle; - retval *= 1000000000000000000ULL; - - retval += bottom; - - return retval; - } - extern "C" __int128 __gg__numeric_display_to_binary(unsigned char *signp, @@ -811,6 +696,114 @@ __gg__numeric_display_to_binary(unsigned char *signp, return retval; } +extern "C" +__int128 +__gg__packed_to_binary(const unsigned char *psz, + int nplaces ) + { + // See the comments in __gg__numeric_display_to_binary() above. + + __int128 retval = 0; + + static const unsigned char dp2bin[160] = + { + // This may not be the weirdest table I've ever created, but it is + // certainly a contender. Given the packed decimal byte 0x23, it + // returns the equivalent decimal value of 23. Note that the final + // entries in each line are intended to handle the final place of + // signed values. 0x2D, for example, gets picked up as 20. + 00, 01, 02, 03, 04, 05, 06, 07, 8, 9, 0, 0, 0, 0, 0, 0, // 0x00 + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 10, 10, 10, 10, 10, // 0x10 + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 20, 20, 20, 20, 20, 20, // 0x20 + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 30, 30, 30, 30, 30, 30, // 0x30 + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 40, 40, 40, 40, 40, 40, // 0x40 + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 50, 50, 50, 50, 50, 50, // 0x50 + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 60, 60, 60, 60, 60, 60, // 0x60 + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 70, 70, 70, 70, 70, 70, // 0x70 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 80, 80, 80, 80, 80, 80, // 0x80 + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 90, 90, 90, 90, 90, 90, // 0x90 + }; + + uint64_t top = 0; + uint64_t middle = 0; + uint64_t bottom = 0; + + int count_bottom; + int count_middle; + int count_top; + + // Turn places into n digits + int n = nplaces * 2; + + // Digits 1 through 18 come from the bottom: + if( n <= 18 ) + { + count_bottom = n; + count_middle = 0; + count_top = 0; + } + else if( n<= 36 ) + { + count_bottom = 18; + count_middle = n - 18; + count_top = 0; + } + else + { + count_bottom = 18; + count_middle = 18; + count_top = n - 36; + } + + while( count_top ) + { + top *= 100 ; + top += dp2bin[*psz++]; + count_top -= 2; + } + + while( count_middle ) + { + middle *= 100 ; + middle += dp2bin[*psz++]; + count_middle -= 2; + } + + while( count_bottom ) + { + bottom *= 100 ; + bottom += dp2bin[*psz++]; + count_bottom -= 2; + } + + retval = top; + retval *= 1000000000000000000ULL; // 10E18 + + retval += middle; + retval *= 1000000000000000000ULL; + + retval += bottom; + + // retval is now the binary value of the packed decimal number. + + // back up one byte to fetch the sign nybble. + uint8_t sign_nybble = *(psz-1) & 0x0F; + + if( sign_nybble > 9 ) + { + // There is a sign nybble. We have to divide the result by ten to offset + // left shift due place taken up by the sign nybble. + retval /= 10; + + if( sign_nybble == PACKED_NYBBLE_MINUS ) + { + retval = -retval ; + } + } + + return retval; + } + diff --git a/libgcobol/stringbin.h b/libgcobol/stringbin.h index db1ec9dde6a2..de003e79054b 100644 --- a/libgcobol/stringbin.h +++ b/libgcobol/stringbin.h @@ -49,4 +49,9 @@ __int128 __gg__numeric_display_to_binary( unsigned char *sign_byte, const unsigned char *digits, int ndigits ); +extern "C" +__int128 +__gg__packed_to_binary(const unsigned char *psz, + int nplaces ); + #endif