/* Functions for LTO dump tool.
Copyright (C) 2018-2024 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
. */
#define INCLUDE_MEMORY
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "function.h"
#include "basic-block.h"
#include "tree.h"
#include "gimple.h"
#include "cfg.h"
#include "tree-cfg.h"
#include "tree-pass.h"
#include "tree-streamer.h"
#include "cgraph.h"
#include "opts.h"
#include "debug.h"
#include "lto-partition.h"
#include "tree-pretty-print.h"
#include "lto-common.h"
/* Stores details of symbols for dumping symbol list. */
class symbol_entry
{
public:
symtab_node *node;
symbol_entry (symtab_node *node_): node (node_)
{}
virtual ~symbol_entry ()
{}
char* get_name () const
{
if (flag_lto_dump_demangle)
return xstrdup (node->name ());
else
return xstrdup (node->asm_name ());
}
virtual size_t get_size () const = 0;
virtual void dump ()
{
const char *name = get_name ();
const char *type_name = node->get_symtab_type_string ();
const char *visibility = node->get_visibility_string ();
size_t sz = get_size ();
printf ("%s %s %4" PRIu64 " %s ", type_name, visibility, (uint64_t) sz,
name);
}
};
/* Stores variable specific details of symbols for dumping symbol list. */
class variable_entry: public symbol_entry
{
public:
variable_entry (varpool_node *node_): symbol_entry (node_)
{}
virtual ~variable_entry ()
{}
size_t get_size () const final override
{
varpool_node *vnode = dyn_cast (node);
if (DECL_SIZE (vnode->decl) && tree_fits_shwi_p (DECL_SIZE (vnode->decl)))
return tree_to_shwi (DECL_SIZE (vnode->decl));
return 0;
}
void dump () final override
{
symbol_entry :: dump ();
varpool_node *vnode = dyn_cast (node);
vnode->get_constructor ();
tree value_tree = DECL_INITIAL (vnode->decl);
if (flag_lto_print_value && value_tree)
print_generic_expr (stdout, value_tree, TDF_NONE);
printf ("\n");
}
};
/* Stores function specific details of symbols for dumping symbol list. */
class function_entry: public symbol_entry
{
public:
function_entry (cgraph_node *node_): symbol_entry (node_)
{}
virtual ~function_entry ()
{}
void dump () final override
{
symbol_entry :: dump ();
printf ("\n");
}
size_t get_size () const final override
{
cgraph_node *cnode = dyn_cast (node);
gcc_assert (cnode);
return (cnode->definition && !cnode->thunk && !cnode->alias)
? n_basic_blocks_for_fn (DECL_STRUCT_FUNCTION (cnode->decl))
: 0;
}
};
/* Comparing symbols based on size. */
int size_compare (const void *a, const void *b)
{
const symbol_entry *e1 = *(const symbol_entry * const*) a;
const symbol_entry *e2 = *(const symbol_entry * const*) b;
return e1->get_size () - e2->get_size ();
}
/* Comparing symbols based on name. */
int name_compare (const void *a, const void *b)
{
const symbol_entry *e1 = *(const symbol_entry * const*) a;
const symbol_entry *e2 = *(const symbol_entry * const*) b;
return strcmp (e1->get_name (), e2->get_name ());
}
/* Dump list of functions and their details. */
void dump_list_functions (void)
{
auto_vec v;
cgraph_node *cnode;
FOR_EACH_FUNCTION (cnode)
{
if (cnode->definition && !cnode->alias)
cnode->get_untransformed_body ();
symbol_entry *e = new function_entry (cnode);
if (!flag_lto_dump_defined || (cnode->definition && !cnode->alias))
v.safe_push (e);
}
if (flag_lto_size_sort)
v.qsort (size_compare);
else if (flag_lto_name_sort)
v.qsort (name_compare);
if (flag_lto_reverse_sort)
v.reverse ();
printf ("Type Visibility Size Name");
if (flag_lto_print_value)
printf (" Value");
printf ("\n");
int i=0;
symbol_entry* e;
FOR_EACH_VEC_ELT (v, i, e)
{
e->dump ();
delete e;
}
}
/* Dump list of variables and their details. */
void dump_list_variables (void)
{
auto_vec v;
varpool_node *vnode;
FOR_EACH_VARIABLE (vnode)
{
symbol_entry *e = new variable_entry (vnode);
if (!flag_lto_dump_defined || vnode->definition)
v.safe_push (e);
}
if (flag_lto_size_sort)
v.qsort (size_compare);
else if (flag_lto_name_sort)
v.qsort (name_compare);
if (flag_lto_reverse_sort)
v.reverse ();
printf ("\n");
int i=0;
symbol_entry* e;
FOR_EACH_VEC_ELT (v, i, e)
{
e->dump ();
delete e;
}
}
/* Dump symbol table in graphviz format. */
void dump_symtab_graphviz (void)
{
symtab->dump_graphviz (stdout);
}
/* Dump symbol list. */
void dump_list (void)
{
dump_list_functions ();
dump_list_variables ();
}
/* Dump specific variables and functions used in IL. */
void dump_symbol ()
{
symtab_node *node;
printf ("Symbol: %s\n", flag_lto_dump_symbol);
FOR_EACH_SYMBOL (node)
{
if (!strcmp (flag_lto_dump_symbol, node->name ()))
{
node->debug ();
printf ("\n");
}
}
}
/* Dump specific gimple body of specified function. */
void dump_body ()
{
int flag = 0;
dump_flags_t flags = TDF_NONE;
if (flag_dump_level)
flags = parse_dump_option (flag_dump_level, NULL);
if (flags == TDF_ERROR)
{
error_at (input_location, "Level not found, use none, slim, blocks, vops.");
return;
}
cgraph_node *cnode;
FOR_EACH_DEFINED_FUNCTION (cnode)
if (!cnode->alias
&& !strcmp (cnode->asm_name (), flag_dump_body))
{
printf ("GIMPLE body of function: %s\n\n", cnode->asm_name ());
cnode->get_untransformed_body ();
debug_function (cnode->decl, flags);
flag = 1;
}
if (!flag)
error_at (input_location, "Function not found.");
}
/* List of command line options for dumping. */
void dump_tool_help ()
{
const char *msg =
"Usage: lto-dump [OPTION]... SUB_COMMAND [OPTION]...\n\n"
"LTO dump tool command line options.\n\n"
" -list [options] Dump the symbol list.\n"
" -demangle Dump the demangled output.\n"
" -defined-only Dump only the defined symbols.\n"
" -print-value Dump initial values of the variables.\n"
" -name-sort Sort the symbols alphabetically.\n"
" -size-sort Sort the symbols according to size.\n"
" -reverse-sort Dump the symbols in reverse order.\n"
" -symbol= Dump the details of specific symbol.\n"
" -objects Dump the details of LTO objects.\n"
" -callgraph Dump the callgraph in graphviz format.\n"
" -type-stats Dump statistics of tree types.\n"
" -tree-stats Dump statistics of trees.\n"
" -gimple-stats Dump statistics of GIMPLE statements.\n"
" -dump-body= Dump the specific GIMPLE body.\n"
" -dump-level= Deciding the optimization level of body.\n"
" -help Display the dump tool help.\n";
fputs (msg, stdout);
}
unsigned int
lto_option_lang_mask (void)
{
return CL_LTODump;
}
/* Functions for dumping various details in LTO dump tool are called
in lto_main(). The purpose of this dump tool is to analyze the LTO
object files. */
void
lto_main (void)
{
quiet_flag = true;
if (flag_lto_dump_tool_help)
{
dump_tool_help ();
exit (SUCCESS_EXIT_CODE);
}
/* LTO is called as a front end, even though it is not a front end.
Because it is called as a front end, TV_PHASE_PARSING and
TV_PARSE_GLOBAL are active, and we need to turn them off while
doing LTO. Later we turn them back on so they are active up in
toplev.cc. */
/* Initialize the LTO front end. */
lto_fe_init ();
g_timer = NULL;
/* Read all the symbols and call graph from all the files in the
command line. */
read_cgraph_and_symbols (num_in_fnames, in_fnames);
/* Dump symbol list. */
if (flag_lto_dump_list)
dump_list ();
else if (flag_lto_dump_symbol)
{
/* Dump specific variables and functions used in IL. */
dump_symbol ();
}
else if (flag_lto_gimple_stats)
{
/* Dump gimple statement statistics. */
cgraph_node *node;
FOR_EACH_DEFINED_FUNCTION (node)
if (!node->alias)
node->get_untransformed_body ();
if (!GATHER_STATISTICS)
warning_at (input_location, 0,
"Not configured with "
"%<--enable-gather-detailed-mem-stats%>.");
else
dump_gimple_statistics ();
}
else if (flag_lto_tree_stats)
{
/* Dump tree statistics. */
if (!GATHER_STATISTICS)
warning_at (input_location, 0,
"Not configured with "
"%<--enable-gather-detailed-mem-stats%>.");
else
{
printf ("Tree statistics\n");
dump_tree_statistics ();
}
}
else if (flag_dump_body)
{
/* Dump specific gimple body of specified function. */
dump_body ();
}
else if (flag_dump_callgraph)
dump_symtab_graphviz ();
else
dump_tool_help ();
/* Exit right now. */
exit (SUCCESS_EXIT_CODE);
}