diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0f178df1d18f8c46a2b06aa00667e81f3a1be32a..e25b2f50b1b4eafb7c71a2bf6dafa408d27afc50 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -23,6 +23,7 @@ /llvm/lib/Analysis/ScalarEvolution.cpp @nikic /llvm/lib/Analysis/ValueTracking.cpp @nikic /llvm/lib/IR/ConstantRange.cpp @nikic +/llvm/lib/IR/Core.cpp @nikic /llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @nikic /llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @nikic /llvm/lib/Transforms/InstCombine/ @nikic @@ -63,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 -/mlir/lib/Dialect/Linalg/* @dcaballe @nicolasvasilache +/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 diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index d608ea449f1d40c841e2ae356f4a0fa8431d2d48..a57ba28faf160b7b6df2ac5ba8f89e9cb3a54b6e 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -239,7 +239,7 @@ mlir:dlti: - mlir/**/DLTI/** mlir:emitc: - - mlir/**/EmitC/** + - mlir/**/*EmitC*/** - mlir/lib/Target/Cpp/** mlir:func: @@ -306,7 +306,7 @@ mlir:tensor: - mlir/**/Tensor/** mlir:tosa: - - mlir/**/Tosa/** + - mlir/**/*Tosa*/** mlir:ub: - mlir/**/UB/** diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index 131ad3004f457743403fbc07a64193cf53262c6a..02082a84d8c10779f1b8209db580d9ac496551b0 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -38,9 +38,6 @@ jobs: if: github.repository == 'llvm/llvm-project' outputs: release-version: ${{ steps.vars.outputs.release-version }} - flags: ${{ steps.vars.outputs.flags }} - build-dir: ${{ steps.vars.outputs.build-dir }} - rc-flags: ${{ steps.vars.outputs.rc-flags }} ref: ${{ steps.vars.outputs.ref }} upload: ${{ steps.vars.outputs.upload }} @@ -85,17 +82,11 @@ jobs: fi bash .github/workflows/set-release-binary-outputs.sh "$tag" "$upload" - # Try to get around the 6 hour timeout by first running a job to fill - # the build cache. - fill-cache: - name: "Fill Cache ${{ matrix.os }}" + build-stage1-linux: + name: "Build Stage 1 Linux" needs: prepare - runs-on: ${{ matrix.os }} + runs-on: ubuntu-22.04 if: github.repository == 'llvm/llvm-project' - strategy: - matrix: - os: - - ubuntu-22.04 steps: - name: Checkout LLVM uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -109,81 +100,207 @@ jobs: uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9 with: max-size: 250M - key: sccache-${{ matrix.os }}-release + key: sccache-${{ runner.os }}-release variant: sccache - - name: Build Clang + - name: Build Stage 1 Clang run: | - cmake -G Ninja -C clang/cmake/caches/Release.cmake -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_POSITION_INDEPENDENT_CODE=ON -S llvm -B build - ninja -v -C build clang + sudo chown $USER:$USER /mnt/ + cmake -G Ninja -C clang/cmake/caches/Release.cmake -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -S llvm -B /mnt/build + ninja -v -C /mnt/build + # We need to create an archive of the build directory, because it has too + # many files to upload. + - name: Package Build and Source Directories + run: | + tar -c . | zstd -T0 -c > llvm-project.tar.zst + tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst - build-binaries: - name: ${{ matrix.target.triple }} - permissions: - contents: write # To upload assets to release. + - name: Upload Stage 1 Source + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + with: + name: stage1-source + path: llvm-project.tar.zst + retention-days: 2 + + - name: Upload Stage 1 Build Dir + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + with: + name: stage1-build + path: build.tar.zst + retention-days: 2 + + build-stage2-linux: + name: "Build Stage 2 Linux" needs: - prepare - - fill-cache - runs-on: ${{ matrix.target.runs-on }} + - build-stage1-linux + runs-on: ubuntu-22.04 if: github.repository == 'llvm/llvm-project' - strategy: - fail-fast: false - matrix: - target: - - triple: x86_64-linux-gnu-ubuntu-22.04 - os: ubuntu-22.04 - runs-on: ubuntu-22.04-16x64 - debian-build-deps: > - chrpath - gcc-multilib - ninja-build - steps: - - name: Checkout LLVM - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Install Ninja + uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main + + - name: Download Stage 1 Artifacts + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: - ref: ${{ needs.prepare.outputs.ref }} - path: ${{ needs.prepare.outputs.build-dir }}/llvm-project + pattern: stage1-* + merge-multiple: true - - name: Setup sccache - uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9 + - name: Unpack Artifacts + run: | + tar --zstd -xf llvm-project.tar.zst + rm llvm-project.tar.zst + sudo chown $USER:$USER /mnt/ + tar --zstd -C /mnt -xf build.tar.zst + rm build.tar.zst + + - name: Build Stage 2 + run: | + ninja -C /mnt/build stage2-instrumented + + # We need to create an archive of the build directory, because it has too + # many files to upload. + - name: Save Build and Source Directories + run: | + tar -c . | zstd -T0 -c > llvm-project.tar.zst + tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst + + - name: Upload Stage 2 Source + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 with: - max-size: 250M - key: sccache-${{ matrix.target.os }}-release - save: false - variant: sccache + name: stage2-source + path: ${{ github.workspace }}/llvm-project.tar.zst + retention-days: 2 + + - name: Upload Stage 2 Build Dir + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + with: + name: stage2-build + path: ${{ github.workspace }}/build.tar.zst + retention-days: 2 - - name: Install Brew build dependencies - if: matrix.target.brew-build-deps != '' - run: brew install ${{ matrix.target.brew-build-deps }} - - name: Install Debian build dependencies - if: matrix.target.debian-build-deps != '' - run: sudo apt install ${{ matrix.target.debian-build-deps }} + build-stage3-linux: + name: "Build Stage 3 Linux" + needs: + - prepare + - build-stage2-linux + outputs: + filename: ${{ steps.package-info.outputs.release-filename }} + runs-on: ubuntu-22.04-16x64 + if: github.repository == 'llvm/llvm-project' + steps: + - name: Install Ninja + uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main + + - name: 'Download artifact' + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + with: + pattern: stage2-* + merge-multiple: true - - name: Set macOS build env variables - if: runner.os == 'macOS' + - name: Unpack Artifact run: | - echo "MACOSX_DEPLOYMENT_TARGET=10.9" >> "$GITHUB_ENV" + tar --zstd -xf llvm-project.tar.zst + rm llvm-project.tar.zst + sudo chown $USER:$USER /mnt/ + tar --zstd -C /mnt -xf build.tar.zst + rm build.tar.zst - - name: Build and test release + - name: Build Release Package run: | - ${{ needs.prepare.outputs.build-dir }}/llvm-project/llvm/utils/release/test-release.sh \ - ${{ needs.prepare.outputs.flags }} \ - -triple ${{ matrix.target.triple }} \ - -use-ninja \ - -no-checkout \ - -use-cmake-cache \ - -no-test-suite \ - -configure-flags "-DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache" + ninja -C /mnt/build stage2-package - - name: Upload binaries - if: ${{ always() && needs.prepare.outputs.upload == 'true' }} + - id: package-info + run: | + filename="LLVM-${{ needs.prepare.outputs.release-version }}-Linux.tar.gz" + echo "filename=$filename" >> $GITHUB_OUTPUT + echo "path=/mnt/build/tools/clang/stage2-bins/$filename" >> $GITHUB_OUTPUT + + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + if: always() + with: + name: release-binary + path: ${{ steps.package-info.outputs.path }} + + # Clean up some build files to reduce size of artifact. + - name: Clean Up Build Directory + run: | + find /mnt/build -iname ${{ steps.package-info.outputs.filename }} -delete + + # We need to create an archive of the build directory, because it has too + # many files to upload. + - name: Save Build and Source Directories + run: | + tar -c . | zstd -T0 -c > llvm-project.tar.zst + tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst + + - name: Upload Stage 3 Source + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + with: + name: stage3-source + path: llvm-project.tar.zst + retention-days: 2 + + - name: Upload Stage 3 Build Dir + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + with: + name: stage3-build + path: build.tar.zst + retention-days: 2 + + upload-release-binaries-linux: + name: "Upload Linux Release Binaries" + needs: + - prepare + - build-stage3-linux + if : ${{ needs.prepare.outputs.upload == 'true' }} + runs-on: ubuntu-22.04 + permissions: + contents: write # For release uploads + + steps: + - name: 'Download artifact' + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + with: + name: release-binary + + - name: Upload Release run: | sudo apt install python3-github - ${{ needs.prepare.outputs.build-dir }}/llvm-project/llvm/utils/release/github-upload-release.py \ + ./llvm-project/llvm/utils/release/github-upload-release.py \ --token ${{ github.token }} \ --release ${{ needs.prepare.outputs.release-version }} \ upload \ - --files ${{ needs.prepare.outputs.build-dir }}/clang+llvm-${{ needs.prepare.outputs.release-version }}-${{ matrix.target.triple }}.tar.xz + --files ${{ needs.build-stage3-linux.outputs.release-filename }} + + + test-stage3-linux: + name: "Test Stage 3 Linux" + needs: + - prepare + - build-stage3-linux + runs-on: ubuntu-22.04 + if: github.repository == 'llvm/llvm-project' + steps: + - name: Install Ninja + uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main + + - name: 'Download artifact' + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + with: + pattern: stage3-* + merge-multiple: true + + - name: Unpack Artifact + run: | + tar --zstd -xf llvm-project.tar.zst + rm llvm-project.tar.zst + sudo chown $USER:$USER /mnt/ + tar --zstd -C /mnt -xf build.tar.zst + rm build.tar.zst + + - name: Run Tests + run: | + ninja -C /mnt/build stage2-check-all diff --git a/.github/workflows/release-doxygen.yml b/.github/workflows/release-doxygen.yml index 5e322849a1d093fd775066c7a1768b00f52501c6..12c14bea52f624925e1b9ee5f6e108a24e066197 100644 --- a/.github/workflows/release-doxygen.yml +++ b/.github/workflows/release-doxygen.yml @@ -56,12 +56,12 @@ jobs: pip3 install --user -r ./llvm/docs/requirements.txt - name: Build Doxygen - env: - GITHUB_TOKEN: ${{ github.token }} run: | ./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-sphinx - name: Upload Doxygen if: env.upload + env: + GITHUB_TOKEN: ${{ github.token }} run: | ./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" upload --files ./*doxygen*.tar.xz diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml index 53da8662b0203a87d29e5ed33f678478db02b8bf..29049ff014288733e638b4f434543e44722c5d6d 100644 --- a/.github/workflows/release-tasks.yml +++ b/.github/workflows/release-tasks.yml @@ -1,7 +1,7 @@ name: Release Task permissions: - contents: write + contents: read on: push: @@ -27,6 +27,8 @@ jobs: release-create: name: Create a New Release runs-on: ubuntu-latest + permissions: + contents: write # For creating the release. needs: validate-tag steps: @@ -55,6 +57,8 @@ jobs: release-doxygen: name: Build and Upload Release Doxygen + permissions: + contents: write needs: - validate-tag - release-create @@ -72,6 +76,8 @@ jobs: release-binaries: name: Build Release Binaries + permissions: + contents: write needs: - validate-tag - release-create diff --git a/.github/workflows/set-release-binary-outputs.sh b/.github/workflows/set-release-binary-outputs.sh index 59470cf83ba75525f543230a94f2340ca11f32e0..14d0798364e914124cdaf46813e9a4c5dae652ec 100644 --- a/.github/workflows/set-release-binary-outputs.sh +++ b/.github/workflows/set-release-binary-outputs.sh @@ -15,10 +15,8 @@ if echo $tag | grep -e '^[0-9a-f]\+$'; then # This is a plain commit. # TODO: Don't hardcode this. release_version="18" - build_dir="$tag" upload='false' ref="$tag" - flags="-git-ref $tag -test-asserts" else @@ -30,12 +28,7 @@ else fi release_version=`echo "$tag" | sed 's/llvmorg-//g'` release=`echo "$release_version" | sed 's/-.*//g'` - build_dir=`echo "$release_version" | sed 's,^[^-]\+,final,' | sed 's,[^-]\+-rc\(.\+\),rc\1,'` - rc_flags=`echo "$release_version" | sed 's,^[^-]\+,-final,' | sed 's,[^-]\+-rc\(.\+\),-rc \1 -test-asserts,' | sed 's,--,-,'` - flags="-release $release $rc_flags" fi echo "release-version=$release_version" >> $GITHUB_OUTPUT -echo "build-dir=$build_dir" >> $GITHUB_OUTPUT -echo "flags=$flags" >> $GITHUB_OUTPUT echo "upload=$upload" >> $GITHUB_OUTPUT echo "ref=$tag" >> $GITHUB_OUTPUT diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index 8b1af9e8153925760985efcb954e87a046b2c1f2..75765819ac464eb83bec5d5643d50195ee07a88e 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -20,6 +20,7 @@ #include "bolt/Core/JumpTable.h" #include "bolt/Core/MCPlusBuilder.h" #include "bolt/RuntimeLibs/RuntimeLibrary.h" +#include "llvm/ADT/AddressRanges.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/iterator.h" @@ -726,6 +727,9 @@ public: uint64_t OldTextSectionOffset{0}; uint64_t OldTextSectionSize{0}; + /// Area in the input binary reserved for BOLT. + AddressRange BOLTReserved; + /// Address of the code/function that is executed before any other code in /// the binary. std::optional StartFunctionAddress; diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h index 26d2d01f86267127f4c4bea9daa5862b4b412738..3c641581e247a84b918f975b1a8524c2a9401b7b 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -930,6 +930,8 @@ public: return const_cast(this)->getInstructionAtOffset(Offset); } + std::optional disassembleInstructionAtOffset(uint64_t Offset) const; + /// Return offset for the first instruction. If there is data at the /// beginning of a function then offset of the first instruction could /// be different from 0 diff --git a/bolt/include/bolt/Passes/FrameAnalysis.h b/bolt/include/bolt/Passes/FrameAnalysis.h index 66246bd6647bb3202a2778b63efe9de27adf08af..44b54d4ed45d4d7a84914f811ca21618fe0a87dc 100644 --- a/bolt/include/bolt/Passes/FrameAnalysis.h +++ b/bolt/include/bolt/Passes/FrameAnalysis.h @@ -170,10 +170,6 @@ class FrameAnalysis { std::unique_ptr> SPTMap; - /// A vector that stores ids of the allocators that are used in SPT - /// computation - std::vector SPTAllocatorsId; - public: explicit FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG); diff --git a/bolt/include/bolt/Passes/IndirectCallPromotion.h b/bolt/include/bolt/Passes/IndirectCallPromotion.h index adc58d70ec0f4d9e5bb09765d1ce863c0bdf15df..8ec160b867cf8ce143b0227e7e3ef0c1adfadf0a 100644 --- a/bolt/include/bolt/Passes/IndirectCallPromotion.h +++ b/bolt/include/bolt/Passes/IndirectCallPromotion.h @@ -104,7 +104,7 @@ class IndirectCallPromotion : public BinaryFunctionPass { struct Location { MCSymbol *Sym{nullptr}; uint64_t Addr{0}; - bool isValid() const { return Sym || (!Sym && Addr != 0); } + bool isValid() const { return Sym || Addr != 0; } Location() {} explicit Location(MCSymbol *Sym) : Sym(Sym) {} explicit Location(uint64_t Addr) : Addr(Addr) {} diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h index 2c482bd2b9ea965ff1bc289ead744bfe3f19737d..12e0813d089d14a23fe96cbd95fc9c03918e4693 100644 --- a/bolt/include/bolt/Rewrite/DWARFRewriter.h +++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h @@ -177,13 +177,6 @@ private: DIEValue &HighPCAttrInfo, std::optional RangesBase = std::nullopt); - /// Adds a \p Str to .debug_str section. - /// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using - /// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets - /// for this contribution of \p Unit. - void addStringHelper(DIEBuilder &DIEBldr, DIE &Die, const DWARFUnit &Unit, - DIEValue &DIEAttrInfo, StringRef Str); - public: DWARFRewriter(BinaryContext &BC) : BC(BC) {} diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h index 41a92e7ba01e8495bc0a6b4329017d2f1e2c40fd..64113bd026012e8a66c29c0442039348baf71385 100644 --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -97,6 +97,10 @@ private: /// from meta data in the file. void discoverFileObjects(); + /// Check if the input binary has a space reserved for BOLT and use it for new + /// section allocations if found. + void discoverBOLTReserved(); + /// Check whether we should use DT_FINI or DT_FINI_ARRAY for instrumentation. /// DT_FINI is preferred; DT_FINI_ARRAY is only used when no DT_FINI entry was /// found. diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index 1fa96dfaabde819eeb3ff70e5340cc46af5ed49a..de34421ebeb08486b2cd4e50dd01f635ebb4d576 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -1167,6 +1167,21 @@ void BinaryFunction::handleAArch64IndirectCall(MCInst &Instruction, } } +std::optional +BinaryFunction::disassembleInstructionAtOffset(uint64_t Offset) const { + assert(CurrentState == State::Empty && "Function should not be disassembled"); + assert(Offset < MaxSize && "Invalid offset"); + ErrorOr> FunctionData = getData(); + assert(FunctionData && "Cannot get function as data"); + MCInst Instr; + uint64_t InstrSize = 0; + const uint64_t InstrAddress = getAddress() + Offset; + if (BC.DisAsm->getInstruction(Instr, InstrSize, FunctionData->slice(Offset), + InstrAddress, nulls())) + return Instr; + return std::nullopt; +} + Error BinaryFunction::disassemble() { NamedRegionTimer T("disassemble", "Disassemble function", "buildfuncs", "Build Binary Functions", opts::TimeBuild); diff --git a/bolt/lib/Core/ParallelUtilities.cpp b/bolt/lib/Core/ParallelUtilities.cpp index 5f5e96e0e7881c56eedd33db06782f9ad9bdf301..a24c37c06f1ac1ce4d0b49e697b8e876864cd81b 100644 --- a/bolt/lib/Core/ParallelUtilities.cpp +++ b/bolt/lib/Core/ParallelUtilities.cpp @@ -188,8 +188,20 @@ void runOnEachFunctionWithUniqueAllocId( LLVM_DEBUG(T.stopTimer()); }; + unsigned AllocId = 1; + auto EnsureAllocatorExists = [&BC](unsigned AllocId) { + if (!BC.MIB->checkAllocatorExists(AllocId)) { + MCPlusBuilder::AllocatorIdTy Id = + BC.MIB->initializeNewAnnotationAllocator(); + (void)Id; + assert(AllocId == Id && "unexpected allocator id created"); + } + }; + if (opts::NoThreads || ForceSequential) { - runBlock(BC.getBinaryFunctions().begin(), BC.getBinaryFunctions().end(), 0); + EnsureAllocatorExists(AllocId); + runBlock(BC.getBinaryFunctions().begin(), BC.getBinaryFunctions().end(), + AllocId); return; } // This lock is used to postpone task execution @@ -205,19 +217,13 @@ void runOnEachFunctionWithUniqueAllocId( ThreadPoolInterface &Pool = getThreadPool(); auto BlockBegin = BC.getBinaryFunctions().begin(); unsigned CurrentCost = 0; - unsigned AllocId = 1; for (auto It = BC.getBinaryFunctions().begin(); It != BC.getBinaryFunctions().end(); ++It) { BinaryFunction &BF = It->second; CurrentCost += computeCostFor(BF, SkipPredicate, SchedPolicy); if (CurrentCost >= BlockCost) { - if (!BC.MIB->checkAllocatorExists(AllocId)) { - MCPlusBuilder::AllocatorIdTy Id = - BC.MIB->initializeNewAnnotationAllocator(); - (void)Id; - assert(AllocId == Id && "unexpected allocator id created"); - } + EnsureAllocatorExists(AllocId); Pool.async(runBlock, BlockBegin, std::next(It), AllocId); AllocId++; BlockBegin = std::next(It); @@ -225,12 +231,7 @@ void runOnEachFunctionWithUniqueAllocId( } } - if (!BC.MIB->checkAllocatorExists(AllocId)) { - MCPlusBuilder::AllocatorIdTy Id = - BC.MIB->initializeNewAnnotationAllocator(); - (void)Id; - assert(AllocId == Id && "unexpected allocator id created"); - } + EnsureAllocatorExists(AllocId); Pool.async(runBlock, BlockBegin, BC.getBinaryFunctions().end(), AllocId); Lock.unlock(); diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index c0ba73108f5778b4e385560c2129e6ef3bb056c8..df6dbcddeed56a972e59e1b17d897a7f17644c75 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -910,6 +910,11 @@ uint64_t SimplifyConditionalTailCalls::fixTailCalls(BinaryFunction &BF) { auto &CTCAnnotation = MIB->getOrCreateAnnotationAs(*CondBranch, "CTCTakenCount"); CTCAnnotation = CTCTakenFreq; + // Preserve Offset annotation, used in BAT. + // Instr is a direct tail call instruction that was created when CTCs are + // first expanded, and has the original CTC offset set. + if (std::optional Offset = MIB->getOffset(*Instr)) + MIB->setOffset(*CondBranch, *Offset); // Remove the unused successor which may be eliminated later // if there are no other users. diff --git a/bolt/lib/Passes/FrameAnalysis.cpp b/bolt/lib/Passes/FrameAnalysis.cpp index 7f1245e39f567b933ade802d06597c09f6edfe56..4ebfd8f158f7f56dbf632974f74cbd079f9cd265 100644 --- a/bolt/lib/Passes/FrameAnalysis.cpp +++ b/bolt/lib/Passes/FrameAnalysis.cpp @@ -561,11 +561,6 @@ FrameAnalysis::FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG) NamedRegionTimer T1("clearspt", "clear spt", "FA", "FA breakdown", opts::TimeFA); clearSPTMap(); - - // Clean up memory allocated for annotation values - if (!opts::NoThreads) - for (MCPlusBuilder::AllocatorIdTy Id : SPTAllocatorsId) - BC.MIB->freeValuesAllocator(Id); } } diff --git a/bolt/lib/Passes/SplitFunctions.cpp b/bolt/lib/Passes/SplitFunctions.cpp index f9e634d15a97244c58adf185c0ea4ec80017079c..bd0b6dea0e065a0b9c723523692761f2c365977e 100644 --- a/bolt/lib/Passes/SplitFunctions.cpp +++ b/bolt/lib/Passes/SplitFunctions.cpp @@ -715,6 +715,12 @@ Error SplitFunctions::runOnFunctions(BinaryContext &BC) { if (!opts::SplitFunctions) return Error::success(); + if (BC.IsLinuxKernel && BC.BOLTReserved.empty()) { + BC.errs() << "BOLT-ERROR: split functions require reserved space in the " + "Linux kernel binary\n"; + exit(1); + } + // If split strategy is not CDSplit, then a second run of the pass is not // needed after function reordering. if (BC.HasFinalizedFunctionOrder && @@ -829,6 +835,13 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { } } } + + // Outlining blocks with dynamic branches is not supported yet. + if (BC.IsLinuxKernel) { + if (llvm::any_of( + *BB, [&](MCInst &Inst) { return BC.MIB->isDynamicBranch(Inst); })) + BB->setCanOutline(false); + } } BF.getLayout().updateLayoutIndices(); diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index 5108392c824c1073638c4dcbb4b870b61dc8d5f4..302bcf1f2d87d931aae5cfd476f69140f8276e2a 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" @@ -773,9 +774,19 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc, bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count, uint64_t Mispreds) { + bool IsReturn = false; auto handleAddress = [&](uint64_t &Addr, bool IsFrom) -> BinaryFunction * { if (BinaryFunction *Func = getBinaryFunctionContainingAddress(Addr)) { Addr -= Func->getAddress(); + if (IsFrom) { + auto checkReturn = [&](auto MaybeInst) { + IsReturn = MaybeInst && BC->MIB->isReturn(*MaybeInst); + }; + if (Func->hasInstructions()) + checkReturn(Func->getInstructionAtOffset(Addr)); + else + checkReturn(Func->disassembleInstructionAtOffset(Addr)); + } if (BAT) Addr = BAT->translate(Func->getAddress(), Addr, IsFrom); @@ -792,6 +803,9 @@ bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count, }; BinaryFunction *FromFunc = handleAddress(From, /*IsFrom=*/true); + // Ignore returns. + if (IsReturn) + return true; BinaryFunction *ToFunc = handleAddress(To, /*IsFrom=*/false); if (!FromFunc && !ToFunc) return false; @@ -1986,7 +2000,7 @@ std::error_code DataAggregator::parseMMapEvents() { std::pair FileMMapInfo = FileMMapInfoRes.get(); if (FileMMapInfo.second.PID == -1) continue; - if (FileMMapInfo.first.equals("(deleted)")) + if (FileMMapInfo.first == "(deleted)") continue; // Consider only the first mapping of the file for any given PID @@ -2326,7 +2340,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, continue; BinaryFunction *BF = BC.getBinaryFunctionAtAddress(FuncAddress); assert(BF); - YamlBF.Name = FuncName.str(); + YamlBF.Name = getLocationName(*BF); YamlBF.Id = BF->getFunctionNumber(); YamlBF.Hash = BAT->getBFHash(FuncAddress); YamlBF.ExecCount = BF->getKnownExecutionCount(); @@ -2365,10 +2379,19 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, return CSI; }; + // Lookup containing basic block offset and index + auto getBlock = [&BlockMap](uint32_t Offset) { + auto BlockIt = BlockMap.upper_bound(Offset); + if (LLVM_UNLIKELY(BlockIt == BlockMap.begin())) { + errs() << "BOLT-ERROR: invalid BAT section\n"; + exit(1); + } + --BlockIt; + return std::pair(BlockIt->first, BlockIt->second.getBBIndex()); + }; + for (const auto &[FromOffset, SuccKV] : Branches.IntraIndex) { - if (!BlockMap.isInputBlock(FromOffset)) - continue; - const unsigned Index = BlockMap.getBBIndex(FromOffset); + const auto &[_, Index] = getBlock(FromOffset); yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[Index]; for (const auto &[SuccOffset, SuccDataIdx] : SuccKV) if (BlockMap.isInputBlock(SuccOffset)) @@ -2376,10 +2399,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, getSuccessorInfo(SuccOffset, SuccDataIdx)); } for (const auto &[FromOffset, CallTo] : Branches.InterIndex) { - auto BlockIt = BlockMap.upper_bound(FromOffset); - --BlockIt; - const unsigned BlockOffset = BlockIt->first; - const unsigned BlockIndex = BlockIt->second.getBBIndex(); + const auto &[BlockOffset, BlockIndex] = getBlock(FromOffset); yaml::bolt::BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex]; const uint32_t Offset = FromOffset - BlockOffset; for (const auto &[CallToLoc, CallToIdx] : CallTo) @@ -2390,6 +2410,17 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, return A.Offset < B.Offset; }); } + // Set entry counts, similar to DataReader::readProfile. + for (const llvm::bolt::BranchInfo &BI : Branches.EntryData) { + if (!BlockMap.isInputBlock(BI.To.Offset)) { + if (opts::Verbosity >= 1) + errs() << "BOLT-WARNING: Unexpected EntryData in " << FuncName + << " at 0x" << Twine::utohexstr(BI.To.Offset) << '\n'; + continue; + } + const unsigned BlockIndex = BlockMap.getBBIndex(BI.To.Offset); + YamlBF.Blocks[BlockIndex].ExecCount += BI.Branches; + } // Drop blocks without a hash, won't be useful for stale matching. llvm::erase_if(YamlBF.Blocks, [](const yaml::bolt::BinaryBasicBlockProfile &YamlBB) { diff --git a/bolt/lib/Profile/DataReader.cpp b/bolt/lib/Profile/DataReader.cpp index 67f357fe4d3f0c42f704a52ca163e328652e7136..b2511ba1039989d3a062544df8681f7df3a1d199 100644 --- a/bolt/lib/Profile/DataReader.cpp +++ b/bolt/lib/Profile/DataReader.cpp @@ -1205,8 +1205,7 @@ std::error_code DataReader::parse() { // Add entry data for branches to another function or branches // to entry points (including recursive calls) - if (BI.To.IsSymbol && - (!BI.From.Name.equals(BI.To.Name) || BI.To.Offset == 0)) { + if (BI.To.IsSymbol && (BI.From.Name != BI.To.Name || BI.To.Offset == 0)) { I = GetOrCreateFuncEntry(BI.To.Name); I->second.EntryData.emplace_back(std::move(BI)); } diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index feeba89a40dc4d03c48b3836c0e27fa5f68903d7..9d4297f913f3a7ada11fffd29d53ae8d4867fe60 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -582,19 +582,51 @@ static void emitDWOBuilder(const std::string &DWOName, Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter); } -void DWARFRewriter::addStringHelper(DIEBuilder &DIEBldr, DIE &Die, - const DWARFUnit &Unit, - DIEValue &DIEAttrInfo, StringRef Str) { - uint32_t NewOffset = StrWriter->addString(Str); +/// Adds a \p Str to .debug_str section. +/// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using +/// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets +/// for this contribution of \p Unit. +static void addStringHelper(DebugStrOffsetsWriter &StrOffstsWriter, + DebugStrWriter &StrWriter, DIEBuilder &DIEBldr, + DIE &Die, const DWARFUnit &Unit, + DIEValue &DIEAttrInfo, StringRef Str) { + uint32_t NewOffset = StrWriter.addString(Str); if (Unit.getVersion() >= 5) { - StrOffstsWriter->updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(), - NewOffset); + StrOffstsWriter.updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(), + NewOffset); return; } DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(), DIEInteger(NewOffset)); } +static std::string +updateDWONameCompDir(DebugStrOffsetsWriter &StrOffstsWriter, + DebugStrWriter &StrWriter, + std::unordered_map &NameToIndexMap, + DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &UnitDIE) { + DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name); + if (!DWONameAttrInfo) + DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name); + assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU."); + std::string ObjectName; + + ObjectName = getDWOName(Unit, NameToIndexMap); + addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit, + DWONameAttrInfo, ObjectName.c_str()); + + DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir); + assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU."); + + if (!opts::DwarfOutputPath.empty()) { + if (!sys::fs::exists(opts::DwarfOutputPath)) + sys::fs::create_directory(opts::DwarfOutputPath); + addStringHelper(StrOffstsWriter, StrWriter, DIEBldr, UnitDIE, Unit, + CompDirAttrInfo, opts::DwarfOutputPath.c_str()); + } + return ObjectName; +} + using DWARFUnitVec = std::vector; using CUPartitionVector = std::vector; /// Partitions CUs in to buckets. Bucket size is controlled by @@ -692,33 +724,6 @@ void DWARFRewriter::updateDebugInfo() { // specified. std::unordered_map NameToIndexMap; - auto updateDWONameCompDir = [&](DWARFUnit &Unit, DIEBuilder &DIEBldr, - DIE &UnitDIE) -> std::string { - DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name); - if (!DWONameAttrInfo) - DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name); - assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU."); - std::string ObjectName; - - { - std::lock_guard Lock(AccessMutex); - ObjectName = getDWOName(Unit, NameToIndexMap); - } - addStringHelper(DIEBldr, UnitDIE, Unit, DWONameAttrInfo, - ObjectName.c_str()); - - DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir); - assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU."); - - if (!opts::DwarfOutputPath.empty()) { - if (!sys::fs::exists(opts::DwarfOutputPath)) - sys::fs::create_directory(opts::DwarfOutputPath); - addStringHelper(DIEBldr, UnitDIE, Unit, CompDirAttrInfo, - opts::DwarfOutputPath.c_str()); - } - return ObjectName; - }; - DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC, *StrWriter); DWPState State; @@ -741,8 +746,13 @@ void DWARFRewriter::updateDebugInfo() { DIEBuilder DWODIEBuilder(BC, &(*SplitCU)->getContext(), DebugNamesTable, Unit); DWODIEBuilder.buildDWOUnit(**SplitCU); - std::string DWOName = updateDWONameCompDir( - *Unit, *DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit)); + std::string DWOName = ""; + { + std::lock_guard Lock(AccessMutex); + DWOName = updateDWONameCompDir(*StrOffstsWriter, *StrWriter, + NameToIndexMap, *Unit, *DIEBlder, + *DIEBlder->getUnitDIEbyUnit(*Unit)); + } DebugLoclistWriter DebugLocDWoWriter(*Unit, Unit->getVersion(), true); DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter; @@ -1540,7 +1550,7 @@ CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder, for (const SectionRef &Section : Obj->sections()) { StringRef Contents = cantFail(Section.getContents()); StringRef Name = cantFail(Section.getName()); - if (Name.equals(".debug_types")) + if (Name == ".debug_types") BC.registerOrUpdateNoteSection(".debug_types", copyByteArray(Contents), Contents.size()); } @@ -1623,10 +1633,10 @@ void DWARFRewriter::finalizeDebugSections( for (const SectionRef &Secs : Obj->sections()) { StringRef Contents = cantFail(Secs.getContents()); StringRef Name = cantFail(Secs.getName()); - if (Name.equals(".debug_abbrev")) { + if (Name == ".debug_abbrev") { BC.registerOrUpdateNoteSection(".debug_abbrev", copyByteArray(Contents), Contents.size()); - } else if (Name.equals(".debug_info")) { + } else if (Name == ".debug_info") { BC.registerOrUpdateNoteSection(".debug_info", copyByteArray(Contents), Contents.size()); } @@ -1761,7 +1771,7 @@ std::optional updateDebugData( }; switch (SectionIter->second.second) { default: { - if (!SectionName.equals("debug_str.dwo")) + if (SectionName != "debug_str.dwo") errs() << "BOLT-WARNING: unsupported debug section: " << SectionName << "\n"; return SectionContents; @@ -1949,7 +1959,7 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU, continue; } - if (SectionName.equals("debug_str.dwo")) { + if (SectionName == "debug_str.dwo") { CurStrSection = OutData; } else { // Since handleDebugDataPatching returned true, we already know this is diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index 17077b4fa2487adfaf2c05a514fb48c750751d38..99775ccfe38d30f427915d3f015397cc37726756 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -62,6 +62,11 @@ static cl::opt cl::desc("dump Linux kernel PCI fixup table"), cl::init(false), cl::Hidden, cl::cat(BoltCategory)); +static cl::opt DumpSMPLocks("dump-smp-locks", + cl::desc("dump Linux kernel SMP locks"), + cl::init(false), cl::Hidden, + cl::cat(BoltCategory)); + static cl::opt DumpStaticCalls("dump-static-calls", cl::desc("dump Linux kernel static calls"), cl::init(false), cl::Hidden, @@ -119,19 +124,18 @@ inline raw_ostream &operator<<(raw_ostream &OS, const ORCState &E) { namespace { class LinuxKernelRewriter final : public MetadataRewriter { - /// Linux Kernel special sections point to a specific instruction in many - /// cases. Unlike SDTMarkerInfo, these markers can come from different - /// sections. - struct LKInstructionMarkerInfo { - uint64_t SectionOffset; - int32_t PCRelativeOffset; - bool IsPCRelative; - StringRef SectionName; + /// Information required for updating metadata referencing an instruction. + struct InstructionFixup { + BinarySection &Section; // Section referencing the instruction. + uint64_t Offset; // Offset in the section above. + BinaryFunction &BF; // Function containing the instruction. + MCSymbol &Label; // Label marking the instruction. + bool IsPCRelative; // If the reference type is relative. }; + std::vector Fixups; - /// Map linux kernel program locations/instructions to their pointers in - /// special linux kernel sections - std::unordered_map> LKMarkers; + /// Size of an entry in .smp_locks section. + static constexpr size_t SMP_LOCKS_ENTRY_SIZE = 4; /// Linux ORC sections. ErrorOr ORCUnwindSection = std::errc::bad_address; @@ -221,23 +225,20 @@ class LinuxKernelRewriter final : public MetadataRewriter { ErrorOr PCIFixupSection = std::errc::bad_address; static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16; - /// Insert an LKMarker for a given code pointer \p PC from a non-code section - /// \p SectionName. - void insertLKMarker(uint64_t PC, uint64_t SectionOffset, - int32_t PCRelativeOffset, bool IsPCRelative, - StringRef SectionName); - /// Process linux kernel special sections and their relocations. void processLKSections(); /// Process __ksymtab and __ksymtab_gpl. void processLKKSymtab(bool IsGPL = false); - /// Process special linux kernel section, .smp_locks. - void processLKSMPLocks(); + // Create relocations in sections requiring fixups. + // + // Make sure functions that will not be emitted are marked as such before this + // function is executed. + void processInstructionFixups(); - /// Update LKMarkers' locations for the output binary. - void updateLKMarkers(); + /// Process .smp_locks section. + Error processSMPLocks(); /// Read ORC unwind information and annotate instructions. Error readORCTables(); @@ -282,16 +283,14 @@ class LinuxKernelRewriter final : public MetadataRewriter { Error rewriteStaticKeysJumpTable(); Error updateStaticKeysJumpTablePostEmit(); - /// Mark instructions referenced by kernel metadata. - Error markInstructions(); - public: LinuxKernelRewriter(BinaryContext &BC) : MetadataRewriter("linux-kernel-rewriter", BC) {} Error preCFGInitializer() override { processLKSections(); - if (Error E = markInstructions()) + + if (Error E = processSMPLocks()) return E; if (Error E = readORCTables()) @@ -352,12 +351,12 @@ public: if (Error E = rewriteBugTable()) return E; + processInstructionFixups(); + return Error::success(); } Error postEmitFinalizer() override { - updateLKMarkers(); - if (Error E = updateStaticKeysJumpTablePostEmit()) return E; @@ -368,39 +367,9 @@ public: } }; -Error LinuxKernelRewriter::markInstructions() { - for (const uint64_t PC : llvm::make_first_range(LKMarkers)) { - BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(PC); - - if (!BF || !BC.shouldEmit(*BF)) - continue; - - const uint64_t Offset = PC - BF->getAddress(); - MCInst *Inst = BF->getInstructionAtOffset(Offset); - if (!Inst) - return createStringError(errc::executable_format_error, - "no instruction matches kernel marker offset"); - - BC.MIB->setOffset(*Inst, static_cast(Offset)); - - BF->setHasSDTMarker(true); - } - - return Error::success(); -} - -void LinuxKernelRewriter::insertLKMarker(uint64_t PC, uint64_t SectionOffset, - int32_t PCRelativeOffset, - bool IsPCRelative, - StringRef SectionName) { - LKMarkers[PC].emplace_back(LKInstructionMarkerInfo{ - SectionOffset, PCRelativeOffset, IsPCRelative, SectionName}); -} - void LinuxKernelRewriter::processLKSections() { processLKKSymtab(); processLKKSymtab(true); - processLKSMPLocks(); } /// Process __ksymtab[_gpl] sections of Linux Kernel. @@ -439,79 +408,73 @@ void LinuxKernelRewriter::processLKKSymtab(bool IsGPL) { /// .smp_locks section contains PC-relative references to instructions with LOCK /// prefix. The prefix can be converted to NOP at boot time on non-SMP systems. -void LinuxKernelRewriter::processLKSMPLocks() { - ErrorOr SectionOrError = +Error LinuxKernelRewriter::processSMPLocks() { + ErrorOr SMPLocksSection = BC.getUniqueSectionByName(".smp_locks"); - if (!SectionOrError) - return; + if (!SMPLocksSection) + return Error::success(); - uint64_t SectionSize = SectionOrError->getSize(); - const uint64_t SectionAddress = SectionOrError->getAddress(); - assert((SectionSize % 4) == 0 && - "The size of the .smp_locks section should be a multiple of 4"); + const uint64_t SectionSize = SMPLocksSection->getSize(); + const uint64_t SectionAddress = SMPLocksSection->getAddress(); + if (SectionSize % SMP_LOCKS_ENTRY_SIZE) + return createStringError(errc::executable_format_error, + "bad size of .smp_locks section"); - for (uint64_t I = 0; I < SectionSize; I += 4) { - const uint64_t EntryAddress = SectionAddress + I; - ErrorOr Offset = BC.getSignedValueAtAddress(EntryAddress, 4); - assert(Offset && "Reading valid PC-relative offset for a .smp_locks entry"); - int32_t SignedOffset = *Offset; - uint64_t RefAddress = EntryAddress + SignedOffset; + DataExtractor DE = DataExtractor(SMPLocksSection->getContents(), + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + DataExtractor::Cursor Cursor(0); + while (Cursor && Cursor.tell() < SectionSize) { + const uint64_t Offset = Cursor.tell(); + const uint64_t IP = SectionAddress + Offset + (int32_t)DE.getU32(Cursor); + + // Consume the status of the cursor. + if (!Cursor) + return createStringError(errc::executable_format_error, + "error while reading .smp_locks: %s", + toString(Cursor.takeError()).c_str()); + + if (opts::DumpSMPLocks) + BC.outs() << "SMP lock at 0x: " << Twine::utohexstr(IP) << '\n'; - BinaryFunction *ContainingBF = - BC.getBinaryFunctionContainingAddress(RefAddress); - if (!ContainingBF) + BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(IP); + if (!BF || !BC.shouldEmit(*BF)) continue; - insertLKMarker(RefAddress, I, SignedOffset, true, ".smp_locks"); - } -} + MCInst *Inst = BF->getInstructionAtOffset(IP - BF->getAddress()); + if (!Inst) + return createStringError(errc::executable_format_error, + "no instruction matches lock at 0x%" PRIx64, IP); -void LinuxKernelRewriter::updateLKMarkers() { - if (LKMarkers.size() == 0) - return; + // Check for duplicate entries. + if (BC.MIB->hasAnnotation(*Inst, "SMPLock")) + return createStringError(errc::executable_format_error, + "duplicate SMP lock at 0x%" PRIx64, IP); - std::unordered_map PatchCounts; - for (std::pair> - &LKMarkerInfoKV : LKMarkers) { - const uint64_t OriginalAddress = LKMarkerInfoKV.first; - const BinaryFunction *BF = - BC.getBinaryFunctionContainingAddress(OriginalAddress, false, true); - if (!BF) - continue; + BC.MIB->addAnnotation(*Inst, "SMPLock", true); + MCSymbol *Label = + BC.MIB->getOrCreateInstLabel(*Inst, "__SMPLock_", BC.Ctx.get()); - uint64_t NewAddress = BF->translateInputToOutputAddress(OriginalAddress); - if (NewAddress == 0) - continue; + Fixups.push_back({*SMPLocksSection, Offset, *BF, *Label, + /*IsPCRelative*/ true}); + } - // Apply base address. - if (OriginalAddress >= 0xffffffff00000000 && NewAddress < 0xffffffff) - NewAddress = NewAddress + 0xffffffff00000000; + const uint64_t NumEntries = SectionSize / SMP_LOCKS_ENTRY_SIZE; + BC.outs() << "BOLT-INFO: parsed " << NumEntries << " SMP lock entries\n"; - if (OriginalAddress == NewAddress) + return Error::success(); +} + +void LinuxKernelRewriter::processInstructionFixups() { + for (InstructionFixup &Fixup : Fixups) { + if (!BC.shouldEmit(Fixup.BF)) continue; - for (LKInstructionMarkerInfo &LKMarkerInfo : LKMarkerInfoKV.second) { - StringRef SectionName = LKMarkerInfo.SectionName; - SimpleBinaryPatcher *LKPatcher; - ErrorOr BSec = BC.getUniqueSectionByName(SectionName); - assert(BSec && "missing section info for kernel section"); - if (!BSec->getPatcher()) - BSec->registerPatcher(std::make_unique()); - LKPatcher = static_cast(BSec->getPatcher()); - PatchCounts[std::string(SectionName)]++; - if (LKMarkerInfo.IsPCRelative) - LKPatcher->addLE32Patch(LKMarkerInfo.SectionOffset, - NewAddress - OriginalAddress + - LKMarkerInfo.PCRelativeOffset); - else - LKPatcher->addLE64Patch(LKMarkerInfo.SectionOffset, NewAddress); - } + Fixup.Section.addRelocation(Fixup.Offset, &Fixup.Label, + Fixup.IsPCRelative ? ELF::R_X86_64_PC32 + : ELF::R_X86_64_64, + /*Addend*/ 0); } - BC.outs() << "BOLT-INFO: patching linux kernel sections. Total patches per " - "section are as follows:\n"; - for (const std::pair &KV : PatchCounts) - BC.outs() << " Section: " << KV.first << ", patch-counts: " << KV.second - << '\n'; } Error LinuxKernelRewriter::readORCTables() { @@ -783,11 +746,9 @@ Error LinuxKernelRewriter::rewriteORCTables() { }; // Emit new ORC entries for the emitted function. - auto emitORC = [&](const BinaryFunction &BF) -> Error { - assert(!BF.isSplit() && "Split functions not supported by ORC writer yet."); - + auto emitORC = [&](const FunctionFragment &FF) -> Error { ORCState CurrentState = NullORC; - for (BinaryBasicBlock *BB : BF.getLayout().blocks()) { + for (BinaryBasicBlock *BB : FF) { for (MCInst &Inst : *BB) { ErrorOr ErrorOrState = BC.MIB->tryGetAnnotationAs(Inst, "ORC"); @@ -808,7 +769,36 @@ Error LinuxKernelRewriter::rewriteORCTables() { return Error::success(); }; + // Emit ORC entries for cold fragments. We assume that these fragments are + // emitted contiguously in memory using reserved space in the kernel. This + // assumption is validated in post-emit pass validateORCTables() where we + // check that ORC entries are sorted by their addresses. + auto emitColdORC = [&]() -> Error { + for (BinaryFunction &BF : + llvm::make_second_range(BC.getBinaryFunctions())) { + if (!BC.shouldEmit(BF)) + continue; + for (FunctionFragment &FF : BF.getLayout().getSplitFragments()) + if (Error E = emitORC(FF)) + return E; + } + + return Error::success(); + }; + + bool ShouldEmitCold = !BC.BOLTReserved.empty(); for (ORCListEntry &Entry : ORCEntries) { + if (ShouldEmitCold && Entry.IP > BC.BOLTReserved.start()) { + if (Error E = emitColdORC()) + return E; + + // Emit terminator entry at the end of the reserved region. + if (Error E = emitORCEntry(BC.BOLTReserved.end(), NullORC)) + return E; + + ShouldEmitCold = false; + } + // Emit original entries for functions that we haven't modified. if (!Entry.BF || !BC.shouldEmit(*Entry.BF)) { // Emit terminator only if it marks the start of a function. @@ -822,7 +812,7 @@ Error LinuxKernelRewriter::rewriteORCTables() { // Emit all ORC entries for a function referenced by an entry and skip over // the rest of entries for this function by resetting its ORC attribute. if (Entry.BF->hasORC()) { - if (Error E = emitORC(*Entry.BF)) + if (Error E = emitORC(Entry.BF->getLayout().getMainFragment())) return E; Entry.BF->setHasORC(false); } @@ -831,10 +821,9 @@ Error LinuxKernelRewriter::rewriteORCTables() { LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitted " << NumEmitted << " ORC entries\n"); - // Replicate terminator entry at the end of sections to match the original - // table sizes. - const BinaryFunction &LastBF = BC.getBinaryFunctions().rbegin()->second; - const uint64_t LastIP = LastBF.getAddress() + LastBF.getMaxSize(); + // Populate ORC tables with a terminator entry with max address to match the + // original table sizes. + const uint64_t LastIP = std::numeric_limits::max(); while (UnwindWriter.bytesRemaining()) { if (Error E = emitORCEntry(LastIP, NullORC, nullptr, /*Force*/ true)) return E; @@ -1696,6 +1685,9 @@ Error LinuxKernelRewriter::readStaticKeysJumpTable() { if (!BC.MIB->getSize(*Inst)) BC.MIB->setSize(*Inst, Size); + if (!BC.MIB->getOffset(*Inst)) + BC.MIB->setOffset(*Inst, JumpAddress - BF->getAddress()); + if (opts::LongJumpLabels) BC.MIB->setSize(*Inst, 5); } diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 23f79e3c135a78f7b7f82e25d91c8987f31775b6..85b39176754b64187b37542f8bb866a22f87cbd1 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -1347,6 +1347,35 @@ void RewriteInstance::discoverFileObjects() { registerFragments(); FileSymbols.clear(); + + discoverBOLTReserved(); +} + +void RewriteInstance::discoverBOLTReserved() { + BinaryData *StartBD = BC->getBinaryDataByName(getBOLTReservedStart()); + BinaryData *EndBD = BC->getBinaryDataByName(getBOLTReservedEnd()); + if (!StartBD != !EndBD) { + BC->errs() << "BOLT-ERROR: one of the symbols is missing from the binary: " + << getBOLTReservedStart() << ", " << getBOLTReservedEnd() + << '\n'; + exit(1); + } + + if (!StartBD) + return; + + if (StartBD->getAddress() >= EndBD->getAddress()) { + BC->errs() << "BOLT-ERROR: invalid reserved space boundaries\n"; + exit(1); + } + BC->BOLTReserved = AddressRange(StartBD->getAddress(), EndBD->getAddress()); + BC->outs() << "BOLT-INFO: using reserved space for allocating new sections\n"; + + PHDRTableOffset = 0; + PHDRTableAddress = 0; + NewTextSegmentAddress = 0; + NewTextSegmentOffset = 0; + NextAvailableAddress = BC->BOLTReserved.start(); } Error RewriteInstance::discoverRtFiniAddress() { @@ -3617,26 +3646,6 @@ void RewriteInstance::updateMetadata() { void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) { BC->deregisterUnusedSections(); - // Check if the input has a space reserved for BOLT. - BinaryData *StartBD = BC->getBinaryDataByName(getBOLTReservedStart()); - BinaryData *EndBD = BC->getBinaryDataByName(getBOLTReservedEnd()); - if (!StartBD != !EndBD) { - BC->errs() << "BOLT-ERROR: one of the symbols is missing from the binary: " - << getBOLTReservedStart() << ", " << getBOLTReservedEnd() - << '\n'; - exit(1); - } - - if (StartBD) { - PHDRTableOffset = 0; - PHDRTableAddress = 0; - NewTextSegmentAddress = 0; - NewTextSegmentOffset = 0; - NextAvailableAddress = StartBD->getAddress(); - BC->outs() - << "BOLT-INFO: using reserved space for allocating new sections\n"; - } - // If no new .eh_frame was written, remove relocated original .eh_frame. BinarySection *RelocatedEHFrameSection = getSection(".relocated" + getEHFrameSectionName()); @@ -3657,12 +3666,12 @@ void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) { // Map the rest of the sections. mapAllocatableSections(MapSection); - if (StartBD) { - const uint64_t ReservedSpace = EndBD->getAddress() - StartBD->getAddress(); - const uint64_t AllocatedSize = NextAvailableAddress - StartBD->getAddress(); - if (ReservedSpace < AllocatedSize) { - BC->errs() << "BOLT-ERROR: reserved space (" << ReservedSpace << " byte" - << (ReservedSpace == 1 ? "" : "s") + if (!BC->BOLTReserved.empty()) { + const uint64_t AllocatedSize = + NextAvailableAddress - BC->BOLTReserved.start(); + if (BC->BOLTReserved.size() < AllocatedSize) { + BC->errs() << "BOLT-ERROR: reserved space (" << BC->BOLTReserved.size() + << " byte" << (BC->BOLTReserved.size() == 1 ? "" : "s") << ") is smaller than required for new allocations (" << AllocatedSize << " bytes)\n"; exit(1); @@ -5852,13 +5861,11 @@ void RewriteInstance::writeEHFrameHeader() { NextAvailableAddress += EHFrameHdrSec.getOutputSize(); - if (const BinaryData *ReservedEnd = - BC->getBinaryDataByName(getBOLTReservedEnd())) { - if (NextAvailableAddress > ReservedEnd->getAddress()) { - BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() - << " into reserved space\n"; - exit(1); - } + if (!BC->BOLTReserved.empty() && + (NextAvailableAddress > BC->BOLTReserved.end())) { + BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName() + << " into reserved space\n"; + exit(1); } // Merge new .eh_frame with the relocated original so that gdb can locate all @@ -5892,7 +5899,7 @@ uint64_t RewriteInstance::getNewValueForSymbol(const StringRef Name) { uint64_t RewriteInstance::getFileOffsetForAddress(uint64_t Address) const { // Check if it's possibly part of the new segment. - if (Address >= NewTextSegmentAddress) + if (NewTextSegmentAddress && Address >= NewTextSegmentAddress) return Address - NewTextSegmentAddress + NewTextSegmentOffset; // Find an existing segment that matches the address. diff --git a/bolt/lib/Rewrite/SDTRewriter.cpp b/bolt/lib/Rewrite/SDTRewriter.cpp index cc663b28990f8efbc0af0287184a9aff40b63a0e..a3928c554ad66c2c65b6cd3b39c4f070ef37e862 100644 --- a/bolt/lib/Rewrite/SDTRewriter.cpp +++ b/bolt/lib/Rewrite/SDTRewriter.cpp @@ -87,7 +87,7 @@ void SDTRewriter::readSection() { StringRef Name = DE.getCStr(&Offset); - if (!Name.equals("stapsdt")) + if (Name != "stapsdt") errs() << "BOLT-WARNING: SDT note name \"" << Name << "\" is not expected\n"; diff --git a/bolt/test/X86/Inputs/blarge_new_bat_branchentry.preagg.txt b/bolt/test/X86/Inputs/blarge_new_bat_branchentry.preagg.txt new file mode 100644 index 0000000000000000000000000000000000000000..546da92f94dbae31f997def130516d8a9d793c42 --- /dev/null +++ b/bolt/test/X86/Inputs/blarge_new_bat_branchentry.preagg.txt @@ -0,0 +1 @@ +B 80010c 800194 1 0 diff --git a/bolt/test/X86/Inputs/jump-table-fixed-ref-pic.s b/bolt/test/X86/Inputs/jump-table-fixed-ref-pic.s new file mode 100644 index 0000000000000000000000000000000000000000..66629a4880e6433c8f1cbdf5af4d6c93574df026 --- /dev/null +++ b/bolt/test/X86/Inputs/jump-table-fixed-ref-pic.s @@ -0,0 +1,35 @@ + .globl main + .type main, %function +main: + .cfi_startproc + cmpq $0x3, %rdi + jae .L4 + cmpq $0x1, %rdi + jne .L4 + mov .Ljt_pic+8(%rip), %rax + lea .Ljt_pic(%rip), %rdx + add %rdx, %rax + jmpq *%rax +.L1: + movq $0x1, %rax + jmp .L5 +.L2: + movq $0x0, %rax + jmp .L5 +.L3: + movq $0x2, %rax + jmp .L5 +.L4: + mov $0x3, %rax +.L5: + retq + .cfi_endproc + + .section .rodata + .align 16 +.Ljt_pic: + .long .L1 - .Ljt_pic + .long .L2 - .Ljt_pic + .long .L3 - .Ljt_pic + .long .L4 - .Ljt_pic + diff --git a/bolt/test/X86/bolt-address-translation-yaml.test b/bolt/test/X86/bolt-address-translation-yaml.test index af24c3d84a0f15f9f8d68eabf7b42daac3e39e69..c15d6ce15ed0df6dbc13fe837b7e7d2ffb7650be 100644 --- a/bolt/test/X86/bolt-address-translation-yaml.test +++ b/bolt/test/X86/bolt-address-translation-yaml.test @@ -5,6 +5,17 @@ RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt \ RUN: --reorder-blocks=ext-tsp --split-functions --split-strategy=cdsplit \ RUN: --reorder-functions=cdsort --enable-bat --dyno-stats --skip-funcs=main \ RUN: 2>&1 | FileCheck --check-prefix WRITE-BAT-CHECK %s +# Check that branch with entry in BAT is accounted for. +RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat_branchentry.preagg.txt \ +RUN: -w %t.yaml -o %t.fdata +RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o %t.null +RUN: FileCheck --input-file %t.yaml --check-prefix BRANCHENTRY-YAML-CHECK %s +RUN: FileCheck --input-file %t.yaml-fdata --check-prefix BRANCHENTRY-YAML-CHECK %s +BRANCHENTRY-YAML-CHECK: - name: SolveCubic +BRANCHENTRY-YAML-CHECK: bid: 0 +BRANCHENTRY-YAML-CHECK: hash: 0x700F19D24600000 +BRANCHENTRY-YAML-CHECK-NEXT: succ: [ { bid: 7, cnt: 1 } +# Large profile test RUN: perf2bolt %t.out --pa -p %p/Inputs/blarge_new_bat.preagg.txt -w %t.yaml -o %t.fdata \ RUN: 2>&1 | FileCheck --check-prefix READ-BAT-CHECK %s RUN: FileCheck --input-file %t.yaml --check-prefix YAML-BAT-CHECK %s @@ -13,7 +24,7 @@ RUN: llvm-bolt %t.exe -data %t.fdata -w %t.yaml-fdata -o /dev/null RUN: FileCheck --input-file %t.yaml-fdata --check-prefix YAML-BAT-CHECK %s # Test resulting YAML profile with the original binary (no-stale mode) -RUN: llvm-bolt %t.exe -data %t.yaml -o %t.null -dyno-stats \ +RUN: llvm-bolt %t.exe -data %t.yaml -o %t.null -dyno-stats 2>&1 \ RUN: | FileCheck --check-prefix CHECK-BOLT-YAML %s WRITE-BAT-CHECK: BOLT-INFO: Wrote 5 BAT maps @@ -48,6 +59,10 @@ YAML-BAT-CHECK-NEXT: hash: 0x6AF7E61EA3966722 YAML-BAT-CHECK-NEXT: exec: 25 YAML-BAT-CHECK-NEXT: nblocks: 15 YAML-BAT-CHECK-NEXT: blocks: +YAML-BAT-CHECK-NEXT: - bid: 0 +YAML-BAT-CHECK-NEXT: insns: [[#]] +YAML-BAT-CHECK-NEXT: hash: 0x700F19D24600000 +YAML-BAT-CHECK-NEXT: exec: 25 YAML-BAT-CHECK: - bid: 3 YAML-BAT-CHECK-NEXT: insns: [[#]] YAML-BAT-CHECK-NEXT: hash: 0xDDA1DC5F69F900AC @@ -63,7 +78,8 @@ YAML-BAT-CHECK-NEXT: blocks: YAML-BAT-CHECK: - bid: 1 YAML-BAT-CHECK-NEXT: insns: [[#]] YAML-BAT-CHECK-NEXT: hash: 0xD70DC695320E0010 -YAML-BAT-CHECK-NEXT: succ: {{.*}} { bid: 2, cnt: [[#]] } +YAML-BAT-CHECK-NEXT: succ: {{.*}} { bid: 2, cnt: [[#]] CHECK-BOLT-YAML: pre-processing profile using YAML profile reader CHECK-BOLT-YAML-NEXT: 5 out of 16 functions in the binary (31.2%) have non-empty execution profile +CHECK-BOLT-YAML-NOT: invalid (possibly stale) profile diff --git a/bolt/test/X86/jump-table-fixed-ref-pic.test b/bolt/test/X86/jump-table-fixed-ref-pic.test new file mode 100644 index 0000000000000000000000000000000000000000..4195b97aac501ebc4a77a22f1c3efb601400a529 --- /dev/null +++ b/bolt/test/X86/jump-table-fixed-ref-pic.test @@ -0,0 +1,9 @@ +# Verify that BOLT detects fixed destination of indirect jump for PIC +# case. + +XFAIL: * + +RUN: %clang %cflags -no-pie %S/Inputs/jump-table-fixed-ref-pic.s -Wl,-q -o %t +RUN: llvm-bolt %t --relocs -o %t.null 2>&1 | FileCheck %s + +CHECK: BOLT-INFO: fixed indirect branch detected in main diff --git a/bolt/test/X86/linux-smp-locks.s b/bolt/test/X86/linux-smp-locks.s new file mode 100644 index 0000000000000000000000000000000000000000..5f4410d14fc6b08c38dbd13c1a6b234c88ca1d2b --- /dev/null +++ b/bolt/test/X86/linux-smp-locks.s @@ -0,0 +1,40 @@ +# REQUIRES: system-linux + +## Check that BOLT correctly parses and updates the Linux kernel .smp_locks +## section. + +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o +# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ +# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie +# RUN: llvm-bolt %t.exe --print-normalized --keep-nops=0 --bolt-info=0 -o %t.out \ +# RUN: |& FileCheck %s + +## Check the output of BOLT with NOPs removed. + +# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s + +# CHECK: BOLT-INFO: Linux kernel binary detected +# CHECK: BOLT-INFO: parsed 2 SMP lock entries + + .text + .globl _start + .type _start, %function +_start: + nop + nop +.L0: + lock incl (%rdi) +# CHECK: lock {{.*}} SMPLock +.L1: + lock orb $0x40, 0x4(%rsi) +# CHECK: lock {{.*}} SMPLock + ret + .size _start, .-_start + + .section .smp_locks,"a",@progbits + .long .L0 - . + .long .L1 - . + +## Fake Linux Kernel sections. + .section __ksymtab,"a",@progbits + .section __ksymtab_gpl,"a",@progbits diff --git a/bolt/test/X86/linux-static-keys.s b/bolt/test/X86/linux-static-keys.s index 08454bf9763193d2de46005cd470d38f5661a959..fb419e0f76275590da3b2a72449db8239b104b56 100644 --- a/bolt/test/X86/linux-static-keys.s +++ b/bolt/test/X86/linux-static-keys.s @@ -3,6 +3,8 @@ ## Check that BOLT correctly updates the Linux kernel static keys jump table. # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o +# RUN: link_fdata %s %t.o %t.fdata +# RUN: llvm-strip --strip-unneeded %t.o # RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ # RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr @@ -11,6 +13,12 @@ # RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \ # RUN: --bolt-info=0 |& FileCheck %s +## Verify that profile is matched correctly. + +# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \ +# RUN: --bolt-info=0 --data %t.fdata |& \ +# RUN: FileCheck --check-prefix=CHECK-FDATA %s + ## Verify the bindings again on the rewritten binary with nops removed. # RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s @@ -25,15 +33,24 @@ _start: # CHECK: Binary Function "_start" nop .L0: - jmp .L1 + jmp L1 # CHECK: jit # CHECK-SAME: # ID: 1 {{.*}} # Likely: 0 # InitValue: 1 nop -.L1: +L1: .nops 5 + jmp .L0 # CHECK: jit # CHECK-SAME: # ID: 2 {{.*}} # Likely: 1 # InitValue: 1 -.L2: + +## Check that a branch profile associated with a NOP is handled properly when +## dynamic branch is created. + +# FDATA: 1 _start #L1# 1 _start #L2# 3 42 +# CHECK-FDATA: jit {{.*}} # ID: 2 +# CHECK-FDATA-NEXT: jmp +# CHECK-FDATA-NEXT: Successors: {{.*}} (mispreds: 3, count: 42) +L2: nop .size _start, .-_start @@ -51,11 +68,11 @@ foo: __start___jump_table: .long .L0 - . # Jump address - .long .L1 - . # Target address + .long L1 - . # Target address .quad 1 # Key address - .long .L1 - . # Jump address - .long .L2 - . # Target address + .long L1 - . # Jump address + .long L2 - . # Target address .quad 0 # Key address .globl __stop___jump_table diff --git a/bolt/test/X86/register-fragments-bolt-symbols.s b/bolt/test/X86/register-fragments-bolt-symbols.s index fa9b70e0b2d8919a371b609263e03c1d902547bb..6478adf19372b2938061dd36389330e4b6ebe79d 100644 --- a/bolt/test/X86/register-fragments-bolt-symbols.s +++ b/bolt/test/X86/register-fragments-bolt-symbols.s @@ -15,6 +15,8 @@ # PREAGG: B X:0 #chain.cold.0# 1 0 # RUN: perf2bolt %t.bolt -p %t.preagg --pa -o %t.bat.fdata -w %t.bat.yaml -v=1 \ # RUN: | FileCheck %s --check-prefix=CHECK-REGISTER +# RUN: FileCheck --input-file %t.bat.fdata --check-prefix=CHECK-FDATA %s +# RUN: FileCheck --input-file %t.bat.yaml --check-prefix=CHECK-YAML %s # CHECK-SYMS: l df *ABS* [[#]] chain.s # CHECK-SYMS: l F .bolt.org.text [[#]] chain @@ -24,6 +26,9 @@ # CHECK-REGISTER: BOLT-INFO: marking chain.cold.0/1(*2) as a fragment of chain/2(*2) +# CHECK-FDATA: 0 [unknown] 0 1 chain/chain.s/2 10 0 1 +# CHECK-YAML: - name: 'chain/chain.s/2' + .file "chain.s" .text .type chain, @function diff --git a/bolt/test/X86/sctc-bug4.test b/bolt/test/X86/sctc-bug4.test index 00f5ee429b635e089c9bcf0693ba3a0e99fe6f07..92aca5110059f4a7fd5ef9cc43db9e1c22286c35 100644 --- a/bolt/test/X86/sctc-bug4.test +++ b/bolt/test/X86/sctc-bug4.test @@ -1,20 +1,23 @@ -# Check that fallthrough blocks are handled properly. +# Check that fallthrough blocks are handled properly and Offset annotation is +# set for conditional tail calls. RUN: %clang %cflags %S/Inputs/sctc_bug4.s -o %t -RUN: llvm-bolt %t -o %t.null \ +RUN: llvm-bolt %t -o %t.null --enable-bat \ RUN: -funcs=test_func -print-sctc -sequential-disassembly 2>&1 | FileCheck %s CHECK: .Ltmp2 (3 instructions, align : 1) CHECK-NEXT: CFI State : 0 +CHECK-NEXT: Input offset: 0x24 CHECK-NEXT: Predecessors: .LFT1 CHECK-NEXT: 00000024: cmpq $0x20, %rsi -CHECK-NEXT: 00000028: ja dummy # TAILCALL {{.*}}# CTCTakenCount: 0 +CHECK-NEXT: 00000028: ja dummy # TAILCALL # Offset: 53 # CTCTakenCount: 0 CHECK-NEXT: 0000002a: jmp .Ltmp4 CHECK-NEXT: Successors: .Ltmp4 CHECK-NEXT: CFI State: 0 CHECK: .Ltmp1 (2 instructions, align : 1) CHECK-NEXT: CFI State : 0 +CHECK-NEXT: Input offset: 0x2c CHECK-NEXT: Predecessors: .LFT0 CHECK-NEXT: 0000002c: xorq %r11, %rax CHECK-NEXT: 0000002f: retq @@ -22,4 +25,5 @@ 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/bolt/test/runtime/bolt-reserved.cpp b/bolt/test/runtime/bolt-reserved.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c88b1e284d074ee3371ccfa2f28019dbce4b0daa --- /dev/null +++ b/bolt/test/runtime/bolt-reserved.cpp @@ -0,0 +1,40 @@ +// REQUIRES: system-linux + +/* + * Check that llvm-bolt uses reserved space in a binary for allocating + * new sections. + */ + +// RUN: %clang %s -o %t.exe -Wl,-q +// RUN: llvm-bolt %t.exe -o %t.bolt.exe 2>&1 | FileCheck %s +// RUN: %t.bolt.exe + +// CHECK: BOLT-INFO: using reserved space + +/* + * Check that llvm-bolt detects a condition when the reserved space is + * not enough for allocating new sections. + */ + +// RUN: %clang %s -o %t.tiny.exe -Wl,--no-eh-frame-hdr -Wl,-q -DTINY +// RUN: not llvm-bolt %t.tiny.exe -o %t.tiny.bolt.exe 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-TINY + +// CHECK-TINY: BOLT-ERROR: reserved space (1 byte) is smaller than required + +#ifdef TINY +#define RSIZE "1" +#else +#define RSIZE "8192 * 1024" +#endif + +asm(".pushsection .text \n\ + .globl __bolt_reserved_start \n\ + .type __bolt_reserved_start, @object \n\ + __bolt_reserved_start: \n\ + .space " RSIZE " \n\ + .globl __bolt_reserved_end \n\ + __bolt_reserved_end: \n\ + .popsection"); + +int main() { return 0; } diff --git a/clang-tools-extra/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp b/clang-tools-extra/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp index df77bf7ea46da33cf655438e833b300ce159f0c9..469323f0ee9d7fa24295b0f5df0f6391a10df9a5 100644 --- a/clang-tools-extra/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp +++ b/clang-tools-extra/clang-include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp @@ -15,9 +15,11 @@ const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap() { static const HeaderMapCollector::RegexHeaderMap STLPostfixHeaderMap = { {"include/__stdarg___gnuc_va_list.h$", ""}, {"include/__stdarg___va_copy.h$", ""}, + {"include/__stdarg_header_macro.h$", ""}, {"include/__stdarg_va_arg.h$", ""}, {"include/__stdarg_va_copy.h$", ""}, {"include/__stdarg_va_list.h$", ""}, + {"include/__stddef_header_macro.h$", ""}, {"include/__stddef_max_align_t.h$", ""}, {"include/__stddef_null.h$", ""}, {"include/__stddef_nullptr_t.h$", ""}, diff --git a/clang-tools-extra/clang-query/Query.cpp b/clang-tools-extra/clang-query/Query.cpp index c436d6fa9498688b82ee890b872ce5e2c3595a39..9d5807a52fa8ed4f6612e8ef9f2ce52c3c65cb66 100644 --- a/clang-tools-extra/clang-query/Query.cpp +++ b/clang-tools-extra/clang-query/Query.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Query.h" +#include "QueryParser.h" #include "QuerySession.h" #include "clang/AST/ASTDumper.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -281,5 +282,26 @@ const QueryKind SetQueryKind::value; const QueryKind SetQueryKind::value; #endif +bool FileQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { + auto Buffer = llvm::MemoryBuffer::getFile(StringRef{File}.trim()); + if (!Buffer) { + if (Prefix.has_value()) + llvm::errs() << *Prefix << ": "; + llvm::errs() << "cannot open " << File << ": " + << Buffer.getError().message() << "\n"; + return false; + } + + StringRef FileContentRef(Buffer.get()->getBuffer()); + + while (!FileContentRef.empty()) { + QueryRef Q = QueryParser::parse(FileContentRef, QS); + if (!Q->run(llvm::outs(), QS)) + return false; + FileContentRef = Q->RemainingContent; + } + return true; +} + } // namespace query } // namespace clang diff --git a/clang-tools-extra/clang-query/Query.h b/clang-tools-extra/clang-query/Query.h index 7aefa6bb5ee0dd5a991c294ae6a09b5bce213723..7242479633c24f8e94e75ff4ff8e3600e182d2a6 100644 --- a/clang-tools-extra/clang-query/Query.h +++ b/clang-tools-extra/clang-query/Query.h @@ -30,7 +30,8 @@ enum QueryKind { QK_SetTraversalKind, QK_EnableOutputKind, QK_DisableOutputKind, - QK_Quit + QK_Quit, + QK_File }; class QuerySession; @@ -188,6 +189,21 @@ struct DisableOutputQuery : SetNonExclusiveOutputQuery { } }; +struct FileQuery : Query { + FileQuery(StringRef File, StringRef Prefix = StringRef()) + : Query(QK_File), File(File), + Prefix(!Prefix.empty() ? std::optional(Prefix) + : std::nullopt) {} + + bool run(llvm::raw_ostream &OS, QuerySession &QS) const override; + + static bool classof(const Query *Q) { return Q->Kind == QK_File; } + +private: + std::string File; + std::optional Prefix; +}; + } // namespace query } // namespace clang diff --git a/clang-tools-extra/clang-query/QueryParser.cpp b/clang-tools-extra/clang-query/QueryParser.cpp index 162acc1a598dd58f36f5244abf70312b6e1bcd99..85a442bdd7dedab0318d2d881580401989519c92 100644 --- a/clang-tools-extra/clang-query/QueryParser.cpp +++ b/clang-tools-extra/clang-query/QueryParser.cpp @@ -183,7 +183,8 @@ enum ParsedQueryKind { PQK_Unlet, PQK_Quit, PQK_Enable, - PQK_Disable + PQK_Disable, + PQK_File }; enum ParsedQueryVariable { @@ -222,12 +223,14 @@ QueryRef QueryParser::doParse() { .Case("let", PQK_Let) .Case("m", PQK_Match, /*IsCompletion=*/false) .Case("match", PQK_Match) - .Case("q", PQK_Quit, /*IsCompletion=*/false) + .Case("q", PQK_Quit, /*IsCompletion=*/false) .Case("quit", PQK_Quit) .Case("set", PQK_Set) .Case("enable", PQK_Enable) .Case("disable", PQK_Disable) .Case("unlet", PQK_Unlet) + .Case("f", PQK_File, /*IsCompletion=*/false) + .Case("file", PQK_File) .Default(PQK_Invalid); switch (QKind) { @@ -351,6 +354,9 @@ QueryRef QueryParser::doParse() { return endQuery(new LetQuery(Name, VariantValue())); } + case PQK_File: + return new FileQuery(Line); + case PQK_Invalid: return new InvalidQuery("unknown command: " + CommandStr); } diff --git a/clang-tools-extra/clang-query/tool/ClangQuery.cpp b/clang-tools-extra/clang-query/tool/ClangQuery.cpp index da7ac27014480966c13035bd676acb5825339fcb..a2de7a2dced86e1ccb0f118676a658562f07d3ee 100644 --- a/clang-tools-extra/clang-query/tool/ClangQuery.cpp +++ b/clang-tools-extra/clang-query/tool/ClangQuery.cpp @@ -74,22 +74,8 @@ static cl::opt PreloadFile( bool runCommandsInFile(const char *ExeName, std::string const &FileName, QuerySession &QS) { - auto Buffer = llvm::MemoryBuffer::getFile(FileName); - if (!Buffer) { - llvm::errs() << ExeName << ": cannot open " << FileName << ": " - << Buffer.getError().message() << "\n"; - return true; - } - - StringRef FileContentRef(Buffer.get()->getBuffer()); - - while (!FileContentRef.empty()) { - QueryRef Q = QueryParser::parse(FileContentRef, QS); - if (!Q->run(llvm::outs(), QS)) - return true; - FileContentRef = Q->RemainingContent; - } - return false; + FileQuery Query(FileName, ExeName); + return !Query.run(llvm::errs(), QS); } int main(int argc, const char **argv) { diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index b877ea06dc05cd78b5536155efb25afa4ae32906..1cd7cdd10bc25f2402bb3d71692b017644cf41f6 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -373,11 +373,11 @@ static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context, const auto &RegisteredCheckers = AnalyzerOptions::getRegisteredCheckers(IncludeExperimental); - bool AnalyzerChecksEnabled = false; - for (StringRef CheckName : RegisteredCheckers) { - std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str()); - AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName); - } + const bool AnalyzerChecksEnabled = + llvm::any_of(RegisteredCheckers, [&](StringRef CheckName) -> bool { + return Context.isCheckEnabled( + (AnalyzerCheckNamePrefix + CheckName).str()); + }); if (!AnalyzerChecksEnabled) return List; diff --git a/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp index 710b361e16c0a717b562fa32aee9f332032430cb..6028bb2258136bb7bd8336968ed721bcd081d420 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp @@ -171,7 +171,7 @@ std::optional ClangTidyCheck::OptionsView::getEnumInt( if (IgnoreCase) { if (Value.equals_insensitive(NameAndEnum.second)) return NameAndEnum.first; - } else if (Value.equals(NameAndEnum.second)) { + } else if (Value == NameAndEnum.second) { return NameAndEnum.first; } else if (Value.equals_insensitive(NameAndEnum.second)) { Closest = NameAndEnum.second; diff --git a/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp index 4c2416a89aef9b74cc1d7dc33022e4acc71ce2ca..9e714b4be4dfea31dcfd83f0f9a7c1d31e725532 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/CastingThroughVoidCheck.cpp @@ -7,12 +7,10 @@ //===----------------------------------------------------------------------===// #include "CastingThroughVoidCheck.h" -#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" -#include "llvm/ADT/StringSet.h" using namespace clang::ast_matchers; @@ -27,7 +25,8 @@ void CastingThroughVoidCheck::registerMatchers(MatchFinder *Finder) { hasSourceExpression( explicitCastExpr( hasSourceExpression( - expr(hasType(qualType().bind("source_type")))), + expr(hasType(qualType(unless(pointsTo(voidType()))) + .bind("source_type")))), hasDestinationType( qualType(pointsTo(voidType())).bind("void_type"))) .bind("cast"))), diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp index e7be8134781e48de409fcd36a0e7d9dfe10a06d1..36687a8e761e85fce8b52494d2c68fb20051c2e5 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp @@ -25,8 +25,8 @@ AST_MATCHER(QualType, isEnableIf) { const NamedDecl *TypeDecl = Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(); return TypeDecl->isInStdNamespace() && - (TypeDecl->getName().equals("enable_if") || - TypeDecl->getName().equals("enable_if_t")); + (TypeDecl->getName() == "enable_if" || + TypeDecl->getName() == "enable_if_t"); }; const Type *BaseType = Node.getTypePtr(); // Case: pointer or reference to enable_if. diff --git a/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp index f6714d056518daf5d3672de30bd1fd6d0c6e8d10..53956661d57d1364bcb61b37d8afa93bac27bcd1 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp @@ -178,8 +178,11 @@ std::optional ReservedIdentifierCheck::getDeclFailureInfo(const NamedDecl *Decl, const SourceManager &) const { assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() && - !Decl->isImplicit() && "Decl must be an explicit identifier with a name."); + // Implicit identifiers cannot fail. + if (Decl->isImplicit()) + return std::nullopt; + return getFailureInfoImpl( Decl->getName(), isa(Decl->getDeclContext()), /*IsMacro = */ false, getLangOpts(), Invert, AllowedIdentifiers); diff --git a/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp index 8ae37d4f774d23041cb9b9816ae6e0d0e2c792d7..cacba38b4a5aa88b7b70619519a310e8dbc90294 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ReturnConstRefFromParameterCheck.cpp @@ -17,8 +17,11 @@ namespace clang::tidy::bugprone { void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( - returnStmt(hasReturnValue(declRefExpr(to(parmVarDecl(hasType( - hasCanonicalType(matchers::isReferenceToConst()))))))) + returnStmt( + hasReturnValue(declRefExpr(to(parmVarDecl(hasType(hasCanonicalType( + qualType(matchers::isReferenceToConst()).bind("type"))))))), + hasAncestor(functionDecl(hasReturnTypeLoc( + loc(qualType(hasCanonicalType(equalsBoundNode("type")))))))) .bind("ret"), this); } @@ -26,9 +29,13 @@ void ReturnConstRefFromParameterCheck::registerMatchers(MatchFinder *Finder) { void ReturnConstRefFromParameterCheck::check( const MatchFinder::MatchResult &Result) { const auto *R = Result.Nodes.getNodeAs("ret"); - diag(R->getRetValue()->getBeginLoc(), - "returning a constant reference parameter may cause a use-after-free " - "when the parameter is constructed from a temporary"); + const SourceRange Range = R->getRetValue()->getSourceRange(); + if (Range.isInvalid()) + return; + diag(Range.getBegin(), + "returning a constant reference parameter may cause use-after-free " + "when the parameter is constructed from a temporary") + << Range; } } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp index b06a903f92b3e2d6bd8013f6f197293355f2321f..00370ee9b3004caa62e2431a0c0727253af2836c 100644 --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -25,6 +25,7 @@ #include "../misc/StaticAssertCheck.h" #include "../misc/ThrowByValueCatchByReferenceCheck.h" #include "../performance/MoveConstructorInitCheck.h" +#include "../readability/EnumInitialValueCheck.h" #include "../readability/UppercaseLiteralSuffixCheck.h" #include "CommandProcessorCheck.h" #include "DefaultOperatorNewAlignmentCheck.h" @@ -299,6 +300,9 @@ public: "cert-flp37-c"); // FIO CheckFactories.registerCheck("cert-fio38-c"); + // INT + CheckFactories.registerCheck( + "cert-int09-c"); // MSC CheckFactories.registerCheck( "cert-msc24-c"); diff --git a/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp b/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp index 51cc26400f7f382e915af6751daa3ea6ba494353..bf09a6662d9552e59f37611451e3ab2edf50e244 100644 --- a/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp +++ b/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp @@ -9,6 +9,7 @@ #include "SignedBitwiseCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" using namespace clang::ast_matchers; using namespace clang::ast_matchers::internal; @@ -29,8 +30,8 @@ void SignedBitwiseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { const auto SignedIntegerOperand = (IgnorePositiveIntegerLiterals - ? expr(ignoringImpCasts(hasType(isSignedInteger())), - unless(integerLiteral())) + ? expr(ignoringImpCasts( + allOf(hasType(isSignedInteger()), unless(integerLiteral())))) : expr(ignoringImpCasts(hasType(isSignedInteger())))) .bind("signed-operand"); diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp index 3229e302eb4322197e1b1369a7fe901e7168d42b..a1786ba5acfdf5f43117d4dafde939d8043b999a 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp @@ -421,7 +421,7 @@ getContainerFromBeginEndCall(const Expr *Init, bool IsBegin, bool *IsArrow, return {}; if (IsReverse && !Call->Name.consume_back("r")) return {}; - if (!Call->Name.empty() && !Call->Name.equals("c")) + if (!Call->Name.empty() && Call->Name != "c") return {}; return std::make_pair(Call->Container, Call->CallKind); } diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp index 660996aba7b70d6e28696f1e24f2cf7da3e4fa92..aa60c904a363dacb64bb0f15551171dafcc94d32 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp @@ -138,7 +138,8 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) { if (!Converter.canApply()) { diag(PrintfCall->getBeginLoc(), "unable to use '%0' instead of %1 because %2") - << ReplacementFunction << OldFunction->getIdentifier() + << PrintfCall->getSourceRange() << ReplacementFunction + << OldFunction->getIdentifier() << Converter.conversionNotPossibleReason(); return; } diff --git a/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp b/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp index e92350632b556be086736385b72e1b7c430c925c..c13a8010c22210d59b028c54ff054b264898dcda 100644 --- a/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp @@ -55,14 +55,6 @@ AST_MATCHER(QualType, isLocalConstQualified) { return Node.isLocalConstQualified(); } -AST_MATCHER(QualType, isTypeOfType) { - return isa(Node.getTypePtr()); -} - -AST_MATCHER(QualType, isTypeOfExprType) { - return isa(Node.getTypePtr()); -} - struct CheckResult { // Source range of the relevant `const` token in the definition being checked. CharSourceRange ConstRange; @@ -110,16 +102,11 @@ void ConstReturnTypeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { void ConstReturnTypeCheck::registerMatchers(MatchFinder *Finder) { // Find all function definitions for which the return types are `const` // qualified, ignoring decltype types. - auto NonLocalConstType = - qualType(unless(isLocalConstQualified()), - anyOf(decltypeType(), autoType(), isTypeOfType(), - isTypeOfExprType(), substTemplateTypeParmType())); Finder->addMatcher( - functionDecl( - returns(allOf(isConstQualified(), unless(NonLocalConstType))), - anyOf(isDefinition(), cxxMethodDecl(isPure())), - // Overridden functions are not actionable. - unless(cxxMethodDecl(isOverride()))) + functionDecl(returns(isLocalConstQualified()), + anyOf(isDefinition(), cxxMethodDecl(isPure())), + // Overridden functions are not actionable. + unless(cxxMethodDecl(isOverride()))) .bind("func"), this); } diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp index dc30531ebda0e98fe18e2f325111ba8d303598b4..c3208392df1566f98b38933a71fe40d9cf26d976 100644 --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -1358,7 +1358,7 @@ IdentifierNamingCheck::getFailureInfo( std::replace(KindName.begin(), KindName.end(), '_', ' '); std::string Fixup = fixupWithStyle(Type, Name, Style, HNOption, ND); - if (StringRef(Fixup).equals(Name)) { + if (StringRef(Fixup) == Name) { if (!IgnoreFailedSplit) { LLVM_DEBUG(Location.print(llvm::dbgs(), SM); llvm::dbgs() @@ -1374,6 +1374,10 @@ IdentifierNamingCheck::getFailureInfo( std::optional IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl, const SourceManager &SM) const { + // Implicit identifiers cannot be renamed. + if (Decl->isImplicit()) + return std::nullopt; + SourceLocation Loc = Decl->getLocation(); const FileStyle &FileStyle = getStyleForFile(SM.getFilename(Loc)); if (!FileStyle.isActive()) diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index edb67614bd5585de8fa974f8fb3b129ab6360682..fd4730d9c8b9c8652e92743769bb3bb5df1193c3 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SimplifyBooleanExprCheck.h" +#include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/SaveAndRestore.h" @@ -280,9 +281,8 @@ public: if (!S) { return true; } - if (Check->IgnoreMacros && S->getBeginLoc().isMacroID()) { + if (Check->canBeBypassed(S)) return false; - } if (!shouldIgnore(S)) StmtStack.push_back(S); return true; @@ -513,17 +513,23 @@ public: return true; } - static bool isUnaryLNot(const Expr *E) { - return isa(E) && + bool isExpectedUnaryLNot(const Expr *E) { + return !Check->canBeBypassed(E) && isa(E) && cast(E)->getOpcode() == UO_LNot; } + bool isExpectedBinaryOp(const Expr *E) { + const auto *BinaryOp = dyn_cast(E); + return !Check->canBeBypassed(E) && BinaryOp && BinaryOp->isLogicalOp() && + BinaryOp->getType()->isBooleanType(); + } + template static bool checkEitherSide(const BinaryOperator *BO, Functor Func) { return Func(BO->getLHS()) || Func(BO->getRHS()); } - static bool nestedDemorgan(const Expr *E, unsigned NestingLevel) { + bool nestedDemorgan(const Expr *E, unsigned NestingLevel) { const auto *BO = dyn_cast(E->IgnoreUnlessSpelledInSource()); if (!BO) return false; @@ -539,15 +545,13 @@ public: return true; case BO_LAnd: case BO_LOr: - if (checkEitherSide(BO, isUnaryLNot)) - return true; - if (NestingLevel) { - if (checkEitherSide(BO, [NestingLevel](const Expr *E) { - return nestedDemorgan(E, NestingLevel - 1); - })) - return true; - } - return false; + return checkEitherSide( + BO, + [this](const Expr *E) { return isExpectedUnaryLNot(E); }) || + (NestingLevel && + checkEitherSide(BO, [this, NestingLevel](const Expr *E) { + return nestedDemorgan(E, NestingLevel - 1); + })); default: return false; } @@ -556,19 +560,19 @@ public: bool TraverseUnaryOperator(UnaryOperator *Op) { if (!Check->SimplifyDeMorgan || Op->getOpcode() != UO_LNot) return Base::TraverseUnaryOperator(Op); - Expr *SubImp = Op->getSubExpr()->IgnoreImplicit(); - auto *Parens = dyn_cast(SubImp); - auto *BinaryOp = - Parens - ? dyn_cast(Parens->getSubExpr()->IgnoreImplicit()) - : dyn_cast(SubImp); - if (!BinaryOp || !BinaryOp->isLogicalOp() || - !BinaryOp->getType()->isBooleanType()) + const Expr *SubImp = Op->getSubExpr()->IgnoreImplicit(); + const auto *Parens = dyn_cast(SubImp); + const Expr *SubExpr = + Parens ? Parens->getSubExpr()->IgnoreImplicit() : SubImp; + if (!isExpectedBinaryOp(SubExpr)) return Base::TraverseUnaryOperator(Op); + const auto *BinaryOp = cast(SubExpr); if (Check->SimplifyDeMorganRelaxed || - checkEitherSide(BinaryOp, isUnaryLNot) || - checkEitherSide(BinaryOp, - [](const Expr *E) { return nestedDemorgan(E, 1); })) { + checkEitherSide( + BinaryOp, + [this](const Expr *E) { return isExpectedUnaryLNot(E); }) || + checkEitherSide( + BinaryOp, [this](const Expr *E) { return nestedDemorgan(E, 1); })) { if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(), Parens) && !Check->areDiagsSelfContained()) { @@ -694,6 +698,10 @@ void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { Visitor(this, *Result.Context).traverse(); } +bool SimplifyBooleanExprCheck::canBeBypassed(const Stmt *S) const { + return IgnoreMacros && S->getBeginLoc().isMacroID(); +} + void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, SourceLocation Loc, StringRef Description, diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h index ccc6f3d879fc02f4a3698ca67643081c0075ac91..63c3caa01e01a7e43f13eff769db763d9a5c8ce5 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h @@ -64,6 +64,8 @@ private: StringRef Description, SourceRange ReplacementRange, StringRef Replacement); + bool canBeBypassed(const Stmt *S) const; + const bool IgnoreMacros; const bool ChainedConditionalReturn; const bool ChainedConditionalAssignment; diff --git a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp index 65356cc3929c54e7a28d2c63e2244d60a53d4dc3..08adc7134cfea2ef2ae2cd413da443612c377696 100644 --- a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp @@ -59,10 +59,6 @@ void StaticAccessedThroughInstanceCheck::check( const Expr *BaseExpr = MemberExpression->getBase(); - // Do not warn for overloaded -> operators. - if (isa(BaseExpr)) - return; - const QualType BaseType = BaseExpr->getType()->isPointerType() ? BaseExpr->getType()->getPointeeType().getUnqualifiedType() @@ -89,17 +85,30 @@ void StaticAccessedThroughInstanceCheck::check( return; SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc(); - auto Diag = - diag(MemberExprStartLoc, "static member accessed through instance"); - - if (BaseExpr->HasSideEffects(*AstContext) || - getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold) - return; + auto CreateFix = [&] { + return FixItHint::CreateReplacement( + CharSourceRange::getCharRange(MemberExprStartLoc, + MemberExpression->getMemberLoc()), + BaseTypeName + "::"); + }; + + { + auto Diag = + diag(MemberExprStartLoc, "static member accessed through instance"); + + if (getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold) + return; + + if (!BaseExpr->HasSideEffects(*AstContext, + /* IncludePossibleEffects =*/true)) { + Diag << CreateFix(); + return; + } + } - Diag << FixItHint::CreateReplacement( - CharSourceRange::getCharRange(MemberExprStartLoc, - MemberExpression->getMemberLoc()), - BaseTypeName + "::"); + diag(MemberExprStartLoc, "member base expression may carry some side effects", + DiagnosticIDs::Level::Note) + << BaseExpr->getSourceRange() << CreateFix(); } } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp index 3b5d89c8c647196ec57c779e7a93de23d3052994..7c0bbef3ca0878e758a39c21e31f6b93e2a45157 100644 --- a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp @@ -7,12 +7,15 @@ //===----------------------------------------------------------------------===// #include "StringCompareCheck.h" -#include "../utils/FixItHintUtils.h" +#include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/FixIt.h" +#include "llvm/ADT/StringRef.h" using namespace clang::ast_matchers; +namespace optutils = clang::tidy::utils::options; namespace clang::tidy::readability { @@ -20,11 +23,27 @@ static const StringRef CompareMessage = "do not use 'compare' to test equality " "of strings; use the string equality " "operator instead"; +static const StringRef DefaultStringLikeClasses = "::std::basic_string;" + "::std::basic_string_view"; + +StringCompareCheck::StringCompareCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + StringLikeClasses(optutils::parseStringList( + Options.get("StringLikeClasses", DefaultStringLikeClasses))) {} + +void StringCompareCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "StringLikeClasses", + optutils::serializeStringList(StringLikeClasses)); +} + void StringCompareCheck::registerMatchers(MatchFinder *Finder) { + if (StringLikeClasses.empty()) { + return; + } const auto StrCompare = cxxMemberCallExpr( - callee(cxxMethodDecl(hasName("compare"), - ofClass(classTemplateSpecializationDecl( - hasName("::std::basic_string"))))), + callee(cxxMethodDecl(hasName("compare"), ofClass(cxxRecordDecl(hasAnyName( + StringLikeClasses))))), hasArgument(0, expr().bind("str2")), argumentCountIs(1), callee(memberExpr().bind("str1"))); diff --git a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h index 812736d806b71da25c9bc1ac433bbb2c47b544fe..150090901a6e97a139078f7d6c635d9b58804f4c 100644 --- a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h +++ b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STRINGCOMPARECHECK_H #include "../ClangTidyCheck.h" +#include namespace clang::tidy::readability { @@ -20,13 +21,18 @@ namespace clang::tidy::readability { /// http://clang.llvm.org/extra/clang-tidy/checks/readability/string-compare.html class StringCompareCheck : public ClangTidyCheck { public: - StringCompareCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + StringCompareCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + const std::vector StringLikeClasses; }; } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp index 3eb80019ae753e69eacdbd53efffba7de747d3c6..18420d0c8488d2192b23e9033c7827a88021fdc9 100644 --- a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp @@ -138,11 +138,11 @@ static bool applyAbbreviationHeuristic( const llvm::StringMap &AbbreviationDictionary, StringRef Arg, StringRef Param) { if (AbbreviationDictionary.contains(Arg) && - Param.equals(AbbreviationDictionary.lookup(Arg))) + Param == AbbreviationDictionary.lookup(Arg)) return true; if (AbbreviationDictionary.contains(Param) && - Arg.equals(AbbreviationDictionary.lookup(Param))) + Arg == AbbreviationDictionary.lookup(Param)) return true; return false; diff --git a/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp b/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp index a44720c47eca2d7eb9ef815d7cea3dc18a44c616..0fa54b3847ebc22f6cbcfc90ecfa1a63cab10a91 100644 --- a/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp +++ b/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp @@ -88,8 +88,7 @@ determineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile, if (FileCopy.consume_front(Parts.first) && FileCopy.consume_back(Parts.second)) { // Determine the kind of this inclusion. - if (FileCopy.equals("/internal/") || - FileCopy.equals("/proto/")) { + if (FileCopy == "/internal/" || FileCopy == "/proto/") { return IncludeSorter::IK_MainTUInclude; } } diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp index 962a243ce94d48bfcf3e9ed1ffa3278749cbb35a..e811f5519de2c136c2789aeb26fb569f2095832c 100644 --- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp @@ -61,6 +61,7 @@ struct DenseMapInfo { namespace clang::tidy { namespace { + class NameLookup { llvm::PointerIntPair Data; @@ -78,19 +79,58 @@ public: operator bool() const { return !hasMultipleResolutions(); } const NamedDecl *operator*() const { return getDecl(); } }; + } // namespace static const NamedDecl *findDecl(const RecordDecl &RecDecl, StringRef DeclName) { for (const Decl *D : RecDecl.decls()) { if (const auto *ND = dyn_cast(D)) { - if (ND->getDeclName().isIdentifier() && ND->getName().equals(DeclName)) + if (ND->getDeclName().isIdentifier() && ND->getName() == DeclName) return ND; } } return nullptr; } +/// Returns the function that \p Method is overridding. If There are none or +/// multiple overrides it returns nullptr. If the overridden function itself is +/// overridding then it will recurse up to find the first decl of the function. +static const CXXMethodDecl *getOverrideMethod(const CXXMethodDecl *Method) { + if (Method->size_overridden_methods() != 1) + return nullptr; + + while (true) { + Method = *Method->begin_overridden_methods(); + assert(Method && "Overridden method shouldn't be null"); + unsigned NumOverrides = Method->size_overridden_methods(); + if (NumOverrides == 0) + return Method; + if (NumOverrides > 1) + return nullptr; + } +} + +static bool hasNoName(const NamedDecl *Decl) { + return !Decl->getIdentifier() || Decl->getName().empty(); +} + +static const NamedDecl *getFailureForNamedDecl(const NamedDecl *ND) { + const auto *Canonical = cast(ND->getCanonicalDecl()); + if (Canonical != ND) + return Canonical; + + if (const auto *Method = dyn_cast(ND)) { + if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) + Canonical = cast(Overridden->getCanonicalDecl()); + + if (Canonical != ND) + return Canonical; + } + + return ND; +} + /// Returns a decl matching the \p DeclName in \p Parent or one of its base /// classes. If \p AggressiveTemplateLookup is `true` then it will check /// template dependent base classes as well. @@ -132,24 +172,6 @@ static NameLookup findDeclInBases(const CXXRecordDecl &Parent, return NameLookup(Found); // If nullptr, decl wasn't found. } -/// Returns the function that \p Method is overridding. If There are none or -/// multiple overrides it returns nullptr. If the overridden function itself is -/// overridding then it will recurse up to find the first decl of the function. -static const CXXMethodDecl *getOverrideMethod(const CXXMethodDecl *Method) { - if (Method->size_overridden_methods() != 1) - return nullptr; - - while (true) { - Method = *Method->begin_overridden_methods(); - assert(Method && "Overridden method shouldn't be null"); - unsigned NumOverrides = Method->size_overridden_methods(); - if (NumOverrides == 0) - return Method; - if (NumOverrides > 1) - return nullptr; - } -} - namespace { /// Callback supplies macros to RenamerClangTidyCheck::checkMacro @@ -192,10 +214,6 @@ public: : Check(Check), SM(SM), AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {} - static bool hasNoName(const NamedDecl *Decl) { - return !Decl->getIdentifier() || Decl->getName().empty(); - } - bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return false; } @@ -246,29 +264,10 @@ public: } bool VisitNamedDecl(NamedDecl *Decl) { - if (hasNoName(Decl)) - return true; - - const auto *Canonical = cast(Decl->getCanonicalDecl()); - if (Canonical != Decl) { - Check->addUsage(Canonical, Decl->getLocation(), SM); - return true; - } - - // Fix overridden methods - if (const auto *Method = dyn_cast(Decl)) { - if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) { - Check->addUsage(Overridden, Method->getLocation(), SM); - return true; // Don't try to add the actual decl as a Failure. - } - } - - // Ignore ClassTemplateSpecializationDecl which are creating duplicate - // replacements with CXXRecordDecl. - if (isa(Decl)) - return true; - - Check->checkNamedDecl(Decl, SM); + SourceRange UsageRange = + DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation()) + .getSourceRange(); + Check->addUsage(Decl, UsageRange, SM); return true; } @@ -413,82 +412,97 @@ void RenamerClangTidyCheck::registerPPCallbacks( std::make_unique(SM, this)); } -void RenamerClangTidyCheck::addUsage( - const RenamerClangTidyCheck::NamingCheckId &Decl, SourceRange Range, - const SourceManager &SourceMgr) { +std::pair +RenamerClangTidyCheck::addUsage( + const RenamerClangTidyCheck::NamingCheckId &FailureId, + SourceRange UsageRange, const SourceManager &SourceMgr) { // Do nothing if the provided range is invalid. - if (Range.isInvalid()) - return; + if (UsageRange.isInvalid()) + return {NamingCheckFailures.end(), false}; - // If we have a source manager, use it to convert to the spelling location for - // performing the fix. This is necessary because macros can map the same - // spelling location to different source locations, and we only want to fix - // the token once, before it is expanded by the macro. - SourceLocation FixLocation = Range.getBegin(); + // Get the spelling location for performing the fix. This is necessary because + // macros can map the same spelling location to different source locations, + // and we only want to fix the token once, before it is expanded by the macro. + SourceLocation FixLocation = UsageRange.getBegin(); FixLocation = SourceMgr.getSpellingLoc(FixLocation); if (FixLocation.isInvalid()) - return; + return {NamingCheckFailures.end(), false}; + + auto EmplaceResult = NamingCheckFailures.try_emplace(FailureId); + NamingCheckFailure &Failure = EmplaceResult.first->second; // Try to insert the identifier location in the Usages map, and bail out if it // is already in there - RenamerClangTidyCheck::NamingCheckFailure &Failure = - NamingCheckFailures[Decl]; if (!Failure.RawUsageLocs.insert(FixLocation).second) - return; + return EmplaceResult; - if (!Failure.shouldFix()) - return; + if (Failure.FixStatus != RenamerClangTidyCheck::ShouldFixStatus::ShouldFix) + return EmplaceResult; if (SourceMgr.isWrittenInScratchSpace(FixLocation)) Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro; - if (!utils::rangeCanBeFixed(Range, &SourceMgr)) + if (!utils::rangeCanBeFixed(UsageRange, &SourceMgr)) Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro; + + return EmplaceResult; } -void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl, SourceRange Range, +void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl, + SourceRange UsageRange, const SourceManager &SourceMgr) { - // Don't keep track for non-identifier names. - auto *II = Decl->getIdentifier(); - if (!II) + if (hasNoName(Decl)) + return; + + // Ignore ClassTemplateSpecializationDecl which are creating duplicate + // replacements with CXXRecordDecl. + if (isa(Decl)) return; - if (const auto *Method = dyn_cast(Decl)) { - if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) - Decl = Overridden; - } - Decl = cast(Decl->getCanonicalDecl()); - return addUsage( - RenamerClangTidyCheck::NamingCheckId(Decl->getLocation(), II->getName()), - Range, SourceMgr); -} -void RenamerClangTidyCheck::checkNamedDecl(const NamedDecl *Decl, - const SourceManager &SourceMgr) { - std::optional MaybeFailure = getDeclFailureInfo(Decl, SourceMgr); + // We don't want to create a failure for every NamedDecl we find. Ideally + // there is just one NamedDecl in every group of "related" NamedDecls that + // becomes the failure. This NamedDecl and all of its related NamedDecls + // become usages. E.g. Since NamedDecls are Redeclarable, only the canonical + // NamedDecl becomes the failure and all redeclarations become usages. + const NamedDecl *FailureDecl = getFailureForNamedDecl(Decl); + + std::optional MaybeFailure = + getDeclFailureInfo(FailureDecl, SourceMgr); if (!MaybeFailure) return; - FailureInfo &Info = *MaybeFailure; - NamingCheckFailure &Failure = - NamingCheckFailures[NamingCheckId(Decl->getLocation(), Decl->getName())]; - SourceRange Range = - DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation()) - .getSourceRange(); - - const IdentifierTable &Idents = Decl->getASTContext().Idents; - auto CheckNewIdentifier = Idents.find(Info.Fixup); + NamingCheckId FailureId(FailureDecl->getLocation(), FailureDecl->getName()); + + auto [FailureIter, NewFailure] = addUsage(FailureId, UsageRange, SourceMgr); + + if (FailureIter == NamingCheckFailures.end()) { + // Nothing to do if the usage wasn't accepted. + return; + } + if (!NewFailure) { + // FailureInfo has already been provided. + return; + } + + // Update the stored failure with info regarding the FailureDecl. + NamingCheckFailure &Failure = FailureIter->second; + Failure.Info = std::move(*MaybeFailure); + + // Don't overwritte the failure status if it was already set. + if (!Failure.shouldFix()) { + return; + } + const IdentifierTable &Idents = FailureDecl->getASTContext().Idents; + auto CheckNewIdentifier = Idents.find(Failure.Info.Fixup); if (CheckNewIdentifier != Idents.end()) { const IdentifierInfo *Ident = CheckNewIdentifier->second; if (Ident->isKeyword(getLangOpts())) Failure.FixStatus = ShouldFixStatus::ConflictsWithKeyword; else if (Ident->hasMacroDefinition()) Failure.FixStatus = ShouldFixStatus::ConflictsWithMacroDefinition; - } else if (!isValidAsciiIdentifier(Info.Fixup)) { + } else if (!isValidAsciiIdentifier(Failure.Info.Fixup)) { Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier; } - - Failure.Info = std::move(Info); - addUsage(Decl, Range, SourceMgr); } void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) { diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h index be5b6f0c7f76785d7f2aada7dd394cf3a568ddd4..3d5721b789ac2e63ac2e161e3a1d8fd8bb1ec881 100644 --- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h +++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.h @@ -115,15 +115,9 @@ public: void expandMacro(const Token &MacroNameTok, const MacroInfo *MI, const SourceManager &SourceMgr); - void addUsage(const RenamerClangTidyCheck::NamingCheckId &Decl, - SourceRange Range, const SourceManager &SourceMgr); - - /// Convenience method when the usage to be added is a NamedDecl. void addUsage(const NamedDecl *Decl, SourceRange Range, const SourceManager &SourceMgr); - void checkNamedDecl(const NamedDecl *Decl, const SourceManager &SourceMgr); - protected: /// Overridden by derived classes, returns information about if and how a Decl /// failed the check. A 'std::nullopt' result means the Decl did not fail the @@ -158,6 +152,14 @@ protected: const NamingCheckFailure &Failure) const = 0; private: + // Manage additions to the Failure/usage map + // + // return the result of NamingCheckFailures::try_emplace() if the usage was + // accepted. + std::pair + addUsage(const RenamerClangTidyCheck::NamingCheckId &FailureId, + SourceRange UsageRange, const SourceManager &SourceMgr); + NamingCheckFailureMap NamingCheckFailures; const bool AggressiveDependentMemberLookup; }; diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index d5818e0ca309b03fafadc20f1fbdfe4f902ab615..ecd490145dd3c4583443b2978a018dc128f220f0 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -918,7 +918,9 @@ void PreamblePatch::apply(CompilerInvocation &CI) const { // no guarantees around using arbitrary options when reusing PCHs, and // different target opts can result in crashes, see // ParsedASTTest.PreambleWithDifferentTarget. - CI.TargetOpts = Baseline->TargetOpts; + // Make sure this is a deep copy, as the same Baseline might be used + // concurrently. + *CI.TargetOpts = *Baseline->TargetOpts; // No need to map an empty file. if (PatchContents.empty()) diff --git a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp index 42eeba36a80e43eaa5bc9f87f454297f8ea409f1..785ec4086ea7606e2258e0d842f8bfa60f99147a 100644 --- a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp +++ b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp @@ -18,9 +18,11 @@ namespace { const std::pair IncludeMappings[] = { {"include/__stdarg___gnuc_va_list.h", ""}, {"include/__stdarg___va_copy.h", ""}, + {"include/__stdarg_header_macro.h", ""}, {"include/__stdarg_va_arg.h", ""}, {"include/__stdarg_va_copy.h", ""}, {"include/__stdarg_va_list.h", ""}, + {"include/__stddef_header_macro.h", ""}, {"include/__stddef_max_align_t.h", ""}, {"include/__stddef_null.h", ""}, {"include/__stddef_nullptr_t.h", ""}, diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp index e36b3249bc7b92948c4bf1a5dc2169c025606302..44080802a28958ec1f3670019f6da43d69aa8097 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp @@ -40,15 +40,12 @@ namespace { /// void f() { E e1 = EV1; } /// /// After: -/// enum class E { EV1, EV2 }; -/// void f() { E e1 = E::EV1; } +/// enum class E { V1, V2 }; +/// void f() { E e1 = E::V1; } /// /// Note that the respective project code might not compile anymore /// if it made use of the now-gone implicit conversion to int. /// This is out of scope for this tweak. -/// -/// TODO: In the above example, we could detect that the values -/// start with the enum name, and remove that prefix. class ScopifyEnum : public Tweak { const char *id() const final; @@ -63,14 +60,13 @@ class ScopifyEnum : public Tweak { std::function; llvm::Error addClassKeywordToDeclarations(); llvm::Error scopifyEnumValues(); - llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef Prefix); + llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef EnumName, + bool StripPrefix); llvm::Expected getContentForFile(StringRef FilePath); - unsigned getOffsetFromPosition(const Position &Pos, StringRef Content) const; llvm::Error addReplacementForReference(const ReferencesResult::Reference &Ref, const MakeReplacement &GetReplacement); llvm::Error addReplacement(StringRef FilePath, StringRef Content, const tooling::Replacement &Replacement); - Position getPosition(const Decl &D) const; const EnumDecl *D = nullptr; const Selection *S = nullptr; @@ -109,7 +105,8 @@ Expected ScopifyEnum::apply(const Selection &Inputs) { llvm::Error ScopifyEnum::addClassKeywordToDeclarations() { for (const auto &Ref : - findReferences(*S->AST, getPosition(*D), 0, S->Index, false) + findReferences(*S->AST, sourceLocToPosition(*SM, D->getBeginLoc()), 0, + S->Index, false) .References) { if (!(Ref.Attributes & ReferencesResult::Declaration)) continue; @@ -125,25 +122,46 @@ llvm::Error ScopifyEnum::addClassKeywordToDeclarations() { } llvm::Error ScopifyEnum::scopifyEnumValues() { - std::string PrefixToInsert(D->getName()); - PrefixToInsert += "::"; - for (auto E : D->enumerators()) { - if (auto Err = scopifyEnumValue(*E, PrefixToInsert)) + StringRef EnumName(D->getName()); + bool StripPrefix = true; + for (const EnumConstantDecl *E : D->enumerators()) { + if (!E->getName().starts_with(EnumName)) { + StripPrefix = false; + break; + } + } + for (const EnumConstantDecl *E : D->enumerators()) { + if (auto Err = scopifyEnumValue(*E, EnumName, StripPrefix)) return Err; } return llvm::Error::success(); } llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD, - StringRef Prefix) { + StringRef EnumName, + bool StripPrefix) { for (const auto &Ref : - findReferences(*S->AST, getPosition(CD), 0, S->Index, false) + findReferences(*S->AST, sourceLocToPosition(*SM, CD.getBeginLoc()), 0, + S->Index, false) .References) { - if (Ref.Attributes & ReferencesResult::Declaration) + if (Ref.Attributes & ReferencesResult::Declaration) { + if (StripPrefix) { + const auto MakeReplacement = [&EnumName](StringRef FilePath, + StringRef Content, + unsigned Offset) { + unsigned Length = EnumName.size(); + if (Content[Offset + Length] == '_') + ++Length; + return tooling::Replacement(FilePath, Offset, Length, {}); + }; + if (auto Err = addReplacementForReference(Ref, MakeReplacement)) + return Err; + } continue; + } - const auto MakeReplacement = [&Prefix](StringRef FilePath, - StringRef Content, unsigned Offset) { + const auto MakeReplacement = [&](StringRef FilePath, StringRef Content, + unsigned Offset) { const auto IsAlreadyScoped = [Content, Offset] { if (Offset < 2) return false; @@ -164,9 +182,18 @@ llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD, } return false; }; - return IsAlreadyScoped() - ? tooling::Replacement() - : tooling::Replacement(FilePath, Offset, 0, Prefix); + if (StripPrefix) { + const int ExtraLength = + Content[Offset + EnumName.size()] == '_' ? 1 : 0; + if (IsAlreadyScoped()) + return tooling::Replacement(FilePath, Offset, + EnumName.size() + ExtraLength, {}); + return tooling::Replacement(FilePath, Offset + EnumName.size(), + ExtraLength, "::"); + } + return IsAlreadyScoped() ? tooling::Replacement() + : tooling::Replacement(FilePath, Offset, 0, + EnumName.str() + "::"); }; if (auto Err = addReplacementForReference(Ref, MakeReplacement)) return Err; @@ -187,27 +214,19 @@ llvm::Expected ScopifyEnum::getContentForFile(StringRef FilePath) { return Content; } -unsigned int ScopifyEnum::getOffsetFromPosition(const Position &Pos, - StringRef Content) const { - unsigned int Offset = 0; - - for (std::size_t LinesRemaining = Pos.line; - Offset < Content.size() && LinesRemaining;) { - if (Content[Offset++] == '\n') - --LinesRemaining; - } - return Offset + Pos.character; -} - llvm::Error ScopifyEnum::addReplacementForReference(const ReferencesResult::Reference &Ref, const MakeReplacement &GetReplacement) { StringRef FilePath = Ref.Loc.uri.file(); - auto Content = getContentForFile(FilePath); + llvm::Expected Content = getContentForFile(FilePath); if (!Content) return Content.takeError(); - unsigned Offset = getOffsetFromPosition(Ref.Loc.range.start, *Content); - tooling::Replacement Replacement = GetReplacement(FilePath, *Content, Offset); + llvm::Expected Offset = + positionToOffset(*Content, Ref.Loc.range.start); + if (!Offset) + return Offset.takeError(); + tooling::Replacement Replacement = + GetReplacement(FilePath, *Content, *Offset); if (Replacement.isApplicable()) return addReplacement(FilePath, *Content, Replacement); return llvm::Error::success(); @@ -223,13 +242,5 @@ ScopifyEnum::addReplacement(StringRef FilePath, StringRef Content, return llvm::Error::success(); } -Position ScopifyEnum::getPosition(const Decl &D) const { - const SourceLocation Loc = D.getLocation(); - Position Pos; - Pos.line = SM->getSpellingLineNumber(Loc) - 1; - Pos.character = SM->getSpellingColumnNumber(Loc) - 1; - return Pos; -} - } // namespace } // namespace clang::clangd diff --git a/clang-tools-extra/clangd/test/delimited-input-comment-at-the-end.test b/clang-tools-extra/clangd/test/delimited-input-comment-at-the-end.test index bbbd72f8c59f6f28e3ed947b7eb2c75341876122..85a1f2199fadf903bf3d5b00dadeeeb380a7519d 100644 --- a/clang-tools-extra/clangd/test/delimited-input-comment-at-the-end.test +++ b/clang-tools-extra/clangd/test/delimited-input-comment-at-the-end.test @@ -1,11 +1,11 @@ -# RUN: clangd -input-style=delimited -sync -input-mirror-file %t < %s -# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t -# -# RUN: clangd -lit-test -input-mirror-file %t < %s -# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t -# -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} ---- -{"jsonrpc":"2.0","id":3,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} +# RUN: clangd -input-style=delimited -sync -input-mirror-file %t < %s +# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t +# +# RUN: clangd -lit-test -input-mirror-file %t < %s +# RUN: grep '{"jsonrpc":"2.0","id":3,"method":"exit"}' %t +# +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/test/hover.test b/clang-tools-extra/clangd/test/hover.test index ec8d0488fa5ed119f9ed03d472f4f8d358f6f196..dc76ae85fa41dd34a3ceeb2e7fa0de8226bc9751 100644 --- a/clang-tools-extra/clangd/test/hover.test +++ b/clang-tools-extra/clangd/test/hover.test @@ -1,57 +1,57 @@ -# RUN: clangd -lit-test < %s | FileCheck %s -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} ---- -{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}} ---- -{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":27}}} -# CHECK: "id": 1, -# CHECK-NEXT: "jsonrpc": "2.0", -# CHECK-NEXT: "result": { -# CHECK-NEXT: "contents": { -# CHECK-NEXT: "kind": "plaintext", -# CHECK-NEXT: "value": "function foo\n\n→ void\n\nvoid foo()" -# CHECK-NEXT: }, -# CHECK-NEXT: "range": { -# CHECK-NEXT: "end": { -# CHECK-NEXT: "character": 28, -# CHECK-NEXT: "line": 0 -# CHECK-NEXT: }, -# CHECK-NEXT: "start": { -# CHECK-NEXT: "character": 25, -# CHECK-NEXT: "line": 0 -# CHECK-NEXT: } -# CHECK-NEXT: } -# CHECK-NEXT: } -# CHECK-NEXT:} ---- -{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":10}}} -# CHECK: "id": 1, -# CHECK-NEXT: "jsonrpc": "2.0", -# CHECK-NEXT: "result": null ---- -{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main2.cpp","languageId":"cpp","version":1,"text":"enum foo{}; int main() { foo f; }\n"}}} ---- -{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main2.cpp"},"position":{"line":0,"character":27}}} -# CHECK: "id": 1, -# CHECK-NEXT: "jsonrpc": "2.0", -# CHECK-NEXT: "result": { -# CHECK-NEXT: "contents": { -# CHECK-NEXT: "kind": "plaintext", -# CHECK-NEXT: "value": "enum foo\n\nenum foo {}" -# CHECK-NEXT: }, -# CHECK-NEXT: "range": { -# CHECK-NEXT: "end": { -# CHECK-NEXT: "character": 28, -# CHECK-NEXT: "line": 0 -# CHECK-NEXT: }, -# CHECK-NEXT: "start": { -# CHECK-NEXT: "character": 25, -# CHECK-NEXT: "line": 0 -# CHECK-NEXT: } -# CHECK-NEXT: } -# CHECK-NEXT: } -# CHECK-NEXT:} ---- -{"jsonrpc":"2.0","id":3,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} +# RUN: clangd -lit-test < %s | FileCheck %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void foo(); int main() { foo(); }\n"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":27}}} +# CHECK: "id": 1, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": { +# CHECK-NEXT: "contents": { +# CHECK-NEXT: "kind": "plaintext", +# CHECK-NEXT: "value": "function foo\n\n→ void\n\nvoid foo()" +# CHECK-NEXT: }, +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 28, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 25, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT:} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":10}}} +# CHECK: "id": 1, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": null +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main2.cpp","languageId":"cpp","version":1,"text":"enum foo{}; int main() { foo f; }\n"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main2.cpp"},"position":{"line":0,"character":27}}} +# CHECK: "id": 1, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": { +# CHECK-NEXT: "contents": { +# CHECK-NEXT: "kind": "plaintext", +# CHECK-NEXT: "value": "enum foo\n\nenum foo {}" +# CHECK-NEXT: }, +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 28, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 25, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT:} +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/test/spaces-in-delimited-input.test b/clang-tools-extra/clangd/test/spaces-in-delimited-input.test index dc2e2f5ea0f64dd4dad1a3d3055cffd9e44b3cc0..aa191b6f2097f97b4a3f74c1f90049c28945f657 100644 --- a/clang-tools-extra/clangd/test/spaces-in-delimited-input.test +++ b/clang-tools-extra/clangd/test/spaces-in-delimited-input.test @@ -1,13 +1,13 @@ -# RUN: clangd -input-style=delimited -sync < %s 2>&1 | FileCheck %s -# RUN: clangd -lit-test -sync < %s 2>&1 | FileCheck %s -# -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} - ---- - -{"jsonrpc":"2.0","id":3,"method":"shutdown"} - ---- - -{"jsonrpc":"2.0","method":"exit"} -# CHECK-NOT: JSON parse error +# RUN: clangd -input-style=delimited -sync < %s 2>&1 | FileCheck %s +# RUN: clangd -lit-test -sync < %s 2>&1 | FileCheck %s +# +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} + +--- + +{"jsonrpc":"2.0","id":3,"method":"shutdown"} + +--- + +{"jsonrpc":"2.0","method":"exit"} +# CHECK-NOT: JSON parse error diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 94437857cecca6a94df8ad316b29b6302d812cd1..0b2273f0a9a6e36f218c9c06382fa6cee6f2ad80 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -642,10 +642,7 @@ TEST_F(TargetDeclTest, RewrittenBinaryOperator) { bool x = (Foo(1) [[!=]] Foo(2)); )cpp"; EXPECT_DECLS("CXXRewrittenBinaryOperator", - {"std::strong_ordering operator<=>(const Foo &) const = default", - Rel::TemplatePattern}, - {"bool operator==(const Foo &) const noexcept = default", - Rel::TemplateInstantiation}); + {"bool operator==(const Foo &) const noexcept = default"}); } TEST_F(TargetDeclTest, FunctionTemplate) { diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 5ead74748f550cb4536c7edf9cc246a4d41b5a42..d9e97e5215a2612666fecd077eb0156005e33cc0 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -965,6 +965,19 @@ class Foo final {})cpp"; // Bindings are in theory public members of an anonymous struct. HI.AccessSpecifier = "public"; }}, + {// Don't crash on invalid decl with invalid init expr. + R"cpp( + Unknown [[^abc]] = invalid; + // error-ok + )cpp", + [](HoverInfo &HI) { + HI.Name = "abc"; + HI.Kind = index::SymbolKind::Variable; + HI.NamespaceScope = ""; + HI.Definition = "int abc = ()"; + HI.Type = "int"; + HI.AccessSpecifier = "public"; + }}, {// Extra info for function call. R"cpp( void fun(int arg_a, int &arg_b) {}; @@ -3078,7 +3091,7 @@ TEST(Hover, All) { HI.NamespaceScope = ""; HI.Definition = "bool operator==(const Foo &) const noexcept = default"; - HI.Documentation = "Foo spaceship"; + HI.Documentation = ""; }}, }; @@ -3881,7 +3894,7 @@ TEST(Hover, SpaceshipTemplateNoCrash) { TU.ExtraArgs.push_back("-std=c++20"); auto AST = TU.build(); auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); - EXPECT_EQ(HI->Documentation, "Foo bar baz"); + EXPECT_EQ(HI->Documentation, ""); } TEST(Hover, ForwardStructNoCrash) { diff --git a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp index b5a964a5a26d8074260215f3a2ffcee041811bdc..5da059faaf5e8c2dcc8990cd6f42282413320117 100644 --- a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp @@ -26,7 +26,7 @@ enum ^E; )cpp"); } -TEST_F(ScopifyEnumTest, ApplyTest) { +TEST_F(ScopifyEnumTest, ApplyTestWithPrefix) { std::string Original = R"cpp( enum ^E { EV1, EV2, EV3 }; enum E; @@ -39,13 +39,69 @@ E func(E in) } )cpp"; std::string Expected = R"cpp( -enum class E { EV1, EV2, EV3 }; +enum class E { V1, V2, V3 }; enum class E; E func(E in) { - E out = E::EV1; - if (in == E::EV2) - out = E::EV3; + E out = E::V1; + if (in == E::V2) + out = E::V3; + return out; +} +)cpp"; + FileName = "Test.cpp"; + SCOPED_TRACE(Original); + EXPECT_EQ(apply(Original), Expected); +} + +TEST_F(ScopifyEnumTest, ApplyTestWithPrefixAndUnderscore) { + std::string Original = R"cpp( +enum ^E { E_V1, E_V2, E_V3 }; +enum E; +E func(E in) +{ + E out = E_V1; + if (in == E_V2) + out = E::E_V3; + return out; +} +)cpp"; + std::string Expected = R"cpp( +enum class E { V1, V2, V3 }; +enum class E; +E func(E in) +{ + E out = E::V1; + if (in == E::V2) + out = E::V3; + return out; +} +)cpp"; + FileName = "Test.cpp"; + SCOPED_TRACE(Original); + EXPECT_EQ(apply(Original), Expected); +} + +TEST_F(ScopifyEnumTest, ApplyTestWithoutPrefix) { + std::string Original = R"cpp( +enum ^E { V1, V2, V3 }; +enum E; +E func(E in) +{ + E out = V1; + if (in == V2) + out = E::V3; + return out; +} +)cpp"; + std::string Expected = R"cpp( +enum class E { V1, V2, V3 }; +enum class E; +E func(E in) +{ + E out = E::V1; + if (in == E::V2) + out = E::V3; return out; } )cpp"; diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 5956ccb925485c77c941029282d5dcac3fde9185..fc976ce3a33d541e63002a740045f30c00eec798 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -69,6 +69,9 @@ Code completion Code actions ^^^^^^^^^^^^ +- The tweak for turning unscoped into scoped enums now removes redundant prefixes + from the enum values. + Signature help ^^^^^^^^^^^^^^ @@ -87,7 +90,9 @@ Improvements to clang-doc Improvements to clang-query --------------------------- -The improvements are... +- Added the `file` command to dynamically load a list of commands and matchers + from an external file, allowing the cost of reading the compilation database + and building the AST to be imposed just once for faster prototyping. Improvements to clang-rename ---------------------------- @@ -166,6 +171,10 @@ New checks New check aliases ^^^^^^^^^^^^^^^^^ +- New alias :doc:`cert-int09-c ` to + :doc:`readability-enum-initial-value ` + was added. + Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -173,6 +182,11 @@ Changes in existing checks ` check by detecting side effect from calling a method with non-const reference parameters. +- Improved :doc:`bugprone-casting-through-void + ` check by ignoring casts + where source is already a ``void``` pointer, making middle ``void`` pointer + casts bug-free. + - Improved :doc:`bugprone-forwarding-reference-overload ` check to ignore deleted constructors which won't hide other overloads. @@ -247,6 +261,10 @@ Changes in existing checks - Improved :doc:`google-runtime-int ` check performance through optimizations. +- Improved :doc:`hicpp-signed-bitwise ` + check by ignoring false positives involving positive integer literals behind + implicit casts when `IgnorePositiveIntegerLiterals` is enabled. + - Improved :doc:`hicpp-ignored-remove-result ` check by ignoring other functions with same prefixes as the target specific functions. @@ -313,6 +331,10 @@ Changes in existing checks ` check by adding fix-its. +- Improved :doc:`readability-const-return-type + ` check to eliminate false + positives when returning types with const not at the top level. + - Improved :doc:`readability-duplicate-include ` check by excluding include directives that form the filename using macro. @@ -332,11 +354,25 @@ Changes in existing checks ` check to properly emit warnings for static data member with an in-class initializer. +- Improved :doc:`readability-static-accessed-through-instance + ` check to + support calls to overloaded operators as base expression and provide fixes to + expressions with side-effects. + +- Improved :doc:`readability-simplify-boolean-expr + ` check to avoid to emit + warning for macro when IgnoreMacro option is enabled. + - Improved :doc:`readability-static-definition-in-anonymous-namespace ` check by resolving fix-it overlaps in template code by disregarding implicit instances. +- Improved :doc:`readability-string-compare + ` check to also detect + usages of ``std::string_view::compare``. Added a `StringLikeClasses` option + to detect usages of ``compare`` method in custom string-like classes. + Removed checks ^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/bad-signal-to-kill-thread.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/bad-signal-to-kill-thread.rst index 24b08da6c5c30b4a22e656aa8bc1026c501edc41..365624a8b1a0ad5ea841835b9d4eb2f6566b3129 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/bad-signal-to-kill-thread.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/bad-signal-to-kill-thread.rst @@ -14,3 +14,5 @@ just the individual thread. Use any signal except ``SIGTERM``. This check corresponds to the CERT C Coding Standard rule `POS44-C. Do not use signals to terminate threads `_. + +`cert-pos44-c` redirects here as an alias of this check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/macro-parentheses.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/macro-parentheses.rst index b6bafcec1644f7454dc9afcca09bc19f705814b4..80cea089564e4b558df97759815ccbfdfe1eac90 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/macro-parentheses.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/macro-parentheses.rst @@ -17,3 +17,7 @@ completely before it is used. It is also recommended to surround macro arguments in the replacement list with parentheses. This ensures that the argument value is calculated properly. + +This check corresponds to the CERT C Coding Standard rule +`PRE20-C. Macro replacement lists should be parenthesized. +`_ diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-memory-comparison.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-memory-comparison.rst index 549e214b241b116341cb236c3e8bbac96d6e6a0d..f82863f7c2f18f1e3804482d01a8a0fa52c6a94c 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-memory-comparison.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/suspicious-memory-comparison.rst @@ -29,3 +29,5 @@ This check is also related to and partially overlaps the CERT C++ Coding Standar and `EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation `_ + +`cert-exp42-c` redirects here as an alias of this check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/int09-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/int09-c.rst new file mode 100644 index 0000000000000000000000000000000000000000..74c606929547db263c5b9f2b95940fb6a56f6d8b --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert/int09-c.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-int09-c +.. meta:: + :http-equiv=refresh: 5;URL=../readability/enum-initial-value.html + +cert-int09-c +============ + +The `cert-int09-c` check is an alias, please see +:doc:`readability-enum-initial-value <../readability/enum-initial-value>` for +more information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/concurrency/thread-canceltype-asynchronous.rst b/clang-tools-extra/docs/clang-tidy/checks/concurrency/thread-canceltype-asynchronous.rst index 11edd001365d1414f6a00a60dbf5f474eef47c1d..5e4d980077d507fcb0bbfa7d6e4ef1a196265b91 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/concurrency/thread-canceltype-asynchronous.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/concurrency/thread-canceltype-asynchronous.rst @@ -17,3 +17,5 @@ be acted upon and the effect is as if it was an asynchronous cancellation. This check corresponds to the CERT C Coding Standard rule `POS47-C. Do not use threads that can be canceled asynchronously `_. + +`cert-pos47-c` redirects here as an alias of this check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 49747ff896ba5c6f8556af99a0993090328e406b..046a5ff57ad1c9c6800d7dddfe39e359d2c2bbf1 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -120,7 +120,7 @@ Clang-Tidy Checks :doc:`bugprone-posix-return `, "Yes" :doc:`bugprone-redundant-branch-condition `, "Yes" :doc:`bugprone-reserved-identifier `, "Yes" - :doc:`bugprone-return-const-ref-from-parameter ` + :doc:`bugprone-return-const-ref-from-parameter `, :doc:`bugprone-shared-ptr-array-mismatch `, "Yes" :doc:`bugprone-signal-handler `, :doc:`bugprone-signed-char-misuse `, @@ -395,8 +395,10 @@ Clang-Tidy Checks :doc:`readability-use-std-min-max `, "Yes" :doc:`zircon-temporary-objects `, +Check aliases +------------- -.. csv-table:: Aliases.. +.. csv-table:: :header: "Name", "Redirect", "Offers fixes" :doc:`bugprone-narrowing-conversions `, :doc:`cppcoreguidelines-narrowing-conversions `, @@ -413,6 +415,7 @@ Clang-Tidy Checks :doc:`cert-exp42-c `, :doc:`bugprone-suspicious-memory-comparison `, :doc:`cert-fio38-c `, :doc:`misc-non-copyable-objects `, :doc:`cert-flp37-c `, :doc:`bugprone-suspicious-memory-comparison `, + :doc:`cert-int09-c `, :doc:`readability-enum-initial-value `, "Yes" :doc:`cert-msc24-c `, :doc:`bugprone-unsafe-functions `, :doc:`cert-msc30-c `, :doc:`cert-msc50-cpp `, :doc:`cert-msc32-c `, :doc:`cert-msc51-cpp `, diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/throw-by-value-catch-by-reference.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/throw-by-value-catch-by-reference.rst index af6ec1416e5e270f30f0e3669d560e38515e20c3..b89fbe8b44665e0892ea32837bc54e9a4237b141 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/throw-by-value-catch-by-reference.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/throw-by-value-catch-by-reference.rst @@ -3,8 +3,7 @@ misc-throw-by-value-catch-by-reference ====================================== -`cert-err09-cpp` redirects here as an alias for this check. -`cert-err61-cpp` redirects here as an alias for this check. +`cert-err09-cpp` and `cert-err61-cpp` redirect here as aliases of this check. Finds violations of the rule "Throw by value, catch by reference" presented for example in "C++ Coding Standards" by H. Sutter and A. Alexandrescu, as well as diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst index 9bb691e9d9512e41a2d79399ef12d23414996a26..79648a1104bca237f0bcb5e518f0e27c2a6827db 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst @@ -118,7 +118,7 @@ Options .. option:: PrintfLikeFunctions - A semicolon-separated list of (fully qualified) extra function names to + A semicolon-separated list of (fully qualified) function names to replace, with the requirement that the first parameter contains the printf-style format string and the arguments to be formatted follow immediately afterwards. If neither this option nor @@ -128,7 +128,7 @@ Options .. option:: FprintfLikeFunctions - A semicolon-separated list of (fully qualified) extra function names to + A semicolon-separated list of (fully qualified) function names to replace, with the requirement that the first parameter is retained, the second parameter contains the printf-style format string and the arguments to be formatted follow immediately afterwards. If neither this diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst index 660efc1eaff3e53527aad41a19151be0fe13062d..b27e10d5c13369e54279056f76d5f7d248cf616d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst @@ -6,70 +6,83 @@ readability-enum-initial-value Enforces consistent style for enumerators' initialization, covering three styles: none, first only, or all initialized explicitly. -When adding new enumerations, inconsistent initial value will cause potential -enumeration value conflicts. +An inconsistent style and strictness to defining the initializing value of +enumerators may cause issues if the enumeration is extended with new +enumerators that obtain their integer representation implicitly. -In an enumeration, the following three cases are accepted. -1. none of enumerators are explicit initialized. -2. the first enumerator is explicit initialized. -3. all of enumerators are explicit initialized. +The following three cases are accepted: + +#. **No** enumerators are explicit initialized. +#. Exactly **the first** enumerator is explicit initialized. +#. **All** enumerators are explicit initialized. .. code-block:: c++ - // valid, none of enumerators are initialized. - enum A { - e0, - e1, - e2, + enum A { // (1) Valid, none of enumerators are initialized. + a0, + a1, + a2, }; - // valid, the first enumerator is initialized. - enum A { - e0 = 0, - e1, - e2, + enum B { // (2) Valid, the first enumerator is initialized. + b0 = 0, + b1, + b2, }; - // valid, all of enumerators are initialized. - enum A { - e0 = 0, - e1 = 1, - e2 = 2, + enum C { // (3) Valid, all of enumerators are initialized. + c0 = 0, + c1 = 1, + c2 = 2, }; - // invalid, e1 is not explicit initialized. - enum A { + enum D { // Invalid, d1 is not explicitly initialized! + d0 = 0, + d1, + d2 = 2, + }; + + enum E { // Invalid, e1, e3, and e5 are not explicitly initialized. e0 = 0, e1, e2 = 2, + e3, // Dangerous, as the numeric values of e3 and e5 are both 3, and this is not explicitly visible in the code! + e4 = 2, + e5, }; +This check corresponds to the CERT C Coding Standard recommendation `INT09-C. Ensure enumeration constants map to unique values +`_. + +`cert-int09-c` redirects here as an alias of this check. + Options ------- .. option:: AllowExplicitZeroFirstInitialValue - If set to `false`, the first enumerator must not be explicitly initialized. - See examples below. Default is `true`. + If set to `false`, the first enumerator must not be explicitly initialized to + a literal ``0``. + Default is `true`. .. code-block:: c++ - enum A { - e0 = 0, // not allowed if AllowExplicitZeroFirstInitialValue is false - e1, - e2, + enum F { + f0 = 0, // Not allowed if AllowExplicitZeroFirstInitialValue is false. + f1, + f2, }; .. option:: AllowExplicitSequentialInitialValues - If set to `false`, sequential initializations are not allowed. - See examples below. Default is `true`. + If set to `false`, explicit initialization to sequential values are not + allowed. + Default is `true`. .. code-block:: c++ - enum A { - e0 = 1, // not allowed if AllowExplicitSequentialInitialValues is false - e1 = 2, - e2 = 3, - }; + enum G { + g0 = 1, // Not allowed if AllowExplicitSequentialInitialValues is false. + g1 = 2, + g2 = 3, diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst index 23d12f418366402bb1d97517e19a8c04cbd354fb..ffb3738bf72c92aff474e7bed546843c345f4bb9 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst @@ -35,3 +35,6 @@ is changed to: C::E1; C::E2; +The `--fix` commandline option provides default support for safe fixes, whereas +`--fix-notes` enables fixes that may replace expressions with side effects, +potentially altering the program's behavior. diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst index 268632eee61a278ce7899ccc81c1163c5b07879b..4be2473bed2d74808e609e935295b78425077282 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst @@ -14,10 +14,12 @@ recommended to avoid the risk of incorrect interpretation of the return value and to simplify the code. The string equality and inequality operators can also be faster than the ``compare`` method due to early termination. -Examples: +Example +------- .. code-block:: c++ + // The same rules apply to std::string_view. std::string str1{"a"}; std::string str2{"b"}; @@ -50,5 +52,36 @@ Examples: } The above code examples show the list of if-statements that this check will -give a warning for. All of them uses ``compare`` to check if equality or +give a warning for. All of them use ``compare`` to check equality or inequality of two strings instead of using the correct operators. + +Options +------- + +.. option:: StringLikeClasses + + A string containing semicolon-separated names of string-like classes. + By default contains only ``::std::basic_string`` + and ``::std::basic_string_view``. If a class from this list has + a ``compare`` method similar to that of ``std::string``, it will be checked + in the same way. + +Example +^^^^^^^ + +.. code-block:: c++ + + struct CustomString { + public: + int compare (const CustomString& other) const; + } + + CustomString str1; + CustomString str2; + + // use str1 != str2 instead. + if (str1.compare(str2)) { + } + +If `StringLikeClasses` contains ``CustomString``, the check will suggest +replacing ``compare`` with equality operator. diff --git a/clang-tools-extra/test/clang-query/Inputs/empty.script b/clang-tools-extra/test/clang-query/Inputs/empty.script new file mode 100644 index 0000000000000000000000000000000000000000..3c30abd1ae5d1526771c5a4ce09674ea6ba01c56 --- /dev/null +++ b/clang-tools-extra/test/clang-query/Inputs/empty.script @@ -0,0 +1 @@ +# This file intentionally has no queries diff --git a/clang-tools-extra/test/clang-query/Inputs/file.script b/clang-tools-extra/test/clang-query/Inputs/file.script new file mode 100644 index 0000000000000000000000000000000000000000..b58e7bbc24bfb917e979c1946e9efae7dfa40f97 --- /dev/null +++ b/clang-tools-extra/test/clang-query/Inputs/file.script @@ -0,0 +1 @@ +f DIRECTORY/runtime_file.script diff --git a/clang-tools-extra/test/clang-query/Inputs/runtime_file.script b/clang-tools-extra/test/clang-query/Inputs/runtime_file.script new file mode 100644 index 0000000000000000000000000000000000000000..714d7f03b1bf6aaa4dc6b40d1c048de29cfd7f20 --- /dev/null +++ b/clang-tools-extra/test/clang-query/Inputs/runtime_file.script @@ -0,0 +1,5 @@ +set bind-root false + +l func functionDecl(hasName("bar")) +m func.bind("f") +m varDecl().bind("v") \ No newline at end of file diff --git a/clang-tools-extra/test/clang-query/errors.c b/clang-tools-extra/test/clang-query/errors.c index bbb742125744f9bc96ad36b22302fa794a67332b..3b9059ab0257f4448a525ee60ad5cb1f2f1cf0c3 100644 --- a/clang-tools-extra/test/clang-query/errors.c +++ b/clang-tools-extra/test/clang-query/errors.c @@ -1,10 +1,12 @@ // RUN: not clang-query -c foo -c bar %s -- | FileCheck %s // RUN: not clang-query -f %S/Inputs/foo.script %s -- | FileCheck %s // RUN: not clang-query -f %S/Inputs/nonexistent.script %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT %s +// RUN: not clang-query -c 'file %S/Inputs/nonexistent.script' %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT-FILEQUERY %s // RUN: not clang-query -c foo -f foo %s -- 2>&1 | FileCheck --check-prefix=CHECK-BOTH %s // CHECK: unknown command: foo // CHECK-NOT: unknown command: bar // CHECK-NONEXISTENT: cannot open {{.*}}nonexistent.script +// CHECK-NONEXISTENT-FILEQUERY: cannot open {{.*}}nonexistent.script // CHECK-BOTH: cannot specify both -c and -f diff --git a/clang-tools-extra/test/clang-query/file-empty.c b/clang-tools-extra/test/clang-query/file-empty.c new file mode 100644 index 0000000000000000000000000000000000000000..15137c57e915ea561e85201d8427021f18bc4336 --- /dev/null +++ b/clang-tools-extra/test/clang-query/file-empty.c @@ -0,0 +1,2 @@ +// RUN: clang-query -c 'file %S/Inputs/empty.script' %s -- +// COM: no output expected; nothing to CHECK diff --git a/clang-tools-extra/test/clang-query/file-query.c b/clang-tools-extra/test/clang-query/file-query.c new file mode 100644 index 0000000000000000000000000000000000000000..10a44e7aaccf24f14d6668620c7ed1251d3fa7ca --- /dev/null +++ b/clang-tools-extra/test/clang-query/file-query.c @@ -0,0 +1,14 @@ +// RUN: rm -rf %/t +// RUN: mkdir %/t +// RUN: cp %/S/Inputs/file.script %/t/file.script +// RUN: cp %/S/Inputs/runtime_file.script %/t/runtime_file.script +// Need to embed the correct temp path in the actual JSON-RPC requests. +// RUN: sed -e "s|DIRECTORY|%/t|" %/t/file.script > %/t/file.script.temp + +// RUN: clang-query -c 'file %/t/file.script.temp' %s -- | FileCheck %s + +// CHECK: file-query.c:11:1: note: "f" binds here +void bar(void) {} + +// CHECK: file-query.c:14:1: note: "v" binds here +int baz{1}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string index d031f27beb9dfef99d61b44bff19a6dc8e1a885f..0c160bc182b6ebdf4ddce7f460ad38c8fd8a99a4 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string @@ -108,6 +108,8 @@ struct basic_string_view { constexpr bool starts_with(C ch) const noexcept; constexpr bool starts_with(const C* s) const; + constexpr int compare(basic_string_view sv) const noexcept; + static constexpr size_t npos = -1; }; @@ -132,6 +134,14 @@ bool operator==(const std::wstring&, const std::wstring&); bool operator==(const std::wstring&, const wchar_t*); bool operator==(const wchar_t*, const std::wstring&); +bool operator==(const std::string_view&, const std::string_view&); +bool operator==(const std::string_view&, const char*); +bool operator==(const char*, const std::string_view&); + +bool operator!=(const std::string_view&, const std::string_view&); +bool operator!=(const std::string_view&, const char*); +bool operator!=(const char*, const std::string_view&); + size_t strlen(const char* str); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp index 3913d2d8a295c75e75c49c0503fa32a364959a33..a784e498858738967066381585b8c958140182ca 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/casting-through-void.cpp @@ -89,3 +89,10 @@ void bit_cast() { __builtin_bit_cast(int *, static_cast(&d)); // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not cast 'double *' to 'int *' through 'void *' [bugprone-casting-through-void] } + +namespace PR87069 { + void castconstVoidToVoid() { + const void* ptr = nullptr; + int* numberPtr = static_cast(const_cast(ptr)); + } +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp index a83a019ec7437da6ccda446b20af87f0639ee303..ca41bdf74a1073d1dc1dfb125f1b13944bdc835d 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/return-const-ref-from-parameter.cpp @@ -1,9 +1,18 @@ -// RUN: %check_clang_tidy %s bugprone-return-const-ref-from-parameter %t +// RUN: %check_clang_tidy %s bugprone-return-const-ref-from-parameter %t -- -- -fno-delayed-template-parsing using T = int; using TConst = int const; using TConstRef = int const&; +template +struct Wrapper { Wrapper(T); }; + +template +struct Identity { using type = T; }; + +template +struct ConstRef { using type = const T&; }; + namespace invalid { int const &f1(int const &a) { return a; } @@ -18,8 +27,59 @@ int const &f3(TConstRef a) { return a; } int const &f4(TConst &a) { return a; } // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter +template +const T& tf1(const T &a) { return a; } +// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: returning a constant reference parameter + +template +const T& itf1(const T &a) { return a; } +// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: returning a constant reference parameter + +template +typename ConstRef::type itf2(const T &a) { return a; } +// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter + +template +typename ConstRef::type itf3(typename ConstRef::type a) { return a; } +// CHECK-MESSAGES: :[[@LINE-1]]:72: warning: returning a constant reference parameter + +template +const T& itf4(typename ConstRef::type a) { return a; } +// CHECK-MESSAGES: :[[@LINE-1]]:54: warning: returning a constant reference parameter + +void instantiate(const int ¶m, const float ¶mf, int &mut_param, float &mut_paramf) { + itf1(0); + itf1(param); + itf1(paramf); + itf2(0); + itf2(param); + itf2(paramf); + itf3(0); + itf3(param); + itf3(paramf); + itf4(0); + itf4(param); + itf4(paramf); +} + +struct C { + const C& foo(const C&c) { return c; } +// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: returning a constant reference parameter +}; + } // namespace invalid +namespace false_negative_because_dependent_and_not_instantiated { +template +typename ConstRef::type tf2(const T &a) { return a; } + +template +typename ConstRef::type tf3(typename ConstRef::type a) { return a; } + +template +const T& tf4(typename ConstRef::type a) { return a; } +} // false_negative_because_dependent_and_not_instantiated + namespace valid { int const &f1(int &a) { return a; } @@ -28,4 +88,58 @@ int const &f2(int &&a) { return a; } int f1(int const &a) { return a; } +template +T tf1(T a) { return a; } + +template +T tf2(const T a) { return a; } + +template +T tf3(const T &a) { return a; } + +template +Identity::type tf4(const T &a) { return a; } + +template +T itf1(T a) { return a; } + +template +T itf2(const T a) { return a; } + +template +T itf3(const T &a) { return a; } + +template +Wrapper itf4(const T& a) { return a; } + +template +const T& itf5(T& a) { return a; } + +template +T itf6(T& a) { return a; } + +void instantiate(const int ¶m, const float ¶mf, int &mut_param, float &mut_paramf) { + itf1(0); + itf1(param); + itf1(paramf); + itf2(0); + itf2(param); + itf2(paramf); + itf3(0); + itf3(param); + itf3(paramf); + itf2(0); + itf2(param); + itf2(paramf); + itf3(0); + itf3(param); + itf3(paramf); + itf4(param); + itf4(paramf); + itf5(mut_param); + itf5(mut_paramf); + itf6(mut_param); + itf6(mut_paramf); +} + } // namespace valid diff --git a/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp b/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp index edbb56f90cb0e1ea421676b90dd4444b8a638184..aca7ae1fd76fbe6172c9d5b264703cc1d44c0450 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp @@ -11,6 +11,7 @@ void examples() { // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use of a signed integer operand with a binary bitwise operator unsigned URes2 = URes << 1; //Ok + unsigned URes3 = URes & 1; //Ok int IResult; IResult = 10 & 2; //Ok @@ -21,6 +22,8 @@ void examples() { IResult = Int << 1; // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator IResult = ~0; //Ok + IResult = -1 & 1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise] } enum EnumConstruction { diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include.h b/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include.h index bf288023274b155ccbe453d79097a67a09884f7e..22d3a3acbc916ed7bf889572da8d7efa9b026697 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include.h +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include.h @@ -1,15 +1,15 @@ -#ifndef READABILITY_DUPLICATE_INCLUDE_H -#define READABILITY_DUPLICATE_INCLUDE_H - -extern int g; -#include "duplicate-include2.h" -extern int h; -#include "duplicate-include2.h" -extern int i; -// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: duplicate include -// CHECK-FIXES: {{^extern int g;$}} -// CHECK-FIXES-NEXT: {{^#include "duplicate-include2.h"$}} -// CHECK-FIXES-NEXT: {{^extern int h;$}} -// CHECK-FIXES-NEXT: {{^extern int i;$}} - -#endif +#ifndef READABILITY_DUPLICATE_INCLUDE_H +#define READABILITY_DUPLICATE_INCLUDE_H + +extern int g; +#include "duplicate-include2.h" +extern int h; +#include "duplicate-include2.h" +extern int i; +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: duplicate include +// CHECK-FIXES: {{^extern int g;$}} +// CHECK-FIXES-NEXT: {{^#include "duplicate-include2.h"$}} +// CHECK-FIXES-NEXT: {{^extern int h;$}} +// CHECK-FIXES-NEXT: {{^extern int i;$}} + +#endif diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include2.h b/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include2.h index 58dfa757ee7aedd584aca417031b86f8b2c9e79b..fcbabe12fc378aae5a59689b70b776cb80892088 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include2.h +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/duplicate-include2.h @@ -1 +1 @@ -// This file is intentionally empty. +// This file is intentionally empty. diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/system/sys/types.h b/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/system/sys/types.h index 58dfa757ee7aedd584aca417031b86f8b2c9e79b..fcbabe12fc378aae5a59689b70b776cb80892088 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/system/sys/types.h +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/Inputs/duplicate-include/system/sys/types.h @@ -1 +1 @@ -// This file is intentionally empty. +// This file is intentionally empty. diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp index 10b2858c9caa820dbc3d54cae6cc36678afc92e2..76a3555663b180e72e8da519b5a7f26308624eb0 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/const-return-type.cpp @@ -215,11 +215,9 @@ CREATE_FUNCTION(); using ty = const int; ty p21() {} -// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty' (aka 'const int') is typedef const int ty2; ty2 p22() {} -// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'ty2' (aka 'const int') i // Declaration uses a macro, while definition doesn't. In this case, we won't // fix the declaration, and will instead issue a warning. @@ -249,7 +247,6 @@ auto p27() -> int const { return 3; } // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const int' is 'const'-qu std::add_const::type p28() { return 3; } -// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'std::add_const::typ // p29, p30 are based on // llvm/projects/test-suite/SingleSource/Benchmarks/Misc-C++-EH/spirit.cpp: @@ -355,3 +352,20 @@ struct p41 { // CHECK-FIXES: T foo() const { return 2; } }; template struct p41; + +namespace PR73270 { + template + struct Pair { + using first_type = const K; + using second_type = V; + }; + + template + typename PairType::first_type getFirst() { + return {}; + } + + void test() { + getFirst>(); + } +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return-if-constexpr.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return-if-constexpr.cpp index 6532940eaf2314b2e2621246e2bbe57bc8c88a4c..1edb3237eaf4d6a380c5abbf191e4bac5e30758a 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return-if-constexpr.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return-if-constexpr.cpp @@ -1,22 +1,22 @@ -// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++17 - -// Constexpr if is an exception to the rule, we cannot remove the else. -void f() { - if (sizeof(int) > 4) - return; - else - return; - // CHECK-MESSAGES: [[@LINE-2]]:3: warning: do not use 'else' after 'return' - - if constexpr (sizeof(int) > 4) - return; - else - return; - - if constexpr (sizeof(int) > 4) - return; - else if constexpr (sizeof(long) > 4) - return; - else - return; -} +// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++17 + +// Constexpr if is an exception to the rule, we cannot remove the else. +void f() { + if (sizeof(int) > 4) + return; + else + return; + // CHECK-MESSAGES: [[@LINE-2]]:3: warning: do not use 'else' after 'return' + + if constexpr (sizeof(int) > 4) + return; + else + return; + + if constexpr (sizeof(int) > 4) + return; + else if constexpr (sizeof(long) > 4) + return; + else + return; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/magic-numbers-todo.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/magic-numbers-todo.cpp deleted file mode 100644 index 99d9be262a8979d40d65508615795014403f719a..0000000000000000000000000000000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/magic-numbers-todo.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %check_clang_tidy %s readability-magic-numbers %t -- -// XFAIL: * - -int ProcessSomething(int input); - -int DoWork() -{ - if (((int)4) > ProcessSomething(10)) - // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 4 is a magic number; consider replacing it with a named constant [readability-magic-numbers] - return 0; - - return 0; -} - - diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp index 7d0cfe7e27dc2206e673d7c590992b342a09bf9b..d1df79e23a1e6f9402e3056f1941b482e19aebab 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp @@ -6,6 +6,7 @@ // RUN: -- #define NEGATE(expr) !(expr) +#define NOT_AND_NOT(a, b) (!a && !b) bool without_macro(bool a, bool b) { return !(!a && b); @@ -13,8 +14,17 @@ bool without_macro(bool a, bool b) { // CHECK-FIXES: return a || !b; } -bool macro(bool a, bool b) { - return NEGATE(!a && b); - // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:12: warning: boolean expression can be simplified by DeMorgan's theorem - // CHECK-FIXES: return NEGATE(!a && b); +void macro(bool a, bool b) { + NEGATE(!a && b); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: NEGATE(!a && b); + !NOT_AND_NOT(a, b); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: !NOT_AND_NOT(a, b); + !(NEGATE(a) && b); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: !(NEGATE(a) && b); + !(a && NEGATE(b)); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: !(a && NEGATE(b)); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp index 81c1cecf607f660548ac7eb351a58c306262a4b1..202fe9be6d00c530e7cce1fadda783101bef9fb4 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s readability-static-accessed-through-instance %t -- -- -isystem %S/Inputs/static-accessed-through-instance +// RUN: %check_clang_tidy %s readability-static-accessed-through-instance %t -- --fix-notes -- -isystem %S/Inputs/static-accessed-through-instance #include <__clang_cuda_builtin_vars.h> enum OutEnum { @@ -47,7 +47,8 @@ C &f(int, int, int, int); void g() { f(1, 2, 3, 4).x; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance] - // CHECK-FIXES: {{^}} f(1, 2, 3, 4).x;{{$}} + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} C::x;{{$}} } int i(int &); @@ -59,12 +60,14 @@ int k(bool); void f(C c) { j(i(h().x)); // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: static member - // CHECK-FIXES: {{^}} j(i(h().x));{{$}} + // CHECK-MESSAGES: :[[@LINE-2]]:7: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} j(i(C::x));{{$}} // The execution of h() depends on the return value of a(). j(k(a() && h().x)); // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: static member - // CHECK-FIXES: {{^}} j(k(a() && h().x));{{$}} + // CHECK-MESSAGES: :[[@LINE-2]]:14: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} j(k(a() && C::x));{{$}} if ([c]() { c.ns(); @@ -72,7 +75,8 @@ void f(C c) { }().x == 15) ; // CHECK-MESSAGES: :[[@LINE-5]]:7: warning: static member - // CHECK-FIXES: {{^}} if ([c]() {{{$}} + // CHECK-MESSAGES: :[[@LINE-6]]:7: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} if (C::x == 15){{$}} } // Nested specifiers @@ -261,8 +265,11 @@ struct Qptr { }; int func(Qptr qp) { - qp->y = 10; // OK, the overloaded operator might have side-effects. - qp->K = 10; // + qp->y = 10; + qp->K = 10; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance] + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} Q::K = 10; } namespace { @@ -380,3 +387,20 @@ namespace PR51861 { // CHECK-FIXES: {{^}} PR51861::Foo::getBar();{{$}} } } + +namespace PR75163 { + struct Static { + static void call(); + }; + + struct Ptr { + Static* operator->(); + }; + + void test(Ptr& ptr) { + ptr->call(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: static member accessed through instance [readability-static-accessed-through-instance] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} PR75163::Static::call();{{$}} + } +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare-custom-string-classes.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare-custom-string-classes.cpp new file mode 100644 index 0000000000000000000000000000000000000000..faf135833ee15dd823bfcba4f33c0a973db34acf --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare-custom-string-classes.cpp @@ -0,0 +1,35 @@ +// RUN: %check_clang_tidy %s readability-string-compare %t -- -config='{CheckOptions: {readability-string-compare.StringLikeClasses: "CustomStringTemplateBase;CustomStringNonTemplateBase"}}' -- -isystem %clang_tidy_headers +#include + +struct CustomStringNonTemplateBase { + int compare(const CustomStringNonTemplateBase& Other) const { + return 123; // value is not important for check + } +}; + +template +struct CustomStringTemplateBase { + int compare(const CustomStringTemplateBase& Other) const { + return 123; + } +}; + +struct CustomString1 : CustomStringNonTemplateBase {}; +struct CustomString2 : CustomStringTemplateBase {}; + +void CustomStringClasses() { + std::string_view sv1("a"); + std::string_view sv2("b"); + if (sv1.compare(sv2)) { // No warning - if a std class is not listed in StringLikeClasses, it won't be checked. + } + + CustomString1 custom1; + if (custom1.compare(custom1)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] + + CustomString2 custom2; + if (custom2.compare(custom2)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp index 2c08b86cf72fa02c6d0db353c897bf19e9e903ff..c4fea4341617b8c4d59a53ebe07d3ee5fb7e064f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp @@ -67,11 +67,27 @@ void Test() { if (str1.compare(comp())) { } // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; + + std::string_view sv1("a"); + std::string_view sv2("b"); + if (sv1.compare(sv2)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] +} + +struct DerivedFromStdString : std::string {}; + +void TestDerivedClass() { + DerivedFromStdString derived; + if (derived.compare(derived)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] } void Valid() { std::string str1("a", 1); std::string str2("b", 1); + if (str1 == str2) { } if (str1 != str2) { @@ -96,4 +112,11 @@ void Valid() { } if (str1.compare(str2) == -1) { } + + std::string_view sv1("a"); + std::string_view sv2("b"); + if (sv1 == sv2) { + } + if (sv1.compare(sv2) > 0) { + } } diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp index cb0f0bc4d133085e71d01dd9b96032f75166f887..d287412454cadd5b2b1ebb72375743e65f962273 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp @@ -66,9 +66,5 @@ // RUN: clang-tidy --checks="-*,readability-identifier-naming" --dump-config %S/Inputs/config-files/- -- | grep "readability-identifier-naming\." | sort --check // Dumped config does not overflow for unsigned options -// RUN: clang-tidy --dump-config \ -// RUN: --checks="-*,misc-throw-by-value-catch-by-reference" \ -// RUN: -- | grep -v -q "misc-throw-by-value-catch-by-reference.MaxSize: '-1'" - -// RUN: clang-tidy --dump-config %S/Inputs/config-files/5/- \ -// RUN: -- | grep -q "misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976'" +// RUN: clang-tidy --dump-config %S/Inputs/config-files/5/- -- | FileCheck %s -check-prefix=CHECK-OVERFLOW +// CHECK-OVERFLOW: misc-throw-by-value-catch-by-reference.MaxSize: '1152921504606846976' diff --git a/clang-tools-extra/test/modularize/Inputs/CompileError/module.modulemap b/clang-tools-extra/test/modularize/Inputs/CompileError/module.modulemap index 64180adf5beb358bc34636b89a7ee8f71d34b8b1..f71b66f148ed1920bede5f2c47216a0d396f77d8 100644 --- a/clang-tools-extra/test/modularize/Inputs/CompileError/module.modulemap +++ b/clang-tools-extra/test/modularize/Inputs/CompileError/module.modulemap @@ -1,10 +1,10 @@ -// module.modulemap - -module Level1A { - header "Level1A.h" - export * -} -module HasError { - header "HasError.h" - export * -} +// module.modulemap + +module Level1A { + header "Level1A.h" + export * +} +module HasError { + header "HasError.h" + export * +} diff --git a/clang-tools-extra/test/modularize/Inputs/MissingHeader/module.modulemap b/clang-tools-extra/test/modularize/Inputs/MissingHeader/module.modulemap index 9acb4923f9ac3747548fc900c2c01ab138a08b23..330e13f5ee1558a11f96a2ee83f5222237c5cd51 100644 --- a/clang-tools-extra/test/modularize/Inputs/MissingHeader/module.modulemap +++ b/clang-tools-extra/test/modularize/Inputs/MissingHeader/module.modulemap @@ -1,10 +1,10 @@ -// module.modulemap - -module Level1A { - header "Level1A.h" - export * -} -module Missing { - header "Missing.h" - export * -} +// module.modulemap + +module Level1A { + header "Level1A.h" + export * +} +module Missing { + header "Missing.h" + export * +} diff --git a/clang-tools-extra/test/pp-trace/Inputs/module.modulemap b/clang-tools-extra/test/pp-trace/Inputs/module.modulemap index f16bbc6e2e05b4d50ab56ce84b9efa1b5cffad06..415c874f09d373a3d929f2f2d76cd28a79484622 100644 --- a/clang-tools-extra/test/pp-trace/Inputs/module.modulemap +++ b/clang-tools-extra/test/pp-trace/Inputs/module.modulemap @@ -1,18 +1,18 @@ -// module.modulemap - -module Level1A { - header "Level1A.h" - export * -} -module Level1B { - header "Level1B.h" - export * - module Level2B { - header "Level2B.h" - export * - } -} -module Level2A { - header "Level2A.h" - export * -} +// module.modulemap + +module Level1A { + header "Level1A.h" + export * +} +module Level1B { + header "Level1B.h" + export * + module Level2B { + header "Level2B.h" + export * + } +} +module Level2A { + header "Level2A.h" + export * +} diff --git a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp index 06b0d7b365904e74840a2cd4bb6d4135b179d6eb..b561e2bb98332113021670dceea19e1104902465 100644 --- a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp +++ b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp @@ -197,7 +197,7 @@ TEST_F(QueryParserTest, Comment) { TEST_F(QueryParserTest, Complete) { std::vector Comps = QueryParser::complete("", 0, QS); - ASSERT_EQ(8u, Comps.size()); + ASSERT_EQ(9u, Comps.size()); EXPECT_EQ("help ", Comps[0].TypedText); EXPECT_EQ("help", Comps[0].DisplayText); EXPECT_EQ("let ", Comps[1].TypedText); @@ -214,6 +214,8 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("disable", Comps[6].DisplayText); EXPECT_EQ("unlet ", Comps[7].TypedText); EXPECT_EQ("unlet", Comps[7].DisplayText); + EXPECT_EQ("file ", Comps[8].TypedText); + EXPECT_EQ("file", Comps[8].DisplayText); Comps = QueryParser::complete("set o", 5, QS); ASSERT_EQ(1u, Comps.size()); diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index cf97e3c6e851aee92dd5378b85515304fc875999..c20ce47a12abbd8390ce66a57efde38ac6b52125 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -523,6 +523,8 @@ endif() if( CLANG_INCLUDE_TESTS ) + find_package(Perl) + add_subdirectory(unittests) list(APPEND CLANG_TEST_DEPS ClangUnitTests) list(APPEND CLANG_TEST_PARAMS diff --git a/clang/docs/Block-ABI-Apple.rst b/clang/docs/Block-ABI-Apple.rst index 68f7a3819ca22e81d055566478b9b407f68cdef2..f46f2f991ad7f1d085414e7e77058d0b26686b96 100644 --- a/clang/docs/Block-ABI-Apple.rst +++ b/clang/docs/Block-ABI-Apple.rst @@ -80,7 +80,7 @@ The following flags bits are in use thusly for a possible ABI.2010.3.16: In 10.6.ABI the (1<<29) was usually set and was always ignored by the runtime - it had been a transitional marker that did not get deleted after the transition. This bit is now paired with (1<<30), and represented as the pair -(3<<30), for the following combinations of valid bit settings, and their +(3<<29), for the following combinations of valid bit settings, and their meanings: .. code-block:: c diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 39f7cded36edbff5846925aa56319682a04ed476..6d092219877f91f68d46025cf550e37531f029d5 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -861,7 +861,8 @@ the configuration (without a prefix: ``Auto``). **AlignConsecutiveShortCaseStatements** (``ShortCaseStatementsAlignmentStyle``) :versionbadge:`clang-format 17` :ref:`¶ ` Style of aligning consecutive short case labels. - Only applies if ``AllowShortCaseLabelsOnASingleLine`` is ``true``. + Only applies if ``AllowShortCaseExpressionOnASingleLine`` or + ``AllowShortCaseLabelsOnASingleLine`` is ``true``. .. code-block:: yaml @@ -935,8 +936,26 @@ the configuration (without a prefix: ``Auto``). default: return ""; } - * ``bool AlignCaseColons`` Whether aligned case labels are aligned on the colon, or on the - , or on the tokens after the colon. + * ``bool AlignCaseArrows`` Whether to align the case arrows when aligning short case expressions. + + .. code-block:: java + + true: + i = switch (day) { + case THURSDAY, SATURDAY -> 8; + case WEDNESDAY -> 9; + default -> 0; + }; + + false: + i = switch (day) { + case THURSDAY, SATURDAY -> 8; + case WEDNESDAY -> 9; + default -> 0; + }; + + * ``bool AlignCaseColons`` Whether aligned case labels are aligned on the colon, or on the tokens + after the colon. .. code-block:: c++ @@ -1692,6 +1711,21 @@ the configuration (without a prefix: ``Auto``). +.. _AllowShortCaseExpressionOnASingleLine: + +**AllowShortCaseExpressionOnASingleLine** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ ` + Whether to merge a short switch labeled rule into a single line. + + .. code-block:: java + + true: false: + switch (a) { vs. switch (a) { + case 1 -> 1; case 1 -> + default -> 0; 1; + }; default -> + 0; + }; + .. _AllowShortCaseLabelsOnASingleLine: **AllowShortCaseLabelsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 3.6` :ref:`¶ ` diff --git a/clang/docs/ClangOffloadBundler.rst b/clang/docs/ClangOffloadBundler.rst index 515e6c00a3b8003083508e562812317338314206..3c241027d405cae7c32b33eca7b528ab4ad3a994 100644 --- a/clang/docs/ClangOffloadBundler.rst +++ b/clang/docs/ClangOffloadBundler.rst @@ -245,7 +245,7 @@ Where: object as a data section with the name ``.hip_fatbin``. hipv4 Offload code object for the HIP language. Used for AMD GPU - code objects with at least ABI version V4 when the + code objects with at least ABI version V4 and above when the ``clang-offload-bundler`` is used to create a *fat binary* to be loaded by the HIP runtime. The fat binary can be loaded directly from a file, or be embedded in the host code @@ -254,6 +254,14 @@ Where: openmp Offload code object for the OpenMP language extension. ============= ============================================================== +Note: The distinction between the `hip` and `hipv4` offload kinds is historically based. +Originally, these designations might have indicated different versions of the +code object ABI. However, as the system has evolved, the ABI version is now embedded +directly within the code object itself, making these historical distinctions irrelevant +during the unbundling process. Consequently, `hip` and `hipv4` are treated as compatible +in current implementations, facilitating interchangeable handling of code objects +without differentiation based on offload kind. + **target-triple** The target triple of the code object. See `Target Triple `_. @@ -295,7 +303,7 @@ Compatibility Rules for Bundle Entry ID A code object, specified using its Bundle Entry ID, can be loaded and executed on a target processor, if: - * Their offload kinds are the same. + * Their offload kinds are the same or comptible. * Their target triples are compatible. * Their Target IDs are compatible as defined in :ref:`compatibility-target-id`. diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index c2e90f4e7d587ada3c26a321def23227b56ed95f..a09c409f8f91a3da3b3a47ba15b846fd8103a5a6 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -656,6 +656,7 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in T __builtin_elementwise_ceil(T x) return the smallest integral value greater than or equal to x floating point types 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_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 @@ -1661,8 +1662,11 @@ The following type trait primitives are supported by Clang. Those traits marked ``T`` from ``U`` is ill-formed. Deprecated, use ``__reference_constructs_from_temporary``. * ``__reference_constructs_from_temporary(T, U)`` (C++) - Returns true if a reference ``T`` can be constructed from a temporary of type + Returns true if a reference ``T`` can be direct-initialized from a temporary of type a non-cv-qualified ``U``. +* ``__reference_converts_from_temporary(T, U)`` (C++) + Returns true if a reference ``T`` can be copy-initialized from a temporary of type + a non-cv-qualified ``U``. * ``__underlying_type`` (C++, GNU, Microsoft) In addition, the following expression traits are supported: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0e3f7cf89ca88517c78e4d0cb32b3198d3d7d9e8..4702b8c10cdbb3e1ed175b0e634070bf87ccc8b0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -48,6 +48,13 @@ C++ Specific Potentially Breaking Changes - Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template void T();``. This error can be disabled via `-Wno-strict-primary-template-shadow` for compatibility with previous versions of clang. +- The behavior controlled by the `-frelaxed-template-template-args` flag is now + on by default, and the flag is deprecated. Until the flag is finally removed, + it's negative spelling can be used to obtain compatibility with previous + versions of clang. + +- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906). + ABI Changes in This Version --------------------------- - Fixed Microsoft name mangling of implicitly defined variables used for thread @@ -69,6 +76,9 @@ ABI Changes in This Version returning a class in a register. This affects some uses of std::pair. (#GH86384). +- Fixed Microsoft calling convention when returning classes that have a deleted + copy assignment operator. Such a class should be returned indirectly. + AST Dumping Potentially Breaking Changes ---------------------------------------- @@ -85,6 +95,25 @@ Clang Frontend Potentially Breaking Changes of ``-Wno-gnu-binary-literal`` will no longer silence this pedantic warning, which may break existing uses with ``-Werror``. +- The normalization of 3 element target triples where ``-none-`` is the middle + element has changed. For example, ``armv7m-none-eabi`` previously normalized + to ``armv7m-none-unknown-eabi``, with ``none`` for the vendor and ``unknown`` + for the operating system. It now normalizes to ``armv7m-unknown-none-eabi``, + which has ``unknown`` vendor and ``none`` operating system. + + The affected triples are primarily for bare metal Arm where it is intended + that ``none`` means that there is no operating system. As opposed to an unknown + type of operating system. + + This change my cause clang to not find libraries, or libraries to be built at + different file system locations. This can be fixed by changing your builds to + use the new normalized triple. However, we recommend instead getting the + normalized triple from clang itself, as this will make your builds more + robust in case of future changes:: + + $ clang --target= -print-target-triple + + What's New in Clang |release|? ============================== Some of the major new features and improvements to Clang are listed @@ -94,6 +123,17 @@ sections with improvements to Clang's support for those languages. C++ Language Changes -------------------- +- C++17 support is now completed, with the enablement of the + relaxed temlate template argument matching rules introduced in P0522, + which was retroactively applied as a defect report. + While the implementation already existed since Clang 4, it was turned off by + default, and was controlled with the `-frelaxed-template-template-args` flag. + In this release, we implement provisional wording for a core defect on + P0522 (CWG2398), which avoids the most serious compatibility issues caused + by it, allowing us to enable it by default in this release. + The flag is now deprecated, and will be removed in the next release, but can + still be used to turn it off and regain compatibility with previous versions + (#GH36505). - Implemented ``_BitInt`` literal suffixes ``__wb`` or ``__WB`` as a Clang extension with ``unsigned`` modifiers also allowed. (#GH85223). C++17 Feature Support @@ -142,6 +182,9 @@ C++23 Feature Support - Implemented `P2448R2: Relaxing some constexpr restrictions `_. +- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for + `P2255R2: Type trait to determine if a reference binds to a temporary `_. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -153,6 +196,9 @@ C++2c Feature Support - Implemented `P2748R5 Disallow Binding a Returned Glvalue to a Temporary `_. +- Implemented `P2809R3: Trivial infinite loops are not Undefined Behavior `_. + + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Substitute template parameter pack, when it is not explicitly specified @@ -173,6 +219,9 @@ Resolutions to C++ Defect Reports - Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers. (`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers `_). +- P0522 implementation is enabled by default in all language versions, and + provisional wording for CWG2398 is implemented. + C Language Changes ------------------ @@ -260,6 +309,11 @@ New Compiler Flags allow late parsing certain attributes in specific contexts where they would not normally be late parsed. +- ``-fseparate-named-sections`` uses separate unique sections for global + symbols in named special sections (i.e. symbols annotated with + ``__attribute__((section(...)))``. This enables linker GC to collect unused + symbols without having to use a per-symbol section. + Deprecated Compiler Flags ------------------------- @@ -294,6 +348,10 @@ Modified Compiler Flags - Carved out ``-Wformat`` warning about scoped enums into a subwarning and make it controlled by ``-Wformat-pedantic``. Fixes #GH88595. +- Trivial infinite loops (i.e loops with a constant controlling expresion + evaluating to ``true`` and an empty body such as ``while(1);``) + are considered infinite, even when the ``-ffinite-loop`` flag is set. + Removed Compiler Flags ------------------------- @@ -502,6 +560,9 @@ Bug Fixes in This Version The values of 0 and 1 block any unrolling of the loop. This keeps the same behavior with GCC. Fixes (`#88624 `_). +- Clang will no longer emit a duplicate -Wunused-value warning for an expression + `(A, B)` which evaluates to glvalue `B` that can be converted to non ODR-use. (#GH45783) + Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -630,6 +691,23 @@ Bug Fixes to C++ Support - Fix a bug on template partial specialization with issue on deduction of nontype template parameter whose type is `decltype(auto)`. Fixes (#GH68885). - Clang now correctly treats the noexcept-specifier of a friend function to be a complete-class context. +- Fix an assertion failure when parsing an invalid members of an anonymous class. (#GH85447) +- Fixed a misuse of ``UnresolvedLookupExpr`` for ill-formed templated expressions. Fixes (#GH48673), (#GH63243) + and (#GH88832). +- Clang now defers all substitution into the exception specification of a function template specialization + until the noexcept-specifier is instantiated. +- Fix a crash when an implicitly declared ``operator==`` function with a trailing requires-clause has its + constraints compared to that of another declaration. +- Fix a bug where explicit specializations of member functions/function templates would have substitution + performed incorrectly when checking constraints. Fixes (#GH90349). +- Clang now allows constrained member functions to be explicitly specialized for an implicit instantiation + of a class template. +- Fix a C++23 bug in implementation of P2564R3 which evaluates immediate invocations in place + within initializers for variables that are usable in constant expressions or are constant + initialized, rather than evaluating them as a part of the larger manifestly constant evaluated + expression. +- Fix a bug in access control checking due to dealyed checking of friend declaration. Fixes (#GH12361). +- Correctly treat the compound statement of an ``if consteval`` as an immediate context. Fixes (#GH91509). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -729,6 +807,7 @@ CUDA/HIP Language Changes CUDA Support ^^^^^^^^^^^^ +- Clang now supports CUDA SDK up to 12.4 AIX Support ^^^^^^^^^^^ @@ -781,6 +860,9 @@ clang-format ``BreakTemplateDeclarations``. - ``AlwaysBreakAfterReturnType`` is deprecated and renamed to ``BreakAfterReturnType``. +- Handles Java ``switch`` expressions. +- Adds ``AllowShortCaseExpressionOnASingleLine`` option. +- Adds ``AlignCaseArrows`` suboption to ``AlignConsecutiveShortCaseStatements``. libclang -------- diff --git a/clang/docs/StandardCPlusPlusModules.rst b/clang/docs/StandardCPlusPlusModules.rst index ee57fb5da6485769213f8485ac0e7a7f9fbd774d..1c3c4d319c0e18970a82c83af65fba33cfd9cd89 100644 --- a/clang/docs/StandardCPlusPlusModules.rst +++ b/clang/docs/StandardCPlusPlusModules.rst @@ -8,109 +8,92 @@ Standard C++ Modules Introduction ============ -The term ``modules`` has a lot of meanings. For the users of Clang, modules may -refer to ``Objective-C Modules``, ``Clang C++ Modules`` (or ``Clang Header Modules``, -etc.) or ``Standard C++ Modules``. The implementation of all these kinds of modules in Clang -has a lot of shared code, but from the perspective of users, their semantics and -command line interfaces are very different. This document focuses on -an introduction of how to use standard C++ modules in Clang. - -There is already a detailed document about `Clang modules `_, it -should be helpful to read `Clang modules `_ if you want to know -more about the general idea of modules. Since standard C++ modules have different semantics -(and work flows) from `Clang modules`, this page describes the background and use of -Clang with standard C++ modules. - -Modules exist in two forms in the C++ Language Specification. They can refer to -either "Named Modules" or to "Header Units". This document covers both forms. +The term ``module`` is ambiguous, as it is used to mean multiple things in +Clang. For Clang users, a module may refer to an ``Objective-C Module``, +`Clang Module `_ (also called a ``Clang Header Module``) or a +``C++20 Module`` (or a ``Standard C++ Module``). The implementation of all +these kinds of modules in Clang shares a lot of code, but from the perspective +of users their semantics and command line interfaces are very different. This +document is an introduction to the use of C++20 modules in Clang. In the +remainder of this document, the term ``module`` will refer to Standard C++20 +modules and the term ``Clang module`` will refer to the Clang Modules +extension. + +In terms of the C++ Standard, modules consist of two components: "Named +Modules" or "Header Units". This document covers both. Standard C++ Named modules ========================== -This document was intended to be a manual first and foremost, however, we consider it helpful to -introduce some language background here for readers who are not familiar with -the new language feature. This document is not intended to be a language -tutorial; it will only introduce necessary concepts about the -structure and building of the project. +In order to better understand the compiler's behavior, it is helpful to +understand some terms and definitions for readers who are not familiar with the +C++ feature. This document is not a tutorial on C++; it only introduces +necessary concepts to better understand use of modules in a project. Background and terminology -------------------------- -Modules -~~~~~~~ - -In this document, the term ``Modules``/``modules`` refers to standard C++ modules -feature if it is not decorated by ``Clang``. - -Clang Modules -~~~~~~~~~~~~~ - -In this document, the term ``Clang Modules``/``Clang modules`` refer to Clang -c++ modules extension. These are also known as ``Clang header modules``, -``Clang module map modules`` or ``Clang c++ modules``. - Module and module unit ~~~~~~~~~~~~~~~~~~~~~~ -A module consists of one or more module units. A module unit is a special -translation unit. Every module unit must have a module declaration. The syntax -of the module declaration is: +A module consists of one or more module units. A module unit is a special kind +of translation unit. A module unit should almost always start with a module +declaration. The syntax of the module declaration is: .. code-block:: c++ [export] module module_name[:partition_name]; -Terms enclosed in ``[]`` are optional. The syntax of ``module_name`` and ``partition_name`` -in regex form corresponds to ``[a-zA-Z_][a-zA-Z_0-9\.]*``. In particular, a literal dot ``.`` -in the name has no semantic meaning (e.g. implying a hierarchy). +Terms enclosed in ``[]`` are optional. ``module_name`` and ``partition_name`` +follow the rules for a C++ identifier, except that they may contain one or more +period (``.``) characters. Note that a ``.`` in the name has no semantic +meaning and does not imply any hierarchy. -In this document, module units are classified into: +In this document, module units are classified as: -* Primary module interface unit. - -* Module implementation unit. - -* Module interface partition unit. - -* Internal module partition unit. +* Primary module interface unit +* Module implementation unit +* Module partition interface unit +* Internal module partition unit A primary module interface unit is a module unit whose module declaration is -``export module module_name;``. The ``module_name`` here denotes the name of the +``export module module_name;`` where ``module_name`` denotes the name of the module. A module should have one and only one primary module interface unit. A module implementation unit is a module unit whose module declaration is -``module module_name;``. A module could have multiple module implementation -units with the same declaration. +``module module_name;``. Multiple module implementation units can be declared +in the same module. -A module interface partition unit is a module unit whose module declaration is +A module partition interface unit is a module unit whose module declaration is ``export module module_name:partition_name;``. The ``partition_name`` should be unique within any given module. -An internal module partition unit is a module unit whose module declaration -is ``module module_name:partition_name;``. The ``partition_name`` should be -unique within any given module. +An internal module partition unit is a module unit whose module +declaration is ``module module_name:partition_name;``. The ``partition_name`` +should be unique within any given module. -In this document, we use the following umbrella terms: +In this document, we use the following terms: * A ``module interface unit`` refers to either a ``primary module interface unit`` - or a ``module interface partition unit``. + or a ``module partition interface unit``. -* An ``importable module unit`` refers to either a ``module interface unit`` - or a ``internal module partition unit``. +* An ``importable module unit`` refers to either a ``module interface unit`` or + an ``internal module partition unit``. -* A ``module partition unit`` refers to either a ``module interface partition unit`` - or a ``internal module partition unit``. +* A ``module partition unit`` refers to either a ``module partition interface unit`` + or an ``internal module partition unit``. -Built Module Interface file -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Built Module Interface +~~~~~~~~~~~~~~~~~~~~~~ -A ``Built Module Interface file`` stands for the precompiled result of an importable module unit. -It is also called the acronym ``BMI`` generally. +A ``Built Module Interface`` (or ``BMI``) is the precompiled result of an +importable module unit. Global module fragment ~~~~~~~~~~~~~~~~~~~~~~ -In a module unit, the section from ``module;`` to the module declaration is called the global module fragment. +The ``global module fragment`` (or ``GMF``) is the code between the ``module;`` +and the module declaration within a module unit. How to build projects using modules @@ -138,7 +121,7 @@ Let's see a "hello world" example that uses modules. return 0; } -Then we type: +Then, on the command line, invoke Clang like: .. code-block:: console @@ -148,9 +131,9 @@ Then we type: Hello World! In this example, we make and use a simple module ``Hello`` which contains only a -primary module interface unit ``Hello.cppm``. +primary module interface unit named ``Hello.cppm``. -Then let's see a little bit more complex "hello world" example which uses the 4 kinds of module units. +A more complex "hello world" example which uses the 4 kinds of module units is: .. code-block:: c++ @@ -192,7 +175,7 @@ Then let's see a little bit more complex "hello world" example which uses the 4 return 0; } -Then we are able to compile the example by the following command: +Then, back on the command line, invoke Clang with: .. code-block:: console @@ -216,51 +199,57 @@ We explain the options in the following sections. How to enable standard C++ modules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Currently, standard C++ modules are enabled automatically -if the language standard is ``-std=c++20`` or newer. +Standard C++ modules are enabled automatically when the language standard mode +is ``-std=c++20`` or newer. How to produce a BMI ~~~~~~~~~~~~~~~~~~~~ -We can generate a BMI for an importable module unit by either ``--precompile`` -or ``-fmodule-output`` flags. +To generate a BMI for an importable module unit, use either the ``--precompile`` +or ``-fmodule-output`` command line options. -The ``--precompile`` option generates the BMI as the output of the compilation and the output path -can be specified using the ``-o`` option. +The ``--precompile`` option generates the BMI as the output of the compilation +with the output path specified using the ``-o`` option. -The ``-fmodule-output`` option generates the BMI as a by-product of the compilation. -If ``-fmodule-output=`` is specified, the BMI will be emitted the specified location. Then if -``-fmodule-output`` and ``-c`` are specified, the BMI will be emitted in the directory of the -output file with the name of the input file with the new extension ``.pcm``. Otherwise, the BMI -will be emitted in the working directory with the name of the input file with the new extension +The ``-fmodule-output`` option generates the BMI as a by-product of the +compilation. If ``-fmodule-output=`` is specified, the BMI will be emitted to +the specified location. If ``-fmodule-output`` and ``-c`` are specified, the +BMI will be emitted in the directory of the output file with the name of the +input file with the extension ``.pcm``. Otherwise, the BMI will be emitted in +the working directory with the name of the input file with the extension ``.pcm``. -The style to generate BMIs by ``--precompile`` is called two-phase compilation since it takes -2 steps to compile a source file to an object file. The style to generate BMIs by ``-fmodule-output`` -is called one-phase compilation respectively. The one-phase compilation model is simpler -for build systems to implement and the two-phase compilation has the potential to compile faster due -to higher parallelism. As an example, if there are two module units A and B, and B depends on A, the -one-phase compilation model would need to compile them serially, whereas the two-phase compilation -model may be able to compile them simultaneously if the compilation from A.pcm to A.o takes a long -time. - -File name requirement -~~~~~~~~~~~~~~~~~~~~~ - -The file name of an ``importable module unit`` should end with ``.cppm`` -(or ``.ccm``, ``.cxxm``, ``.c++m``). The file name of a ``module implementation unit`` -should end with ``.cpp`` (or ``.cc``, ``.cxx``, ``.c++``). - -The file name of BMIs should end with ``.pcm``. -The file name of the BMI of a ``primary module interface unit`` should be ``module_name.pcm``. -The file name of BMIs of ``module partition unit`` should be ``module_name-partition_name.pcm``. - -If the file names use different extensions, Clang may fail to build the module. -For example, if the filename of an ``importable module unit`` ends with ``.cpp`` instead of ``.cppm``, -then we can't generate a BMI for the ``importable module unit`` by ``--precompile`` option -since ``--precompile`` option now would only run preprocessor, which is equal to `-E` now. -If we want the filename of an ``importable module unit`` ends with other suffixes instead of ``.cppm``, -we could put ``-x c++-module`` in front of the file. For example, +Generating BMIs with ``--precompile`` is referred to as two-phase compilation +because it takes two steps to compile a source file to an object file. +Generating BMIs with ``-fmodule-output`` is called one-phase compilation. The +one-phase compilation model is simpler for build systems to implement while the +two-phase compilation has the potential to compile faster due to higher +parallelism. As an example, if there are two module units ``A`` and ``B``, and +``B`` depends on ``A``, the one-phase compilation model needs to compile them +serially, whereas the two-phase compilation model is able to be compiled as +soon as ``A.pcm`` is available, and thus can be compiled simultaneously as the +``A.pcm`` to ``A.o`` compilation step. + +File name requirements +~~~~~~~~~~~~~~~~~~~~~~ + +By convention, ``importable module unit`` files should use ``.cppm`` (or +``.ccm``, ``.cxxm``, or ``.c++m``) as a file extension. +``Module implementation unit`` files should use ``.cpp`` (or ``.cc``, ``.cxx``, +or ``.c++``) as a file extension. + +A BMI should use ``.pcm`` as a file extension. The file name of the BMI for a +``primary module interface unit`` should be ``module_name.pcm``. The file name +of a BMI for a ``module partition unit`` should be +``module_name-partition_name.pcm``. + +Clang may fail to build the module if different extensions are used. For +example, if the filename of an ``importable module unit`` ends with ``.cpp`` +instead of ``.cppm``, then Clang cannot generate a BMI for the +``importable module unit`` with the ``--precompile`` option because the +``--precompile`` option would only run the preprocessor (``-E``). If using a +different extension than the conventional one for an ``importable module unit`` +you can specify ``-x c++-module`` before the file. For example, .. code-block:: c++ @@ -279,8 +268,9 @@ we could put ``-x c++-module`` in front of the file. For example, return 0; } -Now the filename of the ``module interface`` ends with ``.cpp`` instead of ``.cppm``, -we can't compile them by the original command lines. But we are still able to do it by: +In this example, the extension used by the ``module interface`` is ``.cpp`` +instead of ``.cppm``, so it cannot be compiled like the previous example, but +it can be compiled with: .. code-block:: console @@ -289,12 +279,12 @@ we can't compile them by the original command lines. But we are still able to do $ ./Hello.out Hello World! -Module name requirement -~~~~~~~~~~~~~~~~~~~~~~~ +Module name requirements +~~~~~~~~~~~~~~~~~~~~~~~~ -[module.unit]p1 says: +.. -.. code-block:: text + [module.unit]p1: All module-names either beginning with an identifier consisting of std followed by zero or more digits or containing a reserved identifier ([lex.name]) are reserved and shall not @@ -302,7 +292,7 @@ Module name requirement module-name is a reserved identifier, the module name is reserved for use by C++ implementations; otherwise it is reserved for future standardization. -So all of the following name is not valid by default: +Therefore, none of the following names are valid by default: .. code-block:: text @@ -312,75 +302,74 @@ So all of the following name is not valid by default: __test // and so on ... -If you still want to use the reserved module names for any reason, use -``-Wno-reserved-module-identifier`` to suppress the warning. +Using a reserved module name is strongly discouraged, but +``-Wno-reserved-module-identifier`` can be used to suppress the warning. -How to specify the dependent BMIs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Specifying dependent BMIs +~~~~~~~~~~~~~~~~~~~~~~~~~ -There are 3 methods to specify the dependent BMIs: +There are 3 ways to specify a dependent BMI: -* (1) ``-fprebuilt-module-path=``. -* (2) ``-fmodule-file=`` (Deprecated). -* (3) ``-fmodule-file==``. +1. ``-fprebuilt-module-path=``. +2. ``-fmodule-file=`` (Deprecated). +3. ``-fmodule-file==``. -The option ``-fprebuilt-module-path`` tells the compiler the path where to search for dependent BMIs. -It may be used multiple times just like ``-I`` for specifying paths for header files. The look up rule here is: +The ``-fprebuilt-module-path`` option specifies the path to search for +dependent BMIs. Multiple paths may be specified, similar to using ``-I`` to +specify a search path for header files. When importing a module ``M``, the +compiler looks for ``M.pcm`` in the directories specified by +``-fprebuilt-module-path``. Similarly, when importing a partition module unit +``M:P``, the compiler looks for ``M-P.pcm`` in the directories specified by +``-fprebuilt-module-path``. -* (1) When we import module M. The compiler would look up M.pcm in the directories specified - by ``-fprebuilt-module-path``. -* (2) When we import partition module unit M:P. The compiler would look up M-P.pcm in the - directories specified by ``-fprebuilt-module-path``. - -The option ``-fmodule-file=`` tells the compiler to load the specified BMI directly. -The option ``-fmodule-file==`` tells the compiler to load the specified BMI -for the module specified by ```` when necessary. The main difference is that +The ``-fmodule-file=`` option causes the compiler to load the +specified BMI directly. The ``-fmodule-file==`` +option causes the compiler to load the specified BMI for the module specified +by ```` when necessary. The main difference is that ``-fmodule-file=`` will load the BMI eagerly, whereas -``-fmodule-file==`` will only load the BMI lazily, which is similar -with ``-fprebuilt-module-path``. The option ``-fmodule-file=`` for named modules is deprecated -and is planning to be removed in future versions. +``-fmodule-file==`` will only load the BMI lazily, +as will ``-fprebuilt-module-path``. The ``-fmodule-file=`` option +for named modules is deprecated and will be removed in a future version of +Clang. -In case all ``-fprebuilt-module-path=``, ``-fmodule-file=`` and -``-fmodule-file==`` exist, the ``-fmodule-file=`` option -takes highest precedence and ``-fmodule-file==`` will take the second -highest precedence. +When these options are specified in the same invocation of the compiler, the +``-fmodule-file=`` option takes precedence over +``-fmodule-file==``, which takes precedence over +``-fprebuilt-module-path=``. -We need to specify all the dependent (directly and indirectly) BMIs. -See https://github.com/llvm/llvm-project/issues/62707 for detail. +Note: all dependant BMIs must be specified explicitly, either directly or +indirectly dependent BMIs explicitly. See +https://github.com/llvm/llvm-project/issues/62707 for details. -When we compile a ``module implementation unit``, we must specify the BMI of the corresponding -``primary module interface unit``. -Since the language specification says a module implementation unit implicitly imports -the primary module interface unit. +When compiling a ``module implementation unit``, the BMI of the corresponding +``primary module interface unit`` must be specified because a module +implementation unit implicitly imports the primary module interface unit. [module.unit]p8 A module-declaration that contains neither an export-keyword nor a module-partition implicitly imports the primary module interface unit of the module as if by a module-import-declaration. -All of the 3 options ``-fprebuilt-module-path=``, ``-fmodule-file=`` -and ``-fmodule-file==`` may occur multiple times. -For example, the command line to compile ``M.cppm`` in -the above example could be rewritten into: +The ``-fprebuilt-module-path=``, ``-fmodule-file=``, +and ``-fmodule-file==`` options may be specified +multiple times. For example, the command line to compile ``M.cppm`` in +the previous example could be rewritten as: .. code-block:: console $ clang++ -std=c++20 M.cppm --precompile -fmodule-file=M:interface_part=M-interface_part.pcm -fmodule-file=M:impl_part=M-impl_part.pcm -o M.pcm When there are multiple ``-fmodule-file==`` options for the same -````, the last ``-fmodule-file==`` will override the previous -``-fmodule-file==`` options. - -``-fprebuilt-module-path`` is more convenient and ``-fmodule-file`` is faster since -it saves time for file lookup. +````, the last ``-fmodule-file==`` overrides the +previous ``-fmodule-file==`` option. Remember that module units still have an object counterpart to the BMI ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It is easy to forget to compile BMIs at first since we may envision module interfaces like headers. -However, this is not true. -Module units are translation units. We need to compile them to object files -and link the object files like the example shows. +While module interfaces resemble traditional header files, they still require +compilation. Module units are translation units, and need to be compiled to +object files, which then need to be linked together as the following examples +show. For example, the traditional compilation processes for headers are like: @@ -400,24 +389,27 @@ And the compilation process for module units are like: mod1.cppm -> clang++ mod1.cppm ... -> mod1.pcm --,--> clang++ mod1.pcm ... -> mod1.o -+ src2.cpp ----------------------------------------+> clang++ src2.cpp -------> src2.o -' -As the diagrams show, we need to compile the BMI from module units to object files and link the object files. -(But we can't do this for the BMI from header units. See the later section for the definition of header units) +As the diagrams show, we need to compile the BMI from module units to object +files and then link the object files. (However, this cannot be done for the BMI +from header units. See the section on :ref:`header units ` for +more details. -If we want to create a module library, we can't just ship the BMIs in an archive. -We must compile these BMIs(``*.pcm``) into object files(``*.o``) and add those object files to the archive instead. +BMIs cannot be shipped in an archive to create a module library. Instead, the +BMIs(``*.pcm``) are compiled into object files(``*.o``) and those object files +are added to the archive instead. -Consistency Requirement -~~~~~~~~~~~~~~~~~~~~~~~ +Consistency Requirements +~~~~~~~~~~~~~~~~~~~~~~~~ -If we envision modules as a cache to speed up compilation, then - as with other caching techniques - -it is important to keep cache consistency. -So **currently** Clang will do very strict check for consistency. +Modules can be viewed as a kind of cache to speed up compilation. Thus, like +other caching techniques, it is important to maintain cache consistency which +is why Clang does very strict checking for consistency. Options consistency ^^^^^^^^^^^^^^^^^^^ -The language option of module units and their non-module-unit users should be consistent. -The following example is not allowed: +Compiler options related to the language dialect for a module unit and its +non-module-unit uses need to be consistent. Consider the following example: .. code-block:: c++ @@ -432,9 +424,8 @@ The following example is not allowed: $ clang++ -std=c++20 M.cppm --precompile -o M.pcm $ clang++ -std=c++23 Use.cpp -fprebuilt-module-path=. -The compiler would reject the example due to the inconsistent language options. -Not all options are language options. -For example, the following example is allowed: +Clang rejects the example due to the inconsistent language standard modes. Not +all compiler options are language dialect options, though. For example: .. code-block:: console @@ -444,9 +435,12 @@ For example, the following example is allowed: # Inconsistent debugging level. $ clang++ -std=c++20 -g Use.cpp -fprebuilt-module-path=. -Although the two examples have inconsistent optimization and debugging level, both of them are accepted. +Although the optimization and debugging levels are inconsistent, these +compilations are accepted because the compiler options do not impact the +language dialect. -Note that **currently** the compiler doesn't consider inconsistent macro definition a problem. For example: +Note that the compiler **currently** doesn't reject inconsistent macro +definitions (this may change in the future). For example: .. code-block:: console @@ -454,43 +448,43 @@ Note that **currently** the compiler doesn't consider inconsistent macro definit # Inconsistent optimization level. $ clang++ -std=c++20 -O3 -DNDEBUG Use.cpp -fprebuilt-module-path=. -Currently Clang would accept the above example. But it may produce surprising results if the -debugging code depends on consistent use of ``NDEBUG`` also in other translation units. +Currently, Clang accepts the above example, though it may produce surprising +results if the debugging code depends on consistent use of ``NDEBUG`` in other +translation units. -Definitions consistency -^^^^^^^^^^^^^^^^^^^^^^^ +Object definition consistency +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The C++ language requires that declarations of the same entity in different +translation units have the same definition, which is known as the One +Definition Rule (ODR). Without modules, the compiler cannot perform strong ODR +violation checking because it only sees one translation unit at a time. With +the use of modules, the compiler can perform checks for ODR violations across +translation units. -The C++ language defines that same declarations in different translation units should have -the same definition, as known as ODR (One Definition Rule). Prior to modules, the translation -units don't dependent on each other and the compiler itself can't perform a strong -ODR violation check. With the introduction of modules, now the compiler have -the chance to perform ODR violations with language semantics across translation units. - -However, in the practice, we found the existing ODR checking mechanism is not stable -enough. Many people suffers from the false positive ODR violation diagnostics, AKA, -the compiler are complaining two identical declarations have different definitions -incorrectly. Also the true positive ODR violations are rarely reported. -Also we learned that MSVC don't perform ODR check for declarations in the global module -fragment. - -So in order to get better user experience, save the time checking ODR and keep consistent -behavior with MSVC, we disabled the ODR check for the declarations in the global module -fragment by default. Users who want more strict check can still use the -``-Xclang -fno-skip-odr-check-in-gmf`` flag to get the ODR check enabled. It is also -encouraged to report issues if users find false positive ODR violations or false negative ODR -violations with the flag enabled. +However, the current ODR checking mechanisms are not perfect. There are a +significant number of false positive ODR violation diagnostics, where the +compiler incorrectly diagnoses two identical declarations as having different +definitions. Further, true positive ODR violations are not always reported. + +To give a better user experience, improve compilation performance, and for +consistency with MSVC, ODR checking of declarations in the global module +fragment is disabled by default. These checks can be enabled by specifying +``-Xclang -fno-skip-odr-check-in-gmf`` when compiling. If the check is enabled +and you encounter incorrect or missing diagnostics, please report them via the +`community issue tracker `_. ABI Impacts ----------- -This section describes the new ABI changes brought by modules. - -Only Itanium C++ ABI related change are mentioned +This section describes the new ABI changes brought by modules. Only changes to +the Itanium C++ ABI are covered. -Mangling Names -~~~~~~~~~~~~~~ +Name Mangling +~~~~~~~~~~~~~ -The declarations in a module unit which are not in the global module fragment have new linkage names. +The declarations in a module unit which are not in the global module fragment +have new linkage names. For example, @@ -501,22 +495,24 @@ For example, export int foo(); } -The linkage name of ``NS::foo()`` would be ``_ZN2NSW1M3fooEv``. -This couldn't be demangled by previous versions of the debugger or demangler. -As of LLVM 15.x, users can utilize ``llvm-cxxfilt`` to demangle this: +The linkage name of ``NS::foo()`` is ``_ZN2NSW1M3fooEv``. This couldn't be +demangled by previous versions of the debugger or demangler. As of LLVM 15.x, +``llvm-cxxfilt`` can be used to demangle this: .. code-block:: console $ llvm-cxxfilt _ZN2NSW1M3fooEv + NS::foo@M() -The result would be ``NS::foo@M()``, which reads as ``NS::foo()`` in module ``M``. +The result should be read as ``NS::foo()`` in module ``M``. -The ABI implies that we can't declare something in a module unit and define it in a non-module unit (or vice-versa), -as this would result in linking errors. +The ABI implies that something cannot be declared in a module unit and defined +in a non-module unit (or vice-versa), as this would result in linking errors. -If we still want to implement declarations within the compatible ABI in module unit, -we can use the language-linkage specifier. Since the declarations in the language-linkage specifier -is attached to the global module fragments. For example: +Despite this, it is possible to implement declarations with a compatible ABI in +a module unit by using a language linkage specifier because the declarations in +the language linkage specifier are attached to the global module fragment. For +example: .. code-block:: c++ @@ -530,43 +526,47 @@ Now the linkage name of ``NS::foo()`` will be ``_ZN2NS3fooEv``. Module Initializers ~~~~~~~~~~~~~~~~~~~ -All the importable module units are required to emit an initializer function. -The initializer function should contain calls to importing modules first and -all the dynamic-initializers in the current module unit then. +All importable module units are required to emit an initializer function to +handle the dynamic initialization of non-inline variables in the module unit. +The importable module unit has to emit the initializer even if there is no +dynamic initialization; otherwise, the importer may call a nonexistent +function. The initializer function emits calls to imported modules first +followed by calls to all to of the dynamic initializers in the current module +unit. -Translation units explicitly or implicitly importing named modules must call -the initializer functions of the imported named modules within the sequence of -the dynamic-initializers in the TU. Initializations of entities at namespace -scope are appearance-ordered. This (recursively) extends into imported modules -at the point of appearance of the import declaration. +Translation units that explicitly or implicitly import a named module must call +the initializer functions of the imported named module within the sequence of +the dynamic initializers in the translation unit. Initializations of entities +at namespace scope are appearance-ordered. This (recursively) extends to +imported modules at the point of appearance of the import declaration. -It is allowed to omit calls to importing modules if it is known empty. - -It is allowed to omit calls to importing modules for which is known to be called. +If the imported module is known to be empty, the call to its initializer may be +omitted. Additionally, if the imported module is known to have already been +imported, the call to its initializer may be omitted. Reduced BMI ----------- -To support the 2 phase compilation model, Clang chose to put everything needed to -produce an object into the BMI. But every consumer of the BMI, except itself, doesn't -need such informations. It makes the BMI to larger and so may introduce unnecessary -dependencies into the BMI. To mitigate the problem, we decided to reduce the information -contained in the BMI. - -To be clear, we call the default BMI as Full BMI and the new introduced BMI as Reduced -BMI. +To support the two-phase compilation model, Clang puts everything needed to +produce an object into the BMI. However, other consumers of the BMI generally +don't need that information. This makes the BMI larger and may introduce +unnecessary dependencies for the BMI. To mitigate the problem, Clang has a +compiler option to reduce the information contained in the BMI. These two +formats are known as Full BMI and Reduced BMI, respectively. -Users can use ``-fexperimental-modules-reduced-bmi`` flag to enable the Reduced BMI. +Users can use the ``-fexperimental-modules-reduced-bmi`` option to produce a +Reduced BMI. -For one phase compilation model (CMake implements this model), with -``-fexperimental-modules-reduced-bmi``, the generated BMI will be Reduced BMI automatically. -(The output path of the BMI is specified by ``-fmodule-output=`` as usual one phase -compilation model). +For the one-phase compilation model (CMake implements this model), with +``-fexperimental-modules-reduced-bmi``, the generated BMI will be a Reduced +BMI automatically. (The output path of the BMI is specified by +``-fmodule-output=`` as usual with the one-phase compilation model). -It is still possible to support Reduced BMI in two phase compilation model. With -``-fexperimental-modules-reduced-bmi``, ``--precompile`` and ``-fmodule-output=`` specified, -the generated BMI specified by ``-o`` will be full BMI and the BMI specified by -``-fmodule-output=`` will be Reduced BMI. The dependency graph may be: +It is also possible to produce a Reduced BMI with the two-phase compilation +model. When ``-fexperimental-modules-reduced-bmi``, ``--precompile``, and +``-fmodule-output=`` are specified, the generated BMI specified by ``-o`` will +be a full BMI and the BMI specified by ``-fmodule-output=`` will be a Reduced +BMI. The dependency graph in this case would look like: .. code-block:: none @@ -577,15 +577,16 @@ the generated BMI specified by ``-o`` will be full BMI and the BMI specified by -> ... -> consumer_n.cpp -We don't emit diagnostics if ``-fexperimental-modules-reduced-bmi`` is used with a non-module -unit. This design helps the end users of one phase compilation model to perform experiments -early without asking for the help of build systems. The users of build systems which supports -two phase compilation model still need helps from build systems. +Clang does not emit diagnostics when ``-fexperimental-modules-reduced-bmi`` is +used with a non-module unit. This design permits users of the one-phase +compilation model to try using reduced BMIs without needing to modify the build +system. The two-phase compilation module requires build system support. -Within Reduced BMI, we won't write unreachable entities from GMF, definitions of non-inline -functions and non-inline variables. This may not be a transparent change. -`[module.global.frag]ex2 `_ may be a good -example: +In a Reduced BMI, Clang does not emit unreachable entities from the global +module fragment, or definitions of non-inline functions and non-inline +variables. This may not be a transparent change. + +Consider the following example: .. code-block:: c++ @@ -633,22 +634,23 @@ example: // module M's interface, so is discarded int c = use_h(); // OK -In the above example, the function definition of ``N::g`` is elided from the Reduced -BMI of ``M.cppm``. Then the use of ``use_g`` in ``M-impl.cpp`` fails -to instantiate. For such issues, users can add references to ``N::g`` in the module purview -of ``M.cppm`` to make sure it is reachable, e.g., ``using N::g;``. +In the above example, the function definition of ``N::g`` is elided from the +Reduced BMI of ``M.cppm``. Then the use of ``use_g`` in ``M-impl.cpp`` +fails to instantiate. For such issues, users can add references to ``N::g`` in +the `module purview `_ of ``M.cppm`` to +ensure it is reachable, e.g. ``using N::g;``. -We think the Reduced BMI is the correct direction. But given it is a drastic change, -we'd like to make it experimental first to avoid breaking existing users. The roadmap -of Reduced BMI may be: +Support for Reduced BMIs is still experimental, but it may become the default +in the future. The expected roadmap for Reduced BMIs as of Clang 19.x is: -1. ``-fexperimental-modules-reduced-bmi`` is opt in for 1~2 releases. The period depends -on testing feedbacks. -2. We would announce Reduced BMI is not experimental and introduce ``-fmodules-reduced-bmi``. -and suggest users to enable this mode. This may takes 1~2 releases too. -3. Finally we will enable this by default. When that time comes, the term BMI will refer to -the reduced BMI today and the Full BMI will only be meaningful to build systems which -loves to support two phase compilations. +1. ``-fexperimental-modules-reduced-bmi`` is opt-in for 1~2 releases. The period depends + on user feedback and may be extended. +2. Announce that Reduced BMIs are no longer experimental and introduce + ``-fmodules-reduced-bmi`` as a new option, and recommend use of the new + option. This transition is expected to take 1~2 additional releases as well. +3. Finally, ``-fmodules-reduced-bmi`` will be the default. When that time + comes, the term BMI will refer to the Reduced BMI and the Full BMI will only + be meaningful to build systems which elect to support two-phase compilation. Performance Tips ---------------- @@ -656,13 +658,11 @@ Performance Tips Reduce duplications ~~~~~~~~~~~~~~~~~~~ -While it is legal to have duplicated declarations in the global module fragments -of different module units, it is not free for clang to deal with the duplicated -declarations. In other word, for a translation unit, it will compile slower if the -translation unit itself and its importing module units contains a lot duplicated -declarations. - -For example, +While it is valid to have duplicated declarations in the global module fragments +of different module units, it is not free for Clang to deal with the duplicated +declarations. A translation unit will compile more slowly if there is a lot of +duplicated declarations between the translation unit and modules it imports. +For example: .. code-block:: c++ @@ -698,9 +698,9 @@ For example, import M; ... // use declarations from module M. -When ``big.header.h`` is big enough and there are a lot of partitions, -the compilation of ``use.cpp`` may be slower than -the following style significantly: +When ``big.header.h`` is big enough and there are a lot of partitions, the +compilation of ``use.cpp`` may be significantly slower than the following +approach: .. code-block:: c++ @@ -738,22 +738,21 @@ the following style significantly: import M; ... // use declarations from module M. -The key part of the tip is to reduce the duplications from the text includes. - -Ideas for converting to modules -------------------------------- +Reducing the duplication from textual includes is what improves compile-time +performance. -For new libraries, we encourage them to use modules completely from day one if possible. -This will be pretty helpful to make the whole ecosystems to get ready. +Transitioning to modules +------------------------ -For many existing libraries, it may be a breaking change to refactor themselves -into modules completely. So that many existing libraries need to provide headers and module -interfaces for a while to not break existing users. -Here we provide some ideas to ease the transition process for existing libraries. -**Note that the this section is only about helping ideas instead of requirement from clang**. +It is best for new code and libraries to use modules from the start if +possible. However, it may be a breaking change for existing code or libraries +to switch to modules. As a result, many existing libraries need to provide +both headers and module interfaces for a while to not break existing users. -Let's start with the case that there is no dependency or no dependent libraries providing -modules for your library. +This section suggests some suggestions on how to ease the transition process +for existing libraries. **Note that this information is only intended as +guidance, rather than as requirements to use modules in Clang.** It presumes +the project is starting with no module-based dependencies. ABI non-breaking styles ~~~~~~~~~~~~~~~~~~~~~~~ @@ -776,9 +775,9 @@ export-using style using decl_n; } -As the example shows, you need to include all the headers containing declarations needs -to be exported and `using` such declarations in an `export` block. Then, basically, -we're done. +This example shows how to include all the headers containing declarations which +need to be exported, and uses `using` declarations in an `export` block to +produce the module interface. export extern-C++ style ^^^^^^^^^^^^^^^^^^^^^^^ @@ -799,7 +798,7 @@ export extern-C++ style #include "header_n.h" } -Then in your headers (from ``header_1.h`` to ``header_n.h``), you need to define the macro: +Headers (from ``header_1.h`` to ``header_n.h``) need to define the macro: .. code-block:: c++ @@ -809,9 +808,10 @@ Then in your headers (from ``header_1.h`` to ``header_n.h``), you need to define #define EXPORT #endif -And you should put ``EXPORT`` to the beginning of the declarations you want to export. +and put ``EXPORT`` on the declarations you want to export. -Also it is suggested to refactor your headers to include thirdparty headers conditionally: +Also, it is recommended to refactor headers to include third-party headers +conditionally: .. code-block:: c++ @@ -823,26 +823,25 @@ Also it is suggested to refactor your headers to include thirdparty headers cond ... -This may be helpful to get better diagnostic messages if you forgot to update your module -interface unit file during maintaining. +This can be helpful because it gives better diagnostic messages if the module +interface unit is not properly updated when modifying code. -The reasoning for the practice is that the declarations in the language linkage are considered -to be attached to the global module. So the ABI of your library in the modular version -wouldn't change. +This approach works because the declarations with language linkage are attached +to the global module. Thus, the ABI of the modular form of the library does not +change. -While this style looks not as convenient as the export-using style, it is easier to convert -to other styles. +While this style is more involved than the export-using style, it makes it +easier to further refactor the library to other styles. ABI breaking style ~~~~~~~~~~~~~~~~~~ -The term ``ABI breaking`` sounds terrifying generally. But you may want it here if you want -to force your users to introduce your library in a consistent way. E.g., they either include -your headers all the way or import your modules all the way. -The style prevents the users to include your headers and import your modules at the same time -in the same repo. +The term ``ABI breaking`` may sound like a bad approach. However, this style +forces consumers of the library use it in a consistent way. e.g., either always +include headers for the library or always import modules. The style prevents +the ability to mix includes and imports for the library. -The pattern for ABI breaking style is similar with export extern-C++ style. +The pattern for ABI breaking style is similar to the export extern-C++ style. .. code-block:: c++ @@ -865,7 +864,7 @@ The pattern for ABI breaking style is similar with export extern-C++ style. ... #include "source_n.cpp" #else // the number of .cpp files in your project are a lot - // Using all the declarations from thirdparty libraries which are + // Using all the declarations from third-party libraries which are // used in the .cpp files. namespace third_party_namespace { using third_party_decl_used_in_cpp_1; @@ -875,11 +874,11 @@ The pattern for ABI breaking style is similar with export extern-C++ style. } #endif -(And add `EXPORT` and conditional include to the headers as suggested in the export -extern-C++ style section) +(And add `EXPORT` and conditional include to the headers as suggested in the +export extern-C++ style section.) -Remember that the ABI get changed and we need to compile our source files into the -new ABI format. This is the job of the additional part of the interface unit: +The ABI with modules is different and thus we need to compile the source files +into the new ABI. This is done by an additional part of the interface unit: .. code-block:: c++ @@ -890,7 +889,7 @@ new ABI format. This is the job of the additional part of the interface unit: ... #include "source_n.cpp" #else // the number of .cpp files in your project are a lot - // Using all the declarations from thirdparty libraries which are + // Using all the declarations from third-party libraries which are // used in the .cpp files. namespace third_party_namespace { using third_party_decl_used_in_cpp_1; @@ -900,16 +899,17 @@ new ABI format. This is the job of the additional part of the interface unit: } #endif -In case the number of your source files are small, we may put everything in the private -module fragment directly. (it is suggested to add conditional include to the source -files too). But it will make the compilation of the module interface unit to be slow -when the number of the source files are not small enough. +If the number of source files is small, everything can be put in the private +module fragment directly (it is recommended to add conditional includes to the +source files as well). However, compile time performance will be bad if there +are a lot of source files to compile. -**Note that the private module fragment can only be in the primary module interface unit -and the primary module interface unit containing private module fragment should be the only -module unit of the corresponding module.** +**Note that the private module fragment can only be in the primary module +interface unit and the primary module interface unit containing the private +module fragment should be the only module unit of the corresponding module.** -In that case, you need to convert your source files (.cpp files) to module implementation units: +In this case, source files (.cpp files) must be converted to module +implementation units: .. code-block:: c++ @@ -925,45 +925,40 @@ In that case, you need to convert your source files (.cpp files) to module imple // Following off should be unchanged. ... -The module implementation unit will import the primary module implicitly. -We don't include any headers in the module implementation units -here since we want to avoid duplicated declarations between translation units. -This is the reason why we add non-exported using declarations from the third -party libraries in the primary module interface unit. - -And if you provide your library as ``libyour_library.so``, you probably need to -provide a modular one ``libyour_library_modules.so`` since you changed the ABI. - -What if there are headers only inclued by the source files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The module implementation unit will import the primary module implicitly. Do +not include any headers in the module implementation units as it avoids +duplicated declarations between translation units. This is why non-exported +using declarations should be added from third-party libraries in the primary +module interface unit. -The above practice may be problematic if there are headers only included by the source -files. If you're using private module fragment, you may solve the issue by including them -in the private module fragment. While it is OK to solve it by including the implementation -headers in the module purview if you're using implementation module units, it may be -suboptimal since the primary module interface units now containing entities not belongs -to the interface. +If the library is provided as ``libyour_library.so``, a modular library (e.g., +``libyour_library_modules.so``) may also need to be provided for ABI +compatibility. -If you're a perfectionist, maybe you can improve it by introducing internal module partition unit. +What if there are headers only included by the source files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The internal module partition unit is an importable module unit which is internal -to the module itself. The concept just meets the headers only included by the source files. +The above practice may be problematic if there are headers only included by the +source files. When using a private module fragment, this issue may be solved by +including those headers in the private module fragment. While it is OK to solve +it by including the implementation headers in the module purview when using +implementation module units, it may be suboptimal because the primary module +interface units now contain entities that do not belong to the interface. -We don't show code snippet since it may be too verbose or not good or not general. -But it may not be too hard if you can understand the points of the section. +This can potentially be improved by introducing a module partition +implementation unit. An internal module partition unit is an importable +module unit which is internal to the module itself. Providing a header to skip parsing redundant headers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It is a problem for clang to handle redeclarations between translation units. -Also there is a long standing issue in clang (`problematic include after import `_). -But even if the issue get fixed in clang someday, the users may still get slower compilation speed -and larger BMI size. So it is suggested to not include headers after importing the corresponding -library. - -However, it is not easy for users if your library are included by other dependencies. - -So the users may have to write codes like: +Many redeclarations shared between translation units causes Clang to have +slower compile-time performance. Further, there are known issues with +`include after import `_. +Even when that issue is resolved, users may still get slower compilation speed +and larger BMIs. For these reasons, it is recommended to not include headers +after importing the corresponding module. However, it is not always easy if the +library is included by other dependencies, as in: .. code-block:: c++ @@ -977,9 +972,9 @@ or import your_library; #include "third_party/A.h" // #include "your_library/a_header.h" -For such cases, we suggest the libraries providing modules and the headers at the same time -to provide a header to skip parsing all the headers in your libraries. So the users can -import your library as the following style to skip redundant handling: +For such cases, it is best if the library providing both module and header +interfaces also provides a header which skips parsing so that the library can +be imported with the following approach that skips redundant redeclarations: .. code-block:: c++ @@ -987,9 +982,9 @@ import your library as the following style to skip redundant handling: #include "your_library_imported.h" #include "third_party/A.h" // #include "your_library/a_header.h" but got skipped -The implementation of ``your_library_imported.h`` can be a set of controlling macros or -an overall controlling macro if you're using `#pragma once`. So you can convert your -headers to: +The implementation of ``your_library_imported.h`` can be a set of controlling +macros or an overall controlling macro if using `#pragma once`. Then headers +can be refactored to: .. code-block:: c++ @@ -998,25 +993,24 @@ headers to: ... #endif -If the modules imported by your library provides such headers too, remember to add them to -your ``your_library_imported.h`` too. +If the modules imported by the library provide such headers, remember to add +them to ``your_library_imported.h`` too. Importing modules ~~~~~~~~~~~~~~~~~ -When there are dependent libraries providing modules, we suggest you to import that in -your module. - -Most of the existing libraries would fall into this catagory once the std module gets available. +When there are dependent libraries providing modules, they should be imported +in your module as well. Many existing libraries will fall into this category +once the ``std`` module is more widely available. All dependent libraries providing modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Life gets easier if all the dependent libraries providing modules. +Of course, most of the complexity disappears if all the dependent libraries +provide modules. -You need to convert your headers to include thirdparty headers conditionally. - -Then for export-using style: +Headers need to be converted to include third-party headers conditionally. Then, +for the export-using style: .. code-block:: c++ @@ -1035,7 +1029,7 @@ Then for export-using style: using decl_n; } -For export extern-C++ style: +or, for the export extern-C++ style: .. code-block:: c++ @@ -1049,7 +1043,7 @@ For export extern-C++ style: #include "header_n.h" } -For ABI breaking style, +or, for the ABI-breaking style, .. code-block:: c++ @@ -1069,35 +1063,39 @@ For ABI breaking style, #include "source_n.cpp" #endif -We don't need the non-exported using declarations if we're using implementation module -units now. We can import thirdparty modules directly in the implementation module -units. +Non-exported ``using`` declarations are unnecessary if using implementation +module units. Instead, third-party modules can be imported directly in +implementation module units. Partial dependent libraries providing modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In this case, we have to mix the use of ``include`` and ``import`` in the module of our -library. The key point here is still to remove duplicated declarations in translation -units as much as possible. If the imported modules provide headers to skip parsing their -headers, we should include that after the including. If the imported modules don't provide -the headers, we can make it ourselves if we still want to optimize it. - -Known Problems --------------- - -The following describes issues in the current implementation of modules. -Please see https://github.com/llvm/llvm-project/labels/clang%3Amodules for more issues -or file a new issue if you don't find an existing one. -If you're going to create a new issue for standard C++ modules, -please start the title with ``[C++20] [Modules]`` (or ``[C++23] [Modules]``, etc) -and add the label ``clang:modules`` (if you have permissions for that). - -For higher level support for proposals, you could visit https://clang.llvm.org/cxx_status.html. - -Including headers after import is problematic -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If the library has to mix the use of ``include`` and ``import`` in its module, +the primary goal is still the removal of duplicated declarations in translation +units as much as possible. If the imported modules provide headers to skip +parsing their headers, those should be included after the import. If the +imported modules don't provide such a header, one can be made manually for +improved compile time performance. + +Known Issues +------------ + +The following describes issues in the current implementation of modules. Please +see +`the issues list for modules `_ +for a list of issues or to file a new issue if you don't find an existing one. +When creating a new issue for standard C++ modules, please start the title with +``[C++20] [Modules]`` (or ``[C++23] [Modules]``, etc) and add the label +``clang:modules`` if possible. + +A high-level overview of support for standards features, including modules, can +be found on the `C++ Feature Status `_ +page. + +Including headers after import is not well-supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For example, the following example can be accept: +The following example is accepted: .. code-block:: c++ @@ -1110,8 +1108,8 @@ For example, the following example can be accept: return 0; } -but it will get rejected if we reverse the order of ``#include `` and -``import foo;``: +but if the order of ``#include `` and ``import foo;`` is reversed, +then the code is currently rejected: .. code-block:: c++ @@ -1126,33 +1124,31 @@ but it will get rejected if we reverse the order of ``#include `` and Both of the above examples should be accepted. -This is a limitation in the implementation. In the first example, -the compiler will see and parse first then the compiler will see the import. -So the ODR Checking and declarations merging will happen in the deserializer. -In the second example, the compiler will see the import first and the include second. -As a result, the ODR Checking and declarations merging will happen in the semantic analyzer. +This is a limitation of the implementation. In the first example, the compiler +will see and parse ```` first then it will see the ``import``. In +this case, ODR checking and declaration merging will happen in the +deserializer. In the second example, the compiler will see the ``import`` first +and the ``#include`` second which results in ODR checking and declarations +merging happening in the semantic analyzer. This is due to a divergence in the +implementation path. This is tracked by +`#61465 `_. -So there is divergence in the implementation path. It might be understandable that why -the orders matter here in the case. -(Note that "understandable" is different from "makes sense"). - -This is tracked in: https://github.com/llvm/llvm-project/issues/61465 - -Ignored PreferredName Attribute -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Due to a tricky problem, when Clang writes BMIs, Clang will ignore the ``preferred_name`` attribute, if any. -This implies that the ``preferred_name`` wouldn't show in debugger or dumping. +Ignored ``preferred_name`` Attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is tracked in: https://github.com/llvm/llvm-project/issues/56490 +When Clang writes BMIs, it will ignore the ``preferred_name`` attribute on +declarations which use it. Thus, the preferred name will not be displayed in +the debugger as expected. This is tracked by +`#56490 `_. Don't emit macros about module declaration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is covered by P1857R3. We mention it again here since users may abuse it before we implement it. +This is covered by `P1857R3 `_. It is mentioned here +because we want users to be aware that we don't yet implement it. -Someone may want to write code which could be compiled both by modules or non-modules. -A direct idea would be use macros like: +A direct approach to write code that can be compiled by both modules and +non-module builds may look like: .. code-block:: c++ @@ -1162,39 +1158,37 @@ A direct idea would be use macros like: IMPORT header_name EXPORT ... -So this file could be triggered like a module unit or a non-module unit depending on the definition -of some macros. -However, this kind of usage is forbidden by P1857R3 but we haven't implemented P1857R3 yet. -This means that is possible to write illegal modules code now, and obviously this will stop working -once P1857R3 is implemented. -A simple suggestion would be "Don't play macro tricks with module declarations". +The intent of this is that this file can be compiled like a module unit or a +non-module unit depending on the definition of some macros. However, this usage +is forbidden by P1857R3 which is not yet implemented in Clang. This means that +is possible to write invalid modules which will no longer be accepted once +P1857R3 is implemented. This is tracked by +`#56917 `_. + +Until then, it is recommended not to mix macros with module declarations. -This is tracked in: https://github.com/llvm/llvm-project/issues/56917 In consistent filename suffix requirement for importable module units ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Currently, clang requires the file name of an ``importable module unit`` should end with ``.cppm`` -(or ``.ccm``, ``.cxxm``, ``.c++m``). However, the behavior is inconsistent with other compilers. - -This is tracked in: https://github.com/llvm/llvm-project/issues/57416 - -clang-cl is not compatible with the standard C++ modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now we can't use the `/clang:-fmodule-file` or `/clang:-fprebuilt-module-path` to specify -the BMI within ``clang-cl.exe``. +Currently, Clang requires the file name of an ``importable module unit`` to +have ``.cppm`` (or ``.ccm``, ``.cxxm``, ``.c++m``) as the file extension. +However, the behavior is inconsistent with other compilers. This is tracked by +`#57416 `_. -This is tracked in: https://github.com/llvm/llvm-project/issues/64118 +clang-cl is not compatible with standard C++ modules +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -false positive ODR violation diagnostic due to using inconsistent qualified but the same type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``/clang:-fmodule-file`` and ``/clang:-fprebuilt-module-path`` cannot be used +to specify the BMI with ``clang-cl.exe``. This is tracked by +`#64118 `_. -ODR violation is a pretty common issue when using modules. -Sometimes the program violated the One Definition Rule actually. -But sometimes it shows the compiler gives false positive diagnostics. +Incorrect ODR violation diagnostics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -One often reported example is: +ODR violations are a common issue when using modules. Clang sometimes produces +false-positive diagnostics or fails to produce true-positive diagnostics of the +One Definition Rule. One often-reported example is: .. code-block:: c++ @@ -1222,51 +1216,49 @@ One often reported example is: export module repro; export import :part; -Currently the compiler complains about the inconsistent definition of `fun()` in -2 module units. This is incorrect. Since both definitions of `fun()` has the same -spelling and `T` refers to the same type entity finally. So the program should be -fine. - -This is tracked in https://github.com/llvm/llvm-project/issues/78850. +Currently the compiler incorrectly diagnoses the inconsistent definition of +``fun()`` in two module units. Because both definitions of ``fun()`` have the +same spelling and ``T`` refers to the same type entity, there is no ODR +violation. This is tracked by +`#78850 `_. Using TU-local entity in other units ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Module units are translation units. So the entities which should only be local to the -module unit itself shouldn't be used by other units in any means. +Module units are translation units, so the entities which should be local to +the module unit itself should never be used by other units. -In the language side, to address the idea formally, the language specification defines -the concept of ``TU-local`` and ``exposure`` in +The C++ standard defines the concept of ``TU-local`` and ``exposure`` in `basic.link/p14 `_, `basic.link/p15 `_, `basic.link/p16 `_, -`basic.link/p17 `_ and +`basic.link/p17 `_, and `basic.link/p18 `_. -However, the compiler doesn't support these 2 ideas formally. -This results in unclear and confusing diagnostic messages. -And it is worse that the compiler may import TU-local entities to other units without any -diagnostics. +However, Clang doesn't formally support these two concepts. This results in +unclear or confusing diagnostic messages. Further, Clang may import +``TU-local`` entities to other units without any diagnostics. This is tracked +by `#78173 `_. -This is tracked in https://github.com/llvm/llvm-project/issues/78173. +.. _header-units: Header Units ============ -How to build projects using header unit ---------------------------------------- +How to build projects using header units +---------------------------------------- .. warning:: - The user interfaces of header units is highly experimental. There are still - many unanswered question about how tools should interact with header units. - The user interfaces described here may change after we have progress on how - tools should support for header units. + The support for header units, including related command line options, is + experimental. There are still many unanswered question about how tools + should interact with header units. The details described here may change in + the future. Quick Start ~~~~~~~~~~~ -For the following example, +The following example: .. code-block:: c++ @@ -1275,7 +1267,7 @@ For the following example, std::cout << "Hello World.\n"; } -we could compile it as +could be compiled with: .. code-block:: console @@ -1285,14 +1277,14 @@ we could compile it as How to produce BMIs ~~~~~~~~~~~~~~~~~~~ -Similar to named modules, we could use ``--precompile`` to produce the BMI. -But we need to specify that the input file is a header by ``-xc++-system-header`` or ``-xc++-user-header``. +Similar to named modules, ``--precompile`` can be used to produce a BMI. +However, that requires specifying that the input file is a header by using +``-xc++-system-header`` or ``-xc++-user-header``. -Also we could use `-fmodule-header={user,system}` option to produce the BMI for header units -which has suffix like `.h` or `.hh`. -The value of `-fmodule-header` means the user search path or the system search path. -The default value for `-fmodule-header` is `user`. -For example, +The ``-fmodule-header={user,system}`` option can also be used to produce a BMI +for header units which have a file extension like `.h` or `.hh`. The argument to +``-fmodule-header`` specifies either the user search path or the system search +path. The default value for ``-fmodule-header`` is ``user``. For example: .. code-block:: c++ @@ -1308,16 +1300,16 @@ For example, Hello(); } -We could compile it as: +could be compiled with: .. code-block:: console $ clang++ -std=c++20 -fmodule-header foo.h -o foo.pcm $ clang++ -std=c++20 -fmodule-file=foo.pcm use.cpp -For headers which don't have a suffix, we need to pass ``-xc++-header`` -(or ``-xc++-system-header`` or ``-xc++-user-header``) to mark it as a header. -For example, +For headers which do not have a file extension, ``-xc++-header`` (or +``-xc++-system-header``, ``-xc++-user-header``) must be used to specify the +file as a header. For example: .. code-block:: c++ @@ -1332,23 +1324,25 @@ For example, $ clang++ -std=c++20 -fmodule-header=system -xc++-header iostream -o iostream.pcm $ clang++ -std=c++20 -fmodule-file=iostream.pcm use.cpp -How to specify the dependent BMIs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +How to specify dependent BMIs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We could use ``-fmodule-file`` to specify the BMIs, and this option may occur multiple times as well. +``-fmodule-file`` can be used to specify a dependent BMI (or multiple times for +more than one dependent BMI). -With the existing implementation ``-fprebuilt-module-path`` cannot be used for header units -(since they are nominally anonymous). -For header units, use ``-fmodule-file`` to include the relevant PCM file for each header unit. +With the existing implementation, ``-fprebuilt-module-path`` cannot be used for +header units (because they are nominally anonymous). For header units, use +``-fmodule-file`` to include the relevant PCM file for each header unit. -This is expect to be solved in future editions of the compiler either by the tooling finding and specifying -the -fmodule-file or by the use of a module-mapper that understands how to map the header name to their PCMs. +This is expect to be solved in a future version of Clang either by the compiler +finding and specifying ``-fmodule-file`` automatically, or by the use of a +module-mapper that understands how to map the header name to their PCMs. -Don't compile the BMI -~~~~~~~~~~~~~~~~~~~~~ +Compiling a header unit to an object file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Another difference with modules is that we can't compile the BMI from a header unit. -For example: +A header unit cannot be compiled to an object file due to the semantics of +header units. For example: .. code-block:: console @@ -1356,15 +1350,13 @@ For example: # This is not allowed! $ clang++ iostream.pcm -c -o iostream.o -It makes sense due to the semantics of header units, which are just like headers. - Include translation ~~~~~~~~~~~~~~~~~~~ -The C++ spec allows the vendors to convert ``#include header-name`` to ``import header-name;`` when possible. -Currently, Clang would do this translation for the ``#include`` in the global module fragment. - -For example, the following two examples are the same: +The C++ standard allows vendors to convert ``#include header-name`` to +``import header-name;`` when possible. Currently, Clang does this translation +for the ``#include`` in the global module fragment. For example, the following +example: .. code-block:: c++ @@ -1375,7 +1367,7 @@ For example, the following two examples are the same: std::cout << "Hello.\n"; } -with the following one: +is the same as this example: .. code-block:: c++ @@ -1391,17 +1383,17 @@ with the following one: $ clang++ -std=c++20 -xc++-system-header --precompile iostream -o iostream.pcm $ clang++ -std=c++20 -fmodule-file=iostream.pcm --precompile M.cppm -o M.cpp -In the latter example, the Clang could find the BMI for the ```` -so it would try to replace the ``#include `` to ``import ;`` automatically. +In the latter example, Clang can find the BMI for ```` and so it +tries to replace the ``#include `` with ``import ;`` +automatically. -Relationships between Clang modules ------------------------------------ +Differences between Clang modules and header units +-------------------------------------------------- -Header units have pretty similar semantics with Clang modules. -The semantics of both of them are like headers. - -In fact, we could even "mimic" the sytle of header units by Clang modules: +Header units have similar semantics to Clang modules. The semantics of both are +like headers. Therefore, header units can be mimicked by Clang modules as in +the following example: .. code-block:: c++ @@ -1414,46 +1406,45 @@ In fact, we could even "mimic" the sytle of header units by Clang modules: $ clang++ -std=c++20 -fimplicit-modules -fmodule-map-file=.modulemap main.cpp -It would be simpler if we are using libcxx: +This example is simplified when using libc++: .. code-block:: console $ clang++ -std=c++20 main.cpp -fimplicit-modules -fimplicit-module-maps -Since there is already one -`module map `_ -in the source of libcxx. - -Then immediately leads to the question: why don't we implement header units through Clang header modules? +because libc++ already supplies a +`module map `_. -The main reason for this is that Clang modules have more semantics like hierarchy or -wrapping multiple headers together as a big module. -However, these things are not part of Standard C++ Header units, -and we want to avoid the impression that these additional semantics get interpreted as Standard C++ behavior. +This raises the question: why are header units not implemented through Clang +modules? -Another reason is that there are proposals to introduce module mappers to the C++ standard -(for example, https://wg21.link/p1184r2). -If we decide to reuse Clang's modulemap, we may get in trouble once we need to introduce another module mapper. +This is primarily because Clang modules have more hierarchical semantics when +wrapping multiple headers together as one module, which is not supported by +Standard C++ Header units. We want to avoid the impression that these +additional semantics get interpreted as Standard C++ behavior. -So the final answer for why we don't reuse the interface of Clang modules for header units is that -there are some differences between header units and Clang modules and that ignoring those -differences now would likely become a problem in the future. +Another reason is that there are proposals to introduce module mappers to the +C++ standard (for example, https://wg21.link/p1184r2). Reusing Clang's +``modulemap`` may be more difficult if we need to introduce another module +mapper. -Discover Dependencies -===================== +Discovering Dependencies +======================== -Prior to modules, all the translation units can be compiled parallelly. -But it is not true for the module units. The presence of module units requires -us to compile the translation units in a (topological) order. +Without use of modules, all the translation units in a project can be compiled +in parallel. However, the presence of module units requires compiling the +translation units in a topological order. -The clang-scan-deps scanner implemented -`P1689 paper `_ -to describe the order. Only named modules are supported now. +The ``clang-scan-deps`` tool can extract dependency information and produce a +JSON file conforming to the specification described in +`P1689 `_. +Only named modules are supported currently. -We need a compilation database to use clang-scan-deps. See +A compilation database is needed when using ``clang-scan-deps``. See `JSON Compilation Database Format Specification `_ -for example. Note that the ``output`` entry is necessary for clang-scan-deps -to scan P1689 format. Here is an example: +for more information about compilation databases. Note that the ``output`` +JSON attribute is necessary for ``clang-scan-deps`` to scan using the P1689 +format. For example: .. code-block:: c++ @@ -1533,13 +1524,13 @@ And here is the compilation database: } ] -And we can get the dependency information in P1689 format by: +To get the dependency information in P1689 format, use: .. code-block:: console $ clang-scan-deps -format=p1689 -compilation-database P1689.json -And we will get: +to get: .. code-block:: text @@ -1619,14 +1610,14 @@ And we will get: See the P1689 paper for the meaning of the fields. -And if the user want a finer-grained control for any reason, e.g., to scan the generated source files, -the user can choose to get the dependency information per file. For example: +Getting dependency information per file with finer-grained control (such as +scanning generated source files) is possible. For example: .. code-block:: console $ clang-scan-deps -format=p1689 -- /clang++ -std=c++20 impl_part.cppm -c -o impl_part.o -And we'll get: +will produce: .. code-block:: text @@ -1652,22 +1643,23 @@ And we'll get: "version": 1 } -In this way, we can pass the single command line options after the ``--``. -Then clang-scan-deps will extract the necessary information from the options. -Note that we need to specify the path to the compiler executable instead of saying -``clang++`` simply. +Individual command line options can be specified after ``--``. +``clang-scan-deps`` will extract the necessary information from the specified +options. Note that the path to the compiler executable needs to be specified +explicitly instead of using ``clang++`` directly. -The users may want the scanner to get the transitional dependency information for headers. -Otherwise, the users have to scan twice for the project, once for headers and once for modules. -To address the requirement, clang-scan-deps will recognize the specified preprocessor options -in the given command line and generate the corresponding dependency information. For example, +Users may want the scanner to get the transitional dependency information for +headers. Otherwise, the project has to be scanned twice, once for headers and +once for modules. To address this, ``clang-scan-deps`` will recognize the +specified preprocessor options in the given command line and generate the +corresponding dependency information. For example: .. code-block:: console $ clang-scan-deps -format=p1689 -- ../bin/clang++ -std=c++20 impl_part.cppm -c -o impl_part.o -MD -MT impl_part.ddi -MF impl_part.dep $ cat impl_part.dep -We will get: +will produce: .. code-block:: text @@ -1679,41 +1671,41 @@ We will get: /usr/include/bits/types/__locale_t.h \ ... -When clang-scan-deps detects ``-MF`` option, clang-scan-deps will try to write the +When ``clang-scan-deps`` detects the ``-MF`` option, it will try to write the dependency information for headers to the file specified by ``-MF``. Possible Issues: Failed to find system headers ---------------------------------------------- -In case the users encounter errors like ``fatal error: 'stddef.h' file not found``, -probably the specified ``/clang++`` refers to a symlink -instead a real binary. There are 4 potential solutions to the problem: - -* (1) End users can resolve the issue by pointing the specified compiler executable to - the real binary instead of the symlink. -* (2) End users can invoke ``/clang++ -print-resource-dir`` - to get the corresponding resource directory for your compiler and add that directory - to the include search paths manually in the build scripts. -* (3) Build systems that use a compilation database as the input for clang-scan-deps - scanner, the build system can add the flag ``--resource-dir-recipe invoke-compiler`` to - the clang-scan-deps scanner to calculate the resources directory dynamically. - The calculation happens only once for a unique ``/clang++``. -* (4) For build systems that invokes the clang-scan-deps scanner per file, repeatedly - calculating the resource directory may be inefficient. In such cases, the build - system can cache the resource directory by itself and pass ``-resource-dir `` - explicitly in the command line options: +If encountering an error like ``fatal error: 'stddef.h' file not found``, +the specified ``/clang++`` probably refers to a +symlink instead a real binary. There are four potential solutions to the +problem: -.. code-block:: console +1. Point the specified compiler executable to the real binary instead of the + symlink. +2. Invoke ``/clang++ -print-resource-dir`` to get + the corresponding resource directory for your compiler and add that + directory to the include search paths manually in the build scripts. +3. For build systems that use a compilation database as the input for + ``clang-scan-deps``, the build system can add the + ``--resource-dir-recipe invoke-compiler`` option when executing + ``clang-scan-deps`` to calculate the resource directory dynamically. + The calculation happens only once for a unique ``/clang++``. +4. For build systems that invoke ``clang-scan-deps`` per file, repeatedly + calculating the resource directory may be inefficient. In such cases, the + build system can cache the resource directory and specify + ``-resource-dir `` explicitly, as in: + + .. code-block:: console - $ clang-scan-deps -format=p1689 -- /clang++ -std=c++20 -resource-dir mod.cppm -c -o mod.o + $ clang-scan-deps -format=p1689 -- /clang++ -std=c++20 -resource-dir mod.cppm -c -o mod.o Import modules with clang-repl ============================== -We're able to import C++20 named modules with clang-repl. - -Let's start with a simple example: +``clang-repl`` supports importing C++20 named modules. For example: .. code-block:: c++ @@ -1723,7 +1715,7 @@ Let's start with a simple example: return "Hello Interpreter for Modules!"; } -We still need to compile the named module in ahead. +The named module still needs to be compiled ahead of time. .. code-block:: console @@ -1731,10 +1723,9 @@ We still need to compile the named module in ahead. $ clang++ M.pcm -c -o M.o $ clang++ -shared M.o -o libM.so -Note that we need to compile the module unit into a dynamic library so that the clang-repl -can load the object files of the module units. - -Then we are able to import module ``M`` in clang-repl. +Note that the module unit needs to be compiled as a dynamic library so that +``clang-repl`` can load the object files of the module units. Then it is +possible to import module ``M`` in clang-repl. .. code-block:: console @@ -1753,17 +1744,18 @@ Possible Questions How modules speed up compilation -------------------------------- -A classic theory for the reason why modules speed up the compilation is: -if there are ``n`` headers and ``m`` source files and each header is included by each source file, -then the complexity of the compilation is ``O(n*m)``; -But if there are ``n`` module interfaces and ``m`` source files, the complexity of the compilation is -``O(n+m)``. So, using modules would be a big win when scaling. -In a simpler word, we could get rid of many redundant compilations by using modules. +A classic theory for the reason why modules speed up the compilation is: if +there are ``n`` headers and ``m`` source files and each header is included by +each source file, then the complexity of the compilation is ``O(n*m)``. +However, if there are ``n`` module interfaces and ``m`` source files, the +complexity of the compilation is ``O(n+m)``. Therefore, using modules would be +a significant improvement at scale. More simply, use of modules causes many of +the redundant compilations to no longer be necessary. -Roughly, this theory is correct. But the problem is that it is too rough. -The behavior depends on the optimization level, as we will illustrate below. +While this is accurate at a high level, this depends greatly on the +optimization level, as illustrated below. -First is ``O0``. The compilation process is described in the following graph. +First is ``-O0``. The compilation process is described in the following graph. .. code-block:: none @@ -1771,13 +1763,13 @@ First is ``O0``. The compilation process is described in the following graph. │ │ │ │ └---parsing----sema----codegen--┴----- transformations ---- codegen ----┴---- codegen --┘ - ┌---------------------------------------------------------------------------------------┐ + ├---------------------------------------------------------------------------------------┐ | │ | source file │ | │ └---------------------------------------------------------------------------------------┘ - ┌--------┐ + ├--------┐ │ │ │imported│ │ │ @@ -1785,18 +1777,17 @@ First is ``O0``. The compilation process is described in the following graph. │ │ └--------┘ -Here we can see that the source file (could be a non-module unit or a module unit) would get processed by the -whole pipeline. -But the imported code would only get involved in semantic analysis, which is mainly about name lookup, -overload resolution and template instantiation. -All of these processes are fast relative to the whole compilation process. -More importantly, the imported code only needs to be processed once in frontend code generation, -as well as the whole middle end and backend. -So we could get a big win for the compilation time in O0. +In this case, the source file (which could be a non-module unit or a module +unit) would get processed by the entire pipeline. However, the imported code +would only get involved in semantic analysis, which, for the most part, is name +lookup, overload resolution, and template instantiation. All of these processes +are fast relative to the whole compilation process. More importantly, the +imported code only needs to be processed once during frontend code generation, +as well as the whole middle end and backend. So we could get a big win for the +compilation time in ``-O0``. -But with optimizations, things are different: - -(we omit ``code generation`` part for each end due to the limited space) +But with optimizations, things are different (the ``code generation`` part for +each end is omitted due to limited space): .. code-block:: none @@ -1804,12 +1795,12 @@ But with optimizations, things are different: │ │ │ │ └--- parsing ---- sema -----┴--- optimizations --- IPO ---- optimizations---┴--- optimizations -┘ - ┌-----------------------------------------------------------------------------------------------┐ + ├-----------------------------------------------------------------------------------------------┐ │ │ │ source file │ │ │ └-----------------------------------------------------------------------------------------------┘ - ┌---------------------------------------┐ + ├---------------------------------------┐ │ │ │ │ │ imported code │ @@ -1817,27 +1808,29 @@ But with optimizations, things are different: │ │ └---------------------------------------┘ -It would be very unfortunate if we end up with worse performance after using modules. -The main concern is that when we compile a source file, the compiler needs to see the function body -of imported module units so that it can perform IPO (InterProcedural Optimization, primarily inlining -in practice) to optimize functions in current source file with the help of the information provided by -the imported module units. -In other words, the imported code would be processed again and again in importee units -by optimizations (including IPO itself). -The optimizations before IPO and the IPO itself are the most time-consuming part in whole compilation process. -So from this perspective, we might not be able to get the improvements described in the theory. -But we could still save the time for optimizations after IPO and the whole backend. - -Overall, at ``O0`` the implementations of functions defined in a module will not impact module users, -but at higher optimization levels the definitions of such functions are provided to user compilations for the -purposes of optimization (but definitions of these functions are still not included in the use's object file)- -this means the build speedup at higher optimization levels may be lower than expected given ``O0`` experience, -but does provide by more optimization opportunities. +It would be very unfortunate if we end up with worse performance when using +modules. The main concern is that when a source file is compiled, the compiler +needs to see the body of imported module units so that it can perform IPO +(InterProcedural Optimization, primarily inlining in practice) to optimize +functions in the current source file with the help of the information provided +by the imported module units. In other words, the imported code would be +processed again and again in importee units by optimizations (including IPO +itself). The optimizations before IPO and IPO itself are the most time-consuming +part in whole compilation process. So from this perspective, it might not be +possible to get the compile time improvements described, but there could be +time savings for optimizations after IPO and the whole backend. + +Overall, at ``-O0`` the implementations of functions defined in a module will +not impact module users, but at higher optimization levels the definitions of +such functions are provided to user compilations for the purposes of +optimization (but definitions of these functions are still not included in the +use's object file). This means the build speedup at higher optimization levels +may be lower than expected given ``-O0`` experience, but does provide more +optimization opportunities. Interoperability with Clang Modules ----------------------------------- -We **wish** to support clang modules and standard c++ modules at the same time, -but the mixed using form is not well used/tested yet. - -Please file new github issues as you find interoperability problems. +We **wish** to support Clang modules and standard C++ modules at the same time, +but the mixing them together is not well used/tested yet. Please file new +GitHub issues as you find interoperability problems. diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 370de7d9c769e8173b7dc3ddcc6f70d9e12ef115..80ba70f67126fa23543819655e98e2fcb7d46a32 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -3504,7 +3504,7 @@ Differences between ``*17`` and ``*23`` modes: - ``nullptr`` and ``nullptr_t`` are supported, only in ``*23`` mode. - ``ATOMIC_VAR_INIT`` is removed from ``*23`` mode. - ``bool``, ``true``, ``false``, ``alignas``, ``alignof``, ``static_assert``, - and ``thread_local` are now first-class keywords, only in ``*23`` mode. + and ``thread_local`` are now first-class keywords, only in ``*23`` mode. - ``typeof`` and ``typeof_unqual`` are supported, only ``*23`` mode. - Bit-precise integers (``_BitInt(N)``) are supported by default in ``*23`` mode, and as an extension in ``*17`` and earlier modes. diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 0d87df36ced0e91fdd6953ca4e5e352ea5e16c4d..eb8b58323da4d783695c73310f717cb32feb2d05 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -3033,13 +3033,6 @@ Further examples of injection vulnerabilities this checker can find. sprintf(buf, s); // warn: untrusted data used as a format string } - void test() { - size_t ts; - scanf("%zd", &ts); // 'ts' marked as tainted - int *p = (int *)malloc(ts * sizeof(int)); - // warn: untrusted data used as buffer size - } - There are built-in sources, propagations and sinks even if no external taint configuration is provided. @@ -3067,9 +3060,7 @@ Default propagations rules: Default sinks: ``printf``, ``setproctitle``, ``system``, ``popen``, ``execl``, ``execle``, - ``execlp``, ``execv``, ``execvp``, ``execvP``, ``execve``, ``dlopen``, - ``memcpy``, ``memmove``, ``strncpy``, ``strndup``, ``malloc``, ``calloc``, - ``alloca``, ``memccpy``, ``realloc``, ``bcopy`` + ``execlp``, ``execv``, ``execvp``, ``execvP``, ``execve``, ``dlopen`` Please note that there are no built-in filter functions. diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt index 2252d0ccde96d235a7e45df500ee14a77cfc4340..eaeadf2656b0bffc5378fcb5d9de2d14048f161c 100644 --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -632,11 +632,12 @@ clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp -clang/unittests/Analysis/FlowSensitive/SolverTest.cpp +clang/unittests/Analysis/FlowSensitive/SolverTest.h clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp clang/unittests/Analysis/FlowSensitive/TestingSupport.h clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +clang/unittests/Analysis/FlowSensitive/WatchedLiteralsSolverTest.cpp clang/unittests/AST/ASTImporterFixtures.cpp clang/unittests/AST/ASTImporterFixtures.h clang/unittests/AST/ASTImporterObjCTest.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 6dbd06251ddad69d94ba95c5f57b061e775968cf..e03b11219478670277472f41b6c31cc31f2f6961 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1116,7 +1116,8 @@ public: CanQualType BFloat16Ty; CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3 CanQualType VoidPtrTy, NullPtrTy; - CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy; + CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy, + UnknownAnyTy; CanQualType BuiltinFnTy; CanQualType PseudoObjectTy, ARCUnbridgedCastTy; CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy; diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def index 0a36fdc5d9c0f7ee7f12405d7bf04f2fd7aef666..444be4311a7431ab56a2f828a31d03a6303b84ac 100644 --- a/clang/include/clang/AST/BuiltinTypes.def +++ b/clang/include/clang/AST/BuiltinTypes.def @@ -285,6 +285,9 @@ PLACEHOLDER_TYPE(Overload, OverloadTy) // x->foo # if only contains non-static members PLACEHOLDER_TYPE(BoundMember, BoundMemberTy) +// The type of an unresolved template. Used in UnresolvedLookupExpr. +PLACEHOLDER_TYPE(UnresolvedTemplate, UnresolvedTemplateTy) + // The type of an expression which refers to a pseudo-object, // such as those introduced by Objective C's @property or // VS.NET's __property declarations. A placeholder type. The diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index a53c27a99a8c3645c48c8ff2554e0ebf67cd38f1..de8b923645f8d6418a1f8a8ae1a5ed80115b8016 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -5049,6 +5049,11 @@ static constexpr StringRef getOpenMPVariantManglingSeparatorStr() { return "$ompvariant"; } +/// Returns whether the given FunctionDecl has an __arm[_locally]_streaming +/// attribute. +bool IsArmStreamingFunction(const FunctionDecl *FD, + bool IncludeLocallyStreaming); + } // namespace clang #endif // LLVM_CLANG_AST_DECL_H diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index ab3f810b45192b5ca9e98b734aa872ab1402c48d..fac65628ffede8329b71c9d850502a743087a47c 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3163,8 +3163,30 @@ public: /// This arises in several ways: /// * we might be waiting for argument-dependent lookup; /// * the name might resolve to an overloaded function; +/// * the name might resolve to a non-function template; for example, in the +/// following snippet, the return expression of the member function +/// 'foo()' might remain unresolved until instantiation: +/// +/// \code +/// struct P { +/// template using I = T; +/// }; +/// +/// struct Q { +/// template int foo() { +/// return T::template I; +/// } +/// }; +/// \endcode +/// +/// ...which is distinct from modeling function overloads, and therefore we use +/// a different builtin type 'UnresolvedTemplate' to avoid confusion. This is +/// done in Sema::BuildTemplateIdExpr. +/// /// and eventually: /// * the lookup might have included a function template. +/// * the unresolved template gets transformed in an instantiation or gets +/// diagnosed for its direct use. /// /// These never include UnresolvedUsingValueDecls, which are always class /// members and therefore appear only in UnresolvedMemberLookupExprs. diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index bb4cb1f5d5080bd33103645e267b1e948e91d6ca..3d0b1ab9d31e068ddc90f8c97571473473cc2a12 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -26,14 +26,17 @@ class OpenACCClause { protected: OpenACCClause(OpenACCClauseKind K, SourceLocation BeginLoc, SourceLocation EndLoc) - : Kind(K), Location(BeginLoc, EndLoc) {} + : Kind(K), Location(BeginLoc, EndLoc) { + assert(!BeginLoc.isInvalid() && !EndLoc.isInvalid() && + "Begin and end location must be valid for OpenACCClause"); + } public: OpenACCClauseKind getClauseKind() const { return Kind; } SourceLocation getBeginLoc() const { return Location.getBegin(); } SourceLocation getEndLoc() const { return Location.getEnd(); } - static bool classof(const OpenACCClause *) { return true; } + static bool classof(const OpenACCClause *) { return false; } using child_iterator = StmtIterator; using const_child_iterator = ConstStmtIterator; @@ -60,6 +63,8 @@ protected: : OpenACCClause(K, BeginLoc, EndLoc), LParenLoc(LParenLoc) {} public: + static bool classof(const OpenACCClause *C); + SourceLocation getLParenLoc() const { return LParenLoc; } child_range children() { @@ -89,6 +94,9 @@ protected: } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Default; + } OpenACCDefaultClauseKind getDefaultClauseKind() const { return DefaultClauseKind; } @@ -113,6 +121,8 @@ protected: ConditionExpr(ConditionExpr) {} public: + static bool classof(const OpenACCClause *C); + bool hasConditionExpr() const { return ConditionExpr; } const Expr *getConditionExpr() const { return ConditionExpr; } Expr *getConditionExpr() { return ConditionExpr; } @@ -140,6 +150,9 @@ protected: Expr *ConditionExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::If; + } static OpenACCIfClause *Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *ConditionExpr, SourceLocation EndLoc); @@ -151,6 +164,9 @@ class OpenACCSelfClause : public OpenACCClauseWithCondition { Expr *ConditionExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Self; + } static OpenACCSelfClause *Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *ConditionExpr, SourceLocation EndLoc); @@ -177,6 +193,7 @@ protected: llvm::ArrayRef getExprs() const { return Exprs; } public: + static bool classof(const OpenACCClause *C); child_range children() { return child_range(reinterpret_cast(Exprs.begin()), reinterpret_cast(Exprs.end())); @@ -189,6 +206,49 @@ public: } }; +// Represents the 'devnum' and expressions lists for the 'wait' clause. +class OpenACCWaitClause final + : public OpenACCClauseWithExprs, + public llvm::TrailingObjects { + SourceLocation QueuesLoc; + OpenACCWaitClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + Expr *DevNumExpr, SourceLocation QueuesLoc, + ArrayRef QueueIdExprs, SourceLocation EndLoc) + : OpenACCClauseWithExprs(OpenACCClauseKind::Wait, BeginLoc, LParenLoc, + EndLoc), + QueuesLoc(QueuesLoc) { + // The first element of the trailing storage is always the devnum expr, + // whether it is used or not. + std::uninitialized_copy(&DevNumExpr, &DevNumExpr + 1, + getTrailingObjects()); + std::uninitialized_copy(QueueIdExprs.begin(), QueueIdExprs.end(), + getTrailingObjects() + 1); + setExprs( + MutableArrayRef(getTrailingObjects(), QueueIdExprs.size() + 1)); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Wait; + } + static OpenACCWaitClause *Create(const ASTContext &C, SourceLocation BeginLoc, + SourceLocation LParenLoc, Expr *DevNumExpr, + SourceLocation QueuesLoc, + ArrayRef QueueIdExprs, + SourceLocation EndLoc); + + bool hasQueuesTag() const { return !QueuesLoc.isInvalid(); } + SourceLocation getQueuesLoc() const { return QueuesLoc; } + bool hasDevNumExpr() const { return getExprs()[0]; } + Expr *getDevNumExpr() const { return getExprs()[0]; } + llvm::ArrayRef getQueueIdExprs() { + return OpenACCClauseWithExprs::getExprs().drop_front(); + } + llvm::ArrayRef getQueueIdExprs() const { + return OpenACCClauseWithExprs::getExprs().drop_front(); + } +}; + class OpenACCNumGangsClause final : public OpenACCClauseWithExprs, public llvm::TrailingObjects { @@ -203,6 +263,9 @@ class OpenACCNumGangsClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::NumGangs; + } static OpenACCNumGangsClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef IntExprs, SourceLocation EndLoc); @@ -227,10 +290,12 @@ protected: SourceLocation EndLoc) : OpenACCClauseWithExprs(K, BeginLoc, LParenLoc, EndLoc), IntExpr(IntExpr) { - setExprs(MutableArrayRef{&this->IntExpr, 1}); + if (IntExpr) + setExprs(MutableArrayRef{&this->IntExpr, 1}); } public: + static bool classof(const OpenACCClause *C); bool hasIntExpr() const { return !getExprs().empty(); } const Expr *getIntExpr() const { return hasIntExpr() ? getExprs()[0] : nullptr; @@ -244,6 +309,9 @@ class OpenACCNumWorkersClause : public OpenACCClauseWithSingleIntExpr { Expr *IntExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::NumWorkers; + } static OpenACCNumWorkersClause *Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, @@ -255,11 +323,28 @@ class OpenACCVectorLengthClause : public OpenACCClauseWithSingleIntExpr { Expr *IntExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::VectorLength; + } static OpenACCVectorLengthClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *IntExpr, SourceLocation EndLoc); }; +class OpenACCAsyncClause : public OpenACCClauseWithSingleIntExpr { + OpenACCAsyncClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + Expr *IntExpr, SourceLocation EndLoc); + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Async; + } + static OpenACCAsyncClause *Create(const ASTContext &C, + SourceLocation BeginLoc, + SourceLocation LParenLoc, Expr *IntExpr, + SourceLocation EndLoc); +}; + /// Represents a clause with one or more 'var' objects, represented as an expr, /// as its arguments. Var-list is expected to be stored in trailing storage. /// For now, we're just storing the original expression in its entirety, unlike @@ -271,6 +356,7 @@ protected: : OpenACCClauseWithExprs(K, BeginLoc, LParenLoc, EndLoc) {} public: + static bool classof(const OpenACCClause *C); ArrayRef getVarList() { return getExprs(); } ArrayRef getVarList() const { return getExprs(); } }; @@ -289,11 +375,249 @@ class OpenACCPrivateClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Private; + } static OpenACCPrivateClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, SourceLocation EndLoc); }; +class OpenACCFirstPrivateClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + + OpenACCFirstPrivateClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(OpenACCClauseKind::FirstPrivate, BeginLoc, + LParenLoc, EndLoc) { + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::FirstPrivate; + } + static OpenACCFirstPrivateClause * + Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCDevicePtrClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + + OpenACCDevicePtrClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(OpenACCClauseKind::DevicePtr, BeginLoc, + LParenLoc, EndLoc) { + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::DevicePtr; + } + static OpenACCDevicePtrClause * + Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCAttachClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + + OpenACCAttachClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(OpenACCClauseKind::Attach, BeginLoc, LParenLoc, + EndLoc) { + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Attach; + } + static OpenACCAttachClause * + Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCNoCreateClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + + OpenACCNoCreateClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(OpenACCClauseKind::NoCreate, BeginLoc, + LParenLoc, EndLoc) { + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::NoCreate; + } + static OpenACCNoCreateClause * + Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCPresentClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + + OpenACCPresentClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(OpenACCClauseKind::Present, BeginLoc, + LParenLoc, EndLoc) { + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Present; + } + static OpenACCPresentClause * + Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCCopyClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + + OpenACCCopyClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc, + SourceLocation LParenLoc, ArrayRef VarList, + SourceLocation EndLoc) + : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc) { + assert((Spelling == OpenACCClauseKind::Copy || + Spelling == OpenACCClauseKind::PCopy || + Spelling == OpenACCClauseKind::PresentOrCopy) && + "Invalid clause kind for copy-clause"); + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Copy || + C->getClauseKind() == OpenACCClauseKind::PCopy || + C->getClauseKind() == OpenACCClauseKind::PresentOrCopy; + } + static OpenACCCopyClause * + Create(const ASTContext &C, OpenACCClauseKind Spelling, + SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCCopyInClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + bool IsReadOnly; + + OpenACCCopyInClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc, + SourceLocation LParenLoc, bool IsReadOnly, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc), + IsReadOnly(IsReadOnly) { + assert((Spelling == OpenACCClauseKind::CopyIn || + Spelling == OpenACCClauseKind::PCopyIn || + Spelling == OpenACCClauseKind::PresentOrCopyIn) && + "Invalid clause kind for copyin-clause"); + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::CopyIn || + C->getClauseKind() == OpenACCClauseKind::PCopyIn || + C->getClauseKind() == OpenACCClauseKind::PresentOrCopyIn; + } + bool isReadOnly() const { return IsReadOnly; } + static OpenACCCopyInClause * + Create(const ASTContext &C, OpenACCClauseKind Spelling, + SourceLocation BeginLoc, SourceLocation LParenLoc, bool IsReadOnly, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCCopyOutClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + bool IsZero; + + OpenACCCopyOutClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc, + SourceLocation LParenLoc, bool IsZero, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc), + IsZero(IsZero) { + assert((Spelling == OpenACCClauseKind::CopyOut || + Spelling == OpenACCClauseKind::PCopyOut || + Spelling == OpenACCClauseKind::PresentOrCopyOut) && + "Invalid clause kind for copyout-clause"); + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::CopyOut || + C->getClauseKind() == OpenACCClauseKind::PCopyOut || + C->getClauseKind() == OpenACCClauseKind::PresentOrCopyOut; + } + bool isZero() const { return IsZero; } + static OpenACCCopyOutClause * + Create(const ASTContext &C, OpenACCClauseKind Spelling, + SourceLocation BeginLoc, SourceLocation LParenLoc, bool IsZero, + ArrayRef VarList, SourceLocation EndLoc); +}; + +class OpenACCCreateClause final + : public OpenACCClauseWithVarList, + public llvm::TrailingObjects { + bool IsZero; + + OpenACCCreateClause(OpenACCClauseKind Spelling, SourceLocation BeginLoc, + SourceLocation LParenLoc, bool IsZero, + ArrayRef VarList, SourceLocation EndLoc) + : OpenACCClauseWithVarList(Spelling, BeginLoc, LParenLoc, EndLoc), + IsZero(IsZero) { + assert((Spelling == OpenACCClauseKind::Create || + Spelling == OpenACCClauseKind::PCreate || + Spelling == OpenACCClauseKind::PresentOrCreate) && + "Invalid clause kind for create-clause"); + std::uninitialized_copy(VarList.begin(), VarList.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), VarList.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Create || + C->getClauseKind() == OpenACCClauseKind::PCreate || + C->getClauseKind() == OpenACCClauseKind::PresentOrCreate; + } + bool isZero() const { return IsZero; } + static OpenACCCreateClause * + Create(const ASTContext &C, OpenACCClauseKind Spelling, + SourceLocation BeginLoc, SourceLocation LParenLoc, bool IsZero, + ArrayRef VarList, SourceLocation EndLoc); +}; + template class OpenACCClauseVisitor { Impl &getDerived() { return static_cast(*this); } @@ -312,6 +636,10 @@ public: case OpenACCClauseKind::CLAUSE_NAME: \ Visit##CLAUSE_NAME##Clause(*cast(C)); \ return; +#define CLAUSE_ALIAS(ALIAS_NAME, CLAUSE_NAME) \ + case OpenACCClauseKind::ALIAS_NAME: \ + Visit##CLAUSE_NAME##Clause(*cast(C)); \ + return; #include "clang/Basic/OpenACCClauses.def" default: diff --git a/clang/include/clang/AST/StmtOpenACC.h b/clang/include/clang/AST/StmtOpenACC.h index 66f8f844e0b29e1ef34c202751784224809882b7..b706864798baaf3f156aafcd1aafc848261f2944 100644 --- a/clang/include/clang/AST/StmtOpenACC.h +++ b/clang/include/clang/AST/StmtOpenACC.h @@ -93,6 +93,10 @@ protected: } public: + static bool classof(const Stmt *T) { + return false; + } + child_range children() { if (getAssociatedStmt()) return child_range(&AssociatedStmt, &AssociatedStmt + 1); diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h index 67eccdd030dcdd43f9488c902db5da658550eb1d..763af244547647ce5a8d2af5e4fc67cc0f423a85 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -283,9 +283,8 @@ llvm::Expected> diagnoseFunction( if (!Context) return Context.takeError(); - auto OwnedSolver = std::make_unique(MaxSATIterations); - const WatchedLiteralsSolver *Solver = OwnedSolver.get(); - DataflowAnalysisContext AnalysisContext(std::move(OwnedSolver)); + auto Solver = std::make_unique(MaxSATIterations); + DataflowAnalysisContext AnalysisContext(*Solver); Environment Env(AnalysisContext, FuncDecl); AnalysisT Analysis = createAnalysis(ASTCtx, Env); llvm::SmallVector Diagnostics; diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h index aa2c366cb164a92612088bbcefae8f897ca84898..5be4a1145f40d7158cf727c3cb5666f1304f4fb9 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -67,7 +67,19 @@ public: DataflowAnalysisContext(std::unique_ptr S, Options Opts = Options{ /*ContextSensitiveOpts=*/std::nullopt, - /*Logger=*/nullptr}); + /*Logger=*/nullptr}) + : DataflowAnalysisContext(*S, std::move(S), Opts) {} + + /// Constructs a dataflow analysis context. + /// + /// Requirements: + /// + /// `S` must outlive the `DataflowAnalysisContext`. + DataflowAnalysisContext(Solver &S, Options Opts = Options{ + /*ContextSensitiveOpts=*/std::nullopt, + /*Logger=*/nullptr}) + : DataflowAnalysisContext(S, nullptr, Opts) {} + ~DataflowAnalysisContext(); /// Sets a callback that returns the names and types of the synthetic fields @@ -209,6 +221,13 @@ private: using DenseMapInfo::isEqual; }; + /// `S` is the solver to use. `OwnedSolver` may be: + /// * Null (in which case `S` is non-onwed and must outlive this object), or + /// * Non-null (in which case it must refer to `S`, and the + /// `DataflowAnalysisContext will take ownership of `OwnedSolver`). + DataflowAnalysisContext(Solver &S, std::unique_ptr &&OwnedSolver, + Options Opts); + // Extends the set of modeled field declarations. void addModeledFields(const FieldSet &Fields); @@ -232,7 +251,8 @@ private: Solver::Result::Status::Unsatisfiable; } - std::unique_ptr S; + Solver &S; + std::unique_ptr OwnedSolver; std::unique_ptr A; // Maps from program declarations and statements to storage locations that are diff --git a/clang/include/clang/Analysis/FlowSensitive/Solver.h b/clang/include/clang/Analysis/FlowSensitive/Solver.h index 079f6802f241eead339cdcf0718d796512e03492..6166a503ab413aef748119cf85b6b861141e5014 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Solver.h +++ b/clang/include/clang/Analysis/FlowSensitive/Solver.h @@ -87,6 +87,9 @@ public: /// /// All elements in `Vals` must not be null. virtual Result solve(llvm::ArrayRef Vals) = 0; + + // Did the solver reach its resource limit? + virtual bool reachedLimit() const = 0; }; llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Solver::Result &); diff --git a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h index 5448eecf6d41a25e2f253ae4eaf9e44296c84ddd..b5cd7aa10fd7d2502376049dded4f5c693d26008 100644 --- a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h +++ b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h @@ -48,8 +48,7 @@ public: Result solve(llvm::ArrayRef Vals) override; - // The solver reached its maximum number of iterations. - bool reachedLimit() const { return MaxIterations == 0; } + bool reachedLimit() const override { return MaxIterations == 0; } }; } // namespace dataflow diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 0225598cbbe8ad8f1d5e8085688e22fda4ef75b1..52552ba488560b2462139026789e564ac0387bda 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4415,6 +4415,18 @@ def HLSLResourceBinding: InheritableAttr { let Documentation = [HLSLResourceBindingDocs]; } +def HLSLPackOffset: HLSLAnnotationAttr { + let Spellings = [HLSLAnnotation<"packoffset">]; + let LangOpts = [HLSL]; + let Args = [IntArgument<"Subcomponent">, IntArgument<"Component">]; + let Documentation = [HLSLPackOffsetDocs]; + let AdditionalMembers = [{ + unsigned getOffset() { + return subcomponent * 4 + component; + } + }]; +} + def HLSLSV_DispatchThreadID: HLSLAnnotationAttr { let Spellings = [HLSLAnnotation<"SV_DispatchThreadID">]; let Subjects = SubjectList<[ParmVar, Field]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index a0bbe5861c5722dea021d62fb609ec98b5db7031..f351822ac74bd5a451d951305dbfcc6c64c95f55 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2527,6 +2527,9 @@ Example "subtarget features" from the x86 backend include: "mmx", "sse", "sse4.2 "avx", "xop" and largely correspond to the machine specific options handled by the front end. +Note that this attribute does not apply transitively to nested functions such +as blocks or C++ lambdas. + Additionally, this attribute supports function multiversioning for ELF based x86/x86-64 targets, which can be used to create multiple implementations of the same function that will be resolved at runtime based on the priority of their @@ -3782,6 +3785,12 @@ for that function. This attribute is incompatible with the ``always_inline`` and ``minsize`` attributes. + +Note that this attribute does not apply recursively to nested functions such as +lambdas or blocks when using declaration-specific attribute syntaxes such as double +square brackets (``[[]]``) or ``__attribute__``. The ``#pragma`` syntax can be +used to apply the attribute to all functions, including nested functions, in a +range of source code. }]; } @@ -5654,11 +5663,12 @@ The ``preserve_none`` calling convention tries to preserve as few general registers as possible. So all general registers are caller saved registers. It also uses more general registers to pass arguments. This attribute doesn't impact floating-point registers (XMMs/YMMs). Floating-point registers still -follow the c calling convention. +follow the c calling convention. ``preserve_none``'s ABI is still unstable, and +may be changed in the future. - Only RSP and RBP are preserved by callee. -- Register RDI, RSI, RDX, RCX, R8, R9, R11, R12, R13, R14, R15 and RAX now can +- Register R12, R13, R14, R15, RDI, RSI, RDX, RCX, R8, R9, R11, and RAX now can be used to pass function arguments. }]; } @@ -7398,6 +7408,26 @@ The full documentation is available here: https://docs.microsoft.com/en-us/windo }]; } +def HLSLPackOffsetDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The packoffset attribute is used to change the layout of a cbuffer. +Attribute spelling in HLSL is: ``packoffset( c[Subcomponent][.component] )``. +A subcomponent is a register number, which is an integer. A component is in the form of [.xyzw]. + +Examples: + +.. code-block:: c++ + + cbuffer A { + float3 a : packoffset(c0.y); + float4 b : packoffset(c4); + } + +The full documentation is available here: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-packoffset + }]; +} + def HLSLSV_DispatchThreadIDDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index de721a87b3341def3eaeacd12f68e9d7653c95c0..11982af3fa609bea617811b4b550954da698258e 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1326,6 +1326,12 @@ def ElementwiseSqrt : Builtin { let Prototype = "void(...)"; } +def ElementwiseTan : Builtin { + let Spellings = ["__builtin_elementwise_tan"]; + let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def ElementwiseTrunc : Builtin { let Spellings = ["__builtin_elementwise_trunc"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; diff --git a/clang/include/clang/Basic/BuiltinsNVPTX.def b/clang/include/clang/Basic/BuiltinsNVPTX.def index 8d3c5e69d55cf492f27e65fee1aa3a6a9bc9d011..9e243d740ed7aec7e7b8b94f305478e6eba9011d 100644 --- a/clang/include/clang/Basic/BuiltinsNVPTX.def +++ b/clang/include/clang/Basic/BuiltinsNVPTX.def @@ -61,7 +61,9 @@ #pragma push_macro("PTX81") #pragma push_macro("PTX82") #pragma push_macro("PTX83") -#define PTX83 "ptx83" +#pragma push_macro("PTX84") +#define PTX84 "ptx84" +#define PTX83 "ptx83|" PTX84 #define PTX82 "ptx82|" PTX83 #define PTX81 "ptx81|" PTX82 #define PTX80 "ptx80|" PTX81 @@ -1091,3 +1093,4 @@ TARGET_BUILTIN(__nvvm_getctarank_shared_cluster, "iv*3", "", AND(SM_90,PTX78)) #pragma pop_macro("PTX81") #pragma pop_macro("PTX82") #pragma pop_macro("PTX83") +#pragma pop_macro("PTX84") diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def index 7e950914ad946d07d2b55711362399771dd28c4c..8645cff1e8679f36d8595f19a165f9236014e71d 100644 --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -190,6 +190,10 @@ TARGET_BUILTIN(__builtin_wasm_relaxed_dot_i8x16_i7x16_s_i16x8, "V8sV16ScV16Sc", TARGET_BUILTIN(__builtin_wasm_relaxed_dot_i8x16_i7x16_add_s_i32x4, "V4iV16ScV16ScV4i", "nc", "relaxed-simd") TARGET_BUILTIN(__builtin_wasm_relaxed_dot_bf16x8_add_f32_f32x4, "V4fV8UsV8UsV4f", "nc", "relaxed-simd") +// Half-Precision (fp16) +TARGET_BUILTIN(__builtin_wasm_loadf16_f32, "fh*", "nU", "half-precision") +TARGET_BUILTIN(__builtin_wasm_storef16_f32, "vfh*", "n", "half-precision") + // Reference Types builtins // Some builtins are custom type-checked - see 't' as part of the third argument, // in which case the argument spec (second argument) is unused. diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 340b08dd7e2a33422dfc4680ff71d2f6152a709d..07b0ca1691a679a2148a72734e2a658bb5286d58 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -57,6 +57,7 @@ CODEGENOPT(UniqueSectionNames, 1, 1) ///< Set for -funique-section-names. CODEGENOPT(UniqueBasicBlockSectionNames, 1, 1) ///< Set for -funique-basic-block-section-names, ///< Produce unique section names with ///< basic block sections. +CODEGENOPT(SeparateNamedSections, 1, 0) ///< Set for -fseparate-named-sections. CODEGENOPT(EnableAIXExtendedAltivecABI, 1, 0) ///< Set for -mabi=vec-extabi. Enables the extended Altivec ABI on AIX. CODEGENOPT(XCOFFReadOnlyPointers, 1, 0) ///< Set for -mxcoff-roptr. CODEGENOPT(AllTocData, 1, 0) ///< AIX -mtocdata @@ -308,6 +309,7 @@ CODEGENOPT(UnrollLoops , 1, 0) ///< Control whether loops are unrolled. CODEGENOPT(RerollLoops , 1, 0) ///< Control whether loops are rerolled. CODEGENOPT(NoUseJumpTables , 1, 0) ///< Set when -fno-jump-tables is enabled. VALUE_CODEGENOPT(UnwindTables, 2, 0) ///< Unwind tables (1) or asynchronous unwind tables (2) +CODEGENOPT(LinkBitcodePostopt, 1, 0) ///< Link builtin bitcodes after optimization pipeline. CODEGENOPT(VectorizeLoop , 1, 0) ///< Run loop vectorizer. CODEGENOPT(VectorizeSLP , 1, 0) ///< Run SLP vectorizer. CODEGENOPT(ProfileSampleAccurate, 1, 0) ///< Sample profile is accurate. diff --git a/clang/include/clang/Basic/Cuda.h b/clang/include/clang/Basic/Cuda.h index ba0e4465a0f5a0a2f7673d549c7228406be6a9d4..2d67c4181d12957e0e1df2429e3c6a3f6935c40c 100644 --- a/clang/include/clang/Basic/Cuda.h +++ b/clang/include/clang/Basic/Cuda.h @@ -41,9 +41,10 @@ enum class CudaVersion { CUDA_121, CUDA_122, CUDA_123, + CUDA_124, FULLY_SUPPORTED = CUDA_123, PARTIALLY_SUPPORTED = - CUDA_123, // Partially supported. Proceed with a warning. + CUDA_124, // Partially supported. Proceed with a warning. NEW = 10000, // Too new. Issue a warning, but allow using it. }; const char *CudaVersionToString(CudaVersion V); diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index ed3fd9b1c4a55b6a9a3694ecd54575bd1056de36..9781fcaa4ff5e913e619d347a2c5d2a98940bd9d 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -435,7 +435,7 @@ def warn_drv_diagnostics_misexpect_requires_pgo : Warning< def warn_drv_clang_unsupported : Warning< "the clang compiler does not support '%0'">; def warn_drv_deprecated_arg : Warning< - "argument '%0' is deprecated, use '%1' instead">, InGroup; + "argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup; def warn_drv_deprecated_custom : Warning< "argument '%0' is deprecated, %1">, InGroup; def warn_drv_assuming_mfloat_abi_is : Warning< diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index fcffadacc8e631ebbd7b3d54368b02a50c815b29..e456ec2cac461c2bd39e67be8a87870ca7f23376 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -134,6 +134,8 @@ def err_fe_no_pch_in_dir : Error< "no suitable precompiled header file found in directory '%0'">; def err_fe_action_not_available : Error< "action %0 not compiled in">; +def err_fe_invalid_multiple_actions : Error< + "'%0' action ignored; '%1' action specified previously">; def err_fe_invalid_alignment : Error< "invalid value '%1' in '%0'; alignment must be a power of 2">; def err_fe_invalid_exception_model diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 60f87da2a7387c25fff72fefb54d5852e627e299..2beb1d45124b4945f6c4fa7d254380673b5f7d8e 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1507,6 +1507,9 @@ def BranchProtection : DiagGroup<"branch-protection">; // Warnings for HLSL Clang extensions def HLSLExtension : DiagGroup<"hlsl-extensions">; +// Warning for mix packoffset and non-packoffset. +def HLSLMixPackOffset : DiagGroup<"mix-packoffset">; + // Warnings for DXIL validation def DXILValidation : DiagGroup<"dxil-validation">; diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td index 6896e0f5aa593c2e598bec39e2b331a089352bce..674742431dcb2d7dfff7284ca8d071d361275388 100644 --- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td +++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td @@ -25,6 +25,7 @@ def err_unsupported_vendor : Error<"vendor '%0' is not supported: '%1'">; def err_unsupported_environment : Error<"environment '%0' is not supported: '%1'">; def err_unsupported_os : Error<"os '%0' is not supported: '%1'">; def err_cannot_read_input_list : Error<"could not read %select{alias list|filelist}0 '%1': %2">; +def err_invalid_label: Error<"label '%0' is reserved: use a different label name for -X