249 changes: 183 additions & 66 deletions .github/workflows/release-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ jobs:
if: github.repository == 'llvm/llvm-project'
outputs:
release-version: ${{ steps.vars.outputs.release-version }}
flags: ${{ steps.vars.outputs.flags }}
build-dir: ${{ steps.vars.outputs.build-dir }}
rc-flags: ${{ steps.vars.outputs.rc-flags }}
ref: ${{ steps.vars.outputs.ref }}
upload: ${{ steps.vars.outputs.upload }}

Expand Down Expand Up @@ -85,17 +82,11 @@ jobs:
fi
bash .github/workflows/set-release-binary-outputs.sh "$tag" "$upload"
# Try to get around the 6 hour timeout by first running a job to fill
# the build cache.
fill-cache:
name: "Fill Cache ${{ matrix.os }}"
build-stage1-linux:
name: "Build Stage 1 Linux"
needs: prepare
runs-on: ${{ matrix.os }}
runs-on: ubuntu-22.04
if: github.repository == 'llvm/llvm-project'
strategy:
matrix:
os:
- ubuntu-22.04
steps:
- name: Checkout LLVM
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Expand All @@ -109,81 +100,207 @@ jobs:
uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9
with:
max-size: 250M
key: sccache-${{ matrix.os }}-release
key: sccache-${{ runner.os }}-release
variant: sccache

- name: Build Clang
- name: Build Stage 1 Clang
run: |
cmake -G Ninja -C clang/cmake/caches/Release.cmake -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_POSITION_INDEPENDENT_CODE=ON -S llvm -B build
ninja -v -C build clang
sudo chown $USER:$USER /mnt/
cmake -G Ninja -C clang/cmake/caches/Release.cmake -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -S llvm -B /mnt/build
ninja -v -C /mnt/build
# We need to create an archive of the build directory, because it has too
# many files to upload.
- name: Package Build and Source Directories
run: |
tar -c . | zstd -T0 -c > llvm-project.tar.zst
tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst
build-binaries:
name: ${{ matrix.target.triple }}
permissions:
contents: write # To upload assets to release.
- name: Upload Stage 1 Source
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
with:
name: stage1-source
path: llvm-project.tar.zst
retention-days: 2

- name: Upload Stage 1 Build Dir
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
with:
name: stage1-build
path: build.tar.zst
retention-days: 2

build-stage2-linux:
name: "Build Stage 2 Linux"
needs:
- prepare
- fill-cache
runs-on: ${{ matrix.target.runs-on }}
- build-stage1-linux
runs-on: ubuntu-22.04
if: github.repository == 'llvm/llvm-project'
strategy:
fail-fast: false
matrix:
target:
- triple: x86_64-linux-gnu-ubuntu-22.04
os: ubuntu-22.04
runs-on: ubuntu-22.04-16x64
debian-build-deps: >
chrpath
gcc-multilib
ninja-build
steps:
- name: Checkout LLVM
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Install Ninja
uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main

- name: Download Stage 1 Artifacts
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
ref: ${{ needs.prepare.outputs.ref }}
path: ${{ needs.prepare.outputs.build-dir }}/llvm-project
pattern: stage1-*
merge-multiple: true

- name: Setup sccache
uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9
- name: Unpack Artifacts
run: |
tar --zstd -xf llvm-project.tar.zst
rm llvm-project.tar.zst
sudo chown $USER:$USER /mnt/
tar --zstd -C /mnt -xf build.tar.zst
rm build.tar.zst
- name: Build Stage 2
run: |
ninja -C /mnt/build stage2-instrumented
# We need to create an archive of the build directory, because it has too
# many files to upload.
- name: Save Build and Source Directories
run: |
tar -c . | zstd -T0 -c > llvm-project.tar.zst
tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst
- name: Upload Stage 2 Source
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
with:
max-size: 250M
key: sccache-${{ matrix.target.os }}-release
save: false
variant: sccache
name: stage2-source
path: ${{ github.workspace }}/llvm-project.tar.zst
retention-days: 2

