introduce -fcallgraph-info option

classic Classic list List threaded Threaded
43 messages Options
123
Reply | Threaded
Open this post in threaded view
|

introduce -fcallgraph-info option

Alexandre Oliva-4
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?


for  gcc/ChangeLog
From  Eric Botcazou  <[hidden email]>, Alexandre Oliva  <[hidden email]>

        * 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 ();
 

--
Alexandre Oliva, freedom fighter  he/him   https://FSFLA.org/blogs/lxo
Be the change, be Free!        FSF VP & FSF Latin America board member
GNU Toolchain Engineer                        Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás - Che GNUevara
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Richard Biener
On October 26, 2019 6:35:43 AM GMT+02:00, Alexandre Oliva <[hidden email]> 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  <[hidden email]>, Alexandre Oliva
><[hidden email]>
>
> * 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 ();
>

Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
Hi, Richi,

On Oct 26, 2019, Richard Biener <[hidden email]> wrote:

> How does it relate to the LTO-dump utility we have now which can in
> theory provide a more complete view?

Erhm...  Not at all, AFAICT.  The only vague resemblance I see is in the
presence of the word "callgraph" in the description of what both can do,
but even that's not quite the same callgraph, besides the different
format.

E.g., the reason we gather expanded calls rather than just use
cgraph_edges is that the latter would dump several "calls" that are
builtins expanded internally by the compiler, and would NOT dump other
ops that are expanded as (lib)calls.  In order to get an accurate
assessment of stack size requirements, the presence of the builtins
could be confusing but not so much trouble, but the absence of libcalls
would underestimate the potential max total stack use by a symbol.

> Maybe some infrastructure can be
> shared here (the actual dumping of the cgraph?)

You mean the one-liner loop in cgraph_node::dump_graphviz, called by
lto-dump to dump the cgraph?  That doesn't really seem worth sharing;
more so considering we dump edges in a quite different format, and not
just the edges.  In this different format expected by gnatstack, we also
dump the nodes, and include information in the labels of the nodes, such
as their original spelling and location, as well as stack use:

node: { title: "_ada_p" label: "P\np.adb:1:1\n48 bytes (static)" }

and dynamic stack allocations (alloca and vlas):

node: { title: "p.adb:p__u" label: "u\np.adb:21:3\n2 dynamic objects\n rt p.adb:23:5\n ri p.adb:24:5" }

and though edges to libcalls may carry just as little info as a graphviz
"from" -> "to" edge:

edge: { sourcename: "add" targetname: "__addvsi3" }

those between user symbols carry location info as well:

edge: { sourcename: "p__s" targetname: "p.adb:p__v" label: "p.adb:46:5" }

So I'm afraid I don't see anything that could be usefully factored out.
Do you?

Thanks,

--
Alexandre Oliva, freedom fighter  he/him   https://FSFLA.org/blogs/lxo
Be the change, be Free!        FSF VP & FSF Latin America board member
GNU Toolchain Engineer                        Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás - Che GNUevara
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
On Oct 26, 2019, Alexandre Oliva <[hidden email]> wrote:

> E.g., the reason we gather expanded calls rather than just use
> cgraph_edges is that the latter would dump several "calls" that are
> builtins expanded internally by the compiler, and would NOT dump other
> ops that are expanded as (lib)calls.

It occurred to me that we could reuse most cgraph edges and avoid
duplicating them in the callees vec, namely those that aren't builtins
or libcalls.  Those that are builtins might or might not become actual
calls, so we disregard the cgraph_edges and record them in the vec
instead.  Those that are libcalls (builtins in a different sense)
introduced during expand are not even present in the cgraph edges, so we
record them in the vec as well.  Here's the patch that implements this.

Regstrapped on x86_64-linux-gnu.  Ok to install?

for  gcc/ChangeLog
From  Eric Botcazou  <[hidden email]>, Alexandre Oliva  <[hidden email]>

        * 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.
        (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_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               |   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                 |  179 ++++++++++++++++++++++++++++++++++--------
 16 files changed, 393 insertions(+), 50 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..3f79a38aeaec8 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,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 43ac5dffd2457..14794c420a215 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<callinfo_callee, va_gc> *callees;
+
+  /* Dynamic allocations encountered within the function, if callgraph
+     da is enabled.  */
+  vec<callinfo_dalloc, va_gc> *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 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..4589bc09fb0c7 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,116 @@ 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, location_t location, tree callee)
+{
+  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);
+
+  if (!callee)
+    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;
+      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 output file.  */
 static FILE *
 open_auxiliary_file (const char *ext)
