2017-12-13 23:12:01 -08:00
|
|
|
//===-- ProfileReader.cpp - BOLT profile de-serializer ----------*- 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"
|
2018-04-09 19:10:19 -07:00
|
|
|
#include "Passes/MCF.h"
|
2017-12-13 23:12:01 -08:00
|
|
|
#include "ProfileReader.h"
|
|
|
|
|
#include "ProfileYAMLMapping.h"
|
|
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
|
|
|
|
|
|
namespace opts {
|
|
|
|
|
extern llvm::cl::opt<unsigned> Verbosity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
|
namespace bolt {
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ProfileReader::buildNameMaps(std::map<uint64_t, BinaryFunction> &Functions) {
|
2018-04-09 19:10:19 -07:00
|
|
|
for (auto &YamlBF : YamlBP.Functions) {
|
2017-12-13 23:12:01 -08:00
|
|
|
StringRef Name = YamlBF.Name;
|
|
|
|
|
const auto Pos = Name.find("(*");
|
|
|
|
|
if (Pos != StringRef::npos)
|
|
|
|
|
Name = Name.substr(0, Pos);
|
|
|
|
|
ProfileNameToProfile[Name] = &YamlBF;
|
|
|
|
|
if (const auto CommonName = getLTOCommonName(Name)) {
|
|
|
|
|
LTOCommonNameMap[*CommonName].push_back(&YamlBF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (auto &BFI : Functions) {
|
|
|
|
|
const auto &Function = BFI.second;
|
|
|
|
|
for (auto &Name : Function.getNames()) {
|
|
|
|
|
if (const auto CommonName = getLTOCommonName(Name)) {
|
|
|
|
|
LTOCommonNameFunctionMap[*CommonName].insert(&Function);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ProfileReader::parseFunctionProfile(BinaryFunction &BF,
|
|
|
|
|
const yaml::bolt::BinaryFunctionProfile &YamlBF) {
|
|
|
|
|
auto &BC = BF.getBinaryContext();
|
|
|
|
|
|
|
|
|
|
bool ProfileMatched = true;
|
|
|
|
|
uint64_t MismatchedBlocks = 0;
|
|
|
|
|
uint64_t MismatchedCalls = 0;
|
|
|
|
|
uint64_t MismatchedEdges = 0;
|
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
uint64_t FunctionExecutionCount = 0;
|
|
|
|
|
|
2017-12-13 23:12:01 -08:00
|
|
|
BF.setExecutionCount(YamlBF.ExecCount);
|
|
|
|
|
|
|
|
|
|
if (YamlBF.Hash != BF.hash(true, true)) {
|
|
|
|
|
if (opts::Verbosity >= 1)
|
2018-04-13 10:09:55 -07:00
|
|
|
errs() << "BOLT-WARNING: function hash mismatch\n";
|
2017-12-13 23:12:01 -08:00
|
|
|
ProfileMatched = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (YamlBF.NumBasicBlocks != BF.size()) {
|
|
|
|
|
if (opts::Verbosity >= 1)
|
|
|
|
|
errs() << "BOLT-WARNING: number of basic blocks mismatch\n";
|
|
|
|
|
ProfileMatched = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto DFSOrder = BF.dfs();
|
|
|
|
|
|
|
|
|
|
for (const auto &YamlBB : YamlBF.Blocks) {
|
|
|
|
|
if (YamlBB.Index >= DFSOrder.size()) {
|
|
|
|
|
if (opts::Verbosity >= 2)
|
|
|
|
|
errs() << "BOLT-WARNING: index " << YamlBB.Index
|
|
|
|
|
<< " is out of bounds\n";
|
|
|
|
|
++MismatchedBlocks;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto &BB = *DFSOrder[YamlBB.Index];
|
2018-04-09 19:10:19 -07:00
|
|
|
|
2018-04-13 11:18:46 -07:00
|
|
|
// Basic samples profile (without LBR) does not have branches information
|
|
|
|
|
// and needs a special processing.
|
2018-04-09 19:10:19 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 23:12:01 -08:00
|
|
|
BB.setExecutionCount(YamlBB.ExecCount);
|
|
|
|
|
|
|
|
|
|
for (const auto &YamlCSI: YamlBB.CallSites) {
|
|
|
|
|
auto *Callee = YamlCSI.DestId < YamlProfileToFunction.size() ?
|
|
|
|
|
YamlProfileToFunction[YamlCSI.DestId] : nullptr;
|
|
|
|
|
bool IsFunction = Callee ? true : false;
|
|
|
|
|
const MCSymbol *CalleeSymbol = nullptr;
|
|
|
|
|
if (IsFunction) {
|
|
|
|
|
CalleeSymbol = Callee->getSymbolForEntry(YamlCSI.EntryDiscriminator);
|
|
|
|
|
}
|
|
|
|
|
StringRef Name = CalleeSymbol ? CalleeSymbol->getName() : "<unknown>";
|
|
|
|
|
BF.getAllCallSites().emplace_back(
|
|
|
|
|
IsFunction, Name, YamlCSI.Count, YamlCSI.Mispreds, YamlCSI.Offset);
|
|
|
|
|
|
|
|
|
|
if (YamlCSI.Offset >= BB.getOriginalSize()) {
|
|
|
|
|
if (opts::Verbosity >= 2)
|
|
|
|
|
errs() << "BOLT-WARNING: offset " << YamlCSI.Offset
|
|
|
|
|
<< " out of bounds in block " << BB.getName() << '\n';
|
|
|
|
|
++MismatchedCalls;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto *Instr =
|
|
|
|
|
BF.getInstructionAtOffset(BB.getInputOffset() + YamlCSI.Offset);
|
|
|
|
|
if (!Instr) {
|
|
|
|
|
if (opts::Verbosity >= 2)
|
|
|
|
|
errs() << "BOLT-WARNING: no instruction at offset " << YamlCSI.Offset
|
|
|
|
|
<< " in block " << BB.getName() << '\n';
|
|
|
|
|
++MismatchedCalls;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-03-09 09:45:13 -08:00
|
|
|
if (!BC.MIB->isCall(*Instr) && !BC.MIB->isIndirectBranch(*Instr)) {
|
2017-12-13 23:12:01 -08:00
|
|
|
if (opts::Verbosity >= 2)
|
|
|
|
|
errs() << "BOLT-WARNING: expected call at offset " << YamlCSI.Offset
|
|
|
|
|
<< " in block " << BB.getName() << '\n';
|
|
|
|
|
++MismatchedCalls;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto setAnnotation = [&](StringRef Name, uint64_t Count) {
|
2018-03-09 09:45:13 -08:00
|
|
|
if (BC.MIB->hasAnnotation(*Instr, Name)) {
|
2017-12-13 23:12:01 -08:00
|
|
|
if (opts::Verbosity >= 1)
|
|
|
|
|
errs() << "BOLT-WARNING: ignoring duplicate " << Name
|
|
|
|
|
<< " info for offset 0x" << Twine::utohexstr(YamlCSI.Offset)
|
|
|
|
|
<< " in function " << BF << '\n';
|
|
|
|
|
return;
|
|
|
|
|
}
|
[BOLT][Refactoring] Isolate changes to MC layer
Summary:
Changes that we made to MCInst, MCOperand, MCExpr, etc. are now all
moved into tools/llvm-bolt. That required a change to the way we handle
annotations and any extra operands for MCInst.
Any MCPlus information is now attached via an extra operand of type
MCInst with an opcode ANNOTATION_LABEL. Since this operand is MCInst, we
attach extra info as operands to this instruction. For first-level
annotations use functions to access the information, such as
getConditionalTailCall() or getEHInfo(), etc. For the rest, optional or
second-class annotations, use a general named-annotation interface such
as getAnnotationAs<uint64_t>(Inst, "Count").
I did a test on HHVM binary, and a memory consumption went down a little
bit while the runtime remained the same.
(cherry picked from FBD7405412)
2018-03-19 18:32:12 -07:00
|
|
|
BC.MIB->addAnnotation(*Instr, Name, Count);
|
2017-12-13 23:12:01 -08:00
|
|
|
};
|
|
|
|
|
|
2018-03-09 09:45:13 -08:00
|
|
|
if (BC.MIB->isIndirectCall(*Instr) || BC.MIB->isIndirectBranch(*Instr)) {
|
2017-12-13 23:12:01 -08:00
|
|
|
IndirectCallSiteProfile &CSP =
|
[BOLT][Refactoring] Isolate changes to MC layer
Summary:
Changes that we made to MCInst, MCOperand, MCExpr, etc. are now all
moved into tools/llvm-bolt. That required a change to the way we handle
annotations and any extra operands for MCInst.
Any MCPlus information is now attached via an extra operand of type
MCInst with an opcode ANNOTATION_LABEL. Since this operand is MCInst, we
attach extra info as operands to this instruction. For first-level
annotations use functions to access the information, such as
getConditionalTailCall() or getEHInfo(), etc. For the rest, optional or
second-class annotations, use a general named-annotation interface such
as getAnnotationAs<uint64_t>(Inst, "Count").
I did a test on HHVM binary, and a memory consumption went down a little
bit while the runtime remained the same.
(cherry picked from FBD7405412)
2018-03-19 18:32:12 -07:00
|
|
|
BC.MIB->getOrCreateAnnotationAs<IndirectCallSiteProfile>(
|
2017-12-13 23:12:01 -08:00
|
|
|
*Instr, "CallProfile");
|
|
|
|
|
CSP.emplace_back(IsFunction, Name, YamlCSI.Count, YamlCSI.Mispreds);
|
2018-03-09 09:45:13 -08:00
|
|
|
} else if (BC.MIB->getConditionalTailCall(*Instr)) {
|
2017-12-13 23:12:01 -08:00
|
|
|
setAnnotation("CTCTakenCount", YamlCSI.Count);
|
|
|
|
|
setAnnotation("CTCMispredCount", YamlCSI.Mispreds);
|
|
|
|
|
} else {
|
|
|
|
|
setAnnotation("Count", YamlCSI.Count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto &YamlSI : YamlBB.Successors) {
|
|
|
|
|
if (YamlSI.Index >= DFSOrder.size()) {
|
|
|
|
|
if (opts::Verbosity >= 1)
|
|
|
|
|
errs() << "BOLT-WARNING: index out of bounds for profiled block\n";
|
|
|
|
|
++MismatchedEdges;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto &SuccessorBB = *DFSOrder[YamlSI.Index];
|
|
|
|
|
if (!BB.getSuccessor(SuccessorBB.getLabel())) {
|
|
|
|
|
if (opts::Verbosity >= 1)
|
|
|
|
|
errs() << "BOLT-WARNING: no successor for block " << BB.getName()
|
|
|
|
|
<< " that matches index " << YamlSI.Index << " or block "
|
|
|
|
|
<< SuccessorBB.getName() << '\n';
|
|
|
|
|
++MismatchedEdges;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-13 11:21:59 -08:00
|
|
|
auto &BI = BB.getBranchInfo(SuccessorBB);
|
|
|
|
|
BI.Count += YamlSI.Count;
|
|
|
|
|
BI.MispredictedCount += YamlSI.Mispreds;
|
2017-12-13 23:12:01 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 23:12:01 -08:00
|
|
|
ProfileMatched &= !MismatchedBlocks && !MismatchedCalls && !MismatchedEdges;
|
|
|
|
|
|
|
|
|
|
if (ProfileMatched)
|
2018-04-27 14:16:42 -07:00
|
|
|
BF.markProfiled(YamlBP.Header.Flags);
|
2017-12-13 23:12:01 -08:00
|
|
|
|
|
|
|
|
if (!ProfileMatched && opts::Verbosity >= 1) {
|
|
|
|
|
errs() << "BOLT-WARNING: " << MismatchedBlocks << " blocks, "
|
|
|
|
|
<< MismatchedCalls << " calls, and " << MismatchedEdges
|
|
|
|
|
<< " edges in profile did not match function " << BF << '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ProfileMatched;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::error_code
|
|
|
|
|
ProfileReader::readProfile(const std::string &FileName,
|
|
|
|
|
std::map<uint64_t, BinaryFunction> &Functions) {
|
|
|
|
|
auto MB = MemoryBuffer::getFileOrSTDIN(FileName);
|
|
|
|
|
if (std::error_code EC = MB.getError()) {
|
|
|
|
|
errs() << "ERROR: cannot open " << FileName << ": " << EC.message() << "\n";
|
|
|
|
|
return EC;
|
|
|
|
|
}
|
|
|
|
|
yaml::Input YamlInput(MB.get()->getBuffer());
|
2018-04-09 19:10:19 -07:00
|
|
|
|
|
|
|
|
// Consume YAML file.
|
|
|
|
|
YamlInput >> YamlBP;
|
2017-12-13 23:12:01 -08:00
|
|
|
if (YamlInput.error()) {
|
2018-04-09 19:10:19 -07:00
|
|
|
errs() << "BOLT-ERROR: syntax error parsing profile in " << FileName
|
|
|
|
|
<< " : " << YamlInput.error().message() << '\n';
|
2017-12-13 23:12:01 -08:00
|
|
|
return YamlInput.error();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
// 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.
|
2017-12-13 23:12:01 -08:00
|
|
|
buildNameMaps(Functions);
|
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
YamlProfileToFunction.resize(YamlBP.Functions.size() + 1);
|
2018-02-14 12:30:27 -08:00
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// by hash. The second pass allows name ambiguity for LTO private functions.
|
2017-12-13 23:12:01 -08:00
|
|
|
for (auto &BFI : Functions) {
|
|
|
|
|
auto &Function = BFI.second;
|
|
|
|
|
auto Hash = Function.hash(true, true);
|
2018-02-14 12:30:27 -08:00
|
|
|
for (auto &FunctionName : Function.getNames()) {
|
|
|
|
|
auto PI = ProfileNameToProfile.find(FunctionName);
|
2018-04-09 19:10:19 -07:00
|
|
|
if (PI == ProfileNameToProfile.end()) {
|
2018-02-14 12:30:27 -08:00
|
|
|
continue;
|
2018-04-09 19:10:19 -07:00
|
|
|
}
|
2018-02-14 12:30:27 -08:00
|
|
|
auto &YamlBF = *PI->getValue();
|
|
|
|
|
if (YamlBF.Hash == Hash) {
|
|
|
|
|
matchProfileToFunction(YamlBF, Function);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &BFI : Functions) {
|
|
|
|
|
auto &Function = BFI.second;
|
|
|
|
|
|
|
|
|
|
if (ProfiledFunctions.count(&Function))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
auto Hash = Function.hash(/*Recompute = */false); // was just recomputed
|
2017-12-13 23:12:01 -08:00
|
|
|
for (auto &FunctionName : Function.getNames()) {
|
|
|
|
|
const auto CommonName = getLTOCommonName(FunctionName);
|
|
|
|
|
if (CommonName) {
|
|
|
|
|
auto I = LTOCommonNameMap.find(*CommonName);
|
|
|
|
|
if (I == LTOCommonNameMap.end())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
bool ProfileMatched{false};
|
|
|
|
|
auto <OProfiles = I->getValue();
|
|
|
|
|
for (auto *YamlBF : LTOProfiles) {
|
|
|
|
|
if (YamlBF->Used)
|
|
|
|
|
continue;
|
|
|
|
|
if (YamlBF->Hash == Hash) {
|
|
|
|
|
matchProfileToFunction(*YamlBF, Function);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ProfileMatched)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// If there's only one function with a given name, try to
|
|
|
|
|
// match it partially.
|
|
|
|
|
if (LTOProfiles.size() == 1 &&
|
|
|
|
|
LTOCommonNameFunctionMap[*CommonName].size() == 1 &&
|
|
|
|
|
!LTOProfiles.front()->Used) {
|
|
|
|
|
matchProfileToFunction(*LTOProfiles.front(), Function);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
auto PI = ProfileNameToProfile.find(FunctionName);
|
|
|
|
|
if (PI == ProfileNameToProfile.end())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
auto &YamlBF = *PI->getValue();
|
2018-02-14 12:30:27 -08:00
|
|
|
if (!YamlBF.Used) {
|
|
|
|
|
matchProfileToFunction(YamlBF, Function);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-12-13 23:12:01 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-09 19:10:19 -07:00
|
|
|
for (auto &YamlBF : YamlBP.Functions) {
|
2017-12-13 23:12:01 -08:00
|
|
|
if (!YamlBF.Used) {
|
|
|
|
|
errs() << "BOLT-WARNING: profile ignored for function "
|
|
|
|
|
<< YamlBF.Name << '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
for (auto &YamlBF : YamlBP.Functions) {
|
2017-12-13 23:12:01 -08:00
|
|
|
if (YamlBF.Id >= YamlProfileToFunction.size()) {
|
|
|
|
|
// Such profile was ignored.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (auto *BF = YamlProfileToFunction[YamlBF.Id]) {
|
|
|
|
|
parseFunctionProfile(*BF, YamlBF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return YamlInput.error();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-09 19:10:19 -07:00
|
|
|
bool ProfileReader::usesEvent(StringRef Name) const {
|
|
|
|
|
return YamlBP.Header.EventNames.find(Name) != StringRef::npos;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 23:12:01 -08:00
|
|
|
} // end namespace bolt
|
|
|
|
|
} // end namespace llvm
|