"Reapply "[ctx_profile] Profile reader and writer" (#92199)"
This reverts commit 2c54bf497f.
Fixed gcc-7 issue.
This commit is contained in:
92
llvm/include/llvm/ProfileData/PGOCtxProfReader.h
Normal file
92
llvm/include/llvm/ProfileData/PGOCtxProfReader.h
Normal file
@@ -0,0 +1,92 @@
|
||||
//===--- 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 {
|
||||
assert(hasCallsite(I) && "Callsite not found");
|
||||
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
|
||||
91
llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
Normal file
91
llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
Normal file
@@ -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
|
||||
@@ -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
|
||||
@@ -20,6 +22,7 @@ add_llvm_component_library(LLVMProfileData
|
||||
intrinsics_gen
|
||||
|
||||
LINK_COMPONENTS
|
||||
BitstreamReader
|
||||
Core
|
||||
Object
|
||||
Support
|
||||
|
||||
173
llvm/lib/ProfileData/PGOCtxProfReader.cpp
Normal file
173
llvm/lib/ProfileData/PGOCtxProfReader.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
//===- 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/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) \
|
||||
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 [Iter, Inserted] = Callsites[Index].insert(
|
||||
{G, PGOContextualProfile(G, std::move(Counters))});
|
||||
if (!Inserted)
|
||||
return make_error<InstrProfError>(instrprof_error::invalid_prof,
|
||||
"Duplicate GUID for same callsite.");
|
||||
return Iter->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<InstrProfError>(instrprof_error::invalid_prof, Msg);
|
||||
}
|
||||
|
||||
Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
|
||||
return make_error<InstrProfError>(instrprof_error::unsupported_version, 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.
|
||||
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)
|
||||
return wrongValue(
|
||||
"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, like records (profile
|
||||
// components) introduced later.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PGOContextualProfile Ret(*Guid, std::move(*Counters));
|
||||
|
||||
while (canReadContext()) {
|
||||
EXPECT_OR_RET(SC, readContext(true));
|
||||
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));
|
||||
}
|
||||
|
||||
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 " + Twine(*Code) +
|
||||
" is higher than supported version " +
|
||||
Twine(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 std::move(Ret);
|
||||
}
|
||||
49
llvm/lib/ProfileData/PGOCtxProfWriter.cpp
Normal file
49
llvm/lib/ProfileData/PGOCtxProfWriter.cpp
Normal file
@@ -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 (uint32_t 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 (uint32_t 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);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ add_llvm_unittest(ProfileDataTests
|
||||
InstrProfTest.cpp
|
||||
ItaniumManglingCanonicalizerTest.cpp
|
||||
MemProfTest.cpp
|
||||
PGOCtxProfReaderWriterTest.cpp
|
||||
SampleProfTest.cpp
|
||||
SymbolRemappingReaderTest.cpp
|
||||
)
|
||||
|
||||
255
llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
Normal file
255
llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
//===-------------- 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);
|
||||
}
|
||||
}
|
||||
{
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> 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);
|
||||
}
|
||||
}
|
||||
{
|
||||
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.
|
||||
}
|
||||
}
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user