diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e25b2f50b1b4eafb7c71a2bf6dafa408d27afc50..6adae58832e191f107a10f8fc9f4c009082d3b20 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -132,7 +132,7 @@ clang/test/AST/Interp/ @tbaederr /bolt/ @aaupov @maksfb @rafaelauler @ayermolo @dcci # Bazel build system. -/utils/bazel/ @rupprecht +/utils/bazel/ @rupprecht @keith # InstallAPI and TextAPI /llvm/**/TextAPI/ @cyndyishida diff --git a/.github/workflows/issue-release-workflow.yml b/.github/workflows/issue-release-workflow.yml index eb88ec6e43c57409b6656c9b3e21bdc7cf76fede..5027d4f3ea6f10a6b56341386e9b44070e206436 100644 --- a/.github/workflows/issue-release-workflow.yml +++ b/.github/workflows/issue-release-workflow.yml @@ -53,7 +53,7 @@ jobs: - name: Setup Environment run: | - pip install -r ./llvm/utils/git/requirements.txt + pip install --require-hashes -r ./llvm/utils/git/requirements.txt ./llvm/utils/git/github-automation.py --token ${{ github.token }} setup-llvmbot-git - name: Backport Commits diff --git a/.github/workflows/issue-subscriber.yml b/.github/workflows/issue-subscriber.yml index ef6cd0674e8085c19d56b27f428f788066a05b59..ef4fdf44181938dea5d002cd0a63c64120eb7270 100644 --- a/.github/workflows/issue-subscriber.yml +++ b/.github/workflows/issue-subscriber.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Update watchers working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/libclang-abi-tests.yml b/.github/workflows/libclang-abi-tests.yml index ccfc1e5fb8a742db287f7d1bb9ef60d0974b9dfa..972d21c3bcedf9249d280f1247a5a28693a5e6f5 100644 --- a/.github/workflows/libclang-abi-tests.yml +++ b/.github/workflows/libclang-abi-tests.yml @@ -33,7 +33,6 @@ jobs: ABI_HEADERS: ${{ steps.vars.outputs.ABI_HEADERS }} ABI_LIBS: ${{ steps.vars.outputs.ABI_LIBS }} BASELINE_VERSION_MAJOR: ${{ steps.vars.outputs.BASELINE_VERSION_MAJOR }} - BASELINE_VERSION_MINOR: ${{ steps.vars.outputs.BASELINE_VERSION_MINOR }} LLVM_VERSION_MAJOR: ${{ steps.version.outputs.LLVM_VERSION_MAJOR }} LLVM_VERSION_MINOR: ${{ steps.version.outputs.LLVM_VERSION_MINOR }} LLVM_VERSION_PATCH: ${{ steps.version.outputs.LLVM_VERSION_PATCH }} @@ -51,9 +50,9 @@ jobs: id: vars run: | remote_repo='https://github.com/llvm/llvm-project' - if [ ${{ steps.version.outputs.LLVM_VERSION_MINOR }} -ne 0 ] || [ ${{ steps.version.outputs.LLVM_VERSION_PATCH }} -eq 0 ]; then + if [ ${{ steps.version.outputs.LLVM_VERSION_PATCH }} -eq 0 ]; then major_version=$(( ${{ steps.version.outputs.LLVM_VERSION_MAJOR }} - 1)) - baseline_ref="llvmorg-$major_version.0.0" + baseline_ref="llvmorg-$major_version.1.0" # If there is a minor release, we want to use that as the base line. minor_ref=$(git ls-remote --refs -t "$remote_repo" llvmorg-"$major_version".[1-9].[0-9] | tail -n1 | grep -o 'llvmorg-.\+' || true) @@ -75,7 +74,7 @@ jobs: else { echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.LLVM_VERSION_MAJOR }}" - echo "BASELINE_REF=llvmorg-${{ steps.version.outputs.LLVM_VERSION_MAJOR }}.0.0" + echo "BASELINE_REF=llvmorg-${{ steps.version.outputs.LLVM_VERSION_MAJOR }}.1.0" echo "ABI_HEADERS=." echo "ABI_LIBS=libclang.so libclang-cpp.so" } >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/llvm-bugs.yml b/.github/workflows/llvm-bugs.yml index f592dd6ccd90331cf1338b8ce803faefe56da874..c392078fa45251355e0226e42eefdbfef01ee814 100644 --- a/.github/workflows/llvm-bugs.yml +++ b/.github/workflows/llvm-bugs.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'llvm/llvm-project' steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 check-latest: true diff --git a/.github/workflows/merged-prs.yml b/.github/workflows/merged-prs.yml index 37fc6c67f000baa4da677313bbe7e316ccaaa14c..e29afd4097f9fbe5adf22ae77d5d0cf9f3fa3186 100644 --- a/.github/workflows/merged-prs.yml +++ b/.github/workflows/merged-prs.yml @@ -29,7 +29,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Add Buildbot information comment working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/new-prs.yml b/.github/workflows/new-prs.yml index a60f82ce35d1f317ae474a74fec036294326da0e..88175d6f8d64d4bbbb034ae4985eefa8427e2550 100644 --- a/.github/workflows/new-prs.yml +++ b/.github/workflows/new-prs.yml @@ -43,7 +43,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Greet Author working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/pr-request-release-note.yml b/.github/workflows/pr-request-release-note.yml new file mode 100644 index 0000000000000000000000000000000000000000..5e48ce7aee2e2b32d1f2fc3a086358454e63c056 --- /dev/null +++ b/.github/workflows/pr-request-release-note.yml @@ -0,0 +1,43 @@ +name: PR Request Release Note + +permissions: + contents: read + pull-requests: write + +on: + pull_request: + types: + - closed + +jobs: + request-release-note: + if: >- + github.repository_owner == 'llvm' && + startsWith(github.ref, 'refs/heads/release') + + runs-on: ubuntu-latest + steps: + # We need to pull the script from the main branch, so that we ensure + # we get the latest version of this script. + - name: Checkout Scripts + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + sparse-checkout: | + llvm/utils/git/requirements.txt + llvm/utils/git/github-automation.py + sparse-checkout-cone-mode: false + + - name: Install Dependencies + run: | + pip install --require-hashes -r llvm/utils/git/requirements.txt + + - name: Request Release Note + env: + # We need to use an llvmbot token here, because we are mentioning a user. + GITHUB_TOKEN: ${{ github.token }} + run: | + python3 llvm/utils/git/github-automation.py \ + --repo "$GITHUB_REPOSITORY" \ + --token "$GITHUB_TOKEN" \ + request-release-note \ + --pr-number ${{ github.event.pull_request.number}} diff --git a/.github/workflows/pr-subscriber.yml b/.github/workflows/pr-subscriber.yml index 3952493bb698fe707f75b2ab437a7f8c195bba67..272d3e2f9ef8a31da37ab1301be79227dc23c96c 100644 --- a/.github/workflows/pr-subscriber.yml +++ b/.github/workflows/pr-subscriber.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Automation Script working-directory: ./llvm/utils/git/ run: | - pip install -r requirements.txt + pip install --require-hashes -r requirements.txt - name: Update watchers working-directory: ./llvm/utils/git/ diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index 02082a84d8c10779f1b8209db580d9ac496551b0..fc497a7de94f7a629c71d0908ed2fbd6193bd4ea 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -47,7 +47,7 @@ jobs: - name: Install Dependencies run: | - pip install -r ./llvm/utils/git/requirements.txt + pip install --require-hashes -r ./llvm/utils/git/requirements.txt - name: Check Permissions env: diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index c6d779080bbe73b0c15f812556a1d3881f0ba0ef..4ce6119a407f52897b68fcd9efcf3b4ed5a9c862 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -23,7 +23,7 @@ jobs: - name: Install dependencies run: | - pip install -r ./llvm/utils/git/requirements.txt + pip install --require-hashes -r ./llvm/utils/git/requirements.txt - name: Version Check run: | diff --git a/bolt/docs/CommandLineArgumentReference.md b/bolt/docs/CommandLineArgumentReference.md new file mode 100644 index 0000000000000000000000000000000000000000..1951ad5a2dc5eb8997f32ef25e51cd6e807a6e5f --- /dev/null +++ b/bolt/docs/CommandLineArgumentReference.md @@ -0,0 +1,1213 @@ +# BOLT - a post-link optimizer developed to speed up large applications + +## SYNOPSIS + +`llvm-bolt [-o outputfile] .bolt [-data=perf.fdata] [options]` + +## OPTIONS + +### Generic options + +- `-h` + + Alias for `--help` + +- `--help` + + Display available options (`--help-hidden` for more). + +- `--help-hidden` + + Display all available options. + +- `--help-list` + + Display list of available options (`--help-list-hidden` for more). + +- `--help-list-hidden` + + Display list of all available options. + +- `--print-all-options` + + Print all option values after command line parsing. + +- `--print-options` + + Print non-default options after command line parsing. + +- `--version` + + Display the version of this program. + +### Output options + +- `-o ` + + output file + +- `-w ` + + Save recorded profile to a file + +### BOLT generic options + +- `--align-text=` + + Alignment of .text section + +- `--allow-stripped` + + Allow processing of stripped binaries + +- `--asm-dump[=]` + + Dump function into assembly + +- `-b` + + Alias for -data + +- `--bolt-id=` + + Add any string to tag this execution in the output binary via bolt info section + +- `--break-funcs=` + + List of functions to core dump on (debugging) + +- `--check-encoding` + + Perform verification of LLVM instruction encoding/decoding. Every instruction + in the input is decoded and re-encoded. If the resulting bytes do not match + the input, a warning message is printed. + +- `--cu-processing-batch-size=` + + Specifies the size of batches for processing CUs. Higher number has better + performance, but more memory usage. Default value is 1. + +- `--data=` + + + +- `--debug-skeleton-cu` + + Prints out offsets for abbrev and debug_info of Skeleton CUs that get patched. + +- `--deterministic-debuginfo` + + Disables parallel execution of tasks that may produce nondeterministic debug info + +- `--dot-tooltip-code` + + Add basic block instructions as tool tips on nodes + +- `--dump-cg=` + + Dump callgraph to the given file + +- `--dump-data` + + Dump parsed bolt data for debugging + +- `--dump-dot-all` + + Dump function CFGs to graphviz format after each stage; enable '-print-loops' + for color-coded blocks + +- `--dump-orc` + + Dump raw ORC unwind information (sorted) + +- `--dwarf-output-path=` + + Path to where .dwo files or dwp file will be written out to. + +- `--dwp=` + + Path and name to DWP file. + +- `--dyno-stats` + + Print execution info based on profile + +- `--dyno-stats-all` + + Print dyno stats after each stage + +- `--dyno-stats-scale=` + + Scale to be applied while reporting dyno stats + +- `--enable-bat` + + Write BOLT Address Translation tables + +- `--force-data-relocations` + + Force relocations to data sections to always be processed + +- `--force-patch` + + Force patching of original entry points + +- `--funcs=` + + Limit optimizations to functions from the list + +- `--funcs-file=` + + File with list of functions to optimize + +- `--funcs-file-no-regex=` + + File with list of functions to optimize (non-regex) + +- `--funcs-no-regex=` + + Limit optimizations to functions from the list (non-regex) + +- `--hot-data` + + Hot data symbols support (relocation mode) + +- `--hot-functions-at-end` + + If reorder-functions is used, order functions putting hottest last + +- `--hot-text` + + Generate hot text symbols. Apply this option to a precompiled binary that + manually calls into hugify, such that at runtime hugify call will put hot + code into 2M pages. This requires relocation. + +- `--hot-text-move-sections=` + + List of sections containing functions used for hugifying hot text. BOLT makes + sure these functions are not placed on the same page as the hot text. + (default='.stub,.mover'). + +- `--insert-retpolines` + + Run retpoline insertion pass + +- `--keep-aranges` + + Keep or generate .debug_aranges section if .gdb_index is written + +- `--keep-tmp` + + Preserve intermediate .o file + +- `--lite` + + Skip processing of cold functions + +- `--max-data-relocations=` + + Maximum number of data relocations to process + +- `--max-funcs=` + + Maximum number of functions to process + +- `--no-huge-pages` + + Use regular size pages for code alignment + +- `--no-threads` + + Disable multithreading + +- `--pad-funcs=` + + List of functions to pad with amount of bytes + +- `--profile-format=` + + Format to dump profile output in aggregation mode, default is fdata + - `=fdata`: offset-based plaintext format + - `=yaml`: dense YAML representation + +- `--r11-availability=` + + Determine the availability of r11 before indirect branches + - `=never`: r11 not available + - `=always`: r11 available before calls and jumps + - `=abi`r11 available before calls but not before jumps + +- `--relocs` + + Use relocations in the binary (default=autodetect) + +- `--remove-symtab` + + Remove .symtab section + +- `--reorder-skip-symbols=` + + List of symbol names that cannot be reordered + +- `--reorder-symbols=` + + List of symbol names that can be reordered + +- `--retpoline-lfence` + + Determine if lfence instruction should exist in the retpoline + +- `--skip-funcs=` + + List of functions to skip + +- `--skip-funcs-file=` + + File with list of functions to skip + +- `--strict` + + Trust the input to be from a well-formed source + +- `--tasks-per-thread=` + + Number of tasks to be created per thread + +- `--thread-count=` + + Number of threads + +- `--top-called-limit=` + + Maximum number of functions to print in top called functions section + +- `--trap-avx512` + + In relocation mode trap upon entry to any function that uses AVX-512 instructions + +- `--trap-old-code` + + Insert traps in old function bodies (relocation mode) + +- `--update-debug-sections` + + Update DWARF debug sections of the executable + +- `--use-gnu-stack` + + Use GNU_STACK program header for new segment (workaround for issues with + strip/objcopy) + +- `--use-old-text` + + Re-use space in old .text if possible (relocation mode) + +- `-v ` + + Set verbosity level for diagnostic output + +- `--write-dwp` + + Output a single dwarf package file (dwp) instead of multiple non-relocatable + dwarf object files (dwo). + +### BOLT optimization options + +- `--align-blocks` + + Align basic blocks + +- `--align-blocks-min-size=` + + Minimal size of the basic block that should be aligned + +- `--align-blocks-threshold=` + + Align only blocks with frequency larger than containing function execution + frequency specified in percent. E.g. 1000 means aligning blocks that are 10 + times more frequently executed than the containing function. + +- `--align-functions=` + + Align functions at a given value (relocation mode) + +- `--align-functions-max-bytes=` + + Maximum number of bytes to use to align functions + +- `--assume-abi` + + Assume the ABI is never violated + +- `--block-alignment=` + + Boundary to use for alignment of basic blocks + +- `--bolt-seed=` + + Seed for randomization + +- `--cg-from-perf-data` + + Use perf data directly when constructing the call graph for stale functions + +- `--cg-ignore-recursive-calls` + + Ignore recursive calls when constructing the call graph + +- `--cg-use-split-hot-size` + + Use hot/cold data on basic blocks to determine hot sizes for call graph functions + +- `--cold-threshold=` + + Tenths of percents of main entry frequency to use as a threshold when + evaluating whether a basic block is cold (0 means it is only considered + cold if the block has zero samples). Default: 0 + +- `--elim-link-veneers` + + Run veneer elimination pass + +- `--eliminate-unreachable` + + Eliminate unreachable code + +- `--equalize-bb-counts` + + Use same count for BBs that should have equivalent count (used in non-LBR + and shrink wrapping) + +- `--execution-count-threshold=` + + Perform profiling accuracy-sensitive optimizations only if function execution + count >= the threshold (default: 0) + +- `--fix-block-counts` + + Adjust block counts based on outgoing branch counts + +- `--fix-func-counts` + + Adjust function counts based on basic blocks execution count + +- `--force-inline=` + + List of functions to always consider for inlining + +- `--frame-opt=` + + Optimize stack frame accesses + - `none`: do not perform frame optimization + - `hot`: perform FOP on hot functions + - `all`: perform FOP on all functions + +- `--frame-opt-rm-stores` + + Apply additional analysis to remove stores (experimental) + +- `--function-order=` + + File containing an ordered list of functions to use for function reordering + +- `--generate-function-order=` + + File to dump the ordered list of functions to use for function reordering + +- `--generate-link-sections=` + + Generate a list of function sections in a format suitable for inclusion in a + linker script + +- `--group-stubs` + + Share stubs across functions + +- `--hugify` + + Automatically put hot code on 2MB page(s) (hugify) at runtime. No manual call + to hugify is needed in the binary (which is what --hot-text relies on). + +- `--icf` + + Fold functions with identical code + +- `--icp` + + Alias for --indirect-call-promotion + +- `--icp-calls-remaining-percent-threshold=` + + The percentage threshold against remaining unpromoted indirect call count + for the promotion for calls + +- `--icp-calls-topn` + + Alias for --indirect-call-promotion-calls-topn + +- `--icp-calls-total-percent-threshold=` + + The percentage threshold against total count for the promotion for calls + +- `--icp-eliminate-loads` + + Enable load elimination using memory profiling data when performing ICP + +- `--icp-funcs=` + + List of functions to enable ICP for + +- `--icp-inline` + + Only promote call targets eligible for inlining + +- `--icp-jt-remaining-percent-threshold=` + + The percentage threshold against remaining unpromoted indirect call count for + the promotion for jump tables + +- `--icp-jt-targets` + + Alias for --icp-jump-tables-targets + +- `--icp-jt-topn` + + Alias for --indirect-call-promotion-jump-tables-topn + +- `--icp-jt-total-percent-threshold=` + + The percentage threshold against total count for the promotion for jump tables + +- `--icp-jump-tables-targets` + + For jump tables, optimize indirect jmp targets instead of indices + +- `--icp-mp-threshold` + + Alias for --indirect-call-promotion-mispredict-threshold + +- `--icp-old-code-sequence` + + Use old code sequence for promoted calls + +- `--icp-top-callsites=` + + Optimize hottest calls until at least this percentage of all indirect calls + frequency is covered. 0 = all callsites + +- `--icp-topn` + + Alias for --indirect-call-promotion-topn + +- `--icp-use-mp` + + Alias for --indirect-call-promotion-use-mispredicts + +- `--indirect-call-promotion=` + + Indirect call promotion + - `none`: do not perform indirect call promotion + - `calls`: perform ICP on indirect calls + - `jump-tables`: perform ICP on jump tables + - `all`: perform ICP on calls and jump tables + +- `--indirect-call-promotion-calls-topn=` + + Limit number of targets to consider when doing indirect call promotion on + calls. 0 = no limit + +- `--indirect-call-promotion-jump-tables-topn=` + + Limit number of targets to consider when doing indirect call promotion on + jump tables. 0 = no limit + +- `--indirect-call-promotion-mispredict-threshold=` + + Misprediction threshold for skipping ICP on an indirect call + +- `--indirect-call-promotion-topn=` + + Limit number of targets to consider when doing indirect call promotion. + 0 = no limit + +- `--indirect-call-promotion-use-mispredicts` + + Use misprediction frequency for determining whether or not ICP should be + applied at a callsite. The `-indirect-call-promotion-mispredict-threshold` + value will be used by this heuristic + +- `--infer-fall-throughs` + + Infer execution count for fall-through blocks + +- `--infer-stale-profile` + + Infer counts from stale profile data. + +- `--inline-all` + + Inline all functions + +- `--inline-ap` + + Adjust function profile after inlining + +- `--inline-limit=` + + Maximum number of call sites to inline + +- `--inline-max-iters=` + + Maximum number of inline iterations + +- `--inline-memcpy` + + Inline memcpy using 'rep movsb' instruction (X86-only) + +- `--inline-small-functions` + + Inline functions if increase in size is less than defined by `-inline-small-functions-bytes` + +- `--inline-small-functions-bytes=` + + Max number of bytes for the function to be considered small for inlining purposes + +- `--instrument` + + Instrument code to generate accurate profile data + +- `--iterative-guess` + + In non-LBR mode, guess edge counts using iterative technique + +- `--jt-footprint-optimize-for-icache` + + With jt-footprint-reduction, only process PIC jumptables and turn off other + transformations that increase code size + +- `--jt-footprint-reduction` + + Make jump tables size smaller at the cost of using more instructions at jump + sites + +- `-jump-tables=` + + Jump tables support (default=basic) + - `none`: do not optimize functions with jump tables + - `basic`: optimize functions with jump tables + - `move`: move jump tables to a separate section + - `split`: split jump tables section into hot and cold based on function + execution frequency + - `aggressive`: aggressively split jump tables section based on usage of the + tables + +- `--keep-nops` + + Keep no-op instructions. By default they are removed. + +- `--lite-threshold-count=` + + Similar to '-lite-threshold-pct' but specify threshold using absolute function + call count. I.e. limit processing to functions executed at least the specified + number of times. + +- `--lite-threshold-pct=` + + Threshold (in percent) for selecting functions to process in lite mode. Higher + threshold means fewer functions to process. E.g threshold of 90 means only top + 10 percent of functions with profile will be processed. + +- `--mcf-use-rarcs` + + In MCF, consider the possibility of cancelling flow to balance edges + +- `--memcpy1-spec=` + + List of functions with call sites for which to specialize memcpy() for size 1 + +- `--min-branch-clusters` + + Use a modified clustering algorithm geared towards minimizing branches + +- `--no-inline` + + Disable all inlining (overrides other inlining options) + +- `--no-scan` + + Do not scan cold functions for external references (may result in slower binary) + +- `--peepholes=` + + Enable peephole optimizations + - `none`: disable peepholes + - `double-jumps`: remove double jumps when able + - `tailcall-traps`: insert tail call traps + - `useless-branches`: remove useless conditional branches + - `all`: enable all peephole optimizations + +- `--plt=` + + Optimize PLT calls (requires linking with -znow) + - `none`: do not optimize PLT calls + - `hot`: optimize executed (hot) PLT calls + - `all`: optimize all PLT calls + +- `--preserve-blocks-alignment` + + Try to preserve basic block alignment + +- `--profile-ignore-hash` + + Ignore hash while reading function profile + +- `--profile-use-dfs` + + Use DFS order for YAML profile + +- `--reg-reassign` + + Reassign registers so as to avoid using REX prefixes in hot code + +- `--reorder-blocks=` + + Change layout of basic blocks in a function + - `none`: do not reorder basic blocks + - `reverse`: layout blocks in reverse order + - `normal`: perform optimal layout based on profile + - `branch-predictor`: perform optimal layout prioritizing branch predictions + - `cache`: perform optimal layout prioritizing I-cache behavior + - `cache+`: perform layout optimizing I-cache behavior + - `ext-tsp`: perform layout optimizing I-cache behavior + - `cluster-shuffle`: perform random layout of clusters + +- `--reorder-data=` + + List of sections to reorder + +- `--reorder-data-algo=` + + Algorithm used to reorder data sections + - `count`: sort hot data by read counts + - `funcs`: sort hot data by hot function usage and count + +- `--reorder-data-inplace` + + Reorder data sections in place + +- `--reorder-data-max-bytes=` + + Maximum number of bytes to reorder + +- `--reorder-data-max-symbols=` + + Maximum number of symbols to reorder + +- `--reorder-functions=` + + Reorder and cluster functions (works only with relocations) + - `none`: do not reorder functions + - `exec-count`: order by execution count + - `hfsort`: use hfsort algorithm + - `hfsort+`: use hfsort+ algorithm + - `cdsort`: use cache-directed sort + - `pettis-hansen`: use Pettis-Hansen algorithm + - `random`: reorder functions randomly + - `user`: use function order specified by -function-order + +- `--reorder-functions-use-hot-size` + + Use a function's hot size when doing clustering + +- `--report-bad-layout=` + + Print top functions with suboptimal code layout on input + +- `--report-stale` + + Print the list of functions with stale profile + +- `--runtime-hugify-lib=` + + Specify file name of the runtime hugify library + +- `--runtime-instrumentation-lib=` + + Specify file name of the runtime instrumentation library + +- `--sctc-mode=` + + Mode for simplify conditional tail calls + - `always`: always perform sctc + - `preserve`: only perform sctc when branch direction is preserved + - `heuristic`: use branch prediction data to control sctc + +- `--sequential-disassembly` + + Performs disassembly sequentially + +- `--shrink-wrapping-threshold=` + + Percentage of prologue execution count to use as threshold when evaluating + whether a block is cold enough to be profitable to move eligible spills there + +- `--simplify-conditional-tail-calls` + + Simplify conditional tail calls by removing unnecessary jumps + +- `--simplify-rodata-loads` + + Simplify loads from read-only sections by replacing the memory operand with + the constant found in the corresponding section + +- `--split-align-threshold=` + + When deciding to split a function, apply this alignment while doing the size + comparison (see -split-threshold). Default value: 2. + +- `--split-all-cold` + + Outline as many cold basic blocks as possible + +- `--split-eh` + + Split C++ exception handling code + +- `--split-functions` + + Split functions into fragments + +- `--split-strategy=` + + Strategy used to partition blocks into fragments + + - `profile2`: split each function into a hot and cold fragment using + profiling information + - `cdsplit`: split each function into a hot, warm, and cold fragment using + profiling information + - `random2`: split each function into a hot and cold fragment at a randomly + chosen split point (ignoring any available profiling information) + - `randomN`: split each function into N fragments at randomly chosen split + points (ignoring any available profiling information) + - `all`: split all basic blocks of each function into fragments such that + each fragment contains exactly a single basic block + +- `--split-threshold=` + + Split function only if its main size is reduced by more than given amount of + bytes. Default value: 0, i.e. split iff the size is reduced. Note that on + some architectures the size can increase after splitting. + +- `--stale-matching-max-func-size=` + + The maximum size of a function to consider for inference. + +- `--stale-threshold=` + + Maximum percentage of stale functions to tolerate (default: 100) + +- `--stoke` + + Turn on the stoke analysis + +- `--strip-rep-ret` + + Strip 'repz' prefix from 'repz retq' sequence (on by default) + +- `--tail-duplication=` + + Duplicate unconditional branches that cross a cache line + + - `none` do not apply + - `aggressive` aggressive strategy + - `moderate` moderate strategy + - `cache` cache-aware duplication strategy + +- `--tsp-threshold=` + + Maximum number of hot basic blocks in a function for which to use a precise TSP solution while re-ordering basic blocks + +- `--use-aggr-reg-reassign` + + Use register liveness analysis to try to find more opportunities for -reg-reassign optimization + +- `--use-compact-aligner` + + Use compact approach for aligning functions + +- `--use-edge-counts` + + Use edge count data when doing clustering + +- `--verify-cfg` + + Verify the CFG after every pass + +- `--x86-align-branch-boundary-hot-only` + + Only apply branch boundary alignment in hot code + +- `--x86-strip-redundant-address-size` + + Remove redundant Address-Size override prefix + +### BOLT options in relocation mode + +- `-align-macro-fusion=` + + Fix instruction alignment for macro-fusion (x86 relocation mode) + + - `none`: do not insert alignment no-ops for macro-fusion + - `hot`: only insert alignment no-ops on hot execution paths (default) + - `all`: always align instructions to allow macro-fusion + +### BOLT instrumentation options + +`llvm-bolt -instrument [-o outputfile] ` + +- `--conservative-instrumentation` + + Disable instrumentation optimizations that sacrifice profile accuracy (for + debugging, default: false) + +- `--instrument-calls` + + Record profile for inter-function control flow activity (default: true) + +- `--instrument-hot-only` + + Only insert instrumentation on hot functions (needs profile, default: false) + +- `--instrumentation-binpath=` + + Path to instrumented binary in case if /proc/self/map_files is not accessible + due to access restriction issues + +- `--instrumentation-file=` + + File name where instrumented profile will be saved (default: /tmp/prof.fdata) + +- `--instrumentation-file-append-pid` + + Append PID to saved profile file name (default: false) + +- `--instrumentation-no-counters-clear` + + Don't clear counters across dumps (use with `instrumentation-sleep-time` option) + +- `--instrumentation-sleep-time=` + + Interval between profile writes (default: 0 = write only at program end). + This is useful for service workloads when you want to dump profile every X + minutes or if you are killing the program and the profile is not being + dumped at the end. + +- `--instrumentation-wait-forks` + + Wait until all forks of instrumented process will finish (use with + `instrumentation-sleep-time` option) + +### Data aggregation options (perf2bolt) + +`perf2bolt -p perf.data [-o outputfile] perf.fdata ` + +- `--autofdo` + + Generate autofdo textual data instead of bolt data + +- `--filter-mem-profile` + + If processing a memory profile, filter out stack or heap accesses that won't + be useful for BOLT to reduce profile file size + +- `--ignore-build-id` + + Continue even if build-ids in input binary and perf.data mismatch + +- `--ignore-interrupt-lbr` + + Ignore kernel interrupt LBR that happens asynchronously + +- `--itrace=` + + Generate LBR info with perf itrace argument + +- `--nl` + + Aggregate basic samples (without LBR info) + +- `--pa` + + Skip perf and read data from a pre-aggregated file format + +- `--perfdata=` + + Data file + +- `--pid=` + + Only use samples from process with specified PID + +- `--time-aggr` + + Time BOLT aggregator + +- `--use-event-pc` + + Use event PC in combination with LBR sampling + +### BOLT printing options + +#### Generic options + +- `--print-aliases` + + Print aliases when printing objects + +- `--print-all` + + Print functions after each stage + +- `--print-cfg` + + Print functions after CFG construction + +- `--print-debug-info` + + Print debug info when printing functions + +- `--print-disasm` + + Print function after disassembly + +- `--print-dyno-opcode-stats=` + + Print per instruction opcode dyno stats and the functionnames:BB offsets of + the nth highest execution counts + +- `--print-dyno-stats-only` + + While printing functions output dyno-stats and skip instructions + +- `--print-exceptions` + + Print exception handling data + +- `--print-globals` + + Print global symbols after disassembly + +- `--print-jump-tables` + + Print jump tables + +- `--print-loops` + + Print loop related information + +- `--print-mem-data` + + Print memory data annotations when printing functions + +- `--print-normalized` + + Print functions after CFG is normalized + +- `--print-only=` + + List of functions to print + +- `--print-orc` + + Print ORC unwind information for instructions + +- `--print-profile` + + Print functions after attaching profile + +- `--print-profile-stats` + + Print profile quality/bias analysis + +- `--print-pseudo-probes=` + + Print pseudo probe info + - `=decode`: decode probes section from binary + - `=address_conversion`: update address2ProbesMap with output block address + - `=encoded_probes`: display the encoded probes in binary section + - `=all`: enable all debugging printout + +- `--print-relocations` + + Print relocations when printing functions/objects + +- `--print-reordered-data` + + Print section contents after reordering + +- `--print-retpoline-insertion` + + Print functions after retpoline insertion pass + +- `--print-sdt` + + Print all SDT markers + +- `--print-sections` + + Print all registered sections + +- `--print-unknown` + + Print names of functions with unknown control flow + +- `--time-opts` + + Print time spent in each optimization + +#### Optimization options + +- `--print-after-branch-fixup` + + Print function after fixing local branches + +- `--print-after-jt-footprint-reduction` + + Print function after jt-footprint-reduction pass + +- `--print-after-lowering` + + Print function after instruction lowering + +- `--print-cache-metrics` + + Calculate and print various metrics for instruction cache + +- `--print-clusters` + + Print clusters + +- `--print-finalized` + + Print function after CFG is finalized + +- `--print-fix-relaxations` + + Print functions after fix relaxations pass + +- `--print-fix-riscv-calls` + + Print functions after fix RISCV calls pass + +- `--print-fop` + + Print functions after frame optimizer pass + +- `--print-function-statistics=` + + Print statistics about basic block ordering + +- `--print-icf` + + Print functions after ICF optimization + +- `--print-icp` + + Print functions after indirect call promotion + +- `--print-inline` + + Print functions after inlining optimization + +- `--print-longjmp` + + Print functions after longjmp pass + +- `--print-optimize-bodyless` + + Print functions after bodyless optimization + +- `--print-output-address-range` + + Print output address range for each basic block in the function + whenBinaryFunction::print is called + +- `--print-peepholes` + + Print functions after peephole optimization + +- `--print-plt` + + Print functions after PLT optimization + +- `--print-regreassign` + + Print functions after regreassign pass + +- `--print-reordered` + + Print functions after layout optimization + +- `--print-reordered-functions` + + Print functions after clustering + +- `--print-sctc` + + Print functions after conditional tail call simplification + +- `--print-simplify-rodata-loads` + + Print functions after simplification of RO data loads + +- `--print-sorted-by=` + + Print functions sorted by order of dyno stats + - `executed-forward-branches`: executed forward branches + - `taken-forward-branches`: taken forward branches + - `executed-backward-branches`: executed backward branches + - `taken-backward-branches`: taken backward branches + - `executed-unconditional-branches`: executed unconditional branches + - `all-function-calls`: all function calls + - `indirect-calls`: indirect calls + - `PLT-calls`: PLT calls + - `executed-instructions`: executed instructions + - `executed-load-instructions`: executed load instructions + - `executed-store-instructions`: executed store instructions + - `taken-jump-table-branches`: taken jump table branches + - `taken-unknown-indirect-branches`: taken unknown indirect branches + - `total-branches`: total branches + - `taken-branches`: taken branches + - `non-taken-conditional-branches`: non-taken conditional branches + - `taken-conditional-branches`: taken conditional branches + - `all-conditional-branches`: all conditional branches + - `linker-inserted-veneer-calls`: linker-inserted veneer calls + - `all`: sorted by all names + +- `--print-sorted-by-order=` + + Use ascending or descending order when printing functions ordered by dyno stats + +- `--print-split` + + Print functions after code splitting + +- `--print-stoke` + + Print functions after stoke analysis + +- `--print-uce` + + Print functions after unreachable code elimination + +- `--print-veneer-elimination` + + Print functions after veneer elimination pass + +- `--time-build` + + Print time spent constructing binary functions + +- `--time-rewrite` + + Print time spent in rewriting passes diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h index 06084819ec0b3ab96daa83f071a5594e8d7efcd0..c5ad0ac18339ab6fd667e27dd5ac95e46c8d625f 100644 --- a/bolt/include/bolt/Core/DIEBuilder.h +++ b/bolt/include/bolt/Core/DIEBuilder.h @@ -129,6 +129,9 @@ private: uint64_t UnitSize{0}; llvm::DenseSet AllProcessed; DWARF5AcceleratorTable &DebugNamesTable; + // Unordered map to handle name collision if output DWO directory is + // specified. + std::unordered_map NameToIndexMap; /// Returns current state of the DIEBuilder State &getState() { return *BuilderState.get(); } @@ -384,6 +387,17 @@ public: bool deleteValue(DIEValueList *Die, dwarf::Attribute Attribute) { return Die->deleteValue(Attribute); } + /// Updates DWO Name and Compilation directory for Skeleton CU \p Unit. + std::string updateDWONameCompDir(DebugStrOffsetsWriter &StrOffstsWriter, + DebugStrWriter &StrWriter, + DWARFUnit &SkeletonCU, + std::optional DwarfOutputPath, + std::optional DWONameToUse); + /// Updates DWO Name and Compilation directory for Type Units. + void updateDWONameCompDirForTypes(DebugStrOffsetsWriter &StrOffstsWriter, + DebugStrWriter &StrWriter, DWARFUnit &Unit, + std::optional DwarfOutputPath, + const StringRef DWOName); }; } // namespace bolt } // namespace llvm diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h index 166bb3617e57f68448eb662348e2e414ed885b32..585bafa0888496ff15c8ce5c5610738c303eb51d 100644 --- a/bolt/include/bolt/Core/DebugData.h +++ b/bolt/include/bolt/Core/DebugData.h @@ -430,7 +430,7 @@ protected: using DebugStrOffsetsBufferVector = SmallVector; class DebugStrOffsetsWriter { public: - DebugStrOffsetsWriter() { + DebugStrOffsetsWriter(BinaryContext &BC) : BC(BC) { StrOffsetsBuffer = std::make_unique(); StrOffsetsStream = std::make_unique(*StrOffsetsBuffer); } @@ -460,6 +460,10 @@ public: StrOffsets.clear(); } + bool isStrOffsetsSectionModified() const { + return StrOffsetSectionWasModified; + } + private: std::unique_ptr StrOffsetsBuffer; std::unique_ptr StrOffsetsStream; @@ -467,13 +471,16 @@ private: SmallVector StrOffsets; std::unordered_map ProcessedBaseOffsets; bool StrOffsetSectionWasModified = false; + BinaryContext &BC; }; using DebugStrBufferVector = SmallVector; class DebugStrWriter { public: DebugStrWriter() = delete; - DebugStrWriter(BinaryContext &BC) : BC(BC) { create(); } + DebugStrWriter(DWARFContext &DwCtx, bool IsDWO) : DwCtx(DwCtx), IsDWO(IsDWO) { + create(); + } std::unique_ptr releaseBuffer() { return std::move(StrBuffer); } @@ -495,7 +502,8 @@ private: void create(); std::unique_ptr StrBuffer; std::unique_ptr StrStream; - BinaryContext &BC; + DWARFContext &DwCtx; + bool IsDWO; }; enum class LocWriterKind { DebugLocWriter, DebugLoclistWriter }; diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index f7614cf9ac9777be6812e228190cc20c810b4c3d..f7cf538bd0e867eba4f11636b45361a2280c4706 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -438,8 +438,8 @@ public: return false; } - /// Check whether we support inverting this branch - virtual bool isUnsupportedBranch(const MCInst &Inst) const { return false; } + /// Check whether this conditional branch can be reversed + virtual bool isReversibleBranch(const MCInst &Inst) const { return true; } /// Return true of the instruction is of pseudo kind. virtual bool isPseudo(const MCInst &Inst) const { diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h index f2fa59bcaa1a3ace0d96446bba629992700ea362..c158a9bb3e3f253f5d0e60f4f22c8bb53c42b12b 100644 --- a/bolt/include/bolt/Profile/DataAggregator.h +++ b/bolt/include/bolt/Profile/DataAggregator.h @@ -122,14 +122,14 @@ private: uint64_t ExternCount{0}; }; - struct BranchInfo { + struct TakenBranchInfo { uint64_t TakenCount{0}; uint64_t MispredCount{0}; }; /// Intermediate storage for profile data. We save the results of parsing /// and use them later for processing and assigning profile. - std::unordered_map BranchLBRs; + std::unordered_map BranchLBRs; std::unordered_map FallthroughLBRs; std::vector AggregatedLBRs; std::unordered_map BasicSamples; diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h index 12e0813d089d14a23fe96cbd95fc9c03918e4693..8dec32de9008e5e692a3f61a8ac1b655a3af6d66 100644 --- a/bolt/include/bolt/Rewrite/DWARFRewriter.h +++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h @@ -203,13 +203,16 @@ public: using OverriddenSectionsMap = std::unordered_map; /// Output .dwo files. void writeDWOFiles(DWARFUnit &, const OverriddenSectionsMap &, - const std::string &, DebugLocWriter &); + const std::string &, DebugLocWriter &, + DebugStrOffsetsWriter &, DebugStrWriter &); using KnownSectionsEntry = std::pair; struct DWPState { std::unique_ptr Out; std::unique_ptr TmpBC; std::unique_ptr Streamer; std::unique_ptr Strings; + /// Used to store String sections for .dwo files if they are being modified. + std::vector> StrSections; const MCObjectFileInfo *MCOFI = nullptr; const DWARFUnitIndex *CUIndex = nullptr; std::deque> UncompressedSections; @@ -230,7 +233,8 @@ public: /// add content of dwo to .dwp file. void updateDWP(DWARFUnit &, const OverriddenSectionsMap &, const UnitMeta &, - UnitMetaVectorType &, DWPState &, DebugLocWriter &); + UnitMetaVectorType &, DWPState &, DebugLocWriter &, + DebugStrOffsetsWriter &, DebugStrWriter &); }; } // namespace bolt diff --git a/bolt/lib/Core/BinaryBasicBlock.cpp b/bolt/lib/Core/BinaryBasicBlock.cpp index 4a83fece0e43d4eba92e95afd17c5988092be72a..a4b9a7f558cd80da7f115ae0deaf96dc348bc64d 100644 --- a/bolt/lib/Core/BinaryBasicBlock.cpp +++ b/bolt/lib/Core/BinaryBasicBlock.cpp @@ -131,11 +131,10 @@ bool BinaryBasicBlock::validateSuccessorInvariants() { break; } case 2: - Valid = (CondBranch && - (TBB == getConditionalSuccessor(true)->getLabel() && - ((!UncondBranch && !FBB) || - (UncondBranch && - FBB == getConditionalSuccessor(false)->getLabel())))); + Valid = + CondBranch && TBB == getConditionalSuccessor(true)->getLabel() && + (UncondBranch ? FBB == getConditionalSuccessor(false)->getLabel() + : !FBB); break; } } diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index de34421ebeb08486b2cd4e50dd01f635ebb4d576..10b93e702984fb180f8d38e652b07340cc5a2846 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -1284,7 +1284,7 @@ Error BinaryFunction::disassemble() { const bool IsCondBranch = MIB->isConditionalBranch(Instruction); MCSymbol *TargetSymbol = nullptr; - if (BC.MIB->isUnsupportedBranch(Instruction)) { + if (!BC.MIB->isReversibleBranch(Instruction)) { setIgnored(); if (BinaryFunction *TargetFunc = BC.getBinaryFunctionContainingAddress(TargetAddress)) @@ -3252,12 +3252,9 @@ bool BinaryFunction::validateCFG() const { if (CurrentState == State::CFG_Finalized) return true; - bool Valid = true; for (BinaryBasicBlock *BB : BasicBlocks) - Valid &= BB->validateSuccessorInvariants(); - - if (!Valid) - return Valid; + if (!BB->validateSuccessorInvariants()) + return false; // Make sure all blocks in CFG are valid. auto validateBlock = [this](const BinaryBasicBlock *BB, StringRef Desc) { @@ -3326,7 +3323,7 @@ bool BinaryFunction::validateCFG() const { } } - return Valid; + return true; } void BinaryFunction::fixBranches() { @@ -3384,7 +3381,7 @@ void BinaryFunction::fixBranches() { // Reverse branch condition and swap successors. auto swapSuccessors = [&]() { - if (MIB->isUnsupportedBranch(*CondBranch)) { + if (!MIB->isReversibleBranch(*CondBranch)) { if (opts::Verbosity) { BC.outs() << "BOLT-INFO: unable to swap successors in " << *this << '\n'; diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp index c4b0b251c1201fcf9f27454b3b26fc861d5235bb..34c455a36cce4c98fa302d27a3071d5a3bfa09ff 100644 --- a/bolt/lib/Core/DIEBuilder.cpp +++ b/bolt/lib/Core/DIEBuilder.cpp @@ -22,6 +22,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/LEB128.h" #include @@ -41,6 +42,90 @@ extern cl::opt Verbosity; namespace llvm { namespace bolt { +/// Returns DWO Name to be used to update DW_AT_dwo_name/DW_AT_GNU_dwo_name +/// either in CU or TU unit die. Handles case where user specifies output DWO +/// directory, and there are duplicate names. Assumes DWO ID is unique. +static std::string +getDWOName(llvm::DWARFUnit &CU, + std::unordered_map &NameToIndexMap, + std::optional &DwarfOutputPath) { + assert(CU.getDWOId() && "DWO ID not found."); + std::string DWOName = dwarf::toString( + CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), + ""); + assert(!DWOName.empty() && + "DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exist."); + if (DwarfOutputPath) { + DWOName = std::string(sys::path::filename(DWOName)); + auto Iter = NameToIndexMap.find(DWOName); + if (Iter == NameToIndexMap.end()) + Iter = NameToIndexMap.insert({DWOName, 0}).first; + DWOName.append(std::to_string(Iter->second)); + ++Iter->second; + } + DWOName.append(".dwo"); + return DWOName; +} + +/// Adds a \p Str to .debug_str section. +/// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using +/// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets +/// for this contribution of \p Unit. +static void addStringHelper(DebugStrOffsetsWriter &StrOffstsWriter, + DebugStrWriter &StrWriter, DIEBuilder &DIEBldr, + DIE &Die, const DWARFUnit &Unit, + DIEValue &DIEAttrInfo, StringRef Str) { + uint32_t NewOffset = StrWriter.addString(Str); + if (Unit.getVersion() >= 5) { + StrOffstsWriter.updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(), + NewOffset); + return; + } + DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(), + DIEInteger(NewOffset)); +} + +std::string DIEBuilder::updateDWONameCompDir( + DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter, + DWARFUnit &SkeletonCU, std::optional DwarfOutputPath, + std::optional DWONameToUse) { + DIE &UnitDIE = *getUnitDIEbyUnit(SkeletonCU); + DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name); + if (!DWONameAttrInfo) + DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name); + if (!DWONameAttrInfo) + return ""; + std::string ObjectName; + if (DWONameToUse) + ObjectName = *DWONameToUse; + else + ObjectName = getDWOName(SkeletonCU, NameToIndexMap, DwarfOutputPath); + addStringHelper(StrOffstsWriter, StrWriter, *this, UnitDIE, SkeletonCU, + DWONameAttrInfo, ObjectName); + + DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir); + assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU."); + + if (DwarfOutputPath) { + if (!sys::fs::exists(*DwarfOutputPath)) + sys::fs::create_directory(*DwarfOutputPath); + addStringHelper(StrOffstsWriter, StrWriter, *this, UnitDIE, SkeletonCU, + CompDirAttrInfo, *DwarfOutputPath); + } + return ObjectName; +} + +void DIEBuilder::updateDWONameCompDirForTypes( + DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter, + DWARFUnit &Unit, std::optional DwarfOutputPath, + const StringRef DWOName) { + for (DWARFUnit *DU : getState().DWARF5TUVector) + updateDWONameCompDir(StrOffstsWriter, StrWriter, *DU, DwarfOutputPath, + DWOName); + if (StrOffstsWriter.isStrOffsetsSectionModified()) + StrOffstsWriter.finalizeSection(Unit, *this); +} + void DIEBuilder::updateReferences() { for (auto &[SrcDIEInfo, ReferenceInfo] : getState().AddrReferences) { DIEInfo *DstDIEInfo = ReferenceInfo.Dst; diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp index a987a103a08b93ad7311fc81efb752ca15bac303..f502a503124702a1ecf63b31c33c8ae06e794833 100644 --- a/bolt/lib/Core/DebugData.cpp +++ b/bolt/lib/Core/DebugData.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -867,10 +868,17 @@ void DebugStrOffsetsWriter::finalizeSection(DWARFUnit &Unit, DIEBuilder &DIEBldr) { std::optional AttrVal = findAttributeInfo(Unit.getUnitDIE(), dwarf::DW_AT_str_offsets_base); - if (!AttrVal) + if (!AttrVal && !Unit.isDWOUnit()) return; - std::optional Val = AttrVal->V.getAsSectionOffset(); - assert(Val && "DW_AT_str_offsets_base Value not present."); + std::optional Val = std::nullopt; + if (AttrVal) { + Val = AttrVal->V.getAsSectionOffset(); + } else { + if (!Unit.isDWOUnit()) + BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: " + "DW_AT_str_offsets_base Value not present\n"; + Val = 0; + } DIE &Die = *DIEBldr.getUnitDIEbyUnit(Unit); DIEValue StrListBaseAttrInfo = Die.findAttribute(dwarf::DW_AT_str_offsets_base); @@ -915,7 +923,11 @@ void DebugStrWriter::create() { } void DebugStrWriter::initialize() { - auto StrSection = BC.DwCtx->getDWARFObj().getStrSection(); + StringRef StrSection; + if (IsDWO) + StrSection = DwCtx.getDWARFObj().getStrDWOSection(); + else + StrSection = DwCtx.getDWARFObj().getStrSection(); (*StrStream) << StrSection; } diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index df6dbcddeed56a972e59e1b17d897a7f17644c75..298ba29ff5b3ff0404a9815f2a17cf95efa23ffc 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -674,7 +674,8 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) { MCPlusBuilder *MIB = Function.getBinaryContext().MIB.get(); for (BinaryBasicBlock &BB : Function) { auto checkAndPatch = [&](BinaryBasicBlock *Pred, BinaryBasicBlock *Succ, - const MCSymbol *SuccSym) { + const MCSymbol *SuccSym, + std::optional Offset) { // Ignore infinite loop jumps or fallthrough tail jumps. if (Pred == Succ || Succ == &BB) return false; @@ -715,6 +716,11 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) { Pred->removeSuccessor(&BB); Pred->eraseInstruction(Pred->findInstruction(Branch)); Pred->addTailCallInstruction(SuccSym); + if (Offset) { + MCInst *TailCall = Pred->getLastNonPseudoInstr(); + assert(TailCall); + MIB->setOffset(*TailCall, *Offset); + } } else { return false; } @@ -757,7 +763,8 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) { if (Pred->getSuccessor() == &BB || (Pred->getConditionalSuccessor(true) == &BB && !IsTailCall) || Pred->getConditionalSuccessor(false) == &BB) - if (checkAndPatch(Pred, Succ, SuccSym) && MarkInvalid) + if (checkAndPatch(Pred, Succ, SuccSym, MIB->getOffset(*Inst)) && + MarkInvalid) BB.markValid(BB.pred_size() != 0 || BB.isLandingPad() || BB.isEntryPoint()); } diff --git a/bolt/lib/Passes/Instrumentation.cpp b/bolt/lib/Passes/Instrumentation.cpp index 68acff7e6a867ccd94180651a6e54f36b83cffe8..14f506f9ca9689ad6ebb9735e24ac5c93b77b72a 100644 --- a/bolt/lib/Passes/Instrumentation.cpp +++ b/bolt/lib/Passes/Instrumentation.cpp @@ -480,7 +480,7 @@ void Instrumentation::instrumentFunction(BinaryFunction &Function, else if (BC.MIB->isUnconditionalBranch(Inst)) HasUnconditionalBranch = true; else if ((!BC.MIB->isCall(Inst) && !BC.MIB->isConditionalBranch(Inst)) || - BC.MIB->isUnsupportedBranch(Inst)) + !BC.MIB->isReversibleBranch(Inst)) continue; const uint32_t FromOffset = *BC.MIB->getOffset(Inst); diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index 302bcf1f2d87d931aae5cfd476f69140f8276e2a..f55caa7f03f738b839ad5aca914f65aea654639e 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -1227,7 +1227,7 @@ ErrorOr DataAggregator::parseLocationOrOffset() { if (Sep == StringRef::npos) return parseOffset(); StringRef LookAhead = ParsingBuf.substr(0, Sep); - if (LookAhead.find_first_of(":") == StringRef::npos) + if (!LookAhead.contains(':')) return parseOffset(); ErrorOr BuildID = parseString(':'); @@ -1464,7 +1464,7 @@ uint64_t DataAggregator::parseLBRSample(const PerfBranchSample &Sample, uint64_t To = getBinaryFunctionContainingAddress(LBR.To) ? LBR.To : 0; if (!From && !To) continue; - BranchInfo &Info = BranchLBRs[Trace(From, To)]; + TakenBranchInfo &Info = BranchLBRs[Trace(From, To)]; ++Info.TakenCount; Info.MispredCount += LBR.Mispred; } @@ -1609,7 +1609,7 @@ void DataAggregator::processBranchEvents() { for (const auto &AggrLBR : BranchLBRs) { const Trace &Loc = AggrLBR.first; - const BranchInfo &Info = AggrLBR.second; + const TakenBranchInfo &Info = AggrLBR.second; doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount); } } @@ -2253,13 +2253,13 @@ DataAggregator::writeAggregatedFile(StringRef OutputFilename) const { } else { for (const auto &KV : NamesToBranches) { const FuncBranchData &FBD = KV.second; - for (const llvm::bolt::BranchInfo &BI : FBD.Data) { + for (const BranchInfo &BI : FBD.Data) { writeLocation(BI.From); writeLocation(BI.To); OutFile << BI.Mispreds << " " << BI.Branches << "\n"; ++BranchValues; } - for (const llvm::bolt::BranchInfo &BI : FBD.EntryData) { + for (const BranchInfo &BI : FBD.EntryData) { // Do not output if source is a known symbol, since this was already // accounted for in the source function if (BI.From.IsSymbol) @@ -2355,30 +2355,6 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, for (auto BI = BlockMap.begin(), BE = BlockMap.end(); BI != BE; ++BI) YamlBF.Blocks[BI->second.getBBIndex()].Hash = BI->second.getBBHash(); - auto getSuccessorInfo = [&](uint32_t SuccOffset, unsigned SuccDataIdx) { - const llvm::bolt::BranchInfo &BI = Branches.Data.at(SuccDataIdx); - yaml::bolt::SuccessorInfo SI; - SI.Index = BlockMap.getBBIndex(SuccOffset); - SI.Count = BI.Branches; - SI.Mispreds = BI.Mispreds; - return SI; - }; - - auto getCallSiteInfo = [&](Location CallToLoc, unsigned CallToIdx, - uint32_t Offset) { - const llvm::bolt::BranchInfo &BI = Branches.Data.at(CallToIdx); - yaml::bolt::CallSiteInfo CSI; - CSI.DestId = 0; // designated for unknown functions - CSI.EntryDiscriminator = 0; - CSI.Count = BI.Branches; - CSI.Mispreds = BI.Mispreds; - CSI.Offset = Offset; - if (BinaryData *BD = BC.getBinaryDataByName(CallToLoc.Name)) - YAMLProfileWriter::setCSIDestination(BC, CSI, BD->getSymbol(), BAT, - CallToLoc.Offset); - return CSI; - }; - // Lookup containing basic block offset and index auto getBlock = [&BlockMap](uint32_t Offset) { auto BlockIt = BlockMap.upper_bound(Offset); @@ -2390,28 +2366,29 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, return std::pair(BlockIt->first, BlockIt->second.getBBIndex()); }; - for (const auto &[FromOffset, SuccKV] : Branches.IntraIndex) { - const auto &[_, Index] = getBlock(FromOffset); - yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[Index]; - for (const auto &[SuccOffset, SuccDataIdx] : SuccKV) - if (BlockMap.isInputBlock(SuccOffset)) - YamlBB.Successors.emplace_back( - getSuccessorInfo(SuccOffset, SuccDataIdx)); - } - for (const auto &[FromOffset, CallTo] : Branches.InterIndex) { - const auto &[BlockOffset, BlockIndex] = getBlock(FromOffset); - yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex]; - const uint32_t Offset = FromOffset - BlockOffset; - for (const auto &[CallToLoc, CallToIdx] : CallTo) - YamlBB.CallSites.emplace_back( - getCallSiteInfo(CallToLoc, CallToIdx, Offset)); - llvm::sort(YamlBB.CallSites, [](yaml::bolt::CallSiteInfo &A, - yaml::bolt::CallSiteInfo &B) { - return A.Offset < B.Offset; - }); + for (const BranchInfo &BI : Branches.Data) { + using namespace yaml::bolt; + const auto &[BlockOffset, BlockIndex] = getBlock(BI.From.Offset); + BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex]; + if (BI.To.IsSymbol && BI.To.Name == BI.From.Name && BI.To.Offset != 0) { + // Internal branch + const unsigned SuccIndex = getBlock(BI.To.Offset).second; + auto &SI = YamlBB.Successors.emplace_back(SuccessorInfo{SuccIndex}); + SI.Count = BI.Branches; + SI.Mispreds = BI.Mispreds; + } else { + // Call + const uint32_t Offset = BI.From.Offset - BlockOffset; + auto &CSI = YamlBB.CallSites.emplace_back(CallSiteInfo{Offset}); + CSI.Count = BI.Branches; + CSI.Mispreds = BI.Mispreds; + if (const BinaryData *BD = BC.getBinaryDataByName(BI.To.Name)) + YAMLProfileWriter::setCSIDestination(BC, CSI, BD->getSymbol(), BAT, + BI.To.Offset); + } } // Set entry counts, similar to DataReader::readProfile. - for (const llvm::bolt::BranchInfo &BI : Branches.EntryData) { + for (const BranchInfo &BI : Branches.EntryData) { if (!BlockMap.isInputBlock(BI.To.Offset)) { if (opts::Verbosity >= 1) errs() << "BOLT-WARNING: Unexpected EntryData in " << FuncName diff --git a/bolt/lib/Profile/DataReader.cpp b/bolt/lib/Profile/DataReader.cpp index b2511ba1039989d3a062544df8681f7df3a1d199..06c5e96b780643a947b6851ba8c36ca3b988f322 100644 --- a/bolt/lib/Profile/DataReader.cpp +++ b/bolt/lib/Profile/DataReader.cpp @@ -775,6 +775,7 @@ bool DataReader::recordBranch(BinaryFunction &BF, uint64_t From, uint64_t To, if (collectedInBoltedBinary() && FromBB == ToBB) return true; + // Allow passthrough blocks. BinaryBasicBlock *FTSuccessor = FromBB->getConditionalSuccessor(false); if (FTSuccessor && FTSuccessor->succ_size() == 1 && FTSuccessor->getSuccessor(ToBB->getLabel())) { diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index e4673f6e3c301dc2c385cac687b5b24a9e3e925b..29d94067f459fc0500211679083607b4cc8cfcc3 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -99,6 +99,9 @@ bool YAMLProfileReader::parseFunctionProfile( FuncRawBranchCount += YamlSI.Count; BF.setRawBranchCount(FuncRawBranchCount); + if (BF.empty()) + return true; + if (!opts::IgnoreHash && YamlBF.Hash != BF.computeHash(IsDFSOrder, HashFunction)) { if (opts::Verbosity >= 1) @@ -218,17 +221,28 @@ bool YAMLProfileReader::parseFunctionProfile( continue; } - BinaryBasicBlock &SuccessorBB = *Order[YamlSI.Index]; - if (!BB.getSuccessor(SuccessorBB.getLabel())) { - if (opts::Verbosity >= 1) - errs() << "BOLT-WARNING: no successor for block " << BB.getName() - << " that matches index " << YamlSI.Index << " or block " - << SuccessorBB.getName() << '\n'; - ++MismatchedEdges; - continue; + BinaryBasicBlock *ToBB = Order[YamlSI.Index]; + if (!BB.getSuccessor(ToBB->getLabel())) { + // Allow passthrough blocks. + BinaryBasicBlock *FTSuccessor = BB.getConditionalSuccessor(false); + if (FTSuccessor && FTSuccessor->succ_size() == 1 && + FTSuccessor->getSuccessor(ToBB->getLabel())) { + BinaryBasicBlock::BinaryBranchInfo &FTBI = + FTSuccessor->getBranchInfo(*ToBB); + FTBI.Count += YamlSI.Count; + FTBI.MispredictedCount += YamlSI.Mispreds; + ToBB = FTSuccessor; + } else { + if (opts::Verbosity >= 1) + errs() << "BOLT-WARNING: no successor for block " << BB.getName() + << " that matches index " << YamlSI.Index << " or block " + << ToBB->getName() << '\n'; + ++MismatchedEdges; + continue; + } } - BinaryBasicBlock::BinaryBranchInfo &BI = BB.getBranchInfo(SuccessorBB); + BinaryBasicBlock::BinaryBranchInfo &BI = BB.getBranchInfo(*ToBB); BI.Count += YamlSI.Count; BI.MispredictedCount += YamlSI.Mispreds; } diff --git a/bolt/lib/Rewrite/CMakeLists.txt b/bolt/lib/Rewrite/CMakeLists.txt index 6890f52e2b28bbf8a3bc4c9b569b157a4c432f68..578f1763bfe4ec9e5e345c4d5cc5ca102188400e 100644 --- a/bolt/lib/Rewrite/CMakeLists.txt +++ b/bolt/lib/Rewrite/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + Core DebugInfoDWARF DWP JITLink diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index 9d4297f913f3a7ada11fffd29d53ae8d4867fe60..d582ce7b33a27fbc56b4cbb8ac8a6cad88aa7645 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -458,32 +458,6 @@ static std::optional getAsAddress(const DWARFUnit &DU, return std::nullopt; } -/// Returns DWO Name to be used. Handles case where user specifies output DWO -/// directory, and there are duplicate names. Assumes DWO ID is unique. -static std::string -getDWOName(llvm::DWARFUnit &CU, - std::unordered_map &NameToIndexMap) { - std::optional DWOId = CU.getDWOId(); - assert(DWOId && "DWO ID not found."); - (void)DWOId; - - std::string DWOName = dwarf::toString( - CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), - ""); - assert(!DWOName.empty() && - "DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exists."); - if (!opts::DwarfOutputPath.empty()) { - DWOName = std::string(sys::path::filename(DWOName)); - auto Iter = NameToIndexMap.find(DWOName); - if (Iter == NameToIndexMap.end()) - Iter = NameToIndexMap.insert({DWOName, 0}).first; - DWOName.append(std::to_string(Iter->second)); - ++Iter->second; - } - DWOName.append(".dwo"); - return DWOName; -} - static std::unique_ptr createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile, StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr, @@ -515,7 +489,9 @@ static void emitDWOBuilder(const std::string &DWOName, DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter, DWARFUnit &SplitCU, DWARFUnit &CU, DWARFRewriter::DWPState &State, - DebugLocWriter &LocWriter) { + DebugLocWriter &LocWriter, + DebugStrOffsetsWriter &StrOffstsWriter, + DebugStrWriter &StrWriter) { // Populate debug_info and debug_abbrev for current dwo into StringRef. DWODIEBuilder.generateAbbrevs(); DWODIEBuilder.finish(); @@ -577,54 +553,10 @@ static void emitDWOBuilder(const std::string &DWOName, } if (opts::WriteDWP) Rewriter.updateDWP(CU, OverriddenSections, CUMI, TUMetaVector, State, - LocWriter); + LocWriter, StrOffstsWriter, StrWriter); else - Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter); -} - -/// Adds a \p Str to .debug_str section. -/// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using -/// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets -/// for this contribution of \p Unit. -static void addStringHelper(DebugStrOffsetsWriter &StrOffstsWriter, - DebugStrWriter &StrWriter, DIEBuilder &DIEBldr, - DIE &Die, const DWARFUnit &Unit, - DIEValue &DIEAttrInfo, StringRef Str) { - uint32_t NewOffset = StrWriter.addString(Str); - if (Unit.getVersion() >= 5) { - StrOffstsWriter.updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(), - NewOffset); - return; - } - DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(), - DIEInteger(NewOffset)); -} - -static std::string -updateDWONameCompDir(DebugStrOffsetsWriter &StrOffstsWriter, - DebugStrWriter &StrWriter, - std::unordered_map &NameToIndexMap, - DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &UnitDIE) { - DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name); - if (!DWONameAttrInfo) - DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name); - assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU."); - std::string ObjectName; - - ObjectName = getDWOName(Unit, NameToIndexMap); - addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit, - DWONameAttrInfo, ObjectName.c_str()); - - DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir); - assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU."); - - if (!opts::DwarfOutputPath.empty()) { - if (!sys::fs::exists(opts::DwarfOutputPath)) - sys::fs::create_directory(opts::DwarfOutputPath); - addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit, - CompDirAttrInfo, opts::DwarfOutputPath.c_str()); - } - return ObjectName; + Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter, + StrOffstsWriter, StrWriter); } using DWARFUnitVec = std::vector; @@ -673,9 +605,8 @@ void DWARFRewriter::updateDebugInfo() { return; ARangesSectionWriter = std::make_unique(); - StrWriter = std::make_unique(BC); - - StrOffstsWriter = std::make_unique(); + StrWriter = std::make_unique(*BC.DwCtx, false); + StrOffstsWriter = std::make_unique(BC); if (!opts::DeterministicDebugInfo) { opts::DeterministicDebugInfo = true; @@ -720,10 +651,6 @@ void DWARFRewriter::updateDebugInfo() { return LocListWritersByCU[CUIndex++].get(); }; - // Unordered maps to handle name collision if output DWO directory is - // specified. - std::unordered_map NameToIndexMap; - DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC, *StrWriter); DWPState State; @@ -747,13 +674,20 @@ void DWARFRewriter::updateDebugInfo() { Unit); DWODIEBuilder.buildDWOUnit(**SplitCU); std::string DWOName = ""; + std::optional DwarfOutputPath = + opts::DwarfOutputPath.empty() + ? std::nullopt + : std::optional(opts::DwarfOutputPath.c_str()); { std::lock_guard Lock(AccessMutex); - DWOName = updateDWONameCompDir(*StrOffstsWriter, *StrWriter, - NameToIndexMap, *Unit, *DIEBlder, - *DIEBlder->getUnitDIEbyUnit(*Unit)); + DWOName = DIEBlder->updateDWONameCompDir( + *StrOffstsWriter, *StrWriter, *Unit, DwarfOutputPath, std::nullopt); } - + DebugStrOffsetsWriter DWOStrOffstsWriter(BC); + DebugStrWriter DWOStrWriter((*SplitCU)->getContext(), true); + DWODIEBuilder.updateDWONameCompDirForTypes(DWOStrOffstsWriter, + DWOStrWriter, **SplitCU, + DwarfOutputPath, DWOName); DebugLoclistWriter DebugLocDWoWriter(*Unit, Unit->getVersion(), true); DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter; if (Unit->getVersion() >= 5) { @@ -771,7 +705,7 @@ void DWARFRewriter::updateDebugInfo() { TempRangesSectionWriter->finalizeSection(); emitDWOBuilder(DWOName, DWODIEBuilder, *this, **SplitCU, *Unit, State, - DebugLocDWoWriter); + DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter); } if (Unit->getVersion() >= 5) { @@ -1736,6 +1670,7 @@ std::optional updateDebugData( const DWARFUnitIndex::Entry *CUDWOEntry, uint64_t DWOId, std::unique_ptr &OutputBuffer, DebugRangeListsSectionWriter *RangeListsWriter, DebugLocWriter &LocWriter, + DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter, const llvm::bolt::DWARFRewriter::OverriddenSectionsMap &OverridenSections) { using DWOSectionContribution = @@ -1774,6 +1709,11 @@ std::optional updateDebugData( if (SectionName != "debug_str.dwo") errs() << "BOLT-WARNING: unsupported debug section: " << SectionName << "\n"; + if (StrWriter.isInitialized()) { + OutputBuffer = StrWriter.releaseBuffer(); + return StringRef(reinterpret_cast(OutputBuffer->data()), + OutputBuffer->size()); + } return SectionContents; } case DWARFSectionKind::DW_SECT_INFO: { @@ -1783,6 +1723,11 @@ std::optional updateDebugData( return getOverridenSection(DWARFSectionKind::DW_SECT_EXT_TYPES); } case DWARFSectionKind::DW_SECT_STR_OFFSETS: { + if (StrOffstsWriter.isFinalized()) { + OutputBuffer = StrOffstsWriter.releaseBuffer(); + return StringRef(reinterpret_cast(OutputBuffer->data()), + OutputBuffer->size()); + } return getSliceData(CUDWOEntry, SectionContents, DWARFSectionKind::DW_SECT_STR_OFFSETS, DWPOffset); } @@ -1884,7 +1829,9 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU, const OverriddenSectionsMap &OverridenSections, const DWARFRewriter::UnitMeta &CUMI, DWARFRewriter::UnitMetaVectorType &TUMetaVector, - DWPState &State, DebugLocWriter &LocWriter) { + DWPState &State, DebugLocWriter &LocWriter, + DebugStrOffsetsWriter &StrOffstsWriter, + DebugStrWriter &StrWriter) { const uint64_t DWOId = *CU.getDWOId(); MCSection *const StrOffsetSection = State.MCOFI->getDwarfStrOffDWOSection(); assert(StrOffsetSection && "StrOffsetSection does not exist."); @@ -1941,15 +1888,18 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU, TUEntry.Contributions[Index].getLength32(); State.TypeIndexEntries.insert(std::make_pair(Hash, TUEntry)); }; + std::unique_ptr StrOffsetsOutputData; + std::unique_ptr StrOutputData; for (const SectionRef &Section : DWOFile->sections()) { - std::unique_ptr OutputData; + std::unique_ptr OutputData = nullptr; StringRef SectionName = getSectionName(Section); Expected ContentsExp = Section.getContents(); assert(ContentsExp && "Invalid contents."); - std::optional TOutData = updateDebugData( - (*DWOCU)->getContext(), SectionName, *ContentsExp, State.KnownSections, - *State.Streamer, *this, CUDWOEntry, DWOId, OutputData, - RangeListssWriter, LocWriter, OverridenSections); + std::optional TOutData = + updateDebugData((*DWOCU)->getContext(), SectionName, *ContentsExp, + State.KnownSections, *State.Streamer, *this, CUDWOEntry, + DWOId, OutputData, RangeListssWriter, LocWriter, + StrOffstsWriter, StrWriter, OverridenSections); if (!TOutData) continue; @@ -1961,14 +1911,17 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU, if (SectionName == "debug_str.dwo") { CurStrSection = OutData; + StrOutputData = std::move(OutputData); } else { // Since handleDebugDataPatching returned true, we already know this is // a known section. auto SectionIter = State.KnownSections.find(SectionName); - if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS) + if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS) { CurStrOffsetSection = OutData; - else + StrOffsetsOutputData = std::move(OutputData); + } else { State.Streamer->emitBytes(OutData); + } unsigned int Index = getContributionIndex(SectionIter->second.second, State.IndexVersion); uint64_t Offset = State.ContributionOffsets[Index]; @@ -1992,6 +1945,10 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU, // based on hash. if (!StrSectionWrittenOut && !CurStrOffsetSection.empty() && !CurStrSection.empty()) { + // If debug_str.dwo section was modified storing it until dwp is written + // out. DWPStringPool stores raw pointers to strings. + if (StrOutputData) + State.StrSections.push_back(std::move(StrOutputData)); writeStringsAndOffsets(*State.Streamer.get(), *State.Strings.get(), StrOffsetSection, CurStrSection, CurStrOffsetSection, CU.getVersion()); @@ -2017,7 +1974,8 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU, void DWARFRewriter::writeDWOFiles( DWARFUnit &CU, const OverriddenSectionsMap &OverridenSections, - const std::string &DWOName, DebugLocWriter &LocWriter) { + const std::string &DWOName, DebugLocWriter &LocWriter, + DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter) { // Setup DWP code once. DWARFContext *DWOCtx = BC.getDWOContext(); const uint64_t DWOId = *CU.getDWOId(); @@ -2072,10 +2030,11 @@ void DWARFRewriter::writeDWOFiles( // have .debug_rnglists so won't be part of the loop below. if (!RangeListssWriter->empty()) { std::unique_ptr OutputData; - if (std::optional OutData = updateDebugData( - (*DWOCU)->getContext(), "debug_rnglists.dwo", "", KnownSections, - *Streamer, *this, CUDWOEntry, DWOId, OutputData, - RangeListssWriter, LocWriter, OverridenSections)) + if (std::optional OutData = + updateDebugData((*DWOCU)->getContext(), "debug_rnglists.dwo", "", + KnownSections, *Streamer, *this, CUDWOEntry, + DWOId, OutputData, RangeListssWriter, LocWriter, + StrOffstsWriter, StrWriter, OverridenSections)) Streamer->emitBytes(*OutData); } } @@ -2090,7 +2049,7 @@ void DWARFRewriter::writeDWOFiles( if (std::optional OutData = updateDebugData( (*DWOCU)->getContext(), SectionName, *ContentsExp, KnownSections, *Streamer, *this, CUDWOEntry, DWOId, OutputData, RangeListssWriter, - LocWriter, OverridenSections)) + LocWriter, StrOffstsWriter, StrWriter, OverridenSections)) Streamer->emitBytes(*OutData); } Streamer->finish(); diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 85b39176754b64187b37542f8bb866a22f87cbd1..6e1021a6df22c8640c17285fb4743fa667a67373 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -4808,6 +4808,40 @@ void RewriteInstance::updateELFSymbolTable( // Create a new symbol based on the existing symbol. ELFSymTy NewSymbol = Symbol; + // Handle special symbols based on their name. + Expected SymbolName = Symbol.getName(StringSection); + assert(SymbolName && "cannot get symbol name"); + + auto updateSymbolValue = [&](const StringRef Name, + std::optional Value = std::nullopt) { + NewSymbol.st_value = Value ? *Value : getNewValueForSymbol(Name); + NewSymbol.st_shndx = ELF::SHN_ABS; + BC->outs() << "BOLT-INFO: setting " << Name << " to 0x" + << Twine::utohexstr(NewSymbol.st_value) << '\n'; + }; + + if (*SymbolName == "__hot_start" || *SymbolName == "__hot_end") { + if (opts::HotText) { + updateSymbolValue(*SymbolName); + ++NumHotTextSymsUpdated; + } + goto registerSymbol; + } + + if (*SymbolName == "__hot_data_start" || *SymbolName == "__hot_data_end") { + if (opts::HotData) { + updateSymbolValue(*SymbolName); + ++NumHotDataSymsUpdated; + } + goto registerSymbol; + } + + if (*SymbolName == "_end") { + if (NextAvailableAddress > Symbol.st_value) + updateSymbolValue(*SymbolName, NextAvailableAddress); + goto registerSymbol; + } + if (Function) { // If the symbol matched a function that was not emitted, update the // corresponding section index but otherwise leave it unchanged. @@ -4904,33 +4938,7 @@ void RewriteInstance::updateELFSymbolTable( } } - // Handle special symbols based on their name. - Expected SymbolName = Symbol.getName(StringSection); - assert(SymbolName && "cannot get symbol name"); - - auto updateSymbolValue = [&](const StringRef Name, - std::optional Value = std::nullopt) { - NewSymbol.st_value = Value ? *Value : getNewValueForSymbol(Name); - NewSymbol.st_shndx = ELF::SHN_ABS; - BC->outs() << "BOLT-INFO: setting " << Name << " to 0x" - << Twine::utohexstr(NewSymbol.st_value) << '\n'; - }; - - if (opts::HotText && - (*SymbolName == "__hot_start" || *SymbolName == "__hot_end")) { - updateSymbolValue(*SymbolName); - ++NumHotTextSymsUpdated; - } - - if (opts::HotData && (*SymbolName == "__hot_data_start" || - *SymbolName == "__hot_data_end")) { - updateSymbolValue(*SymbolName); - ++NumHotDataSymsUpdated; - } - - if (*SymbolName == "_end" && NextAvailableAddress > Symbol.st_value) - updateSymbolValue(*SymbolName, NextAvailableAddress); - + registerSymbol: if (IsDynSym) Write((&Symbol - cantFail(Obj.symbols(&SymTabSection)).begin()) * sizeof(ELFSymTy), diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp index 8b1894953f3757f036aa2d7e58f2f033861b0efd..8fdacffcb147b69ef8306fd915cb4c9929d4cb66 100644 --- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp @@ -328,19 +328,19 @@ public: return false; } - bool isUnsupportedBranch(const MCInst &Inst) const override { + bool isReversibleBranch(const MCInst &Inst) const override { if (isDynamicBranch(Inst)) - return true; + return false; switch (Inst.getOpcode()) { default: - return false; + return true; case X86::LOOP: case X86::LOOPE: case X86::LOOPNE: case X86::JECXZ: case X86::JRCXZ: - return true; + return false; } } @@ -1874,7 +1874,7 @@ public: } // Handle conditional branches and ignore indirect branches - if (!isUnsupportedBranch(*I) && getCondCode(*I) == X86::COND_INVALID) { + if (isReversibleBranch(*I) && getCondCode(*I) == X86::COND_INVALID) { // Indirect branch return false; } diff --git a/bolt/test/X86/Inputs/blarge_new_bat_order.preagg.txt b/bolt/test/X86/Inputs/blarge_new_bat_order.preagg.txt new file mode 100644 index 0000000000000000000000000000000000000000..e4e1f170343c62e7fcaad9d5fa16f5a47316f5dd --- /dev/null +++ b/bolt/test/X86/Inputs/blarge_new_bat_order.preagg.txt @@ -0,0 +1,2 @@ +B 800154 401050 20 0 +F 800159 800193 7 diff --git a/bolt/test/X86/Inputs/dwarf5-df-types-debug-names-main.s b/bolt/test/X86/Inputs/dwarf5-df-types-debug-names-main.s index f89f28ec13f4eb8612a1c32bbade9cd4b7bd6606..34ba21f695177474c9bd27d33f47e3578621eecb 100644 --- a/bolt/test/X86/Inputs/dwarf5-df-types-debug-names-main.s +++ b/bolt/test/X86/Inputs/dwarf5-df-types-debug-names-main.s @@ -207,7 +207,7 @@ main: # @main .Linfo_string5: .asciz "f2" # string offset=24 .Linfo_string6: - .asciz "/home/ayermolo/local/tasks/T138552329/typeDedupSplit" # string offset=27 + .asciz "." # string offset=27 .Linfo_string7: .asciz "main.dwo" # string offset=80 .Linfo_string8: @@ -234,15 +234,15 @@ main: # @main .long 19 .long 24 .long 27 - .long 80 - .long 89 - .long 92 - .long 97 - .long 100 - .long 103 - .long 106 - .long 112 - .long 220 + .long 29 + .long 38 + .long 41 + .long 46 + .long 49 + .long 52 + .long 55 + .long 61 + .long 169 .section .debug_info.dwo,"e",@progbits .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit .Ldebug_info_dwo_start2: @@ -474,7 +474,7 @@ main: # @main .byte 1 .byte 8 .byte 2 - .ascii "/home/ayermolo/local/tasks/T138552329/typeDedupSplit" + .ascii "." .byte 0 .byte 46 .byte 0 diff --git a/bolt/test/X86/bb-with-two-tail-calls.s b/bolt/test/X86/bb-with-two-tail-calls.s index caad7b3d735f5f8d1ba64660f61a27231acd62ff..b6703e352ff4bfdd6a29cfc74343c745b826fde6 100644 --- a/bolt/test/X86/bb-with-two-tail-calls.s +++ b/bolt/test/X86/bb-with-two-tail-calls.s @@ -1,19 +1,17 @@ # This reproduces a bug with dynostats when trying to compute branch stats # at a block with two tails calls (one conditional and one unconditional). -# REQUIRES: system-linux - # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \ # RUN: %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 -nostdlib # RUN: llvm-bolt %t.exe -o %t.out --data %t.fdata --lite=0 --dyno-stats \ -# RUN: --print-sctc --print-only=_start 2>&1 | FileCheck %s +# RUN: --print-sctc --print-only=_start -enable-bat 2>&1 | FileCheck %s # CHECK-NOT: Assertion `BranchInfo.size() == 2 && "could only be called for blocks with 2 successors"' failed. # Two tail calls in the same basic block after SCTC: -# CHECK: {{.*}}: ja {{.*}} # TAILCALL # CTCTakenCount: {{.*}} -# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL +# CHECK: {{.*}}: ja {{.*}} # TAILCALL # Offset: 7 # CTCTakenCount: 4 +# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL # Offset: 13 .globl _start _start: @@ -23,7 +21,9 @@ a: ja b x: ret # FDATA: 1 _start #a# 1 _start #b# 2 4 b: jmp e -c: jmp f +c: + .nops 1 + jmp f .globl e e: diff --git a/bolt/test/X86/bolt-address-translation-yaml.test b/bolt/test/X86/bolt-address-translation-yaml.test index c15d6ce15ed0df6dbc13fe837b7e7d2ffb7650be..e21513b7dfe592cb5a2bc14f6aab74564440259a 100644 --- a/bolt/test/X86/bolt-address-translation-yaml.test +++ b/bolt/test/X86/bolt-address-translation-yaml.test @@ -15,6 +15,17 @@ BRANCHENTRY-YAML-CHECK: - name: SolveCubic BRANCHENTRY-YAML-CHECK: bid: 0 BRANCHENTRY-YAML-CHECK: hash: 0x700F19D24600000 BRANCHENTRY-YAML-CHECK-NEXT: succ: [ { bid: 7, cnt: 1 } +# Check that the order is correct between BAT YAML and FDATA->YAML. +RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat_order.preagg.txt \ +RUN: -w %t.yaml -o %t.fdata +RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o %t.null +RUN: FileCheck --input-file %t.yaml --check-prefix ORDER-YAML-CHECK %s +RUN: FileCheck --input-file %t.yaml-fdata --check-prefix ORDER-YAML-CHECK %s +ORDER-YAML-CHECK: - name: SolveCubic +ORDER-YAML-CHECK: bid: 3 +ORDER-YAML-CHECK: hash: 0xDDA1DC5F69F900AC +ORDER-YAML-CHECK-NEXT: calls: [ { off: 0x26, fid: [[#]], cnt: 20 } ] +ORDER-YAML-CHECK-NEXT: succ: [ { bid: 5, cnt: 7 } # Large profile test RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat.preagg.txt -w %t.yaml -o %t.fdata \ RUN: 2>&1 | FileCheck --check-prefix READ-BAT-CHECK %s diff --git a/bolt/test/X86/dwarf5-df-types-modify-dwo-name-mixed.test b/bolt/test/X86/dwarf5-df-types-modify-dwo-name-mixed.test new file mode 100644 index 0000000000000000000000000000000000000000..a4f5ee77ab565a559878b507dea2be70d516266f --- /dev/null +++ b/bolt/test/X86/dwarf5-df-types-modify-dwo-name-mixed.test @@ -0,0 +1,198 @@ +; RUN: rm -rf %t +; RUN: mkdir %t +; RUN: cd %t +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-debug-names-main.s \ +; RUN: -split-dwarf-file=main.dwo -o main.o +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-dup-helper.s \ +; RUN: -split-dwarf-file=helper.dwo -o helper.o +; RUN: %clang %cflags -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections +; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt > log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets main.dwo.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo.dwo >> log.txt +; RUN: cat log.txt | FileCheck -check-prefix=BOLT %s + +;; Test is a mix of DWARF5 TUs where one has DW_AT_comp_dir/DW_AT_dwo_name, and another one doesn't. +;; Tests that BOLT correctly updates DW_AT_dwo_name for TUs. + +; BOLT: DW_TAG_skeleton_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT: DW_TAG_skeleton_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("helper.dwo.dwo") +; BOLT: DW_TAG_type_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT: DW_TAG_type_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT: DW_TAG_type_unit +; BOLT-NOT: DW_AT_dwo_name +; BOLT: DW_TAG_type_unit +; BOLT-NOT: DW_AT_dwo_name +; BOLT: DW_TAG_compile_unit +; BOLT: .debug_str_offsets.dwo contents: +; BOLT-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5 +; BOLT-NEXT: "main" +; BOLT-NEXT: "int" +; BOLT-NEXT: "argc" +; BOLT-NEXT: "argv" +; BOLT-NEXT: "char" +; BOLT-NEXT: "f2" +; BOLT-NEXT: "." +; BOLT-NEXT: "main.dwo.dwo" +; BOLT-NEXT: "c1" +; BOLT-NEXT: "Foo2" +; BOLT-NEXT: "f3" +; BOLT-NEXT: "c2" +; BOLT-NEXT: "c3" +; BOLT-NEXT: "Foo2a" +; BOLT-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)" +; BOLT-NEXT: "main.cpp" +; BOLT-NEXT: helper.dwo.dwo: file format elf64-x86-64 + +; BOLT: .debug_str_offsets.dwo contents: +; BOLT-NEXT: 0x00000000: Contribution size = 64, Format = DWARF32, Version = 5 +; BOLT-NEXT: "fooint" +; BOLT-NEXT: "int" +; BOLT-NEXT: "_Z3foov" +; BOLT-NEXT: "foo" +; BOLT-NEXT: "fint" +; BOLT-NEXT: "c1" +; BOLT-NEXT: "c2" +; BOLT-NEXT: "Foo2Int" +; BOLT-NEXT: "f" +; BOLT-NEXT: "char" +; BOLT-NEXT: "c3" +; BOLT-NEXT: "Foo2a" +; BOLT-NEXT: "clang version 18.0.0" +; BOLT-NEXT: "helper.cpp" +; BOLT-NEXT: "helper.dwo" + + +;; Tests that BOLT correctly handles updating DW_AT_dwo_name when it outputs a DWP file. +;; Currently skipping one of Type units because it is not being de-dupped. +;; In the tu-index this TU is not present. +; RUN: rm main.exe.bolt +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --write-dwp +; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt.dwp > logDWP.txt +; RUN: llvm-dwarfdump --debug-str-offsets main.exe.bolt.dwp >> logDWP.txt +; RUN: cat logDWP.txt | FileCheck -check-prefix=BOLT-DWP %s +; BOLT-DWP: DW_TAG_type_unit +; BOLT-DWP: DW_AT_comp_dir (".") +; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT-DWP: DW_TAG_type_unit +; BOLT-DWP: DW_AT_comp_dir (".") +; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT-DWP: DW_TAG_compile_unit +; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT-DWP: DW_TAG_type_unit +; BOLT-DW-NOT: DW_AT_dwo_name +; BOLT-DWP: Contribution size = 68, Format = DWARF32, Version = 5 +; BOLT-DWP-NEXT: "main" +; BOLT-DWP-NEXT: "int" +; BOLT-DWP-NEXT: "argc" +; BOLT-DWP-NEXT: "argv" +; BOLT-DWP-NEXT: "char" +; BOLT-DWP-NEXT: "f2" +; BOLT-DWP-NEXT: "." +; BOLT-DWP-NEXT: "main.dwo.dwo" +; BOLT-DWP-NEXT: "c1" +; BOLT-DWP-NEXT: "Foo2" +; BOLT-DWP-NEXT: "f3" +; BOLT-DWP-NEXT: "c2" +; BOLT-DWP-NEXT: "c3" +; BOLT-DWP-NEXT: "Foo2a" +; BOLT-DWP-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)" +; BOLT-DWP-NEXT: "main.cpp" +; BOLT-DWP-NEXT: Contribution size = 64, Format = DWARF32, Version = 5 +; BOLT-DWP-NEXT: "fooint" +; BOLT-DWP-NEXT: "int" +; BOLT-DWP-NEXT: "_Z3foov" +; BOLT-DWP-NEXT: "foo" +; BOLT-DWP-NEXT: "fint" +; BOLT-DWP-NEXT: "c1" +; BOLT-DWP-NEXT: "c2" +; BOLT-DWP-NEXT: "Foo2Int" +; BOLT-DWP-NEXT: "f" +; BOLT-DWP-NEXT: "char" +; BOLT-DWP-NEXT: "c3" +; BOLT-DWP-NEXT: "Foo2a" +; BOLT-DWP-NEXT: "clang version 18.0.0" +; BOLT-DWP-NEXT: "helper.cpp" +; BOLT-DWP-NEXT: "helper.dwo + +;; Tests that BOLT correctly handles updating DW_AT_comp_dir/DW_AT_dwo_name when outptut directory is specified. + +; RUN: mkdir DWOOut +; RUN: rm main.exe.bolt +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --dwarf-output-path=%t/DWOOut +; RUN: cd DWOOut +; RUN: llvm-dwarfdump --debug-info -r 0 ../main.exe.bolt > log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo0.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo0.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets main.dwo0.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo0.dwo >> log.txt +; RUN: cat log.txt | FileCheck -check-prefix=BOLT-PATH %s + +; BOLT-PATH: DW_TAG_skeleton_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo") +; BOLT-PATH: DW_TAG_skeleton_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo") +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo") +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo") +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH-NOT: DW_AT_comp_dir +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH-NOT: DW_AT_comp_dir +; BOLT-PATH: DW_TAG_compile_unit +; BOLT-PATH: .debug_str_offsets.dwo contents: +; BOLT-PATH-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5 +; BOLT-PATH-NEXT: "main" +; BOLT-PATH-NEXT: "int" +; BOLT-PATH-NEXT: "argc" +; BOLT-PATH-NEXT: "argv" +; BOLT-PATH-NEXT: "char" +; BOLT-PATH-NEXT: "f2" +; BOLT-PATH-NEXT: dwarf5-df-types-modify-dwo-name-mixed.test.tmp/DWOOut" +; BOLT-PATH-NEXT: "main.dwo0.dwo" +; BOLT-PATH-NEXT: "c1" +; BOLT-PATH-NEXT: "Foo2" +; BOLT-PATH-NEXT: "f3" +; BOLT-PATH-NEXT: "c2" +; BOLT-PATH-NEXT: "c3" +; BOLT-PATH-NEXT: "Foo2a" +; BOLT-PATH-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)" +; BOLT-PATH-NEXT: "main.cpp" +; BOLT-PATH-NEXT: helper.dwo0.dwo: file format elf64-x86-64 + +; BOLT-PATH: .debug_str_offsets.dwo contents: +; BOLT-PATH-NEXT: Contribution size = 64, Format = DWARF32, Version = 5 +; BOLT-PATH-NEXT: "fooint" +; BOLT-PATH-NEXT: "int" +; BOLT-PATH-NEXT: "_Z3foov" +; BOLT-PATH-NEXT: "foo" +; BOLT-PATH-NEXT: "fint" +; BOLT-PATH-NEXT: "c1" +; BOLT-PATH-NEXT: "c2" +; BOLT-PATH-NEXT: "Foo2Int" +; BOLT-PATH-NEXT: "f" +; BOLT-PATH-NEXT: "char" +; BOLT-PATH-NEXT: "c3" +; BOLT-PATH-NEXT: "Foo2a" +; BOLT-PATH-NEXT: "clang version 18.0.0" +; BOLT-PATH-NEXT: "helper.cpp" +; BOLT-PATH-NEXT: "helper.dwo" diff --git a/bolt/test/X86/dwarf5-df-types-modify-dwo-name.test b/bolt/test/X86/dwarf5-df-types-modify-dwo-name.test new file mode 100644 index 0000000000000000000000000000000000000000..086f8f8139628e46bc614c4a36d53ccca1924d03 --- /dev/null +++ b/bolt/test/X86/dwarf5-df-types-modify-dwo-name.test @@ -0,0 +1,175 @@ +; RUN: rm -rf %t +; RUN: mkdir %t +; RUN: cd %t +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-debug-names-main.s \ +; RUN: -split-dwarf-file=main.dwo -o main.o +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-debug-names-helper.s \ +; RUN: -split-dwarf-file=helper.dwo -o helper.o +; RUN: %clang %cflags -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections +; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt > log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets main.dwo.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo.dwo >> log.txt +; RUN: cat log.txt | FileCheck -check-prefix=BOLT %s + +;; Tests that BOLT correctly updates DW_AT_dwo_name for TU Untis. + +; BOLT: DW_TAG_skeleton_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT: DW_TAG_skeleton_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("helper.dwo.dwo") +; BOLT: DW_TAG_type_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT: DW_TAG_type_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT: DW_TAG_type_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("helper.dwo.dwo") +; BOLT: DW_TAG_type_unit +; BOLT: DW_AT_comp_dir (".") +; BOLT: DW_AT_dwo_name ("helper.dwo.dwo") +; BOLT: .debug_str_offsets.dwo contents: +; BOLT-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5 +; BOLT-NEXT: "main" +; BOLT-NEXT: "int" +; BOLT-NEXT: "argc" +; BOLT-NEXT: "argv" +; BOLT-NEXT: "char" +; BOLT-NEXT: "f2" +; BOLT-NEXT: "." +; BOLT-NEXT: "main.dwo.dwo" +; BOLT-NEXT: "c1" +; BOLT-NEXT: "Foo2" +; BOLT-NEXT: "f3" +; BOLT-NEXT: "c2" +; BOLT-NEXT: "c3" +; BOLT-NEXT: "Foo2a" +; BOLT-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)" +; BOLT-NEXT: "main.cpp" +; BOLT-NEXT: helper.dwo.dwo: file format elf64-x86-64 + +; BOLT: .debug_str_offsets.dwo contents: +; BOLT-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5 +; BOLT-NEXT: "fooint" +; BOLT-NEXT: "int" +; BOLT-NEXT: "_Z3foov" +; BOLT-NEXT: "foo" +; BOLT-NEXT: "fint" +; BOLT-NEXT: "." +; BOLT-NEXT: "helper.dwo.dwo" +; BOLT-NEXT: "c1" +; BOLT-NEXT: "c2" +; BOLT-NEXT: "Foo2Int" +; BOLT-NEXT: "f" +; BOLT-NEXT: "char" +; BOLT-NEXT: "c3" +; BOLT-NEXT: "Foo2a" +; BOLT-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)" +; BOLT-NEXT: "helper.cpp" + + +;; Tests that BOLT correctly handles updating DW_AT_dwo_name when it outputs a DWP file. +;; Currently skipping one of Type units because it is not being de-dupped. +;; In the tu-index this TU is not present. +; RUN: rm main.exe.bolt +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --write-dwp +; RUN: llvm-dwarfdump --debug-info -r 0 main.exe.bolt.dwp > logDWP.txt +; RUN: llvm-dwarfdump --debug-str-offsets main.exe.bolt.dwp >> logDWP.txt +; RUN: cat logDWP.txt | FileCheck -check-prefix=BOLT-DWP %s +; BOLT-DWP: DW_TAG_type_unit +; BOLT-DWP: DW_AT_comp_dir (".") +; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT-DWP: DW_TAG_type_unit +; BOLT-DWP: DW_AT_comp_dir (".") +; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT-DWP: DW_TAG_compile_unit +; BOLT-DWP: DW_AT_dwo_name ("main.dwo.dwo") +; BOLT-DWP: DW_TAG_type_unit +; BOLT-DWP: DW_AT_comp_dir (".") +; BOLT-DWP: DW_AT_dwo_name ("helper.dwo.dwo") +; BOLT-DWP: DW_TAG_type_unit +; BOLT-DWP: DW_TAG_compile_unit +; BOLT-DWP: DW_AT_name ("helper.cpp") +; BOLT-DWP: DW_AT_dwo_name ("helper.dwo.dwo") + +;; Tests that BOLT correctly handles updating DW_AT_comp_dir/DW_AT_dwo_name when outptut directory is specified. + +; RUN: mkdir DWOOut +; RUN: rm main.exe.bolt +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --dwarf-output-path=%t/DWOOut +; RUN: cd DWOOut +; RUN: llvm-dwarfdump --debug-info -r 0 ../main.exe.bolt > log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo0.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo0.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets main.dwo0.dwo >> log.txt +; RUN: llvm-dwarfdump --debug-str-offsets helper.dwo0.dwo >> log.txt +; RUN: cat log.txt | FileCheck -check-prefix=BOLT-PATH %s + +; BOLT-PATH: DW_TAG_skeleton_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo") +; BOLT-PATH: DW_TAG_skeleton_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo") +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo") +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("main.dwo0.dwo") +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo") +; BOLT-PATH: DW_TAG_type_unit +; BOLT-PATH: DW_AT_comp_dir (" +; BOLT-PATH-SAME: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut +; BOLT-PATH: DW_AT_dwo_name ("helper.dwo0.dwo") +; BOLT-PATH: .debug_str_offsets.dwo contents: +; BOLT-PATH-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5 +; BOLT-PATH-NEXT: "main" +; BOLT-PATH-NEXT: "int" +; BOLT-PATH-NEXT: "argc" +; BOLT-PATH-NEXT: "argv" +; BOLT-PATH-NEXT: "char" +; BOLT-PATH-NEXT: "f2" +; BOLT-PATH-NEXT: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut" +; BOLT-PATH-NEXT: "main.dwo0.dwo" +; BOLT-PATH-NEXT: "c1" +; BOLT-PATH-NEXT: "Foo2" +; BOLT-PATH-NEXT: "f3" +; BOLT-PATH-NEXT: "c2" +; BOLT-PATH-NEXT: "c3" +; BOLT-PATH-NEXT: "Foo2a" +; BOLT-PATH-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)" +; BOLT-PATH-NEXT: "main.cpp" +; BOLT-PATH-NEXT: helper.dwo0.dwo: file format elf64-x86-64 + +; BOLT-PATH: .debug_str_offsets.dwo contents: +; BOLT-PATH-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5 +; BOLT-PATH-NEXT: "fooint" +; BOLT-PATH-NEXT: "int" +; BOLT-PATH-NEXT: "_Z3foov" +; BOLT-PATH-NEXT: "foo" +; BOLT-PATH-NEXT: "fint" +; BOLT-PATH-NEXT: dwarf5-df-types-modify-dwo-name.test.tmp/DWOOut" +; BOLT-PATH-NEXT: "helper.dwo0.dwo" +; BOLT-PATH-NEXT: "c1" +; BOLT-PATH-NEXT: "c2" +; BOLT-PATH-NEXT: "Foo2Int" +; BOLT-PATH-NEXT: "f" +; BOLT-PATH-NEXT: "char" +; BOLT-PATH-NEXT: "c3" +; BOLT-PATH-NEXT: "Foo2a" +; BOLT-PATH-NEXT: "clang version 18.0.0git (git@github.com:ayermolo/llvm-project.git db35fa8fc524127079662802c4735dbf397f86d0)" +; BOLT-PATH-NEXT: "helper.cpp" diff --git a/bolt/test/X86/profile-passthrough-block.test b/bolt/test/X86/profile-passthrough-block.test new file mode 100644 index 0000000000000000000000000000000000000000..1b875885260dc8bdf629cabb7d63d392d89d8124 --- /dev/null +++ b/bolt/test/X86/profile-passthrough-block.test @@ -0,0 +1,67 @@ +## Test YAMLProfileReader support for pass-through blocks in non-matching edges: +## match the profile edge A -> C to the CFG with blocks A -> B -> C. + +# REQUIRES: system-linux +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib +# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml --profile-ignore-hash -v=1 \ +# RUN: --print-cfg 2>&1 | FileCheck %s + +# CHECK: Binary Function "main" after building cfg +# CHECK: Profile Acc : 100.0% +# CHECK-NOT: BOLT-WARNING: no successor for block .LFT0 that matches index 3 or block .Ltmp0 + +#--- main.s +.globl main +.type main, @function +main: + .cfi_startproc +.LBB00: + pushq %rbp + movq %rsp, %rbp + subq $16, %rsp + testq %rax, %rax + js .LBB03 +.LBB01: + jne .LBB04 +.LBB02: + nop +.LBB03: + xorl %eax, %eax + addq $16, %rsp + popq %rbp + retq +.LBB04: + xorl %eax, %eax + addq $16, %rsp + popq %rbp + retq +## For relocations against .text +.LBB05: + call exit + .cfi_endproc + .size main, .-main + +#--- yaml +--- +header: + profile-version: 1 + binary-name: 'profile-passthrough-block.s.tmp.exe' + binary-build-id: '' + profile-flags: [ lbr ] + profile-origin: branch profile reader + profile-events: '' + dfs-order: false + hash-func: xxh3 +functions: + - name: main + fid: 0 + hash: 0x0000000000000000 + exec: 1 + nblocks: 6 + blocks: + - bid: 1 + insns: 1 + succ: [ { bid: 3, cnt: 1} ] +... diff --git a/bolt/test/X86/yaml-non-simple.test b/bolt/test/X86/yaml-non-simple.test new file mode 100644 index 0000000000000000000000000000000000000000..fef98f692a71039f48563b1d5124f26b161c4225 --- /dev/null +++ b/bolt/test/X86/yaml-non-simple.test @@ -0,0 +1,71 @@ +## Check that YAML profile for non-simple function is not reported as stale. + +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o +# RUN: %clang %cflags %t.o -o %t.exe -nostdlib +# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml --profile-ignore-hash -v=1 \ +# RUN: --report-stale 2>&1 | FileCheck %s + +# CHECK: BOLT-INFO: could not disassemble function main. Will ignore. +# CHECK: BOLT-INFO: could not disassemble function main.cold. Will ignore. +# CHECK: BOLT-INFO: 0 out of 2 functions in the binary (0.0%) have non-empty execution profile +# CHECK: BOLT-INFO: 1 function with profile could not be optimized + +#--- main.s +.globl main +.type main, @function +main: + .cfi_startproc +.LBB00: + pushq %rbp + movq %rsp, %rbp + subq $16, %rsp + testq %rax, %rax + js .LBB03 +.LBB01: + jne .LBB04 +.LBB02: + nop +.LBB03: + xorl %eax, %eax + addq $16, %rsp + popq %rbp + retq +.LBB04: + xorl %eax, %eax + addq $16, %rsp + popq %rbp + retq + .cfi_endproc + .size main, .-main + +.globl main.cold +.type main.cold, @function +main.cold: + .cfi_startproc + nop + .cfi_endproc + .size main.cold, .-main.cold + +#--- yaml +--- +header: + profile-version: 1 + binary-name: 'yaml-non-simple.s.tmp.exe' + binary-build-id: '' + profile-flags: [ lbr ] + profile-origin: branch profile reader + profile-events: '' + dfs-order: false + hash-func: xxh3 +functions: + - name: main + fid: 0 + hash: 0x0000000000000000 + exec: 1 + nblocks: 5 + blocks: + - bid: 1 + insns: 1 + succ: [ { bid: 3, cnt: 1} ] +... diff --git a/bolt/test/runtime/X86/hot-end-symbol.s b/bolt/test/runtime/X86/hot-end-symbol.s index e6d83d77167acde60626e0be308fb3f3c716ca39..6ae771cead75682010dbd0739682f00b4f30239a 100755 --- a/bolt/test/runtime/X86/hot-end-symbol.s +++ b/bolt/test/runtime/X86/hot-end-symbol.s @@ -12,6 +12,7 @@ # RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q # RUN: llvm-bolt %t.exe --relocs=1 --hot-text --reorder-functions=hfsort \ +# RUN: --split-functions --split-strategy=all \ # RUN: --data %t.fdata -o %t.out | FileCheck %s # RUN: %t.out 1 @@ -30,12 +31,12 @@ # CHECK-OUTPUT: __hot_start # CHECK-OUTPUT-NEXT: main # CHECK-OUTPUT-NEXT: __hot_end +# CHECK-OUTPUT-NOT: __hot_start.cold .text .globl main .type main, %function .globl __hot_start - .type __hot_start, %object .p2align 4 main: __hot_start: diff --git a/clang-tools-extra/clang-query/CMakeLists.txt b/clang-tools-extra/clang-query/CMakeLists.txt index 8a58d4224e049d2485772bf69281ba535e145bc2..34f018c4a03f389c6cb6767f7cf293de64ea9aeb 100644 --- a/clang-tools-extra/clang-query/CMakeLists.txt +++ b/clang-tools-extra/clang-query/CMakeLists.txt @@ -20,7 +20,6 @@ clang_target_link_libraries(clangQuery clangBasic clangDynamicASTMatchers clangFrontend - clangTooling clangSerialization ) diff --git a/clang-tools-extra/clang-query/Query.cpp b/clang-tools-extra/clang-query/Query.cpp index 9d5807a52fa8ed4f6612e8ef9f2ce52c3c65cb66..93f4104d39db8c95b8a840d4e29de278fabb0249 100644 --- a/clang-tools-extra/clang-query/Query.cpp +++ b/clang-tools-extra/clang-query/Query.cpp @@ -13,7 +13,6 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/TextDiagnostic.h" -#include "clang/Tooling/NodeIntrospection.h" #include "llvm/Support/raw_ostream.h" #include @@ -69,8 +68,6 @@ bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { "Diagnostic location for bound nodes.\n" " detailed-ast " "Detailed AST output for bound nodes.\n" - " srcloc " - "Source locations and ranges for bound nodes.\n" " dump " "Detailed AST output for bound nodes (alias of detailed-ast).\n\n"; return true; @@ -91,90 +88,6 @@ struct CollectBoundNodes : MatchFinder::MatchCallback { } }; -void dumpLocations(llvm::raw_ostream &OS, DynTypedNode Node, ASTContext &Ctx, - const DiagnosticsEngine &Diags, SourceManager const &SM) { - auto Locs = clang::tooling::NodeIntrospection::GetLocations(Node); - - auto PrintLocations = [](llvm::raw_ostream &OS, auto Iter, auto End) { - auto CommonEntry = Iter->first; - auto Scout = Iter; - SmallVector LocationStrings; - while (Scout->first == CommonEntry) { - LocationStrings.push_back( - tooling::LocationCallFormatterCpp::format(*Iter->second)); - if (Scout == End) - break; - ++Scout; - if (Scout->first == CommonEntry) - ++Iter; - } - llvm::sort(LocationStrings); - for (auto &LS : LocationStrings) { - OS << " * \"" << LS << "\"\n"; - } - return Iter; - }; - - TextDiagnostic TD(OS, Ctx.getLangOpts(), &Diags.getDiagnosticOptions()); - - for (auto Iter = Locs.LocationAccessors.begin(); - Iter != Locs.LocationAccessors.end(); ++Iter) { - if (!Iter->first.isValid()) - continue; - - TD.emitDiagnostic(FullSourceLoc(Iter->first, SM), DiagnosticsEngine::Note, - "source locations here", std::nullopt, std::nullopt); - - Iter = PrintLocations(OS, Iter, Locs.LocationAccessors.end()); - OS << '\n'; - } - - for (auto Iter = Locs.RangeAccessors.begin(); - Iter != Locs.RangeAccessors.end(); ++Iter) { - - if (!Iter->first.getBegin().isValid()) - continue; - - if (SM.getPresumedLineNumber(Iter->first.getBegin()) != - SM.getPresumedLineNumber(Iter->first.getEnd())) - continue; - - TD.emitDiagnostic( - FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note, - "source ranges here " + Iter->first.printToString(SM), - CharSourceRange::getTokenRange(Iter->first), std::nullopt); - - Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end()); - } - for (auto Iter = Locs.RangeAccessors.begin(); - Iter != Locs.RangeAccessors.end(); ++Iter) { - - if (!Iter->first.getBegin().isValid()) - continue; - - if (SM.getPresumedLineNumber(Iter->first.getBegin()) == - SM.getPresumedLineNumber(Iter->first.getEnd())) - continue; - - TD.emitDiagnostic( - FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note, - "source range " + Iter->first.printToString(SM) + " starting here...", - CharSourceRange::getTokenRange(Iter->first), std::nullopt); - - auto ColNum = SM.getPresumedColumnNumber(Iter->first.getEnd()); - auto LastLineLoc = Iter->first.getEnd().getLocWithOffset(-(ColNum - 1)); - - TD.emitDiagnostic(FullSourceLoc(Iter->first.getEnd(), SM), - DiagnosticsEngine::Note, "... ending here", - CharSourceRange::getTokenRange( - SourceRange(LastLineLoc, Iter->first.getEnd())), - std::nullopt); - - Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end()); - } - OS << "\n"; -} - } // namespace bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { @@ -195,8 +108,7 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { return false; } - auto &Ctx = AST->getASTContext(); - const auto &SM = Ctx.getSourceManager(); + ASTContext &Ctx = AST->getASTContext(); Ctx.getParentMapContext().setTraversalKind(QS.TK); Finder.matchAST(Ctx); @@ -244,19 +156,11 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { } if (QS.DetailedASTOutput) { OS << "Binding for \"" << BI->first << "\":\n"; - const ASTContext &Ctx = AST->getASTContext(); ASTDumper Dumper(OS, Ctx, AST->getDiagnostics().getShowColors()); Dumper.SetTraversalKind(QS.TK); Dumper.Visit(BI->second); OS << "\n"; } - if (QS.SrcLocOutput) { - OS << "\n \"" << BI->first << "\" Source locations\n"; - OS << " " << std::string(19 + BI->first.size(), '-') << '\n'; - - dumpLocations(OS, BI->second, Ctx, AST->getDiagnostics(), SM); - OS << "\n"; - } } if (MI->getMap().empty()) diff --git a/clang-tools-extra/clang-query/Query.h b/clang-tools-extra/clang-query/Query.h index 7242479633c24f8e94e75ff4ff8e3600e182d2a6..af250fbe13ce3fa72e0b3b729392c5447655538e 100644 --- a/clang-tools-extra/clang-query/Query.h +++ b/clang-tools-extra/clang-query/Query.h @@ -17,7 +17,7 @@ namespace clang { namespace query { -enum OutputKind { OK_Diag, OK_Print, OK_DetailedAST, OK_SrcLoc }; +enum OutputKind { OK_Diag, OK_Print, OK_DetailedAST }; enum QueryKind { QK_Invalid, @@ -149,7 +149,6 @@ struct SetExclusiveOutputQuery : Query { QS.DiagOutput = false; QS.DetailedASTOutput = false; QS.PrintOutput = false; - QS.SrcLocOutput = false; QS.*Var = true; return true; } diff --git a/clang-tools-extra/clang-query/QueryParser.cpp b/clang-tools-extra/clang-query/QueryParser.cpp index 85a442bdd7dedab0318d2d881580401989519c92..1d0b7d9bc6fc84350f0497da542603661a4da3c8 100644 --- a/clang-tools-extra/clang-query/QueryParser.cpp +++ b/clang-tools-extra/clang-query/QueryParser.cpp @@ -11,7 +11,6 @@ #include "QuerySession.h" #include "clang/ASTMatchers/Dynamic/Parser.h" #include "clang/Basic/CharInfo.h" -#include "clang/Tooling/NodeIntrospection.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include @@ -104,19 +103,16 @@ QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) { template QueryRef QueryParser::parseSetOutputKind() { StringRef ValStr; - bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport(); - unsigned OutKind = - LexOrCompleteWord(this, ValStr) - .Case("diag", OK_Diag) - .Case("print", OK_Print) - .Case("detailed-ast", OK_DetailedAST) - .Case("srcloc", OK_SrcLoc, /*IsCompletion=*/HasIntrospection) - .Case("dump", OK_DetailedAST) - .Default(~0u); + unsigned OutKind = LexOrCompleteWord(this, ValStr) + .Case("diag", OK_Diag) + .Case("print", OK_Print) + .Case("detailed-ast", OK_DetailedAST) + .Case("dump", OK_DetailedAST) + .Default(~0u); if (OutKind == ~0u) { - return new InvalidQuery("expected 'diag', 'print', 'detailed-ast'" + - StringRef(HasIntrospection ? ", 'srcloc'" : "") + - " or 'dump', got '" + ValStr + "'"); + return new InvalidQuery("expected 'diag', 'print', 'detailed-ast' or " + "'dump', got '" + + ValStr + "'"); } switch (OutKind) { @@ -126,10 +122,6 @@ template QueryRef QueryParser::parseSetOutputKind() { return new QueryType(&QuerySession::DiagOutput); case OK_Print: return new QueryType(&QuerySession::PrintOutput); - case OK_SrcLoc: - if (HasIntrospection) - return new QueryType(&QuerySession::SrcLocOutput); - return new InvalidQuery("'srcloc' output support is not available."); } llvm_unreachable("Invalid output kind"); diff --git a/clang-tools-extra/clang-query/QuerySession.h b/clang-tools-extra/clang-query/QuerySession.h index 9a08289a253449193d9a0a0868d8ec93a24dd244..31a4900e26190b13354c5101ef8ddb0fecd61e36 100644 --- a/clang-tools-extra/clang-query/QuerySession.h +++ b/clang-tools-extra/clang-query/QuerySession.h @@ -25,15 +25,14 @@ class QuerySession { public: QuerySession(llvm::ArrayRef> ASTs) : ASTs(ASTs), PrintOutput(false), DiagOutput(true), - DetailedASTOutput(false), SrcLocOutput(false), BindRoot(true), - PrintMatcher(false), Terminate(false), TK(TK_AsIs) {} + DetailedASTOutput(false), BindRoot(true), PrintMatcher(false), + Terminate(false), TK(TK_AsIs) {} llvm::ArrayRef> ASTs; bool PrintOutput; bool DiagOutput; bool DetailedASTOutput; - bool SrcLocOutput; bool BindRoot; bool PrintMatcher; diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp index de2a3b51422a5c3bbe20aa2da2d8e3cabfcd3452..200bb87a5ac3cbc6c90c8d207a10371a2e4ab629 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -311,7 +311,18 @@ ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer( : Context(Ctx), ExternalDiagEngine(ExternalDiagEngine), RemoveIncompatibleErrors(RemoveIncompatibleErrors), GetFixesFromNotes(GetFixesFromNotes), - EnableNolintBlocks(EnableNolintBlocks) {} + EnableNolintBlocks(EnableNolintBlocks) { + + if (Context.getOptions().HeaderFilterRegex && + !Context.getOptions().HeaderFilterRegex->empty()) + HeaderFilter = + std::make_unique(*Context.getOptions().HeaderFilterRegex); + + if (Context.getOptions().ExcludeHeaderFilterRegex && + !Context.getOptions().ExcludeHeaderFilterRegex->empty()) + ExcludeHeaderFilter = std::make_unique( + *Context.getOptions().ExcludeHeaderFilterRegex); +} void ClangTidyDiagnosticConsumer::finalizeLastError() { if (!Errors.empty()) { @@ -562,22 +573,17 @@ void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location, } StringRef FileName(File->getName()); - LastErrorRelatesToUserCode = LastErrorRelatesToUserCode || - Sources.isInMainFile(Location) || - getHeaderFilter()->match(FileName); + LastErrorRelatesToUserCode = + LastErrorRelatesToUserCode || Sources.isInMainFile(Location) || + (HeaderFilter && + (HeaderFilter->match(FileName) && + !(ExcludeHeaderFilter && ExcludeHeaderFilter->match(FileName)))); unsigned LineNumber = Sources.getExpansionLineNumber(Location); LastErrorPassesLineFilter = LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber); } -llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() { - if (!HeaderFilter) - HeaderFilter = - std::make_unique(*Context.getOptions().HeaderFilterRegex); - return HeaderFilter.get(); -} - void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() { // Each error is modelled as the set of intervals in which it applies // replacements. To detect overlapping replacements, we use a sweep line diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index 9280eb1e1f218dfb22c3fb8e6025db9d882733bd..97e16a12febd04a8b4f43ee215d49954a3721101 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -313,6 +313,7 @@ private: bool EnableNolintBlocks; std::vector Errors; std::unique_ptr HeaderFilter; + std::unique_ptr ExcludeHeaderFilter; bool LastErrorRelatesToUserCode = false; bool LastErrorPassesLineFilter = false; bool LastErrorWasIgnored = false; diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp index cbf21a0e2ae344b5dc32c1c0c43920c1d3cb5e66..445c7f85c900c66507e271ebabf9c6780fca3bcf 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp @@ -170,6 +170,8 @@ template <> struct MappingTraits { IO.mapOptional("ImplementationFileExtensions", Options.ImplementationFileExtensions); IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex); + IO.mapOptional("ExcludeHeaderFilterRegex", + Options.ExcludeHeaderFilterRegex); IO.mapOptional("FormatStyle", Options.FormatStyle); IO.mapOptional("User", Options.User); IO.mapOptional("CheckOptions", Options.CheckOptions); @@ -191,7 +193,8 @@ ClangTidyOptions ClangTidyOptions::getDefaults() { Options.WarningsAsErrors = ""; Options.HeaderFileExtensions = {"", "h", "hh", "hpp", "hxx"}; Options.ImplementationFileExtensions = {"c", "cc", "cpp", "cxx"}; - Options.HeaderFilterRegex = ""; + Options.HeaderFilterRegex = std::nullopt; + Options.ExcludeHeaderFilterRegex = std::nullopt; Options.SystemHeaders = false; Options.FormatStyle = "none"; Options.User = std::nullopt; @@ -231,6 +234,7 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other, overrideValue(ImplementationFileExtensions, Other.ImplementationFileExtensions); overrideValue(HeaderFilterRegex, Other.HeaderFilterRegex); + overrideValue(ExcludeHeaderFilterRegex, Other.ExcludeHeaderFilterRegex); overrideValue(SystemHeaders, Other.SystemHeaders); overrideValue(FormatStyle, Other.FormatStyle); overrideValue(User, Other.User); diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index e7636cb5d9b06312171f73adeb529d549b744603..85d5a02ebbc1bc9a2ef19e4d7e742ecab86d51ff 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -83,6 +83,10 @@ struct ClangTidyOptions { /// main files will always be displayed. std::optional HeaderFilterRegex; + /// \brief Exclude warnings from headers matching this filter, even if they + /// match \c HeaderFilterRegex. + std::optional ExcludeHeaderFilterRegex; + /// Output warnings from system headers matching \c HeaderFilterRegex. std::optional SystemHeaders; diff --git a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp index 6f22f02f301835e4ef94f8afed89edbe918b5544..f99beac668ce72e12314cb8f245274c7e8d3b291 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp @@ -9,20 +9,20 @@ #include "ImplicitWideningOfMultiplicationResultCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" #include "clang/Lex/Lexer.h" #include using namespace clang::ast_matchers; -namespace clang { +namespace clang::tidy::bugprone { + namespace { AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) { return Node.isPartOfExplicitCast(); } +AST_MATCHER(Expr, containsErrors) { return Node.containsErrors(); } } // namespace -} // namespace clang - -namespace clang::tidy::bugprone { static const Expr *getLHSOfMulBinOp(const Expr *E) { assert(E == E->IgnoreParens() && "Already skipped all parens!"); @@ -250,7 +250,8 @@ void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting( void ImplicitWideningOfMultiplicationResultCheck::registerMatchers( MatchFinder *Finder) { - Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(), + Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(), + isInTemplateInstantiation(), isPartOfExplicitCast())), hasCastKind(CK_IntegralCast)) .bind("x"), diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp index 9ab59e6b0474f0b70d5d9654a72df2c230b56cc6..600eab37552766ba1fec108bf83a4d642c9437ff 100644 --- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp @@ -71,7 +71,9 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) { ofClass(matchers::matchesAnyListedName(OptionalTypes)))), hasType(ConstructTypeMatcher), hasArgument(0U, ignoringImpCasts(anyOf(OptionalDereferenceMatcher, - StdMoveCallMatcher)))) + StdMoveCallMatcher))), + unless(anyOf(hasAncestor(typeLoc()), + hasAncestor(expr(matchers::hasUnevaluatedContext()))))) .bind("expr"), this); } diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp index ca1ae551cc632af3da4bd0d12b4697a8892ff985..2fca7ae2e7eee884dce290c95e6bf131a5f27a99 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp @@ -171,8 +171,7 @@ void SuspiciousEnumUsageCheck::check(const MatchFinder::MatchResult &Result) { // Skip when one of the parameters is an empty enum. The // hasDisjointValueRange function could not decide the values properly in // case of an empty enum. - if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() || - OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end()) + if (EnumDec->enumerators().empty() || OtherEnumDec->enumerators().empty()) return; if (!hasDisjointValueRange(EnumDec, OtherEnumDec)) diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MisleadingCaptureDefaultByValueCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/MisleadingCaptureDefaultByValueCheck.cpp index 00dfa17a1ccf611e0eeb8928028581da3dc51f75..5dee7f91a93410bdf36a6effb6feab2ab8d2a1f1 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MisleadingCaptureDefaultByValueCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MisleadingCaptureDefaultByValueCheck.cpp @@ -67,8 +67,7 @@ static std::string createReplacementText(const LambdaExpr *Lambda) { AppendName("this"); } } - if (!Replacement.empty() && - Lambda->explicit_capture_begin() != Lambda->explicit_capture_end()) { + if (!Replacement.empty() && !Lambda->explicit_captures().empty()) { // Add back separator if we are adding explicit capture variables. Stream << ", "; } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp index d2117c67a76d0b6bba43d0cabbf62b4dc20808ad..ed76ac665049d1cf3af0fb61a1950932c3ae89f0 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp @@ -25,7 +25,9 @@ SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck( "AllowMissingMoveFunctions", false)), AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", false)), AllowMissingMoveFunctionsWhenCopyIsDeleted( - Options.get("AllowMissingMoveFunctionsWhenCopyIsDeleted", false)) {} + Options.get("AllowMissingMoveFunctionsWhenCopyIsDeleted", false)), + AllowImplicitlyDeletedCopyOrMove( + Options.get("AllowImplicitlyDeletedCopyOrMove", false)) {} void SpecialMemberFunctionsCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { @@ -33,17 +35,34 @@ void SpecialMemberFunctionsCheck::storeOptions( Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor); Options.store(Opts, "AllowMissingMoveFunctionsWhenCopyIsDeleted", AllowMissingMoveFunctionsWhenCopyIsDeleted); + Options.store(Opts, "AllowImplicitlyDeletedCopyOrMove", + AllowImplicitlyDeletedCopyOrMove); +} + +std::optional +SpecialMemberFunctionsCheck::getCheckTraversalKind() const { + return AllowImplicitlyDeletedCopyOrMove ? TK_AsIs + : TK_IgnoreUnlessSpelledInSource; } void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) { + auto IsNotImplicitOrDeleted = anyOf(unless(isImplicit()), isDeleted()); + Finder->addMatcher( cxxRecordDecl( - eachOf(has(cxxDestructorDecl().bind("dtor")), - has(cxxConstructorDecl(isCopyConstructor()).bind("copy-ctor")), - has(cxxMethodDecl(isCopyAssignmentOperator()) + unless(isImplicit()), + eachOf(has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")), + has(cxxConstructorDecl(isCopyConstructor(), + IsNotImplicitOrDeleted) + .bind("copy-ctor")), + has(cxxMethodDecl(isCopyAssignmentOperator(), + IsNotImplicitOrDeleted) .bind("copy-assign")), - has(cxxConstructorDecl(isMoveConstructor()).bind("move-ctor")), - has(cxxMethodDecl(isMoveAssignmentOperator()) + has(cxxConstructorDecl(isMoveConstructor(), + IsNotImplicitOrDeleted) + .bind("move-ctor")), + has(cxxMethodDecl(isMoveAssignmentOperator(), + IsNotImplicitOrDeleted) .bind("move-assign")))) .bind("class-def"), this); @@ -127,7 +146,8 @@ void SpecialMemberFunctionsCheck::check( for (const auto &KV : Matchers) if (const auto *MethodDecl = Result.Nodes.getNodeAs(KV.first)) { - StoreMember({KV.second, MethodDecl->isDeleted()}); + StoreMember( + {KV.second, MethodDecl->isDeleted(), MethodDecl->isImplicit()}); } } @@ -144,7 +164,13 @@ void SpecialMemberFunctionsCheck::checkForMissingMembers( auto HasMember = [&](SpecialMemberFunctionKind Kind) { return llvm::any_of(DefinedMembers, [Kind](const auto &Data) { - return Data.FunctionKind == Kind; + return Data.FunctionKind == Kind && !Data.IsImplicit; + }); + }; + + auto HasImplicitDeletedMember = [&](SpecialMemberFunctionKind Kind) { + return llvm::any_of(DefinedMembers, [Kind](const auto &Data) { + return Data.FunctionKind == Kind && Data.IsImplicit && Data.IsDeleted; }); }; @@ -154,9 +180,17 @@ void SpecialMemberFunctionsCheck::checkForMissingMembers( }); }; - auto RequireMember = [&](SpecialMemberFunctionKind Kind) { - if (!HasMember(Kind)) - MissingMembers.push_back(Kind); + auto RequireMembers = [&](SpecialMemberFunctionKind Kind1, + SpecialMemberFunctionKind Kind2) { + if (AllowImplicitlyDeletedCopyOrMove && HasImplicitDeletedMember(Kind1) && + HasImplicitDeletedMember(Kind2)) + return; + + if (!HasMember(Kind1)) + MissingMembers.push_back(Kind1); + + if (!HasMember(Kind2)) + MissingMembers.push_back(Kind2); }; bool RequireThree = @@ -180,8 +214,8 @@ void SpecialMemberFunctionsCheck::checkForMissingMembers( !HasMember(SpecialMemberFunctionKind::NonDefaultDestructor)) MissingMembers.push_back(SpecialMemberFunctionKind::Destructor); - RequireMember(SpecialMemberFunctionKind::CopyConstructor); - RequireMember(SpecialMemberFunctionKind::CopyAssignment); + RequireMembers(SpecialMemberFunctionKind::CopyConstructor, + SpecialMemberFunctionKind::CopyAssignment); } if (RequireFive && @@ -189,14 +223,16 @@ void SpecialMemberFunctionsCheck::checkForMissingMembers( (IsDeleted(SpecialMemberFunctionKind::CopyConstructor) && IsDeleted(SpecialMemberFunctionKind::CopyAssignment)))) { assert(RequireThree); - RequireMember(SpecialMemberFunctionKind::MoveConstructor); - RequireMember(SpecialMemberFunctionKind::MoveAssignment); + RequireMembers(SpecialMemberFunctionKind::MoveConstructor, + SpecialMemberFunctionKind::MoveAssignment); } if (!MissingMembers.empty()) { llvm::SmallVector DefinedMemberKinds; - llvm::transform(DefinedMembers, std::back_inserter(DefinedMemberKinds), - [](const auto &Data) { return Data.FunctionKind; }); + for (const auto &Data : DefinedMembers) { + if (!Data.IsImplicit) + DefinedMemberKinds.push_back(Data.FunctionKind); + } diag(ID.first, "class '%0' defines %1 but does not define %2") << ID.second << cppcoreguidelines::join(DefinedMemberKinds, " and ") << cppcoreguidelines::join(MissingMembers, " or "); diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h index 6042f0fd6cb05432f378744a944001b5175f967a..9ebc03ed2fa139dd67bf33dcf4c90fbf79f48360 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h @@ -30,9 +30,8 @@ public: void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void onEndOfTranslationUnit() override; - std::optional getCheckTraversalKind() const override { - return TK_IgnoreUnlessSpelledInSource; - } + std::optional getCheckTraversalKind() const override; + enum class SpecialMemberFunctionKind : uint8_t { Destructor, DefaultDestructor, @@ -46,6 +45,7 @@ public: struct SpecialMemberFunctionData { SpecialMemberFunctionKind FunctionKind; bool IsDeleted; + bool IsImplicit = false; bool operator==(const SpecialMemberFunctionData &Other) const { return (Other.FunctionKind == FunctionKind) && @@ -67,6 +67,7 @@ private: const bool AllowMissingMoveFunctions; const bool AllowSoleDefaultDtor; const bool AllowMissingMoveFunctionsWhenCopyIsDeleted; + const bool AllowImplicitlyDeletedCopyOrMove; ClassDefiningSpecialMembersMap ClassWithSpecialMembers; }; diff --git a/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp index 3f1d2f9f58099112c0d65a6c87cd7e3f215a1be7..c2d9286312dc4ac26ee5ee158ac4b253470ccb5a 100644 --- a/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UnusedParametersCheck.cpp @@ -192,9 +192,7 @@ void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) { // In non-strict mode ignore function definitions with empty bodies // (constructor initializer counts for non-empty body). - if (StrictMode || - (Function->getBody()->child_begin() != - Function->getBody()->child_end()) || + if (StrictMode || !Function->getBody()->children().empty() || (isa(Function) && cast(Function)->getNumCtorInitializers() > 0)) warnOnUnusedParameter(Result, Function, I); diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 8005d6e91c060c6e9b0e08d8f11cffb094c5fa4e..576805c4c7f1811cca003beca447dd4ab4d8608d 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -41,6 +41,7 @@ add_clang_library(clangTidyModernizeModule UseNullptrCheck.cpp UseOverrideCheck.cpp UseStartsEndsWithCheck.cpp + UseStdFormatCheck.cpp UseStdNumbersCheck.cpp UseStdPrintCheck.cpp UseTrailingReturnTypeCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/MinMaxUseInitializerListCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MinMaxUseInitializerListCheck.cpp index 45f7700463d5708673a78d5caf31cea9a3d0dd17..418699ffbc4d1a636f8c9702b35f20156390f5c3 100644 --- a/clang-tools-extra/clang-tidy/modernize/MinMaxUseInitializerListCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/MinMaxUseInitializerListCheck.cpp @@ -129,17 +129,17 @@ generateReplacements(const MatchFinder::MatchResult &Match, continue; } + // if the nested call is not the same as the top call + if (InnerCall->getDirectCallee()->getQualifiedNameAsString() != + TopCall->getDirectCallee()->getQualifiedNameAsString()) + continue; + const FindArgsResult InnerResult = findArgs(InnerCall); // if the nested call doesn't have arguments skip it if (!InnerResult.First || !InnerResult.Last) continue; - // if the nested call is not the same as the top call - if (InnerCall->getDirectCallee()->getQualifiedNameAsString() != - TopCall->getDirectCallee()->getQualifiedNameAsString()) - continue; - // if the nested call doesn't have the same compare function if ((Result.Compare || InnerResult.Compare) && !utils::areStatementsIdentical(Result.Compare, InnerResult.Compare, diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 776558433c5baa492e8f33637becee5d85775de1..b9c7a2dc383e88360ea3fa6483c6c8c1f9ac7d63 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -42,6 +42,7 @@ #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" #include "UseStartsEndsWithCheck.h" +#include "UseStdFormatCheck.h" #include "UseStdNumbersCheck.h" #include "UseStdPrintCheck.h" #include "UseTrailingReturnTypeCheck.h" @@ -76,6 +77,7 @@ public: "modernize-use-designated-initializers"); CheckFactories.registerCheck( "modernize-use-starts-ends-with"); + CheckFactories.registerCheck("modernize-use-std-format"); CheckFactories.registerCheck( "modernize-use-std-numbers"); CheckFactories.registerCheck("modernize-use-std-print"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp index 6d7d1d6b87c60acb66b3de1001c3901d6a5a78a3..7a021fe14436a102037f442433db810ff0ef3366 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseConstraintsCheck.cpp @@ -41,6 +41,8 @@ AST_MATCHER(FunctionDecl, hasOtherDeclarations) { void UseConstraintsCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( functionTemplateDecl( + // Skip external libraries included as system headers + unless(isExpansionInSystemHeader()), has(functionDecl(unless(hasOtherDeclarations()), isDefinition(), hasReturnTypeLoc(typeLoc().bind("return"))) .bind("function"))) @@ -57,6 +59,8 @@ matchEnableIfSpecializationImplTypename(TypeLoc TheType) { return std::nullopt; } TheType = Dep.getQualifierLoc().getTypeLoc(); + if (TheType.isNull()) + return std::nullopt; } if (const auto SpecializationLoc = @@ -254,7 +258,7 @@ findInsertionForConstraint(const FunctionDecl *Function, ASTContext &Context) { return utils::lexer::findPreviousTokenKind(Init->getSourceLocation(), SM, LangOpts, tok::colon); } - if (Constructor->init_begin() != Constructor->init_end()) + if (!Constructor->inits().empty()) return std::nullopt; } if (Function->isDeleted()) { diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cef21f1318a2a96fa11a2b5d366dc74b723e824 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp @@ -0,0 +1,107 @@ +//===--- UseStdFormatCheck.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 "UseStdFormatCheck.h" +#include "../utils/FormatStringConverter.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/FixIt.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +namespace { +AST_MATCHER(StringLiteral, isOrdinary) { return Node.isOrdinary(); } +} // namespace + +UseStdFormatCheck::UseStdFormatCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + StrictMode(Options.getLocalOrGlobal("StrictMode", false)), + StrFormatLikeFunctions(utils::options::parseStringList( + Options.get("StrFormatLikeFunctions", ""))), + ReplacementFormatFunction( + Options.get("ReplacementFormatFunction", "std::format")), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()), + MaybeHeaderToInclude(Options.get("FormatHeader")) { + if (StrFormatLikeFunctions.empty()) + StrFormatLikeFunctions.push_back("absl::StrFormat"); + + if (!MaybeHeaderToInclude && ReplacementFormatFunction == "std::format") + MaybeHeaderToInclude = ""; +} + +void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(argumentCountAtLeast(1), + hasArgument(0, stringLiteral(isOrdinary())), + callee(functionDecl(unless(cxxMethodDecl()), + matchers::matchesAnyListedName( + StrFormatLikeFunctions)) + .bind("func_decl"))) + .bind("strformat"), + this); +} + +void UseStdFormatCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + using utils::options::serializeStringList; + Options.store(Opts, "StrictMode", StrictMode); + Options.store(Opts, "StrFormatLikeFunctions", + serializeStringList(StrFormatLikeFunctions)); + Options.store(Opts, "ReplacementFormatFunction", ReplacementFormatFunction); + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); + if (MaybeHeaderToInclude) + Options.store(Opts, "FormatHeader", *MaybeHeaderToInclude); +} + +void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) { + const unsigned FormatArgOffset = 0; + const auto *OldFunction = Result.Nodes.getNodeAs("func_decl"); + const auto *StrFormat = Result.Nodes.getNodeAs("strformat"); + + utils::FormatStringConverter::Configuration ConverterConfig; + ConverterConfig.StrictMode = StrictMode; + utils::FormatStringConverter Converter(Result.Context, StrFormat, + FormatArgOffset, ConverterConfig, + getLangOpts()); + const Expr *StrFormatCall = StrFormat->getCallee(); + if (!Converter.canApply()) { + diag(StrFormat->getBeginLoc(), + "unable to use '%0' instead of %1 because %2") + << StrFormatCall->getSourceRange() << ReplacementFormatFunction + << OldFunction->getIdentifier() + << Converter.conversionNotPossibleReason(); + return; + } + + DiagnosticBuilder Diag = + diag(StrFormatCall->getBeginLoc(), "use '%0' instead of %1") + << ReplacementFormatFunction << OldFunction->getIdentifier(); + Diag << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(StrFormatCall->getSourceRange()), + ReplacementFormatFunction); + Converter.applyFixes(Diag, *Result.SourceManager); + + if (MaybeHeaderToInclude) + Diag << IncludeInserter.createIncludeInsertion( + Result.Context->getSourceManager().getFileID( + StrFormatCall->getBeginLoc()), + *MaybeHeaderToInclude); +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..b59a4708c6e4bc6d492581a90ab50a6bb10a715a --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h @@ -0,0 +1,51 @@ +//===--- UseStdFormatCheck.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_MODERNIZE_USESTDFORMATCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" + +namespace clang::tidy::modernize { + +/// Converts calls to absl::StrFormat, or other functions via configuration +/// options, to C++20's std::format, or another function via a configuration +/// option, modifying the format string appropriately and removing +/// now-unnecessary calls to std::string::c_str() and std::string::data(). +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-format.html +class UseStdFormatCheck : public ClangTidyCheck { +public: + UseStdFormatCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + if (ReplacementFormatFunction == "std::format") + return LangOpts.CPlusPlus20; + return LangOpts.CPlusPlus; + } + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + bool StrictMode; + std::vector StrFormatLikeFunctions; + StringRef ReplacementFormatFunction; + utils::IncludeInserter IncludeInserter; + std::optional MaybeHeaderToInclude; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp index aa60c904a363dacb64bb0f15551171dafcc94d32..ff990feadc0c1d944f786b15eecd3f9d35a9a5be 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp @@ -129,8 +129,11 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) { FormatArgOffset = 1; } + utils::FormatStringConverter::Configuration ConverterConfig; + ConverterConfig.StrictMode = StrictMode; + ConverterConfig.AllowTrailingNewlineRemoval = true; utils::FormatStringConverter Converter( - Result.Context, Printf, FormatArgOffset, StrictMode, getLangOpts()); + Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts()); const Expr *PrintfCall = Printf->getCallee(); const StringRef ReplacementFunction = Converter.usePrintNewlineFunction() ? ReplacementPrintlnFunction diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index f82f4417141d3df287dfafb5182715aa09be7352..7388f20ef288eb68c877bf26a2130a17edd8967b 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -53,6 +53,7 @@ Configuration files: Checks - Same as '--checks'. Additionally, the list of globs can be specified as a list instead of a string. + ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'. ExtraArgs - Same as '--extra-args'. ExtraArgsBefore - Same as '--extra-args-before'. FormatStyle - Same as '--format-style'. @@ -132,6 +133,20 @@ option in .clang-tidy file, if any. cl::init(""), cl::cat(ClangTidyCategory)); +static cl::opt ExcludeHeaderFilter("exclude-header-filter", + desc(R"( +Regular expression matching the names of the +headers to exclude diagnostics from. Diagnostics +from the main file of each translation unit are +always displayed. +Must be used together with --header-filter. +Can be used together with -line-filter. +This option overrides the 'ExcludeHeaderFilterRegex' +option in .clang-tidy file, if any. +)"), + cl::init(""), + cl::cat(ClangTidyCategory)); + static cl::opt SystemHeaders("system-headers", desc(R"( Display the errors from system headers. This option overrides the 'SystemHeaders' option @@ -353,6 +368,7 @@ static std::unique_ptr createOptionsProvider( DefaultOptions.Checks = DefaultChecks; DefaultOptions.WarningsAsErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; + DefaultOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; DefaultOptions.FormatStyle = FormatStyle; DefaultOptions.User = llvm::sys::Process::GetEnv("USER"); @@ -367,6 +383,8 @@ static std::unique_ptr createOptionsProvider( OverrideOptions.WarningsAsErrors = WarningsAsErrors; if (HeaderFilter.getNumOccurrences() > 0) OverrideOptions.HeaderFilterRegex = HeaderFilter; + if (ExcludeHeaderFilter.getNumOccurrences() > 0) + OverrideOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter; if (SystemHeaders.getNumOccurrences() > 0) OverrideOptions.SystemHeaders = SystemHeaders; if (FormatStyle.getNumOccurrences() > 0) diff --git a/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py b/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py index 1bd4a5b283091c0ebeadf08ae56cf571362c1519..4dd20bec81d3be95722f409e541735a52f60674b 100755 --- a/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py +++ b/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py @@ -106,11 +106,14 @@ def get_tidy_invocation( use_color, plugins, warnings_as_errors, + exclude_header_filter, ): """Gets a command line for clang-tidy.""" start = [clang_tidy_binary] if allow_enabling_alpha_checkers: start.append("-allow-enabling-analyzer-alpha-checkers") + if exclude_header_filter is not None: + start.append("--exclude-header-filter=" + exclude_header_filter) if header_filter is not None: start.append("-header-filter=" + header_filter) if line_filter is not None: @@ -228,6 +231,7 @@ def run_tidy(args, clang_tidy_binary, tmpdir, build_path, queue, lock, failed_fi args.use_color, args.plugins, args.warnings_as_errors, + args.exclude_header_filter, ) proc = subprocess.Popen( @@ -292,6 +296,14 @@ def main(): "-config option after reading specified config file. " "Use either -config-file or -config, not both.", ) + parser.add_argument( + "-exclude-header-filter", + default=None, + help="Regular expression matching the names of the " + "headers to exclude diagnostics from. Diagnostics from " + "the main file of each translation unit are always " + "displayed.", + ) parser.add_argument( "-header-filter", default=None, @@ -450,6 +462,7 @@ def main(): args.use_color, args.plugins, args.warnings_as_errors, + args.exclude_header_filter, ) invocation.append("-list-checks") invocation.append("-") diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp index ad10f745b6acfb704ac5f7dfefd8ac2c1781d365..845e71c5003b80bbdb2cfc8ecb8bb9751f04dd2f 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp @@ -198,10 +198,11 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) { FormatStringConverter::FormatStringConverter(ASTContext *ContextIn, const CallExpr *Call, unsigned FormatArgOffset, - bool StrictMode, + const Configuration ConfigIn, const LangOptions &LO) - : Context(ContextIn), - CastMismatchedIntegerTypes(castMismatchedIntegerTypes(Call, StrictMode)), + : Context(ContextIn), Config(ConfigIn), + CastMismatchedIntegerTypes( + castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)), Args(Call->getArgs()), NumArgs(Call->getNumArgs()), ArgsOffset(FormatArgOffset + 1), LangOpts(LO) { assert(ArgsOffset <= NumArgs); @@ -627,9 +628,12 @@ void FormatStringConverter::finalizeFormatText() { // It's clearer to convert printf("Hello\r\n"); to std::print("Hello\r\n") // than to std::println("Hello\r"); - if (StringRef(StandardFormatString).ends_with("\\n") && - !StringRef(StandardFormatString).ends_with("\\\\n") && - !StringRef(StandardFormatString).ends_with("\\r\\n")) { + // Use StringRef until C++20 std::string::ends_with() is available. + const auto StandardFormatStringRef = StringRef(StandardFormatString); + if (Config.AllowTrailingNewlineRemoval && + StandardFormatStringRef.ends_with("\\n") && + !StandardFormatStringRef.ends_with("\\\\n") && + !StandardFormatStringRef.ends_with("\\r\\n")) { UsePrintNewlineFunction = true; FormatStringNeededRewriting = true; StandardFormatString.erase(StandardFormatString.end() - 2, diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h index 1949870f62ed68c181dbeddfecf8f2644a1e8804..1109a0b602262fe9793de1e30423aee2565cdbd5 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h @@ -32,8 +32,14 @@ class FormatStringConverter public: using ConversionSpecifier = clang::analyze_format_string::ConversionSpecifier; using PrintfSpecifier = analyze_printf::PrintfSpecifier; + + struct Configuration { + bool StrictMode = false; + bool AllowTrailingNewlineRemoval = false; + }; + FormatStringConverter(ASTContext *Context, const CallExpr *Call, - unsigned FormatArgOffset, bool StrictMode, + unsigned FormatArgOffset, Configuration Config, const LangOptions &LO); bool canApply() const { return ConversionNotPossibleReason.empty(); } @@ -45,6 +51,7 @@ public: private: ASTContext *Context; + const Configuration Config; const bool CastMismatchedIntegerTypes; const Expr *const *Args; const unsigned NumArgs; diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index 1b86ea19cf28daa75cea3c9deb39e4191d797c22..fda1e5fdf8d82c944ae0e628158fd0554b8248a0 100644 --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -50,16 +50,11 @@ getTemplateSpecializationArgLocs(const NamedDecl &ND) { if (const ASTTemplateArgumentListInfo *Args = Func->getTemplateSpecializationArgsAsWritten()) return Args->arguments(); - } else if (auto *Cls = - llvm::dyn_cast(&ND)) { + } else if (auto *Cls = llvm::dyn_cast(&ND)) { if (auto *Args = Cls->getTemplateArgsAsWritten()) return Args->arguments(); - } else if (auto *Var = - llvm::dyn_cast(&ND)) { - if (auto *Args = Var->getTemplateArgsAsWritten()) - return Args->arguments(); } else if (auto *Var = llvm::dyn_cast(&ND)) { - if (auto *Args = Var->getTemplateArgsInfo()) + if (auto *Args = Var->getTemplateArgsAsWritten()) return Args->arguments(); } // We return std::nullopt for ClassTemplateSpecializationDecls because it does @@ -270,22 +265,10 @@ std::string printTemplateSpecializationArgs(const NamedDecl &ND) { getTemplateSpecializationArgLocs(ND)) { printTemplateArgumentList(OS, *Args, Policy); } else if (auto *Cls = llvm::dyn_cast(&ND)) { - if (const TypeSourceInfo *TSI = Cls->getTypeAsWritten()) { - // ClassTemplateSpecializationDecls do not contain - // TemplateArgumentTypeLocs, they only have TemplateArgumentTypes. So we - // create a new argument location list from TypeSourceInfo. - auto STL = TSI->getTypeLoc().getAs(); - llvm::SmallVector ArgLocs; - ArgLocs.reserve(STL.getNumArgs()); - for (unsigned I = 0; I < STL.getNumArgs(); ++I) - ArgLocs.push_back(STL.getArgLoc(I)); - printTemplateArgumentList(OS, ArgLocs, Policy); - } else { - // FIXME: Fix cases when getTypeAsWritten returns null inside clang AST, - // e.g. friend decls. Currently we fallback to Template Arguments without - // location information. - printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy); - } + // FIXME: Fix cases when getTypeAsWritten returns null inside clang AST, + // e.g. friend decls. Currently we fallback to Template Arguments without + // location information. + printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy); } OS.flush(); return TemplateArgs; @@ -453,10 +436,12 @@ bool hasReservedScope(const DeclContext &DC) { } QualType declaredType(const TypeDecl *D) { + ASTContext &Context = D->getASTContext(); if (const auto *CTSD = llvm::dyn_cast(D)) - if (const auto *TSI = CTSD->getTypeAsWritten()) - return TSI->getType(); - return D->getASTContext().getTypeDeclType(D); + if (const auto *Args = CTSD->getTemplateArgsAsWritten()) + return Context.getTemplateSpecializationType( + TemplateName(CTSD->getSpecializedTemplate()), Args->arguments()); + return Context.getTypeDeclType(D); } namespace { diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index 08f99e11ac9be183a832c67fa649088aa41bb8a8..eb025f21f3616126ee7e0a22d0019f95bdae0073 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -693,17 +693,22 @@ public: return true; } + bool + VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D) { + if (auto *Args = D->getTemplateArgsAsWritten()) + H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc()); + return true; + } + bool VisitClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl *D) { if (auto *TPL = D->getTemplateParameters()) H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc()); - if (auto *Args = D->getTemplateArgsAsWritten()) - H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc()); return true; } bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *D) { - if (auto *Args = D->getTemplateArgsInfo()) + if (auto *Args = D->getTemplateArgsAsWritten()) H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc()); return true; } @@ -712,8 +717,6 @@ public: VarTemplatePartialSpecializationDecl *D) { if (auto *TPL = D->getTemplateParameters()) H.addAngleBracketTokens(TPL->getLAngleLoc(), TPL->getRAngleLoc()); - if (auto *Args = D->getTemplateArgsAsWritten()) - H.addAngleBracketTokens(Args->getLAngleLoc(), Args->getRAngleLoc()); return true; } diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp index 75b30e66d63761fd4bcbc007c97a1f299d947409..c0fc4453a3fccc61bb8abe75c7abf139b98e10ca 100644 --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -1090,11 +1090,10 @@ llvm::Expected rename(const RenameInputs &RInputs) { return MainFileRenameEdit.takeError(); llvm::DenseSet RenamedRanges; - if (const auto *MD = dyn_cast(&RenameDecl)) { + if (!isa(RenameDecl)) { // TODO: Insert the ranges from the ObjCMethodDecl/ObjCMessageExpr selector // pieces which are being renamed. This will require us to make changes to // locateDeclAt to preserve this AST node. - } else { RenamedRanges.insert(CurrentIdentifier); } diff --git a/clang-tools-extra/clangd/test/infinite-instantiation.test b/clang-tools-extra/clangd/test/infinite-instantiation.test index 85a1b656f49086c7d463be50fdd2af808a0506cf..d379a9c2d523e6fe2287ec2357cd4c07c84ebc1e 100644 --- a/clang-tools-extra/clangd/test/infinite-instantiation.test +++ b/clang-tools-extra/clangd/test/infinite-instantiation.test @@ -1,5 +1,6 @@ -// RUN: cp %s %t.cpp -// RUN: not clangd -check=%t.cpp 2>&1 | FileCheck -strict-whitespace %s +// RUN: rm -rf %t.dir && mkdir -p %t.dir +// RUN: echo '[{"directory": "%/t.dir", "command": "clang -ftemplate-depth=100 -x c++ %s", "file": "%/s"}]' > %t.dir/compile_commands.json +// RUN: not clangd --compile-commands-dir=%t.dir -check=%s 2>&1 | FileCheck -strict-whitespace %s // CHECK: [template_recursion_depth_exceeded] diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp index db516a1f62a35773b9e46932fec066984afb8a93..aaaf758e72236b556ac522105bb2deabba932fe2 100644 --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -589,6 +589,12 @@ TEST(SelectionTest, CommonAncestor) { auto x = [[ns::^C]]; )cpp", "ConceptReference"}, + {R"cpp( + template + concept D = true; + template void g(D<[[^T]]> auto abc) {} + )cpp", + "TemplateTypeParmTypeLoc"}, }; for (const Case &C : Cases) { diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8183d394cf4253719d1d8fbb7a929ab5ab05f8be..6a9892bada9155e4ee87ceef384e1937f0e2cfa9 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -94,6 +94,8 @@ Improvements to clang-query from an external file, allowing the cost of reading the compilation database and building the AST to be imposed just once for faster prototyping. +- Removed support for ``enable output srcloc``. Fixes #GH82591 + Improvements to clang-rename ---------------------------- @@ -115,6 +117,9 @@ Improvements to clang-tidy - Fixed `--verify-config` option not properly parsing checks when using the literal operator in the `.clang-tidy` config. +- Added argument `--exclude-header-filter` and config option `ExcludeHeaderFilterRegex` + to exclude headers from analysis via a RegEx. + New checks ^^^^^^^^^^ @@ -150,6 +155,15 @@ New checks Finds initializer lists for aggregate types that could be written as designated initializers instead. +- New :doc:`modernize-use-std-format + ` check. + + Converts calls to ``absl::StrFormat``, or other functions via + configuration options, to C++20's ``std::format``, or another function + via a configuration option, modifying the format string appropriately and + removing now-unnecessary calls to ``std::string::c_str()`` and + ``std::string::data()``. + - New :doc:`readability-enum-initial-value ` check. @@ -204,6 +218,10 @@ Changes in existing checks eliminating false positives resulting from direct usage of bitwise operators within parentheses. +- Improved :doc:`bugprone-optional-value-conversion + ` check by eliminating + false positives resulting from use of optionals in unevaluated context. + - Improved :doc:`bugprone-suspicious-include ` check by replacing the local options `HeaderFileExtensions` and `ImplementationFileExtensions` by the @@ -246,6 +264,12 @@ Changes in existing checks `. Fixed incorrect hints when using list-initialization. +- Improved :doc:`cppcoreguidelines-special-member-functions + ` check with a + new option `AllowImplicitlyDeletedCopyOrMove`, which removes the requirement + for explicit copy or move special member functions when they are already + implicitly deleted. + - Improved :doc:`google-build-namespaces ` check by replacing the local option `HeaderFileExtensions` by the global option of the same name. @@ -306,6 +330,10 @@ Changes in existing checks don't remove parentheses used in ``sizeof`` calls when they have array index accesses as arguments. +- Improved :doc:`modernize-use-constraints + ` check by fixing a crash that + occurred in some scenarios and excluding system headers from analysis. + - Improved :doc:`modernize-use-nullptr ` check to include support for C23, which also has introduced the ``nullptr`` keyword. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/special-member-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/special-member-functions.rst index 176956d6cb2bd725e7ff96d83de36a6eff80b9ce..20f898fdab93053e803b186d346a2995a4a26e8b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/special-member-functions.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/special-member-functions.rst @@ -45,9 +45,10 @@ Options .. option:: AllowMissingMoveFunctions - When set to `true` (default is `false`), this check doesn't flag classes which define no move - operations at all. It still flags classes which define only one of either - move constructor or move assignment operator. With this option enabled, the following class won't be flagged: + When set to `true` (default is `false`), this check doesn't flag classes + which define no move operations at all. It still flags classes which define + only one of either move constructor or move assignment operator. With this + option enabled, the following class won't be flagged: .. code-block:: c++ @@ -59,10 +60,11 @@ Options .. option:: AllowMissingMoveFunctionsWhenCopyIsDeleted - When set to `true` (default is `false`), this check doesn't flag classes which define deleted copy - operations but don't define move operations. This flag is related to Google C++ Style Guide - https://google.github.io/styleguide/cppguide.html#Copyable_Movable_Types. With this option enabled, the - following class won't be flagged: + When set to `true` (default is `false`), this check doesn't flag classes + which define deleted copy operations but don't define move operations. This + flag is related to Google C++ Style Guide `Copyable and Movable Types + `_. + With this option enabled, the following class won't be flagged: .. code-block:: c++ @@ -71,3 +73,15 @@ Options A& operator=(const A&) = delete; ~A(); }; + +.. option:: AllowImplicitlyDeletedCopyOrMove + + When set to `true` (default is `false`), this check doesn't flag classes + which implicitly delete copy or move operations. + With this option enabled, the following class won't be flagged: + + .. code-block:: c++ + + struct A : boost::noncopyable { + ~A() { std::cout << "dtor\n"; } + }; diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 046a5ff57ad1c9c6800d7dddfe39e359d2c2bbf1..85e4f0352ac22b2b4e23cc36eb6b14814b48a08b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -300,6 +300,7 @@ Clang-Tidy Checks :doc:`modernize-use-nullptr `, "Yes" :doc:`modernize-use-override `, "Yes" :doc:`modernize-use-starts-ends-with `, "Yes" + :doc:`modernize-use-std-format `, "Yes" :doc:`modernize-use-std-numbers `, "Yes" :doc:`modernize-use-std-print `, "Yes" :doc:`modernize-use-trailing-return-type `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-constraints.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-constraints.rst index be62dd5823d552ce8d46ff3cf4765ca41ae87aa3..a8b31b80e580b0679f272d6e7ffd38bf2e05a429 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-constraints.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-constraints.rst @@ -68,3 +68,7 @@ The tool will replace the above code with, // The tool will not emit a diagnostic or attempt to replace the code. template = 0> struct my_class {}; + +.. note:: + + System headers are not analyzed by this check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst new file mode 100644 index 0000000000000000000000000000000000000000..a1599f0fc58fe68df702d446ffd6cb6523e084ba --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst @@ -0,0 +1,84 @@ +.. title:: clang-tidy - modernize-use-std-format + +modernize-use-std-format +======================== + +Converts calls to ``absl::StrFormat``, or other functions via +configuration options, to C++20's ``std::format``, or another function +via a configuration option, modifying the format string appropriately and +removing now-unnecessary calls to ``std::string::c_str()`` and +``std::string::data()``. + +For example, it turns lines like + +.. code-block:: c++ + + return absl::StrFormat("The %s is %3d", description.c_str(), value); + +into: + +.. code-block:: c++ + + return std::format("The {} is {:3}", description, value); + +The check uses the same format-string-conversion algorithm as +`modernize-use-std-print <../modernize/use-std-print.html>`_ and its +shortcomings are described in the documentation for that check. + +Options +------- + +.. option:: StrictMode + + When `true`, the check will add casts when converting from variadic + functions and printing signed or unsigned integer types (including + fixed-width integer types from ````, ``ptrdiff_t``, ``size_t`` + and ``ssize_t``) as the opposite signedness to ensure that the output + would matches that of a simple wrapper for ``std::sprintf`` that + accepted a C-style variable argument list. For example, with + `StrictMode` enabled, + + .. code-block:: c++ + + extern std::string strprintf(const char *format, ...); + int i = -42; + unsigned int u = 0xffffffff; + return strprintf("%d %u\n", i, u); + + would be converted to + + .. code-block:: c++ + + return std::format("{} {}\n", static_cast(i), static_cast(u)); + + to ensure that the output will continue to be the unsigned representation + of -42 and the signed representation of 0xffffffff (often 4294967254 + and -1 respectively). When `false` (which is the default), these casts + will not be added which may cause a change in the output. Note that this + option makes no difference for the default value of + `StrFormatLikeFunctions` since ``absl::StrFormat`` takes a function + parameter pack and is not a variadic function. + +.. option:: StrFormatLikeFunctions + + A semicolon-separated list of (fully qualified) function names to + replace, with the requirement that the first parameter contains the + printf-style format string and the arguments to be formatted follow + immediately afterwards. The default value for this option is + `absl::StrFormat`. + +.. option:: ReplacementFormatFunction + + The function that will be used to replace the function set by the + `StrFormatLikeFunctions` option rather than the default + `std::format`. It is expected that the function provides an interface + that is compatible with ``std::format``. A suitable candidate would be + `fmt::format`. + +.. option:: FormatHeader + + The header that must be included for the declaration of + `ReplacementFormatFunction` so that a ``#include`` directive can be added if + required. If `ReplacementFormatFunction` is `std::format` then this option will + default to ````, otherwise this option will default to nothing + and no ``#include`` directive will be added. diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst index 852566f26672385553b1445d0863c640a97f10a6..9ccacefa3c2c5999bfdff1a874d74699144833a0 100644 --- a/clang-tools-extra/docs/clang-tidy/index.rst +++ b/clang-tools-extra/docs/clang-tidy/index.rst @@ -116,122 +116,130 @@ An overview of all the command-line options: Generic Options: - --help - Display available options (--help-hidden for more) - --help-list - Display list of available options (--help-list-hidden for more) - --version - Display the version of this program + --help - Display available options (--help-hidden for more) + --help-list - Display list of available options (--help-list-hidden for more) + --version - Display the version of this program clang-tidy options: - --checks= - Comma-separated list of globs with optional '-' - prefix. Globs are processed in order of - appearance in the list. Globs without '-' - prefix add checks with matching names to the - set, globs with the '-' prefix remove checks - with matching names from the set of enabled - checks. This option's value is appended to the - value of the 'Checks' option in .clang-tidy - file, if any. - --config= - Specifies a configuration in YAML/JSON format: - -config="{Checks: '*', - CheckOptions: {x: y}}" - When the value is empty, clang-tidy will - attempt to find a file named .clang-tidy for - each source file in its parent directories. - --config-file= - Specify the path of .clang-tidy or custom config file: - e.g. --config-file=/some/path/myTidyConfigFile - This option internally works exactly the same way as - --config option after reading specified config file. - Use either --config-file or --config, not both. - --dump-config - Dumps configuration in the YAML format to - stdout. This option can be used along with a - file name (and '--' if the file is outside of a - project with configured compilation database). - The configuration used for this file will be - printed. - Use along with -checks=* to include - configuration of all checks. - --enable-check-profile - Enable per-check timing profiles, and print a - report to stderr. - --enable-module-headers-parsing - Enables preprocessor-level module header parsing - for C++20 and above, empowering specific checks - to detect macro definitions within modules. This - feature may cause performance and parsing issues - and is therefore considered experimental. - --explain-config - For each enabled check explains, where it is - enabled, i.e. in clang-tidy binary, command - line or a specific configuration file. - --export-fixes= - YAML file to store suggested fixes in. The - stored fixes can be applied to the input source - code with clang-apply-replacements. - --extra-arg= - Additional argument to append to the compiler command line - --extra-arg-before= - Additional argument to prepend to the compiler command line - --fix - Apply suggested fixes. Without -fix-errors - clang-tidy will bail out if any compilation - errors were found. - --fix-errors - Apply suggested fixes even if compilation - errors were found. If compiler errors have - attached fix-its, clang-tidy will apply them as - well. - --fix-notes - If a warning has no fix, but a single fix can - be found through an associated diagnostic note, - apply the fix. - Specifying this flag will implicitly enable the - '--fix' flag. - --format-style= - Style for formatting code around applied fixes: - - 'none' (default) turns off formatting - - 'file' (literally 'file', not a placeholder) - uses .clang-format file in the closest parent - directory - - '{ }' specifies options inline, e.g. - -format-style='{BasedOnStyle: llvm, IndentWidth: 8}' - - 'llvm', 'google', 'webkit', 'mozilla' - See clang-format documentation for the up-to-date - information about formatting styles and options. - This option overrides the 'FormatStyle` option in - .clang-tidy file, if any. - --header-filter= - Regular expression matching the names of the - headers to output diagnostics from. Diagnostics - from the main file of each translation unit are - always displayed. - Can be used together with -line-filter. - This option overrides the 'HeaderFilterRegex' - option in .clang-tidy file, if any. - --line-filter= - List of files with line ranges to filter the - warnings. Can be used together with - -header-filter. The format of the list is a - JSON array of objects: - [ - {"name":"file1.cpp","lines":[[1,3],[5,7]]}, - {"name":"file2.h"} - ] - --list-checks - List all enabled checks and exit. Use with - -checks=* to list all available checks. - --load= - Load the specified plugin - -p - Build path - --quiet - Run clang-tidy in quiet mode. This suppresses - printing statistics about ignored warnings and - warnings treated as errors if the respective - options are specified. - --store-check-profile= - By default reports are printed in tabulated - format to stderr. When this option is passed, - these per-TU profiles are instead stored as JSON. - --system-headers - Display the errors from system headers. - This option overrides the 'SystemHeaders' option - in .clang-tidy file, if any. - --use-color - Use colors in diagnostics. If not set, colors - will be used if the terminal connected to - standard output supports colors. - This option overrides the 'UseColor' option in - .clang-tidy file, if any. - --verify-config - Check the config files to ensure each check and - option is recognized. - --vfsoverlay= - Overlay the virtual filesystem described by file - over the real file system. - --warnings-as-errors= - Upgrades warnings to errors. Same format as - '-checks'. - This option's value is appended to the value of - the 'WarningsAsErrors' option in .clang-tidy - file, if any. + --checks= - Comma-separated list of globs with optional '-' + prefix. Globs are processed in order of + appearance in the list. Globs without '-' + prefix add checks with matching names to the + set, globs with the '-' prefix remove checks + with matching names from the set of enabled + checks. This option's value is appended to the + value of the 'Checks' option in .clang-tidy + file, if any. + --config= - Specifies a configuration in YAML/JSON format: + -config="{Checks: '*', + CheckOptions: {x: y}}" + When the value is empty, clang-tidy will + attempt to find a file named .clang-tidy for + each source file in its parent directories. + --config-file= - Specify the path of .clang-tidy or custom config file: + e.g. --config-file=/some/path/myTidyConfigFile + This option internally works exactly the same way as + --config option after reading specified config file. + Use either --config-file or --config, not both. + --dump-config - Dumps configuration in the YAML format to + stdout. This option can be used along with a + file name (and '--' if the file is outside of a + project with configured compilation database). + The configuration used for this file will be + printed. + Use along with -checks=* to include + configuration of all checks. + --enable-check-profile - Enable per-check timing profiles, and print a + report to stderr. + --enable-module-headers-parsing - Enables preprocessor-level module header parsing + for C++20 and above, empowering specific checks + to detect macro definitions within modules. This + feature may cause performance and parsing issues + and is therefore considered experimental. + --exclude-header-filter= - Regular expression matching the names of the + headers to exclude diagnostics from. Diagnostics + from the main file of each translation unit are + always displayed. + Must be used together with --header-filter. + Can be used together with -line-filter. + This option overrides the 'ExcludeHeaderFilterRegex' + option in .clang-tidy file, if any. + --explain-config - For each enabled check explains, where it is + enabled, i.e. in clang-tidy binary, command + line or a specific configuration file. + --export-fixes= - YAML file to store suggested fixes in. The + stored fixes can be applied to the input source + code with clang-apply-replacements. + --extra-arg= - Additional argument to append to the compiler command line + --extra-arg-before= - Additional argument to prepend to the compiler command line + --fix - Apply suggested fixes. Without -fix-errors + clang-tidy will bail out if any compilation + errors were found. + --fix-errors - Apply suggested fixes even if compilation + errors were found. If compiler errors have + attached fix-its, clang-tidy will apply them as + well. + --fix-notes - If a warning has no fix, but a single fix can + be found through an associated diagnostic note, + apply the fix. + Specifying this flag will implicitly enable the + '--fix' flag. + --format-style= - Style for formatting code around applied fixes: + - 'none' (default) turns off formatting + - 'file' (literally 'file', not a placeholder) + uses .clang-format file in the closest parent + directory + - '{ }' specifies options inline, e.g. + -format-style='{BasedOnStyle: llvm, IndentWidth: 8}' + - 'llvm', 'google', 'webkit', 'mozilla' + See clang-format documentation for the up-to-date + information about formatting styles and options. + This option overrides the 'FormatStyle` option in + .clang-tidy file, if any. + --header-filter= - Regular expression matching the names of the + headers to output diagnostics from. Diagnostics + from the main file of each translation unit are + always displayed. + Can be used together with -line-filter. + This option overrides the 'HeaderFilterRegex' + option in .clang-tidy file, if any. + --line-filter= - List of files with line ranges to filter the + warnings. Can be used together with + -header-filter. The format of the list is a + JSON array of objects: + [ + {"name":"file1.cpp","lines":[[1,3],[5,7]]}, + {"name":"file2.h"} + ] + --list-checks - List all enabled checks and exit. Use with + -checks=* to list all available checks. + --load= - Load the specified plugin + -p - Build path + --quiet - Run clang-tidy in quiet mode. This suppresses + printing statistics about ignored warnings and + warnings treated as errors if the respective + options are specified. + --store-check-profile= - By default reports are printed in tabulated + format to stderr. When this option is passed, + these per-TU profiles are instead stored as JSON. + --system-headers - Display the errors from system headers. + This option overrides the 'SystemHeaders' option + in .clang-tidy file, if any. + --use-color - Use colors in diagnostics. If not set, colors + will be used if the terminal connected to + standard output supports colors. + This option overrides the 'UseColor' option in + .clang-tidy file, if any. + --verify-config - Check the config files to ensure each check and + option is recognized. + --vfsoverlay= - Overlay the virtual filesystem described by file + over the real file system. + --warnings-as-errors= - Upgrades warnings to errors. Same format as + '-checks'. + This option's value is appended to the value of + the 'WarningsAsErrors' option in .clang-tidy + file, if any. -p is used to read a compile command database. @@ -269,6 +277,7 @@ An overview of all the command-line options: Checks - Same as '--checks'. Additionally, the list of globs can be specified as a list instead of a string. + ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'. ExtraArgs - Same as '--extra-args'. ExtraArgsBefore - Same as '--extra-args-before'. FormatStyle - Same as '--format-style'. diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp index 878067aca0173feec649a575c865d0dbb241e860..f7cc9d19123635d9a5c3c79655ca4b0c6d038cb5 100644 --- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -267,18 +267,21 @@ public: return true; } - // Report a reference from explicit specializations to the specialized - // template. Implicit ones are filtered out by RAV and explicit instantiations - // are already traversed through typelocs. + // Report a reference from explicit specializations/instantiations to the + // specialized template. Implicit ones are filtered out by RAV. bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) { - if (CTSD->isExplicitSpecialization()) + // if (CTSD->isExplicitSpecialization()) + if (clang::isTemplateExplicitInstantiationOrSpecialization( + CTSD->getTemplateSpecializationKind())) report(CTSD->getLocation(), CTSD->getSpecializedTemplate()->getTemplatedDecl()); return true; } bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) { - if (VTSD->isExplicitSpecialization()) + // if (VTSD->isExplicitSpecialization()) + if (clang::isTemplateExplicitInstantiationOrSpecialization( + VTSD->getTemplateSpecializationKind())) report(VTSD->getLocation(), VTSD->getSpecializedTemplate()->getTemplatedDecl()); return true; diff --git a/clang-tools-extra/modularize/ModularizeUtilities.cpp b/clang-tools-extra/modularize/ModularizeUtilities.cpp index 53e8a49d1a54893316d52ed1de4c176a61673e99..b202b3aae8f8a3a5488bb019a30553ef3fa0df2e 100644 --- a/clang-tools-extra/modularize/ModularizeUtilities.cpp +++ b/clang-tools-extra/modularize/ModularizeUtilities.cpp @@ -435,11 +435,9 @@ static std::string replaceDotDot(StringRef Path) { llvm::sys::path::const_iterator B = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); while (B != E) { - if (B->compare(".") == 0) { - } - else if (B->compare("..") == 0) + if (*B == "..") llvm::sys::path::remove_filename(Buffer); - else + else if (*B != ".") llvm::sys::path::append(Buffer, *B); ++B; } diff --git a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py index 6d4b466afa691a74b59185e360f0b9a8b6597261..e92179ac82c6a06ddccbb670c17c28c19a72cf2b 100755 --- a/clang-tools-extra/test/clang-tidy/check_clang_tidy.py +++ b/clang-tools-extra/test/clang-tidy/check_clang_tidy.py @@ -99,6 +99,7 @@ class CheckRunner: self.has_check_fixes = False self.has_check_messages = False self.has_check_notes = False + self.expect_no_diagnosis = False self.export_fixes = args.export_fixes self.fixes = MessagePrefix("CHECK-FIXES") self.messages = MessagePrefix("CHECK-MESSAGES") @@ -172,12 +173,21 @@ class CheckRunner: ) if not has_check_fix and not has_check_message and not has_check_note: - sys.exit( - "%s, %s or %s not found in the input" - % (self.fixes.prefix, self.messages.prefix, self.notes.prefix) - ) + self.expect_no_diagnosis = True - assert self.has_check_fixes or self.has_check_messages or self.has_check_notes + expect_diagnosis = ( + self.has_check_fixes or self.has_check_messages or self.has_check_notes + ) + if self.expect_no_diagnosis and expect_diagnosis: + sys.exit( + "%s, %s or %s not found in the input" + % ( + self.fixes.prefix, + self.messages.prefix, + self.notes.prefix, + ) + ) + assert expect_diagnosis or self.expect_no_diagnosis def prepare_test_inputs(self): # Remove the contents of the CHECK lines to avoid CHECKs matching on @@ -226,6 +236,10 @@ class CheckRunner: print("------------------------------------------------------------------") return clang_tidy_output + def check_no_diagnosis(self, clang_tidy_output): + if clang_tidy_output != "": + sys.exit("No diagnostics were expected, but found the ones above") + def check_fixes(self): if self.has_check_fixes: try_run( @@ -277,7 +291,9 @@ class CheckRunner: self.get_prefixes() self.prepare_test_inputs() clang_tidy_output = self.run_clang_tidy() - if self.export_fixes is None: + if self.expect_no_diagnosis: + self.check_no_diagnosis(clang_tidy_output) + elif self.export_fixes is None: self.check_fixes() self.check_messages(clang_tidy_output) self.check_notes(clang_tidy_output) diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp index 72ef35c956d2e868849e9bd679e7d09d7d8022b0..1228d64bb6909e93da78943e81a6422da1fc2202 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp @@ -210,4 +210,6 @@ void correct(std::optional param) std::optional* p2 = &p; takeOptionalValue(p2->value_or(5U)); takeOptionalRef(p2->value_or(5U)); + + using Type = decltype(takeOptionalValue(*param)); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp index 300fff6cb179bfc00b7e48635af8efbcedaf9891..2e2964dda1dafdd996e1def36fa0463aecb72cb5 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init-no-crash.cpp @@ -5,3 +5,11 @@ struct X { // CHECK-MESSAGES: :[[@LINE-1]]:5: error: field has incomplete type 'X' [clang-diagnostic-error] int a = 10; }; + +template class NoCrash { + // CHECK-MESSAGES: :[[@LINE+2]]:20: error: base class has incomplete type + // CHECK-MESSAGES: :[[@LINE-2]]:29: note: definition of 'NoCrash' is not complete until the closing '}' + class B : public NoCrash { + template B(U u) {} + }; +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp index 8d6992afef08a977ee35673db604369ae866927e..eaa73b906ce09289c56f18b8d916b00334954d1c 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp @@ -463,12 +463,6 @@ struct NegativeIncompleteArrayMember { char e[]; }; -template class NoCrash { - class B : public NoCrash { - template B(U u) {} - }; -}; - struct PositiveBitfieldMember { PositiveBitfieldMember() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/special-member-functions-relaxed.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/special-member-functions-relaxed.cpp index 0c17f57968a990b3d62c7d9e0cf392536803e935..26142ccc835f2948fc63fe4652b8e882894f591a 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/special-member-functions-relaxed.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/special-member-functions-relaxed.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -config="{CheckOptions: {cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions: true, cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: true}}" -- +// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -config="{CheckOptions: {cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions: true, cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: true, cppcoreguidelines-special-member-functions.AllowImplicitlyDeletedCopyOrMove: true}}" -- // Don't warn on destructors without definitions, they might be defaulted in another TU. class DeclaresDestructor { @@ -34,12 +34,13 @@ class DefinesCopyAssignment { class DefinesMoveConstructor { DefinesMoveConstructor(DefinesMoveConstructor &&); }; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor, a copy constructor, a copy assignment operator or a move assignment operator [cppcoreguidelines-special-member-functions] +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor or a move assignment operator [cppcoreguidelines-special-member-functions] class DefinesMoveAssignment { DefinesMoveAssignment &operator=(DefinesMoveAssignment &&); }; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor, a copy constructor, a copy assignment operator or a move constructor [cppcoreguidelines-special-member-functions] +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor or a move constructor [cppcoreguidelines-special-member-functions] + class DefinesNothing { }; @@ -81,3 +82,22 @@ struct TemplateClass { // This should not cause problems. TemplateClass InstantiationWithInt; TemplateClass InstantiationWithDouble; + +struct NoCopy +{ + NoCopy() = default; + ~NoCopy() = default; + + NoCopy(const NoCopy&) = delete; + NoCopy(NoCopy&&) = delete; + + NoCopy& operator=(const NoCopy&) = delete; + NoCopy& operator=(NoCopy&&) = delete; +}; + +// CHECK-MESSAGES: [[@LINE+1]]:8: warning: class 'NonCopyable' defines a copy constructor but does not define a destructor or a copy assignment operator [cppcoreguidelines-special-member-functions] +struct NonCopyable : NoCopy +{ + NonCopyable() = default; + NonCopyable(const NonCopyable&) = delete; +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.hpp b/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4918aae16cb94a1720708a55bdd816a69c9763dc --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.hpp @@ -0,0 +1,6 @@ +// RUN: %check_clang_tidy %s misc-unused-using-decls %t + +// Verify that we don't generate the warnings on header files. +namespace foo { class Foo {}; } + +using foo::Foo; diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.hxx b/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.hxx deleted file mode 100644 index f15e4fae80c0bc40a86cf47412213cabb5eaeaf3..0000000000000000000000000000000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.hxx +++ /dev/null @@ -1,6 +0,0 @@ -// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- --fix-notes -- -fno-delayed-template-parsing -isystem %S/Inputs - -// Verify that we don't generate the warnings on header files. -namespace foo { class Foo {}; } - -using foo::Foo; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp index 7934c6e93ffbd38a7ae4eaa1b11215172da557a9..fe512a8f3bf3211668961c9da8632783315aeb54 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/make-unique.cpp @@ -606,11 +606,8 @@ void invoke_template() { template_fun(foo); } -void no_fix_for_invalid_new_loc() { - // FIXME: Although the code is valid, the end location of `new struct Base` is - // invalid. Correct it once https://bugs.llvm.org/show_bug.cgi?id=35952 is - // fixed. +void fix_for_c_style_struct() { auto T = std::unique_ptr(new struct Base); // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::make_unique instead - // CHECK-FIXES: auto T = std::unique_ptr(new struct Base); + // CHECK-FIXES: auto T = std::make_unique(); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp index 51ab9bda975f10baba00221fe02a6ab13f07ae90..1f2dad2b933ca70d7957d967f94bcecb8027681d 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/min-max-use-initializer-list.cpp @@ -300,6 +300,27 @@ B maxTT2 = std::max(B(), std::max(B(), B())); B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; }); // CHECK-FIXES: B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; }); +struct GH91982 { + int fun0Args(); + int fun1Arg(int a); + int fun2Args(int a, int b); + int fun3Args(int a, int b, int c); + int fun4Args(int a, int b, int c, int d); + + int foo() { + return std::max( + fun0Args(), + std::max(fun1Arg(0), + std::max(fun2Args(0, 1), + std::max(fun3Args(0, 1, 2), fun4Args(0, 1, 2, 3))))); +// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] +// CHECK-FIXES: return std::max( +// CHECK-FIXES-NEXT: {fun0Args(), +// CHECK-FIXES-NEXT: fun1Arg(0), +// CHECK-FIXES-NEXT: fun2Args(0, 1), +// CHECK-FIXES-NEXT: fun3Args(0, 1, 2), fun4Args(0, 1, 2, 3)}); + } +}; } // namespace diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp index 3ec44be8a1c8c3ca5f0c520e274b889293174550..3bcd5cd74024eaf1ba29f9d20dca2c9c9739fd00 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-constraints.cpp @@ -724,3 +724,35 @@ void not_last_param() { } } // namespace enable_if_trailing_type_parameter + + +// Issue fixes: + +namespace PR91872 { + +enum expression_template_option { value1, value2 }; + +template struct number_category { + static const int value = 0; +}; + +constexpr int number_kind_complex = 1; + +template +struct number { + using type = T; +}; + +template struct component_type { + using type = T; +}; + +template +inline typename std::enable_if< + number_category::value == number_kind_complex, + component_type>>::type::type +abs(const number &v) { + return {}; +} + +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-custom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..815e22b29155153b392a34e5238fc8deba9bb6a9 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-custom.cpp @@ -0,0 +1,52 @@ +// RUN: %check_clang_tidy -check-suffixes=,STRICT \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: { \ +// RUN: modernize-use-std-format.StrictMode: true, \ +// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \ +// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \ +// RUN: modernize-use-std-format.FormatHeader: '' \ +// RUN: }}" \ +// RUN: -- -isystem %clang_tidy_headers +// RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: { \ +// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \ +// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \ +// RUN: modernize-use-std-format.FormatHeader: '' \ +// RUN: }}" \ +// RUN: -- -isystem %clang_tidy_headers + +#include +#include +// CHECK-FIXES: #include + +std::string strprintf(const char *, ...); + +namespace mynamespace { + std::string strprintf2(const char *, ...); +} + +std::string strprintf_test(const std::string &name, double value) { + return strprintf("'%s'='%f'\n", name.c_str(), value); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format] + // CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value); + + return mynamespace::strprintf2("'%s'='%f'\n", name.c_str(), value); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf2' [modernize-use-std-format] + // CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value); +} + +std::string StrFormat_strict_conversion() { + const unsigned char uc = 'A'; + return strprintf("Integer %hhd from unsigned char\n", uc); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format] + // CHECK-FIXES-NOTSTRICT: return fmt::format("Integer {} from unsigned char\n", uc); + // CHECK-FIXES-STRICT: return fmt::format("Integer {} from unsigned char\n", static_cast(uc)); +} + +// Ensure that MatchesAnyListedNameMatcher::NameMatcher::match() can cope with a +// NamedDecl that has no name when we're trying to match unqualified_strprintf. +std::string A(const std::string &in) +{ + return "_" + in; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-fmt.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-fmt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d136cf309168d5eac9bd00468e817b902fe6d71 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-fmt.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: { \ +// RUN: StrictMode: true, \ +// RUN: modernize-use-std-format.StrFormatLikeFunctions: 'fmt::sprintf', \ +// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \ +// RUN: modernize-use-std-format.FormatHeader: '' \ +// RUN: }}" \ +// RUN: -- -isystem %clang_tidy_headers + +// CHECK-FIXES: #include +#include + +namespace fmt +{ +// Use const char * for the format since the real type is hard to mock up. +template +std::string sprintf(const char *format, const Args&... args); +} // namespace fmt + +std::string fmt_sprintf_simple() { + return fmt::sprintf("Hello %s %d", "world", 42); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'sprintf' [modernize-use-std-format] + // CHECK-FIXES: fmt::format("Hello {} {}", "world", 42); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8dea1dce2c97222c790adc29504db76cc2c432e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp @@ -0,0 +1,120 @@ +// RUN: %check_clang_tidy \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: {StrictMode: true}}" \ +// RUN: -- -isystem %clang_tidy_headers +// RUN: %check_clang_tidy \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: {StrictMode: false}}" \ +// RUN: -- -isystem %clang_tidy_headers +#include +// CHECK-FIXES: #include + +namespace absl +{ +// Use const char * for the format since the real type is hard to mock up. +template +std::string StrFormat(const char *format, const Args&... args); +} // namespace absl + +template +struct iterator { + T *operator->(); + T &operator*(); +}; + +std::string StrFormat_simple() { + return absl::StrFormat("Hello"); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("Hello"); +} + +std::string StrFormat_complex(const char *name, double value) { + return absl::StrFormat("'%s'='%f'", name, value); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("'{}'='{:f}'", name, value); +} + +std::string StrFormat_integer_conversions() { + return absl::StrFormat("int:%d int:%d char:%c char:%c", 65, 'A', 66, 'B'); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("int:{} int:{:d} char:{:c} char:{}", 65, 'A', 66, 'B'); +} + +// FormatConverter is capable of removing newlines from the end of the format +// string. Ensure that isn't incorrectly happening for std::format. +std::string StrFormat_no_newline_removal() { + return absl::StrFormat("a line\n"); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("a line\n"); +} + +// FormatConverter is capable of removing newlines from the end of the format +// string. Ensure that isn't incorrectly happening for std::format. +std::string StrFormat_cstr_removal(const std::string &s1, const std::string *s2) { + return absl::StrFormat("%s %s %s %s", s1.c_str(), s1.data(), s2->c_str(), s2->data()); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("{} {} {} {}", s1, s1, *s2, *s2); +} + +std::string StrFormat_strict_conversion() { + const unsigned char uc = 'A'; + return absl::StrFormat("Integer %hhd from unsigned char\n", uc); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("Integer {} from unsigned char\n", uc); +} + +std::string StrFormat_field_width_and_precision() { + auto s1 = absl::StrFormat("width only:%*d width and precision:%*.*f precision only:%.*f", 3, 42, 4, 2, 3.14159265358979323846, 5, 2.718); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("width only:{:{}} width and precision:{:{}.{}f} precision only:{:.{}f}", 42, 3, 3.14159265358979323846, 4, 2, 2.718, 5); + + auto s2 = absl::StrFormat("width and precision positional:%1$*2$.*3$f after", 3.14159265358979323846, 4, 2); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("width and precision positional:{0:{1}.{2}f} after", 3.14159265358979323846, 4, 2); + + const int width = 10, precision = 3; + const unsigned int ui1 = 42, ui2 = 43, ui3 = 44; + auto s3 = absl::StrFormat("casts width only:%*d width and precision:%*.*d precision only:%.*d\n", 3, ui1, 4, 2, ui2, 5, ui3); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES-NOTSTRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", ui1, 3, ui2, 4, 2, ui3, 5); + // CHECK-FIXES-STRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", static_cast(ui1), 3, static_cast(ui2), 4, 2, static_cast(ui3), 5); + + auto s4 = absl::StrFormat("c_str removal width only:%*s width and precision:%*.*s precision only:%.*s", 3, s1.c_str(), 4, 2, s2.c_str(), 5, s3.c_str()); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("c_str removal width only:{:>{}} width and precision:{:>{}.{}} precision only:{:.{}}", s1, 3, s2, 4, 2, s3, 5); + + const std::string *ps1 = &s1, *ps2 = &s2, *ps3 = &s3; + auto s5 = absl::StrFormat("c_str() removal pointer width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, ps1->c_str(), 4, 2, ps2->c_str(), 5, ps3->c_str()); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("c_str() removal pointer width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *ps1, 3, *ps2, 4, 2, *ps3, 5); + + iterator is1, is2, is3; + auto s6 = absl::StrFormat("c_str() removal iterator width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, is1->c_str(), 4, 2, is2->c_str(), 5, is3->c_str()); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("c_str() removal iterator width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *is1, 3, *is2, 4, 2, *is3, 5); + + return s1 + s2 + s3 + s4 + s5 + s6; +} + +std::string StrFormat_macros() { + // The function call is replaced even though it comes from a macro. +#define FORMAT absl::StrFormat + auto s1 = FORMAT("Hello %d", 42); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Hello {}", 42); + + // The format string is replaced even though it comes from a macro, this + // behaviour is required so that that macros are replaced. +#define FORMAT_STRING "Hello %s" + auto s2 = absl::StrFormat(FORMAT_STRING, 42); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Hello {}", 42); + + // Arguments that are macros aren't replaced with their value, even if they are rearranged. +#define VALUE 3.14159265358979323846 +#define WIDTH 10 +#define PRECISION 4 + auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION); +} diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/.clang-tidy b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/.clang-tidy index 942169f2ec42989e4153cb84281c764d206308a1..83605c85dd92cb8ec84ae3971c87db2ac9320b01 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/.clang-tidy +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/.clang-tidy @@ -1,2 +1,3 @@ Checks: 'from-parent' HeaderFilterRegex: 'parent' +ExcludeHeaderFilterRegex: 'exc-parent' diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/1/.clang-tidy b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/1/.clang-tidy index 800fd4e8eb2a942b52f7abec528efffccf693b81..c37f16bc2d7d258bbfb73dc2618bb4f1358579f0 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/1/.clang-tidy +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/1/.clang-tidy @@ -1,2 +1,3 @@ Checks: 'from-child1' HeaderFilterRegex: 'child1' +ExcludeHeaderFilterRegex: 'exc-child1' diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy index 28dc8517ac9fe86ae836349cf6e0d07a4fbc82d2..9365108255bd8b9fa6adda6460e74d286b54f13f 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy @@ -1,3 +1,4 @@ InheritParentConfig: true Checks: 'from-child3' HeaderFilterRegex: 'child3' +ExcludeHeaderFilterRegex: 'exc-child3' diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp index d287412454cadd5b2b1ebb72375743e65f962273..44d43ebbf8d2041fc662ed31ca8a27a5b4008b04 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp @@ -1,18 +1,23 @@ // RUN: clang-tidy -dump-config %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-BASE // CHECK-BASE: Checks: {{.*}}from-parent // CHECK-BASE: HeaderFilterRegex: parent +// CHECK-BASE: ExcludeHeaderFilterRegex: exc-parent // RUN: clang-tidy -dump-config %S/Inputs/config-files/1/- -- | FileCheck %s -check-prefix=CHECK-CHILD1 // CHECK-CHILD1: Checks: {{.*}}from-child1 // CHECK-CHILD1: HeaderFilterRegex: child1 +// CHECK-CHILD1: ExcludeHeaderFilterRegex: exc-child1 // RUN: clang-tidy -dump-config %S/Inputs/config-files/2/- -- | FileCheck %s -check-prefix=CHECK-CHILD2 // CHECK-CHILD2: Checks: {{.*}}from-parent // CHECK-CHILD2: HeaderFilterRegex: parent +// CHECK-CHILD2: ExcludeHeaderFilterRegex: exc-parent // RUN: clang-tidy -dump-config %S/Inputs/config-files/3/- -- | FileCheck %s -check-prefix=CHECK-CHILD3 // CHECK-CHILD3: Checks: {{.*}}from-parent,from-child3 // CHECK-CHILD3: HeaderFilterRegex: child3 -// RUN: clang-tidy -dump-config -checks='from-command-line' -header-filter='from command line' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-COMMAND-LINE +// CHECK-CHILD3: ExcludeHeaderFilterRegex: exc-child3 +// RUN: clang-tidy -dump-config -checks='from-command-line' -header-filter='from command line' -exclude-header-filter='from_command_line' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-COMMAND-LINE // CHECK-COMMAND-LINE: Checks: {{.*}}from-parent,from-command-line // CHECK-COMMAND-LINE: HeaderFilterRegex: from command line +// CHECK-COMMAND-LINE: ExcludeHeaderFilterRegex: from_command_line // For this test we have to use names of the real checks because otherwise values are ignored. // Running with the old key: , value: CheckOptions @@ -68,3 +73,11 @@ // Dumped config does not overflow for unsigned options // RUN: clang-tidy --dump-config %S/Inputs/config-files/5/- -- | FileCheck %s -check-prefix=CHECK-OVERFLOW // CHECK-OVERFLOW: misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976' + +// RUN: clang-tidy -dump-config -checks='readability-function-size' -header-filter='foo/*' -exclude-header-filter='bar*' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-EXCLUDE-HEADERS +// CHECK-EXCLUDE-HEADERS: HeaderFilterRegex: 'foo/*' +// CHECK-EXCLUDE-HEADERS: ExcludeHeaderFilterRegex: 'bar*' + +// RUN: clang-tidy -dump-config -checks='readability-function-size' -header-filter='' -exclude-header-filter='' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=EMPTY-CHECK-EXCLUDE-HEADERS +// EMPTY-CHECK-EXCLUDE-HEADERS: HeaderFilterRegex: '' +// EMPTY-CHECK-EXCLUDE-HEADERS: ExcludeHeaderFilterRegex: '' diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp index a7498723de2bf5c328d47124d428bf1bb37fafa3..448ef9ddf166ccc1dca385907074f79d91f22064 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/file-filter.cpp @@ -11,6 +11,7 @@ // RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers -quiet %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK4-QUIET %s // RUN: clang-tidy -checks='-*,cppcoreguidelines-pro-type-cstyle-cast' -header-filter='.*' -system-headers %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK5 %s // RUN: clang-tidy -checks='-*,cppcoreguidelines-pro-type-cstyle-cast' -header-filter='.*' %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK5-NO-SYSTEM-HEADERS %s +// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -exclude-header-filter='header1\.h' %s -- -I %S/Inputs/file-filter/ -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK6 %s #include "header1.h" // CHECK-NOT: warning: @@ -21,6 +22,7 @@ // CHECK3-QUIET-NOT: warning: // CHECK4: header1.h:1:12: warning: single-argument constructors // CHECK4-QUIET: header1.h:1:12: warning: single-argument constructors +// CHECK6-NOT: warning: #include "header2.h" // CHECK-NOT: warning: @@ -31,6 +33,7 @@ // CHECK3-QUIET: header2.h:1:12: warning: single-argument constructors // CHECK4: header2.h:1:12: warning: single-argument constructors // CHECK4-QUIET: header2.h:1:12: warning: single-argument constructors +// CHECK6: header2.h:1:12: warning: single-argument constructors #include // CHECK-NOT: warning: @@ -41,6 +44,7 @@ // CHECK3-QUIET-NOT: warning: // CHECK4: system-header.h:1:12: warning: single-argument constructors // CHECK4-QUIET: system-header.h:1:12: warning: single-argument constructors +// CHECK6-NOT: warning: class A { A(int); }; // CHECK: :[[@LINE-1]]:11: warning: single-argument constructors @@ -51,6 +55,7 @@ class A { A(int); }; // CHECK3-QUIET: :[[@LINE-6]]:11: warning: single-argument constructors // CHECK4: :[[@LINE-7]]:11: warning: single-argument constructors // CHECK4-QUIET: :[[@LINE-8]]:11: warning: single-argument constructors +// CHECK6: :[[@LINE-9]]:11: warning: single-argument constructors // CHECK-NOT: warning: // CHECK-QUIET-NOT: warning: @@ -73,6 +78,8 @@ class A { A(int); }; // CHECK4-NOT: Suppressed {{.*}} warnings // CHECK4-NOT: Use -header-filter=.* {{.*}} // CHECK4-QUIET-NOT: Suppressed +// CHECK6: Suppressed 2 warnings (2 in non-user code) +// CHECK6: Use -header-filter=.* {{.*}} int x = 123; auto x_ptr = TO_FLOAT_PTR(&x); diff --git a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp index b561e2bb98332113021670dceea19e1104902465..e414587c568b73eadcc56aad35d2e4ffd79ddebe 100644 --- a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp +++ b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp @@ -9,7 +9,6 @@ #include "QueryParser.h" #include "Query.h" #include "QuerySession.h" -#include "clang/Tooling/NodeIntrospection.h" #include "llvm/LineEditor/LineEditor.h" #include "gtest/gtest.h" @@ -61,7 +60,6 @@ TEST_F(QueryParserTest, Quit) { TEST_F(QueryParserTest, Set) { - bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport(); QueryRef Q = parse("set"); ASSERT_TRUE(isa(Q)); EXPECT_EQ("expected variable name", cast(Q)->ErrStr); @@ -72,13 +70,8 @@ TEST_F(QueryParserTest, Set) { Q = parse("set output"); ASSERT_TRUE(isa(Q)); - if (HasIntrospection) - EXPECT_EQ( - "expected 'diag', 'print', 'detailed-ast', 'srcloc' or 'dump', got ''", - cast(Q)->ErrStr); - else - EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''", - cast(Q)->ErrStr); + EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''", + cast(Q)->ErrStr); Q = parse("set bind-root true foo"); ASSERT_TRUE(isa(Q)); @@ -86,13 +79,8 @@ TEST_F(QueryParserTest, Set) { Q = parse("set output foo"); ASSERT_TRUE(isa(Q)); - if (HasIntrospection) - EXPECT_EQ("expected 'diag', 'print', 'detailed-ast', 'srcloc' or 'dump', " - "got 'foo'", - cast(Q)->ErrStr); - else - EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'", - cast(Q)->ErrStr); + EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'", + cast(Q)->ErrStr); Q = parse("set output dump"); ASSERT_TRUE(isa(Q)); @@ -232,10 +220,8 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("output ", Comps[0].TypedText); EXPECT_EQ("output", Comps[0].DisplayText); - bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport(); - Comps = QueryParser::complete("enable output ", 14, QS); - ASSERT_EQ(HasIntrospection ? 5u : 4u, Comps.size()); + ASSERT_EQ(4u, Comps.size()); EXPECT_EQ("diag ", Comps[0].TypedText); EXPECT_EQ("diag", Comps[0].DisplayText); @@ -243,12 +229,8 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("print", Comps[1].DisplayText); EXPECT_EQ("detailed-ast ", Comps[2].TypedText); EXPECT_EQ("detailed-ast", Comps[2].DisplayText); - if (HasIntrospection) { - EXPECT_EQ("srcloc ", Comps[3].TypedText); - EXPECT_EQ("srcloc", Comps[3].DisplayText); - } - EXPECT_EQ("dump ", Comps[HasIntrospection ? 4 : 3].TypedText); - EXPECT_EQ("dump", Comps[HasIntrospection ? 4 : 3].DisplayText); + EXPECT_EQ("dump ", Comps[3].TypedText); + EXPECT_EQ("dump", Comps[3].DisplayText); Comps = QueryParser::complete("set traversal ", 14, QS); ASSERT_EQ(2u, Comps.size()); diff --git a/clang/cmake/caches/HLSL.cmake b/clang/cmake/caches/HLSL.cmake index 84850c86f12cd7ab92237452ce8b2bd8fdfb0dce..ed813f60c9c69970e8c93784b39cc3955693a062 100644 --- a/clang/cmake/caches/HLSL.cmake +++ b/clang/cmake/caches/HLSL.cmake @@ -8,6 +8,12 @@ set(LLVM_EXPERIMENTAL_TARGETS_TO_BUILD "DirectX;SPIRV" CACHE STRING "") # HLSL support is currently limted to clang, eventually it will expand to # clang-tools-extra too. -set(LLVM_ENABLE_PROJECTS "clang" CACHE STRING "") +set(LLVM_ENABLE_PROJECTS "clang;clang-tools-extra" CACHE STRING "") set(CLANG_ENABLE_HLSL On CACHE BOOL "") + +if (HLSL_ENABLE_DISTRIBUTION) + set(LLVM_DISTRIBUTION_COMPONENTS + "clang;hlsl-resource-headers;clangd" + CACHE STRING "") +endif() diff --git a/clang/docs/ClangLinkerWrapper.rst b/clang/docs/ClangLinkerWrapper.rst index 3bef558475735115fba43d0290bdd9c0d76dbca7..99352863b4773a38d33ce168a297fe1f2f4199b9 100644 --- a/clang/docs/ClangLinkerWrapper.rst +++ b/clang/docs/ClangLinkerWrapper.rst @@ -46,6 +46,8 @@ only for the linker wrapper will be forwarded to the wrapped linker job. -l Search for library --opt-level= Optimization level for LTO + --override-image= + Uses the provided file as if it were the output of the device link step -o Path to file to write output --pass-remarks-analysis= Pass remarks for LTO @@ -87,6 +89,42 @@ other. Generally, this requires that the target triple and architecture match. An exception is made when the architecture is listed as ``generic``, which will cause it be linked with any other device code with the same target triple. +Debugging +========= + +The linker wrapper performs a lot of steps internally, such as input matching, +symbol resolution, and image registration. This makes it difficult to debug in +some scenarios. The behavior of the linker-wrapper is controlled mostly through +metadata, described in `clang documentation +`_. Intermediate output can +be obtained from the linker-wrapper using the ``--save-temps`` flag. These files +can then be modified. + +.. code-block:: sh + + $> clang openmp.c -fopenmp --offload-arch=gfx90a -c + $> clang openmp.o -fopenmp --offload-arch=gfx90a -Wl,--save-temps + $> ; Modify temp files. + $> llvm-objcopy --update-section=.llvm.offloading=out.bc openmp.o + +Doing this will allow you to override one of the input files by replacing its +embedded offloading metadata with a user-modified version. However, this will be +more difficult when there are multiple input files. For a very large hammer, the +``--override-image==`` flag can be used. + +In the following example, we use the ``--save-temps`` to obtain the LLVM-IR just +before running the backend. We then modify it to test altered behavior, and then +compile it to a binary. This can then be passed to the linker-wrapper which will +then ignore all embedded metadata and use the provided image as if it were the +result of the device linking phase. + +.. code-block:: sh + + $> clang openmp.c -fopenmp --offload-arch=gfx90a -Wl,--save-temps + $> ; Modify temp files. + $> clang --target=amdgcn-amd-amdhsa -mcpu=gfx90a -nogpulib out.bc -o a.out + $> clang openmp.c -fopenmp --offload-arch=gfx90a -Wl,--override-image=openmp=a.out + Example ======= diff --git a/clang/docs/HLSL/AvailabilityDiagnostics.rst b/clang/docs/HLSL/AvailabilityDiagnostics.rst new file mode 100644 index 0000000000000000000000000000000000000000..bb9d02f21dde626cf999c6f58818301d8f133c1f --- /dev/null +++ b/clang/docs/HLSL/AvailabilityDiagnostics.rst @@ -0,0 +1,137 @@ +============================= +HLSL Availability Diagnostics +============================= + +.. contents:: + :local: + +Introduction +============ + +HLSL availability diagnostics emits errors or warning when unavailable shader APIs are used. Unavailable shader APIs are APIs that are exposed in HLSL code but are not available in the target shader stage or shader model version. + +There are three modes of HLSL availability diagnostic: + +#. **Default mode** - compiler emits an error when an unavailable API is found in a code that is reachable from the shader entry point function or from an exported library function (when compiling a shader library) + +#. **Relaxed mode** - same as default mode except the compiler emits a warning. This mode is enabled by ``-Wno-error=hlsl-availability``. + +#. **Strict mode** - compiler emits an error when an unavailable API is found in parsed code regardless of whether it can be reached from the shader entry point or exported functions, or not. This mode is enabled by ``-fhlsl-strict-availability``. + +Implementation Details +====================== + +Environment Parameter +--------------------- + +In order to encode API availability based on the shader model version and shader model stage a new ``environment`` parameter was added to the existing Clang ``availability`` attribute. + +The values allowed for this parameter are a subset of values allowed as the ``llvm::Triple`` environment component. If the environment parameters is present, the declared availability attribute applies only to targets with the same platform and environment. + +Default and Relaxed Diagnostic Modes +------------------------------------ + +This mode is implemented in ``DiagnoseHLSLAvailability`` class in ``SemaHLSL.cpp`` and it is invoked after the whole translation unit is parsed (from ``Sema::ActOnEndOfTranslationUnit``). The implementation iterates over all shader entry points and exported library functions in the translation unit and performs an AST traversal of each function body. + +When a reference to another function or member method is found (``DeclRefExpr`` or ``MemberExpr``) and it has a body, the AST of the referenced function is also scanned. This chain of AST traversals will reach all of the code that is reachable from the initial shader entry point or exported library function and avoids the need to generate a call graph. + +All shader APIs have an availability attribute that specifies the shader model version (and environment, if applicable) when this API was first introduced.When a reference to a function without a definition is found and it has an availability attribute, the version of the attribute is checked against the target shader model version and shader stage (if shader stage context is known), and an appropriate diagnostic is generated as needed. + +All shader entry functions have ``HLSLShaderAttr`` attribute that specifies what type of shader this function represents. However, for exported library functions the target shader stage is unknown, so in this case the HLSL API availability will be only checked against the shader model version. It means that for exported library functions the diagnostic of APIs with availability specific to shader stage will be deferred until DXIL linking time. + +A list of functions that were already scanned is kept in order to avoid duplicate scans and diagnostics (see ``DiagnoseHLSLAvailability::ScannedDecls``). It might happen that a shader library has multiple shader entry points for different shader stages that all call into the same shared function. It is therefore important to record not just that a function has been scanned, but also in which shader stage context. This is done by using ``llvm::DenseMap`` that maps ``FunctionDecl *`` to a ``unsigned`` bitmap that represents a set of shader stages (or environments) the function has been scanned for. The ``N``'th bit in the set is set if the function has been scanned in shader environment whose ``HLSLShaderAttr::ShaderType`` integer value equals ``N``. + +The emitted diagnostic messages belong to ``hlsl-availability`` diagnostic group and are reported as errors by default. With ``-Wno-error=hlsl-availability`` flag they become warning, making it relaxed HLSL diagnostics mode. + +Strict Diagnostic Mode +---------------------- + +When strict HLSL availability diagnostic mode is enabled the compiler must report all HLSL API availability issues regardless of code reachability. The implementation of this mode takes advantage of an existing diagnostic scan in ``DiagnoseUnguardedAvailability`` class which is already traversing AST of each function as soon as the function body has been parsed. For HLSL, this pass was only slightly modified, such as making sure diagnostic messages are in the ``hlsl-availability`` group and that availability checks based on shader stage are not included if the shader stage context is unknown. + +If the compilation target is a shader library, only availability based on shader model version can be diagnosed during this scan. To diagnose availability based on shader stage, the compiler needs to run the AST traversals implementated in ``DiagnoseHLSLAvailability`` at the end of the translation unit as described above. + +As a result, availability based on specific shader stage will only be diagnosed in code that is reachable from a shader entry point or library export function. It also means that function bodies might be scanned multiple time. When that happens, care should be taken not to produce duplicated diagnostics. + +======== +Examples +======== + +**Note** +For the example below, the ``WaveActiveCountBits`` API function became available in shader model 6.0 and ``WaveMultiPrefixSum`` in shader model 6.5. + +The availability of ``ddx`` function depends on a shader stage. It is available for pixel shaders in shader model 2.1 and higher, for compute, mesh and amplification shaders in shader model 6.6 and higher. For any other shader stages it is not available. + +Compute shader example +====================== + +.. code-block:: c++ + + float unusedFunction(float f) { + return ddx(f); + } + + [numthreads(4, 4, 1)] + void main(uint3 threadId : SV_DispatchThreadId) { + float f1 = ddx(threadId.x); + float f2 = WaveActiveCountBits(threadId.y == 1.0); + } + +When compiled as compute shader for shader model version 5.0, Clang will emit the following error by default: + +.. code-block:: console + + <>:7:13: error: 'ddx' is only available in compute shader environment on Shader Model 6.6 or newer + <>:8:13: error: 'WaveActiveCountBits' is only available on Shader Model 6.5 or newer + +With relaxed diagnostic mode this errors will become warnings. + +With strict diagnostic mode, in addition to the 2 errors above Clang will also emit error for the ``ddx`` call in ``unusedFunction``.: + +.. code-block:: console + + <>:2:9: error: 'ddx' is only available in compute shader environment on Shader Model 6.5 or newer + <>:7:13: error: 'ddx' is only available in compute shader environment on Shader Model 6.5 or newer + <>:7:13: error: 'WaveActiveCountBits' is only available on Shader Model 6.5 or newer + +Shader library example +====================== + +.. code-block:: c++ + + float myFunction(float f) { + return ddx(f); + } + + float unusedFunction(float f) { + return WaveMultiPrefixSum(f, 1.0); + } + + [shader("compute")] + [numthreads(4, 4, 1)] + void main(uint3 threadId : SV_DispatchThreadId) { + float f = 3; + float e = myFunction(f); + } + + [shader("pixel")] + void main() { + float f = 3; + float e = myFunction(f); + } + +When compiled as shader library vshader model version 6.4, Clang will emit the following error by default: + +.. code-block:: console + + <>:2:9: error: 'ddx' is only available in compute shader environment on Shader Model 6.5 or newer + +With relaxed diagnostic mode this errors will become warnings. + +With strict diagnostic mode Clang will also emit errors for availability issues in code that is not used by any of the entry points: + +.. code-block:: console + + <>2:9: error: 'ddx' is only available in compute shader environment on Shader Model 6.6 or newer + <>:6:9: error: 'WaveActiveCountBits' is only available on Shader Model 6.5 or newer + +Note that ``myFunction`` is reachable from both pixel and compute shader entry points is therefore scanned twice - once for each context. The diagnostic is emitted only for the compute shader context. diff --git a/clang/docs/HLSL/HLSLDocs.rst b/clang/docs/HLSL/HLSLDocs.rst index 97b2425f013b3453cc06b2f61a5846414033880c..1e50a66d984b53b1d39845c2c6a3a7b5b37d6a09 100644 --- a/clang/docs/HLSL/HLSLDocs.rst +++ b/clang/docs/HLSL/HLSLDocs.rst @@ -16,3 +16,4 @@ HLSL Design and Implementation ResourceTypes EntryFunctions FunctionCalls + AvailabilityDiagnostics diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index bb1b68f6671b1a7a3058968ce28671dc58c9eeaa..a16b9c44ef0eab493d07b6a148ee20519430da03 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -3546,33 +3546,35 @@ cxxMethodDecl(isConst()) matches A::foo() but not A::bar() -Matcher<CXXMethodDecl>isExplicitObjectMemberFunction -
Matches if the given method declaration declares a member function with an explicit object parameter.
+Matcher<CXXMethodDecl>isCopyAssignmentOperator
+
Matches if the given method declaration declares a copy assignment
+operator.
 
 Given
 struct A {
-  int operator-(this A, int);
-  void fun(this A &&self);
-  static int operator()(int);
-  int operator+(int);
+  A &operator=(const A &);
+  A &operator=(A &&);
 };
 
