Files
clang-p2996/bolt/ProfileWriter.cpp
Maksim Panchenko b6cb112feb [BOLT] New profile format
Summary:
A new profile that is more resilient to minor binary modifications.

BranchData is eliminated. For calls, the data is converted into instruction
annotations if the profile matches a function. If a profile cannot be matched,
AllCallSites data should have call sites profiles.

The new profile format is YAML, which is quite verbose. It still takes
less space than the older format because we avoid function name repetition.

The plan is to get rid of the old profile format eventually.

merge-fdata does not work with the new format yet.

(cherry picked from FBD6753747)
2017-12-13 23:12:01 -08:00

175 lines
5.3 KiB
C++

//===-- ProfileWriter.cpp - Serialize profiling data ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//
//===----------------------------------------------------------------------===//
#include "BinaryBasicBlock.h"
#include "BinaryFunction.h"
#include "ProfileWriter.h"
#include "ProfileYAMLMapping.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#undef DEBUG_TYPE
#define DEBUG_TYPE "bolt-prof"
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();
YamlBF.Name = BF.getPrintName();
YamlBF.Id = BF.getFunctionNumber();
YamlBF.Hash = BF.hash(true, true);
YamlBF.ExecCount = BF.getKnownExecutionCount();
YamlBF.NumBasicBlocks = BF.size();
for (const auto *BB : BF.dfs()) {
yaml::bolt::BinaryBasicBlockProfile YamlBB;
YamlBB.Index = BB->getLayoutIndex();
YamlBB.NumInstructions = BB->getNumNonPseudos();
YamlBB.ExecCount = BB->getKnownExecutionCount();
for (const auto &Instr : *BB) {
if (!BC.MIA->isCall(Instr) && !BC.MIA->isIndirectBranch(Instr))
continue;
yaml::bolt::CallSiteInfo CSI;
auto Offset = BC.MIA->tryGetAnnotationAs<uint64_t>(Instr, "Offset");
if (!Offset || Offset.get() < BB->getInputOffset())
continue;
CSI.Offset = Offset.get() - BB->getInputOffset();
if (BC.MIA->isIndirectCall(Instr) || BC.MIA->isIndirectBranch(Instr)) {
auto ICSP =
BC.MIA->tryGetAnnotationAs<IndirectCallSiteProfile>(Instr,
"CallProfile");
if (!ICSP)
continue;
for (auto &CSP : ICSP.get()) {
CSI.DestId = 0; // designated for unknown functions
CSI.EntryDiscriminator = 0;
if (CSP.IsFunction) {
const auto *CalleeSymbol = BC.getGlobalSymbolByName(CSP.Name);
if (CalleeSymbol) {
const auto *Callee = BC.getFunctionForSymbol(CalleeSymbol);
if (Callee) {
CSI.DestId = Callee->getFunctionNumber();
}
}
}
CSI.Count = CSP.Count;
CSI.Mispreds = CSP.Mispreds;
YamlBB.CallSites.push_back(CSI);
}
} else { // direct call or a tail call
const auto *CalleeSymbol = BC.MIA->getTargetSymbol(Instr);
const auto Callee = BC.getFunctionForSymbol(CalleeSymbol);
if (Callee) {
CSI.DestId = Callee->getFunctionNumber();;
CSI.EntryDiscriminator = Callee->getEntryForSymbol(CalleeSymbol);
}
if (BC.MIA->getConditionalTailCall(Instr)) {
auto CTCCount =
BC.MIA->tryGetAnnotationAs<uint64_t>(Instr, "CTCTakenCount");
if (CTCCount) {
CSI.Count = *CTCCount;
auto CTCMispreds =
BC.MIA->tryGetAnnotationAs<uint64_t>(Instr, "CTCMispredCount");
if (CTCMispreds)
CSI.Mispreds = *CTCMispreds;
}
} else {
auto Count = BC.MIA->tryGetAnnotationAs<uint64_t>(Instr, "Count");
if (Count)
CSI.Count = *Count;
}
if (CSI.Count)
YamlBB.CallSites.emplace_back(CSI);
}
}
// Skip printing if there's no profile data for non-entry basic block.
if (YamlBB.CallSites.empty() && !BB->isEntryPoint()) {
uint64_t SuccessorExecCount = 0;
for (auto &BranchInfo : BB->branch_info()) {
SuccessorExecCount += BranchInfo.Count;
}
if (!SuccessorExecCount)
continue;
}
auto BranchInfo = BB->branch_info_begin();
for (const auto *Successor : BB->successors()) {
yaml::bolt::SuccessorInfo YamlSI;
YamlSI.Index = Successor->getLayoutIndex();
YamlSI.Count = BranchInfo->Count;
YamlSI.Mispreds = BranchInfo->MispredictedCount;
YamlBB.Successors.emplace_back(YamlSI);
++BranchInfo;
}
YamlBF.Blocks.emplace_back(YamlBB);
}
}
} // end anonymous namespace
void ProfileWriter::printBinaryFunctionProfile(const BinaryFunction &BF) {
yaml::bolt::BinaryFunctionProfile YamlBF;
convert(BF, YamlBF);
yaml::Output Out(*OS);
Out << YamlBF;
}
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::Output Out(*OS);
Out << YamlBFs;
}
} // namespace bolt
} // namespace llvm