forked from OSchip/llvm-project
[BOLT] Support for non-LBR profile in YAML
Summary: Expanded YAML profile format to support different kinds of profile including LBR and non-LBR (and memevents in the future). The profile now starts with a header that includes the profile description. "profile-flags" field includes either "lbr" or "sample", but not both at the same time. It could also include "memevent" in addition to other flags. For now, the only way to generate non-LBR YAML profile is through conversion. Once task is done, it should be possible to use perf2bolt for it. (cherry picked from FBD7595693)
This commit is contained in:
parent
4878770072
commit
8b049d3c7f
|
@ -10,8 +10,6 @@
|
|||
// Interface to function in binary (machine) form. This is assembly-level
|
||||
// code representation with the control flow.
|
||||
//
|
||||
// TODO: memory management for instructions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TOOLS_LLVM_BOLT_BINARY_FUNCTION_H
|
||||
|
@ -213,6 +211,14 @@ public:
|
|||
Emitted, /// Instructions have been emitted to output.
|
||||
};
|
||||
|
||||
/// Types of profile the function can use. Could be a combination.
|
||||
enum {
|
||||
PF_NONE = 0, /// No profile.
|
||||
PF_LBR = 1, /// Profile is based on last branch records.
|
||||
PF_SAMPLE = 2, /// Non-LBR sample-based profile.
|
||||
PF_MEMEVENT = 4, /// Profile has mem events.
|
||||
};
|
||||
|
||||
/// Settings for splitting function bodies into hot/cold partitions.
|
||||
enum SplittingType : char {
|
||||
ST_NONE = 0, /// Do not split functions
|
||||
|
@ -328,8 +334,8 @@ private:
|
|||
/// Profile match ratio for BranchData.
|
||||
float ProfileMatchRatio{0.0f};
|
||||
|
||||
/// Indicates if function profile was collected using LBRs.
|
||||
bool HasLBRProfile{true};
|
||||
/// Indicates the type of profile the function is using.
|
||||
uint16_t ProfileFlags{PF_NONE};
|
||||
|
||||
/// For functions with mismatched profile we store all call profile
|
||||
/// information at a function level (as opposed to tying it to
|
||||
|
@ -1485,11 +1491,33 @@ public:
|
|||
ProfileMatchRatio = 1.0f;
|
||||
}
|
||||
|
||||
/// Check if the function profile used LBR.
|
||||
bool hasLBRProfile() const {
|
||||
return ProfileFlags & PF_LBR;
|
||||
}
|
||||
|
||||
/// Set status of the function profile.
|
||||
void setHasLBRProfile(bool Value) {
|
||||
assert(!(ProfileFlags & PF_SAMPLE) &&
|
||||
"cannot combine LBR and samples profile");
|
||||
ProfileFlags |= PF_LBR;
|
||||
}
|
||||
|
||||
/// Return flags describing a profile for this function.
|
||||
uint16_t getProfileFlags() const {
|
||||
return ProfileFlags;
|
||||
}
|
||||
|
||||
/// Set profile flags for the function to \p Flags.
|
||||
void setProfileFlags(uint16_t Flags) {
|
||||
ProfileFlags = Flags;
|
||||
}
|
||||
|
||||
void addCFIInstruction(uint64_t Offset, MCCFIInstruction &&Inst) {
|
||||
assert(!Instructions.empty());
|
||||
|
||||
// Fix CFI instructions skipping NOPs. We need to fix this because changing
|
||||
// CFI state after a NOP, besides being wrong and innacurate, makes it
|
||||
// CFI state after a NOP, besides being wrong and inaccurate, makes it
|
||||
// harder for us to recover this information, since we can create empty BBs
|
||||
// with NOPs and then reorder it away.
|
||||
// We fix this by moving the CFI instruction just before any NOPs.
|
||||
|
|
|
@ -306,7 +306,7 @@ void BinaryFunction::postProcessProfile() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!HasLBRProfile)
|
||||
if (!hasLBRProfile())
|
||||
return;
|
||||
|
||||
// Pre-sort branch data.
|
||||
|
@ -428,11 +428,13 @@ void BinaryFunction::readProfile() {
|
|||
return;
|
||||
|
||||
if (!BC.DR.hasLBR()) {
|
||||
HasLBRProfile = false;
|
||||
ProfileFlags = PF_SAMPLE;
|
||||
readSampleData();
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileFlags = PF_LBR;
|
||||
|
||||
// Possibly assign/re-assign branch profile data.
|
||||
matchProfileData();
|
||||
|
||||
|
@ -631,10 +633,7 @@ void BinaryFunction::readSampleData() {
|
|||
}
|
||||
ExecutionCount = TotalEntryCount;
|
||||
|
||||
estimateEdgeCounts(BC, *this);
|
||||
|
||||
if (opts::DoMCF != MCF_DISABLE)
|
||||
solveMCF(*this, opts::DoMCF);
|
||||
estimateEdgeCounts(*this);
|
||||
}
|
||||
|
||||
void BinaryFunction::inferFallThroughCounts() {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "BinaryBasicBlock.h"
|
||||
#include "BinaryFunction.h"
|
||||
#include "Passes/MCF.h"
|
||||
#include "ProfileReader.h"
|
||||
#include "ProfileYAMLMapping.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
|
@ -24,7 +25,7 @@ namespace bolt {
|
|||
|
||||
void
|
||||
ProfileReader::buildNameMaps(std::map<uint64_t, BinaryFunction> &Functions) {
|
||||
for (auto &YamlBF : YamlBFs) {
|
||||
for (auto &YamlBF : YamlBP.Functions) {
|
||||
StringRef Name = YamlBF.Name;
|
||||
const auto Pos = Name.find("(*");
|
||||
if (Pos != StringRef::npos)
|
||||
|
@ -54,6 +55,10 @@ ProfileReader::parseFunctionProfile(BinaryFunction &BF,
|
|||
uint64_t MismatchedCalls = 0;
|
||||
uint64_t MismatchedEdges = 0;
|
||||
|
||||
BF.setProfileFlags(YamlBP.Header.Flags);
|
||||
|
||||
uint64_t FunctionExecutionCount = 0;
|
||||
|
||||
BF.setExecutionCount(YamlBF.ExecCount);
|
||||
|
||||
if (YamlBF.Hash != BF.hash(true, true)) {
|
||||
|
@ -80,6 +85,26 @@ ProfileReader::parseFunctionProfile(BinaryFunction &BF,
|
|||
}
|
||||
|
||||
auto &BB = *DFSOrder[YamlBB.Index];
|
||||
|
||||
// Non-LBR profile does not have branches information and needs a special
|
||||
// processing.
|
||||
if (YamlBP.Header.Flags & BinaryFunction::PF_SAMPLE) {
|
||||
if (!YamlBB.EventCount) {
|
||||
BB.setExecutionCount(0);
|
||||
continue;
|
||||
}
|
||||
auto NumSamples = YamlBB.EventCount * 1000;
|
||||
if (NormalizeByInsnCount && BB.getNumNonPseudos()) {
|
||||
NumSamples /= BB.getNumNonPseudos();
|
||||
} else if (NormalizeByCalls) {
|
||||
NumSamples /= BB.getNumCalls() + 1;
|
||||
}
|
||||
BB.setExecutionCount(NumSamples);
|
||||
if (BB.isEntryPoint())
|
||||
FunctionExecutionCount += NumSamples;
|
||||
continue;
|
||||
}
|
||||
|
||||
BB.setExecutionCount(YamlBB.ExecCount);
|
||||
|
||||
for (const auto &YamlCSI: YamlBB.CallSites) {
|
||||
|
@ -167,6 +192,17 @@ ProfileReader::parseFunctionProfile(BinaryFunction &BF,
|
|||
}
|
||||
}
|
||||
|
||||
// If basic block profile wasn't read it should be 0.
|
||||
for (auto &BB : BF) {
|
||||
if (BB.getExecutionCount() == BinaryBasicBlock::COUNT_NO_PROFILE)
|
||||
BB.setExecutionCount(0);
|
||||
}
|
||||
|
||||
if (YamlBP.Header.Flags & BinaryFunction::PF_SAMPLE) {
|
||||
BF.setExecutionCount(FunctionExecutionCount);
|
||||
estimateEdgeCounts(BF);
|
||||
}
|
||||
|
||||
ProfileMatched &= !MismatchedBlocks && !MismatchedCalls && !MismatchedEdges;
|
||||
|
||||
if (ProfileMatched)
|
||||
|
@ -189,18 +225,33 @@ ProfileReader::readProfile(const std::string &FileName,
|
|||
errs() << "ERROR: cannot open " << FileName << ": " << EC.message() << "\n";
|
||||
return EC;
|
||||
}
|
||||
|
||||
yaml::Input YamlInput(MB.get()->getBuffer());
|
||||
YamlInput >> YamlBFs;
|
||||
|
||||
// Consume YAML file.
|
||||
YamlInput >> YamlBP;
|
||||
if (YamlInput.error()) {
|
||||
errs() << "BOLT-ERROR: syntax error parsing " << FileName << " : "
|
||||
<< YamlInput.error().message() << '\n';
|
||||
errs() << "BOLT-ERROR: syntax error parsing profile in " << FileName
|
||||
<< " : " << YamlInput.error().message() << '\n';
|
||||
return YamlInput.error();
|
||||
}
|
||||
|
||||
// Sanity check.
|
||||
if (YamlBP.Header.Version != 1) {
|
||||
errs() << "BOLT-ERROR: cannot read profile : unsupported version\n";
|
||||
return std::make_error_code(std::errc::executable_format_error);
|
||||
}
|
||||
if (YamlBP.Header.EventNames.find(',') != StringRef::npos) {
|
||||
errs() << "BOLT-ERROR: multiple events in profile are not supported\n";
|
||||
return std::make_error_code(std::errc::executable_format_error);
|
||||
}
|
||||
|
||||
NormalizeByInsnCount = usesEvent("cycles") || usesEvent("instructions");
|
||||
NormalizeByCalls = usesEvent("branches");
|
||||
|
||||
// Match profile to function based on a function name.
|
||||
buildNameMaps(Functions);
|
||||
|
||||
YamlProfileToFunction.resize(YamlBFs.size() + 1);
|
||||
YamlProfileToFunction.resize(YamlBP.Functions.size() + 1);
|
||||
|
||||
// We have to do 2 passes since LTO introduces an ambiguity in function
|
||||
// names. The first pass assigns profiles that match 100% by name and
|
||||
|
@ -210,8 +261,9 @@ ProfileReader::readProfile(const std::string &FileName,
|
|||
auto Hash = Function.hash(true, true);
|
||||
for (auto &FunctionName : Function.getNames()) {
|
||||
auto PI = ProfileNameToProfile.find(FunctionName);
|
||||
if (PI == ProfileNameToProfile.end())
|
||||
if (PI == ProfileNameToProfile.end()) {
|
||||
continue;
|
||||
}
|
||||
auto &YamlBF = *PI->getValue();
|
||||
if (YamlBF.Hash == Hash) {
|
||||
matchProfileToFunction(YamlBF, Function);
|
||||
|
@ -267,14 +319,14 @@ ProfileReader::readProfile(const std::string &FileName,
|
|||
}
|
||||
}
|
||||
}
|
||||
for (auto &YamlBF : YamlBFs) {
|
||||
for (auto &YamlBF : YamlBP.Functions) {
|
||||
if (!YamlBF.Used) {
|
||||
errs() << "BOLT-WARNING: profile ignored for function "
|
||||
<< YamlBF.Name << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &YamlBF : YamlBFs) {
|
||||
for (auto &YamlBF : YamlBP.Functions) {
|
||||
if (YamlBF.Id >= YamlProfileToFunction.size()) {
|
||||
// Such profile was ignored.
|
||||
continue;
|
||||
|
@ -287,5 +339,9 @@ ProfileReader::readProfile(const std::string &FileName,
|
|||
return YamlInput.error();
|
||||
}
|
||||
|
||||
bool ProfileReader::usesEvent(StringRef Name) const {
|
||||
return YamlBP.Header.EventNames.find(Name) != StringRef::npos;
|
||||
}
|
||||
|
||||
} // end namespace bolt
|
||||
} // end namespace llvm
|
||||
|
|
|
@ -20,6 +20,15 @@ namespace llvm {
|
|||
namespace bolt {
|
||||
|
||||
class ProfileReader {
|
||||
private:
|
||||
|
||||
/// Adjustments for non-LBR profiles.
|
||||
bool NormalizeByInsnCount{false};
|
||||
bool NormalizeByCalls{false};
|
||||
|
||||
/// Binary profile in YAML format.
|
||||
yaml::bolt::BinaryProfile YamlBP;
|
||||
|
||||
/// Map a function ID from a YAML profile to a BinaryFunction object.
|
||||
std::vector<BinaryFunction *> YamlProfileToFunction;
|
||||
|
||||
|
@ -31,9 +40,6 @@ class ProfileReader {
|
|||
bool parseFunctionProfile(BinaryFunction &Function,
|
||||
const yaml::bolt::BinaryFunctionProfile &YamlBF);
|
||||
|
||||
/// All function profiles in YAML format.
|
||||
std::vector<yaml::bolt::BinaryFunctionProfile> YamlBFs;
|
||||
|
||||
/// For LTO symbol resolution.
|
||||
/// Map a common LTO prefix to a list of YAML profiles matching the prefix.
|
||||
StringMap<std::vector<yaml::bolt::BinaryFunctionProfile *>> LTOCommonNameMap;
|
||||
|
@ -61,6 +67,9 @@ class ProfileReader {
|
|||
ProfiledFunctions.emplace(&BF);
|
||||
}
|
||||
|
||||
/// Check if the profile uses an event with a given \p Name.
|
||||
bool usesEvent(StringRef Name) const;
|
||||
|
||||
public:
|
||||
/// Read profile from a file and associate with a set of functions.
|
||||
std::error_code readProfile(const std::string &FileName,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "BinaryBasicBlock.h"
|
||||
#include "BinaryFunction.h"
|
||||
#include "DataAggregator.h"
|
||||
#include "ProfileWriter.h"
|
||||
#include "ProfileYAMLMapping.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
@ -26,36 +27,39 @@
|
|||
namespace llvm {
|
||||
namespace bolt {
|
||||
|
||||
std::error_code
|
||||
ProfileWriter::writeProfile(std::map<uint64_t, BinaryFunction> &Functions) {
|
||||
std::error_code EC;
|
||||
OS = make_unique<raw_fd_ostream>(FileName, EC, sys::fs::F_None);
|
||||
if (EC) {
|
||||
errs() << "BOLT-WARNING: " << EC.message() << " : unable to open "
|
||||
<< FileName << " for output.\n";
|
||||
return EC;
|
||||
}
|
||||
|
||||
printBinaryFunctionsProfile(Functions);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
namespace {
|
||||
void
|
||||
convert(const BinaryFunction &BF, yaml::bolt::BinaryFunctionProfile &YamlBF) {
|
||||
auto &BC = BF.getBinaryContext();
|
||||
|
||||
const auto LBRProfile = BF.hasLBRProfile();
|
||||
|
||||
YamlBF.Name = BF.getPrintName();
|
||||
YamlBF.Id = BF.getFunctionNumber();
|
||||
YamlBF.Hash = BF.hash(true, true);
|
||||
YamlBF.ExecCount = BF.getKnownExecutionCount();
|
||||
YamlBF.NumBasicBlocks = BF.size();
|
||||
YamlBF.ExecCount = BF.getKnownExecutionCount();
|
||||
|
||||
FuncSampleData *SampleDataOrErr{nullptr};
|
||||
if (!LBRProfile) {
|
||||
SampleDataOrErr = BC.DR.getFuncSampleData(BF.getNames());
|
||||
if (!SampleDataOrErr)
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto *BB : BF.dfs()) {
|
||||
yaml::bolt::BinaryBasicBlockProfile YamlBB;
|
||||
YamlBB.Index = BB->getLayoutIndex();
|
||||
YamlBB.NumInstructions = BB->getNumNonPseudos();
|
||||
|
||||
if (!LBRProfile) {
|
||||
YamlBB.EventCount =
|
||||
SampleDataOrErr->getSamples(BB->getInputOffset(), BB->getEndOffset());
|
||||
if (YamlBB.EventCount)
|
||||
YamlBF.Blocks.emplace_back(YamlBB);
|
||||
continue;
|
||||
}
|
||||
|
||||
YamlBB.ExecCount = BB->getKnownExecutionCount();
|
||||
|
||||
for (const auto &Instr : *BB) {
|
||||
|
@ -121,7 +125,10 @@ convert(const BinaryFunction &BF, yaml::bolt::BinaryFunctionProfile &YamlBF) {
|
|||
}
|
||||
|
||||
// Skip printing if there's no profile data for non-entry basic block.
|
||||
if (YamlBB.CallSites.empty() && !BB->isEntryPoint()) {
|
||||
// Include landing pads with non-zero execution count.
|
||||
if (YamlBB.CallSites.empty() &&
|
||||
!BB->isEntryPoint() &&
|
||||
!(BB->isLandingPad() && BB->getKnownExecutionCount() != 0)) {
|
||||
uint64_t SuccessorExecCount = 0;
|
||||
for (auto &BranchInfo : BB->branch_info()) {
|
||||
SuccessorExecCount += BranchInfo.Count;
|
||||
|
@ -147,28 +154,78 @@ convert(const BinaryFunction &BF, yaml::bolt::BinaryFunctionProfile &YamlBF) {
|
|||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
void ProfileWriter::printBinaryFunctionProfile(const BinaryFunction &BF) {
|
||||
yaml::bolt::BinaryFunctionProfile YamlBF;
|
||||
convert(BF, YamlBF);
|
||||
std::error_code
|
||||
ProfileWriter::writeProfile(const RewriteInstance &RI) {
|
||||
const auto &Functions = RI.getFunctions();
|
||||
|
||||
yaml::Output Out(*OS);
|
||||
Out << YamlBF;
|
||||
}
|
||||
std::error_code EC;
|
||||
OS = make_unique<raw_fd_ostream>(FileName, EC, sys::fs::F_None);
|
||||
if (EC) {
|
||||
errs() << "BOLT-WARNING: " << EC.message() << " : unable to open "
|
||||
<< FileName << " for output.\n";
|
||||
return EC;
|
||||
}
|
||||
|
||||
void ProfileWriter::printBinaryFunctionsProfile(
|
||||
std::map<uint64_t, BinaryFunction> &BFs) {
|
||||
std::vector<yaml::bolt::BinaryFunctionProfile> YamlBFs;
|
||||
for (auto &BFI : BFs) {
|
||||
const auto &BF = BFI.second;
|
||||
if (BF.hasProfile()) {
|
||||
yaml::bolt::BinaryFunctionProfile YamlBF;
|
||||
convert(BF, YamlBF);
|
||||
YamlBFs.emplace_back(YamlBF);
|
||||
yaml::bolt::BinaryProfile BP;
|
||||
|
||||
// Fill out the header info.
|
||||
BP.Header.Version = 1;
|
||||
auto FileName = RI.getInputFileName();
|
||||
BP.Header.FileName = FileName ? *FileName : "<unknown>";
|
||||
auto BuildID = RI.getBuildID();
|
||||
BP.Header.Id = BuildID ? *BuildID : "<unknown>";
|
||||
|
||||
if (RI.getDataAggregator().started()) {
|
||||
BP.Header.Origin = "aggregator";
|
||||
} else {
|
||||
BP.Header.Origin = "conversion";
|
||||
}
|
||||
|
||||
auto EventNames = RI.getDataAggregator().getEventNames();
|
||||
if (EventNames.empty())
|
||||
EventNames = RI.getBinaryContext().DR.getEventNames();
|
||||
if (!EventNames.empty()) {
|
||||
std::string Sep = "";
|
||||
for (const auto &EventEntry : EventNames) {
|
||||
BP.Header.EventNames += Sep + EventEntry.first().str();
|
||||
Sep = ",";
|
||||
}
|
||||
}
|
||||
|
||||
yaml::Output Out(*OS);
|
||||
Out << YamlBFs;
|
||||
// Make sure the profile is consistent across all functions.
|
||||
uint16_t ProfileFlags = BinaryFunction::PF_NONE;
|
||||
for (const auto &BFI : Functions) {
|
||||
const auto &BF = BFI.second;
|
||||
if (BF.hasProfile() && !BF.empty()) {
|
||||
assert(BF.getProfileFlags() != BinaryFunction::PF_NONE);
|
||||
if (ProfileFlags == BinaryFunction::PF_NONE) {
|
||||
ProfileFlags = BF.getProfileFlags();
|
||||
}
|
||||
assert(BF.getProfileFlags() == ProfileFlags &&
|
||||
"expected consistent profile flags across all functions");
|
||||
}
|
||||
}
|
||||
BP.Header.Flags = ProfileFlags;
|
||||
|
||||
// Add all function objects.
|
||||
for (const auto &BFI : Functions) {
|
||||
const auto &BF = BFI.second;
|
||||
if (BF.hasProfile()) {
|
||||
// In conversion mode ignore stale functions.
|
||||
if (!BF.hasValidProfile() && !RI.getDataAggregator().started())
|
||||
continue;
|
||||
|
||||
yaml::bolt::BinaryFunctionProfile YamlBF;
|
||||
convert(BF, YamlBF);
|
||||
BP.Functions.emplace_back(YamlBF);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the profile.
|
||||
yaml::Output Out(*OS, nullptr, 0);
|
||||
Out << BP;
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
} // namespace bolt
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "BinaryContext.h"
|
||||
#include "BinaryFunction.h"
|
||||
#include "ProfileYAMLMapping.h"
|
||||
#include "RewriteInstance.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <system_error>
|
||||
|
@ -30,21 +31,15 @@ class ProfileWriter {
|
|||
|
||||
std::string FileName;
|
||||
|
||||
std::error_code write(BinaryFunction &BF);
|
||||
|
||||
std::unique_ptr<raw_fd_ostream> OS;
|
||||
|
||||
void printBinaryFunctionProfile(const BinaryFunction &BF);
|
||||
|
||||
void printBinaryFunctionsProfile(std::map<uint64_t, BinaryFunction> &BFs);
|
||||
|
||||
public:
|
||||
explicit ProfileWriter(const std::string &FileName)
|
||||
: FileName(FileName) {
|
||||
}
|
||||
|
||||
/// Write profile for functions.
|
||||
std::error_code writeProfile(std::map<uint64_t, BinaryFunction> &Functions);
|
||||
/// Save execution profile for that instance.
|
||||
std::error_code writeProfile(const RewriteInstance &RI);
|
||||
};
|
||||
|
||||
} // namespace bolt
|
||||
|
|
|
@ -14,10 +14,13 @@
|
|||
#ifndef LLVM_TOOLS_LLVM_BOLT_PROFILEYAMLMAPPING_H
|
||||
#define LLVM_TOOLS_LLVM_BOLT_PROFILEYAMLMAPPING_H
|
||||
|
||||
#include "BinaryFunction.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
#include <vector>
|
||||
|
||||
using llvm::bolt::BinaryFunction;
|
||||
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
|
||||
|
@ -92,6 +95,7 @@ struct BinaryBasicBlockProfile {
|
|||
uint32_t NumInstructions{0};
|
||||
llvm::yaml::Hex64 Hash{0};
|
||||
uint64_t ExecCount{0};
|
||||
uint64_t EventCount{0};
|
||||
std::vector<CallSiteInfo> CallSites;
|
||||
std::vector<SuccessorInfo> Successors;
|
||||
|
||||
|
@ -109,6 +113,7 @@ template <> struct MappingTraits<bolt::BinaryBasicBlockProfile> {
|
|||
YamlIO.mapRequired("bid", BBP.Index);
|
||||
YamlIO.mapRequired("insns", BBP.NumInstructions);
|
||||
YamlIO.mapOptional("exec", BBP.ExecCount, (uint64_t)0);
|
||||
YamlIO.mapOptional("events", BBP.EventCount, (uint64_t)0);
|
||||
YamlIO.mapOptional("calls", BBP.CallSites,
|
||||
std::vector<bolt::CallSiteInfo>());
|
||||
YamlIO.mapOptional("succ", BBP.Successors,
|
||||
|
@ -134,7 +139,7 @@ struct BinaryFunctionProfile {
|
|||
std::vector<BinaryBasicBlockProfile> Blocks;
|
||||
bool Used{false};
|
||||
};
|
||||
}
|
||||
} // end namespace bolt
|
||||
|
||||
template <> struct MappingTraits<bolt::BinaryFunctionProfile> {
|
||||
static void mapping(IO &YamlIO, bolt::BinaryFunctionProfile &BFP) {
|
||||
|
@ -148,9 +153,64 @@ template <> struct MappingTraits<bolt::BinaryFunctionProfile> {
|
|||
}
|
||||
};
|
||||
|
||||
LLVM_YAML_STRONG_TYPEDEF(uint16_t, PROFILE_PF)
|
||||
|
||||
template <>
|
||||
struct ScalarBitSetTraits<PROFILE_PF> {
|
||||
static void bitset(IO &io, PROFILE_PF &value) {
|
||||
io.bitSetCase(value, "lbr", BinaryFunction::PF_LBR);
|
||||
io.bitSetCase(value, "sample", BinaryFunction::PF_SAMPLE);
|
||||
io.bitSetCase(value, "memevent", BinaryFunction::PF_MEMEVENT);
|
||||
}
|
||||
};
|
||||
|
||||
namespace bolt {
|
||||
struct BinaryProfileHeader {
|
||||
uint32_t Version{1};
|
||||
std::string FileName; // Name of the profiled binary.
|
||||
std::string Id; // BuildID.
|
||||
PROFILE_PF Flags{BinaryFunction::PF_NONE};
|
||||
// Type of the profile.
|
||||
std::string Origin; // How the profile was obtained.
|
||||
std::string EventNames; // Events used for sample profile.
|
||||
};
|
||||
} // end namespace bolt
|
||||
|
||||
template <> struct MappingTraits<bolt::BinaryProfileHeader> {
|
||||
static void mapping(IO &YamlIO, bolt::BinaryProfileHeader &Header) {
|
||||
YamlIO.mapRequired("profile-version", Header.Version);
|
||||
YamlIO.mapRequired("binary-name", Header.FileName);
|
||||
YamlIO.mapOptional("binary-build-id", Header.Id);
|
||||
YamlIO.mapRequired("profile-flags", Header.Flags);
|
||||
YamlIO.mapOptional("profile-origin", Header.Origin);
|
||||
YamlIO.mapOptional("profile-events", Header.EventNames);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace yaml
|
||||
} // end namespace llvm
|
||||
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::BinaryFunctionProfile)
|
||||
|
||||
namespace llvm {
|
||||
namespace yaml {
|
||||
|
||||
namespace bolt {
|
||||
struct BinaryProfile {
|
||||
BinaryProfileHeader Header;
|
||||
std::vector<BinaryFunctionProfile> Functions;
|
||||
};
|
||||
}
|
||||
|
||||
template <> struct MappingTraits<bolt::BinaryProfile> {
|
||||
static void mapping(IO &YamlIO, bolt::BinaryProfile &BP) {
|
||||
YamlIO.mapRequired("header", BP.Header);
|
||||
YamlIO.mapRequired("functions", BP.Functions);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace yaml
|
||||
} // end namespace llvm
|
||||
|
||||
LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(llvm::yaml::bolt::BinaryFunctionProfile)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -876,7 +876,7 @@ void RewriteInstance::discoverStorage() {
|
|||
}
|
||||
|
||||
Optional<std::string>
|
||||
RewriteInstance::getBuildID() {
|
||||
RewriteInstance::getBuildID() const {
|
||||
for (auto &Section : InputFile->sections()) {
|
||||
StringRef SectionName;
|
||||
Section.getName(SectionName);
|
||||
|
@ -2263,7 +2263,8 @@ void RewriteInstance::processProfileData() {
|
|||
|
||||
if (!opts::BoltProfile.empty()) {
|
||||
ProfileReader PR;
|
||||
PR.readProfile(opts::BoltProfile, BinaryFunctions);
|
||||
auto EC = PR.readProfile(opts::BoltProfile, BinaryFunctions);
|
||||
check_error(EC, "cannot read profile");
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -2292,7 +2293,7 @@ void RewriteInstance::processProfileData() {
|
|||
|
||||
if (!opts::SaveProfile.empty()) {
|
||||
ProfileWriter PW(opts::SaveProfile);
|
||||
PW.writeProfile(BinaryFunctions);
|
||||
PW.writeProfile(*this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -180,8 +180,7 @@ public:
|
|||
std::vector<const BinaryFunction *> FunctionStack);
|
||||
|
||||
/// Map all sections to their final addresses.
|
||||
void
|
||||
mapFileSections(orc::RTDyldObjectLinkingLayer::ObjHandleT &ObjectsHandle);
|
||||
void mapFileSections(orc::VModuleKey ObjectsHandle);
|
||||
|
||||
/// Update output object's values based on the final \p Layout.
|
||||
void updateOutputValues(const MCAsmLayout &Layout);
|
||||
|
@ -241,10 +240,6 @@ public:
|
|||
DWARFAddressRangesVector translateModuleAddressRanges(
|
||||
const DWARFAddressRangesVector &InputRanges) const;
|
||||
|
||||
uint64_t getTotalScore() const {
|
||||
return BC->TotalScore;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Emit a single function.
|
||||
void emitFunction(MCStreamer &Streamer, BinaryFunction &Function,
|
||||
|
@ -254,9 +249,6 @@ private:
|
|||
/// new sections.
|
||||
void discoverStorage();
|
||||
|
||||
/// Read binary sections and find a gnu note section with the build-id
|
||||
Optional<std::string> getBuildID();
|
||||
|
||||
/// Adjust function sizes and set proper maximum size values after the whole
|
||||
/// symbol table has been processed.
|
||||
void adjustFunctionBoundaries();
|
||||
|
@ -416,7 +408,7 @@ private:
|
|||
const int Argc;
|
||||
const char *const *Argv;
|
||||
|
||||
/// Holds our data aggregator in case user supplied a raw perf data file
|
||||
/// Holds our data aggregator in case user supplied a raw perf data file.
|
||||
DataAggregator &DA;
|
||||
|
||||
std::unique_ptr<BinaryContext> BC;
|
||||
|
@ -426,6 +418,9 @@ private:
|
|||
/// among other things.
|
||||
std::shared_ptr<ExecutableFileMemoryManager> EFMM;
|
||||
|
||||
std::unique_ptr<orc::SymbolStringPool> SSP;
|
||||
std::unique_ptr<orc::ExecutionSession> ES;
|
||||
|
||||
// Run ObjectLinkingLayer() with custom memory manager and symbol resolver.
|
||||
std::unique_ptr<orc::RTDyldObjectLinkingLayer> OLT;
|
||||
|
||||
|
@ -530,6 +525,39 @@ private:
|
|||
uint64_t NumDataRelocations{0};
|
||||
|
||||
friend class RewriteInstanceDiff;
|
||||
|
||||
public:
|
||||
|
||||
/// Return binary context.
|
||||
const BinaryContext &getBinaryContext() const {
|
||||
return *BC;
|
||||
}
|
||||
|
||||
/// Return total score of all functions for this instance.
|
||||
uint64_t getTotalScore() const {
|
||||
return BC->TotalScore;
|
||||
}
|
||||
|
||||
/// Return all functions for this rewrite instance.
|
||||
const std::map<uint64_t, BinaryFunction> &getFunctions() const {
|
||||
return BinaryFunctions;
|
||||
}
|
||||
|
||||
/// Return the name of the input file.
|
||||
Optional<StringRef> getInputFileName() const {
|
||||
if (InputFile)
|
||||
return InputFile->getFileName();
|
||||
return NoneType();
|
||||
}
|
||||
|
||||
/// Read binary sections and find a gnu note section with the build-id
|
||||
/// of the input file.
|
||||
Optional<std::string> getBuildID() const;
|
||||
|
||||
/// Provide an access to the profile data aggregator.
|
||||
const DataAggregator &getDataAggregator() const {
|
||||
return DA;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace bolt
|
||||
|
|
|
@ -644,6 +644,17 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
unsigned getCanonicalBranchOpcode(unsigned Opcode) const override {
|
||||
switch (Opcode) {
|
||||
default:
|
||||
return Opcode;
|
||||
case AArch64::TBNZW: return AArch64::TBZW;
|
||||
case AArch64::TBNZX: return AArch64::TBZX;
|
||||
case AArch64::CBNZW: return AArch64::CBZW;
|
||||
case AArch64::CBNZX: return AArch64::CBZX;
|
||||
}
|
||||
}
|
||||
|
||||
bool reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
|
||||
MCContext *Ctx) const override {
|
||||
if (isTB(Inst) || isCB(Inst)) {
|
||||
|
|
|
@ -3,4 +3,7 @@ set(LLVM_LINK_COMPONENTS Support)
|
|||
add_llvm_tool(merge-fdata
|
||||
merge-fdata.cpp
|
||||
../DataReader.cpp
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
||||
|
|
|
@ -22,9 +22,7 @@
|
|||
#include <unordered_map>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace object;
|
||||
using namespace yaml;
|
||||
using namespace bolt;
|
||||
using namespace llvm::yaml::bolt;
|
||||
|
||||
namespace opts {
|
||||
|
||||
|
@ -83,6 +81,53 @@ static void report_error(Twine Message, StringRef CustomError) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
void mergeProfileHeaders(BinaryProfileHeader &MergedHeader,
|
||||
const BinaryProfileHeader &Header) {
|
||||
if (MergedHeader.FileName.empty()) {
|
||||
MergedHeader.FileName = Header.FileName;
|
||||
}
|
||||
if (!MergedHeader.FileName.empty() &&
|
||||
MergedHeader.FileName != Header.FileName) {
|
||||
errs() << "WARNING: merging profile from a binary for "
|
||||
<< Header.FileName << " into a profile for binary "
|
||||
<< MergedHeader.FileName << '\n';
|
||||
}
|
||||
if (MergedHeader.Id.empty()) {
|
||||
MergedHeader.Id = Header.Id;
|
||||
}
|
||||
if (!MergedHeader.Id.empty() && (MergedHeader.Id != Header.Id)) {
|
||||
errs() << "WARNING: build-ids in merged profiles do not match\n";
|
||||
}
|
||||
|
||||
// Cannot merge samples profile with LBR profile.
|
||||
if (!MergedHeader.Flags) {
|
||||
MergedHeader.Flags = Header.Flags;
|
||||
}
|
||||
constexpr auto Mask = llvm::bolt::BinaryFunction::PF_LBR |
|
||||
llvm::bolt::BinaryFunction::PF_SAMPLE;
|
||||
if ((MergedHeader.Flags & Mask) != (Header.Flags & Mask)) {
|
||||
errs() << "ERROR: cannot merge LBR profile with non-LBR profile\n";
|
||||
exit(1);
|
||||
}
|
||||
MergedHeader.Flags = MergedHeader.Flags | Header.Flags;
|
||||
|
||||
if (!Header.Origin.empty()) {
|
||||
if (MergedHeader.Origin.empty()) {
|
||||
MergedHeader.Origin = Header.Origin;
|
||||
} else if (MergedHeader.Origin != Header.Origin) {
|
||||
MergedHeader.Origin += "; " + Header.Origin;
|
||||
}
|
||||
}
|
||||
|
||||
if (MergedHeader.EventNames.empty()) {
|
||||
MergedHeader.EventNames = Header.EventNames;
|
||||
}
|
||||
if (MergedHeader.EventNames != Header.EventNames) {
|
||||
errs() << "WARNING: merging profiles with different sampling events\n";
|
||||
MergedHeader.EventNames += "," + Header.EventNames;
|
||||
}
|
||||
}
|
||||
|
||||
void mergeBasicBlockProfile(BinaryBasicBlockProfile &MergedBB,
|
||||
BinaryBasicBlockProfile &&BB,
|
||||
const BinaryFunctionProfile &BF) {
|
||||
|
@ -97,6 +142,9 @@ void mergeBasicBlockProfile(BinaryBasicBlockProfile &MergedBB,
|
|||
// Update the execution count.
|
||||
MergedBB.ExecCount += BB.ExecCount;
|
||||
|
||||
// Update the event count.
|
||||
MergedBB.EventCount += BB.EventCount;
|
||||
|
||||
// Merge calls sites.
|
||||
std::unordered_map<uint32_t, CallSiteInfo *> CSByOffset;
|
||||
for (auto &CS : BB.CallSites)
|
||||
|
@ -201,6 +249,10 @@ int main(int argc, char **argv) {
|
|||
|
||||
ToolName = argv[0];
|
||||
|
||||
// Merged header.
|
||||
BinaryProfileHeader MergedHeader;
|
||||
MergedHeader.Version = 1;
|
||||
|
||||
// Merged information for all functions.
|
||||
StringMap<BinaryFunctionProfile> MergedBFs;
|
||||
|
||||
|
@ -208,24 +260,33 @@ int main(int argc, char **argv) {
|
|||
auto MB = MemoryBuffer::getFileOrSTDIN(InputDataFilename);
|
||||
if (std::error_code EC = MB.getError())
|
||||
report_error(InputDataFilename, EC);
|
||||
yaml::Input YamlInput(MB.get()->getBuffer());
|
||||
|
||||
errs() << "Merging data from " << InputDataFilename << "...\n";
|
||||
|
||||
std::vector<BinaryFunctionProfile> BFs;
|
||||
yaml::Input YamlInput(MB.get()->getBuffer());
|
||||
YamlInput >> BFs;
|
||||
BinaryProfile BP;
|
||||
YamlInput >> BP;
|
||||
if (YamlInput.error())
|
||||
report_error(InputDataFilename, YamlInput.error());
|
||||
|
||||
// Do the merge.
|
||||
for (auto &BF : BFs) {
|
||||
// Sanity check.
|
||||
if (BP.Header.Version != 1) {
|
||||
errs() << "Unable to merge data from profile using version "
|
||||
<< BP.Header.Version << '\n';
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Merge the header.
|
||||
mergeProfileHeaders(MergedHeader, BP.Header);
|
||||
|
||||
// Do the function merge.
|
||||
for (auto &BF : BP.Functions) {
|
||||
if (!MergedBFs.count(BF.Name)) {
|
||||
MergedBFs.insert(std::make_pair(BF.Name, BF));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &MergedBF = MergedBFs.find(BF.Name)->second;
|
||||
|
||||
mergeFunctionProfile(MergedBF, std::move(BF));
|
||||
}
|
||||
}
|
||||
|
@ -233,21 +294,23 @@ int main(int argc, char **argv) {
|
|||
if (!opts::SuppressMergedDataOutput) {
|
||||
yaml::Output YamlOut(outs());
|
||||
|
||||
std::vector<BinaryFunctionProfile> AllBFs(MergedBFs.size());
|
||||
BinaryProfile MergedProfile;
|
||||
MergedProfile.Header = MergedHeader;
|
||||
MergedProfile.Functions.resize(MergedBFs.size());
|
||||
std::transform(MergedBFs.begin(),
|
||||
MergedBFs.end(),
|
||||
AllBFs.begin(),
|
||||
[](StringMapEntry<BinaryFunctionProfile> &V) {
|
||||
MergedProfile.Functions.begin(),
|
||||
[] (StringMapEntry<BinaryFunctionProfile> &V) {
|
||||
return V.second;
|
||||
});
|
||||
|
||||
// For consistency, sort functions by their IDs.
|
||||
std::sort(AllBFs.begin(), AllBFs.end(),
|
||||
std::sort(MergedProfile.Functions.begin(), MergedProfile.Functions.end(),
|
||||
[] (BinaryFunctionProfile &A, BinaryFunctionProfile &B) {
|
||||
return A.Id < B.Id;
|
||||
});
|
||||
|
||||
YamlOut << AllBFs;
|
||||
YamlOut << MergedProfile;
|
||||
}
|
||||
|
||||
errs() << "Data for " << MergedBFs.size()
|
||||
|
|
Loading…
Reference in New Issue