[llvm] [ctx_profile] Profile reader and writer (PR #91859)

Mircea Trofin via llvm-commits llvm-commits at lists.llvm.org
Tue May 14 14:59:56 PDT 2024


https://github.com/mtrofin updated https://github.com/llvm/llvm-project/pull/91859

>From d89622c1203776d680b72b2bb25d4a5e6e0d2e0f Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Thu, 9 May 2024 14:55:27 -0700
Subject: [PATCH 1/5] [ctx_profile] Profile reader and writer

Utility converting a profile coming from `compiler_rt` to bitstream.

`PGOCtxProfileWriter::write` would be used as the `Writer` parameter for
`__llvm_ctx_profile_fetch` API. This is expected to happen in user code,
for example in the RPC hanler tasked with collecting a profile, and would
look like this:

```
// set up an output stream "Out", which could contain other stuff
{
  // constructing the Writer will start the section, in Out, containing
  // the collected contextual profiles.
  PGOCtxProfWriter Writer(Out);
  __llvm_ctx_profile_fetch(&Writer, +[](void* W, const ContextNode &N) {
    reinterpret_cast<PGOCtxProfWriter*>(W)->write(N);
  });
  // Writer going out of scope will finish up the section.
}
```
---
 .../llvm/ProfileData/PGOCtxProfReader.h       |  91 ++++++
 .../llvm/ProfileData/PGOCtxProfWriter.h       |  91 ++++++
 llvm/lib/ProfileData/CMakeLists.txt           |   2 +
 llvm/lib/ProfileData/PGOCtxProfReader.cpp     | 165 +++++++++++
 llvm/lib/ProfileData/PGOCtxProfWriter.cpp     |  49 ++++
 llvm/unittests/ProfileData/CMakeLists.txt     |   1 +
 .../PGOCtxProfReaderWriterTest.cpp            | 266 ++++++++++++++++++
 7 files changed, 665 insertions(+)
 create mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfReader.h
 create mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
 create mode 100644 llvm/lib/ProfileData/PGOCtxProfReader.cpp
 create mode 100644 llvm/lib/ProfileData/PGOCtxProfWriter.cpp
 create mode 100644 llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp

diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
new file mode 100644
index 0000000000000..e5219504163b6
--- /dev/null
+++ b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
@@ -0,0 +1,91 @@
+//===--- PGOCtxProfReader.h - Contextual profile reader ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// Reader for contextual iFDO profile, which comes in bitstream format.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H
+#define LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H
+
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Bitstream/BitstreamReader.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/ProfileData/PGOCtxProfWriter.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <vector>
+
+namespace llvm {
+/// The loaded contextual profile, suitable for mutation during IPO passes. We
+/// generally expect a fraction of counters and of callsites to be populated.
+/// We continue to model counters as vectors, but callsites are modeled as a map
+/// of a map. The expectation is that, typically, there is a small number of
+/// indirect targets (usually, 1 for direct calls); but potentially a large
+/// number of callsites, and, as inlining progresses, the callsite count of a
+/// caller will grow.
+class PGOContextualProfile final {
+public:
+  using CallTargetMapTy = std::map<GlobalValue::GUID, PGOContextualProfile>;
+  using CallsiteMapTy = DenseMap<uint32_t, CallTargetMapTy>;
+
+private:
+  friend class PGOCtxProfileReader;
+  GlobalValue::GUID GUID = 0;
+  SmallVector<uint64_t, 16> Counters;
+  CallsiteMapTy Callsites;
+
+  PGOContextualProfile(GlobalValue::GUID G,
+                       SmallVectorImpl<uint64_t> &&Counters)
+      : GUID(G), Counters(std::move(Counters)) {}
+
+  Expected<PGOContextualProfile &>
+  getOrEmplace(uint32_t Index, GlobalValue::GUID G,
+               SmallVectorImpl<uint64_t> &&Counters);
+
+public:
+  PGOContextualProfile(const PGOContextualProfile &) = delete;
+  PGOContextualProfile &operator=(const PGOContextualProfile &) = delete;
+  PGOContextualProfile(PGOContextualProfile &&) = default;
+  PGOContextualProfile &operator=(PGOContextualProfile &&) = default;
+
+  GlobalValue::GUID guid() const { return GUID; }
+  const SmallVectorImpl<uint64_t> &counters() const { return Counters; }
+  const CallsiteMapTy &callsites() const { return Callsites; }
+  CallsiteMapTy &callsites() { return Callsites; }
+
+  bool hasCallsite(uint32_t I) const {
+    return Callsites.find(I) != Callsites.end();
+  }
+
+  const CallTargetMapTy &callsite(uint32_t I) const {
+    return Callsites.find(I)->second;
+  }
+  void getContainedGuids(DenseSet<GlobalValue::GUID> &Guids) const;
+};
+
+class PGOCtxProfileReader final {
+  BitstreamCursor &Cursor;
+  Expected<BitstreamEntry> advance();
+  Error readMetadata();
+  Error wrongValue(const Twine &);
+  Error unsupported(const Twine &);
+
+  Expected<std::pair<std::optional<uint32_t>, PGOContextualProfile>>
+  readContext(bool ExpectIndex);
+  bool canReadContext();
+
+public:
+  PGOCtxProfileReader(BitstreamCursor &Cursor) : Cursor(Cursor) {}
+
+  Expected<std::map<GlobalValue::GUID, PGOContextualProfile>> loadContexts();
+};
+} // namespace llvm
+#endif
\ No newline at end of file
diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
new file mode 100644
index 0000000000000..65cd4d1b0d873
--- /dev/null
+++ b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
@@ -0,0 +1,91 @@
+//===- PGOCtxProfWriter.h - Contextual Profile Writer -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares a utility for writing a contextual profile to bitstream.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_
+#define LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_
+
+#include "llvm/Bitstream/BitstreamWriter.h"
+#include "llvm/ProfileData/CtxInstrContextNode.h"
+
+namespace llvm {
+enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters };
+
+enum PGOCtxProfileBlockIDs {
+  ProfileMetadataBlockID = 100,
+  ContextNodeBlockID = ProfileMetadataBlockID + 1
+};
+
+/// Write one or more ContextNodes to the provided raw_fd_stream.
+/// The caller must destroy the PGOCtxProfileWriter object before closing the
+/// stream.
+/// The design allows serializing a bunch of contexts embedded in some other
+/// file. The overall format is:
+///
+///  [... other data written to the stream...]
+///  SubBlock(ProfileMetadataBlockID)
+///   Version
+///   SubBlock(ContextNodeBlockID)
+///     [RECORDS]
+///     SubBlock(ContextNodeBlockID)
+///       [RECORDS]
+///       [... more SubBlocks]
+///     EndBlock
+///   EndBlock
+///
+/// The "RECORDS" are bitsream records. The IDs are in CtxProfileCodes (except)
+/// for Version, which is just for metadata). All contexts will have Guid and
+/// Counters, and all but the roots have CalleeIndex. The order in which the
+/// records appear does not matter, but they must precede any subcontexts,
+/// because that helps keep the reader code simpler.
+///
+/// Subblock containment captures the context->subcontext relationship. The
+/// "next()" relationship in the raw profile, between call targets of indirect
+/// calls, are just modeled as peer subblocks where the callee index is the
+/// same.
+///
+/// Versioning: the writer may produce additional records not known by the
+/// reader. The version number indicates a more structural change.
+/// The current version, in particular, is set up to expect optional extensions
+/// like value profiling - which would appear as additional records. For
+/// example, value profiling would produce a new record with a new record ID,
+/// containing the profiled values (much like the counters)
+class PGOCtxProfileWriter final {
+  SmallVector<char, 1 << 20> Buff;
+  BitstreamWriter Writer;
+
+  void writeCounters(const ctx_profile::ContextNode &Node);
+  void writeImpl(std::optional<uint32_t> CallerIndex,
+                 const ctx_profile::ContextNode &Node);
+
+public:
+  PGOCtxProfileWriter(raw_fd_stream &Out,
+                      std::optional<unsigned> VersionOverride = std::nullopt)
+      : Writer(Buff, &Out, 0) {
+    Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID,
+                         CodeLen);
+    const auto Version = VersionOverride ? *VersionOverride : CurrentVersion;
+    Writer.EmitRecord(PGOCtxProfileRecords::Version,
+                      SmallVector<unsigned, 1>({Version}));
+  }
+
+  ~PGOCtxProfileWriter() { Writer.ExitBlock(); }
+
+  void write(const ctx_profile::ContextNode &);
+
+  // constants used in writing which a reader may find useful.
+  static constexpr unsigned CodeLen = 2;
+  static constexpr uint32_t CurrentVersion = 1;
+  static constexpr unsigned VBREncodingBits = 6;
+};
+
+} // namespace llvm
+#endif
\ No newline at end of file
diff --git a/llvm/lib/ProfileData/CMakeLists.txt b/llvm/lib/ProfileData/CMakeLists.txt
index 408f9ff01ec87..2397eebaf7b19 100644
--- a/llvm/lib/ProfileData/CMakeLists.txt
+++ b/llvm/lib/ProfileData/CMakeLists.txt
@@ -7,6 +7,8 @@ add_llvm_component_library(LLVMProfileData
   ItaniumManglingCanonicalizer.cpp
   MemProf.cpp
   MemProfReader.cpp
+  PGOCtxProfReader.cpp
+  PGOCtxProfWriter.cpp
   ProfileSummaryBuilder.cpp
   SampleProf.cpp
   SampleProfReader.cpp
diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
new file mode 100644
index 0000000000000..fa9f57f90faa5
--- /dev/null
+++ b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
@@ -0,0 +1,165 @@
+//===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Read a contextual profile into a datastructure suitable for maintenance
+// throughout IPO
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ProfileData/PGOCtxProfReader.h"
+#include "llvm/Bitstream/BitCodeEnums.h"
+#include "llvm/Bitstream/BitstreamReader.h"
+#include "llvm/ProfileData/PGOCtxProfWriter.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+
+using namespace llvm;
+
+#define EXPECT_OR_RET(LHS, RHS)                                                \
+  auto LHS = RHS;                                                              \
+  if (!LHS)                                                                    \
+    return LHS.takeError();
+
+#define RET_ON_ERR(EXPR)                                                       \
+  if (auto Err = (EXPR))                                                       \
+    return Err;
+
+Expected<PGOContextualProfile &>
+PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G,
+                                   SmallVectorImpl<uint64_t> &&Counters) {
+  auto I = Callsites[Index].insert(
+      {G, PGOContextualProfile(G, std::move(Counters))});
+  if (!I.second)
+    return make_error<StringError>(llvm::errc::invalid_argument,
+                                   "Duplicate GUID for same callsite.");
+  return I.first->second;
+}
+
+void PGOContextualProfile::getContainedGuids(
+    DenseSet<GlobalValue::GUID> &Guids) const {
+  Guids.insert(GUID);
+  for (const auto &[_, Callsite] : Callsites)
+    for (const auto &[_, Callee] : Callsite)
+      Callee.getContainedGuids(Guids);
+}
+
+Expected<BitstreamEntry> PGOCtxProfileReader::advance() {
+  return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs);
+}
+
+Error PGOCtxProfileReader::wrongValue(const Twine &Msg) {
+  return make_error<StringError>(llvm::errc::invalid_argument, Msg);
+}
+
+Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
+  return make_error<StringError>(llvm::errc::not_supported, Msg);
+}
+
+bool PGOCtxProfileReader::canReadContext() {
+  auto Blk = advance();
+  if (!Blk) {
+    consumeError(Blk.takeError());
+    return false;
+  }
+  return Blk->Kind == BitstreamEntry::SubBlock &&
+         Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID;
+}
+
+Expected<std::pair<std::optional<uint32_t>, PGOContextualProfile>>
+PGOCtxProfileReader::readContext(bool ExpectIndex) {
+  RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID));
+
+  std::optional<ctx_profile::GUID> Guid;
+  std::optional<SmallVector<uint64_t, 16>> Counters;
+  std::optional<uint32_t> CallsiteIndex;
+
+  SmallVector<uint64_t, 1> RecordValues;
+
+  // We don't prescribe the order in which the records come in, and we are ok
+  // if other unsupported records appear. We seek in the current subblock until
+  // we get all we know.
+  while (!Guid || !Counters || (ExpectIndex && !CallsiteIndex)) {
+    RecordValues.clear();
+    EXPECT_OR_RET(Entry, advance());
+    if (Entry->Kind != BitstreamEntry::Record)
+      return unsupported(
+          "Expected records before encountering more subcontexts");
+    EXPECT_OR_RET(ReadRecord,
+                  Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues));
+    switch (*ReadRecord) {
+    case PGOCtxProfileRecords::Guid: {
+      if (RecordValues.size() != 1)
+        return wrongValue("The GUID record should have exactly one value");
+      Guid = RecordValues[0];
+      break;
+    }
+    case PGOCtxProfileRecords::Counters:
+      Counters = std::move(RecordValues);
+      if (Counters->empty())
+        return wrongValue("Empty counters. At least the entry counter (one "
+                          "value) was expected");
+      break;
+    case PGOCtxProfileRecords::CalleeIndex: {
+      if (!ExpectIndex)
+        return wrongValue("The root context should not have a callee index");
+      if (RecordValues.size() != 1)
+        return wrongValue("The callee index should have exactly one value");
+      CallsiteIndex = RecordValues[0];
+      break;
+    }
+    default:
+      // OK if we see records we do not understand.
+      break;
+    }
+  }
+
+  PGOContextualProfile Ret(*Guid, std::move(*Counters));
+
+  while (canReadContext()) {
+    EXPECT_OR_RET(SC, readContext(true));
+    if (!Ret.callsites()[*SC->first]
+             .insert({SC->second.guid(), std::move(SC->second)})
+             .second)
+      return wrongValue("Duplicate");
+  }
+  return std::make_pair(CallsiteIndex, std::move(Ret));
+}
+
+Error PGOCtxProfileReader::readMetadata() {
+  EXPECT_OR_RET(Blk, advance());
+  if (Blk->Kind != BitstreamEntry::SubBlock)
+    return unsupported("Expected Version record");
+  RET_ON_ERR(
+      Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID));
+  EXPECT_OR_RET(MData, advance());
+  if (MData->Kind != BitstreamEntry::Record)
+    return unsupported("Expected Version record");
+
+  SmallVector<uint64_t, 1> Ver;
+  EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver));
+  if (*Code != PGOCtxProfileRecords::Version)
+    return unsupported("Expected Version record");
+  if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion)
+    return unsupported("Version " + std::to_string(*Code) +
+                       " is higher than supported version " +
+                       std::to_string(PGOCtxProfileWriter::CurrentVersion));
+  return Error::success();
+}
+
+Expected<std::map<GlobalValue::GUID, PGOContextualProfile>>
+PGOCtxProfileReader::loadContexts() {
+  std::map<GlobalValue::GUID, PGOContextualProfile> Ret;
+  RET_ON_ERR(readMetadata());
+  while (canReadContext()) {
+    EXPECT_OR_RET(E, readContext(false));
+    auto Key = E->second.guid();
+    if (!Ret.insert({Key, std::move(E->second)}).second)
+      return wrongValue("Duplicate roots");
+  }
+  return Ret;
+}
\ No newline at end of file
diff --git a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
new file mode 100644
index 0000000000000..e1cae92a78b8d
--- /dev/null
+++ b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
@@ -0,0 +1,49 @@
+//===- PGOCtxProfWriter.cpp - Contextual Instrumentation profile writer ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Write a contextual profile to bitstream.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ProfileData/PGOCtxProfWriter.h"
+#include "llvm/Bitstream/BitCodeEnums.h"
+
+using namespace llvm;
+using namespace llvm::ctx_profile;
+
+void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) {
+  Writer.EmitCode(bitc::UNABBREV_RECORD);
+  Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits);
+  Writer.EmitVBR(Node.counters_size(), VBREncodingBits);
+  for (auto I = 0U; I < Node.counters_size(); ++I)
+    Writer.EmitVBR64(Node.counters()[I], VBREncodingBits);
+}
+
+// recursively write all the subcontexts. We do need to traverse depth first to
+// model the context->subcontext implicitly, and since this captures call
+// stacks, we don't really need to be worried about stack overflow and we can
+// keep the implementation simple.
+void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex,
+                                    const ContextNode &Node) {
+  Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextNodeBlockID, CodeLen);
+  Writer.EmitRecord(PGOCtxProfileRecords::Guid,
+                    SmallVector<uint64_t, 1>{Node.guid()});
+  if (CallerIndex)
+    Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex,
+                      SmallVector<uint64_t, 1>{*CallerIndex});
+  writeCounters(Node);
+  for (auto I = 0U; I < Node.callsites_size(); ++I)
+    for (const auto *Subcontext = Node.subContexts()[I]; Subcontext;
+         Subcontext = Subcontext->next())
+      writeImpl(I, *Subcontext);
+  Writer.ExitBlock();
+}
+
+void PGOCtxProfileWriter::write(const ContextNode &RootNode) {
+  writeImpl(std::nullopt, RootNode);
+}
\ No newline at end of file
diff --git a/llvm/unittests/ProfileData/CMakeLists.txt b/llvm/unittests/ProfileData/CMakeLists.txt
index ce3a0a45ccf18..c92642ded8282 100644
--- a/llvm/unittests/ProfileData/CMakeLists.txt
+++ b/llvm/unittests/ProfileData/CMakeLists.txt
@@ -13,6 +13,7 @@ add_llvm_unittest(ProfileDataTests
   InstrProfTest.cpp
   ItaniumManglingCanonicalizerTest.cpp
   MemProfTest.cpp
+  PGOCtxProfReaderWriterTest.cpp
   SampleProfTest.cpp
   SymbolRemappingReaderTest.cpp
   )
