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