https://gcc.gnu.org/g:c528988b76fc277031308dba4daa30638ba0ff07
commit r17-5598-gc528988b76fc277031308dba4daa30638ba0ff07 Author: Robert Dubner <[email protected]> Date: Mon Oct 26 16:57:16 2026 -0400 cobol: Changes for GDB-COBOL functionality. Recent development, debugging, and feature expansion to the GDB-COBOL debugger required changes to the compiler. These changes primarily make the current currency sign text (which can change when one COBOL program calls another) available to the debugger. Another set of changes do something similar for field contents during stack changes during debugging. gcc/cobol/ChangeLog: * genapi.cc (enter_program_common): Save currency state on entry. (initialize_the_data): Likewise. (save_debug_field_binding): Associate field contents with a frame. (save_debug_linkage_bindings): Likewise. (establish_using): Bug fix of subordinate local storare field offsets during recursion. (parser_division): Initial currency state. (parser_set_pointers): Save debug linkage bindings. (parser_local_add): Rebase linkage offset during recursion. * gengen.h (struct gg_function_t): Linkage binding for data and capacity. * lexio.cc (cdftext::lex_open): Remove comment; adjust source format. * symbols.cc (symbol_program_callables): Make sure a recursive program can call itself. libgcobol/ChangeLog: * charmaps.cc (struct currency_state): Currency state follows its program-id during debugging. Likewise. (__gg__get_currency_state): Likewise. (__gg__activate_currency_state): Likewise. (__gg__new_currency_state): Likewise. (__gg__update_debug_currency_sign): Likewise. * charmaps.h (struct currency_state): Likewise. (__gg__new_currency_state): Likewise. (__gg__get_currency_state): Likewise. (__gg__activate_currency_state): Likewise. (__gg__update_debug_currency_sign): Likewise. * gfileio.cc (do_acu_hyphen_translation): Get rid of compiler warning. (sequential_file_write): Likewise. * libgcobol.cc (struct program_state): Currency state. (initialize_program_state): Likewise. (__gg__push_program_state): Likewise. (__gg__pop_program_state): Likewise. (__gg__push_local_variable): Likewise. (__gg__pop_local_variables): Likewise. * libgcobol.map: Likewise. * valconv.cc (__gg__currency_sign_init): Likewise. (__gg__currency_sign): Likewise. Diff: --- gcc/cobol/genapi.cc | 129 +++++++++++++++++++++++++++++++++++++++++++----- gcc/cobol/gengen.h | 9 ++++ gcc/cobol/lexio.cc | 9 +--- gcc/cobol/symbols.cc | 4 ++ libgcobol/charmaps.cc | 66 +++++++++++++++++++++++++ libgcobol/charmaps.h | 11 +++++ libgcobol/gfileio.cc | 7 ++- libgcobol/libgcobol.cc | 16 ++++-- libgcobol/libgcobol.map | 5 ++ libgcobol/valconv.cc | 10 ++-- 10 files changed, 237 insertions(+), 29 deletions(-) diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc index 98df1c4db91c..f4350c0833fe 100644 --- a/gcc/cobol/genapi.cc +++ b/gcc/cobol/genapi.cc @@ -32,6 +32,7 @@ #include "coretypes.h" #include "tree.h" +#include "options.h" #include "stor-layout.h" #include "ggc.h" #include "gtype-desc.h" @@ -3017,6 +3018,19 @@ enter_program_common(const char *funcname, const char *funcname_) vs_static, integer_one_node); + current_function->saved_currency_state = + gg_define_variable(VOID_P, "_currency_state", vs_static, + gg_cast(VOID_P, null_pointer_node)); + + // This once-written marker is read by the debugger, not the program. + // Keep its store and storage even when compiling optimized code. + current_function->saved_currency_view = + gg_define_variable(build_qualified_type(VOID_P, TYPE_QUAL_VOLATILE), + "_currency_view", vs_static, + gg_cast(VOID_P, null_pointer_node)); + TREE_THIS_VOLATILE(current_function->saved_currency_view) = 1; + TREE_SIDE_EFFECTS(current_function->saved_currency_view) = 1; + gg_create_goto_pair(¤t_function->skip_init_goto, ¤t_function->skip_init_label); @@ -6538,6 +6552,18 @@ initialize_the_data() NULL_TREE); } + // Retain this program's completed table across calls. This assignment is + // in the one-time initialization block; activation is emitted at entry. + gg_assign(current_function->saved_currency_state, + gg_call_expr(VOID_P, "__gg__get_currency_state", NULL_TREE)); + // Give the debugger this program's view without exposing runtime C++ + // layout or changing the active runtime state during frame inspection. + tree active_currency_view = gg_declare_variable( + VOID_P, "__gg__debug_currency_signs", NULL_TREE, vs_extern); + gg_assign(current_function->saved_currency_view, + gg_cast(TREE_TYPE(current_function->saved_currency_view), + active_currency_view)); + // It's time to tell the library about DECIMAL-POINT IS COMMA: if( symbol_decimal_point() == ',' ) { @@ -6563,6 +6589,56 @@ initialize_the_data() NULL_TREE); } +/* Shared descriptors describe the active invocation. Debug builds also + keep each invocation's binding in its own frame, updating it after + SET ADDRESS OF as well as entry. No stores are emitted without debug info. */ +static void +save_debug_field_binding(cbl_field_t *field) + { + if( debug_info_level == DINFO_LEVEL_NONE ) + { + return; + } + auto &binding = current_function->debug_field_bindings[field->our_index]; + if( binding.data == NULL_TREE ) + { + tree binding_type = build_qualified_type(SIZE_T, TYPE_QUAL_VOLATILE); + for( const char *member_name : {"data", "capacity"} ) + { + char name[128]; + snprintf(name, sizeof(name), "_gdb_%s_" HOST_SIZE_T_PRINT_DEC, + member_name, (fmt_size_t)field->our_index); + tree decl = gg_define_variable(binding_type, name, vs_stack); + TREE_THIS_VOLATILE(decl) = 1; + TREE_SIDE_EFFECTS(decl) = 1; + if( strcmp(member_name, "data") == 0 ) + { + binding.data = decl; + } + else + { + binding.capacity = decl; + } + } + } + gg_assign(binding.data, + gg_cast(TREE_TYPE(binding.data), member(field->var_decl_node, "data"))); + gg_assign(binding.capacity, + gg_cast(TREE_TYPE(binding.capacity), member(field->var_decl_node, "capacity"))); + } + +static void +save_debug_linkage_bindings(cbl_field_t *field) + { + save_debug_field_binding(field); + std::vector<cbl_field_t *> fields; + collect_linkage_offsets(field, member(field->var_decl_node, "data"), fields); + for( cbl_field_t *child : fields ) + { + save_debug_field_binding(child); + } + } + static void establish_using(size_t nusing, @@ -6676,19 +6752,24 @@ establish_using(size_t nusing, } } - // We need to be able to restore prior arguments when doing recursive - // calls: - IF( member(args[i].refer.field->var_decl_node, "data"), - ne_op, - gg_cast(UCHAR_P, null_pointer_node) ) + // Restore group children as well as the USING root after recursion. + std::vector<cbl_field_t *> linkage_fields; + collect_linkage_offsets(new_var, member(new_var->var_decl_node, "data"), linkage_fields); + linkage_fields.push_back(new_var); + for( cbl_field_t *field : linkage_fields ) { - gg_call(VOID, - "__gg__push_local_variable", - gg_get_address_of(args[i].refer.field->var_decl_node), - NULL_TREE); + IF( member(field->var_decl_node, "data"), + ne_op, + gg_cast(UCHAR_P, null_pointer_node) ) + { + gg_call(VOID, + "__gg__push_local_variable", + gg_get_address_of(field->var_decl_node), + NULL_TREE); + } + ELSE + ENDIF } - ELSE - ENDIF if( crv == by_reference_e ) { @@ -6815,7 +6896,7 @@ establish_using(size_t nusing, tree array_type = build_array_type_nelts(UCHAR, new_var->data.capacity()); tree data_decl_node = gg_define_variable( array_type, NULL, - vs_static); + vs_stack); gg_assign( member(new_var->var_decl_node, "data"), gg_pointer_to_array(data_decl_node) ); @@ -6871,6 +6952,7 @@ establish_using(size_t nusing, next_index += 1; } } + save_debug_linkage_bindings(new_var); } } } @@ -7040,7 +7122,16 @@ parser_division(cbl_division_t division, ENDIF } + // Programs without a DATA DIVISION still need their default currency + // state initialized before the common entry label. + initialize_the_data(); gg_append_statement(current_function->skip_init_label); + // Every invocation, including reentry, selects this program's immutable + // currency state before CANCEL handling or executable COBOL statements. + gg_call(VOID, + "__gg__activate_currency_state", + current_function->saved_currency_state, + NULL_TREE); // This is where we check to see if somebody tried to cancel us IF( gg_call_expr( INT, "__gg__is_canceled", @@ -13891,6 +13982,10 @@ parser_set_pointers( size_t ntgt, cbl_refer_t *tgts, cbl_refer_t source ) // through the level01 variable's children: propagate_linkage_offsets(tgts[i].field, member(tgts[i].field->var_decl_node, "data")); + if( tgts[i].field->attr & linkage_e ) + { + save_debug_linkage_bindings(tgts[i].field); + } } } } @@ -15039,9 +15134,19 @@ parser_local_add(struct cbl_field_t *new_var ) gg_assign( member(new_var->var_decl_node, "data"), gg_pointer_to_array(data_decl_node) ); } + else if( const cbl_field_t *parent = parent_of(new_var) ) + { + // A recursive invocation has a new root allocation. Rebase children + // even when their shared descriptors still point into the caller's data. + gg_assign(member(new_var->var_decl_node, "data"), + gg_add(member(parent->var_decl_node, "data"), + gg_subtract(member(new_var->var_decl_node, "offset"), + member(parent->var_decl_node, "offset")))); + } cbl_refer_t wrapper; wrapper.field = new_var; initialize_variable_internal(wrapper); + save_debug_field_binding(new_var); } void diff --git a/gcc/cobol/gengen.h b/gcc/cobol/gengen.h index 7b9fe61f0dce..ad5400c44d9c 100644 --- a/gcc/cobol/gengen.h +++ b/gcc/cobol/gengen.h @@ -187,6 +187,15 @@ struct gg_function_t tree var_decl_return; tree first_time_through; + tree saved_currency_state; // Static pointer to this program's immutable table. + tree saved_currency_view; // Debugger-readable view for this program. + + struct debug_field_binding_t + { + tree data = NULL_TREE; + tree capacity = NULL_TREE; + }; + std::unordered_map<size_t, debug_field_binding_t> debug_field_bindings; tree skip_init_goto; tree skip_init_label; diff --git a/gcc/cobol/lexio.cc b/gcc/cobol/lexio.cc index 7138dd657d2a..f789fe2d2bd5 100644 --- a/gcc/cobol/lexio.cc +++ b/gcc/cobol/lexio.cc @@ -1504,13 +1504,6 @@ namespace cmd_line { FILE * cdftext::lex_open( const char filename[] ) { - /* - * The source_format stack should be instantiated here with one element, the - * source format that lexio determined from the command-line options and - * heuristics. The recently added command_line_indicator_column is probably - * redundant. - */ - source_format_stack_t source_format; source_format.push(cmd_line::source_format); @@ -1540,6 +1533,8 @@ cdftext::lex_open( const char filename[] ) { included_files.clear(); dbgmsg("lex_open: '%s'", filename); + source_format.top() = cmd_line::source_format; + cobol_filename(filename, inode_of(input)); filespan_t mfile( free_form_reference_format( input, source_format ) ); diff --git a/gcc/cobol/symbols.cc b/gcc/cobol/symbols.cc index 20467e72087c..c1ba30c0cd09 100644 --- a/gcc/cobol/symbols.cc +++ b/gcc/cobol/symbols.cc @@ -5395,6 +5395,10 @@ symbol_program_callables( size_t program ) { symbolset_t callables = common_callables[program]; auto self = cbl_label_of(symbol_at(program)); + // A recursive program can call itself even when it is not COMMON. + if( self->recursive ) { + callables.insert(program); + } auto start_with = 0 < self->parent? self->parent : program; // Build a list of programs reachable by the current program. diff --git a/libgcobol/charmaps.cc b/libgcobol/charmaps.cc index 27f4b8f467d6..e87a28725bdf 100644 --- a/libgcobol/charmaps.cc +++ b/libgcobol/charmaps.cc @@ -38,6 +38,7 @@ #include <ctime> #include <algorithm> +#include <memory> #include <unordered_map> #include <vector> #include <langinfo.h> @@ -83,6 +84,71 @@ std::vector<std::string> __gg__currency_signs(256) ; int __gg__default_currency_sign; char *__gg__ct_currency_signs[256]; // Compile-time currency signs +// Each program's currency setup creates one state. Completed states are +// immutable; only their vector storage changes owners on activation. +struct currency_state + { + std::vector<std::string> signs; + const char *debug_signs[256] = {}; + + currency_state() : signs(256) + { + } + }; + +// Heap allocation keeps views stable when this ownership registry grows. +// Retain the states for the lifetime of their programs, including saved +// callers whose tables are temporarily inactive. +static std::vector<std::unique_ptr<currency_state>> currency_states; +currency_state *__gg__active_currency_state = nullptr; + +extern "C" + { + const char *const *__gg__debug_currency_signs = nullptr; + } + +extern "C" +currency_state * +__gg__get_currency_state() + { + return __gg__active_currency_state; + } + +extern "C" +void +__gg__activate_currency_state(currency_state *state) + { + if( state != __gg__active_currency_state ) + { + if( __gg__active_currency_state != nullptr ) + { + __gg__currency_signs.swap(__gg__active_currency_state->signs); + } + __gg__currency_signs.swap(state->signs); + __gg__active_currency_state = state; + } + + // std::vector::swap preserves the string objects and their c_str pointers, + // including short strings. Publish the existing view with one store. + __gg__debug_currency_signs = state->debug_signs; + } + +void +__gg__new_currency_state() + { + std::unique_ptr<currency_state> state(new currency_state); + currency_states.push_back(std::move(state)); + __gg__activate_currency_state(currency_states.back().get()); + } + +void +__gg__update_debug_currency_sign(unsigned symbol) + { + const std::string &sign = __gg__currency_signs[symbol]; + __gg__active_currency_state->debug_signs[symbol] = + sign.empty() ? nullptr : sign.c_str(); + } + cbl_encoding_t __gg__console_encoding = no_encoding_e ; cbl_encoding_t __gg__display_encoding = no_encoding_e; cbl_encoding_t __gg__national_encoding = no_encoding_e; diff --git a/libgcobol/charmaps.h b/libgcobol/charmaps.h index 626ef3852340..d324a4361ef3 100644 --- a/libgcobol/charmaps.h +++ b/libgcobol/charmaps.h @@ -128,6 +128,17 @@ extern int __gg__quote_character ; extern int __gg__low_value_character ; extern int __gg__high_value_character ; extern std::vector<std::string> __gg__currency_signs ; +// Debugger ABI: pointer to the active state's 256 target string pointers. +// Entries are NUL-terminated strings, or null for unregistered symbols. +// The runtime owns each view and its strings for the process lifetime. +// Read the exported pointer again after every program-state transition. +extern "C" const char *const *__gg__debug_currency_signs; +struct currency_state; +extern currency_state *__gg__active_currency_state; +void __gg__new_currency_state(); +extern "C" currency_state *__gg__get_currency_state(); +extern "C" void __gg__activate_currency_state(currency_state *state); +void __gg__update_debug_currency_sign(unsigned symbol); extern int __gg__default_currency_sign; extern cbl_encoding_t __gg__display_encoding ; extern cbl_encoding_t __gg__national_encoding ; diff --git a/libgcobol/gfileio.cc b/libgcobol/gfileio.cc index db66b274f0fc..3f37a4b2ff31 100644 --- a/libgcobol/gfileio.cc +++ b/libgcobol/gfileio.cc @@ -399,7 +399,10 @@ do_acu_hyphen_translation (char *src) memcpy(file_open_buff, src, len); file_open_buff[len + 1] = 0; - strncpy(file_open_name, file_open_buff, (size_t)COB_FILE_MAX); + // We know that len is no more than 4096 at this writing. That means + // the file_open_buff[4098] has no more than 4096 characters. That means + // it is absolutely safe to copy file_open_buff into file_open_name[4098]. + strcpy(file_open_name, file_open_buff); } static void @@ -3170,7 +3173,7 @@ sequential_file_write(cblc_file_t *file, uint8_t chvert = '\0'; size_t bytes_to_write; - int lcount; + int lcount=0; switch(file->org) { case file_disorganized_e: diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc index 609fb2221758..8ee2dc6850c0 100644 --- a/libgcobol/libgcobol.cc +++ b/libgcobol/libgcobol.cc @@ -573,7 +573,7 @@ struct program_state int rt_quote_character; int rt_low_value_character; int rt_high_value_character; - std::vector<std::string> rt_currency_signs; + currency_state *rt_currency_signs; const unsigned short *rt_collation; // Points to a table of 256 values; cbl_encoding_t rt_display_encoding; cbl_encoding_t rt_national_encoding; @@ -581,7 +581,7 @@ struct program_state cbl_char_t rt_working_init; cbl_char_t rt_local_init; - program_state() : rt_currency_signs(256) + program_state() : rt_currency_signs(nullptr) { // IBM defaults to the \" QUOTE compiler option. quote_character must // be set to \' when the APOST compiler option is in effect @@ -701,7 +701,8 @@ initialize_program_state() console_init(); program_state initial_value = {}; program_states.push_back(initial_value); - __gg__currency_signs = program_states.back().rt_currency_signs; + __gg__currency_sign_init(); + program_states.back().rt_currency_signs = __gg__active_currency_state; } extern "C" @@ -715,9 +716,11 @@ extern "C" void __gg__push_program_state() { + // Save the immutable state by pointer, without copying its strings or view. + program_states.back().rt_currency_signs = __gg__active_currency_state; // Duplicate the state at the back of the stack program_states.push_back(program_states.back()); - __gg__currency_signs = program_states.back().rt_currency_signs; + __gg__activate_currency_state(program_states.back().rt_currency_signs); } extern "C" @@ -732,7 +735,7 @@ __gg__pop_program_state() __gg__high_value_character = program_states.back().rt_high_value_character ; __gg__display_encoding = program_states.back().rt_display_encoding ; __gg__national_encoding = program_states.back().rt_national_encoding ; - __gg__currency_signs = program_states.back().rt_currency_signs ; + __gg__activate_currency_state(program_states.back().rt_currency_signs); __gg__working_init = program_states.back().rt_working_init ; __gg__local_init = program_states.back().rt_local_init ; @@ -10997,6 +11000,7 @@ typedef struct LV_DATA size_t unique_id; cblc_field_t *field; unsigned char *data; + size_t capacity; }LV_DATA; static std::vector<LV_DATA> lv_variables; @@ -11009,6 +11013,7 @@ __gg__push_local_variable(cblc_field_t *field) lv_data.unique_id = __gg__unique_prog_id; lv_data.field = field; lv_data.data = field->data; + lv_data.capacity = field->capacity; lv_variables.push_back(lv_data); } @@ -11020,6 +11025,7 @@ __gg__pop_local_variables() && lv_variables.back().unique_id == __gg__unique_prog_id) { lv_variables.back().field->data = lv_variables.back().data; + lv_variables.back().field->capacity = lv_variables.back().capacity; lv_variables.pop_back(); } } diff --git a/libgcobol/libgcobol.map b/libgcobol/libgcobol.map index 5b9e4ea98c5d..8d6f9e1e6bed 100644 --- a/libgcobol/libgcobol.map +++ b/libgcobol/libgcobol.map @@ -6,6 +6,7 @@ GCOBOL_17.1 __gg__accept_arg_value; __gg__accept_envar; __gg__acos; + __gg__activate_currency_state; __gg__allocate; __gg__alphabet_create; __gg__alphabet_use; @@ -63,6 +64,7 @@ GCOBOL_17.1 __gg__day_to_yyyyddd; __gg__deallocate; __gg__decimal_point_is_comma; + __gg__debug_currency_signs; __gg__default_compute_error; __gg__dialects; __gg__display; @@ -123,6 +125,7 @@ GCOBOL_17.1 __gg__get_argc; __gg__get_argv; __gg__get_command_line; + __gg__get_currency_state; __gg__get_date_dow; __gg__get_date_hhmmssff; __gg__get_date_yyddd; @@ -284,6 +287,8 @@ GCOBOL_17.1 __compat_file_status_word; + gcobol_fileops; + posix_clock_gettime; posix_errno; posix_fstat; diff --git a/libgcobol/valconv.cc b/libgcobol/valconv.cc index 7da5a651a0f8..1b319828f105 100644 --- a/libgcobol/valconv.cc +++ b/libgcobol/valconv.cc @@ -571,17 +571,21 @@ extern "C" void __gg__currency_sign_init() // This duplicates the constructor. { - for( auto str : __gg__currency_signs ) { - str.clear(); - } + // A program's one-time currency setup starts a fresh immutable state. + __gg__new_currency_state(); } extern "C" void __gg__currency_sign(int symbol, const char *sign) { + if( __gg__active_currency_state == nullptr ) + { + __gg__new_currency_state(); + } __gg__currency_signs[symbol] = sign; __gg__default_currency_sign = *sign; + __gg__update_debug_currency_sign(symbol); } extern "C"