diff --git a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
new file mode 100644
index 0000000000000..727fb900b942b
--- /dev/null
+++ b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
@@ -0,0 +1,266 @@
+//===-------------- PGOCtxProfReadWriteTest.cpp ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Bitstream/BitstreamReader.h"
+#include "llvm/ProfileData/CtxInstrContextNode.h"
+#include "llvm/ProfileData/PGOCtxProfReader.h"
+#include "llvm/ProfileData/PGOCtxProfWriter.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/SupportHelpers.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::ctx_profile;
+
+class PGOCtxProfRWTest : public ::testing::Test {
+  std::vector<std::unique_ptr<char[]>> Nodes;
+  std::map<GUID, const ContextNode *> Roots;
+
+public:
+  ContextNode *createNode(GUID Guid, uint32_t NrCounters, uint32_t NrCallsites,
+                          ContextNode *Next = nullptr) {
+    auto AllocSize = ContextNode::getAllocSize(NrCounters, NrCallsites);
+    auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get();
+    std::memset(Mem, 0, AllocSize);
+    auto *Ret = new (Mem) ContextNode(Guid, NrCounters, NrCallsites, Next);
+    return Ret;
+  }
+
+  void SetUp() override {
+    // Root (guid 1) has 2 callsites, one used for an indirect call to either
+    // guid 2 or 4.
+    // guid 2 calls guid 5
+    // guid 5 calls guid 2
+    // there's also a second root, guid3.
+    auto *Root1 = createNode(1, 2, 2);
+    Root1->counters()[0] = 10;
+    Root1->counters()[1] = 11;
+    Roots.insert({1, Root1});
+    auto *L1 = createNode(2, 1, 1);
+    L1->counters()[0] = 12;
+    Root1->subContexts()[1] = createNode(4, 3, 1, L1);
+    Root1->subContexts()[1]->counters()[0] = 13;
+    Root1->subContexts()[1]->counters()[1] = 14;
+    Root1->subContexts()[1]->counters()[2] = 15;
+
+    auto *L3 = createNode(5, 6, 3);
+    for (auto I = 0; I < 6; ++I)
+      L3->counters()[I] = 16 + I;
+    L1->subContexts()[0] = L3;
+    L3->subContexts()[2] = createNode(2, 1, 1);
+    L3->subContexts()[2]->counters()[0] = 30;
+    auto *Root2 = createNode(3, 1, 0);
+    Root2->counters()[0] = 40;
+    Roots.insert({3, Root2});
+  }
+
+  const std::map<GUID, const ContextNode *> &roots() const { return Roots; }
+};
+
+void checkSame(const ContextNode &Raw, const PGOContextualProfile &Profile) {
+  EXPECT_EQ(Raw.guid(), Profile.guid());
+  ASSERT_EQ(Raw.counters_size(), Profile.counters().size());
+  for (auto I = 0U; I < Raw.counters_size(); ++I)
+    EXPECT_EQ(Raw.counters()[I], Profile.counters()[I]);
+
+  for (auto I = 0U; I < Raw.callsites_size(); ++I) {
+    if (Raw.subContexts()[I] == nullptr)
+      continue;
+    EXPECT_TRUE(Profile.hasCallsite(I));
+    const auto &ProfileTargets = Profile.callsite(I);
+
+    std::map<GUID, const ContextNode *> Targets;
+    for (const auto *N = Raw.subContexts()[I]; N; N = N->next())
+      EXPECT_TRUE(Targets.insert({N->guid(), N}).second);
+
+    EXPECT_EQ(Targets.size(), ProfileTargets.size());
+    for (auto It : Targets) {
+      auto PIt = ProfileTargets.find(It.second->guid());
+      EXPECT_NE(PIt, ProfileTargets.end());
+      checkSame(*It.second, PIt->second);
+    }
+  }
+}
+
+TEST_F(PGOCtxProfRWTest, RoundTrip) {
+  llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
+  {
+    std::error_code EC;
+    raw_fd_stream Out(ProfileFile.path(), EC);
+    ASSERT_FALSE(EC);
+    {
+      PGOCtxProfileWriter Writer(Out);
+      for (auto &[_, R] : roots())
+        Writer.write(*R);
+    }
+    Out.flush();
+    Out.close();
+  }
+  {
+    auto MB = MemoryBuffer::getFile(ProfileFile.path());
+    ASSERT_TRUE(!!MB);
+    ASSERT_NE(*MB, nullptr);
+    BitstreamCursor Cursor((*MB)->getBuffer());
+    PGOCtxProfileReader Reader(Cursor);
+    auto Expected = Reader.loadContexts();
+    ASSERT_TRUE(!!Expected);
+    auto &Ctxes = *Expected;
+    EXPECT_EQ(Ctxes.size(), roots().size());
+    EXPECT_EQ(Ctxes.size(), 2U);
+    for (auto &[G, R] : roots())
+      checkSame(*R, Ctxes.find(G)->second);
+  }
+}
+
+TEST_F(PGOCtxProfRWTest, InvalidCounters) {
+  auto *R = createNode(1, 0, 1);
+  llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
+  {
+    std::error_code EC;
+    raw_fd_stream Out(ProfileFile.path(), EC);
+    ASSERT_FALSE(EC);
+    {
+      PGOCtxProfileWriter Writer(Out);
+      Writer.write(*R);
+    }
+    Out.flush();
+    Out.close();
+  }
+  {
+    auto MB = MemoryBuffer::getFile(ProfileFile.path());
+    ASSERT_TRUE(!!MB);
+    ASSERT_NE(*MB, nullptr);
+    BitstreamCursor Cursor((*MB)->getBuffer());
+    PGOCtxProfileReader Reader(Cursor);
+    auto Expected = Reader.loadContexts();
+    EXPECT_FALSE(Expected);
+    consumeError(Expected.takeError());
+  }
+}
+
+TEST_F(PGOCtxProfRWTest, Empty) {
+  BitstreamCursor Cursor("");
+  PGOCtxProfileReader Reader(Cursor);
+  auto Expected = Reader.loadContexts();
+  EXPECT_FALSE(Expected);
+  consumeError(Expected.takeError());
+}
+
+TEST_F(PGOCtxProfRWTest, Invalid) {
+  BitstreamCursor Cursor("Surely this is not valid");
+  PGOCtxProfileReader Reader(Cursor);
+  auto Expected = Reader.loadContexts();
+  EXPECT_FALSE(Expected);
+  consumeError(Expected.takeError());
+}
+
+TEST_F(PGOCtxProfRWTest, ValidButEmpty) {
+  llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
+  {
+    std::error_code EC;
+    raw_fd_stream Out(ProfileFile.path(), EC);
+    ASSERT_FALSE(EC);
+    {
+      PGOCtxProfileWriter Writer(Out);
+      // don't write anything - this will just produce the metadata subblock.
+    }
+    Out.flush();
+    Out.close();
+  }
+  {
+    auto MB = MemoryBuffer::getFile(ProfileFile.path());
+    ASSERT_TRUE(!!MB);
+    ASSERT_NE(*MB, nullptr);
+    BitstreamCursor Cursor((*MB)->getBuffer());
+    PGOCtxProfileReader Reader(Cursor);
+    auto Expected = Reader.loadContexts();
+    EXPECT_TRUE(!!Expected);
+    EXPECT_TRUE(Expected->empty());
+  }
+}
+
+TEST_F(PGOCtxProfRWTest, WrongVersion) {
+  llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
+  {
+    std::error_code EC;
+    raw_fd_stream Out(ProfileFile.path(), EC);
+    ASSERT_FALSE(EC);
+    {
+      PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1);
+    }
+    Out.flush();
+    Out.close();
+  }
+  {
+    auto MB = MemoryBuffer::getFile(ProfileFile.path());
+    ASSERT_TRUE(!!MB);
+    ASSERT_NE(*MB, nullptr);
+    BitstreamCursor Cursor((*MB)->getBuffer());
+    PGOCtxProfileReader Reader(Cursor);
+    auto Expected = Reader.loadContexts();
+    EXPECT_FALSE(Expected);
+    consumeError(Expected.takeError());
+  }
+}
+
+TEST_F(PGOCtxProfRWTest, DuplicateRoots) {
+  llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
+  {
+    std::error_code EC;
+    raw_fd_stream Out(ProfileFile.path(), EC);
+    ASSERT_FALSE(EC);
+    {
+      PGOCtxProfileWriter Writer(Out);
+      Writer.write(*createNode(1, 1, 1));
+      Writer.write(*createNode(1, 1, 1));
+    }
+    Out.flush();
+    Out.close();
+  }
+  {
+    auto MB = MemoryBuffer::getFile(ProfileFile.path());
+    ASSERT_TRUE(!!MB);
+    ASSERT_NE(*MB, nullptr);
+    BitstreamCursor Cursor((*MB)->getBuffer());
+    PGOCtxProfileReader Reader(Cursor);
+    auto Expected = Reader.loadContexts();
+    EXPECT_FALSE(Expected);
+    consumeError(Expected.takeError());
+  }
+}
+
+TEST_F(PGOCtxProfRWTest, DuplicateTargets) {
+  llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true);
+  {
+    std::error_code EC;
+    raw_fd_stream Out(ProfileFile.path(), EC);
+    ASSERT_FALSE(EC);
+    {
+      auto *R = createNode(1, 1, 1);
+      auto *L1 = createNode(2, 1, 0);
+      auto *L2 = createNode(2, 1, 0, L1);
+      R->subContexts()[0] = L2;
+      PGOCtxProfileWriter Writer(Out);
+      Writer.write(*R);
+    }
+    Out.flush();
+    Out.close();
+  }
+  {
+    auto MB = MemoryBuffer::getFile(ProfileFile.path());
+    ASSERT_TRUE(!!MB);
+    ASSERT_NE(*MB, nullptr);
+    BitstreamCursor Cursor((*MB)->getBuffer());
+    PGOCtxProfileReader Reader(Cursor);
+    auto Expected = Reader.loadContexts();
+    EXPECT_FALSE(Expected);
+    consumeError(Expected.takeError());
+  }
+}
\ No newline at end of file