-cxxMethodDecl(isExplicitObjectMemberFunction()) matches the first two methods but not the last two.
+cxxMethodDecl(isCopyAssignmentOperator()) matches the first method but not
+the second one.
 
-Matcher<CXXMethodDecl>isCopyAssignmentOperator -
Matches if the given method declaration declares a copy assignment
-operator.
+Matcher<CXXMethodDecl>isExplicitObjectMemberFunction
+
Matches if the given method declaration declares a member function with an
+explicit object parameter.
 
 Given
 struct A {
-  A &operator=(const A &);
-  A &operator=(A &&);
+ int operator-(this A, int);
+ void fun(this A &&self);
+ static int operator()(int);
+ int operator+(int);
 };
 
-cxxMethodDecl(isCopyAssignmentOperator()) matches the first method but not
-the second one.
+cxxMethodDecl(isExplicitObjectMemberFunction()) matches the first two
+methods but not the last two.
 
@@ -6713,7 +6715,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>, Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>, Matcher<CXXUnresolvedConstructExpr>, - Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>, + Matcher<CompoundLiteralExpr>, Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>, Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>, Matcher<TypedefNameDecl> @@ -6757,7 +6759,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>, Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>, Matcher<CXXUnresolvedConstructExpr>, - Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>, + Matcher<CompoundLiteralExpr>, Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>, Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>, Matcher<TypedefNameDecl> @@ -6985,7 +6987,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>, Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>, Matcher<CXXUnresolvedConstructExpr>, - Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>, + Matcher<CompoundLiteralExpr>, Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>, Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>, Matcher<TypedefNameDecl> @@ -7219,7 +7221,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>, Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>, Matcher<CXXUnresolvedConstructExpr>, - Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>, + Matcher<CompoundLiteralExpr>, Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>, Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>, Matcher<TypedefNameDecl> @@ -7416,7 +7418,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>, Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>, Matcher<CXXUnresolvedConstructExpr>, - Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>, + Matcher<CompoundLiteralExpr>, Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>, Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>, Matcher<TypedefNameDecl> @@ -7620,7 +7622,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>, Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>, Matcher<CXXUnresolvedConstructExpr>, - Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>, + Matcher<CompoundLiteralExpr>, Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>, Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>, Matcher<TypedefNameDecl> @@ -7677,7 +7679,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>, Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>, Matcher<CXXUnresolvedConstructExpr>, - Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>, + Matcher<CompoundLiteralExpr>, Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>, Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>, Matcher<TypedefNameDecl> @@ -7875,9 +7877,10 @@ int a = b ?: 1; Matcher<ClassTemplateSpecializationDecl>forEachTemplateArgumentMatcher<TemplateArgument> InnerMatcher -
Matches classTemplateSpecialization, templateSpecializationType and
-functionDecl nodes where the template argument matches the inner matcher.
-This matcher may produce multiple matches.
+
Matches templateSpecializationType, class template specialization,
+variable template specialization, and function template specialization
+nodes where the template argument matches the inner matcher. This matcher
+may produce multiple matches.
 
 Given
   template <typename T, unsigned N, unsigned M>
