From: Dhruv Chawla <[email protected]>

Build libgcovreader.a from the afdo_tools GCOV reader sources and link it
into the GCC backend so auto-profile can consume GCOV v3/v4 profiles
produced by afdo_create:

  - compile afdo-gcov-{io,reader,reader-v3,reader-v4}.o into
    libgcovreader.a
  - drive auto-profile.cc through the shared gcov_reader API
  - add -fauto-profile-format= and -fauto-profile-partial
  - document the options in invoke.texi
  - match PHI arguments on predecessor edges (bb->preds) instead of
    successor edges

Regression tested on aarch64-linux-gnu with no new regressions.

gcc/ChangeLog:

        * Makefile.in (GCOVREADERLIB, GCOVREADER_OBJS): Build
        libgcovreader.a from afdo_tools GCOV reader sources and link it
        into the backend.
        * auto-profile.cc: Read GCOV v3/v4 profiles through
        libgcovreader; build afdo_source_profile from gcov_profile;
        match PHI arguments on predecessor edges.
        (match_with_target): Fix the symbol suffix comparison
        loop, which never advanced.
        * auto-profile.h: Update related declarations.
        * common.opt (fauto-profile-format=, fauto-profile-partial): New
        options.
        * doc/invoke.texi: Document -fauto-profile-format and
        -fauto-profile-partial.
        * flag-types.h (auto_profile_format): New enumeration.

Signed-off-by: Dhruv Chawla <[email protected]>
Co-authored-by: Kugan Vivekanandarajah <[email protected]>
Co-authored-by: Rahul Shinde <[email protected]>
---
 gcc/Makefile.in     |  32 +++-
 gcc/auto-profile.cc | 453 +++++++++++++++++---------------------------
 gcc/auto-profile.h  |   2 +-
 gcc/common.opt      |  24 +++
 gcc/doc/invoke.texi |  29 ++-
 gcc/flag-types.h    |   9 +
 6 files changed, 268 insertions(+), 281 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 9d43004613c..377fad85987 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -447,6 +447,11 @@ CPPINC = -I$(srcdir)/../libcpp/include
 CODYLIB = ../libcody/libcody.a
 CODYINC = -I$(srcdir)/../libcody
 
+GCOVREADERLIB = libgcovreader.a
+GCOVREADER_OBJS = afdo-file-mmap.o afdo-gcov-io.o afdo-gcov-reader.o \
+       afdo-gcov-reader-v3.o afdo-gcov-reader-v4.o
+GCOVREADERINC = -I$(srcdir)/../afdo_tools/src
+
 # Where to find decNumber
 enable_decimal_float = @enable_decimal_float@
 DECNUM = $(srcdir)/../libdecnumber
@@ -1198,7 +1203,7 @@ BUILD_ERRORS = build/errors.o
 INCLUDES = -I. -I$(@D) -I$(srcdir) -I$(srcdir)/$(@D) \
           -I$(srcdir)/../include $(INCINTL) \
           $(CPPINC) $(CODYINC) $(GMPINC) $(DECNUMINC) $(BACKTRACEINC) \
-          $(ISLINC)
+          $(ISLINC) $(GCOVREADERINC)
 
 COMPILE.base = $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) -o $@
 ifeq ($(CXXDEPMODE),depmode=gcc3)
@@ -1979,7 +1984,7 @@ endif
 EXTRA_BACKEND_OBJS = simple-diagnostic-path.o diagnostics/lazy-paths.o
 
 BACKEND = $(EXTRA_BACKEND_OBJS) libbackend.a main.o libcommon-target.a \
-       libcommon.a $(CPPLIB) $(LIBDECNUMBER)
+       libcommon.a $(CPPLIB) $(LIBDECNUMBER) $(GCOVREADERLIB)
 
 # This is defined to "yes" if Tree checking is enabled, which roughly means
 # front-end checking.
@@ -2005,7 +2010,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h 
insn-codes.h \
  genversion$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \
  gcov-tool$(exeext) \
  gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.cc libbackend.a \
- libcommon-target.a libcommon.a libgcc.mk perf.data
+ libcommon-target.a libcommon.a $(GCOVREADERLIB) libgcc.mk perf.data
 
 # This symlink makes the full installation name of the driver be available
 # from within the *build* directory, for use when running the JIT library
@@ -2336,6 +2341,27 @@ SELFTEST_FLAGS = -nostdinc $(DEVNULL) -S -o $(DEVNULL) \
 SELFTEST_TARGETS = @selftest_languages@
 selftest: $(SELFTEST_TARGETS)
 
