On October 26, 2019 6:35:43 AM GMT+02:00, Alexandre Oliva <ol...@adacore.com> wrote: >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, 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. > >Compared with the earlier version, this patch does not modify cgraph, >and instead adds the required information next to the stage usage >function data structure, so we only hold one of those at at time. I've >switched to vecs from linked lists, for more compact edges and dynamic >allocation annotations, and arranged for them to be released as soon as >we've printed out the information. I have NOT changed the file format, >because existing tools such as gnatstack consume the current format. > >Regstrapped on x86_64-linux-gnu. Ok to install?
How does it relate to the LTO-dump utility we have now which can in theory provide a more complete view? Maybe some infrastructure can be shared here (the actual dumping of the cgraph?) Thanks, Richard. > >for gcc/ChangeLog >From Eric Botcazou <ebotca...@adacore.com>, Alexandre Oliva ><ol...@adacore.com> > > * common.opt (-fcallgraph-info[=]): New option. > * doc/invoke.texi (Debugging 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. > (rest_of_handle_thread_prologue_and_epilogue): Release callees > and dallocs after output_stack_usage. > (record_final_call, record_dynamic_alloc): New. > * function.h (struct callee, struct 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_indirect_emitted): Likewise. > (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_indirect_call_node_vcg): New. > (dump_final_callee_vcg, dump_final_node_vcg): New. > (output_stack_usage): New. > (lang_dependent_init): Open and start file if > -fcallgraph-info. > (finalize): If callgraph_info_file is not null, finish it, > close it, and reset callgraph info state. > >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 | 17 ++++ > gcc/explow.c | 5 + > gcc/flag-types.h | 16 ++++ > gcc/function.c | 63 ++++++++++++++-- > gcc/function.h | 25 ++++++ > 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 | 169 >++++++++++++++++++++++++++++++++++-------- > 16 files changed, 381 insertions(+), 51 deletions(-) > >diff --git a/gcc/ada/gcc-interface/misc.c >b/gcc/ada/gcc-interface/misc.c >index 4abd4d5708a54..d68b37384ff7f 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 5d811f113c907..bd302383377ba 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 ae904473d0dc6..67c7c77598a3f 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 cc279f411d796..63d646fba2b42 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-<register> Mark <register> as being preserved across >functions. >diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi >index 1407d019d1404..545b842eade71 100644 >--- a/gcc/doc/invoke.texi >+++ b/gcc/doc/invoke.texi >@@ -583,8 +583,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 >@@ -14533,6 +14534,18 @@ 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-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. >+ > @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 7eb854bca4a6d..83c786366c1aa 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 a2103282d469d..b23d3a271f1ee 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 a1c76a4dd7a84..152f927097c47 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<stack_usage> (); >+ 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<stack_usage> (); >- 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,51 @@ 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) >- output_stack_usage (); >+ if (flag_stack_usage_info || flag_callgraph_info) >+ { >+ output_stack_usage (); >+ vec_free (cfun->su->dallocs); >+ cfun->su->dallocs = NULL; >+ vec_free (cfun->su->callees); >+ cfun->su->callees = NULL; >+ } > > return 0; > } > >+/* Record a final call to CALLEE at LOCATION. */ >+ >+void >+record_final_call (tree callee, location_t location) >+{ >+ struct 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 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 43ac5dffd2457..ec523765d6eec 100644 >--- a/gcc/function.h >+++ b/gcc/function.h >@@ -192,6 +192,18 @@ public: > poly_int64 length; > }; > >+struct GTY(()) callee >+{ >+ location_t location; >+ tree decl; >+}; >+ >+struct GTY(()) dalloc >+{ >+ location_t location; >+ char const *name; >+}; >+ > class GTY(()) stack_usage > { > public: >@@ -210,6 +222,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<callee, va_gc> *callees; >+ >+ /* Dynamic allocations enocuntered within the function, if callgraph >+ da is enabled. */ >+ vec<dalloc, va_gc> *dallocs; > }; > >#define current_function_static_stack_size >(cfun->su->static_stack_size) >@@ -406,6 +425,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 914bb8eb8d699..394f0fda9c98c 100644 >--- a/gcc/gimplify.c >+++ b/gcc/gimplify.c >@@ -1697,6 +1697,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 ef43dae12fb64..8916f7e4da0bb 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 10b9f108f8d06..f46b468a968e7 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 835d63556e6a8..6cccada4aeb1d 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 6dcbb2dcb1369..bd09ec4d7a7af 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 ("<built-in>", 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 1d4fe6e8950cc..cbea48c486e3c 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 1c7002f5c37cd..016f6d688f324 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 bool callgraph_info_indirect_emitted = false; > 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,22 @@ output_stack_usage (void) > stack_usage += current_function_dynamic_stack_size; > } > >- if (stack_usage_file) >+ if (flag_callgraph_info & CALLGRAPH_INFO_STACK_USAGE) > { >- 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, '.'); >- } >- } >+ if (stack_usage) >+ fprintf (cf, "\\n" HOST_WIDE_INT_PRINT_DEC " bytes (%s)", >+ stack_usage, >+ stack_usage_kind_str[stack_usage_kind]); > else >- { >- const char *dot = strrchr (name, '.'); >- if (dot) >- name = dot + 1; >- } >+ fputs ("\\n0 bytes", cf); >+ } > >- 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]); >+ if (stack_usage_file) >+ { >+ 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,6 +1010,106 @@ output_stack_usage (void) > } > } > >+/* Dump placeholder node for indirect calls in VCG format. */ >+ >+#define INDIRECT_CALL_NAME "__indirect_call" >+ >+static void >+dump_final_indirect_call_node_vcg (FILE *f) >+{ >+ if (callgraph_info_indirect_emitted) >+ return; >+ >+ fputs ("node: { title: \"", f); >+ fputs (INDIRECT_CALL_NAME, f); >+ fputs ("\" label: \"", f); >+ fputs ("Indirect Call Placeholder", f); >+ fputs ("\" shape : ellipse }\n", f); >+ callgraph_info_indirect_emitted = true; >+} >+ >+/* Dump final cgraph edge in VCG format. */ >+ >+static void >+dump_final_callee_vcg (FILE *f, struct callee *callee) >+{ >+ fputs ("edge: { sourcename: \"", f); >+ print_decl_identifier (f, current_function_decl, >PRINT_DECL_UNIQUE_NAME); >+ fputs ("\" targetname: \"", f); >+ if (callee->decl) >+ print_decl_identifier (f, callee->decl, PRINT_DECL_UNIQUE_NAME); >+ else >+ fputs (INDIRECT_CALL_NAME, f); >+ if (LOCATION_LOCUS (callee->location) != UNKNOWN_LOCATION) >+ { >+ expanded_location loc; >+ fputs ("\" label: \"", f); >+ loc = expand_location (callee->location); >+ fprintf (f, "%s:%d:%d", loc.file, loc.line, loc.column); >+ } >+ fputs ("\" }\n", f); >+ >+ if (!callee->decl) >+ dump_final_indirect_call_node_vcg (f); >+} >+ >+/* Dump final cgraph node in VCG format. */ >+ >+static void >+dump_final_node_vcg (FILE *f) >+{ >+ fputs ("node: { title: \"", f); >+ print_decl_identifier (f, current_function_decl, >PRINT_DECL_UNIQUE_NAME); >+ fputs ("\" label: \"", f); >+ print_decl_identifier (f, current_function_decl, PRINT_DECL_NAME); >+ fputs ("\\n", f); >+ print_decl_identifier (f, current_function_decl, PRINT_DECL_ORIGIN); >+ >+ if (DECL_EXTERNAL (current_function_decl)) >+ { >+ fputs ("\" shape : ellipse }\n", f); >+ return; >+ } >+ >+ 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; >+ 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); >+ } >+ } >+ >+ fputs ("\" }\n", f); >+ >+ if (flag_callgraph_info) >+ { >+ unsigned i; >+ callee *c; >+ FOR_EACH_VEC_SAFE_ELT (cfun->su->callees, i, c) >+ dump_final_callee_vcg (f, c); >+ } >+} >+ >+/* 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 output file. */ > static FILE * > open_auxiliary_file (const char *ext) >@@ -1900,6 +1984,15 @@ 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); >+ } > } > > /* This creates various _DECL nodes, so needs to be called after the >@@ -2044,6 +2137,14 @@ 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; >+ callgraph_info_indirect_emitted = false; >+ } >+ > if (seen_error ()) > coverage_remove_note_file (); >