@@ -7899,10 +7902,25 @@ functionDecl(forEachTemplateArgument(refersToType(builtinType())))
 
+Matcher<ClassTemplateSpecializationDecl>hasAnyTemplateArgumentLocMatcher<TemplateArgumentLoc> InnerMatcher +
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+that have at least one `TemplateArgumentLoc` matching the given
+`InnerMatcher`.
+
+Given
+  template<typename T> class A {};
+  A<int> a;
+varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasAnyTemplateArgumentLoc(
+  hasTypeLoc(loc(asString("int")))))))
+  matches `A<int> a`.
+
+ + Matcher<ClassTemplateSpecializationDecl>hasAnyTemplateArgumentMatcher<TemplateArgument> InnerMatcher -
Matches classTemplateSpecializations, templateSpecializationType and
-functionDecl that have at least one TemplateArgument matching the given
-InnerMatcher.
+
Matches templateSpecializationTypes, class template specializations,
+variable template specializations, and function template specializations
+that have at least one TemplateArgument matching the given InnerMatcher.
 
 Given
   template<typename T> class A {};
@@ -7933,9 +7951,25 @@ classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl()))
 
+Matcher<ClassTemplateSpecializationDecl>hasTemplateArgumentLocunsigned Index, Matcher<TemplateArgumentLoc> InnerMatcher +
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
+
+Given
+  template<typename T, typename U> class A {};
+  A<double, int> b;
+  A<int, double> c;
+varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasTemplateArgumentLoc(0,
+  hasTypeLoc(loc(asString("double")))))))
+  matches `A<double, int> b`, but not `A<int, double> c`.
+
+ + Matcher<ClassTemplateSpecializationDecl>hasTemplateArgumentunsigned N, Matcher<TemplateArgument> InnerMatcher -
Matches classTemplateSpecializations, templateSpecializationType and
-functionDecl where the n'th TemplateArgument matches the given InnerMatcher.
+
Matches templateSpecializationType, class template specializations,
+variable template specializations, and function template specializations
+where the n'th TemplateArgument matches the given InnerMatcher.
 
 Given
   template<typename T, typename U> class A {};
