| /* |
| * Copyright (c) 2021-2026 Symas Corporation |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of the Symas Corporation nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "cobol-system.h" |
| |
| #include "coretypes.h" |
| #include "tree.h" |
| #include "tree-iterator.h" |
| #include "stringpool.h" |
| #include "diagnostic-core.h" |
| #include "target.h" |
| |
| #include "../../libgcobol/ec.h" |
| #include "../../libgcobol/common-defs.h" |
| #include "util.h" |
| #include "cbldiag.h" |
| #include "symbols.h" |
| #include "gengen.h" |
| #include "inspect.h" |
| #include "../../libgcobol/io.h" |
| #include "genapi.h" |
| #include "genutil.h" |
| #include "genmath.h" |
| #include "structs.h" |
| #include "../../libgcobol/gcobolio.h" |
| #include "../../libgcobol/charmaps.h" |
| #include "../../libgcobol/valconv.h" |
| #include "show_parse.h" |
| #include "fold-const.h" |
| #include "realmpfr.h" |
| #include "compare.h" |
| |
| static cbl_figconst_t |
| is_figconst_t(const cbl_field_t *field) |
| { |
| cbl_figconst_t figconst = (cbl_figconst_t)(field->attr & FIGCONST_MASK); |
| return figconst; |
| } |
| |
| static cbl_figconst_t |
| is_figconst(const cbl_refer_t &sourceref) |
| { |
| return is_figconst_t(sourceref.field); |
| } |
| |
| static int |
| digits_to_bytes(int digits) |
| { |
| int retval; |
| if( digits <= 2 ) |
| { |
| retval = 1; |
| } |
| else if( digits <= 4 ) |
| { |
| retval = 2; |
| } |
| else if( digits <= 9 ) |
| { |
| retval = 4; |
| } |
| else if( digits <= 18 ) |
| { |
| retval = 8; |
| } |
| else |
| { |
| retval = 16; |
| } |
| return retval; |
| } |
| |
| static tree |
| get_reference_to_data(cbl_field_t *field) |
| { |
| // Given a field, we can derive the type of data the field needs to provide. |
| // That field has a field->data_decl_node, which is the starting point for |
| // the reference to the data we calculate. |
| tree retval = NULL_TREE; |
| tree field_type = data_decl_type_for(field); |
| tree data_type = TREE_TYPE(field->data_decl_node); |
| bool field_is_array = TREE_CODE(field_type) == ARRAY_TYPE; |
| bool data_is_array = TREE_CODE(data_type) == ARRAY_TYPE; |
| |
| int field_code = TREE_CODE(field_type); |
| int data_code = TREE_CODE(data_type); |
| size_t field_size = TREE_INT_CST_LOW(TYPE_SIZE_UNIT(field_type)); |
| size_t data_size = TREE_INT_CST_LOW(TYPE_SIZE_UNIT(data_type)); |
| |
| if( field_code == data_code && field_size == data_size ) |
| { |
| if( !field_is_array ) |
| { |
| // The two types are the same and are not ARRAY_TYPE |
| if( field->offset == 0 ) |
| { |
| // This is an "ah, that feels good" moment. Getting here means the |
| // field is something like "77 foo pic 9999" and that means the |
| // data_decl_node is exactly what is needed. |
| retval = field->data_decl_node; |
| } |
| else |
| { |
| // We have an offset. |
| if( (field->offset % field_size) == 0 ) |
| { |
| // The offset is an integer number of bytes from data_decl_node: |
| size_t index = field->offset % field_size; |
| retval = gg_indirect( gg_cast(build_pointer_type(data_type), |
| gg_get_address_of(field->data_decl_node)), |
| build_int_cst_type(SIZE_T, index)); |
| } |
| else |
| { |
| // The offset is some random number of bytes. We need to do a |
| // retval = *(data_type *)((char *)&data_decl_node + offset) |
| tree base = gg_get_address_of(field->data_decl_node); |
| base = gg_cast(UCHAR_P, base); |
| base = gg_add(base, build_int_cst_type(SIZE_T, field->offset)); |
| retval = gg_cast(field_type, gg_indirect(base)); |
| } |
| } |
| } |
| else |
| { |
| // The two types are the same ARRAY_TYPE |
| retval = gg_cast(UCHAR_P, gg_pointer_to_array(field->data_decl_node)); |
| if( field->offset ) |
| { |
| retval = gg_add(retval, build_int_cst_type(SIZE_T, field->offset)); |
| } |
| } |
| } |
| else if( field_is_array && data_is_array ) |
| { |
| // We have two different array types |
| retval = gg_cast(UCHAR_P, gg_pointer_to_array(field->data_decl_node)); |
| if( field->offset ) |
| { |
| retval = gg_add(retval, build_int_cst_type(SIZE_T, field->offset)); |
| } |
| } |
| else if( !field_is_array && !data_is_array ) |
| { |
| // The two data types are different, and neither is an array |
| if( field->offset == 0 ) |
| { |
| if( field_size == data_size ) |
| { |
| // The offset is zero, and the sizes are the same. |
| // This must be something like REDEFINES or the like: |
| retval = gg_cast(field_type, field->data_decl_node); |
| } |
| else |
| { |
| // The sizes are different: |
| // retval = *(data_type *)((char *)&data_decl_node) |
| tree base = gg_get_address_of(field->data_decl_node); |
| retval = gg_indirect(gg_cast(build_pointer_type(field_type), base)); |
| } |
| } |
| else |
| { |
| // There is an offset |
| tree base = gg_get_address_of(field->data_decl_node); |
| base = gg_cast(UCHAR_P, base); |
| base = gg_add(base, build_int_cst_type(SIZE_T, field->offset)); |
| retval = gg_indirect(gg_cast(build_pointer_type(field_type), base)); |
| } |
| } |
| else if( !field_is_array && data_is_array ) |
| { |
| // The return is a scalar, but we start from an array. |
| tree base = gg_pointer_to_array(field->data_decl_node); |
| base = gg_cast(UCHAR_P, base); |
| if( field->offset ) |
| { |
| base = gg_add(base, build_int_cst_type(SIZE_T, field->offset)); |
| } |
| base = gg_cast(build_pointer_type(field_type), base); |
| retval = gg_indirect(base); |
| } |
| else // if( field_is_array !data_is_array ) |
| { |
| // The return is an array, but we start from a scalar |
| tree base = gg_get_address_of(field->data_decl_node); |
| base = gg_cast(UCHAR_P, base); |
| if( field->offset ) |
| { |
| base = gg_add(base, build_int_cst_type(SIZE_T, field->offset)); |
| } |
| retval = base; |
| } |
| |
| return retval; |
| } |
| |
| static void |
| conditional_abs(tree source, const cbl_field_t *field) |
| { |
| Analyze(); |
| if( !(field->attr & signable_e) ) |
| { |
| gg_assign(source, gg_abs(source)); |
| } |
| } |
| |
| static tree |
| get_literalN_value(cbl_field_t *var) |
| { |
| // Get the literal N value from the integer var_decl |
| tree retval = NULL_TREE; |
| tree var_type = tree_type_from_size(var->data.capacity(), |
| var->attr & signable_e); |
| retval = gg_cast(var_type, var->data_decl_node); |
| return retval; |
| } |
| |
| static size_t |
| get_bytes_needed(cbl_field_t *field) |
| { |
| size_t retval = 0; |
| switch(field->type) |
| { |
| case FldIndex: |
| case FldPointer: |
| case FldFloat: |
| case FldLiteralN: |
| retval = field->data.capacity(); |
| break; |
| |
| case FldNumericDisplay: |
| { |
| int digits; |
| if( field->attr & scaled_e && field->data.rdigits<0) |
| { |
| digits = field->data.digits + -field->data.rdigits; |
| } |
| else |
| { |
| digits = field->data.digits; |
| } |
| retval = digits_to_bytes(digits); |
| break; |
| } |
| |
| case FldPacked: |
| { |
| int digits; |
| if( field->attr & scaled_e && field->data.rdigits<0) |
| { |
| digits = field->data.digits + -field->data.rdigits; |
| } |
| else |
| { |
| digits = field->data.digits; |
| } |
| if( !(field->attr & separate_e) ) |
| { |
| // This is COMP-3, so there is a sign nybble. |
| digits += 1; |
| } |
| retval = (digits+1)/2; |
| break; |
| } |
| |
| case FldNumericBinary: |
| case FldNumericBin5: |
| { |
| if( field->data.digits ) |
| { |
| int digits; |
| if( field->attr & scaled_e && field->data.rdigits<0) |
| { |
| digits = field->data.digits + -field->data.rdigits; |
| } |
| else |
| { |
| digits = field->data.digits; |
| } |
| retval = digits_to_bytes(digits); |
| } |
| else |
| { |
| retval = field->data.capacity(); |
| } |
| break; |
| } |
| |
| default: |
| cbl_internal_error("%s: Knows not the variable type %s for %s", |
| __func__, |
| cbl_field_type_str(field->type), |
| field->name ); |
| break; |
| } |
| return retval; |
| } |
| |
| static void |
| get_binary_value_from_float(tree value, |
| const cbl_refer_t &dest, |
| cbl_field_t *source, |
| tree source_offset |
| ) |
| { |
| // The destination is something with rdigits; the source is FldFloat |
| tree ftype; |
| switch( source->data.capacity() ) |
| { |
| case 4: |
| ftype = FLOAT; |
| break; |
| case 8: |
| ftype = DOUBLE; |
| break; |
| case 16: |
| ftype = FLOAT128; |
| break; |
| default: |
| gcc_unreachable(); |
| break; |
| } |
| tree fvalue = gg_define_variable(ftype); |
| gg_assign(fvalue, |
| gg_indirect(gg_cast(build_pointer_type(ftype), |
| gg_add( member(source->var_decl_node,"data"), |
| source_offset)))); |
| |
| // We need to convert the floating point value to an integer value with the |
| // rdigits lined up properly. |
| |
| int rdigits = get_scaled_rdigits( dest.field ); |
| gg_assign(fvalue, |
| gg_multiply(fvalue, |
| gg_float(ftype, |
| 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 principle. I am deferring this problem until |
| // I understand it better. |
| |
| // We now have a floating point value that has been multiplied by 10**rdigits |
| gg_assign(value, gg_trunc(TREE_TYPE(value), fvalue)); |
| } |
| |
| static bool |
| mh_identical(const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref) |
| { |
| // Check to see if the two variables are identical types, thus allowing |
| // for a simple byte-for-byte copy of the data areas: |
| bool moved = false; |
| if( destref.field->type == sourceref.field->type |
| && destref.field->data.capacity() == sourceref.field->data.capacity() |
| && destref.field->data.digits == sourceref.field->data.digits |
| && destref.field->data.rdigits == sourceref.field->data.rdigits |
| && (destref.field->attr & (signable_e|separate_e|leading_e)) |
| == (sourceref.field->attr & (signable_e|separate_e|leading_e)) |
| && destref.field->codeset.encoding == sourceref.field->codeset.encoding |
| ) |
| { |
| // The source and destination are identical in type and the |
| // Source doesn't have a depending_on clause |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("mh_identical()"); |
| } |
| if( refer_is_super_clean(destref) |
| && refer_is_super_clean(sourceref) ) |
| { |
| // They are identical, and they have no subscripts |
| |
| tree source = get_reference_to_data(sourceref.field); |
| tree dest = get_reference_to_data(destref.field); |
| |
| tree type = data_decl_type_for(destref.field); |
| if( TREE_CODE(type) == ARRAY_TYPE ) |
| { |
| // We are dealing with pointers to UCHAR. |
| // The move has to be done with a copy: |
| gg_memcpy(dest, |
| source, |
| build_int_cst_type(SIZE_T, |
| destref.field->data.capacity())); |
| } |
| else |
| { |
| // We are dealing with scalars |
| gg_assign(dest, source); |
| } |
| moved = true; |
| } |
| } |
| return moved; |
| } |
| |
| static bool |
| mh_source_is_literalN(cbl_refer_t &destref, |
| cbl_refer_t &sourceref, |
| bool check_for_error, |
| cbl_round_t rounded, |
| tree size_error) |
| { |
| bool moved = false; |
| if( sourceref.field->type == FldLiteralN ) |
| { |
| Analyze(); |
| switch( destref.field->type ) |
| { |
| case FldGroup: |
| case FldAlphanumeric: |
| { |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("mh_source_is_literalN: __gg__psz_to_alpha_move") |
| } |
| |
| // In accordance with the rules of moving a numeric to an alphabetic, |
| // we need to eliminate any leading sign character from the text |
| // string: |
| |
| const char *original = sourceref.field->data.original(); |
| if( *original == ascii_plus || *original == ascii_minus ) |
| { |
| original += 1; |
| } |
| |
| // We need the data sent to __gg__psz_to_alpha_move to be in the |
| // encoding of the destination. In accordance with the rules of |
| // cbl_field_t::internalize, the FldLiteralN is in source-code |
| // encoding, so we have to convert. |
| |
| size_t charsout; |
| const char *converted = __gg__iconverter( |
| DEFAULT_SOURCE_ENCODING, |
| destref.field->codeset.encoding, |
| original, |
| strlen(original), |
| &charsout); |
| gg_call(VOID, |
| "__gg__psz_to_alpha_move", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| refer_size_dest(destref), |
| build_string_literal(charsout, converted), |
| build_int_cst_type(SIZE_T, charsout), |
| NULL_TREE); |
| moved = true; |
| break; |
| } |
| |
| case FldPointer: |
| case FldIndex: |
| { |
| // We know this is a move to an eight-byte value: |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("mh_source_is_literalN: pointer/index") |
| } |
| |
| if( sourceref.field->data.capacity() < 8 ) |
| { |
| // There are too few bytes in sourceref |
| if( sourceref.field->attr & signable_e ) |
| { |
| static tree highbyte = gg_define_variable(UCHAR, "..mh_litN_highbyte", vs_file_static); |
| // Pick up the source byte that has the sign bit. |
| gg_assign(highbyte, |
| gg_get_indirect_reference(gg_add(member(sourceref.field->var_decl_node, |
| "data"), |
| build_int_cst_type(SIZE_T, |
| sourceref.field->data.capacity()-1)), |
| integer_zero_node)); |
| IF( gg_bitwise_and(highbyte, build_int_cst_type(UCHAR, 0x80)), |
| eq_op, |
| build_int_cst_type(UCHAR, 0x80) ) |
| { |
| // We are dealing with a negative number |
| gg_memset(gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)), |
| build_int_cst_type(UCHAR, 0xFF), |
| build_int_cst_type(SIZE_T, 8)); |
| } |
| ELSE |
| gg_memset(gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)), |
| build_int_cst_type(UCHAR, 0x00), |
| build_int_cst_type(SIZE_T, 8)); |
| ENDIF |
| } |
| else |
| { |
| // The too-short source is positive. |
| gg_memset(gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)), |
| build_int_cst_type(UCHAR, 0x00), |
| build_int_cst_type(SIZE_T, 8)); |
| } |
| } |
| |
| tree literalN_value = get_literalN_value(sourceref.field); |
| scale_by_power_of_ten_N(literalN_value, -sourceref.field->data.rdigits); |
| gg_memcpy(gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)), |
| gg_get_address_of(literalN_value), |
| build_int_cst_type(SIZE_T, sourceref.field->data.capacity())); |
| moved = true; |
| |
| break; |
| } |
| |
| case FldNumericBin5: |
| { |
| // We are moving from a FldLiteralN (which we know has no subscripts or |
| // refmods), to a NumericBin5, which might. |
| |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("mh_source_is_literalN: FldNumericBin5") |
| } |
| |
| // For now, we are ignoring intermediates: |
| assert( !(destref.field->attr & intermediate_e) ); |
| |
| int bytes_needed = std::max(destref.field->data.capacity(), |
| sourceref.field->data.capacity()); |
| tree calc_type = tree_type_from_size(bytes_needed, |
| sourceref.field->attr & signable_e); |
| tree dest_type = tree_type_from_size( destref.field->data.capacity(), |
| destref.field->attr & signable_e); |
| |
| // Pick up the source data. |
| tree source = gg_define_variable(calc_type); |
| gg_assign(source, gg_cast(calc_type, sourceref.field->data_decl_node)); |
| |
| // Take the absolute value, if the destination is not signable |
| conditional_abs(source, destref.field); |
| |
| // See if it needs to be scaled: |
| scale_by_power_of_ten_N( |
| source, |
| destref.field->data.rdigits-sourceref.field->data.rdigits); |
| |
| if( check_for_error && size_error ) |
| { |
| Analyzer.Message("Check to see if result fits"); |
| if( destref.field->data.digits ) |
| { |
| 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)); |
| } |
| ELSE |
| ENDIF |
| } |
| } |
| |
| Analyzer.Message("Move to destination location"); |
| tree dest_location = gg_indirect( |
| gg_cast(build_pointer_type(dest_type), |
| gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)))); |
| gg_assign(dest_location, gg_cast(dest_type, source)); |
| moved = true; |
| break; |
| } |
| |
| case FldNumericDisplay: |
| case FldNumericBinary: |
| case FldNumericEdited: |
| case FldPacked: |
| { |
| static tree berror = gg_define_variable(INT, "..mh_litN_berror", vs_file_static); |
| gg_assign(berror, integer_zero_node); |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("calling get_literalN_value ") |
| } |
| tree literalN_value = get_literalN_value(sourceref.field); |
| |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("calling __gg__int128_to_qualified_field ") |
| } |
| |
| gg_call(INT, |
| "__gg__int128_to_qualified_field", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| refer_size_dest(destref), |
| gg_cast(INT128, literalN_value), |
| build_int_cst_type(INT, sourceref.field->data.rdigits), |
| build_int_cst_type(INT, rounded), |
| gg_get_address_of(berror), |
| NULL_TREE); |
| |
| if( size_error ) |
| { |
| IF( berror, ne_op, integer_zero_node ) |
| { |
| gg_assign(size_error, gg_bitwise_or(size_error, integer_one_node)); |
| } |
| ELSE |
| ENDIF |
| } |
| moved = true; |
| break; |
| } |
| |
| case FldAlphaEdited: |
| { |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT(" FldAlphaEdited") |
| } |
| |
| // __gg__string_to_alpha_edited expects the source string to be in |
| // the same encoding as the target. The rule in internalize is that |
| // a FldLiteralN::data.initial is left in source-code space, so it |
| // needs to be converted to the destination encoding. |
| size_t charsout; |
| const char *converted_ = __gg__iconverter( |
| DEFAULT_SOURCE_ENCODING, |
| destref.field->codeset.encoding, |
| sourceref.field->data.original(), |
| strlen(sourceref.field->data.original()), |
| &charsout); |
| // Copy converted, because __gg__string_to_alpha_edited might have its |
| // own reasons to use charmap_t, which could mess up the static buffer |
| // used by __gg__iconverter: |
| char *converted = xstrdup(converted_); |
| gg_call(VOID, |
| "__gg__string_to_alpha_edited", |
| gg_add( member(destref.field->var_decl_node, "data"), |
| refer_offset(destref) ), |
| build_int_cst_type(INT, destref.field->codeset.encoding), |
| gg_string_literal(converted), |
| build_int_cst_type(INT, strlen(converted)), |
| gg_string_literal(destref.field->data.picture), |
| NULL_TREE); |
| moved = true; |
| free(converted); |
| break; |
| } |
| |
| case FldFloat: |
| { |
| tree tdest = gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref) ); |
| switch( destref.field->data.capacity() ) |
| { |
| case 4: |
| { |
| // The following generated code is the exact equivalent |
| // of the C code: |
| // *(float *)dest = (float)data.value |
| gg_assign(gg_indirect(gg_cast(build_pointer_type(FLOAT), tdest)), |
| gg_cast (FLOAT, sourceref.field->data.value_of())); |
| break; |
| } |
| case 8: |
| { |
| gg_assign(gg_indirect(gg_cast(build_pointer_type(DOUBLE), tdest)), |
| gg_cast (DOUBLE, sourceref.field->data.value_of())); |
| break; |
| } |
| case 16: |
| { |
| gg_assign(gg_indirect(gg_cast(build_pointer_type(FLOAT128), tdest)), |
| sourceref.field->data.value_of()); |
| break; |
| } |
| } |
| moved=true; |
| break; |
| } |
| |
| default: |
| cbl_internal_error( |
| "In %<parser_move(%s to %s)%>, the move of FldLiteralN to %s " |
| "is unimplemented", |
| sourceref.field->name, |
| destref.field->name, |
| cbl_field_type_str(destref.field->type)); |
| break; |
| } |
| } |
| return moved; |
| } |
| |
| static |
| tree float_type_of(int n) |
| { |
| switch(n) |
| { |
| case 4: |
| return FLOAT; |
| case 8: |
| return DOUBLE; |
| case 16: |
| return FLOAT128; |
| default: |
| gcc_unreachable(); |
| } |
| return NULL_TREE; |
| } |
| |
| static tree |
| float_type_of(const cbl_field_t *field) |
| { |
| gcc_assert(field->type == FldFloat); |
| return float_type_of(field->data.capacity()); |
| } |
| |
| static tree |
| float_type_of(const cbl_refer_t *refer) |
| { |
| return float_type_of(refer->field); |
| } |
| |
| static bool |
| mh_dest_is_float( cbl_refer_t &destref, |
| cbl_refer_t &sourceref, |
| TREEPLET &tsource, |
| cbl_round_t rounded, |
| tree size_error) // int |
| { |
| bool moved = false; |
| if( destref.field->type == FldFloat ) |
| { |
| Analyze(); |
| switch( sourceref.field->type ) |
| { |
| case FldPointer: |
| case FldIndex: |
| case FldNumericBin5: |
| case FldNumericDisplay: |
| case FldNumericBinary: |
| case FldNumericEdited: |
| case FldPacked: |
| { |
| switch( destref.field->data.capacity() ) |
| { |
| case 4: |
| gg_call(VOID, |
| "__gg__float32_from_int128", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| build_int_cst_type(INT, rounded), |
| size_error ? gg_get_address_of(size_error) : null_pointer_node, |
| NULL_TREE); |
| break; |
| case 8: |
| gg_call(VOID, |
| "__gg__float64_from_int128", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| build_int_cst_type(INT, rounded), |
| size_error ? gg_get_address_of(size_error) : null_pointer_node, |
| NULL_TREE); |
| break; |
| case 16: |
| gg_call(VOID, |
| "__gg__float128_from_int128", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| build_int_cst_type(INT, rounded), |
| size_error ? gg_get_address_of(size_error) : null_pointer_node, |
| NULL_TREE); |
| break; |
| } |
| moved = true; |
| break; |
| } |
| |
| case FldFloat: |
| { |
| // We are testing for size. First, we need to check to see if the |
| // source is INFINITY. If so, that's an automatic size error |
| |
| IF( gg_call_expr( INT, |
| "__gg__is_float_infinite", |
| tsource.pfield, |
| tsource.offset, |
| NULL_TREE), |
| ne_op, |
| integer_zero_node ) |
| { |
| if( size_error ) |
| { |
| gg_assign(size_error, integer_one_node ); |
| } |
| } |
| ELSE |
| { |
| // The source isn't infinite. |
| // If the destination is bigger than the source, then we can |
| // do an untested move: |
| |
| if( destref.field->data.capacity() >= sourceref.field->data.capacity() ) |
| { |
| tree dtype = float_type_of(&destref); |
| tree stype = float_type_of(&sourceref); |
| |
| tree tdest = gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)); |
| tree source = gg_add(member(sourceref.field->var_decl_node, "data"), |
| refer_offset(sourceref)); |
| gg_assign(gg_indirect(gg_cast(build_pointer_type(dtype), tdest)), |
| gg_cast(dtype, |
| gg_indirect(gg_cast(build_pointer_type(stype), |
| source)))); |
| } |
| else |
| { |
| // There are only three possible moves left: |
| if(destref.field->data.capacity() == 8 ) |
| { |
| if( size_error ) |
| { |
| gg_assign(size_error, |
| gg_call_expr( INT, |
| "__gg__float64_from_128", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| NULL_TREE)); |
| } |
| else |
| { |
| gg_call( INT, |
| "__gg__float64_from_128", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| NULL_TREE); |
| } |
| } |
| else |
| { |
| // The destination has to be float32 |
| if( sourceref.field->data.capacity() == 8 ) |
| { |
| if( size_error ) |
| { |
| gg_assign(size_error, |
| gg_call_expr( INT, |
| "__gg__float32_from_64", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| NULL_TREE)); |
| } |
| else |
| { |
| gg_call( INT, |
| "__gg__float32_from_64", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| NULL_TREE); |
| } |
| |
| } |
| else |
| { |
| if( size_error ) |
| { |
| gg_assign(size_error, |
| gg_call_expr( INT, |
| "__gg__float32_from_128", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| NULL_TREE)); |
| } |
| else |
| { |
| gg_call( INT, |
| "__gg__float32_from_128", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| tsource.pfield, |
| tsource.offset, |
| NULL_TREE); |
| } |
| } |
| } |
| } |
| } |
| ENDIF |
| |
| moved = true; |
| break; |
| } |
| |
| case FldLiteralA: |
| case FldAlphanumeric: |
| case FldGroup: |
| { |
| // Alphanumeric to float is inherently slow. Send it off to the library |
| break; |
| } |
| |
| default: |
| cbl_internal_error("In %<mh_dest_is_float%>(%s to %s), the " |
| "move of %s to %s is unimplemented", |
| sourceref.field->name, |
| destref.field->name, |
| cbl_field_type_str(sourceref.field->type), |
| cbl_field_type_str(destref.field->type)); |
| break; |
| } |
| } |
| return moved; |
| } |
| |
| static void |
| picky_memset(tree &dest_p, unsigned char value, size_t length) |
| { |
| if( length ) |
| { |
| tree dest_ep = gg_define_variable(TREE_TYPE(dest_p)); |
| gg_assign(dest_ep, |
| gg_add( dest_p, |
| build_int_cst_type(SIZE_T, length))); |
| WHILE( dest_p, lt_op, dest_ep ) |
| { |
| gg_assign(gg_indirect(dest_p), |
| build_int_cst_type(UCHAR, value)); |
| gg_increment(dest_p); |
| } |
| WEND |
| } |
| } |
| |
| static void |
| picky_memcpy(tree &dest_p, const tree &source_p, size_t length, tree zero) |
| { |
| // This is the routine that copies digits for NumericDisplay. In addition |
| // to just moving digits from source to destination, it has to handle |
| // clearing up embedded sign information. |
| if( length ) |
| { |
| tree dest_ep = gg_define_variable(TREE_TYPE(dest_p)); |
| gg_assign(dest_ep, |
| gg_add( dest_p, |
| build_int_cst_type(SIZE_T, length))); |
| WHILE( dest_p, lt_op, dest_ep ) |
| { |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(zero, |
| gg_bitwise_and(gg_indirect(source_p), |
| build_int_cst_type(UCHAR, 0x0F)))); |
| gg_increment(dest_p); |
| gg_increment(source_p); |
| } |
| WEND |
| } |
| } |
| |
| static bool |
| mh_numeric_display( const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref, |
| const TREEPLET &tsource, |
| tree size_error) |
| { |
| bool moved = false; |
| |
| charmap_t *charmap_source = |
| __gg__get_charmap(sourceref.field->codeset.encoding); |
| if( destref.field->type == FldNumericDisplay |
| && sourceref.field->type == FldNumericDisplay |
| && !(destref.field->attr & scaled_e) |
| && !(sourceref.field->attr & scaled_e) |
| && charmap_source->stride() == 1 |
| && sourceref.field->codeset.encoding == destref.field->codeset.encoding |
| ) |
| { |
| // We can do simple moves of single-byte same-encoding numeric display. |
| // More complex ones get sent to __gg__move |
| |
| Analyze(); |
| // I believe that there are 450 pathways through the following code. |
| // That's because there are five different valid combination of signable_e, |
| // separate_e, and leading_e. There are three possibilities for |
| // sender/receiver rdigits (too many, too few, and just right), and the |
| // same for ldigits. 5 * 5 * 3 * 3 * 2 = 450. |
| |
| // Fasten your seat belts. |
| |
| // This routine is complicated by the fact that although I had several |
| // false starts of putting this into libgcobol, I keep coming back to the |
| // fact that assignment of zoned values is common. And, so, there are all |
| // kinds of things that are known at compile time that would turn into |
| // execution-time decisions if I moved them to the library. So, complex |
| // or not, I am doing all this code here at compile time because it will |
| // minimize the code at execution time. |
| |
| // One thing to keep in mind is the problem caused by a source value being |
| // internally signed. That turns an ASCII "123" into "12t", and we |
| // very probably don't want that "t" to find its way into the destination |
| // value. The internal sign characteristic of ASCII is that the high |
| // nybble of the sign location is 0x30 or 0x70. For EBCDIC, the high |
| // nybble is 0xC0 for positive values, and 0xD0 for negative; all other |
| // digits are 0x70. |
| |
| charmap_t *charmap_dest = |
| __gg__get_charmap( destref.field->codeset.encoding); |
| |
| static tree source_sign_loc = gg_define_variable(UCHAR_P, |
| "..mhnd_sign_loc", |
| vs_file_static); |
| static tree dest_sign_loc = gg_define_variable(UCHAR_P, |
| "..mhnd_dest_sign_loc", |
| vs_file_static); |
| static tree source_sign = gg_define_variable(INT, |
| "..mhnd_sign", |
| vs_file_static); |
| // The destination data pointer |
| static tree dest_p = gg_define_variable( UCHAR_P, |
| "..mhnd_dest", |
| vs_file_static); |
| // The source data pointer |
| static tree source_p = gg_define_variable( UCHAR_P, |
| "..mhnd_source", |
| vs_file_static); |
| // When we need an end pointer |
| static tree source_ep = gg_define_variable( UCHAR_P, |
| "..mhnd_source_e", |
| vs_file_static); |
| |
| bool source_is_signable = sourceref.field->attr & signable_e; |
| bool source_is_leading = sourceref.field->attr & leading_e; |
| bool source_is_separate = sourceref.field->attr & separate_e; |
| |
| bool dest_is_signable = destref.field->attr & signable_e; |
| bool dest_is_leading = destref.field->attr & leading_e; |
| bool dest_is_separate = destref.field->attr & separate_e; |
| |
| int switch_source = (source_is_signable ? 4 : 0 ) |
| + (source_is_leading ? 2 : 0 ) |
| + (source_is_separate ? 1 : 0 ) ; |
| |
| int switch_dest = (dest_is_signable ? 4 : 0 ) |
| + (dest_is_leading ? 2 : 0 ) |
| + (dest_is_separate ? 1 : 0 ) ; |
| |
| // Calculate the start of the source data: |
| gg_assign(source_p, gg_add(member(sourceref.field, "data"), |
| tsource.offset)); |
| |
| // Calculate the start of the destination data |
| gg_assign(dest_p, qualified_data_location(destref)); |
| |
| // Figure out exactly where the sign is, if any, and where the input |
| // digits are. |
| |
| switch( switch_source ) |
| { |
| case 0: |
| case 1: |
| case 2: |
| case 3: |
| // not signable |
| gg_assign(source_sign, integer_zero_node); |
| break; |
| case 4: |
| // signable, not leading, not separate |
| // Calculate location of the sign byte; it's the last byte of the data |
| gg_assign(source_sign_loc, |
| gg_add(source_p, |
| build_int_cst_type(SIZE_T, |
| sourceref.field->data.capacity()-1))); |
| break; |
| case 5: |
| // signable, not leading, separate |
| // Calculate location of the sign byte; it's the last byte of the data |
| gg_assign(source_sign_loc, |
| gg_add(source_p, |
| build_int_cst_type(SIZE_T, |
| sourceref.field->data.capacity()-1))); |
| break; |
| case 6: |
| // signable, leading, not separate |
| // Calculate location of the sign byte; it's the first byte of the data |
| gg_assign(source_sign_loc, source_p); |
| break; |
| case 7: |
| // signable, leading, separate |
| // Calculate location of the sign byte; it's the first byte of the data |
| gg_assign(source_sign_loc, source_p); |
| gg_increment(source_p); |
| break; |
| } |
| // At this point, the source sign is at source_sign_loc, and the digits |
| // start at source_p |
| |
| // Let's learn what the source sign is |
| if( source_is_signable && source_is_separate ) |
| { |
| IF( gg_indirect(source_sign_loc), |
| eq_op, |
| build_int_cst_type(UCHAR, |
| charmap_source->mapped_character(ascii_minus)) ) |
| { |
| // Flag the source as negative |
| gg_assign(source_sign, integer_one_node); |
| } |
| ELSE |
| { |
| // Flag the source as positive |
| gg_assign(source_sign, integer_zero_node); |
| } |
| ENDIF |
| } |
| if( source_is_signable && !source_is_separate ) |
| { |
| // We need to look for an indication that we are internally signed. We |
| // can tell that by checking to see if the digit is between '0' and '9' |
| IF( gg_indirect(source_sign_loc), |
| lt_op, |
| build_int_cst_type(UCHAR, |
| charmap_source->mapped_character(ascii_0)) ) |
| { |
| // The sign byte is less than '0', so we are negative |
| gg_assign(source_sign, integer_one_node); |
| } |
| ELSE |
| { |
| IF( gg_indirect(source_sign_loc), |
| gt_op, |
| build_int_cst_type(UCHAR, |
| charmap_source->mapped_character(ascii_9)) ) |
| { |
| // The sign byte is greater than '9', so we are negative |
| gg_assign(source_sign, integer_one_node); |
| } |
| ELSE |
| { |
| // The sign byte is betwixt '0' and '9', so we are positive |
| gg_assign(source_sign, integer_zero_node); |
| } |
| ENDIF |
| } |
| ENDIF |
| } |
| |
| // We now know the source's sign, and where its digits are. |
| |
| // The first order of business is to move the digits into place. To do |
| // that, we need to know where things go in the destination: |
| |
| switch( switch_dest ) |
| { |
| case 0: |
| case 1: |
| case 2: |
| case 3: |
| // not signable |
| break; |
| case 4: |
| // signable, not leading, not separate |
| // Calculate location of the sign byte; it's the last byte of the data |
| gg_assign(dest_sign_loc, |
| gg_add(dest_p, |
| build_int_cst_type(SIZE_T, |
| destref.field->data.capacity()-1))); |
| break; |
| case 5: |
| // signable, not leading, separate |
| // Calculate location of the sign byte; it's the last byte of the data |
| gg_assign(dest_sign_loc, |
| gg_add(dest_p, |
| build_int_cst_type(SIZE_T, |
| destref.field->data.capacity()-1))); |
| break; |
| case 6: |
| // signable, leading, not separate |
| // Calculate location of the sign byte; it's the first byte of the data |
| gg_assign(dest_sign_loc, dest_p); |
| break; |
| case 7: |
| // signable, leading, separate |
| // Calculate location of the sign byte; it's the first byte of the data |
| gg_assign(dest_sign_loc, dest_p); |
| gg_increment(dest_p); |
| break; |
| } |
| |
| // We can now start copying the digits to the left of the decimal place |
| |
| int dest_ldigits = (int)destref.field->data.digits |
| - destref.field->data.rdigits; |
| int source_ldigits = (int)sourceref.field->data.digits |
| - sourceref.field->data.rdigits; |
| |
| int digit_count = 0; |
| |
| if( dest_ldigits > source_ldigits ) |
| { |
| // The destination has more ldigits than the source, and needs some |
| // leading zeroes: |
| picky_memset( dest_p, |
| charmap_dest->mapped_character(ascii_0) , |
| dest_ldigits - source_ldigits); |
| // With the leading zeros set, set the number of ldigits to copy: |
| digit_count = source_ldigits; |
| } |
| else if( dest_ldigits == source_ldigits ) |
| { |
| // This is the Goldilocks zone. Everything is *just* right. |
| digit_count = dest_ldigits; |
| } |
| else // dest_ldigits < source_ldigits |
| { |
| // The destination is smaller than the source. We have to throw away the |
| // the high-order digits of the source. If any of them are non-zero, then |
| // we need to indicate a size error. |
| gg_assign(source_ep, |
| gg_add( source_p, |
| build_int_cst_type( SIZE_T, |
| source_ldigits-dest_ldigits))); |
| WHILE(source_p, lt_op, source_ep) |
| { |
| if( size_error ) |
| { |
| IF( gg_indirect(source_p), |
| ne_op, |
| build_int_cst_type( UCHAR, |
| charmap_source->mapped_character(ascii_0)) ) |
| { |
| set_exception_code(ec_size_truncation_e); |
| gg_assign(size_error, integer_one_node); |
| } |
| ELSE |
| ENDIF |
| } |
| gg_increment(source_p); |
| } |
| WEND |
| |
| // Having skipped over the leading digits, we are in position to move the |
| // remaining digits |
| digit_count = dest_ldigits; |
| } |
| // We now have digit_count, which will cover the ldigits. Augment it by |
| // the number of rdigits: |
| |
| int dest_rdigits = destref.field->data.rdigits; |
| int source_rdigits = sourceref.field->data.rdigits; |
| |
| int trailing_zeros = 0; |
| |
| if( dest_rdigits > source_rdigits ) |
| { |
| // The destination has more rdigits than the source |
| |
| // Copy over the available digits: |
| digit_count += source_rdigits; |
| |
| // And then tack on the needed trailing zeroes: |
| trailing_zeros = dest_rdigits - source_rdigits; |
| } |
| else if( dest_rdigits == source_rdigits ) |
| { |
| // This is the Goldilocks zone. Everything is *just* right. |
| digit_count += dest_rdigits; |
| } |
| else |
| { |
| // The destination has fewer rdigits than the source. We send |
| // over only the necessary rdigits, discarding the ones to the right. |
| digit_count += dest_rdigits; |
| } |
| picky_memcpy(dest_p, |
| source_p, |
| digit_count, |
| build_int_cst_type(UCHAR, |
| charmap_dest->mapped_character(ascii_0))); |
| picky_memset( dest_p, |
| charmap_dest->mapped_character(ascii_0), |
| trailing_zeros); |
| |
| // With the digits in place, the only thing left is to establish the sign |
| |
| switch( switch_dest ) |
| { |
| case 0: |
| case 1: |
| case 2: |
| case 3: |
| // not signable, so there is nothing to do. |
| break; |
| case 4: |
| case 6: |
| // signable, not leading, not separate |
| if( charmap_dest->is_like_ebcdic() ) |
| { |
| IF( source_sign, ne_op, integer_zero_node ) |
| { |
| // It's negative ebcdic, so we have to turn the bit off. |
| gg_assign(gg_indirect(dest_sign_loc), |
| gg_bitwise_and(gg_indirect(dest_sign_loc), |
| build_int_cst_type(UCHAR, |
| ~NUMERIC_DISPLAY_SIGN_BIT_EBCDIC))); |
| } |
| ELSE |
| { |
| } |
| ENDIF |
| } |
| else |
| { |
| IF( source_sign, ne_op, integer_zero_node ) |
| { |
| // It's negative ascii, so we have to turn the bit on. |
| gg_assign(gg_indirect(dest_sign_loc), |
| gg_bitwise_or(gg_indirect(dest_sign_loc), |
| build_int_cst_type(UCHAR, |
| NUMERIC_DISPLAY_SIGN_BIT_ASCII))); |
| } |
| ELSE |
| { |
| } |
| ENDIF |
| } |
| break; |
| case 5: |
| case 7: |
| // signable, not leading, separate |
| // signable, leading, separate |
| // Calculate location of the sign byte; it's the last byte of the data |
| |
| IF( source_sign, eq_op, integer_zero_node ) |
| { |
| gg_assign(gg_indirect(dest_sign_loc), |
| build_int_cst_type(UCHAR, |
| charmap_dest->mapped_character(ascii_plus))); |
| } |
| ELSE |
| { |
| gg_assign(gg_indirect(dest_sign_loc), |
| build_int_cst_type(UCHAR, |
| charmap_dest->mapped_character(ascii_minus))); |
| } |
| ENDIF |
| break; |
| } |
| moved = true; |
| } |
| return moved; |
| } //NUMERIC_DISPLAY_SIGN |
| |
| static bool |
| mh_little_endian( const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref, |
| const TREEPLET &tsource, |
| bool check_for_error, |
| tree size_error) |
| { |
| bool moved = false; |
| |
| cbl_figconst_t figconst = cbl_figconst_of( sourceref.field->data.original()); |
| |
| if( !figconst |
| && !(destref.field->attr & scaled_e) |
| && !(destref.field->attr & (intermediate_e )) |
| && !(sourceref.field->attr & (intermediate_e )) |
| && sourceref.field->type != FldGroup |
| && sourceref.field->type != FldLiteralA |
| && sourceref.field->type != FldAlphanumeric |
| && sourceref.field->type != FldNumericEdited |
| && sourceref.field->type != FldPacked |
| && ( destref.field->type == FldNumericBin5 |
| || destref.field->type == FldPointer |
| || destref.field->type == FldIndex ) ) |
| { |
| Analyze(); |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("mh_little_endian") |
| SHOW_PARSE_END |
| } |
| |
| int bytes_needed = get_bytes_needed(sourceref.field); |
| tree source_type = tree_type_from_size(bytes_needed, |
| sourceref.field->attr |
| & signable_e) ; |
| tree source = gg_define_variable(source_type); |
| |
| if( sourceref.field->type == FldFloat ) |
| { |
| get_binary_value_from_float(source, |
| destref, |
| sourceref.field, |
| tsource.offset); |
| |
| // Get binary value from float actually scales the source value to the |
| // dest:: rdigits |
| copy_little_endian_into_place(destref.field, |
| refer_offset(destref), |
| source, |
| destref.field->data.rdigits, |
| check_for_error, |
| size_error); |
| moved = true; |
| } |
| else |
| { |
| get_binary_value( source, |
| NULL, |
| sourceref.field, |
| tsource.offset); |
| copy_little_endian_into_place(destref.field, |
| refer_offset(destref), |
| source, |
| sourceref.field->data.rdigits, |
| check_for_error, |
| size_error); |
| moved = true; |
| } |
| } |
| return moved; |
| } |
| |
| static bool |
| mh_source_is_group( const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref, |
| const TREEPLET &tsrc) |
| { |
| bool retval = false; |
| charmap_t *charmap = __gg__get_charmap(destref.field->codeset.encoding); |
| if( sourceref.field->type == FldGroup && !(destref.field->attr & rjust_e) |
| && sourceref.field->codeset.encoding == destref.field->codeset.encoding |
| && charmap->stride() == 1) |
| { |
| Analyze(); |
| // We are moving a group to a something. The rule here is just move as |
| // many bytes as you can, and, if necessary, fill with spaces |
| tree tdest = gg_add( member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)); |
| tree tsource = gg_add( member(sourceref.field->var_decl_node, "data"), |
| tsrc.offset); |
| tree dbytes = refer_size_dest(destref); |
| tree sbytes = tsrc.length; |
| |
| IF( sbytes, ge_op, gg_cast(TREE_TYPE(sbytes), dbytes) ) |
| { |
| // There are too many source bytes |
| gg_memcpy(tdest, tsource, dbytes); |
| } |
| ELSE |
| { |
| // There are too few source bytes: |
| int dest_space = charmap->mapped_character(ascii_space); |
| gg_memset(tdest, build_int_cst_type(INT, dest_space), dbytes); |
| gg_memcpy(tdest, tsource, sbytes); |
| } |
| ENDIF |
| retval = true; |
| } |
| return retval; |
| } |
| |
| static bool |
| mh_source_is_literalA(const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref, |
| cbl_round_t rounded, |
| tree size_error) |
| { |
| bool moved = false; |
| if( sourceref.field->type == FldLiteralA ) |
| { |
| // We are moving a literal somewhere. Because a program-id can take |
| // variables of ANY LENGTH, we don't know the length of the target |
| // variable. We do, however, know its encoding. So, we are going to |
| // construct a string with the same number of characters as the source, but |
| // in the target variable's encoding. |
| |
| // We will then call a library routine that will be in charge of run-time |
| // trimming or space filling, as necessary. |
| |
| cbl_encoding_t encoding_dest = destref.field->codeset.encoding; |
| charmap_t *charmap_dest = __gg__get_charmap(encoding_dest); |
| |
| static char *buffer = NULL; |
| static size_t buffer_size = 0; |
| size_t source_length; |
| size_t dest_length; |
| if( sourceref.field->attr & hex_encoded_e ) |
| { |
| // Hex-encoded data is moved as-is |
| source_length = sourceref.field->data.capacity(); |
| dest_length = std::min(source_length, |
| static_cast<size_t>(destref.field->data.capacity())); |
| } |
| else |
| { |
| // Otherwise, data.initial prevails: |
| size_t source_based_on_strlen = strlen(sourceref.field->data.original()); |
| size_t source_based_on_capacity = sourceref.field->data.capacity() / |
| sourceref.field->codeset.stride() ; |
| source_length = std::max( source_based_on_strlen , |
| source_based_on_capacity ); |
| dest_length = source_length * charmap_dest->stride(); |
| } |
| |
| if( buffer_size < dest_length ) |
| { |
| buffer_size = dest_length; |
| buffer = static_cast<char *>(xrealloc(buffer, buffer_size)); |
| } |
| gcc_assert(buffer); |
| |
| cbl_figconst_t figconst = cbl_figconst_of( sourceref.field->data.original()); |
| size_t outlength; |
| if( figconst ) |
| { |
| // We are going to fill 'buffer' with a solid run of the figurative |
| // constant in the destination codeset. |
| char const_char = 0x7F; // Head off a compiler warning about |
| // // uninitialized variables |
| switch(figconst) |
| { |
| case normal_value_e : |
| // This is not possible, it says here in the fine print. |
| gcc_unreachable(); |
| break; |
| case low_value_e : |
| const_char = charmap_dest->low_value_character(); |
| break; |
| case zero_value_e : |
| const_char = charmap_dest->mapped_character(ascii_zero); |
| break; |
| case space_value_e : |
| const_char = charmap_dest->mapped_character(ascii_space); |
| break; |
| case quote_value_e : |
| const_char = charmap_dest->quote_character(); |
| break; |
| case high_value_e : |
| const_char = charmap_dest->high_value_character(); |
| break; |
| case null_value_e: |
| const_char = 0x00; |
| break; |
| } |
| memset(buffer, const_char, source_length); |
| } |
| else |
| { |
| if( sourceref.field->attr & hex_encoded_e ) |
| { |
| // hex_encoded data goes as is: |
| memcpy(buffer, sourceref.field->data.original(), dest_length); |
| outlength = dest_length; |
| } |
| else |
| { |
| // We are going to convert the source string to the destination |
| // codeset, and then copy it to 'buffer', trimming if necessary, and |
| // space-filling to the right if necessary: |
| const char *source_string = |
| __gg__iconverter( |
| sourceref.field->codeset.default_encodings.source->type, |
| encoding_dest, |
| sourceref.field->data.original(), |
| source_length, |
| &outlength ); |
| if( outlength > dest_length ) |
| { |
| outlength = dest_length; |
| } |
| // Copy over the converted string |
| memcpy( buffer, |
| source_string, |
| outlength ); |
| } |
| } |
| |
| // Check to see if we can do a simple alphanumeric-to-alphanumeric move |
| if( ( destref.field->type == FldAlphanumeric |
| || destref.field->type == FldGroup ) |
| && !(destref.field->attr & any_length_e) |
| && !sourceref.all |
| && !size_error) |
| { |
| // A simple alpha-to-alpha move is possible |
| size_t dest_bytes = destref.field->data.capacity(); |
| // We have 'outlength' bytes in 'buffer' that need to go to |
| // destref.field->data.capacity() bytes at destref.field->data. |
| char *src = static_cast<char *>(xmalloc(dest_bytes)); |
| size_t src_bytes = std::min(outlength, dest_bytes); |
| charmap_t *charmap = __gg__get_charmap(destref.field->codeset.encoding); |
| charmap->memset(src, charmap->mapped_character(ascii_space), dest_bytes); |
| |
| if( destref.field->attr & rjust_e ) |
| { |
| size_t fill = 0; |
| if( src_bytes < dest_bytes ) |
| { |
| fill = dest_bytes - src_bytes; |
| } |
| memcpy(src+fill, buffer+outlength-src_bytes, src_bytes); |
| } |
| else |
| { |
| memcpy(src, buffer, src_bytes); |
| } |
| // src is now the desired string, space-filled if necessary on the right, |
| // (or on the left, for rjust_e destinations). |
| |
| if( refer_is_clean(destref) ) |
| { |
| gg_memcpy(member(destref.field->var_decl_node, "data"), |
| build_string_literal(dest_bytes, src), |
| build_int_cst_type(SIZE_T, dest_bytes)); |
| } |
| else |
| { |
| // The refer has some information in it. |
| gg_memcpy(gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)), |
| build_string_literal(dest_bytes, src), |
| refer_size_dest(destref)); |
| } |
| free(src); |
| } |
| else |
| { |
| // This is more complicated than a simple alpha-to-alpha move |
| if( destref.refmod.from |
| || destref.refmod.len ) |
| { |
| // Let the move routine know to treat the destination as alphanumeric |
| attribute_bit_set(destref.field, refmod_e); |
| } |
| // If the source is flagged ALL, or if we are setting the destination to |
| // a figurative constant, pass along the ALL bit: |
| int rounded_parameter = rounded |
| | ((sourceref.all || figconst ) ? REFER_ALL_BIT : 0); |
| |
| if( size_error ) |
| { |
| gg_assign(size_error, |
| gg_call_expr( INT, |
| "__gg__move_literala", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| refer_size_dest(destref), |
| build_int_cst_type(INT, rounded_parameter), |
| build_string_literal(outlength, |
| buffer), |
| build_int_cst_type( SIZE_T, outlength), |
| NULL_TREE)); |
| } |
| else |
| { |
| gg_call ( INT, |
| "__gg__move_literala", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| refer_size_dest(destref), |
| build_int_cst_type(INT, rounded_parameter), |
| build_string_literal(outlength, |
| buffer), |
| build_int_cst_type( SIZE_T, outlength), |
| NULL_TREE); |
| } |
| if( destref.refmod.from |
| || destref.refmod.len ) |
| { |
| // Return that value to its original form |
| attribute_bit_clear(destref.field, refmod_e); |
| } |
| } |
| |
| moved = true; |
| } |
| return moved; |
| } |
| |
| static bool |
| have_common_parent(const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref) |
| { |
| /* We are trying to lay down fast code when possible. But sometimes we have |
| to go slower in order to be accurate. The COBOL specification explicitly |
| says that when the storage areas of sending and receiving operands |
| overlap: |
| 1) When the data items are not described by the same data description |
| entry, the result of the statement is undefined. |
| 2) When the data items are described by the same data description entry, |
| the result of the statement is the same as if the data items shared |
| no part of their respective storage areas. |
| |
| There is an additional paragraph: |
| In the case of reference modification, the unique data item produced by |
| reference modification is not considered to be the same data description |
| entry as any other data description entry. Therefore, if an overlapping |
| situation exists, the results of the operation are undefined. |
| |
| This routine will return TRUE when neither reference is a refmod, and |
| both operands ultimately have the same parent (indicating that they are |
| part of the same data description. |
| |
| The point is that when we return True, then the two are not refmods, and |
| they have a common parent, so we have to use a memmove. When we return |
| False, then we can use a faster memcpy. |
| */ |
| bool retval = true; |
| if( destref.is_refmod_reference() ) |
| { |
| retval = false; |
| } |
| else if( sourceref.is_refmod_reference() ) |
| { |
| retval = false; |
| } |
| else |
| { |
| // Neither is a refmod. Check for common parentage: |
| const cbl_field_t *poppa = destref.field; |
| const cbl_field_t *momma = sourceref.field; |
| while( parent_of(poppa) ) |
| { |
| // Follow the first family_tree up as far as we can. |
| poppa = parent_of(poppa); |
| } |
| while( parent_of(momma) ) |
| { |
| // Follow the second family_tree up as far as we can. |
| momma = parent_of(momma); |
| } |
| if( poppa != momma ) |
| { |
| /* Okay, so the analogy breaks down. Think of momma and poppa as |
| bacteria, or something. */ |
| retval = false; |
| } |
| } |
| |
| return retval; |
| } |
| |
| static bool |
| mh_alpha_to_alpha(const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref, |
| cbl_round_t /*rounded*/, |
| tree size_error) |
| { |
| bool moved = false; |
| // If a bunch of conditions are met, we can do a move without resorting to |
| // the library. |
| if( sourceref.field->type == FldAlphanumeric |
| && destref.field->type == FldAlphanumeric |
| && !size_error |
| && sourceref.field->codeset.encoding == destref.field->codeset.encoding |
| && !(destref.field->attr & rjust_e) |
| && !(sourceref.field->attr & any_length_e) |
| && !(destref.field->attr & any_length_e) |
| && !(sourceref.field->attr & intermediate_e) |
| && !sourceref.all |
| ) |
| { |
| void (*mover)(tree, tree, tree); // dest, source, count |
| mover = have_common_parent(destref, sourceref) ? gg_memmove : gg_memcpy; |
| |
| // We are in a position to simply move bytes from the source to the dest. |
| if( refer_is_clean(sourceref) && refer_is_clean(destref) ) |
| { |
| // Source and destination are both clean |
| if( destref.field->data.capacity() <= sourceref.field->data.capacity() ) |
| { |
| // This is the simplest case of all |
| mover(member( destref.field->var_decl_node, "data"), |
| member(sourceref.field->var_decl_node, "data"), |
| build_int_cst_type(SIZE_T, destref.field->data.capacity())); |
| moved = true; |
| } |
| else |
| { |
| // This is a tad more complicated. The source is too short, so we need |
| // to copy over what we can... |
| mover(member( destref.field->var_decl_node, "data"), |
| member(sourceref.field->var_decl_node, "data"), |
| build_int_cst_type(SIZE_T, sourceref.field->data.capacity())); |
| // And then space-fill the rest: |
| size_t fill_bytes = |
| destref.field->data.capacity() - sourceref.field->data.capacity(); |
| |
| // ...and then create a memory area with the fill spaces... |
| char *spaces = static_cast<char *>(xmalloc(fill_bytes)); |
| charmap_t *charmap =__gg__get_charmap(destref.field->codeset.encoding); |
| charmap->memset(spaces, |
| charmap->mapped_character(ascii_space), |
| fill_bytes); |
| // ...and then copy those spaces into place. |
| mover( |
| gg_add(member(destref.field->var_decl_node, "data"), |
| build_int_cst_type(SIZE_T, sourceref.field->data.capacity())), |
| build_string_literal(fill_bytes, spaces), |
| build_int_cst_type(SIZE_T, fill_bytes)); |
| free(spaces); |
| moved = true; |
| } |
| } |
| |
| if( !refer_is_clean(sourceref) && refer_is_clean(destref) ) |
| { |
| // The source is dirty, but the destination is clean: |
| tree source_data; |
| tree source_len; |
| |
| tree dest_data; |
| tree dest_len; |
| |
| source_data = gg_add(member(sourceref.field->var_decl_node, "data"), |
| refer_offset(sourceref)); |
| source_len = refer_size_source(sourceref); |
| |
| dest_data = member(destref.field->var_decl_node, "data"); |
| |
| dest_len = build_int_cst_type(SIZE_T, destref.field->data.capacity()); |
| IF( source_len, ge_op, dest_len ) |
| { |
| // The source has enough (or more) bytes to fill the destination: |
| mover(dest_data, source_data, dest_len); |
| } |
| ELSE |
| { |
| // The source data is too short. We need to copy over what we have... |
| mover(dest_data, source_data, source_len); |
| |
| // And then right-fill the remainder with spaces. Create a buffer with |
| // more than enough spaces for our purposes: |
| size_t fill_bytes = destref.field->data.capacity(); |
| char *spaces = static_cast<char *>(xmalloc(fill_bytes)); |
| charmap_t *charmap =__gg__get_charmap(destref.field->codeset.encoding); |
| charmap->memset(spaces, |
| charmap->mapped_character(ascii_space), |
| fill_bytes); |
| // And then copy enough of those spaces into place. |
| mover(gg_add(dest_data, source_len), |
| build_string_literal(fill_bytes, spaces), |
| gg_subtract(dest_len, source_len)); |
| free(spaces); |
| } |
| ENDIF |
| moved = true; |
| } |
| if( refer_is_clean(sourceref) && !refer_is_clean(destref) ) |
| { |
| // The source is clean but the destination is dirty: |
| tree source_data; |
| tree source_len; |
| |
| tree dest_data; |
| tree dest_len ; |
| |
| source_data = member(sourceref.field->var_decl_node, "data"); |
| source_len = build_int_cst_type(SIZE_T, |
| sourceref.field->data.capacity()); |
| dest_data = gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref)); |
| dest_len = refer_size_dest(destref); |
| IF( source_len, ge_op, dest_len ) |
| { |
| // The source has enough (or more) bytes to fill the destination: |
| mover(dest_data, source_data, dest_len); |
| } |
| ELSE |
| { |
| // The source data is too short. We need to copy over what we have... |
| mover(dest_data, source_data, source_len); |
| |
| // And then right-fill the remainder with spaces. Create a buffer with |
| // more than enough spaces for our purposes: |
| size_t fill_bytes = destref.field->data.capacity(); |
| char *spaces = static_cast<char *>(xmalloc(fill_bytes)); |
| charmap_t *charmap =__gg__get_charmap(destref.field->codeset.encoding); |
| charmap->memset(spaces, |
| charmap->mapped_character(ascii_space), |
| fill_bytes); |
| // And then copy enough of those spaces into place. |
| mover(gg_add(dest_data, source_len), |
| build_string_literal(fill_bytes, spaces), |
| gg_subtract(dest_len, source_len)); |
| free(spaces); |
| } |
| ENDIF |
| |
| moved = true; |
| } |
| if( !refer_is_clean(sourceref) && !refer_is_clean(destref) ) |
| { |
| // Both the source and the dest are "dirty" |
| tree source_data = gg_define_variable(UCHAR_P); |
| tree source_len = gg_define_variable(SIZE_T); |
| |
| tree dest_data = gg_define_variable(UCHAR_P); |
| tree dest_len = gg_define_variable(SIZE_T); |
| |
| gg_assign(source_data, |
| gg_add(member(sourceref.field->var_decl_node, "data"), |
| refer_offset(sourceref))); |
| gg_assign(source_len, refer_size_source(sourceref)); |
| |
| gg_assign(dest_data, |
| gg_add(member(destref.field->var_decl_node, "data"), |
| refer_offset(destref))); |
| gg_assign(dest_len, refer_size_dest(destref)); |
| IF( source_len, ge_op, dest_len ) |
| { |
| // The source has enough (or more) bytes to fill the destination: |
| mover(dest_data, source_data, dest_len); |
| } |
| ELSE |
| { |
| // The source data is too short. We need to copy over what we have... |
| mover(dest_data, source_data, source_len); |
| |
| // And then right-fill the remainder with spaces. Create a buffer with |
| // more than enough spaces for our purposes: |
| size_t fill_bytes = destref.field->data.capacity(); |
| char *spaces = static_cast<char *>(xmalloc(fill_bytes)); |
| charmap_t *charmap =__gg__get_charmap(destref.field->codeset.encoding); |
| charmap->memset(spaces, |
| charmap->mapped_character(ascii_space), |
| fill_bytes); |
| // And then copy enough of those spaces into place. |
| mover(gg_add(dest_data, source_len), |
| build_string_literal(fill_bytes, spaces), |
| gg_subtract(dest_len, source_len)); |
| free(spaces); |
| } |
| ENDIF |
| |
| moved = true; |
| } |
| } |
| return moved; |
| } |
| |
| static bool |
| mh_numdisp_to_packed(const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref); |
| |
| static bool |
| mh_packed_to_packed(const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref) |
| { |
| if( (destref.field->type != FldPacked ) |
| || (sourceref.field->type != FldPacked ) |
| || (destref.field->attr & scaled_e ) |
| || (sourceref.field->attr & scaled_e ) |
| || (destref.field->attr & packed_no_sign_e ) |
| || (sourceref.field->attr & packed_no_sign_e ) ) |
| { |
| return false; |
| } |
| // Arriving here means both are packed, neither is scaled, and neither is |
| // COMP-6 or PACKED NO SIGN. |
| |
| // We are going to move source to the dest doing the absolute minimum number |
| // of operations. We are thus going to use memcpy (with constant lengths) |
| // as much as we can, and use conditionals and nybble operations as little |
| // little as possible. |
| |
| // There are two broad cases. The more straightforward case is where source |
| // rdigits and dest rdigits are both even, or both odd. When that is the |
| // case, the source and destination decimal places are "in phase" somewhere |
| // inside both the dest and the source. Once we figure out the right |
| // offsets, we can memcpy the "inside" of the source to the correct location |
| // in the dest. We fiddle with the leading digits, the trailing digits, and |
| // the sign nybble as necessary. |
| |
| tree source_location = gg_define_variable(UCHAR_P); |
| tree dest_location = gg_define_variable(UCHAR_P); |
| tree source_sign = gg_define_variable(UCHAR_P); |
| tree dest_sign = gg_define_variable(UCHAR_P); |
| tree temp; |
| |
| get_location(temp, destref); |
| gg_assign(dest_location, temp); |
| |
| get_location(temp, sourceref); |
| gg_assign(source_location, temp); |
| |
| int source_digits = sourceref.field->data.digits; |
| int source_rdigits = sourceref.field->data.rdigits; |
| size_t source_capacity = source_digits/2 + 1; |
| if( ((destref.field->data.rdigits ^ source_rdigits) & 1) ) |
| { |
| /* This is an "out-of-phase" move, e.g., MOVE 999v99 to 999v9. The code |
| below handles in-phase moves, so we handle this by making a left-shifted |
| copy of the source side. By left-shifting it one nybble, incrementing |
| the source_rdigits, and changing the location to the shifted version, we |
| turn the out-of-phase problem into an in-phase problem. */ |
| size_t shifted_size; |
| if( source_digits & 1 ) |
| { |
| // The source, plus the sign nybble, fills an even number of nybbles, and |
| // so the shift requires an addition byte on the left. |
| shifted_size = source_capacity + 1; |
| } |
| else |
| { |
| // The highest-order source nybble is a zero, so the shift will fill it |
| // without any additional storage needed. |
| shifted_size = source_capacity; |
| } |
| // Allocate storage for the shifted version: |
| tree shifted_type = build_array_type_nelts(UCHAR, shifted_size); |
| tree shifted = gg_define_variable(shifted_type); |
| TREE_ADDRESSABLE(shifted) = 1; |
| tree source_p = gg_define_variable(UCHAR_P); |
| tree shifted_p_left = gg_define_variable(UCHAR_P); |
| tree shifted_p_right = gg_define_variable(UCHAR_P); |
| tree carry = gg_define_variable(UCHAR); |
| tree carry_next = gg_define_variable(UCHAR); |
| gg_assign(source_p, |
| gg_add(source_location, |
| build_int_cst_type(SIZE_T, |
| source_capacity-1))); |
| gg_assign(shifted_p_left, gg_pointer_to_array(shifted)); |
| gg_assign(shifted_p_right, |
| gg_add(shifted_p_left, |
| build_int_cst_type(SIZE_T, shifted_size-1))); |
| // Start with the right side. |
| // Pick up the carry, which is the left side of the rightmost byte |
| gg_assign(carry, |
| gg_rshift(gg_indirect(source_p), |
| build_int_cst_type(SIZE_T, 4))); |
| // Keep the sign nybble in place, but with a zero to its left |
| gg_assign(gg_indirect(shifted_p_right), |
| gg_bitwise_and(gg_indirect(source_p), |
| build_int_cst_type(UCHAR, 0x0F))); |
| |
| gg_decrement(source_p); |
| gg_decrement(shifted_p_right); |
| WHILE(shifted_p_right, gt_op, shifted_p_left) |
| { |
| gg_assign(carry_next, |
| gg_rshift(gg_indirect(source_p), |
| build_int_cst_type(SIZE_T, 4))); |
| gg_assign(gg_indirect(shifted_p_right), |
| gg_bitwise_or(gg_lshift(gg_indirect(source_p), |
| build_int_cst_type(SIZE_T, 4)), |
| carry)); |
| gg_assign(carry, carry_next); |
| gg_decrement(source_p); |
| gg_decrement(shifted_p_right); |
| } |
| WEND |
| // At this point, shifted_p_right equals shifted_p_left |
| if( source_digits & 1 ) |
| { |
| // The source, plus the sign nybble, fills an even number of nybbles, and |
| // so the shift requires an addition byte on the left. |
| gg_assign(gg_indirect(shifted_p_left), carry); |
| } |
| else |
| { |
| // The highest-order source nybble is a zero, so the shift will fill it |
| // without any additional storage needed. |
| gg_assign(gg_indirect(shifted_p_left), |
| gg_bitwise_or(gg_lshift(gg_indirect(source_p), |
| build_int_cst_type(SIZE_T, 4)), |
| carry)); |
| } |
| |
| // We now have the left-shifted source in 'shifted'. |
| source_digits += 1; |
| source_rdigits += 1; |
| source_capacity = source_digits/2 + 1; |
| gg_assign(source_location, shifted_p_left); |
| } |
| gg_assign(source_sign, |
| gg_add(source_location, |
| build_int_cst_type(SIZE_T, |
| source_capacity-1))); |
| gg_assign(dest_sign, |
| gg_add(dest_location, |
| build_int_cst_type(SIZE_T, |
| destref.field->data.capacity()-1))); |
| |
| // This is the straightforward case, where the dest and source decimal |
| // places are in phase, which means that the middles of the values can |
| // simply be moved. |
| int dest_rbytes = destref.field->data.rdigits/2 + 1; |
| int dest_lbytes = destref.field->data.capacity() - dest_rbytes; |
| |
| int source_rbytes = source_rdigits/2 + 1; |
| int source_lbytes = source_capacity - source_rbytes; |
| |
| uint32_t dest_bytes = destref.field->data.capacity(); |
| |
| if( source_lbytes > dest_lbytes ) |
| { |
| // There are too many source lbytes. We just skip those extra bytes, |
| // truncating off those high-order digits. |
| gg_assign(source_location, |
| gg_add(source_location, |
| build_int_cst_type(SIZE_T, |
| source_lbytes-dest_lbytes))); |
| } |
| else if( source_lbytes < dest_lbytes ) |
| { |
| // There are too few source lbytes. We zero out the extra bytes on the |
| // left side of the destination. |
| gg_memset(dest_location, |
| integer_zero_node, |
| build_int_cst_type(SIZE_T, dest_lbytes-source_lbytes)); |
| gg_assign(dest_location, |
| gg_add(dest_location, |
| build_int_cst_type(SIZE_T, dest_lbytes-source_lbytes))); |
| // And reduce the total number to move by the number we zeroed: |
| dest_bytes -= dest_lbytes-source_lbytes; |
| } |
| source_lbytes = dest_lbytes; |
| // We have lined up source_location and dest_location so that the source |
| // lbytes will go into the correct location in the destination |
| |
| // We copy over as many bytes as we have in the source that can fit into |
| // the destination: |
| size_t bytes_to_copy = |
| std::min(static_cast<uint32_t>(source_lbytes) + source_rbytes, |
| dest_bytes); |
| gg_memcpy(dest_location, |
| source_location, |
| build_int_cst_type(SIZE_T, bytes_to_copy)); |
| |
| // We make sure the final sign nybble is correct. |
| |
| if( source_rbytes == dest_rbytes ) |
| { |
| // The sign nybble from the source is now in the destination. It might |
| // need to be changed |
| if( !(sourceref.field->attr & signable_e) |
| && (destref.field->attr & signable_e) ) |
| { |
| // The unsignable source has an 0xF sign nybble, so we need to turn |
| // that into an positive 0xC in the signable destination: |
| gg_assign(gg_indirect(dest_sign), |
| gg_bitwise_and(gg_indirect(dest_sign), |
| build_int_cst_type(UCHAR, 0xFC))); |
| } |
| else if( (sourceref.field->attr & signable_e) |
| && !(destref.field->attr & signable_e) ) |
| { |
| // The signable source has an 0xC or 0xD sign nybble, so we need to |
| // turn that into an 0xF in the unsignable destination: |
| gg_assign(gg_indirect(dest_sign), |
| gg_bitwise_or(gg_indirect(dest_sign), |
| build_int_cst_type(UCHAR, 0x0F))); |
| } |
| } |
| else |
| { |
| // There is mismatch between source and dest rdigits: |
| if( source_rbytes < dest_rbytes ) |
| { |
| // The source was too short to fill the destination, which means we |
| // currently have a source's sign nybble sitting in the middle of the |
| // destination. We need to zero out that nybble |
| gg_assign(gg_indirect(dest_location, |
| build_int_cst_type(SIZE_T, |
| bytes_to_copy-1)), |
| gg_bitwise_and(gg_indirect(dest_location, |
| build_int_cst_type(SIZE_T, |
| bytes_to_copy-1)), |
| build_int_cst_type(UCHAR, 0xF0))); |
| // And then we need to zero out the remaining dest_rbytes: |
| int remaining_rbytes = dest_rbytes - source_rbytes; |
| if( remaining_rbytes > 1 ) |
| { |
| gg_memset(gg_add(dest_location, |
| build_int_cst_type(SIZE_T, bytes_to_copy)), |
| integer_zero_node, |
| build_int_cst_type(SIZE_T, |
| destref.field->data.capacity() - bytes_to_copy)); |
| } |
| // And now we have to adjust the final nybble: |
| |
| if( !(sourceref.field->attr & signable_e) |
| && (destref.field->attr & signable_e) ) |
| { |
| // The source is unsignable, so we turn that into an positive 0xC in |
| // the signable destination: |
| gg_assign(gg_indirect(dest_sign), build_int_cst_type(UCHAR, 0x0C)); |
| } |
| else if( (sourceref.field->attr & signable_e) |
| && !(destref.field->attr & signable_e) ) |
| { |
| gg_assign(gg_indirect(dest_sign), build_int_cst_type(UCHAR, 0x0F)); |
| } |
| else |
| { |
| // The source and the destination are either both signable, or |
| // both unsignable. We copy the source's sign nybble to the dest. |
| gg_assign(gg_indirect(dest_sign), |
| gg_bitwise_or(gg_indirect(dest_sign), |
| gg_bitwise_and(gg_indirect(source_sign), |
| build_int_cst_type(UCHAR, |
| 0x0F)))); |
| } |
| } |
| else // source_rbytes > dest_rbytes |
| { |
| // There were more source_rbytes than we needed, which means the final |
| // nybble of the destination is a digit that needs to be truncated |
| // away and replaced with the correct sign nybble. |
| if( !(sourceref.field->attr & signable_e) |
| && (destref.field->attr & signable_e) ) |
| { |
| // The source was unsignable, so we set the sign nybble to a |
| // a positive 0x0C |
| gg_assign(gg_indirect(dest_sign), |
| gg_bitwise_or(gg_bitwise_and(gg_indirect(dest_sign), |
| build_int_cst_type(UCHAR, 0xF0)), |
| build_int_cst_type(UCHAR, 0x0C))); |
| } |
| else if( (sourceref.field->attr & signable_e) |
| && !(destref.field->attr & signable_e) ) |
| { |
| // The dest is unsignable; turn the final nybble into an 0xFo |
| gg_assign(gg_indirect(dest_sign), |
| gg_bitwise_or(gg_indirect(dest_sign), |
| build_int_cst_type(UCHAR, 0x0F))); |
| } |
| else |
| { |
| // The source and the destination are either both signable, or |
| // both unsignable. We copy the source's sign nybble to the dest. |
| gg_assign(gg_indirect(dest_sign), |
| gg_bitwise_or(gg_bitwise_and(gg_indirect(dest_sign), |
| build_int_cst_type(UCHAR, 0xF0)), |
| gg_bitwise_and(gg_indirect(source_sign), |
| build_int_cst_type(UCHAR, 0x0F)))); |
| } |
| } |
| } |
| return true; |
| } |
| |
| void |
| move_helper(tree size_error, // This is an INT |
| cbl_refer_t destref, |
| cbl_refer_t sourceref, // Call move_helper with this resolved. |
| TREEPLET &tsource, |
| cbl_round_t rounded, |
| bool check_for_error, // True means our called wants to know about truncation errors |
| bool restore_on_error |
| ) |
| { |
| Analyze(); |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("move_helper()"); |
| } |
| |
| if( size_error ) |
| { |
| gg_assign(size_error, integer_zero_node); |
| } |
| |
| static tree stash = gg_define_variable(UCHAR_P, "..mh_stash", vs_file_static); |
| |
| tree st_data = NULL_TREE; |
| tree st_size = NULL_TREE; |
| if( restore_on_error ) |
| { |
| // We are creating a copy of the original destination in case we clobber it |
| // and have to restore it because of a computational error. |
| static bool first_time = true; |
| static size_t stash_size = 1024; |
| if( first_time ) |
| { |
| first_time = false; |
| gg_assign(stash, gg_cast(UCHAR_P, gg_malloc(stash_size))); |
| } |
| if( stash_size < destref.field->data.capacity() ) |
| { |
| stash_size = destref.field->data.capacity(); |
| gg_assign(stash, gg_cast(UCHAR_P, gg_realloc(stash, stash_size))); |
| } |
| st_data = qualified_data_location(destref); |
| st_size = refer_size_dest(destref); |
| gg_memcpy(stash, |
| st_data, |
| st_size); |
| } |
| |
| bool moved = mh_source_is_group(destref, sourceref, tsource); |
| |
| if( !moved ) |
| { |
| moved = mh_identical(destref, sourceref); |
| } |
| |
| if( !moved ) |
| { |
| moved = mh_packed_to_packed(destref, |
| sourceref); |
| } |
| |
| if( !moved ) |
| { |
| moved = mh_numdisp_to_packed(destref, |
| sourceref); |
| } |
| |
| if( !moved ) |
| { |
| moved = mh_source_is_literalN(destref, |
| sourceref, |
| check_for_error, |
| rounded, |
| size_error); |
| } |
| |
| if( !moved ) |
| { |
| moved = mh_dest_is_float( destref, |
| sourceref, |
| tsource, |
| rounded, |
| size_error); |
| } |
| |
| if( !moved && rounded == truncation_e ) |
| { |
| moved = mh_numeric_display( destref, |
| sourceref, |
| tsource, |
| size_error); |
| } |
| |
| if( !moved ) |
| { |
| moved = mh_little_endian( destref, |
| sourceref, |
| tsource, |
| restore_on_error, |
| size_error); |
| } |
| |
| if( !moved ) |
| { |
| moved = mh_source_is_literalA(destref, |
| sourceref, |
| rounded, |
| size_error); |
| } |
| |
| if( !moved ) |
| { |
| moved = mh_alpha_to_alpha(destref, |
| sourceref, |
| rounded, |
| size_error); |
| } |
| |
| if( !moved ) |
| { |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_INDENT |
| SHOW_PARSE_TEXT("default __gg__move") |
| } |
| |
| if( destref.refmod.from |
| || destref.refmod.len |
| || sourceref.refmod.from |
| || sourceref.refmod.len ) |
| { |
| // Let the move routine know to treat the destination as alphanumeric |
| attribute_bit_set(destref.field, refmod_e); |
| } |
| |
| int nflags = (sourceref.all ? REFER_T_MOVE_ALL : 0) |
| + (sourceref.addr_of ? REFER_T_ADDRESS_OF : 0); |
| |
| if( size_error ) |
| { |
| gg_assign(size_error, |
| gg_call_expr( INT, |
| "__gg__move", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| refer_size_dest(destref), |
| tsource.pfield, |
| tsource.offset, |
| tsource.length, |
| build_int_cst_type(INT, nflags), |
| build_int_cst_type(INT, rounded), |
| NULL_TREE)); |
| } |
| else |
| { |
| gg_call ( INT, |
| "__gg__move", |
| gg_get_address_of(destref.field->var_decl_node), |
| refer_offset(destref), |
| refer_size_dest(destref), |
| tsource.pfield, |
| tsource.offset, |
| tsource.length, |
| build_int_cst_type(INT, nflags), |
| build_int_cst_type(INT, rounded), |
| NULL_TREE); |
| |
| } |
| if( destref.refmod.from |
| || destref.refmod.len |
| || sourceref.refmod.from |
| || sourceref.refmod.len ) |
| { |
| // Return that value to its original form |
| attribute_bit_clear(destref.field, refmod_e); |
| } |
| |
| // moved = true; // commented out to quiet cppcheck |
| } |
| |
| if( restore_on_error ) |
| { |
| IF(size_error, ne_op, integer_zero_node) |
| { |
| gg_memcpy(st_data, |
| stash, |
| st_size); |
| } |
| ELSE |
| ENDIF |
| } |
| else |
| { |
| if( check_for_error ) |
| { |
| IF(size_error, ne_op, integer_zero_node) |
| { |
| // We had a size error, but there was no restore_on_error. Pointer |
| // Let our lord and master know there was a truncation: |
| set_exception_code(ec_size_truncation_e); |
| } |
| ELSE |
| ENDIF |
| } |
| } |
| |
| SHOW_PARSE1 |
| { |
| SHOW_PARSE_END |
| } |
| } |
| |
| void |
| parser_move(cbl_refer_t destref, |
| cbl_refer_t sourceref, |
| cbl_round_t rounded, |
| bool skip_fill_from // Defaults to false |
| ) |
| { |
| Analyze(); |
| SHOW_PARSE |
| { |
| SHOW_PARSE_HEADER |
| if( sourceref.field && is_figconst_low(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" LOW-VALUE") |
| } |
| else if( sourceref.field && is_figconst_zero(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" ZERO-VALUE") |
| } |
| else if( sourceref.field && is_figconst_space(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" SPACE-VALUE") |
| } |
| else if( sourceref.field && is_figconst_quote(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" QUOTE-VALUE") |
| } |
| else if( sourceref.field && is_figconst_high(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" HIGH-VALUE") |
| } |
| else |
| { |
| SHOW_PARSE_REF(" ", sourceref) |
| } |
| SHOW_PARSE_REF(" TO ", destref) |
| switch(rounded) |
| { |
| case away_from_zero_e: |
| SHOW_PARSE_TEXT(" AWAY_FROM_ZERO") |
| break; |
| case nearest_toward_zero_e: |
| SHOW_PARSE_TEXT(" NEAREST_TOWARD_ZERO") |
| break; |
| case toward_greater_e: |
| SHOW_PARSE_TEXT(" TOWARD_GREATER") |
| break; |
| case toward_lesser_e: |
| SHOW_PARSE_TEXT(" TOWARD_LESSER") |
| break; |
| case nearest_away_from_zero_e: |
| SHOW_PARSE_TEXT(" NEAREST_AWAY_FROM_ZERO") |
| break; |
| case nearest_even_e: |
| SHOW_PARSE_TEXT(" NEAREST_EVEN") |
| break; |
| case prohibited_e: |
| SHOW_PARSE_TEXT(" PROHIBITED") |
| break; |
| case truncation_e: |
| SHOW_PARSE_TEXT(" TRUNCATED") |
| break; |
| default: |
| gcc_unreachable(); |
| break; |
| } |
| SHOW_PARSE_END |
| } |
| |
| if( !skip_fill_from ) |
| { |
| cbl_figconst_t figconst = is_figconst(sourceref); |
| if( figconst ) |
| { |
| sourceref.all = true; |
| } |
| } |
| |
| TRACE1 |
| { |
| TRACE1_HEADER |
| TRACE1_TEXT("About to call move_helper") |
| } |
| TREEPLET tsource; |
| treeplet_fill_source(tsource, sourceref); |
| static bool dont_check_for_error = false; |
| move_helper(NULL, destref, sourceref, tsource, rounded, dont_check_for_error ); |
| |
| TRACE1 |
| { |
| TRACE1_INDENT |
| TRACE1_REFER_INFO("source ", sourceref) |
| TRACE1_INDENT |
| TRACE1_REFER_INFO("dest ", destref) |
| TRACE1_END |
| } |
| } |
| |
| static |
| void |
| parser_move_multi(cbl_refer_t destref, |
| cbl_refer_t sourceref, |
| TREEPLET tsource, |
| cbl_round_t rounded, |
| bool skip_fill_from ) |
| { |
| Analyze(); |
| SHOW_PARSE |
| { |
| SHOW_PARSE_HEADER |
| if( sourceref.field && is_figconst_low(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" LOW-VALUE") |
| } |
| else if( sourceref.field && is_figconst_zero(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" ZERO-VALUE") |
| } |
| else if( sourceref.field && is_figconst_space(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" SPACE-VALUE") |
| } |
| else if( sourceref.field && is_figconst_quote(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" QUOTE-VALUE") |
| } |
| else if( sourceref.field && is_figconst_high(sourceref.field) ) |
| { |
| SHOW_PARSE_TEXT(" HIGH-VALUE") |
| } |
| else |
| { |
| SHOW_PARSE_REF(" ", sourceref) |
| } |
| SHOW_PARSE_REF(" TO ", destref) |
| switch(rounded) |
| { |
| case away_from_zero_e: |
| SHOW_PARSE_TEXT(" AWAY_FROM_ZERO") |
| break; |
| case nearest_toward_zero_e: |
| SHOW_PARSE_TEXT(" NEAREST_TOWARD_ZERO") |
| break; |
| case toward_greater_e: |
| SHOW_PARSE_TEXT(" TOWARD_GREATER") |
| break; |
| case toward_lesser_e: |
| SHOW_PARSE_TEXT(" TOWARD_LESSER") |
| break; |
| case nearest_away_from_zero_e: |
| SHOW_PARSE_TEXT(" NEAREST_AWAY_FROM_ZERO") |
| break; |
| case nearest_even_e: |
| SHOW_PARSE_TEXT(" NEAREST_EVEN") |
| break; |
| case prohibited_e: |
| SHOW_PARSE_TEXT(" PROHIBITED") |
| break; |
| case truncation_e: |
| SHOW_PARSE_TEXT(" TRUNCATED") |
| break; |
| default: |
| gcc_unreachable(); |
| break; |
| } |
| SHOW_PARSE_END |
| } |
| |
| if( !skip_fill_from ) |
| { |
| cbl_figconst_t figconst = is_figconst(sourceref); |
| if( figconst ) |
| { |
| sourceref.all = true; |
| } |
| } |
| |
| TRACE1 |
| { |
| TRACE1_HEADER |
| TRACE1_TEXT("About to call move_helper") |
| } |
| |
| static bool dont_check_for_error = false; |
| move_helper(NULL, destref, sourceref, tsource, rounded, dont_check_for_error ); |
| |
| TRACE1 |
| { |
| TRACE1_INDENT |
| TRACE1_REFER_INFO("source ", sourceref) |
| TRACE1_INDENT |
| TRACE1_REFER_INFO("dest ", destref) |
| TRACE1_END |
| } |
| } |
| |
| void |
| parser_move(size_t ntgt, cbl_refer_t *tgts, cbl_refer_t src, cbl_round_t rounded) |
| { |
| if( mode_syntax_only() ) return; |
| |
| cbl_figconst_t figconst = is_figconst(src); |
| if( figconst ) |
| { |
| src.all = true; |
| } |
| TREEPLET tsource; |
| treeplet_fill_source(tsource, src); |
| static const bool skip_fill_from = true; |
| for( cbl_refer_t *p=tgts; p < tgts + ntgt; p++ ) |
| { |
| parser_move_multi(*p, src, tsource, rounded, skip_fill_from); |
| } |
| } |
| |
| #if 0 |
| // This is a debugging function used from time-to-time |
| static void |
| hex_of(tree location, size_t bytes) |
| { |
| gg_printf("0x", NULL_TREE); |
| for(size_t i=0; i<bytes; i++) |
| { |
| gg_printf("%2.2X", gg_indirect_i(gg_cast(UCHAR_P, location), i), NULL_TREE); |
| } |
| } |
| |
| static void |
| hex_msg(const char *msg, tree location, size_t bytes) |
| { |
| gg_printf("%s ", gg_string_literal(msg), NULL_TREE); |
| hex_of(location, bytes); |
| gg_printf("\n", NULL_TREE); |
| } |
| |
| #endif |
| |
| static bool |
| mh_numdisp_to_packed(const cbl_refer_t &destref, |
| const cbl_refer_t &sourceref) |
| { |
| const charmap_t *charmap = |
| __gg__get_charmap(sourceref.field->codeset.encoding); |
| if( (destref.field->type != FldPacked ) |
| || (sourceref.field->type != FldNumericDisplay ) |
| || (charmap->stride() != 1 ) |
| || (destref.field->attr & scaled_e ) |
| || (sourceref.field->attr & scaled_e ) |
| || (destref.field->attr & packed_no_sign_e ) |
| || (sourceref.field->attr & leading_e ) |
| || (sourceref.field->attr & separate_e ) ) |
| { |
| return false; |
| } |
| /* Source is NumericDisplay, dest is packed, neither are scaled, the |
| packed destination has a sign nybble, and the numeric source has an |
| ordinarysign bit encoded in the final digit. */ |
| tree uzero = build_int_cst_type(UCHAR, 0); |
| tree umask = build_int_cst_type(UCHAR, 0x0F); |
| tree ufour = build_int_cst_type(SIZE_T, 4); |
| |
| tree source_location = gg_define_variable(UCHAR_P); |
| tree dest_location = gg_define_variable(UCHAR_P); |
| tree dest_p = gg_define_variable(UCHAR_P); |
| tree source_p = gg_define_variable(UCHAR_P); |
| |
| tree temp; |
| |
| get_location(temp, destref); |
| gg_assign(dest_location, temp); |
| gg_assign(dest_p, dest_location); |
| get_location(temp, sourceref); |
| gg_assign(source_location, temp); |
| |
| int source_digits = sourceref.field->data.digits; |
| int source_rdigits = sourceref.field->data.rdigits; |
| int source_ldigits = source_digits - source_rdigits; |
| int dest_digits = destref.field->data.digits; |
| int dest_rdigits = destref.field->data.rdigits; |
| int dest_ldigits = dest_digits - dest_rdigits; |
| |
| int truncate_ldigits = std::max(0, source_ldigits-dest_ldigits); |
| int truncate_rdigits = std::max(0, source_rdigits-dest_rdigits); |
| int leading_zeroes = std::max(0, dest_ldigits-source_ldigits); |
| int trailing_zeroes = std::max(0, dest_rdigits-source_rdigits); |
| |
| int zero_pairs; |
| int digit_pairs; |
| int source_remaining; |
| |
| if( truncate_ldigits ) |
| { |
| // We handle truncation of digits on the left by moving the starting line. |
| gg_assign(source_p, |
| gg_add(source_location, |
| build_int_cst_type(SIZE_T, truncate_ldigits))); |
| source_digits -= truncate_ldigits; |
| source_ldigits -= truncate_ldigits; |
| } |
| else |
| { |
| gg_assign(source_p, source_location); |
| } |
| |
| if( truncate_rdigits ) |
| { |
| // We handle truncation of digits on the right by moving the finish line. |
| source_digits -= truncate_rdigits; |
| source_ldigits -= truncate_rdigits; |
| } |
| |
| if( !source_digits ) |
| { |
| // When source_digits is zero, it means that some pervert of a COBOL |
| // programmer told us to MOVE 999V TO V999. The result has to be zero, |
| // and our life down below will be easier when we know that there is at |
| // least one digit that needs to be moved from the source to the |
| // destination. |
| gg_memset(dest_p, |
| integer_zero_node, |
| build_int_cst_type(SIZE_T, destref.field->data.capacity())); |
| goto adjust_sign; |
| } |
| |
| source_remaining = source_digits; |
| |
| // The first thing we need to do is adjust the first byte of the destination |
| // so that we know where we are in left-nybble/right-nybble space. Let's |
| // call the digit at source_p "N". (That digit might be a leading zero.) |
| // When dest_digits is an even number, it means the final result is something |
| // like 0N.23.4s. So, when dest_digits is even, we have to start things off |
| // with "0N". |
| |
| if( !(dest_digits & 0x01) ) |
| { |
| // dest_digits is an even number. |
| if( leading_zeroes ) |
| { |
| // The first byte is "0N", but N is zero: |
| gg_assign(gg_indirect(dest_p), uzero); |
| leading_zeroes -= 1; |
| } |
| else |
| { |
| // The first byte is "0N", where N is the value from the first character |
| // of the source. We know that source_remaining is at least one at this |
| // point. |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_and(gg_indirect(source_p), umask)); |
| gg_increment(source_p); |
| source_remaining -= 1; |
| } |
| gg_increment(dest_p); |
| } |
| |
| // At this point, we know that leading + source + trailing is an odd |
| // number. |
| |
| // We know that dest_p is set up to accept a left/right pair next. Let's |
| // see if we have enough leading_zeroes to warrant using memset: |
| zero_pairs = leading_zeroes/2; |
| if( zero_pairs ) |
| { |
| // We can use memset to handle left-side zero-fill: |
| tree tpairs = build_int_cst_type(SIZE_T, zero_pairs); |
| gg_memset(dest_p, integer_zero_node, tpairs); |
| gg_assign(dest_p, gg_add(dest_p, tpairs)); |
| leading_zeroes -= 2 * zero_pairs; |
| } |
| |
| // dest-p is still set up for a left/right pair. |
| if( leading_zeroes ) |
| { |
| // But we still have one leading zero left. We know at this point that |
| // there is at least one source digit left, so build the byte using |
| // zero/*source_p |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_and(gg_indirect(source_p), umask)); |
| //leading_zeroes -= 1; |
| source_remaining -= 1; |
| gg_increment(source_p); |
| gg_increment(dest_p); |
| } |
| |
| // At this point, we know that leading_zeroes is zero. We know that |
| // source_remaining + trailing_zeroes is an odd number. We |
| // currently have dest_p lined up on a left-right boundary. |
| |
| // We are going to transfer as many pairs of source_remaining digits as we |
| // can. |
| |
| digit_pairs = source_remaining/2; |
| if( digit_pairs ) |
| { |
| tree dest_end = gg_define_variable(UCHAR_P); |
| gg_assign(dest_end, |
| gg_add(dest_p, |
| build_int_cst_type(SIZE_T, digit_pairs))); |
| WHILE( dest_p, lt_op, dest_end ) |
| { |
| tree left_nybble = gg_lshift(gg_indirect(source_p), ufour); |
| tree right_nybble = gg_bitwise_and(gg_indirect(source_p, |
| integer_one_node), |
| umask); |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(left_nybble, right_nybble)); |
| gg_increment(dest_p); |
| gg_assign(source_p, |
| gg_add(source_p, build_int_cst_type(SIZE_T, 2))); |
| } |
| WEND |
| source_remaining -= 2 * digit_pairs; |
| } |
| |
| // At this point, source_remaining is zero or one |
| |
| if( source_remaining ) |
| { |
| gg_assign(gg_indirect(dest_p), |
| gg_lshift(gg_indirect(source_p), ufour)); |
| gg_increment(dest_p); |
| //source_remaining -= 1; |
| if( trailing_zeroes ) |
| { |
| trailing_zeroes -= 1; |
| } |
| } |
| // At this point, we know trailing_zeroes has to be an even number, and we |
| // need to zero out that many nybbles: |
| |
| if( trailing_zeroes >= 2 ) |
| { |
| zero_pairs = trailing_zeroes/2; |
| // We can use memset to handle left-side zero-fill: |
| tree tpairs = build_int_cst_type(SIZE_T, zero_pairs); |
| gg_memset(dest_p, integer_zero_node, tpairs); |
| gg_assign(dest_p, gg_add(dest_p, tpairs)); |
| trailing_zeroes -= 2 * zero_pairs; |
| } |
| |
| if( trailing_zeroes ) |
| { |
| // There is one trailing zero left |
| gg_assign(gg_indirect(dest_p), uzero); |
| gg_increment(dest_p); |
| //trailing_zeroes -= 1; |
| } |
| |
| adjust_sign: |
| gg_assign(dest_p, gg_add(dest_location, |
| build_int_cst_type(SIZE_T, |
| destref.field->data.capacity()-1))); |
| |
| if( !(destref.field->attr & signable_e) ) |
| { |
| // The destination is not signable |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(gg_indirect(dest_p), umask)); |
| } |
| else |
| { |
| if( sourceref.field->attr & signable_e ) |
| { |
| // This is the location of the character with the sign flag. |
| gg_assign(source_p, gg_add(source_location, |
| build_int_cst_type(SIZE_T, |
| sourceref.field->data.capacity()-1))); |
| if( charmap->is_like_ebcdic() ) |
| { |
| // EBCDIC digits are 0xF0 through 0xF9; negative is flagged by |
| // 0xD0 through 0xD9 |
| IF( gg_indirect(source_p), lt_op, build_int_cst_type(UCHAR, 0xF0) ) |
| { |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(gg_indirect(dest_p), |
| build_int_cst_type(UCHAR, 0x0D))); |
| } |
| ELSE |
| { |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(gg_indirect(dest_p), |
| build_int_cst_type(UCHAR, 0x0C))); |
| } |
| ENDIF |
| } |
| else |
| { |
| // EBCDIC digits are 0x30 through 0x39; negative is flagged by |
| // 0x70 through 0x79 |
| IF( gg_indirect(source_p), ge_op, build_int_cst_type(UCHAR, 0x70) ) |
| { |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(gg_indirect(dest_p), |
| build_int_cst_type(UCHAR, 0x0D))); |
| } |
| ELSE |
| { |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(gg_indirect(dest_p), |
| build_int_cst_type(UCHAR, 0x0C))); |
| } |
| ENDIF |
| } |
| } |
| else |
| { |
| gg_assign(gg_indirect(dest_p), |
| gg_bitwise_or(gg_indirect(dest_p), |
| build_int_cst_type(UCHAR, 0x0C))); |
| } |
| } |
| |
| return true; |
| } |