105 changes: 105 additions & 0 deletions .github/workflows/restart-preempted-libcxx-jobs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Restart Preempted Libc++ Workflow

# The libc++ builders run on preemptable VMs, which can be shutdown at any time.
# This workflow identifies when a workflow run was canceled due to the VM being preempted,
# and restarts the workflow run.

# We identify a canceled workflow run by checking the annotations of the check runs in the check suite,
# which should contain the message "The runner has received a shutdown signal."

# Note: If a job is both preempted and also contains a non-preemption failure, we do not restart the workflow.

on:
workflow_run:
workflows: [Build and Test libc\+\+]
types:
- completed

permissions:
contents: read

jobs:
restart:
if: github.repository_owner == 'llvm' && (github.event.workflow_run.conclusion == 'failure' || github.event.workflow_run.conclusion == 'cancelled')
name: "Restart Job"
permissions:
statuses: read
checks: read
actions: write
runs-on: ubuntu-latest
steps:
- name: "Restart Job"
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
with:
script: |
const failure_regex = /Process completed with exit code 1./
const preemption_regex = /The runner has received a shutdown signal/
console.log('Listing check runs for suite')
const check_suites = await github.rest.checks.listForSuite({
owner: context.repo.owner,
repo: context.repo.repo,
check_suite_id: context.payload.workflow_run.check_suite_id
})
check_run_ids = [];
for (check_run of check_suites.data.check_runs) {
console.log('Checking check run: ' + check_run.id);
if (check_run.status != 'completed') {
console.log('Check run was not completed. Skipping.');
continue;
}
if (check_run.conclusion != 'failure' && check_run.conclusion != 'cancelled') {
console.log('Check run had conclusion: ' + check_run.conclusion + '. Skipping.');
continue;
}
check_run_ids.push(check_run.id);
}
has_preempted_job = false;
for (check_run_id of check_run_ids) {
console.log('Listing annotations for check run: ' + check_run_id);
annotations = await github.rest.checks.listAnnotations({
owner: context.repo.owner,
repo: context.repo.repo,
check_run_id: check_run_id
})
for (annotation of annotations.data) {
if (annotation.annotation_level != 'failure') {
continue;
}
const preemption_match = annotation.message.match(preemption_regex);
if (preemption_match != null) {
console.log('Found preemption message: ' + annotation.message);
has_preempted_job = true;
}
const failure_match = annotation.message.match(failure_regex);
if (failure_match != null) {
// We only want to restart the workflow if all of the failures were due to preemption.
// We don't want to restart the workflow if there were other failures.
console.log('Choosing not to rerun workflow because we found a non-preemption failure');
console.log('Failure message: ' + annotation.message);
return;
}
}
}
if (!has_preempted_job) {
console.log('No preempted jobs found. Not restarting workflow.');
return;
}
console.log("Restarted workflow: " + context.payload.workflow_run.id);
await github.rest.actions.reRunWorkflowFailedJobs({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id
})
4 changes: 3 additions & 1 deletion bolt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
set(LLVM_SUBPROJECT_TITLE "BOLT")

include(ExternalProject)

