From patchwork Sat Oct 26 04:35:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 1184553 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-511811-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=adacore.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="uX9WV9vI"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 470Sq54N30z9sPV for ; Sat, 26 Oct 2019 15:36:15 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type :content-transfer-encoding; q=dns; s=default; b=smCfztB0UlKUHmYo rPi+DbE6lZbhRy6erGD50tszAkUbnH/NBeM7E4iE0Eyo+t2g9DUwuOJaB16oR/J+ OsXkLUJicfVbTTtnxnIBMe8/QmP+dOJlt6+ZZOjd0fSoakmvCy+65D0Wd40LshGp RWpX/nJoIXIWJM35jEYfd2U7/5c= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type :content-transfer-encoding; s=default; bh=9xzRpoVu2svAxmaJwmtR5A XIKTg=; b=uX9WV9vIhj0BNSt8eBdtPDKcAAa4J/PdzmeroQssM90AFtDRPNSmNH IklNIllmzoxoSkChvQ4d7nm6F5gB/lA0QymAiIfY8fSxliA8Qy/Gmz0reTdBhYkR Qq44PI991pQ80rv8HRd/xGgU3j5Fs7kIlC6Zn6MLPwrG1zQ0jNc7I= Received: (qmail 81650 invoked by alias); 26 Oct 2019 04:36:07 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 81642 invoked by uid 89); 26 Oct 2019 04:36:06 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.4 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, KAM_STOCKGEN, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.1 spammy=finalized, decoration, void_type_node, backward X-HELO: rock.gnat.com Received: from rock.gnat.com (HELO rock.gnat.com) (205.232.38.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 26 Oct 2019 04:36:02 +0000 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 246A356053; Sat, 26 Oct 2019 00:36:01 -0400 (EDT) Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id q3afIWrz6fr1; Sat, 26 Oct 2019 00:36:01 -0400 (EDT) Received: from free.home (tron.gnat.com [IPv6:2620:20:4000:0:46a8:42ff:fe0e:e294]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by rock.gnat.com (Postfix) with ESMTPS id 8507E56051; Sat, 26 Oct 2019 00:35:59 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id x9Q4ZhBK005032 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Sat, 26 Oct 2019 01:35:44 -0300 From: Alexandre Oliva To: gcc-patches@gcc.gnu.org Cc: ebotcazou@adacore.com, rguenther@suse.de, joseph@codesourcery.com Subject: introduce -fcallgraph-info option Date: Sat, 26 Oct 2019 01:35:43 -0300 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2 (gnu/linux) MIME-Version: 1.0 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 , Alexandre Oliva * 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- Mark 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 (); + 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,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 *callees; + + /* Dynamic allocations enocuntered within the function, if callgraph + da is enabled. */ + vec *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 ("", 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 ();