@@ -7953,34 +7987,6 @@ functionDecl(hasTemplateArgument(0, refersToType(asString("int"))))
 
-Matcher<ClassTemplateSpecializationDecl>hasTypeLocMatcher<TypeLoc> Inner -
Matches if the type location of a node matches the inner matcher.
-
-Examples:
-  int x;
-declaratorDecl(hasTypeLoc(loc(asString("int"))))
-  matches int x
-
-auto x = int(3);
-cxxTemporaryObjectExpr(hasTypeLoc(loc(asString("int"))))
-  matches int(3)
-
-struct Foo { Foo(int, int); };
-auto x = Foo(1, 2);
-cxxFunctionalCastExpr(hasTypeLoc(loc(asString("struct Foo"))))
-  matches Foo(1, 2)
-
-Usable as: Matcher<BlockDecl>, Matcher<CXXBaseSpecifier>,
-  Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
-  Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
-  Matcher<CXXUnresolvedConstructExpr>,
-  Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
-  Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
-  Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
-  Matcher<TypedefNameDecl>
-
- - Matcher<ComplexType>hasElementTypeMatcher<Type>
Matches arrays and C99 complex types that have a specific element
 type.
@@ -7996,8 +8002,8 @@ Usable as: Matcher<CompoundLiteralExpr>hasTypeLocMatcher<TypeLoc> Inner
-
Matches if the type location of a node matches the inner matcher.
+Matcher<CompoundLiteralExpr>hasTypeLocMatcher<TypeLoc> Inner
+
Matches if the type location of a node matches the inner matcher.
 
 Examples:
   int x;
