cobol.mostlyclean:
+gcobol.clean:
+# This is intended for non-general use. It is a last-ditch effort to flush
+# out all oject files and executable code for gcobol and libgcobol, causing
+# a complete rebuild of all executable code.
+ rm -fr gcobol cobol1 cobol/* \
+ ../*/libgcobol/*
+
cobol.clean:
rm -fr gcobol cobol1 cobol/*
}
int i = 1;
for( auto& elem : *this ) {
- dbgmsg("cbl_enabled_exceptions_t: %2d {%s, %s, %zu}",
+ dbgmsg("cbl_enabled_exceptions_t: %2d {%s, %s, %lu}",
i++,
elem.location? "with location" : " no location",
ec_type_str(elem.ec),
- elem.file );
+ gb4(elem.file) );
}
std::swap(debug, yydebug);
}
#define TSI_BACK (tsi_last(current_function->statement_list_stack.back()))
extern char *cobol_name_mangler(const char *cobol_name);
-static tree gg_attribute_bit_get(struct cbl_field_t *var, cbl_field_attr_t bits);
+static tree gg_attribute_bit_get( struct cbl_field_t *var,
+ cbl_field_attr_t bits);
static tree label_list_out_goto;
static tree label_list_out_label;
tree file_static_variable(tree type, const char *v)
{
- // This routine returns a reference to an already-defined file_static variable
- // You need to know the type that was used for the definition.
+ // This routine returns a reference to an already-defined file_static
+ // variable. You need to know the type that was used for the definition.
return gg_declare_variable(type, v, NULL, vs_file_static);
}
// set using -f-trace-debug, defined in lang.opt
int f_trace_debug;
-// When doing WRITE statements, the IBM Language Reference and the ISO/IEC_2014
-// standard specify that when the ADVANCING clause is omitted, the default is
-// AFTER ADVANCING 1 LINE.
+// When doing WRITE statements, the IBM Language Reference and the
+// ISO/IEC_2014 standard specify that when the ADVANCING clause is omitted, the
+// default isAFTER ADVANCING 1 LINE.
//
// MicroFocus and GnuCOBOL state that the default is BEFORE ADVANCING 1 LINE
//
trace_handle = gg_define_variable(INT, "trace_handle", vs_static);
trace_indent = gg_define_variable(INT, "trace_indent", vs_static);
- bTRACE1 = getenv("GCOBOL_TRACE") ? getenv("GCOBOL_TRACE") : gv_trace_switch;
+ bTRACE1 = getenv("GCOBOL_TRACE") ? getenv("GCOBOL_TRACE") :gv_trace_switch;
if( bTRACE1 && strcmp(bTRACE1, "0") != 0 )
{
gg_set_current_line_number(DEFAULT_LINE_NUMBER);
- gg_define_function( INT,
- "main",
- INT, "argc",
- build_pointer_type(CHAR_P), "argv",
- NULL_TREE);
+ tree function_decl = gg_define_function( INT,
+ "main",
+ "main",
+ INT, "argc",
+ build_pointer_type(CHAR_P), "argv",
+ NULL_TREE);
+
+ // Modify the default settings for main(), as empirically determined from
+ // examining C/C+_+ compilations. (See the comment for gg_build_fn_decl()).
+ TREE_ADDRESSABLE(function_decl) = 0;
+ TREE_USED(function_decl) = 0;
+ TREE_NOTHROW(function_decl) = 0;
+ TREE_STATIC(function_decl) = 1;
+ DECL_EXTERNAL (function_decl) = 0;
+ TREE_PUBLIC (function_decl) = 1;
+ DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1;
// Pick up pointers to the input parameters:
// First is the INT which is the number of argv[] entries
};
};
-static std::map<program_reference_t, std::list<called_tree_t> > call_targets;
+static std::map<program_reference_t, std::list<tree> > call_targets;
static std::map<tree, cbl_call_convention_t> called_targets;
-static void
-parser_call_target( tree func )
+static
+void
+set_call_convention(tree function_decl, cbl_call_convention_t convention)
{
- cbl_call_convention_t convention = current_call_convention();
- const char *name = IDENTIFIER_POINTER( DECL_NAME(func) );
- program_reference_t key(current_program_index(), name);
-
- // Each func is unique and inserted only once.
- assert( called_targets.find(func) == called_targets.end() );
- called_targets[func] = convention;
+ called_targets[function_decl] = convention;
+ }
- called_tree_t value(func, convention);
- auto& p = call_targets[key];
- p.push_back(value);
+static
+void
+parser_call_target( const char *name, tree call_expr )
+ {
+ /* This routine gets called when parser_call() has been invoked with a
+ literal target. That target is a COBOL name like "prog_2". However,
+ there is the case when "prog_2" is a forward reference to a contained
+ program nested inside "prog_1". In that case, the actual definition
+ of "prog_2" will end up with a name like "prog_2.62", and eventually
+ the target of the call will have to be modified from "prog_2" to
+ "prog_2.62".
+
+ We save the call expression for this call, and then we update it later,
+ after we know whether or not it was a forward reference to a local
+ function. */
+
+ program_reference_t key(current_program_index(), name);
+ auto& p = call_targets[key];
+ p.push_back(call_expr);
}
/*
cbl_call_convention_t
parser_call_target_convention( tree func )
{
- auto p = called_targets.find(func);
- if( p != called_targets.end() ) return p->second;
+ auto p = called_targets.find(func);
+ if( p != called_targets.end() )
+ {
+ // This was found in our list of call targets
+ return p->second;
+ }
- return cbl_call_cobol_e;
+ return cbl_call_cobol_e;
}
void
k.called);
char ch = '[';
for( auto func : v ) {
- fprintf( stderr, "%c %s", ch, IDENTIFIER_POINTER(DECL_NAME(func.node)) );
+ fprintf( stderr, "%c %s", ch, IDENTIFIER_POINTER(DECL_NAME(func)) );
ch = ',';
}
fprintf(stderr, " ]\n");
const char plain_name[],
const char mangled_name[] )
{
- auto key = program_reference_t(caller, plain_name);
- auto p = call_targets.find(key);
- if( p == call_targets.end() ) return 0;
+ auto key = program_reference_t(caller, plain_name);
+ auto p = call_targets.find(key);
+ if( p == call_targets.end() ) return 0;
- for( auto func : p->second )
- {
- func.convention = cbl_call_verbatim_e;
- DECL_NAME(func.node) = get_identifier(mangled_name);
- }
- return p->second.size();
+ for( auto call_expr : p->second )
+ {
+ tree fndecl_type = build_varargs_function_type_array( COBOL_FUNCTION_RETURN_TYPE,
+ 0, // No parameters yet
+ NULL); // And, hence, no types
+
+ // Fetch the FUNCTION_DECL for that FUNCTION_TYPE
+ tree function_decl = gg_build_fn_decl(mangled_name, fndecl_type);
+ tree function_address = gg_get_address_of(function_decl);
+
+ TREE_OPERAND(call_expr, 1) = function_address;
+ }
+ return p->second.size();
}
static tree
-function_handle_from_name(cbl_refer_t &name,
+function_pointer_from_name(cbl_refer_t &name,
tree function_return_type)
{
Analyze();
function_return_type,
0,
NULL);
- tree function_pointer = build_pointer_type(function_type);
- tree function_handle = gg_define_variable(function_pointer, "..function_handle.1", vs_stack);
-
+ tree function_pointer_type = build_pointer_type(function_type);
+ tree function_pointer = gg_define_variable(function_pointer_type,
+ "..function_pointer.1",
+ vs_stack);
if( name.field->type == FldPointer )
{
// If the parameter is a pointer, just pick up the value and head for the
// exit
if( refer_is_clean(name) )
{
- gg_memcpy(gg_get_address_of(function_handle),
+ gg_memcpy(gg_get_address_of(function_pointer),
member(name.field->var_decl_node, "data"),
sizeof_pointer);
}
else
{
- gg_memcpy(gg_get_address_of(function_handle),
+ gg_memcpy(gg_get_address_of(function_pointer),
qualified_data_location(name),
sizeof_pointer);
}
- return function_handle;
+ return function_pointer;
}
else if( use_static_call() && is_literal(name.field) )
{
- // It's a literal, and we are using static calls. Generate the CALL, and
- // pass the address expression to parser_call_target(). That will cause
- // parser_call_target_update() to replace any nested CALL "foo" with the
- // local "foo.60" name.
-
- // We create a reference to it, which is later resolved by the linker.
- tree addr_expr = gg_get_function_address( function_return_type,
- name.field->data.initial);
- gg_assign(function_handle, addr_expr);
+ tree fndecl_type = build_varargs_function_type_array( function_return_type,
+ 0, // No parameters yet
+ NULL); // And, hence, no types
- tree func = TREE_OPERAND(addr_expr, 0);
- parser_call_target(func); // add function to list of call targets
+ // Fetch the FUNCTION_DECL for that FUNCTION_TYPE
+ tree function_decl = gg_build_fn_decl(name.field->data.initial,
+ fndecl_type);
+ // Take the address of the function decl:
+ tree address_of_function = gg_get_address_of(function_decl);
+ gg_assign(function_pointer, address_of_function);
}
else
{
- // This is not a literal or static
+ // We are not using static calls.
if( name.field->type == FldLiteralA )
{
- gg_assign(function_handle,
+ gg_assign(function_pointer,
gg_cast(build_pointer_type(function_type),
- gg_call_expr(VOID_P,
- "__gg__function_handle_from_literal",
- build_int_cst_type(INT, current_function->our_symbol_table_index),
- gg_string_literal(name.field->data.initial),
- NULL_TREE)));
+ gg_call_expr( VOID_P,
+ "__gg__function_handle_from_literal",
+ build_int_cst_type(INT,
+ current_function->our_symbol_table_index),
+ gg_string_literal(name.field->data.initial),
+ NULL_TREE)));
}
else
{
- gg_assign(function_handle,
+ gg_assign(function_pointer,
gg_cast(build_pointer_type(function_type),
gg_call_expr( VOID_P,
- "__gg__function_handle_from_name",
- build_int_cst_type(INT, current_function->our_symbol_table_index),
- gg_get_address_of(name.field->var_decl_node),
- refer_offset(name),
- refer_size_source( name),
- NULL_TREE)));
+ "__gg__function_handle_from_name",
+ build_int_cst_type(INT,
+ current_function->our_symbol_table_index),
+ gg_get_address_of(name.field->var_decl_node),
+ refer_offset(name),
+ refer_size_source( name),
+ NULL_TREE)));
}
}
- return function_handle;
+ return function_pointer;
}
void
for( size_t i=0; i<nprogs; i++ )
{
- tree function_handle = function_handle_from_name( progs[i],
- COBOL_FUNCTION_RETURN_TYPE);
+ tree function_pointer = function_pointer_from_name( progs[i],
+ COBOL_FUNCTION_RETURN_TYPE);
gg_call(VOID,
"__gg__to_be_canceled",
- gg_cast(SIZE_T, function_handle),
+ gg_cast(SIZE_T, function_pointer),
NULL_TREE);
}
}
}
void
-parser_statement_begin( const cbl_name_t statement_name,
+parser_statement_begin( const cbl_name_t statement_name,
tree ecs,
tree dcls )
{
// operation, we need to store the location information and do the exception
// overhead:
- static const std::set<std::string> file_ops =
+ static const std::set<std::string> file_ops =
{
"OPEN",
"CLOSE",
{
SHOW_PARSE_HEADER
char ach[256];
- sprintf(ach, "leaving level:%d %s", file_level, current_filename.back().c_str());
+ sprintf(ach,
+ "leaving level:%d %s",
+ file_level,
+ current_filename.back().c_str());
SHOW_PARSE_TEXT(ach)
SHOW_PARSE_END
}
}
file_level -= 1;
current_filename.pop_back();
+
+ if( file_level == 0 )
+ {
+ // We are leaving the top-level file, which means this compilation is
+ // done, done, done.
+ gg_leaving_the_source_code_file();
+ }
}
void
// have no parameters. We'll chain the parameters on in parser_division(),
// when we process PROCEDURE DIVISION USING...
- gg_define_function_with_no_parameters( COBOL_FUNCTION_RETURN_TYPE,
- funcname,
- funcname_);
+ gg_define_function(COBOL_FUNCTION_RETURN_TYPE,
+ funcname,
+ funcname_,
+ NULL_TREE);
current_function->first_time_through =
- gg_define_variable(INT,
- "_first_time_through",
- vs_static,
- integer_one_node);
+ gg_define_variable(INT,
+ "_first_time_through",
+ vs_static,
+ integer_one_node);
gg_create_goto_pair(¤t_function->skip_init_goto,
¤t_function->skip_init_label);
current_function->current_section = NULL;
current_function->current_paragraph = NULL;
- current_function->is_truly_nested = false;
-
// Text conversion must be initialized before the code generated by
// parser_symbol_add runs.
// The first thing we have to do is mangle this name. This is safe even
// though the end result will be mangled again, because the mangler doesn't
// change a mangled name.
- char *mangled_name = cobol_name_mangler(funcname_);
+
+ char *mangled_name;
+
+ if( current_call_convention() == cbl_call_cobol_e )
+ {
+ mangled_name = cobol_name_mangler(funcname_);
+ }
+ else
+ {
+ mangled_name = xstrdup(funcname_);
+ }
size_t parent_index = current_program_index();
- char funcname[128];
+ char *funcname;
if( parent_index )
{
// This is a nested function. Tack on the parent_index to the end of it.
- sprintf(funcname, "%s." HOST_SIZE_T_PRINT_DEC, mangled_name,
- (fmt_size_t)parent_index);
+ funcname = xasprintf( "%s." HOST_SIZE_T_PRINT_DEC,
+ mangled_name,
+ (fmt_size_t)parent_index);
}
else
{
// This is a top-level function; just use the straight mangled name
- strcpy(funcname, mangled_name);
+ funcname = xstrdup(mangled_name);
}
free(mangled_name);
TRACE1_TEXT("\"")
TRACE1_END
}
+
+ free(funcname);
}
void
case FldNumericDisplay:
case FldNumericBinary:
case FldPacked:
- if( field->data.digits > 18 )
+ if( field->data.digits > 18 )
{
retval = UINT128;
nbytes = 16;
cbl_field_type_str(field->type));
break;
}
- }
- if( retval == SIZE_T && field->attr & signable_e )
- {
- retval = SSIZE_T;
- }
- if( retval == UINT128 && field->attr & signable_e )
- {
- retval = INT128;
+ if( retval == SIZE_T && field->attr & signable_e )
+ {
+ retval = SSIZE_T;
+ }
+ if( retval == UINT128 && field->attr & signable_e )
+ {
+ retval = INT128;
+ }
}
return retval;
}
static inline bool
is_valuable( cbl_field_type_t type ) {
+ /* The name of this routine is a play on words, in English. It doesn't
+ mean "Is worth a lot". It means "Can be converted to a value." */
switch ( type ) {
case FldInvalid:
case FldGroup:
case FldAlphanumeric:
case FldNumericEdited:
- case FldAlphaEdited:
case FldLiteralA:
case FldClass:
case FldConditional:
// COBOL form to a little-endian binary representation so that they
// can be conveyed BY CONTENT/BY VALUE in a CALL or user-defined
// function activation.
+ case FldAlphaEdited:
case FldNumericDisplay:
case FldNumericBinary:
case FldFloat:
static
void
-pe_stuff(cbl_refer_t refer, ec_type_t ec)
+program_end_stuff(cbl_refer_t refer, ec_type_t ec)
{
// This is the moral equivalent of a C "return xyz;".
gg_assign(retval, gg_cast(return_type, integer_zero_node));
- gg_modify_function_type(current_function->function_decl,
- return_type);
-
if( is_valuable( field_type ) )
{
// The field being returned is numeric.
IF( current_function->called_by_main_counter, eq_op, integer_zero_node )
{
// This function wasn't called by main, so we treat it like a GOBACK
- pe_stuff(refer, ec);
+ program_end_stuff(refer, ec);
}
ELSE
{
// This was a recursive call into the function originally called by
// main. Because we are under the control of a calling program, we
// treat this like a GOBACK
- pe_stuff(refer, ec);
+ program_end_stuff(refer, ec);
}
ELSE
{
{
}
ENDIF
- pe_stuff(refer, ec);
+ program_end_stuff(refer, ec);
}
}
{
parser_local_add(returning);
current_function->returning = returning;
+
+ size_t nbytes = 0;
+ tree returning_type = tree_type_from_field_type(returning, nbytes);
+ gg_modify_function_type(current_function->function_decl, returning_type);
}
// Stash the returning variables for use during parser_return()
void
create_and_call(size_t narg,
cbl_ffi_arg_t args[],
- tree function_handle,
+ tree function_pointer,
+ const char *funcname,
tree returned_value_type,
cbl_refer_t returned,
- cbl_label_t *not_except
- )
+ cbl_label_t *not_except)
{
// We have a good function handle, so we are going to create a call
tree *arguments = NULL;
gg_assign(var_decl_call_parameter_count,
build_int_cst_type(INT, narg));
- gg_assign(var_decl_call_parameter_signature,
- gg_cast(CHAR_P, function_handle));
+ tree call_expr = NULL_TREE;
+ if( function_pointer )
+ {
+ gg_assign(var_decl_call_parameter_signature,
+ gg_cast(CHAR_P, function_pointer));
- tree call_expr = gg_call_expr_list( returned_value_type,
- function_handle,
+ call_expr = gg_call_expr_list(returned_value_type,
+ function_pointer,
+ narg,
+ arguments );
+ }
+ else
+ {
+ tree fndecl_type = build_varargs_function_type_array( returned_value_type,
+ 0, // No parameters yet
+ NULL); // And, hence, no types
+
+ // Fetch the FUNCTION_DECL for that FUNCTION_TYPE
+ tree function_decl = gg_build_fn_decl(funcname, fndecl_type);
+ set_call_convention(function_decl, current_call_convention());
+
+ // Take the address of the function decl:
+ tree address_of_function = gg_get_address_of(function_decl);
+
+ // Stash that address as the called program's signature:
+ tree address_as_char_p = gg_cast(CHAR_P, address_of_function);
+ tree assigment = gg_assign( var_decl_call_parameter_signature,
+ address_as_char_p);
+ // The source of the assigment is the second element of a MODIFY_EXPR
+ parser_call_target( funcname, assigment );
+
+ // Create the call_expr from that address
+ call_expr = build_call_array_loc( location_from_lineno(),
+ returned_value_type,
+ address_of_function,
narg,
- arguments );
+ arguments);
+ // Among other possibilities, this might be a forward reference to a
+ // contained function. The name here is "prog2", and ultimately will need
+ // to be replaced with a call to "prog2.62". So, this call expr goes into
+ // a list of call expressions whose function_decl targets will be replaced.
+ parser_call_target( funcname, call_expr );
+ }
+
tree returned_value;
+
if( returned.field )
{
- returned_value = gg_define_variable(returned_value_type);
+ // Because the CALL had a RETURNING clause, RETURN-CODE doesn't return a
+ // value. So, we make sure it is zero
+ //// gg_assign(var_decl_return_code, build_int_cst_type(SHORT, 0));
- // We are expecting a return value of type CHAR_P, SSIZE_T, SIZE_T,
- // UINT128 or INT128
+ // We expect the return value to be a 64-bit or 128-bit integer. How
+ // we treat that returned value depends on the target.
+
+ // Pick up that value:
+ returned_value = gg_define_variable(returned_value_type);
push_program_state();
gg_assign(returned_value, gg_cast(returned_value_type, call_expr));
pop_program_state();
- // Because the CALL had a RETURNING clause, RETURN-CODE doesn't return a
- // value. So, we make sure it is zero
-//// gg_assign(var_decl_return_code, build_int_cst_type(SHORT, 0));
-
if( returned_value_type == CHAR_P )
{
tree returned_location = gg_define_uchar_star();
// We are getting close to establishing the function_type. To do that,
// we want to establish the function's return type.
-// gg_push_context();
size_t nbytes;
tree returned_value_type = tree_type_from_field_type(returned.field, nbytes);
- tree function_handle = function_handle_from_name( name,
- returned_value_type);
- if( (use_static_call() && is_literal(name.field))
- || (name.field && name.field->type == FldPointer) )
+ if( use_static_call() && is_literal(name.field) )
{
- // If these conditions are true, then we know we have a good
- // function_handle, and we don't need to check
+ // name is a literal
create_and_call(narg,
args,
- function_handle,
+ NULL_TREE,
+ name.field->data.initial,
returned_value_type,
returned,
- not_except
- );
+ not_except);
+ }
+ else if( name.field && name.field->type == FldPointer )
+ {
+ tree function_pointer = function_pointer_from_name( name,
+ returned_value_type);
+ // This is call-by-pointer; we know function_pointer is good:
+ create_and_call(narg,
+ args,
+ function_pointer,
+ nullptr,
+ returned_value_type,
+ returned,
+ not_except);
}
else
{
+ tree function_pointer = function_pointer_from_name( name,
+ returned_value_type);
// We might not have a good handle, so we have to check:
- IF( function_handle,
+ IF( function_pointer,
ne_op,
- gg_cast(TREE_TYPE(function_handle), null_pointer_node) )
+ gg_cast(TREE_TYPE(function_pointer), null_pointer_node) )
{
- create_and_call(narg,
- args,
- function_handle,
- returned_value_type,
- returned,
- not_except
- );
+ create_and_call(narg,
+ args,
+ function_pointer,
+ nullptr,
+ returned_value_type,
+ returned,
+ not_except);
}
ELSE
{
gg_append_statement( not_except->structs.call_exception->bottom.label );
free( not_except->structs.call_exception );
}
-// gg_pop_context();
-
}
// Set global variable to use alternative ENTRY point.
|| source.field->type == FldLiteralA))
{
// This is something like SET varp TO ENTRY "ref".
- tree function_handle = function_handle_from_name(source,
+ tree function_pointer = function_pointer_from_name(source,
COBOL_FUNCTION_RETURN_TYPE);
gg_memcpy(qualified_data_location(tgts[i]),
- gg_get_address_of(function_handle),
+ gg_get_address_of(function_pointer),
sizeof_pointer);
}
else
// We haven't seen this caller before
callers.insert(caller);
- char ach[2*sizeof(cbl_name_t)];
+ char ach[3*sizeof(cbl_name_t)];
tree names_table_type = build_array_type_nelts(CHAR_P, mol->second.size()+1);
sprintf(ach, "..our_accessible_functions_" HOST_SIZE_T_PRINT_DEC,
(fmt_size_t)caller);
callee != mol->second.end();
callee++ )
{
- sprintf(ach, "%s." HOST_SIZE_T_PRINT_DEC, (*callee)->name,
+ sprintf(ach,
+ "%s." HOST_SIZE_T_PRINT_DEC,
+ (*callee)->name,
(fmt_size_t)(*callee)->parent_node->our_index);
CONSTRUCTOR_APPEND_ELT( CONSTRUCTOR_ELTS(constr_names),
// We have the original nul-terminated text at data.initial. We have a
// copy of it in buffer[] in the internal codeset.
+ static const char name_base[] = "_literal_a_";
+
// We will reuse a single static structure for each string
static std::unordered_map<std::string, int> seen_before;
+
std::string field_string(buffer);
+
+#if 0
+ /* This code is suppoed to re-use literals, and seems to work just fine in
+ x86_64-linux and on an Apple aarch64 M1 Macbook Pro. But on an M1
+ mini, using -Os optimization, attempts were made in the generated
+ assembly language to define _literal_a_1 more than once.
+
+ I didn't know how to try to track this one down, so I decided simply to
+ punt by removing the code.
+
+ I am leaving the code here because of a conviction that it someday should
+ be tracked down. */
+
std::unordered_map<std::string, int>::const_iterator it =
seen_before.find(field_string);
- static const char name_base[] = "_literal_a_";
-
if( it != seen_before.end() )
{
// We've seen that string before.
vs_file_static);
}
else
+#endif
{
// We have not seen that string before
- static int nvar = 1;
+ static int nvar = 0;
+ nvar += 1;
seen_before[field_string] = nvar;
char ach[32];
TREE_USED(field->var_decl_node) = 1;
TREE_STATIC(field->var_decl_node) = 1;
DECL_PRESERVE_P (field->var_decl_node) = 1;
- nvar += 1;
}
// TRACE1
// {
if( !ancestor && (new_var->level > LEVEL01 && new_var->level <= LEVEL49 ) )
{
- cbl_internal_error("%s: %d %qs has NULL ancestor", __func__,
+ cbl_internal_error("%s: %d %qs has NULL ancestor", __func__,
new_var->level, new_var->name);
}
*
* * 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
+ * * 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.
struct cbl_translation_unit_t gg_trans_unit;
+// This set is used to prevent duplicated top-level program names from breaking
+// the compiler when a source code module makes that mistake.
+static std::unordered_set<std::string> names_we_have_seen;
+
+// This vector is used to process the function_decls at the point we leave
+// the file.
+static std::vector<tree> finalized_function_decls;
+
void
gg_build_translation_unit(const char *filename)
{
return retval;
}
-static
char *
-show_type(tree type)
+gg_show_type(tree type)
{
if( !type )
{
- cbl_internal_error("The given type is not NULL, and that is just not fair");
+ cbl_internal_error("The given type is NULL, and that is just not fair");
}
if( DECL_P(type) )
cbl_internal_error("The given type is not a declaration or a TYPE");
}
- static char ach[1024];
+ static char ach[1100];
+ static char ach2[1024];
+ static char ach3[1024];
switch( TREE_CODE(type) )
{
case POINTER_TYPE:
- sprintf(ach, "POINTER");
+ strcpy(ach2, gg_show_type(TREE_TYPE(type)));
+ sprintf(ach, "POINTER to %s", ach2);
break;
case VOID_TYPE:
break;
case FUNCTION_TYPE:
- sprintf(ach, "FUNCTION");
-// sprintf(ach,
-// "%3ld-bit %s INT",
-// TREE_INT_CST_LOW(TYPE_SIZE(type)),
-// (TYPE_UNSIGNED(type) ? "unsigned" : " signed"));
+ strcpy(ach3, gg_show_type(TREE_TYPE(type)));
+ sprintf(ach, "FUNCTION returning %s", ach3);
break;
default:
return ach;
}
-void
+tree
gg_assign(tree dest, const tree source)
{
// This does the equivalent of a C/C++ "dest = source". When X1 is set, it
// This routine also provides for the possibility that the assignment is
// for a source that is a function invocation, as in
// "dest = function_call()"
+ tree stmt = NULL_TREE;
saw_pointer = false;
tree dest_type = adjust_for_type(TREE_TYPE(dest));
if( okay )
{
- tree stmt = build2_loc( location_from_lineno(),
- MODIFY_EXPR,
- TREE_TYPE(dest),
- dest,
- source);
+ stmt = build2_loc(location_from_lineno(),
+ MODIFY_EXPR,
+ TREE_TYPE(dest),
+ dest,
+ source);
gg_append_statement(stmt);
}
else
// the same. This is a compilation-time error, since we want the caller to
// have sorted the types out explicitly. If we don't throw an error here,
// the gimple reduction will do so. Better to do it here, when we know
- // where we are.
- dbgmsg("Inefficient assignment");
- if(DECL_P(dest) && DECL_NAME(dest))
- {
- dbgmsg(" Destination is %s", IDENTIFIER_POINTER(DECL_NAME(dest)));
- }
- dbgmsg(" dest type is %s%s", show_type(dest_type), p2 ? "_P" : "");
- if(DECL_P(source) && DECL_NAME(source))
+ // where we are.S
+ static const int debugging = 1;
+ if( debugging )
{
- dbgmsg(" Source is %s", IDENTIFIER_POINTER(DECL_NAME(source)));
+ fprintf(stderr, "Inefficient assignment\n");
+ if(DECL_P(dest) && DECL_NAME(dest))
+ {
+ fprintf(stderr, " Destination is %s\n", IDENTIFIER_POINTER(DECL_NAME(dest)));
+ }
+ fprintf(stderr, " dest type is %s%s\n", gg_show_type(dest_type), p2 ? "_P" : "");
+ if(DECL_P(source) && DECL_NAME(source))
+ {
+ fprintf(stderr, " Source is %s\n", IDENTIFIER_POINTER(DECL_NAME(source)));
+ }
+ fprintf(stderr, " source type is %s%s\n", gg_show_type(source_type), p2 ? "_P" : "");
}
- dbgmsg(" source type is %s%s", show_type(source_type), p2 ? "_P" : "");
- gcc_unreachable();
+ cbl_internal_error("Attempting an assignment of differing types.");
}
+ return stmt;
}
tree
}
}
-void
-gg_modify_function_type(tree function_decl, tree return_type)
- {
- tree fndecl_type = build_varargs_function_type_array( return_type,
- 0, // No parameters yet
- NULL); // And, hence, no types
- TREE_TYPE(function_decl) = fndecl_type;
- tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type);
- DECL_CONTEXT (resdecl) = function_decl;
- DECL_RESULT (function_decl) = resdecl;
- }
+/* There are five ways that we use function_decls:
-tree
-gg_define_function_with_no_parameters(tree return_type,
- const char *funcname,
- const char *unmangled_name)
- {
- // This routine builds a function_decl, puts it on the stack, and
- // gives it a context.
+ 1, We define a main() entry point.
+ 2. We call a function that turns out to be a static "t" function local to the source code module.
+ 3. We define an global "T" function, and possibly call it later.
+ 4. We call a function that we define later in the source code module.
+ 5. We call a function that ends up being an extern that is not defined in the source code module.
- // At this time we don't know how many parameters this function expects, so
- // we set things up and we'll tack on the parameters later.
+ Cases 3. and 4. turn out to require the same flags. Here are the combinations of
+ flags that are required for each flavor of function_decl. This was empirically
+ determind by compiling a C++ program with sample code for each type.
- // Create the FUNCTION_TYPE for that array:
- // int nparams = 1;
- // tree types[1] = {VOID_P};
- // const char *names[1] = {"_p1"};
+ | addressable | used | nothrow | static | external | public | no_instrument
+main | | | | X | | X | X
+local | X | X | X | X | | | X
+external defined inside | X | X | X | X | | X | X
+external defined elsewhere | X | X | | | X | X |
- // tree fndecl_type = build_varargs_function_type_array( return_type,
- // nparams,
- // types);
+*/
- tree fndecl_type = build_varargs_function_type_array( return_type,
- 0, // No parameters yet
- NULL); // And, hence, no types
- // Create the FUNCTION_DECL for that FUNCTION_TYPE
- tree function_decl = build_fn_decl (funcname, fndecl_type);
+static std::unordered_map<std::string, tree> map_of_function_decls;
- // Some of this stuff is magical, and is based on compiling C programs
- // and just mimicking the results.
- TREE_ADDRESSABLE(function_decl) = 1;
- TREE_STATIC(function_decl) = 1;
- DECL_EXTERNAL (function_decl) = 0;
- DECL_PRESERVE_P (function_decl) = 0;
- DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1;
- DECL_ARTIFICIAL(function_decl) = 0;
- TREE_NOTHROW(function_decl) = 0;
- TREE_USED(function_decl) = 1;
+static
+std::string function_decl_key(const char *funcname, tree fndecl_type)
+ {
+ std::string retval;
+ retval += funcname;
+ retval += gg_show_type(TREE_TYPE(fndecl_type));
+ return retval;
+ }
- // This code makes COBOL nested programs actual visible on the
- // source code "trans_unit_decl" level, but with non-public "static"
- // visibility.
- if( gg_trans_unit.function_stack.size() == 0 )
+tree
+gg_peek_fn_decl(const char *funcname, tree fndecl_type)
+ {
+ // When funcname is found in map_of_function_decls, this routine returns
+ // the type of the return value of that function decl.
+
+ tree retval = NULL_TREE;
+ std::string key = function_decl_key(funcname, fndecl_type);
+ std::unordered_map<std::string, tree>::const_iterator it =
+ map_of_function_decls.find(key);
+ if( it != map_of_function_decls.end() )
{
- // gg_trans_unit.function_stack is empty, so our context is
- // the compilation module, and we need to be public:
- DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl;
- TREE_PUBLIC(function_decl) = 1;
+ // This function_decl has already been defined.
+ retval = TREE_TYPE(TREE_TYPE(it->second));
+ }
+ return retval;
+ }
+
+tree
+gg_build_fn_decl(const char *funcname, tree fndecl_type)
+ {
+ tree function_decl;
+
+ std::string key = function_decl_key(funcname, fndecl_type);
+ std::unordered_map<std::string, tree>::const_iterator it =
+ map_of_function_decls.find(key);
+ if( it != map_of_function_decls.end() )
+ {
+ // This function_decl has already been defined. Just return it; the caller
+ // is responsible for modifying it, if necessary.
+ function_decl = it->second;
}
else
{
- // The stack has something in it, so we are building a nested function.
- // Make the current function our context
- DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl;
- TREE_PUBLIC(function_decl) = 0;
+ // When creating a never-seen function_decl, we default to the type used
+ // for calling a function defined elsewhere. It's up to our caller to
+ // modify the flags, for example if this is part of creating a function.
- // This function is file static, but nobody calls it, so without
- // intervention -O1+ optimizations will discard it.
- DECL_PRESERVE_P (function_decl) = 1;
+ function_decl = build_fn_decl(funcname, fndecl_type);
- // Append this function to the list of functions and variables
- // associated with the computation module.
- gg_append_var_decl(function_decl);
- }
-
- // Establish the RESULT_DECL for the function:
- tree resdecl = build_decl (location_from_lineno(), RESULT_DECL, NULL_TREE, return_type);
- DECL_CONTEXT (resdecl) = function_decl;
- DECL_RESULT (function_decl) = resdecl;
+ // These are the bits shown in the table in the comment up above
+ TREE_ADDRESSABLE(function_decl) = 1;
+ TREE_USED(function_decl) = 1;
+ TREE_NOTHROW(function_decl) = 0;
+ TREE_STATIC(function_decl) = 0;
+ DECL_EXTERNAL (function_decl) = 1;
+ TREE_PUBLIC (function_decl) = 1;
+ DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 0;
- // The function_decl has a .function member, a pointer to struct_function.
- // This is quietly, almost invisibly, extremely important. You need to
- // call this routine after DECL_RESULT has been established:
+ DECL_PRESERVE_P (function_decl) = 0;
+ DECL_ARTIFICIAL(function_decl) = 0;
+ map_of_function_decls[key] = function_decl;
+ }
+ return function_decl;
+ }
- allocate_struct_function(function_decl, false);
+tree
+gg_define_function( tree return_type,
+ const char *funcname,
+ const char *unmangled_name,
+ ...)
+ {
+ // This routine builds a function_decl, puts it on the stack, and
+ // gives it a context.
- struct gg_function_t new_function = {};
- new_function.context_count = 0;
- new_function.function_decl = function_decl;
- new_function.our_name = IDENTIFIER_POINTER(DECL_NAME(function_decl));
- new_function.our_unmangled_name = xstrdup(unmangled_name);
- new_function.function_address = gg_get_function_address(VOID, new_function.our_name);
+ // At this time we don't know how many parameters this function expects, so
+ // we set things up and we'll tack on the parameters later.
- // Each program on the stack gets a unique identifier. This is used, for
- // example, to make sure that static variables have unique names.
- static size_t program_id = 0;
- new_function.program_id_number = program_id++;
+ /* There is some bookkeeping we need to do to avoid crashing.
- // With everything established, put this function_decl on the stack
- gg_trans_unit.function_stack.push_back(new_function);
+ It's possible for the source code to have two top-level functions with
+ the same name. This is a compile-time error, but the GCC processing gets
+ upset when it happens. We'll prevent it from happening here:
- // All we need is a context, and we are ready to go:
- gg_push_context();
- return function_decl;
- }
+ */
-void
-gg_tack_on_function_parameters(tree function_decl, ...)
- {
int nparams = 0;
tree types[ARG_LIMIT];
const char *names[ARG_LIMIT];
va_list params;
- va_start(params, function_decl);
+ va_start(params, unmangled_name);
for(;;)
{
tree var_type = va_arg(params, tree);
nparams += 1;
if(nparams > ARG_LIMIT)
{
- yywarn("%d parameters? Really? Are you insane?",ARG_LIMIT+1);
+ yywarn("%d parameters? Really? Are you insane?", ARG_LIMIT+1);
gcc_unreachable();
}
}
va_end(params);
- // Chain the names onto the variables list:
- for(int i=0; i<nparams; i++)
+ std::unordered_set<std::string>::const_iterator it =
+ names_we_have_seen.find(funcname);
+ if( it != names_we_have_seen.end() )
{
- chain_parameter_to_function(function_decl, types[i], names[i]);
+ static int bum_counter = 1;
+ // We have seen this name before. Replace it with something unique:
+ char ach[32];
+ sprintf(ach, "..no_dupes.%d", bum_counter++);
+ funcname = ach;
}
- }
-
-void
-gg_define_function(tree return_type, const char *funcname, ...)
- {
- // This routine builds a function_decl, puts it on the stack, and
- // gives it a context.
-
- // After the funcname, we expect the formal parameters: pairs of types/names
- // terminated by a NULL_TREE
-
- int nparams = 0;
-
- tree types[ARG_LIMIT];
- const char *names[ARG_LIMIT];
-
- va_list params;
- va_start(params,funcname);
- for(;;)
+ else
{
- tree var_type = va_arg(params, tree);
- if( !var_type )
- {
- break;
- }
-
- if( TREE_CODE(var_type) >= NUM_TREE_CODES)
- {
- // Warning: This test is not completely reliable, because a garbage
- // byte could have a valid TREE_CODE. But it does help.
- yywarn("You forgot to put a %<NULL_TREE%> at the end of a "
- "%<gg_define_function()%> again");
- gcc_unreachable();
- }
-
- const char *name = va_arg(params, const char *);
-
- types[nparams] = var_type;
- names[nparams] = name;
- nparams += 1;
- if(nparams > ARG_LIMIT)
- {
- yywarn("%d parameters? Really? Are you insane?", ARG_LIMIT+1);
- gcc_unreachable();
- }
+ names_we_have_seen.insert(funcname);
}
- va_end(params);
- // Create the FUNCTION_TYPE for that array:
tree fndecl_type = build_varargs_function_type_array( return_type,
nparams,
types);
// Create the FUNCTION_DECL for that FUNCTION_TYPE
- tree function_decl = build_fn_decl (funcname, fndecl_type);
-
- // Some of this stuff is magical, and is based on compiling C programs
- // and just mimicking the results.
- TREE_ADDRESSABLE(function_decl) = 1;
- TREE_STATIC(function_decl) = 1;
- DECL_EXTERNAL (function_decl) = 0;
- DECL_PRESERVE_P (function_decl) = 0;
- DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1;
- DECL_ARTIFICIAL(function_decl) = 0;
- TREE_NOTHROW(function_decl) = 0;
- TREE_USED(function_decl) = 1;
+ tree function_decl = gg_build_fn_decl (funcname, fndecl_type);
// This code makes COBOL nested programs actual visible on the
// source code "trans_unit_decl" level, but with non-public "static"
if( gg_trans_unit.function_stack.size() == 0 )
{
// gg_trans_unit.function_stack is empty, so our context is
- // the compilation module, and we need to be public:
+ // the compilation module, and we need to be public because this is a
+ // top-level function with global scope:
+
+ // These are the bits shown in the table for gg_build_fn_decl()
+ TREE_ADDRESSABLE(function_decl) = 1;
+ TREE_USED(function_decl) = 1;
+ TREE_NOTHROW(function_decl) = 1;
+ TREE_STATIC(function_decl) = 1;
+ DECL_EXTERNAL (function_decl) = 0;
+ TREE_PUBLIC (function_decl) = 1;
+ DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1;
DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl;
- TREE_PUBLIC(function_decl) = 1;
}
else
{
- // The stack has something in it, so we are building a nested function.
- // Make the current function our context
+ // The stack has something in it, so we are building a contained
+ // program-id. Such function are implemented local static functions.
+ //
+ // It's not necessarily true that a static call to such a function will be
+ // part of the source code (the call can be through a variable), and so
+ // optimization routines can decide the function isn't used and can
+ // therefore be optimized away. The preserve flag prevents that.
+
+ // These are the bits shown in the table for gg_build_fn_decl()
+ TREE_ADDRESSABLE(function_decl) = 1;
+ TREE_USED(function_decl) = 1;
+ TREE_NOTHROW(function_decl) = 1;
+ TREE_STATIC(function_decl) = 1;
+ DECL_EXTERNAL (function_decl) = 0;
+ TREE_PUBLIC (function_decl) = 0;
+ DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(function_decl) = 1;
DECL_CONTEXT (function_decl) = gg_trans_unit.trans_unit_decl;
-
- // We need to make it public, because otherwise COBOL CALL "func"
- // won't be able to find it, because dlopen/dlsym won't find it.
- TREE_PUBLIC(function_decl) = 0;
-
- // Append this function to the list of functions and variables
- // associated with the computation module.
+ DECL_CONTEXT(function_decl) = gg_trans_unit.trans_unit_decl;
+ DECL_PRESERVE_P (function_decl) = 1;
gg_append_var_decl(function_decl);
}
struct gg_function_t new_function = {};
new_function.context_count = 0;
new_function.function_decl = function_decl;
+ new_function.our_name = IDENTIFIER_POINTER(DECL_NAME(function_decl));
+ new_function.our_unmangled_name = xstrdup(unmangled_name);
+ new_function.function_address = gg_get_address_of(function_decl);
// Each program on the stack gets a unique identifier. This is used, for
// example, to make sure that static variables have unique names.
// All we need is a context, and we are ready to go:
gg_push_context();
+ return function_decl;
+ }
+
+void
+gg_modify_function_type(tree function_decl, tree return_type)
+ {
+ tree fndecl_type = build_varargs_function_type_array( return_type,
+ 0, // No parameters yet
+ NULL); // And, hence, no types
+ TREE_TYPE(function_decl) = fndecl_type;
+ tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type);
+ DECL_CONTEXT (resdecl) = function_decl;
+ DECL_RESULT (function_decl) = resdecl;
}
tree
// Finish off the context
gg_pop_context();
- if( gg_trans_unit.function_stack.back().is_truly_nested )
- {
- // This code is for true nested functions.
-
- ///////// DANGER, WILL ROBINSON!
- ///////// This is all well and good. It does not, however, work.
- ///////// I tried to implement it because I had a Brilliant Idea for
- ///////// building COBOL paragraphs in a way that would easily allow
- ///////// the GDB "NEXT" command to step over a PERFORM <paragraph>.
- ///////// But, alas, I realized that it was just not going to work.
- /////////
- ///////// Pity.
- /////////
- ///////// But at that point, I was here, and I am leaving this uncooked
- ///////// code in case I someday want to return to it. If it becomes
- ///////// your job, rather than mine, I encourage you to write a C
- ///////// program that uses the GNU extensions that allow true nested
- ///////// functions, and reverse engineer the "finish_function"
- ///////// function, and get it working.
- /////////
- ///////// Good luck. Bob Dubner, 2022-08-13
-
- // Because this is a nested function, let's make sure that it actually
- // has a function that it is nested within
- gcc_assert(gg_trans_unit.function_stack.size() > 1 );
-
- /* Genericize before inlining. Delay genericizing nested functions
- until their parent function is genericized. Since finalizing
- requires GENERIC, delay that as well. */
-
- // This is the comment in gcc/c/c-decl.c:
-
- /* Register this function with cgraph just far enough to get it
- added to our parent's nested function list. Handy, since the
- C front end does not have such a list. */
-
- static cgraph_node *node = cgraph_node::get_create (current_function->function_decl);
- gcc_assert(node);
-
- }
- else
- {
- // This makes the function visible on the source code module level.
- cgraph_node::finalize_function (current_function->function_decl, true);
- }
+ /* Because COBOL functions can be misleadingly referenced before they
+ defined, and because our compiler is single pass, we need to defer
+ actually passing the function_decls to the middle end until we are
+ done with the entire compilation unit.
+
+ An actual example:
+
+ IDENTIFICATION DIVISION.
+ PROGRAM-ID. A.
+ DATA DIVISION.
+ WORKING-STORAGE SECTION.
+ 01 CWD PIC X(100).
+ 01 LEN_OF_CWD PIC 999 VALUE 100.
+ PROCEDURE DIVISION.
+ CALL "getcwd" USING BY REFERENCE CWD BY VALUE LEN_OF_CWD
+ DISPLAY CWD
+ goback.
+ END PROGRAM A.
+ IDENTIFICATION DIVISION.
+ PROGRAM-ID. B.
+ DATA DIVISION.
+ WORKING-STORAGE SECTION.
+ 01 CWD PIC X(100).
+ 01 RETURNED-CWD PIC X(100).
+ 01 LEN_OF_CWD PIC 999 VALUE 100.
+ PROCEDURE DIVISION.
+ CALL "getcwd" USING BY REFERENCE CWD BY VALUE LEN_OF_CWD RETURNING RETURNED-CWD
+ DISPLAY RETURNED-CWD
+ goback.
+ END PROGRAM B.
+
+ When we encounter the first call to getcwd, we have no clue as to the
+ type of the return value, so we assume it is COBOL_FUNCTION_RETURN_TYPE
+
+ When we encounter the second call, we learn that it returns CHAR_P. But
+ an attempt to change the return type of the function_decl will result
+ in problems if the function_decl of A is processed by the middle end
+ before we get a chance to change the getcwd functiona_decl.
+
+ Hence the need for finalized_function_decls, which gets processed
+ at the end of the file. */
+
+ finalized_function_decls.push_back(current_function->function_decl);
dump_function (TDI_original, current_function->function_decl);
gg_trans_unit.function_stack.pop_back();
}
+void
+gg_leaving_the_source_code_file()
+ {
+ for( std::vector<tree>::const_iterator it=finalized_function_decls.begin();
+ it != finalized_function_decls.end();
+ it++ )
+ {
+ //This makes the function visible on the source code module level.
+ cgraph_node::finalize_function(*it, true);
+ }
+ }
+
void
gg_push_context()
{
}
tree
-gg_call_expr_list(tree return_type, tree function_name, int param_count, tree args[])
+gg_call_expr_list(tree return_type, tree function_pointer, int param_count, tree args[])
{
// Generalized caller. param_count is the count of params in the arg[]]
tree the_call = build_call_array_loc(location_from_lineno(),
return_type,
- function_name,
+ function_pointer,
param_count,
args);
// This routine returns the call_expr; the caller will have to deal with it
return NULL_TREE;
}
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
+
void
gg_insert_into_assembler(const char ach[])
{
gg_insert_into_assembler(ach);
}
}
+
+#pragma GCC diagnostic pop
\ No newline at end of file
// logical way: All programs are siblings, with the context being the source
// code module. The nested aspect is not reflected in the GENERIC tree.
- // Truly nested functions are implemented within the generic tree; the
- // nested function is completely inside the outer function. This was
- // implemented to support paragraphs as callable entities.
- bool is_truly_nested;
-
// This variable, which appears on the stack, contains the exit_address
// for the terminating proc of a PERFORM A or PERFORM A THROUGH B
tree perform_exit_address;
extern tree gg_cast(tree type, tree var);
// Assignment, that is to say, A = B
-extern void gg_assign(tree dest, const tree source);
+extern tree gg_assign(tree dest, const tree source);
// struct creation and field access
// Create struct, and access a field in a struct
extern void gg_return(tree operand = NULL_TREE);
// These routines are the preample and postamble that bracket everything else
-extern void gg_define_function(tree return_type, const char *funcname, ...);
-extern tree gg_define_function_with_no_parameters(tree return_type,
- const char *funcname,
- const char *unmangled_name);
+extern tree gg_build_fn_decl(const char *funcname, tree fndecl_type);
+extern tree gg_peek_fn_decl(const char *funcname);
+extern tree gg_define_function( tree return_type,
+ const char *funcname,
+ const char *unmangled_name,
+ ...);
extern void chain_parameter_to_function( tree function_decl,
const tree param_type,
const char *name);
+extern void gg_modify_function_type(tree function_decl, tree return_type);
extern void gg_finalize_function();
extern void gg_push_context();
// These are a generalized call constructor. The first for when you just want
// the function called, because you don't care about the return value. The others
// are for when you do need the return value.
-extern tree gg_call_expr_list(tree return_type, tree function_name, int param_count, tree[]);
+extern tree gg_call_expr_list(tree return_type,
+ tree function_pointer,
+ int param_count, tree[]);
// The following is a garden-variety call, with known return type and known
// but in the case where the return value is unimportant.
void gg_record_statement_list_start();
tree gg_record_statement_list_finish();
-// These routines are in support of PERFORM PARAGRAPH
-extern tree gg_get_function_decl(tree return_type, const char *funcname, ...);
-
// Used to call system exit()
extern void gg_exit(tree exit_code);
extern void gg_abort();
extern tree gg_string_literal(const char *string);
#define CURRENT_LINE_NUMBER (cobol_location().first_line)
-location_t location_from_lineno();
+extern location_t location_from_lineno();
// When set to true, use UNKNOWN_LOCATION instead of CURRENT_LINE_NUMBER
extern void gg_set_current_line_number(int line_number);
extern tree gg_trans_unit_var_decl(const char *var_name);
-tree gg_open(tree char_star_A, tree int_B);
-tree gg_close(tree int_A);
-tree gg_get_indirect_reference(tree pointer, tree offset);
+extern tree gg_open(tree char_star_A, tree int_B);
+extern tree gg_close(tree int_A);
+extern tree gg_get_indirect_reference(tree pointer, tree offset);
-void gg_insert_into_assembler(const char ach[]);
-void gg_insert_into_assemblerf(const char *format, ...) ATTRIBUTE_PRINTF_1;
+extern void gg_insert_into_assembler(const char ach[]);
+extern void gg_insert_into_assemblerf(const char *format, ...) ATTRIBUTE_PRINTF_1;
-void gg_modify_function_type(tree function_decl, tree return_type);
+extern char *gg_show_type(tree type);
+extern void gg_leaving_the_source_code_file();
#endif
return output;
}
if( erc == -1 ) {
- yywarn("'%s' was accepted as %zu", input, integer);
+ yywarn("'%s' was accepted as %ld", input, integer);
}
return output;
}
* subprograms, and whether or not they are COMMON. PROGRAM may be
* the caller, or a subprogram could call COMMON sibling.
*/
+
+ static std::unordered_set<size_t> callers_we_have_seen;
if( programs.size() == 1 ) {
if( yydebug ) parser_call_targets_dump();
for( size_t caller : symbol_program_programs() ) {
+ // We are running through the entire growing list of called programs
+ // at the point of each END PROGRAM. This confuses the name changing
+ // routines, so we use a std::set to avoid doing callers more than
+ // once.
+ if( callers_we_have_seen.find(caller) != callers_we_have_seen.end() )
+ {
+ continue;
+ }
const char *caller_name = cbl_label_of(symbol_at(caller))->name;
for( auto callable : symbol_program_callables(caller) ) {
auto called = cbl_label_of(symbol_at(callable));
called->mangled_name? called->mangled_name : called->name;
size_t n =
- parser_call_target_update(caller, called->name, mangled_name);
+ parser_call_target_update(caller,
+ called->name,
+ mangled_name);
// Zero is not an error
dbgmsg("updated " HOST_SIZE_T_PRINT_UNSIGNED
" calls from #%-3" GCC_PRISZ "u (%s) s/%s/%s/",
(fmt_size_t)n, (fmt_size_t)caller, caller_name,
called->name, mangled_name);
}
+ callers_we_have_seen.insert(caller);
}
if( yydebug ) parser_call_targets_dump();
}