22 changes: 10 additions & 12 deletions bolt/lib/Core/DIEBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Timer.h"

#include <algorithm>
#include <cstdint>
Expand All @@ -39,8 +38,7 @@
#define DEBUG_TYPE "bolt"
namespace opts {
extern cl::opt<unsigned> Verbosity;
extern cl::opt<bool> TimeDebug;
} // namespace opts
}
namespace llvm {
namespace bolt {

Expand Down Expand Up @@ -351,8 +349,6 @@ void DIEBuilder::buildCompileUnits(const bool Init) {
}
}
void DIEBuilder::buildCompileUnits(const std::vector<DWARFUnit *> &CUs) {
NamedRegionTimer T("buildcompileunits", "build compile units", "debug",
"update debug info", opts::TimeDebug);
BuilderState.reset(new State());
// Allocating enough for current batch being processed.
// In real use cases we either processing a batch of CUs with no cross
Expand Down Expand Up @@ -560,8 +556,6 @@ void DIEBuilder::populateDebugNamesTable(
}

void DIEBuilder::updateDebugNamesTable() {
NamedRegionTimer T("updatedebugnames", "update debug_names table",
"debug", "update debug info", opts::TimeDebug);
auto finalizeDebugNamesTableForCU = [&](DWARFUnit &CU,
uint64_t &UnitStartOffset) -> void {
DIE *UnitDIE = getUnitDIEbyUnit(CU);
Expand All @@ -572,14 +566,18 @@ void DIEBuilder::updateDebugNamesTable() {
UnitStartOffset += CurUnitInfo.UnitLength;
};

auto It = llvm::partition_point(getState().DUList, [](DWARFUnit *CU) {
return CU->getVersion() < 5 && CU->isTypeUnit();
});
uint64_t TypeUnitStartOffset = 0;
for (DWARFUnit *CU : llvm::make_range(getState().DUList.begin(), It))
for (DWARFUnit *CU : getState().DUList) {
if (!(CU->getVersion() < 5 && CU->isTypeUnit()))
break;
finalizeDebugNamesTableForCU(*CU, TypeUnitStartOffset);
for (DWARFUnit *CU : llvm::make_range(It, getState().DUList.end()))
}

for (DWARFUnit *CU : getState().DUList) {
if (CU->getVersion() < 5 && CU->isTypeUnit())
continue;
finalizeDebugNamesTableForCU(*CU, DebugNamesUnitSize);
}
updateReferences();
}

Expand Down
7 changes: 0 additions & 7 deletions bolt/lib/Core/DebugNames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,11 @@
#include "bolt/Core/BinaryContext.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Timer.h"
#include <cstdint>
#include <optional>

namespace opts {
extern llvm::cl::opt<bool> TimeDebug;
} // namespace opts
namespace llvm {
namespace bolt {
DWARF5AcceleratorTable::DWARF5AcceleratorTable(
Expand Down Expand Up @@ -745,8 +740,6 @@ void DWARF5AcceleratorTable::emitAugmentationString() const {
void DWARF5AcceleratorTable::emitAccelTable() {
if (!NeedToCreate)
return;
NamedRegionTimer T("emitAccelTable", "Emit Accelerator Table",
"debug", "Update Debug Info", opts::TimeDebug);
finalize();
populateAbbrevsMap();
writeEntries();
Expand Down
4 changes: 1 addition & 3 deletions bolt/lib/Core/FunctionLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ FunctionFragment::const_iterator FunctionFragment::end() const {
return const_iterator(Layout->block_begin() + StartIndex + Size);
}

BinaryBasicBlock *FunctionFragment::front() const { return *begin(); }

BinaryBasicBlock *FunctionFragment::back() const { return *std::prev(end()); }
const BinaryBasicBlock *FunctionFragment::front() const { return *begin(); }

FunctionLayout::FunctionLayout() { addFragment(); }

Expand Down
7 changes: 0 additions & 7 deletions bolt/lib/Core/GDBIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@
//===----------------------------------------------------------------------===//

#include "bolt/Core/GDBIndex.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Timer.h"

namespace opts {
extern llvm::cl::opt<bool> TimeDebug;
} // namespace opts
using namespace llvm::bolt;
using namespace llvm::support::endian;

Expand All @@ -28,8 +23,6 @@ void GDBIndex::updateGdbIndexSection(
DebugARangesSectionWriter &ARangesSectionWriter) {
if (!BC.getGdbIndexSection())
return;
NamedRegionTimer T("updateGdbIndex", "Update gdb_index Section",
"debug", "Update Debug Info", opts::TimeDebug);
// See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html
// for .gdb_index section format.

Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Passes/ADRRelaxationPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) {
BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol);
if (TargetBF == &BF && !BB.isSplit())
continue;

// No relaxation needed if ADR references a basic block in the same
// fragment.
if (BinaryBasicBlock *TargetBB = BF.getBasicBlockForLabel(Symbol))
Expand Down
2 changes: 0 additions & 2 deletions bolt/lib/Passes/BinaryPasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1264,8 +1264,6 @@ Error AssignSections::runOnFunctions(BinaryContext &BC) {
if (opts::isHotTextMover(Function)) {
Function.setCodeSectionName(BC.getHotTextMoverSectionName());
Function.setColdCodeSectionName(BC.getHotTextMoverSectionName());
// TODO: find a better place to mark a function as a mover.
Function.setHotTextMover(true);
continue;
}

Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Passes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_llvm_library(LLVMBOLTPasses
PatchEntries.cpp
PettisAndHansen.cpp
PLTCall.cpp
ContinuityStats.cpp
RegAnalysis.cpp
RegReAssign.cpp
ReorderAlgorithm.cpp
Expand Down
250 changes: 250 additions & 0 deletions bolt/lib/Passes/ContinuityStats.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
//===- bolt/Passes/ContinuityStats.cpp --------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the continuity stats calculation pass.
//
//===----------------------------------------------------------------------===//

#include "bolt/Passes/ContinuityStats.h"
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "llvm/Support/CommandLine.h"
#include <queue>
#include <unordered_map>
#include <unordered_set>

#define DEBUG_TYPE "bolt-opts"

using namespace llvm;
using namespace bolt;

namespace opts {
extern cl::opt<unsigned> Verbosity;
cl::opt<unsigned> NumFunctionsForContinuityCheck(
"num-functions-for-continuity-check",
cl::desc("number of hottest functions to print aggregated "
"CFG discontinuity stats of."),
cl::init(1000), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltOptCategory));
} // namespace opts

namespace {
using FunctionListType = std::vector<const BinaryFunction *>;
using function_iterator = FunctionListType::iterator;

template <typename T>
void printDistribution(raw_ostream &OS, std::vector<T> &values,
bool Fraction = false) {
if (values.empty())
return;
// Sort values from largest to smallest and print the MAX, TOP 1%, 5%, 10%,
// 20%, 50%, 80%, MIN. If Fraction is true, then values are printed as
// fractions instead of integers.
std::sort(values.begin(), values.end());

auto printLine = [&](std::string Text, double Percent) {
int Rank = int(values.size() * (1.0 - Percent / 100));
if (Percent == 0)
Rank = values.size() - 1;
if (Fraction)
OS << " " << Text << std::string(9 - Text.length(), ' ') << ": "
<< format("%.2lf%%", values[Rank] * 100) << "\n";
else
OS << " " << Text << std::string(9 - Text.length(), ' ') << ": "
<< values[Rank] << "\n";
};

printLine("MAX", 0);
const int percentages[] = {1, 5, 10, 20, 50, 80};
for (size_t i = 0; i < sizeof(percentages) / sizeof(percentages[0]); ++i) {
printLine("TOP " + std::to_string(percentages[i]) + "%", percentages[i]);
}
printLine("MIN", 100);
}

void printCFGContinuityStats(raw_ostream &OS,
iterator_range<function_iterator> &Functions) {
// Given a perfect profile, every positive-execution-count BB should be
// connected to an entry of the function through a positive-execution-count
// directed path in the control flow graph.
std::vector<size_t> NumUnreachables;
std::vector<size_t> SumECUnreachables;
std::vector<double> FractionECUnreachables;

for (auto it = Functions.begin(); it != Functions.end(); ++it) {
const BinaryFunction *Function = *it;
if (Function->size() <= 1)
continue;

// Compute the sum of all BB execution counts (ECs).
size_t NumPosECBBs = 0;
size_t SumAllBBEC = 0;
for (const BinaryBasicBlock &BB : *Function) {
const size_t BBEC = BB.getKnownExecutionCount();
NumPosECBBs += BBEC > 0 ? 1 : 0;
SumAllBBEC += BBEC;
}

// Perform BFS on subgraph of CFG induced by positive weight edges.
// Compute the number of BBs reachable from the entry(s) of the function and
// the sum of their execution counts (ECs).
std::unordered_map<unsigned, const BinaryBasicBlock *> IndexToBB;
std::unordered_set<unsigned> Visited;
std::queue<unsigned> Queue;
for (const BinaryBasicBlock &BB : *Function) {
// Make sure BB.getIndex() is not already in IndexToBB.
assert(IndexToBB.find(BB.getIndex()) == IndexToBB.end());
IndexToBB[BB.getIndex()] = &BB;
if (BB.isEntryPoint() && BB.getKnownExecutionCount() > 0) {
Queue.push(BB.getIndex());
Visited.insert(BB.getIndex());
}
}
while (!Queue.empty()) {
const unsigned BBIndex = Queue.front();
const BinaryBasicBlock *BB = IndexToBB[BBIndex];
Queue.pop();
auto SuccBIIter = BB->branch_info_begin();
for (const BinaryBasicBlock *Succ : BB->successors()) {
const uint64_t Count = SuccBIIter->Count;
if (Count == BinaryBasicBlock::COUNT_NO_PROFILE || Count == 0) {
++SuccBIIter;
continue;
}
if (!Visited.insert(Succ->getIndex()).second) {
++SuccBIIter;
continue;
}
Queue.push(Succ->getIndex());
++SuccBIIter;
}
}

const size_t NumReachableBBs = Visited.size();

// Loop through Visited, and sum the corresponding BBs' execution counts
// (ECs).
size_t SumReachableBBEC = 0;
for (const unsigned BBIndex : Visited) {
const BinaryBasicBlock *BB = IndexToBB[BBIndex];
SumReachableBBEC += BB->getKnownExecutionCount();
}

const size_t NumPosECBBsUnreachableFromEntry =
NumPosECBBs - NumReachableBBs;
const size_t SumUnreachableBBEC = SumAllBBEC - SumReachableBBEC;
const double FractionECUnreachable =
(double)SumUnreachableBBEC / SumAllBBEC;

if (opts::Verbosity >= 2 && FractionECUnreachable >= 0.05) {
OS << "Non-trivial CFG discontinuity observed in function "
<< Function->getPrintName() << "\n";
LLVM_DEBUG(Function->dump());
}

NumUnreachables.push_back(NumPosECBBsUnreachableFromEntry);
SumECUnreachables.push_back(SumUnreachableBBEC);
FractionECUnreachables.push_back(FractionECUnreachable);
}

if (FractionECUnreachables.empty())
return;

std::sort(FractionECUnreachables.begin(), FractionECUnreachables.end());
const int Rank = int(FractionECUnreachables.size() * 0.95);
OS << format("top 5%% function CFG discontinuity is %.2lf%%\n",
FractionECUnreachables[Rank] * 100);

if (opts::Verbosity >= 1) {
OS << "abbreviations: EC = execution count, POS BBs = positive EC BBs\n"
<< "distribution of NUM(unreachable POS BBs) among all focal "
"functions\n";
printDistribution(OS, NumUnreachables);

OS << "distribution of SUM_EC(unreachable POS BBs) among all focal "
"functions\n";
printDistribution(OS, SumECUnreachables);

OS << "distribution of [(SUM_EC(unreachable POS BBs) / SUM_EC(all "
"POS BBs))] among all focal functions\n";
printDistribution(OS, FractionECUnreachables, /*Fraction=*/true);
}
}

void printAll(BinaryContext &BC, FunctionListType &ValidFunctions,
size_t NumTopFunctions) {
// Sort the list of functions by execution counts (reverse).
llvm::sort(ValidFunctions,
[&](const BinaryFunction *A, const BinaryFunction *B) {
return A->getKnownExecutionCount() > B->getKnownExecutionCount();
});

const size_t RealNumTopFunctions =
std::min(NumTopFunctions, ValidFunctions.size());

iterator_range<function_iterator> Functions(
ValidFunctions.begin(), ValidFunctions.begin() + RealNumTopFunctions);

BC.outs() << format("BOLT-INFO: among the hottest %zu functions ",
RealNumTopFunctions);
printCFGContinuityStats(BC.outs(), Functions);

// Print more detailed bucketed stats if requested.
if (opts::Verbosity >= 1 && RealNumTopFunctions >= 5) {
const size_t PerBucketSize = RealNumTopFunctions / 5;
BC.outs() << format(
"Detailed stats for 5 buckets, each with %zu functions:\n",
PerBucketSize);

// For each bucket, print the CFG continuity stats of the functions in the
// bucket.
for (size_t BucketIndex = 0; BucketIndex < 5; ++BucketIndex) {
const size_t StartIndex = BucketIndex * PerBucketSize;
const size_t EndIndex = StartIndex + PerBucketSize;
iterator_range<function_iterator> Functions(
ValidFunctions.begin() + StartIndex,
ValidFunctions.begin() + EndIndex);
const size_t MaxFunctionExecutionCount =
ValidFunctions[StartIndex]->getKnownExecutionCount();
const size_t MinFunctionExecutionCount =
ValidFunctions[EndIndex - 1]->getKnownExecutionCount();
BC.outs() << format("----------------\n| Bucket %zu: "
"|\n----------------\n",
BucketIndex + 1)
<< format(
"execution counts of the %zu functions in the bucket: "
"%zu-%zu\n",
EndIndex - StartIndex, MinFunctionExecutionCount,
MaxFunctionExecutionCount);
printCFGContinuityStats(BC.outs(), Functions);
}
}
}
} // namespace

bool PrintContinuityStats::shouldOptimize(const BinaryFunction &BF) const {
if (BF.empty() || !BF.hasValidProfile())
return false;

return BinaryFunctionPass::shouldOptimize(BF);
}

Error PrintContinuityStats::runOnFunctions(BinaryContext &BC) {
// Create a list of functions with valid profiles.
FunctionListType ValidFunctions;
for (const auto &BFI : BC.getBinaryFunctions()) {
const BinaryFunction *Function = &BFI.second;
if (PrintContinuityStats::shouldOptimize(*Function))
ValidFunctions.push_back(Function);
}
if (ValidFunctions.empty() || opts::NumFunctionsForContinuityCheck == 0)
return Error::success();

printAll(BC, ValidFunctions, opts::NumFunctionsForContinuityCheck);
return Error::success();
}
12 changes: 0 additions & 12 deletions bolt/lib/Passes/IdenticalCodeFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,6 @@ TimeICF("time-icf",
cl::cat(BoltOptCategory));
} // namespace opts

bool IdenticalCodeFolding::shouldOptimize(const BinaryFunction &BF) const {
if (BF.hasUnknownControlFlow())
return false;
if (BF.isFolded())
return false;
if (BF.hasSDTMarker())
return false;
if (BF.isPseudo())
return false;
return BinaryFunctionPass::shouldOptimize(BF);
}

/// Compare two jump tables in 2 functions. The function relies on consistent
/// ordering of basic blocks in both binary functions (e.g. DFS).
static bool equalJumpTables(const JumpTable &JumpTableA,
Expand Down
440 changes: 3 additions & 437 deletions bolt/lib/Passes/LongJmp.cpp

Large diffs are not rendered by default.

15 changes: 5 additions & 10 deletions bolt/lib/Passes/PatchEntries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
//===----------------------------------------------------------------------===//

#include "bolt/Passes/PatchEntries.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "bolt/Utils/NameResolver.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CommandLine.h"
Expand All @@ -36,20 +35,16 @@ Error PatchEntries::runOnFunctions(BinaryContext &BC) {
if (!opts::ForcePatch) {
// Mark the binary for patching if we did not create external references
// for original code in any of functions we are not going to emit.
bool NeedsPatching =
llvm::any_of(llvm::make_second_range(BC.getBinaryFunctions()),
[&](BinaryFunction &BF) {
return !BF.isPseudo() && !BC.shouldEmit(BF) &&
!BF.hasExternalRefRelocations();
});
bool NeedsPatching = llvm::any_of(
llvm::make_second_range(BC.getBinaryFunctions()),
[&](BinaryFunction &BF) {
return !BC.shouldEmit(BF) && !BF.hasExternalRefRelocations();
});

if (!NeedsPatching)
return Error::success();
}

assert(!opts::UseOldText &&
"Cannot patch entries while overwriting original .text");

if (opts::Verbosity >= 1)
BC.outs() << "BOLT-INFO: patching entries in original code\n";

Expand Down
43 changes: 15 additions & 28 deletions bolt/lib/Passes/VeneerElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,26 @@ Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
if (!opts::EliminateVeneers || !BC.isAArch64())
return Error::success();

std::map<uint64_t, BinaryFunction> &BFs = BC.getBinaryFunctions();
std::unordered_map<const MCSymbol *, const MCSymbol *> VeneerDestinations;
uint64_t VeneersCount = 0;
uint64_t NumAllVeneers = 0;
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
if (!BF.isAArch64Veneer())
for (auto &It : BFs) {
BinaryFunction &VeneerFunction = It.second;
if (!VeneerFunction.isAArch64Veneer())
continue;

++NumAllVeneers;

if (BF.isIgnored())
continue;

MCInst &FirstInstruction = *(BF.begin()->begin());
const MCSymbol *VeneerTargetSymbol;
if (BC.MIB->isTailCall(FirstInstruction)) {
VeneerTargetSymbol = BC.MIB->getTargetSymbol(FirstInstruction);
} else {
if (!BC.MIB->hasAnnotation(FirstInstruction, "AArch64Veneer"))
continue;
VeneerTargetSymbol = BC.MIB->getTargetSymbol(FirstInstruction, 1);
}

if (!VeneerTargetSymbol)
continue;

for (const MCSymbol *Symbol : BF.getSymbols())
VeneerDestinations[Symbol] = VeneerTargetSymbol;

VeneersCount++;
BF.setPseudo(true);
VeneerFunction.setPseudo(true);
MCInst &FirstInstruction = *(VeneerFunction.begin()->begin());
const MCSymbol *VeneerTargetSymbol =
BC.MIB->getTargetSymbol(FirstInstruction, 1);
assert(VeneerTargetSymbol && "Expecting target symbol for instruction");
for (const MCSymbol *Symbol : VeneerFunction.getSymbols())
VeneerDestinations[Symbol] = VeneerTargetSymbol;
}

BC.outs() << "BOLT-INFO: number of removed linker-inserted veneers: "
<< VeneersCount << ". Total veneers: " << NumAllVeneers << '\n';
<< VeneersCount << "\n";

// Handle veneers to veneers in case they occur
for (auto &Entry : VeneerDestinations) {
Expand All @@ -79,8 +65,9 @@ Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
}

uint64_t VeneerCallers = 0;
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
for (BinaryBasicBlock &BB : BF) {
for (auto &It : BFs) {
BinaryFunction &Function = It.second;
for (BinaryBasicBlock &BB : Function) {
for (MCInst &Instr : BB) {
if (!BC.MIB->isCall(Instr) || BC.MIB->isIndirectCall(Instr))
continue;
Expand Down
22 changes: 2 additions & 20 deletions bolt/lib/Profile/BoltAddressTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,12 @@
#include "bolt/Profile/BoltAddressTranslation.h"
#include "bolt/Core/BinaryFunction.h"
#include "llvm/ADT/APInt.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Timer.h"

#define DEBUG_TYPE "bolt-bat"

namespace opts {
extern llvm::cl::OptionCategory BoltCategory;
llvm::cl::opt<bool>
TimeBAT("time-bat",
llvm::cl::desc("print time spent processing BAT tables"),
llvm::cl::Hidden, llvm::cl::cat(BoltCategory));
} // namespace opts

namespace llvm {
namespace bolt {

Expand Down Expand Up @@ -85,9 +75,8 @@ void BoltAddressTranslation::writeEntriesForBB(
}
}

void BoltAddressTranslation::constructMaps(const BinaryContext &BC) {
NamedRegionTimer T("constuctmaps", "construct translation maps", "bat",
"process BAT", opts::TimeBAT);
void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Writing BOLT Address Translation Tables\n");
for (auto &BFI : BC.getBinaryFunctions()) {
const BinaryFunction &Function = BFI.second;
const uint64_t InputAddress = Function.getAddress();
Expand Down Expand Up @@ -151,11 +140,6 @@ void BoltAddressTranslation::constructMaps(const BinaryContext &BC) {
Maps.emplace(FF.getAddress(), std::move(Map));
}
}
}

void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Writing BOLT Address Translation Tables\n");
constructMaps(BC);

// Output addresses are delta-encoded
uint64_t PrevAddress = 0;
Expand Down Expand Up @@ -199,8 +183,6 @@ size_t BoltAddressTranslation::getNumEqualOffsets(const MapTy &Map,

template <bool Cold>
void BoltAddressTranslation::writeMaps(uint64_t &PrevAddress, raw_ostream &OS) {
NamedRegionTimer T("writemaps", "write translation maps", "bat",
"process BAT", opts::TimeBAT);
const uint32_t NumFuncs =
llvm::count_if(llvm::make_first_range(Maps), [&](const uint64_t Address) {
return Cold == ColdPartSource.count(Address);
Expand Down
6 changes: 1 addition & 5 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,11 +643,7 @@ size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) {
// equal number of blocks.
if (NamespaceToProfiledBFSizesIt->second.count(BF->size()) == 0)
continue;
auto NamespaceToBFsIt = NamespaceToBFs.find(Namespace);
if (NamespaceToBFsIt == NamespaceToBFs.end())
NamespaceToBFs[Namespace] = {BF};
else
NamespaceToBFsIt->second.push_back(BF);
NamespaceToBFs[Namespace].push_back(BF);
}

// Iterates through all profiled functions and binary functions belonging to
Expand Down
9 changes: 6 additions & 3 deletions bolt/lib/Rewrite/BinaryPassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "bolt/Passes/AllocCombiner.h"
#include "bolt/Passes/AsmDump.h"
#include "bolt/Passes/CMOVConversion.h"
#include "bolt/Passes/ContinuityStats.h"
#include "bolt/Passes/FixRISCVCallsPass.h"
#include "bolt/Passes/FixRelaxationPass.h"
#include "bolt/Passes/FrameOptimizer.h"
Expand Down Expand Up @@ -373,6 +374,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
if (opts::PrintProfileStats)
Manager.registerPass(std::make_unique<PrintProfileStats>(NeverPrint));

Manager.registerPass(std::make_unique<PrintContinuityStats>(NeverPrint));

Manager.registerPass(std::make_unique<ValidateInternalCalls>(NeverPrint));

Manager.registerPass(std::make_unique<ValidateMemRefs>(NeverPrint));
Expand Down Expand Up @@ -489,9 +492,6 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
// memory profiling data.
Manager.registerPass(std::make_unique<ReorderData>());

// Assign each function an output section.
Manager.registerPass(std::make_unique<AssignSections>());

if (BC.isAArch64()) {
Manager.registerPass(std::make_unique<ADRRelaxationPass>());

Expand All @@ -515,6 +515,9 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(
std::make_unique<RetpolineInsertion>(PrintRetpolineInsertion));

// Assign each function an output section.
Manager.registerPass(std::make_unique<AssignSections>());

// Patch original function entries
if (BC.HasRelocations)
Manager.registerPass(std::make_unique<PatchEntries>());
Expand Down
36 changes: 5 additions & 31 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdint>
Expand Down Expand Up @@ -368,11 +367,6 @@ static cl::opt<bool> AlwaysConvertToRanges(
cl::ReallyHidden, cl::init(false), cl::cat(BoltCategory));

extern cl::opt<std::string> CompDirOverride;

cl::opt<bool>
TimeDebug("time-debug",
cl::desc("print time spent processing debug information"),
cl::Hidden, cl::cat(BoltCategory));
} // namespace opts

/// If DW_AT_low_pc exists sets LowPC and returns true.
Expand Down Expand Up @@ -553,8 +547,6 @@ using CUPartitionVector = std::vector<DWARFUnitVec>;
/// cu-processing-batch-size. All the CUs that have cross CU reference reference
/// as a source are put in to the same initial bucket.
static CUPartitionVector partitionCUs(DWARFContext &DwCtx) {
NamedRegionTimer T("partitioncus", "partition cus", "debug",
"update debug info", opts::TimeDebug);
CUPartitionVector Vec(2);
unsigned Counter = 0;
const DWARFDebugAbbrev *Abbr = DwCtx.getDebugAbbrev();
Expand Down Expand Up @@ -615,9 +607,11 @@ void DWARFRewriter::updateDebugInfo() {
}

uint32_t CUIndex = 0;
std::mutex AccessMutex;
// Needs to be invoked in the same order as CUs are processed.
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) {
auto AddrW = std::make_unique<DebugAddrWriterDwarf5>(
Expand Down Expand Up @@ -683,8 +677,6 @@ void DWARFRewriter::updateDebugInfo() {
GDBIndexSection, TempRangesSectionWriter);
};
auto processMainBinaryCU = [&](DWARFUnit &Unit, DIEBuilder &DIEBlder) {
NamedRegionTimer T("processmainbinarycu", "process main binary CU",
"debug", "update debug info", opts::TimeDebug);
std::optional<DWARFUnit *> SplitCU;
std::optional<uint64_t> RangesBase;
std::optional<uint64_t> DWOId = Unit.getDWOId();
Expand Down Expand Up @@ -717,11 +709,7 @@ void DWARFRewriter::updateDebugInfo() {
};

DIEBuilder DIEBlder(BC, BC.DwCtx.get(), DebugNamesTable);
{
NamedRegionTimer T("buildtypeunits", "build type units", "debug",
"update debug info", opts::TimeDebug);
DIEBlder.buildTypeUnits(StrOffstsWriter.get());
}
DIEBlder.buildTypeUnits(StrOffstsWriter.get());
SmallVector<char, 20> OutBuffer;
std::unique_ptr<raw_svector_ostream> ObjOS =
std::make_unique<raw_svector_ostream>(OutBuffer);
Expand All @@ -735,9 +723,8 @@ void DWARFRewriter::updateDebugInfo() {
CUPartitionVector PartVec = partitionCUs(*BC.DwCtx);
const unsigned int ThreadCount =
std::min(opts::DebugThreadCount, opts::ThreadCount);
auto updateSplitCUs = [&]() {
NamedRegionTimer T("updatesplitcus", "update split CUs", "debug",
"update debug info", opts::TimeDebug);
for (std::vector<DWARFUnit *> &Vec : PartVec) {
DIEBlder.buildCompileUnits(Vec);
llvm::SmallVector<std::unique_ptr<DIEBuilder>, 72> DWODIEBuildersByCU;
ThreadPoolInterface &ThreadPool =
ParallelUtilities::getThreadPool(ThreadCount);
Expand Down Expand Up @@ -777,13 +764,6 @@ void DWARFRewriter::updateDebugInfo() {
});
}
ThreadPool.wait();
return DWODIEBuildersByCU;
};

for (std::vector<DWARFUnit *> &Vec : PartVec) {
DIEBlder.buildCompileUnits(Vec);
llvm::SmallVector<std::unique_ptr<DIEBuilder>, 72> DWODIEBuildersByCU =
updateSplitCUs();
for (std::unique_ptr<DIEBuilder> &DWODIEBuilderPtr : DWODIEBuildersByCU)
DWODIEBuilderPtr->updateDebugNamesTable();
for (DWARFUnit *CU : DIEBlder.getProcessedCUs())
Expand Down Expand Up @@ -1471,8 +1451,6 @@ void DWARFRewriter::updateLineTableOffsets(const MCAssembler &Asm) {
CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder,
DIEStreamer &Streamer,
GDBIndex &GDBIndexSection) {
NamedRegionTimer T("finalizetypesections", "finalize type sections",
"debug", "update debug info", opts::TimeDebug);
// update TypeUnit DW_AT_stmt_list with new .debug_line information.
auto updateLineTable = [&](const DWARFUnit &Unit) -> void {
DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(Unit);
Expand Down Expand Up @@ -1538,8 +1516,6 @@ void DWARFRewriter::finalizeDebugSections(
DIEBuilder &DIEBlder, DWARF5AcceleratorTable &DebugNamesTable,
DIEStreamer &Streamer, raw_svector_ostream &ObjOS, CUOffsetMap &CUMap,
DebugAddrWriter &FinalAddrWriter) {
NamedRegionTimer T("finalizedebugsections", "finalize debug sections",
"debug", "update debug info", opts::TimeDebug);
if (StrWriter->isInitialized()) {
RewriteInstance::addToDebugSectionsToOverwrite(".debug_str");
std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents =
Expand Down Expand Up @@ -1652,8 +1628,6 @@ void DWARFRewriter::finalizeCompileUnits(DIEBuilder &DIEBlder,
CUOffsetMap &CUMap,
const std::list<DWARFUnit *> &CUs,
DebugAddrWriter &FinalAddrWriter) {
NamedRegionTimer T("finalizecompileunits", "finalize compile units",
"debug", "update debug info", opts::TimeDebug);
for (DWARFUnit *CU : CUs) {
auto AddressWriterIterator = AddressWritersByCU.find(CU->getOffset());
assert(AddressWriterIterator != AddressWritersByCU.end() &&
Expand Down
20 changes: 3 additions & 17 deletions bolt/lib/Rewrite/PseudoProbeRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Timer.h"
#include <memory>

#undef DEBUG_TYPE
Expand All @@ -31,10 +30,6 @@ using namespace bolt;

namespace opts {

cl::opt<bool> TimeProbes("time-probes",
cl::desc("print time spent processing pseudo probes"),
cl::Hidden, cl::cat(BoltCategory));

enum PrintPseudoProbesOptions {
PPP_None = 0,
PPP_Probes_Section_Decode = 0x1,
Expand Down Expand Up @@ -108,9 +103,6 @@ Error PseudoProbeRewriter::postEmitFinalizer() {
parsePseudoProbe();
updatePseudoProbes();

// encode pseudo probes with updated addresses
encodePseudoProbes();

return Error::success();
}

Expand All @@ -124,8 +116,6 @@ void PseudoProbeRewriter::parsePseudoProbe(bool ProfiledOnly) {
return;
}

NamedRegionTimer T("parseprobes", "parse pseudo probes", "probes",
"process pseudo probes", opts::TimeProbes);
// If only one section is found, it might mean the ELF is corrupted.
if (!PseudoProbeDescSection) {
errs() << "BOLT-WARNING: fail in reading .pseudo_probe_desc binary\n";
Expand Down Expand Up @@ -206,8 +196,6 @@ void PseudoProbeRewriter::updatePseudoProbes() {
// check if there is pseudo probe section decoded
if (ProbeDecoder.getAddress2ProbesMap().empty())
return;
NamedRegionTimer T("updateprobes", "update pseudo probes", "probes",
"process pseudo probes", opts::TimeProbes);
// input address converted to output
AddressProbesMap &Address2ProbesMap = ProbeDecoder.getAddress2ProbesMap();
const GUIDProbeFunctionMap &GUID2Func = ProbeDecoder.getGUID2FuncDescMap();
Expand Down Expand Up @@ -284,15 +272,13 @@ void PseudoProbeRewriter::updatePseudoProbes() {
}
outs() << "=======================================\n";
}

// encode pseudo probes with updated addresses
encodePseudoProbes();
}

void PseudoProbeRewriter::encodePseudoProbes() {
MCPseudoProbeDecoder &ProbeDecoder(*ProbeDecoderPtr);
// check if there is pseudo probe section decoded
if (ProbeDecoder.getAddress2ProbesMap().empty())
return;
NamedRegionTimer T("encodeprobes", "encode pseudo probes", "probes",
"process pseudo probes", opts::TimeProbes);
// Buffer for new pseudo probes section
SmallString<8> Contents;
MCDecodedPseudoProbe *LastProbe = nullptr;
Expand Down
42 changes: 6 additions & 36 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3557,11 +3557,9 @@ void RewriteInstance::finalizeMetadataPreEmit() {
}

void RewriteInstance::updateMetadata() {
{
NamedRegionTimer T("updatemetadata-postemit", "update metadata post-emit",
TimerGroupName, TimerGroupDesc, opts::TimeRewrite);
MetadataManager.runFinalizersAfterEmit();
}
NamedRegionTimer T("updatemetadata-postemit", "update metadata post-emit",
TimerGroupName, TimerGroupDesc, opts::TimeRewrite);
MetadataManager.runFinalizersAfterEmit();

if (opts::UpdateDebugSections) {
NamedRegionTimer T("updateDebugInfo", "update debug info", TimerGroupName,
Expand Down Expand Up @@ -3723,41 +3721,15 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {
return Address;
};

// Try to allocate sections before the \p Address and return an address for
// the allocation of the first section or 0 if \p is not big enough.
auto allocateBefore = [&](uint64_t Address) -> uint64_t {
for (auto SI = CodeSections.rbegin(), SE = CodeSections.rend(); SI != SE;
++SI) {
BinarySection *Section = *SI;
if (Section->getOutputSize() > Address)
return 0;
Address -= Section->getOutputSize();
Address = alignDown(Address, Section->getAlignment());
Section->setOutputAddress(Address);
}
return Address;
};

// Check if we can fit code in the original .text
bool AllocationDone = false;
if (opts::UseOldText) {
uint64_t StartAddress;
uint64_t EndAddress;
if (opts::HotFunctionsAtEnd) {
EndAddress = BC->OldTextSectionAddress + BC->OldTextSectionSize;
StartAddress = allocateBefore(EndAddress);
} else {
StartAddress = BC->OldTextSectionAddress;
EndAddress = allocateAt(BC->OldTextSectionAddress);
}
const uint64_t CodeSize =
allocateAt(BC->OldTextSectionAddress) - BC->OldTextSectionAddress;

const uint64_t CodeSize = EndAddress - StartAddress;
if (CodeSize <= BC->OldTextSectionSize) {
BC->outs() << "BOLT-INFO: using original .text for new code with 0x"
<< Twine::utohexstr(opts::AlignText) << " alignment";
if (StartAddress != BC->OldTextSectionAddress)
BC->outs() << " at 0x" << Twine::utohexstr(StartAddress);
BC->outs() << '\n';
<< Twine::utohexstr(opts::AlignText) << " alignment\n";
AllocationDone = true;
} else {
BC->errs()
Expand Down Expand Up @@ -5563,8 +5535,6 @@ uint64_t RewriteInstance::getNewFunctionOrDataAddress(uint64_t OldAddress) {
}

void RewriteInstance::rewriteFile() {
NamedRegionTimer T("rewrite", "rewrite file", TimerGroupName,
TimerGroupDesc, opts::TimeRewrite);
std::error_code EC;
Out = std::make_unique<ToolOutputFile>(opts::OutputFilename, EC,
sys::fs::OF_None);
Expand Down
49 changes: 49 additions & 0 deletions bolt/test/AArch64/adr-relaxation.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## Check that llvm-bolt will not unnecessarily relax ADR instruction.

# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static
# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=random2
# RUN: llvm-objdump -d --disassemble-symbols=_start %t.bolt | FileCheck %s
# RUN: llvm-objdump -d --disassemble-symbols=foo.cold.0 %t.bolt \
# RUN: | FileCheck --check-prefix=CHECK-FOO %s

## ADR below references its containing function that is split. But ADR is always
## in the main fragment, thus there is no need to relax it.
.text
.globl _start
.type _start, %function
_start:
# CHECK: <_start>:
.cfi_startproc
adr x1, _start
# CHECK-NOT: adrp
# CHECK: adr
cmp x1, x11
b.hi .L1
mov x0, #0x0
.L1:
ret x30
.cfi_endproc
.size _start, .-_start


## In foo, ADR is in the split fragment but references the main one. Thus, it
## needs to be relaxed into ADRP + ADD.
.globl foo
.type foo, %function
foo:
.cfi_startproc
cmp x1, x11
b.hi .L2
mov x0, #0x0
.L2:
# CHECK-FOO: <foo.cold.0>:
adr x1, foo
# CHECK-FOO: adrp
# CHECK-FOO-NEXT: add
ret x30
.cfi_endproc
.size foo, .-foo

## Force relocation mode.
.reloc 0, R_AARCH64_NONE
4 changes: 4 additions & 0 deletions bolt/test/X86/cfg-discontinuity-reporting.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Check profile discontinuity reporting
RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe
RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt | FileCheck %s
CHECK: among the hottest 5 functions top 5% function CFG discontinuity is 100.00%
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-apply-replacements/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangApplyReplacements
add_clang_library(clangApplyReplacements STATIC
lib/Tooling/ApplyReplacements.cpp

DEPENDS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,8 @@ groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs,

if (auto Entry = SM.getFileManager().getOptionalFileRef(Path)) {
if (SourceTU) {
auto &Replaces = DiagReplacements[*Entry];
auto It = Replaces.find(R);
if (It == Replaces.end())
Replaces.emplace(R, SourceTU);
else if (It->second != SourceTU)
auto [It, Inserted] = DiagReplacements[*Entry].try_emplace(R, SourceTU);
if (!Inserted && It->second != SourceTU)
// This replacement is a duplicate of one suggested by another TU.
return;
}
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-change-namespace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangChangeNamespace
add_clang_library(clangChangeNamespace STATIC
ChangeNamespace.cpp

DEPENDS
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,9 +606,8 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
// If this reference has been processed as a function call, we do not
// process it again.
if (ProcessedFuncRefs.count(FuncRef))
if (!ProcessedFuncRefs.insert(FuncRef).second)
return;
ProcessedFuncRefs.insert(FuncRef);
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
assert(Func);
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangDoc
add_clang_library(clangDoc STATIC
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-include-fixer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
support
)

add_clang_library(clangIncludeFixer
add_clang_library(clangIncludeFixer STATIC
IncludeFixer.cpp
IncludeFixerContext.cpp
InMemorySymbolIndex.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(findAllSymbols
add_clang_library(findAllSymbols STATIC
FindAllSymbols.cpp
FindAllSymbolsAction.cpp
FindAllMacros.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_clang_library(clangIncludeFixerPlugin
add_clang_library(clangIncludeFixerPlugin STATIC
IncludeFixerPlugin.cpp

LINK_LIBS
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-move/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangMove
add_clang_library(clangMove STATIC
Move.cpp
HelperDeclRefGraph.cpp

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-query/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangQuery
add_clang_library(clangQuery STATIC
Query.cpp
QueryParser.cpp

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-reorder-fields/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
support
)

add_clang_library(clangReorderFields
add_clang_library(clangReorderFields STATIC
ReorderFieldsAction.cpp

DEPENDS
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ configure_file(
${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-config.h)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})

add_clang_library(clangTidy
add_clang_library(clangTidy STATIC
ClangTidy.cpp
ClangTidyCheck.cpp
ClangTidyModule.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/abseil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyAbseilModule
add_clang_library(clangTidyAbseilModule STATIC
AbseilTidyModule.cpp
CleanupCtadCheck.cpp
DurationAdditionCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/altera/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
support
)

add_clang_library(clangTidyAlteraModule
add_clang_library(clangTidyAlteraModule STATIC
AlteraTidyModule.cpp
IdDependentBackwardBranchCheck.cpp
KernelNameRestrictionCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyAndroidModule
add_clang_library(clangTidyAndroidModule STATIC
AndroidTidyModule.cpp
CloexecAccept4Check.cpp
CloexecAcceptCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/boost/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyBoostModule
add_clang_library(clangTidyBoostModule STATIC
BoostTidyModule.cpp
UseRangesCheck.cpp
UseToStringCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyBugproneModule
add_clang_library(clangTidyBugproneModule STATIC
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
AssignmentInIfConditionCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,13 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
}
// Check if a definition in another namespace exists.
const auto DeclName = CurDecl->getName();
if (!DeclNameToDefinitions.contains(DeclName)) {
auto It = DeclNameToDefinitions.find(DeclName);
if (It == DeclNameToDefinitions.end()) {
continue; // No definition in this translation unit, we can skip it.
}
// Make a warning for each definition with the same name (in other
// namespaces).
const auto &Definitions = DeclNameToDefinitions[DeclName];
const auto &Definitions = It->second;
for (const auto *Def : Definitions) {
diag(CurDecl->getLocation(),
"no definition found for %0, but a definition with "
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/cert/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)

add_clang_library(clangTidyCERTModule
add_clang_library(clangTidyCERTModule STATIC
CERTTidyModule.cpp
CommandProcessorCheck.cpp
DefaultOperatorNewAlignmentCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyConcurrencyModule
add_clang_library(clangTidyConcurrencyModule STATIC
ConcurrencyTidyModule.cpp
MtUnsafeCheck.cpp
ThreadCanceltypeAsynchronousCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyCppCoreGuidelinesModule
add_clang_library(clangTidyCppCoreGuidelinesModule STATIC
AvoidCapturingLambdaCoroutinesCheck.cpp
AvoidConstOrRefDataMembersCheck.cpp
AvoidDoWhileCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,8 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer(
// It only includes fields that have not been fixed
SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) {
if (!HasRecordClassMemberSet.contains(F)) {
if (HasRecordClassMemberSet.insert(F).second)
AllFieldsToInit.insert(F);
HasRecordClassMemberSet.insert(F);
}
});
if (FieldsToInit.empty())
return;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/darwin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyDarwinModule
add_clang_library(clangTidyDarwinModule STATIC
AvoidSpinlockCheck.cpp
DarwinTidyModule.cpp
DispatchOnceNonstaticCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/fuchsia/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyFuchsiaModule
add_clang_library(clangTidyFuchsiaModule STATIC
DefaultArgumentsCallsCheck.cpp
DefaultArgumentsDeclarationsCheck.cpp
FuchsiaTidyModule.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/google/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyGoogleModule
add_clang_library(clangTidyGoogleModule STATIC
AvoidCStyleCastsCheck.cpp
AvoidNSObjectNewCheck.cpp
AvoidThrowingObjCExceptionCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/hicpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyHICPPModule
add_clang_library(clangTidyHICPPModule STATIC
ExceptionBaseclassCheck.cpp
HICPPTidyModule.cpp
IgnoredRemoveResultCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyLinuxKernelModule
add_clang_library(clangTidyLinuxKernelModule STATIC
LinuxKernelTidyModule.cpp
MustCheckErrsCheck.cpp

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyLLVMModule
add_clang_library(clangTidyLLVMModule STATIC
HeaderGuardCheck.cpp
IncludeOrderCheck.cpp
LLVMTidyModule.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyLLVMLibcModule
add_clang_library(clangTidyLLVMLibcModule STATIC
CalleeNamespaceCheck.cpp
ImplementationInNamespaceCheck.cpp
InlineFunctionDeclCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ add_custom_command(
add_custom_target(genconfusable DEPENDS Confusables.inc)
set_target_properties(genconfusable PROPERTIES FOLDER "Clang Tools Extra/Sourcegenning")

add_clang_library(clangTidyMiscModule
add_clang_library(clangTidyMiscModule STATIC
ConstCorrectnessCheck.cpp
CoroutineHostileRAIICheck.cpp
DefinitionsInHeadersCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyModernizeModule
add_clang_library(clangTidyModernizeModule STATIC
AvoidBindCheck.cpp
AvoidCArraysCheck.cpp
ConcatNestedNamespacesCheck.cpp
Expand Down
226 changes: 135 additions & 91 deletions clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "UseStartsEndsWithCheck.h"

#include "../utils/ASTUtils.h"
#include "../utils/OptionsUtils.h"
#include "clang/Lex/Lexer.h"

Expand All @@ -16,13 +17,71 @@
using namespace clang::ast_matchers;

namespace clang::tidy::modernize {
struct NotLengthExprForStringNode {
NotLengthExprForStringNode(std::string ID, DynTypedNode Node,
ASTContext *Context)
: ID(std::move(ID)), Node(std::move(Node)), Context(Context) {}
bool operator()(const internal::BoundNodesMap &Nodes) const {
// Match a string literal and an integer size or strlen() call.
if (const auto *StringLiteralNode = Nodes.getNodeAs<StringLiteral>(ID)) {
if (const auto *IntegerLiteralSizeNode = Node.get<IntegerLiteral>()) {
return StringLiteralNode->getLength() !=
IntegerLiteralSizeNode->getValue().getZExtValue();
}

if (const auto *StrlenNode = Node.get<CallExpr>()) {
if (StrlenNode->getDirectCallee()->getName() != "strlen" ||
StrlenNode->getNumArgs() != 1) {
return true;
}

if (const auto *StrlenArgNode = dyn_cast<StringLiteral>(
StrlenNode->getArg(0)->IgnoreParenImpCasts())) {
return StrlenArgNode->getLength() != StringLiteralNode->getLength();
}
}
}

// Match a string variable and a call to length() or size().
if (const auto *ExprNode = Nodes.getNodeAs<Expr>(ID)) {
if (const auto *MemberCallNode = Node.get<CXXMemberCallExpr>()) {
const CXXMethodDecl *MethodDeclNode = MemberCallNode->getMethodDecl();
const StringRef Name = MethodDeclNode->getName();
if (!MethodDeclNode->isConst() || MethodDeclNode->getNumParams() != 0 ||
(Name != "size" && Name != "length")) {
return true;
}

if (const auto *OnNode =
dyn_cast<Expr>(MemberCallNode->getImplicitObjectArgument())) {
return !utils::areStatementsIdentical(OnNode->IgnoreParenImpCasts(),
ExprNode->IgnoreParenImpCasts(),
*Context);
}
}
}

return true;
}

private:
std::string ID;
DynTypedNode Node;
ASTContext *Context;
};

AST_MATCHER_P(Expr, lengthExprForStringNode, std::string, ID) {
return Builder->removeBindings(NotLengthExprForStringNode(
ID, DynTypedNode::create(Node), &(Finder->getASTContext())));
}

UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}

void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
const auto ZeroLiteral = integerLiteral(equals(0));

const auto HasStartsWithMethodWithName = [](const std::string &Name) {
return hasMethod(
cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
Expand All @@ -32,119 +91,104 @@ void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
anyOf(HasStartsWithMethodWithName("starts_with"),
HasStartsWithMethodWithName("startsWith"),
HasStartsWithMethodWithName("startswith"));
const auto ClassWithStartsWithFunction = cxxRecordDecl(anyOf(
HasStartsWithMethod, hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
cxxRecordDecl(HasStartsWithMethod)))))));
const auto OnClassWithStartsWithFunction =
on(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
anyOf(HasStartsWithMethod,
hasAnyBase(hasType(hasCanonicalType(
hasDeclaration(cxxRecordDecl(HasStartsWithMethod)))))))))));

const auto HasEndsWithMethodWithName = [](const std::string &Name) {
return hasMethod(
cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
.bind("ends_with_fun"));
};
const auto HasEndsWithMethod = anyOf(HasEndsWithMethodWithName("ends_with"),
HasEndsWithMethodWithName("endsWith"),
HasEndsWithMethodWithName("endswith"));
const auto OnClassWithEndsWithFunction =
on(expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
anyOf(HasEndsWithMethod,
hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
cxxRecordDecl(HasEndsWithMethod)))))))))))
.bind("haystack"));

// Case 1: X.find(Y) [!=]= 0 -> starts_with.
const auto FindExpr = cxxMemberCallExpr(
// A method call with no second argument or the second argument is zero...
anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)),
// ... named find...
callee(cxxMethodDecl(hasName("find")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));

// Case 2: X.rfind(Y, 0) [!=]= 0 -> starts_with.
const auto RFindExpr = cxxMemberCallExpr(
// A method call with a second argument of zero...
hasArgument(1, ZeroLiteral),
// ... named rfind...
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
// ... on a class with a starts_with function.
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// Bind search expression.
hasArgument(0, expr().bind("search_expr")));

// Match a string literal and an integer or strlen() call matching the length.
const auto HasStringLiteralAndLengthArgs = [](const auto StringArgIndex,
const auto LengthArgIndex) {
return allOf(
hasArgument(StringArgIndex, stringLiteral().bind("string_literal_arg")),
hasArgument(LengthArgIndex,
anyOf(integerLiteral().bind("integer_literal_size_arg"),
callExpr(callee(functionDecl(parameterCountIs(1),
hasName("strlen"))),
hasArgument(0, stringLiteral().bind(
"strlen_arg"))))));
};

// Match a string variable and a call to length() or size().
const auto HasStringVariableAndSizeCallArgs = [](const auto StringArgIndex,
const auto LengthArgIndex) {
return allOf(
hasArgument(StringArgIndex, declRefExpr(hasDeclaration(
decl().bind("string_var_decl")))),
hasArgument(LengthArgIndex,
cxxMemberCallExpr(
callee(cxxMethodDecl(isConst(), parameterCountIs(0),
hasAnyName("size", "length"))),
on(declRefExpr(
to(decl(equalsBoundNode("string_var_decl"))))))));
};

// Match either one of the two cases above.
const auto HasStringAndLengthArgs =
[HasStringLiteralAndLengthArgs, HasStringVariableAndSizeCallArgs](
const auto StringArgIndex, const auto LengthArgIndex) {
return anyOf(
HasStringLiteralAndLengthArgs(StringArgIndex, LengthArgIndex),
HasStringVariableAndSizeCallArgs(StringArgIndex, LengthArgIndex));
};
OnClassWithStartsWithFunction, hasArgument(0, expr().bind("needle")));

// Case 3: X.compare(0, LEN(Y), Y) [!=]= 0 -> starts_with.
const auto CompareExpr = cxxMemberCallExpr(
// A method call with three arguments...
argumentCountIs(3),
// ... where the first argument is zero...
hasArgument(0, ZeroLiteral),
// ... named compare...
argumentCountIs(3), hasArgument(0, ZeroLiteral),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
// ... on a class with a starts_with function...
on(hasType(
hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
// ... where the third argument is some string and the second a length.
HasStringAndLengthArgs(2, 1),
// Bind search expression.
hasArgument(2, expr().bind("search_expr")));
OnClassWithStartsWithFunction, hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")));

// Case 4: X.compare(LEN(X) - LEN(Y), LEN(Y), Y) [!=]= 0 -> ends_with.
const auto CompareEndsWithExpr = cxxMemberCallExpr(
argumentCountIs(3),
callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
OnClassWithEndsWithFunction, hasArgument(2, expr().bind("needle")),
hasArgument(1, lengthExprForStringNode("needle")),
hasArgument(0,
binaryOperator(hasOperatorName("-"),
hasLHS(lengthExprForStringNode("haystack")),
hasRHS(lengthExprForStringNode("needle")))));

// All cases comparing to 0.
Finder->addMatcher(
// Match [=!]= with a zero on one side and (r?)find|compare on the other.
binaryOperator(
hasAnyOperatorName("==", "!="),
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr))
hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
CompareEndsWithExpr))
.bind("find_expr"),
ZeroLiteral))
.bind("expr"),
this);

// Case 5: X.rfind(Y) [!=]= LEN(X) - LEN(Y) -> ends_with.
Finder->addMatcher(
binaryOperator(
hasAnyOperatorName("==", "!="),
hasOperands(
cxxMemberCallExpr(
anyOf(
argumentCountIs(1),
allOf(argumentCountIs(2),
hasArgument(
1,
anyOf(declRefExpr(to(varDecl(hasName("npos")))),
memberExpr(member(hasName("npos"))))))),
callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
OnClassWithEndsWithFunction,
hasArgument(0, expr().bind("needle")))
.bind("find_expr"),
binaryOperator(hasOperatorName("-"),
hasLHS(lengthExprForStringNode("haystack")),
hasRHS(lengthExprForStringNode("needle")))))
.bind("expr"),
this);
}

void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>("find_expr");
const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("find_fun");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("search_expr");
const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("needle");
const auto *StartsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("starts_with_fun");

const auto *StringLiteralArg =
Result.Nodes.getNodeAs<StringLiteral>("string_literal_arg");
const auto *IntegerLiteralSizeArg =
Result.Nodes.getNodeAs<IntegerLiteral>("integer_literal_size_arg");
const auto *StrlenArg = Result.Nodes.getNodeAs<StringLiteral>("strlen_arg");

// Filter out compare cases where the length does not match string literal.
if (StringLiteralArg && IntegerLiteralSizeArg &&
StringLiteralArg->getLength() !=
IntegerLiteralSizeArg->getValue().getZExtValue()) {
return;
}

if (StringLiteralArg && StrlenArg &&
StringLiteralArg->getLength() != StrlenArg->getLength()) {
return;
}
const auto *EndsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("ends_with_fun");
assert(bool(StartsWithFunction) != bool(EndsWithFunction));
const CXXMethodDecl *ReplacementFunction =
StartsWithFunction ? StartsWithFunction : EndsWithFunction;

if (ComparisonExpr->getBeginLoc().isMacroID()) {
return;
Expand All @@ -154,26 +198,26 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {

auto Diagnostic =
diag(FindExpr->getExprLoc(), "use %0 instead of %1() %select{==|!=}2 0")
<< StartsWithFunction->getName() << FindFun->getName() << Neg;
<< ReplacementFunction->getName() << FindFun->getName() << Neg;

// Remove possible arguments after search expression and ' [!=]= 0' suffix.
// Remove possible arguments after search expression and ' [!=]= .+' suffix.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
*Result.SourceManager, getLangOpts()),
ComparisonExpr->getEndLoc()),
")");

// Remove possible '0 [!=]= ' prefix.
// Remove possible '.+ [!=]= ' prefix.
Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));

// Replace method name by 'starts_with'.
// Replace method name by '(starts|ends)_with'.
// Remove possible arguments before search expression.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(FindExpr->getExprLoc(),
SearchExpr->getBeginLoc()),
(StartsWithFunction->getName() + "(").str());
(ReplacementFunction->getName() + "(").str());

// Add possible negation '!'.
if (Neg) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace clang::tidy::modernize {

/// Checks for common roundabout ways to express ``starts_with`` and
/// ``ends_with`` and suggests replacing with ``starts_with`` when the method is
/// ``ends_with`` and suggests replacing with the simpler method when it is
/// available. Notably, this will work with ``std::string`` and
/// ``std::string_view``.
///
Expand Down
7 changes: 4 additions & 3 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
this->PP = PP;
}

void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {
Expand Down Expand Up @@ -75,9 +76,9 @@ void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) {

utils::FormatStringConverter::Configuration ConverterConfig;
ConverterConfig.StrictMode = StrictMode;
utils::FormatStringConverter Converter(Result.Context, StrFormat,
FormatArgOffset, ConverterConfig,
getLangOpts());
utils::FormatStringConverter Converter(
Result.Context, StrFormat, FormatArgOffset, ConverterConfig,
getLangOpts(), *Result.SourceManager, *PP);
const Expr *StrFormatCall = StrFormat->getCallee();
if (!Converter.canApply()) {
diag(StrFormat->getBeginLoc(),
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class UseStdFormatCheck : public ClangTidyCheck {
StringRef ReplacementFormatFunction;
utils::IncludeInserter IncludeInserter;
std::optional<StringRef> MaybeHeaderToInclude;
Preprocessor *PP = nullptr;
};

} // namespace clang::tidy::modernize
Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ void UseStdPrintCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
this->PP = PP;
}

static clang::ast_matchers::StatementMatcher
Expand Down Expand Up @@ -131,7 +132,8 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {
ConverterConfig.StrictMode = StrictMode;
ConverterConfig.AllowTrailingNewlineRemoval = true;
utils::FormatStringConverter Converter(
Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts());
Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts(),
*Result.SourceManager, *PP);
const Expr *PrintfCall = Printf->getCallee();
const StringRef ReplacementFunction = Converter.usePrintNewlineFunction()
? ReplacementPrintlnFunction
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class UseStdPrintCheck : public ClangTidyCheck {
}

private:
Preprocessor *PP;
bool StrictMode;
std::vector<StringRef> PrintfLikeFunctions;
std::vector<StringRef> FprintfLikeFunctions;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/mpi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyMPIModule
add_clang_library(clangTidyMPIModule STATIC
BufferDerefCheck.cpp
MPITidyModule.cpp
TypeMismatchCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/objc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyObjCModule
add_clang_library(clangTidyObjCModule STATIC
AssertEquals.cpp
AvoidNSErrorInitCheck.cpp
DeallocInCategoryCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/openmp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyOpenMPModule
add_clang_library(clangTidyOpenMPModule STATIC
ExceptionEscapeCheck.cpp
OpenMPTidyModule.cpp
UseDefaultNoneCheck.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/performance/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyPerformanceModule
add_clang_library(clangTidyPerformanceModule STATIC
AvoidEndlCheck.cpp
EnumSizeCheck.cpp
FasterStringFindCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,9 @@ void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
}

if (const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl();
RecordDecl && !(RecordDecl->hasMoveConstructor() &&
RecordDecl->hasMoveAssignment())) {
RecordDecl && RecordDecl->hasDefinition() &&
!(RecordDecl->hasMoveConstructor() &&
RecordDecl->hasMoveAssignment())) {
const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment();
const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor();
const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_clang_library(clangTidyPlugin
add_clang_library(clangTidyPlugin STATIC
ClangTidyPlugin.cpp

LINK_LIBS
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/portability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ set(LLVM_LINK_COMPONENTS
TargetParser
)

add_clang_library(clangTidyPortabilityModule
add_clang_library(clangTidyPortabilityModule STATIC
PortabilityTidyModule.cpp
RestrictSystemIncludesCheck.cpp
SIMDIntrinsicsCheck.cpp
StdAllocatorConstCheck.cpp
TemplateVirtualMemberFunctionCheck.cpp

LINK_LIBS
clangTidy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "RestrictSystemIncludesCheck.h"
#include "SIMDIntrinsicsCheck.h"
#include "StdAllocatorConstCheck.h"
#include "TemplateVirtualMemberFunctionCheck.h"

namespace clang::tidy {
namespace portability {
Expand All @@ -25,6 +26,8 @@ class PortabilityModule : public ClangTidyModule {
"portability-simd-intrinsics");
CheckFactories.registerCheck<StdAllocatorConstCheck>(
"portability-std-allocator-const");
CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
"portability-template-virtual-member-function");
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===--- TemplateVirtualMemberFunctionCheck.cpp - clang-tidy --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "TemplateVirtualMemberFunctionCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::portability {
namespace {
AST_MATCHER(CXXMethodDecl, isUsed) { return Node.isUsed(); }
} // namespace

void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
unless(isExplicitTemplateSpecialization()))
.bind("specialization")),
isVirtual(), unless(isUsed()),
unless(cxxDestructorDecl(isDefaulted())))
.bind("method"),
this);
}

void TemplateVirtualMemberFunctionCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *ImplicitSpecialization =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
const auto *MethodDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("method");

diag(MethodDecl->getLocation(),
"unspecified virtual member function instantiation; the virtual "
"member function is not instantiated but it might be with a "
"different compiler");
diag(ImplicitSpecialization->getPointOfInstantiation(),
"template instantiated here", DiagnosticIDs::Note);
}

} // namespace clang::tidy::portability
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--- TemplateVirtualMemberFunctionCheck.h - clang-tidy ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::portability {

/// Upon instantiating a template class, non-virtual member functions don't have
/// to be instantiated unless they are used. Virtual member function
/// instantiation on the other hand is unspecified and depends on the
/// implementation of the compiler. This check intends to find cases when a
/// virtual member function is not instantiated but it might be with a different
/// compiler.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
public:
TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
};

} // namespace clang::tidy::portability

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/readability/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyReadabilityModule
add_clang_library(clangTidyReadabilityModule STATIC
AvoidConstParamsInDecls.cpp
AvoidNestedConditionalOperatorCheck.cpp
AvoidReturnWithVoidValueCheck.cpp
Expand Down
32 changes: 18 additions & 14 deletions clang-tools-extra/clang-tidy/rename_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
#
# ===-----------------------------------------------------------------------===#

from __future__ import unicode_literals

import argparse
import glob
import io
import os
import re
import sys
from typing import List


def replaceInFileRegex(fileName, sFrom, sTo):
def replaceInFileRegex(fileName: str, sFrom: str, sTo: str) -> None:
if sFrom == sTo:
return

Expand All @@ -35,7 +35,7 @@ def replaceInFileRegex(fileName, sFrom, sTo):
f.write(txt)


def replaceInFile(fileName, sFrom, sTo):
def replaceInFile(fileName: str, sFrom: str, sTo: str) -> None:
if sFrom == sTo:
return
txt = None
Expand All @@ -51,7 +51,7 @@ def replaceInFile(fileName, sFrom, sTo):
f.write(txt)


def generateCommentLineHeader(filename):
def generateCommentLineHeader(filename: str) -> str:
return "".join(
[
"//===--- ",
Expand All @@ -63,7 +63,7 @@ def generateCommentLineHeader(filename):
)


def generateCommentLineSource(filename):
def generateCommentLineSource(filename: str) -> str:
return "".join(
[
"//===--- ",
Expand All @@ -75,7 +75,7 @@ def generateCommentLineSource(filename):
)


def fileRename(fileName, sFrom, sTo):
def fileRename(fileName: str, sFrom: str, sTo: str) -> str:
if sFrom not in fileName or sFrom == sTo:
return fileName
newFileName = fileName.replace(sFrom, sTo)
Expand All @@ -84,7 +84,7 @@ def fileRename(fileName, sFrom, sTo):
return newFileName


def deleteMatchingLines(fileName, pattern):
def deleteMatchingLines(fileName: str, pattern: str) -> bool:
lines = None
with io.open(fileName, "r", encoding="utf8") as f:
lines = f.readlines()
Expand All @@ -101,7 +101,7 @@ def deleteMatchingLines(fileName, pattern):
return True


def getListOfFiles(clang_tidy_path):
def getListOfFiles(clang_tidy_path: str) -> List[str]:
files = glob.glob(os.path.join(clang_tidy_path, "**"), recursive=True)
files += [
os.path.normpath(os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst"))
Expand All @@ -124,7 +124,7 @@ def getListOfFiles(clang_tidy_path):

# Adapts the module's CMakelist file. Returns 'True' if it could add a new
# entry and 'False' if the entry already existed.
def adapt_cmake(module_path, check_name_camel):
def adapt_cmake(module_path: str, check_name_camel: str) -> bool:
filename = os.path.join(module_path, "CMakeLists.txt")
with io.open(filename, "r", encoding="utf8") as f:
lines = f.readlines()
Expand Down Expand Up @@ -153,7 +153,9 @@ def adapt_cmake(module_path, check_name_camel):


# Modifies the module to include the new check.
def adapt_module(module_path, module, check_name, check_name_camel):
def adapt_module(
module_path: str, module: str, check_name: str, check_name_camel: str
) -> None:
modulecpp = next(
iter(
filter(
Expand Down Expand Up @@ -204,7 +206,9 @@ def adapt_module(module_path, module, check_name, check_name_camel):


# Adds a release notes entry.
def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
def add_release_notes(
clang_tidy_path: str, old_check_name: str, new_check_name: str
) -> None:
filename = os.path.normpath(
os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst")
)
Expand Down Expand Up @@ -262,7 +266,7 @@ def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
f.write(line)


def main():
def main() -> None:
parser = argparse.ArgumentParser(description="Rename clang-tidy check.")
parser.add_argument("old_check_name", type=str, help="Old check name.")
parser.add_argument("new_check_name", type=str, help="New check name.")
Expand Down Expand Up @@ -311,7 +315,7 @@ def main():
"Check name '%s' not found in %s. Exiting."
% (check_name_camel, cmake_lists)
)
return 1
sys.exit(1)

modulecpp = next(
iter(
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set(LLVM_LINK_COMPONENTS
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES ClangTidyMain.cpp ClangTidyToolMain.cpp)

add_clang_library(clangTidyMain
add_clang_library(clangTidyMain STATIC
ClangTidyMain.cpp

LINK_LIBS
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyUtils
add_clang_library(clangTidyUtils STATIC
Aliasing.cpp
ASTUtils.cpp
BracesAroundStatement.cpp
Expand Down
65 changes: 60 additions & 5 deletions clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/FixIt.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -195,11 +196,10 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) {
return false;
}

FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
const Configuration ConfigIn,
const LangOptions &LO)
FormatStringConverter::FormatStringConverter(
ASTContext *ContextIn, const CallExpr *Call, unsigned FormatArgOffset,
const Configuration ConfigIn, const LangOptions &LO, SourceManager &SM,
Preprocessor &PP)
: Context(ContextIn), Config(ConfigIn),
CastMismatchedIntegerTypes(
castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)),
Expand All @@ -208,11 +208,22 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
assert(ArgsOffset <= NumArgs);
FormatExpr = llvm::dyn_cast<StringLiteral>(
Args[FormatArgOffset]->IgnoreImplicitAsWritten());

if (!FormatExpr || !FormatExpr->isOrdinary()) {
// Function must have a narrow string literal as its first argument.
conversionNotPossible("first argument is not a narrow string literal");
return;
}

if (const std::optional<StringRef> MaybeMacroName =
formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
MaybeMacroName) {
conversionNotPossible(
("format string contains unreplaceable macro '" + *MaybeMacroName + "'")
.str());
return;
}

PrintfFormatString = FormatExpr->getString();

// Assume that the output will be approximately the same size as the input,
Expand All @@ -230,6 +241,50 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
finalizeFormatText();
}

std::optional<StringRef>
FormatStringConverter::formatStringContainsUnreplaceableMacro(
const CallExpr *Call, const StringLiteral *FormatExpr, SourceManager &SM,
Preprocessor &PP) {
// If a macro invocation surrounds the entire call then we don't want that to
// inhibit conversion. The whole format string will appear to come from that
// macro, as will the function call.
std::optional<StringRef> MaybeSurroundingMacroName;
if (SourceLocation BeginCallLoc = Call->getBeginLoc();
BeginCallLoc.isMacroID())
MaybeSurroundingMacroName =
Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());

for (auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();
I != E; ++I) {
const SourceLocation &TokenLoc = *I;
if (TokenLoc.isMacroID()) {
const StringRef MacroName =
Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());

if (MaybeSurroundingMacroName != MacroName) {
// glibc uses __PRI64_PREFIX and __PRIPTR_PREFIX to define the prefixes
// for types that change size so we must look for multiple prefixes.
if (!MacroName.starts_with("PRI") && !MacroName.starts_with("__PRI"))
return MacroName;

const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
const OptionalFileEntryRef MaybeFileEntry =
SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
if (!MaybeFileEntry)
return MacroName;

HeaderSearch &HS = PP.getHeaderSearchInfo();
// Check if the file is a system header
if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
llvm::sys::path::filename(MaybeFileEntry->getName()) !=
"inttypes.h")
return MacroName;
}
}
}
return std::nullopt;
}

void FormatStringConverter::emitAlignment(const PrintfSpecifier &FS,
std::string &FormatSpec) {
ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
Expand Down
7 changes: 6 additions & 1 deletion clang-tools-extra/clang-tidy/utils/FormatStringConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class FormatStringConverter

FormatStringConverter(ASTContext *Context, const CallExpr *Call,
unsigned FormatArgOffset, Configuration Config,
const LangOptions &LO);
const LangOptions &LO, SourceManager &SM,
Preprocessor &PP);

bool canApply() const { return ConversionNotPossibleReason.empty(); }
const std::string &conversionNotPossibleReason() const {
Expand Down Expand Up @@ -110,6 +111,10 @@ class FormatStringConverter

void appendFormatText(StringRef Text);
void finalizeFormatText();
static std::optional<StringRef>
formatStringContainsUnreplaceableMacro(const CallExpr *CallExpr,
const StringLiteral *FormatExpr,
SourceManager &SM, Preprocessor &PP);
bool conversionNotPossible(std::string Reason) {
ConversionNotPossibleReason = std::move(Reason);
return false;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/zircon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)

add_clang_library(clangTidyZirconModule
add_clang_library(clangTidyZirconModule STATIC
TemporaryObjectsCheck.cpp
ZirconTidyModule.cpp

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ endif()
include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/../clang-tidy")
include_directories(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/../include-cleaner/include")

add_clang_library(clangDaemon
add_clang_library(clangDaemon STATIC
AST.cpp
ASTSignals.cpp
ClangdLSPServer.cpp
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/Headers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ class IncludeStructure::RecordHeaders : public PPCallbacks {
IDs.push_back(HID);
}
}
Out->MainFileIncludesBySpelling.try_emplace(Inc.Written)
.first->second.push_back(Out->MainFileIncludes.size() - 1);
Out->MainFileIncludesBySpelling[Inc.Written].push_back(
Out->MainFileIncludes.size() - 1);
}

// Record include graph (not just for main-file includes)
Expand Down
3 changes: 1 addition & 2 deletions clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2282,8 +2282,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
return;
}
auto It = CallsIn.try_emplace(R.Container, std::vector<Range>{}).first;
It->second.push_back(Loc->range);
CallsIn[R.Container].push_back(Loc->range);

ContainerLookup.IDs.insert(R.Container);
});
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/index/remote/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if (CLANGD_ENABLE_REMOTE)
# target-local?
add_definitions(-DGOOGLE_PROTOBUF_NO_RTTI=1)

add_clang_library(clangdRemoteIndex
add_clang_library(clangdRemoteIndex STATIC
Client.cpp

LINK_LIBS
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_clang_library(clangdRemoteMarshalling
add_clang_library(clangdRemoteMarshalling STATIC
Marshalling.cpp

LINK_LIBS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../)
# When compiled without Remote Index support, the real implementation index
# client is not present. Users will get a notification about this when trying
# to connect to remote index server instance.
add_clang_library(clangdRemoteIndex
add_clang_library(clangdRemoteIndex STATIC
UnimplementedClient.cpp

LINK_LIBS
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB OR NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
list(APPEND CLANGD_ATOMIC_LIB "atomic")
endif()

add_clang_library(clangdSupport
add_clang_library(clangdSupport STATIC
Bracket.cpp
Cancellation.cpp
Context.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES ClangdToolMain.cpp)

add_clang_library(clangdMain
add_clang_library(clangdMain STATIC
ClangdMain.cpp
Check.cpp
)
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/xpc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ set(LLVM_LINK_COMPONENTS
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES Conversion.cpp XPCTransport.cpp)

add_clang_library(clangdXpcJsonConversions
add_clang_library(clangdXpcJsonConversions STATIC
Conversion.cpp
LINK_LIBS clangDaemon clangdSupport
)

add_clang_library(clangdXpcTransport
add_clang_library(clangdXpcTransport STATIC
XPCTransport.cpp
LINK_LIBS clangDaemon clangdSupport clangdXpcJsonConversions
DEPENDS ClangDriverOptions
Expand Down
20 changes: 18 additions & 2 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ New checks
Gives warnings for tagged unions, where the number of tags is
different from the number of data members inside the union.

- New :doc:`portability-template-virtual-member-function
<clang-tidy/checks/portability/template-virtual-member-function>` check.

Finds cases when an uninstantiated virtual member function in a template class
causes cross-compiler incompatibility.

New check aliases
^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -196,18 +202,28 @@ Changes in existing checks
<clang-tidy/checks/modernize/use-nullptr>` check to also recognize
``NULL``/``__null`` (but not ``0``) when used with a templated type.

- Improved :doc:`modernize-use-starts-ends-with
<clang-tidy/checks/modernize/use-starts-ends-with>` check to handle two cases
that can be replaced with ``ends_with``

- Improved :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check to support replacing
member function calls too.
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<inttypes.h>`` in the format string.

- Improved :doc:`modernize-use-std-print
<clang-tidy/checks/modernize/use-std-print>` check to support replacing
member function calls too.
member function calls too and to only expand macros starting with ``PRI``
and ``__PRI`` from ``<inttypes.h>`` in the format string.

- Improved :doc:`performance-avoid-endl
<clang-tidy/checks/performance/avoid-endl>` check to use ``std::endl`` as
placeholder when lexer cannot get source text.

- Improved :doc:`performance-move-const-arg
<clang-tidy/checks/performance/move-const-arg>` check to fix a crash when
an argument type is declared but not defined.

- Improved :doc:`readability-container-contains
<clang-tidy/checks/readability/container-contains>` check to let it work on
any class that has a ``contains`` method.
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ Clang-Tidy Checks
:doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
:doc:`portability-template-virtual-member-function <portability/template-virtual-member-function>`,
:doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
:doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
:doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ modernize-use-starts-ends-with
==============================

Checks for common roundabout ways to express ``starts_with`` and ``ends_with``
and suggests replacing with ``starts_with`` when the method is available.
Notably, this will work with ``std::string`` and ``std::string_view``.
and suggests replacing with the simpler method when it is available. Notably,
this will work with ``std::string`` and ``std::string_view``.

.. code-block:: c++

std::string s = "...";
if (s.find("prefix") == 0) { /* do something */ }
if (s.rfind("prefix", 0) == 0) { /* do something */ }
if (s.compare(0, strlen("prefix"), "prefix") == 0) { /* do something */ }
if (s.compare(s.size() - strlen("suffix"), strlen("suffix"), "suffix") == 0) {
/* do something */
}
if (s.rfind("suffix") == (s.length() - 6)) {
/* do something */
}
becomes

Expand All @@ -22,3 +28,5 @@ becomes
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.starts_with("prefix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
if (s.ends_with("suffix")) { /* do something */ }
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ into:

The check uses the same format-string-conversion algorithm as
`modernize-use-std-print <../modernize/use-std-print.html>`_ and its
shortcomings are described in the documentation for that check.
shortcomings and behaviour in combination with macros are described in the
documentation for that check.

Options
-------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,15 @@ into:
std::println(stderr, "The {} is {:3}", description, value);

If the `ReplacementPrintFunction` or `ReplacementPrintlnFunction` options
are left, or assigned to their default values then this check is only
enabled with `-std=c++23` or later.
are left at or set to their default values then this check is only enabled
with `-std=c++23` or later.

Macros starting with ``PRI`` and ``__PRI`` from `<inttypes.h>` are
expanded, escaping is handled and adjacent strings are concatenated to form
a single ``StringLiteral`` before the format string is converted. Use of
any other macros in the format string will cause a warning message to be
emitted and no conversion will be performed. The converted format string
will always be a single string literal.

The check doesn't do a bad job, but it's not perfect. In particular:

Expand All @@ -34,13 +41,10 @@ The check doesn't do a bad job, but it's not perfect. In particular:
possible.

- At the point that the check runs, the AST contains a single
``StringLiteral`` for the format string and any macro expansion, token
pasting, adjacent string literal concatenation and escaping has been
handled. Although it's possible for the check to automatically put the
escapes back, they may not be exactly as they were written (e.g.
``"\x0a"`` will become ``"\n"`` and ``"ab" "cd"`` will become
``"abcd"``.) This is helpful since it means that the ``PRIx`` macros from
``<inttypes.h>`` are removed correctly.
``StringLiteral`` for the format string where escapes have been expanded.
The check tries to reconstruct escape sequences, they may not be the same
as they were written (e.g. ``"\x41\x0a"`` will become ``"A\n"`` and
``"ab" "cd"`` will become ``"abcd"``.)

- It supports field widths, precision, positional arguments, leading zeros,
leading ``+``, alignment and alternative forms.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.. title:: clang-tidy - portability-template-virtual-member-function

portability-template-virtual-member-function
============================================

Finds cases when an uninstantiated virtual member function in a template class causes
cross-compiler incompatibility.

Upon instantiating a template class, non-virtual member functions don't have to be
instantiated unless they are used. Virtual member function instantiation on the other hand
is unspecified and depends on the implementation of the compiler.

In the following snippets the virtual member function is not instantiated by GCC and Clang,
but it is instantiated by MSVC, so while the snippet is accepted by the former compilers,
it is rejected by the latter.

.. code:: c++

template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;

static void used() {}

virtual void unused() {
T MSVCError = this;
};
};

int main() {
CrossPlatformError<int>::used();
return 0;
}

Cross-platform projects that need to support MSVC on Windows might see compiler errors
because certain virtual member functions are instantiated, which are not instantiated
by other compilers on other platforms. This check highlights such virtual member functions.
2 changes: 1 addition & 1 deletion clang-tools-extra/include-cleaner/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
set(LLVM_LINK_COMPONENTS Support)

add_clang_library(clangIncludeCleaner
add_clang_library(clangIncludeCleaner STATIC
Analysis.cpp
IncludeSpeller.cpp
FindHeaders.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,46 @@ typedef __UINT32_TYPE__ uint32_t;
typedef __UINT16_TYPE__ uint16_t;
typedef __UINT8_TYPE__ uint8_t;

#define PRIdMAX "lld"
#define PRId64 "lld"
#if __WORDSIZE == 64
# define __PRI64_PREFIX "l"
#else
# define __PRI64_PREFIX "ll"
#endif

#define PRIdMAX __PRI64_PREFIX "d"
#define PRId64 __PRI64_PREFIX "d"
#define PRId32 "d"
#define PRId16 "hd"
#define PRId8 "hhd"

#define PRIiMAX "lli"
#define PRIi64 "lli"
#define PRIiMAX __PRI64_PREFIX "i"
#define PRIi64 __PRI64_PREFIX "i"
#define PRIi32 "i"
#define PRIi16 "hi"
#define PRIi8 "hhi"

#define PRIiFAST64 "lli"
#define PRIiFAST64 __PRI64_PREFIX "i"
#define PRIiFAST32 "i"
#define PRIiFAST16 "hi"
#define PRIiFAST8 "hhi"

#define PRIiLEAST64 "lli"
#define PRIiLEAST64 __PRI64_PREFIX "i"
#define PRIiLEAST32 "i"
#define PRIiLEAST16 "hi"
#define PRIiLEAST8 "hhi"

#define PRIuMAX "llu"
#define PRIu64 "llu"
#define PRIuMAX __PRI64_PREFIX "u"
#define PRIu64 __PRI64_PREFIX "u"
#define PRIu32 "u"
#define PRIu16 "hu"
#define PRIu8 "hhu"

#define PRIuFAST64 "llu"
#define PRIuFAST64 __PRI64_PREFIX "u"
#define PRIuFAST32 "u"
#define PRIuFAST16 "hu"
#define PRIuFAST8 "hhu"

#define PRIuLEAST64 "llu"
#define PRIuLEAST64 __PRI64_PREFIX "u"
#define PRIuLEAST32 "u"
#define PRIuLEAST16 "hu"
#define PRIuLEAST8 "hhu"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ struct basic_string {
constexpr bool starts_with(C ch) const noexcept;
constexpr bool starts_with(const C* s) const;

constexpr bool ends_with(std::basic_string_view<C, T> sv) const noexcept;
constexpr bool ends_with(C ch) const noexcept;
constexpr bool ends_with(const C* s) const;

_Type& operator[](size_type);
const _Type& operator[](size_type) const;

Expand Down Expand Up @@ -108,6 +112,10 @@ struct basic_string_view {
constexpr bool starts_with(C ch) const noexcept;
constexpr bool starts_with(const C* s) const;

constexpr bool ends_with(basic_string_view sv) const noexcept;
constexpr bool ends_with(C ch) const noexcept;
constexpr bool ends_with(const C* s) const;

constexpr int compare(basic_string_view sv) const noexcept;

static constexpr size_t npos = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,62 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
// CHECK-FIXES: !s.starts_with(sv);

s.compare(s.size() - 6, 6, "suffix") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

s.compare(s.size() - 6, strlen("abcdef"), "suffix") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

std::string suffix = "suffix";
s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind("suffix") == s.size() - 6;
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

s.rfind("suffix") == s.size() - strlen("suffix");
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with("suffix");

s.rfind(suffix) == s.size() - suffix.size();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix, std::string::npos) == s.size() - suffix.size();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix) == (s.size() - suffix.size());
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix, s.npos) == (s.size() - suffix.size());
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix, s.npos) == (((s.size()) - (suffix.size())));
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

s.rfind(suffix) != s.size() - suffix.size();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: !s.ends_with(suffix);

(s.size() - suffix.size()) == s.rfind(suffix);
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: s.ends_with(suffix);

struct S {
std::string s;
} t;
t.s.rfind(suffix) == (t.s.size() - suffix.size());
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
// CHECK-FIXES: t.s.ends_with(suffix);

// Expressions that don't trigger the check are here.
#define EQ(x, y) ((x) == (y))
EQ(s.find("a"), 0);
Expand All @@ -219,4 +275,5 @@ void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss,
STARTS_WITH_COMPARE(s, s) == 0;

s.compare(0, 1, "ab") == 0;
s.rfind(suffix, 1) == s.size() - suffix.size();
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// RUN: %check_clang_tidy \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: {StrictMode: true}}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: -- -isystem %clang_tidy_headers \
// RUN: -DPRI_CMDLINE_MACRO="\"s\"" \
// RUN: -D__PRI_CMDLINE_MACRO="\"s\""
// RUN: %check_clang_tidy \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: {StrictMode: false}}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: -- -isystem %clang_tidy_headers \
// RUN: -DPRI_CMDLINE_MACRO="\"s\"" \
// RUN: -D__PRI_CMDLINE_MACRO="\"s\""
#include <string>
// CHECK-FIXES: #include <format>
#include <inttypes.h>

namespace absl
{
Expand Down Expand Up @@ -102,18 +107,74 @@ std::string StrFormat_macros() {
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {}", 42);

// The format string is replaced even though it comes from a macro, this
// behaviour is required so that that <inttypes.h> macros are replaced.
#define FORMAT_STRING "Hello %s"
auto s2 = absl::StrFormat(FORMAT_STRING, 42);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {}", 42);

// Arguments that are macros aren't replaced with their value, even if they are rearranged.
#define VALUE 3.14159265358979323846
#define WIDTH 10
#define PRECISION 4
auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION);

const uint64_t u64 = 42;
const uint32_t u32 = 32;
std::string s;

auto s4 = absl::StrFormat("Replaceable macro at end %" PRIu64, u64);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Replaceable macro at end {}", u64);

auto s5 = absl::StrFormat("Replaceable macros in middle %" PRIu64 " %" PRIu32 "\n", u64, u32);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: std::format("Replaceable macros in middle {} {}\n", u64, u32);

// These need PRI and __PRI prefixes so that the check get as far as looking for
// where the macro comes from.
#define PRI_FMT_MACRO "s"
#define __PRI_FMT_MACRO "s"

auto s6 = absl::StrFormat("Unreplaceable macro at end %" PRI_FMT_MACRO, s.c_str());
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format]

auto s7 = absl::StrFormat(__PRI_FMT_MACRO " Unreplaceable macro at beginning %s", s);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_FMT_MACRO' [modernize-use-std-format]

auto s8 = absl::StrFormat("Unreplacemable macro %" PRI_FMT_MACRO " in the middle", s);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format]

auto s9 = absl::StrFormat("First macro is replaceable %" PRIu64 " but second one is not %" __PRI_FMT_MACRO, u64, s);
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_FMT_MACRO' [modernize-use-std-format]

// Needs a PRI prefix so that we get as far as looking for where the macro comes from
auto s10 = absl::StrFormat(" macro from command line %" PRI_CMDLINE_MACRO, s);
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_CMDLINE_MACRO' [modernize-use-std-format]

// Needs a __PRI prefix so that we get as far as looking for where the macro comes from
auto s11 = absl::StrFormat(" macro from command line %" __PRI_CMDLINE_MACRO, s);
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_CMDLINE_MACRO' [modernize-use-std-format]

// We ought to be able to fix this since the macro surrounds the whole call
// and therefore can't change the format string independently. This is
// required to be able to fix calls inside Catch2 macros for example.
#define SURROUND_ALL(x) x
auto s12 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation %" PRIu64, u64));
// CHECK-MESSAGES: [[@LINE-1]]:27: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: auto s12 = SURROUND_ALL(std::format("Macro surrounding entire invocation {}", u64));

// But having that surrounding macro shouldn't stop us ignoring an
// unreplaceable macro elsewhere.
auto s13 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation with unreplaceable macro %" PRI_FMT_MACRO, s));
// CHECK-MESSAGES: [[@LINE-1]]:27: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format]

// At the moment at least the check will replace occurrences where the
// function name is the result of expanding a macro.
#define SURROUND_FUNCTION_NAME(x) absl:: x
auto s14 = SURROUND_FUNCTION_NAME(StrFormat)("Hello %d", 4442);
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
// CHECK-FIXES: auto s14 = std::format("Hello {}", 4442);

// We can't safely fix occurrences where the macro may affect the format
// string differently in different builds.
#define SURROUND_FORMAT(x) "!" x
auto s15 = absl::StrFormat(SURROUND_FORMAT("Hello %d"), 4443);
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'SURROUND_FORMAT' [modernize-use-std-format]
}
Loading