aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Botcazou <ebotcazou@adacore.com>2019-11-06 10:57:18 +0000
committerAlexandre Oliva <aoliva@gcc.gnu.org>2019-11-06 10:57:18 +0000
commit3cf3da88be453f3fceaa596ee78be8d1e5aa21ca (patch)
treeead0ae87adfe46ab5e364107f3cfe9d60d9d3761
parent5d183d1740d8d8b84991f186ce4d992ee799536f (diff)
downloadgcc-3cf3da88be453f3fceaa596ee78be8d1e5aa21ca.zip
gcc-3cf3da88be453f3fceaa596ee78be8d1e5aa21ca.tar.gz
gcc-3cf3da88be453f3fceaa596ee78be8d1e5aa21ca.tar.bz2
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 <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
-rw-r--r--gcc/ChangeLog48
-rw-r--r--gcc/ada/ChangeLog5
-rw-r--r--gcc/ada/gcc-interface/misc.c3
-rw-r--r--gcc/builtins.c4
-rw-r--r--gcc/calls.c6
-rw-r--r--gcc/common.opt8
-rw-r--r--gcc/doc/invoke.texi23
-rw-r--r--gcc/explow.c5
-rw-r--r--gcc/flag-types.h16
-rw-r--r--gcc/function.c59
-rw-r--r--gcc/function.h30
-rw-r--r--gcc/gimplify.c4
-rw-r--r--gcc/optabs-libfuncs.c4
-rw-r--r--gcc/opts.c26
-rw-r--r--gcc/output.h2
-rw-r--r--gcc/print-tree.c76
-rw-r--r--gcc/print-tree.h4
-rw-r--r--gcc/toplev.c178
18 files changed, 449 insertions, 52 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 38160dd..897b494 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog
index 193bd10..c69910c 100644
--- a/gcc/ada/ChangeLog
+++ b/gcc/ada/ChangeLog
@@ -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".
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 c8fa86f..245fad0 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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;
}
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 fdd923e..12c0083 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -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.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index cf7cf20..227ad27 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
@@ -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}
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 8feb246..5fa0ba6 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..8916f7e 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 ();