+# AutoFDO reader objects: afdo_tools sources archived into $(GCOVREADERLIB) for
+# cc1.
+GCOVREADER_COMPILE = $(COMPILER) -c $(CXXFLAGS) -fno-exceptions -fno-rtti \
+       -DIN_GCC -include auto-host.h -I$(srcdir) $(GCOVREADERINC)
+
+afdo-file-mmap.o: $(srcdir)/../afdo_tools/src/file_mmap.cc
+       $(GCOVREADER_COMPILE) $< $(OUTPUT_OPTION)
+afdo-gcov-io.o: $(srcdir)/../afdo_tools/src/gcov_io.cc
+       $(GCOVREADER_COMPILE) $< $(OUTPUT_OPTION)
+afdo-gcov-reader.o: $(srcdir)/../afdo_tools/src/gcov_reader.cc
+       $(GCOVREADER_COMPILE) $< $(OUTPUT_OPTION)
+afdo-gcov-reader-v3.o: $(srcdir)/../afdo_tools/src/gcov_reader_v3.cc
+       $(GCOVREADER_COMPILE) $< $(OUTPUT_OPTION)
+afdo-gcov-reader-v4.o: $(srcdir)/../afdo_tools/src/gcov_reader_v4.cc
+       $(GCOVREADER_COMPILE) $< $(OUTPUT_OPTION)
+
+$(GCOVREADERLIB): $(GCOVREADER_OBJS)
+       -rm -f $@
+       $(AR) $(AR_FLAGS) $@ $(GCOVREADER_OBJS)
+       $(RANLIB) $@
+
 # Recompile all the language-independent object files.
 # This is used only if the user explicitly asks for it.
 compilations: $(BACKEND)
diff --git a/gcc/auto-profile.cc b/gcc/auto-profile.cc
index 9b9ab5f4d9a..4253d54679e 100644
--- a/gcc/auto-profile.cc
+++ b/gcc/auto-profile.cc
@@ -21,8 +21,15 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #define INCLUDE_MAP
 #define INCLUDE_SET
+#define INCLUDE_STRING
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
+/* AutoFDO profile reader shared with the afdo_tools.  Included before
+   gcov-io.h as its namespaced constants share names with gcov-io.h macros,
+   so they must be parsed before those macros are defined.  */
+#include "gcov.h"
+#include "gcov_reader.h"
 #include "backend.h"
 #include "tree.h"
 #include "gimple.h"
@@ -227,44 +234,9 @@ struct string_compare
   }
 };
 
-/* Store the summary information for the profile.  */
-struct summary_info
-{
-  /* There are currently 16 hard-coded percentiles in the GCOV format.  */
-  static constexpr unsigned NUM_PERCENTILES = 16;
-
-  /* The detailed summary is a histogram-based calculation of the minimum
-     execution count required to belong to a certain set of percentile of
-     counts.  */
-  struct detailed_summary
-  {
-    /* The percentile that this represents (multiplied by 1,000,000).  */
-    uint32_t cutoff;
-    /* The minimum execution count required to belong to this percentile.  */
-    uint64_t min_count;
-    /* The number of samples which belong to this percentile.  */
-    uint64_t num_counts;
-  };
-
-  /* The sum of execution counts of all samples.  */
-  uint64_t total_count;
-  /* The maximum individual count.  */
-  uint64_t max_count;
-  /* The maximum head count across all functions.  */
-  uint64_t max_function_count;
-  /* The number of lines that have samples.  */
-  uint64_t num_counts;
-  /* The number of functions that have samples.  */
-  uint64_t num_functions;
-  /* The percentile threshold information.  */
-  detailed_summary detailed_summaries[NUM_PERCENTILES];
-
-  /* Read profile.  Return TRUE on success.  */
-  bool read ();
-
-  /* Get the minimum count required for percentile CUTOFF.  */
-  uint64_t get_threshold_count (uint32_t cutoff);
-};
+/* Get the minimum count required for percentile CUTOFF from summary S.  */
+static uint64_t get_threshold_count (const gcov::gcov_summary &s,
+                                    uint32_t cutoff);
 
 /* Store a string array, indexed by string position in the array.  */
 class string_table
@@ -303,8 +275,10 @@ public:
      NAME.  */
   std::pair<const char *, int> get_original_name (const char *name) const;
 
-  /* Read profile, return TRUE on success.  */
-  bool read ();
+  /* Copy the string table from a gcov_profile.  The string_table symbol index 
is
+     kept identical to gcov_function::id, so descriptors built from either side
+     agree.  */
+  void construct (const gcov::gcov_profile &profile);
 
   /* Return number of entries.  */
   size_t num_entries () { return symbol_names_.length (); }
@@ -384,11 +358,12 @@ class function_instance
 public:
   typedef auto_vec<function_instance *> function_instance_stack;
 
-  /* Read the profile and return a function_instance with head count as
-     HEAD_COUNT. Recursively read callsites to create nested function_instances
-     too. STACK is used to track the recursive creation process.  */
+  /* Build a function_instance from GF.  Recursively builds nested
+     function_instances for inlined callsites.  STACK is used to track the
+     recursive creation process.  */
   static function_instance *
