introduce -fcallgraph-info option
authorEric Botcazou <ebotcazou@adacore.com>
Wed, 6 Nov 2019 10:57:18 +0000 (10:57 +0000)
committerAlexandre Oliva <aoliva@gcc.gnu.org>
Wed, 6 Nov 2019 10:57:18 +0000 (10:57 +0000)
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  <ebotcazou@adacore.com>, Alexandre Oliva  <oliva@adacore.com>

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

Co-Authored-By: Alexandre Oliva <oliva@adacore.com>
From-SVN: r277876

18 files changed:
gcc/ChangeLog
gcc/ada/ChangeLog
gcc/ada/gcc-interface/misc.c
gcc/builtins.c
gcc/calls.c
gcc/common.opt
gcc/doc/invoke.texi
gcc/explow.c
gcc/flag-types.h
gcc/function.c
gcc/function.h
gcc/gimplify.c
gcc/optabs-libfuncs.c
gcc/opts.c
gcc/output.h
gcc/print-tree.c
gcc/print-tree.h
gcc/toplev.c

index 38160dd631e9aa51b09e5a9ac66784d6f7cadd78..897b494517291867e8b3830307c23253a14664bc 100644 (file)
@@ -1,3 +1,49 @@
+2019-11-06  Eric Botcazou  <ebotcazou@adacore.com>
+           Alexandre Oliva  <oliva@adacore.com>
+
+       * 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.
+
 2019-11-06  Gergö Barany  <gergo@codesourcery.com>
            Frederik Harwath  <frederik@codesourcery.com>
            Thomas Schwinge  <thomas@codesourcery.com>
@@ -7,7 +53,7 @@
        (new_omp_context): Initialize these.
        (scan_sharing_clauses): Record reduction clauses on OpenACC constructs.
        (scan_omp_for): Check reduction clauses for incorrect nesting.
-       
+
 2019-11-06  Jakub Jelinek  <jakub@redhat.com>
 
        PR inline-asm/92352
index 193bd10c7574b2ad9d3b777f4cea4dd101a872c4..c69910c97cc4a1ab135fc553b6f44a2fde2d1f22 100644 (file)
@@ -1,3 +1,8 @@
+2019-11-06  Eric Botcazou  <ebotcazou@adacore.com>
+           Alexandre Oliva  <oliva@adacore.com>
+
+       * gcc-interface/misc.c (callgraph_info_file): Delete.
+
 2019-10-27  Jakub Jelinek  <jakub@redhat.com>
 
        * locales.c (iso_3166): Add missing comma after "United-States".
index 4abd4d5708a540eb66d04b230da5a9df9e7125a8..d68b37384ff7f2682f81e4a371afa998e4d7a1b4 100644 (file)
@@ -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;
index c8fa86f641dbbe1b3141850f2111416b35f193d5..245fad02d9c6638c0aae47d2ef31150fb7b342d8 100644 (file)
@@ -5407,6 +5407,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;
 }
 
index e2b770fc277bd6f20fef77d5c3df64955dda561b..62921351b112c7fdd9004924fd44a854c0763da7 100644 (file)
@@ -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.
index fdd923e3c35679f48f5e202c3eb4e0db67ef3807..12c0083964ec2e192ba739e23cea53e4df85f869 100644 (file)
@@ -1093,6 +1093,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.
index cf7cf206193d96e3b90b43d42c04a8673c13f987..227ad27390e63b613d55c49c6d885b7fcbc88e9b 100644 (file)
@@ -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
@@ -14567,6 +14568,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}
index 7eb854bca4a6dcc5b15e5c42df1a5e88a19f2464..83c786366c1aaaaefd251e9805d4c698da485a3f 100644 (file)
@@ -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);
 }
 \f
 /* Emit one stack probe at ADDRESS, an address within the stack.  */
index a2103282d469db31ad157a87572068d943061c8c..b23d3a271f1ee69abb85d229a69222651fc4ba24 100644 (file)
@@ -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,
index a1c76a4dd7a84554f746c6ab99717cfe54ed0390..3f79a38aeaec81537445eaf09ac1db40184437d4 100644 (file)
@@ -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 =
index 43ac5dffd245794dd6822be0f471e42059b7942d..14794c420a215bc9c53e41ecfef555c9fd221a22 100644 (file)
@@ -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
index 8feb2464b97daec28c0772d44be764e43717c3db..5fa0ba6dda60979e75061ef4333a81a65e64fb15 100644 (file)
@@ -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
index ef43dae12fb64e7af881e5be7c960380bd41d90b..8916f7e4da0bbf281a4251741cbad80425cf76ec 100644 (file)
@@ -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;
 }
 
index 10b9f108f8d0618501c1567bb4f8a7f9de2bdb98..f46b468a968e717b216cbb612d38497df563a9c5 100644 (file)
@@ -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;
index 835d63556e6a8b4cdb5cad80b5322ba31086478b..6cccada4aeb1decad5050a282c8afa1524d5b9f4 100644 (file)
@@ -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 */
index 6dcbb2dcb1369a0f0fb7fbcdff9322807d868d32..bd09ec4d7a7afcfdef0e5278e3cd232febb5266a 100644 (file)
@@ -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
index 1d4fe6e8950cc4733cab8a3b7c1c70e20492edd6..cbea48c486e3cc4f3ea7ff19fcf5b3991f7b9a96 100644 (file)
@@ -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
index 00a5e83212601bef62ea9a254ddbeae35cde8e6e..18fea1c3dd1cbecd195ee2a5656a322b4e7feb5f 100644 (file)
@@ -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 ();