/* Read and write coverage files, and associated functionality.
- Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
- 2000, 2001, 2003, 2004 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.
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 2, or (at your option) any later
+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
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#define GCOV_LINKAGE
#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 "ggc.h"
+#include "tm_p.h"
#include "coverage.h"
#include "langhooks.h"
-#include "hashtab.h"
#include "tree-iterator.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 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 checksum; /* function checksum */
- unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */
+ unsigned lineno_checksum; /* function lineno checksum */
+ unsigned cfg_checksum; /* function cfg checksum */
+ tree fn_decl; /* the function decl */
+ tree ctr_vars[GCOV_COUNTERS]; /* counter variables. */
};
/* Counts information for a function. */
-typedef struct counts_entry
+typedef struct counts_entry : pointer_hash <counts_entry>
{
/* We hash by */
unsigned ident;
unsigned ctr;
/* Store */
- unsigned checksum;
+ unsigned lineno_checksum;
+ unsigned cfg_checksum;
gcov_type *counts;
struct gcov_ctr_summary summary;
- /* Workspace */
- struct counts_entry *chain;
-
+ /* 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 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 prg_ctr_mask; /* Mask of counter types generated. */
-static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */
/* Counter information for current function. */
static unsigned fn_ctr_mask; /* Mask of counters used. */
+static GTY(()) tree fn_v_ctrs[GCOV_COUNTERS]; /* counter variables. */
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. */
-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;
+/* 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;
-/* Hash table of count data. */
-static htab_t counts_hash = NULL;
+/* 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;
-/* Trees representing the counter table arrays. */
-static GTY(()) tree tree_ctr_tables[GCOV_COUNTERS];
+/* File stamp for notes file. */
+static unsigned bbg_file_stamp;
-/* The names of the counter tables. Not used if we're
- generating counters at tree level. */
-static GTY(()) rtx ctr_labels[GCOV_COUNTERS];
+/* 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 unsigned compute_checksum (void);
-static unsigned coverage_checksum_string (unsigned, const char *);
-static tree build_fn_info_type (unsigned);
-static tree build_fn_info_value (const struct function_list *, tree);
-static tree build_ctr_info_type (void);
-static tree build_ctr_info_value (unsigned, tree);
-static tree build_gcov_info (void);
-static void create_coverage (void);
-
+static tree build_var (tree, tree, int);
+static void build_fn_info_type (tree, unsigned, tree);
+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
-static hashval_t
-htab_counts_entry_hash (const void *of)
+/* Return the type node for gcov_type. */
+
+tree
+get_gcov_type (void)
{
- const counts_entry_t *entry = of;
+ 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)
+{
+ 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 counts_entry *entry)
+{
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 *entry1 = of1;
- const counts_entry_t *entry2 = 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 *entry = 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
read_counts_file (void)
{
gcov_unsigned_t fn_ident = 0;
- gcov_unsigned_t checksum = -1;
- counts_entry_t *summaried = NULL;
- unsigned seen_summary = 0;
+ struct gcov_summary summary;
+ unsigned new_summary = 1;
gcov_unsigned_t tag;
int is_error = 0;
+ unsigned lineno_checksum = 0;
+ unsigned cfg_checksum = 0;
if (!gcov_open (da_file_name, 1))
return;
if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
{
- warning ("%qs is not a gcov data file", da_file_name);
+ warning (0, "%qs is not a gcov data file", da_file_name);
gcov_close ();
return;
}
GCOV_UNSIGNED2STRING (v, tag);
GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
- warning ("%qs is version %q.*s, expected version %q.*s",
+ warning (0, "%qs is version %q.*s, expected version %q.*s",
da_file_name, 4, v, 4, e);
gcov_close ();
return;
}
- /* Read and discard the stamp. */
- gcov_read_unsigned ();
-
- counts_hash = htab_create (10,
- htab_counts_entry_hash, htab_counts_entry_eq,
- htab_counts_entry_del);
+ /* Read the stamp, used for creating a generation count. */
+ tag = gcov_read_unsigned ();
+ bbg_file_stamp = crc32_unsigned (bbg_file_stamp, tag);
+
+ counts_hash = new hash_table<counts_entry> (10);
while ((tag = gcov_read_unsigned ()))
{
gcov_unsigned_t length;
offset = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
- fn_ident = gcov_read_unsigned ();
- checksum = gcov_read_unsigned ();
- if (seen_summary)
+ if (length)
{
- /* We have already seen a summary, this means that this
- new function begins a new set of program runs. We
- must unlink the summaried chain. */
- counts_entry_t *entry, *chain;
-
- for (entry = summaried; entry; entry = chain)
- {
- chain = entry->chain;
- entry->chain = NULL;
- }
- summaried = NULL;
- seen_summary = 0;
+ fn_ident = gcov_read_unsigned ();
+ lineno_checksum = gcov_read_unsigned ();
+ cfg_checksum = gcov_read_unsigned ();
}
+ else
+ fn_ident = lineno_checksum = cfg_checksum = 0;
+ new_summary = 1;
}
else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
{
- counts_entry_t *entry;
- struct gcov_summary summary;
+ struct gcov_summary sum;
+ unsigned ix;
- gcov_read_summary (&summary);
- seen_summary = 1;
- for (entry = summaried; entry; entry = entry->chain)
- {
- struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr];
+ if (new_summary)
+ memset (&summary, 0, sizeof (summary));
- entry->summary.runs += csum->runs;
- entry->summary.sum_all += csum->sum_all;
- if (entry->summary.run_max < csum->run_max)
- entry->summary.run_max = csum->run_max;
- entry->summary.sum_max += csum->sum_max;
+ gcov_read_summary (&sum);
+ for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++)
+ {
+ summary.ctrs[ix].runs += sum.ctrs[ix].runs;
+ summary.ctrs[ix].sum_all += sum.ctrs[ix].sum_all;
+ if (summary.ctrs[ix].run_max < sum.ctrs[ix].run_max)
+ 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)
{
- *slot = entry = xcalloc (1, sizeof (counts_entry_t));
- entry->ident = elt.ident;
+ *slot = entry = XCNEW (counts_entry_t);
+ entry->ident = fn_ident;
entry->ctr = elt.ctr;
- entry->checksum = checksum;
- entry->summary.num = n_counts;
- entry->counts = xcalloc (n_counts, sizeof (gcov_type));
+ entry->lineno_checksum = lineno_checksum;
+ entry->cfg_checksum = cfg_checksum;
+ 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->checksum != checksum)
+ else if (entry->lineno_checksum != lineno_checksum
+ || entry->cfg_checksum != cfg_checksum)
{
- error ("coverage mismatch for function %u while reading execution counters.",
- fn_ident);
- error ("checksum is %x instead of %x", entry->checksum, checksum);
- htab_delete (counts_hash);
+ error ("Profile data for function %u is corrupted", fn_ident);
+ error ("checksum is (%x,%x) instead of (%x,%x)",
+ entry->lineno_checksum, entry->cfg_checksum,
+ lineno_checksum, cfg_checksum);
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
else if (entry->summary.num != n_counts)
{
- error ("coverage mismatch for function %u while reading execution counters.",
- fn_ident);
+ 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)
ctr_names[elt.ctr], fn_ident);
goto skip_merge;
}
-
- if (elt.ctr < GCOV_COUNTERS_SUMMABLE
- /* This should always be true for a just allocated entry,
- and always false for an existing one. Check this way, in
- case the gcov file is corrupt. */
- && (!entry->chain || summaried != entry))
+ else
{
- entry->chain = summaried;
- summaried = entry;
+ entry->summary.runs += summary.ctrs[elt.ctr].runs;
+ entry->summary.sum_all += summary.ctrs[elt.ctr].sum_all;
+ if (entry->summary.run_max < summary.ctrs[elt.ctr].run_max)
+ entry->summary.run_max = summary.ctrs[elt.ctr].run_max;
+ entry->summary.sum_max += summary.ctrs[elt.ctr].sum_max;
}
for (ix = 0; ix != n_counts; ix++)
entry->counts[ix] += gcov_read_counter ();
{
error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted",
da_file_name);
- htab_delete (counts_hash);
+ delete counts_hash;
+ counts_hash = NULL;
break;
}
}
gcov_type *
get_coverage_counts (unsigned counter, unsigned expected,
+ unsigned cfg_checksum, unsigned lineno_checksum,
const struct gcov_ctr_summary **summary)
{
counts_entry_t *entry, elt;
- gcov_unsigned_t checksum = -1;
/* No hash table, no counts. */
if (!counts_hash)
{
static int warned = 0;
- if (!warned++)
- inform ((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;
- elt.ctr = counter;
- entry = htab_find (counts_hash, &elt);
- if (!entry)
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ elt.ident = current_function_funcdef_no + 1;
+ else
{
- warning ("no coverage for function %qs found.", IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl)));
- return 0;
+ gcc_assert (coverage_node_map_initialized_p ());
+ elt.ident = cgraph_node::get (cfun->decl)->profile_id;
}
-
- checksum = compute_checksum ();
- if (entry->checksum != checksum)
+ elt.ctr = counter;
+ 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
+ can do about it. */
+ return NULL;
+
+ if (entry->cfg_checksum != cfg_checksum
+ || entry->summary.num != expected)
{
- error ("coverage mismatch for function %qs while reading counter %qs.",
- IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)),
- ctr_names[counter]);
- error ("checksum is %x instead of %x", entry->checksum, checksum);
- return 0;
+ static int warned = 0;
+ bool warning_printed = false;
+ tree id = DECL_ASSEMBLER_NAME (current_function_decl);
+
+ warning_printed =
+ 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 && dump_enabled_p ())
+ {
+ 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++)
+ {
+ 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)
+ dump_printf (MSG_OPTIMIZED_LOCATIONS,
+ "this can result in poorly optimized code\n");
+ }
+ }
+
+ return NULL;
}
- else if (entry->summary.num != expected)
+ else if (entry->lineno_checksum != lineno_checksum)
{
- error ("coverage mismatch for function %qs while reading counter %qs.",
- IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)),
- ctr_names[counter]);
- error ("number of counters is %d instead of %d", entry->summary.num, expected);
- return 0;
+ 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));
}
if (summary)
if (!num)
return 1;
- if (!tree_ctr_tables[counter])
+ if (!fn_v_ctrs[counter])
{
- /* Generate and save a copy of this so it can be shared. */
- /* We don't know the size yet; make it big enough that nobody
- will make any clever transformation on it. */
- char buf[20];
- tree domain_tree
- = build_index_type (build_int_cst (NULL_TREE, 1000)); /* replaced later */
- tree gcov_type_array_type
- = build_array_type (GCOV_TYPE_NODE, domain_tree);
- tree_ctr_tables[counter]
- = build_decl (VAR_DECL, NULL_TREE, gcov_type_array_type);
- TREE_STATIC (tree_ctr_tables[counter]) = 1;
- ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1);
- DECL_NAME (tree_ctr_tables[counter]) = get_identifier (buf);
- DECL_ALIGN (tree_ctr_tables[counter]) = TYPE_ALIGN (GCOV_TYPE_NODE);
+ tree array_type = build_array_type (get_gcov_type (), NULL_TREE);
+
+ fn_v_ctrs[counter]
+ = build_var (current_function_decl, array_type, counter);
}
+
fn_b_ctrs[counter] = fn_n_ctrs[counter];
fn_n_ctrs[counter] += num;
+
fn_ctr_mask |= 1 << counter;
return 1;
}
-/* Generate a MEM rtl to access COUNTER NO. */
+/* Generate a tree to access COUNTER NO. */
-rtx
-rtl_coverage_counter_ref (unsigned counter, unsigned no)
+tree
+tree_coverage_counter_ref (unsigned counter, unsigned no)
{
- unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1);
- enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0);
- rtx ref;
+ tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
- no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
- if (!ctr_labels[counter])
- {
- ctr_labels[counter] = gen_rtx_SYMBOL_REF (Pmode,
- ggc_strdup (IDENTIFIER_POINTER (DECL_NAME
- (tree_ctr_tables[counter]))));
- SYMBOL_REF_FLAGS (ctr_labels[counter]) = SYMBOL_FLAG_LOCAL;
- }
- ref = plus_constant (ctr_labels[counter], gcov_size / BITS_PER_UNIT * no);
- ref = gen_rtx_MEM (mode, ref);
- set_mem_alias_set (ref, new_alias_set ());
- MEM_NOTRAP_P (ref) = 1;
- return ref;
+ no += fn_b_ctrs[counter];
+
+ /* "no" here is an array index, scaled to bytes later. */
+ return build4 (ARRAY_REF, gcov_type_node, fn_v_ctrs[counter],
+ build_int_cst (integer_type_node, no), NULL, NULL);
}
-/* Generate a tree to access COUNTER NO. */
+/* Generate a tree to access the address of COUNTER NO. */
tree
-tree_coverage_counter_ref (unsigned counter, unsigned no)
+tree_coverage_counter_addr (unsigned counter, unsigned no)
{
- tree domain_type = TYPE_DOMAIN (TREE_TYPE (tree_ctr_tables[counter]));
+ tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
- no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
+ no += fn_b_ctrs[counter];
/* "no" here is an array index, scaled to bytes later. */
- return build4 (ARRAY_REF, GCOV_TYPE_NODE, tree_ctr_tables[counter],
- fold_convert (domain_type,
- build_int_cst (NULL_TREE, no)),
- TYPE_MIN_VALUE (domain_type),
- size_binop (EXACT_DIV_EXPR, TYPE_SIZE_UNIT (GCOV_TYPE_NODE),
- size_int (TYPE_ALIGN_UNIT (GCOV_TYPE_NODE))));
+ return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node,
+ fn_v_ctrs[counter],
+ build_int_cst (integer_type_node, no),
+ NULL, NULL));
}
\f
+
/* Generate a checksum for a string. CHKSUM is the current
checksum. */
char *dup = NULL;
/* Look for everything that looks if it were produced by
- get_file_function_name_long and zero out the second part
+ get_file_function_name and zero out the second part
that may result from flag_random_seed. This is not critical
as the checksums are used only for sanity checking. */
for (i = 0; string[i]; i++)
{
+ int offset = 0;
+ if (!strncmp (string + i, "_GLOBAL__N_", 11))
+ offset = 11;
if (!strncmp (string + i, "_GLOBAL__", 9))
- for (i = i + 9; string[i]; i++)
- if (string[i]=='_')
- {
- int y;
- unsigned seed;
- int scan;
-
- for (y = 1; y < 9; y++)
- if (!(string[i + y] >= '0' && string[i + y] <= '9')
- && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
- break;
- if (y != 9 || string[i + 9] != '_')
- continue;
- for (y = 10; y < 18; y++)
- if (!(string[i + y] >= '0' && string[i + y] <= '9')
- && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
- break;
- if (y != 18)
- continue;
- scan = sscanf (string + i + 10, "%X", &seed);
- gcc_assert (scan);
- if (seed != crc32_string (0, flag_random_seed))
- continue;
- string = dup = xstrdup (string);
- for (y = 10; y < 18; y++)
- dup[i + y] = '0';
- break;
- }
- break;
+ offset = 9;
+
+ /* C++ namespaces do have scheme:
+ _GLOBAL__N_<filename>_<wrongmagicnumber>_<magicnumber>functionname
+ since filename might contain extra underscores there seems
+ to be no better chance then walk all possible offsets looking
+ for magicnumber. */
+ if (offset)
+ {
+ for (i = i + offset; string[i]; i++)
+ if (string[i]=='_')
+ {
+ int y;
+
+ for (y = 1; y < 9; y++)
+ if (!(string[i + y] >= '0' && string[i + y] <= '9')
+ && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
+ break;
+ if (y != 9 || string[i + 9] != '_')
+ continue;
+ for (y = 10; y < 18; y++)
+ if (!(string[i + y] >= '0' && string[i + y] <= '9')
+ && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
+ break;
+ if (y != 18)
+ continue;
+ if (!dup)
+ string = dup = xstrdup (string);
+ for (y = 10; y < 18; y++)
+ dup[i + y] = '0';
+ }
+ break;
+ }
}
chksum = crc32_string (chksum, string);
- if (dup)
- free (dup);
+ free (dup);
return chksum;
}
/* Compute checksum for the current function. We generate a CRC32. */
-static unsigned
-compute_checksum (void)
+unsigned
+coverage_compute_lineno_checksum (void)
{
expanded_location xloc
= expand_location (DECL_SOURCE_LOCATION (current_function_decl));
return chksum;
}
+
+/* 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.
+ This is to make the profile data useable across source code change.
+ The downside of this is that the compiler may use potentially
+ wrong profile data - that the source code change has non-trivial impact
+ on the validity of profile data (e.g. the reversed condition)
+ but the compiler won't detect the change and use the wrong profile data. */
+
+unsigned
+coverage_compute_cfg_checksum (struct function *fn)
+{
+ basic_block bb;
+ unsigned chksum = n_basic_blocks_for_fn (fn);
+
+ FOR_EACH_BB_FN (bb, fn)
+ {
+ edge e;
+ edge_iterator ei;
+ chksum = crc32_byte (chksum, bb->index);
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ chksum = crc32_byte (chksum, e->dest->index);
+ }
+ }
+
+ 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 (void)
+coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
- if (no_coverage)
+ 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 || !bbg_file_name)
return 0;
- if (!bbg_function_announced)
+ 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
{
- expanded_location xloc
- = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
- unsigned long offset;
+ gcc_assert (coverage_node_map_initialized_p ());
+ gcov_write_unsigned (
+ cgraph_node::get (current_function_decl)->profile_id);
+ }
- 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;
- }
+ 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);
- /* Announce function */
- offset = gcov_write_tag (GCOV_TAG_FUNCTION);
- gcov_write_unsigned (current_function_funcdef_no + 1);
- gcov_write_unsigned (compute_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 ();
}
error has occurred. Save function coverage counts. */
void
-coverage_end_function (void)
+coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
unsigned i;
- if (bbg_file_opened > 1 && gcov_is_error ())
+ if (bbg_file_name && gcov_is_error ())
{
- warning ("error writing %qs", bbg_file_name);
- bbg_file_opened = -1;
+ warning (0, "error writing %qs", bbg_file_name);
+ unlink (bbg_file_name);
+ bbg_file_name = NULL;
}
if (fn_ctr_mask)
{
- struct function_list *item;
+ struct coverage_data *item = 0;
- item = xmalloc (sizeof (struct 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->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;
- item->next = 0;
- item->ident = current_function_funcdef_no + 1;
- item->checksum = compute_checksum ();
for (i = 0; i != GCOV_COUNTERS; i++)
{
- item->n_ctrs[i] = fn_n_ctrs[i];
- prg_n_ctrs[i] += fn_n_ctrs[i];
- fn_n_ctrs[i] = fn_b_ctrs[i] = 0;
+ tree var = fn_v_ctrs[i];
+
+ if (item)
+ item->ctr_vars[i] = var;
+ if (var)
+ {
+ tree array_type = build_index_type (size_int (fn_n_ctrs[i] - 1));
+ array_type = build_array_type (get_gcov_type (), array_type);
+ TREE_TYPE (var) = array_type;
+ DECL_SIZE (var) = TYPE_SIZE (array_type);
+ DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
+ 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;
}
- bbg_function_announced = 0;
}
-/* Creates the gcov_fn_info RECORD_TYPE. */
+/* Build a coverage variable of TYPE for function FN_DECL. If COUNTER
+ >= 0 it is a counter array, otherwise it is the function structure. */
static tree
-build_fn_info_type (unsigned int counters)
+build_var (tree fn_decl, tree type, int counter)
{
- tree type = lang_hooks.types.make_type (RECORD_TYPE);
+ tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, type);
+ 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)
+ strcpy (buf, "__gcov__");
+ else
+ 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);
+
+ return var;
+}
+
+/* Creates the gcov_fn_info RECORD_TYPE. */
+
+static void
+build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
+{
+ tree ctr_info = lang_hooks.types.make_type (RECORD_TYPE);
tree field, fields;
tree array_type;
+ gcc_assert (counters);
+
+ /* ctr_info::num */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ fields = field;
+
+ /* ctr_info::values */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (get_gcov_type ()));
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ finish_builtin_struct (ctr_info, "__gcov_ctr_info", fields, NULL_TREE);
+
+ /* key */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (build_qualified_type
+ (gcov_info_type, TYPE_QUAL_CONST)));
+ fields = field;
+
/* ident */
- fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* lineno_checksum */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
- /* checksum */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
- TREE_CHAIN (field) = fields;
+ /* cfg checksum */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
fields = field;
- array_type = build_int_cst (NULL_TREE, counters - 1);
- array_type = build_index_type (array_type);
- array_type = build_array_type (unsigned_type_node, array_type);
+ array_type = build_index_type (size_int (counters - 1));
+ array_type = build_array_type (ctr_info, array_type);
/* counters */
- field = build_decl (FIELD_DECL, NULL_TREE, array_type);
- TREE_CHAIN (field) = fields;
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, array_type);
+ DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
-
- return type;
}
-/* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is
- the function being processed and TYPE is the gcov_fn_info
- RECORD_TYPE. */
+/* 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_value (const struct function_list *function, tree type)
+build_fn_info (const struct coverage_data *data, tree type, tree key)
{
- tree value = NULL_TREE;
tree fields = TYPE_FIELDS (type);
+ tree ctr_type;
unsigned ix;
- tree array_value = NULL_TREE;
+ vec<constructor_elt, va_gc> *v1 = NULL;
+ vec<constructor_elt, va_gc> *v2 = NULL;
+ /* key */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build1 (ADDR_EXPR, TREE_TYPE (fields), key));
+ fields = DECL_CHAIN (fields);
+
/* ident */
- value = tree_cons (fields, build_int_cstu (unsigned_intSI_type_node,
- function->ident), value);
- fields = TREE_CHAIN (fields);
-
- /* checksum */
- value = tree_cons (fields, build_int_cstu (unsigned_intSI_type_node,
- function->checksum), value);
- fields = TREE_CHAIN (fields);
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ data->ident));
+ fields = DECL_CHAIN (fields);
+
+ /* lineno_checksum */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ data->lineno_checksum));
+ fields = DECL_CHAIN (fields);
+
+ /* cfg_checksum */
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ data->cfg_checksum));
+ fields = DECL_CHAIN (fields);
/* counters */
+ ctr_type = TREE_TYPE (TREE_TYPE (fields));
for (ix = 0; ix != GCOV_COUNTERS; ix++)
if (prg_ctr_mask & (1 << ix))
{
- tree counters = build_int_cstu (unsigned_type_node,
- function->n_ctrs[ix]);
-
- array_value = tree_cons (NULL_TREE, counters, array_value);
+ vec<constructor_elt, va_gc> *ctr = NULL;
+ tree var = data->ctr_vars[ix];
+ unsigned count = 0;
+
+ if (var)
+ count
+ = tree_to_shwi (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))))
+ + 1;
+
+ CONSTRUCTOR_APPEND_ELT (ctr, TYPE_FIELDS (ctr_type),
+ build_int_cstu (get_gcov_unsigned_t (),
+ count));
+
+ if (var)
+ CONSTRUCTOR_APPEND_ELT (ctr, DECL_CHAIN (TYPE_FIELDS (ctr_type)),
+ build_fold_addr_expr (var));
+
+ CONSTRUCTOR_APPEND_ELT (v2, NULL, build_constructor (ctr_type, ctr));
}
+
+ CONSTRUCTOR_APPEND_ELT (v1, fields,
+ build_constructor (TREE_TYPE (fields), v2));
- array_value = build_constructor (TREE_TYPE (fields), nreverse (array_value));
- value = tree_cons (fields, array_value, value);
-
- value = build_constructor (type, nreverse (value));
-
- return value;
+ return build_constructor (type, v1);
}
-/* Creates the gcov_ctr_info RECORD_TYPE. */
+/* 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 tree
-build_ctr_info_type (void)
+static void
+build_info_type (tree type, tree fn_info_ptr_type)
{
- tree type = lang_hooks.types.make_type (RECORD_TYPE);
tree field, fields = NULL_TREE;
- tree gcov_ptr_type = build_pointer_type (GCOV_TYPE_NODE);
- tree gcov_merge_fn_type;
+ tree merge_fn_type;
- /* counters */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
- TREE_CHAIN (field) = fields;
+ /* Version ident */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
fields = field;
- /* values */
- field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
- TREE_CHAIN (field) = fields;
+ /* next pointer */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (build_qualified_type
+ (type, TYPE_QUAL_CONST)));
+ DECL_CHAIN (field) = fields;
fields = field;
- /* merge */
- gcov_merge_fn_type =
- build_function_type_list (void_type_node,
- gcov_ptr_type, unsigned_type_node,
- NULL_TREE);
- field = build_decl (FIELD_DECL, NULL_TREE,
- build_pointer_type (gcov_merge_fn_type));
- TREE_CHAIN (field) = fields;
+ /* stamp */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
fields = field;
- finish_builtin_struct (type, "__gcov_ctr_info", fields, NULL_TREE);
-
- return type;
-}
-
-/* Creates a CONSTRUCTOR for a gcov_ctr_info. COUNTER is
- the counter being processed and TYPE is the gcov_ctr_info
- RECORD_TYPE. */
-
-static tree
-build_ctr_info_value (unsigned int counter, tree type)
-{
- tree value = NULL_TREE;
- tree fields = TYPE_FIELDS (type);
- tree fn;
+ /* Filename */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ build_pointer_type (build_qualified_type
+ (char_type_node, TYPE_QUAL_CONST)));
+ DECL_CHAIN (field) = fields;
+ fields = field;
- /* counters */
- value = tree_cons (fields,
- build_int_cstu (unsigned_intSI_type_node,
- prg_n_ctrs[counter]),
- value);
- fields = TREE_CHAIN (fields);
+ /* merge fn array */
+ merge_fn_type
+ = build_function_type_list (void_type_node,
+ build_pointer_type (get_gcov_type ()),
+ get_gcov_unsigned_t (), NULL_TREE);
+ merge_fn_type
+ = build_array_type (build_pointer_type (merge_fn_type),
+ build_index_type (size_int (GCOV_COUNTERS - 1)));
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ merge_fn_type);
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* n_functions */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* 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_ptr_type);
+ DECL_CHAIN (field) = fields;
+ fields = field;
- if (prg_n_ctrs[counter])
- {
- tree array_type;
-
- array_type = build_int_cstu (unsigned_type_node,
- prg_n_ctrs[counter] - 1);
- array_type = build_index_type (array_type);
- array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)),
- array_type);
-
- TREE_TYPE (tree_ctr_tables[counter]) = array_type;
- DECL_SIZE (tree_ctr_tables[counter]) = TYPE_SIZE (array_type);
- DECL_SIZE_UNIT (tree_ctr_tables[counter]) = TYPE_SIZE_UNIT (array_type);
- assemble_variable (tree_ctr_tables[counter], 0, 0, 0);
-
- value = tree_cons (fields,
- build1 (ADDR_EXPR, TREE_TYPE (fields),
- tree_ctr_tables[counter]),
- value);
- }
- else
- value = tree_cons (fields, null_pointer_node, value);
- fields = TREE_CHAIN (fields);
-
- fn = build_decl (FUNCTION_DECL,
- get_identifier (ctr_merge_functions[counter]),
- TREE_TYPE (TREE_TYPE (fields)));
- DECL_EXTERNAL (fn) = 1;
- TREE_PUBLIC (fn) = 1;
- DECL_ARTIFICIAL (fn) = 1;
- TREE_NOTHROW (fn) = 1;
- value = tree_cons (fields,
- build1 (ADDR_EXPR, TREE_TYPE (fields), fn),
- value);
-
- value = build_constructor (type, nreverse (value));
-
- return value;
+ finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
}
-/* Creates the gcov_info RECORD_TYPE and initializer for it. 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_gcov_info (void)
+build_info (tree info_type, tree fn_ary)
{
- unsigned n_ctr_types, ix;
- tree type, const_type;
- tree fn_info_type, fn_info_value = NULL_TREE;
- tree fn_info_ptr_type;
- tree ctr_info_type, ctr_info_ary_type, ctr_info_value = NULL_TREE;
- tree field, fields = NULL_TREE;
- tree value = NULL_TREE;
+ tree info_fields = TYPE_FIELDS (info_type);
+ tree merge_fn_type, n_funcs;
+ unsigned ix;
tree filename_string;
- char *filename;
- int filename_len;
- unsigned n_fns;
- const struct function_list *fn;
- tree string_type;
-
- /* Count the number of active counters. */
- for (n_ctr_types = 0, ix = 0; ix != GCOV_COUNTERS; ix++)
- if (prg_ctr_mask & (1 << ix))
- n_ctr_types++;
-
- type = lang_hooks.types.make_type (RECORD_TYPE);
- const_type = build_qualified_type (type, TYPE_QUAL_CONST);
+ int da_file_name_len;
+ vec<constructor_elt, va_gc> *v1 = NULL;
+ vec<constructor_elt, va_gc> *v2 = NULL;
/* Version ident */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
- value = tree_cons (field, build_int_cstu (unsigned_intSI_type_node,
- GCOV_VERSION), value);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_int_cstu (TREE_TYPE (info_fields),
+ GCOV_VERSION));
+ info_fields = DECL_CHAIN (info_fields);
/* next -- NULL */
- field = build_decl (FIELD_DECL, NULL_TREE, build_pointer_type (const_type));
- TREE_CHAIN (field) = fields;
- fields = field;
- value = tree_cons (field, null_pointer_node, value);
-
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
+ info_fields = DECL_CHAIN (info_fields);
+
/* stamp */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
- value = tree_cons (field, build_int_cstu (unsigned_intSI_type_node,
- local_tick), value);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_int_cstu (TREE_TYPE (info_fields),
+ bbg_file_stamp));
+ info_fields = DECL_CHAIN (info_fields);
/* Filename */
- string_type = build_pointer_type (build_qualified_type (char_type_node,
- TYPE_QUAL_CONST));
- field = build_decl (FIELD_DECL, NULL_TREE, string_type);
- TREE_CHAIN (field) = fields;
- fields = field;
- filename = getpwd ();
- filename = (filename && da_file_name[0] != '/'
- ? concat (filename, "/", da_file_name, NULL)
- : da_file_name);
- filename_len = strlen (filename);
- filename_string = build_string (filename_len + 1, filename);
- if (filename != da_file_name)
- free (filename);
+ da_file_name_len = strlen (da_file_name);
+ filename_string = build_string (da_file_name_len + 1, da_file_name);
TREE_TYPE (filename_string) = build_array_type
- (char_type_node, build_index_type
- (build_int_cst (NULL_TREE, filename_len)));
- value = tree_cons (field, build1 (ADDR_EXPR, string_type, filename_string),
- value);
-
- /* Build the fn_info type and initializer. */
- fn_info_type = build_fn_info_type (n_ctr_types);
- fn_info_ptr_type = build_pointer_type (build_qualified_type
- (fn_info_type, TYPE_QUAL_CONST));
- for (fn = functions_head, n_fns = 0; fn; fn = fn->next, n_fns++)
- fn_info_value = tree_cons (NULL_TREE,
- build_fn_info_value (fn, fn_info_type),
- fn_info_value);
- if (n_fns)
+ (char_type_node, build_index_type (size_int (da_file_name_len)));
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build1 (ADDR_EXPR, TREE_TYPE (info_fields),
+ filename_string));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* merge fn array -- NULL slots indicate unmeasured counters */
+ merge_fn_type = TREE_TYPE (TREE_TYPE (info_fields));
+ for (ix = 0; ix != GCOV_COUNTERS; ix++)
{
- tree array_type;
-
- array_type = build_index_type (build_int_cst (NULL_TREE, n_fns - 1));
- array_type = build_array_type (fn_info_type, array_type);
+ tree ptr = null_pointer_node;
- fn_info_value = build_constructor (array_type, nreverse (fn_info_value));
- fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value);
+ if ((1u << ix) & prg_ctr_mask)
+ {
+ tree merge_fn = build_decl (BUILTINS_LOCATION,
+ FUNCTION_DECL,
+ get_identifier (ctr_merge_functions[ix]),
+ TREE_TYPE (merge_fn_type));
+ DECL_EXTERNAL (merge_fn) = 1;
+ TREE_PUBLIC (merge_fn) = 1;
+ DECL_ARTIFICIAL (merge_fn) = 1;
+ TREE_NOTHROW (merge_fn) = 1;
+ /* Initialize assembler name so we can stream out. */
+ DECL_ASSEMBLER_NAME (merge_fn);
+ ptr = build1 (ADDR_EXPR, merge_fn_type, merge_fn);
+ }
+ CONSTRUCTOR_APPEND_ELT (v2, NULL, ptr);
}
- else
- fn_info_value = null_pointer_node;
-
- /* number of functions */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
- value = tree_cons (field,
- build_int_cstu (unsigned_type_node, n_fns),
- value);
-
- /* fn_info table */
- field = build_decl (FIELD_DECL, NULL_TREE, fn_info_ptr_type);
- TREE_CHAIN (field) = fields;
- fields = field;
- value = tree_cons (field, fn_info_value, value);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_constructor (TREE_TYPE (info_fields), v2));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* n_functions */
+ 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);
+
+ /* functions */
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ 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);
+}
- /* counter_mask */
- field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
- TREE_CHAIN (field) = fields;
- fields = field;
- value = tree_cons (field,
- build_int_cstu (unsigned_type_node, prg_ctr_mask),
- value);
+/* Generate the constructor function to call __gcov_init. */
- /* counters */
- ctr_info_type = build_ctr_info_type ();
- ctr_info_ary_type = build_index_type (build_int_cst (NULL_TREE,
- n_ctr_types));
- ctr_info_ary_type = build_array_type (ctr_info_type, ctr_info_ary_type);
- for (ix = 0; ix != GCOV_COUNTERS; ix++)
- if (prg_ctr_mask & (1 << ix))
- ctr_info_value = tree_cons (NULL_TREE,
- build_ctr_info_value (ix, ctr_info_type),
- ctr_info_value);
- ctr_info_value = build_constructor (ctr_info_ary_type,
- nreverse (ctr_info_value));
-
- field = build_decl (FIELD_DECL, NULL_TREE, ctr_info_ary_type);
- TREE_CHAIN (field) = fields;
- fields = field;
- value = tree_cons (field, ctr_info_value, value);
+static void
+build_init_ctor (tree gcov_info_type)
+{
+ tree ctor, stmt, init_fn;
- finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
+ /* 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);
- value = build_constructor (type, nreverse (value));
+ /* 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);
- return value;
+ /* Generate a constructor to run it. */
+ cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
}
-/* 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. */
+/* 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 void
-create_coverage (void)
+static bool
+coverage_obj_init (void)
{
- tree gcov_info, gcov_init, body, t;
+ 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;
- t = build_gcov_info ();
+ if (symtab->dump_file)
+ fprintf (symtab->dump_file, "Using data file %s\n", da_file_name);
- gcov_info = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (t));
- TREE_STATIC (gcov_info) = 1;
+ /* Prune functions. */
+ for (fn_prev = &functions_head; (fn = *fn_prev);)
+ if (DECL_STRUCT_FUNCTION (fn->fn_decl))
+ 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_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_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) = t;
+ DECL_NAME (gcov_info_var) = get_identifier (name_buf);
- /* Build structure. */
- assemble_variable (gcov_info, 0, 0, 0);
+ 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 (FUNCTION_DECL, get_identifier ("__gcov_init"), t);
- TREE_PUBLIC (t) = 1;
- DECL_EXTERNAL (t) = 1;
- gcov_init = t;
+ return true;
+}
- /* Generate a call to __gcov_init(&gcov_info). */
- body = NULL;
- t = build_fold_addr_expr (gcov_info);
- t = tree_cons (NULL, t, NULL);
- t = build_function_call_expr (gcov_init, 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);
+ int prefix_len = 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 ();
+
+ if (profile_data_prefix)
+ prefix_len = strlen (profile_data_prefix);
/* Name of da file. */
- da_file_name = xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
- strcpy (da_file_name, filename);
- strcat (da_file_name, GCOV_DATA_SUFFIX);
+ da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
+ + prefix_len + 2);
+
+ if (profile_data_prefix)
+ {
+ memcpy (da_file_name, profile_data_prefix, prefix_len);
+ da_file_name[prefix_len++] = '/';
+ }
+ 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 = xmalloc (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 (!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);
+ }
+ }
- read_counts_file ();
+ 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"