29 changes: 18 additions & 11 deletions bolt/lib/Core/DIEBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,32 +461,42 @@ getUnitForOffset(DIEBuilder &Builder, DWARFContext &DWCtx,
return nullptr;
}

uint32_t DIEBuilder::finalizeDIEs(
DWARFUnit &CU, DIE &Die,
std::vector<std::optional<BOLTDWARF5AccelTableData *>> &Parents,
uint32_t &CurOffset) {
uint32_t
DIEBuilder::finalizeDIEs(DWARFUnit &CU, DIE &Die,
std::optional<BOLTDWARF5AccelTableData *> Parent,
uint32_t NumberParentsInChain, uint32_t &CurOffset) {
getState().DWARFDieAddressesParsed.erase(Die.getOffset());
uint32_t CurSize = 0;
Die.setOffset(CurOffset);
std::optional<BOLTDWARF5AccelTableData *> NameEntry =
DebugNamesTable.addAccelTableEntry(
CU, Die, SkeletonCU ? SkeletonCU->getDWOId() : std::nullopt,
Parents.back());
NumberParentsInChain, Parent);
// It is possible that an indexed debugging information entry has a parent
// that is not indexed (for example, if its parent does not have a name
// attribute). In such a case, a parent attribute may point to a nameless
// index entry (that is, one that cannot be reached from any entry in the name
// table), or it may point to the nearest ancestor that does have an index
// entry.
// Skipping entry is not very useful for LLDB. This follows clang where
// children of forward declaration won't have DW_IDX_parent.
// https://github.com/llvm/llvm-project/pull/91808

// If Parent is nullopt and NumberParentsInChain is not zero, then forward
// declaration was encountered in this DF traversal. Propagating nullopt for
// Parent to children.
if (!Parent && NumberParentsInChain)
NameEntry = std::nullopt;
if (NameEntry)
Parents.push_back(std::move(NameEntry));
++NumberParentsInChain;
for (DIEValue &Val : Die.values())
CurSize += Val.sizeOf(CU.getFormParams());
CurSize += getULEB128Size(Die.getAbbrevNumber());
CurOffset += CurSize;

for (DIE &Child : Die.children()) {
uint32_t ChildSize = finalizeDIEs(CU, Child, Parents, CurOffset);
uint32_t ChildSize =
finalizeDIEs(CU, Child, NameEntry, NumberParentsInChain, CurOffset);
CurSize += ChildSize;
}
// for children end mark.
Expand All @@ -496,9 +506,6 @@ uint32_t DIEBuilder::finalizeDIEs(
}

Die.setSize(CurSize);
if (NameEntry)
Parents.pop_back();

return CurSize;
}

Expand All @@ -510,7 +517,7 @@ void DIEBuilder::finish() {
DebugNamesTable.setCurrentUnit(CU, UnitStartOffset);
std::vector<std::optional<BOLTDWARF5AccelTableData *>> Parents;
Parents.push_back(std::nullopt);
finalizeDIEs(CU, *UnitDIE, Parents, CurOffset);
finalizeDIEs(CU, *UnitDIE, std::nullopt, 0, CurOffset);

DWARFUnitInfo &CurUnitInfo = getUnitInfoByDwarfUnit(CU);
CurUnitInfo.UnitOffset = UnitStartOffset;
Expand Down
11 changes: 9 additions & 2 deletions bolt/lib/Core/DebugNames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ static uint64_t getEntryID(const BOLTDWARF5AccelTableData &Entry) {
std::optional<BOLTDWARF5AccelTableData *>
DWARF5AcceleratorTable::addAccelTableEntry(
DWARFUnit &Unit, const DIE &Die, const std::optional<uint64_t> &DWOID,
const uint32_t NumberParentsInChain,
std::optional<BOLTDWARF5AccelTableData *> &Parent) {
if (Unit.getVersion() < 5 || !NeedToCreate)
return std::nullopt;
Expand Down Expand Up @@ -312,8 +313,14 @@ DWARF5AcceleratorTable::addAccelTableEntry(
// Keeping memory footprint down.
if (ParentOffset)
EntryRelativeOffsets.insert({*ParentOffset, 0});
bool IsParentRoot = false;
// If there is no parent and no valid Entries in parent chain this is a root
// to be marked with a flag.
if (!Parent && !NumberParentsInChain)
IsParentRoot = true;
It.Values.push_back(new (Allocator) BOLTDWARF5AccelTableData(
Die.getOffset(), ParentOffset, DieTag, UnitID, IsTU, SecondIndex));
Die.getOffset(), ParentOffset, DieTag, UnitID, IsParentRoot, IsTU,
SecondIndex));
return It.Values.back();
};

Expand Down Expand Up @@ -462,7 +469,7 @@ void DWARF5AcceleratorTable::populateAbbrevsMap() {
Abbrev.addAttribute({dwarf::DW_IDX_die_offset, dwarf::DW_FORM_ref4});
if (std::optional<uint64_t> Offset = Value->getParentDieOffset())
Abbrev.addAttribute({dwarf::DW_IDX_parent, dwarf::DW_FORM_ref4});
else
else if (Value->isParentRoot())
Abbrev.addAttribute(
{dwarf::DW_IDX_parent, dwarf::DW_FORM_flag_present});
FoldingSetNodeID ID;
Expand Down
185 changes: 185 additions & 0 deletions bolt/lib/Core/GDBIndex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//===- bolt/Core/GDBIndex.cpp - GDB Index support ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "bolt/Core/GDBIndex.h"

using namespace llvm::bolt;
using namespace llvm::support::endian;

void GDBIndex::addGDBTypeUnitEntry(const GDBIndexTUEntry &&Entry) {
std::lock_guard<std::mutex> Lock(GDBIndexMutex);
if (!BC.getGdbIndexSection())
return;
GDBIndexTUEntryVector.emplace_back(Entry);
}

void GDBIndex::updateGdbIndexSection(
const CUOffsetMap &CUMap, const uint32_t NumCUs,
DebugARangesSectionWriter &ARangesSectionWriter) {
if (!BC.getGdbIndexSection())
return;

// See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html
// for .gdb_index section format.

StringRef GdbIndexContents = BC.getGdbIndexSection()->getContents();

const char *Data = GdbIndexContents.data();

// Parse the header.
const uint32_t Version = read32le(Data);
if (Version != 7 && Version != 8) {
errs() << "BOLT-ERROR: can only process .gdb_index versions 7 and 8\n";
exit(1);
}

// Some .gdb_index generators use file offsets while others use section
// offsets. Hence we can only rely on offsets relative to each other,
// and ignore their absolute values.
const uint32_t CUListOffset = read32le(Data + 4);
const uint32_t CUTypesOffset = read32le(Data + 8);
const uint32_t AddressTableOffset = read32le(Data + 12);
const uint32_t SymbolTableOffset = read32le(Data + 16);
const uint32_t ConstantPoolOffset = read32le(Data + 20);
Data += 24;

// Map CUs offsets to indices and verify existing index table.
std::map<uint32_t, uint32_t> OffsetToIndexMap;
const uint32_t CUListSize = CUTypesOffset - CUListOffset;
const uint32_t TUListSize = AddressTableOffset - CUTypesOffset;
const unsigned NUmCUsEncoded = CUListSize / 16;
unsigned MaxDWARFVersion = BC.DwCtx->getMaxVersion();
unsigned NumDWARF5TUs =
getGDBIndexTUEntryVector().size() - BC.DwCtx->getNumTypeUnits();
bool SkipTypeUnits = false;
// For DWARF5 Types are in .debug_info.
// LLD doesn't generate Types CU List, and in CU list offset
// only includes CUs.
// GDB 11+ includes only CUs in CU list and generates Types
// list.
// GDB 9 includes CUs and TUs in CU list and generates TYpes
// list. The NumCUs is CUs + TUs, so need to modify the check.
// For split-dwarf
// GDB-11, DWARF5: TU units from dwo are not included.
// GDB-11, DWARF4: TU units from dwo are included.
if (MaxDWARFVersion >= 5)
SkipTypeUnits = !TUListSize ? true
: ((NUmCUsEncoded + NumDWARF5TUs) ==
BC.DwCtx->getNumCompileUnits());

if (!((CUListSize == NumCUs * 16) ||
(CUListSize == (NumCUs + NumDWARF5TUs) * 16))) {
errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n";
exit(1);
}
DenseSet<uint64_t> OriginalOffsets;
for (unsigned Index = 0, Units = BC.DwCtx->getNumCompileUnits();
Index < Units; ++Index) {
const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index);
if (SkipTypeUnits && CU->isTypeUnit())
continue;
const uint64_t Offset = read64le(Data);
Data += 16;
if (CU->getOffset() != Offset) {
errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n";
exit(1);
}

OriginalOffsets.insert(Offset);
OffsetToIndexMap[Offset] = Index;
}

// Ignore old address table.
const uint32_t OldAddressTableSize = SymbolTableOffset - AddressTableOffset;
// Move Data to the beginning of symbol table.
Data += SymbolTableOffset - CUTypesOffset;

// Calculate the size of the new address table.
uint32_t NewAddressTableSize = 0;
for (const auto &CURangesPair : ARangesSectionWriter.getCUAddressRanges()) {
const SmallVector<DebugAddressRange, 2> &Ranges = CURangesPair.second;
NewAddressTableSize += Ranges.size() * 20;
}

// Difference between old and new table (and section) sizes.
// Could be negative.
int32_t Delta = NewAddressTableSize - OldAddressTableSize;

size_t NewGdbIndexSize = GdbIndexContents.size() + Delta;

// Free'd by ExecutableFileMemoryManager.
auto *NewGdbIndexContents = new uint8_t[NewGdbIndexSize];
uint8_t *Buffer = NewGdbIndexContents;

write32le(Buffer, Version);
write32le(Buffer + 4, CUListOffset);
write32le(Buffer + 8, CUTypesOffset);
write32le(Buffer + 12, AddressTableOffset);
write32le(Buffer + 16, SymbolTableOffset + Delta);
write32le(Buffer + 20, ConstantPoolOffset + Delta);
Buffer += 24;

using MapEntry = std::pair<uint32_t, CUInfo>;
std::vector<MapEntry> CUVector(CUMap.begin(), CUMap.end());
// Need to sort since we write out all of TUs in .debug_info before CUs.
std::sort(CUVector.begin(), CUVector.end(),
[](const MapEntry &E1, const MapEntry &E2) -> bool {
return E1.second.Offset < E2.second.Offset;
});
// Writing out CU List <Offset, Size>
for (auto &CUInfo : CUVector) {
// Skipping TU for DWARF5 when they are not included in CU list.
if (!OriginalOffsets.count(CUInfo.first))
continue;
write64le(Buffer, CUInfo.second.Offset);
// Length encoded in CU doesn't contain first 4 bytes that encode length.
write64le(Buffer + 8, CUInfo.second.Length + 4);
Buffer += 16;
}

// Rewrite TU CU List, since abbrevs can be different.
// Entry example:
// 0: offset = 0x00000000, type_offset = 0x0000001e, type_signature =
// 0x418503b8111e9a7b Spec says " triplet, the first value is the CU offset,
// the second value is the type offset in the CU, and the third value is the
// type signature" Looking at what is being generated by gdb-add-index. The
// first entry is TU offset, second entry is offset from it, and third entry
// is the type signature.
if (TUListSize)
for (const GDBIndexTUEntry &Entry : getGDBIndexTUEntryVector()) {
write64le(Buffer, Entry.UnitOffset);
write64le(Buffer + 8, Entry.TypeDIERelativeOffset);
write64le(Buffer + 16, Entry.TypeHash);
Buffer += sizeof(GDBIndexTUEntry);
}

// Generate new address table.
for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair :
ARangesSectionWriter.getCUAddressRanges()) {
const uint32_t CUIndex = OffsetToIndexMap[CURangesPair.first];
const DebugAddressRangesVector &Ranges = CURangesPair.second;
for (const DebugAddressRange &Range : Ranges) {
write64le(Buffer, Range.LowPC);
write64le(Buffer + 8, Range.HighPC);
write32le(Buffer + 16, CUIndex);
Buffer += 20;
}
}

const size_t TrailingSize =
GdbIndexContents.data() + GdbIndexContents.size() - Data;
assert(Buffer + TrailingSize == NewGdbIndexContents + NewGdbIndexSize &&
"size calculation error");

// Copy over the rest of the original data.
memcpy(Buffer, Data, TrailingSize);

// Register the new section.
BC.registerOrUpdateNoteSection(".gdb_index", NewGdbIndexContents,
NewGdbIndexSize);
}
8 changes: 4 additions & 4 deletions bolt/lib/Passes/ValidateMemRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst,
if (!BD)
return false;

