Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/llvm/llvm-project into fix/…
Browse files Browse the repository at this point in the history
…97230
  • Loading branch information
a-tarasyuk committed Jul 12, 2024
2 parents a8a20bf + 8ef26f1 commit 88952f5
Show file tree
Hide file tree
Showing 1,384 changed files with 41,356 additions and 13,272 deletions.
7 changes: 3 additions & 4 deletions bolt/include/bolt/Core/DebugData.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,17 +226,16 @@ class DebugRangesSectionWriter {
/// Needs to be invoked before each \p CU is processed.
void virtual initSection(DWARFUnit &CU){};

/// Initializes Ranges section with empty list.
void initSection();

protected:
std::unique_ptr<DebugBufferVector> RangesBuffer;

std::unique_ptr<raw_svector_ostream> RangesStream;

std::mutex WriterMutex;

/// Current offset in the section (updated as new entries are written).
/// Starts with 16 since the first 16 bytes are reserved for an empty range.
uint32_t SectionOffset{0};

/// Offset of an empty address ranges list.
static constexpr uint64_t EmptyRangesOffset{0};

Expand Down
8 changes: 8 additions & 0 deletions bolt/include/bolt/Core/HashUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryContext.h"
#include "bolt/Profile/ProfileYAMLMapping.h"

namespace llvm {
namespace bolt {
Expand All @@ -35,6 +36,13 @@ std::string hashBlock(BinaryContext &BC, const BinaryBasicBlock &BB,

std::string hashBlockLoose(BinaryContext &BC, const BinaryBasicBlock &BB);

std::string hashBlockCalls(BinaryContext &BC, const BinaryBasicBlock &BB);

std::string
hashBlockCalls(const DenseMap<uint32_t, yaml::bolt::BinaryFunctionProfile *>
&IdToYamlFunction,
const yaml::bolt::BinaryBasicBlockProfile &YamlBB);

} // namespace bolt
} // namespace llvm

Expand Down
7 changes: 7 additions & 0 deletions bolt/include/bolt/Profile/YAMLProfileReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class YAMLProfileReader : public ProfileReaderBase {
/// Check if the file contains YAML.
static bool isYAML(StringRef Filename);

using ProfileLookupMap =
DenseMap<uint32_t, yaml::bolt::BinaryFunctionProfile *>;

private:
/// Adjustments for basic samples profiles (without LBR).
bool NormalizeByInsnCount{false};
Expand All @@ -56,6 +59,10 @@ class YAMLProfileReader : public ProfileReaderBase {
/// is attributed.
FunctionSet ProfiledFunctions;

/// Maps profiled function id to function, for function matching with calls as
/// anchors.
ProfileLookupMap IdToYamLBF;

/// 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;
Expand Down
6 changes: 6 additions & 0 deletions bolt/include/bolt/Utils/NameResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ class NameResolver {
std::tie(LHS, RHS) = UniqueName.split(Sep);
return (LHS + Suffix + Twine(Sep) + RHS).str();
}

// Drops the suffix that describes the function's number of names.
static StringRef dropNumNames(StringRef Name) {
const size_t Pos = Name.find("(*");
return Pos != StringRef::npos ? Name.substr(0, Pos) : Name;
}
};

} // namespace bolt
Expand Down
16 changes: 8 additions & 8 deletions bolt/lib/Core/DebugData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,14 @@ DebugRangesSectionWriter::DebugRangesSectionWriter() {
RangesBuffer = std::make_unique<DebugBufferVector>();
RangesStream = std::make_unique<raw_svector_ostream>(*RangesBuffer);

// Add an empty range as the first entry;
SectionOffset +=
writeAddressRanges(*RangesStream.get(), DebugAddressRangesVector{});
Kind = RangesWriterKind::DebugRangesWriter;
}

void DebugRangesSectionWriter::initSection() {
// Adds an empty range to the buffer.
writeAddressRanges(*RangesStream.get(), DebugAddressRangesVector{});
}

uint64_t DebugRangesSectionWriter::addRanges(
DebugAddressRangesVector &&Ranges,
std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) {
Expand All @@ -166,21 +168,20 @@ uint64_t DebugRangesSectionWriter::addRanges(DebugAddressRangesVector &Ranges) {
// Reading the SectionOffset and updating it should be atomic to guarantee
// unique and correct offsets in patches.
std::lock_guard<std::mutex> Lock(WriterMutex);
const uint32_t EntryOffset = SectionOffset;
SectionOffset += writeAddressRanges(*RangesStream.get(), Ranges);
const uint32_t EntryOffset = RangesBuffer->size();
writeAddressRanges(*RangesStream.get(), Ranges);

return EntryOffset;
}

uint64_t DebugRangesSectionWriter::getSectionOffset() {
std::lock_guard<std::mutex> Lock(WriterMutex);
return SectionOffset;
return RangesBuffer->size();
}

void DebugRangesSectionWriter::appendToRangeBuffer(
const DebugBufferVector &CUBuffer) {
*RangesStream << CUBuffer;
SectionOffset = RangesBuffer->size();
}

DebugAddrWriter *DebugRangeListsSectionWriter::AddrWriter = nullptr;
Expand Down Expand Up @@ -327,7 +328,6 @@ void DebugRangeListsSectionWriter::finalizeSection() {
*RangesStream << *Header;
*RangesStream << *CUArrayBuffer;
*RangesStream << *CUBodyBuffer;
SectionOffset = RangesBuffer->size();
}

void DebugRangeListsSectionWriter::initSection(DWARFUnit &Unit) {
Expand Down
46 changes: 46 additions & 0 deletions bolt/lib/Core/HashUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "bolt/Core/HashUtilities.h"
#include "bolt/Core/BinaryContext.h"
#include "bolt/Utils/NameResolver.h"
#include "llvm/MC/MCInstPrinter.h"

namespace llvm {
Expand Down Expand Up @@ -155,5 +156,50 @@ std::string hashBlockLoose(BinaryContext &BC, const BinaryBasicBlock &BB) {
return HashString;
}

/// An even looser hash level relative to $ hashBlockLoose to use with stale
/// profile matching, composed of the names of a block's called functions in
/// lexicographic order.
std::string hashBlockCalls(BinaryContext &BC, const BinaryBasicBlock &BB) {
// The hash is computed by creating a string of all lexicographically ordered
// called function names.
std::vector<std::string> FunctionNames;
for (const MCInst &Instr : BB) {
// Skip non-call instructions.
if (!BC.MIB->isCall(Instr))
continue;
const MCSymbol *CallSymbol = BC.MIB->getTargetSymbol(Instr);
if (!CallSymbol)
continue;
FunctionNames.push_back(std::string(CallSymbol->getName()));
}
std::sort(FunctionNames.begin(), FunctionNames.end());
std::string HashString;
for (const std::string &FunctionName : FunctionNames)
HashString.append(FunctionName);

return HashString;
}

/// The same as the $hashBlockCalls function, but for profiled functions.
std::string
hashBlockCalls(const DenseMap<uint32_t, yaml::bolt::BinaryFunctionProfile *>
&IdToYamlFunction,
const yaml::bolt::BinaryBasicBlockProfile &YamlBB) {
std::vector<std::string> FunctionNames;
for (const yaml::bolt::CallSiteInfo &CallSiteInfo : YamlBB.CallSites) {
auto It = IdToYamlFunction.find(CallSiteInfo.DestId);
if (It == IdToYamlFunction.end())
continue;
StringRef Name = NameResolver::dropNumNames(It->second->Name);
FunctionNames.push_back(std::string(Name));
}
std::sort(FunctionNames.begin(), FunctionNames.end());
std::string HashString;
for (const std::string &FunctionName : FunctionNames)
HashString.append(FunctionName);

return HashString;
}

} // namespace bolt
} // namespace llvm
103 changes: 84 additions & 19 deletions bolt/lib/Profile/StaleProfileMatching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,43 @@ class StaleMatcher {
public:
/// Initialize stale matcher.
void init(const std::vector<FlowBlock *> &Blocks,
const std::vector<BlendedBlockHash> &Hashes) {
const std::vector<BlendedBlockHash> &Hashes,
const std::vector<uint64_t> &CallHashes) {
assert(Blocks.size() == Hashes.size() &&
Hashes.size() == CallHashes.size() &&
"incorrect matcher initialization");
for (size_t I = 0; I < Blocks.size(); I++) {
FlowBlock *Block = Blocks[I];
uint16_t OpHash = Hashes[I].OpcodeHash;
OpHashToBlocks[OpHash].push_back(std::make_pair(Hashes[I], Block));
if (CallHashes[I])
CallHashToBlocks[CallHashes[I]].push_back(
std::make_pair(Hashes[I], Block));
}
}

/// Find the most similar block for a given hash.
const FlowBlock *matchBlock(BlendedBlockHash BlendedHash) const {
const FlowBlock *matchBlock(BlendedBlockHash BlendedHash,
uint64_t CallHash) const {
const FlowBlock *BestBlock = matchWithOpcodes(BlendedHash);
return BestBlock ? BestBlock : matchWithCalls(BlendedHash, CallHash);
}

/// Returns true if the two basic blocks (in the binary and in the profile)
/// corresponding to the given hashes are matched to each other with a high
/// confidence.
static bool isHighConfidenceMatch(BlendedBlockHash Hash1,
BlendedBlockHash Hash2) {
return Hash1.InstrHash == Hash2.InstrHash;
}

private:
using HashBlockPairType = std::pair<BlendedBlockHash, FlowBlock *>;
std::unordered_map<uint16_t, std::vector<HashBlockPairType>> OpHashToBlocks;
std::unordered_map<uint64_t, std::vector<HashBlockPairType>> CallHashToBlocks;

// Uses OpcodeHash to find the most similar block for a given hash.
const FlowBlock *matchWithOpcodes(BlendedBlockHash BlendedHash) const {
auto BlockIt = OpHashToBlocks.find(BlendedHash.OpcodeHash);
if (BlockIt == OpHashToBlocks.end())
return nullptr;
Expand All @@ -220,17 +245,27 @@ class StaleMatcher {
return BestBlock;
}

/// Returns true if the two basic blocks (in the binary and in the profile)
/// corresponding to the given hashes are matched to each other with a high
/// confidence.
static bool isHighConfidenceMatch(BlendedBlockHash Hash1,
BlendedBlockHash Hash2) {
return Hash1.InstrHash == Hash2.InstrHash;
// Uses CallHash to find the most similar block for a given hash.
const FlowBlock *matchWithCalls(BlendedBlockHash BlendedHash,
uint64_t CallHash) const {
if (!CallHash)
return nullptr;
auto BlockIt = CallHashToBlocks.find(CallHash);
if (BlockIt == CallHashToBlocks.end())
return nullptr;
FlowBlock *BestBlock = nullptr;
uint64_t BestDist = std::numeric_limits<uint64_t>::max();
for (const auto &[Hash, Block] : BlockIt->second) {
uint64_t Dist = Hash.OpcodeHash > BlendedHash.OpcodeHash
? Hash.OpcodeHash - BlendedHash.OpcodeHash
: BlendedHash.OpcodeHash - Hash.OpcodeHash;
if (BestBlock == nullptr || Dist < BestDist) {
BestDist = Dist;
BestBlock = Block;
}
}
return BestBlock;
}

private:
using HashBlockPairType = std::pair<BlendedBlockHash, FlowBlock *>;
std::unordered_map<uint16_t, std::vector<HashBlockPairType>> OpHashToBlocks;
};

void BinaryFunction::computeBlockHashes(HashFunction HashFunction) const {
Expand Down Expand Up @@ -412,33 +447,62 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
/// of the basic blocks in the binary, the count is "matched" to the block.
/// Similarly, if both the source and the target of a count in the profile are
/// matched to a jump in the binary, the count is recorded in CFG.
size_t matchWeightsByHashes(
BinaryContext &BC, const BinaryFunction::BasicBlockOrderType &BlockOrder,
const yaml::bolt::BinaryFunctionProfile &YamlBF, FlowFunction &Func) {
size_t
matchWeightsByHashes(BinaryContext &BC,
const BinaryFunction::BasicBlockOrderType &BlockOrder,
const yaml::bolt::BinaryFunctionProfile &YamlBF,
FlowFunction &Func, HashFunction HashFunction,
YAMLProfileReader::ProfileLookupMap &IdToYamlBF) {

assert(Func.Blocks.size() == BlockOrder.size() + 2);

std::vector<uint64_t> CallHashes;
std::vector<FlowBlock *> Blocks;
std::vector<BlendedBlockHash> BlendedHashes;
for (uint64_t I = 0; I < BlockOrder.size(); I++) {
const BinaryBasicBlock *BB = BlockOrder[I];
assert(BB->getHash() != 0 && "empty hash of BinaryBasicBlock");

std::string CallHashStr = hashBlockCalls(BC, *BB);
if (CallHashStr.empty()) {
CallHashes.push_back(0);
} else {
if (HashFunction == HashFunction::StdHash)
CallHashes.push_back(std::hash<std::string>{}(CallHashStr));
else if (HashFunction == HashFunction::XXH3)
CallHashes.push_back(llvm::xxh3_64bits(CallHashStr));
else
llvm_unreachable("Unhandled HashFunction");
}

Blocks.push_back(&Func.Blocks[I + 1]);
BlendedBlockHash BlendedHash(BB->getHash());
BlendedHashes.push_back(BlendedHash);
LLVM_DEBUG(dbgs() << "BB with index " << I << " has hash = "
<< Twine::utohexstr(BB->getHash()) << "\n");
}
StaleMatcher Matcher;
Matcher.init(Blocks, BlendedHashes);
Matcher.init(Blocks, BlendedHashes, CallHashes);

// Index in yaml profile => corresponding (matched) block
DenseMap<uint64_t, const FlowBlock *> MatchedBlocks;
// Match blocks from the profile to the blocks in CFG
for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF.Blocks) {
assert(YamlBB.Hash != 0 && "empty hash of BinaryBasicBlockProfile");
BlendedBlockHash YamlHash(YamlBB.Hash);
const FlowBlock *MatchedBlock = Matcher.matchBlock(YamlHash);
// Always match the entry block.

const FlowBlock *MatchedBlock = nullptr;
std::string CallHashStr = hashBlockCalls(IdToYamlBF, YamlBB);
uint64_t CallHash = 0;
if (!CallHashStr.empty()) {
if (HashFunction == HashFunction::StdHash)
CallHash = std::hash<std::string>{}(CallHashStr);
else if (HashFunction == HashFunction::XXH3)
CallHash = llvm::xxh3_64bits(CallHashStr);
else
llvm_unreachable("Unhandled HashFunction");
}
MatchedBlock = Matcher.matchBlock(YamlHash, CallHash);
if (MatchedBlock == nullptr && YamlBB.Index == 0)
MatchedBlock = Blocks[0];
if (MatchedBlock != nullptr) {
Expand Down Expand Up @@ -763,7 +827,8 @@ bool YAMLProfileReader::inferStaleProfile(

// Match as many block/jump counts from the stale profile as possible
size_t MatchedBlocks =
matchWeightsByHashes(BF.getBinaryContext(), BlockOrder, YamlBF, Func);
matchWeightsByHashes(BF.getBinaryContext(), BlockOrder, YamlBF, Func,
YamlBP.Header.HashFunction, IdToYamLBF);

// Adjust the flow function by marking unreachable blocks Unlikely so that
// they don't get any counts assigned.
Expand Down
4 changes: 4 additions & 0 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,10 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
NormalizeByInsnCount = usesEvent("cycles") || usesEvent("instructions");
NormalizeByCalls = usesEvent("branches");

// Map profiled function ids to names.
for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions)
IdToYamLBF[YamlBF.Id] = &YamlBF;

uint64_t NumUnused = 0;
for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions) {
if (YamlBF.Id >= YamlProfileToFunction.size()) {
Expand Down
Loading

0 comments on commit 88952f5

Please sign in to comment.