-  read_function_instance (function_instance_stack *stack, bool toplevel = 
true);
+  from_gcov (const gcov::gcov_function &gf, function_instance_stack *stack,
+            bool toplevel = true);
 
   /* Recursively deallocate all callsites (nested function_instances).  */
   ~function_instance ();
@@ -584,18 +559,23 @@ private:
   /* Map from callsite to callee function_instance.  */
   typedef std::map<callsite, function_instance *> callsite_map;
 
+  /* Map from source location (decl_lineno) to profile (count_info).  */
+  typedef std::map<unsigned, count_info> position_count_map;
+
   function_instance (unsigned symbol_name, unsigned file_name,
-                    gcov_type head_count)
+                    gcov_type head_count, const gcov::gcov_function *gcov)
     : descriptor_ (file_name, symbol_name), total_count_ (0),
-      head_count_ (head_count), timestamp_ (0),
-      removed_icall_target_ (false), realized_ (false), in_worklist_ (false),
-      inlined_to_ (NULL), location_ (UNKNOWN_LOCATION),
+      head_count_ (head_count), timestamp_ (0), gcov_ (gcov),
+      pos_built_ (false), removed_icall_target_ (false), realized_ (false),
+      in_worklist_ (false), inlined_to_ (NULL), location_ (UNKNOWN_LOCATION),
       call_location_ (UNKNOWN_LOCATION)
   {
   }
 
-  /* Map from source location (decl_lineno) to profile (count_info).  */
-  typedef std::map<unsigned, count_info> position_count_map;
+  /* Build pos_counts from the backing gcov_function's position counts.  These
+     are the bulk of the profile and are not copied up front, so a function the
+     pass never queries never materializes them.  */
+  void materialize_pos_counts () const;
 
   /* The indices into the string table identifying the function_instance.  */
   function_instance_descriptor descriptor_;
@@ -613,8 +593,11 @@ private:
   /* Map from callsite location to callee function_instance.  */
   callsite_map callsites;
 
-  /* Map from source location to count_info.  */
-  position_count_map pos_counts;
+  /* Backing profile function and the position counts lazily materialized from
+     it.  */
+  const gcov::gcov_function *gcov_;
+  mutable bool pos_built_;
+  mutable position_count_map pos_counts;
 
   /* True if function was removed from indir target list.  */
   bool removed_icall_target_;
@@ -651,7 +634,7 @@ public:
   {
     autofdo_source_profile *map = new autofdo_source_profile ();
 
-    if (map->read ())
+    if (map->construct ())
       return map;
     delete map;
     return NULL;
@@ -703,8 +686,8 @@ private:
 
   autofdo_source_profile () {}
 
-  /* Read AutoFDO profile and returns TRUE on success.  */
-  bool read ();
+  /* Build afdo_source_profile from afdo_gcov_profile.  */
+  bool construct ();
 
   /* Return the function_instance in the profile that correspond to the
      inline STACK.  */
@@ -731,8 +714,8 @@ private:
   auto_vec <function_instance *> duplicate_functions_;
 };
 
-/* Store the summary information from the GCOV file.  */
-static summary_info *afdo_summary_info;
+/* Store the summary information from the profile.  */
+static gcov::gcov_summary afdo_summary_info;
 
 /* Store the strings read from the profile data file.  */
 static string_table *afdo_string_table;
@@ -740,6 +723,10 @@ static string_table *afdo_string_table;
 /* Store the AutoFDO source profile.  */
 static autofdo_source_profile *afdo_source_profile;
 
+/* The profile reader, kept alive for the whole pass.  */
+static std::unique_ptr<gcov::gcov_reader_base> afdo_reader;
+static const gcov::gcov_profile *afdo_gcov_profile;
+
 /* gcov_summary structure to store the profile_info.  */
 static gcov_summary *afdo_profile_info;
 
@@ -987,45 +974,24 @@ get_normalized_path (const char *path, bool from_gcov = 
false)
   return lbasename (path);
 }
 
-/* Member functions for summary_info.  */
-
-bool
-summary_info::read ()
-{
-  if (gcov_read_unsigned () != GCOV_TAG_AFDO_SUMMARY)
-    return false;
-
-  total_count = gcov_read_counter ();
-  max_count = gcov_read_counter ();
-  max_function_count = gcov_read_counter ();
-  num_counts = gcov_read_counter ();
-  num_functions = gcov_read_counter ();
-  uint64_t num_detailed_summaries = gcov_read_counter ();
-  gcc_checking_assert (num_detailed_summaries == NUM_PERCENTILES);
-  for (uint64_t i = 0; i < num_detailed_summaries; i++)
-    {
-      detailed_summaries[i].cutoff = gcov_read_unsigned ();
-      detailed_summaries[i].min_count = gcov_read_counter ();
-      detailed_summaries[i].num_counts = gcov_read_counter ();
-    }
-
-  return !gcov_is_error ();
-}
-
-/* Get the minimum count required for percentile CUTOFF.  */
+/* Get the minimum count required for percentile CUTOFF from summary S.  */
 
