From: Dhruv Chawla <dhr...@nvidia.com>

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 <dhr...@nvidia.com>

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                           | 401 ++++++++++++++----
 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  |  41 ++
 .../gcc.dg/tree-prof/afdo-lto_priv-basic-1.c  |  14 +
 .../gcc.dg/tree-prof/afdo-lto_priv-header-0.c |  41 ++
 .../gcc.dg/tree-prof/afdo-lto_priv-header-0.h |   3 +
 .../gcc.dg/tree-prof/afdo-lto_priv-header-1.c |  12 +
 .../gcc.dg/tree-prof/afdo-lto_priv-header-1.h |   3 +
 gcc/testsuite/lib/profopt.exp                 |   2 +-
 11 files changed, 446 insertions(+), 83 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 7ff952632c3..764a23ce32d 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_;
@@ -815,7 +864,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 +913,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 (DECL_SOURCE_FILE (cn->decl),
+                           get_filename (filename_idx)))
+             return {stripped->second, filename_idx};
+         }
+       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;
+  filenames_.safe_push (xstrdup (name));
+  return filenames_.length () - 1;
+}
+
 /* Read the string table. Return TRUE if reading is successful.  */
 
 bool
@@ -879,12 +1033,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 ()));
+      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 +1158,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 +1281,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 +1377,9 @@ 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 (
+                                      DECL_SOURCE_FILE (callee->decl)));
          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 (
+                                   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 = 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 (
+      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..096cc65cc43
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-0.c
@@ -0,0 +1,41 @@
+/* { 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 ();
+    }
+}
+
+/* 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/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..0ad59f31aff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-basic-1.c
@@ -0,0 +1,14 @@
+/* { 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();
+}
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..21115c48ac4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-0.c
@@ -0,0 +1,41 @@
+/* { 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 ();
+    }
+}
+
+/* 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/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..df368b93f0f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/afdo-lto_priv-header-1.c
@@ -0,0 +1,12 @@
+/* { 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();
+}
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

Reply via email to