56 changes: 56 additions & 0 deletions bolt/include/bolt/Profile/YAMLProfileReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,59 @@ class YAMLProfileReader : public ProfileReaderBase {
using ProfileLookupMap =
DenseMap<uint32_t, yaml::bolt::BinaryFunctionProfile *>;

/// A class for matching binary functions in functions in the YAML profile.
/// First, a call graph is constructed for both profiled and binary functions.
/// Then functions are hashed based on the names of their callee/caller
/// functions. Finally, functions are matched based on these neighbor hashes.
class CallGraphMatcher {
public:
/// Constructs the call graphs for binary and profiled functions and
/// computes neighbor hashes for binary functions.
CallGraphMatcher(BinaryContext &BC, yaml::bolt::BinaryProfile &YamlBP,
ProfileLookupMap &IdToYAMLBF);

/// Returns the YamlBFs adjacent to the parameter YamlBF in the call graph.
std::optional<std::set<yaml::bolt::BinaryFunctionProfile *>>
getAdjacentYamlBFs(yaml::bolt::BinaryFunctionProfile &YamlBF) {
auto It = YamlBFAdjacencyMap.find(&YamlBF);
return It == YamlBFAdjacencyMap.end() ? std::nullopt
: std::make_optional(It->second);
}

/// Returns the binary functions with the parameter neighbor hash.
std::optional<std::vector<BinaryFunction *>>
getBFsWithNeighborHash(uint64_t NeighborHash) {
auto It = NeighborHashToBFs.find(NeighborHash);
return It == NeighborHashToBFs.end() ? std::nullopt
: std::make_optional(It->second);
}

private:
/// Adds edges to the binary function call graph given the callsites of the
/// parameter function.
void constructBFCG(BinaryContext &BC, yaml::bolt::BinaryProfile &YamlBP);

/// Using the constructed binary function call graph, computes and creates
/// mappings from "neighbor hash" (composed of the function names of callee
/// and caller functions of a function) to binary functions.
void computeBFNeighborHashes(BinaryContext &BC);

/// Constructs the call graph for profile functions.
void constructYAMLFCG(yaml::bolt::BinaryProfile &YamlBP,
ProfileLookupMap &IdToYAMLBF);

/// Adjacency map for binary functions in the call graph.
DenseMap<BinaryFunction *, std::set<BinaryFunction *>> BFAdjacencyMap;

/// Maps neighbor hashes to binary functions.
DenseMap<uint64_t, std::vector<BinaryFunction *>> NeighborHashToBFs;

/// Adjacency map for profile functions in the call graph.
DenseMap<yaml::bolt::BinaryFunctionProfile *,
std::set<yaml::bolt::BinaryFunctionProfile *>>
YamlBFAdjacencyMap;
};

private:
/// Adjustments for basic samples profiles (without LBR).
bool NormalizeByInsnCount{false};
Expand Down Expand Up @@ -100,6 +153,9 @@ class YAMLProfileReader : public ProfileReaderBase {
/// Matches functions using exact hash.
size_t matchWithHash(BinaryContext &BC);

/// Matches functions using the call graph.
size_t matchWithCallGraph(BinaryContext &BC);

/// Matches functions with similarly named profiled functions.
size_t matchWithNameSimilarity(BinaryContext &BC);

Expand Down
11 changes: 10 additions & 1 deletion bolt/include/bolt/RuntimeLibs/RuntimeLibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,16 @@ class RuntimeLibrary {
uint64_t RuntimeFiniAddress{0};
uint64_t RuntimeStartAddress{0};

/// Get the full path to a runtime library specified by \p LibFileName.
/// Get the full path to a runtime library specified by \p LibFileName and \p
/// ToolPath.
static std::string getLibPathByToolPath(StringRef ToolPath,
StringRef LibFileName);

/// Get the full path to a runtime library by the install directory.
static std::string getLibPathByInstalled(StringRef LibFileName);

/// Gets the full path to a runtime library based on whether it exists
/// in the install libdir or runtime libdir.
static std::string getLibPath(StringRef ToolPath, StringRef LibFileName);

/// Load a static runtime library specified by \p LibPath.
Expand Down
2 changes: 2 additions & 0 deletions bolt/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
add_compile_definitions(CMAKE_INSTALL_FULL_LIBDIR="${CMAKE_INSTALL_FULL_LIBDIR}")

add_subdirectory(Core)
add_subdirectory(Passes)
add_subdirectory(Profile)
Expand Down
26 changes: 13 additions & 13 deletions bolt/lib/Core/BinaryContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ PrintMemData("print-mem-data",

cl::opt<std::string> CompDirOverride(
"comp-dir-override",
cl::desc("overrides DW_AT_comp_dir, and provides an alterantive base "
cl::desc("overrides DW_AT_comp_dir, and provides an alternative base "
"location, which is used with DW_AT_dwo_name to construct a path "
"to *.dwo files."),
cl::Hidden, cl::init(""), cl::cat(BoltCategory));
Expand Down Expand Up @@ -646,7 +646,7 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
const BinaryFunction *TargetBF = getBinaryFunctionContainingAddress(Value);
const bool DoesBelongToFunction =
BF.containsAddress(Value) ||
(TargetBF && TargetBF->isParentOrChildOf(BF));
(TargetBF && areRelatedFragments(TargetBF, &BF));
if (!DoesBelongToFunction) {
LLVM_DEBUG({
if (!BF.containsAddress(Value)) {
Expand Down Expand Up @@ -839,9 +839,11 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
assert(Address == JT->getAddress() && "unexpected non-empty jump table");

// Prevent associating a jump table to a specific fragment twice.
// This simple check arises from the assumption: no more than 2 fragments.
if (JT->Parents.size() == 1 && JT->Parents[0] != &Function) {
assert(JT->Parents[0]->isParentOrChildOf(Function) &&
if (!llvm::is_contained(JT->Parents, &Function)) {
assert(llvm::all_of(JT->Parents,
[&](const BinaryFunction *BF) {
return areRelatedFragments(&Function, BF);
}) &&
"cannot re-use jump table of a different function");
// Duplicate the entry for the parent function for easy access
JT->Parents.push_back(&Function);
Expand All @@ -852,8 +854,8 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
JT->print(this->outs());
}
Function.JumpTables.emplace(Address, JT);
JT->Parents[0]->setHasIndirectTargetToSplitFragment(true);
JT->Parents[1]->setHasIndirectTargetToSplitFragment(true);
for (BinaryFunction *Parent : JT->Parents)
Parent->setHasIndirectTargetToSplitFragment(true);
}

bool IsJumpTableParent = false;
Expand Down Expand Up @@ -1209,12 +1211,13 @@ void BinaryContext::generateSymbolHashes() {
}

bool BinaryContext::registerFragment(BinaryFunction &TargetFunction,
BinaryFunction &Function) const {
BinaryFunction &Function) {
assert(TargetFunction.isFragment() && "TargetFunction must be a fragment");
if (TargetFunction.isChildOf(Function))
return true;
TargetFunction.addParentFragment(Function);
Function.addFragment(TargetFunction);
FragmentClasses.unionSets(&TargetFunction, &Function);
if (!HasRelocations) {
TargetFunction.setSimple(false);
Function.setSimple(false);
Expand Down Expand Up @@ -1336,7 +1339,7 @@ void BinaryContext::processInterproceduralReferences() {

if (TargetFunction) {
if (TargetFunction->isFragment() &&
!TargetFunction->isChildOf(Function)) {
!areRelatedFragments(TargetFunction, &Function)) {
this->errs()
<< "BOLT-WARNING: interprocedural reference between unrelated "
"fragments: "
Expand Down Expand Up @@ -2367,10 +2370,7 @@ BinaryContext::calculateEmittedSize(BinaryFunction &BF, bool FixBranches) {
std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(VecOS);
std::unique_ptr<MCStreamer> Streamer(TheTarget->createMCObjectStreamer(
*TheTriple, *LocalCtx, std::unique_ptr<MCAsmBackend>(MAB), std::move(OW),
std::unique_ptr<MCCodeEmitter>(MCEInstance.MCE.release()), *STI,
/*RelaxAll=*/false,
/*IncrementalLinkerCompatible=*/false,
/*DWARFMustBeAtTheEnd=*/false));
std::unique_ptr<MCCodeEmitter>(MCEInstance.MCE.release()), *STI));

Streamer->initSections(false, *STI);

Expand Down
2 changes: 1 addition & 1 deletion bolt/lib/Core/Exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ Error BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
"BOLT-ERROR: cannot find landing pad fragment");
BC.addInterproceduralReference(this, Fragment->getAddress());
BC.processInterproceduralReferences();
assert(isParentOrChildOf(*Fragment) &&
assert(BC.areRelatedFragments(this, Fragment) &&
"BOLT-ERROR: cannot have landing pads in different functions");
setHasIndirectTargetToSplitFragment(true);
BC.addFragmentsToSkip(this);
Expand Down
7 changes: 2 additions & 5 deletions bolt/lib/Passes/AsmDump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,8 @@ void dumpFunction(const BinaryFunction &BF) {
auto FOut = std::make_unique<formatted_raw_ostream>(OS);
FOut->SetUnbuffered();
std::unique_ptr<MCStreamer> AsmStreamer(
createAsmStreamer(*LocalCtx, std::move(FOut),
/*isVerboseAsm=*/true,
/*useDwarfDirectory=*/false, InstructionPrinter,
std::move(MCEInstance.MCE), std::move(MAB),
/*ShowInst=*/false));
createAsmStreamer(*LocalCtx, std::move(FOut), InstructionPrinter,
std::move(MCEInstance.MCE), std::move(MAB)));
AsmStreamer->initSections(true, *BC.STI);
std::unique_ptr<TargetMachine> TM(BC.TheTarget->createTargetMachine(
BC.TripleName, "", "", TargetOptions(), std::nullopt));
Expand Down
4 changes: 3 additions & 1 deletion bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ MaxSamples("max-samples",
cl::cat(AggregatorCategory));

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

cl::opt<bool> ReadPreAggregated(
Expand Down Expand Up @@ -2298,7 +2299,8 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,

yaml::bolt::BinaryProfile BP;

const MCPseudoProbeDecoder *PseudoProbeDecoder = BC.getPseudoProbeDecoder();
const MCPseudoProbeDecoder *PseudoProbeDecoder =
opts::ProfileUsePseudoProbes ? BC.getPseudoProbeDecoder() : nullptr;

// Fill out the header info.
BP.Header.Version = 1;
Expand Down
162 changes: 155 additions & 7 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,87 @@ llvm::cl::opt<bool>
MatchProfileWithFunctionHash("match-profile-with-function-hash",
cl::desc("Match profile with function hash"),
cl::Hidden, cl::cat(BoltOptCategory));
llvm::cl::opt<bool>
MatchWithCallGraph("match-with-call-graph",
cl::desc("Match functions with call graph"), cl::Hidden,
cl::cat(BoltOptCategory));

llvm::cl::opt<bool> ProfileUseDFS("profile-use-dfs",
cl::desc("use DFS order for YAML profile"),
cl::Hidden, cl::cat(BoltOptCategory));

llvm::cl::opt<bool> ProfileUsePseudoProbes(
"profile-use-pseudo-probes",
cl::desc("Use pseudo probes for profile generation and matching"),
cl::Hidden, cl::cat(BoltOptCategory));
} // namespace opts

namespace llvm {
namespace bolt {

YAMLProfileReader::CallGraphMatcher::CallGraphMatcher(
BinaryContext &BC, yaml::bolt::BinaryProfile &YamlBP,
ProfileLookupMap &IdToYAMLBF) {
constructBFCG(BC, YamlBP);
constructYAMLFCG(YamlBP, IdToYAMLBF);
computeBFNeighborHashes(BC);
}

void YAMLProfileReader::CallGraphMatcher::constructBFCG(
BinaryContext &BC, yaml::bolt::BinaryProfile &YamlBP) {
for (BinaryFunction *BF : BC.getAllBinaryFunctions()) {
for (const BinaryBasicBlock &BB : BF->blocks()) {
for (const MCInst &Instr : BB) {
if (!BC.MIB->isCall(Instr))
continue;
const MCSymbol *CallSymbol = BC.MIB->getTargetSymbol(Instr);
if (!CallSymbol)
continue;
BinaryData *BD = BC.getBinaryDataByName(CallSymbol->getName());
if (!BD)
continue;
BinaryFunction *CalleeBF = BC.getFunctionForSymbol(BD->getSymbol());
if (!CalleeBF)
continue;

BFAdjacencyMap[CalleeBF].insert(BF);
BFAdjacencyMap[BF].insert(CalleeBF);
}
}
}
}

void YAMLProfileReader::CallGraphMatcher::computeBFNeighborHashes(
BinaryContext &BC) {
for (BinaryFunction *BF : BC.getAllBinaryFunctions()) {
auto It = BFAdjacencyMap.find(BF);
if (It == BFAdjacencyMap.end())
continue;
auto &AdjacentBFs = It->second;
std::string HashStr;
for (BinaryFunction *BF : AdjacentBFs)
HashStr += BF->getOneName();
uint64_t Hash = std::hash<std::string>{}(HashStr);
NeighborHashToBFs[Hash].push_back(BF);
}
}

void YAMLProfileReader::CallGraphMatcher::constructYAMLFCG(
yaml::bolt::BinaryProfile &YamlBP, ProfileLookupMap &IdToYAMLBF) {

for (auto &CallerYamlBF : YamlBP.Functions) {
for (auto &YamlBB : CallerYamlBF.Blocks) {
for (auto &CallSite : YamlBB.CallSites) {
auto IdToYAMLBFIt = IdToYAMLBF.find(CallSite.DestId);
if (IdToYAMLBFIt == IdToYAMLBF.end())
continue;
YamlBFAdjacencyMap[&CallerYamlBF].insert(IdToYAMLBFIt->second);
YamlBFAdjacencyMap[IdToYAMLBFIt->second].insert(&CallerYamlBF);
}
}
}
}

bool YAMLProfileReader::isYAML(const StringRef Filename) {
if (auto MB = MemoryBuffer::getFileOrSTDIN(Filename)) {
StringRef Buffer = (*MB)->getBuffer();
Expand Down Expand Up @@ -350,7 +422,7 @@ bool YAMLProfileReader::profileMatches(
}

bool YAMLProfileReader::mayHaveProfileData(const BinaryFunction &BF) {
if (opts::MatchProfileWithFunctionHash)
if (opts::MatchProfileWithFunctionHash || opts::MatchWithCallGraph)
return true;
for (StringRef Name : BF.getNames())
if (ProfileFunctionNames.contains(Name))
Expand Down Expand Up @@ -446,6 +518,79 @@ size_t YAMLProfileReader::matchWithLTOCommonName() {
return MatchedWithLTOCommonName;
}

size_t YAMLProfileReader::matchWithCallGraph(BinaryContext &BC) {
if (!opts::MatchWithCallGraph)
return 0;

size_t MatchedWithCallGraph = 0;
CallGraphMatcher CGMatcher(BC, YamlBP, IdToYamLBF);

ItaniumPartialDemangler Demangler;
auto GetBaseName = [&](std::string &FunctionName) {
if (Demangler.partialDemangle(FunctionName.c_str()))
return std::string("");
size_t BufferSize = 1;
char *Buffer = static_cast<char *>(std::malloc(BufferSize));
char *BaseName = Demangler.getFunctionBaseName(Buffer, &BufferSize);
if (!BaseName) {
std::free(Buffer);
return std::string("");
}
if (Buffer != BaseName)
Buffer = BaseName;
std::string BaseNameStr(Buffer, BufferSize);
std::free(Buffer);
return BaseNameStr;
};

// Matches YAMLBF to BFs with neighbor hashes.
for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions) {
if (YamlBF.Used)
continue;
auto AdjacentYamlBFsOpt = CGMatcher.getAdjacentYamlBFs(YamlBF);
if (!AdjacentYamlBFsOpt)
continue;
std::set<yaml::bolt::BinaryFunctionProfile *> AdjacentYamlBFs =
AdjacentYamlBFsOpt.value();
std::string AdjacentYamlBFsHashStr;
for (auto *AdjacentYamlBF : AdjacentYamlBFs)
AdjacentYamlBFsHashStr += AdjacentYamlBF->Name;
uint64_t Hash = std::hash<std::string>{}(AdjacentYamlBFsHashStr);
auto BFsWithSameHashOpt = CGMatcher.getBFsWithNeighborHash(Hash);
if (!BFsWithSameHashOpt)
continue;
std::vector<BinaryFunction *> BFsWithSameHash = BFsWithSameHashOpt.value();
// Finds the binary function with the longest common prefix to the profiled
// function and matches.
BinaryFunction *ClosestBF = nullptr;
size_t LCP = 0;
std::string YamlBFBaseName = GetBaseName(YamlBF.Name);
for (BinaryFunction *BF : BFsWithSameHash) {
if (ProfiledFunctions.count(BF))
continue;
std::string BFName = std::string(BF->getOneName());
std::string BFBaseName = GetBaseName(BFName);
size_t PrefixLength = 0;
size_t N = std::min(YamlBFBaseName.size(), BFBaseName.size());
for (size_t I = 0; I < N; ++I) {
if (YamlBFBaseName[I] != BFBaseName[I])
break;
++PrefixLength;
}
if (PrefixLength >= LCP) {
LCP = PrefixLength;
ClosestBF = BF;
}
}
if (ClosestBF) {
matchProfileToFunction(YamlBF, *ClosestBF);
++MatchedWithCallGraph;
}
}

return MatchedWithCallGraph;
}

size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) {
if (opts::NameSimilarityFunctionMatchingThreshold == 0)
return 0;
Expand Down Expand Up @@ -581,9 +726,14 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
}
}

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

const size_t MatchedWithExactName = matchWithExactName();
const size_t MatchedWithHash = matchWithHash(BC);
const size_t MatchedWithLTOCommonName = matchWithLTOCommonName();
const size_t MatchedWithCallGraph = matchWithCallGraph(BC);
const size_t MatchedWithNameSimilarity = matchWithNameSimilarity(BC);

for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs))
Expand All @@ -603,18 +753,15 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
<< " functions with hash\n";
outs() << "BOLT-INFO: matched " << MatchedWithLTOCommonName
<< " functions with matching LTO common names\n";
outs() << "BOLT-INFO: matched " << MatchedWithCallGraph
<< " functions with call graph\n";
outs() << "BOLT-INFO: matched " << MatchedWithNameSimilarity
<< " functions with similar names\n";
}

// Set for parseFunctionProfile().
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 All @@ -630,7 +777,8 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {

BC.setNumUnusedProfiledObjects(NumUnused);

if (opts::Lite && opts::MatchProfileWithFunctionHash) {
if (opts::Lite &&
(opts::MatchProfileWithFunctionHash || opts::MatchWithCallGraph)) {
for (BinaryFunction *BF : BC.getAllBinaryFunctions())
if (!BF->hasProfile())
BF->setIgnored();
Expand Down
4 changes: 3 additions & 1 deletion bolt/lib/Profile/YAMLProfileWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

namespace opts {
extern llvm::cl::opt<bool> ProfileUseDFS;
extern llvm::cl::opt<bool> ProfileUsePseudoProbes;
} // namespace opts

namespace llvm {
Expand Down Expand Up @@ -57,7 +58,8 @@ YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS,
const BoltAddressTranslation *BAT) {
yaml::bolt::BinaryFunctionProfile YamlBF;
const BinaryContext &BC = BF.getBinaryContext();
const MCPseudoProbeDecoder *PseudoProbeDecoder = BC.getPseudoProbeDecoder();
const MCPseudoProbeDecoder *PseudoProbeDecoder =
opts::ProfileUsePseudoProbes ? BC.getPseudoProbeDecoder() : nullptr;

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

Expand Down
7 changes: 6 additions & 1 deletion bolt/lib/Rewrite/BinaryPassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,10 @@ static cl::opt<bool> CMOVConversionFlag("cmov-conversion",
cl::ReallyHidden,
cl::cat(BoltOptCategory));

static cl::opt<bool> ShortenInstructions("shorten-instructions",
cl::desc("shorten instructions"),
cl::init(true),
cl::cat(BoltOptCategory));
} // namespace opts

namespace llvm {
Expand Down Expand Up @@ -378,7 +382,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
else if (opts::Hugify)
Manager.registerPass(std::make_unique<HugePage>(NeverPrint));

Manager.registerPass(std::make_unique<ShortenInstructions>(NeverPrint));
Manager.registerPass(std::make_unique<ShortenInstructions>(NeverPrint),
opts::ShortenInstructions);

Manager.registerPass(std::make_unique<RemoveNops>(NeverPrint),
!opts::KeepNops);
Expand Down
174 changes: 80 additions & 94 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,6 @@ static cl::opt<bool> KeepARanges(
"keep or generate .debug_aranges section if .gdb_index is written"),
cl::Hidden, cl::cat(BoltCategory));

static cl::opt<bool> DeterministicDebugInfo(
"deterministic-debuginfo",
cl::desc("disables parallel execution of tasks that may produce "
"nondeterministic debug info"),
cl::init(true), cl::cat(BoltCategory));

static cl::opt<std::string> DwarfOutputPath(
"dwarf-output-path",
cl::desc("Path to where .dwo files or dwp file will be written out to."),
Expand Down Expand Up @@ -607,11 +601,6 @@ void DWARFRewriter::updateDebugInfo() {
StrWriter = std::make_unique<DebugStrWriter>(*BC.DwCtx, false);
StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>(BC);

if (!opts::DeterministicDebugInfo) {
opts::DeterministicDebugInfo = true;
errs() << "BOLT-WARNING: --deterministic-debuginfo is being deprecated\n";
}

/// Stores and serializes information that will be put into the
/// .debug_addr DWARF section.
std::unique_ptr<DebugAddrWriter> FinalAddrWriter;
Expand All @@ -631,8 +620,8 @@ void DWARFRewriter::updateDebugInfo() {
uint32_t CUIndex = 0;
std::mutex AccessMutex;
// Needs to be invoked in the same order as CUs are processed.
auto createRangeLocListAddressWriters =
[&](DWARFUnit &CU) -> DebugLocWriter * {
llvm::DenseMap<uint64_t, uint64_t> LocListWritersIndexByCU;
auto createRangeLocListAddressWriters = [&](DWARFUnit &CU) {
std::lock_guard<std::mutex> Lock(AccessMutex);
const uint16_t DwarfVersion = CU.getVersion();
if (DwarfVersion >= 5) {
Expand All @@ -652,7 +641,6 @@ void DWARFRewriter::updateDebugInfo() {
RangeListsWritersByCU[*DWOId] = std::move(DWORangeListsSectionWriter);
}
AddressWritersByCU[CU.getOffset()] = std::move(AddrW);

} else {
auto AddrW =
std::make_unique<DebugAddrWriter>(&BC, CU.getAddressByteSize());
Expand All @@ -668,7 +656,7 @@ void DWARFRewriter::updateDebugInfo() {
std::move(LegacyRangesSectionWriterByCU);
}
}
return LocListWritersByCU[CUIndex++].get();
LocListWritersIndexByCU[CU.getOffset()] = CUIndex++;
};

DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
Expand All @@ -677,74 +665,62 @@ void DWARFRewriter::updateDebugInfo() {
DWPState State;
if (opts::WriteDWP)
initDWPState(State);
auto processUnitDIE = [&](DWARFUnit *Unit, DIEBuilder *DIEBlder) {
// Check if the unit is a skeleton and we need special updates for it and
// its matching split/DWO CU.
auto processSplitCU = [&](DWARFUnit &Unit, DWARFUnit &SplitCU,
DIEBuilder &DIEBlder,
DebugRangesSectionWriter &TempRangesSectionWriter,
DebugAddrWriter &AddressWriter,
const std::string &DWOName,
const std::optional<std::string> &DwarfOutputPath) {
DIEBuilder DWODIEBuilder(BC, &(SplitCU).getContext(), DebugNamesTable,
&Unit);
DWODIEBuilder.buildDWOUnit(SplitCU);
DebugStrOffsetsWriter DWOStrOffstsWriter(BC);
DebugStrWriter DWOStrWriter((SplitCU).getContext(), true);
DWODIEBuilder.updateDWONameCompDirForTypes(
DWOStrOffstsWriter, DWOStrWriter, SplitCU, DwarfOutputPath, DWOName);
DebugLoclistWriter DebugLocDWoWriter(Unit, Unit.getVersion(), true,
AddressWriter);

updateUnitDebugInfo(SplitCU, DWODIEBuilder, DebugLocDWoWriter,
TempRangesSectionWriter, AddressWriter);
DebugLocDWoWriter.finalize(DWODIEBuilder,
*DWODIEBuilder.getUnitDIEbyUnit(SplitCU));
if (Unit.getVersion() >= 5)
TempRangesSectionWriter.finalizeSection();

emitDWOBuilder(DWOName, DWODIEBuilder, *this, SplitCU, Unit, State,
DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter,
GDBIndexSection);
};
auto processMainBinaryCU = [&](DWARFUnit &Unit, DIEBuilder &DIEBlder) {
std::optional<DWARFUnit *> SplitCU;
std::optional<uint64_t> RangesBase;
std::optional<uint64_t> DWOId = Unit->getDWOId();
std::optional<uint64_t> DWOId = Unit.getDWOId();
if (DWOId)
SplitCU = BC.getDWOCU(*DWOId);
DebugLocWriter *DebugLocWriter = createRangeLocListAddressWriters(*Unit);
DebugRangesSectionWriter *RangesSectionWriter =
Unit->getVersion() >= 5 ? RangeListsSectionWriter.get()
: LegacyRangesSectionWriter.get();
DebugAddrWriter *AddressWriter =
AddressWritersByCU[Unit->getOffset()].get();
// Skipping CUs that failed to load.
if (SplitCU) {
DIEBuilder DWODIEBuilder(BC, &(*SplitCU)->getContext(), DebugNamesTable,
Unit);
DWODIEBuilder.buildDWOUnit(**SplitCU);
std::string DWOName = "";
std::optional<std::string> DwarfOutputPath =
opts::DwarfOutputPath.empty()
? std::nullopt
: std::optional<std::string>(opts::DwarfOutputPath.c_str());
{
std::lock_guard<std::mutex> Lock(AccessMutex);
DWOName = DIEBlder->updateDWONameCompDir(
*StrOffstsWriter, *StrWriter, *Unit, DwarfOutputPath, std::nullopt);
}
DebugStrOffsetsWriter DWOStrOffstsWriter(BC);
DebugStrWriter DWOStrWriter((*SplitCU)->getContext(), true);
DWODIEBuilder.updateDWONameCompDirForTypes(DWOStrOffstsWriter,
DWOStrWriter, **SplitCU,
DwarfOutputPath, DWOName);
DebugLoclistWriter DebugLocDWoWriter(*Unit, Unit->getVersion(), true,
*AddressWriter);
DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter;
if (Unit->getVersion() >= 5) {
TempRangesSectionWriter = RangeListsWritersByCU[*DWOId].get();
} else {
TempRangesSectionWriter = LegacyRangesWritersByCU[*DWOId].get();
RangesBase = RangesSectionWriter->getSectionOffset();
}

updateUnitDebugInfo(*(*SplitCU), DWODIEBuilder, DebugLocDWoWriter,
*TempRangesSectionWriter, *AddressWriter);
DebugLocDWoWriter.finalize(DWODIEBuilder,
*DWODIEBuilder.getUnitDIEbyUnit(**SplitCU));
if (Unit->getVersion() >= 5)
TempRangesSectionWriter->finalizeSection();

emitDWOBuilder(DWOName, DWODIEBuilder, *this, **SplitCU, *Unit, State,
DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter,
GDBIndexSection);
}

if (Unit->getVersion() >= 5) {
RangesBase = RangesSectionWriter->getSectionOffset() +
DebugLocWriter &DebugLocWriter =
*LocListWritersByCU[LocListWritersIndexByCU[Unit.getOffset()]].get();
DebugRangesSectionWriter &RangesSectionWriter =
Unit.getVersion() >= 5 ? *RangeListsSectionWriter.get()
: *LegacyRangesSectionWriter.get();
DebugAddrWriter &AddressWriter =
*AddressWritersByCU[Unit.getOffset()].get();
if (Unit.getVersion() >= 5)
RangeListsSectionWriter->setAddressWriter(&AddressWriter);
if (Unit.getVersion() >= 5) {
RangesBase = RangesSectionWriter.getSectionOffset() +
getDWARF5RngListLocListHeaderSize();
RangesSectionWriter->initSection(*Unit);
StrOffstsWriter->finalizeSection(*Unit, *DIEBlder);
RangesSectionWriter.initSection(Unit);
StrOffstsWriter->finalizeSection(Unit, DIEBlder);
} else if (SplitCU) {
RangesBase = LegacyRangesSectionWriter.get()->getSectionOffset();
}

updateUnitDebugInfo(*Unit, *DIEBlder, *DebugLocWriter, *RangesSectionWriter,
*AddressWriter, RangesBase);
DebugLocWriter->finalize(*DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit));
if (Unit->getVersion() >= 5)
RangesSectionWriter->finalizeSection();
updateUnitDebugInfo(Unit, DIEBlder, DebugLocWriter, RangesSectionWriter,
AddressWriter, RangesBase);
DebugLocWriter.finalize(DIEBlder, *DIEBlder.getUnitDIEbyUnit(Unit));
if (Unit.getVersion() >= 5)
RangesSectionWriter.finalizeSection();
};

DIEBuilder DIEBlder(BC, BC.DwCtx.get(), DebugNamesTable);
Expand All @@ -759,25 +735,35 @@ void DWARFRewriter::updateDebugInfo() {
CUOffsetMap OffsetMap =
finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection);

const bool SingleThreadedMode =
opts::NoThreads || opts::DeterministicDebugInfo;
if (!SingleThreadedMode)
DIEBlder.buildCompileUnits();
if (SingleThreadedMode) {
CUPartitionVector PartVec = partitionCUs(*BC.DwCtx);
for (std::vector<DWARFUnit *> &Vec : PartVec) {
DIEBlder.buildCompileUnits(Vec);
for (DWARFUnit *CU : DIEBlder.getProcessedCUs())
processUnitDIE(CU, &DIEBlder);
finalizeCompileUnits(DIEBlder, *Streamer, OffsetMap,
DIEBlder.getProcessedCUs(), *FinalAddrWriter);
CUPartitionVector PartVec = partitionCUs(*BC.DwCtx);
for (std::vector<DWARFUnit *> &Vec : PartVec) {
DIEBlder.buildCompileUnits(Vec);
for (DWARFUnit *CU : DIEBlder.getProcessedCUs()) {
createRangeLocListAddressWriters(*CU);
std::optional<DWARFUnit *> SplitCU;
std::optional<uint64_t> DWOId = CU->getDWOId();
if (DWOId)
SplitCU = BC.getDWOCU(*DWOId);
if (!SplitCU)
continue;
DebugAddrWriter &AddressWriter =
*AddressWritersByCU[CU->getOffset()].get();
DebugRangesSectionWriter *TempRangesSectionWriter =
CU->getVersion() >= 5 ? RangeListsWritersByCU[*DWOId].get()
: LegacyRangesWritersByCU[*DWOId].get();
std::optional<std::string> DwarfOutputPath =
opts::DwarfOutputPath.empty()
? std::nullopt
: std::optional<std::string>(opts::DwarfOutputPath.c_str());
std::string DWOName = DIEBlder.updateDWONameCompDir(
*StrOffstsWriter, *StrWriter, *CU, DwarfOutputPath, std::nullopt);
processSplitCU(*CU, **SplitCU, DIEBlder, *TempRangesSectionWriter,
AddressWriter, DWOName, DwarfOutputPath);
}
} else {
// Update unit debug info in parallel
ThreadPoolInterface &ThreadPool = ParallelUtilities::getThreadPool();
for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units())
ThreadPool.async(processUnitDIE, CU.get(), &DIEBlder);
ThreadPool.wait();
for (DWARFUnit *CU : DIEBlder.getProcessedCUs())
processMainBinaryCU(*CU, DIEBlder);
finalizeCompileUnits(DIEBlder, *Streamer, OffsetMap,
DIEBlder.getProcessedCUs(), *FinalAddrWriter);
}

DebugNamesTable.emitAccelTable();
Expand Down
6 changes: 5 additions & 1 deletion bolt/lib/Rewrite/PseudoProbeRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ static cl::opt<PrintPseudoProbesOptions> PrintPseudoProbes(
clEnumValN(PPP_All, "all", "enable all debugging printout")),
cl::Hidden, cl::cat(BoltCategory));

extern cl::opt<bool> ProfileUsePseudoProbes;
} // namespace opts

namespace {
Expand Down Expand Up @@ -89,12 +90,15 @@ class PseudoProbeRewriter final : public MetadataRewriter {
};

Error PseudoProbeRewriter::preCFGInitializer() {
parsePseudoProbe();
if (opts::ProfileUsePseudoProbes)
parsePseudoProbe();

return Error::success();
}

Error PseudoProbeRewriter::postEmitFinalizer() {
if (!opts::ProfileUsePseudoProbes)
parsePseudoProbe();
updatePseudoProbes();

return Error::success();
Expand Down
8 changes: 4 additions & 4 deletions bolt/lib/RuntimeLibs/HugifyRuntimeLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ cl::opt<bool>
"(which is what --hot-text relies on)."),
cl::cat(BoltOptCategory));

static cl::opt<std::string> RuntimeHugifyLib(
"runtime-hugify-lib",
cl::desc("specify file name of the runtime hugify library"),
cl::init("libbolt_rt_hugify.a"), cl::cat(BoltOptCategory));
static cl::opt<std::string>
RuntimeHugifyLib("runtime-hugify-lib",
cl::desc("specify path of the runtime hugify library"),
cl::init("libbolt_rt_hugify.a"), cl::cat(BoltOptCategory));

} // namespace opts

Expand Down
2 changes: 1 addition & 1 deletion bolt/lib/RuntimeLibs/InstrumentationRuntimeLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace opts {

cl::opt<std::string> RuntimeInstrumentationLib(
"runtime-instrumentation-lib",
cl::desc("specify file name of the runtime instrumentation library"),
cl::desc("specify path of the runtime instrumentation library"),
cl::init("libbolt_rt_instr.a"), cl::cat(BoltOptCategory));

extern cl::opt<bool> InstrumentationFileAppendPID;
Expand Down
35 changes: 29 additions & 6 deletions bolt/lib/RuntimeLibs/RuntimeLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ using namespace bolt;

void RuntimeLibrary::anchor() {}

std::string RuntimeLibrary::getLibPath(StringRef ToolPath,
StringRef LibFileName) {
std::string RuntimeLibrary::getLibPathByToolPath(StringRef ToolPath,
StringRef LibFileName) {
StringRef Dir = llvm::sys::path::parent_path(ToolPath);
SmallString<128> LibPath = llvm::sys::path::parent_path(Dir);
llvm::sys::path::append(LibPath, "lib" LLVM_LIBDIR_SUFFIX);
Expand All @@ -38,13 +38,36 @@ std::string RuntimeLibrary::getLibPath(StringRef ToolPath,
llvm::sys::path::append(LibPath, "lib" LLVM_LIBDIR_SUFFIX);
}
llvm::sys::path::append(LibPath, LibFileName);
if (!llvm::sys::fs::exists(LibPath)) {
errs() << "BOLT-ERROR: library not found: " << LibPath << "\n";
exit(1);
}
return std::string(LibPath);
}

std::string RuntimeLibrary::getLibPathByInstalled(StringRef LibFileName) {
SmallString<128> LibPath(CMAKE_INSTALL_FULL_LIBDIR);
llvm::sys::path::append(LibPath, LibFileName);
return std::string(LibPath);
}

std::string RuntimeLibrary::getLibPath(StringRef ToolPath,
StringRef LibFileName) {
if (llvm::sys::fs::exists(LibFileName)) {
return std::string(LibFileName);
}

std::string ByTool = getLibPathByToolPath(ToolPath, LibFileName);
if (llvm::sys::fs::exists(ByTool)) {
return ByTool;
}

std::string ByInstalled = getLibPathByInstalled(LibFileName);
if (llvm::sys::fs::exists(ByInstalled)) {
return ByInstalled;
}

errs() << "BOLT-ERROR: library not found: " << ByTool << ", " << ByInstalled
<< ", or " << LibFileName << "\n";
exit(1);
}

void RuntimeLibrary::loadLibrary(StringRef LibPath, BOLTLinker &Linker,
BOLTLinker::SectionsMapper MapSections) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf =
Expand Down
12 changes: 12 additions & 0 deletions bolt/lib/Target/AArch64/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ set(LLVM_LINK_COMPONENTS
AArch64Desc
)

if(BOLT_BUILT_STANDALONE)
set(LLVM_TARGET_DEFINITIONS ${LLVM_MAIN_SRC_DIR}/lib/Target/AArch64/AArch64.td)
list(APPEND LLVM_TABLEGEN_FLAGS -I ${LLVM_MAIN_SRC_DIR}/lib/Target/AArch64)
tablegen(LLVM AArch64GenInstrInfo.inc -gen-instr-info)
tablegen(LLVM AArch64GenRegisterInfo.inc -gen-register-info)
tablegen(LLVM AArch64GenSystemOperands.inc -gen-searchable-tables)
tablegen(LLVM AArch64GenSubtargetInfo.inc -gen-subtarget)

add_public_tablegen_target(AArch64CommonTableGen)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
endif()

add_llvm_library(LLVMBOLTTargetAArch64
AArch64MCPlusBuilder.cpp

Expand Down
13 changes: 13 additions & 0 deletions bolt/lib/Target/RISCV/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ set(LLVM_LINK_COMPONENTS
RISCVDesc
)

if(BOLT_BUILT_STANDALONE)
# tablegen, copied from llvm/lib/Target/RISCV/CMakeLists.txt
set(LLVM_TARGET_DEFINITIONS ${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV/RISCV.td)
list(APPEND LLVM_TABLEGEN_FLAGS -I ${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV)
tablegen(LLVM RISCVGenInstrInfo.inc -gen-instr-info)
tablegen(LLVM RISCVGenRegisterInfo.inc -gen-register-info)
tablegen(LLVM RISCVGenSearchableTables.inc -gen-searchable-tables)
tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget)

add_public_tablegen_target(RISCVCommonTableGen)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
endif()

add_llvm_library(LLVMBOLTTargetRISCV
RISCVMCPlusBuilder.cpp

Expand Down
12 changes: 12 additions & 0 deletions bolt/lib/Target/X86/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ set(LLVM_LINK_COMPONENTS
X86Desc
)

if(BOLT_BUILT_STANDALONE)
set(LLVM_TARGET_DEFINITIONS ${LLVM_MAIN_SRC_DIR}/lib/Target/X86/X86.td)
list(APPEND LLVM_TABLEGEN_FLAGS -I ${LLVM_MAIN_SRC_DIR}/lib/Target/X86)
tablegen(LLVM X86GenInstrInfo.inc -gen-instr-info -instr-info-expand-mi-operand-info=0)
tablegen(LLVM X86GenMnemonicTables.inc -gen-x86-mnemonic-tables -asmwriternum=1)
tablegen(LLVM X86GenRegisterInfo.inc -gen-register-info)
tablegen(LLVM X86GenSubtargetInfo.inc -gen-subtarget)

add_public_tablegen_target(X86CommonTableGen)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
endif()

add_llvm_library(LLVMBOLTTargetX86
X86MCPlusBuilder.cpp
X86MCSymbolizer.cpp
Expand Down
32 changes: 28 additions & 4 deletions bolt/lib/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc)
find_first_existing_vc_file("${BOLT_SOURCE_DIR}" bolt_vc)

# The VC revision include that we want to generate.
set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc")

set(generate_vcs_version_script "${LLVM_CMAKE_DIR}/GenerateVersionFromVCS.cmake")

# Create custom target to generate the VC revision include.
add_custom_command(OUTPUT "${version_inc}"
DEPENDS "${llvm_vc}" "${bolt_vc}" "${generate_vcs_version_script}"
COMMAND ${CMAKE_COMMAND} "-DNAMES=BOLT"
"-DHEADER_FILE=${version_inc}"
"-DBOLT_SOURCE_DIR=${BOLT_SOURCE_DIR}"
"-DLLVM_VC_REPOSITORY=${llvm_vc_repository}"
"-DLLVM_VC_REVISION=${llvm_vc_revision}"
"-DLLVM_FORCE_VC_REVISION=${LLVM_FORCE_VC_REVISION}"
"-DLLVM_FORCE_VC_REPOSITORY=${LLVM_FORCE_VC_REPOSITORY}"
-P "${generate_vcs_version_script}")

# Mark the generated header as being generated.
set_source_files_properties("${version_inc}"
PROPERTIES GENERATED TRUE
HEADER_FILE_ONLY TRUE)

include_directories(${CMAKE_CURRENT_BINARY_DIR})

add_llvm_library(LLVMBOLTUtils
CommandLineOpts.cpp
Utils.cpp

${version_inc}
DISABLE_LLVM_LINK_LLVM_DYLIB

LINK_LIBS
${LLVM_PTHREAD_LIB}

DEPENDS
llvm_vcsrevision_h

LINK_COMPONENTS
Support
)
6 changes: 3 additions & 3 deletions bolt/lib/Utils/CommandLineOpts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
//===----------------------------------------------------------------------===//

#include "bolt/Utils/CommandLineOpts.h"
#include "llvm/Support/VCSRevision.h"
#include "VCSVersion.inc"

using namespace llvm;

namespace llvm {
namespace bolt {
const char *BoltRevision =
#ifdef LLVM_REVISION
LLVM_REVISION;
#ifdef BOLT_REVISION
BOLT_REVISION;
#else
"<unknown>";
#endif
Expand Down
24 changes: 18 additions & 6 deletions bolt/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,19 @@ add_library(bolt_rt_instr STATIC
instr.cpp
${CMAKE_CURRENT_BINARY_DIR}/config.h
)
set_target_properties(bolt_rt_instr PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${LLVM_LIBRARY_DIR}")
set_target_properties(bolt_rt_instr PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
add_library(bolt_rt_hugify STATIC
hugify.cpp
${CMAKE_CURRENT_BINARY_DIR}/config.h
)
set_target_properties(bolt_rt_hugify PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${LLVM_LIBRARY_DIR}")
set_target_properties(bolt_rt_hugify PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")

if(NOT BOLT_BUILT_STANDALONE)
add_custom_command(TARGET bolt_rt_instr POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib/libbolt_rt_instr.a" "${LLVM_LIBRARY_DIR}")
add_custom_command(TARGET bolt_rt_hugify POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib/libbolt_rt_hugify.a" "${LLVM_LIBRARY_DIR}")
endif()

set(BOLT_RT_FLAGS
-ffreestanding
Expand All @@ -46,18 +53,23 @@ target_include_directories(bolt_rt_instr PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(bolt_rt_hugify PRIVATE ${BOLT_RT_FLAGS})
target_include_directories(bolt_rt_hugify PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

install(TARGETS bolt_rt_instr DESTINATION "lib${LLVM_LIBDIR_SUFFIX}")
install(TARGETS bolt_rt_hugify DESTINATION "lib${LLVM_LIBDIR_SUFFIX}")
install(TARGETS bolt_rt_instr DESTINATION "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
install(TARGETS bolt_rt_hugify DESTINATION "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")

if (CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_library(bolt_rt_instr_osx STATIC
instr.cpp
${CMAKE_CURRENT_BINARY_DIR}/config.h
)
set_target_properties(bolt_rt_instr_osx PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${LLVM_LIBRARY_DIR}")
set_target_properties(bolt_rt_instr_osx PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")
target_include_directories(bolt_rt_instr_osx PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(bolt_rt_instr_osx PRIVATE
-target x86_64-apple-darwin19.6.0
${BOLT_RT_FLAGS})
install(TARGETS bolt_rt_instr_osx DESTINATION "lib${LLVM_LIBDIR_SUFFIX}")
install(TARGETS bolt_rt_instr_osx DESTINATION "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}")

if(NOT BOLT_BUILT_STANDALONE)
add_custom_command(TARGET bolt_rt_instr_osx POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/lib/libbolt_rt_instr_osx.a" "${LLVM_LIBRARY_DIR}")
endif()
endif()
2 changes: 1 addition & 1 deletion bolt/test/AArch64/update-debug-reloc.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# update-debug-sections option.

RUN: %clang %cflags -g %p/../Inputs/asm_foo.s %p/../Inputs/asm_main.c -o %t.exe
RUN: llvm-bolt %t.exe -o %t --update-debug-sections
RUN: llvm-bolt %t.exe -o %t --update-debug-sections 2>&1 | FileCheck %s

CHECK: BOLT-INFO: Target architecture: aarch64
CHECK-NOT: Reloc num: 10
Expand Down
4 changes: 2 additions & 2 deletions bolt/test/AArch64/veneer-gold.s
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ dummy:
.type foo, %function
foo:
# CHECK: <foo>:
# CHECK-NEXT : {{.*}} bl {{.*}} <foo2>
# CHECK-NEXT: {{.*}} bl {{.*}} <foo2>
bl .L2
ret
.size foo, .-foo
Expand All @@ -38,7 +38,7 @@ foo:
.type foo2, %function
foo2:
# CHECK: <foo2>:
# CHECK-NEXT : {{.*}} bl {{.*}} <foo2>
# CHECK-NEXT: {{.*}} bl {{.*}} <foo2>
bl .L2
ret
.size foo2, .-foo2
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/dwarf5-df-types-modify-dwo-name-mixed.test
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
; BOLT-DWP: DW_TAG_compile_unit
; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DW-NOT: DW_AT_dwo_name
; BOLT-DWP-NOT: DW_AT_dwo_name
; BOLT-DWP: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-DWP-NEXT: "main"
; BOLT-DWP-NEXT: "int"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
# RUN: %clang %cflags %tmain.o %thelper.o -o %t.exe
# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt | FileCheck --check-prefix=POSTCHECK %s
# RUN: llvm-dwarfdump --show-form --verbose --debug-addr %t.bolt | FileCheck --check-prefix=POSTCHECKADDR %s
# RUN: llvm-dwarfdump --show-form --verbose --debug-types %t.bolt | FileCheck --check-prefix=POSTCHECKTU %s

## This test checks that BOLT handles correctly backward and forward cross CU references
## for DWARF5 and DWARF4 with -fdebug-types-section
## for DWARF5 and DWARF4 with -fdebug-types-section and checks the address table is correct.

# POSTCHECK: version = 0x0005
# POSTCHECK: DW_TAG_type_unit
Expand All @@ -29,6 +30,15 @@
# POSTCHECK: DW_TAG_variable [20]
# POSTCHECK: DW_AT_type [DW_FORM_ref_addr] (0x{{[0-9a-f]+}} "Foo3a")

# POSTCHECKADDR: Addrs: [
# POSTCHECKADDR-NEXT: 0x0000000000001360
# POSTCHECKADDR-NEXT: 0x0000000000000000
# POSTCHECKADDR-NEXT: ]
# POSTCHECKADDR: Addrs: [
# POSTCHECKADDR-NEXT: 0x00000000000013e0
# POSTCHECKADDR-NEXT: 0x0000000000000000
# POSTCHECKADDR-NEXT: ]

# POSTCHECKTU: version = 0x0004
# POSTCHECKTU: DW_TAG_type_unit
# POSTCHECKTU: DW_TAG_structure_type
Expand Down
19 changes: 18 additions & 1 deletion bolt/test/X86/dwarf5-locexpr-referrence.test
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
# RUN: %clang %cflags -dwarf-5 %tmain.o %thelper.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt | FileCheck --check-prefix=CHECK %s
# RUN: llvm-dwarfdump --show-form --verbose --debug-addr %t.bolt | FileCheck --check-prefix=CHECKADDR %s

## This test checks that we update relative DIE references with DW_OP_convert that are in locexpr.
## This test checks that we update relative DIE references with DW_OP_convert that are in locexpr
## and checks the address table is correct.

# CHECK: version = 0x0005
# CHECK: DW_TAG_variable
Expand All @@ -19,3 +21,18 @@
# CHECK-SAME: DW_OP_convert (0x00000028 -> 0x00000092)
# CHECK-SAME: DW_OP_convert (0x0000002c -> 0x00000096)
# CHECK: version = 0x0005

# CHECKADDR: Addrs: [
# CHECKADDR-NEXT: 0x0000000000001330
# CHECKADDR-NEXT: 0x0000000000000000
# CHECKADDR-NEXT: 0x0000000000001333
# CHECKADDR-NEXT: ]
# CHECKADDR: Addrs: [
# CHECKADDR-NEXT: 0x0000000000001340
# CHECKADDR-NEXT: 0x0000000000000000
# CHECKADDR-NEXT: 0x0000000000001343
# CHECKADDR-NEXT: ]
# CHECKADDR: Addrs: [
# CHECKADDR-NEXT: 0x0000000000001320
# CHECKADDR-NEXT: 0x0000000000000000
# CHECKADDR-NEXT: ]
2 changes: 1 addition & 1 deletion bolt/test/X86/dwarf5-one-loclists-two-bases.test
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
# POSTCHECK: version = 0x0005
# POSTCHECK: DW_AT_loclists_base [DW_FORM_sec_offset] (0x0000000c)
# POSTCHECK: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c)
# POSTCHECK-EMPTY
# POSTCHECK-EMPTY:
# POSTCHECK: DW_TAG_variable
# POSTCHECK: DW_AT_location [DW_FORM_loclistx]
# POSTCHECK-SAME: indexed (0x0)
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/dwarf5-two-loclists.test
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
# POSTCHECK: version = 0x0005
# POSTCHECK: DW_AT_loclists_base [DW_FORM_sec_offset] (0x0000000c)
# POSTCHECK: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c)
# POSTCHECK-EMPTY
# POSTCHECK-EMPTY:
# POSTCHECK: DW_TAG_variable
# POSTCHECK: DW_AT_location [DW_FORM_loclistx]
# POSTCHECK-SAME: indexed (0x0)
Expand Down
4 changes: 2 additions & 2 deletions bolt/test/X86/dwarf5-two-rnglists.test
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
# POSTCHECK-NEXT: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
# POSTCHECK-NEXT: DW_AT_loclists_base [DW_FORM_sec_offset] (0x0000000c)
# POSTCHECK-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c)
# POSTCHECK-EMPTY
# POSTCHECK-EMPTY:
# POSTCHECK: DW_TAG_subprogram
# POSTCHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx]
# POSTCHECK-SAME: indexed (0x1)
Expand All @@ -75,7 +75,7 @@
# POSTCHECK-NEXT: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000030)
# POSTCHECK-NEXT: DW_AT_loclists_base [DW_FORM_sec_offset] (0x00000045)
# POSTCHECK-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x00000035)
# POSTCHECK-EMPTY
# POSTCHECK-EMPTY:

# POSTCHECK: DW_TAG_subprogram
# POSTCHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx]
Expand Down
103 changes: 103 additions & 0 deletions bolt/test/X86/match-functions-with-call-graph.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
## Tests blocks matching by called function names in inferStaleProfile.

# REQUIRES: system-linux
# RUN: split-file %s %t
# RUN: %clang %cflags %t/main.cpp -o %t.exe -Wl,-q -nostdlib
# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml --profile-ignore-hash -v=1 \
# RUN: --dyno-stats --print-cfg --infer-stale-profile=1 --match-with-call-graph 2>&1 | FileCheck %s

# CHECK: BOLT-INFO: matched 1 functions with call graph

#--- main.cpp
void foo() {}

void bar() {}

void qux() {
foo();
bar();
}

void fred() {
foo();
qux();
bar();
bar();
foo();
}

int main() {
return 0;
}

#--- yaml
---
header:
profile-version: 1
binary-name: 'match-functions-with-calls-as-anchors.s.tmp.exe'
binary-build-id: '<unknown>'
profile-flags: [ lbr ]
profile-origin: branch profile reader
profile-events: ''
dfs-order: false
hash-func: xxh3
functions:
- name: main
fid: 0
hash: 0x0000000000000001
exec: 1
nblocks: 6
blocks:
- bid: 1
hash: 0x0000000000000001
insns: 1
succ: [ { bid: 3, cnt: 1} ]
- name: _Z3foov
fid: 1
hash: 0x0000000000000002
exec: 1
nblocks: 6
blocks:
- bid: 1
hash: 0x0000000000000002
insns: 1
succ: [ { bid: 3, cnt: 1} ]

- name: _Z3barv
fid: 2
hash: 0x0000000000000003
exec: 1
nblocks: 6
blocks:
- bid: 1
hash: 0x0000000000000003
insns: 1
succ: [ { bid: 3, cnt: 1} ]
- name: _Z3quxv
fid: 3
hash: 0x0000000000000004
exec: 4
nblocks: 6
blocks:
- bid: 1
hash: 0x0000000000000004
insns: 1
succ: [ { bid: 3, cnt: 1} ]
calls: [ { off : 0, fid : 1, cnt : 0},
{ off : 0, fid : 2, cnt : 0} ]
- name: _Z4bazv
fid: 4
hash: 0x0000000000000005
exec: 1
nblocks: 6
blocks:
- bid: 1
hash: 0x0000000000000005
insns: 1
succ: [ { bid: 3, cnt: 1} ]
calls: [ { off : 0, fid : 3, cnt : 0},
{ off : 0, fid : 1, cnt : 0},
{ off : 0, fid : 2, cnt : 0},
{ off : 0, fid : 1, cnt : 0},
{ off : 0, fid : 2, cnt : 0} ]
...
12 changes: 10 additions & 2 deletions bolt/test/X86/pseudoprobe-decoding-inline.test
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
# PREAGG: B X:0 #main# 1 0
## Check pseudo-probes in regular YAML profile (non-BOLTed binary)
# RUN: link_fdata %s %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin %t.preagg PREAGG
# RUN: perf2bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin -p %t.preagg --pa -w %t.yaml -o %t.fdata
# RUN: perf2bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin -p %t.preagg --pa -w %t.yaml -o %t.fdata --profile-use-pseudo-probes
# RUN: FileCheck --input-file %t.yaml %s --check-prefix CHECK-YAML
## Check pseudo-probes in BAT YAML profile (BOLTed binary)
# RUN: link_fdata %s %t.bolt %t.preagg2 PREAGG
# RUN: perf2bolt %t.bolt -p %t.preagg2 --pa -w %t.yaml2 -o %t.fdata2
# RUN: perf2bolt %t.bolt -p %t.preagg2 --pa -w %t.yaml2 -o %t.fdata2 --profile-use-pseudo-probes
# RUN: FileCheck --input-file %t.yaml2 %s --check-prefix CHECK-YAML
# CHECK-YAML: name: bar
# CHECK-YAML: - bid: 0
Expand All @@ -29,6 +29,14 @@
# CHECK-YAML: pseudo_probes: [ { guid: 0xDB956436E78DD5FA, id: 1, type: 0 }, { guid: 0x5CF8C24CDB18BDAC, id: 1, type: 0 }, { guid: 0x5CF8C24CDB18BDAC, id: 2, type: 0 } ]
# CHECK-YAML: guid: 0xDB956436E78DD5FA
# CHECK-YAML: pseudo_probe_desc_hash: 0x10000FFFFFFFF
#
## Check that without --profile-use-pseudo-probes option, no pseudo probes are
## generated
# RUN: perf2bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin -p %t.preagg --pa -w %t.yaml -o %t.fdata
# RUN: FileCheck --input-file %t.yaml %s --check-prefix CHECK-NO-OPT
# CHECK-NO-OPT-NOT: pseudo_probes
# CHECK-NO-OPT-NOT: guid
# CHECK-NO-OPT-NOT: pseudo_probe_desc_hash

CHECK: Report of decoding input pseudo probe binaries

Expand Down
95 changes: 95 additions & 0 deletions bolt/test/X86/three-way-split-jt.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
## This reproduces an issue where the function is split into three fragments
## and all fragments access the same jump table.

# REQUIRES: system-linux

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: llvm-strip --strip-unneeded %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.out -v=1 -print-only=main.warm -print-cfg 2>&1 | FileCheck %s

# CHECK-DAG: BOLT-INFO: marking main.warm as a fragment of main
# CHECK-DAG: BOLT-INFO: marking main.cold as a fragment of main
# CHECK-DAG: BOLT-INFO: processing main.warm as a sibling of non-ignored function
# CHECK-DAG: BOLT-INFO: processing main.cold as a sibling of non-ignored function
# CHECK-DAG: BOLT-WARNING: Ignoring main.cold
# CHECK-DAG: BOLT-WARNING: Ignoring main.warm
# CHECK-DAG: BOLT-WARNING: Ignoring main
# CHECK: BOLT-WARNING: skipped 3 functions due to cold fragments

# CHECK: PIC Jump table JUMP_TABLE for function main, main.warm, main.cold
# CHECK-NEXT: 0x0000 : __ENTRY_main@0x[[#]]
# CHECK-NEXT: 0x0004 : __ENTRY_main@0x[[#]]
# CHECK-NEXT: 0x0008 : __ENTRY_main.cold@0x[[#]]
# CHECK-NEXT: 0x000c : __ENTRY_main@0x[[#]]
.globl main
.type main, %function
.p2align 2
main:
LBB0:
andl $0xf, %ecx
cmpb $0x4, %cl
## exit through ret
ja LBB3

## jump table dispatch, jumping to label indexed by val in %ecx
LBB1:
leaq JUMP_TABLE(%rip), %r8
movzbl %cl, %ecx
movslq (%r8,%rcx,4), %rax
addq %rax, %r8
jmpq *%r8

LBB2:
xorq %rax, %rax
LBB3:
addq $0x8, %rsp
ret
.size main, .-main

.globl main.warm
.type main.warm, %function
.p2align 2
main.warm:
LBB20:
andl $0xb, %ebx
cmpb $0x1, %cl
# exit through ret
ja LBB23

## jump table dispatch, jumping to label indexed by val in %ecx
LBB21:
leaq JUMP_TABLE(%rip), %r8
movzbl %cl, %ecx
movslq (%r8,%rcx,4), %rax
addq %rax, %r8
jmpq *%r8

LBB22:
xorq %rax, %rax
LBB23:
addq $0x8, %rsp
ret
.size main.warm, .-main.warm

## cold fragment is only reachable through jump table
.globl main.cold
.type main.cold, %function
main.cold:
leaq JUMP_TABLE(%rip), %r8
movzbl %cl, %ecx
movslq (%r8,%rcx,4), %rax
addq %rax, %r8
jmpq *%r8
LBB4:
callq abort
.size main.cold, .-main.cold

.rodata
## jmp table, entries must be R_X86_64_PC32 relocs
.globl JUMP_TABLE
JUMP_TABLE:
.long LBB2-JUMP_TABLE
.long LBB3-JUMP_TABLE
.long LBB4-JUMP_TABLE
.long LBB3-JUMP_TABLE
14 changes: 13 additions & 1 deletion bolt/test/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,22 @@

tool_dirs = [config.llvm_tools_dir, config.test_source_root]

llvm_bolt_args = []

if config.libbolt_rt_instr:
llvm_bolt_args.append(f"--runtime-instrumentation-lib={config.libbolt_rt_instr}")

if config.libbolt_rt_hugify:
llvm_bolt_args.append(f"--runtime-hugify-lib={config.libbolt_rt_hugify}")

tools = [
ToolSubst("llc", unresolved="fatal"),
ToolSubst("llvm-dwarfdump", unresolved="fatal"),
ToolSubst("llvm-bolt", unresolved="fatal"),
ToolSubst(
"llvm-bolt",
unresolved="fatal",
extra_args=llvm_bolt_args,
),
ToolSubst("llvm-boltdiff", unresolved="fatal"),
ToolSubst("llvm-bolt-heatmap", unresolved="fatal"),
ToolSubst("llvm-bat-dump", unresolved="fatal"),
Expand Down
2 changes: 2 additions & 0 deletions bolt/test/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ config.bolt_clang = "@BOLT_CLANG_EXE@"
config.bolt_lld = "@BOLT_LLD_EXE@"
config.targets_to_build = "@BOLT_TARGETS_TO_BUILD@"
config.gnu_ld = "@GNU_LD_EXECUTABLE@"
config.libbolt_rt_instr = "@LIBBOLT_RT_INSTR@"
config.libbolt_rt_hugify = "@LIBBOLT_RT_HUGIFY@"

import lit.llvm
lit.llvm.initialize(lit_config, config)
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/perf2bolt/lit.local.cfg
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import shutil

if shutil.which("perf") != None:
if shutil.which("perf") is not None:
config.available_features.add("perf")
4 changes: 2 additions & 2 deletions bolt/utils/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:20.04 AS builder
FROM ubuntu:24.04 AS builder

ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=UTC
Expand Down Expand Up @@ -26,6 +26,6 @@ RUN mkdir build && \
ninja install-llvm-bolt install-perf2bolt install-merge-fdata \
install-llvm-boltdiff install-bolt_rt

FROM ubuntu:20.04
FROM ubuntu:24.04

COPY --from=builder /home/bolt/install /usr/local
13 changes: 13 additions & 0 deletions clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <optional>
#include <string>

Expand Down Expand Up @@ -979,6 +980,18 @@ static llvm::Error serializeIndex(ClangDocContext &CDCtx) {
"error creating index file: " +
FileErr.message());
}
llvm::SmallString<128> RootPath(CDCtx.OutDirectory);
if (llvm::sys::path::is_relative(RootPath)) {
llvm::sys::fs::make_absolute(RootPath);
}
// Replace the escaped characters with a forward slash. It shouldn't matter
// when rendering the webpage in a web browser. This helps to prevent the
// JavaScript from escaping characters incorrectly, and introducing bad paths
// in the URLs.
std::string RootPathEscaped = RootPath.str().str();
std::replace(RootPathEscaped.begin(), RootPathEscaped.end(), '\\', '/');
OS << "var RootPath = \"" << RootPathEscaped << "\";\n";

CDCtx.Idx.sort();
llvm::json::OStream J(OS, 2);
std::function<void(Index)> IndexToJSON = [&](const Index &I) {
Expand Down
44 changes: 35 additions & 9 deletions clang-tools-extra/clang-doc/Mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,28 @@
#include "clang/AST/Comment.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Mutex.h"

namespace clang {
namespace doc {

static llvm::StringSet<> USRVisited;
static llvm::sys::Mutex USRVisitedGuard;

template <typename T> bool isTypedefAnonRecord(const T *D) {
if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
return C->getTypedefNameForAnonDecl();
}
return false;
}

void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
TraverseDecl(Context.getTranslationUnitDecl());
}

template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
template <typename T>
bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
// If we're looking a decl not in user files, skip this decl.
if (D->getASTContext().getSourceManager().isInSystemHeader(D->getLocation()))
return true;
Expand All @@ -34,6 +46,16 @@ template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
// If there is an error generating a USR for the decl, skip this decl.
if (index::generateUSRForDecl(D, USR))
return true;
// Prevent Visiting USR twice
{
std::lock_guard<llvm::sys::Mutex> Guard(USRVisitedGuard);
StringRef Visited = USR.str();
if (USRVisited.count(Visited) && !isTypedefAnonRecord<T>(D))
return true;
// We considered a USR to be visited only when its defined
if (IsDefinition)
USRVisited.insert(Visited);
}
bool IsFileInRootDir;
llvm::SmallString<128> File =
getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
Expand All @@ -53,30 +75,34 @@ template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
}

bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl *D) {
return mapDecl(D);
return mapDecl(D, /*isDefinition=*/true);
}

bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); }
bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) {
return mapDecl(D, D->isThisDeclarationADefinition());
}

bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); }
bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) {
return mapDecl(D, D->isThisDeclarationADefinition());
}

bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl *D) {
return mapDecl(D);
return mapDecl(D, D->isThisDeclarationADefinition());
}

bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) {
// Don't visit CXXMethodDecls twice
if (isa<CXXMethodDecl>(D))
return true;
return mapDecl(D);
return mapDecl(D, D->isThisDeclarationADefinition());
}

bool MapASTVisitor::VisitTypedefDecl(const TypedefDecl *D) {
return mapDecl(D);
return mapDecl(D, /*isDefinition=*/true);
}

bool MapASTVisitor::VisitTypeAliasDecl(const TypeAliasDecl *D) {
return mapDecl(D);
return mapDecl(D, /*isDefinition=*/true);
}

comments::FullComment *
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-doc/Mapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
bool VisitTypeAliasDecl(const TypeAliasDecl *D);

private:
template <typename T> bool mapDecl(const T *D);
template <typename T> bool mapDecl(const T *D, bool IsDefinition);

int getLine(const NamedDecl *D, const ASTContext &Context) const;
llvm::SmallString<128> getFile(const NamedDecl *D, const ASTContext &Context,
Expand Down
51 changes: 13 additions & 38 deletions clang-tools-extra/clang-doc/assets/index.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,17 @@
// Append using posix-style a file name or directory to Base
function append(Base, New) {
if (!New)
return Base;
if (Base)
Base += "/";
Base += New;
return Base;
}

// Get relative path to access FilePath from CurrentDirectory
function computeRelativePath(FilePath, CurrentDirectory) {
var Path = FilePath;
while (Path) {
if (CurrentDirectory == Path)
return FilePath.substring(Path.length + 1);
Path = Path.substring(0, Path.lastIndexOf("/"));
}

var Dir = CurrentDirectory;
var Result = "";
while (Dir) {
if (Dir == FilePath)
break;
Dir = Dir.substring(0, Dir.lastIndexOf("/"));
Result = append(Result, "..")
function genLink(Ref) {
// we treat the file paths different depending on if we're
// serving via a http server or viewing from a local
var Path = window.location.protocol.startsWith("file") ?
`${window.location.protocol}//${window.location.host}/${Ref.Path}` :
`${window.location.protocol}//${RootPath}/${Ref.Path}`;
if (Ref.RefType === "namespace") {
Path = `${Path}/index.html`
} else if (Ref.Path === "") {
Path = `${Path}${Ref.Name}.html`;
} else {
Path = `${Path}/${Ref.Name}.html`;
}
Result = append(Result, FilePath.substring(Dir.length))
return Result;
}

function genLink(Ref, CurrentDirectory) {
var Path = computeRelativePath(Ref.Path, CurrentDirectory);
if (Ref.RefType == "namespace")
Path = append(Path, "index.html");
else
Path = append(Path, Ref.Name + ".html")

ANode = document.createElement("a");
ANode = document.createElement("a");
ANode.setAttribute("href", Path);
var TextNode = document.createTextNode(Ref.Name);
ANode.appendChild(TextNode);
Expand Down
1 change: 0 additions & 1 deletion clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ Example usage for a project using a compile commands database:
for (auto &Group : USRToBitcode) {
Pool.async([&]() {
std::vector<std::unique_ptr<doc::Info>> Infos;

for (auto &Bitcode : Group.getValue()) {
llvm::BitstreamCursor Stream(Bitcode);
doc::ClangDocBitcodeReader Reader(Stream);
Expand Down
45 changes: 36 additions & 9 deletions clang-tools-extra/clang-tidy/add_new_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import os
import re
import sys
import textwrap

# Adapts the module's CMakelist file. Returns 'True' if it could add a new
# entry and 'False' if the entry already existed.
Expand Down Expand Up @@ -53,7 +54,14 @@ def adapt_cmake(module_path, check_name_camel):


# Adds a header for the new check.
def write_header(module_path, module, namespace, check_name, check_name_camel):
def write_header(
module_path, module, namespace, check_name, check_name_camel, description
):
wrapped_desc = "\n".join(
textwrap.wrap(
description, width=80, initial_indent="/// ", subsequent_indent="/// "
)
)
filename = os.path.join(module_path, check_name_camel) + ".h"
print("Creating %s..." % filename)
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
Expand Down Expand Up @@ -85,7 +93,7 @@ def write_header(module_path, module, namespace, check_name, check_name_camel):
namespace clang::tidy::%(namespace)s {
/// FIXME: Write a short description.
%(description)s
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/%(module)s/%(check_name)s.html
Expand All @@ -107,6 +115,7 @@ class %(check_name_camel)s : public ClangTidyCheck {
"check_name": check_name,
"module": module,
"namespace": namespace,
"description": wrapped_desc,
}
)

Expand Down Expand Up @@ -235,7 +244,12 @@ def adapt_module(module_path, module, check_name, check_name_camel):


# Adds a release notes entry.
def add_release_notes(module_path, module, check_name):
def add_release_notes(module_path, module, check_name, description):
wrapped_desc = "\n".join(
textwrap.wrap(
description, width=80, initial_indent=" ", subsequent_indent=" "
)
)
check_name_dashes = module + "-" + check_name
filename = os.path.normpath(
os.path.join(module_path, "../../docs/ReleaseNotes.rst")
Expand Down Expand Up @@ -281,10 +295,10 @@ def add_release_notes(module_path, module, check_name):
"""- New :doc:`%s
<clang-tidy/checks/%s/%s>` check.
FIXME: add release notes.
%s
"""
% (check_name_dashes, module, check_name)
% (check_name_dashes, module, check_name, wrapped_desc)
)
note_added = True

Expand Down Expand Up @@ -552,8 +566,8 @@ def format_link_alias(doc_file):
f.write(' :header: "Name", "Offers fixes"\n\n')
f.writelines(checks)
# and the aliases
f.write("\n\n")
f.write(".. csv-table:: Aliases..\n")
f.write("\nCheck aliases\n-------------\n\n")
f.write(".. csv-table::\n")
f.write(' :header: "Name", "Redirect", "Offers fixes"\n\n')
f.writelines(checks_alias)
break
Expand Down Expand Up @@ -612,6 +626,13 @@ def main():
default="c++",
metavar="LANG",
)
parser.add_argument(
"--description",
"-d",
help="short description of what the check does",
default="FIXME: Write a short description",
type=str,
)
parser.add_argument(
"module",
nargs="?",
Expand Down Expand Up @@ -652,10 +673,16 @@ def main():
else:
namespace = module

write_header(module_path, module, namespace, check_name, check_name_camel)
description = args.description
if not description.endswith("."):
description += "."

write_header(
module_path, module, namespace, check_name, check_name_camel, description
)
write_implementation(module_path, module, namespace, check_name_camel)
adapt_module(module_path, module, check_name, check_name_camel)
add_release_notes(module_path, module, check_name)
add_release_notes(module_path, module, check_name, description)
test_extension = language_to_extension.get(args.language)
write_test(module_path, module, check_name, test_extension)
write_docs(module_path, module, check_name)
Expand Down
354 changes: 177 additions & 177 deletions clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp

Large diffs are not rendered by default.

17 changes: 8 additions & 9 deletions clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,12 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
// shall be run.
const auto FunctionScope =
functionDecl(
hasBody(
compoundStmt(forEachDescendant(
declStmt(containsAnyDeclaration(
LocalValDecl.bind("local-value")),
unless(has(decompositionDecl())))
.bind("decl-stmt")))
.bind("scope")))
hasBody(stmt(forEachDescendant(
declStmt(containsAnyDeclaration(
LocalValDecl.bind("local-value")),
unless(has(decompositionDecl())))
.bind("decl-stmt")))
.bind("scope")))
.bind("function-decl");

Finder->addMatcher(FunctionScope, this);
Expand All @@ -109,7 +108,7 @@ void ConstCorrectnessCheck::registerMatchers(MatchFinder *Finder) {
enum class VariableCategory { Value, Reference, Pointer };

void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
const auto *LocalScope = Result.Nodes.getNodeAs<CompoundStmt>("scope");
const auto *LocalScope = Result.Nodes.getNodeAs<Stmt>("scope");
const auto *Variable = Result.Nodes.getNodeAs<VarDecl>("local-value");
const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function-decl");

Expand Down Expand Up @@ -198,7 +197,7 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
}
}

void ConstCorrectnessCheck::registerScope(const CompoundStmt *LocalScope,
void ConstCorrectnessCheck::registerScope(const Stmt *LocalScope,
ASTContext *Context) {
auto &Analyzer = ScopesCache[LocalScope];
if (!Analyzer)
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class ConstCorrectnessCheck : public ClangTidyCheck {
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
void registerScope(const CompoundStmt *LocalScope, ASTContext *Context);
void registerScope(const Stmt *LocalScope, ASTContext *Context);

using MutationAnalyzer = std::unique_ptr<ExprMutationAnalyzer>;
llvm::DenseMap<const CompoundStmt *, MutationAnalyzer> ScopesCache;
llvm::DenseMap<const Stmt *, MutationAnalyzer> ScopesCache;
llvm::DenseSet<SourceLocation> TemplateDiagnosticsCache;

const bool AnalyzeValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,65 +119,72 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
}
}

const size_t Index = llvm::find(Function->parameters(), Param) -
Function->parameters().begin();
handleConstRefFix(*Function, *Param, *Result.Context);
}

void UnnecessaryValueParamCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
}

void UnnecessaryValueParamCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "AllowedTypes",
utils::options::serializeStringList(AllowedTypes));
}

void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {
MutationAnalyzerCache.clear();
}

void UnnecessaryValueParamCheck::handleConstRefFix(const FunctionDecl &Function,
const ParmVarDecl &Param,
ASTContext &Context) {
const size_t Index =
llvm::find(Function.parameters(), &Param) - Function.parameters().begin();
const bool IsConstQualified =
Param.getType().getCanonicalType().isConstQualified();

auto Diag =
diag(Param->getLocation(),
diag(Param.getLocation(),
"the %select{|const qualified }0parameter %1 is copied for each "
"invocation%select{ but only used as a const reference|}0; consider "
"making it a %select{const |}0reference")
<< IsConstQualified << paramNameOrIndex(Param->getName(), Index);
<< IsConstQualified << paramNameOrIndex(Param.getName(), Index);
// Do not propose fixes when:
// 1. the ParmVarDecl is in a macro, since we cannot place them correctly
// 2. the function is virtual as it might break overrides
// 3. the function is referenced outside of a call expression within the
// compilation unit as the signature change could introduce build errors.
// 4. the function is an explicit template/ specialization.
const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Function);
if (Param->getBeginLoc().isMacroID() || (Method && Method->isVirtual()) ||
isReferencedOutsideOfCallExpr(*Function, *Result.Context) ||
Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
const auto *Method = llvm::dyn_cast<CXXMethodDecl>(&Function);
if (Param.getBeginLoc().isMacroID() || (Method && Method->isVirtual()) ||
isReferencedOutsideOfCallExpr(Function, Context) ||
Function.getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
return;
for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
for (const auto *FunctionDecl = &Function; FunctionDecl != nullptr;
FunctionDecl = FunctionDecl->getPreviousDecl()) {
const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
*Result.Context);
Diag << utils::fixit::changeVarDeclToReference(CurrentParam, Context);
// The parameter of each declaration needs to be checked individually as to
// whether it is const or not as constness can differ between definition and
// declaration.
if (!CurrentParam.getType().getCanonicalType().isConstQualified()) {
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
CurrentParam, *Result.Context, DeclSpec::TQ::TQ_const))
CurrentParam, Context, DeclSpec::TQ::TQ_const))
Diag << *Fix;
}
}
}

void UnnecessaryValueParamCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
}

void UnnecessaryValueParamCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "AllowedTypes",
utils::options::serializeStringList(AllowedTypes));
}

void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {
MutationAnalyzerCache.clear();
}

void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Param,
const DeclRefExpr &CopyArgument,
const ASTContext &Context) {
ASTContext &Context) {
auto Diag = diag(CopyArgument.getBeginLoc(),
"parameter %0 is passed by value and only copied once; "
"consider moving it to avoid unnecessary copies")
<< &Var;
<< &Param;
// Do not propose fixes in macros since we cannot place them correctly.
if (CopyArgument.getBeginLoc().isMacroID())
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ class UnnecessaryValueParamCheck : public ClangTidyCheck {
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void onEndOfTranslationUnit() override;

private:
void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
const ASTContext &Context);
protected:
// Create diagnostics. These are virtual so that derived classes can change
// behaviour.
virtual void handleMoveFix(const ParmVarDecl &Param,
const DeclRefExpr &CopyArgument,
ASTContext &Context);
virtual void handleConstRefFix(const FunctionDecl &Function,
const ParmVarDecl &Param, ASTContext &Context);

private:
ExprMutationAnalyzer::Memoized MutationAnalyzerCache;
utils::IncludeInserter Inserter;
const std::vector<StringRef> AllowedTypes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,12 @@ void NonConstParameterCheck::diagnoseNonConstParameters() {
if (!Function)
continue;
unsigned Index = Par->getFunctionScopeIndex();
for (FunctionDecl *FnDecl : Function->redecls())
for (FunctionDecl *FnDecl : Function->redecls()) {
if (FnDecl->getNumParams() <= Index)
continue;
Fixes.push_back(FixItHint::CreateInsertion(
FnDecl->getParamDecl(Index)->getBeginLoc(), "const "));
}

diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
<< Par->getName() << Fixes;
Expand Down
323 changes: 183 additions & 140 deletions clang-tools-extra/clang-tidy/tool/run-clang-tidy.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt,
if (FirstStmt == SecondStmt)
return true;

if (FirstStmt->getStmtClass() != FirstStmt->getStmtClass())
if (FirstStmt->getStmtClass() != SecondStmt->getStmtClass())
return false;

if (isa<Expr>(FirstStmt) && isa<Expr>(SecondStmt)) {
Expand Down
10 changes: 5 additions & 5 deletions clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ bool isStandardPointerConvertible(QualType From, QualType To) {
if (RD->isCompleteDefinition() &&
isBaseOf(From->getPointeeType().getTypePtr(),
To->getPointeeType().getTypePtr())) {
return true;
// If B is an inaccessible or ambiguous base class of D, a program
// that necessitates this conversion is ill-formed
return isUnambiguousPublicBaseClass(From->getPointeeType().getTypePtr(),
To->getPointeeType().getTypePtr());
}
}

Expand Down Expand Up @@ -375,10 +378,7 @@ bool ExceptionAnalyzer::ExceptionInfo::filterByCatch(
isPointerOrPointerToMember(ExceptionCanTy->getTypePtr())) {
// A standard pointer conversion not involving conversions to pointers to
// private or protected or ambiguous classes ...
if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy) &&
isUnambiguousPublicBaseClass(
ExceptionCanTy->getTypePtr()->getPointeeType().getTypePtr(),
HandlerCanTy->getTypePtr()->getPointeeType().getTypePtr())) {
if (isStandardPointerConvertible(ExceptionCanTy, HandlerCanTy)) {
TypesToDelete.push_back(ExceptionTy);
}
// A function pointer conversion ...
Expand Down
64 changes: 32 additions & 32 deletions clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,6 @@ static constexpr const char ArgName[] = "ArgName";

namespace clang::tidy::utils {

static bool operator==(const UseRangesCheck::Indexes &L,
const UseRangesCheck::Indexes &R) {
return std::tie(L.BeginArg, L.EndArg, L.ReplaceArg) ==
std::tie(R.BeginArg, R.EndArg, R.ReplaceArg);
}

static std::string getFullPrefix(ArrayRef<UseRangesCheck::Indexes> Signature) {
std::string Output;
llvm::raw_string_ostream OS(Output);
Expand All @@ -54,15 +48,6 @@ static std::string getFullPrefix(ArrayRef<UseRangesCheck::Indexes> Signature) {
return Output;
}

static llvm::hash_code hash_value(const UseRangesCheck::Indexes &Indexes) {
return llvm::hash_combine(Indexes.BeginArg, Indexes.EndArg,
Indexes.ReplaceArg);
}

static llvm::hash_code hash_value(const UseRangesCheck::Signature &Sig) {
return llvm::hash_combine_range(Sig.begin(), Sig.end());
}

namespace {

AST_MATCHER(Expr, hasSideEffects) {
Expand Down Expand Up @@ -123,32 +108,34 @@ makeMatcherPair(StringRef State, const UseRangesCheck::Indexes &Indexes,
}

void UseRangesCheck::registerMatchers(MatchFinder *Finder) {
Replaces = getReplacerMap();
auto Replaces = getReplacerMap();
ReverseDescriptor = getReverseDescriptor();
auto BeginEndNames = getFreeBeginEndMethods();
llvm::SmallVector<StringRef, 4> BeginNames{
llvm::make_first_range(BeginEndNames)};
llvm::SmallVector<StringRef, 4> EndNames{
llvm::make_second_range(BeginEndNames)};
llvm::DenseSet<ArrayRef<Signature>> Seen;
Replacers.clear();
llvm::DenseSet<Replacer *> SeenRepl;
for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
const ArrayRef<Signature> &Signatures =
I->getValue()->getReplacementSignatures();
if (!Seen.insert(Signatures).second)
auto Replacer = I->getValue();
if (!SeenRepl.insert(Replacer.get()).second)
continue;
assert(!Signatures.empty() &&
llvm::all_of(Signatures, [](auto Index) { return !Index.empty(); }));
Replacers.push_back(Replacer);
assert(!Replacer->getReplacementSignatures().empty() &&
llvm::all_of(Replacer->getReplacementSignatures(),
[](auto Index) { return !Index.empty(); }));
std::vector<StringRef> Names(1, I->getKey());
for (auto J = std::next(I); J != E; ++J)
if (J->getValue()->getReplacementSignatures() == Signatures)
if (J->getValue() == Replacer)
Names.push_back(J->getKey());

std::vector<ast_matchers::internal::DynTypedMatcher> TotalMatchers;
// As we match on the first matched signature, we need to sort the
// signatures in order of length(longest to shortest). This way any
// signature that is a subset of another signature will be matched after the
// other.
SmallVector<Signature> SigVec(Signatures);
SmallVector<Signature> SigVec(Replacer->getReplacementSignatures());
llvm::sort(SigVec, [](auto &L, auto &R) { return R.size() < L.size(); });
for (const auto &Signature : SigVec) {
std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
Expand All @@ -163,7 +150,8 @@ void UseRangesCheck::registerMatchers(MatchFinder *Finder) {
}
Finder->addMatcher(
callExpr(
callee(functionDecl(hasAnyName(std::move(Names))).bind(FuncDecl)),
callee(functionDecl(hasAnyName(std::move(Names)))
.bind((FuncDecl + Twine(Replacers.size() - 1).str()))),
ast_matchers::internal::DynTypedMatcher::constructVariadic(
ast_matchers::internal::DynTypedMatcher::VO_AnyOf,
ASTNodeKind::getFromNodeKind<CallExpr>(),
Expand Down Expand Up @@ -205,21 +193,33 @@ static void removeFunctionArgs(DiagnosticBuilder &Diag, const CallExpr &Call,
}

void UseRangesCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(FuncDecl);
std::string Qualified = "::" + Function->getQualifiedNameAsString();
auto Iter = Replaces.find(Qualified);
assert(Iter != Replaces.end());
Replacer *Replacer = nullptr;
const FunctionDecl *Function = nullptr;
for (auto [Node, Value] : Result.Nodes.getMap()) {
StringRef NodeStr(Node);
if (!NodeStr.consume_front(FuncDecl))
continue;
Function = Value.get<FunctionDecl>();
size_t Index;
if (NodeStr.getAsInteger(10, Index)) {
llvm_unreachable("Unable to extract replacer index");
}
assert(Index < Replacers.size());
Replacer = Replacers[Index].get();
break;
}
assert(Replacer && Function);
SmallString<64> Buffer;
for (const Signature &Sig : Iter->getValue()->getReplacementSignatures()) {
for (const Signature &Sig : Replacer->getReplacementSignatures()) {
Buffer.assign({BoundCall, getFullPrefix(Sig)});
const auto *Call = Result.Nodes.getNodeAs<CallExpr>(Buffer);
if (!Call)
continue;
auto Diag = createDiag(*Call);
if (auto ReplaceName = Iter->getValue()->getReplaceName(*Function))
if (auto ReplaceName = Replacer->getReplaceName(*Function))
Diag << FixItHint::CreateReplacement(Call->getCallee()->getSourceRange(),
*ReplaceName);
if (auto Include = Iter->getValue()->getHeaderInclusion(*Function))
if (auto Include = Replacer->getHeaderInclusion(*Function))
Diag << Inserter.createIncludeInsertion(
Result.SourceManager->getFileID(Call->getBeginLoc()), *Include);
llvm::SmallVector<unsigned, 3> ToRemove;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/utils/UseRangesCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class UseRangesCheck : public ClangTidyCheck {
std::optional<TraversalKind> getCheckTraversalKind() const override;

private:
ReplacerMap Replaces;
std::vector<llvm::IntrusiveRefCntPtr<Replacer>> Replacers;
std::optional<ReverseIteratorDescriptor> ReverseDescriptor;
IncludeInserter Inserter;
};
Expand Down
20 changes: 20 additions & 0 deletions clang-tools-extra/clangd/IncludeCleaner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,26 @@ computeIncludeCleanerFindings(ParsedAST &AST, bool AnalyzeAngledIncludes) {
Ref.RT != include_cleaner::RefType::Explicit)
return;

// Check if we have any headers with the same spelling, in edge cases
// like `#include_next "foo.h"`, the user can't ever include the
// physical foo.h, but can have a spelling that refers to it.
// We postpone this check because spelling a header for every usage is
// expensive.
std::string Spelling = include_cleaner::spellHeader(
{Providers.front(), AST.getPreprocessor().getHeaderSearchInfo(),
MainFile});
for (auto *Inc :
ConvertedIncludes.match(include_cleaner::Header{Spelling})) {
Satisfied = true;
auto HeaderID =
AST.getIncludeStructure().getID(&Inc->Resolved->getFileEntry());
assert(HeaderID.has_value() &&
"ConvertedIncludes only contains resolved includes.");
Used.insert(*HeaderID);
}
if (Satisfied)
return;

// We actually always want to map usages to their spellings, but
// spelling locations can point into preamble section. Using these
// offsets could lead into crashes in presence of stale preambles. Hence
Expand Down
22 changes: 22 additions & 0 deletions clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,28 @@ TEST(IncludeCleaner, ResourceDirIsIgnored) {
EXPECT_THAT(Findings.MissingIncludes, IsEmpty());
}

TEST(IncludeCleaner, DifferentHeaderSameSpelling) {
// `foo` is declared in foo_inner/foo.h, but there's no way to spell it
// directly. Make sure we don't generate unusued/missing include findings in
// such cases.
auto TU = TestTU::withCode(R"cpp(
#include <foo.h>
void baz() {
foo();
}
)cpp");
TU.AdditionalFiles["foo/foo.h"] = guard("#include_next <foo.h>");
TU.AdditionalFiles["foo_inner/foo.h"] = guard(R"cpp(
void foo();
)cpp");
TU.ExtraArgs.push_back("-Ifoo");
TU.ExtraArgs.push_back("-Ifoo_inner");

auto AST = TU.build();
auto Findings = computeIncludeCleanerFindings(AST);
EXPECT_THAT(Findings.UnusedIncludes, IsEmpty());
EXPECT_THAT(Findings.MissingIncludes, IsEmpty());
}
} // namespace
} // namespace clangd
} // namespace clang
421 changes: 0 additions & 421 deletions clang-tools-extra/docs/ReleaseNotes.rst

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
.. title:: clang-tidy - clang-analyzer-cplusplus.Move
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-move

clang-analyzer-cplusplus.Move
=============================

Find use-after-move bugs in C++.

The clang-analyzer-cplusplus.Move check is an alias of
Clang Static Analyzer cplusplus.Move.
The `clang-analyzer-cplusplus.Move` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-move>`_
for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.. title:: clang-tidy - clang-analyzer-optin.taint.TaintedAlloc
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#optin-taint-taintedalloc

clang-analyzer-optin.taint.TaintedAlloc
=======================================

Check for memory allocations, where the size parameter might be a tainted
(attacker controlled) value.

The `clang-analyzer-optin.taint.TaintedAlloc` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#optin-taint-taintedalloc>`_
for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. title:: clang-tidy - clang-analyzer-security.PutenvStackArray

clang-analyzer-security.PutenvStackArray
========================================

Finds calls to the function 'putenv' which pass a pointer to an automatic
(stack-allocated) array as the argument.

The clang-analyzer-security.PutenvStackArray check is an alias of
Clang Static Analyzer security.PutenvStackArray.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. title:: clang-tidy - clang-analyzer-unix.BlockInCriticalSection
.. meta::
:http-equiv=refresh: 5;URL=https://clang.llvm.org/docs/analyzer/checkers.html#unix-blockincriticalsection

clang-analyzer-unix.BlockInCriticalSection
==========================================

Check for calls to blocking functions inside a critical section.

The `clang-analyzer-unix.BlockInCriticalSection` check is an alias, please see
`Clang Static Analyzer Available Checkers
<https://clang.llvm.org/docs/analyzer/checkers.html#unix-blockincriticalsection>`_
for more information.
10 changes: 7 additions & 3 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Clang-Tidy Checks
:doc:`bugprone-not-null-terminated-result <bugprone/not-null-terminated-result>`, "Yes"
:doc:`bugprone-optional-value-conversion <bugprone/optional-value-conversion>`, "Yes"
:doc:`bugprone-parent-virtual-call <bugprone/parent-virtual-call>`, "Yes"
:doc:`bugprone-pointer-arithmetic-on-polymorphic-object <bugprone/pointer-arithmetic-on-polymorphic-object>`,
:doc:`bugprone-posix-return <bugprone/posix-return>`, "Yes"
:doc:`bugprone-redundant-branch-condition <bugprone/redundant-branch-condition>`, "Yes"
:doc:`bugprone-reserved-identifier <bugprone/reserved-identifier>`, "Yes"
Expand Down Expand Up @@ -158,7 +159,6 @@ Clang-Tidy Checks
:doc:`bugprone-unused-raii <bugprone/unused-raii>`, "Yes"
:doc:`bugprone-unused-return-value <bugprone/unused-return-value>`,
:doc:`bugprone-use-after-move <bugprone/use-after-move>`,
:doc:`bugprone-pointer-arithmetic-on-polymorphic-object <bugprone/pointer-arithmetic-on-polymorphic-object>`,
:doc:`bugprone-virtual-near-miss <bugprone/virtual-near-miss>`, "Yes"
:doc:`cert-dcl50-cpp <cert/dcl50-cpp>`,
:doc:`cert-dcl58-cpp <cert/dcl58-cpp>`,
Expand Down Expand Up @@ -269,7 +269,7 @@ Clang-Tidy Checks
:doc:`misc-unused-parameters <misc/unused-parameters>`, "Yes"
:doc:`misc-unused-using-decls <misc/unused-using-decls>`, "Yes"
:doc:`misc-use-anonymous-namespace <misc/use-anonymous-namespace>`,
:doc:`misc-use-internal-linkage <misc/use-internal-linkage>`,
:doc:`misc-use-internal-linkage <misc/use-internal-linkage>`, "Yes"
:doc:`modernize-avoid-bind <modernize/avoid-bind>`, "Yes"
:doc:`modernize-avoid-c-arrays <modernize/avoid-c-arrays>`,
:doc:`modernize-concat-nested-namespaces <modernize/concat-nested-namespaces>`, "Yes"
Expand Down Expand Up @@ -409,6 +409,7 @@ Check aliases
:doc:`bugprone-narrowing-conversions <bugprone/narrowing-conversions>`, :doc:`cppcoreguidelines-narrowing-conversions <cppcoreguidelines/narrowing-conversions>`,
:doc:`cert-con36-c <cert/con36-c>`, :doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`,
:doc:`cert-con54-cpp <cert/con54-cpp>`, :doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`,
:doc:`cert-ctr56-cpp <cert/ctr56-cpp>`, :doc:`bugprone-pointer-arithmetic-on-polymorphic-object <bugprone/pointer-arithmetic-on-polymorphic-object>`,
:doc:`cert-dcl03-c <cert/dcl03-c>`, :doc:`misc-static-assert <misc/static-assert>`, "Yes"
:doc:`cert-dcl16-c <cert/dcl16-c>`, :doc:`readability-uppercase-literal-suffix <readability/uppercase-literal-suffix>`, "Yes"
:doc:`cert-dcl37-c <cert/dcl37-c>`, :doc:`bugprone-reserved-identifier <bugprone/reserved-identifier>`, "Yes"
Expand Down Expand Up @@ -448,7 +449,7 @@ Check aliases
:doc:`clang-analyzer-core.uninitialized.UndefReturn <clang-analyzer/core.uninitialized.UndefReturn>`, `Clang Static Analyzer core.uninitialized.UndefReturn <https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-undefreturn>`_,
:doc:`clang-analyzer-cplusplus.ArrayDelete <clang-analyzer/cplusplus.ArrayDelete>`, `Clang Static Analyzer cplusplus.ArrayDelete <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete>`_,
:doc:`clang-analyzer-cplusplus.InnerPointer <clang-analyzer/cplusplus.InnerPointer>`, `Clang Static Analyzer cplusplus.InnerPointer <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-innerpointer>`_,
:doc:`clang-analyzer-cplusplus.Move <clang-analyzer/cplusplus.Move>`, Clang Static Analyzer cplusplus.Move,
:doc:`clang-analyzer-cplusplus.Move <clang-analyzer/cplusplus.Move>`, `Clang Static Analyzer cplusplus.Move <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-move>`_,
:doc:`clang-analyzer-cplusplus.NewDelete <clang-analyzer/cplusplus.NewDelete>`, `Clang Static Analyzer cplusplus.NewDelete <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-newdelete>`_,
:doc:`clang-analyzer-cplusplus.NewDeleteLeaks <clang-analyzer/cplusplus.NewDeleteLeaks>`, `Clang Static Analyzer cplusplus.NewDeleteLeaks <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-newdeleteleaks>`_,
:doc:`clang-analyzer-cplusplus.PlacementNew <clang-analyzer/cplusplus.PlacementNew>`, `Clang Static Analyzer cplusplus.PlacementNew <https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-placementnew>`_,
Expand All @@ -471,6 +472,7 @@ Check aliases
:doc:`clang-analyzer-optin.performance.GCDAntipattern <clang-analyzer/optin.performance.GCDAntipattern>`, `Clang Static Analyzer optin.performance.GCDAntipattern <https://clang.llvm.org/docs/analyzer/checkers.html#optin-performance-gcdantipattern>`_,
:doc:`clang-analyzer-optin.performance.Padding <clang-analyzer/optin.performance.Padding>`, `Clang Static Analyzer optin.performance.Padding <https://clang.llvm.org/docs/analyzer/checkers.html#optin-performance-padding>`_,
:doc:`clang-analyzer-optin.portability.UnixAPI <clang-analyzer/optin.portability.UnixAPI>`, `Clang Static Analyzer optin.portability.UnixAPI <https://clang.llvm.org/docs/analyzer/checkers.html#optin-portability-unixapi>`_,
:doc:`clang-analyzer-optin.taint.TaintedAlloc <clang-analyzer/optin.taint.TaintedAlloc>`, `Clang Static Analyzer optin.taint.TaintedAlloc <https://clang.llvm.org/docs/analyzer/checkers.html#optin-taint-taintedalloc>`_,
:doc:`clang-analyzer-osx.API <clang-analyzer/osx.API>`, `Clang Static Analyzer osx.API <https://clang.llvm.org/docs/analyzer/checkers.html#osx-api>`_,
:doc:`clang-analyzer-osx.MIG <clang-analyzer/osx.MIG>`, Clang Static Analyzer osx.MIG,
:doc:`clang-analyzer-osx.NumberObjectConversion <clang-analyzer/osx.NumberObjectConversion>`, `Clang Static Analyzer osx.NumberObjectConversion <https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion>`_,
Expand Down Expand Up @@ -501,6 +503,7 @@ Check aliases
:doc:`clang-analyzer-osx.coreFoundation.containers.OutOfBounds <clang-analyzer/osx.coreFoundation.containers.OutOfBounds>`, `Clang Static Analyzer osx.coreFoundation.containers.OutOfBounds <https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-outofbounds>`_,
:doc:`clang-analyzer-osx.coreFoundation.containers.PointerSizedValues <clang-analyzer/osx.coreFoundation.containers.PointerSizedValues>`, `Clang Static Analyzer osx.coreFoundation.containers.PointerSizedValues <https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-pointersizedvalues>`_,
:doc:`clang-analyzer-security.FloatLoopCounter <clang-analyzer/security.FloatLoopCounter>`, `Clang Static Analyzer security.FloatLoopCounter <https://clang.llvm.org/docs/analyzer/checkers.html#security-floatloopcounter>`_,
:doc:`clang-analyzer-security.PutenvStackArray <clang-analyzer/security.PutenvStackArray>`, Clang Static Analyzer security.PutenvStackArray,
:doc:`clang-analyzer-security.SetgidSetuidOrder <clang-analyzer/security.SetgidSetuidOrder>`, Clang Static Analyzer security.SetgidSetuidOrder,
:doc:`clang-analyzer-security.cert.env.InvalidPtr <clang-analyzer/security.cert.env.InvalidPtr>`, `Clang Static Analyzer security.cert.env.InvalidPtr <https://clang.llvm.org/docs/analyzer/checkers.html#security-cert-env-invalidptr>`_,
:doc:`clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling <clang-analyzer/security.insecureAPI.DeprecatedOrUnsafeBufferHandling>`, `Clang Static Analyzer security.insecureAPI.DeprecatedOrUnsafeBufferHandling <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-deprecatedorunsafebufferhandling>`_,
Expand All @@ -517,6 +520,7 @@ Check aliases
:doc:`clang-analyzer-security.insecureAPI.strcpy <clang-analyzer/security.insecureAPI.strcpy>`, `Clang Static Analyzer security.insecureAPI.strcpy <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-strcpy>`_,
:doc:`clang-analyzer-security.insecureAPI.vfork <clang-analyzer/security.insecureAPI.vfork>`, `Clang Static Analyzer security.insecureAPI.vfork <https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-vfork>`_,
:doc:`clang-analyzer-unix.API <clang-analyzer/unix.API>`, `Clang Static Analyzer unix.API <https://clang.llvm.org/docs/analyzer/checkers.html#unix-api>`_,
:doc:`clang-analyzer-unix.BlockInCriticalSection <clang-analyzer/unix.BlockInCriticalSection>`, `Clang Static Analyzer unix.BlockInCriticalSection <https://clang.llvm.org/docs/analyzer/checkers.html#unix-blockincriticalsection>`_,
:doc:`clang-analyzer-unix.Errno <clang-analyzer/unix.Errno>`, `Clang Static Analyzer unix.Errno <https://clang.llvm.org/docs/analyzer/checkers.html#unix-errno>`_,
:doc:`clang-analyzer-unix.Malloc <clang-analyzer/unix.Malloc>`, `Clang Static Analyzer unix.Malloc <https://clang.llvm.org/docs/analyzer/checkers.html#unix-malloc>`_,
:doc:`clang-analyzer-unix.MallocSizeof <clang-analyzer/unix.MallocSizeof>`, `Clang Static Analyzer unix.MallocSizeof <https://clang.llvm.org/docs/analyzer/checkers.html#unix-mallocsizeof>`_,
Expand Down
17 changes: 14 additions & 3 deletions clang-tools-extra/include-cleaner/lib/Analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,20 @@ analyze(llvm::ArrayRef<Decl *> ASTRoots,
}
if (!Satisfied && !Providers.empty() &&
Ref.RT == RefType::Explicit &&
!HeaderFilter(Providers.front().resolvedPath()))
Missing.insert(spellHeader(
{Providers.front(), PP.getHeaderSearchInfo(), MainFile}));
!HeaderFilter(Providers.front().resolvedPath())) {
// Check if we have any headers with the same spelling, in edge
// cases like `#include_next "foo.h"`, the user can't ever
// include the physical foo.h, but can have a spelling that
// refers to it.
auto Spelling = spellHeader(
{Providers.front(), PP.getHeaderSearchInfo(), MainFile});
for (const Include *I : Inc.match(Header{Spelling})) {
Used.insert(I);
Satisfied = true;
}
if (!Satisfied)
Missing.insert(std::move(Spelling));
}
});

AnalysisResults Results;
Expand Down
26 changes: 26 additions & 0 deletions clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "clang-include-cleaner/Record.h"
#include "clang-include-cleaner/Types.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclBase.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
Expand Down Expand Up @@ -296,6 +297,31 @@ TEST_F(AnalyzeTest, ResourceDirIsIgnored) {
EXPECT_THAT(Results.Missing, testing::IsEmpty());
}

TEST_F(AnalyzeTest, DifferentHeaderSameSpelling) {
Inputs.ExtraArgs.push_back("-Ifoo");
Inputs.ExtraArgs.push_back("-Ifoo_inner");
// `foo` is declared in foo_inner/foo.h, but there's no way to spell it
// directly. Make sure we don't generate unusued/missing include findings in
// such cases.
Inputs.Code = R"cpp(
#include <foo.h>
void baz() {
foo();
}
)cpp";
Inputs.ExtraFiles["foo/foo.h"] = guard("#include_next <foo.h>");
Inputs.ExtraFiles["foo_inner/foo.h"] = guard(R"cpp(
void foo();
)cpp");
TestAST AST(Inputs);
std::vector<Decl *> DeclsInTU;
for (auto *D : AST.context().getTranslationUnitDecl()->decls())
DeclsInTU.push_back(D);
auto Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor());
EXPECT_THAT(Results.Unused, testing::IsEmpty());
EXPECT_THAT(Results.Missing, testing::IsEmpty());
}

TEST(FixIncludes, Basic) {
llvm::StringRef Code = R"cpp(#include "d.h"
#include "a.h"
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/test/clang-doc/basic-project.test
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// See https://github.com/llvm/llvm-project/issues/97507.
// UNSUPPORTED: target={{.*}}

// RUN: rm -rf %t && mkdir -p %t/docs %t/build
// RUN: sed 's|$test_dir|%/S|g' %S/Inputs/basic-project/database_template.json > %t/build/compile_commands.json
// RUN: clang-doc --format=html --output=%t/docs --executor=all-TUs %t/build/compile_commands.json
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/test/clang-doc/test-path-abs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: rm -rf %t && mkdir %t
// RUN: clang-doc --format=html --executor=standalone %s --output=%t
// RUN: FileCheck %s -input-file=%t/index_json.js -check-prefix=JSON-INDEX
// RUN: rm -rf %t

// JSON-INDEX: var RootPath = "{{.*}}test-path-abs.cpp.tmp";
Original file line number Diff line number Diff line change
Expand Up @@ -756,3 +756,21 @@ struct test_implicit_throw {
};

}}

void pointer_exception_can_not_escape_with_const_void_handler() noexcept {
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'pointer_exception_can_not_escape_with_const_void_handler' which should not throw exceptions
const int value = 42;
try {
throw &value;
} catch (const void *) {
}
}

void pointer_exception_can_not_escape_with_void_handler() noexcept {
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'pointer_exception_can_not_escape_with_void_handler' which should not throw exceptions
int value = 42;
try {
throw &value;
} catch (void *) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// RUN: misc-const-correctness.TransformValues: true, \
// RUN: misc-const-correctness.WarnPointersAsValues: false, \
// RUN: misc-const-correctness.TransformPointersAsValues: false \
// RUN: }}" -- -fno-delayed-template-parsing
// RUN: }}" -- -fno-delayed-template-parsing -fexceptions

// ------- Provide test samples for primitive builtins ---------
// - every 'p_*' variable is a 'potential_const_*' variable
Expand Down Expand Up @@ -56,6 +56,15 @@ void some_function(double np_arg0, wchar_t np_arg1) {
np_local6--;
}

int function_try_block() try {
int p_local0 = 0;
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
// CHECK-FIXES: int const p_local0
return p_local0;
} catch (...) {
return 0;
}

void nested_scopes() {
int p_local0 = 2;
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
Expand Down
Loading