@@ -8017,7 +8023,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
   Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
   Matcher<CXXUnresolvedConstructExpr>,
-  Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
+  Matcher<CompoundLiteralExpr>,
   Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
   Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
   Matcher<TypedefNameDecl>
@@ -8066,6 +8072,21 @@ with compoundStmt()
 
+Matcher<DeclRefExpr>hasAnyTemplateArgumentLocMatcher<TemplateArgumentLoc> InnerMatcher +
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+that have at least one `TemplateArgumentLoc` matching the given
+`InnerMatcher`.
+
+Given
+  template<typename T> class A {};
+  A<int> a;
+varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasAnyTemplateArgumentLoc(
+  hasTypeLoc(loc(asString("int")))))))
+  matches `A<int> a`.
+
+ + Matcher<DeclRefExpr>hasDeclarationMatcher<Decl> InnerMatcher
Matches a node if the declaration associated with that node
 matches the given matcher.
@@ -8100,9 +8121,10 @@ Usable as: Matcher<DeclRefExpr>hasTemplateArgumentLocunsigned Index, Matcher<TemplateArgumentLoc> InnerMatcher
-
Matches template specialization `TypeLoc`s where the n'th
-`TemplateArgumentLoc` matches the given `InnerMatcher`.
+Matcher<DeclRefExpr>hasTemplateArgumentLocunsigned Index, Matcher<TemplateArgumentLoc> InnerMatcher
+
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
 
 Given
   template<typename T, typename U> class A {};
