[ctxprof] Prepare profile format for flat profiles (#129626)

The profile format has now a separate section called "Contexts" - there will be a corresponding one for flat profiles. The root has a separate tag because, in addition to not having a callsite ID as all the other context nodes have under it, it will have additional fields in subsequent patches.

The rest of this patch amounts to a bit of refactorings in the reader/writer (for better reuse later) and tests fixups.
This commit is contained in:
Mircea Trofin 2025-03-05 07:22:35 -08:00 committed by GitHub
parent bdbc434498
commit 5223ddd83f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 470 additions and 264 deletions

View File

@ -114,9 +114,14 @@ public:
};
/// Abstraction for the parameter passed to `__llvm_ctx_profile_fetch`.
/// `startContextSection` is called before any context roots are sent for
/// writing. Then one or more `writeContextual` calls are made; finally,
/// `endContextSection` is called.
class ProfileWriter {
public:
virtual void startContextSection() = 0;
virtual void writeContextual(const ctx_profile::ContextNode &RootNode) = 0;
virtual void endContextSection() = 0;
virtual ~ProfileWriter() = default;
};
} // namespace ctx_profile

View File

@ -298,6 +298,7 @@ bool __llvm_ctx_profile_fetch(ProfileWriter &Writer) {
__sanitizer::GenericScopedLock<__sanitizer::SpinMutex> Lock(
&AllContextsMutex);
Writer.startContextSection();
for (int I = 0, E = AllContextRoots.Size(); I < E; ++I) {
auto *Root = AllContextRoots[I];
__sanitizer::GenericScopedLock<__sanitizer::StaticSpinMutex> TakenLock(
@ -308,6 +309,7 @@ bool __llvm_ctx_profile_fetch(ProfileWriter &Writer) {
}
Writer.writeContextual(*Root->FirstNode);
}
Writer.endContextSection();
return true;
}

View File

@ -183,11 +183,18 @@ TEST_F(ContextTest, Dump) {
public:
ContextRoot *const Root;
const size_t Entries;
int EnteredSectionCount = 0;
int ExitedSectionCount = 0;
bool State = false;
TestProfileWriter(ContextRoot *Root, size_t Entries)
: Root(Root), Entries(Entries) {}
void writeContextual(const ContextNode &Node) override {
EXPECT_EQ(EnteredSectionCount, 1);
EXPECT_EQ(ExitedSectionCount, 0);
EXPECT_FALSE(Root->Taken.TryLock());
EXPECT_EQ(Node.guid(), 1U);
EXPECT_EQ(Node.counters()[0], Entries);
@ -205,7 +212,13 @@ TEST_F(ContextTest, Dump) {
EXPECT_EQ(SN.subContexts()[0], nullptr);
State = true;
}
void startContextSection() override { ++EnteredSectionCount; }
void endContextSection() override {
EXPECT_EQ(EnteredSectionCount, 1);
++ExitedSectionCount;
}
};
TestProfileWriter W(&Root, 1);
EXPECT_FALSE(W.State);
__llvm_ctx_profile_fetch(W);
@ -217,4 +230,6 @@ TEST_F(ContextTest, Dump) {
EXPECT_FALSE(W2.State);
__llvm_ctx_profile_fetch(W2);
EXPECT_TRUE(W2.State);
EXPECT_EQ(W2.EnteredSectionCount, 1);
EXPECT_EQ(W2.ExitedSectionCount, 1);
}

View File

@ -62,7 +62,14 @@ class TestProfileWriter : public ProfileWriter {
}
}
public:
void startContextSection() override {
std::cout << "Entered Context Section" << std::endl;
}
void endContextSection() override {
std::cout << "Exited Context Section" << std::endl;
}
void writeContextual(const ContextNode &RootNode) override {
printProfile(RootNode, "", "");
}
@ -77,6 +84,7 @@ public:
// path gets instrumented).
// The second context is in the loop. We expect 2 entries and each of the
// branches would be taken once, so the second counter is 1.
// CHECK-NEXT: Entered Context Section
// CHECK-NEXT: Guid: 8657661246551306189
// CHECK-NEXT: Entries: 1
// CHECK-NEXT: 2 counters and 3 callsites
@ -91,6 +99,7 @@ public:
// CHECK-NEXT: Entries: 2
// CHECK-NEXT: 2 counters and 2 callsites
// CHECK-NEXT: Counter values: 2 1
// CHECK-NEXT: Exited Context Section
bool profileWriter() {
TestProfileWriter W;

View File

@ -54,6 +54,8 @@ public:
return Profiles.Contexts;
}
const PGOCtxProfile &profiles() const { return Profiles; }
bool isFunctionKnown(const Function &F) const {
return getDefinedFunctionGUID(F) != 0;
}

View File

@ -114,9 +114,14 @@ public:
};
/// Abstraction for the parameter passed to `__llvm_ctx_profile_fetch`.
/// `startContextSection` is called before any context roots are sent for
/// writing. Then one or more `writeContextual` calls are made; finally,
/// `endContextSection` is called.
class ProfileWriter {
public:
virtual void startContextSection() = 0;
virtual void writeContextual(const ctx_profile::ContextNode &RootNode) = 0;
virtual void endContextSection() = 0;
virtual ~ProfileWriter() = default;
};
} // namespace ctx_profile

View File