-uint64_t
-summary_info::get_threshold_count (uint32_t cutoff)
+static uint64_t
+get_threshold_count (const gcov::gcov_summary &s, uint32_t cutoff)
 {
   /* The cutoffs stored in the GCOV are fractions multiplied by 1,000,000.  */
   gcc_checking_assert (cutoff <= 1'000'000);
+  const std::vector<gcov::gcov_summary_detailed_entry> &entries
+    = s.detailed_entries;
+  if (entries.empty ())
+    return 0;
   unsigned idx = 0;
   /* Find the first cutoff at least as high as CUTOFF.  */
-  for (; idx < NUM_PERCENTILES; idx++)
-    if (detailed_summaries[idx].cutoff >= cutoff)
+  for (; idx < entries.size (); idx++)
+    if (entries[idx].cutoff >= cutoff)
       break;
-  idx = std::min (NUM_PERCENTILES - 1, idx);
-  return detailed_summaries[idx].min_count;
+  idx = std::min ((unsigned) entries.size () - 1, idx);
+  return entries[idx].min_count;
 }
 
 /* Member functions for string_table.  */
@@ -1204,36 +1170,33 @@ string_table::add_filename (const char *name)
   return filenames_.length () - 1;
 }
 
-/* Read the string table. Return TRUE if reading is successful.  */
+/* Copy the string table from a gcov_profile.  The string_table symbol index is
+   kept identical to gcov_function::id, so descriptors built from either side
+   agree.  */
 
-bool
-string_table::read ()
+void
+string_table::construct (const gcov::gcov_profile &profile)
 {
-  if (gcov_read_unsigned () != GCOV_TAG_AFDO_FILE_NAMES)
-    return false;
-  /* 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++)
+  const std::vector<std::string> &file_names = profile.get_file_names ();
+  const std::vector<std::string> &symbols = profile.get_function_names ();
+  const std::vector<uint32_t> &file_map = profile.get_file_name_map ();
+
+  filenames_.reserve (file_names.size ());
+  for (unsigned i = 0; i < file_names.size (); i++)
     {
-      const char *filename = gcov_read_string ();
-      filenames_.quick_push (xstrdup (get_normalized_path (filename, true)));
+      filenames_.quick_push (
+       xstrdup (get_normalized_path (file_names[i].c_str (), true)));
       filename_map_[filenames_.last ()] = i;
-      free (const_cast<char *> (filename));
-      if (gcov_is_error ())
-       return false;
     }
-  /* Read in the function name -> file name table.  */
-  unsigned string_num = gcov_read_unsigned ();
-  symbol_names_.reserve (string_num);
-  for (unsigned i = 0; i < string_num; i++)
+
+  symbol_names_.reserve (symbols.size ());
+  for (unsigned i = 0; i < symbols.size (); i++)
     {
-      symbol_names_.quick_push (const_cast<char *> (gcov_read_string ()));
-      symbol_name_map_[symbol_names_.last ()] = i;
-      unsigned filename_idx = gcov_read_unsigned ();
-      symbol_to_filename_map_[symbol_names_.last ()] = filename_idx;
+      int filename_idx = unknown_filename;
+      if (i < file_map.size () && file_map[i] < filenames_.length ())
+       filename_idx = (int) file_map[i];
+      add_symbol_name (xstrdup (symbols[i].c_str ()), filename_idx);
+
       char *original = const_cast<char *> (
        autofdo::get_original_name (symbol_names_.last ()));
       if (strcmp (original, symbol_names_.last ()))
@@ -1252,11 +1215,7 @@ string_table::read ()
       else
        /* There are no suffixes to remove.  */
        free (original);
-
-      if (gcov_is_error ())
-       return false;
     }
-  return true;
 }
 
 /* Return cgraph node corresponding to given NAME_INDEX,
@@ -1449,6 +1408,8 @@ function_instance::merge (function_instance *other,
       callsites[iter->first]->merge (iter->second, new_functions);
     }
 
+  materialize_pos_counts ();
+  other->materialize_pos_counts ();
   for (position_count_map::const_iterator iter = other->pos_counts.begin ();
        iter != other->pos_counts.end (); ++iter)
     if (pos_counts.count (iter->first) == 0)
@@ -1572,10 +1533,8 @@ match_with_target (cgraph_node *n,
     {
       int i;
       bool in_suffix = false;
-      for (i = 0; i; i++)
+      for (i = 0; name[i] && name[i] == symbol_name[i]; i++)
        {
-         if (name[i] != symbol_name[i])
-           break;
          if (name[i] == '.')
            in_suffix = true;
        }
@@ -1644,6 +1603,7 @@ count_info *
 function_instance::lookup_count (location_t loc, inline_stack &stack,
                                 cgraph_node *node)
 {
+  materialize_pos_counts ();
   gcc_checking_assert (stack.length () < 2);
   if (stack.length ())
     {
@@ -1701,6 +1661,7 @@ function_instance::match (cgraph_node *node,
                          vec <function_instance *> &new_functions,
                          name_index_map &to_symbol_name)
 {
+  materialize_pos_counts ();
   if (get_location () != UNKNOWN_LOCATION)
     return false;
   set_location (DECL_SOURCE_LOCATION (node->decl));
@@ -2234,6 +2195,7 @@ function_instance::offline_if_not_realized
 void
 function_instance::dump (FILE *f, int indent, bool nested) const
 {
+  materialize_pos_counts ();
   if (!nested)
     fprintf (f, "%*s%s total:%" PRIu64 " head:%" PRId64 "\n", indent, "",
             afdo_string_table->get_symbol_name (symbol_name ()),
@@ -2309,6 +2271,7 @@ function_instance::debug () const
 bool
 function_instance::get_count_info (location_t loc, count_info *info) const
 {
+  materialize_pos_counts ();
   position_count_map::const_iterator iter = pos_counts.find (loc);
   if (iter == pos_counts.end ())
     return false;
@@ -2709,87 +2672,66 @@ autofdo_source_profile::offline_unrealized_inlines ()
       function.second->dump (dump_file);
 }
 
-/* Read the profile and create a function_instance with head count as
-   HEAD_COUNT. Recursively read callsites to create nested function_instances
-   too. STACK is used to track the recursive creation process.  */
-
-/* function instance profile format:
-
-   ENTRY_COUNT: 8 bytes
-   TIMESTAMP: 8 bytes (only for toplevel symbols)
-   NAME_INDEX: 4 bytes
-   NUM_POS_COUNTS: 4 bytes
-   NUM_CALLSITES: 4 byte
-   POS_COUNT_1:
-     POS_1_OFFSET: 4 bytes
-     NUM_TARGETS: 4 bytes
-     COUNT: 8 bytes
-     TARGET_1:
-       VALUE_PROFILE_TYPE: 4 bytes
-       TARGET_IDX: 8 bytes
-       COUNT: 8 bytes
-     TARGET_2
-     ...
-     TARGET_n
-   POS_COUNT_2
-   ...
-   POS_COUNT_N
-   CALLSITE_1:
-     CALLSITE_1_OFFSET: 4 bytes
-     FUNCTION_INSTANCE_PROFILE (nested)
-   CALLSITE_2
-   ...
-   CALLSITE_n.  */
+/* Build pos_counts from the backing gcov_function's position counts.  These
+   are the bulk of the profile and are not copied up front, so a function the
+   pass never queries never materializes them.  */
 