- name: Upload Stage 2 Build Dir
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
with:
name: stage2-build
path: ${{ github.workspace }}/build.tar.zst
retention-days: 2

- name: Install Brew build dependencies
if: matrix.target.brew-build-deps != ''
run: brew install ${{ matrix.target.brew-build-deps }}

- name: Install Debian build dependencies
if: matrix.target.debian-build-deps != ''
run: sudo apt install ${{ matrix.target.debian-build-deps }}
build-stage3-linux:
name: "Build Stage 3 Linux"
needs:
- prepare
- build-stage2-linux
outputs:
filename: ${{ steps.package-info.outputs.release-filename }}
runs-on: ubuntu-22.04-16x64
if: github.repository == 'llvm/llvm-project'
steps:
- name: Install Ninja
uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main

- name: 'Download artifact'
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
pattern: stage2-*
merge-multiple: true

- name: Set macOS build env variables
if: runner.os == 'macOS'
- name: Unpack Artifact
run: |
echo "MACOSX_DEPLOYMENT_TARGET=10.9" >> "$GITHUB_ENV"
tar --zstd -xf llvm-project.tar.zst
rm llvm-project.tar.zst
sudo chown $USER:$USER /mnt/
tar --zstd -C /mnt -xf build.tar.zst
rm build.tar.zst
- name: Build and test release
- name: Build Release Package
run: |
${{ needs.prepare.outputs.build-dir }}/llvm-project/llvm/utils/release/test-release.sh \
${{ needs.prepare.outputs.flags }} \
-triple ${{ matrix.target.triple }} \
-use-ninja \
-no-checkout \
-use-cmake-cache \
-no-test-suite \
-configure-flags "-DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache"
ninja -C /mnt/build stage2-package
- name: Upload binaries
if: ${{ always() && needs.prepare.outputs.upload == 'true' }}
- id: package-info
run: |
filename="LLVM-${{ needs.prepare.outputs.release-version }}-Linux.tar.gz"
echo "filename=$filename" >> $GITHUB_OUTPUT
echo "path=/mnt/build/tools/clang/stage2-bins/$filename" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
if: always()
with:
name: release-binary
path: ${{ steps.package-info.outputs.path }}

# Clean up some build files to reduce size of artifact.
- name: Clean Up Build Directory
run: |
find /mnt/build -iname ${{ steps.package-info.outputs.filename }} -delete
# We need to create an archive of the build directory, because it has too
# many files to upload.
- name: Save Build and Source Directories
run: |
tar -c . | zstd -T0 -c > llvm-project.tar.zst
tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst
- name: Upload Stage 3 Source
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
with:
name: stage3-source
path: llvm-project.tar.zst
retention-days: 2

- name: Upload Stage 3 Build Dir
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0
with:
name: stage3-build
path: build.tar.zst
retention-days: 2

upload-release-binaries-linux:
name: "Upload Linux Release Binaries"
needs:
- prepare
- build-stage3-linux
if : ${{ needs.prepare.outputs.upload == 'true' }}
runs-on: ubuntu-22.04
permissions:
contents: write # For release uploads

steps:
- name: 'Download artifact'
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
name: release-binary

- name: Upload Release
run: |
sudo apt install python3-github
${{ needs.prepare.outputs.build-dir }}/llvm-project/llvm/utils/release/github-upload-release.py \
./llvm-project/llvm/utils/release/github-upload-release.py \
--token ${{ github.token }} \
--release ${{ needs.prepare.outputs.release-version }} \
upload \
--files ${{ needs.prepare.outputs.build-dir }}/clang+llvm-${{ needs.prepare.outputs.release-version }}-${{ matrix.target.triple }}.tar.xz
--files ${{ needs.build-stage3-linux.outputs.release-filename }}
test-stage3-linux:
name: "Test Stage 3 Linux"
needs:
- prepare
- build-stage3-linux
runs-on: ubuntu-22.04
if: github.repository == 'llvm/llvm-project'
steps:
- name: Install Ninja
uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main

- name: 'Download artifact'
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
pattern: stage3-*
merge-multiple: true

