/* Read and write coverage files, and associated functionality.
- Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
- 2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
- 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 "hashtab.h"
#include "tree-iterator.h"
-#include "cgraph.h"
+#include "context.h"
+#include "pass_manager.h"
#include "tree-pass.h"
+#include "cgraph.h"
+#include "dumpfile.h"
#include "diagnostic-core.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"
-struct GTY((chain_next ("%h.next"))) function_list
+struct GTY((chain_next ("%h.next"))) coverage_data
{
- struct function_list *next; /* next function */
+ struct coverage_data *next; /* next function */
unsigned ident; /* function ident */
unsigned lineno_checksum; /* function lineno checksum */
unsigned cfg_checksum; /* function cfg checksum */
};
/* Counts information for a function. */
-typedef struct counts_entry
+typedef struct counts_entry : pointer_hash <counts_entry>
{
/* We hash by */
unsigned ident;
unsigned cfg_checksum;
gcov_type *counts;
struct gcov_ctr_summary summary;
+
+ /* hash_table support. */
+ 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 function_list *functions_head = 0;
-static struct function_list **functions_tail = &functions_head;
+static GTY(()) struct coverage_data *functions_head = 0;
+static struct coverage_data **functions_tail = &functions_head;
static unsigned no_coverage = 0;
/* Cumulative counter information for whole program. */
static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */
static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */
-/* Name of the output file for coverage output file. */
+/* Coverage info VAR_DECL and function info type nodes. */
+static GTY(()) tree gcov_info_var;
+static GTY(()) tree gcov_fn_info_type;
+static GTY(()) tree gcov_fn_info_ptr_type;
+
+/* Name of the notes (gcno) output file. The "bbg" prefix is for
+ historical reasons, when the notes file contained only the
+ basic block graph notes.
+ If this is NULL we're not writing to the notes file. */
static char *bbg_file_name;
-static unsigned bbg_file_opened;
-static int bbg_function_announced;
-/* Name of the count data file. */
-static char *da_file_name;
+/* File stamp for notes file. */
+static unsigned bbg_file_stamp;
-/* Hash table of count data. */
-static htab_t counts_hash = NULL;
+/* Name of the count data (gcda) file. */
+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 hashval_t htab_counts_entry_hash (const void *);
-static int htab_counts_entry_eq (const void *, const void *);
-static void htab_counts_entry_del (void *);
static void read_counts_file (void);
static tree build_var (tree, tree, int);
static void build_fn_info_type (tree, unsigned, tree);
-static tree build_fn_info (const struct function_list *, tree, tree);
-static void build_info_type (tree, unsigned, tree);
-static tree build_info (tree, tree, tree, unsigned);
-static void create_coverage (void);
+static void build_info_type (tree, tree);
+static tree build_fn_info (const struct coverage_data *, tree, tree);
+static tree build_info (tree, tree);
+static bool coverage_obj_init (void);
+static vec<constructor_elt, va_gc> *coverage_obj_fn
+(vec<constructor_elt, va_gc> *, tree, struct coverage_data const *);
+static void coverage_obj_finish (vec<constructor_elt, va_gc> *);
\f
/* Return the type node for gcov_type. */
tree
get_gcov_type (void)
{
- return lang_hooks.types.type_for_size (GCOV_TYPE_SIZE, false);
+ machine_mode mode = smallest_mode_for_size (GCOV_TYPE_SIZE, MODE_INT);
+ return lang_hooks.types.type_for_mode (mode, false);
}
/* Return the type node for gcov_unsigned_t. */
static tree
get_gcov_unsigned_t (void)
{
- return lang_hooks.types.type_for_size (32, true);
+ machine_mode mode = smallest_mode_for_size (32, MODE_INT);
+ return lang_hooks.types.type_for_mode (mode, true);
}
\f
-static hashval_t
-htab_counts_entry_hash (const void *of)
+inline hashval_t
+counts_entry::hash (const counts_entry *entry)
{
- const counts_entry_t *const entry = (const counts_entry_t *) of;
-
return entry->ident * GCOV_COUNTERS + entry->ctr;
}
-static int
-htab_counts_entry_eq (const void *of1, const void *of2)
+inline int
+counts_entry::equal (const counts_entry *entry1, const counts_entry *entry2)
{
- const counts_entry_t *const entry1 = (const counts_entry_t *) of1;
- const counts_entry_t *const entry2 = (const counts_entry_t *) of2;
-
return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
}
-static void
-htab_counts_entry_del (void *of)
+inline void
+counts_entry::remove (counts_entry *entry)
{
- counts_entry_t *const entry = (counts_entry_t *) of;
-
free (entry->counts);
free (entry);
}
+/* Hash table of count data. */
+static hash_table<counts_entry> *counts_hash;
+
/* Read in the counts file, if available. */
static void
return;
}
- /* Read and discard the stamp. */
- gcov_read_unsigned ();
+ /* Read the stamp, used for creating a generation count. */
+ tag = gcov_read_unsigned ();
+ bbg_file_stamp = crc32_unsigned (bbg_file_stamp, tag);
- counts_hash = htab_create (10,
- htab_counts_entry_hash, htab_counts_entry_eq,
- htab_counts_entry_del);
+ counts_hash = new hash_table<counts_entry> (10);
while ((tag = gcov_read_unsigned ()))
{
gcov_unsigned_t length;
summary.ctrs[ix].run_max = sum.ctrs[ix].run_max;
summary.ctrs[ix].sum_max += sum.ctrs[ix].sum_max;
}
+ if (new_summary)
+ memcpy (summary.ctrs[GCOV_COUNTER_ARCS].histogram,
+ sum.ctrs[GCOV_COUNTER_ARCS].histogram,
+ sizeof (gcov_bucket_type) * GCOV_HISTOGRAM_SIZE);
+ else
+ gcov_histogram_merge (summary.ctrs[GCOV_COUNTER_ARCS].histogram,
+ sum.ctrs[GCOV_COUNTER_ARCS].histogram);
new_summary = 0;
}
else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
elt.ident = fn_ident;
elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
- slot = (counts_entry_t **) htab_find_slot
- (counts_hash, &elt, INSERT);
+ slot = counts_hash->find_slot (&elt, INSERT);
entry = *slot;
if (!entry)
{
entry->ctr = elt.ctr;
entry->lineno_checksum = lineno_checksum;
entry->cfg_checksum = cfg_checksum;
- entry->summary = summary.ctrs[elt.ctr];
- entry->summary.num = n_counts;
+ if (elt.ctr < GCOV_COUNTERS_SUMMABLE)
+ entry->summary = summary.ctrs[elt.ctr];
+ entry->summary.num = n_counts;
entry->counts = XCNEWVEC (gcov_type, n_counts);
}
else if (entry->lineno_checksum != lineno_checksum
error ("checksum is (%x,%x) instead of (%x,%x)",
entry->lineno_checksum, entry->cfg_checksum,
lineno_checksum, cfg_checksum);
- htab_delete (counts_hash);
+ 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);
- htab_delete (counts_hash);
+ 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);
- htab_delete (counts_hash);
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
}
{
static int warned = 0;
- if (!warned++)
- inform (input_location, (flag_guess_branch_prob
- ? "file %s not found, execution counts estimated"
- : "file %s not found, execution counts assumed to be zero"),
- da_file_name);
+ if (!warned++ && dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ (flag_guess_branch_prob
+ ? "file %s not found, execution counts estimated\n"
+ : "file %s not found, execution counts assumed to "
+ "be zero\n"),
+ 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_entry_t *) htab_find (counts_hash, &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
warning_at (input_location, OPT_Wcoverage_mismatch,
"the control flow of function %qE does not match "
"its profile data (counter %qs)", id, ctr_names[counter]);
- if (warning_printed)
+ if (warning_printed && dump_enabled_p ())
{
- inform (input_location, "use -Wno-error=coverage-mismatch to tolerate "
- "the mismatch but performance may drop if the function is hot");
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "use -Wno-error=coverage-mismatch to tolerate "
+ "the mismatch but performance may drop if the "
+ "function is hot\n");
if (!seen_error ()
&& !warned++)
{
- inform (input_location, "coverage mismatch ignored");
- inform (input_location, flag_guess_branch_prob
- ? G_("execution counts estimated")
- : G_("execution counts assumed to be zero"));
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "coverage mismatch ignored\n");
+ dump_printf (MSG_OPTIMIZED_LOCATIONS,
+ flag_guess_branch_prob
+ ? G_("execution counts estimated\n")
+ : G_("execution counts assumed to be zero\n"));
if (!flag_guess_branch_prob)
- inform (input_location,
- "this can result in poorly optimized code");
+ dump_printf (MSG_OPTIMIZED_LOCATIONS,
+ "this can result in poorly optimized code\n");
}
}
}
else if (entry->lineno_checksum != lineno_checksum)
{
- warning (0, "source location 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));
}
return chksum;
}
-/* Compute cfg checksum for the current function.
+/* Compute profile ID. This is better to be unique in whole program. */
+
+unsigned
+coverage_compute_profile_id (struct cgraph_node *n)
+{
+ unsigned chksum;
+
+ /* 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.
+ Gcov file formats wants non-zero function IDs. */
+ chksum = chksum & 0x7fffffff;
+ return chksum + (!chksum);
+}
+
+/* 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;
+ unsigned chksum = n_basic_blocks_for_fn (fn);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, fn)
{
edge e;
edge_iterator ei;
return chksum;
}
\f
-/* Begin output to the graph file for the current function.
- Opens the output file, if not already done. Writes the
- function header, if not already done. Returns nonzero if data
- should be output. */
+/* Begin output to the notes file for the current function.
+ Writes the function header. Returns nonzero if data should be output. */
int
-coverage_begin_output (unsigned lineno_checksum, unsigned cfg_checksum)
+coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
+ expanded_location xloc;
+ unsigned long offset;
+
/* We don't need to output .gcno file unless we're under -ftest-coverage
(e.g. -fprofile-arcs/generate/use don't need .gcno to work). */
- if (no_coverage || !flag_test_coverage || flag_compare_debug)
+ if (no_coverage || !bbg_file_name)
return 0;
- if (!bbg_function_announced)
- {
- expanded_location xloc
- = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
- unsigned long offset;
-
- if (!bbg_file_opened)
- {
- if (!gcov_open (bbg_file_name, -1))
- error ("cannot open %s", bbg_file_name);
- else
- {
- gcov_write_unsigned (GCOV_NOTE_MAGIC);
- gcov_write_unsigned (GCOV_VERSION);
- gcov_write_unsigned (local_tick);
- }
- bbg_file_opened = 1;
- }
+ xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
+ /* Announce function */
+ offset = gcov_write_tag (GCOV_TAG_FUNCTION);
+ 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);
+ }
- /* Announce function */
- offset = gcov_write_tag (GCOV_TAG_FUNCTION);
- gcov_write_unsigned (current_function_funcdef_no + 1);
- gcov_write_unsigned (lineno_checksum);
- gcov_write_unsigned (cfg_checksum);
- gcov_write_string (IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl)));
- gcov_write_string (xloc.file);
- gcov_write_unsigned (xloc.line);
- gcov_write_length (offset);
+ gcov_write_unsigned (lineno_checksum);
+ gcov_write_unsigned (cfg_checksum);
+ gcov_write_string (IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (current_function_decl)));
+ gcov_write_string (xloc.file);
+ gcov_write_unsigned (xloc.line);
+ gcov_write_length (offset);
- bbg_function_announced = 1;
- }
return !gcov_is_error ();
}
{
unsigned i;
- if (bbg_file_opened > 1 && gcov_is_error ())
+ if (bbg_file_name && gcov_is_error ())
{
warning (0, "error writing %qs", bbg_file_name);
- bbg_file_opened = -1;
+ unlink (bbg_file_name);
+ bbg_file_name = NULL;
}
if (fn_ctr_mask)
{
- struct function_list *item;
+ struct coverage_data *item = 0;
- item = ggc_alloc_function_list ();
+ item = ggc_alloc<coverage_data> ();
+
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ item->ident = current_function_funcdef_no + 1;
+ else
+ {
+ gcc_assert (coverage_node_map_initialized_p ());
+ item->ident = cgraph_node::get (cfun->decl)->profile_id;
+ }
- item->next = 0;
- 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;
+
for (i = 0; i != GCOV_COUNTERS; i++)
{
tree var = fn_v_ctrs[i];
-
- item->ctr_vars[i] = var;
+
+ if (item)
+ item->ctr_vars[i] = var;
if (var)
{
tree array_type = build_index_type (size_int (fn_n_ctrs[i] - 1));
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;
fn_v_ctrs[i] = NULL_TREE;
}
prg_ctr_mask |= fn_ctr_mask;
fn_ctr_mask = 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 (item->fn_decl))
- {
- *functions_tail = item;
- functions_tail = &item->next;
- }
}
- bbg_function_announced = 0;
}
/* Build a coverage variable of TYPE for function FN_DECL. If COUNTER
- >= 0 it is a counter array, otherwise it is the function structure.
- Propagate appropriate linkage and visibility from the function decl. */
+ >= 0 it is a counter array, otherwise it is the function structure. */
static tree
build_var (tree fn_decl, tree type, int counter)
{
tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, type);
- tree fn_name = DECL_ASSEMBLER_NAME (fn_decl);
- char *buf = (char *)alloca (IDENTIFIER_LENGTH (fn_name) + 10);
+ const char *fn_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fn_decl));
+ char *buf;
+ size_t fn_name_len, len;
+
+ fn_name = targetm.strip_name_encoding (fn_name);
+ fn_name_len = strlen (fn_name);
+ buf = XALLOCAVEC (char, fn_name_len + 8 + sizeof (int) * 3);
if (counter < 0)
- sprintf (buf, "__gcov__%s", IDENTIFIER_POINTER (fn_name));
+ strcpy (buf, "__gcov__");
else
- sprintf (buf, "__gcov%u_%s", counter, IDENTIFIER_POINTER (fn_name));
+ sprintf (buf, "__gcov%u_", counter);
+ len = strlen (buf);
+#ifndef NO_DOT_IN_LABEL
+ buf[len - 1] = '.';
+#elif !defined NO_DOLLAR_IN_LABEL
+ buf[len - 1] = '$';
+#endif
+ memcpy (buf + len, fn_name, fn_name_len + 1);
DECL_NAME (var) = get_identifier (buf);
TREE_STATIC (var) = 1;
TREE_ADDRESSABLE (var) = 1;
+ DECL_NONALIASED (var) = 1;
DECL_ALIGN (var) = TYPE_ALIGN (type);
- DECL_WEAK (var) = DECL_WEAK (fn_decl);
- TREE_PUBLIC (var)
- = TREE_PUBLIC (fn_decl) && (counter < 0 || DECL_WEAK (fn_decl));
- if (DECL_ONE_ONLY (fn_decl))
- make_decl_one_only (var, DECL_COMDAT_GROUP (fn_decl));
-
- if (TREE_PUBLIC (var))
- {
- DECL_VISIBILITY (var) = DECL_VISIBILITY (fn_decl);
- DECL_VISIBILITY_SPECIFIED (var)
- = DECL_VISIBILITY_SPECIFIED (fn_decl);
-
- /* Initialize assembler name so we can stream out. */
- DECL_ASSEMBLER_NAME (var);
- }
return var;
}
finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
}
-/* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is
- the function being processed and TYPE is the gcov_fn_info
- RECORD_TYPE. KEY is the object file key. */
+/* Returns a CONSTRUCTOR for a gcov_fn_info. DATA is
+ the coverage data for the function and TYPE is the gcov_fn_info
+ RECORD_TYPE. KEY is the object file key. */
static tree
-build_fn_info (const struct function_list *function, tree type, tree key)
+build_fn_info (const struct coverage_data *data, tree type, tree key)
{
tree fields = TYPE_FIELDS (type);
tree ctr_type;
unsigned ix;
- VEC(constructor_elt,gc) *v1 = NULL;
- VEC(constructor_elt,gc) *v2 = NULL;
+ vec<constructor_elt, va_gc> *v1 = NULL;
+ vec<constructor_elt, va_gc> *v2 = NULL;
/* key */
CONSTRUCTOR_APPEND_ELT (v1, fields,
/* ident */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
- function->ident));
+ data->ident));
fields = DECL_CHAIN (fields);
/* lineno_checksum */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
- function->lineno_checksum));
+ data->lineno_checksum));
fields = DECL_CHAIN (fields);
/* cfg_checksum */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (),
- function->cfg_checksum));
+ data->cfg_checksum));
fields = DECL_CHAIN (fields);
/* counters */
for (ix = 0; ix != GCOV_COUNTERS; ix++)
if (prg_ctr_mask & (1 << ix))
{
- VEC(constructor_elt,gc) *ctr = NULL;
- tree var = function->ctr_vars[ix];
+ vec<constructor_elt, va_gc> *ctr = NULL;
+ tree var = data->ctr_vars[ix];
unsigned count = 0;
if (var)
count
- = tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))), 0)
+ = tree_to_shwi (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))))
+ 1;
CONSTRUCTOR_APPEND_ELT (ctr, TYPE_FIELDS (ctr_type),
return build_constructor (type, v1);
}
-/* Creaste gcov_info_struct. N_FUNCS is the number of functions in
- the trailing array. */
+/* Create gcov_info struct. TYPE is the incomplete RECORD_TYPE to be
+ completed, and FN_INFO_PTR_TYPE is a pointer to the function info type. */
static void
-build_info_type (tree type, unsigned n_funcs, tree fn_info_type)
+build_info_type (tree type, tree fn_info_ptr_type)
{
tree field, fields = NULL_TREE;
- tree merge_fn_type, fn_info_array;
+ tree merge_fn_type;
- gcc_assert (n_funcs);
-
/* Version ident */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
- /* function_info pointer array */
- fn_info_type = build_pointer_type
- (build_qualified_type (fn_info_type, TYPE_QUAL_CONST));
- fn_info_array = build_index_type (size_int (n_funcs));
- fn_info_array = build_array_type (fn_info_type, fn_info_array);
+ /* function_info pointer pointer */
+ fn_info_ptr_type = build_pointer_type
+ (build_qualified_type (fn_info_ptr_type, TYPE_QUAL_CONST));
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
- fn_info_array);
+ fn_info_ptr_type);
DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
}
-/* Creates the gcov_info initializer. Returns a CONSTRUCTOR. */
+/* Returns a CONSTRUCTOR for the gcov_info object. INFO_TYPE is the
+ gcov_info structure type, FN_ARY is the array of pointers to
+ function info objects. */
static tree
-build_info (tree info_type, tree fn_type, tree key_var, unsigned n_funcs)
+build_info (tree info_type, tree fn_ary)
{
tree info_fields = TYPE_FIELDS (info_type);
- tree merge_fn_type, fn_info_ptr_type;
+ tree merge_fn_type, n_funcs;
unsigned ix;
tree filename_string;
int da_file_name_len;
- const struct function_list *fn;
- VEC(constructor_elt,gc) *v1 = NULL;
- VEC(constructor_elt,gc) *v2 = NULL;
- VEC(constructor_elt,gc) *v3 = NULL;
+ vec<constructor_elt, va_gc> *v1 = NULL;
+ vec<constructor_elt, va_gc> *v2 = NULL;
/* Version ident */
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
/* stamp */
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build_int_cstu (TREE_TYPE (info_fields),
- local_tick));
+ bbg_file_stamp));
info_fields = DECL_CHAIN (info_fields);
/* Filename */
info_fields = DECL_CHAIN (info_fields);
/* n_functions */
- CONSTRUCTOR_APPEND_ELT (v1, info_fields,
- build_int_cstu (TREE_TYPE (info_fields), n_funcs));
+ n_funcs = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (fn_ary)));
+ n_funcs = fold_build2 (PLUS_EXPR, TREE_TYPE (info_fields),
+ n_funcs, size_one_node);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields, n_funcs);
info_fields = DECL_CHAIN (info_fields);
-
- /* Build the fn_info type and initializer. */
- fn_info_ptr_type = TREE_TYPE (TREE_TYPE (info_fields));
-
- for (fn = functions_head; fn; fn = fn->next)
- {
- tree init = build_fn_info (fn, fn_type, key_var);
- tree var = build_var (fn->fn_decl, fn_type, -1);
- DECL_INITIAL (var) = init;
- varpool_finalize_decl (var);
-
- CONSTRUCTOR_APPEND_ELT (v3, NULL,
- build1 (ADDR_EXPR, fn_info_ptr_type, var));
- }
+ /* functions */
CONSTRUCTOR_APPEND_ELT (v1, info_fields,
- build_constructor (TREE_TYPE (info_fields), v3));
+ build1 (ADDR_EXPR, TREE_TYPE (info_fields), fn_ary));
+ info_fields = DECL_CHAIN (info_fields);
+
+ gcc_assert (!info_fields);
return build_constructor (info_type, v1);
}
-/* Write out the structure which libgcov uses to locate all the
- counters. The structures used here must match those defined in
- gcov-io.h. Write out the constructor to call __gcov_init. */
+/* Generate the constructor function to call __gcov_init. */
static void
-create_coverage (void)
+build_init_ctor (tree gcov_info_type)
{
- tree gcov_info, gcov_init, body, t;
- tree gcov_info_type, gcov_fn_type;
- unsigned n_counters = 0, n_functions = 0;
- struct function_list *fn;
- struct function_list **fn_prev;
+ tree ctor, stmt, init_fn;
+
+ /* Build a decl for __gcov_init. */
+ init_fn = build_pointer_type (gcov_info_type);
+ init_fn = build_function_type_list (void_type_node, init_fn, NULL);
+ init_fn = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+ get_identifier ("__gcov_init"), init_fn);
+ TREE_PUBLIC (init_fn) = 1;
+ DECL_EXTERNAL (init_fn) = 1;
+ DECL_ASSEMBLER_NAME (init_fn);
+
+ /* Generate a call to __gcov_init(&gcov_info). */
+ ctor = NULL;
+ stmt = build_fold_addr_expr (gcov_info_var);
+ stmt = build_call_expr (init_fn, 1, stmt);
+ append_to_statement_list (stmt, &ctor);
+
+ /* Generate a constructor to run it. */
+ cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
+}
+
+/* Create the gcov_info types and object. Generate the constructor
+ function to call __gcov_init. Does not generate the initializer
+ for the object. Returns TRUE if coverage data is being emitted. */
+
+static bool
+coverage_obj_init (void)
+{
+ tree gcov_info_type;
+ unsigned n_counters = 0;
unsigned ix;
+ struct coverage_data *fn;
+ struct coverage_data **fn_prev;
char name_buf[32];
no_coverage = 1; /* Disable any further coverage. */
if (!prg_ctr_mask)
- return;
+ 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);
- for (ix = 0; ix != GCOV_COUNTERS; ix++)
- if ((1u << ix) & prg_ctr_mask)
- n_counters++;
+ /* Prune functions. */
for (fn_prev = &functions_head; (fn = *fn_prev);)
if (DECL_STRUCT_FUNCTION (fn->fn_decl))
- {
- n_functions++;
- fn_prev = &fn->next;
- }
+ fn_prev = &fn->next;
else
/* The function is not being emitted, remove from list. */
*fn_prev = fn->next;
+
+ if (functions_head == NULL)
+ return false;
+
+ for (ix = 0; ix != GCOV_COUNTERS; ix++)
+ if ((1u << ix) & prg_ctr_mask)
+ n_counters++;
/* Build the info and fn_info types. These are mutually recursive. */
gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE);
- gcov_fn_type = lang_hooks.types.make_type (RECORD_TYPE);
- build_fn_info_type (gcov_fn_type, n_counters, gcov_info_type);
- build_info_type (gcov_info_type, n_functions, gcov_fn_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_info_type (gcov_info_type, gcov_fn_info_ptr_type);
/* Build the gcov info var, this is referred to in its own
initializer. */
- gcov_info = build_decl (BUILTINS_LOCATION,
- VAR_DECL, NULL_TREE, gcov_info_type);
- TREE_STATIC (gcov_info) = 1;
+ gcov_info_var = build_decl (BUILTINS_LOCATION,
+ VAR_DECL, NULL_TREE, gcov_info_type);
+ TREE_STATIC (gcov_info_var) = 1;
ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
- DECL_NAME (gcov_info) = get_identifier (name_buf);
- DECL_INITIAL (gcov_info) = build_info (gcov_info_type, gcov_fn_type,
- gcov_info, n_functions);
+ DECL_NAME (gcov_info_var) = get_identifier (name_buf);
- /* Build structure. */
- varpool_finalize_decl (gcov_info);
+ build_init_ctor (gcov_info_type);
- /* Build a decl for __gcov_init. */
- t = build_pointer_type (TREE_TYPE (gcov_info));
- t = build_function_type_list (void_type_node, t, NULL);
- t = build_decl (BUILTINS_LOCATION,
- FUNCTION_DECL, get_identifier ("__gcov_init"), t);
- TREE_PUBLIC (t) = 1;
- DECL_EXTERNAL (t) = 1;
- DECL_ASSEMBLER_NAME (t); /* Initialize assembler name so we can stream out. */
- gcov_init = t;
+ return true;
+}
- /* Generate a call to __gcov_init(&gcov_info). */
- body = NULL;
- t = build_fold_addr_expr (gcov_info);
- t = build_call_expr (gcov_init, 1, t);
- append_to_statement_list (t, &body);
+/* Generate the coverage function info for FN and DATA. Append a
+ pointer to that object to CTOR and return the appended CTOR. */
- /* Generate a constructor to run it. */
- cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY);
+static vec<constructor_elt, va_gc> *
+coverage_obj_fn (vec<constructor_elt, va_gc> *ctor, tree fn,
+ struct coverage_data const *data)
+{
+ tree init = build_fn_info (data, gcov_fn_info_type, gcov_info_var);
+ tree var = build_var (fn, gcov_fn_info_type, -1);
+
+ DECL_INITIAL (var) = init;
+ varpool_node::finalize_decl (var);
+
+ CONSTRUCTOR_APPEND_ELT (ctor, NULL,
+ build1 (ADDR_EXPR, gcov_fn_info_ptr_type, var));
+ return ctor;
}
-\f
+
+/* Finalize the coverage data. Generates the array of pointers to
+ function objects from CTOR. Generate the gcov_info initializer. */
+
+static void
+coverage_obj_finish (vec<constructor_elt, va_gc> *ctor)
+{
+ unsigned n_functions = vec_safe_length (ctor);
+ tree fn_info_ary_type = build_array_type
+ (build_qualified_type (gcov_fn_info_ptr_type, TYPE_QUAL_CONST),
+ build_index_type (size_int (n_functions - 1)));
+ tree fn_info_ary = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE,
+ fn_info_ary_type);
+ char name_buf[32];
+
+ TREE_STATIC (fn_info_ary) = 1;
+ 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_node::finalize_decl (fn_info_ary);
+
+ DECL_INITIAL (gcov_info_var)
+ = build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
+ varpool_node::finalize_decl (gcov_info_var);
+}
+
/* Perform file-level initialization. Read in data file, generate name
- of graph file. */
+ of notes file. */
void
coverage_init (const char *filename)
{
int len = strlen (filename);
- /* + 1 for extra '/', in case prefix doesn't end with /. */
- int prefix_len;
+ int prefix_len = 0;
- if (profile_data_prefix == 0 && !IS_ABSOLUTE_PATH(&filename[0]))
+ /* Since coverage_init is invoked very early, before the pass
+ manager, we need to set up the dumping explicitly. This is
+ similar to the handling in finish_optimization_passes. */
+ int profile_pass_num =
+ g->get_passes ()->get_pass_profile ()->static_pass_number;
+ g->get_dumps ()->dump_start (profile_pass_num, NULL);
+
+ if (!profile_data_prefix && !IS_ABSOLUTE_PATH (filename))
profile_data_prefix = getpwd ();
- prefix_len = (profile_data_prefix) ? strlen (profile_data_prefix) + 1 : 0;
+ if (profile_data_prefix)
+ prefix_len = strlen (profile_data_prefix);
/* Name of da file. */
da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
- + prefix_len + 1);
+ + prefix_len + 2);
if (profile_data_prefix)
{
- strcpy (da_file_name, profile_data_prefix);
- da_file_name[prefix_len - 1] = '/';
- da_file_name[prefix_len] = 0;
+ memcpy (da_file_name, profile_data_prefix, prefix_len);
+ da_file_name[prefix_len++] = '/';
}
- else
- da_file_name[0] = 0;
- strcat (da_file_name, filename);
- strcat (da_file_name, GCOV_DATA_SUFFIX);
+ memcpy (da_file_name + prefix_len, filename, len);
+ strcpy (da_file_name + prefix_len + len, GCOV_DATA_SUFFIX);
+
+ bbg_file_stamp = local_tick;
+
+ if (flag_auto_profile)
+ read_autofdo_file ();
+ else if (flag_branch_probabilities)
+ read_counts_file ();
/* Name of bbg file. */
- bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1);
- strcpy (bbg_file_name, filename);
- strcat (bbg_file_name, GCOV_NOTE_SUFFIX);
+ if (flag_test_coverage && !flag_compare_debug)
+ {
+ bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1);
+ memcpy (bbg_file_name, filename, len);
+ strcpy (bbg_file_name + len, GCOV_NOTE_SUFFIX);
- if (flag_branch_probabilities)
- read_counts_file ();
+ if (!gcov_open (bbg_file_name, -1))
+ {
+ error ("cannot open %s", bbg_file_name);
+ bbg_file_name = NULL;
+ }
+ else
+ {
+ gcov_write_unsigned (GCOV_NOTE_MAGIC);
+ gcov_write_unsigned (GCOV_VERSION);
+ gcov_write_unsigned (bbg_file_stamp);
+ }
+ }
+
+ g->get_dumps ()->dump_finish (profile_pass_num);
}
-/* Performs file-level cleanup. Close graph file, generate coverage
+/* Performs file-level cleanup. Close notes file, generate coverage
variables and constructor. */
void
coverage_finish (void)
{
- create_coverage ();
- if (bbg_file_opened)
+ if (bbg_file_name && gcov_close ())
+ unlink (bbg_file_name);
+
+ if (!flag_branch_probabilities && flag_test_coverage
+ && (!local_tick || local_tick == (unsigned)-1))
+ /* Only remove the da file, if we're emitting coverage code and
+ cannot uniquely stamp it. If we can stamp it, libgcov will DTRT. */
+ unlink (da_file_name);
+
+ if (coverage_obj_init ())
{
- int error = gcov_close ();
-
- if (error)
- unlink (bbg_file_name);
- if (!local_tick)
- /* Only remove the da file, if we cannot stamp it. If we can
- stamp it, libgcov will DTRT. */
- unlink (da_file_name);
+ vec<constructor_elt, va_gc> *fn_ctor = NULL;
+ struct coverage_data *fn;
+
+ for (fn = functions_head; fn; fn = fn->next)
+ fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn);
+ coverage_obj_finish (fn_ctor);
}
+
+ XDELETEVEC (da_file_name);
+ da_file_name = NULL;
}
#include "gt-coverage.h"