@@ -8176,8 +8198,8 @@ declStmt(hasSingleDecl(anything()))
 
-Matcher<DeclaratorDecl>hasTypeLocMatcher<TypeLoc> Inner -
Matches if the type location of a node matches the inner matcher.
+Matcher<DeclaratorDecl>hasTypeLocMatcher<TypeLoc> Inner
+
Matches if the type location of a node matches the inner matcher.
 
 Examples:
   int x;
@@ -8197,7 +8219,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
   Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
   Matcher<CXXUnresolvedConstructExpr>,
-  Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
+  Matcher<CompoundLiteralExpr>,
   Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
   Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
   Matcher<TypedefNameDecl>
@@ -8393,8 +8415,8 @@ actual casts "explicit" casts.)
 
-Matcher<ExplicitCastExpr>hasTypeLocMatcher<TypeLoc> Inner -
Matches if the type location of a node matches the inner matcher.
+Matcher<ExplicitCastExpr>hasTypeLocMatcher<TypeLoc> Inner
+
Matches if the type location of a node matches the inner matcher.
 
 Examples:
   int x;
@@ -8414,7 +8436,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
   Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
   Matcher<CXXUnresolvedConstructExpr>,
-  Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
+  Matcher<CompoundLiteralExpr>,
   Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
   Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
   Matcher<TypedefNameDecl>
@@ -8707,9 +8729,10 @@ Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
 
 
 Matcher<FunctionDecl>forEachTemplateArgumentMatcher<TemplateArgument> InnerMatcher
-
Matches classTemplateSpecialization, templateSpecializationType and
-functionDecl nodes where the template argument matches the inner matcher.
-This matcher may produce multiple matches.
+
Matches templateSpecializationType, class template specialization,
+variable template specialization, and function template specialization
+nodes where the template argument matches the inner matcher. This matcher
+may produce multiple matches.
 
 Given
   template <typename T, unsigned N, unsigned M>
@@ -8778,10 +8801,25 @@ matching y.
 
+Matcher<FunctionDecl>hasAnyTemplateArgumentLocMatcher<TemplateArgumentLoc> InnerMatcher +
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+that have at least one `TemplateArgumentLoc` matching the given
+`InnerMatcher`.
+
+Given
+  template<typename T> class A {};
+  A<int> a;
+varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasAnyTemplateArgumentLoc(
+  hasTypeLoc(loc(asString("int")))))))
+  matches `A<int> a`.
+
+ + Matcher<FunctionDecl>hasAnyTemplateArgumentMatcher<TemplateArgument> InnerMatcher -
Matches classTemplateSpecializations, templateSpecializationType and
-functionDecl that have at least one TemplateArgument matching the given
-InnerMatcher.
+
Matches templateSpecializationTypes, class template specializations,
+variable template specializations, and function template specializations
+that have at least one TemplateArgument matching the given InnerMatcher.
 
 Given
   template<typename T> class A {};
@@ -8878,9 +8916,25 @@ functionDecl(hasReturnTypeLoc(loc(asString("int"))))
 
+Matcher<FunctionDecl>hasTemplateArgumentLocunsigned Index, Matcher<TemplateArgumentLoc> InnerMatcher +
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
+
+Given
+  template<typename T, typename U> class A {};
+  A<double, int> b;
+  A<int, double> c;
+varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasTemplateArgumentLoc(0,
+  hasTypeLoc(loc(asString("double")))))))
+  matches `A<double, int> b`, but not `A<int, double> c`.
+
+ + Matcher<FunctionDecl>hasTemplateArgumentunsigned N, Matcher<TemplateArgument> InnerMatcher -
Matches classTemplateSpecializations, templateSpecializationType and
-functionDecl where the n'th TemplateArgument matches the given InnerMatcher.
+
Matches templateSpecializationType, class template specializations,
+variable template specializations, and function template specializations
+where the n'th TemplateArgument matches the given InnerMatcher.
 
 Given
   template<typename T, typename U> class A {};
@@ -9473,8 +9527,8 @@ matching y.
 
-Matcher<ObjCPropertyDecl>hasTypeLocMatcher<TypeLoc> Inner -
Matches if the type location of a node matches the inner matcher.
+Matcher<ObjCPropertyDecl>hasTypeLocMatcher<TypeLoc> Inner
+
Matches if the type location of a node matches the inner matcher.
 
 Examples:
   int x;
@@ -9494,7 +9548,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
   Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
   Matcher<CXXUnresolvedConstructExpr>,
-  Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
+  Matcher<CompoundLiteralExpr>,
   Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
   Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
   Matcher<TypedefNameDecl>
@@ -9919,8 +9973,8 @@ Usable as: Matcher<TemplateArgumentLoc>hasTypeLocMatcher<TypeLoc> Inner
-
Matches if the type location of a node matches the inner matcher.
+Matcher<TemplateArgumentLoc>hasTypeLocMatcher<TypeLoc> Inner
+
Matches if the type location of a node matches the inner matcher.
 
 Examples:
   int x;
@@ -9940,7 +9994,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
   Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
   Matcher<CXXUnresolvedConstructExpr>,
-  Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
+  Matcher<CompoundLiteralExpr>,
   Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
   Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
   Matcher<TypedefNameDecl>
@@ -10014,9 +10068,11 @@ matches the specialization of struct A generated by A<X>.
 
-Matcher<TemplateSpecializationTypeLoc>hasAnyTemplateArgumentLocMatcher<TemplateArgumentLoc> InnerMatcher -
Matches template specialization `TypeLoc`s that have at least one
-`TemplateArgumentLoc` matching the given `InnerMatcher`.
+Matcher<TemplateSpecializationTypeLoc>hasAnyTemplateArgumentLocMatcher<TemplateArgumentLoc> InnerMatcher
+
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+that have at least one `TemplateArgumentLoc` matching the given
+`InnerMatcher`.
 
 Given
   template<typename T> class A {};
@@ -10027,9 +10083,10 @@ varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasAnyTemplateArgumentLoc(
 
-Matcher<TemplateSpecializationTypeLoc>hasTemplateArgumentLocunsigned Index, Matcher<TemplateArgumentLoc> InnerMatcher -
Matches template specialization `TypeLoc`s where the n'th
-`TemplateArgumentLoc` matches the given `InnerMatcher`.
+Matcher<TemplateSpecializationTypeLoc>hasTemplateArgumentLocunsigned Index, Matcher<TemplateArgumentLoc> InnerMatcher
+
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
 
 Given
   template<typename T, typename U> class A {};
@@ -10041,10 +10098,11 @@ varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasTemplateArgumentLoc(0,
 
-Matcher<TemplateSpecializationType>forEachTemplateArgumentMatcher<TemplateArgument> InnerMatcher -
Matches classTemplateSpecialization, templateSpecializationType and
-functionDecl nodes where the template argument matches the inner matcher.
-This matcher may produce multiple matches.
+Matcher<TemplateSpecializationType>forEachTemplateArgumentMatcher<TemplateArgument> InnerMatcher
+
Matches templateSpecializationType, class template specialization,
+variable template specialization, and function template specialization
+nodes where the template argument matches the inner matcher. This matcher
+may produce multiple matches.
 
 Given
   template <typename T, unsigned N, unsigned M>
@@ -10066,10 +10124,10 @@ functionDecl(forEachTemplateArgument(refersToType(builtinType())))
 
-Matcher<TemplateSpecializationType>hasAnyTemplateArgumentMatcher<TemplateArgument> InnerMatcher -
Matches classTemplateSpecializations, templateSpecializationType and
-functionDecl that have at least one TemplateArgument matching the given
-InnerMatcher.
+Matcher<TemplateSpecializationType>hasAnyTemplateArgumentMatcher<TemplateArgument> InnerMatcher
+
Matches templateSpecializationTypes, class template specializations,
+variable template specializations, and function template specializations
+that have at least one TemplateArgument matching the given InnerMatcher.
 
 Given
   template<typename T> class A {};
@@ -10122,9 +10180,10 @@ Usable as: Matcher<TemplateSpecializationType>hasTemplateArgumentunsigned N, Matcher<TemplateArgument> InnerMatcher
-
Matches classTemplateSpecializations, templateSpecializationType and
-functionDecl where the n'th TemplateArgument matches the given InnerMatcher.
+Matcher<TemplateSpecializationType>hasTemplateArgumentunsigned N, Matcher<TemplateArgument> InnerMatcher
+
Matches templateSpecializationType, class template specializations,
+variable template specializations, and function template specializations
+where the n'th TemplateArgument matches the given InnerMatcher.
 
 Given
   template<typename T, typename U> class A {};
@@ -10182,8 +10241,8 @@ QualType-matcher matches.
 
-Matcher<TypedefNameDecl>hasTypeLocMatcher<TypeLoc> Inner -
Matches if the type location of a node matches the inner matcher.
+Matcher<TypedefNameDecl>hasTypeLocMatcher<TypeLoc> Inner
+
Matches if the type location of a node matches the inner matcher.
 
 Examples:
   int x;
@@ -10203,7 +10262,7 @@ Usable as: Matcher<CXXCtorInitializer>, Matcher<CXXFunctionalCastExpr>,
   Matcher<CXXNewExpr>, Matcher<CXXTemporaryObjectExpr>,
   Matcher<CXXUnresolvedConstructExpr>,
-  Matcher<ClassTemplateSpecializationDecl>, Matcher<CompoundLiteralExpr>,
+  Matcher<CompoundLiteralExpr>,
   Matcher<DeclaratorDecl>, Matcher<ExplicitCastExpr>,
   Matcher<ObjCPropertyDecl>, Matcher<TemplateArgumentLoc>,
   Matcher<TypedefNameDecl>
@@ -10449,6 +10508,105 @@ Example matches x (matcher = varDecl(hasInitializer(callExpr())))
 
+Matcher<VarTemplateSpecializationDecl>forEachTemplateArgumentMatcher<TemplateArgument> InnerMatcher +
Matches templateSpecializationType, class template specialization,
+variable template specialization, and function template specialization
+nodes where the template argument matches the inner matcher. This matcher
+may produce multiple matches.
+
+Given
+  template <typename T, unsigned N, unsigned M>
+  struct Matrix {};
+
+  constexpr unsigned R = 2;
+  Matrix<int, R * 2, R * 4> M;
+
+  template <typename T, typename U>
+  void f(T&& t, U&& u) {}
+
+  bool B = false;
+  f(R, B);
+templateSpecializationType(forEachTemplateArgument(isExpr(expr())))
+  matches twice, with expr() matching 'R * 2' and 'R * 4'
+functionDecl(forEachTemplateArgument(refersToType(builtinType())))
+  matches the specialization f<unsigned, bool> twice, for 'unsigned'
+  and 'bool'
+
+ + +Matcher<VarTemplateSpecializationDecl>hasAnyTemplateArgumentLocMatcher<TemplateArgumentLoc> InnerMatcher +
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+that have at least one `TemplateArgumentLoc` matching the given
+`InnerMatcher`.
+
+Given
+  template<typename T> class A {};
+  A<int> a;
+varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasAnyTemplateArgumentLoc(
+  hasTypeLoc(loc(asString("int")))))))
+  matches `A<int> a`.
+
+ + +Matcher<VarTemplateSpecializationDecl>hasAnyTemplateArgumentMatcher<TemplateArgument> InnerMatcher +
Matches templateSpecializationTypes, class template specializations,
+variable template specializations, and function template specializations
+that have at least one TemplateArgument matching the given InnerMatcher.
+
+Given
+  template<typename T> class A {};
+  template<> class A<double> {};
+  A<int> a;
+
+  template<typename T> f() {};
+  void func() { f<int>(); };
+
+classTemplateSpecializationDecl(hasAnyTemplateArgument(
+    refersToType(asString("int"))))
+  matches the specialization A<int>
+
+functionDecl(hasAnyTemplateArgument(refersToType(asString("int"))))
+  matches the specialization f<int>
+
+ + +Matcher<VarTemplateSpecializationDecl>hasTemplateArgumentLocunsigned Index, Matcher<TemplateArgumentLoc> InnerMatcher +
Matches template specialization `TypeLoc`s, class template specializations,
+variable template specializations, and function template specializations
+where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
+
+Given
+  template<typename T, typename U> class A {};
+  A<double, int> b;
+  A<int, double> c;
+varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasTemplateArgumentLoc(0,
+  hasTypeLoc(loc(asString("double")))))))
+  matches `A<double, int> b`, but not `A<int, double> c`.
+
+ + +Matcher<VarTemplateSpecializationDecl>hasTemplateArgumentunsigned N, Matcher<TemplateArgument> InnerMatcher +
Matches templateSpecializationType, class template specializations,
+variable template specializations, and function template specializations
+where the n'th TemplateArgument matches the given InnerMatcher.
+
+Given
+  template<typename T, typename U> class A {};
+  A<bool, int> b;
+  A<int, bool> c;
+
+  template<typename T> void f() {}
+  void func() { f<int>(); };
+classTemplateSpecializationDecl(hasTemplateArgument(
+    1, refersToType(asString("int"))))
+  matches the specialization A<bool, int>
+
+functionDecl(hasTemplateArgument(0, refersToType(asString("int"))))
+  matches the specialization f<int>
+
+ + Matcher<VariableArrayType>hasSizeExprMatcher<Expr> InnerMatcher
Matches VariableArrayType nodes that have a specific size
 expression.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 28ac54127383aa6eb9e09553d97fa1a262765587..81e9d0423f96aecced463f43e5ffadb4efde3288 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -51,10 +51,14 @@ C++ Specific Potentially Breaking Changes
 - The behavior controlled by the `-frelaxed-template-template-args` flag is now
   on by default, and the flag is deprecated. Until the flag is finally removed,
   it's negative spelling can be used to obtain compatibility with previous