>From 5156a1ccf06073dfae8ad980d46886caf7e8a21e Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Mon, 13 May 2024 13:23:07 -0700
Subject: [PATCH 2/5] stylistic stuff

---
 llvm/lib/ProfileData/PGOCtxProfReader.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
index fa9f57f90faa5..0fddfb5b2029d 100644
--- a/llvm/lib/ProfileData/PGOCtxProfReader.cpp
+++ b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
@@ -92,28 +92,27 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
     EXPECT_OR_RET(ReadRecord,
                   Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues));
     switch (*ReadRecord) {
-    case PGOCtxProfileRecords::Guid: {
+    case PGOCtxProfileRecords::Guid:
       if (RecordValues.size() != 1)
         return wrongValue("The GUID record should have exactly one value");
       Guid = RecordValues[0];
       break;
-    }
     case PGOCtxProfileRecords::Counters:
       Counters = std::move(RecordValues);
       if (Counters->empty())
         return wrongValue("Empty counters. At least the entry counter (one "
                           "value) was expected");
       break;
-    case PGOCtxProfileRecords::CalleeIndex: {
+    case PGOCtxProfileRecords::CalleeIndex:
       if (!ExpectIndex)
         return wrongValue("The root context should not have a callee index");
       if (RecordValues.size() != 1)
         return wrongValue("The callee index should have exactly one value");
       CallsiteIndex = RecordValues[0];
       break;
-    }
     default:
-      // OK if we see records we do not understand.
+      // OK if we see records we do not understand, like records (profiles)
+      // introduced later.
       break;
     }
   }