const uint64_t TargetAddress = BD->getAddress() + Offset;
JumpTable *JT = BC.getJumpTableContainingAddress(TargetAddress);
JumpTable *JT = BC.getJumpTableContainingAddress(BD->getAddress());
if (!JT)
return false;

Expand All @@ -43,8 +42,9 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst,
// the jump table label with a regular rodata reference. Get a
// non-JT reference by fetching the symbol 1 byte before the JT
// label.
MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(TargetAddress - 1, "DATAat");
BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, 1, &*BC.Ctx, 0);
MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(BD->getAddress() - 1, "DATAat");
BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, Offset + 1, &*BC.Ctx,
0);
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: replaced reference @" << BF.getPrintName()
<< " from " << BD->getName() << " to " << NewSym->getName()
<< " + 1\n");
Expand Down
7 changes: 2 additions & 5 deletions bolt/lib/Passes/VeneerElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,8 @@ Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
continue;

VeneerCallers++;
if (!BC.MIB->replaceBranchTarget(
Instr, VeneerDestinations[TargetSymbol], BC.Ctx.get())) {
return createFatalBOLTError(
"BOLT-ERROR: updating veneer call destination failed\n");
}
BC.MIB->replaceBranchTarget(Instr, VeneerDestinations[TargetSymbol],
BC.Ctx.get());
}
}
}
Expand Down
113 changes: 113 additions & 0 deletions bolt/lib/Rewrite/BuildIDRewriter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//===- bolt/Rewrite/BuildIDRewriter.cpp -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Read and update build ID stored in ELF note section.
//
//===----------------------------------------------------------------------===//

#include "bolt/Rewrite/MetadataRewriter.h"
#include "bolt/Rewrite/MetadataRewriters.h"
#include "llvm/Support/Errc.h"

using namespace llvm;
using namespace bolt;

namespace {

/// The build-id is typically a stream of 20 bytes. Return these bytes in
/// printable hexadecimal form.
std::string getPrintableBuildID(StringRef BuildID) {
std::string Str;
raw_string_ostream OS(Str);
for (const char &Char : BuildID)
OS << format("%.2x", static_cast<unsigned char>(Char));

return OS.str();
}

class BuildIDRewriter final : public MetadataRewriter {

/// Information about binary build ID.
ErrorOr<BinarySection &> BuildIDSection{std::errc::bad_address};
StringRef BuildID;
std::optional<uint64_t> BuildIDOffset;
std::optional<uint64_t> BuildIDSize;

public:
BuildIDRewriter(StringRef Name, BinaryContext &BC)
: MetadataRewriter(Name, BC) {}

Error sectionInitializer() override;

Error postEmitFinalizer() override;
};

Error BuildIDRewriter::sectionInitializer() {
// Typically, build ID will reside in .note.gnu.build-id section. Howerver,
// a linker script can change the section name and such is the case with
// the Linux kernel. Hence, we iterate over all note sections.
for (BinarySection &NoteSection : BC.sections()) {
if (!NoteSection.isNote())
continue;

StringRef Buf = NoteSection.getContents();
DataExtractor DE = DataExtractor(Buf, BC.AsmInfo->isLittleEndian(),
BC.AsmInfo->getCodePointerSize());
DataExtractor::Cursor Cursor(0);
while (Cursor && !DE.eof(Cursor)) {
const uint32_t NameSz = DE.getU32(Cursor);
const uint32_t DescSz = DE.getU32(Cursor);
const uint32_t Type = DE.getU32(Cursor);

StringRef Name =
NameSz ? Buf.slice(Cursor.tell(), Cursor.tell() + NameSz) : "<empty>";
Cursor.seek(alignTo(Cursor.tell() + NameSz, 4));

const uint64_t DescOffset = Cursor.tell();
StringRef Desc =
DescSz ? Buf.slice(DescOffset, DescOffset + DescSz) : "<empty>";
Cursor.seek(alignTo(DescOffset + DescSz, 4));

if (!Cursor)
return createStringError(errc::executable_format_error,
"out of bounds while reading note section: %s",
toString(Cursor.takeError()).c_str());

if (Type == ELF::NT_GNU_BUILD_ID && Name.substr(0, 3) == "GNU" &&
DescSz) {
BuildIDSection = NoteSection;
BuildID = Desc;
BC.setFileBuildID(getPrintableBuildID(Desc));
BuildIDOffset = DescOffset;
BuildIDSize = DescSz;

return Error::success();
}
}
}

return Error::success();
}

Error BuildIDRewriter::postEmitFinalizer() {
if (!BuildIDSection || !BuildIDOffset)
return Error::success();

const uint8_t LastByte = BuildID[BuildID.size() - 1];
SmallVector<char, 1> Patch = {static_cast<char>(LastByte ^ 1)};
BuildIDSection->addPatch(*BuildIDOffset + BuildID.size() - 1, Patch);
BC.outs() << "BOLT-INFO: patched build-id (flipped last bit)\n";

return Error::success();
}
} // namespace

std::unique_ptr<MetadataRewriter>
llvm::bolt::createBuildIDRewriter(BinaryContext &BC) {
return std::make_unique<BuildIDRewriter>("build-id-rewriter", BC);
}
1 change: 1 addition & 0 deletions bolt/lib/Rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_llvm_library(LLVMBOLTRewrite
LinuxKernelRewriter.cpp
MachORewriteInstance.cpp
MetadataManager.cpp
BuildIDRewriter.cpp
PseudoProbeRewriter.cpp
RewriteInstance.cpp
SDTRewriter.cpp
Expand Down
12 changes: 12 additions & 0 deletions bolt/lib/Rewrite/MetadataManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ void MetadataManager::registerRewriter(
Rewriters.emplace_back(std::move(Rewriter));
}

void MetadataManager::runSectionInitializers() {
for (auto &Rewriter : Rewriters) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invoking " << Rewriter->getName()
<< " after reading sections\n");
if (Error E = Rewriter->sectionInitializer()) {
errs() << "BOLT-ERROR: while running " << Rewriter->getName()
<< " after reading sections: " << toString(std::move(E)) << '\n';
exit(1);
}
}
}

void MetadataManager::runInitializersPreCFG() {
for (auto &Rewriter : Rewriters) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invoking " << Rewriter->getName()
Expand Down
92 changes: 8 additions & 84 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,82 +643,6 @@ Error RewriteInstance::discoverStorage() {
return Error::success();
}

void RewriteInstance::parseBuildID() {
if (!BuildIDSection)
return;

StringRef Buf = BuildIDSection->getContents();

// Reading notes section (see Portable Formats Specification, Version 1.1,
// pg 2-5, section "Note Section").
DataExtractor DE =
DataExtractor(Buf,
/*IsLittleEndian=*/true, InputFile->getBytesInAddress());
uint64_t Offset = 0;
if (!DE.isValidOffset(Offset))
return;
uint32_t NameSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return;
uint32_t DescSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return;
uint32_t Type = DE.getU32(&Offset);

LLVM_DEBUG(dbgs() << "NameSz = " << NameSz << "; DescSz = " << DescSz
<< "; Type = " << Type << "\n");

// Type 3 is a GNU build-id note section
if (Type != 3)
return;

StringRef Name = Buf.slice(Offset, Offset + NameSz);
Offset = alignTo(Offset + NameSz, 4);
if (Name.substr(0, 3) != "GNU")
return;

BuildID = Buf.slice(Offset, Offset + DescSz);
}

std::optional<std::string> RewriteInstance::getPrintableBuildID() const {
if (BuildID.empty())
return std::nullopt;

std::string Str;
raw_string_ostream OS(Str);
const unsigned char *CharIter = BuildID.bytes_begin();
while (CharIter != BuildID.bytes_end()) {
if (*CharIter < 0x10)
OS << "0";
OS << Twine::utohexstr(*CharIter);
++CharIter;
}
return OS.str();
}

void RewriteInstance::patchBuildID() {
raw_fd_ostream &OS = Out->os();

if (BuildID.empty())
return;

size_t IDOffset = BuildIDSection->getContents().rfind(BuildID);
assert(IDOffset != StringRef::npos && "failed to patch build-id");

uint64_t FileOffset = getFileOffsetForAddress(BuildIDSection->getAddress());
if (!FileOffset) {
BC->errs()
<< "BOLT-WARNING: Non-allocatable build-id will not be updated.\n";
return;
}

char LastIDByte = BuildID[BuildID.size() - 1];
LastIDByte ^= 1;
OS.pwrite(&LastIDByte, 1, FileOffset + IDOffset + BuildID.size() - 1);

BC->outs() << "BOLT-INFO: patched build-id (flipped last bit)\n";
}

Error RewriteInstance::run() {
assert(BC && "failed to create a binary context");

Expand Down Expand Up @@ -1977,7 +1901,6 @@ Error RewriteInstance::readSpecialSections() {
".rela" + std::string(BC->getMainCodeSectionName()));
HasSymbolTable = (bool)BC->getUniqueSectionByName(".symtab");
EHFrameSection = BC->getUniqueSectionByName(".eh_frame");
BuildIDSection = BC->getUniqueSectionByName(".note.gnu.build-id");

if (ErrorOr<BinarySection &> BATSec =
BC->getUniqueSectionByName(BoltAddressTranslation::SECTION_NAME)) {
Expand Down Expand Up @@ -2035,10 +1958,7 @@ Error RewriteInstance::readSpecialSections() {
report_error("expected valid eh_frame section", EHFrameOrError.takeError());
CFIRdWrt.reset(new CFIReaderWriter(*BC, *EHFrameOrError.get()));

// Parse build-id
parseBuildID();
if (std::optional<std::string> FileBuildID = getPrintableBuildID())
BC->setFileBuildID(*FileBuildID);
processSectionMetadata();

// Read .dynamic/PT_DYNAMIC.
return readELFDynamic();
Expand Down Expand Up @@ -3218,14 +3138,20 @@ void RewriteInstance::initializeMetadataManager() {
if (BC->IsLinuxKernel)
MetadataManager.registerRewriter(createLinuxKernelRewriter(*BC));

MetadataManager.registerRewriter(createBuildIDRewriter(*BC));

MetadataManager.registerRewriter(createPseudoProbeRewriter(*BC));

MetadataManager.registerRewriter(createSDTRewriter(*BC));
}

void RewriteInstance::processMetadataPreCFG() {
void RewriteInstance::processSectionMetadata() {
initializeMetadataManager();

MetadataManager.runSectionInitializers();
}

void RewriteInstance::processMetadataPreCFG() {
MetadataManager.runInitializersPreCFG();

processProfileDataPreCFG();
Expand Down Expand Up @@ -5772,8 +5698,6 @@ void RewriteInstance::rewriteFile() {
// Update symbol tables.
patchELFSymTabs();

patchBuildID();

if (opts::EnableBAT)
encodeBATSection();

Expand Down
7 changes: 3 additions & 4 deletions bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
return getTargetAddend(Op.getExpr());
}

bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
void replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) &&
"Invalid instruction");
Expand All @@ -638,7 +638,6 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {

*OI = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}

/// Matches indirect branch patterns in AArch64 related to a jump table (JT),
Expand Down Expand Up @@ -969,7 +968,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
}
}

bool reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
void reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
if (isTB(Inst) || isCB(Inst)) {
Inst.setOpcode(getInvertedBranchOpcode(Inst.getOpcode()));
Expand All @@ -984,7 +983,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
LLVM_DEBUG(Inst.dump());
llvm_unreachable("Unrecognized branch instruction");
}
return replaceBranchTarget(Inst, TBB, Ctx);
replaceBranchTarget(Inst, TBB, Ctx);
}

int getPCRelEncodingSize(const MCInst &Inst) const override {
Expand Down
7 changes: 3 additions & 4 deletions bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,14 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
}
}

