From: Eric Botcazou Date: Wed, 6 Nov 2019 10:57:18 +0000 (+0000) Subject: introduce -fcallgraph-info option X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=3cf3da88be453f3fceaa596ee78be8d1e5aa21ca;p=gcc.git introduce -fcallgraph-info option This was first submitted many years ago https://gcc.gnu.org/ml/gcc-patches/2010-10/msg02468.html The command line option -fcallgraph-info is added and makes the compiler generate another output file (xxx.ci) for each compilation unit (or LTO partitoin), which is a valid VCG file (you can launch your favorite VCG viewer on it unmodified) and contains the "final" callgraph of the unit. "final" is a bit of a misnomer as this is actually the callgraph at RTL expansion time, but since most high-level optimizations are done at the Tree level and RTL doesn't usually fiddle with calls, it's final in almost all cases. Moreover, the nodes can be decorated with additional info: -fcallgraph-info=su adds stack usage info and -fcallgraph-info=da dynamic allocation info. for gcc/ChangeLog From Eric Botcazou , Alexandre Oliva * common.opt (-fcallgraph-info[=]): New option. * doc/invoke.texi (Developer options): Document it. * opts.c (common_handle_option): Handle it. * builtins.c (expand_builtin_alloca): Record allocation if -fcallgraph-info=da. * calls.c (expand_call): If -fcallgraph-info, record the call. (emit_library_call_value_1): Likewise. * flag-types.h (enum callgraph_info_type): New type. * explow.c: Include stringpool.h. (set_stack_check_libfunc): Set SET_SYMBOL_REF_DECL on the symbol. * function.c (allocate_stack_usage_info): New. (allocate_struct_function): Call it for -fcallgraph-info. (prepare_function_start): Call it otherwise. (record_final_call, record_dynamic_alloc): New. * function.h (struct callinfo_callee): New. (CALLEE_FROM_CGRAPH_P): New. (struct callinfo_dalloc): New. (struct stack_usage): Add callees and dallocs. (record_final_call, record_dynamic_alloc): Declare. * gimplify.c (gimplify_decl_expr): Record dynamically-allocated object if -fcallgraph-info=da. * optabs-libfuncs.c (build_libfunc_function): Keep SYMBOL_REF_DECL. * print-tree.h (print_decl_identifier): Declare. (PRINT_DECL_ORIGIN, PRINT_DECL_NAME, PRINT_DECL_UNIQUE_NAME): New. * print-tree.c: Include print-tree.h. (print_decl_identifier): New function. * toplev.c: Include print-tree.h. (callgraph_info_file): New global variable. (callgraph_info_external_printed): Likewise. (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 From-SVN: r277876 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 38160dd631e..897b4945172 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,49 @@ +2019-11-06 Eric Botcazou + Alexandre Oliva + + * common.opt (-fcallgraph-info[=]): New option. + * doc/invoke.texi (Developer options): Document it. + * opts.c (common_handle_option): Handle it. + * builtins.c (expand_builtin_alloca): Record allocation if + -fcallgraph-info=da. + * calls.c (expand_call): If -fcallgraph-info, record the call. + (emit_library_call_value_1): Likewise. + * flag-types.h (enum callgraph_info_type): New type. + * explow.c: Include stringpool.h. + (set_stack_check_libfunc): Set SET_SYMBOL_REF_DECL on the symbol. + * function.c (allocate_stack_usage_info): New. + (allocate_struct_function): Call it for -fcallgraph-info. + (prepare_function_start): Call it otherwise. + (record_final_call, record_dynamic_alloc): New. + * function.h (struct callinfo_callee): New. + (CALLEE_FROM_CGRAPH_P): New. + (struct callinfo_dalloc): New. + (struct stack_usage): Add callees and dallocs. + (record_final_call, record_dynamic_alloc): Declare. + * gimplify.c (gimplify_decl_expr): Record dynamically-allocated + object if -fcallgraph-info=da. + * optabs-libfuncs.c (build_libfunc_function): Keep SYMBOL_REF_DECL. + * print-tree.h (print_decl_identifier): Declare. + (PRINT_DECL_ORIGIN, PRINT_DECL_NAME, PRINT_DECL_UNIQUE_NAME): New. + * print-tree.c: Include print-tree.h. + (print_decl_identifier): New function. + * toplev.c: Include print-tree.h. + (callgraph_info_file): New global variable. + (callgraph_info_external_printed): Likewise. + (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 Frederik Harwath Thomas Schwinge @@ -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 PR inline-asm/92352 diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index 193bd10c757..c69910c97cc 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,8 @@ +2019-11-06 Eric Botcazou + Alexandre Oliva + + * gcc-interface/misc.c (callgraph_info_file): Delete. + 2019-10-27 Jakub Jelinek * 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 4abd4d5708a..d68b37384ff 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 c8fa86f641d..245fad02d9c 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 e2b770fc277..62921351b11 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 fdd923e3c35..12c0083964e 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- Mark as being preserved across functions. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index cf7cf206193..227ad27390e 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 7eb854bca4a..83c786366c1 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 a2103282d46..b23d3a271f1 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 a1c76a4dd7a..3f79a38aeae 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -4725,6 +4725,16 @@ get_last_funcdef_no (void) return funcdef_no; } +/* Allocate and initialize the stack usage info data structure for the + current function. */ +static void +allocate_stack_usage_info (void) +{ + gcc_assert (!cfun->su); + cfun->su = ggc_cleared_alloc (); + cfun->su->static_stack_size = -1; +} + /* Allocate a function structure for FNDECL and set its contents to the defaults. Set cfun to the newly-allocated object. Some of the helper functions invoked during initialization assume @@ -4802,6 +4812,9 @@ allocate_struct_function (tree fndecl, bool abstract_p) if (!profile_flag && !flag_instrument_function_entry_exit) DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl) = 1; + + if (flag_callgraph_info) + allocate_stack_usage_info (); } /* Don't enable begin stmt markers if var-tracking at assignments is @@ -4846,11 +4859,8 @@ prepare_function_start (void) init_expr (); default_rtl_profile (); - if (flag_stack_usage_info) - { - cfun->su = ggc_cleared_alloc (); - cfun->su->static_stack_size = -1; - } + if (flag_stack_usage_info && !flag_callgraph_info) + allocate_stack_usage_info (); cse_not_expected = ! optimize; @@ -6373,12 +6383,49 @@ rest_of_handle_thread_prologue_and_epilogue (void) cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0); /* The stack usage info is finalized during prologue expansion. */ - if (flag_stack_usage_info) + if (flag_stack_usage_info || flag_callgraph_info) output_stack_usage (); return 0; } +/* Record a final call to CALLEE at LOCATION. */ + +void +record_final_call (tree callee, location_t location) +{ + if (!callee || CALLEE_FROM_CGRAPH_P (callee)) + return; + + struct callinfo_callee datum = { location, callee }; + vec_safe_push (cfun->su->callees, datum); +} + +/* Record a dynamic allocation made for DECL_OR_EXP. */ + +void +record_dynamic_alloc (tree decl_or_exp) +{ + struct callinfo_dalloc datum; + + if (DECL_P (decl_or_exp)) + { + datum.location = DECL_SOURCE_LOCATION (decl_or_exp); + const char *name = lang_hooks.decl_printable_name (decl_or_exp, 2); + const char *dot = strrchr (name, '.'); + if (dot) + name = dot + 1; + datum.name = ggc_strdup (name); + } + else + { + datum.location = EXPR_LOCATION (decl_or_exp); + datum.name = NULL; + } + + vec_safe_push (cfun->su->dallocs, datum); +} + namespace { const pass_data pass_data_thread_prologue_and_epilogue = diff --git a/gcc/function.h b/gcc/function.h index 43ac5dffd24..14794c420a2 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -192,6 +192,23 @@ public: poly_int64 length; }; +/* Describe emitted builtin calls for -fcallgraph-info. Those that + are not builtin are taken from cgraph edges. */ +struct GTY(()) callinfo_callee +{ + location_t location; + tree decl; +}; +#define CALLEE_FROM_CGRAPH_P(T) \ + (!fndecl_built_in_p (T) && !DECL_IS_BUILTIN (T)) + +/* Describe dynamic allocation for -fcallgraph-info=da. */ +struct GTY(()) callinfo_dalloc +{ + location_t location; + char const *name; +}; + class GTY(()) stack_usage { public: @@ -210,6 +227,13 @@ public: /* Nonzero if the amount of stack space allocated dynamically cannot be bounded at compile-time. */ unsigned int has_unbounded_dynamic_stack_size : 1; + + /* Functions called within the function, if callgraph is enabled. */ + vec *callees; + + /* Dynamic allocations encountered within the function, if callgraph + da is enabled. */ + vec *dallocs; }; #define current_function_static_stack_size (cfun->su->static_stack_size) @@ -406,6 +430,12 @@ void add_local_decl (struct function *fun, tree d); #define FOR_EACH_LOCAL_DECL(FUN, I, D) \ FOR_EACH_VEC_SAFE_ELT_REVERSE ((FUN)->local_decls, I, D) +/* Record a final call to CALLEE at LOCATION. */ +void record_final_call (tree callee, location_t location); + +/* Record a dynamic allocation made for DECL_OR_EXP. */ +void record_dynamic_alloc (tree decl_or_exp); + /* If va_list_[gf]pr_size is set to this, it means we don't know how many units need to be saved. */ #define VA_LIST_MAX_GPR_SIZE 255 diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 8feb2464b97..5fa0ba6dda6 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 ef43dae12fb..8916f7e4da0 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 10b9f108f8d..f46b468a968 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 835d63556e6..6cccada4aeb 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 6dcbb2dcb13..bd09ec4d7a7 100644 --- a/gcc/print-tree.c +++ b/gcc/print-tree.c @@ -1035,6 +1035,82 @@ print_node (FILE *file, const char *prefix, tree node, int indent, fprintf (file, ">"); } +/* Print the identifier for DECL according to FLAGS. */ + +void +print_decl_identifier (FILE *file, tree decl, int flags) +{ + bool needs_colon = false; + const char *name; + char c; + + if (flags & PRINT_DECL_ORIGIN) + { + if (DECL_IS_BUILTIN (decl)) + fputs ("", file); + else + { + expanded_location loc + = expand_location (DECL_SOURCE_LOCATION (decl)); + fprintf (file, "%s:%d:%d", loc.file, loc.line, loc.column); + } + needs_colon = true; + } + + if (flags & PRINT_DECL_UNIQUE_NAME) + { + name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); + if (!TREE_PUBLIC (decl) + || (DECL_WEAK (decl) && !DECL_EXTERNAL (decl))) + /* The symbol has internal or weak linkage so its assembler name + is not necessarily unique among the compilation units of the + program. We therefore have to further mangle it. But we can't + simply use DECL_SOURCE_FILE because it contains the name of the + file the symbol originates from so, e.g. for function templates + in C++ where the templates are defined in a header file, we can + have symbols with the same assembler name and DECL_SOURCE_FILE. + That's why we use the name of the top-level source file of the + compilation unit. ??? Unnecessary for Ada. */ + name = ACONCAT ((main_input_filename, ":", name, NULL)); + } + else if (flags & PRINT_DECL_NAME) + { + /* We don't want to print the full qualified name because it can be long, + so we strip the scope prefix, but we may need to deal with the suffix + created by the compiler. */ + const char *suffix = strchr (IDENTIFIER_POINTER (DECL_NAME (decl)), '.'); + name = lang_hooks.decl_printable_name (decl, 2); + if (suffix) + { + const char *dot = strchr (name, '.'); + while (dot && strcasecmp (dot, suffix) != 0) + { + name = dot + 1; + dot = strchr (name, '.'); + } + } + else + { + const char *dot = strrchr (name, '.'); + if (dot) + name = dot + 1; + } + } + else + return; + + if (needs_colon) + fputc (':', file); + + while ((c = *name++) != '\0') + { + /* Strip double-quotes because of VCG. */ + if (c == '"') + continue; + fputc (c, file); + } +} + /* Print the node NODE on standard error, for debugging. Most nodes referred to by this one are printed recursively diff --git a/gcc/print-tree.h b/gcc/print-tree.h index 1d4fe6e8950..cbea48c486e 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 00a5e832126..18fea1c3dd1 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 ();