Hi Dhruv,
> On 9 Sep 2025, at 2:02 pm, Dhruv Chawla <[email protected]> wrote:
>
> Hi, there was a bug with patch v2 where DECL_SOURCE_FILE wouldn't always
> match the basename of the source file. This is because DECL_SOURCE_FILE
> contains the path provided to the frontend as is, without any conversion
> to a full path or just the basename. Fixed by using lbasename () on
> DECL_SOURCE_FILE.
>
> I have attached a version of this patch that fixes this issue and tests it
> in the testcases.
>
> --
> Regards,
> Dhruv
>
> -- >8 --
>
> From aaee1c2fd1d99e74c4ad7f3af30f2a9c71aa4b62 Mon Sep 17 00:00:00 2001
> From: Dhruv Chawla <[email protected]>
> Date: Sun, 10 Aug 2025 22:27:37 -0700
> Subject: [PATCH] [AutoFDO] Source filename tracking in GCOV
>
> This patch is a respin of the RFC originally posted at
> https://gcc.gnu.org/pipermail/gcc-patches/2025-June/686835.html.
>
> The patch reads the file names from the GCOV file and compares them
> against DECL_SOURCE_FILE for symbols to decide which profile count to
> annotate them with.
>
> The primary decision is around choosing the file name when there are
> clashes for the symbol names with suffixes removed. For example, which
> filename is to be chosen for symbol 'effect' when there exist
> 'effect.lto_priv.0' and 'effect.lto_priv.1'? For this, the symbols in
> the current TU named 'effect' are looked up and compared against the function
> instances. The one whose file name matches is used for annotation.
>
> The patch also updates the string table reader to strip suffixes when
> reading the GCOV file instead of during offline_external_functions ().
> This allows tracking clashing names more efficiently.
>
> Autoprofilebootstrapped and regtested on aarch64-linux-gnu.
>
> Signed-off-by: Dhruv Chawla <[email protected]>
>
> gcc/ChangeLog:
>
> * auto-profile.cc (AUTO_PROFILE_VERSION): Bump to 3.
> (string_table::~string_table): Free filenames as well.
> (string_table::get_filename): New function.
> (string_table::get_filename_idx): Likewise.
> (string_table::get_idx_for_file): Likewise.
> (string_table::get_original_name): Likewise.
> (string_table::add_name): Use filename index as well.
> (string_table::add_filename): New function.
> (string_table::read): Read file names and strip symbol suffixes as
> well.
> (function_instance::merge): Bail if filenames don't match.
> (function_instance::offline): Handle filenames for symbol lookup.
> (match_with_target): Likewise.
> (autofdo_source_profile::offline_external_functions): Likewise.
> Also update to call find_function_instance,
> add_function_instance and remove_function_instance.
> (autofdo_source_profile::offline_unrealized_inlines): Likewise.
> (function_instance::read_function_instance): Lookup filename
> index while reading.
> (autofdo_source_profile::~autofdo_source_profile): Handle
> filenames.
> (autofdo_source_profile::get_function_instance_by_decl):
> Likewise.
> (autofdo_source_profile::get_function_instance_by_name_index):
> Update to delegate to find_function_instance.
> (autofdo_source_profile::get_function_instance_by_inline_stack):
> Handle filenames.
> (autofdo_source_profile::add_function_instance): Likewise.
> (autofdo_source_profile::read): Likewise.
> (autofdo_source_profile::find_function_instance): New function.
> (autofdo_source_profile::remove_function_instance): Likewise.
>
> gcc/c/ChangeLog:
>
> * Make-lang.in: Bump GCOV version passed to create_gcov to 3.
>
> gcc/cp/ChangeLog:
>
> * Make-lang.in: Bump GCOV version passed to create_gcov to 3.
>
> gcc/lto/ChangeLog:
>
> * Make-lang.in: Bump GCOV version passed to create_gcov to 3.
>
> gcc/testsuite/ChangeLog:
>
> * lib/profopt.exp: Bump GCOV version passed to create_gcov to 3.
> * gcc.dg/tree-prof/afdo-lto_priv-basic-0.c: New test.
> * gcc.dg/tree-prof/afdo-lto_priv-basic-1.c: Likewise.
> * gcc.dg/tree-prof/afdo-lto_priv-header-0.c: Likewise.
> * gcc.dg/tree-prof/afdo-lto_priv-header-0.h: Likewise.
> * gcc.dg/tree-prof/afdo-lto_priv-header-1.c: Likewise.
> * gcc.dg/tree-prof/afdo-lto_priv-header-1.h: Likewise.
> ---
> gcc/auto-profile.cc | 403 ++++++++++++++----
> gcc/c/Make-lang.in | 4 +-
> gcc/cp/Make-lang.in | 4 +-
> gcc/lto/Make-lang.in | 4 +-
> .../gcc.dg/tree-prof/afdo-lto_priv-basic-0.c | 47 ++
> .../gcc.dg/tree-prof/afdo-lto_priv-basic-1.c | 20 +
> .../gcc.dg/tree-prof/afdo-lto_priv-header-0.c | 47 ++
> .../gcc.dg/tree-prof/afdo-lto_priv-header-0.h | 3 +
> .../gcc.dg/tree-prof/afdo-lto_priv-header-1.c | 18 +
> .../gcc.dg/tree-prof/afdo-lto_priv-header-1.h | 3 +
> gcc/testsuite/lib/profopt.exp | 2 +-
> 11 files changed, 471 insertions(+), 84 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-0.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-1.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.h
> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.h
>
> diff --git a/gcc/auto-profile.cc b/gcc/auto-profile.cc
> index ce607a68d2e..189f98d683e 100644
> --- a/gcc/auto-profile.cc
> +++ b/gcc/auto-profile.cc
> @@ -122,7 +122,7 @@ along with GCC; see the file COPYING3. If not see
> */
> #define DEFAULT_AUTO_PROFILE_FILE "fbdata.afdo"
> -#define AUTO_PROFILE_VERSION 2
> +#define AUTO_PROFILE_VERSION 3
> /* profile counts determined by AFDO smaller than afdo_hot_bb_threshold are
> considered cols. */
> @@ -170,7 +170,7 @@ struct decl_lineno
> typedef auto_vec<decl_lineno, 20> inline_stack;
> /* String array that stores function names. */
> -typedef auto_vec<char *> string_vector;
> +typedef auto_vec<const char *> string_vector;
> /* Map from function name's index in string_table to target's
> execution count. */
> @@ -228,6 +228,23 @@ public:
> /* For a given index, returns the string. */
> const char *get_name (int index) const;
> + /* For a given index, returns the string. */
> + const char *get_filename (int index) const;
> +
> + /* For a given function index, returns the index. */
> + int get_filename_idx (int index) const;
> +
> + /* For a given function name, returns the index. */
> + int get_filename_idx (const char *name) const;
> +
> + /* For a given filename, returns the index. */
> + int get_idx_for_file (const char *name) const;
> +
> + /* Get the original name and file name index for a node. This will return
> the
> + name from the current TU if there are multiple symbols that map to
> + NAME. */
> + std::pair<const char *, int> get_original_name (const char *name) const;
> +
> /* Read profile, return TRUE on success. */
> bool read ();
> @@ -238,14 +255,26 @@ public:
> }
> /* Add new name and return its index. */
> - int add_name (char *);
> + int add_name (const char *string, int filename_idx);
> +
> + /* Add new filename and return its index (returning the same if it already
> + * exists). */
> + int add_filename (const char *name);
> /* Return cgraph node corresponding to given name index. */
> cgraph_node *get_cgraph_node (int);
> private:
> typedef std::map<const char *, unsigned, string_compare> string_index_map;
> + typedef std::map<const char *, auto_vec<unsigned>, string_compare>
> + string_names_map;
> + typedef std::map<const char *, char *, string_compare> string_string_map;
> string_vector vector_;
> string_index_map map_;
> + string_vector filenames_;
> + string_index_map filenames_map_;
> + string_index_map name_filenames_map_;
> + string_string_map original_names_map_;
> + string_names_map clashing_names_;
> };
> /* Profile of a function instance:
> @@ -276,6 +305,12 @@ public:
> {
> return name_;
> }
> +
> + int file_name () const
> + {
> + return file_name_;
> + }
> +
> int
> set_name (int index)
> {
> @@ -454,10 +489,10 @@ private:
> /* Map from callsite to callee function_instance. */
> typedef std::map<callsite, function_instance *> callsite_map;
> - function_instance (unsigned name, gcov_type head_count)
> - : name_ (name), total_count_ (0), head_count_ (head_count),
> - removed_icall_target_ (false), realized_ (false),
> - in_worklist_ (false), inlined_to_ (NULL),
> + function_instance (unsigned name, unsigned file_name, gcov_type head_count)
> + : name_ (name), file_name_ (file_name), total_count_ (0),
> + head_count_ (head_count), removed_icall_target_ (false),
> + realized_ (false), in_worklist_ (false), inlined_to_ (NULL),
> location_ (UNKNOWN_LOCATION), call_location_ (UNKNOWN_LOCATION)
> {
> }
> @@ -468,6 +503,9 @@ private:
> /* function_instance name index in the string_table. */
> unsigned name_;
> + /* The file that the function_instance comes from. */
> + unsigned file_name_;
> +
> /* Total sample count. */
> gcov_type total_count_;
> @@ -523,8 +561,9 @@ public:
> /* For a given DECL, returns the top-level function_instance. */
> function_instance *get_function_instance_by_decl (tree decl) const;
> - /* For a given name index, returns the top-level function_instance. */
> - function_instance *get_function_instance_by_name_index (int) const;
> + /* For a given name index (and original name), returns the top-level
> + * function_instance. */
> + function_instance *get_function_instance_by_name_index (int, int) const;
> void add_function_instance (function_instance *);
> @@ -555,9 +594,10 @@ public:
> void offline_unrealized_inlines ();
> private:
> - /* Map from function_instance name index (in string_table) to
> - function_instance. */
> - typedef std::map<unsigned, function_instance *> name_function_instance_map;
> + /* Map from function_instance file index (in string_table) to name index
> (in
> + string_table) to function_instance. */
> + typedef std::map<unsigned, std::map<unsigned, function_instance *>>
> + name_function_instance_map;
> autofdo_source_profile () {}
> @@ -569,6 +609,15 @@ private:
> function_instance *
> get_function_instance_by_inline_stack (const inline_stack &stack) const;
> + /* Find the matching function instance which has FUN_ID as its name and
> + FILE_ID as the filename. */
> + function_instance *find_function_instance (unsigned fun_id,
> + unsigned file_id) const;
> +
> + /* Remove a function instance from the map. This automatically handles the
> + filename lookup. Returns true if the entry was actually deleted. */
> + bool remove_function_instance (function_instance *inst);
> +
> name_function_instance_map map_;
> auto_vec <function_instance *> duplicate_functions_;
> @@ -592,7 +641,6 @@ static gcov_type afdo_count_scale = 1;
> /* Helper functions. */
> -
> /* Return the original name of NAME: strip the suffix that starts
> with '.' for names that are generetad after auto-profile pass.
> This is to match profiled names with the names in the IR at this stage.
> @@ -815,7 +863,12 @@ get_relative_location_for_stmt (tree fn, gimple *stmt)
> string_table::~string_table ()
> {
> for (unsigned i = 0; i < vector_.length (); i++)
> - free (vector_[i]);
> + free (const_cast<char *> (vector_[i]));
> + for (unsigned i = 0; i < filenames_.length (); i++)
> + free (const_cast<char *> (filenames_[i]));
> + for (auto it = original_names_map_.begin (); it != original_names_map_.end
> ();
> + it++)
> + free (it->second);
> }
> @@ -859,16 +912,116 @@ string_table::get_name (int index) const
> return vector_[index];
> }
> +/* For a given index, returns the string. */
> +
> +const char *
> +string_table::get_filename (int index) const
> +{
> + /* There may not be any file name for some functions, ignore them. */
> + if (index == -1)
> + return "<unknown>";
> + gcc_assert (index >= 0 && index < (int) filenames_.length ());
> + return filenames_[index];
> +}
> +
> +/* For a given function index, returns the index. */
> +
> +int
> +string_table::get_filename_idx (int index) const
> +{
> + return get_filename_idx (get_name (index));
> +}
> +
> +/* For a given function name, returns the index. */
> +
> +int
> +string_table::get_filename_idx (const char *name) const
> +{
> + auto it = name_filenames_map_.find (name);
> + if (it != name_filenames_map_.end () && it->second < filenames_.length ())
> + return it->second;
> + return -1;
> +}
> +
> +/* For a given filename, returns the index. */
> +int
> +string_table::get_idx_for_file (const char *name) const
> +{
> + if (auto it = filenames_map_.find (name); it != filenames_map_.end ())
> + return it->second;
> + return -1;
> +}
> +
> +/* Get the original name for a node. This will return the name from the
> + current TU if there are multiple symbols that map to NAME. */
> +
> +std::pair<const char *, int>
> +string_table::get_original_name (const char *name) const
> +{
> + /* Check if the un-prefixed name differs from the actual name. */
> + auto stripped = original_names_map_.find (name);
> +
> + /* The original name for the symbol is its name, i.e. there are no
> + suffixes. */
> + if (stripped == original_names_map_.end ())
> + return {name, get_filename_idx (name)};
> +
> + /* Figure out if a clash exists. */
> + auto clash = clashing_names_.find (stripped->second);
> + gcc_assert (clash != clashing_names_.end ());
> +
> + /* Try to find a function from the current TU. */
> + gcc_assert (clash->second.length () >= 1);
> + if (symtab_node *n
> + = cgraph_node::get_for_asmname (get_identifier (stripped->second));
> + n && is_a<cgraph_node *> (n))
> + for (cgraph_node *cn = dyn_cast<cgraph_node *> (n); cn;)
> + {
> + /* Check if there is a symbol in the current TU that has the same name
> + as in the GCOV. */
> + for (auto name : clash->second)
> + {
> + int filename_idx = get_filename_idx (name);
> + if (cn->definition && cn->has_gimple_body_p ()
> + && !strcmp (lbasename (DECL_SOURCE_FILE (cn->decl)),
> + get_filename (filename_idx)))
> + return {stripped->second, filename_idx};
> + }
In some rare cases we can match multiple but we will return the first. AFIK,
this should not be common but can we add a -Wauto-profile warning in this case.
> + cn = dyn_cast<cgraph_node *> (cn->next_sharing_asm_name);
> + }
> +
> + /* No match found. Just stick to the current symbol and return the
> stripped
> + name. */
> + return {stripped->second, get_filename_idx (name)};
> +}
> +
> /* Add new name SRRING and return its index. */
> int
> -string_table::add_name (char *string)
> +string_table::add_name (const char *string, int filename_idx)
> {
> + gcc_assert (
> + filename_idx == -1
> + || (filename_idx >= 0 && filename_idx < (int) filenames_.length ()));
> vector_.safe_push (string);
> map_[vector_.last ()] = vector_.length () - 1;
> + name_filenames_map_[vector_.last ()] = filename_idx;
> return vector_.length () - 1;
> }
> +/* Add new filename and return its index (returning the same if it already
> + * exists). */
> +
> +int
> +string_table::add_filename (const char *name)
> +{
> + for (unsigned i = 0; i < filenames_.length (); i++)
> + if (!strcmp (name, filenames_[i]))
> + return i;
This is linear search. Can we use the map:
auto it = filenames_map_.find (name);
if (it != filenames_map_.end ())
return it->second;
> + filenames_.safe_push (xstrdup (name));
> + return filenames_.length () - 1;
> +}
> +
> /* Read the string table. Return TRUE if reading is successful. */
> bool
> @@ -879,12 +1032,42 @@ string_table::read ()
> /* Skip the length of the section. */
> gcov_read_unsigned ();
> /* Read in the file name table. */
> + unsigned file_num = gcov_read_unsigned ();
> + filenames_.reserve (file_num);
> + for (unsigned i = 0; i < file_num; i++)
> + {
> + filenames_.quick_push (xstrdup (gcov_read_string ()));
For vector_ you removed the xstrdup. Here you are still keeping it. This could
be leaking memory.
> + filenames_map_[filenames_.last ()] = i;
> + if (gcov_is_error ())
> + return false;
> + }
> + /* Read in the function name -> file name table. */
> unsigned string_num = gcov_read_unsigned ();
> vector_.reserve (string_num);
> for (unsigned i = 0; i < string_num; i++)
> {
> - vector_.quick_push (xstrdup (gcov_read_string ()));
> + vector_.quick_push (const_cast<char *> (gcov_read_string ()));.
> map_[vector_.last ()] = i;
> + unsigned filename_idx = gcov_read_unsigned ();
> + name_filenames_map_[vector_.last ()] = filename_idx;
> + char *original
> + = const_cast<char *> (autofdo::get_original_name (vector_.last ()));
> + if (strcmp (original, vector_.last ()))
> + {
> + /* Take ownership of ORIGINAL. */
> + original_names_map_[vector_.last ()] = original;
> + clashing_names_[original].safe_push (i);
> + /* It is possible that a public symbol with the stripped name exists.
> + If it does exist, add it as well. */
> + auto publik = map_.find (original);
> + if (publik != map_.end ()
> + && clashing_names_.find (publik->first) == clashing_names_.end ())
> + clashing_names_[publik->first].safe_push (publik->second);
> + }
> + else
> + /* There are no suffixes to remove. */
> + free (original);
> +
> if (gcov_is_error ())
> return false;
> }
> @@ -974,6 +1157,10 @@ function_instance::merge (function_instance *other,
> {
> /* Do not merge to itself and only merge functions of same name. */
> gcc_checking_assert (other != this && other->name () == name ());
> +
> + if (file_name () != other->file_name ())
> + return;
> +
> total_count_ += other->total_count_;
> if (other->total_count () && total_count () && other->head_count () == -1)
> head_count_ = -1;
> @@ -1093,7 +1280,8 @@ function_instance::offline (function_instance *fn,
> gcc_checking_assert (s->total_count_ >= 0);
> }
> function_instance *to
> - = afdo_source_profile->get_function_instance_by_name_index (fn->name ());
> + = afdo_source_profile->get_function_instance_by_name_index (
> + fn->name (), fn->file_name ());
> fn->set_inlined_to (NULL);
> /* If there is offline function of same name, we need to merge profile.
> Delay this by adding function to a worklist so we do not run into
> @@ -1188,7 +1376,10 @@ match_with_target (cgraph_node *n,
> {
> int index = afdo_string_table->get_index (symbol_name);
> if (index == -1)
> - index = afdo_string_table->add_name (xstrdup (symbol_name));
> + index = afdo_string_table->add_name (
> + xstrdup (symbol_name),
> + afdo_string_table->add_filename (
> + lbasename (DECL_SOURCE_FILE (callee->decl))));
I think DECL_SOURCE_FILE(decl) can return NULL.
Thanks,
Kugan
> if (dump_file)
> fprintf (dump_file,
> " Renaming inlined call target %s to %s\n",
> @@ -1976,10 +2167,11 @@ autofdo_source_profile::offline_external_functions ()
> for (size_t i = 1; i < afdo_string_table->num_entries (); i++)
> {
> const char *n1 = afdo_string_table->get_name (i);
> - char *n2 = get_original_name (n1);
> + std::pair<const char *, int> name_filename
> + = afdo_string_table->get_original_name (n1);
> + const char *n2 = name_filename.first;
> if (!strcmp (n1, n2))
> {
> - free (n2);
> /* Watch for duplicate entries.
> This seems to happen in practice and may be useful to distingush
> multiple static symbols of the same name, but we do not realy
> @@ -1999,10 +2191,9 @@ autofdo_source_profile::offline_external_functions ()
> fprintf (dump_file, "Adding rename removing clone suffxes %s -> %s\n",
> n1, n2);
> int index = afdo_string_table->get_index (n2);
> - if (index != -1)
> - free (n2);
> - else
> - index = afdo_string_table->add_name (n2);
> + if (index == -1)
> + index
> + = afdo_string_table->add_name (xstrdup (n2), name_filename.second);
> to_symbol_name.put (i, index);
> }
> last_name = afdo_string_table->num_entries ();
> @@ -2021,7 +2212,9 @@ autofdo_source_profile::offline_external_functions ()
> if (index2 != -1)
> {
> if (index == -1)
> - index = afdo_string_table->add_name (xstrdup (name));
> + index = afdo_string_table->add_name (
> + xstrdup (name), afdo_string_table->add_filename (
> + lbasename (DECL_SOURCE_FILE (node->decl))));
> if (dump_file)
> {
> fprintf (dump_file, "Adding dwarf->symbol rename %s -> %s\n",
> @@ -2088,11 +2281,12 @@ autofdo_source_profile::offline_external_functions ()
> auto_vec <function_instance *, 20>fns2;
> /* Poppulate worklist with all functions to process. Processing
> may introduce new functions by offlining. */
> - for (auto const &iter : map_)
> - {
> - iter.second->set_in_worklist ();
> - fns.safe_push (iter.second);
> - }
> + for (auto const &file: map_)
> + for (auto const &function: file.second)
> + {
> + function.second->set_in_worklist ();
> + fns.safe_push (function.second);
> + }
> /* There are two worklists. First all functions needs to be matched
> with gimple body and only then we want to do merging, since matching
> @@ -2122,12 +2316,11 @@ autofdo_source_profile::offline_external_functions ()
> int *newn = to_symbol_name.get (index);
> if (newn)
> {
> + if (find_function_instance (index, f->file_name ()) == f)
> + remove_function_instance (f);
> f->set_name (*newn);
> - if (map_.count (index)
> - && map_[index] == f)
> - map_.erase (index);
> - if (!map_.count (*newn))
> - map_[*newn] = f;
> + if (!find_function_instance (*newn, f->file_name ()))
> + add_function_instance (f);
> }
> if (cgraph_node *n = f->get_cgraph_node ())
> {
> @@ -2145,7 +2338,9 @@ autofdo_source_profile::offline_external_functions ()
> /* If map has different function_instance of same name, then
> this is a duplicated entry which needs to be merged. */
> - if (map_.count (index) && map_[index] != f)
> + function_instance *index_inst
> + = find_function_instance (index, f->file_name ());
> + if (index_inst && index_inst != f)
> {
> if (dump_file)
> {
> @@ -2153,7 +2348,7 @@ autofdo_source_profile::offline_external_functions ()
> f->dump_inline_stack (dump_file);
> fprintf (dump_file, "\n");
> }
> - map_[index]->merge (f, fns);
> + index_inst->merge (f, fns);
> gcc_checking_assert (!f->inlined_to ());
> f->clear_in_worklist ();
> delete f;
> @@ -2166,8 +2361,8 @@ autofdo_source_profile::offline_external_functions ()
> if (dump_file)
> fprintf (dump_file, "Removing external %s\n",
> afdo_string_table->get_name (f->name ()));
> - if (map_.count (index) && map_[index] == f)
> - map_.erase (f->name ());
> + if (index_inst == f)
> + remove_function_instance (f);
> delete f;
> }
> /* If this is offline function instance seen in this
> @@ -2181,11 +2376,12 @@ autofdo_source_profile::offline_external_functions ()
> }
> }
> if (dump_file)
> - for (auto const &iter : map_)
> - {
> - seen.contains (iter.second->name ());
> - iter.second->dump (dump_file);
> - }
> + for (auto const &file: map_)
> + for (auto const &function: file.second)
> + {
> + seen.contains (function.second->name ());
> + function.second->dump (dump_file);
> + }
> }
> /* Walk scope block BLOCK and mark all inlined functions as realized. */
> @@ -2238,16 +2434,19 @@ autofdo_source_profile::offline_unrealized_inlines ()
> auto_vec <function_instance *>fns;
> /* Poppulate worklist with all functions to process. Processing
> may introduce new functions by offlining. */
> - for (auto const &iter : map_)
> - {
> - fns.safe_push (iter.second);
> - iter.second->set_in_worklist ();
> - }
> + for (auto const &file: map_)
> + for (auto const &function: file.second)
> + {
> + fns.safe_push (function.second);
> + function.second->set_in_worklist ();
> + }
> while (fns.length ())
> {
> function_instance *f = fns.pop ();
> int index = f->name ();
> - bool in_map = map_.count (index);
> + function_instance *index_inst
> + = find_function_instance (index, f->file_name ());
> + bool in_map = index_inst != nullptr;
> if (in_map)
> if (cgraph_node *n = f->get_cgraph_node ())
> {
> @@ -2264,7 +2463,7 @@ autofdo_source_profile::offline_unrealized_inlines ()
> && f->in_worklist_p ());
> /* If this is duplicated instance, merge it into one in map. */
> - if (in_map && map_[index] != f)
> + if (in_map && index_inst != f)
> {
> if (dump_file)
> {
> @@ -2272,7 +2471,7 @@ autofdo_source_profile::offline_unrealized_inlines ()
> f->dump_inline_stack (dump_file);
> fprintf (dump_file, "\n");
> }
> - map_[index]->merge (f, fns);
> + index_inst->merge (f, fns);
> f->clear_in_worklist ();
> gcc_checking_assert (!f->inlined_to ());
> delete f;
> @@ -2283,7 +2482,7 @@ autofdo_source_profile::offline_unrealized_inlines ()
> if (dump_file)
> fprintf (dump_file, "Removing optimized out function %s\n",
> afdo_string_table->get_name (f->name ()));
> - map_.erase (index);
> + remove_function_instance (index_inst);
> f->clear_in_worklist ();
> delete f;
> }
> @@ -2291,8 +2490,9 @@ autofdo_source_profile::offline_unrealized_inlines ()
> f->clear_in_worklist ();
> }
> if (dump_file)
> - for (auto const &iter : map_)
> - iter.second->dump (dump_file);
> + for (auto const &file: map_)
> + for (auto const &function: file.second)
> + function.second->dump (dump_file);
> }
> /* Read the profile and create a function_instance with head count as
> @@ -2333,7 +2533,9 @@ function_instance::read_function_instance
> (function_instance_stack *stack,
> unsigned name = gcov_read_unsigned ();
> unsigned num_pos_counts = gcov_read_unsigned ();
> unsigned num_callsites = gcov_read_unsigned ();
> - function_instance *s = new function_instance (name, head_count);
> + function_instance *s
> + = new function_instance (name, afdo_string_table->get_filename_idx
> (name),
> + head_count);
> if (!stack->is_empty ())
> s->set_inlined_to (stack->last ());
> stack->safe_push (s);
> @@ -2373,9 +2575,11 @@ function_instance::read_function_instance
> (function_instance_stack *stack,
> autofdo_source_profile::~autofdo_source_profile ()
> {
> - for (name_function_instance_map::const_iterator iter = map_.begin ();
> - iter != map_.end (); ++iter)
> - delete iter->second;
> + for (name_function_instance_map::const_iterator it = map_.begin ();
> + it != map_.end (); ++it)
> + for (decltype (it->second)::const_iterator iter = it->second.begin ();
> + iter != it->second.end (); ++iter)
> + delete iter->second;
> }
> /* For a given DECL, returns the top-level function_instance. */
> @@ -2383,21 +2587,25 @@ autofdo_source_profile::~autofdo_source_profile ()
> function_instance *
> autofdo_source_profile::get_function_instance_by_decl (tree decl) const
> {
> + const char *filename = lbasename (DECL_SOURCE_FILE (decl));
> int index = afdo_string_table->get_index_by_decl (decl);
> if (index == -1)
> return NULL;
> - name_function_instance_map::const_iterator ret = map_.find (index);
> - return ret == map_.end () ? NULL : ret->second;
> + name_function_instance_map::const_iterator ret
> + = map_.find (afdo_string_table->get_idx_for_file (filename));
> + if (ret == map_.end ())
> + return NULL;
> + auto decl_inst = ret->second.find (index);
> + return decl_inst == ret->second.end () ? NULL : decl_inst->second;
> }
> /* For a given NAME_INDEX, returns the top-level function_instance. */
> function_instance *
> -autofdo_source_profile::get_function_instance_by_name_index (int name_index)
> - const
> +autofdo_source_profile::get_function_instance_by_name_index (
> + int name_index, int file_name) const
> {
> - name_function_instance_map::const_iterator ret = map_.find (name_index);
> - return ret == map_.end () ? NULL : ret->second;
> + return find_function_instance (name_index, file_name);
> }
> /* Add function instance FN. */
> @@ -2405,9 +2613,10 @@
> autofdo_source_profile::get_function_instance_by_name_index (int name_index)
> void
> autofdo_source_profile::add_function_instance (function_instance *fn)
> {
> + auto &inner_map = map_[fn->file_name ()];
> int index = fn->name ();
> - gcc_checking_assert (map_.count (index) == 0);
> - map_[index] = fn;
> + gcc_checking_assert (inner_map.count (index) == 0);
> + inner_map[index] = fn;
> }
> /* Find count_info for a given gimple STMT. If found, store the count_info
> @@ -2610,13 +2819,12 @@ autofdo_source_profile::read ()
> for (unsigned i = 0; i < function_num; i++)
> {
> function_instance::function_instance_stack stack;
> - function_instance *s = function_instance::read_function_instance (
> - &stack, gcov_read_counter ());
> - int fun_id = s->name ();
> - /* If function_instace with get_original_name (without the clone
> - suffix) exixts, merge the function instances. */
> - if (map_.count (fun_id) == 0)
> - map_[fun_id] = s;
> + function_instance *s
> + = function_instance::read_function_instance (&stack,
> + gcov_read_counter ());
> +
> + if (find_function_instance (s->name (), s->file_name ()) == nullptr)
> + add_function_instance (s);
> else
> fatal_error (UNKNOWN_LOCATION,
> "auto-profile contains duplicated function instance %s",
> @@ -2657,9 +2865,14 @@ function_instance *
> autofdo_source_profile::get_function_instance_by_inline_stack (
> const inline_stack &stack) const
> {
> - name_function_instance_map::const_iterator iter = map_.find (
> - afdo_string_table->get_index_by_decl (stack[stack.length () -
> 1].decl));
> - if (iter == map_.end ())
> + name_function_instance_map::const_iterator it
> + = map_.find (afdo_string_table->get_idx_for_file (
> + lbasename (DECL_SOURCE_FILE (stack[stack.length () - 1].decl))));
> + if (it == map_.end ())
> + return NULL;
> + auto iter = it->second.find (
> + afdo_string_table->get_index_by_decl (stack[stack.length () - 1].decl));
> + if (iter == it->second.end ())
> {
> if (dump_file)
> fprintf (dump_file, "No offline instance for %s\n",
> @@ -2694,6 +2907,42 @@
> autofdo_source_profile::get_function_instance_by_inline_stack (
> return s;
> }
> +/* Find the matching function instance which has FUN_ID as its name. */
> +
> +function_instance *
> +autofdo_source_profile::find_function_instance (unsigned fun_id,
> + unsigned file_name) const
> +{
> + auto it = map_.find (file_name);
> + if (it == map_.end ())
> + return NULL;
> + auto fun = it->second.find (fun_id);
> + if (fun == it->second.end ())
> + return NULL;
> + return fun->second;
> +}
> +
> +/* Remove a function instance from the map. This automatically handles the
> + filename lookup. Returns true if the entry was actually deleted. */
> +
> +bool
> +autofdo_source_profile::remove_function_instance (function_instance *inst)
> +{
> + auto it = map_.find (inst->file_name ());
> + if (it == map_.end ())
> + return false;
> +
> + auto fun = it->second.find (inst->name ());
> + if (fun == it->second.end ())
> + return false;
> +
> + it->second.erase (inst->name ());
> + if (it->second.empty ())
> + map_.erase (inst->file_name ());
> +
> + return true;
> +}
> +
> /* Module profile is only used by LIPO. Here we simply ignore it. */
> static void
> diff --git a/gcc/c/Make-lang.in b/gcc/c/Make-lang.in
> index 2517b64439f..1efe3115602 100644
> --- a/gcc/c/Make-lang.in
> +++ b/gcc/c/Make-lang.in
> @@ -101,7 +101,7 @@ create_fdas_for_cc1: ../stage1-gcc/cc1$(exeext)
> ../prev-gcc/$(PERF_DATA)
> echo $$perf_path; \
> if [ -f $$perf_path ]; then \
> profile_name=cc1_$$component_in_prev.fda; \
> - $(CREATE_GCOV) -binary ../stage1-gcc/cc1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 2; \
> + $(CREATE_GCOV) -binary ../stage1-gcc/cc1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 3; \
> fi; \
> done;
> @@ -111,7 +111,7 @@ create_fdas_for_cc1: ../stage1-gcc/cc1$(exeext)
> ../prev-gcc/$(PERF_DATA)
> echo $$perf_path; \
> if [ -f $$perf_path ]; then \
> profile_name=cc1_$$component_in_prev_target.fda; \
> - $(CREATE_GCOV) -binary ../prev-gcc/cc1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 2; \
> + $(CREATE_GCOV) -binary ../prev-gcc/cc1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 3; \
> fi; \
> done;
> diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
> index dae3c6846e0..15ca4dd1d70 100644
> --- a/gcc/cp/Make-lang.in
> +++ b/gcc/cp/Make-lang.in
> @@ -199,7 +199,7 @@ create_fdas_for_cc1plus: ../stage1-gcc/cc1plus$(exeext)
> ../prev-gcc/$(PERF_DATA)
> echo $$perf_path; \
> if [ -f $$perf_path ]; then \
> profile_name=cc1plus_$$component_in_prev.fda; \
> - $(CREATE_GCOV) -binary ../stage1-gcc/cc1plus$(exeext) -gcov
> $$profile_name -profile $$perf_path -gcov_version 2; \
> + $(CREATE_GCOV) -binary ../stage1-gcc/cc1plus$(exeext) -gcov
> $$profile_name -profile $$perf_path -gcov_version 3; \
> fi; \
> done;
> @@ -209,7 +209,7 @@ create_fdas_for_cc1plus: ../stage1-gcc/cc1plus$(exeext)
> ../prev-gcc/$(PERF_DATA)
> echo $$perf_path; \
> if [ -f $$perf_path ]; then \
> profile_name=cc1plus_$$component_in_prev_target.fda; \
> - $(CREATE_GCOV) -binary ../prev-gcc/cc1plus$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 2; \
> + $(CREATE_GCOV) -binary ../prev-gcc/cc1plus$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 3; \
> fi; \
> done;
> diff --git a/gcc/lto/Make-lang.in b/gcc/lto/Make-lang.in
> index 553e6ddd0d2..baa3bd4a4ab 100644
> --- a/gcc/lto/Make-lang.in
> +++ b/gcc/lto/Make-lang.in
> @@ -118,7 +118,7 @@ create_fdas_for_lto1: ../stage1-gcc/lto1$(exeext)
> ../prev-gcc/$(PERF_DATA)
> echo $$perf_path; \
> if [ -f $$perf_path ]; then \
> profile_name=lto1_$$component_in_prev.fda; \
> - $(CREATE_GCOV) -binary ../stage1-gcc/lto1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 2; \
> + $(CREATE_GCOV) -binary ../stage1-gcc/lto1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 3; \
> fi; \
> done;
> @@ -128,7 +128,7 @@ create_fdas_for_lto1: ../stage1-gcc/lto1$(exeext)
> ../prev-gcc/$(PERF_DATA)
> echo $$perf_path; \
> if [ -f $$perf_path ]; then \
> profile_name=lto1_$$component_in_prev_target.fda; \
> - $(CREATE_GCOV) -binary ../prev-gcc/lto1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 2; \
> + $(CREATE_GCOV) -binary ../prev-gcc/lto1$(exeext) -gcov $$profile_name
> -profile $$perf_path -gcov_version 3; \
> fi; \
> done;
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-0.c
> b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-0.c
> new file mode 100644
> index 00000000000..551e5a8a425
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-0.c
> @@ -0,0 +1,47 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "afdo-lto_priv-basic-1.c" } */
> +/* { dg-options "-O2 -flto -fdump-ipa-afdo" } */
> +/* { dg-require-profiling "-fauto-profile" } */
> +
> +#define TRIP 1000000000
> +
> +/* Check against exported symbols. */
> +__attribute__ ((noinline, noipa)) void effect_1 () {}
> +__attribute__ ((noinline, noipa)) void effect_2 () {}
> +__attribute__ ((noinline, noipa)) static int foo () { return 5; }
> +
> +/* Prevent GCC from optimizing the loop. */
> +__attribute__ ((noinline, noipa)) int
> +use (int x)
> +{
> + volatile int y = x;
> + return x;
> +}
> +
> +extern void global ();
> +
> +int
> +main ()
> +{
> + for (int i = 0; i < TRIP; i++)
> + {
> + /* Call only 50% of the time. */
> + if (use (i) < TRIP / 2)
> + global ();
> +
> + if (foo () < 5)
> + /* This function is never called. */
> + effect_1 ();
> + else
> + effect_2 ();
> + }
> +}
> +
> +/* Check that the annotation actually occurs. */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for main"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for use" afdo
> } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for foo" afdo
> } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for effect_2"
> afdo } } */
> +
> +/* There should be no collision with effect_1 from afdo-lto_priv-basic-1.c.
> */
> +/* { dg-final-use-autofdo { scan-ipa-dump "No afdo profile for effect_1"
> afdo } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-1.c
> b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-1.c
> new file mode 100644
> index 00000000000..38601d1a38e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-1.c
> @@ -0,0 +1,20 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "afdo-lto_priv-basic-0.c" } */
> +/* { dg-options "-O2 -flto -fdump-ipa-afdo" } */
> +/* { dg-require-profiling "-fauto-profile" } */
> +
> +__attribute__((noinline, noipa)) static void do_nothing() {}
> +__attribute__((noinline, noipa)) static void effect_1() { do_nothing(); }
> +__attribute__((noinline, noipa)) static void effect_2() { do_nothing(); }
> +
> +void global()
> +{
> + effect_1();
> + effect_2();
> +}
> +
> +/* Check that the annotation actually occurs. */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for global"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for effect_2"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for effect_1"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for
> do_nothing" afdo } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.c
> b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.c
> new file mode 100644
> index 00000000000..b30412284c6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.c
> @@ -0,0 +1,47 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "afdo-lto_priv-header-1.c" } */
> +/* { dg-options "-O2 -flto -fdump-ipa-afdo" } */
> +/* { dg-require-profiling "-fauto-profile" } */
> +
> +/* Verify that symbols included from headers get their file names set and
> + compared correctly. */
> +
> +#define TRIP 1000000000
> +
> +#include "afdo-lto_priv-header-0.h"
> +
> +/* Prevent GCC from optimizing the loop. */
> +__attribute__ ((noinline, noipa)) int
> +use (int x)
> +{
> + volatile int y = x;
> + return x;
> +}
> +
> +extern void global ();
> +
> +int
> +main ()
> +{
> + for (int i = 0; i < TRIP; i++)
> + {
> + /* Call only 50% of the time. */
> + if (use (i) < TRIP / 2)
> + global ();
> +
> + if (foo () < 5)
> + /* This function is never called. */
> + effect_1 ();
> + else
> + effect_2 ();
> + }
> +}
> +
> +/* Check that the annotation actually occurs. */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for main"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for use" afdo
> } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for foo" afdo
> } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for effect_2"
> afdo } } */
> +
> +/* There should be no collision with effect_1 from afdo-lto_priv-header-1.c.
> */
> +/* { dg-final-use-autofdo { scan-ipa-dump "No afdo profile for effect_1"
> afdo } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.h
> b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.h
> new file mode 100644
> index 00000000000..ee2a14d8eca
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.h
> @@ -0,0 +1,3 @@
> +__attribute__ ((noinline, noipa)) static void effect_1 () {}
> +__attribute__ ((noinline, noipa)) static void effect_2 () {}
> +__attribute__ ((noinline, noipa)) static int foo () { return 5; }
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.c
> b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.c
> new file mode 100644
> index 00000000000..b9dfe92676e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.c
> @@ -0,0 +1,18 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "afdo-lto_priv-header-0.c" } */
> +/* { dg-options "-O2 -flto -fdump-ipa-afdo" } */
> +/* { dg-require-profiling "-fauto-profile" } */
> +
> +#include "afdo-lto_priv-header-1.h"
> +
> +void global()
> +{
> + effect_1();
> + effect_2();
> +}
> +
> +/* Check that the annotation actually occurs. */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for global"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for effect_2"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for effect_1"
> afdo } } */
> +/* { dg-final-use-autofdo { scan-ipa-dump-not "No afdo profile for
> do_nothing" afdo } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.h
> b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.h
> new file mode 100644
> index 00000000000..249c97081d3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.h
> @@ -0,0 +1,3 @@
> +__attribute__((noinline, noipa)) static void do_nothing() {}
> +__attribute__((noinline, noipa)) static void effect_1() { do_nothing(); }
> +__attribute__((noinline, noipa)) static void effect_2() { do_nothing(); }
> diff --git a/gcc/testsuite/lib/profopt.exp b/gcc/testsuite/lib/profopt.exp
> index 81d86c632d1..0001f6798dd 100644
> --- a/gcc/testsuite/lib/profopt.exp
> +++ b/gcc/testsuite/lib/profopt.exp
> @@ -452,7 +452,7 @@ proc profopt-execute { src } {
> # convert profile
> if { $run_autofdo == 1 } {
> set bprefix "afdo."
> - set cmd "create_gcov --binary $execname1 --profile=$tmpdir/$base.perf.data
> -gcov_version=2 --gcov=$tmpdir/$bprefix$base.$ext"
> + set cmd "create_gcov --binary $execname1 --profile=$tmpdir/$base.perf.data
> --gcov_version=3 --gcov=$tmpdir/$bprefix$base.$ext"
> verbose "Running $cmd"
> set id [remote_spawn "" $cmd]
> if { $id < 0 } {
> --
> 2.44.0
> <0001-AutoFDO-Source-filename-tracking-in-GCOV.patch>