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>

Reply via email to