2021-12-21 10:21:41 -08:00
|
|
|
//===- bolt/tools/merge-fdata/merge-fdata.cpp -----------------------------===//
|
2016-04-08 12:18:06 -07:00
|
|
|
//
|
2021-03-15 18:04:18 -07:00
|
|
|
// 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
|
2016-04-08 12:18:06 -07:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2021-12-21 10:21:41 -08:00
|
|
|
// Tool for merging profile in fdata format:
|
|
|
|
//
|
|
|
|
// $ merge-fdata 1.fdata 2.fdata 3.fdata > merged.fdata
|
2016-06-21 18:44:42 -07:00
|
|
|
//
|
2016-04-08 12:18:06 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2021-10-08 11:47:10 -07:00
|
|
|
#include "bolt/Profile/ProfileYAMLMapping.h"
|
2023-02-02 12:02:02 -08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2018-04-05 13:03:05 -07:00
|
|
|
#include "llvm/ADT/StringMap.h"
|
2016-04-08 12:18:06 -07:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
2022-05-31 21:14:31 +08:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
2016-04-08 12:18:06 -07:00
|
|
|
#include "llvm/Support/ManagedStatic.h"
|
2021-10-08 11:47:10 -07:00
|
|
|
#include "llvm/Support/PrettyStackTrace.h"
|
2016-04-08 12:18:06 -07:00
|
|
|
#include "llvm/Support/Signals.h"
|
2023-05-19 14:56:46 -07:00
|
|
|
#include "llvm/Support/ThreadPool.h"
|
|
|
|
#include <algorithm>
|
2024-12-14 22:26:20 -08:00
|
|
|
#include <fstream>
|
2023-05-19 14:56:46 -07:00
|
|
|
#include <mutex>
|
2018-04-05 13:03:05 -07:00
|
|
|
#include <unordered_map>
|
2016-04-08 12:18:06 -07:00
|
|
|
|
|
|
|
using namespace llvm;
|
2018-04-09 19:10:19 -07:00
|
|
|
using namespace llvm::yaml::bolt;
|
2016-04-08 12:18:06 -07:00
|
|
|
|
|
|
|
namespace opts {
|
|
|
|
|
2025-03-06 14:11:05 +08:00
|
|
|
static cl::OptionCategory MergeFdataCategory("merge-fdata options");
|
2017-03-28 14:40:20 -07:00
|
|
|
|
2016-06-09 17:45:15 -07:00
|
|
|
enum SortType : char {
|
|
|
|
ST_NONE,
|
|
|
|
ST_EXEC_COUNT, /// Sort based on function execution count.
|
|
|
|
ST_TOTAL_BRANCHES, /// Sort based on all branches in the function.
|
|
|
|
};
|
|
|
|
|
2016-04-08 12:18:06 -07:00
|
|
|
static cl::list<std::string>
|
2017-03-28 14:40:20 -07:00
|
|
|
InputDataFilenames(
|
|
|
|
cl::Positional,
|
|
|
|
cl::CommaSeparated,
|
|
|
|
cl::desc("<fdata1> [<fdata2>]..."),
|
|
|
|
cl::OneOrMore,
|
|
|
|
cl::cat(MergeFdataCategory));
|
|
|
|
|
|
|
|
static cl::opt<SortType>
|
|
|
|
PrintFunctionList("print",
|
|
|
|
cl::desc("print the list of objects with count to stderr"),
|
|
|
|
cl::init(ST_NONE),
|
|
|
|
cl::values(clEnumValN(ST_NONE,
|
|
|
|
"none",
|
|
|
|
"do not print objects/functions"),
|
|
|
|
clEnumValN(ST_EXEC_COUNT,
|
|
|
|
"exec",
|
|
|
|
"print functions sorted by execution count"),
|
|
|
|
clEnumValN(ST_TOTAL_BRANCHES,
|
|
|
|
"branches",
|
[BOLT rebase] Rebase fixes on top of LLVM Feb2018
Summary:
This commit includes all code necessary to make BOLT working again
after the rebase. This includes a redesign of the EHFrame work,
cherry-pick of the 3dnow disassembly work, compilation error fixes,
and port of the debug_info work. The macroop fusion feature is not
ported yet.
The rebased version has minor changes to the "executed instructions"
dynostats counter because REP prefixes are considered a part of the
instruction it applies to. Also, some X86 instructions had the "mayLoad"
tablegen property removed, which BOLT uses to identify and account
for loads, thus reducing the total number of loads reported by
dynostats. This was observed in X86::MOVDQUmr. TRAP instructions are
not terminators anymore, changing our CFG. This commit adds compensation
to preserve this old behavior and minimize tests changes. debug_info
sections are now slightly larger. The discriminator field in the line
table is slightly different due to a change upstream. New profiles
generated with the other bolt are incompatible with this version
because of different hash values calculated for functions, so they will
be considered 100% stale. This commit changes the corresponding test
to XFAIL so it can be updated. The hash function changes because it
relies on raw opcode values, which change according to the opcodes
described in the X86 tablegen files. When processing HHVM, bolt was
observed to be using about 800MB more memory in the rebased version
and being about 5% slower.
(cherry picked from FBD7078072)
2018-02-06 15:00:23 -08:00
|
|
|
"print functions sorted by total branch count")),
|
2017-03-28 14:40:20 -07:00
|
|
|
cl::cat(MergeFdataCategory));
|
2016-04-08 12:18:06 -07:00
|
|
|
|
2016-06-09 17:45:15 -07:00
|
|
|
static cl::opt<bool>
|
|
|
|
SuppressMergedDataOutput("q",
|
2017-03-28 14:40:20 -07:00
|
|
|
cl::desc("do not print merged data to stdout"),
|
|
|
|
cl::init(false),
|
|
|
|
cl::Optional,
|
|
|
|
cl::cat(MergeFdataCategory));
|
2016-06-09 17:45:15 -07:00
|
|
|
|
2022-06-01 23:53:59 +08:00
|
|
|
static cl::opt<std::string>
|
|
|
|
OutputFilePath("o",
|
|
|
|
cl::value_desc("file"),
|
|
|
|
cl::desc("Write output to <file>"),
|
|
|
|
cl::cat(MergeFdataCategory));
|
|
|
|
|
2016-04-08 12:18:06 -07:00
|
|
|
} // namespace opts
|
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
namespace {
|
|
|
|
|
2016-04-08 12:18:06 -07:00
|
|
|
static StringRef ToolName;
|
|
|
|
|
|
|
|
static void report_error(StringRef Message, std::error_code EC) {
|
|
|
|
assert(EC);
|
|
|
|
errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
static void report_error(Twine Message, StringRef CustomError) {
|
|
|
|
errs() << ToolName << ": '" << Message << "': " << CustomError << ".\n";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2022-06-01 23:53:59 +08:00
|
|
|
static raw_fd_ostream &output() {
|
|
|
|
if (opts::OutputFilePath.empty() || opts::OutputFilePath == "-")
|
|
|
|
return outs();
|
|
|
|
else {
|
|
|
|
std::error_code EC;
|
|
|
|
static raw_fd_ostream Output(opts::OutputFilePath, EC);
|
|
|
|
if (EC)
|
|
|
|
report_error(opts::OutputFilePath, EC);
|
|
|
|
return Output;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
void mergeProfileHeaders(BinaryProfileHeader &MergedHeader,
|
|
|
|
const BinaryProfileHeader &Header) {
|
2021-12-28 18:43:53 -08:00
|
|
|
if (MergedHeader.FileName.empty())
|
2018-04-09 19:10:19 -07:00
|
|
|
MergedHeader.FileName = Header.FileName;
|
2021-12-28 18:43:53 -08:00
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
if (!MergedHeader.FileName.empty() &&
|
2021-12-28 18:43:53 -08:00
|
|
|
MergedHeader.FileName != Header.FileName)
|
2021-12-14 16:52:51 -08:00
|
|
|
errs() << "WARNING: merging profile from a binary for " << Header.FileName
|
|
|
|
<< " into a profile for binary " << MergedHeader.FileName << '\n';
|
2021-12-28 18:43:53 -08:00
|
|
|
|
|
|
|
if (MergedHeader.Id.empty())
|
2018-04-09 19:10:19 -07:00
|
|
|
MergedHeader.Id = Header.Id;
|
2021-12-28 18:43:53 -08:00
|
|
|
|
|
|
|
if (!MergedHeader.Id.empty() && (MergedHeader.Id != Header.Id))
|
2018-04-09 19:10:19 -07:00
|
|
|
errs() << "WARNING: build-ids in merged profiles do not match\n";
|
|
|
|
|
|
|
|
// Cannot merge samples profile with LBR profile.
|
2021-12-28 18:43:53 -08:00
|
|
|
if (!MergedHeader.Flags)
|
2018-04-09 19:10:19 -07:00
|
|
|
MergedHeader.Flags = Header.Flags;
|
2021-12-28 18:43:53 -08:00
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
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()) {
|
2021-12-28 18:43:53 -08:00
|
|
|
if (MergedHeader.Origin.empty())
|
2018-04-09 19:10:19 -07:00
|
|
|
MergedHeader.Origin = Header.Origin;
|
2021-12-28 18:43:53 -08:00
|
|
|
else if (MergedHeader.Origin != Header.Origin)
|
2018-04-09 19:10:19 -07:00
|
|
|
MergedHeader.Origin += "; " + Header.Origin;
|
|
|
|
}
|
|
|
|
|
2021-12-28 18:43:53 -08:00
|
|
|
if (MergedHeader.EventNames.empty())
|
2018-04-09 19:10:19 -07:00
|
|
|
MergedHeader.EventNames = Header.EventNames;
|
2021-12-28 18:43:53 -08:00
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
if (MergedHeader.EventNames != Header.EventNames) {
|
|
|
|
errs() << "WARNING: merging profiles with different sampling events\n";
|
|
|
|
MergedHeader.EventNames += "," + Header.EventNames;
|
|
|
|
}
|
2024-09-25 23:18:34 +02:00
|
|
|
|
|
|
|
if (MergedHeader.HashFunction != Header.HashFunction)
|
|
|
|
report_error("merge conflict",
|
|
|
|
"cannot merge profiles with different hash functions");
|
2018-04-09 19:10:19 -07:00
|
|
|
}
|
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
void mergeBasicBlockProfile(BinaryBasicBlockProfile &MergedBB,
|
|
|
|
BinaryBasicBlockProfile &&BB,
|
|
|
|
const BinaryFunctionProfile &BF) {
|
|
|
|
// Verify that the blocks match.
|
|
|
|
if (BB.NumInstructions != MergedBB.NumInstructions)
|
|
|
|
report_error(BF.Name + " : BB #" + Twine(BB.Index),
|
|
|
|
"number of instructions in block mismatch");
|
|
|
|
if (BB.Hash != MergedBB.Hash)
|
|
|
|
report_error(BF.Name + " : BB #" + Twine(BB.Index),
|
|
|
|
"basic block hash mismatch");
|
|
|
|
|
|
|
|
// Update the execution count.
|
|
|
|
MergedBB.ExecCount += BB.ExecCount;
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
// Update the event count.
|
|
|
|
MergedBB.EventCount += BB.EventCount;
|
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
// Merge calls sites.
|
|
|
|
std::unordered_map<uint32_t, CallSiteInfo *> CSByOffset;
|
2021-04-08 00:19:26 -07:00
|
|
|
for (CallSiteInfo &CS : BB.CallSites)
|
2018-04-05 13:03:05 -07:00
|
|
|
CSByOffset.emplace(std::make_pair(CS.Offset, &CS));
|
|
|
|
|
2021-04-08 00:19:26 -07:00
|
|
|
for (CallSiteInfo &MergedCS : MergedBB.CallSites) {
|
2018-04-05 13:03:05 -07:00
|
|
|
auto CSI = CSByOffset.find(MergedCS.Offset);
|
|
|
|
if (CSI == CSByOffset.end())
|
|
|
|
continue;
|
2021-04-08 00:19:26 -07:00
|
|
|
yaml::bolt::CallSiteInfo &CS = *CSI->second;
|
2018-04-05 13:03:05 -07:00
|
|
|
if (CS != MergedCS)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
MergedCS.Count += CS.Count;
|
|
|
|
MergedCS.Mispreds += CS.Mispreds;
|
|
|
|
|
|
|
|
CSByOffset.erase(CSI);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append the rest of call sites.
|
2021-12-28 18:43:53 -08:00
|
|
|
for (std::pair<const uint32_t, CallSiteInfo *> CSI : CSByOffset)
|
2018-04-05 13:03:05 -07:00
|
|
|
MergedBB.CallSites.emplace_back(std::move(*CSI.second));
|
|
|
|
|
|
|
|
// Merge successor info.
|
|
|
|
std::vector<SuccessorInfo *> SIByIndex(BF.NumBasicBlocks);
|
2021-04-08 00:19:26 -07:00
|
|
|
for (SuccessorInfo &SI : BB.Successors) {
|
2018-04-05 13:03:05 -07:00
|
|
|
if (SI.Index >= BF.NumBasicBlocks)
|
|
|
|
report_error(BF.Name, "bad successor index");
|
|
|
|
SIByIndex[SI.Index] = &SI;
|
|
|
|
}
|
2021-04-08 00:19:26 -07:00
|
|
|
for (SuccessorInfo &MergedSI : MergedBB.Successors) {
|
2018-04-05 13:03:05 -07:00
|
|
|
if (!SIByIndex[MergedSI.Index])
|
|
|
|
continue;
|
2021-04-08 00:19:26 -07:00
|
|
|
SuccessorInfo &SI = *SIByIndex[MergedSI.Index];
|
2018-04-05 13:03:05 -07:00
|
|
|
|
|
|
|
MergedSI.Count += SI.Count;
|
|
|
|
MergedSI.Mispreds += SI.Mispreds;
|
|
|
|
|
|
|
|
SIByIndex[MergedSI.Index] = nullptr;
|
|
|
|
}
|
2021-12-28 18:43:53 -08:00
|
|
|
for (SuccessorInfo *SI : SIByIndex)
|
|
|
|
if (SI)
|
2018-04-05 13:03:05 -07:00
|
|
|
MergedBB.Successors.emplace_back(std::move(*SI));
|
|
|
|
}
|
|
|
|
|
|
|
|
void mergeFunctionProfile(BinaryFunctionProfile &MergedBF,
|
|
|
|
BinaryFunctionProfile &&BF) {
|
|
|
|
// Validate that we are merging the correct function.
|
|
|
|
if (BF.NumBasicBlocks != MergedBF.NumBasicBlocks)
|
|
|
|
report_error(BF.Name, "number of basic blocks mismatch");
|
|
|
|
if (BF.Id != MergedBF.Id)
|
|
|
|
report_error(BF.Name, "ID mismatch");
|
|
|
|
if (BF.Hash != MergedBF.Hash)
|
|
|
|
report_error(BF.Name, "hash mismatch");
|
|
|
|
|
|
|
|
// Update the execution count.
|
|
|
|
MergedBF.ExecCount += BF.ExecCount;
|
|
|
|
|
|
|
|
// Merge basic blocks profile.
|
|
|
|
std::vector<BinaryBasicBlockProfile *> BlockByIndex(BF.NumBasicBlocks);
|
2021-04-08 00:19:26 -07:00
|
|
|
for (BinaryBasicBlockProfile &BB : BF.Blocks) {
|
2018-04-05 13:03:05 -07:00
|
|
|
if (BB.Index >= BF.NumBasicBlocks)
|
|
|
|
report_error(BF.Name + " : BB #" + Twine(BB.Index),
|
|
|
|
"bad basic block index");
|
|
|
|
BlockByIndex[BB.Index] = &BB;
|
|
|
|
}
|
2021-04-08 00:19:26 -07:00
|
|
|
for (BinaryBasicBlockProfile &MergedBB : MergedBF.Blocks) {
|
2018-04-05 13:03:05 -07:00
|
|
|
if (!BlockByIndex[MergedBB.Index])
|
|
|
|
continue;
|
2021-04-08 00:19:26 -07:00
|
|
|
BinaryBasicBlockProfile &BB = *BlockByIndex[MergedBB.Index];
|
2018-04-05 13:03:05 -07:00
|
|
|
|
|
|
|
mergeBasicBlockProfile(MergedBB, std::move(BB), MergedBF);
|
|
|
|
|
|
|
|
// Ignore this block in the future.
|
|
|
|
BlockByIndex[MergedBB.Index] = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append blocks unique to BF (i.e. those that are not in MergedBF).
|
2021-12-28 18:43:53 -08:00
|
|
|
for (BinaryBasicBlockProfile *BB : BlockByIndex)
|
|
|
|
if (BB)
|
2018-04-05 13:03:05 -07:00
|
|
|
MergedBF.Blocks.emplace_back(std::move(*BB));
|
|
|
|
}
|
|
|
|
|
2018-06-21 14:45:38 -07:00
|
|
|
bool isYAML(const StringRef Filename) {
|
2021-04-08 00:19:26 -07:00
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
|
|
|
|
MemoryBuffer::getFileOrSTDIN(Filename);
|
2018-06-21 14:45:38 -07:00
|
|
|
if (std::error_code EC = MB.getError())
|
|
|
|
report_error(Filename, EC);
|
2021-04-08 00:19:26 -07:00
|
|
|
StringRef Buffer = MB.get()->getBuffer();
|
2023-12-13 23:34:49 -08:00
|
|
|
if (Buffer.starts_with("---\n"))
|
2018-06-21 14:45:38 -07:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-05-31 21:14:31 +08:00
|
|
|
void mergeLegacyProfiles(const SmallVectorImpl<std::string> &Filenames) {
|
2018-06-21 14:45:38 -07:00
|
|
|
errs() << "Using legacy profile format.\n";
|
2023-03-31 16:07:47 +08:00
|
|
|
std::optional<bool> BoltedCollection;
|
2024-12-13 17:28:37 +01:00
|
|
|
std::optional<bool> NoLBRCollection;
|
2023-05-19 14:56:46 -07:00
|
|
|
std::mutex BoltedCollectionMutex;
|
2024-12-14 22:38:24 -08:00
|
|
|
struct CounterTy {
|
|
|
|
uint64_t Exec{0};
|
|
|
|
uint64_t Mispred{0};
|
|
|
|
CounterTy &operator+=(const CounterTy &O) {
|
|
|
|
Exec += O.Exec;
|
|
|
|
Mispred += O.Mispred;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
CounterTy operator+(const CounterTy &O) { return *this += O; }
|
|
|
|
};
|
|
|
|
typedef StringMap<CounterTy> ProfileTy;
|
2023-05-19 14:56:46 -07:00
|
|
|
|
|
|
|
auto ParseProfile = [&](const std::string &Filename, auto &Profiles) {
|
|
|
|
const llvm::thread::id tid = llvm::this_thread::get_id();
|
|
|
|
|
2018-06-21 14:45:38 -07:00
|
|
|
if (isYAML(Filename))
|
|
|
|
report_error(Filename, "cannot mix YAML and legacy formats");
|
2019-10-11 13:32:14 -07:00
|
|
|
|
2024-12-14 22:26:20 -08:00
|
|
|
std::ifstream FdataFile(Filename, std::ios::in);
|
|
|
|
std::string FdataLine;
|
|
|
|
std::getline(FdataFile, FdataLine);
|
|
|
|
|
|
|
|
auto checkMode = [&](const std::string &Key, std::optional<bool> &Flag) {
|
|
|
|
const bool KeyIsSet = FdataLine.rfind(Key, 0) == 0;
|
|
|
|
|
|
|
|
if (!Flag.has_value())
|
|
|
|
Flag = KeyIsSet;
|
|
|
|
else if (*Flag != KeyIsSet)
|
|
|
|
report_error(Filename, "cannot mix profile with and without " + Key);
|
|
|
|
if (KeyIsSet)
|
|
|
|
// Advance line
|
|
|
|
std::getline(FdataFile, FdataLine);
|
|
|
|
};
|
|
|
|
|
2023-05-19 14:56:46 -07:00
|
|
|
ProfileTy *Profile;
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> Lock(BoltedCollectionMutex);
|
|
|
|
// Check if the string "boltedcollection" is in the first line
|
2024-12-14 22:26:20 -08:00
|
|
|
checkMode("boltedcollection", BoltedCollection);
|
2024-12-13 17:28:37 +01:00
|
|
|
// Check if the string "no_lbr" is in the first line
|
|
|
|
// (or second line if BoltedCollection is true)
|
2024-12-14 22:26:20 -08:00
|
|
|
checkMode("no_lbr", NoLBRCollection);
|
2023-05-19 14:56:46 -07:00
|
|
|
Profile = &Profiles[tid];
|
2019-10-11 13:32:14 -07:00
|
|
|
}
|
|
|
|
|
2024-12-14 22:26:20 -08:00
|
|
|
do {
|
|
|
|
StringRef Line(FdataLine);
|
2024-12-14 22:38:24 -08:00
|
|
|
CounterTy Count;
|
|
|
|
auto [Signature, ExecCount] = Line.rsplit(' ');
|
|
|
|
if (ExecCount.getAsInteger(10, Count.Exec))
|
|
|
|
report_error(Filename, "Malformed / corrupted execution count");
|
|
|
|
// Only LBR profile has misprediction field
|
|
|
|
if (!NoLBRCollection.value_or(false)) {
|
|
|
|
auto [SignatureLBR, MispredCount] = Signature.rsplit(' ');
|
|
|
|
Signature = SignatureLBR;
|
|
|
|
if (MispredCount.getAsInteger(10, Count.Mispred))
|
|
|
|
report_error(Filename, "Malformed / corrupted misprediction count");
|
|
|
|
}
|
|
|
|
|
2023-05-19 14:56:46 -07:00
|
|
|
Count += Profile->lookup(Signature);
|
|
|
|
Profile->insert_or_assign(Signature, Count);
|
2024-12-14 22:26:20 -08:00
|
|
|
} while (std::getline(FdataFile, FdataLine));
|
2023-05-19 14:56:46 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// The final reduction has non-trivial cost, make sure each thread has at
|
|
|
|
// least 4 tasks.
|
|
|
|
ThreadPoolStrategy S = optimal_concurrency(
|
|
|
|
std::max(Filenames.size() / 4, static_cast<size_t>(1)));
|
2024-03-05 18:00:46 -08:00
|
|
|
DefaultThreadPool Pool(S);
|
2024-02-19 18:07:12 -08:00
|
|
|
DenseMap<llvm::thread::id, ProfileTy> ParsedProfiles(
|
|
|
|
Pool.getMaxConcurrency());
|
2023-05-19 14:56:46 -07:00
|
|
|
for (const auto &Filename : Filenames)
|
|
|
|
Pool.async(ParseProfile, std::cref(Filename), std::ref(ParsedProfiles));
|
|
|
|
Pool.wait();
|
|
|
|
|
|
|
|
ProfileTy MergedProfile;
|
|
|
|
for (const auto &[Thread, Profile] : ParsedProfiles)
|
|
|
|
for (const auto &[Key, Value] : Profile) {
|
2024-12-14 22:38:24 -08:00
|
|
|
CounterTy Count = MergedProfile.lookup(Key) + Value;
|
2023-05-19 14:56:46 -07:00
|
|
|
MergedProfile.insert_or_assign(Key, Count);
|
2022-04-11 21:56:12 +08:00
|
|
|
}
|
|
|
|
|
2024-01-18 16:22:55 -08:00
|
|
|
if (BoltedCollection.value_or(false))
|
2022-06-01 23:53:59 +08:00
|
|
|
output() << "boltedcollection\n";
|
2024-12-13 17:28:37 +01:00
|
|
|
if (NoLBRCollection.value_or(false))
|
|
|
|
output() << "no_lbr\n";
|
2024-12-14 22:38:24 -08:00
|
|
|
for (const auto &[Key, Value] : MergedProfile) {
|
|
|
|
output() << Key << " ";
|
|
|
|
if (!NoLBRCollection.value_or(false))
|
|
|
|
output() << Value.Mispred << " ";
|
|
|
|
output() << Value.Exec << "\n";
|
|
|
|
}
|
2022-04-11 21:56:12 +08:00
|
|
|
|
2018-06-21 14:45:38 -07:00
|
|
|
errs() << "Profile from " << Filenames.size() << " files merged.\n";
|
|
|
|
}
|
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2016-04-08 12:18:06 -07:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
// Print a stack trace if we signal out.
|
[BOLT rebase] Rebase fixes on top of LLVM Feb2018
Summary:
This commit includes all code necessary to make BOLT working again
after the rebase. This includes a redesign of the EHFrame work,
cherry-pick of the 3dnow disassembly work, compilation error fixes,
and port of the debug_info work. The macroop fusion feature is not
ported yet.
The rebased version has minor changes to the "executed instructions"
dynostats counter because REP prefixes are considered a part of the
instruction it applies to. Also, some X86 instructions had the "mayLoad"
tablegen property removed, which BOLT uses to identify and account
for loads, thus reducing the total number of loads reported by
dynostats. This was observed in X86::MOVDQUmr. TRAP instructions are
not terminators anymore, changing our CFG. This commit adds compensation
to preserve this old behavior and minimize tests changes. debug_info
sections are now slightly larger. The discriminator field in the line
table is slightly different due to a change upstream. New profiles
generated with the other bolt are incompatible with this version
because of different hash values calculated for functions, so they will
be considered 100% stale. This commit changes the corresponding test
to XFAIL so it can be updated. The hash function changes because it
relies on raw opcode values, which change according to the opcodes
described in the X86 tablegen files. When processing HHVM, bolt was
observed to be using about 800MB more memory in the rebased version
and being about 5% slower.
(cherry picked from FBD7078072)
2018-02-06 15:00:23 -08:00
|
|
|
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
2016-04-08 12:18:06 -07:00
|
|
|
PrettyStackTraceProgram X(argc, argv);
|
|
|
|
|
2021-12-14 16:52:51 -08:00
|
|
|
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
|
2016-04-08 12:18:06 -07:00
|
|
|
|
2017-03-28 14:40:20 -07:00
|
|
|
cl::HideUnrelatedOptions(opts::MergeFdataCategory);
|
|
|
|
|
2016-04-08 12:18:06 -07:00
|
|
|
cl::ParseCommandLineOptions(argc, argv,
|
2018-04-05 13:03:05 -07:00
|
|
|
"merge multiple fdata into a single file");
|
2016-04-08 12:18:06 -07:00
|
|
|
|
|
|
|
ToolName = argv[0];
|
|
|
|
|
2022-05-31 21:14:31 +08:00
|
|
|
// Recursively expand input directories into input file lists.
|
|
|
|
SmallVector<std::string> Inputs;
|
|
|
|
for (std::string &InputDataFilename : opts::InputDataFilenames) {
|
|
|
|
if (!llvm::sys::fs::exists(InputDataFilename))
|
|
|
|
report_error(InputDataFilename,
|
|
|
|
std::make_error_code(std::errc::no_such_file_or_directory));
|
|
|
|
if (llvm::sys::fs::is_regular_file(InputDataFilename))
|
|
|
|
Inputs.emplace_back(InputDataFilename);
|
|
|
|
else if (llvm::sys::fs::is_directory(InputDataFilename)) {
|
|
|
|
std::error_code EC;
|
|
|
|
for (llvm::sys::fs::recursive_directory_iterator F(InputDataFilename, EC),
|
|
|
|
E;
|
|
|
|
F != E && !EC; F.increment(EC))
|
|
|
|
if (llvm::sys::fs::is_regular_file(F->path()))
|
|
|
|
Inputs.emplace_back(F->path());
|
|
|
|
if (EC)
|
|
|
|
report_error(InputDataFilename, EC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isYAML(Inputs.front())) {
|
|
|
|
mergeLegacyProfiles(Inputs);
|
2018-06-21 14:45:38 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
// Merged header.
|
|
|
|
BinaryProfileHeader MergedHeader;
|
|
|
|
MergedHeader.Version = 1;
|
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
// Merged information for all functions.
|
|
|
|
StringMap<BinaryFunctionProfile> MergedBFs;
|
2017-10-16 13:09:43 -07:00
|
|
|
|
2024-09-25 23:18:34 +02:00
|
|
|
bool FirstHeader = true;
|
2022-05-31 21:14:31 +08:00
|
|
|
for (std::string &InputDataFilename : Inputs) {
|
2021-04-08 00:19:26 -07:00
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
|
|
|
|
MemoryBuffer::getFileOrSTDIN(InputDataFilename);
|
2018-04-05 13:03:05 -07:00
|
|
|
if (std::error_code EC = MB.getError())
|
2016-04-08 12:18:06 -07:00
|
|
|
report_error(InputDataFilename, EC);
|
2018-04-09 19:10:19 -07:00
|
|
|
yaml::Input YamlInput(MB.get()->getBuffer());
|
2024-09-03 11:27:57 -07:00
|
|
|
YamlInput.setAllowUnknownKeys(true);
|
2016-04-08 12:18:06 -07:00
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
errs() << "Merging data from " << InputDataFilename << "...\n";
|
2017-08-02 10:59:33 -07:00
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
BinaryProfile BP;
|
|
|
|
YamlInput >> BP;
|
2018-04-05 13:03:05 -07:00
|
|
|
if (YamlInput.error())
|
|
|
|
report_error(InputDataFilename, YamlInput.error());
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
// 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.
|
2024-09-25 23:18:34 +02:00
|
|
|
if (FirstHeader) {
|
|
|
|
MergedHeader = BP.Header;
|
|
|
|
FirstHeader = false;
|
|
|
|
} else {
|
|
|
|
mergeProfileHeaders(MergedHeader, BP.Header);
|
|
|
|
}
|
2018-04-09 19:10:19 -07:00
|
|
|
|
|
|
|
// Do the function merge.
|
2021-04-08 00:19:26 -07:00
|
|
|
for (BinaryFunctionProfile &BF : BP.Functions) {
|
2018-04-05 13:03:05 -07:00
|
|
|
if (!MergedBFs.count(BF.Name)) {
|
|
|
|
MergedBFs.insert(std::make_pair(BF.Name, BF));
|
|
|
|
continue;
|
2016-04-08 12:18:06 -07:00
|
|
|
}
|
2017-08-02 10:59:33 -07:00
|
|
|
|
2021-04-08 00:19:26 -07:00
|
|
|
BinaryFunctionProfile &MergedBF = MergedBFs.find(BF.Name)->second;
|
2018-04-05 13:03:05 -07:00
|
|
|
mergeFunctionProfile(MergedBF, std::move(BF));
|
2017-10-16 13:09:43 -07:00
|
|
|
}
|
2016-04-08 12:18:06 -07:00
|
|
|
}
|
|
|
|
|
2016-06-09 17:45:15 -07:00
|
|
|
if (!opts::SuppressMergedDataOutput) {
|
2022-06-01 23:53:59 +08:00
|
|
|
yaml::Output YamlOut(output());
|
2018-04-05 13:03:05 -07:00
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
BinaryProfile MergedProfile;
|
|
|
|
MergedProfile.Header = MergedHeader;
|
|
|
|
MergedProfile.Functions.resize(MergedBFs.size());
|
2023-02-02 12:02:02 -08:00
|
|
|
llvm::copy(llvm::make_second_range(MergedBFs),
|
|
|
|
MergedProfile.Functions.begin());
|
2018-04-05 13:03:05 -07:00
|
|
|
|
|
|
|
// For consistency, sort functions by their IDs.
|
2022-06-23 22:15:47 -07:00
|
|
|
llvm::sort(MergedProfile.Functions,
|
|
|
|
[](const BinaryFunctionProfile &A,
|
|
|
|
const BinaryFunctionProfile &B) { return A.Id < B.Id; });
|
2018-04-05 13:03:05 -07:00
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
YamlOut << MergedProfile;
|
2016-04-08 12:18:06 -07:00
|
|
|
}
|
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
errs() << "Data for " << MergedBFs.size()
|
2016-06-09 17:45:15 -07:00
|
|
|
<< " unique objects successfully merged.\n";
|
|
|
|
|
|
|
|
if (opts::PrintFunctionList != opts::ST_NONE) {
|
|
|
|
// List of function names with execution count.
|
2018-04-05 13:03:05 -07:00
|
|
|
std::vector<std::pair<uint64_t, StringRef>> FunctionList(MergedBFs.size());
|
2021-12-14 16:52:51 -08:00
|
|
|
using CountFuncType = std::function<std::pair<uint64_t, StringRef>(
|
|
|
|
const StringMapEntry<BinaryFunctionProfile> &)>;
|
2018-04-05 13:03:05 -07:00
|
|
|
CountFuncType ExecCountFunc =
|
|
|
|
[](const StringMapEntry<BinaryFunctionProfile> &V) {
|
2021-12-14 16:52:51 -08:00
|
|
|
return std::make_pair(V.second.ExecCount, StringRef(V.second.Name));
|
|
|
|
};
|
2018-04-05 13:03:05 -07:00
|
|
|
CountFuncType BranchCountFunc =
|
|
|
|
[](const StringMapEntry<BinaryFunctionProfile> &V) {
|
2021-12-14 16:52:51 -08:00
|
|
|
// Return total branch count.
|
|
|
|
uint64_t BranchCount = 0;
|
2021-12-28 18:43:53 -08:00
|
|
|
for (const BinaryBasicBlockProfile &BI : V.second.Blocks)
|
|
|
|
for (const SuccessorInfo &SI : BI.Successors)
|
2021-12-14 16:52:51 -08:00
|
|
|
BranchCount += SI.Count;
|
|
|
|
return std::make_pair(BranchCount, StringRef(V.second.Name));
|
|
|
|
};
|
2016-06-09 17:45:15 -07:00
|
|
|
|
|
|
|
CountFuncType CountFunc = (opts::PrintFunctionList == opts::ST_EXEC_COUNT)
|
2021-12-14 16:52:51 -08:00
|
|
|
? ExecCountFunc
|
|
|
|
: BranchCountFunc;
|
2022-06-23 22:15:47 -07:00
|
|
|
llvm::transform(MergedBFs, FunctionList.begin(), CountFunc);
|
|
|
|
llvm::stable_sort(reverse(FunctionList));
|
2016-06-09 17:45:15 -07:00
|
|
|
errs() << "Functions sorted by "
|
2021-12-14 16:52:51 -08:00
|
|
|
<< (opts::PrintFunctionList == opts::ST_EXEC_COUNT ? "execution"
|
|
|
|
: "total branch")
|
2016-06-09 17:45:15 -07:00
|
|
|
<< " count:\n";
|
2021-12-28 18:43:53 -08:00
|
|
|
for (std::pair<uint64_t, StringRef> &FI : FunctionList)
|
2016-06-09 17:45:15 -07:00
|
|
|
errs() << FI.second << " : " << FI.first << '\n';
|
|
|
|
}
|
2016-04-08 12:18:06 -07:00
|
|
|
|
2018-04-05 13:03:05 -07:00
|
|
|
return 0;
|
2016-04-08 12:18:06 -07:00
|
|
|
}
|