- name: Unpack Artifact
run: |
tar --zstd -xf llvm-project.tar.zst
rm llvm-project.tar.zst
sudo chown $USER:$USER /mnt/
tar --zstd -C /mnt -xf build.tar.zst
rm build.tar.zst
- name: Run Tests
run: |
ninja -C /mnt/build stage2-check-all
4 changes: 2 additions & 2 deletions .github/workflows/release-doxygen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ jobs:
pip3 install --user -r ./llvm/docs/requirements.txt
- name: Build Doxygen
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-sphinx
- name: Upload Doxygen
if: env.upload
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" upload --files ./*doxygen*.tar.xz
8 changes: 7 additions & 1 deletion .github/workflows/release-tasks.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Release Task

permissions:
contents: write
contents: read

on:
push:
Expand All @@ -27,6 +27,8 @@ jobs:
release-create:
name: Create a New Release
runs-on: ubuntu-latest
permissions:
contents: write # For creating the release.
needs: validate-tag

steps:
Expand Down Expand Up @@ -55,6 +57,8 @@ jobs:

release-doxygen:
name: Build and Upload Release Doxygen
permissions:
contents: write
needs:
- validate-tag
- release-create
Expand All @@ -72,6 +76,8 @@ jobs:

release-binaries:
name: Build Release Binaries
permissions:
contents: write
needs:
- validate-tag
- release-create
Expand Down
7 changes: 0 additions & 7 deletions .github/workflows/set-release-binary-outputs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ if echo $tag | grep -e '^[0-9a-f]\+$'; then
# This is a plain commit.
# TODO: Don't hardcode this.
release_version="18"
build_dir="$tag"
upload='false'
ref="$tag"
flags="-git-ref $tag -test-asserts"

else

Expand All @@ -30,12 +28,7 @@ else
fi
release_version=`echo "$tag" | sed 's/llvmorg-//g'`
release=`echo "$release_version" | sed 's/-.*//g'`
build_dir=`echo "$release_version" | sed 's,^[^-]\+,final,' | sed 's,[^-]\+-rc\(.\+\),rc\1,'`
rc_flags=`echo "$release_version" | sed 's,^[^-]\+,-final,' | sed 's,[^-]\+-rc\(.\+\),-rc \1 -test-asserts,' | sed 's,--,-,'`
flags="-release $release $rc_flags"
fi
echo "release-version=$release_version" >> $GITHUB_OUTPUT
echo "build-dir=$build_dir" >> $GITHUB_OUTPUT
echo "flags=$flags" >> $GITHUB_OUTPUT
echo "upload=$upload" >> $GITHUB_OUTPUT
echo "ref=$tag" >> $GITHUB_OUTPUT
1,213 changes: 1,213 additions & 0 deletions bolt/docs/CommandLineArgumentReference.md

Large diffs are not rendered by default.

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

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

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

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

bool isStrOffsetsSectionModified() const {
return StrOffsetSectionWasModified;
}

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

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