>From 169774ab44282f73768f60a36b76ce58e118e76a Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Mon, 13 May 2024 15:35:23 -0700
Subject: [PATCH 3/5] missing newlines at eof

---
 llvm/include/llvm/ProfileData/PGOCtxProfReader.h          | 2 +-
 llvm/include/llvm/ProfileData/PGOCtxProfWriter.h          | 2 +-
 llvm/lib/ProfileData/PGOCtxProfReader.cpp                 | 2 +-
 llvm/lib/ProfileData/PGOCtxProfWriter.cpp                 | 2 +-
 llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
index e5219504163b6..2c8a0f281105c 100644
--- a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
+++ b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
@@ -88,4 +88,4 @@ class PGOCtxProfileReader final {
   Expected<std::map<GlobalValue::GUID, PGOContextualProfile>> loadContexts();
 };
 } // namespace llvm
-#endif
\ No newline at end of file
+#endif
diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
index 65cd4d1b0d873..15578c51a4957 100644
--- a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
+++ b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
@@ -88,4 +88,4 @@ class PGOCtxProfileWriter final {
 };
 
 } // namespace llvm
-#endif
\ No newline at end of file
+#endif
diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
index 0fddfb5b2029d..0de08841e4cf2 100644
--- a/llvm/lib/ProfileData/PGOCtxProfReader.cpp
+++ b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
@@ -161,4 +161,4 @@ PGOCtxProfileReader::loadContexts() {
       return wrongValue("Duplicate roots");
   }
   return Ret;