@ -190,8 +190,12 @@ class PGOCtxProfileReader final {
Error unsupported(const Twine &);
Expected<std::pair<std::optional<uint32_t>, PGOCtxProfContext>>
readContext(bool ExpectIndex);
bool canReadContext();
readProfile(PGOCtxProfileBlockIDs Kind);
bool canEnterBlockWithID(PGOCtxProfileBlockIDs ID);
Error enterBlockWithID(PGOCtxProfileBlockIDs ID);
Error loadContexts(CtxProfContextualProfiles &);
public:
PGOCtxProfileReader(StringRef Buffer)
@ -201,7 +205,6 @@ public:
Expected<PGOCtxProfile> loadProfiles();
};
void convertCtxProfToYaml(raw_ostream &OS,
const PGOCtxProfContext::CallTargetMapTy &);
void convertCtxProfToYaml(raw_ostream &OS, const PGOCtxProfile &);
} // namespace llvm
#endif

View File

@ -23,7 +23,9 @@ enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters };
enum PGOCtxProfileBlockIDs {
ProfileMetadataBlockID = bitc::FIRST_APPLICATION_BLOCKID,
ContextNodeBlockID = ProfileMetadataBlockID + 1
ContextsSectionBlockID = ProfileMetadataBlockID + 1,
ContextRootBlockID = ContextsSectionBlockID + 1,
ContextNodeBlockID = ContextRootBlockID + 1,
};
/// Write one or more ContextNodes to the provided raw_fd_stream.
@ -60,23 +62,30 @@ enum PGOCtxProfileBlockIDs {
/// 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 {
BitstreamWriter Writer;
class PGOCtxProfileWriter : public ctx_profile::ProfileWriter {
enum class EmptyContextCriteria { None, EntryIsZero, AllAreZero };
void writeCounters(const ctx_profile::ContextNode &Node);
BitstreamWriter Writer;
const bool IncludeEmpty;
void writeGuid(ctx_profile::GUID Guid);
void writeCounters(ArrayRef<uint64_t> Counters);
void writeImpl(std::optional<uint32_t> CallerIndex,
const ctx_profile::ContextNode &Node);
public:
PGOCtxProfileWriter(raw_ostream &Out,
std::optional<unsigned> VersionOverride = std::nullopt);
std::optional<unsigned> VersionOverride = std::nullopt,
bool IncludeEmpty = false);
~PGOCtxProfileWriter() { Writer.ExitBlock(); }
void write(const ctx_profile::ContextNode &);
void startContextSection() override;
void writeContextual(const ctx_profile::ContextNode &RootNode) override;
void endContextSection() override;
// constants used in writing which a reader may find useful.
static constexpr unsigned CodeLen = 2;
static constexpr uint32_t CurrentVersion = 1;
static constexpr uint32_t CurrentVersion = 2;
static constexpr unsigned VBREncodingBits = 6;
static constexpr StringRef ContainerMagic = "CTXP";
};

View File