@@ -1900,6 +1994,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 +2147,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 ();
 


--
Alexandre Oliva, freedom fighter  he/him   https://FSFLA.org/blogs/lxo
Be the change, be Free!        FSF VP & FSF Latin America board member
GNU Toolchain Engineer                        Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás - Che GNUevara
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Richard Biener
On Sun, 27 Oct 2019, Alexandre Oliva wrote:

> On Oct 26, 2019, Alexandre Oliva <[hidden email]> wrote:
>
> > E.g., the reason we gather expanded calls rather than just use
> > cgraph_edges is that the latter would dump several "calls" that are
> > builtins expanded internally by the compiler, and would NOT dump other
> > ops that are expanded as (lib)calls.
>
> It occurred to me that we could reuse most cgraph edges and avoid
> duplicating them in the callees vec, namely those that aren't builtins
> or libcalls.  Those that are builtins might or might not become actual
> calls, so we disregard the cgraph_edges and record them in the vec
> instead.  Those that are libcalls (builtins in a different sense)
> introduced during expand are not even present in the cgraph edges, so we
> record them in the vec as well.  Here's the patch that implements this.
>
> Regstrapped on x86_64-linux-gnu.  Ok to install?

<...>

> --- 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.

I guess you need to elaborate on 'per-file'.  With LTO as far as I
understand you'll get the graph per LTRANS unit (did you check
where the output is generated?).

Is this mainly a debugging tool or does it serve a different purpose?

Otherwise OK.

Thanks,
Richard.

> +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.
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Joseph Myers
In reply to this post by Alexandre Oliva-4
On Sat, 26 Oct 2019, Alexandre Oliva wrote:

> Regstrapped on x86_64-linux-gnu.  Ok to install?

I have only a peripheral comment:

> 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

We have a test in the testsuite that all option help text consistently
ends with '.' (see gcc.misc-tests/help.exp).  I'd have expected these
options, lacking that '.', to cause that test to fail.

--
Joseph S. Myers
[hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
On Oct 28, 2019, Joseph Myers <[hidden email]> wrote:

> We have a test in the testsuite that all option help text consistently
> ends with '.' (see gcc.misc-tests/help.exp).  I'd have expected these
> options, lacking that '.', to cause that test to fail.

Thanks.  I've fixed the common.opt changes, and I'll submit momentarily
a patch for help.exp to extend it to cover --help=common output as well.

--
Alexandre Oliva, freedom fighter  he/him   https://FSFLA.org/blogs/lxo
Be the change, be Free!        FSF VP & FSF Latin America board member
GNU Toolchain Engineer                        Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás - Che GNUevara
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
In reply to this post by Joseph Myers
On Oct 28, 2019, Joseph Myers <[hidden email]> wrote:

> We have a test in the testsuite that all option help text consistently
> ends with '.' (see gcc.misc-tests/help.exp).  I'd have expected these
> options, lacking that '.', to cause that test to fail.

Here's the patch.  Tested on x86_64-linux-gnu.  Ok to install?


Test --help=common for full sentences

The portion of help.exp that checks that help output contains full
sentences failed to cover common options.


for  gcc/testsuite/ChangeLog

        * gcc.misc-tests/help.exp: Test --help=common for full sentences.
---
 gcc/testsuite/gcc.misc-tests/help.exp |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/gcc/testsuite/gcc.misc-tests/help.exp b/gcc/testsuite/gcc.misc-tests/help.exp
index b8a09fc..4bb359f 100644
--- a/gcc/testsuite/gcc.misc-tests/help.exp
+++ b/gcc/testsuite/gcc.misc-tests/help.exp
@@ -146,8 +146,7 @@ check_for_options c "--help=joined,undocumented" "" "" ""
 # find the source a failure.
 
 foreach cls { "ada" "c" "c++" "d" "fortran" "go" \
-    "optimizers" "param" "target" "warnings" } {
-
+ "common" "optimizers" "param" "target" "warnings" } {
     check_for_options c "--help=$cls" "" "^ +-.*\[^:.\]$" ""
 }
 


--
Alexandre Oliva, freedom fighter  he/him   https://FSFLA.org/blogs/lxo
Be the change, be Free!        FSF VP & FSF Latin America board member
GNU Toolchain Engineer                        Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás - Che GNUevara
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
In reply to this post by Richard Biener
On Oct 28, 2019, Richard Biener <[hidden email]> wrote:

> I guess you need to elaborate on 'per-file'.  With LTO as far as I
> understand you'll get the graph per LTRANS unit (did you check
> where the output is generated?).