-  versions of clang.
+  versions of clang. The deprecation warning for the negative spelling can be
+  disabled with `-Wno-deprecated-no-relaxed-template-template-args`.
 
 - Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).
 
+- Clang now performs semantic analysis for unary operators with dependent operands
+  that are known to be of non-class non-enumeration type prior to instantiation.
+
 ABI Changes in This Version
 ---------------------------
 - Fixed Microsoft name mangling of implicitly defined variables used for thread
@@ -114,6 +118,9 @@ Clang Frontend Potentially Breaking Changes
     $ clang --target= -print-target-triple
     
 
+- The ``hasTypeLoc`` AST matcher will no longer match a ``classTemplateSpecializationDecl``;
+  existing uses should switch to ``templateArgumentLoc`` or ``hasAnyTemplateArgumentLoc`` instead.
+
 What's New in Clang |release|?
 ==============================
 Some of the major new features and improvements to Clang are listed
@@ -219,6 +226,9 @@ Resolutions to C++ Defect Reports
 - Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
   (`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers `_).
 
+- Clang now allows attributes on concepts.
+  (`CWG2428: Deprecating a concept `_).
+
 - P0522 implementation is enabled by default in all language versions, and
   provisional wording for CWG2398 is implemented.
 
@@ -393,6 +403,10 @@ Attribute Changes in Clang
 - Clang now warns that the ``exclude_from_explicit_instantiation`` attribute
   is ignored when applied to a local class or a member thereof.
 
+- The ``clspv_libclc_builtin`` attribute has been added to allow clspv
+  (`OpenCL-C to Vulkan SPIR-V compiler `_) to identify functions coming from libclc
+  (`OpenCL-C builtin library `_).
+
 Improvements to Clang's diagnostics
 -----------------------------------
 - Clang now applies syntax highlighting to the code snippets it
@@ -484,9 +498,15 @@ Improvements to Clang's diagnostics
        }
      };
 
+- Clang emits a ``-Wparentheses`` warning for expressions with consecutive comparisons like ``x < y < z``.
+  Fixes #GH20456.
+
 Improvements to Clang's time-trace
 ----------------------------------
 
+- Clang now specifies that using ``auto`` in a lambda parameter is a C++14 extension when
+  appropriate. (`#46059: `_).
+
 Bug Fixes in This Version
 -------------------------
 - Clang's ``-Wundefined-func-template`` no longer warns on pure virtual