bool reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
void reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
auto Opcode = getInvertedBranchOpcode(Inst.getOpcode());
Inst.setOpcode(Opcode);
return replaceBranchTarget(Inst, TBB, Ctx);
replaceBranchTarget(Inst, TBB, Ctx);
}

bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
void replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) &&
"Invalid instruction");
Expand All @@ -170,7 +170,6 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {

Inst.getOperand(SymOpIndex) = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}

IndirectBranchType analyzeIndirectBranch(
Expand Down
6 changes: 2 additions & 4 deletions bolt/lib/Target/X86/X86MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2794,14 +2794,13 @@ class X86MCPlusBuilder : public MCPlusBuilder {
Inst.addOperand(MCOperand::createImm(CC));
}

bool reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
void reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
unsigned InvCC = getInvertedCondCode(getCondCode(Inst));
assert(InvCC != X86::COND_INVALID && "invalid branch instruction");
Inst.getOperand(Info->get(Inst.getOpcode()).NumOperands - 1).setImm(InvCC);
Inst.getOperand(0) = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}

bool replaceBranchCondition(MCInst &Inst, const MCSymbol *TBB, MCContext *Ctx,
Expand Down Expand Up @@ -2844,13 +2843,12 @@ class X86MCPlusBuilder : public MCPlusBuilder {
}
}

bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
void replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) &&
"Invalid instruction");
Inst.getOperand(0) = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}

MCPhysReg getX86R11() const override { return X86::R11; }
Expand Down
10 changes: 0 additions & 10 deletions bolt/test/Inputs/lsda.ldscript

This file was deleted.

710 changes: 710 additions & 0 deletions bolt/test/X86/Inputs/dwarf4-df-input-lowpc-ranges-other.s

Large diffs are not rendered by default.

753 changes: 753 additions & 0 deletions bolt/test/X86/Inputs/dwarf5-df-input-lowpc-ranges-other.s

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions bolt/test/X86/dwarf4-df-input-lowpc-ranges-cus.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
; RUN: rm -rf %t
; RUN: mkdir %t
; RUN: cd %t
; RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf4-df-input-lowpc-ranges-main.s \
; RUN: -split-dwarf-file=main.dwo -o main.o
; RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf4-df-input-lowpc-ranges-other.s \
; RUN: -split-dwarf-file=mainOther.dwo -o other.o
; RUN: %clang %cflags -gdwarf-4 -gsplit-dwarf=split main.o other.o -o main.exe
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections
; RUN: llvm-dwarfdump --show-form --verbose --debug-ranges main.exe.bolt &> %t/foo.txt
; RUN: llvm-dwarfdump --show-form --verbose --debug-info main.exe.bolt >> %t/foo.txt
; RUN: cat %t/foo.txt | FileCheck -check-prefix=BOLT %s
; RUN: not llvm-dwarfdump --show-form --verbose --debug-info main.dwo.dwo mainOther.dwo.dwo &> %t/mainddwodwo.txt
; RUN: cat %t/mainddwodwo.txt | FileCheck -check-prefix=BOLT-DWO-MAIN %s

;; Tests that BOLT correctly handles Skeleton CU which has DW_AT_low_pc/DW_AT_ranges as input and handles multiple CUs with ranges.

