1,213 changes: 1,213 additions & 0 deletions bolt/docs/CommandLineArgumentReference.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions bolt/include/bolt/Core/BinaryFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,8 @@ class BinaryFunction {
return const_cast<BinaryFunction *>(this)->getInstructionAtOffset(Offset);
}

std::optional<MCInst> disassembleInstructionAtOffset(uint64_t Offset) const;

/// Return offset for the first instruction. If there is data at the
/// beginning of a function then offset of the first instruction could
/// be different from 0
Expand Down
14 changes: 14 additions & 0 deletions bolt/include/bolt/Core/DIEBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ class DIEBuilder {
uint64_t UnitSize{0};
llvm::DenseSet<uint64_t> AllProcessed;
DWARF5AcceleratorTable &DebugNamesTable;
// Unordered map to handle name collision if output DWO directory is
// specified.
std::unordered_map<std::string, uint32_t> NameToIndexMap;

/// Returns current state of the DIEBuilder
State &getState() { return *BuilderState.get(); }
Expand Down Expand Up @@ -384,6 +387,17 @@ class DIEBuilder {
bool deleteValue(DIEValueList *Die, dwarf::Attribute Attribute) {
return Die->deleteValue(Attribute);
}
/// Updates DWO Name and Compilation directory for Skeleton CU \p Unit.
std::string updateDWONameCompDir(DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter,
DWARFUnit &SkeletonCU,
std::optional<StringRef> DwarfOutputPath,
std::optional<StringRef> DWONameToUse);
/// Updates DWO Name and Compilation directory for Type Units.
void updateDWONameCompDirForTypes(DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter, DWARFUnit &Unit,
std::optional<StringRef> DwarfOutputPath,
const StringRef DWOName);
};
} // namespace bolt
} // namespace llvm
Expand Down
14 changes: 11 additions & 3 deletions bolt/include/bolt/Core/DebugData.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ class DebugAddrWriterDwarf5 : public DebugAddrWriter {
using DebugStrOffsetsBufferVector = SmallVector<char, 16>;
class DebugStrOffsetsWriter {
public:
DebugStrOffsetsWriter() {
DebugStrOffsetsWriter(BinaryContext &BC) : BC(BC) {
StrOffsetsBuffer = std::make_unique<DebugStrOffsetsBufferVector>();
StrOffsetsStream = std::make_unique<raw_svector_ostream>(*StrOffsetsBuffer);
}
Expand Down Expand Up @@ -460,20 +460,27 @@ class DebugStrOffsetsWriter {
StrOffsets.clear();
}

bool isStrOffsetsSectionModified() const {
return StrOffsetSectionWasModified;
}

private:
std::unique_ptr<DebugStrOffsetsBufferVector> StrOffsetsBuffer;
std::unique_ptr<raw_svector_ostream> StrOffsetsStream;
std::map<uint32_t, uint32_t> IndexToAddressMap;
SmallVector<uint32_t, 5> StrOffsets;
std::unordered_map<uint64_t, uint64_t> ProcessedBaseOffsets;
bool StrOffsetSectionWasModified = false;
BinaryContext &BC;
};

using DebugStrBufferVector = SmallVector<char, 16>;
class DebugStrWriter {
public:
DebugStrWriter() = delete;
DebugStrWriter(BinaryContext &BC) : BC(BC) { create(); }
DebugStrWriter(DWARFContext &DwCtx, bool IsDWO) : DwCtx(DwCtx), IsDWO(IsDWO) {
create();
}
std::unique_ptr<DebugStrBufferVector> releaseBuffer() {
return std::move(StrBuffer);
}
Expand All @@ -495,7 +502,8 @@ class DebugStrWriter {
void create();
std::unique_ptr<DebugStrBufferVector> StrBuffer;
std::unique_ptr<raw_svector_ostream> StrStream;
BinaryContext &BC;
DWARFContext &DwCtx;
bool IsDWO;
};

enum class LocWriterKind { DebugLocWriter, DebugLoclistWriter };
Expand Down
4 changes: 2 additions & 2 deletions bolt/include/bolt/Core/MCPlusBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,8 @@ class MCPlusBuilder {
return false;
}

/// Check whether we support inverting this branch
virtual bool isUnsupportedBranch(const MCInst &Inst) const { return false; }
/// Check whether this conditional branch can be reversed
virtual bool isReversibleBranch(const MCInst &Inst) const { return true; }

/// Return true of the instruction is of pseudo kind.
virtual bool isPseudo(const MCInst &Inst) const {
Expand Down
4 changes: 0 additions & 4 deletions bolt/include/bolt/Passes/FrameAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,6 @@ class FrameAnalysis {
std::unique_ptr<StackPointerTracking>>
SPTMap;

/// A vector that stores ids of the allocators that are used in SPT
/// computation
std::vector<MCPlusBuilder::AllocatorIdTy> SPTAllocatorsId;

public:
explicit FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG);

Expand Down
2 changes: 1 addition & 1 deletion bolt/include/bolt/Passes/IndirectCallPromotion.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class IndirectCallPromotion : public BinaryFunctionPass {
struct Location {
MCSymbol *Sym{nullptr};
uint64_t Addr{0};
bool isValid() const { return Sym || (!Sym && Addr != 0); }
bool isValid() const { return Sym || Addr != 0; }
Location() {}
explicit Location(MCSymbol *Sym) : Sym(Sym) {}
explicit Location(uint64_t Addr) : Addr(Addr) {}
Expand Down
4 changes: 2 additions & 2 deletions bolt/include/bolt/Profile/DataAggregator.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,14 @@ class DataAggregator : public DataReader {
uint64_t ExternCount{0};
};

struct BranchInfo {
struct TakenBranchInfo {
uint64_t TakenCount{0};
uint64_t MispredCount{0};
};

/// Intermediate storage for profile data. We save the results of parsing
/// and use them later for processing and assigning profile.
std::unordered_map<Trace, BranchInfo, TraceHash> BranchLBRs;
std::unordered_map<Trace, TakenBranchInfo, TraceHash> BranchLBRs;
std::unordered_map<Trace, FTInfo, TraceHash> FallthroughLBRs;
std::vector<AggregatedLBREntry> AggregatedLBRs;
std::unordered_map<uint64_t, uint64_t> BasicSamples;
Expand Down
8 changes: 6 additions & 2 deletions bolt/include/bolt/Rewrite/DWARFRewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,16 @@ class DWARFRewriter {
using OverriddenSectionsMap = std::unordered_map<DWARFSectionKind, StringRef>;
/// Output .dwo files.
void writeDWOFiles(DWARFUnit &, const OverriddenSectionsMap &,
const std::string &, DebugLocWriter &);
const std::string &, DebugLocWriter &,
DebugStrOffsetsWriter &, DebugStrWriter &);
using KnownSectionsEntry = std::pair<MCSection *, DWARFSectionKind>;
struct DWPState {
std::unique_ptr<ToolOutputFile> Out;
std::unique_ptr<BinaryContext> TmpBC;
std::unique_ptr<MCStreamer> Streamer;
std::unique_ptr<DWPStringPool> Strings;
/// Used to store String sections for .dwo files if they are being modified.
std::vector<std::unique_ptr<DebugBufferVector>> StrSections;
const MCObjectFileInfo *MCOFI = nullptr;
const DWARFUnitIndex *CUIndex = nullptr;
std::deque<SmallString<32>> UncompressedSections;
Expand All @@ -230,7 +233,8 @@ class DWARFRewriter {

/// add content of dwo to .dwp file.
void updateDWP(DWARFUnit &, const OverriddenSectionsMap &, const UnitMeta &,
UnitMetaVectorType &, DWPState &, DebugLocWriter &);
UnitMetaVectorType &, DWPState &, DebugLocWriter &,
DebugStrOffsetsWriter &, DebugStrWriter &);
};

} // namespace bolt
Expand Down
9 changes: 4 additions & 5 deletions bolt/lib/Core/BinaryBasicBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,10 @@ bool BinaryBasicBlock::validateSuccessorInvariants() {
break;
}
case 2:
Valid = (CondBranch &&
(TBB == getConditionalSuccessor(true)->getLabel() &&
((!UncondBranch && !FBB) ||
(UncondBranch &&
FBB == getConditionalSuccessor(false)->getLabel()))));
Valid =
CondBranch && TBB == getConditionalSuccessor(true)->getLabel() &&
(UncondBranch ? FBB == getConditionalSuccessor(false)->getLabel()
: !FBB);
break;
}
}
Expand Down
28 changes: 20 additions & 8 deletions bolt/lib/Core/BinaryFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,21 @@ void BinaryFunction::handleAArch64IndirectCall(MCInst &Instruction,
}
}

std::optional<MCInst>
BinaryFunction::disassembleInstructionAtOffset(uint64_t Offset) const {
assert(CurrentState == State::Empty && "Function should not be disassembled");
assert(Offset < MaxSize && "Invalid offset");
ErrorOr<ArrayRef<unsigned char>> FunctionData = getData();
assert(FunctionData && "Cannot get function as data");
MCInst Instr;
uint64_t InstrSize = 0;
const uint64_t InstrAddress = getAddress() + Offset;
if (BC.DisAsm->getInstruction(Instr, InstrSize, FunctionData->slice(Offset),
InstrAddress, nulls()))
return Instr;
return std::nullopt;
}

Error BinaryFunction::disassemble() {
NamedRegionTimer T("disassemble", "Disassemble function", "buildfuncs",
"Build Binary Functions", opts::TimeBuild);
Expand Down Expand Up @@ -1269,7 +1284,7 @@ Error BinaryFunction::disassemble() {
const bool IsCondBranch = MIB->isConditionalBranch(Instruction);
MCSymbol *TargetSymbol = nullptr;

if (BC.MIB->isUnsupportedBranch(Instruction)) {
if (!BC.MIB->isReversibleBranch(Instruction)) {
setIgnored();
if (BinaryFunction *TargetFunc =
BC.getBinaryFunctionContainingAddress(TargetAddress))
Expand Down Expand Up @@ -3237,12 +3252,9 @@ bool BinaryFunction::validateCFG() const {
if (CurrentState == State::CFG_Finalized)
return true;

bool Valid = true;
for (BinaryBasicBlock *BB : BasicBlocks)
Valid &= BB->validateSuccessorInvariants();

if (!Valid)
return Valid;
if (!BB->validateSuccessorInvariants())
return false;

// Make sure all blocks in CFG are valid.
auto validateBlock = [this](const BinaryBasicBlock *BB, StringRef Desc) {
Expand Down Expand Up @@ -3311,7 +3323,7 @@ bool BinaryFunction::validateCFG() const {
}
}

return Valid;
return true;
}

void BinaryFunction::fixBranches() {
Expand Down Expand Up @@ -3369,7 +3381,7 @@ void BinaryFunction::fixBranches() {

// Reverse branch condition and swap successors.
auto swapSuccessors = [&]() {
if (MIB->isUnsupportedBranch(*CondBranch)) {
if (!MIB->isReversibleBranch(*CondBranch)) {
if (opts::Verbosity) {
BC.outs() << "BOLT-INFO: unable to swap successors in " << *this
<< '\n';
Expand Down
85 changes: 85 additions & 0 deletions bolt/lib/Core/DIEBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"

#include <algorithm>
Expand All @@ -41,6 +42,90 @@ extern cl::opt<unsigned> Verbosity;
namespace llvm {
namespace bolt {

/// Returns DWO Name to be used to update DW_AT_dwo_name/DW_AT_GNU_dwo_name
/// either in CU or TU unit die. Handles case where user specifies output DWO
/// directory, and there are duplicate names. Assumes DWO ID is unique.
static std::string
getDWOName(llvm::DWARFUnit &CU,
std::unordered_map<std::string, uint32_t> &NameToIndexMap,
std::optional<StringRef> &DwarfOutputPath) {
assert(CU.getDWOId() && "DWO ID not found.");
std::string DWOName = dwarf::toString(
CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),
"");
assert(!DWOName.empty() &&
"DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exist.");
if (DwarfOutputPath) {
DWOName = std::string(sys::path::filename(DWOName));
auto Iter = NameToIndexMap.find(DWOName);
if (Iter == NameToIndexMap.end())
Iter = NameToIndexMap.insert({DWOName, 0}).first;
DWOName.append(std::to_string(Iter->second));
++Iter->second;
}
DWOName.append(".dwo");
return DWOName;
}

/// Adds a \p Str to .debug_str section.
/// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using
/// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets
/// for this contribution of \p Unit.
static void addStringHelper(DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter, DIEBuilder &DIEBldr,
DIE &Die, const DWARFUnit &Unit,
DIEValue &DIEAttrInfo, StringRef Str) {
uint32_t NewOffset = StrWriter.addString(Str);
if (Unit.getVersion() >= 5) {
StrOffstsWriter.updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(),
NewOffset);
return;
}
DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(),
DIEInteger(NewOffset));
}

std::string DIEBuilder::updateDWONameCompDir(
DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter,
DWARFUnit &SkeletonCU, std::optional<StringRef> DwarfOutputPath,
std::optional<StringRef> DWONameToUse) {
DIE &UnitDIE = *getUnitDIEbyUnit(SkeletonCU);
DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name);
if (!DWONameAttrInfo)
DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name);
if (!DWONameAttrInfo)
return "";
std::string ObjectName;
if (DWONameToUse)
ObjectName = *DWONameToUse;
else
ObjectName = getDWOName(SkeletonCU, NameToIndexMap, DwarfOutputPath);
addStringHelper(StrOffstsWriter, StrWriter, *this, UnitDIE, SkeletonCU,
DWONameAttrInfo, ObjectName);

DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir);
assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU.");

if (DwarfOutputPath) {
if (!sys::fs::exists(*DwarfOutputPath))
sys::fs::create_directory(*DwarfOutputPath);
addStringHelper(StrOffstsWriter, StrWriter, *this, UnitDIE, SkeletonCU,
CompDirAttrInfo, *DwarfOutputPath);
}
return ObjectName;
}

void DIEBuilder::updateDWONameCompDirForTypes(
DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter,
DWARFUnit &Unit, std::optional<StringRef> DwarfOutputPath,
const StringRef DWOName) {
for (DWARFUnit *DU : getState().DWARF5TUVector)
updateDWONameCompDir(StrOffstsWriter, StrWriter, *DU, DwarfOutputPath,
DWOName);
if (StrOffstsWriter.isStrOffsetsSectionModified())
StrOffstsWriter.finalizeSection(Unit, *this);
}

void DIEBuilder::updateReferences() {
for (auto &[SrcDIEInfo, ReferenceInfo] : getState().AddrReferences) {
DIEInfo *DstDIEInfo = ReferenceInfo.Dst;
Expand Down
20 changes: 16 additions & 4 deletions bolt/lib/Core/DebugData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>

Expand Down Expand Up @@ -867,10 +868,17 @@ void DebugStrOffsetsWriter::finalizeSection(DWARFUnit &Unit,
DIEBuilder &DIEBldr) {
std::optional<AttrInfo> AttrVal =
findAttributeInfo(Unit.getUnitDIE(), dwarf::DW_AT_str_offsets_base);
if (!AttrVal)
if (!AttrVal && !Unit.isDWOUnit())
return;
std::optional<uint64_t> Val = AttrVal->V.getAsSectionOffset();
assert(Val && "DW_AT_str_offsets_base Value not present.");
std::optional<uint64_t> Val = std::nullopt;
if (AttrVal) {
Val = AttrVal->V.getAsSectionOffset();
} else {
if (!Unit.isDWOUnit())
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: "
"DW_AT_str_offsets_base Value not present\n";
Val = 0;
}
DIE &Die = *DIEBldr.getUnitDIEbyUnit(Unit);
DIEValue StrListBaseAttrInfo =
Die.findAttribute(dwarf::DW_AT_str_offsets_base);
Expand Down Expand Up @@ -915,7 +923,11 @@ void DebugStrWriter::create() {
}

void DebugStrWriter::initialize() {
auto StrSection = BC.DwCtx->getDWARFObj().getStrSection();
StringRef StrSection;
if (IsDWO)
StrSection = DwCtx.getDWARFObj().getStrDWOSection();
else
StrSection = DwCtx.getDWARFObj().getStrSection();
(*StrStream) << StrSection;
}

Expand Down
7 changes: 1 addition & 6 deletions bolt/lib/Core/ParallelUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,7 @@ void runOnEachFunctionWithUniqueAllocId(
}
}

if (!BC.MIB->checkAllocatorExists(AllocId)) {
MCPlusBuilder::AllocatorIdTy Id =
BC.MIB->initializeNewAnnotationAllocator();
(void)Id;
assert(AllocId == Id && "unexpected allocator id created");
}
EnsureAllocatorExists(AllocId);

Pool.async(runBlock, BlockBegin, BC.getBinaryFunctions().end(), AllocId);
Lock.unlock();
Expand Down
8 changes: 8 additions & 0 deletions bolt/lib/Passes/BinaryPasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,9 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) {
Pred->removeSuccessor(&BB);
Pred->eraseInstruction(Pred->findInstruction(Branch));
Pred->addTailCallInstruction(SuccSym);
MCInst *TailCall = Pred->getLastNonPseudoInstr();
assert(TailCall);
MIB->setOffset(*TailCall, BB.getOffset());
} else {
return false;
}
Expand Down Expand Up @@ -910,6 +913,11 @@ uint64_t SimplifyConditionalTailCalls::fixTailCalls(BinaryFunction &BF) {
auto &CTCAnnotation =
MIB->getOrCreateAnnotationAs<uint64_t>(*CondBranch, "CTCTakenCount");
CTCAnnotation = CTCTakenFreq;
// Preserve Offset annotation, used in BAT.
// Instr is a direct tail call instruction that was created when CTCs are
// first expanded, and has the original CTC offset set.
if (std::optional<uint32_t> Offset = MIB->getOffset(*Instr))
MIB->setOffset(*CondBranch, *Offset);

// Remove the unused successor which may be eliminated later
// if there are no other users.
Expand Down
5 changes: 0 additions & 5 deletions bolt/lib/Passes/FrameAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,11 +561,6 @@ FrameAnalysis::FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG)
NamedRegionTimer T1("clearspt", "clear spt", "FA", "FA breakdown",
opts::TimeFA);
clearSPTMap();

// Clean up memory allocated for annotation values
if (!opts::NoThreads)
for (MCPlusBuilder::AllocatorIdTy Id : SPTAllocatorsId)
BC.MIB->freeValuesAllocator(Id);
}
}

Expand Down
2 changes: 1 addition & 1 deletion bolt/lib/Passes/Instrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ void Instrumentation::instrumentFunction(BinaryFunction &Function,
else if (BC.MIB->isUnconditionalBranch(Inst))
HasUnconditionalBranch = true;
else if ((!BC.MIB->isCall(Inst) && !BC.MIB->isConditionalBranch(Inst)) ||
BC.MIB->isUnsupportedBranch(Inst))
!BC.MIB->isReversibleBranch(Inst))
continue;

const uint32_t FromOffset = *BC.MIB->getOffset(Inst);
Expand Down
110 changes: 59 additions & 51 deletions bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
Expand Down Expand Up @@ -773,9 +774,19 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,

bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
uint64_t Mispreds) {
bool IsReturn = false;
auto handleAddress = [&](uint64_t &Addr, bool IsFrom) -> BinaryFunction * {
if (BinaryFunction *Func = getBinaryFunctionContainingAddress(Addr)) {
Addr -= Func->getAddress();
if (IsFrom) {
auto checkReturn = [&](auto MaybeInst) {
IsReturn = MaybeInst && BC->MIB->isReturn(*MaybeInst);
};
if (Func->hasInstructions())
checkReturn(Func->getInstructionAtOffset(Addr));
else
checkReturn(Func->disassembleInstructionAtOffset(Addr));
}

if (BAT)
Addr = BAT->translate(Func->getAddress(), Addr, IsFrom);
Expand All @@ -792,6 +803,9 @@ bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
};

BinaryFunction *FromFunc = handleAddress(From, /*IsFrom=*/true);
// Ignore returns.
if (IsReturn)
return true;
BinaryFunction *ToFunc = handleAddress(To, /*IsFrom=*/false);
if (!FromFunc && !ToFunc)
return false;
Expand Down Expand Up @@ -1450,7 +1464,7 @@ uint64_t DataAggregator::parseLBRSample(const PerfBranchSample &Sample,
uint64_t To = getBinaryFunctionContainingAddress(LBR.To) ? LBR.To : 0;
if (!From && !To)
continue;
BranchInfo &Info = BranchLBRs[Trace(From, To)];
TakenBranchInfo &Info = BranchLBRs[Trace(From, To)];
++Info.TakenCount;
Info.MispredCount += LBR.Mispred;
}
Expand Down Expand Up @@ -1595,7 +1609,7 @@ void DataAggregator::processBranchEvents() {

for (const auto &AggrLBR : BranchLBRs) {
const Trace &Loc = AggrLBR.first;
const BranchInfo &Info = AggrLBR.second;
const TakenBranchInfo &Info = AggrLBR.second;
doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount);
}
}
Expand Down Expand Up @@ -1986,7 +2000,7 @@ std::error_code DataAggregator::parseMMapEvents() {
std::pair<StringRef, MMapInfo> FileMMapInfo = FileMMapInfoRes.get();
if (FileMMapInfo.second.PID == -1)
continue;
if (FileMMapInfo.first.equals("(deleted)"))
if (FileMMapInfo.first == "(deleted)")
continue;

// Consider only the first mapping of the file for any given PID
Expand Down Expand Up @@ -2239,13 +2253,13 @@ DataAggregator::writeAggregatedFile(StringRef OutputFilename) const {
} else {
for (const auto &KV : NamesToBranches) {
const FuncBranchData &FBD = KV.second;
for (const llvm::bolt::BranchInfo &BI : FBD.Data) {
for (const BranchInfo &BI : FBD.Data) {
writeLocation(BI.From);
writeLocation(BI.To);
OutFile << BI.Mispreds << " " << BI.Branches << "\n";
++BranchValues;
}
for (const llvm::bolt::BranchInfo &BI : FBD.EntryData) {
for (const BranchInfo &BI : FBD.EntryData) {
// Do not output if source is a known symbol, since this was already
// accounted for in the source function
if (BI.From.IsSymbol)
Expand Down Expand Up @@ -2326,7 +2340,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
continue;
BinaryFunction *BF = BC.getBinaryFunctionAtAddress(FuncAddress);
assert(BF);
YamlBF.Name = FuncName.str();
YamlBF.Name = getLocationName(*BF);
YamlBF.Id = BF->getFunctionNumber();
YamlBF.Hash = BAT->getBFHash(FuncAddress);
YamlBF.ExecCount = BF->getKnownExecutionCount();
Expand All @@ -2341,54 +2355,48 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
for (auto BI = BlockMap.begin(), BE = BlockMap.end(); BI != BE; ++BI)
YamlBF.Blocks[BI->second.getBBIndex()].Hash = BI->second.getBBHash();

auto getSuccessorInfo = [&](uint32_t SuccOffset, unsigned SuccDataIdx) {
const llvm::bolt::BranchInfo &BI = Branches.Data.at(SuccDataIdx);
yaml::bolt::SuccessorInfo SI;
SI.Index = BlockMap.getBBIndex(SuccOffset);
SI.Count = BI.Branches;
SI.Mispreds = BI.Mispreds;
return SI;
};

auto getCallSiteInfo = [&](Location CallToLoc, unsigned CallToIdx,
uint32_t Offset) {
const llvm::bolt::BranchInfo &BI = Branches.Data.at(CallToIdx);
yaml::bolt::CallSiteInfo CSI;
CSI.DestId = 0; // designated for unknown functions
CSI.EntryDiscriminator = 0;
CSI.Count = BI.Branches;
CSI.Mispreds = BI.Mispreds;
CSI.Offset = Offset;
if (BinaryData *BD = BC.getBinaryDataByName(CallToLoc.Name))
YAMLProfileWriter::setCSIDestination(BC, CSI, BD->getSymbol(), BAT,
CallToLoc.Offset);
return CSI;
// Lookup containing basic block offset and index
auto getBlock = [&BlockMap](uint32_t Offset) {
auto BlockIt = BlockMap.upper_bound(Offset);
if (LLVM_UNLIKELY(BlockIt == BlockMap.begin())) {
errs() << "BOLT-ERROR: invalid BAT section\n";
exit(1);
}
--BlockIt;
return std::pair(BlockIt->first, BlockIt->second.getBBIndex());
};

for (const auto &[FromOffset, SuccKV] : Branches.IntraIndex) {
if (!BlockMap.isInputBlock(FromOffset))
continue;
const unsigned Index = BlockMap.getBBIndex(FromOffset);
yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[Index];
for (const auto &[SuccOffset, SuccDataIdx] : SuccKV)
if (BlockMap.isInputBlock(SuccOffset))
YamlBB.Successors.emplace_back(
getSuccessorInfo(SuccOffset, SuccDataIdx));
for (const BranchInfo &BI : Branches.Data) {
using namespace yaml::bolt;
const auto &[BlockOffset, BlockIndex] = getBlock(BI.From.Offset);
BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex];
if (BI.To.IsSymbol && BI.To.Name == BI.From.Name && BI.To.Offset != 0) {
// Internal branch
const unsigned SuccIndex = getBlock(BI.To.Offset).second;
auto &SI = YamlBB.Successors.emplace_back(SuccessorInfo{SuccIndex});
SI.Count = BI.Branches;
SI.Mispreds = BI.Mispreds;
} else {
// Call
const uint32_t Offset = BI.From.Offset - BlockOffset;
auto &CSI = YamlBB.CallSites.emplace_back(CallSiteInfo{Offset});
CSI.Count = BI.Branches;
CSI.Mispreds = BI.Mispreds;
if (const BinaryData *BD = BC.getBinaryDataByName(BI.To.Name))
YAMLProfileWriter::setCSIDestination(BC, CSI, BD->getSymbol(), BAT,
BI.To.Offset);
}
}
for (const auto &[FromOffset, CallTo] : Branches.InterIndex) {
auto BlockIt = BlockMap.upper_bound(FromOffset);
--BlockIt;
const unsigned BlockOffset = BlockIt->first;
const unsigned BlockIndex = BlockIt->second.getBBIndex();
yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex];
const uint32_t Offset = FromOffset - BlockOffset;
for (const auto &[CallToLoc, CallToIdx] : CallTo)
YamlBB.CallSites.emplace_back(
getCallSiteInfo(CallToLoc, CallToIdx, Offset));
llvm::sort(YamlBB.CallSites, [](yaml::bolt::CallSiteInfo &A,
yaml::bolt::CallSiteInfo &B) {
return A.Offset < B.Offset;
});
// Set entry counts, similar to DataReader::readProfile.
for (const BranchInfo &BI : Branches.EntryData) {
if (!BlockMap.isInputBlock(BI.To.Offset)) {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: Unexpected EntryData in " << FuncName
<< " at 0x" << Twine::utohexstr(BI.To.Offset) << '\n';
continue;
}
const unsigned BlockIndex = BlockMap.getBBIndex(BI.To.Offset);
YamlBF.Blocks[BlockIndex].ExecCount += BI.Branches;
}
// Drop blocks without a hash, won't be useful for stale matching.
llvm::erase_if(YamlBF.Blocks,
Expand Down
4 changes: 2 additions & 2 deletions bolt/lib/Profile/DataReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@ bool DataReader::recordBranch(BinaryFunction &BF, uint64_t From, uint64_t To,
if (collectedInBoltedBinary() && FromBB == ToBB)
return true;

// Allow passthrough blocks.
BinaryBasicBlock *FTSuccessor = FromBB->getConditionalSuccessor(false);
if (FTSuccessor && FTSuccessor->succ_size() == 1 &&
FTSuccessor->getSuccessor(ToBB->getLabel())) {
Expand Down Expand Up @@ -1205,8 +1206,7 @@ std::error_code DataReader::parse() {

// Add entry data for branches to another function or branches
// to entry points (including recursive calls)
if (BI.To.IsSymbol &&
(!BI.From.Name.equals(BI.To.Name) || BI.To.Offset == 0)) {
if (BI.To.IsSymbol && (BI.From.Name != BI.To.Name || BI.To.Offset == 0)) {
I = GetOrCreateFuncEntry(BI.To.Name);
I->second.EntryData.emplace_back(std::move(BI));
}
Expand Down
29 changes: 20 additions & 9 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,17 +218,28 @@ bool YAMLProfileReader::parseFunctionProfile(
continue;
}

BinaryBasicBlock &SuccessorBB = *Order[YamlSI.Index];
if (!BB.getSuccessor(SuccessorBB.getLabel())) {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: no successor for block " << BB.getName()
<< " that matches index " << YamlSI.Index << " or block "
<< SuccessorBB.getName() << '\n';
++MismatchedEdges;
continue;
BinaryBasicBlock *ToBB = Order[YamlSI.Index];
if (!BB.getSuccessor(ToBB->getLabel())) {
// Allow passthrough blocks.
BinaryBasicBlock *FTSuccessor = BB.getConditionalSuccessor(false);
if (FTSuccessor && FTSuccessor->succ_size() == 1 &&
FTSuccessor->getSuccessor(ToBB->getLabel())) {
BinaryBasicBlock::BinaryBranchInfo &FTBI =
FTSuccessor->getBranchInfo(*ToBB);
FTBI.Count += YamlSI.Count;
FTBI.MispredictedCount += YamlSI.Mispreds;
ToBB = FTSuccessor;
} else {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: no successor for block " << BB.getName()
<< " that matches index " << YamlSI.Index << " or block "
<< ToBB->getName() << '\n';
++MismatchedEdges;
continue;
}
}

BinaryBasicBlock::BinaryBranchInfo &BI = BB.getBranchInfo(SuccessorBB);
BinaryBasicBlock::BinaryBranchInfo &BI = BB.getBranchInfo(*ToBB);
BI.Count += YamlSI.Count;
BI.MispredictedCount += YamlSI.Mispreds;
}
Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS
Core
DebugInfoDWARF
DWP
JITLink
Expand Down
169 changes: 64 additions & 105 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,32 +458,6 @@ static std::optional<uint64_t> getAsAddress(const DWARFUnit &DU,
return std::nullopt;
}

/// Returns DWO Name to be used. Handles case where user specifies output DWO
/// directory, and there are duplicate names. Assumes DWO ID is unique.
static std::string
getDWOName(llvm::DWARFUnit &CU,
std::unordered_map<std::string, uint32_t> &NameToIndexMap) {
std::optional<uint64_t> DWOId = CU.getDWOId();
assert(DWOId && "DWO ID not found.");
(void)DWOId;

std::string DWOName = dwarf::toString(
CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),
"");
assert(!DWOName.empty() &&
"DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exists.");
if (!opts::DwarfOutputPath.empty()) {
DWOName = std::string(sys::path::filename(DWOName));
auto Iter = NameToIndexMap.find(DWOName);
if (Iter == NameToIndexMap.end())
Iter = NameToIndexMap.insert({DWOName, 0}).first;
DWOName.append(std::to_string(Iter->second));
++Iter->second;
}
DWOName.append(".dwo");
return DWOName;
}

static std::unique_ptr<DIEStreamer>
createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,
StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr,
Expand Down Expand Up @@ -515,7 +489,9 @@ static void emitDWOBuilder(const std::string &DWOName,
DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter,
DWARFUnit &SplitCU, DWARFUnit &CU,
DWARFRewriter::DWPState &State,
DebugLocWriter &LocWriter) {
DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter) {
// Populate debug_info and debug_abbrev for current dwo into StringRef.
DWODIEBuilder.generateAbbrevs();
DWODIEBuilder.finish();
Expand Down Expand Up @@ -577,54 +553,10 @@ static void emitDWOBuilder(const std::string &DWOName,
}
if (opts::WriteDWP)
Rewriter.updateDWP(CU, OverriddenSections, CUMI, TUMetaVector, State,
LocWriter);
LocWriter, StrOffstsWriter, StrWriter);
else
Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter);
}

/// Adds a \p Str to .debug_str section.
/// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using
/// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets
/// for this contribution of \p Unit.
static void addStringHelper(DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter, DIEBuilder &DIEBldr,
DIE &Die, const DWARFUnit &Unit,
DIEValue &DIEAttrInfo, StringRef Str) {
uint32_t NewOffset = StrWriter.addString(Str);
if (Unit.getVersion() >= 5) {
StrOffstsWriter.updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(),
NewOffset);
return;
}
DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(),
DIEInteger(NewOffset));
}

static std::string
updateDWONameCompDir(DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter,
std::unordered_map<std::string, uint32_t> &NameToIndexMap,
DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &UnitDIE) {
DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name);
if (!DWONameAttrInfo)
DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name);
assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU.");
std::string ObjectName;

ObjectName = getDWOName(Unit, NameToIndexMap);
addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit,
DWONameAttrInfo, ObjectName.c_str());

DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir);
assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU.");

if (!opts::DwarfOutputPath.empty()) {
if (!sys::fs::exists(opts::DwarfOutputPath))
sys::fs::create_directory(opts::DwarfOutputPath);
addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit,
CompDirAttrInfo, opts::DwarfOutputPath.c_str());
}
return ObjectName;
Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter,
StrOffstsWriter, StrWriter);
}

using DWARFUnitVec = std::vector<DWARFUnit *>;
Expand Down Expand Up @@ -673,9 +605,8 @@ void DWARFRewriter::updateDebugInfo() {
return;

ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>();
StrWriter = std::make_unique<DebugStrWriter>(BC);

StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>();
StrWriter = std::make_unique<DebugStrWriter>(*BC.DwCtx, false);
StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>(BC);

if (!opts::DeterministicDebugInfo) {
opts::DeterministicDebugInfo = true;
Expand Down Expand Up @@ -720,10 +651,6 @@ void DWARFRewriter::updateDebugInfo() {
return LocListWritersByCU[CUIndex++].get();
};

// Unordered maps to handle name collision if output DWO directory is
// specified.
std::unordered_map<std::string, uint32_t> NameToIndexMap;

DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
*StrWriter);
DWPState State;
Expand All @@ -747,13 +674,20 @@ void DWARFRewriter::updateDebugInfo() {
Unit);
DWODIEBuilder.buildDWOUnit(**SplitCU);
std::string DWOName = "";
std::optional<std::string> DwarfOutputPath =
opts::DwarfOutputPath.empty()
? std::nullopt
: std::optional<std::string>(opts::DwarfOutputPath.c_str());
{
std::lock_guard<std::mutex> Lock(AccessMutex);
DWOName = updateDWONameCompDir(*StrOffstsWriter, *StrWriter,
NameToIndexMap, *Unit, *DIEBlder,
*DIEBlder->getUnitDIEbyUnit(*Unit));
DWOName = DIEBlder->updateDWONameCompDir(
*StrOffstsWriter, *StrWriter, *Unit, DwarfOutputPath, std::nullopt);
}

DebugStrOffsetsWriter DWOStrOffstsWriter(BC);
DebugStrWriter DWOStrWriter((*SplitCU)->getContext(), true);
DWODIEBuilder.updateDWONameCompDirForTypes(DWOStrOffstsWriter,
DWOStrWriter, **SplitCU,
DwarfOutputPath, DWOName);
DebugLoclistWriter DebugLocDWoWriter(*Unit, Unit->getVersion(), true);
DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter;
if (Unit->getVersion() >= 5) {
Expand All @@ -771,7 +705,7 @@ void DWARFRewriter::updateDebugInfo() {
TempRangesSectionWriter->finalizeSection();

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

if (Unit->getVersion() >= 5) {
Expand Down Expand Up @@ -1550,7 +1484,7 @@ CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder,
for (const SectionRef &Section : Obj->sections()) {
StringRef Contents = cantFail(Section.getContents());
StringRef Name = cantFail(Section.getName());
if (Name.equals(".debug_types"))
if (Name == ".debug_types")
BC.registerOrUpdateNoteSection(".debug_types", copyByteArray(Contents),
Contents.size());
}
Expand Down Expand Up @@ -1633,10 +1567,10 @@ void DWARFRewriter::finalizeDebugSections(
for (const SectionRef &Secs : Obj->sections()) {
StringRef Contents = cantFail(Secs.getContents());
StringRef Name = cantFail(Secs.getName());
if (Name.equals(".debug_abbrev")) {
if (Name == ".debug_abbrev") {
BC.registerOrUpdateNoteSection(".debug_abbrev", copyByteArray(Contents),
Contents.size());
} else if (Name.equals(".debug_info")) {
} else if (Name == ".debug_info") {
BC.registerOrUpdateNoteSection(".debug_info", copyByteArray(Contents),
Contents.size());
}
Expand Down Expand Up @@ -1736,6 +1670,7 @@ std::optional<StringRef> updateDebugData(
const DWARFUnitIndex::Entry *CUDWOEntry, uint64_t DWOId,
std::unique_ptr<DebugBufferVector> &OutputBuffer,
DebugRangeListsSectionWriter *RangeListsWriter, DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter,
const llvm::bolt::DWARFRewriter::OverriddenSectionsMap &OverridenSections) {

using DWOSectionContribution =
Expand Down Expand Up @@ -1771,9 +1706,14 @@ std::optional<StringRef> updateDebugData(
};
switch (SectionIter->second.second) {
default: {
if (!SectionName.equals("debug_str.dwo"))
if (SectionName != "debug_str.dwo")
errs() << "BOLT-WARNING: unsupported debug section: " << SectionName
<< "\n";
if (StrWriter.isInitialized()) {
OutputBuffer = StrWriter.releaseBuffer();
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
OutputBuffer->size());
}
return SectionContents;
}
case DWARFSectionKind::DW_SECT_INFO: {
Expand All @@ -1783,6 +1723,11 @@ std::optional<StringRef> updateDebugData(
return getOverridenSection(DWARFSectionKind::DW_SECT_EXT_TYPES);
}
case DWARFSectionKind::DW_SECT_STR_OFFSETS: {
if (StrOffstsWriter.isFinalized()) {
OutputBuffer = StrOffstsWriter.releaseBuffer();
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
OutputBuffer->size());
}
return getSliceData(CUDWOEntry, SectionContents,
DWARFSectionKind::DW_SECT_STR_OFFSETS, DWPOffset);
}
Expand Down Expand Up @@ -1884,7 +1829,9 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU,
const OverriddenSectionsMap &OverridenSections,
const DWARFRewriter::UnitMeta &CUMI,
DWARFRewriter::UnitMetaVectorType &TUMetaVector,
DWPState &State, DebugLocWriter &LocWriter) {
DWPState &State, DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter) {
const uint64_t DWOId = *CU.getDWOId();
MCSection *const StrOffsetSection = State.MCOFI->getDwarfStrOffDWOSection();
assert(StrOffsetSection && "StrOffsetSection does not exist.");
Expand Down Expand Up @@ -1941,15 +1888,18 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU,
TUEntry.Contributions[Index].getLength32();
State.TypeIndexEntries.insert(std::make_pair(Hash, TUEntry));
};
std::unique_ptr<DebugBufferVector> StrOffsetsOutputData;
std::unique_ptr<DebugBufferVector> StrOutputData;
for (const SectionRef &Section : DWOFile->sections()) {
std::unique_ptr<DebugBufferVector> OutputData;
std::unique_ptr<DebugBufferVector> OutputData = nullptr;
StringRef SectionName = getSectionName(Section);
Expected<StringRef> ContentsExp = Section.getContents();
assert(ContentsExp && "Invalid contents.");
std::optional<StringRef> TOutData = updateDebugData(
(*DWOCU)->getContext(), SectionName, *ContentsExp, State.KnownSections,
*State.Streamer, *this, CUDWOEntry, DWOId, OutputData,
RangeListssWriter, LocWriter, OverridenSections);
std::optional<StringRef> TOutData =
updateDebugData((*DWOCU)->getContext(), SectionName, *ContentsExp,
State.KnownSections, *State.Streamer, *this, CUDWOEntry,
DWOId, OutputData, RangeListssWriter, LocWriter,
StrOffstsWriter, StrWriter, OverridenSections);
if (!TOutData)
continue;

Expand All @@ -1959,16 +1909,19 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU,
continue;
}

if (SectionName.equals("debug_str.dwo")) {
if (SectionName == "debug_str.dwo") {
CurStrSection = OutData;
StrOutputData = std::move(OutputData);
} else {
// Since handleDebugDataPatching returned true, we already know this is
// a known section.
auto SectionIter = State.KnownSections.find(SectionName);
if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS)
if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS) {
CurStrOffsetSection = OutData;
else
StrOffsetsOutputData = std::move(OutputData);
} else {
State.Streamer->emitBytes(OutData);
}
unsigned int Index =
getContributionIndex(SectionIter->second.second, State.IndexVersion);
uint64_t Offset = State.ContributionOffsets[Index];
Expand All @@ -1992,6 +1945,10 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU,
// based on hash.
if (!StrSectionWrittenOut && !CurStrOffsetSection.empty() &&
!CurStrSection.empty()) {
// If debug_str.dwo section was modified storing it until dwp is written
// out. DWPStringPool stores raw pointers to strings.
if (StrOutputData)
State.StrSections.push_back(std::move(StrOutputData));
writeStringsAndOffsets(*State.Streamer.get(), *State.Strings.get(),
StrOffsetSection, CurStrSection,
CurStrOffsetSection, CU.getVersion());
Expand All @@ -2017,7 +1974,8 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU,

void DWARFRewriter::writeDWOFiles(
DWARFUnit &CU, const OverriddenSectionsMap &OverridenSections,
const std::string &DWOName, DebugLocWriter &LocWriter) {
const std::string &DWOName, DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter) {
// Setup DWP code once.
DWARFContext *DWOCtx = BC.getDWOContext();
const uint64_t DWOId = *CU.getDWOId();
Expand Down Expand Up @@ -2072,10 +2030,11 @@ void DWARFRewriter::writeDWOFiles(
// have .debug_rnglists so won't be part of the loop below.
if (!RangeListssWriter->empty()) {
std::unique_ptr<DebugBufferVector> OutputData;
if (std::optional<StringRef> OutData = updateDebugData(
(*DWOCU)->getContext(), "debug_rnglists.dwo", "", KnownSections,
*Streamer, *this, CUDWOEntry, DWOId, OutputData,
RangeListssWriter, LocWriter, OverridenSections))
if (std::optional<StringRef> OutData =
updateDebugData((*DWOCU)->getContext(), "debug_rnglists.dwo", "",
KnownSections, *Streamer, *this, CUDWOEntry,
DWOId, OutputData, RangeListssWriter, LocWriter,
StrOffstsWriter, StrWriter, OverridenSections))
Streamer->emitBytes(*OutData);
}
}
Expand All @@ -2090,7 +2049,7 @@ void DWARFRewriter::writeDWOFiles(
if (std::optional<StringRef> OutData = updateDebugData(
(*DWOCU)->getContext(), SectionName, *ContentsExp, KnownSections,
*Streamer, *this, CUDWOEntry, DWOId, OutputData, RangeListssWriter,
LocWriter, OverridenSections))
LocWriter, StrOffstsWriter, StrWriter, OverridenSections))
Streamer->emitBytes(*OutData);
}
Streamer->finish();
Expand Down
2 changes: 1 addition & 1 deletion bolt/lib/Rewrite/SDTRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void SDTRewriter::readSection() {

StringRef Name = DE.getCStr(&Offset);

if (!Name.equals("stapsdt"))
if (Name != "stapsdt")
errs() << "BOLT-WARNING: SDT note name \"" << Name
<< "\" is not expected\n";

Expand Down
10 changes: 5 additions & 5 deletions bolt/lib/Target/X86/X86MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,19 +328,19 @@ class X86MCPlusBuilder : public MCPlusBuilder {
return false;
}

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

switch (Inst.getOpcode()) {
default:
return false;
return true;
case X86::LOOP:
case X86::LOOPE:
case X86::LOOPNE:
case X86::JECXZ:
case X86::JRCXZ:
return true;
return false;
}
}

Expand Down Expand Up @@ -1874,7 +1874,7 @@ class X86MCPlusBuilder : public MCPlusBuilder {
}

// Handle conditional branches and ignore indirect branches
if (!isUnsupportedBranch(*I) && getCondCode(*I) == X86::COND_INVALID) {
if (isReversibleBranch(*I) && getCondCode(*I) == X86::COND_INVALID) {
// Indirect branch
return false;
}
Expand Down
1 change: 1 addition & 0 deletions bolt/test/X86/Inputs/blarge_new_bat_branchentry.preagg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
B 80010c 800194 1 0
2 changes: 2 additions & 0 deletions bolt/test/X86/Inputs/blarge_new_bat_order.preagg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
B 800154 401050 20 0
F 800159 800193 7
22 changes: 11 additions & 11 deletions bolt/test/X86/Inputs/dwarf5-df-types-debug-names-main.s
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ main: # @main
.Linfo_string5:
.asciz "f2" # string offset=24
.Linfo_string6:
.asciz "/home/ayermolo/local/tasks/T138552329/typeDedupSplit" # string offset=27
.asciz "." # string offset=27
.Linfo_string7:
.asciz "main.dwo" # string offset=80
.Linfo_string8:
Expand All @@ -234,15 +234,15 @@ main: # @main
.long 19
.long 24
.long 27
.long 80
.long 89
.long 92
.long 97
.long 100
.long 103
.long 106
.long 112
.long 220
.long 29
.long 38
.long 41
.long 46
.long 49
.long 52
.long 55
.long 61
.long 169
.section .debug_info.dwo,"e",@progbits
.long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit
.Ldebug_info_dwo_start2:
Expand Down Expand Up @@ -474,7 +474,7 @@ main: # @main
.byte 1
.byte 8
.byte 2
.ascii "/home/ayermolo/local/tasks/T138552329/typeDedupSplit"
.ascii "."
.byte 0
.byte 46
.byte 0
Expand Down
35 changes: 35 additions & 0 deletions bolt/test/X86/Inputs/jump-table-fixed-ref-pic.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.globl main
.type main, %function
main:
.cfi_startproc
cmpq $0x3, %rdi
jae .L4
cmpq $0x1, %rdi
jne .L4
mov .Ljt_pic+8(%rip), %rax
lea .Ljt_pic(%rip), %rdx
add %rdx, %rax
jmpq *%rax
.L1:
movq $0x1, %rax
jmp .L5
.L2:
movq $0x0, %rax
jmp .L5
.L3:
movq $0x2, %rax
jmp .L5
.L4:
mov $0x3, %rax
.L5:
retq
.cfi_endproc

.section .rodata
.align 16
.Ljt_pic:
.long .L1 - .Ljt_pic
.long .L2 - .Ljt_pic
.long .L3 - .Ljt_pic
.long .L4 - .Ljt_pic

6 changes: 3 additions & 3 deletions bolt/test/X86/bb-with-two-tail-calls.s
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
# RUN: llvm-strip --strip-unneeded %t.o
# 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 2>&1 | FileCheck %s
# RUN: --print-sctc --print-only=_start -enable-bat 2>&1 | FileCheck %s
# 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 # CTCTakenCount: {{.*}}
# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL
# CHECK: {{.*}}: ja {{.*}} # TAILCALL # Offset: 7 # CTCTakenCount: 4
# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL # Offset: 12

.globl _start
_start:
Expand Down
31 changes: 29 additions & 2 deletions bolt/test/X86/bolt-address-translation-yaml.test
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@ RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt \
RUN: --reorder-blocks=ext-tsp --split-functions --split-strategy=cdsplit \
RUN: --reorder-functions=cdsort --enable-bat --dyno-stats --skip-funcs=main \
RUN: 2>&1 | FileCheck --check-prefix WRITE-BAT-CHECK %s
# Check that branch with entry in BAT is accounted for.
RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat_branchentry.preagg.txt \
RUN: -w %t.yaml -o %t.fdata
RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o %t.null
RUN: FileCheck --input-file %t.yaml --check-prefix BRANCHENTRY-YAML-CHECK %s
RUN: FileCheck --input-file %t.yaml-fdata --check-prefix BRANCHENTRY-YAML-CHECK %s
BRANCHENTRY-YAML-CHECK: - name: SolveCubic
BRANCHENTRY-YAML-CHECK: bid: 0
BRANCHENTRY-YAML-CHECK: hash: 0x700F19D24600000
BRANCHENTRY-YAML-CHECK-NEXT: succ: [ { bid: 7, cnt: 1 }
# Check that the order is correct between BAT YAML and FDATA->YAML.
RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat_order.preagg.txt \
RUN: -w %t.yaml -o %t.fdata
RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o %t.null
RUN: FileCheck --input-file %t.yaml --check-prefix ORDER-YAML-CHECK %s
RUN: FileCheck --input-file %t.yaml-fdata --check-prefix ORDER-YAML-CHECK %s
ORDER-YAML-CHECK: - name: SolveCubic
ORDER-YAML-CHECK: bid: 3
ORDER-YAML-CHECK: hash: 0xDDA1DC5F69F900AC
ORDER-YAML-CHECK-NEXT: calls: [ { off: 0x26, fid: [[#]], cnt: 20 } ]
ORDER-YAML-CHECK-NEXT: succ: [ { bid: 5, cnt: 7 }
# Large profile test
RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat.preagg.txt -w %t.yaml -o %t.fdata \
RUN: 2>&1 | FileCheck --check-prefix READ-BAT-CHECK %s
RUN: FileCheck --input-file %t.yaml --check-prefix YAML-BAT-CHECK %s
Expand All @@ -13,7 +35,7 @@ RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o /dev/null
RUN: FileCheck --input-file %t.yaml-fdata --check-prefix YAML-BAT-CHECK %s

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

WRITE-BAT-CHECK: BOLT-INFO: Wrote 5 BAT maps
Expand Down Expand Up @@ -48,6 +70,10 @@ YAML-BAT-CHECK-NEXT: hash: 0x6AF7E61EA3966722
YAML-BAT-CHECK-NEXT: exec: 25
YAML-BAT-CHECK-NEXT: nblocks: 15
YAML-BAT-CHECK-NEXT: blocks:
YAML-BAT-CHECK-NEXT: - bid: 0
YAML-BAT-CHECK-NEXT: insns: [[#]]
YAML-BAT-CHECK-NEXT: hash: 0x700F19D24600000
YAML-BAT-CHECK-NEXT: exec: 25
YAML-BAT-CHECK: - bid: 3
YAML-BAT-CHECK-NEXT: insns: [[#]]
YAML-BAT-CHECK-NEXT: hash: 0xDDA1DC5F69F900AC
Expand All @@ -63,7 +89,8 @@ YAML-BAT-CHECK-NEXT: blocks:
YAML-BAT-CHECK: - bid: 1
YAML-BAT-CHECK-NEXT: insns: [[#]]
YAML-BAT-CHECK-NEXT: hash: 0xD70DC695320E0010
YAML-BAT-CHECK-NEXT: succ: {{.*}} { bid: 2, cnt: [[#]] }
YAML-BAT-CHECK-NEXT: succ: {{.*}} { bid: 2, cnt: [[#]]

CHECK-BOLT-YAML: pre-processing profile using YAML profile reader
CHECK-BOLT-YAML-NEXT: 5 out of 16 functions in the binary (31.2%) have non-empty execution profile
CHECK-BOLT-YAML-NOT: invalid (possibly stale) profile
198 changes: 198 additions & 0 deletions bolt/test/X86/dwarf5-df-types-modify-dwo-name-mixed.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
; RUN: rm -rf %t
; RUN: mkdir %t
; RUN: cd %t
; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-debug-names-main.s \
; RUN: -split-dwarf-file=main.dwo -o main.o
; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-dup-helper.s \
; RUN: -split-dwarf-file=helper.dwo -o helper.o
; RUN: %clang %cflags -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections
; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt > log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets main.dwo.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo.dwo >> log.txt
; RUN: cat log.txt | FileCheck -check-prefix=BOLT %s

;; Test is a mix of DWARF5 TUs where one has DW_AT_comp_dir/DW_AT_dwo_name, and another one doesn't.
;; Tests that BOLT correctly updates DW_AT_dwo_name for TUs.

; BOLT: DW_TAG_skeleton_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT: DW_TAG_skeleton_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("helper.dwo.dwo")
; BOLT: DW_TAG_type_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT: DW_TAG_type_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT: DW_TAG_type_unit
; BOLT-NOT: DW_AT_dwo_name
; BOLT: DW_TAG_type_unit
; BOLT-NOT: DW_AT_dwo_name
; BOLT: DW_TAG_compile_unit
; BOLT: .debug_str_offsets.dwo contents:
; BOLT-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-NEXT: "main"
; BOLT-NEXT: "int"
; BOLT-NEXT: "argc"
; BOLT-NEXT: "argv"
; BOLT-NEXT: "char"
; BOLT-NEXT: "f2"
; BOLT-NEXT: "."
; BOLT-NEXT: "main.dwo.dwo"
; BOLT-NEXT: "c1"
; BOLT-NEXT: "Foo2"
; BOLT-NEXT: "f3"
; BOLT-NEXT: "c2"
; BOLT-NEXT: "c3"
; BOLT-NEXT: "Foo2a"
; BOLT-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)"
; BOLT-NEXT: "main.cpp"
; BOLT-NEXT: helper.dwo.dwo: file format elf64-x86-64

; BOLT: .debug_str_offsets.dwo contents:
; BOLT-NEXT: 0x00000000: Contribution size = 64, Format = DWARF32, Version = 5
; BOLT-NEXT: "fooint"
; BOLT-NEXT: "int"
; BOLT-NEXT: "_Z3foov"
; BOLT-NEXT: "foo"
; BOLT-NEXT: "fint"
; BOLT-NEXT: "c1"
; BOLT-NEXT: "c2"
; BOLT-NEXT: "Foo2Int"
; BOLT-NEXT: "f"
; BOLT-NEXT: "char"
; BOLT-NEXT: "c3"
; BOLT-NEXT: "Foo2a"
; BOLT-NEXT: "clang version 18.0.0"
; BOLT-NEXT: "helper.cpp"
; BOLT-NEXT: "helper.dwo"


;; Tests that BOLT correctly handles updating DW_AT_dwo_name when it outputs a DWP file.
;; Currently skipping one of Type units because it is not being de-dupped.
;; In the tu-index this TU is not present.
; RUN: rm main.exe.bolt
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --write-dwp
; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt.dwp > logDWP.txt
; RUN: llvm-dwarfdump --debug-str-offsets main.exe.bolt.dwp >> logDWP.txt
; RUN: cat logDWP.txt | FileCheck -check-prefix=BOLT-DWP %s
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DWP: DW_AT_comp_dir (".")
; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DWP: DW_AT_comp_dir (".")
; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT-DWP: DW_TAG_compile_unit
; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DW-NOT: DW_AT_dwo_name
; BOLT-DWP: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-DWP-NEXT: "main"
; BOLT-DWP-NEXT: "int"
; BOLT-DWP-NEXT: "argc"
; BOLT-DWP-NEXT: "argv"
; BOLT-DWP-NEXT: "char"
; BOLT-DWP-NEXT: "f2"
; BOLT-DWP-NEXT: "."
; BOLT-DWP-NEXT: "main.dwo.dwo"
; BOLT-DWP-NEXT: "c1"
; BOLT-DWP-NEXT: "Foo2"
; BOLT-DWP-NEXT: "f3"
; BOLT-DWP-NEXT: "c2"
; BOLT-DWP-NEXT: "c3"
; BOLT-DWP-NEXT: "Foo2a"
; BOLT-DWP-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)"
; BOLT-DWP-NEXT: "main.cpp"
; BOLT-DWP-NEXT: Contribution size = 64, Format = DWARF32, Version = 5
; BOLT-DWP-NEXT: "fooint"
; BOLT-DWP-NEXT: "int"
; BOLT-DWP-NEXT: "_Z3foov"
; BOLT-DWP-NEXT: "foo"
; BOLT-DWP-NEXT: "fint"
; BOLT-DWP-NEXT: "c1"
; BOLT-DWP-NEXT: "c2"
; BOLT-DWP-NEXT: "Foo2Int"
; BOLT-DWP-NEXT: "f"
; BOLT-DWP-NEXT: "char"
; BOLT-DWP-NEXT: "c3"
; BOLT-DWP-NEXT: "Foo2a"
; BOLT-DWP-NEXT: "clang version 18.0.0"
; BOLT-DWP-NEXT: "helper.cpp"
; BOLT-DWP-NEXT: "helper.dwo

;; Tests that BOLT correctly handles updating DW_AT_comp_dir/DW_AT_dwo_name when outptut directory is specified.

; RUN: mkdir DWOOut
; RUN: rm main.exe.bolt
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --dwarf-output-path=%t/DWOOut
; RUN: cd DWOOut
; RUN: llvm-dwarfdump --debug-info -r 0 ../main.exe.bolt > log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo0.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo0.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets main.dwo0.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo0.dwo >> log.txt
; RUN: cat log.txt | FileCheck -check-prefix=BOLT-PATH %s

; BOLT-PATH: DW_TAG_skeleton_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo")
; BOLT-PATH: DW_TAG_skeleton_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo")
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo")
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo")
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH-NOT: DW_AT_comp_dir
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH-NOT: DW_AT_comp_dir
; BOLT-PATH: DW_TAG_compile_unit
; BOLT-PATH: .debug_str_offsets.dwo contents:
; BOLT-PATH-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-PATH-NEXT: "main"
; BOLT-PATH-NEXT: "int"
; BOLT-PATH-NEXT: "argc"
; BOLT-PATH-NEXT: "argv"
; BOLT-PATH-NEXT: "char"
; BOLT-PATH-NEXT: "f2"
; BOLT-PATH-NEXT: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut"
; BOLT-PATH-NEXT: "main.dwo0.dwo"
; BOLT-PATH-NEXT: "c1"
; BOLT-PATH-NEXT: "Foo2"
; BOLT-PATH-NEXT: "f3"
; BOLT-PATH-NEXT: "c2"
; BOLT-PATH-NEXT: "c3"
; BOLT-PATH-NEXT: "Foo2a"
; BOLT-PATH-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)"
; BOLT-PATH-NEXT: "main.cpp"
; BOLT-PATH-NEXT: helper.dwo0.dwo: file format elf64-x86-64

; BOLT-PATH: .debug_str_offsets.dwo contents:
; BOLT-PATH-NEXT: Contribution size = 64, Format = DWARF32, Version = 5
; BOLT-PATH-NEXT: "fooint"
; BOLT-PATH-NEXT: "int"
; BOLT-PATH-NEXT: "_Z3foov"
; BOLT-PATH-NEXT: "foo"
; BOLT-PATH-NEXT: "fint"
; BOLT-PATH-NEXT: "c1"
; BOLT-PATH-NEXT: "c2"
; BOLT-PATH-NEXT: "Foo2Int"
; BOLT-PATH-NEXT: "f"
; BOLT-PATH-NEXT: "char"
; BOLT-PATH-NEXT: "c3"
; BOLT-PATH-NEXT: "Foo2a"
; BOLT-PATH-NEXT: "clang version 18.0.0"
; BOLT-PATH-NEXT: "helper.cpp"
; BOLT-PATH-NEXT: "helper.dwo"
175 changes: 175 additions & 0 deletions bolt/test/X86/dwarf5-df-types-modify-dwo-name.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
; RUN: rm -rf %t
; RUN: mkdir %t
; RUN: cd %t
; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-debug-names-main.s \
; RUN: -split-dwarf-file=main.dwo -o main.o
; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-debug-names-helper.s \
; RUN: -split-dwarf-file=helper.dwo -o helper.o
; RUN: %clang %cflags -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections
; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt > log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets main.dwo.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo.dwo >> log.txt
; RUN: cat log.txt | FileCheck -check-prefix=BOLT %s

;; Tests that BOLT correctly updates DW_AT_dwo_name for TU Untis.

; BOLT: DW_TAG_skeleton_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT: DW_TAG_skeleton_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("helper.dwo.dwo")
; BOLT: DW_TAG_type_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT: DW_TAG_type_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT: DW_TAG_type_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("helper.dwo.dwo")
; BOLT: DW_TAG_type_unit
; BOLT: DW_AT_comp_dir (".")
; BOLT: DW_AT_dwo_name ("helper.dwo.dwo")
; BOLT: .debug_str_offsets.dwo contents:
; BOLT-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-NEXT: "main"
; BOLT-NEXT: "int"
; BOLT-NEXT: "argc"
; BOLT-NEXT: "argv"
; BOLT-NEXT: "char"
; BOLT-NEXT: "f2"
; BOLT-NEXT: "."
; BOLT-NEXT: "main.dwo.dwo"
; BOLT-NEXT: "c1"
; BOLT-NEXT: "Foo2"
; BOLT-NEXT: "f3"
; BOLT-NEXT: "c2"
; BOLT-NEXT: "c3"
; BOLT-NEXT: "Foo2a"
; BOLT-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)"
; BOLT-NEXT: "main.cpp"
; BOLT-NEXT: helper.dwo.dwo: file format elf64-x86-64

; BOLT: .debug_str_offsets.dwo contents:
; BOLT-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-NEXT: "fooint"
; BOLT-NEXT: "int"
; BOLT-NEXT: "_Z3foov"
; BOLT-NEXT: "foo"
; BOLT-NEXT: "fint"
; BOLT-NEXT: "."
; BOLT-NEXT: "helper.dwo.dwo"
; BOLT-NEXT: "c1"
; BOLT-NEXT: "c2"
; BOLT-NEXT: "Foo2Int"
; BOLT-NEXT: "f"
; BOLT-NEXT: "char"
; BOLT-NEXT: "c3"
; BOLT-NEXT: "Foo2a"
; BOLT-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)"
; BOLT-NEXT: "helper.cpp"


;; Tests that BOLT correctly handles updating DW_AT_dwo_name when it outputs a DWP file.
;; Currently skipping one of Type units because it is not being de-dupped.
;; In the tu-index this TU is not present.
; RUN: rm main.exe.bolt
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --write-dwp
; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt.dwp > logDWP.txt
; RUN: llvm-dwarfdump --debug-str-offsets main.exe.bolt.dwp >> logDWP.txt
; RUN: cat logDWP.txt | FileCheck -check-prefix=BOLT-DWP %s
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DWP: DW_AT_comp_dir (".")
; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DWP: DW_AT_comp_dir (".")
; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT-DWP: DW_TAG_compile_unit
; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo")
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DWP: DW_AT_comp_dir (".")
; BOLT-DWP: DW_AT_dwo_name ("helper.dwo.dwo")
; BOLT-DWP: DW_TAG_type_unit
; BOLT-DWP: DW_TAG_compile_unit
; BOLT-DWP: DW_AT_name ("helper.cpp")
; BOLT-DWP: DW_AT_dwo_name ("helper.dwo.dwo")

;; Tests that BOLT correctly handles updating DW_AT_comp_dir/DW_AT_dwo_name when outptut directory is specified.

; RUN: mkdir DWOOut
; RUN: rm main.exe.bolt
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --dwarf-output-path=%t/DWOOut
; RUN: cd DWOOut
; RUN: llvm-dwarfdump --debug-info -r 0 ../main.exe.bolt > log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo0.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo0.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets main.dwo0.dwo >> log.txt
; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo0.dwo >> log.txt
; RUN: cat log.txt | FileCheck -check-prefix=BOLT-PATH %s

; BOLT-PATH: DW_TAG_skeleton_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo")
; BOLT-PATH: DW_TAG_skeleton_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo")
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo")
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo")
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo")
; BOLT-PATH: DW_TAG_type_unit
; BOLT-PATH: DW_AT_comp_dir ("
; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut
; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo")
; BOLT-PATH: .debug_str_offsets.dwo contents:
; BOLT-PATH-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-PATH-NEXT: "main"
; BOLT-PATH-NEXT: "int"
; BOLT-PATH-NEXT: "argc"
; BOLT-PATH-NEXT: "argv"
; BOLT-PATH-NEXT: "char"
; BOLT-PATH-NEXT: "f2"
; BOLT-PATH-NEXT: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut"
; BOLT-PATH-NEXT: "main.dwo0.dwo"
; BOLT-PATH-NEXT: "c1"
; BOLT-PATH-NEXT: "Foo2"
; BOLT-PATH-NEXT: "f3"
; BOLT-PATH-NEXT: "c2"
; BOLT-PATH-NEXT: "c3"
; BOLT-PATH-NEXT: "Foo2a"
; BOLT-PATH-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)"
; BOLT-PATH-NEXT: "main.cpp"
; BOLT-PATH-NEXT: helper.dwo0.dwo: file format elf64-x86-64

; BOLT-PATH: .debug_str_offsets.dwo contents:
; BOLT-PATH-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
; BOLT-PATH-NEXT: "fooint"
; BOLT-PATH-NEXT: "int"
; BOLT-PATH-NEXT: "_Z3foov"
; BOLT-PATH-NEXT: "foo"
; BOLT-PATH-NEXT: "fint"
; BOLT-PATH-NEXT: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut"
; BOLT-PATH-NEXT: "helper.dwo0.dwo"
; BOLT-PATH-NEXT: "c1"
; BOLT-PATH-NEXT: "c2"
; BOLT-PATH-NEXT: "Foo2Int"
; BOLT-PATH-NEXT: "f"
; BOLT-PATH-NEXT: "char"
; BOLT-PATH-NEXT: "c3"
; BOLT-PATH-NEXT: "Foo2a"
; BOLT-PATH-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)"
; BOLT-PATH-NEXT: "helper.cpp"
9 changes: 9 additions & 0 deletions bolt/test/X86/jump-table-fixed-ref-pic.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Verify that BOLT detects fixed destination of indirect jump for PIC
# case.

XFAIL: *

RUN: %clang %cflags -no-pie %S/Inputs/jump-table-fixed-ref-pic.s -Wl,-q -o %t
RUN: llvm-bolt %t --relocs -o %t.null 2>&1 | FileCheck %s

CHECK: BOLT-INFO: fixed indirect branch detected in main
67 changes: 67 additions & 0 deletions bolt/test/X86/profile-passthrough-block.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
## Test YAMLProfileReader support for pass-through blocks in non-matching edges:
## match the profile edge A -> C to the CFG with blocks A -> B -> C.

# REQUIRES: system-linux
# RUN: split-file %s %t
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib
# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml --profile-ignore-hash -v=1 \
# RUN: --print-cfg 2>&1 | FileCheck %s

# CHECK: Binary Function "main" after building cfg
# CHECK: Profile Acc : 100.0%
# CHECK-NOT: BOLT-WARNING: no successor for block .LFT0 that matches index 3 or block .Ltmp0

#--- main.s
.globl main
.type main, @function
main:
.cfi_startproc
.LBB00:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
testq %rax, %rax
js .LBB03
.LBB01:
jne .LBB04
.LBB02:
nop
.LBB03:
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
.LBB04:
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
## For relocations against .text
.LBB05:
call exit
.cfi_endproc
.size main, .-main

#--- yaml
---
header:
profile-version: 1
binary-name: 'profile-passthrough-block.s.tmp.exe'
binary-build-id: '<unknown>'
profile-flags: [ lbr ]
profile-origin: branch profile reader
profile-events: ''
dfs-order: false
hash-func: xxh3
functions:
- name: main
fid: 0
hash: 0x0000000000000000
exec: 1
nblocks: 6
blocks:
- bid: 1
insns: 1
succ: [ { bid: 3, cnt: 1} ]
...
5 changes: 5 additions & 0 deletions bolt/test/X86/register-fragments-bolt-symbols.s
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# PREAGG: B X:0 #chain.cold.0# 1 0
# RUN: perf2bolt %t.bolt -p %t.preagg --pa -o %t.bat.fdata -w %t.bat.yaml -v=1 \
# RUN: | FileCheck %s --check-prefix=CHECK-REGISTER
# RUN: FileCheck --input-file %t.bat.fdata --check-prefix=CHECK-FDATA %s
# RUN: FileCheck --input-file %t.bat.yaml --check-prefix=CHECK-YAML %s

# CHECK-SYMS: l df *ABS* [[#]] chain.s
# CHECK-SYMS: l F .bolt.org.text [[#]] chain
Expand All @@ -24,6 +26,9 @@

# CHECK-REGISTER: BOLT-INFO: marking chain.cold.0/1(*2) as a fragment of chain/2(*2)

# CHECK-FDATA: 0 [unknown] 0 1 chain/chain.s/2 10 0 1
# CHECK-YAML: - name: 'chain/chain.s/2'

.file "chain.s"
.text
.type chain, @function
Expand Down
10 changes: 7 additions & 3 deletions bolt/test/X86/sctc-bug4.test
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
# Check that fallthrough blocks are handled properly.
# Check that fallthrough blocks are handled properly and Offset annotation is
# set for conditional tail calls.

RUN: %clang %cflags %S/Inputs/sctc_bug4.s -o %t
RUN: llvm-bolt %t -o %t.null \
RUN: llvm-bolt %t -o %t.null --enable-bat \
RUN: -funcs=test_func -print-sctc -sequential-disassembly 2>&1 | FileCheck %s

CHECK: .Ltmp2 (3 instructions, align : 1)
CHECK-NEXT: CFI State : 0
CHECK-NEXT: Input offset: 0x24
CHECK-NEXT: Predecessors: .LFT1
CHECK-NEXT: 00000024: cmpq $0x20, %rsi
CHECK-NEXT: 00000028: ja dummy # TAILCALL {{.*}}# CTCTakenCount: 0
CHECK-NEXT: 00000028: ja dummy # TAILCALL # Offset: 53 # CTCTakenCount: 0
CHECK-NEXT: 0000002a: jmp .Ltmp4
CHECK-NEXT: Successors: .Ltmp4
CHECK-NEXT: CFI State: 0

CHECK: .Ltmp1 (2 instructions, align : 1)
CHECK-NEXT: CFI State : 0
CHECK-NEXT: Input offset: 0x2c
CHECK-NEXT: Predecessors: .LFT0
CHECK-NEXT: 0000002c: xorq %r11, %rax
CHECK-NEXT: 0000002f: retq
CHECK-NEXT: CFI State: 0

CHECK: .Ltmp4 (4 instructions, align : 1)
CHECK-NEXT: CFI State : 0
CHECK-NEXT: Input offset: 0x3a
CHECK-NEXT: Predecessors: .Ltmp2
40 changes: 40 additions & 0 deletions bolt/test/runtime/bolt-reserved.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// REQUIRES: system-linux

/*
* Check that llvm-bolt uses reserved space in a binary for allocating
* new sections.
*/

// RUN: %clang %s -o %t.exe -Wl,-q
// RUN: llvm-bolt %t.exe -o %t.bolt.exe 2>&1 | FileCheck %s
// RUN: %t.bolt.exe

// CHECK: BOLT-INFO: using reserved space

/*
* Check that llvm-bolt detects a condition when the reserved space is
* not enough for allocating new sections.
*/

// RUN: %clang %s -o %t.tiny.exe -Wl,--no-eh-frame-hdr -Wl,-q -DTINY
// RUN: not llvm-bolt %t.tiny.exe -o %t.tiny.bolt.exe 2>&1 | \
// RUN: FileCheck %s --check-prefix=CHECK-TINY

// CHECK-TINY: BOLT-ERROR: reserved space (1 byte) is smaller than required

#ifdef TINY
#define RSIZE "1"
#else
#define RSIZE "8192 * 1024"
#endif

asm(".pushsection .text \n\
.globl __bolt_reserved_start \n\
.type __bolt_reserved_start, @object \n\
__bolt_reserved_start: \n\
.space " RSIZE " \n\
.globl __bolt_reserved_end \n\
__bolt_reserved_end: \n\
.popsection");

int main() { return 0; }
1 change: 0 additions & 1 deletion clang-tools-extra/clang-query/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ clang_target_link_libraries(clangQuery
clangBasic
clangDynamicASTMatchers
clangFrontend
clangTooling
clangSerialization
)

Expand Down
120 changes: 23 additions & 97 deletions clang-tools-extra/clang-query/Query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
//===----------------------------------------------------------------------===//

#include "Query.h"
#include "QueryParser.h"
#include "QuerySession.h"
#include "clang/AST/ASTDumper.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/TextDiagnostic.h"
#include "clang/Tooling/NodeIntrospection.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>

Expand Down Expand Up @@ -68,8 +68,6 @@ bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
"Diagnostic location for bound nodes.\n"
" detailed-ast "
"Detailed AST output for bound nodes.\n"
" srcloc "
"Source locations and ranges for bound nodes.\n"
" dump "
"Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
return true;
Expand All @@ -90,90 +88,6 @@ struct CollectBoundNodes : MatchFinder::MatchCallback {
}
};

void dumpLocations(llvm::raw_ostream &OS, DynTypedNode Node, ASTContext &Ctx,
const DiagnosticsEngine &Diags, SourceManager const &SM) {
auto Locs = clang::tooling::NodeIntrospection::GetLocations(Node);

auto PrintLocations = [](llvm::raw_ostream &OS, auto Iter, auto End) {
auto CommonEntry = Iter->first;
auto Scout = Iter;
SmallVector<std::string> LocationStrings;
while (Scout->first == CommonEntry) {
LocationStrings.push_back(
tooling::LocationCallFormatterCpp::format(*Iter->second));
if (Scout == End)
break;
++Scout;
if (Scout->first == CommonEntry)
++Iter;
}
llvm::sort(LocationStrings);
for (auto &LS : LocationStrings) {
OS << " * \"" << LS << "\"\n";
}
return Iter;
};

TextDiagnostic TD(OS, Ctx.getLangOpts(), &Diags.getDiagnosticOptions());

for (auto Iter = Locs.LocationAccessors.begin();
Iter != Locs.LocationAccessors.end(); ++Iter) {
if (!Iter->first.isValid())
continue;

TD.emitDiagnostic(FullSourceLoc(Iter->first, SM), DiagnosticsEngine::Note,
"source locations here", std::nullopt, std::nullopt);

Iter = PrintLocations(OS, Iter, Locs.LocationAccessors.end());
OS << '\n';
}

for (auto Iter = Locs.RangeAccessors.begin();
Iter != Locs.RangeAccessors.end(); ++Iter) {

if (!Iter->first.getBegin().isValid())
continue;

if (SM.getPresumedLineNumber(Iter->first.getBegin()) !=
SM.getPresumedLineNumber(Iter->first.getEnd()))
continue;

TD.emitDiagnostic(
FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note,
"source ranges here " + Iter->first.printToString(SM),
CharSourceRange::getTokenRange(Iter->first), std::nullopt);

Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end());
}
for (auto Iter = Locs.RangeAccessors.begin();
Iter != Locs.RangeAccessors.end(); ++Iter) {

if (!Iter->first.getBegin().isValid())
continue;

if (SM.getPresumedLineNumber(Iter->first.getBegin()) ==
SM.getPresumedLineNumber(Iter->first.getEnd()))
continue;

TD.emitDiagnostic(
FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note,
"source range " + Iter->first.printToString(SM) + " starting here...",
CharSourceRange::getTokenRange(Iter->first), std::nullopt);

auto ColNum = SM.getPresumedColumnNumber(Iter->first.getEnd());
auto LastLineLoc = Iter->first.getEnd().getLocWithOffset(-(ColNum - 1));

TD.emitDiagnostic(FullSourceLoc(Iter->first.getEnd(), SM),
DiagnosticsEngine::Note, "... ending here",
CharSourceRange::getTokenRange(
SourceRange(LastLineLoc, Iter->first.getEnd())),
std::nullopt);

Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end());
}
OS << "\n";
}

} // namespace

bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
Expand All @@ -194,8 +108,7 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
return false;
}

auto &Ctx = AST->getASTContext();
const auto &SM = Ctx.getSourceManager();
ASTContext &Ctx = AST->getASTContext();
Ctx.getParentMapContext().setTraversalKind(QS.TK);
Finder.matchAST(Ctx);

Expand Down Expand Up @@ -243,19 +156,11 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
}
if (QS.DetailedASTOutput) {
OS << "Binding for \"" << BI->first << "\":\n";
const ASTContext &Ctx = AST->getASTContext();
ASTDumper Dumper(OS, Ctx, AST->getDiagnostics().getShowColors());
Dumper.SetTraversalKind(QS.TK);
Dumper.Visit(BI->second);
OS << "\n";
}
if (QS.SrcLocOutput) {
OS << "\n \"" << BI->first << "\" Source locations\n";
OS << " " << std::string(19 + BI->first.size(), '-') << '\n';

dumpLocations(OS, BI->second, Ctx, AST->getDiagnostics(), SM);
OS << "\n";
}
}

if (MI->getMap().empty())
Expand All @@ -281,5 +186,26 @@ const QueryKind SetQueryKind<bool>::value;
const QueryKind SetQueryKind<OutputKind>::value;
#endif

bool FileQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
auto Buffer = llvm::MemoryBuffer::getFile(StringRef{File}.trim());
if (!Buffer) {
if (Prefix.has_value())
llvm::errs() << *Prefix << ": ";
llvm::errs() << "cannot open " << File << ": "
<< Buffer.getError().message() << "\n";
return false;
}

StringRef FileContentRef(Buffer.get()->getBuffer());

while (!FileContentRef.empty()) {
QueryRef Q = QueryParser::parse(FileContentRef, QS);
if (!Q->run(llvm::outs(), QS))
return false;
FileContentRef = Q->RemainingContent;
}
return true;
}

} // namespace query
} // namespace clang
21 changes: 18 additions & 3 deletions clang-tools-extra/clang-query/Query.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
namespace clang {
namespace query {

enum OutputKind { OK_Diag, OK_Print, OK_DetailedAST, OK_SrcLoc };
enum OutputKind { OK_Diag, OK_Print, OK_DetailedAST };

enum QueryKind {
QK_Invalid,
Expand All @@ -30,7 +30,8 @@ enum QueryKind {
QK_SetTraversalKind,
QK_EnableOutputKind,
QK_DisableOutputKind,
QK_Quit
QK_Quit,
QK_File
};

class QuerySession;
Expand Down Expand Up @@ -148,7 +149,6 @@ struct SetExclusiveOutputQuery : Query {
QS.DiagOutput = false;
QS.DetailedASTOutput = false;
QS.PrintOutput = false;
QS.SrcLocOutput = false;
QS.*Var = true;
return true;
}
Expand Down Expand Up @@ -188,6 +188,21 @@ struct DisableOutputQuery : SetNonExclusiveOutputQuery {
}
};

struct FileQuery : Query {
FileQuery(StringRef File, StringRef Prefix = StringRef())
: Query(QK_File), File(File),
Prefix(!Prefix.empty() ? std::optional<std::string>(Prefix)
: std::nullopt) {}

bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;

static bool classof(const Query *Q) { return Q->Kind == QK_File; }

private:
std::string File;
std::optional<std::string> Prefix;
};

} // namespace query
} // namespace clang

Expand Down
36 changes: 17 additions & 19 deletions clang-tools-extra/clang-query/QueryParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include "QuerySession.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Tooling/NodeIntrospection.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include <optional>
Expand Down Expand Up @@ -104,19 +103,16 @@ QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {

template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() {
StringRef ValStr;
bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport();
unsigned OutKind =
LexOrCompleteWord<unsigned>(this, ValStr)
.Case("diag", OK_Diag)
.Case("print", OK_Print)
.Case("detailed-ast", OK_DetailedAST)
.Case("srcloc", OK_SrcLoc, /*IsCompletion=*/HasIntrospection)
.Case("dump", OK_DetailedAST)
.Default(~0u);
unsigned OutKind = LexOrCompleteWord<unsigned>(this, ValStr)
.Case("diag", OK_Diag)
.Case("print", OK_Print)
.Case("detailed-ast", OK_DetailedAST)
.Case("dump", OK_DetailedAST)
.Default(~0u);
if (OutKind == ~0u) {
return new InvalidQuery("expected 'diag', 'print', 'detailed-ast'" +
StringRef(HasIntrospection ? ", 'srcloc'" : "") +
" or 'dump', got '" + ValStr + "'");
return new InvalidQuery("expected 'diag', 'print', 'detailed-ast' or "
"'dump', got '" +
ValStr + "'");
}

switch (OutKind) {
Expand All @@ -126,10 +122,6 @@ template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() {
return new QueryType(&QuerySession::DiagOutput);
case OK_Print:
return new QueryType(&QuerySession::PrintOutput);
case OK_SrcLoc:
if (HasIntrospection)
return new QueryType(&QuerySession::SrcLocOutput);
return new InvalidQuery("'srcloc' output support is not available.");
}

llvm_unreachable("Invalid output kind");
Expand Down Expand Up @@ -183,7 +175,8 @@ enum ParsedQueryKind {
PQK_Unlet,
PQK_Quit,
PQK_Enable,
PQK_Disable
PQK_Disable,
PQK_File
};

enum ParsedQueryVariable {
Expand Down Expand Up @@ -222,12 +215,14 @@ QueryRef QueryParser::doParse() {
.Case("let", PQK_Let)
.Case("m", PQK_Match, /*IsCompletion=*/false)
.Case("match", PQK_Match)
.Case("q", PQK_Quit, /*IsCompletion=*/false)
.Case("q", PQK_Quit, /*IsCompletion=*/false)
.Case("quit", PQK_Quit)
.Case("set", PQK_Set)
.Case("enable", PQK_Enable)
.Case("disable", PQK_Disable)
.Case("unlet", PQK_Unlet)
.Case("f", PQK_File, /*IsCompletion=*/false)
.Case("file", PQK_File)
.Default(PQK_Invalid);

switch (QKind) {
Expand Down Expand Up @@ -351,6 +346,9 @@ QueryRef QueryParser::doParse() {
return endQuery(new LetQuery(Name, VariantValue()));
}

case PQK_File:
return new FileQuery(Line);

case PQK_Invalid:
return new InvalidQuery("unknown command: " + CommandStr);
}
Expand Down
5 changes: 2 additions & 3 deletions clang-tools-extra/clang-query/QuerySession.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ class QuerySession {
public:
QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
: ASTs(ASTs), PrintOutput(false), DiagOutput(true),
DetailedASTOutput(false), SrcLocOutput(false), BindRoot(true),
PrintMatcher(false), Terminate(false), TK(TK_AsIs) {}
DetailedASTOutput(false), BindRoot(true), PrintMatcher(false),
Terminate(false), TK(TK_AsIs) {}

llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;

bool PrintOutput;
bool DiagOutput;
bool DetailedASTOutput;
bool SrcLocOutput;

bool BindRoot;
bool PrintMatcher;
Expand Down
18 changes: 2 additions & 16 deletions clang-tools-extra/clang-query/tool/ClangQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,8 @@ static cl::opt<std::string> PreloadFile(

bool runCommandsInFile(const char *ExeName, std::string const &FileName,
QuerySession &QS) {
auto Buffer = llvm::MemoryBuffer::getFile(FileName);
if (!Buffer) {
llvm::errs() << ExeName << ": cannot open " << FileName << ": "
<< Buffer.getError().message() << "\n";
return true;
}

StringRef FileContentRef(Buffer.get()->getBuffer());

while (!FileContentRef.empty()) {
QueryRef Q = QueryParser::parse(FileContentRef, QS);
if (!Q->run(llvm::outs(), QS))
return true;
FileContentRef = Q->RemainingContent;
}
return false;
FileQuery Query(FileName, ExeName);
return !Query.run(llvm::errs(), QS);
}

int main(int argc, const char **argv) {
Expand Down
10 changes: 5 additions & 5 deletions clang-tools-extra/clang-tidy/ClangTidy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,11 +373,11 @@ static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context,

const auto &RegisteredCheckers =
AnalyzerOptions::getRegisteredCheckers(IncludeExperimental);
bool AnalyzerChecksEnabled = false;
for (StringRef CheckName : RegisteredCheckers) {
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName);
}
const bool AnalyzerChecksEnabled =
llvm::any_of(RegisteredCheckers, [&](StringRef CheckName) -> bool {
return Context.isCheckEnabled(
(AnalyzerCheckNamePrefix + CheckName).str());
});

if (!AnalyzerChecksEnabled)
return List;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-tidy/ClangTidyCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ std::optional<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
if (IgnoreCase) {
if (Value.equals_insensitive(NameAndEnum.second))
return NameAndEnum.first;
} else if (Value.equals(NameAndEnum.second)) {
} else if (Value == NameAndEnum.second) {
return NameAndEnum.first;
} else if (Value.equals_insensitive(NameAndEnum.second)) {
Closest = NameAndEnum.second;
Expand Down
28 changes: 17 additions & 11 deletions clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,18 @@ ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
: Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
RemoveIncompatibleErrors(RemoveIncompatibleErrors),
GetFixesFromNotes(GetFixesFromNotes),
EnableNolintBlocks(EnableNolintBlocks) {}
EnableNolintBlocks(EnableNolintBlocks) {

if (Context.getOptions().HeaderFilterRegex &&
!Context.getOptions().HeaderFilterRegex->empty())
HeaderFilter =
std::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);

if (Context.getOptions().ExcludeHeaderFilterRegex &&
!Context.getOptions().ExcludeHeaderFilterRegex->empty())
ExcludeHeaderFilter = std::make_unique<llvm::Regex>(
*Context.getOptions().ExcludeHeaderFilterRegex);
}

void ClangTidyDiagnosticConsumer::finalizeLastError() {
if (!Errors.empty()) {
Expand Down Expand Up @@ -562,22 +573,17 @@ void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
}

StringRef FileName(File->getName());
LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
Sources.isInMainFile(Location) ||
getHeaderFilter()->match(FileName);
LastErrorRelatesToUserCode =
LastErrorRelatesToUserCode || Sources.isInMainFile(Location) ||
(HeaderFilter &&
(HeaderFilter->match(FileName) &&
!(ExcludeHeaderFilter && ExcludeHeaderFilter->match(FileName))));

unsigned LineNumber = Sources.getExpansionLineNumber(Location);
LastErrorPassesLineFilter =
LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
}

llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
if (!HeaderFilter)
HeaderFilter =
std::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
return HeaderFilter.get();
}

void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
// Each error is modelled as the set of intervals in which it applies
// replacements. To detect overlapping replacements, we use a sweep line
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
bool EnableNolintBlocks;
std::vector<ClangTidyError> Errors;
std::unique_ptr<llvm::Regex> HeaderFilter;
std::unique_ptr<llvm::Regex> ExcludeHeaderFilter;
bool LastErrorRelatesToUserCode = false;
bool LastErrorPassesLineFilter = false;
bool LastErrorWasIgnored = false;
Expand Down
6 changes: 5 additions & 1 deletion clang-tools-extra/clang-tidy/ClangTidyOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ template <> struct MappingTraits<ClangTidyOptions> {
IO.mapOptional("ImplementationFileExtensions",
Options.ImplementationFileExtensions);
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
IO.mapOptional("ExcludeHeaderFilterRegex",
Options.ExcludeHeaderFilterRegex);
IO.mapOptional("FormatStyle", Options.FormatStyle);
IO.mapOptional("User", Options.User);
IO.mapOptional("CheckOptions", Options.CheckOptions);
Expand All @@ -191,7 +193,8 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
Options.WarningsAsErrors = "";
Options.HeaderFileExtensions = {"", "h", "hh", "hpp", "hxx"};
Options.ImplementationFileExtensions = {"c", "cc", "cpp", "cxx"};
Options.HeaderFilterRegex = "";
Options.HeaderFilterRegex = std::nullopt;
Options.ExcludeHeaderFilterRegex = std::nullopt;
Options.SystemHeaders = false;
Options.FormatStyle = "none";
Options.User = std::nullopt;
Expand Down Expand Up @@ -231,6 +234,7 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
overrideValue(ImplementationFileExtensions,
Other.ImplementationFileExtensions);
overrideValue(HeaderFilterRegex, Other.HeaderFilterRegex);
overrideValue(ExcludeHeaderFilterRegex, Other.ExcludeHeaderFilterRegex);
overrideValue(SystemHeaders, Other.SystemHeaders);
overrideValue(FormatStyle, Other.FormatStyle);
overrideValue(User, Other.User);
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/ClangTidyOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ struct ClangTidyOptions {
/// main files will always be displayed.
std::optional<std::string> HeaderFilterRegex;

/// \brief Exclude warnings from headers matching this filter, even if they
/// match \c HeaderFilterRegex.
std::optional<std::string> ExcludeHeaderFilterRegex;

/// Output warnings from system headers matching \c HeaderFilterRegex.
std::optional<bool> SystemHeaders;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ AST_MATCHER(QualType, isEnableIf) {
const NamedDecl *TypeDecl =
Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
return TypeDecl->isInStdNamespace() &&
(TypeDecl->getName().equals("enable_if") ||
TypeDecl->getName().equals("enable_if_t"));
(TypeDecl->getName() == "enable_if" ||
TypeDecl->getName() == "enable_if_t");
};
const Type *BaseType = Node.getTypePtr();
// Case: pointer or reference to enable_if.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
#include "ImplicitWideningOfMultiplicationResultCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Lex/Lexer.h"
#include <optional>

using namespace clang::ast_matchers;

namespace clang {
namespace clang::tidy::bugprone {

namespace {
AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
return Node.isPartOfExplicitCast();
}
AST_MATCHER(Expr, containsErrors) { return Node.containsErrors(); }
} // namespace
} // namespace clang

namespace clang::tidy::bugprone {

static const Expr *getLHSOfMulBinOp(const Expr *E) {
assert(E == E->IgnoreParens() && "Already skipped all parens!");
Expand Down Expand Up @@ -250,7 +250,8 @@ void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(

void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
MatchFinder *Finder) {
Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(),
Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),
isInTemplateInstantiation(),
isPartOfExplicitCast())),
hasCastKind(CK_IntegralCast))
.bind("x"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {
ofClass(matchers::matchesAnyListedName(OptionalTypes)))),
hasType(ConstructTypeMatcher),
hasArgument(0U, ignoringImpCasts(anyOf(OptionalDereferenceMatcher,
StdMoveCallMatcher))))
StdMoveCallMatcher))),
unless(anyOf(hasAncestor(typeLoc()),
hasAncestor(expr(matchers::hasUnevaluatedContext())))))
.bind("expr"),
this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,11 @@ std::optional<RenamerClangTidyCheck::FailureInfo>
ReservedIdentifierCheck::getDeclFailureInfo(const NamedDecl *Decl,
const SourceManager &) const {
assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() &&
!Decl->isImplicit() &&
"Decl must be an explicit identifier with a name.");
// Implicit identifiers cannot fail.
if (Decl->isImplicit())
return std::nullopt;

return getFailureInfoImpl(
Decl->getName(), isa<TranslationUnitDecl>(Decl->getDeclContext()),
/*IsMacro = */ false, getLangOpts(), Invert, AllowedIdentifiers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,7 @@ void SuspiciousEnumUsageCheck::check(const MatchFinder::MatchResult &Result) {
// Skip when one of the parameters is an empty enum. The
// hasDisjointValueRange function could not decide the values properly in
// case of an empty enum.
if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
if (EnumDec->enumerators().empty() || OtherEnumDec->enumerators().empty())
return;

if (!hasDisjointValueRange(EnumDec, OtherEnumDec))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ static std::string createReplacementText(const LambdaExpr *Lambda) {
AppendName("this");
}
}
if (!Replacement.empty() &&
Lambda->explicit_capture_begin() != Lambda->explicit_capture_end()) {
if (!Replacement.empty() && !Lambda->explicit_captures().empty()) {
// Add back separator if we are adding explicit capture variables.
Stream << ", ";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,44 @@ SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
"AllowMissingMoveFunctions", false)),
AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", false)),
AllowMissingMoveFunctionsWhenCopyIsDeleted(
Options.get("AllowMissingMoveFunctionsWhenCopyIsDeleted", false)) {}
Options.get("AllowMissingMoveFunctionsWhenCopyIsDeleted", false)),
AllowImplicitlyDeletedCopyOrMove(
Options.get("AllowImplicitlyDeletedCopyOrMove", false)) {}

void SpecialMemberFunctionsCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
Options.store(Opts, "AllowMissingMoveFunctionsWhenCopyIsDeleted",
AllowMissingMoveFunctionsWhenCopyIsDeleted);
Options.store(Opts, "AllowImplicitlyDeletedCopyOrMove",
AllowImplicitlyDeletedCopyOrMove);
}

std::optional<TraversalKind>
SpecialMemberFunctionsCheck::getCheckTraversalKind() const {
return AllowImplicitlyDeletedCopyOrMove ? TK_AsIs
: TK_IgnoreUnlessSpelledInSource;
}

void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) {
auto IsNotImplicitOrDeleted = anyOf(unless(isImplicit()), isDeleted());

Finder->addMatcher(
cxxRecordDecl(
eachOf(has(cxxDestructorDecl().bind("dtor")),
has(cxxConstructorDecl(isCopyConstructor()).bind("copy-ctor")),
has(cxxMethodDecl(isCopyAssignmentOperator())
unless(isImplicit()),
eachOf(has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")),
has(cxxConstructorDecl(isCopyConstructor(),
IsNotImplicitOrDeleted)
.bind("copy-ctor")),
has(cxxMethodDecl(isCopyAssignmentOperator(),
IsNotImplicitOrDeleted)
.bind("copy-assign")),
has(cxxConstructorDecl(isMoveConstructor()).bind("move-ctor")),
has(cxxMethodDecl(isMoveAssignmentOperator())
has(cxxConstructorDecl(isMoveConstructor(),
IsNotImplicitOrDeleted)
.bind("move-ctor")),
has(cxxMethodDecl(isMoveAssignmentOperator(),
IsNotImplicitOrDeleted)
.bind("move-assign"))))
.bind("class-def"),
this);
Expand Down Expand Up @@ -127,7 +146,8 @@ void SpecialMemberFunctionsCheck::check(
for (const auto &KV : Matchers)
if (const auto *MethodDecl =
Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
StoreMember({KV.second, MethodDecl->isDeleted()});
StoreMember(
{KV.second, MethodDecl->isDeleted(), MethodDecl->isImplicit()});
}
}

Expand All @@ -144,7 +164,13 @@ void SpecialMemberFunctionsCheck::checkForMissingMembers(

auto HasMember = [&](SpecialMemberFunctionKind Kind) {
return llvm::any_of(DefinedMembers, [Kind](const auto &Data) {
return Data.FunctionKind == Kind;
return Data.FunctionKind == Kind && !Data.IsImplicit;
});
};

auto HasImplicitDeletedMember = [&](SpecialMemberFunctionKind Kind) {
return llvm::any_of(DefinedMembers, [Kind](const auto &Data) {
return Data.FunctionKind == Kind && Data.IsImplicit && Data.IsDeleted;
});
};

Expand All @@ -154,9 +180,17 @@ void SpecialMemberFunctionsCheck::checkForMissingMembers(
});
};

auto RequireMember = [&](SpecialMemberFunctionKind Kind) {
if (!HasMember(Kind))
MissingMembers.push_back(Kind);
auto RequireMembers = [&](SpecialMemberFunctionKind Kind1,
SpecialMemberFunctionKind Kind2) {
if (AllowImplicitlyDeletedCopyOrMove && HasImplicitDeletedMember(Kind1) &&
HasImplicitDeletedMember(Kind2))
return;

if (!HasMember(Kind1))
MissingMembers.push_back(Kind1);

if (!HasMember(Kind2))
MissingMembers.push_back(Kind2);
};

bool RequireThree =
Expand All @@ -180,23 +214,25 @@ void SpecialMemberFunctionsCheck::checkForMissingMembers(
!HasMember(SpecialMemberFunctionKind::NonDefaultDestructor))
MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);

RequireMember(SpecialMemberFunctionKind::CopyConstructor);
RequireMember(SpecialMemberFunctionKind::CopyAssignment);
RequireMembers(SpecialMemberFunctionKind::CopyConstructor,
SpecialMemberFunctionKind::CopyAssignment);
}

if (RequireFive &&
!(AllowMissingMoveFunctionsWhenCopyIsDeleted &&
(IsDeleted(SpecialMemberFunctionKind::CopyConstructor) &&
IsDeleted(SpecialMemberFunctionKind::CopyAssignment)))) {
assert(RequireThree);
RequireMember(SpecialMemberFunctionKind::MoveConstructor);
RequireMember(SpecialMemberFunctionKind::MoveAssignment);
RequireMembers(SpecialMemberFunctionKind::MoveConstructor,
SpecialMemberFunctionKind::MoveAssignment);
}

if (!MissingMembers.empty()) {
llvm::SmallVector<SpecialMemberFunctionKind, 5> DefinedMemberKinds;
llvm::transform(DefinedMembers, std::back_inserter(DefinedMemberKinds),
[](const auto &Data) { return Data.FunctionKind; });
for (const auto &Data : DefinedMembers) {
if (!Data.IsImplicit)
DefinedMemberKinds.push_back(Data.FunctionKind);
}
diag(ID.first, "class '%0' defines %1 but does not define %2")
<< ID.second << cppcoreguidelines::join(DefinedMemberKinds, " and ")
<< cppcoreguidelines::join(MissingMembers, " or ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ class SpecialMemberFunctionsCheck : public ClangTidyCheck {
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void onEndOfTranslationUnit() override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
std::optional<TraversalKind> getCheckTraversalKind() const override;

enum class SpecialMemberFunctionKind : uint8_t {
Destructor,
DefaultDestructor,
Expand All @@ -46,6 +45,7 @@ class SpecialMemberFunctionsCheck : public ClangTidyCheck {
struct SpecialMemberFunctionData {
SpecialMemberFunctionKind FunctionKind;
bool IsDeleted;
bool IsImplicit = false;

bool operator==(const SpecialMemberFunctionData &Other) const {
return (Other.FunctionKind == FunctionKind) &&
Expand All @@ -67,6 +67,7 @@ class SpecialMemberFunctionsCheck : public ClangTidyCheck {
const bool AllowMissingMoveFunctions;
const bool AllowSoleDefaultDtor;
const bool AllowMissingMoveFunctionsWhenCopyIsDeleted;
const bool AllowImplicitlyDeletedCopyOrMove;
ClassDefiningSpecialMembersMap ClassWithSpecialMembers;
};

Expand Down
5 changes: 3 additions & 2 deletions clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "SignedBitwiseCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"

using namespace clang::ast_matchers;
using namespace clang::ast_matchers::internal;
Expand All @@ -29,8 +30,8 @@ void SignedBitwiseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) {
const auto SignedIntegerOperand =
(IgnorePositiveIntegerLiterals
? expr(ignoringImpCasts(hasType(isSignedInteger())),
unless(integerLiteral()))
? expr(ignoringImpCasts(
allOf(hasType(isSignedInteger()), unless(integerLiteral()))))
: expr(ignoringImpCasts(hasType(isSignedInteger()))))
.bind("signed-operand");

Expand Down
4 changes: 1 addition & 3 deletions clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,7 @@ void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {

// In non-strict mode ignore function definitions with empty bodies
// (constructor initializer counts for non-empty body).
if (StrictMode ||
(Function->getBody()->child_begin() !=
Function->getBody()->child_end()) ||
if (StrictMode || !Function->getBody()->children().empty() ||
(isa<CXXConstructorDecl>(Function) &&
cast<CXXConstructorDecl>(Function)->getNumCtorInitializers() > 0))
warnOnUnusedParameter(Result, Function, I);
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ add_clang_library(clangTidyModernizeModule
UseNullptrCheck.cpp
UseOverrideCheck.cpp
UseStartsEndsWithCheck.cpp
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
UseStdPrintCheck.cpp
UseTrailingReturnTypeCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ getContainerFromBeginEndCall(const Expr *Init, bool IsBegin, bool *IsArrow,
return {};
if (IsReverse && !Call->Name.consume_back("r"))
return {};
if (!Call->Name.empty() && !Call->Name.equals("c"))
if (!Call->Name.empty() && Call->Name != "c")
return {};
return std::make_pair(Call->Container, Call->CallKind);
}
Expand Down
Loading