/* Read and write coverage files, and associated functionality.
- Copyright (C) 1990-2014 Free Software Foundation, Inc.
+ Copyright (C) 1990-2015 Free Software Foundation, Inc.
Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
based on some ideas from Dain Samples of UC Berkeley.
Further mangling by Bob Manson, Cygnus Support.
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "stringpool.h"
#include "stor-layout.h"
#include "flags.h"
#include "output.h"
#include "regs.h"
-#include "expr.h"
+#include "hard-reg-set.h"
#include "function.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
+#include "expr.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
#include "basic-block.h"
#include "toplev.h"
#include "tm_p.h"
-#include "ggc.h"
#include "coverage.h"
#include "langhooks.h"
-#include "hash-table.h"
#include "tree-iterator.h"
#include "context.h"
#include "pass_manager.h"
#include "intl.h"
#include "filenames.h"
#include "target.h"
+#include "params.h"
+#include "auto-profile.h"
#include "gcov-io.h"
#include "gcov-io.c"
};
/* Counts information for a function. */
-typedef struct counts_entry
+typedef struct counts_entry : pointer_hash <counts_entry>
{
/* We hash by */
unsigned ident;
struct gcov_ctr_summary summary;
/* hash_table support. */
- typedef counts_entry value_type;
- typedef counts_entry compare_type;
- static inline hashval_t hash (const value_type *);
- static int equal (const value_type *, const compare_type *);
- static void remove (value_type *);
+ static inline hashval_t hash (const counts_entry *);
+ static int equal (const counts_entry *, const counts_entry *);
+ static void remove (counts_entry *);
} counts_entry_t;
static GTY(()) struct coverage_data *functions_head = 0;
static char *da_file_name;
/* The names of merge functions for counters. */
-static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
-static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
+#define STR(str) #str
+#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) STR(__gcov_merge ## FN_TYPE),
+static const char *const ctr_merge_functions[GCOV_COUNTERS] = {
+#include "gcov-counter.def"
+};
+#undef DEF_GCOV_COUNTER
+#undef STR
+
+#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) NAME,
+static const char *const ctr_names[GCOV_COUNTERS] = {
+#include "gcov-counter.def"
+};
+#undef DEF_GCOV_COUNTER
/* Forward declarations. */
static void read_counts_file (void);
tree
get_gcov_type (void)
{
- enum machine_mode mode = smallest_mode_for_size (GCOV_TYPE_SIZE, MODE_INT);
+ machine_mode mode = smallest_mode_for_size (GCOV_TYPE_SIZE, MODE_INT);
return lang_hooks.types.type_for_mode (mode, false);
}
static tree
get_gcov_unsigned_t (void)
{
- enum machine_mode mode = smallest_mode_for_size (32, MODE_INT);
+ machine_mode mode = smallest_mode_for_size (32, MODE_INT);
return lang_hooks.types.type_for_mode (mode, true);
}
\f
inline hashval_t
-counts_entry::hash (const value_type *entry)
+counts_entry::hash (const counts_entry *entry)
{
return entry->ident * GCOV_COUNTERS + entry->ctr;
}
inline int
-counts_entry::equal (const value_type *entry1,
- const compare_type *entry2)
+counts_entry::equal (const counts_entry *entry1, const counts_entry *entry2)
{
return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
}
inline void
-counts_entry::remove (value_type *entry)
+counts_entry::remove (counts_entry *entry)
{
free (entry->counts);
free (entry);
}
/* Hash table of count data. */
-static hash_table <counts_entry> counts_hash;
+static hash_table<counts_entry> *counts_hash;
/* Read in the counts file, if available. */
tag = gcov_read_unsigned ();
bbg_file_stamp = crc32_unsigned (bbg_file_stamp, tag);
- counts_hash.create (10);
+ counts_hash = new hash_table<counts_entry> (10);
while ((tag = gcov_read_unsigned ()))
{
gcov_unsigned_t length;
elt.ident = fn_ident;
elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
- slot = counts_hash.find_slot (&elt, INSERT);
+ slot = counts_hash->find_slot (&elt, INSERT);
entry = *slot;
if (!entry)
{
error ("checksum is (%x,%x) instead of (%x,%x)",
entry->lineno_checksum, entry->cfg_checksum,
lineno_checksum, cfg_checksum);
- counts_hash.dispose ();
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
else if (entry->summary.num != n_counts)
{
error ("Profile data for function %u is corrupted", fn_ident);
error ("number of counters is %d instead of %d", entry->summary.num, n_counts);
- counts_hash.dispose ();
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
{
error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted",
da_file_name);
- counts_hash.dispose ();
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
}
counts_entry_t *entry, elt;
/* No hash table, no counts. */
- if (!counts_hash.is_created ())
+ if (!counts_hash)
{
static int warned = 0;
da_file_name);
return NULL;
}
-
- elt.ident = current_function_funcdef_no + 1;
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ elt.ident = current_function_funcdef_no + 1;
+ else
+ {
+ gcc_assert (coverage_node_map_initialized_p ());
+ elt.ident = cgraph_node::get (cfun->decl)->profile_id;
+ }
elt.ctr = counter;
- entry = counts_hash.find (&elt);
+ entry = counts_hash->find (&elt);
if (!entry || !entry->summary.num)
/* The function was not emitted, or is weak and not chosen in the
final executable. Silently fail, because there's nothing we
}
else if (entry->lineno_checksum != lineno_checksum)
{
- warning (0, "source locations for function %qE have changed,"
+ warning (OPT_Wcoverage_mismatch,
+ "source locations for function %qE have changed,"
" the profile data may be out of date",
DECL_ASSEMBLER_NAME (current_function_decl));
}
unsigned
coverage_compute_profile_id (struct cgraph_node *n)
{
- expanded_location xloc
- = expand_location (DECL_SOURCE_LOCATION (n->decl));
- unsigned chksum = xloc.line;
+ unsigned chksum;
- chksum = coverage_checksum_string (chksum, xloc.file);
- chksum = coverage_checksum_string
- (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl)));
- if (first_global_object_name)
- chksum = coverage_checksum_string
- (chksum, first_global_object_name);
- chksum = coverage_checksum_string
- (chksum, aux_base_name);
+ /* Externally visible symbols have unique name. */
+ if (TREE_PUBLIC (n->decl) || DECL_EXTERNAL (n->decl) || n->unique_name)
+ {
+ chksum = coverage_checksum_string
+ (0, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl)));
+ }
+ else
+ {
+ expanded_location xloc
+ = expand_location (DECL_SOURCE_LOCATION (n->decl));
+ bool use_name_only = (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID) == 0);
+
+ chksum = (use_name_only ? 0 : xloc.line);
+ chksum = coverage_checksum_string (chksum, xloc.file);
+ chksum = coverage_checksum_string
+ (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl)));
+ if (!use_name_only && first_global_object_name)
+ chksum = coverage_checksum_string
+ (chksum, first_global_object_name);
+ chksum = coverage_checksum_string
+ (chksum, aux_base_name);
+ }
- /* Non-negative integers are hopefully small enough to fit in all targets. */
- return chksum & 0x7fffffff;
+ /* Non-negative integers are hopefully small enough to fit in all targets.
+ Gcov file formats wants non-zero function IDs. */
+ chksum = chksum & 0x7fffffff;
+ return chksum + (!chksum);
}
-/* Compute cfg checksum for the current function.
+/* Compute cfg checksum for the function FN given as argument.
The checksum is calculated carefully so that
source code changes that doesn't affect the control flow graph
won't change the checksum.
but the compiler won't detect the change and use the wrong profile data. */
unsigned
-coverage_compute_cfg_checksum (void)
+coverage_compute_cfg_checksum (struct function *fn)
{
basic_block bb;
- unsigned chksum = n_basic_blocks_for_fn (cfun);
+ unsigned chksum = n_basic_blocks_for_fn (fn);
- FOR_EACH_BB_FN (bb, cfun)
+ FOR_EACH_BB_FN (bb, fn)
{
edge e;
edge_iterator ei;
/* Announce function */
offset = gcov_write_tag (GCOV_TAG_FUNCTION);
- gcov_write_unsigned (current_function_funcdef_no + 1);
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ gcov_write_unsigned (current_function_funcdef_no + 1);
+ else
+ {
+ gcc_assert (coverage_node_map_initialized_p ());
+ gcov_write_unsigned (
+ cgraph_node::get (current_function_decl)->profile_id);
+ }
+
gcov_write_unsigned (lineno_checksum);
gcov_write_unsigned (cfg_checksum);
gcov_write_string (IDENTIFIER_POINTER
{
struct coverage_data *item = 0;
- /* If the function is extern (i.e. extern inline), then we won't
- be outputting it, so don't chain it onto the function
- list. */
- if (!DECL_EXTERNAL (current_function_decl))
+ item = ggc_alloc<coverage_data> ();
+
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ item->ident = current_function_funcdef_no + 1;
+ else
{
- item = ggc_alloc_coverage_data ();
-
- item->ident = current_function_funcdef_no + 1;
- item->lineno_checksum = lineno_checksum;
- item->cfg_checksum = cfg_checksum;
-
- item->fn_decl = current_function_decl;
- item->next = 0;
- *functions_tail = item;
- functions_tail = &item->next;
+ gcc_assert (coverage_node_map_initialized_p ());
+ item->ident = cgraph_node::get (cfun->decl)->profile_id;
}
+ item->lineno_checksum = lineno_checksum;
+ item->cfg_checksum = cfg_checksum;
+
+ item->fn_decl = current_function_decl;
+ item->next = 0;
+ *functions_tail = item;
+ functions_tail = &item->next;
+
for (i = 0; i != GCOV_COUNTERS; i++)
{
tree var = fn_v_ctrs[i];
TREE_TYPE (var) = array_type;
DECL_SIZE (var) = TYPE_SIZE (array_type);
DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
- varpool_finalize_decl (var);
+ varpool_node::finalize_decl (var);
}
fn_b_ctrs[i] = fn_n_ctrs[i] = 0;
DECL_NAME (var) = get_identifier (buf);
TREE_STATIC (var) = 1;
TREE_ADDRESSABLE (var) = 1;
+ DECL_NONALIASED (var) = 1;
DECL_ALIGN (var) = TYPE_ALIGN (type);
return var;
if (!prg_ctr_mask)
return false;
- if (cgraph_dump_file)
- fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, "Using data file %s\n", da_file_name);
/* Prune functions. */
for (fn_prev = &functions_head; (fn = *fn_prev);)
/* Build the info and fn_info types. These are mutually recursive. */
gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE);
gcov_fn_info_type = lang_hooks.types.make_type (RECORD_TYPE);
+ build_fn_info_type (gcov_fn_info_type, n_counters, gcov_info_type);
+ gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE);
gcov_fn_info_ptr_type = build_pointer_type
(build_qualified_type (gcov_fn_info_type, TYPE_QUAL_CONST));
- build_fn_info_type (gcov_fn_info_type, n_counters, gcov_info_type);
build_info_type (gcov_info_type, gcov_fn_info_ptr_type);
/* Build the gcov info var, this is referred to in its own
tree var = build_var (fn, gcov_fn_info_type, -1);
DECL_INITIAL (var) = init;
- varpool_finalize_decl (var);
+ varpool_node::finalize_decl (var);
CONSTRUCTOR_APPEND_ELT (ctor, NULL,
build1 (ADDR_EXPR, gcov_fn_info_ptr_type, var));
ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 1);
DECL_NAME (fn_info_ary) = get_identifier (name_buf);
DECL_INITIAL (fn_info_ary) = build_constructor (fn_info_ary_type, ctor);
- varpool_finalize_decl (fn_info_ary);
+ varpool_node::finalize_decl (fn_info_ary);
DECL_INITIAL (gcov_info_var)
= build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
- varpool_finalize_decl (gcov_info_var);
+ varpool_node::finalize_decl (gcov_info_var);
}
/* Perform file-level initialization. Read in data file, generate name
bbg_file_stamp = local_tick;
- if (flag_branch_probabilities)
+ if (flag_auto_profile)
+ read_autofdo_file ();
+ else if (flag_branch_probabilities)
read_counts_file ();
/* Name of bbg file. */