Add the symbol-name, source-path and function-lookup helpers shared by
the DWARF reader and the two correlation passes.

A profile must be keyed on the names a later compilation looks up, so
clone suffixes (.isra.N, .constprop.N, .cold, ...) are stripped, and
source paths are canonicalized when interned and compared component-wise
because DWARF spells one file differently between compilation units.
Merging a name into its parent is a separate, narrower rule, so a
specialized clone keeps its own counts rather than being absorbed by a
sibling.

Address lookup allows for the overlapping ranges that aliases, ICF and
.cold partitioning produce; callees resolve only at an exact entry PC.

afdo_tools/ChangeLog:

        * src/symbol_util.cc: New file.
        * src/symbol_util.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/symbol_util.cc | 246 ++++++++++++++++++++++++++++++++++
 afdo_tools/src/symbol_util.h  |  69 ++++++++++
 2 files changed, 315 insertions(+)
 create mode 100644 afdo_tools/src/symbol_util.cc
 create mode 100644 afdo_tools/src/symbol_util.h

diff --git a/afdo_tools/src/symbol_util.cc b/afdo_tools/src/symbol_util.cc
new file mode 100644
index 00000000000..cc868049e92
--- /dev/null
+++ b/afdo_tools/src/symbol_util.cc
@@ -0,0 +1,246 @@
+/* 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/>.  */
+
+#include "symbol_util.h"
+
+#include <algorithm>
+
+namespace gcc_symbol {
+
+namespace {
+
+const char *const GCC_GENERATED_SUFFIX_TAGS[] = {
+  ".isra", ".constprop", ".part", ".__part", ".lto_priv", ".clone",
+  ".cold", ".hot"
+};
+
+const char *const GCC_SPLIT_PART_TAGS[] = {".cold", ".hot"};
+const char *const GCC_DOTTED_COPY_TAGS[] = {".llvm.", ".__part."};
+
+bool
+is_digit_suffix (const std::string &s)
+{
+  if (s.empty ())
+    return false;
+  for (unsigned char c : s)
+    if (c < '0' || c > '9')
+      return false;
+  return true;
+}
+
+bool
+is_gcc_generated_suffix_tag (const std::string &tag)
+{
+  for (const char *candidate : GCC_GENERATED_SUFFIX_TAGS)
+    if (tag == candidate)
+      return true;
+  return false;
+}
+
+void
+erase_last_tag (std::string &name, const std::string &tag)
+{
+  const size_t at = name.rfind (tag);
+  if (at == std::string::npos)
+    return;
+  const size_t after = at + tag.size ();
+  if (after == name.size ())
+    {
+      name.erase (at);
+      return;
+    }
+  if (name[after] == '.' && is_digit_suffix (name.substr (after + 1)))
+    name.erase (at);
+}
+
+void
+erase_last_dotted_tag (std::string &name, const std::string &tag)
+{
+  const size_t at = name.rfind (tag);
+  if (at == std::string::npos)
+    return;
+  const size_t app = at + tag.size ();
+  const size_t next = name.find ('.', app);
+  const size_t app_end = next == std::string::npos ? name.size () : next;
+  if (app == app_end
+      || !is_digit_suffix (name.substr (app, app_end - app)))
+    return;
+  name.erase (at, app_end - at);
+}
+
+} /* anonymous namespace.  */
+
+std::string
+symbol_base_name (std::string name)
+{
+  for (;;)
+    {
+      const size_t dot = name.rfind ('.');
+      if (dot == std::string::npos || dot + 1 >= name.size ())
+       break;
+
+      const std::string tail = name.substr (dot + 1);
+      if (tail == "cold" || tail == "hot")
+       {
+         name.erase (dot);
+         continue;
+       }
+
+      if (dot == 0 || !is_digit_suffix (tail))
+       break;
+
+      const size_t prev = name.rfind ('.', dot - 1);
+      if (prev == std::string::npos
+         || !is_gcc_generated_suffix_tag (name.substr (prev, dot - prev)))
+       break;
+
+      name.erase (prev);
+    }
+  return name;
+}
+
+std::string
+symbol_split_part_parent (std::string name)
+{
+  for (const char *tag : GCC_SPLIT_PART_TAGS)
+    erase_last_tag (name, tag);
+  for (const char *tag : GCC_DOTTED_COPY_TAGS)
+    erase_last_dotted_tag (name, tag);
+  return name;
+}
+
+bool
+name_equivalent (const std::string &a, const std::string &b)
+{
+  if (a.empty () || b.empty ())
+    return false;
+  if (a == b)
+    return true;
+  const std::string a_part = symbol_split_part_parent (a);
+  const std::string b_part = symbol_split_part_parent (b);
+  if (a_part == b_part)
+    return true;
+  const std::string a_base = symbol_base_name (a_part);
+  const std::string b_base = symbol_base_name (b_part);
+  return a_base == b_base && (a_part == a_base || b_part == b_base);
+}
+
+} /* namespace gcc_symbol.  */
+
+namespace source_path {
+
+bool
+ends_with (const std::string &s, const std::string &suffix)
+{
+  return s.size () >= suffix.size ()
+        && s.compare (s.size () - suffix.size (), suffix.size (), suffix) == 0;
+}
+
+bool
+equivalent (const std::string &a, const std::string &b)
+{
+  if (a == b)
+    return true;
+  if (a.empty () || b.empty ())
+    return false;
+  const auto is_separator = [] (char c) { return c == '/' || c == '\\'; };
+  const std::string &longer = a.size () > b.size () ? a : b;
+  const std::string &shorter = a.size () > b.size () ? b : a;
+  if (is_separator (shorter[0]) || !ends_with (longer, shorter))
+    return false;
+  const size_t start = longer.size () - shorter.size ();
+  return start != 0 && is_separator (longer[start - 1]);
+}
+
+} /* namespace source_path.  */
+
+namespace dwarf_debug {
+
+namespace {
+
+bool
+function_has_valid_range (const function_metadata &func)
+{
+  return func.end_addr > func.start_addr;
+}
+
+bool
+address_in_function_range (const function_metadata &func, uint64_t address)
+{
+  return address >= func.start_addr && address < func.end_addr;
+}
+
+} /* anonymous namespace.  */
+
+const function_metadata *
+function_containing_address (const std::vector<function_metadata> &funcs,
+                            uint64_t address)
+{
+  if (funcs.empty ())
+    return nullptr;
+  auto it
+    = std::upper_bound (funcs.begin (),
+                       funcs.end (),
+                       address,
+                       [] (uint64_t addr, const function_metadata &func)
+                       {
+                         return addr < func.start_addr;
+                       });
+  if (it == funcs.begin ())
+    return nullptr;
+  size_t last = static_cast<size_t> (it - funcs.begin ()) - 1;
+  while (true)
+    {
+      const function_metadata &func = funcs[last];
+      if (address_in_function_range (func, address))
+       {
+         const function_metadata *best = &func;
+         while (last > 0 && funcs[last - 1].start_addr == func.start_addr)
+           {
+             --last;
+             if (address_in_function_range (funcs[last], address))
+               best = &funcs[last];
+           }
+         return best;
+       }
+      if (last == 0)
+       return nullptr;
+      --last;
+    }
+}
+
+const function_metadata *
+function_at_entry_pc (const std::vector<function_metadata> &funcs, uint64_t pc)
+{
+  if (funcs.empty ())
+    return nullptr;
+  auto it
+    = std::lower_bound (funcs.begin (),
+                       funcs.end (),
+                       pc,
+                       [] (const function_metadata &func, uint64_t addr)
+                       {
+                         return func.start_addr < addr;
+                       });
+  for (; it != funcs.end () && it->start_addr == pc; ++it)
+    if (!string_empty (it->name) && function_has_valid_range (*it))
+      return &*it;
+  return nullptr;
+}
+
+} /* namespace dwarf_debug.  */
diff --git a/afdo_tools/src/symbol_util.h b/afdo_tools/src/symbol_util.h
new file mode 100644
index 00000000000..77ce5f7c2e2
--- /dev/null
+++ b/afdo_tools/src/symbol_util.h
@@ -0,0 +1,69 @@
+/* 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_SYMBOL_UTIL_H
+#define GCC_SYMBOL_UTIL_H
+
+#include "dwarf_types.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace gcc_symbol {
+
+/* NAME with every trailing GCC clone suffix stripped: .isra.N, .constprop.N,
+   .lto_priv.N, .cold, .hot, ...  */
+std::string symbol_base_name (std::string name);
+
+/* Name shared by a function and its split parts for emit accounting.  */
+std::string symbol_split_part_parent (std::string name);
+
+/* Match split parts or a generated variant with its unsuffixed function.  */
+bool name_equivalent (const std::string &a, const std::string &b);
+
+} /* namespace gcc_symbol.  */
+
+namespace source_path {
+
+/* C++14 stand-in for std::string::ends_with (C++20).  */
+bool ends_with (const std::string &s, const std::string &suffix);
+
+/* True when paths are equal or one is a relative component suffix of the
+   other.  */
+bool equivalent (const std::string &a, const std::string &b);
+
+} /* namespace source_path.  */
+
+namespace dwarf_debug {
+
+/* Find the function containing ADDRESS in a start-sorted function list.
+   Overlapping ranges require checking earlier starts too.  */
+const function_metadata *
+function_containing_address (const std::vector<function_metadata> &funcs,
+                            uint64_t address);
+
+/* Resolve a callee only at an exact symtab entry PC.  */
+const function_metadata *
+function_at_entry_pc (const std::vector<function_metadata> &funcs,
+                     uint64_t pc);
+
+} /* namespace dwarf_debug.  */
+
+#endif /* GCC_SYMBOL_UTIL_H.  */
-- 
2.34.1

Reply via email to