Yeah, I guess this was not designed with LTO in mind; it probably even
predates LTO.  We get per-LTRANS unit indeed, and the output is
generated in the temporary dir, which is not desirable behavior for
sure.  The outputs seem to be usable if you can figure out what they
are, but I'm not sure how to go about combining the multiple .ci files,
or how to name the combined output, since it's not generally expected
that these files will be created at link time, rather than at compile
time.  I'll bring this up internally and come back with some
improvement.

> Is this mainly a debugging tool or does it serve a different purpose?

It feeds gnatstack, that's a tool to compute max stack depth and perform
other call graph analyzes.  I don't think of it as a debugging tool.

https://www.adacore.com/gnatpro/toolsuite/gnatstack
http://docs.adacore.com/live/wave/gnatstack/html/gnatstack_ug/index.html

--
Alexandre Oliva, freedom fighter  he/him   https://FSFLA.org/blogs/lxo
Be the change, be Free!        FSF VP & FSF Latin America board member
GNU Toolchain Engineer                        Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás - Che GNUevara
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Richard Biener
On Wed, 30 Oct 2019, Alexandre Oliva wrote:

> On Oct 28, 2019, Richard Biener <[hidden email]> wrote:
>
> > I guess you need to elaborate on 'per-file'.  With LTO as far as I
> > understand you'll get the graph per LTRANS unit (did you check
> > where the output is generated?).
>
> Yeah, I guess this was not designed with LTO in mind; it probably even
> predates LTO.  We get per-LTRANS unit indeed, and the output is
> generated in the temporary dir, which is not desirable behavior for
> sure.  The outputs seem to be usable if you can figure out what they
> are, but I'm not sure how to go about combining the multiple .ci files,
> or how to name the combined output, since it's not generally expected
> that these files will be created at link time, rather than at compile
> time.  I'll bring this up internally and come back with some
> improvement.
>
> > Is this mainly a debugging tool or does it serve a different purpose?
>
> It feeds gnatstack, that's a tool to compute max stack depth and perform
> other call graph analyzes.  I don't think of it as a debugging tool.
>
> https://www.adacore.com/gnatpro/toolsuite/gnatstack
> http://docs.adacore.com/live/wave/gnatstack/html/gnatstack_ug/index.html

Ah, thanks for clarification.  One way of operation would be to
generate the graph during the compilation step even with LTO though
it then becomes much less precise.  Note that during LTRANS you
could get at the original file via the DECL_SOURCE_LOCATION of
the TRANSLATION_UNIT_DECL each function is ultimately rooted in
so there's the vague possibility to annotate the graph accordingly
to help combining the output.  Additional arguments to
-fcallgraph-info might be used to direct the output to a specific
directory as well.

Richard.

Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Joseph Myers
In reply to this post by Alexandre Oliva-4
On Wed, 30 Oct 2019, Alexandre Oliva wrote:

> On Oct 28, 2019, Joseph Myers <[hidden email]> wrote:
>
> > We have a test in the testsuite that all option help text consistently
> > ends with '.' (see gcc.misc-tests/help.exp).  I'd have expected these
> > options, lacking that '.', to cause that test to fail.
>
> Here's the patch.  Tested on x86_64-linux-gnu.  Ok to install?
>
>
> Test --help=common for full sentences
>
> The portion of help.exp that checks that help output contains full
> sentences failed to cover common options.

OK.

