diff options
author | Rong Xu <xur@google.com> | 2014-07-21 16:47:22 -0700 |
---|---|---|
committer | Rong Xu <xur@google.com> | 2014-07-29 15:31:03 -0700 |
commit | 38a8aecfb882072900434499696b5c32a2274515 (patch) | |
tree | 2aac97f0ae24b03cd98c1a06e989c031c173f889 /gcc-4.9/gcc/coverage.c | |
parent | c231900e5dcc14d8296bd9f62b45997a49d4d5e7 (diff) | |
download | toolchain_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.c | 1899 |
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 ("e_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 ("e_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 ("e_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" |