aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/gcc/coverage.c
diff options
context:
space:
mode:
authorRong Xu <xur@google.com>2014-07-21 16:47:22 -0700
committerRong Xu <xur@google.com>2014-07-29 15:31:03 -0700
commit38a8aecfb882072900434499696b5c32a2274515 (patch)
tree2aac97f0ae24b03cd98c1a06e989c031c173f889 /gcc-4.9/gcc/coverage.c
parentc231900e5dcc14d8296bd9f62b45997a49d4d5e7 (diff)
downloadtoolchain_gcc-38a8aecfb882072900434499696b5c32a2274515.tar.gz
toolchain_gcc-38a8aecfb882072900434499696b5c32a2274515.tar.bz2
toolchain_gcc-38a8aecfb882072900434499696b5c32a2274515.zip
[4.9] Switch gcc-4.9 to use google/gcc-4_9 branch.
This source drop uses svn version r212828 of google/gcc-4.9 branch. We also cherry-picked r213062, r213063 and r213064 to fix windows build issues. All gcc-4.9 patches before July 3rd are ported to google/gcc-4.9. The following prior commits has not been merged to google branch yet. (They are included in this commit). e7af147f979e657fe2df00808e5b4319b0e088c6, baf87df3cb2683649ba7e9872362a7e721117c23, and c231900e5dcc14d8296bd9f62b45997a49d4d5e7. Change-Id: I4bea3ea470387ff751c2be4cb0d4a12059b9299b
Diffstat (limited to 'gcc-4.9/gcc/coverage.c')
-rw-r--r--gcc-4.9/gcc/coverage.c1899
1 files changed, 1826 insertions, 73 deletions
diff --git a/gcc-4.9/gcc/coverage.c b/gcc-4.9/gcc/coverage.c
index 4c06fa479..a7f183d0c 100644
--- a/gcc-4.9/gcc/coverage.c
+++ b/gcc-4.9/gcc/coverage.c
@@ -50,13 +50,32 @@ along with GCC; see the file COPYING3. If not see
#include "tree-pass.h"
#include "cgraph.h"
#include "dumpfile.h"
+#include "opts.h"
+#include "gcov-io.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "gimple-ssa.h"
+#include "cpplib.h"
+#include "incpath.h"
#include "diagnostic-core.h"
#include "intl.h"
+#include "l-ipo.h"
#include "filenames.h"
+#include "dwarf2asm.h"
#include "target.h"
#include "gcov-io.h"
#include "gcov-io.c"
+#include "params.h"
+#include "dbgcnt.h"
+#include "input.h"
+#include "pointer-set.h"
+#include "auto-profile.h"
struct GTY((chain_next ("%h.next"))) coverage_data
{
@@ -68,11 +87,20 @@ struct GTY((chain_next ("%h.next"))) coverage_data
tree ctr_vars[GCOV_COUNTERS]; /* counter variables. */
};
+static bool profiling_enabled_p (void);
+
+/* Linked list of -D/-U/-imacro/-include strings for a source module. */
+struct str_list
+{
+ char *str;
+ struct str_list *next;
+};
+
/* Counts information for a function. */
typedef struct counts_entry
{
/* We hash by */
- unsigned ident;
+ unsigned HOST_WIDEST_INT ident;
unsigned ctr;
/* Store */
@@ -118,13 +146,42 @@ static unsigned bbg_file_stamp;
/* Name of the count data (gcda) file. */
static char *da_file_name;
+static char *da_base_file_name;
+static char *main_input_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
+
+/* True during the period that counts_hash is being rebuilt. */
+static bool rebuilding_counts_hash = false;
+
+struct gcov_module_info **module_infos = NULL;
+
+/* List of -D/-U options. */
+static struct str_list *cpp_defines_head = NULL, *cpp_defines_tail = NULL;
+static unsigned num_cpp_defines = 0;
+
+/* List of -imcaro/-include options. */
+static struct str_list *cpp_includes_head = NULL, *cpp_includes_tail = NULL;
+static unsigned num_cpp_includes = 0;
+
+/* True if the current module has any asm statements. */
+static bool has_asm_statement;
/* Forward declarations. */
-static void read_counts_file (void);
+static void read_counts_file (const char *, unsigned);
static tree build_var (tree, tree, int);
static void build_fn_info_type (tree, unsigned, tree);
static void build_info_type (tree, tree);
@@ -134,6 +191,8 @@ 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> *);
+static char * get_da_file_name (const char *);
+static tree build_gcov_module_info_type (void);
/* Return the type node for gcov_type. */
@@ -146,13 +205,22 @@ get_gcov_type (void)
/* Return the type node for gcov_unsigned_t. */
-static tree
+tree
get_gcov_unsigned_t (void)
{
enum machine_mode mode = smallest_mode_for_size (32, MODE_INT);
return lang_hooks.types.type_for_mode (mode, true);
}
+/* Return the type node for const char *. */
+
+tree
+get_const_string_type (void)
+{
+ return build_pointer_type
+ (build_qualified_type (char_type_node, TYPE_QUAL_CONST));
+}
+
inline hashval_t
counts_entry::hash (const value_type *entry)
{
@@ -169,28 +237,491 @@ counts_entry::equal (const value_type *entry1,
inline void
counts_entry::remove (value_type *entry)
{
- free (entry->counts);
- free (entry);
+ /* When rebuilding counts_hash, we will reuse the entry. */
+ if (!rebuilding_counts_hash)
+ {
+ free (entry->counts);
+ free (entry);
+ }
}
/* Hash table of count data. */
static hash_table <counts_entry> counts_hash;
-/* Read in the counts file, if available. */
+/* Returns true if MOD_ID is the id of the last source module. */
+int
+is_last_module (unsigned mod_id)
+{
+ return (mod_id == module_infos[num_in_fnames - 1]->ident);
+}
+
+/* String hash function */
+
+struct string_hasher
+{
+ /* hash_table support. */
+ typedef char value_type;
+ typedef char compare_type;
+ static inline hashval_t hash (const value_type *);
+ static int equal (const value_type *, const compare_type *);
+ static void remove (value_type *) {};
+};
+
+hashval_t
+string_hasher::hash (const char* s)
+{
+ return htab_hash_string (s);
+}
+
+/* String equal function */
+
+int
+string_hasher::equal (const char *s1, const char *s2)
+{
+ return !strcmp (s1, s2);
+}
+
+/* Command line option descriptor. */
+
+struct opt_desc
+{
+ const char *opt_str;
+ const char *opt_neg_str;
+ bool default_val; /* TODO better handling of default */
+};
+
+static struct opt_desc force_matching_cg_opts[] =
+ {
+ { "-fexceptions", "-fno-exceptions", true },
+ { "-fsized-delete", "-fno-sized-delete", false },
+ { "-frtti", "-fno-rtti", true },
+ { "-fstrict-aliasing", "-fno-strict-aliasing", true },
+ { "-fsigned-char", "-funsigned-char", true },
+ /* { "-fsigned-char", "-fno-signed-char", true },
+ { "-funsigned-char", "-fno-unsigned-char", false }, */
+ { "-ansi", "", false },
+ { NULL, NULL, false }
+ };
+
+/* A helper function to check if OPTION_STRING is one of the codegen
+ options specified in FORCE_MATCHING_CG_ARGS. If yes, set the
+ corresponding entry in CG_ARG_VAL to the value of the option specified
+ in OPTION_STRING. */
+
+static void
+check_cg_opts (bool *cg_opt_val, const char *option_str)
+{
+ unsigned int i;
+ for (i = 0; force_matching_cg_opts[i].opt_str; i++)
+ {
+ if (!strcmp (force_matching_cg_opts[i].opt_str, option_str))
+ cg_opt_val[i] = true;
+ else if (!strcmp (force_matching_cg_opts[i].opt_neg_str, option_str))
+ cg_opt_val[i] = false;
+ }
+}
+
+/* A helper function to check if CG_OPTS1 and CG_OPTS are identical. It returns
+ true if yes, false otherwise. */
+
+static bool
+has_incompatible_cg_opts (bool *cg_opts1, bool *cg_opts2, unsigned num_cg_opts)
+{
+ unsigned i;
+
+ for (i = 0; i < num_cg_opts; i++)
+ {
+ if (cg_opts1[i] != cg_opts2[i])
+ return true;
+ }
+
+ return false;
+}
+
+/* Returns true if the command-line arguments stored in the given module-infos
+ are incompatible. */
+bool
+incompatible_cl_args (struct gcov_module_info* mod_info1,
+ struct gcov_module_info* mod_info2)
+{
+ char **warning_opts1 = XNEWVEC (char *, mod_info1->num_cl_args);
+ char **warning_opts2 = XNEWVEC (char *, mod_info2->num_cl_args);
+ char **non_warning_opts1 = XNEWVEC (char *, mod_info1->num_cl_args);
+ char **non_warning_opts2 = XNEWVEC (char *, mod_info2->num_cl_args);
+ char *std_opts1 = NULL, *std_opts2 = NULL;
+ unsigned arch_isa1 = 0, arch_isa2 = 0;
+ unsigned int i, num_warning_opts1 = 0, num_warning_opts2 = 0;
+ unsigned int num_non_warning_opts1 = 0, num_non_warning_opts2 = 0;
+ bool warning_mismatch = false;
+ bool non_warning_mismatch = false;
+ hash_table <string_hasher> option_tab1, option_tab2;
+ unsigned int start_index1 = mod_info1->num_quote_paths
+ + mod_info1->num_bracket_paths + mod_info1->num_system_paths
+ + mod_info1->num_cpp_defines + mod_info1->num_cpp_includes;
+ unsigned int start_index2 = mod_info2->num_quote_paths
+ + mod_info2->num_bracket_paths + mod_info2->num_system_paths
+ + mod_info2->num_cpp_defines + mod_info2->num_cpp_includes;
+
+ bool *cg_opts1, *cg_opts2, has_any_incompatible_cg_opts, has_incompatible_std;
+ bool has_incompatible_arch_isa;
+ unsigned int num_cg_opts = 0;
+
+ for (i = 0; force_matching_cg_opts[i].opt_str; i++)
+ num_cg_opts++;
+
+ cg_opts1 = XCNEWVEC (bool, num_cg_opts);
+ cg_opts2 = XCNEWVEC (bool, num_cg_opts);
+
+ /* Initialize the array to default value */
+ for (i = 0; force_matching_cg_opts[i].opt_str; i++)
+ {
+ cg_opts1[i] = force_matching_cg_opts[i].default_val;
+ cg_opts2[i] = force_matching_cg_opts[i].default_val;
+ }
+
+ option_tab1.create (10);
+ option_tab2.create (10);
+
+ /* First, separate the warning and non-warning options. */
+ for (i = 0; i < mod_info1->num_cl_args; i++)
+ if (mod_info1->string_array[start_index1 + i][1] == 'W')
+ warning_opts1[num_warning_opts1++] =
+ mod_info1->string_array[start_index1 + i];
+ else
+ {
+ char **slot;
+ char *option_string = mod_info1->string_array[start_index1 + i];
+
+ check_cg_opts (cg_opts1, option_string);
+ if (strstr (option_string, "-std="))
+ std_opts1 = option_string;
+
+ if (!strncmp (option_string, "-m",2))
+ arch_isa1 = crc32_string (arch_isa1, option_string);
+
+ slot = option_tab1.find_slot (option_string, INSERT);
+ if (!*slot)
+ {
+ *slot = option_string;
+ non_warning_opts1[num_non_warning_opts1++] = option_string;
+ }
+ }
+
+ for (i = 0; i < mod_info2->num_cl_args; i++)
+ if (mod_info2->string_array[start_index2 + i][1] == 'W')
+ warning_opts2[num_warning_opts2++] =
+ mod_info2->string_array[start_index2 + i];
+ else
+ {
+ char **slot;
+ char *option_string = mod_info2->string_array[start_index2 + i];
+
+ check_cg_opts (cg_opts2, option_string);
+ if (strstr (option_string, "-std="))
+ std_opts2 = option_string;
+
+ if (!strncmp (option_string, "-m",2))
+ arch_isa2 = crc32_string (arch_isa2, option_string);
+
+ slot = option_tab2.find_slot (option_string, INSERT);
+ if (!*slot)
+ {
+ *slot = option_string;
+ non_warning_opts2[num_non_warning_opts2++] = option_string;
+ }
+ }
+
+ has_incompatible_std =
+ std_opts1 != std_opts2 && (std_opts1 == NULL || std_opts2 == NULL
+ || strcmp (std_opts1, std_opts2));
+
+ has_incompatible_arch_isa = (arch_isa1 != arch_isa2);
+ /* Compare warning options. If these mismatch, we emit a warning. */
+ if (num_warning_opts1 != num_warning_opts2)
+ warning_mismatch = true;
+ else
+ for (i = 0; i < num_warning_opts1 && !warning_mismatch; i++)
+ warning_mismatch = strcmp (warning_opts1[i], warning_opts2[i]) != 0;
+
+ /* Compare non-warning options. If these mismatch, we emit a warning, and if
+ -fripa-disallow-opt-mismatch is supplied, the two modules are also
+ incompatible. */
+ if (num_non_warning_opts1 != num_non_warning_opts2)
+ non_warning_mismatch = true;
+ else
+ for (i = 0; i < num_non_warning_opts1 && !non_warning_mismatch; i++)
+ non_warning_mismatch =
+ strcmp (non_warning_opts1[i], non_warning_opts2[i]) != 0;
+
+ if (warn_ripa_opt_mismatch && (warning_mismatch || non_warning_mismatch))
+ warning (OPT_Wripa_opt_mismatch, "command line arguments mismatch for %s "
+ "and %s", mod_info1->source_filename, mod_info2->source_filename);
+
+ if (warn_ripa_opt_mismatch && non_warning_mismatch && dump_enabled_p ())
+ {
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
+ "Options for %s", mod_info1->source_filename);
+ for (i = 0; i < num_non_warning_opts1; i++)
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
+ non_warning_opts1[i]);
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
+ "Options for %s", mod_info2->source_filename);
+ for (i = 0; i < num_non_warning_opts2; i++)
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, UNKNOWN_LOCATION,
+ non_warning_opts2[i]);
+ }
+
+ has_any_incompatible_cg_opts
+ = has_incompatible_cg_opts (cg_opts1, cg_opts2, num_cg_opts);
+
+ XDELETEVEC (warning_opts1);
+ XDELETEVEC (warning_opts2);
+ XDELETEVEC (non_warning_opts1);
+ XDELETEVEC (non_warning_opts2);
+ XDELETEVEC (cg_opts1);
+ XDELETEVEC (cg_opts2);
+ option_tab1.dispose ();
+ option_tab2.dispose ();
+ return ((flag_ripa_disallow_opt_mismatch && non_warning_mismatch)
+ || has_any_incompatible_cg_opts || has_incompatible_std
+ || has_incompatible_arch_isa);
+}
+
+
+/* Support for module sorting based on user specfication. */
+struct module_name_entry
+{
+ typedef module_name_entry value_type;
+ typedef module_name_entry compare_type;
+ static inline hashval_t hash (const value_type *entry);
+ static inline int equal (const value_type *entry1, const compare_type *entry2);
+ static inline void remove (value_type *v);
+
+ const char *source_name;
+ int order;
+};
+
+/* Hash function for module name */
+
+hashval_t
+module_name_entry::hash (const value_type *s)
+{
+ return htab_hash_string (s->source_name);
+}
+
+/* Delete function for module name */
+
+void
+module_name_entry::remove (value_type *entry)
+{
+ /* XDELETE (entry->source_name); */
+ XDELETE (entry);
+}
+
+/* Equal function for module name */
+
+int
+module_name_entry::equal (const value_type *s1, const compare_type *s2)
+{
+ return !strcmp (s1->source_name, s2->source_name);
+}
+
+static hash_table<module_name_entry> module_name_tab;
+
+/* Comparison function for sorting module_infos array. */
+
+static int
+cmp_module_name_entry (const void *p1, const void *p2)
+{
+ module_name_entry **slot1, **slot2;
+ module_name_entry *m_e1, *m_e2;
+
+ struct gcov_module_info *const *e1 = (struct gcov_module_info *const *) p1;
+ struct gcov_module_info *const *e2 = (struct gcov_module_info *const *) p2;
+
+ module_name_entry e;
+ e.source_name = (*e1)->source_filename;
+ slot1 = module_name_tab.find_slot (&e, NO_INSERT);
+ e.source_name = (*e2)->source_filename;
+ slot2 = module_name_tab.find_slot (&e, NO_INSERT);
+
+ if (!slot1 || !*slot1)
+ return 1;
+
+ if (!slot2 || !*slot2)
+ return -1;
+
+ gcc_assert (slot1 && *slot1 && slot2 && *slot2);
+ m_e1 = *slot1;
+ m_e2 = *slot2;
+
+ return m_e1->order - m_e2->order;
+}
+
+/* Comparison function for sorting fname array */
+
+static int
+cmp_fname_entry (const void *p1, const void *p2)
+{
+ module_name_entry **slot1, **slot2;
+ module_name_entry *m_e1, *m_e2;
+
+ const char *const *e1 = (const char *const *) p1;
+ const char *const *e2 = (const char *const *) p2;
+
+ module_name_entry e;
+
+ e.source_name = *e1;
+ slot1 = module_name_tab.find_slot (&e, NO_INSERT);
+ e.source_name = *e2;
+ slot2 = module_name_tab.find_slot (&e, NO_INSERT);
+
+ if (!slot1 || !*slot1)
+ return 1;
+
+ if (!slot2 || !*slot2)
+ return -1;
+
+ gcc_assert (slot1 && *slot1 && slot2 && *slot2);
+ m_e1 = *slot1;
+ m_e2 = *slot2;
+
+ return m_e1->order - m_e2->order;
+}
+
+/* Reorder module group according to file IMPORTS_FILE */
+
+static void
+reorder_module_groups (const char *imports_file, unsigned max_group)
+{
+ FILE *f;
+ int order = 0;
+ const int max_line_size = (1 << 16);
+ char line[max_line_size];
+
+ module_name_tab.create (20);
+
+ f = fopen (imports_file, "r");
+ if (!f)
+ error ("Can't open file %s", imports_file);
+
+ while (fgets (line, max_line_size, f))
+ {
+ size_t n = strlen (line);
+ gcc_assert (n < max_line_size - 1);
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+
+ module_name_entry **slot;
+ module_name_entry *m_e = XCNEW (module_name_entry);
+
+ m_e->source_name = xstrdup (line);
+ m_e->order = order;
+
+ slot = module_name_tab.find_slot (m_e, INSERT);
+ gcc_assert (!*slot);
+ *slot = m_e;
+
+ order++;
+ }
+
+ /* Now do the sorting */
+
+ qsort (&module_infos[1], num_in_fnames - 1, sizeof (void *),
+ cmp_module_name_entry);
+ qsort (&in_fnames[1], num_in_fnames - 1, sizeof (void *),
+ cmp_fname_entry);
+
+ {
+ unsigned i;
+
+ for (i = 0; i < num_in_fnames; i++)
+ fprintf (stderr, "*** %s (%s)\n", in_fnames[i],
+ i < max_group ? "Kept":"Skipped");
+
+ for (i = 0; i < num_in_fnames; i++)
+ fprintf (stderr, "### %s (%s)\n", module_infos[i]->source_filename,
+ i < max_group ? "Kept":"Skipped");
+
+ }
+
+ if (num_in_fnames > max_group)
+ num_in_fnames = max_group;
+
+ module_name_tab.dispose ();
+}
+
+typedef struct {
+ unsigned int mod_id;
+ const char *mod_name;
+} mod_id_to_name_t;
+
+static vec<mod_id_to_name_t> *mod_names;
+
+static void
+record_module_name (unsigned int mod_id, const char *name)
+{
+ mod_id_to_name_t t;
+
+ t.mod_id = mod_id;
+ t.mod_name = xstrdup (name);
+ if (!mod_names)
+ vec_alloc (mod_names, 10);
+ mod_names->safe_push (t);
+}
+
+/* Return the module name for module with MOD_ID. */
+
+const char *
+get_module_name (unsigned int mod_id)
+{
+ size_t i;
+ mod_id_to_name_t *elt;
+
+ for (i = 0; mod_names->iterate (i, &elt); i++)
+ {
+ if (elt->mod_id == mod_id)
+ return elt->mod_name;
+ }
+
+ gcc_assert (0);
+ return NULL;
+}
+
+/* Read in the counts file, if available. DA_FILE_NAME is the
+ name of the gcda file, and MODULE_ID is the module id of the
+ associated source module. */
static void
-read_counts_file (void)
+read_counts_file (const char *da_file_name, unsigned module_id)
{
gcov_unsigned_t fn_ident = 0;
struct gcov_summary summary;
unsigned new_summary = 1;
gcov_unsigned_t tag;
int is_error = 0;
+ unsigned module_infos_read = 0;
+ struct pointer_set_t *modset = 0;
+ unsigned max_group = PARAM_VALUE (PARAM_MAX_LIPO_GROUP);
unsigned lineno_checksum = 0;
unsigned cfg_checksum = 0;
+ const char *imports_filename;
+
+ if (max_group == 0)
+ max_group = (unsigned) -1;
if (!gcov_open (da_file_name, 1))
- return;
+ {
+ if (PARAM_VALUE (PARAM_GCOV_DEBUG))
+ {
+ /* Try to find .gcda file in the current working dir. */
+ da_file_name = lbasename (da_file_name);
+ if (!gcov_open (da_file_name, 1))
+ return;
+ }
+ else
+ return;
+ }
if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
{
@@ -215,7 +746,9 @@ read_counts_file (void)
tag = gcov_read_unsigned ();
bbg_file_stamp = crc32_unsigned (bbg_file_stamp, tag);
- counts_hash.create (10);
+ if (!counts_hash.is_created ())
+ counts_hash.create (10);
+
while ((tag = gcov_read_unsigned ()))
{
gcov_unsigned_t length;
@@ -267,7 +800,7 @@ read_counts_file (void)
unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
unsigned ix;
- elt.ident = fn_ident;
+ elt.ident = GEN_FUNC_GLOBAL_ID (module_id, fn_ident);
elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
slot = counts_hash.find_slot (&elt, INSERT);
@@ -275,7 +808,7 @@ read_counts_file (void)
if (!entry)
{
*slot = entry = XCNEW (counts_entry_t);
- entry->ident = fn_ident;
+ entry->ident = elt.ident;
entry->ctr = elt.ctr;
entry->lineno_checksum = lineno_checksum;
entry->cfg_checksum = cfg_checksum;
@@ -319,6 +852,133 @@ read_counts_file (void)
entry->counts[ix] += gcov_read_counter ();
skip_merge:;
}
+ /* Skip the MODULE_INFO records if not in dyn-ipa mode, or when reading
+ auxiliary modules. */
+ else if (tag == GCOV_TAG_MODULE_INFO && flag_dyn_ipa && !module_id)
+ {
+ struct gcov_module_info* mod_info;
+ size_t info_sz;
+ /* each string has at least 8 bytes, so MOD_INFO's
+ persistent length >= in core size. */
+ mod_info
+ = (struct gcov_module_info *) alloca ((length + 2)
+ * sizeof (gcov_unsigned_t));
+ gcov_read_module_info (mod_info, length);
+ info_sz = (sizeof (struct gcov_module_info) +
+ sizeof (void *) * (mod_info->num_quote_paths +
+ mod_info->num_bracket_paths +
+ mod_info->num_system_paths +
+ mod_info->num_cpp_defines +
+ mod_info->num_cpp_includes +
+ mod_info->num_cl_args));
+ /* The first MODULE_INFO record must be for the primary module. */
+ if (module_infos_read == 0)
+ {
+ gcc_assert (mod_info->is_primary && !modset);
+ module_infos_read++;
+ modset = pointer_set_create ();
+ pointer_set_insert (modset, (void *)(size_t)mod_info->ident);
+ primary_module_id = mod_info->ident;
+ include_all_aux = MODULE_INCLUDE_ALL_AUX_FLAG (mod_info);
+ module_infos = XCNEWVEC (struct gcov_module_info *, 1);
+ module_infos[0] = XCNEWVAR (struct gcov_module_info, info_sz);
+ memcpy (module_infos[0], mod_info, info_sz);
+ }
+ else
+ {
+ int fd;
+ char *aux_da_filename = get_da_file_name (mod_info->da_filename);
+ gcc_assert (!mod_info->is_primary);
+ if (pointer_set_insert (modset, (void *)(size_t)mod_info->ident))
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "Not importing %s: already imported",
+ mod_info->source_filename);
+ }
+ else if ((module_infos[0]->lang & GCOV_MODULE_LANG_MASK) !=
+ (mod_info->lang & GCOV_MODULE_LANG_MASK))
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "Not importing %s: source language"
+ " different from primary module's source"
+ " language",
+ mod_info->source_filename);
+ }
+ else if (module_infos_read == max_group
+ /* If reordering is specified, delay the cutoff
+ until after sorting. */
+ && !getenv ("LIPO_REORDER_GROUP"))
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "Not importing %s: maximum group size"
+ " reached", mod_info->source_filename);
+ }
+ else if (incompatible_cl_args (module_infos[0], mod_info))
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "Not importing %s: command-line"
+ " arguments not compatible with primary"
+ " module",
+ mod_info->source_filename);
+ }
+ else if ((fd = open (aux_da_filename, O_RDONLY)) < 0)
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "Not importing %s: couldn't open %s",
+ mod_info->source_filename,
+ aux_da_filename);
+ }
+ else if ((mod_info->lang & GCOV_MODULE_ASM_STMTS)
+ && flag_ripa_disallow_asm_modules)
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "Not importing %s: contains assembler"
+ " statements", mod_info->source_filename);
+ }
+ else if (mod_info->is_primary == false
+ && MODULE_EXPORTED_FLAG (mod_info) == false)
+ {
+ warning (0, "MODULE_ID=%d (%s) is an auxiliary module, "
+ "but export_bit is not set. \n",
+ mod_info->ident, mod_info->source_filename);
+ }
+ else
+ {
+ close (fd);
+ module_infos_read++;
+ add_input_filename (mod_info->source_filename);
+ module_infos = XRESIZEVEC (struct gcov_module_info *,
+ module_infos, num_in_fnames);
+ gcc_assert (num_in_fnames == module_infos_read);
+ module_infos[module_infos_read - 1]
+ = XCNEWVAR (struct gcov_module_info, info_sz);
+ memcpy (module_infos[module_infos_read - 1], mod_info,
+ info_sz);
+ }
+ }
+
+ record_module_name (mod_info->ident,
+ lbasename (mod_info->source_filename));
+
+ if (dump_enabled_p ())
+ {
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "MODULE Id=%d, Is_Primary=%s,"
+ " Is_Exported=%s, Include_all=%s, Name=%s (%s)",
+ mod_info->ident, mod_info->is_primary?"yes":"no",
+ MODULE_EXPORTED_FLAG (mod_info)?"yes":"no",
+ MODULE_INCLUDE_ALL_AUX_FLAG (mod_info)?"yes"
+ :"no",
+ mod_info->source_filename,
+ mod_info->da_filename);
+ }
+ }
gcov_sync (offset, length);
if ((is_error = gcov_is_error ()))
{
@@ -329,9 +989,48 @@ read_counts_file (void)
}
}
+ if ((imports_filename = getenv ("LIPO_REORDER_GROUP"))
+ && flag_dyn_ipa && !module_id)
+ {
+ reorder_module_groups (imports_filename, max_group);
+ if (module_infos_read != num_in_fnames)
+ module_infos_read = num_in_fnames;
+ }
+
+ /* TODO: profile based multiple module compilation does not work
+ together with command line (-combine) based ipo -- add a nice
+ warning and bail out instead of asserting. */
+
+ if (modset)
+ pointer_set_destroy (modset);
+ gcc_assert (module_infos_read == 0
+ || module_infos_read == num_in_fnames);
+
+ if (flag_dyn_ipa)
+ gcc_assert (primary_module_id && num_in_fnames >= 1);
+
gcov_close ();
}
+/* Returns the coverage data entry for counter type COUNTER of function
+ FUNC. EXPECTED is the number of expected counter entries. */
+
+static counts_entry_t *
+get_coverage_counts_entry (struct function *func, unsigned counter)
+{
+ counts_entry_t *entry, elt;
+
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ elt.ident = FUNC_DECL_GLOBAL_ID (func);
+ else
+ elt.ident = coverage_compute_profile_id (cgraph_get_node (func->decl));
+
+ elt.ctr = counter;
+ entry = counts_hash.find (&elt);
+
+ return entry;
+}
+
/* Returns the counters for a particular tag. */
gcov_type *
@@ -339,7 +1038,7 @@ get_coverage_counts (unsigned counter, unsigned expected,
unsigned cfg_checksum, unsigned lineno_checksum,
const struct gcov_ctr_summary **summary)
{
- counts_entry_t *entry, elt;
+ counts_entry_t *entry;
/* No hash table, no counts. */
if (!counts_hash.is_created ())
@@ -356,15 +1055,18 @@ get_coverage_counts (unsigned counter, unsigned expected,
return NULL;
}
- elt.ident = current_function_funcdef_no + 1;
- elt.ctr = counter;
- entry = counts_hash.find (&elt);
+ entry = get_coverage_counts_entry (cfun, counter);
+
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 (!flag_dyn_ipa && dump_enabled_p ())
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+ "no coverage for function %s found",
+ IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (current_function_decl)));
+ return NULL;
+ }
+
if (entry->cfg_checksum != cfg_checksum
|| entry->summary.num != expected)
{
@@ -400,12 +1102,13 @@ get_coverage_counts (unsigned counter, unsigned expected,
return NULL;
}
- else if (entry->lineno_checksum != lineno_checksum)
- {
- warning (0, "source locations for function %qE have changed,"
- " the profile data may be out of date",
- DECL_ASSEMBLER_NAME (current_function_decl));
- }
+ else if (entry->lineno_checksum != lineno_checksum)
+ {
+ warning (OPT_Wripa_opt_mismatch,
+ "Source location for function %qE have changed,"
+ " the profile data may be out of date",
+ DECL_ASSEMBLER_NAME (current_function_decl));
+ }
if (summary)
*summary = &entry->summary;
@@ -413,6 +1116,31 @@ get_coverage_counts (unsigned counter, unsigned expected,
return entry->counts;
}
+/* Returns the coverage data entry for counter type COUNTER of function
+ FUNC. On return, *N_COUNTS is set to the number of entries in the counter. */
+
+gcov_type *
+get_coverage_counts_no_warn (struct function *f, unsigned counter, unsigned *n_counts)
+{
+ counts_entry_t *entry, elt;
+
+ /* No hash table, no counts. */
+ if (!counts_hash.is_created () || !f)
+ return NULL;
+
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ elt.ident = FUNC_DECL_GLOBAL_ID (f);
+ else
+ elt.ident = coverage_compute_profile_id (cgraph_get_node (f->decl));
+ elt.ctr = counter;
+ entry = counts_hash.find (&elt);
+ if (!entry)
+ return NULL;
+
+ *n_counts = entry->summary.num;
+ return entry->counts;
+}
+
/* Allocate NUM counters of type COUNTER. Returns nonzero if the
allocation succeeded. */
@@ -474,6 +1202,26 @@ tree_coverage_counter_addr (unsigned counter, unsigned no)
}
+/* Generate a crc32 of a string with specified STR_LEN when it's not 0.
+ Non-zero STR_LEN should only be seen in LIPO mode. */
+
+static unsigned
+crc32_string_1 (unsigned chksum, const char *string, unsigned str_len)
+{
+ char *dup;
+
+ if (!L_IPO_COMP_MODE || str_len == 0)
+ return crc32_string (chksum, string);
+
+ gcc_assert (str_len > 0 && str_len < strlen (string));
+ dup = xstrdup (string);
+ dup[str_len] = 0;
+ chksum = crc32_string (chksum, dup);
+ free (dup);
+
+ return chksum;
+}
+
/* Generate a checksum for a string. CHKSUM is the current
checksum. */
@@ -482,6 +1230,25 @@ coverage_checksum_string (unsigned chksum, const char *string)
{
int i;
char *dup = NULL;
+ unsigned lipo_orig_str_len = 0;
+
+ /* Strip out the ending ".cmo.[0-9]*" string from function
+ name. Otherwise we will have lineno checksum mismatch. */
+ if (L_IPO_COMP_MODE)
+ {
+ int len;
+
+ i = len = strlen (string);
+ while (i--)
+ if ((string[i] < '0' || string[i] > '9'))
+ break;
+ if ((i > 5) && (i != len - 1))
+ {
+ if (!strncmp (string + i - 4, ".cmo.", 5))
+ lipo_orig_str_len = i - 4;
+ }
+
+ }
/* Look for everything that looks if it were produced by
get_file_function_name and zero out the second part
@@ -528,8 +1295,9 @@ coverage_checksum_string (unsigned chksum, const char *string)
}
}
- chksum = crc32_string (chksum, string);
- free (dup);
+ chksum = crc32_string_1 (chksum, string, lipo_orig_str_len);
+ if (dup)
+ free (dup);
return chksum;
}
@@ -539,13 +1307,32 @@ coverage_checksum_string (unsigned chksum, const char *string)
unsigned
coverage_compute_lineno_checksum (void)
{
+ tree name;
expanded_location xloc
= expand_location (DECL_SOURCE_LOCATION (current_function_decl));
unsigned chksum = xloc.line;
+ const char *pathless_filename = xloc.file;
+ int i;
+ for (i = strlen (xloc.file); i >= 0; i--)
+ if (IS_DIR_SEPARATOR (pathless_filename[i]))
+ {
+ pathless_filename += i + 1;
+ break;
+ }
+
+ chksum = coverage_checksum_string (chksum, pathless_filename);
+
+ /* Note: it is a bad design that C++ FE associate the convertion function type
+ with the name of the decl. This leads to cross contamination between different
+ conversion operators in different modules (If conv_type_names map is cleared
+ at the end of parsing of each module). */
+ if (flag_dyn_ipa && lang_hooks.user_conv_function_p (current_function_decl))
+ name = DECL_ASSEMBLER_NAME (current_function_decl);
+ else
+ name = DECL_NAME (current_function_decl);
- chksum = coverage_checksum_string (chksum, xloc.file);
chksum = coverage_checksum_string
- (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)));
+ (chksum, IDENTIFIER_POINTER (name));
return chksum;
}
@@ -557,12 +1344,13 @@ coverage_compute_profile_id (struct cgraph_node *n)
{
expanded_location xloc
= expand_location (DECL_SOURCE_LOCATION (n->decl));
- unsigned chksum = xloc.line;
+ bool use_name_only = (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID) == 0);
+ unsigned 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 (first_global_object_name)
+ if (!use_name_only && first_global_object_name)
chksum = coverage_checksum_string
(chksum, first_global_object_name);
chksum = coverage_checksum_string
@@ -620,7 +1408,12 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
/* Announce function */
offset = gcov_write_tag (GCOV_TAG_FUNCTION);
- gcov_write_unsigned (current_function_funcdef_no + 1);
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ gcov_write_unsigned (FUNC_DECL_FUNC_ID (cfun));
+ else
+ gcov_write_unsigned (coverage_compute_profile_id (
+ cgraph_get_node (cfun->decl)));
+
gcov_write_unsigned (lineno_checksum);
gcov_write_unsigned (cfg_checksum);
gcov_write_string (IDENTIFIER_POINTER
@@ -658,7 +1451,16 @@ coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum)
{
item = ggc_alloc_coverage_data ();
- item->ident = current_function_funcdef_no + 1;
+ if (PARAM_VALUE (PARAM_PROFILE_FUNC_INTERNAL_ID))
+ item->ident = FUNC_DECL_FUNC_ID (cfun);
+ else
+ {
+ if (flag_dyn_ipa)
+ error ("param=profile-func-internal-id=0 is not"
+ " supported in LIPO mode. ");
+ item->ident = coverage_compute_profile_id (
+ cgraph_get_node (cfun->decl));
+ }
item->lineno_checksum = lineno_checksum;
item->cfg_checksum = cfg_checksum;
@@ -692,6 +1494,70 @@ coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum)
}
}
+/* True if a function entry corresponding to the given FN_IDENT
+ is present in the coverage internal data structures. */
+
+bool
+coverage_function_present (unsigned fn_ident)
+{
+ struct coverage_data *item = functions_head;
+ while (item && item->ident != fn_ident)
+ item = item->next;
+ return item != NULL;
+}
+
+/* Update function and program direct-call coverage counts. */
+
+void
+coverage_dc_end_function (void)
+{
+ tree var;
+
+ if (fn_ctr_mask)
+ {
+ const unsigned idx = GCOV_COUNTER_DIRECT_CALL;
+ struct coverage_data *item = functions_head;
+ while (item && item->ident != (unsigned) FUNC_DECL_FUNC_ID (cfun))
+ item = item->next;
+
+ /* If a matching function entry hasn't been found, either this function
+ has had no coverage counts added in the profile pass, or this
+ is a new function (function versioning, etc). Create a new entry. */
+ if (!item)
+ {
+ int cnt;
+
+ item = ggc_alloc_coverage_data ();
+ *functions_tail = item;
+ functions_tail = &item->next;
+ item->next = 0;
+ item->ident = FUNC_DECL_FUNC_ID (cfun);
+ item->fn_decl = current_function_decl;
+ item->lineno_checksum = coverage_compute_lineno_checksum ();
+ item->cfg_checksum = coverage_compute_cfg_checksum ();
+ for (cnt = 0; cnt < GCOV_COUNTERS; cnt++)
+ item->ctr_vars[cnt] = NULL_TREE;
+ }
+
+ var = fn_v_ctrs[idx];
+ item->ctr_vars[idx] = var;
+ if (var)
+ {
+ tree array_type = build_index_type (size_int (fn_n_ctrs[idx] - 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_finalize_decl (var);
+ }
+
+ fn_n_ctrs[idx] = fn_b_ctrs[idx] = 0;
+ fn_v_ctrs[idx] = NULL_TREE;
+ prg_ctr_mask |= fn_ctr_mask;
+ fn_ctr_mask = 0;
+ }
+}
+
/* Build a coverage variable of TYPE for function FN_DECL. If COUNTER
>= 0 it is a counter array, otherwise it is the function structure. */
@@ -737,18 +1603,18 @@ build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
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 */
@@ -756,13 +1622,13 @@ build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
build_pointer_type (build_qualified_type
(gcov_info_type, TYPE_QUAL_CONST)));
fields = field;
-
+
/* ident */
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 ());
@@ -845,6 +1711,12 @@ build_fn_info (const struct coverage_data *data, tree type, tree key)
build_fold_addr_expr (var));
CONSTRUCTOR_APPEND_ELT (v2, NULL, build_constructor (ctr_type, ctr));
+
+ /* In LIPO mode, coverage_finish is called late when pruning can not be
+ * done, so we need to force emitting counter variables even for
+ * eliminated functions to avoid unsat. */
+ if (flag_dyn_ipa && var)
+ varpool_finalize_decl (var);
}
CONSTRUCTOR_APPEND_ELT (v1, fields,
@@ -860,7 +1732,7 @@ static void
build_info_type (tree type, tree fn_info_ptr_type)
{
tree field, fields = NULL_TREE;
- tree merge_fn_type;
+ tree merge_fn_type, mod_type;
/* Version ident */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
@@ -868,6 +1740,13 @@ build_info_type (tree type, tree fn_info_ptr_type)
DECL_CHAIN (field) = fields;
fields = field;
+ /* mod_info */
+ mod_type = build_gcov_module_info_type ();
+ mod_type = build_pointer_type (mod_type);
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, mod_type);
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
/* next pointer */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_pointer_type (build_qualified_type
@@ -888,6 +1767,12 @@ build_info_type (tree type, tree fn_info_ptr_type)
DECL_CHAIN (field) = fields;
fields = field;
+ /* eof_pos */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
/* merge fn array */
merge_fn_type
= build_function_type_list (void_type_node,
@@ -918,6 +1803,402 @@ build_info_type (tree type, tree fn_info_ptr_type)
finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
}
+/* Compute an array (tree) of include path strings. STRING_TYPE is
+ the path string type, INC_PATH_VALUE is the initial value of the
+ path array, PATHS gives raw path string values, and NUM is the
+ number of paths. */
+
+static void
+build_inc_path_array_value (tree string_type, vec<constructor_elt, va_gc> **v,
+ cpp_dir *paths, int num)
+{
+ int i;
+ cpp_dir *pdir;
+ for (i = 0, pdir = paths; i < num; pdir = pdir->next)
+ {
+ const char *path_raw_string;
+ int path_string_length;
+ tree path_string;
+ path_raw_string = pdir->name;
+ path_string_length = strlen (path_raw_string);
+ path_string = build_string (path_string_length + 1, path_raw_string);
+ TREE_TYPE (path_string) = build_array_type
+ (char_type_node, build_index_type
+ (build_int_cst (NULL_TREE, path_string_length)));
+ CONSTRUCTOR_APPEND_ELT (*v, NULL,
+ build1 (ADDR_EXPR, string_type, path_string));
+ i++;
+ }
+}
+
+/* Compute an array (tree) of strings. STR_TYPE is the string type,
+ STR_ARRAY_VALUE is the initial value of the string array, and HEAD gives
+ the list of raw strings. */
+
+static void
+build_str_array_value (tree str_type, vec<constructor_elt, va_gc> **v,
+ struct str_list *head)
+{
+ const char *raw_str;
+ int str_length;
+ while (head)
+ {
+ tree str;
+ raw_str = head->str;
+ str_length = strlen (raw_str);
+ str = build_string (str_length + 1, raw_str);
+ TREE_TYPE (str) =
+ build_array_type (char_type_node,
+ build_index_type (build_int_cst (NULL_TREE,
+ str_length)));
+ CONSTRUCTOR_APPEND_ELT (*v, NULL,
+ build1 (ADDR_EXPR, str_type, str));
+ head = head->next;
+ }
+ return;
+}
+
+/* Compute an array (tree) of command-line argument strings. STRING_TYPE is
+ the string type, CL_ARGS_VALUE is the initial value of the command-line
+ args array. */
+
+static void
+build_cl_args_array_value (tree string_type, vec<constructor_elt, va_gc> **v)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_lipo_cl_args; i++)
+ {
+ int arg_length = strlen (lipo_cl_args[i]);
+ tree arg_string = build_string (arg_length + 1, lipo_cl_args[i]);
+ TREE_TYPE (arg_string) =
+ build_array_type (char_type_node,
+ build_index_type (build_int_cst (NULL_TREE,
+ arg_length)));
+ CONSTRUCTOR_APPEND_ELT (*v, NULL,
+ build1 (ADDR_EXPR, string_type, arg_string));
+ }
+ return;
+}
+
+/* Emit mapping between module name and function id to the function's
+ assembler name, for use in correlating function idents in the gcda file
+ with the function name. */
+
+void
+emit_function_name (void)
+{
+ fprintf (stderr, "Module %s FuncId %u Name %s\n",
+ (L_IPO_COMP_MODE
+ ? get_module_name (FUNC_DECL_MODULE_ID (cfun))
+ : main_input_file_name),
+ FUNC_DECL_FUNC_ID (cfun),
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)));
+}
+
+/* Returns the type of the module info associated with the
+ current source module being compiled. */
+
+static tree
+build_gcov_module_info_type (void)
+{
+ tree type, field, fields = NULL_TREE;
+ tree string_type, index_type, string_array_type;
+
+ cpp_dir *quote_paths, *bracket_paths, *system_paths, *pdir;
+ int num_quote_paths = 0, num_bracket_paths = 0, num_system_paths = 0;
+
+ type = lang_hooks.types.make_type (RECORD_TYPE);
+ string_type = build_pointer_type (
+ build_qualified_type (char_type_node,
+ TYPE_QUAL_CONST));
+
+ /* ident */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* is_primary */
+ /* We also overload this field to store a flag that indicates whether this
+ module was built in regular FDO or LIPO mode (-fripa). When reading this
+ field from a GCDA file, it should be used as the IS_PRIMARY flag. When
+ reading this field from the binary's data section, it should be used
+ as a FDO/LIPO flag. */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* flags: is_exported and include_all_aux flag. */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* lang field */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* ggc_memory field */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* da_filename */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, string_type);
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* Source name */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, string_type);
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* Num quote paths */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* Num bracket paths */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* Num system paths */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* Num -D/-U options. */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* Num -imacro/-include options. */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+ get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ /* Num command-line args. */
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, get_gcov_unsigned_t ());
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ get_include_chains (&quote_paths, &bracket_paths, &system_paths);
+ for (pdir = quote_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == bracket_paths)
+ break;
+ num_quote_paths++;
+ }
+ for (pdir = bracket_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == system_paths)
+ break;
+ num_bracket_paths++;
+ }
+ for (pdir = system_paths; pdir; pdir = pdir->next)
+ num_system_paths++;
+
+ /* string array */
+ index_type = build_index_type (build_int_cst (NULL_TREE,
+ num_quote_paths +
+ num_bracket_paths +
+ num_system_paths +
+ num_cpp_defines +
+ num_cpp_includes +
+ num_lipo_cl_args));
+
+ string_array_type = build_array_type (string_type, index_type);
+ field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, string_array_type);
+ DECL_CHAIN (field) = fields;
+ fields = field;
+
+ finish_builtin_struct (type, "__gcov_module_info", fields, NULL_TREE);
+
+ return type;
+}
+
+/* Returns the value of the module info associated with the
+ current source module being compiled. */
+
+static tree
+build_gcov_module_info_value (tree mod_type)
+{
+ tree info_fields, mod_info;
+ tree value = NULL_TREE;
+ int file_name_len;
+ tree filename_string, string_array_type, string_type;
+ cpp_dir *quote_paths, *bracket_paths, *system_paths, *pdir;
+ int num_quote_paths = 0, num_bracket_paths = 0, num_system_paths = 0;
+ unsigned lang;
+ char name_buf[50];
+ vec<constructor_elt,va_gc> *v = NULL, *path_v = NULL;
+
+ info_fields = TYPE_FIELDS (mod_type);
+
+ /* ident */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (), 0));
+
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* is_primary */
+ /* We also overload this field to store a flag that indicates whether this
+ module was built in regular FDO or LIPO mode (-fripa). When reading this
+ field from a GCDA file, it should be used as the IS_PRIMARY flag. When
+ reading this field from the binary's data section, it should be used
+ as a FDO/LIPO flag. */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ flag_dyn_ipa ? 1 : 0));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* flags */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (), 0));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* lang field */
+ if (!strcmp (lang_hooks.name, "GNU C"))
+ lang = GCOV_MODULE_C_LANG;
+ else if (!strcmp (lang_hooks.name, "GNU C++"))
+ lang = GCOV_MODULE_CPP_LANG;
+ else
+ lang = GCOV_MODULE_UNKNOWN_LANG;
+ if (has_asm_statement)
+ lang |= GCOV_MODULE_ASM_STMTS;
+
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (), lang));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* ggc_memory field */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (), ggc_total_memory));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* da_filename */
+
+ string_type = TREE_TYPE (info_fields);
+ file_name_len = strlen (da_base_file_name);
+ filename_string = build_string (file_name_len + 1, da_base_file_name);
+ TREE_TYPE (filename_string) = build_array_type
+ (char_type_node, build_index_type
+ (build_int_cst (NULL_TREE, file_name_len)));
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build1 (ADDR_EXPR, string_type, filename_string));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* Source name */
+
+ file_name_len = strlen (main_input_file_name);
+ filename_string = build_string (file_name_len + 1, main_input_file_name);
+ TREE_TYPE (filename_string) = build_array_type
+ (char_type_node, build_index_type
+ (build_int_cst (NULL_TREE, file_name_len)));
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build1 (ADDR_EXPR, string_type, filename_string));
+ info_fields = DECL_CHAIN (info_fields);
+
+ get_include_chains (&quote_paths, &bracket_paths, &system_paths);
+ for (pdir = quote_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == bracket_paths)
+ break;
+ num_quote_paths++;
+ }
+ for (pdir = bracket_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == system_paths)
+ break;
+ num_bracket_paths++;
+ }
+ for (pdir = system_paths; pdir; pdir = pdir->next)
+ num_system_paths++;
+
+ /* Num quote paths */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ num_quote_paths));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* Num bracket paths */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ num_bracket_paths));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* Num system paths */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ num_system_paths));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* Num -D/-U options. */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ num_cpp_defines));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* Num -imacro/-include options. */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ num_cpp_includes));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* Num command-line args. */
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_int_cstu (get_gcov_unsigned_t (),
+ num_lipo_cl_args));
+ info_fields = DECL_CHAIN (info_fields);
+
+ /* string array */
+ string_array_type = TREE_TYPE (info_fields);
+ build_inc_path_array_value (string_type, &path_v,
+ quote_paths, num_quote_paths);
+ build_inc_path_array_value (string_type, &path_v,
+ bracket_paths, num_bracket_paths);
+ build_inc_path_array_value (string_type, &path_v,
+ system_paths, num_system_paths);
+ build_str_array_value (string_type, &path_v,
+ cpp_defines_head);
+ build_str_array_value (string_type, &path_v,
+ cpp_includes_head);
+ build_cl_args_array_value (string_type, &path_v);
+ CONSTRUCTOR_APPEND_ELT (v, info_fields,
+ build_constructor (string_array_type, path_v));
+ info_fields = DECL_CHAIN (info_fields);
+
+ gcc_assert (!info_fields);
+ value = build_constructor (mod_type, v);
+
+ mod_info = build_decl (BUILTINS_LOCATION, VAR_DECL,
+ NULL_TREE, TREE_TYPE (value));
+ TREE_STATIC (mod_info) = 1;
+ ASM_GENERATE_INTERNAL_LABEL (name_buf, "MODINFO", 0);
+ DECL_NAME (mod_info) = get_identifier (name_buf);
+ DECL_INITIAL (mod_info) = value;
+
+ /* Build structure. */
+ varpool_finalize_decl (mod_info);
+
+ return mod_info;
+}
+
/* 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. */
@@ -928,6 +2209,7 @@ build_info (tree info_type, tree fn_ary)
tree info_fields = TYPE_FIELDS (info_type);
tree merge_fn_type, n_funcs;
unsigned ix;
+ tree mod_value = NULL_TREE;
tree filename_string;
int da_file_name_len;
vec<constructor_elt, va_gc> *v1 = NULL;
@@ -939,6 +2221,14 @@ build_info (tree info_type, tree fn_ary)
GCOV_VERSION));
info_fields = DECL_CHAIN (info_fields);
+ /* mod_info */
+ mod_value = build_gcov_module_info_value (TREE_TYPE (TREE_TYPE (info_fields)));
+ mod_value = build1 (ADDR_EXPR,
+ build_pointer_type (TREE_TYPE (mod_value)),
+ mod_value);
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields, mod_value);
+ info_fields = DECL_CHAIN (info_fields);
+
/* next -- NULL */
CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
info_fields = DECL_CHAIN (info_fields);
@@ -959,6 +2249,11 @@ build_info (tree info_type, tree fn_ary)
filename_string));
info_fields = DECL_CHAIN (info_fields);
+ /* eof_pos */
+ CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+ build_int_cstu (TREE_TYPE (info_fields), 0));
+ 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++)
@@ -1050,12 +2345,15 @@ coverage_obj_init (void)
fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
/* 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 (!flag_dyn_ipa)
+ /* in lipo mode, coverage_finish is called when function struct is cleared,
+ so pruning code here will skip all 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;
@@ -1080,8 +2378,6 @@ coverage_obj_init (void)
ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
DECL_NAME (gcov_info_var) = get_identifier (name_buf);
- build_init_ctor (gcov_info_type);
-
return true;
}
@@ -1104,7 +2400,8 @@ coverage_obj_fn (vec<constructor_elt, va_gc> *ctor, tree fn,
}
/* Finalize the coverage data. Generates the array of pointers to
- function objects from CTOR. Generate the gcov_info initializer. */
+ function objects from CTOR. Generate the gcov_info initializer.
+ Generate the constructor function to call __gcov_init. */
static void
coverage_obj_finish (vec<constructor_elt, va_gc> *ctor)
@@ -1122,20 +2419,289 @@ coverage_obj_finish (vec<constructor_elt, va_gc> *ctor)
DECL_NAME (fn_info_ary) = get_identifier (name_buf);
DECL_INITIAL (fn_info_ary) = build_constructor (fn_info_ary_type, ctor);
varpool_finalize_decl (fn_info_ary);
-
+
DECL_INITIAL (gcov_info_var)
= build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
+
+ build_init_ctor (TREE_TYPE (gcov_info_var));
+
varpool_finalize_decl (gcov_info_var);
}
+/* Get the da file name, given base file name. */
+
+static char *
+get_da_file_name (const char *base_file_name)
+{
+ char *da_file_name;
+ int len = strlen (base_file_name);
+ const char *prefix = profile_data_prefix;
+ int prefix_len = 0;
+
+ if (profile_data_prefix == 0 && !IS_ABSOLUTE_PATH(&base_file_name[0]))
+ {
+ profile_data_prefix = getpwd ();
+ prefix = profile_data_prefix;
+ }
+
+ prefix_len = (prefix) ? strlen (prefix) + 1 : 0;
+
+ /* Name of da file. */
+ da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
+ + prefix_len + 2);
+
+ if (prefix)
+ {
+ strcpy (da_file_name, prefix);
+ da_file_name[prefix_len - 1] = '/';
+ da_file_name[prefix_len] = 0;
+ }
+ else
+ da_file_name[0] = 0;
+ strcat (da_file_name, base_file_name);
+ if (profile_base_name_suffix_to_strip)
+ {
+ int base_name_len = strlen (da_file_name);
+ int suffix_to_strip_len = strlen (profile_base_name_suffix_to_strip);
+
+ if (base_name_len > suffix_to_strip_len
+ && !strcmp (da_file_name + (base_name_len - suffix_to_strip_len),
+ profile_base_name_suffix_to_strip))
+ da_file_name[base_name_len - suffix_to_strip_len] = '\0';
+ }
+
+ strcat (da_file_name, GCOV_DATA_SUFFIX);
+ return da_file_name;
+}
+
+/* Callback to move counts_entry from one hash table to
+ the target hashtable */
+
+int
+move_hash_entry_callback (counts_entry **x,
+ hash_table <counts_entry> *target_counts_hash)
+{
+ counts_entry *entry = *x;
+ counts_entry **slot;
+ slot = target_counts_hash->find_slot (entry, INSERT);
+ *slot = entry;
+ return 1;
+}
+
+/* Rebuild counts_hash already built the primary module. This hashtable
+ was built with a module-id of zero. It needs to be rebuilt taking the
+ correct primary module-id into account. */
+
+int
+rehash_callback (counts_entry **x,
+ hash_table <counts_entry> *target_counts_hash)
+{
+ counts_entry *entry = *x;
+ counts_entry **slot;
+
+ entry->ident = GEN_FUNC_GLOBAL_ID (primary_module_id, entry->ident);
+ slot = target_counts_hash->find_slot (entry, INSERT);
+ *slot = entry;
+ return 1;
+}
+
+/* Rebuild counts_hash already built the primary module. This hashtable
+ was built with a module-id of zero. It needs to be rebuilt taking the
+ correct primary module-id into account. */
+
+static void
+rebuild_counts_hash (void)
+{
+ hash_table <counts_entry> tmp_counts_hash;
+ tmp_counts_hash.create (10);
+ gcc_assert (primary_module_id);
+
+ rebuilding_counts_hash = true;
+
+ /* Move the counts entries to the temporary hashtable. */
+ counts_hash.traverse_noresize <
+ hash_table <counts_entry> *,
+ move_hash_entry_callback> (&tmp_counts_hash);
+ counts_hash.empty ();
+
+ /* Now rehash and copy back. */
+ tmp_counts_hash.traverse_noresize <
+ hash_table <counts_entry> *,
+ rehash_callback> (&counts_hash);
+ tmp_counts_hash.dispose();
+
+ rebuilding_counts_hash = false;
+}
+
+/* Add the module information record for the module with id
+ MODULE_ID. IS_PRIMARY is true if the module is the primary module.
+ INDEX is the index of the new record in the module info array. */
+
+void
+add_module_info (unsigned module_id, bool is_primary, int index)
+{
+ struct gcov_module_info *cur_info;
+ module_infos = XRESIZEVEC (struct gcov_module_info *,
+ module_infos, index + 1);
+ module_infos[index] = XNEW (struct gcov_module_info);
+ cur_info = module_infos[index];
+ cur_info->ident = module_id;
+ SET_MODULE_EXPORTED (cur_info);
+ cur_info->num_quote_paths = 0;
+ cur_info->num_bracket_paths = 0;
+ cur_info->da_filename = NULL;
+ cur_info->source_filename = NULL;
+ if (is_primary)
+ primary_module_id = module_id;
+}
+
+/* Process the include paths needed for parsing the aux modules.
+ The sub_pattern is in the form SUB_PATH:NEW_SUB_PATH. If it is
+ defined, the SUB_PATH in ORIG_INC_PATH will be replaced with
+ NEW_SUB_PATH. */
+
+static void
+process_include (char **orig_inc_path, char* old_sub, char *new_sub)
+{
+ char *inc_path, *orig_sub;
+
+ if (strlen (*orig_inc_path) < strlen (old_sub))
+ return;
+
+ inc_path = (char*) xmalloc (strlen (*orig_inc_path) + strlen (new_sub)
+ - strlen (old_sub) + 1);
+ orig_sub = strstr (*orig_inc_path, old_sub);
+ if (!orig_sub)
+ {
+ inform (UNKNOWN_LOCATION, "subpath %s not found in path %s",
+ old_sub, *orig_inc_path);
+ free (inc_path);
+ return;
+ }
+
+ strncpy (inc_path, *orig_inc_path, orig_sub - *orig_inc_path);
+ inc_path[orig_sub - *orig_inc_path] = '\0';
+ strcat (inc_path, new_sub);
+ strcat (inc_path, orig_sub + strlen (old_sub));
+
+ free (*orig_inc_path);
+ *orig_inc_path = inc_path;
+}
+
+/* Process include paths for MOD_INFO according to option
+ -fripa-inc-path-sub=OLD_SUB:NEW_SUB */
+
+static void
+process_include_paths_1 (struct gcov_module_info *mod_info,
+ char* old_sub, char *new_sub)
+{
+ unsigned i, j;
+
+ for (i = 0; i < mod_info->num_quote_paths; i++)
+ process_include (&mod_info->string_array[i], old_sub, new_sub);
+
+ for (i = 0, j = mod_info->num_quote_paths;
+ i < mod_info->num_bracket_paths; i++, j++)
+ process_include (&mod_info->string_array[j], old_sub, new_sub);
+
+ for (i = 0, j = mod_info->num_quote_paths + mod_info->num_bracket_paths +
+ mod_info->num_cpp_defines; i < mod_info->num_cpp_includes; i++, j++)
+ process_include (&mod_info->string_array[j], old_sub, new_sub);
+
+}
+
+/* Process include paths for MOD_INFO according to option
+ -fripa-inc-path-sub=old_sub1:new_sub1[,old_sub2:new_sub2] */
+
+static void
+process_include_paths (struct gcov_module_info *mod_info)
+{
+ char *sub_pattern, *cur, *next, *new_sub;
+
+ if (!lipo_inc_path_pattern)
+ return;
+
+ sub_pattern = xstrdup (lipo_inc_path_pattern);
+ cur = sub_pattern;
+
+ do
+ {
+ next = strchr (cur, ',');
+ if (next)
+ *next++ = '\0';
+ new_sub = strchr (cur, ':');
+ if (!new_sub)
+ {
+ error ("Invalid path substibution pattern %s", sub_pattern);
+ free (sub_pattern);
+ return;
+ }
+ *new_sub++ = '\0';
+ process_include_paths_1 (mod_info, cur, new_sub);
+ cur = next;
+ } while (cur);
+ free (sub_pattern);
+}
+
+/* Set the prepreprocessing context (include search paths, -D/-U).
+ PARSE_IN is the preprocessor reader, I is the index of the module,
+ and VERBOSE is the verbose flag. */
+
+void
+set_lipo_c_parsing_context (struct cpp_reader *parse_in, int i, bool verbose)
+{
+ struct gcov_module_info *mod_info;
+ if (!L_IPO_COMP_MODE)
+ return;
+
+ mod_info = module_infos[i];
+
+ gcc_assert (flag_dyn_ipa);
+ current_module_id = mod_info->ident;
+ reset_funcdef_no ();
+
+ if (current_module_id != primary_module_id)
+ {
+ unsigned i, j;
+
+ process_include_paths (mod_info);
+ /* Setup include paths. */
+ clear_include_chains ();
+ for (i = 0; i < mod_info->num_quote_paths; i++)
+ add_path (xstrdup (mod_info->string_array[i]),
+ QUOTE, 0, 1);
+ for (i = 0, j = mod_info->num_quote_paths;
+ i < mod_info->num_bracket_paths; i++, j++)
+ add_path (xstrdup (mod_info->string_array[j]),
+ BRACKET, 0, 1);
+ for (i = 0; i < mod_info->num_system_paths; i++, j++)
+ add_path (xstrdup (mod_info->string_array[j]),
+ SYSTEM, 0, 1);
+ register_include_chains (parse_in, NULL, NULL, NULL,
+ 0, 0, verbose);
+
+ /* Setup defines/undefs. */
+ for (i = 0; i < mod_info->num_cpp_defines; i++, j++)
+ if (mod_info->string_array[j][0] == 'D')
+ cpp_define (parse_in, mod_info->string_array[j] + 1);
+ else
+ cpp_undef (parse_in, mod_info->string_array[j] + 1);
+
+ /* Setup -imacro/-include. */
+ for (i = 0; i < mod_info->num_cpp_includes; i++, j++)
+ cpp_push_include (parse_in, mod_info->string_array[j]);
+ }
+}
+
/* Perform file-level initialization. Read in data file, generate name
- of notes file. */
+ of graph file. */
void
-coverage_init (const char *filename)
+coverage_init (const char *filename, const char* source_name)
{
+ char* src_name_prefix = 0;
+ int src_name_prefix_len = 0;
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
@@ -1144,28 +2710,52 @@ coverage_init (const char *filename)
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 ();
+ has_asm_statement = false;
+ da_file_name = get_da_file_name (filename);
+ da_base_file_name = XNEWVEC (char, strlen (filename) + 1);
+ strcpy (da_base_file_name, filename);
- if (profile_data_prefix)
- prefix_len = strlen (profile_data_prefix);
+ if (profile_data_prefix == 0 && !IS_ABSOLUTE_PATH (source_name))
+ {
+ src_name_prefix = getpwd ();
+ src_name_prefix_len = strlen (src_name_prefix) + 1;
+ }
+ main_input_file_name = XNEWVEC (char, strlen (source_name) + 1
+ + src_name_prefix_len);
+ if (!src_name_prefix)
+ strcpy (main_input_file_name, source_name);
+ else
+ {
+ strcpy (main_input_file_name, src_name_prefix);
+ strcat (main_input_file_name, "/");
+ strcat (main_input_file_name, source_name);
+ }
- /* Name of da file. */
- da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
- + prefix_len + 2);
+ bbg_file_stamp = local_tick;
- if (profile_data_prefix)
+ if (flag_branch_probabilities && !flag_auto_profile)
+ read_counts_file (da_file_name, 0);
+
+ /* Rebuild counts_hash and read the auxiliary GCDA files. */
+ if (flag_profile_use && L_IPO_COMP_MODE)
{
- memcpy (da_file_name, profile_data_prefix, prefix_len);
- da_file_name[prefix_len++] = '/';
+ unsigned i;
+ gcc_assert (flag_dyn_ipa);
+ rebuild_counts_hash ();
+ for (i = 1; i < num_in_fnames; i++)
+ read_counts_file (get_da_file_name (module_infos[i]->da_filename),
+ module_infos[i]->ident);
}
- 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_branch_probabilities)
- read_counts_file ();
+ /* Define variables which are referenced at runtime by libgcov. */
+ if (profiling_enabled_p ())
+ {
+ tree_init_instrumentation ();
+ tree_init_dyn_ipa_parameters ();
+ tree_init_instrumentation_sampling ();
+ }
+ if (flag_auto_profile)
+ init_auto_profile ();
/* Name of bbg file. */
if (flag_test_coverage && !flag_compare_debug)
@@ -1173,7 +2763,6 @@ coverage_init (const char *filename)
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);
@@ -1190,7 +2779,17 @@ coverage_init (const char *filename)
g->get_dumps ()->dump_finish (profile_pass_num);
}
-/* Performs file-level cleanup. Close notes file, generate coverage
+/* Return True if any type of profiling is enabled which requires linking
+ in libgcov otherwise return False. */
+
+static bool
+profiling_enabled_p (void)
+{
+ return profile_arc_flag
+ || flag_profile_generate_sampling;
+}
+
+/* Performs file-level cleanup. Close graph file, generate coverage
variables and constructor. */
void
@@ -1219,4 +2818,158 @@ coverage_finish (void)
da_file_name = NULL;
}
+/* Add S to the end of the string-list, the head and tail of which are
+ pointed-to by HEAD and TAIL, respectively. */
+
+static void
+str_list_append (struct str_list **head, struct str_list **tail, const char *s)
+{
+ struct str_list *e = XNEW (struct str_list);
+ e->str = XNEWVEC (char, strlen (s) + 1);
+ strcpy (e->str, s);
+ e->next = NULL;
+ if (*tail)
+ (*tail)->next = e;
+ else
+ *head = e;
+ *tail = e;
+}
+
+/* Copies the macro def or undef CPP_DEF and saves the copy
+ in a list. IS_DEF is a flag indicating if CPP_DEF represents
+ a -D or -U. */
+
+void
+coverage_note_define (const char *cpp_def, bool is_def)
+{
+ char *s = XNEWVEC (char, strlen (cpp_def) + 2);
+ s[0] = is_def ? 'D' : 'U';
+ strcpy (s + 1, cpp_def);
+ str_list_append (&cpp_defines_head, &cpp_defines_tail, s);
+ num_cpp_defines++;
+}
+
+/* Copies the -imacro/-include FILENAME and saves the copy in a list. */
+
+void
+coverage_note_include (const char *filename)
+{
+ str_list_append (&cpp_includes_head, &cpp_includes_tail, filename);
+ num_cpp_includes++;
+}
+
+/* Mark this module as containing asm statements. */
+
+void
+coverage_has_asm_stmt (void)
+{
+ has_asm_statement = flag_ripa_disallow_asm_modules;
+}
+
+/* Write compilation info to the .note section. */
+
+void
+write_compilation_info_to_asm (void)
+{
+ unsigned lang;
+ /* Write lang, ggc_memory to ASM section. */
+ switch_to_section (get_section (".gnu.switches.text.lipo_info",
+ SECTION_DEBUG, NULL));
+ if (!strcmp (lang_hooks.name, "GNU C"))
+ lang = GCOV_MODULE_C_LANG;
+ else if (!strcmp (lang_hooks.name, "GNU C++"))
+ lang = GCOV_MODULE_CPP_LANG;
+ else
+ lang = GCOV_MODULE_UNKNOWN_LANG;
+ if (has_asm_statement)
+ lang |= GCOV_MODULE_ASM_STMTS;
+ dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
+ dw2_asm_output_data_uleb128 (lang, NULL);
+ dw2_asm_output_data_uleb128 (ggc_total_memory, NULL);
+}
+
+/* Write command line options to the .note section. */
+
+void
+write_compilation_flags_to_asm (void)
+{
+ size_t i;
+ cpp_dir *quote_paths, *bracket_paths, *system_paths, *pdir;
+ struct str_list *pdef, *pinc;
+ int num_quote_paths = 0;
+ int num_bracket_paths = 0;
+ int num_system_paths = 0;
+
+ get_include_chains (&quote_paths, &bracket_paths, &system_paths);
+
+ /* Write quote_paths to ASM section. */
+ switch_to_section (get_section (".gnu.switches.text.quote_paths",
+ SECTION_DEBUG, NULL));
+ for (pdir = quote_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == bracket_paths)
+ break;
+ num_quote_paths++;
+ }
+ dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
+ dw2_asm_output_data_uleb128 (num_quote_paths, NULL);
+ for (pdir = quote_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == bracket_paths)
+ break;
+ dw2_asm_output_nstring (pdir->name, (size_t)-1, NULL);
+ }
+
+ /* Write bracket_paths to ASM section. */
+ switch_to_section (get_section (".gnu.switches.text.bracket_paths",
+ SECTION_DEBUG, NULL));
+ for (pdir = bracket_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == system_paths)
+ break;
+ num_bracket_paths++;
+ }
+ dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
+ dw2_asm_output_data_uleb128 (num_bracket_paths, NULL);
+ for (pdir = bracket_paths; pdir; pdir = pdir->next)
+ {
+ if (pdir == system_paths)
+ break;
+ dw2_asm_output_nstring (pdir->name, (size_t)-1, NULL);
+ }
+
+ /* Write system_paths to ASM section. */
+ switch_to_section (get_section (".gnu.switches.text.system_paths",
+ SECTION_DEBUG, NULL));
+ for (pdir = system_paths; pdir; pdir = pdir->next)
+ num_system_paths++;
+ dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
+ dw2_asm_output_data_uleb128 (num_system_paths, NULL);
+ for (pdir = system_paths; pdir; pdir = pdir->next)
+ dw2_asm_output_nstring (pdir->name, (size_t)-1, NULL);
+
+ /* Write cpp_defines to ASM section. */
+ switch_to_section (get_section (".gnu.switches.text.cpp_defines",
+ SECTION_DEBUG, NULL));
+ dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
+ dw2_asm_output_data_uleb128 (num_cpp_defines, NULL);
+ for (pdef = cpp_defines_head; pdef; pdef = pdef->next)
+ dw2_asm_output_nstring (pdef->str, (size_t)-1, NULL);
+
+ /* Write cpp_includes to ASM section. */
+ switch_to_section (get_section (".gnu.switches.text.cpp_includes",
+ SECTION_DEBUG, NULL));
+ dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
+ dw2_asm_output_data_uleb128 (num_cpp_includes, NULL);
+ for (pinc = cpp_includes_head; pinc; pinc = pinc->next)
+ dw2_asm_output_nstring (pinc->str, (size_t)-1, NULL);
+
+ /* Write cl_args to ASM section. */
+ switch_to_section (get_section (".gnu.switches.text.cl_args",
+ SECTION_DEBUG, NULL));
+ dw2_asm_output_nstring (in_fnames[0], (size_t)-1, NULL);
+ dw2_asm_output_data_uleb128 (num_lipo_cl_args, NULL);
+ for (i = 0; i < num_lipo_cl_args; i++)
+ dw2_asm_output_nstring (lipo_cl_args[i], (size_t)-1, NULL);
+}
#include "gt-coverage.h"