diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6adae58832e19..0b2e4e1be86d8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -64,8 +64,8 @@ clang/test/AST/Interp/ @tbaederr /mlir/Dialect/*/Transforms/Bufferize.cpp @matthias-springer # Linalg Dialect in MLIR. -/mlir/include/mlir/Dialect/Linalg/* @dcaballe @nicolasvasilache @rengolin -/mlir/lib/Dialect/Linalg/* @dcaballe @nicolasvasilache @rengolin +/mlir/include/mlir/Dialect/Linalg @dcaballe @nicolasvasilache @rengolin +/mlir/lib/Dialect/Linalg @dcaballe @nicolasvasilache @rengolin /mlir/lib/Dialect/Linalg/Transforms/DecomposeLinalgOps.cpp @MaheshRavishankar @nicolasvasilache /mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp @MaheshRavishankar @nicolasvasilache /mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp @MaheshRavishankar @nicolasvasilache @@ -85,8 +85,8 @@ clang/test/AST/Interp/ @tbaederr /mlir/**/*VectorToSCF* @banach-space @dcaballe @matthias-springer @nicolasvasilache /mlir/**/*VectorToLLVM* @banach-space @dcaballe @nicolasvasilache /mlir/**/*X86Vector* @aartbik @dcaballe @nicolasvasilache -/mlir/include/mlir/Dialect/Vector/* @dcaballe @nicolasvasilache -/mlir/lib/Dialect/Vector/* @dcaballe @nicolasvasilache +/mlir/include/mlir/Dialect/Vector @dcaballe @nicolasvasilache +/mlir/lib/Dialect/Vector @dcaballe @nicolasvasilache /mlir/lib/Dialect/Vector/Transforms/* @hanhanW @nicolasvasilache /mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp @MaheshRavishankar @nicolasvasilache /mlir/**/*EmulateNarrowType* @dcaballe @hanhanW @@ -120,6 +120,9 @@ clang/test/AST/Interp/ @tbaederr /mlir/**/LLVMIR/**/BasicPtxBuilderInterface* @grypp /mlir/**/NVVM* @grypp +# MLIR Index Dialect +/mlir/**/Index* @mogball + # MLIR Python Bindings /mlir/test/python/ @ftynse @makslevental @stellaraccident /mlir/python/ @ftynse @makslevental @stellaraccident @@ -141,3 +144,8 @@ clang/test/AST/Interp/ @tbaederr # ExtractAPI /clang/**/ExtractAPI @daniel-grumberg + +# DWARFLinker, dwarfutil, dsymutil +/llvm/**/DWARFLinker/ @JDevlieghere +/llvm/**/dsymutil/ @JDevlieghere +/llvm/**/llvm-dwarfutil/ @JDevlieghere diff --git a/.github/workflows/issue-write.yml b/.github/workflows/issue-write.yml index e003be006c4e1..d4814a2fe9014 100644 --- a/.github/workflows/issue-write.yml +++ b/.github/workflows/issue-write.yml @@ -5,6 +5,7 @@ on: workflows: - "Check code formatting" - "Check for private emails used in PRs" + - "PR Request Release Note" types: - completed @@ -17,7 +18,11 @@ jobs: permissions: pull-requests: write if: > - github.event.workflow_run.event == 'pull_request' + github.event.workflow_run.event == 'pull_request' && + ( + github.event.workflow_run.conclusion == 'success' || + github.event.workflow_run.conclusion == 'failure' + ) steps: - name: 'Download artifact' uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 @@ -92,7 +97,11 @@ jobs: var pr_number = 0; gql_result.repository.ref.associatedPullRequests.nodes.forEach((pr) => { - if (pr.baseRepository.owner.login = context.repo.owner && pr.state == 'OPEN') { + + // The largest PR number is the one we care about. The only way + // to have more than one associated pull requests is if all the + // old pull requests are in the closed state. + if (pr.baseRepository.owner.login = context.repo.owner && pr.number > pr_number) { pr_number = pr.number; } }); diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml index d7c21394ca486..1456f245cf7c0 100644 --- a/.github/workflows/libcxx-build-and-test.yaml +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -63,8 +63,8 @@ jobs: cxx: [ 'clang++-19' ] include: - config: 'generic-gcc' - cc: 'gcc-13' - cxx: 'g++-13' + cc: 'gcc-14' + cxx: 'g++-14' steps: - uses: actions/checkout@v4 - name: ${{ matrix.config }}.${{ matrix.cxx }} @@ -101,8 +101,8 @@ jobs: cxx: [ 'clang++-19' ] include: - config: 'generic-gcc-cxx11' - cc: 'gcc-13' - cxx: 'g++-13' + cc: 'gcc-14' + cxx: 'g++-14' - config: 'generic-cxx23' cc: 'clang-17' cxx: 'clang++-17' diff --git a/.github/workflows/pr-request-release-note.yml b/.github/workflows/pr-request-release-note.yml index 5e48ce7aee2e2..2fa501dda16bb 100644 --- a/.github/workflows/pr-request-release-note.yml +++ b/.github/workflows/pr-request-release-note.yml @@ -2,7 +2,6 @@ name: PR Request Release Note permissions: contents: read - pull-requests: write on: pull_request: @@ -41,3 +40,10 @@ jobs: --token "$GITHUB_TOKEN" \ request-release-note \ --pr-number ${{ github.event.pull_request.number}} + + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + if: always() + with: + name: workflow-args + path: | + comments diff --git a/bolt/docs/CommandLineArgumentReference.md b/bolt/docs/CommandLineArgumentReference.md index 49e226513028f..17c52c65e472f 100644 --- a/bolt/docs/CommandLineArgumentReference.md +++ b/bolt/docs/CommandLineArgumentReference.md @@ -56,6 +56,14 @@ Allow processing of stripped binaries +- `--alt-inst-feature-size=` + + Size of feature field in .altinstructions + +- `--alt-inst-has-padlen` + + Specify that .altinstructions has padlen field + - `--asm-dump[=]` Dump function into assembly @@ -78,6 +86,16 @@ in the input is decoded and re-encoded. If the resulting bytes do not match the input, a warning message is printed. +- `--comp-dir-override=` + + Overrides DW_AT_comp_dir, and provides an alterantive base location, which is + used with DW_AT_dwo_name to construct a path to *.dwo files. + +- `--create-debug-names-section` + + Creates .debug_names section, if the input binary doesn't have it already, for + DWARF5 CU/TUs. + - `--cu-processing-batch-size=` Specifies the size of batches for processing CUs. Higher number has better @@ -93,7 +111,7 @@ - `--debug-skeleton-cu` - Prints out offsetrs for abbrev and debu_info of Skeleton CUs that get patched. + Prints out offsets for abbrev and debug_info of Skeleton CUs that get patched. - `--deterministic-debuginfo` @@ -104,6 +122,10 @@ Add basic block instructions as tool tips on nodes +- `--dump-alt-instructions` + + Dump Linux alternative instructions info + - `--dump-cg=` Dump callgraph to the given file @@ -117,10 +139,34 @@ Dump function CFGs to graphviz format after each stage;enable '-print-loops' for color-coded blocks +- `--dump-linux-exceptions` + + Dump Linux kernel exception table + - `--dump-orc` Dump raw ORC unwind information (sorted) +- `--dump-para-sites` + + Dump Linux kernel paravitual patch sites + +- `--dump-pci-fixups` + + Dump Linux kernel PCI fixup table + +- `--dump-smp-locks` + + Dump Linux kernel SMP locks + +- `--dump-static-calls` + + Dump Linux kernel static calls + +- `--dump-static-keys` + + Dump Linux kernel static keys jump table + - `--dwarf-output-path=` Path to where .dwo files or dwp file will be written out to. @@ -205,6 +251,18 @@ Skip processing of cold functions +- `--log-file=` + + Redirect journaling to a file instead of stdout/stderr + +- `--long-jump-labels` + + Always use long jumps/nops for Linux kernel static keys + +- `--match-profile-with-function-hash` + + Match profile with function hash + - `--max-data-relocations=` Maximum number of data relocations to process @@ -274,6 +332,10 @@ Number of tasks to be created per thread +- `--terminal-trap` + + Assume that execution stops at trap instruction + - `--thread-count=` Number of threads @@ -618,10 +680,6 @@ 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 @@ -630,6 +688,10 @@ Use a modified clustering algorithm geared towards minimizing branches +- `--name-similarity-function-matching-threshold=` + + Match functions using namespace and edit distance. + - `--no-inline` Disable all inlining (overrides other inlining options) @@ -710,7 +772,7 @@ - `none`: do not reorder functions - `exec-count`: order by execution count - `hfsort`: use hfsort algorithm - - `hfsort+`: use hfsort+ algorithm + - `hfsort+`: use cache-directed sort - `cdsort`: use cache-directed sort - `pettis-hansen`: use Pettis-Hansen algorithm - `random`: reorder functions randomly @@ -804,8 +866,8 @@ - `--stale-matching-min-matched-block=` - Minimum percent of exact match block for a function to be considered for - profile inference. + Percentage threshold of matched basic blocks at which stale profile inference + is executed. - `--stale-threshold=` @@ -853,6 +915,10 @@ 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=` @@ -1039,6 +1105,10 @@ Print clusters +- `--print-estimate-edge-counts` + + Print function after edge counts are set for no-LBR profile + - `--print-finalized` Print function after CFG is finalized @@ -1071,6 +1141,10 @@ Print functions after inlining optimization +- `--print-large-functions` + + Print functions that could not be overwritten due to excessive size + - `--print-longjmp` Print functions after longjmp pass @@ -1166,4 +1240,4 @@ - `--print-options` - Print non-default options after command line parsing + Print non-default options after command line parsing \ No newline at end of file diff --git a/bolt/docs/OptimizingLinux.md b/bolt/docs/OptimizingLinux.md new file mode 100644 index 0000000000000..0045f0ead9fd0 --- /dev/null +++ b/bolt/docs/OptimizingLinux.md @@ -0,0 +1,125 @@ +# Optimizing Linux Kernel with BOLT + + +## Introduction + +Many Linux applications spend a significant amount of their execution time in the kernel. Thus, when we consider code optimization for system performance, it is essential to improve the CPU utilization not only in the user-space applications and libraries but also in the kernel. BOLT has demonstrated double-digit gains while being applied to user-space programs. This guide shows how to apply BOLT to the x86-64 Linux kernel and enhance your system's performance. In our experiments, BOLT boosted database TPS by 2 percent when applied to the kernel compiled with the highest level optimizations, including PGO and LTO. The database spent ~40% of the time in the kernel and was quite sensitive to kernel performance. + +BOLT optimizes code layout based on a low-level execution profile collected with the Linux `perf` tool. The best quality profile should include branch history, such as Intel's last branch records (LBR). BOLT runs on a linked binary and reorders the code while combining frequently executed blocks of instructions in a manner best suited for the hardware. Other than branch instructions, most of the code is left unchanged. Additionally, BOLT updates all metadata associated with the modified code, including DWARF debug information and Linux ORC unwind information. + +While BOLT optimizations are not specific to the Linux kernel, certain quirks distinguish the kernel from user-level applications. + +BOLT has been successfully applied to and tested with several flavors of the x86-64 Linux kernel. + + +## QuickStart Guide + +BOLT operates on a statically-linked kernel executable, a.k.a. `vmlinux` binary. However, most Linux distributions use a `vmlinuz` compressed image for system booting. To use BOLT on the kernel, you must either repackage `vmlinuz` after BOLT optimizations or add steps for running BOLT into the kernel build and rebuild `vmlinuz`. Uncompressing `vmlinuz` and repackaging it with a new `vmlinux` binary falls beyond the scope of this guide, and at some point, we may add the capability to run BOLT directly on `vmlinuz`. Meanwhile, this guide focuses on steps for integrating BOLT into the kernel build process. + + +### Building the Kernel + +After downloading the kernel sources and configuration for your distribution, you should be able to build `vmlinuz` using the `make bzImage` command. Ideally, the kernel should binary match the kernel on the system you are about to optimize (the target system). The binary matching part is critical as BOLT performs profile matching and optimizations at the binary level. We recommend installing a freshly built kernel on the target system to avoid any discrepancies. + +Note that the kernel build will produce several artifacts besides bzImage. The most important of them is the uncompressed `vmlinux` binary, which will be used in the next steps. Make sure to save this file. + +Build and target systems should have a `perf` tool installed for collecting and processing profiles. If your build system differs from the target, make sure `perf` versions are compatible. The build system should also have the latest BOLT binary and tools (`llvm-bolt`, `perf2bolt`). + +Once the target system boots with the freshly-built kernel, start your workload, such as a database benchmark. While the system is under load, collect the kernel profile using perf: + + +```bash +$ sudo perf record -a -e cycles -j any,k -F 5000 -- sleep 600 +``` + + +Convert `perf` profile into a format suitable for BOLT passing the `vmlinux` binary to `perf2bolt`: + + +```bash +$ sudo chwon $USER perf.data +$ perf2bolt -p perf.data -o perf.fdata vmlinux +``` + + +Under a high load, `perf.data` should be several gigabytes in size and you should expect the converted `perf.fdata` not to exceed 100 MB. + +Profiles collected from multiple workloads could be joined into a single profile using `merge-fdata` utility: +```bash +$ merge-fdata perf.1.fdata perf.2.fdata ... perf..fdata > perf.merged.fdata +``` + +Two changes are required for the kernel build. The first one is optional but highly recommended. It introduces a BOLT-reserved space into `vmlinux` code section: + + +```diff +--- a/arch/x86/kernel/vmlinux.lds.S ++++ b/arch/x86/kernel/vmlinux.lds.S +@@ -139,6 +139,11 @@ SECTIONS + STATIC_CALL_TEXT + *(.gnu.warning) + ++ /* Allocate space for BOLT */ ++ __bolt_reserved_start = .; ++ . += 2048 * 1024; ++ __bolt_reserved_end = .; ++ + #ifdef CONFIG_RETPOLINE + __indirect_thunk_start = .; + *(.text.__x86.*) +``` + + +The second patch adds a step that runs BOLT on `vmlinux` binary: + + +```diff +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -340,5 +340,13 @@ if is_enabled CONFIG_KALLSYMS; then + fi + fi + ++# Apply BOLT ++BOLT=llvm-bolt ++BOLT_PROFILE=perf.fdata ++BOLT_OPTS="--dyno-stats --eliminate-unreachable=0 --reorder-blocks=ext-tsp --simplify-conditional-tail-calls=0 --skip-funcs=__entry_text_start,irq_entries_start --split-functions" ++mv vmlinux vmlinux.pre-bolt ++echo BOLTing vmlinux ++${BOLT} vmlinux.pre-bolt -o vmlinux --data ${BOLT_PROFILE} ${BOLT_OPTS} ++ + # For fixdep + echo "vmlinux: $0" > .vmlinux.d +``` + + +If you skipped the first step or are running BOLT on a pre-built `vmlinux` binary, drop the `--split-functions` option. + + +## Performance Expectations + +By improving the code layout, BOLT can boost the kernel's performance by up to 5% by reducing instruction cache misses and branch mispredictions. When measuring total system performance, you should scale this number accordingly based on the time your application spends in the kernel (excluding I/O time). + + +## Profile Quality + +The timing and duration of the profiling may have a significant effect on the performance of the BOLTed kernel. If you don't know your workload well, it's recommended that you profile for the whole duration of the benchmark run. As longer times will result in larger `perf.data` files, you can lower the profiling frequency by providing a smaller value of `-F` flag. E.g., to record the kernel profile for half an hour, use the following command: + + +```bash +$ sudo perf record -a -e cycles -j any,k -F 1000 -- sleep 1800 +``` + + + +## BOLT Disassembly + +BOLT annotates the disassembly with control-flow information and attaches Linux-specific metadata to the code. To view annotated disassembly, run: + + +```bash +$ llvm-bolt vmlinux -o /dev/null --print-cfg +``` + + +If you want to limit the disassembly to a set of functions, add `--print-only=,,...`, where a function name is specified using regular expressions. diff --git a/bolt/include/bolt/Core/BinaryBasicBlock.h b/bolt/include/bolt/Core/BinaryBasicBlock.h index a57b70714fe38..9a9d7b8735d71 100644 --- a/bolt/include/bolt/Core/BinaryBasicBlock.h +++ b/bolt/include/bolt/Core/BinaryBasicBlock.h @@ -842,15 +842,6 @@ class BinaryBasicBlock { bool analyzeBranch(const MCSymbol *&TBB, const MCSymbol *&FBB, MCInst *&CondBranch, MCInst *&UncondBranch); - /// Return true if iterator \p I is pointing to the first instruction in - /// a pair that could be macro-fused. - bool isMacroOpFusionPair(const_iterator I) const; - - /// If the basic block has a pair of instructions suitable for macro-fusion, - /// return iterator to the first instruction of the pair. - /// Otherwise return end(). - const_iterator getMacroOpFusionPair() const; - /// Printer required for printing dominator trees. void printAsOperand(raw_ostream &OS, bool PrintType = true) { if (PrintType) diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index 4ec3de3da1bf8..73932c4ca2fb3 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -698,10 +698,6 @@ class BinaryContext { /// Binary-wide aggregated stats. struct BinaryStats { - /// Stats for macro-fusion. - uint64_t MissedMacroFusionPairs{0}; - uint64_t MissedMacroFusionExecCount{0}; - /// Stats for stale profile matching: /// the total number of basic blocks in the profile uint32_t NumStaleBlocks{0}; diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h index 3c641581e247a..7d0afee4d1b78 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -835,10 +835,6 @@ class BinaryFunction { /// them. void calculateLoopInfo(); - /// Calculate missed macro-fusion opportunities and update BinaryContext - /// stats. - void calculateMacroOpFusionStats(); - /// Returns if BinaryDominatorTree has been constructed for this function. bool hasDomTree() const { return BDT != nullptr; } @@ -930,6 +926,10 @@ class BinaryFunction { return const_cast(this)->getInstructionAtOffset(Offset); } + /// When the function is in disassembled state, return an instruction that + /// contains the \p Offset. + MCInst *getInstructionContainingOffset(uint64_t Offset); + std::optional disassembleInstructionAtOffset(uint64_t Offset) const; /// Return offset for the first instruction. If there is data at the diff --git a/bolt/include/bolt/Passes/BinaryFunctionCallGraph.h b/bolt/include/bolt/Core/BinaryFunctionCallGraph.h similarity index 95% rename from bolt/include/bolt/Passes/BinaryFunctionCallGraph.h rename to bolt/include/bolt/Core/BinaryFunctionCallGraph.h index 52e17db4f50ce..4ff5b1b94c5e5 100644 --- a/bolt/include/bolt/Passes/BinaryFunctionCallGraph.h +++ b/bolt/include/bolt/Core/BinaryFunctionCallGraph.h @@ -1,4 +1,4 @@ -//===- bolt/Passes/CallGraph.h ----------------------------------*- C++ -*-===// +//===- bolt/Core/CallGraph.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. @@ -9,7 +9,7 @@ #ifndef BOLT_PASSES_BINARY_FUNCTION_CALLGRAPH_H #define BOLT_PASSES_BINARY_FUNCTION_CALLGRAPH_H -#include "bolt/Passes/CallGraph.h" +#include "bolt/Core/CallGraph.h" #include #include #include diff --git a/bolt/include/bolt/Passes/CallGraph.h b/bolt/include/bolt/Core/CallGraph.h similarity index 98% rename from bolt/include/bolt/Passes/CallGraph.h rename to bolt/include/bolt/Core/CallGraph.h index bdbc50bb78e87..2fc18e61afcaa 100644 --- a/bolt/include/bolt/Passes/CallGraph.h +++ b/bolt/include/bolt/Core/CallGraph.h @@ -1,4 +1,4 @@ -//===- bolt/Passes/CallGraph.h ----------------------------------*- C++ -*-===// +//===- bolt/Core/CallGraph.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. diff --git a/bolt/include/bolt/Passes/CallGraphWalker.h b/bolt/include/bolt/Core/CallGraphWalker.h similarity index 95% rename from bolt/include/bolt/Passes/CallGraphWalker.h rename to bolt/include/bolt/Core/CallGraphWalker.h index ac45644be362f..b0a73aee14369 100644 --- a/bolt/include/bolt/Passes/CallGraphWalker.h +++ b/bolt/include/bolt/Core/CallGraphWalker.h @@ -1,4 +1,4 @@ -//===- bolt/Passes/CallGraphWalker.h ----------------------------*- C++ -*-===// +//===- bolt/Core/CallGraphWalker.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. diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h index 585bafa088849..144433ac78a37 100644 --- a/bolt/include/bolt/Core/DebugData.h +++ b/bolt/include/bolt/Core/DebugData.h @@ -210,6 +210,15 @@ class DebugRangesSectionWriter { static bool classof(const DebugRangesSectionWriter *Writer) { return Writer->getKind() == RangesWriterKind::DebugRangesWriter; } + + /// Append a range to the main buffer. + void appendToRangeBuffer(const DebugBufferVector &CUBuffer); + + /// Sets Unit DIE to be updated for CU. + void setDie(DIE *Die) { this->Die = Die; } + + /// Returns Unit DIE to be updated for CU. + DIE *getDie() const { return Die; } /// Writes out range lists for a current CU being processed. void virtual finalizeSection(){}; @@ -232,6 +241,9 @@ class DebugRangesSectionWriter { static constexpr uint64_t EmptyRangesOffset{0}; private: + /// Stores Unit DIE to be updated for CU. + DIE *Die{0}; + RangesWriterKind Kind; }; diff --git a/bolt/include/bolt/Core/DebugNames.h b/bolt/include/bolt/Core/DebugNames.h index a14a30529fad5..0e61a0e4f9d9f 100644 --- a/bolt/include/bolt/Core/DebugNames.h +++ b/bolt/include/bolt/Core/DebugNames.h @@ -91,6 +91,10 @@ class DWARF5AcceleratorTable { uint64_t CurrentUnitOffset = 0; const DWARFUnit *CurrentUnit = nullptr; std::unordered_map AbbrevTagToIndexMap; + /// Contains a map of TU hashes to a Foreign TU indecies. + /// This is used to reduce the size of Foreign TU list since there could be + /// multiple TUs with the same hash. + DenseMap TUHashToIndexMap; /// Represents a group of entries with identical name (and hence, hash value). struct HashData { diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index 765372aa9e402..ab07f07e49845 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -439,7 +439,20 @@ class MCPlusBuilder { } /// Check whether this conditional branch can be reversed - virtual bool isReversibleBranch(const MCInst &Inst) const { return true; } + virtual bool isReversibleBranch(const MCInst &Inst) const { + assert(!isUnsupportedInstruction(Inst) && isConditionalBranch(Inst) && + "Instruction is not known conditional branch"); + + if (isDynamicBranch(Inst)) + return false; + return true; + } + + /// Return true if this instruction inhibits analysis of the containing + /// function. + virtual bool isUnsupportedInstruction(const MCInst &Inst) const { + return false; + } /// Return true of the instruction is of pseudo kind. virtual bool isPseudo(const MCInst &Inst) const { @@ -917,13 +930,6 @@ class MCPlusBuilder { /// Return true if the instruction is encoded using EVEX (AVX-512). virtual bool hasEVEXEncoding(const MCInst &Inst) const { return false; } - /// Return true if a pair of instructions represented by \p Insts - /// could be fused into a single uop. - virtual bool isMacroOpFusionPair(ArrayRef Insts) const { - llvm_unreachable("not implemented"); - return false; - } - struct X86MemOperand { unsigned BaseRegNum; int64_t ScaleImm; diff --git a/bolt/include/bolt/Passes/HFSort.h b/bolt/include/bolt/Passes/HFSort.h index 3787187733d07..2a35760329bb8 100644 --- a/bolt/include/bolt/Passes/HFSort.h +++ b/bolt/include/bolt/Passes/HFSort.h @@ -19,7 +19,7 @@ #ifndef BOLT_PASSES_HFSORT_H #define BOLT_PASSES_HFSORT_H -#include "bolt/Passes/CallGraph.h" +#include "bolt/Core/CallGraph.h" #include #include diff --git a/bolt/include/bolt/Passes/RegReAssign.h b/bolt/include/bolt/Passes/RegReAssign.h index c50e32ff46e29..a7554a1215104 100644 --- a/bolt/include/bolt/Passes/RegReAssign.h +++ b/bolt/include/bolt/Passes/RegReAssign.h @@ -9,7 +9,7 @@ #ifndef BOLT_PASSES_REGREASSIGN_H #define BOLT_PASSES_REGREASSIGN_H -#include "bolt/Passes/BinaryFunctionCallGraph.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Passes/BinaryPasses.h" #include "bolt/Passes/RegAnalysis.h" diff --git a/bolt/include/bolt/Passes/ReorderFunctions.h b/bolt/include/bolt/Passes/ReorderFunctions.h index 4c88142c58871..7da32324bc933 100644 --- a/bolt/include/bolt/Passes/ReorderFunctions.h +++ b/bolt/include/bolt/Passes/ReorderFunctions.h @@ -9,7 +9,7 @@ #ifndef BOLT_PASSES_REORDER_FUNCTIONS_H #define BOLT_PASSES_REORDER_FUNCTIONS_H -#include "bolt/Passes/BinaryFunctionCallGraph.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Passes/BinaryPasses.h" namespace llvm { diff --git a/bolt/include/bolt/Profile/YAMLProfileReader.h b/bolt/include/bolt/Profile/YAMLProfileReader.h index 7a8aa176c30f1..8bcae2b4df739 100644 --- a/bolt/include/bolt/Profile/YAMLProfileReader.h +++ b/bolt/include/bolt/Profile/YAMLProfileReader.h @@ -93,6 +93,9 @@ class YAMLProfileReader : public ProfileReaderBase { ProfiledFunctions.emplace(&BF); } + /// Matches functions with similarly named profiled functions. + uint64_t matchWithNameSimilarity(BinaryContext &BC); + /// Check if the profile uses an event with a given \p Name. bool usesEvent(StringRef Name) const; }; diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h index 4559ff5ff5159..4f576eaa95576 100644 --- a/bolt/include/bolt/Rewrite/DWARFRewriter.h +++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h @@ -16,7 +16,6 @@ #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/DIE.h" #include "llvm/DWP/DWP.h" -#include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCContext.h" #include "llvm/Support/ToolOutputFile.h" #include @@ -90,6 +89,10 @@ class DWARFRewriter { /// Store Rangelists writer for each DWO CU. RangeListsDWOWriers RangeListsWritersByCU; + /// Stores ranges writer for each DWO CU. + std::unordered_map> + LegacyRangesWritersByCU; + std::mutex LocListDebugInfoPatchesMutex; /// Dwo id specific its RangesBase. @@ -183,7 +186,7 @@ class DWARFRewriter { void updateDebugInfo(); /// Update stmt_list for CUs based on the new .debug_line \p Layout. - void updateLineTableOffsets(const MCAsmLayout &Layout); + void updateLineTableOffsets(const MCAssembler &Asm); uint64_t getDwoRangesBase(uint64_t DWOId) { return DwoRangesBase[DWOId]; } diff --git a/bolt/lib/Core/BinaryBasicBlock.cpp b/bolt/lib/Core/BinaryBasicBlock.cpp index a4b9a7f558cd8..2a2192b79bb4b 100644 --- a/bolt/lib/Core/BinaryBasicBlock.cpp +++ b/bolt/lib/Core/BinaryBasicBlock.cpp @@ -404,45 +404,6 @@ bool BinaryBasicBlock::analyzeBranch(const MCSymbol *&TBB, const MCSymbol *&FBB, CondBranch, UncondBranch); } -bool BinaryBasicBlock::isMacroOpFusionPair(const_iterator I) const { - auto &MIB = Function->getBinaryContext().MIB; - ArrayRef Insts = Instructions; - return MIB->isMacroOpFusionPair(Insts.slice(I - begin())); -} - -BinaryBasicBlock::const_iterator -BinaryBasicBlock::getMacroOpFusionPair() const { - if (!Function->getBinaryContext().isX86()) - return end(); - - if (getNumNonPseudos() < 2 || succ_size() != 2) - return end(); - - auto RI = getLastNonPseudo(); - assert(RI != rend() && "cannot have an empty block with 2 successors"); - - BinaryContext &BC = Function->getBinaryContext(); - - // Skip instruction if it's an unconditional branch following - // a conditional one. - if (BC.MIB->isUnconditionalBranch(*RI)) - ++RI; - - if (!BC.MIB->isConditionalBranch(*RI)) - return end(); - - // Start checking with instruction preceding the conditional branch. - ++RI; - if (RI == rend()) - return end(); - - auto II = std::prev(RI.base()); // convert to a forward iterator - if (isMacroOpFusionPair(II)) - return II; - - return end(); -} - MCInst *BinaryBasicBlock::getTerminatorBefore(MCInst *Pos) { BinaryContext &BC = Function->getBinaryContext(); auto Itr = rbegin(); diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp index db02dc0fae4ee..0a1f1bb9e0d20 100644 --- a/bolt/lib/Core/BinaryContext.cpp +++ b/bolt/lib/Core/BinaryContext.cpp @@ -20,7 +20,6 @@ #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" -#include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" @@ -2404,32 +2403,23 @@ BinaryContext::calculateEmittedSize(BinaryFunction &BF, bool FixBranches) { Streamer->emitLabel(SplitStartLabel); emitFunctionBody(*Streamer, BF, FF, /*EmitCodeOnly=*/true); Streamer->emitLabel(SplitEndLabel); - // To avoid calling MCObjectStreamer::flushPendingLabels() which is - // private - Streamer->emitBytes(StringRef("")); - Streamer->switchSection(Section); } - // To avoid calling MCObjectStreamer::flushPendingLabels() which is private or - // MCStreamer::Finish(), which does more than we want - Streamer->emitBytes(StringRef("")); - MCAssembler &Assembler = static_cast(Streamer.get())->getAssembler(); - MCAsmLayout Layout(Assembler); - Assembler.layout(Layout); + Assembler.layout(); // Obtain fragment sizes. std::vector FragmentSizes; // Main fragment size. - const uint64_t HotSize = - Layout.getSymbolOffset(*EndLabel) - Layout.getSymbolOffset(*StartLabel); + const uint64_t HotSize = Assembler.getSymbolOffset(*EndLabel) - + Assembler.getSymbolOffset(*StartLabel); FragmentSizes.push_back(HotSize); // Split fragment sizes. uint64_t ColdSize = 0; for (const auto &Labels : SplitLabels) { - uint64_t Size = Layout.getSymbolOffset(*Labels.second) - - Layout.getSymbolOffset(*Labels.first); + uint64_t Size = Assembler.getSymbolOffset(*Labels.second) - + Assembler.getSymbolOffset(*Labels.first); FragmentSizes.push_back(Size); ColdSize += Size; } @@ -2439,7 +2429,8 @@ BinaryContext::calculateEmittedSize(BinaryFunction &BF, bool FixBranches) { for (FunctionFragment &FF : BF.getLayout().fragments()) { BinaryBasicBlock *PrevBB = nullptr; for (BinaryBasicBlock *BB : FF) { - const uint64_t BBStartOffset = Layout.getSymbolOffset(*(BB->getLabel())); + const uint64_t BBStartOffset = + Assembler.getSymbolOffset(*(BB->getLabel())); BB->setOutputStartAddress(BBStartOffset); if (PrevBB) PrevBB->setOutputEndAddress(BBStartOffset); diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp index 5793963f9b80d..f6dfa249f9a9f 100644 --- a/bolt/lib/Core/BinaryEmitter.cpp +++ b/bolt/lib/Core/BinaryEmitter.cpp @@ -38,19 +38,6 @@ extern cl::opt PreserveBlocksAlignment; cl::opt AlignBlocks("align-blocks", cl::desc("align basic blocks"), cl::cat(BoltOptCategory)); -cl::opt -AlignMacroOpFusion("align-macro-fusion", - cl::desc("fix instruction alignment for macro-fusion (x86 relocation mode)"), - cl::init(MFT_HOT), - cl::values(clEnumValN(MFT_NONE, "none", - "do not insert alignment no-ops for macro-fusion"), - clEnumValN(MFT_HOT, "hot", - "only insert alignment no-ops on hot execution paths (default)"), - clEnumValN(MFT_ALL, "all", - "always align instructions to allow macro-fusion")), - cl::ZeroOrMore, - cl::cat(BoltRelocCategory)); - static cl::list BreakFunctionNames("break-funcs", cl::CommaSeparated, @@ -453,20 +440,7 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, Streamer.emitLabel(EntrySymbol); } - // Check if special alignment for macro-fusion is needed. - bool MayNeedMacroFusionAlignment = - (opts::AlignMacroOpFusion == MFT_ALL) || - (opts::AlignMacroOpFusion == MFT_HOT && BB->getKnownExecutionCount()); - BinaryBasicBlock::const_iterator MacroFusionPair; - if (MayNeedMacroFusionAlignment) { - MacroFusionPair = BB->getMacroOpFusionPair(); - if (MacroFusionPair == BB->end()) - MayNeedMacroFusionAlignment = false; - } - SMLoc LastLocSeen; - // Remember if the last instruction emitted was a prefix. - bool LastIsPrefix = false; for (auto I = BB->begin(), E = BB->end(); I != E; ++I) { MCInst &Instr = *I; @@ -479,16 +453,6 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, continue; } - // Handle macro-fusion alignment. If we emitted a prefix as - // the last instruction, we should've already emitted the associated - // alignment hint, so don't emit it twice. - if (MayNeedMacroFusionAlignment && !LastIsPrefix && - I == MacroFusionPair) { - // This assumes the second instruction in the macro-op pair will get - // assigned to its own MCRelaxableFragment. Since all JCC instructions - // are relaxable, we should be safe. - } - if (!EmitCodeOnly) { // A symbol to be emitted before the instruction to mark its location. MCSymbol *InstrLabel = BC.MIB->getInstLabel(Instr); @@ -525,7 +489,6 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, } Streamer.emitInstruction(Instr, *BC.STI); - LastIsPrefix = BC.MIB->isPrefix(Instr); } } diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index d13e28999a05c..89c05e3ab005b 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #define DEBUG_TYPE "bolt" @@ -542,7 +543,7 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation) { else OS << "\n"; } - if (BB->getCFIState() >= 0) + if (hasCFI()) OS << " CFI State : " << BB->getCFIState() << '\n'; if (opts::EnableBAT) { OS << " Input offset: 0x" << Twine::utohexstr(BB->getInputOffset()) @@ -610,7 +611,7 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation) { } // In CFG_Finalized state we can miscalculate CFI state at exit. - if (CurrentState == State::CFG) { + if (CurrentState == State::CFG && hasCFI()) { const int32_t CFIStateAtExit = BB->getCFIStateAtExit(); if (CFIStateAtExit >= 0) OS << " CFI State: " << CFIStateAtExit << '\n'; @@ -1275,6 +1276,10 @@ Error BinaryFunction::disassemble() { } } + bool IsUnsupported = BC.MIB->isUnsupportedInstruction(Instruction); + if (IsUnsupported) + setIgnored(); + if (MIB->isBranch(Instruction) || MIB->isCall(Instruction)) { uint64_t TargetAddress = 0; if (MIB->evaluateBranch(Instruction, AbsoluteInstrAddr, Size, @@ -1288,12 +1293,10 @@ Error BinaryFunction::disassemble() { const bool IsCondBranch = MIB->isConditionalBranch(Instruction); MCSymbol *TargetSymbol = nullptr; - if (!BC.MIB->isReversibleBranch(Instruction)) { - setIgnored(); - if (BinaryFunction *TargetFunc = + if (IsUnsupported) + if (auto *TargetFunc = BC.getBinaryFunctionContainingAddress(TargetAddress)) TargetFunc->setIgnored(); - } if (IsCall && containsAddress(TargetAddress)) { if (TargetAddress == getAddress()) { @@ -2276,8 +2279,6 @@ void BinaryFunction::postProcessCFG() { postProcessBranches(); } - calculateMacroOpFusionStats(); - // The final cleanup of intermediate structures. clearList(IgnoredBranches); @@ -2294,29 +2295,6 @@ void BinaryFunction::postProcessCFG() { "invalid CFG detected after post-processing"); } -void BinaryFunction::calculateMacroOpFusionStats() { - if (!getBinaryContext().isX86()) - return; - for (const BinaryBasicBlock &BB : blocks()) { - auto II = BB.getMacroOpFusionPair(); - if (II == BB.end()) - continue; - - // Check offset of the second instruction. - // FIXME: arch-specific. - const uint32_t Offset = BC.MIB->getOffsetWithDefault(*std::next(II), 0); - if (!Offset || (getAddress() + Offset) % 64) - continue; - - LLVM_DEBUG(dbgs() << "\nmissed macro-op fusion at address 0x" - << Twine::utohexstr(getAddress() + Offset) - << " in function " << *this << "; executed " - << BB.getKnownExecutionCount() << " times.\n"); - ++BC.Stats.MissedMacroFusionPairs; - BC.Stats.MissedMacroFusionExecCount += BB.getKnownExecutionCount(); - } -} - void BinaryFunction::removeTagsFromProfile() { for (BinaryBasicBlock *BB : BasicBlocks) { if (BB->ExecutionCount == BinaryBasicBlock::COUNT_NO_PROFILE) @@ -4471,6 +4449,18 @@ MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) { } } +MCInst *BinaryFunction::getInstructionContainingOffset(uint64_t Offset) { + assert(CurrentState == State::Disassembled && "Wrong function state"); + + if (Offset > Size) + return nullptr; + + auto II = Instructions.upper_bound(Offset); + assert(II != Instructions.begin() && "First instruction not at offset 0"); + --II; + return &II->second; +} + void BinaryFunction::printLoopInfo(raw_ostream &OS) const { if (!opts::shouldPrint(*this)) return; diff --git a/bolt/lib/Passes/BinaryFunctionCallGraph.cpp b/bolt/lib/Core/BinaryFunctionCallGraph.cpp similarity index 98% rename from bolt/lib/Passes/BinaryFunctionCallGraph.cpp rename to bolt/lib/Core/BinaryFunctionCallGraph.cpp index bbcc9751c0cbe..b4b7897aa426a 100644 --- a/bolt/lib/Passes/BinaryFunctionCallGraph.cpp +++ b/bolt/lib/Core/BinaryFunctionCallGraph.cpp @@ -1,4 +1,4 @@ -//===- bolt/Passes/BinaryFunctionCallGraph.cpp ----------------------------===// +//===- bolt/Core/BinaryFunctionCallGraph.cpp ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#include "bolt/Passes/BinaryFunctionCallGraph.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Core/BinaryContext.h" #include "bolt/Core/BinaryFunction.h" #include "llvm/Support/CommandLine.h" diff --git a/bolt/lib/Core/CMakeLists.txt b/bolt/lib/Core/CMakeLists.txt index 873cf67a56291..bb58667066fd8 100644 --- a/bolt/lib/Core/CMakeLists.txt +++ b/bolt/lib/Core/CMakeLists.txt @@ -17,8 +17,11 @@ add_llvm_library(LLVMBOLTCore BinaryData.cpp BinaryEmitter.cpp BinaryFunction.cpp + BinaryFunctionCallGraph.cpp BinaryFunctionProfile.cpp BinarySection.cpp + CallGraph.cpp + CallGraphWalker.cpp DebugData.cpp DebugNames.cpp DIEBuilder.cpp diff --git a/bolt/lib/Passes/CallGraph.cpp b/bolt/lib/Core/CallGraph.cpp similarity index 92% rename from bolt/lib/Passes/CallGraph.cpp rename to bolt/lib/Core/CallGraph.cpp index ee2ec26f3e50d..f1d52737bf556 100644 --- a/bolt/lib/Passes/CallGraph.cpp +++ b/bolt/lib/Core/CallGraph.cpp @@ -1,4 +1,4 @@ -//===- bolt/Passes/CallGraph.cpp ------------------------------------------===// +//===- bolt/Core/CallGraph.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,16 +10,16 @@ // //===----------------------------------------------------------------------===// -#include "bolt/Passes/CallGraph.h" +#include "bolt/Core/CallGraph.h" #define DEBUG_TYPE "callgraph" #if defined(__x86_64__) && !defined(_MSC_VER) -# if (!defined USE_SSECRC) -# define USE_SSECRC -# endif +#if (!defined USE_SSECRC) +#define USE_SSECRC +#endif #else -# undef USE_SSECRC +#undef USE_SSECRC #endif static LLVM_ATTRIBUTE_UNUSED inline size_t hash_int64_fallback(int64_t k) { @@ -50,7 +50,7 @@ static inline size_t hash_int64_pair(int64_t k1, int64_t k2) { // crc32 is commutative, so we need to perturb k1 so that (k1, k2) hashes // differently from (k2, k1). k1 += k1; - __asm("crc32q %1, %0\n" : "+r" (k1) : "rm"(k2)); + __asm("crc32q %1, %0\n" : "+r"(k1) : "rm"(k2)); return k1; #else return (hash_int64(k1) << 1) ^ hash_int64(k2); @@ -118,5 +118,5 @@ void CallGraph::adjustArcWeights() { } } -} -} +} // namespace bolt +} // namespace llvm diff --git a/bolt/lib/Passes/CallGraphWalker.cpp b/bolt/lib/Core/CallGraphWalker.cpp similarity index 90% rename from bolt/lib/Passes/CallGraphWalker.cpp rename to bolt/lib/Core/CallGraphWalker.cpp index 53fb1cb4d98c9..cbfa178d8e068 100644 --- a/bolt/lib/Passes/CallGraphWalker.cpp +++ b/bolt/lib/Core/CallGraphWalker.cpp @@ -1,4 +1,4 @@ -//===- bolt/Passes/CallGraphWalker.cpp ------------------------------------===// +//===- bolt/Core/CallGraphWalker.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#include "bolt/Passes/CallGraphWalker.h" -#include "bolt/Passes/BinaryFunctionCallGraph.h" +#include "bolt/Core/CallGraphWalker.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Timer.h" #include diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp index f502a50312470..08d4c45aac791 100644 --- a/bolt/lib/Core/DebugData.cpp +++ b/bolt/lib/Core/DebugData.cpp @@ -177,6 +177,12 @@ uint64_t DebugRangesSectionWriter::getSectionOffset() { return SectionOffset; } +void DebugRangesSectionWriter::appendToRangeBuffer( + const DebugBufferVector &CUBuffer) { + *RangesStream << CUBuffer; + SectionOffset = RangesBuffer->size(); +} + DebugAddrWriter *DebugRangeListsSectionWriter::AddrWriter = nullptr; uint64_t DebugRangeListsSectionWriter::addRanges( diff --git a/bolt/lib/Core/DebugNames.cpp b/bolt/lib/Core/DebugNames.cpp index ebe895e019ccb..640b29ec36d5c 100644 --- a/bolt/lib/Core/DebugNames.cpp +++ b/bolt/lib/Core/DebugNames.cpp @@ -90,7 +90,11 @@ void DWARF5AcceleratorTable::addUnit(DWARFUnit &Unit, auto Iter = CUOffsetsToPatch.insert({*DWOID, CUList.size()}); if (Iter.second) CUList.push_back(BADCUOFFSET); - ForeignTUList.push_back(cast(&Unit)->getTypeHash()); + const uint64_t TUHash = cast(&Unit)->getTypeHash(); + if (!TUHashToIndexMap.count(TUHash)) { + TUHashToIndexMap.insert({TUHash, ForeignTUList.size()}); + ForeignTUList.push_back(TUHash); + } } else { LocalTUList.push_back(CurrentUnitOffset); } @@ -231,8 +235,13 @@ DWARF5AcceleratorTable::addAccelTableEntry( IsTU = Unit.isTypeUnit(); DieTag = Die.getTag(); if (IsTU) { - if (DWOID) - return ForeignTUList.size() - 1; + if (DWOID) { + const uint64_t TUHash = cast(&Unit)->getTypeHash(); + auto Iter = TUHashToIndexMap.find(TUHash); + assert(Iter != TUHashToIndexMap.end() && + "Could not find TU hash in map"); + return Iter->second; + } return LocalTUList.size() - 1; } return CUList.size() - 1; diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index ecc2c08a30324..fa95ad7324ac1 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -44,7 +44,6 @@ namespace opts { extern cl::OptionCategory BoltCategory; extern cl::OptionCategory BoltOptCategory; -extern cl::opt AlignMacroOpFusion; extern cl::opt Verbosity; extern cl::opt EnableBAT; extern cl::opt ExecutionCountThreshold; @@ -1637,25 +1636,6 @@ Error PrintProgramStats::runOnFunctions(BinaryContext &BC) { } } - // Print information on missed macro-fusion opportunities seen on input. - if (BC.Stats.MissedMacroFusionPairs) { - BC.outs() << format( - "BOLT-INFO: the input contains %zu (dynamic count : %zu)" - " opportunities for macro-fusion optimization", - BC.Stats.MissedMacroFusionPairs, BC.Stats.MissedMacroFusionExecCount); - switch (opts::AlignMacroOpFusion) { - case MFT_NONE: - BC.outs() << ". Use -align-macro-fusion to fix.\n"; - break; - case MFT_HOT: - BC.outs() << ". Will fix instances on a hot path.\n"; - break; - case MFT_ALL: - BC.outs() << " that are going to be fixed\n"; - break; - } - } - // Collect and print information about suboptimal code layout on input. if (opts::ReportBadLayout) { std::vector SuboptimalFuncs; diff --git a/bolt/lib/Passes/CMakeLists.txt b/bolt/lib/Passes/CMakeLists.txt index 04057a895d666..407d8b03f7397 100644 --- a/bolt/lib/Passes/CMakeLists.txt +++ b/bolt/lib/Passes/CMakeLists.txt @@ -4,11 +4,8 @@ add_llvm_library(LLVMBOLTPasses AllocCombiner.cpp AsmDump.cpp BinaryPasses.cpp - BinaryFunctionCallGraph.cpp CMOVConversion.cpp CacheMetrics.cpp - CallGraph.cpp - CallGraphWalker.cpp DataflowAnalysis.cpp DataflowInfoManager.cpp FrameAnalysis.cpp diff --git a/bolt/lib/Passes/FrameAnalysis.cpp b/bolt/lib/Passes/FrameAnalysis.cpp index 4ebfd8f158f7f..e027fc0403cd9 100644 --- a/bolt/lib/Passes/FrameAnalysis.cpp +++ b/bolt/lib/Passes/FrameAnalysis.cpp @@ -11,8 +11,8 @@ //===----------------------------------------------------------------------===// #include "bolt/Passes/FrameAnalysis.h" +#include "bolt/Core/CallGraphWalker.h" #include "bolt/Core/ParallelUtilities.h" -#include "bolt/Passes/CallGraphWalker.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/Timer.h" #include diff --git a/bolt/lib/Passes/FrameOptimizer.cpp b/bolt/lib/Passes/FrameOptimizer.cpp index 8461225e1819c..1c0f9555f9eb9 100644 --- a/bolt/lib/Passes/FrameOptimizer.cpp +++ b/bolt/lib/Passes/FrameOptimizer.cpp @@ -11,8 +11,8 @@ //===----------------------------------------------------------------------===// #include "bolt/Passes/FrameOptimizer.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Core/ParallelUtilities.h" -#include "bolt/Passes/BinaryFunctionCallGraph.h" #include "bolt/Passes/DataflowInfoManager.h" #include "bolt/Passes/ShrinkWrapping.h" #include "bolt/Passes/StackAvailableExpressions.h" diff --git a/bolt/lib/Passes/IndirectCallPromotion.cpp b/bolt/lib/Passes/IndirectCallPromotion.cpp index 55eede641fd2f..a73eb867b3777 100644 --- a/bolt/lib/Passes/IndirectCallPromotion.cpp +++ b/bolt/lib/Passes/IndirectCallPromotion.cpp @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// #include "bolt/Passes/IndirectCallPromotion.h" -#include "bolt/Passes/BinaryFunctionCallGraph.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Passes/DataflowInfoManager.h" #include "bolt/Passes/Inliner.h" #include "llvm/ADT/STLExtras.h" diff --git a/bolt/lib/Passes/Instrumentation.cpp b/bolt/lib/Passes/Instrumentation.cpp index 14f506f9ca968..e824a42d82696 100644 --- a/bolt/lib/Passes/Instrumentation.cpp +++ b/bolt/lib/Passes/Instrumentation.cpp @@ -479,8 +479,7 @@ void Instrumentation::instrumentFunction(BinaryFunction &Function, HasJumpTable = true; else if (BC.MIB->isUnconditionalBranch(Inst)) HasUnconditionalBranch = true; - else if ((!BC.MIB->isCall(Inst) && !BC.MIB->isConditionalBranch(Inst)) || - !BC.MIB->isReversibleBranch(Inst)) + else if (!(BC.MIB->isCall(Inst) || BC.MIB->isConditionalBranch(Inst))) continue; const uint32_t FromOffset = *BC.MIB->getOffset(Inst); diff --git a/bolt/lib/Passes/JTFootprintReduction.cpp b/bolt/lib/Passes/JTFootprintReduction.cpp index fd291f96004d9..71bdbba950df1 100644 --- a/bolt/lib/Passes/JTFootprintReduction.cpp +++ b/bolt/lib/Passes/JTFootprintReduction.cpp @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// #include "bolt/Passes/JTFootprintReduction.h" -#include "bolt/Passes/BinaryFunctionCallGraph.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Passes/DataflowInfoManager.h" #include "llvm/Support/CommandLine.h" diff --git a/bolt/lib/Passes/RegAnalysis.cpp b/bolt/lib/Passes/RegAnalysis.cpp index 9054385c20b59..66ca6800b1a5f 100644 --- a/bolt/lib/Passes/RegAnalysis.cpp +++ b/bolt/lib/Passes/RegAnalysis.cpp @@ -12,7 +12,7 @@ #include "bolt/Passes/RegAnalysis.h" #include "bolt/Core/BinaryFunction.h" -#include "bolt/Passes/CallGraphWalker.h" +#include "bolt/Core/CallGraphWalker.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/CommandLine.h" diff --git a/bolt/lib/Passes/RegReAssign.cpp b/bolt/lib/Passes/RegReAssign.cpp index 0becfb4a06a38..60349f18b11d3 100644 --- a/bolt/lib/Passes/RegReAssign.cpp +++ b/bolt/lib/Passes/RegReAssign.cpp @@ -11,8 +11,8 @@ //===----------------------------------------------------------------------===// #include "bolt/Passes/RegReAssign.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Core/MCPlus.h" -#include "bolt/Passes/BinaryFunctionCallGraph.h" #include "bolt/Passes/DataflowAnalysis.h" #include "bolt/Passes/DataflowInfoManager.h" #include "bolt/Utils/Utils.h" diff --git a/bolt/lib/Passes/StokeInfo.cpp b/bolt/lib/Passes/StokeInfo.cpp index 499cac4217099..dd033508d200c 100644 --- a/bolt/lib/Passes/StokeInfo.cpp +++ b/bolt/lib/Passes/StokeInfo.cpp @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// #include "bolt/Passes/StokeInfo.h" -#include "bolt/Passes/BinaryFunctionCallGraph.h" +#include "bolt/Core/BinaryFunctionCallGraph.h" #include "bolt/Passes/DataflowInfoManager.h" #include "llvm/Support/CommandLine.h" diff --git a/bolt/lib/Profile/CMakeLists.txt b/bolt/lib/Profile/CMakeLists.txt index ca8b9c34e63b1..39708baac4036 100644 --- a/bolt/lib/Profile/CMakeLists.txt +++ b/bolt/lib/Profile/CMakeLists.txt @@ -10,6 +10,7 @@ add_llvm_library(LLVMBOLTProfile DISABLE_LLVM_LINK_LLVM_DYLIB LINK_COMPONENTS + Demangle Support TransformUtils ) diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index f25f59201f1cd..63222147bedd6 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -11,8 +11,11 @@ #include "bolt/Core/BinaryFunction.h" #include "bolt/Passes/MCF.h" #include "bolt/Profile/ProfileYAMLMapping.h" +#include "bolt/Utils/NameResolver.h" #include "bolt/Utils/Utils.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/edit_distance.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/CommandLine.h" using namespace llvm; @@ -22,12 +25,23 @@ namespace opts { extern cl::opt Verbosity; extern cl::OptionCategory BoltOptCategory; extern cl::opt InferStaleProfile; +extern cl::opt Lite; + +cl::opt NameSimilarityFunctionMatchingThreshold( + "name-similarity-function-matching-threshold", + cl::desc("Match functions using namespace and edit distance"), cl::init(0), + cl::Hidden, cl::cat(BoltOptCategory)); static llvm::cl::opt IgnoreHash("profile-ignore-hash", cl::desc("ignore hash while reading function profile"), cl::Hidden, cl::cat(BoltOptCategory)); +llvm::cl::opt + MatchProfileWithFunctionHash("match-profile-with-function-hash", + cl::desc("Match profile with function hash"), + cl::Hidden, cl::cat(BoltOptCategory)); + llvm::cl::opt ProfileUseDFS("profile-use-dfs", cl::desc("use DFS order for YAML profile"), cl::Hidden, cl::cat(BoltOptCategory)); @@ -329,6 +343,8 @@ Error YAMLProfileReader::preprocessProfile(BinaryContext &BC) { } bool YAMLProfileReader::mayHaveProfileData(const BinaryFunction &BF) { + if (opts::MatchProfileWithFunctionHash) + return true; for (StringRef Name : BF.getNames()) if (ProfileFunctionNames.contains(Name)) return true; @@ -342,6 +358,111 @@ bool YAMLProfileReader::mayHaveProfileData(const BinaryFunction &BF) { return false; } +uint64_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) { + uint64_t MatchedWithNameSimilarity = 0; + ItaniumPartialDemangler Demangler; + + // Demangle and derive namespace from function name. + auto DemangleName = [&](std::string &FunctionName) { + StringRef RestoredName = NameResolver::restore(FunctionName); + return demangle(RestoredName); + }; + auto DeriveNameSpace = [&](std::string &DemangledName) { + if (Demangler.partialDemangle(DemangledName.c_str())) + return std::string(""); + std::vector Buffer(DemangledName.begin(), DemangledName.end()); + size_t BufferSize; + char *NameSpace = + Demangler.getFunctionDeclContextName(&Buffer[0], &BufferSize); + return std::string(NameSpace, BufferSize); + }; + + // Maps namespaces to associated function block counts and gets profile + // function names and namespaces to minimize the number of BFs to process and + // avoid repeated name demangling/namespace derivation. + StringMap> NamespaceToProfiledBFSizes; + std::vector ProfileBFDemangledNames; + ProfileBFDemangledNames.reserve(YamlBP.Functions.size()); + std::vector ProfiledBFNamespaces; + ProfiledBFNamespaces.reserve(YamlBP.Functions.size()); + + for (auto &YamlBF : YamlBP.Functions) { + std::string YamlBFDemangledName = DemangleName(YamlBF.Name); + ProfileBFDemangledNames.push_back(YamlBFDemangledName); + std::string YamlBFNamespace = DeriveNameSpace(YamlBFDemangledName); + ProfiledBFNamespaces.push_back(YamlBFNamespace); + NamespaceToProfiledBFSizes[YamlBFNamespace].insert(YamlBF.NumBasicBlocks); + } + + StringMap> NamespaceToBFs; + + // Maps namespaces to BFs excluding binary functions with no equal sized + // profiled functions belonging to the same namespace. + for (BinaryFunction *BF : BC.getAllBinaryFunctions()) { + std::string DemangledName = BF->getDemangledName(); + std::string Namespace = DeriveNameSpace(DemangledName); + + auto NamespaceToProfiledBFSizesIt = + NamespaceToProfiledBFSizes.find(Namespace); + // Skip if there are no ProfileBFs with a given \p Namespace. + if (NamespaceToProfiledBFSizesIt == NamespaceToProfiledBFSizes.end()) + continue; + // Skip if there are no ProfileBFs in a given \p Namespace with + // equal number of blocks. + if (NamespaceToProfiledBFSizesIt->second.count(BF->size()) == 0) + continue; + auto NamespaceToBFsIt = NamespaceToBFs.find(Namespace); + if (NamespaceToBFsIt == NamespaceToBFs.end()) + NamespaceToBFs[Namespace] = {BF}; + else + NamespaceToBFsIt->second.push_back(BF); + } + + // Iterates through all profiled functions and binary functions belonging to + // the same namespace and matches based on edit distance threshold. + assert(YamlBP.Functions.size() == ProfiledBFNamespaces.size() && + ProfiledBFNamespaces.size() == ProfileBFDemangledNames.size()); + for (size_t I = 0; I < YamlBP.Functions.size(); ++I) { + yaml::bolt::BinaryFunctionProfile &YamlBF = YamlBP.Functions[I]; + std::string &YamlBFNamespace = ProfiledBFNamespaces[I]; + if (YamlBF.Used) + continue; + // Skip if there are no BFs in a given \p Namespace. + auto It = NamespaceToBFs.find(YamlBFNamespace); + if (It == NamespaceToBFs.end()) + continue; + + std::string &YamlBFDemangledName = ProfileBFDemangledNames[I]; + std::vector BFs = It->second; + unsigned MinEditDistance = UINT_MAX; + BinaryFunction *ClosestNameBF = nullptr; + + // Determines BF the closest to the profiled function, in the + // same namespace. + for (BinaryFunction *BF : BFs) { + if (ProfiledFunctions.count(BF)) + continue; + if (BF->size() != YamlBF.NumBasicBlocks) + continue; + std::string BFDemangledName = BF->getDemangledName(); + unsigned BFEditDistance = + StringRef(BFDemangledName).edit_distance(YamlBFDemangledName); + if (BFEditDistance < MinEditDistance) { + MinEditDistance = BFEditDistance; + ClosestNameBF = BF; + } + } + + if (ClosestNameBF && + MinEditDistance <= opts::NameSimilarityFunctionMatchingThreshold) { + matchProfileToFunction(YamlBF, *ClosestNameBF); + ++MatchedWithNameSimilarity; + } + } + + return MatchedWithNameSimilarity; +} + Error YAMLProfileReader::readProfile(BinaryContext &BC) { if (opts::Verbosity >= 1) { outs() << "BOLT-INFO: YAML profile with hash: "; @@ -363,9 +484,24 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { return Profile.Hash == static_cast(BF.getHash()); }; - // We have to do 2 passes since LTO introduces an ambiguity in function - // names. The first pass assigns profiles that match 100% by name and - // by hash. The second pass allows name ambiguity for LTO private functions. + uint64_t MatchedWithExactName = 0; + uint64_t MatchedWithHash = 0; + uint64_t MatchedWithLTOCommonName = 0; + + // Computes hash for binary functions. + if (opts::MatchProfileWithFunctionHash) { + for (auto &[_, BF] : BC.getBinaryFunctions()) { + BF.computeHash(YamlBP.Header.IsDFSOrder, YamlBP.Header.HashFunction); + } + } else if (!opts::IgnoreHash) { + for (BinaryFunction *BF : ProfileBFs) { + if (!BF) + continue; + BF->computeHash(YamlBP.Header.IsDFSOrder, YamlBP.Header.HashFunction); + } + } + + // This first pass assigns profiles that match 100% by name and by hash. for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs)) { if (!BF) continue; @@ -374,15 +510,35 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { // the profile. Function.setExecutionCount(BinaryFunction::COUNT_NO_PROFILE); - // Recompute hash once per function. - if (!opts::IgnoreHash) - Function.computeHash(YamlBP.Header.IsDFSOrder, - YamlBP.Header.HashFunction); - - if (profileMatches(YamlBF, Function)) + if (profileMatches(YamlBF, Function)) { matchProfileToFunction(YamlBF, Function); + ++MatchedWithExactName; + } + } + + // Iterates through profiled functions to match the first binary function with + // the same exact hash. Serves to match identical, renamed functions. + // Collisions are possible where multiple functions share the same exact hash. + if (opts::MatchProfileWithFunctionHash) { + DenseMap StrictHashToBF; + StrictHashToBF.reserve(BC.getBinaryFunctions().size()); + + for (auto &[_, BF] : BC.getBinaryFunctions()) + StrictHashToBF[BF.getHash()] = &BF; + + for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions) { + if (YamlBF.Used) + continue; + auto It = StrictHashToBF.find(YamlBF.Hash); + if (It != StrictHashToBF.end() && !ProfiledFunctions.count(It->second)) { + BinaryFunction *BF = It->second; + matchProfileToFunction(YamlBF, *BF); + ++MatchedWithHash; + } + } } + // This second pass allows name ambiguity for LTO private functions. for (const auto &[CommonName, LTOProfiles] : LTOCommonNameMap) { if (!LTOCommonNameFunctionMap.contains(CommonName)) continue; @@ -396,6 +552,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { for (BinaryFunction *BF : Functions) { if (!ProfiledFunctions.count(BF) && profileMatches(*YamlBF, *BF)) { matchProfileToFunction(*YamlBF, *BF); + ++MatchedWithLTOCommonName; return true; } } @@ -407,19 +564,38 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { // partially. if (!ProfileMatched && LTOProfiles.size() == 1 && Functions.size() == 1 && !LTOProfiles.front()->Used && - !ProfiledFunctions.count(*Functions.begin())) + !ProfiledFunctions.count(*Functions.begin())) { matchProfileToFunction(*LTOProfiles.front(), **Functions.begin()); + ++MatchedWithLTOCommonName; + } } for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs)) if (!YamlBF.Used && BF && !ProfiledFunctions.count(BF)) matchProfileToFunction(YamlBF, *BF); + // Uses name similarity to match functions that were not matched by name. + uint64_t MatchedWithNameSimilarity = + opts::NameSimilarityFunctionMatchingThreshold > 0 + ? matchWithNameSimilarity(BC) + : 0; + for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions) if (!YamlBF.Used && opts::Verbosity >= 1) errs() << "BOLT-WARNING: profile ignored for function " << YamlBF.Name << '\n'; + if (opts::Verbosity >= 1) { + outs() << "BOLT-INFO: matched " << MatchedWithExactName + << " functions with identical names\n"; + outs() << "BOLT-INFO: matched " << MatchedWithHash + << " functions with hash\n"; + outs() << "BOLT-INFO: matched " << MatchedWithLTOCommonName + << " functions with matching LTO common names\n"; + outs() << "BOLT-INFO: matched " << MatchedWithNameSimilarity + << " functions with similar names\n"; + } + // Set for parseFunctionProfile(). NormalizeByInsnCount = usesEvent("cycles") || usesEvent("instructions"); NormalizeByCalls = usesEvent("branches"); @@ -439,6 +615,12 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { BC.setNumUnusedProfiledObjects(NumUnused); + if (opts::Lite && opts::MatchProfileWithFunctionHash) { + for (BinaryFunction *BF : BC.getAllBinaryFunctions()) + if (!BF->hasProfile()) + BF->setIgnored(); + } + return Error::success(); } diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index 0e475031eae4f..1f426d0adfc61 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -29,7 +29,7 @@ #include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCAsmLayout.h" +#include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCStreamer.h" #include "llvm/Object/ObjectFile.h" @@ -646,6 +646,15 @@ void DWARFRewriter::updateDebugInfo() { } else { LocListWritersByCU[CUIndex] = std::make_unique(); + if (std::optional DWOId = CU.getDWOId()) { + assert(LegacyRangesWritersByCU.count(*DWOId) == 0 && + "LegacyRangeLists writer for DWO unit already exists."); + auto LegacyRangesSectionWriterByCU = + std::make_unique(); + LegacyRangesSectionWriterByCU->initSection(CU); + LegacyRangesWritersByCU[*DWOId] = + std::move(LegacyRangesSectionWriterByCU); + } } return LocListWritersByCU[CUIndex++].get(); }; @@ -693,6 +702,7 @@ void DWARFRewriter::updateDebugInfo() { if (Unit->getVersion() >= 5) { TempRangesSectionWriter = RangeListsWritersByCU[*DWOId].get(); } else { + TempRangesSectionWriter = LegacyRangesWritersByCU[*DWOId].get(); RangesBase = RangesSectionWriter->getSectionOffset(); setDwoRangesBase(*DWOId, *RangesBase); } @@ -1274,9 +1284,17 @@ void DWARFRewriter::updateDWARFObjectAddressRanges( } if (RangesBaseInfo) { - DIEBldr.replaceValue(&Die, RangesBaseInfo.getAttribute(), - RangesBaseInfo.getForm(), - DIEInteger(static_cast(*RangesBase))); + if (RangesBaseInfo.getAttribute() == dwarf::DW_AT_GNU_ranges_base) { + auto RangesWriterIterator = + LegacyRangesWritersByCU.find(*Unit.getDWOId()); + assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && + "RangesWriter does not exist for DWOId"); + RangesWriterIterator->second->setDie(&Die); + } else { + DIEBldr.replaceValue(&Die, RangesBaseInfo.getAttribute(), + RangesBaseInfo.getForm(), + DIEInteger(static_cast(*RangesBase))); + } RangesBase = std::nullopt; } } @@ -1294,20 +1312,12 @@ void DWARFRewriter::updateDWARFObjectAddressRanges( RangesAttrInfo.getForm() == dwarf::DW_FORM_sec_offset) NeedConverted = true; - uint64_t CurRangeBase = 0; - if (Unit.isDWOUnit()) { - if (std::optional DWOId = Unit.getDWOId()) - CurRangeBase = getDwoRangesBase(*DWOId); - else - errs() << "BOLT-WARNING: [internal-dwarf-error]: DWOId is not found " - "for DWO Unit."; - } if (NeedConverted || RangesAttrInfo.getForm() == dwarf::DW_FORM_rnglistx) DIEBldr.replaceValue(&Die, dwarf::DW_AT_ranges, dwarf::DW_FORM_rnglistx, DIEInteger(DebugRangesOffset)); else DIEBldr.replaceValue(&Die, dwarf::DW_AT_ranges, RangesAttrInfo.getForm(), - DIEInteger(DebugRangesOffset - CurRangeBase)); + DIEInteger(DebugRangesOffset)); if (!RangesBase) { if (LowPCAttrInfo && @@ -1324,15 +1334,21 @@ void DWARFRewriter::updateDWARFObjectAddressRanges( // If we are at this point we are in the CU/Skeleton CU, and // DW_AT_GNU_ranges_base or DW_AT_rnglists_base doesn't exist. - if (Unit.getVersion() <= 4) + if (Unit.getVersion() <= 4) { DIEBldr.addValue(&Die, dwarf::DW_AT_GNU_ranges_base, dwarf::DW_FORM_data4, - DIEInteger(*RangesBase)); - else if (Unit.getVersion() == 5) + DIEInteger(INT_MAX)); + auto RangesWriterIterator = + LegacyRangesWritersByCU.find(*Unit.getDWOId()); + assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && + "RangesWriter does not exist for DWOId"); + RangesWriterIterator->second->setDie(&Die); + } else if (Unit.getVersion() == 5) { DIEBldr.addValue(&Die, dwarf::DW_AT_rnglists_base, dwarf::DW_FORM_sec_offset, DIEInteger(*RangesBase)); - else + } else { DIEBldr.addValue(&Die, dwarf::DW_AT_rnglists_base, dwarf::DW_FORM_sec_offset, DIEInteger(*RangesBase)); + } return; } @@ -1351,7 +1367,7 @@ void DWARFRewriter::updateDWARFObjectAddressRanges( } } -void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) { +void DWARFRewriter::updateLineTableOffsets(const MCAssembler &Asm) { ErrorOr DbgInfoSection = BC.getUniqueSectionByName(".debug_info"); ErrorOr TypeInfoSection = @@ -1392,7 +1408,8 @@ void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) { if (!StmtOffset) continue; - const uint64_t LineTableOffset = Layout.getSymbolOffset(*Label); + const uint64_t LineTableOffset = + Asm.getSymbolOffset(*Label); DebugLineOffsetMap[*StmtOffset] = LineTableOffset; assert(DbgInfoSection && ".debug_info section must exist"); LineTablePatchMap[CU.get()] = LineTableOffset; @@ -1610,6 +1627,30 @@ void DWARFRewriter::finalizeCompileUnits(DIEBuilder &DIEBlder, DIEStreamer &Streamer, CUOffsetMap &CUMap, const std::list &CUs) { + for (DWARFUnit *CU : CUs) { + if (CU->getVersion() != 4) + continue; + std::optional DWOId = CU->getDWOId(); + if (!DWOId) + continue; + auto RangesWriterIterator = LegacyRangesWritersByCU.find(*DWOId); + assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && + "RangesWriter does not exist for DWOId"); + std::unique_ptr &LegacyRangesWriter = + RangesWriterIterator->second; + std::optional Die = LegacyRangesWriter->getDie(); + if (!Die || !Die.value()) + continue; + DIEValue DvalGNUBase = + Die.value()->findAttribute(dwarf::DW_AT_GNU_ranges_base); + assert(DvalGNUBase && "GNU_ranges_base attribute does not exist for DWOId"); + DIEBlder.replaceValue( + Die.value(), dwarf::DW_AT_GNU_ranges_base, DvalGNUBase.getForm(), + DIEInteger(LegacyRangesSectionWriter->getSectionOffset())); + std::unique_ptr RangesWritersContents = + LegacyRangesWriter->releaseBuffer(); + LegacyRangesSectionWriter->appendToRangeBuffer(*RangesWritersContents); + } DIEBlder.generateAbbrevs(); DIEBlder.finish(); // generate debug_info and CUMap @@ -2097,7 +2138,6 @@ void DWARFRewriter::convertToRangesPatchDebugInfo( DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, uint64_t RangesSectionOffset, DIEValue &LowPCAttrInfo, DIEValue &HighPCAttrInfo, std::optional RangesBase) { - uint32_t BaseOffset = 0; dwarf::Form LowForm = LowPCAttrInfo.getForm(); dwarf::Attribute RangeBaseAttribute = dwarf::DW_AT_GNU_ranges_base; dwarf::Form RangesForm = dwarf::DW_FORM_sec_offset; @@ -2112,45 +2152,40 @@ void DWARFRewriter::convertToRangesPatchDebugInfo( Die.getTag() == dwarf::DW_TAG_skeleton_unit; if (!IsUnitDie) DIEBldr.deleteValue(&Die, LowPCAttrInfo.getAttribute()); - // In DWARF4 for DW_AT_low_pc in binary DW_FORM_addr is used. In the DWO - // section DW_FORM_GNU_addr_index is used. So for if we are converting - // DW_AT_low_pc/DW_AT_high_pc and see DW_FORM_GNU_addr_index. We are - // converting in DWO section, and DW_AT_ranges [DW_FORM_sec_offset] is - // relative to DW_AT_GNU_ranges_base. - if (LowForm == dwarf::DW_FORM_GNU_addr_index) { - // Ranges are relative to DW_AT_GNU_ranges_base. - uint64_t CurRangeBase = 0; - if (std::optional DWOId = Unit.getDWOId()) { - CurRangeBase = getDwoRangesBase(*DWOId); - } - BaseOffset = CurRangeBase; - } else { - // In DWARF 5 we can have DW_AT_low_pc either as DW_FORM_addr, or - // DW_FORM_addrx. Former is when DW_AT_rnglists_base is present. Latter is - // when it's absent. - if (IsUnitDie) { - if (LowForm == dwarf::DW_FORM_addrx) { - const uint32_t Index = AddrWriter->getIndexFromAddress(0, Unit); - DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(), - LowPCAttrInfo.getForm(), DIEInteger(Index)); - } else { - DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(), - LowPCAttrInfo.getForm(), DIEInteger(0)); - } + + // In DWARF 5 we can have DW_AT_low_pc either as DW_FORM_addr, or + // DW_FORM_addrx. Former is when DW_AT_rnglists_base is present. Latter is + // when it's absent. + if (IsUnitDie) { + if (LowForm == dwarf::DW_FORM_addrx) { + const uint32_t Index = AddrWriter->getIndexFromAddress(0, Unit); + DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(), + LowPCAttrInfo.getForm(), DIEInteger(Index)); + } else { + DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(), + LowPCAttrInfo.getForm(), DIEInteger(0)); } - // Original CU didn't have DW_AT_*_base. We converted it's children (or - // dwo), so need to insert it into CU. - if (RangesBase) + } + // Original CU didn't have DW_AT_*_base. We converted it's children (or + // dwo), so need to insert it into CU. + if (RangesBase) { + if (Unit.getVersion() >= 5) { DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset, DIEInteger(*RangesBase)); + } else { + DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset, + DIEInteger(INT_MAX)); + auto RangesWriterIterator = + LegacyRangesWritersByCU.find(*Unit.getDWOId()); + assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && + "RangesWriter does not exist for DWOId"); + RangesWriterIterator->second->setDie(&Die); + } } - uint64_t RangeAttrVal = RangesSectionOffset - BaseOffset; - if (Unit.getVersion() >= 5) - RangeAttrVal = RangesSectionOffset; // HighPC was conveted into DW_AT_ranges. // For DWARF5 we only access ranges through index. DIEBldr.replaceValue(&Die, HighPCAttrInfo.getAttribute(), dwarf::DW_AT_ranges, - RangesForm, DIEInteger(RangeAttrVal)); + RangesForm, DIEInteger(RangesSectionOffset)); } diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index aa18e79f73b9d..03b414b71caca 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -295,9 +295,6 @@ class LinuxKernelRewriter final : public MetadataRewriter { if (Error E = processSMPLocks()) return E; - if (Error E = readORCTables()) - return E; - if (Error E = readStaticCalls()) return E; @@ -313,6 +310,11 @@ class LinuxKernelRewriter final : public MetadataRewriter { if (Error E = readAltInstructions()) return E; + // Some ORC entries could be linked to alternative instruction + // sequences. Hence, we read ORC after .altinstructions. + if (Error E = readORCTables()) + return E; + if (Error E = readPCIFixupTable()) return E; @@ -563,11 +565,28 @@ Error LinuxKernelRewriter::readORCTables() { if (!BF->hasInstructions()) continue; - MCInst *Inst = BF->getInstructionAtOffset(IP - BF->getAddress()); - if (!Inst) + const uint64_t Offset = IP - BF->getAddress(); + MCInst *Inst = BF->getInstructionAtOffset(Offset); + if (!Inst) { + // Check if there is an alternative instruction(s) at this IP. Multiple + // alternative instructions can take a place of a single original + // instruction and each alternative can have a separate ORC entry. + // Since ORC table is shared between all alternative sequences, there's + // a requirement that only one (out of many) sequences can have an + // instruction from the ORC table to avoid ambiguities/conflicts. + // + // For now, we have limited support for alternatives. I.e. we still print + // functions with them, but will not change the code in the output binary. + // As such, we can ignore alternative ORC entries. They will be preserved + // in the binary, but will not get printed in the instruction stream. + Inst = BF->getInstructionContainingOffset(Offset); + if (Inst || BC.MIB->hasAnnotation(*Inst, "AltInst")) + continue; + return createStringError( errc::executable_format_error, "no instruction at address 0x%" PRIx64 " in .orc_unwind_ip", IP); + } // Some addresses will have two entries associated with them. The first // one being a "weak" section terminator. Since we ignore the terminator, @@ -1440,7 +1459,7 @@ Error LinuxKernelRewriter::tryReadAltInstructions(uint32_t AltInstFeatureSize, AltBF->setIgnored(); } - if (!BF || !BC.shouldEmit(*BF)) + if (!BF || !BF->hasInstructions()) continue; if (OrgInstAddress + OrgSize > BF->getAddress() + BF->getSize()) diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 1a3a8af21d81b..ded2f577237fe 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -40,7 +40,6 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCStreamer.h" @@ -76,12 +75,12 @@ extern cl::opt X86AlignBranchWithin32BBoundaries; namespace opts { -extern cl::opt AlignMacroOpFusion; extern cl::list HotTextMoveSections; extern cl::opt Hugify; extern cl::opt Instrument; extern cl::opt JumpTables; extern cl::opt KeepNops; +extern cl::opt Lite; extern cl::list ReorderData; extern cl::opt ReorderFunctions; extern cl::opt TerminalTrap; @@ -140,9 +139,6 @@ KeepTmp("keep-tmp", cl::Hidden, cl::cat(BoltCategory)); -cl::opt Lite("lite", cl::desc("skip processing of cold functions"), - cl::cat(BoltCategory)); - static cl::opt LiteThresholdPct("lite-threshold-pct", cl::desc("threshold (in percent) for selecting functions to process in lite " @@ -1972,12 +1968,6 @@ void RewriteInstance::adjustCommandLineOptions() { if (RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary()) RtLibrary->adjustCommandLineOptions(*BC); - if (opts::AlignMacroOpFusion != MFT_NONE && !BC->isX86()) { - BC->outs() - << "BOLT-INFO: disabling -align-macro-fusion on non-x86 platform\n"; - opts::AlignMacroOpFusion = MFT_NONE; - } - if (BC->isX86() && BC->MAB->allowAutoPadding()) { if (!BC->HasRelocations) { BC->errs() @@ -1988,13 +1978,6 @@ void RewriteInstance::adjustCommandLineOptions() { BC->outs() << "BOLT-WARNING: using mitigation for Intel JCC erratum, layout " "may take several minutes\n"; - opts::AlignMacroOpFusion = MFT_NONE; - } - - if (opts::AlignMacroOpFusion != MFT_NONE && !BC->HasRelocations) { - BC->outs() << "BOLT-INFO: disabling -align-macro-fusion in non-relocation " - "mode\n"; - opts::AlignMacroOpFusion = MFT_NONE; } if (opts::SplitEH && !BC->HasRelocations) { @@ -2016,14 +1999,6 @@ void RewriteInstance::adjustCommandLineOptions() { opts::StrictMode = true; } - if (BC->isX86() && BC->HasRelocations && - opts::AlignMacroOpFusion == MFT_HOT && !ProfileReader) { - BC->outs() - << "BOLT-INFO: enabling -align-macro-fusion=all since no profile " - "was specified\n"; - opts::AlignMacroOpFusion = MFT_ALL; - } - if (!BC->HasRelocations && opts::ReorderFunctions != ReorderFunctions::RT_NONE) { BC->errs() << "BOLT-ERROR: function reordering only works when " @@ -3510,9 +3485,8 @@ void RewriteInstance::emitAndLink() { updateOutputValues(*Linker); if (opts::UpdateDebugSections) { - MCAsmLayout FinalLayout( - static_cast(Streamer.get())->getAssembler()); - DebugInfoRewriter->updateLineTableOffsets(FinalLayout); + DebugInfoRewriter->updateLineTableOffsets( + static_cast(*Streamer).getAssembler()); } if (RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary()) diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index 5220d305b838d..1a2327df1d323 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -141,10 +141,6 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { *AArch64ExprB.getSubExpr(), Comp); } - bool isMacroOpFusionPair(ArrayRef Insts) const override { - return false; - } - bool shortenInstruction(MCInst &, const MCSubtargetInfo &) const override { return false; } @@ -706,8 +702,20 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { unsigned ShiftVal = AArch64_AM::getArithShiftValue(OperandExtension); AArch64_AM::ShiftExtendType ExtendType = AArch64_AM::getArithExtendType(OperandExtension); - if (ShiftVal != 2) - llvm_unreachable("Failed to match indirect branch! (fragment 2)"); + if (ShiftVal != 2) { + // TODO: Handle the patten where ShiftVal != 2. + // The following code sequence below has no shift amount, + // the range could be 0 to 4. + // The pattern comes from libc, it occurs when the binary is static. + // adr x6, 0x219fb0 + // add x6, x6, x14, lsl #2 + // ldr w7, [x6] + // add x6, x6, w7, sxtw => no shift amount + // br x6 + errs() << "BOLT-WARNING: " + "Failed to match indirect branch: ShiftVAL != 2 \n"; + return false; + } if (ExtendType == AArch64_AM::SXTB) ScaleValue = 1LL; @@ -752,6 +760,19 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { return true; } + if (DefJTBaseAdd->getOpcode() == AArch64::ADR) { + // TODO: Handle the pattern where there is no adrp/add pair. + // It also occurs when the binary is static. + // adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50> + // ldrh w13, [x13, w12, uxtw #1] + // adr x12, 0x247b30 <__gettextparse+0x5b0> + // add x13, x12, w13, sxth #2 + // br x13 + errs() << "BOLT-WARNING: Failed to match indirect branch: " + "nop/adr instead of adrp/add \n"; + return false; + } + assert(DefJTBaseAdd->getOpcode() == AArch64::ADDXri && "Failed to match jump table base address pattern! (1)"); diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp index 515c9a94c58cd..37136f4a5c551 100644 --- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp @@ -328,19 +328,19 @@ class X86MCPlusBuilder : public MCPlusBuilder { return false; } - bool isReversibleBranch(const MCInst &Inst) const override { - if (isDynamicBranch(Inst)) - return false; - + bool isUnsupportedInstruction(const MCInst &Inst) const override { switch (Inst.getOpcode()) { default: - return true; + return false; + case X86::LOOP: case X86::LOOPE: case X86::LOOPNE: case X86::JECXZ: case X86::JRCXZ: - return false; + // These have a short displacement, and therefore (often) break after + // basic block relayout. + return true; } } @@ -661,40 +661,6 @@ class X86MCPlusBuilder : public MCPlusBuilder { return (Desc.TSFlags & X86II::EncodingMask) == X86II::EVEX; } - bool isMacroOpFusionPair(ArrayRef Insts) const override { - const auto *I = Insts.begin(); - while (I != Insts.end() && isPrefix(*I)) - ++I; - if (I == Insts.end()) - return false; - - const MCInst &FirstInst = *I; - ++I; - while (I != Insts.end() && isPrefix(*I)) - ++I; - if (I == Insts.end()) - return false; - const MCInst &SecondInst = *I; - - if (!isConditionalBranch(SecondInst)) - return false; - // Cannot fuse if the first instruction uses RIP-relative memory. - if (hasPCRelOperand(FirstInst)) - return false; - - const X86::FirstMacroFusionInstKind CmpKind = - X86::classifyFirstOpcodeInMacroFusion(FirstInst.getOpcode()); - if (CmpKind == X86::FirstMacroFusionInstKind::Invalid) - return false; - - X86::CondCode CC = static_cast(getCondCode(SecondInst)); - X86::SecondMacroFusionInstKind BranchKind = - X86::classifySecondCondCodeInMacroFusion(CC); - if (BranchKind == X86::SecondMacroFusionInstKind::Invalid) - return false; - return X86::isMacroFused(CmpKind, BranchKind); - } - std::optional evaluateX86MemoryOperand(const MCInst &Inst) const override { int MemOpNo = getMemoryOperandNo(Inst); @@ -1879,11 +1845,9 @@ class X86MCPlusBuilder : public MCPlusBuilder { continue; } - // Handle conditional branches and ignore indirect branches - if (isReversibleBranch(*I) && getCondCode(*I) == X86::COND_INVALID) { - // Indirect branch + // Ignore indirect branches + if (getCondCode(*I) == X86::COND_INVALID) return false; - } if (CondBranch == nullptr) { const MCSymbol *TargetBB = getTargetSymbol(*I); diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp index 41c89bc8aeba4..b9bc79f408a6b 100644 --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -128,6 +128,9 @@ cl::opt cl::desc("instrument code to generate accurate profile data"), cl::cat(BoltOptCategory)); +cl::opt Lite("lite", cl::desc("skip processing of cold functions"), + cl::cat(BoltCategory)); + cl::opt OutputFilename("o", cl::desc(""), diff --git a/bolt/test/AArch64/test-indirect-branch.s b/bolt/test/AArch64/test-indirect-branch.s new file mode 100644 index 0000000000000..168e50c8f47f5 --- /dev/null +++ b/bolt/test/AArch64/test-indirect-branch.s @@ -0,0 +1,83 @@ +// Test how BOLT handles indirect branch sequence of instructions in +// AArch64MCPlus builder. + +// clang-format off + +// REQUIRES: system-linux +// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o +// RUN: %clang %cflags --target=aarch64-unknown-linux %t.o -o %t.exe -Wl,-q +// RUN: llvm-bolt %t.exe -o %t.bolt --print-cfg --strict\ +// RUN: -v=1 2>&1 | FileCheck %s + +// Pattern 1: there is no shift amount after the 'add' instruction. +// +// adr x6, 0x219fb0 +// add x6, x6, x14, lsl #2 +// ldr w7, [x6] +// add x6, x6, w7, sxtw => no shift amount +// br x6 +// + +// Pattern 2: nop/adr pair is used in place of adrp/add +// +// nop => nop/adr instead of adrp/add +// adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50> +// ldrh w13, [x13, w12, uxtw #1] +// adr x12, 0x247b30 <__gettextparse+0x5b0> +// add x13, x12, w13, sxth #2 +// br x13 + + .section .text + .align 4 + .globl _start + .type _start, %function +_start: + bl test1 + bl test2 +// mov x0, #4 +// mov w8, #93 +// svc #0 + +// Pattern 1 +// CHECK: BOLT-WARNING: Failed to match indirect branch: ShiftVAL != 2 + .globl test1 + .type test1, %function +test1: + mov x1, #0 + adr x3, datatable + add x3, x3, x1, lsl #2 + ldr w2, [x3] + add x3, x3, w2, sxtw + br x3 +test1_0: + ret +test1_1: + ret +test1_2: + ret + +// Pattern 2 +// CHECK: BOLT-WARNING: Failed to match indirect branch: nop/adr instead of adrp/add + .globl test2 + .type test2, %function +test2: + nop + adr x3, jump_table + ldrh w3, [x3, x1, lsl #1] + adr x1, test2_0 + add x3, x1, w3, sxth #2 + br x3 +test2_0: + ret +test2_1: + ret + + .section .rodata,"a",@progbits +datatable: + .word test1_0-datatable + .word test1_1-datatable + .word test1_2-datatable + +jump_table: + .hword (test2_0-test2_0)>>2 + .hword (test2_1-test2_0)>>2 diff --git a/bolt/test/X86/debug-fission-single-convert.s b/bolt/test/X86/debug-fission-single-convert.s index 28fcb6686e0a2..4cd881740b2f8 100644 --- a/bolt/test/X86/debug-fission-single-convert.s +++ b/bolt/test/X86/debug-fission-single-convert.s @@ -31,11 +31,11 @@ # CHECK-DWO-DWO: 00000010 # CHECK-DWO-DWO: 00000050 # CHECK-DWO-DWO: DW_TAG_subprogram -# CHECK-DWO-DWO-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 +# CHECK-DWO-DWO-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 # CHECK-DWO-DWO: DW_TAG_subprogram -# CHECK-DWO-DWO-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000020 +# CHECK-DWO-DWO-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000030 # CHECK-DWO-DWO: DW_TAG_subprogram -# CHECK-DWO-DWO-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040 +# CHECK-DWO-DWO-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000050 # CHECK-ADDR-SEC: .debug_addr contents: # CHECK-ADDR-SEC: 0x00000000: Addrs: [ diff --git a/bolt/test/X86/dwarf4-df-dualcu.test b/bolt/test/X86/dwarf4-df-dualcu.test index b690623b70d83..fb328eb1872e0 100644 --- a/bolt/test/X86/dwarf4-df-dualcu.test +++ b/bolt/test/X86/dwarf4-df-dualcu.test @@ -37,36 +37,38 @@ ; BOLT: .debug_ranges ; BOLT-NEXT: 00000000 -; BOLT-NEXT: 00000010 [[#%.16x,ADDR:]] [[#%.16x,ADDRB:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] ; BOLT-NEXT: 00000010 -; BOLT-NEXT: 00000030 [[#%.16x,ADDR1:]] [[#%.16x,ADDR1B:]] -; BOLT-NEXT: 00000030 -; BOLT-NEXT: 00000050 [[#%.16x,ADDR2:]] [[#%.16x,ADDR2B:]] -; BOLT-NEXT: 00000050 [[#%.16x,ADDR3:]] [[#%.16x,ADDR3B:]] +; BOLT-NEXT: 00000040 +; BOLT-NEXT: 00000050 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] ; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000080 [[#%.16x,ADDR4:]] [[#%.16x,ADDR4B:]] -; BOLT-NEXT: 00000080 -; BOLT-NEXT: 000000a0 [[#%.16x,ADDR5:]] [[#%.16x,ADDR5B:]] -; BOLT-NEXT: 000000a0 +; BOLT-NEXT: 00000070 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] +; BOLT-NEXT: 00000070 +; BOLT-NEXT: 00000090 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] +; BOLT-NEXT: 00000090 +; BOLT-NEXT: 000000b0 +; BOLT-NEXT: 000000c0 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] +; BOLT-NEXT: 000000c0 ; BOLT: DW_TAG_compile_unit ; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x00000016] = "main.dwo.dwo") ; BOLT-NEXT: DW_AT_GNU_dwo_id ; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) -; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000050 -; BOLT-NEXT: [0x[[#ADDR2]], 0x[[#ADDR2B]]) -; BOLT-NEXT: [0x[[#ADDR3]], 0x[[#ADDR3B]])) +; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 +; BOLT-NEXT: [0x[[#ADDR1]], 0x[[#ADDRB1]]) +; BOLT-NEXT: [0x[[#ADDR2]], 0x[[#ADDRB2]])) ; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000000) -; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000010) +; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000040) ; BOLT-NEXT: Compile ; BOLT: DW_TAG_compile_unit ; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x00000023] = "helper.dwo.dwo") ; BOLT-NEXT: DW_AT_GNU_dwo_id ; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) -; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x000000a0 -; BOLT-NEXT: [0x[[#ADDR5]], 0x[[#ADDR5B]]) +; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000090 +; BOLT-NEXT: [0x[[#ADDR3]], 0x[[#ADDRB3]]) ; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000010) -; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000080) +; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x000000b0) ; PRE-BOLT-DWO-MAIN: version = 0x0004 ; PRE-BOLT-DWO-MAIN: DW_TAG_compile_unit @@ -113,13 +115,13 @@ ; BOLT-DWO-MAIN-NEXT: DW_AT_decl_line ; BOLT-DWO-MAIN-NEXT: DW_AT_location [DW_FORM_exprloc] (DW_OP_GNU_addr_index 0x1) ; BOLT-DWO-MAIN: DW_TAG_subprogram [4] -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 ; BOLT-DWO-MAIN-NEXT: ) ; BOLT-DWO-MAIN-NEXT: DW_AT_frame_base ; BOLT-DWO-MAIN-NEXT: DW_AT_linkage_name [DW_FORM_GNU_str_index] (indexed (00000003) string = "_Z3usePiS_") ; BOLT-DWO-MAIN-NEXT: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000004) string = "use") ; BOLT-DWO-MAIN: DW_TAG_subprogram [6] -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000020 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000030 ; BOLT-DWO-MAIN-NEXT: ) ; BOLT-DWO-MAIN-NEXT: DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_reg6 RBP) ; BOLT-DWO-MAIN-NEXT: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000005) string = "main") @@ -160,4 +162,4 @@ ; BOLT-DWO-HELPER-NEXT: DW_AT_decl_line ; BOLT-DWO-HELPER-NEXT: DW_AT_location [DW_FORM_exprloc] (DW_OP_GNU_addr_index 0x1) ; BOLT-DWO-HELPER: DW_TAG_subprogram [4] -; BOLT-DWO-HELPER-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 +; BOLT-DWO-HELPER-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 diff --git a/bolt/test/X86/dwarf4-df-input-lowpc-ranges-cus.test b/bolt/test/X86/dwarf4-df-input-lowpc-ranges-cus.test index c9abd02bbb7d9..cf9357d5f3c59 100644 --- a/bolt/test/X86/dwarf4-df-input-lowpc-ranges-cus.test +++ b/bolt/test/X86/dwarf4-df-input-lowpc-ranges-cus.test @@ -17,45 +17,47 @@ ; BOLT: .debug_ranges ; BOLT-NEXT: 00000000 -; BOLT-NEXT: 00000010 -; BOLT-NEXT: 00000010 -; BOLT-NEXT: 00000010 +; BOLT-NEXT: 00000010 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR4:]] [[#%.16x,ADDRB4:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]] ; BOLT-NEXT: 00000010 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000090 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR4:]] [[#%.16x,ADDRB4:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]] ; BOLT-NEXT: 00000090 -; BOLT-NEXT: 00000110 -; BOLT-NEXT: 00000110 -; BOLT-NEXT: 00000110 -; BOLT-NEXT: 00000110 -; BOLT-NEXT: 00000150 -; BOLT-NEXT: 00000150 -; BOLT-NEXT: 00000150 -; BOLT-NEXT: 00000150 -; BOLT-NEXT: 00000190 [[#%.16x,ADDR8:]] [[#%.16x,ADDRB8:]] -; BOLT-NEXT: 00000190 [[#%.16x,ADDR9:]] [[#%.16x,ADDRB9:]] -; BOLT-NEXT: 00000190 [[#%.16x,ADDR10:]] [[#%.16x,ADDRB10:]] -; BOLT-NEXT: 00000190 [[#%.16x,ADDR11:]] [[#%.16x,ADDRB11:]] -; BOLT-NEXT: 00000190 [[#%.16x,ADDR12:]] [[#%.16x,ADDRB12:]] -; BOLT-NEXT: 00000190 [[#%.16x,ADDR13:]] [[#%.16x,ADDRB13:]] -; BOLT-NEXT: 00000190 [[#%.16x,ADDR14:]] [[#%.16x,ADDRB14:]] -; BOLT-NEXT: 00000190 +; BOLT-NEXT: 000000a0 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] +; BOLT-NEXT: 000000a0 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] +; BOLT-NEXT: 000000a0 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] +; BOLT-NEXT: 000000a0 +; BOLT-NEXT: 000000e0 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]] +; BOLT-NEXT: 000000e0 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]] +; BOLT-NEXT: 000000e0 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]] +; BOLT-NEXT: 000000e0 +; BOLT-NEXT: 00000120 [[#%.16x,ADDR8:]] [[#%.16x,ADDRB8:]] +; BOLT-NEXT: 00000120 [[#%.16x,ADDR9:]] [[#%.16x,ADDRB9:]] +; BOLT-NEXT: 00000120 [[#%.16x,ADDR10:]] [[#%.16x,ADDRB10:]] +; BOLT-NEXT: 00000120 [[#%.16x,ADDR11:]] [[#%.16x,ADDRB11:]] +; BOLT-NEXT: 00000120 [[#%.16x,ADDR12:]] [[#%.16x,ADDRB12:]] +; BOLT-NEXT: 00000120 [[#%.16x,ADDR13:]] [[#%.16x,ADDRB13:]] +; BOLT-NEXT: 00000120 [[#%.16x,ADDR14:]] [[#%.16x,ADDRB14:]] +; BOLT-NEXT: 00000120 +; BOLT-NEXT: 000001a0 +; BOLT-NEXT: 000001b0 [[#%.16x,ADDR8:]] [[#%.16x,ADDRB8:]] +; BOLT-NEXT: 000001b0 [[#%.16x,ADDR9:]] [[#%.16x,ADDRB9:]] +; BOLT-NEXT: 000001b0 [[#%.16x,ADDR10:]] [[#%.16x,ADDRB10:]] +; BOLT-NEXT: 000001b0 +; BOLT-NEXT: 000001f0 [[#%.16x,ADDR12:]] [[#%.16x,ADDRB12:]] +; BOLT-NEXT: 000001f0 [[#%.16x,ADDR13:]] [[#%.16x,ADDRB13:]] +; BOLT-NEXT: 000001f0 [[#%.16x,ADDR14:]] [[#%.16x,ADDRB14:]] +; BOLT-NEXT: 000001f0 ; BOLT: DW_TAG_compile_unit ; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x{{[0-9a-fA-F]+}}] = "main.dwo.dwo") ; BOLT-NEXT: DW_AT_GNU_dwo_id -; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000010) +; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000090) ; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) -; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000090 +; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 ; BOLT-NEXT: [0x[[#ADDR1]], 0x[[#ADDRB1]]) ; BOLT-NEXT: [0x[[#ADDR2]], 0x[[#ADDRB2]]) ; BOLT-NEXT: [0x[[#ADDR3]], 0x[[#ADDRB3]]) @@ -64,13 +66,14 @@ ; BOLT-NEXT: [0x[[#ADDR6]], 0x[[#ADDRB6]]) ; BOLT-NEXT: [0x[[#ADDR7]], 0x[[#ADDRB7]]) ; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000000) +; BOLT-NEXT: Compile Unit ; BOLT: DW_TAG_compile_unit ; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x{{[0-9a-fA-F]+}}] = "mainOther.dwo.dwo") ; BOLT-NEXT: DW_AT_GNU_dwo_id -; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000110) +; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x000001a0) ; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) -; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000190 +; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000120 ; BOLT-NEXT: [0x[[#ADDR8]], 0x[[#ADDRB8]]) ; BOLT-NEXT: [0x[[#ADDR9]], 0x[[#ADDRB9]]) ; BOLT-NEXT: [0x[[#ADDR10]], 0x[[#ADDRB10]]) @@ -79,19 +82,20 @@ ; BOLT-NEXT: [0x[[#ADDR13]], 0x[[#ADDRB13]]) ; BOLT-NEXT: [0x[[#ADDR14]], 0x[[#ADDRB14]]) ; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000018) +; BOLT: {{^$}} ; BOLT-DWO-MAIN: DW_TAG_subprogram -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000050 ; BOLT-DWO-MAIN: DW_TAG_subprogram -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000050 diff --git a/bolt/test/X86/dwarf4-df-input-lowpc-ranges.test b/bolt/test/X86/dwarf4-df-input-lowpc-ranges.test index 276bea4ba0c1c..ab4353a282475 100644 --- a/bolt/test/X86/dwarf4-df-input-lowpc-ranges.test +++ b/bolt/test/X86/dwarf4-df-input-lowpc-ranges.test @@ -15,29 +15,30 @@ ; BOLT: .debug_ranges ; BOLT-NEXT: 00000000 -; BOLT-NEXT: 00000010 -; BOLT-NEXT: 00000010 -; BOLT-NEXT: 00000010 +; BOLT-NEXT: 00000010 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR4:]] [[#%.16x,ADDRB4:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]] +; BOLT-NEXT: 00000010 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]] ; BOLT-NEXT: 00000010 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000050 -; BOLT-NEXT: 00000090 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR4:]] [[#%.16x,ADDRB4:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]] -; BOLT-NEXT: 00000090 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]] ; BOLT-NEXT: 00000090 +; BOLT-NEXT: 000000a0 [[#%.16x,ADDR1:]] [[#%.16x,ADDRB1:]] +; BOLT-NEXT: 000000a0 [[#%.16x,ADDR2:]] [[#%.16x,ADDRB2:]] +; BOLT-NEXT: 000000a0 [[#%.16x,ADDR3:]] [[#%.16x,ADDRB3:]] +; BOLT-NEXT: 000000a0 +; BOLT-NEXT: 000000e0 [[#%.16x,ADDR5:]] [[#%.16x,ADDRB5:]] +; BOLT-NEXT: 000000e0 [[#%.16x,ADDR6:]] [[#%.16x,ADDRB6:]] +; BOLT-NEXT: 000000e0 [[#%.16x,ADDR7:]] [[#%.16x,ADDRB7:]] +; BOLT-NEXT: 000000e0 ; BOLT: DW_TAG_compile_unit ; BOLT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x{{[0-9a-fA-F]+}}] = "main.dwo.dwo") ; BOLT-NEXT: DW_AT_GNU_dwo_id -; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000010) +; BOLT-NEXT: DW_AT_GNU_ranges_base [DW_FORM_sec_offset] (0x00000090) ; BOLT-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) -; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000090 +; BOLT-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 ; BOLT-NEXT: [0x[[#ADDR1]], 0x[[#ADDRB1]]) ; BOLT-NEXT: [0x[[#ADDR2]], 0x[[#ADDRB2]]) ; BOLT-NEXT: [0x[[#ADDR3]], 0x[[#ADDRB3]]) @@ -48,9 +49,9 @@ ; BOLT-NEXT: DW_AT_GNU_addr_base [DW_FORM_sec_offset] (0x00000000) ; BOLT-DWO-MAIN: DW_TAG_subprogram -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000010 ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram ; BOLT-DWO-MAIN: DW_TAG_subprogram -; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000040 +; BOLT-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000050 diff --git a/bolt/test/X86/dwarf5-df-types-debug-names.test b/bolt/test/X86/dwarf5-df-types-debug-names.test index f5a2c9c10353e..7c1c8e4fd5b38 100644 --- a/bolt/test/X86/dwarf5-df-types-debug-names.test +++ b/bolt/test/X86/dwarf5-df-types-debug-names.test @@ -18,19 +18,19 @@ ; BOLT: type_signature = [[TYPE1:0x[0-9a-f]*]] ; BOLT: Compile Unit ; BOLT: type_signature = [[TYPE2:0x[0-9a-f]*]] -; BOLT: type_signature = [[TYPE3:0x[0-9a-f]*]] +; BOLT: type_signature = [[TYPE1]] ; BOLT: Compile Unit ; BOLT: [[OFFSET:0x[0-9a-f]*]]: Compile Unit ; BOLT: [[OFFSET1:0x[0-9a-f]*]]: Compile Unit ; BOLT: Name Index @ 0x0 { ; BOLT-NEXT: Header { -; BOLT-NEXT: Length: 0x17E +; BOLT-NEXT: Length: 0x176 ; BOLT-NEXT: Format: DWARF32 ; BOLT-NEXT: Version: 5 ; BOLT-NEXT: CU count: 2 ; BOLT-NEXT: Local TU count: 0 -; BOLT-NEXT: Foreign TU count: 4 +; BOLT-NEXT: Foreign TU count: 3 ; BOLT-NEXT: Bucket count: 9 ; BOLT-NEXT: Name count: 9 ; BOLT-NEXT: Abbreviations table size: 0x37 @@ -44,7 +44,6 @@ ; BOLT-NEXT: ForeignTU[0]: [[TYPE]] ; BOLT-NEXT: ForeignTU[1]: [[TYPE1]] ; BOLT-NEXT: ForeignTU[2]: [[TYPE2]] -; BOLT-NEXT: ForeignTU[3]: [[TYPE3]] ; BOLT-NEXT: ] ; BOLT-NEXT: Abbreviations [ ; BOLT-NEXT: Abbreviation [[ABBREV:0x[0-9a-f]*]] { @@ -173,7 +172,7 @@ ; BOLT-NEXT: Entry @ {{.+}} { ; BOLT-NEXT: Abbrev: [[ABBREV]] ; BOLT-NEXT: Tag: DW_TAG_structure_type -; BOLT-NEXT: DW_IDX_type_unit: 0x03 +; BOLT-NEXT: DW_IDX_type_unit: 0x01 ; BOLT-NEXT: DW_IDX_compile_unit: 0x01 ; BOLT-NEXT: DW_IDX_die_offset: 0x00000021 ; BOLT-NEXT: DW_IDX_parent: @@ -237,7 +236,7 @@ ; BOLT-NEXT: Entry @ {{.+}} { ; BOLT-NEXT: Abbrev: 0x5 ; BOLT-NEXT: Tag: DW_TAG_base_type -; BOLT-NEXT: DW_IDX_type_unit: 0x03 +; BOLT-NEXT: DW_IDX_type_unit: 0x01 ; BOLT-NEXT: DW_IDX_compile_unit: 0x01 ; BOLT-NEXT: DW_IDX_die_offset: 0x00000048 ; BOLT-NEXT: DW_IDX_parent: diff --git a/bolt/test/X86/hashing-based-function-matching.test b/bolt/test/X86/hashing-based-function-matching.test new file mode 100644 index 0000000000000..41c991b4612ec --- /dev/null +++ b/bolt/test/X86/hashing-based-function-matching.test @@ -0,0 +1,63 @@ +## Tests function matching in YAMLProfileReader by function hash. + +# 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 -v=2 \ +# RUN: --print-cfg --match-profile-with-function-hash --profile-ignore-hash=0 2>&1 | FileCheck %s + +# CHECK: BOLT-INFO: matched 1 functions with hash + +#--- 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 + .reloc 0, R_X86_64_NONE + .cfi_endproc + .size main, .-main + +#--- yaml +--- +header: + profile-version: 1 + binary-name: 'hashing-based-function-matching.s.tmp.exe' + binary-build-id: '' + profile-flags: [ lbr ] + profile-origin: branch profile reader + profile-events: '' + dfs-order: false + hash-func: xxh3 +functions: + - name: main2 + fid: 0 + hash: 0x6E7F15254DE2478 + exec: 1 + nblocks: 6 + blocks: + - bid: 1 + insns: 1 + succ: [ { bid: 3, cnt: 1} ] +... diff --git a/bolt/test/X86/issue20.s b/bolt/test/X86/issue20.s index 99a4f2ea2ac99..9645eaf2e6223 100644 --- a/bolt/test/X86/issue20.s +++ b/bolt/test/X86/issue20.s @@ -13,7 +13,6 @@ # CHECK-NOT: BOLT-INFO: UCE removed {{.*}} blocks and {{.*}} bytes of code # CHECK: Binary Function "main" # CHECK: .LFT{{.*}} (2 instructions, align : 1) -# CHECK-NEXT: CFI State : 0 # CHECK-NEXT: 00000004: andq # CHECK-NEXT: 00000008: jmpq # CHECK-NEXT: Successors: .Ltmp{{.*}}, .Ltmp{{.*}}, .Ltmp{{.*}}, .Ltmp{{.*}} diff --git a/bolt/test/X86/issue20.test b/bolt/test/X86/issue20.test index dcb1ce5ab1567..09f9c36cc9e3d 100644 --- a/bolt/test/X86/issue20.test +++ b/bolt/test/X86/issue20.test @@ -9,7 +9,6 @@ CHECK-NOT: BOLT-INFO: UCE removed {{.*}} blocks and {{.*}}| bytes of code CHECK: Binary Function "main" CHECK: .LFT0 (2 instructions, align : 1) -CHECK-NEXT: CFI State : 0 CHECK-NEXT: 00000004: andq CHECK-NEXT: 00000008: jmpq CHECK-NEXT: Successors: .Ltmp1, .Ltmp2, .Ltmp3, .Ltmp4 diff --git a/bolt/test/X86/linux-alt-instruction.s b/bolt/test/X86/linux-alt-instruction.s index dc1b12a277785..fe3abbfc2b4c9 100644 --- a/bolt/test/X86/linux-alt-instruction.s +++ b/bolt/test/X86/linux-alt-instruction.s @@ -38,7 +38,7 @@ # RUN: llvm-bolt %t.exe --print-cfg -o %t.fs4.out | FileCheck %s # CHECK: BOLT-INFO: Linux kernel binary detected -# CHECK: BOLT-INFO: parsed 2 alternative instruction entries +# CHECK: BOLT-INFO: parsed 3 alternative instruction entries .text .globl _start @@ -50,10 +50,12 @@ _start: # CHECK: rdtsc # CHECK-SAME: AltInst: 1 # CHECK-SAME: AltInst2: 2 +# CHECK-SAME: AltInst3: 3 nop # CHECK-NEXT: nop # CHECK-SAME: AltInst: 1 # CHECK-SAME: AltInst2: 2 +# CHECK-SAME: AltInst3: 3 nop nop .L1: @@ -66,6 +68,9 @@ _start: rdtsc .A1: rdtscp +.A2: + pushf + pop %rax .Ae: ## Alternative instruction info. @@ -92,11 +97,51 @@ _start: .word 0x3b # feature flags .endif .byte .L1 - .L0 # org size - .byte .Ae - .A1 # alt size + .byte .A2 - .A1 # alt size .ifdef PADLEN .byte 0 .endif + .long .L0 - . # org instruction + .long .A2 - . # alt instruction +.ifdef FEATURE_SIZE_4 + .long 0x110 # feature flags +.else + .word 0x110 # feature flags +.endif + .byte .L1 - .L0 # org size + .byte .Ae - .A2 # alt size +.ifdef PADLEN + .byte 0 +.endif + +## ORC unwind for "pushf; pop %rax" alternative sequence. + .section .orc_unwind,"a",@progbits + .align 4 + .section .orc_unwind_ip,"a",@progbits + .align 4 + + .section .orc_unwind + .2byte 8 + .2byte 0 + .2byte 0x205 + .section .orc_unwind_ip + .long _start - . + + .section .orc_unwind + .2byte 16 + .2byte 0 + .2byte 0x205 + .section .orc_unwind_ip + .long .L0 + 1 - . + + .section .orc_unwind + .2byte 8 + .2byte 0 + .2byte 0x205 + .section .orc_unwind_ip + .long .L0 + 2 - . + ## Fake Linux Kernel sections. .section __ksymtab,"a",@progbits .section __ksymtab_gpl,"a",@progbits diff --git a/bolt/test/X86/name-similarity-function-matching.test b/bolt/test/X86/name-similarity-function-matching.test new file mode 100644 index 0000000000000..97ef4bf0cff61 --- /dev/null +++ b/bolt/test/X86/name-similarity-function-matching.test @@ -0,0 +1,63 @@ +## Tests function matching in YAMLProfileReader by name similarity. + +# 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 -v=2 \ +# RUN: --print-cfg --name-similarity-function-matching-threshold=1 --funcs=main --profile-ignore-hash=0 2>&1 | FileCheck %s + +# CHECK: BOLT-INFO: matched 1 functions with similar names + +#--- 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 + .reloc 0, R_X86_64_NONE + .cfi_endproc + .size main, .-main + +#--- yaml +--- +header: + profile-version: 1 + binary-name: 'hashing-based-function-matching.s.tmp.exe' + binary-build-id: '' + profile-flags: [ lbr ] + profile-origin: branch profile reader + profile-events: '' + dfs-order: false + hash-func: xxh3 +functions: + - name: main2 + fid: 0 + hash: 0x0000000000000001 + exec: 1 + nblocks: 5 + blocks: + - bid: 1 + insns: 1 + succ: [ { bid: 3, cnt: 1} ] +... diff --git a/bolt/test/X86/reader-stale-yaml-std.test b/bolt/test/X86/reader-stale-yaml-std.test index b43442ca9ea95..9abe042d0e968 100644 --- a/bolt/test/X86/reader-stale-yaml-std.test +++ b/bolt/test/X86/reader-stale-yaml-std.test @@ -1,3 +1,4 @@ +# UNSUPPORTED: true ## This script checks that YamlProfileReader in llvm-bolt is reading data ## correctly and stale data is corrected by profile inference. diff --git a/bolt/test/X86/sctc-bug4.test b/bolt/test/X86/sctc-bug4.test index 21a602b6729ae..e192a238a4d0c 100644 --- a/bolt/test/X86/sctc-bug4.test +++ b/bolt/test/X86/sctc-bug4.test @@ -6,24 +6,19 @@ RUN: llvm-bolt %t -o %t.null --enable-bat \ RUN: -funcs=test_func -print-sctc -sequential-disassembly 2>&1 | FileCheck %s CHECK: .Ltmp2 (3 instructions, align : 1) -CHECK-NEXT: CFI State : 0 CHECK-NEXT: Input offset: 0x24 CHECK-NEXT: Predecessors: .LFT1 CHECK-NEXT: 00000024: cmpq $0x20, %rsi CHECK-NEXT: 00000028: ja dummy # TAILCALL # Offset: 53 # CTCTakenCount: 0 CHECK-NEXT: 0000002a: jmp .Ltmp4 CHECK-NEXT: Successors: .Ltmp4 -CHECK-NEXT: CFI State: 0 CHECK: .Ltmp1 (2 instructions, align : 1) -CHECK-NEXT: CFI State : 0 CHECK-NEXT: Input offset: 0x2c CHECK-NEXT: Predecessors: .LFT0 CHECK-NEXT: 0000002c: xorq %r11, %rax CHECK-NEXT: 0000002f: retq -CHECK-NEXT: CFI State: 0 CHECK: .Ltmp4 (4 instructions, align : 1) -CHECK-NEXT: CFI State : 0 CHECK-NEXT: Input offset: 0x3a CHECK-NEXT: Predecessors: .Ltmp2 diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp index c0faf5f7e8fd9..b5da17564c2e0 100644 --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -55,7 +55,7 @@ class HTMLTag { operator TagType() const { return Value; } operator bool() = delete; - bool IsSelfClosing() const; + bool isSelfClosing() const; llvm::SmallString<16> ToString() const; private: @@ -71,7 +71,7 @@ struct HTMLNode { HTMLNode(NodeType Type) : Type(Type) {} virtual ~HTMLNode() = default; - virtual void Render(llvm::raw_ostream &OS, int IndentationLevel) = 0; + virtual void render(llvm::raw_ostream &OS, int IndentationLevel) = 0; NodeType Type; // Type of node }; @@ -80,7 +80,7 @@ struct TextNode : public HTMLNode { : HTMLNode(NodeType::NODE_TEXT), Text(Text.str()) {} std::string Text; // Content of node - void Render(llvm::raw_ostream &OS, int IndentationLevel) override; + void render(llvm::raw_ostream &OS, int IndentationLevel) override; }; struct TagNode : public HTMLNode { @@ -94,17 +94,17 @@ struct TagNode : public HTMLNode { std::vector> Attributes; // List of key-value attributes for tag - void Render(llvm::raw_ostream &OS, int IndentationLevel) override; + void render(llvm::raw_ostream &OS, int IndentationLevel) override; }; constexpr const char *kDoctypeDecl = ""; struct HTMLFile { std::vector> Children; // List of child nodes - void Render(llvm::raw_ostream &OS) { + void render(llvm::raw_ostream &OS) { OS << kDoctypeDecl << "\n"; for (const auto &C : Children) { - C->Render(OS, 0); + C->render(OS, 0); OS << "\n"; } } @@ -112,7 +112,7 @@ struct HTMLFile { } // namespace -bool HTMLTag::IsSelfClosing() const { +bool HTMLTag::isSelfClosing() const { switch (Value) { case HTMLTag::TAG_META: case HTMLTag::TAG_LINK: @@ -177,12 +177,12 @@ llvm::SmallString<16> HTMLTag::ToString() const { llvm_unreachable("Unhandled HTMLTag::TagType"); } -void TextNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { +void TextNode::render(llvm::raw_ostream &OS, int IndentationLevel) { OS.indent(IndentationLevel * 2); printHTMLEscaped(Text, OS); } -void TagNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { +void TagNode::render(llvm::raw_ostream &OS, int IndentationLevel) { // Children nodes are rendered in the same line if all of them are text nodes bool InlineChildren = true; for (const auto &C : Children) @@ -194,7 +194,7 @@ void TagNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { OS << "<" << Tag.ToString(); for (const auto &A : Attributes) OS << " " << A.first << "=\"" << A.second << "\""; - if (Tag.IsSelfClosing()) { + if (Tag.isSelfClosing()) { OS << "/>"; return; } @@ -205,7 +205,7 @@ void TagNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { for (const auto &C : Children) { int ChildrenIndentation = InlineChildren || !NewLineRendered ? 0 : IndentationLevel + 1; - C->Render(OS, ChildrenIndentation); + C->render(OS, ChildrenIndentation); if (!InlineChildren && (C == Children.back() || (C->Type != NodeType::NODE_TEXT || (&C + 1)->get()->Type != NodeType::NODE_TEXT))) { @@ -221,7 +221,7 @@ void TagNode::Render(llvm::raw_ostream &OS, int IndentationLevel) { template ::value>> -static void AppendVector(std::vector &&New, +static void appendVector(std::vector &&New, std::vector &Original) { std::move(New.begin(), New.end(), std::back_inserter(Original)); } @@ -289,9 +289,18 @@ genStylesheetsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) { static std::vector> genJsScriptsHTML(StringRef InfoPath, const ClangDocContext &CDCtx) { std::vector> Out; + + // index_json.js is part of every generated HTML file + SmallString<128> IndexJSONPath = computeRelativePath("", InfoPath); + auto IndexJSONNode = std::make_unique(HTMLTag::TAG_SCRIPT); + llvm::sys::path::append(IndexJSONPath, "index_json.js"); + llvm::sys::path::native(IndexJSONPath, llvm::sys::path::Style::posix); + IndexJSONNode->Attributes.emplace_back("src", std::string(IndexJSONPath)); + Out.emplace_back(std::move(IndexJSONNode)); + for (const auto &FilePath : CDCtx.JsScripts) { - auto ScriptNode = std::make_unique(HTMLTag::TAG_SCRIPT); SmallString<128> ScriptPath = computeRelativePath("", InfoPath); + auto ScriptNode = std::make_unique(HTMLTag::TAG_SCRIPT); llvm::sys::path::append(ScriptPath, llvm::sys::path::filename(FilePath)); // Paths in HTML must be in posix-style llvm::sys::path::native(ScriptPath, llvm::sys::path::Style::posix); @@ -313,8 +322,7 @@ genReference(const Reference &Type, StringRef CurrentDirectory, if (Type.Path.empty()) { if (!JumpToSection) return std::make_unique(Type.Name); - else - return genLink(Type.Name, "#" + *JumpToSection); + return genLink(Type.Name, "#" + *JumpToSection); } llvm::SmallString<64> Path = Type.getRelativeFilePath(CurrentDirectory); llvm::sys::path::append(Path, Type.getFileBaseName() + ".html"); @@ -357,7 +365,7 @@ genEnumsBlock(const std::vector &Enums, auto &DivBody = Out.back(); for (const auto &E : Enums) { std::vector> Nodes = genHTML(E, CDCtx); - AppendVector(std::move(Nodes), DivBody->Children); + appendVector(std::move(Nodes), DivBody->Children); } return Out; } @@ -388,7 +396,7 @@ genFunctionsBlock(const std::vector &Functions, for (const auto &F : Functions) { std::vector> Nodes = genHTML(F, CDCtx, ParentInfoDir); - AppendVector(std::move(Nodes), DivBody->Children); + appendVector(std::move(Nodes), DivBody->Children); } return Out; } @@ -478,10 +486,10 @@ genFileHeadNodes(StringRef Title, StringRef InfoPath, Out.emplace_back(std::make_unique(HTMLTag::TAG_TITLE, Title)); std::vector> StylesheetsNodes = genStylesheetsHTML(InfoPath, CDCtx); - AppendVector(std::move(StylesheetsNodes), Out); + appendVector(std::move(StylesheetsNodes), Out); std::vector> JsNodes = genJsScriptsHTML(InfoPath, CDCtx); - AppendVector(std::move(JsNodes), Out); + appendVector(std::move(JsNodes), Out); return Out; } @@ -513,7 +521,7 @@ static std::unique_ptr genInfoFileMainNode( MainContentNode->Attributes.emplace_back("id", "main-content"); MainContentNode->Attributes.emplace_back( "class", "col-xs-12 col-sm-9 col-md-8 main-content"); - AppendVector(std::move(MainContentInnerNodes), MainContentNode->Children); + appendVector(std::move(MainContentInnerNodes), MainContentNode->Children); auto RightSidebarNode = std::make_unique(HTMLTag::TAG_DIV); RightSidebarNode->Attributes.emplace_back("id", "sidebar-right"); @@ -521,7 +529,7 @@ static std::unique_ptr genInfoFileMainNode( "class", "col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"); std::vector> InfoIndexHTML = genHTML(InfoIndex, InfoPath, true); - AppendVector(std::move(InfoIndexHTML), RightSidebarNode->Children); + appendVector(std::move(InfoIndexHTML), RightSidebarNode->Children); MainNode->Children.emplace_back(std::move(LeftSidebarNode)); MainNode->Children.emplace_back(std::move(MainContentNode)); @@ -555,7 +563,7 @@ genInfoFile(StringRef Title, StringRef InfoPath, genInfoFileMainNode(InfoPath, MainContentNodes, InfoIndex); std::unique_ptr FooterNode = genFileFooterNode(); - AppendVector(std::move(HeadNodes), F.Children); + appendVector(std::move(HeadNodes), F.Children); F.Children.emplace_back(std::move(HeaderNode)); F.Children.emplace_back(std::move(MainNode)); F.Children.emplace_back(std::move(FooterNode)); @@ -594,7 +602,7 @@ genHTML(const Index &Index, StringRef InfoPath, bool IsOutermostList) { for (const auto &C : Index.Children) { auto LiBody = std::make_unique(HTMLTag::TAG_LI); std::vector> Nodes = genHTML(C, InfoPath, false); - AppendVector(std::move(Nodes), LiBody->Children); + appendVector(std::move(Nodes), LiBody->Children); UlBody->Children.emplace_back(std::move(LiBody)); } return Out; @@ -609,7 +617,9 @@ static std::unique_ptr genHTML(const CommentInfo &I) { FullComment->Children.emplace_back(std::move(Node)); } return std::move(FullComment); - } else if (I.Kind == "ParagraphComment") { + } + + if (I.Kind == "ParagraphComment") { auto ParagraphComment = std::make_unique(HTMLTag::TAG_P); for (const auto &Child : I.Children) { std::unique_ptr Node = genHTML(*Child); @@ -619,7 +629,9 @@ static std::unique_ptr genHTML(const CommentInfo &I) { if (ParagraphComment->Children.empty()) return nullptr; return std::move(ParagraphComment); - } else if (I.Kind == "TextComment") { + } + + if (I.Kind == "TextComment") { if (I.Text == "") return nullptr; return std::make_unique(I.Text); @@ -639,11 +651,7 @@ static std::unique_ptr genHTML(const std::vector &C) { static std::vector> genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) { std::vector> Out; - std::string EnumType; - if (I.Scoped) - EnumType = "enum class "; - else - EnumType = "enum "; + std::string EnumType = I.Scoped ? "enum class " : "enum "; Out.emplace_back( std::make_unique(HTMLTag::TAG_H3, EnumType + I.Name)); @@ -737,17 +745,17 @@ genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx, std::vector> ChildNamespaces = genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath); - AppendVector(std::move(ChildNamespaces), Out); + appendVector(std::move(ChildNamespaces), Out); std::vector> ChildRecords = genReferencesBlock(I.Children.Records, "Records", BasePath); - AppendVector(std::move(ChildRecords), Out); + appendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = genFunctionsBlock(I.Children.Functions, CDCtx, BasePath); - AppendVector(std::move(ChildFunctions), Out); + appendVector(std::move(ChildFunctions), Out); std::vector> ChildEnums = genEnumsBlock(I.Children.Enums, CDCtx); - AppendVector(std::move(ChildEnums), Out); + appendVector(std::move(ChildEnums), Out); if (!I.Children.Namespaces.empty()) InfoIndex.Children.emplace_back("Namespaces", "Namespaces"); @@ -791,29 +799,29 @@ genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx, auto &PBody = Out.back(); PBody->Children.emplace_back(std::make_unique("Inherits from ")); if (Parents.empty()) - AppendVector(std::move(VParents), PBody->Children); + appendVector(std::move(VParents), PBody->Children); else if (VParents.empty()) - AppendVector(std::move(Parents), PBody->Children); + appendVector(std::move(Parents), PBody->Children); else { - AppendVector(std::move(Parents), PBody->Children); + appendVector(std::move(Parents), PBody->Children); PBody->Children.emplace_back(std::make_unique(", ")); - AppendVector(std::move(VParents), PBody->Children); + appendVector(std::move(VParents), PBody->Children); } } std::vector> Members = genRecordMembersBlock(I.Members, I.Path); - AppendVector(std::move(Members), Out); + appendVector(std::move(Members), Out); std::vector> ChildRecords = genReferencesBlock(I.Children.Records, "Records", I.Path); - AppendVector(std::move(ChildRecords), Out); + appendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = genFunctionsBlock(I.Children.Functions, CDCtx, I.Path); - AppendVector(std::move(ChildFunctions), Out); + appendVector(std::move(ChildFunctions), Out); std::vector> ChildEnums = genEnumsBlock(I.Children.Enums, CDCtx); - AppendVector(std::move(ChildEnums), Out); + appendVector(std::move(ChildEnums), Out); if (!I.Members.empty()) InfoIndex.Children.emplace_back("Members", "Members"); @@ -936,7 +944,7 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS, HTMLFile F = genInfoFile(InfoTitle, I->getRelativeFilePath(""), MainContentNodes, InfoIndex, CDCtx); - F.Render(OS); + F.render(OS); return llvm::Error::success(); } @@ -959,7 +967,7 @@ static std::string getRefType(InfoType IT) { llvm_unreachable("Unknown InfoType"); } -static llvm::Error SerializeIndex(ClangDocContext &CDCtx) { +static llvm::Error serializeIndex(ClangDocContext &CDCtx) { std::error_code OK; std::error_code FileErr; llvm::SmallString<128> FilePath; @@ -985,9 +993,9 @@ static llvm::Error SerializeIndex(ClangDocContext &CDCtx) { }); }); }; - OS << "var JsonIndex = `\n"; + OS << "async function LoadIndex() {\nreturn"; IndexToJSON(CDCtx.Idx); - OS << "`;\n"; + OS << ";\n}"; return llvm::Error::success(); } @@ -1009,7 +1017,7 @@ static std::unique_ptr genIndexFileMainNode() { return MainNode; } -static llvm::Error GenIndex(const ClangDocContext &CDCtx) { +static llvm::Error genIndex(const ClangDocContext &CDCtx) { std::error_code FileErr, OK; llvm::SmallString<128> IndexPath; llvm::sys::path::native(CDCtx.OutDirectory, IndexPath); @@ -1029,17 +1037,17 @@ static llvm::Error GenIndex(const ClangDocContext &CDCtx) { std::unique_ptr MainNode = genIndexFileMainNode(); std::unique_ptr FooterNode = genFileFooterNode(); - AppendVector(std::move(HeadNodes), F.Children); + appendVector(std::move(HeadNodes), F.Children); F.Children.emplace_back(std::move(HeaderNode)); F.Children.emplace_back(std::move(MainNode)); F.Children.emplace_back(std::move(FooterNode)); - F.Render(IndexOS); + F.render(IndexOS); return llvm::Error::success(); } -static llvm::Error CopyFile(StringRef FilePath, StringRef OutDirectory) { +static llvm::Error copyFile(StringRef FilePath, StringRef OutDirectory) { llvm::SmallString<128> PathWrite; llvm::sys::path::native(OutDirectory, PathWrite); llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath)); @@ -1057,20 +1065,20 @@ static llvm::Error CopyFile(StringRef FilePath, StringRef OutDirectory) { } llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) { - auto Err = SerializeIndex(CDCtx); + auto Err = serializeIndex(CDCtx); if (Err) return Err; - Err = GenIndex(CDCtx); + Err = genIndex(CDCtx); if (Err) return Err; for (const auto &FilePath : CDCtx.UserStylesheets) { - Err = CopyFile(FilePath, CDCtx.OutDirectory); + Err = copyFile(FilePath, CDCtx.OutDirectory); if (Err) return Err; } - for (const auto &FilePath : CDCtx.FilesToCopy) { - Err = CopyFile(FilePath, CDCtx.OutDirectory); + for (const auto &FilePath : CDCtx.JsScripts) { + Err = copyFile(FilePath, CDCtx.OutDirectory); if (Err) return Err; } diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp index 2afff2929cf79..d08afbb962189 100644 --- a/clang-tools-extra/clang-doc/Representation.cpp +++ b/clang-tools-extra/clang-doc/Representation.cpp @@ -368,11 +368,9 @@ ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx, StringRef ProjectName, bool PublicOnly, StringRef OutDirectory, StringRef SourceRoot, StringRef RepositoryUrl, - std::vector UserStylesheets, - std::vector JsScripts) + std::vector UserStylesheets) : ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly), - OutDirectory(OutDirectory), UserStylesheets(UserStylesheets), - JsScripts(JsScripts) { + OutDirectory(OutDirectory), UserStylesheets(UserStylesheets) { llvm::SmallString<128> SourceRootDir(SourceRoot); if (SourceRoot.empty()) // If no SourceRoot was provided the current path is used as the default diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index a6b144eb7fa2a..d70c279f7a2bd 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -482,8 +482,7 @@ struct ClangDocContext { ClangDocContext(tooling::ExecutionContext *ECtx, StringRef ProjectName, bool PublicOnly, StringRef OutDirectory, StringRef SourceRoot, StringRef RepositoryUrl, - std::vector UserStylesheets, - std::vector JsScripts); + std::vector UserStylesheets); tooling::ExecutionContext *ECtx; std::string ProjectName; // Name of project clang-doc is documenting. bool PublicOnly; // Indicates if only public declarations are documented. @@ -498,8 +497,6 @@ struct ClangDocContext { std::vector UserStylesheets; // JavaScript files that will be imported in allHTML file. std::vector JsScripts; - // Other files that should be copied to OutDirectory, besides UserStylesheets. - std::vector FilesToCopy; Index Idx; }; diff --git a/clang-tools-extra/clang-doc/assets/index.js b/clang-tools-extra/clang-doc/assets/index.js index a5ac90f2e06e7..49818763a4393 100644 --- a/clang-tools-extra/clang-doc/assets/index.js +++ b/clang-tools-extra/clang-doc/assets/index.js @@ -80,8 +80,8 @@ function createIndex(Index) { // Runs after DOM loads document.addEventListener("DOMContentLoaded", function() { - // JsonIndex is a variable from another file that contains the index - // in JSON format - var Index = JSON.parse(JsonIndex); - createIndex(Index); + // LoadIndex is an asynchronous function that will be generated clang-doc. + // It ensures that the function call will not block as soon the page loads, + // since the index object are often huge and can contain thousands of lines. + LoadIndex().then((Index) => { createIndex(Index); }); }); diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt index 4944251245c6b..e93a5728d6b6b 100644 --- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt +++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt @@ -25,7 +25,7 @@ set(assets ) set(asset_dir "${CMAKE_CURRENT_SOURCE_DIR}/../assets") -set(resource_dir "${CMAKE_BINARY_DIR}/share/clang") +set(resource_dir "${CMAKE_BINARY_DIR}/share/clang-doc") set(out_files) function(copy_files_to_dst src_dir dst_dir file) @@ -42,7 +42,7 @@ endfunction(copy_files_to_dst) foreach(f ${assets}) install(FILES ${asset_dir}/${f} - DESTINATION "${CMAKE_INSTALL_DATADIR}/clang" + DESTINATION "${CMAKE_INSTALL_DATADIR}/clang-doc" COMPONENT clang-doc) copy_files_to_dst(${asset_dir} ${resource_dir} ${f}) endforeach(f) diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp index 5a43c70a5ebc3..6198a6e0cdcc3 100644 --- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp +++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp @@ -150,7 +150,7 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) { CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(), std::string(FilePath)); else if (llvm::sys::path::extension(FilePath) == ".js") - CDCtx.FilesToCopy.emplace_back(FilePath.str()); + CDCtx.JsScripts.emplace_back(FilePath.str()); } } if (FileErr) @@ -167,7 +167,7 @@ llvm::Error getDefaultAssetFiles(const char *Argv0, llvm::SmallString<128> AssetsPath; AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath); - llvm::sys::path::append(AssetsPath, "..", "share", "clang"); + llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc"); llvm::SmallString<128> DefaultStylesheet; llvm::sys::path::native(AssetsPath, DefaultStylesheet); llvm::sys::path::append(DefaultStylesheet, @@ -176,8 +176,6 @@ llvm::Error getDefaultAssetFiles(const char *Argv0, llvm::sys::path::native(AssetsPath, IndexJS); llvm::sys::path::append(IndexJS, "index.js"); - llvm::outs() << "Using default asset: " << AssetsPath << "\n"; - if (!llvm::sys::fs::is_regular_file(IndexJS)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "default index.js file missing at " + @@ -191,7 +189,7 @@ llvm::Error getDefaultAssetFiles(const char *Argv0, CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(), std::string(DefaultStylesheet)); - CDCtx.FilesToCopy.emplace_back(IndexJS.str()); + CDCtx.JsScripts.emplace_back(IndexJS.str()); return llvm::Error::success(); } @@ -254,12 +252,13 @@ Example usage for a project using a compile commands database: OutDirectory, SourceRoot, RepositoryUrl, - {UserStylesheets.begin(), UserStylesheets.end()}, - {"index.js", "index_json.js"}}; + {UserStylesheets.begin(), UserStylesheets.end()} + }; if (Format == "html") { if (auto Err = getHtmlAssetFiles(argv[0], CDCtx)) { - llvm::outs() << "warning: " << toString(std::move(Err)) << "\n"; + llvm::errs() << toString(std::move(Err)) << "\n"; + return 1; } } diff --git a/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp index fafb029e7de1b..04ca006143503 100644 --- a/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp +++ b/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp @@ -9,6 +9,7 @@ #include "RedundantStrcatCallsCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include using namespace clang::ast_matchers; diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 1b92d2e60cc17..689eb92a3d8d1 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -51,6 +51,7 @@ #include "NotNullTerminatedResultCheck.h" #include "OptionalValueConversionCheck.h" #include "ParentVirtualCallCheck.h" +#include "PointerArithmeticOnPolymorphicObjectCheck.h" #include "PosixReturnCheck.h" #include "RedundantBranchConditionCheck.h" #include "ReservedIdentifierCheck.h" @@ -171,6 +172,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-multiple-statement-macro"); CheckFactories.registerCheck( "bugprone-optional-value-conversion"); + CheckFactories.registerCheck( + "bugprone-pointer-arithmetic-on-polymorphic-object"); CheckFactories.registerCheck( "bugprone-redundant-branch-condition"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 2d303191f8865..cb0d8ae98bac5 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -48,6 +48,7 @@ add_clang_library(clangTidyBugproneModule NotNullTerminatedResultCheck.cpp OptionalValueConversionCheck.cpp ParentVirtualCallCheck.cpp + PointerArithmeticOnPolymorphicObjectCheck.cpp PosixReturnCheck.cpp RedundantBranchConditionCheck.cpp ReservedIdentifierCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/MacroRepeatedSideEffectsCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MacroRepeatedSideEffectsCheck.cpp index a64c9b2905ca1..01276af6c7d8f 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MacroRepeatedSideEffectsCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/MacroRepeatedSideEffectsCheck.cpp @@ -12,6 +12,7 @@ #include "clang/Lex/MacroArgs.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" +#include namespace clang::tidy::bugprone { diff --git a/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp new file mode 100644 index 0000000000000..6e6ad10fabbb3 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp @@ -0,0 +1,81 @@ +//===--- PointerArithmeticOnPolymorphicObjectCheck.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 "PointerArithmeticOnPolymorphicObjectCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +namespace { +AST_MATCHER(CXXRecordDecl, isAbstract) { return Node.isAbstract(); } +AST_MATCHER(CXXRecordDecl, isPolymorphic) { return Node.isPolymorphic(); } +} // namespace + +PointerArithmeticOnPolymorphicObjectCheck:: + PointerArithmeticOnPolymorphicObjectCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IgnoreInheritedVirtualFunctions( + Options.get("IgnoreInheritedVirtualFunctions", false)) {} + +void PointerArithmeticOnPolymorphicObjectCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoreInheritedVirtualFunctions", + IgnoreInheritedVirtualFunctions); +} + +void PointerArithmeticOnPolymorphicObjectCheck::registerMatchers( + MatchFinder *Finder) { + const auto PolymorphicPointerExpr = + expr(hasType(hasCanonicalType(pointerType(pointee(hasCanonicalType( + hasDeclaration(cxxRecordDecl(unless(isFinal()), isPolymorphic()) + .bind("pointee")))))))) + .bind("pointer"); + + const auto PointerExprWithVirtualMethod = + expr(hasType(hasCanonicalType( + pointerType(pointee(hasCanonicalType(hasDeclaration( + cxxRecordDecl( + unless(isFinal()), + anyOf(hasMethod(isVirtualAsWritten()), isAbstract())) + .bind("pointee")))))))) + .bind("pointer"); + + const auto SelectedPointerExpr = IgnoreInheritedVirtualFunctions + ? PointerExprWithVirtualMethod + : PolymorphicPointerExpr; + + const auto ArraySubscript = arraySubscriptExpr(hasBase(SelectedPointerExpr)); + + const auto BinaryOperators = + binaryOperator(hasAnyOperatorName("+", "-", "+=", "-="), + hasEitherOperand(SelectedPointerExpr)); + + const auto UnaryOperators = unaryOperator( + hasAnyOperatorName("++", "--"), hasUnaryOperand(SelectedPointerExpr)); + + Finder->addMatcher(ArraySubscript, this); + Finder->addMatcher(BinaryOperators, this); + Finder->addMatcher(UnaryOperators, this); +} + +void PointerArithmeticOnPolymorphicObjectCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *PointerExpr = Result.Nodes.getNodeAs("pointer"); + const auto *PointeeDecl = Result.Nodes.getNodeAs("pointee"); + + diag(PointerExpr->getBeginLoc(), + "pointer arithmetic on polymorphic object of type %0 can result in " + "undefined behavior if the dynamic type differs from the pointer type") + << PointeeDecl << PointerExpr->getSourceRange(); +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.h b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.h new file mode 100644 index 0000000000000..84f2d8e74ba87 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.h @@ -0,0 +1,41 @@ +//===--- PointerArithmeticOnPolymorphicObjectCheck.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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POINTERARITHMETICONPOLYMORPHICOBJECTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POINTERARITHMETICONPOLYMORPHICOBJECTCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// Finds pointer arithmetic performed on classes that contain a +/// virtual function. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.html +class PointerArithmeticOnPolymorphicObjectCheck : public ClangTidyCheck { +public: + PointerArithmeticOnPolymorphicObjectCheck(StringRef Name, + ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + const bool IgnoreInheritedVirtualFunctions; +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POINTERARITHMETICONPOLYMORPHICOBJECTCHECK_H diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp index c25ee42d0899a..d517e8473d94a 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp @@ -296,7 +296,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-integer-call")) { diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression " - "that results in an integer") + "of integer type") << E->getSourceRange(); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-this")) { diag(E->getBeginLoc(), @@ -314,7 +314,7 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { << E->getSourceRange(); } else { diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression " - "that results in a pointer") + "of pointer type") << E->getSourceRange(); } } else if (const auto *E = Result.Nodes.getNodeAs( @@ -348,25 +348,28 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { } else if (ElementSize > CharUnits::Zero() && DenominatorSize > CharUnits::Zero() && ElementSize != DenominatorSize) { - diag(E->getOperatorLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" - " numerator is not a multiple of denominator") + // FIXME: Apparently there are no testcases that cover this branch! + diag(E->getOperatorLoc(), + "suspicious usage of 'sizeof(array)/sizeof(...)';" + " denominator differs from the size of array elements") << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (NumTy && DenomTy && NumTy == DenomTy) { - // FIXME: This message is wrong, it should not refer to sizeof "pointer" - // usage (and by the way, it would be to clarify all the messages). diag(E->getOperatorLoc(), - "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'") + "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions " + "have the same type") << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (!WarnOnSizeOfPointer) { // When 'WarnOnSizeOfPointer' is enabled, these messages become redundant: if (PointedTy && DenomTy && PointedTy == DenomTy) { diag(E->getOperatorLoc(), - "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'") + "suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer " + "is divided by size of pointed type") << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (NumTy && DenomTy && NumTy->isPointerType() && DenomTy->isPointerType()) { diag(E->getOperatorLoc(), - "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'") + "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions " + "have pointer types") << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } } diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp index 00370ee9b3004..ffb62b409b29b 100644 --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "../bugprone/BadSignalToKillThreadCheck.h" +#include "../bugprone/PointerArithmeticOnPolymorphicObjectCheck.h" #include "../bugprone/ReservedIdentifierCheck.h" #include "../bugprone/SignalHandlerCheck.h" #include "../bugprone/SignedCharMisuseCheck.h" @@ -238,6 +239,10 @@ class CERTModule : public ClangTidyModule { // CON CheckFactories.registerCheck( "cert-con54-cpp"); + // CTR + CheckFactories + .registerCheck( + "cert-ctr56-cpp"); // DCL CheckFactories.registerCheck("cert-dcl50-cpp"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp index ee17b0e014288..a97ec9fe3fe3d 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "AvoidNonConstGlobalVariablesCheck.h" -#include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" @@ -15,13 +14,23 @@ using namespace clang::ast_matchers; namespace clang::tidy::cppcoreguidelines { +AvoidNonConstGlobalVariablesCheck::AvoidNonConstGlobalVariablesCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AllowInternalLinkage(Options.get("AllowInternalLinkage", false)) {} + void AvoidNonConstGlobalVariablesCheck::registerMatchers(MatchFinder *Finder) { + auto NamespaceMatcher = AllowInternalLinkage + ? namespaceDecl(unless(isAnonymous())) + : namespaceDecl(); auto GlobalContext = varDecl(hasGlobalStorage(), - hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()))); + hasDeclContext(anyOf(NamespaceMatcher, translationUnitDecl()))); auto GlobalVariable = varDecl( GlobalContext, + AllowInternalLinkage ? varDecl(unless(isStaticStorageClass())) + : varDecl(), unless(anyOf( isConstexpr(), hasType(isConstQualified()), hasType(referenceType())))); // References can't be changed, only the @@ -43,7 +52,6 @@ void AvoidNonConstGlobalVariablesCheck::registerMatchers(MatchFinder *Finder) { void AvoidNonConstGlobalVariablesCheck::check( const MatchFinder::MatchResult &Result) { - if (const auto *Variable = Result.Nodes.getNodeAs("non-const_variable")) { diag(Variable->getLocation(), "variable %0 is non-const and globally " @@ -63,4 +71,9 @@ void AvoidNonConstGlobalVariablesCheck::check( } } +void AvoidNonConstGlobalVariablesCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowInternalLinkage", AllowInternalLinkage); +} + } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.h index e816ca9b47d41..a912763489db9 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidNonConstGlobalVariablesCheck.h @@ -20,10 +20,13 @@ namespace clang::tidy::cppcoreguidelines { /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables.html class AvoidNonConstGlobalVariablesCheck : public ClangTidyCheck { public: - AvoidNonConstGlobalVariablesCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + AvoidNonConstGlobalVariablesCheck(StringRef Name, ClangTidyContext *Context); void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + const bool AllowInternalLinkage; }; } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp b/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp index 70d0281df28fa..44ccc2bc906a5 100644 --- a/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.cpp @@ -18,6 +18,26 @@ using namespace clang::ast_matchers; +namespace clang::tidy { + +template <> +struct OptionEnumMapping { + static llvm::ArrayRef< + std::pair> + getEnumMapping() { + static constexpr std::pair + Mapping[] = { + {misc::UseInternalLinkageCheck::FixModeKind::None, "None"}, + {misc::UseInternalLinkageCheck::FixModeKind::UseStatic, + "UseStatic"}, + }; + return {Mapping}; + } +}; + +} // namespace clang::tidy + namespace clang::tidy::misc { namespace { @@ -57,6 +77,16 @@ AST_POLYMORPHIC_MATCHER(isExternStorageClass, } // namespace +UseInternalLinkageCheck::UseInternalLinkageCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + HeaderFileExtensions(Context->getHeaderFileExtensions()), + FixMode(Options.get("FixMode", FixModeKind::UseStatic)) {} + +void UseInternalLinkageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "FixMode", FixMode); +} + void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) { auto Common = allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions), @@ -82,11 +112,21 @@ static constexpr StringRef Message = void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *FD = Result.Nodes.getNodeAs("fn")) { - diag(FD->getLocation(), Message) << "function" << FD; + DiagnosticBuilder DB = diag(FD->getLocation(), Message) << "function" << FD; + SourceLocation FixLoc = FD->getTypeSpecStartLoc(); + if (FixLoc.isInvalid() || FixLoc.isMacroID()) + return; + if (FixMode == FixModeKind::UseStatic) + DB << FixItHint::CreateInsertion(FixLoc, "static "); return; } if (const auto *VD = Result.Nodes.getNodeAs("var")) { - diag(VD->getLocation(), Message) << "variable" << VD; + DiagnosticBuilder DB = diag(VD->getLocation(), Message) << "variable" << VD; + SourceLocation FixLoc = VD->getTypeSpecStartLoc(); + if (FixLoc.isInvalid() || FixLoc.isMacroID()) + return; + if (FixMode == FixModeKind::UseStatic) + DB << FixItHint::CreateInsertion(FixLoc, "static "); return; } llvm_unreachable(""); diff --git a/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.h b/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.h index a3c1c33965903..0d6c3e43aa945 100644 --- a/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.h +++ b/clang-tools-extra/clang-tidy/misc/UseInternalLinkageCheck.h @@ -20,17 +20,22 @@ namespace clang::tidy::misc { /// http://clang.llvm.org/extra/clang-tidy/checks/misc/use-internal-linkage.html class UseInternalLinkageCheck : public ClangTidyCheck { public: - UseInternalLinkageCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - HeaderFileExtensions(Context->getHeaderFileExtensions()) {} + UseInternalLinkageCheck(StringRef Name, ClangTidyContext *Context); void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; std::optional getCheckTraversalKind() const override { return TK_IgnoreUnlessSpelledInSource; } + enum class FixModeKind { + None, + UseStatic, + }; + private: FileExtensionsSet HeaderFileExtensions; + FixModeKind FixMode; }; } // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index fd4730d9c8b9c..499c88ef5d4e4 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -9,6 +9,7 @@ #include "SimplifyBooleanExprCheck.h" #include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/DiagnosticIDs.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/SaveAndRestore.h" @@ -702,7 +703,8 @@ bool SimplifyBooleanExprCheck::canBeBypassed(const Stmt *S) const { return IgnoreMacros && S->getBeginLoc().isMacroID(); } -void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, +/// @brief return true when replacement created. +bool SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, SourceLocation Loc, StringRef Description, SourceRange ReplacementRange, @@ -712,8 +714,10 @@ void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, Context.getSourceManager(), getLangOpts()); DiagnosticBuilder Diag = diag(Loc, Description); - if (!containsDiscardedTokens(Context, CharRange)) + const bool HasReplacement = !containsDiscardedTokens(Context, CharRange); + if (HasReplacement) Diag << FixItHint::CreateReplacement(CharRange, Replacement); + return HasReplacement; } void SimplifyBooleanExprCheck::replaceWithThenStatement( @@ -751,8 +755,18 @@ void SimplifyBooleanExprCheck::replaceWithReturnCondition( replacementExpression(Context, Negated, If->getCond()); std::string Replacement = ("return " + Condition + Terminator).str(); SourceLocation Start = BoolLiteral->getBeginLoc(); - issueDiag(Context, Start, SimplifyConditionalReturnDiagnostic, - If->getSourceRange(), Replacement); + + const bool HasReplacement = + issueDiag(Context, Start, SimplifyConditionalReturnDiagnostic, + If->getSourceRange(), Replacement); + + if (!HasReplacement) { + const SourceRange ConditionRange = If->getCond()->getSourceRange(); + if (ConditionRange.isValid()) + diag(ConditionRange.getBegin(), "conditions that can be simplified", + DiagnosticIDs::Note) + << ConditionRange; + } } void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( @@ -760,9 +774,23 @@ void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( const IfStmt *If, const Expr *ThenReturn) { const std::string Replacement = "return " + replacementExpression(Context, Negated, If->getCond()); - issueDiag(Context, ThenReturn->getBeginLoc(), - SimplifyConditionalReturnDiagnostic, - SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); + + const bool HasReplacement = issueDiag( + Context, ThenReturn->getBeginLoc(), SimplifyConditionalReturnDiagnostic, + SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); + + if (!HasReplacement) { + const SourceRange ConditionRange = If->getCond()->getSourceRange(); + if (ConditionRange.isValid()) + diag(ConditionRange.getBegin(), "conditions that can be simplified", + DiagnosticIDs::Note) + << ConditionRange; + const SourceRange ReturnRange = Ret->getSourceRange(); + if (ReturnRange.isValid()) + diag(ReturnRange.getBegin(), "return statement that can be simplified", + DiagnosticIDs::Note) + << ReturnRange; + } } void SimplifyBooleanExprCheck::replaceWithAssignment(const ASTContext &Context, diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h index 63c3caa01e01a..2ea6968798408 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h @@ -60,7 +60,7 @@ class SimplifyBooleanExprCheck : public ClangTidyCheck { const BinaryOperator *Inner, bool TryOfferFix, const Stmt *Parent, const ParenExpr *Parens); - void issueDiag(const ASTContext &Context, SourceLocation Loc, + bool issueDiag(const ASTContext &Context, SourceLocation Loc, StringRef Description, SourceRange ReplacementRange, StringRef Replacement); diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 7388f20ef288e..d42dafa8ffc36 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -325,6 +325,14 @@ option is recognized. )"), cl::init(false), cl::cat(ClangTidyCategory)); +static cl::opt AllowNoChecks("allow-no-checks", desc(R"( +Allow empty enabled checks. This suppresses +the "no checks enabled" error when disabling +all of the checks. +)"), + cl::init(false), + cl::cat(ClangTidyCategory)); + namespace clang::tidy { static void printStats(const ClangTidyStats &Stats) { @@ -598,7 +606,7 @@ int clangTidyMain(int argc, const char **argv) { } if (ListChecks) { - if (EnabledChecks.empty()) { + if (EnabledChecks.empty() && !AllowNoChecks) { llvm::errs() << "No checks enabled.\n"; return 1; } @@ -652,6 +660,10 @@ int clangTidyMain(int argc, const char **argv) { } if (EnabledChecks.empty()) { + if (AllowNoChecks) { + llvm::outs() << "No checks enabled.\n"; + return 0; + } llvm::errs() << "Error: no checks enabled.\n"; llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true); return 1; diff --git a/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py index b048460abf2fc..62cb4297c50f7 100755 --- a/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py +++ b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py @@ -229,6 +229,11 @@ def main(): default=[], help="Load the specified plugin in clang-tidy.", ) + parser.add_argument( + "-allow-no-checks", + action="store_true", + help="Allow empty enabled checks.", + ) clang_tidy_args = [] argv = sys.argv[1:] @@ -327,6 +332,8 @@ def main(): common_clang_tidy_args.append("-p=%s" % args.build_path) if args.use_color: common_clang_tidy_args.append("--use-color") + if args.allow_no_checks: + common_clang_tidy_args.append("--allow-no-checks") for arg in args.extra_arg: common_clang_tidy_args.append("-extra-arg=%s" % arg) for arg in args.extra_arg_before: 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 4dd20bec81d3b..0dc35ad587362 100755 --- a/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py +++ b/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py @@ -107,6 +107,7 @@ def get_tidy_invocation( plugins, warnings_as_errors, exclude_header_filter, + allow_no_checks, ): """Gets a command line for clang-tidy.""" start = [clang_tidy_binary] @@ -147,6 +148,8 @@ def get_tidy_invocation( start.append("-load=" + plugin) if warnings_as_errors: start.append("--warnings-as-errors=" + warnings_as_errors) + if allow_no_checks: + start.append("--allow-no-checks") start.append(f) return start @@ -232,6 +235,7 @@ def run_tidy(args, clang_tidy_binary, tmpdir, build_path, queue, lock, failed_fi args.plugins, args.warnings_as_errors, args.exclude_header_filter, + args.allow_no_checks, ) proc = subprocess.Popen( @@ -261,20 +265,20 @@ def main(): parser.add_argument( "-allow-enabling-alpha-checkers", action="store_true", - help="allow alpha checkers from clang-analyzer.", + help="Allow alpha checkers from clang-analyzer.", ) parser.add_argument( - "-clang-tidy-binary", metavar="PATH", help="path to clang-tidy binary" + "-clang-tidy-binary", metavar="PATH", help="Path to clang-tidy binary." ) parser.add_argument( "-clang-apply-replacements-binary", metavar="PATH", - help="path to clang-apply-replacements binary", + help="Path to clang-apply-replacements binary.", ) parser.add_argument( "-checks", default=None, - help="checks filter, when not specified, use clang-tidy default", + help="Checks filter, when not specified, use clang-tidy default.", ) config_group = parser.add_mutually_exclusive_group() config_group.add_argument( @@ -307,7 +311,7 @@ def main(): parser.add_argument( "-header-filter", default=None, - help="regular expression matching the names of the " + help="Regular expression matching the names of the " "headers to output diagnostics from. Diagnostics from " "the main file of each translation unit are always " "displayed.", @@ -347,19 +351,22 @@ def main(): "-j", type=int, default=0, - help="number of tidy instances to be run in parallel.", + help="Number of tidy instances to be run in parallel.", ) parser.add_argument( - "files", nargs="*", default=[".*"], help="files to be processed (regex on path)" + "files", + nargs="*", + default=[".*"], + help="Files to be processed (regex on path).", ) - parser.add_argument("-fix", action="store_true", help="apply fix-its") + parser.add_argument("-fix", action="store_true", help="apply fix-its.") parser.add_argument( - "-format", action="store_true", help="Reformat code after applying fixes" + "-format", action="store_true", help="Reformat code after applying fixes." ) parser.add_argument( "-style", default="file", - help="The style of reformat code after applying fixes", + help="The style of reformat code after applying fixes.", ) parser.add_argument( "-use-color", @@ -388,7 +395,7 @@ def main(): help="Additional argument to prepend to the compiler command line.", ) parser.add_argument( - "-quiet", action="store_true", help="Run clang-tidy in quiet mode" + "-quiet", action="store_true", help="Run clang-tidy in quiet mode." ) parser.add_argument( "-load", @@ -400,7 +407,12 @@ def main(): parser.add_argument( "-warnings-as-errors", default=None, - help="Upgrades warnings to errors. Same format as '-checks'", + help="Upgrades warnings to errors. Same format as '-checks'.", + ) + parser.add_argument( + "-allow-no-checks", + action="store_true", + help="Allow empty enabled checks.", ) args = parser.parse_args() @@ -463,6 +475,7 @@ def main(): args.plugins, args.warnings_as_errors, args.exclude_header_filter, + args.allow_no_checks, ) invocation.append("-list-checks") invocation.append("-") diff --git a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp index a6062ccf42230..106feb7fb4172 100644 --- a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp +++ b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp @@ -296,7 +296,7 @@ AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) { return false; } const auto *const Method = - dyn_cast(OpCall->getDirectCallee()); + dyn_cast_or_null(OpCall->getDirectCallee()); if (Method == nullptr) { // This is not a member operator. Typically, a friend operator. These diff --git a/clang-tools-extra/clangd/Format.cpp b/clang-tools-extra/clangd/Format.cpp index 272a34d4ed797..fc56a1c8c5030 100644 --- a/clang-tools-extra/clangd/Format.cpp +++ b/clang-tools-extra/clangd/Format.cpp @@ -281,7 +281,7 @@ formatIncremental(llvm::StringRef OriginalCode, unsigned OriginalCursor, // Never *remove* lines in response to pressing enter! This annoys users. if (InsertedText == "\n") { Style.MaxEmptyLinesToKeep = 1000; - Style.KeepEmptyLinesAtTheStartOfBlocks = true; + Style.KeepEmptyLines.AtStartOfBlock = true; } // Compute the code we want to format: diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp index 5bec7966a9c3a..85c80eb482efb 100644 --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/clang-tools-extra/clangd/Selection.h b/clang-tools-extra/clangd/Selection.h index f80ee83c4b94c..cb40c6d403487 100644 --- a/clang-tools-extra/clangd/Selection.h +++ b/clang-tools-extra/clangd/Selection.h @@ -45,6 +45,7 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/SmallVector.h" +#include namespace clang { namespace clangd { diff --git a/clang-tools-extra/clangd/index/remote/Client.h b/clang-tools-extra/clangd/index/remote/Client.h index ff5348bd23279..9004012f066ae 100644 --- a/clang-tools-extra/clangd/index/remote/Client.h +++ b/clang-tools-extra/clangd/index/remote/Client.h @@ -12,8 +12,11 @@ #include "index/Index.h" #include "llvm/ADT/StringRef.h" +#include + namespace clang { namespace clangd { + namespace remote { /// Returns an SymbolIndex client that passes requests to remote index located diff --git a/clang-tools-extra/clangd/index/remote/server/Server.cpp b/clang-tools-extra/clangd/index/remote/server/Server.cpp index 4ef3ab6f9af9c..52fca53260a16 100644 --- a/clang-tools-extra/clangd/index/remote/server/Server.cpp +++ b/clang-tools-extra/clangd/index/remote/server/Server.cpp @@ -499,8 +499,6 @@ int main(int argc, char *argv[]) { } llvm::errs().SetBuffered(); - // Don't flush stdout when logging for thread safety. - llvm::errs().tie(nullptr); auto Logger = makeLogger(LogPrefix.getValue(), llvm::errs()); clang::clangd::LoggingSession LoggingSession(*Logger); diff --git a/clang-tools-extra/clangd/support/Trace.cpp b/clang-tools-extra/clangd/support/Trace.cpp index 419c2eee99ec8..7c6490f2e92fb 100644 --- a/clang-tools-extra/clangd/support/Trace.cpp +++ b/clang-tools-extra/clangd/support/Trace.cpp @@ -159,8 +159,8 @@ class JSONTracer : public EventTracer { Out.object([&] { Out.attribute("pid", 0); Out.attribute("ph", Phase); - for (const auto &KV : Event) - Out.attribute(KV.first, KV.second); + for (const auto *KV : llvm::json::sortedElements(Event)) + Out.attribute(KV->first, KV->second); }); } diff --git a/clang-tools-extra/clangd/test/trace.test b/clang-tools-extra/clangd/test/trace.test index cb6a226b496a4..56f2d6333f1de 100644 --- a/clang-tools-extra/clangd/test/trace.test +++ b/clang-tools-extra/clangd/test/trace.test @@ -8,17 +8,17 @@ # CHECK: "traceEvents": [ # CHECK: { # CHECK: "ph": "X", -# CHECK: "name": "BuildPreamble", # CHECK: "args": { # CHECK: "File": "{{.*(/|\\)}}foo.c" # CHECK: }, +# CHECK: "name": "BuildPreamble", # CHECK: } # CHECK: { # CHECK: "ph": "X", -# CHECK: "name": "BuildAST", # CHECK: "args": { # CHECK: "File": "{{.*(/|\\)}}foo.c" # CHECK: }, +# CHECK: "name": "BuildAST", # CHECK: } # CHECK: ] # CHECK: } diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index c3ba655ee2dc6..73000d96c6ca8 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -840,8 +840,6 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var // Use buffered stream to stderr (we still flush each log message). Unbuffered // stream can cause significant (non-deterministic) latency for the logger. llvm::errs().SetBuffered(); - // Don't flush stdout when logging, this would be both slow and racy! - llvm::errs().tie(nullptr); StreamLogger Logger(llvm::errs(), LogLevel); LoggingSession LoggingSession(Logger); // Write some initial logs before we start doing any real work. diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp index 3101bf34acd71..32c8e8a63a215 100644 --- a/clang-tools-extra/clangd/unittests/ASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -80,7 +80,7 @@ TEST(GetDeducedType, KwAutoKwDecltypeExpansion) { namespace std { template - class [[initializer_list]] {}; + class [[initializer_list]] { const _E *a, *b; }; } ^auto i = {1,2}; diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index d9e97e5215a26..8d6d4223d7260 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -2284,7 +2284,7 @@ TEST(Hover, All) { namespace std { template - class initializer_list {}; + class initializer_list { const _E *a, *b; }; } void foo() { ^[[auto]] i = {1,2}; diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp index 5b1531eb2fa60..a5a349e93037a 100644 --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -945,7 +945,7 @@ TEST(ParameterHints, ConstructorStdInitList) { // Do not show hints for std::initializer_list constructors. assertParameterHints(R"cpp( namespace std { - template class initializer_list {}; + template class initializer_list { const E *a, *b; }; } struct S { S(std::initializer_list param); diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index cbceb9a343f87..d393c72974d44 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -771,7 +771,7 @@ TEST(LocateSymbol, All) { namespace std { template - class [[initializer_list]] {}; + class [[initializer_list]] { const _E *a, *b; }; } ^auto i = {1,2}; diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 16fdc20eafd62..e570c8184f8b0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -125,6 +125,9 @@ Improvements to clang-tidy - Added argument `--exclude-header-filter` and config option `ExcludeHeaderFilterRegex` to exclude headers from analysis via a RegEx. +- Added argument `--allow-no-checks` to suppress "no checks enabled" error + when disabling all of the checks by `--checks='-*'`. + New checks ^^^^^^^^^^ @@ -134,6 +137,11 @@ New checks Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP can be constructed outside itself and the derived class. +- New :doc:`bugprone-pointer-arithmetic-on-polymorphic-object + ` check. + + Finds pointer arithmetic performed on classes that contain a virtual function. + - New :doc:`bugprone-return-const-ref-from-parameter ` check. @@ -196,6 +204,11 @@ New checks New check aliases ^^^^^^^^^^^^^^^^^ +- New alias :doc:`cert-ctr56-cpp ` to + :doc:`bugprone-pointer-arithmetic-on-polymorphic-object + ` + was added. + - New alias :doc:`cert-int09-c ` to :doc:`readability-enum-initial-value ` was added. @@ -238,10 +251,10 @@ Changes in existing checks false positives resulting from use of optionals in unevaluated context. - Improved :doc:`bugprone-sizeof-expression - ` check by eliminating some - false positives and adding a new (off-by-default) option - `WarnOnSizeOfPointer` that reports all ``sizeof(pointer)`` expressions - (except for a few that are idiomatic). + ` check by clarifying the + diagnostics, eliminating some false positives and adding a new + (off-by-default) option `WarnOnSizeOfPointer` that reports all + ``sizeof(pointer)`` expressions (except for a few that are idiomatic). - Improved :doc:`bugprone-suspicious-include ` check by replacing the local @@ -266,6 +279,11 @@ Changes in existing checks ` check to also handle calls to ``std::forward``. +- Improved :doc:`cppcoreguidelines-avoid-non-const-global-variables + ` check + with a new option `AllowInternalLinkage` to disable the warning for variables + with internal linkage. + - Improved :doc:`cppcoreguidelines-macro-usage ` check by ignoring macro with hash preprocessing token. @@ -398,7 +416,7 @@ Changes in existing checks analyzed, so the check now handles the common patterns `const auto e = (*vector_ptr)[i]` and `const auto e = vector_ptr->at(i);`. Calls to mutable function where there exists a `const` overload are also - handled. + handled. Fix crash in the case of a non-member operator call. - Improved :doc:`readability-avoid-return-with-void-value ` check by adding @@ -452,7 +470,8 @@ Changes in existing checks - Improved :doc:`readability-simplify-boolean-expr ` check to avoid to emit - warning for macro when IgnoreMacro option is enabled. + warning for macro when IgnoreMacro option is enabled and improve messages + when auto-fix does not work. - Improved :doc:`readability-static-definition-in-anonymous-namespace ` diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.rst new file mode 100644 index 0000000000000..1884acd5f12b3 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.rst @@ -0,0 +1,68 @@ +.. title:: clang-tidy - bugprone-pointer-arithmetic-on-polymorphic-object + +bugprone-pointer-arithmetic-on-polymorphic-object +================================================= + +Finds pointer arithmetic performed on classes that contain a virtual function. + +Pointer arithmetic on polymorphic objects where the pointer's static type is +different from its dynamic type is undefined behavior, as the two types could +have different sizes, and thus the vtable pointer could point to an +invalid address. + +Finding pointers where the static type contains a virtual member function is a +good heuristic, as the pointer is likely to point to a different, +derived object. + +Example: + +.. code-block:: c++ + + struct Base { + virtual void ~Base(); + }; + + struct Derived : public Base {}; + + void foo() { + Base *b = new Derived[10]; + + b += 1; + // warning: pointer arithmetic on class that declares a virtual function can + // result in undefined behavior if the dynamic type differs from the + // pointer type + + delete[] static_cast(b); + } + +Options +------- + +.. option:: IgnoreInheritedVirtualFunctions + + When `true`, objects that only inherit a virtual function are not checked. + Classes that do not declare a new virtual function are excluded + by default, as they make up the majority of false positives. + Default: `false`. + + .. code-block:: c++ + + void bar() { + Base *b = new Base[10]; + b += 1; // warning, as Base declares a virtual destructor + + delete[] b; + + Derived *d = new Derived[10]; // Derived overrides the destructor, and + // declares no other virtual functions + d += 1; // warning only if IgnoreVirtualDeclarationsOnly is set to false + + delete[] d; + } + +References +---------- + +This check corresponds to the SEI Cert rule +`CTR56-CPP. Do not use pointer arithmetic on polymorphic objects +`_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/ctr56-cpp.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/ctr56-cpp.rst new file mode 100644 index 0000000000000..e42acbe552359 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert/ctr56-cpp.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-ctr56-cpp +.. meta:: + :http-equiv=refresh: 5;URL=../bugprone/pointer-arithmetic-on-polymorphic-object.html + +cert-ctr56-cpp +============== + +The `cert-ctr56-cpp` check is an alias, please see +:doc:`bugprone-pointer-arithmetic-on-polymorphic-object +<../bugprone/pointer-arithmetic-on-polymorphic-object>` for more information. \ No newline at end of file diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables.rst index 21c20af6e8107..8da284ca13e3d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables.rst @@ -41,3 +41,11 @@ The variables ``a``, ``c``, ``c_ptr1``, ``c_const_ptr`` and ``c_reference`` will all generate warnings since they are either a non-const globally accessible variable, a pointer or a reference providing global access to non-const data or both. + +Options +------- + +.. option:: AllowInternalLinkage + + When set to `true`, static non-const variables and variables in anonymous + namespaces will not generate a warning. The default value is `false`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-member-init.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-member-init.rst index ae55bf7bd7c86..97af01a895e1c 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-member-init.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-member-init.rst @@ -3,7 +3,7 @@ cppcoreguidelines-pro-type-member-init ====================================== -The check flags user-defined constructor definitions that do not +The check flags user-provided constructor definitions that do not initialize all fields that would be left in an undefined state by default construction, e.g. builtins, pointers and record types without user-provided default constructors containing at least one such diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index a698cecc0825c..9671f3895f5d3 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -157,6 +157,7 @@ Clang-Tidy Checks :doc:`bugprone-unused-raii `, "Yes" :doc:`bugprone-unused-return-value `, :doc:`bugprone-use-after-move `, + :doc:`bugprone-pointer-arithmetic-on-polymorphic-object `, :doc:`bugprone-virtual-near-miss `, "Yes" :doc:`cert-dcl50-cpp `, :doc:`cert-dcl58-cpp `, diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/use-internal-linkage.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/use-internal-linkage.rst index e8e43a1fb3d63..7147af9a7919b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/use-internal-linkage.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/use-internal-linkage.rst @@ -25,3 +25,16 @@ Example: } // already declared as extern extern int v2; + +Options +------- + +.. option:: FixMode + + Selects what kind of a fix the check should provide. The default is `UseStatic`. + + ``None`` + Don't fix automatically. + + ``UseStatic`` + Add ``static`` for internal linkage variable and function. diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst index 9ccacefa3c2c5..c8fc34c61caeb 100644 --- a/clang-tools-extra/docs/clang-tidy/index.rst +++ b/clang-tools-extra/docs/clang-tidy/index.rst @@ -240,6 +240,9 @@ An overview of all the command-line options: This option's value is appended to the value of the 'WarningsAsErrors' option in .clang-tidy file, if any. + --allow-no-checks - Allow empty enabled checks. This suppresses + the "no checks enabled" error when disabling + all of the checks. -p is used to read a compile command database. diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp index dac3f39e1a658..282abce3246ca 100644 --- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -534,7 +534,7 @@ TEST(WalkAST, Enums) { TEST(WalkAST, InitializerList) { testWalk(R"cpp( namespace std { - template struct $implicit^initializer_list {}; + template struct $implicit^initializer_list { const T *a, *b; }; })cpp", R"cpp( const char* s = ""; diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Calculator.cpp b/clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Calculator.cpp index df1778c3b9d55..64f31dbf13d87 100644 --- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Calculator.cpp +++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/src/Calculator.cpp @@ -1,5 +1,4 @@ #include "Calculator.h" -#include int Calculator::add(int a, int b) { return a + b; @@ -14,8 +13,5 @@ int Calculator::multiply(int a, int b) { } double Calculator::divide(int a, int b) { - if (b == 0) { - throw std::invalid_argument("Division by zero"); - } return static_cast(a) / b; } diff --git a/clang-tools-extra/test/clang-doc/Inputs/test-assets/test.css b/clang-tools-extra/test/clang-doc/Inputs/test-assets/test.css new file mode 100644 index 0000000000000..5c5532659be5e --- /dev/null +++ b/clang-tools-extra/test/clang-doc/Inputs/test-assets/test.css @@ -0,0 +1,3 @@ +body { + padding: 0; +} \ No newline at end of file diff --git a/clang-tools-extra/test/clang-doc/Inputs/test-assets/test.js b/clang-tools-extra/test/clang-doc/Inputs/test-assets/test.js new file mode 100644 index 0000000000000..06f320c59b9b0 --- /dev/null +++ b/clang-tools-extra/test/clang-doc/Inputs/test-assets/test.js @@ -0,0 +1 @@ +console.log("Hello, world!"); \ No newline at end of file diff --git a/clang-tools-extra/test/clang-doc/assets.cpp b/clang-tools-extra/test/clang-doc/assets.cpp new file mode 100644 index 0000000000000..d5a2d20e92240 --- /dev/null +++ b/clang-tools-extra/test/clang-doc/assets.cpp @@ -0,0 +1,22 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: clang-doc --format=html --output=%t --asset=%S/Inputs/test-assets --executor=standalone %s +// RUN: FileCheck %s -input-file=%t/index.html -check-prefix=INDEX +// RUN: FileCheck %s -input-file=%t/test.css -check-prefix=CSS +// RUN: FileCheck %s -input-file=%t/test.js -check-prefix=JS + +// INDEX: +// INDEX-NEXT: +// INDEX-NEXT: Index +// INDEX-NEXT: +// INDEX-NEXT: +// INDEX-NEXT: +// INDEX-NEXT:
+// INDEX-NEXT:
+// INDEX-NEXT: +// INDEX-NEXT:
+ +// CSS: body { +// CSS-NEXT: padding: 0; +// CSS-NEXT: } + +// JS: console.log("Hello, world!"); \ No newline at end of file diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test index 0898acaea3a33..bab5f8e1761bc 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.test +++ b/clang-tools-extra/test/clang-doc/basic-project.test @@ -7,8 +7,8 @@ // RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Rectangle.html -check-prefix=HTML-RECTANGLE // RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Circle.html -check-prefix=HTML-CIRCLE -// JSON-INDEX: var JsonIndex = ` -// JSON-INDEX-NEXT: { +// JSON-INDEX: async function LoadIndex() { +// JSON-INDEX-NEXT: return{ // JSON-INDEX-NEXT: "USR": "{{([0-9A-F]{40})}}", // JSON-INDEX-NEXT: "Name": "", // JSON-INDEX-NEXT: "RefType": "default", @@ -51,14 +51,15 @@ // JSON-INDEX-NEXT: ] // JSON-INDEX-NEXT: } // JSON-INDEX-NEXT: ] -// JSON-INDEX-NEXT: }`; +// JSON-INDEX-NEXT: }; +// JSON-INDEX-NEXT: } // HTML-SHAPE: // HTML-SHAPE-NEXT: // HTML-SHAPE-NEXT: class Shape // HTML-SHAPE-NEXT: -// HTML-SHAPE-NEXT: -// HTML-SHAPE-NEXT: +// HTML-SHAPE-NEXT: +// HTML-SHAPE-NEXT: // HTML-SHAPE-NEXT:
// HTML-SHAPE-NEXT:
// HTML-SHAPE-NEXT: @@ -122,8 +123,8 @@ // HTML-CALC-NEXT: // HTML-CALC-NEXT: class Calculator // HTML-CALC-NEXT: -// HTML-CALC-NEXT: // HTML-CALC-NEXT: +// HTML-CALC-NEXT: // HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT: @@ -139,25 +140,25 @@ // HTML-CALC-NEXT:
// HTML-CALC-NEXT:

add

// HTML-CALC-NEXT:

public int add(int a, int b)

-// HTML-CALC-NEXT:

Defined at line 4 of file {{.*}}Calculator.cpp

+// HTML-CALC-NEXT:

Defined at line 3 of file {{.*}}Calculator.cpp

// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT:

subtract

// HTML-CALC-NEXT:

public int subtract(int a, int b)

-// HTML-CALC-NEXT:

Defined at line 8 of file {{.*}}Calculator.cpp

+// HTML-CALC-NEXT:

Defined at line 7 of file {{.*}}Calculator.cpp

// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT:

multiply

// HTML-CALC-NEXT:

public int multiply(int a, int b)

-// HTML-CALC-NEXT:

Defined at line 12 of file {{.*}}Calculator.cpp

+// HTML-CALC-NEXT:

Defined at line 11 of file {{.*}}Calculator.cpp

// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT:

divide

// HTML-CALC-NEXT:

public double divide(int a, int b)

-// HTML-CALC-NEXT:

Defined at line 16 of file {{.*}}Calculator.cpp

+// HTML-CALC-NEXT:

Defined at line 15 of file {{.*}}Calculator.cpp

// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
// HTML-CALC-NEXT:
@@ -200,8 +201,8 @@ // HTML-RECTANGLE-NEXT: // HTML-RECTANGLE-NEXT: class Rectangle // HTML-RECTANGLE-NEXT: -// HTML-RECTANGLE-NEXT: // HTML-RECTANGLE-NEXT: +// HTML-RECTANGLE-NEXT: // HTML-RECTANGLE-NEXT:
// HTML-RECTANGLE-NEXT:
// HTML-RECTANGLE-NEXT: @@ -281,8 +282,8 @@ // HTML-CIRCLE-NEXT: // HTML-CIRCLE-NEXT: class Circle // HTML-CIRCLE-NEXT: -// HTML-CIRCLE-NEXT: // HTML-CIRCLE-NEXT: +// HTML-CIRCLE-NEXT: // HTML-CIRCLE-NEXT:
// HTML-CIRCLE-NEXT:
// HTML-CIRCLE-NEXT: diff --git a/clang-tools-extra/test/clang-doc/single-source-html.cpp b/clang-tools-extra/test/clang-doc/single-source-html.cpp deleted file mode 100644 index 32f232b9c45a0..0000000000000 --- a/clang-tools-extra/test/clang-doc/single-source-html.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: clang-doc --format=html --executor=standalone %s -output=%t/docs | FileCheck %s -// CHECK: Using default asset: {{.*}}{{[\/]}}share{{[\/]}}clang \ No newline at end of file diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp new file mode 100644 index 0000000000000..caf24f79ad2d8 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp @@ -0,0 +1,140 @@ +// RUN: %check_clang_tidy %s bugprone-pointer-arithmetic-on-polymorphic-object %t -- + +class Base { +public: + virtual ~Base() {} +}; + +class Derived : public Base {}; + +class FinalDerived final : public Base {}; + +class AbstractBase { +public: + virtual void f() = 0; + virtual ~AbstractBase() {} +}; + +class AbstractInherited : public AbstractBase {}; + +class AbstractOverride : public AbstractInherited { +public: + void f() override {} +}; + +void operators() { + Base *b = new Derived[10]; + + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + b = b + 1; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + b++; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + --b; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + b[1]; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + delete[] static_cast(b); +} + +void subclassWarnings() { + Base *b = new Base[10]; + + // False positive that's impossible to distinguish without + // path-sensitive analysis, but the code is bug-prone regardless. + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + delete[] b; + + // Common false positive is a class that overrides all parent functions. + // Is a warning because of the check configuration. + Derived *d = new Derived[10]; + + d += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Derived' + + delete[] d; + + // Final classes cannot have a dynamic type. + FinalDerived *fd = new FinalDerived[10]; + + fd += 1; + // no-warning + + delete[] fd; +} + +void abstractWarnings() { + // Classes with an abstract member funtion are always matched. + AbstractBase *ab = new AbstractOverride[10]; + + ab += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractBase' + + delete[] static_cast(ab); + + AbstractInherited *ai = new AbstractOverride[10]; + + ai += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractInherited' + + delete[] static_cast(ai); + + // Is a warning because of the check configuration. + AbstractOverride *ao = new AbstractOverride[10]; + + ao += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractOverride' + + delete[] ao; +} + +template +void templateWarning(T *t) { + // FIXME: Tidy doesn't support template instantiation locations properly. + t += 1; + // no-warning +} + +void functionArgument(Base *b) { + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + templateWarning(b); +} + +using BaseAlias = Base; +using DerivedAlias = Derived; +using FinalDerivedAlias = FinalDerived; + +using BasePtr = Base*; +using DerivedPtr = Derived*; +using FinalDerivedPtr = FinalDerived*; + +void typeAliases(BaseAlias *b, DerivedAlias *d, FinalDerivedAlias *fd, + BasePtr bp, DerivedPtr dp, FinalDerivedPtr fdp) { + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + d += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Derived' + + fd += 1; + // no-warning + + bp += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + dp += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Derived' + + fdp += 1; + // no-warning +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp new file mode 100644 index 0000000000000..48757bbc9b10e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp @@ -0,0 +1,141 @@ +// RUN: %check_clang_tidy %s bugprone-pointer-arithmetic-on-polymorphic-object %t -- \ +// RUN: -config="{CheckOptions: \ +// RUN: {bugprone-pointer-arithmetic-on-polymorphic-object.IgnoreInheritedVirtualFunctions: true}}" + +class Base { +public: + virtual ~Base() {} +}; + +class Derived : public Base {}; + +class FinalDerived final : public Base {}; + +class AbstractBase { +public: + virtual void f() = 0; + virtual ~AbstractBase() {} +}; + +class AbstractInherited : public AbstractBase {}; + +class AbstractOverride : public AbstractInherited { +public: + void f() override {} +}; + +void operators() { + Base *b = new Derived[10]; + + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + b = b + 1; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + b++; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + --b; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + b[1]; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type differs from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object] + + delete[] static_cast(b); +} + +void subclassWarnings() { + Base *b = new Base[10]; + + // False positive that's impossible to distinguish without + // path-sensitive analysis, but the code is bug-prone regardless. + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + delete[] b; + + // Common false positive is a class that overrides all parent functions. + Derived *d = new Derived[10]; + + d += 1; + // no-warning + + delete[] d; + + // Final classes cannot have a dynamic type. + FinalDerived *fd = new FinalDerived[10]; + + fd += 1; + // no-warning + + delete[] fd; +} + +void abstractWarnings() { + // Classes with an abstract member funtion are always matched. + AbstractBase *ab = new AbstractOverride[10]; + + ab += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractBase' + + delete[] static_cast(ab); + + AbstractInherited *ai = new AbstractOverride[10]; + + ai += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractInherited' + + delete[] static_cast(ai); + + // If all abstract member functions are overridden, the class is not matched. + AbstractOverride *ao = new AbstractOverride[10]; + + ao += 1; + // no-warning + + delete[] ao; +} + +template +void templateWarning(T *t) { + // FIXME: Tidy doesn't support template instantiation locations properly. + t += 1; + // no-warning +} + +void functionArgument(Base *b) { + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + templateWarning(b); +} + +using BaseAlias = Base; +using DerivedAlias = Derived; +using FinalDerivedAlias = FinalDerived; + +using BasePtr = Base*; +using DerivedPtr = Derived*; +using FinalDerivedPtr = FinalDerived*; + +void typeAliases(BaseAlias *b, DerivedAlias *d, FinalDerivedAlias *fd, + BasePtr bp, DerivedPtr dp, FinalDerivedPtr fdp) { + b += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + d += 1; + // no-warning + + fd += 1; + // no-warning + + bp += 1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' + + dp += 1; + // no-warning + + fdp += 1; + // no-warning +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-2.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-2.c index aef930f2c8fda..b898071a56613 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-2.c +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-2.c @@ -34,24 +34,24 @@ int Test5() { int sum = 0; sum += sizeof(&S); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(__typeof(&S)); sum += sizeof(&TS); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(__typeof(&TS)); sum += sizeof(STRKWD MyStruct*); sum += sizeof(__typeof(STRKWD MyStruct*)); sum += sizeof(TypedefStruct*); sum += sizeof(__typeof(TypedefStruct*)); sum += sizeof(PTTS); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PMyStruct); sum += sizeof(PS); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PS2); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&A10); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type #ifdef __cplusplus MyStruct &rS = S; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-any-pointer.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-any-pointer.cpp index bfb2ec3a9eb02..4c65d253baefd 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-any-pointer.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-any-pointer.cpp @@ -38,17 +38,17 @@ int Test1(const char* ptr) { sum += sizeof(sum, LEN); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(..., ...)' sum += sizeof(AsBool()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(AsInt()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(AsEnum()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(AsEnumClass()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(M{}.AsInt()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(M{}.AsEnum()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(sizeof(X)); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' sum += sizeof(LEN + sizeof(X)); @@ -62,7 +62,7 @@ int Test1(const char* ptr) { sum += sizeof(LEN + - + -sizeof(X)); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' sum += sizeof(char) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type sum += sizeof(A) / sizeof(S); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(char) / sizeof(int); @@ -72,21 +72,21 @@ int Test1(const char* ptr) { sum += sizeof(B[0]) / sizeof(A); // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(ptr) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(ptr) / sizeof(ptr[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(ptr) / sizeof(char*); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(ptr) / sizeof(void*); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(ptr) / sizeof(const void volatile*); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(ptr) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(int) * sizeof(char); // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication sum += sizeof(ptr) * sizeof(ptr[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication sum += sizeof(int) * (2 * sizeof(char)); // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication @@ -127,54 +127,54 @@ int Test5() { int sum = 0; sum += sizeof(&S.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&kGlocalMyStruct.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&kGlocalMyStructPtr->arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(S.arr + 0); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(+ S.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof((int*)S.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(S.ptr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(kGlocalMyStruct.ptr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(kGlocalMyStructPtr->ptr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&kGlocalMyStruct); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&S); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(MyStruct*); sum += sizeof(PMyStruct); sum += sizeof(PS); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PS2); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&A10); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PtrArray) / sizeof(PtrArray[1]); - // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(A10) / sizeof(PtrArray[0]); sum += sizeof(PC) / sizeof(PtrArray[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer - // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type + // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type sum += sizeof(ArrayC) / sizeof(PtrArray[0]); // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(PChar); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PInt); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PPInt); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PPMyStruct); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type return sum; } @@ -200,9 +200,9 @@ void GenericFunctionTest() { // reported by the `sizeof(pointer)` checks, but this causes some false // positives, so it would be good to create an exception for them. some_generic_function(&IntPP, sizeof(IntP)); - // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: suspicious usage of 'sizeof()' on an expression of pointer type some_generic_function(&ClassPP, sizeof(ClassP)); - // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression of pointer type } int ValidExpressions() { @@ -222,7 +222,7 @@ int ValidExpressions() { sum += sizeof(A[sizeof(A) / sizeof(int)]); // Here the outer sizeof is reported, but the inner ones are accepted: sum += sizeof(&A[sizeof(A) / sizeof(int)]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(sizeof(0)); // Special case: sizeof size_t. sum += sizeof(void*); sum += sizeof(void const *); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp index 5ef2c46ffbdf1..5c6b728efcee9 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp @@ -68,7 +68,7 @@ int Test5() { sum += sizeof(A10) / sizeof(PtrArray[0]); // No warning. sum += sizeof(PC) / sizeof(PtrArray[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type sum += sizeof(ArrayC) / sizeof(PtrArray[0]); // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp index 064f31cb08c6b..671fd8370894f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp @@ -48,13 +48,13 @@ bool TestTrait2() { template bool TestTrait3() { return sizeof(ReturnOverload(0)) == sizeof(T{}); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type } template bool TestTrait4() { return sizeof(ReturnTemplate(0)) == sizeof(T{}); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type } bool TestTemplates() { @@ -79,17 +79,17 @@ int Test1(const char* ptr) { sum += sizeof(sum, LEN); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(..., ...)' sum += sizeof(AsBool()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(AsInt()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(AsEnum()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(AsEnumClass()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(M{}.AsInt()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(M{}.AsEnum()); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of integer type sum += sizeof(sizeof(X)); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' sum += sizeof(LEN + sizeof(X)); @@ -103,7 +103,7 @@ int Test1(const char* ptr) { sum += sizeof(LEN + - + -sizeof(X)); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' sum += sizeof(char) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type sum += sizeof(A) / sizeof(S); // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(char) / sizeof(int); @@ -113,17 +113,17 @@ int Test1(const char* ptr) { sum += sizeof(B[0]) / sizeof(A); // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(ptr) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer is divided by size of pointed type sum += sizeof(ptr) / sizeof(ptr[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer is divided by size of pointed type sum += sizeof(ptr) / sizeof(char*); - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have pointer types sum += sizeof(ptr) / sizeof(void*); - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have pointer types sum += sizeof(ptr) / sizeof(const void volatile*); - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have pointer types sum += sizeof(ptr) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer is divided by size of pointed type sum += sizeof(int) * sizeof(char); // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication sum += sizeof(ptr) * sizeof(ptr[0]); @@ -156,11 +156,11 @@ int CE4 = sizeof sizeof(MyConstChar); int Test2(MyConstChar* A) { int sum = 0; sum += sizeof(MyConstChar) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type sum += sizeof(MyConstChar) / sizeof(MyChar); - // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type sum += sizeof(A[0]) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type return sum; } @@ -169,7 +169,7 @@ int Foo() { int A[T]; return sizeof(T); } // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(K)' template int Bar() { T A[5]; return sizeof(A[0]) / sizeof(T); } -// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' +// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type int Test3() { return Foo<42>() + Bar(); } static const char* kABC = "abc"; @@ -211,43 +211,43 @@ int Test5() { int sum = 0; sum += sizeof(&S.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&kGlocalMyStruct.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&kGlocalMyStructPtr->arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(S.arr + 0); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(+ S.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof((int*)S.arr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(S.ptr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(kGlocalMyStruct.ptr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(kGlocalMyStructPtr->ptr); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&kGlocalMyStruct); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&S); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(MyStruct*); sum += sizeof(PMyStruct); sum += sizeof(PS); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PS2); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(&A10); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(PtrArray) / sizeof(PtrArray[1]); - // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious usage of 'sizeof()' on an expression of pointer type sum += sizeof(A10) / sizeof(PtrArray[0]); sum += sizeof(PC) / sizeof(PtrArray[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer - // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression of pointer type + // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions have the same type sum += sizeof(ArrayC) / sizeof(PtrArray[0]); // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator @@ -321,7 +321,7 @@ void GenericFunctionTest() { // NOTE: `sizeof(IntP)` is only reported with `WarnOnSizeOfPointer=true`. some_generic_function(&IntPP, sizeof(IntP)); some_generic_function(&ClassPP, sizeof(ClassP)); - // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression that results in a pointer + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: suspicious usage of 'sizeof()' on an expression of pointer type } int ValidExpressions() { diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-non-const-global-variables.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-non-const-global-variables.cpp index 3ca1029433d22..334332def216f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-non-const-global-variables.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-non-const-global-variables.cpp @@ -1,4 +1,6 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-avoid-non-const-global-variables %t +// RUN: %check_clang_tidy %s -check-suffixes=,DEFAULT cppcoreguidelines-avoid-non-const-global-variables %t +// RUN: %check_clang_tidy %s -check-suffixes=,INTERNAL-LINKAGE cppcoreguidelines-avoid-non-const-global-variables %t -- \ +// RUN: -config="{CheckOptions: {cppcoreguidelines-avoid-non-const-global-variables.AllowInternalLinkage : 'true'}}" int nonConstInt = 0; // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'nonConstInt' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] @@ -38,9 +40,16 @@ int function() { namespace { int nonConstAnonymousNamespaceInt = 0; -// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'nonConstAnonymousNamespaceInt' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:5: warning: variable 'nonConstAnonymousNamespaceInt' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-INTERNAL-LINKAGE-NOT: :[[@LINE-2]]:5: warning: variable 'nonConstAnonymousNamespaceInt' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] } // namespace +static int nonConstStaticInt = 0; +// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: variable 'nonConstStaticInt' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-INTERNAL-LINKAGE-NOT: :[[@LINE-2]]:12: warning: variable 'nonConstStaticInt' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] + +static const int constStaticInt = 0; + class DummyClass { public: int nonConstPublicMemberVariable = 0; @@ -126,7 +135,8 @@ const DummyEnum constNamespaceEnumInstance = DummyEnum::first; namespace { DummyEnum nonConstAnonymousNamespaceEnumInstance = DummyEnum::first; } -// CHECK-MESSAGES: :[[@LINE-2]]:11: warning: variable 'nonConstAnonymousNamespaceEnumInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:11: warning: variable 'nonConstAnonymousNamespaceEnumInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-INTERNAL-LINKAGE-NOT: :[[@LINE-2]]:11: warning: variable 'nonConstAnonymousNamespaceEnumInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] // CHECKING FOR NON-CONST GLOBAL STRUCT /////////////////////////////////////// struct DummyStruct { @@ -169,7 +179,8 @@ const DummyStruct constNamespaceDummyStructInstance; namespace { DummyStruct nonConstAnonymousNamespaceStructInstance; } -// CHECK-MESSAGES: :[[@LINE-2]]:13: warning: variable 'nonConstAnonymousNamespaceStructInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:13: warning: variable 'nonConstAnonymousNamespaceStructInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-INTERNAL-LINKAGE-NOT: :[[@LINE-2]]:11: warning: variable 'nonConstAnonymousNamespaceEnumInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] // CHECKING FOR NON-CONST GLOBAL UNION //////////////////////////////////////// union DummyUnion { @@ -209,7 +220,8 @@ const DummyUnion constNamespaceDummyUnionInstance = {0x0}; namespace { DummyUnion nonConstAnonymousNamespaceUnionInstance = {0x0}; } -// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: variable 'nonConstAnonymousNamespaceUnionInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:12: warning: variable 'nonConstAnonymousNamespaceUnionInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] +// CHECK-MESSAGES-INTERNAL-LINKAGE-NOT: :[[@LINE-3]]:12: warning: variable 'nonConstAnonymousNamespaceUnionInstance' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables] // CHECKING FOR NON-CONST GLOBAL FUNCTION POINTER ///////////////////////////// int dummyFunction() { 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 eaa73b906ce09..d999b84cae03e 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 @@ -243,7 +243,7 @@ struct PositiveUninitializedBaseOrdering : public NegativeAggregateType, }; // We shouldn't need to initialize anything because PositiveUninitializedBase -// has a user-defined constructor. +// has a user-provided constructor. struct NegativeUninitializedBase : public PositiveUninitializedBase { NegativeUninitializedBase() {} }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-fix-mode-none.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-fix-mode-none.cpp new file mode 100644 index 0000000000000..3f2f5897bf718 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-fix-mode-none.cpp @@ -0,0 +1,10 @@ +// RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- \ +// RUN: -config="{CheckOptions: {misc-use-internal-linkage.FixMode: 'None'}}" -- -I%S/Inputs/use-internal-linkage + +void func() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func' +// CHECK-FIXES-NOT: static void func() {} + +int global; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'global' +// CHECK-FIXES-NOT: static int global; diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-func.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-func.cpp index c6c513fe0b0c0..9c91389542b03 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-func.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-func.cpp @@ -1,16 +1,21 @@ // RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- -- -I%S/Inputs/use-internal-linkage +// RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- \ +// RUN: -config="{CheckOptions: {misc-use-internal-linkage.FixMode: 'UseStatic'}}" -- -I%S/Inputs/use-internal-linkage #include "func.h" void func() {} // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func' +// CHECK-FIXES: static void func() {} template void func_template() {} // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func_template' +// CHECK-FIXES: static void func_template() {} void func_cpp_inc(); // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func_cpp_inc' +// CHECK-FIXES: static void func_cpp_inc(); #include "func_cpp.inc" diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-var.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-var.cpp index bd5ef5431de6c..01b8d28e61230 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-var.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-internal-linkage-var.cpp @@ -1,13 +1,17 @@ // RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- -- -I%S/Inputs/use-internal-linkage +// RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- \ +// RUN: -config="{CheckOptions: {misc-use-internal-linkage.FixMode: 'UseStatic'}}" -- -I%S/Inputs/use-internal-linkage #include "var.h" int global; // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'global' +// CHECK-FIXES: static int global; template T global_template; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: variable 'global_template' +// CHECK-FIXES: static T global_template; int gloabl_header; 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 1f2dad2b933ca..c7632fe007a4f 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 @@ -11,6 +11,7 @@ T max(T a, T b) { namespace std { template< class T > struct initializer_list { + const T *a, *b; initializer_list()=default; initializer_list(T*,int){} const T* begin() const {return nullptr;} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp index 2004993ebde51..150e3ac6494e3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace-ignore-implicit-constructors.cpp @@ -4,10 +4,11 @@ // RUN: true}}" namespace std { -template +template class initializer_list { public: + const E *a, *b; initializer_list() noexcept {} }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp index f7b1ad55f5df5..3f4a14cd9bb64 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-emplace.cpp @@ -8,9 +8,10 @@ // RUN: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}}" namespace std { -template +template class initializer_list { public: + const E *a, *b; initializer_list() noexcept {} }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp index 35091eb77c4c5..e1e25d76d4909 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/inefficient-vector-operation.cpp @@ -5,7 +5,7 @@ namespace std { -typedef int size_t; +typedef decltype(sizeof 0) size_t; template class initializer_list { public: @@ -15,6 +15,8 @@ template class initializer_list { using size_type = size_t; using iterator = const E*; using const_iterator = const E*; + iterator p; + size_t sz; initializer_list(); size_t size() const; // number of elements const E* begin() const; // first element diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-initialization.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-initialization.cpp index f259552dc8f1d..d02bb98cf583c 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-initialization.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-initialization.cpp @@ -906,3 +906,12 @@ void negativeNonConstMemberExpr() { } } + +bool operator==(ExpensiveToCopyType, ExpensiveToCopyType); + +template bool OperatorWithNoDirectCallee(T t) { + ExpensiveToCopyType a1; + ExpensiveToCopyType a2 = a1; + return a1 == t; +} + diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp index f42f2f37155af..b50ad4ce25839 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/isolate-declaration-cxx17.cpp @@ -31,7 +31,7 @@ struct SomeClass { namespace std { template -class initializer_list {}; +class initializer_list { const T *a, *b; }; template class vector { diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr.cpp index c14438aa93801..bad1055a01904 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr.cpp @@ -353,6 +353,23 @@ bool conditional_return_statements(int i) { // CHECK-FIXES: {{^}} return i == 0;{{$}} // CHECK-FIXES-NEXT: {{^}$}} +bool conditional_return_statements_no_fix_1(int i) { + if (i == 0) return true; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: {{.*}} in conditional return statement + // CHECK-MESSAGES: :[[@LINE-2]]:7: note: conditions that can be simplified + // comment + return false; + // CHECK-MESSAGES: :[[@LINE-1]]:3: note: return statement that can be simplified +} + +bool conditional_return_statements_no_fix_2(int i) { + if (i == 0) return true; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: {{.*}} in conditional return statement + // CHECK-MESSAGES: :[[@LINE-2]]:7: note: conditions that can be simplified + // comment + else return false; +} + bool conditional_return_statements_then_expr(int i, int j) { if (i == j) return (i == 0); else return false; } diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/allow-no-checks.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/allow-no-checks.cpp new file mode 100644 index 0000000000000..a1f059b92384d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/allow-no-checks.cpp @@ -0,0 +1,4 @@ +// RUN: not clang-tidy %s -checks='-*' +// RUN: clang-tidy %s -checks='-*' --allow-no-checks | FileCheck --match-full-lines %s + +// CHECK: No checks enabled. diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp index 9aabb1ed30e42..e4a7340318b93 100644 --- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp @@ -30,7 +30,7 @@ ClangDocContext getClangDocContext(std::vector UserStylesheets = {}, StringRef RepositoryUrl = "") { ClangDocContext CDCtx{ - {}, "test-project", {}, {}, {}, RepositoryUrl, UserStylesheets, {}}; + {}, "test-project", {}, {}, {}, RepositoryUrl, UserStylesheets}; CDCtx.UserStylesheets.insert( CDCtx.UserStylesheets.begin(), "../share/clang/clang-doc-default-stylesheet.css"); @@ -66,6 +66,7 @@ TEST(HTMLGeneratorTest, emitNamespaceHTML) { namespace Namespace +
test-project
@@ -176,6 +177,7 @@ TEST(HTMLGeneratorTest, emitRecordHTML) { class r +
test-project
@@ -290,6 +292,7 @@ TEST(HTMLGeneratorTest, emitFunctionHTML) { +
test-project
@@ -337,6 +340,7 @@ TEST(HTMLGeneratorTest, emitEnumHTML) { +
test-project
@@ -422,6 +426,7 @@ TEST(HTMLGeneratorTest, emitCommentHTML) { +
test-project
diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake b/clang/cmake/caches/Fuchsia-stage2.cmake index a573ec5473210..9892b5d58e719 100644 --- a/clang/cmake/caches/Fuchsia-stage2.cmake +++ b/clang/cmake/caches/Fuchsia-stage2.cmake @@ -321,7 +321,7 @@ foreach(target armv6m-unknown-eabi;armv7m-unknown-eabi;armv8m-unknown-eabi) set(RUNTIMES_${target}_CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") set(RUNTIMES_${target}_CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY CACHE STRING "") foreach(lang C;CXX;ASM) - set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "--target=${target} -mthumb" CACHE STRING "") + set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "--target=${target} -mthumb -Wno-atomic-alignment" CACHE STRING "") endforeach() foreach(type SHARED;MODULE;EXE) set(RUNTIMES_${target}_CMAKE_${type}_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "") diff --git a/clang/docs/BoundsSafety.rst b/clang/docs/BoundsSafety.rst index f1837675ec9bf..8fd655663edb0 100644 --- a/clang/docs/BoundsSafety.rst +++ b/clang/docs/BoundsSafety.rst @@ -8,6 +8,9 @@ Overview ======== +**NOTE:** This is a design document and the feature is not available for users yet. +Please see :doc:`BoundsSafetyImplPlans` for more details. + ``-fbounds-safety`` is a C extension to enforce bounds safety to prevent out-of-bounds (OOB) memory accesses, which remain a major source of security vulnerabilities in C. ``-fbounds-safety`` aims to eliminate this class of bugs @@ -55,9 +58,7 @@ adopt, offering these properties that make it widely adoptable in practice: * It has a relatively low adoption cost. This document discusses the key designs of ``-fbounds-safety``. The document is -subject to be actively updated with a more detailed specification. The -implementation plan can be found in :doc:`BoundsSafetyImplPlans`. - +subject to be actively updated with a more detailed specification. Programming Model ================= diff --git a/clang/docs/BoundsSafetyImplPlans.rst b/clang/docs/BoundsSafetyImplPlans.rst index 4fbf87f966350..93c2ed7b43402 100644 --- a/clang/docs/BoundsSafetyImplPlans.rst +++ b/clang/docs/BoundsSafetyImplPlans.rst @@ -5,8 +5,31 @@ Implementation plans for ``-fbounds-safety`` .. contents:: :local: +Gradual updates with experimental flag +====================================== + +The feature will be implemented as a series of smaller PRs and we will guard our +implementation with an experimental flag ``-fexperimental-bounds-safety`` until +the usable model is fully available. Once the model is ready for use, we will +expose the flag ``-fbounds-safety``. + +Possible patch sets +------------------- + +* External bounds annotations and the (late) parsing logic. +* Internal bounds annotations (wide pointers) and their parsing logic. +* Clang code generation for wide pointers with debug information. +* Pointer cast semantics involving bounds annotations (this could be divided + into multiple sub-PRs). +* CFG analysis for pairs of related pointer and count assignments and the likes. +* Bounds check expressions in AST and the Clang code generation (this could also + be divided into multiple sub-PRs). + +Proposed implementation +======================= + External bounds annotations -=========================== +--------------------------- The bounds annotations are C type attributes appertaining to pointer types. If an attribute is added to the position of a declaration attribute, e.g., ``int @@ -14,7 +37,7 @@ an attribute is added to the position of a declaration attribute, e.g., ``int type of the declaration (``int *``). New sugar types -=============== +--------------- An external bounds annotation creates a type sugar of the underlying pointer types. We will introduce a new sugar type, ``DynamicBoundsPointerType`` to @@ -29,7 +52,7 @@ overloading. However, this design requires a separate logic to walk through the entire type hierarchy to check type compatibility of bounds annotations. Late parsing for C -================== +------------------ A bounds annotation such as ``__counted_by(count)`` can be added to type of a struct field declaration where count is another field of the same struct @@ -43,7 +66,7 @@ same logic. This requires introducing late parsing logic for C/C++ type attributes. Internal bounds annotations -=========================== +--------------------------- ``__indexable`` and ``__bidi_indexable`` alter pointer representations to be equivalent to a struct with the pointer and the corresponding bounds fields. @@ -65,7 +88,7 @@ operations returning wide pointers. Alternatively, a new ``TEK`` and an expression emitter dedicated to wide pointers could be introduced. Default bounds annotations -========================== +-------------------------- The model may implicitly add ``__bidi_indexable`` or ``__single`` depending on the context of the declaration that has the pointer type. ``__bidi_indexable`` @@ -79,7 +102,7 @@ This also requires the parser to reset the type of the declaration with the newly created type with the right default attribute. Promotion expression -==================== +-------------------- A new expression will be introduced to represent the conversion from a pointer with an external bounds annotation, such as ``__counted_by``, to @@ -88,7 +111,7 @@ CastExprs because it requires an extra subexpression(s) to provide the bounds information necessary to create a wide pointer. Bounds check expression -======================= +----------------------- Bounds checks are part of semantics defined in the ``-fbounds-safety`` language model. Hence, exposing the bounds checks and other semantic actions in the AST @@ -98,7 +121,7 @@ and has the additional sub-expressions that are necessary to perform the check according to the kind. Paired assignment check -======================= +----------------------- ``-fbounds-safety`` enforces that variables or fields related with the same external bounds annotation (e.g., ``buf`` and ``count`` related with @@ -123,7 +146,7 @@ provides a linear view of statements within each ``CFGBlock`` (Clang ``CFGBlock`` represents a single basic block in a source-level CFG). Bounds check optimizations -========================== +-------------------------- In ``-fbounds-safety``, the Clang frontend emits run-time checks for every memory dereference if the type system or analyses in the frontend couldn’t @@ -229,27 +252,3 @@ solution. ``-fbounds-safety`` is not currently supported in C++, but we believe the general approach would be applicable for future efforts. - -Upstreaming plan -================ - -Gradual updates with experimental flag --------------------------------------- - -The upstreaming will take place as a series of smaller PRs and we will guard our -implementation with an experimental flag ``-fexperimental-bounds-safety`` until -the usable model is fully upstreamed. Once the model is ready for use, we will -expose the flag ``-fbounds-safety``. - -Possible patch sets -------------------- - -* External bounds annotations and the (late) parsing logic. -* Internal bounds annotations (wide pointers) and their parsing logic. -* Clang code generation for wide pointers with debug information. -* Pointer cast semantics involving bounds annotations (this could be divided - into multiple sub-PRs). -* CFG analysis for pairs of related pointer and count assignments and the likes. -* Bounds check expressions in AST and the Clang code generation (this could also - be divided into multiple sub-PRs). - diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index bb00c20922d36..6c2e6da594847 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4443,23 +4443,51 @@ the configuration (without a prefix: ``Auto``). false: import {VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying, VeryLongImportsAreAnnoying,} from "some/module.js" +.. _KeepEmptyLines: + +**KeepEmptyLines** (``KeepEmptyLinesStyle``) :versionbadge:`clang-format 19` :ref:`¶ ` + Which empty lines are kept. See ``MaxEmptyLinesToKeep`` for how many + consecutive empty lines are kept. + + Nested configuration flags: + + Options regarding which empty lines are kept. + + For example, the config below will remove empty lines at start of the + file, end of the file, and start of blocks. + + + .. code-block:: c++ + + KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: false + AtStartOfFile: false + + * ``bool AtEndOfFile`` Keep empty lines at end of file. + + * ``bool AtStartOfBlock`` Keep empty lines at start of a block. + + .. code-block:: c++ + + true: false: + if (foo) { vs. if (foo) { + bar(); + bar(); } + } + + * ``bool AtStartOfFile`` Keep empty lines at start of file. + + .. _KeepEmptyLinesAtEOF: **KeepEmptyLinesAtEOF** (``Boolean``) :versionbadge:`clang-format 17` :ref:`¶ ` - Keep empty lines (up to ``MaxEmptyLinesToKeep``) at end of file. + This option is deprecated. See ``AtEndOfFile`` of ``KeepEmptyLines``. .. _KeepEmptyLinesAtTheStartOfBlocks: **KeepEmptyLinesAtTheStartOfBlocks** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ ` - If true, the empty line at the start of blocks is kept. - - .. code-block:: c++ - - true: false: - if (foo) { vs. if (foo) { - bar(); - bar(); } - } + This option is deprecated. See ``AtStartOfBlock`` of ``KeepEmptyLines``. .. _LambdaBodyIndentation: @@ -6214,6 +6242,7 @@ the configuration (without a prefix: ``Auto``). # Example of usage: SpacesInParens: Custom SpacesInParensOptions: + ExceptDoubleParentheses: false InConditionalStatements: true InEmptyParentheses: true @@ -6226,9 +6255,22 @@ the configuration (without a prefix: ``Auto``). # Should be declared this way: SpacesInParens: Custom SpacesInParensOptions: + ExceptDoubleParentheses: false InConditionalStatements: true Other: true + * ``bool ExceptDoubleParentheses`` Override any of the following options to prevent addition of space + when both opening and closing parentheses use multiple parentheses. + + .. code-block:: c++ + + true: + __attribute__(( noreturn )) + __decltype__(( x )) + if (( a = b )) + false: + Uses the applicable option. + * ``bool InConditionalStatements`` Put a space in parentheses only inside conditional statements (``for/if/while/switch...``). @@ -6242,8 +6284,9 @@ the configuration (without a prefix: ``Auto``). .. code-block:: c++ - true: false: - x = ( int32 )y vs. x = (int32)y + true: false: + x = ( int32 )y vs. x = (int32)y + y = (( int (*)(int) )foo)(x); y = ((int (*)(int))foo)(x); * ``bool InEmptyParentheses`` Insert a space in empty parentheses, i.e. ``()``. @@ -6261,8 +6304,8 @@ the configuration (without a prefix: ``Auto``). .. code-block:: c++ - true: false: - t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; + true: false: + t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; .. _SpacesInParentheses: diff --git a/clang/docs/HIPSupport.rst b/clang/docs/HIPSupport.rst index 5ba84c2f67055..e26297c7af97a 100644 --- a/clang/docs/HIPSupport.rst +++ b/clang/docs/HIPSupport.rst @@ -284,3 +284,48 @@ Example Usage Base* basePtr = &obj; basePtr->virtualFunction(); // Allowed since obj is constructed in device code } + +SPIR-V Support on HIPAMD ToolChain +================================== + +The HIPAMD ToolChain supports targetting +`AMDGCN Flavoured SPIR-V `_. +The support for SPIR-V in the ROCm and HIPAMD ToolChain is under active +development. + +Compilation Process +------------------- + +When compiling HIP programs with the intent of utilizing SPIR-V, the process +diverges from the traditional compilation flow: + +Using ``--offload-arch=amdgcnspirv`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- **Target Triple**: The ``--offload-arch=amdgcnspirv`` flag instructs the + compiler to use the target triple ``spirv64-amd-amdhsa``. This approach does + generates generic AMDGCN SPIR-V which retains architecture specific elements + without hardcoding them, thus allowing for optimal target specific code to be + generated at run time, when the concrete target is known. + +- **LLVM IR Translation**: The program is compiled to LLVM Intermediate + Representation (IR), which is subsequently translated into SPIR-V. In the + future, this translation step will be replaced by direct SPIR-V emission via + the SPIR-V Back-end. + +- **Clang Offload Bundler**: The resulting SPIR-V is embedded in the Clang + offload bundler with the bundle ID ``hip-spirv64-amd-amdhsa--amdgcnspirv``. + +Mixed with Normal ``--offload-arch`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Mixing ``amdgcnspirv`` and concrete ``gfx###`` targets via ``--offload-arch`` +is not currently supported; this limitation is temporary and will be removed in +a future release** + +Architecture Specific Macros +---------------------------- + +None of the architecture specific :doc:`AMDGPU macros ` are +defined when targeting SPIR-V. An alternative, more flexible mechanism to enable +doing per target / per feature code selection will be added in the future. diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 9830b35faae12..d9439d49a2e29 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -657,6 +657,12 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in T __builtin_elementwise_sin(T x) return the sine of x interpreted as an angle in radians floating point types T __builtin_elementwise_cos(T x) return the cosine of x interpreted as an angle in radians floating point types T __builtin_elementwise_tan(T x) return the tangent of x interpreted as an angle in radians floating point types + T __builtin_elementwise_asin(T x) return the arcsine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_acos(T x) return the arccosine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_atan(T x) return the arctangent of x interpreted as an angle in radians floating point types + T __builtin_elementwise_sinh(T x) return the hyperbolic sine of angle x in radians floating point types + T __builtin_elementwise_cosh(T x) return the hyperbolic cosine of angle x in radians floating point types + T __builtin_elementwise_tanh(T x) return the hyperbolic tangent of angle x in radians floating point types T __builtin_elementwise_floor(T x) return the largest integral value less than or equal to x floating point types T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types @@ -3477,6 +3483,60 @@ Query for this feature with ``__has_builtin(__builtin_trap)``. ``__builtin_arm_trap`` is lowered to the ``llvm.aarch64.break`` builtin, and then to ``brk #payload``. +``__builtin_verbose_trap`` +-------------------------- + +``__builtin_verbose_trap`` causes the program to stop its execution abnormally +and shows a human-readable description of the reason for the termination when a +debugger is attached or in a symbolicated crash log. + +**Syntax**: + +.. code-block:: c++ + + __builtin_verbose_trap(const char *category, const char *reason) + +**Description** + +``__builtin_verbose_trap`` is lowered to the ` ``llvm.trap`` `_ builtin. +Additionally, clang emits debugging information that represents an artificial +inline frame whose name encodes the category and reason strings passed to the builtin, +prefixed by a "magic" prefix. + +For example, consider the following code: + +.. code-block:: c++ + + void foo(int* p) { + if (p == nullptr) + __builtin_verbose_trap("check null", "Argument must not be null!"); + } + +The debugging information would look as if it were produced for the following code: + +.. code-block:: c++ + + __attribute__((always_inline)) + inline void "__clang_trap_msg$check null$Argument must not be null!"() { + __builtin_trap(); + } + + void foo(int* p) { + if (p == nullptr) + "__clang_trap_msg$check null$Argument must not be null!"(); + } + +However, the generated code would not actually contain a call to the artificial +function — it only exists in the debugging information. + +Query for this feature with ``__has_builtin(__builtin_verbose_trap)``. Note that +users need to enable debug information to enable this feature. A call to this +builtin is equivalent to a call to ``__builtin_trap`` if debug information isn't +enabled. + +The optimizer can merge calls to trap with different messages, which degrades +the debugging experience. + ``__builtin_allow_runtime_check`` --------------------------------- @@ -5339,7 +5399,7 @@ The ``#pragma clang section`` directive obeys the following rules: * The pragma clang section is enabled automatically, without need of any flags. -* This feature is only defined to work sensibly for ELF targets. +* This feature is only defined to work sensibly for ELF and Mach-O targets. * If section name is specified through _attribute_((section("myname"))), then the attribute name gains precedence. @@ -5362,16 +5422,85 @@ The ``#pragma comment(lib, ...)`` directive is supported on all ELF targets. The second parameter is the library name (without the traditional Unix prefix of ``lib``). This allows you to provide an implicit link of dependent libraries. -Evaluating Object Size Dynamically -================================== +Evaluating Object Size +====================== + +Clang supports the builtins ``__builtin_object_size`` and +``__builtin_dynamic_object_size``. The semantics are compatible with GCC's +builtins of the same names, but the details are slightly different. + +.. code-block:: c -Clang supports the builtin ``__builtin_dynamic_object_size``, the semantics are -the same as GCC's ``__builtin_object_size`` (which Clang also supports), but -``__builtin_dynamic_object_size`` can evaluate the object's size at runtime. -``__builtin_dynamic_object_size`` is meant to be used as a drop-in replacement -for ``__builtin_object_size`` in libraries that support it. + size_t __builtin_[dynamic_]object_size(const void *ptr, int type) + +Returns the number of accessible bytes ``n`` past ``ptr``. The value returned +depends on ``type``, which is required to be an integer constant between 0 and +3: + +* If ``type & 2 == 0``, the least ``n`` is returned such that accesses to + ``(const char*)ptr + n`` and beyond are known to be out of bounds. This is + ``(size_t)-1`` if no better bound is known. +* If ``type & 2 == 2``, the greatest ``n`` is returned such that accesses to + ``(const char*)ptr + i`` are known to be in bounds, for 0 <= ``i`` < ``n``. + This is ``(size_t)0`` if no better bound is known. + +.. code-block:: c + + char small[10], large[100]; + bool cond; + // Returns 100: writes of more than 100 bytes are known to be out of bounds. + int n100 = __builtin_object_size(cond ? small : large, 0); + // Returns 10: writes of 10 or fewer bytes are known to be in bounds. + int n10 = __builtin_object_size(cond ? small : large, 2); + +* If ``type & 1 == 0``, pointers are considered to be in bounds if they point + into the same storage as ``ptr`` -- that is, the same stack object, global + variable, or heap allocation. +* If ``type & 1 == 1``, pointers are considered to be in bounds if they point + to the same subobject that ``ptr`` points to. If ``ptr`` points to an array + element, other elements of the same array, but not of enclosing arrays, are + considered in bounds. + +.. code-block:: c + + struct X { char a, b, c; } x; + static_assert(__builtin_object_size(&x, 0) == 3); + static_assert(__builtin_object_size(&x.b, 0) == 2); + static_assert(__builtin_object_size(&x.b, 1) == 1); + +.. code-block:: c -For instance, here is a program that ``__builtin_dynamic_object_size`` will make + char a[10][10][10]; + static_assert(__builtin_object_size(&a, 1) == 1000); + static_assert(__builtin_object_size(&a[1], 1) == 900); + static_assert(__builtin_object_size(&a[1][1], 1) == 90); + static_assert(__builtin_object_size(&a[1][1][1], 1) == 9); + +The values returned by this builtin are a best effort conservative approximation +of the correct answers. When ``type & 2 == 0``, the true value is less than or +equal to the value returned by the builtin, and when ``type & 2 == 1``, the true +value is greater than or equal to the value returned by the builtin. + +For ``__builtin_object_size``, the value is determined entirely at compile time. +With optimization enabled, better results will be produced, especially when the +call to ``__builtin_object_size`` is in a different function from the formation +of the pointer. Unlike in GCC, enabling optimization in Clang does not allow +more information about subobjects to be determined, so the ``type & 1 == 1`` +case will often give imprecise results when used across a function call boundary +even when optimization is enabled. + +`The pass_object_size and pass_dynamic_object_size attributes `_ +can be used to invisibly pass the object size for a pointer parameter alongside +the pointer in a function call. This allows more precise object sizes to be +determined both when building without optimizations and in the ``type & 1 == 1`` +case. + +For ``__builtin_dynamic_object_size``, the result is not limited to being a +compile time constant. Instead, a small amount of runtime evaluation is +permitted to determine the size of the object, in order to give a more precise +result. ``__builtin_dynamic_object_size`` is meant to be used as a drop-in +replacement for ``__builtin_object_size`` in libraries that support it. For +instance, here is a program that ``__builtin_dynamic_object_size`` will make safer: .. code-block:: c diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index 19b3384293aed..68674f318c84f 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -328,6 +328,23 @@ be done in a single instruction with an immediate integer. ``pointer`` must have pointer type, and ``integer`` must have integer type. The result has type ``ptrauth_extra_data_t``. +``ptrauth_string_discriminator`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_string_discriminator(string) + +Compute a constant discriminator from the given string. + +``string`` must be a string literal of ``char`` character type. The result has +type ``ptrauth_extra_data_t``. + +The result value is never zero and always within range for both the +``__ptrauth`` qualifier and ``ptrauth_blend_discriminator``. + +This can be used in constant expressions. + ``ptrauth_strip`` ^^^^^^^^^^^^^^^^^ @@ -339,6 +356,25 @@ Given that ``signedPointer`` matches the layout for signed pointers signed with the given key, extract the raw pointer from it. This operation does not trap and cannot fail, even if the pointer is not validly signed. +``ptrauth_sign_constant`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_sign_constant(pointer, key, discriminator) + +Return a signed pointer for a constant address in a manner which guarantees +a non-attackable sequence. + +``pointer`` must be a constant expression of pointer type which evaluates to +a non-null pointer. +``key`` must be a constant expression of type ``ptrauth_key``. +``discriminator`` must be a constant expression of pointer or integer type; +if an integer, it will be coerced to ``ptrauth_extra_data_t``. +The result will have the same type as ``pointer``. + +This can be used in constant expressions. + ``ptrauth_sign_unauthenticated`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 36e23981cc5df..36cf615a4287c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -95,10 +95,29 @@ ABI Changes in This Version - Fixed Microsoft calling convention when returning classes that have a deleted copy assignment operator. Such a class should be returned indirectly. +- Removed the global alias that was pointing to AArch64 Function Multiversioning + ifuncs. Its purpose was to preserve backwards compatibility when the ".ifunc" + suffix got removed from the name mangling. The alias interacts badly with + GlobalOpt (see the issue #96197). + +- Fixed Microsoft name mangling for auto non-type template arguments of pointer + type for MSVC 1920+. This change resolves incompatibilities with code compiled + by MSVC 1920+ but will introduce incompatibilities with code compiled by + earlier versions of Clang unless such code is built with the compiler option + `-fms-compatibility-version=19.14` to imitate the MSVC 1914 mangling behavior. + +- Fixed Microsoft name mangling for auto non-type template arguments of pointer + to member type for MSVC 1920+. This change resolves incompatibilities with code + compiled by MSVC 1920+ but will introduce incompatibilities with code compiled by + earlier versions of Clang unless such code is built with the compiler option + `-fms-compatibility-version=19.14` to imitate the MSVC 1914 mangling behavior. + (GH#70899). + AST Dumping Potentially Breaking Changes ---------------------------------------- - The text ast-dumper has improved printing of TemplateArguments. +- The text decl-dumper prints template parameters' trailing requires expressions now. Clang Frontend Potentially Breaking Changes ------------------------------------------- @@ -280,6 +299,9 @@ Resolutions to C++ Defect Reports - P0522 implementation is enabled by default in all language versions, and provisional wording for CWG2398 is implemented. +- Clang now performs type-only lookup for the name in ``using enum`` declaration. + (`CWG2877: Type-only lookup for using-enum-declarator `_). + - Clang now requires a template argument list after a template keyword. (`CWG96: Syntactic disambiguation using the template keyword `_). @@ -289,6 +311,12 @@ Resolutions to C++ Defect Reports C Language Changes ------------------ +C2y Feature Support +^^^^^^^^^^^^^^^^^^^ +- Clang now enables C2y mode with ``-std=c2y``. This sets ``__STDC_VERSION__`` + to ``202400L`` so that it's greater than the value for C23. The value of this + macro is subject to change in the future. + C23 Feature Support ^^^^^^^^^^^^^^^^^^^ - No longer diagnose use of binary literals as an extension in C23 mode. Fixes @@ -317,6 +345,11 @@ C23 Feature Support - Properly promote bit-fields of bit-precise integer types to the field's type rather than to ``int``. #GH87641 +- Added the ``INFINITY`` and ``NAN`` macros to Clang's ```` + freestanding implementation; these macros were defined in ```` in C99 + but C23 added them to ```` in + `WG14 N2848 `_. + Non-comprehensive list of changes in this release ------------------------------------------------- @@ -372,6 +405,11 @@ Non-comprehensive list of changes in this release - ``#pragma GCC diagnostic warning "-Wfoo"`` can now downgrade ``-Werror=foo`` errors and certain default-to-error ``-W`` diagnostics to warnings. +- Support importing C++20 modules in clang-repl. + +- Added support for ``TypeLoc::dump()`` for easier debugging, and improved + textual and JSON dumping for various ``TypeLoc``-related nodes. + New Compiler Flags ------------------ - ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and @@ -501,6 +539,11 @@ Attribute Changes in Clang }; +- Introduced new function type attributes ``[[clang::nonblocking]]``, ``[[clang::nonallocating]]``, + ``[[clang::blocking]]``, and ``[[clang::allocating]]``, with GNU-style variants as well. + The attributes declare constraints about a function's behavior pertaining to blocking and + heap memory allocation. + Improvements to Clang's diagnostics ----------------------------------- - Clang now applies syntax highlighting to the code snippets it @@ -598,6 +641,19 @@ Improvements to Clang's diagnostics - Clang no longer emits a "declared here" note for a builtin function that has no declaration in source. Fixes #GH93369. +- Clang now has an improved error message for captures that refer to a class member. + Fixes #GH94764. + +- Clang now diagnoses unsupported class declarations for ``std::initializer_list`` when they are + used rather than when they are needed for constant evaluation or when code is generated for them. + The check is now stricter to prevent crashes for some unsupported declarations (Fixes #GH95495). + +- Clang now diagnoses dangling cases where a pointer is assigned to a temporary + that will be destroyed at the end of the full expression. + Fixes #GH54492. + +- Clang now shows implicit deduction guides when diagnosing overload resolution failure. #GH92393. + Improvements to Clang's time-trace ---------------------------------- @@ -711,6 +767,9 @@ Bug Fixes to Compiler Builtins - Fix crash when atomic builtins are called with pointer to zero-size struct (#GH90330) +- Clang now allows pointee types of atomic builtin arguments to be complete template types + that was not instantiated elsewhere. + Bug Fixes to Attribute Support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -898,7 +957,12 @@ Bug Fixes to C++ Support between the addresses of two labels (a GNU extension) to a pointer within a constant expression. (#GH95366). - Fix immediate escalation bugs in the presence of dependent call arguments. (#GH94935) - Clang now diagnoses explicit specializations with storage class specifiers in all contexts. - +- Fix an assertion failure caused by parsing a lambda used as a default argument for the value of a + forward-declared class. (#GH93512). +- Fixed a bug in access checking inside return-type-requirement of compound requirements. (#GH93788). +- Fixed an assertion failure about invalid conversion when calling lambda. (#GH96205). +- Fixed a bug where the first operand of binary ``operator&`` would be transformed as if it was the operand + of the address of operator. (#GH97483). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1031,10 +1095,10 @@ AIX Support WebAssembly Support ^^^^^^^^^^^^^^^^^^^ -The -mcpu=generic configuration now enables multivalue feature, which is -standardized and available in all major engines. Enabling multivalue here only -enables the language feature but does not turn on the multivalue ABI (this -enables non-ABI uses of multivalue, like exnref). +The -mcpu=generic configuration now enables multivalue and reference-types. +These proposals are standardized and available in all major engines. Enabling +multivalue here only enables the language feature but does not turn on the +multivalue ABI (this enables non-ABI uses of multivalue, like exnref). AVR Support ^^^^^^^^^^^ @@ -1075,6 +1139,11 @@ clang-format - Adds ``AllowShortCaseExpressionOnASingleLine`` option. - Adds ``AlignCaseArrows`` suboption to ``AlignConsecutiveShortCaseStatements``. - Adds ``LeftWithLastLine`` suboption to ``AlignEscapedNewlines``. +- Adds ``KeepEmptyLines`` option to deprecate ``KeepEmptyLinesAtEOF`` + and ``KeepEmptyLinesAtTheStartOfBlocks``. +- Add ``ExceptDoubleParentheses`` sub-option for ``SpacesInParensOptions`` + to override addition of spaces between multiple, non-redundant parentheses + similar to the rules used for ``RemoveParentheses``. libclang -------- @@ -1103,11 +1172,6 @@ Crash and bug fixes Improvements ^^^^^^^^^^^^ -- Support importing C++20 modules in clang-repl. - -- Added support for ``TypeLoc::dump()`` for easier debugging, and improved - textual and JSON dumping for various ``TypeLoc``-related nodes. - Moved checkers ^^^^^^^^^^^^^^ diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index d273102fe9000..087c75bc39a16 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -3458,9 +3458,9 @@ Differences between various standard modes clang supports the -std option, which changes what language mode clang uses. The supported modes for C are c89, gnu89, c94, c99, gnu99, c11, gnu11, c17, -gnu17, c23, gnu23, and various aliases for those modes. If no -std option is -specified, clang defaults to gnu17 mode. Many C99 and C11 features are -supported in earlier modes as a conforming extension, with a warning. Use +gnu17, c23, gnu23, c2y, gnu2y, and various aliases for those modes. If no -std +option is specified, clang defaults to gnu17 mode. Many C99 and C11 features +are supported in earlier modes as a conforming extension, with a warning. Use ``-pedantic-errors`` to request an error if a feature from a later standard revision is used in an earlier mode. @@ -3523,6 +3523,10 @@ Differences between ``*17`` and ``*23`` modes: - ``[[]]`` attributes are supported by default in ``*23`` mode, and as an extension in ``*17`` and earlier modes. +Differences between ``*23`` and ``*2y`` modes: + +- ``__STDC_VERSION__`` is defined to ``202400L`` rather than ``202311L``. + GCC extensions not implemented yet ---------------------------------- diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index b8d5f372bdf61..42c097d973d53 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -420,21 +420,56 @@ around, such as ``std::string_view``. cplusplus.Move (C++) """""""""""""""""""" -Method calls on a moved-from object and copying a moved-from object will be reported. - +Find use-after-move bugs in C++. This includes method calls on moved-from +objects, assignment of a moved-from object, and repeated move of a moved-from +object. .. code-block:: cpp - struct A { + struct A { void foo() {} }; - void f() { + void f1() { A a; A b = std::move(a); // note: 'a' became 'moved-from' here a.foo(); // warn: method call on a 'moved-from' object 'a' } + void f2() { + A a; + A b = std::move(a); + A c(std::move(a)); // warn: move of an already moved-from object + } + + void f3() { + A a; + A b = std::move(a); + b = a; // warn: copy of moved-from object + } + +The checker option ``WarnOn`` controls on what objects the use-after-move is +checked: + +* The most strict value is ``KnownsOnly``, in this mode only objects are + checked whose type is known to be move-unsafe. These include most STL objects + (but excluding move-safe ones) and smart pointers. +* With option value ``KnownsAndLocals`` local variables (of any type) are + additionally checked. The idea behind this is that local variables are + usually not tempting to be re-used so an use after move is more likely a bug + than with member variables. +* With option value ``All`` any use-after move condition is checked on all + kinds of variables, excluding global variables and known move-safe cases. + +Default value is ``KnownsAndLocals``. + +Calls of methods named ``empty()`` or ``isEmpty()`` are allowed on moved-from +objects because these methods are considered as move-safe. Functions called +``reset()``, ``destroy()``, ``clear()``, ``assign``, ``resize``, ``shrink`` are +treated as state-reset functions and are allowed on moved-from objects, these +make the object valid again. This applies to any type of object (not only STL +ones). + .. _cplusplus-NewDelete: cplusplus.NewDelete (C++) diff --git a/clang/docs/analyzer/images/analyzer_html.png b/clang/docs/analyzer/images/analyzer_html.png new file mode 100644 index 0000000000000..607ea1882e71b Binary files /dev/null and b/clang/docs/analyzer/images/analyzer_html.png differ diff --git a/clang/docs/analyzer/images/analyzer_xcode.png b/clang/docs/analyzer/images/analyzer_xcode.png new file mode 100644 index 0000000000000..84c69809e8c39 Binary files /dev/null and b/clang/docs/analyzer/images/analyzer_xcode.png differ diff --git a/clang/docs/analyzer/images/scan_build_cmd.png b/clang/docs/analyzer/images/scan_build_cmd.png new file mode 100644 index 0000000000000..464fd4e129a20 Binary files /dev/null and b/clang/docs/analyzer/images/scan_build_cmd.png differ diff --git a/clang/docs/analyzer/user-docs.rst b/clang/docs/analyzer/user-docs.rst index 2292cec6944b1..08cb5119e810b 100644 --- a/clang/docs/analyzer/user-docs.rst +++ b/clang/docs/analyzer/user-docs.rst @@ -6,5 +6,9 @@ Contents: .. toctree:: :maxdepth: 2 + user-docs/Installation + user-docs/CommandLineUsage + user-docs/UsingWithXCode + user-docs/FilingBugs user-docs/CrossTranslationUnit user-docs/TaintAnalysisConfiguration diff --git a/clang/docs/analyzer/user-docs/CommandLineUsage.rst b/clang/docs/analyzer/user-docs/CommandLineUsage.rst new file mode 100644 index 0000000000000..d7f8253469df4 --- /dev/null +++ b/clang/docs/analyzer/user-docs/CommandLineUsage.rst @@ -0,0 +1,239 @@ +Command Line Usage: scan-build and CodeChecker +============================================== + +This document provides guidelines for running the static analyzer from the command line on whole projects. +CodeChecker and scan-build are two CLI tools for using CSA on multiple files (tranlation units). +Both provide a way of driving the analyzer, detecting compilation flags, and generating reports. +CodeChecker is more actively maintained, provides heuristics for working with multiple versions of popular compilers and it also comes with a web-based GUI for viewing, filtering, categorizing and suppressing the results. +Therefore CodeChecker is recommended in case you need any of the above features or just more customizability in general. + +Comparison of CodeChecker and scan-build +---------------------------------------- + +The static analyzer is by design a GUI tool originally intended to be consumed by the XCode IDE. +Its purpose is to find buggy execution paths in the program, and such paths are very hard to comprehend by looking at a non-interactive standard output. +It is possible, however, to invoke the static analyzer from the command line in order to obtain analysis results, and then later view them interactively in a graphical interface. +The following tools are used commonly to run the analyzer from the command line. +Both tools are wrapper scripts to drive the analysis and the underlying invocations of the Clang compiler: + +1. scan-build_ is an old and simple command line tool that emits static analyzer warnings as HTML files while compiling your project. You can view the analysis results in your web browser. + - Useful for individual developers who simply want to view static analysis results at their desk, or in a very simple collaborative environment. + - Works on all major platforms (Windows, Linux, macOS) and is available as a package in many Linux distributions. + - Does not include support for cross-translation-unit analysis. + +2. CodeChecker_ is a driver and web server that runs the static analyzer on your projects on demand and maintains a database of issues. + - Perfect for managing large amounts of thee static analyzer warnings in a collaborative environment. + - Generally much more feature-rich than scan-build. + - Supports incremental analysis: Results can be stored in a database, subsequent analysis runs can be compared to list the newly added defects. + - :doc:`CrossTranslationUnit` is supported fully on Linux via CodeChecker. + - Can run clang-tidy checkers too. + - Open source, but out-of-tree, i.e. not part of the LLVM project. + +scan-build +---------- + +**scan-build** is a command line utility that enables a user to run the static analyzer over their codebase as part of performing a regular build (from the command line). + +How does it work? +~~~~~~~~~~~~~~~~~ + +During a project build, as source files are compiled they are also analyzed in tandem by the static analyzer. + +Upon completion of the build, results are then presented to the user within a web browser. + +Will it work with any build system? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**scan-build** has little or no knowledge about how you build your code. It works by overriding the ``CC`` and ``CXX`` environment variables to (hopefully) change your build to use a "fake" compiler instead of the one that would normally build your project. This fake compiler executes either ``clang`` or ``gcc`` (depending on the platform) to compile your code and then executes the static analyzer to analyze your code. + +This "poor man's interposition" works amazingly well in many cases and falls down in others. Please consult the information on this page on making the best use of **scan-build**, which includes getting it to work when the aforementioned hack fails to work. + +.. image:: ../images/scan_build_cmd.png + +.. image:: ../images/analyzer_html.png + +**Viewing static analyzer results in a web browser** + +Basic Usage +~~~~~~~~~~~ + +Basic usage of ``scan-build`` is designed to be simple: just place the word "scan-build" in front of your build command:: + + $ scan-build make + $ scan-build xcodebuild + +In the first case ``scan-build`` analyzes the code of a project built with ``make`` and in the second case ``scan-build`` analyzes a project built using ``xcodebuild``. + +Here is the general format for invoking ``scan-build``:: + + $ scan-build [scan-build options] [command options] + +Operationally, ``scan-build`` literally runs with all of the subsequent options passed to it. For example, one can pass ``-j4`` to ``make`` get a parallel build over 4 cores:: + + $ scan-build make -j4 + +In almost all cases, ``scan-build`` makes no effort to interpret the options after the build command; it simply passes them through. In general, ``scan-build`` should support parallel builds, but **not distributed builds**. + +It is also possible to use ``scan-build`` to analyze specific files:: + + $ scan-build gcc -c t1.c t2.c + +This example causes the files ``t1.c`` and ``t2.c`` to be analyzed. + +For Windows Users +~~~~~~~~~~~~~~~~~ + +Windows users must have Perl installed to use scan-build. + +``scan-build.bat`` script allows you to launch scan-build in the same way as it described in the Basic Usage section above. To invoke scan-build from an arbitrary location, add the path to the folder containing scan-build.bat to your PATH environment variable. + +If you have unexpected compilation/make problems when running scan-build with MinGW/MSYS the following information may be helpful: + +- If getting unexpected ``"fatal error: no input files"`` while building with MSYS make from the Windows cmd, try one of these solutions: + - Use MinGW ``mingw32-make`` instead of MSYS ``make`` and exclude the path to MSYS from PATH to prevent ``mingw32-make`` from using MSYS utils. MSYS utils are dependent on the MSYS runtime and they are not intended for being run from the Windows cmd. Specifically, makefile commands with backslashed quotes may be heavily corrupted when passed for execution. + - Run ``make`` from the sh shell:: + + $ scan-build [scan-build options] sh -c "make [make options]" + +- If getting ``"Error : *** target pattern contains no `%'"`` while using GNU Make 3.81, try to use another version of make. + +Other Options +~~~~~~~~~~~~~ + +As mentioned above, extra options can be passed to ``scan-build``. These options prefix the build command. For example:: + + $ scan-build -k -V make + $ scan-build -k -V xcodebuild + +Here is a subset of useful options: + +- **-o**: Target directory for HTML report files. Subdirectories will be created as needed to represent separate "runs" of the analyzer. If this option is not specified, a directory is created in ``/tmp`` to store the reports. +- **-h** *(or no arguments)*: Display all ``scan-build`` options. +- **-k**, **--keep-going**: Add a "keep on going" option to the specified build command. This option currently supports ``make`` and ``xcodebuild``. This is a convenience option; one can specify this behavior directly using build options. +- **-v**: Verbose output from scan-build and the analyzer. **A second and third "-v" increases verbosity**, and is useful for filing bug reports against the analyzer. +- **-V**: View analysis results in a web browser when the build command completes. +- **--use-analyzer Xcode** *(or)* **--use-analyzer [path to clang]**: ``scan-build`` uses the 'clang' executable relative to itself for static analysis. One can override this behavior with this option by using the 'clang' packaged with Xcode (on OS X) or from the PATH. + +A complete list of options can be obtained by running ``scan-build`` with no arguments. + +Output of scan-build +~~~~~~~~~~~~~~~~~~~~ + +The output of scan-build is a set of HTML files, each one which represents a separate bug report. A single ``index.html`` file is generated for surveying all of the bugs. You can then just open ``index.html`` in a web browser to view the bug reports. + +Where the HTML files are generated is specified with a **-o** option to ``scan-build``. If **-o** isn't specified, a directory in ``/tmp`` is created to store the files (``scan-build`` will print a message telling you where they are). If you want to view the reports immediately after the build completes, pass **-V** to ``scan-build``. + +Recommended Usage Guidelines +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section describes a few recommendations with running the analyzer. + +Always Analyze a Project in its "Debug" Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Most projects can be built in a "debug" mode that enables assertions. Assertions are picked up by the static analyzer to prune infeasible paths, which in some cases can greatly reduce the number of false positives (bogus error reports) emitted by the tool. + +Another option is to use ``--force-analyze-debug-code`` flag of **scan-build** tool which would enable assertions automatically. + +Use Verbose Output when Debugging scan-build +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``scan-build`` takes a **-v** option to emit verbose output about what it's doing; two **-v** options emit more information. Redirecting the output of ``scan-build`` to a text file (make sure to redirect standard error) is useful for filing bug reports against ``scan-build`` or the analyzer, as we can see the exact options (and files) passed to the analyzer. For more comprehensible logs, don't perform a parallel build. + +Run './configure' through scan-build +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If an analyzed project uses an autoconf generated ``configure`` script, you will probably need to run ``configure`` script through ``scan-build`` in order to analyze the project. + +**Example**:: + + $ scan-build ./configure + $ scan-build --keep-cc make + +The reason ``configure`` also needs to be run through ``scan-build`` is because ``scan-build`` scans your source files by *interposing* on the compiler. This interposition is currently done by ``scan-build`` temporarily setting the environment variable ``CC`` to ``ccc-analyzer``. The program ``ccc-analyzer`` acts like a fake compiler, forwarding its command line arguments over to the compiler to perform regular compilation and ``clang`` to perform static analysis. + +Running ``configure`` typically generates makefiles that have hardwired paths to the compiler, and by running ``configure`` through ``scan-build`` that path is set to ``ccc-analyzer``. + +Analyzing iPhone Projects +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Conceptually Xcode projects for iPhone applications are nearly the same as their cousins for desktop applications. **scan-build** can analyze these projects as well, but users often encounter problems with just building their iPhone projects from the command line because there are a few extra preparative steps they need to take (e.g., setup code signing). + +Recommendation: use "Build and Analyze" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The absolute easiest way to analyze iPhone projects is to use the `Analyze feature in Xcode `_ (which is based on the static analyzer). There a user can analyze their project right from a menu without most of the setup described later. + +`Instructions are available <../xcode.html>`_ on this website on how to use open source builds of the analyzer as a replacement for the one bundled with Xcode. + +Using scan-build directly +~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you wish to use **scan-build** with your iPhone project, keep the following things in mind: + +- Analyze your project in the ``Debug`` configuration, either by setting this as your configuration with Xcode or by passing ``-configuration Debug`` to ``xcodebuild``. +- Analyze your project using the ``Simulator`` as your base SDK. It is possible to analyze your code when targeting the device, but this is much easier to do when using Xcode's *Build and Analyze* feature. +- Check that your code signing SDK is set to the simulator SDK as well, and make sure this option is set to ``Don't Code Sign``. + +Note that you can most of this without actually modifying your project. For example, if your application targets iPhoneOS 2.2, you could run **scan-build** in the following manner from the command line:: + + $ scan-build xcodebuild -configuration Debug -sdk iphonesimulator2.2 + +Alternatively, if your application targets iPhoneOS 3.0:: + + $ scan-build xcodebuild -configuration Debug -sdk iphonesimulator3.0 + +Gotcha: using the right compiler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Recall that **scan-build** analyzes your project by using a compiler to compile the project and ``clang`` to analyze your project. The script uses simple heuristics to determine which compiler should be used (it defaults to ``clang`` on Darwin and ``gcc`` on other platforms). When analyzing iPhone projects, **scan-build** may pick the wrong compiler than the one Xcode would use to build your project. For example, this could be because multiple versions of a compiler may be installed on your system, especially if you are developing for the iPhone. + +When compiling your application to run on the simulator, it is important that **scan-build** finds the correct version of ``gcc/clang``. Otherwise, you may see strange build errors that only happen when you run ``scan-build``. + +**scan-build** provides the ``--use-cc`` and ``--use-c++`` options to hardwire which compiler scan-build should use for building your code. Note that although you are chiefly interested in analyzing your project, keep in mind that running the analyzer is intimately tied to the build, and not being able to compile your code means it won't get fully analyzed (if at all). + +If you aren't certain which compiler Xcode uses to build your project, try just running ``xcodebuild`` (without **scan-build**). You should see the full path to the compiler that Xcode is using, and use that as an argument to ``--use-cc``. + +CodeChecker +----------- + +Basic Usage +~~~~~~~~~~~ + +Install CodeChecker as described here: `CodeChecker Install Guide `_. + +Create a compilation database. If you use cmake then pass the ``-DCMAKE_EXPORT_COMPILE_COMMANDS=1`` parameter to cmake. Cmake will create a ``compile_commands.json`` file. +If you have a Makefile based or similar build system then you can log the build commands with the help of CodeChecker:: + + make clean + CodeChecker log -b "make" -o compile_commands.json + +Analyze your project:: + + CodeChecker analyze compile_commands.json -o ./reports + +View the analysis results. +Print the detailed results in the command line:: + + CodeChecker parse --print-steps ./reports + +Or view the detailed results in a browser:: + + CodeChecker parse ./reports -e html -o ./reports_html + firefox ./reports_html/index.html + +Optional: store the analysis results in a DB:: + + mkdir ./ws + CodeChecker server -w ./ws -v 8555 & + CodeChecker store ./reports --name my-project --url http://localhost:8555/Default + +Optional: manage (categorize, suppress) the results in your web browser:: + + firefox http://localhost:8555/Default + +Detailed Usage +~~~~~~~~~~~~~~ + +For extended documentation please refer to the `official site of CodeChecker `_! + diff --git a/clang/docs/analyzer/user-docs/FilingBugs.rst b/clang/docs/analyzer/user-docs/FilingBugs.rst new file mode 100644 index 0000000000000..4fd7100b38d42 --- /dev/null +++ b/clang/docs/analyzer/user-docs/FilingBugs.rst @@ -0,0 +1,18 @@ +Filing Bugs and Feature Requests +================================ + +We encourage users to file bug reports for any problems that they encounter. +We also welcome feature requests. When filing a bug report, please do the +following: + +- Include the checker build (for prebuilt Mac OS X binaries) or the git hash. + +- Provide a self-contained, reduced test case that exhibits the issue you are + experiencing. + +- Test cases don't tell us everything. Please briefly describe the problem you + are seeing, including what you thought should have been the expected behavior + and why. + +Please `file bugs and feature requests `_ +in `LLVM's issue tracker `_ and label the report with the ``clang:static analyzer`` label. diff --git a/clang/docs/analyzer/user-docs/Installation.rst b/clang/docs/analyzer/user-docs/Installation.rst new file mode 100644 index 0000000000000..f1656fc80c2e3 --- /dev/null +++ b/clang/docs/analyzer/user-docs/Installation.rst @@ -0,0 +1,37 @@ +Obtaining the Static Analyzer +============================= + +This page describes how to download and install the analyzer. Once the analyzer is installed, follow the :doc:`CommandLineUsage` on using the command line to get started analyzing your code. + +.. contents:: + :local: + + +Building the Analyzer from Source +--------------------------------- + +Currently there are no officially supported binary distributions for the static analyzer. +You must build Clang and LLVM manually. +To do so, please follow the instructions for `building Clang from source code `_. + +Once the Clang is built, you need to add the location of the ``clang`` binary and the locations of the command line utilities (`CodeChecker` or ``scan-build`` and ``scan-view``) to you PATH for :doc:`CommandLineUsage`. + +[Legacy] Packaged Builds (Mac OS X) +----------------------------------- + +Semi-regular pre-built binaries of the analyzer used to be available on Mac OS X. These were built to run on OS X 10.7 and later. + +For older builds for MacOS visit https://clang-analyzer.llvm.org/release_notes.html. + +Packaged builds for other platforms may eventually be provided, but we need volunteers who are willing to help provide such regular builds. If you wish to help contribute regular builds of the analyzer on other platforms, please get in touch via `LLVM Discourse `_. + +[Legacy] Using Packaged Builds +------------------------------ + +To use the legacy pacakge builds, simply unpack it anywhere. If the build archive has the name **``checker-XXX.tar.bz2``** then the archive will expand to a directory called **``checker-XXX``**. You do not need to place this directory or the contents of this directory in any special place. Uninstalling the analyzer is as simple as deleting this directory. + +Most of the files in the **``checker-XXX``** directory will be supporting files for the analyzer that you can simply ignore. Most users will only care about two files, which are located at the top of the **``checker-XXX``** directory: + +* **scan-build**: ``scan-build`` is the high-level command line utility for running the analyzer +* **scan-view**: ``scan-view`` a companion command line utility to ``scan-build``, ``scan-view`` is used to view analysis results generated by ``scan-build``. There is an option that one can pass to ``scan-build`` to cause ``scan-view`` to run as soon as it the analysis of a build completes + diff --git a/clang/docs/analyzer/user-docs/UsingWithXCode.rst b/clang/docs/analyzer/user-docs/UsingWithXCode.rst new file mode 100644 index 0000000000000..d4f76afa9fb9a --- /dev/null +++ b/clang/docs/analyzer/user-docs/UsingWithXCode.rst @@ -0,0 +1,88 @@ +Running the analyzer within Xcode +================================= + +.. contents:: + :local: + +Since Xcode 3.2, users have been able to run the static analyzer `directly within Xcode `_. + +It integrates directly with the Xcode build system and presents analysis results directly within Xcode's editor. + +Can I use the open source analyzer builds with Xcode? +----------------------------------------------------- + +**Yes**. Instructions are included below. + +.. image:: ../images/analyzer_xcode.png + +**Viewing static analyzer results in Xcode** + +Key features: +------------- + +- **Integrated workflow:** Results are integrated within Xcode. There is no experience of using a separate tool, and activating the analyzer requires a single keystroke or mouse click. +- **Transparency:** Works effortlessly with Xcode projects (including iPhone projects). +- **Cons:** Doesn't work well with non-Xcode projects. For those, consider :doc:`CommandLineUsage`. + +Getting Started +--------------- + +Xcode is available as a free download from Apple on the `Mac App Store `_, with `instructions available `_ for using the analyzer. + +Using open source analyzer builds with Xcode +-------------------------------------------- + +By default, Xcode uses the version of ``clang`` that came bundled with it to analyze your code. It is possible to change Xcode's behavior to use an alternate version of ``clang`` for this purpose while continuing to use the ``clang`` that came with Xcode for compiling projects. + +Why try open source builds? +---------------------------- + +The advantage of using open source analyzer builds (provided on this website) is that they are often newer than the analyzer provided with Xcode, and thus can contain bug fixes, new checks, or simply better analysis. + +On the other hand, new checks can be experimental, with results of variable quality. Users are encouraged to file bug reports (for any version of the analyzer) where they encounter false positives or other issues here: :doc:`FilingBugs`. + +set-xcode-analyzer +------------------ + +Starting with analyzer build checker-234, analyzer builds contain a command line utility called ``set-xcode-analyzer`` that allows users to change what copy of ``clang`` that Xcode uses for analysis:: + + $ set-xcode-analyzer -h + Usage: set-xcode-analyzer [options] + + Options: + -h, --help show this help message and exit + --use-checker-build=PATH + Use the Clang located at the provided absolute path, + e.g. /Users/foo/checker-1 + --use-xcode-clang Use the Clang bundled with Xcode + +Operationally, **set-xcode-analyzer** edits Xcode's configuration files to point it to use the version of ``clang`` you specify for static analysis. Within this model it provides you two basic modes: + +- **--use-xcode-clang:** Switch Xcode (back) to using the ``clang`` that came bundled with it for static analysis. +- **--use-checker-build:** Switch Xcode to using the ``clang`` provided by the specified analyzer build. + +Things to keep in mind +---------------------- + +- You should quit Xcode prior to running ``set-xcode-analyzer``. +- You will need to run ``set-xcode-analyzer`` under **``sudo``** in order to have write privileges to modify the Xcode configuration files. + +Examples +-------- + +**Example 1**: Telling Xcode to use checker-235:: + + $ pwd + /tmp + $ tar xjf checker-235.tar.bz2 + $ sudo checker-235/set-xcode-analyzer --use-checker-build=/tmp/checker-235 + +Note that you typically won't install an analyzer build in ``/tmp``, but the point of this example is that ``set-xcode-analyzer`` just wants a full path to an untarred analyzer build. + +**Example 2**: Telling Xcode to use a very specific version of ``clang``:: + + $ sudo set-xcode-analyzer --use-checker-build=~/mycrazyclangbuild/bin/clang + +**Example 3**: Resetting Xcode to its default behavior:: + + $ sudo set-xcode-analyzer --use-xcode-clang diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt index 4866bd4aee634..a8ee8f1fcb87c 100644 --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -5357,7 +5357,6 @@ llvm/include/llvm/MC/MCAsmInfoELF.h llvm/include/llvm/MC/MCAsmInfoGOFF.h llvm/include/llvm/MC/MCAsmInfoWasm.h llvm/include/llvm/MC/MCAsmInfoXCOFF.h -llvm/include/llvm/MC/MCAsmLayout.h llvm/include/llvm/MC/MCCodeView.h llvm/include/llvm/MC/MCContext.h llvm/include/llvm/MC/MCFixedLenDisassembler.h diff --git a/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.cpp b/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.cpp index 6cb87e4b5e19c..79a315d5a4ee2 100644 --- a/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.cpp +++ b/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.cpp @@ -18,6 +18,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Sema/Sema.h" +#include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/Passes/OptimizationLevel.h" #include "llvm/Passes/PassBuilder.h" diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h index ebcd8059284d8..447f2592d2359 100644 --- a/clang/include/clang/AST/ASTConsumer.h +++ b/clang/include/clang/AST/ASTConsumer.h @@ -23,6 +23,7 @@ namespace clang { class ASTDeserializationListener; // layering violation because void* is ugly class SemaConsumer; // layering violation required for safe SemaConsumer class TagDecl; + class DeclaratorDecl; class VarDecl; class FunctionDecl; class ImportDecl; @@ -105,7 +106,7 @@ class ASTConsumer { /// CompleteExternalDeclaration - Callback invoked at the end of a translation /// unit to notify the consumer that the given external declaration should be /// completed. - virtual void CompleteExternalDeclaration(VarDecl *D) {} + virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {} /// Callback invoked when an MSInheritanceAttr has been attached to a /// CXXRecordDecl. diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index de86cb5e9d7fc..a99f2dc6eb3f2 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -37,6 +37,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/TypeSize.h" #include @@ -467,6 +468,14 @@ class ASTContext : public RefCountedBase { /// This is the top-level (C++20) Named module we are building. Module *CurrentCXXNamedModule = nullptr; + /// Help structures to decide whether two `const Module *` belongs + /// to the same conceptual module to avoid the expensive to string comparison + /// if possible. + /// + /// Not serialized intentionally. + llvm::StringMap PrimaryModuleNameMap; + llvm::DenseMap SameModuleLookupSet; + static constexpr unsigned ConstantArrayTypesLog2InitSize = 8; static constexpr unsigned GeneralTypesLog2InitSize = 9; static constexpr unsigned FunctionProtoTypesLog2InitSize = 12; @@ -1073,6 +1082,12 @@ class ASTContext : public RefCountedBase { /// Get module under construction, nullptr if this is not a C++20 module. Module *getCurrentNamedModule() const { return CurrentCXXNamedModule; } + /// If the two module \p M1 and \p M2 are in the same module. + /// + /// FIXME: The signature may be confusing since `clang::Module` means to + /// a module fragment or a module unit but not a C++20 module. + bool isInSameModule(const Module *M1, const Module *M2); + TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl->getMostRecentDecl(); } @@ -1263,6 +1278,11 @@ class ASTContext : public RefCountedBase { /// space. QualType removeAddrSpaceQualType(QualType T) const; + /// Return the "other" discriminator used for the pointer auth schema used for + /// vtable pointers in instances of the requested type. + uint16_t + getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD); + /// Apply Objective-C protocol qualifiers to the given type. /// \param allowOnPointerType specifies if we can apply protocol /// qualifiers on ObjCObjectPointerType. It can be set to true when @@ -3424,12 +3444,21 @@ OPT_LIST(V) /// Whether a C++ static variable or CUDA/HIP kernel should be externalized. bool shouldExternalize(const Decl *D) const; + /// Resolve the root record to be used to derive the vtable pointer + /// authentication policy for the specified record. + const CXXRecordDecl * + baseForVTableAuthentication(const CXXRecordDecl *ThisClass); + bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, + StringRef MangledName); + StringRef getCUIDHash() const; private: /// All OMPTraitInfo objects live in this collection, one per /// `pragma omp [begin] declare variant` directive. SmallVector, 4> OMPTraitInfoVector; + + llvm::DenseMap> ThunksToBeAbbreviated; }; /// Insertion operator for diagnostics. diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h index ab036f1d445ac..4b627c65e276b 100644 --- a/clang/include/clang/AST/AbstractBasicReader.h +++ b/clang/include/clang/AST/AbstractBasicReader.h @@ -244,6 +244,15 @@ class DataStreamBasicReader : public BasicReaderBase { return FunctionProtoType::ExtParameterInfo::getFromOpaqueValue(value); } + FunctionEffect readFunctionEffect() { + uint32_t value = asImpl().readUInt32(); + return FunctionEffect::fromOpaqueInt32(value); + } + + EffectConditionExpr readEffectConditionExpr() { + return EffectConditionExpr{asImpl().readExprRef()}; + } + NestedNameSpecifier *readNestedNameSpecifier() { auto &ctx = getASTContext(); diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h index 8e42fcaad1d38..b941add8bde88 100644 --- a/clang/include/clang/AST/AbstractBasicWriter.h +++ b/clang/include/clang/AST/AbstractBasicWriter.h @@ -222,6 +222,14 @@ class DataStreamBasicWriter : public BasicWriterBase { asImpl().writeUInt32(epi.getOpaqueValue()); } + void writeFunctionEffect(FunctionEffect E) { + asImpl().writeUInt32(E.toOpaqueInt32()); + } + + void writeEffectConditionExpr(EffectConditionExpr CE) { + asImpl().writeExprRef(CE.getCondition()); + } + void writeNestedNameSpecifier(NestedNameSpecifier *NNS) { // Nested name specifiers usually aren't too long. I think that 8 would // typically accommodate the vast majority. diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 7fd80b90d1033..5957f14098363 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3042,6 +3042,16 @@ class FunctionDecl : public DeclaratorDecl, /// computed and stored. unsigned getODRHash() const; + FunctionEffectsRef getFunctionEffects() const { + // Effects may differ between declarations, but they should be propagated + // from old to new on any redeclaration, so it suffices to look at + // getMostRecentDecl(). + if (const auto *FPT = + getMostRecentDecl()->getType()->getAs()) + return FPT->getFunctionEffects(); + return {}; + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { @@ -4670,6 +4680,13 @@ class BlockDecl : public Decl, public DeclContext { SourceRange getSourceRange() const override LLVM_READONLY; + FunctionEffectsRef getFunctionEffects() const { + if (const TypeSourceInfo *TSI = getSignatureAsWritten()) + if (const auto *FPT = TSI->getType()->getAs()) + return FPT->getFunctionEffects(); + return {}; + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == Block; } diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 3310f57acc683..45dac82e54077 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -835,10 +835,7 @@ class alignas(8) Decl { /// Get the module that owns this declaration for linkage purposes. /// There only ever is such a standard C++ module. - /// - /// \param IgnoreLinkage Ignore the linkage of the entity; assume that - /// all declarations in a global module fragment are unowned. - Module *getOwningModuleForLinkage(bool IgnoreLinkage = false) const; + Module *getOwningModuleForLinkage() const; /// Determine whether this declaration is definitely visible to name lookup, /// independent of whether the owning module is visible. diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h index e8f4860e13f1f..e5e27389fac60 100644 --- a/clang/include/clang/AST/DeclID.h +++ b/clang/include/clang/AST/DeclID.h @@ -197,6 +197,8 @@ class LocalDeclID : public DeclIDBase { static LocalDeclID get(ASTReader &Reader, serialization::ModuleFile &MF, DeclID ID); + static LocalDeclID get(ASTReader &Reader, serialization::ModuleFile &MF, + unsigned ModuleFileIndex, unsigned LocalDeclID); LocalDeclID &operator++() { ++ID; @@ -259,11 +261,7 @@ template <> struct DenseMapInfo { } static unsigned getHashValue(const GlobalDeclID &Key) { - // Our default hash algorithm for 64 bits integer may not be very good. - // In GlobalDeclID's case, it is pretty common that the lower 32 bits can - // be same. - // FIXME: Remove this when we fix the underlying issue. - return llvm::hash_value(Key.getRawValue()); + return DenseMapInfo::getHashValue(Key.getRawValue()); } static bool isEqual(const GlobalDeclID &L, const GlobalDeclID &R) { diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 3bc8cae4d8c86..a8add9d1337c6 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -384,7 +384,7 @@ class Expr : public ValueStmt { bool isRValue() const { return Kind >= CL_XValue; } bool isModifiable() const { return getModifiable() == CM_Modifiable; } - /// Create a simple, modifiably lvalue + /// Create a simple, modifiable lvalue static Classification makeSimpleLValue() { return Classification(CL_LValue, CM_Modifiable); } @@ -787,6 +787,11 @@ class Expr : public ValueStmt { const Expr *PtrExpression, ASTContext &Ctx, EvalResult &Status) const; + /// If the current Expr can be evaluated to a pointer to a null-terminated + /// constant string, return the constant string (without the terminating + /// null). + std::optional tryEvaluateString(ASTContext &Ctx) const; + /// Enumeration used to describe the kind of Null pointer constant /// returned from \c isNullPointerConstant(). enum NullPointerConstantKind { @@ -1287,7 +1292,7 @@ class DeclRefExpr final DeclRefExpr(const ASTContext &Ctx, NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, ValueDecl *D, - bool RefersToEnlosingVariableOrCapture, + bool RefersToEnclosingVariableOrCapture, const DeclarationNameInfo &NameInfo, NamedDecl *FoundD, const TemplateArgumentListInfo *TemplateArgs, QualType T, ExprValueKind VK, NonOdrUseReason NOUR); @@ -1653,14 +1658,14 @@ class FloatingLiteral : public Expr, private APFloatStorage { } /// Get a raw enumeration value representing the floating-point semantics of - /// this literal (32-bit IEEE, x87, ...), suitable for serialisation. + /// this literal (32-bit IEEE, x87, ...), suitable for serialization. llvm::APFloatBase::Semantics getRawSemantics() const { return static_cast( FloatingLiteralBits.Semantics); } /// Set the raw enumeration value representing the floating-point semantics of - /// this literal (32-bit IEEE, x87, ...), suitable for serialisation. + /// this literal (32-bit IEEE, x87, ...), suitable for serialization. void setRawSemantics(llvm::APFloatBase::Semantics Sem) { FloatingLiteralBits.Semantics = Sem; } @@ -2125,7 +2130,7 @@ class SYCLUniqueStableNameExpr final : public Expr { static std::string ComputeName(ASTContext &Context, QualType Ty); }; -/// ParenExpr - This represents a parethesized expression, e.g. "(1)". This +/// ParenExpr - This represents a parenthesized expression, e.g. "(1)". This /// AST node is only formed if full location information is requested. class ParenExpr : public Expr { SourceLocation L, R; @@ -2241,7 +2246,7 @@ class UnaryOperator final bool canOverflow() const { return UnaryOperatorBits.CanOverflow; } void setCanOverflow(bool C) { UnaryOperatorBits.CanOverflow = C; } - /// Get the FP contractability status of this operator. Only meaningful for + /// Get the FP contractibility status of this operator. Only meaningful for /// operations on floating point types. bool isFPContractableWithinStatement(const LangOptions &LO) const { return getFPFeaturesInEffect(LO).allowFPContractWithinStatement(); @@ -4049,7 +4054,7 @@ class BinaryOperator : public Expr { return FPOptionsOverride(); } - /// Get the FP contractability status of this operator. Only meaningful for + /// Get the FP contractibility status of this operator. Only meaningful for /// operations on floating point types. bool isFPContractableWithinStatement(const LangOptions &LO) const { return getFPFeaturesInEffect(LO).allowFPContractWithinStatement(); @@ -4290,7 +4295,7 @@ class BinaryConditionalOperator : public AbstractConditionalOperator { } /// getFalseExpr - Return the subexpression which will be - /// evaluated if the condnition evaluates to false; this is + /// evaluated if the condition evaluates to false; this is /// defined in terms of the opaque value. Expr *getFalseExpr() const { return cast(SubExprs[RHS]); @@ -5998,7 +6003,7 @@ class GenericSelectionExpr final // if *It1 and *It2 are bound to the same objects. // An alternative design approach was discussed during review; // store an Association object inside the iterator, and return a reference - // to it when dereferenced. This idea was discarded beacuse of nasty + // to it when dereferenced. This idea was discarded because of nasty // lifetime issues: // AssociationIterator It = ...; // const Association &Assoc = *It++; // Oops, Assoc is dangling. diff --git a/clang/include/clang/AST/GlobalDecl.h b/clang/include/clang/AST/GlobalDecl.h index 88abba28c991d..386693cabb1fb 100644 --- a/clang/include/clang/AST/GlobalDecl.h +++ b/clang/include/clang/AST/GlobalDecl.h @@ -145,6 +145,10 @@ class GlobalDecl { LHS.MultiVersionIndex == RHS.MultiVersionIndex; } + bool operator!=(const GlobalDecl &Other) const { + return !(*this == Other); + } + void *getAsOpaquePtr() const { return Value.getOpaqueValue(); } explicit operator bool() const { return getAsOpaquePtr(); } diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index e586b0cec43df..d5f6c0f6cc67d 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -130,15 +130,15 @@ class MangleContext { // FIXME: consider replacing raw_ostream & with something like SmallString &. void mangleName(GlobalDecl GD, raw_ostream &); virtual void mangleCXXName(GlobalDecl GD, raw_ostream &) = 0; - virtual void mangleThunk(const CXXMethodDecl *MD, - const ThunkInfo &Thunk, - raw_ostream &) = 0; + virtual void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, + bool ElideOverrideInfo, raw_ostream &) = 0; virtual void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, - const ThisAdjustment &ThisAdjustment, - raw_ostream &) = 0; + const ThunkInfo &Thunk, + bool ElideOverrideInfo, raw_ostream &) = 0; virtual void mangleReferenceTemporary(const VarDecl *D, unsigned ManglingNumber, raw_ostream &) = 0; + virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0; virtual void mangleCXXRTTI(QualType T, raw_ostream &) = 0; virtual void mangleCXXRTTIName(QualType T, raw_ostream &, bool NormalizeIntegers = false) = 0; @@ -192,7 +192,6 @@ class ItaniumMangleContext : public MangleContext { bool IsAux = false) : MangleContext(C, D, MK_Itanium, IsAux) {} - virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0; virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) = 0; virtual void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset, const CXXRecordDecl *Type, diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 6df1d93a7ba2e..5f7d619518762 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -117,6 +117,8 @@ def ExtParameterInfo : PropertyType<"FunctionProtoType::ExtParameterInfo">; def FixedPointSemantics : PropertyType<"llvm::FixedPointSemantics"> { let PassByReference = 1; } +def FunctionEffect : PropertyType<"FunctionEffect">; +def EffectConditionExpr : PropertyType<"EffectConditionExpr">; def Identifier : RefPropertyType<"IdentifierInfo"> { let ConstWhenWriting = 1; } def LValuePathEntry : PropertyType<"APValue::LValuePathEntry">; def LValuePathSerializationHelper : diff --git a/clang/include/clang/AST/Redeclarable.h b/clang/include/clang/AST/Redeclarable.h index 091bb886f2d49..74ccd74ed60d6 100644 --- a/clang/include/clang/AST/Redeclarable.h +++ b/clang/include/clang/AST/Redeclarable.h @@ -281,10 +281,10 @@ class Redeclarable { return tmp; } - friend bool operator==(redecl_iterator x, redecl_iterator y) { + friend bool operator==(const redecl_iterator &x, const redecl_iterator &y) { return x.Current == y.Current; } - friend bool operator!=(redecl_iterator x, redecl_iterator y) { + friend bool operator!=(const redecl_iterator &x, const redecl_iterator &y) { return x.Current != y.Current; } }; diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index 24a7fde76195d..e3b7dd261535d 100644 --- a/clang/include/clang/AST/TemplateName.h +++ b/clang/include/clang/AST/TemplateName.h @@ -198,7 +198,8 @@ class SubstTemplateTemplateParmPackStorage : public UncommonTemplateNameStorage, /// /// Here, "apply" is treated as a template name within the typename /// specifier in the typedef. "apply" is a nested template, and can -/// only be understood in the context of +/// only be understood in the context of a template instantiation, +/// hence is represented as a dependent template name. class TemplateName { // NameDecl is either a TemplateDecl or a UsingShadowDecl depending on the // NameKind. diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 61246479188e9..62836ec5c6312 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -118,6 +118,7 @@ class EnumDecl; class Expr; class ExtQualsTypeCommonBase; class FunctionDecl; +class FunctionEffectSet; class IdentifierInfo; class NamedDecl; class ObjCInterfaceDecl; @@ -131,6 +132,7 @@ class TemplateArgument; class TemplateArgumentListInfo; class TemplateArgumentLoc; class TemplateTypeParmDecl; +template class TreeTransform; class TypedefNameDecl; class UnresolvedUsingTypenameDecl; class UsingShadowDecl; @@ -4524,8 +4526,13 @@ class FunctionType : public Type { LLVM_PREFERRED_TYPE(bool) unsigned HasArmTypeAttributes : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned EffectsHaveConditions : 1; + unsigned NumFunctionEffects : 4; + FunctionTypeExtraBitfields() - : NumExceptionType(0), HasArmTypeAttributes(false) {} + : NumExceptionType(0), HasArmTypeAttributes(false), + EffectsHaveConditions(false), NumFunctionEffects(0) {} }; /// The AArch64 SME ACLE (Arm C/C++ Language Extensions) define a number @@ -4658,6 +4665,296 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// ------------------------------------------------------------------------------ + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { + None = 0, + NonBlocking = 1, + NonAllocating = 2, + Blocking = 3, + Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { + // Can verification inspect callees' implementations? (e.g. nonblocking: + // yes, tcb+types: no). This also implies the need for 2nd-pass + // verification. + FE_InferrableOnCallees = 0x1, + + // Language constructs which effects can diagnose as disallowed. + FE_ExcludeThrow = 0x2, + FE_ExcludeCatch = 0x4, + FE_ExcludeObjCMessageSend = 0x8, + FE_ExcludeStaticLocalVars = 0x10, + FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { + return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { + switch (kind()) { + case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; + case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars. + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; + case Kind::Blocking: + case Kind::Allocating: + return 0; + case Kind::None: + break; + } + llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl &Callee) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect &LHS, const FunctionEffect &RHS) { + return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect &LHS, const FunctionEffect &RHS) { + return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect &LHS, const FunctionEffect &RHS) { + return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class EffectConditionExpr { + Expr *Cond = nullptr; // if null, unconditional. + +public: + EffectConditionExpr() = default; + EffectConditionExpr(Expr *E) : Cond(E) {} + + Expr *getCondition() const { return Cond; } + + bool operator==(const EffectConditionExpr &RHS) const { + return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + EffectConditionExpr Cond; + + FunctionEffectWithCondition() = default; + FunctionEffectWithCondition(const FunctionEffect &E, + const EffectConditionExpr &C) + : Effect(E), Cond(C) {} + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; +}; + +/// Support iteration in parallel through a pair of FunctionEffect and +/// EffectConditionExpr containers. +template class FunctionEffectIterator { + friend Container; + + const Container *Outer = nullptr; + size_t Idx = 0; + +public: + FunctionEffectIterator(); + FunctionEffectIterator(const Container &O, size_t I) : Outer(&O), Idx(I) {} + bool operator==(const FunctionEffectIterator &Other) const { + return Idx == Other.Idx; + } + bool operator!=(const FunctionEffectIterator &Other) const { + return Idx != Other.Idx; + } + + FunctionEffectIterator operator++() { + ++Idx; + return *this; + } + + FunctionEffectWithCondition operator*() const { + assert(Outer != nullptr && "invalid FunctionEffectIterator"); + bool HasConds = !Outer->Conditions.empty(); + return FunctionEffectWithCondition{Outer->Effects[Idx], + HasConds ? Outer->Conditions[Idx] + : EffectConditionExpr()}; + } +}; + +/// An immutable set of FunctionEffects and possibly conditions attached to +/// them. The effects and conditions reside in memory not managed by this object +/// (typically, trailing objects in FunctionProtoType, or borrowed references +/// from a FunctionEffectSet). +/// +/// Invariants: +/// - there is never more than one instance of any given effect. +/// - the array of conditions is either empty or has the same size as the +/// array of effects. +/// - some conditions may be null expressions; each condition pertains to +/// the effect at the same array index. +/// +/// Also, if there are any conditions, at least one of those expressions will be +/// dependent, but this is only asserted in the constructor of +/// FunctionProtoType. +/// +/// See also FunctionEffectSet, in Sema, which provides a mutable set. +class FunctionEffectsRef { + // Restrict classes which can call the private constructor -- these friends + // all maintain the required invariants. FunctionEffectSet is generally the + // only way in which the arrays are created; FunctionProtoType will not + // reorder them. + friend FunctionProtoType; + friend FunctionEffectSet; + + ArrayRef Effects; + ArrayRef Conditions; + + // The arrays are expected to have been sorted by the caller, with the + // effects in order. The conditions array must be empty or the same size + // as the effects array, since the conditions are associated with the effects + // at the same array indices. + FunctionEffectsRef(ArrayRef FX, + ArrayRef Conds) + : Effects(FX), Conditions(Conds) {} + +public: + /// Extract the effects from a Type if it is a function, block, or member + /// function pointer, or a reference or pointer to one. + static FunctionEffectsRef get(QualType QT); + + /// Asserts invariants. + static FunctionEffectsRef create(ArrayRef FX, + ArrayRef Conds); + + FunctionEffectsRef() = default; + + bool empty() const { return Effects.empty(); } + size_t size() const { return Effects.size(); } + + ArrayRef effects() const { return Effects; } + ArrayRef conditions() const { return Conditions; } + + using iterator = FunctionEffectIterator; + friend iterator; + iterator begin() const { return iterator(*this, 0); } + iterator end() const { return iterator(*this, size()); } + + friend bool operator==(const FunctionEffectsRef &LHS, + const FunctionEffectsRef &RHS) { + return LHS.Effects == RHS.Effects && LHS.Conditions == RHS.Conditions; + } + friend bool operator!=(const FunctionEffectsRef &LHS, + const FunctionEffectsRef &RHS) { + return !(LHS == RHS); + } + + void Profile(llvm::FoldingSetNodeID &ID) const; + void dump(llvm::raw_ostream &OS) const; +}; + +/// A mutable set of FunctionEffects and possibly conditions attached to them. +/// Used to compare and merge effects on declarations. +/// +/// Has the same invariants as FunctionEffectsRef. +class FunctionEffectSet { + SmallVector Effects; + SmallVector Conditions; + +public: + FunctionEffectSet() = default; + + explicit FunctionEffectSet(const FunctionEffectsRef &FX) + : Effects(FX.effects()), Conditions(FX.conditions()) {} + + bool empty() const { return Effects.empty(); } + size_t size() const { return Effects.size(); } + + using iterator = FunctionEffectIterator; + friend iterator; + iterator begin() const { return iterator(*this, 0); } + iterator end() const { return iterator(*this, size()); } + + operator FunctionEffectsRef() const { return {Effects, Conditions}; } + + void dump(llvm::raw_ostream &OS) const; + + // Mutators + + // On insertion, a conflict occurs when attempting to insert an + // effect which is opposite an effect already in the set, or attempting + // to insert an effect which is already in the set but with a condition + // which is not identical. + struct Conflict { + FunctionEffectWithCondition Kept; + FunctionEffectWithCondition Rejected; + }; + using Conflicts = SmallVector; + + // Returns true for success (obviating a check of Errs.empty()). + bool insert(const FunctionEffectWithCondition &NewEC, Conflicts &Errs); + + // Returns true for success (obviating a check of Errs.empty()). + bool insert(const FunctionEffectsRef &Set, Conflicts &Errs); + + // Set operations + + static FunctionEffectSet getUnion(FunctionEffectsRef LHS, + FunctionEffectsRef RHS, Conflicts &Errs); + static FunctionEffectSet getIntersection(FunctionEffectsRef LHS, + FunctionEffectsRef RHS); +}; + /// Represents a prototype with parameter type info, e.g. /// 'int foo(int)' or 'int foo(void)'. 'void' is represented as having no /// parameters, not as having a single void parameter. Such a type can have @@ -4672,7 +4969,8 @@ class FunctionProtoType final FunctionProtoType, QualType, SourceLocation, FunctionType::FunctionTypeExtraBitfields, FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType, - Expr *, FunctionDecl *, FunctionType::ExtParameterInfo, Qualifiers> { + Expr *, FunctionDecl *, FunctionType::ExtParameterInfo, + FunctionEffect, EffectConditionExpr, Qualifiers> { friend class ASTContext; // ASTContext creates these. friend TrailingObjects; @@ -4703,9 +5001,15 @@ class FunctionProtoType final // an ExtParameterInfo for each of the parameters. Present if and // only if hasExtParameterInfos() is true. // + // * Optionally, an array of getNumFunctionEffects() FunctionEffect. + // Present only when getNumFunctionEffects() > 0 + // + // * Optionally, an array of getNumFunctionEffects() EffectConditionExpr. + // Present only when getNumFunctionEffectConditions() > 0. + // // * Optionally a Qualifiers object to represent extra qualifiers that can't - // be represented by FunctionTypeBitfields.FastTypeQuals. Present if and only - // if hasExtQualifiers() is true. + // be represented by FunctionTypeBitfields.FastTypeQuals. Present if and + // only if hasExtQualifiers() is true. // // The optional FunctionTypeExtraBitfields has to be before the data // related to the exception specification since it contains the number @@ -4761,6 +5065,7 @@ class FunctionProtoType final ExceptionSpecInfo ExceptionSpec; const ExtParameterInfo *ExtParameterInfos = nullptr; SourceLocation EllipsisLoc; + FunctionEffectsRef FunctionEffects; ExtProtoInfo() : Variadic(false), HasTrailingReturn(false), @@ -4778,7 +5083,8 @@ class FunctionProtoType final bool requiresFunctionProtoTypeExtraBitfields() const { return ExceptionSpec.Type == EST_Dynamic || - requiresFunctionProtoTypeArmAttributes(); + requiresFunctionProtoTypeArmAttributes() || + !FunctionEffects.empty(); } bool requiresFunctionProtoTypeArmAttributes() const { @@ -4826,6 +5132,14 @@ class FunctionProtoType final return hasExtParameterInfos() ? getNumParams() : 0; } + unsigned numTrailingObjects(OverloadToken) const { + return getNumFunctionEffects(); + } + + unsigned numTrailingObjects(OverloadToken) const { + return getNumFunctionEffectConditions(); + } + /// Determine whether there are any argument types that /// contain an unexpanded parameter pack. static bool containsAnyUnexpandedParameterPack(const QualType *ArgArray, @@ -4927,6 +5241,7 @@ class FunctionProtoType final EPI.RefQualifier = getRefQualifier(); EPI.ExtParameterInfos = getExtParameterInfosOrNull(); EPI.AArch64SMEAttributes = getAArch64SMEAttributes(); + EPI.FunctionEffects = getFunctionEffects(); return EPI; } @@ -5138,6 +5453,62 @@ class FunctionProtoType final return false; } + unsigned getNumFunctionEffects() const { + return hasExtraBitfields() + ? getTrailingObjects() + ->NumFunctionEffects + : 0; + } + + // For serialization. + ArrayRef getFunctionEffectsWithoutConditions() const { + if (hasExtraBitfields()) { + const auto *Bitfields = getTrailingObjects(); + if (Bitfields->NumFunctionEffects > 0) + return {getTrailingObjects(), + Bitfields->NumFunctionEffects}; + } + return {}; + } + + unsigned getNumFunctionEffectConditions() const { + if (hasExtraBitfields()) { + const auto *Bitfields = getTrailingObjects(); + if (Bitfields->EffectsHaveConditions) + return Bitfields->NumFunctionEffects; + } + return 0; + } + + // For serialization. + ArrayRef getFunctionEffectConditions() const { + if (hasExtraBitfields()) { + const auto *Bitfields = getTrailingObjects(); + if (Bitfields->EffectsHaveConditions) + return {getTrailingObjects(), + Bitfields->NumFunctionEffects}; + } + return {}; + } + + // Combines effects with their conditions. + FunctionEffectsRef getFunctionEffects() const { + if (hasExtraBitfields()) { + const auto *Bitfields = getTrailingObjects(); + if (Bitfields->NumFunctionEffects > 0) { + const size_t NumConds = Bitfields->EffectsHaveConditions + ? Bitfields->NumFunctionEffects + : 0; + return FunctionEffectsRef( + {getTrailingObjects(), + Bitfields->NumFunctionEffects}, + {NumConds ? getTrailingObjects() : nullptr, + NumConds}); + } + } + return {}; + } + bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index aba14b222a03a..7d4353c2773a3 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -352,6 +352,12 @@ let Class = FunctionProtoType in { def : Property<"AArch64SMEAttributes", UInt32> { let Read = [{ node->getAArch64SMEAttributes() }]; } + def : Property<"functionEffects", Array> { + let Read = [{ node->getFunctionEffectsWithoutConditions() }]; + } + def : Property<"functionEffectConds", Array> { + let Read = [{ node->getFunctionEffectConditions() }]; + } def : Creator<[{ auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm, @@ -368,6 +374,7 @@ let Class = FunctionProtoType in { epi.ExtParameterInfos = extParameterInfo.empty() ? nullptr : extParameterInfo.data(); epi.AArch64SMEAttributes = AArch64SMEAttributes; + epi.FunctionEffects = FunctionEffectsRef::create(functionEffects, functionEffectConds); return ctx.getFunctionType(returnType, parameters, epi); }]>; } diff --git a/clang/include/clang/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h index fbf6c041a1ec1..a5de41dbc22f1 100644 --- a/clang/include/clang/AST/VTableBuilder.h +++ b/clang/include/clang/AST/VTableBuilder.h @@ -361,6 +361,10 @@ class VTableContextBase { }; class ItaniumVTableContext : public VTableContextBase { +public: + typedef llvm::DenseMap + OriginalMethodMapTy; + private: /// Contains the index (relative to the vtable address point) @@ -384,6 +388,10 @@ class ItaniumVTableContext : public VTableContextBase { VirtualBaseClassOffsetOffsetsMapTy; VirtualBaseClassOffsetOffsetsMapTy VirtualBaseClassOffsetOffsets; + /// Map from a virtual method to the nearest method in the primary base class + /// chain that it overrides. + OriginalMethodMapTy OriginalMethodMap; + void computeVTableRelatedInformation(const CXXRecordDecl *RD) override; public: @@ -425,6 +433,27 @@ class ItaniumVTableContext : public VTableContextBase { CharUnits getVirtualBaseOffsetOffset(const CXXRecordDecl *RD, const CXXRecordDecl *VBase); + /// Return the method that added the v-table slot that will be used to call + /// the given method. + /// + /// In the Itanium ABI, where overrides always cause methods to be added to + /// the primary v-table if they're not already there, this will be the first + /// declaration in the primary base class chain for which the return type + /// adjustment is trivial. + GlobalDecl findOriginalMethod(GlobalDecl GD); + + const CXXMethodDecl *findOriginalMethodInMap(const CXXMethodDecl *MD) const; + + void setOriginalMethod(const CXXMethodDecl *Key, const CXXMethodDecl *Val) { + OriginalMethodMap[Key] = Val; + } + + /// This method is reserved for the implementation and shouldn't be used + /// directly. + const OriginalMethodMapTy &getOriginalMethodMap() { + return OriginalMethodMap; + } + static bool classof(const VTableContextBase *VT) { return !VT->isMicrosoft(); } diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h index 763af24454764..50a7018872061 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -178,13 +178,50 @@ template struct DataflowAnalysisState { Environment Env; }; +/// A callback to be called with the state before or after visiting a CFG +/// element. +template +using CFGEltCallback = std::function &)>; + +/// A pair of callbacks to be called with the state before and after visiting a +/// CFG element. +/// Either or both of the callbacks may be null. +template struct CFGEltCallbacks { + CFGEltCallback Before; + CFGEltCallback After; +}; + +/// A callback for performing diagnosis on a CFG element, called with the state +/// before or after visiting that CFG element. Returns a list of diagnostics +/// to emit (if any). +template +using DiagnosisCallback = llvm::function_ref( + const CFGElement &, ASTContext &, + const TransferStateForDiagnostics &)>; + +/// A pair of callbacks for performing diagnosis on a CFG element, called with +/// the state before and after visiting that CFG element. +/// Either or both of the callbacks may be null. +template struct DiagnosisCallbacks { + DiagnosisCallback Before; + DiagnosisCallback After; +}; + +/// Default for the maximum number of SAT solver iterations during analysis. +inline constexpr std::int64_t kDefaultMaxSATIterations = 1'000'000'000; + +/// Default for the maximum number of block visits during analysis. +inline constexpr std::int32_t kDefaultMaxBlockVisits = 20'000; + /// Performs dataflow analysis and returns a mapping from basic block IDs to /// dataflow analysis states that model the respective basic blocks. The /// returned vector, if any, will have the same size as the number of CFG /// blocks, with indices corresponding to basic block IDs. Returns an error if /// the dataflow analysis cannot be performed successfully. Otherwise, calls -/// `PostVisitCFG` on each CFG element with the final analysis results at that -/// program point. +/// `PostAnalysisCallbacks` on each CFG element with the final analysis results +/// before and after that program point. /// /// `MaxBlockVisits` caps the number of block visits during analysis. See /// `runTypeErasedDataflowAnalysis` for a full description. The default value is @@ -194,30 +231,40 @@ template struct DataflowAnalysisState { template llvm::Expected>>> -runDataflowAnalysis( - const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv, - std::function &)> - PostVisitCFG = nullptr, - std::int32_t MaxBlockVisits = 20'000) { - std::function - PostVisitCFGClosure = nullptr; - if (PostVisitCFG) { - PostVisitCFGClosure = [&PostVisitCFG]( - const CFGElement &Element, - const TypeErasedDataflowAnalysisState &State) { - auto *Lattice = - llvm::any_cast(&State.Lattice.Value); - // FIXME: we should not be copying the environment here! - // Ultimately the PostVisitCFG only gets a const reference anyway. - PostVisitCFG(Element, DataflowAnalysisState{ - *Lattice, State.Env.fork()}); - }; +runDataflowAnalysis(const AdornedCFG &ACFG, AnalysisT &Analysis, + const Environment &InitEnv, + CFGEltCallbacks PostAnalysisCallbacks, + std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) { + CFGEltCallbacksTypeErased TypeErasedCallbacks; + if (PostAnalysisCallbacks.Before) { + TypeErasedCallbacks.Before = + [&PostAnalysisCallbacks](const CFGElement &Element, + const TypeErasedDataflowAnalysisState &State) { + auto *Lattice = + llvm::any_cast(&State.Lattice.Value); + // FIXME: we should not be copying the environment here! + // Ultimately the `CFGEltCallback` only gets a const reference anyway. + PostAnalysisCallbacks.Before( + Element, DataflowAnalysisState{ + *Lattice, State.Env.fork()}); + }; + } + if (PostAnalysisCallbacks.After) { + TypeErasedCallbacks.After = + [&PostAnalysisCallbacks](const CFGElement &Element, + const TypeErasedDataflowAnalysisState &State) { + auto *Lattice = + llvm::any_cast(&State.Lattice.Value); + // FIXME: we should not be copying the environment here! + // Ultimately the `CFGEltCallback` only gets a const reference anyway. + PostAnalysisCallbacks.After( + Element, DataflowAnalysisState{ + *Lattice, State.Env.fork()}); + }; } auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis( - ACFG, Analysis, InitEnv, PostVisitCFGClosure, MaxBlockVisits); + ACFG, Analysis, InitEnv, TypeErasedCallbacks, MaxBlockVisits); if (!TypeErasedBlockStates) return TypeErasedBlockStates.takeError(); @@ -239,6 +286,22 @@ runDataflowAnalysis( return std::move(BlockStates); } +/// Overload that takes only one post-analysis callback, which is run on the +/// state after visiting the `CFGElement`. This is provided for backwards +/// compatibility; new callers should call the overload taking `CFGEltCallbacks` +/// instead. +template +llvm::Expected>>> +runDataflowAnalysis( + const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv, + CFGEltCallback PostAnalysisCallbackAfterElt = nullptr, + std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) { + return runDataflowAnalysis(ACFG, Analysis, InitEnv, + {nullptr, PostAnalysisCallbackAfterElt}, + MaxBlockVisits); +} + // Create an analysis class that is derived from `DataflowAnalysis`. This is an // SFINAE adapter that allows us to call two different variants of constructor // (either with or without the optional `Environment` parameter). @@ -271,14 +334,11 @@ auto createAnalysis(ASTContext &ASTCtx, Environment &Env) /// `runDataflowAnalysis` for a full description and explanation of the default /// value. template -llvm::Expected> diagnoseFunction( - const FunctionDecl &FuncDecl, ASTContext &ASTCtx, - llvm::function_ref( - const CFGElement &, ASTContext &, - const TransferStateForDiagnostics &)> - Diagnoser, - std::int64_t MaxSATIterations = 1'000'000'000, - std::int32_t MaxBlockVisits = 20'000) { +llvm::Expected> +diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx, + DiagnosisCallbacks Diagnoser, + std::int64_t MaxSATIterations = kDefaultMaxSATIterations, + std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) { llvm::Expected Context = AdornedCFG::build(FuncDecl); if (!Context) return Context.takeError(); @@ -288,21 +348,38 @@ llvm::Expected> diagnoseFunction( Environment Env(AnalysisContext, FuncDecl); AnalysisT Analysis = createAnalysis(ASTCtx, Env); llvm::SmallVector Diagnostics; + CFGEltCallbacksTypeErased PostAnalysisCallbacks; + if (Diagnoser.Before) { + PostAnalysisCallbacks.Before = + [&ASTCtx, &Diagnoser, + &Diagnostics](const CFGElement &Elt, + const TypeErasedDataflowAnalysisState &State) mutable { + auto EltDiagnostics = Diagnoser.Before( + Elt, ASTCtx, + TransferStateForDiagnostics( + llvm::any_cast( + State.Lattice.Value), + State.Env)); + llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); + }; + } + if (Diagnoser.After) { + PostAnalysisCallbacks.After = + [&ASTCtx, &Diagnoser, + &Diagnostics](const CFGElement &Elt, + const TypeErasedDataflowAnalysisState &State) mutable { + auto EltDiagnostics = Diagnoser.After( + Elt, ASTCtx, + TransferStateForDiagnostics( + llvm::any_cast( + State.Lattice.Value), + State.Env)); + llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); + }; + } if (llvm::Error Err = - runTypeErasedDataflowAnalysis( - *Context, Analysis, Env, - [&ASTCtx, &Diagnoser, &Diagnostics]( - const CFGElement &Elt, - const TypeErasedDataflowAnalysisState &State) mutable { - auto EltDiagnostics = Diagnoser( - Elt, ASTCtx, - TransferStateForDiagnostics( - llvm::any_cast( - State.Lattice.Value), - State.Env)); - llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); - }, - MaxBlockVisits) + runTypeErasedDataflowAnalysis(*Context, Analysis, Env, + PostAnalysisCallbacks, MaxBlockVisits) .takeError()) return std::move(Err); @@ -313,6 +390,21 @@ llvm::Expected> diagnoseFunction( return Diagnostics; } +/// Overload that takes only one diagnosis callback, which is run on the state +/// after visiting the `CFGElement`. This is provided for backwards +/// compatibility; new callers should call the overload taking +/// `DiagnosisCallbacks` instead. +template +llvm::Expected> +diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx, + DiagnosisCallback Diagnoser, + std::int64_t MaxSATIterations = kDefaultMaxSATIterations, + std::int32_t MaxBlockVisits = kDefaultMaxBlockVisits) { + DiagnosisCallbacks Callbacks = {nullptr, Diagnoser}; + return diagnoseFunction(FuncDecl, ASTCtx, Callbacks, MaxSATIterations, + MaxBlockVisits); +} + /// Abstract base class for dataflow "models": reusable analysis components that /// model a particular aspect of program semantics in the `Environment`. For /// example, a model may capture a type and its related functions. diff --git a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h index b3722bf3ec80a..512453e2be67a 100644 --- a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h @@ -132,12 +132,25 @@ struct TypeErasedDataflowAnalysisState { } }; +/// A callback to be called with the state before or after visiting a CFG +/// element. +using CFGEltCallbackTypeErased = std::function; + +/// A pair of callbacks to be called with the state before and after visiting a +/// CFG element. +/// Either or both of the callbacks may be null. +struct CFGEltCallbacksTypeErased { + CFGEltCallbackTypeErased Before; + CFGEltCallbackTypeErased After; +}; + /// Performs dataflow analysis and returns a mapping from basic block IDs to /// dataflow analysis states that model the respective basic blocks. Indices of /// the returned vector correspond to basic block IDs. Returns an error if the /// dataflow analysis cannot be performed successfully. Otherwise, calls -/// `PostVisitCFG` on each CFG element with the final analysis results at that -/// program point. +/// `PostAnalysisCallbacks` on each CFG element with the final analysis results +/// before and after that program point. /// /// `MaxBlockVisits` caps the number of block visits during analysis. It doesn't /// distinguish between repeat visits to the same block and visits to distinct @@ -148,9 +161,7 @@ llvm::Expected>> runTypeErasedDataflowAnalysis( const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis, const Environment &InitEnv, - std::function - PostVisitCFG, + const CFGEltCallbacksTypeErased &PostAnalysisCallbacks, std::int32_t MaxBlockVisits); } // namespace dataflow diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index e801c7fafd893..452cd1810f653 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -287,7 +287,7 @@ class DefaultIntArgument : IntArgument { // possible values, and a list of enumerators to map them to. class EnumArgument values, list enums, bit opt = 0, bit fake = 0, - bit isExternalType = 0> + bit isExternalType = 0, bit isCovered = 1> : Argument { string Type = type; // When true, the argument will be parsed as an unevaluated string literal @@ -296,13 +296,16 @@ class EnumArgument values, list Values = values; list Enums = enums; bit IsExternalType = isExternalType; + // We need to know whether an external enum is fully covered by the options + // in order to decide whether to emit unreachable default labels in a switch. + bit IsCovered = isCovered; } // FIXME: There should be a VariadicArgument type that takes any other type // of argument and generates the appropriate type. class VariadicEnumArgument values, list enums, - bit isExternalType = 0> + bit isExternalType = 0, bit isCovered = 1> : Argument { string Type = type; // When true, the argument will be parsed as an unevaluated string literal @@ -311,6 +314,9 @@ class VariadicEnumArgument Values = values; list Enums = enums; bit IsExternalType = isExternalType; + // We need to know whether an external enum is fully covered by the options + // in order to decide whether to emit unreachable default labels in a switch. + bit IsCovered = isCovered; } // Represents an attribute wrapped by another attribute. @@ -680,6 +686,10 @@ class Attr { bit PragmaAttributeSupport; // Set to true if this attribute accepts parameter pack expansion expressions. bit AcceptsExprPack = 0; + // To support multiple enum parameters to an attribute without breaking + // our existing general parsing we need to have a separate flag that + // opts an attribute into strict parsing of attribute parameters + bit StrictEnumParameters = 0; // Lists language options, one of which is required to be true for the // attribute to be applicable. If empty, no language options are required. list LangOpts = []; @@ -1455,6 +1465,28 @@ def CXX11NoReturn : InheritableAttr { let Documentation = [CXX11NoReturnDocs]; } +def NonBlocking : TypeAttr { + let Spellings = [Clang<"nonblocking">]; + let Args = [ExprArgument<"Cond", /*optional*/1>]; + let Documentation = [NonBlockingDocs]; +} + +def NonAllocating : TypeAttr { + let Spellings = [Clang<"nonallocating">]; + let Args = [ExprArgument<"Cond", /*optional*/1>]; + let Documentation = [NonAllocatingDocs]; +} + +def Blocking : TypeAttr { + let Spellings = [Clang<"blocking">]; + let Documentation = [BlockingDocs]; +} + +def Allocating : TypeAttr { + let Spellings = [Clang<"allocating">]; + let Documentation = [AllocatingDocs]; +} + // Similar to CUDA, OpenCL attributes do not receive a [[]] spelling because // the specification does not expose them with one currently. def OpenCLKernel : InheritableAttr { @@ -2913,7 +2945,7 @@ def CodeModel : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"model">]; let Args = [EnumArgument<"Model", "llvm::CodeModel::Model", /*is_string=*/1, ["normal", "medium", "extreme"], ["Small", "Medium", "Large"], - /*opt=*/0, /*fake=*/0, /*isExternalType=*/1>]; + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0>]; let Subjects = SubjectList<[NonTLSGlobalVar], ErrorDiag>; let Documentation = [CodeModelDocs]; } @@ -4472,7 +4504,7 @@ def HLSLShader : InheritableAttr { ["Pixel", "Vertex", "Geometry", "Hull", "Domain", "Compute", "RayGeneration", "Intersection", "AnyHit", "ClosestHit", "Miss", "Callable", "Mesh", "Amplification"], - /*opt=*/0, /*fake=*/0, /*isExternalType=*/1> + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0> ]; let Documentation = [HLSLSV_ShaderTypeAttrDocs]; let AdditionalMembers = @@ -4487,30 +4519,31 @@ def HLSLResource : InheritableAttr { let Spellings = []; let Subjects = SubjectList<[Struct]>; let LangOpts = [HLSL]; - let Args = [EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass", - /*is_string=*/0, - ["SRV", "UAV", "CBuffer", "Sampler"], - ["SRV", "UAV", "CBuffer", "Sampler"], - /*opt=*/0, /*fake=*/0, /*isExternalType=*/1>, - EnumArgument<"ResourceKind", "llvm::hlsl::ResourceKind", - /*is_string=*/0, - ["Texture1D", "Texture2D", "Texture2DMS", - "Texture3D", "TextureCube", "Texture1DArray", - "Texture2DArray", "Texture2DMSArray", - "TextureCubeArray", "TypedBuffer", "RawBuffer", - "StructuredBuffer", "CBuffer", "Sampler", - "TBuffer", "RTAccelerationStructure", - "FeedbackTexture2D", "FeedbackTexture2DArray"], - ["Texture1D", "Texture2D", "Texture2DMS", - "Texture3D", "TextureCube", "Texture1DArray", - "Texture2DArray", "Texture2DMSArray", - "TextureCubeArray", "TypedBuffer", "RawBuffer", - "StructuredBuffer", "CBuffer", "Sampler", - "TBuffer", "RTAccelerationStructure", - "FeedbackTexture2D", "FeedbackTexture2DArray"], - /*opt=*/0, /*fake=*/0, /*isExternalType=*/1>, - DefaultBoolArgument<"isROV", /*default=*/0> - ]; + let Args = [ + EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass", + /*is_string=*/0, ["SRV", "UAV", "CBuffer", "Sampler"], + ["SRV", "UAV", "CBuffer", "Sampler"], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1>, + EnumArgument< + "ResourceKind", "llvm::hlsl::ResourceKind", + /*is_string=*/0, + [ + "Texture1D", "Texture2D", "Texture2DMS", "Texture3D", "TextureCube", + "Texture1DArray", "Texture2DArray", "Texture2DMSArray", + "TextureCubeArray", "TypedBuffer", "RawBuffer", "StructuredBuffer", + "CBuffer", "Sampler", "TBuffer", "RTAccelerationStructure", + "FeedbackTexture2D", "FeedbackTexture2DArray" + ], + [ + "Texture1D", "Texture2D", "Texture2DMS", "Texture3D", "TextureCube", + "Texture1DArray", "Texture2DArray", "Texture2DMSArray", + "TextureCubeArray", "TypedBuffer", "RawBuffer", "StructuredBuffer", + "CBuffer", "Sampler", "TBuffer", "RTAccelerationStructure", + "FeedbackTexture2D", "FeedbackTexture2DArray" + ], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/0>, + DefaultBoolArgument<"isROV", /*default=*/0> + ]; let Documentation = [InternalOnly]; } @@ -4547,6 +4580,31 @@ def NoRandomizeLayout : InheritableAttr { } def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>; +def VTablePointerAuthentication : InheritableAttr { + let Spellings = [Clang<"ptrauth_vtable_pointer">]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [Undocumented]; + let StrictEnumParameters = 1; + let Args = [EnumArgument<"Key", "VPtrAuthKeyType", /*is_string=*/ true, + ["default_key", "no_authentication", "process_dependent", + "process_independent"], + ["DefaultKey", "NoKey", "ProcessDependent", + "ProcessIndependent"]>, + EnumArgument<"AddressDiscrimination", "AddressDiscriminationMode", + /*is_string=*/ true, + ["default_address_discrimination", "no_address_discrimination", + "address_discrimination"], + ["DefaultAddressDiscrimination", "NoAddressDiscrimination", + "AddressDiscrimination"]>, + EnumArgument<"ExtraDiscrimination", "ExtraDiscrimination", + /*is_string=*/ true, + ["default_extra_discrimination", "no_extra_discrimination", + "type_discrimination", "custom_discrimination"], + ["DefaultExtraDiscrimination", "NoExtraDiscrimination", + "TypeDiscrimination", "CustomDiscrimination"]>, + IntArgument<"CustomDiscriminationValue", 1>]; +} + def FunctionReturnThunks : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"function_return">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 70d5dfa8aaf86..ab4bd003541fa 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5672,9 +5672,10 @@ may be changed in the future. be used to pass function arguments. Floating-point registers (XMMs/YMMs) still follow the C calling convention. - On AArch64, only LR and FP are preserved by the callee. - Registers X19-X28, X0-X7, and X9-X15 are used to pass function arguments. - X8, X16-X18, SIMD and floating-point registers follow the AAPCS calling - convention. + Registers X20-X28, X0-X7, and X9-X14 are used to pass function arguments. + X8, X16-X19, SIMD and floating-point registers follow the AAPCS calling + convention. X15 is not available for argument passing on Windows, but is + used to pass arguments on other platforms. }]; } @@ -6584,7 +6585,8 @@ like pointers to an object of type ``T``: private: int *valuePointer; public: - int *getInt() { return &valuePointer; } + IntPointer(const IntOwner&); + int *getInt() { return valuePointer; } }; The argument ``T`` is optional and is ignored. @@ -6600,12 +6602,8 @@ When the Owner's lifetime ends, it will consider the Pointer to be dangling. .. code-block:: c++ int f() { - IntPointer P; - if (true) { - IntOwner O(7); - P = IntPointer(O); // P "points into" O - } // P is dangling - return P.get(); // error: Using a dangling Pointer. + IntPointer P(IntOwner{}); // P "points into" a temporary IntOwner object + P.getInt(); // P is dangling } }]; @@ -8106,3 +8104,72 @@ Attribute used by `clspv`_ (OpenCL-C to Vulkan SPIR-V compiler) to identify func .. _`libclc`: https://libclc.llvm.org }]; } + +def DocCatNonBlockingNonAllocating : DocumentationCategory<"Performance Constraint Attributes"> { + let Content = [{ +The ``nonblocking``, ``blocking``, ``nonallocating`` and ``allocating`` attributes can be attached +to function types, including blocks, C++ lambdas, and member functions. The attributes declare +constraints about a function's behavior pertaining to blocking and heap memory allocation. + +There are several rules for function types with these attributes, enforced with +compiler warnings: + +- When assigning or otherwise converting to a function pointer of ``nonblocking`` or + ``nonallocating`` type, the source must also be a function or function pointer of + that type, unless it is a null pointer, i.e. the attributes should not be "spoofed". Conversions + that remove the attributes are transparent and valid. + +- An override of a ``nonblocking`` or ``nonallocating`` virtual method must also be declared + with that same attribute (or a stronger one.) An overriding method may add an attribute. + +- A redeclaration of a ``nonblocking`` or ``nonallocating`` function must also be declared with + the same attribute (or a stronger one). A redeclaration may add an attribute. + +The warnings are controlled by ``-Wfunction-effects``, which is enabled by default. + +In a future commit, the compiler will diagnose function calls from ``nonblocking`` and ``nonallocating`` +functions to other functions which lack the appropriate attribute. + }]; +} + +def NonBlockingDocs : Documentation { + let Category = DocCatNonBlockingNonAllocating; + let Heading = "nonblocking"; + let Content = [{ +Declares that a function or function type either does or does not block in any way, according +to the optional, compile-time constant boolean argument, which defaults to true. When the argument +is false, the attribute is equivalent to ``blocking``. + +For the purposes of diagnostics, ``nonblocking`` is considered to include the +``nonallocating`` guarantee and is therefore a "stronger" constraint or attribute. + }]; +} + +def NonAllocatingDocs : Documentation { + let Category = DocCatNonBlockingNonAllocating; + let Heading = "nonallocating"; + let Content = [{ +Declares that a function or function type either does or does not allocate heap memory, according +to the optional, compile-time constant boolean argument, which defaults to true. When the argument +is false, the attribute is equivalent to ``allocating``. + }]; +} + +def BlockingDocs : Documentation { + let Category = DocCatNonBlockingNonAllocating; + let Heading = "blocking"; + let Content = [{ +Declares that a function potentially blocks, and prevents any potential inference of ``nonblocking`` +by the compiler. + }]; +} + +def AllocatingDocs : Documentation { + let Category = DocCatNonBlockingNonAllocating; + let Heading = "allocating"; + let Content = [{ +Declares that a function potentially allocates heap memory, and prevents any potential inference +of ``nonallocating`` by the compiler. + }]; +} + diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 7bef5fd7ad40f..f5b15cf90d1f8 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1152,6 +1152,12 @@ def Trap : Builtin { let Prototype = "void()"; } +def VerboseTrap : Builtin { + let Spellings = ["__builtin_verbose_trap"]; + let Attributes = [NoThrow, NoReturn]; + let Prototype = "void(char const*, char const*)"; +} + def Debugtrap : Builtin { let Spellings = ["__builtin_debugtrap"]; let Attributes = [NoThrow]; @@ -1218,6 +1224,24 @@ def ElementwiseAbs : Builtin { let Prototype = "void(...)"; } +def ElementwiseACos : Builtin { + let Spellings = ["__builtin_elementwise_acos"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + +def ElementwiseASin : Builtin { + let Spellings = ["__builtin_elementwise_asin"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + +def ElementwiseATan : Builtin { + let Spellings = ["__builtin_elementwise_atan"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseBitreverse : Builtin { let Spellings = ["__builtin_elementwise_bitreverse"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; @@ -1248,6 +1272,12 @@ def ElementwiseCos : Builtin { let Prototype = "void(...)"; } +def ElementwiseCosh : Builtin { + let Spellings = ["__builtin_elementwise_cosh"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseExp : Builtin { let Spellings = ["__builtin_elementwise_exp"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; @@ -1320,6 +1350,12 @@ def ElementwiseSin : Builtin { let Prototype = "void(...)"; } +def ElementwiseSinh : Builtin { + let Spellings = ["__builtin_elementwise_sinh"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseSqrt : Builtin { let Spellings = ["__builtin_elementwise_sqrt"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; @@ -1332,6 +1368,12 @@ def ElementwiseTan : Builtin { let Prototype = "void(...)"; } +def ElementwiseTanh : Builtin { + let Spellings = ["__builtin_elementwise_tanh"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseTrunc : Builtin { let Spellings = ["__builtin_elementwise_trunc"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; @@ -4393,6 +4435,12 @@ def PtrauthSignUnauthenticated : Builtin { let Prototype = "void*(void*,int,void*)"; } +def PtrauthSignConstant : Builtin { + let Spellings = ["__builtin_ptrauth_sign_constant"]; + let Attributes = [CustomTypeChecking, NoThrow, Const, Constexpr]; + let Prototype = "void*(void*,int,void*)"; +} + def PtrauthSignGenericData : Builtin { let Spellings = ["__builtin_ptrauth_sign_generic_data"]; let Attributes = [CustomTypeChecking, NoThrow, Const]; @@ -4411,6 +4459,12 @@ def PtrauthAuth : Builtin { let Prototype = "void*(void*,int,void*)"; } +def PtrauthStringDiscriminator : Builtin { + let Spellings = ["__builtin_ptrauth_string_discriminator"]; + let Attributes = [NoThrow, Const, Constexpr]; + let Prototype = "size_t(char const*)"; +} + // OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions. // We need the generic prototype, since the packet type could be anything. def ReadPipe : OCLPipeLangBuiltin { diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index a73e63355cfd7..56bba448e12a4 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -149,6 +149,12 @@ BUILTIN(__builtin_amdgcn_mqsad_pk_u16_u8, "WUiWUiUiWUi", "nc") BUILTIN(__builtin_amdgcn_mqsad_u32_u8, "V4UiWUiUiV4Ui", "nc") BUILTIN(__builtin_amdgcn_make_buffer_rsrc, "Qbv*sii", "nc") +BUILTIN(__builtin_amdgcn_raw_buffer_store_b8, "vcQbiiIi", "n") +BUILTIN(__builtin_amdgcn_raw_buffer_store_b16, "vsQbiiIi", "n") +BUILTIN(__builtin_amdgcn_raw_buffer_store_b32, "viQbiiIi", "n") +BUILTIN(__builtin_amdgcn_raw_buffer_store_b64, "vV2iQbiiIi", "n") +BUILTIN(__builtin_amdgcn_raw_buffer_store_b96, "vV3iQbiiIi", "n") +BUILTIN(__builtin_amdgcn_raw_buffer_store_b128, "vV4iQbiiIi", "n") //===----------------------------------------------------------------------===// // Ballot builtins. diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index e3f6da4a84f69..25de2204f04c0 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -36,6 +36,7 @@ VALUE_CODEGENOPT(Name, Bits, Default) #endif CODEGENOPT(DisableIntegratedAS, 1, 0) ///< -no-integrated-as +CODEGENOPT(Crel, 1, 0) ///< -Wa,--crel CODEGENOPT(RelaxELFRelocations, 1, 1) ///< -Wa,-mrelax-relocations={yes,no} CODEGENOPT(AsmVerbose , 1, 0) ///< -dA, -fverbose-asm. CODEGENOPT(PreserveAsmComments, 1, 1) ///< -dA, -fno-preserve-as-comments. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 00523a84d3895..f2a707a8ba8d7 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H #define LLVM_CLANG_BASIC_CODEGENOPTIONS_H +#include "clang/Basic/PointerAuthOptions.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/XRayInstr.h" #include "llvm/ADT/FloatingPointMode.h" @@ -391,6 +392,9 @@ class CodeGenOptions : public CodeGenOptionsBase { std::vector Reciprocals; + /// Configuration for pointer-signing. + PointerAuthOptions PointerAuth; + /// The preferred width for auto-vectorization transforms. This is intended to /// override default transforms based on the width of the architected vector /// registers. diff --git a/clang/include/clang/Basic/Cuda.h b/clang/include/clang/Basic/Cuda.h index 0d5e38e825aa7..83699f8897f66 100644 --- a/clang/include/clang/Basic/Cuda.h +++ b/clang/include/clang/Basic/Cuda.h @@ -52,7 +52,7 @@ const char *CudaVersionToString(CudaVersion V); // Input is "Major.Minor" CudaVersion CudaStringToVersion(const llvm::Twine &S); -enum class CudaArch { +enum class OffloadArch { UNUSED, UNKNOWN, // TODO: Deprecate and remove GPU architectures older than sm_52. @@ -128,12 +128,13 @@ enum class CudaArch { GFX12_GENERIC, GFX1200, GFX1201, + AMDGCNSPIRV, Generic, // A processor model named 'generic' if the target backend defines a // public one. LAST, - CudaDefault = CudaArch::SM_52, - HIPDefault = CudaArch::GFX906, + CudaDefault = OffloadArch::SM_52, + HIPDefault = OffloadArch::GFX906, }; enum class CUDAFunctionTarget { @@ -144,26 +145,26 @@ enum class CUDAFunctionTarget { InvalidTarget }; -static inline bool IsNVIDIAGpuArch(CudaArch A) { - return A >= CudaArch::SM_20 && A < CudaArch::GFX600; +static inline bool IsNVIDIAOffloadArch(OffloadArch A) { + return A >= OffloadArch::SM_20 && A < OffloadArch::GFX600; } -static inline bool IsAMDGpuArch(CudaArch A) { +static inline bool IsAMDOffloadArch(OffloadArch A) { // Generic processor model is for testing only. - return A >= CudaArch::GFX600 && A < CudaArch::Generic; + return A >= OffloadArch::GFX600 && A < OffloadArch::Generic; } -const char *CudaArchToString(CudaArch A); -const char *CudaArchToVirtualArchString(CudaArch A); +const char *OffloadArchToString(OffloadArch A); +const char *OffloadArchToVirtualArchString(OffloadArch A); // The input should have the form "sm_20". -CudaArch StringToCudaArch(llvm::StringRef S); +OffloadArch StringToOffloadArch(llvm::StringRef S); -/// Get the earliest CudaVersion that supports the given CudaArch. -CudaVersion MinVersionForCudaArch(CudaArch A); +/// Get the earliest CudaVersion that supports the given OffloadArch. +CudaVersion MinVersionForOffloadArch(OffloadArch A); -/// Get the latest CudaVersion that supports the given CudaArch. -CudaVersion MaxVersionForCudaArch(CudaArch A); +/// Get the latest CudaVersion that supports the given OffloadArch. +CudaVersion MaxVersionForOffloadArch(OffloadArch A); // Various SDK-dependent features that affect CUDA compilation enum class CudaFeature { diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 1ca2cb85565a1..a62bdc21298ee 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -801,6 +801,10 @@ def warn_drv_missing_multilib : Warning< def note_drv_available_multilibs : Note< "available multilibs are:%0">; +def err_drv_experimental_crel : Error< + "-Wa,--allow-experimental-crel must be specified to use -Wa,--crel. " + "CREL is experimental and uses a non-standard section type code">; + def warn_android_unversioned_fallback : Warning< "using unversioned Android target directory %0 for target %1; unversioned " "directories will not be used in Clang 19 -- provide a versioned directory " diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 9b37d4bd3205b..9431eea1f6be2 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -299,6 +299,10 @@ def CPre23CompatPedantic : DiagGroup<"pre-c23-compat-pedantic", def : DiagGroup<"pre-c2x-compat", [CPre23Compat]>; def : DiagGroup<"pre-c2x-compat-pedantic", [CPre23CompatPedantic]>; +def CPre2yCompat : DiagGroup<"pre-c2y-compat">; +def CPre2yCompatPedantic : DiagGroup<"pre-c2y-compat-pedantic", + [CPre2yCompat]>; + // Warnings for C++ code which is not compatible with previous C++ standards. def CXXPre14Compat : DiagGroup<"pre-c++14-compat">; def : DiagGroup<"c++98-c++11-compat", [CXXPre14Compat]>; @@ -430,6 +434,7 @@ def LogicalOpParentheses: DiagGroup<"logical-op-parentheses">; def LogicalNotParentheses: DiagGroup<"logical-not-parentheses">; def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">; def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">; +def DanglingAssignment: DiagGroup<"dangling-assignment">; def DanglingElse: DiagGroup<"dangling-else">; def DanglingField : DiagGroup<"dangling-field">; def DanglingInitializerList : DiagGroup<"dangling-initializer-list">; @@ -437,7 +442,8 @@ def DanglingGsl : DiagGroup<"dangling-gsl">; def ReturnStackAddress : DiagGroup<"return-stack-address">; // Name of this warning in GCC def : DiagGroup<"return-local-addr", [ReturnStackAddress]>; -def Dangling : DiagGroup<"dangling", [DanglingField, +def Dangling : DiagGroup<"dangling", [DanglingAssignment, + DanglingField, DanglingInitializerList, DanglingGsl, ReturnStackAddress]>; @@ -1195,6 +1201,9 @@ def C23 : DiagGroup<"c23-extensions">; def : DiagGroup<"c2x-extensions", [C23]>; +// A warning group for warnings about using C2y features as extensions. +def C2y : DiagGroup<"c2y-extensions">; + // Previously supported warning group which is no longer pertinent as binary // literals are a C++14 and C23 extension now instead of a GNU extension. def GNUBinaryLiteral : DiagGroup<"gnu-binary-literal">; @@ -1527,6 +1536,10 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">; def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">; def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer]>; +// Warnings and notes related to the function effects system underlying +// the nonblocking and nonallocating attributes. +def FunctionEffects : DiagGroup<"function-effects">; + // Warnings and notes InstallAPI verification. def InstallAPIViolation : DiagGroup<"installapi-violation">; @@ -1535,3 +1548,7 @@ def BitIntExtension : DiagGroup<"bit-int-extension">; // Warnings about misuse of ExtractAPI options. def ExtractAPIMisuse : DiagGroup<"extractapi-misuse">; + +// Warnings about using the non-standard extension having an explicit specialization +// with a storage class specifier. +def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-storage-class">; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index d8c3fee7841f4..12aab09f28556 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -157,9 +157,12 @@ def err_duplicate_default_assoc : Error< "duplicate default generic association">; def note_previous_default_assoc : Note< "previous default generic association is here">; -def ext_generic_with_type_arg : Extension< - "passing a type argument as the first operand to '_Generic' is a Clang " - "extension">, InGroup>; +def ext_c2y_generic_with_type_arg : Extension< + "passing a type argument as the first operand to '_Generic' is a C2y " + "extension">, InGroup; +def warn_c2y_compat_generic_with_type_arg : Warning< + "passing a type argument as the first operand to '_Generic' is incompatible " + "with C standards before C2y">, InGroup, DefaultIgnore; def ext_c99_feature : Extension< "'%0' is a C99 extension">, InGroup; @@ -1508,6 +1511,8 @@ def err_omp_unexpected_append_op : Error< "unexpected operation specified in 'append_args' clause, expected 'interop'">; def err_omp_unexpected_execution_modifier : Error< "unexpected 'execution' modifier in non-executable context">; +def err_omp_unknown_adjust_args_op : Error< + "incorrect adjust_args type, expected 'need_device_ptr' or 'nothing'">; def err_omp_declare_variant_wrong_clause : Error< "expected %select{'match'|'match', 'adjust_args', or 'append_args'}0 clause " "on 'omp declare variant' directive">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d3993dda9e316..3df64b2ecef1b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -924,12 +924,28 @@ def err_ptrauth_value_bad_type : Error<"%select{signed value|extra discriminator|blended pointer|blended " "integer}0 must have %select{pointer|integer|pointer or integer}1 " "type; type here is %2">; +def err_ptrauth_bad_constant_pointer : + Error<"argument to ptrauth_sign_constant must refer to a global variable " + "or function">; +def err_ptrauth_bad_constant_discriminator : + Error<"discriminator argument to ptrauth_sign_constant must be a constant " + "integer, the address of the global variable where the result " + "will be stored, or a blend of the two">; def warn_ptrauth_sign_null_pointer : Warning<"signing a null pointer will yield a non-null pointer">, InGroup; def warn_ptrauth_auth_null_pointer : Warning<"authenticating a null pointer will almost certainly trap">, InGroup; +def err_ptrauth_string_not_literal : Error< + "argument must be a string literal%select{| of char type}0">; + +def note_ptrauth_virtual_function_pointer_incomplete_arg_ret : + Note<"cannot take an address of a virtual member function if its return or " + "argument types are incomplete">; +def note_ptrauth_virtual_function_incomplete_arg_ret_type : + Note<"%0 is incomplete">; + /// main() // static main() is not an error in C, just in C++. @@ -964,7 +980,7 @@ def err_main_global_variable : def warn_main_redefined : Warning<"variable named 'main' with external linkage " "has undefined behavior">, InGroup
; def ext_main_used : Extension< - "ISO C++ does not allow 'main' to be used by a program">, InGroup
; + "referring to 'main' within an expression is a Clang extension">, InGroup
; /// parser diagnostics def ext_no_declarators : ExtWarn<"declaration does not declare anything">, @@ -2403,6 +2419,7 @@ def err_selected_explicit_constructor : Error< "chosen constructor is explicit in copy-initialization">; def note_explicit_ctor_deduction_guide_here : Note< "explicit %select{constructor|deduction guide}0 declared here">; +def note_implicit_deduction_guide : Note<"implicit deduction guide declared as '%0'">; // C++11 auto def warn_cxx98_compat_auto_type_specifier : Warning< @@ -3237,6 +3254,9 @@ def warn_unsupported_target_attribute def err_attribute_unsupported : Error<"%0 attribute is not supported on targets missing %1;" " specify an appropriate -march= or -mcpu=">; +def err_attribute_unsupported_m_profile + : Error<"on M-profile architectures %0 attribute is not supported on targets missing %1;" + " specify an appropriate -march= or -mcpu=">; def err_duplicate_target_attribute : Error<"%select{unsupported|duplicate|unknown}0%select{| CPU|" " tune CPU}1 '%2' in the '%select{target|target_clones|target_version}3' " @@ -3527,6 +3547,12 @@ def warn_alignment_not_power_of_two : Warning< InGroup>; def err_alignment_dependent_typedef_name : Error< "requested alignment is dependent but declaration is not dependent">; +def ext_c2y_alignof_incomplete_array : Extension< + "'alignof' on an incomplete array type is a C2y extension">, + InGroup; +def warn_c2y_compat_alignof_incomplete_array : Warning< + "'alignof' on an incomplete array type is incompatible with C standards " + "before C2y">, InGroup, DefaultIgnore; def warn_alignment_builtin_useless : Warning< "%select{aligning a value|the result of checking whether a value is aligned}0" @@ -5357,7 +5383,7 @@ def err_not_class_template_specialization : Error< "cannot specialize a %select{dependent template|template template " "parameter}0">; def ext_explicit_specialization_storage_class : ExtWarn< - "explicit specialization cannot have a storage class">; + "explicit specialization cannot have a storage class">, InGroup; def err_dependent_function_template_spec_no_match : Error< "no candidate function template was found for dependent" " %select{member|friend}0 function template specialization">; @@ -6066,6 +6092,9 @@ def err_thread_thread_different_kind : Error< def err_mismatched_owning_module : Error< "declaration of %0 in %select{the global module|module %2}1 follows " "declaration in %select{the global module|module %4}3">; +def err_multiple_decl_in_different_modules : Error< + "declaration %0 attached to named module '%1' can't be attached to " + "other modules">; def err_redefinition_different_type : Error< "redefinition of %0 with a different type%diff{: $ vs $|}1,2">; def err_redefinition_different_kind : Error< @@ -7641,8 +7670,12 @@ def ext_gnu_ptr_func_arith : Extension< InGroup; def err_readonly_message_assignment : Error< "assigning to 'readonly' return result of an Objective-C message not allowed">; -def ext_increment_complex : Extension< - "'%select{--|++}0' on an object of complex type is a Clang extension">; +def ext_c2y_increment_complex : Extension< + "'%select{--|++}0' on an object of complex type is a C2y extension">, + InGroup; +def warn_c2y_compat_increment_complex : Warning< + "'%select{--|++}0' on an object of complex type is incompatible with C " + "standards before C2y">, InGroup, DefaultIgnore; def ext_integer_complement_complex : Extension< "ISO C does not support '~' for complex conjugation of %0">; def err_nosetter_property_assignment : Error< @@ -8169,6 +8202,8 @@ let CategoryName = "Lambda Issue" in { "'&' must precede a capture when the capture default is '='">; def err_capture_does_not_name_variable : Error< "%0 in capture list does not name a variable">; + def err_capture_class_member_does_not_name_variable : Error< + "class member %0 cannot appear in capture list as it is not a variable">; def err_capture_non_automatic_variable : Error< "%0 cannot be captured because it does not have automatic storage " "duration">; @@ -8928,6 +8963,8 @@ def err_expected_callable_argument : Error< "expected a callable expression as %ordinal0 argument to %1, found %2">; def note_building_builtin_dump_struct_call : Note< "in call to printing function with arguments '(%0)' while dumping struct">; +def err_builtin_verbose_trap_arg : Error< + "argument to __builtin_verbose_trap must %select{be a pointer to a constant string|not contain $}0">; def err_atomic_load_store_uses_lib : Error< "atomic %select{load|store}0 requires runtime support that is not " @@ -10083,6 +10120,11 @@ def warn_new_dangling_initializer_list : Warning< "the allocated initializer list}0 " "will be destroyed at the end of the full-expression">, InGroup; +def warn_dangling_pointer_assignment : Warning< + "object backing the pointer %0 " + "will be destroyed at the end of the full-expression">, + InGroup; + def warn_unsupported_lifetime_extension : Warning< "lifetime extension of " "%select{temporary|backing array of initializer list}0 created " @@ -10872,6 +10914,23 @@ def warn_imp_cast_drops_unaligned : Warning< "implicit cast from type %0 to type %1 drops __unaligned qualifier">, InGroup>; +// Function effects +// spoofing nonblocking/nonallocating +def warn_invalid_add_func_effects : Warning< + "attribute '%0' should not be added via type conversion">, + InGroup; +def warn_mismatched_func_effect_override : Warning< + "attribute '%0' on overriding function does not match base declaration">, + InGroup; +def warn_mismatched_func_effect_redeclaration : Warning< + "attribute '%0' on function does not match previous declaration">, + InGroup; +def warn_conflicting_func_effects : Warning< + "effects conflict when merging declarations; kept '%0', discarded '%1'">, + InGroup; +def err_func_with_effects_no_prototype : Error< + "'%0' function must have a prototype">; + } // end of sema category let CategoryName = "API Notes Issue" in { @@ -11063,7 +11122,7 @@ def err_omp_loop_variable_type : Error< def err_omp_loop_incr_not_compatible : Error< "increment expression must cause %0 to %select{decrease|increase}1 " "on each iteration of OpenMP for loop">; -def note_omp_loop_cond_requres_compatible_incr : Note< +def note_omp_loop_cond_requires_compatible_incr : Note< "loop step is expected to be %select{negative|positive}0 due to this condition">; def err_omp_loop_diff_cxx : Error< "could not calculate number of iterations calling 'operator-' with " @@ -11126,16 +11185,12 @@ def err_omp_several_directives_in_region : Error< def note_omp_previous_directive : Note< "previous '%0' directive used here">; def err_omp_sections_not_compound_stmt : Error< - "the statement for '#pragma omp sections' must be a compound statement">; -def err_omp_parallel_sections_not_compound_stmt : Error< - "the statement for '#pragma omp parallel sections' must be a compound statement">; + "the statement for '#pragma omp %0' must be a compound statement">; def err_omp_orphaned_section_directive : Error< "%select{orphaned 'omp section' directives are prohibited, it|'omp section' directive}0" " must be closely nested to a sections region%select{|, not a %1 region}0">; def err_omp_sections_substmt_not_section : Error< - "statement in 'omp sections' directive must be enclosed into a section region">; -def err_omp_parallel_sections_substmt_not_section : Error< - "statement in 'omp parallel sections' directive must be enclosed into a section region">; + "statement in 'omp %0' directive must be enclosed into a section region">; def err_omp_parallel_reduction_in_task_firstprivate : Error< "argument of a reduction clause of a %0 construct must not appear in a firstprivate clause on a task construct">; def err_omp_atomic_read_not_expression_statement : Error< @@ -12173,6 +12228,30 @@ def warn_cuda_maxclusterrank_sm_90 : Warning< "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring " "%1 attribute">, InGroup; +// VTable pointer authentication errors +def err_non_polymorphic_vtable_pointer_auth : Error< + "cannot set vtable pointer authentication on monomorphic type %0">; +def err_incomplete_type_vtable_pointer_auth : Error< + "cannot set vtable pointer authentication on an incomplete type %0">; +def err_non_top_level_vtable_pointer_auth : Error< + "cannot set vtable pointer authentication on %0 which is a subclass of polymorphic type %1">; +def err_duplicated_vtable_pointer_auth : Error< + "multiple vtable pointer authentication policies on %0">; +def err_invalid_authentication_key : Error< + "invalid authentication key %0">; +def err_invalid_address_discrimination : Error< + "invalid address discrimination mode %0">; +def err_invalid_extra_discrimination : Error< + "invalid extra discrimination selection %0">; +def err_invalid_custom_discrimination : Error< + "invalid custom discrimination">; +def err_missing_custom_discrimination : Error< + "missing custom discrimination">; +def err_no_default_vtable_pointer_auth : Error< + "cannot specify a default vtable pointer authentication " + "%select{key|address discrimination mode|discriminator}0 with no default set" +>; + def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must " "have a bit size of at least %select{2|1}0">; def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit " @@ -12207,6 +12286,9 @@ def err_std_source_location_impl_not_found : Error< def err_std_source_location_impl_malformed : Error< "'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">; +def err_std_initializer_list_malformed : Error< + "%0 layout not recognized. Must be a non-polymorphic class type with no bases and two fields: a 'const E *' and either another 'const E *' or a 'std::size_t'">; + // HLSL Diagnostics def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in '%1' shaders, requires %select{|one of the following: }2%3">; def err_hlsl_attr_invalid_type : Error< @@ -12253,6 +12335,9 @@ def warn_hlsl_availability_unavailable : Warning, InGroup, DefaultError; +def err_hlsl_export_not_on_function : Error< + "export declaration can only be used on functions">; + // Layout randomization diagnostics. def err_non_designated_init_used : Error< "a randomized struct can only be initialized with a designated initializer">; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 6dd6b5614f44c..491759e2fcdbb 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -87,6 +87,7 @@ LANGOPT(C99 , 1, 0, "C99") LANGOPT(C11 , 1, 0, "C11") LANGOPT(C17 , 1, 0, "C17") LANGOPT(C23 , 1, 0, "C23") +LANGOPT(C2y , 1, 0, "C2y") LANGOPT(MSVCCompat , 1, 0, "Microsoft Visual C++ full compatibility mode") LANGOPT(Kernel , 1, 0, "Kernel mode") LANGOPT(MicrosoftExt , 1, 0, "Microsoft C++ extensions") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 75e88afbd9705..91f1c2f2e6239 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -575,6 +575,10 @@ class LangOptions : public LangOptionsBase { // implementation on real-world examples. std::string OpenACCMacroOverride; + // Indicates if the wasm-opt binary must be ignored in the case of a + // WebAssembly target. + bool NoWasmOpt = false; + LangOptions(); /// Set language defaults for the given input language and diff --git a/clang/include/clang/Basic/LangStandard.h b/clang/include/clang/Basic/LangStandard.h index 8e25afc833661..f79b4aafb0b26 100644 --- a/clang/include/clang/Basic/LangStandard.h +++ b/clang/include/clang/Basic/LangStandard.h @@ -52,18 +52,19 @@ enum LangFeatures { C11 = (1 << 2), C17 = (1 << 3), C23 = (1 << 4), - CPlusPlus = (1 << 5), - CPlusPlus11 = (1 << 6), - CPlusPlus14 = (1 << 7), - CPlusPlus17 = (1 << 8), - CPlusPlus20 = (1 << 9), - CPlusPlus23 = (1 << 10), - CPlusPlus26 = (1 << 11), - Digraphs = (1 << 12), - GNUMode = (1 << 13), - HexFloat = (1 << 14), - OpenCL = (1 << 15), - HLSL = (1 << 16) + C2y = (1 << 5), + CPlusPlus = (1 << 6), + CPlusPlus11 = (1 << 7), + CPlusPlus14 = (1 << 8), + CPlusPlus17 = (1 << 9), + CPlusPlus20 = (1 << 10), + CPlusPlus23 = (1 << 11), + CPlusPlus26 = (1 << 12), + Digraphs = (1 << 13), + GNUMode = (1 << 14), + HexFloat = (1 << 15), + OpenCL = (1 << 16), + HLSL = (1 << 17) }; /// LangStandard - Information about the properties of a particular language @@ -106,6 +107,9 @@ struct LangStandard { /// isC23 - Language is a superset of C23. bool isC23() const { return Flags & C23; } + /// isC2y - Language is a superset of C2y. + bool isC2y() const { return Flags & C2y; } + /// isCPlusPlus - Language is a C++ variant. bool isCPlusPlus() const { return Flags & CPlusPlus; } diff --git a/clang/include/clang/Basic/LangStandards.def b/clang/include/clang/Basic/LangStandards.def index b6192e48efc11..f0c259307ac4e 100644 --- a/clang/include/clang/Basic/LangStandards.def +++ b/clang/include/clang/Basic/LangStandards.def @@ -99,6 +99,15 @@ LANGSTANDARD_ALIAS_DEPR(gnu23, "gnu2x") // FIXME: Add the alias for iso9899:202* once we know the year ISO publishes // the document (expected to be 2024). +// C2y modes +LANGSTANDARD(c2y, "c2y", + C, "Working Draft for ISO C2y", + LineComment | C99 | C11 | C17 | C23 | C2y | Digraphs | HexFloat) +LANGSTANDARD(gnu2y, "gnu2y", + C, "Working Draft for ISO C2y with GNU extensions", + LineComment | C99 | C11 | C17 | C23 | C2y | Digraphs | GNUMode | HexFloat) + + // C++ modes LANGSTANDARD(cxx98, "c++98", CXX, "ISO C++ 1998 with amendments", diff --git a/clang/include/clang/Basic/OpenMPKinds.h b/clang/include/clang/Basic/OpenMPKinds.h index d127498774c7f..1d7192c4bdf9d 100644 --- a/clang/include/clang/Basic/OpenMPKinds.h +++ b/clang/include/clang/Basic/OpenMPKinds.h @@ -368,6 +368,20 @@ bool needsTaskBasedThreadLimit(OpenMPDirectiveKind DKind); /// is restricted only to memory order clauses of "OMPC_acquire", /// "OMPC_relaxed" and "OMPC_seq_cst". bool checkFailClauseParameter(OpenMPClauseKind FailClauseParameter); + +/// Checks if the specified directive is considered as "executable". This +/// combines the OpenMP categories of "executable" and "subsidiary", plus +/// any other directives that should be treated as executable. +/// \param DKind Specified directive. +/// \return true - if the above condition is met for this directive +/// otherwise - false. +bool isOpenMPExecutableDirective(OpenMPDirectiveKind DKind); + +/// Checks if the specified directive can capture variables. +/// \param DKind Specified directive. +/// \return true - if the above condition is met for this directive +/// otherwise - false. +bool isOpenMPCapturingDirective(OpenMPDirectiveKind DKind); } #endif diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index e5cdcc31ebfb7..197d63642ca6d 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -14,10 +14,169 @@ #ifndef LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H #define LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/ADT/STLForwardCompat.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Target/TargetOptions.h" +#include + namespace clang { constexpr unsigned PointerAuthKeyNone = -1; +class PointerAuthSchema { +public: + enum class Kind : unsigned { + None, + ARM8_3, + }; + + /// Hardware pointer-signing keys in ARM8.3. + /// + /// These values are the same used in ptrauth.h. + enum class ARM8_3Key : unsigned { + ASIA = 0, + ASIB = 1, + ASDA = 2, + ASDB = 3 + }; + + /// Forms of extra discrimination. + enum class Discrimination : unsigned { + /// No additional discrimination. + None, + + /// Include a hash of the entity's type. + Type, + + /// Include a hash of the entity's identity. + Decl, + + /// Discriminate using a constant value. + Constant, + }; + +private: + Kind TheKind : 2; + unsigned IsAddressDiscriminated : 1; + unsigned IsIsaPointer : 1; + unsigned AuthenticatesNullValues : 1; + PointerAuthenticationMode SelectedAuthenticationMode : 2; + Discrimination DiscriminationKind : 2; + unsigned Key : 2; + unsigned ConstantDiscriminator : 16; + +public: + PointerAuthSchema() : TheKind(Kind::None) {} + + PointerAuthSchema( + ARM8_3Key Key, bool IsAddressDiscriminated, + PointerAuthenticationMode AuthenticationMode, + Discrimination OtherDiscrimination, + std::optional ConstantDiscriminatorOrNone = std::nullopt, + bool IsIsaPointer = false, bool AuthenticatesNullValues = false) + : TheKind(Kind::ARM8_3), IsAddressDiscriminated(IsAddressDiscriminated), + IsIsaPointer(IsIsaPointer), + AuthenticatesNullValues(AuthenticatesNullValues), + SelectedAuthenticationMode(AuthenticationMode), + DiscriminationKind(OtherDiscrimination), Key(llvm::to_underlying(Key)) { + assert((getOtherDiscrimination() != Discrimination::Constant || + ConstantDiscriminatorOrNone) && + "constant discrimination requires a constant!"); + if (ConstantDiscriminatorOrNone) + ConstantDiscriminator = *ConstantDiscriminatorOrNone; + } + + PointerAuthSchema( + ARM8_3Key Key, bool IsAddressDiscriminated, + Discrimination OtherDiscrimination, + std::optional ConstantDiscriminatorOrNone = std::nullopt, + bool IsIsaPointer = false, bool AuthenticatesNullValues = false) + : PointerAuthSchema(Key, IsAddressDiscriminated, + PointerAuthenticationMode::SignAndAuth, + OtherDiscrimination, ConstantDiscriminatorOrNone, + IsIsaPointer, AuthenticatesNullValues) {} + + Kind getKind() const { return TheKind; } + + explicit operator bool() const { return isEnabled(); } + + bool isEnabled() const { return getKind() != Kind::None; } + + bool isAddressDiscriminated() const { + assert(getKind() != Kind::None); + return IsAddressDiscriminated; + } + + bool isIsaPointer() const { + assert(getKind() != Kind::None); + return IsIsaPointer; + } + + bool authenticatesNullValues() const { + assert(getKind() != Kind::None); + return AuthenticatesNullValues; + } + + bool hasOtherDiscrimination() const { + return getOtherDiscrimination() != Discrimination::None; + } + + Discrimination getOtherDiscrimination() const { + assert(getKind() != Kind::None); + return DiscriminationKind; + } + + uint16_t getConstantDiscrimination() const { + assert(getOtherDiscrimination() == Discrimination::Constant); + return ConstantDiscriminator; + } + + unsigned getKey() const { + switch (getKind()) { + case Kind::None: + llvm_unreachable("calling getKey() on disabled schema"); + case Kind::ARM8_3: + return llvm::to_underlying(getARM8_3Key()); + } + llvm_unreachable("bad key kind"); + } + + PointerAuthenticationMode getAuthenticationMode() const { + return SelectedAuthenticationMode; + } + + ARM8_3Key getARM8_3Key() const { + assert(getKind() == Kind::ARM8_3); + return ARM8_3Key(Key); + } +}; + +struct PointerAuthOptions { + /// The ABI for C function pointers. + PointerAuthSchema FunctionPointers; + + /// The ABI for C++ virtual table pointers (the pointer to the table + /// itself) as installed in an actual class instance. + PointerAuthSchema CXXVTablePointers; + + /// TypeInfo has external ABI requirements and is emitted without + /// actually having parsed the libcxx definition, so we can't simply + /// perform a look up. The settings for this should match the exact + /// specification in type_info.h + PointerAuthSchema CXXTypeInfoVTablePointer; + + /// The ABI for C++ virtual table pointers as installed in a VTT. + PointerAuthSchema CXXVTTVTablePointers; + + /// The ABI for most C++ virtual function pointers, i.e. v-table entries. + PointerAuthSchema CXXVirtualFunctionPointers; + + /// The ABI for variadic C++ virtual function pointers. + PointerAuthSchema CXXVirtualVariadicFunctionPointers; +}; + } // end namespace clang #endif diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index d2e2e914327f2..d3ccc7ef81c07 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -1981,6 +1981,7 @@ class SourceManager : public RefCountedBase { SourceLocation SpellLoc, SourceLocation ExpansionLoc, unsigned ExpansionLength) const; + void updateSlocUsageStats() const; }; /// Comparison function object. diff --git a/clang/include/clang/Basic/TargetCXXABI.h b/clang/include/clang/Basic/TargetCXXABI.h index c113a6a048ad4..d204452afbf4b 100644 --- a/clang/include/clang/Basic/TargetCXXABI.h +++ b/clang/include/clang/Basic/TargetCXXABI.h @@ -116,7 +116,7 @@ class TargetCXXABI { return T.isKnownWindowsMSVCEnvironment(); } llvm_unreachable("invalid CXXABI kind"); - }; + } /// Does this ABI generally fall into the Itanium family of ABIs? bool isItaniumFamily() const { diff --git a/clang/include/clang/Basic/Thunk.h b/clang/include/clang/Basic/Thunk.h index 0247e279408f0..8ff7603e0094d 100644 --- a/clang/include/clang/Basic/Thunk.h +++ b/clang/include/clang/Basic/Thunk.h @@ -21,6 +21,7 @@ namespace clang { class CXXMethodDecl; +class Type; /// A return adjustment. struct ReturnAdjustment { @@ -162,20 +163,24 @@ struct ThunkInfo { /// Holds a pointer to the overridden method this thunk is for, /// if needed by the ABI to distinguish different thunks with equal - /// adjustments. Otherwise, null. + /// adjustments. + /// In the Itanium ABI, this field can hold the method that created the + /// vtable entry for this thunk. + /// Otherwise, null. /// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using /// an ABI-specific comparator. const CXXMethodDecl *Method; + const Type *ThisType; - ThunkInfo() : Method(nullptr) {} + ThunkInfo() : Method(nullptr), ThisType(nullptr) {} ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return, - const CXXMethodDecl *Method = nullptr) - : This(This), Return(Return), Method(Method) {} + const Type *ThisT, const CXXMethodDecl *Method = nullptr) + : This(This), Return(Return), Method(Method), ThisType(ThisT) {} friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) { return LHS.This == RHS.This && LHS.Return == RHS.Return && - LHS.Method == RHS.Method; + LHS.Method == RHS.Method && LHS.ThisType == RHS.ThisType; } bool isEmpty() const { diff --git a/clang/include/clang/Basic/arm_sme.td b/clang/include/clang/Basic/arm_sme.td index 564a58e4eb670..ce8908f566f2f 100644 --- a/clang/include/clang/Basic/arm_sme.td +++ b/clang/include/clang/Basic/arm_sme.td @@ -15,11 +15,13 @@ include "arm_sve_sme_incl.td" +let SVETargetGuard = InvalidMode in { + //////////////////////////////////////////////////////////////////////////////// // Loads multiclass ZALoad ch> { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _H : MInst<"svld1_hor_" # n_suffix, "vimPQ", t, [IsLoad, IsOverloadNone, IsStreaming, IsInOutZA], MemEltTyDefault, i_prefix # "_horiz", ch>; @@ -44,7 +46,7 @@ defm SVLD1_ZA32 : ZALoad<"za32", "i", "aarch64_sme_ld1w", [ImmCheck<0, ImmCheck0 defm SVLD1_ZA64 : ZALoad<"za64", "l", "aarch64_sme_ld1d", [ImmCheck<0, ImmCheck0_7>]>; defm SVLD1_ZA128 : ZALoad<"za128", "q", "aarch64_sme_ld1q", [ImmCheck<0, ImmCheck0_15>]>; -let TargetGuard = "sme" in { +let SMETargetGuard = "sme" in { def SVLDR_VNUM_ZA : MInst<"svldr_vnum_za", "vmQl", "", [IsOverloadNone, IsStreamingCompatible, IsInOutZA], MemEltTyDefault, "aarch64_sme_ldr">; @@ -58,7 +60,7 @@ def SVLDR_ZA : MInst<"svldr_za", "vmQ", "", // Stores multiclass ZAStore ch> { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _H : MInst<"svst1_hor_" # n_suffix, "vimP%", t, [IsStore, IsOverloadNone, IsStreaming, IsInZA], MemEltTyDefault, i_prefix # "_horiz", ch>; @@ -83,7 +85,7 @@ defm SVST1_ZA32 : ZAStore<"za32", "i", "aarch64_sme_st1w", [ImmCheck<0, ImmCheck defm SVST1_ZA64 : ZAStore<"za64", "l", "aarch64_sme_st1d", [ImmCheck<0, ImmCheck0_7>]>; defm SVST1_ZA128 : ZAStore<"za128", "q", "aarch64_sme_st1q", [ImmCheck<0, ImmCheck0_15>]>; -let TargetGuard = "sme" in { +let SMETargetGuard = "sme" in { def SVSTR_VNUM_ZA : MInst<"svstr_vnum_za", "vm%l", "", [IsOverloadNone, IsStreamingCompatible, IsInZA], MemEltTyDefault, "aarch64_sme_str">; @@ -97,7 +99,7 @@ def SVSTR_ZA : MInst<"svstr_za", "vm%", "", // Read horizontal/vertical ZA slices multiclass ZARead ch> { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _H : SInst<"svread_hor_" # n_suffix # "[_{d}]", "ddPim", t, MergeOp1, i_prefix # "_horiz", [IsReadZA, IsStreaming, IsInZA], ch>; @@ -118,7 +120,7 @@ defm SVREAD_ZA128 : ZARead<"za128", "csilUcUsUiUlhbfd", "aarch64_sme_readq", [Im // Write horizontal/vertical ZA slices multiclass ZAWrite ch> { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _H : SInst<"svwrite_hor_" # n_suffix # "[_{d}]", "vimPd", t, MergeOp1, i_prefix # "_horiz", [IsWriteZA, IsStreaming, IsInOutZA], ch>; @@ -138,7 +140,7 @@ defm SVWRITE_ZA128 : ZAWrite<"za128", "csilUcUsUiUlhbfd", "aarch64_sme_writeq", //////////////////////////////////////////////////////////////////////////////// // SME - Zero -let TargetGuard = "sme" in { +let SMETargetGuard = "sme" in { def SVZERO_MASK_ZA : SInst<"svzero_mask_za", "vi", "", MergeNone, "aarch64_sme_zero", [IsOverloadNone, IsStreamingCompatible, IsInOutZA], [ImmCheck<0, ImmCheck0_255>]>; @@ -146,7 +148,7 @@ let TargetGuard = "sme" in { [IsOverloadNone, IsStreamingCompatible, IsOutZA]>; } -let TargetGuard = "sme2p1" in { +let SMETargetGuard = "sme2p1" in { def SVZERO_ZA64_VG1x2 : SInst<"svzero_za64_vg1x2", "vm", "", MergeNone, "aarch64_sme_zero_za64_vg1x2", [IsOverloadNone, IsStreaming, IsInOutZA]>; def SVZERO_ZA64_VG1x4 : SInst<"svzero_za64_vg1x4", "vm", "", MergeNone, "aarch64_sme_zero_za64_vg1x4", @@ -169,7 +171,7 @@ let TargetGuard = "sme2p1" in { // SME - Counting elements in a streaming vector multiclass ZACount { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME : SInst<"sv" # n_suffix, "nv", "", MergeNone, "aarch64_sme_" # n_suffix, [IsOverloadNone, IsStreamingCompatible]>; @@ -185,13 +187,13 @@ defm SVCNTSD : ZACount<"cntsd">; // SME - ADDHA/ADDVA multiclass ZAAdd { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _ZA32: SInst<"sv" # n_suffix # "_za32[_{d}]", "viPPd", "iUi", MergeOp1, "aarch64_sme_" # n_suffix, [IsStreaming, IsInOutZA], [ImmCheck<0, ImmCheck0_3>]>; } - let TargetGuard = "sme-i16i64" in { + let SMETargetGuard = "sme-i16i64" in { def NAME # _ZA64: SInst<"sv" # n_suffix # "_za64[_{d}]", "viPPd", "lUl", MergeOp1, "aarch64_sme_" # n_suffix, [IsStreaming, IsInOutZA], [ImmCheck<0, ImmCheck0_7>]>; @@ -205,7 +207,7 @@ defm SVADDVA : ZAAdd<"addva">; // SME - SMOPA, SMOPS, UMOPA, UMOPS multiclass ZAIntOuterProd { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _ZA32_B: SInst<"sv" # n_suffix2 # "_za32[_{d}]", "viPPdd", !cond(!eq(n_suffix1, "s") : "", true: "U") # "c", MergeOp1, "aarch64_sme_" # n_suffix1 # n_suffix2 # "_wide", @@ -213,7 +215,7 @@ multiclass ZAIntOuterProd { [ImmCheck<0, ImmCheck0_3>]>; } - let TargetGuard = "sme-i16i64" in { + let SMETargetGuard = "sme-i16i64" in { def NAME # _ZA64_H: SInst<"sv" # n_suffix2 # "_za64[_{d}]", "viPPdd", !cond(!eq(n_suffix1, "s") : "", true: "U") # "s", MergeOp1, "aarch64_sme_" # n_suffix1 # n_suffix2 # "_wide", @@ -231,7 +233,7 @@ defm SVUMOPS : ZAIntOuterProd<"u", "mops">; // SME - SUMOPA, SUMOPS, USMOPA, USMOPS multiclass ZAIntOuterProdMixedSigns { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _ZA32_B: SInst<"sv" # n_suffix1 # n_suffix2 # "_za32[_{d}]", "viPPd" # !cond(!eq(n_suffix1, "su") : "u", true: "x"), !cond(!eq(n_suffix1, "su") : "", true: "U") # "c", @@ -240,7 +242,7 @@ multiclass ZAIntOuterProdMixedSigns { [ImmCheck<0, ImmCheck0_3>]>; } - let TargetGuard = "sme-i16i64" in { + let SMETargetGuard = "sme-i16i64" in { def NAME # _ZA64_H: SInst<"sv" # n_suffix1 # n_suffix2 # "_za64[_{d}]", "viPPd" # !cond(!eq(n_suffix1, "su") : "u", true: "x"), !cond(!eq(n_suffix1, "su") : "", true: "U") # "s", @@ -259,7 +261,7 @@ defm SVUSMOPS : ZAIntOuterProdMixedSigns<"us", "mops">; // SME - FMOPA, FMOPS multiclass ZAFPOuterProd { - let TargetGuard = "sme" in { + let SMETargetGuard = "sme" in { def NAME # _ZA32_B: SInst<"sv" # n_suffix # "_za32[_{d}]", "viPPdd", "h", MergeOp1, "aarch64_sme_" # n_suffix # "_wide", [IsStreaming, IsInOutZA], @@ -276,7 +278,7 @@ multiclass ZAFPOuterProd { [ImmCheck<0, ImmCheck0_3>]>; } - let TargetGuard = "sme-f64f64" in { + let SMETargetGuard = "sme-f64f64" in { def NAME # _ZA64_D: SInst<"sv" # n_suffix # "_za64[_{d}]", "viPPdd", "d", MergeOp1, "aarch64_sme_" # n_suffix, [IsStreaming, IsInOutZA], @@ -291,7 +293,7 @@ defm SVMOPS : ZAFPOuterProd<"mops">; // SME2 - ADD, SUB multiclass ZAAddSub { - let TargetGuard = "sme2" in { + let SMETargetGuard = "sme2" in { def NAME # _WRITE_SINGLE_ZA32_VG1X2_I32 : Inst<"sv" # n_suffix # "_write[_single]_za32[_{d}]_vg1x2", "vm2d", "iUi", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x2", [IsStreaming, IsInOutZA], []>; def NAME # _WRITE_SINGLE_ZA32_VG1X4_I32 : Inst<"sv" # n_suffix # "_write[_single]_za32[_{d}]_vg1x4", "vm4d", "iUi", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x4", [IsStreaming, IsInOutZA], []>; @@ -302,7 +304,7 @@ multiclass ZAAddSub { def NAME # _ZA32_VG1X4_I32 : Inst<"sv" # n_suffix # "_za32[_{d}]_vg1x4", "vm4", "iUif", MergeNone, "aarch64_sme_" # n_suffix # "_za32_vg1x4", [IsStreaming, IsInOutZA], []>; } - let TargetGuard = "sme2,sme-i16i64" in { + let SMETargetGuard = "sme2,sme-i16i64" in { def NAME # _WRITE_SINGLE_ZA64_VG1X2_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x2", "vm2d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x2", [IsStreaming, IsInOutZA], []>; def NAME # _WRITE_SINGLE_ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x4", "vm4d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x4", [IsStreaming, IsInOutZA], []>; @@ -313,17 +315,17 @@ multiclass ZAAddSub { def NAME # _ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsInOutZA], []>; } - let TargetGuard = "sme2,sme-f64f64" in { + let SMETargetGuard = "sme2,sme-f64f64" in { def NAME # _ZA64_VG1X2_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x2", "vm2", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x2", [IsStreaming, IsInOutZA], []>; def NAME # _ZA64_VG1X4_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsInOutZA], []>; } - let TargetGuard = "sme-f16f16|sme-f8f16" in { + let SMETargetGuard = "sme-f16f16|sme-f8f16" in { def NAME # _ZA16_VG1X2_F16 : Inst<"sv" # n_suffix # "_za16[_{d}]_vg1x2", "vm2", "h", MergeNone, "aarch64_sme_" # n_suffix # "_za16_vg1x2", [IsStreaming, IsInOutZA], []>; def NAME # _ZA16_VG1X4_F16 : Inst<"sv" # n_suffix # "_za16[_{d}]_vg1x4", "vm4", "h", MergeNone, "aarch64_sme_" # n_suffix # "_za16_vg1x4", [IsStreaming, IsInOutZA], []>; } - let TargetGuard = "sme2,b16b16" in { + let SMETargetGuard = "sme2,b16b16" in { def NAME # _ZA16_VG1X2_BF16 : Inst<"sv" # n_suffix # "_za16[_{d}]_vg1x2", "vm2", "b", MergeNone, "aarch64_sme_" # n_suffix # "_za16_vg1x2", [IsStreaming, IsInOutZA], []>; def NAME # _ZA16_VG1X4_BF16 : Inst<"sv" # n_suffix # "_za16[_{d}]_vg1x4", "vm4", "b", MergeNone, "aarch64_sme_" # n_suffix # "_za16_vg1x4", [IsStreaming, IsInOutZA], []>; } @@ -347,7 +349,7 @@ multiclass ZAWrite_VG checks> { def NAME # _VG1x4 : Inst<"svwrite_" # n # "[_{d}]_vg1x4", "vm4", t, MergeNone, i # "_vg1x4", [IsInOutZA, IsStreaming], []>; } -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { defm SVWRITE_ZA8 : ZAWrite_VG<"za8", "cUc", "aarch64_sme_write", [ImmCheck<0, ImmCheck0_0>]>; defm SVWRITE_ZA16 : ZAWrite_VG<"za16", "sUshb", "aarch64_sme_write", [ImmCheck<0, ImmCheck0_1>]>; defm SVWRITE_ZA32 : ZAWrite_VG<"za32", "iUif", "aarch64_sme_write", [ImmCheck<0, ImmCheck0_3>]>; @@ -363,7 +365,7 @@ multiclass ZARead_VG checks> { def NAME # _VG1x4 : Inst<"svread_" # n # "_{d}_vg1x4", "4m", t, MergeNone, i # "_vg1x4", [IsInZA, IsStreaming], []>; } -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { defm SVREAD_ZA8 : ZARead_VG<"za8", "cUc", "aarch64_sme_read", [ImmCheck<0, ImmCheck0_0>]>; defm SVREAD_ZA16 : ZARead_VG<"za16", "sUshb", "aarch64_sme_read", [ImmCheck<0, ImmCheck0_1>]>; defm SVREAD_ZA32 : ZARead_VG<"za32", "iUif", "aarch64_sme_read", [ImmCheck<0, ImmCheck0_3>]>; @@ -374,7 +376,7 @@ let TargetGuard = "sme2" in { // Outer product and accumulate/subtract // -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { def SVSMOPA : Inst<"svmopa_za32[_{d}]_m", "viPPdd", "s", MergeNone, "aarch64_sme_smopa_za32", [IsInOutZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; def SVUSMOPA : Inst<"svmopa_za32[_{d}]_m", "viPPdd", "Us", MergeNone, "aarch64_sme_umopa_za32", [IsInOutZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; @@ -434,7 +436,7 @@ let TargetGuard = "sme2" in { def SVDOT_LANE_ZA32_VG1x4_F16 : Inst<"svdot_lane_za32[_{d}]_vg1x4", "vm4di", "bh", MergeNone, "aarch64_sme_fdot_lane_za32_vg1x4", [IsStreaming, IsInOutZA], [ImmCheck<3, ImmCheck0_3>]>; } -let TargetGuard = "sme2,sme-i16i64" in { +let SMETargetGuard = "sme2,sme-i16i64" in { def SVVDOT_LANE_ZA64_VG1x4_S : Inst<"svvdot_lane_za64[_{d}]_vg1x4", "vm4di", "s", MergeNone, "aarch64_sme_svdot_lane_za64_vg1x4", [IsStreaming, IsInOutZA], [ImmCheck<3, ImmCheck0_1>]>; def SVVDOT_LANE_ZA64_VG1x4_U : Inst<"svvdot_lane_za64[_{d}]_vg1x4", "vm4di", "Us", MergeNone, "aarch64_sme_uvdot_lane_za64_vg1x4", [IsStreaming, IsInOutZA], [ImmCheck<3, ImmCheck0_1>]>; @@ -453,7 +455,7 @@ let TargetGuard = "sme2,sme-i16i64" in { } // FMLA/FMLS -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { def SVMLA_MULTI_VG1x2_F32 : Inst<"svmla_za32[_{d}]_vg1x2", "vm22", "f", MergeNone, "aarch64_sme_fmla_vg1x2", [IsStreaming, IsInOutZA], []>; def SVMLA_MULTI_VG1x4_F32 : Inst<"svmla_za32[_{d}]_vg1x4", "vm44", "f", MergeNone, "aarch64_sme_fmla_vg1x4", [IsStreaming, IsInOutZA], []>; def SVMLS_MULTI_VG1x2_F32 : Inst<"svmls_za32[_{d}]_vg1x2", "vm22", "f", MergeNone, "aarch64_sme_fmls_vg1x2", [IsStreaming, IsInOutZA], []>; @@ -470,7 +472,7 @@ let TargetGuard = "sme2" in { def SVMLS_LANE_VG1x4_F32 : Inst<"svmls_lane_za32[_{d}]_vg1x4", "vm4di", "f", MergeNone, "aarch64_sme_fmls_lane_vg1x4", [IsStreaming, IsInOutZA], [ImmCheck<3, ImmCheck0_3>]>; } -let TargetGuard = "sme2,sme-f64f64" in { +let SMETargetGuard = "sme2,sme-f64f64" in { def SVMLA_MULTI_VG1x2_F64 : Inst<"svmla_za64[_{d}]_vg1x2", "vm22", "d", MergeNone, "aarch64_sme_fmla_vg1x2", [IsStreaming, IsInOutZA], []>; def SVMLA_MULTI_VG1x4_F64 : Inst<"svmla_za64[_{d}]_vg1x4", "vm44", "d", MergeNone, "aarch64_sme_fmla_vg1x4", [IsStreaming, IsInOutZA], []>; def SVMLS_MULTI_VG1x2_F64 : Inst<"svmls_za64[_{d}]_vg1x2", "vm22", "d", MergeNone, "aarch64_sme_fmls_vg1x2", [IsStreaming, IsInOutZA], []>; @@ -487,7 +489,7 @@ let TargetGuard = "sme2,sme-f64f64" in { def SVMLS_LANE_VG1x4_F64 : Inst<"svmls_lane_za64[_{d}]_vg1x4", "vm4di", "d", MergeNone, "aarch64_sme_fmls_lane_vg1x4", [IsStreaming, IsInOutZA], [ImmCheck<3, ImmCheck0_1>]>; } -let TargetGuard = "sme-f16f16" in { +let SMETargetGuard = "sme-f16f16" in { def SVMLA_MULTI_VG1x2_F16 : Inst<"svmla_za16[_f16]_vg1x2", "vm22", "h", MergeNone, "aarch64_sme_fmla_vg1x2", [IsStreaming, IsInOutZA], []>; def SVMLA_MULTI_VG1x4_F16 : Inst<"svmla_za16[_f16]_vg1x4", "vm44", "h", MergeNone, "aarch64_sme_fmla_vg1x4", [IsStreaming, IsInOutZA], []>; def SVMLS_MULTI_VG1x2_F16 : Inst<"svmls_za16[_f16]_vg1x2", "vm22", "h", MergeNone, "aarch64_sme_fmls_vg1x2", [IsStreaming, IsInOutZA], []>; @@ -504,7 +506,7 @@ let TargetGuard = "sme-f16f16" in { def SVMLS_LANE_VG1x4_F16 : Inst<"svmls_lane_za16[_f16]_vg1x4", "vm4di", "h", MergeNone, "aarch64_sme_fmls_lane_vg1x4", [IsStreaming, IsInOutZA], [ImmCheck<3, ImmCheck0_7>]>; } -let TargetGuard = "sme2,b16b16" in { +let SMETargetGuard = "sme2,b16b16" in { def SVMLA_MULTI_VG1x2_BF16 : Inst<"svmla_za16[_bf16]_vg1x2", "vm22", "b", MergeNone, "aarch64_sme_fmla_vg1x2", [IsStreaming, IsInOutZA], []>; def SVMLA_MULTI_VG1x4_BF16 : Inst<"svmla_za16[_bf16]_vg1x4", "vm44", "b", MergeNone, "aarch64_sme_fmla_vg1x4", [IsStreaming, IsInOutZA], []>; def SVMLS_MULTI_VG1x2_BF16 : Inst<"svmls_za16[_bf16]_vg1x2", "vm22", "b", MergeNone, "aarch64_sme_fmls_vg1x2", [IsStreaming, IsInOutZA], []>; @@ -523,7 +525,7 @@ let TargetGuard = "sme2,b16b16" in { // FMLAL/FMLSL/UMLAL/SMLAL // SMLALL/UMLALL/USMLALL/SUMLALL -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { // MULTI MLAL def SVMLAL_MULTI_VG2x2_F16 : Inst<"svmla_za32[_{d}]_vg2x2", "vm22", "bh", MergeNone, "aarch64_sme_fmlal_vg2x2", [IsStreaming, IsInOutZA], []>; def SVMLAL_MULTI_VG2x4_F16 : Inst<"svmla_za32[_{d}]_vg2x4", "vm44", "bh", MergeNone, "aarch64_sme_fmlal_vg2x4", [IsStreaming, IsInOutZA], []>; @@ -653,7 +655,7 @@ let TargetGuard = "sme2" in { def SVUSMLALL_LANE_VG4x4 : Inst<"svusmla_lane_za32[_{d}]_vg4x4", "vm4xi", "Uc", MergeNone, "aarch64_sme_usmla_za32_lane_vg4x4", [IsStreaming, IsInOutZA], [ImmCheck<3, ImmCheck0_15>]>; } -let TargetGuard = "sme2,sme-i16i64" in { +let SMETargetGuard = "sme2,sme-i16i64" in { // MULTI MLAL def SVMLAL_MULTI_VG4x2_S16 : Inst<"svmla_za64[_{d}]_vg4x2", "vm22", "s", MergeNone, "aarch64_sme_smla_za64_vg4x2", [IsStreaming, IsInOutZA], []>; def SVMLAL_MULTI_VG4x2_U16 : Inst<"svmla_za64[_{d}]_vg4x2", "vm22", "Us", MergeNone, "aarch64_sme_umla_za64_vg4x2", [IsStreaming, IsInOutZA], []>; @@ -702,7 +704,7 @@ let TargetGuard = "sme2,sme-i16i64" in { // // Spill and fill of ZT0 // -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { def SVLDR_ZT : Inst<"svldr_zt", "viQ", "", MergeNone, "aarch64_sme_ldr_zt", [IsOverloadNone, IsStreamingCompatible, IsInOutZT0], [ImmCheck<0, ImmCheck0_0>]>; def SVSTR_ZT : Inst<"svstr_zt", "vi%", "", MergeNone, "aarch64_sme_str_zt", [IsOverloadNone, IsStreamingCompatible, IsInZT0], [ImmCheck<0, ImmCheck0_0>]>; } @@ -710,14 +712,14 @@ let TargetGuard = "sme2" in { // // Zero ZT0 // -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { def SVZERO_ZT : Inst<"svzero_zt", "vi", "", MergeNone, "aarch64_sme_zero_zt", [IsOverloadNone, IsStreamingCompatible, IsOutZT0], [ImmCheck<0, ImmCheck0_0>]>; } // // lookup table expand four contiguous registers // -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { def SVLUTI2_LANE_ZT_X4 : Inst<"svluti2_lane_zt_{d}_x4", "4.di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti2_lane_zt_x4", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_3>]>; def SVLUTI4_LANE_ZT_X4 : Inst<"svluti4_lane_zt_{d}_x4", "4.di[i", "sUsiUibhf", MergeNone, "aarch64_sme_luti4_lane_zt_x4", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_1>]>; } @@ -725,7 +727,7 @@ let TargetGuard = "sme2" in { // // lookup table expand one register // -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { def SVLUTI2_LANE_ZT : Inst<"svluti2_lane_zt_{d}", "di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti2_lane_zt", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_15>]>; def SVLUTI4_LANE_ZT : Inst<"svluti4_lane_zt_{d}", "di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti4_lane_zt", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_7>]>; } @@ -733,14 +735,14 @@ let TargetGuard = "sme2" in { // // lookup table expand two contiguous registers // -let TargetGuard = "sme2" in { +let SMETargetGuard = "sme2" in { def SVLUTI2_LANE_ZT_X2 : Inst<"svluti2_lane_zt_{d}_x2", "2.di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti2_lane_zt_x2", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_7>]>; def SVLUTI4_LANE_ZT_X2 : Inst<"svluti4_lane_zt_{d}_x2", "2.di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti4_lane_zt_x2", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_3>]>; } //////////////////////////////////////////////////////////////////////////////// // SME2p1 - FMOPA, FMOPS (non-widening) -let TargetGuard = "sme2,b16b16" in { +let SMETargetGuard = "sme2,b16b16" in { def SVMOPA_BF16_NW : SInst<"svmopa_za16[_bf16]_m", "viPPdd", "b", MergeNone, "aarch64_sme_mopa", [IsStreaming, IsInOutZA], @@ -751,7 +753,7 @@ let TargetGuard = "sme2,b16b16" in { [ImmCheck<0, ImmCheck0_1>]>; } -let TargetGuard = "sme-f16f16" in { +let SMETargetGuard = "sme-f16f16" in { def SVMOPA_F16_NW : SInst<"svmopa_za16[_f16]_m", "viPPdd", "h", MergeNone, "aarch64_sme_mopa", [IsStreaming, IsInOutZA], @@ -761,3 +763,58 @@ let TargetGuard = "sme-f16f16" in { [IsStreaming, IsInOutZA], [ImmCheck<0, ImmCheck0_1>]>; } + + +multiclass ZAReadz ch> { + let SMETargetGuard = "sme2p1" in { + def NAME # _H : SInst<"svreadz_hor_" # n_suffix # "_{d}_vg" # vg_num, vg_num # "im", t, + MergeNone, i_prefix # "_horiz_x" # vg_num, + [IsStreaming, IsInOutZA], ch>; + + def NAME # _V : SInst<"svreadz_ver_" # n_suffix # "_{d}_vg" # vg_num, vg_num # "im", t, + MergeNone, i_prefix # "_vert_x" #vg_num, + [IsStreaming, IsInOutZA], ch>; + } +} + +defm SVREADZ_ZA8_X2 : ZAReadz<"za8", "2", "cUc", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_0>]>; +defm SVREADZ_ZA16_X2 : ZAReadz<"za16", "2", "sUshb", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_1>]>; +defm SVREADZ_ZA32_X2 : ZAReadz<"za32", "2", "iUif", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_3>]>; +defm SVREADZ_ZA64_X2 : ZAReadz<"za64", "2", "lUld", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_7>]>; + +defm SVREADZ_ZA8_X4 : ZAReadz<"za8", "4", "cUc", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_0>]>; +defm SVREADZ_ZA16_X4 : ZAReadz<"za16", "4", "sUshb", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_1>]>; +defm SVREADZ_ZA32_X4 : ZAReadz<"za32", "4", "iUif", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_3>]>; +defm SVREADZ_ZA64_X4 : ZAReadz<"za64", "4", "lUld", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_7>]>; + + +multiclass ZAReadzSingle ch> { + let SMETargetGuard = "sme2p1" in { + def NAME # _H : SInst<"svreadz_hor_" # n_suffix # "_{d}", "dim", t, + MergeNone, i_prefix # "_horiz", + [IsStreaming, IsInOutZA], ch>; + + def NAME # _V : SInst<"svreadz_ver_" # n_suffix # "_{d}", "dim", t, + MergeNone, i_prefix # "_vert", + [IsStreaming, IsInOutZA], ch>; + } +} + +defm SVREADZ_ZA8 : ZAReadzSingle<"za8", "cUc", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_0>]>; +defm SVREADZ_ZA16 : ZAReadzSingle<"za16", "sUshb", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_1>]>; +defm SVREADZ_ZA32 : ZAReadzSingle<"za32", "iUif", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_3>]>; +defm SVREADZ_ZA64 : ZAReadzSingle<"za64", "lUld", "aarch64_sme_readz", [ImmCheck<0, ImmCheck0_7>]>; +defm SVREADZ_ZA128 : ZAReadzSingle<"za128", "csilUcUiUsUlbhfd", "aarch64_sme_readz_q", [ImmCheck<0, ImmCheck0_15>]>; + +multiclass ZAReadzArray{ + let SMETargetGuard = "sme2p1" in { + def NAME # _B : SInst<"svreadz_za8_{d}_vg1x" # vg_num, vg_num # "m", "cUc", MergeNone, "aarch64_sme_readz_x" # vg_num, [IsStreaming, IsInOutZA]>; + def NAME # _H : SInst<"svreadz_za16_{d}_vg1x" # vg_num, vg_num # "m", "sUsbh", MergeNone, "aarch64_sme_readz_x" # vg_num, [IsStreaming, IsInOutZA]>; + def NAME # _S : SInst<"svreadz_za32_{d}_vg1x" # vg_num, vg_num # "m", "iUif", MergeNone, "aarch64_sme_readz_x" # vg_num, [IsStreaming, IsInOutZA]>; + def NAME # _D : SInst<"svreadz_za64_{d}_vg1x" # vg_num, vg_num # "m", "lUld", MergeNone, "aarch64_sme_readz_x" # vg_num, [IsStreaming, IsInOutZA]>; + } +} + +defm SVREADZ_VG2 : ZAReadzArray<"2">; +defm SVREADZ_VG4 : ZAReadzArray<"4">; +} // let SVETargetGuard = InvalidMode diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td index f5972b41e7b50..94c093d891156 100644 --- a/clang/include/clang/Basic/arm_sve.td +++ b/clang/include/clang/Basic/arm_sve.td @@ -27,7 +27,7 @@ def SVLD1UH : MInst<"svld1uh_{d}", "dPX", "ilUiUl", [IsLoad, IsZExtRetu def SVLD1SW : MInst<"svld1sw_{d}", "dPU", "lUl", [IsLoad, VerifyRuntimeMode], MemEltTyInt32, "aarch64_sve_ld1">; def SVLD1UW : MInst<"svld1uw_{d}", "dPY", "lUl", [IsLoad, IsZExtReturn, VerifyRuntimeMode], MemEltTyInt32, "aarch64_sve_ld1">; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVLD1_BF : MInst<"svld1[_{2}]", "dPc", "b", [IsLoad, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_ld1">; def SVLD1_VNUM_BF : MInst<"svld1_vnum[_{2}]", "dPcl", "b", [IsLoad, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_ld1">; } @@ -41,6 +41,7 @@ def SVLD1UH_VNUM : MInst<"svld1uh_vnum_{d}", "dPXl", "ilUiUl", [IsLoad, def SVLD1SW_VNUM : MInst<"svld1sw_vnum_{d}", "dPUl", "lUl", [IsLoad, VerifyRuntimeMode], MemEltTyInt32, "aarch64_sve_ld1">; def SVLD1UW_VNUM : MInst<"svld1uw_vnum_{d}", "dPYl", "lUl", [IsLoad, IsZExtReturn, VerifyRuntimeMode], MemEltTyInt32, "aarch64_sve_ld1">; +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { // Load one vector (vector base) def SVLD1_GATHER_BASES_U : MInst<"svld1_gather[_{2}base]_{d}", "dPu", "ilUiUlfd", [IsGatherLoad], MemEltTyDefault, "aarch64_sve_ld1_gather_scalar_offset">; def SVLD1SB_GATHER_BASES_U : MInst<"svld1sb_gather[_{2}base]_{d}", "dPu", "ilUiUl", [IsGatherLoad], MemEltTyInt8, "aarch64_sve_ld1_gather_scalar_offset">; @@ -136,12 +137,14 @@ def SVLDFF1SH_VNUM : MInst<"svldff1sh_vnum_{d}", "dPTl", "ilUiUl", [IsL def SVLDFF1UH_VNUM : MInst<"svldff1uh_vnum_{d}", "dPXl", "ilUiUl", [IsLoad, IsZExtReturn], MemEltTyInt16, "aarch64_sve_ldff1">; def SVLDFF1SW_VNUM : MInst<"svldff1sw_vnum_{d}", "dPUl", "lUl", [IsLoad], MemEltTyInt32, "aarch64_sve_ldff1">; def SVLDFF1UW_VNUM : MInst<"svldff1uw_vnum_{d}", "dPYl", "lUl", [IsLoad, IsZExtReturn], MemEltTyInt32, "aarch64_sve_ldff1">; +} // let SVETargetGuard = "sve", SMETargetGuard = InvalidMode -let TargetGuard = "sve,bf16" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = InvalidMode in { def SVLDFF1_BF : MInst<"svldff1[_{2}]", "dPc", "b", [IsLoad], MemEltTyDefault, "aarch64_sve_ldff1">; def SVLDFF1_VNUM_BF : MInst<"svldff1_vnum[_{2}]", "dPcl", "b", [IsLoad], MemEltTyDefault, "aarch64_sve_ldff1">; } +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { // First-faulting load one vector (vector base) def SVLDFF1_GATHER_BASES_U : MInst<"svldff1_gather[_{2}base]_{d}", "dPu", "ilUiUlfd", [IsGatherLoad], MemEltTyDefault, "aarch64_sve_ldff1_gather_scalar_offset">; def SVLDFF1SB_GATHER_BASES_U : MInst<"svldff1sb_gather[_{2}base]_{d}", "dPu", "ilUiUl", [IsGatherLoad], MemEltTyInt8, "aarch64_sve_ldff1_gather_scalar_offset">; @@ -236,8 +239,9 @@ def SVLDNF1SH_VNUM : MInst<"svldnf1sh_vnum_{d}", "dPTl", "ilUiUl", [IsL def SVLDNF1UH_VNUM : MInst<"svldnf1uh_vnum_{d}", "dPXl", "ilUiUl", [IsLoad, IsZExtReturn], MemEltTyInt16, "aarch64_sve_ldnf1">; def SVLDNF1SW_VNUM : MInst<"svldnf1sw_vnum_{d}", "dPUl", "lUl", [IsLoad], MemEltTyInt32, "aarch64_sve_ldnf1">; def SVLDNF1UW_VNUM : MInst<"svldnf1uw_vnum_{d}", "dPYl", "lUl", [IsLoad, IsZExtReturn], MemEltTyInt32, "aarch64_sve_ldnf1">; +} // let SVETargetGuard = "sve", SMETargetGuard = InvalidMode -let TargetGuard = "sve,bf16" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = InvalidMode in { def SVLDNF1_BF : MInst<"svldnf1[_{2}]", "dPc", "b", [IsLoad], MemEltTyDefault, "aarch64_sve_ldnf1">; def SVLDNF1_VNUM_BF : MInst<"svldnf1_vnum[_{2}]", "dPcl", "b", [IsLoad], MemEltTyDefault, "aarch64_sve_ldnf1">; } @@ -248,7 +252,7 @@ def SVLDNT1 : MInst<"svldnt1[_{2}]", "dPc", "csilUcUsUiUlhfd", [IsLoad, VerifyRu // Load one vector, unextended load, non-temporal (scalar base, VL displacement) def SVLDNT1_VNUM : MInst<"svldnt1_vnum[_{2}]", "dPcl", "csilUcUsUiUlhfd", [IsLoad, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_ldnt1">; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVLDNT1_BF : MInst<"svldnt1[_{2}]", "dPc", "b", [IsLoad, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_ldnt1">; def SVLDNT1_VNUM_BF : MInst<"svldnt1_vnum[_{2}]", "dPcl", "b", [IsLoad, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_ldnt1">; } @@ -256,13 +260,13 @@ let TargetGuard = "(sve,bf16)|sme" in { // Load one quadword and replicate (scalar base) def SVLD1RQ : SInst<"svld1rq[_{2}]", "dPc", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_ld1rq", [VerifyRuntimeMode]>; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVLD1RQ_BF : SInst<"svld1rq[_{2}]", "dPc", "b", MergeNone, "aarch64_sve_ld1rq", [VerifyRuntimeMode]>; } multiclass StructLoad { def : SInst; - let TargetGuard = "(sve,bf16)|sme" in { + let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def: SInst; } } @@ -278,27 +282,30 @@ defm SVLD3_VNUM : StructLoad<"svld3_vnum[_{2}]", "3Pcl", "aarch64_sve_ld3_sret"> defm SVLD4_VNUM : StructLoad<"svld4_vnum[_{2}]", "4Pcl", "aarch64_sve_ld4_sret">; // Load one octoword and replicate (scalar base) -let TargetGuard = "sve,f64mm" in { +let SVETargetGuard = "sve,f64mm", SMETargetGuard = InvalidMode in { def SVLD1RO : SInst<"svld1ro[_{2}]", "dPc", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_ld1ro">; } -let TargetGuard = "sve,f64mm,bf16" in { +let SVETargetGuard = "sve,f64mm,bf16", SMETargetGuard = InvalidMode in { def SVLD1RO_BF16 : SInst<"svld1ro[_{2}]", "dPc", "b", MergeNone, "aarch64_sve_ld1ro">; } -let TargetGuard = "sve,bf16" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = InvalidMode in { + def SVBFMMLA : SInst<"svbfmmla[_{0}]", "MMdd", "b", MergeNone, "aarch64_sve_bfmmla", [IsOverloadNone]>; +} + +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVBFDOT : SInst<"svbfdot[_{0}]", "MMdd", "b", MergeNone, "aarch64_sve_bfdot", [IsOverloadNone, VerifyRuntimeMode]>; def SVBFMLALB : SInst<"svbfmlalb[_{0}]", "MMdd", "b", MergeNone, "aarch64_sve_bfmlalb", [IsOverloadNone, VerifyRuntimeMode]>; def SVBFMLALT : SInst<"svbfmlalt[_{0}]", "MMdd", "b", MergeNone, "aarch64_sve_bfmlalt", [IsOverloadNone, VerifyRuntimeMode]>; - def SVBFMMLA : SInst<"svbfmmla[_{0}]", "MMdd", "b", MergeNone, "aarch64_sve_bfmmla", [IsOverloadNone, VerifyRuntimeMode]>; def SVBFDOT_N : SInst<"svbfdot[_n_{0}]", "MMda", "b", MergeNone, "aarch64_sve_bfdot", [IsOverloadNone, VerifyRuntimeMode]>; - def SVBFMLAL_N : SInst<"svbfmlalb[_n_{0}]", "MMda", "b", MergeNone, "aarch64_sve_bfmlalb", [IsOverloadNone, VerifyRuntimeMode]>; + def SVBFMLALB_N : SInst<"svbfmlalb[_n_{0}]", "MMda", "b", MergeNone, "aarch64_sve_bfmlalb", [IsOverloadNone, VerifyRuntimeMode]>; def SVBFMLALT_N : SInst<"svbfmlalt[_n_{0}]", "MMda", "b", MergeNone, "aarch64_sve_bfmlalt", [IsOverloadNone, VerifyRuntimeMode]>; def SVBFDOT_LANE : SInst<"svbfdot_lane[_{0}]", "MMddi", "b", MergeNone, "aarch64_sve_bfdot_lane_v2", [IsOverloadNone, VerifyRuntimeMode], [ImmCheck<3, ImmCheck0_3>]>; def SVBFMLALB_LANE : SInst<"svbfmlalb_lane[_{0}]", "MMddi", "b", MergeNone, "aarch64_sve_bfmlalb_lane_v2", [IsOverloadNone, VerifyRuntimeMode], [ImmCheck<3, ImmCheck0_7>]>; def SVBFMLALT_LANE : SInst<"svbfmlalt_lane[_{0}]", "MMddi", "b", MergeNone, "aarch64_sve_bfmlalt_lane_v2", [IsOverloadNone, VerifyRuntimeMode], [ImmCheck<3, ImmCheck0_7>]>; -} +} // let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" -let TargetGuard = "sve2p1" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = InvalidMode in { // Contiguous zero-extending load to quadword (single vector). def SVLD1UWQ : MInst<"svld1uwq[_{d}]", "dPc", "iUif", [IsLoad], MemEltTyInt32, "aarch64_sve_ld1uwq">; def SVLD1UWQ_VNUM : MInst<"svld1uwq_vnum[_{d}]", "dPcl", "iUif", [IsLoad], MemEltTyInt32, "aarch64_sve_ld1uwq">; @@ -351,11 +358,12 @@ def SVST1H_VNUM_U : MInst<"svst1h_vnum[_{d}]", "vPFld", "UiUl", [IsSt def SVST1W_VNUM_S : MInst<"svst1w_vnum[_{d}]", "vPCld", "l", [IsStore, VerifyRuntimeMode], MemEltTyInt32, "aarch64_sve_st1">; def SVST1W_VNUM_U : MInst<"svst1w_vnum[_{d}]", "vPGld", "Ul", [IsStore, VerifyRuntimeMode], MemEltTyInt32, "aarch64_sve_st1">; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVST1_BF : MInst<"svst1[_{d}]", "vPpd", "b", [IsStore, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_st1">; def SVST1_VNUM_BF : MInst<"svst1_vnum[_{d}]", "vPpld", "b", [IsStore, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_st1">; } +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { // Store one vector (vector base) def SVST1_SCATTER_BASES_U : MInst<"svst1_scatter[_{2}base_{d}]", "vPud", "ilUiUlfd", [IsScatterStore], MemEltTyDefault, "aarch64_sve_st1_scatter_scalar_offset">; def SVST1B_SCATTER_BASES_U : MInst<"svst1b_scatter[_{2}base_{d}]", "vPud", "ilUiUl", [IsScatterStore], MemEltTyInt8, "aarch64_sve_st1_scatter_scalar_offset">; @@ -424,10 +432,11 @@ def SVST1H_SCATTER_32B_INDICES_UU : MInst<"svst1h_scatter_[{3}]index[_{d}]", "vP def SVST1_SCATTER_INDEX_S : MInst<"svst1_scatter[_{2}base]_index[_{d}]", "vPuld", "ilUiUlfd", [IsScatterStore], MemEltTyDefault, "aarch64_sve_st1_scatter_scalar_offset">; def SVST1H_SCATTER_INDEX_S : MInst<"svst1h_scatter[_{2}base]_index[_{d}]", "vPuld", "ilUiUl", [IsScatterStore], MemEltTyInt16, "aarch64_sve_st1_scatter_scalar_offset">; def SVST1W_SCATTER_INDEX_S : MInst<"svst1w_scatter[_{2}base]_index[_{d}]", "vPuld", "lUl", [IsScatterStore], MemEltTyInt32, "aarch64_sve_st1_scatter_scalar_offset">; +} // let SVETargetGuard = "sve" multiclass StructStore { def : SInst; - let TargetGuard = "sve,bf16" in { + let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def: SInst; } } @@ -447,12 +456,12 @@ def SVSTNT1 : MInst<"svstnt1[_{d}]", "vPpd", "csilUcUsUiUlhfd", [IsStore, Verify // Store one vector, with no truncation, non-temporal (scalar base, VL displacement) def SVSTNT1_VNUM : MInst<"svstnt1_vnum[_{d}]", "vPpld", "csilUcUsUiUlhfd", [IsStore, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_stnt1">; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVSTNT1_BF : MInst<"svstnt1[_{d}]", "vPpd", "b", [IsStore, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_stnt1">; def SVSTNT1_VNUM_BF : MInst<"svstnt1_vnum[_{d}]", "vPpld", "b", [IsStore, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_stnt1">; } -let TargetGuard = "sve2p1" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = InvalidMode in { // Contiguous truncating store from quadword (single vector). def SVST1UWQ : MInst<"svst1wq[_{d}]", "vPcd", "iUif", [IsStore], MemEltTyInt32, "aarch64_sve_st1wq">; def SVST1UWQ_VNUM : MInst<"svst1wq_vnum[_{d}]", "vPcld", "iUif", [IsStore], MemEltTyInt32, "aarch64_sve_st1wq">; @@ -499,6 +508,7 @@ def SVPRFH_VNUM : MInst<"svprfh_vnum", "vPQlJ", "s", [IsPrefetch, VerifyRuntimeM def SVPRFW_VNUM : MInst<"svprfw_vnum", "vPQlJ", "i", [IsPrefetch, VerifyRuntimeMode], MemEltTyInt32, "aarch64_sve_prf">; def SVPRFD_VNUM : MInst<"svprfd_vnum", "vPQlJ", "l", [IsPrefetch, VerifyRuntimeMode], MemEltTyInt64, "aarch64_sve_prf">; +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { // Prefetch (Vector bases) def SVPRFB_GATHER_BASES : MInst<"svprfb_gather[_{2}base]", "vPdJ", "UiUl", [IsGatherPrefetch], MemEltTyInt8, "aarch64_sve_prfb_gather_scalar_offset">; def SVPRFH_GATHER_BASES : MInst<"svprfh_gather[_{2}base]", "vPdJ", "UiUl", [IsGatherPrefetch], MemEltTyInt16, "aarch64_sve_prfh_gather_scalar_offset">; @@ -531,21 +541,24 @@ def SVPRFB_GATHER_BASES_OFFSET : MInst<"svprfb_gather[_{2}base]_offset", "vPdlJ" def SVPRFH_GATHER_BASES_OFFSET : MInst<"svprfh_gather[_{2}base]_index", "vPdlJ", "UiUl", [IsGatherPrefetch], MemEltTyInt16, "aarch64_sve_prfh_gather_scalar_offset">; def SVPRFW_GATHER_BASES_OFFSET : MInst<"svprfw_gather[_{2}base]_index", "vPdlJ", "UiUl", [IsGatherPrefetch], MemEltTyInt32, "aarch64_sve_prfw_gather_scalar_offset">; def SVPRFD_GATHER_BASES_OFFSET : MInst<"svprfd_gather[_{2}base]_index", "vPdlJ", "UiUl", [IsGatherPrefetch], MemEltTyInt64, "aarch64_sve_prfd_gather_scalar_offset">; +} // let SVETargetGuard = "sve" //////////////////////////////////////////////////////////////////////////////// // Address calculations +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { def SVADRB : SInst<"svadrb[_{0}base]_[{2}]offset", "uud", "ilUiUl", MergeNone, "aarch64_sve_adrb">; def SVADRH : SInst<"svadrh[_{0}base]_[{2}]index", "uud", "ilUiUl", MergeNone, "aarch64_sve_adrh">; def SVADRW : SInst<"svadrw[_{0}base]_[{2}]index", "uud", "ilUiUl", MergeNone, "aarch64_sve_adrw">; def SVADRD : SInst<"svadrd[_{0}base]_[{2}]index", "uud", "ilUiUl", MergeNone, "aarch64_sve_adrd">; +} // let SVETargetGuard = "sve" //////////////////////////////////////////////////////////////////////////////// // Scalar to vector def SVDUPQ_8 : SInst<"svdupq[_n]_{d}", "dssssssssssssssss", "cUc", MergeNone, "", [VerifyRuntimeMode]>; def SVDUPQ_16 : SInst<"svdupq[_n]_{d}", "dssssssss", "sUsh", MergeNone, "", [VerifyRuntimeMode]>; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVDUPQ_BF16 : SInst<"svdupq[_n]_{d}", "dssssssss", "b", MergeNone, "", [VerifyRuntimeMode]>; } def SVDUPQ_32 : SInst<"svdupq[_n]_{d}", "dssss", "iUif", MergeNone, "", [VerifyRuntimeMode]>; @@ -553,7 +566,7 @@ def SVDUPQ_64 : SInst<"svdupq[_n]_{d}", "dss", "lUld", MergeNone, "", [VerifyRu multiclass svdup_base { def NAME : SInst; - let TargetGuard = "(sve,bf16)|sme" in { + let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def _BF16: SInst; } } @@ -644,9 +657,7 @@ def SVDOT_LANE_U : SInst<"svdot_lane[_{d}]", "ddqqi", "UiUl", MergeNone, "aarc //////////////////////////////////////////////////////////////////////////////// // Logical operations -let TargetGuard = "sve|sme" in { // FIXME: Make this the default for most operations. defm SVAND : SInstZPZZ<"svand", "csilUcUsUiUl", "aarch64_sve_and", "aarch64_sve_and_u">; -} defm SVBIC : SInstZPZZ<"svbic", "csilUcUsUiUl", "aarch64_sve_bic", "aarch64_sve_bic_u">; defm SVEOR : SInstZPZZ<"sveor", "csilUcUsUiUl", "aarch64_sve_eor", "aarch64_sve_eor_u">; defm SVORR : SInstZPZZ<"svorr", "csilUcUsUiUl", "aarch64_sve_orr", "aarch64_sve_orr_u">; @@ -684,7 +695,7 @@ def SVASRD_X : SInst<"svasrd[_n_{d}]", "dPdi", "csil", MergeAny, "aa def SVASRD_Z : SInst<"svasrd[_n_{d}]", "dPdi", "csil", MergeZero, "aarch64_sve_asrd", [VerifyRuntimeMode], [ImmCheck<2, ImmCheckShiftRight, 1>]>; def SVINSR : SInst<"svinsr[_n_{d}]", "dds", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_insr", [VerifyRuntimeMode]>; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVINSR_BF16 : SInst<"svinsr[_n_{d}]", "dds", "b", MergeNone, "aarch64_sve_insr", [VerifyRuntimeMode]>; } @@ -773,7 +784,7 @@ defm SVCLS : SInstCLS<"svcls", "csil", "aarch64_sve_cls", [VerifyRunt defm SVCLZ : SInstCLS<"svclz", "csilUcUsUiUl", "aarch64_sve_clz", [VerifyRuntimeMode]>; defm SVCNT : SInstCLS<"svcnt", "csilUcUsUiUlhfd", "aarch64_sve_cnt", [VerifyRuntimeMode]>; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { defm SVCNT_BF16 : SInstCLS<"svcnt", "b", "aarch64_sve_cnt", [VerifyRuntimeMode]>; } @@ -802,9 +813,7 @@ defm SVABS_F : SInstZPZ<"svabs", "hfd", "aarch64_sve_fabs">; defm SVNEG_F : SInstZPZ<"svneg", "hfd", "aarch64_sve_fneg">; defm SVABD_F : SInstZPZZ<"svabd", "hfd", "aarch64_sve_fabd", "aarch64_sve_fabd_u">; -let TargetGuard = "sve|sme" in { // FIXME: Make this the default for most operations. defm SVADD_F : SInstZPZZ<"svadd", "hfd", "aarch64_sve_fadd", "aarch64_sve_fadd_u">; -} defm SVDIV_F : SInstZPZZ<"svdiv", "hfd", "aarch64_sve_fdiv", "aarch64_sve_fdiv_u">; defm SVDIVR_F : SInstZPZZ<"svdivr", "hfd", "aarch64_sve_fdivr", "aarch64_sve_fdiv_u", [ReverseMergeAnyBinOp]>; defm SVMAX_F : SInstZPZZ<"svmax", "hfd", "aarch64_sve_fmax", "aarch64_sve_fmax_u">; @@ -826,10 +835,12 @@ defm SVRINTX : SInstZPZ<"svrintx", "hfd", "aarch64_sve_frintx">; defm SVRINTZ : SInstZPZ<"svrintz", "hfd", "aarch64_sve_frintz">; defm SVSQRT : SInstZPZ<"svsqrt", "hfd", "aarch64_sve_fsqrt">; +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { def SVEXPA : SInst<"svexpa[_{d}]", "du", "hfd", MergeNone, "aarch64_sve_fexpa_x">; def SVTMAD : SInst<"svtmad[_{d}]", "dddi", "hfd", MergeNone, "aarch64_sve_ftmad_x", [], [ImmCheck<2, ImmCheck0_7>]>; def SVTSMUL : SInst<"svtsmul[_{d}]", "ddu", "hfd", MergeNone, "aarch64_sve_ftsmul_x">; def SVTSSEL : SInst<"svtssel[_{d}]", "ddu", "hfd", MergeNone, "aarch64_sve_ftssel_x">; +} def SVSCALE_M : SInst<"svscale[_{d}]", "dPdx", "hfd", MergeOp1, "aarch64_sve_fscale", [VerifyRuntimeMode]>; def SVSCALE_X : SInst<"svscale[_{d}]", "dPdx", "hfd", MergeAny, "aarch64_sve_fscale", [VerifyRuntimeMode]>; @@ -932,7 +943,7 @@ defm SVFCVTZS_S64_F16 : SInstCvtMXZ<"svcvt_s64[_f16]", "ddPO", "dPO", "l", "aar defm SVFCVTZS_S32_F32 : SInstCvtMXZ<"svcvt_s32[_f32]", "ddPM", "dPM", "i", "aarch64_sve_fcvtzs", [IsOverloadCvt]>; defm SVFCVTZS_S64_F32 : SInstCvtMXZ<"svcvt_s64[_f32]", "ddPM", "dPM", "l", "aarch64_sve_fcvtzs_i64f32">; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { defm SVCVT_BF16_F32 : SInstCvtMXZ<"svcvt_bf16[_f32]", "ddPM", "dPM", "b", "aarch64_sve_fcvt_bf16f32">; def SVCVTNT_BF16_F32 : SInst<"svcvtnt_bf16[_f32]", "ddPM", "b", MergeOp1, "aarch64_sve_fcvtnt_bf16f32", [IsOverloadNone, VerifyRuntimeMode]>; } @@ -992,7 +1003,7 @@ defm SVFCVT_F32_F64 : SInstCvtMXZ<"svcvt_f32[_f64]", "MMPd", "MPd", "d", "aarc defm SVFCVT_F64_F16 : SInstCvtMXZ<"svcvt_f64[_f16]", "ddPO", "dPO", "d", "aarch64_sve_fcvt_f64f16">; defm SVFCVT_F64_F32 : SInstCvtMXZ<"svcvt_f64[_f32]", "ddPM", "dPM", "d", "aarch64_sve_fcvt_f64f32">; -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { defm SVCVTLT_F32 : SInstCvtMX<"svcvtlt_f32[_f16]", "ddPh", "dPh", "f", "aarch64_sve_fcvtlt_f32f16">; defm SVCVTLT_F64 : SInstCvtMX<"svcvtlt_f64[_f32]", "ddPh", "dPh", "d", "aarch64_sve_fcvtlt_f64f32">; @@ -1012,7 +1023,7 @@ def SVCVTXNT_F32 : SInst<"svcvtxnt_f32[_f64]", "MMPd", "d", MergeOp1, "aarch6 multiclass SVEPerm { def : SInst; - let TargetGuard = "sve,bf16" in { + let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def: SInst; } } @@ -1022,19 +1033,22 @@ defm SVCLASTA_N : SVEPerm<"svclasta[_n_{d}]", "sPsd", "aarch64_sve_clasta_n">; defm SVCLASTB : SVEPerm<"svclastb[_{d}]", "dPdd", "aarch64_sve_clastb">; defm SVCLASTB_N : SVEPerm<"svclastb[_n_{d}]", "sPsd", "aarch64_sve_clastb_n">; +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { def SVCOMPACT : SInst<"svcompact[_{d}]", "dPd", "ilUiUlfd", MergeNone, "aarch64_sve_compact">; +} + // Note: svdup_lane is implemented using the intrinsic for TBL to represent a // splat of any possible lane. It is upto LLVM to pick a more efficient // instruction such as DUP (indexed) if the lane index fits the range of the // instruction's immediate. def SVDUP_LANE : SInst<"svdup_lane[_{d}]", "ddL", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_tbl", [VerifyRuntimeMode]>; -let TargetGuard = "sve,bf16" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVDUP_LANE_BF16 : SInst<"svdup_lane[_{d}]", "ddL", "b", MergeNone, "aarch64_sve_tbl", [VerifyRuntimeMode]>; } def SVDUPQ_LANE : SInst<"svdupq_lane[_{d}]", "ddn", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_dupq_lane", [VerifyRuntimeMode]>; -let TargetGuard = "sve,bf16" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVDUPQ_LANE_BF16 : SInst<"svdupq_lane[_{d}]", "ddn", "b", MergeNone, "aarch64_sve_dupq_lane", [VerifyRuntimeMode]>; } def SVEXT : SInst<"svext[_{d}]", "dddi", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_ext", [VerifyRuntimeMode], [ImmCheck<2, ImmCheckExtract, 1>]>; @@ -1045,7 +1059,7 @@ def SVSEL : SInst<"svsel[_{d}]", "dPdd", "csilUcUsUiUlhfd", MergeNo def SVSPLICE : SInst<"svsplice[_{d}]", "dPdd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_splice", [VerifyRuntimeMode]>; def SVTBL : SInst<"svtbl[_{d}]", "ddu", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_tbl", [VerifyRuntimeMode]>; -let TargetGuard = "sve,bf16" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVTBL_BF16 : SInst<"svtbl[_{d}]", "ddu", "b", MergeNone, "aarch64_sve_tbl", [VerifyRuntimeMode]>; } @@ -1060,7 +1074,7 @@ def SVUZP2 : SInst<"svuzp2[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNo def SVZIP1 : SInst<"svzip1[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_zip1", [VerifyRuntimeMode]>; def SVZIP2 : SInst<"svzip2[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_zip2", [VerifyRuntimeMode]>; -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVEXT_BF16 : SInst<"svext[_{d}]", "dddi", "b", MergeNone, "aarch64_sve_ext", [VerifyRuntimeMode], [ImmCheck<2, ImmCheckExtract, 1>]>; def SVREV_BF16 : SInst<"svrev[_{d}]", "dd", "b", MergeNone, "aarch64_sve_rev", [VerifyRuntimeMode]>; def SVSEL_BF16 : SInst<"svsel[_{d}]", "dPdd", "b", MergeNone, "aarch64_sve_sel", [VerifyRuntimeMode]>; @@ -1109,11 +1123,8 @@ def SVZIP2_B64 : SInst<"svzip2_b64", "PPP", "Pc", MergeNone, "aarch64_sve_zip // Predicate creation def SVPFALSE : SInst<"svpfalse[_b]", "Pv", "", MergeNone, "", [IsOverloadNone, VerifyRuntimeMode]>; - -let TargetGuard = "sve|sme" in { // FIXME: Make this the default for most operations. def SVPTRUE_PAT : SInst<"svptrue_pat_{d}", "PI", "PcPsPiPl", MergeNone, "aarch64_sve_ptrue", [VerifyRuntimeMode]>; def SVPTRUE : SInst<"svptrue_{d}", "Pv", "PcPsPiPl", MergeNone, "aarch64_sve_ptrue", [IsAppendSVALL, VerifyRuntimeMode]>; -} def SVDUPQ_B8 : SInst<"svdupq[_n]_{d}", "Pssssssssssssssss", "Pc", MergeNone, "", [VerifyRuntimeMode]>; def SVDUPQ_B16 : SInst<"svdupq[_n]_{d}", "Pssssssss", "Ps", MergeNone, "", [VerifyRuntimeMode]>; @@ -1125,9 +1136,7 @@ def SVDUP_N_B : SInst<"svdup[_n]_{d}", "Ps", "PcPsPiPl", MergeNone, "", [V //////////////////////////////////////////////////////////////////////////////// // Predicate operations -let TargetGuard = "sve|sme" in { // FIXME: Make this the default for most operations. def SVAND_B_Z : SInst<"svand[_b]_z", "PPPP", "Pc", MergeNone, "aarch64_sve_and_z", [VerifyRuntimeMode]>; -} def SVBIC_B_Z : SInst<"svbic[_b]_z", "PPPP", "Pc", MergeNone, "aarch64_sve_bic_z", [VerifyRuntimeMode]>; def SVEOR_B_Z : SInst<"sveor[_b]_z", "PPPP", "Pc", MergeNone, "aarch64_sve_eor_z", [VerifyRuntimeMode]>; def SVMOV_B_Z : SInst<"svmov[_b]_z", "PPP", "Pc", MergeNone, "", [VerifyRuntimeMode]>; // Uses custom expansion @@ -1158,10 +1167,12 @@ def SVPTEST_LAST : SInst<"svptest_last", "sPP", "Pc", MergeNone, "aarch64_sve_ //////////////////////////////////////////////////////////////////////////////// // FFR manipulation +let SVETargetGuard = "sve", SMETargetGuard = InvalidMode in { def SVRDFFR : SInst<"svrdffr", "Pv", "Pc", MergeNone, "", [IsOverloadNone]>; def SVRDFFR_Z : SInst<"svrdffr_z", "PP", "Pc", MergeNone, "", [IsOverloadNone]>; def SVSETFFR : SInst<"svsetffr", "vv", "", MergeNone, "", [IsOverloadNone]>; def SVWRFFR : SInst<"svwrffr", "vP", "Pc", MergeNone, "", [IsOverloadNone]>; +} //////////////////////////////////////////////////////////////////////////////// // Counting elements @@ -1179,7 +1190,7 @@ def SVCNTD : SInst<"svcntd", "nv", "", MergeNone, "aarch64_sve_cntd", [IsAppendS def SVCNTP : SInst<"svcntp_{d}", "nPP", "PcPsPiPl", MergeNone, "aarch64_sve_cntp", [VerifyRuntimeMode]>; def SVLEN : SInst<"svlen[_{d}]", "nd", "csilUcUsUiUlhfd", MergeNone, "", [VerifyRuntimeMode]>; -let TargetGuard = "sve,bf16" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVLEN_BF16 : SInst<"svlen[_{d}]", "nd", "b", MergeNone, "", [VerifyRuntimeMode]>; } @@ -1245,11 +1256,13 @@ def SVQINCP_N_S64 : SInst<"svqincp[_n_s64]_{d}", "llP", "PcPsPiPl", MergeNone, " def SVQINCP_N_U32 : SInst<"svqincp[_n_u32]_{d}", "mmP", "PcPsPiPl", MergeNone, "aarch64_sve_uqincp_n32", [VerifyRuntimeMode]>; def SVQINCP_N_U64 : SInst<"svqincp[_n_u64]_{d}", "nnP", "PcPsPiPl", MergeNone, "aarch64_sve_uqincp_n64", [VerifyRuntimeMode]>; -let TargetGuard = "sve,i8mm" in { +let SVETargetGuard = "sve,i8mm", SMETargetGuard = InvalidMode in { def SVMLLA_S32 : SInst<"svmmla[_s32]", "ddqq","i", MergeNone, "aarch64_sve_smmla">; def SVMLLA_U32 : SInst<"svmmla[_u32]", "ddqq","Ui", MergeNone, "aarch64_sve_ummla">; def SVUSMLLA_S32 : SInst<"svusmmla[_s32]", "ddbq","i", MergeNone, "aarch64_sve_usmmla">; +} +let SVETargetGuard = "sve,i8mm", SMETargetGuard = "sme,i8mm"in { def SVUSDOT_S : SInst<"svusdot[_s32]", "ddbq", "i", MergeNone, "aarch64_sve_usdot", [VerifyRuntimeMode]>; def SVUSDOT_N_S : SInst<"svusdot[_n_s32]", "ddbr", "i", MergeNone, "aarch64_sve_usdot", [VerifyRuntimeMode]>; def SVSUDOT_S : SInst<"svsudot[_s32]", "ddqb", "i", MergeNone, "aarch64_sve_usdot", [ReverseUSDOT, VerifyRuntimeMode]>; @@ -1259,32 +1272,31 @@ def SVUSDOT_LANE_S : SInst<"svusdot_lane[_s32]", "ddbqi", "i", MergeNone, "a def SVSUDOT_LANE_S : SInst<"svsudot_lane[_s32]", "ddqbi", "i", MergeNone, "aarch64_sve_sudot_lane", [VerifyRuntimeMode], [ImmCheck<3, ImmCheckLaneIndexDot, 2>]>; } -let TargetGuard = "sve,f32mm" in { +let SVETargetGuard = "sve,f32mm", SMETargetGuard = InvalidMode in { def SVMLLA_F32 : SInst<"svmmla[_f32]", "dddd","f", MergeNone, "aarch64_sve_fmmla">; } -let TargetGuard = "sve,f64mm" in { +let SVETargetGuard = "sve,f64mm", SMETargetGuard = InvalidMode in { def SVMLLA_F64 : SInst<"svmmla[_f64]", "dddd","d", MergeNone, "aarch64_sve_fmmla">; -def SVTRN1Q : SInst<"svtrn1q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_trn1q", [VerifyRuntimeMode]>; -def SVTRN2Q : SInst<"svtrn2q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_trn2q", [VerifyRuntimeMode]>; -def SVUZP1Q : SInst<"svuzp1q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_uzp1q", [VerifyRuntimeMode]>; -def SVUZP2Q : SInst<"svuzp2q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_uzp2q", [VerifyRuntimeMode]>; -def SVZIP1Q : SInst<"svzip1q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_zip1q", [VerifyRuntimeMode]>; -def SVZIP2Q : SInst<"svzip2q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_zip2q", [VerifyRuntimeMode]>; +def SVTRN1Q : SInst<"svtrn1q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_trn1q">; +def SVTRN2Q : SInst<"svtrn2q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_trn2q">; +def SVUZP1Q : SInst<"svuzp1q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_uzp1q">; +def SVUZP2Q : SInst<"svuzp2q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_uzp2q">; +def SVZIP1Q : SInst<"svzip1q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_zip1q">; +def SVZIP2Q : SInst<"svzip2q[_{d}]", "ddd", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_zip2q">; } -let TargetGuard = "sve,bf16,f64mm" in { +let SVETargetGuard = "sve,bf16,f64mm", SMETargetGuard = InvalidMode in { def SVTRN1Q_BF16 : SInst<"svtrn1q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_trn1q">; def SVTRN2Q_BF16 : SInst<"svtrn2q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_trn2q">; -def SVUZP1Q_BF16 : SInst<"svuzp1q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_uzp1q", [VerifyRuntimeMode]>; -def SVUZP2Q_BF16 : SInst<"svuzp2q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_uzp2q", [VerifyRuntimeMode]>; -def SVZIP1Q_BF16 : SInst<"svzip1q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_zip1q", [VerifyRuntimeMode]>; -def SVZIP2Q_BF16 : SInst<"svzip2q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_zip2q", [VerifyRuntimeMode]>; +def SVUZP1Q_BF16 : SInst<"svuzp1q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_uzp1q">; +def SVUZP2Q_BF16 : SInst<"svuzp2q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_uzp2q">; +def SVZIP1Q_BF16 : SInst<"svzip1q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_zip1q">; +def SVZIP2Q_BF16 : SInst<"svzip2q[_{d}]", "ddd", "b", MergeNone, "aarch64_sve_zip2q">; } //////////////////////////////////////////////////////////////////////////////// // Vector creation -let TargetGuard = "sve|sme" in { // FIXME: Make this the default for most operations. def SVUNDEF_1 : SInst<"svundef_{d}", "dv", "csilUcUsUiUlhfd", MergeNone, "", [IsUndef, VerifyRuntimeMode]>; def SVUNDEF_2 : SInst<"svundef2_{d}", "2v", "csilUcUsUiUlhfd", MergeNone, "", [IsUndef, VerifyRuntimeMode]>; def SVUNDEF_3 : SInst<"svundef3_{d}", "3v", "csilUcUsUiUlhfd", MergeNone, "", [IsUndef, VerifyRuntimeMode]>; @@ -1293,9 +1305,8 @@ def SVUNDEF_4 : SInst<"svundef4_{d}", "4v", "csilUcUsUiUlhfd", MergeNone, "", [I def SVCREATE_2 : SInst<"svcreate2[_{d}]", "2dd", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleCreate, VerifyRuntimeMode]>; def SVCREATE_3 : SInst<"svcreate3[_{d}]", "3ddd", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleCreate, VerifyRuntimeMode]>; def SVCREATE_4 : SInst<"svcreate4[_{d}]", "4dddd", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleCreate, VerifyRuntimeMode]>; -} -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVUNDEF_1_BF16 : SInst<"svundef_{d}", "dv", "b", MergeNone, "", [IsUndef, VerifyRuntimeMode]>; def SVUNDEF_2_BF16 : SInst<"svundef2_{d}", "2v", "b", MergeNone, "", [IsUndef, VerifyRuntimeMode]>; def SVUNDEF_3_BF16 : SInst<"svundef3_{d}", "3v", "b", MergeNone, "", [IsUndef, VerifyRuntimeMode]>; @@ -1306,14 +1317,13 @@ def SVCREATE_3_BF16 : SInst<"svcreate3[_{d}]", "3ddd", "b", MergeNone, "", [IsT def SVCREATE_4_BF16 : SInst<"svcreate4[_{d}]", "4dddd", "b", MergeNone, "", [IsTupleCreate, VerifyRuntimeMode]>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVCREATE_2_B : SInst<"svcreate2[_b]", "2dd", "Pc", MergeNone, "", [IsTupleCreate, VerifyRuntimeMode]>; def SVCREATE_4_B : SInst<"svcreate4[_b]", "4dddd", "Pc", MergeNone, "", [IsTupleCreate, VerifyRuntimeMode]>; } //////////////////////////////////////////////////////////////////////////////// // Vector insertion and extraction -let TargetGuard = "sve|sme" in { // FIXME: Make this the default for most operations. def SVGET_2 : SInst<"svget2[_{d}]", "d2i", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_1>]>; def SVGET_3 : SInst<"svget3[_{d}]", "d3i", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_2>]>; def SVGET_4 : SInst<"svget4[_{d}]", "d4i", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; @@ -1321,9 +1331,8 @@ def SVGET_4 : SInst<"svget4[_{d}]", "d4i", "csilUcUsUiUlhfd", MergeNone, "", [Is def SVSET_2 : SInst<"svset2[_{d}]", "22id", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleSet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_1>]>; def SVSET_3 : SInst<"svset3[_{d}]", "33id", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleSet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_2>]>; def SVSET_4 : SInst<"svset4[_{d}]", "44id", "csilUcUsUiUlhfd", MergeNone, "", [IsTupleSet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; -} -let TargetGuard = "(sve,bf16)|sme" in { +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { def SVGET_2_BF16 : SInst<"svget2[_{d}]", "d2i", "b", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_1>]>; def SVGET_3_BF16 : SInst<"svget3[_{d}]", "d3i", "b", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_2>]>; def SVGET_4_BF16 : SInst<"svget4[_{d}]", "d4i", "b", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; @@ -1333,7 +1342,7 @@ def SVSET_3_BF16 : SInst<"svset3[_{d}]", "33id", "b", MergeNone, "", [IsTupleSet def SVSET_4_BF16 : SInst<"svset4[_{d}]", "44id", "b", MergeNone, "", [IsTupleSet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVGET_2_B : SInst<"svget2[_b]", "d2i", "Pc", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_1>]>; def SVGET_4_B : SInst<"svget4[_b]", "d4i", "Pc", MergeNone, "", [IsTupleGet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; @@ -1341,13 +1350,13 @@ let TargetGuard = "sve2p1|sme2" in { def SVSET_4_B : SInst<"svset4[_b]", "44id", "Pc", MergeNone, "", [IsTupleSet, VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVUNDEF_2_B: Inst<"svundef2_b", "2", "Pc", MergeNone, "", [IsUndef, VerifyRuntimeMode], []>; def SVUNDEF_4_B: Inst<"svundef4_b", "4", "Pc", MergeNone, "", [IsUndef, VerifyRuntimeMode], []>; } //////////////////////////////////////////////////////////////////////////////// // SVE2 WhileGE/GT -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVWHILEGE_S32 : SInst<"svwhilege_{d}[_{1}]", "Pkk", "PcPsPiPl", MergeNone, "aarch64_sve_whilege", [IsOverloadWhileOrMultiVecCvt, VerifyRuntimeMode]>; def SVWHILEGE_S64 : SInst<"svwhilege_{d}[_{1}]", "Pll", "PcPsPiPl", MergeNone, "aarch64_sve_whilege", [IsOverloadWhileOrMultiVecCvt, VerifyRuntimeMode]>; def SVWHILEGT_S32 : SInst<"svwhilegt_{d}[_{1}]", "Pkk", "PcPsPiPl", MergeNone, "aarch64_sve_whilegt", [IsOverloadWhileOrMultiVecCvt, VerifyRuntimeMode]>; @@ -1358,7 +1367,7 @@ def SVWHILEHS_U32 : SInst<"svwhilege_{d}[_{1}]", "Pmm", "PUcPUsPUiPUl", MergeNon def SVWHILEHS_U64 : SInst<"svwhilege_{d}[_{1}]", "Pnn", "PUcPUsPUiPUl", MergeNone, "aarch64_sve_whilehs", [IsOverloadWhileOrMultiVecCvt, VerifyRuntimeMode]>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVWHILEGE_S64_X2 : SInst<"svwhilege_{d}[_{1}]_x2", "2ll", "PcPsPiPl", MergeNone, "aarch64_sve_whilege_x2", [VerifyRuntimeMode]>; def SVWHILEGT_S64_X2 : SInst<"svwhilegt_{d}[_{1}]_x2", "2ll", "PcPsPiPl", MergeNone, "aarch64_sve_whilegt_x2", [VerifyRuntimeMode]>; def SVWHILEHI_U64_X2 : SInst<"svwhilegt_{d}[_{1}]_x2", "2nn", "PcPsPiPl", MergeNone, "aarch64_sve_whilehi_x2", [VerifyRuntimeMode]>; @@ -1373,7 +1382,7 @@ let TargetGuard = "sve2p1|sme2" in { //////////////////////////////////////////////////////////////////////////////// // SVE2 - Uniform DSP operations -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { defm SVQADD_S : SInstZPZZ<"svqadd", "csli", "aarch64_sve_sqadd", "aarch64_sve_sqadd">; defm SVQADD_U : SInstZPZZ<"svqadd", "UcUsUiUl", "aarch64_sve_uqadd", "aarch64_sve_uqadd">; defm SVHADD_S : SInstZPZZ<"svhadd", "csli", "aarch64_sve_shadd", "aarch64_sve_shadd">; @@ -1408,7 +1417,7 @@ multiclass SInstZPZxZ; } -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { defm SVQRSHL_S : SInstZPZxZ<"svqrshl", "csil", "dPdx", "dPdK", "aarch64_sve_sqrshl", [VerifyRuntimeMode]>; defm SVQRSHL_U : SInstZPZxZ<"svqrshl", "UcUsUiUl", "dPdx", "dPdK", "aarch64_sve_uqrshl", [VerifyRuntimeMode]>; defm SVQSHL_S : SInstZPZxZ<"svqshl", "csil", "dPdx", "dPdK", "aarch64_sve_sqshl", [VerifyRuntimeMode]>; @@ -1462,7 +1471,7 @@ multiclass SInstPairwise; } -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { defm SVADDP : SInstPairwise<"svaddp", "csliUcUsUiUl", "aarch64_sve_addp", [VerifyRuntimeMode]>; defm SVADDP_F : SInstPairwise<"svaddp", "hfd", "aarch64_sve_faddp", [VerifyRuntimeMode]>; defm SVMAXNMP : SInstPairwise<"svmaxnmp", "hfd", "aarch64_sve_fmaxnmp", [VerifyRuntimeMode]>; @@ -1478,7 +1487,7 @@ defm SVMINP_U : SInstPairwise<"svminp", "UcUsUiUl", "aarch64_sve_uminp", [ //////////////////////////////////////////////////////////////////////////////// // SVE2 - Widening pairwise arithmetic -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVADALP_S_M : SInst<"svadalp[_{d}]", "dPdh", "sil", MergeOp1, "aarch64_sve_sadalp", [VerifyRuntimeMode]>; def SVADALP_S_X : SInst<"svadalp[_{d}]", "dPdh", "sil", MergeAny, "aarch64_sve_sadalp", [VerifyRuntimeMode]>; def SVADALP_S_Z : SInst<"svadalp[_{d}]", "dPdh", "sil", MergeZero, "aarch64_sve_sadalp", [VerifyRuntimeMode]>; @@ -1492,7 +1501,7 @@ def SVADALP_U_Z : SInst<"svadalp[_{d}]", "dPdh", "UsUiUl", MergeZero, "aarch64_s // SVE2 - Bitwise ternary logical instructions // -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVBCAX : SInst<"svbcax[_{d}]", "dddd", "csilUcUsUiUl", MergeNone, "aarch64_sve_bcax", [VerifyRuntimeMode]>; def SVBSL : SInst<"svbsl[_{d}]", "dddd", "csilUcUsUiUl", MergeNone, "aarch64_sve_bsl", [VerifyRuntimeMode]>; def SVBSL1N : SInst<"svbsl1n[_{d}]", "dddd", "csilUcUsUiUl", MergeNone, "aarch64_sve_bsl1n", [VerifyRuntimeMode]>; @@ -1512,7 +1521,7 @@ def SVXAR_N : SInst<"svxar[_n_{d}]", "dddi", "csilUcUsUiUl", MergeNone, "aar //////////////////////////////////////////////////////////////////////////////// // SVE2 - Large integer arithmetic -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVADCLB : SInst<"svadclb[_{d}]", "dddd", "UiUl", MergeNone, "aarch64_sve_adclb", [VerifyRuntimeMode]>; def SVADCLT : SInst<"svadclt[_{d}]", "dddd", "UiUl", MergeNone, "aarch64_sve_adclt", [VerifyRuntimeMode]>; def SVSBCLB : SInst<"svsbclb[_{d}]", "dddd", "UiUl", MergeNone, "aarch64_sve_sbclb", [VerifyRuntimeMode]>; @@ -1527,7 +1536,7 @@ def SVSBCLT_N : SInst<"svsbclt[_n_{d}]", "ddda", "UiUl", MergeNone, "aarch64_sve //////////////////////////////////////////////////////////////////////////////// // SVE2 - Multiplication by indexed elements -let TargetGuard = "sve2|sme" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVMLA_LANE_2 : SInst<"svmla_lane[_{d}]", "ddddi", "silUsUiUl", MergeNone, "aarch64_sve_mla_lane", [VerifyRuntimeMode], [ImmCheck<3, ImmCheckLaneIndex, 2>]>; def SVMLS_LANE_2 : SInst<"svmls_lane[_{d}]", "ddddi", "silUsUiUl", MergeNone, "aarch64_sve_mls_lane", [VerifyRuntimeMode], [ImmCheck<3, ImmCheckLaneIndex, 2>]>; def SVMUL_LANE_2 : SInst<"svmul_lane[_{d}]", "dddi", "silUsUiUl", MergeNone, "aarch64_sve_mul_lane", [VerifyRuntimeMode], [ImmCheck<2, ImmCheckLaneIndex, 1>]>; @@ -1535,7 +1544,7 @@ def SVMUL_LANE_2 : SInst<"svmul_lane[_{d}]", "dddi", "silUsUiUl", MergeNone, "a //////////////////////////////////////////////////////////////////////////////// // SVE2 - Uniform complex integer arithmetic -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVCADD : SInst<"svcadd[_{d}]", "dddi", "csilUcUsUiUl", MergeNone, "aarch64_sve_cadd_x", [VerifyRuntimeMode], [ImmCheck<2, ImmCheckComplexRot90_270>]>; def SVSQCADD : SInst<"svqcadd[_{d}]", "dddi", "csil", MergeNone, "aarch64_sve_sqcadd_x", [VerifyRuntimeMode], [ImmCheck<2, ImmCheckComplexRot90_270>]>; def SVCMLA : SInst<"svcmla[_{d}]", "ddddi", "csilUcUsUiUl", MergeNone, "aarch64_sve_cmla_x", [VerifyRuntimeMode], [ImmCheck<3, ImmCheckComplexRotAll90>]>; @@ -1564,7 +1573,7 @@ multiclass SInstWideDSPWide { def _N : SInst; } -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { defm SVABALB_S : SInstWideDSPAcc<"svabalb", "sil", "aarch64_sve_sabalb">; defm SVABALB_U : SInstWideDSPAcc<"svabalb", "UsUiUl", "aarch64_sve_uabalb">; defm SVABALT_S : SInstWideDSPAcc<"svabalt", "sil", "aarch64_sve_sabalt">; @@ -1643,7 +1652,7 @@ def SVQDMULLT_LANE : SInst<"svqdmullt_lane[_{d}]", "dhhi", "il", MergeNone, " //////////////////////////////////////////////////////////////////////////////// // SVE2 - Narrowing DSP operations -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVADDHNB : SInst<"svaddhnb[_{d}]", "hdd", "silUsUiUl", MergeNone, "aarch64_sve_addhnb", [VerifyRuntimeMode]>; def SVADDHNT : SInst<"svaddhnt[_{d}]", "hhdd", "silUsUiUl", MergeNone, "aarch64_sve_addhnt", [VerifyRuntimeMode]>; def SVRADDHNB : SInst<"svraddhnb[_{d}]", "hdd", "silUsUiUl", MergeNone, "aarch64_sve_raddhnb", [VerifyRuntimeMode]>; @@ -1683,7 +1692,7 @@ def SVQRSHRNT_U : SInst<"svqrshrnt[_n_{d}]", "hhdi", "UsUiUl", MergeNone, " //////////////////////////////////////////////////////////////////////////////// // SVE2 - Unary narrowing operations -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVQXTNB_S : SInst<"svqxtnb[_{d}]", "hd", "sil", MergeNone, "aarch64_sve_sqxtnb", [VerifyRuntimeMode]>; def SVQXTNB_U : SInst<"svqxtnb[_{d}]", "hd", "UsUiUl", MergeNone, "aarch64_sve_uqxtnb", [VerifyRuntimeMode]>; def SVQXTUNB_S : SInst<"svqxtunb[_{d}]", "ed", "sil", MergeNone, "aarch64_sve_sqxtunb", [VerifyRuntimeMode]>; @@ -1696,7 +1705,7 @@ def SVQXTUNT_S : SInst<"svqxtunt[_{d}]", "eed", "sil", MergeNone, "aarch64_s //////////////////////////////////////////////////////////////////////////////// // SVE2 - Widening complex integer arithmetic -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { defm SVADDLBT : SInstWideDSPLong<"svaddlbt", "sil", "aarch64_sve_saddlbt">; defm SVSUBLBT : SInstWideDSPLong<"svsublbt", "sil", "aarch64_sve_ssublbt">; defm SVSUBLTB : SInstWideDSPLong<"svsubltb", "sil", "aarch64_sve_ssubltb">; @@ -1707,7 +1716,7 @@ defm SVQDMLSLBT : SInstWideDSPAcc<"svqdmlslbt", "sil", "aarch64_sve_sqdmlslbt">; //////////////////////////////////////////////////////////////////////////////// // SVE2 - Non-temporal gather/scatter -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = InvalidMode in { // Non-temporal gather load one vector (vector base) def SVLDNT1_GATHER_BASES_U : MInst<"svldnt1_gather[_{2}base]_{0}", "dPu", "ilUiUlfd", [IsGatherLoad], MemEltTyDefault, "aarch64_sve_ldnt1_gather_scalar_offset">; def SVLDNT1SB_GATHER_BASES_U : MInst<"svldnt1sb_gather[_{2}base]_{0}", "dPu", "ilUiUl", [IsGatherLoad], MemEltTyInt8, "aarch64_sve_ldnt1_gather_scalar_offset">; @@ -1830,7 +1839,7 @@ def SVSTNT1W_SCATTER_INDEX_S : MInst<"svstnt1w_scatter[_{2}base]_index[_{d}]", " //////////////////////////////////////////////////////////////////////////////// // SVE2 - Polynomial arithmetic -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVEORBT : SInst<"sveorbt[_{d}]", "dddd", "csilUcUsUiUl", MergeNone, "aarch64_sve_eorbt", [VerifyRuntimeMode]>; def SVEORBT_N : SInst<"sveorbt[_n_{d}]", "ddda", "csilUcUsUiUl", MergeNone, "aarch64_sve_eorbt", [VerifyRuntimeMode]>; def SVEORTB : SInst<"sveortb[_{d}]", "dddd", "csilUcUsUiUl", MergeNone, "aarch64_sve_eortb", [VerifyRuntimeMode]>; @@ -1850,7 +1859,7 @@ def SVPMULLT_PAIR_N : SInst<"svpmullt_pair[_n_{d}]", "dda", "UcUi", Mer //////////////////////////////////////////////////////////////////////////////// // SVE2 - Complex integer dot product -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVCDOT : SInst<"svcdot[_{d}]", "ddqqi", "il", MergeNone, "aarch64_sve_cdot", [VerifyRuntimeMode], [ImmCheck<3, ImmCheckComplexRotAll90>]>; def SVCDOT_LANE : SInst<"svcdot_lane[_{d}]", "ddqqii", "il", MergeNone, "aarch64_sve_cdot_lane", [VerifyRuntimeMode], [ImmCheck<4, ImmCheckComplexRotAll90>, ImmCheck<3, ImmCheckLaneIndexDot, 2>]>; @@ -1859,7 +1868,7 @@ def SVCDOT_LANE : SInst<"svcdot_lane[_{d}]", "ddqqii", "il", MergeNone, "aarch //////////////////////////////////////////////////////////////////////////////// // SVE2 - Floating-point widening multiply-accumulate -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVMLALB_F : SInst<"svmlalb[_{d}]", "ddhh", "f", MergeNone, "aarch64_sve_fmlalb", [VerifyRuntimeMode]>; def SVMLALB_F_N : SInst<"svmlalb[_n_{d}]", "ddhR", "f", MergeNone, "aarch64_sve_fmlalb", [VerifyRuntimeMode]>; def SVMLALB_F_LANE : SInst<"svmlalb_lane[_{d}]", "ddhhi", "f", MergeNone, "aarch64_sve_fmlalb_lane", [VerifyRuntimeMode], [ImmCheck<3, ImmCheckLaneIndex, 2>]>; @@ -1877,7 +1886,7 @@ def SVMLSLT_F_LANE : SInst<"svmlslt_lane[_{d}]", "ddhhi", "f", MergeNone, "aar //////////////////////////////////////////////////////////////////////////////// // SVE2 - Floating-point integer binary logarithm -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVLOGB_M : SInst<"svlogb[_{d}]", "xxPd", "hfd", MergeOp1, "aarch64_sve_flogb", [VerifyRuntimeMode]>; def SVLOGB_X : SInst<"svlogb[_{d}]", "xPd", "hfd", MergeAnyExp, "aarch64_sve_flogb", [VerifyRuntimeMode]>; def SVLOGB_Z : SInst<"svlogb[_{d}]", "xPd", "hfd", MergeZeroExp, "aarch64_sve_flogb", [VerifyRuntimeMode]>; @@ -1886,7 +1895,7 @@ def SVLOGB_Z : SInst<"svlogb[_{d}]", "xPd", "hfd", MergeZeroExp, "aarch64_sve_ //////////////////////////////////////////////////////////////////////////////// // SVE2 - Vector Histogram count -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = InvalidMode in { def SVHISTCNT : SInst<"svhistcnt[_{d}]_z", "uPdd", "ilUiUl", MergeNone, "aarch64_sve_histcnt">; def SVHISTSEG : SInst<"svhistseg[_{d}]", "udd", "cUc", MergeNone, "aarch64_sve_histseg">; } @@ -1894,14 +1903,14 @@ def SVHISTSEG : SInst<"svhistseg[_{d}]", "udd", "cUc", MergeNone, "aarch6 //////////////////////////////////////////////////////////////////////////////// // SVE2 - Character match -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = InvalidMode in { def SVMATCH : SInst<"svmatch[_{d}]", "PPdd", "csUcUs", MergeNone, "aarch64_sve_match">; def SVNMATCH : SInst<"svnmatch[_{d}]", "PPdd", "csUcUs", MergeNone, "aarch64_sve_nmatch">; } //////////////////////////////////////////////////////////////////////////////// // SVE2 - Contiguous conflict detection -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVWHILERW_B : SInst<"svwhilerw[_{1}]", "Pcc", "cUc", MergeNone, "aarch64_sve_whilerw_b", [IsOverloadWhileRW, VerifyRuntimeMode]>; def SVWHILERW_H : SInst<"svwhilerw[_{1}]", "Pcc", "sUsh", MergeNone, "aarch64_sve_whilerw_h", [IsOverloadWhileRW, VerifyRuntimeMode]>; def SVWHILERW_S : SInst<"svwhilerw[_{1}]", "Pcc", "iUif", MergeNone, "aarch64_sve_whilerw_s", [IsOverloadWhileRW, VerifyRuntimeMode]>; @@ -1913,19 +1922,19 @@ def SVWHILEWR_S : SInst<"svwhilewr[_{1}]", "Pcc", "iUif", MergeNone, "aarch64_sv def SVWHILEWR_D : SInst<"svwhilewr[_{1}]", "Pcc", "lUld", MergeNone, "aarch64_sve_whilewr_d", [IsOverloadWhileRW, VerifyRuntimeMode]>; } -let TargetGuard = "sve2,bf16" in { +let SVETargetGuard = "sve2,bf16", SMETargetGuard = "sme,bf16" in { def SVWHILERW_H_BF16 : SInst<"svwhilerw[_{1}]", "Pcc", "b", MergeNone, "aarch64_sve_whilerw_h", [IsOverloadWhileRW, VerifyRuntimeMode]>; def SVWHILEWR_H_BF16 : SInst<"svwhilewr[_{1}]", "Pcc", "b", MergeNone, "aarch64_sve_whilewr_h", [IsOverloadWhileRW, VerifyRuntimeMode]>; } //////////////////////////////////////////////////////////////////////////////// // SVE2 - Extended table lookup/permute -let TargetGuard = "sve2" in { +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { def SVTBL2 : SInst<"svtbl2[_{d}]", "d2u", "csilUcUsUiUlhfd", MergeNone, "", [VerifyRuntimeMode]>; def SVTBX : SInst<"svtbx[_{d}]", "dddu", "csilUcUsUiUlhfd", MergeNone, "aarch64_sve_tbx", [VerifyRuntimeMode]>; } -let TargetGuard = "sve2,bf16" in { +let SVETargetGuard = "sve2,bf16", SMETargetGuard = "sme,bf16" in { def SVTBL2_BF16 : SInst<"svtbl2[_{d}]", "d2u", "b", MergeNone, "", [VerifyRuntimeMode]>; def SVTBX_BF16 : SInst<"svtbx[_{d}]", "dddu", "b", MergeNone, "aarch64_sve_tbx", [VerifyRuntimeMode]>; } @@ -1933,7 +1942,7 @@ def SVTBX_BF16 : SInst<"svtbx[_{d}]", "dddu", "b", MergeNone, "aarch64_sve_tbx //////////////////////////////////////////////////////////////////////////////// // SVE2 - Optional -let TargetGuard = "sve2-aes" in { +let SVETargetGuard = "sve2-aes", SMETargetGuard = InvalidMode in { def SVAESD : SInst<"svaesd[_{d}]", "ddd", "Uc", MergeNone, "aarch64_sve_aesd", [IsOverloadNone]>; def SVAESIMC : SInst<"svaesimc[_{d}]", "dd", "Uc", MergeNone, "aarch64_sve_aesimc", [IsOverloadNone]>; def SVAESE : SInst<"svaese[_{d}]", "ddd", "Uc", MergeNone, "aarch64_sve_aese", [IsOverloadNone]>; @@ -1946,16 +1955,16 @@ def SVPMULLT_PAIR_U64 : SInst<"svpmullt_pair[_{d}]", "ddd", "Ul", MergeNone, def SVPMULLT_PAIR_N_U64 : SInst<"svpmullt_pair[_n_{d}]", "dda", "Ul", MergeNone, "aarch64_sve_pmullt_pair">; } -let TargetGuard = "sve2-sha3" in { +let SVETargetGuard = "sve2-sha3", SMETargetGuard = InvalidMode in { // FIXME: valid from sme2p1. def SVRAX1 : SInst<"svrax1[_{d}]", "ddd", "lUl", MergeNone, "aarch64_sve_rax1", [IsOverloadNone]>; } -let TargetGuard = "sve2-sm4" in { +let SVETargetGuard = "sve2-sm4", SMETargetGuard = InvalidMode in { def SVSM4E : SInst<"svsm4e[_{d}]", "ddd", "Ui", MergeNone, "aarch64_sve_sm4e", [IsOverloadNone]>; def SVSM4EKEY : SInst<"svsm4ekey[_{d}]", "ddd", "Ui", MergeNone, "aarch64_sve_sm4ekey", [IsOverloadNone]>; } -let TargetGuard = "sve2-bitperm" in { +let SVETargetGuard = "sve2-bitperm", SMETargetGuard = InvalidMode in { def SVBDEP : SInst<"svbdep[_{d}]", "ddd", "UcUsUiUl", MergeNone, "aarch64_sve_bdep_x">; def SVBDEP_N : SInst<"svbdep[_n_{d}]", "dda", "UcUsUiUl", MergeNone, "aarch64_sve_bdep_x">; def SVBEXT : SInst<"svbext[_{d}]", "ddd", "UcUsUiUl", MergeNone, "aarch64_sve_bext_x">; @@ -1964,7 +1973,7 @@ def SVBGRP : SInst<"svbgrp[_{d}]", "ddd", "UcUsUiUl", MergeNone, "aarch64_sv def SVBGRP_N : SInst<"svbgrp[_n_{d}]", "dda", "UcUsUiUl", MergeNone, "aarch64_sve_bgrp_x">; } -let TargetGuard = "sve2p1|sme" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme" in { def SVPSEL_B : SInst<"svpsel_lane_b8", "PPPm", "Pc", MergeNone, "", [VerifyRuntimeMode], []>; def SVPSEL_H : SInst<"svpsel_lane_b16", "PPPm", "Ps", MergeNone, "", [VerifyRuntimeMode], []>; def SVPSEL_S : SInst<"svpsel_lane_b32", "PPPm", "Pi", MergeNone, "", [VerifyRuntimeMode], []>; @@ -1972,7 +1981,7 @@ def SVPSEL_D : SInst<"svpsel_lane_b64", "PPPm", "Pl", MergeNone, "", [VerifyRunt } // Standalone sve2.1 builtins -let TargetGuard = "sve2p1" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = InvalidMode in { def SVORQV : SInst<"svorqv[_{d}]", "{Pd", "csilUcUsUiUl", MergeNone, "aarch64_sve_orqv", [IsReductionQV]>; def SVEORQV : SInst<"sveorqv[_{d}]", "{Pd", "csilUcUsUiUl", MergeNone, "aarch64_sve_eorqv", [IsReductionQV]>; def SVADDQV : SInst<"svaddqv[_{d}]", "{Pd", "csilUcUsUiUl", MergeNone, "aarch64_sve_addqv", [IsReductionQV]>; @@ -1989,7 +1998,7 @@ def SVFMAXQV : SInst<"svmaxqv[_{d}]", "{Pd", "hfd", MergeNone, "aarch64_sve_ def SVFMINQV : SInst<"svminqv[_{d}]", "{Pd", "hfd", MergeNone, "aarch64_sve_fminqv", [IsReductionQV]>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVPEXT_SINGLE : SInst<"svpext_lane_{d}", "P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; def SVPEXT_X2 : SInst<"svpext_lane_{d}_x2", "2.P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext_x2", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_1>]>; @@ -2028,7 +2037,7 @@ multiclass MultiVecLoad { def SV # NAME # D_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}]_x4", "4}cl", "lUld", [IsStructLoad, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { defm LD1 : MultiVecLoad<"ld1">; defm LDNT1 : MultiVecLoad<"ldnt1">; } @@ -2053,12 +2062,12 @@ multiclass MultiVecStore { def SV # NAME # D_VNUM_X4 : MInst<"sv" # i # "_vnum" # "[_{2}_x4]", "v}pl4", "lUld", [IsStructStore, VerifyRuntimeMode], MemEltTyDefault, "aarch64_sve_" # i # "_pn_x4">; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { defm ST1 : MultiVecStore<"st1">; defm STNT1 : MultiVecStore<"stnt1">; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVDOT_X2_S : SInst<"svdot[_{d}_{2}]", "ddhh", "i", MergeNone, "aarch64_sve_sdot_x2", [VerifyRuntimeMode], []>; def SVDOT_X2_U : SInst<"svdot[_{d}_{2}]", "ddhh", "Ui", MergeNone, "aarch64_sve_udot_x2", [VerifyRuntimeMode], []>; def SVDOT_X2_F : SInst<"svdot[_{d}_{2}]", "ddhh", "f", MergeNone, "aarch64_sve_fdot_x2", [VerifyRuntimeMode], []>; @@ -2067,14 +2076,14 @@ def SVDOT_LANE_X2_U : SInst<"svdot_lane[_{d}_{2}]", "ddhhi", "Ui", MergeNone, "a def SVDOT_LANE_X2_F : SInst<"svdot_lane[_{d}_{2}]", "ddhhi", "f", MergeNone, "aarch64_sve_fdot_lane_x2", [VerifyRuntimeMode], [ImmCheck<3, ImmCheck0_3>]>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme" in { def SVSCLAMP : SInst<"svclamp[_{d}]", "dddd", "csil", MergeNone, "aarch64_sve_sclamp", [VerifyRuntimeMode], []>; def SVUCLAMP : SInst<"svclamp[_{d}]", "dddd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp", [VerifyRuntimeMode], []>; defm SVREVD : SInstZPZ<"svrevd", "csilUcUsUiUlbhfd", "aarch64_sve_revd">; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVPTRUE_COUNT : SInst<"svptrue_{d}", "}v", "QcQsQiQl", MergeNone, "aarch64_sve_ptrue_{d}", [IsOverloadNone, VerifyRuntimeMode], []>; def SVPFALSE_COUNT_ALIAS : SInst<"svpfalse_c", "}v", "", MergeNone, "", [IsOverloadNone, VerifyRuntimeMode]>; @@ -2083,7 +2092,7 @@ let TargetGuard = "sve2p1|sme2" in { def SVCNTP_COUNT : SInst<"svcntp_{d}", "n}i", "QcQsQiQl", MergeNone, "aarch64_sve_cntp_{d}", [IsOverloadNone, VerifyRuntimeMode], [ImmCheck<1, ImmCheck2_4_Mul2>]>; } -let TargetGuard = "(sve2|sme2),b16b16" in { +let SVETargetGuard = "sve2,b16b16", SMETargetGuard = "sme2,b16b16" in { defm SVMUL_BF : SInstZPZZ<"svmul", "b", "aarch64_sve_fmul", "aarch64_sve_fmul_u", [VerifyRuntimeMode]>; defm SVADD_BF : SInstZPZZ<"svadd", "b", "aarch64_sve_fadd", "aarch64_sve_fadd_u", [VerifyRuntimeMode]>; defm SVSUB_BF : SInstZPZZ<"svsub", "b", "aarch64_sve_fsub", "aarch64_sve_fsub_u", [VerifyRuntimeMode]>; @@ -2110,7 +2119,7 @@ multiclass MinMaxIntr { def SVF # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "bhfd", MergeNone, "aarch64_sve_f" # i # zm # "_" # mul, [IsStreaming], []>; } -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { // == SMAX / UMAX / FMAX == defm MAX_SINGLE_X2 : MinMaxIntr<"max", "_single", "x2", "22d">; defm MAX_MULTI_X2 : MinMaxIntr<"max", "", "x2", "222">; @@ -2132,13 +2141,13 @@ multiclass SInstMinMaxByVector { def NAME # _X4 : SInst<"sv" # name # "nm[_{d}_x4]", "444", "bhfd", MergeNone, "aarch64_sve_f" # name # "nm_x4", [IsStreaming], []>; } -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { // == FMINNM / FMAXNM == defm SVMINNM : SInstMinMaxByVector<"min">; defm SVMAXNM : SInstMinMaxByVector<"max">; } -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { // FRINTA / FRINTM / FRINTN / FRINTP def SVRINTA_X2 : SInst<"svrinta[_{d}_x2]", "22", "f", MergeNone, "aarch64_sve_frinta_x2", [IsStreaming], []>; def SVRINTA_X4 : SInst<"svrinta[_{d}_x4]", "44", "f", MergeNone, "aarch64_sve_frinta_x4", [IsStreaming], []>; @@ -2153,7 +2162,7 @@ let TargetGuard = "sme2" in { def SVRINTP_X4 : SInst<"svrintp[_{d}_x4]", "44", "f", MergeNone, "aarch64_sve_frintp_x4", [IsStreaming], []>; } -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVSCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "csil", MergeNone, "aarch64_sve_sclamp_single_x2", [IsStreaming], []>; def SVUCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp_single_x2", [IsStreaming], []>; def SVFCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "hfd", MergeNone, "aarch64_sve_fclamp_single_x2", [IsStreaming], []>; @@ -2163,12 +2172,12 @@ let TargetGuard = "sme2" in { def SVFCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "hfd", MergeNone, "aarch64_sve_fclamp_single_x4", [IsStreaming], []>; } -let TargetGuard = "sme2,b16b16"in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2,b16b16"in { def SVBFCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "b", MergeNone, "aarch64_sve_bfclamp_single_x2", [IsStreaming], []>; def SVBFCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "b", MergeNone, "aarch64_sve_bfclamp_single_x4", [IsStreaming], []>; } -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { // == ADD (vectors) == def SVADD_SINGLE_X2 : SInst<"svadd[_single_{d}_x2]", "22d", "cUcsUsiUilUl", MergeNone, "aarch64_sve_add_single_x2", [IsStreaming], []>; def SVADD_SINGLE_X4 : SInst<"svadd[_single_{d}_x4]", "44d", "cUcsUsiUilUl", MergeNone, "aarch64_sve_add_single_x4", [IsStreaming], []>; @@ -2210,7 +2219,7 @@ let TargetGuard = "sme2" in { def SVSQDMULH_X4 : SInst<"svqdmulh[_{d}_x4]", "444", "csil", MergeNone, "aarch64_sve_sqdmulh_vgx4", [IsStreaming], []>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def REINTERPRET_SVBOOL_TO_SVCOUNT : Inst<"svreinterpret[_c]", "}P", "Pc", MergeNone, "", [VerifyRuntimeMode], []>; def REINTERPRET_SVCOUNT_TO_SVBOOL : Inst<"svreinterpret[_b]", "P}", "Pc", MergeNone, "", [VerifyRuntimeMode], []>; @@ -2222,7 +2231,7 @@ let TargetGuard = "sve2p1|sme2" in { def SVSQRSHRUN_X2 : SInst<"svqrshrun[_n]_{0}[_{d}_x2]", "e2i", "i", MergeNone, "aarch64_sve_sqrshrun_x2", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck1_16>]>; } -let TargetGuard = "sve2p1" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = InvalidMode in { // ZIPQ1, ZIPQ2, UZPQ1, UZPQ2 def SVZIPQ1 : SInst<"svzipq1[_{d}]", "ddd", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_zipq1", [], []>; def SVZIPQ2 : SInst<"svzipq2[_{d}]", "ddd", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_zipq2", [], []>; @@ -2233,15 +2242,7 @@ let TargetGuard = "sve2p1" in { def SVTBXQ : SInst<"svtbxq[_{d}]", "dddu", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_tbxq">; // EXTQ def EXTQ : SInst<"svextq[_{d}]", "dddk", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_extq", [], [ImmCheck<2, ImmCheck0_15>]>; - // DUPQ - def SVDUP_LANEQ_B : SInst<"svdup_laneq[_{d}]", "ddi", "cUc", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_15>]>; - def SVDUP_LANEQ_H : SInst<"svdup_laneq[_{d}]", "ddi", "sUsh", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_7>]>; - def SVDUP_LANEQ_S : SInst<"svdup_laneq[_{d}]", "ddi", "iUif", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; - def SVDUP_LANEQ_D : SInst<"svdup_laneq[_{d}]", "ddi", "lUld", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_1>]>; - let TargetGuard = "bf16" in { - def SVDUP_LANEQ_BF16 : SInst<"svdup_laneq[_{d}]", "ddi", "b", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_7>]>; - } // PMOV // Move to Pred multiclass PMOV_TO_PRED flags=[], ImmCheckType immCh > { @@ -2264,10 +2265,22 @@ let TargetGuard = "sve2p1" in { defm SVPMOV_TO_VEC_LANE_D : PMOV_TO_VEC<"svpmov", "lUl", "aarch64_sve_pmov_to_vector_lane" ,[], ImmCheck1_7>; } +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2p1" in { + // DUPQ + def SVDUP_LANEQ_B : SInst<"svdup_laneq[_{d}]", "ddi", "cUc", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_15>]>; + def SVDUP_LANEQ_H : SInst<"svdup_laneq[_{d}]", "ddi", "sUsh", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_7>]>; + def SVDUP_LANEQ_S : SInst<"svdup_laneq[_{d}]", "ddi", "iUif", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_3>]>; + def SVDUP_LANEQ_D : SInst<"svdup_laneq[_{d}]", "ddi", "lUld", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_1>]>; +} + +let SVETargetGuard = "sve2p1,bf16", SMETargetGuard = "sme2p1,bf16" in { + def SVDUP_LANEQ_BF16 : SInst<"svdup_laneq[_{d}]", "ddi", "b", MergeNone, "aarch64_sve_dup_laneq", [VerifyRuntimeMode], [ImmCheck<1, ImmCheck0_7>]>; +} + // // Multi-vector convert to/from floating-point. // -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVCVT_F16_X2 : SInst<"svcvt_f16[_f32_x2]", "e2", "f", MergeNone, "aarch64_sve_fcvt_x2", [IsStreaming],[]>; def SVCVT_BF16_X2 : SInst<"svcvt_bf16[_f32_x2]", "$2", "f", MergeNone, "aarch64_sve_bfcvt_x2", [IsOverloadNone, IsStreaming],[]>; @@ -2282,14 +2295,14 @@ let TargetGuard = "sme2" in { def SVCVT_S32_F32_X4 : SInst<"svcvt_{d}[_f32_x4]", "4.d4.M", "i", MergeNone, "aarch64_sve_fcvtzs_x4", [IsStreaming, IsOverloadWhileOrMultiVecCvt], []>; } -let TargetGuard = "sme-f16f16" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme-f16f16" in { def SVCVT_F32_X2 : SInst<"svcvt_{d}[_f16_x2]", "2h", "f", MergeNone, "aarch64_sve_fcvt_widen_x2", [ IsStreaming],[]>; } // // Multi-vector floating-point convert from single-precision to interleaved half-precision/BFloat16 // -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVCVTN_F16_X2 : SInst<"svcvtn_f16[_f32_x2]", "e2", "f", MergeNone, "aarch64_sve_fcvtn_x2", [IsStreaming],[]>; def SVCVTN_BF16_X2 : SInst<"svcvtn_bf16[_f32_x2]", "$2", "f", MergeNone, "aarch64_sve_bfcvtn_x2", [IsOverloadNone, IsStreaming],[]>; } @@ -2297,14 +2310,14 @@ let TargetGuard = "sme2" in { // //Multi-vector floating-point convert from half-precision to deinterleaved single-precision. // -let TargetGuard = "sme-f16f16" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme-f16f16" in { def SVCVTL_F32_X2 : SInst<"svcvtl_f32[_f16_x2]", "2h", "f", MergeNone, "aarch64_sve_fcvtl_widen_x2", [ IsStreaming],[]>; } // // Multi-vector saturating extract narrow // -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVQCVT_S16_S32_X2 : SInst<"svqcvt_s16[_{d}_x2]", "h2.d", "i", MergeNone, "aarch64_sve_sqcvt_x2", [IsStreaming], []>; def SVQCVT_U16_U32_X2 : SInst<"svqcvt_u16[_{d}_x2]", "e2.d", "Ui", MergeNone, "aarch64_sve_uqcvt_x2", [IsStreaming], []>; def SVQCVT_U16_S32_X2 : SInst<"svqcvt_u16[_{d}_x2]", "e2.d", "i", MergeNone, "aarch64_sve_sqcvtu_x2", [IsStreaming], []>; @@ -2321,13 +2334,13 @@ let TargetGuard = "sme2" in { // // Multi-vector saturating extract narrow and interleave // -let TargetGuard = "sme2|sve2p1" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { def SVQCVTN_S16_S32_X2 : SInst<"svqcvtn_s16[_{d}_x2]", "h2.d", "i", MergeNone, "aarch64_sve_sqcvtn_x2", [VerifyRuntimeMode], []>; def SVQCVTN_U16_U32_X2 : SInst<"svqcvtn_u16[_{d}_x2]", "e2.d", "Ui", MergeNone, "aarch64_sve_uqcvtn_x2", [VerifyRuntimeMode], []>; def SVQCVTN_U16_S32_X2 : SInst<"svqcvtn_u16[_{d}_x2]", "e2.d", "i", MergeNone, "aarch64_sve_sqcvtun_x2", [VerifyRuntimeMode], []>; } -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVQCVTN_S8_S32_X4 : SInst<"svqcvtn_s8[_{d}_x4]", "q4.d", "i", MergeNone, "aarch64_sve_sqcvtn_x4", [IsStreaming], []>; def SVQCVTN_U8_U32_X4 : SInst<"svqcvtn_u8[_{d}_x4]", "b4.d", "Ui", MergeNone, "aarch64_sve_uqcvtn_x4", [IsStreaming], []>; def SVQCVTN_U8_S32_X4 : SInst<"svqcvtn_u8[_{d}_x4]", "b4.d", "i", MergeNone, "aarch64_sve_sqcvtun_x4", [IsStreaming], []>; @@ -2341,7 +2354,7 @@ let TargetGuard = "sme2" in { // Multi-vector zip/unzip // -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVZIP_X2 : SInst<"svzip[_{d}_x2]", "22", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_zip_x2", [IsStreaming], []>; def SVZIPQ_X2 : SInst<"svzipq[_{d}_x2]", "22", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_zipq_x2", [IsStreaming], []>; def SVZIP_X4 : SInst<"svzip[_{d}_x4]", "44", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_zip_x4", [IsStreaming], []>; @@ -2357,14 +2370,14 @@ let TargetGuard = "sme2" in { // Multi-vector unpack // -let TargetGuard = "sme2" in { +let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVSUNPK_X2 : SInst<"svunpk_{d}[_{1}_x2]", "2h", "sil", MergeNone, "aarch64_sve_sunpk_x2", [IsStreaming], []>; def SVUUNPK_X2 : SInst<"svunpk_{d}[_{1}_x2]", "2h", "UsUiUl", MergeNone, "aarch64_sve_uunpk_x2", [IsStreaming], []>; def SVSUNPK_X4 : SInst<"svunpk_{d}[_{3}_x4]", "42.h", "sil", MergeNone, "aarch64_sve_sunpk_x4", [IsStreaming], []>; def SVUUNPK_X4 : SInst<"svunpk_{d}[_{3}_x4]", "42.h", "UsUiUl", MergeNone, "aarch64_sve_uunpk_x4", [IsStreaming], []>; } -let TargetGuard = "sve2p1|sme2" in { +let SVETargetGuard = "sve2p1", SMETargetGuard = "sme2" in { // == BFloat16 multiply-subtract == def SVBFMLSLB : SInst<"svbfmlslb[_{d}]", "dd$$", "f", MergeNone, "aarch64_sve_bfmlslb", [IsOverloadNone, VerifyRuntimeMode], []>; def SVBFMLSLT : SInst<"svbfmlslt[_{d}]", "dd$$", "f", MergeNone, "aarch64_sve_bfmlslt", [IsOverloadNone, VerifyRuntimeMode], []>; diff --git a/clang/include/clang/Basic/arm_sve_sme_incl.td b/clang/include/clang/Basic/arm_sve_sme_incl.td index 37e3925509836..6ec357825a132 100644 --- a/clang/include/clang/Basic/arm_sve_sme_incl.td +++ b/clang/include/clang/Basic/arm_sve_sme_incl.td @@ -267,12 +267,15 @@ class ImmCheck { ImmCheckType Kind = kind; } +defvar InvalidMode = ""; + class Inst ft, list ch, MemEltType met = MemEltTyDefault> { string Name = n; string Prototype = p; string Types = t; - string TargetGuard = "sve"; + string SVETargetGuard = "sve"; + string SMETargetGuard = "sme"; int Merge = mt.Value; string MergeSuffix = mt.Suffix; string LLVMIntrinsic = i; diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index fda0855dc8683..d4822dc160082 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -42,6 +42,7 @@ class CXXConstructorDecl; class CXXDestructorDecl; class CXXRecordDecl; class CXXMethodDecl; +class GlobalDecl; class ObjCMethodDecl; class ObjCProtocolDecl; @@ -104,6 +105,9 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T); unsigned getLLVMFieldNumber(CodeGenModule &CGM, const RecordDecl *RD, const FieldDecl *FD); +/// Return a declaration discriminator for the given global decl. +uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); + /// Given the language and code-generation options that Clang was configured /// with, set the default LLVM IR attributes for a function definition. /// The attributes set here are mostly global target-configuration and diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 498acfd380131..28d4764b6d60b 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -25,8 +25,11 @@ #include namespace clang { -namespace CodeGen { +class GlobalDecl; +class PointerAuthSchema; +class QualType; +namespace CodeGen { class CodeGenModule; /// A convenience builder class for complex constant initializers, @@ -199,6 +202,11 @@ class ConstantAggregateBuilderBase { add(llvm::ConstantInt::get(intTy, value, isSigned)); } + /// Add a signed pointer using the given pointer authentication schema. + void addSignedPointer(llvm::Constant *Pointer, + const PointerAuthSchema &Schema, GlobalDecl CalleeDecl, + QualType CalleeType); + /// Add a null pointer of a specific type. void addNullPointer(llvm::PointerType *ptrTy) { add(llvm::ConstantPointerNull::get(ptrTy)); diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h index edacd82bf899d..59b9840d02e08 100644 --- a/clang/include/clang/CodeGen/ModuleBuilder.h +++ b/clang/include/clang/CodeGen/ModuleBuilder.h @@ -15,6 +15,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" namespace llvm { class Constant; @@ -27,6 +28,9 @@ namespace llvm { } } +// Prefix of the name of the artificial inline frame. +inline constexpr llvm::StringRef ClangTrapPrefix = "__clang_trap_msg"; + namespace clang { class CodeGenOptions; class CoverageSourceInfo; diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 084c3ffe69ae8..cc1538372d5f8 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -747,9 +747,6 @@ class Driver { /// option. void setDriverMode(StringRef DriverModeValue); - /// Set the resource directory, depending on which driver is being used. - void setResourceDirectory(); - /// Parse the \p Args list for LTO options and record the type of LTO /// compilation based on which -f(no-)?lto(=.*)? option occurs last. void setLTOMode(const llvm::opt::ArgList &Args); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index bbf860aa491e1..58ca6f2bea9e4 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4612,10 +4612,10 @@ def inline_asm_EQ : Joined<["-"], "inline-asm=">, Group, NormalizedValuesScope<"CodeGenOptions">, NormalizedValues<["IAD_ATT", "IAD_Intel"]>, MarshallingInfoEnum, "IAD_ATT">; def mcmodel_EQ : Joined<["-"], "mcmodel=">, Group, - Visibility<[ClangOption, CC1Option]>, + Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>, MarshallingInfoString, [{"default"}]>; def mlarge_data_threshold_EQ : Joined<["-"], "mlarge-data-threshold=">, Group, - Flags<[TargetSpecific]>, Visibility<[ClangOption, CC1Option]>, + Flags<[TargetSpecific]>, Visibility<[ClangOption, CC1Option,FlangOption, FC1Option]>, MarshallingInfoInt, "0">; def mtls_size_EQ : Joined<["-"], "mtls-size=">, Group, Visibility<[ClangOption, CC1Option]>, @@ -5713,6 +5713,11 @@ def print_supported_extensions : Flag<["-", "--"], "print-supported-extensions"> Visibility<[ClangOption, CC1Option, CLOption]>, HelpText<"Print supported -march extensions (RISC-V, AArch64 and ARM only)">, MarshallingInfoFlag>; +def print_enabled_extensions : Flag<["-", "--"], "print-enabled-extensions">, + Visibility<[ClangOption, CC1Option, CLOption]>, + HelpText<"Print the extensions enabled by the given target and -march/-mcpu options." + " (AArch64 only)">, + MarshallingInfoFlag>; def : Flag<["-"], "mcpu=help">, Alias; def : Flag<["-"], "mtune=help">, Alias; def time : Flag<["-"], "time">, @@ -6314,12 +6319,12 @@ def mno_gather : Flag<["-"], "mno-gather">, Group, def mno_scatter : Flag<["-"], "mno-scatter">, Group, HelpText<"Disable generation of scatter instructions in auto-vectorization(x86 only)">; def mapx_features_EQ : CommaJoined<["-"], "mapx-features=">, Group, - HelpText<"Enable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf">; + HelpText<"Enable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf,zu">, Visibility<[ClangOption, CLOption, FlangOption]>; def mno_apx_features_EQ : CommaJoined<["-"], "mno-apx-features=">, Group, - HelpText<"Disable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf">; + HelpText<"Disable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,nf,cf,zu">, Visibility<[ClangOption, CLOption, FlangOption]>; // For stability, we only add a feature to -mapxf after it passes the validation of llvm-test-suite && cpu2017 on Intel SDE. -def mapxf : Flag<["-"], "mapxf">, Alias, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf"]>; -def mno_apxf : Flag<["-"], "mno-apxf">, Alias, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf"]>; +def mapxf : Flag<["-"], "mapxf">, Alias, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf","cf"]>; +def mno_apxf : Flag<["-"], "mno-apxf">, Alias, AliasArgs<["egpr","push2pop2","ppx","ndd","ccmp","nf","cf"]>; def mapx_inline_asm_use_gpr32 : Flag<["-"], "mapx-inline-asm-use-gpr32">, Group, HelpText<"Enable use of GPR32 in inline assembly for APX">; } // let Flags = [TargetSpecific] @@ -7022,6 +7027,9 @@ def massembler_no_warn : Flag<["-"], "massembler-no-warn">, def massembler_fatal_warnings : Flag<["-"], "massembler-fatal-warnings">, HelpText<"Make assembler warnings fatal">, MarshallingInfoFlag>; +def crel : Flag<["--"], "crel">, + HelpText<"Enable CREL relocation format (ELF only)">, + MarshallingInfoFlag>; def mrelax_relocations_no : Flag<["-"], "mrelax-relocations=no">, HelpText<"Disable x86 relax relocations">, MarshallingInfoNegativeFlag>; @@ -8597,6 +8605,8 @@ def : Separate<["-"], "Xmicrosoft-windows-sdk-root">, Alias<_SLASH_winsdkdir>; def : Separate<["-"], "Xmicrosoft-windows-sdk-version">, Alias<_SLASH_winsdkversion>; +def : Separate<["-"], "Xmicrosoft-windows-sys-root">, + Alias<_SLASH_winsysroot>; // Ignored: @@ -8768,3 +8778,11 @@ def spirv : DXCFlag<"spirv">, def fspv_target_env_EQ : Joined<["-"], "fspv-target-env=">, Group, HelpText<"Specify the target environment">, Values<"vulkan1.2, vulkan1.3">; +def no_wasm_opt : Flag<["--"], "no-wasm-opt">, + Group, + HelpText<"Disable the wasm-opt optimizer">, + MarshallingInfoFlag>; +def wasm_opt : Flag<["--"], "wasm-opt">, + Group, + HelpText<"Enable the wasm-opt optimizer (default)">, + MarshallingInfoNegativeFlag>; diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h index 1f93bd612e9b0..ece1384d5d3c0 100644 --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -526,6 +526,9 @@ class ToolChain { // Returns target specific standard library path if it exists. std::optional getStdlibPath() const; + // Returns target specific standard library include path if it exists. + std::optional getStdlibIncludePath() const; + // Returns /lib// or /lib/. // This is used by runtimes (such as OpenMP) to find arch-specific libraries. virtual path_list getArchSpecificLibPaths() const; diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 7d257be10af42..c454ab2bc0ce2 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -3095,20 +3095,49 @@ struct FormatStyle { bool JavaScriptWrapImports; // clang-format on - /// Keep empty lines (up to ``MaxEmptyLinesToKeep``) at end of file. - /// \version 17 - bool KeepEmptyLinesAtEOF; - - /// If true, the empty line at the start of blocks is kept. + /// Options regarding which empty lines are kept. + /// + /// For example, the config below will remove empty lines at start of the + /// file, end of the file, and start of blocks. + /// /// \code - /// true: false: - /// if (foo) { vs. if (foo) { - /// bar(); - /// bar(); } - /// } + /// KeepEmptyLines: + /// AtEndOfFile: false + /// AtStartOfBlock: false + /// AtStartOfFile: false /// \endcode + struct KeepEmptyLinesStyle { + /// Keep empty lines at end of file. + bool AtEndOfFile; + /// Keep empty lines at start of a block. + /// \code + /// true: false: + /// if (foo) { vs. if (foo) { + /// bar(); + /// bar(); } + /// } + /// \endcode + bool AtStartOfBlock; + /// Keep empty lines at start of file. + bool AtStartOfFile; + bool operator==(const KeepEmptyLinesStyle &R) const { + return AtEndOfFile == R.AtEndOfFile && + AtStartOfBlock == R.AtStartOfBlock && + AtStartOfFile == R.AtStartOfFile; + } + }; + /// Which empty lines are kept. See ``MaxEmptyLinesToKeep`` for how many + /// consecutive empty lines are kept. + /// \version 19 + KeepEmptyLinesStyle KeepEmptyLines; + + /// This option is deprecated. See ``AtEndOfFile`` of ``KeepEmptyLines``. + /// \version 17 + // bool KeepEmptyLinesAtEOF; + + /// This option is deprecated. See ``AtStartOfBlock`` of ``KeepEmptyLines``. /// \version 3.7 - bool KeepEmptyLinesAtTheStartOfBlocks; + // bool KeepEmptyLinesAtTheStartOfBlocks; /// Indentation logic for lambda bodies. enum LambdaBodyIndentationKind : int8_t { @@ -4650,10 +4679,22 @@ struct FormatStyle { /// # Should be declared this way: /// SpacesInParens: Custom /// SpacesInParensOptions: + /// ExceptDoubleParentheses: false /// InConditionalStatements: true /// Other: true /// \endcode struct SpacesInParensCustom { + /// Override any of the following options to prevent addition of space + /// when both opening and closing parentheses use multiple parentheses. + /// \code + /// true: + /// __attribute__(( noreturn )) + /// __decltype__(( x )) + /// if (( a = b )) + /// \endcode + /// false: + /// Uses the applicable option. + bool ExceptDoubleParentheses; /// Put a space in parentheses only inside conditional statements /// (``for/if/while/switch...``). /// \code @@ -4664,8 +4705,9 @@ struct FormatStyle { bool InConditionalStatements; /// Put a space in C style casts. /// \code - /// true: false: - /// x = ( int32 )y vs. x = (int32)y + /// true: false: + /// x = ( int32 )y vs. x = (int32)y + /// y = (( int (*)(int) )foo)(x); y = ((int (*)(int))foo)(x); /// \endcode bool InCStyleCasts; /// Insert a space in empty parentheses, i.e. ``()``. @@ -4681,23 +4723,26 @@ struct FormatStyle { bool InEmptyParentheses; /// Put a space in parentheses not covered by preceding options. /// \code - /// true: false: - /// t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; + /// true: false: + /// t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; /// \endcode bool Other; SpacesInParensCustom() - : InConditionalStatements(false), InCStyleCasts(false), - InEmptyParentheses(false), Other(false) {} + : ExceptDoubleParentheses(false), InConditionalStatements(false), + InCStyleCasts(false), InEmptyParentheses(false), Other(false) {} - SpacesInParensCustom(bool InConditionalStatements, bool InCStyleCasts, + SpacesInParensCustom(bool ExceptDoubleParentheses, + bool InConditionalStatements, bool InCStyleCasts, bool InEmptyParentheses, bool Other) - : InConditionalStatements(InConditionalStatements), + : ExceptDoubleParentheses(ExceptDoubleParentheses), + InConditionalStatements(InConditionalStatements), InCStyleCasts(InCStyleCasts), InEmptyParentheses(InEmptyParentheses), Other(Other) {} bool operator==(const SpacesInParensCustom &R) const { - return InConditionalStatements == R.InConditionalStatements && + return ExceptDoubleParentheses == R.ExceptDoubleParentheses && + InConditionalStatements == R.InConditionalStatements && InCStyleCasts == R.InCStyleCasts && InEmptyParentheses == R.InEmptyParentheses && Other == R.Other; } @@ -4715,6 +4760,7 @@ struct FormatStyle { /// # Example of usage: /// SpacesInParens: Custom /// SpacesInParensOptions: + /// ExceptDoubleParentheses: false /// InConditionalStatements: true /// InEmptyParentheses: true /// \endcode @@ -5033,10 +5079,7 @@ struct FormatStyle { JavaImportGroups == R.JavaImportGroups && JavaScriptQuotes == R.JavaScriptQuotes && JavaScriptWrapImports == R.JavaScriptWrapImports && - KeepEmptyLinesAtEOF == R.KeepEmptyLinesAtEOF && - KeepEmptyLinesAtTheStartOfBlocks == - R.KeepEmptyLinesAtTheStartOfBlocks && - Language == R.Language && + KeepEmptyLines == R.KeepEmptyLines && Language == R.Language && LambdaBodyIndentation == R.LambdaBodyIndentation && LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin && MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros && diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 1a2a39411e58d..9daa0a1ecf948 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -305,6 +305,15 @@ class CompilerInvocation : public CompilerInvocationBase { /// executable), for finding the builtin compiler path. static std::string GetResourcesPath(const char *Argv0, void *MainAddr); + /// Populate \p Opts with the default set of pointer authentication-related + /// options given \p LangOpts and \p Triple. + /// + /// Note: This is intended to be used by tools which must be aware of + /// pointer authentication-related code generation, e.g. lldb. + static void setDefaultPointerAuthOptions(PointerAuthOptions &Opts, + const LangOptions &LangOpts, + const llvm::Triple &Triple); + /// Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built. std::string getModuleHash() const; diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index ebb8e9e59c6b6..5e5034fe01eb5 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -306,6 +306,10 @@ class FrontendOptions { LLVM_PREFERRED_TYPE(bool) unsigned PrintSupportedExtensions : 1; + /// Print the extensions enabled for the current target. + LLVM_PREFERRED_TYPE(bool) + unsigned PrintEnabledExtensions : 1; + /// Show the -version text. LLVM_PREFERRED_TYPE(bool) unsigned ShowVersion : 1; diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h b/clang/include/clang/Frontend/MultiplexConsumer.h index 4ed0d86d3cdfb..e49e3392d1f31 100644 --- a/clang/include/clang/Frontend/MultiplexConsumer.h +++ b/clang/include/clang/Frontend/MultiplexConsumer.h @@ -67,7 +67,7 @@ class MultiplexConsumer : public SemaConsumer { void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override; void HandleImplicitImportDecl(ImportDecl *D) override; void CompleteTentativeDefinition(VarDecl *D) override; - void CompleteExternalDeclaration(VarDecl *D) override; + void CompleteExternalDeclaration(DeclaratorDecl *D) override; void AssignInheritanceModel(CXXRecordDecl *RD) override; void HandleVTable(CXXRecordDecl *RD) override; ASTMutationListener *GetASTMutationListener() override; diff --git a/clang/include/clang/InstallAPI/Visitor.h b/clang/include/clang/InstallAPI/Visitor.h index 9ac948ded3e33..3680ee566ca87 100644 --- a/clang/include/clang/InstallAPI/Visitor.h +++ b/clang/include/clang/InstallAPI/Visitor.h @@ -60,8 +60,8 @@ class InstallAPIVisitor final : public ASTConsumer, std::string getMangledName(const NamedDecl *D) const; std::string getBackendMangledName(llvm::Twine Name) const; std::string getMangledCXXVTableName(const CXXRecordDecl *D) const; - std::string getMangledCXXThunk(const GlobalDecl &D, - const ThunkInfo &Thunk) const; + std::string getMangledCXXThunk(const GlobalDecl &D, const ThunkInfo &Thunk, + bool ElideOverrideInfo) const; std::string getMangledCXXRTTI(const CXXRecordDecl *D) const; std::string getMangledCXXRTTIName(const CXXRecordDecl *D) const; std::string getMangledCtorDtor(const CXXMethodDecl *D, int Type) const; diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h index e4437ac0e3526..17635146a1614 100644 --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -11,7 +11,6 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/CachedHashString.h" -#include "llvm/ADT/Hashing.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/HashBuilder.h" @@ -310,21 +309,12 @@ class HeaderSearchOptions { } }; -inline llvm::hash_code hash_value(const HeaderSearchOptions::Entry &E) { - return llvm::hash_combine(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot); -} - template inline void addHash(llvm::HashBuilder &HBuilder, const HeaderSearchOptions::Entry &E) { HBuilder.add(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot); } -inline llvm::hash_code -hash_value(const HeaderSearchOptions::SystemHeaderPrefix &SHP) { - return llvm::hash_combine(SHP.Prefix, SHP.IsSystemHeader); -} - template inline void addHash(llvm::HashBuilder &HBuilder, const HeaderSearchOptions::SystemHeaderPrefix &SHP) { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 95c0655f9a214..6880fa4bb0b03 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3017,11 +3017,12 @@ class Parser : public CodeCompletionHandler { const IdentifierInfo *EnclosingScope = nullptr); void MaybeParseHLSLAnnotations(Declarator &D, - SourceLocation *EndLoc = nullptr) { + SourceLocation *EndLoc = nullptr, + bool CouldBeBitField = false) { assert(getLangOpts().HLSL && "MaybeParseHLSLAnnotations is for HLSL only"); if (Tok.is(tok::colon)) { ParsedAttributes Attrs(AttrFactory); - ParseHLSLAnnotations(Attrs, EndLoc); + ParseHLSLAnnotations(Attrs, EndLoc, CouldBeBitField); D.takeAttributes(Attrs); } } @@ -3029,12 +3030,13 @@ class Parser : public CodeCompletionHandler { void MaybeParseHLSLAnnotations(ParsedAttributes &Attrs, SourceLocation *EndLoc = nullptr) { assert(getLangOpts().HLSL && "MaybeParseHLSLAnnotations is for HLSL only"); - if (getLangOpts().HLSL && Tok.is(tok::colon)) + if (Tok.is(tok::colon)) ParseHLSLAnnotations(Attrs, EndLoc); } void ParseHLSLAnnotations(ParsedAttributes &Attrs, - SourceLocation *EndLoc = nullptr); + SourceLocation *EndLoc = nullptr, + bool CouldBeBitField = false); Decl *ParseHLSLBuffer(SourceLocation &DeclEnd); void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) { @@ -3511,6 +3513,19 @@ class Parser : public CodeCompletionHandler { /// metadirective and therefore ends on the closing paren. StmtResult ParseOpenMPDeclarativeOrExecutableDirective( ParsedStmtContext StmtCtx, bool ReadDirectiveWithinMetadirective = false); + + /// Parses executable directive. + /// + /// \param StmtCtx The context in which we're parsing the directive. + /// \param DKind The kind of the executable directive. + /// \param Loc Source location of the beginning of the directive. + /// \param ReadDirectiveWithinMetadirective true if directive is within a + /// metadirective and therefore ends on the closing paren. + StmtResult + ParseOpenMPExecutableDirective(ParsedStmtContext StmtCtx, + OpenMPDirectiveKind DKind, SourceLocation Loc, + bool ReadDirectiveWithinMetadirective); + /// Parses clause of kind \a CKind for directive of a kind \a Kind. /// /// \param DKind Kind of current directive. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 95a732bcba618..75a80540dbcbf 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -473,6 +473,63 @@ enum class TagUseKind { Friend // Friend declaration: 'friend struct foo;' }; +/// Used with attributes/effects with a boolean condition, e.g. `nonblocking`. +enum class FunctionEffectMode : uint8_t { + None, // effect is not present. + False, // effect(false). + True, // effect(true). + Dependent // effect(expr) where expr is dependent. +}; + +struct FunctionEffectDiff { + enum class Kind { Added, Removed, ConditionMismatch }; + + FunctionEffect::Kind EffectKind; + Kind DiffKind; + FunctionEffectWithCondition Old; // invalid when Added. + FunctionEffectWithCondition New; // invalid when Removed. + + StringRef effectName() const { + if (Old.Effect.kind() != FunctionEffect::Kind::None) + return Old.Effect.name(); + return New.Effect.name(); + } + + /// Describes the result of effects differing between a base class's virtual + /// method and an overriding method in a subclass. + enum class OverrideResult { + NoAction, + Warn, + Merge // Merge missing effect from base to derived. + }; + + /// Return true if adding or removing the effect as part of a type conversion + /// should generate a diagnostic. + bool shouldDiagnoseConversion(QualType SrcType, + const FunctionEffectsRef &SrcFX, + QualType DstType, + const FunctionEffectsRef &DstFX) const; + + /// Return true if adding or removing the effect in a redeclaration should + /// generate a diagnostic. + bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction, + const FunctionEffectsRef &OldFX, + const FunctionDecl &NewFunction, + const FunctionEffectsRef &NewFX) const; + + /// Return true if adding or removing the effect in a C++ virtual method + /// override should generate a diagnostic. + OverrideResult shouldDiagnoseMethodOverride( + const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, + const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const; +}; + +struct FunctionEffectDifferences : public SmallVector { + /// Caller should short-circuit by checking for equality first. + FunctionEffectDifferences(const FunctionEffectsRef &Old, + const FunctionEffectsRef &New); +}; + /// Sema - This implements semantic analysis and AST building for C. /// \nosubgrouping class Sema final : public SemaBase { @@ -555,6 +612,7 @@ class Sema final : public SemaBase { /// void addExternalSource(ExternalSemaSource *E); + /// Print out statistics about the semantic analysis. void PrintStats() const; /// Warn that the stack is nearly exhausted. @@ -600,6 +658,10 @@ class Sema final : public SemaBase { /// in deferred diagnostics. bool hasUncompilableErrorOccurred() const; + /// Looks through the macro-expansion chain for the given + /// location, looking for a macro expansion with the given name. + /// If one is found, returns true and sets the location to that + /// expansion loc. bool findMacroSpelling(SourceLocation &loc, StringRef name); /// Calls \c Lexer::getLocForEndOfToken() @@ -630,10 +692,27 @@ class Sema final : public SemaBase { Private }; + /// This is called before the very first declaration in the translation unit + /// is parsed. Note that the ASTContext may have already injected some + /// declarations. void ActOnStartOfTranslationUnit(); + /// ActOnEndOfTranslationUnit - This is called at the very end of the + /// translation unit when EOF is reached and all but the top-level scope is + /// popped. void ActOnEndOfTranslationUnit(); void ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind); + /// Determines the active Scope associated with the given declaration + /// context. + /// + /// This routine maps a declaration context to the active Scope object that + /// represents that declaration context in the parser. It is typically used + /// from "scope-less" code (e.g., template instantiation, lazy creation of + /// declarations) that injects a name for name-lookup purposes and, therefore, + /// must update the Scope. + /// + /// \returns The scope corresponding to the given declaraion context, or NULL + /// if no such scope is open. Scope *getScopeForContext(DeclContext *Ctx); void PushFunctionScope(); @@ -662,6 +741,14 @@ class Sema final : public SemaBase { using PoppedFunctionScopePtr = std::unique_ptr; + /// Pop a function (or block or lambda or captured region) scope from the + /// stack. + /// + /// \param WP The warning policy to use for CFG-based warnings, or null if + /// such warnings should not be produced. + /// \param D The declaration corresponding to this function scope, if + /// producing CFG-based warnings. + /// \param BlockType The type of the block expression, if D is a BlockDecl. PoppedFunctionScopePtr PopFunctionScopeInfo(const sema::AnalysisBasedWarnings::Policy *WP = nullptr, const Decl *D = nullptr, @@ -677,6 +764,8 @@ class Sema final : public SemaBase { void PushCompoundScope(bool IsStmtExpr); void PopCompoundScope(); + /// Determine whether any errors occurred within this function/method/ + /// block. bool hasAnyUnrecoverableErrorsInThisFunction() const; /// Retrieve the current block, if any. @@ -783,6 +872,34 @@ class Sema final : public SemaBase { /// Warn when implicitly casting 0 to nullptr. void diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E); + // ----- function effects --- + + /// Warn when implicitly changing function effects. + void diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType, + SourceLocation Loc); + + /// Warn and return true if adding an effect to a set would create a conflict. + bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX, + const FunctionEffectWithCondition &EC, + SourceLocation NewAttrLoc); + + // Report a failure to merge function effects between declarations due to a + // conflict. + void + diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs, + SourceLocation NewLoc, + SourceLocation OldLoc); + + /// Try to parse the conditional expression attached to an effect attribute + /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty + /// optional on error. + std::optional + ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName); + + /// makeUnavailableInSystemHeader - There is an error in the current + /// context. If we're still in a system header, and we can plausibly + /// make the relevant declaration unavailable instead of erroring, do + /// so and return true. bool makeUnavailableInSystemHeader(SourceLocation loc, UnavailableAttr::ImplicitReason reason); @@ -858,6 +975,17 @@ class Sema final : public SemaBase { bool (*IsPlausibleResult)(QualType) = nullptr); /// Figure out if an expression could be turned into a call. + /// + /// Use this when trying to recover from an error where the programmer may + /// have written just the name of a function instead of actually calling it. + /// + /// \param E - The expression to examine. + /// \param ZeroArgCallReturnTy - If the expression can be turned into a call + /// with no arguments, this parameter is set to the type returned by such a + /// call; otherwise, it is set to an empty QualType. + /// \param OverloadSet - If the expression is an overloaded function + /// name, this parameter is populated with the decls of the various + /// overloads. bool tryExprAsCall(Expr &E, QualType &ZeroArgCallReturnTy, UnresolvedSetImpl &NonTemplateOverloads); @@ -1208,23 +1336,34 @@ class Sema final : public SemaBase { AR_delayed }; + /// SetMemberAccessSpecifier - Set the access specifier of a member. + /// Returns true on error (when the previous member decl access specifier + /// is different from the new member decl access specifier). bool SetMemberAccessSpecifier(NamedDecl *MemberDecl, NamedDecl *PrevMemberDecl, AccessSpecifier LexicalAS); + /// Perform access-control checking on a previously-unresolved member + /// access which has now been resolved to a member. AccessResult CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, DeclAccessPair FoundDecl); AccessResult CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, DeclAccessPair FoundDecl); + + /// Checks access to an overloaded operator new or delete. AccessResult CheckAllocationAccess(SourceLocation OperatorLoc, SourceRange PlacementRange, CXXRecordDecl *NamingClass, DeclAccessPair FoundDecl, bool Diagnose = true); + + /// Checks access to a constructor. AccessResult CheckConstructorAccess(SourceLocation Loc, CXXConstructorDecl *D, DeclAccessPair FoundDecl, const InitializedEntity &Entity, bool IsCopyBindingRefToTemp = false); + + /// Checks access to a constructor. AccessResult CheckConstructorAccess(SourceLocation Loc, CXXConstructorDecl *D, DeclAccessPair FoundDecl, const InitializedEntity &Entity, @@ -1233,10 +1372,16 @@ class Sema final : public SemaBase { CXXDestructorDecl *Dtor, const PartialDiagnostic &PDiag, QualType objectType = QualType()); + + /// Checks access to the target of a friend declaration. AccessResult CheckFriendAccess(NamedDecl *D); + + /// Checks access to a member. AccessResult CheckMemberAccess(SourceLocation UseLoc, CXXRecordDecl *NamingClass, DeclAccessPair Found); + + /// Checks implicit access to a member in a structured binding. AccessResult CheckStructuredBindingMemberAccess(SourceLocation UseLoc, CXXRecordDecl *DecomposedClass, @@ -1244,6 +1389,9 @@ class Sema final : public SemaBase { AccessResult CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr, const SourceRange &, DeclAccessPair FoundDecl); + + /// Checks access to an overloaded member operator, including + /// conversion operators. AccessResult CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr, Expr *ArgExpr, DeclAccessPair FoundDecl); @@ -1252,13 +1400,43 @@ class Sema final : public SemaBase { DeclAccessPair FoundDecl); AccessResult CheckAddressOfMemberAccess(Expr *OvlExpr, DeclAccessPair FoundDecl); + + /// Checks access for a hierarchy conversion. + /// + /// \param ForceCheck true if this check should be performed even if access + /// control is disabled; some things rely on this for semantics + /// \param ForceUnprivileged true if this check should proceed as if the + /// context had no special privileges AccessResult CheckBaseClassAccess(SourceLocation AccessLoc, QualType Base, QualType Derived, const CXXBasePath &Path, unsigned DiagID, bool ForceCheck = false, bool ForceUnprivileged = false); + + /// Checks access to all the declarations in the given result set. void CheckLookupAccess(const LookupResult &R); + + /// Checks access to Target from the given class. The check will take access + /// specifiers into account, but no member access expressions and such. + /// + /// \param Target the declaration to check if it can be accessed + /// \param NamingClass the class in which the lookup was started. + /// \param BaseType type of the left side of member access expression. + /// \p BaseType and \p NamingClass are used for C++ access control. + /// Depending on the lookup case, they should be set to the following: + /// - lhs.target (member access without a qualifier): + /// \p BaseType and \p NamingClass are both the type of 'lhs'. + /// - lhs.X::target (member access with a qualifier): + /// BaseType is the type of 'lhs', NamingClass is 'X' + /// - X::target (qualified lookup without member access): + /// BaseType is null, NamingClass is 'X'. + /// - target (unqualified lookup). + /// BaseType is null, NamingClass is the parent class of 'target'. + /// \return true if the Target is accessible from the Class, false otherwise. bool IsSimplyAccessible(NamedDecl *Decl, CXXRecordDecl *NamingClass, QualType BaseType); + + /// Is the given member accessible for the purposes of deciding whether to + /// define a special member function as deleted? bool isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass, DeclAccessPair Found, QualType ObjectType, SourceLocation Loc, @@ -2010,11 +2188,17 @@ class Sema final : public SemaBase { FormatArgumentPassingKind ArgPassingKind; }; + /// Given a FunctionDecl's FormatAttr, attempts to populate the + /// FomatStringInfo parameter with the FormatAttr's correct format_idx and + /// firstDataArg. Returns true when the format fits the function and the + /// FormatStringInfo has been populated. static bool getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember, bool IsVariadic, FormatStringInfo *FSI); // Used by C++ template instantiation. ExprResult BuiltinShuffleVector(CallExpr *TheCall); + + /// ConvertVectorExpr - Handle __builtin_convertvector ExprResult ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo, SourceLocation BuiltinLoc, SourceLocation RParenLoc); @@ -2035,8 +2219,9 @@ class Sema final : public SemaBase { bool FormatStringHasSArg(const StringLiteral *FExpr); - static bool GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx); - + /// Check for comparisons of floating-point values using == and !=. Issue a + /// warning if the comparison is not likely to do what the programmer + /// intended. void CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS, BinaryOperatorKind Opcode); @@ -2096,13 +2281,26 @@ class Sema final : public SemaBase { /// Check to see if a given expression could have '.c_str()' called on it. bool hasCStrMethod(const Expr *E); + /// Diagnose pointers that are always non-null. + /// \param E the expression containing the pointer + /// \param NullKind NPCK_NotNull if E is a cast to bool, otherwise, E is + /// compared to a null pointer + /// \param IsEqual True when the comparison is equal to a null pointer + /// \param Range Extra SourceRange to highlight in the diagnostic void DiagnoseAlwaysNonNullPointer(Expr *E, Expr::NullPointerConstantKind NullType, bool IsEqual, SourceRange Range); + /// CheckParmsForFunctionDef - Check that the parameters of the given + /// function are appropriate for the definition of a function. This + /// takes care of any checks that cannot be performed on the + /// declaration itself, e.g., that the types of each of the function + /// parameters are complete. bool CheckParmsForFunctionDef(ArrayRef Parameters, bool CheckParameterNames); + /// CheckCastAlign - Implements -Wcast-align, which warns when a + /// pointer cast increases the alignment requirements. void CheckCastAlign(Expr *Op, QualType T, SourceRange TRange); /// checkUnsafeAssigns - Check whether +1 expr is being assigned @@ -2126,7 +2324,7 @@ class Sema final : public SemaBase { /// \p PossibleBody, has a suspicious null statement as a body. void DiagnoseEmptyLoopBody(const Stmt *S, const Stmt *PossibleBody); - /// Warn if a value is moved to itself. + /// DiagnoseSelfMove - Emits a warning if a value is moved to itself. void DiagnoseSelfMove(const Expr *LHSExpr, const Expr *RHSExpr, SourceLocation OpLoc); @@ -2143,43 +2341,97 @@ class Sema final : public SemaBase { bool IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base, const TypeSourceInfo *Derived); + /// CheckFunctionCall - Check a direct function call for various correctness + /// and safety properties not strictly enforced by the C type system. bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, const FunctionProtoType *Proto); bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res); bool BuiltinVectorToScalarMath(CallExpr *TheCall); - bool CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); - + /// Handles the checks for format strings, non-POD arguments to vararg + /// functions, NULL arguments passed to non-NULL parameters, diagnose_if + /// attributes and AArch64 SME attributes. void checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, const Expr *ThisArg, ArrayRef Args, bool IsMemberFunction, SourceLocation Loc, SourceRange Range, VariadicCallType CallType); + /// \brief Enforce the bounds of a TCB + /// CheckTCBEnforcement - Enforces that every function in a named TCB only + /// directly calls other functions in the same TCB as marked by the + /// enforce_tcb and enforce_tcb_leaf attributes. void CheckTCBEnforcement(const SourceLocation CallExprLoc, const NamedDecl *Callee); void CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc); + /// BuiltinConstantArg - Handle a check if argument ArgNum of CallExpr + /// TheCall is a constant expression. bool BuiltinConstantArg(CallExpr *TheCall, int ArgNum, llvm::APSInt &Result); + + /// BuiltinConstantArgRange - Handle a check if argument ArgNum of CallExpr + /// TheCall is a constant expression in the range [Low, High]. bool BuiltinConstantArgRange(CallExpr *TheCall, int ArgNum, int Low, int High, bool RangeIsError = true); + + /// BuiltinConstantArgMultiple - Handle a check if argument ArgNum of CallExpr + /// TheCall is a constant expression is a multiple of Num.. bool BuiltinConstantArgMultiple(CallExpr *TheCall, int ArgNum, unsigned Multiple); + + /// BuiltinConstantArgPower2 - Check if argument ArgNum of TheCall is a + /// constant expression representing a power of 2. bool BuiltinConstantArgPower2(CallExpr *TheCall, int ArgNum); + + /// BuiltinConstantArgShiftedByte - Check if argument ArgNum of TheCall is + /// a constant expression representing an arbitrary byte value shifted left by + /// a multiple of 8 bits. bool BuiltinConstantArgShiftedByte(CallExpr *TheCall, int ArgNum, unsigned ArgBits); + + /// BuiltinConstantArgShiftedByteOr0xFF - Check if argument ArgNum of + /// TheCall is a constant expression representing either a shifted byte value, + /// or a value of the form 0x??FF (i.e. a member of the arithmetic progression + /// 0x00FF, 0x01FF, ..., 0xFFFF). This strange range check is needed for some + /// Arm MVE intrinsics. bool BuiltinConstantArgShiftedByteOrXXFF(CallExpr *TheCall, int ArgNum, unsigned ArgBits); + /// Checks that a call expression's argument count is at least the desired + /// number. This is useful when doing custom type-checking on a variadic + /// function. Returns true on error. bool checkArgCountAtLeast(CallExpr *Call, unsigned MinArgCount); + + /// Checks that a call expression's argument count is at most the desired + /// number. This is useful when doing custom type-checking on a variadic + /// function. Returns true on error. bool checkArgCountAtMost(CallExpr *Call, unsigned MaxArgCount); + + /// Checks that a call expression's argument count is in the desired range. + /// This is useful when doing custom type-checking on a variadic function. + /// Returns true on error. bool checkArgCountRange(CallExpr *Call, unsigned MinArgCount, unsigned MaxArgCount); + + /// Checks that a call expression's argument count is the desired number. + /// This is useful when doing custom type-checking. Returns true on error. bool checkArgCount(CallExpr *Call, unsigned DesiredArgCount); + /// Returns true if the argument consists of one contiguous run of 1s with any + /// number of 0s on either side. The 1s are allowed to wrap from LSB to MSB, + /// so 0x000FFF0, 0x0000FFFF, 0xFF0000FF, 0x0 are all runs. 0x0F0F0000 is not, + /// since all 1s are not contiguous. bool ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum); + void CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC, + bool *ICContext = nullptr, + bool IsListInit = false); + + bool BuiltinElementwiseTernaryMath(CallExpr *TheCall, + bool CheckForFloatArgs = true); + bool PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall); + private: void CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr, const ArraySubscriptExpr *ASE = nullptr, @@ -2188,11 +2440,21 @@ class Sema final : public SemaBase { bool CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall, const FunctionProtoType *Proto); + + /// Checks function calls when a FunctionDecl or a NamedDecl is not available, + /// such as function pointers returned from functions. bool CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto); + + /// CheckConstructorCall - Check a constructor call for correctness and safety + /// properties not enforced by the C type system. void CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType, ArrayRef Args, const FunctionProtoType *Proto, SourceLocation Loc); + /// Warn if a pointer or reference argument passed to a function points to an + /// object that is less aligned than the parameter. This can happen when + /// creating a typedef with a lower alignment than the original type and then + /// calling functions defined in terms of the original type. void CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl, StringRef ParamName, QualType ArgTy, QualType ParamTy); @@ -2206,30 +2468,77 @@ class Sema final : public SemaBase { void checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, CallExpr *TheCall); + /// Check the arguments to '__builtin_va_start' or '__builtin_ms_va_start' + /// for validity. Emit an error and return true on failure; return false + /// on success. bool BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall); bool BuiltinVAStartARMMicrosoft(CallExpr *Call); + + /// BuiltinUnorderedCompare - Handle functions like __builtin_isgreater and + /// friends. This is declared to take (...), so we have to check everything. bool BuiltinUnorderedCompare(CallExpr *TheCall, unsigned BuiltinID); + + /// BuiltinSemaBuiltinFPClassification - Handle functions like + /// __builtin_isnan and friends. This is declared to take (...), so we have + /// to check everything. bool BuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs, unsigned BuiltinID); + + /// Perform semantic analysis for a call to __builtin_complex. bool BuiltinComplex(CallExpr *TheCall); bool BuiltinOSLogFormat(CallExpr *TheCall); + /// BuiltinPrefetch - Handle __builtin_prefetch. + /// This is declared to take (const void*, ...) and can take two + /// optional constant int args. bool BuiltinPrefetch(CallExpr *TheCall); + + /// Handle __builtin_alloca_with_align. This is declared + /// as (size_t, size_t) where the second size_t must be a power of 2 greater + /// than 8. bool BuiltinAllocaWithAlign(CallExpr *TheCall); + + /// BuiltinArithmeticFence - Handle __arithmetic_fence. bool BuiltinArithmeticFence(CallExpr *TheCall); + + /// BuiltinAssume - Handle __assume (MS Extension). + /// __assume does not evaluate its arguments, and should warn if its argument + /// has side effects. bool BuiltinAssume(CallExpr *TheCall); + + /// Handle __builtin_assume_aligned. This is declared + /// as (const void*, size_t, ...) and can take one optional constant int arg. bool BuiltinAssumeAligned(CallExpr *TheCall); + + /// BuiltinLongjmp - Handle __builtin_longjmp(void *env[5], int val). + /// This checks that the target supports __builtin_longjmp and + /// that val is a constant 1. bool BuiltinLongjmp(CallExpr *TheCall); + + /// BuiltinSetjmp - Handle __builtin_setjmp(void *env[5]). + /// This checks that the target supports __builtin_setjmp. bool BuiltinSetjmp(CallExpr *TheCall); + + /// We have a call to a function like __sync_fetch_and_add, which is an + /// overloaded function based on the pointer type of its first argument. + /// The main BuildCallExpr routines have already promoted the types of + /// arguments because all of these calls are prototyped as void(...). + /// + /// This function goes through and does final semantic checking for these + /// builtins, as well as generating any warnings. ExprResult BuiltinAtomicOverloaded(ExprResult TheCallResult); + + /// BuiltinNontemporalOverloaded - We have a call to + /// __builtin_nontemporal_store or __builtin_nontemporal_load, which is an + /// overloaded function based on the pointer type of its last argument. + /// + /// This function goes through and does final semantic checking for these + /// builtins. ExprResult BuiltinNontemporalOverloaded(ExprResult TheCallResult); ExprResult AtomicOpsOverloaded(ExprResult TheCallResult, AtomicExpr::AtomicOp Op); bool BuiltinElementwiseMath(CallExpr *TheCall); - bool BuiltinElementwiseTernaryMath(CallExpr *TheCall, - bool CheckForFloatArgs = true); - bool PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall); bool PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall); bool BuiltinNonDeterministicValue(CallExpr *TheCall); @@ -2241,6 +2550,9 @@ class Sema final : public SemaBase { ExprResult BuiltinMatrixColumnMajorStore(CallExpr *TheCall, ExprResult CallResult); + /// CheckFormatArguments - Check calls to printf and scanf (and similar + /// functions) for correct use of format strings. + /// Returns true if a format string has been fully checked. bool CheckFormatArguments(const FormatAttr *Format, ArrayRef Args, bool IsCXXMember, VariadicCallType CallType, SourceLocation Loc, @@ -2255,18 +2567,32 @@ class Sema final : public SemaBase { void CheckInfNaNFunction(const CallExpr *Call, const FunctionDecl *FDecl); + /// Warn when using the wrong abs() function. void CheckAbsoluteValueFunction(const CallExpr *Call, const FunctionDecl *FDecl); void CheckMaxUnsignedZero(const CallExpr *Call, const FunctionDecl *FDecl); + /// Check for dangerous or invalid arguments to memset(). + /// + /// This issues warnings on known problematic, dangerous or unspecified + /// arguments to the standard 'memset', 'memcpy', 'memmove', and 'memcmp' + /// function calls. + /// + /// \param Call The call expression to diagnose. void CheckMemaccessArguments(const CallExpr *Call, unsigned BId, IdentifierInfo *FnName); + // Warn if the user has made the 'size' argument to strlcpy or strlcat + // be the size of the source, instead of the destination. void CheckStrlcpycatArguments(const CallExpr *Call, IdentifierInfo *FnName); + // Warn on anti-patterns as the 'size' argument to strncat. + // The correct size argument should look like following: + // strncat(dst, src, sizeof(dst) - strlen(dest) - 1); void CheckStrncatArguments(const CallExpr *Call, IdentifierInfo *FnName); + /// Alerts the user that they are attempting to free a non-malloc'd object. void CheckFreeArguments(const CallExpr *E); void CheckReturnValExpr(Expr *RetValExp, QualType lhsType, @@ -2274,8 +2600,21 @@ class Sema final : public SemaBase { const AttrVec *Attrs = nullptr, const FunctionDecl *FD = nullptr); + /// Diagnoses "dangerous" implicit conversions within the given + /// expression (which is a full expression). Implements -Wconversion + /// and -Wsign-compare. + /// + /// \param CC the "context" location of the implicit conversion, i.e. + /// the most location of the syntactic entity requiring the implicit + /// conversion void CheckImplicitConversions(Expr *E, SourceLocation CC = SourceLocation()); + + /// CheckBoolLikeConversion - Check conversion of given expression to boolean. + /// Input argument E is a logical expression. void CheckBoolLikeConversion(Expr *E, SourceLocation CC); + + /// Diagnose when expression is an integer constant expression and its + /// evaluation results in integer overflow void CheckForIntOverflow(const Expr *E); void CheckUnsequencedOperations(const Expr *E); @@ -2395,10 +2734,37 @@ class Sema final : public SemaBase { bool RequireCompleteEnumDecl(EnumDecl *D, SourceLocation L, CXXScopeSpec *SS = nullptr); + /// Compute the DeclContext that is associated with the given type. + /// + /// \param T the type for which we are attempting to find a DeclContext. + /// + /// \returns the declaration context represented by the type T, + /// or NULL if the declaration context cannot be computed (e.g., because it is + /// dependent and not the current instantiation). DeclContext *computeDeclContext(QualType T); + + /// Compute the DeclContext that is associated with the given + /// scope specifier. + /// + /// \param SS the C++ scope specifier as it appears in the source + /// + /// \param EnteringContext when true, we will be entering the context of + /// this scope specifier, so we can retrieve the declaration context of a + /// class template or class template partial specialization even if it is + /// not the current instantiation. + /// + /// \returns the declaration context represented by the scope specifier @p SS, + /// or NULL if the declaration context cannot be computed (e.g., because it is + /// dependent and not the current instantiation). DeclContext *computeDeclContext(const CXXScopeSpec &SS, bool EnteringContext = false); bool isDependentScopeSpecifier(const CXXScopeSpec &SS); + + /// If the given nested name specifier refers to the current + /// instantiation, return the declaration that corresponds to that + /// current instantiation (C++0x [temp.dep.type]p1). + /// + /// \param NNS a dependent nested name specifier. CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS); /// The parser has parsed a global nested-name-specifier '::'. @@ -2424,8 +2790,18 @@ class Sema final : public SemaBase { bool ActOnSuperScopeSpecifier(SourceLocation SuperLoc, SourceLocation ColonColonLoc, CXXScopeSpec &SS); + /// Determines whether the given declaration is an valid acceptable + /// result for name lookup of a nested-name-specifier. + /// \param SD Declaration checked for nested-name-specifier. + /// \param IsExtension If not null and the declaration is accepted as an + /// extension, the pointed variable is assigned true. bool isAcceptableNestedNameSpecifier(const NamedDecl *SD, bool *CanCorrect = nullptr); + + /// If the given nested-name-specifier begins with a bare identifier + /// (e.g., Base::), perform name lookup for that identifier as a + /// nested-name-specifier within the given scope, and return the result of + /// that name lookup. NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS); /// Keeps information about an identifier in a nested-name-spec. @@ -2457,6 +2833,37 @@ class Sema final : public SemaBase { IdentifierLoc(IdLoc), CCLoc(ColonColonLoc) {} }; + /// Build a new nested-name-specifier for "identifier::", as described + /// by ActOnCXXNestedNameSpecifier. + /// + /// \param S Scope in which the nested-name-specifier occurs. + /// \param IdInfo Parser information about an identifier in the + /// nested-name-spec. + /// \param EnteringContext If true, enter the context specified by the + /// nested-name-specifier. + /// \param SS Optional nested name specifier preceding the identifier. + /// \param ScopeLookupResult Provides the result of name lookup within the + /// scope of the nested-name-specifier that was computed at template + /// definition time. + /// \param ErrorRecoveryLookup Specifies if the method is called to improve + /// error recovery and what kind of recovery is performed. + /// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':' + /// are allowed. The bool value pointed by this parameter is set to + /// 'true' if the identifier is treated as if it was followed by ':', + /// not '::'. + /// \param OnlyNamespace If true, only considers namespaces in lookup. + /// + /// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in + /// that it contains an extra parameter \p ScopeLookupResult, which provides + /// the result of name lookup within the scope of the nested-name-specifier + /// that was computed at template definition time. + /// + /// If ErrorRecoveryLookup is true, then this call is used to improve error + /// recovery. This means that it should not emit diagnostics, it should + /// just return true on failure. It also means it should only return a valid + /// scope if it *knows* that the result is correct. It should not return in a + /// dependent context, for example. Nor will it extend \p SS with the scope + /// specifier. bool BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, bool EnteringContext, CXXScopeSpec &SS, NamedDecl *ScopeLookupResult, @@ -2528,6 +2935,12 @@ class Sema final : public SemaBase { SourceLocation ColonColonLoc, QualType Type); + /// IsInvalidUnlessNestedName - This method is used for error recovery + /// purposes to determine whether the specified identifier is only valid as + /// a nested name specifier, for example a namespace name. It is + /// conservatively correct to always return false from this method. + /// + /// The arguments are the same as those passed to ActOnCXXNestedNameSpecifier. bool IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS, NestedNameSpecInfo &IdInfo, bool EnteringContext); @@ -2685,7 +3098,7 @@ class Sema final : public SemaBase { TentativeDefinitionsType TentativeDefinitions; /// All the external declarations encoutered and used in the TU. - SmallVector ExternalDeclarations; + SmallVector ExternalDeclarations; /// Generally null except when we temporarily switch decl contexts, /// like in \see SemaObjC::ActOnObjCTemporaryExitContainerContext. @@ -2704,6 +3117,14 @@ class Sema final : public SemaBase { DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType = nullptr); + /// If the identifier refers to a type name within this scope, + /// return the declaration of that type. + /// + /// This routine performs ordinary name lookup of the identifier II + /// within the given scope, with optional C++ scope specifier SS, to + /// determine whether the name refers to a type. If so, returns an + /// opaque pointer (actually a QualType) corresponding to that + /// type. Otherwise, returns NULL. ParsedType getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, Scope *S, CXXScopeSpec *SS = nullptr, bool isClassName = false, bool HasTrailingDot = false, @@ -2714,7 +3135,28 @@ class Sema final : public SemaBase { ImplicitTypenameContext AllowImplicitTypename = ImplicitTypenameContext::No, IdentifierInfo **CorrectedII = nullptr); + + /// isTagName() - This method is called *for error recovery purposes only* + /// to determine if the specified name is a valid tag name ("struct foo"). If + /// so, this returns the TST for the tag corresponding to it (TST_enum, + /// TST_union, TST_struct, TST_interface, TST_class). This is used to + /// diagnose cases in C where the user forgot to specify the tag. TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S); + + /// isMicrosoftMissingTypename - In Microsoft mode, within class scope, + /// if a CXXScopeSpec's type is equal to the type of one of the base classes + /// then downgrade the missing typename error to a warning. + /// This is needed for MSVC compatibility; Example: + /// @code + /// template class A { + /// public: + /// typedef int TYPE; + /// }; + /// template class B : public A { + /// public: + /// A::TYPE a; // no typename required because A is a base class. + /// }; + /// @endcode bool isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S); void DiagnoseUnknownTypeName(IdentifierInfo *&II, SourceLocation IILoc, Scope *S, CXXScopeSpec *SS, @@ -2967,11 +3409,47 @@ class Sema final : public SemaBase { NamedDecl *HandleDeclarator(Scope *S, Declarator &D, MultiTemplateParamsArg TemplateParameterLists); + + /// Attempt to fold a variable-sized type to a constant-sized type, returning + /// true if we were successful. bool tryToFixVariablyModifiedVarType(TypeSourceInfo *&TInfo, QualType &T, SourceLocation Loc, unsigned FailedFoldDiagID); + + /// Register the given locally-scoped extern "C" declaration so + /// that it can be found later for redeclarations. We include any extern "C" + /// declaration that is not visible in the translation unit here, not just + /// function-scope declarations. void RegisterLocallyScopedExternCDecl(NamedDecl *ND, Scope *S); + + /// DiagnoseClassNameShadow - Implement C++ [class.mem]p13: + /// If T is the name of a class, then each of the following shall have a + /// name different from T: + /// - every static data member of class T; + /// - every member function of class T + /// - every member of class T that is itself a type; + /// \returns true if the declaration name violates these rules. bool DiagnoseClassNameShadow(DeclContext *DC, DeclarationNameInfo Info); + + /// Diagnose a declaration whose declarator-id has the given + /// nested-name-specifier. + /// + /// \param SS The nested-name-specifier of the declarator-id. + /// + /// \param DC The declaration context to which the nested-name-specifier + /// resolves. + /// + /// \param Name The name of the entity being declared. + /// + /// \param Loc The location of the name of the entity being declared. + /// + /// \param IsMemberSpecialization Whether we are declaring a member + /// specialization. + /// + /// \param TemplateId The template-id, if any. + /// + /// \returns true if we cannot safely recover from this error, false + /// otherwise. bool diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC, DeclarationName Name, SourceLocation Loc, TemplateIdAnnotation *TemplateId, @@ -2979,20 +3457,43 @@ class Sema final : public SemaBase { bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key); + /// Diagnose function specifiers on a declaration of an identifier that + /// does not identify a function. void DiagnoseFunctionSpecifiers(const DeclSpec &DS); + + /// Return the declaration shadowed by the given typedef \p D, or null + /// if it doesn't shadow any declaration or shadowing warnings are disabled. NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D, const LookupResult &R); + + /// Return the declaration shadowed by the given variable \p D, or null + /// if it doesn't shadow any declaration or shadowing warnings are disabled. NamedDecl *getShadowedDeclaration(const VarDecl *D, const LookupResult &R); + + /// Return the declaration shadowed by the given variable \p D, or null + /// if it doesn't shadow any declaration or shadowing warnings are disabled. NamedDecl *getShadowedDeclaration(const BindingDecl *D, const LookupResult &R); + /// Diagnose variable or built-in function shadowing. Implements + /// -Wshadow. + /// + /// This method is called whenever a VarDecl is added to a "useful" + /// scope. + /// + /// \param ShadowedDecl the declaration that is shadowed by the given variable + /// \param R the lookup of the name void CheckShadow(NamedDecl *D, NamedDecl *ShadowedDecl, const LookupResult &R); + + /// Check -Wshadow without the advantage of a previous lookup. void CheckShadow(Scope *S, VarDecl *D); /// Warn if 'E', which is an expression that is about to be modified, refers /// to a shadowing declaration. void CheckShadowingDeclModification(Expr *E, SourceLocation Loc); + /// Diagnose shadowing for variables shadowed in the lambda record \p LambdaRD + /// when these variables are captured by the lambda. void DiagnoseShadowingLambdaDecls(const sema::LambdaScopeInfo *LSI); void handleTagNumbering(const TagDecl *Tag, Scope *TagScope); @@ -3002,6 +3503,10 @@ class Sema final : public SemaBase { NamedDecl *ActOnTypedefDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous); + + /// ActOnTypedefNameDecl - Perform semantic checking for a declaration which + /// declares a typedef-name, either using the 'typedef' type specifier or via + /// a C++0x [dcl.typedef]p2 alias-declaration: 'using T = A;'. NamedDecl *ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *D, LookupResult &Previous, bool &Redeclaration); NamedDecl *ActOnVariableDeclarator( @@ -3009,7 +3514,18 @@ class Sema final : public SemaBase { LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, bool &AddToScope, ArrayRef Bindings = std::nullopt); - // Returns true if the variable declaration is a redeclaration + /// Perform semantic checking on a newly-created variable + /// declaration. + /// + /// This routine performs all of the type-checking required for a + /// variable declaration once it has been built. It is used both to + /// check variables after they have been parsed and their declarators + /// have been translated into a declaration, and to check variables + /// that have been instantiated from a template. + /// + /// Sets NewVD->isInvalidDecl() if an error was encountered. + /// + /// Returns true if the variable declaration is a redeclaration. bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous); void CheckVariableDeclarationType(VarDecl *NewVD); void CheckCompleteVariableDeclaration(VarDecl *VD); @@ -3019,22 +3535,78 @@ class Sema final : public SemaBase { LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, bool &AddToScope); + + /// AddOverriddenMethods - See if a method overrides any in the base classes, + /// and if so, check that it's a valid override and remember it. bool AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD); - // Returns true if the function declaration is a redeclaration + /// Perform semantic checking of a new function declaration. + /// + /// Performs semantic analysis of the new function declaration + /// NewFD. This routine performs all semantic checking that does not + /// require the actual declarator involved in the declaration, and is + /// used both for the declaration of functions as they are parsed + /// (called via ActOnDeclarator) and for the declaration of functions + /// that have been instantiated via C++ template instantiation (called + /// via InstantiateDecl). + /// + /// \param IsMemberSpecialization whether this new function declaration is + /// a member specialization (that replaces any definition provided by the + /// previous declaration). + /// + /// This sets NewFD->isInvalidDecl() to true if there was an error. + /// + /// \returns true if the function declaration is a redeclaration. bool CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, LookupResult &Previous, bool IsMemberSpecialization, bool DeclIsDefn); + + /// Checks if the new declaration declared in dependent context must be + /// put in the same redeclaration chain as the specified declaration. + /// + /// \param D Declaration that is checked. + /// \param PrevDecl Previous declaration found with proper lookup method for + /// the same declaration name. + /// \returns True if D must be added to the redeclaration chain which PrevDecl + /// belongs to. bool shouldLinkDependentDeclWithPrevious(Decl *D, Decl *OldDecl); + + /// Determines if we can perform a correct type check for \p D as a + /// redeclaration of \p PrevDecl. If not, we can generally still perform a + /// best-effort check. + /// + /// \param NewD The new declaration. + /// \param OldD The old declaration. + /// \param NewT The portion of the type of the new declaration to check. + /// \param OldT The portion of the type of the old declaration to check. bool canFullyTypeCheckRedeclaration(ValueDecl *NewD, ValueDecl *OldD, QualType NewT, QualType OldT); void CheckMain(FunctionDecl *FD, const DeclSpec &D); void CheckMSVCRTEntryPoint(FunctionDecl *FD); + + /// Returns an implicit CodeSegAttr if a __declspec(code_seg) is found on a + /// containing class. Otherwise it will return implicit SectionAttr if the + /// function is a definition and there is an active value on CodeSegStack + /// (from the current #pragma code-seg value). + /// + /// \param FD Function being declared. + /// \param IsDefinition Whether it is a definition or just a declaration. + /// \returns A CodeSegAttr or SectionAttr to apply to the function or + /// nullptr if no attribute should be added. Attr *getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD, bool IsDefinition); + + /// Common checks for a parameter-declaration that should apply to both + /// function parameters and non-type template parameters. void CheckFunctionOrTemplateParamDeclarator(Scope *S, Declarator &D); + + /// ActOnParamDeclarator - Called from Parser::ParseFunctionDeclarator() + /// to introduce parameters into function prototype scope. Decl *ActOnParamDeclarator(Scope *S, Declarator &D, SourceLocation ExplicitThisLoc = {}); + + /// Synthesizes a variable for a parameter arising from a + /// typedef. ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC, SourceLocation Loc, QualType T); ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc, @@ -3084,8 +3656,16 @@ class Sema final : public SemaBase { NonTrivialCUnionContext UseContext, unsigned NonTrivialKind); + /// AddInitializerToDecl - Adds the initializer Init to the + /// declaration dcl. If DirectInit is true, this is C++ direct + /// initialization rather than copy initialization. void AddInitializerToDecl(Decl *dcl, Expr *init, bool DirectInit); void ActOnUninitializedDecl(Decl *dcl); + + /// ActOnInitializerError - Given that there was an error parsing an + /// initializer for the given declaration, try to at least re-establish + /// invariants such as whether a variable's type is either dependent or + /// complete. void ActOnInitializerError(Decl *Dcl); void ActOnCXXForRangeDecl(Decl *D); @@ -3093,11 +3673,19 @@ class Sema final : public SemaBase { IdentifierInfo *Ident, ParsedAttributes &Attrs); + /// Check if VD needs to be dllexport/dllimport due to being in a + /// dllexport/import function. void CheckStaticLocalForDllExport(VarDecl *VD); void CheckThreadLocalForLargeAlignment(VarDecl *VD); + + /// FinalizeDeclaration - called by ParseDeclarationAfterDeclarator to perform + /// any semantic actions necessary after any initializer has been attached. void FinalizeDeclaration(Decl *D); DeclGroupPtrTy FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, ArrayRef Group); + + /// BuildDeclaratorGroup - convert a list of declarations into a declaration + /// group, performing any necessary semantic checking. DeclGroupPtrTy BuildDeclaratorGroup(MutableArrayRef Group); /// Should be called on all declarations that might have attached @@ -3154,6 +3742,18 @@ class Sema final : public SemaBase { /// \c constexpr in C++11 or has an 'auto' return type in C++14). bool canSkipFunctionBody(Decl *D); + /// Given the set of return statements within a function body, + /// compute the variables that are subject to the named return value + /// optimization. + /// + /// Each of the variables that is subject to the named return value + /// optimization will be marked as NRVO variables in the AST, and any + /// return statement that has a marked NRVO variable as its NRVO candidate can + /// use the named return value optimization. + /// + /// This function applies a very simplistic algorithm for NRVO: if every + /// return statement in the scope of a variable has the same NRVO candidate, + /// that candidate is an NRVO variable. void computeNRVO(Stmt *Body, sema::FunctionScopeInfo *Scope); Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body); Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation); @@ -3183,15 +3783,25 @@ class Sema final : public SemaBase { void ActOnPopScope(SourceLocation Loc, Scope *S); + /// ParsedFreeStandingDeclSpec - This method is invoked when a declspec with + /// no declarator (e.g. "struct foo;") is parsed. Decl *ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, DeclSpec &DS, const ParsedAttributesView &DeclAttrs, RecordDecl *&AnonRecord); + + /// ParsedFreeStandingDeclSpec - This method is invoked when a declspec with + /// no declarator (e.g. "struct foo;") is parsed. It also accepts template + /// parameters to cope with template friend declarations. Decl *ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, DeclSpec &DS, const ParsedAttributesView &DeclAttrs, MultiTemplateParamsArg TemplateParams, bool IsExplicitInstantiation, RecordDecl *&AnonRecord); + /// BuildAnonymousStructOrUnion - Handle the declaration of an + /// anonymous structure or union. Anonymous unions are a C++ feature + /// (C++ [class.union]) and a C11 feature; anonymous structures + /// are a C11 feature and GNU C++ extension. Decl *BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, AccessSpecifier AS, RecordDecl *Record, const PrintingPolicy &Policy); @@ -3200,8 +3810,24 @@ class Sema final : public SemaBase { /// a tag declaration is an anonymous union or struct. void ActOnDefinedDeclarationSpecifier(Decl *D); + /// Emit diagnostic warnings for placeholder members. + /// We can only do that after the class is fully constructed, + /// as anonymous union/structs can insert placeholders + /// in their parent scope (which might be a Record). void DiagPlaceholderFieldDeclDefinitions(RecordDecl *Record); + /// BuildMicrosoftCAnonymousStruct - Handle the declaration of an + /// Microsoft C anonymous structure. + /// Ref: http://msdn.microsoft.com/en-us/library/z2cx9y4f.aspx + /// Example: + /// + /// struct A { int a; }; + /// struct B { struct A; int b; }; + /// + /// void foo() { + /// B var; + /// var.a = 3; + /// } Decl *BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS, RecordDecl *Record); @@ -3223,6 +3849,10 @@ class Sema final : public SemaBase { /// what kind of non-tag type this is. NonTagKind getNonTagTypeDeclKind(const Decl *D, TagTypeKind TTK); + /// Determine whether a tag with a given kind is acceptable + /// as a redeclaration of the given tag declaration. + /// + /// \returns true if the new tag kind is acceptable, false otherwise. bool isAcceptableTagRedeclaration(const TagDecl *Previous, TagTypeKind NewTag, bool isDefinition, SourceLocation NewTagLoc, const IdentifierInfo *Name); @@ -3237,6 +3867,16 @@ class Sema final : public SemaBase { OOK_Macro, }; + /// This is invoked when we see 'struct foo' or 'struct {'. In the + /// former case, Name will be non-null. In the later case, Name will be null. + /// TagSpec indicates what kind of tag this is. TUK indicates whether this is + /// a reference/declaration/definition of a tag. + /// + /// \param IsTypeSpecifier \c true if this is a type-specifier (or + /// trailing-type-specifier) other than one in an alias-declaration. + /// + /// \param SkipBody If non-null, will be set to indicate if the caller should + /// skip the definition of this tag and treat it as if it were a declaration. DeclResult ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, @@ -3249,13 +3889,26 @@ class Sema final : public SemaBase { bool IsTypeSpecifier, bool IsTemplateParamOrArg, OffsetOfKind OOK, SkipBodyInfo *SkipBody = nullptr); + /// ActOnField - Each field of a C struct/union is passed into this in order + /// to create a FieldDecl object for it. Decl *ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart, Declarator &D, Expr *BitfieldWidth); + /// HandleField - Analyze a field of a C struct or a C++ data member. FieldDecl *HandleField(Scope *S, RecordDecl *TagD, SourceLocation DeclStart, Declarator &D, Expr *BitfieldWidth, InClassInitStyle InitStyle, AccessSpecifier AS); + /// Build a new FieldDecl and check its well-formedness. + /// + /// This routine builds a new FieldDecl given the fields name, type, + /// record, etc. \p PrevDecl should refer to any previous declaration + /// with the same name and in the same scope as the field to be + /// created. + /// + /// \returns a new FieldDecl. + /// + /// \todo The Declarator argument is a hack. It will be removed once FieldDecl *CheckFieldDecl(DeclarationName Name, QualType T, TypeSourceInfo *TInfo, RecordDecl *Record, SourceLocation Loc, bool Mutable, @@ -3265,6 +3918,10 @@ class Sema final : public SemaBase { bool CheckNontrivialField(FieldDecl *FD); + /// ActOnLastBitfield - This routine handles synthesized bitfields rules for + /// class and class extensions. For every class \@interface and class + /// extension \@interface, if the last ivar is a bitfield of any type, + /// then add an implicit `char :0` ivar to the end of that interface. void ActOnLastBitfield(SourceLocation DeclStart, SmallVectorImpl &AllIvarDecls); @@ -3312,7 +3969,12 @@ class Sema final : public SemaBase { EnumConstantDecl *LastEnumConst, SourceLocation IdLoc, IdentifierInfo *Id, Expr *val); + + /// Check that this is a valid underlying type for an enum declaration. bool CheckEnumUnderlyingType(TypeSourceInfo *TI); + + /// Check whether this is a valid redeclaration of a previous enumeration. + /// \return true if the redeclaration was invalid. bool CheckEnumRedeclaration(SourceLocation EnumLoc, bool IsScoped, QualType EnumUnderlyingTy, bool IsFixed, const EnumDecl *Prev); @@ -3389,26 +4051,101 @@ class Sema final : public SemaBase { AMK_OptionalProtocolImplementation }; + /// mergeDeclAttributes - Copy attributes from the Old decl to the New one. void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); + + /// MergeTypedefNameDecl - We just parsed a typedef 'New' which has the + /// same name and scope as a previous declaration 'Old'. Figure out + /// how to resolve this situation, merging decls or emitting + /// diagnostics as appropriate. If there was an error, set New to be invalid. void MergeTypedefNameDecl(Scope *S, TypedefNameDecl *New, LookupResult &OldDecls); + + /// MergeFunctionDecl - We just parsed a function 'New' from + /// declarator D which has the same name and scope as a previous + /// declaration 'Old'. Figure out how to resolve this situation, + /// merging decls or emitting diagnostics as appropriate. + /// + /// In C++, New and Old must be declarations that are not + /// overloaded. Use IsOverload to determine whether New and Old are + /// overloaded, and to select the Old declaration that New should be + /// merged with. + /// + /// Returns true if there was an error, false otherwise. bool MergeFunctionDecl(FunctionDecl *New, NamedDecl *&Old, Scope *S, bool MergeTypeWithOld, bool NewDeclIsDefn); + + /// Completes the merge of two function declarations that are + /// known to be compatible. + /// + /// This routine handles the merging of attributes and other + /// properties of function declarations from the old declaration to + /// the new declaration, once we know that New is in fact a + /// redeclaration of Old. + /// + /// \returns false bool MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old, Scope *S, bool MergeTypeWithOld); void mergeObjCMethodDecls(ObjCMethodDecl *New, ObjCMethodDecl *Old); + + /// MergeVarDecl - We just parsed a variable 'New' which has the same name + /// and scope as a previous declaration 'Old'. Figure out how to resolve this + /// situation, merging decls or emitting diagnostics as appropriate. + /// + /// Tentative definition rules (C99 6.9.2p2) are checked by + /// FinalizeDeclaratorGroup. Unfortunately, we can't analyze tentative + /// definitions here, since the initializer hasn't been attached. void MergeVarDecl(VarDecl *New, LookupResult &Previous); + + /// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and + /// scope as a previous declaration 'Old'. Figure out how to merge their + /// types, emitting diagnostics as appropriate. + /// + /// Declarations using the auto type specifier (C++ [decl.spec.auto]) call + /// back to here in AddInitializerToDecl. We can't check them before the + /// initializer is attached. void MergeVarDeclTypes(VarDecl *New, VarDecl *Old, bool MergeTypeWithOld); + + /// We've just determined that \p Old and \p New both appear to be definitions + /// of the same variable. Either diagnose or fix the problem. bool checkVarDeclRedefinition(VarDecl *OldDefn, VarDecl *NewDefn); void notePreviousDefinition(const NamedDecl *Old, SourceLocation New); + /// Filters out lookup results that don't fall within the given scope + /// as determined by isDeclInScope. void FilterLookupForScope(LookupResult &R, DeclContext *Ctx, Scope *S, bool ConsiderLinkage, bool AllowInlineNamespace); + /// We've determined that \p New is a redeclaration of \p Old. Check that they + /// have compatible owning modules. bool CheckRedeclarationModuleOwnership(NamedDecl *New, NamedDecl *Old); + + /// [module.interface]p6: + /// A redeclaration of an entity X is implicitly exported if X was introduced + /// by an exported declaration; otherwise it shall not be exported. bool CheckRedeclarationExported(NamedDecl *New, NamedDecl *Old); + + /// A wrapper function for checking the semantic restrictions of + /// a redeclaration within a module. bool CheckRedeclarationInModule(NamedDecl *New, NamedDecl *Old); + + /// Check the redefinition in C++20 Modules. + /// + /// [basic.def.odr]p14: + /// For any definable item D with definitions in multiple translation units, + /// - if D is a non-inline non-templated function or variable, or + /// - if the definitions in different translation units do not satisfy the + /// following requirements, + /// the program is ill-formed; a diagnostic is required only if the + /// definable item is attached to a named module and a prior definition is + /// reachable at the point where a later definition occurs. + /// - Each such definition shall not be attached to a named module + /// ([module.unit]). + /// - Each such definition shall consist of the same sequence of tokens, ... + /// ... + /// + /// Return true if the redefinition is not allowed. Return false otherwise. bool IsRedefinitionInModule(const NamedDecl *New, const NamedDecl *Old) const; bool ShouldWarnIfUnusedFileScopedDecl(const DeclaratorDecl *D) const; @@ -3424,16 +4161,47 @@ class Sema final : public SemaBase { void DiagnoseUnusedNestedTypedefs(const RecordDecl *D, DiagReceiverTy DiagReceiver); void DiagnoseUnusedDecl(const NamedDecl *ND); + + /// DiagnoseUnusedDecl - Emit warnings about declarations that are not used + /// unless they are marked attr(unused). void DiagnoseUnusedDecl(const NamedDecl *ND, DiagReceiverTy DiagReceiver); /// If VD is set but not otherwise used, diagnose, for a parameter or a /// variable. void DiagnoseUnusedButSetDecl(const VarDecl *VD, DiagReceiverTy DiagReceiver); + /// getNonFieldDeclScope - Retrieves the innermost scope, starting + /// from S, where a non-field would be declared. This routine copes + /// with the difference between C and C++ scoping rules in structs and + /// unions. For example, the following code is well-formed in C but + /// ill-formed in C++: + /// @code + /// struct S6 { + /// enum { BAR } e; + /// }; + /// + /// void test_S6() { + /// struct S6 a; + /// a.e = BAR; + /// } + /// @endcode + /// For the declaration of BAR, this routine will return a different + /// scope. The scope S will be the scope of the unnamed enumeration + /// within S6. In C++, this routine will return the scope associated + /// with S6, because the enumeration's scope is a transparent + /// context but structures can contain non-field names. In C, this + /// routine will return the translation unit scope, since the + /// enumeration's scope is a transparent context and structures cannot + /// contain non-field names. Scope *getNonFieldDeclScope(Scope *S); FunctionDecl *CreateBuiltin(IdentifierInfo *II, QualType Type, unsigned ID, SourceLocation Loc); + + /// LazilyCreateBuiltin - The specified Builtin-ID was first used at + /// file scope. lazily create a decl for it. ForRedeclaration is true + /// if we're creating this built-in in anticipation of redeclaring the + /// built-in. NamedDecl *LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID, Scope *S, bool ForRedeclaration, SourceLocation Loc); @@ -3441,7 +4209,11 @@ class Sema final : public SemaBase { /// Valid types should not have multiple attributes with different CCs. const AttributedType *getCallingConvAttributedType(QualType T) const; + /// GetNameForDeclarator - Determine the full declaration name for the + /// given Declarator. DeclarationNameInfo GetNameForDeclarator(Declarator &D); + + /// Retrieves the declaration name from a parsed unqualified-id. DeclarationNameInfo GetNameFromUnqualifiedId(const UnqualifiedId &Name); /// ParsingInitForAutoVars - a set of declarations with auto types for which @@ -3453,6 +4225,8 @@ class Sema final : public SemaBase { void deduceOpenCLAddressSpace(ValueDecl *decl); + /// Adjust the \c DeclContext for a function or variable that might be a + /// function-local external declaration. static bool adjustContextForLocalExternDecl(DeclContext *&DC); void MarkTypoCorrectedFunctionDefinition(const NamedDecl *F); @@ -3485,10 +4259,29 @@ class Sema final : public SemaBase { static bool CanBeGetReturnObject(const FunctionDecl *FD); static bool CanBeGetReturnTypeOnAllocFailure(const FunctionDecl *FD); + /// ImplicitlyDefineFunction - An undeclared identifier was used in a function + /// call, forming a call to an implicitly defined function (per C99 6.5.1p2). NamedDecl *ImplicitlyDefineFunction(SourceLocation Loc, IdentifierInfo &II, Scope *S); + + /// If this function is a C++ replaceable global allocation function + /// (C++2a [basic.stc.dynamic.allocation], C++2a [new.delete]), + /// adds any function attributes that we know a priori based on the standard. + /// + /// We need to check for duplicate attributes both here and where user-written + /// attributes are applied to declarations. void AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction( FunctionDecl *FD); + + /// Adds any function attributes that we know a priori based on + /// the declaration of this function. + /// + /// These attributes can apply both to implicitly-declared builtins + /// (like __builtin___printf_chk) or to library-declared functions + /// like NSLog or printf. + /// + /// We need to check for duplicate attributes both here and where user-written + /// attributes are applied to declarations. void AddKnownFunctionAttributes(FunctionDecl *FD); /// VerifyBitField - verifies that a bit field expression is an ICE and has @@ -3545,6 +4338,18 @@ class Sema final : public SemaBase { /// it looks like the user is trying to modify the shadowing declaration. llvm::DenseMap ShadowingDecls; + // We need this to handle + // + // typedef struct { + // void *foo() { return 0; } + // } A; + // + // When we see foo we don't know if after the typedef we will get 'A' or '*A' + // for example. If 'A', foo will have external linkage. If we have '*A', + // foo will have no linkage. Since we can't know until we get to the end + // of the typedef, this function finds out if D might have non-external + // linkage. Callers should verify at the end of the TU if it D has external + // linkage or not. static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD); ///@} @@ -3660,9 +4465,17 @@ class Sema final : public SemaBase { /// This is only necessary for issuing pretty diagnostics. ExtVectorDeclsType ExtVectorDecls; + /// Check if the argument \p E is a ASCII string literal. If not emit an error + /// and return false, otherwise set \p Str to the value of the string literal + /// and return true. bool checkStringLiteralArgumentAttr(const AttributeCommonInfo &CI, const Expr *E, StringRef &Str, SourceLocation *ArgLocation = nullptr); + + /// Check if the argument \p ArgNum of \p Attr is a ASCII string literal. + /// If not emit an error and return false. If the argument is an identifier it + /// will emit an error with a fixit hint and treat it as if it was a string + /// literal. bool checkStringLiteralArgumentAttr(const ParsedAttr &Attr, unsigned ArgNum, StringRef &Str, SourceLocation *ArgLocation = nullptr); @@ -3701,14 +4514,26 @@ class Sema final : public SemaBase { SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI, StringRef Name); + /// Used to implement to perform semantic checking on + /// attribute((section("foo"))) specifiers. + /// + /// In this case, "foo" is passed in to be checked. If the section + /// specifier is invalid, return an Error that indicates the problem. + /// + /// This is a simple quality of implementation feature to catch errors + /// and give good diagnostics in cases when the assembler or code generator + /// would otherwise reject the section specifier. llvm::Error isValidSectionSpecifier(StringRef Str); bool checkSectionName(SourceLocation LiteralLoc, StringRef Str); CodeSegAttr *mergeCodeSegAttr(Decl *D, const AttributeCommonInfo &CI, StringRef Name); + // Check for things we'd like to warn about. Multiversioning issues are + // handled later in the process, once we know how many exist. bool checkTargetAttr(SourceLocation LiteralLoc, StringRef Str); - bool checkTargetVersionAttr(SourceLocation LiteralLoc, Decl *D, - StringRef &Str, bool &isDefault); + + /// Check Target Version attrs + bool checkTargetVersionAttr(SourceLocation Loc, Decl *D, StringRef Str); bool checkTargetClonesAttrString( SourceLocation LiteralLoc, StringRef Str, const StringLiteral *Literal, Decl *D, bool &HasDefault, bool &HasCommas, bool &HasNotDefault, @@ -3760,6 +4585,8 @@ class Sema final : public SemaBase { const ParsedAttr &attr, CallingConv &CC, const FunctionDecl *FD = nullptr, CUDAFunctionTarget CFT = CUDAFunctionTarget::InvalidTarget); + /// Checks a regparm attribute, returning true if it is ill-formed and + /// otherwise setting numParams to the appropriate value. bool CheckRegparmAttr(const ParsedAttr &attr, unsigned &value); /// Create an CUDALaunchBoundsAttr attribute. @@ -3791,7 +4618,8 @@ class Sema final : public SemaBase { EnforceTCBLeafAttr *mergeEnforceTCBLeafAttr(Decl *D, const EnforceTCBLeafAttr &AL); - // Helper for delayed processing of attributes. + /// Helper for delayed processing TransparentUnion or + /// BPFPreserveAccessIndexAttr attribute. void ProcessDeclAttributeDelayed(Decl *D, const ParsedAttributesView &AttrList); @@ -3822,17 +4650,30 @@ class Sema final : public SemaBase { bool IgnoreTypeAttributes; }; + /// ProcessDeclAttributeList - Apply all the decl attributes in the specified + /// attribute list to the specified decl, ignoring any type attributes. void ProcessDeclAttributeList(Scope *S, Decl *D, const ParsedAttributesView &AttrList, const ProcessDeclAttributeOptions &Options = ProcessDeclAttributeOptions()); + + /// Annotation attributes are the only attributes allowed after an access + /// specifier. bool ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl, const ParsedAttributesView &AttrList); + /// checkUnusedDeclAttributes - Given a declarator which is not being + /// used to build a declaration, complain about any decl attributes + /// which might be lying around on it. void checkUnusedDeclAttributes(Declarator &D); + /// DeclClonePragmaWeak - clone existing decl (maybe definition), + /// \#pragma weak needs a non-definition decl and source may not have one. NamedDecl *DeclClonePragmaWeak(NamedDecl *ND, const IdentifierInfo *II, SourceLocation Loc); + + /// DeclApplyPragmaWeak - A declaration (maybe definition) needs \#pragma weak + /// applied to it, possibly with an alias. void DeclApplyPragmaWeak(Scope *S, NamedDecl *ND, const WeakInfo &W); void ProcessPragmaWeak(Scope *S, Decl *D); @@ -3841,6 +4682,9 @@ class Sema final : public SemaBase { void PopParsingDeclaration(ParsingDeclState state, Decl *decl); + /// Given a set of delayed diagnostics, re-emit them as if they had + /// been delayed in the current context instead of in the given pool. + /// Essentially, this just moves them to the current pool. void redelayDiagnostics(sema::DelayedDiagnosticPool &pool); /// Check if IdxExpr is a valid parameter index for a function or @@ -3920,9 +4764,15 @@ class Sema final : public SemaBase { SourceLocation LBrace, const ParsedAttributesView &AttrList, UsingDirectiveDecl *&UsingDecl, bool IsNested); + + /// ActOnFinishNamespaceDef - This callback is called after a namespace is + /// exited. Decl is the DeclTy returned by ActOnStartNamespaceDef. void ActOnFinishNamespaceDef(Decl *Dcl, SourceLocation RBrace); NamespaceDecl *getStdNamespace() const; + + /// Retrieve the special "std" namespace, which may require us to + /// implicitly define the namespace. NamespaceDecl *getOrCreateStdNamespace(); CXXRecordDecl *getStdBadAlloc() const; @@ -3978,20 +4828,70 @@ class Sema final : public SemaBase { CXXScopeSpec &SS, SourceLocation IdentLoc, IdentifierInfo *Ident); + /// Remove decls we can't actually see from a lookup being used to declare + /// shadow using decls. + /// + /// \param S - The scope of the potential shadow decl + /// \param Previous - The lookup of a potential shadow decl's name. void FilterUsingLookup(Scope *S, LookupResult &lookup); + + /// Hides a using shadow declaration. This is required by the current + /// using-decl implementation when a resolvable using declaration in a + /// class is followed by a declaration which would hide or override + /// one or more of the using decl's targets; for example: + /// + /// struct Base { void foo(int); }; + /// struct Derived : Base { + /// using Base::foo; + /// void foo(int); + /// }; + /// + /// The governing language is C++03 [namespace.udecl]p12: + /// + /// When a using-declaration brings names from a base class into a + /// derived class scope, member functions in the derived class + /// override and/or hide member functions with the same name and + /// parameter types in a base class (rather than conflicting). + /// + /// There are two ways to implement this: + /// (1) optimistically create shadow decls when they're not hidden + /// by existing declarations, or + /// (2) don't create any shadow decls (or at least don't make them + /// visible) until we've fully parsed/instantiated the class. + /// The problem with (1) is that we might have to retroactively remove + /// a shadow decl, which requires several O(n) operations because the + /// decl structures are (very reasonably) not designed for removal. + /// (2) avoids this but is very fiddly and phase-dependent. void HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow); + + /// Determines whether to create a using shadow decl for a particular + /// decl, given the set of decls existing prior to this using lookup. bool CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Target, const LookupResult &PreviousDecls, UsingShadowDecl *&PrevShadow); + + /// Builds a shadow declaration corresponding to a 'using' declaration. UsingShadowDecl *BuildUsingShadowDecl(Scope *S, BaseUsingDecl *BUD, NamedDecl *Target, UsingShadowDecl *PrevDecl); + /// Checks that the given using declaration is not an invalid + /// redeclaration. Note that this is checking only for the using decl + /// itself, not for any ill-formedness among the UsingShadowDecls. bool CheckUsingDeclRedeclaration(SourceLocation UsingLoc, bool HasTypenameKeyword, const CXXScopeSpec &SS, SourceLocation NameLoc, const LookupResult &Previous); + + /// Checks that the given nested-name qualifier used in a using decl + /// in the current context is appropriately related to the current + /// scope. If an error is found, diagnoses it and returns true. + /// R is nullptr, if the caller has not (yet) done a lookup, otherwise it's + /// the result of that lookup. UD is likewise nullptr, except when we have an + /// already-populated UsingDecl whose shadow decls contain the same + /// information (i.e. we're instantiating a UsingDecl with non-dependent + /// scope). bool CheckUsingDeclQualifier(SourceLocation UsingLoc, bool HasTypename, const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, @@ -3999,6 +4899,11 @@ class Sema final : public SemaBase { const LookupResult *R = nullptr, const UsingDecl *UD = nullptr); + /// Builds a using declaration. + /// + /// \param IsInstantiation - Whether this call arises from an + /// instantiation of an unresolved using declaration. We treat + /// the lookup differently for these declarations. NamedDecl *BuildUsingDeclaration(Scope *S, AccessSpecifier AS, SourceLocation UsingLoc, bool HasTypenameKeyword, @@ -4015,6 +4920,7 @@ class Sema final : public SemaBase { NamedDecl *BuildUsingPackDecl(NamedDecl *InstantiatedFrom, ArrayRef Expansions); + /// Additional checks for a using declaration referring to a constructor name. bool CheckInheritingConstructorUsingDecl(UsingDecl *UD); /// Given a derived-class using shadow declaration for a constructor and the @@ -4031,8 +4937,8 @@ class Sema final : public SemaBase { const ParsedAttributesView &AttrList); Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS, SourceLocation UsingLoc, - SourceLocation EnumLoc, - SourceLocation IdentLoc, IdentifierInfo &II, + SourceLocation EnumLoc, SourceRange TyLoc, + const IdentifierInfo &II, ParsedType Ty, CXXScopeSpec *SS = nullptr); Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS, MultiTemplateParamsArg TemplateParams, @@ -4294,6 +5200,11 @@ class Sema final : public SemaBase { void DiagnoseImmediateEscalatingReason(FunctionDecl *FD); + /// Given a constructor and the set of arguments provided for the + /// constructor, convert the arguments and add any required default arguments + /// to form a proper call to this constructor. + /// + /// \returns true if an error occurred, false otherwise. bool CompleteConstructorCall(CXXConstructorDecl *Constructor, QualType DeclInitType, MultiExprArg ArgsPtr, SourceLocation Loc, @@ -4333,34 +5244,78 @@ class Sema final : public SemaBase { void DefineImplicitLambdaToBlockPointerConversion(SourceLocation CurrentLoc, CXXConversionDecl *Conv); + /// ActOnStartLinkageSpecification - Parsed the beginning of a C++ + /// linkage specification, including the language and (if present) + /// the '{'. ExternLoc is the location of the 'extern', Lang is the + /// language string literal. LBraceLoc, if valid, provides the location of + /// the '{' brace. Otherwise, this linkage specification does not + /// have any braces. Decl *ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc, Expr *LangStr, SourceLocation LBraceLoc); + + /// ActOnFinishLinkageSpecification - Complete the definition of + /// the C++ linkage specification LinkageSpec. If RBraceLoc is + /// valid, it's the position of the closing '}' brace in a linkage + /// specification that uses braces. Decl *ActOnFinishLinkageSpecification(Scope *S, Decl *LinkageSpec, SourceLocation RBraceLoc); //===--------------------------------------------------------------------===// // C++ Classes // + + /// Get the class that is directly named by the current context. This is the + /// class for which an unqualified-id in this scope could name a constructor + /// or destructor. + /// + /// If the scope specifier denotes a class, this will be that class. + /// If the scope specifier is empty, this will be the class whose + /// member-specification we are currently within. Otherwise, there + /// is no such class. CXXRecordDecl *getCurrentClass(Scope *S, const CXXScopeSpec *SS); + + /// isCurrentClassName - Determine whether the identifier II is the + /// name of the class type currently being defined. In the case of + /// nested classes, this will only return true if II is the name of + /// the innermost class. bool isCurrentClassName(const IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS = nullptr); + + /// Determine whether the identifier II is a typo for the name of + /// the class type currently being defined. If so, update it to the identifier + /// that should have been used. bool isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS); + /// ActOnAccessSpecifier - Parsed an access specifier followed by a colon. bool ActOnAccessSpecifier(AccessSpecifier Access, SourceLocation ASLoc, SourceLocation ColonLoc, const ParsedAttributesView &Attrs); + /// ActOnCXXMemberDeclarator - This is invoked when a C++ class member + /// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the + /// bitfield width if there is one, 'InitExpr' specifies the initializer if + /// one has been parsed, and 'InitStyle' is set if an in-class initializer is + /// present (but parsing it has been deferred). NamedDecl * ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, MultiTemplateParamsArg TemplateParameterLists, Expr *BitfieldWidth, const VirtSpecifiers &VS, InClassInitStyle InitStyle); + /// Enter a new C++ default initializer scope. After calling this, the + /// caller must call \ref ActOnFinishCXXInClassMemberInitializer, even if + /// parsing or instantiating the initializer failed. void ActOnStartCXXInClassMemberInitializer(); + + /// This is invoked after parsing an in-class initializer for a + /// non-static C++ class member, and after instantiating an in-class + /// initializer in a class template. Such actions are deferred until the class + /// is complete. void ActOnFinishCXXInClassMemberInitializer(Decl *VarDecl, SourceLocation EqualLoc, Expr *Init); + /// Handle a C++ member initializer using parentheses syntax. MemInitResult ActOnMemInitializer(Decl *ConstructorD, Scope *S, CXXScopeSpec &SS, IdentifierInfo *MemberOrBase, ParsedType TemplateTypeTy, @@ -4368,6 +5323,7 @@ class Sema final : public SemaBase { SourceLocation LParenLoc, ArrayRef Args, SourceLocation RParenLoc, SourceLocation EllipsisLoc); + /// Handle a C++ member initializer using braced-init-list syntax. MemInitResult ActOnMemInitializer(Decl *ConstructorD, Scope *S, CXXScopeSpec &SS, IdentifierInfo *MemberOrBase, @@ -4375,6 +5331,7 @@ class Sema final : public SemaBase { const DeclSpec &DS, SourceLocation IdLoc, Expr *InitList, SourceLocation EllipsisLoc); + /// Handle a C++ member initializer. MemInitResult BuildMemInitializer(Decl *ConstructorD, Scope *S, CXXScopeSpec &SS, IdentifierInfo *MemberOrBase, @@ -4462,8 +5419,14 @@ class Sema final : public SemaBase { /// \returns true if any work was done, false otherwise. bool DefineUsedVTables(); + /// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared + /// special functions, such as the default constructor, copy + /// constructor, or destructor, to the given C++ class (C++ + /// [special]p1). This routine can only be executed just before the + /// definition of the class is complete. void AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl); + /// ActOnMemInitializers - Handle the member initializers for a constructor. void ActOnMemInitializers(Decl *ConstructorDecl, SourceLocation ColonLoc, ArrayRef MemInits, bool AnyErrors); @@ -4476,31 +5439,66 @@ class Sema final : public SemaBase { void referenceDLLExportedClassMethods(); + /// Perform propagation of DLL attributes from a derived class to a + /// templated base class for MS compatibility. void propagateDLLAttrToBaseClassTemplate( CXXRecordDecl *Class, Attr *ClassAttr, ClassTemplateSpecializationDecl *BaseTemplateSpec, SourceLocation BaseLoc); + /// Perform semantic checks on a class definition that has been + /// completing, introducing implicitly-declared members, checking for + /// abstract types, etc. + /// + /// \param S The scope in which the class was parsed. Null if we didn't just + /// parse a class definition. + /// \param Record The completed class. void CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record); /// Check that the C++ class annoated with "trivial_abi" satisfies all the /// conditions that are needed for the attribute to have an effect. void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD); + /// Check that VTable Pointer authentication is only being set on the first + /// first instantiation of the vtable + void checkIncorrectVTablePointerAuthenticationAttribute(CXXRecordDecl &RD); + void ActOnFinishCXXMemberSpecification(Scope *S, SourceLocation RLoc, Decl *TagDecl, SourceLocation LBrac, SourceLocation RBrac, const ParsedAttributesView &AttrList); + + /// Perform any semantic analysis which needs to be delayed until all + /// pending class member declarations have been parsed. void ActOnFinishCXXMemberDecls(); void ActOnFinishCXXNonNestedClass(); + /// This is used to implement the constant expression evaluation part of the + /// attribute enable_if extension. There is nothing in standard C++ which + /// would require reentering parameters. void ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param); unsigned ActOnReenterTemplateScope(Decl *Template, llvm::function_ref EnterScope); void ActOnStartDelayedMemberDeclarations(Scope *S, Decl *Record); + + /// ActOnStartDelayedCXXMethodDeclaration - We have completed + /// parsing a top-level (non-nested) C++ class, and we are now + /// parsing those parts of the given Method declaration that could + /// not be parsed earlier (C++ [class.mem]p2), such as default + /// arguments. This action should enter the scope of the given + /// Method declaration as if we had just parsed the qualified method + /// name. However, it should not bring the parameters into scope; + /// that will be performed by ActOnDelayedCXXMethodParameter. void ActOnStartDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnDelayedCXXMethodParameter(Scope *S, Decl *Param); void ActOnFinishDelayedMemberDeclarations(Scope *S, Decl *Record); + + /// ActOnFinishDelayedCXXMethodDeclaration - We have finished + /// processing the delayed method declaration for Method. The method + /// declaration is now considered finished. There may be a separate + /// ActOnStartOfFunctionDef action later (not necessarily + /// immediately!) for this method, if it was also defined inside the + /// class body. void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnFinishDelayedMemberInitializers(Decl *Record); @@ -4513,21 +5511,79 @@ class Sema final : public SemaBase { Decl *BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, Expr *AssertMessageExpr, SourceLocation RParenLoc, bool Failed); + + /// Try to print more useful information about a failed static_assert + /// with expression \E void DiagnoseStaticAssertDetails(const Expr *E); + /// Handle a friend type declaration. This works in tandem with + /// ActOnTag. + /// + /// Notes on friend class templates: + /// + /// We generally treat friend class declarations as if they were + /// declaring a class. So, for example, the elaborated type specifier + /// in a friend declaration is required to obey the restrictions of a + /// class-head (i.e. no typedefs in the scope chain), template + /// parameters are required to match up with simple template-ids, &c. + /// However, unlike when declaring a template specialization, it's + /// okay to refer to a template specialization without an empty + /// template parameter declaration, e.g. + /// friend class A::B; + /// We permit this as a special case; if there are any template + /// parameters present at all, require proper matching, i.e. + /// template <> template \ friend class A::B; Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, MultiTemplateParamsArg TemplateParams); NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D, MultiTemplateParamsArg TemplateParams); + /// CheckConstructorDeclarator - Called by ActOnDeclarator to check + /// the well-formedness of the constructor declarator @p D with type @p + /// R. If there are any errors in the declarator, this routine will + /// emit diagnostics and set the invalid bit to true. In any case, the type + /// will be updated to reflect a well-formed type for the constructor and + /// returned. QualType CheckConstructorDeclarator(Declarator &D, QualType R, StorageClass &SC); + + /// CheckConstructor - Checks a fully-formed constructor for + /// well-formedness, issuing any diagnostics required. Returns true if + /// the constructor declarator is invalid. void CheckConstructor(CXXConstructorDecl *Constructor); + + /// CheckDestructorDeclarator - Called by ActOnDeclarator to check + /// the well-formednes of the destructor declarator @p D with type @p + /// R. If there are any errors in the declarator, this routine will + /// emit diagnostics and set the declarator to invalid. Even if this happens, + /// will be updated to reflect a well-formed type for the destructor and + /// returned. QualType CheckDestructorDeclarator(Declarator &D, QualType R, StorageClass &SC); + + /// CheckDestructor - Checks a fully-formed destructor definition for + /// well-formedness, issuing any diagnostics required. Returns true + /// on error. bool CheckDestructor(CXXDestructorDecl *Destructor); + + /// CheckConversionDeclarator - Called by ActOnDeclarator to check the + /// well-formednes of the conversion function declarator @p D with + /// type @p R. If there are any errors in the declarator, this routine + /// will emit diagnostics and return true. Otherwise, it will return + /// false. Either way, the type @p R will be updated to reflect a + /// well-formed type for the conversion operator. void CheckConversionDeclarator(Declarator &D, QualType &R, StorageClass &SC); + + /// ActOnConversionDeclarator - Called by ActOnDeclarator to complete + /// the declaration of the given C++ conversion function. This routine + /// is responsible for recording the conversion function in the C++ + /// class, if possible. Decl *ActOnConversionDeclarator(CXXConversionDecl *Conversion); + + /// Check the validity of a declarator that we parsed for a deduction-guide. + /// These aren't actually declarators in the grammar, so we need to check that + /// the user didn't specify any pieces that are not part of the + /// deduction-guide grammar. Return true on invalid deduction-guide. bool CheckDeductionGuideDeclarator(Declarator &D, QualType &R, StorageClass &SC); @@ -4574,25 +5630,44 @@ class Sema final : public SemaBase { // C++ Derived Classes // - /// ActOnBaseSpecifier - Parsed a base specifier + /// Check the validity of a C++ base class specifier. + /// + /// \returns a new CXXBaseSpecifier if well-formed, emits diagnostics + /// and returns NULL otherwise. CXXBaseSpecifier *CheckBaseSpecifier(CXXRecordDecl *Class, SourceRange SpecifierRange, bool Virtual, AccessSpecifier Access, TypeSourceInfo *TInfo, SourceLocation EllipsisLoc); + /// ActOnBaseSpecifier - Parsed a base specifier. A base specifier is + /// one entry in the base class list of a class specifier, for + /// example: + /// class foo : public bar, virtual private baz { + /// 'public bar' and 'virtual private baz' are each base-specifiers. BaseResult ActOnBaseSpecifier(Decl *classdecl, SourceRange SpecifierRange, const ParsedAttributesView &Attrs, bool Virtual, AccessSpecifier Access, ParsedType basetype, SourceLocation BaseLoc, SourceLocation EllipsisLoc); + /// Performs the actual work of attaching the given base class + /// specifiers to a C++ class. bool AttachBaseSpecifiers(CXXRecordDecl *Class, MutableArrayRef Bases); + + /// ActOnBaseSpecifiers - Attach the given base specifiers to the + /// class, after checking whether there are any duplicate base + /// classes. void ActOnBaseSpecifiers(Decl *ClassDecl, MutableArrayRef Bases); + /// Determine whether the type \p Derived is a C++ class that is + /// derived from the type \p Base. bool IsDerivedFrom(SourceLocation Loc, QualType Derived, QualType Base); + + /// Determine whether the type \p Derived is a C++ class that is + /// derived from the type \p Base. bool IsDerivedFrom(SourceLocation Loc, QualType Derived, QualType Base, CXXBasePaths &Paths); @@ -4603,6 +5678,19 @@ class Sema final : public SemaBase { SourceLocation Loc, SourceRange Range, CXXCastPath *BasePath = nullptr, bool IgnoreAccess = false); + + /// CheckDerivedToBaseConversion - Check whether the Derived-to-Base + /// conversion (where Derived and Base are class types) is + /// well-formed, meaning that the conversion is unambiguous (and + /// that all of the base classes are accessible). Returns true + /// and emits a diagnostic if the code is ill-formed, returns false + /// otherwise. Loc is the location where this routine should point to + /// if there is an error, and Range is the source range to highlight + /// if there is an error. + /// + /// If either InaccessibleBaseID or AmbiguousBaseConvID are 0, then the + /// diagnostic for the respective type of error will be suppressed, but the + /// check for ill-formed code will still be performed. bool CheckDerivedToBaseConversion(QualType Derived, QualType Base, unsigned InaccessibleBaseID, unsigned AmbiguousBaseConvID, @@ -4610,9 +5698,21 @@ class Sema final : public SemaBase { DeclarationName Name, CXXCastPath *BasePath, bool IgnoreAccess = false); + /// Builds a string representing ambiguous paths from a + /// specific derived class to different subobjects of the same base + /// class. + /// + /// This function builds a string that can be used in error messages + /// to show the different paths that one can take through the + /// inheritance hierarchy to go from the derived class to different + /// subobjects of a base class. The result looks something like this: + /// @code + /// struct D -> struct B -> struct A + /// struct D -> struct C -> struct A + /// @endcode std::string getAmbiguousPathsDisplayString(CXXBasePaths &Paths); - bool CheckOverridingFunctionAttributes(const CXXMethodDecl *New, + bool CheckOverridingFunctionAttributes(CXXMethodDecl *New, const CXXMethodDecl *Old); /// CheckOverridingFunctionReturnType - Checks whether the return types are @@ -4624,6 +5724,11 @@ class Sema final : public SemaBase { bool CheckExplicitObjectOverride(CXXMethodDecl *New, const CXXMethodDecl *Old); + /// Mark the given method pure. + /// + /// \param Method the method to be marked pure. + /// + /// \param InitRange the source range that covers the "0" initializer. bool CheckPureMethod(CXXMethodDecl *Method, SourceRange InitRange); /// CheckOverrideControl - Check C++11 override control semantics. @@ -4633,8 +5738,8 @@ class Sema final : public SemaBase { /// not used in the declaration of an overriding method. void DiagnoseAbsenceOfOverrideControl(NamedDecl *D, bool Inconsistent); - /// CheckForFunctionMarkedFinal - Checks whether a virtual member function - /// overrides a virtual member function marked 'final', according to + /// CheckIfOverriddenFunctionIsMarkedFinal - Checks whether a virtual member + /// function overrides a virtual member function marked 'final', according to /// C++11 [class.virtual]p4. bool CheckIfOverriddenFunctionIsMarkedFinal(const CXXMethodDecl *New, const CXXMethodDecl *Old); @@ -4668,8 +5773,14 @@ class Sema final : public SemaBase { // C++ Overloaded Operators [C++ 13.5] // + /// CheckOverloadedOperatorDeclaration - Check whether the declaration + /// of this overloaded operator is well-formed. If so, returns false; + /// otherwise, emits appropriate diagnostics and returns true. bool CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl); + /// CheckLiteralOperatorDeclaration - Check whether the declaration + /// of this literal operator function is well-formed. If so, returns + /// false; otherwise, emits appropriate diagnostics and returns true. bool CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl); /// ActOnExplicitBoolSpecifier - Build an ExplicitSpecifier from an expression @@ -4680,6 +5791,9 @@ class Sema final : public SemaBase { /// Returns true if the explicit specifier is now resolved. bool tryResolveExplicitSpecifier(ExplicitSpecifier &ExplicitSpec); + /// ActOnCXXConditionDeclarationExpr - Parsed a condition declaration of a + /// C++ if/switch/while/for statement. + /// e.g: "if (int x = f()) {...}" DeclResult ActOnCXXConditionDeclaration(Scope *S, Declarator &D); // Emitting members of dllexported classes is delayed until the class @@ -4687,26 +5801,49 @@ class Sema final : public SemaBase { SmallVector DelayedDllExportClasses; SmallVector DelayedDllExportMemberFunctions; + /// Merge the exception specifications of two variable declarations. + /// + /// This is called when there's a redeclaration of a VarDecl. The function + /// checks if the redeclaration might have an exception specification and + /// validates compatibility and merges the specs if necessary. void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old); + + /// MergeCXXFunctionDecl - Merge two declarations of the same C++ + /// function, once we already know that they have the same + /// type. Subroutine of MergeFunctionDecl. Returns true if there was an + /// error, false otherwise. bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Scope *S); /// Helpers for dealing with blocks and functions. void CheckCXXDefaultArguments(FunctionDecl *FD); + + /// CheckExtraCXXDefaultArguments - Check for any extra default + /// arguments in the declarator, which is not a function declaration + /// or definition and therefore is not permitted to have default + /// arguments. This routine should be invoked for every declarator + /// that is not a function declaration or definition. void CheckExtraCXXDefaultArguments(Declarator &D); CXXSpecialMemberKind getSpecialMember(const CXXMethodDecl *MD) { return getDefaultedFunctionKind(MD).asSpecialMember(); } + /// Perform semantic analysis for the variable declaration that + /// occurs within a C++ catch clause, returning the newly-created + /// variable. VarDecl *BuildExceptionDeclaration(Scope *S, TypeSourceInfo *TInfo, SourceLocation StartLoc, SourceLocation IdLoc, const IdentifierInfo *Id); + /// ActOnExceptionDeclarator - Parsed the exception-declarator in a C++ catch + /// handler. Decl *ActOnExceptionDeclarator(Scope *S, Declarator &D); void DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock); + /// Handle a friend tag declaration where the scope specifier was + /// templated. DeclResult ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc, CXXScopeSpec &SS, IdentifierInfo *Name, @@ -4721,6 +5858,8 @@ class Sema final : public SemaBase { AccessSpecifier AS, const ParsedAttr &MSPropertyAttr); + /// Diagnose why the specified class does not have a trivial special member of + /// the given kind. void DiagnoseNontrivial(const CXXRecordDecl *Record, CXXSpecialMemberKind CSM); @@ -4732,6 +5871,9 @@ class Sema final : public SemaBase { TAH_ConsiderTrivialABI }; + /// Determine whether a defaulted or deleted special member function is + /// trivial, as specified in C++11 [class.ctor]p5, C++11 [class.copy]p12, + /// C++11 [class.copy]p25, and C++11 [class.dtor]p5. bool SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMemberKind CSM, TrivialABIHandling TAH = TAH_IgnoreTrivialABI, bool Diagnose = false); @@ -4784,6 +5926,13 @@ class Sema final : public SemaBase { } }; + /// Determine the kind of defaulting that would be done for a given function. + /// + /// If the function is both a default constructor and a copy / move + /// constructor (due to having a default argument for the first parameter), + /// this picks CXXSpecialMemberKind::DefaultConstructor. + /// + /// FIXME: Check that case is properly handled by all callers. DefaultedFunctionKind getDefaultedFunctionKind(const FunctionDecl *FD); /// Handle a C++11 empty-declaration and attribute-declaration. @@ -4798,20 +5947,43 @@ class Sema final : public SemaBase { CheckValid }; + // Check whether a function declaration satisfies the requirements of a + // constexpr function definition or a constexpr constructor definition. If so, + // return true. If not, produce appropriate diagnostics (unless asked not to + // by Kind) and return false. + // + // This implements C++11 [dcl.constexpr]p3,4, as amended by DR1360. bool CheckConstexprFunctionDefinition(const FunctionDecl *FD, CheckConstexprKind Kind); + /// Diagnose methods which overload virtual methods in a base class + /// without overriding any. void DiagnoseHiddenVirtualMethods(CXXMethodDecl *MD); + + /// Check if a method overloads virtual methods in a base class without + /// overriding any. void FindHiddenVirtualMethods(CXXMethodDecl *MD, SmallVectorImpl &OverloadedMethods); void NoteHiddenVirtualMethods(CXXMethodDecl *MD, SmallVectorImpl &OverloadedMethods); + + /// ActOnParamDefaultArgument - Check whether the default argument + /// provided for a function parameter is well-formed. If so, attach it + /// to the parameter declaration. void ActOnParamDefaultArgument(Decl *param, SourceLocation EqualLoc, Expr *defarg); + + /// ActOnParamUnparsedDefaultArgument - We've seen a default + /// argument for a function parameter, but we can't parse it yet + /// because we're inside a class definition. Note that this default + /// argument will be parsed later. void ActOnParamUnparsedDefaultArgument(Decl *param, SourceLocation EqualLoc, SourceLocation ArgLoc); + + /// ActOnParamDefaultArgumentError - Parsing or semantic analysis of + /// the default argument for the parameter param failed. void ActOnParamDefaultArgumentError(Decl *param, SourceLocation EqualLoc, Expr *DefaultArg); ExprResult ConvertParamDefaultArgument(ParmVarDecl *Param, Expr *DefaultArg, @@ -4972,9 +6144,26 @@ class Sema final : public SemaBase { const FunctionProtoType *FPT); void UpdateExceptionSpec(FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI); + + /// CheckSpecifiedExceptionType - Check if the given type is valid in an + /// exception specification. Incomplete types, or pointers to incomplete types + /// other than void are not allowed. + /// + /// \param[in,out] T The exception type. This will be decayed to a pointer + /// type + /// when the input is an array or a function type. bool CheckSpecifiedExceptionType(QualType &T, SourceRange Range); + + /// CheckDistantExceptionSpec - Check if the given type is a pointer or + /// pointer to member to a function with an exception specification. This + /// means that it is invalid to add another level of indirection. bool CheckDistantExceptionSpec(QualType T); bool CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New); + + /// CheckEquivalentExceptionSpec - Check if the two types have equivalent + /// exception specifications. Exception specifications are equivalent if + /// they allow exactly the same set of exception types. It does not matter how + /// that is achieved. See C++ [except.spec]p2. bool CheckEquivalentExceptionSpec(const FunctionProtoType *Old, SourceLocation OldLoc, const FunctionProtoType *New, @@ -4986,12 +6175,22 @@ class Sema final : public SemaBase { const FunctionProtoType *New, SourceLocation NewLoc); bool handlerCanCatch(QualType HandlerType, QualType ExceptionType); + + /// CheckExceptionSpecSubset - Check whether the second function type's + /// exception specification is a subset (or equivalent) of the first function + /// type. This is used by override and pointer assignment checks. bool CheckExceptionSpecSubset( const PartialDiagnostic &DiagID, const PartialDiagnostic &NestedDiagID, const PartialDiagnostic &NoteID, const PartialDiagnostic &NoThrowDiagID, const FunctionProtoType *Superset, bool SkipSupersetFirstParameter, SourceLocation SuperLoc, const FunctionProtoType *Subset, bool SkipSubsetFirstParameter, SourceLocation SubLoc); + + /// CheckParamExceptionSpec - Check if the parameter and return types of the + /// two functions have equivalent exception specs. This is part of the + /// assignment and override compatibility check. We do not check the + /// parameters of parameter function pointers recursively, as no sane + /// programmer would even be able to write such a function type. bool CheckParamExceptionSpec( const PartialDiagnostic &NestedDiagID, const PartialDiagnostic &NoteID, const FunctionProtoType *Target, bool SkipTargetFirstParameter, @@ -5286,6 +6485,8 @@ class Sema final : public SemaBase { AA_Passing_CFAudited }; + /// Determine whether the use of this declaration is valid, without + /// emitting diagnostics. bool CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid); // A version of DiagnoseUseOfDecl that should be used if overload resolution // has been used to find this declaration, which means we don't have to bother @@ -5297,14 +6498,31 @@ class Sema final : public SemaBase { /*SkipTrailingRequiresClause=*/true); } + /// Determine whether the use of this declaration is valid, and + /// emit any corresponding diagnostics. + /// + /// This routine diagnoses various problems with referencing + /// declarations that can occur when using a declaration. For example, + /// it might warn if a deprecated or unavailable declaration is being + /// used, or produce an error (and return true) if a C++0x deleted + /// function is being used. + /// + /// \returns true if there was an error (this declaration cannot be + /// referenced), false otherwise. bool DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass = nullptr, bool ObjCPropertyAccess = false, bool AvoidPartialAvailabilityChecks = false, ObjCInterfaceDecl *ClassReciever = nullptr, bool SkipTrailingRequiresClause = false); + + /// Emit a note explaining that this function is deleted. void NoteDeletedFunction(FunctionDecl *FD); + /// DiagnoseSentinelCalls - This routine checks whether a call or + /// message-send is to a declaration with the sentinel attribute, and + /// if so, it checks that the requirements of the sentinel are + /// satisfied. void DiagnoseSentinelCalls(const NamedDecl *D, SourceLocation Loc, ArrayRef Args); @@ -5325,6 +6543,10 @@ class Sema final : public SemaBase { TypeSourceInfo *TransformToPotentiallyEvaluated(TypeSourceInfo *TInfo); ExprResult HandleExprEvaluationContextForTypeof(Expr *E); + /// Check whether E, which is either a discarded-value expression or an + /// unevaluated operand, is a simple-assignment to a volatlie-qualified + /// lvalue, and if so, remove it from the list of volatile-qualified + /// assignments that we are going to warn are deprecated. void CheckUnusedVolatileAssignment(Expr *E); ExprResult ActOnConstantExpression(ExprResult Res); @@ -5342,11 +6564,27 @@ class Sema final : public SemaBase { // because the name denotes a virtual function and was written without an // explicit nested-name-specifier). void MarkAnyDeclReferenced(SourceLocation Loc, Decl *D, bool MightBeOdrUse); + + /// Mark a function referenced, and check whether it is odr-used + /// (C++ [basic.def.odr]p2, C99 6.9p3) void MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, bool MightBeOdrUse = true); + + /// Mark a variable referenced, and check whether it is odr-used + /// (C++ [basic.def.odr]p2, C99 6.9p3). Note that this should not be + /// used directly for normal expressions referring to VarDecl. void MarkVariableReferenced(SourceLocation Loc, VarDecl *Var); + + /// Perform reference-marking and odr-use handling for a DeclRefExpr. + /// + /// Note, this may change the dependence of the DeclRefExpr, and so needs to + /// be handled with care if the DeclRefExpr is not newly-created. void MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base = nullptr); + + /// Perform reference-marking and odr-use handling for a MemberExpr. void MarkMemberReferenced(MemberExpr *E); + + /// Perform reference-marking and odr-use handling for a FunctionParmPackExpr. void MarkFunctionParmPackReferenced(FunctionParmPackExpr *E); void MarkCaptureUsedInEnclosingContext(ValueDecl *Capture, SourceLocation Loc, unsigned CapturingScopeIndex); @@ -5415,6 +6653,13 @@ class Sema final : public SemaBase { /// referenced. Used when template instantiation instantiates a non-dependent /// type -- entities referenced by the type are now referenced. void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); + + /// Mark any declarations that appear within this expression or any + /// potentially-evaluated subexpressions as "referenced". + /// + /// \param SkipLocalVariables If true, don't mark local variables as + /// 'referenced'. + /// \param StopAt Subexpressions that we shouldn't recurse into. void MarkDeclarationsReferencedInExpr( Expr *E, bool SkipLocalVariables = false, ArrayRef StopAt = std::nullopt); @@ -5457,13 +6702,33 @@ class Sema final : public SemaBase { bool IsInlineAsmIdentifier = false, Token *KeywordReplacement = nullptr); + /// Decomposes the given name into a DeclarationNameInfo, its location, and + /// possibly a list of template arguments. + /// + /// If this produces template arguments, it is permitted to call + /// DecomposeTemplateName. + /// + /// This actually loses a lot of source location information for + /// non-standard name kinds; we should consider preserving that in + /// some way. void DecomposeUnqualifiedId(const UnqualifiedId &Id, TemplateArgumentListInfo &Buffer, DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *&TemplateArgs); + /// Diagnose a lookup that found results in an enclosing class during error + /// recovery. This usually indicates that the results were found in a + /// dependent base class that could not be searched as part of a template + /// definition. Always issues a diagnostic (though this may be only a warning + /// in MS compatibility mode). + /// + /// Return \c true if the error is unrecoverable, or \c false if the caller + /// should attempt to recover using these lookup results. bool DiagnoseDependentMemberLookup(const LookupResult &R); + /// Diagnose an empty lookup. + /// + /// \return false if new lookup candidates were found bool DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, CorrectionCandidateCallback &CCC, @@ -5486,6 +6751,9 @@ class Sema final : public SemaBase { NamedDecl *FoundD = nullptr, SourceLocation TemplateKWLoc = SourceLocation(), const TemplateArgumentListInfo *TemplateArgs = nullptr); + + /// BuildDeclRefExpr - Build an expression that references a + /// declaration that does not require a closure capture. DeclRefExpr * BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, const DeclarationNameInfo &NameInfo, @@ -5496,6 +6764,10 @@ class Sema final : public SemaBase { bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R, bool HasTrailingLParen); + /// BuildQualifiedDeclarationNameExpr - Build a C++ qualified + /// declaration name, generally during template instantiation. + /// There's a large number of things which don't need to be done along + /// this path. ExprResult BuildQualifiedDeclarationNameExpr( CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI = nullptr); @@ -5503,6 +6775,8 @@ class Sema final : public SemaBase { ExprResult BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R, bool NeedsADL, bool AcceptInvalidDecl = false); + + /// Complete semantic analysis for a reference to the given declaration. ExprResult BuildDeclarationNameExpr( const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, NamedDecl *D, NamedDecl *FoundD = nullptr, @@ -5528,7 +6802,10 @@ class Sema final : public SemaBase { MultiExprArg Val); /// ActOnStringLiteral - The specified tokens were lexed as pasted string - /// fragments (e.g. "foo" "bar" L"baz"). + /// fragments (e.g. "foo" "bar" L"baz"). The result string has to handle + /// string concatenation ([C99 5.1.1.2, translation phase #6]), so it may come + /// from multiple tokens. However, the common case is that StringToks points + /// to one string. ExprResult ActOnStringLiteral(ArrayRef StringToks, Scope *UDLScope = nullptr); @@ -5563,36 +6840,88 @@ class Sema final : public SemaBase { Expr *InputExpr, bool IsAfterAmp = false); ExprResult BuildUnaryOp(Scope *S, SourceLocation OpLoc, UnaryOperatorKind Opc, Expr *Input, bool IsAfterAmp = false); + + /// Unary Operators. 'Tok' is the token for the operator. ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Op, Expr *Input, bool IsAfterAmp = false); + /// Determine whether the given expression is a qualified member + /// access expression, of a form that could be turned into a pointer to member + /// with the address-of operator. bool isQualifiedMemberAccess(Expr *E); bool CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc, const Expr *Op, const CXXMethodDecl *MD); + /// CheckAddressOfOperand - The operand of & must be either a function + /// designator or an lvalue designating an object. If it is an lvalue, the + /// object cannot be declared with storage class register or be a bit field. + /// Note: The usual conversions are *not* applied to the operand of the & + /// operator (C99 6.3.2.1p[2-4]), and its result is never an lvalue. + /// In C++, the operand might be an overloaded function name, in which case + /// we allow the '&' but retain the overloaded-function type. QualType CheckAddressOfOperand(ExprResult &Operand, SourceLocation OpLoc); + /// ActOnAlignasTypeArgument - Handle @c alignas(type-id) and @c + /// _Alignas(type-name) . + /// [dcl.align] An alignment-specifier of the form + /// alignas(type-id) has the same effect as alignas(alignof(type-id)). + /// + /// [N1570 6.7.5] _Alignas(type-name) is equivalent to + /// _Alignas(_Alignof(type-name)). bool ActOnAlignasTypeArgument(StringRef KWName, ParsedType Ty, SourceLocation OpLoc, SourceRange R); bool CheckAlignasTypeArgument(StringRef KWName, TypeSourceInfo *TInfo, SourceLocation OpLoc, SourceRange R); + /// Build a sizeof or alignof expression given a type operand. ExprResult CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo, SourceLocation OpLoc, UnaryExprOrTypeTrait ExprKind, SourceRange R); + + /// Build a sizeof or alignof expression given an expression + /// operand. ExprResult CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc, UnaryExprOrTypeTrait ExprKind); + + /// ActOnUnaryExprOrTypeTraitExpr - Handle @c sizeof(type) and @c sizeof @c + /// expr and the same for @c alignof and @c __alignof + /// Note that the ArgRange is invalid if isType is false. ExprResult ActOnUnaryExprOrTypeTraitExpr(SourceLocation OpLoc, UnaryExprOrTypeTrait ExprKind, bool IsType, void *TyOrEx, SourceRange ArgRange); + /// Check for operands with placeholder types and complain if found. + /// Returns ExprError() if there was an error and no recovery was possible. ExprResult CheckPlaceholderExpr(Expr *E); bool CheckVecStepExpr(Expr *E); + /// Check the constraints on expression operands to unary type expression + /// and type traits. + /// + /// Completes any types necessary and validates the constraints on the operand + /// expression. The logic mostly mirrors the type-based overload, but may + /// modify the expression as it completes the type for that expression through + /// template instantiation, etc. bool CheckUnaryExprOrTypeTraitOperand(Expr *E, UnaryExprOrTypeTrait ExprKind); + + /// Check the constraints on operands to unary expression and type + /// traits. + /// + /// This will complete any types necessary, and validate the various + /// constraints on those operands. + /// + /// The UsualUnaryConversions() function is *not* called by this routine. + /// C99 6.3.2.1p[2-4] all state: + /// Except when it is the operand of the sizeof operator ... + /// + /// C++ [expr.sizeof]p4 + /// The lvalue-to-rvalue, array-to-pointer, and function-to-pointer + /// standard conversions are not applied to the operand of sizeof. + /// + /// This policy is followed for all of the unary trait expressions. bool CheckUnaryExprOrTypeTraitOperand(QualType ExprType, SourceLocation OpLoc, SourceRange ExprRange, UnaryExprOrTypeTrait ExprKind, @@ -5611,10 +6940,26 @@ class Sema final : public SemaBase { Expr *ColumnIdx, SourceLocation RBLoc); + /// ConvertArgumentsForCall - Converts the arguments specified in + /// Args/NumArgs to the parameter types of the function FDecl with + /// function prototype Proto. Call is the call expression itself, and + /// Fn is the function expression. For a C++ member function, this + /// routine does not attempt to convert the object argument. Returns + /// true if the call is ill-formed. bool ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, FunctionDecl *FDecl, const FunctionProtoType *Proto, ArrayRef Args, SourceLocation RParenLoc, bool ExecConfig = false); + + /// CheckStaticArrayArgument - If the given argument corresponds to a static + /// array parameter, check that it is non-null, and that if it is formed by + /// array-to-pointer decay, the underlying array is sufficiently large. + /// + /// C99 6.7.5.3p7: If the keyword static also appears within the [ and ] of + /// the array type derivation, then for each call to the function, the value + /// of the corresponding actual argument shall provide access to the first + /// element of an array with at least as many elements as specified by the + /// size expression. void CheckStaticArrayArgument(SourceLocation CallLoc, ParmVarDecl *Param, const Expr *ArgExpr); @@ -5624,16 +6969,29 @@ class Sema final : public SemaBase { ExprResult ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig = nullptr); + + /// BuildCallExpr - Handle a call to Fn with the specified array of arguments. + /// This provides the location of the left/right parens and a list of comma + /// locations. ExprResult BuildCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig = nullptr, bool IsExecConfig = false, bool AllowRecovery = false); + + /// BuildBuiltinCallExpr - Create a call to a builtin function specified by Id + // with the specified CallArgs Expr *BuildBuiltinCallExpr(SourceLocation Loc, Builtin::ID Id, MultiExprArg CallArgs); using ADLCallKind = CallExpr::ADLCallKind; + /// BuildResolvedCallExpr - Build a call to a resolved expression, + /// i.e. an expression not of \p OverloadTy. The expression should + /// unary-convert to an expression of function-pointer or + /// block-pointer type. + /// + /// \param NDecl the declaration being called, if available ExprResult BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, SourceLocation LParenLoc, ArrayRef Arg, SourceLocation RParenLoc, @@ -5644,6 +7002,8 @@ class Sema final : public SemaBase { ParsedType &Ty, SourceLocation RParenLoc, Expr *CastExpr); + /// Prepares for a scalar cast, performing all the necessary stages + /// except the final cast and returning the kind required. CastKind PrepareScalarCast(ExprResult &src, QualType destType); /// Build an altivec or OpenCL literal. @@ -5651,6 +7011,8 @@ class Sema final : public SemaBase { SourceLocation RParenLoc, Expr *E, TypeSourceInfo *TInfo); + /// This is not an AltiVec-style cast or or C++ direct-initialization, so turn + /// the ParenListExpr into a sequence of comma binary operators. ExprResult MaybeConvertParenListExprToParenExpr(Scope *S, Expr *ME); ExprResult ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty, @@ -5667,15 +7029,23 @@ class Sema final : public SemaBase { ExprResult BuildInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, SourceLocation RBraceLoc); + /// Binary Operators. 'Tok' is the token for the operator. ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, tok::TokenKind Kind, Expr *LHSExpr, Expr *RHSExpr); ExprResult BuildBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr); + + /// CreateBuiltinBinOp - Creates a new built-in binary operation with + /// operator @p Opc at location @c TokLoc. This routine only supports + /// built-in operations; ActOnBinOp handles overloaded operators. ExprResult CreateBuiltinBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr); void LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, UnresolvedSetImpl &Functions); + /// Look for instances where it is likely the comma operator is confused with + /// another operator. There is an explicit list of acceptable expressions for + /// the left hand side of the comma operator, otherwise emit a warning. void DiagnoseCommaOperator(const Expr *LHS, SourceLocation Loc); /// ActOnConditionalOp - Parse a ?: operation. Note that 'LHS' may be null @@ -5771,17 +7141,26 @@ class Sema final : public SemaBase { //===---------------------------- Clang Extensions ----------------------===// - /// __builtin_convertvector(...) + /// ActOnConvertVectorExpr - create a new convert-vector expression from the + /// provided arguments. + /// + /// __builtin_convertvector( value, dst type ) + /// ExprResult ActOnConvertVectorExpr(Expr *E, ParsedType ParsedDestTy, SourceLocation BuiltinLoc, SourceLocation RParenLoc); //===---------------------------- OpenCL Features -----------------------===// - /// __builtin_astype(...) + /// Parse a __builtin_astype expression. + /// + /// __builtin_astype( value, dst type ) + /// ExprResult ActOnAsTypeExpr(Expr *E, ParsedType ParsedDestTy, SourceLocation BuiltinLoc, SourceLocation RParenLoc); + + /// Create a new AsTypeExpr node (bitcast) from the arguments. ExprResult BuildAsTypeExpr(Expr *E, QualType DestTy, SourceLocation BuiltinLoc, SourceLocation RParenLoc); @@ -5791,6 +7170,26 @@ class Sema final : public SemaBase { ArrayRef SubExprs, QualType T = QualType()); + /// Cast a base object to a member's actual type. + /// + /// There are two relevant checks: + /// + /// C++ [class.access.base]p7: + /// + /// If a class member access operator [...] is used to access a non-static + /// data member or non-static member function, the reference is ill-formed + /// if the left operand [...] cannot be implicitly converted to a pointer to + /// the naming class of the right operand. + /// + /// C++ [expr.ref]p7: + /// + /// If E2 is a non-static data member or a non-static member function, the + /// program is ill-formed if the class of which E2 is directly a member is + /// an ambiguous base (11.8) of the naming class (11.9.3) of E2. + /// + /// Note that the latter check does not consider access; the access of the + /// "real" base class is checked as appropriate when checking the access of + /// the member name. ExprResult PerformObjectMemberConversion(Expr *From, NestedNameSpecifier *Qualifier, NamedDecl *FoundDecl, @@ -5824,6 +7223,7 @@ class Sema final : public SemaBase { void MarkExpressionAsImmediateEscalating(Expr *E); + // Check that the SME attributes for PSTATE.ZA and PSTATE.SM are compatible. bool IsInvalidSMECallConversion(QualType FromType, QualType ToType); /// Abstract base class used for diagnosing integer constant @@ -5963,6 +7363,9 @@ class Sema final : public SemaBase { ExprResult &Cond, ExprResult &LHS, ExprResult &RHS, ExprValueKind &VK, ExprObjectKind &OK, SourceLocation QuestionLoc); + /// Emit a specialized diagnostic when one expression is a null pointer + /// constant and the other is not a pointer. Returns true if a diagnostic is + /// emitted. bool DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr, SourceLocation QuestionLoc); @@ -5971,8 +7374,19 @@ class Sema final : public SemaBase { SourceLocation Loc, bool IsCompAssign, bool AllowBothBool, bool AllowBoolConversion, bool AllowBoolOperation, bool ReportInvalid); + + /// Return a signed ext_vector_type that is of identical size and number of + /// elements. For floating point vectors, return an integer type of identical + /// size and number of elements. In the non ext_vector_type case, search from + /// the largest type to the smallest type to avoid cases where long long == + /// long, where long gets picked over long long. QualType GetSignedVectorType(QualType V); QualType GetSignedSizelessVectorType(QualType V); + + /// CheckVectorCompareOperands - vector comparisons are a clang extension that + /// operates on extended vector types. Instead of producing an IntTy result, + /// like a scalar comparison, a vector comparison produces a vector of integer + /// types. QualType CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc); @@ -6008,17 +7422,40 @@ class Sema final : public SemaBase { QualType CheckMatrixMultiplyOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign); + /// Are the two types SVE-bitcast-compatible types? I.e. is bitcasting from + /// the first SVE type (e.g. an SVE VLAT) to the second type (e.g. an SVE + /// VLST) allowed? + /// + /// This will also return false if the two given types do not make sense from + /// the perspective of SVE bitcasts. bool isValidSveBitcast(QualType srcType, QualType destType); + /// Are the two types matrix types and do they have the same dimensions i.e. + /// do they have the same number of rows and the same number of columns? bool areMatrixTypesOfTheSameDimension(QualType srcTy, QualType destTy); bool areVectorTypesSameSize(QualType srcType, QualType destType); + + /// Are the two types lax-compatible vector types? That is, given + /// that one of them is a vector, do they have equal storage sizes, + /// where the storage size is the number of elements times the element + /// size? + /// + /// This will also return false if either of the types is neither a + /// vector nor a real type. bool areLaxCompatibleVectorTypes(QualType srcType, QualType destType); + + /// Is this a legal conversion between two types, one of which is + /// known to be a vector type? bool isLaxVectorConversion(QualType srcType, QualType destType); + + // This returns true if at least one of the types is an altivec vector. bool anyAltivecTypes(QualType srcType, QualType destType); // type checking C++ declaration initializers (C++ [dcl.init]). + /// Check a cast of an unknown-any type. We intentionally only + /// trigger this for C-style casts. ExprResult checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, Expr *CastExpr, CastKind &CastKind, ExprValueKind &VK, CXXCastPath &Path); @@ -6104,7 +7541,9 @@ class Sema final : public SemaBase { VAK_Invalid }; - // Determines which VarArgKind fits an expression. + /// Determine the degree of POD-ness for an expression. + /// Incomplete types are considered POD, since this check can be performed + /// when we're in an unevaluated context. VarArgKind isValidVarArgType(const QualType &Ty); /// Check to see if the given expression is a valid argument to a variadic @@ -6274,6 +7713,10 @@ class Sema final : public SemaBase { /// type checking binary operators (subroutines of CreateBuiltinBinOp). QualType InvalidOperands(SourceLocation Loc, ExprResult &LHS, ExprResult &RHS); + + /// Diagnose cases where a scalar was implicitly converted to a vector and + /// diagnose the underlying types. Otherwise, diagnose the error + /// as invalid vector logical operands for non-C++ cases. QualType InvalidLogicalVectorOperands(SourceLocation Loc, ExprResult &LHS, ExprResult &RHS); @@ -6492,6 +7935,7 @@ class Sema final : public SemaBase { llvm::SmallVector, 1> ImplicitlyRetainedSelfLocs; + /// Do an explicit extend of the given block pointer if we're in ARC. void maybeExtendBlockObject(ExprResult &E); private: @@ -6542,6 +7986,10 @@ class Sema final : public SemaBase { /// used in initializer of the field. llvm::MapVector DeleteExprs; + /// Handle the result of the special case name lookup for inheriting + /// constructor declarations. 'NS::X::X' and 'NS::X<...>::X' are treated as + /// constructor names in member using declarations, even if 'X' is not the + /// name of the corresponding type. ParsedType getInheritingConstructorName(CXXScopeSpec &SS, SourceLocation NameLoc, const IdentifierInfo &Name); @@ -6556,8 +8004,11 @@ class Sema final : public SemaBase { ParsedType getDestructorTypeForDecltype(const DeclSpec &DS, ParsedType ObjectType); + /// Build a C++ typeid expression with a type operand. ExprResult BuildCXXTypeId(QualType TypeInfoType, SourceLocation TypeidLoc, TypeSourceInfo *Operand, SourceLocation RParenLoc); + + /// Build a C++ typeid expression with an expression operand. ExprResult BuildCXXTypeId(QualType TypeInfoType, SourceLocation TypeidLoc, Expr *Operand, SourceLocation RParenLoc); @@ -6566,8 +8017,11 @@ class Sema final : public SemaBase { bool isType, void *TyOrExpr, SourceLocation RParenLoc); + /// Build a Microsoft __uuidof expression with a type operand. ExprResult BuildCXXUuidof(QualType TypeInfoType, SourceLocation TypeidLoc, TypeSourceInfo *Operand, SourceLocation RParenLoc); + + /// Build a Microsoft __uuidof expression with an expression operand. ExprResult BuildCXXUuidof(QualType TypeInfoType, SourceLocation TypeidLoc, Expr *Operand, SourceLocation RParenLoc); @@ -6649,6 +8103,8 @@ class Sema final : public SemaBase { ExprResult ActOnCXXThrow(Scope *S, SourceLocation OpLoc, Expr *expr); ExprResult BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, bool IsThrownVarInScope); + + /// CheckCXXThrowOperand - Validate the operand of a throw. bool CheckCXXThrowOperand(SourceLocation ThrowLoc, QualType ThrowTy, Expr *E); /// ActOnCXXTypeConstructExpr - Parse construction of a specified type. @@ -6667,7 +8123,22 @@ class Sema final : public SemaBase { SourceLocation RParenLoc, bool ListInitialization); - /// ActOnCXXNew - Parsed a C++ 'new' expression. + /// Parsed a C++ 'new' expression (C++ 5.3.4). + /// + /// E.g.: + /// @code new (memory) int[size][4] @endcode + /// or + /// @code ::new Foo(23, "hello") @endcode + /// + /// \param StartLoc The first location of the expression. + /// \param UseGlobal True if 'new' was prefixed with '::'. + /// \param PlacementLParen Opening paren of the placement arguments. + /// \param PlacementArgs Placement new arguments. + /// \param PlacementRParen Closing paren of the placement arguments. + /// \param TypeIdParens If the type is in parens, the source range. + /// \param D The type to be allocated, as well as array dimensions. + /// \param Initializer The initializing expression or initializer-list, or + /// null if there is none. ExprResult ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, SourceLocation PlacementLParen, MultiExprArg PlacementArgs, @@ -6690,6 +8161,8 @@ class Sema final : public SemaBase { void diagnoseUnavailableAlignedAllocation(const FunctionDecl &FD, SourceLocation Loc); + /// Checks that a type is suitable as the allocated type + /// in a new-expression. bool CheckAllocatedType(QualType AllocType, SourceLocation Loc, SourceRange R); @@ -6715,6 +8188,30 @@ class Sema final : public SemaBase { FunctionDecl *&OperatorNew, FunctionDecl *&OperatorDelete, bool Diagnose = true); + + /// DeclareGlobalNewDelete - Declare the global forms of operator new and + /// delete. These are: + /// @code + /// // C++03: + /// void* operator new(std::size_t) throw(std::bad_alloc); + /// void* operator new[](std::size_t) throw(std::bad_alloc); + /// void operator delete(void *) throw(); + /// void operator delete[](void *) throw(); + /// // C++11: + /// void* operator new(std::size_t); + /// void* operator new[](std::size_t); + /// void operator delete(void *) noexcept; + /// void operator delete[](void *) noexcept; + /// // C++1y: + /// void* operator new(std::size_t); + /// void* operator new[](std::size_t); + /// void operator delete(void *) noexcept; + /// void operator delete[](void *) noexcept; + /// void operator delete(void *, std::size_t) noexcept; + /// void operator delete[](void *, std::size_t) noexcept; + /// @endcode + /// Note that the placement and nothrow forms of new are *not* implicitly + /// declared. Their use requires including \. void DeclareGlobalNewDelete(); void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return, ArrayRef Params); @@ -6730,7 +8227,10 @@ class Sema final : public SemaBase { FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc, CXXRecordDecl *RD); - /// ActOnCXXDelete - Parsed a C++ 'delete' expression + /// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in: + /// @code ::delete ptr; @endcode + /// or + /// @code delete [] ptr; @endcode ExprResult ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, bool ArrayForm, Expr *Operand); void CheckVirtualDtorCall(CXXDestructorDecl *dtor, SourceLocation Loc, @@ -6781,6 +8281,10 @@ class Sema final : public SemaBase { bool IsTemplateArgument = false); StmtResult ActOnFinishFullStmt(Stmt *Stmt); + /// Process the expression contained within a decltype. For such expressions, + /// certain semantic checks on temporaries are delayed until this point, and + /// are omitted for the 'topmost' call in the decltype expression. If the + /// topmost call bound a temporary, strip that temporary off the expression. ExprResult ActOnDecltypeExpression(Expr *E); bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id, @@ -6792,18 +8296,35 @@ class Sema final : public SemaBase { SourceLocation StmtLoc, ConditionKind CK); + /// Check the use of the given variable as a C++ condition in an if, + /// while, do-while, or switch statement. ExprResult CheckConditionVariable(VarDecl *ConditionVar, SourceLocation StmtLoc, ConditionKind CK); /// CheckCXXBooleanCondition - Returns true if conversion to bool is invalid. ExprResult CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr = false); + /// Helper function to determine whether this is the (deprecated) C++ + /// conversion from a string literal to a pointer to non-const char or + /// non-const wchar_t (for narrow and wide string literals, + /// respectively). bool IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType); + /// PerformImplicitConversion - Perform an implicit conversion of the + /// expression From to the type ToType using the pre-computed implicit + /// conversion sequence ICS. Returns the converted + /// expression. Action is the kind of conversion we're performing, + /// used in the error message. ExprResult PerformImplicitConversion( Expr *From, QualType ToType, const ImplicitConversionSequence &ICS, AssignmentAction Action, CheckedConversionKind CCK = CheckedConversionKind::Implicit); + + /// PerformImplicitConversion - Perform an implicit conversion of the + /// expression From to the type ToType by following the standard + /// conversion sequence SCS. Returns the converted + /// expression. Flavor is the context in which we're performing this + /// conversion, for use in error messages. ExprResult PerformImplicitConversion(Expr *From, QualType ToType, const StandardConversionSequence &SCS, AssignmentAction Action, @@ -6848,10 +8369,42 @@ class Sema final : public SemaBase { ExprResult &LHS, ExprResult &RHS, SourceLocation QuestionLoc); + /// Check the operands of ?: under C++ semantics. + /// + /// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y + /// extension. In this case, LHS == Cond. (But they're not aliases.) + /// + /// This function also implements GCC's vector extension and the + /// OpenCL/ext_vector_type extension for conditionals. The vector extensions + /// permit the use of a?b:c where the type of a is that of a integer vector + /// with the same number of elements and size as the vectors of b and c. If + /// one of either b or c is a scalar it is implicitly converted to match the + /// type of the vector. Otherwise the expression is ill-formed. If both b and + /// c are scalars, then b and c are checked and converted to the type of a if + /// possible. + /// + /// The expressions are evaluated differently for GCC's and OpenCL's + /// extensions. For the GCC extension, the ?: operator is evaluated as + /// (a[0] != 0 ? b[0] : c[0], .. , a[n] != 0 ? b[n] : c[n]). + /// For the OpenCL extensions, the ?: operator is evaluated as + /// (most-significant-bit-set(a[0]) ? b[0] : c[0], .. , + /// most-significant-bit-set(a[n]) ? b[n] : c[n]). QualType CXXCheckConditionalOperands( // C++ 5.16 ExprResult &cond, ExprResult &lhs, ExprResult &rhs, ExprValueKind &VK, ExprObjectKind &OK, SourceLocation questionLoc); + /// Find a merged pointer type and convert the two expressions to it. + /// + /// This finds the composite pointer type for \p E1 and \p E2 according to + /// C++2a [expr.type]p3. It converts both expressions to this type and returns + /// it. It does not emit diagnostics (FIXME: that's not true if \p + /// ConvertArgs is \c true). + /// + /// \param Loc The location of the operator requiring these two expressions to + /// be converted to the composite pointer type. + /// + /// \param ConvertArgs If \c false, do not convert E1 and E2 to the target + /// type. QualType FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2, bool ConvertArgs = true); QualType FindCompositePointerType(SourceLocation Loc, ExprResult &E1, @@ -6999,10 +8552,15 @@ class Sema final : public SemaBase { bool isPotentialImplicitMemberAccess(const CXXScopeSpec &SS, LookupResult &R, bool IsAddressOfOperand); + /// Builds an expression which might be an implicit member expression. ExprResult BuildPossibleImplicitMemberExpr( const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R, const TemplateArgumentListInfo *TemplateArgs, const Scope *S); + /// Builds an implicit member access expression. The current context + /// is known to be an instance method, and the given unqualified lookup + /// set is known to contain only instance members, at least one of which + /// is from an appropriate type. ExprResult BuildImplicitMemberExpr(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R, @@ -7015,6 +8573,16 @@ class Sema final : public SemaBase { NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs); + /// The main callback when the parser finds something like + /// expression . [nested-name-specifier] identifier + /// expression -> [nested-name-specifier] identifier + /// where 'identifier' encompasses a fairly broad spectrum of + /// possibilities, including destructor and operator references. + /// + /// \param OpKind either tok::arrow or tok::period + /// \param ObjCImpDecl the current Objective-C \@implementation + /// decl; this is an ugly hack around the fact that Objective-C + /// \@implementations aren't properly put in the context chain ExprResult ActOnMemberAccessExpr(Scope *S, Expr *Base, SourceLocation OpLoc, tok::TokenKind OpKind, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, @@ -7029,6 +8597,18 @@ class Sema final : public SemaBase { ExprValueKind VK, ExprObjectKind OK, const TemplateArgumentListInfo *TemplateArgs = nullptr); + // Check whether the declarations we found through a nested-name + // specifier in a member expression are actually members of the base + // type. The restriction here is: + // + // C++ [expr.ref]p2: + // ... In these cases, the id-expression shall name a + // member of the class or of one of its base classes. + // + // So it's perfectly legitimate for the nested-name specifier to name + // an unrelated class, and for us to find an overload set including + // decls from classes which are not superclasses, as long as the decl + // we actually pick through overload resolution is from a superclass. bool CheckQualifiedMemberReference(Expr *BaseExpr, QualType BaseType, const CXXScopeSpec &SS, const LookupResult &R); @@ -7066,6 +8646,7 @@ class Sema final : public SemaBase { DeclAccessPair FoundDecl, const DeclarationNameInfo &MemberNameInfo); + /// Perform conversions on the LHS of a member access expression. ExprResult PerformMemberExprBaseConversion(Expr *Base, bool IsArrow); ExprResult BuildAnonymousStructUnionMemberReference( @@ -7099,6 +8680,8 @@ class Sema final : public SemaBase { bool IsStringInit(Expr *Init, const ArrayType *AT); + /// Determine whether we can perform aggregate initialization for the purposes + /// of overload resolution. bool CanPerformAggregateInitializationForOverloadResolution( const InitializedEntity &Entity, InitListExpr *From); @@ -7501,35 +9084,158 @@ class Sema final : public SemaBase { Scope *S, DeclarationName Name, SourceLocation Loc, LookupNameKind NameKind, RedeclarationKind Redecl = RedeclarationKind::NotForRedeclaration); + + /// Lookup a builtin function, when name lookup would otherwise + /// fail. bool LookupBuiltin(LookupResult &R); void LookupNecessaryTypesForBuiltin(Scope *S, unsigned ID); + + /// Perform unqualified name lookup starting from a given + /// scope. + /// + /// Unqualified name lookup (C++ [basic.lookup.unqual], C99 6.2.1) is + /// used to find names within the current scope. For example, 'x' in + /// @code + /// int x; + /// int f() { + /// return x; // unqualified name look finds 'x' in the global scope + /// } + /// @endcode + /// + /// Different lookup criteria can find different names. For example, a + /// particular scope can have both a struct and a function of the same + /// name, and each can be found by certain lookup criteria. For more + /// information about lookup criteria, see the documentation for the + /// class LookupCriteria. + /// + /// @param S The scope from which unqualified name lookup will + /// begin. If the lookup criteria permits, name lookup may also search + /// in the parent scopes. + /// + /// @param [in,out] R Specifies the lookup to perform (e.g., the name to + /// look up and the lookup kind), and is updated with the results of lookup + /// including zero or more declarations and possibly additional information + /// used to diagnose ambiguities. + /// + /// @returns \c true if lookup succeeded and false otherwise. bool LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation = false, bool ForceNoCPlusPlus = false); + + /// Perform qualified name lookup into a given context. + /// + /// Qualified name lookup (C++ [basic.lookup.qual]) is used to find + /// names when the context of those names is explicit specified, e.g., + /// "std::vector" or "x->member", or as part of unqualified name lookup. + /// + /// Different lookup criteria can find different names. For example, a + /// particular scope can have both a struct and a function of the same + /// name, and each can be found by certain lookup criteria. For more + /// information about lookup criteria, see the documentation for the + /// class LookupCriteria. + /// + /// \param R captures both the lookup criteria and any lookup results found. + /// + /// \param LookupCtx The context in which qualified name lookup will + /// search. If the lookup criteria permits, name lookup may also search + /// in the parent contexts or (for C++ classes) base classes. + /// + /// \param InUnqualifiedLookup true if this is qualified name lookup that + /// occurs as part of unqualified name lookup. + /// + /// \returns true if lookup succeeded, false if it failed. bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, bool InUnqualifiedLookup = false); + + /// Performs qualified name lookup or special type of lookup for + /// "__super::" scope specifier. + /// + /// This routine is a convenience overload meant to be called from contexts + /// that need to perform a qualified name lookup with an optional C++ scope + /// specifier that might require special kind of lookup. + /// + /// \param R captures both the lookup criteria and any lookup results found. + /// + /// \param LookupCtx The context in which qualified name lookup will + /// search. + /// + /// \param SS An optional C++ scope-specifier. + /// + /// \returns true if lookup succeeded, false if it failed. bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, CXXScopeSpec &SS); + + /// Performs name lookup for a name that was parsed in the + /// source code, and may contain a C++ scope specifier. + /// + /// This routine is a convenience routine meant to be called from + /// contexts that receive a name and an optional C++ scope specifier + /// (e.g., "N::M::x"). It will then perform either qualified or + /// unqualified name lookup (with LookupQualifiedName or LookupName, + /// respectively) on the given name and return those results. It will + /// perform a special type of lookup for "__super::" scope specifier. + /// + /// @param S The scope from which unqualified name lookup will + /// begin. + /// + /// @param SS An optional C++ scope-specifier, e.g., "::N::M". + /// + /// @param EnteringContext Indicates whether we are going to enter the + /// context of the scope-specifier SS (if present). + /// + /// @returns True if any decls were found (but possibly ambiguous) bool LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, QualType ObjectType, bool AllowBuiltinCreation = false, bool EnteringContext = false); + + /// Perform qualified name lookup into all base classes of the given + /// class. + /// + /// \param R captures both the lookup criteria and any lookup results found. + /// + /// \param Class The context in which qualified name lookup will + /// search. Name lookup will search in all base classes merging the results. + /// + /// @returns True if any decls were found (but possibly ambiguous) bool LookupInSuper(LookupResult &R, CXXRecordDecl *Class); void LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S, UnresolvedSetImpl &Functions); + /// LookupOrCreateLabel - Do a name lookup of a label with the specified name. + /// If GnuLabelLoc is a valid source location, then this is a definition + /// of an __label__ label name, otherwise it is a normal label definition + /// or use. LabelDecl *LookupOrCreateLabel(IdentifierInfo *II, SourceLocation IdentLoc, SourceLocation GnuLabelLoc = SourceLocation()); + /// Look up the constructors for the given class. DeclContextLookupResult LookupConstructors(CXXRecordDecl *Class); + + /// Look up the default constructor for the given class. CXXConstructorDecl *LookupDefaultConstructor(CXXRecordDecl *Class); + + /// Look up the copying constructor for the given class. CXXConstructorDecl *LookupCopyingConstructor(CXXRecordDecl *Class, unsigned Quals); + + /// Look up the copying assignment operator for the given class. CXXMethodDecl *LookupCopyingAssignment(CXXRecordDecl *Class, unsigned Quals, bool RValueThis, unsigned ThisQuals); + + /// Look up the moving constructor for the given class. CXXConstructorDecl *LookupMovingConstructor(CXXRecordDecl *Class, unsigned Quals); + + /// Look up the moving assignment operator for the given class. CXXMethodDecl *LookupMovingAssignment(CXXRecordDecl *Class, unsigned Quals, bool RValueThis, unsigned ThisQuals); + + /// Look for the destructor of the given class. + /// + /// During semantic analysis, this routine should be used in lieu of + /// CXXRecordDecl::getDestructor(). + /// + /// \returns The destructor for this class. CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); /// Force the declaration of any implicitly-declared members of this @@ -7618,18 +9324,49 @@ class Sema final : public SemaBase { const PartialDiagnostic &TypoDiag, bool ErrorRecovery = true); + /// Diagnose a successfully-corrected typo. Separated from the correction + /// itself to allow external validation of the result, etc. + /// + /// \param Correction The result of performing typo correction. + /// \param TypoDiag The diagnostic to produce. This will have the corrected + /// string added to it (and usually also a fixit). + /// \param PrevNote A note to use when indicating the location of the entity + /// to which we are correcting. Will have the correction string added + /// to it. + /// \param ErrorRecovery If \c true (the default), the caller is going to + /// recover from the typo as if the corrected string had been typed. + /// In this case, \c PDiag must be an error, and we will attach a fixit + /// to it. void diagnoseTypo(const TypoCorrection &Correction, const PartialDiagnostic &TypoDiag, const PartialDiagnostic &PrevNote, bool ErrorRecovery = true); + /// Find the associated classes and namespaces for + /// argument-dependent lookup for a call with the given set of + /// arguments. + /// + /// This routine computes the sets of associated classes and associated + /// namespaces searched by argument-dependent lookup + /// (C++ [basic.lookup.argdep]) for a given set of arguments. void FindAssociatedClassesAndNamespaces( SourceLocation InstantiationLoc, ArrayRef Args, AssociatedNamespaceSet &AssociatedNamespaces, AssociatedClassSet &AssociatedClasses); + /// Produce a diagnostic describing the ambiguity that resulted + /// from name lookup. + /// + /// \param Result The result of the ambiguous lookup to be diagnosed. void DiagnoseAmbiguousLookup(LookupResult &Result); + /// LookupLiteralOperator - Determine which literal operator should be used + /// for a user-defined literal, per C++11 [lex.ext]. + /// + /// Normal overload resolution is not used to select which literal operator to + /// call for a user-defined literal. Look up the provided literal operator + /// name, and filter the results to the appropriate set for the given argument + /// types. LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef ArgTys, bool AllowRaw, bool AllowTemplate, @@ -7654,6 +9391,37 @@ class Sema final : public SemaBase { CTK_ErrorRecovery // CorrectTypo used in normal error recovery. }; + /// Try to "correct" a typo in the source code by finding + /// visible declarations whose names are similar to the name that was + /// present in the source code. + /// + /// \param TypoName the \c DeclarationNameInfo structure that contains + /// the name that was present in the source code along with its location. + /// + /// \param LookupKind the name-lookup criteria used to search for the name. + /// + /// \param S the scope in which name lookup occurs. + /// + /// \param SS the nested-name-specifier that precedes the name we're + /// looking for, if present. + /// + /// \param CCC A CorrectionCandidateCallback object that provides further + /// validation of typo correction candidates. It also provides flags for + /// determining the set of keywords permitted. + /// + /// \param MemberContext if non-NULL, the context in which to look for + /// a member access expression. + /// + /// \param EnteringContext whether we're entering the context described by + /// the nested-name-specifier SS. + /// + /// \param OPT when non-NULL, the search for visible declarations will + /// also walk the protocols in the qualified interfaces of \p OPT. + /// + /// \returns a \c TypoCorrection containing the corrected name if the typo + /// along with information such as the \c NamedDecl where the corrected name + /// was declared, and any additional \c NestedNameSpecifier needed to access + /// it (C++ only). The \c TypoCorrection is empty if there is no correction. TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, Sema::LookupNameKind LookupKind, Scope *S, CXXScopeSpec *SS, CorrectionCandidateCallback &CCC, @@ -7663,6 +9431,44 @@ class Sema final : public SemaBase { const ObjCObjectPointerType *OPT = nullptr, bool RecordFailure = true); + /// Try to "correct" a typo in the source code by finding + /// visible declarations whose names are similar to the name that was + /// present in the source code. + /// + /// \param TypoName the \c DeclarationNameInfo structure that contains + /// the name that was present in the source code along with its location. + /// + /// \param LookupKind the name-lookup criteria used to search for the name. + /// + /// \param S the scope in which name lookup occurs. + /// + /// \param SS the nested-name-specifier that precedes the name we're + /// looking for, if present. + /// + /// \param CCC A CorrectionCandidateCallback object that provides further + /// validation of typo correction candidates. It also provides flags for + /// determining the set of keywords permitted. + /// + /// \param TDG A TypoDiagnosticGenerator functor that will be used to print + /// diagnostics when the actual typo correction is attempted. + /// + /// \param TRC A TypoRecoveryCallback functor that will be used to build an + /// Expr from a typo correction candidate. + /// + /// \param MemberContext if non-NULL, the context in which to look for + /// a member access expression. + /// + /// \param EnteringContext whether we're entering the context described by + /// the nested-name-specifier SS. + /// + /// \param OPT when non-NULL, the search for visible declarations will + /// also walk the protocols in the qualified interfaces of \p OPT. + /// + /// \returns a new \c TypoExpr that will later be replaced in the AST with an + /// Expr representing the result of performing typo correction, or nullptr if + /// typo correction is not possible. If nullptr is returned, no diagnostics + /// will be emitted and it is the responsibility of the caller to emit any + /// that are needed. TypoExpr *CorrectTypoDelayed( const DeclarationNameInfo &Typo, Sema::LookupNameKind LookupKind, Scope *S, CXXScopeSpec *SS, CorrectionCandidateCallback &CCC, @@ -7721,6 +9527,7 @@ class Sema final : public SemaBase { bool CppLookupName(LookupResult &R, Scope *S); + /// Determine if we could use all the declarations in the module. bool isUsableModule(const Module *M); /// Helper for CorrectTypo and CorrectTypoDelayed used to create and @@ -7879,8 +9686,12 @@ class Sema final : public SemaBase { void createImplicitModuleImportForErrorRecovery(SourceLocation Loc, Module *Mod); + /// We have parsed the start of an export declaration, including the '{' + /// (if present). Decl *ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, SourceLocation LBraceLoc); + + /// Complete the definition of an export declaration. Decl *ActOnFinishExportDecl(Scope *S, Decl *ExportDecl, SourceLocation RBraceLoc); @@ -7982,6 +9793,40 @@ class Sema final : public SemaBase { /// non-function. Ovl_NonFunction }; + + /// Determine whether the given New declaration is an overload of the + /// declarations in Old. This routine returns Ovl_Match or Ovl_NonFunction if + /// New and Old cannot be overloaded, e.g., if New has the same signature as + /// some function in Old (C++ 1.3.10) or if the Old declarations aren't + /// functions (or function templates) at all. When it does return Ovl_Match or + /// Ovl_NonFunction, MatchedDecl will point to the decl that New cannot be + /// overloaded with. This decl may be a UsingShadowDecl on top of the + /// underlying declaration. + /// + /// Example: Given the following input: + /// + /// void f(int, float); // #1 + /// void f(int, int); // #2 + /// int f(int, int); // #3 + /// + /// When we process #1, there is no previous declaration of "f", so IsOverload + /// will not be used. + /// + /// When we process #2, Old contains only the FunctionDecl for #1. By + /// comparing the parameter types, we see that #1 and #2 are overloaded (since + /// they have different signatures), so this routine returns Ovl_Overload; + /// MatchedDecl is unchanged. + /// + /// When we process #3, Old is an overload set containing #1 and #2. We + /// compare the signatures of #3 to #1 (they're overloaded, so we do nothing) + /// and then #3 to #2. Since the signatures of #3 and #2 are identical (return + /// types of functions are not part of the signature), IsOverload returns + /// Ovl_Match and MatchedDecl will be set to point to the FunctionDecl for #2. + /// + /// 'NewIsUsingShadowDecl' indicates that 'New' is being introduced into a + /// class by a using declaration. The rules for whether to hide shadow + /// declarations ignore some properties which otherwise figure into a function + /// template's signature. OverloadKind CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &OldDecls, NamedDecl *&OldDecl, bool UseMemberUsingDeclRules); @@ -8007,21 +9852,68 @@ class Sema final : public SemaBase { AllowedExplicit AllowExplicit, bool InOverloadResolution, bool CStyle, bool AllowObjCWritebackConversion); + /// PerformImplicitConversion - Perform an implicit conversion of the + /// expression From to the type ToType. Returns the + /// converted expression. Flavor is the kind of conversion we're + /// performing, used in the error message. If @p AllowExplicit, + /// explicit user-defined conversions are permitted. ExprResult PerformImplicitConversion(Expr *From, QualType ToType, AssignmentAction Action, bool AllowExplicit = false); + /// IsIntegralPromotion - Determines whether the conversion from the + /// expression From (whose potentially-adjusted type is FromType) to + /// ToType is an integral promotion (C++ 4.5). If so, returns true and + /// sets PromotedType to the promoted type. bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); + + /// IsFloatingPointPromotion - Determines whether the conversion from + /// FromType to ToType is a floating point promotion (C++ 4.6). If so, + /// returns true and sets PromotedType to the promoted type. bool IsFloatingPointPromotion(QualType FromType, QualType ToType); + + /// Determine if a conversion is a complex promotion. + /// + /// A complex promotion is defined as a complex -> complex conversion + /// where the conversion between the underlying real types is a + /// floating-point or integral promotion. bool IsComplexPromotion(QualType FromType, QualType ToType); + + /// IsPointerConversion - Determines whether the conversion of the + /// expression From, which has the (possibly adjusted) type FromType, + /// can be converted to the type ToType via a pointer conversion (C++ + /// 4.10). If so, returns true and places the converted type (that + /// might differ from ToType in its cv-qualifiers at some level) into + /// ConvertedType. + /// + /// This routine also supports conversions to and from block pointers + /// and conversions with Objective-C's 'id', 'id', and + /// pointers to interfaces. FIXME: Once we've determined the + /// appropriate overloading rules for Objective-C, we may want to + /// split the Objective-C checks into a different routine; however, + /// GCC seems to consider all of these conversions to be pointer + /// conversions, so for now they live here. IncompatibleObjC will be + /// set if the conversion is an allowed Objective-C conversion that + /// should result in a warning. bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType, bool InOverloadResolution, QualType &ConvertedType, bool &IncompatibleObjC); + + /// isObjCPointerConversion - Determines whether this is an + /// Objective-C pointer conversion. Subroutine of IsPointerConversion, + /// with the same arguments and return values. bool isObjCPointerConversion(QualType FromType, QualType ToType, QualType &ConvertedType, bool &IncompatibleObjC); bool IsBlockPointerConversion(QualType FromType, QualType ToType, QualType &ConvertedType); + /// FunctionParamTypesAreEqual - This routine checks two function proto types + /// for equality of their parameter types. Caller has already checked that + /// they have same number of parameters. If the parameters are different, + /// ArgPos will have the parameter index of the first different parameter. + /// If `Reversed` is true, the parameters of `NewType` will be compared in + /// reverse order. That's useful if one of the functions is being used as a + /// C++20 synthesized operator overload with a reversed parameter order. bool FunctionParamTypesAreEqual(ArrayRef Old, ArrayRef New, unsigned *ArgPos = nullptr, @@ -8037,20 +9929,54 @@ class Sema final : public SemaBase { unsigned *ArgPos = nullptr, bool Reversed = false); + /// HandleFunctionTypeMismatch - Gives diagnostic information for differeing + /// function types. Catches different number of parameter, mismatch in + /// parameter types, and different return types. void HandleFunctionTypeMismatch(PartialDiagnostic &PDiag, QualType FromType, QualType ToType); + /// CheckPointerConversion - Check the pointer conversion from the + /// expression From to the type ToType. This routine checks for + /// ambiguous or inaccessible derived-to-base pointer + /// conversions for which IsPointerConversion has already returned + /// true. It returns true and produces a diagnostic if there was an + /// error, or returns false otherwise. bool CheckPointerConversion(Expr *From, QualType ToType, CastKind &Kind, CXXCastPath &BasePath, bool IgnoreBaseAccess, bool Diagnose = true); + + /// IsMemberPointerConversion - Determines whether the conversion of the + /// expression From, which has the (possibly adjusted) type FromType, can be + /// converted to the type ToType via a member pointer conversion (C++ 4.11). + /// If so, returns true and places the converted type (that might differ from + /// ToType in its cv-qualifiers at some level) into ConvertedType. bool IsMemberPointerConversion(Expr *From, QualType FromType, QualType ToType, bool InOverloadResolution, QualType &ConvertedType); + + /// CheckMemberPointerConversion - Check the member pointer conversion from + /// the expression From to the type ToType. This routine checks for ambiguous + /// or virtual or inaccessible base-to-derived member pointer conversions for + /// which IsMemberPointerConversion has already returned true. It returns true + /// and produces a diagnostic if there was an error, or returns false + /// otherwise. bool CheckMemberPointerConversion(Expr *From, QualType ToType, CastKind &Kind, CXXCastPath &BasePath, bool IgnoreBaseAccess); + + /// IsQualificationConversion - Determines whether the conversion from + /// an rvalue of type FromType to ToType is a qualification conversion + /// (C++ 4.4). + /// + /// \param ObjCLifetimeConversion Output parameter that will be set to + /// indicate when the qualification conversion involves a change in the + /// Objective-C object lifetime. bool IsQualificationConversion(QualType FromType, QualType ToType, bool CStyle, bool &ObjCLifetimeConversion); + + /// Determine whether the conversion from FromType to ToType is a valid + /// conversion that strips "noexcept" or "noreturn" off the nested function + /// type. bool IsFunctionConversion(QualType FromType, QualType ToType, QualType &ResultTy); bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); @@ -8066,7 +9992,13 @@ class Sema final : public SemaBase { Expr *From, NestedNameSpecifier *Qualifier, NamedDecl *FoundDecl, CXXMethodDecl *Method); + /// PerformContextuallyConvertToBool - Perform a contextual conversion + /// of the expression From to bool (C++0x [conv]p3). ExprResult PerformContextuallyConvertToBool(Expr *From); + + /// PerformContextuallyConvertToObjCPointer - Perform a contextual + /// conversion of the expression From to an Objective-C pointer type. + /// Returns a valid but null ExprResult if no conversion sequence exists. ExprResult PerformContextuallyConvertToObjCPointer(Expr *From); /// Contexts in which a converted constant expression is required. @@ -8093,6 +10025,9 @@ class Sema final : public SemaBase { APValue &Value, CCEKind CCE, NamedDecl *Dest = nullptr); + /// EvaluateConvertedConstantExpression - Evaluate an Expression + /// That is a converted constant expression + /// (which was built with BuildConvertedConstantExpression) ExprResult EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, CCEKind CCE, bool RequireInt, @@ -8213,10 +10148,24 @@ class Sema final : public SemaBase { }; using ReferenceConversions = ReferenceConversionsScope::ReferenceConversions; + /// CompareReferenceRelationship - Compare the two types T1 and T2 to + /// determine whether they are reference-compatible, + /// reference-related, or incompatible, for use in C++ initialization by + /// reference (C++ [dcl.ref.init]p4). Neither type can be a reference + /// type, and the first type (T1) is the pointee type of the reference + /// type being initialized. ReferenceCompareResult CompareReferenceRelationship(SourceLocation Loc, QualType T1, QualType T2, ReferenceConversions *Conv = nullptr); + /// AddOverloadCandidate - Adds the given function to the set of + /// candidate functions, using the given function call arguments. If + /// @p SuppressUserConversions, then don't allow user-defined + /// conversions via constructors or conversion operators. + /// + /// \param PartialOverloading true if we are performing "partial" overloading + /// based on an incomplete set of function arguments. This feature is used by + /// code completion. void AddOverloadCandidate( FunctionDecl *Function, DeclAccessPair FoundDecl, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false, @@ -8226,18 +10175,32 @@ class Sema final : public SemaBase { ConversionSequenceList EarlyConversions = std::nullopt, OverloadCandidateParamOrder PO = {}, bool AggregateCandidateDeduction = false); + + /// Add all of the function declarations in the given function set to + /// the overload candidate set. void AddFunctionCandidates( const UnresolvedSetImpl &Functions, ArrayRef Args, OverloadCandidateSet &CandidateSet, TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr, bool SuppressUserConversions = false, bool PartialOverloading = false, bool FirstArgumentIsBase = false); + + /// AddMethodCandidate - Adds a named decl (which is some kind of + /// method) as a method candidate to the given overload set. void AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, Expr::Classification ObjectClassification, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool SuppressUserConversion = false, OverloadCandidateParamOrder PO = {}); + + /// AddMethodCandidate - Adds the given C++ member function to the set + /// of candidate functions, using the given function call arguments + /// and the object argument (@c Object). For example, in a call + /// @c o.f(a1,a2), @c Object will contain @c o and @c Args will contain + /// both @c a1 and @c a2. If @p SuppressUserConversions, then don't + /// allow user-defined conversions via constructors or conversion + /// operators. void AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, QualType ObjectType, @@ -8247,6 +10210,10 @@ class Sema final : public SemaBase { bool PartialOverloading = false, ConversionSequenceList EarlyConversions = std::nullopt, OverloadCandidateParamOrder PO = {}); + + /// Add a C++ member function template as a candidate to the candidate + /// set, using template argument deduction to produce an appropriate member + /// function template specialization. void AddMethodTemplateCandidate( FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, @@ -8254,6 +10221,10 @@ class Sema final : public SemaBase { Expr::Classification ObjectClassification, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false, bool PartialOverloading = false, OverloadCandidateParamOrder PO = {}); + + /// Add a C++ function template specialization as a candidate + /// in the candidate set, using template argument deduction to produce + /// an appropriate function template specialization. void AddTemplateOverloadCandidate( FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, @@ -8262,6 +10233,10 @@ class Sema final : public SemaBase { ADLCallKind IsADLCandidate = ADLCallKind::NotADL, OverloadCandidateParamOrder PO = {}, bool AggregateCandidateDeduction = false); + + /// Check that implicit conversion sequences can be formed for each argument + /// whose corresponding parameter has a non-dependent type, per DR1391's + /// [temp.deduct.call]p10. bool CheckNonDependentConversions( FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, @@ -8269,37 +10244,91 @@ class Sema final : public SemaBase { CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(), Expr::Classification ObjectClassification = {}, OverloadCandidateParamOrder PO = {}); + + /// AddConversionCandidate - Add a C++ conversion function as a + /// candidate in the candidate set (C++ [over.match.conv], + /// C++ [over.match.copy]). From is the expression we're converting from, + /// and ToType is the type that we're eventually trying to convert to + /// (which may or may not be the same type as the type that the + /// conversion function produces). void AddConversionCandidate( CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From, QualType ToType, OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit, bool AllowExplicit, bool AllowResultConversion = true); + + /// Adds a conversion function template specialization + /// candidate to the overload set, using template argument deduction + /// to deduce the template arguments of the conversion function + /// template from the type that we are converting to (C++ + /// [temp.deduct.conv]). void AddTemplateConversionCandidate( FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From, QualType ToType, OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit, bool AllowExplicit, bool AllowResultConversion = true); + + /// AddSurrogateCandidate - Adds a "surrogate" candidate function that + /// converts the given @c Object to a function pointer via the + /// conversion function @c Conversion, and then attempts to call it + /// with the given arguments (C++ [over.call.object]p2-4). Proto is + /// the type of function that we'll eventually be calling. void AddSurrogateCandidate(CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, const FunctionProtoType *Proto, Expr *Object, ArrayRef Args, OverloadCandidateSet &CandidateSet); + + /// Add all of the non-member operator function declarations in the given + /// function set to the overload candidate set. void AddNonMemberOperatorCandidates( const UnresolvedSetImpl &Functions, ArrayRef Args, OverloadCandidateSet &CandidateSet, TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr); + + /// Add overload candidates for overloaded operators that are + /// member functions. + /// + /// Add the overloaded operator candidates that are member functions + /// for the operator Op that was used in an operator expression such + /// as "x Op y". , Args/NumArgs provides the operator arguments, and + /// CandidateSet will store the added overload candidates. (C++ + /// [over.match.oper]). void AddMemberOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, ArrayRef Args, OverloadCandidateSet &CandidateSet, OverloadCandidateParamOrder PO = {}); + + /// AddBuiltinCandidate - Add a candidate for a built-in + /// operator. ResultTy and ParamTys are the result and parameter types + /// of the built-in candidate, respectively. Args and NumArgs are the + /// arguments being passed to the candidate. IsAssignmentOperator + /// should be true when this built-in candidate is an assignment + /// operator. NumContextualBoolArguments is the number of arguments + /// (at the beginning of the argument list) that will be contextually + /// converted to bool. void AddBuiltinCandidate(QualType *ParamTys, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool IsAssignmentOperator = false, unsigned NumContextualBoolArguments = 0); + + /// AddBuiltinOperatorCandidates - Add the appropriate built-in + /// operator overloads to the candidate set (C++ [over.built]), based + /// on the operator @p Op and the arguments given. For example, if the + /// operator is a binary '+', this routine might add "int + /// operator+(int, int)" to cover integer addition. void AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, ArrayRef Args, OverloadCandidateSet &CandidateSet); + + /// Add function candidates found via argument-dependent lookup + /// to the set of overloading candidates. + /// + /// This routine performs argument-dependent name lookup based on the + /// given function name (which may also be an operator name) and adds + /// all of the overload candidates found by ADL to the overload + /// candidate set (C++ [basic.lookup.argdep]). void AddArgumentDependentLookupCandidates( DeclarationName Name, SourceLocation Loc, ArrayRef Args, TemplateArgumentListInfo *ExplicitTemplateArgs, @@ -8369,31 +10398,83 @@ class Sema final : public SemaBase { // R (S::*)(A) --> R (A) QualType ExtractUnqualifiedFunctionType(QualType PossiblyAFunctionType); + /// ResolveAddressOfOverloadedFunction - Try to resolve the address of + /// an overloaded function (C++ [over.over]), where @p From is an + /// expression with overloaded function type and @p ToType is the type + /// we're trying to resolve to. For example: + /// + /// @code + /// int f(double); + /// int f(int); + /// + /// int (*pfd)(double) = f; // selects f(double) + /// @endcode + /// + /// This routine returns the resulting FunctionDecl if it could be + /// resolved, and NULL otherwise. When @p Complain is true, this + /// routine will emit diagnostics if there is an error. FunctionDecl * ResolveAddressOfOverloadedFunction(Expr *AddressOfExpr, QualType TargetType, bool Complain, DeclAccessPair &Found, bool *pHadMultipleCandidates = nullptr); + /// Given an expression that refers to an overloaded function, try to + /// resolve that function to a single function that can have its address + /// taken. This will modify `Pair` iff it returns non-null. + /// + /// This routine can only succeed if from all of the candidates in the + /// overload set for SrcExpr that can have their addresses taken, there is one + /// candidate that is more constrained than the rest. FunctionDecl * resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &FoundResult); + /// Given an overloaded function, tries to turn it into a non-overloaded + /// function reference using resolveAddressOfSingleOverloadCandidate. This + /// will perform access checks, diagnose the use of the resultant decl, and, + /// if requested, potentially perform a function-to-pointer decay. + /// + /// Returns false if resolveAddressOfSingleOverloadCandidate fails. + /// Otherwise, returns true. This may emit diagnostics and return true. bool resolveAndFixAddressOfSingleOverloadCandidate( ExprResult &SrcExpr, bool DoFunctionPointerConversion = false); + /// Given an expression that refers to an overloaded function, try to + /// resolve that overloaded function expression down to a single function. + /// + /// This routine can only resolve template-ids that refer to a single function + /// template, where that template-id refers to a single template whose + /// template arguments are either provided by the template-id or have + /// defaults, as described in C++0x [temp.arg.explicit]p3. + /// + /// If no template-ids are found, no diagnostics are emitted and NULL is + /// returned. FunctionDecl *ResolveSingleFunctionTemplateSpecialization( OverloadExpr *ovl, bool Complain = false, DeclAccessPair *Found = nullptr, TemplateSpecCandidateSet *FailedTSC = nullptr); + // Resolve and fix an overloaded expression that can be resolved + // because it identifies a single function template specialization. + // + // Last three arguments should only be supplied if Complain = true + // + // Return true if it was logically possible to so resolve the + // expression, regardless of whether or not it succeeded. Always + // returns true if 'complain' is set. bool ResolveAndFixSingleFunctionTemplateSpecialization( ExprResult &SrcExpr, bool DoFunctionPointerConversion = false, bool Complain = false, SourceRange OpRangeForComplaining = SourceRange(), QualType DestTypeForComplaining = QualType(), unsigned DiagIDForComplaining = 0); + /// Add the overload candidates named by callee and/or found by argument + /// dependent lookup to the given overload set. void AddOverloadedCallCandidates(UnresolvedLookupExpr *ULE, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool PartialOverloading = false); + + /// Add the call candidates from the given set of lookup results to the given + /// overload set. Non-function lookup results are ignored. void AddOverloadedCallCandidates( LookupResult &R, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, OverloadCandidateSet &CandidateSet); @@ -8406,6 +10487,13 @@ class Sema final : public SemaBase { FRS_DiagnosticIssued }; + /// Build a call to 'begin' or 'end' for a C++11 for-range statement. If the + /// given LookupResult is non-empty, it is assumed to describe a member which + /// will be invoked. Otherwise, the function will be found via argument + /// dependent lookup. + /// CallExpr is set to a valid expression and FRS_Success returned on success, + /// otherwise CallExpr is set to ExprError() and some non-success value + /// is returned. ForRangeStatus BuildForRangeBeginEndCall(SourceLocation Loc, SourceLocation RangeLoc, const DeclarationNameInfo &NameInfo, @@ -8413,11 +10501,20 @@ class Sema final : public SemaBase { OverloadCandidateSet *CandidateSet, Expr *Range, ExprResult *CallExpr); + /// BuildOverloadedCallExpr - Given the call expression that calls Fn + /// (which eventually refers to the declaration Func) and the call + /// arguments Args/NumArgs, attempt to resolve the function call down + /// to a specific function. If overload resolution succeeds, returns + /// the call expression produced by overload resolution. + /// Otherwise, emits diagnostics and returns ExprError. ExprResult BuildOverloadedCallExpr( Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, SourceLocation LParenLoc, MultiExprArg Args, SourceLocation RParenLoc, Expr *ExecConfig, bool AllowTypoCorrection = true, bool CalleesAddressIsTaken = false); + /// Constructs and populates an OverloadedCandidateSet from + /// the given function. + /// \returns true when an the ExprResult output parameter has been set. bool buildOverloadedCallSet(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, MultiExprArg Args, SourceLocation RParenLoc, OverloadCandidateSet *CandidateSet, @@ -8429,15 +10526,55 @@ class Sema final : public SemaBase { const UnresolvedSetImpl &Fns, bool PerformADL = true); + /// Create a unary operation that may resolve to an overloaded + /// operator. + /// + /// \param OpLoc The location of the operator itself (e.g., '*'). + /// + /// \param Opc The UnaryOperatorKind that describes this operator. + /// + /// \param Fns The set of non-member functions that will be + /// considered by overload resolution. The caller needs to build this + /// set based on the context using, e.g., + /// LookupOverloadedOperatorName() and ArgumentDependentLookup(). This + /// set should not contain any member functions; those will be added + /// by CreateOverloadedUnaryOp(). + /// + /// \param Input The input argument. ExprResult CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc, const UnresolvedSetImpl &Fns, Expr *input, bool RequiresADL = true); + /// Perform lookup for an overloaded binary operator. void LookupOverloadedBinOp(OverloadCandidateSet &CandidateSet, OverloadedOperatorKind Op, const UnresolvedSetImpl &Fns, ArrayRef Args, bool RequiresADL = true); + + /// Create a binary operation that may resolve to an overloaded + /// operator. + /// + /// \param OpLoc The location of the operator itself (e.g., '+'). + /// + /// \param Opc The BinaryOperatorKind that describes this operator. + /// + /// \param Fns The set of non-member functions that will be + /// considered by overload resolution. The caller needs to build this + /// set based on the context using, e.g., + /// LookupOverloadedOperatorName() and ArgumentDependentLookup(). This + /// set should not contain any member functions; those will be added + /// by CreateOverloadedBinOp(). + /// + /// \param LHS Left-hand argument. + /// \param RHS Right-hand argument. + /// \param PerformADL Whether to consider operator candidates found by ADL. + /// \param AllowRewrittenCandidates Whether to consider candidates found by + /// C++20 operator rewrites. + /// \param DefaultedFn If we are synthesizing a defaulted operator function, + /// the function in question. Such a function is never a candidate in + /// our overload resolution. This also enables synthesizing a three-way + /// comparison from < and == as described in C++20 [class.spaceship]p1. ExprResult CreateOverloadedBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc, const UnresolvedSetImpl &Fns, Expr *LHS, Expr *RHS, bool RequiresADL = true, @@ -8452,15 +10589,30 @@ class Sema final : public SemaBase { SourceLocation RLoc, Expr *Base, MultiExprArg Args); + /// BuildCallToMemberFunction - Build a call to a member + /// function. MemExpr is the expression that refers to the member + /// function (and includes the object parameter), Args/NumArgs are the + /// arguments to the function call (not including the object + /// parameter). The caller needs to validate that the member + /// expression refers to a non-static member function or an overloaded + /// member function. ExprResult BuildCallToMemberFunction( Scope *S, Expr *MemExpr, SourceLocation LParenLoc, MultiExprArg Args, SourceLocation RParenLoc, Expr *ExecConfig = nullptr, bool IsExecConfig = false, bool AllowRecovery = false); + + /// BuildCallToObjectOfClassType - Build a call to an object of class + /// type (C++ [over.call.object]), which can end up invoking an + /// overloaded function call operator (@c operator()) or performing a + /// user-defined conversion on the object argument. ExprResult BuildCallToObjectOfClassType(Scope *S, Expr *Object, SourceLocation LParenLoc, MultiExprArg Args, SourceLocation RParenLoc); + /// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator-> + /// (if one exists), where @c Base is an expression of class type and + /// @c Member is the name of the member we're trying to find. ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc, bool *NoArrowOperatorFound = nullptr); @@ -8469,11 +10621,18 @@ class Sema final : public SemaBase { CXXConversionDecl *Method, bool HadMultipleCandidates); + /// BuildLiteralOperatorCall - Build a UserDefinedLiteral by creating a call + /// to a literal operator described by the provided lookup results. ExprResult BuildLiteralOperatorCall( LookupResult &R, DeclarationNameInfo &SuffixInfo, ArrayRef Args, SourceLocation LitEndLoc, TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr); + /// FixOverloadedFunctionReference - E is an expression that refers to + /// a C++ overloaded function (possibly with some parentheses and + /// perhaps a '&' around it). We have resolved the overloaded function + /// to the function declaration Fn, so patch up the expression E to + /// refer (possibly indirectly) to Fn. Returns the new expr. ExprResult FixOverloadedFunctionReference(Expr *E, DeclAccessPair FoundDecl, FunctionDecl *Fn); ExprResult FixOverloadedFunctionReference(ExprResult, @@ -8528,6 +10687,8 @@ class Sema final : public SemaBase { StmtResult ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHS, SourceLocation DotDotDotLoc, ExprResult RHS, SourceLocation ColonLoc); + + /// ActOnCaseStmtBody - This installs a statement as the body of a case. void ActOnCaseStmtBody(Stmt *CaseStmt, Stmt *SubStmt); StmtResult ActOnDefaultStmt(SourceLocation DefaultLoc, @@ -8582,6 +10743,10 @@ class Sema final : public SemaBase { FullExprArg Third, SourceLocation RParenLoc, Stmt *Body); + /// In an Objective C collection iteration statement: + /// for (x in y) + /// x can be an arbitrary l-value expression. Bind it up as a + /// full-expression. StmtResult ActOnForEachLValueExpr(Expr *E); enum BuildForRangeKind { @@ -8595,17 +10760,42 @@ class Sema final : public SemaBase { BFRK_Check }; + /// ActOnCXXForRangeStmt - Check and build a C++11 for-range statement. + /// + /// C++11 [stmt.ranged]: + /// A range-based for statement is equivalent to + /// + /// { + /// auto && __range = range-init; + /// for ( auto __begin = begin-expr, + /// __end = end-expr; + /// __begin != __end; + /// ++__begin ) { + /// for-range-declaration = *__begin; + /// statement + /// } + /// } + /// + /// The body of the loop is not available yet, since it cannot be analysed + /// until we have determined the type of the for-range-declaration. StmtResult ActOnCXXForRangeStmt( Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, SourceLocation RParenLoc, BuildForRangeKind Kind, ArrayRef LifetimeExtendTemps = {}); + + /// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement. StmtResult BuildCXXForRangeStmt( SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt, SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End, Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc, BuildForRangeKind Kind, ArrayRef LifetimeExtendTemps = {}); + + /// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement. + /// This is a separate step from ActOnCXXForRangeStmt because analysis of the + /// body cannot be performed until after the type of the range variable is + /// determined. StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body); StmtResult ActOnGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc, @@ -8625,12 +10815,54 @@ class Sema final : public SemaBase { bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; } }; enum class SimplerImplicitMoveMode { ForceOff, Normal, ForceOn }; + + /// Determine whether the given expression might be move-eligible or + /// copy-elidable in either a (co_)return statement or throw expression, + /// without considering function return type, if applicable. + /// + /// \param E The expression being returned from the function or block, + /// being thrown, or being co_returned from a coroutine. This expression + /// might be modified by the implementation. + /// + /// \param Mode Overrides detection of current language mode + /// and uses the rules for C++23. + /// + /// \returns An aggregate which contains the Candidate and isMoveEligible + /// and isCopyElidable methods. If Candidate is non-null, it means + /// isMoveEligible() would be true under the most permissive language + /// standard. NamedReturnInfo getNamedReturnInfo( Expr *&E, SimplerImplicitMoveMode Mode = SimplerImplicitMoveMode::Normal); + + /// Determine whether the given NRVO candidate variable is move-eligible or + /// copy-elidable, without considering function return type. + /// + /// \param VD The NRVO candidate variable. + /// + /// \returns An aggregate which contains the Candidate and isMoveEligible + /// and isCopyElidable methods. If Candidate is non-null, it means + /// isMoveEligible() would be true under the most permissive language + /// standard. NamedReturnInfo getNamedReturnInfo(const VarDecl *VD); + + /// Updates given NamedReturnInfo's move-eligible and + /// copy-elidable statuses, considering the function + /// return type criteria as applicable to return statements. + /// + /// \param Info The NamedReturnInfo object to update. + /// + /// \param ReturnType This is the return type of the function. + /// \returns The copy elision candidate, in case the initial return expression + /// was copy elidable, or nullptr otherwise. const VarDecl *getCopyElisionCandidate(NamedReturnInfo &Info, QualType ReturnType); + /// Perform the initialization of a potentially-movable value, which + /// is the result of return value. + /// + /// This routine implements C++20 [class.copy.elision]p3, which attempts to + /// treat returned lvalues as rvalues in certain cases (to prefer move + /// construction), then falls back to treating them as lvalues if that failed. ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity, const NamedReturnInfo &NRInfo, Expr *Value, @@ -8638,6 +10870,8 @@ class Sema final : public SemaBase { TypeLoc getReturnTypeLoc(FunctionDecl *FD) const; + /// Deduce the return type for a function from a returned expression, per + /// C++1y [dcl.spec.auto]p6. bool DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, SourceLocation ReturnLoc, Expr *RetExpr, const AutoType *AT); @@ -8646,12 +10880,20 @@ class Sema final : public SemaBase { Scope *CurScope); StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, bool AllowRecovery = false); + + /// ActOnCapScopeReturnStmt - Utility routine to type-check return statements + /// for capturing scopes. StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, NamedReturnInfo &NRInfo, bool SupressSimplerImplicitMoves); + /// ActOnCXXCatchBlock - Takes an exception declaration and a handler block + /// and creates a proper catch handler from them. StmtResult ActOnCXXCatchBlock(SourceLocation CatchLoc, Decl *ExDecl, Stmt *HandlerBlock); + + /// ActOnCXXTryBlock - Takes a try compound-statement and a number of + /// handlers and creates a try statement from them. StmtResult ActOnCXXTryBlock(SourceLocation TryLoc, Stmt *TryBlock, ArrayRef Handlers); @@ -8917,6 +11159,8 @@ class Sema final : public SemaBase { TemplateTy &SuggestedTemplate, TemplateNameKind &SuggestedKind); + /// Determine whether we would be unable to instantiate this template (because + /// it either has no definition, or is in the process of being instantiated). bool DiagnoseUninstantiableTemplate(SourceLocation PointOfInstantiation, NamedDecl *Instantiation, bool InstantiatedFromMember, @@ -8940,8 +11184,22 @@ class Sema final : public SemaBase { /// Ignored when MSVC compatibility is enabled. void DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl, bool SupportedForCompatibility = false); + + /// AdjustDeclIfTemplate - If the given decl happens to be a template, reset + /// the parameter D to reference the templated declaration and return a + /// pointer to the template declaration. Otherwise, do nothing to D and return + /// null. TemplateDecl *AdjustDeclIfTemplate(Decl *&Decl); + /// ActOnTypeParameter - Called when a C++ template type parameter + /// (e.g., "typename T") has been parsed. Typename specifies whether + /// the keyword "typename" was used to declare the type parameter + /// (otherwise, "class" was used), and KeyLoc is the location of the + /// "class" or "typename" keyword. ParamName is the name of the + /// parameter (NULL indicates an unnamed template parameter) and + /// ParamNameLoc is the location of the parameter name (if any). + /// If the type parameter has a default argument, it will be added + /// later via ActOnTypeParameterDefault. NamedDecl *ActOnTypeParameter(Scope *S, bool Typename, SourceLocation EllipsisLoc, SourceLocation KeyLoc, @@ -8962,6 +11220,10 @@ class Sema final : public SemaBase { SourceLocation EllipsisLoc, bool AllowUnexpandedPack); + /// Attach a type-constraint to a template parameter. + /// \returns true if an error occurred. This can happen if the + /// immediately-declared constraint could not be formed (e.g. incorrect number + /// of arguments for the named concept). bool AttachTypeConstraint(NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, ConceptDecl *NamedConcept, NamedDecl *FoundDecl, @@ -8974,8 +11236,16 @@ class Sema final : public SemaBase { NonTypeTemplateParmDecl *OrigConstrainedParm, SourceLocation EllipsisLoc); + /// Require the given type to be a structural type, and diagnose if it is not. + /// + /// \return \c true if an error was produced. bool RequireStructuralType(QualType T, SourceLocation Loc); + /// Check that the type of a non-type template parameter is + /// well-formed. + /// + /// \returns the (possibly-promoted) parameter type if valid; + /// otherwise, produces a diagnostic and returns a NULL type. QualType CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI, SourceLocation Loc); QualType CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc); @@ -8984,12 +11254,19 @@ class Sema final : public SemaBase { unsigned Depth, unsigned Position, SourceLocation EqualLoc, Expr *DefaultArg); + + /// ActOnTemplateTemplateParameter - Called when a C++ template template + /// parameter (e.g. T in template