-function_instance *
-function_instance::read_function_instance (function_instance_stack *stack,
-                                          bool toplevel)
+void
+function_instance::materialize_pos_counts () const
 {
-  gcov_type_unsigned timestamp = 0;
-  gcov_type head_count = -1;
-  if (toplevel)
+  bool built = pos_built_;
+  pos_built_ = true;
+  if (built || !gcov_)
+    return;
+  for (const std::unique_ptr<gcov::gcov_location> &locp : gcov_->locations)
     {
-      head_count = gcov_read_counter ();
-      timestamp = (gcov_type_unsigned) gcov_read_counter ();
+      const gcov::gcov_location *loc = locp.get ();
+      unsigned offset = (loc->offset << 16) | (loc->discriminator & 0xffff);
+      if (loc->type == gcov::gcov_location::kind::normal)
+       pos_counts[offset].count
+         = static_cast<const gcov::gcov_location_normal *> (loc)->count;
+      else if (loc->type == gcov::gcov_location::kind::call)
+       {
+         const gcov::gcov_location_call *call
+           = static_cast<const gcov::gcov_location_call *> (loc);
+         pos_counts[offset].count = call->count;
+         /* Only indirect call target histogram is supported now.  */
+         for (const auto &t : call->callee_map)
+           pos_counts[offset].targets[t.first] = t.second;
+       }
     }
-  unsigned name = gcov_read_unsigned ();
-  unsigned num_pos_counts = gcov_read_unsigned ();
-  unsigned num_callsites = gcov_read_unsigned ();
+}
+
+/* Build a function_instance from GF.  Recursively builds nested
+   function_instances for inlined callsites.  STACK is used to track the
+   recursive creation process.  */
+
+function_instance *
+function_instance::from_gcov (const gcov::gcov_function &gf,
+                             function_instance_stack *stack, bool toplevel)
+{
+  gcov_type head_count = toplevel ? (gcov_type) gf.head_count : -1;
+  unsigned name = gf.id;
   function_instance *s
     = new function_instance (name,
                             afdo_string_table->get_filename_by_symbol (name),
-                            head_count);
-  if (timestamp > 0)
-    s->set_timestamp (timestamp);
+                            head_count, &gf);
+  s->total_count_ = gf.total_count;
+  if (toplevel && gf.timestamp > 0)
+    s->set_timestamp (gf.timestamp);
   if (!stack->is_empty ())
     s->set_inlined_to (stack->last ());
   stack->safe_push (s);
 