-}
\ No newline at end of file
+}
diff --git a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
index e1cae92a78b8d..91506235fd51e 100644
--- a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
+++ b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
@@ -46,4 +46,4 @@ void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex,
 
 void PGOCtxProfileWriter::write(const ContextNode &RootNode) {
   writeImpl(std::nullopt, RootNode);
-}
\ No newline at end of file
+}
diff --git a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
index 727fb900b942b..c9c856498ac23 100644
--- a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
+++ b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
@@ -263,4 +263,4 @@ TEST_F(PGOCtxProfRWTest, DuplicateTargets) {
     EXPECT_FALSE(Expected);
     consumeError(Expected.takeError());
   }
-}
\ No newline at end of file
+}

>From b7f0ce5b791fc3825f6e067109fe42e5d00bd5ee Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Mon, 13 May 2024 18:33:45 -0700
Subject: [PATCH 4/5] feedback

---
 llvm/include/llvm/ProfileData/PGOCtxProfReader.h     |  1 +
 llvm/lib/ProfileData/PGOCtxProfReader.cpp            |  9 ++++++---
 .../ProfileData/PGOCtxProfReaderWriterTest.cpp       | 12 ------------
 3 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
index 2c8a0f281105c..a19b3f51d642d 100644
--- a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
+++ b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h
@@ -66,6 +66,7 @@ class PGOContextualProfile final {
   }
 
   const CallTargetMapTy &callsite(uint32_t I) const {
+    assert(hasCallsite(I) && "Callsite not found");
     return Callsites.find(I)->second;
   }
   void getContainedGuids(DenseSet<GlobalValue::GUID> &Guids) const;
diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
index 0de08841e4cf2..3acd204bbee09 100644
--- a/llvm/lib/ProfileData/PGOCtxProfReader.cpp
+++ b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
@@ -14,12 +14,15 @@
 #include "llvm/ProfileData/PGOCtxProfReader.h"
 #include "llvm/Bitstream/BitCodeEnums.h"
 #include "llvm/Bitstream/BitstreamReader.h"
+#include "llvm/ProfileData/InstrProf.h"
 #include "llvm/ProfileData/PGOCtxProfWriter.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
 
 using namespace llvm;
 
+// FIXME(#92054) - these Error handling macros are (re-)invented in a few
+// places.
 #define EXPECT_OR_RET(LHS, RHS)                                                \
   auto LHS = RHS;                                                              \
   if (!LHS)                                                                    \
@@ -53,11 +56,11 @@ Expected<BitstreamEntry> PGOCtxProfileReader::advance() {
 }
 
 Error PGOCtxProfileReader::wrongValue(const Twine &Msg) {
-  return make_error<StringError>(llvm::errc::invalid_argument, Msg);
+  return make_error<InstrProfError>(instrprof_error::invalid_prof, Msg);
 }
 
 Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
-  return make_error<StringError>(llvm::errc::not_supported, Msg);
+  return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg);
 }
 
 bool PGOCtxProfileReader::canReadContext() {
@@ -87,7 +90,7 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
     RecordValues.clear();
     EXPECT_OR_RET(Entry, advance());
     if (Entry->Kind != BitstreamEntry::Record)
-      return unsupported(
+      return wrongValue(
           "Expected records before encountering more subcontexts");
     EXPECT_OR_RET(ReadRecord,
                   Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues));
diff --git a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
index c9c856498ac23..54b67a91ebcec 100644
--- a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
+++ b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
@@ -100,8 +100,6 @@ TEST_F(PGOCtxProfRWTest, RoundTrip) {
       for (auto &[_, R] : roots())
         Writer.write(*R);
     }
-    Out.flush();
-    Out.close();
   }
   {
     auto MB = MemoryBuffer::getFile(ProfileFile.path());
@@ -130,8 +128,6 @@ TEST_F(PGOCtxProfRWTest, InvalidCounters) {
       PGOCtxProfileWriter Writer(Out);
       Writer.write(*R);
     }
-    Out.flush();
-    Out.close();
   }
   {
     auto MB = MemoryBuffer::getFile(ProfileFile.path());
@@ -171,8 +167,6 @@ TEST_F(PGOCtxProfRWTest, ValidButEmpty) {
       PGOCtxProfileWriter Writer(Out);
       // don't write anything - this will just produce the metadata subblock.
     }
-    Out.flush();
-    Out.close();
   }
   {
     auto MB = MemoryBuffer::getFile(ProfileFile.path());
@@ -195,8 +189,6 @@ TEST_F(PGOCtxProfRWTest, WrongVersion) {
     {
       PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1);
     }
-    Out.flush();
-    Out.close();
   }
   {
     auto MB = MemoryBuffer::getFile(ProfileFile.path());
@@ -221,8 +213,6 @@ TEST_F(PGOCtxProfRWTest, DuplicateRoots) {
       Writer.write(*createNode(1, 1, 1));
       Writer.write(*createNode(1, 1, 1));
     }
-    Out.flush();
-    Out.close();
   }
   {
     auto MB = MemoryBuffer::getFile(ProfileFile.path());
@@ -250,8 +240,6 @@ TEST_F(PGOCtxProfRWTest, DuplicateTargets) {
       PGOCtxProfileWriter Writer(Out);
       Writer.write(*R);
     }
-    Out.flush();
-    Out.close();
   }
   {
     auto MB = MemoryBuffer::getFile(ProfileFile.path());

>From a0643572d2004d4c11a1e673e93b5762f4d79afd Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Tue, 14 May 2024 14:59:26 -0700
Subject: [PATCH 5/5] feedback

---
 llvm/lib/ProfileData/PGOCtxProfReader.cpp     | 34 +++++++++++--------
 llvm/lib/ProfileData/PGOCtxProfWriter.cpp     |  4 +--
 .../PGOCtxProfReaderWriterTest.cpp            |  3 +-
 3 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
index 3acd204bbee09..3710f2e4b8185 100644
--- a/llvm/lib/ProfileData/PGOCtxProfReader.cpp
+++ b/llvm/lib/ProfileData/PGOCtxProfReader.cpp
@@ -35,12 +35,12 @@ using namespace llvm;
 Expected<PGOContextualProfile &>
 PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G,
                                    SmallVectorImpl<uint64_t> &&Counters) {
-  auto I = Callsites[Index].insert(
+  auto [Iter, Inserted] = Callsites[Index].insert(
       {G, PGOContextualProfile(G, std::move(Counters))});
-  if (!I.second)
-    return make_error<StringError>(llvm::errc::invalid_argument,
-                                   "Duplicate GUID for same callsite.");
-  return I.first->second;
+  if (!Inserted)
+    return make_error<InstrProfError>(instrprof_error::invalid_prof,
+                                      "Duplicate GUID for same callsite.");
+  return Iter->second;
 }
 
 void PGOContextualProfile::getContainedGuids(
@@ -86,7 +86,11 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
   // We don't prescribe the order in which the records come in, and we are ok
   // if other unsupported records appear. We seek in the current subblock until
   // we get all we know.
-  while (!Guid || !Counters || (ExpectIndex && !CallsiteIndex)) {
+  auto GotAllWeNeed = [&]() {
+    return Guid.has_value() && Counters.has_value() &&
+           (!ExpectIndex || CallsiteIndex.has_value());
+  };
+  while (!GotAllWeNeed()) {
     RecordValues.clear();
     EXPECT_OR_RET(Entry, advance());
     if (Entry->Kind != BitstreamEntry::Record)
@@ -114,8 +118,8 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
       CallsiteIndex = RecordValues[0];
       break;
     default:
-      // OK if we see records we do not understand, like records (profiles)
-      // introduced later.
+      // OK if we see records we do not understand, like records (profile
+      // components) introduced later.
       break;
     }
   }
@@ -124,10 +128,12 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
 
   while (canReadContext()) {
     EXPECT_OR_RET(SC, readContext(true));
-    if (!Ret.callsites()[*SC->first]
-             .insert({SC->second.guid(), std::move(SC->second)})
-             .second)
-      return wrongValue("Duplicate");
+    auto &Targets = Ret.callsites()[*SC->first];
+    auto [_, Inserted] =
+        Targets.insert({SC->second.guid(), std::move(SC->second)});
+    if (!Inserted)
+      return wrongValue(
+          "Unexpected duplicate target (callee) at the same callsite.");
   }
   return std::make_pair(CallsiteIndex, std::move(Ret));
 }
@@ -147,9 +153,9 @@ Error PGOCtxProfileReader::readMetadata() {
   if (*Code != PGOCtxProfileRecords::Version)
     return unsupported("Expected Version record");
   if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion)
-    return unsupported("Version " + std::to_string(*Code) +
+    return unsupported("Version " + Twine(*Code) +
                        " is higher than supported version " +
-                       std::to_string(PGOCtxProfileWriter::CurrentVersion));
+                       Twine(PGOCtxProfileWriter::CurrentVersion));
   return Error::success();
 }
 