--
Joseph S. Myers
[hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
In reply to this post by Richard Biener
On Oct 30, 2019, Richard Biener <[hidden email]> 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.

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?


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  <[hidden email]>, Alexandre Oliva  <[hidden email]>

        * 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-<register> Mark <register> 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<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,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<callinfo_callee, va_gc> *callees;
+
+  /* Dynamic allocations encountered within the function, if callgraph
+     da is enabled.  */
+  vec<callinfo_dalloc, va_gc> *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 ("<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 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 ();
 


--
Alexandre Oliva, freedom fighter  he/him   https://FSFLA.org/blogs/lxo
Be the change, be Free!        FSF VP & FSF Latin America board member
GNU Toolchain Engineer                        Free Software Evangelist
Hay que enGNUrecerse, pero sin perder la terGNUra jamás - Che GNUevara
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Richard Biener
On Sat, 2 Nov 2019, Alexandre Oliva wrote:

> On Oct 30, 2019, Richard Biener <[hidden email]> 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  <[hidden email]>, Alexandre Oliva  <[hidden email]>
>
> * 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-<register> Mark <register> 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<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,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<callinfo_callee, va_gc> *callees;
> +
> +  /* Dynamic allocations encountered within the function, if callgraph
> +     da is enabled.  */
> +  vec<callinfo_dalloc, va_gc> *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 ("<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 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 <[hidden email]>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
On Nov  4, 2019, Richard Biener <[hidden email]> wrote:

> 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].

*nod*, that makes sense to me.  After seeing your suggestion, I started
looking into how to do that, but didn't get very far yet.  For now, I've
split that bit out of the main patch.  So I'm installing the first, big
one, and not installing the latter, posted mainly so that the
documentation bit can be picked up.  Thanks!


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  <[hidden email]>, Alexandre Oliva  <[hidden email]>

        * 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.
        (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          |   23 +++++
 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                 |  178 ++++++++++++++++++++++++++++++++++--------
 16 files changed, 397 insertions(+), 51 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-<register> Mark <register> as being preserved across functions.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index faa7fa9..179452c 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,24 @@ 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 intermediate
+LTO output files.
+
 @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<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,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<callinfo_callee, va_gc> *callees;
+
+  /* Dynamic allocations encountered within the function, if callgraph
+     da is enabled.  */
+  vec<callinfo_dalloc, va_gc> *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 ("<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 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..18fea1c 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,6 +1005,115 @@ output_stack_usage (void)
     }
 }
 
+/* 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 output file.  */
 static FILE *
 open_auxiliary_file (const char *ext)
@@ -1900,6 +1988,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 +2152,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 ();
 


improve and document -flto -fcallgraph-info

From: Alexandre Oliva <[hidden email]>

for  gcc/ChangeLog

        * toplev.c (open_auxiliary_file): Use dump_base_name for LTO
        partitions.
        * doc/invoke.texi (-fcallgraph-info): Adjust.
---
 gcc/doc/invoke.texi |    4 ++--
 gcc/toplev.c        |    8 ++++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 179452c..b7011b7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -14580,8 +14580,8 @@ 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 intermediate
-LTO output files.
+may generate multiple callgraph information files next to the specified
+output file.
 
 @item -d@var{letters}
 @itemx -fdump-rtl-@var{pass}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 18fea1c..8aaf216 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1114,14 +1114,18 @@ output_stack_usage (void)
     output_stack_usage_1 (NULL);
 }
 
-/* Open an auxiliary output file.  */
+/* 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);


--
Alexandre Oliva, freedom fighter   he/him   https://FSFLA.org/blogs/lxo
Free Software Evangelist           Stallman was right, but he's left :(
GNU Toolchain Engineer    FSMatrix: It was he who freed the first of us
FSF & FSFLA board member                The Savior shall return (true);
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
In reply to this post by Richard Biener
On Nov  4, 2019, Richard Biener <[hidden email]> wrote:

> I wonder why we shouldn't simply adjust aux_base_name to something
> else for -flto [in the driver].

About that, having tried to make sense of the current uses of
aux_base_name and of lto-wrapper, three main possibilities occur to me:

a) adjust the driver code to accept -auxbase, and have lto-wrapper
explicitly pass -aux-base ${output_dir-.}/$(lbasename ${output_name}) or
somesuch for each -fltrans command;

b) introduce -auxdir and get lto-wrapper to pass -auxdir ${output_dir-.}
for each -fltrans (and offload) command; or

c) get -fltrans to implicitly adjust aux_base_name with the directory
passed to -dumpdir, if any, or . otherwise

Any preferences?

--
Alexandre Oliva, freedom fighter   he/him   https://FSFLA.org/blogs/lxo
Free Software Evangelist           Stallman was right, but he's left :(
GNU Toolchain Engineer    FSMatrix: It was he who freed the first of us
FSF & FSFLA board member                The Savior shall return (true);
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Thomas Schwinge-8
In reply to this post by Alexandre Oliva-4
Hi Alexandre!

On 2019-10-26T01:35:43-0300, Alexandre Oliva <[hidden email]> 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

Yay, for such analysis tools!  :-)


But I'm curious:

> which is a valid VCG file (you can launch your favorite VCG
> viewer on it unmodified)

What should be my "favorite VCG viewer"?

Google lead me to
<http://www.rw.cdl.uni-saarland.de/users/sander/html/gsvcg1.html>, where
I downloaded 'vcg.20050204.tgz' (which I understand is the 1995 sources
with just some licensing changed?), which I managed to build, but which
fails to run on a simple file:

    Wait....................e*** stack smashing detected ***: <unknown> terminated
    Aborted (core dumped)

I found the patch from
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=152128#64> to cure
that one problem.

(It seems that vcg is not packaged in Debian/Ubuntu anymore, nowadays?)

> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi

> +@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.

Eh, "common VCG format" -- maybe common in the mid-90s?  ;-)

> +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.

I tried that, but 'xvcg' didn't render anything useful for a
'-fcallgraph-info=su,da' dump, hmm.


Also, I found that many years ago, in 2012, Steven Bosscher did "Rework
RTL CFG graph dumping to dump DOT format" (that's Graphviz), and then did
"remove vcg CFG dumper".

Note that I'm not actively objecting VCG if there's a good reason to use
unmaintained mid-90s software, containing obfuscated layout/rendering
source code (as far as I understand), not really in spirit of its GPL
license?  (But I'm not a lawyer, of course.)

So I guess I'm just curious why it's now coming back.


Grüße
 Thomas

signature.asc (671 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Richard Biener
In reply to this post by Alexandre Oliva-4
On Wed, 6 Nov 2019, Alexandre Oliva wrote:

> On Nov  4, 2019, Richard Biener <[hidden email]> wrote:
>
> > I wonder why we shouldn't simply adjust aux_base_name to something
> > else for -flto [in the driver].
>
> About that, having tried to make sense of the current uses of
> aux_base_name and of lto-wrapper, three main possibilities occur to me:
>
> a) adjust the driver code to accept -auxbase, and have lto-wrapper
> explicitly pass -aux-base ${output_dir-.}/$(lbasename ${output_name}) or
> somesuch for each -fltrans command;
>
> b) introduce -auxdir and get lto-wrapper to pass -auxdir ${output_dir-.}
> for each -fltrans (and offload) command; or
>
> c) get -fltrans to implicitly adjust aux_base_name with the directory
> passed to -dumpdir, if any, or . otherwise
>
> Any preferences?

A simple test shows we currently only pass -auxbase-strip /tmp/cc...o
to the LTRANS lto1 invocation plus -dumpbase cc...o
Even with -save-temps this doesn't "improve" unless you have an
explicit output via -o

So isn't one of the main issues that the naming of the auxiliar
files is based on some "temporary" file?  IMHO those should always
behave like if we passed -save-temps but I'm not sure how to achieve
that.  For example -dumpdir and -dumpbase seem to work like this
already so there must be code somewhere doing this and adjusting
-auxbase[-strip] to work the same way would make sense to me.

Doesn't really solve the case for "no output name given" (aka a.out
default) though - I suppose for dumping and auxiliar file naming
purpose we should simply assume -o a.out there?

Thanks,
Richard.
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
On Nov  7, 2019, Richard Biener <[hidden email]> wrote:

> A simple test shows we currently only pass -auxbase-strip /tmp/cc...o
> to the LTRANS lto1 invocation plus -dumpbase cc...o

This -auxbase-strip argument is introduced by the gcc driver that runs
lto1, based on the -o (tmp ltrans .o) argument, but this driver has no
clue as to the executable name or location, and there's nothing
whatsoever in the driver interface that enables aux_base_name to be
located separately from the output name.  Thus the possibilities I
brought up of introducing means for it to be told so, explicitly or by
convention.

--
Alexandre Oliva, freedom fighter   he/him   https://FSFLA.org/blogs/lxo
Free Software Evangelist           Stallman was right, but he's left :(
GNU Toolchain Engineer    FSMatrix: It was he who freed the first of us
FSF & FSFLA board member                The Savior shall return (true);
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Alexandre Oliva-4
In reply to this post by Thomas Schwinge-8
On Nov  6, 2019, Thomas Schwinge <[hidden email]> wrote:

>> which is a valid VCG file (you can launch your favorite VCG
>> viewer on it unmodified)

> What should be my "favorite VCG viewer"?

-ENOCLUE, I'm afraid.  I honestly don't even know which one Eric used
back when he first attempted to contribute this feature, almost 10 years
ago.

What I do know is that visualization is not the primary goal.  There are
indeed newer and more elaborate and modern graph file formats for that.
The primary intended consumer of this output is gnatstack, that's long
used only this simple format.  It's hard to justify rewriting it and
creating an incompatibility when the simple format does the job well.

Plus, it's simple enough and regular enough that it should be quite easy
to parse it with a few lines of awk and post-process the .ci file into
any other graph format of interest, when visualization of the graph is
the aim.  If you show me examples of graph formats that you'd like, that
can represent all the data encoded in .ci files, it wouldn't take much
effort to persuade me to write the few lines of awk, or perhaps even
sed, to convert .ci files output by GCC to the other format ;-)


> I tried that, but 'xvcg' didn't render anything useful for a
> '-fcallgraph-info=su,da' dump, hmm.

Did xvcg fail to display the node information added by su and da?  As
in, do you see the difference the options make to the graph text file,
but not in the visualization?  Or is it something else?


> Also, I found that many years ago, in 2012, Steven Bosscher did "Rework
> RTL CFG graph dumping to dump DOT format" (that's Graphviz), and then did
> "remove vcg CFG dumper".

gnatstack and -fcallgraph-info have been available since long before
that move indeed.

> Note that I'm not actively objecting VCG

Good, thanks for pointing that out :-)

> unmaintained mid-90s software, containing obfuscated layout/rendering
> source code

Since gnatstack is the primary consumer, I think that objection doesn't
apply.


As a Free Software activist, however, I am a little concerned about the
claim about obfuscated source code.  I haven't been able to find any
substantiation of that in your message.  I think that would be OT for
this list, so would you please send me what you got about it at
[hidden email]?  TIA,

--
Alexandre Oliva, freedom fighter   he/him   https://FSFLA.org/blogs/lxo
Free Software Evangelist           Stallman was right, but he's left :(
GNU Toolchain Engineer    FSMatrix: It was he who freed the first of us
FSF & FSFLA board member                The Savior shall return (true);
Reply | Threaded
Open this post in threaded view
|

Re: introduce -fcallgraph-info option

Richard Biener
In reply to this post by Alexandre Oliva-4
On Thu, 7 Nov 2019, Alexandre Oliva wrote:

> On Nov  7, 2019, Richard Biener <[hidden email]> wrote:
>
> > A simple test shows we currently only pass -auxbase-strip /tmp/cc...o
> > to the LTRANS lto1 invocation plus -dumpbase cc...o
>
> This -auxbase-strip argument is introduced by the gcc driver that runs
> lto1, based on the -o (tmp ltrans .o) argument, but this driver has no
> clue as to the executable name or location, and there's nothing
> whatsoever in the driver interface that enables aux_base_name to be
> located separately from the output name.  Thus the possibilities I
> brought up of introducing means for it to be told so, explicitly or by
> convention.

So how's -dumpbase handled?  I'd prefer the same approach for -auxbase

Richard.
123