Define the in-memory representation of a GCOV AutoFDO v3 profile, including
functions, source positions, callsites, indirect targets, and
discriminators.  This isolates the data model from serialization so its
ownership and invariants can be reviewed first.

afdo_tools/ChangeLog:

        * src/gcov.h: New file.
        * src/gcov_discriminator_encoding.h: New file.

Signed-off-by: Kugan Vivekanandarajah <[email protected]>
Co-authored-by: Dhruv Chawla <[email protected]>
Co-authored-by: Rahul Shinde <[email protected]>
---
 afdo_tools/src/gcov.h                        | 293 +++++++++++++++++++
 afdo_tools/src/gcov_discriminator_encoding.h | 106 +++++++
 2 files changed, 399 insertions(+)
 create mode 100644 afdo_tools/src/gcov.h
 create mode 100644 afdo_tools/src/gcov_discriminator_encoding.h

diff --git a/afdo_tools/src/gcov.h b/afdo_tools/src/gcov.h
new file mode 100644
index 00000000000..e5dd89b4a08
--- /dev/null
+++ b/afdo_tools/src/gcov.h
@@ -0,0 +1,293 @@
+/* Copyright The GNU Toolchain Authors
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_GCOV_H
+#define GCC_GCOV_H
+
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace gcov {
+
+/* All-ones sentinel for an absent name, file, section, or symbol index.  */
+constexpr uint32_t INVALID_INDEX = UINT32_MAX;
+
+/* One-shot diagnostic when a profile count wraps a uint64_t.  */
+void gcov_note_count_overflow ();
+
+/* Add profile counts with wrapping uint64 arithmetic, warning once if the
+   add overflows.  */
+inline uint64_t
+gcov_add_count (uint64_t a, uint64_t b)
+{
+  if (b > std::numeric_limits<uint64_t>::max () - a)
+    gcov_note_count_overflow ();
+  return a + b;
+}
+
+/* A symbol's name and defining source file.  */
+struct symbol_identity
+{
+  std::string name;
+  std::string file;
+
+  bool operator< (const symbol_identity &other) const
+  {
+    if (name != other.name)
+      return name < other.name;
+    return file < other.file;
+  }
+};
+
+struct gcov_location
+{
+  uint32_t offset = 0;
+  uint32_t discriminator = 0;
+  enum class kind
+  {
+    normal,
+    call,
+    inlined
+  } type;
+  gcov_location (kind type_)
+    : type (type_)
+  {
+  }
+  virtual ~gcov_location () = default;
+  uint64_t total_count () const;
+};
+
+struct gcov_function
+{
+  struct descriptor
+  {
+    /* Indices into gcov_profile::function_names / file_names (file_index
+       is INVALID_INDEX when the function has no source file).  */
+    uint32_t name_index = 0;
+    uint32_t file_index = INVALID_INDEX;
+  } desc;
+  uint32_t id = 0;
+  /* First-execution time; 0 when unknown, which is what auto-profile's
+     "gf.timestamp > 0" guard tests for.  */
+  uint64_t timestamp = 0;
+  uint64_t head_count = 0;
+  uint64_t total_count = 0;
+  std::vector<std::unique_ptr<gcov_location>> locations;
+};
+
+struct gcov_summary_detailed_entry
+{
+  uint32_t cutoff = 0;
+  uint64_t min_count = 0;
+  uint64_t num_counts = 0;
+};
+
+struct gcov_summary
+{
+  uint64_t total_count = 0;
+  uint64_t max_count = 0;
+  uint64_t max_function_count = 0;
+  uint64_t num_counts = 0;
+  uint64_t num_functions = 0;
+  uint64_t num_detailed_entries = 0;
+  std::vector<gcov_summary_detailed_entry> detailed_entries;
+};
+
+class gcov_profile
+{
+  std::vector<std::string> file_names;
+  std::vector<std::string> function_names;
+  /* Function-name index -> file-name index.  */
+  std::vector<uint32_t> file_name_map;
+  std::vector<gcov_function> functions;
+  gcov_summary summary;
+
+public:
+  gcov_profile () = default;
+  /* Immovable and non-copyable.  */
+  gcov_profile (const gcov_profile &) = delete;
+  gcov_profile (gcov_profile &&) = delete;
+  gcov_profile &operator= (const gcov_profile &) = delete;
+  gcov_profile &operator= (gcov_profile &&) = delete;
+
+  const std::vector<gcov_function> &get_functions () const { return functions; 
}
+
+  const std::vector<std::string> &get_function_names () const
+  {
+    return function_names;
+  }
+
+  const std::vector<std::string> &get_file_names () const { return file_names; 
}
+
+  /* Function id -> index into get_file_names () (INVALID_INDEX if none).  */
+  const std::vector<uint32_t> &get_file_name_map () const
+  {
+    return file_name_map;
+  }
+
+  const gcov_summary &get_summary () const { return summary; }
+
+  void set_summary (gcov_summary s) { summary = std::move (s); }
+
+  /* Replace the name and file tables.  FILE_MAP[i] is the file-table index
+     for function-name i, or INVALID_INDEX.  */
+  void set_names (std::vector<std::string> files,
+                 std::vector<std::string> names,
+                 std::vector<uint32_t> file_map)
+  {
+    file_names = std::move (files);
+    function_names = std::move (names);
+    file_name_map = std::move (file_map);
+  }
+
+  /* Build the deduplicated file-name table FILES and the map from each
+     SYMBOL_PATHS index to its entry in FILES.  */
+  static void
+  build_file_name_table (const std::vector<std::string> &symbol_paths,
+                        std::vector<std::string> &files,
+                        std::vector<uint32_t> &file_map)
+  {
+    files.clear ();
+    /* Reserved slot kept for the format; FILE_MAP spells "no file" as
+       INVALID_INDEX, so nothing ever points here.  */
+    files.push_back ("");
+    std::unordered_map<std::string, uint32_t> path_to_file_idx;
+    file_map.assign (symbol_paths.size (), INVALID_INDEX);
+    /* I is a function-name id, whose index 0 is the reserved empty name.  */
+    for (size_t i = 1; i < symbol_paths.size (); ++i)
+      {
+       const std::string &path = symbol_paths[i];
+       if (path.empty ())
+         continue;
+       auto inserted = path_to_file_idx.emplace (path, 0u);
+       if (inserted.second)
+         {
+           inserted.first->second = static_cast<uint32_t> (files.size ());
+           files.push_back (path);
+         }
+       file_map[i] = inserted.first->second;
+      }
+  }
+
+  void set_functions (std::vector<gcov_function> funcs)
+  {
+    functions = std::move (funcs);
+  }
+};
+
+struct gcov_location_normal : gcov_location
+{
+  uint64_t count = 0;
+  gcov_location_normal ()
+    : gcov_location (kind::normal)
+  {
+  }
+  gcov_location_normal (uint64_t count_) : gcov_location_normal ()
+  {
+    count = count_;
+  }
+};
+
+struct gcov_location_call : gcov_location
+{
+  uint64_t count = 0;
+  /* Callee function id to count.  */
+  std::unordered_map<uint64_t, uint64_t> callee_map;
+  gcov_location_call ()
+    : gcov_location (kind::call)
+  {
+  }
+  gcov_location_call (uint64_t count_) : gcov_location_call ()
+  {
+    count = count_;
+  }
+};
+
+struct gcov_location_inline : gcov_location
+{
+  gcov_function inlined_sym;
+  gcov_location_inline ()
+    : gcov_location (kind::inlined)
+  {
+  }
+};
+
+/* Count contributed by this location: its own count for a normal or call
+   location, or the subtree total for an inlined callee.  */
+inline uint64_t
+gcov_location::total_count () const
+{
+  if (type == kind::normal)
+    return static_cast<const gcov_location_normal *> (this)->count;
+  else if (type == kind::call)
+    return static_cast<const gcov_location_call *> (this)->count;
+  else
+    return static_cast<const gcov_location_inline *> (this)
+      ->inlined_sym.total_count;
+}
+
+/* A function's total_count: the sum of its position counts plus the totals of
+   its inline subtrees.  Canonical definition of gcov_function::total_count;
+   callers that build or mutate a function's locations recompute it via
+   this.  */
+inline uint64_t
+gcov_function_subtree_count (const gcov_function &func)
+{
+  uint64_t total = 0;
+  for (const auto &loc : func.locations)
+    total = gcov_add_count (total, loc->total_count ());
+  return total;
+}
+
+/* AutoFDO v3 on-disk format constants (magic, tags, version).  */
+namespace v3 {
+/* Value-profile histogram type written per indirect-call target in the v3
+   format, and the only type the format admits.  The value is fixed by the
+   original AutoFDO producer rather than by GCC, whose current enum hist_type
+   has no such enumerator (7 is HIST_TYPE_MAX there).  */
+constexpr uint32_t HIST_TYPE_INDIR_CALL_TOPN = 7;
+
+/* GCOV file format constants.  */
+struct format
+{
+  enum : uint32_t
+  {
+    /* Magic numbers.  */
+    AFDO_GCOV_DATA_MAGIC = 0x67636461, /* "gcda" */
+    /* AutoFDO-specific tags.  */
+    GCOV_TAG_AFDO_SUMMARY = 0xa8000000,
+    GCOV_TAG_AFDO_FILE_NAMES = 0xaa000000,
+    GCOV_TAG_AFDO_FUNCTION = 0xac000000,
+    GCOV_TAG_MODULE_GROUPING = 0xae000000,
+    GCOV_TAG_AFDO_WORKING_SET = 0xaf000000,
+    GCOV_WORKING_SET_BUCKETS = 128,
+    /* Profile version word.  */
+    AFDO_GCOV_VERSION = 3
+  };
+};
+
+} /* namespace v3.  */
+
+} /* namespace gcov.  */
+
+#endif /* GCC_GCOV_H.  */
diff --git a/afdo_tools/src/gcov_discriminator_encoding.h 
b/afdo_tools/src/gcov_discriminator_encoding.h
new file mode 100644
index 00000000000..1346ed2cc12
--- /dev/null
+++ b/afdo_tools/src/gcov_discriminator_encoding.h
@@ -0,0 +1,106 @@
+/* Copyright The GNU Toolchain Authors
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Hierarchical discriminator encoding for AutoFDO.  */
+
+#ifndef GCC_GCOV_DISCRIMINATOR_ENCODING_H
+#define GCC_GCOV_DISCRIMINATOR_ENCODING_H
+
+#include <cstdint>
+
+namespace autofdo {
+
+/* Format: [Base:8][Multiplicity:7][CopyID:11][Unused:6].  */
+
+constexpr uint32_t
+get_base_discriminator (uint32_t discriminator)
+{
+  return discriminator & 0xFFu;
+}
+
+/* A Multiplicity field of 0 encodes multiplicity 1.  */
+constexpr uint32_t
+get_multiplicity (uint32_t discriminator)
+{
+  uint32_t mult = (discriminator >> 8) & 0x7Fu;
+  return (mult == 0) ? 1u : mult;
+}
+
+constexpr uint32_t
+get_copy_id (uint32_t discriminator)
+{
+  return (discriminator >> 15) & 0x7FFu;
+}
+
+/* Strip multiplicity and keep base + copy_id (Pass-1 aggregation key).  */
+constexpr uint32_t
+strip_multiplicity (uint32_t discriminator)
+{
+  uint32_t base = get_base_discriminator (discriminator);
+  uint32_t copy_id = get_copy_id (discriminator);
+  return base | (copy_id << 15);
+}
+
+/* Have (rel_line, disc) as uint64_t which matches autofdo's SourceInfo.  */
+constexpr uint64_t
+gcov_pack_line_disc (uint32_t rel_line, uint32_t discriminator)
+{
+  return (static_cast<uint64_t> (rel_line) << 32)
+        | static_cast<uint64_t> (discriminator);
+}
+
+/* Written to the GCOV AFDO_FUNCTION pos_counts and callsite offsets.  */
+constexpr uint32_t
+gcov_compress_offset (uint64_t packed)
+{
+  return static_cast<uint32_t> ((packed >> 16) | (packed & 0xffffu));
+}
+
+/* The two halves of a gcov_pack_line_disc value, back out again.  */
+constexpr uint32_t
+gcov_rel_line_from_packed (uint64_t packed)
+{
+  return static_cast<uint32_t> ((packed >> 32) & 0xffffu);
+}
+
+constexpr uint32_t
+gcov_disc_from_packed (uint64_t packed)
+{
+  return static_cast<uint32_t> (packed & 0xffffffffu);
+}
+
+/* PACKED with its discriminator reduced to the base one, merging the copies
+   a cloning pass made of one source position.  */
+constexpr uint64_t
+gcov_repack_with_base_discriminator (uint64_t packed)
+{
+  return gcov_pack_line_disc
+    (gcov_rel_line_from_packed (packed),
+     get_base_discriminator (gcov_disc_from_packed (packed)));
+}
+
+/* Used to drop samples GCC cannot represent in this encoding.  */
+constexpr bool
+gcov_offset_fields_representable (uint32_t rel_line)
+{
+  return rel_line <= 0xFFFFu;
+}
+
+} /* namespace autofdo.  */
+
+#endif /* GCC_GCOV_DISCRIMINATOR_ENCODING_H.  */
-- 
2.34.1

Reply via email to