diff --git a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
index 91506235fd51e..5081797564469 100644
--- a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
+++ b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp
@@ -20,7 +20,7 @@ void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) {
   Writer.EmitCode(bitc::UNABBREV_RECORD);
   Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits);
   Writer.EmitVBR(Node.counters_size(), VBREncodingBits);
-  for (auto I = 0U; I < Node.counters_size(); ++I)
+  for (uint32_t I = 0U; I < Node.counters_size(); ++I)
     Writer.EmitVBR64(Node.counters()[I], VBREncodingBits);
 }
 
@@ -37,7 +37,7 @@ void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex,
     Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex,
                       SmallVector<uint64_t, 1>{*CallerIndex});
   writeCounters(Node);
-  for (auto I = 0U; I < Node.callsites_size(); ++I)
+  for (uint32_t I = 0U; I < Node.callsites_size(); ++I)
     for (const auto *Subcontext = Node.subContexts()[I]; Subcontext;
          Subcontext = Subcontext->next())
       writeImpl(I, *Subcontext);
diff --git a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
index 54b67a91ebcec..d2cdbb28e2fce 100644
--- a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
+++ b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
@@ -102,7 +102,8 @@ TEST_F(PGOCtxProfRWTest, RoundTrip) {
     }
   }
   {
-    auto MB = MemoryBuffer::getFile(ProfileFile.path());
+    ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
+        MemoryBuffer::getFile(ProfileFile.path());
     ASSERT_TRUE(!!MB);
     ASSERT_NE(*MB, nullptr);
     BitstreamCursor Cursor((*MB)->getBuffer());



More information about the llvm-commits mailing list