; BOLT: .debug_ranges
; BOLT-NEXT: 00000000 <End of list>
; BOLT-NEXT: 00000010
; BOLT-NEXT: 00000010
; BOLT-NEXT: 00000010
; BOLT-NEXT: 00000010 <End of list>
; BOLT-NEXT: 00000050
; BOLT-NEXT: 00000050
; BOLT-NEXT: 00000050
; BOLT-NEXT: 00000050 <End of list>
; BOLT-NEXT: 00000090 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR4:]] [[#%.16x,ADDRB4:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]]
; BOLT-NEXT: 00000090 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]]
; BOLT-NEXT: 00000090 <End of list>
; BOLT-NEXT: 00000110
; BOLT-NEXT: 00000110
; BOLT-NEXT: 00000110
; BOLT-NEXT: 00000110 <End of list>
; BOLT-NEXT: 00000150
; BOLT-NEXT: 00000150
; BOLT-NEXT: 00000150
; BOLT-NEXT: 00000150 <End of list>
; BOLT-NEXT: 00000190 [[#%.16x,ADDR8:]] [[#%.16x,ADDRB8:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR9:]] [[#%.16x,ADDRB9:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR10:]] [[#%.16x,ADDRB10:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR11:]] [[#%.16x,ADDRB11:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR12:]] [[#%.16x,ADDRB12:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR13:]] [[#%.16x,ADDRB13:]]
; BOLT-NEXT: 00000190 [[#%.16x,ADDR14:]] [[#%.16x,ADDRB14:]]
; BOLT-NEXT: 00000190 <End of list>

; BOLT: DW_TAG_compile_unit
; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x{{[0-9a-fA-F]+}}] = "main.dwo.dwo")
; BOLT-NEXT: DW_AT_GNU_dwo_id
; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000010)
; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000090
; BOLT-NEXT: [0x[[#ADDR1]], 0x[[#ADDRB1]])
; BOLT-NEXT: [0x[[#ADDR2]], 0x[[#ADDRB2]])
; BOLT-NEXT: [0x[[#ADDR3]], 0x[[#ADDRB3]])
; BOLT-NEXT: [0x[[#ADDR4]], 0x[[#ADDRB4]])
; BOLT-NEXT: [0x[[#ADDR5]], 0x[[#ADDRB5]])
; BOLT-NEXT: [0x[[#ADDR6]], 0x[[#ADDRB6]])
; BOLT-NEXT: [0x[[#ADDR7]], 0x[[#ADDRB7]])
; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000000)

; BOLT: DW_TAG_compile_unit
; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x{{[0-9a-fA-F]+}}] = "mainOther.dwo.dwo")
; BOLT-NEXT: DW_AT_GNU_dwo_id
; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000110)
; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000190
; BOLT-NEXT: [0x[[#ADDR8]], 0x[[#ADDRB8]])
; BOLT-NEXT: [0x[[#ADDR9]], 0x[[#ADDRB9]])
; BOLT-NEXT: [0x[[#ADDR10]], 0x[[#ADDRB10]])
; BOLT-NEXT: [0x[[#ADDR11]], 0x[[#ADDRB11]])
; BOLT-NEXT: [0x[[#ADDR12]], 0x[[#ADDRB12]])
; BOLT-NEXT: [0x[[#ADDR13]], 0x[[#ADDRB13]])
; BOLT-NEXT: [0x[[#ADDR14]], 0x[[#ADDRB14]])
; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000018)

; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040

; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040
708 changes: 708 additions & 0 deletions bolt/test/X86/dwarf5-debug-names-skip-forward-decl.s

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions bolt/test/X86/dwarf5-df-input-lowpc-ranges-cus.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
; 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-input-lowpc-ranges-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-input-lowpc-ranges-other.s \
; RUN: -split-dwarf-file=mainOther.dwo -o other.o
; RUN: %clang %cflags main.o other.o -o main.exe
; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections
; RUN: llvm-dwarfdump --show-form --verbose --debug-rnglists main.exe.bolt &> %t/foo.txt
; RUN: llvm-dwarfdump --show-form --verbose --debug-addr main.exe.bolt >> %t/foo.txt
; RUN: llvm-dwarfdump --show-form --verbose --debug-info main.exe.bolt >> %t/foo.txt
; RUN: cat %t/foo.txt | FileCheck -check-prefix=BOLT %s
; RUN: llvm-dwarfdump --show-form --verbose --debug-info main.dwo.dwo mainOther.dwo.dwo &> %t/mainddwodwo.txt
; RUN: cat %t/mainddwodwo.txt | FileCheck -check-prefix=BOLT-DWO-MAIN %s

;; Tests that BOLT correctly handles Skeleton CU which has DW_AT_low_pc/DW_AT_ranges as input and handles multiple CUs with ranges.

; BOLT: Addrs: [
; BOLT-NEXT: 0x[[#%.16x,ADDR1:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR2:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR3:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR4:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR5:]]

; BOLT: Addrs: [
; BOLT-NEXT: 0x[[#%.16x,ADDR6:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR7:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR8:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR9:]]
; BOLT-NEXT: 0x[[#%.16x,ADDR10:]]

; BOLT: DW_TAG_skeleton_unit
; BOLT: DW_AT_dwo_name [DW_FORM_strx1] (indexed (00000001) string = "main.dwo.dwo")
; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
; BOLT-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x00000010
; BOLT-NEXT: [0x[[#ADDR1]], 0x[[#ADDR1 + 0x16]])
; BOLT-NEXT: [0x[[#ADDR1 + 0x16]], 0x[[#ADDR1 + 0x24]])
; BOLT-NEXT: [0x[[#ADDR1 + 0x24]], 0x[[#ADDR1 + 0x29]])
; BOLT-NEXT: [0x[[#ADDR1 + 0x30]], 0x[[#ADDR1 + 0x46]])
; BOLT-NEXT: [0x[[#ADDR1 + 0x50]], 0x[[#ADDR1 + 0x77]])
; BOLT-NEXT: [0x[[#ADDR1 + 0x77]], 0x[[#ADDR1 + 0x85]])
; BOLT-NEXT: [0x[[#ADDR1 + 0x85]], 0x[[#ADDR1 + 0x9f]])
; BOLT-NEXT: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
; BOLT-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c)

; BOLT: DW_TAG_skeleton_unit
; BOLT: DW_AT_dwo_name [DW_FORM_strx1] (indexed (00000001) string = "mainOther.dwo.dwo")
; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
; BOLT-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x0000003b
; BOLT-NEXT: [0x[[#ADDR6]], 0x[[#ADDR6 + 0x16]])
; BOLT-NEXT: [0x[[#ADDR6 + 0x16]], 0x[[#ADDR6 + 0x24]])
; BOLT-NEXT: [0x[[#ADDR6 + 0x24]], 0x[[#ADDR6 + 0x29]])
; BOLT-NEXT: [0x[[#ADDR6 + 0x30]], 0x[[#ADDR6 + 0x46]])
; BOLT-NEXT: [0x[[#ADDR6 + 0x50]], 0x[[#ADDR6 + 0x70]])
; BOLT-NEXT: [0x[[#ADDR6 + 0x70]], 0x[[#ADDR6 + 0x7e]])
; BOLT-NEXT: [0x[[#ADDR6 + 0x7e]], 0x[[#ADDR6 + 0x98]])
; BOLT-NEXT: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000038)
; BOLT-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x00000037)

; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x00000014
; BOLT-DWO-MAIN-NEXT: [0x0000000000000000, 0x0000000000000016)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000016, 0x0000000000000024)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000024, 0x0000000000000029))
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000020
; BOLT-DWO-MAIN-NEXT: [0x0000000000000002, 0x0000000000000029)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000029, 0x0000000000000037)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000037, 0x0000000000000051))

; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x00000014
; BOLT-DWO-MAIN-NEXT: [0x0000000000000000, 0x0000000000000016)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000016, 0x0000000000000024)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000024, 0x0000000000000029))
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN: DW_TAG_subprogram
; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000020
; BOLT-DWO-MAIN-NEXT: [0x0000000000000002, 0x0000000000000022)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000022, 0x0000000000000030)
; BOLT-DWO-MAIN-NEXT: [0x0000000000000030, 0x000000000000004a))
63 changes: 63 additions & 0 deletions bolt/test/X86/jt-symbol-disambiguation-4.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
## If the operand references a symbol that differs from the jump table label,
## no reference updating is required even if its target address resides within
## the jump table's range.
## In this test case, consider the second instruction within the main function,
## where the address resulting from 'c + 17' corresponds to one byte beyond the
## address of the .LJTI2_0 jump table label. However, this operand represents
## an offset calculation related to the global variable 'c' and should remain
## unaffected by the jump table.

# REQUIRES: system-linux

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: %clang -no-pie %t.o -o %t.exe -Wl,-q
# RUN: llvm-bolt --funcs=main,foo/1 %t.exe -o %t.exe.bolt --print-normalized \
# RUN: 2>&1 | FileCheck %s

.text
.globl main
.type main,@function
main:
# CHECK: Binary Function "main"
pushq %rbp
movq %rsp, %rbp
movq $-16, %rax
movl c+17(%rax), %edx
# CHECK: movl c+17(%rax), %edx
cmpl $255, %edx
je .LCorrect
movl $1, %eax
popq %rbp
ret
.LCorrect:
movl $0, %eax
popq %rbp
ret

.p2align 4, 0x90
.type foo,@function
foo:
# CHECK: Binary Function "foo
movq $0, %rax
jmpq *.LJTI2_0(,%rax,8)
# CHECK: jmpq *{{.*}} # JUMPTABLE
addl $-36, %eax
.LBB2_2:
addl $-16, %eax
retq
.section .rodata,"a",@progbits
.type c,@object
.data
.globl c
.p2align 4, 0x0
c:
.byte 1
.byte 0xff
.zero 14
.size c, 16
.LJTI2_0:
.quad .LBB2_2
.quad .LBB2_2
.quad .LBB2_2
.quad .LBB2_2

8 changes: 4 additions & 4 deletions bolt/test/lsda-section-name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// disassembled by BOLT.

// RUN: %clang++ %cxxflags -O3 -no-pie -c %s -o %t.o
// RUN: %clang++ %cxxflags -no-pie -fuse-ld=lld %t.o -o %t.exe \
// RUN: -Wl,-q -Wl,--script=%S/Inputs/lsda.ldscript
// RUN: llvm-readelf -SW %t.exe | FileCheck %s
// RUN: llvm-bolt %t.exe -o %t.bolt
// RUN: %clang++ %cxxflags -O3 -no-pie -fuse-ld=lld %t.o -o %t
// RUN: llvm-objcopy --rename-section .gcc_except_table=.gcc_except_table.main %t
// RUN: llvm-readelf -SW %t | FileCheck %s
// RUN: llvm-bolt %t -o %t.bolt

// CHECK: .gcc_except_table.main

Expand Down
1 change: 0 additions & 1 deletion clang-tools-extra/clang-tidy/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ add_clang_library(clangTidyMiscModule
UseAnonymousNamespaceCheck.cpp

LINK_LIBS
clangAnalysis
clangTidy
clangTidyUtils

Expand Down
11 changes: 4 additions & 7 deletions clang-tools-extra/clang-tidy/misc/HeaderIncludeCycleCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,15 @@ class CyclicDependencyCallbacks : public PPCallbacks {
<< FileName;

const bool IsIncludePathValid =
std::all_of(Files.rbegin(), It, [](const Include &Elem) {
std::all_of(Files.rbegin(), It + 1, [](const Include &Elem) {
return !Elem.Name.empty() && Elem.Loc.isValid();
});

if (!IsIncludePathValid)
return;

auto CurrentIt = Files.rbegin();
do {
Check.diag(CurrentIt->Loc, "'%0' included from here", DiagnosticIDs::Note)
<< CurrentIt->Name;
} while (CurrentIt++ != It);
for (const Include &I : llvm::make_range(Files.rbegin(), It + 1))
Check.diag(I.Loc, "'%0' included from here", DiagnosticIDs::Note)
<< I.Name;
}

bool isFileIgnored(StringRef FileName) const {
Expand Down
14 changes: 8 additions & 6 deletions clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,
}

void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {
auto CharPointerType =
hasType(pointerType(pointee(matchers::isSimpleChar())));
Finder->addMatcher(
callExpr(argumentCountAtLeast(1),
hasArgument(0, stringLiteral(isOrdinary())),
callee(functionDecl(unless(cxxMethodDecl()),
matchers::matchesAnyListedName(
StrFormatLikeFunctions))
.bind("func_decl")))
callExpr(
argumentCountAtLeast(1), hasArgument(0, stringLiteral(isOrdinary())),
callee(functionDecl(
unless(cxxMethodDecl()), hasParameter(0, CharPointerType),
matchers::matchesAnyListedName(StrFormatLikeFunctions))
.bind("func_decl")))
.bind("strformat"),
this);
}
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,15 @@ unusedReturnValue(clang::ast_matchers::StatementMatcher MatchedCallExpr) {
}

void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {
auto CharPointerType =
hasType(pointerType(pointee(matchers::isSimpleChar())));
if (!PrintfLikeFunctions.empty())
Finder->addMatcher(
unusedReturnValue(
callExpr(argumentCountAtLeast(1),
hasArgument(0, stringLiteral(isOrdinary())),
callee(functionDecl(unless(cxxMethodDecl()),
hasParameter(0, CharPointerType),
matchers::matchesAnyListedName(
PrintfLikeFunctions))
.bind("func_decl")))
Expand All @@ -113,6 +116,7 @@ void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {
callExpr(argumentCountAtLeast(2),
hasArgument(1, stringLiteral(isOrdinary())),
callee(functionDecl(unless(cxxMethodDecl()),
hasParameter(1, CharPointerType),
matchers::matchesAnyListedName(
FprintfLikeFunctions))
.bind("func_decl")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,14 @@ AST_MATCHER(QualType, isIntegralType) {

AST_MATCHER_P(UserDefinedLiteral, hasLiteral,
clang::ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
if (const Expr *CookedLiteral = Node.getCookedLiteral()) {
const UserDefinedLiteral::LiteralOperatorKind LOK =
Node.getLiteralOperatorKind();
if (LOK == UserDefinedLiteral::LOK_Template ||
LOK == UserDefinedLiteral::LOK_Raw)
return false;

if (const Expr *CookedLiteral = Node.getCookedLiteral())
return InnerMatcher.matches(*CookedLiteral, Finder, Builder);
}
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,35 @@ void RedundantMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {

void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
auto ConstructorMatcher =
cxxConstructExpr(argumentCountIs(0),
hasDeclaration(cxxConstructorDecl(ofClass(cxxRecordDecl(
unless(isTriviallyDefaultConstructible()))))))
cxxConstructExpr(
argumentCountIs(0),
hasDeclaration(cxxConstructorDecl(
ofClass(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))
.bind("class")))))
.bind("construct");

auto HasUnionAsParent = hasParent(recordDecl(isUnion()));

auto HasTypeEqualToConstructorClass = hasType(qualType(
hasCanonicalType(qualType(hasDeclaration(equalsBoundNode("class"))))));

Finder->addMatcher(
cxxConstructorDecl(
unless(isDelegatingConstructor()), ofClass(unless(isUnion())),
forEachConstructorInitializer(
cxxCtorInitializer(withInitializer(ConstructorMatcher),
unless(forField(fieldDecl(
anyOf(hasType(isConstQualified()),
hasParent(recordDecl(isUnion())))))))
cxxCtorInitializer(
withInitializer(ConstructorMatcher),
anyOf(isBaseInitializer(),
forField(fieldDecl(unless(hasType(isConstQualified())),
unless(HasUnionAsParent),
HasTypeEqualToConstructorClass))))
.bind("init")))
.bind("constructor"),
this);

Finder->addMatcher(fieldDecl(hasInClassInitializer(ConstructorMatcher),
unless(hasParent(recordDecl(isUnion()))))
HasTypeEqualToConstructorClass,
unless(HasUnionAsParent))
.bind("field"),
this);
}
Expand Down
8 changes: 5 additions & 3 deletions clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,11 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
assert(ArgsOffset <= NumArgs);
FormatExpr = llvm::dyn_cast<StringLiteral>(
Args[FormatArgOffset]->IgnoreImplicitAsWritten());
assert(FormatExpr);
if (!FormatExpr->isOrdinary())
return; // No wide string support yet
if (!FormatExpr || !FormatExpr->isOrdinary()) {
// Function must have a narrow string literal as its first argument.
conversionNotPossible("first argument is not a narrow string literal");
return;
}
PrintfFormatString = FormatExpr->getString();

// Assume that the output will be approximately the same size as the input,
Expand Down
8 changes: 8 additions & 0 deletions clang-tools-extra/clang-tidy/utils/Matchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isPointerToConst) {
return pointerType(pointee(qualType(isConstQualified())));
}

// Returns QualType matcher for target char type only.
AST_MATCHER(QualType, isSimpleChar) {
const auto ActualType = Node.getTypePtr();
return ActualType &&
(ActualType->isSpecificBuiltinType(BuiltinType::Char_S) ||
ActualType->isSpecificBuiltinType(BuiltinType::Char_U));
}

AST_MATCHER(Expr, hasUnevaluatedContext) {
if (isa<CXXNoexceptExpr>(Node) || isa<RequiresExpr>(Node))
return true;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/FindSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ class DocumentOutline {
if (!MacroName.isValid() || !MacroName.isFileID())
continue;
// All conditions satisfied, add the macro.
if (auto *Tok = AST.getTokens().spelledTokenAt(MacroName))
if (auto *Tok = AST.getTokens().spelledTokenContaining(MacroName))
CurParent = &CurParent->inMacro(
*Tok, SM, AST.getTokens().expansionStartingAt(Tok));
}
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/IncludeCleaner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ collectMacroReferences(ParsedAST &AST) {
for (const auto &[_, Refs] : AST.getMacros().MacroRefs) {
for (const auto &Ref : Refs) {
auto Loc = SM.getComposedLoc(SM.getMainFileID(), Ref.StartOffset);
const auto *Tok = AST.getTokens().spelledTokenAt(Loc);
const auto *Tok = AST.getTokens().spelledTokenContaining(Loc);
if (!Tok)
continue;
auto Macro = locateMacroAt(*Tok, PP);
Expand Down
11 changes: 5 additions & 6 deletions clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,10 @@ class HighlightingsBuilder {
if (!RLoc.isValid())
return;

const auto *RTok = TB.spelledTokenAt(RLoc);
// Handle `>>`. RLoc is always pointing at the right location, just change
// the end to be offset by 1.
// We'll either point at the beginning of `>>`, hence get a proper spelled
// or point in the middle of `>>` hence get no spelled tok.
const auto *RTok = TB.spelledTokenContaining(RLoc);
// Handle `>>`. RLoc is either part of `>>` or a spelled token on its own
// `>`. If it's the former, slice to have length of 1, if latter use the
// token as-is.
if (!RTok || RTok->kind() == tok::greatergreater) {
Position Begin = sourceLocToPosition(SourceMgr, RLoc);
Position End = sourceLocToPosition(SourceMgr, RLoc.getLocWithOffset(1));
Expand Down Expand Up @@ -577,7 +576,7 @@ class HighlightingsBuilder {
return std::nullopt;
// We might have offsets in the main file that don't correspond to any
// spelled tokens.
const auto *Tok = TB.spelledTokenAt(Loc);
const auto *Tok = TB.spelledTokenContaining(Loc);
if (!Tok)
return std::nullopt;
return halfOpenToRange(SourceMgr,
Expand Down
9 changes: 5 additions & 4 deletions clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) {
if (Inc.Resolved.empty())
continue;
auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
const auto *HashTok = AST.getTokens().spelledTokenAt(HashLoc);
const auto *HashTok = AST.getTokens().spelledTokenContaining(HashLoc);
assert(HashTok && "got inclusion at wrong offset");
const auto *IncludeTok = std::next(HashTok);
const auto *FileTok = std::next(IncludeTok);
Expand Down Expand Up @@ -938,7 +938,7 @@ class ReferenceFinder : public index::IndexDataConsumer {
CollectorOpts.CollectMainFileSymbols = true;
for (SourceLocation L : Locs) {
L = SM.getFileLoc(L);
if (const auto *Tok = TB.spelledTokenAt(L))
if (const auto *Tok = TB.spelledTokenContaining(L))
References.push_back(
{*Tok, Roles,
SymbolCollector::getRefContainer(ASTNode.Parent, CollectorOpts)});
Expand Down Expand Up @@ -1216,7 +1216,7 @@ DocumentHighlight toHighlight(const ReferenceFinder::Reference &Ref,
std::optional<DocumentHighlight> toHighlight(SourceLocation Loc,
const syntax::TokenBuffer &TB) {
Loc = TB.sourceManager().getFileLoc(Loc);
if (const auto *Tok = TB.spelledTokenAt(Loc)) {
if (const auto *Tok = TB.spelledTokenContaining(Loc)) {
DocumentHighlight Result;
Result.range = halfOpenToRange(
TB.sourceManager(),
Expand Down Expand Up @@ -1353,7 +1353,8 @@ maybeFindIncludeReferences(ParsedAST &AST, Position Pos,
Loc = SM.getIncludeLoc(SM.getFileID(Loc));

ReferencesResult::Reference Result;
const auto *Token = AST.getTokens().spelledTokenAt(Loc);
const auto *Token = AST.getTokens().spelledTokenContaining(Loc);
assert(Token && "references expected token here");
Result.Loc.range = Range{sourceLocToPosition(SM, Token->location()),
sourceLocToPosition(SM, Token->endLocation())};
Result.Loc.uri = URIMainFile;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/refactor/Rename.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ std::vector<SymbolRange> collectRenameIdentifierRanges(
clangd::Range tokenRangeForLoc(ParsedAST &AST, SourceLocation TokLoc,
const SourceManager &SM,
const LangOptions &LangOpts) {
const auto *Token = AST.getTokens().spelledTokenAt(TokLoc);
const auto *Token = AST.getTokens().spelledTokenContaining(TokLoc);
assert(Token && "rename expects spelled tokens");
clangd::Range Result;
Result.start = sourceLocToPosition(SM, Token->location());
Expand Down
150 changes: 93 additions & 57 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,11 @@ deleteTokensWithKind(const syntax::TokenBuffer &TokBuf, tok::TokenKind Kind,
// looked up in the context containing the function/method.
// FIXME: Drop attributes in function signature.
llvm::Expected<std::string>
getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
const syntax::TokenBuffer &TokBuf,
const HeuristicResolver *Resolver) {
auto &AST = FD->getASTContext();
auto &SM = AST.getSourceManager();
auto TargetContext = findContextForNS(TargetNamespace, FD->getDeclContext());
if (!TargetContext)
return error("define outline: couldn't find a context for target");

llvm::Error Errors = llvm::Error::success();
tooling::Replacements DeclarationCleanups;
Expand Down Expand Up @@ -216,7 +213,7 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
}
const NamedDecl *ND = Ref.Targets.front();
const std::string Qualifier =
getQualification(AST, *TargetContext,
getQualification(AST, TargetContext,
SM.getLocForStartOfFile(SM.getMainFileID()), ND);
if (auto Err = DeclarationCleanups.add(
tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
Expand All @@ -232,7 +229,7 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
if (const auto *Destructor = llvm::dyn_cast<CXXDestructorDecl>(FD)) {
if (auto Err = DeclarationCleanups.add(tooling::Replacement(
SM, Destructor->getLocation(), 0,
getQualification(AST, *TargetContext,
getQualification(AST, TargetContext,
SM.getLocForStartOfFile(SM.getMainFileID()),
Destructor))))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
Expand Down Expand Up @@ -319,29 +316,9 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
}

struct InsertionPoint {
std::string EnclosingNamespace;
const DeclContext *EnclosingNamespace = nullptr;
size_t Offset;
};
// Returns the most natural insertion point for \p QualifiedName in \p Contents.
// This currently cares about only the namespace proximity, but in feature it
// should also try to follow ordering of declarations. For example, if decls
// come in order `foo, bar, baz` then this function should return some point
// between foo and baz for inserting bar.
llvm::Expected<InsertionPoint> getInsertionPoint(llvm::StringRef Contents,
llvm::StringRef QualifiedName,
const LangOptions &LangOpts) {
auto Region = getEligiblePoints(Contents, QualifiedName, LangOpts);

assert(!Region.EligiblePoints.empty());
// FIXME: This selection can be made smarter by looking at the definition
// locations for adjacent decls to Source. Unfortunately pseudo parsing in
// getEligibleRegions only knows about namespace begin/end events so we
// can't match function start/end positions yet.
auto Offset = positionToOffset(Contents, Region.EligiblePoints.back());
if (!Offset)
return Offset.takeError();
return InsertionPoint{Region.EnclosingNamespace, *Offset};
}

// Returns the range that should be deleted from declaration, which always
// contains function body. In addition to that it might contain constructor
Expand Down Expand Up @@ -409,14 +386,9 @@ class DefineOutline : public Tweak {
}

bool prepare(const Selection &Sel) override {
// Bail out if we are not in a header file.
// FIXME: We might want to consider moving method definitions below class
// definition even if we are inside a source file.
if (!isHeaderFile(Sel.AST->getSourceManager().getFilename(Sel.Cursor),
Sel.AST->getLangOpts()))
return false;

SameFile = !isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts());
Source = getSelectedFunction(Sel.ASTSelection.commonAncestor());

// Bail out if the selection is not a in-line function definition.
if (!Source || !Source->doesThisDeclarationHaveABody() ||
Source->isOutOfLine())
Expand All @@ -429,19 +401,24 @@ class DefineOutline : public Tweak {
if (Source->getTemplateSpecializationInfo())
return false;

if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source)) {
// Bail out in templated classes, as it is hard to spell the class name,
// i.e if the template parameter is unnamed.
if (MD->getParent()->isTemplated())
return false;

// The refactoring is meaningless for unnamed classes and definitions
// within unnamed namespaces.
for (const DeclContext *DC = MD->getParent(); DC; DC = DC->getParent()) {
if (auto *ND = llvm::dyn_cast<NamedDecl>(DC)) {
if (ND->getDeclName().isEmpty())
return false;
}
auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source);
if (!MD) {
// Can't outline free-standing functions in the same file.
return !SameFile;
}

// Bail out in templated classes, as it is hard to spell the class name,
// i.e if the template parameter is unnamed.
if (MD->getParent()->isTemplated())
return false;

// The refactoring is meaningless for unnamed classes and namespaces,
// unless we're outlining in the same file
for (const DeclContext *DC = MD->getParent(); DC; DC = DC->getParent()) {
if (auto *ND = llvm::dyn_cast<NamedDecl>(DC)) {
if (ND->getDeclName().isEmpty() &&
(!SameFile || !llvm::dyn_cast<NamespaceDecl>(ND)))
return false;
}
}

Expand All @@ -453,8 +430,8 @@ class DefineOutline : public Tweak {

Expected<Effect> apply(const Selection &Sel) override {
const SourceManager &SM = Sel.AST->getSourceManager();
auto CCFile = getSourceFile(Sel.AST->tuPath(), Sel);

auto CCFile = SameFile ? Sel.AST->tuPath().str()
: getSourceFile(Sel.AST->tuPath(), Sel);
if (!CCFile)
return error("Couldn't find a suitable implementation file.");
assert(Sel.FS && "FS Must be set in apply");
Expand All @@ -464,8 +441,7 @@ class DefineOutline : public Tweak {
if (!Buffer)
return llvm::errorCodeToError(Buffer.getError());
auto Contents = Buffer->get()->getBuffer();
auto InsertionPoint = getInsertionPoint(
Contents, Source->getQualifiedNameAsString(), Sel.AST->getLangOpts());
auto InsertionPoint = getInsertionPoint(Contents, Sel);
if (!InsertionPoint)
return InsertionPoint.takeError();

Expand Down Expand Up @@ -499,17 +475,77 @@ class DefineOutline : public Tweak {
HeaderUpdates = HeaderUpdates.merge(*DelInline);
}

auto HeaderFE = Effect::fileEdit(SM, SM.getMainFileID(), HeaderUpdates);
if (!HeaderFE)
return HeaderFE.takeError();

Effect->ApplyEdits.try_emplace(HeaderFE->first,
std::move(HeaderFE->second));
if (SameFile) {
tooling::Replacements &R = Effect->ApplyEdits[*CCFile].Replacements;
R = R.merge(HeaderUpdates);
} else {
auto HeaderFE = Effect::fileEdit(SM, SM.getMainFileID(), HeaderUpdates);
if (!HeaderFE)
return HeaderFE.takeError();
Effect->ApplyEdits.try_emplace(HeaderFE->first,
std::move(HeaderFE->second));
}
return std::move(*Effect);
}

// Returns the most natural insertion point for \p QualifiedName in \p
// Contents. This currently cares about only the namespace proximity, but in
// feature it should also try to follow ordering of declarations. For example,
// if decls come in order `foo, bar, baz` then this function should return
// some point between foo and baz for inserting bar.
// FIXME: The selection can be made smarter by looking at the definition
// locations for adjacent decls to Source. Unfortunately pseudo parsing in
// getEligibleRegions only knows about namespace begin/end events so we
// can't match function start/end positions yet.
llvm::Expected<InsertionPoint> getInsertionPoint(llvm::StringRef Contents,
const Selection &Sel) {
// If the definition goes to the same file and there is a namespace,
// we should (and, in the case of anonymous namespaces, need to)
// put the definition into the original namespace block.
if (SameFile) {
auto *Klass = Source->getDeclContext()->getOuterLexicalRecordContext();
if (!Klass)
return error("moving to same file not supported for free functions");
const SourceLocation EndLoc = Klass->getBraceRange().getEnd();
const auto &TokBuf = Sel.AST->getTokens();
auto Tokens = TokBuf.expandedTokens();
auto It = llvm::lower_bound(
Tokens, EndLoc, [](const syntax::Token &Tok, SourceLocation EndLoc) {
return Tok.location() < EndLoc;
});
while (It != Tokens.end()) {
if (It->kind() != tok::semi) {
++It;
continue;
}
unsigned Offset = Sel.AST->getSourceManager()
.getDecomposedLoc(It->endLocation())
.second;
return InsertionPoint{Klass->getEnclosingNamespaceContext(), Offset};
}
return error(
"failed to determine insertion location: no end of class found");
}

auto Region = getEligiblePoints(
Contents, Source->getQualifiedNameAsString(), Sel.AST->getLangOpts());

assert(!Region.EligiblePoints.empty());
auto Offset = positionToOffset(Contents, Region.EligiblePoints.back());
if (!Offset)
return Offset.takeError();

auto TargetContext =
findContextForNS(Region.EnclosingNamespace, Source->getDeclContext());
if (!TargetContext)
return error("define outline: couldn't find a context for target");

return InsertionPoint{*TargetContext, *Offset};
}

private:
const FunctionDecl *Source = nullptr;
bool SameFile = false;
};

REGISTER_TWEAK(DefineOutline)
Expand Down
11 changes: 6 additions & 5 deletions clang-tools-extra/clangd/unittests/PreambleTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ TEST(PreamblePatchTest, LocateMacroAtWorks) {
ASSERT_TRUE(AST);

const auto &SM = AST->getSourceManager();
auto *MacroTok = AST->getTokens().spelledTokenAt(
auto *MacroTok = AST->getTokens().spelledTokenContaining(
SM.getComposedLoc(SM.getMainFileID(), Modified.point("use")));
ASSERT_TRUE(MacroTok);

Expand All @@ -441,7 +441,7 @@ TEST(PreamblePatchTest, LocateMacroAtDeletion) {
ASSERT_TRUE(AST);

const auto &SM = AST->getSourceManager();
auto *MacroTok = AST->getTokens().spelledTokenAt(
auto *MacroTok = AST->getTokens().spelledTokenContaining(
SM.getComposedLoc(SM.getMainFileID(), Modified.point()));
ASSERT_TRUE(MacroTok);

Expand Down Expand Up @@ -512,9 +512,10 @@ TEST(PreamblePatchTest, RefsToMacros) {
ExpectedLocations.push_back(referenceRangeIs(R));

for (const auto &P : Modified.points()) {
auto *MacroTok = AST->getTokens().spelledTokenAt(SM.getComposedLoc(
SM.getMainFileID(),
llvm::cantFail(positionToOffset(Modified.code(), P))));
auto *MacroTok =
AST->getTokens().spelledTokenContaining(SM.getComposedLoc(
SM.getMainFileID(),
llvm::cantFail(positionToOffset(Modified.code(), P))));
ASSERT_TRUE(MacroTok);
EXPECT_THAT(findReferences(*AST, P, 0).References,
testing::ElementsAreArray(ExpectedLocations));
Expand Down
10 changes: 7 additions & 3 deletions clang-tools-extra/clangd/unittests/ReplayPeambleTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TokenKinds.h"
Expand All @@ -42,7 +41,11 @@
#include <memory>
#include <vector>

namespace clang::clangd {
namespace clang {

class Module;

namespace clangd {
namespace {
struct Inclusion {
Inclusion(const SourceManager &SM, SourceLocation HashLoc,
Expand Down Expand Up @@ -170,4 +173,5 @@ TEST(ReplayPreambleTest, IncludesAndSkippedFiles) {
}
}
} // namespace
} // namespace clang::clangd
} // namespace clangd
} // namespace clang
16 changes: 15 additions & 1 deletion clang-tools-extra/clangd/unittests/XRefsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2173,6 +2173,11 @@ TEST(FindReferences, WithinAST) {
using $def[[MyTypeD^ef]] = int;
enum MyEnum : $(MyEnum)[[MyTy^peDef]] { };
)cpp",
// UDL
R"cpp(
bool $decl[[operator]]"" _u^dl(unsigned long long value);
bool x = $(x)[[1_udl]];
)cpp",
};
for (const char *Test : Tests)
checkFindRefs(Test);
Expand Down Expand Up @@ -2358,7 +2363,13 @@ TEST(FindReferences, UsedSymbolsFromInclude) {

R"cpp([[#in^clude <vector>]]
std::[[vector]]<int> vec;
)cpp"};
)cpp",

R"cpp(
[[#include ^"udl_header.h"]]
auto x = [[1_b]];
)cpp",
};
for (const char *Test : Tests) {
Annotations T(Test);
auto TU = TestTU::withCode(T.code());
Expand All @@ -2375,6 +2386,9 @@ TEST(FindReferences, UsedSymbolsFromInclude) {
class vector{};
}
)cpp");
TU.AdditionalFiles["udl_header.h"] = guard(R"cpp(
bool operator"" _b(unsigned long long value);
)cpp");
TU.ExtraArgs.push_back("-isystem" + testPath("system"));

auto AST = TU.build();
Expand Down
73 changes: 71 additions & 2 deletions clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,47 @@ TWEAK_TEST(DefineOutline);

TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
FileName = "Test.cpp";
// Not available unless in a header file.
// Not available for free function unless in a header file.
EXPECT_UNAVAILABLE(R"cpp(
[[void [[f^o^o]]() [[{
return;
}]]]])cpp");

// Available in soure file.
EXPECT_AVAILABLE(R"cpp(
struct Foo {
void f^oo() {}
};
)cpp");

// Available within named namespace in source file.
EXPECT_AVAILABLE(R"cpp(
namespace N {
struct Foo {
void f^oo() {}
};
} // namespace N
)cpp");

// Available within anonymous namespace in source file.
EXPECT_AVAILABLE(R"cpp(
namespace {
struct Foo {
void f^oo() {}
};
} // namespace
)cpp");

// Not available for out-of-line method.
EXPECT_UNAVAILABLE(R"cpp(
class Bar {
void baz();
};

[[void [[Bar::[[b^a^z]]]]() [[{
return;
}]]]])cpp");

FileName = "Test.hpp";
// Not available unless function name or fully body is selected.
EXPECT_UNAVAILABLE(R"cpp(
Expand Down Expand Up @@ -100,7 +135,7 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
};
)cpp");

// Not available on definitions within unnamed namespaces
// Not available on definitions in header file within unnamed namespaces
EXPECT_UNAVAILABLE(R"cpp(
namespace {
struct Foo {
Expand Down Expand Up @@ -349,6 +384,40 @@ TEST_F(DefineOutlineTest, ApplyTest) {
}
}

TEST_F(DefineOutlineTest, InCppFile) {
FileName = "Test.cpp";

struct {
llvm::StringRef Test;
llvm::StringRef ExpectedSource;
} Cases[] = {
{
R"cpp(
namespace foo {
namespace {
struct Foo { void ba^r() {} };
struct Bar { void foo(); };
void Bar::foo() {}
}
}
)cpp",
R"cpp(
namespace foo {
namespace {
struct Foo { void bar() ; };void Foo::bar() {}
struct Bar { void foo(); };
void Bar::foo() {}
}
}
)cpp"},
};

for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
EXPECT_EQ(apply(Case.Test, nullptr), Case.ExpectedSource);
}
}

TEST_F(DefineOutlineTest, HandleMacros) {
llvm::StringMap<std::string> EditedFiles;
ExtraFiles["Test.cpp"] = "";
Expand Down
15 changes: 15 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ Changes in existing checks
Additionally, the option `UseHeaderFileExtensions` is removed, so that the
check uses the `HeaderFileExtensions` option unconditionally.

- Improved :doc:`misc-header-include-cycle
<clang-tidy/checks/misc/header-include-cycle>` check by avoiding crash for self
include cycles.

- Improved :doc:`misc-unused-using-decls
<clang-tidy/checks/misc/unused-using-decls>` check by replacing the local
option `HeaderFileExtensions` by the global option of the same name.
Expand Down Expand Up @@ -351,6 +355,11 @@ Changes in existing checks
<clang-tidy/checks/modernize/use-starts-ends-with>` check to also handle
calls to ``compare`` method.

- Improved :doc:`modernize-use-std-print
<clang-tidy/checks/modernize/use-std-print>` check to not crash if the
format string parameter of the function to be replaced is not of the
expected type.

- Improved :doc:`modernize-use-using <clang-tidy/checks/modernize/use-using>`
check by adding support for detection of typedefs declared on function level.

Expand All @@ -371,6 +380,7 @@ Changes in existing checks
- Improved :doc:`readability-container-size-empty
<clang-tidy/checks/readability/container-size-empty>` check to prevent false
positives when utilizing ``size`` or ``length`` methods that accept parameter.
Fixed crash when facing template user defined literals.

- Improved :doc:`readability-duplicate-include
<clang-tidy/checks/readability/duplicate-include>` check by excluding include
Expand Down Expand Up @@ -398,6 +408,11 @@ Changes in existing checks
<clang-tidy/checks/readability/redundant-inline-specifier>` check to properly
emit warnings for static data member with an in-class initializer.

- Improved :doc:`readability-redundant-member-init
<clang-tidy/checks/readability/redundant-member-init>` check to avoid
false-positives when type of the member does not match the type of the
initializer.

- Improved :doc:`readability-static-accessed-through-instance
<clang-tidy/checks/readability/static-accessed-through-instance>` check to
support calls to overloaded operators as base expression and provide fixes to
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// RUN: not clang-tidy %s -checks='-*,misc-header-include-cycle'

#include "header-include-cycle.self.cpp"
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrictMode: true, \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2; bad_format_type_strprintf', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
// RUN: -- -isystem %clang_tidy_headers
// RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \
// RUN: -std=c++20 %s modernize-use-std-format %t -- \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \
// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2; bad_format_type_strprintf', \
// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \
// RUN: modernize-use-std-format.FormatHeader: '<fmt/core.h>' \
// RUN: }}" \
Expand Down Expand Up @@ -50,3 +50,17 @@ std::string A(const std::string &in)
{
return "_" + in;
}

// Issue #92896: Ensure that the check doesn't assert if the argument is
// promoted to something that isn't a string.
struct S {
S(...);
};
std::string bad_format_type_strprintf(const S &, ...);

std::string unsupported_format_parameter_type()
{
// No fixes here because the format parameter of the function called is not a
// string.
return bad_format_type_strprintf("");
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// RUN: %check_clang_tidy -std=c++23 %s modernize-use-std-print %t -- \
// RUN: -config="{CheckOptions: \
// RUN: { \
// RUN: modernize-use-std-print.PrintfLikeFunctions: 'unqualified_printf;::myprintf; mynamespace::myprintf2', \
// RUN: modernize-use-std-print.FprintfLikeFunctions: '::myfprintf; mynamespace::myfprintf2' \
// RUN: modernize-use-std-print.PrintfLikeFunctions: 'unqualified_printf;::myprintf; mynamespace::myprintf2; bad_format_type_printf', \
// RUN: modernize-use-std-print.FprintfLikeFunctions: '::myfprintf; mynamespace::myfprintf2; bad_format_type_fprintf' \
// RUN: } \
// RUN: }" \
// RUN: -- -isystem %clang_tidy_headers
Expand Down Expand Up @@ -86,3 +86,25 @@ void no_name(const std::string &in)
{
"A" + in;
}

int myprintf(const wchar_t *, ...);

void wide_string_not_supported() {
myprintf(L"wide string %s", L"string");
}

// Issue #92896: Ensure that the check doesn't assert if the argument is
// promoted to something that isn't a string.
struct S {
S(...) {}
};
int bad_format_type_printf(const S &, ...);
int bad_format_type_fprintf(FILE *, const S &, ...);

void unsupported_format_parameter_type()
{
// No fixes here because the format parameter of the function called is not a
// string.
bad_format_type_printf("Hello %s", "world");
bad_format_type_fprintf(stderr, "Hello %s", "world");
}
Original file line number Diff line number Diff line change
Expand Up @@ -889,3 +889,9 @@ namespace PR88203 {
// CHECK-FIXES: {{^ }}if (s.empty()) {}{{$}}
}
}

namespace PR94454 {
template <char...>
int operator""_ci() { return 0; }
auto eq = 0_ci == 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,19 @@ struct D7 {

D7<int> d7i;
D7<S> d7s;

struct SS {
SS() = default;
SS(S s) : s(s) {}

S s;
};

struct D8 {
SS ss = S();
};

struct D9 {
D9() : ss(S()) {}
SS ss;
};
21 changes: 11 additions & 10 deletions clang/cmake/caches/CrossWinToARMLinux.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@
# on Windows platform.
#
# NOTE: the build requires a development ARM Linux root filesystem to use
# proper target platform depended library and header files.
# proper target platform depended library and header files:
# - create <full-path-to-clang-configs> directory and put the clang configuration
# file named <TOOLCHAIN_TARGET_TRIPLE>.cfg into it.
# - add the `--sysroot=<path-to-develop-arm-linux-root-fs>` argument into
# this configuration file.
# - add other necessary target depended clang arguments there,
# such as '-mcpu=cortex-a78' & etc.
#
# See more details here: https://clang.llvm.org/docs/UsersManual.html#configuration-files
#
# Configure:
# cmake -G Ninja ^
# -DTOOLCHAIN_TARGET_TRIPLE=armv7-unknown-linux-gnueabihf ^
# -DTOOLCHAIN_TARGET_TRIPLE=aarch64-unknown-linux-gnu ^
# -DCMAKE_INSTALL_PREFIX=../install ^
# -DDEFAULT_SYSROOT=<path-to-develop-arm-linux-root-fs> ^
# -DLLVM_AR=<llvm_obj_root>/bin/llvm-ar[.exe] ^
# -DCLANG_CONFIG_FILE_USER_DIR=<full-path-to-clang-configs> ^
# -DCMAKE_CXX_FLAGS="-D__OPTIMIZE__" ^
# -DREMOTE_TEST_HOST="<hostname>" ^
# -DREMOTE_TEST_USER="<ssh_user_name>" ^
Expand Down Expand Up @@ -43,10 +50,6 @@ get_filename_component(LLVM_PROJECT_DIR
"${CMAKE_CURRENT_LIST_DIR}/../../../"
ABSOLUTE)

if (NOT DEFINED DEFAULT_SYSROOT)
message(WARNING "DEFAULT_SYSROOT must be specified for the cross toolchain build.")
endif()

if (NOT DEFINED LLVM_ENABLE_ASSERTIONS)
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
endif()
Expand Down Expand Up @@ -136,7 +139,6 @@ endif()
set(LLVM_BUILTIN_TARGETS "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")

set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSTEM_NAME "Linux" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSROOT "${DEFAULT_SYSROOT}" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_INSTALL_RPATH "${RUNTIMES_INSTALL_RPATH}" CACHE STRING "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "")
set(BUILTINS_${TOOLCHAIN_TARGET_TRIPLE}_LLVM_CMAKE_DIR "${LLVM_PROJECT_DIR}/llvm/cmake/modules" CACHE PATH "")
Expand All @@ -156,7 +158,6 @@ set(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR ON CACHE BOOL "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LLVM_ENABLE_RUNTIMES "${LLVM_ENABLE_RUNTIMES}" CACHE STRING "")

set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSTEM_NAME "Linux" CACHE STRING "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_SYSROOT "${DEFAULT_SYSROOT}" CACHE STRING "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_INSTALL_RPATH "${RUNTIMES_INSTALL_RPATH}" CACHE STRING "")
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "")

Expand Down
2 changes: 2 additions & 0 deletions clang/cmake/caches/Release.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,6 @@ endif()
# Final Stage Config (stage2)
set_final_stage_var(LLVM_ENABLE_RUNTIMES "${LLVM_RELEASE_ENABLE_RUNTIMES}" STRING)
set_final_stage_var(LLVM_ENABLE_PROJECTS "${LLVM_RELEASE_ENABLE_PROJECTS}" STRING)
set_final_stage_var(CPACK_GENERATOR "TXZ" STRING)
set_final_stage_var(CPACK_ARCHIVE_THREADS "0" STRING)

70 changes: 35 additions & 35 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1799,8 +1799,8 @@ the configuration (without a prefix: ``Auto``).
Never merge functions into a single line.

* ``SFS_InlineOnly`` (in configuration: ``InlineOnly``)
Only merge functions defined inside a class. Same as "inline",
except it does not implies "empty": i.e. top level empty functions
Only merge functions defined inside a class. Same as ``inline``,
except it does not implies ``empty``: i.e. top level empty functions
are not merged either.

.. code-block:: c++
Expand All @@ -1825,7 +1825,7 @@ the configuration (without a prefix: ``Auto``).
}

* ``SFS_Inline`` (in configuration: ``Inline``)
Only merge functions defined inside a class. Implies "empty".
Only merge functions defined inside a class. Implies ``empty``.

.. code-block:: c++

Expand Down Expand Up @@ -2042,7 +2042,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml

AttributeMacros: ['__capability', '__output', '__unused']
AttributeMacros: [__capability, __output, __unused]

.. _BinPackArguments:

Expand Down Expand Up @@ -3802,7 +3802,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml

ForEachMacros: ['RANGES_FOR', 'FOREACH']
ForEachMacros: [RANGES_FOR, FOREACH]

For example: BOOST_FOREACH.

Expand All @@ -3825,7 +3825,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml

IfMacros: ['IF']
IfMacros: [IF]

For example: `KJ_IF_MAYBE
<https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md#maybes>`_
Expand Down Expand Up @@ -4374,7 +4374,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml

JavaImportGroups: ['com.example', 'com', 'org']
JavaImportGroups: [com.example, com, org]


.. code-block:: java
Expand Down Expand Up @@ -4438,7 +4438,7 @@ the configuration (without a prefix: ``Auto``).
VeryLongImportsAreAnnoying,
VeryLongImportsAreAnnoying,
VeryLongImportsAreAnnoying,
} from 'some/module.js'
} from "some/module.js"

false:
import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js"
Expand Down Expand Up @@ -5088,7 +5088,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml

QualifierOrder: ['inline', 'static', 'type', 'const']
QualifierOrder: [inline, static, type, const]


.. code-block:: c++
Expand Down Expand Up @@ -5117,16 +5117,16 @@ the configuration (without a prefix: ``Auto``).

.. note::

it MUST contain 'type'.
It **must** contain ``type``.

Items to the left of 'type' will be placed to the left of the type and
aligned in the order supplied. Items to the right of 'type' will be
Items to the left of ``type`` will be placed to the left of the type and
aligned in the order supplied. Items to the right of ``type`` will be
placed to the right of the type and aligned in the order supplied.


.. code-block:: yaml

QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile' ]
QualifierOrder: [inline, static, type, const, volatile]

.. _RawStringFormats:

Expand All @@ -5138,10 +5138,10 @@ the configuration (without a prefix: ``Auto``).
name will be reformatted assuming the specified language based on the
style for that language defined in the .clang-format file. If no style has
been defined in the .clang-format file for the specific language, a
predefined style given by 'BasedOnStyle' is used. If 'BasedOnStyle' is not
found, the formatting is based on llvm style. A matching delimiter takes
precedence over a matching enclosing function name for determining the
language of the raw string contents.
predefined style given by ``BasedOnStyle`` is used. If ``BasedOnStyle`` is
not found, the formatting is based on ``LLVM`` style. A matching delimiter
takes precedence over a matching enclosing function name for determining
the language of the raw string contents.

If a canonical delimiter is specified, occurrences of other delimiters for
the same language will be updated to the canonical if possible.
Expand All @@ -5156,17 +5156,17 @@ the configuration (without a prefix: ``Auto``).
RawStringFormats:
- Language: TextProto
Delimiters:
- 'pb'
- 'proto'
- pb
- proto
EnclosingFunctions:
- 'PARSE_TEXT_PROTO'
- PARSE_TEXT_PROTO
BasedOnStyle: google
- Language: Cpp
Delimiters:
- 'cc'
- 'cpp'
BasedOnStyle: llvm
CanonicalDelimiter: 'cc'
- cc
- cpp
BasedOnStyle: LLVM
CanonicalDelimiter: cc

.. _ReferenceAlignment:

Expand Down Expand Up @@ -5533,7 +5533,7 @@ the configuration (without a prefix: ``Auto``).

This determines the maximum length of short namespaces by counting
unwrapped lines (i.e. containing neither opening nor closing
namespace brace) and makes "FixNamespaceComments" omit adding
namespace brace) and makes ``FixNamespaceComments`` omit adding
end comments for those.

.. code-block:: c++
Expand Down Expand Up @@ -5645,7 +5645,7 @@ the configuration (without a prefix: ``Auto``).

* ``SUD_Lexicographic`` (in configuration: ``Lexicographic``)
Using declarations are sorted in the order defined as follows:
Split the strings by "::" and discard any initial empty strings. Sort
Split the strings by ``::`` and discard any initial empty strings. Sort
the lists of names lexicographically, and within those groups, names are
in case-insensitive lexicographic order.

Expand All @@ -5659,7 +5659,7 @@ the configuration (without a prefix: ``Auto``).

* ``SUD_LexicographicNumeric`` (in configuration: ``LexicographicNumeric``)
Using declarations are sorted in the order defined as follows:
Split the strings by "::" and discard any initial empty strings. The
Split the strings by ``::`` and discard any initial empty strings. The
last element of each list is a non-namespace name; all others are
namespace names. Sort the lists of names lexicographically, where the
sort order of individual names is that all non-namespace names come
Expand Down Expand Up @@ -5699,7 +5699,7 @@ the configuration (without a prefix: ``Auto``).
.. _SpaceAfterTemplateKeyword:

**SpaceAfterTemplateKeyword** (``Boolean``) :versionbadge:`clang-format 4` :ref:`¶ <SpaceAfterTemplateKeyword>`
If ``true``, a space will be inserted after the 'template' keyword.
If ``true``, a space will be inserted after the ``template`` keyword.

.. code-block:: c++

Expand Down Expand Up @@ -5860,7 +5860,7 @@ the configuration (without a prefix: ``Auto``).

* ``SBPO_NonEmptyParentheses`` (in configuration: ``NonEmptyParentheses``)
Put a space before opening parentheses only if the parentheses are not
empty i.e. '()'
empty.

.. code-block:: c++

Expand Down Expand Up @@ -6245,7 +6245,7 @@ the configuration (without a prefix: ``Auto``).
true: false:
x = ( int32 )y vs. x = (int32)y

* ``bool InEmptyParentheses`` Put a space in parentheses only if the parentheses are empty i.e. '()'
* ``bool InEmptyParentheses`` Insert a space in empty parentheses, i.e. ``()``.

.. code-block:: c++

Expand Down Expand Up @@ -6409,10 +6409,10 @@ the configuration (without a prefix: ``Auto``).
.. code-block:: yaml

TableGenBreakInsideDAGArg: BreakAll
TableGenBreakingDAGArgOperators: ['ins', 'outs']
TableGenBreakingDAGArgOperators: [ins, outs]

makes the line break only occurs inside DAGArgs beginning with the
specified identifiers 'ins' and 'outs'.
specified identifiers ``ins`` and ``outs``.


.. code-block:: c++
Expand Down Expand Up @@ -6450,7 +6450,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml

TypenameMacros: ['STACK_OF', 'LIST']
TypenameMacros: [STACK_OF, LIST]

For example: OpenSSL STACK_OF, BSD LIST_ENTRY.

Expand Down Expand Up @@ -6518,7 +6518,7 @@ the configuration (without a prefix: ``Auto``).

.. code-block:: yaml

WhitespaceSensitiveMacros: ['STRINGIZE', 'PP_STRINGIZE']
WhitespaceSensitiveMacros: [STRINGIZE, PP_STRINGIZE]

For example: BOOST_PP_STRINGIZE

Expand All @@ -6538,7 +6538,7 @@ The goal of the clang-format project is more on the side of supporting a
limited set of styles really well as opposed to supporting every single style
used by a codebase somewhere in the wild. Of course, we do want to support all
major projects and thus have established the following bar for adding style
options. Each new style option must ..
options. Each new style option must:

* be used in a project of significant size (have dozens of contributors)
* have a publicly accessible style guide
Expand Down
24 changes: 24 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4016,6 +4016,30 @@ Note that the `size` argument must be a compile time constant.

Note that this intrinsic cannot yet be called in a ``constexpr`` context.

``__is_bitwise_cloneable``
--------------------------

A type trait is used to check whether a type can be safely copied by memcpy.

**Syntax**:

.. code-block:: c++

bool __is_bitwise_cloneable(Type)

**Description**:

Objects of bitwise cloneable types can be bitwise copied by memcpy/memmove. The
Clang compiler warrants that this behavior is well defined, and won't be
broken by compiler optimizations and sanitizers.

For implicit-lifetime types, the lifetime of the new object is implicitly
started after the copy. For other types (e.g., classes with virtual methods),
the lifetime isn't started, and using the object results in undefined behavior
according to the C++ Standard.

This builtin can be used in constant expressions.

Atomic Min/Max builtins with memory ordering
--------------------------------------------

Expand Down
28 changes: 27 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,16 @@ C++23 Feature Support
- Implemented `P1774R8: Portable assumptions <https://wg21.link/P1774R8>`_.

- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.
Note, the ``-Winvalid-constexpr`` diagnostic is now disabled in C++23 mode,
but can be explicitly specified to retain the old diagnostic checking
behavior.

- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for
`P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_.

- Implemented `P2797R0: Static and explicit object member functions with the same parameter-type-lists <https://wg21.link/P2797R0>`_.
This completes the support for "deducing this".

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -323,6 +329,20 @@ Non-comprehensive list of changes in this release
- Builtins ``__builtin_shufflevector()`` and ``__builtin_convertvector()`` may
now be used within constant expressions.

- When compiling a constexpr function, Clang will check to see whether the
function can *never* be used in a constant expression context and issues a
diagnostic under the ``-Winvalid-constexpr`` diagostic flag (which defaults
to an error). This check can be expensive because the mere presence of a
function marked ``constexpr`` will cause us to undergo constant expression
evaluation, even if the function is not called within the translation unit
being compiled. Due to the expense, Clang no longer checks constexpr function
bodies when the function is defined in a system header file or when
``-Winvalid-constexpr`` is not enabled for the function definition, which
should result in mild compile-time performance improvements.

- Added ``__is_bitwise_cloneable`` which is used to check whether a type
can be safely copied by memcpy/memmove.

- ``#pragma GCC diagnostic warning "-Wfoo"`` can now downgrade ``-Werror=foo``
errors and certain default-to-error ``-W`` diagnostics to warnings.

Expand Down Expand Up @@ -825,6 +845,10 @@ Bug Fixes to C++ Support
- Fix a regression introduced in Clang 18 causing incorrect overload resolution in the presence of functions only
differering by their constraints when only one of these function was variadic.
- Fix a crash when a variable is captured by a block nested inside a lambda. (Fixes #GH93625).
- Fixed a type constraint substitution issue involving a generic lambda expression. (#GH93821)
- Fix a crash caused by improper use of ``__array_extent``. (#GH80474)
- Fixed several bugs in capturing variables within unevaluated contexts. (#GH63845), (#GH67260), (#GH69307),
(#GH88081), (#GH89496), (#GH90669) and (#GH91633).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -886,6 +910,7 @@ Arm and AArch64 Support
* Arm Cortex-A520AE (cortex-a520ae).
* Arm Cortex-A720AE (cortex-a720ae).
* Arm Cortex-R82AE (cortex-r82ae).
* Arm Cortex-R52+ (cortex-r52plus).
* Arm Neoverse-N3 (neoverse-n3).
* Arm Neoverse-V3 (neoverse-v3).
* Arm Neoverse-V3AE (neoverse-v3ae).
Expand Down Expand Up @@ -936,7 +961,7 @@ CUDA/HIP Language Changes

CUDA Support
^^^^^^^^^^^^
- Clang now supports CUDA SDK up to 12.4
- Clang now supports CUDA SDK up to 12.5

AIX Support
^^^^^^^^^^^
Expand Down Expand Up @@ -981,6 +1006,7 @@ AST Matchers
- Fixed ``forEachArgumentWithParam`` and ``forEachArgumentWithParamType`` to
not skip the explicit object parameter for operator calls.
- Fixed captureVars assertion failure if not capturesVariables. (#GH76425)
- ``forCallable`` now properly preserves binding on successful match. (#GH89657)

clang-format
------------
Expand Down
11 changes: 10 additions & 1 deletion clang/docs/UsersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ and ``#pragma clang diagnostic`` are synonyms for Clang. GCC will ignore

The pragma may control any warning that can be used from the command
line. Warnings may be set to ignored, warning, error, or fatal. The
following example code will tell Clang or GCC to ignore the -Wall
following example code will tell Clang or GCC to ignore the ``-Wall``
warnings:

.. code-block:: c
Expand Down Expand Up @@ -1186,6 +1186,15 @@ severity levels. They can be used to change severity of a particular diagnostic
for a region of source file. A notable difference from GCC is that diagnostic
not enabled via command line arguments can't be enabled this way yet.

Some diagnostics associated with a ``-W`` flag have the error severity by
default. They can be ignored or downgraded to warnings:

.. code-block:: cpp

// C only
#pragma GCC diagnostic warning "-Wimplicit-function-declaration"
int main(void) { puts(""); }

In addition to controlling warnings and errors generated by the compiler, it is
possible to generate custom warning and error messages through the following
pragmas:
Expand Down
206 changes: 127 additions & 79 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ Warns when a nullable pointer is returned from a function that has _Nonnull retu
optin
^^^^^

Checkers for portability, performance or coding style specific rules.
Checkers for portability, performance, optional security and coding style specific rules.

.. _optin-core-EnumCastOutOfRange:

Expand Down Expand Up @@ -938,6 +938,53 @@ optin.portability.UnixAPI
"""""""""""""""""""""""""
Finds implementation-defined behavior in UNIX/Posix functions.

.. _optin-taint-TaintedAlloc:

optin.taint.TaintedAlloc (C, C++)
"""""""""""""""""""""""""""""""""

This checker warns for cases when the ``size`` parameter of the ``malloc`` ,
``calloc``, ``realloc``, ``alloca`` or the size parameter of the
array new C++ operator is tainted (potentially attacker controlled).
If an attacker can inject a large value as the size parameter, memory exhaustion
denial of service attack can be carried out.

The ``alpha.security.taint.TaintPropagation`` checker also needs to be enabled for
this checker to give warnings.

The analyzer emits warning only if it cannot prove that the size parameter is
within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
covers the SEI Cert coding standard rule `INT04-C
<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.

You can silence this warning either by bound checking the ``size`` parameter, or
by explicitly marking the ``size`` parameter as sanitized. See the
:ref:`alpha-security-taint-TaintPropagation` checker for more details.

.. code-block:: c

void vulnerable(void) {
size_t size = 0;
scanf("%zu", &size);
int *p = malloc(size); // warn: malloc is called with a tainted (potentially attacker controlled) value
free(p);
}

void not_vulnerable(void) {
size_t size = 0;
scanf("%zu", &size);
if (1024 < size)
return;
int *p = malloc(size); // No warning expected as the the user input is bound
free(p);
}

void vulnerable_cpp(void) {
size_t size = 0;
scanf("%zu", &size);
int *ptr = new int[size];// warn: Memory allocation function is called with a tainted (potentially attacker controlled) value
delete[] ptr;
}

.. _security-checkers:

Expand Down Expand Up @@ -1179,6 +1226,41 @@ security.insecureAPI.DeprecatedOrUnsafeBufferHandling (C)
strncpy(buf, "a", 1); // warn
}

.. _security-putenv-stack-array:

security.PutenvStackArray (C)
"""""""""""""""""""""""""""""
Finds calls to the ``putenv`` function which pass a pointer to a stack-allocated
(automatic) array as the argument. Function ``putenv`` does not copy the passed
string, only a pointer to the data is stored and this data can be read even by
other threads. Content of a stack-allocated array is likely to be overwritten
after exiting from the function.

The problem can be solved by using a static array variable or dynamically
allocated memory. Even better is to avoid using ``putenv`` (it has other
problems related to memory leaks) and use ``setenv`` instead.

The check corresponds to CERT rule
`POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument
<https://wiki.sei.cmu.edu/confluence/display/c/POS34-C.+Do+not+call+putenv%28%29+with+a+pointer+to+an+automatic+variable+as+the+argument>`_.

.. code-block:: c

int f() {
char env[] = "NAME=value";
return putenv(env); // putenv function should not be called with stack-allocated string
}

There is one case where the checker can report a false positive. This is when
the stack-allocated array is used at `putenv` in a function or code branch that
does not return (process is terminated on all execution paths).

Another special case is if the `putenv` is called from function `main`. Here
the stack is deallocated at the end of the program and it should be no problem
to use the stack-allocated string (a multi-threaded program may require more
attention). The checker does not warn for cases when stack space of `main` is
used at the `putenv` call.

security.SetgidSetuidOrder (C)
""""""""""""""""""""""""""""""
When dropping user-level and group-level privileges in a program by using
Expand Down Expand Up @@ -1235,6 +1317,50 @@ Check calls to various UNIX/Posix functions: ``open, pthread_once, calloc, mallo
.. literalinclude:: checkers/unix_api_example.c
:language: c

.. _unix-BlockInCriticalSection:

unix.BlockInCriticalSection (C, C++)
""""""""""""""""""""""""""""""""""""
Check for calls to blocking functions inside a critical section.
Blocking functions detected by this checker: ``sleep, getc, fgets, read, recv``.
Critical section handling functions modeled by this checker:
``lock, unlock, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, mtx_lock, mtx_timedlock, mtx_trylock, mtx_unlock, lock_guard, unique_lock``.

.. code-block:: c

void pthread_lock_example(pthread_mutex_t *m) {
pthread_mutex_lock(m); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
}

.. code-block:: cpp

void overlapping_critical_sections(mtx_t *m1, std::mutex &m2) {
std::lock_guard lg{m2}; // note: entering critical section here
mtx_lock(m1); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
mtx_unlock(m1);
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
// still inside of the critical section of the std::lock_guard
}

**Limitations**

* The ``trylock`` and ``timedlock`` versions of acquiring locks are currently assumed to always succeed.
This can lead to false positives.

.. code-block:: c

void trylock_example(pthread_mutex_t *m) {
if (pthread_mutex_trylock(m) == 0) { // assume trylock always succeeds
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
} else {
sleep(10); // false positive: Incorrect warning about blocking function inside critical section.
}
}

.. _unix-Errno:

unix.Errno (C)
Expand Down Expand Up @@ -2833,41 +2959,6 @@ Warn on mmap() calls that are both writable and executable.
// code
}

.. _alpha-security-putenv-stack-array:

alpha.security.PutenvStackArray (C)
"""""""""""""""""""""""""""""""""""
Finds calls to the ``putenv`` function which pass a pointer to a stack-allocated
(automatic) array as the argument. Function ``putenv`` does not copy the passed
string, only a pointer to the data is stored and this data can be read even by
other threads. Content of a stack-allocated array is likely to be overwritten
after returning from the parent function.

The problem can be solved by using a static array variable or dynamically
allocated memory. Even better is to avoid using ``putenv`` (it has other
problems related to memory leaks) and use ``setenv`` instead.

The check corresponds to CERT rule
`POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument
<https://wiki.sei.cmu.edu/confluence/display/c/POS34-C.+Do+not+call+putenv%28%29+with+a+pointer+to+an+automatic+variable+as+the+argument>`_.

.. code-block:: c

int f() {
char env[] = "NAME=value";
return putenv(env); // putenv function should not be called with stack-allocated string
}

There is one case where the checker can report a false positive. This is when
the stack-allocated array is used at `putenv` in a function or code branch that
does not return (calls `fork` or `exec` like function).

Another special case is if the `putenv` is called from function `main`. Here
the stack is deallocated at the end of the program and it should be no problem
to use the stack-allocated string (a multi-threaded program may require more
attention). The checker does not warn for cases when stack space of `main` is
used at the `putenv` call.

.. _alpha-security-ReturnPtrRange:

alpha.security.ReturnPtrRange (C)
Expand Down Expand Up @@ -3130,49 +3221,6 @@ For a more detailed description of configuration options, please see the
alpha.unix
^^^^^^^^^^

.. _alpha-unix-BlockInCriticalSection:

alpha.unix.BlockInCriticalSection (C)
"""""""""""""""""""""""""""""""""""""
Check for calls to blocking functions inside a critical section.
Blocking functions detected by this checker: ``sleep, getc, fgets, read, recv``.
Critical section handling functions modelled by this checker: ``lock, unlock, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, mtx_lock, mtx_timedlock, mtx_trylock, mtx_unlock, lock_guard, unique_lock``.

.. code-block:: c

void pthread_lock_example(pthread_mutex_t *m) {
pthread_mutex_lock(m); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
}

.. code-block:: cpp

void overlapping_critical_sections(mtx_t *m1, std::mutex &m2) {
std::lock_guard lg{m2}; // note: entering critical section here
mtx_lock(m1); // note: entering critical section here
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
mtx_unlock(m1);
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
// still inside of the critical section of the std::lock_guard
}

**Limitations**

* The ``trylock`` and ``timedlock`` versions of acquiring locks are currently assumed to always succeed.
This can lead to false positives.

.. code-block:: c

void trylock_example(pthread_mutex_t *m) {
if (pthread_mutex_trylock(m) == 0) { // assume trylock always succeeds
sleep(10); // warn: Call to blocking function 'sleep' inside of critical section
pthread_mutex_unlock(m);
} else {
sleep(10); // false positive: Incorrect warning about blocking function inside critical section.
}
}

.. _alpha-unix-Chroot:

alpha.unix.Chroot (C)
Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang-c/Index.h
Original file line number Diff line number Diff line change
Expand Up @@ -2150,7 +2150,11 @@ enum CXCursorKind {
*/
CXCursor_OpenACCComputeConstruct = 320,

CXCursor_LastStmt = CXCursor_OpenACCComputeConstruct,
/** OpenACC Loop Construct.
*/
CXCursor_OpenACCLoopConstruct = 321,

CXCursor_LastStmt = CXCursor_OpenACCLoopConstruct,

/**
* Cursor that represents the translation unit itself.
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/APINotes/APINotesManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H
#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H

#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
Expand All @@ -24,6 +23,7 @@ namespace clang {
class DirectoryEntry;
class FileEntry;
class LangOptions;
class Module;
class SourceManager;

namespace api_notes {
Expand Down
9 changes: 7 additions & 2 deletions clang/include/clang/AST/ASTUnresolvedSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "clang/AST/ASTVector.h"
#include "clang/AST/DeclAccessPair.h"
#include "clang/AST/DeclID.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/Basic/Specifiers.h"
#include <cassert>
Expand Down Expand Up @@ -56,6 +57,10 @@ class ASTUnresolvedSet {
Decls.push_back(DeclAccessPair::make(D, AS), C);
}

void addLazyDecl(ASTContext &C, GlobalDeclID ID, AccessSpecifier AS) {
Decls.push_back(DeclAccessPair::makeLazy(ID.get(), AS), C);
}

/// Replaces the given declaration with the new one, once.
///
/// \return true if the set changed
Expand Down Expand Up @@ -109,10 +114,10 @@ class LazyASTUnresolvedSet {

void reserve(ASTContext &C, unsigned N) { Impl.reserve(C, N); }

void addLazyDecl(ASTContext &C, uintptr_t ID, AccessSpecifier AS) {
void addLazyDecl(ASTContext &C, GlobalDeclID ID, AccessSpecifier AS) {
assert(Impl.empty() || Impl.Decls.isLazy());
Impl.Decls.setLazy(true);
Impl.addDecl(C, reinterpret_cast<NamedDecl *>(ID << 2), AS);
Impl.addLazyDecl(C, ID, AS);
}
};

Expand Down
Loading