@@ -563,6 +583,12 @@ Bug Fixes in This Version
 - Clang will no longer emit a duplicate -Wunused-value warning for an expression
   `(A, B)` which evaluates to glvalue `B` that can be converted to non ODR-use. (#GH45783)
 
+- Clang now correctly disallows VLA type compound literals, e.g. ``(int[size]){}``,
+  as the C standard mandates. (#GH89835)
+
+- ``__is_array`` and ``__is_bounded_array`` no longer return ``true`` for
+  zero-sized arrays. Fixes (#GH54705).
+
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -710,11 +736,25 @@ Bug Fixes to C++ Support
 - Correctly treat the compound statement of an ``if consteval`` as an immediate context. Fixes (#GH91509).
 - When partial ordering alias templates against template template parameters,
   allow pack expansions when the alias has a fixed-size parameter list. Fixes (#GH62529).
+- Clang now ignores template parameters only used within the exception specification of candidate function
+  templates during partial ordering when deducing template arguments from a function declaration or when
+  taking the address of a function template.
+- Fix a bug with checking constrained non-type template parameters for equivalence. Fixes (#GH77377).
+- Fix a bug where the last argument was not considered when considering the most viable function for
+  explicit object argument member functions. Fixes (#GH92188).
+- Fix a C++11 crash when a non-const non-static member function is defined out-of-line with
+  the ``constexpr`` specifier. Fixes (#GH61004).
+- Clang no longer transforms dependent qualified names into implicit class member access expressions
+  until it can be determined whether the name is that of a non-static member.
+- Clang now correctly diagnoses when the current instantiation is used as an incomplete base class.
+- Clang no longer treats ``constexpr`` class scope function template specializations of non-static members
+  as implicitly ``const`` in language modes after C++11.
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 - Clang now properly preserves ``FoundDecls`` within a ``ConceptReference``. (#GH82628)
 - The presence of the ``typename`` keyword is now stored in ``TemplateTemplateParmDecl``.
+- Fixed malformed AST generated for anonymous union access in templates. (#GH90842)
 
 Miscellaneous Bug Fixes
 ^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt
index eaeadf2656b0bffc5378fcb5d9de2d14048f161c..dee51e402b687fa36f598a6ba22262cdb871e173 100644
--- a/clang/docs/tools/clang-formatted-files.txt
+++ b/clang/docs/tools/clang-formatted-files.txt
@@ -124,6 +124,7 @@ clang/include/clang/Analysis/Analyses/CFGReachabilityAnalysis.h
 clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
 clang/include/clang/Analysis/FlowSensitive/AdornedCFG.h
 clang/include/clang/Analysis/FlowSensitive/ASTOps.h
+clang/include/clang/Analysis/FlowSensitive/CNFFormula.h
 clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
 clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
 clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -252,7 +253,6 @@ clang/include/clang/Tooling/CompilationDatabasePluginRegistry.h
 clang/include/clang/Tooling/DiagnosticsYaml.h
 clang/include/clang/Tooling/Execution.h
 clang/include/clang/Tooling/JSONCompilationDatabase.h
-clang/include/clang/Tooling/NodeIntrospection.h
 clang/include/clang/Tooling/Refactoring.h
 clang/include/clang/Tooling/StandaloneExecution.h
 clang/include/clang/Tooling/ToolExecutorPluginRegistry.h
@@ -562,15 +562,11 @@ clang/lib/Tooling/Execution.cpp
 clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp
 clang/lib/Tooling/FixIt.cpp
 clang/lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp
-clang/lib/Tooling/NodeIntrospection.cpp
 clang/lib/Tooling/StandaloneExecution.cpp
 clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp
 clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
 clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
 clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
-clang/lib/Tooling/DumpTool/APIData.h
-clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h
-clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
 clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
 clang/lib/Tooling/Inclusions/IncludeStyle.cpp
 clang/lib/Tooling/Inclusions/StandardLibrary.cpp
@@ -626,6 +622,7 @@ clang/tools/libclang/CXCursor.h
 clang/tools/scan-build-py/tests/functional/src/include/clean-one.h
 clang/unittests/Analysis/CFGBuildResult.h
 clang/unittests/Analysis/MacroExpansionContextTest.cpp
+clang/unittests/Analysis/FlowSensitive/CNFFormula.cpp
 clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
 clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp
 clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp
@@ -637,6 +634,7 @@ clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
 clang/unittests/Analysis/FlowSensitive/TestingSupport.h
 clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp
 clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+clang/unittests/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
 clang/unittests/Analysis/FlowSensitive/WatchedLiteralsSolverTest.cpp
 clang/unittests/AST/ASTImporterFixtures.cpp
 clang/unittests/AST/ASTImporterFixtures.h
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index e03b11219478670277472f41b6c31cc31f2f6961..2ce2b810d3636498cb8f8fe27a427d22cb0ee612 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2611,7 +2611,7 @@ public:
   ///
   /// \returns if this is an array type, the completely unqualified array type
   /// that corresponds to it. Otherwise, returns T.getUnqualifiedType().
-  QualType getUnqualifiedArrayType(QualType T, Qualifiers &Quals);
+  QualType getUnqualifiedArrayType(QualType T, Qualifiers &Quals) const;
 
   /// Determine whether the given types are equivalent after
   /// cvr-qualifiers have been removed.
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index de8b923645f8d6418a1f8a8ae1a5ed80115b8016..5e485ccb85a1391c936b0ec803c96d927caa5033 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2096,13 +2096,12 @@ private:
   ///
   /// \param PointOfInstantiation point at which the function template
   /// specialization was first instantiated.
-  void setFunctionTemplateSpecialization(ASTContext &C,
-                                         FunctionTemplateDecl *Template,
-                                       const TemplateArgumentList *TemplateArgs,
-                                         void *InsertPos,
-                                         TemplateSpecializationKind TSK,
-                          const TemplateArgumentListInfo *TemplateArgsAsWritten,
-                                         SourceLocation PointOfInstantiation);
+  void setFunctionTemplateSpecialization(
+      ASTContext &C, FunctionTemplateDecl *Template,
+      TemplateArgumentList *TemplateArgs, void *InsertPos,
+      TemplateSpecializationKind TSK,
+      const TemplateArgumentListInfo *TemplateArgsAsWritten,
+      SourceLocation PointOfInstantiation);
 
   /// Specify that this record is an instantiation of the
   /// member function FD.
@@ -2981,12 +2980,12 @@ public:
   ///
   /// \param PointOfInstantiation point at which the function template
   /// specialization was first instantiated.
-  void setFunctionTemplateSpecialization(FunctionTemplateDecl *Template,
-                const TemplateArgumentList *TemplateArgs,
-                void *InsertPos,
-                TemplateSpecializationKind TSK = TSK_ImplicitInstantiation,
-                const TemplateArgumentListInfo *TemplateArgsAsWritten = nullptr,
-                SourceLocation PointOfInstantiation = SourceLocation()) {
+  void setFunctionTemplateSpecialization(
+      FunctionTemplateDecl *Template, TemplateArgumentList *TemplateArgs,
+      void *InsertPos,
+      TemplateSpecializationKind TSK = TSK_ImplicitInstantiation,
+      TemplateArgumentListInfo *TemplateArgsAsWritten = nullptr,
+      SourceLocation PointOfInstantiation = SourceLocation()) {
     setFunctionTemplateSpecialization(getASTContext(), Template, TemplateArgs,
                                       InsertPos, TSK, TemplateArgsAsWritten,
                                       PointOfInstantiation);
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 3ee03eebdb8ca45d48358b8e52d2cce85553c5bf..f3d6a321ecf104c279a0b855f4e21c46474e7596 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -478,7 +478,7 @@ class FunctionTemplateSpecializationInfo final
 public:
   /// The template arguments used to produce the function template
   /// specialization from the function template.
-  const TemplateArgumentList *TemplateArguments;
+  TemplateArgumentList *TemplateArguments;
 
   /// The template arguments as written in the sources, if provided.
   /// FIXME: Normally null; tail-allocate this.
@@ -491,7 +491,7 @@ public:
 private:
   FunctionTemplateSpecializationInfo(
       FunctionDecl *FD, FunctionTemplateDecl *Template,
-      TemplateSpecializationKind TSK, const TemplateArgumentList *TemplateArgs,
+      TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
       const ASTTemplateArgumentListInfo *TemplateArgsAsWritten,
       SourceLocation POI, MemberSpecializationInfo *MSInfo)
       : Function(FD, MSInfo ? true : false), Template(Template, TSK - 1),
@@ -511,8 +511,7 @@ public:
 
   static FunctionTemplateSpecializationInfo *
   Create(ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template,
-         TemplateSpecializationKind TSK,
-         const TemplateArgumentList *TemplateArgs,
+         TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
          const TemplateArgumentListInfo *TemplateArgsAsWritten,
          SourceLocation POI, MemberSpecializationInfo *MSInfo);
 
@@ -1776,6 +1775,25 @@ public:
   BuiltinTemplateKind getBuiltinTemplateKind() const { return BTK; }
 };
 
+/// Provides information about an explicit instantiation of a variable or class
+/// template.
+struct ExplicitInstantiationInfo {
+  /// The template arguments as written..
+  const ASTTemplateArgumentListInfo *TemplateArgsAsWritten = nullptr;
+
+  /// The location of the extern keyword.
+  SourceLocation ExternKeywordLoc;
+
+  /// The location of the template keyword.
+  SourceLocation TemplateKeywordLoc;
+
+  ExplicitInstantiationInfo() = default;
+};
+
+using SpecializationOrInstantiationInfo =
+    llvm::PointerUnion;
+
 /// Represents a class template specialization, which refers to
 /// a class template with a given set of template arguments.
 ///
@@ -1789,8 +1807,8 @@ public:
 /// template<>
 /// class array { }; // class template specialization array
 /// \endcode
-class ClassTemplateSpecializationDecl
-  : public CXXRecordDecl, public llvm::FoldingSetNode {
+class ClassTemplateSpecializationDecl : public CXXRecordDecl,
+                                        public llvm::FoldingSetNode {
   /// Structure that stores information about a class template
   /// specialization that was instantiated from a class template partial
   /// specialization.
@@ -1808,23 +1826,9 @@ class ClassTemplateSpecializationDecl
   llvm::PointerUnion
     SpecializedTemplate;
 
-  /// Further info for explicit template specialization/instantiation.
-  struct ExplicitSpecializationInfo {
-    /// The type-as-written.
-    TypeSourceInfo *TypeAsWritten = nullptr;
-
-    /// The location of the extern keyword.
-    SourceLocation ExternLoc;
-
-    /// The location of the template keyword.
-    SourceLocation TemplateKeywordLoc;
-
-    ExplicitSpecializationInfo() = default;
-  };
-
   /// Further info for explicit template specialization/instantiation.
   /// Does not apply to implicit specializations.
-  ExplicitSpecializationInfo *ExplicitInfo = nullptr;
+  SpecializationOrInstantiationInfo ExplicitInfo = nullptr;
 
   /// The template arguments used to describe this specialization.
   const TemplateArgumentList *TemplateArgs;
@@ -2001,44 +2005,49 @@ public:
     SpecializedTemplate = TemplDecl;
   }
 
-  /// Sets the type of this specialization as it was written by
-  /// the user. This will be a class template specialization type.
-  void setTypeAsWritten(TypeSourceInfo *T) {
-    if (!ExplicitInfo)
-      ExplicitInfo = new (getASTContext()) ExplicitSpecializationInfo;
-    ExplicitInfo->TypeAsWritten = T;
+  /// Retrieve the template argument list as written in the sources,
+  /// if any.
+  const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      return Info->TemplateArgsAsWritten;
+    return ExplicitInfo.get();
   }
 
-  /// Gets the type of this specialization as it was written by
-  /// the user, if it was so written.
-  TypeSourceInfo *getTypeAsWritten() const {
-    return ExplicitInfo ? ExplicitInfo->TypeAsWritten : nullptr;
+  /// Set the template argument list as written in the sources.
+  void
+  setTemplateArgsAsWritten(const ASTTemplateArgumentListInfo *ArgsWritten) {
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      Info->TemplateArgsAsWritten = ArgsWritten;
+    else
+      ExplicitInfo = ArgsWritten;
   }
 
-  /// Gets the location of the extern keyword, if present.
-  SourceLocation getExternLoc() const {
-    return ExplicitInfo ? ExplicitInfo->ExternLoc : SourceLocation();
+  /// Set the template argument list as written in the sources.
+  void setTemplateArgsAsWritten(const TemplateArgumentListInfo &ArgsInfo) {
+    setTemplateArgsAsWritten(
+        ASTTemplateArgumentListInfo::Create(getASTContext(), ArgsInfo));
   }
 
-  /// Sets the location of the extern keyword.
-  void setExternLoc(SourceLocation Loc) {
-    if (!ExplicitInfo)
-      ExplicitInfo = new (getASTContext()) ExplicitSpecializationInfo;
-    ExplicitInfo->ExternLoc = Loc;
+  /// Gets the location of the extern keyword, if present.
+  SourceLocation getExternKeywordLoc() const {
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      return Info->ExternKeywordLoc;
+    return SourceLocation();
   }
 
-  /// Sets the location of the template keyword.
-  void setTemplateKeywordLoc(SourceLocation Loc) {
-    if (!ExplicitInfo)
-      ExplicitInfo = new (getASTContext()) ExplicitSpecializationInfo;
-    ExplicitInfo->TemplateKeywordLoc = Loc;
-  }
+  /// Sets the location of the extern keyword.
+  void setExternKeywordLoc(SourceLocation Loc);
 
   /// Gets the location of the template keyword, if present.
   SourceLocation getTemplateKeywordLoc() const {
-    return ExplicitInfo ? ExplicitInfo->TemplateKeywordLoc : SourceLocation();
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      return Info->TemplateKeywordLoc;
+    return SourceLocation();
   }
 
+  /// Sets the location of the template keyword.
+  void setTemplateKeywordLoc(SourceLocation Loc);
+
   SourceRange getSourceRange() const override LLVM_READONLY;
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
@@ -2066,10 +2075,6 @@ class ClassTemplatePartialSpecializationDecl
   /// The list of template parameters
   TemplateParameterList* TemplateParams = nullptr;
 
-  /// The source info for the template arguments as written.
-  /// FIXME: redundant with TypeAsWritten?
-  const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
-
   /// The class template partial specialization from which this
   /// class template partial specialization was instantiated.
   ///
@@ -2078,15 +2083,11 @@ class ClassTemplatePartialSpecializationDecl
   llvm::PointerIntPair
       InstantiatedFromMember;
 
-  ClassTemplatePartialSpecializationDecl(ASTContext &Context, TagKind TK,
-                                         DeclContext *DC,
-                                         SourceLocation StartLoc,
-                                         SourceLocation IdLoc,
-                                         TemplateParameterList *Params,
-                                         ClassTemplateDecl *SpecializedTemplate,
-                                         ArrayRef Args,
-                               const ASTTemplateArgumentListInfo *ArgsAsWritten,
-                               ClassTemplatePartialSpecializationDecl *PrevDecl);
+  ClassTemplatePartialSpecializationDecl(
+      ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
+      SourceLocation IdLoc, TemplateParameterList *Params,
+      ClassTemplateDecl *SpecializedTemplate, ArrayRef Args,
+      ClassTemplatePartialSpecializationDecl *PrevDecl);
 
   ClassTemplatePartialSpecializationDecl(ASTContext &C)
     : ClassTemplateSpecializationDecl(C, ClassTemplatePartialSpecialization),
@@ -2101,11 +2102,8 @@ public:
   static ClassTemplatePartialSpecializationDecl *
   Create(ASTContext &Context, TagKind TK, DeclContext *DC,
          SourceLocation StartLoc, SourceLocation IdLoc,
-         TemplateParameterList *Params,
-         ClassTemplateDecl *SpecializedTemplate,
-         ArrayRef Args,
-         const TemplateArgumentListInfo &ArgInfos,
-         QualType CanonInjectedType,
+         TemplateParameterList *Params, ClassTemplateDecl *SpecializedTemplate,
+         ArrayRef Args, QualType CanonInjectedType,
          ClassTemplatePartialSpecializationDecl *PrevDecl);
 
   static ClassTemplatePartialSpecializationDecl *
@@ -2136,11 +2134,6 @@ public:
     return TemplateParams->hasAssociatedConstraints();
   }
 
-  /// Get the template arguments as written.
-  const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
-    return ArgsAsWritten;
-  }
-
   /// Retrieve the member class template partial specialization from
   /// which this particular class template partial specialization was
   /// instantiated.
@@ -2193,7 +2186,7 @@ public:
   /// template<> template
   /// struct X::Inner { /* ... */ };
   /// \endcode
-  bool isMemberSpecialization() {
+  bool isMemberSpecialization() const {
     const auto *First =
         cast(getFirstDecl());
     return First->InstantiatedFromMember.getInt();
@@ -2216,6 +2209,8 @@ public:
              ->getInjectedSpecializationType();
   }
 
+  SourceRange getSourceRange() const override LLVM_READONLY;
+
   void Profile(llvm::FoldingSetNodeID &ID) const {
     Profile(ID, getTemplateArgs().asArray(), getTemplateParameters(),
             getASTContext());
@@ -2613,27 +2608,12 @@ class VarTemplateSpecializationDecl : public VarDecl,
   llvm::PointerUnion
   SpecializedTemplate;
 
-  /// Further info for explicit template specialization/instantiation.
-  struct ExplicitSpecializationInfo {
-    /// The type-as-written.
-    TypeSourceInfo *TypeAsWritten = nullptr;
-
-    /// The location of the extern keyword.
-    SourceLocation ExternLoc;
-
-    /// The location of the template keyword.
-    SourceLocation TemplateKeywordLoc;
-
-    ExplicitSpecializationInfo() = default;
-  };
-
   /// Further info for explicit template specialization/instantiation.
   /// Does not apply to implicit specializations.
-  ExplicitSpecializationInfo *ExplicitInfo = nullptr;
+  SpecializationOrInstantiationInfo ExplicitInfo = nullptr;
 
   /// The template arguments used to describe this specialization.
   const TemplateArgumentList *TemplateArgs;
-  const ASTTemplateArgumentListInfo *TemplateArgsInfo = nullptr;
 
   /// The point where this template was instantiated (if any).
   SourceLocation PointOfInstantiation;
@@ -2687,14 +2667,6 @@ public:
   /// specialization.
   const TemplateArgumentList &getTemplateArgs() const { return *TemplateArgs; }
 
-  // TODO: Always set this when creating the new specialization?
-  void setTemplateArgsInfo(const TemplateArgumentListInfo &ArgsInfo);
-  void setTemplateArgsInfo(const ASTTemplateArgumentListInfo *ArgsInfo);
-
-  const ASTTemplateArgumentListInfo *getTemplateArgsInfo() const {
-    return TemplateArgsInfo;
-  }
-
   /// Determine the kind of specialization that this
   /// declaration represents.
   TemplateSpecializationKind getSpecializationKind() const {
@@ -2798,44 +2770,49 @@ public:
     SpecializedTemplate = TemplDecl;
   }
 
-  /// Sets the type of this specialization as it was written by
-  /// the user.
-  void setTypeAsWritten(TypeSourceInfo *T) {
-    if (!ExplicitInfo)
-      ExplicitInfo = new (getASTContext()) ExplicitSpecializationInfo;
-    ExplicitInfo->TypeAsWritten = T;
+  /// Retrieve the template argument list as written in the sources,
+  /// if any.
+  const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      return Info->TemplateArgsAsWritten;
+    return ExplicitInfo.get();
+  }
+
+  /// Set the template argument list as written in the sources.
+  void
+  setTemplateArgsAsWritten(const ASTTemplateArgumentListInfo *ArgsWritten) {
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      Info->TemplateArgsAsWritten = ArgsWritten;
+    else
+      ExplicitInfo = ArgsWritten;
   }
 
-  /// Gets the type of this specialization as it was written by
-  /// the user, if it was so written.
-  TypeSourceInfo *getTypeAsWritten() const {
-    return ExplicitInfo ? ExplicitInfo->TypeAsWritten : nullptr;
+  /// Set the template argument list as written in the sources.
+  void setTemplateArgsAsWritten(const TemplateArgumentListInfo &ArgsInfo) {
+    setTemplateArgsAsWritten(
+        ASTTemplateArgumentListInfo::Create(getASTContext(), ArgsInfo));
   }
 
   /// Gets the location of the extern keyword, if present.
-  SourceLocation getExternLoc() const {
-    return ExplicitInfo ? ExplicitInfo->ExternLoc : SourceLocation();
+  SourceLocation getExternKeywordLoc() const {
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      return Info->ExternKeywordLoc;
+    return SourceLocation();
   }
 
   /// Sets the location of the extern keyword.
-  void setExternLoc(SourceLocation Loc) {
-    if (!ExplicitInfo)
-      ExplicitInfo = new (getASTContext()) ExplicitSpecializationInfo;
-    ExplicitInfo->ExternLoc = Loc;
-  }
-
-  /// Sets the location of the template keyword.
-  void setTemplateKeywordLoc(SourceLocation Loc) {
-    if (!ExplicitInfo)
-      ExplicitInfo = new (getASTContext()) ExplicitSpecializationInfo;
-    ExplicitInfo->TemplateKeywordLoc = Loc;
-  }
+  void setExternKeywordLoc(SourceLocation Loc);
 
   /// Gets the location of the template keyword, if present.
   SourceLocation getTemplateKeywordLoc() const {
-    return ExplicitInfo ? ExplicitInfo->TemplateKeywordLoc : SourceLocation();
+    if (auto *Info = ExplicitInfo.dyn_cast())
+      return Info->TemplateKeywordLoc;
+    return SourceLocation();
   }
 
+  /// Sets the location of the template keyword.
+  void setTemplateKeywordLoc(SourceLocation Loc);
+
   SourceRange getSourceRange() const override LLVM_READONLY;
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
@@ -2863,10 +2840,6 @@ class VarTemplatePartialSpecializationDecl
   /// The list of template parameters
   TemplateParameterList *TemplateParams = nullptr;
 
-  /// The source info for the template arguments as written.
-  /// FIXME: redundant with TypeAsWritten?
-  const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
-
   /// The variable template partial specialization from which this
   /// variable template partial specialization was instantiated.
   ///
@@ -2879,8 +2852,7 @@ class VarTemplatePartialSpecializationDecl
       ASTContext &Context, DeclContext *DC, SourceLocation StartLoc,
       SourceLocation IdLoc, TemplateParameterList *Params,
       VarTemplateDecl *SpecializedTemplate, QualType T, TypeSourceInfo *TInfo,
-      StorageClass S, ArrayRef Args,
-      const ASTTemplateArgumentListInfo *ArgInfos);
+      StorageClass S, ArrayRef Args);
 
   VarTemplatePartialSpecializationDecl(ASTContext &Context)
       : VarTemplateSpecializationDecl(VarTemplatePartialSpecialization,
@@ -2897,8 +2869,8 @@ public:
   Create(ASTContext &Context, DeclContext *DC, SourceLocation StartLoc,
          SourceLocation IdLoc, TemplateParameterList *Params,
          VarTemplateDecl *SpecializedTemplate, QualType T,
-         TypeSourceInfo *TInfo, StorageClass S, ArrayRef Args,
-         const TemplateArgumentListInfo &ArgInfos);
+         TypeSourceInfo *TInfo, StorageClass S,
+         ArrayRef Args);
 
   static VarTemplatePartialSpecializationDecl *
   CreateDeserialized(ASTContext &C, GlobalDeclID ID);
@@ -2914,11 +2886,6 @@ public:
     return TemplateParams;
   }
 
-  /// Get the template arguments as written.
-  const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
-    return ArgsAsWritten;
-  }
-
   /// \brief All associated constraints of this partial specialization,
   /// including the requires clause and any constraints derived from
   /// constrained-parameters.
@@ -2981,7 +2948,7 @@ public:
   /// template<> template
   /// U* X::Inner = (T*)(0) + 1;
   /// \endcode
-  bool isMemberSpecialization() {
+  bool isMemberSpecialization() const {
     const auto *First =
         cast(getFirstDecl());
     return First->InstantiatedFromMember.getInt();
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index fac65628ffede8329b71c9d850502a743087a47c..dbf693611a7fa56edf7a9345d0613a3a1af71f59 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -4377,15 +4377,21 @@ class PackIndexingExpr final
   // The pack being indexed, followed by the index
   Stmt *SubExprs[2];
 
-  size_t TransformedExpressions;
+  // The size of the trailing expressions.
+  unsigned TransformedExpressions : 31;
+
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned ExpandedToEmptyPack : 1;
 
   PackIndexingExpr(QualType Type, SourceLocation EllipsisLoc,
                    SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr,
-                   ArrayRef SubstitutedExprs = {})
+                   ArrayRef SubstitutedExprs = {},
+                   bool ExpandedToEmptyPack = false)
       : Expr(PackIndexingExprClass, Type, VK_LValue, OK_Ordinary),
         EllipsisLoc(EllipsisLoc), RSquareLoc(RSquareLoc),
         SubExprs{PackIdExpr, IndexExpr},
-        TransformedExpressions(SubstitutedExprs.size()) {
+        TransformedExpressions(SubstitutedExprs.size()),
+        ExpandedToEmptyPack(ExpandedToEmptyPack) {
 
     auto *Exprs = getTrailingObjects();
     std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(),
@@ -4408,10 +4414,14 @@ public:
                                   SourceLocation EllipsisLoc,
                                   SourceLocation RSquareLoc, Expr *PackIdExpr,
                                   Expr *IndexExpr, std::optional Index,
-                                  ArrayRef SubstitutedExprs = {});
+                                  ArrayRef SubstitutedExprs = {},
+                                  bool ExpandedToEmptyPack = false);
   static PackIndexingExpr *CreateDeserialized(ASTContext &Context,
                                               unsigned NumTransformedExprs);
 
+  /// Determine if the expression was expanded to empty.
+  bool expandsToEmptyPack() const { return ExpandedToEmptyPack; }
+
   /// Determine the location of the 'sizeof' keyword.
   SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
 
@@ -4445,6 +4455,7 @@ public:
     return getTrailingObjects()[*Index];
   }
 
+  /// Return the trailing expressions, regardless of the expansion.
   ArrayRef getExpressions() const {
     return {getTrailingObjects(), TransformedExpressions};
   }
diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h
index 607a2b9d653678836264e0742840d24d1dc69ec4..28ff8c44bd25698564251cb69d67e165f95bbd36 100644
--- a/clang/include/clang/AST/OpenACCClause.h
+++ b/clang/include/clang/AST/OpenACCClause.h
@@ -677,6 +677,35 @@ public:
          ArrayRef VarList, SourceLocation EndLoc);
 };
 
+class OpenACCReductionClause final
+    : public OpenACCClauseWithVarList,
+      public llvm::TrailingObjects {
+  OpenACCReductionOperator Op;
+
+  OpenACCReductionClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
+                         OpenACCReductionOperator Operator,
+                         ArrayRef VarList, SourceLocation EndLoc)
+      : OpenACCClauseWithVarList(OpenACCClauseKind::Reduction, BeginLoc,
+                                 LParenLoc, EndLoc),
+        Op(Operator) {
+    std::uninitialized_copy(VarList.begin(), VarList.end(),
+                            getTrailingObjects());
+    setExprs(MutableArrayRef(getTrailingObjects(), VarList.size()));
+  }
+
+public:
+  static bool classof(const OpenACCClause *C) {
+    return C->getClauseKind() == OpenACCClauseKind::Reduction;
+  }
+
+  static OpenACCReductionClause *
+  Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
+         OpenACCReductionOperator Operator, ArrayRef VarList,
+         SourceLocation EndLoc);
+
+  OpenACCReductionOperator getReductionOp() const { return Op; }
+};
+
 template  class OpenACCClauseVisitor {
   Impl &getDerived() { return static_cast(*this); }
 
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index f9b145b4e86a5579dc13760ecce791468620c681..f5cefedb07e0eba340bd950b3c36769b5a39a774 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -736,13 +736,27 @@ bool RecursiveASTVisitor::TraverseDecl(Decl *D) {
 
   // As a syntax visitor, by default we want to ignore declarations for
   // implicit declarations (ones not typed explicitly by the user).
-  if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) {
-    // For an implicit template type parameter, its type constraints are not
-    // implicit and are not represented anywhere else. We still need to visit
-    // them.
-    if (auto *TTPD = dyn_cast(D))
-      return TraverseTemplateTypeParamDeclConstraints(TTPD);
-    return true;
+  if (!getDerived().shouldVisitImplicitCode()) {
+    if (D->isImplicit()) {
+      // For an implicit template type parameter, its type constraints are not
+      // implicit and are not represented anywhere else. We still need to visit
+      // them.
+      if (auto *TTPD = dyn_cast(D))
+        return TraverseTemplateTypeParamDeclConstraints(TTPD);
+      return true;
+    }
+
+    // Deduction guides for alias templates are always synthesized, so they
+    // should not be traversed unless shouldVisitImplicitCode() returns true.
+    //
+    // It's important to note that checking the implicit bit is not efficient
+    // for the alias case. For deduction guides synthesized from explicit
+    // user-defined deduction guides, we must maintain the explicit bit to
+    // ensure correct overload resolution.
+    if (auto *FTD = dyn_cast(D))
+      if (llvm::isa_and_present(
+              FTD->getDeclName().getCXXDeductionGuideTemplate()))
+        return true;
   }
 
   switch (D->getKind()) {
@@ -2030,6 +2044,15 @@ DEF_TRAVERSE_DECL(RecordDecl, { TRY_TO(TraverseRecordHelper(D)); })
 
 DEF_TRAVERSE_DECL(CXXRecordDecl, { TRY_TO(TraverseCXXRecordHelper(D)); })
 
+template 
+bool RecursiveASTVisitor::TraverseTemplateArgumentLocsHelper(
+    const TemplateArgumentLoc *TAL, unsigned Count) {
+  for (unsigned I = 0; I < Count; ++I) {
+    TRY_TO(TraverseTemplateArgumentLoc(TAL[I]));
+  }
+  return true;
+}
+
 #define DEF_TRAVERSE_TMPL_SPEC_DECL(TMPLDECLKIND, DECLKIND)                    \
   DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplateSpecializationDecl, {                \
     /* For implicit instantiations ("set x;"), we don't want to           \
@@ -2039,9 +2062,12 @@ DEF_TRAVERSE_DECL(CXXRecordDecl, { TRY_TO(TraverseCXXRecordHelper(D)); })
        TemplateSpecializationType).  For explicit instantiations               \
        ("template set;"), we do need a callback, since this               \
        is the only callback that's made for this instantiation.                \
-       We use getTypeAsWritten() to distinguish. */                            \
-    if (TypeSourceInfo *TSI = D->getTypeAsWritten())                           \
-      TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));                              \
+       We use getTemplateArgsAsWritten() to distinguish. */                    \
+    if (const auto *ArgsWritten = D->getTemplateArgsAsWritten()) {             \
+      /* The args that remains unspecialized. */                               \
+      TRY_TO(TraverseTemplateArgumentLocsHelper(                               \
+          ArgsWritten->getTemplateArgs(), ArgsWritten->NumTemplateArgs));      \
+    }                                                                          \
                                                                                \
     if (getDerived().shouldVisitTemplateInstantiations() ||                    \
         D->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {    \
@@ -2061,15 +2087,6 @@ DEF_TRAVERSE_DECL(CXXRecordDecl, { TRY_TO(TraverseCXXRecordHelper(D)); })
 DEF_TRAVERSE_TMPL_SPEC_DECL(Class, CXXRecord)
 DEF_TRAVERSE_TMPL_SPEC_DECL(Var, Var)
 
-template 
-bool RecursiveASTVisitor::TraverseTemplateArgumentLocsHelper(
-    const TemplateArgumentLoc *TAL, unsigned Count) {
-  for (unsigned I = 0; I < Count; ++I) {
-    TRY_TO(TraverseTemplateArgumentLoc(TAL[I]));
-  }
-  return true;
-}
-
 #define DEF_TRAVERSE_TMPL_PART_SPEC_DECL(TMPLDECLKIND, DECLKIND)               \
   DEF_TRAVERSE_DECL(TMPLDECLKIND##TemplatePartialSpecializationDecl, {         \
     /* The partial specialization. */                                          \
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index e6643469e0b3344937d69beeee77e37d8bfed37e..9a5c6e8d562c34a8a19cf26f79b1df442e1901fd 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2523,6 +2523,7 @@ public:
   bool isVectorType() const;                    // GCC vector type.
   bool isExtVectorType() const;                 // Extended vector type.
   bool isExtVectorBoolType() const;             // Extended vector type with bool element.
+  bool isSubscriptableVectorType() const;
   bool isMatrixType() const;                    // Matrix type.
   bool isConstantMatrixType() const;            // Constant matrix type.
   bool isDependentAddressSpaceType() const;     // value-dependent address space qualifier
@@ -7729,6 +7730,10 @@ inline bool Type::isExtVectorBoolType() const {
   return cast(CanonicalType)->getElementType()->isBooleanType();
 }
 
+inline bool Type::isSubscriptableVectorType() const {
+  return isVectorType() || isSveVLSBuiltinType();
+}
+
 inline bool Type::isMatrixType() const {
   return isa(CanonicalType);
 }
@@ -8044,7 +8049,10 @@ inline bool Type::isUndeducedType() const {
 /// Determines whether this is a type for which one can define
 /// an overloaded operator.
 inline bool Type::isOverloadableType() const {
-  return isDependentType() || isRecordType() || isEnumeralType();
+  if (!CanonicalType->isDependentType())
+    return isRecordType() || isEnumeralType();
+  return !isArrayType() && !isFunctionType() && !isAnyPointerType() &&
+         !isMemberPointerType();
 }
 
 /// Determines whether this type is written as a typedef-name.
diff --git a/clang/include/clang/AST/VTTBuilder.h b/clang/include/clang/AST/VTTBuilder.h
index 4acbc1f9e96b2838274721637f5cf30bd1a8c3ec..3c19e61a8701ca8442844181921a298b480382cf 100644
--- a/clang/include/clang/AST/VTTBuilder.h
+++ b/clang/include/clang/AST/VTTBuilder.h
@@ -92,7 +92,7 @@ class VTTBuilder {
   using AddressPointsMapTy = llvm::DenseMap;
 
   /// The sub-VTT indices for the bases of the most derived class.
-  llvm::DenseMap SubVTTIndicies;
+  llvm::DenseMap SubVTTIndices;
 
   /// The secondary virtual pointer indices of all subobjects of
   /// the most derived class.
@@ -148,8 +148,8 @@ public:
   }
 
   /// Returns a reference to the sub-VTT indices.
-  const llvm::DenseMap &getSubVTTIndicies() const {
-    return SubVTTIndicies;
+  const llvm::DenseMap &getSubVTTIndices() const {
+    return SubVTTIndices;
   }
 
   /// Returns a reference to the secondary virtual pointer indices.
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 8a2bbfff9e9e6b0025c5af3cb7d94c0017038efa..0f3257db6f415fd7e8a6cda8f34f9cbb46a2df89 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -764,9 +764,9 @@ AST_POLYMORPHIC_MATCHER(isImplicit,
   return Node.isImplicit();
 }
 
-/// Matches classTemplateSpecializations, templateSpecializationType and
-/// functionDecl that have at least one TemplateArgument matching the given
-/// InnerMatcher.
+/// Matches templateSpecializationTypes, class template specializations,
+/// variable template specializations, and function template specializations
+/// that have at least one TemplateArgument matching the given InnerMatcher.
 ///
 /// Given
 /// \code
@@ -788,8 +788,8 @@ AST_POLYMORPHIC_MATCHER(isImplicit,
 AST_POLYMORPHIC_MATCHER_P(
     hasAnyTemplateArgument,
     AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
-                                    TemplateSpecializationType,
-                                    FunctionDecl),
+                                    VarTemplateSpecializationDecl, FunctionDecl,
+                                    TemplateSpecializationType),
     internal::Matcher, InnerMatcher) {
   ArrayRef List =
       internal::getTemplateSpecializationArgs(Node);
@@ -1047,8 +1047,9 @@ AST_MATCHER(Expr, isTypeDependent) { return Node.isTypeDependent(); }
 /// expr(isValueDependent()) matches return Size
 AST_MATCHER(Expr, isValueDependent) { return Node.isValueDependent(); }
 
-/// Matches classTemplateSpecializations, templateSpecializationType and
-/// functionDecl where the n'th TemplateArgument matches the given InnerMatcher.
+/// Matches templateSpecializationType, class template specializations,
+/// variable template specializations, and function template specializations
+/// where the n'th TemplateArgument matches the given InnerMatcher.
 ///
 /// Given
 /// \code
@@ -1068,8 +1069,8 @@ AST_MATCHER(Expr, isValueDependent) { return Node.isValueDependent(); }
 AST_POLYMORPHIC_MATCHER_P2(
     hasTemplateArgument,
     AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
-                                    TemplateSpecializationType,
-                                    FunctionDecl),
+                                    VarTemplateSpecializationDecl, FunctionDecl,
+                                    TemplateSpecializationType),
     unsigned, N, internal::Matcher, InnerMatcher) {
   ArrayRef List =
       internal::getTemplateSpecializationArgs(Node);
@@ -4066,7 +4067,7 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(
 ///   Matcher, Matcher,
 ///   Matcher, Matcher,
 ///   Matcher,
-///   Matcher, Matcher,
+///   Matcher,
 ///   Matcher, Matcher,
 ///   Matcher, Matcher,
 ///   Matcher
@@ -4075,9 +4076,8 @@ AST_POLYMORPHIC_MATCHER_P(
     AST_POLYMORPHIC_SUPPORTED_TYPES(
         BlockDecl, CXXBaseSpecifier, CXXCtorInitializer, CXXFunctionalCastExpr,
         CXXNewExpr, CXXTemporaryObjectExpr, CXXUnresolvedConstructExpr,
-        ClassTemplateSpecializationDecl, CompoundLiteralExpr, DeclaratorDecl,
-        ExplicitCastExpr, ObjCPropertyDecl, TemplateArgumentLoc,
-        TypedefNameDecl),
+        CompoundLiteralExpr, DeclaratorDecl, ExplicitCastExpr, ObjCPropertyDecl,
+        TemplateArgumentLoc, TypedefNameDecl),
     internal::Matcher, Inner) {
   TypeSourceInfo *source = internal::GetTypeSourceInfo(Node);
   if (source == nullptr) {
@@ -5304,9 +5304,10 @@ AST_POLYMORPHIC_MATCHER_P(parameterCountIs,
   return Node.getNumParams() == N;
 }
 
-/// Matches classTemplateSpecialization, templateSpecializationType and
-/// functionDecl nodes where the template argument matches the inner matcher.
-/// This matcher may produce multiple matches.
+/// Matches templateSpecializationType, class template specialization,
+/// variable template specialization, and function template specialization
+/// nodes where the template argument matches the inner matcher. This matcher
+/// may produce multiple matches.
 ///
 /// Given
 /// \code
@@ -5330,7 +5331,8 @@ AST_POLYMORPHIC_MATCHER_P(parameterCountIs,
 AST_POLYMORPHIC_MATCHER_P(
     forEachTemplateArgument,
     AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
-                                    TemplateSpecializationType, FunctionDecl),
+                                    VarTemplateSpecializationDecl, FunctionDecl,
+                                    TemplateSpecializationType),
     internal::Matcher, InnerMatcher) {
   ArrayRef TemplateArgs =
       clang::ast_matchers::internal::getTemplateSpecializationArgs(Node);
@@ -6905,8 +6907,10 @@ extern const internal::VariadicDynCastAllOfMatcher<
     TypeLoc, TemplateSpecializationTypeLoc>
     templateSpecializationTypeLoc;
 
-/// Matches template specialization `TypeLoc`s that have at least one
-/// `TemplateArgumentLoc` matching the given `InnerMatcher`.
+/// Matches template specialization `TypeLoc`s, class template specializations,
+/// variable template specializations, and function template specializations
+/// that have at least one `TemplateArgumentLoc` matching the given
+/// `InnerMatcher`.
 ///
 /// Given
 /// \code
@@ -6916,20 +6920,21 @@ extern const internal::VariadicDynCastAllOfMatcher<
 /// varDecl(hasTypeLoc(templateSpecializationTypeLoc(hasAnyTemplateArgumentLoc(
 ///   hasTypeLoc(loc(asString("int")))))))
 ///   matches `A a`.
-AST_MATCHER_P(TemplateSpecializationTypeLoc, hasAnyTemplateArgumentLoc,
-              internal::Matcher, InnerMatcher) {
-  for (unsigned Index = 0, N = Node.getNumArgs(); Index < N; ++Index) {
-    clang::ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
-    if (InnerMatcher.matches(Node.getArgLoc(Index), Finder, &Result)) {
-      *Builder = std::move(Result);
-      return true;
-    }
-  }
+AST_POLYMORPHIC_MATCHER_P(
+    hasAnyTemplateArgumentLoc,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
+                                    VarTemplateSpecializationDecl, FunctionDecl,
+                                    DeclRefExpr, TemplateSpecializationTypeLoc),
+    internal::Matcher, InnerMatcher) {
+  auto Args = internal::getTemplateArgsWritten(Node);
+  return matchesFirstInRange(InnerMatcher, Args.begin(), Args.end(), Finder,
+                             Builder) != Args.end();
   return false;
 }
 
-/// Matches template specialization `TypeLoc`s where the n'th
-/// `TemplateArgumentLoc` matches the given `InnerMatcher`.
+/// Matches template specialization `TypeLoc`s, class template specializations,
+/// variable template specializations, and function template specializations
+/// where the n'th `TemplateArgumentLoc` matches the given `InnerMatcher`.
 ///
 /// Given
 /// \code
@@ -6942,10 +6947,13 @@ AST_MATCHER_P(TemplateSpecializationTypeLoc, hasAnyTemplateArgumentLoc,
 ///   matches `A b`, but not `A c`.
 AST_POLYMORPHIC_MATCHER_P2(
     hasTemplateArgumentLoc,
-    AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr, TemplateSpecializationTypeLoc),
+    AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
+                                    VarTemplateSpecializationDecl, FunctionDecl,
+                                    DeclRefExpr, TemplateSpecializationTypeLoc),
     unsigned, Index, internal::Matcher, InnerMatcher) {
-  return internal::MatchTemplateArgLocAt(Node, Index, InnerMatcher, Finder,
-                                         Builder);
+  auto Args = internal::getTemplateArgsWritten(Node);
+  return Index < Args.size() &&
+         InnerMatcher.matches(Args[Index], Finder, Builder);
 }
 
 /// Matches C or C++ elaborated `TypeLoc`s.
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 47d912c73dd7eb491eeef5103012233f84a838ae..c1cc63fdb7433f7c01a771521bde51f41e8a24b0 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -186,10 +186,6 @@ inline TypeSourceInfo *GetTypeSourceInfo(const BlockDecl &Node) {
 inline TypeSourceInfo *GetTypeSourceInfo(const CXXNewExpr &Node) {
   return Node.getAllocatedTypeSourceInfo();
 }
-inline TypeSourceInfo *
-GetTypeSourceInfo(const ClassTemplateSpecializationDecl &Node) {
-  return Node.getTypeAsWritten();
-}
 
 /// Unifies obtaining the FunctionProtoType pointer from both
 /// FunctionProtoType and FunctionDecl nodes..
@@ -1939,6 +1935,11 @@ getTemplateSpecializationArgs(const ClassTemplateSpecializationDecl &D) {
   return D.getTemplateArgs().asArray();
 }
 
+inline ArrayRef
+getTemplateSpecializationArgs(const VarTemplateSpecializationDecl &D) {
+  return D.getTemplateArgs().asArray();
+}
+
 inline ArrayRef
 getTemplateSpecializationArgs(const TemplateSpecializationType &T) {
   return T.template_arguments();
@@ -1948,7 +1949,46 @@ inline ArrayRef
 getTemplateSpecializationArgs(const FunctionDecl &FD) {
   if (const auto* TemplateArgs = FD.getTemplateSpecializationArgs())
     return TemplateArgs->asArray();
-  return ArrayRef();
+  return std::nullopt;
+}
+
+inline ArrayRef
+getTemplateArgsWritten(const ClassTemplateSpecializationDecl &D) {
+  if (const ASTTemplateArgumentListInfo *Args = D.getTemplateArgsAsWritten())
+    return Args->arguments();
+  return std::nullopt;
+}
+
+inline ArrayRef
+getTemplateArgsWritten(const VarTemplateSpecializationDecl &D) {
+  if (const ASTTemplateArgumentListInfo *Args = D.getTemplateArgsAsWritten())
+    return Args->arguments();
+  return std::nullopt;
+}
+
+inline ArrayRef
+getTemplateArgsWritten(const FunctionDecl &FD) {
+  if (const auto *Args = FD.getTemplateSpecializationArgsAsWritten())
+    return Args->arguments();
+  return std::nullopt;
+}
+
+inline ArrayRef
+getTemplateArgsWritten(const DeclRefExpr &DRE) {
+  if (const auto *Args = DRE.getTemplateArgs())
+    return {Args, DRE.getNumTemplateArgs()};
+  return std::nullopt;
+}
+
+inline SmallVector
+getTemplateArgsWritten(const TemplateSpecializationTypeLoc &T) {
+  SmallVector Args;
+  if (!T.isNull()) {
+    Args.reserve(T.getNumArgs());
+    for (unsigned I = 0; I < T.getNumArgs(); ++I)
+      Args.emplace_back(T.getArgLoc(I));
+  }
+  return Args;
 }
 
 struct NotEqualsBoundNodePredicate {
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 5d16dcc824c50c581b7a109d8d0fa9f9b3b19886..228b4ae1e3e1155008c33cd0691e36a63b155744 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -106,6 +106,11 @@ public:
   virtual void handleUnsafeOperation(const Stmt *Operation,
                                      bool IsRelatedToDecl, ASTContext &Ctx) = 0;
 
+  /// Invoked when an unsafe operation with a std container is found.
+  virtual void handleUnsafeOperationInContainer(const Stmt *Operation,
+                                                bool IsRelatedToDecl,
+                                                ASTContext &Ctx) = 0;
+
   /// Invoked when a fix is suggested against a variable. This function groups
   /// all variables that must be fixed together (i.e their types must be changed
   /// to the same target type to prevent type mismatches) into a single fixit.
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
index 3273c642eed517cacbd16252fc78f79e656a5de2..242ad763ba62b9a806f605f97022a8280ad3d152 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def
@@ -36,6 +36,7 @@ WARNING_GADGET(Decrement)
 WARNING_GADGET(ArraySubscript)
 WARNING_GADGET(PointerArithmetic)
 WARNING_GADGET(UnsafeBufferUsageAttr)
+WARNING_GADGET(UnsafeBufferUsageCtorAttr)
 WARNING_GADGET(DataInvocation)
 WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)`
 FIXABLE_GADGET(ULCArraySubscript)          // `DRE[any]` in an Unspecified Lvalue Context
diff --git a/clang/include/clang/Analysis/FlowSensitive/CNFFormula.h b/clang/include/clang/Analysis/FlowSensitive/CNFFormula.h
new file mode 100644
index 0000000000000000000000000000000000000000..fb13e774c67fe798e84ebb3178d7aa589f7af6ef
--- /dev/null
+++ b/clang/include/clang/Analysis/FlowSensitive/CNFFormula.h
@@ -0,0 +1,179 @@
+//===- CNFFormula.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
+//
+//===----------------------------------------------------------------------===//
+//
+//  A representation of a boolean formula in 3-CNF.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_CNFFORMULA_H
+#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_CNFFORMULA_H
+
+#include 
+#include 
+
+#include "clang/Analysis/FlowSensitive/Formula.h"
+
+namespace clang {
+namespace dataflow {
+
+/// Boolean variables are represented as positive integers.
+using Variable = uint32_t;
+
+/// A null boolean variable is used as a placeholder in various data structures
+/// and algorithms.
+constexpr Variable NullVar = 0;
+
+/// Literals are represented as positive integers. Specifically, for a boolean
+/// variable `V` that is represented as the positive integer `I`, the positive
+/// literal `V` is represented as the integer `2*I` and the negative literal
+/// `!V` is represented as the integer `2*I+1`.
+using Literal = uint32_t;
+
+/// A null literal is used as a placeholder in various data structures and
+/// algorithms.
+constexpr Literal NullLit = 0;
+
+/// Clause identifiers are represented as positive integers.
+using ClauseID = uint32_t;
+
+/// A null clause identifier is used as a placeholder in various data structures
+/// and algorithms.
+constexpr ClauseID NullClause = 0;
+
+/// Returns the positive literal `V`.
+inline constexpr Literal posLit(Variable V) { return 2 * V; }
+
+/// Returns the negative literal `!V`.
+inline constexpr Literal negLit(Variable V) { return 2 * V + 1; }
+
+/// Returns whether `L` is a positive literal.
+inline constexpr bool isPosLit(Literal L) { return 0 == (L & 1); }
+
+/// Returns whether `L` is a negative literal.
+inline constexpr bool isNegLit(Literal L) { return 1 == (L & 1); }
+
+/// Returns the negated literal `!L`.
+inline constexpr Literal notLit(Literal L) { return L ^ 1; }
+
+/// Returns the variable of `L`.
+inline constexpr Variable var(Literal L) { return L >> 1; }
+
+/// A boolean formula in 3-CNF (conjunctive normal form with at most 3 literals
+/// per clause).
+class CNFFormula {
+  /// `LargestVar` is equal to the largest positive integer that represents a
+  /// variable in the formula.
+  const Variable LargestVar;
+
+  /// Literals of all clauses in the formula.
+  ///
+  /// The element at index 0 stands for the literal in the null clause. It is
+  /// set to 0 and isn't used. Literals of clauses in the formula start from the
+  /// element at index 1.
+  ///
+  /// For example, for the formula `(L1 v L2) ^ (L2 v L3 v L4)` the elements of
+  /// `Clauses` will be `[0, L1, L2, L2, L3, L4]`.
+  std::vector Clauses;
+
+  /// Start indices of clauses of the formula in `Clauses`.
+  ///
+  /// The element at index 0 stands for the start index of the null clause. It
+  /// is set to 0 and isn't used. Start indices of clauses in the formula start
+  /// from the element at index 1.
+  ///
+  /// For example, for the formula `(L1 v L2) ^ (L2 v L3 v L4)` the elements of
+  /// `ClauseStarts` will be `[0, 1, 3]`. Note that the literals of the first
+  /// clause always start at index 1. The start index for the literals of the
+  /// second clause depends on the size of the first clause and so on.
+  std::vector ClauseStarts;
+
+  /// Indicates that we already know the formula is unsatisfiable.
+  /// During construction, we catch simple cases of conflicting unit-clauses.
+  bool KnownContradictory;
+
+public:
+  explicit CNFFormula(Variable LargestVar);
+
+  /// Adds the `L1 v ... v Ln` clause to the formula.
+  /// Requirements:
+  ///
+  ///  `Li` must not be `NullLit`.
+  ///
+  ///  All literals in the input that are not `NullLit` must be distinct.
+  void addClause(ArrayRef lits);
+
+  /// Returns whether the formula is known to be contradictory.
+  /// This is the case if any of the clauses is empty.
+  bool knownContradictory() const { return KnownContradictory; }
+
+  /// Returns the largest variable in the formula.
+  Variable largestVar() const { return LargestVar; }
+
+  /// Returns the number of clauses in the formula.
+  /// Valid clause IDs are in the range [1, `numClauses()`].
+  ClauseID numClauses() const { return ClauseStarts.size() - 1; }
+
+  /// Returns the number of literals in clause `C`.
+  size_t clauseSize(ClauseID C) const {
+    return C == ClauseStarts.size() - 1 ? Clauses.size() - ClauseStarts[C]
+                                        : ClauseStarts[C + 1] - ClauseStarts[C];
+  }
+
+  /// Returns the literals of clause `C`.
+  /// If `knownContradictory()` is false, each clause has at least one literal.
+  llvm::ArrayRef clauseLiterals(ClauseID C) const {
+    size_t S = clauseSize(C);
+    if (S == 0)
+      return llvm::ArrayRef();
+    return llvm::ArrayRef(&Clauses[ClauseStarts[C]], S);
+  }
+
+  /// An iterator over all literals of all clauses in the formula.
+  /// The iterator allows mutation of the literal through the `*` operator.
+  /// This is to support solvers that mutate the formula during solving.
+  class Iterator {
+    friend class CNFFormula;
+    CNFFormula *CNF;
+    size_t Idx;
+    Iterator(CNFFormula *CNF, size_t Idx) : CNF(CNF), Idx(Idx) {}
+
+  public:
+    Iterator(const Iterator &) = default;
+    Iterator &operator=(const Iterator &) = default;
+
+    Iterator &operator++() {
+      ++Idx;
+      assert(Idx < CNF->Clauses.size() && "Iterator out of bounds");
+      return *this;
+    }
+
+    Iterator next() const {
+      Iterator I = *this;
+      ++I;
+      return I;
+    }
+
+    Literal &operator*() const { return CNF->Clauses[Idx]; }
+  };
+  friend class Iterator;
+
+  /// Returns an iterator to the first literal of clause `C`.
+  Iterator startOfClause(ClauseID C) { return Iterator(this, ClauseStarts[C]); }
+};
+
+/// Converts the conjunction of `Vals` into a formula in conjunctive normal
+/// form where each clause has at least one and at most three literals.
+/// `Atomics` is populated with a mapping from `Variables` to the corresponding
+/// `Atom`s for atomic booleans in the input formulas.
+CNFFormula buildCNF(const llvm::ArrayRef &Formulas,
+                    llvm::DenseMap &Atomics);
+
+} // namespace dataflow
+} // namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_CNFFORMULA_H
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index cdf89c7def2c9185803af843f14fcbd88e59f5f5..097ff2bdfe7ada5a89e3842cbc848fcdd5df4ad4 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -19,6 +19,7 @@
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/Type.h"
+#include "clang/Analysis/FlowSensitive/ASTOps.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/Formula.h"
@@ -30,9 +31,11 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
+#include 
 #include 
 #include 
 #include 
+#include 
 
 namespace clang {
 namespace dataflow {
@@ -155,7 +158,28 @@ public:
 
   /// Creates an environment that uses `DACtx` to store objects that encompass
   /// the state of a program.
-  explicit Environment(DataflowAnalysisContext &DACtx);
+  explicit Environment(DataflowAnalysisContext &DACtx)
+      : DACtx(&DACtx),
+        FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {}
+
+  /// Creates an environment that uses `DACtx` to store objects that encompass
+  /// the state of a program, with `S` as the statement to analyze.
+  Environment(DataflowAnalysisContext &DACtx, Stmt &S) : Environment(DACtx) {
+    InitialTargetStmt = &S;
+  }
+
+  /// Creates an environment that uses `DACtx` to store objects that encompass
+  /// the state of a program, with `FD` as the function to analyze.
+  ///
+  /// Requirements:
+  ///
+  ///  The function must have a body, i.e.
+  ///  `FunctionDecl::doesThisDecalarationHaveABody()` must be true.
+  Environment(DataflowAnalysisContext &DACtx, const FunctionDecl &FD)
+      : Environment(DACtx, *FD.getBody()) {
+    assert(FD.doesThisDeclarationHaveABody());
+    InitialTargetFunc = &FD;
+  }
 
   // Copy-constructor is private, Environments should not be copied. See fork().
   Environment &operator=(const Environment &Other) = delete;
@@ -163,24 +187,11 @@ public:
   Environment(Environment &&Other) = default;
   Environment &operator=(Environment &&Other) = default;
 
-  /// Creates an environment that uses `DACtx` to store objects that encompass
-  /// the state of a program.
-  ///
-  /// If `DeclCtx` is a function, initializes the environment with symbolic
-  /// representations of the function parameters.
-  ///
-  /// If `DeclCtx` is a non-static member function, initializes the environment
-  /// with a symbolic representation of the `this` pointee.
-  Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx);
-
   /// Assigns storage locations and values to all parameters, captures, global
-  /// variables, fields and functions referenced in the function currently being
-  /// analyzed.
-  ///
-  /// Requirements:
+  /// variables, fields and functions referenced in the `Stmt` or `FunctionDecl`
+  /// passed to the constructor.
   ///
-  ///  The function must have a body, i.e.
-  ///  `FunctionDecl::doesThisDecalarationHaveABody()` must be true.
+  /// If no `Stmt` or `FunctionDecl` was supplied, this function does nothing.
   void initialize();
 
   /// Returns a new environment that is a copy of this one.
@@ -193,7 +204,7 @@ public:
   /// forked flow condition references the original).
   Environment fork() const;
 
-  /// Creates and returns an environment to use for an inline analysis  of the
+  /// Creates and returns an environment to use for an inline analysis of the
   /// callee. Uses the storage location from each argument in the `Call` as the
   /// storage location for the corresponding parameter in the callee.
   ///
@@ -365,46 +376,51 @@ public:
   RecordStorageLocation &
   getResultObjectLocation(const Expr &RecordPRValue) const;
 
-  /// Returns the return value of the current function. This can be null if:
+  /// Returns the return value of the function currently being analyzed.
+  /// This can be null if:
   /// - The function has a void return type
   /// - No return value could be determined for the function, for example
   ///   because it calls a function without a body.
   ///
   /// Requirements:
-  ///  The current function must have a non-reference return type.
+  ///  The current analysis target must be a function and must have a
+  ///  non-reference return type.
   Value *getReturnValue() const {
     assert(getCurrentFunc() != nullptr &&
            !getCurrentFunc()->getReturnType()->isReferenceType());
     return ReturnVal;
   }
 
-  /// Returns the storage location for the reference returned by the current
-  /// function. This can be null if function doesn't return a single consistent
-  /// reference.
+  /// Returns the storage location for the reference returned by the function
+  /// currently being analyzed. This can be null if the function doesn't return
+  /// a single consistent reference.
   ///
   /// Requirements:
-  ///  The current function must have a reference return type.
+  ///  The current analysis target must be a function and must have a reference
+  ///  return type.
   StorageLocation *getReturnStorageLocation() const {
     assert(getCurrentFunc() != nullptr &&
            getCurrentFunc()->getReturnType()->isReferenceType());
     return ReturnLoc;
   }
 
-  /// Sets the return value of the current function.
+  /// Sets the return value of the function currently being analyzed.
   ///
   /// Requirements:
-  ///  The current function must have a non-reference return type.
+  ///  The current analysis target must be a function and must have a
+  ///  non-reference return type.
   void setReturnValue(Value *Val) {
     assert(getCurrentFunc() != nullptr &&
            !getCurrentFunc()->getReturnType()->isReferenceType());
     ReturnVal = Val;
   }
 
-  /// Sets the storage location for the reference returned by the current
-  /// function.
+  /// Sets the storage location for the reference returned by the function
+  /// currently being analyzed.
   ///
   /// Requirements:
-  ///  The current function must have a reference return type.
+  ///  The current analysis target must be a function and must have a reference
+  ///  return type.
   void setReturnStorageLocation(StorageLocation *Loc) {
     assert(getCurrentFunc() != nullptr &&
            getCurrentFunc()->getReturnType()->isReferenceType());
@@ -641,23 +657,21 @@ public:
   /// (or the flow condition is overly constraining) or if the solver times out.
   bool allows(const Formula &) const;
 
-  /// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
-  /// returns null.
-  const DeclContext *getDeclCtx() const { return CallStack.back(); }
-
   /// Returns the function currently being analyzed, or null if the code being
   /// analyzed isn't part of a function.
   const FunctionDecl *getCurrentFunc() const {
-    return dyn_cast(getDeclCtx());
+    return CallStack.empty() ? InitialTargetFunc : CallStack.back();
   }
 
-  /// Returns the size of the call stack.
+  /// Returns the size of the call stack, not counting the initial analysis
+  /// target.
   size_t callStackSize() const { return CallStack.size(); }
 
   /// Returns whether this `Environment` can be extended to analyze the given
-  /// `Callee` (i.e. if `pushCall` can be used), with recursion disallowed and a
-  /// given `MaxDepth`.
-  bool canDescend(unsigned MaxDepth, const DeclContext *Callee) const;
+  /// `Callee` (i.e. if `pushCall` can be used).
+  /// Recursion is not allowed. `MaxDepth` is the maximum size of the call stack
+  /// (i.e. the maximum value that `callStackSize()` may assume after the call).
+  bool canDescend(unsigned MaxDepth, const FunctionDecl *Callee) const;
 
   /// Returns the `DataflowAnalysisContext` used by the environment.
   DataflowAnalysisContext &getDataflowAnalysisContext() const { return *DACtx; }
@@ -719,8 +733,8 @@ private:
                         ArrayRef Args);
 
   /// Assigns storage locations and values to all global variables, fields
-  /// and functions referenced in `FuncDecl`. `FuncDecl` must have a body.
-  void initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl);
+  /// and functions in `Referenced`.
+  void initFieldsGlobalsAndFuncs(const ReferencedDecls &Referenced);
 
   static PrValueToResultObject
   buildResultObjectMap(DataflowAnalysisContext *DACtx,
@@ -728,6 +742,11 @@ private:
                        RecordStorageLocation *ThisPointeeLoc,
                        RecordStorageLocation *LocForRecordReturnVal);
 
+  static PrValueToResultObject
+  buildResultObjectMap(DataflowAnalysisContext *DACtx, Stmt *S,
+                       RecordStorageLocation *ThisPointeeLoc,
+                       RecordStorageLocation *LocForRecordReturnVal);
+
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
@@ -736,11 +755,20 @@ private:
   // shared between environments in the same call.
   // https://github.com/llvm/llvm-project/issues/59005
 
-  // `DeclContext` of the block being analysed if provided.
-  std::vector CallStack;
+  // The stack of functions called from the initial analysis target.
+  std::vector CallStack;
+
+  // Initial function to analyze, if a function was passed to the constructor.
+  // Null otherwise.
+  const FunctionDecl *InitialTargetFunc = nullptr;
+  // Top-level statement of the initial analysis target.
+  // If a function was passed to the constructor, this is its body.
+  // If a statement was passed to the constructor, this is that statement.
+  // Null if no analysis target was passed to the constructor.
+  Stmt *InitialTargetStmt = nullptr;
 
   // Maps from prvalues of record type to their result objects. Shared between
-  // all environments for the same function.
+  // all environments for the same analysis target.
   // FIXME: It's somewhat unsatisfactory that we have to use a `shared_ptr`
   // here, though the cost is acceptable: The overhead of a `shared_ptr` is
   // incurred when it is copied, and this happens only relatively rarely (when
@@ -749,7 +777,7 @@ private:
   std::shared_ptr ResultObjectMap;
 
   // The following three member variables handle various different types of
-  // return values.
+  // return values when the current analysis target is a function.
   // - If the return type is not a reference and not a record: Value returned
   //   by the function.
   Value *ReturnVal = nullptr;
@@ -762,7 +790,7 @@ private:
   RecordStorageLocation *LocForRecordReturnVal = nullptr;
 
   // The storage location of the `this` pointee. Should only be null if the
-  // function being analyzed is only a function and not a method.
+  // analysis target is not a method.
   RecordStorageLocation *ThisPointeeLoc = nullptr;
 
   // Maps from declarations and glvalue expression to storage locations that are
diff --git a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h
index b5cd7aa10fd7d2502376049dded4f5c693d26008..d74380b78e935dda7b7391c0b5d06d7589a36e35 100644
--- a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h
+++ b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h
@@ -17,16 +17,17 @@
 #include "clang/Analysis/FlowSensitive/Formula.h"
 #include "clang/Analysis/FlowSensitive/Solver.h"
 #include "llvm/ADT/ArrayRef.h"
-#include 
 
 namespace clang {
 namespace dataflow {
 
 /// A SAT solver that is an implementation of Algorithm D from Knuth's The Art
 /// of Computer Programming Volume 4: Satisfiability, Fascicle 6. It is based on
-/// the Davis-Putnam-Logemann-Loveland (DPLL) algorithm, keeps references to a
-/// single "watched" literal per clause, and uses a set of "active" variables
+/// the Davis-Putnam-Logemann-Loveland (DPLL) algorithm [1], keeps references to
+/// a single "watched" literal per clause, and uses a set of "active" variables
 /// for unit propagation.
+//
+// [1] https://en.wikipedia.org/wiki/DPLL_algorithm
 class WatchedLiteralsSolver : public Solver {
   // Count of the iterations of the main loop of the solver. This spans *all*
   // calls to the underlying solver across the life of this object. It is
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 52552ba488560b2462139026789e564ac0387bda..7008bea483c8723ee70efce35f207972fcaadea3 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -999,7 +999,7 @@ def Availability : InheritableAttr {
               VersionArgument<"deprecated">, VersionArgument<"obsoleted">,
               BoolArgument<"unavailable">, StringArgument<"message">,
               BoolArgument<"strict">, StringArgument<"replacement">,
-              IntArgument<"priority">];
+              IntArgument<"priority">, IdentifierArgument<"environment">];
   let AdditionalMembers =
 [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) {
     return llvm::StringSwitch(Platform)
@@ -1019,7 +1019,7 @@ def Availability : InheritableAttr {
              .Case("xros", "visionOS")
              .Case("xros_app_extension", "visionOS (App Extension)")
              .Case("swift", "Swift")
-             .Case("shadermodel", "HLSL ShaderModel")
+             .Case("shadermodel", "Shader Model")
              .Case("ohos", "OpenHarmony OS")
              .Default(llvm::StringRef());
 }
@@ -1059,7 +1059,34 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
              .Case("visionos_app_extension", "xros_app_extension")
              .Case("ShaderModel", "shadermodel")
              .Default(Platform);
-} }];
+}
+static llvm::StringRef getPrettyEnviromentName(llvm::StringRef Environment) {
+    return llvm::StringSwitch(Environment)
+             .Case("pixel", "pixel shader")
+             .Case("vertex", "vertex shader")
+             .Case("geometry", "geometry shader")
+             .Case("hull", "hull shader")
+             .Case("domain", "domain shader")
+             .Case("compute", "compute shader")
+             .Case("mesh", "mesh shader")
+             .Case("amplification", "amplification shader")
+             .Case("library", "shader library")
+             .Default(Environment);
+}
+static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {
+    return llvm::StringSwitch(Environment)
+             .Case("pixel", llvm::Triple::Pixel)
+             .Case("vertex", llvm::Triple::Vertex)
+             .Case("geometry", llvm::Triple::Geometry)
+             .Case("hull", llvm::Triple::Hull)
+             .Case("domain", llvm::Triple::Domain)
+             .Case("compute", llvm::Triple::Compute)
+             .Case("mesh", llvm::Triple::Mesh)
+             .Case("amplification", llvm::Triple::Amplification)
+             .Case("library", llvm::Triple::Library)
+             .Default(llvm::Triple::UnknownEnvironment);
+}
+}];
   let HasCustomParsing = 1;
   let InheritEvenIfAlreadyPresent = 1;
   let Subjects = SubjectList<[Named]>;
@@ -4561,3 +4588,10 @@ def CodeAlign: StmtAttr {
     static constexpr int MaximumAlignment = 4096;
   }];
 }
+
+def ClspvLibclcBuiltin: InheritableAttr {
+  let Spellings = [Clang<"clspv_libclc_builtin">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [ClspvLibclcBuiltinDoc];
+  let SimpleHandler = 1;
+}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index f351822ac74bd5a451d951305dbfcc6c64c95f55..54197d588eb45084e93e3d8b5eeec277feeee035 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1593,6 +1593,11 @@ replacement=\ *string-literal*
   a warning about use of a deprecated declaration. The Fix-It will replace
   the deprecated declaration with the new declaration specified.
 
+environment=\ *identifier*
+  Target environment in which this declaration is available. If present,
+  the availability attribute applies only to targets with the same platform
+  and environment. The parameter is currently supported only in HLSL.
+
 Multiple availability attributes can be placed on a declaration, which may
 correspond to different platforms. For most platforms, the availability
 attribute with the platform corresponding to the target platform will be used;
@@ -8087,3 +8092,17 @@ requirement:
   }
   }];
 }
+
+def ClspvLibclcBuiltinDoc : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+Attribute used by `clspv`_ (OpenCL-C to Vulkan SPIR-V compiler) to identify functions coming from `libclc`_ (OpenCL-C builtin library).
+
+.. code-block:: c
+
+  void __attribute__((clspv_libclc_builtin)) libclc_builtin() {}
+
+.. _`clspv`: https://github.com/google/clspv
+.. _`libclc`: https://libclc.llvm.org
+}];
+}
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 9781fcaa4ff5e913e619d347a2c5d2a98940bd9d..9d97a75f696f668ac858ce08919b2f7339150d1f 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -436,6 +436,9 @@ def warn_drv_clang_unsupported : Warning<
   "the clang compiler does not support '%0'">;
 def warn_drv_deprecated_arg : Warning<
   "argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup;
+def warn_drv_deprecated_arg_no_relaxed_template_template_args : Warning<
+  "argument '-fno-relaxed-template-template-args' is deprecated">,
+  InGroup;
 def warn_drv_deprecated_custom : Warning<
   "argument '%0' is deprecated, %1">, InGroup;
 def warn_drv_assuming_mfloat_abi_is : Warning<
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 2beb1d45124b4945f6c4fa7d254380673b5f7d8e..4cb4f3d999f7ab466de3794c11bb7692109ee801 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -104,6 +104,7 @@ def EnumConversion : DiagGroup<"enum-conversion",
                                [EnumEnumConversion,
                                 EnumFloatConversion,
                                 EnumCompareConditional]>;
+def DeprecatedNoRelaxedTemplateTemplateArgs : DiagGroup<"deprecated-no-relaxed-template-template-args">;
 def ObjCSignedCharBoolImplicitIntConversion :
   DiagGroup<"objc-signed-char-bool-implicit-int-conversion">;
 def Shorten64To32 : DiagGroup<"shorten-64-to-32">;
@@ -228,6 +229,7 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion,
                                           DeprecatedLiteralOperator,
                                           DeprecatedPragma,
                                           DeprecatedRegister,
+                                          DeprecatedNoRelaxedTemplateTemplateArgs,
                                           DeprecatedThisCapture,
                                           DeprecatedType,
                                           DeprecatedVolatile,
diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index 674742431dcb2d7dfff7284ca8d071d361275388..944b2a38b6e9652c969c4e019d270dcd44ae6192 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -24,7 +24,7 @@ def err_no_matching_target : Error<"no matching target found for target variant
 def err_unsupported_vendor : Error<"vendor '%0' is not supported: '%1'">;
 def err_unsupported_environment : Error<"environment '%0' is not supported: '%1'">;
 def err_unsupported_os : Error<"os '%0' is not supported: '%1'">;
-def err_cannot_read_input_list : Error<"could not read %select{alias list|filelist}0 '%1': %2">;
+def err_cannot_read_input_list : Error<"could not read %0 input list '%1': %2">;
 def err_invalid_label: Error<"label '%0' is reserved: use a different label name for -X