-  for (unsigned i = 0; i < num_pos_counts; i++)
-    {
-      unsigned offset = gcov_read_unsigned ();
-      unsigned num_targets = gcov_read_unsigned ();
-      gcov_type count = gcov_read_counter ();
-      s->pos_counts[offset].count = count;
-
-      for (unsigned j = 0; j < stack->length (); j++)
-        (*stack)[j]->total_count_ += count;
-      for (unsigned j = 0; j < num_targets; j++)
-        {
-         /* Only indirect call target histogram is supported now.  */
-         gcov_read_unsigned ();
-         gcov_type target_idx = gcov_read_counter ();
-         s->pos_counts[offset].targets[target_idx] = gcov_read_counter ();
-        }
-    }
-  for (unsigned i = 0; i < num_callsites; i++)
+  for (const std::unique_ptr<gcov::gcov_location> &locp : gf.locations)
     {
-      unsigned offset = gcov_read_unsigned ();
-      function_instance *callee_function_instance
-       = read_function_instance (stack, false);
-      s->callsites[std::make_pair (offset,
-                                  callee_function_instance->symbol_name ())]
-       = callee_function_instance;
+      if (locp->type != gcov::gcov_location::kind::inlined)
+       continue;
+      const gcov::gcov_location_inline *inl
+       = static_cast<const gcov::gcov_location_inline *> (locp.get ());
+      unsigned offset = (locp->offset << 16) | (locp->discriminator & 0xffff);
+      function_instance *callee = from_gcov (inl->inlined_sym, stack, false);
+      s->callsites[std::make_pair (offset, callee->symbol_name ())] = callee;
     }
   stack->pop ();
   return s;
@@ -3007,41 +2949,19 @@ autofdo_source_profile::get_callsite_total_count (
   return s->total_count () * afdo_count_scale;
 }
 
-/* Read AutoFDO profile and returns TRUE on success.  */
-
-/* source profile format:
-
-   GCOV_TAG_AFDO_FUNCTION: 4 bytes
-   LENGTH: 4 bytes
-   NUM_FUNCTIONS: 4 bytes
-   FUNCTION_INSTANCE_1
-   FUNCTION_INSTANCE_2
-   ...
-   FUNCTION_INSTANCE_N.  */
+/* Build afdo_source_profile from afdo_gcov_profile.  */
 
 bool
