Add a self-contained decoder for ARM Statistical Profiling Extension
auxiliary packets.  It reconstructs packet state and reports sampled
addresses, events, contexts, and operation metadata to the SPE reader.

afdo_tools/ChangeLog:

        * src/arm_spe_aux_decode.cc: New file.
        * src/arm_spe_aux_decode.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/arm_spe_aux_decode.cc | 531 +++++++++++++++++++++++++++
 afdo_tools/src/arm_spe_aux_decode.h  | 100 +++++
 2 files changed, 631 insertions(+)
 create mode 100644 afdo_tools/src/arm_spe_aux_decode.cc
 create mode 100644 afdo_tools/src/arm_spe_aux_decode.h

diff --git a/afdo_tools/src/arm_spe_aux_decode.cc 
b/afdo_tools/src/arm_spe_aux_decode.cc
new file mode 100644
index 00000000000..04bd1b96947
--- /dev/null
+++ b/afdo_tools/src/arm_spe_aux_decode.cc
@@ -0,0 +1,531 @@
+/* Minimal ARM SPE (Statistical Profiling Extension) auxtrace decoder.
+   Packet layout follows tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
+   (Linux v6.12).  */
+
+/* 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 "arm_spe_aux_decode.h"
+
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <fcntl.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <unistd.h>
+
+namespace perf_data {
+
+namespace {
+
+/* Contiguous bit mask covering bits H down to L, as in Linux GENMASK_ULL.  */
+constexpr uint64_t
+genmask_ull (unsigned h, unsigned l)
+{
+  return ((~0ULL) >> (63 - h + l)) << l;
+}
+
+/* Packet headers (Linux arm-spe-pkt-decoder.h).  */
+constexpr unsigned SPE_HEADER0_PAD = 0x0;
+constexpr unsigned SPE_HEADER0_END = 0x1;
+constexpr unsigned SPE_HEADER0_TIMESTAMP = 0x71;
+constexpr unsigned SPE_HEADER0_EVENTS = 0x42;
+constexpr unsigned SPE_HEADER0_SOURCE = 0x43;
+constexpr unsigned SPE_HEADER0_CONTEXT = 0x64;
+constexpr unsigned SPE_HEADER0_OP_TYPE = 0x48;
+constexpr unsigned SPE_HEADER0_EXTENDED = 0x20;
+constexpr unsigned SPE_HEADER0_ADDRESS = 0xb0;
+constexpr unsigned SPE_HEADER0_COUNTER = 0x98;
+constexpr unsigned SPE_HEADER1_ALIGNMENT = 0x0;
+
+constexpr uint64_t SPE_HEADER0_MASK1 = genmask_ull (7, 6) | genmask_ull (3, 0);
+constexpr uint64_t SPE_HEADER0_MASK2 = genmask_ull (7, 2);
+constexpr uint64_t SPE_HEADER0_MASK3 = genmask_ull (7, 3);
+
+constexpr unsigned SPE_ADDR_PKT_HDR_INDEX_INS = 0x0;
+constexpr unsigned SPE_ADDR_PKT_HDR_INDEX_BRANCH = 0x1;
+
+constexpr unsigned SPE_OP_PKT_HDR_CLASS_BR_ERET = 0x2;
+constexpr uint64_t SPE_OP_PKT_COND = 0x1;
+
+constexpr unsigned EV_RETIRED = 1;
+constexpr unsigned EV_NOT_TAKEN = 6;
+
+constexpr unsigned SPE_ADDR_PKT_ADDR_BYTE7_SHIFT = 56;
+constexpr unsigned SPE_ADDR_PKT_EL1 = 1;
+constexpr unsigned SPE_ADDR_PKT_EL2 = 2;
+
+constexpr int SPE_NEED_MORE = -1;
+constexpr int SPE_BAD = -2;
+
+constexpr uint64_t MAX_SPE_RANGE_BYTES = 0x1000;
+
+enum spe_pkt_type
+{
+  SPE_PAD,
+  SPE_END,
+  SPE_TS,
+  SPE_ADDR,
+  SPE_COUNTER,
+  SPE_CTX,
+  SPE_OP,
+  SPE_EVENTS,
+  SPE_DATA_SRC,
+};
+
+struct spe_pkt
+{
+  spe_pkt_type type = SPE_PAD;
+  unsigned char index = 0;
+  uint64_t payload = 0;
+};
+
+static unsigned
+spe_hdr_short_index (unsigned h)
+{
+  return h & genmask_ull (2, 0);
+}
+
+static unsigned
+spe_hdr_extended_index (unsigned h0, unsigned h1)
+{
+  return ((h0 & genmask_ull (1, 0)) << 3) | spe_hdr_short_index (h1);
+}
+
+static unsigned
+spe_op_pkt_hdr_class (unsigned h)
+{
+  return h & genmask_ull (1, 0);
+}
+
+/* Payload byte count encoded in bits 5:4 of a packet header.  */
+static unsigned
+spe_payload_len (unsigned char hdr)
+{
+  return 1U << ((hdr & genmask_ull (5, 4)) >> 4);
+}
+
+/* Mark PACKET as TYPE and read the payload following its 1 + EXT_HDR header
+   bytes.  Returns the whole packet length, or SPE_NEED_MORE / SPE_BAD.  */
+static int
+spe_get_payload (const uint8_t *buf, size_t len, unsigned char ext_hdr,
+                spe_pkt_type type, spe_pkt *packet)
+{
+  packet->type = type;
+  const size_t payload_len = spe_payload_len (buf[ext_hdr]);
+  if (len < 1u + ext_hdr + payload_len)
+    return SPE_NEED_MORE;
+  buf += 1 + ext_hdr;
+  switch (payload_len)
+    {
+    case 1:
+      packet->payload = *buf;
+      break;
+    case 2:
+      packet->payload = read_u16 (buf);
+      break;
+    case 4:
+      packet->payload = read_u32 (buf);
+      break;
+    case 8:
+      packet->payload = read_u64 (buf);
+      break;
+    default:
+      return SPE_BAD;
+    }
+  return static_cast<int> (1 + ext_hdr + payload_len);
+}
+
+/* Consume an alignment PAD up to the next boundary in the full stream.  */
+static int
+spe_get_alignment (const uint8_t *buf, size_t len, uint64_t stream_offset,
+                  spe_pkt *packet)
+{
+  const unsigned alignment = 1u << ((buf[0] & 0xf) + 1);
+  if (len < alignment)
+    return SPE_NEED_MORE;
+  packet->type = SPE_PAD;
+  const unsigned mis
+    = static_cast<unsigned> (stream_offset & (alignment - 1));
+  return static_cast<int> (alignment - mis);
+}
+
+/* Address packets carry the address kind in their header index field.  */
+static int
+spe_get_addr (const uint8_t *buf, size_t len, unsigned char ext_hdr,
+             spe_pkt *packet)
+{
+  if (ext_hdr)
+    packet->index
+      = static_cast<unsigned char> (spe_hdr_extended_index (buf[0], buf[1]));
+  else
+    packet->index = static_cast<unsigned char> (spe_hdr_short_index (buf[0]));
+  return spe_get_payload (buf, len, ext_hdr, SPE_ADDR, packet);
+}
+
+/* Decode the packet at BUF into PACKET and return its length in bytes.  */
+static int
+spe_do_get_packet (const uint8_t *buf, size_t len, uint64_t stream_offset,
+                  spe_pkt *packet)
+{
+  unsigned char ext_hdr = 0;
+
+  *packet = spe_pkt{};
+  if (!len)
+    return SPE_NEED_MORE;
+
+  unsigned hdr = buf[0];
+
+  if (hdr == SPE_HEADER0_PAD)
+    {
+      packet->type = SPE_PAD;
+      return 1;
+    }
+  if (hdr == SPE_HEADER0_END)
+    {
+      packet->type = SPE_END;
+      return 1;
+    }
+  if (hdr == SPE_HEADER0_TIMESTAMP)
+    return spe_get_payload (buf, len, 0, SPE_TS, packet);
+  if ((hdr & SPE_HEADER0_MASK1) == SPE_HEADER0_EVENTS)
+    return spe_get_payload (buf, len, 0, SPE_EVENTS, packet);
+  if ((hdr & SPE_HEADER0_MASK1) == SPE_HEADER0_SOURCE)
+    return spe_get_payload (buf, len, 0, SPE_DATA_SRC, packet);
+  if ((hdr & SPE_HEADER0_MASK2) == SPE_HEADER0_CONTEXT)
+    return spe_get_payload (buf, len, 0, SPE_CTX, packet);
+  if ((hdr & SPE_HEADER0_MASK2) == SPE_HEADER0_OP_TYPE)
+    {
+      packet->index = static_cast<unsigned char> (buf[0] & genmask_ull (1, 0));
+      return spe_get_payload (buf, len, 0, SPE_OP, packet);
+    }
+  if ((hdr & SPE_HEADER0_MASK2) == SPE_HEADER0_EXTENDED)
+    {
+      if (len == 1)
+       return SPE_NEED_MORE;
+      ext_hdr = 1;
+      hdr = buf[1];
+      if (hdr == SPE_HEADER1_ALIGNMENT)
+       return spe_get_alignment (buf, len, stream_offset, packet);
+    }
+
+  if ((hdr & SPE_HEADER0_MASK3) == SPE_HEADER0_ADDRESS)
+    return spe_get_addr (buf, len, ext_hdr, packet);
+  if ((hdr & SPE_HEADER0_MASK3) == SPE_HEADER0_COUNTER)
+    return spe_get_payload (buf, len, ext_hdr, SPE_COUNTER, packet);
+
+  return SPE_BAD;
+}
+
+/* As spe_do_get_packet, but coalescing a run of PAD bytes into one step.  */
+static int
+spe_get_packet (const uint8_t *buf, size_t len, uint64_t stream_offset,
+               spe_pkt *packet)
+{
+  const int ret = spe_do_get_packet (buf, len, stream_offset, packet);
+  if (ret > 0 && packet->type == SPE_PAD)
+    {
+      /* Collapse short PAD runs (perf collapses up to 16).  */
+      int r = ret;
+      while (r < 16 && len > static_cast<size_t> (r) && !buf[r])
+       ++r;
+      return r;
+    }
+  return ret;
+}
+
+/* Virtual address held in an instruction or branch address packet PAYLOAD;
+   non-secure EL1/EL2 addresses carry an implicit all-ones top byte.  */
+static uint64_t
+spe_calc_ip (uint64_t payload)
+{
+  const uint64_t ns = (payload >> 63) & 1ULL;
+  const uint64_t el = (payload & genmask_ull (62, 61)) >> 61;
+  uint64_t ip = payload & genmask_ull (55, 0);
+  if (ns && (el == SPE_ADDR_PKT_EL1 || el == SPE_ADDR_PKT_EL2))
+    ip |= 0xffULL << SPE_ADDR_PKT_ADDR_BYTE7_SHIFT;
+  return ip;
+}
+
+} /* namespace.  */
+
+void
+arm_spe_stream_decoder::clear_record ()
+{
+  from_ip_ = 0;
+  to_ip_ = 0;
+  events_ = 0;
+  op_payload_ = 0;
+  timestamp_ = 0;
+  op_index_ = 0;
+  have_op_ = false;
+}
+
+/* Account a completed record: a retired branch contributes either a taken
+   edge or, for an untaken conditional branch, a not-taken PC.  */
+void
+arm_spe_stream_decoder::commit_record (arm_spe_profile_accumulator *acc)
+{
+  if (!(events_ & (1ULL << EV_RETIRED)) || !have_op_
+      || spe_op_pkt_hdr_class (op_index_) != SPE_OP_PKT_HDR_CLASS_BR_ERET)
+    return;
+  if (from_ip_ == 0 || !acc->accept_addr (from_ip_, acc->ctx))
+    return;
+  if (timestamp_ != 0 && acc->note_timestamp != nullptr)
+    acc->note_timestamp (from_ip_, timestamp_, acc->ctx);
+
+  const bool br_cond = (op_payload_ & SPE_OP_PKT_COND) != 0;
+  const bool not_taken = (events_ & (1ULL << EV_NOT_TAKEN)) != 0;
+  if (br_cond && not_taken)
+    {
+      (*acc->not_taken_counts)[from_ip_] += 1;
+      return;
+    }
+
+  if (to_ip_ == 0 || !acc->accept_addr (to_ip_, acc->ctx))
+    return;
+  if (timestamp_ != 0 && acc->note_timestamp != nullptr)
+    acc->note_timestamp (to_ip_, timestamp_, acc->ctx);
+  (*acc->branch_counts)[{from_ip_, to_ip_}] += 1;
+}
+
+bool
+arm_spe_stream_decoder::consume (const uint8_t *spe_buf, size_t spe_len,
+                                arm_spe_profile_accumulator *acc)
+{
+  if ((spe_buf == nullptr && spe_len != 0) || acc == nullptr
+      || acc->accept_addr == nullptr || acc->branch_counts == nullptr
+      || acc->not_taken_counts == nullptr
+      || spe_len > pending_.max_size () - pending_.size ())
+    return false;
+
+  if (spe_len != 0)
+    pending_.insert (pending_.end (), spe_buf, spe_buf + spe_len);
+  size_t pos = 0;
+  while (pos < pending_.size ())
+    {
+      spe_pkt pkt;
+      const int n = spe_get_packet (pending_.data () + pos,
+                                   pending_.size () - pos,
+                                   stream_offset_ + pos, &pkt);
+      if (n == SPE_NEED_MORE)
+       break;
+      if (n <= 0)
+       return false;
+
+      switch (pkt.type)
+       {
+       case SPE_END:
+         commit_record (acc);
+         clear_record ();
+         break;
+       case SPE_TS:
+         timestamp_ = pkt.payload;
+         commit_record (acc);
+         clear_record ();
+         break;
+       case SPE_ADDR:
+         if (pkt.index == SPE_ADDR_PKT_HDR_INDEX_INS)
+           from_ip_ = spe_calc_ip (pkt.payload);
+         else if (pkt.index == SPE_ADDR_PKT_HDR_INDEX_BRANCH)
+           to_ip_ = spe_calc_ip (pkt.payload);
+         break;
+       case SPE_EVENTS:
+         events_ = pkt.payload;
+         break;
+       case SPE_OP:
+         op_index_ = pkt.index;
+         op_payload_ = pkt.payload;
+         have_op_ = true;
+         break;
+       default:
+         break;
+       }
+      pos += static_cast<size_t> (n);
+    }
+
+  if (pos != 0)
+    {
+      if (pos > UINT64_MAX - stream_offset_)
+       return false;
+      stream_offset_ += pos;
+      pending_.erase (pending_.begin (), pending_.begin () + pos);
+    }
+  return true;
+}
+
+/* AArch64 insns that never fall through: B, BR/RET/ERET/DRPS (not BL/BLR),
+   BRK/HLT, UDF.  Conditional branches still fall through when not taken.  */
+
+static bool
+aarch64_insn_falls_through (uint32_t insn)
+{
+  if ((insn >> 26) == 0x05) /* B <label>.  */
+    return false;
+  if ((insn >> 25) == 0x6b)                          /* Branch to register.  */
+    return ((insn >> 21) & 0xf) == 0x1;                      /* BLR falls 
through.  */
+  if ((insn >> 21) == 0x6a1 || (insn >> 21) == 0x6a2) /* BRK, HLT.  */
+    return false;
+  if ((insn >> 16) == 0) /* UDF.  */
+    return false;
+  return true;
+}
+
+/* Cache the executable PT_LOAD segments of BINARY_PATH.  */
+bool
+arm_text_image::load (const std::string &binary_path)
+{
+  segments_.clear ();
+  if (elf_version (EV_CURRENT) == EV_NONE)
+    return false;
+
+  const int fd = open (binary_path.c_str (), O_RDONLY);
+  if (fd < 0)
+    return false;
+
+  Elf *const elf = elf_begin (fd, ELF_C_READ, nullptr);
+  if (elf == nullptr)
+    {
+      close (fd);
+      return false;
+    }
+
+  size_t phnum = 0;
+  size_t raw_size = 0;
+  const char *const raw = elf_rawfile (elf, &raw_size);
+  if (elf_kind (elf) == ELF_K_ELF && raw != nullptr
+      && elf_getphdrnum (elf, &phnum) == 0)
+    for (size_t i = 0; i < phnum; ++i)
+      {
+       GElf_Phdr phdr;
+       if (gelf_getphdr (elf, i, &phdr) != &phdr)
+         continue;
+       if (phdr.p_type != PT_LOAD || (phdr.p_flags & PF_X) == 0
+           || phdr.p_filesz == 0 || phdr.p_offset > raw_size
+           || phdr.p_filesz > raw_size - phdr.p_offset)
+         continue;
+       const uint8_t *const start
+         = reinterpret_cast<const uint8_t *> (raw) + phdr.p_offset;
+       segments_.push_back (
+         {phdr.p_vaddr, std::vector<uint8_t> (start, start + phdr.p_filesz)});
+      }
+
+  elf_end (elf);
+  close (fd);
+  return !segments_.empty ();
+}
+
+bool
+arm_text_image::can_fall_through (uint64_t addr) const
+{
+  for (const segment &seg : segments_)
+    {
+      if (addr < seg.vaddr)
+       continue;
+      const uint64_t off = addr - seg.vaddr;
+      if (off > seg.bytes.size ()
+         || AARCH64_INSN_SIZE > seg.bytes.size () - off)
+       continue;
+      const uint8_t *const p = seg.bytes.data () + off;
+      const uint32_t insn = static_cast<uint32_t> (p[0])
+                           | (static_cast<uint32_t> (p[1]) << 8)
+                           | (static_cast<uint32_t> (p[2]) << 16)
+                           | (static_cast<uint32_t> (p[3]) << 24);
+      return aarch64_insn_falls_through (insn);
+    }
+  return true;
+}
+
+void
+arm_spe_synthesize_ranges (
+  const branch_stack_edge_count_map &branch_counts,
+  const std::unordered_map<uint64_t, uint64_t> &not_taken_counts,
+  const arm_text_image &text, branch_stack_edge_count_map *range_counts)
+{
+  if (range_counts == nullptr || branch_counts.empty ())
+    return;
+
+  /* Synthesize execution ranges from branch landings.  */
+  std::unordered_map<uint64_t, uint64_t> incoming_weights;
+  incoming_weights.reserve (branch_counts.size ());
+  for (const auto &entry : branch_counts)
+    incoming_weights[entry.first.second] += entry.second;
+
+  std::vector<uint64_t> landings;
+  landings.reserve (incoming_weights.size ());
+  for (const auto &entry : incoming_weights)
+    landings.push_back (entry.first);
+  std::sort (landings.begin (), landings.end ());
+
+  std::unordered_set<uint64_t> branch_pc_set;
+  branch_pc_set.reserve (branch_counts.size () + not_taken_counts.size ());
+  for (const auto &entry : branch_counts)
+    branch_pc_set.insert (entry.first.first);
+  for (const auto &entry : not_taken_counts)
+    branch_pc_set.insert (entry.first);
+
+  std::vector<uint64_t> branch_addrs (
+    branch_pc_set.begin (), branch_pc_set.end ());
+  std::sort (branch_addrs.begin (), branch_addrs.end ());
+
+  for (size_t i = 0; i < landings.size (); ++i)
+    {
+      const uint64_t landing = landings[i];
+      const uint64_t window = landing > UINT64_MAX - MAX_SPE_RANGE_BYTES
+                               ? UINT64_MAX
+                               : landing + MAX_SPE_RANGE_BYTES;
+      const uint64_t scan_end = (i + 1 < landings.size ())
+                                 ? landings[i + 1]
+                                 : window;
+      uint64_t range_start = landing;
+      uint64_t range_weight = incoming_weights[landing];
+
+      while (range_weight > 0 && range_start < scan_end)
+       {
+         const auto next_branch = std::lower_bound (
+           branch_addrs.begin (), branch_addrs.end (), range_start);
+         const bool hit_branch
+           = next_branch != branch_addrs.end () && *next_branch < scan_end;
+         if (!hit_branch && scan_end - range_start < AARCH64_INSN_SIZE)
+           break;
+         const uint64_t range_end
+           = hit_branch ? *next_branch : scan_end - AARCH64_INSN_SIZE;
+
+         if (range_end >= range_start)
+           (*range_counts)[{range_start, range_end}] += range_weight;
+
+         if (!hit_branch)
+           break;
+
+         /* Continue past a branch only with its sampled not-taken weight, and
+            never past an unconditional transfer.  */
+         const auto nit = not_taken_counts.find (*next_branch);
+         range_weight = (nit != not_taken_counts.end ()) ? nit->second : 0;
+         if (!text.can_fall_through (*next_branch))
+           break;
+         if (*next_branch > UINT64_MAX - AARCH64_INSN_SIZE)
+           break;
+         range_start = *next_branch + AARCH64_INSN_SIZE;
+       }
+    }
+}
+
+} /* namespace perf_data.  */
diff --git a/afdo_tools/src/arm_spe_aux_decode.h 
b/afdo_tools/src/arm_spe_aux_decode.h
new file mode 100644
index 00000000000..58e78e0dded
--- /dev/null
+++ b/afdo_tools/src/arm_spe_aux_decode.h
@@ -0,0 +1,100 @@
+/* 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_ARM_SPE_AUX_DECODE_H
+#define GCC_ARM_SPE_AUX_DECODE_H
+
+#include "perf.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace perf_data {
+
+/* AArch64 instructions have a fixed four-byte width.  */
+constexpr uint64_t AARCH64_INSN_SIZE = 4;
+
+/* Filled from SPE AUXTRACE.  Callbacks borrow CTX for address filtering and
+   timestamp recording; taken edges go in branch_counts and conditional
+   not-taken branch PCs in not_taken_counts.  */
+struct arm_spe_profile_accumulator
+{
+  bool (*accept_addr) (uint64_t runtime_addr, void *ctx) = nullptr;
+  void (*note_timestamp) (uint64_t runtime_addr, uint64_t timestamp, void *ctx)
+    = nullptr;
+  void *ctx = nullptr;
+  branch_stack_edge_count_map *branch_counts = nullptr;
+  std::unordered_map<uint64_t, uint64_t> *not_taken_counts = nullptr;
+};
+
+/* Streaming SPE decoder.  Packet fragments and the record under construction
+   survive AUXTRACE chunk boundaries.  */
+class arm_spe_stream_decoder
+{
+  std::vector<uint8_t> pending_;
+  uint64_t stream_offset_ = 0;
+  uint64_t from_ip_ = 0;
+  uint64_t to_ip_ = 0;
+  uint64_t events_ = 0;
+  uint64_t op_payload_ = 0;
+  uint64_t timestamp_ = 0;
+  unsigned char op_index_ = 0;
+  bool have_op_ = false;
+
+  void clear_record ();
+  void commit_record (arm_spe_profile_accumulator *acc);
+
+public:
+  /* Consume the next contiguous bytes in the SPE stream.  */
+  bool consume (const uint8_t *spe_buf, size_t spe_len,
+               arm_spe_profile_accumulator *acc);
+
+  /* False when the stream ends in the middle of a packet.  */
+  bool finish () const { return pending_.empty (); }
+};
+
+/* Profiled binary executable bytes, keyed by static vaddr.  */
+class arm_text_image
+{
+  struct segment
+  {
+    uint64_t vaddr;
+    std::vector<uint8_t> bytes;
+  };
+  std::vector<segment> segments_;
+
+public:
+  bool load (const std::string &binary_path);
+
+  /* False only for an AArch64 unconditional transfer; unknown addrs stay
+     true.  */
+  bool can_fall_through (uint64_t addr) const;
+};
+
+/* LBR-style fall-through ranges; stop where TEXT cannot fall through.  */
+void arm_spe_synthesize_ranges (
+  const branch_stack_edge_count_map &branch_counts,
+  const std::unordered_map<uint64_t, uint64_t> &not_taken_counts,
+  const arm_text_image &text, branch_stack_edge_count_map *range_counts);
+
+} /* namespace perf_data.  */
+
+#endif /* GCC_ARM_SPE_AUX_DECODE_H.  */
-- 
2.34.1

Reply via email to