set(BOLT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
Expand Down Expand Up @@ -121,7 +123,7 @@ option(BOLT_BUILD_TOOLS
"Build the BOLT tools. If OFF, just generate build targets." ON)

add_custom_target(bolt)
set_target_properties(bolt PROPERTIES FOLDER "BOLT")
set_target_properties(bolt PROPERTIES FOLDER "BOLT/Metatargets")
add_llvm_install_targets(install-bolt DEPENDS bolt COMPONENT bolt)

include_directories(
Expand Down
1 change: 0 additions & 1 deletion bolt/cmake/modules/AddBOLT.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ include(LLVMDistributionSupport)

macro(add_bolt_executable name)
add_llvm_executable(${name} ${ARGN})
set_target_properties(${name} PROPERTIES FOLDER "BOLT")
endmacro()

macro(add_bolt_tool name)
Expand Down
5 changes: 5 additions & 0 deletions bolt/docs/BAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,14 @@ equals output offset.
`BRANCHENTRY` bit denotes whether a given offset pair is a control flow source
(branch or call instruction). If not set, it signifies a control flow target
(basic block offset).

`InputAddr` is omitted for equal offsets in input and output function. In this
case, `BRANCHENTRY` bits are encoded separately in a `BranchEntries` bitvector.

Deleted basic blocks are emitted as having `OutputOffset` equal to the size of
the function. They don't affect address translation and only participate in
input basic block mapping.

### Secondary Entry Points table
The table is emitted for hot fragments only. It contains `NumSecEntryPoints`
offsets denoting secondary entry points, delta encoded, implicitly starting at zero.
Expand Down
1 change: 1 addition & 0 deletions bolt/docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ if (LLVM_ENABLE_DOXYGEN)
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating bolt doxygen documentation." VERBATIM)
set_target_properties(doxygen-bolt PROPERTIES FOLDER "BOLT/Docs")

if (LLVM_BUILD_DOCS)
add_dependencies(doxygen doxygen-bolt)
Expand Down
9 changes: 6 additions & 3 deletions bolt/include/bolt/Core/BinaryContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "bolt/Core/BinaryData.h"
#include "bolt/Core/BinarySection.h"
#include "bolt/Core/DebugData.h"
#include "bolt/Core/DynoStats.h"
#include "bolt/Core/JumpTable.h"
#include "bolt/Core/MCPlusBuilder.h"
#include "bolt/RuntimeLibs/RuntimeLibrary.h"
Expand Down Expand Up @@ -359,7 +360,7 @@ class BinaryContext {
void setFileBuildID(StringRef ID) { FileBuildID = std::string(ID); }

bool hasSymbolsWithFileName() const { return HasSymbolsWithFileName; }
void setHasSymbolsWithFileName(bool Value) { HasSymbolsWithFileName = true; }
void setHasSymbolsWithFileName(bool Value) { HasSymbolsWithFileName = Value; }

/// Return true if relocations against symbol with a given name
/// must be created.
Expand Down Expand Up @@ -717,6 +718,9 @@ class BinaryContext {
uint64_t NumStaleBlocksWithEqualIcount{0};
} Stats;

// Original binary execution count stats.
DynoStats InitialDynoStats;

// Address of the first allocated segment.
uint64_t FirstAllocAddress{std::numeric_limits<uint64_t>::max()};

Expand Down Expand Up @@ -1220,8 +1224,7 @@ class BinaryContext {

/// Return a signed value of \p Size stored at \p Address. The address has
/// to be a valid statically allocated address for the binary.
ErrorOr<uint64_t> getSignedValueAtAddress(uint64_t Address,
size_t Size) const;
ErrorOr<int64_t> getSignedValueAtAddress(uint64_t Address, size_t Size) const;

/// Special case of getUnsignedValueAtAddress() that uses a pointer size.
ErrorOr<uint64_t> getPointerAtAddress(uint64_t Address) const {
Expand Down
3 changes: 2 additions & 1 deletion bolt/include/bolt/Core/FunctionLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ class FunctionLayout {
void eraseBasicBlocks(const DenseSet<const BinaryBasicBlock *> ToErase);

/// Make sure fragments' and basic blocks' indices match the current layout.
void updateLayoutIndices();
void updateLayoutIndices() const;
void updateLayoutIndices(ArrayRef<BinaryBasicBlock *> Order) const;

/// Replace the current layout with NewLayout. Uses the block's
/// self-identifying fragment number to assign blocks to infer function
Expand Down
23 changes: 20 additions & 3 deletions bolt/include/bolt/Passes/BinaryPasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,31 @@ class BinaryFunctionPass {
virtual Error runOnFunctions(BinaryContext &BC) = 0;
};

/// A pass to set initial program-wide dynostats.
class DynoStatsSetPass : public BinaryFunctionPass {
public:
DynoStatsSetPass() : BinaryFunctionPass(false) {}

const char *getName() const override {
return "set dyno-stats before optimizations";
}

bool shouldPrint(const BinaryFunction &BF) const override { return false; }

Error runOnFunctions(BinaryContext &BC) override {
BC.InitialDynoStats = getDynoStats(BC.getBinaryFunctions(), BC.isAArch64());
return Error::success();
}
};

/// A pass to print program-wide dynostats.
class DynoStatsPrintPass : public BinaryFunctionPass {
protected:
DynoStats PrevDynoStats;
std::string Title;

public:
DynoStatsPrintPass(const DynoStats &PrevDynoStats, const char *Title)
: BinaryFunctionPass(false), PrevDynoStats(PrevDynoStats), Title(Title) {}
DynoStatsPrintPass(const char *Title)
: BinaryFunctionPass(false), Title(Title) {}

const char *getName() const override {
return "print dyno-stats after optimizations";
Expand All @@ -70,6 +86,7 @@ class DynoStatsPrintPass : public BinaryFunctionPass {
bool shouldPrint(const BinaryFunction &BF) const override { return false; }

Error runOnFunctions(BinaryContext &BC) override {
const DynoStats PrevDynoStats = BC.InitialDynoStats;
const DynoStats NewDynoStats =
getDynoStats(BC.getBinaryFunctions(), BC.isAArch64());
const bool Changed = (NewDynoStats != PrevDynoStats);
Expand Down
41 changes: 15 additions & 26 deletions bolt/include/bolt/Passes/MCF.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,14 @@
#ifndef BOLT_PASSES_MCF_H
#define BOLT_PASSES_MCF_H

#include "bolt/Passes/BinaryPasses.h"
#include "llvm/Support/CommandLine.h"

namespace llvm {
namespace bolt {

class BinaryFunction;
class DataflowInfoManager;

enum MCFCostFunction : char {
MCF_DISABLE = 0,
MCF_LINEAR,
MCF_QUADRATIC,
MCF_LOG,
MCF_BLAMEFTS
};

/// Implement the idea in "SamplePGO - The Power of Profile Guided Optimizations
/// without the Usability Burden" by Diego Novillo to make basic block counts
/// equal if we show that A dominates B, B post-dominates A and they are in the
Expand All @@ -31,23 +25,18 @@ void equalizeBBCounts(DataflowInfoManager &Info, BinaryFunction &BF);

/// Fill edge counts based on the basic block count. Used in nonLBR mode when
/// we only have bb count.
void estimateEdgeCounts(BinaryFunction &BF);

/// Entry point for computing a min-cost flow for the CFG with the goal
/// of fixing the flow of the CFG edges, that is, making sure it obeys the
/// flow-conservation equation SumInEdges = SumOutEdges.
///
/// To do this, we create an instance of the min-cost flow problem in a
/// similar way as the one discussed in the work of Roy Levin "Completing
/// Incomplete Edge Profile by Applying Minimum Cost Circulation Algorithms".
/// We do a few things differently, though. We don't populate edge counts using
/// weights coming from a static branch prediction technique and we don't
/// use the same cost function.
///
/// If cost function BlameFTs is used, assign all remaining flow to
/// fall-throughs. This is used when the sampling is based on taken branches
/// that do not account for them.
void solveMCF(BinaryFunction &BF, MCFCostFunction CostFunction);
class EstimateEdgeCounts : public BinaryFunctionPass {
void runOnFunction(BinaryFunction &BF);

public:
explicit EstimateEdgeCounts(const cl::opt<bool> &PrintPass)
: BinaryFunctionPass(PrintPass) {}

const char *getName() const override { return "estimate-edge-counts"; }

/// Pass entry point
Error runOnFunctions(BinaryContext &BC) override;
};

} // end namespace bolt
} // end namespace llvm
Expand Down
49 changes: 24 additions & 25 deletions bolt/include/bolt/Profile/BoltAddressTranslation.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class BinaryFunction;
class BoltAddressTranslation {
public:
// In-memory representation of the address translation table
using MapTy = std::map<uint32_t, uint32_t>;
using MapTy = std::multimap<uint32_t, uint32_t>;

// List of taken fall-throughs
using FallthroughListTy = SmallVector<std::pair<uint64_t, uint64_t>, 16>;
Expand All @@ -90,7 +90,7 @@ class BoltAddressTranslation {
std::error_code parse(raw_ostream &OS, StringRef Buf);

/// Dump the parsed address translation tables
void dump(raw_ostream &OS);
void dump(raw_ostream &OS) const;

/// If the maps are loaded in memory, perform the lookup to translate LBR
/// addresses in function located at \p FuncAddress.
Expand All @@ -107,7 +107,12 @@ class BoltAddressTranslation {

/// If available, fetch the address of the hot part linked to the cold part
/// at \p Address. Return 0 otherwise.
uint64_t fetchParentAddress(uint64_t Address) const;
uint64_t fetchParentAddress(uint64_t Address) const {
auto Iter = ColdPartSource.find(Address);
if (Iter == ColdPartSource.end())
return 0;
return Iter->second;
}

/// True if the input binary has a translation table we can use to convert
/// addresses when aggregating profile
Expand All @@ -132,7 +137,8 @@ class BoltAddressTranslation {
/// emitted for the start of the BB. More entries may be emitted to cover
/// the location of calls or any instruction that may change control flow.
void writeEntriesForBB(MapTy &Map, const BinaryBasicBlock &BB,
uint64_t FuncInputAddress, uint64_t FuncOutputAddress);
uint64_t FuncInputAddress,
uint64_t FuncOutputAddress) const;

/// Write the serialized address translation table for a function.
template <bool Cold>
Expand All @@ -147,7 +153,7 @@ class BoltAddressTranslation {

/// Returns the bitmask with set bits corresponding to indices of BRANCHENTRY
/// entries in function address translation map.
APInt calculateBranchEntriesBitMask(MapTy &Map, size_t EqualElems);
APInt calculateBranchEntriesBitMask(MapTy &Map, size_t EqualElems) const;

/// Calculate the number of equal offsets (output = input - skew) in the
/// beginning of the function.
Expand Down Expand Up @@ -178,14 +184,9 @@ class BoltAddressTranslation {
public:
/// Map basic block input offset to a basic block index and hash pair.
class BBHashMapTy {
class EntryTy {
struct EntryTy {
unsigned Index;
size_t Hash;

public:
unsigned getBBIndex() const { return Index; }
size_t getBBHash() const { return Hash; }
EntryTy(unsigned Index, size_t Hash) : Index(Index), Hash(Hash) {}
};

std::map<uint32_t, EntryTy> Map;
Expand All @@ -201,34 +202,30 @@ class BoltAddressTranslation {
}

unsigned getBBIndex(uint32_t BBInputOffset) const {
return getEntry(BBInputOffset).getBBIndex();
return getEntry(BBInputOffset).Index;
}

size_t getBBHash(uint32_t BBInputOffset) const {
return getEntry(BBInputOffset).getBBHash();
return getEntry(BBInputOffset).Hash;
}

void addEntry(uint32_t BBInputOffset, unsigned BBIndex, size_t BBHash) {
Map.emplace(BBInputOffset, EntryTy(BBIndex, BBHash));
Map.emplace(BBInputOffset, EntryTy{BBIndex, BBHash});
}

size_t getNumBasicBlocks() const { return Map.size(); }

auto begin() const { return Map.begin(); }
auto end() const { return Map.end(); }
auto upper_bound(uint32_t Offset) const { return Map.upper_bound(Offset); }
auto size() const { return Map.size(); }
};

/// Map function output address to its hash and basic blocks hash map.
class FuncHashesTy {
class EntryTy {
struct EntryTy {
size_t Hash;
BBHashMapTy BBHashMap;

public:
size_t getBFHash() const { return Hash; }
const BBHashMapTy &getBBHashMap() const { return BBHashMap; }
EntryTy(size_t Hash) : Hash(Hash) {}
};

std::unordered_map<uint64_t, EntryTy> Map;
Expand All @@ -240,23 +237,23 @@ class BoltAddressTranslation {

public:
size_t getBFHash(uint64_t FuncOutputAddress) const {
return getEntry(FuncOutputAddress).getBFHash();
return getEntry(FuncOutputAddress).Hash;
}

const BBHashMapTy &getBBHashMap(uint64_t FuncOutputAddress) const {
return getEntry(FuncOutputAddress).getBBHashMap();
return getEntry(FuncOutputAddress).BBHashMap;
}

void addEntry(uint64_t FuncOutputAddress, size_t BFHash) {
Map.emplace(FuncOutputAddress, EntryTy(BFHash));
Map.emplace(FuncOutputAddress, EntryTy{BFHash, BBHashMapTy()});
}

size_t getNumFunctions() const { return Map.size(); };

size_t getNumBasicBlocks() const {
size_t NumBasicBlocks{0};
for (auto &I : Map)
NumBasicBlocks += I.second.getBBHashMap().getNumBasicBlocks();
NumBasicBlocks += I.second.BBHashMap.getNumBasicBlocks();
return NumBasicBlocks;
}
};
Expand All @@ -278,7 +275,9 @@ class BoltAddressTranslation {

/// Returns the number of basic blocks in a function.
size_t getNumBasicBlocks(uint64_t OutputAddress) const {
return NumBasicBlocksMap.at(OutputAddress);
auto It = NumBasicBlocksMap.find(OutputAddress);
assert(It != NumBasicBlocksMap.end());
return It->second;
}

private:
Expand Down
4 changes: 4 additions & 0 deletions bolt/include/bolt/Rewrite/RewriteInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Regex.h"
#include <map>
#include <set>
#include <unordered_map>
Expand Down Expand Up @@ -596,6 +597,9 @@ class RewriteInstance {

NameResolver NR;

// Regex object matching split function names.
const Regex FunctionFragmentTemplate{"(.*)\\.(cold|warm)(\\.[0-9]+)?"};

friend class RewriteInstanceDiff;
};

Expand Down
17 changes: 10 additions & 7 deletions bolt/lib/Core/BinaryContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
AsmInfo(std::move(AsmInfo)), MII(std::move(MII)), STI(std::move(STI)),
InstPrinter(std::move(InstPrinter)), MIA(std::move(MIA)),
MIB(std::move(MIB)), MRI(std::move(MRI)), DisAsm(std::move(DisAsm)),
Logger(Logger) {
Logger(Logger), InitialDynoStats(isAArch64()) {
Relocation::Arch = this->TheTriple->getArch();
RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
Expand Down Expand Up @@ -934,10 +934,13 @@ std::string BinaryContext::generateJumpTableName(const BinaryFunction &BF,
uint64_t Offset = 0;
if (const JumpTable *JT = BF.getJumpTableContainingAddress(Address)) {
Offset = Address - JT->getAddress();
auto Itr = JT->Labels.find(Offset);
if (Itr != JT->Labels.end())
return std::string(Itr->second->getName());
Id = JumpTableIds.at(JT->getAddress());
auto JTLabelsIt = JT->Labels.find(Offset);
if (JTLabelsIt != JT->Labels.end())
return std::string(JTLabelsIt->second->getName());

auto JTIdsIt = JumpTableIds.find(JT->getAddress());
assert(JTIdsIt != JumpTableIds.end());
Id = JTIdsIt->second;
} else {
Id = JumpTableIds[Address] = BF.JumpTables.size();
}
Expand Down Expand Up @@ -2214,8 +2217,8 @@ ErrorOr<uint64_t> BinaryContext::getUnsignedValueAtAddress(uint64_t Address,
return DE.getUnsigned(&ValueOffset, Size);
}

ErrorOr<uint64_t> BinaryContext::getSignedValueAtAddress(uint64_t Address,
size_t Size) const {
ErrorOr<int64_t> BinaryContext::getSignedValueAtAddress(uint64_t Address,
size_t Size) const {
const ErrorOr<const BinarySection &> Section = getSectionForAddress(Address);
if (!Section)
return std::make_error_code(std::errc::bad_address);
Expand Down
4 changes: 3 additions & 1 deletion bolt/lib/Core/BinaryEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,9 @@ void BinaryEmitter::emitJumpTable(const JumpTable &JT, MCSection *HotSection,
// determining its destination.
std::map<MCSymbol *, uint64_t> LabelCounts;
if (opts::JumpTables > JTS_SPLIT && !JT.Counts.empty()) {
MCSymbol *CurrentLabel = JT.Labels.at(0);
auto It = JT.Labels.find(0);
assert(It != JT.Labels.end());
MCSymbol *CurrentLabel = It->second;
uint64_t CurrentLabelCount = 0;
for (unsigned Index = 0; Index < JT.Entries.size(); ++Index) {
auto LI = JT.Labels.find(Index * JT.EntrySize);
Expand Down
23 changes: 17 additions & 6 deletions bolt/lib/Core/BinaryFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,15 +851,19 @@ BinaryFunction::processIndirectBranch(MCInst &Instruction, unsigned Size,
return IndirectBranchType::UNKNOWN;
}

// RIP-relative addressing should be converted to symbol form by now
// in processed instructions (but not in jump).
if (DispExpr) {
auto getExprValue = [&](const MCExpr *Expr) {
const MCSymbol *TargetSym;
uint64_t TargetOffset;
std::tie(TargetSym, TargetOffset) = BC.MIB->getTargetSymbolInfo(DispExpr);
std::tie(TargetSym, TargetOffset) = BC.MIB->getTargetSymbolInfo(Expr);
ErrorOr<uint64_t> SymValueOrError = BC.getSymbolValue(*TargetSym);
assert(SymValueOrError && "global symbol needs a value");
ArrayStart = *SymValueOrError + TargetOffset;
assert(SymValueOrError && "Global symbol needs a value");
return *SymValueOrError + TargetOffset;
};

// RIP-relative addressing should be converted to symbol form by now
// in processed instructions (but not in jump).
if (DispExpr) {
ArrayStart = getExprValue(DispExpr);
BaseRegNum = BC.MIB->getNoRegister();
if (BC.isAArch64()) {
ArrayStart &= ~0xFFFULL;
Expand Down Expand Up @@ -3698,6 +3702,13 @@ BinaryFunction::BasicBlockListType BinaryFunction::dfs() const {

size_t BinaryFunction::computeHash(bool UseDFS, HashFunction HashFunction,
OperandHashFuncTy OperandHashFunc) const {
LLVM_DEBUG({
dbgs() << "BOLT-DEBUG: computeHash " << getPrintName() << ' '
<< (UseDFS ? "dfs" : "bin") << " order "
<< (HashFunction == HashFunction::StdHash ? "std::hash" : "xxh3")
<< '\n';
});

if (size() == 0)
return 0;

Expand Down
4 changes: 2 additions & 2 deletions bolt/lib/Core/DebugNames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ void DWARF5AcceleratorTable::addUnit(DWARFUnit &Unit,
// Returns true if DW_TAG_variable should be included in .debug-names based on
// section 6.1.1.1 for DWARF5 spec.
static bool shouldIncludeVariable(const DWARFUnit &Unit, const DIE &Die) {
if (Die.findAttribute(dwarf::Attribute::DW_AT_declaration))
return false;
const DIEValue LocAttrInfo =
Die.findAttribute(dwarf::Attribute::DW_AT_location);
if (!LocAttrInfo)
Expand Down Expand Up @@ -148,6 +146,8 @@ static bool shouldIncludeVariable(const DWARFUnit &Unit, const DIE &Die) {

bool static canProcess(const DWARFUnit &Unit, const DIE &Die,
std::string &NameToUse, const bool TagsOnly) {
if (Die.findAttribute(dwarf::Attribute::DW_AT_declaration))
return false;
switch (Die.getTag()) {
case dwarf::DW_TAG_base_type:
case dwarf::DW_TAG_class_type:
Expand Down
5 changes: 3 additions & 2 deletions bolt/lib/Core/DynoStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ void DynoStats::print(raw_ostream &OS, const DynoStats *Other,
for (auto &Stat : llvm::reverse(SortedHistogram)) {
OS << format("%20s,%'18lld", Printer->getOpcodeName(Stat.second).data(),
Stat.first * opts::DynoStatsScale);

MaxOpcodeHistogramTy MaxMultiMap = OpcodeHistogram.at(Stat.second).second;
auto It = OpcodeHistogram.find(Stat.second);
assert(It != OpcodeHistogram.end());
MaxOpcodeHistogramTy MaxMultiMap = It->second.second;
// Start with function name:BB offset with highest execution count.
for (auto &Max : llvm::reverse(MaxMultiMap)) {
OS << format(", %'18lld, ", Max.first * opts::DynoStatsScale)
Expand Down
9 changes: 7 additions & 2 deletions bolt/lib/Core/FunctionLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,20 @@ void FunctionLayout::eraseBasicBlocks(
updateLayoutIndices();
}

void FunctionLayout::updateLayoutIndices() {
void FunctionLayout::updateLayoutIndices() const {
unsigned BlockIndex = 0;
for (FunctionFragment &FF : fragments()) {
for (const FunctionFragment &FF : fragments()) {
for (BinaryBasicBlock *const BB : FF) {
BB->setLayoutIndex(BlockIndex++);
BB->setFragmentNum(FF.getFragmentNum());
}
}
}
void FunctionLayout::updateLayoutIndices(
ArrayRef<BinaryBasicBlock *> Order) const {
for (auto [Index, BB] : llvm::enumerate(Order))
BB->setLayoutIndex(Index);
}

bool FunctionLayout::update(const ArrayRef<BinaryBasicBlock *> NewLayout) {
const bool EqualBlockOrder = llvm::equal(Blocks, NewLayout);
Expand Down
4 changes: 3 additions & 1 deletion bolt/lib/Passes/BinaryFunctionCallGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ std::deque<BinaryFunction *> BinaryFunctionCallGraph::buildTraversalOrder() {
std::stack<NodeId> Worklist;

for (BinaryFunction *Func : Funcs) {
const NodeId Id = FuncToNodeId.at(Func);
auto It = FuncToNodeId.find(Func);
assert(It != FuncToNodeId.end());
const NodeId Id = It->second;
Worklist.push(Id);
NodeStatus[Id] = NEW;
}
Expand Down
39 changes: 22 additions & 17 deletions bolt/lib/Passes/BinaryPasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1563,23 +1563,28 @@ Error PrintProgramStats::runOnFunctions(BinaryContext &BC) {
const bool Ascending =
opts::DynoStatsSortOrderOpt == opts::DynoStatsSortOrder::Ascending;

if (SortAll) {
llvm::stable_sort(Functions,
[Ascending, &Stats](const BinaryFunction *A,
const BinaryFunction *B) {
return Ascending ? Stats.at(A) < Stats.at(B)
: Stats.at(B) < Stats.at(A);
});
} else {
llvm::stable_sort(
Functions, [Ascending, &Stats](const BinaryFunction *A,
const BinaryFunction *B) {
const DynoStats &StatsA = Stats.at(A);
const DynoStats &StatsB = Stats.at(B);
return Ascending ? StatsA.lessThan(StatsB, opts::PrintSortedBy)
: StatsB.lessThan(StatsA, opts::PrintSortedBy);
});
}
std::function<bool(const DynoStats &, const DynoStats &)>
DynoStatsComparator =
SortAll ? [](const DynoStats &StatsA,
const DynoStats &StatsB) { return StatsA < StatsB; }
: [](const DynoStats &StatsA, const DynoStats &StatsB) {
return StatsA.lessThan(StatsB, opts::PrintSortedBy);
};

llvm::stable_sort(Functions,
[Ascending, &Stats, DynoStatsComparator](
const BinaryFunction *A, const BinaryFunction *B) {
auto StatsItr = Stats.find(A);
assert(StatsItr != Stats.end());
const DynoStats &StatsA = StatsItr->second;

StatsItr = Stats.find(B);
assert(StatsItr != Stats.end());
const DynoStats &StatsB = StatsItr->second;

return Ascending ? DynoStatsComparator(StatsA, StatsB)
: DynoStatsComparator(StatsB, StatsA);
});

BC.outs() << "BOLT-INFO: top functions sorted by ";
if (SortAll) {
Expand Down
43 changes: 33 additions & 10 deletions bolt/lib/Passes/CacheMetrics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,20 @@ calcTSPScore(const std::vector<BinaryFunction *> &BinaryFunctions,
for (BinaryBasicBlock *DstBB : SrcBB->successors()) {
if (SrcBB != DstBB && BI->Count != BinaryBasicBlock::COUNT_NO_PROFILE) {
JumpCount += BI->Count;
if (BBAddr.at(SrcBB) + BBSize.at(SrcBB) == BBAddr.at(DstBB))

auto BBAddrIt = BBAddr.find(SrcBB);
assert(BBAddrIt != BBAddr.end());
uint64_t SrcBBAddr = BBAddrIt->second;

auto BBSizeIt = BBSize.find(SrcBB);
assert(BBSizeIt != BBSize.end());
uint64_t SrcBBSize = BBSizeIt->second;

BBAddrIt = BBAddr.find(DstBB);
assert(BBAddrIt != BBAddr.end());
uint64_t DstBBAddr = BBAddrIt->second;

if (SrcBBAddr + SrcBBSize == DstBBAddr)
Score += BI->Count;
}
++BI;
Expand Down Expand Up @@ -149,29 +162,39 @@ double expectedCacheHitRatio(
for (BinaryFunction *BF : BinaryFunctions) {
if (BF->getLayout().block_empty())
continue;
const uint64_t Page =
BBAddr.at(BF->getLayout().block_front()) / ITLBPageSize;
PageSamples[Page] += FunctionSamples.at(BF);
auto BBAddrIt = BBAddr.find(BF->getLayout().block_front());
assert(BBAddrIt != BBAddr.end());
const uint64_t Page = BBAddrIt->second / ITLBPageSize;

auto FunctionSamplesIt = FunctionSamples.find(BF);
assert(FunctionSamplesIt != FunctionSamples.end());
PageSamples[Page] += FunctionSamplesIt->second;
}

// Computing the expected number of misses for every function
double Misses = 0;
for (BinaryFunction *BF : BinaryFunctions) {
// Skip the function if it has no samples
if (BF->getLayout().block_empty() || FunctionSamples.at(BF) == 0.0)
auto FunctionSamplesIt = FunctionSamples.find(BF);
assert(FunctionSamplesIt != FunctionSamples.end());
double Samples = FunctionSamplesIt->second;
if (BF->getLayout().block_empty() || Samples == 0.0)
continue;
double Samples = FunctionSamples.at(BF);
const uint64_t Page =
BBAddr.at(BF->getLayout().block_front()) / ITLBPageSize;

auto BBAddrIt = BBAddr.find(BF->getLayout().block_front());
assert(BBAddrIt != BBAddr.end());
const uint64_t Page = BBAddrIt->second / ITLBPageSize;
// The probability that the page is not present in the cache
const double MissProb =
pow(1.0 - PageSamples[Page] / TotalSamples, ITLBEntries);

// Processing all callers of the function
for (std::pair<BinaryFunction *, uint64_t> Pair : Calls[BF]) {
BinaryFunction *SrcFunction = Pair.first;
const uint64_t SrcPage =
BBAddr.at(SrcFunction->getLayout().block_front()) / ITLBPageSize;

BBAddrIt = BBAddr.find(SrcFunction->getLayout().block_front());
assert(BBAddrIt != BBAddr.end());
const uint64_t SrcPage = BBAddrIt->second / ITLBPageSize;
// Is this a 'long' or a 'short' call?
if (Page != SrcPage) {
// This is a miss
Expand Down
4 changes: 3 additions & 1 deletion bolt/lib/Passes/Inliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ Inliner::inlineCall(BinaryBasicBlock &CallerBB,
std::vector<BinaryBasicBlock *> Successors(BB.succ_size());
llvm::transform(BB.successors(), Successors.begin(),
[&InlinedBBMap](const BinaryBasicBlock *BB) {
return InlinedBBMap.at(BB);
auto It = InlinedBBMap.find(BB);
assert(It != InlinedBBMap.end());
return It->second;
});

if (CallerFunction.hasValidProfile() && Callee.hasValidProfile())
Expand Down
33 changes: 21 additions & 12 deletions bolt/lib/Passes/MCF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

#include "bolt/Passes/MCF.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Core/ParallelUtilities.h"
#include "bolt/Passes/DataflowInfoManager.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CommandLine.h"
#include <algorithm>
#include <vector>
Expand All @@ -29,19 +31,10 @@ namespace opts {

extern cl::OptionCategory BoltOptCategory;

extern cl::opt<bool> TimeOpts;

static cl::opt<bool> IterativeGuess(
"iterative-guess",
cl::desc("in non-LBR mode, guess edge counts using iterative technique"),
cl::Hidden, cl::cat(BoltOptCategory));

static cl::opt<bool> UseRArcs(
"mcf-use-rarcs",
cl::desc("in MCF, consider the possibility of cancelling flow to balance "
"edges"),
cl::Hidden, cl::cat(BoltOptCategory));

} // namespace opts

namespace llvm {
Expand Down Expand Up @@ -441,7 +434,7 @@ void equalizeBBCounts(DataflowInfoManager &Info, BinaryFunction &BF) {
}
}

void estimateEdgeCounts(BinaryFunction &BF) {
void EstimateEdgeCounts::runOnFunction(BinaryFunction &BF) {
EdgeWeightMap PredEdgeWeights;
EdgeWeightMap SuccEdgeWeights;
if (!opts::IterativeGuess) {
Expand All @@ -462,8 +455,24 @@ void estimateEdgeCounts(BinaryFunction &BF) {
recalculateBBCounts(BF, /*AllEdges=*/false);
}

void solveMCF(BinaryFunction &BF, MCFCostFunction CostFunction) {
llvm_unreachable("not implemented");
Error EstimateEdgeCounts::runOnFunctions(BinaryContext &BC) {
if (llvm::none_of(llvm::make_second_range(BC.getBinaryFunctions()),
[](const BinaryFunction &BF) {
return BF.getProfileFlags() == BinaryFunction::PF_SAMPLE;
}))
return Error::success();

ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
runOnFunction(BF);
};
ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
return BF.getProfileFlags() != BinaryFunction::PF_SAMPLE;
};

ParallelUtilities::runOnEachFunction(
BC, ParallelUtilities::SchedulingPolicy::SP_BB_QUADRATIC, WorkFun,
SkipFunc, "EstimateEdgeCounts");
return Error::success();
}

} // namespace bolt
Expand Down
50 changes: 29 additions & 21 deletions bolt/lib/Profile/BoltAddressTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ namespace bolt {

const char *BoltAddressTranslation::SECTION_NAME = ".note.bolt_bat";

void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,
const BinaryBasicBlock &BB,
uint64_t FuncInputAddress,
uint64_t FuncOutputAddress) {
void BoltAddressTranslation::writeEntriesForBB(
MapTy &Map, const BinaryBasicBlock &BB, uint64_t FuncInputAddress,
uint64_t FuncOutputAddress) const {
const uint64_t BBOutputOffset =
BB.getOutputAddressRange().first - FuncOutputAddress;
const uint32_t BBInputOffset = BB.getInputOffset();
Expand Down Expand Up @@ -55,7 +54,7 @@ void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,
// and this deleted block will both share the same output address (the same
// key), and we need to map back. We choose here to privilege the successor by
// allowing it to overwrite the previously inserted key in the map.
Map[BBOutputOffset] = BBInputOffset << 1;
Map.emplace(BBOutputOffset, BBInputOffset << 1);

const auto &IOAddressMap =
BB.getFunction()->getBinaryContext().getIOAddressMap();
Expand All @@ -72,8 +71,7 @@ void BoltAddressTranslation::writeEntriesForBB(MapTy &Map,

LLVM_DEBUG(dbgs() << " Key: " << Twine::utohexstr(OutputOffset) << " Val: "
<< Twine::utohexstr(InputOffset) << " (branch)\n");
Map.insert(std::pair<uint32_t, uint32_t>(OutputOffset,
(InputOffset << 1) | BRANCHENTRY));
Map.emplace(OutputOffset, (InputOffset << 1) | BRANCHENTRY);
}
}

Expand Down Expand Up @@ -108,6 +106,19 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
for (const BinaryBasicBlock *const BB :
Function.getLayout().getMainFragment())
writeEntriesForBB(Map, *BB, InputAddress, OutputAddress);
// Add entries for deleted blocks. They are still required for correct BB
// mapping of branches modified by SCTC. By convention, they would have the
// end of the function as output address.
const BBHashMapTy &BBHashMap = getBBHashMap(InputAddress);
if (BBHashMap.size() != Function.size()) {
const uint64_t EndOffset = Function.getOutputSize();
std::unordered_set<uint32_t> MappedInputOffsets;
for (const BinaryBasicBlock &BB : Function)
MappedInputOffsets.emplace(BB.getInputOffset());
for (const auto &[InputOffset, _] : BBHashMap)
if (!llvm::is_contained(MappedInputOffsets, InputOffset))
Map.emplace(EndOffset, InputOffset << 1);
}
Maps.emplace(Function.getOutputAddress(), std::move(Map));
ReverseMap.emplace(OutputAddress, InputAddress);

Expand Down Expand Up @@ -138,8 +149,8 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
<< " basic block hashes\n";
}

APInt BoltAddressTranslation::calculateBranchEntriesBitMask(MapTy &Map,
size_t EqualElems) {
APInt BoltAddressTranslation::calculateBranchEntriesBitMask(
MapTy &Map, size_t EqualElems) const {
APInt BitMask(alignTo(EqualElems, 8), 0);
size_t Index = 0;
for (std::pair<const uint32_t, uint32_t> &KeyVal : Map) {
Expand Down Expand Up @@ -422,7 +433,7 @@ void BoltAddressTranslation::parseMaps(std::vector<uint64_t> &HotFuncs,
}
}

void BoltAddressTranslation::dump(raw_ostream &OS) {
void BoltAddressTranslation::dump(raw_ostream &OS) const {
const size_t NumTables = Maps.size();
OS << "BAT tables for " << NumTables << " functions:\n";
for (const auto &MapEntry : Maps) {
Expand All @@ -447,11 +458,15 @@ void BoltAddressTranslation::dump(raw_ostream &OS) {
OS << formatv(" hash: {0:x}", BBHashMap.getBBHash(Val));
OS << "\n";
}
if (IsHotFunction)
OS << "NumBlocks: " << NumBasicBlocksMap[Address] << '\n';
if (SecondaryEntryPointsMap.count(Address)) {
if (IsHotFunction) {
auto NumBasicBlocksIt = NumBasicBlocksMap.find(Address);
assert(NumBasicBlocksIt != NumBasicBlocksMap.end());
OS << "NumBlocks: " << NumBasicBlocksIt->second << '\n';
}
auto SecondaryEntryPointsIt = SecondaryEntryPointsMap.find(Address);
if (SecondaryEntryPointsIt != SecondaryEntryPointsMap.end()) {
const std::vector<uint32_t> &SecondaryEntryPoints =
SecondaryEntryPointsMap[Address];
SecondaryEntryPointsIt->second;
OS << SecondaryEntryPoints.size() << " secondary entry points:\n";
for (uint32_t EntryPointOffset : SecondaryEntryPoints)
OS << formatv("{0:x}\n", EntryPointOffset);
Expand Down Expand Up @@ -547,13 +562,6 @@ BoltAddressTranslation::getFallthroughsInTrace(uint64_t FuncAddress,
return Res;
}

uint64_t BoltAddressTranslation::fetchParentAddress(uint64_t Address) const {
auto Iter = ColdPartSource.find(Address);
if (Iter == ColdPartSource.end())
return 0;
return Iter->second;
}

bool BoltAddressTranslation::enabledFor(
llvm::object::ELFObjectFileBase *InputFile) const {
for (const SectionRef &Section : InputFile->sections()) {
Expand Down
1 change: 0 additions & 1 deletion bolt/lib/Profile/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ add_llvm_library(LLVMBOLTProfile
target_link_libraries(LLVMBOLTProfile
PRIVATE
LLVMBOLTCore
LLVMBOLTPasses
LLVMBOLTUtils
)
14 changes: 6 additions & 8 deletions bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,6 @@ Error DataAggregator::readProfile(BinaryContext &BC) {
if (std::error_code EC = writeBATYAML(BC, opts::SaveProfile))
report_error("cannot create output data file", EC);
}
PrintProgramStats PPS(BAT);
BC.logBOLTErrorsAndQuitOnFatal(PPS.runOnFunctions(BC));
}

return Error::success();
Expand Down Expand Up @@ -2351,11 +2349,11 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
BAT->getBBHashMap(FuncAddress);
YamlBF.Blocks.resize(YamlBF.NumBasicBlocks);

for (auto &&[Idx, YamlBB] : llvm::enumerate(YamlBF.Blocks))
YamlBB.Index = Idx;

for (auto BI = BlockMap.begin(), BE = BlockMap.end(); BI != BE; ++BI)
YamlBF.Blocks[BI->second.getBBIndex()].Hash = BI->second.getBBHash();
for (auto &&[Entry, YamlBB] : llvm::zip(BlockMap, YamlBF.Blocks)) {
const auto &Block = Entry.second;
YamlBB.Hash = Block.Hash;
YamlBB.Index = Block.Index;
}

// Lookup containing basic block offset and index
auto getBlock = [&BlockMap](uint32_t Offset) {
Expand All @@ -2365,7 +2363,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
exit(1);
}
--BlockIt;
return std::pair(BlockIt->first, BlockIt->second.getBBIndex());
return std::pair(BlockIt->first, BlockIt->second.Index);
};

for (const BranchInfo &BI : Branches.Data) {
Expand Down
2 changes: 0 additions & 2 deletions bolt/lib/Profile/DataReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,6 @@ void DataReader::readSampleData(BinaryFunction &BF) {
}

BF.ExecutionCount = TotalEntryCount;

estimateEdgeCounts(BF);
}

void DataReader::convertBranchData(BinaryFunction &BF) const {
Expand Down
12 changes: 10 additions & 2 deletions bolt/lib/Profile/StaleProfileMatching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "llvm/ADT/Bitfields.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/xxhash.h"
#include "llvm/Transforms/Utils/SampleProfileInference.h"

Expand All @@ -42,6 +43,7 @@ using namespace llvm;

namespace opts {

extern cl::opt<bool> TimeRewrite;
extern cl::OptionCategory BoltOptCategory;

cl::opt<bool>
Expand Down Expand Up @@ -372,8 +374,10 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {

// Create necessary metadata for the flow function
for (FlowJump &Jump : Func.Jumps) {
Func.Blocks.at(Jump.Source).SuccJumps.push_back(&Jump);
Func.Blocks.at(Jump.Target).PredJumps.push_back(&Jump);
assert(Jump.Source < Func.Blocks.size());
Func.Blocks[Jump.Source].SuccJumps.push_back(&Jump);
assert(Jump.Target < Func.Blocks.size());
Func.Blocks[Jump.Target].PredJumps.push_back(&Jump);
}
return Func;
}
Expand Down Expand Up @@ -705,6 +709,10 @@ void assignProfile(BinaryFunction &BF,

bool YAMLProfileReader::inferStaleProfile(
BinaryFunction &BF, const yaml::bolt::BinaryFunctionProfile &YamlBF) {

NamedRegionTimer T("inferStaleProfile", "stale profile inference", "rewrite",
"Rewrite passes", opts::TimeRewrite);

if (!BF.hasCFG())
return false;

Expand Down
17 changes: 9 additions & 8 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,14 @@ bool YAMLProfileReader::parseFunctionProfile(
if (BF.empty())
return true;

if (!opts::IgnoreHash &&
YamlBF.Hash != BF.computeHash(IsDFSOrder, HashFunction)) {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: function hash mismatch\n";
ProfileMatched = false;
if (!opts::IgnoreHash) {
if (!BF.getHash())
BF.computeHash(IsDFSOrder, HashFunction);
if (YamlBF.Hash != BF.getHash()) {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: function hash mismatch\n";
ProfileMatched = false;
}
}

if (YamlBF.NumBasicBlocks != BF.size()) {
Expand Down Expand Up @@ -253,10 +256,8 @@ bool YAMLProfileReader::parseFunctionProfile(
if (BB.getExecutionCount() == BinaryBasicBlock::COUNT_NO_PROFILE)
BB.setExecutionCount(0);

if (YamlBP.Header.Flags & BinaryFunction::PF_SAMPLE) {
if (YamlBP.Header.Flags & BinaryFunction::PF_SAMPLE)
BF.setExecutionCount(FunctionExecutionCount);
estimateEdgeCounts(BF);
}

ProfileMatched &= !MismatchedBlocks && !MismatchedCalls && !MismatchedEdges;

Expand Down
19 changes: 13 additions & 6 deletions bolt/lib/Rewrite/BinaryPassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "bolt/Passes/JTFootprintReduction.h"
#include "bolt/Passes/LongJmp.h"
#include "bolt/Passes/LoopInversionPass.h"
#include "bolt/Passes/MCF.h"
#include "bolt/Passes/PLTCall.h"
#include "bolt/Passes/PatchEntries.h"
#include "bolt/Passes/RegReAssign.h"
Expand Down Expand Up @@ -90,6 +91,11 @@ PrintAfterLowering("print-after-lowering",
cl::desc("print function after instruction lowering"),
cl::Hidden, cl::cat(BoltOptCategory));

static cl::opt<bool> PrintEstimateEdgeCounts(
"print-estimate-edge-counts",
cl::desc("print function after edge counts are set for no-LBR profile"),
cl::Hidden, cl::cat(BoltOptCategory));

cl::opt<bool>
PrintFinalized("print-finalized",
cl::desc("print function after CFG is finalized"),
Expand Down Expand Up @@ -334,8 +340,10 @@ Error BinaryFunctionPassManager::runPasses() {
Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
BinaryFunctionPassManager Manager(BC);

const DynoStats InitialDynoStats =
getDynoStats(BC.getBinaryFunctions(), BC.isAArch64());
Manager.registerPass(
std::make_unique<EstimateEdgeCounts>(PrintEstimateEdgeCounts));

Manager.registerPass(std::make_unique<DynoStatsSetPass>());

Manager.registerPass(std::make_unique<AsmDumpPass>(),
opts::AsmDump.getNumOccurrences());
Expand Down Expand Up @@ -447,10 +455,9 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
Manager.registerPass(std::make_unique<SplitFunctions>(PrintSplit));

// Print final dyno stats right while CFG and instruction analysis are intact.
Manager.registerPass(
std::make_unique<DynoStatsPrintPass>(
InitialDynoStats, "after all optimizations before SCTC and FOP"),
opts::PrintDynoStats || opts::DynoStatsAll);
Manager.registerPass(std::make_unique<DynoStatsPrintPass>(
"after all optimizations before SCTC and FOP"),
opts::PrintDynoStats || opts::DynoStatsAll);

// Add the StokeInfo pass, which extract functions for stoke optimization and
// get the liveness information for them
Expand Down
5 changes: 2 additions & 3 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ static void printDie(DWARFUnit &DU, uint64_t DIEOffset) {
DWARFDataExtractor DebugInfoData = DU.getDebugInfoExtractor();
DWARFDebugInfoEntry DIEEntry;
if (DIEEntry.extractFast(DU, &DIEOffset, DebugInfoData, NextCUOffset, 0)) {
if (const DWARFAbbreviationDeclaration *AbbrDecl =
DIEEntry.getAbbreviationDeclarationPtr()) {
if (DIEEntry.getAbbreviationDeclarationPtr()) {
DWARFDie DDie(&DU, &DIEEntry);
printDie(DDie);
} else {
Expand Down Expand Up @@ -353,7 +352,7 @@ static cl::opt<bool> CreateDebugNames(

static cl::opt<bool>
DebugSkeletonCu("debug-skeleton-cu",
cl::desc("prints out offsetrs for abbrev and debu_info of "
cl::desc("prints out offsets for abbrev and debug_info of "
"Skeleton CUs that get patched."),
cl::ZeroOrMore, cl::Hidden, cl::init(false),
cl::cat(BoltCategory));
Expand Down
2 changes: 1 addition & 1 deletion bolt/lib/Rewrite/LinuxKernelRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ void LinuxKernelRewriter::processLKKSymtab(bool IsGPL) {

for (uint64_t I = 0; I < SectionSize; I += 4) {
const uint64_t EntryAddress = SectionAddress + I;
ErrorOr<uint64_t> Offset = BC.getSignedValueAtAddress(EntryAddress, 4);
ErrorOr<int64_t> Offset = BC.getSignedValueAtAddress(EntryAddress, 4);
assert(Offset && "Reading valid PC-relative offset for a ksymtab entry");
const int32_t SignedOffset = *Offset;
const uint64_t RefAddress = EntryAddress + SignedOffset;
Expand Down
32 changes: 15 additions & 17 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "bolt/Core/MCPlusBuilder.h"
#include "bolt/Core/ParallelUtilities.h"
#include "bolt/Core/Relocation.h"
#include "bolt/Passes/BinaryPasses.h"
#include "bolt/Passes/CacheMetrics.h"
#include "bolt/Passes/ReorderFunctions.h"
#include "bolt/Profile/BoltAddressTranslation.h"
Expand Down Expand Up @@ -54,7 +55,6 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
Expand Down Expand Up @@ -86,6 +86,7 @@ extern cl::list<std::string> ReorderData;
extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions;
extern cl::opt<bool> TerminalTrap;
extern cl::opt<bool> TimeBuild;
extern cl::opt<bool> TimeRewrite;

cl::opt<bool> AllowStripped("allow-stripped",
cl::desc("allow processing of stripped binaries"),
Expand Down Expand Up @@ -235,11 +236,6 @@ UseGnuStack("use-gnu-stack",
cl::ZeroOrMore,
cl::cat(BoltCategory));

static cl::opt<bool>
TimeRewrite("time-rewrite",
cl::desc("print time spent in rewriting passes"), cl::Hidden,
cl::cat(BoltCategory));

static cl::opt<bool>
SequentialDisassembly("sequential-disassembly",
cl::desc("performs disassembly sequentially"),
Expand Down Expand Up @@ -948,9 +944,6 @@ void RewriteInstance::discoverFileObjects() {
BinaryFunction *PreviousFunction = nullptr;
unsigned AnonymousId = 0;

// Regex object for matching cold fragments.
const Regex ColdFragment(".*\\.cold(\\.[0-9]+)?");

const auto SortedSymbolsEnd =
LastSymbol == SortedSymbols.end() ? LastSymbol : std::next(LastSymbol);
for (auto Iter = SortedSymbols.begin(); Iter != SortedSymbolsEnd; ++Iter) {
Expand Down Expand Up @@ -1232,7 +1225,7 @@ void RewriteInstance::discoverFileObjects() {
}

// Check if it's a cold function fragment.
if (ColdFragment.match(SymName)) {
if (FunctionFragmentTemplate.match(SymName)) {
static bool PrintedWarning = false;
if (!PrintedWarning) {
PrintedWarning = true;
Expand Down Expand Up @@ -1463,10 +1456,10 @@ void RewriteInstance::registerFragments() {
for (StringRef Name : Function.getNames()) {
StringRef BaseName = NR.restore(Name);
const bool IsGlobal = BaseName == Name;
const size_t ColdSuffixPos = BaseName.find(".cold");
if (ColdSuffixPos == StringRef::npos)
SmallVector<StringRef> Matches;
if (!FunctionFragmentTemplate.match(BaseName, &Matches))
continue;
StringRef ParentName = BaseName.substr(0, ColdSuffixPos);
StringRef ParentName = Matches[1];
const BinaryData *BD = BC->getBinaryDataByName(ParentName);
const uint64_t NumPossibleLocalParents =
NR.getUniquifiedNameCount(ParentName);
Expand Down Expand Up @@ -1500,7 +1493,7 @@ void RewriteInstance::registerFragments() {
if (!BC->hasSymbolsWithFileName()) {
BC->errs() << "BOLT-ERROR: input file has split functions but does not "
"have FILE symbols. If the binary was stripped, preserve "
"FILE symbols with --keep-file-symbols strip option";
"FILE symbols with --keep-file-symbols strip option\n";
exit(1);
}

Expand Down Expand Up @@ -3209,12 +3202,14 @@ void RewriteInstance::preprocessProfileData() {
if (Error E = ProfileReader->preprocessProfile(*BC.get()))
report_error("cannot pre-process profile", std::move(E));

if (!BC->hasSymbolsWithFileName() && ProfileReader->hasLocalsWithFileName()) {
if (!BC->hasSymbolsWithFileName() && ProfileReader->hasLocalsWithFileName() &&
!opts::AllowStripped) {
BC->errs()
<< "BOLT-ERROR: input binary does not have local file symbols "
"but profile data includes function names with embedded file "
"names. It appears that the input binary was stripped while a "
"profiled binary was not\n";
"profiled binary was not. If you know what you are doing and "
"wish to proceed, use -allow-stripped option.\n";
exit(1);
}
}
Expand Down Expand Up @@ -3285,8 +3280,11 @@ void RewriteInstance::processProfileData() {
// Release memory used by profile reader.
ProfileReader.reset();

if (opts::AggregateOnly)
if (opts::AggregateOnly) {
PrintProgramStats PPS(&*BAT);
BC->logBOLTErrorsAndQuitOnFatal(PPS.runOnFunctions(*BC));
exit(0);
}
}

void RewriteInstance::disassembleFunctions() {
Expand Down
38 changes: 25 additions & 13 deletions bolt/lib/Target/X86/X86MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1932,6 +1932,19 @@ class X86MCPlusBuilder : public MCPlusBuilder {
// = R_X86_64_PC32(Ln) + En - JT
// = R_X86_64_PC32(Ln + offsetof(En))
//
auto isRIPRel = [&](X86MemOperand &MO) {
// NB: DispExpr should be set
return MO.DispExpr != nullptr &&
MO.BaseRegNum == RegInfo->getProgramCounter() &&
MO.IndexRegNum == X86::NoRegister &&
MO.SegRegNum == X86::NoRegister;
};
auto isIndexed = [](X86MemOperand &MO, MCPhysReg R) {
// NB: IndexRegNum should be set.
return MO.IndexRegNum != X86::NoRegister && MO.BaseRegNum == R &&
MO.ScaleImm == 4 && MO.DispImm == 0 &&
MO.SegRegNum == X86::NoRegister;
};
LLVM_DEBUG(dbgs() << "Checking for PIC jump table\n");
MCInst *MemLocInstr = nullptr;
const MCInst *MovInstr = nullptr;
Expand Down Expand Up @@ -1965,9 +1978,8 @@ class X86MCPlusBuilder : public MCPlusBuilder {
std::optional<X86MemOperand> MO = evaluateX86MemoryOperand(Instr);
if (!MO)
break;
if (MO->BaseRegNum != R1 || MO->ScaleImm != 4 ||
MO->IndexRegNum == X86::NoRegister || MO->DispImm != 0 ||
MO->SegRegNum != X86::NoRegister)
if (!isIndexed(*MO, R1))
// POSSIBLE_PIC_JUMP_TABLE
break;
MovInstr = &Instr;
} else {
Expand All @@ -1986,9 +1998,7 @@ class X86MCPlusBuilder : public MCPlusBuilder {
std::optional<X86MemOperand> MO = evaluateX86MemoryOperand(Instr);
if (!MO)
break;
if (MO->BaseRegNum != RegInfo->getProgramCounter() ||
MO->IndexRegNum != X86::NoRegister ||
MO->SegRegNum != X86::NoRegister || MO->DispExpr == nullptr)
if (!isRIPRel(*MO))
break;
MemLocInstr = &Instr;
break;
Expand Down Expand Up @@ -2105,13 +2115,15 @@ class X86MCPlusBuilder : public MCPlusBuilder {
return IndirectBranchType::POSSIBLE_FIXED_BRANCH;
}

if (Type == IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE &&
(MO->ScaleImm != 1 || MO->BaseRegNum != RIPRegister))
return IndirectBranchType::UNKNOWN;

if (Type != IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE &&
MO->ScaleImm != PtrSize)
return IndirectBranchType::UNKNOWN;
switch (Type) {
case IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE:
if (MO->ScaleImm != 1 || MO->BaseRegNum != RIPRegister)
return IndirectBranchType::UNKNOWN;
break;
default:
if (MO->ScaleImm != PtrSize)
return IndirectBranchType::UNKNOWN;
}

MemLocInstrOut = MemLocInstr;

Expand Down
4 changes: 4 additions & 0 deletions bolt/lib/Utils/CommandLineOpts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ cl::opt<bool> TimeOpts("time-opts",
cl::desc("print time spent in each optimization"),
cl::cat(BoltOptCategory));

cl::opt<bool> TimeRewrite("time-rewrite",
cl::desc("print time spent in rewriting passes"),
cl::Hidden, cl::cat(BoltCategory));

cl::opt<bool> UseOldText(
"use-old-text",
cl::desc("re-use space in old .text if possible (relocation mode)"),
Expand Down
3 changes: 3 additions & 0 deletions bolt/test/AArch64/Inputs/array_end.lld_script
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
SECTIONS {
.interp : { *(.interp) }

. = ALIGN(CONSTANT(MAXPAGESIZE));
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
Expand Down
3 changes: 1 addition & 2 deletions bolt/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,14 @@ list(APPEND BOLT_TEST_DEPS
)

add_custom_target(bolt-test-depends DEPENDS ${BOLT_TEST_DEPS})
set_target_properties(bolt-test-depends PROPERTIES FOLDER "BOLT")
set_target_properties(bolt-test-depends PROPERTIES FOLDER "BOLT/Tests")

add_lit_testsuite(check-bolt "Running the BOLT regression tests"
${CMAKE_CURRENT_BINARY_DIR}
PARAMS ${BOLT_TEST_PARAMS}
DEPENDS ${BOLT_TEST_DEPS}
ARGS ${BOLT_TEST_EXTRA_ARGS}
)
set_target_properties(check-bolt PROPERTIES FOLDER "BOLT")

add_lit_testsuites(BOLT ${CMAKE_CURRENT_SOURCE_DIR}
PARAMS ${BOLT_TEST_PARAMS}
Expand Down
3 changes: 3 additions & 0 deletions bolt/test/Inputs/lsda.ldscript
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
SECTIONS {
.interp : { *(.interp) }
. = ALIGN(CONSTANT(MAXPAGESIZE));
.text : { *(.text*) }
. = ALIGN(CONSTANT(MAXPAGESIZE));
.gcc_except_table.main : { *(.gcc_except_table*) }
. = 0x20000;
.eh_frame : { *(.eh_frame) }
Expand Down
10 changes: 10 additions & 0 deletions bolt/test/X86/bb-with-two-tail-calls.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib
# RUN: llvm-bolt %t.exe -o %t.out --data %t.fdata --lite=0 --dyno-stats \
# RUN: --print-sctc --print-only=_start -enable-bat 2>&1 | FileCheck %s
# RUN: llvm-objdump --syms %t.out > %t.log
# RUN: llvm-bat-dump %t.out --dump-all >> %t.log
# RUN: FileCheck %s --input-file %t.log --check-prefix=CHECK-BAT

# CHECK-NOT: Assertion `BranchInfo.size() == 2 && "could only be called for blocks with 2 successors"' failed.
# Two tail calls in the same basic block after SCTC:
# CHECK: {{.*}}: ja {{.*}} # TAILCALL # Offset: 7 # CTCTakenCount: 4
# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL # Offset: 13

# Confirm that a deleted basic block is emitted at function end offset (0xe)
# CHECK-BAT: [[#%x,ADDR:]] g .text [[#%x,SIZE:]] _start
# CHECK-BAT: Function Address: 0x[[#%x,ADDR]]
# CHECK-BAT: 0x[[#%x,SIZE]]
# CHECK-BAT: NumBlocks: 5

.globl _start
_start:
je x
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/bolt-address-translation-yaml.test
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ RUN: | FileCheck --check-prefix CHECK-BOLT-YAML %s

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

READ-BAT-CHECK-NOT: BOLT-ERROR: unable to save profile in YAML format for input file processed by BOLT
READ-BAT-CHECK: BOLT-INFO: Parsed 5 BAT entries
Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/bolt-address-translation.test
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
# CHECK: BOLT: 3 out of 7 functions were overwritten.
# CHECK: BOLT-INFO: Wrote 6 BAT maps
# CHECK: BOLT-INFO: Wrote 3 function and 58 basic block hashes
# CHECK: BOLT-INFO: BAT section size (bytes): 928
# CHECK: BOLT-INFO: BAT section size (bytes): 940
#
# usqrt mappings (hot part). We match against any key (left side containing
# the bolted binary offsets) because BOLT may change where it puts instructions
Expand Down
670 changes: 670 additions & 0 deletions bolt/test/X86/dwarf5-debug-names-class-type-decl.s

Large diffs are not rendered by default.

485 changes: 485 additions & 0 deletions bolt/test/X86/dwarf5-debug-names-enumeration-type-decl.s

Large diffs are not rendered by default.

671 changes: 671 additions & 0 deletions bolt/test/X86/dwarf5-debug-names-structure-type-decl.s

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion bolt/test/X86/register-fragments-bolt-symbols.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,20 @@
# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %S/cdsplit-symbol-names.s -o %t.main.o
# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.chain.o
# RUN: link_fdata %S/cdsplit-symbol-names.s %t.main.o %t.fdata
# RUN: sed -i 's|chain|chain/2|g' %t.fdata
# RUN: llvm-strip --strip-unneeded %t.main.o

## Check warm fragment name matching (produced by cdsplit)
# RUN: %clang %cflags %t.main.o -o %t.warm.exe -Wl,-q
# RUN: llvm-bolt %t.warm.exe -o %t.warm.bolt --split-functions --split-strategy=cdsplit \
# RUN: --call-scale=2 --data=%t.fdata --reorder-blocks=ext-tsp --enable-bat
# RUN: link_fdata %s %t.warm.bolt %t.preagg.warm PREAGGWARM
# PREAGGWARM: B X:0 #chain.warm# 1 0
# RUN: perf2bolt %t.warm.bolt -p %t.preagg.warm --pa -o %t.warm.fdata -w %t.warm.yaml \
# RUN: -v=1 | FileCheck %s --check-prefix=CHECK-BOLT-WARM

# CHECK-BOLT-WARM: marking chain.warm/1(*2) as a fragment of chain

# RUN: sed -i 's|chain|chain/2|g' %t.fdata
# RUN: llvm-objcopy --localize-symbol=chain %t.main.o
# RUN: %clang %cflags %t.chain.o %t.main.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=randomN \
Expand Down
2 changes: 1 addition & 1 deletion bolt/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
add_custom_target(BoltUnitTests)
set_target_properties(BoltUnitTests PROPERTIES FOLDER "BOLT tests")
set_target_properties(BoltUnitTests PROPERTIES FOLDER "BOLT/Tests")

function(add_bolt_unittest test_dirname)
add_unittest(BoltUnitTests ${test_dirname} ${ARGN})
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
set(LLVM_SUBPROJECT_TITLE "Clang Tools Extra")

include(CMakeDependentOption)
include(GNUInstallDirs)

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 @@ -121,7 +121,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
PATTERN "*.h"
)
add_custom_target(clang-tidy-headers)
set_target_properties(clang-tidy-headers PROPERTIES FOLDER "Misc")
set_target_properties(clang-tidy-headers PROPERTIES FOLDER "Clang Tools Extra/Resources")
if(NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-clang-tidy-headers
DEPENDS clang-tidy-headers
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_custom_command(
DEPENDS ${clang_tidy_confusable_chars_gen_target} ConfusableTable/confusables.txt)

add_custom_target(genconfusable DEPENDS Confusables.inc)
set_target_properties(genconfusable PROPERTIES FOLDER "Clang Tools Extra/Sourcegenning")

add_clang_library(clangTidyMiscModule
ConstCorrectnessCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {

Finder->addMatcher(
cxxMemberCallExpr(
argumentCountIs(0),
on(expr(anyOf(hasType(ValidContainer),
hasType(pointsTo(ValidContainer)),
hasType(references(ValidContainer))))
Expand All @@ -163,7 +164,8 @@ void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
this);

Finder->addMatcher(
callExpr(has(cxxDependentScopeMemberExpr(
callExpr(argumentCountIs(0),
has(cxxDependentScopeMemberExpr(
hasObjectExpression(
expr(anyOf(hasType(ValidContainer),
hasType(pointsTo(ValidContainer)),
Expand Down
12 changes: 10 additions & 2 deletions clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1414,13 +1414,21 @@ IdentifierNamingCheck::getDiagInfo(const NamingCheckId &ID,
}};
}

StringRef IdentifierNamingCheck::getRealFileName(StringRef FileName) const {
auto Iter = RealFileNameCache.try_emplace(FileName);
SmallString<256U> &RealFileName = Iter.first->getValue();
if (!Iter.second)
return RealFileName;
llvm::sys::fs::real_path(FileName, RealFileName);
return RealFileName;
}

const IdentifierNamingCheck::FileStyle &
IdentifierNamingCheck::getStyleForFile(StringRef FileName) const {
if (!GetConfigPerFile)
return *MainFileStyle;

SmallString<128> RealFileName;
llvm::sys::fs::real_path(FileName, RealFileName);
StringRef RealFileName = getRealFileName(FileName);
StringRef Parent = llvm::sys::path::parent_path(RealFileName);
auto Iter = NamingStylesCache.find(Parent);
if (Iter != NamingStylesCache.end())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
const NamingCheckFailure &Failure) const override;

const FileStyle &getStyleForFile(StringRef FileName) const;
StringRef getRealFileName(StringRef FileName) const;

/// Find the style kind of a field in an anonymous record.
StyleKind findStyleKindForAnonField(
Expand All @@ -222,6 +223,7 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
/// Stores the style options as a vector, indexed by the specified \ref
/// StyleKind, for a given directory.
mutable llvm::StringMap<FileStyle> NamingStylesCache;
mutable llvm::StringMap<SmallString<256U>> RealFileNameCache;
FileStyle *MainFileStyle;
ClangTidyContext *Context;
const bool GetConfigPerFile;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,

case CK_PointerToBoolean:
case CK_MemberPointerToBoolean: // Fall-through on purpose.
return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
return (Context.getLangOpts().CPlusPlus11 || Context.getLangOpts().C23)
? "nullptr"
: "0";

default:
llvm_unreachable("Unexpected cast kind");
Expand Down Expand Up @@ -165,6 +167,12 @@ bool needsSpacePrefix(SourceLocation Loc, ASTContext &Context) {
void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
const ImplicitCastExpr *Cast,
ASTContext &Context, StringRef OtherType) {
if (!Context.getLangOpts().CPlusPlus) {
Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(),
(Twine("(") + OtherType + ")").str());
return;
}

const Expr *SubExpr = Cast->getSubExpr();
const bool NeedParens = !isa<ParenExpr>(SubExpr->IgnoreImplicit());
const bool NeedSpace = needsSpacePrefix(Cast->getBeginLoc(), Context);
Expand Down Expand Up @@ -267,6 +275,10 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
auto BoolXor =
binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool),
hasRHS(ImplicitCastFromBool));
auto ComparisonInCall = allOf(
hasParent(callExpr()),
hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!="))));

Finder->addMatcher(
traverse(TK_AsIs,
implicitCastExpr(
Expand All @@ -281,6 +293,8 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
// Exclude cases common to implicit cast to and from bool.
unless(ExceptionCases), unless(has(BoolXor)),
// Exclude C23 cases common to implicit cast to bool.
unless(ComparisonInCall),
// Retrieve also parent statement, to check if we need
// additional parens in replacement.
optionally(hasParent(stmt().bind("parentStmt"))),
Expand Down
5 changes: 3 additions & 2 deletions clang-tools-extra/clangd/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ struct Config {
IncludesPolicy UnusedIncludes = IncludesPolicy::Strict;
IncludesPolicy MissingIncludes = IncludesPolicy::None;

/// IncludeCleaner will not diagnose usages of these headers matched by
/// these regexes.
struct {
/// IncludeCleaner will not diagnose usages of these headers matched by
/// these regexes.
std::vector<std::function<bool(llvm::StringRef)>> IgnoreHeader;
bool AnalyzeAngledIncludes = false;
} Includes;
} Diagnostics;

Expand Down
60 changes: 37 additions & 23 deletions clang-tools-extra/clangd/ConfigCompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,32 +572,46 @@ struct FragmentCompiler {
#else
static llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags;
#endif
auto Filters = std::make_shared<std::vector<llvm::Regex>>();
for (auto &HeaderPattern : F.IgnoreHeader) {
// Anchor on the right.
std::string AnchoredPattern = "(" + *HeaderPattern + ")$";
llvm::Regex CompiledRegex(AnchoredPattern, Flags);
std::string RegexError;
if (!CompiledRegex.isValid(RegexError)) {
diag(Warning,
llvm::formatv("Invalid regular expression '{0}': {1}",
*HeaderPattern, RegexError)
.str(),
HeaderPattern.Range);
continue;
std::shared_ptr<std::vector<llvm::Regex>> Filters;
if (!F.IgnoreHeader.empty()) {
Filters = std::make_shared<std::vector<llvm::Regex>>();
for (auto &HeaderPattern : F.IgnoreHeader) {
// Anchor on the right.
std::string AnchoredPattern = "(" + *HeaderPattern + ")$";
llvm::Regex CompiledRegex(AnchoredPattern, Flags);
std::string RegexError;
if (!CompiledRegex.isValid(RegexError)) {
diag(Warning,
llvm::formatv("Invalid regular expression '{0}': {1}",
*HeaderPattern, RegexError)
.str(),
HeaderPattern.Range);
continue;
}
Filters->push_back(std::move(CompiledRegex));
}
Filters->push_back(std::move(CompiledRegex));
}
if (Filters->empty())
// Optional to override the resulting AnalyzeAngledIncludes
// only if it's explicitly set in the current fragment.
// Otherwise it's inherited from parent fragment.
std::optional<bool> AnalyzeAngledIncludes;
if (F.AnalyzeAngledIncludes.has_value())
AnalyzeAngledIncludes = **F.AnalyzeAngledIncludes;
if (!Filters && !AnalyzeAngledIncludes.has_value())
return;
auto Filter = [Filters](llvm::StringRef Path) {
for (auto &Regex : *Filters)
if (Regex.match(Path))
return true;
return false;
};
Out.Apply.push_back([Filter](const Params &, Config &C) {
C.Diagnostics.Includes.IgnoreHeader.emplace_back(Filter);
Out.Apply.push_back([Filters = std::move(Filters),
AnalyzeAngledIncludes](const Params &, Config &C) {
if (Filters) {
auto Filter = [Filters](llvm::StringRef Path) {
for (auto &Regex : *Filters)
if (Regex.match(Path))
return true;
return false;
};
C.Diagnostics.Includes.IgnoreHeader.emplace_back(std::move(Filter));
}
if (AnalyzeAngledIncludes.has_value())
C.Diagnostics.Includes.AnalyzeAngledIncludes = *AnalyzeAngledIncludes;
});
}

Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/ConfigFragment.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ struct Fragment {
/// unused or missing. These can match any suffix of the header file in
/// question.
std::vector<Located<std::string>> IgnoreHeader;

/// If false (default), unused system headers will be ignored.
/// Standard library headers are analyzed regardless of this option.
std::optional<Located<bool>> AnalyzeAngledIncludes;
};
IncludesBlock Includes;

Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/ConfigYAML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ class Parser {
if (auto Values = scalarValues(N))
F.IgnoreHeader = std::move(*Values);
});
Dict.handle("AnalyzeAngledIncludes", [&](Node &N) {
if (auto Value = boolValue(N, "AnalyzeAngledIncludes"))
F.AnalyzeAngledIncludes = *Value;
});
Dict.parse(N);
}

Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/Hover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ fetchTemplateParameters(const TemplateParameterList *Params,
if (NTTP->hasDefaultArgument()) {
P.Default.emplace();
llvm::raw_string_ostream Out(*P.Default);
NTTP->getDefaultArgument()->printPretty(Out, nullptr, PP);
NTTP->getDefaultArgument().getArgument().print(PP, Out,
/*IncludeType=*/false);
}
} else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
P.Type = printType(TTPD, PP);
Expand Down
32 changes: 21 additions & 11 deletions clang-tools-extra/clangd/IncludeCleaner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,24 +68,30 @@ bool isIgnored(llvm::StringRef HeaderPath, HeaderFilter IgnoreHeaders) {
}

bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
const include_cleaner::PragmaIncludes *PI) {
const include_cleaner::PragmaIncludes *PI,
bool AnalyzeAngledIncludes) {
assert(Inc.HeaderID);
auto HID = static_cast<IncludeStructure::HeaderID>(*Inc.HeaderID);
auto FE = AST.getSourceManager().getFileManager().getFileRef(
AST.getIncludeStructure().getRealPath(HID));
assert(FE);
if (FE->getDir() == AST.getPreprocessor()
.getHeaderSearchInfo()
.getModuleMap()
.getBuiltinDir())
.getHeaderSearchInfo()
.getModuleMap()
.getBuiltinDir())
return false;
if (PI && PI->shouldKeep(*FE))
return false;
// FIXME(kirillbobyrev): We currently do not support the umbrella headers.
// System headers are likely to be standard library headers.
// Until we have good support for umbrella headers, don't warn about them.
if (Inc.Written.front() == '<')
return tooling::stdlib::Header::named(Inc.Written).has_value();
// Until we have good support for umbrella headers, don't warn about them
// (unless analysis is explicitly enabled).
if (Inc.Written.front() == '<') {
if (tooling::stdlib::Header::named(Inc.Written))
return true;
if (!AnalyzeAngledIncludes)
return false;
}
if (PI) {
// Check if main file is the public interface for a private header. If so we
// shouldn't diagnose it as unused.
Expand Down Expand Up @@ -266,7 +272,8 @@ Fix fixAll(const Fix &RemoveAllUnused, const Fix &AddAllMissing) {

std::vector<const Inclusion *>
getUnused(ParsedAST &AST,
const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles) {
const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles,
bool AnalyzeAngledIncludes) {
trace::Span Tracer("IncludeCleaner::getUnused");
std::vector<const Inclusion *> Unused;
for (const Inclusion &MFI : AST.getIncludeStructure().MainFileIncludes) {
Expand All @@ -275,7 +282,8 @@ getUnused(ParsedAST &AST,
auto IncludeID = static_cast<IncludeStructure::HeaderID>(*MFI.HeaderID);
if (ReferencedFiles.contains(IncludeID))
continue;
if (!mayConsiderUnused(MFI, AST, &AST.getPragmaIncludes())) {
if (!mayConsiderUnused(MFI, AST, &AST.getPragmaIncludes(),
AnalyzeAngledIncludes)) {
dlog("{0} was not used, but is not eligible to be diagnosed as unused",
MFI.Written);
continue;
Expand Down Expand Up @@ -347,7 +355,8 @@ include_cleaner::Includes convertIncludes(const ParsedAST &AST) {
return ConvertedIncludes;
}

IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) {
IncludeCleanerFindings
computeIncludeCleanerFindings(ParsedAST &AST, bool AnalyzeAngledIncludes) {
// Interaction is only polished for C/CPP.
if (AST.getLangOpts().ObjC)
return {};
Expand Down Expand Up @@ -432,7 +441,8 @@ IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) {
MapInfo::getHashValue(RHS.Symbol);
});
MissingIncludes.erase(llvm::unique(MissingIncludes), MissingIncludes.end());
std::vector<const Inclusion *> UnusedIncludes = getUnused(AST, Used);
std::vector<const Inclusion *> UnusedIncludes =
getUnused(AST, Used, AnalyzeAngledIncludes);
return {std::move(UnusedIncludes), std::move(MissingIncludes)};
}

Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clangd/IncludeCleaner.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ struct IncludeCleanerFindings {
std::vector<MissingIncludeDiagInfo> MissingIncludes;
};

IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST);
IncludeCleanerFindings
computeIncludeCleanerFindings(ParsedAST &AST,
bool AnalyzeAngledIncludes = false);

using HeaderFilter = llvm::ArrayRef<std::function<bool(llvm::StringRef)>>;
std::vector<Diag>
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/ParsedAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,8 @@ std::vector<Diag> getIncludeCleanerDiags(ParsedAST &AST, llvm::StringRef Code,
Cfg.Diagnostics.UnusedIncludes == Config::IncludesPolicy::None;
if (SuppressMissing && SuppressUnused)
return {};
auto Findings = computeIncludeCleanerFindings(AST);
auto Findings = computeIncludeCleanerFindings(
AST, Cfg.Diagnostics.Includes.AnalyzeAngledIncludes);
if (SuppressMissing)
Findings.MissingIncludes.clear();
if (SuppressUnused)
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/../quality/CompletionModel.cmake)
gen_decision_forest(${CMAKE_CURRENT_SOURCE_DIR}/decision_forest_model DecisionForestRuntimeTest ::ns1::ns2::test::Example)

add_custom_target(ClangdUnitTests)
set_target_properties(ClangdUnitTests PROPERTIES FOLDER "Clang Tools Extra/Tests")
add_unittest(ClangdUnitTests ClangdTests
Annotations.cpp
ASTTests.cpp
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/unittests/ClangdTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ TEST(ClangdServerTest, SearchLibDir) {
ErrorCheckingCallbacks DiagConsumer;
MockCompilationDatabase CDB;
CDB.ExtraClangFlags.insert(CDB.ExtraClangFlags.end(),
{"-xc++", "-target", "x86_64-linux-unknown",
{"-xc++", "--target=x86_64-unknown-linux-gnu",
"-m64", "--gcc-toolchain=/randomusr",
"-stdlib=libstdc++"});
ClangdServer Server(CDB, FS, ClangdServer::optsForTest(), &DiagConsumer);
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@ TEST_F(ConfigCompileTests, DiagnosticsIncludeCleaner) {
};
EXPECT_TRUE(HeaderFilter("foo.h"));
EXPECT_FALSE(HeaderFilter("bar.h"));

Frag = {};
EXPECT_FALSE(Conf.Diagnostics.Includes.AnalyzeAngledIncludes);
Frag.Diagnostics.Includes.AnalyzeAngledIncludes = true;
EXPECT_TRUE(compileAndApply());
EXPECT_TRUE(Conf.Diagnostics.Includes.AnalyzeAngledIncludes);
}

TEST_F(ConfigCompileTests, DiagnosticSuppression) {
Expand Down
15 changes: 15 additions & 0 deletions clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,21 @@ TEST(ParseYAML, IncludesIgnoreHeader) {
ElementsAre(val("foo"), val("bar")));
}

TEST(ParseYAML, IncludesAnalyzeAngledIncludes) {
CapturedDiags Diags;
Annotations YAML(R"yaml(
Diagnostics:
Includes:
AnalyzeAngledIncludes: true
)yaml");
auto Results =
Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
ASSERT_THAT(Diags.Diagnostics, IsEmpty());
ASSERT_EQ(Results.size(), 1u);
EXPECT_THAT(Results[0].Diagnostics.Includes.AnalyzeAngledIncludes,
llvm::ValueIs(val(true)));
}

TEST(ParseYAML, Style) {
CapturedDiags Diags;
Annotations YAML(R"yaml(
Expand Down
44 changes: 44 additions & 0 deletions clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ TEST(IncludeCleaner, GetUnusedHeaders) {
#include "unguarded.h"
#include "unused.h"
#include <system_header.h>
#include <non_system_angled_header.h>
void foo() {
a();
b();
Expand All @@ -122,6 +123,7 @@ TEST(IncludeCleaner, GetUnusedHeaders) {
TU.AdditionalFiles["dir/c.h"] = guard("void c();");
TU.AdditionalFiles["unused.h"] = guard("void unused();");
TU.AdditionalFiles["dir/unused.h"] = guard("void dirUnused();");
TU.AdditionalFiles["dir/non_system_angled_header.h"] = guard("");
TU.AdditionalFiles["system/system_header.h"] = guard("");
TU.AdditionalFiles["unguarded.h"] = "";
TU.ExtraArgs.push_back("-I" + testPath("dir"));
Expand All @@ -135,6 +137,48 @@ TEST(IncludeCleaner, GetUnusedHeaders) {
Pointee(writtenInclusion("\"dir/unused.h\""))));
}

TEST(IncludeCleaner, IgnoredAngledHeaders) {
// Currently the default behavior is to ignore unused angled includes
auto TU = TestTU::withCode(R"cpp(
#include <system_header.h>
#include <system_unused.h>
#include <non_system_angled_unused.h>
SystemClass x;
)cpp");
TU.AdditionalFiles["system/system_header.h"] = guard("class SystemClass {};");
TU.AdditionalFiles["system/system_unused.h"] = guard("");
TU.AdditionalFiles["dir/non_system_angled_unused.h"] = guard("");
TU.ExtraArgs = {
"-isystem" + testPath("system"),
"-I" + testPath("dir"),
};
auto AST = TU.build();
IncludeCleanerFindings Findings = computeIncludeCleanerFindings(AST);
EXPECT_THAT(Findings.UnusedIncludes, IsEmpty());
}

TEST(IncludeCleaner, UnusedAngledHeaders) {
auto TU = TestTU::withCode(R"cpp(
#include <system_header.h>
#include <system_unused.h>
#include <non_system_angled_unused.h>
SystemClass x;
)cpp");
TU.AdditionalFiles["system/system_header.h"] = guard("class SystemClass {};");
TU.AdditionalFiles["system/system_unused.h"] = guard("");
TU.AdditionalFiles["dir/non_system_angled_unused.h"] = guard("");
TU.ExtraArgs = {
"-isystem" + testPath("system"),
"-I" + testPath("dir"),
};
auto AST = TU.build();
IncludeCleanerFindings Findings = computeIncludeCleanerFindings(AST, true);
EXPECT_THAT(Findings.UnusedIncludes,
UnorderedElementsAre(
Pointee(writtenInclusion("<system_unused.h>")),
Pointee(writtenInclusion("<non_system_angled_unused.h>"))));
}

TEST(IncludeCleaner, ComputeMissingHeaders) {
Annotations MainFile(R"cpp(
#include "a.h"
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ if (DOXYGEN_FOUND)
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating clang doxygen documentation." VERBATIM)
set_target_properties(doxygen-clang-tools PROPERTIES FOLDER "Clang Tools Extra/Docs")

if (LLVM_BUILD_DOCS)
add_dependencies(doxygen doxygen-clang-tools)
Expand Down
13 changes: 12 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ Objective-C
Miscellaneous
^^^^^^^^^^^^^

- Added a boolean option `AnalyzeAngledIncludes` to `Includes` config section,
which allows to enable unused includes detection for all angled ("system") headers.
At this moment umbrella headers are not supported, so enabling this option
may result in false-positives.

Improvements to clang-doc
-------------------------

Expand Down Expand Up @@ -363,6 +368,10 @@ Changes in existing checks
<clang-tidy/checks/readability/const-return-type>` check to eliminate false
positives when returning types with const not at the top level.

- Improved :doc:`readability-container-size-empty
<clang-tidy/checks/readability/container-size-empty>` check to prevent false
positives when utilizing ``size`` or ``length`` methods that accept parameter.

- Improved :doc:`readability-duplicate-include
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
directives that form the filename using macro.
Expand All @@ -381,7 +390,9 @@ Changes in existing checks
- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check to provide
valid fix suggestions for ``static_cast`` without a preceding space and
fixed problem with duplicate parentheses in double implicit casts.
fixed problem with duplicate parentheses in double implicit casts. Corrected
the fix suggestions for C23 and later by using C-style casts instead of
``static_cast``.

- Improved :doc:`readability-redundant-inline-specifier
<clang-tidy/checks/readability/redundant-inline-specifier>` check to properly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ The following options are described below:

.. code-block:: c++

int doubler(int x) // warns that x is too short
{
return 2 * x;
}
int i = 42; // warns that 'i' is too short

This check does not have any fix suggestions in the general case since
variable names have semantic value.
Expand All @@ -50,7 +47,10 @@ The following options are described below:

.. code-block:: c++

int i = 42; // warns that 'i' is too short
int doubler(int x) // warns that x is too short
{
return 2 * x;
}

This check does not have any fix suggestions in the general case since
variable names have semantic value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ The rules for generating fix-it hints are:
- ``if (!pointer)`` is changed to ``if (pointer == nullptr)``,

- in case of conversions from bool to other built-in types, an explicit
``static_cast`` is proposed to make it clear that a conversion is taking
place:
``static_cast`` (or a C-style cast since C23) is proposed to make it clear
that a conversion is taking place:

- ``int integer = boolean;`` is changed to
``int integer = static_cast<int>(boolean);``,
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/include-cleaner/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
)

add_custom_target(ClangIncludeCleanerUnitTests)
set_target_properties(ClangIncludeCleanerUnitTests PROPERTIES FOLDER "Clang Tools Extra/Tests")
add_unittest(ClangIncludeCleanerUnitTests ClangIncludeCleanerTests
AnalysisTest.cpp
FindHeadersTest.cpp
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/pseudo/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ add_custom_command(OUTPUT ${cxx_bnf_inc}
add_custom_target(cxx_gen
DEPENDS ${cxx_symbols_inc} ${cxx_bnf_inc}
VERBATIM)
set_target_properties(cxx_gen PROPERTIES FOLDER "Clang Tools Extra/Sourcegenning")
1 change: 1 addition & 0 deletions clang-tools-extra/pseudo/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ add_custom_command(OUTPUT HTMLForestResources.inc
DEPENDS ${CLANG_SOURCE_DIR}/utils/bundle_resources.py HTMLForest.css HTMLForest.js HTMLForest.html
VERBATIM)
add_custom_target(clang-pseudo-resources DEPENDS HTMLForestResources.inc)
set_target_properties(clang-pseudo-resources PROPERTIES FOLDER "Clang Tools Extra/Resources")
add_dependencies(clang-pseudo clang-pseudo-resources)
1 change: 1 addition & 0 deletions clang-tools-extra/pseudo/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
)

add_custom_target(ClangPseudoUnitTests)
set_target_properties(ClangPseudoUnitTests PROPERTIES FOLDER "Clang Tools Extra/Tests")
add_unittest(ClangPseudoUnitTests ClangPseudoTests
BracketTest.cpp
CXXTest.cpp
Expand Down
1 change: 0 additions & 1 deletion clang-tools-extra/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ add_lit_testsuite(check-clang-extra "Running clang-tools-extra/test"
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CLANG_TOOLS_TEST_DEPS}
)
set_target_properties(check-clang-extra PROPERTIES FOLDER "Clang extra tools' tests")

add_lit_testsuites(CLANG-EXTRA ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${CLANG_TOOLS_TEST_DEPS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -861,3 +861,31 @@ namespace PR72619 {
if (0 >= s.size()) {}
}
}

namespace PR88203 {
struct SS {
bool empty() const;
int size() const;
int length(int) const;
};

struct SU {
bool empty() const;
int size(int) const;
int length() const;
};

void f(const SS& s) {
if (0 == s.length(1)) {}
if (0 == s.size()) {}
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
// CHECK-FIXES: {{^ }}if (s.empty()) {}{{$}}
}

void f(const SU& s) {
if (0 == s.size(1)) {}
if (0 == s.length()) {}
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: the 'empty' method should be used to check for emptiness instead of 'length' [readability-container-size-empty]
// CHECK-FIXES: {{^ }}if (s.empty()) {}{{$}}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,354 @@
// RUN: %check_clang_tidy %s readability-implicit-bool-conversion %t -- -- -std=c23

#undef NULL
#define NULL 0L

void functionTakingBool(bool);
void functionTakingInt(int);
void functionTakingUnsignedLong(unsigned long);
void functionTakingChar(char);
void functionTakingFloat(float);
void functionTakingDouble(double);
void functionTakingSignedChar(signed char);


////////// Implicit conversion from bool.

void implicitConversionFromBoolSimpleCases() {
bool boolean = true;

functionTakingBool(boolean);

functionTakingInt(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: implicit conversion 'bool' -> 'int' [readability-implicit-bool-conversion]
// CHECK-FIXES: functionTakingInt((int)boolean);

functionTakingUnsignedLong(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'bool' -> 'unsigned long'
// CHECK-FIXES: functionTakingUnsignedLong((unsigned long)boolean);

functionTakingChar(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'bool' -> 'char'
// CHECK-FIXES: functionTakingChar((char)boolean);

functionTakingFloat(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion 'bool' -> 'float'
// CHECK-FIXES: functionTakingFloat((float)boolean);

functionTakingDouble(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'bool' -> 'double'
// CHECK-FIXES: functionTakingDouble((double)boolean);
}

float implicitConversionFromBoolInReturnValue() {
bool boolean = false;
return boolean;
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion 'bool' -> 'float'
// CHECK-FIXES: return (float)boolean;
}

void implicitConversionFromBoolInSingleBoolExpressions(bool b1, bool b2) {
bool boolean = true;
boolean = b1 ^ b2;
boolean |= !b1 || !b2;
boolean &= b1;

int integer = boolean - 3;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: int integer = (int)boolean - 3;

float floating = boolean / 0.3f;
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion 'bool' -> 'float'
// CHECK-FIXES: float floating = (float)boolean / 0.3f;

char character = boolean;
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion 'bool' -> 'char'
// CHECK-FIXES: char character = (char)boolean;
}

void implicitConversionFromBoolInComplexBoolExpressions() {
bool boolean = true;
bool anotherBoolean = false;

int integer = boolean && anotherBoolean;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit conversion 'bool' -> 'int'
// CHECK-MESSAGES: :[[@LINE-2]]:28: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: int integer = (int)boolean && (int)anotherBoolean;

float floating = (boolean || anotherBoolean) * 0.3f;
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: implicit conversion 'bool' -> 'int'
// CHECK-MESSAGES: :[[@LINE-2]]:32: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: float floating = ((int)boolean || (int)anotherBoolean) * 0.3f;

double doubleFloating = (boolean && (anotherBoolean || boolean)) * 0.3;
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit conversion 'bool' -> 'int'
// CHECK-MESSAGES: :[[@LINE-2]]:40: warning: implicit conversion 'bool' -> 'int'
// CHECK-MESSAGES: :[[@LINE-3]]:58: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: double doubleFloating = ((int)boolean && ((int)anotherBoolean || (int)boolean)) * 0.3;
}

void implicitConversionFromBoolLiterals() {
functionTakingInt(true);
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: functionTakingInt(1);

functionTakingUnsignedLong(false);
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'bool' -> 'unsigned long'
// CHECK-FIXES: functionTakingUnsignedLong(0u);

functionTakingSignedChar(true);
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit conversion 'bool' -> 'signed char'
// CHECK-FIXES: functionTakingSignedChar(1);

functionTakingFloat(false);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion 'bool' -> 'float'
// CHECK-FIXES: functionTakingFloat(0.0f);

functionTakingDouble(true);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'bool' -> 'double'
// CHECK-FIXES: functionTakingDouble(1.0);
}

void implicitConversionFromBoolInComparisons() {
bool boolean = true;
int integer = 0;

functionTakingBool(boolean == integer);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: functionTakingBool((int)boolean == integer);

functionTakingBool(integer != boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: functionTakingBool(integer != (int)boolean);
}

void ignoreBoolComparisons() {
bool boolean = true;
bool anotherBoolean = false;

functionTakingBool(boolean == anotherBoolean);
functionTakingBool(boolean != anotherBoolean);
}

void ignoreExplicitCastsFromBool() {
bool boolean = true;

int integer = (int)boolean + 3;
float floating = (float)boolean * 0.3f;
char character = (char)boolean;
}

void ignoreImplicitConversionFromBoolInMacroExpansions() {
bool boolean = true;

#define CAST_FROM_BOOL_IN_MACRO_BODY boolean + 3
int integerFromMacroBody = CAST_FROM_BOOL_IN_MACRO_BODY;

#define CAST_FROM_BOOL_IN_MACRO_ARGUMENT(x) x + 3
int integerFromMacroArgument = CAST_FROM_BOOL_IN_MACRO_ARGUMENT(boolean);
}

////////// Implicit conversions to bool.

void implicitConversionToBoolSimpleCases() {
int integer = 10;
functionTakingBool(integer);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: functionTakingBool(integer != 0);

unsigned long unsignedLong = 10;
functionTakingBool(unsignedLong);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'unsigned long' -> 'bool'
// CHECK-FIXES: functionTakingBool(unsignedLong != 0u);

float floating = 0.0f;
functionTakingBool(floating);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'float' -> 'bool'
// CHECK-FIXES: functionTakingBool(floating != 0.0f);

double doubleFloating = 1.0f;
functionTakingBool(doubleFloating);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'double' -> 'bool'
// CHECK-FIXES: functionTakingBool(doubleFloating != 0.0);

signed char character = 'a';
functionTakingBool(character);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'signed char' -> 'bool'
// CHECK-FIXES: functionTakingBool(character != 0);

int* pointer = nullptr;
functionTakingBool(pointer);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int *' -> 'bool'
// CHECK-FIXES: functionTakingBool(pointer != nullptr);
}

void implicitConversionToBoolInSingleExpressions() {
int integer = 10;
bool boolComingFromInt;
boolComingFromInt = integer;
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: boolComingFromInt = (integer != 0);

float floating = 10.0f;
bool boolComingFromFloat;
boolComingFromFloat = floating;
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit conversion 'float' -> 'bool'
// CHECK-FIXES: boolComingFromFloat = (floating != 0.0f);

signed char character = 'a';
bool boolComingFromChar;
boolComingFromChar = character;
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'signed char' -> 'bool'
// CHECK-FIXES: boolComingFromChar = (character != 0);

int* pointer = nullptr;
bool boolComingFromPointer;
boolComingFromPointer = pointer;
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: implicit conversion 'int *' -> 'bool'
// CHECK-FIXES: boolComingFromPointer = (pointer != nullptr);
}

void implicitConversionToBoolInComplexExpressions() {
bool boolean = true;

int integer = 10;
int anotherInteger = 20;
bool boolComingFromInteger;
boolComingFromInteger = integer + anotherInteger;
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: boolComingFromInteger = ((integer + anotherInteger) != 0);
}

void implicitConversionInNegationExpressions() {
int integer = 10;
bool boolComingFromNegatedInt;
boolComingFromNegatedInt = !integer;
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: boolComingFromNegatedInt = ((!integer) != 0);
}

bool implicitConversionToBoolInReturnValue() {
float floating = 1.0f;
return floating;
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion 'float' -> 'bool'
// CHECK-FIXES: return floating != 0.0f;
}

void implicitConversionToBoolFromLiterals() {
functionTakingBool(0);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: functionTakingBool(false);

functionTakingBool(1);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: functionTakingBool(true);

functionTakingBool(2ul);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'unsigned long' -> 'bool'
// CHECK-FIXES: functionTakingBool(true);

functionTakingBool(0.0f);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'float' -> 'bool'
// CHECK-FIXES: functionTakingBool(false);

functionTakingBool(1.0f);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'float' -> 'bool'
// CHECK-FIXES: functionTakingBool(true);

functionTakingBool(2.0);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'double' -> 'bool'
// CHECK-FIXES: functionTakingBool(true);

functionTakingBool('\0');
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: functionTakingBool(false);

functionTakingBool('a');
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: functionTakingBool(true);

functionTakingBool("");
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'char *' -> 'bool'
// CHECK-FIXES: functionTakingBool(true);

functionTakingBool("abc");
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'char *' -> 'bool'
// CHECK-FIXES: functionTakingBool(true);

functionTakingBool(NULL);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'long' -> 'bool'
// CHECK-FIXES: functionTakingBool(false);
}

void implicitConversionToBoolFromUnaryMinusAndZeroLiterals() {
functionTakingBool(-0);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: functionTakingBool((-0) != 0);

functionTakingBool(-0.0f);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'float' -> 'bool'
// CHECK-FIXES: functionTakingBool((-0.0f) != 0.0f);

functionTakingBool(-0.0);
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: implicit conversion 'double' -> 'bool'
// CHECK-FIXES: functionTakingBool((-0.0) != 0.0);
}

void ignoreExplicitCastsToBool() {
int integer = 10;
bool boolComingFromInt = (bool)integer;

float floating = 10.0f;
bool boolComingFromFloat = (bool)floating;

char character = 'a';
bool boolComingFromChar = (bool)character;

int* pointer = nullptr;
bool booleanComingFromPointer = (bool)pointer;
}

void ignoreImplicitConversionToBoolInMacroExpansions() {
int integer = 3;

#define CAST_TO_BOOL_IN_MACRO_BODY integer && false
bool boolFromMacroBody = CAST_TO_BOOL_IN_MACRO_BODY;

#define CAST_TO_BOOL_IN_MACRO_ARGUMENT(x) x || true
bool boolFromMacroArgument = CAST_TO_BOOL_IN_MACRO_ARGUMENT(integer);
}

int implicitConversionReturnInt()
{
return true;
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: return 1
}

int implicitConversionReturnIntWithParens()
{
return (true);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'bool' -> 'int'
// CHECK-FIXES: return 1
}

bool implicitConversionReturnBool()
{
return 1;
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: return true
}

bool implicitConversionReturnBoolWithParens()
{
return (1);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'int' -> 'bool'
// CHECK-FIXES: return true
}

int keepCompactReturnInC_PR71848() {
bool foo = false;
return( foo );
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit conversion 'bool' -> 'int' [readability-implicit-bool-conversion]
// CHECK-FIXES: return(int)( foo );
}
2 changes: 1 addition & 1 deletion clang-tools-extra/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
add_custom_target(ExtraToolsUnitTests)
set_target_properties(ExtraToolsUnitTests PROPERTIES FOLDER "Extra Tools Unit Tests")
set_target_properties(ExtraToolsUnitTests PROPERTIES FOLDER "Clang Tools Extra/Tests")

function(add_extra_unittest test_dirname)
add_unittest(ExtraToolsUnitTests ${test_dirname} ${ARGN})
Expand Down
13 changes: 6 additions & 7 deletions clang/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.20.0)
set(LLVM_SUBPROJECT_TITLE "Clang")

if(NOT DEFINED LLVM_COMMON_CMAKE_UTILS)
set(LLVM_COMMON_CMAKE_UTILS ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
Expand Down Expand Up @@ -349,10 +350,7 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wno-long-long")
endif ()

check_cxx_compiler_flag("-Werror -Wnested-anon-types" CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG)
if( CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types" )
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types" )
endif ()

# Determine HOST_LINK_VERSION on Darwin.
Expand Down Expand Up @@ -394,14 +392,15 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
# Installing the headers needs to depend on generating any public
# tablegen'd headers.
add_custom_target(clang-headers DEPENDS clang-tablegen-targets)
set_target_properties(clang-headers PROPERTIES FOLDER "Misc")
set_target_properties(clang-headers PROPERTIES FOLDER "Clang/Resources")
if(NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-clang-headers
DEPENDS clang-headers
COMPONENT clang-headers)
endif()

add_custom_target(bash-autocomplete DEPENDS utils/bash-autocomplete.sh)
set_target_properties(bash-autocomplete PROPERTIES FOLDER "Clang/Misc")
install(FILES utils/bash-autocomplete.sh
DESTINATION "${CMAKE_INSTALL_DATADIR}/clang"
COMPONENT bash-autocomplete)
Expand Down Expand Up @@ -482,7 +481,7 @@ add_custom_target(clang-tablegen-targets
omp_gen
ClangDriverOptions
${CLANG_TABLEGEN_TARGETS})
set_target_properties(clang-tablegen-targets PROPERTIES FOLDER "Misc")
set_target_properties(clang-tablegen-targets PROPERTIES FOLDER "Clang/Tablegenning/Targets")
list(APPEND LLVM_COMMON_DEPENDS clang-tablegen-targets)

# Force target to be built as soon as possible. Clang modules builds depend
Expand Down Expand Up @@ -547,7 +546,7 @@ endif()

# Custom target to install all clang libraries.
add_custom_target(clang-libraries)
set_target_properties(clang-libraries PROPERTIES FOLDER "Misc")
set_target_properties(clang-libraries PROPERTIES FOLDER "Clang/Install")

if(NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-clang-libraries
Expand Down
2 changes: 1 addition & 1 deletion clang/bindings/python/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ add_custom_target(check-clang-python
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..)

set(RUN_PYTHON_TESTS TRUE)
set_target_properties(check-clang-python PROPERTIES FOLDER "Clang tests")
set_target_properties(check-clang-python PROPERTIES FOLDER "Clang/Tests")

# Tests require libclang.so which is only built with LLVM_ENABLE_PIC=ON
if(NOT LLVM_ENABLE_PIC)
Expand Down
Loading