-autofdo_source_profile::read ()
+autofdo_source_profile::construct ()
 {
-  if (gcov_read_unsigned () != GCOV_TAG_AFDO_FUNCTION)
-    {
-      inform (UNKNOWN_LOCATION, "Not expected TAG.");
-      return false;
-    }
-
   gcc_checking_assert (!afdo_source_profile);
   afdo_source_profile = this;
 
-  /* Skip the length of the section.  */
-  gcov_read_unsigned ();
-
-  /* Read in the function/callsite profile, and store it in local
-     data structure.  */
-  unsigned function_num = gcov_read_unsigned ();
-  for (unsigned i = 0; i < function_num; i++)
+  /* Build the function/callsite profile from the gcov_profile.  */
+  for (const gcov::gcov_function &gf : afdo_gcov_profile->get_functions ())
     {
       function_instance::function_instance_stack stack;
-      function_instance *s
-       = function_instance::read_function_instance (&stack);
+      function_instance *s = function_instance::from_gcov (gf, &stack);
 
       if (find_function_instance (s->get_descriptor ()) == nullptr)
        add_function_instance (s);
@@ -3063,7 +2983,7 @@ autofdo_source_profile::read ()
   for (auto &p : timestamp_info_map)
     p.second = tp_first_run++;
 
-  afdo_profile_info->sum_max = afdo_summary_info->max_count;
+  afdo_profile_info->sum_max = afdo_summary_info.max_count;
   /* Scale up the profile, but leave some bits in case some counts gets
      bigger than sum_max eventually.  */
   if (afdo_profile_info->sum_max)
@@ -3072,7 +2992,7 @@ autofdo_source_profile::read ()
             / afdo_profile_info->sum_max, 1);
   afdo_profile_info->cutoff *= afdo_count_scale;
   /* Derive the hot count threshold from the profile summary.  */
-  afdo_hot_bb_threshold = afdo_summary_info->get_threshold_count (
+  afdo_hot_bb_threshold = get_threshold_count (afdo_summary_info,
                            param_hot_bb_count_ws_permille * 1000)
                          * afdo_count_scale;
   set_hot_bb_threshold (afdo_hot_bb_threshold);
@@ -3087,7 +3007,7 @@ autofdo_source_profile::read ()
             (int64_t) afdo_profile_info->sum_max, (int64_t) afdo_count_scale,
             (int64_t) (afdo_profile_info->sum_max * afdo_count_scale),
             (int64_t) afdo_profile_info->cutoff,
-            (int64_t) afdo_summary_info->get_threshold_count (
+            (int64_t) get_threshold_count (afdo_summary_info,
               param_hot_bb_count_ws_permille * 1000),
             (int64_t) afdo_hot_bb_threshold);
   afdo_profile_info->sum_max *= afdo_count_scale;
@@ -3186,85 +3106,65 @@ autofdo_source_profile::remove_function_instance 
(function_instance *inst)
   return false;
 }
 
-/* Module profile is only used by LIPO. Here we simply ignore it.  */
-
-static void
-fake_read_autofdo_module_profile ()
-{
-  /* Read in the module info.  */
-  gcov_read_unsigned ();
-
-  /* Skip the length of the section.  */
-  gcov_read_unsigned ();
-
-  /* Read in the file name table.  */
-  unsigned total_module_num = gcov_read_unsigned ();
-  gcc_assert (total_module_num == 0);
-}
-
 /* Read data from profile data file.  */
 
 static void
 read_profile (void)
 {
-  if (gcov_open (auto_profile_file, 1) == 0)
+  gcov::profile_format fmt;
+  switch (flag_auto_profile_format)
     {
-      error ("cannot open profile file %s", auto_profile_file);
-      return;
+    case AUTO_PROFILE_V3:
+      fmt = gcov::profile_format::v3_binary;
+      break;
+    case AUTO_PROFILE_V4:
+      fmt = gcov::profile_format::v4_binary;
+      break;
+    case AUTO_PROFILE_V4_TEXT:
+      fmt = gcov::profile_format::v4_text;
+      break;
+    default:
+      fmt = gcov::detect_format (auto_profile_file);
+      break;
     }
 
-  if (gcov_read_unsigned () != GCOV_DATA_MAGIC)
+  std::unique_ptr<gcov::gcov_reader_base> reader
+    = gcov::make_gcov_reader (auto_profile_file, fmt);
+  if (!reader)
     {
-      error ("AutoFDO profile magic number does not match");
+      error ("cannot determine format of auto-profile file %s",
+            auto_profile_file);
       return;
     }
 
-  /* Skip the version number.  */
-  unsigned version = gcov_read_unsigned ();
-  if (version != AUTO_PROFILE_VERSION)
+  bool read_ok = false;
+  /* Per-TU partial read.  */
+  if (flag_auto_profile_partial && reader->supports_partial_read ()
+      && main_input_filename)
     {
-      error ("AutoFDO profile version %u does not match %u",
-            version, AUTO_PROFILE_VERSION);
-      return;
+      const char *tu = get_normalized_path (main_input_filename);
+      read_ok = reader->read (std::string (tu),
+                             param_profile_func_internal_id == 1);
     }
 
-  /* Skip the empty integer.  */
-  gcov_read_unsigned ();
-
-  /* summary_info.  */
-  afdo_summary_info = new summary_info ();
-  if (!afdo_summary_info->read ())
+  if (!read_ok && !reader->read ())
     {
-      error ("cannot read summary information from %s", auto_profile_file);
+      error ("cannot read auto-profile from %s", auto_profile_file);
       return;
     }
 
-  /* string_table.  */
+  afdo_gcov_profile = reader->get_profile ();
+  afdo_summary_info = afdo_gcov_profile->get_summary ();
+
   afdo_string_table = new string_table ();
-  if (!afdo_string_table->read ())
-    {
-      error ("cannot read string table from %s", auto_profile_file);
-      return;
-    }
+  afdo_string_table->construct (*afdo_gcov_profile);
 
-  /* autofdo_source_profile.  */
   afdo_source_profile = autofdo_source_profile::create ();
-  if (afdo_source_profile == NULL
-      || gcov_is_error ())
-    {
-      error ("cannot read function profile from %s", auto_profile_file);
-      delete afdo_source_profile;
-      afdo_source_profile = NULL;
-      return;
-    }
+  if (afdo_source_profile == NULL)
+    error ("cannot read function profile from %s", auto_profile_file);
 
-  /* autofdo_module_profile.  */
-  fake_read_autofdo_module_profile ();
-  if (gcov_is_error ())
-    {
-      error ("cannot read module profile from %s", auto_profile_file);
-      return;
-    }
+  /* Keep the reader (and the gcov_profile it owns) alive for the pass.  */
+  afdo_reader = std::move (reader);
 }
 
 /* From AutoFDO profiles, find values inside STMT for that we want to measure
@@ -4715,7 +4615,8 @@ end_auto_profile (void)
 {
   delete autofdo::afdo_source_profile;
   delete autofdo::afdo_string_table;
-  delete autofdo::afdo_summary_info;
+  autofdo::afdo_reader.reset ();
+  autofdo::afdo_gcov_profile = NULL;
   profile_info = NULL;
 }
 
diff --git a/gcc/auto-profile.h b/gcc/auto-profile.h
index 2685a1453c5..c52534a941d 100644
--- a/gcc/auto-profile.h
+++ b/gcc/auto-profile.h
@@ -21,7 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef AUTO_PROFILE_H
 #define AUTO_PROFILE_H
 
-#define AUTO_PROFILE_VERSION 3
+#define AUTO_PROFILE_VERSION 4
 
 /* Read, process, finalize AutoFDO data structures.  */
 extern void read_autofdo_file (void);
diff --git a/gcc/common.opt b/gcc/common.opt
index 69a55b8d40d..9e17737e4f2 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1218,6 +1218,30 @@ fauto-profile-inlining
 Common Var(flag_auto_profile_inlining) Init(1) Optimization
 Perform inlining using auto-profile.
 
+fauto-profile-partial
+Common Var(flag_auto_profile_partial) Init(0)
+Read only the current translation unit's data from a version 4 auto-profile,
+instead of the whole profile.
+
+fauto-profile-format=
+Common Joined RejectNegative Enum(auto_profile_format) 
Var(flag_auto_profile_format) Init(AUTO_PROFILE_AUTO)
+Select the on-disk auto-profile format.
+
+Enum
+Name(auto_profile_format) Type(enum auto_profile_format) UnknownError(unknown 
auto-profile format %qs)
+
+EnumValue
+Enum(auto_profile_format) String(auto) Value(AUTO_PROFILE_AUTO)
+
+EnumValue
+Enum(auto_profile_format) String(v3) Value(AUTO_PROFILE_V3)
+
+EnumValue
+Enum(auto_profile_format) String(v4) Value(AUTO_PROFILE_V4)
+
+EnumValue
+Enum(auto_profile_format) String(v4-text) Value(AUTO_PROFILE_V4_TEXT)
+
 ; -fbounds-check causes gcc to generate array bounds checks.
 ; It's presently used only for fortran, algol68, and d languages.
 ; In Fortran and algol68 it is a deprecated alias for -fcheck=bounds,
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3311ff94619..c27336f0e50 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -605,7 +605,8 @@ Objective-C and Objective-C++ Dialects}.
 -fmin-function-alignment=[@var{n}]
 -fno-allocation-dce  -fallow-store-data-races
 -fassociative-math  -fauto-profile  -fauto-profile[=@var{path}]
