Skip to content

Commit

Permalink
[ctx_prof] Move the "from json" logic more centrally to reuse it from…
Browse files Browse the repository at this point in the history
… test. (llvm#106129)

Making the synthesis of a contextual profile file from a JSON descriptor more reusable, for unittest authoring purposes.

The functionality round-trips through the binary format - no reason, currently, to support other ways of loading contextual profiles.
  • Loading branch information
mtrofin committed Aug 27, 2024
1 parent bb27dd8 commit 1022323
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 75 deletions.
1 change: 1 addition & 0 deletions llvm/include/llvm/ProfileData/PGOCtxProfWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,6 @@ class PGOCtxProfileWriter final {
static constexpr StringRef ContainerMagic = "CTXP";
};

Error createCtxProfFromJSON(StringRef Profile, raw_ostream &Out);
} // namespace llvm
#endif
82 changes: 82 additions & 0 deletions llvm/lib/ProfileData/PGOCtxProfWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include "llvm/ProfileData/PGOCtxProfWriter.h"
#include "llvm/Bitstream/BitCodeEnums.h"
#include "llvm/ProfileData/CtxInstrContextNode.h"
#include "llvm/Support/JSON.h"

using namespace llvm;
using namespace llvm::ctx_profile;
Expand Down Expand Up @@ -81,3 +83,83 @@ void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex,
void PGOCtxProfileWriter::write(const ContextNode &RootNode) {
writeImpl(std::nullopt, RootNode);
}

namespace {
// A structural representation of the JSON input.
struct DeserializableCtx {
ctx_profile::GUID Guid = 0;
std::vector<uint64_t> Counters;
std::vector<std::vector<DeserializableCtx>> Callsites;
};

ctx_profile::ContextNode *
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
const std::vector<DeserializableCtx> &DCList);

// Convert a DeserializableCtx into a ContextNode, potentially linking it to
// its sibling (e.g. callee at same callsite) "Next".
ctx_profile::ContextNode *
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
const DeserializableCtx &DC,
ctx_profile::ContextNode *Next = nullptr) {
auto AllocSize = ctx_profile::ContextNode::getAllocSize(DC.Counters.size(),
DC.Callsites.size());
auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get();
std::memset(Mem, 0, AllocSize);
auto *Ret = new (Mem) ctx_profile::ContextNode(DC.Guid, DC.Counters.size(),
DC.Callsites.size(), Next);
std::memcpy(Ret->counters(), DC.Counters.data(),
sizeof(uint64_t) * DC.Counters.size());
for (const auto &[I, DCList] : llvm::enumerate(DC.Callsites))
Ret->subContexts()[I] = createNode(Nodes, DCList);
return Ret;
}

// Convert a list of DeserializableCtx into a linked list of ContextNodes.
ctx_profile::ContextNode *
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
const std::vector<DeserializableCtx> &DCList) {
ctx_profile::ContextNode *List = nullptr;
for (const auto &DC : DCList)
List = createNode(Nodes, DC, List);
return List;
}
} // namespace

namespace llvm {
namespace json {
bool fromJSON(const Value &E, DeserializableCtx &R, Path P) {
json::ObjectMapper Mapper(E, P);
return Mapper && Mapper.map("Guid", R.Guid) &&
Mapper.map("Counters", R.Counters) &&
Mapper.mapOptional("Callsites", R.Callsites);
}
} // namespace json
} // namespace llvm

Error llvm::createCtxProfFromJSON(StringRef Profile, raw_ostream &Out) {
auto P = json::parse(Profile);
if (!P)
return P.takeError();

json::Path::Root R("");
std::vector<DeserializableCtx> DCList;
if (!fromJSON(*P, DCList, R))
return R.getError();
// Nodes provides memory backing for the ContextualNodes.
std::vector<std::unique_ptr<char[]>> Nodes;
std::error_code EC;
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 (EC)
return createStringError(EC, "failed to write output");
return Error::success();
}
82 changes: 7 additions & 75 deletions llvm/tools/llvm-ctxprof-util/llvm-ctxprof-util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,90 +46,22 @@ static cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
cl::desc("Output file"),
cl::sub(FromJSON));

namespace {
// A structural representation of the JSON input.
struct DeserializableCtx {
GlobalValue::GUID Guid = 0;
std::vector<uint64_t> Counters;
std::vector<std::vector<DeserializableCtx>> Callsites;
};

ctx_profile::ContextNode *
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
const std::vector<DeserializableCtx> &DCList);

// Convert a DeserializableCtx into a ContextNode, potentially linking it to
// its sibling (e.g. callee at same callsite) "Next".
ctx_profile::ContextNode *
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
const DeserializableCtx &DC,
ctx_profile::ContextNode *Next = nullptr) {
auto AllocSize = ctx_profile::ContextNode::getAllocSize(DC.Counters.size(),
DC.Callsites.size());
auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get();
std::memset(Mem, 0, AllocSize);
auto *Ret = new (Mem) ctx_profile::ContextNode(DC.Guid, DC.Counters.size(),
DC.Callsites.size(), Next);
std::memcpy(Ret->counters(), DC.Counters.data(),
sizeof(uint64_t) * DC.Counters.size());
for (const auto &[I, DCList] : llvm::enumerate(DC.Callsites))
Ret->subContexts()[I] = createNode(Nodes, DCList);
return Ret;
}

// Convert a list of DeserializableCtx into a linked list of ContextNodes.
ctx_profile::ContextNode *
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
const std::vector<DeserializableCtx> &DCList) {
ctx_profile::ContextNode *List = nullptr;
for (const auto &DC : DCList)
List = createNode(Nodes, DC, List);
return List;
}
} // namespace

namespace llvm {
namespace json {
// Hook into the JSON deserialization.
bool fromJSON(const Value &E, DeserializableCtx &R, Path P) {
json::ObjectMapper Mapper(E, P);
return Mapper && Mapper.map("Guid", R.Guid) &&
Mapper.map("Counters", R.Counters) &&
Mapper.mapOptional("Callsites", R.Callsites);
}
} // namespace json
} // namespace llvm

// Save the bitstream profile from the JSON representation.
Error convertFromJSON() {
auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilename);
if (!BufOrError)
return createFileError(InputFilename, BufOrError.getError());
auto P = json::parse(BufOrError.get()->getBuffer());
if (!P)
return P.takeError();

std::vector<DeserializableCtx> DCList;
json::Path::Root R("");
if (!fromJSON(*P, DCList, R))
return R.getError();
// Nodes provides memory backing for the ContextualNodes.
std::vector<std::unique_ptr<char[]>> Nodes;
std::error_code EC;
raw_fd_stream Out(OutputFilename, EC);
// Using a fd_ostream instead of a fd_stream. The latter would be more
// efficient as the bitstream writer supports incremental flush to it, but the
// json scenario is for test, and file size scalability doesn't really concern
// us.
raw_fd_ostream Out(OutputFilename, EC);
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 (EC)
return createStringError(EC, "failed to write output");
return Error::success();

return llvm::createCtxProfFromJSON(BufOrError.get()->getBuffer(), Out);
}

int main(int argc, const char **argv) {
Expand Down

0 comments on commit 1022323

Please sign in to comment.