From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 43040 invoked by alias); 4 Nov 2019 08:28:19 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 43030 invoked by uid 89); 4 Nov 2019 08:28:19 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-16.4 required=5.0 tests=AWL,BAYES_50,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_SHORT,KAM_STOCKGEN,SPF_PASS autolearn=ham version=3.3.1 spammy=gmbh, H*Ad:U*ebotcazou, UD:gimplify.c, gimplify.c X-HELO: mx1.suse.de Received: from mx2.suse.de (HELO mx1.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 04 Nov 2019 08:28:14 +0000 Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 20792B3FF; Mon, 4 Nov 2019 08:28:12 +0000 (UTC) Date: Mon, 04 Nov 2019 08:28:00 -0000 From: Richard Biener To: Alexandre Oliva cc: gcc-patches@gcc.gnu.org, ebotcazou@adacore.com, joseph@codesourcery.com Subject: Re: introduce -fcallgraph-info option In-Reply-To: Message-ID: References: <84223B6B-F6F0-4B94-835E-6661E1E1EB6A@suse.de> User-Agent: Alpine 2.21 (LSU 202 2017-01-01) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="-1609908220-233976510-1572856092=:5566" X-SW-Source: 2019-11/txt/msg00084.txt.bz2 This message is in MIME format. The first part should be readable text, while the remaining parts are likely unreadable without MIME-aware tools. ---1609908220-233976510-1572856092=:5566 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8BIT Content-length: 30056 On Sat, 2 Nov 2019, Alexandre Oliva wrote: > On Oct 30, 2019, Richard Biener wrote: > > > One way of operation would be to > > generate the graph during the compilation step > > Stack usage is only computed during prologue/epilogue generation in RTL. > > > Additional arguments to -fcallgraph-info might be used to direct the > > output to a specific directory as well. > > I've adjusted open_auxiliary_file for LTO recompilation auxiliary files > to be placed in the same location as the specified executable output > name, and noted that in the documentation. Please leave that part out for now, I'd rather discuss this separately from the bulk of the patch. That is, I wonder why we shouldn't simply adjust aux_base_name to something else for -flto [in the driver]. > I've also added a bitmap to output nodes for externals, accidentally > dropped in the transition to incremental generation of the .ci file. > > Regstrapped on x86_64-linux-gnu. Ok to install? OK for the rest. Thanks, Richard. > > introduce -fcallgraph-info option > > This was first submitted many years ago > https://gcc.gnu.org/ml/gcc-patches/2010-10/msg02468.html > > The command line option -fcallgraph-info is added and makes the > compiler generate another output file (xxx.ci) for each compilation > unit (or LTO partitoin), which is a valid VCG file (you can launch > your favorite VCG viewer on it unmodified) and contains the "final" > callgraph of the unit. "final" is a bit of a misnomer as this is > actually the callgraph at RTL expansion time, but since most > high-level optimizations are done at the Tree level and RTL doesn't > usually fiddle with calls, it's final in almost all cases. Moreover, > the nodes can be decorated with additional info: -fcallgraph-info=su > adds stack usage info and -fcallgraph-info=da dynamic allocation info. > > > for gcc/ChangeLog > From Eric Botcazou , Alexandre Oliva > > * common.opt (-fcallgraph-info[=]): New option. > * doc/invoke.texi (Developer options): Document it. > * opts.c (common_handle_option): Handle it. > * builtins.c (expand_builtin_alloca): Record allocation if > -fcallgraph-info=da. > * calls.c (expand_call): If -fcallgraph-info, record the call. > (emit_library_call_value_1): Likewise. > * flag-types.h (enum callgraph_info_type): New type. > * explow.c: Include stringpool.h. > (set_stack_check_libfunc): Set SET_SYMBOL_REF_DECL on the symbol. > * function.c (allocate_stack_usage_info): New. > (allocate_struct_function): Call it for -fcallgraph-info. > (prepare_function_start): Call it otherwise. > (record_final_call, record_dynamic_alloc): New. > * function.h (struct callinfo_callee): New. > (CALLEE_FROM_CGRAPH_P): New. > (struct callinfo_dalloc): New. > (struct stack_usage): Add callees and dallocs. > (record_final_call, record_dynamic_alloc): Declare. > * gimplify.c (gimplify_decl_expr): Record dynamically-allocated > object if -fcallgraph-info=da. > * optabs-libfuncs.c (build_libfunc_function): Keep SYMBOL_REF_DECL. > * print-tree.h (print_decl_identifier): Declare. > (PRINT_DECL_ORIGIN, PRINT_DECL_NAME, PRINT_DECL_UNIQUE_NAME): New. > * print-tree.c: Include print-tree.h. > (print_decl_identifier): New function. > * toplev.c: Include print-tree.h. > (callgraph_info_file): New global variable. > (callgraph_info_external_printed): Likewise. > (open_auxiliary_file): Use dump_base_name for LTO partitions. > (output_stack_usage): Rename to... > (output_stack_usage_1): ... this. Make it static, add cf > parameter. If -fcallgraph-info=su, print stack usage to cf. > If -fstack-usage, use print_decl_identifier for > pretty-printing. > (INDIRECT_CALL_NAME): New. > (dump_final_node_vcg_start): New. > (dump_final_callee_vcg, dump_final_node_vcg): New. > (output_stack_usage): New. > (lang_dependent_init): Open and start file if > -fcallgraph-info. Allocated callgraph_info_external_printed. > (finalize): If callgraph_info_file is not null, finish it, > close it, and release callgraph_info_external_printed. > > for gcc/ada/ChangeLog > > * gcc-interface/misc.c (callgraph_info_file): Delete. > --- > gcc/ada/gcc-interface/misc.c | 3 - > gcc/builtins.c | 4 + > gcc/calls.c | 6 + > gcc/common.opt | 8 ++ > gcc/doc/invoke.texi | 22 +++++ > gcc/explow.c | 5 + > gcc/flag-types.h | 16 ++++ > gcc/function.c | 59 ++++++++++++- > gcc/function.h | 30 +++++++ > gcc/gimplify.c | 4 + > gcc/optabs-libfuncs.c | 4 - > gcc/opts.c | 26 ++++++ > gcc/output.h | 2 > gcc/print-tree.c | 76 +++++++++++++++++ > gcc/print-tree.h | 4 + > gcc/toplev.c | 186 ++++++++++++++++++++++++++++++++++-------- > 16 files changed, 402 insertions(+), 53 deletions(-) > > diff --git a/gcc/ada/gcc-interface/misc.c b/gcc/ada/gcc-interface/misc.c > index 4abd4d5..d68b373 100644 > --- a/gcc/ada/gcc-interface/misc.c > +++ b/gcc/ada/gcc-interface/misc.c > @@ -54,9 +54,6 @@ > #include "ada-tree.h" > #include "gigi.h" > > -/* This symbol needs to be defined for the front-end. */ > -void *callgraph_info_file = NULL; > - > /* Command-line argc and argv. These variables are global since they are > imported in back_end.adb. */ > unsigned int save_argc; > diff --git a/gcc/builtins.c b/gcc/builtins.c > index 5d811f1..bd30238 100644 > --- a/gcc/builtins.c > +++ b/gcc/builtins.c > @@ -5406,6 +5406,10 @@ expand_builtin_alloca (tree exp) > = allocate_dynamic_stack_space (op0, 0, align, max_size, alloca_for_var); > result = convert_memory_address (ptr_mode, result); > > + /* Dynamic allocations for variables are recorded during gimplification. */ > + if (!alloca_for_var && (flag_callgraph_info & CALLGRAPH_INFO_DYNAMIC_ALLOC)) > + record_dynamic_alloc (exp); > + > return result; > } > > diff --git a/gcc/calls.c b/gcc/calls.c > index e2b770f..6292135 100644 > --- a/gcc/calls.c > +++ b/gcc/calls.c > @@ -3759,6 +3759,9 @@ expand_call (tree exp, rtx target, int ignore) > > preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT; > > + if (flag_callgraph_info) > + record_final_call (fndecl, EXPR_LOCATION (exp)); > + > /* We want to make two insn chains; one for a sibling call, the other > for a normal call. We will select one of the two chains after > initial RTL generation is complete. */ > @@ -5343,6 +5346,9 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, > > before_call = get_last_insn (); > > + if (flag_callgraph_info) > + record_final_call (SYMBOL_REF_DECL (orgfun), UNKNOWN_LOCATION); > + > /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which > will set inhibit_defer_pop to that value. */ > /* The return type is needed to decide how many bytes the function pops. > diff --git a/gcc/common.opt b/gcc/common.opt > index cc279f4..299eac6 100644 > --- a/gcc/common.opt > +++ b/gcc/common.opt > @@ -1091,6 +1091,14 @@ fbtr-bb-exclusive > Common Ignore > Does nothing. Preserved for backward compatibility. > > +fcallgraph-info > +Common Report RejectNegative Var(flag_callgraph_info) Init(NO_CALLGRAPH_INFO); > +Output callgraph information on a per-file basis. > + > +fcallgraph-info= > +Common Report RejectNegative Joined > +Output callgraph information on a per-file basis with decorations. > + > fcall-saved- > Common Joined RejectNegative Var(common_deferred_options) Defer > -fcall-saved- Mark as being preserved across functions. > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index faa7fa9..266021c 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -584,8 +584,9 @@ Objective-C and Objective-C++ Dialects}. > @item Developer Options > @xref{Developer Options,,GCC Developer Options}. > @gccoptlist{-d@var{letters} -dumpspecs -dumpmachine -dumpversion @gol > --dumpfullversion -fchecking -fchecking=@var{n} -fdbg-cnt-list @gol > --fdbg-cnt=@var{counter-value-list} @gol > +-dumpfullversion -fcallgraph-info@r{[}=su,da@r{]} > +-fchecking -fchecking=@var{n} > +-fdbg-cnt-list @gol -fdbg-cnt=@var{counter-value-list} @gol > -fdisable-ipa-@var{pass_name} @gol > -fdisable-rtl-@var{pass_name} @gol > -fdisable-rtl-@var{pass-name}=@var{range-list} @gol > @@ -14564,6 +14565,23 @@ The files are created in the directory of the output file. > > @table @gcctabopt > > +@item -fcallgraph-info > +@itemx -fcallgraph-info=@var{MARKERS} > +@opindex fcallgraph-info > +Makes the compiler output callgraph information for the program, on a > +per-object-file basis. The information is generated in the common VCG format. > +It can be decorated with additional, per-node and/or per-edge information, > +if a list of comma-separated markers is additionally specified. When the > +@code{su} marker is specified, the callgraph is decorated with stack usage > +information; it is equivalent to @option{-fstack-usage}. When the @code{da} > +marker is specified, the callgraph is decorated with information about > +dynamically allocated objects. > + > +When compiling with @option{-flto}, no callgraph information is output > +along with the object file. At LTO link time, @option{-fcallgraph-info} > +may generate multiple callgraph information files next to the specified > +output file. > + > @item -d@var{letters} > @itemx -fdump-rtl-@var{pass} > @itemx -fdump-rtl-@var{pass}=@var{filename} > diff --git a/gcc/explow.c b/gcc/explow.c > index 7eb854b..83c7863 100644 > --- a/gcc/explow.c > +++ b/gcc/explow.c > @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see > #include "dojump.h" > #include "explow.h" > #include "expr.h" > +#include "stringpool.h" > #include "common/common-target.h" > #include "output.h" > #include "params.h" > @@ -1611,6 +1612,10 @@ set_stack_check_libfunc (const char *libfunc_name) > { > gcc_assert (stack_check_libfunc == NULL_RTX); > stack_check_libfunc = gen_rtx_SYMBOL_REF (Pmode, libfunc_name); > + tree decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, > + get_identifier (libfunc_name), void_type_node); > + DECL_EXTERNAL (decl) = 1; > + SET_SYMBOL_REF_DECL (stack_check_libfunc, decl); > } > > /* Emit one stack probe at ADDRESS, an address within the stack. */ > diff --git a/gcc/flag-types.h b/gcc/flag-types.h > index a210328..b23d3a2 100644 > --- a/gcc/flag-types.h > +++ b/gcc/flag-types.h > @@ -200,6 +200,22 @@ enum stack_check_type > FULL_BUILTIN_STACK_CHECK > }; > > +/* Type of callgraph information. */ > +enum callgraph_info_type > +{ > + /* No information. */ > + NO_CALLGRAPH_INFO = 0, > + > + /* Naked callgraph. */ > + CALLGRAPH_INFO_NAKED = 1, > + > + /* Callgraph decorated with stack usage information. */ > + CALLGRAPH_INFO_STACK_USAGE = 2, > + > + /* Callgraph decoration with dynamic allocation information. */ > + CALLGRAPH_INFO_DYNAMIC_ALLOC = 4 > +}; > + > /* Floating-point contraction mode. */ > enum fp_contract_mode { > FP_CONTRACT_OFF = 0, > diff --git a/gcc/function.c b/gcc/function.c > index a1c76a4..3f79a38 100644 > --- a/gcc/function.c > +++ b/gcc/function.c > @@ -4725,6 +4725,16 @@ get_last_funcdef_no (void) > return funcdef_no; > } > > +/* Allocate and initialize the stack usage info data structure for the > + current function. */ > +static void > +allocate_stack_usage_info (void) > +{ > + gcc_assert (!cfun->su); > + cfun->su = ggc_cleared_alloc (); > + cfun->su->static_stack_size = -1; > +} > + > /* Allocate a function structure for FNDECL and set its contents > to the defaults. Set cfun to the newly-allocated object. > Some of the helper functions invoked during initialization assume > @@ -4802,6 +4812,9 @@ allocate_struct_function (tree fndecl, bool abstract_p) > > if (!profile_flag && !flag_instrument_function_entry_exit) > DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl) = 1; > + > + if (flag_callgraph_info) > + allocate_stack_usage_info (); > } > > /* Don't enable begin stmt markers if var-tracking at assignments is > @@ -4846,11 +4859,8 @@ prepare_function_start (void) > init_expr (); > default_rtl_profile (); > > - if (flag_stack_usage_info) > - { > - cfun->su = ggc_cleared_alloc (); > - cfun->su->static_stack_size = -1; > - } > + if (flag_stack_usage_info && !flag_callgraph_info) > + allocate_stack_usage_info (); > > cse_not_expected = ! optimize; > > @@ -6373,12 +6383,49 @@ rest_of_handle_thread_prologue_and_epilogue (void) > cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0); > > /* The stack usage info is finalized during prologue expansion. */ > - if (flag_stack_usage_info) > + if (flag_stack_usage_info || flag_callgraph_info) > output_stack_usage (); > > return 0; > } > > +/* Record a final call to CALLEE at LOCATION. */ > + > +void > +record_final_call (tree callee, location_t location) > +{ > + if (!callee || CALLEE_FROM_CGRAPH_P (callee)) > + return; > + > + struct callinfo_callee datum = { location, callee }; > + vec_safe_push (cfun->su->callees, datum); > +} > + > +/* Record a dynamic allocation made for DECL_OR_EXP. */ > + > +void > +record_dynamic_alloc (tree decl_or_exp) > +{ > + struct callinfo_dalloc datum; > + > + if (DECL_P (decl_or_exp)) > + { > + datum.location = DECL_SOURCE_LOCATION (decl_or_exp); > + const char *name = lang_hooks.decl_printable_name (decl_or_exp, 2); > + const char *dot = strrchr (name, '.'); > + if (dot) > + name = dot + 1; > + datum.name = ggc_strdup (name); > + } > + else > + { > + datum.location = EXPR_LOCATION (decl_or_exp); > + datum.name = NULL; > + } > + > + vec_safe_push (cfun->su->dallocs, datum); > +} > + > namespace { > > const pass_data pass_data_thread_prologue_and_epilogue = > diff --git a/gcc/function.h b/gcc/function.h > index 43ac5dff..14794c4 100644 > --- a/gcc/function.h > +++ b/gcc/function.h > @@ -192,6 +192,23 @@ public: > poly_int64 length; > }; > > +/* Describe emitted builtin calls for -fcallgraph-info. Those that > + are not builtin are taken from cgraph edges. */ > +struct GTY(()) callinfo_callee > +{ > + location_t location; > + tree decl; > +}; > +#define CALLEE_FROM_CGRAPH_P(T) \ > + (!fndecl_built_in_p (T) && !DECL_IS_BUILTIN (T)) > + > +/* Describe dynamic allocation for -fcallgraph-info=da. */ > +struct GTY(()) callinfo_dalloc > +{ > + location_t location; > + char const *name; > +}; > + > class GTY(()) stack_usage > { > public: > @@ -210,6 +227,13 @@ public: > /* Nonzero if the amount of stack space allocated dynamically cannot > be bounded at compile-time. */ > unsigned int has_unbounded_dynamic_stack_size : 1; > + > + /* Functions called within the function, if callgraph is enabled. */ > + vec *callees; > + > + /* Dynamic allocations encountered within the function, if callgraph > + da is enabled. */ > + vec *dallocs; > }; > > #define current_function_static_stack_size (cfun->su->static_stack_size) > @@ -406,6 +430,12 @@ void add_local_decl (struct function *fun, tree d); > #define FOR_EACH_LOCAL_DECL(FUN, I, D) \ > FOR_EACH_VEC_SAFE_ELT_REVERSE ((FUN)->local_decls, I, D) > > +/* Record a final call to CALLEE at LOCATION. */ > +void record_final_call (tree callee, location_t location); > + > +/* Record a dynamic allocation made for DECL_OR_EXP. */ > +void record_dynamic_alloc (tree decl_or_exp); > + > /* If va_list_[gf]pr_size is set to this, it means we don't know how > many units need to be saved. */ > #define VA_LIST_MAX_GPR_SIZE 255 > diff --git a/gcc/gimplify.c b/gcc/gimplify.c > index 12ed3f8..74fc45a 100644 > --- a/gcc/gimplify.c > +++ b/gcc/gimplify.c > @@ -1698,6 +1698,10 @@ gimplify_vla_decl (tree decl, gimple_seq *seq_p) > t = build2 (MODIFY_EXPR, TREE_TYPE (addr), addr, t); > > gimplify_and_add (t, seq_p); > + > + /* Record the dynamic allocation associated with DECL if requested. */ > + if (flag_callgraph_info & CALLGRAPH_INFO_DYNAMIC_ALLOC) > + record_dynamic_alloc (decl); > } > > /* A helper function to be called via walk_tree. Mark all labels under *TP > diff --git a/gcc/optabs-libfuncs.c b/gcc/optabs-libfuncs.c > index ef43dae..8916f7e4 100644 > --- a/gcc/optabs-libfuncs.c > +++ b/gcc/optabs-libfuncs.c > @@ -735,10 +735,6 @@ build_libfunc_function_visibility (const char *name, symbol_visibility vis) > DECL_VISIBILITY_SPECIFIED (decl) = 1; > gcc_assert (DECL_ASSEMBLER_NAME (decl)); > > - /* Zap the nonsensical SYMBOL_REF_DECL for this. What we're left with > - are the flags assigned by targetm.encode_section_info. */ > - SET_SYMBOL_REF_DECL (XEXP (DECL_RTL (decl), 0), NULL); > - > return decl; > } > > diff --git a/gcc/opts.c b/gcc/opts.c > index 10b9f10..f46b468 100644 > --- a/gcc/opts.c > +++ b/gcc/opts.c > @@ -2433,6 +2433,32 @@ common_handle_option (struct gcc_options *opts, > /* Deferred. */ > break; > > + case OPT_fcallgraph_info: > + opts->x_flag_callgraph_info = CALLGRAPH_INFO_NAKED; > + break; > + > + case OPT_fcallgraph_info_: > + { > + char *my_arg, *p; > + my_arg = xstrdup (arg); > + p = strtok (my_arg, ","); > + while (p) > + { > + if (strcmp (p, "su") == 0) > + { > + opts->x_flag_callgraph_info |= CALLGRAPH_INFO_STACK_USAGE; > + opts->x_flag_stack_usage_info = true; > + } > + else if (strcmp (p, "da") == 0) > + opts->x_flag_callgraph_info |= CALLGRAPH_INFO_DYNAMIC_ALLOC; > + else > + return 0; > + p = strtok (NULL, ","); > + } > + free (my_arg); > + } > + break; > + > case OPT_fdiagnostics_show_location_: > diagnostic_prefixing_rule (dc) = (diagnostic_prefixing_rule_t) value; > break; > diff --git a/gcc/output.h b/gcc/output.h > index 835d635..6cccada 100644 > --- a/gcc/output.h > +++ b/gcc/output.h > @@ -604,7 +604,7 @@ extern int maybe_assemble_visibility (tree); > > extern int default_address_cost (rtx, machine_mode, addr_space_t, bool); > > -/* Output stack usage information. */ > +/* Stack usage. */ > extern void output_stack_usage (void); > > #endif /* ! GCC_OUTPUT_H */ > diff --git a/gcc/print-tree.c b/gcc/print-tree.c > index 6dcbb2d..bd09ec4 100644 > --- a/gcc/print-tree.c > +++ b/gcc/print-tree.c > @@ -1035,6 +1035,82 @@ print_node (FILE *file, const char *prefix, tree node, int indent, > fprintf (file, ">"); > } > > +/* Print the identifier for DECL according to FLAGS. */ > + > +void > +print_decl_identifier (FILE *file, tree decl, int flags) > +{ > + bool needs_colon = false; > + const char *name; > + char c; > + > + if (flags & PRINT_DECL_ORIGIN) > + { > + if (DECL_IS_BUILTIN (decl)) > + fputs ("", file); > + else > + { > + expanded_location loc > + = expand_location (DECL_SOURCE_LOCATION (decl)); > + fprintf (file, "%s:%d:%d", loc.file, loc.line, loc.column); > + } > + needs_colon = true; > + } > + > + if (flags & PRINT_DECL_UNIQUE_NAME) > + { > + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); > + if (!TREE_PUBLIC (decl) > + || (DECL_WEAK (decl) && !DECL_EXTERNAL (decl))) > + /* The symbol has internal or weak linkage so its assembler name > + is not necessarily unique among the compilation units of the > + program. We therefore have to further mangle it. But we can't > + simply use DECL_SOURCE_FILE because it contains the name of the > + file the symbol originates from so, e.g. for function templates > + in C++ where the templates are defined in a header file, we can > + have symbols with the same assembler name and DECL_SOURCE_FILE. > + That's why we use the name of the top-level source file of the > + compilation unit. ??? Unnecessary for Ada. */ > + name = ACONCAT ((main_input_filename, ":", name, NULL)); > + } > + else if (flags & PRINT_DECL_NAME) > + { > + /* We don't want to print the full qualified name because it can be long, > + so we strip the scope prefix, but we may need to deal with the suffix > + created by the compiler. */ > + const char *suffix = strchr (IDENTIFIER_POINTER (DECL_NAME (decl)), '.'); > + name = lang_hooks.decl_printable_name (decl, 2); > + if (suffix) > + { > + const char *dot = strchr (name, '.'); > + while (dot && strcasecmp (dot, suffix) != 0) > + { > + name = dot + 1; > + dot = strchr (name, '.'); > + } > + } > + else > + { > + const char *dot = strrchr (name, '.'); > + if (dot) > + name = dot + 1; > + } > + } > + else > + return; > + > + if (needs_colon) > + fputc (':', file); > + > + while ((c = *name++) != '\0') > + { > + /* Strip double-quotes because of VCG. */ > + if (c == '"') > + continue; > + fputc (c, file); > + } > +} > + > > /* Print the node NODE on standard error, for debugging. > Most nodes referred to by this one are printed recursively > diff --git a/gcc/print-tree.h b/gcc/print-tree.h > index 1d4fe6e..cbea48c 100644 > --- a/gcc/print-tree.h > +++ b/gcc/print-tree.h > @@ -42,5 +42,9 @@ extern void print_node (FILE *, const char *, tree, int, > extern void print_node_brief (FILE *, const char *, const_tree, int); > extern void indent_to (FILE *, int); > #endif > +#define PRINT_DECL_ORIGIN 0x1 > +#define PRINT_DECL_NAME 0x2 > +#define PRINT_DECL_UNIQUE_NAME 0x4 > +extern void print_decl_identifier (FILE *, tree, int flags); > > #endif // GCC_PRINT_TREE_H > diff --git a/gcc/toplev.c b/gcc/toplev.c > index 00a5e83..8aaf216 100644 > --- a/gcc/toplev.c > +++ b/gcc/toplev.c > @@ -84,6 +84,7 @@ along with GCC; see the file COPYING3. If not see > #include "dumpfile.h" > #include "ipa-fnsummary.h" > #include "dump-context.h" > +#include "print-tree.h" > #include "optinfo-emit-json.h" > > #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) > @@ -174,6 +175,8 @@ const char *user_label_prefix; > > FILE *asm_out_file; > FILE *aux_info_file; > +FILE *callgraph_info_file = NULL; > +static bitmap callgraph_info_external_printed; > FILE *stack_usage_file = NULL; > > /* The current working directory of a translation. It's generally the > @@ -913,8 +916,8 @@ alloc_for_identifier_to_locale (size_t len) > } > > /* Output stack usage information. */ > -void > -output_stack_usage (void) > +static void > +output_stack_usage_1 (FILE *cf) > { > static bool warning_issued = false; > enum stack_usage_kind_type { STATIC = 0, DYNAMIC, DYNAMIC_BOUNDED }; > @@ -970,41 +973,17 @@ output_stack_usage (void) > stack_usage += current_function_dynamic_stack_size; > } > > + if (flag_callgraph_info & CALLGRAPH_INFO_STACK_USAGE) > + fprintf (cf, "\\n" HOST_WIDE_INT_PRINT_DEC " bytes (%s)", > + stack_usage, > + stack_usage_kind_str[stack_usage_kind]); > + > if (stack_usage_file) > { > - expanded_location loc > - = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); > - /* We don't want to print the full qualified name because it can be long, > - so we strip the scope prefix, but we may need to deal with the suffix > - created by the compiler. */ > - const char *suffix > - = strchr (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)), '.'); > - const char *name > - = lang_hooks.decl_printable_name (current_function_decl, 2); > - if (suffix) > - { > - const char *dot = strchr (name, '.'); > - while (dot && strcasecmp (dot, suffix) != 0) > - { > - name = dot + 1; > - dot = strchr (name, '.'); > - } > - } > - else > - { > - const char *dot = strrchr (name, '.'); > - if (dot) > - name = dot + 1; > - } > - > - fprintf (stack_usage_file, > - "%s:%d:%d:%s\t" HOST_WIDE_INT_PRINT_DEC"\t%s\n", > - loc.file == NULL ? "(artificial)" : lbasename (loc.file), > - loc.line, > - loc.column, > - name, > - stack_usage, > - stack_usage_kind_str[stack_usage_kind]); > + print_decl_identifier (stack_usage_file, current_function_decl, > + PRINT_DECL_ORIGIN | PRINT_DECL_NAME); > + fprintf (stack_usage_file, "\t" HOST_WIDE_INT_PRINT_DEC"\t%s\n", > + stack_usage, stack_usage_kind_str[stack_usage_kind]); > } > > if (warn_stack_usage >= 0 && warn_stack_usage < HOST_WIDE_INT_MAX) > @@ -1026,14 +1005,127 @@ output_stack_usage (void) > } > } > > -/* Open an auxiliary output file. */ > +/* Dump placeholder node for indirect calls in VCG format. */ > + > +#define INDIRECT_CALL_NAME "__indirect_call" > + > +static void > +dump_final_node_vcg_start (FILE *f, tree decl) > +{ > + fputs ("node: { title: \"", f); > + if (decl) > + print_decl_identifier (f, decl, PRINT_DECL_UNIQUE_NAME); > + else > + fputs (INDIRECT_CALL_NAME, f); > + fputs ("\" label: \"", f); > + if (decl) > + { > + print_decl_identifier (f, decl, PRINT_DECL_NAME); > + fputs ("\\n", f); > + print_decl_identifier (f, decl, PRINT_DECL_ORIGIN); > + } > + else > + fputs ("Indirect Call Placeholder", f); > +} > + > +/* Dump final cgraph edge in VCG format. */ > + > +static void > +dump_final_callee_vcg (FILE *f, location_t location, tree callee) > +{ > + if ((!callee || DECL_EXTERNAL (callee)) > + && bitmap_set_bit (callgraph_info_external_printed, > + callee ? DECL_UID (callee) + 1 : 0)) > + { > + dump_final_node_vcg_start (f, callee); > + fputs ("\" shape : ellipse }\n", f); > + } > + > + fputs ("edge: { sourcename: \"", f); > + print_decl_identifier (f, current_function_decl, PRINT_DECL_UNIQUE_NAME); > + fputs ("\" targetname: \"", f); > + if (callee) > + print_decl_identifier (f, callee, PRINT_DECL_UNIQUE_NAME); > + else > + fputs (INDIRECT_CALL_NAME, f); > + if (LOCATION_LOCUS (location) != UNKNOWN_LOCATION) > + { > + expanded_location loc; > + fputs ("\" label: \"", f); > + loc = expand_location (location); > + fprintf (f, "%s:%d:%d", loc.file, loc.line, loc.column); > + } > + fputs ("\" }\n", f); > +} > + > +/* Dump final cgraph node in VCG format. */ > + > +static void > +dump_final_node_vcg (FILE *f) > +{ > + dump_final_node_vcg_start (f, current_function_decl); > + > + if (flag_stack_usage_info > + || (flag_callgraph_info & CALLGRAPH_INFO_STACK_USAGE)) > + output_stack_usage_1 (f); > + > + if (flag_callgraph_info & CALLGRAPH_INFO_DYNAMIC_ALLOC) > + { > + fprintf (f, "\\n%u dynamic objects", vec_safe_length (cfun->su->dallocs)); > + > + unsigned i; > + callinfo_dalloc *cda; > + FOR_EACH_VEC_SAFE_ELT (cfun->su->dallocs, i, cda) > + { > + expanded_location loc = expand_location (cda->location); > + fprintf (f, "\\n %s", cda->name); > + fprintf (f, " %s:%d:%d", loc.file, loc.line, loc.column); > + } > + > + vec_free (cfun->su->dallocs); > + cfun->su->dallocs = NULL; > + } > + > + fputs ("\" }\n", f); > + > + unsigned i; > + callinfo_callee *c; > + FOR_EACH_VEC_SAFE_ELT (cfun->su->callees, i, c) > + dump_final_callee_vcg (f, c->location, c->decl); > + vec_free (cfun->su->callees); > + cfun->su->callees = NULL; > + > + cgraph_node *cnode = cgraph_node::get (current_function_decl); > + for (cgraph_edge *e = cnode->callees; e; e = e->next_callee) > + if (CALLEE_FROM_CGRAPH_P (e->callee->decl)) > + dump_final_callee_vcg (f, gimple_location (e->call_stmt), > + e->callee->decl); > + for (cgraph_edge *e = cnode->indirect_calls; e; e = e->next_callee) > + dump_final_callee_vcg (f, gimple_location (e->call_stmt), NULL); > +} > + > +/* Output stack usage and callgraph info, as requested. */ > +void > +output_stack_usage (void) > +{ > + if (flag_callgraph_info) > + dump_final_node_vcg (callgraph_info_file); > + else > + output_stack_usage_1 (NULL); > +} > + > +/* Open an auxiliary or dump output file. */ > static FILE * > open_auxiliary_file (const char *ext) > { > char *filename; > FILE *file; > + const char *base_name = aux_base_name; > > - filename = concat (aux_base_name, ".", ext, NULL); > + if (in_lto_p) > + base_name = dump_base_name; > + > + filename = concat (base_name, ".", ext, NULL); > file = fopen (filename, "w"); > if (!file) > fatal_error (input_location, "cannot open %s for writing: %m", filename); > @@ -1900,6 +1992,17 @@ lang_dependent_init (const char *name) > /* If stack usage information is desired, open the output file. */ > if (flag_stack_usage && !flag_generate_lto) > stack_usage_file = open_auxiliary_file ("su"); > + > + /* If call graph information is desired, open the output file. */ > + if (flag_callgraph_info && !flag_generate_lto) > + { > + callgraph_info_file = open_auxiliary_file ("ci"); > + /* Write the file header. */ > + fprintf (callgraph_info_file, > + "graph: { title: \"%s\"\n", main_input_filename); > + bitmap_obstack_initialize (NULL); > + callgraph_info_external_printed = BITMAP_ALLOC (NULL); > + } > } > > /* This creates various _DECL nodes, so needs to be called after the > @@ -2053,6 +2156,15 @@ finalize (bool no_backend) > stack_usage_file = NULL; > } > > + if (callgraph_info_file) > + { > + fputs ("}\n", callgraph_info_file); > + fclose (callgraph_info_file); > + callgraph_info_file = NULL; > + BITMAP_FREE (callgraph_info_external_printed); > + bitmap_obstack_release (NULL); > + } > + > if (seen_error ()) > coverage_remove_note_file (); > > > > -- Richard Biener SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg) ---1609908220-233976510-1572856092=:5566--