--fauto-profile-inlining  -fauto-inc-dec  -fbranch-probabilities
+-fauto-profile-inlining  -fauto-profile-format=@var{format}
+-fauto-profile-partial  -fauto-inc-dec  -fbranch-probabilities
 -fcaller-saves
 -fcombine-stack-adjustments  -fconserve-stack
 -ffold-mem-offsets
@@ -16464,6 +16465,32 @@ create_gcov --binary=your_program.unstripped 
--profile=perf.data \
 When auto-profile is available inline all relevant functions which was
 inlined in the tran run before reading the profile feedback.  This improves
 context sensitivity of the profile.  Enabled by default.
+
+@opindex fauto-profile-format
+@item -fauto-profile-format=@var{format}
+Select the on-disk format of the AutoFDO profile supplied to
+@option{-fauto-profile}.  @var{format} is one of:
+
+@table @samp
+@item auto
+Detect the format from the file contents.  This is the default.
+@item v3
+The legacy @samp{gcda}-based format written by older @command{create_gcov}.
+@item v4
+The version 4 binary format.
+@item v4-text
+The textual variant of the version 4 format.
+@end table
+
+@opindex fauto-profile-partial
+@opindex fno-auto-profile-partial
+@item -fauto-profile-partial
+Read only the data needed for the translation unit being compiled from a
+version 4 AutoFDO profile, rather than the whole profile.  This can
+substantially reduce the time spent reading large cross-module profiles.  The
+option has no effect for profiles whose format does not support partial reads
+(such as the legacy version 3 format), which are always read in full.
+Disabled by default.
 @end table
 
 The following options control compiler behavior regarding floating-point
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index df983d9d947..dab1d16f12d 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -160,6 +160,15 @@ enum stack_reuse_level
   SR_ALL
 };
 
+/* On-disk auto-profile format selected by -fauto-profile-format.  */
+enum auto_profile_format
+{
+  AUTO_PROFILE_AUTO,
+  AUTO_PROFILE_V3,
+  AUTO_PROFILE_V4,
+  AUTO_PROFILE_V4_TEXT
+};
+
 /* Control Flow Redundancy hardening options for noreturn calls.  */
 enum hardcfr_noret
 {
-- 
2.34.1

Reply via email to