.ci/generate-buildkite-pipeline-premerge
@@ -191,6 +191,9 @@ function keep-modified-projects() {
}
function check-targets() {
+ # Do not use "check-all" here because if there is "check-all" plus a
+ # project specific target like "check-clang", that project's tests
+ # will be run twice.
projects=${@}
for project in ${projects}; do
case ${project} in
@@ -198,7 +201,7 @@ function check-targets() {
echo "check-clang-tools"
;;
compiler-rt)
- echo "check-all"
+ echo "check-compiler-rt"
;;
cross-project-tests)
echo "check-cross-project"
@@ -216,10 +219,10 @@ function check-targets() {
echo "check-lldb"
;;
pstl)
- echo "check-all"
+ # Currently we do not run pstl tests in CI.
;;
libclc)
- echo "check-all"
+ # Currently there is no testing for libclc.
;;
*)
echo "check-${project}"
.github/CODEOWNERS
@@ -68,7 +68,7 @@
/mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp @MaheshRavishankar @nicolasvasilache
/mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp @hanhanW @nicolasvasilache
/mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp @dcaballe @hanhanW @nicolasvasilache
-/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp @banach-space @dcaballe @hanhanW @nicolasvasilache
+/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp @banach-space @dcaballe @hanhanW @nicolasvasilache @Groverkss
# MemRef Dialect in MLIR.
/mlir/lib/Dialect/MemRef/Transforms/EmulateNarrowType.cpp @MaheshRavishankar @nicolasvasilache
@@ -82,9 +82,9 @@
/mlir/**/*VectorToSCF* @banach-space @dcaballe @matthias-springer @nicolasvasilache
/mlir/**/*VectorToLLVM* @banach-space @dcaballe @nicolasvasilache
/mlir/**/*X86Vector* @aartbik @dcaballe @nicolasvasilache
-/mlir/include/mlir/Dialect/Vector @banach-space @dcaballe @nicolasvasilache
+/mlir/include/mlir/Dialect/Vector @banach-space @dcaballe @nicolasvasilache @Groverkss
/mlir/include/mlir/Dialect/Vector/IR @kuhar
-/mlir/lib/Dialect/Vector @banach-space @dcaballe @nicolasvasilache
+/mlir/lib/Dialect/Vector @banach-space @dcaballe @nicolasvasilache @Groverkss
/mlir/lib/Dialect/Vector/Transforms/* @banach-space @dcaballe @hanhanW @nicolasvasilache
/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp @banach-space @dcaballe @MaheshRavishankar @nicolasvasilache
/mlir/**/*EmulateNarrowType* @dcaballe @hanhanW
.github/new-prs-labeler.yml
@@ -69,7 +69,7 @@ PGO:
- llvm/**/llvm-profdata/**/*
- llvm/**/llvm-profgen/**/*
-vectorization:
+vectorizers:
- llvm/lib/Transforms/Vectorize/**/*
- llvm/include/llvm/Transforms/Vectorize/**/*
@@ -668,7 +668,7 @@ mlgo:
- llvm/lib/CodeGen/ML*
- llvm/unittests/CodeGen/ML*
- llvm/test/CodeGen/MLRegAlloc/**
- - llvm/utils/mlgo-utils/*
+ - llvm/utils/mlgo-utils/**
tools:llvm-exegesis:
- llvm/tools/llvm-exegesis/**
@@ -1008,3 +1008,8 @@ bazel:
offload:
- offload/**
+
+tablegen:
+ - llvm/include/TableGen/**
+ - llvm/lib/TableGen/**
+ - llvm/utils/TableGen/**
.github/workflows/commit-access-review.py
@@ -358,11 +358,10 @@ def main():
gh = github.Github(login_or_token=token)
org = gh.get_organization("llvm")
repo = org.get_repo("llvm-project")
- team = org.get_team_by_slug("llvm-committers")
one_year_ago = datetime.datetime.now() - datetime.timedelta(days=365)
triage_list = {}
- for member in team.get_members():
- triage_list[member.login] = User(member.login, triage_list)
+ for collaborator in repo.get_collaborators(permission="push"):
+ triage_list[collaborator.login] = User(collaborator.login, triage_list)
print("Start:", len(triage_list), "triagers")
# Step 0 Check if users have requested commit access in the last year.
.github/workflows/containers/github-action-ci/stage1.Dockerfile
@@ -2,7 +2,7 @@ FROM docker.io/library/ubuntu:22.04 as base
ENV LLVM_SYSROOT=/opt/llvm
FROM base as stage1-toolchain
-ENV LLVM_VERSION=18.1.8
+ENV LLVM_VERSION=19.1.2
RUN apt-get update && \
apt-get install -y \
.github/workflows/libcxx-build-and-test.yaml
@@ -49,7 +49,8 @@ env:
jobs:
stage1:
if: github.repository_owner == 'llvm'
- runs-on: libcxx-runners-8-set
+ runs-on: libcxx-runners-set
+ container: ghcr.io/libcxx/actions-builder:testing-2024-09-21
continue-on-error: false
strategy:
fail-fast: false
@@ -79,12 +80,14 @@ jobs:
path: |
**/test-results.xml
**/*.abilist
+ **/CMakeConfigureLog.yaml
**/CMakeError.log
**/CMakeOutput.log
**/crash_diagnostics/*
stage2:
if: github.repository_owner == 'llvm'
- runs-on: libcxx-runners-8-set
+ runs-on: libcxx-runners-set
+ container: ghcr.io/libcxx/actions-builder:testing-2024-09-21
needs: [ stage1 ]
continue-on-error: false
strategy:
@@ -123,6 +126,7 @@ jobs:
path: |
**/test-results.xml
**/*.abilist
+ **/CMakeConfigureLog.yaml
**/CMakeError.log
**/CMakeOutput.log
**/crash_diagnostics/*
@@ -155,25 +159,23 @@ jobs:
'generic-no-rtti',
'generic-optimized-speed',
'generic-static',
- # TODO Find a better place for the benchmark and bootstrapping builds to live. They're either very expensive
- # or don't provide much value since the benchmark run results are too noise on the bots.
- 'benchmarks',
'bootstrapping-build'
]
- machine: [ 'libcxx-runners-8-set' ]
+ machine: [ 'libcxx-runners-set' ]
include:
- config: 'generic-cxx26'
- machine: libcxx-runners-8-set
+ machine: libcxx-runners-set
- config: 'generic-asan'
- machine: libcxx-runners-8-set
+ machine: libcxx-runners-set
- config: 'generic-tsan'
- machine: libcxx-runners-8-set
+ machine: libcxx-runners-set
- config: 'generic-ubsan'
- machine: libcxx-runners-8-set
+ machine: libcxx-runners-set
# Use a larger machine for MSAN to avoid timeout and memory allocation issues.
- config: 'generic-msan'
- machine: libcxx-runners-8-set
+ machine: libcxx-runners-set
runs-on: ${{ matrix.machine }}
+ container: ghcr.io/libcxx/actions-builder:testing-2024-09-21
steps:
- uses: actions/checkout@v4
- name: ${{ matrix.config }}
@@ -188,22 +190,30 @@ jobs:
path: |
**/test-results.xml
**/*.abilist
+ **/CMakeConfigureLog.yaml
**/CMakeError.log
**/CMakeOutput.log
**/crash_diagnostics/*
macos:
- runs-on: macos-14
needs: [ stage1 ]
strategy:
- fail-fast: true
+ fail-fast: false
matrix:
- config: [
- generic-cxx03,
- generic-cxx23,
- generic-modules,
- apple-configuration
- ]
+ include:
+ - config: generic-cxx03
+ os: macos-latest
+ - config: generic-cxx23
+ os: macos-latest
+ - config: generic-modules
+ os: macos-latest
+ - config: apple-configuration
+ os: macos-latest
+ - config: apple-system
+ os: macos-13
+ - config: apple-system-hardened
+ os: macos-13
+ runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
@@ -223,6 +233,7 @@ jobs:
path: |
**/test-results.xml
**/*.abilist
+ **/CMakeConfigureLog.yaml
**/CMakeError.log
**/CMakeOutput.log
**/crash_diagnostics/*
@@ -242,6 +253,7 @@ jobs:
- { config: mingw-dll, mingw: true }
- { config: mingw-static, mingw: true }
- { config: mingw-dll-i686, mingw: true }
+ - { config: mingw-incomplete-sysroot, mingw: true }
steps:
- uses: actions/checkout@v4
- name: Install dependencies
@@ -260,6 +272,12 @@ jobs:
del llvm-mingw*.zip
mv llvm-mingw* c:\llvm-mingw
echo "c:\llvm-mingw\bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append
+ - name: Simulate a from-scratch build of llvm-mingw
+ if: ${{ matrix.config == 'mingw-incomplete-sysroot' }}
+ run: |
+ rm -r c:\llvm-mingw\include\c++
+ rm -r c:\llvm-mingw\*-w64-mingw32\lib\libc++*
+ rm -r c:\llvm-mingw\*-w64-mingw32\lib\libunwind*
- name: Add Git Bash to the path
run: |
echo "c:\Program Files\Git\usr\bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append
.github/workflows/release-binaries-all.yml
@@ -43,6 +43,7 @@ on:
- '.github/workflows/release-binaries.yml'
- '.github/workflows/release-binaries-setup-stage/*'
- '.github/workflows/release-binaries-save-stage/*'
+ - 'clang/cmake/caches/Release.cmake'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || 'dispatch' }}
.github/workflows/release-binaries.yml
@@ -328,7 +328,7 @@ jobs:
run: |
# Build some of the mlir tools that take a long time to link
if [ "${{ needs.prepare.outputs.build-flang }}" = "true" ]; then
- ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang-new bbc
+ ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang bbc
fi
ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \
mlir-bytecode-parser-fuzzer \
@@ -420,6 +420,14 @@ jobs:
attestations: write # For artifact attestations
steps:
+ - name: Checkout Release Scripts
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ with:
+ sparse-checkout: |
+ llvm/utils/release/github-upload-release.py
+ llvm/utils/git/requirements.txt
+ sparse-checkout-cone-mode: false
+
- name: 'Download artifact'
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
with:
@@ -442,11 +450,14 @@ jobs:
name: ${{ needs.prepare.outputs.release-binary-filename }}-attestation
path: ${{ needs.prepare.outputs.release-binary-filename }}.jsonl
+ - name: Install Python Requirements
+ run: |
+ pip install --require-hashes -r ./llvm/utils/git/requirements.txt
+
- name: Upload Release
shell: bash
run: |
- sudo apt install python3-github
- ./llvm-project/llvm/utils/release/github-upload-release.py \
+ ./llvm/utils/release/github-upload-release.py \
--token ${{ github.token }} \
--release ${{ needs.prepare.outputs.release-version }} \
upload \
.github/workflows/release-documentation.yml
@@ -72,17 +72,20 @@ jobs:
ref: main
fetch-depth: 0
path: www-releases
+ persist-credentials: false
- name: Upload Release Notes
if: env.upload
env:
- WWW_RELEASES_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }}
+ GH_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }}
run: |
- mkdir -p ../www-releases/${{ inputs.release-version }}
- mv ./docs-build/html-export/* ../www-releases/${{ inputs.release-version }}
- cd ../www-releases
+ mkdir -p www-releases/${{ inputs.release-version }}
+ mv ./docs-build/html-export/* www-releases/${{ inputs.release-version }}
+ cd www-releases
+ git checkout -b ${{ inputs.release-version }}
git add ${{ inputs.release-version }}
git config user.email "llvmbot@llvm.org"
git config user.name "llvmbot"
git commit -a -m "Add ${{ inputs.release-version }} documentation"
- git push "https://$WWW_RELEASES_TOKEN@github.com/${{ github.repository_owner }}/www-releases" main:main
+ git push --force "https://$GH_TOKEN@github.com/llvmbot/www-releases.git" HEAD:refs/heads/${{ inputs.release-version }}
+ gh pr create -f -B main -H ${{ inputs.release-version }} -R llvmbot/www-releases
.gitignore
@@ -51,6 +51,7 @@ autoconf/autom4te.cache
/CMakeSettings.json
# CLion project configuration
/.idea
+/cmake-build*
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
bolt/include/bolt/Core/BinaryBasicBlock.h
@@ -19,6 +19,7 @@
#include "bolt/Core/MCPlus.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/ErrorOr.h"
@@ -818,6 +819,9 @@ public:
return OutputAddressRange;
}
+ uint64_t getOutputStartAddress() const { return OutputAddressRange.first; }
+ uint64_t getOutputEndAddress() const { return OutputAddressRange.second; }
+
bool hasLocSyms() const { return LocSyms != nullptr; }
/// Return mapping of input offsets to symbols in the output.
bolt/include/bolt/Core/BinaryContext.h
@@ -71,14 +71,15 @@ struct SegmentInfo {
uint64_t FileOffset; /// Offset in the file.
uint64_t FileSize; /// Size in file.
uint64_t Alignment; /// Alignment of the segment.
+ bool IsExecutable; /// Is the executable bit set on the Segment?
void print(raw_ostream &OS) const {
- OS << "SegmentInfo { Address: 0x"
- << Twine::utohexstr(Address) << ", Size: 0x"
- << Twine::utohexstr(Size) << ", FileOffset: 0x"
+ OS << "SegmentInfo { Address: 0x" << Twine::utohexstr(Address)
+ << ", Size: 0x" << Twine::utohexstr(Size) << ", FileOffset: 0x"
<< Twine::utohexstr(FileOffset) << ", FileSize: 0x"
<< Twine::utohexstr(FileSize) << ", Alignment: 0x"
- << Twine::utohexstr(Alignment) << "}";
+ << Twine::utohexstr(Alignment) << ", " << (IsExecutable ? "x" : " ")
+ << "}";
};
};
@@ -736,7 +737,7 @@ public:
uint64_t StaleSampleCount{0};
/// the count of exactly matched samples
uint64_t ExactMatchedSampleCount{0};
- /// the count of exactly matched samples
+ /// the count of loosely matched samples
uint64_t LooseMatchedSampleCount{0};
/// the count of exactly pseudo probe matched samples
uint64_t PseudoProbeExactMatchedSampleCount{0};
bolt/include/bolt/Core/BinaryData.h
@@ -226,7 +226,6 @@ inline raw_ostream &operator<<(raw_ostream &OS,
Sep = ",\n ";
TotalCount += AccessInfo.Count;
}
- SS.flush();
OS << TotalCount << " total counts : " << TempString;
return OS;
bolt/include/bolt/Core/BinaryFunction.h
@@ -117,7 +117,6 @@ inline raw_ostream &operator<<(raw_ostream &OS,
TotalCount += CSP.Count;
TotalMispreds += CSP.Mispreds;
}
- SS.flush();
OS << TotalCount << " (" << TotalMispreds << " misses) :" << TempString;
return OS;
@@ -387,6 +386,9 @@ private:
/// Raw branch count for this function in the profile.
uint64_t RawBranchCount{0};
+ /// Dynamically executed function bytes, used for density computation.
+ uint64_t SampleCountInBytes{0};
+
/// Indicates the type of profile the function is using.
uint16_t ProfileFlags{PF_NONE};
@@ -906,6 +908,10 @@ public:
return BB && BB->getOffset() == Offset ? BB : nullptr;
}
+ const BinaryBasicBlock *getBasicBlockAtOffset(uint64_t Offset) const {
+ return const_cast<BinaryFunction *>(this)->getBasicBlockAtOffset(Offset);
+ }
+
/// Retrieve the landing pad BB associated with invoke instruction \p Invoke
/// that is in \p BB. Return nullptr if none exists
BinaryBasicBlock *getLandingPadBBFor(const BinaryBasicBlock &BB,
@@ -1845,6 +1851,9 @@ public:
/// to this function.
void setRawBranchCount(uint64_t Count) { RawBranchCount = Count; }
+ /// Return the number of dynamically executed bytes, from raw perf data.
+ uint64_t getSampleCountInBytes() const { return SampleCountInBytes; }
+
/// Return the execution count for functions with known profile.
/// Return 0 if the function has no profile.
uint64_t getKnownExecutionCount() const {
bolt/include/bolt/Core/DIEBuilder.h
@@ -314,7 +314,7 @@ public:
BC.errs()
<< "BOLT-ERROR: unable to find TypeUnit for Type Unit at offset 0x"
- << DU.getOffset() << "\n";
+ << Twine::utohexstr(DU.getOffset()) << "\n";
return nullptr;
}
bolt/include/bolt/Core/FunctionLayout.h
@@ -123,7 +123,8 @@ public:
const_iterator begin() const;
iterator end();
const_iterator end() const;
- const BinaryBasicBlock *front() const;
+ BinaryBasicBlock *front() const;
+ BinaryBasicBlock *back() const;
friend class FunctionLayout;
};
bolt/include/bolt/Passes/ContinuityStats.h
@@ -0,0 +1,61 @@
+//===- bolt/Passes/ContinuityStats.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass checks how well the BOLT input profile satisfies the following
+// "CFG continuity" property of a perfect profile:
+//
+// Each positive-execution-count block in the function’s CFG
+// should be *reachable* from a positive-execution-count function
+// entry block through a positive-execution-count path.
+//
+// More specifically, for each of the hottest 1000 functions, the pass
+// calculates the function’s fraction of basic block execution counts
+// that is *unreachable*. It then reports the 95th percentile of the
+// distribution of the 1000 unreachable fractions in a single BOLT-INFO line.
+// The smaller the reported value is, the better the BOLT profile
+// satisfies the CFG continuity property.
+
+// The default value of 1000 above can be changed via the hidden BOLT option
+// `-num-functions-for-continuity-check=[N]`.
+// If more detailed stats are needed, `-v=1` can be used: the hottest N
+// functions will be grouped into 5 equally-sized buckets, from the hottest
+// to the coldest; for each bucket, various summary statistics of the
+// distribution of the unreachable fractions and the raw unreachable execution
+// counts will be reported.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BOLT_PASSES_CONTINUITYSTATS_H
+#define BOLT_PASSES_CONTINUITYSTATS_H
+
+#include "bolt/Passes/BinaryPasses.h"
+#include <vector>
+
+namespace llvm {
+
+class raw_ostream;
+
+namespace bolt {
+class BinaryContext;
+
+/// Compute and report to the user the function CFG continuity quality
+class PrintContinuityStats : public BinaryFunctionPass {
+public:
+ explicit PrintContinuityStats(const cl::opt<bool> &PrintPass)
+ : BinaryFunctionPass(PrintPass) {}
+
+ bool shouldOptimize(const BinaryFunction &BF) const override;
+ const char *getName() const override { return "continuity-stats"; }
+ bool shouldPrint(const BinaryFunction &) const override { return false; }
+ Error runOnFunctions(BinaryContext &BC) override;
+};
+
+} // namespace bolt
+} // namespace llvm
+
+#endif // BOLT_PASSES_CONTINUITYSTATS_H
bolt/include/bolt/Passes/LongJmp.h
@@ -63,6 +63,19 @@ class LongJmpPass : public BinaryFunctionPass {
uint32_t NumColdStubs{0};
uint32_t NumSharedStubs{0};
+ /// The shortest distance for any branch instruction on AArch64.
+ static constexpr size_t ShortestJumpBits = 16;
+ static constexpr size_t ShortestJumpSpan = 1ULL << (ShortestJumpBits - 1);
+
+ /// The longest single-instruction branch.
+ static constexpr size_t LongestJumpBits = 28;
+ static constexpr size_t LongestJumpSpan = 1ULL << (LongestJumpBits - 1);
+
+ /// Relax all internal function branches including those between fragments.
+ /// Assume that fragments are placed in different sections but are within
+ /// 128MB of each other.
+ void relaxLocalBranches(BinaryFunction &BF);
+
/// -- Layout estimation methods --
/// Try to do layout before running the emitter, by looking at BinaryFunctions
/// and MCInsts -- this is an estimation. To be correct for longjmp inserter
bolt/include/bolt/Profile/BoltAddressTranslation.h
@@ -141,15 +141,13 @@ private:
uint64_t FuncOutputAddress) const;
/// Write the serialized address translation table for a function.
- template <bool Cold>
- void writeMaps(std::map<uint64_t, MapTy> &Maps, uint64_t &PrevAddress,
- raw_ostream &OS);
+ template <bool Cold> void writeMaps(uint64_t &PrevAddress, raw_ostream &OS);
/// Read the serialized address translation table for a function.
/// Return a parse error if failed.
template <bool Cold>
- void parseMaps(std::vector<uint64_t> &HotFuncs, uint64_t &PrevAddress,
- DataExtractor &DE, uint64_t &Offset, Error &Err);
+ void parseMaps(uint64_t &PrevAddress, DataExtractor &DE, uint64_t &Offset,
+ Error &Err);
/// Returns the bitmask with set bits corresponding to indices of BRANCHENTRY
/// entries in function address translation map.
@@ -161,6 +159,9 @@ private:
std::map<uint64_t, MapTy> Maps;
+ /// Ordered vector with addresses of hot functions.
+ std::vector<uint64_t> HotFuncs;
+
/// Map a function to its basic blocks count
std::unordered_map<uint64_t, size_t> NumBasicBlocksMap;
bolt/include/bolt/Profile/DataAggregator.h
@@ -266,7 +266,8 @@ private:
uint64_t Mispreds);
/// Register a \p Branch.
- bool doBranch(uint64_t From, uint64_t To, uint64_t Count, uint64_t Mispreds);
+ bool doBranch(uint64_t From, uint64_t To, uint64_t Count, uint64_t Mispreds,
+ bool IsPreagg);
/// Register a trace between two LBR entries supplied in execution order.
bool doTrace(const LBREntry &First, const LBREntry &Second,
bolt/include/bolt/Profile/ProfileYAMLMapping.h
@@ -96,9 +96,8 @@ template <> struct MappingTraits<bolt::SuccessorInfo> {
namespace bolt {
struct PseudoProbeInfo {
uint32_t InlineTreeIndex = 0;
- uint64_t BlockMask = 0; // bitset with probe indices
- // Assume BlockMask == 1 if no other probes are set
- std::vector<uint64_t> BlockProbes;
+ uint64_t BlockMask = 0; // bitset with probe indices from 1 to 64
+ std::vector<uint64_t> BlockProbes; // block probes with indices above 64
std::vector<uint64_t> CallProbes;
std::vector<uint64_t> IndCallProbes;
std::vector<uint32_t> InlineTreeNodes;
@@ -113,10 +112,10 @@ struct PseudoProbeInfo {
template <> struct MappingTraits<bolt::PseudoProbeInfo> {
static void mapping(IO &YamlIO, bolt::PseudoProbeInfo &PI) {
- YamlIO.mapOptional("blk", PI.BlockMask, 0);
- YamlIO.mapOptional("blks", PI.BlockProbes, std::vector<uint64_t>());
- YamlIO.mapOptional("calls", PI.CallProbes, std::vector<uint64_t>());
- YamlIO.mapOptional("indcalls", PI.IndCallProbes, std::vector<uint64_t>());
+ YamlIO.mapOptional("blx", PI.BlockMask, 0);
+ YamlIO.mapOptional("blk", PI.BlockProbes, std::vector<uint64_t>());
+ YamlIO.mapOptional("call", PI.CallProbes, std::vector<uint64_t>());
+ YamlIO.mapOptional("icall", PI.IndCallProbes, std::vector<uint64_t>());
YamlIO.mapOptional("id", PI.InlineTreeIndex, 0);
YamlIO.mapOptional("ids", PI.InlineTreeNodes, std::vector<uint32_t>());
}
@@ -170,18 +169,21 @@ template <> struct MappingTraits<bolt::BinaryBasicBlockProfile> {
};
namespace bolt {
-struct InlineTreeInfo {
+struct InlineTreeNode {
uint32_t ParentIndexDelta;
uint32_t CallSiteProbe;
- // Index in PseudoProbeDesc.GUID + 1, 0 for same as previous
+ // Index in PseudoProbeDesc.GUID, UINT32_MAX for same as previous (omitted)
uint32_t GUIDIndex;
- bool operator==(const InlineTreeInfo &) const { return false; }
+ // Decoded contents, ParentIndexDelta becomes absolute value.
+ uint64_t GUID;
+ uint64_t Hash;
+ bool operator==(const InlineTreeNode &) const { return false; }
};
} // end namespace bolt
-template <> struct MappingTraits<bolt::InlineTreeInfo> {
- static void mapping(IO &YamlIO, bolt::InlineTreeInfo &ITI) {
- YamlIO.mapOptional("g", ITI.GUIDIndex, 0);
+template <> struct MappingTraits<bolt::InlineTreeNode> {
+ static void mapping(IO &YamlIO, bolt::InlineTreeNode &ITI) {
+ YamlIO.mapOptional("g", ITI.GUIDIndex, UINT32_MAX);
YamlIO.mapOptional("p", ITI.ParentIndexDelta, 0);
YamlIO.mapOptional("cs", ITI.CallSiteProbe, 0);
}
@@ -192,7 +194,7 @@ template <> struct MappingTraits<bolt::InlineTreeInfo> {
} // end namespace llvm
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::BinaryBasicBlockProfile)
-LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::bolt::InlineTreeInfo)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::bolt::InlineTreeNode)
namespace llvm {
namespace yaml {
@@ -205,7 +207,7 @@ struct BinaryFunctionProfile {
llvm::yaml::Hex64 Hash{0};
uint64_t ExecCount{0};
std::vector<BinaryBasicBlockProfile> Blocks;
- std::vector<InlineTreeInfo> InlineTree;
+ std::vector<InlineTreeNode> InlineTree;
bool Used{false};
};
} // end namespace bolt
@@ -220,7 +222,7 @@ template <> struct MappingTraits<bolt::BinaryFunctionProfile> {
YamlIO.mapOptional("blocks", BFP.Blocks,
std::vector<bolt::BinaryBasicBlockProfile>());
YamlIO.mapOptional("inline_tree", BFP.InlineTree,
- std::vector<bolt::InlineTreeInfo>());
+ std::vector<bolt::InlineTreeNode>());
}
};
@@ -271,23 +273,24 @@ template <> struct MappingTraits<bolt::BinaryProfileHeader> {
};
namespace bolt {
-struct PseudoProbeDesc {
+struct ProfilePseudoProbeDesc {
std::vector<Hex64> GUID;
std::vector<Hex64> Hash;
- std::vector<uint32_t> GUIDHash; // Index of hash for that GUID in Hash
+ std::vector<uint32_t> GUIDHashIdx; // Index of hash for that GUID in Hash
- bool operator==(const PseudoProbeDesc &Other) const {
+ bool operator==(const ProfilePseudoProbeDesc &Other) const {
// Only treat empty Desc as equal
return GUID.empty() && Other.GUID.empty() && Hash.empty() &&
- Other.Hash.empty() && GUIDHash.empty() && Other.GUIDHash.empty();
+ Other.Hash.empty() && GUIDHashIdx.empty() &&
+ Other.GUIDHashIdx.empty();
}
};
} // end namespace bolt
-template <> struct MappingTraits<bolt::PseudoProbeDesc> {
- static void mapping(IO &YamlIO, bolt::PseudoProbeDesc &PD) {
+template <> struct MappingTraits<bolt::ProfilePseudoProbeDesc> {
+ static void mapping(IO &YamlIO, bolt::ProfilePseudoProbeDesc &PD) {
YamlIO.mapRequired("gs", PD.GUID);
- YamlIO.mapRequired("gh", PD.GUIDHash);
+ YamlIO.mapRequired("gh", PD.GUIDHashIdx);
YamlIO.mapRequired("hs", PD.Hash);
}
};
@@ -295,7 +298,7 @@ template <> struct MappingTraits<bolt::PseudoProbeDesc> {
} // end namespace llvm
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::BinaryFunctionProfile)
-LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::PseudoProbeDesc)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::ProfilePseudoProbeDesc)
namespace llvm {
namespace yaml {
@@ -304,7 +307,7 @@ namespace bolt {
struct BinaryProfile {
BinaryProfileHeader Header;
std::vector<BinaryFunctionProfile> Functions;
- PseudoProbeDesc PseudoProbeDesc;
+ ProfilePseudoProbeDesc PseudoProbeDesc;
};
} // namespace bolt
@@ -313,7 +316,7 @@ template <> struct MappingTraits<bolt::BinaryProfile> {
YamlIO.mapRequired("header", BP.Header);
YamlIO.mapRequired("functions", BP.Functions);
YamlIO.mapOptional("pseudo_probe_desc", BP.PseudoProbeDesc,
- bolt::PseudoProbeDesc());
+ bolt::ProfilePseudoProbeDesc());
}
};
bolt/include/bolt/Profile/YAMLProfileReader.h
@@ -101,6 +101,61 @@ public:
YamlBFAdjacencyMap;
};
+ // A class for matching inline tree nodes between profile and binary.
+ // Provides the mapping from profile inline tree node id to a
+ // corresponding binary MCDecodedPseudoProbeInlineTree node.
+ //
+ // The whole mapping process is the following:
+ //
+ // (profile) (binary)
+ // | blocks ^
+ // v |
+ // yaml::bolt::BinaryBasicBlockProfile ~= FlowBlock
+ // ||| probes ^ (majority vote)
+ // v ||| BBPseudoProbeToBlock
+ // yaml::bolt::PseudoProbeInfo MCDecodedPseudoProbe
+ // | InlineTreeIndex ^
+ // v | probe id
+ // [ profile node id (uint32_t) -> MCDecodedPseudoProbeInlineTree *]
+ // InlineTreeNodeMapTy
+ class InlineTreeNodeMapTy {
+ DenseMap<uint32_t, const MCDecodedPseudoProbeInlineTree *> Map;
+
+ void mapInlineTreeNode(uint32_t ProfileNodeIdx,
+ const MCDecodedPseudoProbeInlineTree *BinaryNode) {
+ auto Res = Map.try_emplace(ProfileNodeIdx, BinaryNode);
+ assert(Res.second &&
+ "Duplicate mapping from profile node index to binary inline tree");
+ (void)Res;
+ }
+
+ public:
+ /// Returns matched InlineTree * for a given profile inline_tree_id.
+ const MCDecodedPseudoProbeInlineTree *
+ getInlineTreeNode(uint32_t ProfileInlineTreeNodeId) const {
+ auto It = Map.find(ProfileInlineTreeNodeId);
+ if (It == Map.end())
+ return nullptr;
+ return It->second;
+ }
+
+ // Match up \p YamlInlineTree with binary inline tree rooted at \p Root.
+ // Return the number of matched nodes.
+ //
+ // This function populates the mapping from profile inline tree node id to a
+ // corresponding binary MCDecodedPseudoProbeInlineTree node.
+ size_t matchInlineTrees(
+ const MCPseudoProbeDecoder &Decoder,
+ const std::vector<yaml::bolt::InlineTreeNode> &YamlInlineTree,
+ const MCDecodedPseudoProbeInlineTree *Root);
+ };
+
+ // Partial probe matching specification: matched inline tree and corresponding
+ // BinaryFunctionProfile
+ using ProbeMatchSpec =
+ std::pair<InlineTreeNodeMapTy,
+ std::reference_wrapper<yaml::bolt::BinaryFunctionProfile>>;
+
private:
/// Adjustments for basic samples profiles (without LBR).
bool NormalizeByInsnCount{false};
@@ -110,7 +165,7 @@ private:
yaml::bolt::BinaryProfile YamlBP;
/// Map a function ID from a YAML profile to a BinaryFunction object.
- std::vector<BinaryFunction *> YamlProfileToFunction;
+ DenseMap<uint32_t, BinaryFunction *> YamlProfileToFunction;
using FunctionSet = std::unordered_set<const BinaryFunction *>;
/// To keep track of functions that have a matched profile before the profile
@@ -137,6 +192,10 @@ private:
// Pseudo probe function GUID to inline tree node
GUIDInlineTreeMap TopLevelGUIDToInlineTree;
+ // Mapping from a binary function to its partial match specification
+ // (YAML profile and its inline tree mapping to binary).
+ DenseMap<BinaryFunction *, std::vector<ProbeMatchSpec>> BFToProbeMatchSpecs;
+
/// Populate \p Function profile with the one supplied in YAML format.
bool parseFunctionProfile(BinaryFunction &Function,
const yaml::bolt::BinaryFunctionProfile &YamlBF);
@@ -147,7 +206,8 @@ private:
/// Infer function profile from stale data (collected on older binaries).
bool inferStaleProfile(BinaryFunction &Function,
- const yaml::bolt::BinaryFunctionProfile &YamlBF);
+ const yaml::bolt::BinaryFunctionProfile &YamlBF,
+ const ArrayRef<ProbeMatchSpec> ProbeMatchSpecs);
/// Initialize maps for profile matching.
void buildNameMaps(BinaryContext &BC);
@@ -164,14 +224,16 @@ private:
/// Matches functions using the call graph.
size_t matchWithCallGraph(BinaryContext &BC);
+ /// Matches functions using the call graph.
+ /// Populates BF->partial probe match spec map.
+ size_t matchWithPseudoProbes(BinaryContext &BC);
+
/// Matches functions with similarly named profiled functions.
size_t matchWithNameSimilarity(BinaryContext &BC);
/// Update matched YAML -> BinaryFunction pair.
void matchProfileToFunction(yaml::bolt::BinaryFunctionProfile &YamlBF,
BinaryFunction &BF) {
- if (YamlBF.Id >= YamlProfileToFunction.size())
- YamlProfileToFunction.resize(YamlBF.Id + 1);
YamlProfileToFunction[YamlBF.Id] = &BF;
YamlBF.Used = true;
bolt/include/bolt/Profile/YAMLProfileWriter.h
@@ -43,18 +43,18 @@ public:
GUIDNumMap HashIdxMap;
};
- static std::tuple<std::vector<yaml::bolt::InlineTreeInfo>, InlineTreeMapTy>
+ static std::tuple<std::vector<yaml::bolt::InlineTreeNode>, InlineTreeMapTy>
convertBFInlineTree(const MCPseudoProbeDecoder &Decoder,
const InlineTreeDesc &InlineTree, uint64_t GUID);
+ static std::tuple<yaml::bolt::ProfilePseudoProbeDesc, InlineTreeDesc>
+ convertPseudoProbeDesc(const MCPseudoProbeDecoder &PseudoProbeDecoder);
+
static yaml::bolt::BinaryFunctionProfile
convert(const BinaryFunction &BF, bool UseDFS,
const InlineTreeDesc &InlineTree,
const BoltAddressTranslation *BAT = nullptr);
- static std::tuple<yaml::bolt::PseudoProbeDesc, InlineTreeDesc>
- convertPseudoProbeDesc(const MCPseudoProbeDecoder &PseudoProbeDecoder);
-
/// Set CallSiteInfo destination fields from \p Symbol and return a target
/// BinaryFunction for that symbol.
static const BinaryFunction *
@@ -71,8 +71,8 @@ private:
uint32_t InlineSite;
};
static std::vector<InlineTreeNode>
- getInlineTree(const MCPseudoProbeDecoder &Decoder,
- const MCDecodedPseudoProbeInlineTree *Root);
+ collectInlineTree(const MCPseudoProbeDecoder &Decoder,
+ const MCDecodedPseudoProbeInlineTree &Root);
// 0 - block probe, 1 - indirect call, 2 - direct call
using ProbeList = std::array<SmallVector<uint64_t, 0>, 3>;
bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -510,12 +510,11 @@ private:
};
/// Different types of X86-64 PLT sections.
- const PLTSectionInfo X86_64_PLTSections[4] = {
- { ".plt", 16 },
- { ".plt.got", 8 },
- { ".plt.sec", 8 },
- { nullptr, 0 }
- };
+ const PLTSectionInfo X86_64_PLTSections[5] = {{".plt", 16},
+ {".plt.got", 8},
+ {".plt.sec", 8},
+ {".iplt", 16},
+ {nullptr, 0}};
/// AArch64 PLT sections.
const PLTSectionInfo AArch64_PLTSections[4] = {
bolt/include/bolt/Utils/CommandLineOpts.h
@@ -55,6 +55,7 @@ extern llvm::cl::opt<bool> PrintSections;
enum ProfileFormatKind { PF_Fdata, PF_YAML };
extern llvm::cl::opt<ProfileFormatKind> ProfileFormat;
+extern llvm::cl::opt<bool> ShowDensity;
extern llvm::cl::opt<bool> SplitEH;
extern llvm::cl::opt<bool> StrictMode;
extern llvm::cl::opt<bool> TimeOpts;
bolt/lib/Core/BinaryContext.cpp
@@ -1294,8 +1294,8 @@ bool BinaryContext::handleAArch64Veneer(uint64_t Address, bool MatchOnly) {
Veneer->getOrCreateLocalLabel(Address);
Veneer->setMaxSize(TotalSize);
Veneer->updateState(BinaryFunction::State::Disassembled);
- LLVM_DEBUG(dbgs() << "BOLT-DEBUG: handling veneer function at 0x" << Address
- << "\n");
+ LLVM_DEBUG(dbgs() << "BOLT-DEBUG: handling veneer function at 0x"
+ << Twine::utohexstr(Address) << "\n");
return true;
};
@@ -2021,6 +2021,9 @@ BinaryContext::getBaseAddressForMapping(uint64_t MMapAddress,
// Find a segment with a matching file offset.
for (auto &KV : SegmentMapInfo) {
const SegmentInfo &SegInfo = KV.second;
+ // Only consider executable segments.
+ if (!SegInfo.IsExecutable)
+ continue;
// FileOffset is got from perf event,
// and it is equal to alignDown(SegInfo.FileOffset, pagesize).
// If the pagesize is not equal to SegInfo.Alignment.
bolt/lib/Core/BinaryFunction.cpp
@@ -165,6 +165,12 @@ bool shouldPrint(const BinaryFunction &Function) {
}
}
+ std::optional<StringRef> Origin = Function.getOriginSectionName();
+ if (Origin && llvm::any_of(opts::PrintOnly, [&](const std::string &Name) {
+ return Name == *Origin;
+ }))
+ return true;
+
return false;
}
@@ -2571,6 +2577,7 @@ private:
case MCCFIInstruction::OpAdjustCfaOffset:
case MCCFIInstruction::OpWindowSave:
case MCCFIInstruction::OpNegateRAState:
+ case MCCFIInstruction::OpNegateRAStateWithPC:
case MCCFIInstruction::OpLLVMDefAspaceCfa:
case MCCFIInstruction::OpLabel:
llvm_unreachable("unsupported CFI opcode");
@@ -2709,6 +2716,7 @@ struct CFISnapshotDiff : public CFISnapshot {
case MCCFIInstruction::OpAdjustCfaOffset:
case MCCFIInstruction::OpWindowSave:
case MCCFIInstruction::OpNegateRAState:
+ case MCCFIInstruction::OpNegateRAStateWithPC:
case MCCFIInstruction::OpLLVMDefAspaceCfa:
case MCCFIInstruction::OpLabel:
llvm_unreachable("unsupported CFI opcode");
@@ -2858,6 +2866,7 @@ BinaryFunction::unwindCFIState(int32_t FromState, int32_t ToState,
case MCCFIInstruction::OpAdjustCfaOffset:
case MCCFIInstruction::OpWindowSave:
case MCCFIInstruction::OpNegateRAState:
+ case MCCFIInstruction::OpNegateRAStateWithPC:
case MCCFIInstruction::OpLLVMDefAspaceCfa:
case MCCFIInstruction::OpLabel:
llvm_unreachable("unsupported CFI opcode");
@@ -3678,9 +3687,8 @@ BinaryFunction::BasicBlockListType BinaryFunction::dfs() const {
BinaryBasicBlock *BB = Stack.top();
Stack.pop();
- if (Visited.find(BB) != Visited.end())
+ if (!Visited.insert(BB).second)
continue;
- Visited.insert(BB);
DFS.push_back(BB);
for (BinaryBasicBlock *SuccBB : BB->landing_pads()) {
@@ -3873,11 +3881,8 @@ void BinaryFunction::disambiguateJumpTables(
JumpTable *JT = getJumpTable(Inst);
if (!JT)
continue;
- auto Iter = JumpTables.find(JT);
- if (Iter == JumpTables.end()) {
- JumpTables.insert(JT);
+ if (JumpTables.insert(JT).second)
continue;
- }
// This instruction is an indirect jump using a jump table, but it is
// using the same jump table of another jump. Try all our tricks to
// extract the jump table symbol and make it point to a new, duplicated JT
bolt/lib/Core/DIEBuilder.cpp
@@ -57,11 +57,9 @@ getDWOName(llvm::DWARFUnit &CU,
"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;
+ uint32_t &Index = NameToIndexMap[DWOName];
+ DWOName.append(std::to_string(Index));
+ ++Index;
}
DWOName.append(".dwo");
return DWOName;
@@ -283,8 +281,7 @@ void DIEBuilder::buildTypeUnits(DebugStrOffsetsWriter *StrOffsetWriter,
for (auto &Row : TUIndex.getRows()) {
uint64_t Signature = Row.getSignature();
// manually populate TypeUnit to UnitVector
- DwarfContext->getTypeUnitForHash(DwarfContext->getMaxVersion(), Signature,
- true);
+ DwarfContext->getTypeUnitForHash(Signature, true);
}
}
const unsigned int CUNum = getCUNum(DwarfContext, isDWO());
bolt/lib/Core/FunctionLayout.cpp
@@ -33,7 +33,9 @@ FunctionFragment::const_iterator FunctionFragment::end() const {
return const_iterator(Layout->block_begin() + StartIndex + Size);
}
-const BinaryBasicBlock *FunctionFragment::front() const { return *begin(); }
+BinaryBasicBlock *FunctionFragment::front() const { return *begin(); }
+
+BinaryBasicBlock *FunctionFragment::back() const { return *std::prev(end()); }
FunctionLayout::FunctionLayout() { addFragment(); }
bolt/lib/Core/HashUtilities.cpp
@@ -145,7 +145,7 @@ std::string hashBlockLoose(BinaryContext &BC, const BinaryBasicBlock &BB) {
continue;
}
- std::string Mnemonic = BC.InstPrinter->getMnemonic(&Inst).first;
+ std::string Mnemonic = BC.InstPrinter->getMnemonic(Inst).first;
llvm::erase_if(Mnemonic, [](unsigned char ch) { return std::isspace(ch); });
Opcodes.insert(Mnemonic);
}
bolt/lib/Passes/ADRRelaxationPass.cpp
@@ -56,13 +56,19 @@ void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) {
continue;
}
- // Don't relax adr if it points to the same function and it is not split
- // and BF initial size is < 1MB.
+ // Don't relax ADR if it points to the same function and is in the main
+ // fragment and BF initial size is < 1MB.
const unsigned OneMB = 0x100000;
- if (!BF.isSplit() && BF.getSize() < OneMB) {
+ if (BF.getSize() < OneMB) {
BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol);
- if (TargetBF && TargetBF == &BF)
+ if (TargetBF == &BF && !BB.isSplit())
continue;
+
+ // No relaxation needed if ADR references a basic block in the same
+ // fragment.
+ if (BinaryBasicBlock *TargetBB = BF.getBasicBlockForLabel(Symbol))
+ if (BB.getFragmentNum() == TargetBB->getFragmentNum())
+ continue;
}
MCPhysReg Reg;
bolt/lib/Passes/BinaryPasses.cpp
@@ -15,6 +15,7 @@
#include "bolt/Core/ParallelUtilities.h"
#include "bolt/Passes/ReorderAlgorithm.h"
#include "bolt/Passes/ReorderFunctions.h"
+#include "bolt/Utils/CommandLineOpts.h"
#include "llvm/Support/CommandLine.h"
#include <atomic>
#include <mutex>
@@ -223,6 +224,18 @@ static cl::opt<unsigned> TopCalledLimit(
"functions section"),
cl::init(100), cl::Hidden, cl::cat(BoltCategory));
+// Profile density options, synced with llvm-profgen/ProfileGenerator.cpp
+static cl::opt<int> ProfileDensityCutOffHot(
+ "profile-density-cutoff-hot", cl::init(990000),
+ cl::desc("Total samples cutoff for functions used to calculate "
+ "profile density."));
+
+static cl::opt<double> ProfileDensityThreshold(
+ "profile-density-threshold", cl::init(60),
+ cl::desc("If the profile density is below the given threshold, it "
+ "will be suggested to increase the sampling rate."),
+ cl::Optional);
+
} // namespace opts
namespace llvm {
@@ -1383,6 +1396,7 @@ Error PrintProgramStats::runOnFunctions(BinaryContext &BC) {
uint64_t StaleSampleCount = 0;
uint64_t InferredSampleCount = 0;
std::vector<const BinaryFunction *> ProfiledFunctions;
+ std::vector<std::pair<double, uint64_t>> FuncDensityList;
const char *StaleFuncsHeader = "BOLT-INFO: Functions with stale profile:\n";
for (auto &BFI : BC.getBinaryFunctions()) {
const BinaryFunction &Function = BFI.second;
@@ -1441,6 +1455,22 @@ Error PrintProgramStats::runOnFunctions(BinaryContext &BC) {
StaleSampleCount += SampleCount;
++NumAllStaleFunctions;
}
+
+ if (opts::ShowDensity) {
+ uint64_t Size = Function.getSize();
+ // In case of BOLT split functions registered in BAT, executed traces are
+ // automatically attributed to the main fragment. Add up function sizes
+ // for all fragments.
+ if (IsHotParentOfBOLTSplitFunction)
+ for (const BinaryFunction *Fragment : Function.getFragments())
+ Size += Fragment->getSize();
+ double Density = (double)1.0 * Function.getSampleCountInBytes() / Size;
+ FuncDensityList.emplace_back(Density, SampleCount);
+ LLVM_DEBUG(BC.outs() << Function << ": executed bytes "
+ << Function.getSampleCountInBytes() << ", size (b) "
+ << Size << ", density " << Density
+ << ", sample count " << SampleCount << '\n');
+ }
}
BC.NumProfiledFuncs = ProfiledFunctions.size();
BC.NumStaleProfileFuncs = NumStaleProfileFunctions;
@@ -1722,6 +1752,50 @@ Error PrintProgramStats::runOnFunctions(BinaryContext &BC) {
BC.outs() << ". Use -print-unknown to see the list.";
BC.outs() << '\n';
}
+
+ if (opts::ShowDensity) {
+ double Density = 0.0;
+ // Sorted by the density in descending order.
+ llvm::stable_sort(FuncDensityList,
+ [&](const std::pair<double, uint64_t> &A,
+ const std::pair<double, uint64_t> &B) {
+ if (A.first != B.first)
+ return A.first > B.first;
+ return A.second < B.second;
+ });
+
+ uint64_t AccumulatedSamples = 0;
+ uint32_t I = 0;
+ assert(opts::ProfileDensityCutOffHot <= 1000000 &&
+ "The cutoff value is greater than 1000000(100%)");
+ while (AccumulatedSamples <
+ TotalSampleCount *
+ static_cast<float>(opts::ProfileDensityCutOffHot) /
+ 1000000 &&
+ I < FuncDensityList.size()) {
+ AccumulatedSamples += FuncDensityList[I].second;
+ Density = FuncDensityList[I].first;
+ I++;
+ }
+ if (Density == 0.0) {
+ BC.errs() << "BOLT-WARNING: the output profile is empty or the "
+ "--profile-density-cutoff-hot option is "
+ "set too low. Please check your command.\n";
+ } else if (Density < opts::ProfileDensityThreshold) {
+ BC.errs()
+ << "BOLT-WARNING: BOLT is estimated to optimize better with "
+ << format("%.1f", opts::ProfileDensityThreshold / Density)
+ << "x more samples. Please consider increasing sampling rate or "
+ "profiling for longer duration to get more samples.\n";
+ }
+
+ BC.outs() << "BOLT-INFO: Functions with density >= "
+ << format("%.1f", Density) << " account for "
+ << format("%.2f",
+ static_cast<double>(opts::ProfileDensityCutOffHot) /
+ 10000)
+ << "% total sample counts.\n";
+ }
return Error::success();
}
bolt/lib/Passes/CMakeLists.txt
@@ -26,6 +26,7 @@ add_llvm_library(LLVMBOLTPasses
PatchEntries.cpp
PettisAndHansen.cpp
PLTCall.cpp
+ ContinuityStats.cpp
RegAnalysis.cpp
RegReAssign.cpp
ReorderAlgorithm.cpp
bolt/lib/Passes/ContinuityStats.cpp
@@ -0,0 +1,250 @@
+//===- bolt/Passes/ContinuityStats.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the continuity stats calculation pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Passes/ContinuityStats.h"
+#include "bolt/Core/BinaryBasicBlock.h"
+#include "bolt/Core/BinaryFunction.h"
+#include "bolt/Utils/CommandLineOpts.h"
+#include "llvm/Support/CommandLine.h"
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+
+#define DEBUG_TYPE "bolt-opts"
+
+using namespace llvm;
+using namespace bolt;
+
+namespace opts {
+extern cl::opt<unsigned> Verbosity;
+cl::opt<unsigned> NumFunctionsForContinuityCheck(
+ "num-functions-for-continuity-check",
+ cl::desc("number of hottest functions to print aggregated "
+ "CFG discontinuity stats of."),
+ cl::init(1000), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltOptCategory));
+} // namespace opts
+
+namespace {
+using FunctionListType = std::vector<const BinaryFunction *>;
+using function_iterator = FunctionListType::iterator;
+
+template <typename T>
+void printDistribution(raw_ostream &OS, std::vector<T> &values,
+ bool Fraction = false) {
+ if (values.empty())
+ return;
+ // Sort values from largest to smallest and print the MAX, TOP 1%, 5%, 10%,
+ // 20%, 50%, 80%, MIN. If Fraction is true, then values are printed as
+ // fractions instead of integers.
+ std::sort(values.begin(), values.end());
+
+ auto printLine = [&](std::string Text, double Percent) {
+ int Rank = int(values.size() * (1.0 - Percent / 100));
+ if (Percent == 0)
+ Rank = values.size() - 1;
+ if (Fraction)
+ OS << " " << Text << std::string(9 - Text.length(), ' ') << ": "
+ << format("%.2lf%%", values[Rank] * 100) << "\n";
+ else
+ OS << " " << Text << std::string(9 - Text.length(), ' ') << ": "
+ << values[Rank] << "\n";
+ };
+
+ printLine("MAX", 0);
+ const int percentages[] = {1, 5, 10, 20, 50, 80};
+ for (size_t i = 0; i < sizeof(percentages) / sizeof(percentages[0]); ++i) {
+ printLine("TOP " + std::to_string(percentages[i]) + "%", percentages[i]);
+ }
+ printLine("MIN", 100);
+}
+
+void printCFGContinuityStats(raw_ostream &OS,
+ iterator_range<function_iterator> &Functions) {
+ // Given a perfect profile, every positive-execution-count BB should be
+ // connected to an entry of the function through a positive-execution-count
+ // directed path in the control flow graph.
+ std::vector<size_t> NumUnreachables;
+ std::vector<size_t> SumECUnreachables;
+ std::vector<double> FractionECUnreachables;
+
+ for (auto it = Functions.begin(); it != Functions.end(); ++it) {
+ const BinaryFunction *Function = *it;
+ if (Function->size() <= 1)
+ continue;
+
+ // Compute the sum of all BB execution counts (ECs).
+ size_t NumPosECBBs = 0;
+ size_t SumAllBBEC = 0;
+ for (const BinaryBasicBlock &BB : *Function) {
+ const size_t BBEC = BB.getKnownExecutionCount();
+ NumPosECBBs += BBEC > 0 ? 1 : 0;
+ SumAllBBEC += BBEC;
+ }
+
+ // Perform BFS on subgraph of CFG induced by positive weight edges.
+ // Compute the number of BBs reachable from the entry(s) of the function and
+ // the sum of their execution counts (ECs).
+ std::unordered_map<unsigned, const BinaryBasicBlock *> IndexToBB;
+ std::unordered_set<unsigned> Visited;
+ std::queue<unsigned> Queue;
+ for (const BinaryBasicBlock &BB : *Function) {
+ // Make sure BB.getIndex() is not already in IndexToBB.
+ assert(IndexToBB.find(BB.getIndex()) == IndexToBB.end());
+ IndexToBB[BB.getIndex()] = &BB;
+ if (BB.isEntryPoint() && BB.getKnownExecutionCount() > 0) {
+ Queue.push(BB.getIndex());
+ Visited.insert(BB.getIndex());
+ }
+ }
+ while (!Queue.empty()) {
+ const unsigned BBIndex = Queue.front();
+ const BinaryBasicBlock *BB = IndexToBB[BBIndex];
+ Queue.pop();
+ auto SuccBIIter = BB->branch_info_begin();
+ for (const BinaryBasicBlock *Succ : BB->successors()) {
+ const uint64_t Count = SuccBIIter->Count;
+ if (Count == BinaryBasicBlock::COUNT_NO_PROFILE || Count == 0) {
+ ++SuccBIIter;
+ continue;
+ }
+ if (!Visited.insert(Succ->getIndex()).second) {
+ ++SuccBIIter;
+ continue;
+ }
+ Queue.push(Succ->getIndex());
+ ++SuccBIIter;
+ }
+ }
+
+ const size_t NumReachableBBs = Visited.size();
+
+ // Loop through Visited, and sum the corresponding BBs' execution counts
+ // (ECs).
+ size_t SumReachableBBEC = 0;
+ for (const unsigned BBIndex : Visited) {
+ const BinaryBasicBlock *BB = IndexToBB[BBIndex];
+ SumReachableBBEC += BB->getKnownExecutionCount();
+ }
+
+ const size_t NumPosECBBsUnreachableFromEntry =
+ NumPosECBBs - NumReachableBBs;
+ const size_t SumUnreachableBBEC = SumAllBBEC - SumReachableBBEC;
+ const double FractionECUnreachable =
+ (double)SumUnreachableBBEC / SumAllBBEC;
+
+ if (opts::Verbosity >= 2 && FractionECUnreachable >= 0.05) {
+ OS << "Non-trivial CFG discontinuity observed in function "
+ << Function->getPrintName() << "\n";
+ LLVM_DEBUG(Function->dump());
+ }
+
+ NumUnreachables.push_back(NumPosECBBsUnreachableFromEntry);
+ SumECUnreachables.push_back(SumUnreachableBBEC);
+ FractionECUnreachables.push_back(FractionECUnreachable);
+ }
+
+ if (FractionECUnreachables.empty())
+ return;
+
+ std::sort(FractionECUnreachables.begin(), FractionECUnreachables.end());
+ const int Rank = int(FractionECUnreachables.size() * 0.95);
+ OS << format("top 5%% function CFG discontinuity is %.2lf%%\n",
+ FractionECUnreachables[Rank] * 100);
+
+ if (opts::Verbosity >= 1) {
+ OS << "abbreviations: EC = execution count, POS BBs = positive EC BBs\n"
+ << "distribution of NUM(unreachable POS BBs) among all focal "
+ "functions\n";
+ printDistribution(OS, NumUnreachables);
+
+ OS << "distribution of SUM_EC(unreachable POS BBs) among all focal "
+ "functions\n";
+ printDistribution(OS, SumECUnreachables);
+
+ OS << "distribution of [(SUM_EC(unreachable POS BBs) / SUM_EC(all "
+ "POS BBs))] among all focal functions\n";
+ printDistribution(OS, FractionECUnreachables, /*Fraction=*/true);
+ }
+}
+
+void printAll(BinaryContext &BC, FunctionListType &ValidFunctions,
+ size_t NumTopFunctions) {
+ // Sort the list of functions by execution counts (reverse).
+ llvm::sort(ValidFunctions,
+ [&](const BinaryFunction *A, const BinaryFunction *B) {
+ return A->getKnownExecutionCount() > B->getKnownExecutionCount();
+ });
+
+ const size_t RealNumTopFunctions =
+ std::min(NumTopFunctions, ValidFunctions.size());
+
+ iterator_range<function_iterator> Functions(
+ ValidFunctions.begin(), ValidFunctions.begin() + RealNumTopFunctions);
+
+ BC.outs() << format("BOLT-INFO: among the hottest %zu functions ",
+ RealNumTopFunctions);
+ printCFGContinuityStats(BC.outs(), Functions);
+
+ // Print more detailed bucketed stats if requested.
+ if (opts::Verbosity >= 1 && RealNumTopFunctions >= 5) {
+ const size_t PerBucketSize = RealNumTopFunctions / 5;
+ BC.outs() << format(
+ "Detailed stats for 5 buckets, each with %zu functions:\n",
+ PerBucketSize);
+
+ // For each bucket, print the CFG continuity stats of the functions in the
+ // bucket.
+ for (size_t BucketIndex = 0; BucketIndex < 5; ++BucketIndex) {
+ const size_t StartIndex = BucketIndex * PerBucketSize;
+ const size_t EndIndex = StartIndex + PerBucketSize;
+ iterator_range<function_iterator> Functions(
+ ValidFunctions.begin() + StartIndex,
+ ValidFunctions.begin() + EndIndex);
+ const size_t MaxFunctionExecutionCount =
+ ValidFunctions[StartIndex]->getKnownExecutionCount();
+ const size_t MinFunctionExecutionCount =
+ ValidFunctions[EndIndex - 1]->getKnownExecutionCount();
+ BC.outs() << format("----------------\n| Bucket %zu: "
+ "|\n----------------\n",
+ BucketIndex + 1)
+ << format(
+ "execution counts of the %zu functions in the bucket: "
+ "%zu-%zu\n",
+ EndIndex - StartIndex, MinFunctionExecutionCount,
+ MaxFunctionExecutionCount);
+ printCFGContinuityStats(BC.outs(), Functions);
+ }
+ }
+}
+} // namespace
+
+bool PrintContinuityStats::shouldOptimize(const BinaryFunction &BF) const {
+ if (BF.empty() || !BF.hasValidProfile())
+ return false;
+
+ return BinaryFunctionPass::shouldOptimize(BF);
+}
+
+Error PrintContinuityStats::runOnFunctions(BinaryContext &BC) {
+ // Create a list of functions with valid profiles.
+ FunctionListType ValidFunctions;
+ for (const auto &BFI : BC.getBinaryFunctions()) {
+ const BinaryFunction *Function = &BFI.second;
+ if (PrintContinuityStats::shouldOptimize(*Function))
+ ValidFunctions.push_back(Function);
+ }
+ if (ValidFunctions.empty() || opts::NumFunctionsForContinuityCheck == 0)
+ return Error::success();
+
+ printAll(BC, ValidFunctions, opts::NumFunctionsForContinuityCheck);
+ return Error::success();
+}
bolt/lib/Passes/Instrumentation.cpp
@@ -109,9 +109,8 @@ static bool hasAArch64ExclusiveMemop(
BinaryBasicBlock *BB = BBQueue.front().first;
bool IsLoad = BBQueue.front().second;
BBQueue.pop();
- if (Visited.find(BB) != Visited.end())
+ if (!Visited.insert(BB).second)
continue;
- Visited.insert(BB);
for (const MCInst &Inst : *BB) {
// Two loads one after another - skip whole function
@@ -126,8 +125,7 @@ static bool hasAArch64ExclusiveMemop(
if (BC.MIB->isAArch64ExclusiveLoad(Inst))
IsLoad = true;
- if (IsLoad && BBToSkip.find(BB) == BBToSkip.end()) {
- BBToSkip.insert(BB);
+ if (IsLoad && BBToSkip.insert(BB).second) {
if (opts::Verbosity >= 2) {
outs() << "BOLT-INSTRUMENTER: skip BB " << BB->getName()
<< " due to exclusive instruction in function "
bolt/lib/Passes/LongJmp.cpp
@@ -11,18 +11,26 @@
//===----------------------------------------------------------------------===//
#include "bolt/Passes/LongJmp.h"
+#include "bolt/Core/ParallelUtilities.h"
+#include "llvm/Support/MathExtras.h"
#define DEBUG_TYPE "longjmp"
using namespace llvm;
namespace opts {
+extern cl::OptionCategory BoltCategory;
extern cl::OptionCategory BoltOptCategory;
extern llvm::cl::opt<unsigned> AlignText;
extern cl::opt<unsigned> AlignFunctions;
extern cl::opt<bool> UseOldText;
extern cl::opt<bool> HotFunctionsAtEnd;
+static cl::opt<bool>
+ CompactCodeModel("compact-code-model",
+ cl::desc("generate code for binaries <128MB on AArch64"),
+ cl::init(false), cl::cat(BoltCategory));
+
static cl::opt<bool> GroupStubs("group-stubs",
cl::desc("share stubs across functions"),
cl::init(true), cl::cat(BoltOptCategory));
@@ -61,10 +69,10 @@ static BinaryBasicBlock *getBBAtHotColdSplitPoint(BinaryFunction &Func) {
if (Next != E && (*Next)->isCold())
return *I;
}
- llvm_unreachable("No hot-colt split point found");
+ llvm_unreachable("No hot-cold split point found");
}
-static bool shouldInsertStub(const BinaryContext &BC, const MCInst &Inst) {
+static bool mayNeedStub(const BinaryContext &BC, const MCInst &Inst) {
return (BC.MIB->isBranch(Inst) || BC.MIB->isCall(Inst)) &&
!BC.MIB->isIndirectBranch(Inst) && !BC.MIB->isIndirectCall(Inst);
}
@@ -324,9 +332,8 @@ uint64_t LongJmpPass::tentativeLayoutRelocColdPart(
uint64_t LongJmpPass::tentativeLayoutRelocMode(
const BinaryContext &BC, std::vector<BinaryFunction *> &SortedFunctions,
uint64_t DotAddress) {
-
// Compute hot cold frontier
- uint32_t LastHotIndex = -1u;
+ int64_t LastHotIndex = -1u;
uint32_t CurrentIndex = 0;
if (opts::HotFunctionsAtEnd) {
for (BinaryFunction *BF : SortedFunctions) {
@@ -351,19 +358,20 @@ uint64_t LongJmpPass::tentativeLayoutRelocMode(
// Hot
CurrentIndex = 0;
bool ColdLayoutDone = false;
+ auto runColdLayout = [&]() {
+ DotAddress = tentativeLayoutRelocColdPart(BC, SortedFunctions, DotAddress);
+ ColdLayoutDone = true;
+ if (opts::HotFunctionsAtEnd)
+ DotAddress = alignTo(DotAddress, opts::AlignText);
+ };
for (BinaryFunction *Func : SortedFunctions) {
if (!BC.shouldEmit(*Func)) {
HotAddresses[Func] = Func->getAddress();
continue;
}
- if (!ColdLayoutDone && CurrentIndex >= LastHotIndex) {
- DotAddress =
- tentativeLayoutRelocColdPart(BC, SortedFunctions, DotAddress);
- ColdLayoutDone = true;
- if (opts::HotFunctionsAtEnd)
- DotAddress = alignTo(DotAddress, opts::AlignText);
- }
+ if (!ColdLayoutDone && CurrentIndex >= LastHotIndex)
+ runColdLayout();
DotAddress = alignTo(DotAddress, Func->getMinAlignment());
uint64_t Pad =
@@ -382,6 +390,11 @@ uint64_t LongJmpPass::tentativeLayoutRelocMode(
DotAddress += Func->estimateConstantIslandSize();
++CurrentIndex;
}
+
+ // Ensure that tentative code layout always runs for cold blocks.
+ if (!ColdLayoutDone)
+ runColdLayout();
+
// BBs
for (BinaryFunction *Func : SortedFunctions)
tentativeBBLayout(*Func);
@@ -565,7 +578,7 @@ Error LongJmpPass::relax(BinaryFunction &Func, bool &Modified) {
if (BC.MIB->isPseudo(Inst))
continue;
- if (!shouldInsertStub(BC, Inst)) {
+ if (!mayNeedStub(BC, Inst)) {
DotAddress += InsnSize;
continue;
}
@@ -629,7 +642,283 @@ Error LongJmpPass::relax(BinaryFunction &Func, bool &Modified) {
return Error::success();
}
+void LongJmpPass::relaxLocalBranches(BinaryFunction &BF) {
+ BinaryContext &BC = BF.getBinaryContext();
+ auto &MIB = BC.MIB;
+
+ // Quick path.
+ if (!BF.isSplit() && BF.estimateSize() < ShortestJumpSpan)
+ return;
+
+ auto isBranchOffsetInRange = [&](const MCInst &Inst, int64_t Offset) {
+ const unsigned Bits = MIB->getPCRelEncodingSize(Inst);
+ return isIntN(Bits, Offset);
+ };
+
+ auto isBlockInRange = [&](const MCInst &Inst, uint64_t InstAddress,
+ const BinaryBasicBlock &BB) {
+ const int64_t Offset = BB.getOutputStartAddress() - InstAddress;
+ return isBranchOffsetInRange(Inst, Offset);
+ };
+
+ // Keep track of *all* function trampolines that are going to be added to the
+ // function layout at the end of relaxation.
+ std::vector<std::pair<BinaryBasicBlock *, std::unique_ptr<BinaryBasicBlock>>>
+ FunctionTrampolines;
+
+ // Function fragments are relaxed independently.
+ for (FunctionFragment &FF : BF.getLayout().fragments()) {
+ // Fill out code size estimation for the fragment. Use output BB address
+ // ranges to store offsets from the start of the function fragment.
+ uint64_t CodeSize = 0;
+ for (BinaryBasicBlock *BB : FF) {
+ BB->setOutputStartAddress(CodeSize);
+ CodeSize += BB->estimateSize();
+ BB->setOutputEndAddress(CodeSize);
+ }
+
+ // Dynamically-updated size of the fragment.
+ uint64_t FragmentSize = CodeSize;
+
+ // Size of the trampoline in bytes.
+ constexpr uint64_t TrampolineSize = 4;
+
+ // Trampolines created for the fragment. DestinationBB -> TrampolineBB.
+ // NB: here we store only the first trampoline created for DestinationBB.
+ DenseMap<const BinaryBasicBlock *, BinaryBasicBlock *> FragmentTrampolines;
+
+ // Create a trampoline code after \p BB or at the end of the fragment if BB
+ // is nullptr. If \p UpdateOffsets is true, update FragmentSize and offsets
+ // for basic blocks affected by the insertion of the trampoline.
+ auto addTrampolineAfter = [&](BinaryBasicBlock *BB,
+ BinaryBasicBlock *TargetBB, uint64_t Count,
+ bool UpdateOffsets = true) {
+ FunctionTrampolines.emplace_back(BB ? BB : FF.back(),
+ BF.createBasicBlock());
+ BinaryBasicBlock *TrampolineBB = FunctionTrampolines.back().second.get();
+
+ MCInst Inst;
+ {
+ auto L = BC.scopeLock();
+ MIB->createUncondBranch(Inst, TargetBB->getLabel(), BC.Ctx.get());
+ }
+ TrampolineBB->addInstruction(Inst);
+ TrampolineBB->addSuccessor(TargetBB, Count);
+ TrampolineBB->setExecutionCount(Count);
+ const uint64_t TrampolineAddress =
+ BB ? BB->getOutputEndAddress() : FragmentSize;
+ TrampolineBB->setOutputStartAddress(TrampolineAddress);
+ TrampolineBB->setOutputEndAddress(TrampolineAddress + TrampolineSize);
+ TrampolineBB->setFragmentNum(FF.getFragmentNum());
+
+ if (!FragmentTrampolines.lookup(TargetBB))
+ FragmentTrampolines[TargetBB] = TrampolineBB;
+
+ if (!UpdateOffsets)
+ return TrampolineBB;
+
+ FragmentSize += TrampolineSize;
+
+ // If the trampoline was added at the end of the fragment, offsets of
+ // other fragments should stay intact.
+ if (!BB)
+ return TrampolineBB;
+
+ // Update offsets for blocks after BB.
+ for (BinaryBasicBlock *IBB : FF) {
+ if (IBB->getOutputStartAddress() >= TrampolineAddress) {
+ IBB->setOutputStartAddress(IBB->getOutputStartAddress() +
+ TrampolineSize);
+ IBB->setOutputEndAddress(IBB->getOutputEndAddress() + TrampolineSize);
+ }
+ }
+
+ // Update offsets for trampolines in this fragment that are placed after
+ // the new trampoline. Note that trampoline blocks are not part of the
+ // function/fragment layout until we add them right before the return
+ // from relaxLocalBranches().
+ for (auto &Pair : FunctionTrampolines) {
+ BinaryBasicBlock *IBB = Pair.second.get();
+ if (IBB->getFragmentNum() != TrampolineBB->getFragmentNum())
+ continue;
+ if (IBB == TrampolineBB)
+ continue;
+ if (IBB->getOutputStartAddress() >= TrampolineAddress) {
+ IBB->setOutputStartAddress(IBB->getOutputStartAddress() +
+ TrampolineSize);
+ IBB->setOutputEndAddress(IBB->getOutputEndAddress() + TrampolineSize);
+ }
+ }
+
+ return TrampolineBB;
+ };
+
+ // Pre-populate trampolines by splitting unconditional branches from the
+ // containing basic block.
+ for (BinaryBasicBlock *BB : FF) {
+ MCInst *Inst = BB->getLastNonPseudoInstr();
+ if (!Inst || !MIB->isUnconditionalBranch(*Inst))
+ continue;
+
+ const MCSymbol *TargetSymbol = MIB->getTargetSymbol(*Inst);
+ BB->eraseInstruction(BB->findInstruction(Inst));
+ BB->setOutputEndAddress(BB->getOutputEndAddress() - TrampolineSize);
+
+ BinaryBasicBlock::BinaryBranchInfo BI;
+ BinaryBasicBlock *TargetBB = BB->getSuccessor(TargetSymbol, BI);
+
+ BinaryBasicBlock *TrampolineBB =
+ addTrampolineAfter(BB, TargetBB, BI.Count, /*UpdateOffsets*/ false);
+ BB->replaceSuccessor(TargetBB, TrampolineBB, BI.Count);
+ }
+
+ /// Relax the branch \p Inst in basic block \p BB that targets \p TargetBB.
+ /// \p InstAddress contains offset of the branch from the start of the
+ /// containing function fragment.
+ auto relaxBranch = [&](BinaryBasicBlock *BB, MCInst &Inst,
+ uint64_t InstAddress, BinaryBasicBlock *TargetBB) {
+ BinaryFunction *BF = BB->getParent();
+
+ // Use branch taken count for optimal relaxation.
+ const uint64_t Count = BB->getBranchInfo(*TargetBB).Count;
+ assert(Count != BinaryBasicBlock::COUNT_NO_PROFILE &&
+ "Expected valid branch execution count");
+
+ // Try to reuse an existing trampoline without introducing any new code.
+ BinaryBasicBlock *TrampolineBB = FragmentTrampolines.lookup(TargetBB);
+ if (TrampolineBB && isBlockInRange(Inst, InstAddress, *TrampolineBB)) {
+ BB->replaceSuccessor(TargetBB, TrampolineBB, Count);
+ TrampolineBB->setExecutionCount(TrampolineBB->getExecutionCount() +
+ Count);
+ auto L = BC.scopeLock();
+ MIB->replaceBranchTarget(Inst, TrampolineBB->getLabel(), BC.Ctx.get());
+ return;
+ }
+
+ // For cold branches, check if we can introduce a trampoline at the end
+ // of the fragment that is within the branch reach. Note that such
+ // trampoline may change address later and become unreachable in which
+ // case we will need further relaxation.
+ const int64_t OffsetToEnd = FragmentSize - InstAddress;
+ if (Count == 0 && isBranchOffsetInRange(Inst, OffsetToEnd)) {
+ TrampolineBB = addTrampolineAfter(nullptr, TargetBB, Count);
+ BB->replaceSuccessor(TargetBB, TrampolineBB, Count);
+ auto L = BC.scopeLock();
+ MIB->replaceBranchTarget(Inst, TrampolineBB->getLabel(), BC.Ctx.get());
+
+ return;
+ }
+
+ // Insert a new block after the current one and use it as a trampoline.
+ TrampolineBB = addTrampolineAfter(BB, TargetBB, Count);
+
+ // If the other successor is a fall-through, invert the condition code.
+ const BinaryBasicBlock *const NextBB =
+ BF->getLayout().getBasicBlockAfter(BB, /*IgnoreSplits*/ false);
+ if (BB->getConditionalSuccessor(false) == NextBB) {
+ BB->swapConditionalSuccessors();
+ auto L = BC.scopeLock();
+ MIB->reverseBranchCondition(Inst, NextBB->getLabel(), BC.Ctx.get());
+ } else {
+ auto L = BC.scopeLock();
+ MIB->replaceBranchTarget(Inst, TrampolineBB->getLabel(), BC.Ctx.get());
+ }
+ BB->replaceSuccessor(TargetBB, TrampolineBB, Count);
+ };
+
+ bool MayNeedRelaxation;
+ uint64_t NumIterations = 0;
+ do {
+ MayNeedRelaxation = false;
+ ++NumIterations;
+ for (auto BBI = FF.begin(); BBI != FF.end(); ++BBI) {
+ BinaryBasicBlock *BB = *BBI;
+ uint64_t NextInstOffset = BB->getOutputStartAddress();
+ for (MCInst &Inst : *BB) {
+ const size_t InstAddress = NextInstOffset;
+ if (!MIB->isPseudo(Inst))
+ NextInstOffset += 4;
+
+ if (!mayNeedStub(BF.getBinaryContext(), Inst))
+ continue;
+
+ const size_t BitsAvailable = MIB->getPCRelEncodingSize(Inst);
+
+ // Span of +/-128MB.
+ if (BitsAvailable == LongestJumpBits)
+ continue;
+
+ const MCSymbol *TargetSymbol = MIB->getTargetSymbol(Inst);
+ BinaryBasicBlock *TargetBB = BB->getSuccessor(TargetSymbol);
+ assert(TargetBB &&
+ "Basic block target expected for conditional branch.");
+
+ // Check if the relaxation is needed.
+ if (TargetBB->getFragmentNum() == FF.getFragmentNum() &&
+ isBlockInRange(Inst, InstAddress, *TargetBB))
+ continue;
+
+ relaxBranch(BB, Inst, InstAddress, TargetBB);
+
+ MayNeedRelaxation = true;
+ }
+ }
+
+ // We may have added new instructions, but the whole fragment is less than
+ // the minimum branch span.
+ if (FragmentSize < ShortestJumpSpan)
+ MayNeedRelaxation = false;
+
+ } while (MayNeedRelaxation);
+
+ LLVM_DEBUG({
+ if (NumIterations > 2) {
+ dbgs() << "BOLT-DEBUG: relaxed fragment " << FF.getFragmentNum().get()
+ << " of " << BF << " in " << NumIterations << " iterations\n";
+ }
+ });
+ (void)NumIterations;
+ }
+
+ // Add trampoline blocks from all fragments to the layout.
+ DenseMap<BinaryBasicBlock *, std::vector<std::unique_ptr<BinaryBasicBlock>>>
+ Insertions;
+ for (std::pair<BinaryBasicBlock *, std::unique_ptr<BinaryBasicBlock>> &Pair :
+ FunctionTrampolines) {
+ if (!Pair.second)
+ continue;
+ Insertions[Pair.first].emplace_back(std::move(Pair.second));
+ }
+
+ for (auto &Pair : Insertions) {
+ BF.insertBasicBlocks(Pair.first, std::move(Pair.second),
+ /*UpdateLayout*/ true, /*UpdateCFI*/ true,
+ /*RecomputeLPs*/ false);
+ }
+}
+
Error LongJmpPass::runOnFunctions(BinaryContext &BC) {
+
+ if (opts::CompactCodeModel) {
+ BC.outs()
+ << "BOLT-INFO: relaxing branches for compact code model (<128MB)\n";
+
+ ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
+ relaxLocalBranches(BF);
+ };
+
+ ParallelUtilities::PredicateTy SkipPredicate =
+ [&](const BinaryFunction &BF) {
+ return !BC.shouldEmit(BF) || !BF.isSimple();
+ };
+
+ ParallelUtilities::runOnEachFunction(
+ BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,
+ SkipPredicate, "RelaxLocalBranches");
+
+ return Error::success();
+ }
+
BC.outs() << "BOLT-INFO: Starting stub-insertion pass\n";
std::vector<BinaryFunction *> Sorted = BC.getSortedFunctions();
bool Modified;
bolt/lib/Passes/RetpolineInsertion.cpp
@@ -181,7 +181,6 @@ std::string createRetpolineFunctionTag(BinaryContext &BC,
if (BrInfo.isReg()) {
BC.InstPrinter->printRegName(TagOS, BrInfo.BranchReg);
TagOS << "_";
- TagOS.flush();
return Tag;
}
@@ -212,7 +211,6 @@ std::string createRetpolineFunctionTag(BinaryContext &BC,
BC.InstPrinter->printRegName(TagOS, MemRef.SegRegNum);
}
- TagOS.flush();
return Tag;
}
bolt/lib/Passes/VeneerElimination.cpp
@@ -73,12 +73,12 @@ Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
continue;
const MCSymbol *TargetSymbol = BC.MIB->getTargetSymbol(Instr, 0);
- if (VeneerDestinations.find(TargetSymbol) == VeneerDestinations.end())
+ auto It = VeneerDestinations.find(TargetSymbol);
+ if (It == VeneerDestinations.end())
continue;
VeneerCallers++;
- BC.MIB->replaceBranchTarget(Instr, VeneerDestinations[TargetSymbol],
- BC.Ctx.get());
+ BC.MIB->replaceBranchTarget(Instr, It->second, BC.Ctx.get());
}
}
}
bolt/lib/Profile/BoltAddressTranslation.cpp
@@ -143,8 +143,8 @@ void BoltAddressTranslation::write(const BinaryContext &BC, raw_ostream &OS) {
// Output addresses are delta-encoded
uint64_t PrevAddress = 0;
- writeMaps</*Cold=*/false>(Maps, PrevAddress, OS);
- writeMaps</*Cold=*/true>(Maps, PrevAddress, OS);
+ writeMaps</*Cold=*/false>(PrevAddress, OS);
+ writeMaps</*Cold=*/true>(PrevAddress, OS);
BC.outs() << "BOLT-INFO: Wrote " << Maps.size() << " BAT maps\n";
BC.outs() << "BOLT-INFO: Wrote " << FuncHashes.getNumFunctions()
@@ -182,8 +182,7 @@ size_t BoltAddressTranslation::getNumEqualOffsets(const MapTy &Map,
}
template <bool Cold>
-void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
- uint64_t &PrevAddress, raw_ostream &OS) {
+void BoltAddressTranslation::writeMaps(uint64_t &PrevAddress, raw_ostream &OS) {
const uint32_t NumFuncs =
llvm::count_if(llvm::make_first_range(Maps), [&](const uint64_t Address) {
return Cold == ColdPartSource.count(Address);
@@ -213,9 +212,9 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
: 0;
uint32_t Skew = 0;
if (Cold) {
- auto HotEntryIt = Maps.find(ColdPartSource[Address]);
- assert(HotEntryIt != Maps.end());
- size_t HotIndex = std::distance(Maps.begin(), HotEntryIt);
+ auto HotEntryIt = llvm::lower_bound(HotFuncs, ColdPartSource[Address]);
+ assert(HotEntryIt != HotFuncs.end());
+ size_t HotIndex = std::distance(HotFuncs.begin(), HotEntryIt);
encodeULEB128(HotIndex - PrevIndex, OS);
PrevIndex = HotIndex;
// Skew of all input offsets for cold fragments is simply the first input
@@ -223,6 +222,7 @@ void BoltAddressTranslation::writeMaps(std::map<uint64_t, MapTy> &Maps,
Skew = Map.begin()->second >> 1;
encodeULEB128(Skew, OS);
} else {
+ HotFuncs.push_back(Address);
// Function hash
size_t BFHash = getBFHash(HotInputAddress);
LLVM_DEBUG(dbgs() << "Hash: " << formatv("{0:x}\n", BFHash));
@@ -311,17 +311,15 @@ std::error_code BoltAddressTranslation::parse(raw_ostream &OS, StringRef Buf) {
return make_error_code(llvm::errc::io_error);
Error Err(Error::success());
- std::vector<uint64_t> HotFuncs;
uint64_t PrevAddress = 0;
- parseMaps</*Cold=*/false>(HotFuncs, PrevAddress, DE, Offset, Err);
- parseMaps</*Cold=*/true>(HotFuncs, PrevAddress, DE, Offset, Err);
+ parseMaps</*Cold=*/false>(PrevAddress, DE, Offset, Err);
+ parseMaps</*Cold=*/true>(PrevAddress, DE, Offset, Err);
OS << "BOLT-INFO: Parsed " << Maps.size() << " BAT entries\n";
return errorToErrorCode(std::move(Err));
}
template <bool Cold>
-void BoltAddressTranslation::parseMaps(std::vector<uint64_t> &HotFuncs,
- uint64_t &PrevAddress, DataExtractor &DE,
+void BoltAddressTranslation::parseMaps(uint64_t &PrevAddress, DataExtractor &DE,
uint64_t &Offset, Error &Err) {
const uint32_t NumFunctions = DE.getULEB128(&Offset, &Err);
LLVM_DEBUG(dbgs() << "Parsing " << NumFunctions << (Cold ? " cold" : "")
bolt/lib/Profile/DataAggregator.cpp
@@ -638,8 +638,12 @@ void DataAggregator::processProfile(BinaryContext &BC) {
: BinaryFunction::PF_LBR;
for (auto &BFI : BC.getBinaryFunctions()) {
BinaryFunction &BF = BFI.second;
- if (getBranchData(BF) || getFuncSampleData(BF.getNames()))
+ FuncBranchData *FBD = getBranchData(BF);
+ if (FBD || getFuncSampleData(BF.getNames())) {
BF.markProfiled(Flags);
+ if (FBD)
+ BF.RawBranchCount = FBD->getNumExecutedBranches();
+ }
}
for (auto &FuncBranches : NamesToBranches)
@@ -774,42 +778,75 @@ 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));
- }
+ uint64_t Mispreds, bool IsPreagg) {
+ // Returns whether \p Offset in \p Func contains a return instruction.
+ auto checkReturn = [&](const BinaryFunction &Func, const uint64_t Offset) {
+ auto isReturn = [&](auto MI) { return MI && BC->MIB->isReturn(*MI); };
+ return Func.hasInstructions()
+ ? isReturn(Func.getInstructionAtOffset(Offset))
+ : isReturn(Func.disassembleInstructionAtOffset(Offset));
+ };
- if (BAT)
- Addr = BAT->translate(Func->getAddress(), Addr, IsFrom);
+ // Returns whether \p Offset in \p Func may be a call continuation excluding
+ // entry points and landing pads.
+ auto checkCallCont = [&](const BinaryFunction &Func, const uint64_t Offset) {
+ // No call continuation at a function start.
+ if (!Offset)
+ return false;
+
+ // FIXME: support BAT case where the function might be in empty state
+ // (split fragments declared non-simple).
+ if (!Func.hasCFG())
+ return false;
+
+ // The offset should not be an entry point or a landing pad.
+ const BinaryBasicBlock *ContBB = Func.getBasicBlockAtOffset(Offset);
+ return ContBB && !ContBB->isEntryPoint() && !ContBB->isLandingPad();
+ };
- if (BinaryFunction *ParentFunc = getBATParentFunction(*Func)) {
- Func = ParentFunc;
- if (IsFrom)
- NumColdSamples += Count;
- }
+ // Mutates \p Addr to an offset into the containing function, performing BAT
+ // offset translation and parent lookup.
+ //
+ // Returns the containing function (or BAT parent) and whether the address
+ // corresponds to a return (if \p IsFrom) or a call continuation (otherwise).
+ auto handleAddress = [&](uint64_t &Addr, bool IsFrom) {
+ BinaryFunction *Func = getBinaryFunctionContainingAddress(Addr);
+ if (!Func)
+ return std::pair{Func, false};
- return Func;
- }
- return nullptr;
+ Addr -= Func->getAddress();
+
+ bool IsRetOrCallCont =
+ IsFrom ? checkReturn(*Func, Addr) : checkCallCont(*Func, Addr);
+
+ if (BAT)
+ Addr = BAT->translate(Func->getAddress(), Addr, IsFrom);
+
+ BinaryFunction *ParentFunc = getBATParentFunction(*Func);
+ if (!ParentFunc)
+ return std::pair{Func, IsRetOrCallCont};
+
+ if (IsFrom)
+ NumColdSamples += Count;
+
+ return std::pair{ParentFunc, IsRetOrCallCont};
};
- BinaryFunction *FromFunc = handleAddress(From, /*IsFrom=*/true);
+ uint64_t ToOrig = To;
+ auto [FromFunc, IsReturn] = handleAddress(From, /*IsFrom*/ true);
+ auto [ToFunc, IsCallCont] = handleAddress(To, /*IsFrom*/ false);
+ if (!FromFunc && !ToFunc)
+ return false;
+
+ // Record call to continuation trace.
+ if (IsPreagg && FromFunc != ToFunc && (IsReturn || IsCallCont)) {
+ LBREntry First{ToOrig - 1, ToOrig - 1, false};
+ LBREntry Second{ToOrig, ToOrig, false};
+ return doTrace(First, Second, Count);
+ }
// Ignore returns.
if (IsReturn)
return true;
- BinaryFunction *ToFunc = handleAddress(To, /*IsFrom=*/false);
- if (!FromFunc && !ToFunc)
- return false;
// Treat recursive control transfers as inter-branches.
if (FromFunc == ToFunc && To != 0) {
@@ -826,10 +863,19 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(Second.From);
if (!FromFunc || !ToFunc) {
LLVM_DEBUG({
- dbgs() << "Out of range trace starting in " << FromFunc->getPrintName()
- << formatv(" @ {0:x}", First.To - FromFunc->getAddress())
- << " and ending in " << ToFunc->getPrintName()
- << formatv(" @ {0:x}\n", Second.From - ToFunc->getAddress());
+ dbgs() << "Out of range trace starting in ";
+ if (FromFunc)
+ dbgs() << formatv("{0} @ {1:x}", *FromFunc,
+ First.To - FromFunc->getAddress());
+ else
+ dbgs() << Twine::utohexstr(First.To);
+ dbgs() << " and ending in ";
+ if (ToFunc)
+ dbgs() << formatv("{0} @ {1:x}", *ToFunc,
+ Second.From - ToFunc->getAddress());
+ else
+ dbgs() << Twine::utohexstr(Second.From);
+ dbgs() << '\n';
});
NumLongRangeTraces += Count;
return false;
@@ -845,6 +891,12 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
return false;
}
+ // Set ParentFunc to BAT parent function or FromFunc itself.
+ BinaryFunction *ParentFunc = getBATParentFunction(*FromFunc);
+ if (!ParentFunc)
+ ParentFunc = FromFunc;
+ ParentFunc->SampleCountInBytes += Count * (Second.From - First.To);
+
std::optional<BoltAddressTranslation::FallthroughListTy> FTs =
BAT ? BAT->getFallthroughsInTrace(FromFunc->getAddress(), First.To,
Second.From)
@@ -864,13 +916,12 @@ bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
<< FromFunc->getPrintName() << ":"
<< Twine::utohexstr(First.To) << " to "
<< Twine::utohexstr(Second.From) << ".\n");
- BinaryFunction *ParentFunc = getBATParentFunction(*FromFunc);
for (auto [From, To] : *FTs) {
if (BAT) {
From = BAT->translate(FromFunc->getAddress(), From, /*IsBranchSrc=*/true);
To = BAT->translate(FromFunc->getAddress(), To, /*IsBranchSrc=*/false);
}
- doIntraBranch(ParentFunc ? *ParentFunc : *FromFunc, From, To, Count, false);
+ doIntraBranch(*ParentFunc, From, To, Count, false);
}
return true;
@@ -1611,7 +1662,8 @@ void DataAggregator::processBranchEvents() {
for (const auto &AggrLBR : BranchLBRs) {
const Trace &Loc = AggrLBR.first;
const TakenBranchInfo &Info = AggrLBR.second;
- doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount);
+ doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount,
+ /*IsPreagg*/ false);
}
}
@@ -1772,7 +1824,7 @@ void DataAggregator::processPreAggregated() {
switch (AggrEntry.EntryType) {
case AggregatedLBREntry::BRANCH:
doBranch(AggrEntry.From.Offset, AggrEntry.To.Offset, AggrEntry.Count,
- AggrEntry.Mispreds);
+ AggrEntry.Mispreds, /*IsPreagg*/ true);
break;
case AggregatedLBREntry::FT:
case AggregatedLBREntry::FT_EXTERNAL_ORIGIN: {
@@ -2043,7 +2095,8 @@ std::error_code DataAggregator::parseMMapEvents() {
// size of the mapping, but we know it should not exceed the segment
// alignment value. Hence we are performing an approximate check.
return SegInfo.Address >= MMapInfo.MMapAddress &&
- SegInfo.Address - MMapInfo.MMapAddress < SegInfo.Alignment;
+ SegInfo.Address - MMapInfo.MMapAddress < SegInfo.Alignment &&
+ SegInfo.IsExecutable;
});
if (!MatchFound) {
errs() << "PERF2BOLT-WARNING: ignoring mapping of " << NameToUse
bolt/lib/Profile/StaleProfileMatching.cpp
@@ -228,23 +228,33 @@ public:
NO_MATCH
};
- /// Find the most similar flow block for a profile block given its hashes and
- /// pseudo probe information.
+ /// Find the most similar flow block for a profile block given blended hash.
std::pair<const FlowBlock *, MatchMethod>
- matchBlock(BlendedBlockHash BlendedHash, uint64_t CallHash,
- const ArrayRef<yaml::bolt::PseudoProbeInfo> PseudoProbes,
- const ArrayRef<yaml::bolt::InlineTreeInfo> InlineTree) {
+ matchBlockStrict(BlendedBlockHash BlendedHash) {
const auto &[Block, ExactHash] = matchWithOpcodes(BlendedHash);
if (Block && ExactHash)
return {Block, MATCH_EXACT};
+ return {nullptr, NO_MATCH};
+ }
+
+ /// Find the most similar flow block for a profile block given pseudo probes.
+ std::pair<const FlowBlock *, MatchMethod> matchBlockProbe(
+ const ArrayRef<yaml::bolt::PseudoProbeInfo> PseudoProbes,
+ const YAMLProfileReader::InlineTreeNodeMapTy &InlineTreeNodeMap) {
const auto &[ProbeBlock, ExactProbe] =
- matchWithPseudoProbes(PseudoProbes, InlineTree);
+ matchWithPseudoProbes(PseudoProbes, InlineTreeNodeMap);
if (ProbeBlock)
return {ProbeBlock, ExactProbe ? MATCH_PROBE_EXACT : MATCH_PROBE_LOOSE};
- if (const FlowBlock *BestBlock = matchWithCalls(BlendedHash, CallHash))
- return {BestBlock, MATCH_CALL};
- if (Block)
- return {Block, MATCH_OPCODE};
+ return {nullptr, NO_MATCH};
+ }
+
+ /// Find the most similar flow block for a profile block given its hashes.
+ std::pair<const FlowBlock *, MatchMethod>
+ matchBlockLoose(BlendedBlockHash BlendedHash, uint64_t CallHash) {
+ if (const FlowBlock *CallBlock = matchWithCalls(BlendedHash, CallHash))
+ return {CallBlock, MATCH_CALL};
+ if (const FlowBlock *OpcodeBlock = matchWithOpcodes(BlendedHash).first)
+ return {OpcodeBlock, MATCH_OPCODE};
return {nullptr, NO_MATCH};
}
@@ -256,28 +266,10 @@ public:
return Hash1.InstrHash == Hash2.InstrHash;
}
- /// Returns matched InlineTree * for a given profile inline_tree_id.
- const MCDecodedPseudoProbeInlineTree *
- getInlineTreeNode(uint32_t ProfileInlineTreeNodeId) const {
- auto It = InlineTreeNodeMap.find(ProfileInlineTreeNodeId);
- if (It == InlineTreeNodeMap.end())
- return nullptr;
- return It->second;
- }
-
- void mapInlineTreeNode(uint32_t ProfileNode,
- const MCDecodedPseudoProbeInlineTree *BinaryNode) {
- auto Res = InlineTreeNodeMap.try_emplace(ProfileNode, BinaryNode);
- assert(Res.second &&
- "Duplicate mapping from profile node index to binary inline tree");
- (void)Res;
- }
-
private:
using HashBlockPairType = std::pair<BlendedBlockHash, FlowBlock *>;
std::unordered_map<uint16_t, std::vector<HashBlockPairType>> OpHashToBlocks;
std::unordered_map<uint64_t, std::vector<HashBlockPairType>> CallHashToBlocks;
- DenseMap<uint32_t, const MCDecodedPseudoProbeInlineTree *> InlineTreeNodeMap;
DenseMap<const MCDecodedPseudoProbe *, FlowBlock *> BBPseudoProbeToBlock;
// Uses OpcodeHash to find the most similar block for a given hash.
@@ -322,72 +314,71 @@ private:
return BestBlock;
}
- /// Matches a profile block with an binary block based on pseudo probes.
+ /// Matches a profile block with a binary block based on pseudo probes.
/// Returns the best matching block (or nullptr) and whether the match is
/// unambiguous.
std::pair<const FlowBlock *, bool> matchWithPseudoProbes(
const ArrayRef<yaml::bolt::PseudoProbeInfo> BlockPseudoProbes,
- const ArrayRef<yaml::bolt::InlineTreeInfo> InlineTree) const {
+ const YAMLProfileReader::InlineTreeNodeMapTy &InlineTreeNodeMap) const {
+
if (!opts::StaleMatchingWithPseudoProbes)
return {nullptr, false};
DenseMap<const FlowBlock *, uint32_t> FlowBlockMatchCount;
- auto match = [&](uint32_t NodeId, uint64_t ProbeId) -> const FlowBlock * {
- const MCDecodedPseudoProbeInlineTree *Node = getInlineTreeNode(NodeId);
- if (!Node)
+ auto matchProfileProbeToBlock = [&](uint32_t NodeId,
+ uint64_t ProbeId) -> const FlowBlock * {
+ const MCDecodedPseudoProbeInlineTree *BinaryNode =
+ InlineTreeNodeMap.getInlineTreeNode(NodeId);
+ if (!BinaryNode)
return nullptr;
- const MCDecodedPseudoProbe *BinaryProbe = nullptr;
- for (const MCDecodedPseudoProbe &Probe : Node->getProbes()) {
- if (Probe.getIndex() != ProbeId)
- continue;
- BinaryProbe = &Probe;
- break;
- }
- if (!BinaryProbe)
+ const MCDecodedPseudoProbe Dummy(0, ProbeId, PseudoProbeType::Block, 0, 0,
+ nullptr);
+ ArrayRef<MCDecodedPseudoProbe> BinaryProbes = BinaryNode->getProbes();
+ auto BinaryProbeIt = llvm::lower_bound(
+ BinaryProbes, Dummy, [](const auto &LHS, const auto &RHS) {
+ return LHS.getIndex() < RHS.getIndex();
+ });
+ if (BinaryProbeIt == BinaryNode->getProbes().end() ||
+ BinaryProbeIt->getIndex() != ProbeId)
return nullptr;
- auto It = BBPseudoProbeToBlock.find(BinaryProbe);
+ auto It = BBPseudoProbeToBlock.find(&*BinaryProbeIt);
if (It == BBPseudoProbeToBlock.end())
return nullptr;
return It->second;
};
- auto matchProbe = [&](const yaml::bolt::PseudoProbeInfo &Probe,
- uint32_t NodeId, bool IsBlock1) {
- if (IsBlock1) {
- ++FlowBlockMatchCount[match(NodeId, 1)];
- return;
- }
+ auto matchPseudoProbeInfo = [&](const yaml::bolt::PseudoProbeInfo
+ &ProfileProbe,
+ uint32_t NodeId) {
for (uint64_t Index = 0; Index < 64; ++Index)
- if (Probe.BlockMask & 1ull << Index)
- ++FlowBlockMatchCount[match(NodeId, Index + 1)];
- for (const auto &Probes :
- {Probe.BlockProbes, Probe.IndCallProbes, Probe.CallProbes})
- for (uint64_t Probe : Probes)
- ++FlowBlockMatchCount[match(NodeId, Probe)];
+ if (ProfileProbe.BlockMask & 1ull << Index)
+ ++FlowBlockMatchCount[matchProfileProbeToBlock(NodeId, Index + 1)];
+ for (const auto &ProfileProbes :
+ {ProfileProbe.BlockProbes, ProfileProbe.IndCallProbes,
+ ProfileProbe.CallProbes})
+ for (uint64_t ProfileProbe : ProfileProbes)
+ ++FlowBlockMatchCount[matchProfileProbeToBlock(NodeId, ProfileProbe)];
};
- for (const yaml::bolt::PseudoProbeInfo &Probe : BlockPseudoProbes) {
- bool IsBlock1 = Probe.BlockMask == 0 && Probe.BlockProbes.empty() &&
- Probe.IndCallProbes.empty() && Probe.CallProbes.empty();
- if (!Probe.InlineTreeNodes.empty())
- for (uint32_t Node : Probe.InlineTreeNodes)
- matchProbe(Probe, Node, IsBlock1);
+ for (const yaml::bolt::PseudoProbeInfo &ProfileProbe : BlockPseudoProbes) {
+ if (!ProfileProbe.InlineTreeNodes.empty())
+ for (uint32_t ProfileInlineTreeNode : ProfileProbe.InlineTreeNodes)
+ matchPseudoProbeInfo(ProfileProbe, ProfileInlineTreeNode);
else
- matchProbe(Probe, Probe.InlineTreeIndex, IsBlock1);
+ matchPseudoProbeInfo(ProfileProbe, ProfileProbe.InlineTreeIndex);
}
uint32_t BestMatchCount = 0;
uint32_t TotalMatchCount = 0;
const FlowBlock *BestMatchBlock = nullptr;
- for (auto &[Block, Count] : FlowBlockMatchCount) {
+ for (const auto &[FlowBlock, Count] : FlowBlockMatchCount) {
TotalMatchCount += Count;
- if (Count > BestMatchCount ||
- (Count == BestMatchCount && !BestMatchBlock)) {
- BestMatchBlock = Block;
- BestMatchCount = Count;
- }
+ if (Count < BestMatchCount || (Count == BestMatchCount && BestMatchBlock))
+ continue;
+ BestMatchBlock = FlowBlock;
+ BestMatchCount = Count;
}
- return {BestMatchBlock, BestMatchCount / TotalMatchCount};
+ return {BestMatchBlock, BestMatchCount == TotalMatchCount};
}
};
@@ -565,17 +556,18 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
/// is to extract as much information from the stale profile as possible. Here
/// we assume that each basic block is specified via a hash value computed from
/// its content and the hashes of the unchanged basic blocks stay the same
-/// across different revisions of the binary.
+/// across different revisions of the binary. Blocks may also have pseudo probe
+/// information in the profile and the binary which is used for matching.
/// Whenever there is a count in the profile with the hash corresponding to one
/// of the basic blocks in the binary, the count is "matched" to the block.
/// Similarly, if both the source and the target of a count in the profile are
/// matched to a jump in the binary, the count is recorded in CFG.
-size_t matchWeightsByHashes(
+size_t matchWeights(
BinaryContext &BC, const BinaryFunction::BasicBlockOrderType &BlockOrder,
const yaml::bolt::BinaryFunctionProfile &YamlBF, FlowFunction &Func,
HashFunction HashFunction, YAMLProfileReader::ProfileLookupMap &IdToYamlBF,
- const BinaryFunction &BF, const yaml::bolt::PseudoProbeDesc &YamlPD,
- const YAMLProfileReader::GUIDInlineTreeMap &TopLevelGUIDToInlineTree) {
+ const BinaryFunction &BF,
+ const ArrayRef<YAMLProfileReader::ProbeMatchSpec> ProbeMatchSpecs) {
assert(Func.Blocks.size() == BlockOrder.size() + 2);
@@ -617,56 +609,55 @@ size_t matchWeightsByHashes(
if (const BinaryBasicBlock *BB =
BF.getBasicBlockContainingOffset(Probe.getAddress() - FuncAddr))
Matcher.mapProbeToBB(&Probe, Blocks[BB->getIndex()]);
-
- // Match inline tree nodes by GUID, checksum, parent, and call site.
- uint32_t ParentId = 0;
- uint32_t PrevGUIDIdx = 0;
- uint32_t Index = 0;
- for (const yaml::bolt::InlineTreeInfo &InlineTreeNode : YamlBF.InlineTree) {
- uint64_t GUIDIdx = InlineTreeNode.GUIDIndex;
- if (GUIDIdx)
- PrevGUIDIdx = GUIDIdx;
- else
- GUIDIdx = PrevGUIDIdx;
- assert(GUIDIdx - 1 < YamlPD.GUID.size());
- assert(GUIDIdx - 1 < YamlPD.GUIDHash.size());
- uint64_t GUID = YamlPD.GUID[GUIDIdx - 1];
- uint32_t HashIdx = YamlPD.GUIDHash[GUIDIdx - 1];
- assert(HashIdx < YamlPD.Hash.size());
- uint64_t Hash = YamlPD.Hash[HashIdx];
- uint32_t InlineTreeNodeId = Index++;
- ParentId += InlineTreeNode.ParentIndexDelta;
- uint32_t CallSiteProbe = InlineTreeNode.CallSiteProbe;
- const MCDecodedPseudoProbeInlineTree *Cur = nullptr;
- if (!InlineTreeNodeId) {
- auto It = TopLevelGUIDToInlineTree.find(GUID);
- if (It != TopLevelGUIDToInlineTree.end())
- Cur = It->second;
- } else if (const MCDecodedPseudoProbeInlineTree *Parent =
- Matcher.getInlineTreeNode(ParentId)) {
- for (const MCDecodedPseudoProbeInlineTree &Child :
- Parent->getChildren()) {
- if (Child.Guid == GUID) {
- if (std::get<1>(Child.getInlineSite()) == CallSiteProbe)
- Cur = &Child;
- break;
- }
- }
- }
- if (Cur && Decoder->getFuncDescForGUID(GUID)->FuncHash == Hash)
- Matcher.mapInlineTreeNode(InlineTreeNodeId, Cur);
- }
}
Matcher.init(Blocks, BlendedHashes, CallHashes);
- // Index in yaml profile => corresponding (matched) block
- DenseMap<uint64_t, const FlowBlock *> MatchedBlocks;
- // Match blocks from the profile to the blocks in CFG
+ using FlowBlockTy =
+ std::pair<const FlowBlock *, const yaml::bolt::BinaryBasicBlockProfile *>;
+ using ProfileBlockMatchMap = DenseMap<uint32_t, FlowBlockTy>;
+ // Binary profile => block index => matched block + its block profile
+ DenseMap<const yaml::bolt::BinaryFunctionProfile *, ProfileBlockMatchMap>
+ MatchedBlocks;
+
+ // Map of FlowBlock and matching method.
+ DenseMap<const FlowBlock *, StaleMatcher::MatchMethod> MatchedFlowBlocks;
+
+ auto addMatchedBlock =
+ [&](std::pair<const FlowBlock *, StaleMatcher::MatchMethod> BlockMethod,
+ const yaml::bolt::BinaryFunctionProfile &YamlBP,
+ const yaml::bolt::BinaryBasicBlockProfile &YamlBB) {
+ const auto &[MatchedBlock, Method] = BlockMethod;
+ if (!MatchedBlock)
+ return;
+ // Don't override earlier matches
+ if (MatchedFlowBlocks.contains(MatchedBlock))
+ return;
+ MatchedFlowBlocks.try_emplace(MatchedBlock, Method);
+ MatchedBlocks[&YamlBP][YamlBB.Index] = {MatchedBlock, &YamlBB};
+ };
+
+ // Match blocks from the profile to the blocks in CFG by strict hash.
+ for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF.Blocks) {
+ // Update matching stats.
+ ++BC.Stats.NumStaleBlocks;
+ BC.Stats.StaleSampleCount += YamlBB.ExecCount;
+
+ assert(YamlBB.Hash != 0 && "empty hash of BinaryBasicBlockProfile");
+ BlendedBlockHash YamlHash(YamlBB.Hash);
+ addMatchedBlock(Matcher.matchBlockStrict(YamlHash), YamlBF, YamlBB);
+ }
+ // Match blocks from the profile to the blocks in CFG by pseudo probes.
+ for (const auto &[InlineNodeMap, YamlBP] : ProbeMatchSpecs) {
+ for (const yaml::bolt::BinaryBasicBlockProfile &BB : YamlBP.get().Blocks)
+ if (!BB.PseudoProbes.empty())
+ addMatchedBlock(Matcher.matchBlockProbe(BB.PseudoProbes, InlineNodeMap),
+ YamlBP, BB);
+ }
+ // Match blocks from the profile to the blocks in CFG with loose methods.
for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF.Blocks) {
assert(YamlBB.Hash != 0 && "empty hash of BinaryBasicBlockProfile");
BlendedBlockHash YamlHash(YamlBB.Hash);
- const FlowBlock *MatchedBlock = nullptr;
std::string CallHashStr = hashBlockCalls(IdToYamlBF, YamlBB);
uint64_t CallHash = 0;
if (!CallHashStr.empty()) {
@@ -677,103 +668,103 @@ size_t matchWeightsByHashes(
else
llvm_unreachable("Unhandled HashFunction");
}
- StaleMatcher::MatchMethod Method;
- std::tie(MatchedBlock, Method) = Matcher.matchBlock(
- YamlHash, CallHash, YamlBB.PseudoProbes, YamlBF.InlineTree);
+ auto [MatchedBlock, Method] = Matcher.matchBlockLoose(YamlHash, CallHash);
if (MatchedBlock == nullptr && YamlBB.Index == 0) {
MatchedBlock = Blocks[0];
// Report as loose match
Method = StaleMatcher::MATCH_OPCODE;
}
- if (MatchedBlock != nullptr) {
- const BinaryBasicBlock *BB = BlockOrder[MatchedBlock->Index - 1];
- MatchedBlocks[YamlBB.Index] = MatchedBlock;
+ if (!MatchedBlock) {
+ LLVM_DEBUG(dbgs() << "Couldn't match yaml block (bid = " << YamlBB.Index
+ << ")" << " with hash " << Twine::utohexstr(YamlBB.Hash)
+ << "\n");
+ continue;
+ }
+ addMatchedBlock({MatchedBlock, Method}, YamlBF, YamlBB);
+ }
+
+ // Match jumps from the profile to the jumps from CFG
+ std::vector<uint64_t> OutWeight(Func.Blocks.size(), 0);
+ std::vector<uint64_t> InWeight(Func.Blocks.size(), 0);
+
+ for (const auto &[YamlBF, MatchMap] : MatchedBlocks) {
+ for (const auto &[YamlBBIdx, FlowBlockProfile] : MatchMap) {
+ const auto &[MatchedBlock, YamlBB] = FlowBlockProfile;
+ StaleMatcher::MatchMethod Method = MatchedFlowBlocks.lookup(MatchedBlock);
BlendedBlockHash BinHash = BlendedHashes[MatchedBlock->Index - 1];
- LLVM_DEBUG(dbgs() << "Matched yaml block (bid = " << YamlBB.Index << ")"
- << " with hash " << Twine::utohexstr(YamlBB.Hash)
+ LLVM_DEBUG(dbgs() << "Matched yaml block (bid = " << YamlBBIdx << ")"
+ << " with hash " << Twine::utohexstr(YamlBB->Hash)
<< " to BB (index = " << MatchedBlock->Index - 1 << ")"
<< " with hash " << Twine::utohexstr(BinHash.combine())
<< "\n");
+ uint64_t ExecCount = YamlBB->ExecCount;
// Update matching stats accounting for the matched block.
switch (Method) {
case StaleMatcher::MATCH_EXACT:
++BC.Stats.NumExactMatchedBlocks;
- BC.Stats.ExactMatchedSampleCount += YamlBB.ExecCount;
+ BC.Stats.ExactMatchedSampleCount += ExecCount;
LLVM_DEBUG(dbgs() << " exact match\n");
break;
case StaleMatcher::MATCH_PROBE_EXACT:
++BC.Stats.NumPseudoProbeExactMatchedBlocks;
- BC.Stats.PseudoProbeExactMatchedSampleCount += YamlBB.ExecCount;
+ BC.Stats.PseudoProbeExactMatchedSampleCount += ExecCount;
LLVM_DEBUG(dbgs() << " exact pseudo probe match\n");
break;
case StaleMatcher::MATCH_PROBE_LOOSE:
++BC.Stats.NumPseudoProbeLooseMatchedBlocks;
- BC.Stats.PseudoProbeLooseMatchedSampleCount += YamlBB.ExecCount;
+ BC.Stats.PseudoProbeLooseMatchedSampleCount += ExecCount;
LLVM_DEBUG(dbgs() << " loose pseudo probe match\n");
break;
case StaleMatcher::MATCH_CALL:
++BC.Stats.NumCallMatchedBlocks;
- BC.Stats.CallMatchedSampleCount += YamlBB.ExecCount;
+ BC.Stats.CallMatchedSampleCount += ExecCount;
LLVM_DEBUG(dbgs() << " call match\n");
break;
case StaleMatcher::MATCH_OPCODE:
++BC.Stats.NumLooseMatchedBlocks;
- BC.Stats.LooseMatchedSampleCount += YamlBB.ExecCount;
+ BC.Stats.LooseMatchedSampleCount += ExecCount;
LLVM_DEBUG(dbgs() << " loose match\n");
break;
case StaleMatcher::NO_MATCH:
LLVM_DEBUG(dbgs() << " no match\n");
}
- if (YamlBB.NumInstructions == BB->size())
- ++BC.Stats.NumStaleBlocksWithEqualIcount;
- } else {
- LLVM_DEBUG(
- dbgs() << "Couldn't match yaml block (bid = " << YamlBB.Index << ")"
- << " with hash " << Twine::utohexstr(YamlBB.Hash) << "\n");
}
- // Update matching stats.
- ++BC.Stats.NumStaleBlocks;
- BC.Stats.StaleSampleCount += YamlBB.ExecCount;
- }
-
- // Match jumps from the profile to the jumps from CFG
- std::vector<uint64_t> OutWeight(Func.Blocks.size(), 0);
- std::vector<uint64_t> InWeight(Func.Blocks.size(), 0);
- for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF.Blocks) {
- for (const yaml::bolt::SuccessorInfo &YamlSI : YamlBB.Successors) {
- if (YamlSI.Count == 0)
- continue;
+ for (const yaml::bolt::BinaryBasicBlockProfile &YamlBB : YamlBF->Blocks) {
+ for (const yaml::bolt::SuccessorInfo &YamlSI : YamlBB.Successors) {
+ if (YamlSI.Count == 0)
+ continue;
- // Try to find the jump for a given (src, dst) pair from the profile and
- // assign the jump weight based on the profile count
- const uint64_t SrcIndex = YamlBB.Index;
- const uint64_t DstIndex = YamlSI.Index;
-
- const FlowBlock *MatchedSrcBlock = MatchedBlocks.lookup(SrcIndex);
- const FlowBlock *MatchedDstBlock = MatchedBlocks.lookup(DstIndex);
-
- if (MatchedSrcBlock != nullptr && MatchedDstBlock != nullptr) {
- // Find a jump between the two blocks
- FlowJump *Jump = nullptr;
- for (FlowJump *SuccJump : MatchedSrcBlock->SuccJumps) {
- if (SuccJump->Target == MatchedDstBlock->Index) {
- Jump = SuccJump;
- break;
+ // Try to find the jump for a given (src, dst) pair from the profile and
+ // assign the jump weight based on the profile count
+ const uint64_t SrcIndex = YamlBB.Index;
+ const uint64_t DstIndex = YamlSI.Index;
+
+ const FlowBlock *MatchedSrcBlock = MatchMap.lookup(SrcIndex).first;
+ const FlowBlock *MatchedDstBlock = MatchMap.lookup(DstIndex).first;
+
+ if (MatchedSrcBlock != nullptr && MatchedDstBlock != nullptr) {
+ // Find a jump between the two blocks
+ FlowJump *Jump = nullptr;
+ for (FlowJump *SuccJump : MatchedSrcBlock->SuccJumps) {
+ if (SuccJump->Target == MatchedDstBlock->Index) {
+ Jump = SuccJump;
+ break;
+ }
+ }
+ // Assign the weight, if the corresponding jump is found
+ if (Jump != nullptr) {
+ Jump->Weight = YamlSI.Count;
+ Jump->HasUnknownWeight = false;
}
}
- // Assign the weight, if the corresponding jump is found
- if (Jump != nullptr) {
- Jump->Weight = YamlSI.Count;
- Jump->HasUnknownWeight = false;
- }
+ // Assign the weight for the src block, if it is found
+ if (MatchedSrcBlock != nullptr)
+ OutWeight[MatchedSrcBlock->Index] += YamlSI.Count;
+ // Assign the weight for the dst block, if it is found
+ if (MatchedDstBlock != nullptr)
+ InWeight[MatchedDstBlock->Index] += YamlSI.Count;
}
- // Assign the weight for the src block, if it is found
- if (MatchedSrcBlock != nullptr)
- OutWeight[MatchedSrcBlock->Index] += YamlSI.Count;
- // Assign the weight for the dst block, if it is found
- if (MatchedDstBlock != nullptr)
- InWeight[MatchedDstBlock->Index] += YamlSI.Count;
}
}
@@ -787,7 +778,7 @@ size_t matchWeightsByHashes(
Block.Weight = std::max(OutWeight[Block.Index], InWeight[Block.Index]);
}
- return MatchedBlocks.size();
+ return MatchedBlocks[&YamlBF].size();
}
/// The function finds all blocks that are (i) reachable from the Entry block
@@ -1005,7 +996,8 @@ void assignProfile(BinaryFunction &BF,
}
bool YAMLProfileReader::inferStaleProfile(
- BinaryFunction &BF, const yaml::bolt::BinaryFunctionProfile &YamlBF) {
+ BinaryFunction &BF, const yaml::bolt::BinaryFunctionProfile &YamlBF,
+ const ArrayRef<ProbeMatchSpec> ProbeMatchSpecs) {
NamedRegionTimer T("inferStaleProfile", "stale profile inference", "rewrite",
"Rewrite passes", opts::TimeRewrite);
@@ -1029,9 +1021,8 @@ bool YAMLProfileReader::inferStaleProfile(
// Match as many block/jump counts from the stale profile as possible
size_t MatchedBlocks =
- matchWeightsByHashes(BF.getBinaryContext(), BlockOrder, YamlBF, Func,
- YamlBP.Header.HashFunction, IdToYamLBF, BF,
- YamlBP.PseudoProbeDesc, TopLevelGUIDToInlineTree);
+ matchWeights(BF.getBinaryContext(), BlockOrder, YamlBF, Func,
+ YamlBP.Header.HashFunction, IdToYamLBF, BF, ProbeMatchSpecs);
// Adjust the flow function by marking unreachable blocks Unlikely so that
// they don't get any counts assigned.
bolt/lib/Profile/YAMLProfileReader.cpp
@@ -241,9 +241,7 @@ bool YAMLProfileReader::parseFunctionProfile(
BB.setExecutionCount(YamlBB.ExecCount);
for (const yaml::bolt::CallSiteInfo &YamlCSI : YamlBB.CallSites) {
- BinaryFunction *Callee = YamlCSI.DestId < YamlProfileToFunction.size()
- ? YamlProfileToFunction[YamlCSI.DestId]
- : nullptr;
+ BinaryFunction *Callee = YamlProfileToFunction.lookup(YamlCSI.DestId);
bool IsFunction = Callee ? true : false;
MCSymbol *CalleeSymbol = nullptr;
if (IsFunction)
@@ -354,8 +352,13 @@ bool YAMLProfileReader::parseFunctionProfile(
if (YamlBF.NumBasicBlocks != BF.size())
++BC.Stats.NumStaleFuncsWithEqualBlockCount;
- if (opts::InferStaleProfile && inferStaleProfile(BF, YamlBF))
- ProfileMatched = true;
+ if (!opts::InferStaleProfile)
+ return false;
+ ArrayRef<ProbeMatchSpec> ProbeMatchSpecs;
+ auto BFIt = BFToProbeMatchSpecs.find(&BF);
+ if (BFIt != BFToProbeMatchSpecs.end())
+ ProbeMatchSpecs = BFIt->second;
+ ProfileMatched = inferStaleProfile(BF, YamlBF, ProbeMatchSpecs);
}
if (ProfileMatched)
BF.markProfiled(YamlBP.Header.Flags);
@@ -590,6 +593,101 @@ size_t YAMLProfileReader::matchWithCallGraph(BinaryContext &BC) {
return MatchedWithCallGraph;
}
+size_t YAMLProfileReader::InlineTreeNodeMapTy::matchInlineTrees(
+ const MCPseudoProbeDecoder &Decoder,
+ const std::vector<yaml::bolt::InlineTreeNode> &DecodedInlineTree,
+ const MCDecodedPseudoProbeInlineTree *Root) {
+ // Match inline tree nodes by GUID, checksum, parent, and call site.
+ for (const auto &[InlineTreeNodeId, InlineTreeNode] :
+ llvm::enumerate(DecodedInlineTree)) {
+ uint64_t GUID = InlineTreeNode.GUID;
+ uint64_t Hash = InlineTreeNode.Hash;
+ uint32_t ParentId = InlineTreeNode.ParentIndexDelta;
+ uint32_t CallSiteProbe = InlineTreeNode.CallSiteProbe;
+ const MCDecodedPseudoProbeInlineTree *Cur = nullptr;
+ if (!InlineTreeNodeId) {
+ Cur = Root;
+ } else if (const MCDecodedPseudoProbeInlineTree *Parent =
+ getInlineTreeNode(ParentId)) {
+ for (const MCDecodedPseudoProbeInlineTree &Child :
+ Parent->getChildren()) {
+ if (Child.Guid == GUID) {
+ if (std::get<1>(Child.getInlineSite()) == CallSiteProbe)
+ Cur = &Child;
+ break;
+ }
+ }
+ }
+ // Don't match nodes if the profile is stale (mismatching binary FuncHash
+ // and YAML Hash)
+ if (Cur && Decoder.getFuncDescForGUID(Cur->Guid)->FuncHash == Hash)
+ mapInlineTreeNode(InlineTreeNodeId, Cur);
+ }
+ return Map.size();
+}
+
+// Decode index deltas and indirection through \p YamlPD. Return modified copy
+// of \p YamlInlineTree with populated decoded fields (GUID, Hash, ParentIndex).
+static std::vector<yaml::bolt::InlineTreeNode>
+decodeYamlInlineTree(const yaml::bolt::ProfilePseudoProbeDesc &YamlPD,
+ std::vector<yaml::bolt::InlineTreeNode> YamlInlineTree) {
+ uint32_t ParentId = 0;
+ uint32_t PrevGUIDIdx = 0;
+ for (yaml::bolt::InlineTreeNode &InlineTreeNode : YamlInlineTree) {
+ uint32_t GUIDIdx = InlineTreeNode.GUIDIndex;
+ if (GUIDIdx != UINT32_MAX)
+ PrevGUIDIdx = GUIDIdx;
+ else
+ GUIDIdx = PrevGUIDIdx;
+ uint32_t HashIdx = YamlPD.GUIDHashIdx[GUIDIdx];
+ ParentId += InlineTreeNode.ParentIndexDelta;
+ InlineTreeNode.GUID = YamlPD.GUID[GUIDIdx];
+ InlineTreeNode.Hash = YamlPD.Hash[HashIdx];
+ InlineTreeNode.ParentIndexDelta = ParentId;
+ }
+ return YamlInlineTree;
+}
+
+size_t YAMLProfileReader::matchWithPseudoProbes(BinaryContext &BC) {
+ if (!opts::StaleMatchingWithPseudoProbes)
+ return 0;
+
+ const MCPseudoProbeDecoder *Decoder = BC.getPseudoProbeDecoder();
+ const yaml::bolt::ProfilePseudoProbeDesc &YamlPD = YamlBP.PseudoProbeDesc;
+
+ // Set existing BF->YamlBF match into ProbeMatchSpecs for (local) probe
+ // matching.
+ assert(Decoder &&
+ "If pseudo probes are in use, pseudo probe decoder should exist");
+ for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs)) {
+ // BF is preliminary name-matched function to YamlBF
+ // MatchedBF is final matched function
+ BinaryFunction *MatchedBF = YamlProfileToFunction.lookup(YamlBF.Id);
+ if (!BF)
+ BF = MatchedBF;
+ if (!BF)
+ continue;
+ uint64_t GUID = BF->getGUID();
+ if (!GUID)
+ continue;
+ auto It = TopLevelGUIDToInlineTree.find(GUID);
+ if (It == TopLevelGUIDToInlineTree.end())
+ continue;
+ const MCDecodedPseudoProbeInlineTree *Node = It->second;
+ assert(Node && "Malformed TopLevelGUIDToInlineTree");
+ auto &MatchSpecs = BFToProbeMatchSpecs[BF];
+ auto &InlineTreeMap =
+ MatchSpecs.emplace_back(InlineTreeNodeMapTy(), YamlBF).first;
+ std::vector<yaml::bolt::InlineTreeNode> ProfileInlineTree =
+ decodeYamlInlineTree(YamlPD, YamlBF.InlineTree);
+ // Erase unsuccessful match
+ if (!InlineTreeMap.matchInlineTrees(*Decoder, ProfileInlineTree, Node))
+ MatchSpecs.pop_back();
+ }
+
+ return 0;
+}
+
size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) {
if (opts::NameSimilarityFunctionMatchingThreshold == 0)
return 0;
@@ -646,11 +744,7 @@ size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) {
// equal number of blocks.
if (NamespaceToProfiledBFSizesIt->second.count(BF->size()) == 0)
continue;
- auto NamespaceToBFsIt = NamespaceToBFs.find(Namespace);
- if (NamespaceToBFsIt == NamespaceToBFs.end())
- NamespaceToBFs[Namespace] = {BF};
- else
- NamespaceToBFsIt->second.push_back(BF);
+ NamespaceToBFs[Namespace].push_back(BF);
}
// Iterates through all profiled functions and binary functions belonging to
@@ -710,7 +804,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
break;
}
}
- YamlProfileToFunction.resize(YamlBP.Functions.size() + 1);
+ YamlProfileToFunction.reserve(YamlBP.Functions.size());
// Computes hash for binary functions.
if (opts::MatchProfileWithFunctionHash) {
@@ -743,6 +837,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
const size_t MatchedWithLTOCommonName = matchWithLTOCommonName();
const size_t MatchedWithCallGraph = matchWithCallGraph(BC);
const size_t MatchedWithNameSimilarity = matchWithNameSimilarity(BC);
+ const size_t MatchedWithPseudoProbes = matchWithPseudoProbes(BC);
for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs))
if (!YamlBF.Used && BF && !ProfiledFunctions.count(BF))
@@ -772,12 +867,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
NormalizeByCalls = usesEvent("branches");
uint64_t NumUnused = 0;
for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions) {
- if (YamlBF.Id >= YamlProfileToFunction.size()) {
- // Such profile was ignored.
- ++NumUnused;
- continue;
- }
- if (BinaryFunction *BF = YamlProfileToFunction[YamlBF.Id])
+ if (BinaryFunction *BF = YamlProfileToFunction.lookup(YamlBF.Id))
parseFunctionProfile(*BF, YamlBF);
else
++NumUnused;
bolt/lib/Profile/YAMLProfileWriter.cpp
@@ -18,7 +18,6 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
-#include <queue>
#undef DEBUG_TYPE
#define DEBUG_TYPE "bolt-prof"
@@ -61,32 +60,31 @@ const BinaryFunction *YAMLProfileWriter::setCSIDestination(
}
std::vector<YAMLProfileWriter::InlineTreeNode>
-YAMLProfileWriter::getInlineTree(const MCPseudoProbeDecoder &Decoder,
- const MCDecodedPseudoProbeInlineTree *Root) {
+YAMLProfileWriter::collectInlineTree(
+ const MCPseudoProbeDecoder &Decoder,
+ const MCDecodedPseudoProbeInlineTree &Root) {
auto getHash = [&](const MCDecodedPseudoProbeInlineTree &Node) {
return Decoder.getFuncDescForGUID(Node.Guid)->FuncHash;
};
- assert(Root);
- std::vector<InlineTreeNode> InlineTree;
- InlineTreeNode Node{Root, Root->Guid, getHash(*Root), 0, 0};
- InlineTree.emplace_back(Node);
+ std::vector<InlineTreeNode> InlineTree(
+ {InlineTreeNode{&Root, Root.Guid, getHash(Root), 0, 0}});
uint32_t ParentId = 0;
while (ParentId != InlineTree.size()) {
const MCDecodedPseudoProbeInlineTree *Cur = InlineTree[ParentId].InlineTree;
- for (const MCDecodedPseudoProbeInlineTree &Child : Cur->getChildren()) {
- InlineTreeNode Node{&Child, Child.Guid, getHash(Child), ParentId,
- std::get<1>(Child.getInlineSite())};
- InlineTree.emplace_back(Node);
- }
+ for (const MCDecodedPseudoProbeInlineTree &Child : Cur->getChildren())
+ InlineTree.emplace_back(
+ InlineTreeNode{&Child, Child.Guid, getHash(Child), ParentId,
+ std::get<1>(Child.getInlineSite())});
++ParentId;
}
return InlineTree;
}
-std::tuple<yaml::bolt::PseudoProbeDesc, YAMLProfileWriter::InlineTreeDesc>
+std::tuple<yaml::bolt::ProfilePseudoProbeDesc,
+ YAMLProfileWriter::InlineTreeDesc>
YAMLProfileWriter::convertPseudoProbeDesc(const MCPseudoProbeDecoder &Decoder) {
- yaml::bolt::PseudoProbeDesc Desc;
+ yaml::bolt::ProfilePseudoProbeDesc Desc;
InlineTreeDesc InlineTree;
for (const MCDecodedPseudoProbeInlineTree &TopLev :
@@ -125,7 +123,7 @@ YAMLProfileWriter::convertPseudoProbeDesc(const MCPseudoProbeDecoder &Decoder) {
Desc.GUID.emplace_back(GUID);
InlineTree.GUIDIdxMap[GUID] = Index++;
uint64_t Hash = Decoder.getFuncDescForGUID(GUID)->FuncHash;
- Desc.GUIDHash.emplace_back(InlineTree.HashIdxMap[Hash]);
+ Desc.GUIDHashIdx.emplace_back(InlineTree.HashIdxMap[Hash]);
}
return {Desc, InlineTree};
@@ -176,39 +174,35 @@ YAMLProfileWriter::convertNodeProbes(NodeIdToProbes &NodeProbes) {
else
YamlBPI.InlineTreeNodes = Nodes;
handleMask(BPI.BlockProbes, YamlBPI.BlockProbes, YamlBPI.BlockMask);
- // Assume BlockMask == 1 if no other probes are set
- if (YamlBPI.BlockMask == 1 && YamlBPI.CallProbes.empty() &&
- YamlBPI.IndCallProbes.empty())
- YamlBPI.BlockMask = 0;
}
return YamlProbes;
}
-std::tuple<std::vector<yaml::bolt::InlineTreeInfo>,
+std::tuple<std::vector<yaml::bolt::InlineTreeNode>,
YAMLProfileWriter::InlineTreeMapTy>
YAMLProfileWriter::convertBFInlineTree(const MCPseudoProbeDecoder &Decoder,
const InlineTreeDesc &InlineTree,
uint64_t GUID) {
DenseMap<const MCDecodedPseudoProbeInlineTree *, uint32_t> InlineTreeNodeId;
- std::vector<yaml::bolt::InlineTreeInfo> YamlInlineTree;
+ std::vector<yaml::bolt::InlineTreeNode> YamlInlineTree;
auto It = InlineTree.TopLevelGUIDToInlineTree.find(GUID);
if (It == InlineTree.TopLevelGUIDToInlineTree.end())
return {YamlInlineTree, InlineTreeNodeId};
const MCDecodedPseudoProbeInlineTree *Root = It->second;
- assert(Root);
+ assert(Root && "Malformed TopLevelGUIDToInlineTree");
uint32_t Index = 0;
uint32_t PrevParent = 0;
uint32_t PrevGUIDIdx = 0;
- for (const auto &Node : getInlineTree(Decoder, Root)) {
+ for (const auto &Node : collectInlineTree(Decoder, *Root)) {
InlineTreeNodeId[Node.InlineTree] = Index++;
auto GUIDIdxIt = InlineTree.GUIDIdxMap.find(Node.GUID);
- assert(GUIDIdxIt != InlineTree.GUIDIdxMap.end());
- uint32_t GUIDIdx = GUIDIdxIt->second + 1;
+ assert(GUIDIdxIt != InlineTree.GUIDIdxMap.end() && "Malformed GUIDIdxMap");
+ uint32_t GUIDIdx = GUIDIdxIt->second;
if (GUIDIdx == PrevGUIDIdx)
- GUIDIdx = 0;
+ GUIDIdx = UINT32_MAX;
else
PrevGUIDIdx = GUIDIdx;
- YamlInlineTree.emplace_back(yaml::bolt::InlineTreeInfo{
+ YamlInlineTree.emplace_back(yaml::bolt::InlineTreeNode{
Node.ParentId - PrevParent, Node.InlineSite, GUIDIdx});
PrevParent = Node.ParentId;
}
bolt/lib/Rewrite/BinaryPassManager.cpp
@@ -12,6 +12,7 @@
#include "bolt/Passes/AllocCombiner.h"
#include "bolt/Passes/AsmDump.h"
#include "bolt/Passes/CMOVConversion.h"
+#include "bolt/Passes/ContinuityStats.h"
#include "bolt/Passes/FixRISCVCallsPass.h"
#include "bolt/Passes/FixRelaxationPass.h"
#include "bolt/Passes/FrameOptimizer.h"
@@ -373,6 +374,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
if (opts::PrintProfileStats)
Manager.registerPass(std::make_unique<PrintProfileStats>(NeverPrint));
+ Manager.registerPass(std::make_unique<PrintContinuityStats>(NeverPrint));
+
Manager.registerPass(std::make_unique<ValidateInternalCalls>(NeverPrint));
Manager.registerPass(std::make_unique<ValidateMemRefs>(NeverPrint));
bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -1362,7 +1362,7 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
Die.getTag() == dwarf::DW_TAG_compile_unit)) {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: cannot update ranges for DIE in Unit offset 0x"
- << Unit.getOffset() << '\n';
+ << Twine::utohexstr(Unit.getOffset()) << '\n';
}
}
bolt/lib/Rewrite/RewriteInstance.cpp
@@ -526,11 +526,9 @@ Error RewriteInstance::discoverStorage() {
NextAvailableOffset = std::max(NextAvailableOffset,
Phdr.p_offset + Phdr.p_filesz);
- BC->SegmentMapInfo[Phdr.p_vaddr] = SegmentInfo{Phdr.p_vaddr,
- Phdr.p_memsz,
- Phdr.p_offset,
- Phdr.p_filesz,
- Phdr.p_align};
+ BC->SegmentMapInfo[Phdr.p_vaddr] = SegmentInfo{
+ Phdr.p_vaddr, Phdr.p_memsz, Phdr.p_offset,
+ Phdr.p_filesz, Phdr.p_align, ((Phdr.p_flags & ELF::PF_X) != 0)};
if (BC->TheTriple->getArch() == llvm::Triple::x86_64 &&
Phdr.p_vaddr >= BinaryContext::KernelStartX86_64)
BC->IsLinuxKernel = true;
@@ -791,9 +789,44 @@ void RewriteInstance::discoverFileObjects() {
BinarySection Section(*BC, *cantFail(Sym.getSection()));
return Section.isAllocatable();
};
+ auto checkSymbolInSection = [this](const SymbolInfo &S) {
+ // Sometimes, we encounter symbols with addresses outside their section. If
+ // such symbols happen to fall into another section, they can interfere with
+ // disassembly. Notably, this occurs with AArch64 marker symbols ($d and $t)
+ // that belong to .eh_frame, but end up pointing into .text.
+ // As a workaround, we ignore all symbols that lie outside their sections.
+ auto Section = cantFail(S.Symbol.getSection());
+
+ // Accept all absolute symbols.
+ if (Section == InputFile->section_end())
+ return true;
+
+ uint64_t SecStart = Section->getAddress();
+ uint64_t SecEnd = SecStart + Section->getSize();
+ uint64_t SymEnd = S.Address + ELFSymbolRef(S.Symbol).getSize();
+ if (S.Address >= SecStart && SymEnd <= SecEnd)
+ return true;
+
+ auto SymType = cantFail(S.Symbol.getType());
+ // Skip warnings for common benign cases.
+ if (opts::Verbosity < 1 && SymType == SymbolRef::ST_Other)
+ return false; // E.g. ELF::STT_TLS.
+
+ auto SymName = S.Symbol.getName();
+ auto SecName = cantFail(S.Symbol.getSection())->getName();
+ BC->errs() << "BOLT-WARNING: ignoring symbol "
+ << (SymName ? *SymName : "[unnamed]") << " at 0x"
+ << Twine::utohexstr(S.Address) << ", which lies outside "
+ << (SecName ? *SecName : "[unnamed]") << "\n";
+
+ return false;
+ };
for (const SymbolRef &Symbol : InputFile->symbols())
- if (isSymbolInMemory(Symbol))
- SortedSymbols.push_back({cantFail(Symbol.getAddress()), Symbol});
+ if (isSymbolInMemory(Symbol)) {
+ SymbolInfo SymInfo{cantFail(Symbol.getAddress()), Symbol};
+ if (checkSymbolInSection(SymInfo))
+ SortedSymbols.push_back(SymInfo);
+ }
auto CompareSymbols = [this](const SymbolInfo &A, const SymbolInfo &B) {
if (A.Address != B.Address)
@@ -1533,7 +1566,7 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress,
MCSymbol *Symbol = Rel->Symbol;
if (!Symbol) {
- if (!BC->isAArch64() || !Rel->Addend || !Rel->isIRelative())
+ if (BC->isRISCV() || !Rel->Addend || !Rel->isIRelative())
return;
// IFUNC trampoline without symbol
@@ -4247,7 +4280,6 @@ void RewriteInstance::addBoltInfoSection() {
<< "command line:";
for (int I = 0; I < Argc; ++I)
DescOS << " " << Argv[I];
- DescOS.flush();
// Encode as GNU GOLD VERSION so it is easily printable by 'readelf -n'
const std::string BoltInfo =
@@ -4270,7 +4302,6 @@ void RewriteInstance::encodeBATSection() {
raw_string_ostream DescOS(DescStr);
BAT->write(*BC, DescOS);
- DescOS.flush();
const std::string BoltInfo =
BinarySection::encodeELFNote("BOLT", DescStr, BinarySection::NT_BOLT_BAT);
bolt/lib/RuntimeLibs/InstrumentationRuntimeLibrary.cpp
@@ -314,7 +314,6 @@ std::string InstrumentationRuntimeLibrary::buildTables(BinaryContext &BC) {
}
// Our string table lives immediately after descriptions vector
OS << Summary->StringTable;
- OS.flush();
return TablesStr;
}
bolt/lib/Utils/CommandLineOpts.cpp
@@ -175,6 +175,10 @@ cl::opt<std::string> SaveProfile("w",
cl::desc("save recorded profile to a file"),
cl::cat(BoltOutputCategory));
+cl::opt<bool> ShowDensity("show-density",
+ cl::desc("show profile density details"),
+ cl::Optional, cl::cat(AggregatorCategory));
+
cl::opt<bool> SplitEH("split-eh", cl::desc("split C++ exception handling code"),
cl::Hidden, cl::cat(BoltOptCategory));
bolt/test/AArch64/Inputs/spurious-marker-symbol.yaml
@@ -0,0 +1,56 @@
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+ Entry: 0x2a0000
+ProgramHeaders:
+ - Type: PT_PHDR
+ Flags: [ PF_R ]
+ VAddr: 0x40
+ Align: 0x8
+ FileSize: 0xa8
+ MemSize: 0xa8
+ Offset: 0x40
+ - Type: PT_LOAD
+ Flags: [ PF_R ]
+ VAddr: 0x0
+ Align: 0x10000
+ FileSize: 0xf8
+ MemSize: 0xf8
+ Offset: 0x0
+ - Type: PT_LOAD
+ Flags: [ PF_X, PF_R ]
+ VAddr: 0x2a0000
+ Align: 0x10000
+ FirstSec: .text
+ LastSec: .ignored
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x2a0000
+ AddressAlign: 0x4
+ Content: 400580d2c0035fd6
+ - Name: .ignored
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x2a0008
+ AddressAlign: 0x8
+ Size: 0x8
+ - Name: .eh_frame
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x2a0010
+ AddressAlign: 0x8
+ Content: 1000000000000000017a520004781e010b0c1f00140000001800000000002a0008000000000e01410e010000
+Symbols:
+ - Name: func
+ Section: .text
+ Value: 0x2a0000
+ Size: 0x8
+ - Name: '$d.42'
+ Section: .ignored
+ Value: 0x2a0004
+...
bolt/test/AArch64/adr-relaxation.s
@@ -0,0 +1,49 @@
+## Check that llvm-bolt will not unnecessarily relax ADR instruction.
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static
+# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=random2
+# RUN: llvm-objdump -d --disassemble-symbols=_start %t.bolt | FileCheck %s
+# RUN: llvm-objdump -d --disassemble-symbols=foo.cold.0 %t.bolt \
+# RUN: | FileCheck --check-prefix=CHECK-FOO %s
+
+## ADR below references its containing function that is split. But ADR is always
+## in the main fragment, thus there is no need to relax it.
+ .text
+ .globl _start
+ .type _start, %function
+_start:
+# CHECK: <_start>:
+ .cfi_startproc
+ adr x1, _start
+# CHECK-NOT: adrp
+# CHECK: adr
+ cmp x1, x11
+ b.hi .L1
+ mov x0, #0x0
+.L1:
+ ret x30
+ .cfi_endproc
+.size _start, .-_start
+
+
+## In foo, ADR is in the split fragment but references the main one. Thus, it
+## needs to be relaxed into ADRP + ADD.
+ .globl foo
+ .type foo, %function
+foo:
+ .cfi_startproc
+ cmp x1, x11
+ b.hi .L2
+ mov x0, #0x0
+.L2:
+# CHECK-FOO: <foo.cold.0>:
+ adr x1, foo
+# CHECK-FOO: adrp
+# CHECK-FOO-NEXT: add
+ ret x30
+ .cfi_endproc
+.size foo, .-foo
+
+## Force relocation mode.
+ .reloc 0, R_AARCH64_NONE
bolt/test/AArch64/compact-code-model.s
@@ -0,0 +1,92 @@
+## Check that llvm-bolt successfully relaxes branches for compact (<128MB) code
+## model.
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: link_fdata %s %t.o %t.fdata
+# RUN: llvm-strip --strip-unneeded %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static
+# RUN: llvm-bolt %t.exe -o %t.bolt --data %t.fdata --split-functions \
+# RUN: --keep-nops --compact-code-model
+# RUN: llvm-objdump -d \
+# RUN: --disassemble-symbols=_start,_start.cold.0,foo,foo.cold.0 %t.bolt \
+# RUN: | FileCheck %s
+# RUN: llvm-nm -nS %t.bolt | FileCheck %s --check-prefix=CHECK-NM
+
+## Fragments of _start and foo will be separated by large_function which is over
+## 1MB in size - larger than all conditional branches can cover requiring branch
+## relaxation.
+
+# CHECK-NM: _start
+# CHECK-NM: foo
+# CHECK-NM: 0000000000124f84 T large_function
+# CHECK-NM: _start.cold.0
+# CHECK-NM: foo.cold.0
+
+ .text
+ .globl _start
+ .type _start, %function
+_start:
+# CHECK: <_start>:
+# FDATA: 0 [unknown] 0 1 _start 0 0 100
+ .cfi_startproc
+ cmp x0, 1
+ b.eq .L0
+# CHECK: b.eq
+# CHECK-NEXT: b
+# CHECK-NEXT: b
+
+ bl large_function
+.L0:
+ ret x30
+ .cfi_endproc
+.size _start, .-_start
+
+## Check that long branch in foo() is reused during relaxation. I.e. we should
+## see just one branch to the cold fragment.
+
+ .globl foo
+ .type foo, %function
+foo:
+# CHECK: <foo>:
+# FDATA: 0 [unknown] 0 1 foo 0 0 100
+ .cfi_startproc
+ cmp x0, 0
+.T0:
+ b.eq .ERROR
+# CHECK: b {{.*}} <foo.cold.0>
+# CHECK-NOT: b {{.*}} <foo.cold.0>
+# FDATA: 1 foo #.T0# 1 foo #.T1# 0 100
+.T1:
+ bl large_function
+ cmp x0, 1
+.T2:
+ b.eq .ERROR
+# FDATA: 1 foo #.T2# 1 foo #.T3# 0 100
+.T3:
+ mov x1, x0
+ mov x0, 0
+ ret x30
+
+# CHECK: <foo.cold.0>:
+# CHECK-NEXT: mov x0, #0x1
+# CHECK-NEXT: ret
+.ERROR:
+ mov x0, 1
+ ret x30
+ .cfi_endproc
+.size foo, .-foo
+
+ .globl large_function
+ .type large_function, %function
+large_function:
+# FDATA: 0 [unknown] 0 1 large_function 0 0 100
+ .cfi_startproc
+ .rept 300000
+ nop
+ .endr
+ ret x30
+ .cfi_endproc
+.size large_function, .-large_function
+
+## Force relocation mode.
+ .reloc 0, R_AARCH64_NONE
bolt/test/AArch64/constant_island_pie_update.s
@@ -8,15 +8,15 @@
# RUN: %clang %cflags -fPIC -pie %t.o -o %t.rela.exe -nostdlib \
# RUN: -Wl,-q -Wl,-z,notext
# RUN: llvm-bolt %t.rela.exe -o %t.rela.bolt --use-old-text=0 --lite=0
-# RUN: llvm-objdump -j .text -d --show-all-symbols %t.rela.bolt | FileCheck %s
+# RUN: llvm-objdump -j .text -d -z --show-all-symbols %t.rela.bolt | FileCheck %s
# RUN: llvm-readelf -rsW %t.rela.bolt | FileCheck --check-prefix=ELFCHECK %s
// .relr.dyn
# RUN: %clang %cflags -fPIC -pie %t.o -o %t.relr.exe -nostdlib \
# RUN: -Wl,-q -Wl,-z,notext -Wl,--pack-dyn-relocs=relr
# RUN: llvm-objcopy --remove-section .rela.mytext %t.relr.exe
# RUN: llvm-bolt %t.relr.exe -o %t.relr.bolt --use-old-text=0 --lite=0
-# RUN: llvm-objdump -j .text -d --show-all-symbols %t.relr.bolt | FileCheck %s
-# RUN: llvm-objdump -j .text -d %t.relr.bolt | \
+# RUN: llvm-objdump -j .text -d -z --show-all-symbols %t.relr.bolt | FileCheck %s
+# RUN: llvm-objdump -j .text -d -z %t.relr.bolt | \
# RUN: FileCheck %s --check-prefix=ADDENDCHECK
# RUN: llvm-readelf -rsW %t.relr.bolt | FileCheck --check-prefix=RELRELFCHECK %s
# RUN: llvm-readelf -SW %t.relr.bolt | FileCheck --check-prefix=RELRSZCHECK %s
bolt/test/AArch64/ifunc.c→/dev/null
@@ -1,62 +0,0 @@
-// This test checks that IFUNC trampoline is properly recognised by BOLT
-
-// With -O0 indirect call is performed on IPLT trampoline. IPLT trampoline
-// has IFUNC symbol.
-// RUN: %clang %cflags -nostdlib -O0 -no-pie %s -fuse-ld=lld \
-// RUN: -o %t.O0.exe -Wl,-q
-// RUN: llvm-bolt %t.O0.exe -o %t.O0.bolt.exe \
-// RUN: --print-disasm --print-only=_start | \
-// RUN: FileCheck --check-prefix=CHECK %s
-// RUN: llvm-readelf -aW %t.O0.bolt.exe | \
-// RUN: FileCheck --check-prefix=REL_CHECK %s
-
-// Non-pie static executable doesn't generate PT_DYNAMIC, check relocation
-// is readed successfully and IPLT trampoline has been identified by bolt.
-// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -no-pie \
-// RUN: -o %t.O3_nopie.exe -Wl,-q
-// RUN: llvm-readelf -l %t.O3_nopie.exe | \
-// RUN: FileCheck --check-prefix=NON_DYN_CHECK %s
-// RUN: llvm-bolt %t.O3_nopie.exe -o %t.O3_nopie.bolt.exe \
-// RUN: --print-disasm --print-only=_start | \
-// RUN: FileCheck --check-prefix=CHECK %s
-// RUN: llvm-readelf -aW %t.O3_nopie.bolt.exe | \
-// RUN: FileCheck --check-prefix=REL_CHECK %s
-
-// With -O3 direct call is performed on IPLT trampoline. IPLT trampoline
-// doesn't have associated symbol. The ifunc symbol has the same address as
-// IFUNC resolver function.
-// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -fPIC -pie \
-// RUN: -o %t.O3_pie.exe -Wl,-q
-// RUN: llvm-bolt %t.O3_pie.exe -o %t.O3_pie.bolt.exe \
-// RUN: --print-disasm --print-only=_start | \
-// RUN: FileCheck --check-prefix=CHECK %s
-// RUN: llvm-readelf -aW %t.O3_pie.bolt.exe | \
-// RUN: FileCheck --check-prefix=REL_CHECK %s
-
-// Check that IPLT trampoline located in .plt section are normally handled by
-// BOLT. The gnu-ld linker doesn't use separate .iplt section.
-// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -fPIC -pie \
-// RUN: -T %p/Inputs/iplt.ld -o %t.iplt_O3_pie.exe -Wl,-q
-// RUN: llvm-bolt %t.iplt_O3_pie.exe -o %t.iplt_O3_pie.bolt.exe \
-// RUN: --print-disasm --print-only=_start | \
-// RUN: FileCheck --check-prefix=CHECK %s
-// RUN: llvm-readelf -aW %t.iplt_O3_pie.bolt.exe | \
-// RUN: FileCheck --check-prefix=REL_CHECK %s
-
-// NON_DYN_CHECK-NOT: DYNAMIC
-
-// CHECK: {{(bl? "(resolver_foo|ifoo).*@PLT"|adr x[0-9]+, ifoo)}}
-
-// REL_CHECK: R_AARCH64_IRELATIVE [[#%x,REL_SYMB_ADDR:]]
-// REL_CHECK: [[#REL_SYMB_ADDR]] {{.*}} FUNC {{.*}} resolver_foo
-
-static void foo() {}
-static void bar() {}
-
-extern int use_foo;
-
-static void *resolver_foo(void) { return use_foo ? foo : bar; }
-
-__attribute__((ifunc("resolver_foo"))) void ifoo();
-
-void _start() { ifoo(); }
bolt/test/AArch64/ifunc.test
@@ -0,0 +1,49 @@
+// With -O0 indirect call is performed on IPLT trampoline. IPLT trampoline
+// has IFUNC symbol.
+// RUN: %clang %cflags -nostdlib -O0 -no-pie %p/../Inputs/ifunc.c -fuse-ld=lld \
+// RUN: -o %t.O0.exe -Wl,-q
+// RUN: llvm-bolt %t.O0.exe -o %t.O0.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.O0.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// Non-pie static executable doesn't generate PT_DYNAMIC, check relocation
+// is readed successfully and IPLT trampoline has been identified by bolt.
+// RUN: %clang %cflags -nostdlib -O3 %p/../Inputs/ifunc.c -fuse-ld=lld -no-pie \
+// RUN: -o %t.O3_nopie.exe -Wl,-q
+// RUN: llvm-readelf -l %t.O3_nopie.exe | \
+// RUN: FileCheck --check-prefix=NON_DYN_CHECK %s
+// RUN: llvm-bolt %t.O3_nopie.exe -o %t.O3_nopie.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.O3_nopie.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// With -O3 direct call is performed on IPLT trampoline. IPLT trampoline
+// doesn't have associated symbol. The ifunc symbol has the same address as
+// IFUNC resolver function.
+// RUN: %clang %cflags -nostdlib -O3 %p/../Inputs/ifunc.c -fuse-ld=lld -fPIC -pie \
+// RUN: -o %t.O3_pie.exe -Wl,-q
+// RUN: llvm-bolt %t.O3_pie.exe -o %t.O3_pie.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.O3_pie.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// Check that IPLT trampoline located in .plt section are normally handled by
+// BOLT. The gnu-ld linker doesn't use separate .iplt section.
+// RUN: %clang %cflags -nostdlib -O3 %p/../Inputs/ifunc.c -fuse-ld=lld -fPIC -pie \
+// RUN: -T %p/../Inputs/iplt.ld -o %t.iplt_O3_pie.exe -Wl,-q
+// RUN: llvm-bolt %t.iplt_O3_pie.exe -o %t.iplt_O3_pie.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.iplt_O3_pie.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// NON_DYN_CHECK-NOT: DYNAMIC
+
+// CHECK: {{(bl? "(resolver_foo|ifoo).*@PLT"|adr x[0-9]+, ifoo)}}
+
+// REL_CHECK: R_AARCH64_IRELATIVE [[#%x,REL_SYMB_ADDR:]]
+// REL_CHECK: [[#REL_SYMB_ADDR]] {{.*}} FUNC {{.*}} resolver_foo
bolt/test/AArch64/split-funcs-lite.s
@@ -0,0 +1,27 @@
+# This test checks that tentative code layout for cold blocks always runs.
+# It commonly happens when using lite mode with split functions.
+
+# REQUIRES: system-linux, asserts
+
+# RUN: %clang %cflags -o %t %s
+# RUN: %clang %s %cflags -Wl,-q -o %t
+# RUN: link_fdata --no-lbr %s %t %t.fdata
+# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions \
+# RUN: -debug 2>&1 | FileCheck %s
+
+ .text
+ .globl foo
+ .type foo, %function
+foo:
+.entry_bb:
+# FDATA: 1 foo #.entry_bb# 10
+ cmp x0, #0
+ b.eq .Lcold_bb1
+ ret
+.Lcold_bb1:
+ ret
+
+## Force relocation mode.
+.reloc 0, R_AARCH64_NONE
+
+# CHECK: foo{{.*}} cold tentative: {{.*}}
bolt/test/AArch64/spurious-marker-symbol.test
@@ -0,0 +1,61 @@
+// Check that marker symbols ($d, $x) denoting data embedded in code are ignored
+// if they fall outside their respective sections.
+
+// RUN: yaml2obj %S/Inputs/spurious-marker-symbol.yaml -o %t.exe
+// RUN: llvm-bolt %t.exe -o %t.bolt 2>&1 | FileCheck %s
+// CHECK: 1 out of 1 functions were overwritten
+// RUN: llvm-objdump -j .text -d %t.bolt | FileCheck %s -check-prefix=CHECK-DISASM
+// CHECK-DISASM: func
+// CHECK-DISASM: 2a0000: d2800540 mov
+// CHECK-DISASM: 2a0004: d65f03c0 ret
+
+// The YAML encodes the following assembly and debug information:
+
+ .text
+ .globl func
+ .type func, %function
+func:
+ mov x0, #42
+// $d.42: (symbol in .ignored, with an address in .text)
+ ret
+
+// .eh_frame contains minimal DWARF with a CFA operation on the `ret`. BOLT
+// should ignore the spurious `$d.42`. If it doesn't, then it will stop
+// disassembling after the `mov` and will fail to process the second
+// DW_CFA_def_cfa_offset.
+//
+// CIE
+// length: 00000010
+// CIE_id: 00000000
+// version: 01
+// augmentation:
+// "zR" 7a 52 00
+// - read augmentation data
+// - read FDE pointer encoding
+// code_alignment_factor: 04
+// data_alignment_factor: 78 (-8)
+// return_address_register: 1e (r30 / lr)
+//
+// augmentation data:
+// length: 01
+// FDE pointers are absptr+sdata4 0b
+//
+// initial_instructions:
+// DW_CFA_def_cfa (31, 0): 0c 1f 00
+//
+// Encoding: 10000000'00000000'01'7a5200'04'78'1e'10'0b'0c1f00
+//
+// FDE
+// length: 00000014
+// CIE_pointer: 00000018 (backwards offset from here to CIE)
+// initial_location: 002a0000 (`func` as absptr+sdata4)
+// address_range: 00000008
+// augmentation data:
+// length: 00
+// instructions:
+// DW_CFA_def_cfa_offset (1) 0e 01
+// DW_CFA_advance_loc (1) 41 (`ret` at 0x2a0004)
+// DW_CFA_def_cfa_offset (1) 0e 01 Fails unless $d.42 is ignored.
+// DW_CFA_nop 00 00
+//
+// Encoding: 14000000'18000000'00002a00'08000000'000e0141'0e010000
bolt/test/AArch64/update-weak-reference-symbol.s
@@ -3,7 +3,7 @@
// RUN: %clang %cflags -Wl,-z,notext -shared -Wl,-q %s -o %t.so
// RUN: llvm-bolt %t.so -o %t.so.bolt
// RUN: llvm-nm -n %t.so.bolt > %t.out.txt
-// RUN: llvm-objdump -dj .rodata %t.so.bolt >> %t.out.txt
+// RUN: llvm-objdump -z -dj .rodata %t.so.bolt >> %t.out.txt
// RUN: FileCheck %s --input-file=%t.out.txt
# CHECK: w func_1
bolt/test/Inputs/ifunc.c
@@ -0,0 +1,12 @@
+// This test checks that IFUNC trampoline is properly recognised by BOLT
+
+static void foo() {}
+static void bar() {}
+
+extern int use_foo;
+
+static void *resolver_foo(void) { return use_foo ? foo : bar; }
+
+__attribute__((ifunc("resolver_foo"))) void ifoo();
+
+void _start() { ifoo(); }
bolt/test/X86/callcont-fallthru.s
@@ -0,0 +1,132 @@
+## Ensures that a call continuation fallthrough count is set when using
+## pre-aggregated perf data.
+
+# RUN: %clangxx %cxxflags %s -o %t -Wl,-q -nostdlib
+# RUN: link_fdata %s %t %t.pa1 PREAGG
+# RUN: link_fdata %s %t %t.pa2 PREAGG2
+# RUN: link_fdata %s %t %t.pa3 PREAGG3
+# RUN: link_fdata %s %t %t.pa4 PREAGG4
+
+## Check normal case: fallthrough is not LP or secondary entry.
+# RUN: llvm-strip --strip-unneeded %t -o %t.exe
+# RUN: llvm-bolt %t.exe --pa -p %t.pa1 -o %t.out \
+# RUN: --print-cfg --print-only=main | FileCheck %s
+
+## Check that getFallthroughsInTrace correctly handles a trace starting at plt
+## call continuation
+# RUN: llvm-bolt %t.exe --pa -p %t.pa2 -o %t.out2 \
+# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK2
+
+## Check that we don't treat secondary entry points as call continuation sites.
+# RUN: llvm-bolt %t --pa -p %t.pa3 -o %t.out \
+# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK3
+
+## Check fallthrough to a landing pad case.
+# RUN: llvm-bolt %t.exe --pa -p %t.pa4 -o %t.out \
+# RUN: --print-cfg --print-only=main | FileCheck %s --check-prefix=CHECK4
+
+ .globl foo
+ .type foo, %function
+foo:
+ pushq %rbp
+ movq %rsp, %rbp
+ popq %rbp
+Lfoo_ret:
+ retq
+.size foo, .-foo
+
+ .globl main
+ .type main, %function
+main:
+.Lfunc_begin0:
+ .cfi_startproc
+ .cfi_personality 155, DW.ref.__gxx_personality_v0
+ .cfi_lsda 27, .Lexception0
+ pushq %rbp
+ movq %rsp, %rbp
+ subq $0x20, %rsp
+ movl $0x0, -0x4(%rbp)
+ movl %edi, -0x8(%rbp)
+ movq %rsi, -0x10(%rbp)
+ callq puts@PLT
+## Target is a call continuation
+# PREAGG: B X:0 #Ltmp1# 2 0
+# CHECK: callq puts@PLT
+# CHECK-NEXT: count: 2
+
+Ltmp1:
+ movq -0x10(%rbp), %rax
+ movq 0x8(%rax), %rdi
+ movl %eax, -0x14(%rbp)
+
+Ltmp4:
+ cmpl $0x0, -0x14(%rbp)
+ je Ltmp0
+# CHECK2: je .Ltmp0
+# CHECK2-NEXT: count: 3
+
+ movl $0xa, -0x18(%rbp)
+ callq foo
+## Target is a call continuation
+# PREAGG: B #Lfoo_ret# #Ltmp3# 1 0
+# CHECK: callq foo
+# CHECK-NEXT: count: 1
+
+## PLT call continuation fallthrough spanning the call
+# PREAGG2: F #Ltmp1# #Ltmp3_br# 3
+# CHECK2: callq foo
+# CHECK2-NEXT: count: 3
+
+## Target is a secondary entry point
+# PREAGG3: B X:0 #Ltmp3# 2 0
+# CHECK3: callq foo
+# CHECK3-NEXT: count: 0
+
+## Target is a landing pad
+# PREAGG4: B X:0 #Ltmp3# 2 0
+# CHECK4: callq puts@PLT
+# CHECK4-NEXT: count: 0
+
+Ltmp3:
+ cmpl $0x0, -0x18(%rbp)
+Ltmp3_br:
+ jmp Ltmp2
+
+Ltmp2:
+ movl -0x18(%rbp), %eax
+ addl $-0x1, %eax
+ movl %eax, -0x18(%rbp)
+ jmp Ltmp3
+ jmp Ltmp4
+ jmp Ltmp1
+
+Ltmp0:
+ xorl %eax, %eax
+ addq $0x20, %rsp
+ popq %rbp
+ retq
+.Lfunc_end0:
+ .cfi_endproc
+.size main, .-main
+
+ .section .gcc_except_table,"a",@progbits
+ .p2align 2, 0x0
+GCC_except_table0:
+.Lexception0:
+ .byte 255 # @LPStart Encoding = omit
+ .byte 255 # @TType Encoding = omit
+ .byte 1 # Call site Encoding = uleb128
+ .uleb128 .Lcst_end0-.Lcst_begin0
+.Lcst_begin0:
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Lfunc_end0
+ .uleb128 Ltmp3-.Lfunc_begin0 # jumps to Ltmp3
+ .byte 0 # has no landing pad
+ .byte 0 # On action: cleanup
+.Lcst_end0:
+ .p2align 2, 0x0
+ .hidden DW.ref.__gxx_personality_v0
+ .weak DW.ref.__gxx_personality_v0
+ .section .data.DW.ref.__gxx_personality_v0,"awG",@progbits,DW.ref.__gxx_personality_v0,comdat
+ .p2align 3, 0x0
+ .type DW.ref.__gxx_personality_v0,@object
bolt/test/X86/cfg-discontinuity-reporting.test
@@ -0,0 +1,4 @@
+## Check profile discontinuity reporting
+RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe
+RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt | FileCheck %s
+CHECK: among the hottest 5 functions top 5% function CFG discontinuity is 100.00%
bolt/test/X86/ifunc.test
@@ -0,0 +1,47 @@
+// Check if BOLT can process ifunc symbols from .plt section
+// RUN: %clang %cflags -nostdlib -no-pie %p/../Inputs/ifunc.c -fuse-ld=lld \
+// RUN: -o %t.exe -Wl,-q
+// RUN: llvm-bolt %t.exe -o %t.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// Check if BOLT can process ifunc symbols from .plt section in non-pie static
+// executable case.
+// RUN: %clang %cflags -nostdlib %p/../Inputs/ifunc.c -fuse-ld=lld -no-pie \
+// RUN: -o %t.nopie.exe -Wl,-q
+// RUN: llvm-readelf -l %t.nopie.exe | \
+// RUN: FileCheck --check-prefix=NON_DYN_CHECK %s
+// RUN: llvm-bolt %t.nopie.exe -o %t.nopie.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.nopie.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// Check if BOLT can process ifunc symbols from .plt section in pie executable
+// case.
+// RUN: %clang %cflags -nostdlib %p/../Inputs/ifunc.c -fuse-ld=lld -fPIC -pie \
+// RUN: -o %t.pie.exe -Wl,-q
+// RUN: llvm-bolt %t.pie.exe -o %t.pie.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.pie.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// Check that IPLT trampoline located in .plt section are normally handled by
+// BOLT. The gnu-ld linker doesn't use separate .iplt section.
+// RUN: %clang %cflags -nostdlib %p/../Inputs/ifunc.c -fuse-ld=lld -fPIC -pie \
+// RUN: -T %p/../Inputs/iplt.ld -o %t.iplt_pie.exe -Wl,-q
+// RUN: llvm-bolt %t.iplt_pie.exe -o %t.iplt_pie.bolt.exe \
+// RUN: --print-disasm --print-only=_start | \
+// RUN: FileCheck --check-prefix=CHECK %s
+// RUN: llvm-readelf -aW %t.iplt_pie.bolt.exe | \
+// RUN: FileCheck --check-prefix=REL_CHECK %s
+
+// NON_DYN_CHECK-NOT: DYNAMIC
+
+// CHECK: callq "resolver_foo/1@PLT"
+
+// REL_CHECK: R_X86_64_IRELATIVE [[#%x,REL_SYMB_ADDR:]]
+// REL_CHECK: [[#REL_SYMB_ADDR]] {{.*}} FUNC {{.*}} resolver_foo
bolt/test/X86/log.test
@@ -6,7 +6,7 @@ RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe
RUN: llvm-bolt %t.exe -o %t.null --data %p/Inputs/blarge.fdata -v=2 \
RUN: --reorder-blocks=normal --print-finalized --log-file=%t.log 2>&1 \
RUN: | FileCheck --check-prefix=CHECK --allow-empty %s
-RUN: cat %t.log | FileCheck %s --check-prefix=CHECK-LOG
+RUN: FileCheck %s --check-prefix=CHECK-LOG --input-file %t.log
CHECK-NOT: BOLT-INFO
CHECK-NOT: BOLT-WARNING
@@ -16,4 +16,4 @@ CHECK-NOT: BOLT-ERROR
CHECK-LOG: BOLT-INFO: Target architecture
CHECK-LOG: BOLT-INFO: BOLT version
CHECK-LOG: BOLT-INFO: basic block reordering modified layout
-CHECK-LOG: Binary Function "usqrt"
+CHECK-LOG: Binary Function "main"
bolt/test/X86/match-blocks-with-pseudo-probes-inline.test
@@ -0,0 +1,65 @@
+## Test stale block matching with pseudo probes including inline tree matching.
+# RUN: split-file %s %t
+# RUN: llvm-bolt \
+# RUN: %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin \
+# RUN: -o %t.bolt -data %t/yaml -infer-stale-profile -v=2 \
+# RUN: --stale-matching-with-pseudo-probes 2>&1 | FileCheck %s
+
+# CHECK: BOLT-WARNING: 3 (100.0% of all profiled) functions have invalid (possibly stale) profile
+# CHECK: BOLT-INFO: inference found an exact pseudo probe match for 100.00% of basic blocks (3 out of 3 stale)
+
+#--- yaml
+---
+header:
+ profile-version: 1
+ binary-name: 'inline-cs-pseudoprobe.perfbin'
+ binary-build-id: '<unknown>'
+ profile-flags: [ lbr ]
+ profile-origin: perf data aggregator
+ profile-events: ''
+ dfs-order: false
+ hash-func: xxh3
+functions:
+ - name: bar
+ fid: 9
+ hash: 0x1
+ exec: 1
+ nblocks: 1
+ blocks:
+ - bid: 0
+ insns: 11
+ hash: 0x1
+ exec: 1
+ probes: [ { blx: 9 } ]
+ inline_tree: [ { } ]
+ - name: foo
+ fid: 10
+ hash: 0x2
+ exec: 1
+ nblocks: 6
+ blocks:
+ - bid: 0
+ insns: 3
+ hash: 0x2
+ exec: 1
+ succ: [ { bid: 3, cnt: 0 } ]
+ probes: [ { blx: 3 } ]
+ inline_tree: [ { g: 1 }, { g: 0, cs: 8 } ]
+ - name: main
+ fid: 11
+ hash: 0x3
+ exec: 1
+ nblocks: 6
+ blocks:
+ - bid: 0
+ insns: 3
+ hash: 0x3
+ exec: 1
+ succ: [ { bid: 3, cnt: 0 } ]
+ probes: [ { blx: 3, id: 1 }, { blx: 1 } ]
+ inline_tree: [ { g: 2 }, { g: 1, cs: 2 }, { g: 0, p: 1, cs: 8 } ]
+pseudo_probe_desc:
+ gs: [ 0xE413754A191DB537, 0x5CF8C24CDB18BDAC, 0xDB956436E78DD5FA ]
+ gh: [ 2, 0, 1 ]
+ hs: [ 0x200205A19C5B4, 0x10000FFFFFFFF, 0x10E852DA94 ]
+...
bolt/test/X86/match-blocks-with-pseudo-probes.test
@@ -55,8 +55,8 @@ functions:
hash: 0xFFFFFFFFFFFFFFF1
insns: 1
succ: [ { bid: 3, cnt: 1} ]
- probes: [ { } ]
- inline_tree: [ { g: 1 } ]
+ probes: [ { blx: 1 } ]
+ inline_tree: [ { g: 0 } ]
pseudo_probe_desc:
gs: [ 0xDB956436E78DD5FA ]
gh: [ 0 ]
bolt/test/X86/pre-aggregated-perf.test
@@ -11,7 +11,21 @@ REQUIRES: system-linux
RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe
RUN: perf2bolt %t.exe -o %t --pa -p %p/Inputs/pre-aggregated.txt -w %t.new \
-RUN: --profile-use-dfs | FileCheck %s
+RUN: --show-density \
+RUN: --profile-density-threshold=9 --profile-density-cutoff-hot=970000 \
+RUN: --profile-use-dfs | FileCheck %s --check-prefix=CHECK-P2B
+
+CHECK-P2B: BOLT-INFO: 4 out of 7 functions in the binary (57.1%) have non-empty execution profile
+CHECK-P2B: BOLT-INFO: Functions with density >= 21.7 account for 97.00% total sample counts.
+
+RUN: perf2bolt %t.exe -o %t --pa -p %p/Inputs/pre-aggregated.txt -w %t.new \
+RUN: --show-density \
+RUN: --profile-density-cutoff-hot=970000 \
+RUN: --profile-use-dfs 2>&1 | FileCheck %s --check-prefix=CHECK-WARNING
+
+CHECK-WARNING: BOLT-INFO: 4 out of 7 functions in the binary (57.1%) have non-empty execution profile
+CHECK-WARNING: BOLT-WARNING: BOLT is estimated to optimize better with 2.8x more samples.
+CHECK-WARNING: BOLT-INFO: Functions with density >= 21.7 account for 97.00% total sample counts.
RUN: llvm-bolt %t.exe -data %t -o %t.null | FileCheck %s
RUN: llvm-bolt %t.exe -data %t.new -o %t.null | FileCheck %s
bolt/test/X86/print-only-section.s
@@ -0,0 +1,29 @@
+## Check that --print-only flag works with sections.
+
+# REQUIRES: system-linux
+
+# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe
+# RUN: llvm-bolt %t.exe -o %t.out --print-cfg --print-only=unused_code 2>&1 \
+# RUN: | FileCheck %s
+
+# CHECK: Binary Function "foo"
+# CHECK-NOT: Binary Function "_start"
+
+ .text
+ .globl _start
+ .type _start, %function
+_start:
+ .cfi_startproc
+ ret
+ .cfi_endproc
+ .size _start, .-_start
+
+ .section unused_code,"ax",@progbits
+ .globl foo
+ .type foo, %function
+foo:
+ .cfi_startproc
+ ret
+ .cfi_endproc
+ .size foo, .-foo
bolt/test/X86/pseudoprobe-decoding-inline.test
@@ -14,18 +14,18 @@
# RUN: FileCheck --input-file %t.yaml2 %s --check-prefix CHECK-YAML
# CHECK-YAML: name: bar
# CHECK-YAML: - bid: 0
-# CHECK-YAML: probes: [ { blk: 9 } ]
-# CHECK-YAML: inline_tree: [ { g: 1 } ]
+# CHECK-YAML: probes: [ { blx: 9 } ]
+# CHECK-YAML: inline_tree: [ { } ]
#
# CHECK-YAML: name: foo
# CHECK-YAML: - bid: 0
-# CHECK-YAML: probes: [ { blk: 3 } ]
-# CHECK-YAML: inline_tree: [ { g: 2 }, { g: 1, cs: 8 } ]
+# CHECK-YAML: probes: [ { blx: 3 } ]
+# CHECK-YAML: inline_tree: [ { g: 1 }, { g: 0, cs: 8 } ]
#
# CHECK-YAML: name: main
# CHECK-YAML: - bid: 0
-# CHECK-YAML: probes: [ { blk: 3, id: 1 }, { } ]
-# CHECK-YAML: inline_tree: [ { g: 3 }, { g: 2, cs: 2 }, { g: 1, p: 1, cs: 8 } ]
+# CHECK-YAML: probes: [ { blx: 3, id: 1 }, { blx: 1 } ]
+# CHECK-YAML: inline_tree: [ { g: 2 }, { g: 1, cs: 2 }, { g: 0, p: 1, cs: 8 } ]
#
# CHECK-YAML: pseudo_probe_desc:
# CHECK-YAML-NEXT: gs: [ 0xE413754A191DB537, 0x5CF8C24CDB18BDAC, 0xDB956436E78DD5FA ]
@@ -34,8 +34,8 @@
#
## Check that without --profile-write-pseudo-probes option, no pseudo probes are
## generated
-# RUN: perf2bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin -p %t.preagg --pa -w %t.yaml -o %t.fdata
-# RUN: FileCheck --input-file %t.yaml %s --check-prefix CHECK-NO-OPT
+# RUN: perf2bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/inline-cs-pseudoprobe.perfbin -p %t.preagg --pa -w %t.yaml3 -o %t.fdata
+# RUN: FileCheck --input-file %t.yaml3 %s --check-prefix CHECK-NO-OPT
# CHECK-NO-OPT-NOT: probes:
# CHECK-NO-OPT-NOT: inline_tree:
# CHECK-NO-OPT-NOT: pseudo_probe_desc:
bolt/test/X86/pseudoprobe-decoding-noinline.test
@@ -1,6 +1,45 @@
# REQUIRES: system-linux
-# RUN: llvm-bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/noinline-cs-pseudoprobe.perfbin --print-pseudo-probes=all -o %t.bolt 2>&1 | FileCheck %s
+# RUN: llvm-bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/noinline-cs-pseudoprobe.perfbin --print-pseudo-probes=all -o %t.bolt --lite=0 --enable-bat 2>&1 | FileCheck %s
+# PREAGG: B X:0 #foo# 1 0
+# PREAGG: B X:0 #bar# 1 0
+# PREAGG: B X:0 #main# 1 0
+
+## Check pseudo-probes in regular YAML profile (non-BOLTed binary)
+# RUN: link_fdata %s %S/../../../llvm/test/tools/llvm-profgen/Inputs/noinline-cs-pseudoprobe.perfbin %t.preagg PREAGG
+# RUN: perf2bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/noinline-cs-pseudoprobe.perfbin -p %t.preagg --pa -w %t.yaml -o %t.fdata --profile-write-pseudo-probes
+# RUN: FileCheck --input-file %t.yaml %s --check-prefix CHECK-YAML
+## Check pseudo-probes in BAT YAML profile (BOLTed binary)
+# RUN: link_fdata %s %t.bolt %t.preagg2 PREAGG
+# RUN: perf2bolt %t.bolt -p %t.preagg2 --pa -w %t.yaml2 -o %t.fdata2 --profile-write-pseudo-probes
+# RUN: FileCheck --input-file %t.yaml2 %s --check-prefix CHECK-YAML
+# CHECK-YAML: name: bar
+# CHECK-YAML: - bid: 0
+# CHECK-YAML: probes: [ { blx: 9 } ]
+# CHECK-YAML: inline_tree: [ { } ]
+#
+# CHECK-YAML: name: foo
+# CHECK-YAML: - bid: 0
+# CHECK-YAML: probes: [ { blx: 3 } ]
+# CHECK-YAML: inline_tree: [ { g: 2 } ]
+#
+# CHECK-YAML: name: main
+# CHECK-YAML: - bid: 0
+# CHECK-YAML: probes: [ { blx: 1, call: [ 2 ] } ]
+# CHECK-YAML: inline_tree: [ { g: 1 } ]
+#
+# CHECK-YAML: pseudo_probe_desc:
+# CHECK-YAML-NEXT: gs: [ 0xE413754A191DB537, 0xDB956436E78DD5FA, 0x5CF8C24CDB18BDAC ]
+# CHECK-YAML-NEXT: gh: [ 2, 1, 0 ]
+# CHECK-YAML-NEXT: hs: [ 0x200205A19C5B4, 0x10000FFFFFFFF, 0x10E852DA94 ]
+#
+## Check that without --profile-write-pseudo-probes option, no pseudo probes are
+## generated
+# RUN: perf2bolt %S/../../../llvm/test/tools/llvm-profgen/Inputs/noinline-cs-pseudoprobe.perfbin -p %t.preagg --pa -w %t.yaml3 -o %t.fdata
+# RUN: FileCheck --input-file %t.yaml3 %s --check-prefix CHECK-NO-OPT
+# CHECK-NO-OPT-NOT: probes:
+# CHECK-NO-OPT-NOT: inline_tree:
+# CHECK-NO-OPT-NOT: pseudo_probe_desc:
;; Report of decoding input pseudo probe binaries
; CHECK: GUID: 6699318081062747564 Name: foo
bolt/test/X86/reader-stale-yaml.test
@@ -77,10 +77,10 @@ CHECK2: pre-processing profile using YAML profile reader
CHECK2: applying profile inference for "SolveCubic"
CHECK2: Matched yaml block (bid = 0) with hash 4600940a609c0000 to BB (index = 0) with hash 4600940a609c0000
CHECK2-NEXT: exact match
-CHECK2: Matched yaml block (bid = 1) with hash 167a1f084f130088 to BB (index = 1) with hash 167a1f084f130088
-CHECK2-NEXT: exact match
CHECK2: Matched yaml block (bid = 13) with hash a8d50000f81902a7 to BB (index = 13) with hash a8d5aa43f81902a7
CHECK2-NEXT: loose match
+CHECK2: Matched yaml block (bid = 1) with hash 167a1f084f130088 to BB (index = 1) with hash 167a1f084f130088
+CHECK2-NEXT: exact match
CHECK2: Matched yaml block (bid = 3) with hash c516000073dc00a0 to BB (index = 3) with hash c516b1c973dc00a0
CHECK2-NEXT: loose match
CHECK2: Matched yaml block (bid = 5) with hash 6446e1ea500111 to BB (index = 5) with hash 6446e1ea500111
bolt/test/lit.local.cfg
@@ -1,6 +1,6 @@
host_linux_triple = config.target_triple.split("-")[0] + "-unknown-linux-gnu"
-common_linker_flags = "-fuse-ld=lld -Wl,--unresolved-symbols=ignore-all"
-flags = f"--target={host_linux_triple} {common_linker_flags}"
+common_linker_flags = "-fuse-ld=lld -Wl,--unresolved-symbols=ignore-all -pie"
+flags = f"--target={host_linux_triple} -fPIE {common_linker_flags}"
config.substitutions.insert(0, ("%cflags", f"%cflags {flags}"))
config.substitutions.insert(0, ("%cxxflags", f"%cxxflags {flags}"))
bolt/test/merge-fdata-uninitialized-header.test
@@ -0,0 +1,45 @@
+## Test that merge-fdata correctly handles YAML header with an uninitialized
+## fields. a.yaml does not have hash-func set and it used to crash merge-fdata.
+
+# REQUIRES: system-linux
+
+# RUN: split-file %s %t
+# RUN: not merge-fdata %t/a.yaml %t/b.yaml 2>&1 | FileCheck %s
+
+# CHECK: cannot merge profiles with different hash functions
+
+#--- a.yaml
+---
+header:
+ profile-version: 1
+ binary-name: 'a.out'
+ binary-build-id: '<unknown>'
+ profile-flags: [ lbr ]
+ profile-origin: branch profile reader
+ profile-events: ''
+ dfs-order: false
+functions:
+ - name: 'main'
+ fid: 1
+ hash: 0x50BBA3441D436491
+ exec: 1
+ nblocks: 0
+...
+#--- b.yaml
+---
+header:
+ profile-version: 1
+ binary-name: 'a.out'
+ binary-build-id: '<unknown>'
+ profile-flags: [ lbr ]
+ profile-origin: branch profile reader
+ profile-events: ''
+ dfs-order: false
+ hash-func: xxh3
+functions:
+ - name: 'main'
+ fid: 1
+ hash: 0x50BBA3441D436491
+ exec: 1
+ nblocks: 0
+...
bolt/test/perf2bolt/lit.local.cfg
@@ -1,4 +1,5 @@
import shutil
+import subprocess
-if shutil.which("perf") is not None:
- config.available_features.add("perf")
bolt/test/perf2bolt/perf_test.test
@@ -3,15 +3,12 @@
REQUIRES: system-linux, perf
RUN: %clang %S/Inputs/perf_test.c -fuse-ld=lld -Wl,--script=%S/Inputs/perf_test.lds -o %t
-RUN: perf record -e cycles:u -o %t2 -- %t
+RUN: perf record -Fmax -e cycles:u -o %t2 -- %t
RUN: perf2bolt %t -p=%t2 -o %t3 -nl -ignore-build-id 2>&1 | FileCheck %s
CHECK-NOT: PERF2BOLT-ERROR
CHECK-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection.
RUN: %clang %S/Inputs/perf_test.c -no-pie -fuse-ld=lld -o %t4
-RUN: perf record -e cycles:u -o %t5 -- %t4
-RUN: perf2bolt %t4 -p=%t5 -o %t6 -nl -ignore-build-id 2>&1 | FileCheck %s --check-prefix=CHECK-NO-PIE
-
-CHECK-NO-PIE-NOT: PERF2BOLT-ERROR
-CHECK-NO-PIE-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection.
bolt/tools/driver/llvm-bolt.cpp
@@ -129,6 +129,7 @@ void perf2boltMode(int argc, char **argv) {
exit(1);
}
opts::AggregateOnly = true;
+ opts::ShowDensity = true;
}
void boltDiffMode(int argc, char **argv) {
@@ -202,9 +203,9 @@ int main(int argc, char **argv) {
ToolName = argv[0];
- if (llvm::sys::path::filename(ToolName) == "perf2bolt")
+ if (llvm::sys::path::filename(ToolName).starts_with("perf2bolt"))
perf2boltMode(argc, argv);
- else if (llvm::sys::path::filename(ToolName) == "llvm-boltdiff")
+ else if (llvm::sys::path::filename(ToolName).starts_with("llvm-boltdiff"))
boltDiffMode(argc, argv);
else
boltMode(argc, argv);
bolt/tools/merge-fdata/merge-fdata.cpp
@@ -145,6 +145,10 @@ void mergeProfileHeaders(BinaryProfileHeader &MergedHeader,
errs() << "WARNING: merging profiles with different sampling events\n";
MergedHeader.EventNames += "," + Header.EventNames;
}
+
+ if (MergedHeader.HashFunction != Header.HashFunction)
+ report_error("merge conflict",
+ "cannot merge profiles with different hash functions");
}
void mergeBasicBlockProfile(BinaryBasicBlockProfile &MergedBB,
@@ -386,6 +390,7 @@ int main(int argc, char **argv) {
// Merged information for all functions.
StringMap<BinaryFunctionProfile> MergedBFs;
+ bool FirstHeader = true;
for (std::string &InputDataFilename : Inputs) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
MemoryBuffer::getFileOrSTDIN(InputDataFilename);
@@ -409,7 +414,12 @@ int main(int argc, char **argv) {
}
// Merge the header.
- mergeProfileHeaders(MergedHeader, BP.Header);
+ if (FirstHeader) {
+ MergedHeader = BP.Header;
+ FirstHeader = false;
+ } else {
+ mergeProfileHeaders(MergedHeader, BP.Header);
+ }
// Do the function merge.
for (BinaryFunctionProfile &BF : BP.Functions) {
bolt/unittests/Core/BinaryContext.cpp
@@ -160,13 +160,14 @@ TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
TEST_P(BinaryContextTester, BaseAddress) {
// Check that base address calculation is correct for a binary with the
// following segment layout:
- BC->SegmentMapInfo[0] = SegmentInfo{0, 0x10e8c2b4, 0, 0x10e8c2b4, 0x1000};
+ BC->SegmentMapInfo[0] =
+ SegmentInfo{0, 0x10e8c2b4, 0, 0x10e8c2b4, 0x1000, true};
BC->SegmentMapInfo[0x10e8d2b4] =
- SegmentInfo{0x10e8d2b4, 0x3952faec, 0x10e8c2b4, 0x3952faec, 0x1000};
+ SegmentInfo{0x10e8d2b4, 0x3952faec, 0x10e8c2b4, 0x3952faec, 0x1000, true};
BC->SegmentMapInfo[0x4a3bddc0] =
- SegmentInfo{0x4a3bddc0, 0x148e828, 0x4a3bbdc0, 0x148e828, 0x1000};
+ SegmentInfo{0x4a3bddc0, 0x148e828, 0x4a3bbdc0, 0x148e828, 0x1000, true};
BC->SegmentMapInfo[0x4b84d5e8] =
- SegmentInfo{0x4b84d5e8, 0x294f830, 0x4b84a5e8, 0x3d3820, 0x1000};
+ SegmentInfo{0x4b84d5e8, 0x294f830, 0x4b84a5e8, 0x3d3820, 0x1000, true};
std::optional<uint64_t> BaseAddress =
BC->getBaseAddressForMapping(0x7f13f5556000, 0x10e8c000);
@@ -181,13 +182,13 @@ TEST_P(BinaryContextTester, BaseAddress2) {
// Check that base address calculation is correct for a binary if the
// alignment in ELF file are different from pagesize.
// The segment layout is as follows:
- BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000};
+ BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000, true};
BC->SegmentMapInfo[0x31860] =
- SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000};
+ SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000, true};
BC->SegmentMapInfo[0x41c20] =
- SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000};
+ SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000, true};
BC->SegmentMapInfo[0x54e18] =
- SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000};
+ SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000, true};
std::optional<uint64_t> BaseAddress =
BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000);
@@ -197,3 +198,22 @@ TEST_P(BinaryContextTester, BaseAddress2) {
BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000);
ASSERT_FALSE(BaseAddress.has_value());
}
+
+TEST_P(BinaryContextTester, BaseAddressSegmentsSmallerThanAlignment) {
+ // Check that the correct segment is used to compute the base address
+ // when multiple segments are close together in the ELF file (closer
+ // than the required alignment in the process space).
+ // See https://github.com/llvm/llvm-project/issues/109384
+ BC->SegmentMapInfo[0] = SegmentInfo{0, 0x1d1c, 0, 0x1d1c, 0x10000, false};
+ BC->SegmentMapInfo[0x11d40] =
+ SegmentInfo{0x11d40, 0x11e0, 0x1d40, 0x11e0, 0x10000, true};
+ BC->SegmentMapInfo[0x22f20] =
+ SegmentInfo{0x22f20, 0x10e0, 0x2f20, 0x1f0, 0x10000, false};
+ BC->SegmentMapInfo[0x33110] =
+ SegmentInfo{0x33110, 0x89, 0x3110, 0x88, 0x10000, false};
+
+ std::optional<uint64_t> BaseAddress =
+ BC->getBaseAddressForMapping(0xaaaaaaab1000, 0x1000);
+ ASSERT_TRUE(BaseAddress.has_value());
+ ASSERT_EQ(*BaseAddress, 0xaaaaaaaa0000ULL);
+}
clang-tools-extra/CMakeLists.txt
@@ -27,7 +27,6 @@ add_subdirectory(clang-move)
add_subdirectory(clang-query)
add_subdirectory(include-cleaner)
add_subdirectory(pp-trace)
-add_subdirectory(pseudo)
add_subdirectory(tool-template)
option(CLANG_TOOLS_EXTRA_INCLUDE_DOCS "Generate build targets for the Clang Extra Tools docs."
clang-tools-extra/CODE_OWNERS.TXT
@@ -23,7 +23,7 @@ D: clang-tidy
N: Manuel Klimek
E: klimek@google.com
-D: clang-rename, all parts of clang-tools-extra not covered by someone else
+D: all parts of clang-tools-extra not covered by someone else
N: Sam McCall
E: sammccall@google.com
clang-tools-extra/clang-apply-replacements/CMakeLists.txt
@@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangApplyReplacements
+add_clang_library(clangApplyReplacements STATIC
lib/Tooling/ApplyReplacements.cpp
DEPENDS
clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp
@@ -148,11 +148,8 @@ groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs,
if (auto Entry = SM.getFileManager().getOptionalFileRef(Path)) {
if (SourceTU) {
- auto &Replaces = DiagReplacements[*Entry];
- auto It = Replaces.find(R);
- if (It == Replaces.end())
- Replaces.emplace(R, SourceTU);
- else if (It->second != SourceTU)
+ auto [It, Inserted] = DiagReplacements[*Entry].try_emplace(R, SourceTU);
+ if (!Inserted && It->second != SourceTU)
// This replacement is a duplicate of one suggested by another TU.
return;
}
clang-tools-extra/clang-change-namespace/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangChangeNamespace
+add_clang_library(clangChangeNamespace STATIC
ChangeNamespace.cpp
DEPENDS
clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp
@@ -606,9 +606,8 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
// If this reference has been processed as a function call, we do not
// process it again.
- if (ProcessedFuncRefs.count(FuncRef))
+ if (!ProcessedFuncRefs.insert(FuncRef).second)
return;
- ProcessedFuncRefs.insert(FuncRef);
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
assert(Func);
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
clang-tools-extra/clang-doc/CMakeLists.txt
@@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangDoc
+add_clang_library(clangDoc STATIC
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
clang-tools-extra/clang-doc/Generators.h
@@ -55,4 +55,8 @@ std::string getTagType(TagTypeKind AS);
} // namespace doc
} // namespace clang
+namespace llvm {
+extern template class Registry<clang::doc::Generator>;
+} // namespace llvm
+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -300,8 +300,7 @@ Example usage for a project using a compile commands database:
llvm::StringMap<std::vector<StringRef>> USRToBitcode;
Executor->get()->getToolResults()->forEachResult(
[&](StringRef Key, StringRef Value) {
- auto R = USRToBitcode.try_emplace(Key, std::vector<StringRef>());
- R.first->second.emplace_back(Value);
+ USRToBitcode[Key].emplace_back(Value);
});
// Collects all Infos according to their unique USR value. This map is added
clang-tools-extra/clang-include-fixer/CMakeLists.txt
@@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
support
)
-add_clang_library(clangIncludeFixer
+add_clang_library(clangIncludeFixer STATIC
IncludeFixer.cpp
IncludeFixerContext.cpp
InMemorySymbolIndex.cpp
clang-tools-extra/clang-include-fixer/InMemorySymbolIndex.cpp
@@ -21,7 +21,7 @@ InMemorySymbolIndex::InMemorySymbolIndex(
std::vector<SymbolAndSignals>
InMemorySymbolIndex::search(llvm::StringRef Identifier) {
- auto I = LookupTable.find(std::string(Identifier));
+ auto I = LookupTable.find(Identifier);
if (I != LookupTable.end())
return I->second;
return {};
clang-tools-extra/clang-include-fixer/InMemorySymbolIndex.h
@@ -27,7 +27,8 @@ public:
search(llvm::StringRef Identifier) override;
private:
- std::map<std::string, std::vector<find_all_symbols::SymbolAndSignals>>
+ std::map<std::string, std::vector<find_all_symbols::SymbolAndSignals>,
+ std::less<>>
LookupTable;
};
clang-tools-extra/clang-include-fixer/find-all-symbols/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(findAllSymbols
+add_clang_library(findAllSymbols STATIC
FindAllSymbols.cpp
FindAllSymbolsAction.cpp
FindAllMacros.cpp
clang-tools-extra/clang-include-fixer/plugin/CMakeLists.txt
@@ -1,4 +1,4 @@
-add_clang_library(clangIncludeFixerPlugin
+add_clang_library(clangIncludeFixerPlugin STATIC
IncludeFixerPlugin.cpp
LINK_LIBS
clang-tools-extra/clang-move/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangMove
+add_clang_library(clangMove STATIC
Move.cpp
HelperDeclRefGraph.cpp
clang-tools-extra/clang-move/HelperDeclRefGraph.cpp
@@ -76,11 +76,10 @@ HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
std::function<void(const CallGraphNode *)> VisitNode =
[&](const CallGraphNode *Node) {
- if (ConnectedNodes.count(Node))
+ if (!ConnectedNodes.insert(Node).second)
return;
- ConnectedNodes.insert(Node);
- for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
- VisitNode(*It);
+ for (const CallGraphNode::CallRecord &Callee : *Node)
+ VisitNode(Callee);
};
VisitNode(RootNode);
clang-tools-extra/clang-move/tool/ClangMove.cpp
@@ -199,7 +199,7 @@ int main(int argc, const char **argv) {
for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
OS << " {\n";
OS << " \"FilePath\": \"" << *I << "\",\n";
- const auto Entry = FileMgr.getFile(*I);
+ const auto Entry = FileMgr.getOptionalFileRef(*I);
auto ID = SM.translateFile(*Entry);
std::string Content;
llvm::raw_string_ostream ContentStream(Content);
clang-tools-extra/clang-query/CMakeLists.txt
@@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangQuery
+add_clang_library(clangQuery STATIC
Query.cpp
QueryParser.cpp
clang-tools-extra/clang-query/Query.cpp
@@ -44,7 +44,9 @@ bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
" set bind-root (true|false) "
"Set whether to bind the root matcher to \"root\".\n"
" set print-matcher (true|false) "
- "Set whether to print the current matcher,\n"
+ "Set whether to print the current matcher.\n"
+ " set enable-profile (true|false) "
+ "Set whether to enable matcher profiling.\n"
" set traversal <kind> "
"Set traversal kind of clang-query session. Available kinds are:\n"
" AsIs "
@@ -82,10 +84,24 @@ namespace {
struct CollectBoundNodes : MatchFinder::MatchCallback {
std::vector<BoundNodes> &Bindings;
- CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
+ StringRef Unit;
+ CollectBoundNodes(std::vector<BoundNodes> &Bindings, StringRef Unit)
+ : Bindings(Bindings), Unit(Unit) {}
void run(const MatchFinder::MatchResult &Result) override {
Bindings.push_back(Result.Nodes);
}
+ StringRef getID() const override { return Unit; }
+};
+
+struct QueryProfiler {
+ llvm::StringMap<llvm::TimeRecord> Records;
+
+ ~QueryProfiler() {
+ llvm::TimerGroup TG("clang-query", "clang-query matcher profiling",
+ Records);
+ TG.print(llvm::errs());
+ llvm::errs().flush();
+ }
};
} // namespace
@@ -93,8 +109,19 @@ struct CollectBoundNodes : MatchFinder::MatchCallback {
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
unsigned MatchCount = 0;
+ std::optional<QueryProfiler> Profiler;
+ if (QS.EnableProfile)
+ Profiler.emplace();
+
for (auto &AST : QS.ASTs) {
- MatchFinder Finder;
+ ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
+ std::optional<llvm::StringMap<llvm::TimeRecord>> Records;
+ if (QS.EnableProfile) {
+ Records.emplace();
+ FinderOptions.CheckProfiling.emplace(*Records);
+ }
+
+ MatchFinder Finder(FinderOptions);
std::vector<BoundNodes> Matches;
DynTypedMatcher MaybeBoundMatcher = Matcher;
if (QS.BindRoot) {
@@ -102,7 +129,8 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
if (M)
MaybeBoundMatcher = *M;
}
- CollectBoundNodes Collect(Matches);
+ StringRef OrigSrcName = AST->getOriginalSourceFileName();
+ CollectBoundNodes Collect(Matches, OrigSrcName);
if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
OS << "Not a valid top-level matcher.\n";
return false;
@@ -111,6 +139,8 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
ASTContext &Ctx = AST->getASTContext();
Ctx.getParentMapContext().setTraversalKind(QS.TK);
Finder.matchAST(Ctx);
+ if (QS.EnableProfile)
+ Profiler->Records[OrigSrcName] += (*Records)[OrigSrcName];
if (QS.PrintMatcher) {
SmallVector<StringRef, 4> Lines;
@@ -146,7 +176,7 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
TD.emitDiagnostic(
FullSourceLoc(R.getBegin(), AST->getSourceManager()),
DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
- CharSourceRange::getTokenRange(R), std::nullopt);
+ CharSourceRange::getTokenRange(R), {});
}
}
if (QS.PrintOutput) {
clang-tools-extra/clang-query/QueryParser.cpp
@@ -182,6 +182,7 @@ enum ParsedQueryVariable {
PQV_Output,
PQV_BindRoot,
PQV_PrintMatcher,
+ PQV_EnableProfile,
PQV_Traversal
};
@@ -285,6 +286,7 @@ QueryRef QueryParser::doParse() {
.Case("output", PQV_Output)
.Case("bind-root", PQV_BindRoot)
.Case("print-matcher", PQV_PrintMatcher)
+ .Case("enable-profile", PQV_EnableProfile)
.Case("traversal", PQV_Traversal)
.Default(PQV_Invalid);
if (VarStr.empty())
@@ -303,6 +305,9 @@ QueryRef QueryParser::doParse() {
case PQV_PrintMatcher:
Q = parseSetBool(&QuerySession::PrintMatcher);
break;
+ case PQV_EnableProfile:
+ Q = parseSetBool(&QuerySession::EnableProfile);
+ break;
case PQV_Traversal:
Q = parseSetTraversalKind(&QuerySession::TK);
break;
clang-tools-extra/clang-query/QuerySession.h
@@ -26,7 +26,7 @@ public:
QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
: ASTs(ASTs), PrintOutput(false), DiagOutput(true),
DetailedASTOutput(false), BindRoot(true), PrintMatcher(false),
- Terminate(false), TK(TK_AsIs) {}
+ EnableProfile(false), Terminate(false), TK(TK_AsIs) {}
llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;
@@ -36,6 +36,7 @@ public:
bool BindRoot;
bool PrintMatcher;
+ bool EnableProfile;
bool Terminate;
TraversalKind TK;
clang-tools-extra/clang-reorder-fields/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
support
)
-add_clang_library(clangReorderFields
+add_clang_library(clangReorderFields STATIC
ReorderFieldsAction.cpp
DEPENDS
clang-tools-extra/clang-tidy/CMakeLists.txt
@@ -8,7 +8,7 @@ configure_file(
${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-config.h)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
-add_clang_library(clangTidy
+add_clang_library(clangTidy STATIC
ClangTidy.cpp
ClangTidyCheck.cpp
ClangTidyModule.cpp
@@ -35,7 +35,6 @@ clang_target_link_libraries(clangTidy
clangFrontend
clangLex
clangRewrite
- clangSema
clangSerialization
clangTooling
clangToolingCore
clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -336,6 +336,7 @@ private:
std::unique_ptr<ClangTidyProfiling> Profiling;
std::unique_ptr<ast_matchers::MatchFinder> Finder;
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
+ void anchor() override {};
};
} // namespace
@@ -458,7 +459,6 @@ ClangTidyASTConsumerFactory::createASTConsumer(
if (!AnalyzerOptions.CheckersAndPackages.empty()) {
setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
AnalyzerOptions.AnalysisDiagOpt = PD_NONE;
- AnalyzerOptions.eagerlyAssumeBinOpBifurcation = true;
std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
ento::CreateAnalysisConsumer(Compiler);
AnalysisConsumer->AddDiagnosticConsumer(
@@ -672,6 +672,18 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
Buffer.append(AnalyzerCheck);
Result.Names.insert(Buffer);
}
+ for (std::string OptionName : {
+#define GET_CHECKER_OPTIONS
+#define CHECKER_OPTION(TYPE, CHECKER, OPTION_NAME, DESCRIPTION, DEFAULT, \
+ RELEASE, HIDDEN) \
+ Twine(AnalyzerCheckNamePrefix).concat(CHECKER ":" OPTION_NAME).str(),
+
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef CHECKER_OPTION
+#undef GET_CHECKER_OPTIONS
+ }) {
+ Result.Options.insert(OptionName);
+ }
#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
Context.setOptionsCollector(&Result.Options);
clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -380,7 +380,6 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
++Context.Stats.ErrorsIgnoredNOLINT;
// Ignored a warning, should ignore related notes as well
LastErrorWasIgnored = true;
- Context.DiagEngine->Clear();
for (const auto &Error : SuppressionErrors)
Context.diag(Error);
return;
@@ -457,7 +456,6 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
if (Info.hasSourceManager())
checkFilters(Info.getLocation(), Info.getSourceManager());
- Context.DiagEngine->Clear();
for (const auto &Error : SuppressionErrors)
Context.diag(Error);
}
clang-tools-extra/clang-tidy/ClangTidyModuleRegistry.h
@@ -18,4 +18,8 @@ using ClangTidyModuleRegistry = llvm::Registry<ClangTidyModule>;
} // namespace clang::tidy
+namespace llvm {
+extern template class Registry<clang::tidy::ClangTidyModule>;
+} // namespace llvm
+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp
@@ -116,9 +116,8 @@ void ExpandModularHeadersPPCallbacks::handleModuleFile(
if (!MF)
return;
// Avoid processing a ModuleFile more than once.
- if (VisitedModules.count(MF))
+ if (!VisitedModules.insert(MF).second)
return;
- VisitedModules.insert(MF);
// Visit all the input files of this module and mark them to record their
// contents later.
clang-tools-extra/clang-tidy/abseil/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangTidyAbseilModule
+add_clang_library(clangTidyAbseilModule STATIC
AbseilTidyModule.cpp
CleanupCtadCheck.cpp
DurationAdditionCheck.cpp
clang-tools-extra/clang-tidy/abseil/DurationRewriter.cpp
@@ -244,7 +244,7 @@ std::optional<DurationScale> getScaleForDurationInverse(llvm::StringRef Name) {
{"ToDoubleNanoseconds", DurationScale::Nanoseconds},
{"ToInt64Nanoseconds", DurationScale::Nanoseconds}});
- auto ScaleIter = ScaleMap.find(std::string(Name));
+ auto ScaleIter = ScaleMap.find(Name);
if (ScaleIter == ScaleMap.end())
return std::nullopt;
@@ -260,7 +260,7 @@ std::optional<DurationScale> getScaleForTimeInverse(llvm::StringRef Name) {
{"ToUnixMicros", DurationScale::Microseconds},
{"ToUnixNanos", DurationScale::Nanoseconds}});
- auto ScaleIter = ScaleMap.find(std::string(Name));
+ auto ScaleIter = ScaleMap.find(Name);
if (ScaleIter == ScaleMap.end())
return std::nullopt;
clang-tools-extra/clang-tidy/add_new_check.py
@@ -8,9 +8,6 @@
#
# ===-----------------------------------------------------------------------===#
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import io
import itertools
@@ -19,10 +16,13 @@ import re
import sys
import textwrap
+# FIXME Python 3.9: Replace typing.Tuple with builtins.tuple.
+from typing import Optional, Tuple, Match
+
# Adapts the module's CMakelist file. Returns 'True' if it could add a new
# entry and 'False' if the entry already existed.
-def adapt_cmake(module_path, check_name_camel):
+def adapt_cmake(module_path: str, check_name_camel: str) -> bool:
filename = os.path.join(module_path, "CMakeLists.txt")
# The documentation files are encoded using UTF-8, however on Windows the
@@ -57,14 +57,14 @@ def adapt_cmake(module_path, check_name_camel):
# Adds a header for the new check.
def write_header(
- module_path,
- module,
- namespace,
- check_name,
- check_name_camel,
- description,
- lang_restrict,
-):
+ module_path: str,
+ module: str,
+ namespace: str,
+ check_name: str,
+ check_name_camel: str,
+ description: str,
+ lang_restrict: str,
+) -> None:
wrapped_desc = "\n".join(
textwrap.wrap(
description, width=80, initial_indent="/// ", subsequent_indent="/// "
@@ -139,7 +139,9 @@ public:
# Adds the implementation of the new check.
-def write_implementation(module_path, module, namespace, check_name_camel):
+def write_implementation(
+ module_path: str, module: str, namespace: str, check_name_camel: str
+) -> None:
filename = os.path.join(module_path, check_name_camel) + ".cpp"
print("Creating %s..." % filename)
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
@@ -187,7 +189,7 @@ void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
# Returns the source filename that implements the module.
-def get_module_filename(module_path, module):
+def get_module_filename(module_path: str, module: str) -> str:
modulecpp = list(
filter(
lambda p: p.lower() == module.lower() + "tidymodule.cpp",
@@ -198,7 +200,9 @@ def get_module_filename(module_path, module):
# Modifies the module to include the new check.
-def adapt_module(module_path, module, check_name, check_name_camel):
+def adapt_module(
+ module_path: str, module: str, check_name: str, check_name_camel: str
+) -> None:
filename = get_module_filename(module_path, module)
with io.open(filename, "r", encoding="utf8") as f:
lines = f.readlines()
@@ -217,10 +221,10 @@ def adapt_module(module_path, module, check_name, check_name_camel):
+ '");\n'
)
- lines = iter(lines)
+ lines_iter = iter(lines)
try:
while True:
- line = next(lines)
+ line = next(lines_iter)
if not header_added:
match = re.search('#include "(.*)"', line)
if match:
@@ -247,10 +251,11 @@ def adapt_module(module_path, module, check_name, check_name_camel):
# If we didn't find the check name on this line, look on the
# next one.
prev_line = line
- line = next(lines)
+ line = next(lines_iter)
match = re.search(' *"([^"]*)"', line)
if match:
current_check_name = match.group(1)
+ assert current_check_name
if current_check_name > check_fq_name:
check_added = True
f.write(check_decl)
@@ -262,7 +267,9 @@ def adapt_module(module_path, module, check_name, check_name_camel):
# Adds a release notes entry.
-def add_release_notes(module_path, module, check_name, description):
+def add_release_notes(
+ module_path: str, module: str, check_name: str, description: str
+) -> None:
wrapped_desc = "\n".join(
textwrap.wrap(
description, width=80, initial_indent=" ", subsequent_indent=" "
@@ -324,9 +331,14 @@ def add_release_notes(module_path, module, check_name, description):
# Adds a test for the check.
-def write_test(module_path, module, check_name, test_extension, test_standard):
- if test_standard:
- test_standard = f"-std={test_standard}-or-later "
+def write_test(
+ module_path: str,
+ module: str,
+ check_name: str,
+ test_extension: str,
+ test_standard: Optional[str],
+) -> None:
+ test_standard = f"-std={test_standard}-or-later " if test_standard else ""
check_name_dashes = module + "-" + check_name
filename = os.path.normpath(
os.path.join(
@@ -362,7 +374,7 @@ void awesome_f2();
)
-def get_actual_filename(dirname, filename):
+def get_actual_filename(dirname: str, filename: str) -> str:
if not os.path.isdir(dirname):
return ""
name = os.path.join(dirname, filename)
@@ -376,7 +388,7 @@ def get_actual_filename(dirname, filename):
# Recreates the list of checks in the docs/clang-tidy/checks directory.
-def update_checks_list(clang_tidy_path):
+def update_checks_list(clang_tidy_path: str) -> None:
docs_dir = os.path.join(clang_tidy_path, "../docs/clang-tidy/checks")
filename = os.path.normpath(os.path.join(docs_dir, "list.rst"))
# Read the content of the current list.rst file
@@ -390,12 +402,12 @@ def update_checks_list(clang_tidy_path):
for file in filter(
lambda s: s.endswith(".rst"), os.listdir(os.path.join(docs_dir, subdir))
):
- doc_files.append([subdir, file])
+ doc_files.append((subdir, file))
doc_files.sort()
# We couldn't find the source file from the check name, so try to find the
# class name that corresponds to the check in the module file.
- def filename_from_module(module_name, check_name):
+ def filename_from_module(module_name: str, check_name: str) -> str:
module_path = os.path.join(clang_tidy_path, module_name)
if not os.path.isdir(module_path):
return ""
@@ -433,7 +445,7 @@ def update_checks_list(clang_tidy_path):
return ""
# Examine code looking for a c'tor definition to get the base class name.
- def get_base_class(code, check_file):
+ def get_base_class(code: str, check_file: str) -> str:
check_class_name = os.path.splitext(os.path.basename(check_file))[0]
ctor_pattern = check_class_name + r"\([^:]*\)\s*:\s*([A-Z][A-Za-z0-9]*Check)\("
matches = re.search(r"\s+" + check_class_name + "::" + ctor_pattern, code)
@@ -452,7 +464,7 @@ def update_checks_list(clang_tidy_path):
return ""
# Some simple heuristics to figure out if a check has an autofix or not.
- def has_fixits(code):
+ def has_fixits(code: str) -> bool:
for needle in [
"FixItHint",
"ReplacementText",
@@ -464,7 +476,7 @@ def update_checks_list(clang_tidy_path):
return False
# Try to figure out of the check supports fixits.
- def has_auto_fix(check_name):
+ def has_auto_fix(check_name: str) -> str:
dirname, _, check_name = check_name.partition("-")
check_file = get_actual_filename(
@@ -499,7 +511,7 @@ def update_checks_list(clang_tidy_path):
return ""
- def process_doc(doc_file):
+ def process_doc(doc_file: Tuple[str, str]) -> Tuple[str, Optional[Match[str]]]:
check_name = doc_file[0] + "-" + doc_file[1].replace(".rst", "")
with io.open(os.path.join(docs_dir, *doc_file), "r", encoding="utf8") as doc:
@@ -508,13 +520,13 @@ def update_checks_list(clang_tidy_path):
if match:
# Orphan page, don't list it.
- return "", ""
+ return "", None
match = re.search(r".*:http-equiv=refresh: \d+;URL=(.*).html(.*)", content)
# Is it a redirect?
return check_name, match
- def format_link(doc_file):
+ def format_link(doc_file: Tuple[str, str]) -> str:
check_name, match = process_doc(doc_file)
if not match and check_name and not check_name.startswith("clang-analyzer-"):
return " :doc:`%(check_name)s <%(module)s/%(check)s>`,%(autofix)s\n" % {
@@ -526,7 +538,7 @@ def update_checks_list(clang_tidy_path):
else:
return ""
- def format_link_alias(doc_file):
+ def format_link_alias(doc_file: Tuple[str, str]) -> str:
check_name, match = process_doc(doc_file)
if (match or (check_name.startswith("clang-analyzer-"))) and check_name:
module = doc_file[0]
@@ -543,6 +555,7 @@ def update_checks_list(clang_tidy_path):
ref_end = "_"
else:
redirect_parts = re.search(r"^\.\./([^/]*)/([^/]*)$", match.group(1))
+ assert redirect_parts
title = redirect_parts[1] + "-" + redirect_parts[2]
target = redirect_parts[1] + "/" + redirect_parts[2]
autofix = has_auto_fix(title)
@@ -599,7 +612,7 @@ def update_checks_list(clang_tidy_path):
# Adds a documentation for the check.
-def write_docs(module_path, module, check_name):
+def write_docs(module_path: str, module: str, check_name: str) -> None:
check_name_dashes = module + "-" + check_name
filename = os.path.normpath(
os.path.join(
@@ -623,15 +636,15 @@ FIXME: Describe what patterns does the check detect and why. Give examples.
)
-def get_camel_name(check_name):
+def get_camel_name(check_name: str) -> str:
return "".join(map(lambda elem: elem.capitalize(), check_name.split("-")))
-def get_camel_check_name(check_name):
+def get_camel_check_name(check_name: str) -> str:
return get_camel_name(check_name) + "Check"
-def main():
+def main() -> None:
language_to_extension = {
"c": "c",
"c++": "cpp",
@@ -756,6 +769,8 @@ def main():
)
elif language in ["objc", "objc++"]:
language_restrict = "%(lang)s.ObjC"
+ else:
+ raise ValueError(f"Unsupported language '{language}' was specified")
write_header(
module_path,
@@ -769,7 +784,7 @@ def main():
write_implementation(module_path, module, namespace, check_name_camel)
adapt_module(module_path, module, check_name, check_name_camel)
add_release_notes(module_path, module, check_name, description)
- test_extension = language_to_extension.get(language)
+ test_extension = language_to_extension[language]
write_test(module_path, module, check_name, test_extension, args.standard)
write_docs(module_path, module, check_name)
update_checks_list(clang_tidy_path)
clang-tools-extra/clang-tidy/altera/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
support
)
-add_clang_library(clangTidyAlteraModule
+add_clang_library(clangTidyAlteraModule STATIC
AlteraTidyModule.cpp
IdDependentBackwardBranchCheck.cpp
KernelNameRestrictionCheck.cpp
clang-tools-extra/clang-tidy/altera/IdDependentBackwardBranchCheck.cpp
@@ -78,16 +78,22 @@ void IdDependentBackwardBranchCheck::registerMatchers(MatchFinder *Finder) {
IdDependentBackwardBranchCheck::IdDependencyRecord *
IdDependentBackwardBranchCheck::hasIdDepVar(const Expr *Expression) {
+ if (!Expression)
+ return nullptr;
+
if (const auto *Declaration = dyn_cast<DeclRefExpr>(Expression)) {
// It is a DeclRefExpr, so check if it's an ID-dependent variable.
- const auto *CheckVariable = dyn_cast<VarDecl>(Declaration->getDecl());
+ const auto *CheckVariable =
+ dyn_cast_if_present<VarDecl>(Declaration->getDecl());
+ if (!CheckVariable)
+ return nullptr;
auto FoundVariable = IdDepVarsMap.find(CheckVariable);
if (FoundVariable == IdDepVarsMap.end())
return nullptr;
return &(FoundVariable->second);
}
for (const auto *Child : Expression->children())
- if (const auto *ChildExpression = dyn_cast<Expr>(Child))
+ if (const auto *ChildExpression = dyn_cast_if_present<Expr>(Child))
if (IdDependencyRecord *Result = hasIdDepVar(ChildExpression))
return Result;
return nullptr;
@@ -95,16 +101,21 @@ IdDependentBackwardBranchCheck::hasIdDepVar(const Expr *Expression) {
IdDependentBackwardBranchCheck::IdDependencyRecord *
IdDependentBackwardBranchCheck::hasIdDepField(const Expr *Expression) {
+ if (!Expression)
+ return nullptr;
+
if (const auto *MemberExpression = dyn_cast<MemberExpr>(Expression)) {
const auto *CheckField =
- dyn_cast<FieldDecl>(MemberExpression->getMemberDecl());
+ dyn_cast_if_present<FieldDecl>(MemberExpression->getMemberDecl());
+ if (!CheckField)
+ return nullptr;
auto FoundField = IdDepFieldsMap.find(CheckField);
if (FoundField == IdDepFieldsMap.end())
return nullptr;
return &(FoundField->second);
}
for (const auto *Child : Expression->children())
- if (const auto *ChildExpression = dyn_cast<Expr>(Child))
+ if (const auto *ChildExpression = dyn_cast_if_present<Expr>(Child))
if (IdDependencyRecord *Result = hasIdDepField(ChildExpression))
return Result;
return nullptr;
clang-tools-extra/clang-tidy/android/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangTidyAndroidModule
+add_clang_library(clangTidyAndroidModule STATIC
AndroidTidyModule.cpp
CloexecAccept4Check.cpp
CloexecAcceptCheck.cpp
clang-tools-extra/clang-tidy/boost/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangTidyBoostModule
+add_clang_library(clangTidyBoostModule STATIC
BoostTidyModule.cpp
UseRangesCheck.cpp
UseToStringCheck.cpp
clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp
@@ -204,7 +204,7 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
ReplacerMap Results;
static const Signature SingleSig = {{0}};
static const Signature TwoSig = {{0}, {2}};
- static const auto AddFrom =
+ const auto AddFrom =
[&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
std::initializer_list<StringRef> Names, StringRef Prefix) {
llvm::SmallString<64> Buffer;
@@ -214,17 +214,17 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
}
};
- static const auto AddFromStd =
- [](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
- std::initializer_list<StringRef> Names) {
+ const auto AddFromStd =
+ [&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
+ std::initializer_list<StringRef> Names) {
AddFrom(Replacer, Names, "std");
};
- static const auto AddFromBoost =
- [](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
- std::initializer_list<
- std::pair<StringRef, std::initializer_list<StringRef>>>
- NamespaceAndNames) {
+ const auto AddFromBoost =
+ [&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
+ std::initializer_list<
+ std::pair<StringRef, std::initializer_list<StringRef>>>
+ NamespaceAndNames) {
for (auto [Namespace, Names] : NamespaceAndNames)
AddFrom(Replacer, Names,
SmallString<64>{"boost", (Namespace.empty() ? "" : "::"),
clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.cpp
@@ -0,0 +1,46 @@
+//===--- BitwisePointerCastCheck.cpp - clang-tidy -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BitwisePointerCastCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void BitwisePointerCastCheck::registerMatchers(MatchFinder *Finder) {
+ if (getLangOpts().CPlusPlus20) {
+ auto IsPointerType = refersToType(qualType(isAnyPointer()));
+ Finder->addMatcher(callExpr(hasDeclaration(functionDecl(allOf(
+ hasName("::std::bit_cast"),
+ hasTemplateArgument(0, IsPointerType),
+ hasTemplateArgument(1, IsPointerType)))))
+ .bind("bit_cast"),
+ this);
+ }
+
+ auto IsDoublePointerType =
+ hasType(qualType(pointsTo(qualType(isAnyPointer()))));
+ Finder->addMatcher(callExpr(hasArgument(0, IsDoublePointerType),
+ hasArgument(1, IsDoublePointerType),
+ hasDeclaration(functionDecl(hasName("::memcpy"))))
+ .bind("memcpy"),
+ this);
+}
+
+void BitwisePointerCastCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("bit_cast"))
+ diag(Call->getBeginLoc(),
+ "do not use 'std::bit_cast' to cast between pointers")
+ << Call->getSourceRange();
+ else if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("memcpy"))
+ diag(Call->getBeginLoc(), "do not use 'memcpy' to cast between pointers")
+ << Call->getSourceRange();
+}
+
+} // namespace clang::tidy::bugprone
clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.h
@@ -0,0 +1,34 @@
+//===--- BitwisePointerCastCheck.h - clang-tidy -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BITWISEPOINTERCASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BITWISEPOINTERCASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Warns about code that tries to cast between pointers by means of
+/// ``std::bit_cast`` or ``memcpy``.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/bitwise-pointer-cast.html
+class BitwisePointerCastCheck : public ClangTidyCheck {
+public:
+ BitwisePointerCastCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BITWISEPOINTERCASTCHECK_H
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -14,6 +14,7 @@
#include "AssertSideEffectCheck.h"
#include "AssignmentInIfConditionCheck.h"
#include "BadSignalToKillThreadCheck.h"
+#include "BitwisePointerCastCheck.h"
#include "BoolPointerImplicitConversionCheck.h"
#include "BranchCloneCheck.h"
#include "CastingThroughVoidCheck.h"
@@ -48,6 +49,7 @@
#include "MultipleStatementMacroCheck.h"
#include "NoEscapeCheck.h"
#include "NonZeroEnumToBoolConversionCheck.h"
+#include "NondeterministicPointerIterationOrderCheck.h"
#include "NotNullTerminatedResultCheck.h"
#include "OptionalValueConversionCheck.h"
#include "ParentVirtualCallCheck.h"
@@ -78,6 +80,7 @@
#include "SuspiciousStringviewDataUsageCheck.h"
#include "SwappedArgumentsCheck.h"
#include "SwitchMissingDefaultCaseCheck.h"
+#include "TaggedUnionMemberCountCheck.h"
#include "TerminatingContinueCheck.h"
#include "ThrowKeywordMissingCheck.h"
#include "TooSmallLoopVariableCheck.h"
@@ -108,6 +111,8 @@ public:
"bugprone-assignment-in-if-condition");
CheckFactories.registerCheck<BadSignalToKillThreadCheck>(
"bugprone-bad-signal-to-kill-thread");
+ CheckFactories.registerCheck<BitwisePointerCastCheck>(
+ "bugprone-bitwise-pointer-cast");
CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
"bugprone-bool-pointer-implicit-conversion");
CheckFactories.registerCheck<BranchCloneCheck>("bugprone-branch-clone");
@@ -170,6 +175,8 @@ public:
"bugprone-multiple-new-in-one-expression");
CheckFactories.registerCheck<MultipleStatementMacroCheck>(
"bugprone-multiple-statement-macro");
+ CheckFactories.registerCheck<NondeterministicPointerIterationOrderCheck>(
+ "bugprone-nondeterministic-pointer-iteration-order");
CheckFactories.registerCheck<OptionalValueConversionCheck>(
"bugprone-optional-value-conversion");
CheckFactories.registerCheck<PointerArithmeticOnPolymorphicObjectCheck>(
@@ -229,6 +236,8 @@ public:
"bugprone-suspicious-stringview-data-usage");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
"bugprone-swapped-arguments");
+ CheckFactories.registerCheck<TaggedUnionMemberCountCheck>(
+ "bugprone-tagged-union-member-count");
CheckFactories.registerCheck<TerminatingContinueCheck>(
"bugprone-terminating-continue");
CheckFactories.registerCheck<ThrowKeywordMissingCheck>(
clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -3,11 +3,12 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangTidyBugproneModule
+add_clang_library(clangTidyBugproneModule STATIC
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
AssignmentInIfConditionCheck.cpp
BadSignalToKillThreadCheck.cpp
+ BitwisePointerCastCheck.cpp
BoolPointerImplicitConversionCheck.cpp
BranchCloneCheck.cpp
BugproneTidyModule.cpp
@@ -44,6 +45,7 @@ add_clang_library(clangTidyBugproneModule
MultipleNewInOneExpressionCheck.cpp
MultipleStatementMacroCheck.cpp
NoEscapeCheck.cpp
+ NondeterministicPointerIterationOrderCheck.cpp
NonZeroEnumToBoolConversionCheck.cpp
NotNullTerminatedResultCheck.cpp
OptionalValueConversionCheck.cpp
@@ -73,6 +75,7 @@ add_clang_library(clangTidyBugproneModule
SuspiciousSemicolonCheck.cpp
SuspiciousStringCompareCheck.cpp
SwappedArgumentsCheck.cpp
+ TaggedUnionMemberCountCheck.cpp
TerminatingContinueCheck.cpp
ThrowKeywordMissingCheck.cpp
TooSmallLoopVariableCheck.cpp
clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp
@@ -38,7 +38,9 @@ void CastingThroughVoidCheck::check(const MatchFinder::MatchResult &Result) {
const auto ST = *Result.Nodes.getNodeAs<QualType>("source_type");
const auto VT = *Result.Nodes.getNodeAs<QualType>("void_type");
const auto *CE = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast");
- diag(CE->getExprLoc(), "do not cast %0 to %1 through %2") << ST << TT << VT;
+ diag(CE->getExprLoc(),
+ "do not cast %0 to %1 through %2; use reinterpret_cast instead")
+ << ST << TT << VT;
}
} // namespace clang::tidy::bugprone
clang-tools-extra/clang-tidy/bugprone/CrtpConstructorAccessibilityCheck.cpp
@@ -165,7 +165,7 @@ void CrtpConstructorAccessibilityCheck::check(
WithFriendHintIfNeeded(
diag(Ctor->getLocation(),
- "%0 contructor allows the CRTP to be %select{inherited "
+ "%0 constructor allows the CRTP to be %select{inherited "
"from|constructed}1 as a regular template class; consider making "
"it private%select{| and declaring the derived class as friend}2")
<< Access << IsPublic << NeedsFriend
clang-tools-extra/clang-tidy/bugprone/DanglingHandleCheck.cpp
@@ -97,8 +97,8 @@ DanglingHandleCheck::DanglingHandleCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
HandleClasses(utils::options::parseStringList(Options.get(
- "HandleClasses",
- "std::basic_string_view;std::experimental::basic_string_view"))),
+ "HandleClasses", "std::basic_string_view;std::experimental::basic_"
+ "string_view;std::span"))),
IsAHandle(cxxRecordDecl(hasAnyName(HandleClasses)).bind("handle")) {}
void DanglingHandleCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp
@@ -107,7 +107,6 @@ static std::string getNameOfNamespace(const CXXRecordDecl *Decl) {
std::string Ns;
llvm::raw_string_ostream OStream(Ns);
NsDecl->printQualifiedName(OStream);
- OStream.flush();
return Ns.empty() ? "(global)" : Ns;
}
@@ -147,12 +146,13 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
}
// Check if a definition in another namespace exists.
const auto DeclName = CurDecl->getName();
- if (!DeclNameToDefinitions.contains(DeclName)) {
+ auto It = DeclNameToDefinitions.find(DeclName);
+ if (It == DeclNameToDefinitions.end()) {
continue; // No definition in this translation unit, we can skip it.
}
// Make a warning for each definition with the same name (in other
// namespaces).
- const auto &Definitions = DeclNameToDefinitions[DeclName];
+ const auto &Definitions = It->second;
for (const auto *Def : Definitions) {
diag(CurDecl->getLocation(),
"no definition found for %0, but a definition with "
clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp
@@ -9,7 +9,6 @@
#include "ForwardingReferenceOverloadCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include <algorithm>
using namespace clang::ast_matchers;
@@ -19,14 +18,14 @@ namespace {
// Check if the given type is related to std::enable_if.
AST_MATCHER(QualType, isEnableIf) {
auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
- if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
+ if (!Spec)
return false;
- }
- const NamedDecl *TypeDecl =
- Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
- return TypeDecl->isInStdNamespace() &&
- (TypeDecl->getName() == "enable_if" ||
- TypeDecl->getName() == "enable_if_t");
+
+ const TemplateDecl *TDecl = Spec->getTemplateName().getAsTemplateDecl();
+
+ return TDecl && TDecl->isInStdNamespace() &&
+ (TDecl->getName() == "enable_if" ||
+ TDecl->getName() == "enable_if_t");
};
const Type *BaseType = Node.getTypePtr();
// Case: pointer or reference to enable_if.
clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.cpp
@@ -0,0 +1,78 @@
+//===----- NondeterministicPointerIterationOrderCheck.cpp - clang-tidy ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NondeterministicPointerIterationOrderCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void NondeterministicPointerIterationOrderCheck::registerMatchers(
+ MatchFinder *Finder) {
+
+ auto LoopVariable = varDecl(hasType(
+ qualType(hasCanonicalType(anyOf(referenceType(), pointerType())))));
+
+ auto RangeInit = declRefExpr(to(varDecl(
+ hasType(recordDecl(hasAnyName("std::unordered_set", "std::unordered_map",
+ "std::unordered_multiset",
+ "std::unordered_multimap"))
+ .bind("recorddecl")))));
+
+ Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVariable),
+ hasRangeInit(RangeInit.bind("rangeinit")))
+ .bind("cxxForRangeStmt"),
+ this);
+
+ auto SortFuncM = callee(functionDecl(hasAnyName(
+ "std::is_sorted", "std::nth_element", "std::sort", "std::partial_sort",
+ "std::partition", "std::stable_partition", "std::stable_sort")));
+
+ auto IteratesPointerEltsM = hasArgument(
+ 0,
+ cxxMemberCallExpr(on(hasType(cxxRecordDecl(has(fieldDecl(hasType(qualType(
+ hasCanonicalType(pointsTo(hasCanonicalType(pointerType()))))))))))));
+
+ Finder->addMatcher(
+ callExpr(allOf(SortFuncM, IteratesPointerEltsM)).bind("sortsemantic"),
+ this);
+}
+
+void NondeterministicPointerIterationOrderCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *ForRangePointers =
+ Result.Nodes.getNodeAs<CXXForRangeStmt>("cxxForRangeStmt");
+
+ if ((ForRangePointers) && !(ForRangePointers->getBeginLoc().isMacroID())) {
+ const auto *RangeInit = Result.Nodes.getNodeAs<Stmt>("rangeinit");
+ if (const auto *ClassTemplate =
+ Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(
+ "recorddecl")) {
+ const TemplateArgumentList &TemplateArgs =
+ ClassTemplate->getTemplateArgs();
+ const bool IsAlgoArgPointer =
+ TemplateArgs[0].getAsType()->isPointerType();
+
+ if (IsAlgoArgPointer) {
+ SourceRange R = RangeInit->getSourceRange();
+ diag(R.getBegin(), "iteration of pointers is nondeterministic") << R;
+ }
+ }
+ return;
+ }
+ const auto *SortPointers = Result.Nodes.getNodeAs<Stmt>("sortsemantic");
+
+ if ((SortPointers) && !(SortPointers->getBeginLoc().isMacroID())) {
+ SourceRange R = SortPointers->getSourceRange();
+ diag(R.getBegin(), "sorting pointers is nondeterministic") << R;
+ }
+}
+
+} // namespace clang::tidy::bugprone
clang-tools-extra/clang-tidy/bugprone/NondeterministicPointerIterationOrderCheck.h
@@ -0,0 +1,39 @@
+//=== NondeterministicPointerIterationOrderCheck.h - clang-tidy -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Finds nondeterministic usages of pointers in unordered containers. The
+/// check also finds calls to sorting-like algorithms on a container of
+/// pointers.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/nondeterministic-pointer-iteration-order.html
+class NondeterministicPointerIterationOrderCheck : public ClangTidyCheck {
+public:
+ NondeterministicPointerIterationOrderCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NONDETERMINISTIC_POINTER_ITERATION_ORDER_CHECK_H
clang-tools-extra/clang-tidy/bugprone/PosixReturnCheck.cpp
@@ -7,19 +7,17 @@
//===----------------------------------------------------------------------===//
#include "PosixReturnCheck.h"
-#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
-static StringRef getFunctionSpelling(const MatchFinder::MatchResult &Result,
- const char *BindingStr) {
- const CallExpr *MatchedCall = cast<CallExpr>(
- (Result.Nodes.getNodeAs<BinaryOperator>(BindingStr))->getLHS());
+static StringRef getFunctionSpelling(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("call");
const SourceManager &SM = *Result.SourceManager;
return Lexer::getSourceText(CharSourceRange::getTokenRange(
MatchedCall->getCallee()->getSourceRange()),
@@ -27,32 +25,40 @@ static StringRef getFunctionSpelling(const MatchFinder::MatchResult &Result,
}
void PosixReturnCheck::registerMatchers(MatchFinder *Finder) {
+ const auto PosixCall =
+ callExpr(callee(functionDecl(
+ anyOf(matchesName("^::posix_"), matchesName("^::pthread_")),
+ unless(hasName("::posix_openpt")))))
+ .bind("call");
+ const auto ZeroIntegerLiteral = integerLiteral(equals(0));
+ const auto NegIntegerLiteral =
+ unaryOperator(hasOperatorName("-"), hasUnaryOperand(integerLiteral()));
+
Finder->addMatcher(
binaryOperator(
- hasOperatorName("<"),
- hasLHS(callExpr(callee(functionDecl(
- anyOf(matchesName("^::posix_"), matchesName("^::pthread_")),
- unless(hasName("::posix_openpt")))))),
- hasRHS(integerLiteral(equals(0))))
+ anyOf(allOf(hasOperatorName("<"), hasLHS(PosixCall),
+ hasRHS(ZeroIntegerLiteral)),
+ allOf(hasOperatorName(">"), hasLHS(ZeroIntegerLiteral),
+ hasRHS(PosixCall))))
.bind("ltzop"),
this);
Finder->addMatcher(
binaryOperator(
- hasOperatorName(">="),
- hasLHS(callExpr(callee(functionDecl(
- anyOf(matchesName("^::posix_"), matchesName("^::pthread_")),
- unless(hasName("::posix_openpt")))))),
- hasRHS(integerLiteral(equals(0))))
+ anyOf(allOf(hasOperatorName(">="), hasLHS(PosixCall),
+ hasRHS(ZeroIntegerLiteral)),
+ allOf(hasOperatorName("<="), hasLHS(ZeroIntegerLiteral),
+ hasRHS(PosixCall))))
.bind("atop"),
this);
+ Finder->addMatcher(binaryOperator(hasAnyOperatorName("==", "!="),
+ hasOperands(PosixCall, NegIntegerLiteral))
+ .bind("binop"),
+ this);
Finder->addMatcher(
- binaryOperator(
- hasAnyOperatorName("==", "!=", "<=", "<"),
- hasLHS(callExpr(callee(functionDecl(
- anyOf(matchesName("^::posix_"), matchesName("^::pthread_")),
- unless(hasName("::posix_openpt")))))),
- hasRHS(unaryOperator(hasOperatorName("-"),
- hasUnaryOperand(integerLiteral()))))
+ binaryOperator(anyOf(allOf(hasAnyOperatorName("<=", "<"),
+ hasLHS(PosixCall), hasRHS(NegIntegerLiteral)),
+ allOf(hasAnyOperatorName(">", ">="),
+ hasLHS(NegIntegerLiteral), hasRHS(PosixCall))))
.bind("binop"),
this);
}
@@ -61,10 +67,13 @@ void PosixReturnCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *LessThanZeroOp =
Result.Nodes.getNodeAs<BinaryOperator>("ltzop")) {
SourceLocation OperatorLoc = LessThanZeroOp->getOperatorLoc();
+ StringRef NewBinOp =
+ LessThanZeroOp->getOpcode() == BinaryOperator::Opcode::BO_LT ? ">"
+ : "<";
diag(OperatorLoc, "the comparison always evaluates to false because %0 "
"always returns non-negative values")
- << getFunctionSpelling(Result, "ltzop")
- << FixItHint::CreateReplacement(OperatorLoc, Twine(">").str());
+ << getFunctionSpelling(Result)
+ << FixItHint::CreateReplacement(OperatorLoc, NewBinOp);
return;
}
if (const auto *AlwaysTrueOp =
@@ -72,12 +81,12 @@ void PosixReturnCheck::check(const MatchFinder::MatchResult &Result) {
diag(AlwaysTrueOp->getOperatorLoc(),
"the comparison always evaluates to true because %0 always returns "
"non-negative values")
- << getFunctionSpelling(Result, "atop");
+ << getFunctionSpelling(Result);
return;
}
const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binop");
diag(BinOp->getOperatorLoc(), "%0 only returns non-negative values")
- << getFunctionSpelling(Result, "binop");
+ << getFunctionSpelling(Result);
}
} // namespace clang::tidy::bugprone
clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "ReturnConstRefFromParameterCheck.h"
+#include "clang/AST/Expr.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
@@ -15,19 +16,24 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) {
+ const auto DRef = ignoringParens(
+ declRefExpr(
+ to(parmVarDecl(hasType(hasCanonicalType(
+ qualType(lValueReferenceType(pointee(
+ qualType(isConstQualified()))))
+ .bind("type"))))
+ .bind("param")))
+ .bind("dref"));
+ const auto Func =
+ functionDecl(hasReturnTypeLoc(loc(
+ qualType(hasCanonicalType(equalsBoundNode("type"))))))
+ .bind("func");
+
+ Finder->addMatcher(returnStmt(hasReturnValue(DRef), hasAncestor(Func)), this);
Finder->addMatcher(
- returnStmt(
- hasReturnValue(declRefExpr(
- to(parmVarDecl(hasType(hasCanonicalType(
- qualType(lValueReferenceType(pointee(
- qualType(isConstQualified()))))
- .bind("type"))))
- .bind("param")))),
- hasAncestor(
- functionDecl(hasReturnTypeLoc(loc(qualType(
- hasCanonicalType(equalsBoundNode("type"))))))
- .bind("func")))
- .bind("ret"),
+ returnStmt(hasReturnValue(ignoringParens(conditionalOperator(
+ eachOf(hasTrueExpression(DRef), hasFalseExpression(DRef)),
+ hasAncestor(Func))))),
this);
}
@@ -85,8 +91,8 @@ void ReturnConstRefFromParameterCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
const auto *PD = Result.Nodes.getNodeAs<ParmVarDecl>("param");
- const auto *R = Result.Nodes.getNodeAs<ReturnStmt>("ret");
- const SourceRange Range = R->getRetValue()->getSourceRange();
+ const auto *DRef = Result.Nodes.getNodeAs<DeclRefExpr>("dref");
+ const SourceRange Range = DRef->getSourceRange();
if (Range.isInvalid())
return;
clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
@@ -48,6 +48,8 @@ AST_MATCHER_P2(Expr, hasSizeOfDescendant, int, Depth,
return false;
}
+AST_MATCHER(Expr, offsetOfExpr) { return isa<OffsetOfExpr>(Node); }
+
CharUnits getSizeOfType(const ASTContext &Ctx, const Type *Ty) {
if (!Ty || Ty->isIncompleteType() || Ty->isDependentType() ||
isa<DependentSizedArrayType>(Ty) || !Ty->isConstantSizeType())
@@ -68,7 +70,9 @@ SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
Options.get("WarnOnSizeOfCompareToConstant", true)),
WarnOnSizeOfPointerToAggregate(
Options.get("WarnOnSizeOfPointerToAggregate", true)),
- WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)) {}
+ WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)),
+ WarnOnOffsetDividedBySizeOf(
+ Options.get("WarnOnOffsetDividedBySizeOf", true)) {}
void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
@@ -80,6 +84,8 @@ void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnSizeOfPointerToAggregate",
WarnOnSizeOfPointerToAggregate);
Options.store(Opts, "WarnOnSizeOfPointer", WarnOnSizeOfPointer);
+ Options.store(Opts, "WarnOnOffsetDividedBySizeOf",
+ WarnOnOffsetDividedBySizeOf);
}
void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
@@ -221,17 +227,15 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
const auto ElemType =
arrayType(hasElementType(recordType().bind("elem-type")));
const auto ElemPtrType = pointerType(pointee(type().bind("elem-ptr-type")));
+ const auto SizeofDivideExpr = binaryOperator(
+ hasOperatorName("/"),
+ hasLHS(
+ ignoringParenImpCasts(sizeOfExpr(hasArgumentOfType(hasCanonicalType(
+ type(anyOf(ElemType, ElemPtrType, type())).bind("num-type")))))),
+ hasRHS(ignoringParenImpCasts(sizeOfExpr(
+ hasArgumentOfType(hasCanonicalType(type().bind("denom-type")))))));
- Finder->addMatcher(
- binaryOperator(
- hasOperatorName("/"),
- hasLHS(ignoringParenImpCasts(sizeOfExpr(hasArgumentOfType(
- hasCanonicalType(type(anyOf(ElemType, ElemPtrType, type()))
- .bind("num-type")))))),
- hasRHS(ignoringParenImpCasts(sizeOfExpr(
- hasArgumentOfType(hasCanonicalType(type().bind("denom-type")))))))
- .bind("sizeof-divide-expr"),
- this);
+ Finder->addMatcher(SizeofDivideExpr.bind("sizeof-divide-expr"), this);
// Detect expression like: sizeof(...) * sizeof(...)); most likely an error.
Finder->addMatcher(binaryOperator(hasOperatorName("*"),
@@ -257,8 +261,9 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
.bind("sizeof-sizeof-expr"),
this);
- // Detect sizeof in pointer arithmetic like: N * sizeof(S) == P1 - P2 or
- // (P1 - P2) / sizeof(S) where P1 and P2 are pointers to type S.
+ // Detect sizeof usage in comparisons involving pointer arithmetics, such as
+ // N * sizeof(T) == P1 - P2 or (P1 - P2) / sizeof(T), where P1 and P2 are
+ // pointers to a type T.
const auto PtrDiffExpr = binaryOperator(
hasOperatorName("-"),
hasLHS(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
@@ -285,6 +290,48 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
hasRHS(ignoringParenImpCasts(SizeOfExpr.bind("sizeof-ptr-div-expr"))))
.bind("sizeof-in-ptr-arithmetic-div"),
this);
+
+ // SEI CERT ARR39-C. Do not add or subtract a scaled integer to a pointer.
+ // Detect sizeof, alignof and offsetof usage in pointer arithmetics where
+ // they are used to scale the numeric distance, which is scaled again by
+ // the pointer arithmetic operator. This can result in forming invalid
+ // offsets.
+ //
+ // Examples, where P is a pointer, N is some integer (both compile-time and
+ // run-time): P + sizeof(T), P + sizeof(*P), P + N * sizeof(*P).
+ //
+ // This check does not warn on cases where the pointee type is "1 byte",
+ // as those cases can often come from generics and also do not constitute a
+ // problem because the size does not affect the scale used.
+ const auto InterestingPtrTyForPtrArithmetic =
+ pointerType(pointee(qualType().bind("pointee-type")));
+ const auto SizeofLikeScaleExpr =
+ expr(anyOf(unaryExprOrTypeTraitExpr(ofKind(UETT_SizeOf)),
+ unaryExprOrTypeTraitExpr(ofKind(UETT_AlignOf)),
+ offsetOfExpr()))
+ .bind("sizeof-in-ptr-arithmetic-scale-expr");
+ const auto PtrArithmeticIntegerScaleExpr = binaryOperator(
+ WarnOnOffsetDividedBySizeOf ? binaryOperator(hasAnyOperatorName("*", "/"))
+ : binaryOperator(hasOperatorName("*")),
+ // sizeof(...) * sizeof(...) and sizeof(...) / sizeof(...) is handled
+ // by this check on another path.
+ hasOperands(expr(hasType(isInteger()), unless(SizeofLikeScaleExpr)),
+ SizeofLikeScaleExpr));
+ const auto PtrArithmeticScaledIntegerExpr =
+ expr(anyOf(SizeofLikeScaleExpr, PtrArithmeticIntegerScaleExpr),
+ unless(SizeofDivideExpr));
+
+ Finder->addMatcher(
+ expr(anyOf(
+ binaryOperator(hasAnyOperatorName("+", "-"),
+ hasOperands(hasType(InterestingPtrTyForPtrArithmetic),
+ PtrArithmeticScaledIntegerExpr))
+ .bind("sizeof-in-ptr-arithmetic-plusminus"),
+ binaryOperator(hasAnyOperatorName("+=", "-="),
+ hasLHS(hasType(InterestingPtrTyForPtrArithmetic)),
+ hasRHS(PtrArithmeticScaledIntegerExpr))
+ .bind("sizeof-in-ptr-arithmetic-plusminus"))),
+ this);
}
void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
@@ -409,6 +456,43 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
<< SizeOfExpr->getSourceRange() << E->getOperatorLoc()
<< E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
}
+ } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
+ "sizeof-in-ptr-arithmetic-plusminus")) {
+ const auto *PointeeTy = Result.Nodes.getNodeAs<QualType>("pointee-type");
+ const auto *ScaleExpr =
+ Result.Nodes.getNodeAs<Expr>("sizeof-in-ptr-arithmetic-scale-expr");
+ const CharUnits PointeeSize = getSizeOfType(Ctx, PointeeTy->getTypePtr());
+ const int ScaleKind = [ScaleExpr]() {
+ if (const auto *UTTE = dyn_cast<UnaryExprOrTypeTraitExpr>(ScaleExpr))
+ switch (UTTE->getKind()) {
+ case UETT_SizeOf:
+ return 0;
+ case UETT_AlignOf:
+ return 1;
+ default:
+ return -1;
+ }
+
+ if (isa<OffsetOfExpr>(ScaleExpr))
+ return 2;
+
+ return -1;
+ }();
+
+ if (ScaleKind != -1 && PointeeSize > CharUnits::One()) {
+ diag(E->getExprLoc(),
+ "suspicious usage of '%select{sizeof|alignof|offsetof}0(...)' in "
+ "pointer arithmetic; this scaled value will be scaled again by the "
+ "'%1' operator")
+ << ScaleKind << E->getOpcodeStr() << ScaleExpr->getSourceRange();
+ diag(E->getExprLoc(),
+ "'%0' in pointer arithmetic internally scales with 'sizeof(%1)' == "
+ "%2",
+ DiagnosticIDs::Note)
+ << E->getOpcodeStr()
+ << PointeeTy->getAsString(Ctx.getPrintingPolicy())
+ << PointeeSize.getQuantity();
+ }
}
}
clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.h
@@ -13,7 +13,7 @@
namespace clang::tidy::bugprone {
-/// Find suspicious usages of sizeof expression.
+/// Find suspicious usages of sizeof expressions.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/sizeof-expression.html
@@ -31,6 +31,7 @@ private:
const bool WarnOnSizeOfCompareToConstant;
const bool WarnOnSizeOfPointerToAggregate;
const bool WarnOnSizeOfPointer;
+ const bool WarnOnOffsetDividedBySizeOf;
};
} // namespace clang::tidy::bugprone
clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -0,0 +1,199 @@
+//===--- TaggedUnionMemberCountCheck.cpp - clang-tidy ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TaggedUnionMemberCountCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallSet.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static constexpr llvm::StringLiteral StrictModeOptionName = "StrictMode";
+static constexpr llvm::StringLiteral EnableCountingEnumHeuristicOptionName =
+ "EnableCountingEnumHeuristic";
+static constexpr llvm::StringLiteral CountingEnumPrefixesOptionName =
+ "CountingEnumPrefixes";
+static constexpr llvm::StringLiteral CountingEnumSuffixesOptionName =
+ "CountingEnumSuffixes";
+
+static constexpr bool StrictModeOptionDefaultValue = false;
+static constexpr bool EnableCountingEnumHeuristicOptionDefaultValue = true;
+static constexpr llvm::StringLiteral CountingEnumPrefixesOptionDefaultValue =
+ "";
+static constexpr llvm::StringLiteral CountingEnumSuffixesOptionDefaultValue =
+ "count";
+
+static constexpr llvm::StringLiteral RootMatchBindName = "root";
+static constexpr llvm::StringLiteral UnionMatchBindName = "union";
+static constexpr llvm::StringLiteral TagMatchBindName = "tags";
+
+namespace {
+
+AST_MATCHER_P2(RecordDecl, fieldCountOfKindIsOne,
+ ast_matchers::internal::Matcher<FieldDecl>, InnerMatcher,
+ StringRef, BindName) {
+ // BoundNodesTreeBuilder resets itself when a match occurs.
+ // So to avoid losing previously saved binds, a temporary instance
+ // is used for matching.
+ //
+ // For precedence, see commit: 5b07de1a5faf4a22ae6fd982b877c5e7e3a76559
+ clang::ast_matchers::internal::BoundNodesTreeBuilder TempBuilder;
+
+ const FieldDecl *FirstMatch = nullptr;
+ for (const FieldDecl *Field : Node.fields()) {
+ if (InnerMatcher.matches(*Field, Finder, &TempBuilder)) {
+ if (FirstMatch) {
+ return false;
+ } else {
+ FirstMatch = Field;
+ }
+ }
+ }
+
+ if (FirstMatch) {
+ Builder->setBinding(BindName, clang::DynTypedNode::create(*FirstMatch));
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ StrictMode(
+ Options.get(StrictModeOptionName, StrictModeOptionDefaultValue)),
+ EnableCountingEnumHeuristic(
+ Options.get(EnableCountingEnumHeuristicOptionName,
+ EnableCountingEnumHeuristicOptionDefaultValue)),
+ CountingEnumPrefixes(utils::options::parseStringList(
+ Options.get(CountingEnumPrefixesOptionName,
+ CountingEnumPrefixesOptionDefaultValue))),
+ CountingEnumSuffixes(utils::options::parseStringList(
+ Options.get(CountingEnumSuffixesOptionName,
+ CountingEnumSuffixesOptionDefaultValue))) {
+ if (!EnableCountingEnumHeuristic) {
+ if (Options.get(CountingEnumPrefixesOptionName))
+ configurationDiag("%0: Counting enum heuristic is disabled but "
+ "%1 is set")
+ << Name << CountingEnumPrefixesOptionName;
+ if (Options.get(CountingEnumSuffixesOptionName))
+ configurationDiag("%0: Counting enum heuristic is disabled but "
+ "%1 is set")
+ << Name << CountingEnumSuffixesOptionName;
+ }
+}
+
+void TaggedUnionMemberCountCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, StrictModeOptionName, StrictMode);
+ Options.store(Opts, EnableCountingEnumHeuristicOptionName,
+ EnableCountingEnumHeuristic);
+ Options.store(Opts, CountingEnumPrefixesOptionName,
+ utils::options::serializeStringList(CountingEnumPrefixes));
+ Options.store(Opts, CountingEnumSuffixesOptionName,
+ utils::options::serializeStringList(CountingEnumSuffixes));
+}
+
+void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
+
+ auto UnionField = fieldDecl(hasType(qualType(
+ hasCanonicalType(recordType(hasDeclaration(recordDecl(isUnion())))))));
+
+ auto EnumField = fieldDecl(hasType(
+ qualType(hasCanonicalType(enumType(hasDeclaration(enumDecl()))))));
+
+ auto hasOneUnionField = fieldCountOfKindIsOne(UnionField, UnionMatchBindName);
+ auto hasOneEnumField = fieldCountOfKindIsOne(EnumField, TagMatchBindName);
+
+ Finder->addMatcher(recordDecl(anyOf(isStruct(), isClass()), hasOneUnionField,
+ hasOneEnumField, unless(isImplicit()))
+ .bind(RootMatchBindName),
+ this);
+}
+
+bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name) const {
+ if (llvm::any_of(CountingEnumPrefixes, [Name](StringRef Prefix) -> bool {
+ return Name.starts_with_insensitive(Prefix);
+ }))
+ return true;
+ if (llvm::any_of(CountingEnumSuffixes, [Name](StringRef Suffix) -> bool {
+ return Name.ends_with_insensitive(Suffix);
+ }))
+ return true;
+ return false;
+}
+
+std::pair<const std::size_t, const EnumConstantDecl *>
+TaggedUnionMemberCountCheck::getNumberOfEnumValues(const EnumDecl *ED) {
+ llvm::SmallSet<llvm::APSInt, 16> EnumValues;
+
+ const EnumConstantDecl *LastEnumConstant = nullptr;
+ for (const EnumConstantDecl *Enumerator : ED->enumerators()) {
+ EnumValues.insert(Enumerator->getInitVal());
+ LastEnumConstant = Enumerator;
+ }
+
+ if (EnableCountingEnumHeuristic && LastEnumConstant &&
+ isCountingEnumLikeName(LastEnumConstant->getName()) &&
+ (LastEnumConstant->getInitVal() == (EnumValues.size() - 1))) {
+ return {EnumValues.size() - 1, LastEnumConstant};
+ }
+
+ return {EnumValues.size(), nullptr};
+}
+
+void TaggedUnionMemberCountCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Root = Result.Nodes.getNodeAs<RecordDecl>(RootMatchBindName);
+ const auto *UnionField =
+ Result.Nodes.getNodeAs<FieldDecl>(UnionMatchBindName);
+ const auto *TagField = Result.Nodes.getNodeAs<FieldDecl>(TagMatchBindName);
+
+ assert(Root && "Root is missing!");
+ assert(UnionField && "UnionField is missing!");
+ assert(TagField && "TagField is missing!");
+ if (!Root || !UnionField || !TagField)
+ return;
+
+ const auto *UnionDef =
+ UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
+ const auto *EnumDef = llvm::dyn_cast<EnumDecl>(
+ TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
+
+ assert(UnionDef && "UnionDef is missing!");
+ assert(EnumDef && "EnumDef is missing!");
+ if (!UnionDef || !EnumDef)
+ return;
+
+ const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
+ auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef);
+
+ if (UnionMemberCount > TagCount) {
+ diag(Root->getLocation(),
+ "tagged union has more data members (%0) than tags (%1)!")
+ << UnionMemberCount << TagCount;
+ } else if (StrictMode && UnionMemberCount < TagCount) {
+ diag(Root->getLocation(),
+ "tagged union has fewer data members (%0) than tags (%1)!")
+ << UnionMemberCount << TagCount;
+ }
+
+ if (CountingEnumConstantDecl) {
+ diag(CountingEnumConstantDecl->getLocation(),
+ "assuming that this constant is just an auxiliary value and not "
+ "used for indicating a valid union data member",
+ DiagnosticIDs::Note);
+ }
+}
+
+} // namespace clang::tidy::bugprone
clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -0,0 +1,41 @@
+//===--- TaggedUnionMemberCountCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Gives warnings for tagged unions, where the number of tags is
+/// different from the number of data members inside the union.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/tagged-union-member-count.html
+class TaggedUnionMemberCountCheck : public ClangTidyCheck {
+public:
+ TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool StrictMode;
+ const bool EnableCountingEnumHeuristic;
+ const std::vector<StringRef> CountingEnumPrefixes;
+ const std::vector<StringRef> CountingEnumSuffixes;
+
+ std::pair<const std::size_t, const EnumConstantDecl *>
+ getNumberOfEnumValues(const EnumDecl *ED);
+ bool isCountingEnumLikeName(StringRef Name) const;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "UnsafeFunctionsCheck.h"
+#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/PPCallbacks.h"
@@ -18,6 +19,10 @@ using namespace llvm;
namespace clang::tidy::bugprone {
+static constexpr llvm::StringLiteral OptionNameCustomFunctions =
+ "CustomFunctions";
+static constexpr llvm::StringLiteral OptionNameReportDefaultFunctions =
+ "ReportDefaultFunctions";
static constexpr llvm::StringLiteral OptionNameReportMoreUnsafeFunctions =
"ReportMoreUnsafeFunctions";
@@ -26,6 +31,8 @@ static constexpr llvm::StringLiteral FunctionNamesWithAnnexKReplacementId =
static constexpr llvm::StringLiteral FunctionNamesId = "FunctionsNames";
static constexpr llvm::StringLiteral AdditionalFunctionNamesId =
"AdditionalFunctionsNames";
+static constexpr llvm::StringLiteral CustomFunctionNamesId =
+ "CustomFunctionNames";
static constexpr llvm::StringLiteral DeclRefId = "DRE";
static std::optional<std::string>
@@ -127,57 +134,128 @@ static bool isAnnexKAvailable(std::optional<bool> &CacheVar, Preprocessor *PP,
return CacheVar.value();
}
+static std::vector<UnsafeFunctionsCheck::CheckedFunction>
+parseCheckedFunctions(StringRef Option, ClangTidyContext *Context) {
+ const std::vector<StringRef> Functions =
+ utils::options::parseStringList(Option);
+ std::vector<UnsafeFunctionsCheck::CheckedFunction> Result;
+ Result.reserve(Functions.size());
+
+ for (StringRef Function : Functions) {
+ if (Function.empty())
+ continue;
+
+ const auto [Name, Rest] = Function.split(',');
+ const auto [Replacement, Reason] = Rest.split(',');
+
+ if (Name.trim().empty()) {
+ Context->configurationDiag("invalid configuration value for option '%0'; "
+ "expected the name of an unsafe function")
+ << OptionNameCustomFunctions;
+ continue;
+ }
+
+ Result.push_back(
+ {Name.trim().str(),
+ matchers::MatchesAnyListedNameMatcher::NameMatcher(Name.trim()),
+ Replacement.trim().str(), Reason.trim().str()});
+ }
+
+ return Result;
+}
+
+static std::string serializeCheckedFunctions(
+ const std::vector<UnsafeFunctionsCheck::CheckedFunction> &Functions) {
+ std::vector<std::string> Result;
+ Result.reserve(Functions.size());
+
+ for (const auto &Entry : Functions) {
+ if (Entry.Reason.empty())
+ Result.push_back(Entry.Name + "," + Entry.Replacement);
+ else
+ Result.push_back(Entry.Name + "," + Entry.Replacement + "," +
+ Entry.Reason);
+ }
+
+ return llvm::join(Result, ";");
+}
+
UnsafeFunctionsCheck::UnsafeFunctionsCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
+ CustomFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomFunctions, ""), Context)),
+ ReportDefaultFunctions(
+ Options.get(OptionNameReportDefaultFunctions, true)),
ReportMoreUnsafeFunctions(
Options.get(OptionNameReportMoreUnsafeFunctions, true)) {}
void UnsafeFunctionsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, OptionNameCustomFunctions,
+ serializeCheckedFunctions(CustomFunctions));
+ Options.store(Opts, OptionNameReportDefaultFunctions, ReportDefaultFunctions);
Options.store(Opts, OptionNameReportMoreUnsafeFunctions,
ReportMoreUnsafeFunctions);
}
void UnsafeFunctionsCheck::registerMatchers(MatchFinder *Finder) {
- if (getLangOpts().C11) {
- // Matching functions with safe replacements only in Annex K.
- auto FunctionNamesWithAnnexKReplacementMatcher = hasAnyName(
- "::bsearch", "::ctime", "::fopen", "::fprintf", "::freopen", "::fscanf",
- "::fwprintf", "::fwscanf", "::getenv", "::gmtime", "::localtime",
- "::mbsrtowcs", "::mbstowcs", "::memcpy", "::memmove", "::memset",
- "::printf", "::qsort", "::scanf", "::snprintf", "::sprintf", "::sscanf",
- "::strcat", "::strcpy", "::strerror", "::strlen", "::strncat",
- "::strncpy", "::strtok", "::swprintf", "::swscanf", "::vfprintf",
- "::vfscanf", "::vfwprintf", "::vfwscanf", "::vprintf", "::vscanf",
- "::vsnprintf", "::vsprintf", "::vsscanf", "::vswprintf", "::vswscanf",
- "::vwprintf", "::vwscanf", "::wcrtomb", "::wcscat", "::wcscpy",
- "::wcslen", "::wcsncat", "::wcsncpy", "::wcsrtombs", "::wcstok",
- "::wcstombs", "::wctomb", "::wmemcpy", "::wmemmove", "::wprintf",
- "::wscanf");
+ if (ReportDefaultFunctions) {
+ if (getLangOpts().C11) {
+ // Matching functions with safe replacements only in Annex K.
+ auto FunctionNamesWithAnnexKReplacementMatcher = hasAnyName(
+ "::bsearch", "::ctime", "::fopen", "::fprintf", "::freopen",
+ "::fscanf", "::fwprintf", "::fwscanf", "::getenv", "::gmtime",
+ "::localtime", "::mbsrtowcs", "::mbstowcs", "::memcpy", "::memmove",
+ "::memset", "::printf", "::qsort", "::scanf", "::snprintf",
+ "::sprintf", "::sscanf", "::strcat", "::strcpy", "::strerror",
+ "::strlen", "::strncat", "::strncpy", "::strtok", "::swprintf",
+ "::swscanf", "::vfprintf", "::vfscanf", "::vfwprintf", "::vfwscanf",
+ "::vprintf", "::vscanf", "::vsnprintf", "::vsprintf", "::vsscanf",
+ "::vswprintf", "::vswscanf", "::vwprintf", "::vwscanf", "::wcrtomb",
+ "::wcscat", "::wcscpy", "::wcslen", "::wcsncat", "::wcsncpy",
+ "::wcsrtombs", "::wcstok", "::wcstombs", "::wctomb", "::wmemcpy",
+ "::wmemmove", "::wprintf", "::wscanf");
+ Finder->addMatcher(
+ declRefExpr(to(functionDecl(FunctionNamesWithAnnexKReplacementMatcher)
+ .bind(FunctionNamesWithAnnexKReplacementId)))
+ .bind(DeclRefId),
+ this);
+ }
+
+ // Matching functions with replacements without Annex K.
+ auto FunctionNamesMatcher =
+ hasAnyName("::asctime", "asctime_r", "::gets", "::rewind", "::setbuf");
Finder->addMatcher(
- declRefExpr(to(functionDecl(FunctionNamesWithAnnexKReplacementMatcher)
- .bind(FunctionNamesWithAnnexKReplacementId)))
+ declRefExpr(
+ to(functionDecl(FunctionNamesMatcher).bind(FunctionNamesId)))
.bind(DeclRefId),
this);
+
+ if (ReportMoreUnsafeFunctions) {
+ // Matching functions with replacements without Annex K, at user request.
+ auto AdditionalFunctionNamesMatcher =
+ hasAnyName("::bcmp", "::bcopy", "::bzero", "::getpw", "::vfork");
+ Finder->addMatcher(
+ declRefExpr(to(functionDecl(AdditionalFunctionNamesMatcher)
+ .bind(AdditionalFunctionNamesId)))
+ .bind(DeclRefId),
+ this);
+ }
}
- // Matching functions with replacements without Annex K.
- auto FunctionNamesMatcher =
- hasAnyName("::asctime", "asctime_r", "::gets", "::rewind", "::setbuf");
- Finder->addMatcher(
- declRefExpr(to(functionDecl(FunctionNamesMatcher).bind(FunctionNamesId)))
- .bind(DeclRefId),
- this);
-
- if (ReportMoreUnsafeFunctions) {
- // Matching functions with replacements without Annex K, at user request.
- auto AdditionalFunctionNamesMatcher =
- hasAnyName("::bcmp", "::bcopy", "::bzero", "::getpw", "::vfork");
- Finder->addMatcher(
- declRefExpr(to(functionDecl(AdditionalFunctionNamesMatcher)
- .bind(AdditionalFunctionNamesId)))
- .bind(DeclRefId),
- this);
+ if (!CustomFunctions.empty()) {
+ std::vector<llvm::StringRef> FunctionNames;
+ FunctionNames.reserve(CustomFunctions.size());
+
+ for (const auto &Entry : CustomFunctions)
+ FunctionNames.push_back(Entry.Name);
+
+ auto CustomFunctionsMatcher = matchers::matchesAnyListedName(FunctionNames);
+
+ Finder->addMatcher(declRefExpr(to(functionDecl(CustomFunctionsMatcher)
+ .bind(CustomFunctionNamesId)))
+ .bind(DeclRefId),
+ this);
}
}
@@ -186,16 +264,46 @@ void UnsafeFunctionsCheck::check(const MatchFinder::MatchResult &Result) {
const auto *FuncDecl = cast<FunctionDecl>(DeclRef->getDecl());
assert(DeclRef && FuncDecl && "No valid matched node in check()");
+ // Only one of these are matched at a time.
const auto *AnnexK = Result.Nodes.getNodeAs<FunctionDecl>(
FunctionNamesWithAnnexKReplacementId);
const auto *Normal = Result.Nodes.getNodeAs<FunctionDecl>(FunctionNamesId);
const auto *Additional =
Result.Nodes.getNodeAs<FunctionDecl>(AdditionalFunctionNamesId);
- assert((AnnexK || Normal || Additional) && "No valid match category.");
+ const auto *Custom =
+ Result.Nodes.getNodeAs<FunctionDecl>(CustomFunctionNamesId);
+ assert((AnnexK || Normal || Additional || Custom) &&
+ "No valid match category.");
bool AnnexKIsAvailable =
isAnnexKAvailable(IsAnnexKAvailable, PP, getLangOpts());
StringRef FunctionName = FuncDecl->getName();
+
+ if (Custom) {
+ for (const auto &Entry : CustomFunctions) {
+ if (Entry.Pattern.match(*FuncDecl)) {
+ StringRef Reason =
+ Entry.Reason.empty() ? "is marked as unsafe" : Entry.Reason.c_str();
+
+ if (Entry.Replacement.empty()) {
+ diag(DeclRef->getExprLoc(), "function %0 %1; it should not be used")
+ << FuncDecl << Reason << Entry.Replacement
+ << DeclRef->getSourceRange();
+ } else {
+ diag(DeclRef->getExprLoc(),
+ "function %0 %1; '%2' should be used instead")
+ << FuncDecl << Reason << Entry.Replacement
+ << DeclRef->getSourceRange();
+ }
+
+ return;
+ }
+ }
+
+ llvm_unreachable("No custom function was matched.");
+ return;
+ }
+
const std::optional<std::string> ReplacementFunctionName =
[&]() -> std::optional<std::string> {
if (AnnexK) {
clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFUNCTIONSCHECK_H
#include "../ClangTidyCheck.h"
+#include "../utils/Matchers.h"
#include <optional>
namespace clang::tidy::bugprone {
@@ -32,7 +33,18 @@ public:
Preprocessor *ModuleExpanderPP) override;
void onEndOfTranslationUnit() override;
+ struct CheckedFunction {
+ std::string Name;
+ matchers::MatchesAnyListedNameMatcher::NameMatcher Pattern;
+ std::string Replacement;
+ std::string Reason;
+ };
+
private:
+ const std::vector<CheckedFunction> CustomFunctions;
+
+ // If true, the default set of functions are reported.
+ const bool ReportDefaultFunctions;
/// If true, additional functions from widely used API-s (such as POSIX) are
/// added to the list of reported functions.
const bool ReportMoreUnsafeFunctions;
clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
@@ -14,6 +14,7 @@
#include "../bugprone/ReservedIdentifierCheck.h"
#include "../bugprone/SignalHandlerCheck.h"
#include "../bugprone/SignedCharMisuseCheck.h"
+#include "../bugprone/SizeofExpressionCheck.h"
#include "../bugprone/SpuriouslyWakeUpFunctionsCheck.h"
#include "../bugprone/SuspiciousMemoryComparisonCheck.h"
#include "../bugprone/UnhandledSelfAssignmentCheck.h"
@@ -281,6 +282,9 @@ public:
"cert-oop58-cpp");
// C checkers
+ // ARR
+ CheckFactories.registerCheck<bugprone::SizeofExpressionCheck>(
+ "cert-arr39-c");
// CON
CheckFactories.registerCheck<bugprone::SpuriouslyWakeUpFunctionsCheck>(
"cert-con36-c");
@@ -332,6 +336,12 @@ public:
ClangTidyOptions getModuleOptions() override {
ClangTidyOptions Options;
ClangTidyOptions::OptionMap &Opts = Options.CheckOptions;
+ Opts["cert-arr39-c.WarnOnSizeOfConstant"] = "false";
+ Opts["cert-arr39-c.WarnOnSizeOfIntegerExpression"] = "false";
+ Opts["cert-arr39-c.WarnOnSizeOfThis"] = "false";
+ Opts["cert-arr39-c.WarnOnSizeOfCompareToConstant"] = "false";
+ Opts["cert-arr39-c.WarnOnSizeOfPointer"] = "false";
+ Opts["cert-arr39-c.WarnOnSizeOfPointerToAggregate"] = "false";
Opts["cert-dcl16-c.NewSuffixes"] = "L;LL;LU;LLU";
Opts["cert-err33-c.CheckedFunctions"] = CertErr33CCheckedFunctions;
Opts["cert-err33-c.AllowCastToVoid"] = "true";
clang-tools-extra/clang-tidy/cert/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
)
-add_clang_library(clangTidyCERTModule
+add_clang_library(clangTidyCERTModule STATIC
CERTTidyModule.cpp
CommandProcessorCheck.cpp
DefaultOperatorNewAlignmentCheck.cpp
clang-tools-extra/clang-tidy/cert/FloatLoopCounter.cpp
@@ -9,6 +9,7 @@
#include "FloatLoopCounter.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
using namespace clang::ast_matchers;
@@ -16,15 +17,30 @@ namespace clang::tidy::cert {
void FloatLoopCounter::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
- forStmt(hasIncrement(expr(hasType(realFloatingPointType())))).bind("for"),
+ forStmt(hasIncrement(forEachDescendant(
+ declRefExpr(hasType(realFloatingPointType()),
+ to(varDecl().bind("var")))
+ .bind("inc"))),
+ hasCondition(forEachDescendant(
+ declRefExpr(hasType(realFloatingPointType()),
+ to(varDecl(equalsBoundNode("var"))))
+ .bind("cond"))))
+ .bind("for"),
this);
}
void FloatLoopCounter::check(const MatchFinder::MatchResult &Result) {
const auto *FS = Result.Nodes.getNodeAs<ForStmt>("for");
- diag(FS->getInc()->getExprLoc(), "loop induction expression should not have "
- "floating-point type");
+ diag(FS->getInc()->getBeginLoc(), "loop induction expression should not have "
+ "floating-point type")
+ << Result.Nodes.getNodeAs<DeclRefExpr>("inc")->getSourceRange()
+ << Result.Nodes.getNodeAs<DeclRefExpr>("cond")->getSourceRange();
+
+ if (!FS->getInc()->getType()->isRealFloatingType())
+ if (const auto *V = Result.Nodes.getNodeAs<VarDecl>("var"))
+ diag(V->getBeginLoc(), "floating-point type loop induction variable",
+ DiagnosticIDs::Note);
}
} // namespace clang::tidy::cert
clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyConcurrencyModule
+add_clang_library(clangTidyConcurrencyModule STATIC
ConcurrencyTidyModule.cpp
MtUnsafeCheck.cpp
ThreadCanceltypeAsynchronousCheck.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyCppCoreGuidelinesModule
+add_clang_library(clangTidyCppCoreGuidelinesModule STATIC
AvoidCapturingLambdaCoroutinesCheck.cpp
AvoidConstOrRefDataMembersCheck.cpp
AvoidDoWhileCheck.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp
@@ -67,9 +67,7 @@ static bool canAdvanceAssignment(AssignedLevel Level) {
static void updateAssignmentLevel(
const FieldDecl *Field, const Expr *Init, const CXXConstructorDecl *Ctor,
llvm::DenseMap<const FieldDecl *, AssignedLevel> &AssignedFields) {
- auto It = AssignedFields.find(Field);
- if (It == AssignedFields.end())
- It = AssignedFields.insert({Field, AssignedLevel::None}).first;
+ auto It = AssignedFields.try_emplace(Field, AssignedLevel::None).first;
if (!canAdvanceAssignment(It->second))
// fast path for already decided field.
@@ -85,7 +83,7 @@ static void updateAssignmentLevel(
memberExpr(hasObjectExpression(cxxThisExpr()),
member(fieldDecl(indexNotLessThan(Field->getFieldIndex()))));
auto DeclMatcher = declRefExpr(
- to(varDecl(unless(parmVarDecl()), hasDeclContext(equalsNode(Ctor)))));
+ to(valueDecl(unless(parmVarDecl()), hasDeclContext(equalsNode(Ctor)))));
const bool HasDependence = !match(expr(anyOf(MemberMatcher, DeclMatcher,
hasDescendant(MemberMatcher),
hasDescendant(DeclMatcher))),
clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
@@ -474,10 +474,8 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer(
// It only includes fields that have not been fixed
SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) {
- if (!HasRecordClassMemberSet.contains(F)) {
+ if (HasRecordClassMemberSet.insert(F).second)
AllFieldsToInit.insert(F);
- HasRecordClassMemberSet.insert(F);
- }
});
if (FieldsToInit.empty())
return;
clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeUnionAccessCheck.cpp
@@ -23,8 +23,11 @@ void ProTypeUnionAccessCheck::registerMatchers(MatchFinder *Finder) {
void ProTypeUnionAccessCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Matched = Result.Nodes.getNodeAs<MemberExpr>("expr");
- diag(Matched->getMemberLoc(),
- "do not access members of unions; use (boost::)variant instead");
+ SourceLocation Loc = Matched->getMemberLoc();
+ if (Loc.isInvalid())
+ Loc = Matched->getBeginLoc();
+ diag(Loc, "do not access members of unions; consider using (boost::)variant "
+ "instead");
}
} // namespace clang::tidy::cppcoreguidelines
clang-tools-extra/clang-tidy/darwin/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyDarwinModule
+add_clang_library(clangTidyDarwinModule STATIC
AvoidSpinlockCheck.cpp
DarwinTidyModule.cpp
DispatchOnceNonstaticCheck.cpp
clang-tools-extra/clang-tidy/fuchsia/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyFuchsiaModule
+add_clang_library(clangTidyFuchsiaModule STATIC
DefaultArgumentsCallsCheck.cpp
DefaultArgumentsDeclarationsCheck.cpp
FuchsiaTidyModule.cpp
clang-tools-extra/clang-tidy/google/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyGoogleModule
+add_clang_library(clangTidyGoogleModule STATIC
AvoidCStyleCastsCheck.cpp
AvoidNSObjectNewCheck.cpp
AvoidThrowingObjCExceptionCheck.cpp
clang-tools-extra/clang-tidy/hicpp/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyHICPPModule
+add_clang_library(clangTidyHICPPModule STATIC
ExceptionBaseclassCheck.cpp
HICPPTidyModule.cpp
IgnoredRemoveResultCheck.cpp
clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyLinuxKernelModule
+add_clang_library(clangTidyLinuxKernelModule STATIC
LinuxKernelTidyModule.cpp
MustCheckErrsCheck.cpp
clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyLLVMModule
+add_clang_library(clangTidyLLVMModule STATIC
HeaderGuardCheck.cpp
IncludeOrderCheck.cpp
LLVMTidyModule.cpp
clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyLLVMLibcModule
+add_clang_library(clangTidyLLVMLibcModule STATIC
CalleeNamespaceCheck.cpp
ImplementationInNamespaceCheck.cpp
InlineFunctionDeclCheck.cpp
clang-tools-extra/clang-tidy/misc/CMakeLists.txt
@@ -17,7 +17,7 @@ add_custom_command(
add_custom_target(genconfusable DEPENDS Confusables.inc)
set_target_properties(genconfusable PROPERTIES FOLDER "Clang Tools Extra/Sourcegenning")
-add_clang_library(clangTidyMiscModule
+add_clang_library(clangTidyMiscModule STATIC
ConstCorrectnessCheck.cpp
CoroutineHostileRAIICheck.cpp
DefinitionsInHeadersCheck.cpp
clang-tools-extra/clang-tidy/misc/ConfusableIdentifierCheck.cpp
@@ -137,11 +137,11 @@ static bool mayShadow(const NamedDecl *ND0,
const ConfusableIdentifierCheck::ContextInfo *
ConfusableIdentifierCheck::getContextInfo(const DeclContext *DC) {
const DeclContext *PrimaryContext = DC->getPrimaryContext();
- auto It = ContextInfos.find(PrimaryContext);
- if (It != ContextInfos.end())
+ auto [It, Inserted] = ContextInfos.try_emplace(PrimaryContext);
+ if (!Inserted)
return &It->second;
- ContextInfo &Info = ContextInfos[PrimaryContext];
+ ContextInfo &Info = It->second;
Info.PrimaryContext = PrimaryContext;
Info.NonTransparentContext = PrimaryContext;
clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -172,8 +172,8 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
using namespace utils::fixit;
if (VC == VariableCategory::Value && TransformValues) {
- Diag << addQualifierToVarDecl(*Variable, *Result.Context,
- DeclSpec::TQ_const, QualifierTarget::Value,
+ Diag << addQualifierToVarDecl(*Variable, *Result.Context, Qualifiers::Const,
+ QualifierTarget::Value,
QualifierPolicy::Right);
// FIXME: Add '{}' for default initialization if no user-defined default
// constructor exists and there is no initializer.
@@ -181,8 +181,8 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
}
if (VC == VariableCategory::Reference && TransformReferences) {
- Diag << addQualifierToVarDecl(*Variable, *Result.Context,
- DeclSpec::TQ_const, QualifierTarget::Value,
+ Diag << addQualifierToVarDecl(*Variable, *Result.Context, Qualifiers::Const,
+ QualifierTarget::Value,
QualifierPolicy::Right);
return;
}
@@ -190,7 +190,7 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
if (VC == VariableCategory::Pointer) {
if (WarnPointersAsValues && TransformPointersAsValues) {
Diag << addQualifierToVarDecl(*Variable, *Result.Context,
- DeclSpec::TQ_const, QualifierTarget::Value,
+ Qualifiers::Const, QualifierTarget::Value,
QualifierPolicy::Right);
}
return;
clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp
@@ -102,7 +102,7 @@ void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
// inline is not allowed for main function.
if (FD->isMain())
return;
- diag(FD->getLocation(), /*Description=*/"make as 'inline'",
+ diag(FD->getLocation(), "mark the definition as 'inline'",
DiagnosticIDs::Note)
<< FixItHint::CreateInsertion(FD->getInnerLocStart(), "inline ");
} else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp
@@ -14,6 +14,16 @@ using namespace clang::ast_matchers;
namespace clang::tidy::misc {
+namespace {
+
+AST_MATCHER_P(CXXMethodDecl, firstParameter,
+ ast_matchers::internal::Matcher<ParmVarDecl>, InnerMatcher) {
+ unsigned N = Node.isExplicitObjectMemberFunction() ? 1 : 0;
+ return (N < Node.parameters().size() &&
+ InnerMatcher.matches(*Node.parameters()[N], Finder, Builder));
+}
+} // namespace
+
void UnconventionalAssignOperatorCheck::registerMatchers(
ast_matchers::MatchFinder *Finder) {
const auto HasGoodReturnType =
@@ -29,7 +39,7 @@ void UnconventionalAssignOperatorCheck::registerMatchers(
hasName("operator="), ofClass(recordDecl().bind("class")))
.bind("method");
const auto IsSelfAssign =
- cxxMethodDecl(IsAssign, hasParameter(0, parmVarDecl(hasType(IsSelf))))
+ cxxMethodDecl(IsAssign, firstParameter(parmVarDecl(hasType(IsSelf))))
.bind("method");
Finder->addMatcher(
@@ -41,8 +51,7 @@ void UnconventionalAssignOperatorCheck::registerMatchers(
rValueReferenceType(pointee(isConstQualified()))))));
Finder->addMatcher(
- cxxMethodDecl(IsSelfAssign,
- hasParameter(0, parmVarDecl(hasType(BadSelf))))
+ cxxMethodDecl(IsSelfAssign, firstParameter(parmVarDecl(hasType(BadSelf))))
.bind("ArgumentType"),
this);
clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
@@ -25,6 +25,13 @@ AST_MATCHER_P(DeducedTemplateSpecializationType, refsToTemplatedDecl,
return false;
}
+AST_MATCHER_P(Type, asTagDecl, clang::ast_matchers::internal::Matcher<TagDecl>,
+ DeclMatcher) {
+ if (const TagDecl *ND = Node.getAsTagDecl())
+ return DeclMatcher.matches(*ND, Finder, Builder);
+ return false;
+}
+
} // namespace
// A function that helps to tell whether a TargetDecl in a UsingDecl will be
@@ -61,7 +68,8 @@ void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(userDefinedLiteral().bind("used"), this);
Finder->addMatcher(
loc(elaboratedType(unless(hasQualifier(nestedNameSpecifier())),
- hasUnqualifiedDesugaredType(type().bind("usedType")))),
+ hasUnqualifiedDesugaredType(
+ type(asTagDecl(tagDecl().bind("used")))))),
this);
// Cases where we can identify the UsingShadowDecl directly, rather than
// just its target.
@@ -139,12 +147,6 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
return;
}
- if (const auto *T = Result.Nodes.getNodeAs<Type>("usedType")) {
- if (const auto *ND = T->getAsTagDecl())
- RemoveNamedDecl(ND);
- return;
- }
-
if (const auto *UsedShadow =
Result.Nodes.getNodeAs<UsingShadowDecl>("usedShadow")) {
removeFromFoundDecls(UsedShadow->getTargetDecl());
clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp
@@ -8,12 +8,15 @@
#include "UseInternalLinkageCheck.h"
#include "../utils/FileExtensionsUtils.h"
+#include "../utils/LexerUtils.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/Token.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang::ast_matchers;
@@ -113,7 +116,7 @@ static constexpr StringRef Message =
void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("fn")) {
DiagnosticBuilder DB = diag(FD->getLocation(), Message) << "function" << FD;
- SourceLocation FixLoc = FD->getTypeSpecStartLoc();
+ const SourceLocation FixLoc = FD->getInnerLocStart();
if (FixLoc.isInvalid() || FixLoc.isMacroID())
return;
if (FixMode == FixModeKind::UseStatic)
@@ -128,7 +131,7 @@ void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) {
return;
DiagnosticBuilder DB = diag(VD->getLocation(), Message) << "variable" << VD;
- SourceLocation FixLoc = VD->getTypeSpecStartLoc();
+ const SourceLocation FixLoc = VD->getInnerLocStart();
if (FixLoc.isInvalid() || FixLoc.isMacroID())
return;
if (FixMode == FixModeKind::UseStatic)
clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp
@@ -9,6 +9,7 @@
#include "AvoidCArraysCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
using namespace clang::ast_matchers;
@@ -60,6 +61,7 @@ void AvoidCArraysCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
typeLoc(hasValidBeginLoc(), hasType(arrayType()),
+ optionally(hasParent(parmVarDecl().bind("param_decl"))),
unless(anyOf(hasParent(parmVarDecl(isArgvOfMain())),
hasParent(varDecl(isExternC())),
hasParent(fieldDecl(
@@ -72,11 +74,28 @@ void AvoidCArraysCheck::registerMatchers(MatchFinder *Finder) {
void AvoidCArraysCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ArrayType = Result.Nodes.getNodeAs<TypeLoc>("typeloc");
-
+ const bool IsInParam =
+ Result.Nodes.getNodeAs<ParmVarDecl>("param_decl") != nullptr;
+ const bool IsVLA = ArrayType->getTypePtr()->isVariableArrayType();
+ enum class RecommendType { Array, Vector, Span };
+ llvm::SmallVector<const char *> RecommendTypes{};
+ if (IsVLA) {
+ RecommendTypes.push_back("'std::vector'");
+ } else if (ArrayType->getTypePtr()->isIncompleteArrayType() && IsInParam) {
+ // in function parameter, we also don't know the size of
+ // IncompleteArrayType.
+ if (Result.Context->getLangOpts().CPlusPlus20)
+ RecommendTypes.push_back("'std::span'");
+ else {
+ RecommendTypes.push_back("'std::array'");
+ RecommendTypes.push_back("'std::vector'");
+ }
+ } else {
+ RecommendTypes.push_back("'std::array'");
+ }
diag(ArrayType->getBeginLoc(),
- "do not declare %select{C-style|C VLA}0 arrays, use "
- "%select{std::array<>|std::vector<>}0 instead")
- << ArrayType->getTypePtr()->isVariableArrayType();
+ "do not declare %select{C-style|C VLA}0 arrays, use %1 instead")
+ << IsVLA << llvm::join(RecommendTypes, " or ");
}
} // namespace clang::tidy::modernize
clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyModernizeModule
+add_clang_library(clangTidyModernizeModule STATIC
AvoidBindCheck.cpp
AvoidCArraysCheck.cpp
ConcatNestedNamespacesCheck.cpp
clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp
@@ -262,7 +262,7 @@ StatementMatcher makeIteratorLoopMatcher(bool IsReverse) {
/// EndVarName: 'j' (as a VarDecl)
/// In the second example only:
/// EndCallName: 'container.size()' (as a CXXMemberCallExpr) or
-/// 'size(contaner)' (as a CallExpr)
+/// 'size(container)' (as a CallExpr)
///
/// Client code will need to make sure that:
/// - The containers on which 'size()' is called is the container indexed.
@@ -491,7 +491,7 @@ static bool isDirectMemberExpr(const Expr *E) {
}
/// Given an expression that represents an usage of an element from the
-/// containter that we are iterating over, returns false when it can be
+/// container that we are iterating over, returns false when it can be
/// guaranteed this element cannot be modified as a result of this usage.
static bool canBeModified(ASTContext *Context, const Expr *E) {
if (E->getType().isConstQualified())
@@ -922,7 +922,7 @@ bool LoopConvertCheck::isConvertible(ASTContext *Context,
const ast_matchers::BoundNodes &Nodes,
const ForStmt *Loop,
LoopFixerKind FixerKind) {
- // In self contained diagnosics mode we don't want dependancies on other
+ // In self contained diagnostic mode we don't want dependencies on other
// loops, otherwise, If we already modified the range of this for loop, don't
// do any further updates on this iteration.
if (areDiagsSelfContained())
clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
@@ -777,7 +777,7 @@ bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE,
const LambdaCapture *C,
Expr *Init) {
if (C->capturesVariable()) {
- const ValueDecl *VDecl = C->getCapturedVar();
+ ValueDecl *VDecl = C->getCapturedVar();
if (areSameVariable(IndexVar, VDecl)) {
// FIXME: if the index is captured, it will count as an usage and the
// alias (if any) won't work, because it is only used in case of having
@@ -787,6 +787,8 @@ bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE,
: Usage::UK_CaptureByRef,
C->getLocation()));
}
+ if (VDecl->isInitCapture())
+ TraverseStmtImpl(cast<VarDecl>(VDecl)->getInit());
}
return VisitorBase::TraverseLambdaCapture(LE, C, Init);
}
@@ -816,6 +818,17 @@ bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *S) {
return true;
}
+bool ForLoopIndexUseVisitor::TraverseStmtImpl(Stmt *S) {
+ // All this pointer swapping is a mechanism for tracking immediate parentage
+ // of Stmts.
+ const Stmt *OldNextParent = NextStmtParent;
+ CurrStmtParent = NextStmtParent;
+ NextStmtParent = S;
+ bool Result = VisitorBase::TraverseStmt(S);
+ NextStmtParent = OldNextParent;
+ return Result;
+}
+
bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) {
// If this is an initialization expression for a lambda capture, prune the
// traversal so that we don't end up diagnosing the contained DeclRefExpr as
@@ -828,15 +841,7 @@ bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) {
return true;
}
}
-
- // All this pointer swapping is a mechanism for tracking immediate parentage
- // of Stmts.
- const Stmt *OldNextParent = NextStmtParent;
- CurrStmtParent = NextStmtParent;
- NextStmtParent = S;
- bool Result = VisitorBase::TraverseStmt(S);
- NextStmtParent = OldNextParent;
- return Result;
+ return TraverseStmtImpl(S);
}
std::string VariableNamer::createIndexName() {
clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h
@@ -354,6 +354,8 @@ private:
bool VisitDeclStmt(DeclStmt *S);
bool TraverseStmt(Stmt *S);
+ bool TraverseStmtImpl(Stmt *S);
+
/// Add an expression to the list of expressions on which the container
/// expression depends.
void addComponent(const Expr *E);
clang-tools-extra/clang-tidy/modernize/MinMaxUseInitializerListCheck.cpp
@@ -72,7 +72,11 @@ static FindArgsResult findArgs(const CallExpr *Call) {
return Result;
}
-static SmallVector<FixItHint>
+// Returns `true` as `first` only if a nested call to `std::min` or
+// `std::max` was found. Checking if `FixItHint`s were generated is not enough,
+// as the explicit casts that the check introduces may be generated without a
+// nested `std::min` or `std::max` call.
+static std::pair<bool, SmallVector<FixItHint>>
generateReplacements(const MatchFinder::MatchResult &Match,
const CallExpr *TopCall, const FindArgsResult &Result,
const bool IgnoreNonTrivialTypes,
@@ -91,13 +95,15 @@ generateReplacements(const MatchFinder::MatchResult &Match,
const bool IsResultTypeTrivial = ResultType.isTrivialType(*Match.Context);
if ((!IsResultTypeTrivial && IgnoreNonTrivialTypes))
- return FixItHints;
+ return {false, FixItHints};
if (IsResultTypeTrivial &&
static_cast<std::uint64_t>(
Match.Context->getTypeSizeInChars(ResultType).getQuantity()) >
IgnoreTrivialTypesOfSizeAbove)
- return FixItHints;
+ return {false, FixItHints};
+
+ bool FoundNestedCall = false;
for (const Expr *Arg : Result.Args) {
const auto *InnerCall = dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts());
@@ -146,6 +152,9 @@ generateReplacements(const MatchFinder::MatchResult &Match,
*Match.Context))
continue;
+ // We have found a nested call
+ FoundNestedCall = true;
+
// remove the function call
FixItHints.push_back(
FixItHint::CreateRemoval(InnerCall->getCallee()->getSourceRange()));
@@ -168,7 +177,7 @@ generateReplacements(const MatchFinder::MatchResult &Match,
CharSourceRange::getTokenRange(InnerResult.First->getEndLoc())));
}
- const SmallVector<FixItHint> InnerReplacements = generateReplacements(
+ const auto [_, InnerReplacements] = generateReplacements(
Match, InnerCall, InnerResult, IgnoreNonTrivialTypes,
IgnoreTrivialTypesOfSizeAbove);
@@ -189,7 +198,7 @@ generateReplacements(const MatchFinder::MatchResult &Match,
}
}
- return FixItHints;
+ return {FoundNestedCall, FixItHints};
}
MinMaxUseInitializerListCheck::MinMaxUseInitializerListCheck(
@@ -238,11 +247,11 @@ void MinMaxUseInitializerListCheck::check(
const auto *TopCall = Match.Nodes.getNodeAs<CallExpr>("topCall");
const FindArgsResult Result = findArgs(TopCall);
- const SmallVector<FixItHint> Replacements =
+ const auto [FoundNestedCall, Replacements] =
generateReplacements(Match, TopCall, Result, IgnoreNonTrivialTypes,
IgnoreTrivialTypesOfSizeAbove);
- if (Replacements.empty())
+ if (!FoundNestedCall)
return;
const DiagnosticBuilder Diagnostic =
clang-tools-extra/clang-tidy/modernize/UseDesignatedInitializersCheck.cpp
@@ -80,9 +80,13 @@ unsigned getNumberOfDesignated(const InitListExpr *SyntacticInitList) {
});
}
-AST_MATCHER(CXXRecordDecl, isAggregate) { return Node.isAggregate(); }
+AST_MATCHER(CXXRecordDecl, isAggregate) {
+ return Node.hasDefinition() && Node.isAggregate();
+}
-AST_MATCHER(CXXRecordDecl, isPOD) { return Node.isPOD(); }
+AST_MATCHER(CXXRecordDecl, isPOD) {
+ return Node.hasDefinition() && Node.isPOD();
+}
AST_MATCHER(InitListExpr, isFullyDesignated) {
if (const InitListExpr *SyntacticForm =
clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp
@@ -35,10 +35,20 @@ AST_MATCHER(Type, sugaredNullptrType) {
/// to null within.
/// Finding sequences of explicit casts is necessary so that an entire sequence
/// can be replaced instead of just the inner-most implicit cast.
+///
+/// TODO/NOTE: The second "anyOf" below discards matches on a substituted type,
+/// since we don't know if that would _always_ be a pointer type for all other
+/// specializations, unless the expression was "__null", in which case we assume
+/// that all specializations are expected to be for pointer types. Ideally this
+/// would check for the "NULL" macro instead, but that'd be harder to express.
+/// In practice, "NULL" is often defined as "__null", and this is a useful
+/// condition.
StatementMatcher makeCastSequenceMatcher(llvm::ArrayRef<StringRef> NameList) {
auto ImplicitCastToNull = implicitCastExpr(
anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
- unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
+ anyOf(hasSourceExpression(gnuNullExpr()),
+ unless(hasImplicitDestinationType(
+ qualType(substTemplateTypeParmType())))),
unless(hasSourceExpression(hasType(sugaredNullptrType()))),
unless(hasImplicitDestinationType(
qualType(matchers::matchesAnyListedTypeName(NameList)))));
clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp
@@ -8,7 +8,9 @@
#include "UseStartsEndsWithCheck.h"
-#include "../utils/OptionsUtils.h"
+#include "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
#include <string>
@@ -16,6 +18,63 @@
using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
+struct NotLengthExprForStringNode {
+ NotLengthExprForStringNode(std::string ID, DynTypedNode Node,
+ ASTContext *Context)
+ : ID(std::move(ID)), Node(std::move(Node)), Context(Context) {}
+ bool operator()(const internal::BoundNodesMap &Nodes) const {
+ // Match a string literal and an integer size or strlen() call.
+ if (const auto *StringLiteralNode = Nodes.getNodeAs<StringLiteral>(ID)) {
+ if (const auto *IntegerLiteralSizeNode = Node.get<IntegerLiteral>()) {
+ return StringLiteralNode->getLength() !=
+ IntegerLiteralSizeNode->getValue().getZExtValue();
+ }
+
+ if (const auto *StrlenNode = Node.get<CallExpr>()) {
+ if (StrlenNode->getDirectCallee()->getName() != "strlen" ||
+ StrlenNode->getNumArgs() != 1) {
+ return true;
+ }
+
+ if (const auto *StrlenArgNode = dyn_cast<StringLiteral>(
+ StrlenNode->getArg(0)->IgnoreParenImpCasts())) {
+ return StrlenArgNode->getLength() != StringLiteralNode->getLength();
+ }
+ }
+ }
+
+ // Match a string variable and a call to length() or size().
+ if (const auto *ExprNode = Nodes.getNodeAs<Expr>(ID)) {
+ if (const auto *MemberCallNode = Node.get<CXXMemberCallExpr>()) {
+ const CXXMethodDecl *MethodDeclNode = MemberCallNode->getMethodDecl();
+ const StringRef Name = MethodDeclNode->getName();
+ if (!MethodDeclNode->isConst() || MethodDeclNode->getNumParams() != 0 ||
+ (Name != "size" && Name != "length")) {
+ return true;
+ }
+
+ if (const auto *OnNode =
+ dyn_cast<Expr>(MemberCallNode->getImplicitObjectArgument())) {
+ return !utils::areStatementsIdentical(OnNode->IgnoreParenImpCasts(),
+ ExprNode->IgnoreParenImpCasts(),
+ *Context);
+ }
+ }
+ }
+
+ return true;
+ }
+
+private:
+ std::string ID;
+ DynTypedNode Node;
+ ASTContext *Context;
+};
+
+AST_MATCHER_P(Expr, lengthExprForStringNode, std::string, ID) {
+ return Builder->removeBindings(NotLengthExprForStringNode(
+ ID, DynTypedNode::create(Node), &(Finder->getASTContext())));
+}
UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name,
ClangTidyContext *Context)
@@ -23,140 +82,120 @@ UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name,
void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
const auto ZeroLiteral = integerLiteral(equals(0));
- const auto HasStartsWithMethodWithName = [](const std::string &Name) {
- return hasMethod(
- cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1))
- .bind("starts_with_fun"));
+
+ const auto ClassTypeWithMethod = [](const StringRef MethodBoundName,
+ const auto... Methods) {
+ return cxxRecordDecl(anyOf(
+ hasMethod(cxxMethodDecl(isConst(), parameterCountIs(1),
+ returns(booleanType()), hasAnyName(Methods))
+ .bind(MethodBoundName))...));
};
- const auto HasStartsWithMethod =
- anyOf(HasStartsWithMethodWithName("starts_with"),
- HasStartsWithMethodWithName("startsWith"),
- HasStartsWithMethodWithName("startswith"));
- const auto ClassWithStartsWithFunction = cxxRecordDecl(anyOf(
- HasStartsWithMethod, hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
- cxxRecordDecl(HasStartsWithMethod)))))));
+ const auto OnClassWithStartsWithFunction =
+ ClassTypeWithMethod("starts_with_fun", "starts_with", "startsWith",
+ "startswith", "StartsWith");
+
+ const auto OnClassWithEndsWithFunction = ClassTypeWithMethod(
+ "ends_with_fun", "ends_with", "endsWith", "endswith", "EndsWith");
+
+ // Case 1: X.find(Y) [!=]= 0 -> starts_with.
const auto FindExpr = cxxMemberCallExpr(
- // A method call with no second argument or the second argument is zero...
anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)),
- // ... named find...
- callee(cxxMethodDecl(hasName("find")).bind("find_fun")),
- // ... on a class with a starts_with function.
- on(hasType(
- hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
- // Bind search expression.
- hasArgument(0, expr().bind("search_expr")));
+ callee(
+ cxxMethodDecl(hasName("find"), ofClass(OnClassWithStartsWithFunction))
+ .bind("find_fun")),
+ hasArgument(0, expr().bind("needle")));
+ // Case 2: X.rfind(Y, 0) [!=]= 0 -> starts_with.
const auto RFindExpr = cxxMemberCallExpr(
- // A method call with a second argument of zero...
hasArgument(1, ZeroLiteral),
- // ... named rfind...
- callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")),
- // ... on a class with a starts_with function.
- on(hasType(
- hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
- // Bind search expression.
- hasArgument(0, expr().bind("search_expr")));
-
- // Match a string literal and an integer or strlen() call matching the length.
- const auto HasStringLiteralAndLengthArgs = [](const auto StringArgIndex,
- const auto LengthArgIndex) {
- return allOf(
- hasArgument(StringArgIndex, stringLiteral().bind("string_literal_arg")),
- hasArgument(LengthArgIndex,
- anyOf(integerLiteral().bind("integer_literal_size_arg"),
- callExpr(callee(functionDecl(parameterCountIs(1),
- hasName("strlen"))),
- hasArgument(0, stringLiteral().bind(
- "strlen_arg"))))));
- };
-
- // Match a string variable and a call to length() or size().
- const auto HasStringVariableAndSizeCallArgs = [](const auto StringArgIndex,
- const auto LengthArgIndex) {
- return allOf(
- hasArgument(StringArgIndex, declRefExpr(hasDeclaration(
- decl().bind("string_var_decl")))),
- hasArgument(LengthArgIndex,
- cxxMemberCallExpr(
- callee(cxxMethodDecl(isConst(), parameterCountIs(0),
- hasAnyName("size", "length"))),
- on(declRefExpr(
- to(decl(equalsBoundNode("string_var_decl"))))))));
- };
-
- // Match either one of the two cases above.
- const auto HasStringAndLengthArgs =
- [HasStringLiteralAndLengthArgs, HasStringVariableAndSizeCallArgs](
- const auto StringArgIndex, const auto LengthArgIndex) {
- return anyOf(
- HasStringLiteralAndLengthArgs(StringArgIndex, LengthArgIndex),
- HasStringVariableAndSizeCallArgs(StringArgIndex, LengthArgIndex));
- };
+ callee(cxxMethodDecl(hasName("rfind"),
+ ofClass(OnClassWithStartsWithFunction))
+ .bind("find_fun")),
+ hasArgument(0, expr().bind("needle")));
+ // Case 3: X.compare(0, LEN(Y), Y) [!=]= 0 -> starts_with.
const auto CompareExpr = cxxMemberCallExpr(
- // A method call with three arguments...
+ argumentCountIs(3), hasArgument(0, ZeroLiteral),
+ callee(cxxMethodDecl(hasName("compare"),
+ ofClass(OnClassWithStartsWithFunction))
+ .bind("find_fun")),
+ hasArgument(2, expr().bind("needle")),
+ hasArgument(1, lengthExprForStringNode("needle")));
+
+ // Case 4: X.compare(LEN(X) - LEN(Y), LEN(Y), Y) [!=]= 0 -> ends_with.
+ const auto CompareEndsWithExpr = cxxMemberCallExpr(
argumentCountIs(3),
- // ... where the first argument is zero...
- hasArgument(0, ZeroLiteral),
- // ... named compare...
- callee(cxxMethodDecl(hasName("compare")).bind("find_fun")),
- // ... on a class with a starts_with function...
- on(hasType(
- hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
- // ... where the third argument is some string and the second a length.
- HasStringAndLengthArgs(2, 1),
- // Bind search expression.
- hasArgument(2, expr().bind("search_expr")));
-
+ callee(cxxMethodDecl(hasName("compare"),
+ ofClass(OnClassWithEndsWithFunction))
+ .bind("find_fun")),
+ on(expr().bind("haystack")), hasArgument(2, expr().bind("needle")),
+ hasArgument(1, lengthExprForStringNode("needle")),
+ hasArgument(0,
+ binaryOperator(hasOperatorName("-"),
+ hasLHS(lengthExprForStringNode("haystack")),
+ hasRHS(lengthExprForStringNode("needle")))));
+
+ // All cases comparing to 0.
Finder->addMatcher(
- // Match [=!]= with a zero on one side and (r?)find|compare on the other.
binaryOperator(
- hasAnyOperatorName("==", "!="),
- hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr))
+ matchers::isEqualityOperator(),
+ hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
+ CompareEndsWithExpr))
.bind("find_expr"),
ZeroLiteral))
.bind("expr"),
this);
+
+ // Case 5: X.rfind(Y) [!=]= LEN(X) - LEN(Y) -> ends_with.
+ Finder->addMatcher(
+ binaryOperator(
+ matchers::isEqualityOperator(),
+ hasOperands(
+ cxxMemberCallExpr(
+ anyOf(
+ argumentCountIs(1),
+ allOf(argumentCountIs(2),
+ hasArgument(
+ 1,
+ anyOf(declRefExpr(to(varDecl(hasName("npos")))),
+ memberExpr(member(hasName("npos"))))))),
+ callee(cxxMethodDecl(hasName("rfind"),
+ ofClass(OnClassWithEndsWithFunction))
+ .bind("find_fun")),
+ on(expr().bind("haystack")),
+ hasArgument(0, expr().bind("needle")))
+ .bind("find_expr"),
+ binaryOperator(hasOperatorName("-"),
+ hasLHS(lengthExprForStringNode("haystack")),
+ hasRHS(lengthExprForStringNode("needle")))))
+ .bind("expr"),
+ this);
}
void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>("find_expr");
const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("find_fun");
- const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("search_expr");
+ const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>("needle");
const auto *StartsWithFunction =
Result.Nodes.getNodeAs<CXXMethodDecl>("starts_with_fun");
+ const auto *EndsWithFunction =
+ Result.Nodes.getNodeAs<CXXMethodDecl>("ends_with_fun");
+ assert(bool(StartsWithFunction) != bool(EndsWithFunction));
+ const CXXMethodDecl *ReplacementFunction =
+ StartsWithFunction ? StartsWithFunction : EndsWithFunction;
- const auto *StringLiteralArg =
- Result.Nodes.getNodeAs<StringLiteral>("string_literal_arg");
- const auto *IntegerLiteralSizeArg =
- Result.Nodes.getNodeAs<IntegerLiteral>("integer_literal_size_arg");
- const auto *StrlenArg = Result.Nodes.getNodeAs<StringLiteral>("strlen_arg");
-
- // Filter out compare cases where the length does not match string literal.
- if (StringLiteralArg && IntegerLiteralSizeArg &&
- StringLiteralArg->getLength() !=
- IntegerLiteralSizeArg->getValue().getZExtValue()) {
+ if (ComparisonExpr->getBeginLoc().isMacroID())
return;
- }
-
- if (StringLiteralArg && StrlenArg &&
- StringLiteralArg->getLength() != StrlenArg->getLength()) {
- return;
- }
-
- if (ComparisonExpr->getBeginLoc().isMacroID()) {
- return;
- }
const bool Neg = ComparisonExpr->getOpcode() == BO_NE;
auto Diagnostic =
diag(FindExpr->getExprLoc(), "use %0 instead of %1() %select{==|!=}2 0")
- << StartsWithFunction->getName() << FindFun->getName() << Neg;
+ << ReplacementFunction->getName() << FindFun->getName() << Neg;
- // Remove possible arguments after search expression and ' [!=]= 0' suffix.
+ // Remove possible arguments after search expression and ' [!=]= .+' suffix.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
@@ -164,21 +203,20 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
ComparisonExpr->getEndLoc()),
")");
- // Remove possible '0 [!=]= ' prefix.
+ // Remove possible '.+ [!=]= ' prefix.
Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));
- // Replace method name by 'starts_with'.
+ // Replace method name by '(starts|ends)_with'.
// Remove possible arguments before search expression.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(FindExpr->getExprLoc(),
SearchExpr->getBeginLoc()),
- (StartsWithFunction->getName() + "(").str());
+ (ReplacementFunction->getName() + "(").str());
// Add possible negation '!'.
- if (Neg) {
+ if (Neg)
Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(), "!");
- }
}
} // namespace clang::tidy::modernize
clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.h
@@ -14,7 +14,7 @@
namespace clang::tidy::modernize {
/// Checks for common roundabout ways to express ``starts_with`` and
-/// ``ends_with`` and suggests replacing with ``starts_with`` when the method is
+/// ``ends_with`` and suggests replacing with the simpler method when it is
/// available. Notably, this will work with ``std::string`` and
/// ``std::string_view``.
///
clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp
@@ -44,6 +44,7 @@ void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
+ this->PP = PP;
}
void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {
@@ -75,9 +76,9 @@ void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) {
utils::FormatStringConverter::Configuration ConverterConfig;
ConverterConfig.StrictMode = StrictMode;
- utils::FormatStringConverter Converter(Result.Context, StrFormat,
- FormatArgOffset, ConverterConfig,
- getLangOpts());
+ utils::FormatStringConverter Converter(
+ Result.Context, StrFormat, FormatArgOffset, ConverterConfig,
+ getLangOpts(), *Result.SourceManager, *PP);
const Expr *StrFormatCall = StrFormat->getCallee();
if (!Converter.canApply()) {
diag(StrFormat->getBeginLoc(),
clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h
@@ -44,6 +44,7 @@ private:
StringRef ReplacementFormatFunction;
utils::IncludeInserter IncludeInserter;
std::optional<StringRef> MaybeHeaderToInclude;
+ Preprocessor *PP = nullptr;
};
} // namespace clang::tidy::modernize
clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
@@ -68,6 +68,7 @@ void UseStdPrintCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
+ this->PP = PP;
}
static clang::ast_matchers::StatementMatcher
@@ -131,7 +132,8 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {
ConverterConfig.StrictMode = StrictMode;
ConverterConfig.AllowTrailingNewlineRemoval = true;
utils::FormatStringConverter Converter(
- Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts());
+ Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts(),
+ *Result.SourceManager, *PP);
const Expr *PrintfCall = Printf->getCallee();
const StringRef ReplacementFunction = Converter.usePrintNewlineFunction()
? ReplacementPrintlnFunction
clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.h
@@ -36,6 +36,7 @@ public:
}
private:
+ Preprocessor *PP;
bool StrictMode;
std::vector<StringRef> PrintfLikeFunctions;
std::vector<StringRef> FprintfLikeFunctions;
clang-tools-extra/clang-tidy/mpi/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyMPIModule
+add_clang_library(clangTidyMPIModule STATIC
BufferDerefCheck.cpp
MPITidyModule.cpp
TypeMismatchCheck.cpp
clang-tools-extra/clang-tidy/objc/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyObjCModule
+add_clang_library(clangTidyObjCModule STATIC
AssertEquals.cpp
AvoidNSErrorInitCheck.cpp
DeallocInCategoryCheck.cpp
clang-tools-extra/clang-tidy/openmp/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyOpenMPModule
+add_clang_library(clangTidyOpenMPModule STATIC
ExceptionEscapeCheck.cpp
OpenMPTidyModule.cpp
UseDefaultNoneCheck.cpp
clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.cpp
@@ -46,38 +46,41 @@ void AvoidEndlCheck::check(const MatchFinder::MatchResult &Result) {
// Handle the more common streaming '... << std::endl' case
const CharSourceRange TokenRange =
CharSourceRange::getTokenRange(Expression->getSourceRange());
- const StringRef SourceText = Lexer::getSourceText(
+ StringRef SourceText = Lexer::getSourceText(
TokenRange, *Result.SourceManager, Result.Context->getLangOpts());
-
+ if (SourceText.empty())
+ SourceText = "std::endl";
auto Diag = diag(Expression->getBeginLoc(),
"do not use '%0' with streams; use '\\n' instead")
<< SourceText;
-
- Diag << FixItHint::CreateReplacement(TokenRange, "'\\n'");
+ if (TokenRange.isValid())
+ Diag << FixItHint::CreateReplacement(TokenRange, "'\\n'");
} else {
// Handle the less common function call 'std::endl(...)' case
const auto *CallExpression = llvm::cast<CallExpr>(Expression);
assert(CallExpression->getNumArgs() == 1);
- const StringRef SourceText = Lexer::getSourceText(
+ StringRef SourceText = Lexer::getSourceText(
CharSourceRange::getTokenRange(
CallExpression->getCallee()->getSourceRange()),
*Result.SourceManager, Result.Context->getLangOpts());
+ if (SourceText.empty())
+ SourceText = "std::endl";
+ auto Diag = diag(CallExpression->getBeginLoc(),
+ "do not use '%0' with streams; use '\\n' instead")
+ << SourceText;
const CharSourceRange ArgTokenRange = CharSourceRange::getTokenRange(
CallExpression->getArg(0)->getSourceRange());
const StringRef ArgSourceText = Lexer::getSourceText(
ArgTokenRange, *Result.SourceManager, Result.Context->getLangOpts());
-
- const std::string ReplacementString =
- std::string(ArgSourceText) + " << '\\n'";
-
- diag(CallExpression->getBeginLoc(),
- "do not use '%0' with streams; use '\\n' instead")
- << SourceText
- << FixItHint::CreateReplacement(
- CharSourceRange::getTokenRange(CallExpression->getSourceRange()),
- ReplacementString);
+ const CharSourceRange ReplacementRange =
+ CharSourceRange::getTokenRange(CallExpression->getSourceRange());
+ if (!ArgSourceText.empty() && ReplacementRange.isValid()) {
+ const std::string ReplacementString =
+ std::string(ArgSourceText) + " << '\\n'";
+ Diag << FixItHint::CreateReplacement(ReplacementRange, ReplacementString);
+ }
}
}
clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyPerformanceModule
+add_clang_library(clangTidyPerformanceModule STATIC
AvoidEndlCheck.cpp
EnumSizeCheck.cpp
FasterStringFindCheck.cpp
clang-tools-extra/clang-tidy/performance/ForRangeCopyCheck.cpp
@@ -91,7 +91,7 @@ bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
<< utils::fixit::changeVarDeclToReference(LoopVar, Context);
if (!LoopVar.getType().isConstQualified()) {
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
- LoopVar, Context, DeclSpec::TQ::TQ_const))
+ LoopVar, Context, Qualifiers::Const))
Diagnostic << *Fix;
}
return true;
@@ -129,7 +129,7 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
"making it a const reference");
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
- LoopVar, Context, DeclSpec::TQ::TQ_const))
+ LoopVar, Context, Qualifiers::Const))
Diag << *Fix << utils::fixit::changeVarDeclToReference(LoopVar, Context);
return true;
clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp
@@ -209,8 +209,9 @@ void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
}
if (const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl();
- RecordDecl && !(RecordDecl->hasMoveConstructor() &&
- RecordDecl->hasMoveAssignment())) {
+ RecordDecl && RecordDecl->hasDefinition() &&
+ !(RecordDecl->hasMoveConstructor() &&
+ RecordDecl->hasMoveAssignment())) {
const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment();
const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor();
const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor;
clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
@@ -36,7 +36,7 @@ void recordFixes(const VarDecl &Var, ASTContext &Context,
Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
if (!Var.getType().isLocalConstQualified()) {
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
- Var, Context, DeclSpec::TQ::TQ_const))
+ Var, Context, Qualifiers::Const))
Diagnostic << *Fix;
}
}
clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -172,7 +172,7 @@ void UnnecessaryValueParamCheck::handleConstRefFix(const FunctionDecl &Function,
// declaration.
if (!CurrentParam.getType().getCanonicalType().isConstQualified()) {
if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
- CurrentParam, Context, DeclSpec::TQ::TQ_const))
+ CurrentParam, Context, Qualifiers::Const))
Diag << *Fix;
}
}
clang-tools-extra/clang-tidy/plugin/CMakeLists.txt
@@ -1,4 +1,4 @@
-add_clang_library(clangTidyPlugin
+add_clang_library(clangTidyPlugin STATIC
ClangTidyPlugin.cpp
LINK_LIBS
clang-tools-extra/clang-tidy/portability/CMakeLists.txt
@@ -4,11 +4,12 @@ set(LLVM_LINK_COMPONENTS
TargetParser
)
-add_clang_library(clangTidyPortabilityModule
+add_clang_library(clangTidyPortabilityModule STATIC
PortabilityTidyModule.cpp
RestrictSystemIncludesCheck.cpp
SIMDIntrinsicsCheck.cpp
StdAllocatorConstCheck.cpp
+ TemplateVirtualMemberFunctionCheck.cpp
LINK_LIBS
clangTidy
clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
@@ -12,6 +12,7 @@
#include "RestrictSystemIncludesCheck.h"
#include "SIMDIntrinsicsCheck.h"
#include "StdAllocatorConstCheck.h"
+#include "TemplateVirtualMemberFunctionCheck.h"
namespace clang::tidy {
namespace portability {
@@ -25,6 +26,8 @@ public:
"portability-simd-intrinsics");
CheckFactories.registerCheck<StdAllocatorConstCheck>(
"portability-std-allocator-const");
+ CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
+ "portability-template-virtual-member-function");
}
};
clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp
@@ -0,0 +1,44 @@
+//===--- TemplateVirtualMemberFunctionCheck.cpp - clang-tidy --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TemplateVirtualMemberFunctionCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::portability {
+namespace {
+AST_MATCHER(CXXMethodDecl, isUsed) { return Node.isUsed(); }
+} // namespace
+
+void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
+ unless(isExplicitTemplateSpecialization()))
+ .bind("specialization")),
+ isVirtual(), unless(isUsed()),
+ unless(cxxDestructorDecl(isDefaulted())))
+ .bind("method"),
+ this);
+}
+
+void TemplateVirtualMemberFunctionCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *ImplicitSpecialization =
+ Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
+ const auto *MethodDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+
+ diag(MethodDecl->getLocation(),
+ "unspecified virtual member function instantiation; the virtual "
+ "member function is not instantiated but it might be with a "
+ "different compiler");
+ diag(ImplicitSpecialization->getPointOfInstantiation(),
+ "template instantiated here", DiagnosticIDs::Note);
+}
+
+} // namespace clang::tidy::portability
clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h
@@ -0,0 +1,38 @@
+//===--- TemplateVirtualMemberFunctionCheck.h - clang-tidy ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::portability {
+
+/// Upon instantiating a template class, non-virtual member functions don't have
+/// to be instantiated unless they are used. Virtual member function
+/// instantiation on the other hand is unspecified and depends on the
+/// implementation of the compiler. This check intends to find cases when a
+/// virtual member function is not instantiated but it might be with a different
+/// compiler.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
+class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
+public:
+ TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+};
+
+} // namespace clang::tidy::portability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
clang-tools-extra/clang-tidy/readability/AvoidUnconditionalPreprocessorIfCheck.cpp
@@ -84,7 +84,8 @@ struct AvoidUnconditionalPreprocessorIfPPCallbacks : public PPCallbacks {
return (Tok.getRawIdentifier() == "true" ||
Tok.getRawIdentifier() == "false");
default:
- return Tok.getKind() >= tok::l_square && Tok.getKind() <= tok::caretcaret;
+ return Tok.getKind() >= tok::l_square &&
+ Tok.getKind() <= tok::greatergreatergreater;
}
}
clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyReadabilityModule
+add_clang_library(clangTidyReadabilityModule STATIC
AvoidConstParamsInDecls.cpp
AvoidNestedConditionalOperatorCheck.cpp
AvoidReturnWithVoidValueCheck.cpp
clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
@@ -13,30 +13,40 @@
using namespace clang::ast_matchers;
namespace clang::tidy::readability {
-
void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
- const auto SupportedContainers = hasType(
- hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
- hasAnyName("::std::set", "::std::unordered_set", "::std::map",
- "::std::unordered_map", "::std::multiset",
- "::std::unordered_multiset", "::std::multimap",
- "::std::unordered_multimap"))))));
+ const auto HasContainsMatchingParamType = hasMethod(
+ cxxMethodDecl(isConst(), parameterCountIs(1), returns(booleanType()),
+ hasName("contains"), unless(isDeleted()), isPublic(),
+ hasParameter(0, hasType(hasUnqualifiedDesugaredType(
+ equalsBoundNode("parameterType"))))));
const auto CountCall =
- cxxMemberCallExpr(on(SupportedContainers),
- callee(cxxMethodDecl(hasName("count"))),
- argumentCountIs(1))
+ cxxMemberCallExpr(
+ argumentCountIs(1),
+ callee(cxxMethodDecl(
+ hasName("count"),
+ hasParameter(0, hasType(hasUnqualifiedDesugaredType(
+ type().bind("parameterType")))),
+ ofClass(cxxRecordDecl(HasContainsMatchingParamType)))))
.bind("call");
const auto FindCall =
- cxxMemberCallExpr(on(SupportedContainers),
- callee(cxxMethodDecl(hasName("find"))),
- argumentCountIs(1))
+ cxxMemberCallExpr(
+ argumentCountIs(1),
+ callee(cxxMethodDecl(
+ hasName("find"),
+ hasParameter(0, hasType(hasUnqualifiedDesugaredType(
+ type().bind("parameterType")))),
+ ofClass(cxxRecordDecl(HasContainsMatchingParamType)))))
.bind("call");
- const auto EndCall = cxxMemberCallExpr(on(SupportedContainers),
- callee(cxxMethodDecl(hasName("end"))),
- argumentCountIs(0));
+ const auto EndCall = cxxMemberCallExpr(
+ argumentCountIs(0),
+ callee(
+ cxxMethodDecl(hasName("end"),
+ // In the matchers below, FindCall should always appear
+ // before EndCall so 'parameterType' is properly bound.
+ ofClass(cxxRecordDecl(HasContainsMatchingParamType)))));
const auto Literal0 = integerLiteral(equals(0));
const auto Literal1 = integerLiteral(equals(1));
@@ -52,50 +62,44 @@ void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
.bind("positiveComparison"),
this);
AddSimpleMatcher(
- binaryOperator(hasLHS(CountCall), hasOperatorName("!="), hasRHS(Literal0))
- .bind("positiveComparison"));
- AddSimpleMatcher(
- binaryOperator(hasLHS(Literal0), hasOperatorName("!="), hasRHS(CountCall))
- .bind("positiveComparison"));
- AddSimpleMatcher(
- binaryOperator(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0))
- .bind("positiveComparison"));
- AddSimpleMatcher(
- binaryOperator(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall))
+ binaryOperation(hasOperatorName("!="), hasOperands(CountCall, Literal0))
.bind("positiveComparison"));
AddSimpleMatcher(
- binaryOperator(hasLHS(CountCall), hasOperatorName(">="), hasRHS(Literal1))
+ binaryOperation(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0))
.bind("positiveComparison"));
AddSimpleMatcher(
- binaryOperator(hasLHS(Literal1), hasOperatorName("<="), hasRHS(CountCall))
+ binaryOperation(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall))
.bind("positiveComparison"));
+ AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName(">="),
+ hasRHS(Literal1))
+ .bind("positiveComparison"));
+ AddSimpleMatcher(binaryOperation(hasLHS(Literal1), hasOperatorName("<="),
+ hasRHS(CountCall))
+ .bind("positiveComparison"));
// Find inverted membership tests which use `count()`.
AddSimpleMatcher(
- binaryOperator(hasLHS(CountCall), hasOperatorName("=="), hasRHS(Literal0))
- .bind("negativeComparison"));
- AddSimpleMatcher(
- binaryOperator(hasLHS(Literal0), hasOperatorName("=="), hasRHS(CountCall))
- .bind("negativeComparison"));
- AddSimpleMatcher(
- binaryOperator(hasLHS(CountCall), hasOperatorName("<="), hasRHS(Literal0))
- .bind("negativeComparison"));
- AddSimpleMatcher(
- binaryOperator(hasLHS(Literal0), hasOperatorName(">="), hasRHS(CountCall))
+ binaryOperation(hasOperatorName("=="), hasOperands(CountCall, Literal0))
.bind("negativeComparison"));
+ AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName("<="),
+ hasRHS(Literal0))
+ .bind("negativeComparison"));
+ AddSimpleMatcher(binaryOperation(hasLHS(Literal0), hasOperatorName(">="),
+ hasRHS(CountCall))
+ .bind("negativeComparison"));
AddSimpleMatcher(
- binaryOperator(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1))
+ binaryOperation(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1))
.bind("negativeComparison"));
AddSimpleMatcher(
- binaryOperator(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall))
+ binaryOperation(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall))
.bind("negativeComparison"));
// Find membership tests based on `find() == end()`.
AddSimpleMatcher(
- binaryOperator(hasLHS(FindCall), hasOperatorName("!="), hasRHS(EndCall))
+ binaryOperation(hasOperatorName("!="), hasOperands(FindCall, EndCall))
.bind("positiveComparison"));
AddSimpleMatcher(
- binaryOperator(hasLHS(FindCall), hasOperatorName("=="), hasRHS(EndCall))
+ binaryOperation(hasOperatorName("=="), hasOperands(FindCall, EndCall))
.bind("negativeComparison"));
}
clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h
@@ -13,8 +13,9 @@
namespace clang::tidy::readability {
-/// Finds usages of `container.count()` and `find() == end()` which should be
-/// replaced by a call to the `container.contains()` method introduced in C++20.
+/// Finds usages of `container.count()` and
+/// `container.find() == container.end()` which should be replaced by a call
+/// to the `container.contains()` method.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/container-contains.html
@@ -24,10 +25,11 @@ public:
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) final;
void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
-
-protected:
bool isLanguageVersionSupported(const LangOptions &LO) const final {
- return LO.CPlusPlus20;
+ return LO.CPlusPlus;
+ }
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_AsIs;
}
};
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
@@ -123,6 +123,13 @@ AST_MATCHER(EnumDecl, hasSequentialInitialValues) {
return !AllEnumeratorsArePowersOfTwo;
}
+std::string getName(const EnumDecl *Decl) {
+ if (!Decl->getDeclName())
+ return "<unnamed>";
+
+ return Decl->getQualifiedNameAsString();
+}
+
} // namespace
EnumInitialValueCheck::EnumInitialValueCheck(StringRef Name,
@@ -141,16 +148,18 @@ void EnumInitialValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}
void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(
- enumDecl(unless(isMacro()), unless(hasConsistentInitialValues()))
- .bind("inconsistent"),
- this);
+ Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
+ unless(hasConsistentInitialValues()))
+ .bind("inconsistent"),
+ this);
if (!AllowExplicitZeroFirstInitialValue)
Finder->addMatcher(
- enumDecl(hasZeroInitialValueForFirstEnumerator()).bind("zero_first"),
+ enumDecl(isDefinition(), hasZeroInitialValueForFirstEnumerator())
+ .bind("zero_first"),
this);
if (!AllowExplicitSequentialInitialValues)
- Finder->addMatcher(enumDecl(unless(isMacro()), hasSequentialInitialValues())
+ Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
+ hasSequentialInitialValues())
.bind("sequential"),
this);
}
@@ -158,10 +167,11 @@ void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) {
void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("inconsistent")) {
DiagnosticBuilder Diag =
- diag(Enum->getBeginLoc(),
- "inital values in enum %0 are not consistent, consider explicit "
- "initialization of all, none or only the first enumerator")
- << Enum;
+ diag(
+ Enum->getBeginLoc(),
+ "initial values in enum '%0' are not consistent, consider explicit "
+ "initialization of all, none or only the first enumerator")
+ << getName(Enum);
for (const EnumConstantDecl *ECD : Enum->enumerators())
if (ECD->getInitExpr() == nullptr) {
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
@@ -181,16 +191,16 @@ void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {
if (Loc.isInvalid() || Loc.isMacroID())
return;
DiagnosticBuilder Diag = diag(Loc, "zero initial value for the first "
- "enumerator in %0 can be disregarded")
- << Enum;
+ "enumerator in '%0' can be disregarded")
+ << getName(Enum);
cleanInitialValue(Diag, ECD, *Result.SourceManager, getLangOpts());
return;
}
if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("sequential")) {
DiagnosticBuilder Diag =
diag(Enum->getBeginLoc(),
- "sequential initial value in %0 can be ignored")
- << Enum;
+ "sequential initial value in '%0' can be ignored")
+ << getName(Enum);
for (const EnumConstantDecl *ECD : llvm::drop_begin(Enum->enumerators()))
cleanInitialValue(Diag, ECD, *Result.SourceManager, getLangOpts());
return;
clang-tools-extra/clang-tidy/readability/FunctionCognitiveComplexityCheck.cpp
@@ -126,7 +126,7 @@ struct CognitiveComplexity final {
// Limit of 25 is the "upstream"'s default.
static constexpr unsigned DefaultLimit = 25U;
- // Based on the publicly-avaliable numbers for some big open-source projects
+ // Based on the publicly-available numbers for some big open-source projects
// https://sonarcloud.io/projects?languages=c%2Ccpp&size=5 we can estimate:
// value ~20 would result in no allocs for 98% of functions, ~12 for 96%, ~10
// for 91%, ~8 for 88%, ~6 for 84%, ~4 for 77%, ~2 for 64%, and ~1 for 37%.
clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
@@ -1135,6 +1135,9 @@ StyleKind IdentifierNamingCheck::findStyleKind(
if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias])
return SK_TypeAlias;
+ if (isa<NamespaceAliasDecl>(D) && NamingStyles[SK_Namespace])
+ return SK_Namespace;
+
if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
if (Decl->isAnonymousNamespace())
return SK_Invalid;
clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
@@ -10,6 +10,7 @@
#include "../utils/FixItHintUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/FixIt.h"
#include <queue>
@@ -26,6 +27,8 @@ AST_MATCHER(Stmt, isMacroExpansion) {
return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
}
+AST_MATCHER(Stmt, isC23) { return Finder->getASTContext().getLangOpts().C23; }
+
bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
SourceManager &SM = Context.getSourceManager();
const LangOptions &LO = Context.getLangOpts();
@@ -298,6 +301,11 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
hasCastKind(CK_FloatingToBoolean),
hasCastKind(CK_PointerToBoolean),
hasCastKind(CK_MemberPointerToBoolean)),
+ // Exclude cases of C23 comparison result.
+ unless(allOf(isC23(),
+ hasSourceExpression(ignoringParens(
+ binaryOperator(hasAnyOperatorName(
+ ">", ">=", "==", "!=", "<", "<=")))))),
// Exclude case of using if or while statements with variable
// declaration, e.g.:
// if (int var = functionCall()) {}
clang-tools-extra/clang-tidy/rename_check.py
@@ -8,16 +8,16 @@
#
# ===-----------------------------------------------------------------------===#
-from __future__ import unicode_literals
-
import argparse
import glob
import io
import os
import re
+import sys
+from typing import List
-def replaceInFileRegex(fileName, sFrom, sTo):
+def replaceInFileRegex(fileName: str, sFrom: str, sTo: str) -> None:
if sFrom == sTo:
return
@@ -35,7 +35,7 @@ def replaceInFileRegex(fileName, sFrom, sTo):
f.write(txt)
-def replaceInFile(fileName, sFrom, sTo):
+def replaceInFile(fileName: str, sFrom: str, sTo: str) -> None:
if sFrom == sTo:
return
txt = None
@@ -51,7 +51,7 @@ def replaceInFile(fileName, sFrom, sTo):
f.write(txt)
-def generateCommentLineHeader(filename):
+def generateCommentLineHeader(filename: str) -> str:
return "".join(
[
"//===--- ",
@@ -63,7 +63,7 @@ def generateCommentLineHeader(filename):
)
-def generateCommentLineSource(filename):
+def generateCommentLineSource(filename: str) -> str:
return "".join(
[
"//===--- ",
@@ -75,7 +75,7 @@ def generateCommentLineSource(filename):
)
-def fileRename(fileName, sFrom, sTo):
+def fileRename(fileName: str, sFrom: str, sTo: str) -> str:
if sFrom not in fileName or sFrom == sTo:
return fileName
newFileName = fileName.replace(sFrom, sTo)
@@ -84,7 +84,7 @@ def fileRename(fileName, sFrom, sTo):
return newFileName
-def deleteMatchingLines(fileName, pattern):
+def deleteMatchingLines(fileName: str, pattern: str) -> bool:
lines = None
with io.open(fileName, "r", encoding="utf8") as f:
lines = f.readlines()
@@ -101,7 +101,7 @@ def deleteMatchingLines(fileName, pattern):
return True
-def getListOfFiles(clang_tidy_path):
+def getListOfFiles(clang_tidy_path: str) -> List[str]:
files = glob.glob(os.path.join(clang_tidy_path, "**"), recursive=True)
files += [
os.path.normpath(os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst"))
@@ -124,7 +124,7 @@ def getListOfFiles(clang_tidy_path):
# Adapts the module's CMakelist file. Returns 'True' if it could add a new
# entry and 'False' if the entry already existed.
-def adapt_cmake(module_path, check_name_camel):
+def adapt_cmake(module_path: str, check_name_camel: str) -> bool:
filename = os.path.join(module_path, "CMakeLists.txt")
with io.open(filename, "r", encoding="utf8") as f:
lines = f.readlines()
@@ -153,7 +153,9 @@ def adapt_cmake(module_path, check_name_camel):
# Modifies the module to include the new check.
-def adapt_module(module_path, module, check_name, check_name_camel):
+def adapt_module(
+ module_path: str, module: str, check_name: str, check_name_camel: str
+) -> None:
modulecpp = next(
iter(
filter(
@@ -204,7 +206,9 @@ def adapt_module(module_path, module, check_name, check_name_camel):
# Adds a release notes entry.
-def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
+def add_release_notes(
+ clang_tidy_path: str, old_check_name: str, new_check_name: str
+) -> None:
filename = os.path.normpath(
os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst")
)
@@ -262,7 +266,7 @@ def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
f.write(line)
-def main():
+def main() -> None:
parser = argparse.ArgumentParser(description="Rename clang-tidy check.")
parser.add_argument("old_check_name", type=str, help="Old check name.")
parser.add_argument("new_check_name", type=str, help="New check name.")
@@ -311,7 +315,7 @@ def main():
"Check name '%s' not found in %s. Exiting."
% (check_name_camel, cmake_lists)
)
- return 1
+ sys.exit(1)
modulecpp = next(
iter(
clang-tools-extra/clang-tidy/tool/CMakeLists.txt
@@ -9,7 +9,7 @@ set(LLVM_LINK_COMPONENTS
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES ClangTidyMain.cpp ClangTidyToolMain.cpp)
-add_clang_library(clangTidyMain
+add_clang_library(clangTidyMain STATIC
ClangTidyMain.cpp
LINK_LIBS
clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
@@ -49,7 +49,7 @@ import tempfile
import time
import traceback
from types import ModuleType
-from typing import Any, Awaitable, Callable, List, Optional, Tuple, TypeVar
+from typing import Any, Awaitable, Callable, List, Optional, TypeVar
yaml: Optional[ModuleType] = None
@@ -621,4 +621,7 @@ async def main() -> None:
if __name__ == "__main__":
- asyncio.run(main())
+ try:
+ asyncio.run(main())
+ except KeyboardInterrupt:
+ pass
clang-tools-extra/clang-tidy/utils/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyUtils
+add_clang_library(clangTidyUtils STATIC
Aliasing.cpp
ASTUtils.cpp
BracesAroundStatement.cpp
clang-tools-extra/clang-tidy/utils/FixItHintUtils.cpp
@@ -11,6 +11,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"
+#include "clang/Sema/DeclSpec.h"
#include "clang/Tooling/FixIt.h"
#include <optional>
@@ -71,15 +72,17 @@ static std::optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,
// Build a string that can be emitted as FixIt with either a space in before
// or after the qualifier, either ' const' or 'const '.
-static std::string buildQualifier(DeclSpec::TQ Qualifier,
+static std::string buildQualifier(Qualifiers::TQ Qualifier,
bool WhitespaceBefore = false) {
if (WhitespaceBefore)
- return (llvm::Twine(' ') + DeclSpec::getSpecifierName(Qualifier)).str();
- return (llvm::Twine(DeclSpec::getSpecifierName(Qualifier)) + " ").str();
+ return (llvm::Twine(' ') + Qualifiers::fromCVRMask(Qualifier).getAsString())
+ .str();
+ return (llvm::Twine(Qualifiers::fromCVRMask(Qualifier).getAsString()) + " ")
+ .str();
}
static std::optional<FixItHint> changeValue(const VarDecl &Var,
- DeclSpec::TQ Qualifier,
+ Qualifiers::TQ Qualifier,
QualifierTarget QualTarget,
QualifierPolicy QualPolicy,
const ASTContext &Context) {
@@ -99,7 +102,7 @@ static std::optional<FixItHint> changeValue(const VarDecl &Var,
}
static std::optional<FixItHint> changePointerItself(const VarDecl &Var,
- DeclSpec::TQ Qualifier,
+ Qualifiers::TQ Qualifier,
const ASTContext &Context) {
if (locDangerous(Var.getLocation()))
return std::nullopt;
@@ -112,7 +115,7 @@ static std::optional<FixItHint> changePointerItself(const VarDecl &Var,
}
static std::optional<FixItHint>
-changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
+changePointer(const VarDecl &Var, Qualifiers::TQ Qualifier, const Type *Pointee,
QualifierTarget QualTarget, QualifierPolicy QualPolicy,
const ASTContext &Context) {
// The pointer itself shall be marked as `const`. This is always to the right
@@ -163,7 +166,7 @@ changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
}
static std::optional<FixItHint>
-changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,
+changeReferencee(const VarDecl &Var, Qualifiers::TQ Qualifier, QualType Pointee,
QualifierTarget QualTarget, QualifierPolicy QualPolicy,
const ASTContext &Context) {
if (QualPolicy == QualifierPolicy::Left && isValueType(Pointee))
@@ -183,7 +186,7 @@ changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,
std::optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,
const ASTContext &Context,
- DeclSpec::TQ Qualifier,
+ Qualifiers::TQ Qualifier,
QualifierTarget QualTarget,
QualifierPolicy QualPolicy) {
assert((QualPolicy == QualifierPolicy::Left ||
clang-tools-extra/clang-tidy/utils/FixItHintUtils.h
@@ -11,7 +11,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
-#include "clang/Sema/DeclSpec.h"
+#include "clang/AST/Type.h"
#include <optional>
namespace clang::tidy::utils::fixit {
@@ -41,7 +41,7 @@ enum class QualifierTarget {
/// Requires that `Var` is isolated in written code like in `int foo = 42;`.
std::optional<FixItHint>
addQualifierToVarDecl(const VarDecl &Var, const ASTContext &Context,
- DeclSpec::TQ Qualifier,
+ Qualifiers::TQ Qualifier,
QualifierTarget QualTarget = QualifierTarget::Pointee,
QualifierPolicy QualPolicy = QualifierPolicy::Left);
clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
@@ -18,6 +18,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/FixIt.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Debug.h"
@@ -195,11 +196,10 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) {
return false;
}
-FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
- const CallExpr *Call,
- unsigned FormatArgOffset,
- const Configuration ConfigIn,
- const LangOptions &LO)
+FormatStringConverter::FormatStringConverter(
+ ASTContext *ContextIn, const CallExpr *Call, unsigned FormatArgOffset,
+ const Configuration ConfigIn, const LangOptions &LO, SourceManager &SM,
+ Preprocessor &PP)
: Context(ContextIn), Config(ConfigIn),
CastMismatchedIntegerTypes(
castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)),
@@ -208,11 +208,22 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
assert(ArgsOffset <= NumArgs);
FormatExpr = llvm::dyn_cast<StringLiteral>(
Args[FormatArgOffset]->IgnoreImplicitAsWritten());
+
if (!FormatExpr || !FormatExpr->isOrdinary()) {
// Function must have a narrow string literal as its first argument.
conversionNotPossible("first argument is not a narrow string literal");
return;
}
+
+ if (const std::optional<StringRef> MaybeMacroName =
+ formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
+ MaybeMacroName) {
+ conversionNotPossible(
+ ("format string contains unreplaceable macro '" + *MaybeMacroName + "'")
+ .str());
+ return;
+ }
+
PrintfFormatString = FormatExpr->getString();
// Assume that the output will be approximately the same size as the input,
@@ -230,6 +241,50 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn,
finalizeFormatText();
}
+std::optional<StringRef>
+FormatStringConverter::formatStringContainsUnreplaceableMacro(
+ const CallExpr *Call, const StringLiteral *FormatExpr, SourceManager &SM,
+ Preprocessor &PP) {
+ // If a macro invocation surrounds the entire call then we don't want that to
+ // inhibit conversion. The whole format string will appear to come from that
+ // macro, as will the function call.
+ std::optional<StringRef> MaybeSurroundingMacroName;
+ if (SourceLocation BeginCallLoc = Call->getBeginLoc();
+ BeginCallLoc.isMacroID())
+ MaybeSurroundingMacroName =
+ Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());
+
+ for (auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();
+ I != E; ++I) {
+ const SourceLocation &TokenLoc = *I;
+ if (TokenLoc.isMacroID()) {
+ const StringRef MacroName =
+ Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());
+
+ if (MaybeSurroundingMacroName != MacroName) {
+ // glibc uses __PRI64_PREFIX and __PRIPTR_PREFIX to define the prefixes
+ // for types that change size so we must look for multiple prefixes.
+ if (!MacroName.starts_with("PRI") && !MacroName.starts_with("__PRI"))
+ return MacroName;
+
+ const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
+ const OptionalFileEntryRef MaybeFileEntry =
+ SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
+ if (!MaybeFileEntry)
+ return MacroName;
+
+ HeaderSearch &HS = PP.getHeaderSearchInfo();
+ // Check if the file is a system header
+ if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
+ llvm::sys::path::filename(MaybeFileEntry->getName()) !=
+ "inttypes.h")
+ return MacroName;
+ }
+ }
+ }
+ return std::nullopt;
+}
+
void FormatStringConverter::emitAlignment(const PrintfSpecifier &FS,
std::string &FormatSpec) {
ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
clang-tools-extra/clang-tidy/utils/FormatStringConverter.h
@@ -40,7 +40,8 @@ public:
FormatStringConverter(ASTContext *Context, const CallExpr *Call,
unsigned FormatArgOffset, Configuration Config,
- const LangOptions &LO);
+ const LangOptions &LO, SourceManager &SM,
+ Preprocessor &PP);
bool canApply() const { return ConversionNotPossibleReason.empty(); }
const std::string &conversionNotPossibleReason() const {
@@ -110,6 +111,10 @@ private:
void appendFormatText(StringRef Text);
void finalizeFormatText();
+ static std::optional<StringRef>
+ formatStringContainsUnreplaceableMacro(const CallExpr *CallExpr,
+ const StringLiteral *FormatExpr,
+ SourceManager &SM, Preprocessor &PP);
bool conversionNotPossible(std::string Reason) {
ConversionNotPossibleReason = std::move(Reason);
return false;
clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
@@ -24,13 +24,15 @@ getPreviousTokenAndStart(SourceLocation Location, const SourceManager &SM,
if (Location.isInvalid())
return {Token, Location};
- auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location));
+ const auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location));
while (Location != StartOfFile) {
Location = Lexer::GetBeginningOfToken(Location, SM, LangOpts);
if (!Lexer::getRawToken(Location, Token, SM, LangOpts) &&
(!SkipComments || !Token.is(tok::comment))) {
break;
}
+ if (Location == StartOfFile)
+ return {Token, Location};
Location = Location.getLocWithOffset(-1);
}
return {Token, Location};
clang-tools-extra/clang-tidy/utils/Matchers.h
@@ -85,15 +85,7 @@ public:
NameList.begin(), NameList.end(), std::back_inserter(NameMatchers),
[](const llvm::StringRef Name) { return NameMatcher(Name); });
}
- bool matches(
- const NamedDecl &Node, ast_matchers::internal::ASTMatchFinder *Finder,
- ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override {
- return llvm::any_of(NameMatchers, [&Node](const NameMatcher &NM) {
- return NM.match(Node);
- });
- }
-private:
class NameMatcher {
llvm::Regex Regex;
enum class MatchMode {
@@ -136,6 +128,15 @@ private:
}
};
+ bool matches(
+ const NamedDecl &Node, ast_matchers::internal::ASTMatchFinder *Finder,
+ ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override {
+ return llvm::any_of(NameMatchers, [&Node](const NameMatcher &NM) {
+ return NM.match(Node);
+ });
+ }
+
+private:
std::vector<NameMatcher> NameMatchers;
};
clang-tools-extra/clang-tidy/zircon/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_clang_library(clangTidyZirconModule
+add_clang_library(clangTidyZirconModule STATIC
TemporaryObjectsCheck.cpp
ZirconTidyModule.cpp
clang-tools-extra/clangd/AST.cpp
@@ -144,8 +144,13 @@ getQualification(ASTContext &Context, const DeclContext *DestContext,
// since we stored inner-most parent first.
std::string Result;
llvm::raw_string_ostream OS(Result);
- for (const auto *Parent : llvm::reverse(Parents))
+ for (const auto *Parent : llvm::reverse(Parents)) {
+ if (Parent != *Parents.rbegin() && Parent->isDependent() &&
+ Parent->getAsRecordDecl() &&
+ Parent->getAsRecordDecl()->getDescribedClassTemplate())
+ OS << "template ";
Parent->print(OS, Context.getPrintingPolicy());
+ }
return OS.str();
}
@@ -187,7 +192,6 @@ std::string printQualifiedName(const NamedDecl &ND) {
// In clangd, context is usually available and paths are mostly noise.
Policy.AnonymousTagLocations = false;
ND.printQualifiedName(OS, Policy);
- OS.flush();
assert(!StringRef(QName).starts_with("::"));
return QName;
}
@@ -270,7 +274,6 @@ std::string printTemplateSpecializationArgs(const NamedDecl &ND) {
// location information.
printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy);
}
- OS.flush();
return TemplateArgs;
}
@@ -303,7 +306,6 @@ std::string printObjCMethod(const ObjCMethodDecl &Method) {
OS << ", ...";
OS << ']';
- OS.flush();
return Name;
}
@@ -314,7 +316,6 @@ std::string printObjCContainer(const ObjCContainerDecl &C) {
const ObjCInterfaceDecl *Class = Category->getClassInterface();
OS << getNameOrErrForObjCInterface(Class) << '(' << Category->getName()
<< ')';
- OS.flush();
return Name;
}
if (const ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(&C)) {
@@ -322,7 +323,6 @@ std::string printObjCContainer(const ObjCContainerDecl &C) {
llvm::raw_string_ostream OS(Name);
const ObjCInterfaceDecl *Class = CID->getClassInterface();
OS << getNameOrErrForObjCInterface(Class) << '(' << CID->getName() << ')';
- OS.flush();
return Name;
}
return C.getNameAsString();
clang-tools-extra/clangd/CMakeLists.txt
@@ -61,7 +61,7 @@ endif()
include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/../clang-tidy")
include_directories(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/../include-cleaner/include")
-add_clang_library(clangDaemon
+add_clang_library(clangDaemon STATIC
AST.cpp
ASTSignals.cpp
ClangdLSPServer.cpp
@@ -183,7 +183,6 @@ target_link_libraries(clangDaemon
${LLVM_PTHREAD_LIB}
clangIncludeCleaner
- clangPseudo
clangTidy
clangTidyUtils
clang-tools-extra/clangd/ClangdServer.cpp
@@ -451,6 +451,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos,
CodeCompleteOpts.MainFileSignals = IP->Signals;
CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes;
+ CodeCompleteOpts.ArgumentLists = Config::current().Completion.ArgumentLists;
// FIXME(ibiryukov): even if Preamble is non-null, we may want to check
// both the old and the new version in case only one of them matches.
CodeCompleteResult Result = clangd::codeComplete(
clang-tools-extra/clangd/CodeComplete.cpp
@@ -21,6 +21,7 @@
#include "AST.h"
#include "CodeCompletionStrings.h"
#include "Compiler.h"
+#include "Config.h"
#include "ExpectedTypes.h"
#include "Feature.h"
#include "FileDistance.h"
@@ -350,8 +351,7 @@ struct CodeCompletionBuilder {
CodeCompletionContext::Kind ContextKind,
const CodeCompleteOptions &Opts,
bool IsUsingDeclaration, tok::TokenKind NextTokenKind)
- : ASTCtx(ASTCtx),
- EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets),
+ : ASTCtx(ASTCtx), ArgumentLists(Opts.ArgumentLists),
IsUsingDeclaration(IsUsingDeclaration), NextTokenKind(NextTokenKind) {
Completion.Deprecated = true; // cleared by any non-deprecated overload.
add(C, SemaCCS, ContextKind);
@@ -561,6 +561,15 @@ private:
}
std::string summarizeSnippet() const {
+ /// localize ArgumentLists tests for better readability
+ const bool None = ArgumentLists == Config::ArgumentListsPolicy::None;
+ const bool Open =
+ ArgumentLists == Config::ArgumentListsPolicy::OpenDelimiter;
+ const bool Delim = ArgumentLists == Config::ArgumentListsPolicy::Delimiters;
+ const bool Full =
+ ArgumentLists == Config::ArgumentListsPolicy::FullPlaceholders ||
+ (!None && !Open && !Delim); // <-- failsafe: Full is default
+
if (IsUsingDeclaration)
return "";
auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>();
@@ -568,7 +577,7 @@ private:
// All bundles are function calls.
// FIXME(ibiryukov): sometimes add template arguments to a snippet, e.g.
// we need to complete 'forward<$1>($0)'.
- return "($0)";
+ return None ? "" : (Open ? "(" : "($0)");
if (Snippet->empty())
return "";
@@ -607,7 +616,7 @@ private:
return "";
}
}
- if (EnableFunctionArgSnippets)
+ if (Full)
return *Snippet;
// Replace argument snippets with a simplified pattern.
@@ -622,9 +631,9 @@ private:
bool EmptyArgs = llvm::StringRef(*Snippet).ends_with("()");
if (Snippet->front() == '<')
- return EmptyArgs ? "<$1>()$0" : "<$1>($0)";
+ return None ? "" : (Open ? "<" : (EmptyArgs ? "<$1>()$0" : "<$1>($0)"));
if (Snippet->front() == '(')
- return EmptyArgs ? "()" : "($0)";
+ return None ? "" : (Open ? "(" : (EmptyArgs ? "()" : "($0)"));
return *Snippet; // Not an arg snippet?
}
// 'CompletionItemKind::Interface' matches template type aliases.
@@ -638,7 +647,7 @@ private:
// e.g. Foo<${1:class}>.
if (llvm::StringRef(*Snippet).ends_with("<>"))
return "<>"; // can happen with defaulted template arguments.
- return "<$0>";
+ return None ? "" : (Open ? "<" : "<$0>");
}
return *Snippet;
}
@@ -654,7 +663,8 @@ private:
ASTContext *ASTCtx;
CodeCompletion Completion;
llvm::SmallVector<BundledEntry, 1> Bundled;
- bool EnableFunctionArgSnippets;
+ /// the way argument lists are handled.
+ Config::ArgumentListsPolicy ArgumentLists;
// No snippets will be generated for using declarations and when the function
// arguments are already present.
bool IsUsingDeclaration;
@@ -1409,6 +1419,9 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
Clang->getPreprocessorOpts().SingleFileParseMode = CompletingInPreamble;
Clang->setCodeCompletionConsumer(Consumer.release());
+ if (Input.Preamble.RequiredModules)
+ Input.Preamble.RequiredModules->adjustHeaderSearchOptions(Clang->getHeaderSearchOpts());
+
SyntaxOnlyAction Action;
if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
log("BeginSourceFile() failed when running codeComplete for {0}",
@@ -2122,7 +2135,7 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
// When an is used, Sema is responsible for completing the main file,
// the index can provide results from the preamble.
// Tell Sema not to deserialize the preamble to look for results.
- Result.LoadExternal = !Index;
+ Result.LoadExternal = ForceLoadPreamble || !Index;
Result.IncludeFixIts = IncludeFixIts;
return Result;
clang-tools-extra/clangd/CodeComplete.h
@@ -17,6 +17,7 @@
#include "ASTSignals.h"
#include "Compiler.h"
+#include "Config.h"
#include "Protocol.h"
#include "Quality.h"
#include "index/Index.h"
@@ -52,6 +53,11 @@ struct CodeCompleteOptions {
/// For example, private members are usually inaccessible.
bool IncludeIneligibleResults = false;
+ /// Force sema to load decls from preamble even if an index is provided.
+ /// This is helpful for cases the index can't provide symbols, e.g. with
+ /// experimental c++20 modules
+ bool ForceLoadPreamble = false;
+
/// Combine overloads into a single completion item where possible.
/// If none, the implementation may choose an appropriate behavior.
/// (In practice, ClangdLSPServer enables bundling if the client claims
@@ -96,10 +102,6 @@ struct CodeCompleteOptions {
/// '->' on member access etc.
bool IncludeFixIts = false;
- /// Whether to generate snippets for function arguments on code-completion.
- /// Needs snippets to be enabled as well.
- bool EnableFunctionArgSnippets = true;
-
/// Whether to include index symbols that are not defined in the scopes
/// visible from the code completion point. This applies in contexts without
/// explicit scope qualifiers.
@@ -107,6 +109,10 @@ struct CodeCompleteOptions {
/// Such completions can insert scope qualifiers.
bool AllScopes = false;
+ /// The way argument list on calls '()' and generics '<>' are handled.
+ Config::ArgumentListsPolicy ArgumentLists =
+ Config::ArgumentListsPolicy::FullPlaceholders;
+
/// Whether to use the clang parser, or fallback to text-based completion
/// (using identifiers in the current file and symbol indexes).
enum CodeCompletionParse {
clang-tools-extra/clangd/Config.h
@@ -126,11 +126,25 @@ struct Config {
std::vector<std::string> FullyQualifiedNamespaces;
} Style;
+ /// controls the completion options for argument lists.
+ enum class ArgumentListsPolicy {
+ /// nothing, no argument list and also NO Delimiters "()" or "<>".
+ None,
+ /// open, only opening delimiter "(" or "<".
+ OpenDelimiter,
+ /// empty pair of delimiters "()" or "<>".
+ Delimiters,
+ /// full name of both type and variable.
+ FullPlaceholders,
+ };
+
/// Configures code completion feature.
struct {
/// Whether code completion includes results that are not visible in current
/// scopes.
bool AllScopes = true;
+ /// controls the completion options for argument lists.
+ ArgumentListsPolicy ArgumentLists = ArgumentListsPolicy::FullPlaceholders;
} Completion;
/// Configures hover feature.
@@ -148,6 +162,7 @@ struct Config {
bool DeducedTypes = true;
bool Designators = true;
bool BlockEnd = false;
+ bool DefaultArguments = false;
// Limit the length of type names in inlay hints. (0 means no limit)
uint32_t TypeNameLimit = 32;
} InlayHints;
clang-tools-extra/clangd/ConfigCompile.cpp
@@ -43,7 +43,6 @@
#include "llvm/Support/Regex.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
-#include <algorithm>
#include <memory>
#include <optional>
#include <string>
@@ -622,6 +621,21 @@ struct FragmentCompiler {
C.Completion.AllScopes = AllScopes;
});
}
+ if (F.ArgumentLists) {
+ if (auto Val =
+ compileEnum<Config::ArgumentListsPolicy>("ArgumentLists",
+ *F.ArgumentLists)
+ .map("None", Config::ArgumentListsPolicy::None)
+ .map("OpenDelimiter",
+ Config::ArgumentListsPolicy::OpenDelimiter)
+ .map("Delimiters", Config::ArgumentListsPolicy::Delimiters)
+ .map("FullPlaceholders",
+ Config::ArgumentListsPolicy::FullPlaceholders)
+ .value())
+ Out.Apply.push_back([Val](const Params &, Config &C) {
+ C.Completion.ArgumentLists = *Val;
+ });
+ }
}
void compile(Fragment::HoverBlock &&F) {
@@ -654,6 +668,11 @@ struct FragmentCompiler {
Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config &C) {
C.InlayHints.BlockEnd = Value;
});
+ if (F.DefaultArguments)
+ Out.Apply.push_back(
+ [Value(**F.DefaultArguments)](const Params &, Config &C) {
+ C.InlayHints.DefaultArguments = Value;
+ });
if (F.TypeNameLimit)
Out.Apply.push_back(
[Value(**F.TypeNameLimit)](const Params &, Config &C) {
clang-tools-extra/clangd/ConfigFragment.h
@@ -32,6 +32,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H
+#include "Config.h"
#include "ConfigProvider.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
@@ -308,6 +309,13 @@ struct Fragment {
/// Whether code completion should include suggestions from scopes that are
/// not visible. The required scope prefix will be inserted.
std::optional<Located<bool>> AllScopes;
+ /// How to present the argument list between '()' and '<>':
+ /// valid values are enum Config::ArgumentListsPolicy values:
+ /// None: Nothing at all
+ /// OpenDelimiter: only opening delimiter "(" or "<"
+ /// Delimiters: empty pair of delimiters "()" or "<>"
+ /// FullPlaceholders: full name of both type and parameter
+ std::optional<Located<std::string>> ArgumentLists;
};
CompletionBlock Completion;
@@ -331,6 +339,9 @@ struct Fragment {
std::optional<Located<bool>> Designators;
/// Show defined symbol names at the end of a definition block.
std::optional<Located<bool>> BlockEnd;
+ /// Show parameter names and default values of default arguments after all
+ /// of the explicit arguments.
+ std::optional<Located<bool>> DefaultArguments;
/// Limit the length of type name hints. (0 means no limit)
std::optional<Located<uint32_t>> TypeNameLimit;
};
clang-tools-extra/clangd/ConfigYAML.cpp
@@ -14,7 +14,6 @@
#include "llvm/Support/YAMLParser.h"
#include <optional>
#include <string>
-#include <system_error>
namespace clang {
namespace clangd {
@@ -230,6 +229,10 @@ private:
if (auto AllScopes = boolValue(N, "AllScopes"))
F.AllScopes = *AllScopes;
});
+ Dict.handle("ArgumentLists", [&](Node &N) {
+ if (auto ArgumentLists = scalarValue(N, "ArgumentLists"))
+ F.ArgumentLists = *ArgumentLists;
+ });
Dict.parse(N);
}
@@ -264,6 +267,10 @@ private:
if (auto Value = boolValue(N, "BlockEnd"))
F.BlockEnd = *Value;
});
+ Dict.handle("DefaultArguments", [&](Node &N) {
+ if (auto Value = boolValue(N, "DefaultArguments"))
+ F.DefaultArguments = *Value;
+ });
Dict.handle("TypeNameLimit", [&](Node &N) {
if (auto Value = uint32Value(N, "TypeNameLimit"))
F.TypeNameLimit = *Value;
clang-tools-extra/clangd/Diagnostics.cpp
@@ -319,7 +319,6 @@ std::string mainMessage(const Diag &D, const ClangdDiagnosticOptions &Opts) {
OS << "\n\n";
printDiag(OS, Note);
}
- OS.flush();
return capitalize(std::move(Result));
}
@@ -335,7 +334,6 @@ std::string noteMessage(const Diag &Main, const DiagBase &Note,
OS << "\n\n";
printDiag(OS, Main);
}
- OS.flush();
return capitalize(std::move(Result));
}
clang-tools-extra/clangd/DumpAST.cpp
@@ -187,6 +187,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
TEMPLATE_KIND(SubstTemplateTemplateParm);
TEMPLATE_KIND(SubstTemplateTemplateParmPack);
TEMPLATE_KIND(UsingTemplate);
+ TEMPLATE_KIND(DeducedTemplate);
#undef TEMPLATE_KIND
}
llvm_unreachable("Unhandled NameKind enum");
clang-tools-extra/clangd/Feature.cpp
@@ -8,6 +8,7 @@
#include "Feature.h"
#include "clang/Basic/Version.h"
+#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX
#include "llvm/Support/Compiler.h"
#include "llvm/TargetParser/Host.h"
clang-tools-extra/clangd/FindSymbols.cpp
@@ -111,7 +111,7 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit,
*Req.Limit *= 5;
}
TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(
- Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
+ Req.Limit.value_or(std::numeric_limits<size_t>::max()));
FuzzyMatcher Filter(Req.Query);
Index->fuzzyFind(Req, [HintPath, &Top, &Filter, AnyScope = Req.AnyScope,
@@ -182,7 +182,6 @@ std::string getSymbolName(ASTContext &Ctx, const NamedDecl &ND) {
OS << (Method->isInstanceMethod() ? '-' : '+');
Method->getSelector().print(OS);
- OS.flush();
return Name;
}
return printName(Ctx, ND);
clang-tools-extra/clangd/Headers.cpp
@@ -75,8 +75,8 @@ public:
IDs.push_back(HID);
}
}
- Out->MainFileIncludesBySpelling.try_emplace(Inc.Written)
- .first->second.push_back(Out->MainFileIncludes.size() - 1);
+ Out->MainFileIncludesBySpelling[Inc.Written].push_back(
+ Out->MainFileIncludes.size() - 1);
}
// Record include graph (not just for main-file includes)
clang-tools-extra/clangd/Hover.cpp
@@ -150,7 +150,6 @@ std::string printDefinition(const Decl *D, PrintingPolicy PP,
std::string Definition;
llvm::raw_string_ostream OS(Definition);
D->print(OS, PP);
- OS.flush();
return Definition;
}
@@ -179,7 +178,6 @@ HoverInfo::PrintedType printType(QualType QT, ASTContext &ASTCtx,
OS << TT->getDecl()->getKindName() << " ";
}
QT.print(OS, PP);
- OS.flush();
const Config &Cfg = Config::current();
if (!QT.isNull() && Cfg.Hover.ShowAKA) {
@@ -229,7 +227,6 @@ HoverInfo::PrintedType printType(const TemplateTemplateParmDecl *TTP,
// FIXME: TemplateTemplateParameter doesn't store the info on whether this
// param was a "typename" or "class".
OS << "> class";
- OS.flush();
return Result;
}
@@ -821,7 +818,6 @@ std::string typeAsDefinition(const HoverInfo::PrintedType &PType) {
OS << PType.Type;
if (PType.AKA)
OS << " // aka: " << *PType.AKA;
- OS.flush();
return Result;
}
clang-tools-extra/clangd/InlayHints.cpp
@@ -11,9 +11,11 @@
#include "Config.h"
#include "HeuristicResolver.h"
#include "ParsedAST.h"
+#include "Protocol.h"
#include "SourceCode.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
@@ -23,15 +25,22 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <iterator>
#include <optional>
#include <string>
@@ -372,6 +381,23 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
return Params;
}
+template <typename R>
+std::string joinAndTruncate(const R &Range, size_t MaxLength) {
+ std::string Out;
+ llvm::raw_string_ostream OS(Out);
+ llvm::ListSeparator Sep(", ");
+ for (auto &&Element : Range) {
+ OS << Sep;
+ if (Out.size() + Element.size() >= MaxLength) {
+ OS << "...";
+ break;
+ }
+ OS << Element;
+ }
+ OS.flush();
+ return Out;
+}
+
struct Callee {
// Only one of Decl or Loc is set.
// Loc is for calls through function pointers.
@@ -422,7 +448,8 @@ public:
Callee.Decl = E->getConstructor();
if (!Callee.Decl)
return true;
- processCall(Callee, {E->getArgs(), E->getNumArgs()});
+ processCall(Callee, E->getParenOrBraceRange().getEnd(),
+ {E->getArgs(), E->getNumArgs()});
return true;
}
@@ -495,7 +522,7 @@ public:
dyn_cast_or_null<CXXMethodDecl>(Callee.Decl))
if (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter())
Args = Args.drop_front(1);
- processCall(Callee, Args);
+ processCall(Callee, E->getRParenLoc(), Args);
return true;
}
@@ -709,10 +736,12 @@ public:
private:
using NameVec = SmallVector<StringRef, 8>;
- void processCall(Callee Callee, llvm::ArrayRef<const Expr *> Args) {
+ void processCall(Callee Callee, SourceLocation RParenOrBraceLoc,
+ llvm::ArrayRef<const Expr *> Args) {
assert(Callee.Decl || Callee.Loc);
- if (!Cfg.InlayHints.Parameters || Args.size() == 0)
+ if ((!Cfg.InlayHints.Parameters && !Cfg.InlayHints.DefaultArguments) ||
+ Args.size() == 0)
return;
// The parameter name of a move or copy constructor is not very interesting.
@@ -721,6 +750,9 @@ private:
if (Ctor->isCopyOrMoveConstructor())
return;
+ SmallVector<std::string> FormattedDefaultArgs;
+ bool HasNonDefaultArgs = false;
+
ArrayRef<const ParmVarDecl *> Params, ForwardedParams;
// Resolve parameter packs to their forwarded parameter
SmallVector<const ParmVarDecl *> ForwardedParamsStorage;
@@ -752,15 +784,44 @@ private:
}
StringRef Name = ParameterNames[I];
- bool NameHint = shouldHintName(Args[I], Name);
- bool ReferenceHint = shouldHintReference(Params[I], ForwardedParams[I]);
-
- if (NameHint || ReferenceHint) {
+ const bool NameHint =
+ shouldHintName(Args[I], Name) && Cfg.InlayHints.Parameters;
+ const bool ReferenceHint =
+ shouldHintReference(Params[I], ForwardedParams[I]) &&
+ Cfg.InlayHints.Parameters;
+
+ const bool IsDefault = isa<CXXDefaultArgExpr>(Args[I]);
+ HasNonDefaultArgs |= !IsDefault;
+ if (IsDefault) {
+ if (Cfg.InlayHints.DefaultArguments) {
+ const auto SourceText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Params[I]->getDefaultArgRange()),
+ AST.getSourceManager(), AST.getLangOpts());
+ const auto Abbrev =
+ (SourceText.size() > Cfg.InlayHints.TypeNameLimit ||
+ SourceText.contains("\n"))
+ ? "..."
+ : SourceText;
+ if (NameHint)
+ FormattedDefaultArgs.emplace_back(
+ llvm::formatv("{0}: {1}", Name, Abbrev));
+ else
+ FormattedDefaultArgs.emplace_back(llvm::formatv("{0}", Abbrev));
+ }
+ } else if (NameHint || ReferenceHint) {
addInlayHint(Args[I]->getSourceRange(), HintSide::Left,
InlayHintKind::Parameter, ReferenceHint ? "&" : "",
NameHint ? Name : "", ": ");
}
}
+
+ if (!FormattedDefaultArgs.empty()) {
+ std::string Hint =
+ joinAndTruncate(FormattedDefaultArgs, Cfg.InlayHints.TypeNameLimit);
+ addInlayHint(SourceRange{RParenOrBraceLoc}, HintSide::Left,
+ InlayHintKind::DefaultArgument,
+ HasNonDefaultArgs ? ", " : "", Hint, "");
+ }
}
static bool isSetter(const FunctionDecl *Callee, const NameVec &ParamNames) {
@@ -968,6 +1029,7 @@ private:
CHECK_KIND(Type, DeducedTypes);
CHECK_KIND(Designator, Designators);
CHECK_KIND(BlockEnd, BlockEnd);
+ CHECK_KIND(DefaultArgument, DefaultArguments);
#undef CHECK_KIND
}
clang-tools-extra/clangd/ModulesBuilder.cpp
@@ -12,6 +12,7 @@
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Serialization/ASTReader.h"
+#include "clang/Serialization/InMemoryModuleCache.h"
namespace clang {
namespace clangd {
@@ -127,50 +128,68 @@ struct ModuleFile {
std::string ModuleFilePath;
};
-bool IsModuleFileUpToDate(
- PathRef ModuleFilePath,
- const PrerequisiteModules &RequisiteModules) {
-IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
- CompilerInstance::createDiagnostics(new DiagnosticOptions());
-
+bool IsModuleFileUpToDate(PathRef ModuleFilePath,
+ const PrerequisiteModules &RequisiteModules,
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
auto HSOpts = std::make_shared<HeaderSearchOptions>();
RequisiteModules.adjustHeaderSearchOptions(*HSOpts);
HSOpts->ForceCheckCXX20ModulesInputFiles = true;
HSOpts->ValidateASTInputFilesContent = true;
+ clang::clangd::IgnoreDiagnostics IgnoreDiags;
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(new DiagnosticOptions, &IgnoreDiags,
+ /*ShouldOwnClient=*/false);
+
+ LangOptions LangOpts;
+ LangOpts.SkipODRCheckInGMF = true;
+
+ FileManager FileMgr(FileSystemOptions(), VFS);
+
+ SourceManager SourceMgr(*Diags, FileMgr);
+
+ HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
+ /*Target=*/nullptr);
+
+ TrivialModuleLoader ModuleLoader;
+ Preprocessor PP(std::make_shared<PreprocessorOptions>(), *Diags, LangOpts,
+ SourceMgr, HeaderInfo, ModuleLoader);
+
+ IntrusiveRefCntPtr<InMemoryModuleCache> ModuleCache = new InMemoryModuleCache;
PCHContainerOperations PCHOperations;
- std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile(
- ModuleFilePath.str(), PCHOperations.getRawReader(), ASTUnit::LoadASTOnly,
- Diags, FileSystemOptions(), std::move(HSOpts));
+ ASTReader Reader(PP, *ModuleCache, /*ASTContext=*/nullptr,
+ PCHOperations.getRawReader(), {});
- if (!Unit)
- return false;
+ // We don't need any listener here. By default it will use a validator
+ // listener.
+ Reader.setListener(nullptr);
- auto Reader = Unit->getASTReader();
- if (!Reader)
+ if (Reader.ReadAST(ModuleFilePath, serialization::MK_MainFile,
+ SourceLocation(),
+ ASTReader::ARR_None) != ASTReader::Success)
return false;
bool UpToDate = true;
- Reader->getModuleManager().visit([&](serialization::ModuleFile &MF) -> bool {
- Reader->visitInputFiles(
+ Reader.getModuleManager().visit([&](serialization::ModuleFile &MF) -> bool {
+ Reader.visitInputFiles(
MF, /*IncludeSystem=*/false, /*Complain=*/false,
[&](const serialization::InputFile &IF, bool isSystem) {
if (!IF.getFile() || IF.isOutOfDate())
UpToDate = false;
});
-
return !UpToDate;
});
-
return UpToDate;
}
bool IsModuleFilesUpToDate(
llvm::SmallVector<PathRef> ModuleFilePaths,
- const PrerequisiteModules &RequisiteModules) {
- return llvm::all_of(ModuleFilePaths, [&RequisiteModules](auto ModuleFilePath) {
- return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules);
- });
+ const PrerequisiteModules &RequisiteModules,
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
+ return llvm::all_of(
+ ModuleFilePaths, [&RequisiteModules, VFS](auto ModuleFilePath) {
+ return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
+ });
}
// StandalonePrerequisiteModules - stands for PrerequisiteModules for which all
@@ -347,7 +366,7 @@ bool StandalonePrerequisiteModules::canReuse(
SmallVector<StringRef> BMIPaths;
for (auto &MF : RequiredModules)
BMIPaths.push_back(MF.ModuleFilePath);
- return IsModuleFilesUpToDate(BMIPaths, *this);
+ return IsModuleFilesUpToDate(BMIPaths, *this, VFS);
}
} // namespace clangd
clang-tools-extra/clangd/ParsedAST.cpp
@@ -280,6 +280,8 @@ public:
llvm::StringRef Check;
while (!Checks.empty()) {
std::tie(Check, Checks) = Checks.split(',');
+ Check = Check.trim();
+
if (Check.empty())
continue;
clang-tools-extra/clangd/Preamble.cpp
@@ -913,7 +913,6 @@ PreamblePatch PreamblePatch::create(llvm::StringRef FileName,
PP.PatchedMarks = std::move(ModifiedScan->Marks);
PP.PatchedMacros = std::move(ModifiedScan->Macros);
dlog("Created preamble patch: {0}", Patch.str());
- Patch.flush();
return PP;
}
clang-tools-extra/clangd/Protocol.cpp
@@ -504,6 +504,16 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R,
P.field("offsetEncoding")))
return false;
}
+
+ if (auto *Experimental = O->getObject("experimental")) {
+ if (auto *TextDocument = Experimental->getObject("textDocument")) {
+ if (auto *Completion = TextDocument->getObject("completion")) {
+ if (auto EditsNearCursor = Completion->getBoolean("editsNearCursor"))
+ R.CompletionFixes |= *EditsNearCursor;
+ }
+ }
+ }
+
return true;
}
@@ -1477,6 +1487,7 @@ llvm::json::Value toJSON(const InlayHintKind &Kind) {
return 2;
case InlayHintKind::Designator:
case InlayHintKind::BlockEnd:
+ case InlayHintKind::DefaultArgument:
// This is an extension, don't serialize.
return nullptr;
}
@@ -1517,6 +1528,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) {
return "designator";
case InlayHintKind::BlockEnd:
return "block-end";
+ case InlayHintKind::DefaultArgument:
+ return "default-argument";
}
llvm_unreachable("Unknown clang.clangd.InlayHintKind");
};
clang-tools-extra/clangd/Protocol.h
@@ -1681,6 +1681,15 @@ enum class InlayHintKind {
/// This is a clangd extension.
BlockEnd = 4,
+ /// An inlay hint that is for a default argument.
+ ///
+ /// An example of a parameter hint for a default argument:
+ /// void foo(bool A = true);
+ /// foo(^);
+ /// Adds an inlay hint "A: true".
+ /// This is a clangd extension.
+ DefaultArgument = 6,
+
/// Other ideas for hints that are not currently implemented:
///
/// * Chaining hints, showing the types of intermediate expressions
clang-tools-extra/clangd/Quality.cpp
@@ -554,7 +554,6 @@ std::string sortText(float Score, llvm::StringRef Name) {
llvm::write_hex(OS, encodeFloat(-Score), llvm::HexPrintStyle::Lower,
/*Width=*/2 * sizeof(Score));
OS << Name;
- OS.flush();
return S;
}
clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -1120,6 +1120,7 @@ public:
case TemplateName::SubstTemplateTemplateParm:
case TemplateName::SubstTemplateTemplateParmPack:
case TemplateName::UsingTemplate:
+ case TemplateName::DeducedTemplate:
// Names that could be resolved to a TemplateDecl are handled elsewhere.
break;
}
clang-tools-extra/clangd/SemanticSelection.cpp
@@ -11,9 +11,6 @@
#include "Protocol.h"
#include "Selection.h"
#include "SourceCode.h"
-#include "clang-pseudo/Bracket.h"
-#include "clang-pseudo/DirectiveTree.h"
-#include "clang-pseudo/Token.h"
#include "clang/AST/DeclBase.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
@@ -25,6 +22,9 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
+#include "support/Bracket.h"
+#include "support/DirectiveTree.h"
+#include "support/Token.h"
#include <optional>
#include <queue>
#include <vector>
@@ -181,16 +181,16 @@ llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST) {
// Related issue: https://github.com/clangd/clangd/issues/310
llvm::Expected<std::vector<FoldingRange>>
getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
- auto OrigStream = pseudo::lex(Code, clang::pseudo::genericLangOpts());
+ auto OrigStream = lex(Code, genericLangOpts());
- auto DirectiveStructure = pseudo::DirectiveTree::parse(OrigStream);
- pseudo::chooseConditionalBranches(DirectiveStructure, OrigStream);
+ auto DirectiveStructure = DirectiveTree::parse(OrigStream);
+ chooseConditionalBranches(DirectiveStructure, OrigStream);
// FIXME: Provide ranges in the disabled-PP regions as well.
auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream);
- auto ParseableStream = cook(Preprocessed, clang::pseudo::genericLangOpts());
- pseudo::pairBrackets(ParseableStream);
+ auto ParseableStream = cook(Preprocessed, genericLangOpts());
+ pairBrackets(ParseableStream);
std::vector<FoldingRange> Result;
auto AddFoldingRange = [&](Position Start, Position End,
@@ -205,19 +205,19 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
FR.kind = Kind.str();
Result.push_back(FR);
};
- auto OriginalToken = [&](const pseudo::Token &T) {
+ auto OriginalToken = [&](const Token &T) {
return OrigStream.tokens()[T.OriginalIndex];
};
- auto StartOffset = [&](const pseudo::Token &T) {
+ auto StartOffset = [&](const Token &T) {
return OriginalToken(T).text().data() - Code.data();
};
- auto StartPosition = [&](const pseudo::Token &T) {
+ auto StartPosition = [&](const Token &T) {
return offsetToPosition(Code, StartOffset(T));
};
- auto EndOffset = [&](const pseudo::Token &T) {
+ auto EndOffset = [&](const Token &T) {
return StartOffset(T) + OriginalToken(T).Length;
};
- auto EndPosition = [&](const pseudo::Token &T) {
+ auto EndPosition = [&](const Token &T) {
return offsetToPosition(Code, EndOffset(T));
};
auto Tokens = ParseableStream.tokens();
@@ -235,7 +235,7 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
}
}
}
- auto IsBlockComment = [&](const pseudo::Token &T) {
+ auto IsBlockComment = [&](const Token &T) {
assert(T.Kind == tok::comment);
return OriginalToken(T).Length >= 2 &&
Code.substr(StartOffset(T), 2) == "/*";
@@ -246,10 +246,10 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
T++;
continue;
}
- pseudo::Token *FirstComment = T;
+ Token *FirstComment = T;
// Show starting sentinals (// and /*) of the comment.
Position Start = offsetToPosition(Code, 2 + StartOffset(*FirstComment));
- pseudo::Token *LastComment = T;
+ Token *LastComment = T;
Position End = EndPosition(*T);
while (T != Tokens.end() && T->Kind == tok::comment &&
StartPosition(*T).line <= End.line + 1) {
clang-tools-extra/clangd/SourceCode.cpp
@@ -814,8 +814,8 @@ llvm::SmallVector<llvm::StringRef> ancestorNamespaces(llvm::StringRef NS) {
// Checks whether \p FileName is a valid spelling of main file.
bool isMainFile(llvm::StringRef FileName, const SourceManager &SM) {
- auto FE = SM.getFileManager().getFile(FileName);
- return FE && *FE == SM.getFileEntryForID(SM.getMainFileID());
+ auto FE = SM.getFileManager().getOptionalFileRef(FileName);
+ return FE && FE == SM.getFileEntryRefForID(SM.getMainFileID());
}
} // namespace
clang-tools-extra/clangd/SystemIncludeExtractor.cpp
@@ -483,7 +483,6 @@ std::string convertGlobToRegex(llvm::StringRef Glob) {
}
}
RegStream << '$';
- RegStream.flush();
return RegText;
}
clang-tools-extra/clangd/TidyProvider.cpp
@@ -46,7 +46,7 @@ public:
[this](std::optional<llvm::StringRef> Data) {
Value.reset();
if (Data && !Data->empty()) {
- tidy::DiagCallback Diagnostics = [](const llvm::SMDiagnostic &D) {
+ auto Diagnostics = [](const llvm::SMDiagnostic &D) {
switch (D.getKind()) {
case llvm::SourceMgr::DK_Error:
elog("tidy-config error at {0}:{1}:{2}: {3}", D.getFilename(),
@@ -149,7 +149,7 @@ static void mergeCheckList(std::optional<std::string> &Checks,
*Checks = llvm::join_items(",", *Checks, List);
}
-TidyProviderRef provideEnvironment() {
+TidyProvider provideEnvironment() {
static const std::optional<std::string> User = [] {
std::optional<std::string> Ret = llvm::sys::Process::GetEnv("USER");
#ifdef _WIN32
@@ -167,7 +167,7 @@ TidyProviderRef provideEnvironment() {
return [](tidy::ClangTidyOptions &, llvm::StringRef) {};
}
-TidyProviderRef provideDefaultChecks() {
+TidyProvider provideDefaultChecks() {
// These default checks are chosen for:
// - low false-positive rate
// - providing a lot of value
@@ -251,7 +251,7 @@ TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) {
};
}
-TidyProviderRef provideClangdConfig() {
+TidyProvider provideClangdConfig() {
return [](tidy::ClangTidyOptions &Opts, llvm::StringRef) {
const auto &CurTidyConfig = Config::current().Diagnostics.ClangTidy;
if (!CurTidyConfig.Checks.empty())
clang-tools-extra/clangd/TidyProvider.h
@@ -30,11 +30,11 @@ using TidyProviderRef = llvm::function_ref<void(tidy::ClangTidyOptions &,
TidyProvider combine(std::vector<TidyProvider> Providers);
[diff truncated]