23 changes: 22 additions & 1 deletion bolt/include/bolt/Profile/BoltAddressTranslation.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ class BoltAddressTranslation {
/// Save function and basic block hashes used for metadata dump.
void saveMetadata(BinaryContext &BC);

/// Returns BB hash by function output address (after BOLT) and basic block
/// input offset.
size_t getBBHash(uint64_t FuncOutputAddress, uint32_t BBInputOffset) const;

/// Returns BF hash by function output address (after BOLT).
size_t getBFHash(uint64_t OutputAddress) const;

/// Returns BB index by function output address (after BOLT) and basic block
/// input offset.
unsigned getBBIndex(uint64_t FuncOutputAddress, uint32_t BBInputOffset) const;

/// True if a given \p Address is a function with translation table entry.
bool isBATFunction(uint64_t Address) const { return Maps.count(Address); }

private:
/// Helper to update \p Map by inserting one or more BAT entries reflecting
/// \p BB for function located at \p FuncAddress. At least one entry will be
Expand Down Expand Up @@ -144,12 +158,19 @@ class BoltAddressTranslation {

std::map<uint64_t, MapTy> Maps;

using BBHashMap = std::unordered_map<uint32_t, size_t>;
/// Map basic block input offset to a basic block index and hash pair.
using BBHashMap = std::unordered_map<uint32_t, std::pair<unsigned, size_t>>;
std::unordered_map<uint64_t, std::pair<size_t, BBHashMap>> FuncHashes;

/// Map a function to its basic blocks count
std::unordered_map<uint64_t, size_t> NumBasicBlocksMap;

/// Links outlined cold bocks to their original function
std::map<uint64_t, uint64_t> ColdPartSource;

/// Links output address of a main fragment back to input address.
std::unordered_map<uint64_t, uint64_t> ReverseMap;

/// Identifies the address of a control-flow changing instructions in a
/// translation map entry
const static uint32_t BRANCHENTRY = 0x1;
Expand Down
4 changes: 4 additions & 0 deletions bolt/include/bolt/Profile/DataAggregator.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,10 @@ class DataAggregator : public DataReader {
/// Dump data structures into a file readable by llvm-bolt
std::error_code writeAggregatedFile(StringRef OutputFilename) const;

/// Dump translated data structures into YAML
std::error_code writeBATYAML(BinaryContext &BC,
StringRef OutputFilename) const;

/// Filter out binaries based on PID
void filterBinaryMMapInfo();

Expand Down
4 changes: 4 additions & 0 deletions bolt/include/bolt/Profile/YAMLProfileWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef BOLT_PROFILE_YAML_PROFILE_WRITER_H
#define BOLT_PROFILE_YAML_PROFILE_WRITER_H

#include "bolt/Profile/ProfileYAMLMapping.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>

Expand All @@ -29,6 +30,9 @@ class YAMLProfileWriter {

/// Save execution profile for that instance.
std::error_code writeProfile(const RewriteInstance &RI);

static yaml::bolt::BinaryFunctionProfile convert(const BinaryFunction &BF,
bool UseDFS);
};

} // namespace bolt
Expand Down
8 changes: 7 additions & 1 deletion bolt/lib/Core/BinaryContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,13 @@ void BinaryContext::printInstruction(raw_ostream &OS, const MCInst &Instruction,
OS << Endl;
return;
}
InstPrinter->printInst(&Instruction, 0, "", *STI, OS);
if (std::optional<uint32_t> DynamicID =
MIB->getDynamicBranchID(Instruction)) {
OS << "\tjit\t" << MIB->getTargetSymbol(Instruction)->getName()
<< " # ID: " << DynamicID;
} else {
InstPrinter->printInst(&Instruction, 0, "", *STI, OS);
}
if (MIB->isCall(Instruction)) {
if (MIB->isTailCall(Instruction))
OS << " # TAILCALL ";
Expand Down
17 changes: 16 additions & 1 deletion bolt/lib/Core/BinaryFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3350,6 +3350,16 @@ void BinaryFunction::fixBranches() {

// Eliminate unnecessary conditional branch.
if (TSuccessor == FSuccessor) {
// FIXME: at the moment, we cannot safely remove static key branches.
if (MIB->isDynamicBranch(*CondBranch)) {
if (opts::Verbosity) {
BC.outs()
<< "BOLT-INFO: unable to remove redundant dynamic branch in "
<< *this << '\n';
}
continue;
}

BB->removeDuplicateConditionalSuccessor(CondBranch);
if (TSuccessor != NextBB)
BB->addBranchInstruction(TSuccessor);
Expand All @@ -3358,8 +3368,13 @@ void BinaryFunction::fixBranches() {

// Reverse branch condition and swap successors.
auto swapSuccessors = [&]() {
if (MIB->isUnsupportedBranch(*CondBranch))
if (MIB->isUnsupportedBranch(*CondBranch)) {
if (opts::Verbosity) {
BC.outs() << "BOLT-INFO: unable to swap successors in " << *this
<< '\n';
}
return false;
}
std::swap(TSuccessor, FSuccessor);
BB->swapConditionalSuccessors();
auto L = BC.scopeLock();
Expand Down
49 changes: 41 additions & 8 deletions bolt/lib/Core/DebugNames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,7 @@ DWARF5AcceleratorTable::addAccelTableEntry(
addUnit(Unit, DWOID);
}

auto addEntry =
[&](DIEValue ValName) -> std::optional<BOLTDWARF5AccelTableData *> {
auto getName = [&](DIEValue ValName) -> std::optional<std::string> {
if ((!ValName || ValName.getForm() == dwarf::DW_FORM_string) &&
NameToUse.empty())
return std::nullopt;
Expand Down Expand Up @@ -268,7 +267,16 @@ DWARF5AcceleratorTable::addAccelTableEntry(
// This the same hash function used in DWARF5AccelTableData.
It.HashValue = caseFoldingDjbHash(Name);
}
return Name;
};

auto addEntry =
[&](DIEValue ValName) -> std::optional<BOLTDWARF5AccelTableData *> {
std::optional<std::string> Name = getName(ValName);
if (!Name)
return std::nullopt;

auto &It = Entries[*Name];
bool IsTU = false;
uint32_t DieTag = 0;
uint32_t UnitID = getUnitID(Unit, IsTU, DieTag);
Expand All @@ -278,7 +286,7 @@ DWARF5AcceleratorTable::addAccelTableEntry(
if (Iter == CUOffsetsToPatch.end())
BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find "
"DWO ID in CU offsets for second Unit Index "
<< Name << ". For DIE at offset: "
<< *Name << ". For DIE at offset: "
<< Twine::utohexstr(CurrentUnitOffset + Die.getOffset())
<< ".\n";
SecondIndex = Iter->second;
Expand All @@ -295,11 +303,36 @@ DWARF5AcceleratorTable::addAccelTableEntry(
return It.Values.back();
};

std::optional<BOLTDWARF5AccelTableData *> NameEntry =
addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_name));
std::optional<BOLTDWARF5AccelTableData *> LinkageNameEntry =
addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_linkage_name));
return NameEntry ? NameEntry : LinkageNameEntry;
// Minor optimization not to add entry twice for DW_TAG_namespace if it has no
// DW_AT_name.
if (!(Die.getTag() == dwarf::DW_TAG_namespace &&
!Die.findAttribute(dwarf::Attribute::DW_AT_name)))
addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_linkage_name));
// For the purposes of determining whether a debugging information entry has a
// particular attribute (such as DW_AT_name), if debugging information entry A
// has a DW_AT_specification or DW_AT_abstract_origin attribute pointing to
// another debugging information entry B, any attributes of B are considered
// to be part of A.
auto processReferencedDie = [&](const dwarf::Attribute &Attr)
-> std::optional<BOLTDWARF5AccelTableData *> {
const DIEValue Value = Die.findAttribute(Attr);
if (!Value)
return std::nullopt;
const DIEEntry &DIEENtry = Value.getDIEEntry();
DIE &EntryDie = DIEENtry.getEntry();
addEntry(EntryDie.findAttribute(dwarf::Attribute::DW_AT_linkage_name));
return addEntry(EntryDie.findAttribute(dwarf::Attribute::DW_AT_name));
};

if (std::optional<BOLTDWARF5AccelTableData *> Entry =
processReferencedDie(dwarf::Attribute::DW_AT_abstract_origin))
return *Entry;
if (std::optional<BOLTDWARF5AccelTableData *> Entry =
processReferencedDie(dwarf::Attribute::DW_AT_specification))
return *Entry;

return addEntry(Die.findAttribute(dwarf::Attribute::DW_AT_name));
;
}

/// Algorithm from llvm implementation.
Expand Down
22 changes: 22 additions & 0 deletions bolt/lib/Core/MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,28 @@ void MCPlusBuilder::setSize(MCInst &Inst, uint32_t Size) const {
setAnnotationOpValue(Inst, MCAnnotation::kSize, Size);
}

bool MCPlusBuilder::isDynamicBranch(const MCInst &Inst) const {
if (!hasAnnotation(Inst, MCAnnotation::kDynamicBranch))
return false;
assert(isBranch(Inst) && "Branch expected.");
return true;
}

std::optional<uint32_t>
MCPlusBuilder::getDynamicBranchID(const MCInst &Inst) const {
if (std::optional<int64_t> Value =
getAnnotationOpValue(Inst, MCAnnotation::kDynamicBranch)) {
assert(isBranch(Inst) && "Branch expected.");
return static_cast<uint32_t>(*Value);
}
return std::nullopt;
}

void MCPlusBuilder::setDynamicBranch(MCInst &Inst, uint32_t ID) const {
assert(isBranch(Inst) && "Branch expected.");
setAnnotationOpValue(Inst, MCAnnotation::kDynamicBranch, ID);
}

bool MCPlusBuilder::hasAnnotation(const MCInst &Inst, unsigned Index) const {
return (bool)getAnnotationOpValue(Inst, Index);
}
Expand Down
20 changes: 19 additions & 1 deletion bolt/lib/Passes/BinaryPasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ static cl::opt<unsigned>
cl::desc("print statistics about basic block ordering"),
cl::init(0), cl::cat(BoltOptCategory));

static cl::opt<bool> PrintLargeFunctions(
"print-large-functions",
cl::desc("print functions that could not be overwritten due to excessive "
"size"),
cl::init(false), cl::cat(BoltOptCategory));

static cl::list<bolt::DynoStats::Category>
PrintSortedBy("print-sorted-by", cl::CommaSeparated,
cl::desc("print functions sorted by order of dyno stats"),
Expand Down Expand Up @@ -570,8 +576,12 @@ Error CheckLargeFunctions::runOnFunctions(BinaryContext &BC) {
uint64_t HotSize, ColdSize;
std::tie(HotSize, ColdSize) =
BC.calculateEmittedSize(BF, /*FixBranches=*/false);
if (HotSize > BF.getMaxSize())
if (HotSize > BF.getMaxSize()) {
if (opts::PrintLargeFunctions)
BC.outs() << "BOLT-INFO: " << BF << " size exceeds allocated space by "
<< (HotSize - BF.getMaxSize()) << " bytes\n";
BF.setSimple(false);
}
};

ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
Expand Down Expand Up @@ -852,6 +862,10 @@ uint64_t SimplifyConditionalTailCalls::fixTailCalls(BinaryFunction &BF) {
assert(Result && "internal error analyzing conditional branch");
assert(CondBranch && "conditional branch expected");

// Skip dynamic branches for now.
if (BF.getBinaryContext().MIB->isDynamicBranch(*CondBranch))
continue;

// It's possible that PredBB is also a successor to BB that may have
// been processed by a previous iteration of the SCTC loop, in which
// case it may have been marked invalid. We should skip rewriting in
Expand Down Expand Up @@ -1012,6 +1026,10 @@ uint64_t ShortenInstructions::shortenInstructions(BinaryFunction &Function) {
const BinaryContext &BC = Function.getBinaryContext();
for (BinaryBasicBlock &BB : Function) {
for (MCInst &Inst : BB) {
// Skip shortening instructions with Size annotation.
if (BC.MIB->getSize(Inst))
continue;

MCInst OriginalInst;
if (opts::Verbosity > 2)
OriginalInst = Inst;
Expand Down
120 changes: 109 additions & 11 deletions bolt/lib/Profile/BoltAddressTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const char *BoltAddressTranslation::SECTION_NAME = ".note.bolt_bat";
void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,
const BinaryBasicBlock &BB,
uint64_t FuncAddress) {
uint64_t HotFuncAddress = ColdPartSource.count(FuncAddress)
? ColdPartSource[FuncAddress]
: FuncAddress;
const uint64_t BBOutputOffset =
BB.getOutputAddressRange().first - FuncAddress;
const uint32_t BBInputOffset = BB.getInputOffset();
Expand All @@ -39,6 +42,11 @@ void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,
LLVM_DEBUG(dbgs() << "BB " << BB.getName() << "\n");
LLVM_DEBUG(dbgs() << " Key: " << Twine::utohexstr(BBOutputOffset)
<< " Val: " << Twine::utohexstr(BBInputOffset) << "\n");
LLVM_DEBUG(dbgs() << formatv(" Hash: {0:x}\n",
getBBHash(HotFuncAddress, BBInputOffset)));
(void)HotFuncAddress;
LLVM_DEBUG(dbgs() << formatv(" Index: {0}\n",
getBBIndex(HotFuncAddress, BBInputOffset)));
// In case of conflicts (same Key mapping to different Vals), the last
// update takes precedence. Of course it is not ideal to have conflicts and
// those happen when we have an empty BB that either contained only
Expand Down Expand Up @@ -72,20 +80,28 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Writing BOLT Address Translation Tables\n");
for (auto &BFI : BC.getBinaryFunctions()) {
const BinaryFunction &Function = BFI.second;
const uint64_t InputAddress = Function.getAddress();
const uint64_t OutputAddress = Function.getOutputAddress();
// We don't need a translation table if the body of the function hasn't
// changed
if (Function.isIgnored() || (!BC.HasRelocations && !Function.isSimple()))
continue;

// TBD: handle BAT functions w/multiple entry points.
if (Function.isMultiEntry())
continue;

LLVM_DEBUG(dbgs() << "Function name: " << Function.getPrintName() << "\n");
LLVM_DEBUG(dbgs() << " Address reference: 0x"
<< Twine::utohexstr(Function.getOutputAddress()) << "\n");
LLVM_DEBUG(dbgs() << formatv(" Hash: {0:x}\n", getBFHash(OutputAddress)));

MapTy Map;
for (const BinaryBasicBlock *const BB :
Function.getLayout().getMainFragment())
writeEntriesForBB(Map, *BB, Function.getOutputAddress());
Maps.emplace(Function.getOutputAddress(), std::move(Map));
ReverseMap.emplace(OutputAddress, InputAddress);

if (!Function.isSplit())
continue;
Expand All @@ -94,12 +110,12 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
LLVM_DEBUG(dbgs() << " Cold part\n");
for (const FunctionFragment &FF :
Function.getLayout().getSplitFragments()) {
ColdPartSource.emplace(FF.getAddress(), Function.getOutputAddress());
Map.clear();
for (const BinaryBasicBlock *const BB : FF)
writeEntriesForBB(Map, *BB, FF.getAddress());

Maps.emplace(FF.getAddress(), std::move(Map));
ColdPartSource.emplace(FF.getAddress(), Function.getOutputAddress());
}
}

Expand All @@ -109,6 +125,11 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
writeMaps</*Cold=*/true>(Maps, PrevAddress, OS);

BC.outs() << "BOLT-INFO: Wrote " << Maps.size() << " BAT maps\n";
const uint64_t NumBBHashes = std::accumulate(
FuncHashes.begin(), FuncHashes.end(), 0ull,
[](size_t Acc, const auto &B) { return Acc + B.second.second.size(); });
BC.outs() << "BOLT-INFO: Wrote " << FuncHashes.size() << " function and "
<< NumBBHashes << " basic block hashes\n";
}

APInt BoltAddressTranslation::calculateBranchEntriesBitMask(MapTy &Map,
Expand Down Expand Up @@ -155,6 +176,11 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
// Only process cold fragments in cold mode, and vice versa.
if (Cold != ColdPartSource.count(Address))
continue;
// NB: here we use the input address because hashes are saved early (in
// `saveMetadata`) before output addresses are assigned.
const uint64_t HotInputAddress =
ReverseMap[Cold ? ColdPartSource[Address] : Address];
std::pair<size_t, BBHashMap> &FuncHashPair = FuncHashes[HotInputAddress];
MapTy &Map = MapEntry.second;
const uint32_t NumEntries = Map.size();
LLVM_DEBUG(dbgs() << "Writing " << NumEntries << " entries for 0x"
Expand All @@ -166,6 +192,14 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
std::distance(ColdPartSource.begin(), ColdPartSource.find(Address));
encodeULEB128(HotIndex - PrevIndex, OS);
PrevIndex = HotIndex;
} else {
// Function hash
LLVM_DEBUG(dbgs() << "Hash: " << formatv("{0:x}\n", FuncHashPair.first));
OS.write(reinterpret_cast<char *>(&FuncHashPair.first), 8);
// Number of basic blocks
size_t NumBasicBlocks = FuncHashPair.second.size();
LLVM_DEBUG(dbgs() << "Basic blocks: " << NumBasicBlocks << '\n');
encodeULEB128(NumBasicBlocks, OS);
}
encodeULEB128(NumEntries, OS);
// For hot fragments only: encode the number of equal offsets
Expand All @@ -189,6 +223,7 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
}
size_t Index = 0;
uint64_t InOffset = 0;
size_t PrevBBIndex = 0;
// Output and Input addresses and delta-encoded
for (std::pair<const uint32_t, uint32_t> &KeyVal : Map) {
const uint64_t OutputAddress = KeyVal.first + Address;
Expand All @@ -197,6 +232,17 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
if (Index++ >= EqualElems)
encodeSLEB128(KeyVal.second - InOffset, OS);
InOffset = KeyVal.second; // Keeping InOffset as if BRANCHENTRY is encoded
if ((InOffset & BRANCHENTRY) == 0) {
unsigned BBIndex;
size_t BBHash;
std::tie(BBIndex, BBHash) = FuncHashPair.second[InOffset >> 1];
OS.write(reinterpret_cast<char *>(&BBHash), 8);
// Basic block index in the input binary
encodeULEB128(BBIndex - PrevBBIndex, OS);
PrevBBIndex = BBIndex;
LLVM_DEBUG(dbgs() << formatv("{0:x} -> {1:x} {2:x} {3}\n", KeyVal.first,
InOffset >> 1, BBHash, BBIndex));
}
}
}
}
Expand Down Expand Up @@ -239,12 +285,24 @@ void BoltAddressTranslation::parseMaps(std::vector<uint64_t> &HotFuncs,
size_t HotIndex = 0;
for (uint32_t I = 0; I < NumFunctions; ++I) {
const uint64_t Address = PrevAddress + DE.getULEB128(&Offset, &Err);
uint64_t HotAddress = Cold ? 0 : Address;
PrevAddress = Address;
if (Cold) {
HotIndex += DE.getULEB128(&Offset, &Err);
ColdPartSource.emplace(Address, HotFuncs[HotIndex]);
HotAddress = HotFuncs[HotIndex];
ColdPartSource.emplace(Address, HotAddress);
} else {
HotFuncs.push_back(Address);
// Function hash
const size_t FuncHash = DE.getU64(&Offset, &Err);
FuncHashes[Address].first = FuncHash;
LLVM_DEBUG(dbgs() << formatv("{0:x}: hash {1:x}\n", Address, FuncHash));
// Number of basic blocks
const size_t NumBasicBlocks = DE.getULEB128(&Offset, &Err);
NumBasicBlocksMap.emplace(Address, NumBasicBlocks);
LLVM_DEBUG(dbgs() << formatv("{0:x}: #bbs {1}, {2} bytes\n", Address,
NumBasicBlocks,
getULEB128Size(NumBasicBlocks)));
}
const uint32_t NumEntries = DE.getULEB128(&Offset, &Err);
// Equal offsets, hot fragments only.
Expand Down Expand Up @@ -275,6 +333,7 @@ void BoltAddressTranslation::parseMaps(std::vector<uint64_t> &HotFuncs,
LLVM_DEBUG(dbgs() << "Parsing " << NumEntries << " entries for 0x"
<< Twine::utohexstr(Address) << "\n");
uint64_t InputOffset = 0;
size_t BBIndex = 0;
for (uint32_t J = 0; J < NumEntries; ++J) {
const uint64_t OutputDelta = DE.getULEB128(&Offset, &Err);
const uint64_t OutputAddress = PrevAddress + OutputDelta;
Expand All @@ -288,12 +347,28 @@ void BoltAddressTranslation::parseMaps(std::vector<uint64_t> &HotFuncs,
InputOffset += InputDelta;
}
Map.insert(std::pair<uint32_t, uint32_t>(OutputOffset, InputOffset));
LLVM_DEBUG(
dbgs() << formatv("{0:x} -> {1:x} ({2}/{3}b -> {4}/{5}b), {6:x}\n",
OutputOffset, InputOffset, OutputDelta,
getULEB128Size(OutputDelta), InputDelta,
(J < EqualElems) ? 0 : getSLEB128Size(InputDelta),
OutputAddress));
size_t BBHash = 0;
size_t BBIndexDelta = 0;
const bool IsBranchEntry = InputOffset & BRANCHENTRY;
if (!IsBranchEntry) {
BBHash = DE.getU64(&Offset, &Err);
BBIndexDelta = DE.getULEB128(&Offset, &Err);
BBIndex += BBIndexDelta;
// Map basic block hash to hot fragment by input offset
FuncHashes[HotAddress].second.emplace(InputOffset >> 1,
std::pair(BBIndex, BBHash));
}
LLVM_DEBUG({
dbgs() << formatv(
"{0:x} -> {1:x} ({2}/{3}b -> {4}/{5}b), {6:x}", OutputOffset,
InputOffset, OutputDelta, getULEB128Size(OutputDelta), InputDelta,
(J < EqualElems) ? 0 : getSLEB128Size(InputDelta), OutputAddress);
if (!IsBranchEntry) {
dbgs() << formatv(" {0:x} {1}/{2}b", BBHash, BBIndex,
getULEB128Size(BBIndexDelta));
}
dbgs() << '\n';
});
}
Maps.insert(std::pair<uint64_t, MapTy>(Address, Map));
}
Expand All @@ -303,7 +378,12 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
const size_t NumTables = Maps.size();
OS << "BAT tables for " << NumTables << " functions:\n";
for (const auto &MapEntry : Maps) {
OS << "Function Address: 0x" << Twine::utohexstr(MapEntry.first) << "\n";
const uint64_t Address = MapEntry.first;
const uint64_t HotAddress = fetchParentAddress(Address);
OS << "Function Address: 0x" << Twine::utohexstr(Address);
if (HotAddress == 0)
OS << formatv(", hash: {0:x}", getBFHash(Address));
OS << "\n";
OS << "BB mappings:\n";
for (const auto &Entry : MapEntry.second) {
const bool IsBranch = Entry.second & BRANCHENTRY;
Expand All @@ -312,6 +392,9 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
<< "0x" << Twine::utohexstr(Val);
if (IsBranch)
OS << " (branch)";
else
OS << formatv(" hash: {0:x}",
getBBHash(HotAddress ? HotAddress : Address, Val));
OS << "\n";
}
OS << "\n";
Expand Down Expand Up @@ -435,9 +518,24 @@ void BoltAddressTranslation::saveMetadata(BinaryContext &BC) {
FuncHashes[BF.getAddress()].first = BF.computeHash();
BF.computeBlockHashes();
for (const BinaryBasicBlock &BB : BF)
FuncHashes[BF.getAddress()].second.emplace(BB.getInputOffset(),
BB.getHash());
FuncHashes[BF.getAddress()].second.emplace(
BB.getInputOffset(), std::pair(BB.getIndex(), BB.getHash()));
}
}

unsigned BoltAddressTranslation::getBBIndex(uint64_t FuncOutputAddress,
uint32_t BBInputOffset) const {
return FuncHashes.at(FuncOutputAddress).second.at(BBInputOffset).first;
}

size_t BoltAddressTranslation::getBBHash(uint64_t FuncOutputAddress,
uint32_t BBInputOffset) const {
return FuncHashes.at(FuncOutputAddress).second.at(BBInputOffset).second;
}

size_t BoltAddressTranslation::getBFHash(uint64_t OutputAddress) const {
return FuncHashes.at(OutputAddress).first;
}

} // namespace bolt
} // namespace llvm
68 changes: 64 additions & 4 deletions bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Profile/BoltAddressTranslation.h"
#include "bolt/Profile/Heatmap.h"
#include "bolt/Profile/YAMLProfileWriter.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "bolt/Utils/Utils.h"
#include "llvm/ADT/STLExtras.h"
Expand Down Expand Up @@ -85,6 +86,7 @@ MaxSamples("max-samples",
cl::cat(AggregatorCategory));

extern cl::opt<opts::ProfileFormatKind> ProfileFormat;
extern cl::opt<std::string> SaveProfile;

cl::opt<bool> ReadPreAggregated(
"pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
Expand Down Expand Up @@ -594,10 +596,21 @@ Error DataAggregator::readProfile(BinaryContext &BC) {
convertBranchData(Function);
}

if (opts::AggregateOnly &&
opts::ProfileFormat == opts::ProfileFormatKind::PF_Fdata) {
if (std::error_code EC = writeAggregatedFile(opts::OutputFilename))
report_error("cannot create output data file", EC);
if (opts::AggregateOnly) {
if (opts::ProfileFormat == opts::ProfileFormatKind::PF_Fdata)
if (std::error_code EC = writeAggregatedFile(opts::OutputFilename))
report_error("cannot create output data file", EC);

// BAT YAML is handled by DataAggregator since normal YAML output requires
// CFG which is not available in BAT mode.
if (usesBAT()) {
if (opts::ProfileFormat == opts::ProfileFormatKind::PF_YAML)
if (std::error_code EC = writeBATYAML(BC, opts::OutputFilename))
report_error("cannot create output data file", EC);
if (!opts::SaveProfile.empty())
if (std::error_code EC = writeBATYAML(BC, opts::SaveProfile))
report_error("cannot create output data file", EC);
}
}

return Error::success();
Expand Down Expand Up @@ -2258,6 +2271,53 @@ DataAggregator::writeAggregatedFile(StringRef OutputFilename) const {
return std::error_code();
}

std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
StringRef OutputFilename) const {
std::error_code EC;
raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
if (EC)
return EC;

yaml::bolt::BinaryProfile BP;

// Fill out the header info.
BP.Header.Version = 1;
BP.Header.FileName = std::string(BC.getFilename());
std::optional<StringRef> BuildID = BC.getFileBuildID();
BP.Header.Id = BuildID ? std::string(*BuildID) : "<unknown>";
BP.Header.Origin = std::string(getReaderName());
// Only the input binary layout order is supported.
BP.Header.IsDFSOrder = false;
// FIXME: Need to match hash function used to produce BAT hashes.
BP.Header.HashFunction = HashFunction::Default;

ListSeparator LS(",");
raw_string_ostream EventNamesOS(BP.Header.EventNames);
for (const StringMapEntry<std::nullopt_t> &EventEntry : EventNames)
EventNamesOS << LS << EventEntry.first().str();

BP.Header.Flags = opts::BasicAggregation ? BinaryFunction::PF_SAMPLE
: BinaryFunction::PF_LBR;

if (!opts::BasicAggregation) {
// Convert profile for functions not covered by BAT
for (auto &BFI : BC.getBinaryFunctions()) {
BinaryFunction &Function = BFI.second;
if (!Function.hasProfile())
continue;
if (BAT->isBATFunction(Function.getAddress()))
continue;
BP.Functions.emplace_back(
YAMLProfileWriter::convert(Function, /*UseDFS=*/false));
}
}

// Write the profile.
yaml::Output Out(OutFile, nullptr, 0);
Out << BP;
return std::error_code();
}

void DataAggregator::dump() const { DataReader::dump(); }

void DataAggregator::dump(const LBREntry &LBR) const {
Expand Down
35 changes: 13 additions & 22 deletions bolt/lib/Profile/YAMLProfileWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Profile/ProfileReaderBase.h"
#include "bolt/Profile/ProfileYAMLMapping.h"
#include "bolt/Rewrite/RewriteInstance.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
Expand All @@ -26,15 +25,15 @@ extern llvm::cl::opt<bool> ProfileUseDFS;
namespace llvm {
namespace bolt {

namespace {
void convert(const BinaryFunction &BF,
yaml::bolt::BinaryFunctionProfile &YamlBF) {
yaml::bolt::BinaryFunctionProfile
YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
yaml::bolt::BinaryFunctionProfile YamlBF;
const BinaryContext &BC = BF.getBinaryContext();

const uint16_t LBRProfile = BF.getProfileFlags() & BinaryFunction::PF_LBR;

// Prepare function and block hashes
BF.computeHash(opts::ProfileUseDFS);
BF.computeHash(UseDFS);
BF.computeBlockHashes();

YamlBF.Name = BF.getPrintName();
Expand All @@ -44,7 +43,7 @@ void convert(const BinaryFunction &BF,
YamlBF.ExecCount = BF.getKnownExecutionCount();

BinaryFunction::BasicBlockOrderType Order;
llvm::copy(opts::ProfileUseDFS ? BF.dfs() : BF.getLayout().blocks(),
llvm::copy(UseDFS ? BF.dfs() : BF.getLayout().blocks(),
std::back_inserter(Order));

for (const BinaryBasicBlock *BB : Order) {
Expand Down Expand Up @@ -106,20 +105,14 @@ void convert(const BinaryFunction &BF,
TargetName = Callee->getOneName();
}

auto getAnnotationWithDefault = [&](const MCInst &Inst, StringRef Ann) {
return BC.MIB->getAnnotationWithDefault(Instr, Ann, 0ull);
};
if (BC.MIB->getConditionalTailCall(Instr)) {
auto CTCCount =
BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCTakenCount");
if (CTCCount) {
CSI.Count = *CTCCount;
auto CTCMispreds =
BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCMispredCount");
if (CTCMispreds)
CSI.Mispreds = *CTCMispreds;
}
CSI.Count = getAnnotationWithDefault(Instr, "CTCTakenCount");
CSI.Mispreds = getAnnotationWithDefault(Instr, "CTCMispredCount");
} else {
auto Count = BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "Count");
if (Count)
CSI.Count = *Count;
CSI.Count = getAnnotationWithDefault(Instr, "Count");
}

if (CSI.Count)
Expand Down Expand Up @@ -165,8 +158,8 @@ void convert(const BinaryFunction &BF,

YamlBF.Blocks.emplace_back(YamlBB);
}
return YamlBF;
}
} // end anonymous namespace

std::error_code YAMLProfileWriter::writeProfile(const RewriteInstance &RI) {
const BinaryContext &BC = RI.getBinaryContext();
Expand Down Expand Up @@ -222,9 +215,7 @@ std::error_code YAMLProfileWriter::writeProfile(const RewriteInstance &RI) {
if (!BF.hasValidProfile() && !RI.getProfileReader()->isTrustedSource())
continue;

yaml::bolt::BinaryFunctionProfile YamlBF;
convert(BF, YamlBF);
BP.Functions.emplace_back(YamlBF);
BP.Functions.emplace_back(convert(BF, opts::ProfileUseDFS));
}
}

Expand Down
6 changes: 2 additions & 4 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,9 @@ class DIEStreamer : public DwarfStreamer {
DIEStreamer(DIEBuilder *DIEBldr, DWARFRewriter &Rewriter,
DWARFLinkerBase::OutputFileType OutFileType,
raw_pwrite_stream &OutFile,
std::function<StringRef(StringRef Input)> Translator,
DWARFLinkerBase::MessageHandlerTy Warning)
: DwarfStreamer(OutFileType, OutFile, Translator, Warning),
DIEBldr(DIEBldr), Rewriter(Rewriter){};
: DwarfStreamer(OutFileType, OutFile, Warning), DIEBldr(DIEBldr),
Rewriter(Rewriter){};

using DwarfStreamer::emitCompileUnitHeader;

Expand Down Expand Up @@ -469,7 +468,6 @@ createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,

std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>(
&DIEBldr, Rewriter, DWARFLinkerBase::OutputFileType::Object, OutFile,
[](StringRef Input) -> StringRef { return Input; },
[&](const Twine &Warning, StringRef Context, const DWARFDie *) {});
Error Err = Streamer->init(TheTriple, Swift5ReflectionSegmentName);
if (Err)
Expand Down
387 changes: 387 additions & 0 deletions bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Large diffs are not rendered by default.

25 changes: 11 additions & 14 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,7 @@ static cl::opt<cl::boolOrDefault> RelocationMode(
"relocs", cl::desc("use relocations in the binary (default=autodetect)"),
cl::cat(BoltCategory));

static cl::opt<std::string>
SaveProfile("w",
cl::desc("save recorded profile to a file"),
cl::cat(BoltOutputCategory));
extern cl::opt<std::string> SaveProfile;

static cl::list<std::string>
SkipFunctionNames("skip-funcs",
Expand Down Expand Up @@ -732,6 +729,13 @@ Error RewriteInstance::run() {
// Skip disassembling if we have a translation table and we are running an
// aggregation job.
if (opts::AggregateOnly && BAT->enabledFor(InputFile)) {
// YAML profile in BAT mode requires CFG for .bolt.org.text functions
if (!opts::SaveProfile.empty() ||
opts::ProfileFormat == opts::ProfileFormatKind::PF_YAML) {
selectFunctionsToProcess();
disassembleFunctions();
buildFunctionsCFG();
}
processProfileData();
return Error::success();
}
Expand Down Expand Up @@ -2027,14 +2031,6 @@ void RewriteInstance::adjustCommandLineOptions() {

if (opts::Lite)
BC->outs() << "BOLT-INFO: enabling lite mode\n";

if (!opts::SaveProfile.empty() && BAT->enabledFor(InputFile)) {
BC->errs()
<< "BOLT-ERROR: unable to save profile in YAML format for input "
"file processed by BOLT. Please remove -w option and use branch "
"profile.\n";
exit(1);
}
}

namespace {
Expand Down Expand Up @@ -3126,12 +3122,13 @@ void RewriteInstance::processProfileData() {
}
}

if (!opts::SaveProfile.empty()) {
if (!opts::SaveProfile.empty() && !BAT->enabledFor(InputFile)) {
YAMLProfileWriter PW(opts::SaveProfile);
PW.writeProfile(*this);
}
if (opts::AggregateOnly &&
opts::ProfileFormat == opts::ProfileFormatKind::PF_YAML) {
opts::ProfileFormat == opts::ProfileFormatKind::PF_YAML &&
!BAT->enabledFor(InputFile)) {
YAMLProfileWriter PW(opts::OutputFilename);
PW.writeProfile(*this);
}
Expand Down
13 changes: 13 additions & 0 deletions bolt/lib/Target/X86/X86MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,9 @@ class X86MCPlusBuilder : public MCPlusBuilder {
}

bool isUnsupportedBranch(const MCInst &Inst) const override {
if (isDynamicBranch(Inst))
return true;

switch (Inst.getOpcode()) {
default:
return false;
Expand Down Expand Up @@ -2728,6 +2731,7 @@ class X86MCPlusBuilder : public MCPlusBuilder {

void createUncondBranch(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
Inst.clear();
Inst.setOpcode(X86::JMP_1);
Inst.clear();
Inst.addOperand(MCOperand::createExpr(
Expand Down Expand Up @@ -2776,6 +2780,15 @@ class X86MCPlusBuilder : public MCPlusBuilder {
Inst.addOperand(MCOperand::createImm(CC));
}

void createLongCondBranch(MCInst &Inst, const MCSymbol *Target, unsigned CC,
MCContext *Ctx) const override {
Inst.setOpcode(X86::JCC_4);
Inst.clear();
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx)));
Inst.addOperand(MCOperand::createImm(CC));
}

bool reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
unsigned InvCC = getInvertedCondCode(getCondCode(Inst));
Expand Down
4 changes: 4 additions & 0 deletions bolt/lib/Utils/CommandLineOpts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ cl::opt<ProfileFormatKind> ProfileFormat(
clEnumValN(PF_YAML, "yaml", "dense YAML representation")),
cl::ZeroOrMore, cl::Hidden, cl::cat(BoltCategory));

cl::opt<std::string> SaveProfile("w",
cl::desc("save recorded profile to a file"),
cl::cat(BoltOutputCategory));

cl::opt<bool> SplitEH("split-eh", cl::desc("split C++ exception handling code"),
cl::Hidden, cl::cat(BoltOptCategory));

Expand Down
81 changes: 81 additions & 0 deletions bolt/test/X86/Inputs/blarge_new.preagg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
B 40164b 401608 109 0
B 401611 4017e0 115 0
B 4017f0 401616 117 0
B 401ba2 4015da 6 0
B 4015d5 401b60 1 0
B 40159a 401b60 5 0
B 401b9d 401b70 615 2
B 401b90 401b99 344 37
B 401ba2 40159f 8 0
B 4015b0 401070 9 0
B 401544 4014a0 6 0
B 40188a 401928 5 0
B 40152a 4014b0 21 0
B 40169e 40165b 2 0
B 4014dd 401070 12 1
B 401509 4014ec 2 2
B 401510 401030 673 0
B 4019de 401080 1 0
B 401500 401070 22 0
B 401921 4014d6 9 0
B 4019b3 401080 3 0
B 40162d 401070 113 0
B 4014d1 401800 27 0
B 401a3f 401080 1 0
B 4018d2 401050 17 0
B 401664 4017c0 2 0
B 401680 401070 2 0
B 4017d0 401669 2 0
B 4018f7 40190d 9 0
B 4015bc 401592 6 0
B 401964 401090 5 0
B 4015f8 4015cd 1 0
B 4015ec 401070 6 0
F 40165b 401664 2
F 4017c0 4017d0 2
F 401669 401680 2
F 40190d 401921 9
F 4014d6 4014dd 9
F 401800 4018d2 17
F 4018d7 4018f7 9
F 40159f 4015b0 8
F 401515 401544 6
F 401070 401500 1
F 401070 401070 157
F 4014a0 4014d1 6
F 401616 40162d 112
F 4019e3 401a3f 1
F 4014e2 401500 19
F 401090 401090 5
F 401030 401030 673
F 401505 401510 668
F 401616 4017f0 2
F 401070 4015b0 1
F 4015da 4015ec 6
F 401b60 401b90 6
F 4019b8 4019de 1
F 401969 4019b3 3
F 401505 401509 2
F 401515 40152a 21
F 401592 40159a 4
F 401050 401050 17
F 4015cd 4015d5 1
F 401070 4014dd 1
F 401b99 401ba2 8
F 401b70 401b90 326
F 401b99 401b9d 324
F 401592 4015bc 1
F 401608 401611 109
F 401b70 401b9d 268
F 4015b5 4015bc 5
F 401b99 401b90 1
F 401b70 401ba2 5
F 401632 40164b 108
F 401080 401080 5
F 4014b0 4014d1 21
F 4017e0 4017f0 115
F 4015f1 4015f8 1
F 401685 40169e 2
F 401928 401964 5
F 401800 40188a 5
F 4014ec 401500 2
1,648 changes: 1,648 additions & 0 deletions bolt/test/X86/Inputs/blarge_new.yaml

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions bolt/test/X86/Inputs/blarge_new_bat.preagg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
B 40169e 40165b 7 0
B 401664 800012 7 0
B 401680 401070 7 0
B 800022 401669 7 0
B 401611 800000 121 0
B 40162d 401070 119 0
B 4015d5 800040 2 0
B 800080 4015da 6 0
B 40164b 401608 115 0
B 800080 40159f 24 0
B 4015ec 401070 6 0
B 8001d0 401090 1 0
B 4014d1 800082 25 0
B 401510 401030 616 0
B 8002ab 401080 1 0
B 80007b 80004c 483 1
B 800072 80004c 597 77
B 80010c 800194 1 0
B 401509 4014ec 1 0
B 800010 401616 119 0
B 80024a 401080 1 0
B 800154 401050 20 0
B 4014dd 401070 9 0
B 80021f 401080 1 0
B 800193 4014d6 8 0
B 40159a 800040 19 0
B 4015f8 4015cd 2 0
B 40152a 4014b0 24 0
B 401500 401070 15 0
B 4015bc 401592 21 0
B 401544 4014a0 1 0
B 80004a 800052 24 0
B 4015b0 401070 20 0
B 800050 80007d 29 29
F 401685 40169e 7
F 4014a0 4014d1 1
F 401090 401090 1
F 401050 401050 20
F 40159f 4015b0 20
F 80007d 800080 27
F 401515 401544 1
F 4014e2 401500 13
F 401592 40159a 19
F 401505 401509 1
F 4014b0 4014d1 24
F 800194 8001d0 1
F 8001d5 80021f 1
F 401616 40162d 114
F 80024f 8002ab 1
F 800159 800193 7
F 80004c 800050 26
F 800224 80024a 1
F 800082 80010c 1
F 401080 401080 3
F 401070 401070 168
F 80004c 800072 555
F 401616 800010 2
F 401070 40162d 4
F 800082 800154 20
F 401669 401680 7
F 40159f 800080 1
F 4014ec 401500 1
F 800012 800022 7
F 401030 401030 616
F 80004c 80007b 473
F 800052 800072 24
F 800040 80004a 21
F 4015b5 4015bc 18
F 4015cd 4015d5 2
F 401592 4015bc 1
F 4015da 4015ec 6
F 4015f1 4015f8 2
F 800000 800010 116
F 401608 401611 115
F 401632 40164b 114
F 401515 40152a 24
F 40165b 401664 7
F 401505 401510 612
F 4014d6 4014dd 8
40 changes: 40 additions & 0 deletions bolt/test/X86/bolt-address-translation-yaml.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Check new BAT format containing hashes for YAML profile.

RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe
RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt \
RUN: --reorder-blocks=ext-tsp --split-functions --split-strategy=cdsplit \
RUN: --reorder-functions=cdsort --enable-bat --dyno-stats --skip-funcs=main \
RUN: 2>&1 | FileCheck --check-prefix WRITE-BAT-CHECK %s
RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat.preagg.txt -w %t.yaml -o %t.fdata \
RUN: 2>&1 | FileCheck --check-prefix READ-BAT-CHECK %s
RUN: FileCheck --input-file %t.yaml --check-prefix YAML-BAT-CHECK %s
# Check that YAML converted from fdata matches YAML created directly with BAT.
RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o /dev/null
RUN: FileCheck --input-file %t.yaml-fdata --check-prefix YAML-BAT-CHECK %s

# Test resulting YAML profile with the original binary (no-stale mode)
RUN: llvm-bolt %t.exe -data %t.yaml -o %t.null -dyno-stats \
RUN: | FileCheck --check-prefix CHECK-BOLT-YAML %s

WRITE-BAT-CHECK: BOLT-INFO: Wrote 5 BAT maps
WRITE-BAT-CHECK: BOLT-INFO: Wrote 4 function and 22 basic block hashes
WRITE-BAT-CHECK: BOLT-INFO: BAT section size (bytes): 380

READ-BAT-CHECK-NOT: BOLT-ERROR: unable to save profile in YAML format for input file processed by BOLT
READ-BAT-CHECK: BOLT-INFO: Parsed 5 BAT entries
READ-BAT-CHECK: PERF2BOLT: read 79 aggregated LBR entries

YAML-BAT-CHECK: functions:
YAML-BAT-CHECK: - name: main
YAML-BAT-CHECK-NEXT: fid: 2
YAML-BAT-CHECK-NEXT: hash: 0x9895746D48B2C876
YAML-BAT-CHECK-NEXT: exec: 0
YAML-BAT-CHECK-NEXT: nblocks: 46
YAML-BAT-CHECK-NEXT: blocks:
YAML-BAT-CHECK-NEXT: - bid: 0
YAML-BAT-CHECK-NEXT: insns: 26
YAML-BAT-CHECK-NEXT: hash: 0xA900AE79CFD40000
YAML-BAT-CHECK-NEXT: succ: [ { bid: 3, cnt: 0 }, { bid: 1, cnt: 0 } ]

CHECK-BOLT-YAML: pre-processing profile using YAML profile reader
CHECK-BOLT-YAML-NEXT: 1 out of 16 functions in the binary (6.2%) have non-empty execution profile
11 changes: 6 additions & 5 deletions bolt/test/X86/bolt-address-translation.test
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,22 @@
#
# CHECK: BOLT: 3 out of 7 functions were overwritten.
# CHECK: BOLT-INFO: Wrote 6 BAT maps
# CHECK: BOLT-INFO: BAT section size (bytes): 336
# CHECK: BOLT-INFO: Wrote 3 function and 58 basic block hashes
# CHECK: BOLT-INFO: BAT section size (bytes): 920
#
# usqrt mappings (hot part). We match against any key (left side containing
# the bolted binary offsets) because BOLT may change where it puts instructions
# depending on whether it is relaxing a branch or not. But the original input
# binary offsets (right side) should be the same because these addresses are
# hardcoded in the blarge.yaml file.
#
# CHECK-BAT-DUMP: Function Address: 0x401170
# CHECK-BAT-DUMP: Function Address: 0x401170, hash: 0xace6cbc638b31983
# CHECK-BAT-DUMP-NEXT: BB mappings:
# CHECK-BAT-DUMP-NEXT: 0x0 -> 0x0
# CHECK-BAT-DUMP-NEXT: 0x0 -> 0x0 hash: 0x36007ba1d80c0000
# CHECK-BAT-DUMP-NEXT: 0x8 -> 0x8 (branch)
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x39
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x39 hash: 0x5c06705524800039
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x3d (branch)
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x10
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x10 hash: 0xd70d7a64320e0010
# CHECK-BAT-DUMP-NEXT: 0x{{.*}} -> 0x30 (branch)
#
# CHECK-BAT-DUMP: 3 cold mappings
Expand Down
694 changes: 694 additions & 0 deletions bolt/test/X86/dwarf5-debug-names-dw-at-specification.s

Large diffs are not rendered by default.

273 changes: 160 additions & 113 deletions bolt/test/X86/dwarf5-debug-names-generate-debug-names.test
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
; BOLT: [[OFFSET2:0x[0-9a-f]*]]: Compile Unit
; BOLT: Name Index @ 0x0 {
; BOLT-NEXT: Header {
; BOLT-NEXT: Length: 0x109
; BOLT-NEXT: Length: 0x155
; BOLT-NEXT: Format: DWARF32
; BOLT-NEXT: Version: 5
; BOLT-NEXT: CU count: 2
; BOLT-NEXT: Local TU count: 0
; BOLT-NEXT: Foreign TU count: 0
; BOLT-NEXT: Bucket count: 8
; BOLT-NEXT: Name count: 8
; BOLT-NEXT: Abbreviations table size: 0x1F
; BOLT-NEXT: Bucket count: 10
; BOLT-NEXT: Name count: 10
; BOLT-NEXT: Abbreviations table size: 0x29
; BOLT-NEXT: Augmentation: 'BOLT'
; BOLT-NEXT: }
; BOLT-NEXT: Compilation Unit offsets [
Expand All @@ -31,136 +31,183 @@
; BOLT-NEXT: ]
; BOLT-NEXT: Abbreviations [
; BOLT-NEXT: Abbreviation [[ABBREV1:0x[0-9a-f]*]] {
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: DW_FORM_data1
; BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
; BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
; BOLT-NEXT: }
; BOLT-NEXT: Abbreviation [[ABBREV2:0x[0-9a-f]*]] {
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
; BOLT-NEXT: DW_IDX_compile_unit: DW_FORM_data1
; BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
; BOLT-NEXT: DW_IDX_parent: DW_FORM_ref4
; BOLT-NEXT: }
; BOLT-NEXT: Abbreviation [[ABBREV3:0x[0-9a-f]*]] {
; BOLT-NEXT: Tag: DW_TAG_variable
; BOLT-NEXT: DW_IDX_compile_unit: DW_FORM_data1
; BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
; BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
; BOLT-NEXT: }
; BOLT-NEXT: Abbreviation [[ABBREV3:0x[0-9a-f]*]] {
; BOLT-NEXT: Tag: DW_TAG_variable
; BOLT-NEXT: Abbreviation [[ABBREV4:0x[0-9a-f]*]] {
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: DW_IDX_compile_unit: DW_FORM_data1
; BOLT-NEXT: DW_IDX_die_offset: DW_FORM_ref4
; BOLT-NEXT: DW_IDX_parent: DW_FORM_flag_present
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 0 [
; BOLT-NEXT: Name 1 {
; BOLT-NEXT: Hash: 0xB888030
; BOLT-NEXT: String: {{.+}} "int"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000033
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: Name 1 {
; BOLT-NEXT: Hash: 0x2124BB36
; BOLT-NEXT: String: {{.+}} "useFoo"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000037
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000081
; BOLT-NEXT: DW_IDX_parent: Entry @ 0x14b
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x0000007f
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 1 [
; BOLT-NEXT: EMPTY
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 2 [
; BOLT-NEXT: Name 2 {
; BOLT-NEXT: Hash: 0xFDE4B5D2
; BOLT-NEXT: String: {{.+}} "fooVar"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV3]]
; BOLT-NEXT: Tag: DW_TAG_variable
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000028
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 1 [
; BOLT-NEXT: Name 2 {
; BOLT-NEXT: Hash: 0xB887389
; BOLT-NEXT: String: {{.+}} "foo"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x0000005e
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 3 [
; BOLT-NEXT: EMPTY
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 4 [
; BOLT-NEXT: EMPTY
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 5 [
; BOLT-NEXT: EMPTY
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 6 [
; BOLT-NEXT: Name 3 {
; BOLT-NEXT: Hash: 0xB88B3D2
; BOLT-NEXT: String: {{.+}} "use"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000028
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: Name 3 {
; BOLT-NEXT: Hash: 0x8C06E589
; BOLT-NEXT: String: {{.+}} "_Z3usePiS_"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000028
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: Name 4 {
; BOLT-NEXT: Hash: 0x69A7307E
; BOLT-NEXT: String: {{.+}} "_Z6useFooPi"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000037
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_inlined_subroutine
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000081
; BOLT-NEXT: DW_IDX_parent: Entry @ 0x14b
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 2 [
; BOLT-NEXT: Name 4 {
; BOLT-NEXT: Hash: 0xB88B3D2
; BOLT-NEXT: String: {{.+}} "use"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000028
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: Name 5 {
; BOLT-NEXT: Hash: 0x7C9A7F6A
; BOLT-NEXT: String: {{.+}} "main"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000049
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: Name 5 {
; BOLT-NEXT: Hash: 0x7C9A7F6A
; BOLT-NEXT: String: {{.+}} "main"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000049
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: Name 6 {
; BOLT-NEXT: Hash: 0xB5063CFE
; BOLT-NEXT: String: {{.+}} "_Z3fooi"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x0000005e
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: Name 6 {
; BOLT-NEXT: Hash: 0xFDE4B5D2
; BOLT-NEXT: String: {{.+}} "fooVar"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV3]]
; BOLT-NEXT: Tag: DW_TAG_variable
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000028
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 7 [
; BOLT-NEXT: Name 7 {
; BOLT-NEXT: Hash: 0x8C06E589
; BOLT-NEXT: String: {{.+}} "_Z3usePiS_"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000028
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 3 [
; BOLT-NEXT: Name 7 {
; BOLT-NEXT: Hash: 0x7C952063
; BOLT-NEXT: String: {{.+}} "char"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000092
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 8 [
; BOLT-NEXT: Name 8 {
; BOLT-NEXT: Hash: 0xB888030
; BOLT-NEXT: String: {{.+}} "int"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV4]]
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x00000033
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: Entry @ 0x{{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV4]]
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x0000007f
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 4 [
; BOLT-NEXT: EMPTY
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 5 [
; BOLT-NEXT: EMPTY
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 6 [
; BOLT-NEXT: Name 8 {
; BOLT-NEXT: Hash: 0xB5063CFE
; BOLT-NEXT: String: {{.+}} "_Z3fooi"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x0000005e
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 9 [
; BOLT-NEXT: Name 9 {
; BOLT-NEXT: Hash: 0xB887389
; BOLT-NEXT: String: {{.+}} "foo"
; BOLT-NEXT: Entry @ 0x14b {
; BOLT-NEXT: Abbrev: [[ABBREV1]]
; BOLT-NEXT: Tag: DW_TAG_subprogram
; BOLT-NEXT: DW_IDX_compile_unit: 0x01
; BOLT-NEXT: DW_IDX_die_offset: 0x0000005e
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 7 [
; BOLT-NEXT: EMPTY
; BOLT-NEXT: ]
; BOLT-NEXT: }
; BOLT-NEXT: Name 10 {
; BOLT-NEXT: Hash: 0x7C952063
; BOLT-NEXT: String: {{.+}} "char"
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV4]]
; BOLT-NEXT: Tag: DW_TAG_base_type
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000092
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: }
9 changes: 1 addition & 8 deletions bolt/test/X86/dwarf5-debug-names.test
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
; BOLT: [[OFFSET2:0x[0-9a-f]*]]: Compile Unit
; BOLT: Name Index @ 0x0 {
; BOLT-NEXT: Header {
; BOLT-NEXT: Length: 0x1DA
; BOLT-NEXT: Length: 0x1D4
; BOLT-NEXT: Format: DWARF32
; BOLT-NEXT: Version: 5
; BOLT-NEXT: CU count: 2
Expand Down Expand Up @@ -101,13 +101,6 @@
; BOLT-NEXT: DW_IDX_die_offset: 0x00000061
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV2]]
; BOLT-NEXT: Tag: DW_TAG_namespace
; BOLT-NEXT: DW_IDX_compile_unit: 0x00
; BOLT-NEXT: DW_IDX_die_offset: 0x00000061
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: Name 4 {
; BOLT-NEXT: Hash: 0xBA564846
Expand Down
7 changes: 3 additions & 4 deletions bolt/test/X86/dwarf5-label-low-pc.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

# RUN: llvm-dwarfdump --show-form --verbose --debug-addr %t.bolt > %t.txt
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt >> %t.txt
# RUN: cat %t.txt | FileCheck --check-prefix=POSTCHECK %s

# This test checks that we correctly handle DW_AT_low_pc [DW_FORM_addrx] that is part of DW_TAG_label.

Expand Down Expand Up @@ -35,16 +36,14 @@
# POSTCHECK-NEXT: DW_AT_name
# POSTCHECK-NEXT: DW_AT_decl_file
# POSTCHECK-NEXT: DW_AT_decl_line
# POSTCHECK-NEXT:
# POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addrx] (indexed (00000002)
# POSTCHECK-SAME: [0x[[#ADDR]]
# POSTCHECK-SAME: 0x[[#ADDR]]
# POSTCHECK: DW_TAG_label
# POSTCHECK-NEXT: DW_AT_name
# POSTCHECK-NEXT: DW_AT_decl_file
# POSTCHECK-NEXT: DW_AT_decl_line
# POSTCHECK-NEXT:
# POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addrx] (indexed (00000003)
# POSTCHECK-SAME: [0x[[#ADDR2]]
# POSTCHECK-SAME: 0x[[#ADDR2]]

# clang++ main.cpp -g -S
# int main() {
Expand Down
8 changes: 1 addition & 7 deletions bolt/test/X86/dwarf5-one-cu-debug-names.test
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
; BOLT: [[OFFSET1:0x[0-9a-f]*]]: Compile Unit
; BOLT: Name Index @ 0x0 {
; BOLT-NEXT: Header {
; BOLT-NEXT: Length: 0x154
; BOLT-NEXT: Length: 0x14F
; BOLT-NEXT: Format: DWARF32
; BOLT-NEXT: Version: 5
; BOLT-NEXT: CU count: 1
Expand Down Expand Up @@ -183,12 +183,6 @@
; BOLT-NEXT: DW_IDX_die_offset: 0x00000061
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: Entry @ {{.+}} {
; BOLT-NEXT: Abbrev: [[ABBREV6]]
; BOLT-NEXT: Tag: DW_TAG_namespace
; BOLT-NEXT: DW_IDX_die_offset: 0x00000061
; BOLT-NEXT: DW_IDX_parent: <parent not indexed>
; BOLT-NEXT: }
; BOLT-NEXT: }
; BOLT-NEXT: ]
; BOLT-NEXT: Bucket 9 [
Expand Down
67 changes: 67 additions & 0 deletions bolt/test/X86/linux-static-keys.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# REQUIRES: system-linux

## Check that BOLT correctly updates the Linux kernel static keys jump table.

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr

## Verify static keys jump bindings to instructions.

# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \
# RUN: --bolt-info=0 |& FileCheck %s

## Verify the bindings again on the rewritten binary with nops removed.

# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s

# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 static keys jump entries

.text
.globl _start
.type _start, %function
_start:
# CHECK: Binary Function "_start"
nop
.L0:
jmp .L1
# CHECK: jit
# CHECK-SAME: # ID: 1 {{.*}} # Likely: 0 # InitValue: 1
nop
.L1:
.nops 5
# CHECK: jit
# CHECK-SAME: # ID: 2 {{.*}} # Likely: 1 # InitValue: 1
.L2:
nop
.size _start, .-_start

.globl foo
.type foo, %function
foo:
ret
.size foo, .-foo


## Static keys jump table.
.rodata
.globl __start___jump_table
.type __start___jump_table, %object
__start___jump_table:

.long .L0 - . # Jump address
.long .L1 - . # Target address
.quad 1 # Key address

.long .L1 - . # Jump address
.long .L2 - . # Target address
.quad 0 # Key address

.globl __stop___jump_table
.type __stop___jump_table, %object
__stop___jump_table:

## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
#include "SuspiciousReallocUsageCheck.h"
#include "SuspiciousSemicolonCheck.h"
#include "SuspiciousStringCompareCheck.h"
#include "SuspiciousStringviewDataUsageCheck.h"
#include "SwappedArgumentsCheck.h"
#include "SwitchMissingDefaultCaseCheck.h"
#include "TerminatingContinueCheck.h"
Expand Down Expand Up @@ -218,6 +219,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-suspicious-semicolon");
CheckFactories.registerCheck<SuspiciousStringCompareCheck>(
"bugprone-suspicious-string-compare");
CheckFactories.registerCheck<SuspiciousStringviewDataUsageCheck>(
"bugprone-suspicious-stringview-data-usage");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
"bugprone-swapped-arguments");
CheckFactories.registerCheck<TerminatingContinueCheck>(
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_clang_library(clangTidyBugproneModule
ImplicitWideningOfMultiplicationResultCheck.cpp
InaccurateEraseCheck.cpp
IncorrectEnableIfCheck.cpp
SuspiciousStringviewDataUsageCheck.cpp
SwitchMissingDefaultCaseCheck.cpp
IncDecInConditionsCheck.cpp
IncorrectRoundingsCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===--- SuspiciousStringviewDataUsageCheck.cpp - clang-tidy --------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "SuspiciousStringviewDataUsageCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::bugprone {

SuspiciousStringviewDataUsageCheck::SuspiciousStringviewDataUsageCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StringViewTypes(utils::options::parseStringList(Options.get(
"StringViewTypes", "::std::basic_string_view;::llvm::StringRef"))),
AllowedCallees(
utils::options::parseStringList(Options.get("AllowedCallees", ""))) {}

void SuspiciousStringviewDataUsageCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "StringViewTypes",
utils::options::serializeStringList(StringViewTypes));
Options.store(Opts, "AllowedCallees",
utils::options::serializeStringList(AllowedCallees));
}

bool SuspiciousStringviewDataUsageCheck::isLanguageVersionSupported(
const LangOptions &LangOpts) const {
return LangOpts.CPlusPlus;
}

std::optional<TraversalKind>
SuspiciousStringviewDataUsageCheck::getCheckTraversalKind() const {
return TK_AsIs;
}

void SuspiciousStringviewDataUsageCheck::registerMatchers(MatchFinder *Finder) {

auto AncestorCall = anyOf(
cxxConstructExpr(), callExpr(unless(cxxOperatorCallExpr())), lambdaExpr(),
initListExpr(
hasType(qualType(hasCanonicalType(hasDeclaration(recordDecl()))))));

auto DataMethod =
cxxMethodDecl(hasName("data"),
ofClass(matchers::matchesAnyListedName(StringViewTypes)));

auto SizeCall = cxxMemberCallExpr(
callee(cxxMethodDecl(hasAnyName("size", "length"))),
on(ignoringParenImpCasts(
matchers::isStatementIdenticalToBoundNode("self"))));

auto DescendantSizeCall = expr(hasDescendant(
expr(SizeCall, hasAncestor(expr(AncestorCall).bind("ancestor-size")),
hasAncestor(expr(equalsBoundNode("parent"),
equalsBoundNode("ancestor-size"))))));

Finder->addMatcher(
cxxMemberCallExpr(
on(ignoringParenImpCasts(expr().bind("self"))), callee(DataMethod),
expr().bind("data-call"),
hasParent(expr(anyOf(
invocation(
expr().bind("parent"), unless(cxxOperatorCallExpr()),
hasAnyArgument(
ignoringParenImpCasts(equalsBoundNode("data-call"))),
unless(hasAnyArgument(ignoringParenImpCasts(SizeCall))),
unless(hasAnyArgument(DescendantSizeCall)),
hasDeclaration(namedDecl(
unless(matchers::matchesAnyListedName(AllowedCallees))))),
initListExpr(expr().bind("parent"),
hasType(qualType(hasCanonicalType(hasDeclaration(
recordDecl(unless(matchers::matchesAnyListedName(
AllowedCallees))))))),
unless(DescendantSizeCall)))))),
this);
}

void SuspiciousStringviewDataUsageCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *DataCallExpr =
Result.Nodes.getNodeAs<CXXMemberCallExpr>("data-call");
diag(DataCallExpr->getExprLoc(),
"result of a `data()` call may not be null terminated, provide size "
"information to the callee to prevent potential issues")
<< DataCallExpr->getCallee()->getSourceRange();
}

} // namespace clang::tidy::bugprone
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--- SuspiciousStringviewDataUsageCheck.h - clang-tidy -------//C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSSTRINGVIEWDATAUSAGECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSSTRINGVIEWDATAUSAGECHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::bugprone {

/// Identifies suspicious usages of std::string_view::data() that could lead to
/// reading out-of-bounds data due to inadequate or incorrect string null
/// termination.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/suspicious-stringview-data-usage.html
class SuspiciousStringviewDataUsageCheck : public ClangTidyCheck {
public:
SuspiciousStringviewDataUsageCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
std::optional<TraversalKind> getCheckTraversalKind() const override;

private:
std::vector<llvm::StringRef> StringViewTypes;
std::vector<llvm::StringRef> AllowedCallees;
};

} // namespace clang::tidy::bugprone

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSSTRINGVIEWDATAUSAGECHECK_H
41 changes: 22 additions & 19 deletions clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,16 @@ AST_MATCHER_P(FunctionDecl, isInstantiatedFrom, Matcher<FunctionDecl>,
Finder, Builder);
}

AST_MATCHER_P(CXXMethodDecl, isOperatorOverloading,
llvm::SmallVector<OverloadedOperatorKind>, Kinds) {
return llvm::is_contained(Kinds, Node.getOverloadedOperator());
constexpr std::initializer_list<OverloadedOperatorKind>
AssignmentOverloadedOperatorKinds = {
OO_Equal, OO_PlusEqual, OO_MinusEqual, OO_StarEqual,
OO_SlashEqual, OO_PercentEqual, OO_CaretEqual, OO_AmpEqual,
OO_PipeEqual, OO_LessLessEqual, OO_GreaterGreaterEqual, OO_PlusPlus,
OO_MinusMinus};

AST_MATCHER(FunctionDecl, isAssignmentOverloadedOperator) {
return llvm::is_contained(AssignmentOverloadedOperatorKinds,
Node.getOverloadedOperator());
}
} // namespace

Expand Down Expand Up @@ -164,22 +171,18 @@ void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}

void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
auto MatchedDirectCallExpr = expr(
callExpr(
callee(functionDecl(
// Don't match void overloads of checked functions.
unless(returns(voidType())),
// Don't match copy or move assignment operator.
unless(cxxMethodDecl(isOperatorOverloading(
{OO_Equal, OO_PlusEqual, OO_MinusEqual, OO_StarEqual,
OO_SlashEqual, OO_PercentEqual, OO_CaretEqual, OO_AmpEqual,
OO_PipeEqual, OO_LessLessEqual, OO_GreaterGreaterEqual}))),
anyOf(
isInstantiatedFrom(
matchers::matchesAnyListedName(CheckedFunctions)),
returns(hasCanonicalType(hasDeclaration(namedDecl(
matchers::matchesAnyListedName(CheckedReturnTypes)))))))))
.bind("match"));
auto MatchedDirectCallExpr =
expr(callExpr(callee(functionDecl(
// Don't match copy or move assignment operator.
unless(isAssignmentOverloadedOperator()),
// Don't match void overloads of checked functions.
unless(returns(voidType())),
anyOf(isInstantiatedFrom(matchers::matchesAnyListedName(
CheckedFunctions)),
returns(hasCanonicalType(hasDeclaration(
namedDecl(matchers::matchesAnyListedName(
CheckedReturnTypes)))))))))
.bind("match"));

auto CheckCastToVoid =
AllowCastToVoid ? castExpr(unless(hasCastKind(CK_ToVoid))) : castExpr();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ using namespace clang::ast_matchers::internal;

namespace clang::tidy::cppcoreguidelines {

namespace {
AST_MATCHER_P(LambdaExpr, hasCallOperator, Matcher<CXXMethodDecl>,
InnerMatcher) {
return InnerMatcher.matches(*Node.getCallOperator(), Finder, Builder);
}

AST_MATCHER_P(LambdaExpr, hasLambdaBody, Matcher<Stmt>, InnerMatcher) {
return InnerMatcher.matches(*Node.getBody(), Finder, Builder);
}
} // namespace

void OwningMemoryCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "LegacyResourceProducers", LegacyResourceProducers);
Options.store(Opts, "LegacyResourceConsumers", LegacyResourceConsumers);
Expand Down Expand Up @@ -55,6 +66,8 @@ void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
CreatesLegacyOwner, LegacyOwnerCast);

const auto ConsideredOwner = eachOf(IsOwnerType, CreatesOwner);
const auto ScopeDeclaration = anyOf(translationUnitDecl(), namespaceDecl(),
recordDecl(), functionDecl());

// Find delete expressions that delete non-owners.
Finder->addMatcher(
Expand Down Expand Up @@ -144,13 +157,51 @@ void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
.bind("bad_owner_creation_parameter"))),
this);

auto IsNotInSubLambda = stmt(
hasAncestor(
stmt(anyOf(equalsBoundNode("body"), lambdaExpr())).bind("scope")),
hasAncestor(stmt(equalsBoundNode("scope"), equalsBoundNode("body"))));

// Matching on functions, that return an owner/resource, but don't declare
// their return type as owner.
Finder->addMatcher(
functionDecl(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner))
.bind("bad_owner_return")),
unless(returns(qualType(hasDeclaration(OwnerDecl)))))
.bind("function_decl"),
functionDecl(
decl().bind("function_decl"),
hasBody(
stmt(stmt().bind("body"),
hasDescendant(
returnStmt(hasReturnValue(ConsideredOwner),
// Ignore sub-lambda expressions
IsNotInSubLambda,
// Ignore sub-functions
hasAncestor(functionDecl().bind("context")),
hasAncestor(functionDecl(
equalsBoundNode("context"),
equalsBoundNode("function_decl"))))
.bind("bad_owner_return")))),
returns(qualType(unless(hasDeclaration(OwnerDecl))).bind("result"))),
this);

// Matching on lambdas, that return an owner/resource, but don't declare
// their return type as owner.
Finder->addMatcher(
lambdaExpr(
hasAncestor(decl(ScopeDeclaration).bind("scope-decl")),
hasLambdaBody(
stmt(stmt().bind("body"),
hasDescendant(
returnStmt(
hasReturnValue(ConsideredOwner),
// Ignore sub-lambdas
IsNotInSubLambda,
// Ignore sub-functions
hasAncestor(decl(ScopeDeclaration).bind("context")),
hasAncestor(decl(equalsBoundNode("context"),
equalsBoundNode("scope-decl"))))
.bind("bad_owner_return")))),
hasCallOperator(returns(
qualType(unless(hasDeclaration(OwnerDecl))).bind("result"))))
.bind("lambda"),
this);

// Match on classes that have an owner as member, but don't declare a
Expand Down Expand Up @@ -329,7 +380,7 @@ bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) {
// Function return statements, that are owners/resources, but the function
// declaration does not declare its return value as owner.
const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return");
const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl");
const auto *ResultType = Nodes.getNodeAs<QualType>("result");

// Function return values, that should be owners but aren't.
if (BadReturnType) {
Expand All @@ -338,8 +389,9 @@ bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) {
diag(BadReturnType->getBeginLoc(),
"returning a newly created resource of "
"type %0 or 'gsl::owner<>' from a "
"function whose return type is not 'gsl::owner<>'")
<< Function->getReturnType() << BadReturnType->getSourceRange();
"%select{function|lambda}1 whose return type is not 'gsl::owner<>'")
<< *ResultType << (Nodes.getNodeAs<Expr>("lambda") != nullptr)
<< BadReturnType->getSourceRange();

// FIXME: Rewrite the return type as 'gsl::owner<OriginalType>'
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag,

const Expr *SubExpr = Cast->getSubExpr();

bool NeedInnerParens =
SubExpr != nullptr && utils::fixit::areParensNeededForStatement(*SubExpr);
bool NeedInnerParens = utils::fixit::areParensNeededForStatement(*SubExpr);
bool NeedOuterParens =
Parent != nullptr && utils::fixit::areParensNeededForStatement(*Parent);

Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,8 @@ struct CodeCompletionBuilder {
}
// 'CompletionItemKind::Interface' matches template type aliases.
if (Completion.Kind == CompletionItemKind::Interface ||
Completion.Kind == CompletionItemKind::Class) {
Completion.Kind == CompletionItemKind::Class ||
Completion.Kind == CompletionItemKind::Variable) {
if (Snippet->front() != '<')
return *Snippet; // Not an arg snippet?

Expand Down
5 changes: 4 additions & 1 deletion clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2648,12 +2648,15 @@ TEST(CompletionTest, CompletionFunctionArgsDisabled) {
class foo_class{};
template <class T>
using foo_alias = T**;
template <class T>
T foo_var = T{};
void f() { foo_^ })cpp",
{}, Opts);
EXPECT_THAT(
Results.Completions,
UnorderedElementsAre(AllOf(named("foo_class"), snippetSuffix("<$0>")),
AllOf(named("foo_alias"), snippetSuffix("<$0>"))));
AllOf(named("foo_alias"), snippetSuffix("<$0>")),
AllOf(named("foo_var"), snippetSuffix("<$0>"))));
}
{
auto Results = completions(
Expand Down
11 changes: 11 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ New checks
Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP
can be constructed outside itself and the derived class.

- New :doc:`bugprone-suspicious-stringview-data-usage
<clang-tidy/checks/bugprone/suspicious-stringview-data-usage>` check.

Identifies suspicious usages of ``std::string_view::data()`` that could lead
to reading out-of-bounds data due to inadequate or incorrect string null
termination.

- New :doc:`modernize-use-designated-initializers
<clang-tidy/checks/modernize/use-designated-initializers>` check.

Expand Down Expand Up @@ -165,6 +172,10 @@ Changes in existing checks
giving false positives for deleted functions and fix false negative when some
parameters are forwarded, but other aren't.

- Improved :doc:`cppcoreguidelines-owning-memory
<clang-tidy/checks/cppcoreguidelines/owning-memory>` check to properly handle
return type in lambdas and in nested functions.

- Cleaned up :doc:`cppcoreguidelines-prefer-member-initializer
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>`
by removing enforcement of rule `C.48
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.. title:: clang-tidy - bugprone-suspicious-stringview-data-usage

bugprone-suspicious-stringview-data-usage
=========================================

Identifies suspicious usages of ``std::string_view::data()`` that could lead to
reading out-of-bounds data due to inadequate or incorrect string null
termination.

It warns when the result of ``data()`` is passed to a constructor or function
without also passing the corresponding result of ``size()`` or ``length()``
member function. Such usage can lead to unintended behavior, particularly when
assuming the data pointed to by ``data()`` is null-terminated.

The absence of a ``c_str()`` method in ``std::string_view`` often leads
developers to use ``data()`` as a substitute, especially when interfacing with
C APIs that expect null-terminated strings. However, since ``data()`` does not
guarantee null termination, this can result in unintended behavior if the API
relies on proper null termination for correct string interpretation.

In today's programming landscape, this scenario can occur when implicitly
converting an ``std::string_view`` to an ``std::string``. Since the constructor
in ``std::string`` designed for string-view-like objects is ``explicit``,
attempting to pass an ``std::string_view`` to a function expecting an
``std::string`` will result in a compilation error. As a workaround, developers
may be tempted to utilize the ``.data()`` method to achieve compilation,
introducing potential risks.

For instance:

.. code-block:: c++

void printString(const std::string& str) {
std::cout << "String: " << str << std::endl;
}

void something(std::string_view sv) {
printString(sv.data());
}

In this example, directly passing ``sv`` to the ``printString`` function would
lead to a compilation error due to the explicit nature of the ``std::string``
constructor. Consequently, developers might opt for ``sv.data()`` to resolve the
compilation error, albeit introducing potential hazards as discussed.

.. option:: StringViewTypes

Option allows users to specify custom string view-like types for analysis. It
accepts a semicolon-separated list of type names or regular expressions
matching these types. Default value is:
`::std::basic_string_view;::llvm::StringRef`.

.. option:: AllowedCallees

Specifies methods, functions, or classes where the result of ``.data()`` is
passed to. Allows to exclude such calls from the analysis. Accepts a
semicolon-separated list of names or regular expressions matching these
entities. Default value is: empty string.
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Clang-Tidy Checks
:doc:`bugprone-suspicious-realloc-usage <bugprone/suspicious-realloc-usage>`,
:doc:`bugprone-suspicious-semicolon <bugprone/suspicious-semicolon>`, "Yes"
:doc:`bugprone-suspicious-string-compare <bugprone/suspicious-string-compare>`, "Yes"
:doc:`bugprone-suspicious-stringview-data-usage <bugprone/suspicious-stringview-data-usage>`,
:doc:`bugprone-swapped-arguments <bugprone/swapped-arguments>`, "Yes"
:doc:`bugprone-switch-missing-default-case <bugprone/switch-missing-default-case>`,
:doc:`bugprone-terminating-continue <bugprone/terminating-continue>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct basic_string {
basic_string();
basic_string(const C *p, const A &a = A());
basic_string(const C *p, size_type count);
basic_string(const C *b, const C *e);

~basic_string();

Expand Down Expand Up @@ -85,6 +86,12 @@ struct basic_string_view {
const C *str;
constexpr basic_string_view(const C* s) : str(s) {}

const C *data() const;

bool empty() const;
size_type size() const;
size_type length() const;

size_type find(_Type v, size_type pos = 0) const;
size_type find(C ch, size_type pos = 0) const;
size_type find(const C* s, size_type pos, size_type count) const;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-suspicious-stringview-data-usage %t -- -- -isystem %clang_tidy_headers
#include <string>

struct View {
const char* str;
};

struct Pair {
const char* begin;
const char* end;
};

struct ViewWithSize {
const char* str;
std::string_view::size_type size;
};

void something(const char*);
void something(const char*, unsigned);
void something(const char*, unsigned, const char*);
void something_str(std::string, unsigned);

void invalid(std::string_view sv, std::string_view sv2) {
std::string s(sv.data());
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: result of a `data()` call may not be null terminated, provide size information to the callee to prevent potential issues
std::string si{sv.data()};
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: result of a `data()` call may not be null terminated, provide size information to the callee to prevent potential issues
std::string_view s2(sv.data());
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: result of a `data()` call may not be null terminated, provide size information to the callee to prevent potential issues
something(sv.data());
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: result of a `data()` call may not be null terminated, provide size information to the callee to prevent potential issues
something(sv.data(), sv.size(), sv2.data());
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: result of a `data()` call may not be null terminated, provide size information to the callee to prevent potential issues
something_str(sv.data(), sv.size());
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: result of a `data()` call may not be null terminated, provide size information to the callee to prevent potential issues
View view{sv.data()};
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: result of a `data()` call may not be null terminated, provide size information to the callee to prevent potential issues
}

void valid(std::string_view sv) {
std::string s1(sv.data(), sv.data() + sv.size());
std::string s2(sv.data(), sv.data() + sv.length());
std::string s3(sv.data(), sv.size() + sv.data());
std::string s4(sv.data(), sv.length() + sv.data());
std::string s5(sv.data(), sv.size());
std::string s6(sv.data(), sv.length());
something(sv.data(), sv.size());
something(sv.data(), sv.length());
ViewWithSize view1{sv.data(), sv.size()};
ViewWithSize view2{sv.data(), sv.length()};
Pair view3{sv.data(), sv.data() + sv.size()};
Pair view4{sv.data(), sv.data() + sv.length()};
Pair view5{sv.data(), sv.size() + sv.data()};
Pair view6{sv.data(), sv.length() + sv.data()};
const char* str{sv.data()};
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,53 @@
// RUN: {bugprone-unused-return-value.CheckedFunctions: "::*"}}' \
// RUN: --

struct S {
S(){};
S(S const &);
S(S &&);
S &operator=(S const &);
S &operator=(S &&);
S &operator+=(S);
struct S1 {
S1(){};
S1(S1 const &);
S1(S1 &&);
S1 &operator=(S1 const &);
S1 &operator=(S1 &&);
S1 &operator+=(S1);
S1 &operator++();
S1 &operator++(int);
S1 &operator--();
S1 &operator--(int);
};

S returnValue();
S const &returnRef();
struct S2 {
S2(){};
S2(S2 const &);
S2(S2 &&);
};

S2 &operator-=(S2&, int);
S2 &operator++(S2 &);
S2 &operator++(S2 &, int);

S1 returnValue();
S1 const &returnRef();

void bar() {
returnValue();
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors

S a{};
S1 a{};
a = returnValue();
a.operator=(returnValue());

a = returnRef();
a.operator=(returnRef());

a += returnRef();

a++;
++a;
a--;
--a;

S2 b{};

b -= 1;
b++;
++b;
}
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,109 @@ namespace PR63994 {
// CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'A *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
}
}

namespace PR59389 {
struct S {
S();
S(int);

int value = 1;
};

void testLambdaInFunctionNegative() {
const auto MakeS = []() -> ::gsl::owner<S*> {
return ::gsl::owner<S*>{new S{}};
};
}

void testLambdaInFunctionPositive() {
const auto MakeS = []() -> S* {
return ::gsl::owner<S*>{new S{}};
// CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
};
}

void testFunctionInFunctionNegative() {
struct C {
::gsl::owner<S*> test() {
return ::gsl::owner<S*>{new S{}};
}
};
}

void testFunctionInFunctionPositive() {
struct C {
S* test() {
return ::gsl::owner<S*>{new S{}};
// CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
}
};
}

::gsl::owner<S*> testReverseLambdaNegative() {
const auto MakeI = [] -> int { return 5; };
return ::gsl::owner<S*>{new S(MakeI())};
}

S* testReverseLambdaPositive() {
const auto MakeI = [] -> int { return 5; };
return ::gsl::owner<S*>{new S(MakeI())};
// CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
}

::gsl::owner<S*> testReverseFunctionNegative() {
struct C {
int test() { return 5; }
};
return ::gsl::owner<S*>{new S(C().test())};
}

S* testReverseFunctionPositive() {
struct C {
int test() { return 5; }
};
return ::gsl::owner<S*>{new S(C().test())};
// CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
}

void testLambdaInLambdaNegative() {
const auto MakeS = []() -> ::gsl::owner<S*> {
const auto MakeI = []() -> int { return 5; };
return ::gsl::owner<S*>{new S(MakeI())};
};
}

void testLambdaInLambdaPositive() {
const auto MakeS = []() -> S* {
const auto MakeI = []() -> int { return 5; };
return ::gsl::owner<S*>{new S(MakeI())};
// CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
};
}

void testLambdaInLambdaWithDoubleReturns() {
const auto MakeS = []() -> S* {
const auto MakeS2 = []() -> S* {
return ::gsl::owner<S*>{new S(1)};
// CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>' [cppcoreguidelines-owning-memory]
};
return ::gsl::owner<S*>{new S(2)};
// CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
};
}

void testReverseLambdaInLambdaNegative() {
const auto MakeI = []() -> int {
const auto MakeS = []() -> ::gsl::owner<S*> { return new S(); };
return 5;
};
}

void testReverseLambdaInLambdaPositive() {
const auto MakeI = []() -> int {
const auto MakeS = []() -> S* { return new S(); };
// CHECK-NOTES: [[@LINE-1]]:39: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
return 5;
};
}
}
5 changes: 3 additions & 2 deletions clang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,12 @@ set(CLANG_RESOURCE_DIR "" CACHE STRING
set(C_INCLUDE_DIRS "" CACHE STRING
"Colon separated list of directories clang will search for headers.")

set(USE_DEPRECATED_GCC_INSTALL_PREFIX OFF CACHE BOOL "Temporary workaround before GCC_INSTALL_PREFIX is completely removed")
set(GCC_INSTALL_PREFIX "" CACHE PATH "Directory where gcc is installed." )
set(DEFAULT_SYSROOT "" CACHE STRING
"Default <path> to all compiler invocations for --sysroot=<path>." )
if(GCC_INSTALL_PREFIX)
message(WARNING "GCC_INSTALL_PREFIX is deprecated and will be removed. Use "
if(GCC_INSTALL_PREFIX AND NOT USE_DEPRECATED_GCC_INSTALL_PREFIX)
message(FATAL_ERROR "GCC_INSTALL_PREFIX is deprecated and will be removed. Use "
"configuration files (https://clang.llvm.org/docs/UsersManual.html#configuration-files)"
"to specify the default --gcc-install-dir= or --gcc-triple=. --gcc-toolchain= is discouraged. "
"See https://github.com/llvm/llvm-project/pull/77537 for detail.")
Expand Down
173 changes: 172 additions & 1 deletion clang/bindings/python/clang/cindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,29 @@ def __repr__(self):
# Represents an @available(...) check.
CursorKind.OBJC_AVAILABILITY_CHECK_EXPR = CursorKind(148)

# Fixed point literal.
CursorKind.FIXED_POINT_LITERAL = CursorKind(149)

# OpenMP 5.0 [2.1.4, Array Shaping].
CursorKind.OMP_ARRAY_SHAPING_EXPR = CursorKind(150)

# OpenMP 5.0 [2.1.6 Iterators].
CursorKind.OMP_ITERATOR_EXPR = CursorKind(151)

# OpenCL's addrspace_cast<> expression.
CursorKind.CXX_ADDRSPACE_CAST_EXPR = CursorKind(152)

# Expression that references a C++20 concept.
CursorKind.CONCEPT_SPECIALIZATION_EXPR = CursorKind(153)

# Expression that references a C++20 requires expression.
CursorKind.REQUIRES_EXPR = CursorKind(154)

# Expression that references a C++20 parenthesized list aggregate initializer.
CursorKind.CXX_PAREN_LIST_INIT_EXPR = CursorKind(155)

# Represents a C++26 pack indexing expression.
CursorKind.PACK_INDEXING_EXPR = CursorKind(156)

# A statement whose specific kind is not exposed via this interface.
#
Expand Down Expand Up @@ -1312,6 +1335,114 @@ def __repr__(self):
# OpenMP teams distribute directive.
CursorKind.OMP_TEAMS_DISTRIBUTE_DIRECTIVE = CursorKind(271)

# OpenMP teams distribute simd directive.
CursorKind.OMP_TEAMS_DISTRIBUTE_DIRECTIVE = CursorKind(272)

# OpenMP teams distribute parallel for simd directive.
CursorKind.OMP_TEAMS_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE = CursorKind(273)

# OpenMP teams distribute parallel for directive.
CursorKind.OMP_TEAMS_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE = CursorKind(274)

# OpenMP target teams directive.
CursorKind.OMP_TARGET_TEAMS_DIRECTIVE = CursorKind(275)

# OpenMP target teams distribute directive.
CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_DIRECTIVE = CursorKind(276)

# OpenMP target teams distribute parallel for directive.
CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE = CursorKind(277)

# OpenMP target teams distribute parallel for simd directive.
CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE = CursorKind(278)

# OpenMP target teams distribute simd directive.
CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_SIMD_DIRECTIVE = CursorKind(279)

# C++2a std::bit_cast expression.
CursorKind.BUILTIN_BIT_CAST_EXPR = CursorKind(280)

# OpenMP master taskloop directive.
CursorKind.OMP_MASTER_TASK_LOOP_DIRECTIVE = CursorKind(281)

# OpenMP parallel master taskloop directive.
CursorKind.OMP_PARALLEL_MASTER_TASK_LOOP_DIRECTIVE = CursorKind(282)

# OpenMP master taskloop simd directive.
CursorKind.OMP_MASTER_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(283)

# OpenMP parallel master taskloop simd directive.
CursorKind.OMP_PARALLEL_MASTER_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(284)

# OpenMP parallel master directive.
CursorKind.OMP_PARALLEL_MASTER_DIRECTIVE = CursorKind(285)

# OpenMP depobj directive.
CursorKind.OMP_DEPOBJ_DIRECTIVE = CursorKind(286)

# OpenMP scan directive.
CursorKind.OMP_SCAN_DIRECTIVE = CursorKind(287)

# OpenMP tile directive.
CursorKind.OMP_TILE_DIRECTIVE = CursorKind(288)

# OpenMP canonical loop.
CursorKind.OMP_CANONICAL_LOOP = CursorKind(289)

# OpenMP interop directive.
CursorKind.OMP_INTEROP_DIRECTIVE = CursorKind(290)

# OpenMP dispatch directive.
CursorKind.OMP_DISPATCH_DIRECTIVE = CursorKind(291)

# OpenMP masked directive.
CursorKind.OMP_MASKED_DIRECTIVE = CursorKind(292)

# OpenMP unroll directive.
CursorKind.OMP_UNROLL_DIRECTIVE = CursorKind(293)

# OpenMP metadirective directive.
CursorKind.OMP_META_DIRECTIVE = CursorKind(294)

# OpenMP loop directive.
CursorKind.OMP_GENERIC_LOOP_DIRECTIVE = CursorKind(295)

# OpenMP teams loop directive.
CursorKind.OMP_TEAMS_GENERIC_LOOP_DIRECTIVE = CursorKind(296)

# OpenMP target teams loop directive.
CursorKind.OMP_TARGET_TEAMS_GENERIC_LOOP_DIRECTIVE = CursorKind(297)

# OpenMP parallel loop directive.
CursorKind.OMP_PARALLEL_GENERIC_LOOP_DIRECTIVE = CursorKind(298)

# OpenMP target parallel loop directive.
CursorKind.OMP_TARGET_PARALLEL_GENERIC_LOOP_DIRECTIVE = CursorKind(299)

# OpenMP parallel masked directive.
CursorKind.OMP_PARALLEL_MASKED_DIRECTIVE = CursorKind(300)

# OpenMP masked taskloop directive.
CursorKind.OMP_MASKED_TASK_LOOP_DIRECTIVE = CursorKind(301)

# OpenMP masked taskloop simd directive.
CursorKind.OMP_MASKED_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(302)

# OpenMP parallel masked taskloop directive.
CursorKind.OMP_PARALLEL_MASKED_TASK_LOOP_DIRECTIVE = CursorKind(303)

# OpenMP parallel masked taskloop simd directive.
CursorKind.OMP_PARALLEL_MASKED_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(304)

# OpenMP error directive.
CursorKind.OMP_ERROR_DIRECTIVE = CursorKind(305)

# OpenMP scope directive.
CursorKind.OMP_SCOPE_DIRECTIVE = CursorKind(306)

# OpenACC Compute Construct.
CursorKind.OPEN_ACC_COMPUTE_DIRECTIVE = CursorKind(320)

###
# Other Kinds

Expand Down Expand Up @@ -1349,6 +1480,24 @@ def __repr__(self):

CursorKind.DLLEXPORT_ATTR = CursorKind(418)
CursorKind.DLLIMPORT_ATTR = CursorKind(419)
CursorKind.NS_RETURNS_RETAINED = CursorKind(420)
CursorKind.NS_RETURNS_NOT_RETAINED = CursorKind(421)
CursorKind.NS_RETURNS_AUTORELEASED = CursorKind(422)
CursorKind.NS_CONSUMES_SELF = CursorKind(423)
CursorKind.NS_CONSUMED = CursorKind(424)
CursorKind.OBJC_EXCEPTION = CursorKind(425)
CursorKind.OBJC_NSOBJECT = CursorKind(426)
CursorKind.OBJC_INDEPENDENT_CLASS = CursorKind(427)
CursorKind.OBJC_PRECISE_LIFETIME = CursorKind(428)
CursorKind.OBJC_RETURNS_INNER_POINTER = CursorKind(429)
CursorKind.OBJC_REQUIRES_SUPER = CursorKind(430)
CursorKind.OBJC_ROOT_CLASS = CursorKind(431)
CursorKind.OBJC_SUBCLASSING_RESTRICTED = CursorKind(432)
CursorKind.OBJC_EXPLICIT_PROTOCOL_IMPL = CursorKind(433)
CursorKind.OBJC_DESIGNATED_INITIALIZER = CursorKind(434)
CursorKind.OBJC_RUNTIME_VISIBLE = CursorKind(435)
CursorKind.OBJC_BOXABLE = CursorKind(436)
CursorKind.FLAG_ENUM = CursorKind(437)
CursorKind.CONVERGENT_ATTR = CursorKind(438)
CursorKind.WARN_UNUSED_ATTR = CursorKind(439)
CursorKind.WARN_UNUSED_RESULT_ATTR = CursorKind(440)
Expand Down Expand Up @@ -1395,6 +1544,11 @@ class TemplateArgumentKind(BaseEnumeration):
TemplateArgumentKind.DECLARATION = TemplateArgumentKind(2)
TemplateArgumentKind.NULLPTR = TemplateArgumentKind(3)
TemplateArgumentKind.INTEGRAL = TemplateArgumentKind(4)
TemplateArgumentKind.TEMPLATE = TemplateArgumentKind(5)
TemplateArgumentKind.TEMPLATE_EXPANSION = TemplateArgumentKind(6)
TemplateArgumentKind.EXPRESSION = TemplateArgumentKind(7)
TemplateArgumentKind.PACK = TemplateArgumentKind(8)
TemplateArgumentKind.INVALID = TemplateArgumentKind(9)

### Exception Specification Kinds ###
class ExceptionSpecificationKind(BaseEnumeration):
Expand Down Expand Up @@ -2240,9 +2394,26 @@ def __repr__(self):
TypeKind.OCLQUEUE = TypeKind(159)
TypeKind.OCLRESERVEID = TypeKind(160)

TypeKind.OBJCOBJECT = TypeKind(161)
TypeKind.OBJCCLASS = TypeKind(162)
TypeKind.ATTRIBUTED = TypeKind(163)

TypeKind.OCLINTELSUBGROUPAVCMCEPAYLOAD = TypeKind(164)
TypeKind.OCLINTELSUBGROUPAVCIMEPAYLOAD = TypeKind(165)
TypeKind.OCLINTELSUBGROUPAVCREFPAYLOAD = TypeKind(166)
TypeKind.OCLINTELSUBGROUPAVCSICPAYLOAD = TypeKind(167)
TypeKind.OCLINTELSUBGROUPAVCMCERESULT = TypeKind(168)
TypeKind.OCLINTELSUBGROUPAVCIMERESULT = TypeKind(169)
TypeKind.OCLINTELSUBGROUPAVCREFRESULT = TypeKind(170)
TypeKind.OCLINTELSUBGROUPAVCSICRESULT = TypeKind(171)
TypeKind.OCLINTELSUBGROUPAVCIMERESULTSINGLEREFERENCESTREAMOUT = TypeKind(172)
TypeKind.OCLINTELSUBGROUPAVCIMERESULTSDUALREFERENCESTREAMOUT = TypeKind(173)
TypeKind.OCLINTELSUBGROUPAVCIMERESULTSSINGLEREFERENCESTREAMIN = TypeKind(174)
TypeKind.OCLINTELSUBGROUPAVCIMEDUALREFERENCESTREAMIN = TypeKind(175)

TypeKind.EXTVECTOR = TypeKind(176)
TypeKind.ATOMIC = TypeKind(177)

TypeKind.BTFTAGATTRIBUTED = TypeKind(178)

class RefQualifierKind(BaseEnumeration):
"""Describes a specific ref-qualifier of a type."""
Expand Down
1 change: 1 addition & 0 deletions clang/docs/ClangFormat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.
--dry-run - If set, do not actually make the formatting changes
--dump-config - Dump configuration options to stdout and exit.
Can be used with -style option.
--fail-on-incomplete-format - If set, fail with exit code 1 on incomplete format.
--fallback-style=<string> - The name of the predefined style used as a
fallback in case clang-format is invoked with
-style=file, but can not find the .clang-format
Expand Down
Loading