X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcoverage.c;h=e04d22b7a88d98fb6d5431c2c553f9ec0d175030;hb=e2675c1c71dc387908f953a999b102cf85b6c29f;hp=421b3b1a04e530e0e7f114f0ee23c72214d0f383;hpb=ab6a34f2f7b7c7505a1feeeabdf1d87329eb8878;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/coverage.c b/gcc/coverage.c index 421b3b1a04e..e04d22b7a88 100644 --- a/gcc/coverage.c +++ b/gcc/coverage.c @@ -1,6 +1,7 @@ /* Read and write coverage files, and associated functionality. Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, - 2000, 2001, 2003 Free Software Foundation, Inc. + 2000, 2001, 2003, 2004, 2005, 2007, 2008 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. @@ -10,7 +11,7 @@ This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; either version 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 @@ -19,9 +20,8 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GCC; see the file 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 +. */ #define GCOV_LINKAGE @@ -38,19 +38,21 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "expr.h" #include "function.h" #include "toplev.h" +#include "tm_p.h" #include "ggc.h" -#include "target.h" #include "coverage.h" -#include "libfuncs.h" #include "langhooks.h" #include "hashtab.h" +#include "tree-iterator.h" +#include "cgraph.h" +#include "tree-pass.h" #include "gcov-io.c" struct function_list { - struct function_list *next; /* next function */ - const char *name; /* function name */ + struct function_list *next; /* next function */ + unsigned ident; /* function ident */ unsigned checksum; /* function checksum */ unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */ }; @@ -59,9 +61,9 @@ struct function_list typedef struct counts_entry { /* We hash by */ - char *function_name; + unsigned ident; unsigned ctr; - + /* Store */ unsigned checksum; gcov_type *counts; @@ -69,19 +71,21 @@ typedef struct counts_entry /* Workspace */ struct counts_entry *chain; - + } counts_entry_t; static struct function_list *functions_head = 0; static struct function_list **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]; +static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */ /* Counter information for current function. */ -static unsigned fn_ctr_mask; -static unsigned fn_n_ctrs[GCOV_COUNTERS]; +static unsigned fn_ctr_mask; /* Mask of counters used. */ +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; @@ -94,52 +98,69 @@ static char *da_file_name; /* Hash table of count data. */ static htab_t counts_hash = NULL; -/* The names of the counter tables. */ +/* Trees representing the counter table arrays. */ +static GTY(()) tree tree_ctr_tables[GCOV_COUNTERS]; + +/* The names of the counter tables. Not used if we're + generating counters at tree level. */ static GTY(()) rtx ctr_labels[GCOV_COUNTERS]; +/* 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; + /* Forward declarations. */ -static hashval_t htab_counts_entry_hash PARAMS ((const void *)); -static int htab_counts_entry_eq PARAMS ((const void *, const void *)); -static void htab_counts_entry_del PARAMS ((void *)); -static void read_counts_file PARAMS ((void)); -static unsigned compute_checksum PARAMS ((void)); -static unsigned checksum_string PARAMS ((unsigned, const char *)); -static tree build_fn_info_type PARAMS ((unsigned)); -static tree build_fn_info_value PARAMS ((const struct function_list *, tree)); -static tree build_ctr_info_type PARAMS ((void)); -static tree build_ctr_info_value PARAMS ((unsigned, tree)); -static tree build_gcov_info PARAMS ((void)); -static void create_coverage PARAMS ((void)); +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); + +/* Return the type node for gcov_type. */ + +tree +get_gcov_type (void) +{ + return lang_hooks.types.type_for_size (GCOV_TYPE_SIZE, 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); +} static hashval_t -htab_counts_entry_hash (of) - const void *of; +htab_counts_entry_hash (const void *of) { - const counts_entry_t *entry = of; + const counts_entry_t *const entry = (const counts_entry_t *) of; - return htab_hash_string (entry->function_name) ^ entry->ctr; + return entry->ident * GCOV_COUNTERS + entry->ctr; } static int -htab_counts_entry_eq (of1, of2) - const void *of1; - const void *of2; +htab_counts_entry_eq (const void *of1, const void *of2) { - const counts_entry_t *entry1 = of1; - const counts_entry_t *entry2 = of2; + const counts_entry_t *const entry1 = (const counts_entry_t *) of1; + const counts_entry_t *const entry2 = (const counts_entry_t *) of2; - return !strcmp (entry1->function_name, entry2->function_name) - && entry1->ctr == entry2->ctr; + return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr; } static void -htab_counts_entry_del (of) - void *of; +htab_counts_entry_del (void *of) { - counts_entry_t *entry = of; + counts_entry_t *const entry = (counts_entry_t *) of; - free (entry->function_name); free (entry->counts); free (entry); } @@ -147,55 +168,53 @@ htab_counts_entry_del (of) /* Read in the counts file, if available. */ static void -read_counts_file () +read_counts_file (void) { - char *function_name_buffer = NULL; - unsigned version, ix, checksum = -1; + gcov_unsigned_t fn_ident = 0; + gcov_unsigned_t checksum = -1; counts_entry_t *summaried = NULL; unsigned seen_summary = 0; - + gcov_unsigned_t tag; + int is_error = 0; + if (!gcov_open (da_file_name, 1)) return; - - if (gcov_read_unsigned () != GCOV_DATA_MAGIC) + + if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) { - warning ("`%s' is not a gcov data file", da_file_name); + warning (0, "%qs is not a gcov data file", da_file_name); gcov_close (); return; } - else if ((version = gcov_read_unsigned ()) != GCOV_VERSION) + else if ((tag = gcov_read_unsigned ()) != GCOV_VERSION) { char v[4], e[4]; - unsigned required = GCOV_VERSION; - - for (ix = 4; ix--; required >>= 8, version >>= 8) - { - v[ix] = version; - e[ix] = required; - } - warning ("`%s' is version `%.4s', expected version `%.4s'", - da_file_name, v, e); + + GCOV_UNSIGNED2STRING (v, tag); + GCOV_UNSIGNED2STRING (e, GCOV_VERSION); + + 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); - while (!gcov_is_eof ()) + while ((tag = gcov_read_unsigned ())) { - unsigned tag, length; - unsigned long offset; - int error; - - tag = gcov_read_unsigned (); + gcov_unsigned_t length; + gcov_position_t offset; + length = gcov_read_unsigned (); offset = gcov_position (); if (tag == GCOV_TAG_FUNCTION) { - const char *string = gcov_read_string (); - free (function_name_buffer); - function_name_buffer = string ? xstrdup (string) : NULL; + fn_ident = gcov_read_unsigned (); checksum = gcov_read_unsigned (); if (seen_summary) { @@ -203,7 +222,7 @@ read_counts_file () 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; @@ -217,13 +236,13 @@ read_counts_file () { counts_entry_t *entry; struct gcov_summary summary; - + gcov_read_summary (&summary); seen_summary = 1; for (entry = summaried; entry; entry = entry->chain) { struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr]; - + entry->summary.runs += csum->runs; entry->summary.sum_all += csum->sum_all; if (entry->summary.run_max < csum->run_max) @@ -231,13 +250,13 @@ read_counts_file () entry->summary.sum_max += csum->sum_max; } } - else if (GCOV_TAG_IS_COUNTER (tag) && function_name_buffer) + else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident) { counts_entry_t **slot, *entry, elt; - unsigned n_counts = length / 8; + unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); unsigned ix; - elt.function_name = function_name_buffer; + elt.ident = fn_ident; elt.ctr = GCOV_COUNTER_FOR_TAG (tag); slot = (counts_entry_t **) htab_find_slot @@ -245,43 +264,59 @@ read_counts_file () entry = *slot; if (!entry) { - *slot = entry = xcalloc (1, sizeof (counts_entry_t)); - entry->function_name = xstrdup (elt.function_name); + *slot = entry = XCNEW (counts_entry_t); + entry->ident = elt.ident; entry->ctr = elt.ctr; entry->checksum = checksum; entry->summary.num = n_counts; - entry->counts = xcalloc (n_counts, sizeof (gcov_type)); + entry->counts = XCNEWVEC (gcov_type, n_counts); } - else if (entry->checksum != checksum - || entry->summary.num != n_counts) + else if (entry->checksum != checksum) { - warning ("profile mismatch for `%s'", function_name_buffer); + 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); break; } - - /* 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. */ - if (!entry->chain || summaried != entry) + else if (entry->summary.num != n_counts) + { + error ("coverage mismatch for function %u while reading execution counters", + fn_ident); + error ("number of counters is %d instead of %d", entry->summary.num, n_counts); + htab_delete (counts_hash); + break; + } + else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE) + { + error ("cannot merge separate %s counters for function %u", + 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)) { entry->chain = summaried; summaried = entry; } for (ix = 0; ix != n_counts; ix++) entry->counts[ix] += gcov_read_counter (); + skip_merge:; } - gcov_seek (offset, length); - if ((error = gcov_is_error ())) + gcov_sync (offset, length); + if ((is_error = gcov_is_error ())) { - warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted", - da_file_name); + error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted", + da_file_name); htab_delete (counts_hash); break; } } - free (function_name_buffer); gcov_close (); } @@ -292,33 +327,66 @@ get_coverage_counts (unsigned counter, unsigned expected, const struct gcov_ctr_summary **summary) { counts_entry_t *entry, elt; + gcov_unsigned_t checksum = -1; - /* No hash table, no counts. */ + /* No hash table, no counts. */ if (!counts_hash) { static int warned = 0; if (!warned++) - warning ("file %s not found, execution counts assumed to be zero", - da_file_name); + 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); return NULL; } - elt.function_name - = (char *) IDENTIFIER_POINTER - (DECL_ASSEMBLER_NAME (current_function_decl)); + elt.ident = current_function_funcdef_no + 1; elt.ctr = counter; - entry = htab_find (counts_hash, &elt); + entry = (counts_entry_t *) htab_find (counts_hash, &elt); if (!entry) { - warning ("No profile for function '%s' found.", elt.function_name); - return 0; + warning (0, "no coverage for function %qE found", + DECL_ASSEMBLER_NAME (current_function_decl)); + return NULL; } - - if (expected != entry->summary.num - || compute_checksum () != entry->checksum) + + checksum = compute_checksum (); + if (entry->checksum != checksum + || entry->summary.num != expected) { - warning ("profile mismatch for `%s'", elt.function_name); + static int warned = 0; + tree id = DECL_ASSEMBLER_NAME (current_function_decl); + + if (warn_coverage_mismatch) + warning (OPT_Wcoverage_mismatch, "coverage mismatch for function " + "%qE while reading counter %qs", id, ctr_names[counter]); + else + error ("coverage mismatch for function %qE while reading counter %qs", + id, ctr_names[counter]); + + if (!inhibit_warnings) + { + if (entry->checksum != checksum) + inform (input_location, "checksum is %x instead of %x", entry->checksum, checksum); + else + inform (input_location, "number of counters is %d instead of %d", + entry->summary.num, expected); + } + + if (warn_coverage_mismatch + && !inhibit_warnings + && !warned++) + { + inform (input_location, "coverage mismatch ignored due to -Wcoverage-mismatch"); + inform (input_location, flag_guess_branch_prob + ? "execution counts estimated" + : "execution counts assumed to be zero"); + if (!flag_guess_branch_prob) + inform (input_location, "this can result in poorly optimized code"); + } + return NULL; } @@ -328,71 +396,150 @@ get_coverage_counts (unsigned counter, unsigned expected, return entry->counts; } -/* Generate a MEM rtl to access COUNTER NO . */ +/* Allocate NUM counters of type COUNTER. Returns nonzero if the + allocation succeeded. */ -rtx -coverage_counter_ref (unsigned counter, unsigned no) +int +coverage_counter_alloc (unsigned counter, unsigned num) { - enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0); - rtx ref; + if (no_coverage) + return 0; - if (!ctr_labels[counter]) + if (!num) + return 1; + + if (!tree_ctr_tables[counter]) { - /* Generate and save a copy of this so it can be shared. */ + /* Generate and save a copy of this so it can be shared. Leave + the index type unspecified for now; it will be set after all + functions have been compiled. */ char buf[20]; - + tree gcov_type_node = get_gcov_type (); + tree gcov_type_array_type + = build_array_type (gcov_type_node, NULL_TREE); + tree_ctr_tables[counter] + = build_decl (BUILTINS_LOCATION, + VAR_DECL, NULL_TREE, gcov_type_array_type); + TREE_STATIC (tree_ctr_tables[counter]) = 1; ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1); - ctr_labels[counter] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); - } - if (no + 1 > fn_n_ctrs[counter]) - { - fn_n_ctrs[counter] = no + 1; - fn_ctr_mask |= 1 << counter; + DECL_NAME (tree_ctr_tables[counter]) = get_identifier (buf); + DECL_ALIGN (tree_ctr_tables[counter]) = TYPE_ALIGN (gcov_type_node); + + if (dump_file) + fprintf (dump_file, "Using data file %s\n", da_file_name); } + fn_b_ctrs[counter] = fn_n_ctrs[counter]; + fn_n_ctrs[counter] += num; + fn_ctr_mask |= 1 << counter; + return 1; +} + +/* Generate a tree to access COUNTER NO. */ + +tree +tree_coverage_counter_ref (unsigned counter, unsigned no) +{ + 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" here is an array index, scaled to bytes later. */ + return build4 (ARRAY_REF, gcov_type_node, tree_ctr_tables[counter], + build_int_cst (NULL_TREE, no), NULL, NULL); +} + +/* Generate a tree to access the address of COUNTER NO. */ + +tree +tree_coverage_counter_addr (unsigned counter, unsigned no) +{ + tree gcov_type_node = get_gcov_type (); - no += prg_n_ctrs[counter]; - ref = plus_constant (ctr_labels[counter], - GCOV_TYPE_SIZE / BITS_PER_UNIT * no); - ref = gen_rtx_MEM (mode, ref); - set_mem_alias_set (ref, new_alias_set ()); + gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]); + no += prg_n_ctrs[counter] + fn_b_ctrs[counter]; - return ref; + TREE_ADDRESSABLE (tree_ctr_tables[counter]) = 1; + + /* "no" here is an array index, scaled to bytes later. */ + return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node, + tree_ctr_tables[counter], + build_int_cst (NULL_TREE, no), + NULL, NULL)); } /* Generate a checksum for a string. CHKSUM is the current - checksum. */ + checksum. */ static unsigned -checksum_string (unsigned chksum, const char *string) +coverage_checksum_string (unsigned chksum, const char *string) { - do + int i; + char *dup = NULL; + + /* Look for everything that looks if it were produced by + 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++) { - unsigned value = *string << 24; - unsigned ix; - - for (ix = 8; ix--; value <<= 1) + int offset = 0; + if (!strncmp (string + i, "_GLOBAL__N_", 11)) + offset = 11; + if (!strncmp (string + i, "_GLOBAL__", 9)) + offset = 9; + + /* C++ namespaces do have scheme: + _GLOBAL__N___functionname + since filename might contain extra underscores there seems + to be no better chance then walk all possible offsets looking + for magicnumber. */ + if (offset) { - unsigned feedback; - - feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0; - chksum <<= 1; - chksum ^= feedback; + 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; } } - while (*string++); - + + chksum = crc32_string (chksum, string); + if (dup) + free (dup); + return chksum; } /* Compute checksum for the current function. We generate a CRC32. */ static unsigned -compute_checksum () +compute_checksum (void) { - unsigned chksum = DECL_SOURCE_LINE (current_function_decl); + expanded_location xloc + = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); + unsigned chksum = xloc.line; - chksum = checksum_string (chksum, DECL_SOURCE_FILE (current_function_decl)); - chksum = checksum_string + chksum = coverage_checksum_string (chksum, xloc.file); + chksum = coverage_checksum_string (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl))); return chksum; @@ -400,37 +547,44 @@ compute_checksum () /* 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 non-zero if data + function header, if not already done. Returns nonzero if data should be output. */ int -coverage_begin_output () +coverage_begin_output (void) { + /* 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) + return 0; + if (!bbg_function_announced) { - const char *file = DECL_SOURCE_FILE (current_function_decl); - unsigned line = DECL_SOURCE_LINE (current_function_decl); + 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_GRAPH_MAGIC); + gcov_write_unsigned (GCOV_NOTE_MAGIC); gcov_write_unsigned (GCOV_VERSION); + gcov_write_unsigned (local_tick); } bbg_file_opened = 1; } - + /* 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_unsigned (compute_checksum ()); - gcov_write_string (file); - gcov_write_unsigned (line); + gcov_write_string (xloc.file); + gcov_write_unsigned (xloc.line); gcov_write_length (offset); bbg_function_announced = 1; @@ -442,35 +596,33 @@ coverage_begin_output () error has occurred. Save function coverage counts. */ void -coverage_end_function () +coverage_end_function (void) { unsigned i; - + if (bbg_file_opened > 1 && gcov_is_error ()) - { - warning ("error writing `%s'", bbg_file_name); + { + warning (0, "error writing %qs", bbg_file_name); bbg_file_opened = -1; } if (fn_ctr_mask) { struct function_list *item; - - /* ??? Probably should re-use the existing struct function. */ - item = xmalloc (sizeof (struct function_list)); - + + item = XNEW (struct function_list); + *functions_tail = item; functions_tail = &item->next; - + item->next = 0; - item->name = xstrdup (IDENTIFIER_POINTER - (DECL_ASSEMBLER_NAME (current_function_decl))); + 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] = 0; + fn_n_ctrs[i] = fn_b_ctrs[i] = 0; } prg_ctr_mask |= fn_ctr_mask; fn_ctr_mask = 0; @@ -481,29 +633,29 @@ coverage_end_function () /* Creates the gcov_fn_info RECORD_TYPE. */ static tree -build_fn_info_type (counters) - unsigned counters; +build_fn_info_type (unsigned int counters) { - tree type = (*lang_hooks.types.make_type) (RECORD_TYPE); + tree type = lang_hooks.types.make_type (RECORD_TYPE); tree field, fields; - tree string_type = - build_pointer_type (build_qualified_type (char_type_node, - TYPE_QUAL_CONST)); tree array_type; - - /* name */ - fields = build_decl (FIELD_DECL, NULL_TREE, string_type); + + /* ident */ + fields = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); /* checksum */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); TREE_CHAIN (field) = fields; fields = field; - array_type = build_index_type (build_int_2 (counters - 1, 0)); - array_type = build_array_type (unsigned_type_node, array_type); - + array_type = build_int_cst (NULL_TREE, counters - 1); + array_type = build_index_type (array_type); + array_type = build_array_type (get_gcov_unsigned_t (), array_type); + /* counters */ - field = build_decl (FIELD_DECL, NULL_TREE, array_type); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, array_type); TREE_CHAIN (field) = fields; fields = field; @@ -517,70 +669,74 @@ build_fn_info_type (counters) RECORD_TYPE. */ static tree -build_fn_info_value (function, type) - const struct function_list *function; - tree type; +build_fn_info_value (const struct function_list *function, tree type) { tree value = NULL_TREE; tree fields = TYPE_FIELDS (type); - size_t name_len = strlen (function->name); - tree fname = build_string (name_len + 1, function->name); - tree string_type = - build_pointer_type (build_qualified_type (char_type_node, - TYPE_QUAL_CONST)); unsigned ix; tree array_value = NULL_TREE; - - /* name */ - TREE_TYPE (fname) = - build_array_type (char_type_node, - build_index_type (build_int_2 (name_len, 0))); - value = tree_cons (fields, - build1 (ADDR_EXPR, string_type, fname), - value); + + /* ident */ + value = tree_cons (fields, build_int_cstu (get_gcov_unsigned_t (), + function->ident), value); fields = TREE_CHAIN (fields); - + /* checksum */ - value = tree_cons (fields, - convert (unsigned_type_node, - build_int_2 (function->checksum, 0)), - value); + value = tree_cons (fields, build_int_cstu (get_gcov_unsigned_t (), + function->checksum), value); fields = TREE_CHAIN (fields); - + /* counters */ for (ix = 0; ix != GCOV_COUNTERS; ix++) if (prg_ctr_mask & (1 << ix)) { - tree counters = convert (unsigned_type_node, - build_int_2 (function->n_ctrs[ix], 0)); - + tree counters = build_int_cstu (get_gcov_unsigned_t (), + function->n_ctrs[ix]); + array_value = tree_cons (NULL_TREE, counters, array_value); } - - array_value = build_constructor (TREE_TYPE (fields), nreverse (array_value)); + + /* FIXME: use build_constructor directly. */ + array_value = build_constructor_from_list (TREE_TYPE (fields), + nreverse (array_value)); value = tree_cons (fields, array_value, value); - value = build_constructor (type, nreverse (value)); - + /* FIXME: use build_constructor directly. */ + value = build_constructor_from_list (type, nreverse (value)); + return value; } /* Creates the gcov_ctr_info RECORD_TYPE. */ static tree -build_ctr_info_type () +build_ctr_info_type (void) { - tree type = (*lang_hooks.types.make_type) (RECORD_TYPE); + tree type = lang_hooks.types.make_type (RECORD_TYPE); tree field, fields = NULL_TREE; - + tree gcov_ptr_type = build_pointer_type (get_gcov_type ()); + tree gcov_merge_fn_type; + /* counters */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); TREE_CHAIN (field) = fields; fields = field; /* values */ - field = build_decl (FIELD_DECL, NULL_TREE, - build_pointer_type (make_signed_type (GCOV_TYPE_SIZE))); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, gcov_ptr_type); + TREE_CHAIN (field) = fields; + fields = field; + + /* merge */ + gcov_merge_fn_type = + build_function_type_list (void_type_node, + gcov_ptr_type, get_gcov_unsigned_t (), + NULL_TREE); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, + build_pointer_type (gcov_merge_fn_type)); TREE_CHAIN (field) = fields; fields = field; @@ -594,42 +750,59 @@ build_ctr_info_type () RECORD_TYPE. */ static tree -build_ctr_info_value (counter, type) - unsigned counter; - tree type; +build_ctr_info_value (unsigned int counter, tree type) { tree value = NULL_TREE; tree fields = TYPE_FIELDS (type); + tree fn; /* counters */ value = tree_cons (fields, - convert (unsigned_type_node, - build_int_2 (prg_n_ctrs[counter], 0)), + build_int_cstu (get_gcov_unsigned_t (), + prg_n_ctrs[counter]), value); fields = TREE_CHAIN (fields); if (prg_n_ctrs[counter]) { - tree array_type, array; - - array_type = build_index_type (build_int_2 (prg_n_ctrs[counter] - 1, 0)); + tree array_type; + + array_type = build_int_cstu (get_gcov_unsigned_t (), + prg_n_ctrs[counter] - 1); + array_type = build_index_type (array_type); array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)), array_type); - - array = build (VAR_DECL, array_type, NULL_TREE, NULL_TREE); - TREE_STATIC (array) = 1; - DECL_NAME (array) = get_identifier (XSTR (ctr_labels[counter], 0)); - assemble_variable (array, 0, 0, 0); - + + 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); + varpool_finalize_decl (tree_ctr_tables[counter]); + value = tree_cons (fields, - build1 (ADDR_EXPR, TREE_TYPE (fields), array), + 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 (BUILTINS_LOCATION, + 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; + DECL_ASSEMBLER_NAME (fn); /* Initialize assembler name so we can stream out. */ + value = tree_cons (fields, + build1 (ADDR_EXPR, TREE_TYPE (fields), fn), + value); + + /* FIXME: use build_constructor directly. */ + value = build_constructor_from_list (type, nreverse (value)); - value = build_constructor (type, nreverse (value)); - return value; } @@ -637,7 +810,7 @@ build_ctr_info_value (counter, type) CONSTRUCTOR. */ static tree -build_gcov_info () +build_gcov_info (void) { unsigned n_ctr_types, ix; tree type, const_type; @@ -647,54 +820,57 @@ build_gcov_info () tree field, fields = NULL_TREE; tree value = NULL_TREE; tree filename_string; - char *filename; - int filename_len; + int da_file_name_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); + + type = lang_hooks.types.make_type (RECORD_TYPE); const_type = build_qualified_type (type, TYPE_QUAL_CONST); - + /* Version ident */ - field = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); TREE_CHAIN (field) = fields; fields = field; - value = tree_cons (field, convert (long_unsigned_type_node, - build_int_2 (GCOV_VERSION, 0)), + value = tree_cons (field, build_int_cstu (TREE_TYPE (field), GCOV_VERSION), value); - + /* next -- NULL */ - field = build_decl (FIELD_DECL, NULL_TREE, build_pointer_type (const_type)); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, build_pointer_type (const_type)); TREE_CHAIN (field) = fields; fields = field; value = tree_cons (field, null_pointer_node, value); - + + /* stamp */ + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); + TREE_CHAIN (field) = fields; + fields = field; + value = tree_cons (field, build_int_cstu (TREE_TYPE (field), local_tick), + value); + /* Filename */ string_type = build_pointer_type (build_qualified_type (char_type_node, TYPE_QUAL_CONST)); - field = build_decl (FIELD_DECL, NULL_TREE, string_type); + field = build_decl (BUILTINS_LOCATION, + 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); - TREE_TYPE (filename_string) = - build_array_type (char_type_node, - build_index_type (build_int_2 (filename_len, 0))); + 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, da_file_name_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 @@ -707,59 +883,67 @@ build_gcov_info () { tree array_type; - array_type = build_index_type (build_int_2 (n_fns - 1, 0)); + array_type = build_index_type (build_int_cst (NULL_TREE, n_fns - 1)); array_type = build_array_type (fn_info_type, array_type); - - fn_info_value = build_constructor (array_type, nreverse (fn_info_value)); + + /* FIXME: use build_constructor directly. */ + fn_info_value = build_constructor_from_list (array_type, + nreverse (fn_info_value)); fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value); } else fn_info_value = null_pointer_node; - + /* number of functions */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); TREE_CHAIN (field) = fields; fields = field; value = tree_cons (field, - convert (unsigned_type_node, build_int_2 (n_fns, 0)), + build_int_cstu (get_gcov_unsigned_t (), n_fns), value); - + /* fn_info table */ - field = build_decl (FIELD_DECL, NULL_TREE, fn_info_ptr_type); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, fn_info_ptr_type); TREE_CHAIN (field) = fields; fields = field; value = tree_cons (field, fn_info_value, value); /* counter_mask */ - field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); TREE_CHAIN (field) = fields; fields = field; value = tree_cons (field, - convert (unsigned_type_node, - build_int_2 (prg_ctr_mask, 0)), + build_int_cstu (get_gcov_unsigned_t (), prg_ctr_mask), value); - + /* counters */ ctr_info_type = build_ctr_info_type (); - ctr_info_ary_type = build_index_type (build_int_2 (n_ctr_types, 0)); + 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)); + /* FIXME: use build_constructor directly. */ + ctr_info_value = build_constructor_from_list (ctr_info_ary_type, + nreverse (ctr_info_value)); - field = build_decl (FIELD_DECL, NULL_TREE, ctr_info_ary_type); + field = build_decl (BUILTINS_LOCATION, + FIELD_DECL, NULL_TREE, ctr_info_ary_type); TREE_CHAIN (field) = fields; fields = field; value = tree_cons (field, ctr_info_value, value); - + finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE); - value = build_constructor (type, nreverse (value)); - + /* FIXME: use build_constructor directly. */ + value = build_constructor_from_list (type, nreverse (value)); + return value; } @@ -768,124 +952,103 @@ build_gcov_info () gcov-io.h. Write out the constructor to call __gcov_init. */ static void -create_coverage () +create_coverage (void) { - tree gcov_info, gcov_info_value; - char name[20]; - char *ctor_name; - tree ctor; - rtx gcov_info_address; - int save_flag_inline_functions = flag_inline_functions; + tree gcov_info, gcov_init, body, t; + char name_buf[32]; + + no_coverage = 1; /* Disable any further coverage. */ if (!prg_ctr_mask) return; - - gcov_info_value = build_gcov_info (); - gcov_info = build (VAR_DECL, TREE_TYPE (gcov_info_value), - NULL_TREE, NULL_TREE); - DECL_INITIAL (gcov_info) = gcov_info_value; + t = build_gcov_info (); + gcov_info = build_decl (BUILTINS_LOCATION, + VAR_DECL, NULL_TREE, TREE_TYPE (t)); TREE_STATIC (gcov_info) = 1; - ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0); - DECL_NAME (gcov_info) = get_identifier (name); - + ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0); + DECL_NAME (gcov_info) = get_identifier (name_buf); + DECL_INITIAL (gcov_info) = t; + /* Build structure. */ - assemble_variable (gcov_info, 0, 0, 0); - - /* Build the constructor function to invoke __gcov_init. */ - ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')), - "_GCOV", NULL); - ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name), - build_function_type (void_type_node, NULL_TREE)); - free (ctor_name); - DECL_EXTERNAL (ctor) = 0; - - /* It can be a static function as long as collect2 does not have - to scan the object file to find its ctor/dtor routine. */ - TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors; - TREE_USED (ctor) = 1; - DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node); - - ctor = (*lang_hooks.decls.pushdecl) (ctor); - rest_of_decl_compilation (ctor, 0, 1, 0); - announce_function (ctor); - current_function_decl = ctor; - DECL_INITIAL (ctor) = error_mark_node; - make_decl_rtl (ctor, NULL); - init_function_start (ctor, input_filename, lineno); - (*lang_hooks.decls.pushlevel) (0); - expand_function_start (ctor, 0); - cfun->arc_profile = 0; - - /* Actually generate the code to call __gcov_init. */ - gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0)); - emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1, - gcov_info_address, Pmode); - - expand_function_end (input_filename, lineno, 0); - (*lang_hooks.decls.poplevel) (1, 0, 1); - - /* Since ctor isn't in the list of globals, it would never be emitted - when it's considered to be 'safe' for inlining, so turn off - flag_inline_functions. */ - flag_inline_functions = 0; - - rest_of_compilation (ctor); - - /* Reset flag_inline_functions to its original value. */ - flag_inline_functions = save_flag_inline_functions; - - if (! quiet_flag) - fflush (asm_out_file); - current_function_decl = NULL_TREE; - - if (targetm.have_ctors_dtors) - (* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0), - DEFAULT_INIT_PRIORITY); + varpool_finalize_decl (gcov_info); + + /* 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; + + /* 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 a constructor to run it. */ + cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY); } /* Perform file-level initialization. Read in data file, generate name - of graph file. */ + of graph file. */ void -coverage_init (filename) - const char *filename; +coverage_init (const char *filename) { int len = strlen (filename); + /* + 1 for extra '/', in case prefix doesn't end with /. */ + int prefix_len; + + if (profile_data_prefix == 0 && filename[0] != '/') + profile_data_prefix = getpwd (); - da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1); - strcpy (da_file_name, filename); + prefix_len = (profile_data_prefix) ? strlen (profile_data_prefix) + 1 : 0; + + /* Name of da file. */ + da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX) + + prefix_len + 1); + + if (profile_data_prefix) + { + strcpy (da_file_name, profile_data_prefix); + da_file_name[prefix_len - 1] = '/'; + da_file_name[prefix_len] = 0; + } + else + da_file_name[0] = 0; + strcat (da_file_name, filename); strcat (da_file_name, GCOV_DATA_SUFFIX); - - read_counts_file (); - /* Open the bbg output file. */ - bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1); + /* 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_GRAPH_SUFFIX); + strcat (bbg_file_name, GCOV_NOTE_SUFFIX); + + if (flag_profile_use) + read_counts_file (); } /* Performs file-level cleanup. Close graph file, generate coverage variables and constructor. */ void -coverage_finish () +coverage_finish (void) { create_coverage (); if (bbg_file_opened) { int error = gcov_close (); - + if (error) unlink (bbg_file_name); -#if SELF_COVERAGE - /* If the compiler is instrumented, we should not - unconditionally remove the counts file, because we might be - recompiling ourselves. The .da files are all removed during - copying the stage1 files. */ - if (error) -#endif + 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); } }