enum class LocWriterKind { DebugLocWriter, DebugLoclistWriter };
Expand Down
4 changes: 0 additions & 4 deletions bolt/include/bolt/Passes/FrameAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,6 @@ class FrameAnalysis {
std::unique_ptr<StackPointerTracking>>
SPTMap;

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

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

Expand Down
2 changes: 1 addition & 1 deletion bolt/include/bolt/Passes/IndirectCallPromotion.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class IndirectCallPromotion : public BinaryFunctionPass {
struct Location {
MCSymbol *Sym{nullptr};
uint64_t Addr{0};
bool isValid() const { return Sym || (!Sym && Addr != 0); }
bool isValid() const { return Sym || Addr != 0; }
Location() {}
explicit Location(MCSymbol *Sym) : Sym(Sym) {}
explicit Location(uint64_t Addr) : Addr(Addr) {}
Expand Down
15 changes: 6 additions & 9 deletions bolt/include/bolt/Rewrite/DWARFRewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,6 @@ class DWARFRewriter {
DIEValue &HighPCAttrInfo,
std::optional<uint64_t> RangesBase = std::nullopt);

/// Adds a \p Str to .debug_str section.
/// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using
/// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets
/// for this contribution of \p Unit.
void addStringHelper(DIEBuilder &DIEBldr, DIE &Die, const DWARFUnit &Unit,
DIEValue &DIEAttrInfo, StringRef Str);

public:
DWARFRewriter(BinaryContext &BC) : BC(BC) {}

Expand All @@ -210,13 +203,16 @@ class DWARFRewriter {
using OverriddenSectionsMap = std::unordered_map<DWARFSectionKind, StringRef>;
/// Output .dwo files.
void writeDWOFiles(DWARFUnit &, const OverriddenSectionsMap &,
const std::string &, DebugLocWriter &);
const std::string &, DebugLocWriter &,
DebugStrOffsetsWriter &, DebugStrWriter &);
using KnownSectionsEntry = std::pair<MCSection *, DWARFSectionKind>;
struct DWPState {
std::unique_ptr<ToolOutputFile> Out;
std::unique_ptr<BinaryContext> TmpBC;
std::unique_ptr<MCStreamer> Streamer;
std::unique_ptr<DWPStringPool> Strings;
/// Used to store String sections for .dwo files if they are being modified.
std::vector<std::unique_ptr<DebugBufferVector>> StrSections;
const MCObjectFileInfo *MCOFI = nullptr;
const DWARFUnitIndex *CUIndex = nullptr;
std::deque<SmallString<32>> UncompressedSections;
Expand All @@ -237,7 +233,8 @@ class DWARFRewriter {

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

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

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

Error BinaryFunction::disassemble() {
NamedRegionTimer T("disassemble", "Disassemble function", "buildfuncs",
"Build Binary Functions", opts::TimeBuild);
Expand Down Expand Up @@ -3237,12 +3252,9 @@ bool BinaryFunction::validateCFG() const {
if (CurrentState == State::CFG_Finalized)
return true;

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

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

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

return Valid;
return true;
}

void BinaryFunction::fixBranches() {
Expand Down
85 changes: 85 additions & 0 deletions bolt/lib/Core/DIEBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Expand Down
102 changes: 55 additions & 47 deletions bolt/lib/Profile/DataAggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
Expand Down Expand Up @@ -773,9 +774,19 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,

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

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

BinaryFunction *FromFunc = handleAddress(From, /*IsFrom=*/true);
// Ignore returns.
if (IsReturn)
return true;
BinaryFunction *ToFunc = handleAddress(To, /*IsFrom=*/false);
if (!FromFunc && !ToFunc)
return false;
Expand Down Expand Up @@ -1986,7 +2000,7 @@ std::error_code DataAggregator::parseMMapEvents() {
std::pair<StringRef, MMapInfo> FileMMapInfo = FileMMapInfoRes.get();
if (FileMMapInfo.second.PID == -1)
continue;
if (FileMMapInfo.first.equals("(deleted)"))
if (FileMMapInfo.first == "(deleted)")
continue;

// Consider only the first mapping of the file for any given PID
Expand Down Expand Up @@ -2326,7 +2340,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
continue;
BinaryFunction *BF = BC.getBinaryFunctionAtAddress(FuncAddress);
assert(BF);
YamlBF.Name = FuncName.str();
YamlBF.Name = getLocationName(*BF);
YamlBF.Id = BF->getFunctionNumber();
YamlBF.Hash = BAT->getBFHash(FuncAddress);
YamlBF.ExecCount = BF->getKnownExecutionCount();
Expand All @@ -2341,54 +2355,48 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
for (auto BI = BlockMap.begin(), BE = BlockMap.end(); BI != BE; ++BI)
YamlBF.Blocks[BI->second.getBBIndex()].Hash = BI->second.getBBHash();

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

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

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

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

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

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

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

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

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

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

void DWARFRewriter::addStringHelper(DIEBuilder &DIEBldr, DIE &Die,
const DWARFUnit &Unit,
DIEValue &DIEAttrInfo, StringRef Str) {
uint32_t NewOffset = StrWriter->addString(Str);
if (Unit.getVersion() >= 5) {
StrOffstsWriter->updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(),
NewOffset);
return;
}
DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(),
DIEInteger(NewOffset));
Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter,
StrOffstsWriter, StrWriter);
}

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

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

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

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

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

auto updateDWONameCompDir = [&](DWARFUnit &Unit, DIEBuilder &DIEBldr,
DIE &UnitDIE) -> std::string {
DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name);
if (!DWONameAttrInfo)
DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name);
assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU.");
std::string ObjectName;

{
std::lock_guard<std::mutex> Lock(AccessMutex);
ObjectName = getDWOName(Unit, NameToIndexMap);
}
addStringHelper(DIEBldr, UnitDIE, Unit, DWONameAttrInfo,
ObjectName.c_str());

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

if (!opts::DwarfOutputPath.empty()) {
if (!sys::fs::exists(opts::DwarfOutputPath))
sys::fs::create_directory(opts::DwarfOutputPath);
addStringHelper(DIEBldr, UnitDIE, Unit, CompDirAttrInfo,
opts::DwarfOutputPath.c_str());
}
return ObjectName;
};

DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
*StrWriter);
DWPState State;
Expand All @@ -741,9 +673,21 @@ void DWARFRewriter::updateDebugInfo() {
DIEBuilder DWODIEBuilder(BC, &(*SplitCU)->getContext(), DebugNamesTable,
Unit);
DWODIEBuilder.buildDWOUnit(**SplitCU);
std::string DWOName = updateDWONameCompDir(
*Unit, *DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit));

std::string DWOName = "";
std::optional<std::string> DwarfOutputPath =
opts::DwarfOutputPath.empty()
? std::nullopt
: std::optional<std::string>(opts::DwarfOutputPath.c_str());
{
std::lock_guard<std::mutex> Lock(AccessMutex);
DWOName = DIEBlder->updateDWONameCompDir(
*StrOffstsWriter, *StrWriter, *Unit, DwarfOutputPath, std::nullopt);
}
DebugStrOffsetsWriter DWOStrOffstsWriter(BC);
DebugStrWriter DWOStrWriter((*SplitCU)->getContext(), true);
DWODIEBuilder.updateDWONameCompDirForTypes(DWOStrOffstsWriter,
DWOStrWriter, **SplitCU,
DwarfOutputPath, DWOName);
DebugLoclistWriter DebugLocDWoWriter(*Unit, Unit->getVersion(), true);
DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter;
if (Unit->getVersion() >= 5) {
Expand All @@ -761,7 +705,7 @@ void DWARFRewriter::updateDebugInfo() {
TempRangesSectionWriter->finalizeSection();

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

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

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

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

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

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

StringRef Name = DE.getCStr(&Offset);

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

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

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

6 changes: 3 additions & 3 deletions bolt/test/X86/bb-with-two-tail-calls.s
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
# RUN: llvm-strip --strip-unneeded %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib
# RUN: llvm-bolt %t.exe -o %t.out --data %t.fdata --lite=0 --dyno-stats \
# RUN: --print-sctc --print-only=_start 2>&1 | FileCheck %s
# RUN: --print-sctc --print-only=_start -enable-bat 2>&1 | FileCheck %s
# CHECK-NOT: Assertion `BranchInfo.size() == 2 && "could only be called for blocks with 2 successors"' failed.
# Two tail calls in the same basic block after SCTC:
# CHECK: {{.*}}: ja {{.*}} # TAILCALL # CTCTakenCount: {{.*}}
# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL
# CHECK: {{.*}}: ja {{.*}} # TAILCALL # Offset: 7 # CTCTakenCount: 4
# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL # Offset: 12

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

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

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

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

XFAIL: *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// CHECK: BOLT-INFO: using reserved space

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

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

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

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

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

int main() { return 0; }
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap() {
static const HeaderMapCollector::RegexHeaderMap STLPostfixHeaderMap = {
{"include/__stdarg___gnuc_va_list.h$", "<cstdarg>"},
{"include/__stdarg___va_copy.h$", "<cstdarg>"},
{"include/__stdarg_header_macro.h$", "<cstdarg>"},
{"include/__stdarg_va_arg.h$", "<cstdarg>"},
{"include/__stdarg_va_copy.h$", "<cstdarg>"},
{"include/__stdarg_va_list.h$", "<cstdarg>"},
{"include/__stddef_header_macro.h$", "<cstddef>"},
{"include/__stddef_max_align_t.h$", "<cstddef>"},
{"include/__stddef_null.h$", "<cstddef>"},
{"include/__stddef_nullptr_t.h$", "<cstddef>"},
Expand Down
22 changes: 22 additions & 0 deletions clang-tools-extra/clang-query/Query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "Query.h"
#include "QueryParser.h"
#include "QuerySession.h"
#include "clang/AST/ASTDumper.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
Expand Down Expand Up @@ -281,5 +282,26 @@ const QueryKind SetQueryKind<bool>::value;
const QueryKind SetQueryKind<OutputKind>::value;
#endif

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

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

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

} // namespace query
} // namespace clang
18 changes: 17 additions & 1 deletion clang-tools-extra/clang-query/Query.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ enum QueryKind {
QK_SetTraversalKind,
QK_EnableOutputKind,
QK_DisableOutputKind,
QK_Quit
QK_Quit,
QK_File
};

class QuerySession;
Expand Down Expand Up @@ -188,6 +189,21 @@ struct DisableOutputQuery : SetNonExclusiveOutputQuery {
}
};

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

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

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

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

} // namespace query
} // namespace clang

Expand Down
10 changes: 8 additions & 2 deletions clang-tools-extra/clang-query/QueryParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ enum ParsedQueryKind {
PQK_Unlet,
PQK_Quit,
PQK_Enable,
PQK_Disable
PQK_Disable,
PQK_File
};

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

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

case PQK_File:
return new FileQuery(Line);

case PQK_Invalid:
return new InvalidQuery("unknown command: " + CommandStr);
}
Expand Down
18 changes: 2 additions & 16 deletions clang-tools-extra/clang-query/tool/ClangQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,8 @@ static cl::opt<std::string> PreloadFile(

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

using namespace clang::ast_matchers;

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

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

namespace clang::tidy::bugprone {

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

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

return getFailureInfoImpl(
Decl->getName(), isa<TranslationUnitDecl>(Decl->getDeclContext()),
/*IsMacro = */ false, getLangOpts(), Invert, AllowedIdentifiers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,25 @@ namespace clang::tidy::bugprone {

void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
returnStmt(hasReturnValue(declRefExpr(to(parmVarDecl(hasType(
hasCanonicalType(matchers::isReferenceToConst())))))))
returnStmt(
hasReturnValue(declRefExpr(to(parmVarDecl(hasType(hasCanonicalType(
qualType(matchers::isReferenceToConst()).bind("type"))))))),
hasAncestor(functionDecl(hasReturnTypeLoc(
loc(qualType(hasCanonicalType(equalsBoundNode("type"))))))))
.bind("ret"),
this);
}

void ReturnConstRefFromParameterCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
diag(R->getRetValue()->getBeginLoc(),
"returning a constant reference parameter may cause a use-after-free "
"when the parameter is constructed from a temporary");
const SourceRange Range = R->getRetValue()->getSourceRange();
if (Range.isInvalid())
return;
diag(Range.getBegin(),
"returning a constant reference parameter may cause use-after-free "
"when the parameter is constructed from a temporary")
<< Range;
}

} // namespace clang::tidy::bugprone
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,7 @@ void SuspiciousEnumUsageCheck::check(const MatchFinder::MatchResult &Result) {
// Skip when one of the parameters is an empty enum. The
// hasDisjointValueRange function could not decide the values properly in
// case of an empty enum.
if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
if (EnumDec->enumerators().empty() || OtherEnumDec->enumerators().empty())
return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Expand Down
Loading