From: Robert Dubner Date: Mon, 10 Aug 2026 21:27:42 +0000 (-0400) Subject: cobol: Repair COBOL EXTERNAL data definition. X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;ds=sidebyside;p=thirdparty%2Fgcc.git cobol: Repair COBOL EXTERNAL data definition. COBOL provides the EXTERNAL clause for data definitions. The behavior is the same as for Fortran COMMON data. Until now, we have mistakenly treated those as if they were C global variable definitions, which resulted in errors unless modules were linked with -allow-multiple-definition. These changes instead cause the variable containing the run-time metadata (and its constructor) for a COBOL symbol to be a DECL_WEAK reference, and for the variable's data area to have the DECL_COMMON attribute. This provides for correct COBOL behavior. In addition, giving the COMMON data area the variable's name means that it can be linked with a C module that has an extern declaration or a global data definition of the same name, and they will all refer to the same memory area. gcc/cobol/ChangeLog: * gcobol.1: Documentation * genapi.cc (create_cblc_string_variable): Use new vs_global enum instead of old vs_external. (SET_VAR_DECL): Use new vs_extern instead of vs_external_reference. (parser_division): Likewise. (parser_file_add): Use vs_weak instead of vs_external. (psa_global): Likewise. (psa_new_var_decl): Likewise; Use new naming convention. (parser_symbol_add): Use vs_common instead of vs_external. * gengen.cc (gg_declare_variable): Clean up VAR_DECL attibute assignments; use new variable_scope constants. * gengen.h (enum gg_variable_scope_t): Update the enum constants. libgcobol/ChangeLog: * common-defs.h (enum cbl_field_attr_t): Update comments. * libgcobol.cc (init_var_both): Modify how EXTERNAL variables are initialized. gcc/testsuite/ChangeLog: * cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.cob: New test. * cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.out: New test. --- diff --git a/gcc/cobol/gcobol.1 b/gcc/cobol/gcobol.1 index de968bd02bc..8ad63202b8c 100644 --- a/gcc/cobol/gcobol.1 +++ b/gcc/cobol/gcobol.1 @@ -863,7 +863,7 @@ is a gcc compiler, and follows gcc conventions where applicable. Sometimes those conventions (and user expectations) conflict with common Mainframe practice. Unless required of the compiler by the ISO specification, any such conflicts are resolved in favor of gcc. -.Ss Linking +.Ss Linking Functions Unlike, C, the \*[lang] .Sy CALL statement implies dynamic linking, because for @@ -897,11 +897,6 @@ option. That forces all .Sy CALL statements to be resolved dynamically, at runtime. .ig -Programs that are expected to execute -correctly in the presence of an unresolved symbol (perhaps because the -program logic won't require that particular -.Sy CALL ) -can use linker options to produce an executable anyway. .Pp One corner case yet remains. The .Sy CALL @@ -937,6 +932,53 @@ linking for all statement, regardless of compile-time constants. .. . +.Ss Linking to C +.Nm +can be called from C and can call C functions. Additionally, 01-level +data items can be defined by and/or referenced as C global variables. +.Pp +A top-level +.Sy PROGRAM-ID +has external linkage, just like a non-static C function. The name is +exported in lowercase. Hyphens, being disallowed by the linker, are +encoded; simplest is not to use such names if they are to be +referenced outside \*[lang]. Contained programs do not have external +linkage. +.Pp +A +.Sy CALL +statement invokes the target following the C ABI for the platform. That means for +.D1 CALL Dq FOO +the generated code is the same, no matter if +.Sy FOO +was compiled with C or \*[lang]. Alphanumeric and group parameters +are passed as +.Sy "char *" +pointers (not NUL-terminated). Binary numeric types are passed by +value, as with C. See the CDF +.Sy CALL-CONVENTION +directive to control how the external reference is rendered. +.Pp +A \*[lang] +.Sy EXTERNAL +data item is similar to a Fortran COMMON variable. Many programs may +define an item by the same name, preferably with identical +definitions. The linker recognizes the multiple definitions as defining a single variable. (This is unlike C, where there is one definition and potentially N +.Sy extern +references.) +C can participate when COBOL describes a variable as +.Sy EXTERNAL +in one of two ways: +.Bl -enum +.It +C can declare an +.Sy extern +variable with the same name. +. +.It +C can define a global nonstatic variable (with or without an initializer) at file scope and the COBOL variables +will refer to it. +. .Ss Implemented Exception Conditions By default, per ISO, no EC is enabled. Implemented ECs may be enabled on the command line or via the diff --git a/gcc/cobol/genapi.cc b/gcc/cobol/genapi.cc index 41f46036df9..5fc6c8d726e 100644 --- a/gcc/cobol/genapi.cc +++ b/gcc/cobol/genapi.cc @@ -235,7 +235,7 @@ create_cblc_string_variable(const char *var_name, const char *var_contents) tree entry_point = gg_declare_variable(array_of_characters, var_name, constr, - vs_external); + vs_global); gg_define_from_declaration(entry_point); } @@ -2799,7 +2799,7 @@ parser_enter_file(const char *filename) // Establish our variable declarations for global variables in libgcobol: #define SET_VAR_DECL(A, B, C) \ - A = gg_declare_variable(B, C, NULL_TREE, vs_external_reference) + A = gg_declare_variable(B, C, NULL_TREE, vs_extern) SET_VAR_DECL(var_decl_exception_code , INT , "__gg__exception_code"); SET_VAR_DECL(var_decl_exception_file_status , INT , "__gg__exception_file_status"); @@ -6822,7 +6822,7 @@ parser_division(cbl_division_t division, tree globals_are_initialized = gg_declare_variable( INT, "__gg__globals_are_initialized", NULL, - vs_external_reference); + vs_extern); IF( globals_are_initialized, eq_op, integer_zero_node ) { // one-time initialization happens here @@ -8970,7 +8970,7 @@ parser_file_add(struct cbl_file_t *file) gg_variable_scope_t scope; if( file->attr & external_e ) { - scope = vs_external; + scope = vs_weak; } else { @@ -14445,7 +14445,7 @@ psa_global(cbl_field_t *new_var) } } - new_var->var_decl_node = gg_declare_variable(cblc_field_type_node, ach, NULL, vs_external_reference); + new_var->var_decl_node = gg_declare_variable(cblc_field_type_node, ach, NULL, vs_extern); // global variables already have a .data area defined. We can find that // variable from the new_var->name. It's lower-case, with hyphens @@ -14460,7 +14460,7 @@ psa_global(cbl_field_t *new_var) ach[i] = '_'; } } - new_var->data_decl_node = gg_declare_variable(UCHAR, ach, NULL, vs_external_reference); + new_var->data_decl_node = gg_declare_variable(UCHAR, ach, NULL, vs_extern); } static tree @@ -14478,7 +14478,7 @@ psa_new_var_decl(cbl_field_t *new_var, const char *external_record_base) strcat(ach, "_ra"); // For "Record Area" new_var_decl = gg_define_variable( cblc_field_type_node, ach, - vs_external); + vs_weak); SET_DECL_MODE(new_var_decl, BLKmode); } else @@ -14511,7 +14511,7 @@ psa_new_var_decl(cbl_field_t *new_var, const char *external_record_base) if( new_var->attr & external_e ) { // For external variables, just stick with the original name - sprintf(base_name, "%s_cblc_field", new_var->name); + sprintf(base_name, "%s.cblc", new_var->name); } else { @@ -14569,7 +14569,7 @@ psa_new_var_decl(cbl_field_t *new_var, const char *external_record_base) //fprintf(stderr, "external_e base name is %s\n", base_name); new_var_decl = gg_define_variable( cblc_field_type_node, base_name, - vs_external); + vs_weak); SET_DECL_MODE(new_var_decl, BLKmode); } else if( new_var->attr & (intermediate_e) @@ -15150,7 +15150,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) new_var->data_decl_node = gg_define_variable( array_type, achDataName, - vs_external); + vs_common); data_area = gg_pointer_to_array(new_var->data_decl_node); goto actual_allocate; } @@ -15255,7 +15255,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) new_var->data_decl_node = gg_define_variable( array_type, achDataName, - vs_external); + vs_common); data_area = gg_pointer_to_array(new_var->data_decl_node); } else @@ -15299,7 +15299,7 @@ parser_symbol_add(struct cbl_field_t *new_var ) free(level_88_string); free(class_string); - if( !(new_var->attr & ( linkage_e | based_e)) + if( !(new_var->attr & ( linkage_e | based_e )) && !(new_var->type == FldLiteralN) ) { static const bool explicitly = false; diff --git a/gcc/cobol/gengen.cc b/gcc/cobol/gengen.cc index 67386ad0a36..e41302c5ef7 100644 --- a/gcc/cobol/gengen.cc +++ b/gcc/cobol/gengen.cc @@ -868,16 +868,43 @@ gg_declare_variable(tree type_decl, DECL_EXTERNAL(var_decl) = 0; DECL_CONTEXT (var_decl) = gg_trans_unit.trans_unit_decl; break; - case vs_external: + case vs_weak: + DECL_CONTEXT (var_decl) = gg_trans_unit.trans_unit_decl; + TREE_USED(var_decl) = 1; + TREE_STATIC( var_decl ) = 1; + TREE_PUBLIC( var_decl ) = 1; + DECL_EXTERNAL( var_decl ) = 0; + DECL_COMMON( var_decl ) = 0; + DECL_WEAK( var_decl ) = 1; + // We are not allowed to try to create a COMMON variable more than once + // in a translation unit. + seen[unique_name] = var_decl; + break; + case vs_common: + DECL_CONTEXT (var_decl) = gg_trans_unit.trans_unit_decl; + TREE_USED(var_decl) = 1; + TREE_STATIC( var_decl ) = 1; + TREE_PUBLIC( var_decl ) = 1; + DECL_EXTERNAL( var_decl ) = 0; + DECL_COMMON( var_decl ) = 1; + DECL_WEAK( var_decl ) = 0; + // We are not allowed to try to create a COMMON variable more than once + // in a translation unit. + seen[unique_name] = var_decl; + // A COMMON value with an initializer gets converted to a .data or .bss. + // So, we make sure it has no initializer. + gcc_assert(!initial_value); + initial_value = NULL_TREE; + break; + case vs_global: // This is for defining variables with global scope DECL_CONTEXT (var_decl) = gg_trans_unit.trans_unit_decl; TREE_USED(var_decl) = 1; TREE_STATIC(var_decl) = 1; TREE_PUBLIC(var_decl) = 1; DECL_EXTERNAL(var_decl) = 0; - seen[unique_name] = var_decl; break; - case vs_external_reference: + case vs_extern: // This is for referencing variables defined elsewhere DECL_CONTEXT (var_decl) = gg_trans_unit.trans_unit_decl; TREE_USED(var_decl) = 1; diff --git a/gcc/cobol/gengen.h b/gcc/cobol/gengen.h index ea2f2812a3a..88acf00f70f 100644 --- a/gcc/cobol/gengen.h +++ b/gcc/cobol/gengen.h @@ -109,11 +109,17 @@ */ enum gg_variable_scope_t { - vs_stack, - vs_static, - vs_file_static, // static variable of file scope - vs_external, // Creates a PUBLIC STATIC variable of file scope - vs_external_reference, // References a vs_external variable. + // These scopes reference the GCC world rather than the COBOL world. + // This is in contrast to the cbl_field_attr_t bits, where external_e means + // the variable had the COBOL EXTERNAL clause, which causes a variable to + // have a weak cblc_field_t and a common cblc_field_t::data. + vs_stack, // An automatic variable. + vs_static, // A static variable of function scope. + vs_file_static, // static variable of file scope. + vs_weak, // A "weak" variable. + vs_common, // A COMMON variable. + vs_global, // A global variable, e.g. "int xxx;" in C + vs_extern, // A declaration for a global; "extern int xxx;" }; struct gg_function_t diff --git a/gcc/testsuite/cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.cob b/gcc/testsuite/cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.cob new file mode 100644 index 00000000000..81fd61b2cfe --- /dev/null +++ b/gcc/testsuite/cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.cob @@ -0,0 +1,21 @@ + *> Do not edit this generated file. See README.txt + *> { dg-do run } + *> { dg-output-file "group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.out" } + identification division. + program-id. prog. + data division. + working-storage section. + 01 ext-var pic x(6) external. + 01 ext-vari pic x(6) external value "absurd". + procedure division. + initialize ext-var + display "ext-var is " """"ext-var"""" + initialize ext-var all to value + display "ext-var is " """"ext-var"""" + initialize ext-vari + display "ext-vari is " """"ext-vari"""" + initialize ext-vari all to value + display "ext-vari is " """"ext-vari"""" + goback. + end program prog. + diff --git a/gcc/testsuite/cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.out b/gcc/testsuite/cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.out new file mode 100644 index 00000000000..61894640970 --- /dev/null +++ b/gcc/testsuite/cobol.dg/group2/INITIALIZE_EXTERNAL_data_items_with_VALUE.out @@ -0,0 +1,5 @@ +ext-var is " " +ext-var is " " +ext-vari is " " +ext-vari is "absurd" + diff --git a/libgcobol/common-defs.h b/libgcobol/common-defs.h index 78eb03c8fd7..5c1375414c8 100644 --- a/libgcobol/common-defs.h +++ b/libgcobol/common-defs.h @@ -215,8 +215,8 @@ enum cbl_field_attr_t : uint64_t { refmod_e = 0x0000040000, // Runtime; indicates a refmod is active based_e = 0x0000080000, // pointer capacity, for ADDRESS OF or ALLOCATE any_length_e = 0x0000100000, // inferred length of linkage in nested program - global_e = 0x0000200000, // field has global scope - external_e = 0x0000400000, // field has external scope + global_e = 0x0000200000, // field is COBOL GLOBAL (not GCC global scope) + external_e = 0x0000400000, // field is COBOL EXTERNAL (not GCC extern) blank_zero_e = 0x0000800000, // BLANK WHEN ZERO // data division uses 2 low bits of high byte linkage_e = 0x0001000000, // field is in linkage section diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc index a5c9b4b4ad1..cdc65d6b488 100644 --- a/libgcobol/libgcobol.cc +++ b/libgcobol/libgcobol.cc @@ -5911,12 +5911,20 @@ init_var_both(cblc_field_t *var, return; } - if( !(var->attr & based_e) && (var->attr & external_e) ) + if( !(var->attr & based_e) && (var->attr & external_e) && !var->initial) { // These types can't be initialized return; } + if( flag_bits & JUST_ONCE_BIT && var->attr & external_e ) + { + // This is an EXTERNAL variable with a VALUE clause. We don't initialize + // that on startup. The value is applicable only for explicit INITIALIZE + // statements. + return; + } + // There are times, for example, when we are table with OCCURS, that we // look like a variable with no initial, and we might be tempted to set our // memory to the default. But if a parent has been initialized, we must not