@ -180,7 +180,7 @@ PreservedAnalyses CtxProfAnalysisPrinterPass::run(Module &M,
if (Mode == PrintMode::Everything)
OS << "\nCurrent Profile:\n";
convertCtxProfToYaml(OS, C.contexts());
convertCtxProfToYaml(OS, C.profiles());
OS << "\n";
if (Mode == PrintMode::YAML)
return PreservedAnalyses::all();

View File

@ -19,7 +19,6 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/YAMLTraits.h"
#include <iterator>
#include <utility>
using namespace llvm;
@ -58,19 +57,26 @@ Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg);
}
bool PGOCtxProfileReader::canReadContext() {
bool PGOCtxProfileReader::canEnterBlockWithID(PGOCtxProfileBlockIDs ID) {
auto Blk = advance();
if (!Blk) {
consumeError(Blk.takeError());
return false;
}
return Blk->Kind == BitstreamEntry::SubBlock &&
Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID;
return Blk->Kind == BitstreamEntry::SubBlock && Blk->ID == ID;
}
Error PGOCtxProfileReader::enterBlockWithID(PGOCtxProfileBlockIDs ID) {
RET_ON_ERR(Cursor.EnterSubBlock(ID));
return Error::success();
}
Expected<std::pair<std::optional<uint32_t>, PGOCtxProfContext>>
PGOCtxProfileReader::readContext(bool ExpectIndex) {
RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID));
PGOCtxProfileReader::readProfile(PGOCtxProfileBlockIDs Kind) {
assert((Kind == PGOCtxProfileBlockIDs::ContextRootBlockID ||
Kind == PGOCtxProfileBlockIDs::ContextNodeBlockID) &&
"Unexpected profile kind");
RET_ON_ERR(enterBlockWithID(Kind));
std::optional<ctx_profile::GUID> Guid;
std::optional<SmallVector<uint64_t, 16>> Counters;
@ -78,6 +84,7 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
SmallVector<uint64_t, 1> RecordValues;
const bool ExpectIndex = Kind == PGOCtxProfileBlockIDs::ContextNodeBlockID;
// 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.
@ -121,8 +128,8 @@ PGOCtxProfileReader::readContext(bool ExpectIndex) {
PGOCtxProfContext Ret(*Guid, std::move(*Counters));
while (canReadContext()) {
EXPECT_OR_RET(SC, readContext(true));
while (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextNodeBlockID)) {
EXPECT_OR_RET(SC, readProfile(PGOCtxProfileBlockIDs::ContextNodeBlockID));
auto &Targets = Ret.callsites()[*SC->first];
auto [_, Inserted] =
Targets.insert({SC->second.guid(), std::move(SC->second)});
@ -168,15 +175,23 @@ Error PGOCtxProfileReader::readMetadata() {
return Error::success();
}
Expected<PGOCtxProfile> PGOCtxProfileReader::loadProfiles() {
PGOCtxProfile Ret;
RET_ON_ERR(readMetadata());
while (canReadContext()) {
EXPECT_OR_RET(E, readContext(false));
auto Key = E->second.guid();
if (!Ret.Contexts.insert({Key, std::move(E->second)}).second)
return wrongValue("Duplicate roots");
Error PGOCtxProfileReader::loadContexts(CtxProfContextualProfiles &P) {
if (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextsSectionBlockID)) {
RET_ON_ERR(enterBlockWithID(PGOCtxProfileBlockIDs::ContextsSectionBlockID));
while (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextRootBlockID)) {
EXPECT_OR_RET(E, readProfile(PGOCtxProfileBlockIDs::ContextRootBlockID));
auto Key = E->second.guid();
if (!P.insert({Key, std::move(E->second)}).second)
return wrongValue("Duplicate roots");
}
}
return Error::success();
}
Expected<PGOCtxProfile> PGOCtxProfileReader::loadProfiles() {
RET_ON_ERR(readMetadata());
PGOCtxProfile Ret;
RET_ON_ERR(loadContexts(Ret.Contexts));
return std::move(Ret);
}
@ -224,7 +239,9 @@ void toYaml(yaml::Output &Out,
Out.endSequence();
}
void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) {
void toYaml(yaml::Output &Out, GlobalValue::GUID Guid,
const SmallVectorImpl<uint64_t> &Counters,
const PGOCtxProfContext::CallsiteMapTy &Callsites) {
yaml::EmptyContext Empty;
Out.beginMapping();
void *SaveInfo = nullptr;
@ -232,33 +249,44 @@ void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) {
{
Out.preflightKey("Guid", /*Required=*/true, /*SameAsDefault=*/false,
UseDefault, SaveInfo);
auto Guid = Ctx.guid();
yaml::yamlize(Out, Guid, true, Empty);
Out.postflightKey(nullptr);
}
{
Out.preflightKey("Counters", true, false, UseDefault, SaveInfo);
Out.beginFlowSequence();
for (size_t I = 0U, E = Ctx.counters().size(); I < E; ++I) {
for (size_t I = 0U, E = Counters.size(); I < E; ++I) {
Out.preflightFlowElement(I, SaveInfo);
uint64_t V = Ctx.counters()[I];
uint64_t V = Counters[I];
yaml::yamlize(Out, V, true, Empty);
Out.postflightFlowElement(SaveInfo);
}
Out.endFlowSequence();
Out.postflightKey(nullptr);
}
if (!Ctx.callsites().empty()) {
if (!Callsites.empty()) {
Out.preflightKey("Callsites", true, false, UseDefault, SaveInfo);
toYaml(Out, Ctx.callsites());
toYaml(Out, Callsites);
Out.postflightKey(nullptr);
}
Out.endMapping();
}
void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) {
toYaml(Out, Ctx.guid(), Ctx.counters(), Ctx.callsites());
}
} // namespace
void llvm::convertCtxProfToYaml(
raw_ostream &OS, const PGOCtxProfContext::CallTargetMapTy &Profiles) {
void llvm::convertCtxProfToYaml(raw_ostream &OS,
const PGOCtxProfile &Profiles) {
yaml::Output Out(OS);
toYaml(Out, Profiles);
}
void *SaveInfo = nullptr;
bool UseDefault = false;
Out.beginMapping();
if (!Profiles.Contexts.empty()) {
Out.preflightKey("Contexts", false, false, UseDefault, SaveInfo);
toYaml(Out, Profiles.Contexts);
Out.postflightKey(nullptr);
}
Out.endMapping();
}

View File

@ -13,17 +13,25 @@
#include "llvm/ProfileData/PGOCtxProfWriter.h"
#include "llvm/Bitstream/BitCodeEnums.h"
#include "llvm/ProfileData/CtxInstrContextNode.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::ctx_profile;
static cl::opt<bool>
IncludeEmptyOpt("ctx-prof-include-empty", cl::init(false),
cl::desc("Also write profiles with all-zero counters. "
"Intended for testing/debugging."));
PGOCtxProfileWriter::PGOCtxProfileWriter(
raw_ostream &Out, std::optional<unsigned> VersionOverride)
: Writer(Out, 0) {
raw_ostream &Out, std::optional<unsigned> VersionOverride,
bool IncludeEmpty)
: Writer(Out, 0),
IncludeEmpty(IncludeEmptyOpt.getNumOccurrences() > 0 ? IncludeEmptyOpt
: IncludeEmpty) {
static_assert(ContainerMagic.size() == 4);
Out.write(ContainerMagic.data(), ContainerMagic.size());
Writer.EnterBlockInfoBlock();
@ -43,6 +51,10 @@ PGOCtxProfileWriter::PGOCtxProfileWriter(
};
DescribeBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, "Metadata");
DescribeRecord(PGOCtxProfileRecords::Version, "Version");
DescribeBlock(PGOCtxProfileBlockIDs::ContextsSectionBlockID, "Contexts");
DescribeBlock(PGOCtxProfileBlockIDs::ContextRootBlockID, "Root");
DescribeRecord(PGOCtxProfileRecords::Guid, "GUID");
DescribeRecord(PGOCtxProfileRecords::Counters, "Counters");
DescribeBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID, "Context");
DescribeRecord(PGOCtxProfileRecords::Guid, "GUID");
DescribeRecord(PGOCtxProfileRecords::CalleeIndex, "CalleeIndex");
@ -55,12 +67,16 @@ PGOCtxProfileWriter::PGOCtxProfileWriter(
SmallVector<unsigned, 1>({Version}));
}
void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) {
void PGOCtxProfileWriter::writeCounters(ArrayRef<uint64_t> Counters) {
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);
Writer.EmitVBR(Counters.size(), VBREncodingBits);
for (uint64_t C : Counters)
Writer.EmitVBR64(C, VBREncodingBits);
}
void PGOCtxProfileWriter::writeGuid(ctx_profile::GUID Guid) {
Writer.EmitRecord(PGOCtxProfileRecords::Guid, SmallVector<uint64_t, 1>{Guid});
}
// recursively write all the subcontexts. We do need to traverse depth first to
@ -69,13 +85,18 @@ void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) {
// 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()});
// A node with no counters is an error. We don't expect this to happen from
// the runtime, rather, this is interesting for testing the reader.
if (!IncludeEmpty && (Node.counters_size() > 0 && Node.entrycount() == 0))
return;
Writer.EnterSubblock(CallerIndex ? PGOCtxProfileBlockIDs::ContextNodeBlockID
: PGOCtxProfileBlockIDs::ContextRootBlockID,
CodeLen);
writeGuid(Node.guid());
if (CallerIndex)
Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex,
SmallVector<uint64_t, 1>{*CallerIndex});
writeCounters(Node);
writeCounters({Node.counters(), Node.counters_size()});
for (uint32_t I = 0U; I < Node.callsites_size(); ++I)
for (const auto *Subcontext = Node.subContexts()[I]; Subcontext;
Subcontext = Subcontext->next())
@ -83,7 +104,13 @@ void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex,
Writer.ExitBlock();
}
void PGOCtxProfileWriter::write(const ContextNode &RootNode) {
void PGOCtxProfileWriter::startContextSection() {
Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextsSectionBlockID, CodeLen);
}
void PGOCtxProfileWriter::endContextSection() { Writer.ExitBlock(); }
void PGOCtxProfileWriter::writeContextual(const ContextNode &RootNode) {
writeImpl(std::nullopt, RootNode);
}
@ -96,6 +123,9 @@ struct SerializableCtxRepresentation {
std::vector<uint64_t> Counters;
std::vector<std::vector<SerializableCtxRepresentation>> Callsites;
};
struct SerializableProfileRepresentation {
std::vector<SerializableCtxRepresentation> Contexts;
};
ctx_profile::ContextNode *
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
@ -142,10 +172,16 @@ template <> struct yaml::MappingTraits<SerializableCtxRepresentation> {
}
};
template <> struct yaml::MappingTraits<SerializableProfileRepresentation> {
static void mapping(yaml::IO &IO, SerializableProfileRepresentation &SPR) {
IO.mapOptional("Contexts", SPR.Contexts);
}
};
Error llvm::createCtxProfFromYAML(StringRef Profile, raw_ostream &Out) {
yaml::Input In(Profile);
std::vector<SerializableCtxRepresentation> DCList;
In >> DCList;
SerializableProfileRepresentation SPR;
In >> SPR;
if (In.error())
return createStringError(In.error(), "incorrect yaml content");
std::vector<std::unique_ptr<char[]>> Nodes;
@ -153,12 +189,17 @@ Error llvm::createCtxProfFromYAML(StringRef Profile, raw_ostream &Out) {
if (EC)
return createStringError(EC, "failed to open output");
PGOCtxProfileWriter Writer(Out);
for (const auto &DC : DCList) {
auto *TopList = createNode(Nodes, DC);
if (!TopList)
return createStringError(
"Unexpected error converting internal structure to ctx profile");
Writer.write(*TopList);
if (!SPR.Contexts.empty()) {
Writer.startContextSection();
for (const auto &DC : SPR.Contexts) {
auto *TopList = createNode(Nodes, DC);
if (!TopList)
return createStringError(
"Unexpected error converting internal structure to ctx profile");
Writer.writeContextual(*TopList);
}
Writer.endContextSection();
}
if (EC)
return createStringError(EC, "failed to write output");

View File

@ -59,14 +59,15 @@
; CHECK: ![[AN_ENTRYPOINT_BW]] = !{!"branch_weights", i32 40, i32 60}
;--- profile.yaml
- Guid: 4909520559318251808
Counters: [100, 40]
Callsites: -
- Guid: 11872291593386833696
Counters: [ 100, 5 ]
-
- Guid: 11872291593386833696
Counters: [ 40, 10 ]
Contexts:
- Guid: 4909520559318251808
Counters: [100, 40]
Callsites: -
- Guid: 11872291593386833696
Counters: [ 100, 5 ]
-
- Guid: 11872291593386833696
Counters: [ 40, 10 ]
;--- example.ll
declare void @bar()

View File

@ -39,8 +39,9 @@ exit:
!0 = !{i64 1234}
;--- profile_ok.yaml
- Guid: 1234
Counters: [2, 2, 1, 2]
Contexts:
- Guid: 1234
Counters: [2, 2, 1, 2]
;--- message_pump.ll
; This is a message pump: the loop never exits. This should result in an
@ -61,8 +62,9 @@ exit:
!0 = !{i64 1234}
;--- profile_pump.yaml
- Guid: 1234
Counters: [2, 10, 0]
Contexts:
- Guid: 1234
Counters: [2, 10, 0]
;--- unreachable.ll
; An unreachable block is reached, that's an error
@ -84,5 +86,6 @@ exit:
!0 = !{i64 1234}
;--- profile_unreachable.yaml
- Guid: 1234
Counters: [2, 1, 1, 2]
Contexts:
- Guid: 1234
Counters: [2, 1, 1, 2]

View File

@ -46,17 +46,18 @@ attributes #1 = { noinline }
!2 = !{i64 4000}
;--- profile.yaml
- Guid: 4000
Counters: [10]
Callsites: -
- Guid: 3000
Counters: [10]
Callsites: -
- Guid: 1000
Counters: [10]
-
- Guid: 3000
Counters: [10]
Callsites: -
- Guid: 9000
Counters: [10]
Contexts:
- Guid: 4000
Counters: [10]
Callsites: -
- Guid: 3000
Counters: [10]
Callsites: -
- Guid: 1000
Counters: [10]
-
- Guid: 3000
Counters: [10]
Callsites: -
- Guid: 9000
Counters: [10]

View File

@ -52,5 +52,6 @@ exit:
!0 = !{i64 1234}
;--- profile.yaml
- Guid: 1234
Counters: [6,0,0,0]
Contexts:
- Guid: 1234
Counters: [6,0,0,0]

View File

@ -66,20 +66,21 @@ define void @entrypoint() {
ret void
}
;--- profile.yaml
- Guid: 10507721908651011566
Counters: [1]
Callsites: -
- Guid: 2072045998141807037
Counters: [7]
Callsites: -
- Guid: 3087265239403591524
Counters: [10, 7]
-
- Guid: 4197650231481825559
Counters: [2]
Callsites: -
- Guid: 3087265239403591524
Counters: [1, 2]
Contexts:
- Guid: 10507721908651011566
Counters: [1]
Callsites: -
- Guid: 2072045998141807037
Counters: [7]
Callsites: -
- Guid: 3087265239403591524
Counters: [10, 7]
-
- Guid: 4197650231481825559
Counters: [2]
Callsites: -
- Guid: 3087265239403591524
Counters: [1, 2]
;--- expected.txt
Function Info:
2072045998141807037 : f1. MaxCounterID: 1. MaxCallsiteID: 1
@ -89,19 +90,20 @@ Function Info:
Current Profile:
- Guid: 10507721908651011566
Counters: [ 1 ]
Callsites:
- - Guid: 2072045998141807037
Counters: [ 7 ]
Callsites:
- - Guid: 3087265239403591524
Counters: [ 10, 7 ]
- - Guid: 4197650231481825559
Counters: [ 2 ]
Callsites:
- - Guid: 3087265239403591524
Counters: [ 1, 2 ]
Contexts:
- Guid: 10507721908651011566
Counters: [ 1 ]
Callsites:
- - Guid: 2072045998141807037
Counters: [ 7 ]
Callsites:
- - Guid: 3087265239403591524
Counters: [ 10, 7 ]
- - Guid: 4197650231481825559
Counters: [ 2 ]
Callsites:
- - Guid: 3087265239403591524
Counters: [ 1, 2 ]
Flat Profile:
2072045998141807037 : 7

View File

@ -73,11 +73,12 @@ define i32 @bar(i32 %t) !guid !1 {
!1 = !{i64 5678}
;--- profile.yaml
- Guid: 1234
Counters: [10, 4]
Callsites: -
- Guid: 5678
Counters: [4,3]
-
- Guid: 5678
Counters: [6,6]
Contexts:
- Guid: 1234
Counters: [10, 4]
Callsites: -
- Guid: 5678
Counters: [4,3]
-
- Guid: 5678
Counters: [6,6]

View File

@ -96,30 +96,32 @@ define i32 @b() !guid !2 {
!1 = !{i64 1001}
!2 = !{i64 1002}
;--- profile.yaml
- Guid: 1000
Counters: [10, 2, 8]
Callsites: -
- Guid: 1001
Counters: [2, 100]
Callsites: -
- Guid: 1002
Counters: [100]
-
- Guid: 1001
Counters: [8, 500]
Callsites: -
- Guid: 1002
Counters: [500]
Contexts:
- Guid: 1000
Counters: [10, 2, 8]
Callsites: -
- Guid: 1001
Counters: [2, 100]
Callsites: -
- Guid: 1002
Counters: [100]
-
- Guid: 1001
Counters: [8, 500]
Callsites: -
- Guid: 1002
Counters: [500]
;--- expected.yaml
- Guid: 1000
Counters: [ 10, 2, 8, 100 ]
Callsites:
- [ ]
- - Guid: 1001
Counters: [ 8, 500 ]
Callsites:
- - Guid: 1002
Counters: [ 500 ]
- - Guid: 1002
Counters: [ 100 ]
Contexts:
- Guid: 1000
Counters: [ 10, 2, 8, 100 ]
Callsites:
- [ ]
- - Guid: 1001
Counters: [ 8, 500 ]
Callsites:
- - Guid: 1002
Counters: [ 500 ]
- - Guid: 1002
Counters: [ 100 ]

View File

@ -16,15 +16,16 @@
; the GUID present in the module, which is otherwise present in the profile, but not
; as a root.
;--- profile.yaml
- Guid: 12341
Counters: [9]
- Guid: 1000
Counters: [5]
- Guid: 34234
Counters: [1]
Callsites: -
- Guid: 1000
Counters: [6, 7]
Contexts:
- Guid: 12341
Counters: [9]
- Guid: 1000
Counters: [5]
- Guid: 34234
Counters: [1]
Callsites: -
- Guid: 1000
Counters: [6, 7]
;--- example.ll
declare void @bar()

View File

@ -24,15 +24,16 @@
; This is the reference profile, laid out in the format the json formatter will
; output it from opt.
;--- profile.yaml
- Guid: 12341
Counters: [9]
- Guid: 12074870348631550642
Counters: [5]
- Guid: 11872291593386833696
Counters: [1]
Callsites: -
- Guid: 728453322856651412
Counters: [6, 7]
Contexts:
- Guid: 12341
Counters: [9]
- Guid: 12074870348631550642
Counters: [5]
- Guid: 11872291593386833696
Counters: [1]
Callsites: -
- Guid: 728453322856651412
Counters: [6, 7]
;--- expected-profile-output.txt
Function Info:
4909520559318251808 : an_entrypoint. MaxCounterID: 2. MaxCallsiteID: 1
@ -41,13 +42,14 @@ Function Info:
Current Profile:
- Guid: 11872291593386833696
Counters: [ 1 ]
Callsites:
- - Guid: 728453322856651412
Counters: [ 6, 7 ]
- Guid: 12074870348631550642
Counters: [ 5 ]
Contexts:
- Guid: 11872291593386833696
Counters: [ 1 ]
Callsites:
- - Guid: 728453322856651412
Counters: [ 6, 7 ]
- Guid: 12074870348631550642
Counters: [ 5 ]
Flat Profile:
728453322856651412 : 6 7
@ -91,4 +93,4 @@ no:
}
attributes #0 = { noinline }
!0 = !{ i64 11872291593386833696 }
!0 = !{ i64 11872291593386833696 }

View File

@ -52,10 +52,10 @@
; RUN: opt -module-summary -passes=assign-guid,ctx-instr-gen %t/m1.ll -o %t/m1-instr.bc
; RUN: opt -module-summary -passes=assign-guid,ctx-instr-gen %t/m2.ll -o %t/m2-instr.bc
;
; RUN: echo '[ \
; RUN: echo '{"Contexts": [ \
; RUN: {"Guid": 6019442868614718803, "Counters": [1], "Callsites": [[{"Guid": 15593096274670919754, "Counters": [1]}]]}, \
; RUN: {"Guid": 15593096274670919754, "Counters": [1], "Callsites": [[{"Guid": 6019442868614718803, "Counters": [1]}]]} \
; RUN: ]' > %t_exp/ctxprof.yaml
; RUN: ]}' > %t_exp/ctxprof.yaml
; RUN: llvm-ctxprof-util fromYAML --input %t_exp/ctxprof.yaml --output %t_exp/ctxprof.bitstream
; RUN: llvm-lto2 run %t/m1-instr.bc %t/m2-instr.bc \
; RUN: -o %t_exp/result.o -save-temps \

View File

@ -1,7 +1,7 @@
; REQUIRES: asserts
; RUN: opt -passes=elim-avail-extern -avail-extern-to-local -stats -S 2>&1 < %s | FileCheck %s
;
; RUN: echo '[{"Guid":1234, "Counters": [1]}]' | llvm-ctxprof-util fromYAML --input=- --output=%t_profile.ctxprofdata
; RUN: echo '{"Contexts": [{"Guid":1234, "Counters": [1]}]}' | llvm-ctxprof-util fromYAML --input=- --output=%t_profile.ctxprofdata
;
; Because we pass a contextual profile with a root defined in this module, we expect the outcome to be the same as-if
; we passed -avail-extern-to-local, i.e. available_externally don't get elided and instead get converted to local linkage
@ -9,7 +9,7 @@
; If the profile doesn't apply to this module, available_externally won't get converted to internal linkage, and will be
; removed instead.
; RUN: echo '[{"Guid":5678, "Counters": [1]}]' | llvm-ctxprof-util fromYAML --input=- --output=%t_profile_bad.ctxprofdata
; RUN: echo '{"Contexts": [{"Guid":5678, "Counters": [1]}]}' | llvm-ctxprof-util fromYAML --input=- --output=%t_profile_bad.ctxprofdata
; RUN: opt -passes='assign-guid,require<ctx-prof-analysis>,elim-avail-extern' -use-ctx-profile=%t_profile_bad.ctxprofdata -stats -S 2>&1 < %s | FileCheck %s --check-prefix=NOOP
declare void @call_out(ptr %fct)

View File

@ -1,4 +1,5 @@
- Guid: 123
Counters: [1, 2]
Callsites: - Guid: 1
Contexts:
- Guid: 123
Counters: [1, 2]
Callsites: - Guid: 1

View File

@ -1 +1,2 @@
- Guid: 1231
Contexts:
- Guid: 1231

View File

@ -0,0 +1 @@
- Guid: 1

View File

@ -1,13 +1,14 @@
- Guid: 1000
Counters: [ 1, 2, 3 ]
Callsites:
- [ ]
- - Guid: 2000
Counters: [ 4, 5 ]
- Guid: 18446744073709551613
Counters: [ 6, 7, 8 ]
- - Guid: 3000
Counters: [ 40, 50 ]
- Guid: 18446744073709551612
Counters: [ 5, 9, 10 ]
Contexts:
- Guid: 1000
Counters: [ 1, 2, 3 ]
Callsites:
- [ ]
- - Guid: 2000
Counters: [ 4, 5 ]
- Guid: 18446744073709551613
Counters: [ 6, 7, 8 ]
- - Guid: 3000
Counters: [ 40, 50 ]
- Guid: 18446744073709551612
Counters: [ 5, 9, 10 ]

View File

@ -5,7 +5,7 @@
; RUN: not llvm-ctxprof-util fromYAML nofile.yaml 2>&1 | FileCheck %s --check-prefix=NO_FLAG
; RUN: not llvm-ctxprof-util fromYAML --input nofile.yaml 2>&1 | FileCheck -DMSG=%errc_ENOENT %s --check-prefix=NO_FILE
; RUN: not llvm-ctxprof-util fromYAML --input %S/Inputs/bad.yaml 2>&1 | FileCheck %s --check-prefix=BAD_FORMAT
; RUN: not llvm-ctxprof-util fromYAML --input %S/Inputs/invalid-no-vector.yaml 2>&1 | FileCheck %s --check-prefix=NO_VECTOR
; RUN: not llvm-ctxprof-util fromYAML --input %S/Inputs/invalid-no-section.yaml 2>&1 | FileCheck %s --check-prefix=NO_SECTION
; RUN: not llvm-ctxprof-util fromYAML --input %S/Inputs/invalid-no-ctx.yaml 2>&1 | FileCheck %s --check-prefix=NO_CTX
; RUN: not llvm-ctxprof-util fromYAML --input %S/Inputs/invalid-no-counters.yaml 2>&1 | FileCheck %s --check-prefix=NO_COUNTERS
; RUN: not llvm-ctxprof-util fromYAML --input %S/Inputs/invalid-bad-subctx.yaml 2>&1 | FileCheck %s --check-prefix=BAD_SUBCTX
@ -16,9 +16,9 @@
; INVALID_CMD: Unknown subcommand 'invalidCmd'
; NO_FLAG: Unknown command line argument 'nofile.yaml'.
; NO_FILE: 'nofile.yaml': [[MSG]]
; BAD_FORMAT: YAML:1:3: error: not a mapping
; NO_VECTOR: YAML:1:1: error: not a sequence
; NO_CTX: YAML:1:2: error: not a mapping
; NO_COUNTERS: YAML:1:3: error: missing required key 'Counters'
; BAD_SUBCTX: YAML:3:16: error: not a sequence
; BAD_FORMAT: YAML:1:1: error: not a mapping
; NO_SECTION: YAML:1:1: error: not a mapping
; NO_CTX: YAML:1:1: error: not a mapping
; NO_COUNTERS: YAML:2:5: error: missing required key 'Counters'
; BAD_SUBCTX: YAML:4:18: error: not a sequence
; NO_DIR: failed to open output

View File

@ -18,33 +18,35 @@
; EMPTY: <BLOCKINFO_BLOCK/>
; EMPTY-NEXT: <Metadata NumWords=1 BlockCodeSize=2>
; EMPTY-NEXT: <Version op0=1/>
; EMPTY-NEXT: <Version op0=2/>
; EMPTY-NEXT: </Metadata>
; VALID: <BLOCKINFO_BLOCK/>
; VALID-NEXT: <Metadata NumWords=30 BlockCodeSize=2>
; VALID-NEXT: <Version op0=1/>
; VALID-NEXT: <Context NumWords=20 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=1000/>
; VALID-NEXT: <Counters op0=1 op1=2 op2=3/>
; VALID-NEXT: <Context NumWords=5 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=-3/>
; VALID-NEXT: <CalleeIndex op0=1/>
; VALID-NEXT: <Counters op0=6 op1=7 op2=8/>
; VALID-NEXT: </Context>
; VALID-NEXT: <Context NumWords=3 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=2000/>
; VALID-NEXT: <CalleeIndex op0=1/>
; VALID-NEXT: <Counters op0=4 op1=5/>
; VALID-NEXT: </Context>
; VALID-NEXT: <Context NumWords=3 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=3000/>
; VALID-NEXT: <CalleeIndex op0=2/>
; VALID-NEXT: <Counters op0=40 op1=50/>
; VALID-NEXT: </Context>
; VALID-NEXT: </Context>
; VALID-NEXT: <Context NumWords=4 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=-4/>
; VALID-NEXT: <Counters op0=5 op1=9 op2=10/>
; VALID-NEXT: </Context>
; VALID-NEXT: </Metadata>
; VALID-NEXT: <Metadata NumWords=33 BlockCodeSize=2>
; VALID-NEXT: <Version op0=2/>
; VALID-NEXT: <Contexts NumWords=29 BlockCodeSize=2>
; VALID-NEXT: <Root NumWords=20 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=1000/>
; VALID-NEXT: <Counters op0=1 op1=2 op2=3/>
; VALID-NEXT: <Context NumWords=5 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=-3/>
; VALID-NEXT: <CalleeIndex op0=1/>
; VALID-NEXT: <Counters op0=6 op1=7 op2=8/>
; VALID-NEXT: </Context>
; VALID-NEXT: <Context NumWords=3 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=2000/>
; VALID-NEXT: <CalleeIndex op0=1/>
; VALID-NEXT: <Counters op0=4 op1=5/>
; VALID-NEXT: </Context>
; VALID-NEXT: <Context NumWords=3 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=3000/>
; VALID-NEXT: <CalleeIndex op0=2/>
; VALID-NEXT: <Counters op0=40 op1=50/>
; VALID-NEXT: </Context>
; VALID-NEXT: </Root>
; VALID-NEXT: <Root NumWords=4 BlockCodeSize=2>
; VALID-NEXT: <GUID op0=-4/>
; VALID-NEXT: <Counters op0=5 op1=9 op2=10/>
; VALID-NEXT: </Root>
; VALID-NEXT: </Contexts>
; VALID-NEXT: </Metadata>

View File

@ -77,7 +77,7 @@ Error convertToYaml() {
auto Prof = Reader.loadProfiles();
if (!Prof)
return Prof.takeError();
llvm::convertCtxProfToYaml(Out, Prof->Contexts);
llvm::convertCtxProfToYaml(Out, *Prof);
Out << "\n";
return Error::success();
}

View File

@ -31,6 +31,9 @@ public:
auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get();
std::memset(Mem, 0, AllocSize);
auto *Ret = new (Mem) ContextNode(Guid, NumCounters, NumCallsites, Next);
// set the entrycount to something - unless we're creating an invalid root.
if (Ret->counters_size() > 0)
Ret->counters()[0] = 42;
return Ret;
}
@ -98,8 +101,10 @@ TEST_F(PGOCtxProfRWTest, RoundTrip) {
ASSERT_FALSE(EC);
{
PGOCtxProfileWriter Writer(Out);
Writer.startContextSection();
for (auto &[_, R] : roots())
Writer.write(*R);
Writer.writeContextual(*R);
Writer.endContextSection();
}
}
{
@ -149,7 +154,9 @@ TEST_F(PGOCtxProfRWTest, InvalidCounters) {
ASSERT_FALSE(EC);
{
PGOCtxProfileWriter Writer(Out);
Writer.write(*R);
Writer.startContextSection();
Writer.writeContextual(*R);
Writer.endContextSection();
}
}
{
@ -163,6 +170,60 @@ TEST_F(PGOCtxProfRWTest, InvalidCounters) {
}
}
TEST_F(PGOCtxProfRWTest, CountersAllZero) {
auto *R = createNode(1, 2, 1);
R->counters()[0] = 0;
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.startContextSection();
Writer.writeContextual(*R);
Writer.endContextSection();
}
}
{
auto MB = MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
PGOCtxProfileReader Reader((*MB)->getBuffer());
auto Expected = Reader.loadProfiles();
EXPECT_TRUE(!!Expected);
EXPECT_TRUE(Expected->Contexts.empty());
}
}
TEST_F(PGOCtxProfRWTest, CountersAllZeroWithOverride) {
auto *R = createNode(42, 2, 1);
R->counters()[0] = 0;
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, /*VersionOverride=*/std::nullopt,
/*IncludeEmpty=*/true);
Writer.startContextSection();
Writer.writeContextual(*R);
Writer.endContextSection();
}
}
{
auto MB = MemoryBuffer::getFile(ProfileFile.path());
ASSERT_TRUE(!!MB);
ASSERT_NE(*MB, nullptr);
PGOCtxProfileReader Reader((*MB)->getBuffer());
auto Expected = Reader.loadProfiles();
EXPECT_TRUE(!!Expected);
EXPECT_EQ(Expected->Contexts.size(), 1U);
EXPECT_EQ(Expected->Contexts.begin()->second.guid(), 42U);
}
}
TEST_F(PGOCtxProfRWTest, Empty) {
PGOCtxProfileReader Reader("");
auto Expected = Reader.loadProfiles();
@ -229,9 +290,12 @@ TEST_F(PGOCtxProfRWTest, DuplicateRoots) {
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));
PGOCtxProfileWriter Writer(Out, /*VersionOverride=*/std::nullopt,
/*IncludeEmpty=*/true);
Writer.startContextSection();
Writer.writeContextual(*createNode(1, 1, 1));
Writer.writeContextual(*createNode(1, 1, 1));
Writer.endContextSection();
}
}
{
@ -257,7 +321,9 @@ TEST_F(PGOCtxProfRWTest, DuplicateTargets) {
auto *L2 = createNode(2, 1, 0, L1);
R->subContexts()[0] = L2;
PGOCtxProfileWriter Writer(Out);
Writer.write(*R);
Writer.startContextSection();
Writer.writeContextual(*R);
Writer.endContextSection();
}
}
{

View File

@ -507,6 +507,7 @@ define i32 @f4() !guid !3 {
)IR");
const char *Profile = R"json(
{ "Contexts":
[
{
"Guid": 1000,
@ -537,7 +538,7 @@ define i32 @f4() !guid !3 {
},
{ "Guid": 1003,
"Counters": [103]
}]]}]]}]
}]]}]]}]}
)json";
llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique=*/true);
@ -572,33 +573,34 @@ define i32 @f4() !guid !3 {
CtxProfAnalysisPrinterPass Printer(OS);
Printer.run(*M, MAM);
const char *Expected = R"yaml(
- Guid: 1000
Counters: [ 1, 11, 22 ]
Callsites:
- - Guid: 1001
Counters: [ 10 ]
- Guid: 1003
Counters: [ 12 ]
- - Guid: 1002
Counters: [ 11 ]
Callsites:
- - Guid: 1004
Counters: [ 13 ]
- Guid: 1005
Counters: [ 2 ]
Callsites:
- - Guid: 1000
Counters: [ 1, 102, 204 ]
Callsites:
- - Guid: 1001
Counters: [ 101 ]
- Guid: 1003
Counters: [ 103 ]
- - Guid: 1002
Counters: [ 102 ]
Callsites:
- - Guid: 1004
Counters: [ 104 ]
Contexts:
- Guid: 1000
Counters: [ 1, 11, 22 ]
Callsites:
- - Guid: 1001
Counters: [ 10 ]
- Guid: 1003
Counters: [ 12 ]
- - Guid: 1002
Counters: [ 11 ]
Callsites:
- - Guid: 1004
Counters: [ 13 ]
- Guid: 1005
Counters: [ 2 ]
Callsites:
- - Guid: 1000
Counters: [ 1, 102, 204 ]
Callsites:
- - Guid: 1001
Counters: [ 101 ]
- Guid: 1003
Counters: [ 103 ]
- - Guid: 1002
Counters: [ 102 ]
Callsites:
- - Guid: 1004
Counters: [ 104 ]
)yaml";
EXPECT_EQ(Expected, Str);
}