diff --git a/.ci/monolithic-linux.sh b/.ci/monolithic-linux.sh index 311488b2c2878b132229dc7746c30348d8b27e57..f0577d1069d5dec5e89950b72a5fd8908a5bcb1b 100755 --- a/.ci/monolithic-linux.sh +++ b/.ci/monolithic-linux.sh @@ -49,7 +49,8 @@ cmake -S ${MONOREPO_ROOT}/llvm -B ${BUILD_DIR} \ -D LLVM_ENABLE_LLD=ON \ -D CMAKE_CXX_FLAGS=-gmlt \ -D BOLT_CLANG_EXE=/usr/bin/clang \ - -D LLVM_CCACHE_BUILD=ON + -D LLVM_CCACHE_BUILD=ON \ + -D MLIR_ENABLE_BINDINGS_PYTHON=ON echo "--- ninja" # Targets are not escaped as they are passed as separate arguments. diff --git a/.ci/monolithic-windows.sh b/.ci/monolithic-windows.sh index 00c3037c4c4fd618f45d139e8063b09104bf1822..7ac806a0b399ad54dce56033bd3e2fa8aecedcdc 100755 --- a/.ci/monolithic-windows.sh +++ b/.ci/monolithic-windows.sh @@ -48,7 +48,8 @@ cmake -S ${MONOREPO_ROOT}/llvm -B ${BUILD_DIR} \ -D LLVM_LIT_ARGS="-v --xunit-xml-output ${BUILD_DIR}/test-results.xml" \ -D COMPILER_RT_BUILD_ORC=OFF \ -D CMAKE_C_COMPILER_LAUNCHER=sccache \ - -D CMAKE_CXX_COMPILER_LAUNCHER=sccache + -D CMAKE_CXX_COMPILER_LAUNCHER=sccache \ + -D MLIR_ENABLE_BINDINGS_PYTHON=ON echo "--- ninja" # Targets are not escaped as they are passed as separate arguments. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e2635ffb3eaf0b1ead31a827e285ee849903bfb7..b9a28edc9daf45097d539c0bcc706df89c44e25b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -32,3 +32,30 @@ /clang/www/make_cxx_dr_status @Endilll /lldb/ @JDevlieghere + +/mlir/include/mlir/Interfaces/TilingInterface.* @MaheshRavishankar + +/mlir/lib/Dialect/Linalg/Transforms/DecomposeLinalgOps.cpp @MaheshRavishankar +/mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp @MaheshRavishankar +/mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp @MaheshRavishankar +/mlir/lib/Dialect/MemRef/Transforms/EmulateNarrowType.cpp @MaheshRavishankar +/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp @MaheshRavishankar +/mlir/lib/Interfaces/TilingInterface.* @MaheshRavishankar + +/mlir/**/*EmulateNarrowType* @hanhanW +/mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp @hanhanW +/mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp @hanhanW +/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp @hanhanW +/mlir/lib/Dialect/Tensor/IR/TensorTilingInterfaceImpl.cpp @hanhanW +/mlir/lib/Dialect/Tensor/Transforms/FoldIntoPackAndUnpackPatterns.cpp @hanhanW +/mlir/lib/Dialect/Vector/Transforms/* @hanhanW + +# Transform Dialect in MLIR. +/mlir/include/mlir/Dialect/Transform/* @ftynse +/mlir/lib/Dialect/Transform/* @ftynse + +# SPIR-V in MLIR. +/mlir/**/SPIRV/ @antiagainst @kuhar +/mlir/**/SPIRVTo*/ @antiagainst @kuhar +/mlir/**/*ToSPIRV/ @antiagainst @kuhar +/mlir/tools/mlir-tblgen/SPIRVUtilsGen.cpp @antiagainst @kuhar diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index e4bc53e60066e8d1bb4b9325b4ee02b534186e5c..e4ae2b7c839f9eb5c0f83811408ed65b3539b188 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -1,5 +1,9 @@ clang:dataflow: - - clang/**/Analysis/**/* + - clang/include/clang/Analysis/FlowSensitive/**/* + - clang/lib/Analysis/FlowSensitive/**/* + - clang/unittests/Analysis/FlowSensitive/**/* + - clang/docs/DataFlowAnalysisIntro.md + - clang/docs/DataFlowAnalysisIntroImages/**/* clang:frontend: - clang/lib/AST/**/* diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 17b2fbbae7bd5d66bcfcda4bb0e76bc65e097d4f..9c695e714edd63991718ae3607463293394397ec 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,6 +15,9 @@ on: paths: - 'llvm/docs/**' - 'clang/docs/**' + - 'clang/include/clang/Basic/AttrDocs.td' + - 'clang/include/clang/Driver/ClangOptionDocs.td' + - 'clang/include/clang/Basic/DiagnosticDocs.td' - 'clang-tools-extra/docs/**' - 'lldb/docs/**' - 'libunwind/docs/**' @@ -23,10 +26,15 @@ on: - 'lld/docs/**' - 'openmp/docs/**' - 'polly/docs/**' + - 'flang/docs/**' + - 'flang/include/flang/Optimizer/Dialect/FIROps.td' pull_request: paths: - 'llvm/docs/**' - 'clang/docs/**' + - 'clang/include/clang/Basic/AttrDocs.td' + - 'clang/include/clang/Driver/ClangOptionDocs.td' + - 'clang/include/clang/Basic/DiagnosticDocs.td' - 'clang-tools-extra/docs/**' - 'lldb/docs/**' - 'libunwind/docs/**' @@ -35,11 +43,14 @@ on: - 'lld/docs/**' - 'openmp/docs/**' - 'polly/docs/**' + - 'flang/docs/**' + - 'flang/include/flang/Optimizer/Dialect/FIROps.td' jobs: check-docs-build: name: "Test documentation build" runs-on: ubuntu-latest + if: github.repository == 'llvm/llvm-project' steps: # Don't fetch before checking for file changes to force the file changes # action to use the Github API in pull requests. If it's a push to a @@ -59,6 +70,9 @@ jobs: - 'llvm/docs/**' clang: - 'clang/docs/**' + - 'clang/include/clang/Basic/AttrDocs.td' + - 'clang/include/clang/Driver/ClangOptionDocs.td' + - 'clang/include/clang/Basic/DiagnosticDocs.td' clang-tools-extra: - 'clang-tools-extra/docs/**' lldb: @@ -75,6 +89,9 @@ jobs: - 'openmp/docs/**' polly: - 'polly/docs/**' + flang: + - 'flang/docs/**' + - 'flang/include/flang/Optimizer/Dialect/FIROps.td' - name: Fetch LLVM sources (PR) if: ${{ github.event_name == 'pull_request' }} uses: actions/checkout@v4 @@ -143,4 +160,11 @@ jobs: run: | cmake -B polly-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="polly" -DLLVM_ENABLE_SPHINX=ON ./llvm TZ=UTC ninja -C polly-build docs-polly-html docs-polly-man + - name: Build Flang docs + if: steps.docs-changed-subprojects.outputs.flang_any_changed == 'true' + # TODO(boomanaiden154): Remove the SPHINX_WARNINGS_AS_ERRORS from the + # CMake invocation once the warnings in the flang docs build are fixed. + run: | + cmake -B flang-build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;mlir;flang" -DLLVM_ENABLE_SPHINX=ON -DSPHINX_WARNINGS_AS_ERRORS=OFF ./llvm + TZ=UTC ninja -C flang-build docs-flang-html docs-flang-man diff --git a/.github/workflows/issue-subscriber.yml b/.github/workflows/issue-subscriber.yml index 589142b20607c438f96e9a96140bd9882f91f17b..9a9c8f9c65162b555eb31302c203b22891e245b2 100644 --- a/.github/workflows/issue-subscriber.yml +++ b/.github/workflows/issue-subscriber.yml @@ -15,8 +15,8 @@ jobs: steps: - name: Setup Automation Script run: | - curl -O -L https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/"$GITHUB_SHA"/llvm/utils/git/github-automation.py - curl -O -L https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/"$GITHUB_SHA"/llvm/utils/git/requirements.txt + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/"$GITHUB_SHA"/llvm/utils/git/github-automation.py + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/"$GITHUB_SHA"/llvm/utils/git/requirements.txt chmod a+x github-automation.py pip install -r requirements.txt diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e929c02d11c1d93ceea27784b040b25b27e3e973 --- /dev/null +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -0,0 +1,205 @@ +# This file defines pre-commit CI for libc++, libc++abi, and libunwind (on Github). +# +# We split the configurations in multiple stages with the intent of saving compute time +# when a job fails early in the pipeline. This is why the jobs are marked as `continue-on-error: false`. +# We try to run the CI configurations with the most signal in the first stage. +# +# Stages 1 & 2 are meant to be "smoke tests", and are meant to catch most build/test failures quickly and without using +# too many resources. +# Stage 3 is "everything else", and is meant to catch breakages on more niche or unique configurations. +# +# Therefore, we "fail-fast" for any failures during stages 1 & 2, meaning any job failing cancels all other running jobs, +# under the assumption that if the "smoke tests" fail, then the other configurations will likely fail in the same way. +# However, stage 3 does not fail fast, as it's more likely that any one job failing is a flake or a configuration-specific +# +name: Build and Test libc++ +on: + pull_request: + paths: + - 'libcxx/**' + - 'libcxxabi/**' + - 'libunwind/**' + - 'runtimes/**' + - 'cmake/**' + - '.github/workflows/libcxx-build-and-test.yaml' + +permissions: + contents: read # Default everything to read-only + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + + +env: + CMAKE: "/opt/bin/cmake" + # LLVM POST-BRANCH bump version + # LLVM POST-BRANCH add compiler test for ToT - 1, e.g. "Clang 17" + # LLVM RELEASE bump remove compiler ToT - 3, e.g. "Clang 15" + LLVM_HEAD_VERSION: "18" # Used compiler, update POST-BRANCH. + LLVM_PREVIOUS_VERSION: "17" + LLVM_OLDEST_VERSION: "16" + GCC_STABLE_VERSION: "13" + LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-18" + CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics" + + +jobs: + stage1: + if: github.repository_owner == 'llvm' + runs-on: + group: libcxx-runners-8 + continue-on-error: false + strategy: + fail-fast: true + matrix: + config: [ + 'generic-cxx03', + 'generic-cxx26', + 'generic-modules' + ] + cc: [ 'clang-18' ] + cxx: [ 'clang++-18' ] + clang_tidy: [ 'ON' ] + include: + - config: 'generic-gcc' + cc: 'gcc-13' + cxx: 'g++-13' + clang_tidy: 'OFF' + steps: + - uses: actions/checkout@v4 + - name: ${{ matrix.config }}.${{ matrix.cxx }} + run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + ENABLE_CLANG_TIDY: ${{ matrix.clang_tidy }} + - uses: actions/upload-artifact@v3 + if: always() + with: + name: ${{ matrix.config }}-${{ matrix.cxx }}-results + path: | + **/test-results.xml + **/*.abilist + **/CMakeError.log + **/CMakeOutput.log + **/crash_diagnostics/* + stage2: + if: github.repository_owner == 'llvm' + runs-on: + group: libcxx-runners-8 + needs: [ stage1 ] + continue-on-error: false + strategy: + fail-fast: true + matrix: + config: [ + 'generic-cxx11', + 'generic-cxx14', + 'generic-cxx17', + 'generic-cxx20', + 'generic-cxx23' + ] + cc: [ 'clang-18' ] + cxx: [ 'clang++-18' ] + clang_tidy: [ 'ON' ] + include: + - config: 'generic-gcc-cxx11' + cc: 'gcc-13' + cxx: 'g++-13' + clang_tidy: 'OFF' + - config: 'generic-cxx23' + cc: 'clang-16' + cxx: 'clang++-16' + clang_tidy: 'OFF' + - config: 'generic-cxx23' + cc: 'clang-17' + cxx: 'clang++-17' + clang_tidy: 'OFF' + steps: + - uses: actions/checkout@v4 + - name: ${{ matrix.config }} + run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + ENABLE_CLANG_TIDY: ${{ matrix.clang_tidy }} + - uses: actions/upload-artifact@v3 + if: always() # Upload artifacts even if the build or test suite fails + with: + name: ${{ matrix.config }}-results + path: | + **/test-results.xml + **/*.abilist + **/CMakeError.log + **/CMakeOutput.log + **/crash_diagnostics/* + stage3: + if: github.repository_owner == 'llvm' + needs: [ stage1, stage2 ] + continue-on-error: false + strategy: + fail-fast: false + max-parallel: 8 + matrix: + config: [ + 'generic-abi-unstable', + 'generic-hardening-mode-debug', + 'generic-hardening-mode-extensive', + 'generic-hardening-mode-fast', + 'generic-hardening-mode-fast-with-abi-breaks', + 'generic-merged', + 'generic-modules-lsv', + 'generic-no-exceptions', + 'generic-no-experimental', + 'generic-no-filesystem', + 'generic-no-localization', + 'generic-no-random_device', + 'generic-no-threads', + 'generic-no-tzdb', + 'generic-no-unicode', + 'generic-no-wide-characters', + 'generic-static', + 'generic-with_llvm_unwinder' + ] + machine: [ 'libcxx-runners-8' ] + std_modules: [ 'OFF' ] + include: + - config: 'generic-cxx26' + machine: libcxx-runners-8 + std_modules: 'ON' + - config: 'generic-asan' + machine: libcxx-runners-8 + std_modules: 'OFF' + - config: 'generic-tsan' + machine: libcxx-runners-8 + std_modules: 'OFF' + - config: 'generic-ubsan' + machine: libcxx-runners-8 + std_modules: 'OFF' + # Use a larger machine for MSAN to avoid timeout and memory allocation issues. + - config: 'generic-msan' + machine: libcxx-runners-32 + std_modules: 'OFF' + runs-on: + group: ${{ matrix.machine }} + steps: + - uses: actions/checkout@v4 + - name: ${{ matrix.config }} + run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} + env: + CC: clang-18 + CXX: clang++-18 + ENABLE_CLANG_TIDY: "OFF" + ENABLE_STD_MODULES: ${{ matrix.std_modules }} + - uses: actions/upload-artifact@v3 + if: always() + with: + name: ${{ matrix.config }}-results + path: | + **/test-results.xml + **/*.abilist + **/CMakeError.log + **/CMakeOutput.log + **/crash_diagnostics/* + diff --git a/.github/workflows/pr-code-format.yml b/.github/workflows/pr-code-format.yml index fa0e69211b14788a9003d831d841c019c0551d70..c27c282eb2a19ae5215c9af41eb348006b927c2f 100644 --- a/.github/workflows/pr-code-format.yml +++ b/.github/workflows/pr-code-format.yml @@ -6,23 +6,45 @@ permissions: jobs: code_formatter: runs-on: ubuntu-latest + if: github.repository == 'llvm/llvm-project' steps: - name: Fetch LLVM sources uses: actions/checkout@v4 with: - fetch-depth: 2 # Fetches only the last 2 commits + ref: ${{ github.event.pull_request.head.sha }} + + - name: Checkout through merge base + uses: rmacklin/fetch-through-merge-base@v0 + with: + base_ref: ${{ github.event.pull_request.base.ref }} + head_ref: ${{ github.event.pull_request.head.sha }} + deepen_length: 500 - name: Get changed files id: changed-files uses: tj-actions/changed-files@v39 with: separator: "," - fetch_depth: 2000 # Fetches only the last 2000 commits + skip_initial_fetch: true + + # We need to make sure that we aren't executing/using any code from the + # PR for security reasons as we're using pull_request_target. Checkout + # the target branch with the necessary files. + - name: Fetch code formatting utils + uses: actions/checkout@v4 + with: + sparse-checkout: | + llvm/utils/git/requirements_formatting.txt + llvm/utils/git/code-format-helper.py + sparse-checkout-cone-mode: false + path: code-format-tools - name: "Listed files" + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | echo "Formatting files:" - echo "${{ steps.changed-files.outputs.all_changed_files }}" + echo "$CHANGED_FILES" - name: Install clang-format uses: aminya/setup-cpp@v1 @@ -34,10 +56,10 @@ jobs: with: python-version: '3.11' cache: 'pip' - cache-dependency-path: 'llvm/utils/git/requirements_formatting.txt' + cache-dependency-path: 'code-format-tools/llvm/utils/git/requirements_formatting.txt' - name: Install python dependencies - run: pip install -r llvm/utils/git/requirements_formatting.txt + run: pip install -r code-format-tools/llvm/utils/git/requirements_formatting.txt - name: Run code formatter env: @@ -46,7 +68,7 @@ jobs: END_REV: ${{ github.event.pull_request.head.sha }} CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | - python llvm/utils/git/code-format-helper.py \ + python ./code-format-tools/llvm/utils/git/code-format-helper.py \ --token ${{ secrets.GITHUB_TOKEN }} \ --issue-number $GITHUB_PR_NUMBER \ --start-rev $START_REV \ diff --git a/.github/workflows/pr-subscriber.yml b/.github/workflows/pr-subscriber.yml index ef2ef7b9e4a35c10d3366c5359a4794ad8878856..99f9be771588d9ebf849af887023a3f5ff7b6060 100644 --- a/.github/workflows/pr-subscriber.yml +++ b/.github/workflows/pr-subscriber.yml @@ -15,9 +15,8 @@ jobs: steps: - name: Setup Automation Script run: | - curl -O -L https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/github-automation.py - curl -O -L https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/requirements.txt - curl -O -L https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/.github/workflows/pr-subscriber-wait.py + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/github-automation.py + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/requirements.txt chmod a+x github-automation.py pip install -r requirements.txt diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index e52e52f5d3f36fafa7c2693dc98e40e6113a07ee..75d4f419ab1fdc1e4f1089951824afa20ca876c0 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -57,17 +57,52 @@ jobs: fi bash .github/workflows/set-release-binary-outputs.sh "${{ github.actor }}" "$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 }}" + needs: prepare + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-22.04 + steps: + - name: Checkout LLVM + uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag || github.ref_name }} + + - name: Install Ninja + uses: llvm/actions/install-ninja@main + + - name: Setup sccache + uses: hendrikmuhs/ccache-action@v1 + with: + max-size: 250M + key: sccache-${{ matrix.os }}-release + variant: sccache + + - name: Build Clang + run: | + cmake -G Ninja -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_BUILD_TYPE=Release -DCMAKE_ENABLE_ASSERTIONS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DLLVM_ENABLE_PROJECTS=clang -S llvm -B build + ninja -v -C build + + build-binaries: name: ${{ matrix.target.triple }} permissions: contents: write # To upload assets to release. - needs: prepare + needs: + - prepare + - fill-cache runs-on: ${{ matrix.target.runs-on }} 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 @@ -81,6 +116,14 @@ jobs: ref: ${{ needs.prepare.outputs.ref }} path: ${{ needs.prepare.outputs.build-dir }}/llvm-project + - name: Setup sccache + uses: hendrikmuhs/ccache-action@v1 + with: + max-size: 250M + key: sccache-${{ matrix.target.os }}-release + save: false + variant: sccache + - name: Install Brew build dependencies if: matrix.target.brew-build-deps != '' run: brew install ${{ matrix.target.brew-build-deps }} @@ -102,7 +145,8 @@ jobs: -triple ${{ matrix.target.triple }} \ -use-ninja \ -no-checkout \ - -no-test-suite + -no-test-suite \ + -configure-flags "-DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache" - name: Upload binaries if: ${{ always() && needs.prepare.outputs.upload == 'true' }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 270f58f777ce84d09255f4de910b48e8ae2b13f7..b8e8ab26c3ffa61a1f3a76883034564086d1d237 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -22,6 +22,7 @@ jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest + if: github.repository == 'llvm/llvm-project' permissions: # Needed to upload the results to code-scanning dashboard. security-events: write diff --git a/.github/workflows/sync-release-repo.yml b/.github/workflows/sync-release-repo.yml deleted file mode 100644 index c328a9133e179de52f7c362fcc97bc89a00610f1..0000000000000000000000000000000000000000 --- a/.github/workflows/sync-release-repo.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Sync Release Repositories -permissions: - contents: write -on: - workflow_dispatch -env: - RELEASE_BRANCH: release/17.x - GH_TOKEN: ${{ secrets.RELEASE_WORKFLOW_PUSH_SECRET }} -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - - name: Run Sync Script - run: | - llvm/utils/git/sync-release-repo.sh diff --git a/.mailmap b/.mailmap index 4c787a45603994d68d42fab3ae45a06f89e77440..867af2c55f3bc4e9f2e217b07ba04e76c7180800 100644 --- a/.mailmap +++ b/.mailmap @@ -34,6 +34,7 @@ Jon Roelofs Jon Roelofs +Jonathan Thackray LLVM GN Syncbot Martin Storsjö Med Ismail Bennani diff --git a/bolt/docs/doxygen.cfg.in b/bolt/docs/doxygen.cfg.in index 421ef4760aa37f0d2ad01f88184b68b2df28ac53..01571e79555cec5f0aa1bdc46e33393563e8033c 100644 --- a/bolt/docs/doxygen.cfg.in +++ b/bolt/docs/doxygen.cfg.in @@ -1954,7 +1954,7 @@ PREDEFINED = EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all refrences to function-like macros that are alone on a line, have an +# remove all references to function-like macros that are alone on a line, have an # all uppercase name, and do not end with a semicolon. Such function macros are # typically used for boiler-plate code, and will confuse the parser if not # removed. diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index 59460105f23137167c7a53589779a628ce406404..48a8b2a5d83f2b0a35d47d756289d7ec2a5bc814 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -611,6 +611,9 @@ public: /// Indicates if the binary contains split functions. bool HasSplitFunctions{false}; + /// Indicates if the function ordering of the binary is finalized. + bool HasFinalizedFunctionOrder{false}; + /// Is the binary always loaded at a fixed address. Shared objects and /// position-independent executables (PIEs) are examples of binaries that /// will have HasFixedLoadAddress set to false. @@ -680,6 +683,15 @@ public: /// the execution of the binary is completed. std::optional FiniFunctionAddress; + /// DT_FINI. + std::optional FiniAddress; + + /// DT_FINI_ARRAY. Only used when DT_FINI is not set. + std::optional FiniArrayAddress; + + /// DT_FINI_ARRAYSZ. Only used when DT_FINI is not set. + std::optional FiniArraySize; + /// Page alignment used for code layout. uint64_t PageAlign{HugePageSize}; @@ -941,7 +953,7 @@ public: bool registerFragment(BinaryFunction &TargetFunction, BinaryFunction &Function) const; - /// Add unterprocedural reference for \p Function to \p Address + /// Add interprocedural reference for \p Function to \p Address void addInterproceduralReference(BinaryFunction *Function, uint64_t Address) { InterproceduralReferences.push_back({Function, Address}); } @@ -1221,6 +1233,9 @@ public: /// /// Return the pair where the first size is for the main part, and the second /// size is for the cold one. + /// Modify BinaryBasicBlock::OutputAddressRange for each basic block in the + /// function in place so that BinaryBasicBlock::getOutputSize() gives the + /// emitted size of the basic block. std::pair calculateEmittedSize(BinaryFunction &BF, bool FixBranches = true); @@ -1230,8 +1245,8 @@ public: uint64_t computeInstructionSize(const MCInst &Inst, const MCCodeEmitter *Emitter = nullptr) const { - if (auto Size = MIB->getAnnotationWithDefault(Inst, "Size")) - return Size; + if (std::optional Size = MIB->getSize(Inst)) + return *Size; if (!Emitter) Emitter = this->MCE.get(); @@ -1281,6 +1296,9 @@ public: /// Return true if the function should be emitted to the output file. bool shouldEmit(const BinaryFunction &Function) const; + /// Dump the assembly representation of MCInst to debug output. + void dump(const MCInst &Inst) const; + /// Print the string name for a CFI operation. static void printCFI(raw_ostream &OS, const MCCFIInstruction &Inst); diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h index c67ddccbf4892a70e0e0f78736f4fd4e96f565c2..72c360ca0c2db66b98c37fd278ed2c058757082d 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -319,10 +319,6 @@ private: /// Execution halts whenever this function is entered. bool TrapsOnEntry{false}; - /// True if the function had an indirect branch with a fixed internal - /// destination. - bool HasFixedIndirectBranch{false}; - /// True if the function is a fragment of another function. This means that /// this function could only be entered via its parent or one of its sibling /// fragments. It could be entered at any basic block. It can also return @@ -1445,7 +1441,8 @@ public: /// Rebuilds BBs layout, ignoring dead BBs. Returns the number of removed /// BBs and the removed number of bytes of code. - std::pair eraseInvalidBBs(); + std::pair + eraseInvalidBBs(const MCCodeEmitter *Emitter = nullptr); /// Get the relative order between two basic blocks in the original /// layout. The result is > 0 if B occurs before A and < 0 if B @@ -2172,7 +2169,7 @@ public: /// is corrupted. If it is unable to fix it, it returns false. bool finalizeCFIState(); - /// Return true if this function needs an address-transaltion table after + /// Return true if this function needs an address-translation table after /// its code emission. bool requiresAddressTranslation() const; @@ -2309,15 +2306,10 @@ public: /// removed. uint64_t translateInputToOutputAddress(uint64_t Address) const; - /// Take address ranges corresponding to the input binary and translate - /// them to address ranges in the output binary. - DebugAddressRangesVector translateInputToOutputRanges( - const DWARFAddressRangesVector &InputRanges) const; - - /// Similar to translateInputToOutputRanges() but operates on location lists - /// and moves associated data to output location lists. - DebugLocationsVector - translateInputToOutputLocationList(const DebugLocationsVector &InputLL) const; + /// Translate a contiguous range of addresses in the input binary into a set + /// of ranges in the output binary. + DebugAddressRangesVector + translateInputToOutputRange(DebugAddressRange InRange) const; /// Return true if the function is an AArch64 linker inserted veneer bool isAArch64Veneer() const; diff --git a/bolt/include/bolt/Core/BinarySection.h b/bolt/include/bolt/Core/BinarySection.h index 326d088d1f0465d1d18ebe645f23bb834345f80f..70914f59157d24ef05e812c9372fba409eb81d1a 100644 --- a/bolt/include/bolt/Core/BinarySection.h +++ b/bolt/include/bolt/Core/BinarySection.h @@ -127,7 +127,7 @@ class BinarySection { return *ContentsOrErr; } - /// Get the set of relocations refering to data in this section that + /// Get the set of relocations referring to data in this section that /// has been reordered. The relocation offsets will be modified to /// reflect the new data locations. RelocationSetType reorderRelocations(bool Inplace) const; @@ -375,8 +375,12 @@ public: /// Add a dynamic relocation at the given /p Offset. void addDynamicRelocation(uint64_t Offset, MCSymbol *Symbol, uint64_t Type, uint64_t Addend, uint64_t Value = 0) { - assert(Offset < getSize() && "offset not within section bounds"); - DynamicRelocations.emplace(Relocation{Offset, Symbol, Type, Addend, Value}); + addDynamicRelocation(Relocation{Offset, Symbol, Type, Addend, Value}); + } + + void addDynamicRelocation(const Relocation &Reloc) { + assert(Reloc.Offset < getSize() && "offset not within section bounds"); + DynamicRelocations.emplace(Reloc); } /// Add relocation against the original contents of this section. @@ -410,6 +414,18 @@ public: return Itr != DynamicRelocations.end() ? &*Itr : nullptr; } + std::optional takeDynamicRelocationAt(uint64_t Offset) { + Relocation Key{Offset, 0, 0, 0, 0}; + auto Itr = DynamicRelocations.find(Key); + + if (Itr == DynamicRelocations.end()) + return std::nullopt; + + Relocation Reloc = *Itr; + DynamicRelocations.erase(Itr); + return Reloc; + } + uint64_t hash(const BinaryData &BD) const { std::map Cache; return hash(BD, Cache); diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h index fb86e59468b835abf4c124b8d4dd9bb292c01d2e..1c5252142d4ebf52c205d50443ca02eb8e356ff6 100644 --- a/bolt/include/bolt/Core/DIEBuilder.h +++ b/bolt/include/bolt/Core/DIEBuilder.h @@ -273,7 +273,8 @@ public: void buildCompileUnits(const std::vector &CUs); /// Preventing implicit conversions. template void buildCompileUnits(T) = delete; - void buildBoth(); + /// Builds DWO Unit. For DWARF5 this includes the type units. + void buildDWOUnit(DWARFUnit &U); /// Returns DWARFUnitInfo for DWARFUnit DWARFUnitInfo &getUnitInfoByDwarfUnit(const DWARFUnit &DwarfUnit) { diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h index 3fdc0534c000ae933ffe01449b591063f64e0607..23430cc36167c827bcca7470ce5fa68e3709e04a 100644 --- a/bolt/include/bolt/Core/DebugData.h +++ b/bolt/include/bolt/Core/DebugData.h @@ -18,6 +18,7 @@ #include "llvm/CodeGen/DIE.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/MC/MCDwarf.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/raw_ostream.h" #include @@ -63,7 +64,7 @@ std::optional findAttributeInfo(const DWARFDie DIE, dwarf::Attribute Attr); // DWARF5 Header in order of encoding. -// Types represent encodnig sizes. +// Types represent encoding sizes. using UnitLengthType = uint32_t; using VersionType = uint16_t; using AddressSizeType = uint8_t; @@ -95,6 +96,12 @@ static inline bool operator<(const DebugAddressRange &LHS, return std::tie(LHS.LowPC, LHS.HighPC) < std::tie(RHS.LowPC, RHS.HighPC); } +inline raw_ostream &operator<<(raw_ostream &OS, + const DebugAddressRange &Range) { + OS << formatv("[{0:x}, {1:x})", Range.LowPC, Range.HighPC); + return OS; +} + /// DebugAddressRangesVector - represents a set of absolute address ranges. using DebugAddressRangesVector = SmallVector; @@ -106,6 +113,18 @@ struct DebugLocationEntry { SmallVector Expr; }; +inline raw_ostream &operator<<(raw_ostream &OS, + const DebugLocationEntry &Entry) { + OS << formatv("[{0:x}, {1:x}) : [", Entry.LowPC, Entry.HighPC); + const char *Sep = ""; + for (unsigned Byte : Entry.Expr) { + OS << Sep << Byte; + Sep = ", "; + } + OS << "]"; + return OS; +} + using DebugLocationsVector = SmallVector; /// References a row in a DWARFDebugLine::LineTable by the DWARF @@ -353,13 +372,13 @@ protected: } /// Updates AddressToIndex Map. - /// Follows the same symantics as unordered map []. + /// Follows the same semantics as unordered map []. void updateAddressToIndex(uint64_t Address, uint32_t Index) { AddressToIndex[Address] = Index; } /// Updates IndexToAddress Map. - /// Follows the same symantics as unordered map []. + /// Follows the same semantics as unordered map []. void updateIndexToAddrss(uint64_t Address, uint32_t Index) { IndexToAddress[Index] = Address; } @@ -455,7 +474,7 @@ public: } /// Adds string to .debug_str. - /// On first invokation it initializes internal data stractures. + /// On first invocation it initializes internal data structures. uint32_t addString(StringRef Str); /// Returns False if no strings were added to .debug_str. @@ -466,7 +485,7 @@ private: std::mutex WriterMutex; /// Initializes Buffer and Stream. void initialize(); - /// Creats internal data stractures. + /// Creates internal data structures. void create(); std::unique_ptr StrBuffer; std::unique_ptr StrStream; @@ -566,7 +585,7 @@ public: void finalize(DIEBuilder &DIEBldr, DIE &Die) override; /// Returns CU ID. - /// For Skelton CU it is a CU Offset. + /// For Skeleton CU it is a CU Offset. /// For DWO CU it is a DWO ID. uint64_t getCUID() const { return CU.isDWOUnit() ? *CU.getDWOId() : CU.getOffset(); diff --git a/bolt/include/bolt/Core/DynoStats.h b/bolt/include/bolt/Core/DynoStats.h index 7f24c8c99c94f75585b494d7ef2bf0a29d300521..65256719ba06bdf2d0668404c605ca1c5ea01aa0 100644 --- a/bolt/include/bolt/Core/DynoStats.h +++ b/bolt/include/bolt/Core/DynoStats.h @@ -121,7 +121,7 @@ public: /// Maps instruction opcodes to: /// 1. Accumulated executed instruction counts. /// 2. a multimap that records highest execution counts, function names, - /// and BB offsets where intructions of these opcodes occur. + /// and BB offsets where instructions of these opcodes occur. using MaxOpcodeHistogramTy = std::multimap>; using OpcodeHistogramTy = diff --git a/bolt/include/bolt/Core/Exceptions.h b/bolt/include/bolt/Core/Exceptions.h index 18dfa08097befe9d937c552fde92d4e248f13962..7c09b5b768fe84e964d51a405577fa0a61dc3f32 100644 --- a/bolt/include/bolt/Core/Exceptions.h +++ b/bolt/include/bolt/Core/Exceptions.h @@ -77,7 +77,7 @@ public: /// void PatcherCallback(uint64_t Value, uint64_t Offset, uint64_t Type); /// /// where Value is a value of the reference, Offset - is an offset into the - /// frame data at which the reference occured, and Type is a DWARF encoding + /// frame data at which the reference occurred, and Type is a DWARF encoding /// type of the reference. static Error parse(DWARFDataExtractor Data, uint64_t EHFrameAddress, PatcherCallbackTy PatcherCallback); diff --git a/bolt/include/bolt/Core/MCPlus.h b/bolt/include/bolt/Core/MCPlus.h index 31cc9071de76acef19812f71a3c312f9ad6558a5..b6a9e73f2347e71faa09beb4ae2aad9ff7fc5747 100644 --- a/bolt/include/bolt/Core/MCPlus.h +++ b/bolt/include/bolt/Core/MCPlus.h @@ -32,11 +32,16 @@ namespace MCPlus { /// pad and the uint64_t represents the action. using MCLandingPad = std::pair; -/// An extension to MCInst is provided via an extra operand of type MCInst with -/// ANNOTATION_LABEL opcode (i.e. we are tying an annotation instruction to an -/// existing one). The annotation instruction contains a list of Immediate -/// operands. Each operand either contains a value, or is a pointer to -/// an instance of class MCAnnotation. +/// An extension to MCInst is provided via extra operands, i.e. operands that +/// are not used in the instruction assembly. Any kind of metadata can be +/// attached to MCInst with this "annotation" extension using MCPlusBuilder +/// interface. +// +/// The first extra operand must be of type kInst with an empty (nullptr) +/// value. The kInst operand type is unused on most non-VLIW architectures. +/// We use it to mark the beginning of annotations operands. The rest of the +/// operands are of Immediate type with annotation info encoded into the value +/// of the immediate. /// /// There are 2 distinct groups of annotations. The first group is a first-class /// annotation that affects semantics of the instruction, such as an @@ -55,7 +60,7 @@ using MCLandingPad = std::pair; /// of their corresponding operand. /// /// Annotations in the second group could be addressed either by name, or by -/// by and index which could be queried by providing a name. +/// by index which could be queried by providing the name. class MCAnnotation { public: enum Kind { @@ -67,6 +72,7 @@ public: kConditionalTailCall, /// CTC. kOffset, /// Offset in the function. kLabel, /// MCSymbol pointing to this instruction. + kSize, /// Size of the instruction. kGeneric /// First generic annotation. }; @@ -106,10 +112,11 @@ private: /// Return a number of operands in \Inst excluding operands representing /// annotations. inline unsigned getNumPrimeOperands(const MCInst &Inst) { - if (Inst.getNumOperands() > 0 && std::prev(Inst.end())->isInst()) { - assert(std::prev(Inst.end())->getInst()->getOpcode() == - TargetOpcode::ANNOTATION_LABEL); - return Inst.getNumOperands() - 1; + for (signed I = Inst.getNumOperands() - 1; I >= 0; --I) { + if (Inst.getOperand(I).isInst()) + return I; + if (!Inst.getOperand(I).isImm()) + return Inst.getNumOperands(); } return Inst.getNumOperands(); } diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index d136c627bc5cc1033add30c58fd1b3777d843778..687b49a3cbda43a2a16617dfd39342f56bf32f63 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -65,7 +65,6 @@ public: private: /// A struct that represents a single annotation allocator struct AnnotationAllocator { - SpecificBumpPtrAllocator MCInstAllocator; BumpPtrAllocator ValueAllocator; std::unordered_set AnnotationPool; }; @@ -97,60 +96,62 @@ private: return SignExtend64<56>(ImmValue & 0xff'ffff'ffff'ffffULL); } - MCInst *getAnnotationInst(const MCInst &Inst) const { - if (Inst.getNumOperands() == 0) - return nullptr; + std::optional getFirstAnnotationOpIndex(const MCInst &Inst) const { + const unsigned NumPrimeOperands = MCPlus::getNumPrimeOperands(Inst); + if (Inst.getNumOperands() == NumPrimeOperands) + return std::nullopt; - const MCOperand &LastOp = Inst.getOperand(Inst.getNumOperands() - 1); - if (!LastOp.isInst()) - return nullptr; + assert(Inst.getOperand(NumPrimeOperands).getInst() == nullptr && + "Empty instruction expected."); - MCInst *AnnotationInst = const_cast(LastOp.getInst()); - assert(AnnotationInst->getOpcode() == TargetOpcode::ANNOTATION_LABEL); + return NumPrimeOperands + 1; + } - return AnnotationInst; + MCInst::iterator getAnnotationInstOp(MCInst &Inst) const { + for (MCInst::iterator Iter = Inst.begin(); Iter != Inst.end(); ++Iter) { + if (Iter->isInst()) { + assert(Iter->getInst() == nullptr && "Empty instruction expected."); + return Iter; + } + } + return Inst.end(); } - void removeAnnotationInst(MCInst &Inst) const { - assert(getAnnotationInst(Inst) && "Expected annotation instruction."); - Inst.erase(std::prev(Inst.end())); - assert(!getAnnotationInst(Inst) && - "More than one annotation instruction detected."); + void removeAnnotations(MCInst &Inst) const { + Inst.erase(getAnnotationInstOp(Inst), Inst.end()); } - void setAnnotationOpValue(MCInst &Inst, unsigned Index, int64_t Value, - AllocatorIdTy AllocatorId = 0) { - MCInst *AnnotationInst = getAnnotationInst(Inst); - if (!AnnotationInst) { - AnnotationAllocator &Allocator = getAnnotationAllocator(AllocatorId); - AnnotationInst = new (Allocator.MCInstAllocator.Allocate()) MCInst(); - AnnotationInst->setOpcode(TargetOpcode::ANNOTATION_LABEL); - Inst.addOperand(MCOperand::createInst(AnnotationInst)); + void setAnnotationOpValue(MCInst &Inst, unsigned Index, int64_t Value) const { + const int64_t AnnotationValue = encodeAnnotationImm(Index, Value); + const std::optional FirstAnnotationOp = + getFirstAnnotationOpIndex(Inst); + if (!FirstAnnotationOp) { + Inst.addOperand(MCOperand::createInst(nullptr)); + Inst.addOperand(MCOperand::createImm(AnnotationValue)); + return; } - const int64_t AnnotationValue = encodeAnnotationImm(Index, Value); - for (int I = AnnotationInst->getNumOperands() - 1; I >= 0; --I) { - int64_t ImmValue = AnnotationInst->getOperand(I).getImm(); + for (unsigned I = *FirstAnnotationOp; I < Inst.getNumOperands(); ++I) { + const int64_t ImmValue = Inst.getOperand(I).getImm(); if (extractAnnotationIndex(ImmValue) == Index) { - AnnotationInst->getOperand(I).setImm(AnnotationValue); + Inst.getOperand(I).setImm(AnnotationValue); return; } } - AnnotationInst->addOperand(MCOperand::createImm(AnnotationValue)); + Inst.addOperand(MCOperand::createImm(AnnotationValue)); } std::optional getAnnotationOpValue(const MCInst &Inst, unsigned Index) const { - const MCInst *AnnotationInst = getAnnotationInst(Inst); - if (!AnnotationInst) + std::optional FirstAnnotationOp = getFirstAnnotationOpIndex(Inst); + if (!FirstAnnotationOp) return std::nullopt; - for (int I = AnnotationInst->getNumOperands() - 1; I >= 0; --I) { - int64_t ImmValue = AnnotationInst->getOperand(I).getImm(); - if (extractAnnotationIndex(ImmValue) == Index) { + for (unsigned I = *FirstAnnotationOp; I < Inst.getNumOperands(); ++I) { + const int64_t ImmValue = Inst.getOperand(I).getImm(); + if (extractAnnotationIndex(ImmValue) == Index) return extractAnnotationValue(ImmValue); - } } return std::nullopt; @@ -172,21 +173,18 @@ protected: /// AnnotationNameIndexMap and AnnotationsNames. mutable llvm::sys::RWMutex AnnotationNameMutex; - /// Allocate the TailCall annotation value. Clients of the target-specific + /// Set TailCall annotation value to true. Clients of the target-specific /// MCPlusBuilder classes must use convert/lower/create* interfaces instead. - void setTailCall(MCInst &Inst); + void setTailCall(MCInst &Inst) const; public: /// Transfer annotations from \p SrcInst to \p DstInst. void moveAnnotations(MCInst &&SrcInst, MCInst &DstInst) const { - assert(!getAnnotationInst(DstInst) && - "Destination instruction should not have annotations."); - const MCInst *AnnotationInst = getAnnotationInst(SrcInst); - if (!AnnotationInst) - return; + MCInst::iterator AnnotationOp = getAnnotationInstOp(SrcInst); + for (MCInst::iterator Iter = AnnotationOp; Iter != SrcInst.end(); ++Iter) + DstInst.addOperand(*Iter); - DstInst.addOperand(MCOperand::createInst(AnnotationInst)); - removeAnnotationInst(SrcInst); + SrcInst.erase(AnnotationOp, SrcInst.end()); } /// Return iterator range covering def operands. @@ -390,7 +388,6 @@ public: Allocator.AnnotationPool.clear(); Allocator.ValueAllocator.Reset(); - Allocator.MCInstAllocator.DestroyAll(); } } @@ -560,10 +557,6 @@ public: return false; } - virtual bool isEHLabel(const MCInst &Inst) const { - return Inst.getOpcode() == TargetOpcode::EH_LABEL; - } - virtual bool isPop(const MCInst &Inst) const { return false; } /// Return true if the instruction is used to terminate an indirect branch. @@ -1089,7 +1082,7 @@ public: /// Replace the compound memory operand of Inst with an immediate operand. /// The value of the immediate operand is computed by reading the \p - /// ConstantData array starting from \p offset and assuming little-endianess. + /// ConstantData array starting from \p offset and assuming little-endianness. /// Return true on success. The given instruction is modified in place. virtual bool replaceMemOperandWithImm(MCInst &Inst, StringRef ConstantData, uint64_t Offset) const { @@ -1128,20 +1121,19 @@ public: std::optional getEHInfo(const MCInst &Inst) const; /// Add handler and action info for call instruction. - void addEHInfo(MCInst &Inst, const MCPlus::MCLandingPad &LP); + void addEHInfo(MCInst &Inst, const MCPlus::MCLandingPad &LP) const; /// Update exception-handling info for the invoke instruction \p Inst. /// Return true on success and false otherwise, e.g. if the instruction is /// not an invoke. - bool updateEHInfo(MCInst &Inst, const MCPlus::MCLandingPad &LP); + bool updateEHInfo(MCInst &Inst, const MCPlus::MCLandingPad &LP) const; /// Return non-negative GNU_args_size associated with the instruction /// or -1 if there's no associated info. int64_t getGnuArgsSize(const MCInst &Inst) const; /// Add the value of GNU_args_size to Inst if it already has EH info. - void addGnuArgsSize(MCInst &Inst, int64_t GnuArgsSize, - AllocatorIdTy AllocId = 0); + void addGnuArgsSize(MCInst &Inst, int64_t GnuArgsSize) const; /// Return jump table addressed by this instruction. uint64_t getJumpTable(const MCInst &Inst) const; @@ -1154,7 +1146,7 @@ public: AllocatorIdTy AllocId = 0); /// Disassociate instruction with a jump table. - bool unsetJumpTable(MCInst &Inst); + bool unsetJumpTable(MCInst &Inst) const; /// Return destination of conditional tail call instruction if \p Inst is one. std::optional getConditionalTailCall(const MCInst &Inst) const; @@ -1162,11 +1154,11 @@ public: /// Mark the \p Instruction as a conditional tail call, and set its /// destination address if it is known. If \p Instruction was already marked, /// update its destination with \p Dest. - bool setConditionalTailCall(MCInst &Inst, uint64_t Dest = 0); + bool setConditionalTailCall(MCInst &Inst, uint64_t Dest = 0) const; /// If \p Inst was marked as a conditional tail call convert it to a regular /// branch. Return true if the instruction was converted. - bool unsetConditionalTailCall(MCInst &Inst); + bool unsetConditionalTailCall(MCInst &Inst) const; /// Return offset of \p Inst in the original function, if available. std::optional getOffset(const MCInst &Inst) const; @@ -1175,17 +1167,23 @@ public: uint32_t getOffsetWithDefault(const MCInst &Inst, uint32_t Default) const; /// Set offset of \p Inst in the original function. - bool setOffset(MCInst &Inst, uint32_t Offset, AllocatorIdTy AllocatorId = 0); + bool setOffset(MCInst &Inst, uint32_t Offset) const; /// Remove offset annotation. - bool clearOffset(MCInst &Inst); + bool clearOffset(MCInst &Inst) const; /// Return the label of \p Inst, if available. - std::optional getLabel(const MCInst &Inst) const; + MCSymbol *getLabel(const MCInst &Inst) const; /// Set the label of \p Inst. This label will be emitted right before \p Inst /// is emitted to MCStreamer. - bool setLabel(MCInst &Inst, MCSymbol *Label, AllocatorIdTy AllocatorId = 0); + bool setLabel(MCInst &Inst, MCSymbol *Label) const; + + /// Get instruction size specified via annotation. + std::optional getSize(const MCInst &Inst) const; + + /// Set instruction size. + void setSize(MCInst &Inst, uint32_t Size) const; /// Return MCSymbol that represents a target of this instruction at a given /// operand number \p OpNum. If there's no symbol associated with @@ -1740,15 +1738,6 @@ public: return false; } - virtual bool createEHLabel(MCInst &Inst, const MCSymbol *Label, - MCContext *Ctx) const { - Inst.setOpcode(TargetOpcode::EH_LABEL); - Inst.clear(); - Inst.addOperand(MCOperand::createExpr( - MCSymbolRefExpr::create(Label, MCSymbolRefExpr::VK_None, *Ctx))); - return true; - } - /// Extract a symbol and an addend out of the fixup value expression. /// /// Only the following limited expression types are supported: @@ -1820,6 +1809,8 @@ public: const ValueType &addAnnotation(MCInst &Inst, unsigned Index, const ValueType &Val, AllocatorIdTy AllocatorId = 0) { + assert(Index >= MCPlus::MCAnnotation::kGeneric && + "Generic annotation type expected."); assert(!hasAnnotation(Inst, Index)); AnnotationAllocator &Allocator = getAnnotationAllocator(AllocatorId); auto *A = new (Allocator.ValueAllocator) @@ -1827,8 +1818,7 @@ public: if (!std::is_trivial::value) Allocator.AnnotationPool.insert(A); - setAnnotationOpValue(Inst, Index, reinterpret_cast(A), - AllocatorId); + setAnnotationOpValue(Inst, Index, reinterpret_cast(A)); return A->getValue(); } @@ -1961,21 +1951,21 @@ public: /// /// Return true if the annotation was removed, false if the annotation /// was not present. - bool removeAnnotation(MCInst &Inst, unsigned Index); + bool removeAnnotation(MCInst &Inst, unsigned Index) const; /// Remove annotation associated with \p Name. /// /// Return true if the annotation was removed, false if the annotation /// was not present. - bool removeAnnotation(MCInst &Inst, StringRef Name) { + bool removeAnnotation(MCInst &Inst, StringRef Name) const { const auto Index = getAnnotationIndex(Name); if (!Index) return false; return removeAnnotation(Inst, *Index); } - /// Remove meta-data, but don't destroy it. - void stripAnnotations(MCInst &Inst, bool KeepTC = false); + /// Remove meta-data from the instruction, but don't destroy it. + void stripAnnotations(MCInst &Inst, bool KeepTC = false) const; virtual InstructionListType createInstrumentedIndirectCall(MCInst &&CallInst, MCSymbol *HandlerFuncAddr, diff --git a/bolt/include/bolt/Core/Relocation.h b/bolt/include/bolt/Core/Relocation.h index bc16d952e7a291e696673dd4291d170455807918..933f62a31f8fd7357f62fca5a34b1334b6a2da07 100644 --- a/bolt/include/bolt/Core/Relocation.h +++ b/bolt/include/bolt/Core/Relocation.h @@ -61,7 +61,7 @@ struct Relocation { /// Handle special cases when relocation should not be processed by BOLT or /// change relocation \p Type to proper one before continuing if \p Contents - /// and \P Type mismatch occured. + /// and \P Type mismatch occurred. static bool skipRelocationProcess(uint64_t &Type, uint64_t Contents); // Adjust value depending on relocation type (make it PC relative or not) @@ -124,6 +124,10 @@ struct Relocation { /// otherwise. bool isRelative() const { return isRelative(Type); } + /// Return true if this relocation is R_*_IRELATIVE type. Return false + /// otherwise. + bool isIRelative() const { return isIRelative(Type); } + /// Emit relocation at a current \p Streamer' position. The caller is /// responsible for setting the position correctly. size_t emit(MCStreamer *Streamer) const; diff --git a/bolt/include/bolt/Passes/FixRelaxationPass.h b/bolt/include/bolt/Passes/FixRelaxationPass.h index 398dbf31bba62aaf47b8f9498e6ba3c75cf13009..45ee9cb736037c0c405115ed10cb583f065b09a6 100644 --- a/bolt/include/bolt/Passes/FixRelaxationPass.h +++ b/bolt/include/bolt/Passes/FixRelaxationPass.h @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// // // This file declares the FixRelaxations class, which locates instructions with -// wrong targets and fixes them. Such problems usually occures when linker +// wrong targets and fixes them. Such problems usually occurs when linker // relaxes (changes) instructions, but doesn't fix relocations types properly // for them. // diff --git a/bolt/include/bolt/Passes/InstrumentationSummary.h b/bolt/include/bolt/Passes/InstrumentationSummary.h index 8e991d1f78ff280b74fbb1e791a9a30b6d253663..2bfd5b4a7d3e17d6b01b0534f2de559f8c7bf036 100644 --- a/bolt/include/bolt/Passes/InstrumentationSummary.h +++ b/bolt/include/bolt/Passes/InstrumentationSummary.h @@ -35,7 +35,7 @@ class BinaryFunction; // counters. It also provides function names and offsets used when writing the // fdata file. -// Location information -- analoguous to the concept of the same name in fdata +// Location information -- analogous to the concept of the same name in fdata // writing/reading. The difference is that the name is stored as an index to a // string table written separately. struct LocDescription { diff --git a/bolt/include/bolt/Passes/ReorderAlgorithm.h b/bolt/include/bolt/Passes/ReorderAlgorithm.h index b3d372d1c90ab83ad10d5064de0c618a8baab460..95d9e831ec68bef002074dee4dfe229c885dac23 100644 --- a/bolt/include/bolt/Passes/ReorderAlgorithm.h +++ b/bolt/include/bolt/Passes/ReorderAlgorithm.h @@ -42,7 +42,7 @@ public: virtual void clusterBasicBlocks(BinaryFunction &BF, bool ComputeEdges = false) = 0; - /// Compute for each cluster its averagae execution frequency, that is + /// Compute for each cluster its average execution frequency, that is /// the sum of average frequencies of its blocks (execution count / # instrs). /// The average frequencies are stored in the AvgFreq vector, index by the /// cluster indices in the Clusters vector. @@ -128,7 +128,7 @@ private: // A -> B with execution count 500, // A -> C with execution count 100, and // D -> B with execution count 150 - // wher B, C are the only successors of A and A, D are thr only predessecors + // where B, C are the only successors of A and A, D are the only predecessors // of B. Then if we choose to layout edge A -> B as a fallthrough, the win in // branches would be 500 - 100 - 150 = 250. That is the weight of edge A->B. int64_t calculateWeight(const EdgeTy &E, const BinaryFunction &BF) const; @@ -144,8 +144,8 @@ public: void reset() override; }; -/// Objects of this class implement various basic block reordering alogrithms. -/// Most of these algorithms depend on a clustering alogrithm. +/// Objects of this class implement various basic block reordering algorithms. +/// Most of these algorithms depend on a clustering algorithm. /// Here we have 3 conflicting goals as to how to layout clusters. If we want /// to minimize jump offsets, we should put clusters with heavy inter-cluster /// dependence as close as possible. If we want to maximize the probability diff --git a/bolt/include/bolt/Passes/SplitFunctions.h b/bolt/include/bolt/Passes/SplitFunctions.h index 4058f3317dfbdbbd74f3f48040ca0b745f5984ca..28e9e79d1b8f87c9442163d12903315a84a6e892 100644 --- a/bolt/include/bolt/Passes/SplitFunctions.h +++ b/bolt/include/bolt/Passes/SplitFunctions.h @@ -23,6 +23,9 @@ enum SplitFunctionsStrategy : char { /// Split each function into a hot and cold fragment using profiling /// information. Profile2 = 0, + /// Split each function into a hot, warm, and cold fragment using + /// profiling information. + CDSplit, /// Split each function into a hot and cold fragment at a randomly chosen /// split point (ignoring any available profiling information). Random2, @@ -40,7 +43,7 @@ public: virtual ~SplitStrategy() = default; virtual bool canSplit(const BinaryFunction &BF) = 0; - virtual bool keepEmpty() = 0; + virtual bool compactFragments() = 0; virtual void fragment(const BlockIt Start, const BlockIt End) = 0; }; diff --git a/bolt/include/bolt/Passes/StackReachingUses.h b/bolt/include/bolt/Passes/StackReachingUses.h index ff2c849f47244494eb25e909c9e5186b582fe04d..ee16403d754a869d84b250edc5751392effdda41 100644 --- a/bolt/include/bolt/Passes/StackReachingUses.h +++ b/bolt/include/bolt/Passes/StackReachingUses.h @@ -41,7 +41,7 @@ public: /// Answer whether the stack position written by the store represented in /// \p StoreFIE is loaded from or consumed in any way. The set of all /// relevant expressions reaching this store should be in \p Candidates. - /// If \p IncludelocalAccesses is false, we only consider wheter there is + /// If \p IncludelocalAccesses is false, we only consider whether there is /// a callee that consumes this stack position. bool isStoreUsed(const FrameIndexEntry &StoreFIE, ExprIterator Candidates, bool IncludeLocalAccesses = true) const; diff --git a/bolt/include/bolt/Passes/StokeInfo.h b/bolt/include/bolt/Passes/StokeInfo.h index 256009ef9ff50e32ba71f0011c87f01d9e10df8d..75cfa1e7de4355d827e746f440a3e94ebcba4879 100644 --- a/bolt/include/bolt/Passes/StokeInfo.h +++ b/bolt/include/bolt/Passes/StokeInfo.h @@ -9,7 +9,7 @@ // Pass to get information for functions for the Stoke Optimization // To use the Stoke optimization technique to optimize the HHVM. // This Pass solves the two major problems to use the Stoke program without -// proting its code: +// probing its code: // // 1. Stoke works on function level, but it is only limited to relative // small functions which are loop-free, call-free, exception-free, etc. diff --git a/bolt/include/bolt/Passes/TailDuplication.h b/bolt/include/bolt/Passes/TailDuplication.h index 4121c7e28a0284e2d729781a2008e65e32e2a812..b3f1d7b7d9643a4afc93bc03e54cfbe6c78953de 100644 --- a/bolt/include/bolt/Passes/TailDuplication.h +++ b/bolt/include/bolt/Passes/TailDuplication.h @@ -81,7 +81,7 @@ class TailDuplication : public BinaryFunctionPass { /// Returns true if Reg is used by Inst bool regIsUsed(const MCInst &Inst, unsigned Reg, BinaryContext &BC) const; - /// Returns true if Reg is overwritten before its used by StartBB's sucessors + /// Returns true if Reg is overwritten before its used by StartBB's successors bool isOverwrittenBeforeUsed(BinaryBasicBlock &StartBB, unsigned Reg) const; /// Constant and Copy Propagate for the block formed by OriginalBB and diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h index cc237a6e642b847592171afb13fa847f9777f1a6..5bb4d00024c503b81705ee2aeade3648c8b517a3 100644 --- a/bolt/include/bolt/Profile/DataAggregator.h +++ b/bolt/include/bolt/Profile/DataAggregator.h @@ -301,7 +301,7 @@ private: ErrorOr parseAggregatedLBREntry(); /// Parse either buildid:offset or just offset, representing a location in the - /// binary. Used exclusevely for pre-aggregated LBR samples. + /// binary. Used exclusively for pre-aggregated LBR samples. ErrorOr parseLocationOrOffset(); /// Check if a field separator is the next char to parse and, if yes, consume diff --git a/bolt/include/bolt/Profile/DataReader.h b/bolt/include/bolt/Profile/DataReader.h index 916b4f7e218a2ade022bcfed51c6f1fabb20ec5a..314dcc911558633ab8e1c030bf2d9ebef53a2986 100644 --- a/bolt/include/bolt/Profile/DataReader.h +++ b/bolt/include/bolt/Profile/DataReader.h @@ -350,9 +350,9 @@ protected: /// /// The examples records branches from local symbol func (from t2.c), offset /// 11, to global symbol globalfunc, offset 1d, with 1775 branches, no - /// mispreds. Of these branches, 1002 were preceeded by a sequence of + /// mispreds. Of these branches, 1002 were preceded by a sequence of /// branches from func, offset 18 to offset 20 and then from offset 31 to - /// offset d. The rest 773 branches were preceeded by a different sequence + /// offset d. The rest 773 branches were preceded by a different sequence /// of branches, from func, offset 18 to offset 60 and then from offset 71 to /// offset d. std::error_code parse(); diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h index 2a421c5cfaa4f897b5332e1045d5c3a65510e099..495a3f1d7b7b4c48ab70caca1c0a074ed611e2b3 100644 --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -95,6 +95,15 @@ private: /// from meta data in the file. void discoverFileObjects(); + /// 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. + Error discoverRtFiniAddress(); + + /// If DT_FINI_ARRAY is used for instrumentation, update the relocation of its + /// first entry to point to the instrumentation library's fini address. + void updateRtFiniReloc(); + /// Create and initialize metadata rewriters for this instance. void initializeMetadataManager(); @@ -391,12 +400,6 @@ private: /// Manage a pipeline of metadata handlers. class MetadataManager MetadataManager; - /// Get the contents of the LSDA section for this binary. - ArrayRef getLSDAData(); - - /// Get the mapped address of the LSDA section for this binary. - uint64_t getLSDAAddress(); - static const char TimerGroupName[]; static const char TimerGroupDesc[]; @@ -421,6 +424,7 @@ private: /// Common section names. static StringRef getEHFrameSectionName() { return ".eh_frame"; } + static StringRef getRelaDynSectionName() { return ".rela.dyn"; } /// An instance of the input binary we are processing, externally owned. llvm::object::ELFObjectFileBase *InputFile; @@ -540,7 +544,6 @@ private: } /// Exception handling and stack unwinding information in this binary. - ErrorOr LSDASection{std::errc::bad_address}; ErrorOr EHFrameSection{std::errc::bad_address}; /// .note.gnu.build-id section. diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp index 8e2331db3e330325d4711b65e2cf8f02472a1b14..7b78c9ba30a3032c554bc0784f8aafebd78294bc 100644 --- a/bolt/lib/Core/BinaryContext.cpp +++ b/bolt/lib/Core/BinaryContext.cpp @@ -1402,7 +1402,7 @@ void BinaryContext::foldFunction(BinaryFunction &ChildBF, } void BinaryContext::fixBinaryDataHoles() { - assert(validateObjectNesting() && "object nesting inconsitency detected"); + assert(validateObjectNesting() && "object nesting inconsistency detected"); for (BinarySection &Section : allocatableSections()) { std::vector> Holes; @@ -1451,7 +1451,7 @@ void BinaryContext::fixBinaryDataHoles() { } } - assert(validateObjectNesting() && "object nesting inconsitency detected"); + assert(validateObjectNesting() && "object nesting inconsistency detected"); assert(validateHoles() && "top level hole detected in object map"); } @@ -1738,6 +1738,15 @@ bool BinaryContext::shouldEmit(const BinaryFunction &Function) const { return HasRelocations || Function.isSimple(); } +void BinaryContext::dump(const MCInst &Inst) const { + if (LLVM_UNLIKELY(!InstPrinter)) { + dbgs() << "Cannot dump for InstPrinter is not initialized.\n"; + return; + } + InstPrinter->printInst(&Inst, 0, "", *STI, dbgs()); + dbgs() << "\n"; +} + void BinaryContext::printCFI(raw_ostream &OS, const MCCFIInstruction &Inst) { uint32_t Operation = Inst.getOperation(); switch (Operation) { @@ -1796,7 +1805,7 @@ void BinaryContext::printCFI(raw_ostream &OS, const MCCFIInstruction &Inst) { MarkerSymType BinaryContext::getMarkerType(const SymbolRef &Symbol) const { // For aarch64 and riscv, the ABI defines mapping symbols so we identify data // in the code section (see IHI0056B). $x identifies a symbol starting code or - // the end of a data chunk inside code, $d indentifies start of data. + // the end of a data chunk inside code, $d identifies start of data. if ((!isAArch64() && !isRISCV()) || ELFSymbolRef(Symbol).getSize()) return MarkerSymType::NONE; @@ -1862,10 +1871,6 @@ void BinaryContext::printInstruction(raw_ostream &OS, const MCInst &Instruction, bool PrintMCInst, bool PrintMemData, bool PrintRelocations, StringRef Endl) const { - if (MIB->isEHLabel(Instruction)) { - OS << " EH_LABEL: " << *MIB->getTargetSymbol(Instruction) << Endl; - return; - } OS << format(" %08" PRIx64 ": ", Offset); if (MIB->isCFI(Instruction)) { uint32_t Offset = Instruction.getOperand(0).getImm(); @@ -1901,8 +1906,10 @@ void BinaryContext::printInstruction(raw_ostream &OS, const MCInst &Instruction, } if (std::optional Offset = MIB->getOffset(Instruction)) OS << " # Offset: " << *Offset; - if (auto Label = MIB->getLabel(Instruction)) - OS << " # Label: " << **Label; + if (std::optional Size = MIB->getSize(Instruction)) + OS << " # Size: " << *Size; + if (MCSymbol *Label = MIB->getLabel(Instruction)) + OS << " # Label: " << *Label; MIB->printAnnotations(Instruction, OS); @@ -1928,10 +1935,27 @@ BinaryContext::getBaseAddressForMapping(uint64_t MMapAddress, // Find a segment with a matching file offset. for (auto &KV : SegmentMapInfo) { const SegmentInfo &SegInfo = KV.second; - if (alignDown(SegInfo.FileOffset, SegInfo.Alignment) == FileOffset) { - // Use segment's aligned memory offset to calculate the base address. - const uint64_t MemOffset = alignDown(SegInfo.Address, SegInfo.Alignment); - return MMapAddress - MemOffset; + // FileOffset is got from perf event, + // and it is equal to alignDown(SegInfo.FileOffset, pagesize). + // If the pagesize is not equal to SegInfo.Alignment. + // FileOffset and SegInfo.FileOffset should be aligned first, + // and then judge whether they are equal. + if (alignDown(SegInfo.FileOffset, SegInfo.Alignment) == + alignDown(FileOffset, SegInfo.Alignment)) { + // The function's offset from base address in VAS is aligned by pagesize + // instead of SegInfo.Alignment. Pagesize can't be got from perf events. + // However, The ELF document says that SegInfo.FileOffset should equal + // to SegInfo.Address, modulo the pagesize. + // Reference: https://refspecs.linuxfoundation.org/elf/elf.pdf + + // So alignDown(SegInfo.Address, pagesize) can be calculated by: + // alignDown(SegInfo.Address, pagesize) + // = SegInfo.Address - (SegInfo.Address % pagesize) + // = SegInfo.Address - (SegInfo.FileOffset % pagesize) + // = SegInfo.Address - SegInfo.FileOffset + + // alignDown(SegInfo.FileOffset, pagesize) + // = SegInfo.Address - SegInfo.FileOffset + FileOffset + return MMapAddress - (SegInfo.Address - SegInfo.FileOffset + FileOffset); } } @@ -2307,14 +2331,36 @@ BinaryContext::calculateEmittedSize(BinaryFunction &BF, bool FixBranches) { MCAsmLayout Layout(Assembler); Assembler.layout(Layout); + // Obtain fragment sizes. + std::vector FragmentSizes; + // Main fragment size. const uint64_t HotSize = Layout.getSymbolOffset(*EndLabel) - Layout.getSymbolOffset(*StartLabel); - const uint64_t ColdSize = - std::accumulate(SplitLabels.begin(), SplitLabels.end(), 0ULL, - [&](const uint64_t Accu, const LabelRange &Labels) { - return Accu + Layout.getSymbolOffset(*Labels.second) - - Layout.getSymbolOffset(*Labels.first); - }); + FragmentSizes.push_back(HotSize); + // Split fragment sizes. + uint64_t ColdSize = 0; + for (const auto &Labels : SplitLabels) { + uint64_t Size = Layout.getSymbolOffset(*Labels.second) - + Layout.getSymbolOffset(*Labels.first); + FragmentSizes.push_back(Size); + ColdSize += Size; + } + + // Populate new start and end offsets of each basic block. + uint64_t FragmentIndex = 0; + for (FunctionFragment &FF : BF.getLayout().fragments()) { + BinaryBasicBlock *PrevBB = nullptr; + for (BinaryBasicBlock *BB : FF) { + const uint64_t BBStartOffset = Layout.getSymbolOffset(*(BB->getLabel())); + BB->setOutputStartAddress(BBStartOffset); + if (PrevBB) + PrevBB->setOutputEndAddress(BBStartOffset); + PrevBB = BB; + } + if (PrevBB) + PrevBB->setOutputEndAddress(FragmentSizes[FragmentIndex]); + FragmentIndex++; + } // Clean-up the effect of the code emission. for (const MCSymbol &Symbol : Assembler.symbols()) { diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp index de80a99a74ed0807bc4c641421a5f78746209b4d..fb1bf530c1974aa2069487c4d0d34b87548b3fb2 100644 --- a/bolt/lib/Core/BinaryEmitter.cpp +++ b/bolt/lib/Core/BinaryEmitter.cpp @@ -161,9 +161,17 @@ private: /// \p FirstInstr indicates if \p NewLoc represents the first instruction /// in a sequence, such as a function fragment. /// + /// If \p NewLoc location matches \p PrevLoc, no new line number entry will be + /// created and the function will return \p PrevLoc while \p InstrLabel will + /// be ignored. Otherwise, the caller should use \p InstrLabel to mark the + /// corresponding instruction by emitting \p InstrLabel before it. + /// If \p InstrLabel is set by the caller, its value will be used with \p + /// \p NewLoc. If it was nullptr on entry, it will be populated with a pointer + /// to a new temp symbol used with \p NewLoc. + /// /// Return new current location which is either \p NewLoc or \p PrevLoc. SMLoc emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc, SMLoc PrevLoc, - bool FirstInstr); + bool FirstInstr, MCSymbol *&InstrLabel); /// Use \p FunctionEndSymbol to mark the end of the line info sequence. /// Note that it does not automatically result in the insertion of the EOS @@ -461,13 +469,6 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, continue; // Handle pseudo instructions. - if (BC.MIB->isEHLabel(Instr)) { - const MCSymbol *Label = BC.MIB->getTargetSymbol(Instr); - assert(Instr.getNumOperands() >= 1 && Label && - "bad EH_LABEL instruction"); - Streamer.emitLabel(const_cast(Label)); - continue; - } if (BC.MIB->isCFI(Instr)) { emitCFIInstruction(*BF.getCFIFor(Instr)); continue; @@ -483,23 +484,40 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, // are relaxable, we should be safe. } - if (!EmitCodeOnly && opts::UpdateDebugSections && BF.getDWARFUnit()) { - LastLocSeen = emitLineInfo(BF, Instr.getLoc(), LastLocSeen, FirstInstr); - FirstInstr = false; - } + if (!EmitCodeOnly) { + // A symbol to be emitted before the instruction to mark its location. + MCSymbol *InstrLabel = BC.MIB->getLabel(Instr); + + if (opts::UpdateDebugSections && BF.getDWARFUnit()) { + LastLocSeen = emitLineInfo(BF, Instr.getLoc(), LastLocSeen, + FirstInstr, InstrLabel); + FirstInstr = false; + } + + // Prepare to tag this location with a label if we need to keep track of + // the location of calls/returns for BOLT address translation maps + if (BF.requiresAddressTranslation() && BC.MIB->getOffset(Instr)) { + const uint32_t Offset = *BC.MIB->getOffset(Instr); + if (!InstrLabel) + InstrLabel = BC.Ctx->createTempSymbol(); + BB->getLocSyms().emplace_back(Offset, InstrLabel); + } - // Prepare to tag this location with a label if we need to keep track of - // the location of calls/returns for BOLT address translation maps - if (!EmitCodeOnly && BF.requiresAddressTranslation() && - BC.MIB->getOffset(Instr)) { - const uint32_t Offset = *BC.MIB->getOffset(Instr); - MCSymbol *LocSym = BC.Ctx->createTempSymbol(); - Streamer.emitLabel(LocSym); - BB->getLocSyms().emplace_back(Offset, LocSym); + if (InstrLabel) + Streamer.emitLabel(InstrLabel); } - if (auto Label = BC.MIB->getLabel(Instr)) - Streamer.emitLabel(*Label); + // Emit sized NOPs via MCAsmBackend::writeNopData() interface on x86. + // This is a workaround for invalid NOPs handling by asm/disasm layer. + if (BC.MIB->isNoop(Instr) && BC.isX86()) { + if (std::optional Size = BC.MIB->getSize(Instr)) { + SmallString<15> Code; + raw_svector_ostream VecOS(Code); + BC.MAB->writeNopData(VecOS, *Size, BC.STI.get()); + Streamer.emitBytes(Code); + continue; + } + } Streamer.emitInstruction(Instr, *BC.STI); LastIsPrefix = BC.MIB->isPrefix(Instr); @@ -661,7 +679,8 @@ void BinaryEmitter::emitConstantIslands(BinaryFunction &BF, bool EmitColdPart, } SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc, - SMLoc PrevLoc, bool FirstInstr) { + SMLoc PrevLoc, bool FirstInstr, + MCSymbol *&InstrLabel) { DWARFUnit *FunctionCU = BF.getDWARFUnit(); const DWARFDebugLine::LineTable *FunctionLineTable = BF.getDWARFLineTable(); assert(FunctionCU && "cannot emit line info for function without CU"); @@ -711,12 +730,12 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc, const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc(); BC.Ctx->clearDwarfLocSeen(); - MCSymbol *LineSym = BC.Ctx->createTempSymbol(); - Streamer.emitLabel(LineSym); + if (!InstrLabel) + InstrLabel = BC.Ctx->createTempSymbol(); BC.getDwarfLineTable(FunctionUnitIndex) .getMCLineSections() - .addLineEntry(MCDwarfLineEntry(LineSym, DwarfLoc), + .addLineEntry(MCDwarfLineEntry(InstrLabel, DwarfLoc), Streamer.getCurrentSectionOnly()); return NewLoc; diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index 2e1fa739e53f20b54b8b59483c5ceb48a39efd71..3b0a2314dc1d25249c7e3d4e2b05e8270a608116 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -40,6 +40,7 @@ #include "llvm/Support/Regex.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/xxhash.h" #include #include #include @@ -54,7 +55,6 @@ namespace opts { extern cl::OptionCategory BoltCategory; extern cl::OptionCategory BoltOptCategory; -extern cl::OptionCategory BoltRelocCategory; extern cl::opt EnableBAT; extern cl::opt Instrument; @@ -109,6 +109,13 @@ cl::opt cl::desc("try to preserve basic block alignment"), cl::cat(BoltOptCategory)); +static cl::opt PrintOutputAddressRange( + "print-output-address-range", + cl::desc( + "print output address range for each basic block in the function when" + "BinaryFunction::print is called"), + cl::Hidden, cl::cat(BoltOptCategory)); + cl::opt PrintDynoStats("dyno-stats", cl::desc("print execution info based on profile"), @@ -322,7 +329,8 @@ void BinaryFunction::markUnreachableBlocks() { // Any unnecessary fallthrough jumps revealed after calling eraseInvalidBBs // will be cleaned up by fixBranches(). -std::pair BinaryFunction::eraseInvalidBBs() { +std::pair +BinaryFunction::eraseInvalidBBs(const MCCodeEmitter *Emitter) { DenseSet InvalidBBs; unsigned Count = 0; uint64_t Bytes = 0; @@ -331,7 +339,7 @@ std::pair BinaryFunction::eraseInvalidBBs() { assert(!isEntryPoint(*BB) && "all entry blocks must be valid"); InvalidBBs.insert(BB); ++Count; - Bytes += BC.computeCodeSize(BB->begin(), BB->end()); + Bytes += BC.computeCodeSize(BB->begin(), BB->end(), Emitter); } } @@ -430,8 +438,6 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation) { OS << "\n IsSplit : " << isSplit(); OS << "\n BB Count : " << size(); - if (HasFixedIndirectBranch) - OS << "\n HasFixedIndirectBranch : true"; if (HasUnknownControlFlow) OS << "\n Unknown CF : true"; if (getPersonalityFunction()) @@ -512,6 +518,11 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation) { OS << BB->getName() << " (" << BB->size() << " instructions, align : " << BB->getAlignment() << ")\n"; + if (opts::PrintOutputAddressRange) + OS << formatv(" Output Address Range: [{0:x}, {1:x}) ({2} bytes)\n", + BB->getOutputAddressRange().first, + BB->getOutputAddressRange().second, BB->getOutputSize()); + if (isEntryPoint(*BB)) { if (MCSymbol *EntrySymbol = getSecondaryEntryPointSymbol(*BB)) OS << " Secondary Entry Point: " << EntrySymbol->getName() << '\n'; @@ -533,7 +544,7 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation) { if (BB->getCFIState() >= 0) OS << " CFI State : " << BB->getCFIState() << '\n'; if (opts::EnableBAT) { - OS << " Input offset: " << Twine::utohexstr(BB->getInputOffset()) + OS << " Input offset: 0x" << Twine::utohexstr(BB->getInputOffset()) << "\n"; } if (!BB->pred_empty()) { @@ -1116,7 +1127,7 @@ void BinaryFunction::handleIndirectBranch(MCInst &Instruction, uint64_t Size, Instruction.clear(); MIB->createUncondBranch(Instruction, TargetSymbol, BC.Ctx.get()); TakenBranches.emplace_back(Offset, IndirectTarget - getAddress()); - HasFixedIndirectBranch = true; + addEntryPointAtOffset(IndirectTarget - getAddress()); } else { MIB->convertJmpToTailCall(Instruction); BC.addInterproceduralReference(this, IndirectTarget); @@ -1375,11 +1386,11 @@ add_instruction: if (BC.keepOffsetForInstruction(Instruction)) MIB->setOffset(Instruction, static_cast(Offset)); - if (BC.MIB->isNoop(Instruction)) { - // NOTE: disassembly loses the correct size information for noops. + if (BC.isX86() && BC.MIB->isNoop(Instruction)) { + // NOTE: disassembly loses the correct size information for noops on x86. // E.g. nopw 0x0(%rax,%rax,1) is 9 bytes, but re-encoded it's only // 5 bytes. Preserve the size info using annotations. - MIB->addAnnotation(Instruction, "Size", static_cast(Size)); + MIB->setSize(Instruction, Size); } addInstruction(Offset, std::move(Instruction)); @@ -1892,9 +1903,6 @@ bool BinaryFunction::postProcessIndirectBranches( LastIndirectJumpBB->updateJumpTableSuccessors(); } - if (HasFixedIndirectBranch) - return false; - // Validate that all data references to function offsets are claimed by // recognized jump tables. Register externally referenced blocks as entry // points. @@ -1999,7 +2007,7 @@ bool BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) { } } if (LastNonNop && !MIB->getOffset(*LastNonNop)) - MIB->setOffset(*LastNonNop, static_cast(Offset), AllocatorId); + MIB->setOffset(*LastNonNop, static_cast(Offset)); }; for (auto I = Instructions.begin(), E = Instructions.end(); I != E; ++I) { @@ -2022,7 +2030,7 @@ bool BinaryFunction::buildCFG(MCPlusBuilder::AllocatorIdTy AllocatorId) { if (MIB->isNoop(Instr) && !MIB->getOffset(Instr)) { // If "Offset" annotation is not present, set it and mark the nop for // deletion. - MIB->setOffset(Instr, static_cast(Offset), AllocatorId); + MIB->setOffset(Instr, static_cast(Offset)); // Annotate ordinary nops, so we can safely delete them if required. MIB->addAnnotation(Instr, "NOP", static_cast(1), AllocatorId); } @@ -2303,6 +2311,13 @@ void BinaryFunction::removeConditionalTailCalls() { assert(CTCTargetLabel && "symbol expected for conditional tail call"); MCInst TailCallInstr; BC.MIB->createTailCall(TailCallInstr, CTCTargetLabel, BC.Ctx.get()); + + // Move offset from CTCInstr to TailCallInstr. + if (const std::optional Offset = BC.MIB->getOffset(*CTCInstr)) { + BC.MIB->setOffset(TailCallInstr, *Offset); + BC.MIB->clearOffset(*CTCInstr); + } + // Link new BBs to the original input offset of the BB where the CTC // is, so we can map samples recorded in new BBs back to the original BB // seem in the input binary (if using BAT) @@ -2331,12 +2346,6 @@ void BinaryFunction::removeConditionalTailCalls() { // This branch is no longer a conditional tail call. BC.MIB->unsetConditionalTailCall(*CTCInstr); - - // Move offset from CTCInstr to TailCallInstr. - if (std::optional Offset = BC.MIB->getOffset(*CTCInstr)) { - BC.MIB->setOffset(TailCallInstr, *Offset); - BC.MIB->clearOffset(*CTCInstr); - } } insertBasicBlocks(std::prev(end()), std::move(NewBlocks), @@ -3373,7 +3382,7 @@ void BinaryFunction::propagateGnuArgsSizeInfo( } } else if (BC.MIB->isInvoke(Instr)) { // Add the value of GNU_args_size as an extra operand to invokes. - BC.MIB->addGnuArgsSize(Instr, CurrentGnuArgsSize, AllocId); + BC.MIB->addGnuArgsSize(Instr, CurrentGnuArgsSize); } ++II; } @@ -3628,7 +3637,7 @@ size_t BinaryFunction::computeHash(bool UseDFS, for (const BinaryBasicBlock *BB : Order) HashString.append(hashBlock(BC, *BB, OperandHashFunc)); - return Hash = std::hash{}(HashString); + return Hash = llvm::xxh3_64bits(HashString); } void BinaryFunction::insertBasicBlocks( @@ -4135,10 +4144,6 @@ void BinaryFunction::updateOutputValues(const BOLTLinker &Linker) { if (!requiresAddressMap()) return; - // Output ranges should match the input if the body hasn't changed. - if (!isSimple() && !BC.HasRelocations) - return; - // AArch64 may have functions that only contains a constant island (no code). if (getLayout().block_empty()) return; @@ -4175,7 +4180,7 @@ void BinaryFunction::updateOutputValues(const BOLTLinker &Linker) { assert(PrevBB->getOutputAddressRange().first <= BBAddress && "Bad output address for basic block."); assert((PrevBB->getOutputAddressRange().first != BBAddress || - !hasInstructions() || PrevBB->empty()) && + !hasInstructions() || !PrevBB->getNumNonPseudos()) && "Bad output address for basic block."); PrevBB->setOutputEndAddress(BBAddress); } @@ -4186,6 +4191,12 @@ void BinaryFunction::updateOutputValues(const BOLTLinker &Linker) { ? FF.getAddress() + FF.getImageSize() : getOutputAddress() + getOutputSize()); } + + // Reset output addresses for deleted blocks. + for (BinaryBasicBlock *BB : DeletedBasicBlocks) { + BB->setOutputStartAddress(0); + BB->setOutputEndAddress(0); + } } DebugAddressRangesVector BinaryFunction::getOutputAddressRanges() const { @@ -4251,92 +4262,88 @@ uint64_t BinaryFunction::translateInputToOutputAddress(uint64_t Address) const { BB->getOutputAddressRange().second); } -DebugAddressRangesVector BinaryFunction::translateInputToOutputRanges( - const DWARFAddressRangesVector &InputRanges) const { - DebugAddressRangesVector OutputRanges; +DebugAddressRangesVector +BinaryFunction::translateInputToOutputRange(DebugAddressRange InRange) const { + DebugAddressRangesVector OutRanges; + // The function was removed from the output. Return an empty range. if (isFolded()) - return OutputRanges; + return OutRanges; - // If the function hasn't changed return the same ranges. + // If the function hasn't changed return the same range. if (!isEmitted()) { - OutputRanges.resize(InputRanges.size()); - llvm::transform(InputRanges, OutputRanges.begin(), - [](const DWARFAddressRange &Range) { - return DebugAddressRange(Range.LowPC, Range.HighPC); - }); - return OutputRanges; + OutRanges.emplace_back(InRange); + return OutRanges; + } + + if (!containsAddress(InRange.LowPC)) + return OutRanges; + + // Special case of an empty range [X, X). Some tools expect X to be updated. + if (InRange.LowPC == InRange.HighPC) { + if (uint64_t NewPC = translateInputToOutputAddress(InRange.LowPC)) + OutRanges.push_back(DebugAddressRange{NewPC, NewPC}); + return OutRanges; } - // Even though we will merge ranges in a post-processing pass, we attempt to - // merge them in a main processing loop as it improves the processing time. - uint64_t PrevEndAddress = 0; - for (const DWARFAddressRange &Range : InputRanges) { - if (!containsAddress(Range.LowPC)) { + uint64_t InputOffset = InRange.LowPC - getAddress(); + const uint64_t InputEndOffset = + std::min(InRange.HighPC - getAddress(), getSize()); + + auto BBI = llvm::upper_bound(BasicBlockOffsets, + BasicBlockOffset(InputOffset, nullptr), + CompareBasicBlockOffsets()); + assert(BBI != BasicBlockOffsets.begin()); + + // Iterate over blocks in the input order using BasicBlockOffsets. + for (--BBI; InputOffset < InputEndOffset && BBI != BasicBlockOffsets.end(); + InputOffset = BBI->second->getEndOffset(), ++BBI) { + const BinaryBasicBlock &BB = *BBI->second; + if (InputOffset < BB.getOffset() || InputOffset >= BB.getEndOffset()) { LLVM_DEBUG( dbgs() << "BOLT-DEBUG: invalid debug address range detected for " - << *this << " : [0x" << Twine::utohexstr(Range.LowPC) << ", 0x" - << Twine::utohexstr(Range.HighPC) << "]\n"); - PrevEndAddress = 0; - continue; + << *this << " : [0x" << Twine::utohexstr(InRange.LowPC) + << ", 0x" << Twine::utohexstr(InRange.HighPC) << "]\n"); + break; } - uint64_t InputOffset = Range.LowPC - getAddress(); - const uint64_t InputEndOffset = - std::min(Range.HighPC - getAddress(), getSize()); - - auto BBI = llvm::upper_bound(BasicBlockOffsets, - BasicBlockOffset(InputOffset, nullptr), - CompareBasicBlockOffsets()); - --BBI; - do { - const BinaryBasicBlock *BB = BBI->second; - if (InputOffset < BB->getOffset() || InputOffset >= BB->getEndOffset()) { - LLVM_DEBUG( - dbgs() << "BOLT-DEBUG: invalid debug address range detected for " - << *this << " : [0x" << Twine::utohexstr(Range.LowPC) - << ", 0x" << Twine::utohexstr(Range.HighPC) << "]\n"); - PrevEndAddress = 0; - break; - } - // Skip the range if the block was deleted. - if (const uint64_t OutputStart = BB->getOutputAddressRange().first) { - const uint64_t StartAddress = - OutputStart + InputOffset - BB->getOffset(); - uint64_t EndAddress = BB->getOutputAddressRange().second; - if (InputEndOffset < BB->getEndOffset()) - EndAddress = StartAddress + InputEndOffset - InputOffset; - - if (StartAddress == PrevEndAddress) { - OutputRanges.back().HighPC = - std::max(OutputRanges.back().HighPC, EndAddress); - } else { - OutputRanges.emplace_back(StartAddress, - std::max(StartAddress, EndAddress)); - } - PrevEndAddress = OutputRanges.back().HighPC; - } + // Skip the block if it wasn't emitted. + if (!BB.getOutputAddressRange().first) + continue; - InputOffset = BB->getEndOffset(); - ++BBI; - } while (InputOffset < InputEndOffset); - } + // Find output address for an instruction with an offset greater or equal + // to /p Offset. The output address should fall within the same basic + // block boundaries. + auto translateBlockOffset = [&](const uint64_t Offset) { + const uint64_t OutAddress = BB.getOutputAddressRange().first + Offset; + return std::min(OutAddress, BB.getOutputAddressRange().second); + }; - // Post-processing pass to sort and merge ranges. - llvm::sort(OutputRanges); - DebugAddressRangesVector MergedRanges; - PrevEndAddress = 0; - for (const DebugAddressRange &Range : OutputRanges) { - if (Range.LowPC <= PrevEndAddress) { - MergedRanges.back().HighPC = - std::max(MergedRanges.back().HighPC, Range.HighPC); - } else { - MergedRanges.emplace_back(Range.LowPC, Range.HighPC); + uint64_t OutLowPC = BB.getOutputAddressRange().first; + if (InputOffset > BB.getOffset()) + OutLowPC = translateBlockOffset(InputOffset - BB.getOffset()); + + uint64_t OutHighPC = BB.getOutputAddressRange().second; + if (InputEndOffset < BB.getEndOffset()) { + assert(InputEndOffset >= BB.getOffset()); + OutHighPC = translateBlockOffset(InputEndOffset - BB.getOffset()); } - PrevEndAddress = MergedRanges.back().HighPC; + + // Check if we can expand the last translated range. + if (!OutRanges.empty() && OutRanges.back().HighPC == OutLowPC) + OutRanges.back().HighPC = std::max(OutRanges.back().HighPC, OutHighPC); + else + OutRanges.emplace_back(OutLowPC, std::max(OutLowPC, OutHighPC)); } - return MergedRanges; + LLVM_DEBUG({ + dbgs() << "BOLT-DEBUG: translated address range " << InRange << " -> "; + for (const DebugAddressRange &R : OutRanges) + dbgs() << R << ' '; + dbgs() << '\n'; + }); + + return OutRanges; } MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) { @@ -4355,10 +4362,11 @@ MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) { } if (MCInst *LastInstr = BB->getLastNonPseudoInstr()) { - const uint32_t Size = - BC.MIB->getAnnotationWithDefault(*LastInstr, "Size"); - if (BB->getEndOffset() - Offset == Size) - return LastInstr; + if (std::optional Size = BC.MIB->getSize(*LastInstr)) { + if (BB->getEndOffset() - Offset == Size) { + return LastInstr; + } + } } return nullptr; @@ -4367,92 +4375,6 @@ MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) { } } -DebugLocationsVector BinaryFunction::translateInputToOutputLocationList( - const DebugLocationsVector &InputLL) const { - DebugLocationsVector OutputLL; - - if (isFolded()) - return OutputLL; - - // If the function hasn't changed - there's nothing to update. - if (!isEmitted()) - return InputLL; - - uint64_t PrevEndAddress = 0; - SmallVectorImpl *PrevExpr = nullptr; - for (const DebugLocationEntry &Entry : InputLL) { - const uint64_t Start = Entry.LowPC; - const uint64_t End = Entry.HighPC; - if (!containsAddress(Start)) { - LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invalid debug address range detected " - "for " - << *this << " : [0x" << Twine::utohexstr(Start) - << ", 0x" << Twine::utohexstr(End) << "]\n"); - continue; - } - uint64_t InputOffset = Start - getAddress(); - const uint64_t InputEndOffset = std::min(End - getAddress(), getSize()); - auto BBI = llvm::upper_bound(BasicBlockOffsets, - BasicBlockOffset(InputOffset, nullptr), - CompareBasicBlockOffsets()); - --BBI; - do { - const BinaryBasicBlock *BB = BBI->second; - if (InputOffset < BB->getOffset() || InputOffset >= BB->getEndOffset()) { - LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invalid debug address range detected " - "for " - << *this << " : [0x" << Twine::utohexstr(Start) - << ", 0x" << Twine::utohexstr(End) << "]\n"); - PrevEndAddress = 0; - break; - } - - // Skip the range if the block was deleted. - if (const uint64_t OutputStart = BB->getOutputAddressRange().first) { - const uint64_t StartAddress = - OutputStart + InputOffset - BB->getOffset(); - uint64_t EndAddress = BB->getOutputAddressRange().second; - if (InputEndOffset < BB->getEndOffset()) - EndAddress = StartAddress + InputEndOffset - InputOffset; - - if (StartAddress == PrevEndAddress && Entry.Expr == *PrevExpr) { - OutputLL.back().HighPC = std::max(OutputLL.back().HighPC, EndAddress); - } else { - OutputLL.emplace_back(DebugLocationEntry{ - StartAddress, std::max(StartAddress, EndAddress), Entry.Expr}); - } - PrevEndAddress = OutputLL.back().HighPC; - PrevExpr = &OutputLL.back().Expr; - } - - ++BBI; - InputOffset = BB->getEndOffset(); - } while (InputOffset < InputEndOffset); - } - - // Sort and merge adjacent entries with identical location. - llvm::stable_sort( - OutputLL, [](const DebugLocationEntry &A, const DebugLocationEntry &B) { - return A.LowPC < B.LowPC; - }); - DebugLocationsVector MergedLL; - PrevEndAddress = 0; - PrevExpr = nullptr; - for (const DebugLocationEntry &Entry : OutputLL) { - if (Entry.LowPC <= PrevEndAddress && *PrevExpr == Entry.Expr) { - MergedLL.back().HighPC = std::max(Entry.HighPC, MergedLL.back().HighPC); - } else { - const uint64_t Begin = std::max(Entry.LowPC, PrevEndAddress); - const uint64_t End = std::max(Begin, Entry.HighPC); - MergedLL.emplace_back(DebugLocationEntry{Begin, End, Entry.Expr}); - } - PrevEndAddress = MergedLL.back().HighPC; - PrevExpr = &MergedLL.back().Expr; - } - - return MergedLL; -} - void BinaryFunction::printLoopInfo(raw_ostream &OS) const { if (!opts::shouldPrint(*this)) return; diff --git a/bolt/lib/Core/BinarySection.cpp b/bolt/lib/Core/BinarySection.cpp index 6742302a98e6771a1eab8678e722eda8d8c076b9..a8fc4f07bd3e8f96e926593d41a6270612840c2d 100644 --- a/bolt/lib/Core/BinarySection.cpp +++ b/bolt/lib/Core/BinarySection.cpp @@ -227,7 +227,7 @@ void BinarySection::print(raw_ostream &OS) const { BinarySection::RelocationSetType BinarySection::reorderRelocations(bool Inplace) const { assert(PendingRelocations.empty() && - "reodering pending relocations not supported"); + "reordering pending relocations not supported"); RelocationSetType NewRelocations; for (const Relocation &Rel : relocations()) { uint64_t RelAddr = Rel.Offset + getAddress(); diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp index 223ae714440d97da660f7561dc98a3646fffad66..b809b2935ee9ebf04c0e631f96f14807febdefa2 100644 --- a/bolt/lib/Core/DIEBuilder.cpp +++ b/bolt/lib/Core/DIEBuilder.cpp @@ -193,12 +193,6 @@ void DIEBuilder::buildTypeUnits(const bool Init) { if (Init) BuilderState.reset(new State()); - unsigned int CUNum = getCUNum(DwarfContext, IsDWO); - getState().CloneUnitCtxMap.resize(CUNum); - DWARFContext::unit_iterator_range CU4TURanges = - IsDWO ? DwarfContext->dwo_types_section_units() - : DwarfContext->types_section_units(); - const DWARFUnitIndex &TUIndex = DwarfContext->getTUIndex(); if (!TUIndex.getRows().empty()) { for (auto &Row : TUIndex.getRows()) { @@ -208,6 +202,11 @@ void DIEBuilder::buildTypeUnits(const bool Init) { true); } } + const unsigned int CUNum = getCUNum(DwarfContext, IsDWO); + getState().CloneUnitCtxMap.resize(CUNum); + DWARFContext::unit_iterator_range CU4TURanges = + IsDWO ? DwarfContext->dwo_types_section_units() + : DwarfContext->types_section_units(); getState().Type = ProcessingType::DWARF4TUs; for (std::unique_ptr &DU : CU4TURanges) @@ -278,11 +277,13 @@ void DIEBuilder::buildCompileUnits(const std::vector &CUs) { constructFromUnit(*DU); } -void DIEBuilder::buildBoth() { +void DIEBuilder::buildDWOUnit(DWARFUnit &U) { BuilderState.release(); BuilderState = std::make_unique(); buildTypeUnits(false); - buildCompileUnits(false); + getState().Type = ProcessingType::CUs; + registerUnit(U, false); + constructFromUnit(U); } DIE *DIEBuilder::constructDIEFast(DWARFDie &DDie, DWARFUnit &U, diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp index 7a532fbbb5c2e95583a342cecc8474dd088a381b..9061d4f0e197aec1a69fbdc6461be9ef5b0149a7 100644 --- a/bolt/lib/Core/DebugData.cpp +++ b/bolt/lib/Core/DebugData.cpp @@ -188,7 +188,7 @@ uint64_t DebugRangeListsSectionWriter::addRanges( } struct LocListsRangelistsHeader { - UnitLengthType UnitLength; // Size of loclist entris section, not including + UnitLengthType UnitLength; // Size of loclist entries section, not including // size of header. VersionType Version; AddressSizeType AddressSize; @@ -449,8 +449,8 @@ static void updateAddressBase(DIEBuilder &DIEBlder, DebugAddrWriter &AddrWriter, } void DebugAddrWriter::update(DIEBuilder &DIEBlder, DWARFUnit &CU) { - // Handling the case wehre debug information is a mix of Debug fission and - // monolitic. + // Handling the case where debug information is a mix of Debug fission and + // monolithic. if (!CU.getDWOId()) return; const uint64_t CUID = getCUID(CU); diff --git a/bolt/lib/Core/DynoStats.cpp b/bolt/lib/Core/DynoStats.cpp index 5dd55e13e5b31fdea55eb3071d8e62f8c5368b54..10f076915e92d8b296e9267ac5697b2658b10ca7 100644 --- a/bolt/lib/Core/DynoStats.cpp +++ b/bolt/lib/Core/DynoStats.cpp @@ -136,7 +136,7 @@ void DynoStats::operator+=(const DynoStats &Other) { if (I == OpcodeHistogram.end()) { OpcodeHistogram.emplace(Stat); } else { - // Merge Other Historgrams, log only the opts::PrintDynoOpcodeStat'th + // Merge other histograms, log only the opts::PrintDynoOpcodeStat'th // maximum counts. I->second.first += Stat.second.first; auto &MMap = I->second.second; diff --git a/bolt/lib/Core/Exceptions.cpp b/bolt/lib/Core/Exceptions.cpp index 667f1757e13d7101859025ec9b67bd25a865b099..993f3a7770aa8178eb4d25d8e0beac55529dda20 100644 --- a/bolt/lib/Core/Exceptions.cpp +++ b/bolt/lib/Core/Exceptions.cpp @@ -112,13 +112,18 @@ void BinaryFunction::parseLSDA(ArrayRef LSDASectionData, uint64_t Offset = getLSDAAddress() - LSDASectionAddress; assert(Data.isValidOffset(Offset) && "wrong LSDA address"); - uint8_t LPStartEncoding = Data.getU8(&Offset); - uint64_t LPStart = 0; - // Convert to offset if LPStartEncoding is typed absptr DW_EH_PE_absptr - if (std::optional MaybeLPStart = Data.getEncodedPointer( - &Offset, LPStartEncoding, Offset + LSDASectionAddress)) - LPStart = (LPStartEncoding && 0xFF == 0) ? *MaybeLPStart - : *MaybeLPStart - Address; + const uint8_t LPStartEncoding = Data.getU8(&Offset); + uint64_t LPStart = Address; + if (LPStartEncoding != dwarf::DW_EH_PE_omit) { + std::optional MaybeLPStart = Data.getEncodedPointer( + &Offset, LPStartEncoding, Offset + LSDASectionAddress); + if (!MaybeLPStart) { + errs() << "BOLT-ERROR: unsupported LPStartEncoding: " + << (unsigned)LPStartEncoding << '\n'; + exit(1); + } + LPStart = *MaybeLPStart; + } const uint8_t TTypeEncoding = Data.getU8(&Offset); LSDATypeEncoding = TTypeEncoding; @@ -175,30 +180,13 @@ void BinaryFunction::parseLSDA(ArrayRef LSDASectionData, uint64_t LandingPad = *Data.getEncodedPointer( &CallSitePtr, CallSiteEncoding, CallSitePtr + LSDASectionAddress); uint64_t ActionEntry = Data.getULEB128(&CallSitePtr); - - uint64_t LPOffset = LPStart + LandingPad; - uint64_t LPAddress = Address + LPOffset; - - // Verify if landing pad code is located outside current function - // Support landing pad to builtin_unreachable - if (LPAddress < Address || LPAddress > Address + getSize()) { - BinaryFunction *Fragment = - BC.getBinaryFunctionContainingAddress(LPAddress); - assert(Fragment != nullptr && - "BOLT-ERROR: cannot find landing pad fragment"); - BC.addInterproceduralReference(this, Fragment->getAddress()); - BC.processInterproceduralReferences(); - assert(isParentOrChildOf(*Fragment) && - "BOLT-ERROR: cannot have landing pads in different functions"); - setHasIndirectTargetToSplitFragment(true); - BC.addFragmentsToSkip(this); - return; - } + if (LandingPad) + LandingPad += LPStart; if (opts::PrintExceptions) { outs() << "Call Site: [0x" << Twine::utohexstr(RangeBase + Start) << ", 0x" << Twine::utohexstr(RangeBase + Start + Length) - << "); landing pad: 0x" << Twine::utohexstr(LPOffset) + << "); landing pad: 0x" << Twine::utohexstr(LandingPad) << "; action entry: 0x" << Twine::utohexstr(ActionEntry) << "\n"; outs() << " current offset is " << (CallSitePtr - CallSiteTableStart) << '\n'; @@ -206,7 +194,24 @@ void BinaryFunction::parseLSDA(ArrayRef LSDASectionData, // Create a handler entry if necessary. MCSymbol *LPSymbol = nullptr; - if (LPOffset) { + if (LandingPad) { + // Verify if landing pad code is located outside current function + // Support landing pad to builtin_unreachable + if (LandingPad < Address || LandingPad > Address + getSize()) { + BinaryFunction *Fragment = + BC.getBinaryFunctionContainingAddress(LandingPad); + assert(Fragment != nullptr && + "BOLT-ERROR: cannot find landing pad fragment"); + BC.addInterproceduralReference(this, Fragment->getAddress()); + BC.processInterproceduralReferences(); + assert(isParentOrChildOf(*Fragment) && + "BOLT-ERROR: cannot have landing pads in different functions"); + setHasIndirectTargetToSplitFragment(true); + BC.addFragmentsToSkip(this); + return; + } + + const uint64_t LPOffset = LandingPad - getAddress(); if (!getInstructionAtOffset(LPOffset)) { if (opts::Verbosity >= 1) errs() << "BOLT-WARNING: landing pad " << Twine::utohexstr(LPOffset) @@ -373,12 +378,12 @@ void BinaryFunction::updateEHRanges() { const MCSymbol *StartRange = nullptr; for (BinaryBasicBlock *const BB : FF) { - for (auto II = BB->begin(); II != BB->end(); ++II) { - if (!BC.MIB->isCall(*II)) + for (MCInst &Instr : *BB) { + if (!BC.MIB->isCall(Instr)) continue; // Instruction can throw an exception that should be handled. - const bool Throws = BC.MIB->isInvoke(*II); + const bool Throws = BC.MIB->isInvoke(Instr); // Ignore the call if it's a continuation of a no-throw gap. if (!Throws && !StartRange) @@ -388,7 +393,7 @@ void BinaryFunction::updateEHRanges() { const MCSymbol *LP = nullptr; uint64_t Action = 0; if (const std::optional EHInfo = - BC.MIB->getEHInfo(*II)) + BC.MIB->getEHInfo(Instr)) std::tie(LP, Action) = *EHInfo; // No action if the exception handler has not changed. @@ -397,16 +402,15 @@ void BinaryFunction::updateEHRanges() { continue; // Same symbol is used for the beginning and the end of the range. - const MCSymbol *EHSymbol; - MCInst EHLabel; - { + MCSymbol *EHSymbol; + if (MCSymbol *InstrLabel = BC.MIB->getLabel(Instr)) { + EHSymbol = InstrLabel; + } else { std::unique_lock Lock(BC.CtxMutex); EHSymbol = BC.Ctx->createNamedTempSymbol("EH"); - BC.MIB->createEHLabel(EHLabel, EHSymbol, BC.Ctx.get()); + BC.MIB->setLabel(Instr, EHSymbol); } - II = std::next(BB->insertPseudoInstr(II, EHLabel)); - // At this point we could be in one of the following states: // // I. Exception handler has changed and we need to close previous range diff --git a/bolt/lib/Core/HashUtilities.cpp b/bolt/lib/Core/HashUtilities.cpp index 6c7570dcc44e87b02a75b4ca36761fbddd4b8ecb..d40159b2e216d90f97b510886b7d79e468f895be 100644 --- a/bolt/lib/Core/HashUtilities.cpp +++ b/bolt/lib/Core/HashUtilities.cpp @@ -18,15 +18,6 @@ namespace llvm { namespace bolt { -/// Hashing a 64-bit integer to a 16-bit one. -uint16_t hash_64_to_16(const uint64_t Hash) { - uint16_t Res = (uint16_t)(Hash & 0xFFFF); - Res ^= (uint16_t)((Hash >> 16) & 0xFFFF); - Res ^= (uint16_t)((Hash >> 32) & 0xFFFF); - Res ^= (uint16_t)((Hash >> 48) & 0xFFFF); - return Res; -} - std::string hashInteger(uint64_t Value) { std::string HashString; if (Value == 0) diff --git a/bolt/lib/Core/MCPlusBuilder.cpp b/bolt/lib/Core/MCPlusBuilder.cpp index 036fcf8b3e27fd9e5b3035b2c4875c764aa58719..44e5f88d8950fc3fe1506128da325fe7c9a5e0c8 100644 --- a/bolt/lib/Core/MCPlusBuilder.cpp +++ b/bolt/lib/Core/MCPlusBuilder.cpp @@ -120,7 +120,7 @@ bool MCPlusBuilder::equals(const MCTargetExpr &A, const MCTargetExpr &B, llvm_unreachable("target-specific expressions are unsupported"); } -void MCPlusBuilder::setTailCall(MCInst &Inst) { +void MCPlusBuilder::setTailCall(MCInst &Inst) const { assert(!hasAnnotation(Inst, MCAnnotation::kTailCall)); setAnnotationOpValue(Inst, MCAnnotation::kTailCall, true); } @@ -149,7 +149,7 @@ std::optional MCPlusBuilder::getEHInfo(const MCInst &Inst) const { static_cast(*Action)); } -void MCPlusBuilder::addEHInfo(MCInst &Inst, const MCLandingPad &LP) { +void MCPlusBuilder::addEHInfo(MCInst &Inst, const MCLandingPad &LP) const { if (isCall(Inst)) { assert(!getEHInfo(Inst)); setAnnotationOpValue(Inst, MCAnnotation::kEHLandingPad, @@ -159,7 +159,7 @@ void MCPlusBuilder::addEHInfo(MCInst &Inst, const MCLandingPad &LP) { } } -bool MCPlusBuilder::updateEHInfo(MCInst &Inst, const MCLandingPad &LP) { +bool MCPlusBuilder::updateEHInfo(MCInst &Inst, const MCLandingPad &LP) const { if (!isInvoke(Inst)) return false; @@ -178,13 +178,12 @@ int64_t MCPlusBuilder::getGnuArgsSize(const MCInst &Inst) const { return *Value; } -void MCPlusBuilder::addGnuArgsSize(MCInst &Inst, int64_t GnuArgsSize, - AllocatorIdTy AllocId) { +void MCPlusBuilder::addGnuArgsSize(MCInst &Inst, int64_t GnuArgsSize) const { assert(GnuArgsSize >= 0 && "cannot set GNU_args_size to negative value"); assert(getGnuArgsSize(Inst) == -1LL && "GNU_args_size already set"); assert(isInvoke(Inst) && "GNU_args_size can only be set for invoke"); - setAnnotationOpValue(Inst, MCAnnotation::kGnuArgsSize, GnuArgsSize, AllocId); + setAnnotationOpValue(Inst, MCAnnotation::kGnuArgsSize, GnuArgsSize); } uint64_t MCPlusBuilder::getJumpTable(const MCInst &Inst) const { @@ -203,12 +202,12 @@ bool MCPlusBuilder::setJumpTable(MCInst &Inst, uint64_t Value, uint16_t IndexReg, AllocatorIdTy AllocId) { if (!isIndirectBranch(Inst)) return false; - setAnnotationOpValue(Inst, MCAnnotation::kJumpTable, Value, AllocId); + setAnnotationOpValue(Inst, MCAnnotation::kJumpTable, Value); getOrCreateAnnotationAs(Inst, "JTIndexReg", AllocId) = IndexReg; return true; } -bool MCPlusBuilder::unsetJumpTable(MCInst &Inst) { +bool MCPlusBuilder::unsetJumpTable(MCInst &Inst) const { if (!getJumpTable(Inst)) return false; removeAnnotation(Inst, MCAnnotation::kJumpTable); @@ -225,7 +224,7 @@ MCPlusBuilder::getConditionalTailCall(const MCInst &Inst) const { return static_cast(*Value); } -bool MCPlusBuilder::setConditionalTailCall(MCInst &Inst, uint64_t Dest) { +bool MCPlusBuilder::setConditionalTailCall(MCInst &Inst, uint64_t Dest) const { if (!isConditionalBranch(Inst)) return false; @@ -233,7 +232,7 @@ bool MCPlusBuilder::setConditionalTailCall(MCInst &Inst, uint64_t Dest) { return true; } -bool MCPlusBuilder::unsetConditionalTailCall(MCInst &Inst) { +bool MCPlusBuilder::unsetConditionalTailCall(MCInst &Inst) const { if (!getConditionalTailCall(Inst)) return false; removeAnnotation(Inst, MCAnnotation::kConditionalTailCall); @@ -255,76 +254,78 @@ uint32_t MCPlusBuilder::getOffsetWithDefault(const MCInst &Inst, return Default; } -bool MCPlusBuilder::setOffset(MCInst &Inst, uint32_t Offset, - AllocatorIdTy AllocatorId) { - setAnnotationOpValue(Inst, MCAnnotation::kOffset, Offset, AllocatorId); +bool MCPlusBuilder::setOffset(MCInst &Inst, uint32_t Offset) const { + setAnnotationOpValue(Inst, MCAnnotation::kOffset, Offset); return true; } -bool MCPlusBuilder::clearOffset(MCInst &Inst) { +bool MCPlusBuilder::clearOffset(MCInst &Inst) const { if (!hasAnnotation(Inst, MCAnnotation::kOffset)) return false; removeAnnotation(Inst, MCAnnotation::kOffset); return true; } -std::optional MCPlusBuilder::getLabel(const MCInst &Inst) const { - if (auto Label = tryGetAnnotationAs(Inst, MCAnnotation::kLabel)) - return *Label; - return std::nullopt; +MCSymbol *MCPlusBuilder::getLabel(const MCInst &Inst) const { + if (std::optional Label = + getAnnotationOpValue(Inst, MCAnnotation::kLabel)) + return reinterpret_cast(*Label); + return nullptr; } -bool MCPlusBuilder::setLabel(MCInst &Inst, MCSymbol *Label, - AllocatorIdTy AllocatorId) { - getOrCreateAnnotationAs(Inst, MCAnnotation::kLabel, AllocatorId) = - Label; +bool MCPlusBuilder::setLabel(MCInst &Inst, MCSymbol *Label) const { + setAnnotationOpValue(Inst, MCAnnotation::kLabel, + reinterpret_cast(Label)); return true; } -bool MCPlusBuilder::hasAnnotation(const MCInst &Inst, unsigned Index) const { - const MCInst *AnnotationInst = getAnnotationInst(Inst); - if (!AnnotationInst) - return false; +std::optional MCPlusBuilder::getSize(const MCInst &Inst) const { + if (std::optional Value = + getAnnotationOpValue(Inst, MCAnnotation::kSize)) + return static_cast(*Value); + return std::nullopt; +} +void MCPlusBuilder::setSize(MCInst &Inst, uint32_t Size) const { + setAnnotationOpValue(Inst, MCAnnotation::kSize, Size); +} + +bool MCPlusBuilder::hasAnnotation(const MCInst &Inst, unsigned Index) const { return (bool)getAnnotationOpValue(Inst, Index); } -bool MCPlusBuilder::removeAnnotation(MCInst &Inst, unsigned Index) { - MCInst *AnnotationInst = getAnnotationInst(Inst); - if (!AnnotationInst) +bool MCPlusBuilder::removeAnnotation(MCInst &Inst, unsigned Index) const { + std::optional FirstAnnotationOp = getFirstAnnotationOpIndex(Inst); + if (!FirstAnnotationOp) return false; - for (int I = AnnotationInst->getNumOperands() - 1; I >= 0; --I) { - int64_t ImmValue = AnnotationInst->getOperand(I).getImm(); + for (unsigned I = Inst.getNumOperands() - 1; I >= *FirstAnnotationOp; --I) { + const int64_t ImmValue = Inst.getOperand(I).getImm(); if (extractAnnotationIndex(ImmValue) == Index) { - AnnotationInst->erase(AnnotationInst->begin() + I); + Inst.erase(Inst.begin() + I); return true; } } return false; } -void MCPlusBuilder::stripAnnotations(MCInst &Inst, bool KeepTC) { - MCInst *AnnotationInst = getAnnotationInst(Inst); - if (!AnnotationInst) - return; - // Preserve TailCall annotation. - auto IsTC = hasAnnotation(Inst, MCAnnotation::kTailCall); +void MCPlusBuilder::stripAnnotations(MCInst &Inst, bool KeepTC) const { + KeepTC &= hasAnnotation(Inst, MCAnnotation::kTailCall); - removeAnnotationInst(Inst); + removeAnnotations(Inst); - if (KeepTC && IsTC) + if (KeepTC) setTailCall(Inst); } void MCPlusBuilder::printAnnotations(const MCInst &Inst, raw_ostream &OS) const { - const MCInst *AnnotationInst = getAnnotationInst(Inst); - if (!AnnotationInst) + std::optional FirstAnnotationOp = getFirstAnnotationOpIndex(Inst); + if (!FirstAnnotationOp) return; - for (unsigned I = 0; I < AnnotationInst->getNumOperands(); ++I) { - const int64_t Imm = AnnotationInst->getOperand(I).getImm(); + for (unsigned I = *FirstAnnotationOp; I < Inst.getNumOperands(); ++I) { + const int64_t Imm = Inst.getOperand(I).getImm(); const unsigned Index = extractAnnotationIndex(Imm); const int64_t Value = extractAnnotationValue(Imm); const auto *Annotation = reinterpret_cast(Value); diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp index e4d0f26c305f4e8e560a5fd74a8ac87df90b425a..ded497f3c06118a70b7cc5c9f2e3d1fc088033eb 100644 --- a/bolt/lib/Core/Relocation.cpp +++ b/bolt/lib/Core/Relocation.cpp @@ -295,7 +295,7 @@ static bool skipRelocationProcessAArch64(uint64_t &Type, uint64_t Contents) { // changed TLS access model (e.g. changed global dynamic model // to initial exec), thus changing the instructions. The static // relocations might be invalid at this point and we might no - // need to proccess these relocations anymore. + // need to process these relocations anymore. // More information could be found by searching // elfNN_aarch64_tls_relax in bfd switch (Type) { @@ -365,7 +365,9 @@ static uint64_t encodeValueAArch64(uint64_t Type, uint64_t Value, uint64_t PC) { switch (Type) { default: llvm_unreachable("unsupported relocation"); + case ELF::R_AARCH64_ABS16: case ELF::R_AARCH64_ABS32: + case ELF::R_AARCH64_ABS64: break; case ELF::R_AARCH64_PREL16: case ELF::R_AARCH64_PREL32: @@ -501,7 +503,7 @@ static uint64_t extractValueAArch64(uint64_t Type, uint64_t Contents, case ELF::R_AARCH64_MOVW_UABS_G1: case ELF::R_AARCH64_MOVW_UABS_G0_NC: case ELF::R_AARCH64_MOVW_UABS_G0: - // The shift goest in bits 22:21 of MOV* instructions + // The shift goes in bits 22:21 of MOV* instructions uint8_t Shift = (Contents >> 21) & 0x3; // Immediate goes in bits 20:5 Contents = (Contents >> 5) & 0xffff; diff --git a/bolt/lib/Passes/ADRRelaxationPass.cpp b/bolt/lib/Passes/ADRRelaxationPass.cpp index 7b612cbf6572bd3fad23701180ac2d1fb6a0d485..27a1377adef1641848188f31b55b47ac5f143764 100644 --- a/bolt/lib/Passes/ADRRelaxationPass.cpp +++ b/bolt/lib/Passes/ADRRelaxationPass.cpp @@ -56,9 +56,14 @@ void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) { continue; } - BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol); - if (TargetBF && TargetBF == &BF) - continue; + // Don't relax adr if it points to the same function and it is not split + // and BF initial size is < 1MB. + const unsigned OneMB = 0x100000; + if (!BF.isSplit() && BF.getSize() < OneMB) { + BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol); + if (TargetBF && TargetBF == &BF) + continue; + } MCPhysReg Reg; BC.MIB->getADRReg(Inst, Reg); @@ -72,14 +77,17 @@ void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) { if (It != BB.begin() && BC.MIB->isNoop(*std::prev(It))) { It = BB.eraseInstruction(std::prev(It)); - } else if (opts::StrictMode && !BF.isSimple()) { + } else if (std::next(It) != BB.end() && BC.MIB->isNoop(*std::next(It))) { + BB.eraseInstruction(std::next(It)); + } else if (!opts::StrictMode && !BF.isSimple()) { // If the function is not simple, it may contain a jump table undetected // by us. This jump table may use an offset from the branch instruction // to land in the desired place. If we add new instructions, we // invalidate this offset, so we have to rely on linker-inserted NOP to // replace it with ADRP, and abort if it is not present. + auto L = BC.scopeLock(); errs() << formatv("BOLT-ERROR: Cannot relax adr in non-simple function " - "{0}. Can't proceed in current mode.\n", + "{0}. Use --strict option to override\n", BF.getOneName()); PassFailed = true; return; diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index e50fa9dea602bc7cbb12e556937326a2b1ba63d4..4e1343e2c30be56dc2d9c719790a276b3b38469b 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -317,38 +317,46 @@ void NormalizeCFG::runOnFunctions(BinaryContext &BC) { } void EliminateUnreachableBlocks::runOnFunction(BinaryFunction &Function) { - if (!Function.getLayout().block_empty()) { - unsigned Count; - uint64_t Bytes; - Function.markUnreachableBlocks(); - LLVM_DEBUG({ - for (BinaryBasicBlock &BB : Function) { - if (!BB.isValid()) { - dbgs() << "BOLT-INFO: UCE found unreachable block " << BB.getName() - << " in function " << Function << "\n"; - Function.dump(); - } + BinaryContext &BC = Function.getBinaryContext(); + unsigned Count; + uint64_t Bytes; + Function.markUnreachableBlocks(); + LLVM_DEBUG({ + for (BinaryBasicBlock &BB : Function) { + if (!BB.isValid()) { + dbgs() << "BOLT-INFO: UCE found unreachable block " << BB.getName() + << " in function " << Function << "\n"; + Function.dump(); } - }); - std::tie(Count, Bytes) = Function.eraseInvalidBBs(); - DeletedBlocks += Count; - DeletedBytes += Bytes; - if (Count) { - Modified.insert(&Function); - if (opts::Verbosity > 0) - outs() << "BOLT-INFO: removed " << Count - << " dead basic block(s) accounting for " << Bytes - << " bytes in function " << Function << '\n'; } + }); + BinaryContext::IndependentCodeEmitter Emitter = + BC.createIndependentMCCodeEmitter(); + std::tie(Count, Bytes) = Function.eraseInvalidBBs(Emitter.MCE.get()); + DeletedBlocks += Count; + DeletedBytes += Bytes; + if (Count) { + auto L = BC.scopeLock(); + Modified.insert(&Function); + if (opts::Verbosity > 0) + outs() << "BOLT-INFO: removed " << Count + << " dead basic block(s) accounting for " << Bytes + << " bytes in function " << Function << '\n'; } } void EliminateUnreachableBlocks::runOnFunctions(BinaryContext &BC) { - for (auto &It : BC.getBinaryFunctions()) { - BinaryFunction &Function = It.second; - if (shouldOptimize(Function)) - runOnFunction(Function); - } + ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) { + runOnFunction(BF); + }; + + ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) { + return !shouldOptimize(BF) || BF.getLayout().block_empty(); + }; + + ParallelUtilities::runOnEachFunction( + BC, ParallelUtilities::SchedulingPolicy::SP_CONSTANT, WorkFun, + SkipPredicate, "elimininate-unreachable"); if (DeletedBlocks) outs() << "BOLT-INFO: UCE removed " << DeletedBlocks << " blocks and " @@ -574,65 +582,50 @@ bool CheckLargeFunctions::shouldOptimize(const BinaryFunction &BF) const { } void LowerAnnotations::runOnFunctions(BinaryContext &BC) { - std::vector> PreservedOffsetAnnotations; - std::vector> PreservedLabelAnnotations; - - for (auto &It : BC.getBinaryFunctions()) { - BinaryFunction &BF = It.second; - - for (FunctionFragment &FF : BF.getLayout().fragments()) { + for (BinaryFunction *BF : BC.getAllBinaryFunctions()) { + for (FunctionFragment &FF : BF->getLayout().fragments()) { + // Reset at the start of the new fragment. int64_t CurrentGnuArgsSize = 0; for (BinaryBasicBlock *const BB : FF) { - // First convert GnuArgsSize annotations into CFIs. This may change - // instr pointers, so do it before recording ptrs for preserved - // annotations - if (BF.usesGnuArgsSize()) { - for (auto II = BB->begin(); II != BB->end(); ++II) { - if (!BC.MIB->isInvoke(*II)) - continue; + for (auto II = BB->begin(); II != BB->end(); ++II) { + + // Convert GnuArgsSize annotations into CFIs. + if (BF->usesGnuArgsSize() && BC.MIB->isInvoke(*II)) { const int64_t NewGnuArgsSize = BC.MIB->getGnuArgsSize(*II); assert(NewGnuArgsSize >= 0 && - "expected non-negative GNU_args_size"); + "Expected non-negative GNU_args_size."); if (NewGnuArgsSize != CurrentGnuArgsSize) { - auto InsertII = BF.addCFIInstruction( + auto InsertII = BF->addCFIInstruction( BB, II, MCCFIInstruction::createGnuArgsSize(nullptr, NewGnuArgsSize)); CurrentGnuArgsSize = NewGnuArgsSize; II = std::next(InsertII); } } - } - // Now record preserved annotations separately and then strip - // annotations. - for (auto II = BB->begin(); II != BB->end(); ++II) { - if (BF.requiresAddressTranslation() && BC.MIB->getOffset(*II)) - PreservedOffsetAnnotations.emplace_back(&(*II), - *BC.MIB->getOffset(*II)); - if (auto Label = BC.MIB->getLabel(*II)) - PreservedLabelAnnotations.emplace_back(&*II, *Label); + // Preserve selected annotations and strip the rest. + std::optional Offset = BF->requiresAddressTranslation() + ? BC.MIB->getOffset(*II) + : std::nullopt; + std::optional Size = BC.MIB->getSize(*II); + MCSymbol *Label = BC.MIB->getLabel(*II); + BC.MIB->stripAnnotations(*II); + + if (Offset) + BC.MIB->setOffset(*II, *Offset); + if (Size) + BC.MIB->setSize(*II, *Size); + if (Label) + BC.MIB->setLabel(*II, Label); } } } } - for (BinaryFunction *BF : BC.getInjectedBinaryFunctions()) - for (BinaryBasicBlock &BB : *BF) - for (MCInst &Instruction : BB) { - if (auto Label = BC.MIB->getLabel(Instruction)) - PreservedLabelAnnotations.emplace_back(&Instruction, *Label); - BC.MIB->stripAnnotations(Instruction); - } // Release all memory taken by annotations BC.MIB->freeAnnotations(); - - // Reinsert preserved annotations we need during code emission. - for (const std::pair &Item : PreservedOffsetAnnotations) - BC.MIB->setOffset(*Item.first, Item.second); - for (auto [Instr, Label] : PreservedLabelAnnotations) - BC.MIB->setLabel(*Instr, Label); } // Check for dirty state in MCSymbol objects that might be a consequence diff --git a/bolt/lib/Passes/IndirectCallPromotion.cpp b/bolt/lib/Passes/IndirectCallPromotion.cpp index 457997d35323d8b02fa52e7979654b11679b1f11..8b084c3b63d565a090293b76b86cb1e1c7a5c141 100644 --- a/bolt/lib/Passes/IndirectCallPromotion.cpp +++ b/bolt/lib/Passes/IndirectCallPromotion.cpp @@ -754,6 +754,15 @@ IndirectCallPromotion::rewriteCall( const bool IsTailCallOrJT = (MIB->isTailCall(CallInst) || Function.getJumpTable(CallInst)); + // If we are tracking the indirect call/jump address, propagate the address to + // the ICP code. + const std::optional IndirectInstrOffset = MIB->getOffset(CallInst); + if (IndirectInstrOffset) { + for (auto &[Symbol, Instructions] : ICPcode) + for (MCInst &Inst : Instructions) + MIB->setOffset(Inst, *IndirectInstrOffset); + } + // Move instructions from the tail of the original call block // to the merge block. @@ -767,10 +776,12 @@ IndirectCallPromotion::rewriteCall( TailInsts.push_back(*++TailInst); InstructionListType MovedInst = IndCallBlock.splitInstructions(&CallInst); - // Link new BBs to the original input offset of the BB where the indirect - // call site is, so we can map samples recorded in new BBs back to the - // original BB seen in the input binary (if using BAT) - const uint32_t OrigOffset = IndCallBlock.getInputOffset(); + // Link new BBs to the original input offset of the indirect call site or its + // containing BB, so we can map samples recorded in new BBs back to the + // original BB seen in the input binary (if using BAT). + const uint32_t OrigOffset = IndirectInstrOffset + ? *IndirectInstrOffset + : IndCallBlock.getInputOffset(); IndCallBlock.eraseInstructions(MethodFetchInsns.begin(), MethodFetchInsns.end()); diff --git a/bolt/lib/Passes/Instrumentation.cpp b/bolt/lib/Passes/Instrumentation.cpp index 72adb319d71dc0e437981a31c9ba682523f9b065..e54b0cacc4ca96c9d3ed1adb03fc478a6c567cf8 100644 --- a/bolt/lib/Passes/Instrumentation.cpp +++ b/bolt/lib/Passes/Instrumentation.cpp @@ -34,7 +34,7 @@ cl::opt InstrumentationFilename( cl::opt InstrumentationBinpath( "instrumentation-binpath", - cl::desc("path to instumented binary in case if /proc/self/map_files " + cl::desc("path to instrumented binary in case if /proc/self/map_files " "is not accessible due to access restriction issues"), cl::Optional, cl::cat(BoltInstrCategory)); diff --git a/bolt/lib/Passes/JTFootprintReduction.cpp b/bolt/lib/Passes/JTFootprintReduction.cpp index aabe5a1b121e233aa3cc1835b1a249fbba5ca69e..d690e4d0c003dd517783fe35c96ba8353f18d7cf 100644 --- a/bolt/lib/Passes/JTFootprintReduction.cpp +++ b/bolt/lib/Passes/JTFootprintReduction.cpp @@ -232,7 +232,7 @@ void JTFootprintReduction::optimizeFunction(BinaryFunction &Function, continue; } - llvm_unreachable("Should either optimize PIC or NonPIC successfuly"); + llvm_unreachable("Should either optimize PIC or NonPIC successfully"); } if (!Modified.count(&Function)) diff --git a/bolt/lib/Passes/PatchEntries.cpp b/bolt/lib/Passes/PatchEntries.cpp index 02a044d8b2f69f5642e0dbe81714c16fc7953915..ee7512d89962f69b477ffbfe69ed04264ead8e5a 100644 --- a/bolt/lib/Passes/PatchEntries.cpp +++ b/bolt/lib/Passes/PatchEntries.cpp @@ -98,6 +98,17 @@ void PatchEntries::runOnFunctions(BinaryContext &BC) { }); if (!Success) { + // We can't change output layout for AArch64 due to LongJmp pass + if (BC.isAArch64()) { + if (opts::ForcePatch) { + errs() << "BOLT-ERROR: unable to patch entries in " << Function + << "\n"; + exit(1); + } + + continue; + } + // If the original function entries cannot be patched, then we cannot // safely emit new function body. errs() << "BOLT-WARNING: failed to patch entries in " << Function diff --git a/bolt/lib/Passes/ReorderData.cpp b/bolt/lib/Passes/ReorderData.cpp index 6e1f9b6d77512e12572ad2a6198bb261de95ad81..dc89b383552aba7224cd145993d73c5928d6bdc9 100644 --- a/bolt/lib/Passes/ReorderData.cpp +++ b/bolt/lib/Passes/ReorderData.cpp @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // TODO: -// - make sure writeable data isn't put on same cache line unless temporally +// - make sure writable data isn't put on same cache line unless temporally // local // - estimate temporal locality by looking at CFG? @@ -328,7 +328,7 @@ ReorderData::sortedByCount(BinaryContext &BC, // TODO // add option for cache-line alignment (or just use cache-line when section -// is writeable)? +// is writable)? void ReorderData::setSectionOrder(BinaryContext &BC, BinarySection &OutputSection, DataOrder::iterator Begin, diff --git a/bolt/lib/Passes/ReorderFunctions.cpp b/bolt/lib/Passes/ReorderFunctions.cpp index 92c50f49d20cf7a454547072751e2ae7d29d13bb..7e672969098130da7d62b95fff1abd11d4b83afd 100644 --- a/bolt/lib/Passes/ReorderFunctions.cpp +++ b/bolt/lib/Passes/ReorderFunctions.cpp @@ -427,6 +427,8 @@ void ReorderFunctions::runOnFunctions(BinaryContext &BC) { reorder(std::move(Clusters), BFs); + BC.HasFinalizedFunctionOrder = true; + std::unique_ptr FuncsFile; if (!opts::GenerateFunctionOrderFile.empty()) { FuncsFile = std::make_unique(opts::GenerateFunctionOrderFile, diff --git a/bolt/lib/Passes/RetpolineInsertion.cpp b/bolt/lib/Passes/RetpolineInsertion.cpp index c8a42725c9745a86139f3a4d5e4f6109bbbc0cb3..97eedb882f550688975d11d0a0b0d4f59d725944 100644 --- a/bolt/lib/Passes/RetpolineInsertion.cpp +++ b/bolt/lib/Passes/RetpolineInsertion.cpp @@ -52,9 +52,9 @@ cl::opt R11Availability( cl::values(clEnumValN(RetpolineInsertion::AvailabilityOptions::NEVER, "never", "r11 not available"), clEnumValN(RetpolineInsertion::AvailabilityOptions::ALWAYS, - "always", "r11 avaialable before calls and jumps"), + "always", "r11 available before calls and jumps"), clEnumValN(RetpolineInsertion::AvailabilityOptions::ABI, "abi", - "r11 avaialable before calls but not before jumps")), + "r11 available before calls but not before jumps")), cl::ZeroOrMore, cl::cat(BoltCategory)); } // namespace opts diff --git a/bolt/lib/Passes/ShrinkWrapping.cpp b/bolt/lib/Passes/ShrinkWrapping.cpp index 17f169cc332b644e3a4ee99fb00c33fb873dab8e..2dd57f3bbf3d25b6939c31ae3d1e10826abafdb4 100644 --- a/bolt/lib/Passes/ShrinkWrapping.cpp +++ b/bolt/lib/Passes/ShrinkWrapping.cpp @@ -903,7 +903,7 @@ bool ShrinkWrapping::isBestSavePosCold(unsigned CSR, MCInst *&BestPosSave, return true; } -/// Auxiliar function used to create basic blocks for critical edges and update +/// Auxiliary function used to create basic blocks for critical edges and update /// the dominance frontier with these new locations void ShrinkWrapping::splitFrontierCritEdges( BinaryFunction *Func, SmallVector &Frontier, diff --git a/bolt/lib/Passes/SplitFunctions.cpp b/bolt/lib/Passes/SplitFunctions.cpp index 34973cecdf49161658bb46409ec1e645ccf52db2..af2a5aaa27ed5f4f87805b8f507f2a4c72937c75 100644 --- a/bolt/lib/Passes/SplitFunctions.cpp +++ b/bolt/lib/Passes/SplitFunctions.cpp @@ -92,6 +92,9 @@ static cl::opt SplitStrategy( cl::values(clEnumValN(SplitFunctionsStrategy::Profile2, "profile2", "split each function into a hot and cold fragment " "using profiling information")), + cl::values(clEnumValN(SplitFunctionsStrategy::CDSplit, "cdsplit", + "split each function into a hot, warm, and cold " + "fragment using profiling information")), cl::values(clEnumValN( SplitFunctionsStrategy::Random2, "random2", "split each function into a hot and cold fragment at a randomly chosen " @@ -126,7 +129,7 @@ struct SplitProfile2 final : public SplitStrategy { return BF.hasValidProfile() && hasFullProfile(BF) && !allBlocksCold(BF); } - bool keepEmpty() override { return false; } + bool compactFragments() override { return true; } void fragment(const BlockIt Start, const BlockIt End) override { for (BinaryBasicBlock *const BB : llvm::make_range(Start, End)) { @@ -136,6 +139,51 @@ struct SplitProfile2 final : public SplitStrategy { } }; +struct SplitCacheDirected final : public SplitStrategy { + using BasicBlockOrder = BinaryFunction::BasicBlockOrderType; + + bool canSplit(const BinaryFunction &BF) override { + return BF.hasValidProfile() && hasFullProfile(BF) && !allBlocksCold(BF); + } + + // When some functions are hot-warm split and others are hot-warm-cold split, + // we do not want to change the fragment numbers of the blocks in the hot-warm + // split functions. + bool compactFragments() override { return false; } + + void fragment(const BlockIt Start, const BlockIt End) override { + BasicBlockOrder BlockOrder(Start, End); + BinaryFunction &BF = *BlockOrder.front()->getFunction(); + + size_t BestSplitIndex = findSplitIndex(BF, BlockOrder); + + // Assign fragments based on the computed best split index. + // All basic blocks with index up to the best split index become hot. + // All remaining blocks are warm / cold depending on if count is + // greater than 0 or not. + FragmentNum Main(0); + FragmentNum Cold(1); + FragmentNum Warm(2); + for (size_t Index = 0; Index < BlockOrder.size(); Index++) { + BinaryBasicBlock *BB = BlockOrder[Index]; + if (Index <= BestSplitIndex) + BB->setFragmentNum(Main); + else + BB->setFragmentNum(BB->getKnownExecutionCount() > 0 ? Warm : Cold); + } + } + +private: + /// Find the best index for splitting. The returned value is the index of the + /// last hot basic block. Hence, "no splitting" is equivalent to returning the + /// value which is one less than the size of the function. + size_t findSplitIndex(const BinaryFunction &BF, + const BasicBlockOrder &BlockOrder) { + // Placeholder: hot-warm split after entry block. + return 0; + } +}; + struct SplitRandom2 final : public SplitStrategy { std::minstd_rand0 Gen; @@ -143,7 +191,7 @@ struct SplitRandom2 final : public SplitStrategy { bool canSplit(const BinaryFunction &BF) override { return true; } - bool keepEmpty() override { return false; } + bool compactFragments() override { return true; } void fragment(const BlockIt Start, const BlockIt End) override { using DiffT = typename std::iterator_traits::difference_type; @@ -170,7 +218,7 @@ struct SplitRandomN final : public SplitStrategy { bool canSplit(const BinaryFunction &BF) override { return true; } - bool keepEmpty() override { return false; } + bool compactFragments() override { return true; } void fragment(const BlockIt Start, const BlockIt End) override { using DiffT = typename std::iterator_traits::difference_type; @@ -217,10 +265,10 @@ struct SplitRandomN final : public SplitStrategy { struct SplitAll final : public SplitStrategy { bool canSplit(const BinaryFunction &BF) override { return true; } - bool keepEmpty() override { + bool compactFragments() override { // Keeping empty fragments allows us to test, that empty fragments do not // generate symbols. - return true; + return false; } void fragment(const BlockIt Start, const BlockIt End) override { @@ -246,10 +294,26 @@ void SplitFunctions::runOnFunctions(BinaryContext &BC) { if (!opts::SplitFunctions) return; + // If split strategy is not CDSplit, then a second run of the pass is not + // needed after function reordering. + if (BC.HasFinalizedFunctionOrder && + opts::SplitStrategy != SplitFunctionsStrategy::CDSplit) + return; + std::unique_ptr Strategy; bool ForceSequential = false; switch (opts::SplitStrategy) { + case SplitFunctionsStrategy::CDSplit: + // CDSplit runs two splitting passes: hot-cold splitting (SplitPrfoile2) + // before function reordering and hot-warm-cold splitting + // (SplitCacheDirected) after function reordering. + if (BC.HasFinalizedFunctionOrder) + Strategy = std::make_unique(); + else + Strategy = std::make_unique(); + opts::AggressiveSplitting = true; + break; case SplitFunctionsStrategy::Profile2: Strategy = std::make_unique(); break; @@ -382,7 +446,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { CurrentFragment = BB->getFragmentNum(); } - if (!S.keepEmpty()) { + if (S.compactFragments()) { FragmentNum CurrentFragment = FragmentNum::main(); FragmentNum NewFragment = FragmentNum::main(); for (BinaryBasicBlock *const BB : NewLayout) { @@ -394,7 +458,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { } } - BF.getLayout().update(NewLayout); + const bool LayoutUpdated = BF.getLayout().update(NewLayout); // For shared objects, invoke instructions and corresponding landing pads // have to be placed in the same fragment. When we split them, create @@ -404,7 +468,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { Trampolines = createEHTrampolines(BF); // Check the new size to see if it's worth splitting the function. - if (BC.isX86() && BF.isSplit()) { + if (BC.isX86() && LayoutUpdated) { std::tie(HotSize, ColdSize) = BC.calculateEmittedSize(BF); LLVM_DEBUG(dbgs() << "Estimated size for function " << BF << " post-split is <0x" << Twine::utohexstr(HotSize) @@ -431,6 +495,11 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) { SplitBytesCold += ColdSize; } } + + // Fix branches if the splitting decision of the pass after function + // reordering is different from that of the pass before function reordering. + if (LayoutUpdated && BC.HasFinalizedFunctionOrder) + BF.fixBranches(); } SplitFunctions::TrampolineSetType diff --git a/bolt/lib/Passes/StokeInfo.cpp b/bolt/lib/Passes/StokeInfo.cpp index 57e5a08113dd0f11bee6f5d2391e50c3d2a80a2b..419ba236e1342b02cbb6a4934ac04dc48e49a4da 100644 --- a/bolt/lib/Passes/StokeInfo.cpp +++ b/bolt/lib/Passes/StokeInfo.cpp @@ -50,11 +50,17 @@ void StokeInfo::checkInstr(const BinaryFunction &BF, StokeFuncInfo &FuncInfo) { if (BB->empty()) continue; + // Skip function with exception handling. + if (BB->throw_size() || BB->lp_size()) { + FuncInfo.Omitted = true; + return; + } + for (const MCInst &It : *BB) { if (MIB->isPseudo(It)) continue; // skip function with exception handling yet - if (MIB->isEHLabel(It) || MIB->isInvoke(It)) { + if (MIB->isInvoke(It)) { FuncInfo.Omitted = true; return; } diff --git a/bolt/lib/Passes/TailDuplication.cpp b/bolt/lib/Passes/TailDuplication.cpp index 12d33713c0ff22c52bb39d042cba1436c19eb82d..e63d4be1392f4d8ca5e59ad6aaef3c93e45ee788 100644 --- a/bolt/lib/Passes/TailDuplication.cpp +++ b/bolt/lib/Passes/TailDuplication.cpp @@ -236,7 +236,7 @@ void TailDuplication::constantAndCopyPropagate( break; } - // If the register was replaced everwhere and it was overwritten in either + // If the register was replaced everywhere and it was overwritten in either // one of the iterated through blocks or one of the successor blocks, delete // the original move instruction if (ReplacedEverywhere && @@ -276,7 +276,7 @@ TailDuplication::moderateDuplicate(BinaryBasicBlock &BB, // The block must be hot if (BB.getKnownExecutionCount() == 0) return BlocksToDuplicate; - // and its sucessor is not already in the same cache line + // and its successor is not already in the same cache line if (isInCacheLine(BB, Tail)) return BlocksToDuplicate; // and its size do not exceed the maximum allowed size @@ -299,7 +299,7 @@ TailDuplication::aggressiveDuplicate(BinaryBasicBlock &BB, // The block must be hot if (BB.getKnownExecutionCount() == 0) return BlocksToDuplicate; - // and its sucessor is not already in the same cache line + // and its successor is not already in the same cache line if (isInCacheLine(BB, Tail)) return BlocksToDuplicate; diff --git a/bolt/lib/Passes/ValidateInternalCalls.cpp b/bolt/lib/Passes/ValidateInternalCalls.cpp index 22dadf4f6403be3699cf0a0a2dc7521ced10e49d..516f91acb5084417e4844cd4948e525f79cefaa0 100644 --- a/bolt/lib/Passes/ValidateInternalCalls.cpp +++ b/bolt/lib/Passes/ValidateInternalCalls.cpp @@ -281,18 +281,16 @@ bool ValidateInternalCalls::analyzeFunction(BinaryFunction &Function) const { LLVM_DEBUG({ dbgs() << "Detected out-of-range PIC reference in " << Function << "\nReturn address load: "; - BC.InstPrinter->printInst(TargetInst, 0, "", *BC.STI, dbgs()); - dbgs() << "\nUse: "; - BC.InstPrinter->printInst(&Use, 0, "", *BC.STI, dbgs()); - dbgs() << "\n"; + BC.dump(*TargetInst); + dbgs() << "Use: "; + BC.dump(Use); Function.dump(); }); return false; } LLVM_DEBUG({ dbgs() << "Validated access: "; - BC.InstPrinter->printInst(&Use, 0, "", *BC.STI, dbgs()); - dbgs() << "\n"; + BC.dump(Use); }); } if (!UseDetected) { diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index b72bd0edf1a2df22a16ebb3dd06ed33907533d37..0b306b9965cc3dfb50bd3ddc3cf8109c3b6b006f 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -46,6 +46,11 @@ static cl::opt cl::desc("aggregate basic samples (without LBR info)"), cl::cat(AggregatorCategory)); +static cl::opt + ITraceAggregation("itrace", + cl::desc("Generate LBR info with perf itrace argument"), + cl::cat(AggregatorCategory)); + static cl::opt FilterMemProfile("filter-mem-profile", cl::desc("if processing a memory profile, filter out stack or heap accesses " @@ -163,16 +168,23 @@ void DataAggregator::start() { findPerfExecutable(); - if (opts::BasicAggregation) + if (opts::BasicAggregation) { launchPerfProcess("events without LBR", MainEventsPPI, "script -F pid,event,ip", /*Wait = */false); - else + } else if (!opts::ITraceAggregation.empty()) { + std::string ItracePerfScriptArgs = llvm::formatv( + "script -F pid,ip,brstack --itrace={0}", opts::ITraceAggregation); + launchPerfProcess("branch events with itrace", MainEventsPPI, + ItracePerfScriptArgs.c_str(), + /*Wait = */ false); + } else { launchPerfProcess("branch events", MainEventsPPI, "script -F pid,ip,brstack", /*Wait = */false); + } // Note: we launch script for mem events regardless of the option, as the // command fails fairly fast if mem events were not collected. diff --git a/bolt/lib/Profile/DataReader.cpp b/bolt/lib/Profile/DataReader.cpp index 64873e0de4bab55827890c5a824abd618fc114ce..3d4db6feb51a72eb53ca287fe1c9a5fd0229b45d 100644 --- a/bolt/lib/Profile/DataReader.cpp +++ b/bolt/lib/Profile/DataReader.cpp @@ -698,7 +698,8 @@ bool DataReader::recordBranch(BinaryFunction &BF, uint64_t From, uint64_t To, if (!BC.MIB->isNoop(Instr)) break; - Offset += BC.MIB->getAnnotationWithDefault(Instr, "Size"); + if (std::optional Size = BC.MIB->getSize(Instr)) + Offset += *Size; } if (To == Offset) diff --git a/bolt/lib/Profile/StaleProfileMatching.cpp b/bolt/lib/Profile/StaleProfileMatching.cpp index d00bf87ffc8ad871ecba876be40dc8b822a5503b..6fb6f380f71eecd8551d0168641627195bfc179e 100644 --- a/bolt/lib/Profile/StaleProfileMatching.cpp +++ b/bolt/lib/Profile/StaleProfileMatching.cpp @@ -30,6 +30,7 @@ #include "llvm/ADT/Bitfields.h" #include "llvm/ADT/Hashing.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/xxhash.h" #include "llvm/Transforms/Utils/SampleProfileInference.h" #include @@ -240,12 +241,12 @@ void BinaryFunction::computeBlockHashes() const { // Hashing complete instructions. std::string InstrHashStr = hashBlock( BC, *BB, [&](const MCOperand &Op) { return hashInstOperand(BC, Op); }); - uint64_t InstrHash = std::hash{}(InstrHashStr); - BlendedHashes[I].InstrHash = (uint16_t)hash_value(InstrHash); + uint64_t InstrHash = llvm::xxh3_64bits(InstrHashStr); + BlendedHashes[I].InstrHash = (uint16_t)InstrHash; // Hashing opcodes. std::string OpcodeHashStr = hashBlockLoose(BC, *BB); - OpcodeHashes[I] = std::hash{}(OpcodeHashStr); - BlendedHashes[I].OpcodeHash = (uint16_t)hash_value(OpcodeHashes[I]); + OpcodeHashes[I] = llvm::xxh3_64bits(OpcodeHashStr); + BlendedHashes[I].OpcodeHash = (uint16_t)OpcodeHashes[I]; } // Initialize neighbor hash. @@ -257,7 +258,7 @@ void BinaryFunction::computeBlockHashes() const { uint64_t SuccHash = OpcodeHashes[SuccBB->getIndex()]; Hash = hashing::detail::hash_16_bytes(Hash, SuccHash); } - BlendedHashes[I].SuccHash = (uint8_t)hash_value(Hash); + BlendedHashes[I].SuccHash = (uint8_t)Hash; // Append hashes of predecessors. Hash = 0; @@ -265,7 +266,7 @@ void BinaryFunction::computeBlockHashes() const { uint64_t PredHash = OpcodeHashes[PredBB->getIndex()]; Hash = hashing::detail::hash_16_bytes(Hash, PredHash); } - BlendedHashes[I].PredHash = (uint8_t)hash_value(Hash); + BlendedHashes[I].PredHash = (uint8_t)Hash; } // Assign hashes. @@ -405,6 +406,8 @@ void matchWeightsByHashes(BinaryContext &BC, ++BC.Stats.NumMatchedBlocks; BC.Stats.MatchedSampleCount += YamlBB.ExecCount; LLVM_DEBUG(dbgs() << " exact match\n"); + } else { + LLVM_DEBUG(dbgs() << " loose match\n"); } } else { LLVM_DEBUG( diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index 06cad69754816fde7ee1de4691095e7689c5bbfd..079cb352d36e77c9fdac1f79a42322cab1bb3635 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -31,7 +31,7 @@ static llvm::cl::opt llvm::cl::opt ProfileUseDFS("profile-use-dfs", cl::desc("use DFS order for YAML profile"), cl::Hidden, cl::cat(BoltOptCategory)); -} +} // namespace opts namespace llvm { namespace bolt { @@ -354,7 +354,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { matchProfileToFunction(YamlBF, Function); } - for (auto &[CommonName, LTOProfiles]: LTOCommonNameMap) { + for (const auto &[CommonName, LTOProfiles] : LTOCommonNameMap) { if (!LTOCommonNameFunctionMap.contains(CommonName)) continue; std::unordered_set &Functions = diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp index cd27c71ba2aedf26c8e4865ae8859acb5aa9a907..9946608c96d8ee99b2ca7c977ca7999c3373d98d 100644 --- a/bolt/lib/Rewrite/BinaryPassManager.cpp +++ b/bolt/lib/Rewrite/BinaryPassManager.cpp @@ -72,6 +72,11 @@ static cl::opt JTFootprintReductionFlag( "instructions at jump sites"), cl::cat(BoltOptCategory)); +static cl::opt + KeepNops("keep-nops", + cl::desc("keep no-op instructions. By default they are removed."), + cl::Hidden, cl::cat(BoltOptCategory)); + cl::opt NeverPrint("never-print", cl::desc("never print"), cl::ReallyHidden, cl::cat(BoltOptCategory)); @@ -359,7 +364,8 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) { Manager.registerPass(std::make_unique(NeverPrint)); - Manager.registerPass(std::make_unique(NeverPrint)); + Manager.registerPass(std::make_unique(NeverPrint), + !opts::KeepNops); Manager.registerPass(std::make_unique(PrintNormalized)); @@ -424,6 +430,13 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) { Manager.registerPass( std::make_unique(PrintReorderedFunctions)); + // This is the second run of the SplitFunctions pass required by certain + // splitting strategies (e.g. cdsplit). Running the SplitFunctions pass again + // after ReorderFunctions allows the finalized function order to be utilized + // to make more sophisticated splitting decisions, like hot-warm-cold + // splitting. + Manager.registerPass(std::make_unique(PrintSplit)); + // Print final dyno stats right while CFG and instruction analysis are intact. Manager.registerPass( std::make_unique( diff --git a/bolt/lib/Rewrite/BoltDiff.cpp b/bolt/lib/Rewrite/BoltDiff.cpp index 69ba63a9e83a406276cac35a48c4f3270b2e46d7..16a90510962e8ee1855500b5201e0e195fe0dde6 100644 --- a/bolt/lib/Rewrite/BoltDiff.cpp +++ b/bolt/lib/Rewrite/BoltDiff.cpp @@ -672,7 +672,7 @@ public: }; -} // end nampespace bolt +} // end namespace bolt } // end namespace llvm void RewriteInstance::compare(RewriteInstance &RI2) { diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index 0ac8e3956b90f610588868df903b4b6bc645f7c9..0beb865bd48cb1fc3575a3a0fb6e7b8e24ab9a8a 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -88,6 +88,96 @@ static void printDie(DWARFUnit &DU, uint64_t DIEOffset) { } } +using namespace bolt; + +/// Take a set of DWARF address ranges corresponding to the input binary and +/// translate them to a set of address ranges in the output binary. +static DebugAddressRangesVector +translateInputToOutputRanges(const BinaryFunction &BF, + const DWARFAddressRangesVector &InputRanges) { + DebugAddressRangesVector OutputRanges; + + // If the function hasn't changed return the same ranges. + if (!BF.isEmitted()) { + OutputRanges.resize(InputRanges.size()); + llvm::transform(InputRanges, OutputRanges.begin(), + [](const DWARFAddressRange &Range) { + return DebugAddressRange(Range.LowPC, Range.HighPC); + }); + return OutputRanges; + } + + for (const DWARFAddressRange &Range : InputRanges) + llvm::append_range(OutputRanges, BF.translateInputToOutputRange( + {Range.LowPC, Range.HighPC})); + + // Post-processing pass to sort and merge ranges. + llvm::sort(OutputRanges); + DebugAddressRangesVector MergedRanges; + uint64_t PrevHighPC = 0; + for (const DebugAddressRange &Range : OutputRanges) { + if (Range.LowPC <= PrevHighPC) { + MergedRanges.back().HighPC = + std::max(MergedRanges.back().HighPC, Range.HighPC); + } else { + MergedRanges.emplace_back(Range.LowPC, Range.HighPC); + } + PrevHighPC = MergedRanges.back().HighPC; + } + + return MergedRanges; +} + +/// Similar to translateInputToOutputRanges() but operates on location lists. +static DebugLocationsVector +translateInputToOutputLocationList(const BinaryFunction &BF, + const DebugLocationsVector &InputLL) { + DebugLocationsVector OutputLL; + + // If the function hasn't changed - there's nothing to update. + if (!BF.isEmitted()) + return InputLL; + + for (const DebugLocationEntry &Entry : InputLL) { + DebugAddressRangesVector OutRanges = + BF.translateInputToOutputRange({Entry.LowPC, Entry.HighPC}); + if (!OutRanges.empty() && !OutputLL.empty()) { + if (OutRanges.front().LowPC == OutputLL.back().HighPC && + Entry.Expr == OutputLL.back().Expr) { + OutputLL.back().HighPC = + std::max(OutputLL.back().HighPC, OutRanges.front().HighPC); + OutRanges.erase(OutRanges.begin()); + } + } + llvm::transform(OutRanges, std::back_inserter(OutputLL), + [&Entry](const DebugAddressRange &R) { + return DebugLocationEntry{R.LowPC, R.HighPC, Entry.Expr}; + }); + } + + // Sort and merge adjacent entries with identical locations. + llvm::stable_sort( + OutputLL, [](const DebugLocationEntry &A, const DebugLocationEntry &B) { + return A.LowPC < B.LowPC; + }); + DebugLocationsVector MergedLL; + uint64_t PrevHighPC = 0; + const SmallVectorImpl *PrevExpr = nullptr; + for (const DebugLocationEntry &Entry : OutputLL) { + if (Entry.LowPC <= PrevHighPC && *PrevExpr == Entry.Expr) { + MergedLL.back().HighPC = std::max(Entry.HighPC, MergedLL.back().HighPC); + } else { + const uint64_t Begin = std::max(Entry.LowPC, PrevHighPC); + const uint64_t End = std::max(Begin, Entry.HighPC); + MergedLL.emplace_back(DebugLocationEntry{Begin, End, Entry.Expr}); + } + PrevHighPC = MergedLL.back().HighPC; + PrevExpr = &MergedLL.back().Expr; + } + + return MergedLL; +} + namespace llvm { namespace bolt { /// Emits debug information into .debug_info or .debug_types section. @@ -350,6 +440,7 @@ getDWOName(llvm::DWARFUnit &CU, assert(!DWOName.empty() && "DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exists."); if (!opts::DwarfOutputPath.empty()) { + DWOName = std::string(sys::path::filename(DWOName)); auto Iter = NameToIndexMap.find(DWOName); if (Iter == NameToIndexMap.end()) Iter = NameToIndexMap.insert({DWOName, 0}).first; @@ -390,7 +481,7 @@ emitUnit(DIEBuilder &DIEBldr, DIEStreamer &Streamer, DWARFUnit &Unit) { static void emitDWOBuilder(const std::string &DWOName, DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter, - const DWARFUnit &SplitCU, DWARFUnit &CU, + DWARFUnit &SplitCU, DWARFUnit &CU, DWARFRewriter::DWPState &State, DebugLocWriter &LocWriter) { // Populate debug_info and debug_abbrev for current dwo into StringRef. @@ -407,17 +498,15 @@ static void emitDWOBuilder(const std::string &DWOName, DWARFRewriter::UnitMetaVectorType TUMetaVector; DWARFRewriter::UnitMeta CUMI = {0, 0, 0}; if (SplitCU.getContext().getMaxDWOVersion() >= 5) { - // TODO: Handle DWP as input. Right now it will iterate over all of CUs and - // TUs for (std::unique_ptr &CU : SplitCU.getContext().dwo_info_section_units()) { + if (!CU->isTypeUnit()) + continue; DWARFRewriter::UnitMeta MI = emitUnit(DWODIEBuilder, *Streamer, *CU.get()); - if (CU->isTypeUnit()) - TUMetaVector.emplace_back(MI); - else - CUMI = MI; + TUMetaVector.emplace_back(MI); } + CUMI = emitUnit(DWODIEBuilder, *Streamer, SplitCU); } else { for (std::unique_ptr &CU : SplitCU.getContext().dwo_compile_units()) @@ -616,7 +705,7 @@ void DWARFRewriter::updateDebugInfo() { // Skipping CUs that failed to load. if (SplitCU) { DIEBuilder DWODIEBuilder(&(*SplitCU)->getContext(), true); - DWODIEBuilder.buildBoth(); + DWODIEBuilder.buildDWOUnit(**SplitCU); std::string DWOName = updateDWONameCompDir( *Unit, *DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit)); @@ -861,7 +950,7 @@ void DWARFRewriter::updateUnitDebugInfo( : nullptr; DebugAddressRangesVector OutputRanges; if (Function) { - OutputRanges = Function->translateInputToOutputRanges(*RangesOrError); + OutputRanges = translateInputToOutputRanges(*Function, *RangesOrError); LLVM_DEBUG(if (OutputRanges.empty() != RangesOrError->empty()) { dbgs() << "BOLT-DEBUG: problem with DIE at 0x" << Twine::utohexstr(Die->getOffset()) << " in CU at 0x" @@ -872,8 +961,7 @@ void DWARFRewriter::updateUnitDebugInfo( std::move(OutputRanges), CachedRanges); OutputRanges.clear(); } else if (OutputRanges.empty()) { - OutputRanges.push_back({RangesOrError.get().front().LowPC, - RangesOrError.get().front().HighPC}); + OutputRanges.push_back({0, RangesOrError.get().front().HighPC}); } } else if (!RangesOrError) { consumeError(RangesOrError.takeError()); @@ -1022,7 +1110,7 @@ void DWARFRewriter::updateUnitDebugInfo( DebugLocationsVector OutputLL; if (const BinaryFunction *Function = BC.getBinaryFunctionContainingAddress(Address)) { - OutputLL = Function->translateInputToOutputLocationList(InputLL); + OutputLL = translateInputToOutputLocationList(*Function, InputLL); LLVM_DEBUG(if (OutputLL.empty()) { dbgs() << "BOLT-DEBUG: location list translated to an empty " "one at 0x" @@ -1306,7 +1394,7 @@ void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) { std::optional StmtList = Unit->getUnitDIE().find(dwarf::DW_AT_stmt_list); std::optional Offset = dwarf::toSectionOffset(StmtList); - assert(Offset && "Was not able to retreive value of DW_AT_stmt_list."); + assert(Offset && "Was not able to retrieve value of DW_AT_stmt_list."); return *Offset; }; @@ -1635,7 +1723,7 @@ std::optional updateDebugData( case DWARFSectionKind::DW_SECT_LOCLISTS: { OutputBuffer = LocWriter.getBuffer(); // Creating explicit StringRef here, otherwise - // with impicit conversion it will take null byte as end of + // with implicit conversion it will take null byte as end of // string. return StringRef(reinterpret_cast(OutputBuffer->data()), OutputBuffer->size()); @@ -1904,7 +1992,7 @@ void DWARFRewriter::writeDWOFiles( "No RangeListsWriter for DWO ID."); RangeListssWriter = RangeListsWritersByCU[DWOId].get(); - // Handling .debug_rnglists.dwo seperatly. The original .o/.dwo might not + // Handling .debug_rnglists.dwo separately. The original .o/.dwo might not // have .debug_rnglists so won't be part of the loop below. if (!RangeListssWriter->empty()) { std::unique_ptr OutputData; diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 0d78c9b75e03d322736f0ce9dd570e7007df852f..81c9cbff726bb9a1df68c7a00d9651677a6ebb05 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -704,6 +704,10 @@ Error RewriteInstance::run() { adjustCommandLineOptions(); discoverFileObjects(); + if (opts::Instrument && !BC->IsStaticExecutable) + if (Error E = discoverRtFiniAddress()) + return E; + preprocessProfileData(); // Skip disassembling if we have a translation table and we are running an @@ -740,6 +744,9 @@ Error RewriteInstance::run() { updateMetadata(); + if (opts::Instrument && !BC->IsStaticExecutable) + updateRtFiniReloc(); + if (opts::LinuxKernelMode) { errs() << "BOLT-WARNING: not writing the output file for Linux Kernel\n"; return Error::success(); @@ -1280,6 +1287,77 @@ void RewriteInstance::discoverFileObjects() { registerFragments(); } +Error RewriteInstance::discoverRtFiniAddress() { + // Use DT_FINI if it's available. + if (BC->FiniAddress) { + BC->FiniFunctionAddress = BC->FiniAddress; + return Error::success(); + } + + if (!BC->FiniArrayAddress || !BC->FiniArraySize) { + return createStringError( + std::errc::not_supported, + "Instrumentation needs either DT_FINI or DT_FINI_ARRAY"); + } + + if (*BC->FiniArraySize < BC->AsmInfo->getCodePointerSize()) { + return createStringError(std::errc::not_supported, + "Need at least 1 DT_FINI_ARRAY slot"); + } + + ErrorOr FiniArraySection = + BC->getSectionForAddress(*BC->FiniArrayAddress); + if (auto EC = FiniArraySection.getError()) + return errorCodeToError(EC); + + if (const Relocation *Reloc = FiniArraySection->getDynamicRelocationAt(0)) { + BC->FiniFunctionAddress = Reloc->Addend; + return Error::success(); + } + + if (const Relocation *Reloc = FiniArraySection->getRelocationAt(0)) { + BC->FiniFunctionAddress = Reloc->Value; + return Error::success(); + } + + return createStringError(std::errc::not_supported, + "No relocation for first DT_FINI_ARRAY slot"); +} + +void RewriteInstance::updateRtFiniReloc() { + // Updating DT_FINI is handled by patchELFDynamic. + if (BC->FiniAddress) + return; + + const RuntimeLibrary *RT = BC->getRuntimeLibrary(); + if (!RT || !RT->getRuntimeFiniAddress()) + return; + + assert(BC->FiniArrayAddress && BC->FiniArraySize && + "inconsistent .fini_array state"); + + ErrorOr FiniArraySection = + BC->getSectionForAddress(*BC->FiniArrayAddress); + assert(FiniArraySection && ".fini_array removed"); + + if (std::optional Reloc = + FiniArraySection->takeDynamicRelocationAt(0)) { + assert(Reloc->Addend == BC->FiniFunctionAddress && + "inconsistent .fini_array dynamic relocation"); + Reloc->Addend = RT->getRuntimeFiniAddress(); + FiniArraySection->addDynamicRelocation(*Reloc); + } + + // Update the static relocation by adding a pending relocation which will get + // patched when flushPendingRelocations is called in rewriteFile. Note that + // flushPendingRelocations will calculate the value to patch as + // "Symbol + Addend". Since we don't have a symbol, just set the addend to the + // desired value. + FiniArraySection->addPendingRelocation(Relocation{ + /*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(), + /*Addend*/ RT->getRuntimeFiniAddress(), /*Value*/ 0}); +} + void RewriteInstance::registerFragments() { if (!BC->HasSplitFunctions) return; @@ -1344,24 +1422,41 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress, BinaryFunction *BF = BC->getBinaryFunctionAtAddress(EntryAddress); if (BF && BC->isAArch64()) { - // Handle IFUNC trampoline + // Handle IFUNC trampoline with symbol setPLTSymbol(BF, BF->getOneName()); return; } const Relocation *Rel = BC->getDynamicRelocationAt(TargetAddress); - if (!Rel || !Rel->Symbol) + if (!Rel) return; + MCSymbol *Symbol = Rel->Symbol; + if (!Symbol) { + if (!BC->isAArch64() || !Rel->Addend || !Rel->isIRelative()) + return; + + // IFUNC trampoline without symbol + BinaryFunction *TargetBF = BC->getBinaryFunctionAtAddress(Rel->Addend); + if (!TargetBF) { + errs() + << "BOLT-WARNING: Expected BF to be presented as IFUNC resolver at " + << Twine::utohexstr(Rel->Addend) << ", skipping\n"; + return; + } + + Symbol = TargetBF->getSymbol(); + } + ErrorOr Section = BC->getSectionForAddress(EntryAddress); assert(Section && "cannot get section for address"); if (!BF) - BF = BC->createBinaryFunction(Rel->Symbol->getName().str() + "@PLT", - *Section, EntryAddress, 0, EntrySize, + BF = BC->createBinaryFunction(Symbol->getName().str() + "@PLT", *Section, + EntryAddress, 0, EntrySize, Section->getAlignment()); else - BF->addAlternativeName(Rel->Symbol->getName().str() + "@PLT"); - setPLTSymbol(BF, Rel->Symbol->getName()); + BF->addAlternativeName(Symbol->getName().str() + "@PLT"); + setPLTSymbol(BF, Symbol->getName()); } void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) { @@ -1689,13 +1784,6 @@ void RewriteInstance::relocateEHFrameSection() { check_error(std::move(E), "failed to patch EH frame"); } -ArrayRef RewriteInstance::getLSDAData() { - return ArrayRef(LSDASection->getData(), - LSDASection->getContents().size()); -} - -uint64_t RewriteInstance::getLSDAAddress() { return LSDASection->getAddress(); } - Error RewriteInstance::readSpecialSections() { NamedRegionTimer T("readSpecialSections", "read special sections", TimerGroupName, TimerGroupDesc, opts::TimeRewrite); @@ -1734,7 +1822,6 @@ Error RewriteInstance::readSpecialSections() { HasTextRelocations = (bool)BC->getUniqueSectionByName(".rela.text"); HasSymbolTable = (bool)BC->getUniqueSectionByName(".symtab"); - LSDASection = BC->getUniqueSectionByName(".gcc_except_table"); EHFrameSection = BC->getUniqueSectionByName(".eh_frame"); BuildIDSection = BC->getUniqueSectionByName(".note.gnu.build-id"); @@ -2122,6 +2209,19 @@ void RewriteInstance::processDynamicRelocations() { } // The rest of dynamic relocations - DT_RELA. + // The static executable might have .rela.dyn secion and not have PT_DYNAMIC + if (!DynamicRelocationsSize && BC->IsStaticExecutable) { + ErrorOr DynamicRelSectionOrErr = + BC->getUniqueSectionByName(getRelaDynSectionName()); + if (DynamicRelSectionOrErr) { + DynamicRelocationsAddress = DynamicRelSectionOrErr->getAddress(); + DynamicRelocationsSize = DynamicRelSectionOrErr->getSize(); + const SectionRef &SectionRef = DynamicRelSectionOrErr->getSectionRef(); + DynamicRelativeRelocationsCount = std::distance( + SectionRef.relocation_begin(), SectionRef.relocation_end()); + } + } + if (DynamicRelocationsSize > 0) { ErrorOr DynamicRelSectionOrErr = BC->getSectionForAddress(*DynamicRelocationsAddress); @@ -3092,8 +3192,14 @@ void RewriteInstance::disassembleFunctions() { // Parse LSDA. if (Function.getLSDAAddress() != 0 && - !BC->getFragmentsToSkip().count(&Function)) - Function.parseLSDA(getLSDAData(), getLSDAAddress()); + !BC->getFragmentsToSkip().count(&Function)) { + ErrorOr LSDASection = + BC->getSectionForAddress(Function.getLSDAAddress()); + check_error(LSDASection.getError(), "failed to get LSDA section"); + ArrayRef LSDAData = ArrayRef( + LSDASection->getData(), LSDASection->getContents().size()); + Function.parseLSDA(LSDAData, LSDASection->getAddress()); + } } } @@ -3104,7 +3210,6 @@ void RewriteInstance::buildFunctionsCFG() { // Create annotation indices to allow lock-free execution BC->MIB->getOrCreateAnnotationIndex("JTIndexReg"); BC->MIB->getOrCreateAnnotationIndex("NOP"); - BC->MIB->getOrCreateAnnotationIndex("Size"); ParallelUtilities::WorkFuncWithAllocTy WorkFun = [&](BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId) { @@ -4467,7 +4572,7 @@ void RewriteInstance::updateELFSymbolTable( OutputAddress >= Function->getCodeSection()->getOutputAddress() && OutputAddress < (Function->getCodeSection()->getOutputAddress() + Function->getCodeSection()->getOutputSize()) && - "Cannot locate fragment containg secondary entrypoint"); + "Cannot locate fragment containing secondary entrypoint"); FF = Function->getLayout().fragment_begin(); } @@ -5118,7 +5223,13 @@ Error RewriteInstance::readELFDynamic(ELFObjectFile *File) { } break; case ELF::DT_FINI: - BC->FiniFunctionAddress = Dyn.getPtr(); + BC->FiniAddress = Dyn.getPtr(); + break; + case ELF::DT_FINI_ARRAY: + BC->FiniArrayAddress = Dyn.getPtr(); + break; + case ELF::DT_FINI_ARRAYSZ: + BC->FiniArraySize = Dyn.getPtr(); break; case ELF::DT_RELA: DynamicRelocationsAddress = Dyn.getPtr(); diff --git a/bolt/lib/RuntimeLibs/HugifyRuntimeLibrary.cpp b/bolt/lib/RuntimeLibs/HugifyRuntimeLibrary.cpp index a727ca911354cfae85e293195024dabcf699fc03..b6fc49d3f5d671b8a0205961f2aace7e39253418 100644 --- a/bolt/lib/RuntimeLibs/HugifyRuntimeLibrary.cpp +++ b/bolt/lib/RuntimeLibs/HugifyRuntimeLibrary.cpp @@ -71,8 +71,7 @@ void HugifyRuntimeLibrary::link(BinaryContext &BC, StringRef ToolPath, "We don't currently support linking multiple runtime libraries"); RuntimeStartAddress = Linker.lookupSymbol("__bolt_hugify_self").value_or(0); if (!RuntimeStartAddress) { - errs() << "BOLT-ERROR: instrumentation library does not define " - "__bolt_hugify_self: " + errs() << "BOLT-ERROR: hugify library does not define __bolt_hugify_self: " << LibPath << "\n"; exit(1); } diff --git a/bolt/lib/RuntimeLibs/InstrumentationRuntimeLibrary.cpp b/bolt/lib/RuntimeLibs/InstrumentationRuntimeLibrary.cpp index cc36406543f399543526e552bc6a0d06802625fc..cd1b975be7b90e636ca4d5d6877cb309994e1207 100644 --- a/bolt/lib/RuntimeLibs/InstrumentationRuntimeLibrary.cpp +++ b/bolt/lib/RuntimeLibs/InstrumentationRuntimeLibrary.cpp @@ -57,10 +57,11 @@ void InstrumentationRuntimeLibrary::adjustCommandLineOptions( "the input binary\n"; exit(1); } - if (!BC.FiniFunctionAddress && !BC.IsStaticExecutable) { - errs() << "BOLT-ERROR: input binary lacks DT_FINI entry in the dynamic " - "section but instrumentation currently relies on patching " - "DT_FINI to write the profile\n"; + + if (BC.IsStaticExecutable && !opts::InstrumentationSleepTime) { + errs() << "BOLT-ERROR: instrumentation of static binary currently does not " + "support profile output on binary finalization, so it " + "requires -instrumentation-sleep-time=N (N>0) usage\n"; exit(1); } @@ -89,13 +90,6 @@ void InstrumentationRuntimeLibrary::emitBinary(BinaryContext &BC, "__BOLT", "__counters", MachO::S_REGULAR, SectionKind::getData())); - if (BC.IsStaticExecutable && !opts::InstrumentationSleepTime) { - errs() << "BOLT-ERROR: instrumentation of static binary currently does not " - "support profile output on binary finalization, so it " - "requires -instrumentation-sleep-time=N (N>0) usage\n"; - exit(1); - } - Section->setAlignment(llvm::Align(BC.RegularPageSize)); Streamer.switchSection(Section); diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp index c2bc597950b859c3b15df5734ed119a531514a66..e19c070b081733c1faea19e509c6e3933fc05107 100644 --- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp +++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp @@ -320,7 +320,6 @@ public: default: return false; case RISCV::C_J: - case TargetOpcode::EH_LABEL: OpNum = 0; return true; case RISCV::AUIPC: diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp index 19680fa945dbc0336b50e8956757dbaa2dd90120..a1df5de26234029af7633a796b4b2fdcd56b28a0 100644 --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -160,7 +160,7 @@ cl::opt ProfileFormat( "format to dump profile output in aggregation mode, default is fdata"), cl::init(PF_Fdata), cl::values(clEnumValN(PF_Fdata, "fdata", "offset-based plaintext format"), - clEnumValN(PF_YAML, "yaml", "dense YAML reprensentation")), + clEnumValN(PF_YAML, "yaml", "dense YAML representation")), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltCategory)); cl::opt SplitEH("split-eh", cl::desc("split C++ exception handling code"), diff --git a/bolt/runtime/instr.cpp b/bolt/runtime/instr.cpp index cfd113e805c500c26e23de010a426dac6414c8f0..16e0bbd55f90b1ed373b472fc0d70039b27aba93 100644 --- a/bolt/runtime/instr.cpp +++ b/bolt/runtime/instr.cpp @@ -323,7 +323,7 @@ public: /// Basic member accessing interface. Here we pass the allocator explicitly to /// avoid storing a pointer to it as part of this table (remember there is one - /// hash for each indirect call site, so we wan't to minimize our footprint). + /// hash for each indirect call site, so we want to minimize our footprint). MapEntry &get(uint64_t Key, BumpPtrAllocator &Alloc) { if (!__bolt_instr_conservative) { TryLock L(M); @@ -1281,7 +1281,7 @@ void Graph::computeEdgeFrequencies(const uint64_t *Counters, /// Write to \p FD all of the edge profiles for function \p FuncDesc. Uses /// \p Alloc to allocate helper dynamic structures used to compute profile for -/// edges that we do not explictly instrument. +/// edges that we do not explicitly instrument. const uint8_t *writeFunctionProfile(int FD, ProfileWriterContext &Ctx, const uint8_t *FuncDesc, BumpPtrAllocator &Alloc) { @@ -1514,7 +1514,7 @@ extern "C" void __bolt_instr_clear_counters() { /// * Program execution ended, finalization methods are running and BOLT /// hooked into FINI from your binary dynamic section; /// * You used the sleep timer option and during initialization we forked -/// a separete process that will call this function periodically; +/// a separate process that will call this function periodically; /// * BOLT prints this function address so you can attach a debugger and /// call this function directly to get your profile written to disk /// on demand. diff --git a/bolt/test/AArch64/Inputs/iplt.ld b/bolt/test/AArch64/Inputs/iplt.ld new file mode 100644 index 0000000000000000000000000000000000000000..1e54a249b2182e0ae72a4716e05bc4351bf71329 --- /dev/null +++ b/bolt/test/AArch64/Inputs/iplt.ld @@ -0,0 +1,3 @@ +SECTIONS { + .plt : ALIGN(16) { *(.plt) *(.iplt) } +} diff --git a/bolt/test/AArch64/go_dwarf.test b/bolt/test/AArch64/go_dwarf.test index 7d2765a1c7fb150acfa4ff61553bac70b92c9fbc..f5c4a4a9f478641c836e3baac361e8d7d3582f77 100644 --- a/bolt/test/AArch64/go_dwarf.test +++ b/bolt/test/AArch64/go_dwarf.test @@ -23,7 +23,7 @@ CHECKORIG-NEXT: DW_AT_external (true) CHECKORIG-NEXT: DW_AT_name ("main") CHECKORIG-NEXT: DW_AT_decl_file CHECKORIG-NEXT: DW_AT_decl_line (1) -CHECKORIG-NEXT: DW_AT_decl_column (0x05) +CHECKORIG-NEXT: DW_AT_decl_column (5) CHECKORIG-NEXT: DW_AT_type CHECKORIG-NEXT: DW_AT_low_pc (0x0000000000000660) CHECKORIG-NEXT: DW_AT_high_pc (0x0000000000000684) @@ -47,7 +47,7 @@ CHECK-NEXT: DW_AT_external (true) CHECK-NEXT: DW_AT_name ("main") CHECK-NEXT: DW_AT_decl_file CHECK-NEXT: DW_AT_decl_line (1) -CHECK-NEXT: DW_AT_decl_column (0x05) +CHECK-NEXT: DW_AT_decl_column (5) CHECK-NEXT: DW_AT_type CHECK-NEXT: DW_AT_low_pc (0x0000000000000660) CHECK-NEXT: DW_AT_high_pc (0x0000000000000024) diff --git a/bolt/test/AArch64/got-ld64-relaxation.test b/bolt/test/AArch64/got-ld64-relaxation.test index 74d876b647e3473f547c8ad10d00bc768c8bae0a..ec6042a2ca8264f89e8316c142d88184de59ed4d 100644 --- a/bolt/test/AArch64/got-ld64-relaxation.test +++ b/bolt/test/AArch64/got-ld64-relaxation.test @@ -1,8 +1,8 @@ // This test checks that ADR+LDR instruction sequence relaxed by the linker -// to the ADR+ADD sequence is properly reconized and handled by bolt +// to the ADR+ADD sequence is properly recognized and handled by bolt // RUN: yaml2obj %p/Inputs/got-ld64-relaxation.yaml &> %t.exe -// RUN: llvm-bolt %t.exe -o /dev/null --print-fix-relaxations \ +// RUN: llvm-bolt %t.exe -o %t.null --print-fix-relaxations \ // RUN: --print-only=main | FileCheck %s // CHECK: adrp x0, foo diff --git a/bolt/test/AArch64/hook-fini.s b/bolt/test/AArch64/hook-fini.s new file mode 100644 index 0000000000000000000000000000000000000000..4f321d463ef322b541768f2f839e7045ac3d1a83 --- /dev/null +++ b/bolt/test/AArch64/hook-fini.s @@ -0,0 +1,103 @@ +## Test the different ways of hooking the fini function for instrumentation (via +## DT_FINI and via DT_FINI_ARRAY). We test the latter for both PIE and non-PIE +## binaries because of the different ways of handling relocations (static or +## dynamic). +## All tests perform the following steps: +## - Compile and link for the case to be tested +## - Some sanity-checks on the dynamic section and relocations in the binary to +## verify it has the shape we want for testing: +## - DT_FINI or DT_FINI_ARRAY in dynamic section +## - No relative relocations for non-PIE +## - Instrument +## - Verify generated binary +# REQUIRES: system-linux,bolt-runtime,target=aarch64{{.*}} + +# RUN: %clang %cflags -pie %s -Wl,-q -o %t.exe +# RUN: llvm-readelf -d %t.exe | FileCheck --check-prefix=DYN-FINI %s +# RUN: llvm-readelf -r %t.exe | FileCheck --check-prefix=RELOC-PIE %s +# RUN: llvm-bolt %t.exe -o %t --instrument +# RUN: llvm-readelf -drs %t | FileCheck --check-prefix=CHECK-FINI %s + +# RUN: %clang %cflags -pie %s -Wl,-q,-fini=0 -o %t-no-fini.exe +# RUN: llvm-readelf -d %t-no-fini.exe | FileCheck --check-prefix=DYN-NO-FINI %s +# RUN: llvm-readelf -r %t-no-fini.exe | FileCheck --check-prefix=RELOC-PIE %s +# RUN: llvm-bolt %t-no-fini.exe -o %t-no-fini --instrument +# RUN: llvm-readelf -drs %t-no-fini | FileCheck --check-prefix=CHECK-NO-FINI %s +# RUN: llvm-readelf -ds -x .fini_array %t-no-fini | FileCheck --check-prefix=CHECK-NO-FINI-RELOC %s + +## Create a dummy shared library to link against to force creation of the dynamic section. +# RUN: %clang %cflags %p/../Inputs/stub.c -fPIC -shared -o %t-stub.so +# RUN: %clang %cflags %s -no-pie -Wl,-q,-fini=0 %t-stub.so -o %t-no-pie-no-fini.exe +# RUN: llvm-readelf -r %t-no-pie-no-fini.exe | FileCheck --check-prefix=RELOC-NO-PIE %s +# RUN: llvm-bolt %t-no-pie-no-fini.exe -o %t-no-pie-no-fini --instrument +# RUN: llvm-readelf -ds -x .fini_array %t-no-pie-no-fini | FileCheck --check-prefix=CHECK-NO-PIE-NO-FINI %s + +## With fini: dynamic section should contain DT_FINI +# DYN-FINI: (FINI) + +## Without fini: dynamic section should only contain DT_FINI_ARRAY +# DYN-NO-FINI-NOT: (FINI) +# DYN-NO-FINI: (FINI_ARRAY) +# DYN-NO-FINI: (FINI_ARRAYSZ) + +## With PIE: binary should have relative relocations +# RELOC-PIE: R_AARCH64_RELATIVE + +## Without PIE: binary should not have relative relocations +# RELOC-NO-PIE-NOT: R_AARCH64_RELATIVE + +## Check that DT_FINI is set to __bolt_runtime_fini +# CHECK-FINI: Dynamic section at offset {{.*}} contains {{.*}} entries: +# CHECK-FINI-DAG: (FINI) 0x[[FINI:[[:xdigit:]]+]] +# CHECK-FINI-DAG: (FINI_ARRAY) 0x[[FINI_ARRAY:[[:xdigit:]]+]] +## Check that the dynamic relocation at .fini_array was not patched +# CHECK-FINI: Relocation section '.rela.dyn' at offset {{.*}} contains {{.*}} entries +# CHECK-FINI-NOT: {{0+}}[[FINI_ARRAY]] {{.*}} R_AARCH64_RELATIVE [[FINI]] +# CHECK-FINI: Symbol table '.symtab' contains {{.*}} entries: +# CHECK-FINI: {{0+}}[[FINI]] {{.*}} __bolt_runtime_fini + +## Check that DT_FINI_ARRAY has a dynamic relocation for __bolt_runtime_fini +# CHECK-NO-FINI: Dynamic section at offset {{.*}} contains {{.*}} entries: +# CHECK-NO-FINI-NOT: (FINI) +# CHECK-NO-FINI: (FINI_ARRAY) 0x[[FINI_ARRAY:[[:xdigit:]]+]] +# CHECK-NO-FINI: Relocation section '.rela.dyn' at offset {{.*}} contains {{.*}} entries +# CHECK-NO-FINI: {{0+}}[[FINI_ARRAY]] {{.*}} R_AARCH64_RELATIVE [[FINI_ADDR:[[:xdigit:]]+]] +# CHECK-NO-FINI: Symbol table '.symtab' contains {{.*}} entries: +# CHECK-NO-FINI: {{0+}}[[FINI_ADDR]] {{.*}} __bolt_runtime_fini + +## Check that the static relocation in .fini_array is patched even for PIE +# CHECK-NO-FINI-RELOC: Dynamic section at offset {{.*}} contains {{.*}} entries: +# CHECK-NO-FINI-RELOC: (FINI_ARRAY) 0x[[FINI_ARRAY:[[:xdigit:]]+]] +# CHECK-NO-FINI-RELOC: Symbol table '.symtab' contains {{.*}} entries: +## Read bytes separately so we can reverse them later +# CHECK-NO-FINI-RELOC: {{0+}}[[FINI_ADDR_B0:[[:xdigit:]]{2}]][[FINI_ADDR_B1:[[:xdigit:]]{2}]][[FINI_ADDR_B2:[[:xdigit:]]{2}]][[FINI_ADDR_B3:[[:xdigit:]]{2}]] {{.*}} __bolt_runtime_fini +# CHECK-NO-FINI-RELOC: Hex dump of section '.fini_array': +# CHECK-NO-FINI-RELOC: 0x{{0+}}[[FINI_ARRAY]] [[FINI_ADDR_B3]][[FINI_ADDR_B2]][[FINI_ADDR_B1]][[FINI_ADDR_B0]] 00000000 + +## Check that DT_FINI_ARRAY has static relocation applied for __bolt_runtime_fini +# CHECK-NO-PIE-NO-FINI: Dynamic section at offset {{.*}} contains {{.*}} entries: +# CHECK-NO-PIE-NO-FINI-NOT: (FINI) +# CHECK-NO-PIE-NO-FINI: (FINI_ARRAY) 0x[[FINI_ARRAY:[a-f0-9]+]] +# CHECK-NO-PIE-NO-FINI: Symbol table '.symtab' contains {{.*}} entries: +## Read address bytes separately so we can reverse them later +# CHECK-NO-PIE-NO-FINI: {{0+}}[[FINI_ADDR_B0:[[:xdigit:]]{2}]][[FINI_ADDR_B1:[[:xdigit:]]{2}]][[FINI_ADDR_B2:[[:xdigit:]]{2}]][[FINI_ADDR_B3:[[:xdigit:]]{2}]] {{.*}} __bolt_runtime_fini +# CHECK-NO-PIE-NO-FINI: Hex dump of section '.fini_array': +# CHECK-NO-PIE-NO-FINI: 0x{{0+}}[[FINI_ARRAY]] [[FINI_ADDR_B3]][[FINI_ADDR_B2]][[FINI_ADDR_B1]][[FINI_ADDR_B0]] 00000000 + + .globl _start + .type _start, %function +_start: + # Dummy relocation to force relocation mode. + .reloc 0, R_AARCH64_NONE + ret +.size _start, .-_start + + .globl _fini + .type _fini, %function +_fini: + ret + .size _fini, .-_fini + + .section .fini_array,"aw" + .align 3 + .dword _fini diff --git a/bolt/test/AArch64/ifunc.c b/bolt/test/AArch64/ifunc.c new file mode 100644 index 0000000000000000000000000000000000000000..8edb913ee70d5c0f70cdbe7c4eb19eb2c794d945 --- /dev/null +++ b/bolt/test/AArch64/ifunc.c @@ -0,0 +1,60 @@ +// This test checks that IFUNC trampoline is properly recognised by BOLT + +// With -O0 indirect call is performed on IPLT trampoline. IPLT trampoline +// has IFUNC symbol. +// RUN: %clang %cflags -nostdlib -O0 -no-pie %s -fuse-ld=lld \ +// RUN: -o %t.O0.exe -Wl,-q +// RUN: llvm-bolt %t.O0.exe -o %t.O0.bolt.exe \ +// RUN: --print-disasm --print-only=_start | \ +// RUN: FileCheck --check-prefix=O0_CHECK %s +// RUN: llvm-readelf -aW %t.O0.bolt.exe | \ +// RUN: FileCheck --check-prefix=REL_CHECK %s + +// Non-pie static executable doesn't generate PT_DYNAMIC, check relocation +// is readed successfully and IPLT trampoline has been identified by bolt. +// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -no-pie \ +// RUN: -o %t.O3_nopie.exe -Wl,-q +// RUN: llvm-readelf -l %t.O3_nopie.exe | \ +// RUN: FileCheck --check-prefix=NON_DYN_CHECK %s +// RUN: llvm-bolt %t.O3_nopie.exe -o %t.O3_nopie.bolt.exe \ +// RUN: --print-disasm --print-only=_start | \ +// RUN: FileCheck --check-prefix=O3_CHECK %s +// RUN: llvm-readelf -aW %t.O3_nopie.bolt.exe | \ +// RUN: FileCheck --check-prefix=REL_CHECK %s + +// With -O3 direct call is performed on IPLT trampoline. IPLT trampoline +// doesn't have associated symbol. The ifunc symbol has the same address as +// IFUNC resolver function. +// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -fPIC -pie \ +// RUN: -o %t.O3_pie.exe -Wl,-q +// RUN: llvm-bolt %t.O3_pie.exe -o %t.O3_pie.bolt.exe \ +// RUN: --print-disasm --print-only=_start | \ +// RUN: FileCheck --check-prefix=O3_CHECK %s +// RUN: llvm-readelf -aW %t.O3_pie.bolt.exe | \ +// RUN: FileCheck --check-prefix=REL_CHECK %s + +// Check that IPLT trampoline located in .plt section are normally handled by +// BOLT. The gnu-ld linker doesn't use separate .iplt section. +// RUN: %clang %cflags -nostdlib -O3 %s -fuse-ld=lld -fPIC -pie \ +// RUN: -T %p/Inputs/iplt.ld -o %t.iplt_O3_pie.exe -Wl,-q +// RUN: llvm-bolt %t.iplt_O3_pie.exe -o %t.iplt_O3_pie.bolt.exe \ +// RUN: --print-disasm --print-only=_start | \ +// RUN: FileCheck --check-prefix=O3_CHECK %s +// RUN: llvm-readelf -aW %t.iplt_O3_pie.bolt.exe | \ +// RUN: FileCheck --check-prefix=REL_CHECK %s + +// NON_DYN_CHECK-NOT: DYNAMIC + +// O0_CHECK: adr x{{[0-9]+}}, ifoo +// O3_CHECK: b "{{resolver_foo|ifoo}}{{.*}}@PLT" + +// REL_CHECK: R_AARCH64_IRELATIVE [[#%x,REL_SYMB_ADDR:]] +// REL_CHECK: [[#REL_SYMB_ADDR]] {{.*}} FUNC {{.*}} resolver_foo + +static void foo() {} + +static void *resolver_foo(void) { return foo; } + +__attribute__((ifunc("resolver_foo"))) void ifoo(); + +void _start() { ifoo(); } diff --git a/bolt/test/AArch64/patch-entries.s b/bolt/test/AArch64/patch-entries.s new file mode 100644 index 0000000000000000000000000000000000000000..cf6f72a0b80d49c0c1eaf4a50155c714d0fc04ed --- /dev/null +++ b/bolt/test/AArch64/patch-entries.s @@ -0,0 +1,36 @@ +# This test checks patch entries functionality + +# REQUIRES: system-linux + +# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \ +# RUN: %s -o %t.o +# RUN: %clang %cflags -pie %t.o -o %t.exe -nostdlib -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --use-old-text=0 --lite=0 --skip-funcs=_start +# RUN: llvm-objdump -dz %t.bolt | FileCheck %s + +# CHECK: : +# CHECK-NEXT: adrp x16, 0x[[#%x,ADRP:]] +# CHECK-NEXT: add x16, x16, #0x[[#%x,ADD:]] +# CHECK-NEXT: br x16 + +# CHECK: [[#ADRP + ADD]] : +# CHECK-NEXT: [[#ADRP + ADD]]: {{.*}} ret + +.text +.balign 4 +.global pathedEntries +.type pathedEntries, %function +pathedEntries: + .rept 32 + nop + .endr + ret +.size pathedEntries, .-pathedEntries + +.global _start +.type _start, %function +_start: + bl pathedEntries + .inst 0xdeadbeef + ret +.size _start, .-_start diff --git a/bolt/test/AArch64/r_aarch64_prelxx.s b/bolt/test/AArch64/r_aarch64_prelxx.s index 444dee72b7c04eb0172c85b01979592d954ce840..73bf8387d3634518278b12ee168c0ce44eba8f92 100644 --- a/bolt/test/AArch64/r_aarch64_prelxx.s +++ b/bolt/test/AArch64/r_aarch64_prelxx.s @@ -12,7 +12,7 @@ // CHECKPREL-NEXT: R_AARCH64_PREL32 {{.*}} _start + 4 // CHECKPREL-NEXT: R_AARCH64_PREL64 {{.*}} _start + 8 -// RUN: llvm-bolt %t.exe -o %t.bolt +// RUN: llvm-bolt %t.exe -o %t.bolt --strict // RUN: llvm-objdump -D %t.bolt | FileCheck %s --check-prefix=CHECKPREL32 // CHECKPREL32: [[#%x,DATATABLEADDR:]] : diff --git a/bolt/test/AArch64/reloc-call26.s b/bolt/test/AArch64/reloc-call26.s deleted file mode 100644 index 42e4f7f2b43786fcdd72ba4a614e93562b380fe8..0000000000000000000000000000000000000000 --- a/bolt/test/AArch64/reloc-call26.s +++ /dev/null @@ -1,51 +0,0 @@ -## This test checks processing of R_AARCH64_CALL26 relocation -## when option `--funcs` is enabled - -## We want to test on relocations against functions with both higher -## and lower addresses. The '--force-patch' option is used to prevent -## the functions func1 and func2 from being optimized, so that their -## addresses can remain unchanged. Therefore, the relocations can be -## updated via encodeValueAArch64 and the address order in the output -## binary is func1 < _start < func2. - -# REQUIRES: system-linux - -# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \ -# RUN: %s -o %t.o -# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -# RUN: llvm-bolt %t.exe -o %t.bolt --funcs=func1,func2 \ -# RUN: --force-patch 2>&1 | FileCheck %s -check-prefix=CHECK-BOLT -# RUN: llvm-objdump -d --disassemble-symbols='_start' %t.bolt | \ -# RUN: FileCheck %s -# RUN: llvm-nm --numeric-sort --extern-only %t.bolt | FileCheck \ -# RUN: %s -check-prefix=CHECK-FUNC-ORDER - -# CHECK-BOLT: BOLT-WARNING: failed to patch entries in func1. The function will not be optimized. -# CHECK-BOLT: BOLT-WARNING: failed to patch entries in func2. The function will not be optimized. -# CHECK: {{.*}} bl {{.*}} -# CHECK: {{.*}} bl {{.*}} - -# CHECK-FUNC-ORDER: {{.*}} func1 -# CHECK-FUNC-ORDER-NEXT: {{.*}} _start -# CHECK-FUNC-ORDER-NEXT: {{.*}} func2 - - .text - .align 4 - .global func1 - .type func1, %function -func1: - ret - .size func1, .-func1 - .global _start - .type _start, %function -_start: - bl func1 - bl func2 - mov w8, #93 - svc #0 - .size _start, .-_start - .global func2 - .type func2, %function -func2: - ret - .size func2, .-func2 diff --git a/bolt/test/AArch64/skip-got-rel.test b/bolt/test/AArch64/skip-got-rel.test index 3c36df00b1c82f57b8e299f10e010022c7c08f2b..eaa6ec4b78d744de34cdb4257bc4adb56f2c24bd 100644 --- a/bolt/test/AArch64/skip-got-rel.test +++ b/bolt/test/AArch64/skip-got-rel.test @@ -3,6 +3,6 @@ // normally. RUN: yaml2obj %p/Inputs/skip-got-rel.yaml &> %t.exe -RUN: llvm-bolt %t.exe -o /dev/null --print-cfg --print-only=_start | FileCheck %s +RUN: llvm-bolt %t.exe -o %t.null --print-cfg --print-only=_start | FileCheck %s CHECK: adr x0, foo2 diff --git a/bolt/test/AArch64/unmarked-data.test b/bolt/test/AArch64/unmarked-data.test index 4045e711bb00decb018671106944a5e5432955cd..7a62994bb5c382e7866b10f70f84d44d2485f4e3 100644 --- a/bolt/test/AArch64/unmarked-data.test +++ b/bolt/test/AArch64/unmarked-data.test @@ -9,7 +9,7 @@ // YAML is based in the following assembly: - .equ val, 0x070b0f03 // we use constant that is not a valid instruction so that it can't be silently dissassembled + .equ val, 0x070b0f03 // we use constant that is not a valid instruction so that it can't be silently disassembled .text first: diff --git a/bolt/test/Inputs/lsda.ldscript b/bolt/test/Inputs/lsda.ldscript new file mode 100644 index 0000000000000000000000000000000000000000..aa608ecd97e8c5cdda5fc404eae2a3fa9ddf456c --- /dev/null +++ b/bolt/test/Inputs/lsda.ldscript @@ -0,0 +1,7 @@ +SECTIONS { + .text : { *(.text*) } + .gcc_except_table.main : { *(.gcc_except_table*) } + . = 0x20000; + .eh_frame : { *(.eh_frame) } + . = 0x80000; +} diff --git a/bolt/test/RISCV/branch-no-secondary-entry.s b/bolt/test/RISCV/branch-no-secondary-entry.s index bf8191f25744c9049d0dbf19ad8e499dd31fc031..f83ca515791561b036c1d8478de89b8d2d8c88bb 100644 --- a/bolt/test/RISCV/branch-no-secondary-entry.s +++ b/bolt/test/RISCV/branch-no-secondary-entry.s @@ -1,7 +1,7 @@ /// Test that no secondary entry points are created for basic block labels used /// by branches. // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt -print-cfg -o /dev/null %t 2>&1 | FileCheck %s +// RUN: llvm-bolt -print-cfg -o %t.null %t 2>&1 | FileCheck %s // CHECK: Binary Function "_start" after building cfg { // CHECK: IsMultiEntry: 0 diff --git a/bolt/test/RISCV/call-annotations.s b/bolt/test/RISCV/call-annotations.s index 477c4693cd6dd72f970d9b175df02dfb0610b80b..1a8ac1571f0be240999aa78c25479cd45cfe95d5 100644 --- a/bolt/test/RISCV/call-annotations.s +++ b/bolt/test/RISCV/call-annotations.s @@ -4,7 +4,7 @@ // RUN: llvm-mc -triple riscv64 -mattr=+c -filetype obj -o %t.o %s // RUN: ld.lld --emit-relocs -o %t %t.o // RUN: llvm-bolt --enable-bat --print-cfg --print-fix-riscv-calls \ -// RUN: -o /dev/null %t | FileCheck %s +// RUN: -o %t.null %t | FileCheck %s .text .option norvc diff --git a/bolt/test/RISCV/load-store.s b/bolt/test/RISCV/load-store.s index 5a9785571c808118b87109c87e4facfa88bafaf7..052c5a3879a449f809cd8633221739c5a82f4a05 100644 --- a/bolt/test/RISCV/load-store.s +++ b/bolt/test/RISCV/load-store.s @@ -1,6 +1,6 @@ // RUN: %clang %cflags -o %t %s // RUN: link_fdata --no-lbr %s %t %t.fdata -// RUN: llvm-bolt %t -o /dev/null --data=%t.fdata --dyno-stats | FileCheck %s +// RUN: llvm-bolt %t -o %t.null --data=%t.fdata --dyno-stats | FileCheck %s // CHECK: BOLT-INFO: program-wide dynostats after all optimizations before SCTC and FOP (no change): // CHECK: 3000 : executed instructions diff --git a/bolt/test/RISCV/reloc-abs.s b/bolt/test/RISCV/reloc-abs.s index 5b728f092b3c9f587d063db38f85cda7605b0866..956e0410ece42ab0d4b3c9c32c92c94d62853822 100644 --- a/bolt/test/RISCV/reloc-abs.s +++ b/bolt/test/RISCV/reloc-abs.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -Wl,--defsym='__global_pointer$'=0x2800 -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .data diff --git a/bolt/test/RISCV/reloc-bb-split.s b/bolt/test/RISCV/reloc-bb-split.s index 5995562cf130b07083c967d4797a141a6e9472e3..450c763944529499aed4086f8ee13d0a6418a34c 100644 --- a/bolt/test/RISCV/reloc-bb-split.s +++ b/bolt/test/RISCV/reloc-bb-split.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .data diff --git a/bolt/test/RISCV/reloc-branch.s b/bolt/test/RISCV/reloc-branch.s index 43991149af8c809de6c5d3b9db5686829668853a..6a8b5a28e19d92059aecc9af95cca67797b2c18b 100644 --- a/bolt/test/RISCV/reloc-branch.s +++ b/bolt/test/RISCV/reloc-branch.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .text diff --git a/bolt/test/RISCV/reloc-call.s b/bolt/test/RISCV/reloc-call.s index 8c7f5498c2925594fdba65562b964619736d9951..37cfd993b95d2b9b8f88224eff6b70b42d60fc44 100644 --- a/bolt/test/RISCV/reloc-call.s +++ b/bolt/test/RISCV/reloc-call.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-fix-riscv-calls --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-fix-riscv-calls --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .text diff --git a/bolt/test/RISCV/reloc-got.s b/bolt/test/RISCV/reloc-got.s index dcf9d0ea3ffbf23b999f1dfb2013a448a42e9739..e7e85fddfb1cb9ef20cd50f2052133a446732a14 100644 --- a/bolt/test/RISCV/reloc-got.s +++ b/bolt/test/RISCV/reloc-got.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .data diff --git a/bolt/test/RISCV/reloc-jal.s b/bolt/test/RISCV/reloc-jal.s index 427e8f230d89a4e7787a27104e9fba0e0f115e65..ce54265fac05e900f037ca84bc278118e7bb5b7e 100644 --- a/bolt/test/RISCV/reloc-jal.s +++ b/bolt/test/RISCV/reloc-jal.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .text diff --git a/bolt/test/RISCV/reloc-lohi.s b/bolt/test/RISCV/reloc-lohi.s index a97a1454eee8620fe212eba536a7393541ed3803..c882df0be61d968a67fc95d421db88bc8e612620 100644 --- a/bolt/test/RISCV/reloc-lohi.s +++ b/bolt/test/RISCV/reloc-lohi.s @@ -1,6 +1,6 @@ // RUN: llvm-mc -triple riscv64 -filetype=obj -o %t.o %s // RUN: ld.lld -q -o %t %t.o -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .data diff --git a/bolt/test/RISCV/reloc-pcrel.s b/bolt/test/RISCV/reloc-pcrel.s index 3ad3015a0a57fadaa65fa8e2902b63a76f6e9f8d..c7e41747b4db95bea6424b6d68e1b1550828cb23 100644 --- a/bolt/test/RISCV/reloc-pcrel.s +++ b/bolt/test/RISCV/reloc-pcrel.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .data diff --git a/bolt/test/RISCV/reloc-rvc-branch.s b/bolt/test/RISCV/reloc-rvc-branch.s index 34221c167d865170d3c72005a37c7ed16d0ee77e..87c16d8023faf5118db5d9a59c57be72bb637e75 100644 --- a/bolt/test/RISCV/reloc-rvc-branch.s +++ b/bolt/test/RISCV/reloc-rvc-branch.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .text diff --git a/bolt/test/RISCV/reloc-rvc-jump.s b/bolt/test/RISCV/reloc-rvc-jump.s index 21b10bab09c7cdf82996a6ad3923417f1d32939b..2aae9c3c8e8e57885452e1d1cbc5808f7625fb79 100644 --- a/bolt/test/RISCV/reloc-rvc-jump.s +++ b/bolt/test/RISCV/reloc-rvc-jump.s @@ -1,5 +1,5 @@ // RUN: %clang %cflags -o %t %s -// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=_start -o %t.null %t \ // RUN: | FileCheck %s .text diff --git a/bolt/test/RISCV/reloc-tls.s b/bolt/test/RISCV/reloc-tls.s index 44195818277f5468489ff36ed09b488e9532bca4..a4c7bd06de907cefeb108aec2fd3262fe998f727 100644 --- a/bolt/test/RISCV/reloc-tls.s +++ b/bolt/test/RISCV/reloc-tls.s @@ -1,6 +1,6 @@ // RUN: llvm-mc -triple riscv64 -filetype obj -o %t.o %s // RUN: ld.lld --emit-relocs -o %t %t.o -// RUN: llvm-bolt --print-cfg --print-only=tls_le,tls_ie -o /dev/null %t \ +// RUN: llvm-bolt --print-cfg --print-only=tls_le,tls_ie -o %t.null %t \ // RUN: | FileCheck %s // CHECK-LABEL: Binary Function "tls_le{{.*}}" after building cfg { diff --git a/bolt/test/X86/Inputs/blarge_profile_stale.yaml b/bolt/test/X86/Inputs/blarge_profile_stale.yaml index f5abaed3da39412588473a90779761c9d245a4dd..43b75c99656f1884a4af03c0f07d6d0b7bed2be1 100644 --- a/bolt/test/X86/Inputs/blarge_profile_stale.yaml +++ b/bolt/test/X86/Inputs/blarge_profile_stale.yaml @@ -10,33 +10,33 @@ header: functions: - name: SolveCubic fid: 6 - hash: 0xC6E9098E973BBE19 + hash: 0x0000000000000000 exec: 151 nblocks: 18 blocks: - bid: 0 insns: 43 - hash: 0xed4db287e71c0000 + hash: 0x4600940a609c0000 exec: 151 succ: [ { bid: 1, cnt: 151, mis: 2 }, { bid: 7, cnt: 0 } ] - bid: 1 insns: 7 - hash: 0x39330000e4560088 + hash: 0x167a1f084f130088 succ: [ { bid: 13, cnt: 151 }, { bid: 2, cnt: 0 } ] - bid: 13 insns: 26 - hash: 0xa9700000fe202a7 + hash: 0xa8d50000f81902a7 succ: [ { bid: 3, cnt: 89 }, { bid: 2, cnt: 10 } ] - bid: 3 insns: 9 - hash: 0x62391dad18a700a0 + hash: 0xc516000073dc00a0 succ: [ { bid: 5, cnt: 151 } ] - bid: 5 insns: 9 - hash: 0x4d906d19ecec0111 + hash: 0x6446e1ea500111 - name: usqrt fid: 7 - hash: 0x8B62B1F9AD81EA35 + hash: 0x0000000000000000 exec: 20 nblocks: 6 blocks: @@ -47,10 +47,10 @@ functions: succ: [ { bid: 1, cnt: 0 } ] - bid: 1 insns: 9 - hash: 0x27e43a5e10cd0010 + hash: 0xd70d7a64320e0010 succ: [ { bid: 3, cnt: 320, mis: 171 }, { bid: 2, cnt: 0 } ] - bid: 3 insns: 2 - hash: 0x4db935b6471e0039 + hash: 0x5c06705524800039 succ: [ { bid: 1, cnt: 300, mis: 33 }, { bid: 4, cnt: 20 } ] ... diff --git a/bolt/test/X86/Inputs/dwarf5-cu-no-debug-addr-helper.s b/bolt/test/X86/Inputs/dwarf5-cu-no-debug-addr-helper.s index 1d1f78c1681e67e8051300d90e2643206e685217..0d669e62d4113ccf27e525906c430af0ce255845 100644 --- a/bolt/test/X86/Inputs/dwarf5-cu-no-debug-addr-helper.s +++ b/bolt/test/X86/Inputs/dwarf5-cu-no-debug-addr-helper.s @@ -4,7 +4,7 @@ # int i; # } Foo; -# Manually modifed to remove DW_AT_addr_base and DW_AT_location. +# Manually modified to remove DW_AT_addr_base and DW_AT_location. .text .file "helper.cpp" @@ -104,7 +104,7 @@ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base .long .Lline_table_start0 # DW_AT_stmt_list .byte 2 # DW_AT_comp_dir - #.long .Laddr_table_base0 # DW_AT_addr_base mannually removed + #.long .Laddr_table_base0 # DW_AT_addr_base manually removed .byte 2 # Abbrev [2] 0x1e:0xc DW_TAG_variable .byte 3 # DW_AT_name .long 35 # DW_AT_type diff --git a/bolt/test/X86/Inputs/dwarf5-df-output-dir-same-name-helper.s b/bolt/test/X86/Inputs/dwarf5-df-output-dir-same-name-helper.s new file mode 100644 index 0000000000000000000000000000000000000000..6ff18a192b8e21e6f2abe9ea02b9495e269ead91 --- /dev/null +++ b/bolt/test/X86/Inputs/dwarf5-df-output-dir-same-name-helper.s @@ -0,0 +1,225 @@ +# clang++ helper.cpp -g2 -gsplit-dwarf -S -fdebug-compilation-dir=. +# Modified assembly so that DW_AT_dwo_name has a partial path. +# int foo() { return 0; } + .text + .file "helper.cpp" + .globl _Z3foov # -- Begin function _Z3foov + .p2align 4, 0x90 + .type _Z3foov,@function +_Z3foov: # @_Z3foov +.Lfunc_begin0: + .file 0 "." "helper.cpp" md5 0xde8d315e6d1f74ad53575cef6507e770 + .loc 0 1 0 # helper.cpp:1:0 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp +.Ltmp0: + .loc 0 1 13 prologue_end # helper.cpp:1:13 + xorl %eax, %eax + .loc 0 1 13 epilogue_begin is_stmt 0 # helper.cpp:1:13 + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Ltmp1: +.Lfunc_end0: + .size _Z3foov, .Lfunc_end0-_Z3foov + .cfi_endproc + # -- End function + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 74 # DW_TAG_skeleton_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .ascii "\264B" # DW_AT_GNU_pubnames + .byte 25 # DW_FORM_flag_present + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 4 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .quad -1360665163647638292 + .byte 1 # Abbrev [1] 0x14:0x14 DW_TAG_skeleton_unit + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .byte 0 # DW_AT_comp_dir + # DW_AT_GNU_pubnames + .byte 1 # DW_AT_dwo_name + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 12 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Lskel_string0: + .asciz "." # string offset=0 +.Lskel_string1: + .asciz "objects/o2/split.dwo" # string offset=2 <-- Manually modified. + .section .debug_str_offsets,"",@progbits + .long .Lskel_string0 + .long .Lskel_string1 + .section .debug_str_offsets.dwo,"e",@progbits + .long 28 # Length of String Offsets Set + .short 5 + .short 0 + .section .debug_str.dwo,"eMS",@progbits,1 +.Linfo_string0: + .asciz "_Z3foov" # string offset=0 +.Linfo_string1: + .asciz "foo" # string offset=8 +.Linfo_string2: + .asciz "int" # string offset=12 +.Linfo_string3: + .asciz "clang version 18.0.0" # string offset=16 +.Linfo_string4: + .asciz "helper.cpp" # string offset=37 +.Linfo_string5: + .asciz "objects/o2/split.dwo" # string offset=48 <-- Manually modified. + .section .debug_str_offsets.dwo,"e",@progbits + .long 0 + .long 8 + .long 12 + .long 16 + .long 37 + .long 48 + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad -1360665163647638292 + .byte 1 # Abbrev [1] 0x14:0x1b DW_TAG_compile_unit + .byte 3 # DW_AT_producer + .short 33 # DW_AT_language + .byte 4 # DW_AT_name + .byte 5 # DW_AT_dwo_name + .byte 2 # Abbrev [2] 0x1a:0x10 DW_TAG_subprogram + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 86 + .byte 0 # DW_AT_linkage_name + .byte 1 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 42 # DW_AT_type + # DW_AT_external + .byte 3 # Abbrev [3] 0x2a:0x4 DW_TAG_base_type + .byte 2 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end0: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 110 # DW_AT_linkage_name + .byte 37 # DW_FORM_strx1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .Lfunc_begin0 +.Ldebug_addr_end0: + .section .debug_gnu_pubnames,"",@progbits + .long .LpubNames_end0-.LpubNames_start0 # Length of Public Names Info +.LpubNames_start0: + .short 2 # DWARF Version + .long .Lcu_begin0 # Offset of Compilation Unit Info + .long 40 # Compilation Unit Length + .long 26 # DIE offset + .byte 48 # Attributes: FUNCTION, EXTERNAL + .asciz "foo" # External Name + .long 0 # End Mark +.LpubNames_end0: + .section .debug_gnu_pubtypes,"",@progbits + .long .LpubTypes_end0-.LpubTypes_start0 # Length of Public Types Info +.LpubTypes_start0: + .short 2 # DWARF Version + .long .Lcu_begin0 # Offset of Compilation Unit Info + .long 40 # Compilation Unit Length + .long 42 # DIE offset + .byte 144 # Attributes: TYPE, STATIC + .asciz "int" # External Name + .long 0 # End Mark +.LpubTypes_end0: + .ident "clang version 18.0.0" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/Inputs/dwarf5-df-output-dir-same-name-main.s b/bolt/test/X86/Inputs/dwarf5-df-output-dir-same-name-main.s new file mode 100644 index 0000000000000000000000000000000000000000..45fca52ce509976ffc4c17dabfda0cca47cf6b3d --- /dev/null +++ b/bolt/test/X86/Inputs/dwarf5-df-output-dir-same-name-main.s @@ -0,0 +1,221 @@ +# clang++ main.cpp -g2 -gsplit-dwarf -S -fdebug-compilation-dir=. +# Modified assembly so that DW_AT_dwo_name has a partial path. +# int main() { return 0; } + + .text + .file "main.cpp" + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .file 0 "." "main.cpp" md5 0xbb12fec8d002b1f0e06f7dee4604c6cc + .loc 0 1 0 # main.cpp:1:0 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + movl $0, -4(%rbp) +.Ltmp0: + .loc 0 2 3 prologue_end # main.cpp:2:3 + xorl %eax, %eax + .loc 0 2 3 epilogue_begin is_stmt 0 # main.cpp:2:3 + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Ltmp1: +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + # -- End function + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 74 # DW_TAG_skeleton_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .ascii "\264B" # DW_AT_GNU_pubnames + .byte 25 # DW_FORM_flag_present + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 4 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .quad 6318074710904753300 + .byte 1 # Abbrev [1] 0x14:0x14 DW_TAG_skeleton_unit + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .byte 0 # DW_AT_comp_dir + # DW_AT_GNU_pubnames + .byte 1 # DW_AT_dwo_name + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 12 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Lskel_string0: + .asciz "." # string offset=0 +.Lskel_string1: + .asciz "objects/o1/split.dwo" # string offset=2 <--- Manually modified. + .section .debug_str_offsets,"",@progbits + .long .Lskel_string0 + .long .Lskel_string1 + .section .debug_str_offsets.dwo,"e",@progbits + .long 24 # Length of String Offsets Set + .short 5 + .short 0 + .section .debug_str.dwo,"eMS",@progbits,1 +.Linfo_string0: + .asciz "main" # string offset=0 +.Linfo_string1: + .asciz "int" # string offset=5 +.Linfo_string2: + .asciz "clang version 18.0.0" # string offset=9 +.Linfo_string3: + .asciz "main.cpp" # string offset=30 +.Linfo_string4: + .asciz "objects/o1/split.dwo" # string offset=39 <--- Manually modified. + .section .debug_str_offsets.dwo,"e",@progbits + .long 0 + .long 5 + .long 9 + .long 30 + .long 39 + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 6318074710904753300 + .byte 1 # Abbrev [1] 0x14:0x1a DW_TAG_compile_unit + .byte 2 # DW_AT_producer + .short 33 # DW_AT_language + .byte 3 # DW_AT_name + .byte 4 # DW_AT_dwo_name + .byte 2 # Abbrev [2] 0x1a:0xf DW_TAG_subprogram + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 86 + .byte 0 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 41 # DW_AT_type + # DW_AT_external + .byte 3 # Abbrev [3] 0x29:0x4 DW_TAG_base_type + .byte 1 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end0: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .Lfunc_begin0 +.Ldebug_addr_end0: + .section .debug_gnu_pubnames,"",@progbits + .long .LpubNames_end0-.LpubNames_start0 # Length of Public Names Info +.LpubNames_start0: + .short 2 # DWARF Version + .long .Lcu_begin0 # Offset of Compilation Unit Info + .long 40 # Compilation Unit Length + .long 26 # DIE offset + .byte 48 # Attributes: FUNCTION, EXTERNAL + .asciz "main" # External Name + .long 0 # End Mark +.LpubNames_end0: + .section .debug_gnu_pubtypes,"",@progbits + .long .LpubTypes_end0-.LpubTypes_start0 # Length of Public Types Info +.LpubTypes_start0: + .short 2 # DWARF Version + .long .Lcu_begin0 # Offset of Compilation Unit Info + .long 40 # Compilation Unit Length + .long 41 # DIE offset + .byte 144 # Attributes: TYPE, STATIC + .asciz "int" # External Name + .long 0 # End Mark +.LpubTypes_end0: + .ident "clang version 18.0.0" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/Inputs/dwarf5-df-types-dup-helper.s b/bolt/test/X86/Inputs/dwarf5-df-types-dup-helper.s new file mode 100644 index 0000000000000000000000000000000000000000..cf0bdad3bbda188300fc60e677f4d0196694676d --- /dev/null +++ b/bolt/test/X86/Inputs/dwarf5-df-types-dup-helper.s @@ -0,0 +1,504 @@ +# clang++ -gsplit-dwarf -g2 -gdwarf-5 -gpubnames -fdebug-types-section -fdebug-compilation-dir='.' +# header.h +# struct Foo2a { +# char *c1; +# char *c2; +# char *c3; +# }; + +# main.cpp +# #include "header.h" +# int fooint; +# struct Foo2Int { +# int *c1; +# int *c2; +# }; +# +# int foo() { +# Foo2Int fint; +# Foo2a f; +# return 0; +# } + + .text + .file "helper.cpp" + .file 0 "." "helper.cpp" md5 0xc33186b2db66a78883b1546aace9855d + .globl _Z3foov # -- Begin function _Z3foov + .p2align 4, 0x90 + .type _Z3foov,@function +_Z3foov: # @_Z3foov +.Lfunc_begin0: + .loc 0 8 0 # helper.cpp:8:0 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp +.Ltmp0: + .loc 0 11 3 prologue_end # helper.cpp:11:3 + xorl %eax, %eax + .loc 0 11 3 epilogue_begin is_stmt 0 # helper.cpp:11:3 + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Ltmp1: +.Lfunc_end0: + .size _Z3foov, .Lfunc_end0-_Z3foov + .cfi_endproc + # -- End function + .type fooint,@object # @fooint + .bss + .globl fooint + .p2align 2, 0x0 +fooint: + .long 0 # 0x0 + .size fooint, 4 + + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad -3882554063269480080 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x2a DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x19 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 7 # DW_AT_name + .byte 16 # DW_AT_byte_size + .byte 0 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 5 # DW_AT_name + .long 56 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x2e:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 56 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .byte 8 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x38:0x5 DW_TAG_pointer_type + .long 61 # DW_AT_type + .byte 5 # Abbrev [5] 0x3d:0x4 DW_TAG_base_type + .byte 1 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end0: + .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit +.Ldebug_info_dwo_start1: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 1175092228111723119 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x33 DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x22 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 11 # DW_AT_name + .byte 24 # DW_AT_byte_size + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 5 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x2e:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 8 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x37:0x9 DW_TAG_member + .byte 10 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 16 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x41:0x5 DW_TAG_pointer_type + .long 70 # DW_AT_type + .byte 5 # Abbrev [5] 0x46:0x4 DW_TAG_base_type + .byte 9 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end1: + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 74 # DW_TAG_skeleton_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 4 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .quad 2142419470755914572 + .byte 1 # Abbrev [1] 0x14:0x14 DW_TAG_skeleton_unit + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .byte 0 # DW_AT_comp_dir + .byte 1 # DW_AT_dwo_name + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 12 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Lskel_string0: + .asciz "." # string offset=0 +.Lskel_string1: + .asciz "helper.dwo" # string offset=2 + .section .debug_str_offsets,"",@progbits + .long .Lskel_string0 + .long .Lskel_string1 + .section .debug_str_offsets.dwo,"e",@progbits + .long 64 # Length of String Offsets Set + .short 5 + .short 0 + .section .debug_str.dwo,"eMS",@progbits,1 +.Linfo_string0: + .asciz "fooint" # string offset=0 +.Linfo_string1: + .asciz "int" # string offset=7 +.Linfo_string2: + .asciz "_Z3foov" # string offset=11 +.Linfo_string3: + .asciz "foo" # string offset=19 +.Linfo_string4: + .asciz "fint" # string offset=23 +.Linfo_string5: + .asciz "c1" # string offset=28 +.Linfo_string6: + .asciz "c2" # string offset=31 +.Linfo_string7: + .asciz "Foo2Int" # string offset=34 +.Linfo_string8: + .asciz "f" # string offset=42 +.Linfo_string9: + .asciz "char" # string offset=44 +.Linfo_string10: + .asciz "c3" # string offset=49 +.Linfo_string11: + .asciz "Foo2a" # string offset=52 +.Linfo_string12: + .asciz "clang version 18.0.0" # string offset=58 +.Linfo_string13: + .asciz "helper.cpp" # string offset=79 +.Linfo_string14: + .asciz "helper.dwo" # string offset=90 + .section .debug_str_offsets.dwo,"e",@progbits + .long 0 + .long 7 + .long 11 + .long 19 + .long 23 + .long 28 + .long 31 + .long 34 + .long 42 + .long 44 + .long 49 + .long 52 + .long 58 + .long 79 + .long 90 + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit +.Ldebug_info_dwo_start2: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 2142419470755914572 + .byte 6 # Abbrev [6] 0x14:0x4f DW_TAG_compile_unit + .byte 12 # DW_AT_producer + .short 33 # DW_AT_language + .byte 13 # DW_AT_name + .byte 14 # DW_AT_dwo_name + .byte 7 # Abbrev [7] 0x1a:0xb DW_TAG_variable + .byte 0 # DW_AT_name + .long 37 # DW_AT_type + # DW_AT_external + .byte 0 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 2 # DW_AT_location + .byte 161 + .byte 0 + .byte 5 # Abbrev [5] 0x25:0x4 DW_TAG_base_type + .byte 1 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 8 # Abbrev [8] 0x29:0x27 DW_TAG_subprogram + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 86 + .byte 2 # DW_AT_linkage_name + .byte 3 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 8 # DW_AT_decl_line + .long 37 # DW_AT_type + # DW_AT_external + .byte 9 # Abbrev [9] 0x39:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 112 + .byte 4 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 9 # DW_AT_decl_line + .long 80 # DW_AT_type + .byte 9 # Abbrev [9] 0x44:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 88 + .byte 8 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 10 # DW_AT_decl_line + .long 89 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 10 # Abbrev [10] 0x50:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad -3882554063269480080 # DW_AT_signature + .byte 10 # Abbrev [10] 0x59:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad 1175092228111723119 # DW_AT_signature + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end2: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 65 # DW_TAG_type_unit + .byte 1 # DW_CHILDREN_yes + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 1 # DW_CHILDREN_yes + .byte 54 # DW_AT_calling_convention + .byte 11 # DW_FORM_data1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 13 # DW_TAG_member + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 56 # DW_AT_data_member_location + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 110 # DW_AT_linkage_name + .byte 37 # DW_FORM_strx1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 0 # DW_CHILDREN_no + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 105 # DW_AT_signature + .byte 32 # DW_FORM_ref_sig8 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_line.dwo,"e",@progbits +.Ltmp2: + .long .Ldebug_line_end0-.Ldebug_line_start0 # unit length +.Ldebug_line_start0: + .short 5 + .byte 8 + .byte 0 + .long .Lprologue_end0-.Lprologue_start0 +.Lprologue_start0: + .byte 1 + .byte 1 + .byte 1 + .byte -5 + .byte 14 + .byte 1 + .byte 1 + .byte 1 + .byte 8 + .byte 2 + .byte 46 + .byte 0 + .byte 46 + .byte 0 + .byte 3 + .byte 1 + .byte 8 + .byte 2 + .byte 15 + .byte 5 + .byte 30 + .byte 2 + .ascii "helper.cpp" + .byte 0 + .byte 0 + .byte 0xc3, 0x31, 0x86, 0xb2 + .byte 0xdb, 0x66, 0xa7, 0x88 + .byte 0x83, 0xb1, 0x54, 0x6a + .byte 0xac, 0xe9, 0x85, 0x5d + .ascii "header.h" + .byte 0 + .byte 1 + .byte 0xfe, 0xa7, 0xbb, 0x1f + .byte 0x22, 0xc4, 0x7f, 0x12 + .byte 0x9e, 0x15, 0x69, 0x5f + .byte 0x71, 0x37, 0xa1, 0xe7 +.Lprologue_end0: +.Ldebug_line_end0: + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad fooint + .quad .Lfunc_begin0 +.Ldebug_addr_end0: + .ident "clang version 18.0.0" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/Inputs/dwarf5-df-types-dup-main.s b/bolt/test/X86/Inputs/dwarf5-df-types-dup-main.s new file mode 100644 index 0000000000000000000000000000000000000000..d991094cf0a0818061c061c178e8eb6f66c29e58 --- /dev/null +++ b/bolt/test/X86/Inputs/dwarf5-df-types-dup-main.s @@ -0,0 +1,498 @@ +# clang++ -gsplit-dwarf -g2 -gdwarf-5 -gpubnames -fdebug-types-section -fdebug-compilation-dir='.' +# header.h +# struct Foo2a { +# char *c1; +# char *c2; +# char *c3; +# }; + +# main.cpp +# #include "header.h" +# struct Foo2 { +# char *c1; +# }; +# int main(int argc, char *argv[]) { +# Foo2 f2; +# Foo2a f3; +# return 0; +# } + .text + .file "main.cpp" + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .file 0 "." "main.cpp" md5 0x9c5cea5bb78d3fc265cd175110bfe903 + .loc 0 5 0 # main.cpp:5:0 + .cfi_startproc +# %bb.0: # %entry + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + movl $0, -4(%rbp) + movl %edi, -8(%rbp) + movq %rsi, -16(%rbp) +.Ltmp0: + .loc 0 8 2 prologue_end # main.cpp:8:2 + xorl %eax, %eax + .loc 0 8 2 epilogue_begin is_stmt 0 # main.cpp:8:2 + popq %rbp + .cfi_def_cfa %rsp, 8 + retq +.Ltmp1: +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + # -- End function + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit +.Ldebug_info_dwo_start0: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 5322170643381124694 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x21 DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x10 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 7 # DW_AT_name + .byte 8 # DW_AT_byte_size + .byte 0 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 47 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x2f:0x5 DW_TAG_pointer_type + .long 52 # DW_AT_type + .byte 5 # Abbrev [5] 0x34:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end0: + .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit +.Ldebug_info_dwo_start1: + .short 5 # DWARF version number + .byte 6 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 1175092228111723119 # Type Signature + .long 31 # Type DIE Offset + .byte 1 # Abbrev [1] 0x18:0x33 DW_TAG_type_unit + .short 33 # DW_AT_language + .long 0 # DW_AT_stmt_list + .byte 2 # Abbrev [2] 0x1f:0x22 DW_TAG_structure_type + .byte 5 # DW_AT_calling_convention + .byte 11 # DW_AT_name + .byte 24 # DW_AT_byte_size + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .byte 3 # Abbrev [3] 0x25:0x9 DW_TAG_member + .byte 6 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 2 # DW_AT_decl_line + .byte 0 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x2e:0x9 DW_TAG_member + .byte 9 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 8 # DW_AT_data_member_location + .byte 3 # Abbrev [3] 0x37:0x9 DW_TAG_member + .byte 10 # DW_AT_name + .long 65 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 16 # DW_AT_data_member_location + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x41:0x5 DW_TAG_pointer_type + .long 70 # DW_AT_type + .byte 5 # Abbrev [5] 0x46:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end1: + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 74 # DW_TAG_skeleton_unit + .byte 0 # DW_CHILDREN_no + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 4 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .quad 5962099678818150071 + .byte 1 # Abbrev [1] 0x14:0x14 DW_TAG_skeleton_unit + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .byte 0 # DW_AT_comp_dir + .byte 1 # DW_AT_dwo_name + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 12 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Lskel_string0: + .asciz "." # string offset=0 +.Lskel_string1: + .asciz "main.dwo" # string offset=2 + .section .debug_str_offsets,"",@progbits + .long .Lskel_string0 + .long .Lskel_string1 + .section .debug_str_offsets.dwo,"e",@progbits + .long 64 # Length of String Offsets Set + .short 5 + .short 0 + .section .debug_str.dwo,"eMS",@progbits,1 +.Linfo_string0: + .asciz "main" # string offset=0 +.Linfo_string1: + .asciz "int" # string offset=5 +.Linfo_string2: + .asciz "argc" # string offset=9 +.Linfo_string3: + .asciz "argv" # string offset=14 +.Linfo_string4: + .asciz "char" # string offset=19 +.Linfo_string5: + .asciz "f2" # string offset=24 +.Linfo_string6: + .asciz "c1" # string offset=27 +.Linfo_string7: + .asciz "Foo2" # string offset=30 +.Linfo_string8: + .asciz "f3" # string offset=35 +.Linfo_string9: + .asciz "c2" # string offset=38 +.Linfo_string10: + .asciz "c3" # string offset=41 +.Linfo_string11: + .asciz "Foo2a" # string offset=44 +.Linfo_string12: + .asciz "clang version 18.0.0" # string offset=50 +.Linfo_string13: + .asciz "main.cpp" # string offset=71 +.Linfo_string14: + .asciz "main.dwo" # string offset=80 + .section .debug_str_offsets.dwo,"e",@progbits + .long 0 + .long 5 + .long 9 + .long 14 + .long 19 + .long 24 + .long 27 + .long 30 + .long 35 + .long 38 + .long 41 + .long 44 + .long 50 + .long 71 + .long 80 + .section .debug_info.dwo,"e",@progbits + .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit +.Ldebug_info_dwo_start2: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long 0 # Offset Into Abbrev. Section + .quad 5962099678818150071 + .byte 6 # Abbrev [6] 0x14:0x67 DW_TAG_compile_unit + .byte 12 # DW_AT_producer + .short 33 # DW_AT_language + .byte 13 # DW_AT_name + .byte 14 # DW_AT_dwo_name + .byte 7 # Abbrev [7] 0x1a:0x3c DW_TAG_subprogram + .byte 0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 86 + .byte 0 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .long 86 # DW_AT_type + # DW_AT_external + .byte 8 # Abbrev [8] 0x29:0xb DW_TAG_formal_parameter + .byte 2 # DW_AT_location + .byte 145 + .byte 120 + .byte 2 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .long 86 # DW_AT_type + .byte 8 # Abbrev [8] 0x34:0xb DW_TAG_formal_parameter + .byte 2 # DW_AT_location + .byte 145 + .byte 112 + .byte 3 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 5 # DW_AT_decl_line + .long 90 # DW_AT_type + .byte 9 # Abbrev [9] 0x3f:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 104 + .byte 5 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 6 # DW_AT_decl_line + .long 104 # DW_AT_type + .byte 9 # Abbrev [9] 0x4a:0xb DW_TAG_variable + .byte 2 # DW_AT_location + .byte 145 + .byte 80 + .byte 8 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 113 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 5 # Abbrev [5] 0x56:0x4 DW_TAG_base_type + .byte 1 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 4 # Abbrev [4] 0x5a:0x5 DW_TAG_pointer_type + .long 95 # DW_AT_type + .byte 4 # Abbrev [4] 0x5f:0x5 DW_TAG_pointer_type + .long 100 # DW_AT_type + .byte 5 # Abbrev [5] 0x64:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 10 # Abbrev [10] 0x68:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad 5322170643381124694 # DW_AT_signature + .byte 10 # Abbrev [10] 0x71:0x9 DW_TAG_structure_type + # DW_AT_declaration + .quad 1175092228111723119 # DW_AT_signature + .byte 0 # End Of Children Mark +.Ldebug_info_dwo_end2: + .section .debug_abbrev.dwo,"e",@progbits + .byte 1 # Abbreviation Code + .byte 65 # DW_TAG_type_unit + .byte 1 # DW_CHILDREN_yes + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 1 # DW_CHILDREN_yes + .byte 54 # DW_AT_calling_convention + .byte 11 # DW_FORM_data1 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 13 # DW_TAG_member + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 56 # DW_AT_data_member_location + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 118 # DW_AT_dwo_name + .byte 37 # DW_FORM_strx1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 19 # DW_TAG_structure_type + .byte 0 # DW_CHILDREN_no + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 105 # DW_AT_signature + .byte 32 # DW_FORM_ref_sig8 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_line.dwo,"e",@progbits +.Ltmp2: + .long .Ldebug_line_end0-.Ldebug_line_start0 # unit length +.Ldebug_line_start0: + .short 5 + .byte 8 + .byte 0 + .long .Lprologue_end0-.Lprologue_start0 +.Lprologue_start0: + .byte 1 + .byte 1 + .byte 1 + .byte -5 + .byte 14 + .byte 1 + .byte 1 + .byte 1 + .byte 8 + .byte 2 + .byte 46 + .byte 0 + .byte 46 + .byte 0 + .byte 3 + .byte 1 + .byte 8 + .byte 2 + .byte 15 + .byte 5 + .byte 30 + .byte 2 + .ascii "main.cpp" + .byte 0 + .byte 0 + .byte 0x9c, 0x5c, 0xea, 0x5b + .byte 0xb7, 0x8d, 0x3f, 0xc2 + .byte 0x65, 0xcd, 0x17, 0x51 + .byte 0x10, 0xbf, 0xe9, 0x03 + .ascii "header.h" + .byte 0 + .byte 1 + .byte 0xfe, 0xa7, 0xbb, 0x1f + .byte 0x22, 0xc4, 0x7f, 0x12 + .byte 0x9e, 0x15, 0x69, 0x5f + .byte 0x71, 0x37, 0xa1, 0xe7 +.Lprologue_end0: +.Ldebug_line_end0: + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .Lfunc_begin0 +.Ldebug_addr_end0: + .ident "clang version 18.0.0" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/Inputs/linenumber.cpp b/bolt/test/X86/Inputs/linenumber.cpp index 8a754b9a98235fb86b5537ee59b6a103823184a9..88eb2a8704bb5e52a09fb940de4de5b28590bc0a 100644 --- a/bolt/test/X86/Inputs/linenumber.cpp +++ b/bolt/test/X86/Inputs/linenumber.cpp @@ -1,6 +1,6 @@ int f() { // This will be removed by BOLT but they make sure we have some extra space - // to insert branches and don't run out of space when rewritting the function. + // to insert branches and don't run out of space when rewriting the function. asm("nop"); asm("nop"); asm("nop"); diff --git a/bolt/test/X86/block-reordering.test b/bolt/test/X86/block-reordering.test index a18a2109d37e274315f328ee6e2ce8b8cef4754b..f3a3390e27cb97e47988df59194d7263b3010a13 100644 --- a/bolt/test/X86/block-reordering.test +++ b/bolt/test/X86/block-reordering.test @@ -2,7 +2,7 @@ # according to the new function layout. RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe -RUN: llvm-bolt %t.exe -o /dev/null --data %p/Inputs/blarge.fdata \ +RUN: llvm-bolt %t.exe -o %t.null --data %p/Inputs/blarge.fdata \ RUN: --reorder-blocks=normal --print-finalized 2>&1 | FileCheck %s \ RUN: --check-prefix=CHECK diff --git a/bolt/test/X86/branch-data.test b/bolt/test/X86/branch-data.test index 5d6ff92d18e28b0c2a731ed565d9b2d7b56ba206..0c64caaee8a50b0a9df6191d9366cdb4ace19efc 100644 --- a/bolt/test/X86/branch-data.test +++ b/bolt/test/X86/branch-data.test @@ -3,7 +3,7 @@ # Also checks that llvm-bolt disassembler and CFG builder is working properly. RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe -RUN: llvm-bolt %t.exe -o /dev/null --data %p/Inputs/blarge.fdata --print-cfg | FileCheck %s +RUN: llvm-bolt %t.exe -o %t.null --data %p/Inputs/blarge.fdata --print-cfg | FileCheck %s CHECK: Binary Function "usqrt" CHECK: State : CFG constructed diff --git a/bolt/test/X86/bug-function-layout-execount.s b/bolt/test/X86/bug-function-layout-execount.s index 540b6790d01e900a77ccd1b2740b13ea24042108..c88e4d0043b46335c37efa1cae108224faa1567e 100644 --- a/bolt/test/X86/bug-function-layout-execount.s +++ b/bolt/test/X86/bug-function-layout-execount.s @@ -6,7 +6,7 @@ # RUN: link_fdata %s %t.o %t.fdata # RUN: %clang %cflags %t.o -o %t.exe -Wl,-q # RUN: llvm-bolt %t.exe --data %t.fdata --lite --reorder-functions=exec-count \ -# RUN: -v=2 --debug-only=hfsort -o /dev/null 2>&1 | FileCheck %s +# RUN: -v=2 --debug-only=hfsort -o %t.null 2>&1 | FileCheck %s # CHECK: Starting pass: reorder-functions # CHECK-NEXT: hot func func2 (1500) diff --git a/bolt/test/X86/calculate-emitted-block-size.s b/bolt/test/X86/calculate-emitted-block-size.s new file mode 100644 index 0000000000000000000000000000000000000000..b1d05b83cb87c74d8794066504331e47899452d0 --- /dev/null +++ b/bolt/test/X86/calculate-emitted-block-size.s @@ -0,0 +1,101 @@ +# Test BinaryContext::calculateEmittedSize's functionality to update +# BinaryBasicBlock::OutputAddressRange in place so that the emitted size +# of each basic block is given by BinaryBasicBlock::getOutputSize() + +# 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 %t.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=all \ +# RUN: --print-split --print-only=chain --print-output-address-range \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=SPLITALL %s +# 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 %t.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --print-split \ +# RUN: --print-only=chain --print-output-address-range \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=SPLITHOTCOLD %s + +# SPLITALL: {{^\.LBB00}} +# SPLITALL: Output Address Range: [0x0, 0x12) (18 bytes) +# SPLITALL: {{^\.LFT0}} +# SPLITALL: Output Address Range: [0x0, 0xa) (10 bytes) +# SPLITALL: {{^\.Ltmp1}} +# SPLITALL: Output Address Range: [0x0, 0x2) (2 bytes) +# SPLITALL: {{^\.Ltmp0}} +# SPLITALL: Output Address Range: [0x0, 0x10) (16 bytes) +# SPLITALL: {{^\.Ltmp2}} +# SPLITALL: Output Address Range: [0x0, 0x8) (8 bytes) +# SPLITALL: {{^\.LFT1}} +# SPLITALL: Output Address Range: [0x0, 0x8) (8 bytes) + +# SPLITHOTCOLD: {{^\.LBB00}} +# SPLITHOTCOLD: Output Address Range: [0x0, 0x9) (9 bytes) +# SPLITHOTCOLD: {{^\.LFT0}} +# SPLITHOTCOLD: Output Address Range: [0x9, 0xe) (5 bytes) +# SPLITHOTCOLD: {{^\.Ltmp1}} +# SPLITHOTCOLD: Output Address Range: [0xe, 0x10) (2 bytes) +# SPLITHOTCOLD: {{^\.Ltmp0}} +# SPLITHOTCOLD: Output Address Range: [0x10, 0x1b) (11 bytes) +# SPLITHOTCOLD: {{^\.Ltmp2}} +# SPLITHOTCOLD: Output Address Range: [0x1b, 0x20) (5 bytes) +# SPLITHOTCOLD: {{^\.LFT1}} +# SPLITHOTCOLD: Output Address Range: [0x0, 0x8) (8 bytes) + + .text + .globl chain + .type chain, @function +chain: + pushq %rbp + movq %rsp, %rbp + cmpl $2, %edi +LLentry_LLchain_start: + jge LLchain_start +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLchain_start# 0 10 +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLfast# 0 500 +LLfast: + movl $5, %eax +LLfast_LLexit: + jmp LLexit +# FDATA: 1 chain #LLfast_LLexit# 1 chain #LLexit# 0 500 +LLchain_start: + movl $10, %eax +LLchain_start_LLchain1: + jge LLchain1 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLchain1# 0 10 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLcold# 0 0 +LLcold: + addl $1, %eax +LLchain1: + addl $1, %eax +LLchain1_LLexit: + jmp LLexit +# FDATA: 1 chain #LLchain1_LLexit# 1 chain #LLexit# 0 10 +LLexit: + popq %rbp + ret +LLchain_end: + .size chain, LLchain_end-chain + + + .globl main + .type main, @function +main: + pushq %rbp + movq %rsp, %rbp + movl $1, %edi +LLmain_chain1: + call chain +# FDATA: 1 main #LLmain_chain1# 1 chain 0 0 500 + movl $4, %edi +LLmain_chain2: + call chain +# FDATA: 1 main #LLmain_chain2# 1 chain 0 0 10 + xorl %eax, %eax + popq %rbp + retq +.Lmain_end: + .size main, .Lmain_end-main diff --git a/bolt/test/X86/call-zero.s b/bolt/test/X86/call-zero.s index 3644bf5d962d0541a9ac0e3c3b7597798d7b6067..3d6308d9e6f837b6ea1244730e6ed78d76de70f7 100644 --- a/bolt/test/X86/call-zero.s +++ b/bolt/test/X86/call-zero.s @@ -2,7 +2,7 @@ # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o # RUN: %clang %cflags %t.o -o %t.exe -# RUN: llvm-bolt %t.exe -o /dev/null -v=2 2>&1 | FileCheck %s +# RUN: llvm-bolt %t.exe -o %t.null -v=2 2>&1 | FileCheck %s # CHECK: Function main has a call to address zero. .text diff --git a/bolt/test/X86/cfi-instrs-count.s b/bolt/test/X86/cfi-instrs-count.s index adf28a9e0811f35ecc95ee033ccdf3464fbba711..635d560ae75335930cd70a5692d8d4c254ab96ac 100644 --- a/bolt/test/X86/cfi-instrs-count.s +++ b/bolt/test/X86/cfi-instrs-count.s @@ -3,7 +3,7 @@ # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o # RUN: %clang %cflags %t.o -o %t.exe -# RUN: llvm-bolt %t.exe -o /dev/null --print-cfg 2>&1 | FileCheck %s +# RUN: llvm-bolt %t.exe -o %t.null --print-cfg 2>&1 | FileCheck %s # # CHECK: Binary Function "_Z7catchitv" after building cfg { # CHECK: CFI Instrs : 6 diff --git a/bolt/test/X86/checkvma-large-section.test b/bolt/test/X86/checkvma-large-section.test index afa44111ead49e5d7d46f13a8578144cb6311fd9..89aa4f78d52d12afda8200f697ccaa8f437b7e86 100644 --- a/bolt/test/X86/checkvma-large-section.test +++ b/bolt/test/X86/checkvma-large-section.test @@ -2,7 +2,7 @@ REQUIRES: asserts RUN: split-file %s %t RUN: yaml2obj %t/yaml -o %t.exe --max-size=0 -RUN: llvm-bolt %t.exe -o /dev/null --allow-stripped +RUN: llvm-bolt %t.exe -o %t.null --allow-stripped #--- yaml --- !ELF FileHeader: diff --git a/bolt/test/X86/data-to-data-pcrel.s b/bolt/test/X86/data-to-data-pcrel.s index 256682655d6d37c4b725c52e83b3d3554f1f4766..00b5bf83e25dbf58b6b8c70b00d18d4ea085aa61 100644 --- a/bolt/test/X86/data-to-data-pcrel.s +++ b/bolt/test/X86/data-to-data-pcrel.s @@ -4,7 +4,7 @@ # RUN: llvm-strip --strip-unneeded %t.o # RUN: ld.lld %t.o -o %t.exe -q --unresolved-symbols=ignore-all # RUN: llvm-readelf -Wr %t.exe | FileCheck %s -# RUN: llvm-bolt --strict %t.exe --relocs -o /dev/null +# RUN: llvm-bolt --strict %t.exe --relocs -o %t.null .text .globl _start diff --git a/bolt/test/X86/double-jump.test b/bolt/test/X86/double-jump.test index 237ca4cc21655602e91788e12af08c14f52e2af3..cbd5ce9dae0e5b97a08c51f507e613a8cfe7e840 100644 --- a/bolt/test/X86/double-jump.test +++ b/bolt/test/X86/double-jump.test @@ -1,4 +1,4 @@ -# Test the double jump removqal peephole. +# Test the double jump removal peephole. # This test has commands that rely on shell capabilities that won't execute # correctly on Windows e.g. subshell execution diff --git a/bolt/test/X86/double-rel.s b/bolt/test/X86/double-rel.s index 7de842d9dd3187ba9374345bc761a01fdeae0149..f7754f77e782b744261b135a80a0353fd2bc47b2 100644 --- a/bolt/test/X86/double-rel.s +++ b/bolt/test/X86/double-rel.s @@ -6,7 +6,7 @@ # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o # RUN: ld.lld %t.o -o %t.exe -q --Tdata=0x80000 -# RUN: llvm-bolt %t.exe --relocs -o /dev/null --print-only=_start --print-disasm \ +# RUN: llvm-bolt %t.exe --relocs -o %t.null --print-only=_start --print-disasm \ # RUN: | FileCheck %s --check-prefix=CHECK-BOLT # RUN: llvm-objdump -d --print-imm-hex %t.exe \ # RUN: | FileCheck %s --check-prefix=CHECK-OBJDUMP diff --git a/bolt/test/X86/dwarf-inline-range.s b/bolt/test/X86/dwarf-inline-range.s new file mode 100644 index 0000000000000000000000000000000000000000..6cf4203cf5972e114623c0bc12ef34d096623758 --- /dev/null +++ b/bolt/test/X86/dwarf-inline-range.s @@ -0,0 +1,393 @@ +## Use llvm-dwarfdump to check the integrity of the inlined function "bar" +## DWARF range after llvm-bolt removes a 6-byte nop instruction. +## +## If the range is not properly updated, it will exceed the range of the +## containing function causing llvm-dwarfdump to issue an error. + +# CHECK-NOT: error: DIE address ranges are not contained in its parent's ranges + +# REQUIRES: system-linux + +# RUN: %clang++ %cflags -gdwarf-4 %s -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections +# RUN: llvm-dwarfdump --verify %t.bolt | FileCheck %s + + +# Test compiled with "-O2 -g" from: +# +# unsigned long bar(unsigned long i) { +# asm volatile("nopw %cs:(%rax,%rax)"); +# return ++i; +# } +# +# int main(int argc, char **argv) { +# bar(argc); +# return 0; +# } + + .text + .file "dwarf-inline-range.cpp" + .globl _Z3barm # -- Begin function _Z3barm + .p2align 4, 0x90 + .type _Z3barm,@function +_Z3barm: # @_Z3barm +.Lfunc_begin0: + .file 1 "." "dwarf-inline-range.cpp" + .loc 1 1 0 # dwarf-inline-range.cpp:1:0 + .cfi_startproc +# %bb.0: + #DEBUG_VALUE: bar:i <- $rdi + .loc 1 2 3 prologue_end # dwarf-inline-range.cpp:2:3 + #APP + nopw %cs:(%rax,%rax) + #NO_APP + .loc 1 3 10 # dwarf-inline-range.cpp:3:10 + leaq 1(%rdi), %rax +.Ltmp0: + #DEBUG_VALUE: bar:i <- $rax + .loc 1 3 3 is_stmt 0 # dwarf-inline-range.cpp:3:3 + retq +.Ltmp1: +.Lfunc_end0: + .size _Z3barm, .Lfunc_end0-_Z3barm + .cfi_endproc + # -- End function + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin1: + .loc 1 6 0 is_stmt 1 # dwarf-inline-range.cpp:6:0 + .cfi_startproc +# %bb.0: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + #DEBUG_VALUE: bar:i <- [DW_OP_LLVM_convert 32 5, DW_OP_LLVM_convert 64 5, DW_OP_stack_value] $edi + .loc 1 2 3 prologue_end # dwarf-inline-range.cpp:2:3 + #APP + nopw %cs:(%rax,%rax) + #NO_APP +.Ltmp2: + #DEBUG_VALUE: bar:i <- [DW_OP_LLVM_convert 32 5, DW_OP_LLVM_convert 64 5, DW_OP_plus_uconst 1, DW_OP_stack_value] undef + .loc 1 8 3 # dwarf-inline-range.cpp:8:3 + xorl %eax, %eax + retq +.Ltmp3: +.Lfunc_end1: + .size main, .Lfunc_end1-main + .cfi_endproc + # -- End function + .section .debug_loc,"",@progbits +.Ldebug_loc0: + .quad .Lfunc_begin0-.Lfunc_begin0 + .quad .Ltmp0-.Lfunc_begin0 + .short 1 # Loc expr size + .byte 85 # DW_OP_reg5 + .quad .Ltmp0-.Lfunc_begin0 + .quad .Lfunc_end0-.Lfunc_begin0 + .short 1 # Loc expr size + .byte 80 # DW_OP_reg0 + .quad 0 + .quad 0 +.Ldebug_loc1: + .quad .Lfunc_begin1-.Lfunc_begin0 + .quad .Ltmp2-.Lfunc_begin0 + .short 21 # Loc expr size + .byte 117 # DW_OP_breg5 + .byte 0 # 0 + .byte 16 # DW_OP_constu + .byte 255 # 4294967295 + .byte 255 # + .byte 255 # + .byte 255 # + .byte 15 # + .byte 26 # DW_OP_and + .byte 18 # DW_OP_dup + .byte 16 # DW_OP_constu + .byte 31 # 31 + .byte 37 # DW_OP_shr + .byte 48 # DW_OP_lit0 + .byte 32 # DW_OP_not + .byte 30 # DW_OP_mul + .byte 16 # DW_OP_constu + .byte 32 # 32 + .byte 36 # DW_OP_shl + .byte 33 # DW_OP_or + .byte 159 # DW_OP_stack_value + .quad 0 + .quad 0 + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 14 # DW_FORM_strp + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 14 # DW_FORM_strp + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .ascii "\227B" # DW_AT_GNU_all_call_sites + .byte 25 # DW_FORM_flag_present + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 23 # DW_FORM_sec_offset + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 110 # DW_AT_linkage_name + .byte 14 # DW_FORM_strp + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 32 # DW_AT_inline + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .ascii "\227B" # DW_AT_GNU_all_call_sites + .byte 25 # DW_FORM_flag_present + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 1 # DW_CHILDREN_yes + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0xca DW_TAG_compile_unit + .long .Linfo_string0 # DW_AT_producer + .short 33 # DW_AT_language + .long .Linfo_string1 # DW_AT_name + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Linfo_string2 # DW_AT_comp_dir + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc + .byte 2 # Abbrev [2] 0x2a:0x1d DW_TAG_subprogram + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_GNU_all_call_sites + .long 71 # DW_AT_abstract_origin + .byte 3 # Abbrev [3] 0x3d:0x9 DW_TAG_formal_parameter + .long .Ldebug_loc0 # DW_AT_location + .long 87 # DW_AT_abstract_origin + .byte 0 # End Of Children Mark + .byte 4 # Abbrev [4] 0x47:0x1c DW_TAG_subprogram + .long .Linfo_string3 # DW_AT_linkage_name + .long .Linfo_string4 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 99 # DW_AT_type + # DW_AT_external + .byte 1 # DW_AT_inline + .byte 5 # Abbrev [5] 0x57:0xb DW_TAG_formal_parameter + .long .Linfo_string6 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + .long 99 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 6 # Abbrev [6] 0x63:0x7 DW_TAG_base_type + .long .Linfo_string5 # DW_AT_name + .byte 7 # DW_AT_encoding + .byte 8 # DW_AT_byte_size + .byte 7 # Abbrev [7] 0x6a:0x52 DW_TAG_subprogram + .quad .Lfunc_begin1 # DW_AT_low_pc + .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_GNU_all_call_sites + .long .Linfo_string7 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 6 # DW_AT_decl_line + .long 188 # DW_AT_type + # DW_AT_external + .byte 8 # Abbrev [8] 0x83:0xd DW_TAG_formal_parameter + .byte 1 # DW_AT_location + .byte 85 + .long .Linfo_string9 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 6 # DW_AT_decl_line + .long 188 # DW_AT_type + .byte 8 # Abbrev [8] 0x90:0xd DW_TAG_formal_parameter + .byte 1 # DW_AT_location + .byte 84 + .long .Linfo_string10 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 6 # DW_AT_decl_line + .long 195 # DW_AT_type + .byte 9 # Abbrev [9] 0x9d:0x1e DW_TAG_inlined_subroutine + .long 71 # DW_AT_abstract_origin + .quad .Lfunc_begin1 # DW_AT_low_pc + .long .Ltmp2-.Lfunc_begin1 # DW_AT_high_pc + .byte 1 # DW_AT_call_file + .byte 7 # DW_AT_call_line + .byte 3 # DW_AT_call_column + .byte 3 # Abbrev [3] 0xb1:0x9 DW_TAG_formal_parameter + .long .Ldebug_loc1 # DW_AT_location + .long 87 # DW_AT_abstract_origin + .byte 0 # End Of Children Mark + .byte 0 # End Of Children Mark + .byte 6 # Abbrev [6] 0xbc:0x7 DW_TAG_base_type + .long .Linfo_string8 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 10 # Abbrev [10] 0xc3:0x5 DW_TAG_pointer_type + .long 200 # DW_AT_type + .byte 10 # Abbrev [10] 0xc8:0x5 DW_TAG_pointer_type + .long 205 # DW_AT_type + .byte 6 # Abbrev [6] 0xcd:0x7 DW_TAG_base_type + .long .Linfo_string11 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 15" +.Linfo_string1: + .asciz "dwarf-inline-range.cpp" # string offset=69 +.Linfo_string2: + .asciz "." # string offset=92 +.Linfo_string3: + .asciz "_Z3barm" # string offset=112 +.Linfo_string4: + .asciz "bar" # string offset=120 +.Linfo_string5: + .asciz "unsigned long" # string offset=124 +.Linfo_string6: + .asciz "i" # string offset=138 +.Linfo_string7: + .asciz "main" # string offset=140 +.Linfo_string8: + .asciz "int" # string offset=145 +.Linfo_string9: + .asciz "argc" # string offset=149 +.Linfo_string10: + .asciz "argv" # string offset=154 +.Linfo_string11: + .asciz "char" # string offset=159 + .ident "clang version 15" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/dwarf4-deleted-range.s b/bolt/test/X86/dwarf4-deleted-range.s new file mode 100644 index 0000000000000000000000000000000000000000..7938bc001c0079033d9eeb39e16fba1342b3bb2f --- /dev/null +++ b/bolt/test/X86/dwarf4-deleted-range.s @@ -0,0 +1,432 @@ +# REQUIRES: system-linux + +# RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o +# RUN: %clang %cflags -no-pie %s -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections +# RUN: llvm-dwarfdump --show-children --name=main --debug-info %t.bolt \ +# RUN: | FileCheck %s + +# CHECK: DW_TAG_inlined_subroutine +# CHECK: DW_AT_low_pc +# CHECK-SAME: 0x0000000000000000 + +# CHECK: DW_TAG_GNU_call_site +# CHECK: DW_AT_low_pc +# CHECK-SAME: 0x0000000000000000 + +## Test that llvm-bolt correctly updates DIEs corresponding to deleted code. + +# Test case built from the following source using: +# +# clang -O2 -g ... +# +# Assembly modified with "je" -> "jmp" to introduce unreachable block. +# +# extern void puts(const char *); +# +# static void foo() { +# puts("hi"); +# } +# +# int main(int argc, char **argv) { +# if (argc) +# foo(); +# return 0; +# } + + .text + .file "unreachable.c" + .file 1 "." "unreachable.c" + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .loc 1 7 0 # unreachable.c:7:0 + .cfi_startproc +# %bb.0: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + .loc 1 8 7 prologue_end # unreachable.c:8:7 + testl %edi, %edi +.Ltmp0: + .loc 1 8 7 is_stmt 0 # unreachable.c:8:7 + jmp .LBB0_2 +.Ltmp1: +# %bb.1: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + pushq %rax + .cfi_def_cfa_offset 16 +.Ltmp2: + .loc 1 4 3 is_stmt 1 # unreachable.c:4:3 + movl $.L.str, %edi +.Ltmp3: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + callq puts +.Ltmp4: + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 1 0 3 is_stmt 0 # unreachable.c:0:3 + addq $8, %rsp +.Ltmp5: + .cfi_def_cfa_offset 8 +.LBB0_2: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 1 10 3 is_stmt 1 # unreachable.c:10:3 + xorl %eax, %eax + retq +.Ltmp6: +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + # -- End function + .type .L.str,@object # @.str + .section .rodata.str1.1,"aMS",@progbits,1 +.L.str: + .asciz "hi" + .size .L.str, 3 + + .section .debug_loc,"",@progbits +.Ldebug_loc0: + .quad .Lfunc_begin0-.Lfunc_begin0 + .quad .Ltmp3-.Lfunc_begin0 + .short 1 # Loc expr size + .byte 85 # super-register DW_OP_reg5 + .quad .Ltmp3-.Lfunc_begin0 + .quad .Lfunc_end0-.Lfunc_begin0 + .short 4 # Loc expr size + .byte 243 # DW_OP_GNU_entry_value + .byte 1 # 1 + .byte 85 # super-register DW_OP_reg5 + .byte 159 # DW_OP_stack_value + .quad 0 + .quad 0 +.Ldebug_loc1: + .quad .Lfunc_begin0-.Lfunc_begin0 + .quad .Ltmp4-.Lfunc_begin0 + .short 1 # Loc expr size + .byte 84 # DW_OP_reg4 + .quad .Ltmp4-.Lfunc_begin0 + .quad .Lfunc_end0-.Lfunc_begin0 + .short 4 # Loc expr size + .byte 243 # DW_OP_GNU_entry_value + .byte 1 # 1 + .byte 84 # DW_OP_reg4 + .byte 159 # DW_OP_stack_value + .quad 0 + .quad 0 + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 14 # DW_FORM_strp + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 14 # DW_FORM_strp + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 1 # DW_TAG_array_type + .byte 1 # DW_CHILDREN_yes + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 33 # DW_TAG_subrange_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 55 # DW_AT_count + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 32 # DW_AT_inline + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .ascii "\227B" # DW_AT_GNU_all_call_sites + .byte 25 # DW_FORM_flag_present + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 23 # DW_FORM_sec_offset + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 11 # Abbreviation Code + .ascii "\211\202\001" # DW_TAG_GNU_call_site + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 12 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 13 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 14 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 15 # Abbreviation Code + .byte 38 # DW_TAG_const_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] 0xb:0xd4 DW_TAG_compile_unit + .long .Linfo_string0 # DW_AT_producer + .short 12 # DW_AT_language + .long .Linfo_string1 # DW_AT_name + .long .Lline_table_start0 # DW_AT_stmt_list + .long .Linfo_string2 # DW_AT_comp_dir + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 2 # Abbrev [2] 0x2a:0x11 DW_TAG_variable + .long 59 # DW_AT_type + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 9 # DW_AT_location + .byte 3 + .quad .L.str + .byte 3 # Abbrev [3] 0x3b:0xc DW_TAG_array_type + .long 71 # DW_AT_type + .byte 4 # Abbrev [4] 0x40:0x6 DW_TAG_subrange_type + .long 78 # DW_AT_type + .byte 3 # DW_AT_count + .byte 0 # End Of Children Mark + .byte 5 # Abbrev [5] 0x47:0x7 DW_TAG_base_type + .long .Linfo_string3 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 6 # Abbrev [6] 0x4e:0x7 DW_TAG_base_type + .long .Linfo_string4 # DW_AT_name + .byte 8 # DW_AT_byte_size + .byte 7 # DW_AT_encoding + .byte 7 # Abbrev [7] 0x55:0x8 DW_TAG_subprogram + .long .Linfo_string5 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + .byte 1 # DW_AT_inline + .byte 8 # Abbrev [8] 0x5d:0x59 DW_TAG_subprogram + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_GNU_all_call_sites + .long .Linfo_string7 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + # DW_AT_prototyped + .long 205 # DW_AT_type + # DW_AT_external + .byte 9 # Abbrev [9] 0x76:0xf DW_TAG_formal_parameter + .long .Ldebug_loc0 # DW_AT_location + .long .Linfo_string9 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 205 # DW_AT_type + .byte 9 # Abbrev [9] 0x85:0xf DW_TAG_formal_parameter + .long .Ldebug_loc1 # DW_AT_location + .long .Linfo_string10 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 212 # DW_AT_type + .byte 10 # Abbrev [10] 0x94:0x14 DW_TAG_inlined_subroutine + .long 85 # DW_AT_abstract_origin + .quad .Ltmp2 # DW_AT_low_pc + .long .Ltmp5-.Ltmp2 # DW_AT_high_pc + .byte 1 # DW_AT_call_file + .byte 9 # DW_AT_call_line + .byte 5 # DW_AT_call_column + .byte 11 # Abbrev [11] 0xa8:0xd DW_TAG_GNU_call_site + .long 182 # DW_AT_abstract_origin + .quad .Ltmp4 # DW_AT_low_pc + .byte 0 # End Of Children Mark + .byte 12 # Abbrev [12] 0xb6:0xd DW_TAG_subprogram + .long .Linfo_string6 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + # DW_AT_prototyped + # DW_AT_declaration + # DW_AT_external + .byte 13 # Abbrev [13] 0xbd:0x5 DW_TAG_formal_parameter + .long 195 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 14 # Abbrev [14] 0xc3:0x5 DW_TAG_pointer_type + .long 200 # DW_AT_type + .byte 15 # Abbrev [15] 0xc8:0x5 DW_TAG_const_type + .long 71 # DW_AT_type + .byte 5 # Abbrev [5] 0xcd:0x7 DW_TAG_base_type + .long .Linfo_string8 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 14 # Abbrev [14] 0xd4:0x5 DW_TAG_pointer_type + .long 217 # DW_AT_type + .byte 14 # Abbrev [14] 0xd9:0x5 DW_TAG_pointer_type + .long 71 # DW_AT_type + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 15.0.7 " # string offset=0 +.Linfo_string1: + .asciz "unreachable.c" # string offset=69 +.Linfo_string2: + .asciz "." # string offset=83 +.Linfo_string3: + .asciz "char" # string offset=85 +.Linfo_string4: + .asciz "__ARRAY_SIZE_TYPE__" # string offset=90 +.Linfo_string5: + .asciz "foo" # string offset=110 +.Linfo_string6: + .asciz "puts" # string offset=114 +.Linfo_string7: + .asciz "main" # string offset=119 +.Linfo_string8: + .asciz "int" # string offset=124 +.Linfo_string9: + .asciz "argc" # string offset=128 +.Linfo_string10: + .asciz "argv" # string offset=133 + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/dwarf5-call-pc-function-null-check.test b/bolt/test/X86/dwarf5-call-pc-function-null-check.test index 87a352d99d8ddc46d149543bd149d71ecd7a3d15..b04e30bcf53293339fe9c173f1560347295fa6eb 100644 --- a/bolt/test/X86/dwarf5-call-pc-function-null-check.test +++ b/bolt/test/X86/dwarf5-call-pc-function-null-check.test @@ -8,7 +8,7 @@ # RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.exe.bolt >> %t.txt # RUN: cat %t.txt | FileCheck --check-prefix=CHECK %s -# Test checks we correclty handle nullptr returned by getBinaryFunctionContainingAddress for DW_AT_call_pc. +# Test checks we correctly handle nullptr returned by getBinaryFunctionContainingAddress for DW_AT_call_pc. # This happens when address is not contained in any function. # CHECK: DW_AT_call_pc [DW_FORM_addrx] diff --git a/bolt/test/X86/dwarf5-deleted-range.s b/bolt/test/X86/dwarf5-deleted-range.s new file mode 100644 index 0000000000000000000000000000000000000000..241c694ce89b3c6df58872f1fe8915ea867ff2c3 --- /dev/null +++ b/bolt/test/X86/dwarf5-deleted-range.s @@ -0,0 +1,484 @@ +# REQUIRES: system-linux + +# RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o +# RUN: %clang %cflags -no-pie %s -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections +# RUN: llvm-dwarfdump --show-children --name=main --debug-info %t.bolt \ +# RUN: | FileCheck %s + +# CHECK: DW_TAG_inlined_subroutine +# CHECK: DW_AT_low_pc +# CHECK-SAME: 0x0000000000000000 + +# CHECK: DW_TAG_call_site +# CHECK: DW_AT_call_return_pc +# CHECK-SAME: 0x0000000000000000 + +## Test that llvm-bolt correctly updates DIEs corresponding to deleted code. + +# Test case built from the following source using: +# +# clang -O2 -g -gdwarf-5 ... +# +# Assembly modified with "je" -> "jmp" to introduce unreachable block. +# +# extern void puts(const char *); +# +# static void foo() { +# puts("hi"); +# } +# +# int main(int argc, char **argv) { +# if (argc) +# foo(); +# return 0; +# } + + .text + .file "unreachable.c" + .file 0 "." "unreachable.c" md5 0xd268d8460ea246544a440d4d42235947 + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .loc 0 7 0 # unreachable.c:7:0 + .cfi_startproc +# %bb.0: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + .loc 0 8 7 prologue_end # unreachable.c:8:7 + testl %edi, %edi +.Ltmp0: + .loc 0 8 7 is_stmt 0 # unreachable.c:8:7 + jmp .LBB0_2 +.Ltmp1: +# %bb.1: + #DEBUG_VALUE: main:argc <- $edi + #DEBUG_VALUE: main:argv <- $rsi + pushq %rax + .cfi_def_cfa_offset 16 +.Ltmp2: + .loc 0 4 3 is_stmt 1 # unreachable.c:4:3 + movl $.L.str, %edi +.Ltmp3: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + callq puts +.Ltmp4: + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 0 0 3 is_stmt 0 # unreachable.c:0:3 + addq $8, %rsp +.Ltmp5: + .cfi_def_cfa_offset 8 +.LBB0_2: + #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi + #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi + .loc 0 10 3 is_stmt 1 # unreachable.c:10:3 + xorl %eax, %eax + retq +.Ltmp6: +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + # -- End function + .type .L.str,@object # @.str + .section .rodata.str1.1,"aMS",@progbits,1 +.L.str: + .asciz "hi" + .size .L.str, 3 + + .section .debug_loclists,"",@progbits + .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length +.Ldebug_list_header_start0: + .short 5 # Version + .byte 8 # Address size + .byte 0 # Segment selector size + .long 2 # Offset entry count +.Lloclists_table_base0: + .long .Ldebug_loc0-.Lloclists_table_base0 + .long .Ldebug_loc1-.Lloclists_table_base0 +.Ldebug_loc0: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 85 # super-register DW_OP_reg5 + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp3-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset + .byte 4 # Loc expr size + .byte 163 # DW_OP_entry_value + .byte 1 # 1 + .byte 85 # super-register DW_OP_reg5 + .byte 159 # DW_OP_stack_value + .byte 0 # DW_LLE_end_of_list +.Ldebug_loc1: + .byte 4 # DW_LLE_offset_pair + .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset + .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset + .byte 1 # Loc expr size + .byte 84 # DW_OP_reg4 + .byte 4 # DW_LLE_offset_pair + .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset + .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset + .byte 4 # Loc expr size + .byte 163 # DW_OP_entry_value + .byte 1 # 1 + .byte 84 # DW_OP_reg4 + .byte 159 # DW_OP_stack_value + .byte 0 # DW_LLE_end_of_list +.Ldebug_list_header_end0: + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 37 # DW_FORM_strx1 + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 114 # DW_AT_str_offsets_base + .byte 23 # DW_FORM_sec_offset + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 27 # DW_AT_comp_dir + .byte 37 # DW_FORM_strx1 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 115 # DW_AT_addr_base + .byte 23 # DW_FORM_sec_offset + .ascii "\214\001" # DW_AT_loclists_base + .byte 23 # DW_FORM_sec_offset + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 52 # DW_TAG_variable + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 2 # DW_AT_location + .byte 24 # DW_FORM_exprloc + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 3 # Abbreviation Code + .byte 1 # DW_TAG_array_type + .byte 1 # DW_CHILDREN_yes + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 4 # Abbreviation Code + .byte 33 # DW_TAG_subrange_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 55 # DW_AT_count + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 5 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 6 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 7 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 32 # DW_AT_inline + .byte 33 # DW_FORM_implicit_const + .byte 1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 8 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 122 # DW_AT_call_all_calls + .byte 25 # DW_FORM_flag_present + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 9 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 34 # DW_FORM_loclistx + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 10 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 0 # DW_CHILDREN_no + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 27 # DW_FORM_addrx + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 11 # Abbreviation Code + .byte 72 # DW_TAG_call_site + .byte 0 # DW_CHILDREN_no + .byte 127 # DW_AT_call_origin + .byte 19 # DW_FORM_ref4 + .byte 125 # DW_AT_call_return_pc + .byte 27 # DW_FORM_addrx + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 12 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 3 # DW_AT_name + .byte 37 # DW_FORM_strx1 + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 60 # DW_AT_declaration + .byte 25 # DW_FORM_flag_present + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 13 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 14 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 15 # Abbreviation Code + .byte 38 # DW_TAG_const_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 1 # Abbrev [1] 0xc:0x95 DW_TAG_compile_unit + .byte 0 # DW_AT_producer + .short 12 # DW_AT_language + .byte 1 # DW_AT_name + .long .Lstr_offsets_base0 # DW_AT_str_offsets_base + .long .Lline_table_start0 # DW_AT_stmt_list + .byte 2 # DW_AT_comp_dir + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .long .Laddr_table_base0 # DW_AT_addr_base + .long .Lloclists_table_base0 # DW_AT_loclists_base + .byte 2 # Abbrev [2] 0x27:0xa DW_TAG_variable + .long 49 # DW_AT_type + .byte 0 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .byte 2 # DW_AT_location + .byte 161 + .byte 0 + .byte 3 # Abbrev [3] 0x31:0xc DW_TAG_array_type + .long 61 # DW_AT_type + .byte 4 # Abbrev [4] 0x36:0x6 DW_TAG_subrange_type + .long 65 # DW_AT_type + .byte 3 # DW_AT_count + .byte 0 # End Of Children Mark + .byte 5 # Abbrev [5] 0x3d:0x4 DW_TAG_base_type + .byte 3 # DW_AT_name + .byte 6 # DW_AT_encoding + .byte 1 # DW_AT_byte_size + .byte 6 # Abbrev [6] 0x41:0x4 DW_TAG_base_type + .byte 4 # DW_AT_name + .byte 8 # DW_AT_byte_size + .byte 7 # DW_AT_encoding + .byte 7 # Abbrev [7] 0x45:0x4 DW_TAG_subprogram + .byte 5 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 3 # DW_AT_decl_line + # DW_AT_inline + .byte 8 # Abbrev [8] 0x49:0x35 DW_TAG_subprogram + .byte 1 # DW_AT_low_pc + .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + # DW_AT_call_all_calls + .byte 7 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + # DW_AT_prototyped + .long 146 # DW_AT_type + # DW_AT_external + .byte 9 # Abbrev [9] 0x58:0x9 DW_TAG_formal_parameter + .byte 0 # DW_AT_location + .byte 9 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 146 # DW_AT_type + .byte 9 # Abbrev [9] 0x61:0x9 DW_TAG_formal_parameter + .byte 1 # DW_AT_location + .byte 10 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 7 # DW_AT_decl_line + .long 150 # DW_AT_type + .byte 10 # Abbrev [10] 0x6a:0xd DW_TAG_inlined_subroutine + .long 69 # DW_AT_abstract_origin + .byte 2 # DW_AT_low_pc + .long .Ltmp5-.Ltmp2 # DW_AT_high_pc + .byte 0 # DW_AT_call_file + .byte 9 # DW_AT_call_line + .byte 5 # DW_AT_call_column + .byte 11 # Abbrev [11] 0x77:0x6 DW_TAG_call_site + .long 126 # DW_AT_call_origin + .byte 3 # DW_AT_call_return_pc + .byte 0 # End Of Children Mark + .byte 12 # Abbrev [12] 0x7e:0xa DW_TAG_subprogram + .byte 6 # DW_AT_name + .byte 0 # DW_AT_decl_file + .byte 1 # DW_AT_decl_line + # DW_AT_prototyped + # DW_AT_declaration + # DW_AT_external + .byte 13 # Abbrev [13] 0x82:0x5 DW_TAG_formal_parameter + .long 136 # DW_AT_type + .byte 0 # End Of Children Mark + .byte 14 # Abbrev [14] 0x88:0x5 DW_TAG_pointer_type + .long 141 # DW_AT_type + .byte 15 # Abbrev [15] 0x8d:0x5 DW_TAG_const_type + .long 61 # DW_AT_type + .byte 5 # Abbrev [5] 0x92:0x4 DW_TAG_base_type + .byte 8 # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + .byte 14 # Abbrev [14] 0x96:0x5 DW_TAG_pointer_type + .long 155 # DW_AT_type + .byte 14 # Abbrev [14] 0x9b:0x5 DW_TAG_pointer_type + .long 61 # DW_AT_type + .byte 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str_offsets,"",@progbits + .long 48 # Length of String Offsets Set + .short 5 + .short 0 +.Lstr_offsets_base0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang version 15.0.7 " # string offset=0 +.Linfo_string1: + .asciz "unreachable.c" # string offset=69 +.Linfo_string2: + .asciz "." # string offset=83 +.Linfo_string3: + .asciz "char" # string offset=85 +.Linfo_string4: + .asciz "__ARRAY_SIZE_TYPE__" # string offset=90 +.Linfo_string5: + .asciz "foo" # string offset=110 +.Linfo_string6: + .asciz "puts" # string offset=114 +.Linfo_string7: + .asciz "main" # string offset=119 +.Linfo_string8: + .asciz "int" # string offset=124 +.Linfo_string9: + .asciz "argc" # string offset=128 +.Linfo_string10: + .asciz "argv" # string offset=133 + .section .debug_str_offsets,"",@progbits + .long .Linfo_string0 + .long .Linfo_string1 + .long .Linfo_string2 + .long .Linfo_string3 + .long .Linfo_string4 + .long .Linfo_string5 + .long .Linfo_string6 + .long .Linfo_string7 + .long .Linfo_string8 + .long .Linfo_string9 + .long .Linfo_string10 + .section .debug_addr,"",@progbits + .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution +.Ldebug_addr_start0: + .short 5 # DWARF version number + .byte 8 # Address size + .byte 0 # Segment selector size +.Laddr_table_base0: + .quad .L.str + .quad .Lfunc_begin0 + .quad .Ltmp2 + .quad .Ltmp4 +.Ldebug_addr_end0: + .section .debug_line,"",@progbits +.Lline_table_start0: diff --git a/bolt/test/X86/dwarf5-df-inlined-subroutine-range-0.test b/bolt/test/X86/dwarf5-df-inlined-subroutine-range-0.test index c0e5cb3339ead84563323b4e111fc9a9dc93bdbb..4ecc66f52ff84bce85d60aa4f2e761b5c71522c0 100644 --- a/bolt/test/X86/dwarf5-df-inlined-subroutine-range-0.test +++ b/bolt/test/X86/dwarf5-df-inlined-subroutine-range-0.test @@ -27,6 +27,6 @@ ; BOLT-MAIN: DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x005a => {0x0000005a} "_Z11doStuffSamei") ; BOLT-MAIN: DW_AT_call_file [DW_FORM_data1] (0x00) ; BOLT-MAIN: DW_AT_call_line [DW_FORM_data1] (16) -; BOLT-MAIN: DW_AT_call_column [DW_FORM_data1] (0x1d) +; BOLT-MAIN: DW_AT_call_column [DW_FORM_data1] (29) ; BOLT-MAIN: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000002) address = ) ; BOLT-MAIN: DW_AT_high_pc [DW_FORM_data4] (0x00000042) diff --git a/bolt/test/X86/dwarf5-df-output-dir-same-name.test b/bolt/test/X86/dwarf5-df-output-dir-same-name.test new file mode 100644 index 0000000000000000000000000000000000000000..4fd42e287a11b71fd0f54cd13f31d0ed94c4e524 --- /dev/null +++ b/bolt/test/X86/dwarf5-df-output-dir-same-name.test @@ -0,0 +1,23 @@ +; RUN: rm -rf %t +; RUN: mkdir %t +; RUN: mkdir -p %t/objects/o1 +; RUN: mkdir -p %t/objects/o2 +; RUN: mkdir %t/dwo +; RUN: cd %t +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-output-dir-same-name-main.s \ +; RUN: -split-dwarf-file=objects/o1/split.dwo -o main.o +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-output-dir-same-name-helper.s \ +; RUN: -split-dwarf-file=objects/o2/split.dwo -o helper.o +; RUN: %clang %cflags -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --dwarf-output-path=%t/dwo +; RUN: ls -l %t/dwo > log +; RUN: llvm-dwarfdump --debug-info main.exe.bolt >> log +; RUN: cat log | FileCheck -check-prefix=BOLT %s + +; Tests that BOLT handles correctly writing out .dwo files to the same directory when input has input where part of path +; is in DW_AT_dwo_name and the .dwo file names are the same. + +; BOLT: split.dwo0.dwo +; BOLT: split.dwo1.dwo +; BOLT: DW_AT_dwo_name ("split.dwo0.dwo") +; BOLT: DW_AT_dwo_name ("split.dwo1.dwo") diff --git a/bolt/test/X86/dwarf5-df-types-dup-dwp-input.test b/bolt/test/X86/dwarf5-df-types-dup-dwp-input.test new file mode 100644 index 0000000000000000000000000000000000000000..036d4c9168ee5aa3f5941abbaa565be6c139e826 --- /dev/null +++ b/bolt/test/X86/dwarf5-df-types-dup-dwp-input.test @@ -0,0 +1,29 @@ +; RUN: rm -rf %t +; RUN: mkdir %t +; RUN: cd %t +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-dup-main.s \ +; RUN: -split-dwarf-file=main.dwo -o main.o +; RUN: llvm-mc -dwarf-version=5 -filetype=obj -triple x86_64-unknown-linux %p/Inputs/dwarf5-df-types-dup-helper.s \ +; RUN: -split-dwarf-file=helper.dwo -o helper.o +; RUN: %clang %cflags -gdwarf-5 -gsplit-dwarf=split main.o helper.o -o main.exe +; RUN: llvm-dwp -e main.exe -o main.exe.dwp +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections +; RUN: llvm-dwarfdump --debug-info -r 0 main.dwo.dwo | FileCheck -check-prefix=BOLT-DWO-DWO-MAIN %s +; RUN: llvm-dwarfdump --debug-info -r 0 helper.dwo.dwo | FileCheck -check-prefix=BOLT-DWO-DWO-HELPER %s + +; Tests that BOLT correctly handles DWARF5 DWP file as input. Output has correct CU, and all the type units are written out. + +; BOLT-DWO-DWO-MAIN: debug_info.dwo +; BOLT-DWO-DWO-MAIN-NEXT: type_signature = 0x49dc260088be7e56 +; BOLT-DWO-DWO-MAIN: type_signature = 0x104ec427d2ebea6f +; BOLT-DWO-DWO-MAIN: type_signature = 0xca1e65a66d92b970 +; BOLT-DWO-DWO-MAIN: Compile Unit +; BOLT-DWO-DWO-MAIN-SAME: DWO_id = 0x52bda211bf6d26b7 +; BOLT-DWO-DWO-MAIN-NOT: Compile Unit +; BOLT-DWO-DWO-HELPER: debug_info.dwo +; BOLT-DWO-DWO-HELPER-NEXT: type_signature = 0x49dc260088be7e56 +; BOLT-DWO-DWO-HELPER: type_signature = 0x104ec427d2ebea6f +; BOLT-DWO-DWO-HELPER: type_signature = 0xca1e65a66d92b970 +; BOLT-DWO-DWO-HELPER: Compile Unit +; BOLT-DWO-DWO-HELPER-SAME: DWO_id = 0x1dbb67285a49634c +; BOLT-DWO-DWO-HELPER-NOT: Compile Unit diff --git a/bolt/test/X86/dwarf5-split-dwarf4-monolithic.test b/bolt/test/X86/dwarf5-split-dwarf4-monolithic.test index c0f5705087ccf4be4fe0bb3cd4c85750b76d55da..6fc0825cd2fae4dc4e573a946adcc37128daced9 100644 --- a/bolt/test/X86/dwarf5-split-dwarf4-monolithic.test +++ b/bolt/test/X86/dwarf5-split-dwarf4-monolithic.test @@ -244,7 +244,7 @@ # POSTCHECK-DWO-MAIN: DW_TAG_inlined_subroutine [12] # POSTCHECK-DWO-MAIN-NEXT: DW_AT_abstract_origin [DW_FORM_ref4] # POSTCHECK-DWO-MAIN-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x2) rangelist = 0x00000020 -# Encded as a pair. So it's offset from base address. +# Encoded as a pair. So it's offset from base address. # POSTCHECK-DWO-MAIN-NEXT: [0x0000000000000003, 0x0000000000000007) # POSTCHECK-DWO-MAIN-NEXT: [0x0000000000000013, 0x0000000000000019)) diff --git a/bolt/test/X86/dynrelocs.s b/bolt/test/X86/dynrelocs.s index 7e6a75a9dd3a9b2e630d49fac066f107aca2afb8..6d771df4b4ff81957344738e6cc38b39f9738ac7 100644 --- a/bolt/test/X86/dynrelocs.s +++ b/bolt/test/X86/dynrelocs.s @@ -12,7 +12,7 @@ # but it can happen. In these cases we cannot corrupt the # output binary by writing out dynamic relocs incorrectly. The linker # avoids emitting relocs against read-only sections but we override -# this behvior with the -z notext flag. During runtime, these pages +# this behavior with the -z notext flag. During runtime, these pages # are mapped with write permission and then changed to read-only after # the dynamic linker finishes processing the dynamic relocs. diff --git a/bolt/test/X86/exceptions-args.test b/bolt/test/X86/exceptions-args.test index fe9423896f68d918cfd4b3445179a80b67524d5d..3a4fa2f0eac13b3fddc79ad2e043938172653ffb 100644 --- a/bolt/test/X86/exceptions-args.test +++ b/bolt/test/X86/exceptions-args.test @@ -3,7 +3,7 @@ RUN: %clang %cflags %p/../Inputs/stub.c -fPIC -pie -shared -o %t.so RUN: %clangxx %cxxflags -no-pie %p/Inputs/exc_args.s -o %t %t.so -Wl,-z,notext -RUN: llvm-bolt %t -o /dev/null --print-finalized --print-only=main | FileCheck %s +RUN: llvm-bolt %t -o %t.null --print-finalized --print-only=main | FileCheck %s CHECK: Binary Function "main" after finalize-functions CHECK: callq _Z3fooiiiiiiii {{.*}} GNU_args_size = 16 diff --git a/bolt/test/X86/fallthrough-to-noop.test b/bolt/test/X86/fallthrough-to-noop.test index 909af8ff423a66369f846e29d009aa87b1319738..2055ca603043ab42c2f6c63c8a30152eb7347bb0 100644 --- a/bolt/test/X86/fallthrough-to-noop.test +++ b/bolt/test/X86/fallthrough-to-noop.test @@ -1,4 +1,4 @@ -# Check that profile data for the fall-through jump is not ingored when there is +# Check that profile data for the fall-through jump is not ignored when there is # a conditional jump followed by a no-op. RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \ diff --git a/bolt/test/X86/fptr-addend-pcrel.s b/bolt/test/X86/fptr-addend-pcrel.s index 0e84a8fd36004d4dc3ec718bdcf059e1d5641908..054d177ed55fe015118b153ac996c8bb25b24aec 100644 --- a/bolt/test/X86/fptr-addend-pcrel.s +++ b/bolt/test/X86/fptr-addend-pcrel.s @@ -6,7 +6,7 @@ # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o # RUN: llvm-strip --strip-unneeded %t.o # RUN: ld.lld %t.o -o %t.exe -q -# RUN: llvm-bolt %t.exe --relocs -o /dev/null --print-only=foo --print-disasm \ +# RUN: llvm-bolt %t.exe --relocs -o %t.null --print-only=foo --print-disasm \ # RUN: | FileCheck %s .text diff --git a/bolt/test/X86/gotpcrelx.s b/bolt/test/X86/gotpcrelx.s index 090a433143156dc0267a769ac226b17774d5f231..ddac5ebda0caf0cd849465865cd6c0e8a97e6f49 100644 --- a/bolt/test/X86/gotpcrelx.s +++ b/bolt/test/X86/gotpcrelx.s @@ -12,9 +12,9 @@ # RUN: ld.lld %t.o -o %t.no-relax.exe -q --no-relax # RUN: llvm-bolt %t.exe --relocs -o %t.out --print-cfg --print-only=_start \ # RUN: |& FileCheck --check-prefix=BOLT %s -# RUN: llvm-bolt %t.pie.exe -o /dev/null --print-cfg --print-only=_start \ +# RUN: llvm-bolt %t.pie.exe -o %t.null --print-cfg --print-only=_start \ # RUN: |& FileCheck --check-prefix=PIE-BOLT %s -# RUN: llvm-bolt %t.no-relax.exe -o /dev/null --print-cfg --print-only=_start \ +# RUN: llvm-bolt %t.no-relax.exe -o %t.null --print-cfg --print-only=_start \ # RUN: |& FileCheck --check-prefix=NO-RELAX-BOLT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex \ # RUN: %t.out | FileCheck --check-prefix=DISASM %s diff --git a/bolt/test/X86/icp-inline.s b/bolt/test/X86/icp-inline.s index 318318f96215c26a67c2fb942218ee4a72cc18eb..3c863833449fa6eb6318a351cf2dd4aa88dd6274 100644 --- a/bolt/test/X86/icp-inline.s +++ b/bolt/test/X86/icp-inline.s @@ -20,7 +20,7 @@ # Without -icp-inline option, ICP is performed # RUN: llvm-bolt %t.exe --icp=calls --icp-calls-topn=1 --inline-small-functions\ -# RUN: -o /dev/null --lite=0 \ +# RUN: -o %t.null --lite=0 \ # RUN: --inline-small-functions-bytes=4 --print-icp --data %t.fdata \ # RUN: | FileCheck %s --check-prefix=CHECK-NO-ICP-INLINE # CHECK-NO-ICP-INLINE: Binary Function "main" after indirect-call-promotion @@ -29,7 +29,7 @@ # With -icp-inline option, ICP is not performed (size of bar > inline threshold) # RUN: llvm-bolt %t.exe --icp=calls --icp-calls-topn=1 --inline-small-functions\ -# RUN: -o /dev/null --lite=0 \ +# RUN: -o %t.null --lite=0 \ # RUN: --inline-small-functions-bytes=4 --icp-inline --print-icp \ # RUN: --data %t.fdata | FileCheck %s --check-prefix=CHECK-ICP-INLINE # CHECK-ICP-INLINE: Binary Function "main" after indirect-call-promotion diff --git a/bolt/test/X86/indirect-goto.test b/bolt/test/X86/indirect-goto.test index e2565ff889327aa61728a63127c18d2829f06cb3..bbc11e7d3317154add8bd436f4ceee969e6c2bee 100644 --- a/bolt/test/X86/indirect-goto.test +++ b/bolt/test/X86/indirect-goto.test @@ -1,6 +1,6 @@ # Check llvm-bolt processes binaries compiled from sources that use indirect goto. RUN: %clang %cflags -no-pie %S/Inputs/indirect_goto.c -Wl,-q -o %t -RUN: llvm-bolt %t -o /dev/null --relocs=1 --print-cfg --print-only=main \ +RUN: llvm-bolt %t -o %t.null --relocs=1 --print-cfg --print-only=main \ RUN: --strict \ RUN: 2>&1 | FileCheck %s diff --git a/bolt/test/X86/is-strip.s b/bolt/test/X86/is-strip.s index dca7b03a61380f7ce21430d348f34495b45fdbe3..df12986efc42d7238e71f5c089fe274abb0b83a7 100644 --- a/bolt/test/X86/is-strip.s +++ b/bolt/test/X86/is-strip.s @@ -4,7 +4,7 @@ # RUN: llvm-bolt %t -o %t.out 2>&1 | FileCheck %s -check-prefix=CHECK-NOSTRIP # RUN: cp %t %t.stripped # RUN: llvm-strip -s %t.stripped -# RUN: not llvm-bolt %t.stripped -o /dev/null 2>&1 | FileCheck %s -check-prefix=CHECK-STRIP +# RUN: not llvm-bolt %t.stripped -o %t.null 2>&1 | FileCheck %s -check-prefix=CHECK-STRIP # RUN: llvm-bolt %t.stripped -o %t.out --allow-stripped 2>&1 | FileCheck %s -check-prefix=CHECK-NOSTRIP # CHECK-NOSTRIP-NOT: BOLT-ERROR: stripped binaries are not supported. diff --git a/bolt/test/X86/jmpjmp.test b/bolt/test/X86/jmpjmp.test index b512001c3c4464360ed26e1126e875b86c1e1518..cc6107f47812757a3343c1158e35b3eeb3f53c68 100644 --- a/bolt/test/X86/jmpjmp.test +++ b/bolt/test/X86/jmpjmp.test @@ -3,7 +3,7 @@ RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %S/Inputs/jmpjmp.s -o %t.o RUN: %clang %cflags %t.o -o %t.exe -RUN: llvm-bolt %t.exe -o /dev/null --print-cfg 2>&1 | FileCheck %s +RUN: llvm-bolt %t.exe -o %t.null --print-cfg 2>&1 | FileCheck %s CHECK: Binary Function "testfunc" CHECK: State : CFG constructed diff --git a/bolt/test/X86/jump-table-footprint-reduction.test b/bolt/test/X86/jump-table-footprint-reduction.test index 5a007c626eca898593b87ca754a37d3d6cce8af2..4e0f9b16818d3f81abb0a8379381ee4a417db6fc 100644 --- a/bolt/test/X86/jump-table-footprint-reduction.test +++ b/bolt/test/X86/jump-table-footprint-reduction.test @@ -6,7 +6,7 @@ RUN: %S/Inputs/jump_table_footprint_reduction.s -o %t.o RUN: link_fdata %S/Inputs/jump_table_footprint_reduction.s %t.o %t.fdata --nmtool llvm-nm RUN: llvm-strip --strip-unneeded %t.o RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q -RUN: llvm-bolt %t.exe --data %t.fdata -o /dev/null \ +RUN: llvm-bolt %t.exe --data %t.fdata -o %t.null \ RUN: --jump-tables=move --jt-footprint-reduction --assume-abi --relocs \ RUN: | FileCheck %s diff --git a/bolt/test/X86/jump-table-icp.test b/bolt/test/X86/jump-table-icp.test index 34339dc327fae52b88bf09602ab32a2dba249af5..5b989d18018b0505f7293c6d00d238e5b1e101d6 100644 --- a/bolt/test/X86/jump-table-icp.test +++ b/bolt/test/X86/jump-table-icp.test @@ -37,12 +37,14 @@ CHECK: Successors: .Ltmp{{.*}} (mispreds: 189, count: 189), .LFT{{.*}} (mispre CHECK: .LFT{{.*}} (4 instructions, align : 1) CHECK-NEXT: Exec Count : 881 CHECK: Predecessors: .LBB{{.*}} -CHECK: Successors: .Ltmp{{.*}} (mispreds: 138, count: 155), .Ltmp{{.*}} (mispreds: 0, count: 726) +CHECK: je {{.*}} # Offset: 28 +CHECK-NEXT: Successors: .Ltmp{{.*}} (mispreds: 138, count: 155), .Ltmp{{.*}} (mispreds: 0, count: 726) CHECK: .Ltmp{{.*}} (1 instructions, align : 1) CHECK-NEXT: Exec Count : 726 CHECK: Predecessors: .LFT{{.*}} -CHECK: Successors: .L{{.*}} (mispreds: 126, count: 157), .L{{.*}} (mispreds: 140, count: 156), .L{{.*}} (mispreds: 134, count: 152), .L{{.*}} (mispreds: 137, count: 150), .L{{.*}} (mispreds: 129, count: 148), .L{{.*}} (mispreds: 0, count: 0) +CHECK: jmpq {{.*}} # Offset: 28 +CHECK-NEXT: Successors: .L{{.*}} (mispreds: 126, count: 157), .L{{.*}} (mispreds: 140, count: 156), .L{{.*}} (mispreds: 134, count: 152), .L{{.*}} (mispreds: 137, count: 150), .L{{.*}} (mispreds: 129, count: 148), .L{{.*}} (mispreds: 0, count: 0) CHECK: .Ltmp{{.*}} (5 instructions, align : 1) CHECK-NEXT: Exec Count : 167 diff --git a/bolt/test/X86/jump-table-pic-order.test b/bolt/test/X86/jump-table-pic-order.test index 0206e831f2441d8200c34fe619784bca8813e03a..59c0af252b07b39fc020457914623c66563a3b2d 100644 --- a/bolt/test/X86/jump-table-pic-order.test +++ b/bolt/test/X86/jump-table-pic-order.test @@ -2,7 +2,7 @@ # in the same order as they appear in the input code. RUN: %clang %cflags %S/Inputs/jump-table-pic.s -o %t.exe -Wl,-q -RUN: llvm-bolt %t.exe --strict --print-cfg --print-only=main -o /dev/null \ +RUN: llvm-bolt %t.exe --strict --print-cfg --print-only=main -o %t.null \ RUN: | FileCheck %s CHECK: BB Layout : {{.*, .*, .*,}} [[BB4to6:.*, .*, .*]] diff --git a/bolt/test/X86/jump-table-reference.test b/bolt/test/X86/jump-table-reference.test index 6a019b26cf63d605b3520d59696284440940d98d..9d33c0d5e72718ed30a8ecdc4a57887f7d3237d4 100644 --- a/bolt/test/X86/jump-table-reference.test +++ b/bolt/test/X86/jump-table-reference.test @@ -1,6 +1,6 @@ # Verifies that BOLT detects fixed destination of indirect jump RUN: %clang %cflags -no-pie %S/Inputs/jump_table_reference.s -Wl,-q -o %t -RUN: llvm-bolt %t --relocs -o /dev/null 2>&1 | FileCheck %s +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/keep-nops.s b/bolt/test/X86/keep-nops.s new file mode 100644 index 0000000000000000000000000000000000000000..37da2ff07b9b79827a3ff43e43897bc53b792fe3 --- /dev/null +++ b/bolt/test/X86/keep-nops.s @@ -0,0 +1,69 @@ +## Check that BOLT preserves NOP instructions of different sizes correctly. + +# REQUIRES: system-linux + +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t.exe -q +# RUN: llvm-bolt %t.exe -o %t.bolt.exe --keep-nops --relocs --print-finalized \ +# RUN: |& FileCheck --check-prefix=CHECK-BOLT %s +# RUN: llvm-objdump -d %t.bolt.exe | FileCheck %s + + .text + .globl _start + .type _start,@function +_start: + .cfi_startproc + .nops 1 + .nops 2 + .nops 3 + .nops 4 + .nops 5 + .nops 6 + .nops 7 + .nops 8 + .nops 9 + .nops 10 + .nops 11 + .nops 12 + .nops 13 + .nops 14 + .nops 15 + +# CHECK: <_start>: +# CHECK-NEXT: 90 +# CHECK-NEXT: 66 90 +# CHECK-NEXT: 0f 1f 00 +# CHECK-NEXT: 0f 1f 40 00 +# CHECK-NEXT: 0f 1f 44 00 00 +# CHECK-NEXT: 66 0f 1f 44 00 00 +# CHECK-NEXT: 0f 1f 80 00 00 00 00 +# CHECK-NEXT: 0f 1f 84 00 00 00 00 00 +# CHECK-NEXT: 66 0f 1f 84 00 00 00 00 00 +# CHECK-NEXT: 66 2e 0f 1f 84 00 00 00 00 00 +# CHECK-NEXT: 66 66 2e 0f 1f 84 00 00 00 00 00 +# CHECK-NEXT: 66 66 66 2e 0f 1f 84 00 00 00 00 00 +# CHECK-NEXT: 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 +# CHECK-NEXT: 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 +# CHECK-NEXT: 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 + +# CHECK-BOLT: Size: 1 +# CHECK-BOLT-NEXT: Size: 2 +# CHECK-BOLT-NEXT: Size: 3 +# CHECK-BOLT-NEXT: Size: 4 +# CHECK-BOLT-NEXT: Size: 5 +# CHECK-BOLT-NEXT: Size: 6 +# CHECK-BOLT-NEXT: Size: 7 +# CHECK-BOLT-NEXT: Size: 8 +# CHECK-BOLT-NEXT: Size: 9 +# CHECK-BOLT-NEXT: Size: 10 +# CHECK-BOLT-NEXT: Size: 11 +# CHECK-BOLT-NEXT: Size: 12 +# CHECK-BOLT-NEXT: Size: 13 +# CHECK-BOLT-NEXT: Size: 14 +# CHECK-BOLT-NEXT: Size: 15 + +# Needed for relocation mode. + .reloc 0, R_X86_64_NONE + + .size _start, .-_start + .cfi_endproc diff --git a/bolt/test/X86/layout-heuristic.test b/bolt/test/X86/layout-heuristic.test index bec0ef01c85f697d2fa9a8e87444e387eb2ec8ba..3d24e1aad139a5f113abbb20036620a130bf3ef3 100644 --- a/bolt/test/X86/layout-heuristic.test +++ b/bolt/test/X86/layout-heuristic.test @@ -5,7 +5,7 @@ # Also checks that llvm-bolt disassembler and CFG builder is working properly. RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe -RUN: llvm-bolt %t.exe -o /dev/null --data %p/Inputs/blarge.fdata \ +RUN: llvm-bolt %t.exe -o %t.null --data %p/Inputs/blarge.fdata \ RUN: --reorder-blocks=normal --print-cfg --print-reordered \ RUN: --funcs=main,SolveCubic,usqrt --sequential-disassembly \ RUN: 2>&1 | FileCheck %s diff --git a/bolt/test/X86/line-number.test b/bolt/test/X86/line-number.test index 9c9d98df2f1db66ce5b99e783e3566283b084204..b039962643d4064e4f0e8714a76563f3de295650 100644 --- a/bolt/test/X86/line-number.test +++ b/bolt/test/X86/line-number.test @@ -1,7 +1,7 @@ # Verifies that the extraction of DWARF line number information is correct. RUN: %clangxx %cxxflags %S/Inputs/linenumber.cpp -g -o %t -RUN: llvm-bolt %t -o /dev/null --print-reordered --update-debug-sections \ +RUN: llvm-bolt %t -o %t.null --print-reordered --update-debug-sections \ RUN: --print-debug-info --reorder-blocks=reverse --sequential-disassembly \ RUN: 2>&1 | FileCheck %s diff --git a/bolt/test/X86/lto-name-match.s b/bolt/test/X86/lto-name-match.s index 32d2e0ae40dbed6496b6c8de67cafc9ee5d8a54c..92aad7d5ec62c321bd20285dc238698b4dd7efb0 100644 --- a/bolt/test/X86/lto-name-match.s +++ b/bolt/test/X86/lto-name-match.s @@ -2,7 +2,7 @@ # RUN: link_fdata %s %t.o %t.fdata # RUN: llvm-strip --strip-unneeded %t.o # RUN: %clang %cflags %t.o -o %t.exe -# RUN: llvm-bolt %t.exe --data %t.fdata -o /dev/null | FileCheck %s +# RUN: llvm-bolt %t.exe --data %t.fdata -o %t.null | FileCheck %s ## Check that profile is correctly matched by functions with variable suffixes. ## E.g., LTO-generated name foo.llvm.123 should match foo.llvm.*. diff --git a/bolt/test/X86/no-entry-reordering.test b/bolt/test/X86/no-entry-reordering.test index 8f2e2c41a4c349012c3a87b16f66f0f0424a2bc5..a2638e1388c9a722055e53b50bbe010d3df1e72b 100644 --- a/bolt/test/X86/no-entry-reordering.test +++ b/bolt/test/X86/no-entry-reordering.test @@ -5,7 +5,7 @@ RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %S/Inputs/entry.s -o % RUN: link_fdata %S/Inputs/entry.s %t.o %t.fdata --nmtool llvm-nm RUN: llvm-strip --strip-unneeded %t.o RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib -RUN: llvm-bolt %t.exe --data %t.fdata -o /dev/null --reorder-blocks=normal \ +RUN: llvm-bolt %t.exe --data %t.fdata -o %t.null --reorder-blocks=normal \ RUN: --funcs=_start --print-reordered --sequential-disassembly | FileCheck %s CHECK: BB Layout : .LBB00, {{.*}} diff --git a/bolt/test/X86/pre-aggregated-perf.test b/bolt/test/X86/pre-aggregated-perf.test index 720d179d1c6b8092b8521bc129665f62e472cbb6..e8c3f64239a27ddb831f801ccf76b09d44fda6b1 100644 --- a/bolt/test/X86/pre-aggregated-perf.test +++ b/bolt/test/X86/pre-aggregated-perf.test @@ -46,7 +46,7 @@ PERF2BOLT: 1 usqrt a 1 usqrt 10 0 22 NEWFORMAT: - name: 'frame_dummy/1' NEWFORMAT: fid: 3 -NEWFORMAT: hash: 0x24496F7F9594E89F +NEWFORMAT: hash: 0x28C72085C0BD8D37 NEWFORMAT: exec: 1 NEWFORMAT: - name: usqrt diff --git a/bolt/test/X86/pt_gnu_relro.s b/bolt/test/X86/pt_gnu_relro.s index 4d5c2b527188f172b0b3cffc6b683f1edd15b445..ad8d475c83724b6281e2e9ea58e8b3618b0d67f5 100644 --- a/bolt/test/X86/pt_gnu_relro.s +++ b/bolt/test/X86/pt_gnu_relro.s @@ -21,7 +21,7 @@ # READELF: Section to Segment mapping: # READELF: 04 .got -# RUN: llvm-bolt %t.exe --relocs -o /dev/null -v=1 \ +# RUN: llvm-bolt %t.exe --relocs -o %t.null -v=1 \ # RUN: |& FileCheck --check-prefix=BOLT %s # BOLT: BOLT-INFO: marking .got as GNU_RELRO diff --git a/bolt/test/X86/reader-stale-yaml.test b/bolt/test/X86/reader-stale-yaml.test index 5231032f4f4a75da976c093865295d61fd026bdf..ad0945b1493407c99dafe3c22ad9190f3d4dd236 100644 --- a/bolt/test/X86/reader-stale-yaml.test +++ b/bolt/test/X86/reader-stale-yaml.test @@ -1,23 +1,33 @@ # This script checks that YamlProfileReader in llvm-bolt is reading data # correctly and stale data is corrected by profile inference. +REQUIRES: asserts RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe # Testing "usqrt" -RUN: llvm-bolt %t.exe -o /dev/null --b %p/Inputs/blarge_profile_stale.yaml \ +RUN: llvm-bolt %t.exe -o %t.null --b %p/Inputs/blarge_profile_stale.yaml \ RUN: --print-cfg --print-only=usqrt --infer-stale-profile=1 \ -RUN: --profile-ignore-hash=1 --profile-use-dfs=0 2>&1 | FileCheck %s -check-prefix=CHECK1 +RUN: --profile-ignore-hash=1 --profile-use-dfs=0 --debug-only=bolt-prof 2>&1 | FileCheck %s -check-prefix=CHECK1 # Testing "SolveCubic" -RUN: llvm-bolt %t.exe -o /dev/null --b %p/Inputs/blarge_profile_stale.yaml \ +RUN: llvm-bolt %t.exe -o %t.null --b %p/Inputs/blarge_profile_stale.yaml \ RUN: --print-cfg --print-only=SolveCubic --infer-stale-profile=1 \ -RUN: --profile-ignore-hash=1 --profile-use-dfs=0 2>&1 | FileCheck %s -check-prefix=CHECK2 +RUN: --profile-ignore-hash=1 --profile-use-dfs=0 --debug-only=bolt-prof 2>&1 | FileCheck %s -check-prefix=CHECK2 # Function "usqrt" has stale profile, since the number of blocks in the profile # (nblocks=6) does not match the size of the CFG in the binary. The entry # block (bid=0) has an incorrect (missing) count, which should be inferred by # the algorithm. -# Verify that yaml reader works as expected. +# Verify inference details. CHECK1: pre-processing profile using YAML profile reader +CHECK1: applying profile inference for "usqrt" +CHECK1: Matched yaml block (bid = 0) with hash 1111111111111111 to BB (index = 0) with hash 36007ba1d80c0000 +CHECK1-NEXT: loose match +CHECK1: Matched yaml block (bid = 1) with hash d70d7a64320e0010 to BB (index = 1) with hash d70d7a64320e0010 +CHECK1-NEXT: exact match +CHECK1: Matched yaml block (bid = 3) with hash 5c06705524800039 to BB (index = 3) with hash 5c06705524800039 +CHECK1-NEXT: exact match + +# Verify that yaml reader works as expected. CHECK1: Binary Function "usqrt" after building cfg { CHECK1: State : CFG constructed CHECK1: Address : 0x401170 @@ -28,6 +38,7 @@ CHECK1: BB Count : 5 CHECK1: Exec Count : 20 CHECK1: Branch Count: 640 CHECK1: } + # Verify block counts. CHECK1: .LBB01 (4 instructions, align : 1) CHECK1: Successors: .Ltmp[[#BB13:]] (mispreds: 0, count: 20) @@ -48,7 +59,21 @@ CHECK1: inferred profile for 2 (100.00% of profiled, 100.00% of stale) function # verifies that the inference is able to match two blocks (bid=1 and bid=13) # using "loose" hashes and then correctly propagate the counts. +# Verify inference details. CHECK2: pre-processing profile using YAML profile reader +CHECK2: applying profile inference for "SolveCubic" +CHECK2: Matched yaml block (bid = 0) with hash 4600940a609c0000 to BB (index = 0) with hash 4600940a609c0000 +CHECK2-NEXT: exact match +CHECK2: Matched yaml block (bid = 1) with hash 167a1f084f130088 to BB (index = 1) with hash 167a1f084f130088 +CHECK2-NEXT: exact match +CHECK2: Matched yaml block (bid = 13) with hash a8d50000f81902a7 to BB (index = 13) with hash a8d5aa43f81902a7 +CHECK2-NEXT: loose match +CHECK2: Matched yaml block (bid = 3) with hash c516000073dc00a0 to BB (index = 3) with hash c516b1c973dc00a0 +CHECK2-NEXT: loose match +CHECK2: Matched yaml block (bid = 5) with hash 6446e1ea500111 to BB (index = 5) with hash 6446e1ea500111 +CHECK2-NEXT: exact match + +# Verify that yaml reader works as expected. CHECK2: Binary Function "SolveCubic" after building cfg { CHECK2: State : CFG constructed CHECK2: Address : 0x400e00 @@ -58,6 +83,7 @@ CHECK2: IsSimple : 1 CHECK2: BB Count : 18 CHECK2: Exec Count : 151 CHECK2: Branch Count: 552 + # Verify block counts. CHECK2: .LBB00 (43 instructions, align : 1) CHECK2: Successors: .Ltmp[[#BB7:]] (mispreds: 0, count: 0), .LFT[[#BB1:]] (mispreds: 0, count: 151) diff --git a/bolt/test/X86/reader.test b/bolt/test/X86/reader.test index cc0bfe43511d341dedcd88e95e66d803eabe7501..308b97e30bb5602102eeb3fef29d8f1ddcfe9533 100644 --- a/bolt/test/X86/reader.test +++ b/bolt/test/X86/reader.test @@ -1,7 +1,7 @@ # This script checks that DataReader in llvm-bolt is reading data correctly RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe -RUN: llvm-bolt %t.exe -o /dev/null --data %p/Inputs/blarge.fdata --dump-data \ +RUN: llvm-bolt %t.exe -o %t.null --data %p/Inputs/blarge.fdata --dump-data \ RUN: 2>&1 | sort | FileCheck %s -check-prefix=CHECK CHECK: main 1105 SolveCubic 0 0 151 diff --git a/bolt/test/X86/sctc-bug.test b/bolt/test/X86/sctc-bug.test index a4568aca14144ac51775cf33e7a37bbb616cee27..1b581df23749033ad7728cf739f8b54aa16ebc93 100644 --- a/bolt/test/X86/sctc-bug.test +++ b/bolt/test/X86/sctc-bug.test @@ -1,7 +1,7 @@ # Check that we don't accidentally optimize out a tail call. RUN: %clang %cflags %S/Inputs/sctc_bug.s -o %t -RUN: llvm-bolt %t -o /dev/null --funcs=main --print-after-lowering \ +RUN: llvm-bolt %t -o %t.null --funcs=main --print-after-lowering \ RUN: 2>&1 | FileCheck %s CHECK: jp .L{{.*}} diff --git a/bolt/test/X86/sctc-bug2.test b/bolt/test/X86/sctc-bug2.test index cf23b38249ace34d5bc9e33fa5a6c2f2d4cfd13d..0e235564dc3bd99ee5d31ec17e041c5b9858b09c 100644 --- a/bolt/test/X86/sctc-bug2.test +++ b/bolt/test/X86/sctc-bug2.test @@ -1,7 +1,7 @@ # Check that conditional tail call is not treated as a regular tail call by SCTC. RUN: %clang %cflags %S/Inputs/sctc_bug2.s -o %t -RUN: llvm-bolt %t -o /dev/null --funcs=main --print-after-lowering \ +RUN: llvm-bolt %t -o %t.null --funcs=main --print-after-lowering \ RUN: --sequential-disassembly 2>&1 | FileCheck %s CHECK: .LFT1 diff --git a/bolt/test/X86/sctc-bug3.test b/bolt/test/X86/sctc-bug3.test index fce69bf86a4660d184b54e7f83fd0c136033d165..69c8c454284445dc3b516f9d958cac0d8110cc18 100644 --- a/bolt/test/X86/sctc-bug3.test +++ b/bolt/test/X86/sctc-bug3.test @@ -1,7 +1,7 @@ # Check that we don't accidentally optimize out a tail call. RUN: %clang %cflags %S/Inputs/sctc_bug3.s -o %t -RUN: llvm-bolt %t -o /dev/null --funcs=main --print-after-lowering \ +RUN: llvm-bolt %t -o %t.null --funcs=main --print-after-lowering \ RUN: --sequential-disassembly 2>&1 | FileCheck %s CHECK: .LBB00 (1 instructions, align : 1) diff --git a/bolt/test/X86/sctc-bug4.test b/bolt/test/X86/sctc-bug4.test index b449be1b33892f0dfa09c2da192b841df95091a7..00f5ee429b635e089c9bcf0693ba3a0e99fe6f07 100644 --- a/bolt/test/X86/sctc-bug4.test +++ b/bolt/test/X86/sctc-bug4.test @@ -1,7 +1,7 @@ # Check that fallthrough blocks are handled properly. RUN: %clang %cflags %S/Inputs/sctc_bug4.s -o %t -RUN: llvm-bolt %t -o /dev/null \ +RUN: llvm-bolt %t -o %t.null \ RUN: -funcs=test_func -print-sctc -sequential-disassembly 2>&1 | FileCheck %s CHECK: .Ltmp2 (3 instructions, align : 1) diff --git a/bolt/test/X86/section-end-sym.s b/bolt/test/X86/section-end-sym.s index cf1723c2563ae366bf5393b1bbbc789ea6338e2a..545cf37263da56f993304b1bc5e3a1d3542fc4c0 100644 --- a/bolt/test/X86/section-end-sym.s +++ b/bolt/test/X86/section-end-sym.s @@ -5,7 +5,7 @@ # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o # RUN: ld.lld %t.o -o %t.exe -q -# RUN: llvm-bolt %t.exe -o /dev/null --print-cfg --debug-only=bolt 2>&1 \ +# RUN: llvm-bolt %t.exe -o %t.null --print-cfg --debug-only=bolt 2>&1 \ # RUN: | FileCheck %s # CHECK: considering symbol etext for function diff --git a/bolt/test/X86/tail-duplication-cache.s b/bolt/test/X86/tail-duplication-cache.s index d7955eff9ead9bd480b90ad8c0282af5c28ad82c..c3890c0337dd70f858a0d2360f16546b485c49a9 100644 --- a/bolt/test/X86/tail-duplication-cache.s +++ b/bolt/test/X86/tail-duplication-cache.s @@ -11,7 +11,7 @@ # RUN: --print-finalized --tail-duplication=cache -o %t.out2 \ # RUN: | FileCheck --check-prefix="CHECK2" %s -# A test where the tail is duplicated to eliminate an uncoditional jump +# A test where the tail is duplicated to eliminate an unconditional jump # FDATA: 1 main #.BB0_br# 1 main #.BB4# 0 100 # FDATA: 1 main #.BB0_br# 1 main #.BB1# 0 100 # FDATA: 1 main #.BB1_br# 1 main #.BB3# 0 50 diff --git a/bolt/test/X86/tailcall.test b/bolt/test/X86/tailcall.test index 09a31a6016078ec5f3f2b2c42fdc86be8d7403f3..83b69bd25ab922af7b3e14151a9271ace7483492 100644 --- a/bolt/test/X86/tailcall.test +++ b/bolt/test/X86/tailcall.test @@ -2,7 +2,7 @@ # in control flow graph. RUN: %clang %cflags %S/Inputs/tailcall.s -o %t.exe -RUN: llvm-bolt %t.exe -o /dev/null --print-cfg 2>&1 | FileCheck %s +RUN: llvm-bolt %t.exe -o %t.null --print-cfg 2>&1 | FileCheck %s CHECK: Binary Function "foo" CHECK: jmp bar # TAILCALL diff --git a/bolt/test/X86/vararg.test b/bolt/test/X86/vararg.test index bfbc23efb9dcd95429d828d2eab05359a500fdc6..5df4f3da04214698b8382ec71d2f9059773a12b8 100644 --- a/bolt/test/X86/vararg.test +++ b/bolt/test/X86/vararg.test @@ -5,7 +5,7 @@ REQUIRES: x86_64-linux RUN: %clangxx %cxxflags -no-pie %p/../Inputs/vararg.s -o %t -Wl,-q -RUN: llvm-bolt %t -o /dev/null --print-cfg --print-only=.*printf.* |& FileCheck %s +RUN: llvm-bolt %t -o %t.null --print-cfg --print-only=.*printf.* |& FileCheck %s CHECK: IsSimple : 0 CHECK: Entry Point diff --git a/bolt/test/X86/yaml-multiple-profiles.test b/bolt/test/X86/yaml-multiple-profiles.test index d94f8dec02f14ba0df04c83585a1ddf95e0bed1b..5684da4226be62f8318c03b04c7621774200daea 100644 --- a/bolt/test/X86/yaml-multiple-profiles.test +++ b/bolt/test/X86/yaml-multiple-profiles.test @@ -5,7 +5,7 @@ # RUN: split-file %s %t # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o # RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib -# RUN: llvm-bolt %t.exe -o /dev/null --data %t/profile.yaml \ +# RUN: llvm-bolt %t.exe -o %t.null --data %t/profile.yaml \ # RUN: --profile-ignore-hash -v=1 2>&1 | FileCheck %s # CHECK: BOLT-WARNING: dropping duplicate profile for main_alias(*2) #--- main.s diff --git a/bolt/test/bad-exe.test b/bolt/test/bad-exe.test index 6c1d8cc58dad0f6b9abac626741e8674300f479f..fadc5590ea86f3cf0b77b982640f658553141aea 100644 --- a/bolt/test/bad-exe.test +++ b/bolt/test/bad-exe.test @@ -7,6 +7,6 @@ REQUIRES: system-linux RUN: %clang %cflags %S/Inputs/icf-jump-tables.c -g -o %t RUN: llvm-objcopy --only-keep-debug %t %t.debuginfo -RUN: not llvm-bolt %t.debuginfo -o /dev/null 2>&1 | FileCheck %s +RUN: not llvm-bolt %t.debuginfo -o %t.null 2>&1 | FileCheck %s CHECK: input binary is not a valid ELF executable diff --git a/bolt/test/lsda-section-name.cpp b/bolt/test/lsda-section-name.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41fb17665821911bdf15080730f27b5333d06e0c --- /dev/null +++ b/bolt/test/lsda-section-name.cpp @@ -0,0 +1,47 @@ +// This test check that LSDA section named by .gcc_except_table.main is +// disassembled by BOLT. + +// RUN: %clang++ %cxxflags -O3 -no-pie -c %s -o %t.o +// RUN: %clang++ %cxxflags -no-pie -fuse-ld=lld %t.o -o %t.exe \ +// RUN: -Wl,-q -Wl,--script=%S/Inputs/lsda.ldscript +// RUN: llvm-readelf -SW %t.exe | FileCheck %s +// RUN: llvm-bolt %t.exe -o %t.bolt + +// CHECK: .gcc_except_table.main + +#include + +class MyException : public std::exception { +public: + const char *what() const throw() { + return "Custom Exception: an error occurred!"; + } +}; + +int divide(int a, int b) { + if (b == 0) { + throw MyException(); + } + return a / b; +} + +int main() { + try { + int result = divide(10, 2); // normal case + std::cout << "Result: " << result << std::endl; + result = divide(5, 0); // will cause exception + std::cout << "Result: " << result << std::endl; + // this line will not execute + } catch (const MyException &e) { + // catch custom exception + std::cerr << "Caught exception: " << e.what() << std::endl; + } catch (const std::exception &e) { + // catch other C++ exceptions + std::cerr << "Caught exception: " << e.what() << std::endl; + } catch (...) { + // catch all other exceptions + std::cerr << "Caught unknown exception" << std::endl; + } + + return 0; +} diff --git a/bolt/test/no-relocs.test b/bolt/test/no-relocs.test index 9e22705081e6b72bbee3aefbe63e95b340c9d44c..34993eb330cbdd799a8ab2103ffdc51b65c77e08 100644 --- a/bolt/test/no-relocs.test +++ b/bolt/test/no-relocs.test @@ -5,6 +5,6 @@ REQUIRES: system-linux RUN: %clang %cflags %S/Inputs/icf-jump-tables.c -o %t -RUN: not llvm-bolt %t -o /dev/null --relocs 2>&1 | FileCheck %s +RUN: not llvm-bolt %t -o %t.null --relocs 2>&1 | FileCheck %s CHECK: BOLT-ERROR: relocations against code are missing from the input file. diff --git a/bolt/test/perf2bolt/Inputs/perf_test.c b/bolt/test/perf2bolt/Inputs/perf_test.c new file mode 100644 index 0000000000000000000000000000000000000000..6c7044cc6b67f2171ee594ae42eb00fbe5c6ce53 --- /dev/null +++ b/bolt/test/perf2bolt/Inputs/perf_test.c @@ -0,0 +1,22 @@ +int add(int a, int b) { return a + b; } +int minus(int a, int b) { return a - b; } +int multiple(int a, int b) { return a * b; } +int divide(int a, int b) { + if (b == 0) + return 0; + return a / b; +} + +int main() { + int a = 16; + int b = 8; + + for (int i = 1; i < 1000000; i++) { + add(a, b); + minus(a, b); + multiple(a, b); + divide(a, b); + } + + return 0; +} diff --git a/bolt/test/perf2bolt/Inputs/perf_test.lds b/bolt/test/perf2bolt/Inputs/perf_test.lds new file mode 100644 index 0000000000000000000000000000000000000000..66d925a05bebca0ee6cb918bdfc46bc1a2c4a5d1 --- /dev/null +++ b/bolt/test/perf2bolt/Inputs/perf_test.lds @@ -0,0 +1,13 @@ +SECTIONS { + . = SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + . = 0x212e8; + .dynsym : { *(.dynsym) } + . = 0x31860; + .text : { *(.text*) } + . = 0x41c20; + .fini_array : { *(.fini_array) } + . = 0x54e18; + .data : { *(.data) } +} \ No newline at end of file diff --git a/bolt/test/perf2bolt/lit.local.cfg b/bolt/test/perf2bolt/lit.local.cfg new file mode 100644 index 0000000000000000000000000000000000000000..05f41ff333b0e4f61de08e42c82755ad4a9538b7 --- /dev/null +++ b/bolt/test/perf2bolt/lit.local.cfg @@ -0,0 +1,4 @@ +import shutil + +if shutil.which("perf") != None: + config.available_features.add("perf") \ No newline at end of file diff --git a/bolt/test/perf2bolt/perf_test.test b/bolt/test/perf2bolt/perf_test.test new file mode 100644 index 0000000000000000000000000000000000000000..44db899ab30de564b0fe95442e817e4a10cc7424 --- /dev/null +++ b/bolt/test/perf2bolt/perf_test.test @@ -0,0 +1,17 @@ +# Check perf2bolt binary function which was compiled with pie + +REQUIRES: system-linux, perf + +RUN: %clang %S/Inputs/perf_test.c -fuse-ld=lld -Wl,--script=%S/Inputs/perf_test.lds -o %t +RUN: perf record -e cycles:u -o %t2 -- %t +RUN: perf2bolt %t -p=%t2 -o %t3 -nl -ignore-build-id 2>&1 | FileCheck %s + +CHECK-NOT: PERF2BOLT-ERROR +CHECK-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection. + +RUN: %clang %S/Inputs/perf_test.c -no-pie -fuse-ld=lld -o %t4 +RUN: perf record -e cycles:u -o %t5 -- %t4 +RUN: perf2bolt %t4 -p=%t5 -o %t6 -nl -ignore-build-id 2>&1 | FileCheck %s --check-prefix=CHECK-NO-PIE + +CHECK-NO-PIE-NOT: PERF2BOLT-ERROR +CHECK-NO-PIE-NOT: !! WARNING !! This high mismatch ratio indicates the input binary is probably not the same binary used during profiling collection. \ No newline at end of file diff --git a/bolt/test/pie.test b/bolt/test/pie.test index e8aaa91b7de7dd7489b6d9078ace406ca554b23a..0ce2576ee401c0b77c3a96f9283ceb394e36c4eb 100644 --- a/bolt/test/pie.test +++ b/bolt/test/pie.test @@ -5,6 +5,6 @@ REQUIRES: system-linux RUN: %clang %cflags -fPIC -pie %p/Inputs/jump_table_icp.cpp -o %t -RUN: llvm-bolt %t -o /dev/null 2>&1 | FileCheck %s +RUN: llvm-bolt %t -o %t.null 2>&1 | FileCheck %s CHECK: BOLT-INFO: shared object or position-independent executable detected diff --git a/bolt/test/re-optimize.test b/bolt/test/re-optimize.test index 38f3cf9131aa06eb4d32f6c99eb60fde1f87aacd..2c436d708df82c1873350992c7df081475f1d8d7 100644 --- a/bolt/test/re-optimize.test +++ b/bolt/test/re-optimize.test @@ -5,7 +5,7 @@ REQUIRES: system-linux RUN: %clang %cflags %S/Inputs/icf-jump-tables.c -o %t.exe -RUN: llvm-bolt %t.exe -o %t 2>&1 > /dev/null +RUN: llvm-bolt %t.exe -o %t 2>&1 > %t.null RUN: not llvm-bolt %t -o %t.bolt 2>&1 | FileCheck %s CHECK: BOLT-ERROR: input file was processed by BOLT. Cannot re-optimize. diff --git a/bolt/test/runtime/AArch64/adrrelaxationpass.s b/bolt/test/runtime/AArch64/adrrelaxationpass.s index 5c50cd6371926cbe23175ded354dd7e719ef6338..fa9fb63c613dc1345fc7a81e14da721e91e42345 100644 --- a/bolt/test/runtime/AArch64/adrrelaxationpass.s +++ b/bolt/test/runtime/AArch64/adrrelaxationpass.s @@ -1,33 +1,27 @@ # The second and third ADR instructions are non-local to functions # and must be replaced with ADRP + ADD by BOLT -# Also since main is non-simple, we can't change it's length so we have to -# replace NOP with adrp, and if there is no nop before adr in non-simple +# Also since main and test are non-simple, we can't change it's length so we +# have to replace NOP with adrp, and if there is no nop before adr in non-simple # function, we can't guarantee we didn't break possible jump tables, so we -# fail in strict mode +# fail in non-strict mode # REQUIRES: system-linux # RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \ # RUN: %s -o %t.o # RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -# RUN: llvm-bolt %t.exe -o %t.bolt --adr-relaxation=true +# RUN: llvm-bolt %t.exe -o %t.bolt --adr-relaxation=true --strict # RUN: llvm-objdump --no-print-imm-hex -d --disassemble-symbols=main %t.bolt | FileCheck %s # RUN: %t.bolt -# RUN: not llvm-bolt %t.exe -o %t.bolt --adr-relaxation=true --strict \ +# RUN: not llvm-bolt %t.exe -o %t.bolt --adr-relaxation=true \ # RUN: 2>&1 | FileCheck %s --check-prefix CHECK-ERROR - .data - .align 8 - .global Gvar -Gvar: .xword 0x0 - .global Gvar2 -Gvar2: .xword 0x42 - .text .align 4 .global test .type test, %function test: + adr x2, Gvar mov x0, xzr ret .size test, .-test @@ -47,6 +41,17 @@ br: .CI: .word 0xff + .data + .align 8 + .global Gvar +Gvar: .xword 0x0 + .global Gvar2 +Gvar2: .xword 0x42 + .balign 4 +jmptable: + .word 0 + .word test - jmptable + # CHECK:
: # CHECK-NEXT: adr x0, 0x{{[1-8a-f][0-9a-f]*}} # CHECK-NEXT: adrp x1, 0x{{[1-8a-f][0-9a-f]*}} @@ -54,4 +59,4 @@ br: # CHECK-NEXT: adrp x2, 0x{{[1-8a-f][0-9a-f]*}} # CHECK-NEXT: add x2, x2, #{{[1-8a-f][0-9a-f]*}} # CHECK-NEXT: adr x3, 0x{{[1-8a-f][0-9a-f]*}} -# CHECK-ERROR: BOLT-ERROR: Cannot relax adr in non-simple function main +# CHECK-ERROR: BOLT-ERROR: Cannot relax adr in non-simple function diff --git a/bolt/test/runtime/AArch64/controlflow.s b/bolt/test/runtime/AArch64/controlflow.s index fe9aab88f0c74047633ea5b9ee7e279d85ef8a00..7b0a38779f6e9cfb90027ad9adcd84a76568f4c8 100644 --- a/bolt/test/runtime/AArch64/controlflow.s +++ b/bolt/test/runtime/AArch64/controlflow.s @@ -48,6 +48,7 @@ test_cond_branch: .global test_branch_reg .type test_branch_reg, %function test_branch_reg: + nop adr x0, test_branch_zero br x0 panic @@ -97,6 +98,7 @@ test_call: .global test_call_reg .type test_call_reg, %function test_call_reg: + nop adr x0, test_call_foo blr x0 panic diff --git a/bolt/test/runtime/AArch64/hook-fini.test b/bolt/test/runtime/AArch64/hook-fini.test new file mode 100644 index 0000000000000000000000000000000000000000..8d23b21b6d612f5556608848237edc2c1af6f4de --- /dev/null +++ b/bolt/test/runtime/AArch64/hook-fini.test @@ -0,0 +1,61 @@ +# Test the different ways of hooking the fini function for instrumentation (via +# DT_FINI and via DT_FINI_ARRAY). We test the latter for both PIE and non-PIE +# binaries because of the different ways of handling relocations (static or +# dynamic). +# All tests perform the following steps: +# - Compile and link for the case to be tested +# - Some sanity-checks on the dynamic section and relocations in the binary to +# verify it has the shape we want for testing: +# - DT_FINI or DT_FINI_ARRAY in dynamic section +# - No relative relocations for non-PIE +# - Instrument +# - Run instrumented binary +# - Verify generated profile +REQUIRES: system-linux,bolt-runtime + +RUN: %clang %cflags -pie %p/Inputs/basic-instrumentation.s -Wl,-q -o %t.exe +RUN: llvm-readelf -d %t.exe | FileCheck --check-prefix=DYN-FINI %s +RUN: llvm-readelf -r %t.exe | FileCheck --check-prefix=RELOC-PIE %s +RUN: llvm-bolt %t.exe -o %t --instrument \ +RUN: --instrumentation-file=%t \ +RUN: --instrumentation-file-append-pid +RUN: rm -f %t.*.fdata +RUN: %t +RUN: cat %t.*.fdata | FileCheck %s + +RUN: %clang %cflags -pie %p/Inputs/basic-instrumentation.s -Wl,-q,-fini=0 -o %t-no-fini.exe +RUN: llvm-readelf -d %t-no-fini.exe | FileCheck --check-prefix=DYN-NO-FINI %s +RUN: llvm-readelf -r %t-no-fini.exe | FileCheck --check-prefix=RELOC-PIE %s +RUN: llvm-bolt %t-no-fini.exe -o %t-no-fini --instrument \ +RUN: --instrumentation-file=%t-no-fini \ +RUN: --instrumentation-file-append-pid +RUN: rm -f %t-no-fini.*.fdata +RUN: %t-no-fini +RUN: cat %t-no-fini.*.fdata | FileCheck %s + +RUN: %clang %cflags -no-pie %p/Inputs/basic-instrumentation.s -Wl,-q,-fini=0 -o %t-no-pie-no-fini.exe +RUN: llvm-readelf -d %t-no-pie-no-fini.exe | FileCheck --check-prefix=DYN-NO-FINI %s +RUN: llvm-readelf -r %t-no-pie-no-fini.exe | FileCheck --check-prefix=RELOC-NO-PIE %s +RUN: llvm-bolt %t-no-pie-no-fini.exe -o %t-no-pie-no-fini --instrument \ +RUN: --instrumentation-file=%t-no-pie-no-fini \ +RUN: --instrumentation-file-append-pid +RUN: rm -f %t-no-pie-no-fini.*.fdata +RUN: %t-no-pie-no-fini +RUN: cat %t-no-pie-no-fini.*.fdata | FileCheck %s + +# With fini: dynamic section should contain DT_FINI +DYN-FINI: (FINI) + +# Without fini: dynamic section should only contain DT_FINI_ARRAY +DYN-NO-FINI-NOT: (FINI) +DYN-NO-FINI: (FINI_ARRAY) +DYN-NO-FINI: (FINI_ARRAYSZ) + +# With PIE: binary should have relative relocations +RELOC-PIE: R_AARCH64_RELATIVE + +# Without PIE: binary should not have relative relocations +RELOC-NO-PIE-NOT: R_AARCH64_RELATIVE + +# The instrumented profile should at least say main was called once +CHECK: main 0 0 1{{$}} diff --git a/bolt/test/runtime/X86/asm-dump.c b/bolt/test/runtime/X86/asm-dump.c index fdd448e0c408ddbca63919906658819387337f30..e5383b52351594466d953adfccbbf19bf239e758 100644 --- a/bolt/test/runtime/X86/asm-dump.c +++ b/bolt/test/runtime/X86/asm-dump.c @@ -14,7 +14,7 @@ * RUN: %t.instr > %t.result * * Run BOLT with asm-dump - * RUN: llvm-bolt %t.exe -p %t.fdata --funcs=main --asm-dump=%t -o /dev/null \ + * RUN: llvm-bolt %t.exe -p %t.fdata --funcs=main --asm-dump=%t -o %t.null \ * RUN: | FileCheck %s --check-prefix=CHECK-BOLT * * Check asm file contents @@ -42,7 +42,7 @@ * RUN: %clang -fPIC %t.o -o %t.exe.reopt -Wl,-q * * Finally consume reoptimized file with reconstructed fdata - * RUN: llvm-bolt %t.exe.reopt -p %t.fdata.reconst -o /dev/null \ + * RUN: llvm-bolt %t.exe.reopt -p %t.fdata.reconst -o %t.null \ * RUN: | FileCheck %s --check-prefix=CHECK-REOPT * * CHECK-BOLT: BOLT-INFO: Dumping function assembly to {{.*}}/main.s diff --git a/bolt/test/runtime/X86/exceptions-lpstart-zero.s b/bolt/test/runtime/X86/exceptions-lpstart-zero.s new file mode 100644 index 0000000000000000000000000000000000000000..b487ff0fa2f59114705657d5c262d9a78d94faad --- /dev/null +++ b/bolt/test/runtime/X86/exceptions-lpstart-zero.s @@ -0,0 +1,91 @@ +# RUN: %clangxx %cflags -no-pie %s -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.exe.bolt +# RUN: %t.exe.bolt + +# REQUIRES: system-linux + +## Test that BOLT properly handles LPStart when LPStartEncoding is different +## from DW_EH_PE_omit. + +# The test case compiled with -O1 from: +# +# int main() { +# try { +# throw 42; +# } catch (...) { +# return 0; +# } +# return 1; +# } +# +# The exception table was modified with udata4 LPStartEncoding and sdata4 +# CallSiteEncoding. + + .text + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main +.Lfunc_begin0: + .cfi_startproc + .cfi_personality 3, __gxx_personality_v0 + .cfi_lsda 3, .Lexception0 +# %bb.0: + pushq %rax + .cfi_def_cfa_offset 16 + movl $4, %edi + callq __cxa_allocate_exception + movl $42, (%rax) +.Ltmp0: + movl $_ZTIi, %esi + movq %rax, %rdi + xorl %edx, %edx + callq __cxa_throw +.Ltmp1: +# %bb.1: +.LBB0_2: +.Ltmp2: + movq %rax, %rdi + callq __cxa_begin_catch + callq __cxa_end_catch + xorl %eax, %eax + popq %rcx + .cfi_def_cfa_offset 8 + retq +.Lfunc_end0: + .size main, .Lfunc_end0-main + .cfi_endproc + .section .gcc_except_table,"a",@progbits + .p2align 2 +GCC_except_table0: +.Lexception0: + .byte 3 # @LPStart Encoding = udata4 + .long 0 + .byte 3 # @TType Encoding = udata4 + .uleb128 .Lttbase0-.Lttbaseref0 +.Lttbaseref0: + .byte 11 # Call site Encoding = sdata4 + .uleb128 .Lcst_end0-.Lcst_begin0 +.Lcst_begin0: + .long .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 << + .long .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0 + .long 0 # has no landing pad + .byte 0 # On action: cleanup + .long .Ltmp0-.Lfunc_begin0 # >> Call Site 2 << + .long .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1 + .long .Ltmp2 # jumps to .Ltmp2 + .byte 1 # On action: 1 + .long .Ltmp1-.Lfunc_begin0 # >> Call Site 3 << + .long .Lfunc_end0-.Ltmp1 # Call between .Ltmp1 and .Lfunc_end0 + .long 0 # has no landing pad + .byte 0 # On action: cleanup +.Lcst_end0: + .byte 1 # >> Action Record 1 << + # Catch TypeInfo 1 + .byte 0 # No further actions + .p2align 2 + # >> Catch TypeInfos << + .long 0 # TypeInfo 1 +.Lttbase0: + .p2align 2 + # -- End function diff --git a/bolt/test/runtime/X86/instrumentation-ind-calls.s b/bolt/test/runtime/X86/instrumentation-ind-calls.s index 60f0d82236bdfdb1036e328476804900255c11a7..351baa64aeaf877a3779ad0c39a9259566970751 100755 --- a/bolt/test/runtime/X86/instrumentation-ind-calls.s +++ b/bolt/test/runtime/X86/instrumentation-ind-calls.s @@ -1,5 +1,5 @@ # This reproduces a bug with instrumentation when trying to count calls -# when the target address is computed with a referece to the stack pointer. +# when the target address is computed with a reference to the stack pointer. # Our instrumentation code uses the stack to save registers to be # transparent with the instrumented code, but we end up updating the stack # pointer while doing so, which affects this target address calculation. diff --git a/bolt/test/runtime/iplt.c b/bolt/test/runtime/iplt.c index b0e2e6d250700c9a90af842b3954a98338844b91..d5b56d901e6227d90bfb210683bd3883145d5210 100644 --- a/bolt/test/runtime/iplt.c +++ b/bolt/test/runtime/iplt.c @@ -1,10 +1,16 @@ // This test checks that the ifuncs works after bolt. +// Compiling with 00 results in IFUNC indirect calling. -// RUN: %clang %cflags -no-pie %s -fuse-ld=lld \ +// RUN: %clang %cflags -O0 -no-pie %s -fuse-ld=lld \ // RUN: -o %t.exe -Wl,-q // RUN: llvm-bolt %t.exe -o %t.bolt.exe --use-old-text=0 --lite=0 // RUN: %t.bolt.exe | FileCheck %s +// RUN: %clang %cflags -O3 -no-pie %s -fuse-ld=lld \ +// RUN: -o %t.O3.exe -Wl,-q +// RUN: llvm-bolt %t.O3.exe -o %t.O3.bolt.exe --use-old-text=0 --lite=0 +// RUN: %t.O3.bolt.exe | FileCheck %s + // CHECK: foo #include diff --git a/bolt/test/yaml-profile-kind.c b/bolt/test/yaml-profile-kind.c index b7983c672c74804073e960b07bb9a052ba15d764..3bf2a2619f01f4240e97b84e83862da88f892a7b 100644 --- a/bolt/test/yaml-profile-kind.c +++ b/bolt/test/yaml-profile-kind.c @@ -1,10 +1,10 @@ /* This test checks the handling of YAML profile with different block orders. # RUN: %clang %cflags %s -o %t.exe # RUN: link_fdata %s %t.exe %t.fdata -# RUN: llvm-bolt %t.exe -o /dev/null -data %t.fdata -w %t.yaml +# RUN: llvm-bolt %t.exe -o %t.null -data %t.fdata -w %t.yaml # RUN: FileCheck %s --input-file %t.yaml --check-prefix=CHECK-BINARY # CHECK-BINARY: dfs-order: false -# RUN: llvm-bolt %t.exe -o /dev/null -data %t.fdata -w %t.yaml --profile-use-dfs +# RUN: llvm-bolt %t.exe -o %t.null -data %t.fdata -w %t.yaml --profile-use-dfs # RUN: FileCheck %s --input-file %t.yaml --check-prefix=CHECK-DFS # CHECK-DFS: dfs-order: true diff --git a/bolt/unittests/Core/BinaryContext.cpp b/bolt/unittests/Core/BinaryContext.cpp index bac264141251ccd9953a8398cdd3ce6d61da502f..7ac1c14357596bd7b63744dda394ad47265480e3 100644 --- a/bolt/unittests/Core/BinaryContext.cpp +++ b/bolt/unittests/Core/BinaryContext.cpp @@ -62,6 +62,46 @@ INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester, INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester, ::testing::Values(Triple::aarch64)); +TEST_P(BinaryContextTester, FlushPendingRelocCALL26) { + if (GetParam() != Triple::aarch64) + GTEST_SKIP(); + + // This test checks that encodeValueAArch64 used by flushPendingRelocations + // returns correctly encoded values for CALL26 relocation for both backward + // and forward branches. + // + // The offsets layout is: + // 4: func1 + // 8: bl func1 + // 12: bl func2 + // 16: func2 + + char Data[20] = {}; + BinarySection &BS = BC->registerOrUpdateSection( + ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, + (uint8_t *)Data, sizeof(Data), 4); + MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1"); + ASSERT_TRUE(RelSymbol1); + BS.addRelocation(8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0, true); + MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2"); + ASSERT_TRUE(RelSymbol2); + BS.addRelocation(12, RelSymbol2, ELF::R_AARCH64_CALL26, 0, 0, true); + + std::error_code EC; + SmallVector Vect(sizeof(Data)); + raw_svector_ostream OS(Vect); + + BS.flushPendingRelocations(OS, [&](const MCSymbol *S) { + return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0; + }); + + const uint8_t Func1Call[4] = {255, 255, 255, 151}; + const uint8_t Func2Call[4] = {1, 0, 0, 148}; + + EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n"; + EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n"; +} + #endif TEST_P(BinaryContextTester, BaseAddress) { @@ -83,3 +123,24 @@ TEST_P(BinaryContextTester, BaseAddress) { BaseAddress = BC->getBaseAddressForMapping(0x7f13f5556000, 0x137a000); ASSERT_FALSE(BaseAddress.has_value()); } + +TEST_P(BinaryContextTester, BaseAddress2) { + // Check that base address calculation is correct for a binary if the + // alignment in ELF file are different from pagesize. + // The segment layout is as follows: + BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000}; + BC->SegmentMapInfo[0x31860] = + SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000}; + BC->SegmentMapInfo[0x41c20] = + SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000}; + BC->SegmentMapInfo[0x54e18] = + SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000}; + + std::optional BaseAddress = + BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000); + ASSERT_TRUE(BaseAddress.has_value()); + ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL); + + BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000); + ASSERT_FALSE(BaseAddress.has_value()); +} diff --git a/bolt/utils/nfc-stat-parser.py b/bolt/utils/nfc-stat-parser.py new file mode 100755 index 0000000000000000000000000000000000000000..72a21e0e24a1a7ddfd4be700e33b5a8c1c8d32e8 --- /dev/null +++ b/bolt/utils/nfc-stat-parser.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +import argparse +import csv +import re +import sys +import os +from statistics import geometric_mean + +TIMING_LOG_RE = re.compile(r"(.*)/(.*).tmp(.*)") + + +def main(): + parser = argparse.ArgumentParser( + description="BOLT NFC stat parser", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "input", nargs="+", help="timing.log files produced by llvm-bolt-wrapper" + ) + parser.add_argument( + "--check_longer_than", + default=1, + type=float, + help="Only warn on tests longer than X seconds for at least one side", + ) + parser.add_argument( + "--threshold_single", + default=10, + type=float, + help="Threshold for a single test result swing, abs percent", + ), + parser.add_argument( + "--threshold_agg", + default=5, + type=float, + help="Threshold for geomean test results swing, abs percent", + ), + parser.add_argument("--verbose", "-v", action="store_true") + args = parser.parse_args() + + def fmt_delta(value, exc_threshold, above_bound=True): + formatted_value = format(value, "+.2%") + if not above_bound: + formatted_value += "?" + elif exc_threshold and sys.stdout.isatty(): # terminal supports colors + return f"\033[1m{formatted_value}\033[0m" + return formatted_value + + # Ratios for geomean computation + time_ratios = [] + mem_ratios = [] + # Whether any test exceeds the single test threshold (mem or time) + threshold_single = False + # Whether geomean exceeds aggregate test threshold (mem or time) + threshold_agg = False + + if args.verbose: + print(f"# Individual test threshold: +-{args.threshold_single}%") + print(f"# Aggregate (geomean) test threshold: +-{args.threshold_agg}%") + print( + f"# Checking time swings for tests with runtime >" + f"{args.check_longer_than}s - otherwise marked as ?" + ) + print("Test/binary BOLT_wall_time BOLT_max_rss") + + for input_file in args.input: + input_dir = os.path.dirname(input_file) + with open(input_file) as timing_file: + timing_reader = csv.reader(timing_file, delimiter=";") + for row in timing_reader: + test_name = row[0] + m = TIMING_LOG_RE.match(row[0]) + if m: + test_name = f"{input_dir}/{m.groups()[1]}/{m.groups()[2]}" + else: + # Prepend input dir to unparsed test name + test_name = input_dir + "#" + test_name + time_a, time_b = float(row[1]), float(row[3]) + mem_a, mem_b = int(row[2]), int(row[4]) + # Check if time is above bound for at least one side + time_above_bound = any( + [x > args.check_longer_than for x in [time_a, time_b]] + ) + # Compute B/A ratios (for % delta and geomean) + time_ratio = time_b / time_a if time_a else float('nan') + mem_ratio = mem_b / mem_a if mem_a else float('nan') + # Keep ratios for geomean + if time_above_bound and time_ratio > 0: # must be >0 for gmean + time_ratios += [time_ratio] + mem_ratios += [mem_ratio] + # Deltas: (B/A)-1 = (B-A)/A + time_delta = time_ratio - 1 + mem_delta = mem_ratio - 1 + # Check individual test results vs single test threshold + time_exc = ( + 100.0 * abs(time_delta) > args.threshold_single and time_above_bound + ) + mem_exc = 100.0 * abs(mem_delta) > args.threshold_single + if time_exc or mem_exc: + threshold_single = True + # Print deltas with formatting in verbose mode + if args.verbose or time_exc or mem_exc: + print( + test_name, + fmt_delta(time_delta, time_exc, time_above_bound), + fmt_delta(mem_delta, mem_exc), + ) + + time_gmean_delta = geometric_mean(time_ratios) - 1 + mem_gmean_delta = geometric_mean(mem_ratios) - 1 + time_agg_threshold = 100.0 * abs(time_gmean_delta) > args.threshold_agg + mem_agg_threshold = 100.0 * abs(mem_gmean_delta) > args.threshold_agg + if time_agg_threshold or mem_agg_threshold: + threshold_agg = True + if time_agg_threshold or mem_agg_threshold or args.verbose: + print( + "Geomean", + fmt_delta(time_gmean_delta, time_agg_threshold), + fmt_delta(mem_gmean_delta, mem_agg_threshold), + ) + exit(threshold_single or threshold_agg) + + +if __name__ == "__main__": + main() diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp index b36144d1912fce0041707c32226778490cfa68f9..221e924c10f621d3324021a1475395bcb8b96a96 100644 --- a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp @@ -19,11 +19,14 @@ using namespace clang::ast_matchers; namespace clang::tidy::abseil { +const auto DefaultStringLikeClasses = + "::std::basic_string;::std::basic_string_view"; + StringFindStartswithCheck::StringFindStartswithCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), StringLikeClasses(utils::options::parseStringList( - Options.get("StringLikeClasses", "::std::basic_string"))), + Options.get("StringLikeClasses", DefaultStringLikeClasses))), IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", utils::IncludeSorter::IS_LLVM), areDiagsSelfContained()), diff --git a/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.cpp index 07a987359d4d8d78f4b9212716da9d31a831e16c..43bedd4f73ef44736effccd6be21b17d395fea41 100644 --- a/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.cpp @@ -41,12 +41,18 @@ AST_MATCHER_P2(Expr, hasSideEffect, bool, CheckFunctionCalls, } if (const auto *OpCallExpr = dyn_cast(E)) { + if (const auto *MethodDecl = + dyn_cast_or_null(OpCallExpr->getDirectCallee())) + if (MethodDecl->isConst()) + return false; + OverloadedOperatorKind OpKind = OpCallExpr->getOperator(); return OpKind == OO_Equal || OpKind == OO_PlusEqual || OpKind == OO_MinusEqual || OpKind == OO_StarEqual || OpKind == OO_SlashEqual || OpKind == OO_AmpEqual || OpKind == OO_PipeEqual || OpKind == OO_CaretEqual || OpKind == OO_LessLessEqual || OpKind == OO_GreaterGreaterEqual || + OpKind == OO_LessLess || OpKind == OO_GreaterGreater || OpKind == OO_PlusPlus || OpKind == OO_MinusMinus || OpKind == OO_PercentEqual || OpKind == OO_New || OpKind == OO_Delete || OpKind == OO_Array_New || diff --git a/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.cpp index a01b7f5a4ee6e68fd9391d2e7cda1dc053641bf5..5260a8b4ecb0bad3358ddb2bbc519a5be82ad26d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/LambdaFunctionNameCheck.cpp @@ -82,8 +82,8 @@ void LambdaFunctionNameCheck::registerPPCallbacks( void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) { const auto *E = Result.Nodes.getNodeAs("E"); - if (E->getIdentKind() != PredefinedExpr::Func && - E->getIdentKind() != PredefinedExpr::Function) { + if (E->getIdentKind() != PredefinedIdentKind::Func && + E->getIdentKind() != PredefinedIdentKind::Function) { // We don't care about other PredefinedExprs. return; } diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp index 62cde11ebb7399c2916450264bd20d7f213af381..a1cffbc6661992154c8a09b5f196198f174b730f 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp @@ -192,10 +192,12 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) { } // Detect expression like: sizeof(expr, expr); most likely an error. - Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts( - binaryOperator(hasOperatorName(","))))) - .bind("sizeof-comma-expr"), - this); + Finder->addMatcher( + sizeOfExpr( + has(ignoringParenImpCasts( + binaryOperator(hasOperatorName(",")).bind("sizeof-comma-binop")))) + .bind("sizeof-comma-expr"), + this); // Detect sizeof(...) /sizeof(...)); // FIXME: @@ -255,51 +257,62 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( binaryOperator( hasAnyOperatorName("==", "!=", "<", "<=", ">", ">=", "+", "-"), - hasOperands( - anyOf(ignoringParenImpCasts(SizeOfExpr), - ignoringParenImpCasts(binaryOperator( - hasOperatorName("*"), - hasEitherOperand(ignoringParenImpCasts(SizeOfExpr))))), - ignoringParenImpCasts(PtrDiffExpr))) + hasOperands(anyOf(ignoringParenImpCasts( + SizeOfExpr.bind("sizeof-ptr-mul-expr")), + ignoringParenImpCasts(binaryOperator( + hasOperatorName("*"), + hasEitherOperand(ignoringParenImpCasts( + SizeOfExpr.bind("sizeof-ptr-mul-expr")))))), + ignoringParenImpCasts(PtrDiffExpr))) .bind("sizeof-in-ptr-arithmetic-mul"), this); - Finder->addMatcher(binaryOperator(hasOperatorName("/"), - hasLHS(ignoringParenImpCasts(PtrDiffExpr)), - hasRHS(ignoringParenImpCasts(SizeOfExpr))) - .bind("sizeof-in-ptr-arithmetic-div"), - this); + Finder->addMatcher( + binaryOperator( + hasOperatorName("/"), hasLHS(ignoringParenImpCasts(PtrDiffExpr)), + hasRHS(ignoringParenImpCasts(SizeOfExpr.bind("sizeof-ptr-div-expr")))) + .bind("sizeof-in-ptr-arithmetic-div"), + this); } void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { const ASTContext &Ctx = *Result.Context; if (const auto *E = Result.Nodes.getNodeAs("sizeof-constant")) { - diag(E->getBeginLoc(), - "suspicious usage of 'sizeof(K)'; did you mean 'K'?"); + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(K)'; did you mean 'K'?") + << E->getSourceRange(); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-integer-call")) { diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression " - "that results in an integer"); + "that results in an integer") + << E->getSourceRange(); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-this")) { diag(E->getBeginLoc(), - "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'"); + "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'") + << E->getSourceRange(); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-charp")) { diag(E->getBeginLoc(), - "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?"); + "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?") + << E->getSourceRange(); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-pointer-to-aggregate")) { diag(E->getBeginLoc(), - "suspicious usage of 'sizeof(A*)'; pointer to aggregate"); - } else if (const auto *E = - Result.Nodes.getNodeAs("sizeof-compare-constant")) { - diag(E->getBeginLoc(), - "suspicious comparison of 'sizeof(expr)' to a constant"); + "suspicious usage of 'sizeof(A*)'; pointer to aggregate") + << E->getSourceRange(); + } else if (const auto *E = Result.Nodes.getNodeAs( + "sizeof-compare-constant")) { + diag(E->getOperatorLoc(), + "suspicious comparison of 'sizeof(expr)' to a constant") + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-comma-expr")) { - diag(E->getBeginLoc(), "suspicious usage of 'sizeof(..., ...)'"); + const auto *BO = + Result.Nodes.getNodeAs("sizeof-comma-binop"); + assert(BO); + diag(BO->getOperatorLoc(), "suspicious usage of 'sizeof(..., ...)'") + << E->getSourceRange(); } else if (const auto *E = - Result.Nodes.getNodeAs("sizeof-divide-expr")) { + Result.Nodes.getNodeAs("sizeof-divide-expr")) { const auto *NumTy = Result.Nodes.getNodeAs("num-type"); const auto *DenomTy = Result.Nodes.getNodeAs("denom-type"); const auto *ElementTy = Result.Nodes.getNodeAs("elem-type"); @@ -311,49 +324,64 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { if (DenominatorSize > CharUnits::Zero() && !NumeratorSize.isMultipleOf(DenominatorSize)) { - diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" - " numerator is not a multiple of denominator"); + diag(E->getOperatorLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" + " numerator is not a multiple of denominator") + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (ElementSize > CharUnits::Zero() && DenominatorSize > CharUnits::Zero() && ElementSize != DenominatorSize) { - diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" - " numerator is not a multiple of denominator"); + diag(E->getOperatorLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" + " numerator is not a multiple of denominator") + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (NumTy && DenomTy && NumTy == DenomTy) { - diag(E->getBeginLoc(), - "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'"); + diag(E->getOperatorLoc(), + "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'") + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (PointedTy && DenomTy && PointedTy == DenomTy) { - diag(E->getBeginLoc(), - "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'"); + diag(E->getOperatorLoc(), + "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'") + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } else if (NumTy && DenomTy && NumTy->isPointerType() && DenomTy->isPointerType()) { - diag(E->getBeginLoc(), - "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'"); + diag(E->getOperatorLoc(), + "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'") + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-sizeof-expr")) { - diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'"); - } else if (const auto *E = - Result.Nodes.getNodeAs("sizeof-multiply-sizeof")) { - diag(E->getBeginLoc(), "suspicious 'sizeof' by 'sizeof' multiplication"); - } else if (const auto *E = - Result.Nodes.getNodeAs("sizeof-in-ptr-arithmetic-mul")) { + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'") + << E->getSourceRange(); + } else if (const auto *E = Result.Nodes.getNodeAs( + "sizeof-multiply-sizeof")) { + diag(E->getOperatorLoc(), "suspicious 'sizeof' by 'sizeof' multiplication") + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); + } else if (const auto *E = Result.Nodes.getNodeAs( + "sizeof-in-ptr-arithmetic-mul")) { const auto *LPtrTy = Result.Nodes.getNodeAs("left-ptr-type"); const auto *RPtrTy = Result.Nodes.getNodeAs("right-ptr-type"); const auto *SizeofArgTy = Result.Nodes.getNodeAs("sizeof-arg-type"); + const auto *SizeOfExpr = + Result.Nodes.getNodeAs("sizeof-ptr-mul-expr"); if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { - diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " - "pointer arithmetic"); + diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " + "pointer arithmetic") + << SizeOfExpr->getSourceRange() << E->getOperatorLoc() + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } - } else if (const auto *E = - Result.Nodes.getNodeAs("sizeof-in-ptr-arithmetic-div")) { + } else if (const auto *E = Result.Nodes.getNodeAs( + "sizeof-in-ptr-arithmetic-div")) { const auto *LPtrTy = Result.Nodes.getNodeAs("left-ptr-type"); const auto *RPtrTy = Result.Nodes.getNodeAs("right-ptr-type"); const auto *SizeofArgTy = Result.Nodes.getNodeAs("sizeof-arg-type"); + const auto *SizeOfExpr = + Result.Nodes.getNodeAs("sizeof-ptr-div-expr"); if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { - diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " - "pointer arithmetic"); + diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " + "pointer arithmetic") + << SizeOfExpr->getSourceRange() << E->getOperatorLoc() + << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); } } } diff --git a/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp index e820cd39d83d21b4654ae0afc72835ae0f51f8a6..a0e8700b0522bc51c5dd2473e61ad24653776f03 100644 --- a/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp @@ -52,27 +52,42 @@ AST_MATCHER_P(Stmt, forEachPrevStmt, ast_matchers::internal::Matcher, } return IsHostile; } + +// Matches the expression awaited by the `co_await`. +AST_MATCHER_P(CoawaitExpr, awaitable, ast_matchers::internal::Matcher, + InnerMatcher) { + if (Expr *E = Node.getCommonExpr()) + return InnerMatcher.matches(*E, Finder, Builder); + return false; +} + +auto typeWithNameIn(const std::vector &Names) { + return hasType( + hasCanonicalType(hasDeclaration(namedDecl(hasAnyName(Names))))); +} } // namespace CoroutineHostileRAIICheck::CoroutineHostileRAIICheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), RAIITypesList(utils::options::parseStringList( - Options.get("RAIITypesList", "std::lock_guard;std::scoped_lock"))) {} + Options.get("RAIITypesList", "std::lock_guard;std::scoped_lock"))), + AllowedAwaitablesList(utils::options::parseStringList( + Options.get("AllowedAwaitablesList", ""))) {} void CoroutineHostileRAIICheck::registerMatchers(MatchFinder *Finder) { // A suspension happens with co_await or co_yield. auto ScopedLockable = varDecl(hasType(hasCanonicalType(hasDeclaration( hasAttr(attr::Kind::ScopedLockable))))) .bind("scoped-lockable"); - auto OtherRAII = varDecl(hasType(hasCanonicalType(hasDeclaration( - namedDecl(hasAnyName(RAIITypesList)))))) - .bind("raii"); - Finder->addMatcher(expr(anyOf(coawaitExpr(), coyieldExpr()), - forEachPrevStmt(declStmt(forEach( - varDecl(anyOf(ScopedLockable, OtherRAII)))))) - .bind("suspension"), - this); + auto OtherRAII = varDecl(typeWithNameIn(RAIITypesList)).bind("raii"); + auto AllowedSuspend = awaitable(typeWithNameIn(AllowedAwaitablesList)); + Finder->addMatcher( + expr(anyOf(coawaitExpr(unless(AllowedSuspend)), coyieldExpr()), + forEachPrevStmt( + declStmt(forEach(varDecl(anyOf(ScopedLockable, OtherRAII)))))) + .bind("suspension"), + this); } void CoroutineHostileRAIICheck::check(const MatchFinder::MatchResult &Result) { @@ -94,5 +109,7 @@ void CoroutineHostileRAIICheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "RAIITypesList", utils::options::serializeStringList(RAIITypesList)); + Options.store(Opts, "SafeAwaitableList", + utils::options::serializeStringList(AllowedAwaitablesList)); } } // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.h b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.h index a5e9cb89ef67695ea73dc2c54f22b83b54e986cf..be925097692a48543530d1af8ef916b3f3d5e216 100644 --- a/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.h +++ b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.h @@ -43,6 +43,9 @@ private: // List of fully qualified types which should not persist across a suspension // point in a coroutine. std::vector RAIITypesList; + // List of fully qualified awaitable types which are considered safe to + // co_await. + std::vector AllowedAwaitablesList; }; } // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp index 2a54eaddccb15cfd4f0cb0a66003790b4a07858f..51ee35da623f2023c8866e391cdc7c7bae6c1ae6 100644 --- a/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp @@ -691,12 +691,15 @@ void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) { const auto *MethodDecl = dyn_cast(LP.Callable.Decl); const BindArgument &ObjPtr = FunctionCallArgs.front(); - if (!isa(ignoreTemporariesAndPointers(ObjPtr.E))) { - Stream << ObjPtr.UsageIdentifier; - Stream << "->"; + if (MethodDecl->getOverloadedOperator() == OO_Call) { + Stream << "(*" << ObjPtr.UsageIdentifier << ')'; + } else { + if (!isa(ignoreTemporariesAndPointers(ObjPtr.E))) { + Stream << ObjPtr.UsageIdentifier; + Stream << "->"; + } + Stream << MethodDecl->getNameAsString(); } - - Stream << MethodDecl->getName(); } else { switch (LP.Callable.CE) { case CE_Var: diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp index c92bd8afa6add82e9e84463cf0b8c9bf955bb7f6..e7e01bab0e9d5233c1a2ac1fa337833f938940a7 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp @@ -174,7 +174,7 @@ const Expr *digThroughConstructorsConversions(const Expr *E) { // The initial constructor must take exactly one parameter, but base class // and deferred constructors can take more. if (ConstructExpr->getNumArgs() != 1 || - ConstructExpr->getConstructionKind() != CXXConstructExpr::CK_Complete) + ConstructExpr->getConstructionKind() != CXXConstructionKind::Complete) return nullptr; E = ConstructExpr->getArg(0); if (const auto *Temp = dyn_cast(E)) diff --git a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp index 71fd8eca300c1b2eae326c08ed6eccdfe05c0f18..616e57efa76ded52678458323b80bf02a10e30cd 100644 --- a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -323,7 +323,8 @@ bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag, return false; }; switch (New->getInitializationStyle()) { - case CXXNewExpr::NoInit: { + case CXXNewInitializationStyle::None: + case CXXNewInitializationStyle::Implicit: { if (ArraySizeExpr.empty()) { Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd)); } else { @@ -334,7 +335,7 @@ bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag, } break; } - case CXXNewExpr::CallInit: { + case CXXNewInitializationStyle::Call: { // FIXME: Add fixes for constructors with parameters that can be created // with a C++11 braced-init-list (e.g. std::vector, std::map). // Unlike ordinal cases, braced list can not be deduced in @@ -371,7 +372,7 @@ bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag, } break; } - case CXXNewExpr::ListInit: { + case CXXNewInitializationStyle::List: { // Range of the substring that we do not want to remove. SourceRange InitRange; if (const auto *NewConstruct = New->getConstructExpr()) { diff --git a/clang-tools-extra/clang-tidy/performance/EnumSizeCheck.cpp b/clang-tools-extra/clang-tidy/performance/EnumSizeCheck.cpp index 0d44b8c7706c3c49b215809639bfc34f5f2f1d99..0f3e9d3ef75917bcee9bb41bc7b20205bdd84a44 100644 --- a/clang-tools-extra/clang-tidy/performance/EnumSizeCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/EnumSizeCheck.cpp @@ -23,6 +23,8 @@ namespace clang::tidy::performance { namespace { +AST_MATCHER(EnumDecl, hasEnumerators) { return !Node.enumerators().empty(); } + const std::uint64_t Min8 = std::imaxabs(std::numeric_limits::min()); const std::uint64_t Max8 = std::numeric_limits::max(); @@ -93,6 +95,7 @@ bool EnumSizeCheck::isLanguageVersionSupported( void EnumSizeCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( enumDecl(unless(isExpansionInSystemHeader()), isDefinition(), + hasEnumerators(), unless(matchers::matchesAnyListedName(EnumIgnoreList))) .bind("e"), this); diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp index 7539b3899682e139fa7ef8fc3b9070a5f9dc4ab2..18c5e144e46fe77a00e06752b008a09d4bee5e34 100644 --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -124,6 +124,7 @@ namespace readability { m(TypeAlias) \ m(MacroDefinition) \ m(ObjcIvar) \ + m(Concept) \ enum StyleKind : int { #define ENUMERATE(v) SK_ ## v, @@ -871,8 +872,8 @@ bool IdentifierNamingCheck::matchesStyle( llvm::Regex("^[a-z][a-zA-Z0-9]*$"), llvm::Regex("^[A-Z][A-Z0-9_]*$"), llvm::Regex("^[A-Z][a-zA-Z0-9]*$"), - llvm::Regex("^[A-Z]([a-z0-9]*(_[A-Z])?)*"), - llvm::Regex("^[a-z]([a-z0-9]*(_[A-Z])?)*"), + llvm::Regex("^[A-Z]+([a-z0-9]*_[A-Z0-9]+)*[a-z0-9]*$"), + llvm::Regex("^[a-z]+([a-z0-9]*_[A-Z0-9]+)*[a-z0-9]*$"), llvm::Regex("^[A-Z]([a-z0-9_]*[a-z])*$"), }; @@ -1391,6 +1392,9 @@ StyleKind IdentifierNamingCheck::findStyleKind( return SK_Invalid; } + if (isa(D) && NamingStyles[SK_Concept]) + return SK_Concept; + return SK_Invalid; } diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp index 69e6d73c4fcd7bb58a116662bc7716780b83c9cd..f0fca30de3b3c4db9f9bb9fb80487aaaa174a409 100644 --- a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp @@ -152,7 +152,8 @@ StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression, return "false"; } - if (const auto *IntLit = dyn_cast(Expression)) { + if (const auto *IntLit = + dyn_cast(Expression->IgnoreParens())) { return (IntLit->getValue() == 0) ? "false" : "true"; } @@ -385,7 +386,7 @@ void ImplicitBoolConversionCheck::handleCastFromBool( << DestType; if (const auto *BoolLiteral = - dyn_cast(Cast->getSubExpr())) { + dyn_cast(Cast->getSubExpr()->IgnoreParens())) { Diag << tooling::fixit::createReplacement( *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context)); } else { diff --git a/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp b/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp index fe4d2b6d03aa48b13593f740b9f9a6b6b378d387..eb21827bdeba3ccca719501e27361bd3a0ea7636 100644 --- a/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp +++ b/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp @@ -134,12 +134,13 @@ void IncludeSorter::addInclude(StringRef FileName, bool IsAngled, int Offset = findNextLine(SourceMgr->getCharacterData(EndLocation)); // Record the relevant location information for this inclusion directive. - IncludeLocations[FileName].push_back( + auto &IncludeLocation = IncludeLocations[FileName]; + IncludeLocation.push_back( SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset))); - SourceLocations.push_back(IncludeLocations[FileName].back()); + SourceLocations.push_back(IncludeLocation.back()); // Stop if this inclusion is a duplicate. - if (IncludeLocations[FileName].size() > 1) + if (IncludeLocation.size() > 1) return; // Add the included file's name to the appropriate bucket. diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index c5586eac606a6682b9bf6b075e3ae7a8c09d0c50..5eef43e93cb519f9944c9fff0432f8ca7b1f8792 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1491,6 +1491,46 @@ FuzzyFindRequest speculativeFuzzyFindRequestForCompletion( return CachedReq; } +// This function is similar to Lexer::findNextToken(), but assumes +// that the input SourceLocation is the completion point (which is +// a case findNextToken() does not handle). +std::optional +findTokenAfterCompletionPoint(SourceLocation CompletionPoint, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation Loc = CompletionPoint; + if (Loc.isMacroID()) { + if (!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) + return std::nullopt; + } + + // Advance to the next SourceLocation after the completion point. + // Lexer::findNextToken() would call MeasureTokenLength() here, + // which does not handle the completion point (and can't, because + // the Lexer instance it constructs internally doesn't have a + // Preprocessor and so doesn't know about the completion point). + Loc = Loc.getLocWithOffset(1); + + // Break down the source location. + std::pair LocInfo = SM.getDecomposedLoc(Loc); + + // Try to load the file buffer. + bool InvalidTemp = false; + StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp); + if (InvalidTemp) + return std::nullopt; + + const char *TokenBegin = File.data() + LocInfo.second; + + // Lex from the start of the given location. + Lexer TheLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(), + TokenBegin, File.end()); + // Find the token. + Token Tok; + TheLexer.LexFromRawLexer(Tok); + return Tok; +} + // Runs Sema-based (AST) and Index-based completion, returns merged results. // // There are a few tricky considerations: @@ -1589,7 +1629,7 @@ public: auto Style = getFormatStyleForFile(SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, *SemaCCInput.ParseInput.TFS); - const auto NextToken = Lexer::findNextToken( + const auto NextToken = findTokenAfterCompletionPoint( Recorder->CCSema->getPreprocessor().getCodeCompletionLoc(), Recorder->CCSema->getSourceManager(), Recorder->CCSema->LangOpts); if (NextToken) diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 7f7b5513dff6feea83df69f38f70d5dc093e4aeb..a868d3bb4e3fa1d0a26ab02611efdc11e37bd699 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -408,7 +408,9 @@ void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D, // -2 => 0xfffffffe // -2^32 => 0xffffffff00000000 static llvm::FormattedNumber printHex(const llvm::APSInt &V) { - uint64_t Bits = V.getZExtValue(); + assert(V.getSignificantBits() <= 64 && "Can't print more than 64 bits."); + uint64_t Bits = + V.getBitWidth() > 64 ? V.trunc(64).getZExtValue() : V.getZExtValue(); if (V.isNegative() && V.getSignificantBits() <= 32) return llvm::format_hex(uint32_t(Bits), 0); return llvm::format_hex(Bits, 0); diff --git a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp index 9d6c09cd2ab4b7131ab2ecda333a71a1c431b81c..9ba4d7a95415bc08968cb23d0a8b5397ac135166 100644 --- a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp +++ b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp @@ -668,6 +668,8 @@ const std::pair IncludeMappings[] = { {"bits/syslog-path.h", ""}, {"bits/termios.h", ""}, {"bits/types.h", ""}, + {"bits/types/siginfo_t.h", ""}, + {"bits/types/struct_itimerspec.h", ""}, {"bits/uio.h", ""}, {"bits/ustat.h", ""}, {"bits/utmp.h", ""}, diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt index 526a073f619ea34867ebe9a778a9615b61ff03aa..2e948c23569f686a8abfd227496030ef49b7fe62 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangDaemonTweaks OBJECT PopulateSwitch.cpp RawStringLiteral.cpp RemoveUsingNamespace.cpp + ScopifyEnum.cpp SpecialMembers.cpp SwapIfBranches.cpp diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp index 1e4edc6d6b4bb390ed40ef6aa7bd2329c16ba6a1..79bf962544242bbed5455bceefa990d70b488aab 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp @@ -176,7 +176,8 @@ bool ExtractionContext::exprIsValidOutside(const clang::Stmt *Scope) const { SourceLocation ScopeBegin = Scope->getBeginLoc(); SourceLocation ScopeEnd = Scope->getEndLoc(); for (const Decl *ReferencedDecl : ReferencedDecls) { - if (SM.isPointWithin(ReferencedDecl->getBeginLoc(), ScopeBegin, ScopeEnd) && + if (ReferencedDecl->getBeginLoc().isValid() && + SM.isPointWithin(ReferencedDecl->getBeginLoc(), ScopeBegin, ScopeEnd) && SM.isPointWithin(ReferencedDecl->getEndLoc(), ScopeBegin, ScopeEnd)) return false; } diff --git a/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp b/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp index a152fb5cfabd1dbd1095704c625d0a6a660e19f6..43cfc769f7f71d134206066fd5b3c672a6228f73 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp @@ -176,7 +176,7 @@ bool PopulateSwitch::prepare(const Selection &Sel) { // We need a stored value in order to continue; currently both C and ObjC // enums won't have one. - if (CE->getResultStorageKind() == ConstantExpr::RSK_None) + if (CE->getResultStorageKind() == ConstantResultStorageKind::None) return false; auto Iter = ExpectedCases.find(Normalize(CE->getResultAsAPSInt())); if (Iter != ExpectedCases.end()) diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e36b3249bc7b92948c4bf1a5dc2169c025606302 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp @@ -0,0 +1,235 @@ +//===--- ScopifyEnum.cpp --------------------------------------- -*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ParsedAST.h" +#include "Protocol.h" +#include "Selection.h" +#include "SourceCode.h" +#include "XRefs.h" +#include "refactor/Tweak.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" + +#include +#include +#include +#include +#include + +namespace clang::clangd { +namespace { + +/// Turns an unscoped into a scoped enum type. +/// Before: +/// enum E { EV1, EV2 }; +/// ^ +/// void f() { E e1 = EV1; } +/// +/// After: +/// enum class E { EV1, EV2 }; +/// void f() { E e1 = E::EV1; } +/// +/// 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; + std::string title() const override { return "Convert to scoped enum"; } + llvm::StringLiteral kind() const override { + return CodeAction::REFACTOR_KIND; + } + bool prepare(const Selection &Inputs) override; + Expected apply(const Selection &Inputs) override; + + using MakeReplacement = + std::function; + llvm::Error addClassKeywordToDeclarations(); + llvm::Error scopifyEnumValues(); + llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef Prefix); + 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; + SourceManager *SM = nullptr; + llvm::SmallVector> ExtraBuffers; + llvm::StringMap ContentPerFile; + Effect E; +}; + +REGISTER_TWEAK(ScopifyEnum) + +bool ScopifyEnum::prepare(const Selection &Inputs) { + if (!Inputs.AST->getLangOpts().CPlusPlus11) + return false; + const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor(); + if (!N) + return false; + D = N->ASTNode.get(); + return D && !D->isScoped() && D->isThisDeclarationADefinition(); +} + +Expected ScopifyEnum::apply(const Selection &Inputs) { + S = &Inputs; + SM = &S->AST->getSourceManager(); + E.FormatEdits = false; + ContentPerFile.insert(std::make_pair(SM->getFilename(D->getLocation()), + SM->getBufferData(SM->getMainFileID()))); + + if (auto Err = addClassKeywordToDeclarations()) + return std::move(Err); + if (auto Err = scopifyEnumValues()) + return std::move(Err); + + return E; +} + +llvm::Error ScopifyEnum::addClassKeywordToDeclarations() { + for (const auto &Ref : + findReferences(*S->AST, getPosition(*D), 0, S->Index, false) + .References) { + if (!(Ref.Attributes & ReferencesResult::Declaration)) + continue; + + static const auto MakeReplacement = [](StringRef FilePath, + StringRef Content, unsigned Offset) { + return tooling::Replacement(FilePath, Offset, 0, "class "); + }; + if (auto Err = addReplacementForReference(Ref, MakeReplacement)) + return Err; + } + return llvm::Error::success(); +} + +llvm::Error ScopifyEnum::scopifyEnumValues() { + std::string PrefixToInsert(D->getName()); + PrefixToInsert += "::"; + for (auto E : D->enumerators()) { + if (auto Err = scopifyEnumValue(*E, PrefixToInsert)) + return Err; + } + return llvm::Error::success(); +} + +llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD, + StringRef Prefix) { + for (const auto &Ref : + findReferences(*S->AST, getPosition(CD), 0, S->Index, false) + .References) { + if (Ref.Attributes & ReferencesResult::Declaration) + continue; + + const auto MakeReplacement = [&Prefix](StringRef FilePath, + StringRef Content, unsigned Offset) { + const auto IsAlreadyScoped = [Content, Offset] { + if (Offset < 2) + return false; + unsigned I = Offset; + while (--I > 0) { + switch (Content[I]) { + case ' ': + case '\t': + case '\n': + continue; + case ':': + if (Content[I - 1] == ':') + return true; + [[fallthrough]]; + default: + return false; + } + } + return false; + }; + return IsAlreadyScoped() + ? tooling::Replacement() + : tooling::Replacement(FilePath, Offset, 0, Prefix); + }; + if (auto Err = addReplacementForReference(Ref, MakeReplacement)) + return Err; + } + + return llvm::Error::success(); +} + +llvm::Expected ScopifyEnum::getContentForFile(StringRef FilePath) { + if (auto It = ContentPerFile.find(FilePath); It != ContentPerFile.end()) + return It->second; + auto Buffer = S->FS->getBufferForFile(FilePath); + if (!Buffer) + return llvm::errorCodeToError(Buffer.getError()); + StringRef Content = Buffer->get()->getBuffer(); + ExtraBuffers.push_back(std::move(*Buffer)); + ContentPerFile.insert(std::make_pair(FilePath, Content)); + 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); + if (!Content) + return Content.takeError(); + unsigned Offset = getOffsetFromPosition(Ref.Loc.range.start, *Content); + tooling::Replacement Replacement = GetReplacement(FilePath, *Content, Offset); + if (Replacement.isApplicable()) + return addReplacement(FilePath, *Content, Replacement); + return llvm::Error::success(); +} + +llvm::Error +ScopifyEnum::addReplacement(StringRef FilePath, StringRef Content, + const tooling::Replacement &Replacement) { + Edit &TheEdit = E.ApplyEdits[FilePath]; + TheEdit.InitialCode = Content; + if (auto Err = TheEdit.Replacements.add(Replacement)) + return Err; + 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/diagnostics-tidy.test b/clang-tools-extra/clangd/test/diagnostics-tidy.test index a100c9f4359d10810717c3cb3b7391806f38cb40..e592c9a0be7c3578ef8baf60c2cc8051304fe480 100644 --- a/clang-tools-extra/clangd/test/diagnostics-tidy.test +++ b/clang-tools-extra/clangd/test/diagnostics-tidy.test @@ -14,7 +14,7 @@ # CHECK-NEXT: "message": "Suspicious usage of 'sizeof(K)'; did you mean 'K'?", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { -# CHECK-NEXT: "character": 12, +# CHECK-NEXT: "character": 16, # CHECK-NEXT: "line": 1 # CHECK-NEXT: }, # CHECK-NEXT: "start": { diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index f656a8c587c65330f602df60755f9b59e4fc3128..9fd002d0eebba5f314d0bd953210bb9275ab58b9 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -35,6 +35,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" @@ -714,8 +715,8 @@ int clangdMain(int argc, char *argv[]) { // Clang could run on the main thread. e.g., when the flag '-check' or '-sync' // is enabled. clang::noteBottomOfStack(); + llvm::InitLLVM X(argc, argv); llvm::InitializeAllTargetInfos(); - llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::sys::AddSignalHandler( [](void *) { ThreadCrashReporter::runCrashHandlers(); diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index 8d02b91fdd71669d9b5419543ed0758619e5b46d..9cd195eaf164fb6a94306246e35b2bcf6503d4ff 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -131,6 +131,7 @@ add_unittest(ClangdUnitTests ClangdTests tweaks/PopulateSwitchTests.cpp tweaks/RawStringLiteralTests.cpp tweaks/RemoveUsingNamespaceTests.cpp + tweaks/ScopifyEnumTests.cpp tweaks/ShowSelectionTreeTests.cpp tweaks/SpecialMembersTests.cpp tweaks/SwapIfBranchesTests.cpp diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 766998eb4f3c7198a421d75f64949ff81bec5312..692c6db3c51bedf5c6bc9e86a2a50ce232c6dbf1 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3892,6 +3892,29 @@ TEST(CompletionTest, FunctionArgsExist) { kind(CompletionItemKind::Function)))); } +TEST(CompletionTest, FunctionArgsExist_Issue1785) { + // This is a scenario where the implementation of our check for + // "is there a function argument list right after the cursor" + // gave a bogus result. + clangd::CodeCompleteOptions Opts; + Opts.EnableSnippets = true; + // The whitespace in this testcase is important! + std::string Code = R"cpp( +void waldo(int); + +int main() +{ + wal^ + + + // ( ) +} + )cpp"; + EXPECT_THAT( + completions(Code, {}, Opts).Completions, + Contains(AllOf(labeled("waldo(int)"), snippetSuffix("(${1:int})")))); +} + TEST(CompletionTest, NoCrashDueToMacroOrdering) { EXPECT_THAT(completions(R"cpp( #define ECHO(X) X diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index 14dd1f4b3f6d506e8dd101a07d27801b19c24bdb..a52b647b0029b4facb59f2eac7587bdadc69ea4a 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -305,7 +305,7 @@ TEST(DiagnosticsTest, ClangTidy) { int $main[[main]]() { int y = 4; return SQUARE($macroarg[[++]]y); - return $doubled[[sizeof]](sizeof(int)); + return $doubled[[sizeof(sizeof(int))]]; } // misc-no-recursion uses a custom traversal from the TUDecl diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index 063a60db044060eba6de4a938a694b8dff8ced16..35db757b9c15b5d3258552783cbbe5351c8d1a3c 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -3349,6 +3349,20 @@ TEST(Hover, NoCrashAPInt64) { getHover(AST, T.point(), format::getLLVMStyle(), nullptr); } +TEST(Hover, NoCrashInt128) { + Annotations T(R"cpp( + constexpr __int128_t value = -4; + void foo() { va^lue; } + )cpp"); + auto TU = TestTU::withCode(T.code()); + // Need a triple that support __int128_t. + TU.ExtraArgs.push_back("--target=x86_64-pc-linux-gnu"); + auto AST = TU.build(); + auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr); + ASSERT_TRUE(H); + EXPECT_EQ(H->Value, "-4 (0xfffffffc)"); +} + TEST(Hover, DocsFromMostSpecial) { Annotations T(R"cpp( // doc1 diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp index 3d74a941071f8497b2218ad09586d64397e7ee60..eb5b06cfee43166e88cca47676f55f091310317e 100644 --- a/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractVariableTests.cpp @@ -64,6 +64,14 @@ TEST_F(ExtractVariableTest, Test) { int x = [[1]]; })cpp"; EXPECT_AVAILABLE(AvailableC); + + ExtraArgs = {"-xc"}; + const char *NoCrashCasesC = R"cpp( + // error-ok: broken code, but shouldn't crash + int x = [[foo()]]; + )cpp"; + EXPECT_UNAVAILABLE(NoCrashCasesC); + ExtraArgs = {"-xobjective-c"}; const char *AvailableObjC = R"cpp( __attribute__((objc_root_class)) diff --git a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b5a964a5a26d8074260215f3a2ffcee041811bdc --- /dev/null +++ b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp @@ -0,0 +1,58 @@ +//===-- DefineOutline.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TweakTesting.h" +#include "gtest/gtest.h" + +namespace clang::clangd { +namespace { + +TWEAK_TEST(ScopifyEnum); + +TEST_F(ScopifyEnumTest, TriggersOnUnscopedEnumDecl) { + FileName = "Test.hpp"; + // Not available for scoped enum. + EXPECT_UNAVAILABLE(R"cpp(enum class ^E { V };)cpp"); + + // Not available for non-definition. + EXPECT_UNAVAILABLE(R"cpp( +enum E { V }; +enum ^E; +)cpp"); +} + +TEST_F(ScopifyEnumTest, ApplyTest) { + std::string Original = R"cpp( +enum ^E { EV1, EV2, EV3 }; +enum E; +E func(E in) +{ + E out = EV1; + if (in == EV2) + out = E::EV3; + return out; +} +)cpp"; + std::string Expected = R"cpp( +enum class E { EV1, EV2, EV3 }; +enum class E; +E func(E in) +{ + E out = E::EV1; + if (in == E::EV2) + out = E::EV3; + return out; +} +)cpp"; + FileName = "Test.cpp"; + SCOPED_TRACE(Original); + EXPECT_EQ(apply(Original), Expected); +} + +} // namespace +} // namespace clang::clangd diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index fe8c7175d554c7b98c55522d65b46527f02b440d..6d5f49dc06254512128ba9e0099d2b32e1806b8d 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -70,6 +70,7 @@ Code actions ^^^^^^^^^^^^ - The extract variable tweak gained support for extracting lambda expressions to a variable. +- A new tweak was added for turning unscoped into scoped enums. Signature help ^^^^^^^^^^^^^^ @@ -207,6 +208,15 @@ New check aliases Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Improved :doc:`abseil-string-find-startswith + ` check to also consider + ``std::basic_string_view`` in addition to ``std::basic_string`` by default. + +- Improved :doc:`bugprone-assert-side-effect + ` check to report usage of + non-const ``<<`` and ``>>`` operators in assertions and fixed some false-positives + with const operators. + - Improved :doc:`bugprone-dangling-handle ` check to support functional casting during type conversions at variable initialization, now with improved @@ -224,6 +234,10 @@ Changes in existing checks ` check, so that it does not warn on macros starting with underscore and lowercase letter. +- Improved :doc:`bugprone-sizeof-expression + ` check diagnostics to precisely + highlight specific locations, providing more accurate guidance. + - Improved :doc:`bugprone-unchecked-optional-access ` check, so that it does not crash during handling of optional values. @@ -310,7 +324,9 @@ Changes in existing checks - Improved :doc:`misc-const-correctness ` check to avoid false positive when - using pointer to member function. + using pointer to member function. Additionally, the check no longer emits + a diagnostic when a variable that is not type-dependent is an operand of a + type-dependent binary operator. - Improved :doc:`misc-include-cleaner ` check by adding option @@ -328,7 +344,8 @@ Changes in existing checks - Improved :doc:`modernize-avoid-bind ` check to - not emit a ``return`` for fixes when the function returns ``void``. + not emit a ``return`` for fixes when the function returns ``void`` and to + provide valid fixes for cases involving bound C++ operators. - Improved :doc:`modernize-loop-convert ` to support for-loops with @@ -399,11 +416,14 @@ Changes in existing checks ``Leading_upper_snake_case`` naming convention. The handling of ``typedef`` has been enhanced, particularly within complex types like function pointers and cases where style checks were omitted when functions started with macros. + Added support for C++20 ``concept`` declarations. ``Camel_Snake_Case`` and + ``camel_Snake_Case`` now detect more invalid identifier names. - Improved :doc:`readability-implicit-bool-conversion ` check to take do-while loops into account for the `AllowIntegerConditions` and - `AllowPointerConditions` options. + `AllowPointerConditions` options. It also now provides more consistent + suggestions when parentheses are added to the return value. - Improved :doc:`readability-non-const-parameter ` check to ignore diff --git a/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst b/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst index 8224c37a087b8bc3555781a38567f439cc162db5..c82c38772a5c9a8ace23952586dcf686c75f046d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst @@ -3,9 +3,10 @@ abseil-string-find-startswith ============================= -Checks whether a ``std::string::find()`` or ``std::string::rfind()`` result is -compared with 0, and suggests replacing with ``absl::StartsWith()``. This is -both a readability and performance issue. +Checks whether a ``std::string::find()`` or ``std::string::rfind()`` (and +corresponding ``std::string_view`` methods) result is compared with 0, and +suggests replacing with ``absl::StartsWith()``. This is both a readability and +performance issue. .. code-block:: c++ @@ -28,9 +29,9 @@ Options .. option:: StringLikeClasses - Semicolon-separated list of names of string-like classes. By default only - ``std::basic_string`` is considered. The list of methods to considered is - fixed. + Semicolon-separated list of names of string-like classes. By default both + ``std::basic_string`` and ``std::basic_string_view`` are considered. The list + of methods to be considered is fixed. .. option:: IncludeStyle diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst index 5d125a89e7f43240172eb100fe4426b87a45cc51..789235980ad7b7801b44a8b46fc4c04705c88f1d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-c-arrays.rst @@ -8,3 +8,4 @@ hicpp-avoid-c-arrays The hicpp-avoid-c-arrays check is an alias, please see :doc:`modernize-avoid-c-arrays <../modernize/avoid-c-arrays>` for more information. +It partly enforces the `rule 4.1.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst index ffef01ca5d4eb178d655dde918dffcc29cda6dcf..ccc2a7b34efe540f43d5b32fa01dde363a5d7da3 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/avoid-goto.rst @@ -5,10 +5,7 @@ hicpp-avoid-goto ================ -The `hicpp-avoid-goto` check is an alias to -:doc:`cppcoreguidelines-avoid-goto <../cppcoreguidelines/avoid-goto>`. -Rule `6.3.1 High Integrity C++ `_ -requires that ``goto`` only skips parts of a block and is not used for other -reasons. - -Both coding guidelines implement the same exception to the usage of ``goto``. +The `hicpp-avoid-goto` check is an alias, please see +:doc:`cppcoreguidelines-avoid-goto <../cppcoreguidelines/avoid-goto>` +for more information. +It enforces the `rule 6.3.1 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst index 381fb365e800ea3120e702cd8b5557a0ba75a34c..cfc6e9b6ec3477a77cf0fb639b5560c0c4664baf 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/hicpp/no-assembler.rst @@ -1,10 +1,10 @@ .. title:: clang-tidy - hicpp-no-assembler hicpp-no-assembler -=================== +================== -Check for assembler statements. No fix is offered. +Checks for assembler statements. Use of inline assembly should be avoided since +it restricts the portability of the code. -Inline assembler is forbidden by the `High Integrity C++ Coding Standard -`_ -as it restricts the portability of code. +This enforces `rule 7.5.1 `_ +of the High Integrity C++ Coding Standard. diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/coroutine-hostile-raii.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/coroutine-hostile-raii.rst index b8698ba3de8530039807d7ab841e540cb926c3a0..a39c1853b313c49087794d17a4ff99d52541fceb 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/coroutine-hostile-raii.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/coroutine-hostile-raii.rst @@ -29,16 +29,15 @@ Following types are considered as hostile: .. code-block:: c++ // Call some async API while holding a lock. - { - const my::MutexLock l(&mu_); + task coro() { + const std::lock_guard l(&mu_); // Oops! The async Bar function may finish on a different - // thread from the one that created the MutexLock object and therefore called - // Mutex::Lock -- now Mutex::Unlock will be called on the wrong thread. + // thread from the one that created the lock_guard (and called + // Mutex::Lock). After suspension, Mutex::Unlock will be called on the wrong thread. co_await Bar(); } - Options ------- @@ -48,3 +47,37 @@ Options persist across suspension points. Eg: ``my::lockable; a::b;::my::other::lockable;`` The default value of this option is `"std::lock_guard;std::scoped_lock"`. + +.. option:: AllowedAwaitablesList + + A semicolon-separated list of qualified types of awaitables types which can + be safely awaited while having hostile RAII objects in scope. + + ``co_await``-ing an expression of ``awaitable`` type is considered + safe if the ``awaitable`` type is part of this list. + RAII objects persisting across such a ``co_await`` expression are + considered safe and hence are not flagged. + + Example usage: + + .. code-block:: c++ + + // Consider option AllowedAwaitablesList = "safe_awaitable" + struct safe_awaitable { + bool await_ready() noexcept { return false; } + void await_suspend(std::coroutine_handle<>) noexcept {} + void await_resume() noexcept {} + }; + auto wait() { return safe_awaitable{}; } + + task coro() { + // This persists across both the co_await's but is not flagged + // because the awaitable is considered safe to await on. + const std::lock_guard l(&mu_); + co_await safe_awaitable{}; + co_await wait(); + } + + Eg: ``my::safe::awaitable;other::awaitable`` + The default value of this option is empty string `""`. + diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance/enum-size.rst b/clang-tools-extra/docs/clang-tidy/checks/performance/enum-size.rst index 08054123366eee4210c21445bcd8fc6a11710995..f72b8c7eabc2221e1e8c57f8be6abd1d05595770 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/performance/enum-size.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/performance/enum-size.rst @@ -58,6 +58,8 @@ terms of memory usage and cache performance. However, it's important to consider the trade-offs and potential impact on code readability and maintainability. +Enums without enumerators (empty) are excluded from analysis. + Requires C++11 or above. Does not provide auto-fixes. diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst index 608bc2acdc0d5e561cce1f6fe0b17ef47cb14cb6..e36bbee394f17aef3cb98d6e9030bd7994571361 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst @@ -46,6 +46,7 @@ The following options are described below: - :option:`ClassConstantCase`, :option:`ClassConstantPrefix`, :option:`ClassConstantSuffix`, :option:`ClassConstantIgnoredRegexp`, :option:`ClassConstantHungarianPrefix` - :option:`ClassMemberCase`, :option:`ClassMemberPrefix`, :option:`ClassMemberSuffix`, :option:`ClassMemberIgnoredRegexp`, :option:`ClassMemberHungarianPrefix` - :option:`ClassMethodCase`, :option:`ClassMethodPrefix`, :option:`ClassMethodSuffix`, :option:`ClassMethodIgnoredRegexp` + - :option:`ConceptCase`, :option:`ConceptPrefix`, :option:`ConceptSuffix`, :option:`ConceptIgnoredRegexp` - :option:`ConstantCase`, :option:`ConstantPrefix`, :option:`ConstantSuffix`, :option:`ConstantIgnoredRegexp`, :option:`ConstantHungarianPrefix` - :option:`ConstantMemberCase`, :option:`ConstantMemberPrefix`, :option:`ConstantMemberSuffix`, :option:`ConstantMemberIgnoredRegexp`, :option:`ConstantMemberHungarianPrefix` - :option:`ConstantParameterCase`, :option:`ConstantParameterPrefix`, :option:`ConstantParameterSuffix`, :option:`ConstantParameterIgnoredRegexp`, :option:`ConstantParameterHungarianPrefix` @@ -410,6 +411,46 @@ After: int pre_class_member_post(); }; +.. option:: ConceptCase + + When defined, the check will ensure concept names conform to the + selected casing. + +.. option:: ConceptPrefix + + When defined, the check will ensure concept names will add the + prefixed with the given value (regardless of casing). + +.. option:: ConceptIgnoredRegexp + + Identifier naming checks won't be enforced for concept names + matching this regular expression. + +.. option:: ConceptSuffix + + When defined, the check will ensure concept names will add the + suffix with the given value (regardless of casing). + +For example using values of: + + - ConceptCase of ``CamelCase`` + - ConceptPrefix of ``Pre`` + - ConceptSuffix of ``Post`` + +Identifies and/or transforms concept names as follows: + +Before: + +.. code-block:: c++ + + template concept my_concept = requires (T t) { {t++}; }; + +After: + +.. code-block:: c++ + + template concept PreMyConceptPost = requires (T t) { {t++}; }; + .. option:: ConstantCase When defined, the check will ensure constant names conform to the diff --git a/clang-tools-extra/include-cleaner/lib/Record.cpp b/clang-tools-extra/include-cleaner/lib/Record.cpp index 7a8e10a9c6754962c67afe192bd129e9a003fa94..6e00ff93a7fe2fae384026e9db8d05bcad73a7c3 100644 --- a/clang-tools-extra/include-cleaner/lib/Record.cpp +++ b/clang-tools-extra/include-cleaner/lib/Record.cpp @@ -240,20 +240,10 @@ public: // Make sure current include is covered by the export pragma. if ((Top.Block && HashLine > Top.SeenAtLine) || Top.SeenAtLine == HashLine) { - if (IncludedHeader) { - switch (IncludedHeader->kind()) { - case Header::Physical: - Out->IWYUExportBy[IncludedHeader->physical().getUniqueID()] - .push_back(Top.Path); - break; - case Header::Standard: - Out->StdIWYUExportBy[IncludedHeader->standard()].push_back(Top.Path); - break; - case Header::Verbatim: - assert(false && "unexpected Verbatim header"); - break; - } - } + if (IncludedFile) + Out->IWYUExportBy[IncludedFile->getUniqueID()].push_back(Top.Path); + if (IncludedHeader && IncludedHeader->kind() == Header::Standard) + Out->StdIWYUExportBy[IncludedHeader->standard()].push_back(Top.Path); // main-file #include with export pragma should never be removed. if (Top.SeenAtFile == SM.getMainFileID() && IncludedFile) Out->ShouldKeep.insert(IncludedFile->getUniqueID()); diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp index 307e0652f9ccaf8091ebea93c4e05bf558f02aa0..6c4d9b7862d915b3660a111d2994592848aec7df 100644 --- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -11,6 +11,7 @@ #include "clang/AST/ASTFwd.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -243,6 +244,14 @@ public: return true; } + bool VisitFriendDecl(FriendDecl *D) { + // We already visit the TypeLoc properly, but need to special case the decl + // case. + if (auto *FD = D->getFriendDecl()) + report(D->getLocation(), FD); + return true; + } + bool VisitConceptReference(const ConceptReference *CR) { report(CR->getConceptNameLoc(), CR->getFoundDecl()); return true; diff --git a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp index 36850731d5145399c00d339ef29c6045b81a8f0a..0f2ded5f18345311ba93de370c1895a4ccc6c8d5 100644 --- a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp @@ -452,6 +452,8 @@ TEST_F(PragmaIncludeTest, IWYUExportForStandardHeaders) { auto &FM = Processed.fileManager(); EXPECT_THAT(PI.getExporters(*tooling::stdlib::Header::named(""), FM), testing::UnorderedElementsAre(FileNamed("export.h"))); + EXPECT_THAT(PI.getExporters(llvm::cantFail(FM.getFileRef("string")), FM), + testing::UnorderedElementsAre(FileNamed("export.h"))); } TEST_F(PragmaIncludeTest, IWYUExportBlock) { @@ -556,5 +558,35 @@ TEST_F(PragmaIncludeTest, ExportInUnnamedBuffer) { PI.getExporters(llvm::cantFail(FM->getFileRef("foo.h")), *FM), testing::ElementsAre(llvm::cantFail(FM->getFileRef("exporter.h")))); } + +TEST_F(PragmaIncludeTest, OutlivesFMAndSM) { + Inputs.Code = R"cpp( + #include "public.h" + )cpp"; + Inputs.ExtraFiles["public.h"] = R"cpp( + #include "private.h" + #include "private2.h" // IWYU pragma: export + )cpp"; + Inputs.ExtraFiles["private.h"] = R"cpp( + // IWYU pragma: private, include "public.h" + )cpp"; + Inputs.ExtraFiles["private2.h"] = R"cpp( + // IWYU pragma: private + )cpp"; + build(); // Fills up PI, file/source manager used is destroyed afterwards. + Inputs.MakeAction = nullptr; // Don't populate PI anymore. + + // Now this build gives us a new File&Source Manager. + TestAST Processed = build(); + auto &FM = Processed.fileManager(); + auto PrivateFE = FM.getFile("private.h"); + assert(PrivateFE); + EXPECT_EQ(PI.getPublic(PrivateFE.get()), "\"public.h\""); + + auto Private2FE = FM.getFile("private2.h"); + assert(Private2FE); + EXPECT_THAT(PI.getExporters(Private2FE.get(), FM), + testing::ElementsAre(llvm::cantFail(FM.getFileRef("public.h")))); +} } // namespace } // namespace clang::include_cleaner diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp index 9b99d5a5c32bad3f113a89f507f3b7c22a990a7c..bdfc24b8edee38f0d1f6d4623e059aa0a8cca0cc 100644 --- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -550,5 +550,10 @@ TEST(WalkAST, Concepts) { // FIXME: Foo should be explicitly referenced. testWalk("template concept Foo = true;", "void func() { ^Foo auto x = 1; }"); } + +TEST(WalkAST, FriendDecl) { + testWalk("void $explicit^foo();", "struct Bar { friend void ^foo(); };"); + testWalk("struct $explicit^Foo {};", "struct Bar { friend struct ^Foo; };"); +} } // namespace } // namespace clang::include_cleaner 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 6f569655e6762a9dd8e3b72448341e0dda1f6d04..d0aac7b78ec939b71e7196a4450d25c2879e800b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string @@ -43,6 +43,11 @@ struct basic_string { size_type find(const C* s, size_type pos = 0) const; size_type find(const C* s, size_type pos, size_type n) const; + size_type rfind(const _Type& str, size_type pos = npos) const; + size_type rfind(const C* s, size_type pos, size_type count) const; + size_type rfind(const C* s, size_type pos = npos) const; + size_type rfind(C ch, size_type pos = npos) const; + _Type& insert(size_type pos, const _Type& str); _Type& insert(size_type pos, const C* s); _Type& insert(size_type pos, const C* s, size_type n); @@ -54,6 +59,8 @@ struct basic_string { _Type& operator+=(const C* s); _Type& operator=(const _Type& str); _Type& operator=(const C* s); + + static constexpr size_t npos = -1; }; typedef basic_string string; @@ -63,8 +70,23 @@ typedef basic_string u32string; template > struct basic_string_view { + typedef size_t size_type; + typedef basic_string_view _Type; + const C *str; constexpr basic_string_view(const C* s) : str(s) {} + + size_type find(_Type v, size_type pos = 0) const; + size_type find(C ch, size_type pos = 0) const; + size_type find(const C* s, size_type pos, size_type count) const; + size_type find(const C* s, size_type pos = 0) const; + + size_type rfind(_Type v, size_type pos = npos) const; + size_type rfind(C ch, size_type pos = npos) const; + size_type rfind(const C* s, size_type pos, size_type count) const; + size_type rfind(const C* s, size_type pos = npos) const; + + static constexpr size_t npos = -1; }; typedef basic_string_view string_view; typedef basic_string_view wstring_view; diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp b/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp index e51568077eda151a6c9d9a443074de6c48c7d21b..417598790bc007f8eeee6ae0bc8a8029f6203f2d 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp @@ -1,27 +1,14 @@ // RUN: %check_clang_tidy %s abseil-string-find-startswith %t -- \ -// RUN: -config="{CheckOptions: {abseil-string-find-startswith.StringLikeClasses: '::std::basic_string;::basic_string'}}" +// RUN: -config="{CheckOptions: \ +// RUN: {abseil-string-find-startswith.StringLikeClasses: \ +// RUN: '::std::basic_string;::std::basic_string_view;::basic_string'}}" \ +// RUN: -- -isystem %clang_tidy_headers + +#include using size_t = decltype(sizeof(int)); namespace std { -template class allocator {}; -template class char_traits {}; -template , - typename A = std::allocator> -struct basic_string { - basic_string(); - basic_string(const basic_string &); - basic_string(const C *, const A &a = A()); - ~basic_string(); - int find(basic_string s, int pos = 0); - int find(const char *s, int pos = 0); - int rfind(basic_string s, int pos = npos); - int rfind(const char *s, int pos = npos); - static constexpr size_t npos = -1; -}; -typedef basic_string string; -typedef basic_string wstring; - struct cxx_string { int find(const char *s, int pos = 0); int rfind(const char *s, int pos = npos); @@ -39,7 +26,7 @@ std::string bar(); #define A_MACRO(x, y) ((x) == (y)) -void tests(std::string s, global_string s2) { +void tests(std::string s, global_string s2, std::string_view sv) { s.find("a") == 0; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith instead of find() == 0 [abseil-string-find-startswith] // CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s, "a");{{$}} @@ -96,6 +83,14 @@ void tests(std::string s, global_string s2) { // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith // CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s2, "a");{{$}} + sv.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith + // CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(sv, "a");{{$}} + + sv.rfind("a", 0) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StartsWith + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StartsWith(sv, "a");{{$}} + // expressions that don't trigger the check are here. A_MACRO(s.find("a"), 0); A_MACRO(s.rfind("a", 0), 0); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assert-side-effect.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assert-side-effect.cpp index 6c41e1e320adeac2cc1434b7d9f77286faf80c72..c11638aa823aaebe5e39b225c68cbe89a446c20b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assert-side-effect.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assert-side-effect.cpp @@ -84,5 +84,27 @@ int main() { msvc_assert(mc2 = mc); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: side effect in msvc_assert() condition discarded in release builds + struct OperatorTest { + int operator<<(int i) const { return i; } + int operator<<(int i) { return i; } + int operator+=(int i) const { return i; } + int operator+=(int i) { return i; } + }; + + const OperatorTest const_instance; + assert(const_instance << 1); + assert(const_instance += 1); + + OperatorTest non_const_instance; + assert(static_cast(non_const_instance) << 1); + assert(static_cast(non_const_instance) += 1); + assert(non_const_instance << 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: side effect in assert() condition discarded in release builds + assert(non_const_instance += 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: side effect in assert() condition discarded in release builds + + assert(5<<1); + assert(5>>1); + return 0; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp index a49cd99eeb7dc3d52eefe04fca935723c818775b..5ef2c46ffbdf1344e95e2cec7dc88409b9427148 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression-warn-on-sizeof-pointer-to-aggregate.cpp @@ -68,9 +68,9 @@ int Test5() { sum += sizeof(A10) / sizeof(PtrArray[0]); // No warning. sum += sizeof(PC) / sizeof(PtrArray[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' sum += sizeof(ArrayC) / sizeof(PtrArray[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator return sum; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp index 0e6e87170352c464ee01f608105ec91254b7f08a..003a02209c3d2deee8cc0592b229c601d528b401 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sizeof-expression.cpp @@ -77,7 +77,7 @@ int Test1(const char* ptr) { sum += sizeof(LEN + 1); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)' sum += sizeof(sum, LEN); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(..., ...)' + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(..., ...)' sum += sizeof(AsBool()); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof()' on an expression that results in an integer sum += sizeof(AsInt()); @@ -103,41 +103,41 @@ int Test1(const char* ptr) { sum += sizeof(LEN + - + -sizeof(X)); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))' sum += sizeof(char) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' sum += sizeof(A) / sizeof(S); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(char) / sizeof(int); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(char) / sizeof(A); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(B[0]) / sizeof(A); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator sum += sizeof(ptr) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' sum += sizeof(ptr) / sizeof(ptr[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' sum += sizeof(ptr) / sizeof(char*); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' sum += sizeof(ptr) / sizeof(void*); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' sum += sizeof(ptr) / sizeof(const void volatile*); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)' sum += sizeof(ptr) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' sum += sizeof(ptr) / sizeof(ptr[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)' sum += sizeof(int) * sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication sum += sizeof(ptr) * sizeof(ptr[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication sum += sizeof(int) * (2 * sizeof(char)); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious 'sizeof' by 'sizeof' multiplication sum += (2 * sizeof(char)) * sizeof(int); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: suspicious 'sizeof' by 'sizeof' multiplication if (sizeof(A) < 0x100000) sum += 42; - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious comparison of 'sizeof(expr)' to a constant + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant if (sizeof(A) <= 0xFFFFFFFEU) sum += 42; - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious comparison of 'sizeof(expr)' to a constant + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: suspicious comparison of 'sizeof(expr)' to a constant return sum; } @@ -158,11 +158,11 @@ int CE4 = sizeof sizeof(MyConstChar); int Test2(MyConstChar* A) { int sum = 0; sum += sizeof(MyConstChar) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' sum += sizeof(MyConstChar) / sizeof(MyChar); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' sum += sizeof(A[0]) / sizeof(char); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' return sum; } @@ -171,7 +171,7 @@ int Foo() { int A[T]; return sizeof(T); } // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(K)' template int Bar() { T A[5]; return sizeof(A[0]) / sizeof(T); } -// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' +// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' int Test3() { return Foo<42>() + Bar(); } static const char* kABC = "abc"; @@ -246,10 +246,10 @@ int Test5() { // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate sum += sizeof(PC) / sizeof(PtrArray[0]); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate - // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' + // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)' // CHECK-MESSAGES: :[[@LINE-3]]:23: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate sum += sizeof(ArrayC) / sizeof(PtrArray[0]); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate return sum; @@ -263,34 +263,34 @@ int Test6() { sum += sizeof(struct S) == P - Q; // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += 5 * sizeof(S) != P - Q; - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += sizeof(S) < P - Q; // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += 5 * sizeof(S) <= P - Q; - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += 5 * sizeof(*P) >= P - Q; - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += Q - P > 3 * sizeof(*P); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += sizeof(S) + (P - Q); // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += 5 * sizeof(S) - (P - Q); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += (P - Q) / sizeof(S); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic sum += (P - Q) / sizeof(*Q); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic return sum; } #ifdef __SIZEOF_INT128__ -template <__int128_t N> +template <__int128_t N> #else template // Fallback for platforms which do not define `__int128_t` #endif bool Baz() { return sizeof(A) < N; } -// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious comparison of 'sizeof(expr)' to a constant +// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: suspicious comparison of 'sizeof(expr)' to a constant bool Test7() { return Baz<-1>(); } int ValidExpressions() { diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp index 7b5ccabdd6ef61185094b337fbc80324e8e85a3a..794578ceeeba8fc15ef09ab71e3a67944a5842fd 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-templates.cpp @@ -20,3 +20,13 @@ void instantiate_template_cases() { type_dependent_variables(); type_dependent_variables(); } + +namespace gh57297{ +// The expression to check may not be the dependent operand in a dependent +// operator. + +// Explicitly not declaring a (templated) stream operator +// so the `<<` is a `binaryOperator` with a dependent type. +struct Stream { }; +template void f() { T t; Stream x; x << t; } +} // namespace gh57297 diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp index 2d022e21c85d5665e1df5228f87ec5718282c0ed..55a7e4b8f2954aa187d9a5b6a7df3a0e39116f35 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp @@ -1,7 +1,8 @@ // RUN: %check_clang_tidy -std=c++20 %s misc-coroutine-hostile-raii %t \ -// RUN: -config="{CheckOptions: \ -// RUN: {misc-coroutine-hostile-raii.RAIITypesList: \ -// RUN: 'my::Mutex; ::my::other::Mutex'}}" +// RUN: -config="{CheckOptions: {\ +// RUN: misc-coroutine-hostile-raii.RAIITypesList: 'my::Mutex; ::my::other::Mutex', \ +// RUN: misc-coroutine-hostile-raii.AllowedAwaitablesList: 'safe::awaitable; ::my::other::awaitable' \ +// RUN: }}" namespace std { @@ -135,6 +136,20 @@ ReturnObject scopedLockableTest() { absl::Mutex no_warning_5; } +namespace safe { + struct awaitable { + bool await_ready() noexcept { return false; } + void await_suspend(std::coroutine_handle<>) noexcept {} + void await_resume() noexcept {} +}; +} // namespace safe +ReturnObject RAIISafeSuspendTest() { + absl::Mutex a; + co_await safe::awaitable{}; + using other = safe::awaitable; + co_await other{}; +} + void lambda() { absl::Mutex no_warning; auto lambda = []() -> ReturnObject { diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-bind.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-bind.cpp index 336b3cb4ee08f9470afddf5e1f895a1313a7bf56..22b24d45fe63f76367cfacc9a719a5586accec86 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-bind.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-bind.cpp @@ -43,6 +43,7 @@ struct Foo { struct D { D() = default; void operator()(int x, int y) const {} + operator bool() const { return true; } void MemberFunction(int x) {} int MemberFunctionWithReturn(int x) {} @@ -342,6 +343,7 @@ void testCapturedSubexpressions() { struct E { void MemberFunction(int x) {} int MemberFunctionWithReturn(int x) {} + int operator()(int x, int y) const { return x + y; } void testMemberFunctions() { D *d; @@ -379,6 +381,26 @@ struct E { auto HHH = std::bind(&D::MemberFunctionWithReturn, _1, 1); // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind // CHECK-FIXES: auto HHH = [](auto && PH1) { return PH1->MemberFunctionWithReturn(1); }; + + auto III = std::bind(&D::operator(), d, 1, 2); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind + // CHECK-FIXES: auto III = [d] { (*d)(1, 2); } + + auto JJJ = std::bind(&D::operator(), &dd, 1, 2); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind + // CHECK-FIXES: auto JJJ = [ObjectPtr = &dd] { (*ObjectPtr)(1, 2); } + + auto KKK = std::bind(&D::operator(), _1, 1, 2); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind + // CHECK-FIXES: auto KKK = [](auto && PH1) { (*PH1)(1, 2); }; + + auto LLL = std::bind(&D::operator bool, d); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind + // CHECK-FIXES: auto LLL = [d] { return d->operator bool(); } + + auto MMM = std::bind(&E::operator(), this, 1, 2); + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer a lambda to std::bind + // CHECK-FIXES: auto MMM = [this] { return (*this)(1, 2); } } }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/enum-size.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/enum-size.cpp index 37481a8141c5c458ab8c0c85379c8f50f6144a66..782c12080f5180ee95a7685d7221fc6aa3718446 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/enum-size.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/enum-size.cpp @@ -102,4 +102,7 @@ enum class IgnoredSecondEnum unused2 = 2 }; +enum class EnumClassWithoutValues : int {}; +enum EnumWithoutValues {}; + } diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-case-match.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-case-match.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f692b01923455e879500abf6079c565e6643b8f2 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-case-match.cpp @@ -0,0 +1,60 @@ +// RUN: %check_clang_tidy -std=c++20 %s readability-identifier-naming %t -- \ +// RUN: -config='{CheckOptions: { \ +// RUN: readability-identifier-naming.ClassCase: Camel_Snake_Case, \ +// RUN: readability-identifier-naming.StructCase: camel_Snake_Back, \ +// RUN: }}' + +// clang-format off + +//===----------------------------------------------------------------------===// +// Camel_Snake_Case tests +//===----------------------------------------------------------------------===// +class XML_Parser {}; +class Xml_Parser {}; +class XML_Parser_2 {}; +// NO warnings or fixes expected as these identifiers are Camel_Snake_Case + +class XmlParser {}; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'XmlParser' +// CHECK-FIXES: {{^}}class Xml_Parser {};{{$}} + +class Xml_parser {}; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'Xml_parser' +// CHECK-FIXES: {{^}}class Xml_Parser {};{{$}} + +class xml_parser {}; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'xml_parser' +// CHECK-FIXES: {{^}}class Xml_Parser {};{{$}} + +class xml_Parser {}; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'xml_Parser' +// CHECK-FIXES: {{^}}class Xml_Parser {};{{$}} + +class xml_Parser_2 {}; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'xml_Parser_2' +// CHECK-FIXES: {{^}}class Xml_Parser_2 {};{{$}} + +class t {}; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 't' +// CHECK-FIXES: {{^}}class T {};{{$}} + +//===----------------------------------------------------------------------===// +// camel_Snake_Back tests +//===----------------------------------------------------------------------===// +struct json_Parser {}; +struct json_Parser_2 {}; +struct u {}; +// NO warnings or fixes expected as these identifiers are camel_Snake_Back + +struct JsonParser {}; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: invalid case style for struct 'JsonParser' +// CHECK-FIXES: {{^}}struct json_Parser {};{{$}} + +struct Json_parser {}; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: invalid case style for struct 'Json_parser' +// CHECK-FIXES: {{^}}struct json_Parser {};{{$}} + +struct json_parser {}; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: invalid case style for struct 'json_parser' +// CHECK-FIXES: {{^}}struct json_Parser {};{{$}} + diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp index 84bf7764583e801747b8d2050eac70b135285e08..d2e89a7c9855c9a8b9407406cbc7227b9c24ef44 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp @@ -11,6 +11,7 @@ // RUN: readability-identifier-naming.ClassConstantPrefix: 'k', \ // RUN: readability-identifier-naming.ClassMemberCase: CamelCase, \ // RUN: readability-identifier-naming.ClassMethodCase: camelBack, \ +// RUN: readability-identifier-naming.ConceptCase: CamelCase, \ // RUN: readability-identifier-naming.ConstantCase: UPPER_CASE, \ // RUN: readability-identifier-naming.ConstantSuffix: '_CST', \ // RUN: readability-identifier-naming.ConstexprFunctionCase: lower_case, \ @@ -251,6 +252,15 @@ class my_derived_class : public virtual my_class {}; class CMyWellNamedClass {}; // No warning expected as this class is well named. +template +concept MyConcept = requires (t_t a_t) { {a_t++}; }; +// No warning expected as this concept is well named. + +template +concept my_concept_2 = requires (t_t a_t) { {a_t++}; }; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for concept 'my_concept_2' +// CHECK-FIXES: {{^}}concept MyConcept2 = requires (t_t a_t) { {a_t++}; };{{$}} + template class CMyWellNamedClass2 : public my_class { // CHECK-FIXES: {{^}}class CMyWellNamedClass2 : public CMyClass {{{$}} @@ -413,7 +423,8 @@ class my_other_templated_class : my_templated_class< my_class>, private my_deri template using mysuper_tpl_t = my_other_templated_class <:: FOO_NS ::my_class>; -// CHECK-FIXES: {{^}}using mysuper_tpl_t = CMyOtherTemplatedClass <:: foo_ns ::CMyClass>;{{$}} +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for type alias 'mysuper_tpl_t' +// CHECK-FIXES: {{^}}using mysuper_Tpl_t = CMyOtherTemplatedClass <:: foo_ns ::CMyClass>;{{$}} const int global_Constant = 6; // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: invalid case style for global constant 'global_Constant' diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.cpp index 323cf813c0470009554a14b11c017f398c15223e..f7f5d506a9ce0e0b8311cc78c531f554f4a92252 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/implicit-bool-conversion.cpp @@ -472,6 +472,36 @@ bool f(S& s) { } // namespace ignore_1bit_bitfields +int implicitConversionReturnInt() +{ + return true; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion bool -> 'int' + // CHECK-FIXES: return 1 +} + +int implicitConversionReturnIntWithParens() +{ + return (true); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion bool -> 'int' + // CHECK-FIXES: return 1 +} + + +bool implicitConversionReturnBool() +{ + return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'int' -> bool + // CHECK-FIXES: return true +} + +bool implicitConversionReturnBoolWithParens() +{ + return (1); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: implicit conversion 'int' -> bool + // CHECK-FIXES: return true +} + + namespace PR47000 { int to_int(bool x) { return int{x}; } diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake b/clang/cmake/caches/Fuchsia-stage2.cmake index 13bb5ad3546e98f2dbec998d70031ae68d89579d..4b9085d99378c6fb28fb11d509bcf4df4b777d5b 100644 --- a/clang/cmake/caches/Fuchsia-stage2.cmake +++ b/clang/cmake/caches/Fuchsia-stage2.cmake @@ -81,6 +81,7 @@ if(APPLE) set(LIBCXX_ABI_VERSION 2 CACHE STRING "") set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "") set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "") + set(LIBCXX_HARDENING_MODE "none" CACHE STRING "") set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "") set(RUNTIMES_CMAKE_ARGS "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13;-DCMAKE_OSX_ARCHITECTURES=arm64|x86_64" CACHE STRING "") endif() diff --git a/clang/cmake/caches/Fuchsia.cmake b/clang/cmake/caches/Fuchsia.cmake index 9c68be0bfe54b699dfb6b9fa623c7c7475ed60f3..dad434be720da11beabbbdf7a47a54dee84fd661 100644 --- a/clang/cmake/caches/Fuchsia.cmake +++ b/clang/cmake/caches/Fuchsia.cmake @@ -116,6 +116,7 @@ else() set(LIBCXX_ABI_VERSION 2 CACHE STRING "") set(LIBCXX_ENABLE_SHARED OFF CACHE BOOL "") set(LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "") + set(LIBCXX_HARDENING_MODE "none" CACHE STRING "") set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "") set(LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "") set(RUNTIMES_CMAKE_ARGS "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13;-DCMAKE_OSX_ARCHITECTURES=arm64|x86_64" CACHE STRING "") @@ -197,6 +198,9 @@ foreach(variableName ${variableNames}) endif() endforeach() +# TODO: This is a temporary workaround until we figure out the right solution. +set(BOOTSTRAP_LLVM_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "") + # Setup the bootstrap build. set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "") set(CLANG_BOOTSTRAP_EXTRA_DEPS diff --git a/clang/docs/AutomaticReferenceCounting.rst b/clang/docs/AutomaticReferenceCounting.rst index 820ad3978d5f20633aa125f1611a4ca459ffbbec..bcac73215c9d32e9479441821cdaa1ca008d92e2 100644 --- a/clang/docs/AutomaticReferenceCounting.rst +++ b/clang/docs/AutomaticReferenceCounting.rst @@ -839,8 +839,21 @@ and non-ownership qualification. object lvalue. * For ``__weak`` objects, the current pointee is retained and then released at - the end of the current full-expression. This must execute atomically with - respect to assignments and to the final release of the pointee. + the end of the current full-expression. In particular, messaging a ``__weak`` + object keeps the object retained until the end of the full expression. + + .. code-block:: objc + + __weak MyObject *weakObj; + + void foo() { + // weakObj is retained before the message send and released at the end of + // the full expression. + [weakObj m]; + } + + This must execute atomically with respect to assignments and to the final + release of the pointee. * For all other objects, the lvalue is loaded with primitive semantics. :arc-term:`Assignment` occurs when evaluating an assignment operator. The diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 21342e1b89ea866a0e951bde5b9f3e02973626cb..37d76d5c2dd58c05a47af49a0b4f9ce50314f26e 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2046,11 +2046,28 @@ the configuration (without a prefix: ``Auto``). }; } +.. _BreakAdjacentStringLiterals: + +**BreakAdjacentStringLiterals** (``Boolean``) :versionbadge:`clang-format 18` :ref:`¶ ` + Break between adjacent string literals. + + .. code-block:: c++ + + true: + return "Code" + "\0\52\26\55\55\0" + "x013" + "\02\xBA"; + false: + return "Code" "\0\52\26\55\55\0" "x013" "\02\xBA"; + .. _BreakAfterAttributes: **BreakAfterAttributes** (``AttributeBreakingStyle``) :versionbadge:`clang-format 16` :ref:`¶ ` - Break after a group of C++11 attributes before a function - declaration/definition name. + Break after a group of C++11 attributes before variable or function + (including constructor/destructor) declaration/definition names or before + control statements, i.e. ``if``, ``switch`` (including ``case`` and + ``default`` labels), ``for``, and ``while`` statements. Possible values: @@ -2059,28 +2076,83 @@ the configuration (without a prefix: ``Auto``). .. code-block:: c++ + [[maybe_unused]] + const int i; + [[gnu::const]] [[maybe_unused]] + int j; + [[nodiscard]] inline int f(); [[gnu::const]] [[nodiscard]] int g(); + [[likely]] + if (a) + f(); + else + g(); + + switch (b) { + [[unlikely]] + case 1: + ++b; + break; + [[likely]] + default: + return; + } + * ``ABS_Leave`` (in configuration: ``Leave``) Leave the line breaking after attributes as is. .. code-block:: c++ + [[maybe_unused]] const int i; + [[gnu::const]] [[maybe_unused]] + int j; + [[nodiscard]] inline int f(); [[gnu::const]] [[nodiscard]] int g(); + [[likely]] if (a) + f(); + else + g(); + + switch (b) { + [[unlikely]] case 1: + ++b; + break; + [[likely]] + default: + return; + } + * ``ABS_Never`` (in configuration: ``Never``) Never break after attributes. .. code-block:: c++ + [[maybe_unused]] const int i; + [[gnu::const]] [[maybe_unused]] int j; + [[nodiscard]] inline int f(); [[gnu::const]] [[nodiscard]] int g(); + [[likely]] if (a) + f(); + else + g(); + + switch (b) { + [[unlikely]] case 1: + ++b; + break; + [[likely]] default: + return; + } + .. _BreakAfterJavaFieldAnnotations: diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index b7d88d3d67d0a0c7805887365d6214b3b1baee68..f8e3da5f9736829ab7f409a2f1372ba8222afd64 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -3309,6 +3309,173 @@ are similar. as syntax highlighting, cross-referencing, and so on. The ``c-index-test`` helper program can be used to test these features. +Testing +------- +All functional changes to Clang should come with test coverage demonstrating +the change in behavior. + +Verifying Diagnostics +^^^^^^^^^^^^^^^^^^^^^ +Clang ``-cc1`` supports the ``-verify`` command line option as a way to +validate diagnostic behavior. This option will use special comments within the +test file to verify that expected diagnostics appear in the correct source +locations. If all of the expected diagnostics match the actual output of Clang, +then the invocation will return normally. If there are discrepancies between +the expected and actual output, Clang will emit detailed information about +which expected diagnostics were not seen or which unexpected diagnostics were +seen, etc. A complete example is: + +.. code-block: c++ + + // RUN: %clang_cc1 -verify %s + int A = B; // expected-error {{use of undeclared identifier 'B'}} + +If the test is run and the expected error is emitted on the expected line, the +diagnostic verifier will pass. However, if the expected error does not appear +or appears in a different location than expected, or if additional diagnostics +appear, the diagnostic verifier will fail and emit information as to why. + +The ``-verify`` command optionally accepts a comma-delimited list of one or +more verification prefixes that can be used to craft those special comments. +Each prefix must start with a letter and contain only alphanumeric characters, +hyphens, and underscores. ``-verify`` by itself is equivalent to +``-verify=expected``, meaning that special comments will start with +``expected``. Using different prefixes makes it easier to have separate +``RUN:`` lines in the same test file which result in differing diagnostic +behavior. For example: + +.. code-block:: c++ + + // RUN: %clang_cc1 -verify=foo,bar %s + + int A = B; // foo-error {{use of undeclared identifier 'B'}} + int C = D; // bar-error {{use of undeclared identifier 'D'}} + int E = F; // expected-error {{use of undeclared identifier 'F'}} + +The verifier will recognize ``foo-error`` and ``bar-error`` as special comments +but will not recognize ``expected-error`` as one because the ``-verify`` line +does not contain that as a prefix. Thus, this test would fail verification +because an unexpected diagnostic would appear on the declaration of ``E``. + +Multiple occurrences accumulate prefixes. For example, +``-verify -verify=foo,bar -verify=baz`` is equivalent to +``-verify=expected,foo,bar,baz``. + +Specifying Diagnostics +^^^^^^^^^^^^^^^^^^^^^^ +Indicating that a line expects an error or a warning is simple. Put a comment +on the line that has the diagnostic, use +``expected-{error,warning,remark,note}`` to tag if it's an expected error, +warning, remark, or note (respectively), and place the expected text between +``{{`` and ``}}`` markers. The full text doesn't have to be included, only +enough to ensure that the correct diagnostic was emitted. (Note: full text +should be included in test cases unless there is a compelling reason to use +truncated text instead.) + +Here's an example of the most commonly used way to specify expected +diagnostics: + +.. code-block: c++ + + int A = B; // expected-error {{use of undeclared identifier 'B'}} + +You can place as many diagnostics on one line as you wish. To make the code +more readable, you can use slash-newline to separate out the diagnostics. + +Alternatively, it is possible to specify the line on which the diagnostic +should appear by appending ``@`` to ``expected-``, for example: + +.. code-block: c++ + + #warning some text + // expected-warning@10 {{some text}} + +The line number may be absolute (as above), or relative to the current line by +prefixing the number with either ``+`` or ``-``. + +If the diagnostic is generated in a separate file, for example in a shared +header file, it may be beneficial to be able to declare the file in which the +diagnostic will appear, rather than placing the ``expected-*`` directive in the +actual file itself. This can be done using the following syntax: + +.. code-block: c++ + + // expected-error@path/include.h:15 {{error message}} + +The path can be absolute or relative and the same search paths will be used as +for ``#include`` directives. The line number in an external file may be +substituted with ``*`` meaning that any line number will match (useful where +the included file is, for example, a system header where the actual line number +may change and is not critical). + +As an alternative to specifying a fixed line number, the location of a +diagnostic can instead be indicated by a marker of the form ``#``. +Markers are specified by including them in a comment, and then referenced by +appending the marker to the diagnostic with ``@#``, as with: + +.. code-block: c++ + + #warning some text // #1 + // ... other code ... + // expected-warning@#1 {{some text}} + +The name of a marker used in a directive must be unique within the compilation. + +The simple syntax above allows each specification to match exactly one +diagnostic. You can use the extended syntax to customize this. The extended +syntax is ``expected- {{diag text}}``, where ```` is one of +``error``, ``warning``, ``remark``, or ``note``, and ```` is a positive +integer. This allows the diagnostic to appear as many times as specified. For +example: + +.. code-block: c++ + + void f(); // expected-note 2 {{previous declaration is here}} + +Where the diagnostic is expected to occur a minimum number of times, this can +be specified by appending a ``+`` to the number. For example: + +.. code-block: c++ + + void f(); // expected-note 0+ {{previous declaration is here}} + void g(); // expected-note 1+ {{previous declaration is here}} + +In the first example, the diagnostic becomes optional, i.e. it will be +swallowed if it occurs, but will not generate an error if it does not occur. In +the second example, the diagnostic must occur at least once. As a short-hand, +"one or more" can be specified simply by ``+``. For example: + +.. code-block: c++ + + void g(); // expected-note + {{previous declaration is here}} + +A range can also be specified by ``-``. For example: + +.. code-block: c++ + + void f(); // expected-note 0-1 {{previous declaration is here}} + +In this example, the diagnostic may appear only once, if at all. + +Regex matching mode may be selected by appending ``-re`` to the diagnostic type +and including regexes wrapped in double curly braces in the directive, such as: + +.. code-block: c++ + + expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}} + +Examples matching error: "variable has incomplete type 'struct s'" + +.. code-block: c++ + + // expected-error {{variable has incomplete type 'struct s'}} + // expected-error {{variable has incomplete type}} + + // expected-error-re {{variable has type 'struct {{.}}'}} + // expected-error-re {{variable has type 'struct {{.*}}'}} + // expected-error-re {{variable has type 'struct {{(.*)}}'}} + // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}} + Feature Test Macros =================== Clang implements several ways to test whether a feature is supported or not. diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 30e288f986782fdeb10de11e34fc0dd1911c1c8e..8e01ef6cbb3997e9d24a9e89b2542bd573a1b0c1 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -424,6 +424,18 @@ Builtin Macros "UTF-16" or "UTF-32" (but may change in the future if the ``-fwide-exec-charset="Encoding-Name"`` option is implemented.) +Implementation-defined keywords +=============================== + +__datasizeof +------------ + +``__datasizeof`` behaves like ``sizeof``, except that it returns the size of the +type ignoring tail padding. + +.. + FIXME: This should list all the keyword extensions + .. _langext-vectors: Vectors and Extended Vectors @@ -4617,6 +4629,22 @@ The pragma can take two values: ``on`` and ``off``. float v = t + z; } +``#pragma clang fp reciprocal`` allows control over using reciprocal +approximations in floating point expressions. When enabled, this +pragma allows the expression ``x / y`` to be approximated as ``x * +(1.0 / y)``. This pragma can be used to disable reciprocal +approximation when it is otherwise enabled for the translation unit +with the ``-freciprocal-math`` flag or other fast-math options. The +pragma can take two values: ``on`` and ``off``. + +.. code-block:: c++ + + float f(float x, float y) + { + // Enable floating point reciprocal approximation + #pragma clang fp reciprocal(on) + return x / y; + } ``#pragma clang fp contract`` specifies whether the compiler should contract a multiply and an addition (or subtraction) into a fused FMA diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index afe7e2e79c2d0879d418b2e0fca472556662deab..2ee946af32bf197968fbfae4b7eb54fbfb1565c1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -182,6 +182,8 @@ C++2c Feature Support This is applied to both C++ standard attributes, and other attributes supported by Clang. This completes the implementation of `P2361R6 Unevaluated Strings `_ +- Implemented `P2864R2 Remove Deprecated Arithmetic Conversion on Enumerations From C++26 `_. + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,6 +211,12 @@ C23 Feature Support - Clang now supports ```` which defines several macros for performing checked integer arithmetic. It is also exposed in pre-C23 modes. +- Completed the implementation of + `N2508 `_. We + previously implemented allowing a label at the end of a compound statement, + and now we've implemented allowing a label to be followed by a declaration + instead of a statement. + Non-comprehensive list of changes in this release ------------------------------------------------- @@ -217,6 +225,12 @@ Non-comprehensive list of changes in this release (e.g., ``uint16x8_t``), this returns the constant number of elements at compile-time. For scalable vectors, e.g., SVE or RISC-V V, the number of elements is not known at compile-time and is determined at runtime. +* The ``__datasizeof`` keyword has been added. It is similar to ``sizeof`` + except that it returns the size of a type ignoring tail padding. +* ``__builtin_classify_type()`` now classifies ``_BitInt`` values as the return value ``18`` + and vector types as return value ``19``, to match GCC 14's behavior. + +* Added ``#pragma clang fp reciprocal``. New Compiler Flags ------------------ @@ -232,6 +246,12 @@ New Compiler Flags preserving ``#include`` directives for "system" headers instead of copying the preprocessed text to the output. This can greatly reduce the size of the preprocessed output, which can be helpful when trying to reduce a test case. +* ``-fassume-nothrow-exception-dtor`` is added to assume that the destructor of + an thrown exception object will not throw. The generated code for catch + handlers will be smaller. A throw expression of a type with a + potentially-throwing destructor will lead to an error. + +* ``-fopenacc`` was added as a part of the effort to support OpenACC in clang. Deprecated Compiler Flags ------------------------- @@ -288,6 +308,40 @@ Attribute Changes in Clang When viewing ``S::FruitKind`` in a debugger, it will behave as if the member was declared as type ``E`` rather than ``unsigned``. +- Clang now warns you that the ``_Alignas`` attribute on declaration specifiers + is ignored, changed from the former incorrect suggestion to move it past + declaration specifiers. (`#58637 `_) + +- Clang now introduced ``[[clang::coro_only_destroy_when_complete]]`` attribute + to reduce the size of the destroy functions for coroutines which are known to + be destroyed after having reached the final suspend point. + +- Clang now introduced ``[[clang::coro_return_type]]`` and ``[[clang::coro_wrapper]]`` + attributes. A function returning a type marked with ``[[clang::coro_return_type]]`` + should be a coroutine. A non-coroutine function marked with ``[[clang::coro_wrapper]]`` + is still allowed to return the such a type. This is helpful for analyzers to recognize coroutines from the function signatures. + +- Clang now supports ``[[clang::code_align(N)]]`` as an attribute which can be + applied to a loop and specifies the byte alignment for a loop. This attribute + accepts a positive integer constant initialization expression indicating the + number of bytes for the minimum alignment boundary. Its value must be a power + of 2, between 1 and 4096(inclusive). + + .. code-block:: c++ + + void Array(int *array, size_t n) { + [[clang::code_align(64)]] for (int i = 0; i < n; ++i) array[i] = 0; + } + + template + void func() { + [[clang::code_align(A)]] for(;;) { } + } + +- Clang now introduced ``[[clang::coro_lifetimebound]]`` attribute. + All parameters of a function are considered to be lifetime bound if the function + returns a type annotated with ``[[clang::coro_lifetimebound]]`` and ``[[clang::coro_return_type]]``. + Improvements to Clang's diagnostics ----------------------------------- - Clang constexpr evaluator now prints template arguments when displaying @@ -426,6 +480,26 @@ Improvements to Clang's diagnostics - ``-Wzero-as-null-pointer-constant`` diagnostic is no longer emitted when using ``__null`` (or, more commonly, ``NULL`` when the platform defines it as ``__null``) to be more consistent with GCC. +- Clang will warn on deprecated specializations used in system headers when their instantiation + is caused by user code. +- Clang will now print ``static_assert`` failure details for arithmetic binary operators. + Example: + + .. code-block:: cpp + + static_assert(1 << 4 == 15); + + will now print: + + .. code-block:: text + + error: static assertion failed due to requirement '1 << 4 == 15' + 48 | static_assert(1 << 4 == 15); + | ^~~~~~~~~~~~ + note: expression evaluates to '16 == 15' + 48 | static_assert(1 << 4 == 15); + | ~~~~~~~^~~~~ + Improvements to Clang's time-trace ---------------------------------- @@ -541,6 +615,36 @@ Bug Fixes in This Version Fixes (`#67687 `_) - Fix crash from constexpr evaluator evaluating uninitialized arrays as rvalue. Fixes (`#67317 `_) +- Clang now properly diagnoses use of stand-alone OpenMP directives after a + label (including ``case`` or ``default`` labels). + + Before: + + .. code-block:: c++ + + label: + #pragma omp barrier // ok + + After: + + .. code-block:: c++ + + label: + #pragma omp barrier // error: '#pragma omp barrier' cannot be an immediate substatement + +- Fixed an issue that a benign assertion might hit when instantiating a pack expansion + inside a lambda. (`#61460 `_) +- Fix crash during instantiation of some class template specializations within class + templates. Fixes (`#70375 `_) +- Fix crash during code generation of C++ coroutine initial suspend when the return + type of await_resume is not trivially destructible. + Fixes (`#63803 `_) +- ``__is_trivially_relocatable`` no longer returns true for non-object types + such as references and functions. + Fixes (`#67498 `_) +- Fix crash when the object used as a ``static_assert`` message has ``size`` or ``data`` members + which are not member functions. +- Support UDLs in ``static_assert`` message. Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -665,6 +769,11 @@ Bug Fixes to C++ Support declaration definition. Fixes: (`#61763 `_) +- Fix a bug where implicit deduction guides are not correctly generated for nested template + classes. Fixes: + (`#46200 `_) + (`#57812 `_) + - Diagnose use of a variable-length array in a coroutine. The design of coroutines is such that it is not possible to support VLA use. Fixes: (`#65858 `_) @@ -708,6 +817,21 @@ Miscellaneous Clang Crashes Fixed `Issue 64564 `_ - Fixed a crash when an ObjC ivar has an invalid type. See (`#68001 `_) +- Fixed a crash in C when redefined struct is another nested redefinition. + `Issue 41302 `_ +- Fixed a crash when ``-ast-dump=json`` was used for code using class + template deduction guides. + +OpenACC Specific Changes +------------------------ +- OpenACC Implementation effort is beginning with semantic analysis and parsing + of OpenACC pragmas. The ``-fopenacc`` flag was added to enable these new, + albeit incomplete changes. The ``_OPENACC`` macro is currently defined to + ``1``, as support is too incomplete to update to a standards-required value. +- Added ``-fexperimental-openacc-macro-override``, a command line option to + permit overriding the ``_OPENACC`` macro to be any digit-only value specified + by the user, which permits testing the compiler against existing OpenACC + workloads in order to evaluate implementation progress. Target Specific Changes ----------------------- @@ -740,6 +864,12 @@ Arm and AArch64 Support - New AArch64 asm constraints have been added for r8-r11(Uci) and r12-r15(Ucj). + Support has been added for the following processors (-mcpu identifiers in parenthesis): + + * Arm Cortex-A520 (cortex-a520). + * Arm Cortex-A720 (cortex-a720). + * Arm Cortex-X4 (cortex-x4). + Android Support ^^^^^^^^^^^^^^^ @@ -836,11 +966,14 @@ clang-format - Add ``AllowBreakBeforeNoexceptSpecifier`` option. - Add ``AllowShortCompoundRequirementOnASingleLine`` option. - Change ``BreakAfterAttributes`` from ``Never`` to ``Leave`` in LLVM style. +- Add ``BreakAdjacentStringLiterals`` option. libclang -------- - Exposed arguments of ``clang::annotate``. +- ``clang::getCursorKindForDecl`` now recognizes linkage specifications such as + ``extern "C"`` and reports them as ``CXCursor_LinkageSpec``. Static Analyzer --------------- @@ -848,6 +981,10 @@ Static Analyzer - Added a new checker ``core.BitwiseShift`` which reports situations where bitwise shift operators produce undefined behavior (because some operand is negative or too large). + +- Move checker ``alpha.unix.Errno`` out of the ``alpha`` package + to ``unix.Errno``. + - Move checker ``alpha.unix.StdCLibraryFunctions`` out of the ``alpha`` package to ``unix.StdCLibraryFunctions``. diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index edc2bce6a964dc45079a0e368c30887cca03b91a..2e658557b0e310cd6041af964435c7d63bb49860 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2134,6 +2134,18 @@ are listed below. new operator will always return a pointer that does not alias any other pointer when the function returns. +.. option:: -fassume-nothrow-exception-dtor + + Assume that an exception object' destructor will not throw, and generate + less code for catch handlers. A throw expression of a type with a + potentially-throwing destructor will lead to an error. + + By default, Clang assumes that the exception object may have a throwing + destructor. For the Itanium C++ ABI, Clang generates a landing pad to + destroy local variables and call ``_Unwind_Resume`` for the code + ``catch (...) { ... }``. This option tells Clang that an exception object's + destructor will not throw and code simplification is possible. + .. option:: -ftrap-function=[name] Instruct code generator to emit a function call to the specified diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 43137f4b020f9f79835215529398a3566eab8ac5..f7b48e64e324f002891e5ab920d0549411f40abe 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -755,6 +755,75 @@ security Security related checkers. +.. _security-cert-env-InvalidPtr: + +security.cert.env.InvalidPtr +"""""""""""""""""""""""""""""""""" + +Corresponds to SEI CERT Rules `ENV31-C `_ and `ENV34-C `_. + +* **ENV31-C**: + Rule is about the possible problem with ``main`` function's third argument, environment pointer, + "envp". When environment array is modified using some modification function + such as ``putenv``, ``setenv`` or others, It may happen that memory is reallocated, + however "envp" is not updated to reflect the changes and points to old memory + region. + +* **ENV34-C**: + Some functions return a pointer to a statically allocated buffer. + Consequently, subsequent call of these functions will invalidate previous + pointer. These functions include: ``getenv``, ``localeconv``, ``asctime``, ``setlocale``, ``strerror`` + +.. code-block:: c + + int main(int argc, const char *argv[], const char *envp[]) { + if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { + // setenv call may invalidate 'envp' + /* Handle error */ + } + if (envp != NULL) { + for (size_t i = 0; envp[i] != NULL; ++i) { + puts(envp[i]); + // envp may no longer point to the current environment + // this program has unanticipated behavior, since envp + // does not reflect changes made by setenv function. + } + } + return 0; + } + + void previous_call_invalidation() { + char *p, *pp; + + p = getenv("VAR"); + setenv("SOMEVAR", "VALUE", /*overwrite = */1); + // call to 'setenv' may invalidate p + + *p; + // dereferencing invalid pointer + } + + +The ``InvalidatingGetEnv`` option is available for treating ``getenv`` calls as +invalidating. When enabled, the checker issues a warning if ``getenv`` is called +multiple times and their results are used without first creating a copy. +This level of strictness might be considered overly pedantic for the commonly +used ``getenv`` implementations. + +To enable this option, use: +``-analyzer-config security.cert.env.InvalidPtr:InvalidatingGetEnv=true``. + +By default, this option is set to *false*. + +When this option is enabled, warnings will be generated for scenarios like the +following: + +.. code-block:: c + + char* p = getenv("VAR"); + char* pp = getenv("VAR2"); // assumes this call can invalidate `env` + strlen(p); // warns about accessing invalid ptr + .. _security-FloatLoopCounter: security.FloatLoopCounter (C) @@ -934,6 +1003,76 @@ Check calls to various UNIX/Posix functions: ``open, pthread_once, calloc, mallo .. literalinclude:: checkers/unix_api_example.c :language: c +.. _unix-Errno: + +unix.Errno (C) +"""""""""""""" + +Check for improper use of ``errno``. +This checker implements partially CERT rule +`ERR30-C. Set errno to zero before calling a library function known to set errno, +and check errno only after the function returns a value indicating failure +`_. +The checker can find the first read of ``errno`` after successful standard +function calls. + +The C and POSIX standards often do not define if a standard library function +may change value of ``errno`` if the call does not fail. +Therefore, ``errno`` should only be used if it is known from the return value +of a function that the call has failed. +There are exceptions to this rule (for example ``strtol``) but the affected +functions are not yet supported by the checker. +The return values for the failure cases are documented in the standard Linux man +pages of the functions and in the `POSIX standard `_. + +.. code-block:: c + + int unsafe_errno_read(int sock, void *data, int data_size) { + if (send(sock, data, data_size, 0) != data_size) { + // 'send' can be successful even if not all data was sent + if (errno == 1) { // An undefined value may be read from 'errno' + return 0; + } + } + return 1; + } + +The checker :ref:`unix-StdCLibraryFunctions` must be turned on to get the +warnings from this checker. The supported functions are the same as by +:ref:`unix-StdCLibraryFunctions`. The ``ModelPOSIX`` option of that +checker affects the set of checked functions. + +**Parameters** + +The ``AllowErrnoReadOutsideConditionExpressions`` option allows read of the +errno value if the value is not used in a condition (in ``if`` statements, +loops, conditional expressions, ``switch`` statements). For example ``errno`` +can be stored into a variable without getting a warning by the checker. + +.. code-block:: c + + int unsafe_errno_read(int sock, void *data, int data_size) { + if (send(sock, data, data_size, 0) != data_size) { + int err = errno; + // warning if 'AllowErrnoReadOutsideConditionExpressions' is false + // no warning if 'AllowErrnoReadOutsideConditionExpressions' is true + } + return 1; + } + +Default value of this option is ``true``. This allows save of the errno value +for possible later error handling. + +**Limitations** + + - Only the very first usage of ``errno`` is checked after an affected function + call. Value of ``errno`` is not followed when it is stored into a variable + or returned from a function. + - Documentation of function ``lseek`` is not clear about what happens if the + function returns different value than the expected file position but not -1. + To avoid possible false-positives ``errno`` is allowed to be used in this + case. + .. _unix-Malloc: unix.Malloc (C) @@ -1098,7 +1237,7 @@ state of the value ``errno`` if applicable to the analysis. Many system functions set the ``errno`` value only if an error occurs (together with a specific return value of the function), otherwise it becomes undefined. This checker changes the analysis state to contain such information. This data is -used by other checkers, for example :ref:`alpha-unix-Errno`. +used by other checkers, for example :ref:`unix-Errno`. **Limitations** @@ -2479,75 +2618,6 @@ alpha.security.cert.env SEI CERT checkers of `Environment C coding rules `_. -.. _alpha-security-cert-env-InvalidPtr: - -alpha.security.cert.env.InvalidPtr -"""""""""""""""""""""""""""""""""" - -Corresponds to SEI CERT Rules ENV31-C and ENV34-C. - -ENV31-C: -Rule is about the possible problem with `main` function's third argument, environment pointer, -"envp". When environment array is modified using some modification function -such as putenv, setenv or others, It may happen that memory is reallocated, -however "envp" is not updated to reflect the changes and points to old memory -region. - -ENV34-C: -Some functions return a pointer to a statically allocated buffer. -Consequently, subsequent call of these functions will invalidate previous -pointer. These functions include: getenv, localeconv, asctime, setlocale, strerror - -.. code-block:: c - - int main(int argc, const char *argv[], const char *envp[]) { - if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { - // setenv call may invalidate 'envp' - /* Handle error */ - } - if (envp != NULL) { - for (size_t i = 0; envp[i] != NULL; ++i) { - puts(envp[i]); - // envp may no longer point to the current environment - // this program has unanticipated behavior, since envp - // does not reflect changes made by setenv function. - } - } - return 0; - } - - void previous_call_invalidation() { - char *p, *pp; - - p = getenv("VAR"); - setenv("SOMEVAR", "VALUE", /*overwrite = */1); - // call to 'setenv' may invalidate p - - *p; - // dereferencing invalid pointer - } - - -The ``InvalidatingGetEnv`` option is available for treating getenv calls as -invalidating. When enabled, the checker issues a warning if getenv is called -multiple times and their results are used without first creating a copy. -This level of strictness might be considered overly pedantic for the commonly -used getenv implementations. - -To enable this option, use: -``-analyzer-config alpha.security.cert.env.InvalidPtr:InvalidatingGetEnv=true``. - -By default, this option is set to *false*. - -When this option is enabled, warnings will be generated for scenarios like the -following: - -.. code-block:: c - - char* p = getenv("VAR"); - char* pp = getenv("VAR2"); // assumes this call can invalidate `env` - strlen(p); // warns about accessing invalid ptr - alpha.security.taint ^^^^^^^^^^^^^^^^^^^^ @@ -2826,76 +2896,6 @@ Check improper use of chroot. f(); // warn: no call of chdir("/") immediately after chroot } -.. _alpha-unix-Errno: - -alpha.unix.Errno (C) -"""""""""""""""""""" - -Check for improper use of ``errno``. -This checker implements partially CERT rule -`ERR30-C. Set errno to zero before calling a library function known to set errno, -and check errno only after the function returns a value indicating failure -`_. -The checker can find the first read of ``errno`` after successful standard -function calls. - -The C and POSIX standards often do not define if a standard library function -may change value of ``errno`` if the call does not fail. -Therefore, ``errno`` should only be used if it is known from the return value -of a function that the call has failed. -There are exceptions to this rule (for example ``strtol``) but the affected -functions are not yet supported by the checker. -The return values for the failure cases are documented in the standard Linux man -pages of the functions and in the `POSIX standard `_. - -.. code-block:: c - - int unsafe_errno_read(int sock, void *data, int data_size) { - if (send(sock, data, data_size, 0) != data_size) { - // 'send' can be successful even if not all data was sent - if (errno == 1) { // An undefined value may be read from 'errno' - return 0; - } - } - return 1; - } - -The checker :ref:`unix-StdCLibraryFunctions` must be turned on to get the -warnings from this checker. The supported functions are the same as by -:ref:`unix-StdCLibraryFunctions`. The ``ModelPOSIX`` option of that -checker affects the set of checked functions. - -**Parameters** - -The ``AllowErrnoReadOutsideConditionExpressions`` option allows read of the -errno value if the value is not used in a condition (in ``if`` statements, -loops, conditional expressions, ``switch`` statements). For example ``errno`` -can be stored into a variable without getting a warning by the checker. - -.. code-block:: c - - int unsafe_errno_read(int sock, void *data, int data_size) { - if (send(sock, data, data_size, 0) != data_size) { - int err = errno; - // warning if 'AllowErrnoReadOutsideConditionExpressions' is false - // no warning if 'AllowErrnoReadOutsideConditionExpressions' is true - } - return 1; - } - -Default value of this option is ``true``. This allows save of the errno value -for possible later error handling. - -**Limitations** - - - Only the very first usage of ``errno`` is checked after an affected function - call. Value of ``errno`` is not followed when it is stored into a variable - or returned from a function. - - Documentation of function ``lseek`` is not clear about what happens if the - function returns different value than the expected file position but not -1. - To avoid possible false-positives ``errno`` is allowed to be used in this - case. - .. _alpha-unix-PthreadLock: alpha.unix.PthreadLock (C) diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt index 48cd800bffd0046f1382f1d26cd0345a5a560bf4..18512b1a7bf6b5857ba82323124fdea5da9edfdb 100644 --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -1837,7 +1837,7 @@ compiler-rt/lib/sanitizer_common/sanitizer_stack_store.cpp compiler-rt/lib/sanitizer_common/sanitizer_stack_store.h compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_fuchsia.h compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_win.cpp -compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_fuchsia.h +compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_constants.h compiler-rt/lib/sanitizer_common/sanitizer_thread_safety.h compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.h compiler-rt/lib/sanitizer_common/sanitizer_type_traits.cpp diff --git a/clang/examples/CMakeLists.txt b/clang/examples/CMakeLists.txt index 8be327bcdbb9d63cfee9142ad13147085dd34d9c..2396ecac16b2dc6f6202fe4bf44bfe22ef00ca22 100644 --- a/clang/examples/CMakeLists.txt +++ b/clang/examples/CMakeLists.txt @@ -4,6 +4,7 @@ if(NOT CLANG_BUILD_EXAMPLES) endif() if(CLANG_PLUGIN_SUPPORT) + add_subdirectory(LLVMPrintFunctionNames) add_subdirectory(PrintFunctionNames) add_subdirectory(AnnotateFunctions) add_subdirectory(Attribute) diff --git a/clang/examples/LLVMPrintFunctionNames/CMakeLists.txt b/clang/examples/LLVMPrintFunctionNames/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..61e7a7842e2f3e262064360f7b987aa663859a93 --- /dev/null +++ b/clang/examples/LLVMPrintFunctionNames/CMakeLists.txt @@ -0,0 +1,23 @@ +# If we don't need RTTI or EH, there's no reason to export anything +# from the plugin. +if(NOT MSVC) # MSVC mangles symbols differently, and + # PrintLLVMFunctionNames.export contains C++ symbols. + if(NOT LLVM_REQUIRES_RTTI) + if(NOT LLVM_REQUIRES_EH) + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/LLVMPrintFunctionNames.exports) + endif() + endif() +endif() + +add_llvm_library(LLVMPrintFunctionNames MODULE LLVMPrintFunctionNames.cpp PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS + Support + ) + clang_target_link_libraries(LLVMPrintFunctionNames PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() diff --git a/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.cpp b/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cb87e4b5e19c44e4aafcd6d7a2e4bc5be85e2d6 --- /dev/null +++ b/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.cpp @@ -0,0 +1,75 @@ +//===- LLVMPrintFunctionNames.cpp +//---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Example clang plugin which simply prints the names of all the functions +// within the generated LLVM code. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Sema/Sema.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Passes/OptimizationLevel.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +namespace { + +class PrintPass final : public llvm::AnalysisInfoMixin { + friend struct llvm::AnalysisInfoMixin; + +public: + using Result = llvm::PreservedAnalyses; + + Result run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) { + for (auto &F : M) + llvm::outs() << "[PrintPass] Found function: " << F.getName() << "\n"; + return llvm::PreservedAnalyses::all(); + } + static bool isRequired() { return true; } +}; + +void PrintCallback(llvm::PassBuilder &PB) { + PB.registerPipelineStartEPCallback( + [](llvm::ModulePassManager &MPM, llvm::OptimizationLevel) { + MPM.addPass(PrintPass()); + }); +} + +class LLVMPrintFunctionsConsumer : public ASTConsumer { +public: + LLVMPrintFunctionsConsumer(CompilerInstance &Instance) { + Instance.getCodeGenOpts().PassBuilderCallbacks.push_back(PrintCallback); + } +}; + +class LLVMPrintFunctionNamesAction : public PluginASTAction { +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef) override { + return std::make_unique(CI); + } + bool ParseArgs(const CompilerInstance &, + const std::vector &) override { + return true; + } + PluginASTAction::ActionType getActionType() override { + return AddBeforeMainAction; + } +}; + +} // namespace + +static FrontendPluginRegistry::Add + X("llvm-print-fns", "print function names, llvm level"); diff --git a/clang/test/Preprocessor/Inputs/canonical-system-headers/a.h b/clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.exports similarity index 100% rename from clang/test/Preprocessor/Inputs/canonical-system-headers/a.h rename to clang/examples/LLVMPrintFunctionNames/LLVMPrintFunctionNames.exports diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h new file mode 100644 index 0000000000000000000000000000000000000000..18375c9e51a173b7e21de4a4851adca9de79112e --- /dev/null +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -0,0 +1,175 @@ +//===--- APINotesManager.h - Manage API Notes Files -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H +#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H + +#include "clang/Basic/Module.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/VersionTuple.h" +#include +#include + +namespace clang { + +class DirectoryEntry; +class FileEntry; +class LangOptions; +class SourceManager; + +namespace api_notes { + +class APINotesReader; + +/// The API notes manager helps find API notes associated with declarations. +/// +/// API notes are externally-provided annotations for declarations that can +/// introduce new attributes (covering availability, nullability of +/// parameters/results, and so on) for specific declarations without directly +/// modifying the headers that contain those declarations. +/// +/// The API notes manager is responsible for finding and loading the +/// external API notes files that correspond to a given header. Its primary +/// operation is \c findAPINotes(), which finds the API notes reader that +/// provides information about the declarations at that location. +class APINotesManager { + using ReaderEntry = llvm::PointerUnion; + + SourceManager &SM; + + /// Whether to implicitly search for API notes files based on the + /// source file from which an entity was declared. + bool ImplicitAPINotes; + + /// The Swift version to use when interpreting versioned API notes. + llvm::VersionTuple SwiftVersion; + + enum ReaderKind : unsigned { Public = 0, Private = 1 }; + + /// API notes readers for the current module. + /// + /// There can be up to two of these, one for public headers and one + /// for private headers. + /// + /// Not using std::unique_ptr to store these, since the reader pointers are + /// also stored in llvm::PointerUnion below. + APINotesReader *CurrentModuleReaders[2] = {nullptr, nullptr}; + + /// A mapping from header file directories to the API notes reader for + /// that directory, or a redirection to another directory entry that may + /// have more information, or NULL to indicate that there is no API notes + /// reader for this directory. + llvm::DenseMap Readers; + + /// Load the API notes associated with the given file, whether it is + /// the binary or source form of API notes. + /// + /// \returns the API notes reader for this file, or null if there is + /// a failure. + std::unique_ptr loadAPINotes(FileEntryRef APINotesFile); + + /// Load the API notes associated with the given buffer, whether it is + /// the binary or source form of API notes. + /// + /// \returns the API notes reader for this file, or null if there is + /// a failure. + std::unique_ptr loadAPINotes(StringRef Buffer); + + /// Load the given API notes file for the given header directory. + /// + /// \param HeaderDir The directory at which we + /// + /// \returns true if an error occurred. + bool loadAPINotes(const DirectoryEntry *HeaderDir, FileEntryRef APINotesFile); + + /// Look for API notes in the given directory. + /// + /// This might find either a binary or source API notes. + OptionalFileEntryRef findAPINotesFile(DirectoryEntryRef Directory, + StringRef FileName, + bool WantPublic = true); + + /// Attempt to load API notes for the given framework. A framework will have + /// the API notes file under either {FrameworkPath}/APINotes, + /// {FrameworkPath}/Headers or {FrameworkPath}/PrivateHeaders, while a + /// library will have the API notes simply in its directory. + /// + /// \param FrameworkPath The path to the framework. + /// \param Public Whether to load the public API notes. Otherwise, attempt + /// to load the private API notes. + /// + /// \returns the header directory entry (e.g., for Headers or PrivateHeaders) + /// for which the API notes were successfully loaded, or NULL if API notes + /// could not be loaded for any reason. + OptionalDirectoryEntryRef loadFrameworkAPINotes(llvm::StringRef FrameworkPath, + llvm::StringRef FrameworkName, + bool Public); + +public: + APINotesManager(SourceManager &SM, const LangOptions &LangOpts); + ~APINotesManager(); + + /// Set the Swift version to use when filtering API notes. + void setSwiftVersion(llvm::VersionTuple Version) { + this->SwiftVersion = Version; + } + + /// Load the API notes for the current module. + /// + /// \param M The current module. + /// \param LookInModule Whether to look inside the module itself. + /// \param SearchPaths The paths in which we should search for API notes + /// for the current module. + /// + /// \returns true if API notes were successfully loaded, \c false otherwise. + bool loadCurrentModuleAPINotes(Module *M, bool LookInModule, + ArrayRef SearchPaths); + + /// Get FileEntry for the APINotes of the module that is currently being + /// compiled. + /// + /// \param M The current module. + /// \param LookInModule Whether to look inside the directory of the current + /// module. + /// \param SearchPaths The paths in which we should search for API + /// notes for the current module. + /// + /// \returns a vector of FileEntry where APINotes files are. + llvm::SmallVector + getCurrentModuleAPINotes(Module *M, bool LookInModule, + ArrayRef SearchPaths); + + /// Load Compiled API notes for current module. + /// + /// \param Buffers Array of compiled API notes. + /// + /// \returns true if API notes were successfully loaded, \c false otherwise. + bool loadCurrentModuleAPINotesFromBuffer(ArrayRef Buffers); + + /// Retrieve the set of API notes readers for the current module. + ArrayRef getCurrentModuleReaders() const { + bool HasPublic = CurrentModuleReaders[ReaderKind::Public]; + bool HasPrivate = CurrentModuleReaders[ReaderKind::Private]; + assert((!HasPrivate || HasPublic) && "private module requires public module"); + if (!HasPrivate && !HasPublic) + return {}; + return ArrayRef(CurrentModuleReaders).slice(0, HasPrivate ? 2 : 1); + } + + /// Find the API notes readers that correspond to the given source location. + llvm::SmallVector findAPINotes(SourceLocation Loc); +}; + +} // end namespace api_notes +} // end namespace clang + +#endif diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h index 6098d0ee36fc4777f3124044f68ad345d4b2eba4..9c24ed85b6a124ac8d6a2928389776ed12a9b67b 100644 --- a/clang/include/clang/APINotes/APINotesYAMLCompiler.h +++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h @@ -10,14 +10,25 @@ #define LLVM_CLANG_APINOTES_APINOTESYAMLCOMPILER_H #include "llvm/ADT/StringRef.h" +#include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" +namespace clang { +class FileEntry; +} // namespace clang + namespace clang { namespace api_notes { /// Parses the APINotes YAML content and writes the representation back to the /// specified stream. This provides a means of testing the YAML processing of /// the APINotes format. bool parseAndDumpAPINotes(llvm::StringRef YI, llvm::raw_ostream &OS); + +/// Converts API notes from YAML format to binary format. +bool compileAPINotes(llvm::StringRef YAMLInput, const FileEntry *SourceFile, + llvm::raw_ostream &OS, + llvm::SourceMgr::DiagHandlerTy DiagHandler = nullptr, + void *DiagHandlerCtxt = nullptr); } // namespace api_notes } // namespace clang diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index b74244bc8f1cbd34b3d3f3f9c155d35a9d5f2956..1d116becf06c80553c1d917f08d547d8b3a1fafb 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -737,6 +737,9 @@ inline bool operator!=(const TypedefInfo &LHS, const TypedefInfo &RHS) { return !(LHS == RHS); } +/// The file extension used for the source representation of API notes. +static const constexpr char SOURCE_APINOTES_EXTENSION[] = "apinotes"; + /// Opaque context ID used to refer to an Objective-C class or protocol or a C++ /// namespace. class ContextID { @@ -766,6 +769,7 @@ struct Context { /// data they contain; it is up to the user to ensure that the data /// referenced by the identifier list persists. struct ObjCSelectorRef { + unsigned NumArgs; llvm::ArrayRef Identifiers; }; } // namespace api_notes diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 6bb30824722398923de313629f06494d1fab909d..3e46a5da3fc043f2e40aef2dd6568abe3beb141a 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1156,6 +1156,10 @@ public: /// host code. llvm::DenseSet CUDAExternalDeviceDeclODRUsedByHost; + /// Keep track of CUDA/HIP implicit host device functions used on device side + /// in device compilation. + llvm::DenseSet CUDAImplicitHostDeviceFunUsedByDevice; + ASTContext(LangOptions &LOpts, SourceManager &SM, IdentifierTable &idents, SelectorTable &sels, Builtin::Context &builtins, TranslationUnitKind TUKind); diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 8884bd5a90de198f58039f5441de3a8cdde8341d..1b831c9511e27202840bb50dd558b4e43e89705d 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -40,17 +40,23 @@ class OMPTraitInfo; /// Attr - This represents one attribute. class Attr : public AttributeCommonInfo { private: + LLVM_PREFERRED_TYPE(attr::Kind) unsigned AttrKind : 16; protected: /// An index into the spelling list of an /// attribute defined in Attr.td file. + LLVM_PREFERRED_TYPE(bool) unsigned Inherited : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsPackExpansion : 1; + LLVM_PREFERRED_TYPE(bool) unsigned Implicit : 1; // FIXME: These are properties of the attribute kind, not state for this // instance of the attribute. + LLVM_PREFERRED_TYPE(bool) unsigned IsLateParsed : 1; + LLVM_PREFERRED_TYPE(bool) unsigned InheritEvenIfAlreadyPresent : 1; void *operator new(size_t bytes) noexcept { @@ -243,7 +249,9 @@ public: class ParamIdx { // Idx is exposed only via accessors that specify specific encodings. unsigned Idx : 30; + LLVM_PREFERRED_TYPE(bool) unsigned HasThis : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsValid : 1; void assertComparable(const ParamIdx &I) const { diff --git a/clang/include/clang/AST/CXXInheritance.h b/clang/include/clang/AST/CXXInheritance.h index eec2119f4a1801e72e828daeef1c87c6d5968d56..bbef01843e0b0a4dc23e32bc317f12143dd33797 100644 --- a/clang/include/clang/AST/CXXInheritance.h +++ b/clang/include/clang/AST/CXXInheritance.h @@ -20,7 +20,6 @@ #include "clang/AST/TypeOrdering.h" #include "clang/Basic/Specifiers.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" @@ -131,6 +130,7 @@ class CXXBasePaths { /// class subobjects for that class type. The key of the map is /// the cv-unqualified canonical type of the base class subobject. struct IsVirtBaseAndNumberNonVirtBases { + LLVM_PREFERRED_TYPE(bool) unsigned IsVirtBase : 1; unsigned NumberOfNonVirtBases : 31; }; diff --git a/clang/include/clang/AST/Comment.h b/clang/include/clang/AST/Comment.h index 0b68c11316649aaf17ac8bb1471aa197444de579..dd9906727293fcee16c73fb167fb66d396bb32ef 100644 --- a/clang/include/clang/AST/Comment.h +++ b/clang/include/clang/AST/Comment.h @@ -27,6 +27,8 @@ class TemplateParameterList; namespace comments { class FullComment; +enum class InlineCommandRenderKind; +enum class ParamCommandPassDirection; /// Describes the syntax that was used in a documentation command. /// @@ -47,6 +49,17 @@ enum CommandMarkerKind { CMK_At = 1 }; +enum class CommentKind { + None = 0, +#define COMMENT(CLASS, PARENT) CLASS, +#define COMMENT_RANGE(BASE, FIRST, LAST) \ + First##BASE##Constant = FIRST, Last##BASE##Constant = LAST, +#define LAST_COMMENT_RANGE(BASE, FIRST, LAST) \ + First##BASE##Constant = FIRST, Last##BASE##Constant = LAST +#define ABSTRACT_COMMENT(COMMENT) +#include "clang/AST/CommentNodes.inc" +}; + /// Any part of the comment. /// Abstract class. class Comment { @@ -61,6 +74,7 @@ protected: friend class Comment; /// Type of this AST node. + LLVM_PREFERRED_TYPE(CommentKind) unsigned Kind : 8; }; enum { NumCommentBits = 8 }; @@ -68,10 +82,12 @@ protected: class InlineContentCommentBitfields { friend class InlineContentComment; + LLVM_PREFERRED_TYPE(CommentBitfields) unsigned : NumCommentBits; /// True if there is a newline after this inline content node. /// (There is no separate AST node for a newline.) + LLVM_PREFERRED_TYPE(bool) unsigned HasTrailingNewline : 1; }; enum { NumInlineContentCommentBits = NumCommentBits + 1 }; @@ -79,12 +95,15 @@ protected: class TextCommentBitfields { friend class TextComment; + LLVM_PREFERRED_TYPE(InlineContentCommentBitfields) unsigned : NumInlineContentCommentBits; /// True if \c IsWhitespace field contains a valid value. + LLVM_PREFERRED_TYPE(bool) mutable unsigned IsWhitespaceValid : 1; /// True if this comment AST node contains only whitespace. + LLVM_PREFERRED_TYPE(bool) mutable unsigned IsWhitespace : 1; }; enum { NumTextCommentBits = NumInlineContentCommentBits + 2 }; @@ -92,10 +111,13 @@ protected: class InlineCommandCommentBitfields { friend class InlineCommandComment; + LLVM_PREFERRED_TYPE(InlineContentCommentBitfields) unsigned : NumInlineContentCommentBits; + LLVM_PREFERRED_TYPE(InlineCommandRenderKind) unsigned RenderKind : 3; + LLVM_PREFERRED_TYPE(CommandTraits::KnownCommandIDs) unsigned CommandID : CommandInfo::NumCommandIDBits; }; enum { NumInlineCommandCommentBits = NumInlineContentCommentBits + 3 + @@ -104,9 +126,11 @@ protected: class HTMLTagCommentBitfields { friend class HTMLTagComment; + LLVM_PREFERRED_TYPE(InlineContentCommentBitfields) unsigned : NumInlineContentCommentBits; /// True if we found that this tag is malformed in some way. + LLVM_PREFERRED_TYPE(bool) unsigned IsMalformed : 1; }; enum { NumHTMLTagCommentBits = NumInlineContentCommentBits + 1 }; @@ -114,10 +138,12 @@ protected: class HTMLStartTagCommentBitfields { friend class HTMLStartTagComment; + LLVM_PREFERRED_TYPE(HTMLTagCommentBitfields) unsigned : NumHTMLTagCommentBits; /// True if this tag is self-closing (e. g.,
). This is based on tag /// spelling in comment (plain
would not set this flag). + LLVM_PREFERRED_TYPE(bool) unsigned IsSelfClosing : 1; }; enum { NumHTMLStartTagCommentBits = NumHTMLTagCommentBits + 1 }; @@ -125,12 +151,15 @@ protected: class ParagraphCommentBitfields { friend class ParagraphComment; + LLVM_PREFERRED_TYPE(CommentBitfields) unsigned : NumCommentBits; /// True if \c IsWhitespace field contains a valid value. + LLVM_PREFERRED_TYPE(bool) mutable unsigned IsWhitespaceValid : 1; /// True if this comment AST node contains only whitespace. + LLVM_PREFERRED_TYPE(bool) mutable unsigned IsWhitespace : 1; }; enum { NumParagraphCommentBits = NumCommentBits + 2 }; @@ -138,12 +167,15 @@ protected: class BlockCommandCommentBitfields { friend class BlockCommandComment; + LLVM_PREFERRED_TYPE(CommentBitfields) unsigned : NumCommentBits; + LLVM_PREFERRED_TYPE(CommandTraits::KnownCommandIDs) unsigned CommandID : CommandInfo::NumCommandIDBits; /// Describes the syntax that was used in a documentation command. /// Contains values from CommandMarkerKind enum. + LLVM_PREFERRED_TYPE(CommandMarkerKind) unsigned CommandMarker : 1; }; enum { NumBlockCommandCommentBits = NumCommentBits + @@ -152,12 +184,15 @@ protected: class ParamCommandCommentBitfields { friend class ParamCommandComment; + LLVM_PREFERRED_TYPE(BlockCommandCommentBitfields) unsigned : NumBlockCommandCommentBits; - /// Parameter passing direction, see ParamCommandComment::PassDirection. + /// Parameter passing direction. + LLVM_PREFERRED_TYPE(ParamCommandPassDirection) unsigned Direction : 2; /// True if direction was specified explicitly in the comment. + LLVM_PREFERRED_TYPE(bool) unsigned IsDirectionExplicit : 1; }; enum { NumParamCommandCommentBits = NumBlockCommandCommentBits + 3 }; @@ -183,17 +218,6 @@ protected: } public: - enum CommentKind { - NoCommentKind = 0, -#define COMMENT(CLASS, PARENT) CLASS##Kind, -#define COMMENT_RANGE(BASE, FIRST, LAST) \ - First##BASE##Constant=FIRST##Kind, Last##BASE##Constant=LAST##Kind, -#define LAST_COMMENT_RANGE(BASE, FIRST, LAST) \ - First##BASE##Constant=FIRST##Kind, Last##BASE##Constant=LAST##Kind -#define ABSTRACT_COMMENT(COMMENT) -#include "clang/AST/CommentNodes.inc" - }; - struct Argument { SourceRange Range; StringRef Text; @@ -203,7 +227,7 @@ public: SourceLocation LocBegin, SourceLocation LocEnd) : Loc(LocBegin), Range(SourceRange(LocBegin, LocEnd)) { - CommentBits.Kind = K; + CommentBits.Kind = llvm::to_underlying(K); } CommentKind getCommentKind() const { @@ -249,8 +273,9 @@ protected: public: static bool classof(const Comment *C) { - return C->getCommentKind() >= FirstInlineContentCommentConstant && - C->getCommentKind() <= LastInlineContentCommentConstant; + return C->getCommentKind() >= + CommentKind::FirstInlineContentCommentConstant && + C->getCommentKind() <= CommentKind::LastInlineContentCommentConstant; } void addTrailingNewline() { @@ -267,16 +292,14 @@ class TextComment : public InlineContentComment { StringRef Text; public: - TextComment(SourceLocation LocBegin, - SourceLocation LocEnd, - StringRef Text) : - InlineContentComment(TextCommentKind, LocBegin, LocEnd), - Text(Text) { + TextComment(SourceLocation LocBegin, SourceLocation LocEnd, StringRef Text) + : InlineContentComment(CommentKind::TextComment, LocBegin, LocEnd), + Text(Text) { TextCommentBits.IsWhitespaceValid = false; } static bool classof(const Comment *C) { - return C->getCommentKind() == TextCommentKind; + return C->getCommentKind() == CommentKind::TextComment; } child_iterator child_begin() const { return nullptr; } @@ -298,37 +321,35 @@ private: bool isWhitespaceNoCache() const; }; +/// The most appropriate rendering mode for this command, chosen on command +/// semantics in Doxygen. +enum class InlineCommandRenderKind { + Normal, + Bold, + Monospaced, + Emphasized, + Anchor +}; + /// A command with word-like arguments that is considered inline content. class InlineCommandComment : public InlineContentComment { -public: - /// The most appropriate rendering mode for this command, chosen on command - /// semantics in Doxygen. - enum RenderKind { - RenderNormal, - RenderBold, - RenderMonospaced, - RenderEmphasized, - RenderAnchor - }; - protected: /// Command arguments. ArrayRef Args; public: - InlineCommandComment(SourceLocation LocBegin, - SourceLocation LocEnd, - unsigned CommandID, - RenderKind RK, - ArrayRef Args) : - InlineContentComment(InlineCommandCommentKind, LocBegin, LocEnd), - Args(Args) { - InlineCommandCommentBits.RenderKind = RK; + InlineCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, + unsigned CommandID, InlineCommandRenderKind RK, + ArrayRef Args) + : InlineContentComment(CommentKind::InlineCommandComment, LocBegin, + LocEnd), + Args(Args) { + InlineCommandCommentBits.RenderKind = llvm::to_underlying(RK); InlineCommandCommentBits.CommandID = CommandID; } static bool classof(const Comment *C) { - return C->getCommentKind() == InlineCommandCommentKind; + return C->getCommentKind() == CommentKind::InlineCommandComment; } child_iterator child_begin() const { return nullptr; } @@ -347,8 +368,9 @@ public: return SourceRange(getBeginLoc().getLocWithOffset(-1), getEndLoc()); } - RenderKind getRenderKind() const { - return static_cast(InlineCommandCommentBits.RenderKind); + InlineCommandRenderKind getRenderKind() const { + return static_cast( + InlineCommandCommentBits.RenderKind); } unsigned getNumArgs() const { @@ -386,8 +408,8 @@ protected: public: static bool classof(const Comment *C) { - return C->getCommentKind() >= FirstHTMLTagCommentConstant && - C->getCommentKind() <= LastHTMLTagCommentConstant; + return C->getCommentKind() >= CommentKind::FirstHTMLTagCommentConstant && + C->getCommentKind() <= CommentKind::LastHTMLTagCommentConstant; } StringRef getTagName() const LLVM_READONLY { return TagName; } @@ -443,18 +465,16 @@ private: ArrayRef Attributes; public: - HTMLStartTagComment(SourceLocation LocBegin, - StringRef TagName) : - HTMLTagComment(HTMLStartTagCommentKind, - LocBegin, LocBegin.getLocWithOffset(1 + TagName.size()), - TagName, - LocBegin.getLocWithOffset(1), - LocBegin.getLocWithOffset(1 + TagName.size())) { + HTMLStartTagComment(SourceLocation LocBegin, StringRef TagName) + : HTMLTagComment(CommentKind::HTMLStartTagComment, LocBegin, + LocBegin.getLocWithOffset(1 + TagName.size()), TagName, + LocBegin.getLocWithOffset(1), + LocBegin.getLocWithOffset(1 + TagName.size())) { HTMLStartTagCommentBits.IsSelfClosing = false; } static bool classof(const Comment *C) { - return C->getCommentKind() == HTMLStartTagCommentKind; + return C->getCommentKind() == CommentKind::HTMLStartTagComment; } child_iterator child_begin() const { return nullptr; } @@ -498,18 +518,14 @@ public: /// A closing HTML tag. class HTMLEndTagComment : public HTMLTagComment { public: - HTMLEndTagComment(SourceLocation LocBegin, - SourceLocation LocEnd, - StringRef TagName) : - HTMLTagComment(HTMLEndTagCommentKind, - LocBegin, LocEnd, - TagName, - LocBegin.getLocWithOffset(2), - LocBegin.getLocWithOffset(2 + TagName.size())) - { } + HTMLEndTagComment(SourceLocation LocBegin, SourceLocation LocEnd, + StringRef TagName) + : HTMLTagComment(CommentKind::HTMLEndTagComment, LocBegin, LocEnd, + TagName, LocBegin.getLocWithOffset(2), + LocBegin.getLocWithOffset(2 + TagName.size())) {} static bool classof(const Comment *C) { - return C->getCommentKind() == HTMLEndTagCommentKind; + return C->getCommentKind() == CommentKind::HTMLEndTagComment; } child_iterator child_begin() const { return nullptr; } @@ -529,8 +545,9 @@ protected: public: static bool classof(const Comment *C) { - return C->getCommentKind() >= FirstBlockContentCommentConstant && - C->getCommentKind() <= LastBlockContentCommentConstant; + return C->getCommentKind() >= + CommentKind::FirstBlockContentCommentConstant && + C->getCommentKind() <= CommentKind::LastBlockContentCommentConstant; } }; @@ -539,11 +556,10 @@ class ParagraphComment : public BlockContentComment { ArrayRef Content; public: - ParagraphComment(ArrayRef Content) : - BlockContentComment(ParagraphCommentKind, - SourceLocation(), - SourceLocation()), - Content(Content) { + ParagraphComment(ArrayRef Content) + : BlockContentComment(CommentKind::ParagraphComment, SourceLocation(), + SourceLocation()), + Content(Content) { if (Content.empty()) { ParagraphCommentBits.IsWhitespace = true; ParagraphCommentBits.IsWhitespaceValid = true; @@ -558,7 +574,7 @@ public: } static bool classof(const Comment *C) { - return C->getCommentKind() == ParagraphCommentKind; + return C->getCommentKind() == CommentKind::ParagraphComment; } child_iterator child_begin() const { @@ -606,20 +622,19 @@ protected: } public: - BlockCommandComment(SourceLocation LocBegin, - SourceLocation LocEnd, - unsigned CommandID, - CommandMarkerKind CommandMarker) : - BlockContentComment(BlockCommandCommentKind, LocBegin, LocEnd), - Paragraph(nullptr) { + BlockCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, + unsigned CommandID, CommandMarkerKind CommandMarker) + : BlockContentComment(CommentKind::BlockCommandComment, LocBegin, LocEnd), + Paragraph(nullptr) { setLocation(getCommandNameBeginLoc()); BlockCommandCommentBits.CommandID = CommandID; BlockCommandCommentBits.CommandMarker = CommandMarker; } static bool classof(const Comment *C) { - return C->getCommentKind() >= FirstBlockCommandCommentConstant && - C->getCommentKind() <= LastBlockCommandCommentConstant; + return C->getCommentKind() >= + CommentKind::FirstBlockCommandCommentConstant && + C->getCommentKind() <= CommentKind::LastBlockCommandCommentConstant; } child_iterator child_begin() const { @@ -690,6 +705,8 @@ public: } }; +enum class ParamCommandPassDirection { In, Out, InOut }; + /// Doxygen \\param command. class ParamCommandComment : public BlockCommandComment { private: @@ -702,39 +719,33 @@ public: VarArgParamIndex = ~0U/*InvalidParamIndex*/ - 1U }; - ParamCommandComment(SourceLocation LocBegin, - SourceLocation LocEnd, - unsigned CommandID, - CommandMarkerKind CommandMarker) : - BlockCommandComment(ParamCommandCommentKind, LocBegin, LocEnd, - CommandID, CommandMarker), - ParamIndex(InvalidParamIndex) { - ParamCommandCommentBits.Direction = In; + ParamCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, + unsigned CommandID, CommandMarkerKind CommandMarker) + : BlockCommandComment(CommentKind::ParamCommandComment, LocBegin, LocEnd, + CommandID, CommandMarker), + ParamIndex(InvalidParamIndex) { + ParamCommandCommentBits.Direction = + llvm::to_underlying(ParamCommandPassDirection::In); ParamCommandCommentBits.IsDirectionExplicit = false; } static bool classof(const Comment *C) { - return C->getCommentKind() == ParamCommandCommentKind; + return C->getCommentKind() == CommentKind::ParamCommandComment; } - enum PassDirection { - In, - Out, - InOut - }; + static const char *getDirectionAsString(ParamCommandPassDirection D); - static const char *getDirectionAsString(PassDirection D); - - PassDirection getDirection() const LLVM_READONLY { - return static_cast(ParamCommandCommentBits.Direction); + ParamCommandPassDirection getDirection() const LLVM_READONLY { + return static_cast( + ParamCommandCommentBits.Direction); } bool isDirectionExplicit() const LLVM_READONLY { return ParamCommandCommentBits.IsDirectionExplicit; } - void setDirection(PassDirection Direction, bool Explicit) { - ParamCommandCommentBits.Direction = Direction; + void setDirection(ParamCommandPassDirection Direction, bool Explicit) { + ParamCommandCommentBits.Direction = llvm::to_underlying(Direction); ParamCommandCommentBits.IsDirectionExplicit = Explicit; } @@ -796,16 +807,13 @@ private: ArrayRef Position; public: - TParamCommandComment(SourceLocation LocBegin, - SourceLocation LocEnd, - unsigned CommandID, - CommandMarkerKind CommandMarker) : - BlockCommandComment(TParamCommandCommentKind, LocBegin, LocEnd, CommandID, - CommandMarker) - { } + TParamCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, + unsigned CommandID, CommandMarkerKind CommandMarker) + : BlockCommandComment(CommentKind::TParamCommandComment, LocBegin, LocEnd, + CommandID, CommandMarker) {} static bool classof(const Comment *C) { - return C->getCommentKind() == TParamCommandCommentKind; + return C->getCommentKind() == CommentKind::TParamCommandComment; } bool hasParamName() const { @@ -847,16 +855,13 @@ class VerbatimBlockLineComment : public Comment { StringRef Text; public: - VerbatimBlockLineComment(SourceLocation LocBegin, - StringRef Text) : - Comment(VerbatimBlockLineCommentKind, - LocBegin, - LocBegin.getLocWithOffset(Text.size())), - Text(Text) - { } + VerbatimBlockLineComment(SourceLocation LocBegin, StringRef Text) + : Comment(CommentKind::VerbatimBlockLineComment, LocBegin, + LocBegin.getLocWithOffset(Text.size())), + Text(Text) {} static bool classof(const Comment *C) { - return C->getCommentKind() == VerbatimBlockLineCommentKind; + return C->getCommentKind() == CommentKind::VerbatimBlockLineComment; } child_iterator child_begin() const { return nullptr; } @@ -878,16 +883,15 @@ protected: ArrayRef Lines; public: - VerbatimBlockComment(SourceLocation LocBegin, - SourceLocation LocEnd, - unsigned CommandID) : - BlockCommandComment(VerbatimBlockCommentKind, - LocBegin, LocEnd, CommandID, - CMK_At) // FIXME: improve source fidelity. - { } + VerbatimBlockComment(SourceLocation LocBegin, SourceLocation LocEnd, + unsigned CommandID) + : BlockCommandComment(CommentKind::VerbatimBlockComment, LocBegin, LocEnd, + CommandID, + CMK_At) // FIXME: improve source fidelity. + {} static bool classof(const Comment *C) { - return C->getCommentKind() == VerbatimBlockCommentKind; + return C->getCommentKind() == CommentKind::VerbatimBlockComment; } child_iterator child_begin() const { @@ -929,21 +933,16 @@ protected: SourceLocation TextBegin; public: - VerbatimLineComment(SourceLocation LocBegin, - SourceLocation LocEnd, - unsigned CommandID, - SourceLocation TextBegin, - StringRef Text) : - BlockCommandComment(VerbatimLineCommentKind, - LocBegin, LocEnd, - CommandID, - CMK_At), // FIXME: improve source fidelity. - Text(Text), - TextBegin(TextBegin) - { } + VerbatimLineComment(SourceLocation LocBegin, SourceLocation LocEnd, + unsigned CommandID, SourceLocation TextBegin, + StringRef Text) + : BlockCommandComment(CommentKind::VerbatimLineComment, LocBegin, LocEnd, + CommandID, + CMK_At), // FIXME: improve source fidelity. + Text(Text), TextBegin(TextBegin) {} static bool classof(const Comment *C) { - return C->getCommentKind() == VerbatimLineCommentKind; + return C->getCommentKind() == CommentKind::VerbatimLineComment; } child_iterator child_begin() const { return nullptr; } @@ -1036,28 +1035,35 @@ struct DeclInfo { }; /// If false, only \c CommentDecl is valid. + LLVM_PREFERRED_TYPE(bool) unsigned IsFilled : 1; /// Simplified kind of \c CommentDecl, see \c DeclKind enum. + LLVM_PREFERRED_TYPE(DeclKind) unsigned Kind : 3; /// Is \c CommentDecl a template declaration. + LLVM_PREFERRED_TYPE(TemplateDeclKind) unsigned TemplateKind : 2; /// Is \c CommentDecl an ObjCMethodDecl. + LLVM_PREFERRED_TYPE(bool) unsigned IsObjCMethod : 1; /// Is \c CommentDecl a non-static member function of C++ class or /// instance method of ObjC class. /// Can be true only if \c IsFunctionDecl is true. + LLVM_PREFERRED_TYPE(bool) unsigned IsInstanceMethod : 1; /// Is \c CommentDecl a static member function of C++ class or /// class method of ObjC class. /// Can be true only if \c IsFunctionDecl is true. + LLVM_PREFERRED_TYPE(bool) unsigned IsClassMethod : 1; /// Is \c CommentDecl something we consider a "function" that's variadic. + LLVM_PREFERRED_TYPE(bool) unsigned IsVariadic : 1; void fill(); @@ -1079,9 +1085,9 @@ class FullComment : public Comment { DeclInfo *ThisDeclInfo; public: - FullComment(ArrayRef Blocks, DeclInfo *D) : - Comment(FullCommentKind, SourceLocation(), SourceLocation()), - Blocks(Blocks), ThisDeclInfo(D) { + FullComment(ArrayRef Blocks, DeclInfo *D) + : Comment(CommentKind::FullComment, SourceLocation(), SourceLocation()), + Blocks(Blocks), ThisDeclInfo(D) { if (Blocks.empty()) return; @@ -1091,7 +1097,7 @@ public: } static bool classof(const Comment *C) { - return C->getCommentKind() == FullCommentKind; + return C->getCommentKind() == CommentKind::FullComment; } child_iterator child_begin() const { diff --git a/clang/include/clang/AST/CommentSema.h b/clang/include/clang/AST/CommentSema.h index 5d8df7dbf385a12b6c7466c9cafcf1b21a933b44..03f13283ac0d977ef1263951a04728fc3faa4c8b 100644 --- a/clang/include/clang/AST/CommentSema.h +++ b/clang/include/clang/AST/CommentSema.h @@ -244,8 +244,7 @@ private: StringRef Typo, const TemplateParameterList *TemplateParameters); - InlineCommandComment::RenderKind - getInlineCommandRenderKind(StringRef Name) const; + InlineCommandRenderKind getInlineCommandRenderKind(StringRef Name) const; }; } // end namespace comments diff --git a/clang/include/clang/AST/CommentVisitor.h b/clang/include/clang/AST/CommentVisitor.h index d9a7439f7cc0554a03591550257056aff27c5f29..bbb624a23e68cb722e82d5a6f92973ee0250be41 100644 --- a/clang/include/clang/AST/CommentVisitor.h +++ b/clang/include/clang/AST/CommentVisitor.h @@ -31,8 +31,9 @@ public: switch (C->getCommentKind()) { default: llvm_unreachable("Unknown comment kind!"); #define ABSTRACT_COMMENT(COMMENT) -#define COMMENT(CLASS, PARENT) \ - case Comment::CLASS##Kind: DISPATCH(CLASS, CLASS); +#define COMMENT(CLASS, PARENT) \ + case CommentKind::CLASS: \ + DISPATCH(CLASS, CLASS); #include "clang/AST/CommentNodes.inc" #undef ABSTRACT_COMMENT #undef COMMENT diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index d8495f2c34940c5f90e9da856edcd019921a023d..cd0878d7082514c600bcb8bf547d91f1d96093db 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -75,6 +75,7 @@ class TemplateParameterList; class TypeAliasTemplateDecl; class UnresolvedSetImpl; class VarTemplateDecl; +enum class ImplicitParamKind; /// The top declaration context. class TranslationUnitDecl : public Decl, @@ -969,12 +970,16 @@ private: friend class ASTDeclReader; friend class VarDecl; + LLVM_PREFERRED_TYPE(StorageClass) unsigned SClass : 3; + LLVM_PREFERRED_TYPE(ThreadStorageClassSpecifier) unsigned TSCSpec : 2; + LLVM_PREFERRED_TYPE(InitializationStyle) unsigned InitStyle : 2; /// Whether this variable is an ARC pseudo-__strong variable; see /// isARCPseudoStrong() for details. + LLVM_PREFERRED_TYPE(bool) unsigned ARCPseudoStrong : 1; }; enum { NumVarDeclBits = 8 }; @@ -995,22 +1000,27 @@ protected: friend class ASTDeclReader; friend class ParmVarDecl; + LLVM_PREFERRED_TYPE(VarDeclBitfields) unsigned : NumVarDeclBits; /// Whether this parameter inherits a default argument from a /// prior declaration. + LLVM_PREFERRED_TYPE(bool) unsigned HasInheritedDefaultArg : 1; /// Describes the kind of default argument for this parameter. By default /// this is none. If this is normal, then the default argument is stored in /// the \c VarDecl initializer expression unless we were unable to parse /// (even an invalid) expression for the default argument. + LLVM_PREFERRED_TYPE(DefaultArgKind) unsigned DefaultArgKind : 2; /// Whether this parameter undergoes K&R argument promotion. + LLVM_PREFERRED_TYPE(bool) unsigned IsKNRPromoted : 1; /// Whether this parameter is an ObjC method parameter or not. + LLVM_PREFERRED_TYPE(bool) unsigned IsObjCMethodParam : 1; /// If IsObjCMethodParam, a Decl::ObjCDeclQualifier. @@ -1029,51 +1039,64 @@ protected: friend class ImplicitParamDecl; friend class VarDecl; + LLVM_PREFERRED_TYPE(VarDeclBitfields) unsigned : NumVarDeclBits; // FIXME: We need something similar to CXXRecordDecl::DefinitionData. /// Whether this variable is a definition which was demoted due to /// module merge. + LLVM_PREFERRED_TYPE(bool) unsigned IsThisDeclarationADemotedDefinition : 1; /// Whether this variable is the exception variable in a C++ catch /// or an Objective-C @catch statement. + LLVM_PREFERRED_TYPE(bool) unsigned ExceptionVar : 1; /// Whether this local variable could be allocated in the return /// slot of its function, enabling the named return value optimization /// (NRVO). + LLVM_PREFERRED_TYPE(bool) unsigned NRVOVariable : 1; /// Whether this variable is the for-range-declaration in a C++0x /// for-range statement. + LLVM_PREFERRED_TYPE(bool) unsigned CXXForRangeDecl : 1; /// Whether this variable is the for-in loop declaration in Objective-C. + LLVM_PREFERRED_TYPE(bool) unsigned ObjCForDecl : 1; /// Whether this variable is (C++1z) inline. + LLVM_PREFERRED_TYPE(bool) unsigned IsInline : 1; /// Whether this variable has (C++1z) inline explicitly specified. + LLVM_PREFERRED_TYPE(bool) unsigned IsInlineSpecified : 1; /// Whether this variable is (C++0x) constexpr. + LLVM_PREFERRED_TYPE(bool) unsigned IsConstexpr : 1; /// Whether this variable is the implicit variable for a lambda /// init-capture. + LLVM_PREFERRED_TYPE(bool) unsigned IsInitCapture : 1; /// Whether this local extern variable's previous declaration was /// declared in the same block scope. This controls whether we should merge /// the type of this declaration with its previous declaration. + LLVM_PREFERRED_TYPE(bool) unsigned PreviousDeclInSameBlockScope : 1; /// Defines kind of the ImplicitParamDecl: 'this', 'self', 'vtt', '_cmd' or /// something else. + LLVM_PREFERRED_TYPE(ImplicitParamKind) unsigned ImplicitParamKind : 3; + LLVM_PREFERRED_TYPE(bool) unsigned EscapingByref : 1; }; @@ -1654,36 +1677,36 @@ public: static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; } }; -class ImplicitParamDecl : public VarDecl { - void anchor() override; +/// Defines the kind of the implicit parameter: is this an implicit parameter +/// with pointer to 'this', 'self', '_cmd', virtual table pointers, captured +/// context or something else. +enum class ImplicitParamKind { + /// Parameter for Objective-C 'self' argument + ObjCSelf, -public: - /// Defines the kind of the implicit parameter: is this an implicit parameter - /// with pointer to 'this', 'self', '_cmd', virtual table pointers, captured - /// context or something else. - enum ImplicitParamKind : unsigned { - /// Parameter for Objective-C 'self' argument - ObjCSelf, + /// Parameter for Objective-C '_cmd' argument + ObjCCmd, - /// Parameter for Objective-C '_cmd' argument - ObjCCmd, + /// Parameter for C++ 'this' argument + CXXThis, - /// Parameter for C++ 'this' argument - CXXThis, + /// Parameter for C++ virtual table pointers + CXXVTT, - /// Parameter for C++ virtual table pointers - CXXVTT, + /// Parameter for captured context + CapturedContext, - /// Parameter for captured context - CapturedContext, + /// Parameter for Thread private variable + ThreadPrivateVar, - /// Parameter for Thread private variable - ThreadPrivateVar, + /// Other implicit parameter + Other, +}; - /// Other implicit parameter - Other, - }; +class ImplicitParamDecl : public VarDecl { + void anchor() override; +public: /// Create implicit parameter. static ImplicitParamDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id, @@ -1698,7 +1721,7 @@ public: ImplicitParamKind ParamKind) : VarDecl(ImplicitParam, C, DC, IdLoc, IdLoc, Id, Type, /*TInfo=*/nullptr, SC_None) { - NonParmVarDeclBits.ImplicitParamKind = ParamKind; + NonParmVarDeclBits.ImplicitParamKind = llvm::to_underlying(ParamKind); setImplicit(); } @@ -1706,7 +1729,7 @@ public: : VarDecl(ImplicitParam, C, /*DC=*/nullptr, SourceLocation(), SourceLocation(), /*Id=*/nullptr, Type, /*TInfo=*/nullptr, SC_None) { - NonParmVarDeclBits.ImplicitParamKind = ParamKind; + NonParmVarDeclBits.ImplicitParamKind = llvm::to_underlying(ParamKind); setImplicit(); } @@ -3013,8 +3036,11 @@ class FieldDecl : public DeclaratorDecl, public Mergeable { ISK_CapturedVLAType, }; + LLVM_PREFERRED_TYPE(bool) unsigned BitField : 1; + LLVM_PREFERRED_TYPE(bool) unsigned Mutable : 1; + LLVM_PREFERRED_TYPE(InitStorageKind) unsigned StorageKind : 2; mutable unsigned CachedFieldIndex : 28; diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index fdc59ac7419d9e3b0a47272452365355cbd8fec2..5b1038582bc674700132386ff9687ef30ad86592 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -16,6 +16,7 @@ #include "clang/AST/ASTDumperUtils.h" #include "clang/AST/AttrIterator.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/SelectorLocationsKind.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -284,31 +285,38 @@ private: SourceLocation Loc; /// DeclKind - This indicates which class this is. + LLVM_PREFERRED_TYPE(Kind) unsigned DeclKind : 7; /// InvalidDecl - This indicates a semantic error occurred. + LLVM_PREFERRED_TYPE(bool) unsigned InvalidDecl : 1; /// HasAttrs - This indicates whether the decl has attributes or not. + LLVM_PREFERRED_TYPE(bool) unsigned HasAttrs : 1; /// Implicit - Whether this declaration was implicitly generated by /// the implementation rather than explicitly written by the user. + LLVM_PREFERRED_TYPE(bool) unsigned Implicit : 1; /// Whether this declaration was "used", meaning that a definition is /// required. + LLVM_PREFERRED_TYPE(bool) unsigned Used : 1; /// Whether this declaration was "referenced". /// The difference with 'Used' is whether the reference appears in a /// evaluated context or not, e.g. functions used in uninstantiated templates /// are regarded as "referenced" but not "used". + LLVM_PREFERRED_TYPE(bool) unsigned Referenced : 1; /// Whether this declaration is a top-level declaration (function, /// global variable, etc.) that is lexically inside an objc container /// definition. + LLVM_PREFERRED_TYPE(bool) unsigned TopLevelDeclInObjCContainer : 1; /// Whether statistic collection is enabled. @@ -326,15 +334,19 @@ protected: /// Access - Used by C++ decls for the access specifier. // NOTE: VC++ treats enums as signed, avoid using the AccessSpecifier enum + LLVM_PREFERRED_TYPE(AccessSpecifier) unsigned Access : 2; /// Whether this declaration was loaded from an AST file. + LLVM_PREFERRED_TYPE(bool) unsigned FromASTFile : 1; /// IdentifierNamespace - This specifies what IDNS_* namespace this lives in. + LLVM_PREFERRED_TYPE(IdentifierNamespace) unsigned IdentifierNamespace : 14; /// If 0, we have not computed the linkage of this declaration. + LLVM_PREFERRED_TYPE(Linkage) mutable unsigned CacheValidAndLinkage : 3; /// Allocate memory for a deserialized declaration. @@ -1443,35 +1455,42 @@ class DeclContext { class DeclContextBitfields { friend class DeclContext; /// DeclKind - This indicates which class this is. + LLVM_PREFERRED_TYPE(Decl::Kind) uint64_t DeclKind : 7; /// Whether this declaration context also has some external /// storage that contains additional declarations that are lexically /// part of this context. + LLVM_PREFERRED_TYPE(bool) mutable uint64_t ExternalLexicalStorage : 1; /// Whether this declaration context also has some external /// storage that contains additional declarations that are visible /// in this context. + LLVM_PREFERRED_TYPE(bool) mutable uint64_t ExternalVisibleStorage : 1; /// Whether this declaration context has had externally visible /// storage added since the last lookup. In this case, \c LookupPtr's /// invariant may not hold and needs to be fixed before we perform /// another lookup. + LLVM_PREFERRED_TYPE(bool) mutable uint64_t NeedToReconcileExternalVisibleStorage : 1; /// If \c true, this context may have local lexical declarations /// that are missing from the lookup table. + LLVM_PREFERRED_TYPE(bool) mutable uint64_t HasLazyLocalLexicalLookups : 1; /// If \c true, the external source may have lexical declarations /// that are missing from the lookup table. + LLVM_PREFERRED_TYPE(bool) mutable uint64_t HasLazyExternalLexicalLookups : 1; /// If \c true, lookups should only return identifier from /// DeclContext scope (for example TranslationUnit). Used in /// LookupQualifiedName() + LLVM_PREFERRED_TYPE(bool) mutable uint64_t UseQualifiedLookup : 1; }; @@ -1484,38 +1503,47 @@ class DeclContext { class TagDeclBitfields { friend class TagDecl; /// For the bits in DeclContextBitfields + LLVM_PREFERRED_TYPE(DeclContextBitfields) uint64_t : NumDeclContextBits; /// The TagKind enum. + LLVM_PREFERRED_TYPE(TagTypeKind) uint64_t TagDeclKind : 3; /// True if this is a definition ("struct foo {};"), false if it is a /// declaration ("struct foo;"). It is not considered a definition /// until the definition has been fully processed. + LLVM_PREFERRED_TYPE(bool) uint64_t IsCompleteDefinition : 1; /// True if this is currently being defined. + LLVM_PREFERRED_TYPE(bool) uint64_t IsBeingDefined : 1; /// True if this tag declaration is "embedded" (i.e., defined or declared /// for the very first time) in the syntax of a declarator. + LLVM_PREFERRED_TYPE(bool) uint64_t IsEmbeddedInDeclarator : 1; /// True if this tag is free standing, e.g. "struct foo;". + LLVM_PREFERRED_TYPE(bool) uint64_t IsFreeStanding : 1; /// Indicates whether it is possible for declarations of this kind /// to have an out-of-date definition. /// /// This option is only enabled when modules are enabled. + LLVM_PREFERRED_TYPE(bool) uint64_t MayHaveOutOfDateDef : 1; /// Has the full definition of this type been required by a use somewhere in /// the TU. + LLVM_PREFERRED_TYPE(bool) uint64_t IsCompleteDefinitionRequired : 1; /// Whether this tag is a definition which was demoted due to /// a module merge. + LLVM_PREFERRED_TYPE(bool) uint64_t IsThisDeclarationADemotedDefinition : 1; }; @@ -1528,6 +1556,7 @@ class DeclContext { class EnumDeclBitfields { friend class EnumDecl; /// For the bits in TagDeclBitfields. + LLVM_PREFERRED_TYPE(TagDeclBitfields) uint64_t : NumTagDeclBits; /// Width in bits required to store all the non-negative @@ -1540,19 +1569,23 @@ class DeclContext { /// True if this tag declaration is a scoped enumeration. Only /// possible in C++11 mode. + LLVM_PREFERRED_TYPE(bool) uint64_t IsScoped : 1; /// If this tag declaration is a scoped enum, /// then this is true if the scoped enum was declared using the class /// tag, false if it was declared with the struct tag. No meaning is /// associated if this tag declaration is not a scoped enum. + LLVM_PREFERRED_TYPE(bool) uint64_t IsScopedUsingClassTag : 1; /// True if this is an enumeration with fixed underlying type. Only /// possible in C++11, Microsoft extensions, or Objective C mode. + LLVM_PREFERRED_TYPE(bool) uint64_t IsFixed : 1; /// True if a valid hash is stored in ODRHash. + LLVM_PREFERRED_TYPE(bool) uint64_t HasODRHash : 1; }; @@ -1565,49 +1598,64 @@ class DeclContext { class RecordDeclBitfields { friend class RecordDecl; /// For the bits in TagDeclBitfields. + LLVM_PREFERRED_TYPE(TagDeclBitfields) uint64_t : NumTagDeclBits; /// This is true if this struct ends with a flexible /// array member (e.g. int X[]) or if this union contains a struct that does. /// If so, this cannot be contained in arrays or other structs as a member. + LLVM_PREFERRED_TYPE(bool) uint64_t HasFlexibleArrayMember : 1; /// Whether this is the type of an anonymous struct or union. + LLVM_PREFERRED_TYPE(bool) uint64_t AnonymousStructOrUnion : 1; /// This is true if this struct has at least one member /// containing an Objective-C object pointer type. + LLVM_PREFERRED_TYPE(bool) uint64_t HasObjectMember : 1; /// This is true if struct has at least one member of /// 'volatile' type. + LLVM_PREFERRED_TYPE(bool) uint64_t HasVolatileMember : 1; /// Whether the field declarations of this record have been loaded /// from external storage. To avoid unnecessary deserialization of /// methods/nested types we allow deserialization of just the fields /// when needed. + LLVM_PREFERRED_TYPE(bool) mutable uint64_t LoadedFieldsFromExternalStorage : 1; /// Basic properties of non-trivial C structs. + LLVM_PREFERRED_TYPE(bool) uint64_t NonTrivialToPrimitiveDefaultInitialize : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t NonTrivialToPrimitiveCopy : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t NonTrivialToPrimitiveDestroy : 1; /// The following bits indicate whether this is or contains a C union that /// is non-trivial to default-initialize, destruct, or copy. These bits /// imply the associated basic non-triviality predicates declared above. + LLVM_PREFERRED_TYPE(bool) uint64_t HasNonTrivialToPrimitiveDefaultInitializeCUnion : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t HasNonTrivialToPrimitiveDestructCUnion : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t HasNonTrivialToPrimitiveCopyCUnion : 1; /// Indicates whether this struct is destroyed in the callee. + LLVM_PREFERRED_TYPE(bool) uint64_t ParamDestroyedInCallee : 1; /// Represents the way this type is passed to a function. + LLVM_PREFERRED_TYPE(RecordArgPassingKind) uint64_t ArgPassingRestrictions : 2; /// Indicates whether this struct has had its field layout randomized. + LLVM_PREFERRED_TYPE(bool) uint64_t IsRandomized : 1; /// True if a valid hash is stored in ODRHash. This should shave off some @@ -1624,10 +1672,12 @@ class DeclContext { class OMPDeclareReductionDeclBitfields { friend class OMPDeclareReductionDecl; /// For the bits in DeclContextBitfields + LLVM_PREFERRED_TYPE(DeclContextBitfields) uint64_t : NumDeclContextBits; /// Kind of initializer, /// function call or omp_priv initialization. + LLVM_PREFERRED_TYPE(OMPDeclareReductionInitKind) uint64_t InitializerKind : 2; }; @@ -1644,71 +1694,99 @@ class DeclContext { /// For DeductionCandidateKind friend class CXXDeductionGuideDecl; /// For the bits in DeclContextBitfields. + LLVM_PREFERRED_TYPE(DeclContextBitfields) uint64_t : NumDeclContextBits; + LLVM_PREFERRED_TYPE(StorageClass) uint64_t SClass : 3; + LLVM_PREFERRED_TYPE(bool) uint64_t IsInline : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsInlineSpecified : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsVirtualAsWritten : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsPure : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t HasInheritedPrototype : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t HasWrittenPrototype : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsDeleted : 1; /// Used by CXXMethodDecl + LLVM_PREFERRED_TYPE(bool) uint64_t IsTrivial : 1; /// This flag indicates whether this function is trivial for the purpose of /// calls. This is meaningful only when this function is a copy/move /// constructor or a destructor. + LLVM_PREFERRED_TYPE(bool) uint64_t IsTrivialForCall : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsDefaulted : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsExplicitlyDefaulted : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t HasDefaultedFunctionInfo : 1; /// For member functions of complete types, whether this is an ineligible /// special member function or an unselected destructor. See /// [class.mem.special]. + LLVM_PREFERRED_TYPE(bool) uint64_t IsIneligibleOrNotSelected : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t HasImplicitReturnZero : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsLateTemplateParsed : 1; /// Kind of contexpr specifier as defined by ConstexprSpecKind. + LLVM_PREFERRED_TYPE(ConstexprSpecKind) uint64_t ConstexprKind : 2; + LLVM_PREFERRED_TYPE(bool) uint64_t BodyContainsImmediateEscalatingExpression : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t InstantiationIsPending : 1; /// Indicates if the function uses __try. + LLVM_PREFERRED_TYPE(bool) uint64_t UsesSEHTry : 1; /// Indicates if the function was a definition /// but its body was skipped. + LLVM_PREFERRED_TYPE(bool) uint64_t HasSkippedBody : 1; /// Indicates if the function declaration will /// have a body, once we're done parsing it. + LLVM_PREFERRED_TYPE(bool) uint64_t WillHaveBody : 1; /// Indicates that this function is a multiversioned /// function using attribute 'target'. + LLVM_PREFERRED_TYPE(bool) uint64_t IsMultiVersion : 1; /// Only used by CXXDeductionGuideDecl. Indicates the kind /// of the Deduction Guide that is implicitly generated /// (used during overload resolution). + LLVM_PREFERRED_TYPE(DeductionCandidate) uint64_t DeductionCandidateKind : 2; /// Store the ODRHash after first calculation. + LLVM_PREFERRED_TYPE(bool) uint64_t HasODRHash : 1; /// Indicates if the function uses Floating Point Constrained Intrinsics + LLVM_PREFERRED_TYPE(bool) uint64_t UsesFPIntrin : 1; // Indicates this function is a constrained friend, where the constraint // refers to an enclosing template for hte purposes of [temp.friend]p9. + LLVM_PREFERRED_TYPE(bool) uint64_t FriendConstraintRefersToEnclosingTemplate : 1; }; @@ -1721,6 +1799,7 @@ class DeclContext { class CXXConstructorDeclBitfields { friend class CXXConstructorDecl; /// For the bits in FunctionDeclBitfields. + LLVM_PREFERRED_TYPE(FunctionDeclBitfields) uint64_t : NumFunctionDeclBits; /// 20 bits to fit in the remaining available space. @@ -1729,12 +1808,15 @@ class DeclContext { /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. uint64_t NumCtorInitializers : 17; + LLVM_PREFERRED_TYPE(bool) uint64_t IsInheritingConstructor : 1; /// Whether this constructor has a trail-allocated explicit specifier. + LLVM_PREFERRED_TYPE(bool) uint64_t HasTrailingExplicitSpecifier : 1; /// If this constructor does't have a trail-allocated explicit specifier. /// Whether this constructor is explicit specified. + LLVM_PREFERRED_TYPE(bool) uint64_t IsSimpleExplicit : 1; }; @@ -1748,43 +1830,56 @@ class DeclContext { friend class ObjCMethodDecl; /// For the bits in DeclContextBitfields. + LLVM_PREFERRED_TYPE(DeclContextBitfields) uint64_t : NumDeclContextBits; /// The conventional meaning of this method; an ObjCMethodFamily. /// This is not serialized; instead, it is computed on demand and /// cached. + LLVM_PREFERRED_TYPE(ObjCMethodFamily) mutable uint64_t Family : ObjCMethodFamilyBitWidth; /// instance (true) or class (false) method. + LLVM_PREFERRED_TYPE(bool) uint64_t IsInstance : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsVariadic : 1; /// True if this method is the getter or setter for an explicit property. + LLVM_PREFERRED_TYPE(bool) uint64_t IsPropertyAccessor : 1; /// True if this method is a synthesized property accessor stub. + LLVM_PREFERRED_TYPE(bool) uint64_t IsSynthesizedAccessorStub : 1; /// Method has a definition. + LLVM_PREFERRED_TYPE(bool) uint64_t IsDefined : 1; /// Method redeclaration in the same interface. + LLVM_PREFERRED_TYPE(bool) uint64_t IsRedeclaration : 1; /// Is redeclared in the same interface. + LLVM_PREFERRED_TYPE(bool) mutable uint64_t HasRedeclaration : 1; /// \@required/\@optional + LLVM_PREFERRED_TYPE(ObjCImplementationControl) uint64_t DeclImplementation : 2; /// in, inout, etc. + LLVM_PREFERRED_TYPE(Decl::ObjCDeclQualifier) uint64_t objcDeclQualifier : 7; /// Indicates whether this method has a related result type. + LLVM_PREFERRED_TYPE(bool) uint64_t RelatedResultType : 1; /// Whether the locations of the selector identifiers are in a /// "standard" position, a enum SelectorLocationsKind. + LLVM_PREFERRED_TYPE(SelectorLocationsKind) uint64_t SelLocsKind : 2; /// Whether this method overrides any other in the class hierarchy. @@ -1794,9 +1889,11 @@ class DeclContext { /// the same selector and is of the same kind (class or instance). /// A method in an implementation is not considered as overriding the same /// method in the interface or its categories. + LLVM_PREFERRED_TYPE(bool) uint64_t IsOverriding : 1; /// Indicates if the method was a definition but its body was skipped. + LLVM_PREFERRED_TYPE(bool) uint64_t HasSkippedBody : 1; }; @@ -1809,6 +1906,7 @@ class DeclContext { class ObjCContainerDeclBitfields { friend class ObjCContainerDecl; /// For the bits in DeclContextBitfields + LLVM_PREFERRED_TYPE(DeclContextBitfields) uint32_t : NumDeclContextBits; // Not a bitfield but this saves space. @@ -1827,16 +1925,18 @@ class DeclContext { class LinkageSpecDeclBitfields { friend class LinkageSpecDecl; /// For the bits in DeclContextBitfields. + LLVM_PREFERRED_TYPE(DeclContextBitfields) uint64_t : NumDeclContextBits; - /// The language for this linkage specification with values - /// in the enum LinkageSpecDecl::LanguageIDs. + /// The language for this linkage specification. + LLVM_PREFERRED_TYPE(LinkageSpecLanguageIDs) uint64_t Language : 3; /// True if this linkage spec has braces. /// This is needed so that hasBraces() returns the correct result while the /// linkage spec body is being parsed. Once RBraceLoc has been set this is /// not used, so it doesn't need to be serialized. + LLVM_PREFERRED_TYPE(bool) uint64_t HasBraces : 1; }; @@ -1849,20 +1949,27 @@ class DeclContext { class BlockDeclBitfields { friend class BlockDecl; /// For the bits in DeclContextBitfields. + LLVM_PREFERRED_TYPE(DeclContextBitfields) uint64_t : NumDeclContextBits; + LLVM_PREFERRED_TYPE(bool) uint64_t IsVariadic : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t CapturesCXXThis : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t BlockMissingReturnType : 1; + LLVM_PREFERRED_TYPE(bool) uint64_t IsConversionFromLambda : 1; /// A bit that indicates this block is passed directly to a function as a /// non-escaping parameter. + LLVM_PREFERRED_TYPE(bool) uint64_t DoesNotEscape : 1; /// A bit that indicates whether it's possible to avoid coying this block to /// the heap when it initializes or is assigned to a local variable with /// automatic storage. + LLVM_PREFERRED_TYPE(bool) uint64_t CanAvoidCopyToHeap : 1; }; diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index df1dc5a401f39a5734e46d339a3643eb96222d31..432293583576b5aa8e609817874d69b1645fbf6a 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -154,22 +154,26 @@ class CXXBaseSpecifier { SourceLocation EllipsisLoc; /// Whether this is a virtual base class or not. + LLVM_PREFERRED_TYPE(bool) unsigned Virtual : 1; /// Whether this is the base of a class (true) or of a struct (false). /// /// This determines the mapping from the access specifier as written in the /// source code to the access specifier used for semantic analysis. + LLVM_PREFERRED_TYPE(bool) unsigned BaseOfClass : 1; /// Access specifier as written in the source code (may be AS_none). /// /// The actual type of data stored here is an AccessSpecifier, but we use - /// "unsigned" here to work around a VC++ bug. + /// "unsigned" here to work around Microsoft ABI. + LLVM_PREFERRED_TYPE(AccessSpecifier) unsigned Access : 2; /// Whether the class contains a using declaration /// to inherit the named class's constructors. + LLVM_PREFERRED_TYPE(bool) unsigned InheritConstructors : 1; /// The type of the base class. @@ -290,15 +294,19 @@ private: #include "CXXRecordDeclDefinitionBits.def" /// Whether this class describes a C++ lambda. + LLVM_PREFERRED_TYPE(bool) unsigned IsLambda : 1; /// Whether we are currently parsing base specifiers. + LLVM_PREFERRED_TYPE(bool) unsigned IsParsingBaseSpecifiers : 1; /// True when visible conversion functions are already computed /// and are available. + LLVM_PREFERRED_TYPE(bool) unsigned ComputedVisibleConversions : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasODRHash : 1; /// A hash of parts of the class to help in ODR checking. @@ -383,12 +391,15 @@ private: /// lambda will have been created with the enclosing context as its /// declaration context, rather than function. This is an unfortunate /// artifact of having to parse the default arguments before. + LLVM_PREFERRED_TYPE(LambdaDependencyKind) unsigned DependencyKind : 2; /// Whether this lambda is a generic lambda. + LLVM_PREFERRED_TYPE(bool) unsigned IsGenericLambda : 1; /// The Default Capture. + LLVM_PREFERRED_TYPE(LambdaCaptureDefault) unsigned CaptureDefault : 2; /// The number of captures in this lambda is limited 2^NumCaptures. @@ -398,6 +409,7 @@ private: unsigned NumExplicitCaptures : 12; /// Has known `internal` linkage. + LLVM_PREFERRED_TYPE(bool) unsigned HasKnownInternalLinkage : 1; /// The number used to indicate this lambda expression for name @@ -2305,14 +2317,17 @@ class CXXCtorInitializer final { /// If the initializee is a type, whether that type makes this /// a delegating initialization. + LLVM_PREFERRED_TYPE(bool) unsigned IsDelegating : 1; /// If the initializer is a base initializer, this keeps track /// of whether the base is virtual or not. + LLVM_PREFERRED_TYPE(bool) unsigned IsVirtual : 1; /// Whether or not the initializer is explicitly written /// in the sources. + LLVM_PREFERRED_TYPE(bool) unsigned IsWritten : 1; /// If IsWritten is true, then this number keeps track of the textual order @@ -3588,6 +3603,7 @@ class ConstructorUsingShadowDecl final : public UsingShadowDecl { /// \c true if the constructor ultimately named by this using shadow /// declaration is within a virtual base class subobject of the class that /// contains this declaration. + LLVM_PREFERRED_TYPE(bool) unsigned IsVirtual : 1; ConstructorUsingShadowDecl(ASTContext &C, DeclContext *DC, SourceLocation Loc, diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h index 52efbb13063188dcec67d4a975aab80d8c3cd7da..3e6ca5b3219259e1eafb908afc08208ec5bfc251 100644 --- a/clang/include/clang/AST/DeclFriend.h +++ b/clang/include/clang/AST/DeclFriend.h @@ -73,6 +73,7 @@ private: /// True if this 'friend' declaration is unsupported. Eventually we /// will support every possible friend declaration, but for now we /// silently ignore some and set this flag to authorize all access. + LLVM_PREFERRED_TYPE(bool) unsigned UnsupportedFriend : 1; // The number of "outer" template parameter lists in non-templatic diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index e0b31c58c39a2c5d8e5baf02b35bdeb8c5b383cf..f8f894b4b10d1919a484eae6b14adf45517166ea 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -580,6 +580,7 @@ class ObjCTypeParamDecl : public TypedefNameDecl { unsigned Index : 14; /// The variance of the type parameter. + LLVM_PREFERRED_TYPE(ObjCTypeParamVariance) unsigned Variance : 2; /// The location of the variance, if any. @@ -741,10 +742,13 @@ private: QualType DeclType; TypeSourceInfo *DeclTypeSourceInfo; + LLVM_PREFERRED_TYPE(ObjCPropertyAttribute::Kind) unsigned PropertyAttributes : NumObjCPropertyAttrsBits; + LLVM_PREFERRED_TYPE(ObjCPropertyAttribute::Kind) unsigned PropertyAttributesAsWritten : NumObjCPropertyAttrsBits; // \@required/\@optional + LLVM_PREFERRED_TYPE(PropertyControl) unsigned PropertyImplementation : 2; // getter name of NULL if no getter @@ -1178,14 +1182,17 @@ class ObjCInterfaceDecl : public ObjCContainerDecl /// Indicates that the contents of this Objective-C class will be /// completed by the external AST source when required. + LLVM_PREFERRED_TYPE(bool) mutable unsigned ExternallyCompleted : 1; /// Indicates that the ivar cache does not yet include ivars /// declared in the implementation. + LLVM_PREFERRED_TYPE(bool) mutable unsigned IvarListMissingImplementation : 1; /// Indicates that this interface decl contains at least one initializer /// marked with the 'objc_designated_initializer' attribute. + LLVM_PREFERRED_TYPE(bool) unsigned HasDesignatedInitializers : 1; enum InheritedDesignatedInitializersState { @@ -1201,9 +1208,11 @@ class ObjCInterfaceDecl : public ObjCContainerDecl }; /// One of the \c InheritedDesignatedInitializersState enumeratos. + LLVM_PREFERRED_TYPE(InheritedDesignatedInitializersState) mutable unsigned InheritedDesignatedInitializers : 2; /// Tracks whether a ODR hash has been computed for this interface. + LLVM_PREFERRED_TYPE(bool) unsigned HasODRHash : 1; /// A hash of parts of the class to help in ODR checking. @@ -2007,7 +2016,9 @@ private: ObjCIvarDecl *NextIvar = nullptr; // NOTE: VC++ treats enums as signed, avoid using the AccessControl enum + LLVM_PREFERRED_TYPE(AccessControl) unsigned DeclAccess : 3; + LLVM_PREFERRED_TYPE(bool) unsigned Synthesized : 1; }; @@ -2074,6 +2085,7 @@ class ObjCProtocolDecl : public ObjCContainerDecl, ObjCProtocolList ReferencedProtocols; /// Tracks whether a ODR hash has been computed for this protocol. + LLVM_PREFERRED_TYPE(bool) unsigned HasODRHash : 1; /// A hash of parts of the class to help in ODR checking. @@ -2596,9 +2608,11 @@ class ObjCImplementationDecl : public ObjCImplDecl { /// Do the ivars of this class require initialization other than /// zero-initialization? + LLVM_PREFERRED_TYPE(bool) bool HasNonZeroConstructors : 1; /// Do the ivars of this class require non-trivial destruction? + LLVM_PREFERRED_TYPE(bool) bool HasDestructors : 1; ObjCImplementationDecl(DeclContext *DC, diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 54d28227a4ae91b1dee9bb0949391d802fc2b4a3..832ad2de6b08a82c3f1a5b8ba0a2de7c3ab66d1d 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -83,13 +83,16 @@ class TemplateParameterList final /// Whether this template parameter list contains an unexpanded parameter /// pack. + LLVM_PREFERRED_TYPE(bool) unsigned ContainsUnexpandedParameterPack : 1; /// Whether this template parameter list has a requires clause. + LLVM_PREFERRED_TYPE(bool) unsigned HasRequiresClause : 1; /// Whether any of the template parameters has constrained-parameter /// constraint-expression. + LLVM_PREFERRED_TYPE(bool) unsigned HasConstrainedParameters : 1; protected: @@ -1833,7 +1836,7 @@ class ClassTemplateSpecializationDecl SourceLocation PointOfInstantiation; /// The kind of specialization this declaration refers to. - /// Really a value of type TemplateSpecializationKind. + LLVM_PREFERRED_TYPE(TemplateSpecializationKind) unsigned SpecializationKind : 3; protected: @@ -2638,13 +2641,14 @@ class VarTemplateSpecializationDecl : public VarDecl, SourceLocation PointOfInstantiation; /// The kind of specialization this declaration refers to. - /// Really a value of type TemplateSpecializationKind. + LLVM_PREFERRED_TYPE(TemplateSpecializationKind) unsigned SpecializationKind : 3; /// Whether this declaration is a complete definition of the /// variable template specialization. We can't otherwise tell apart /// an instantiated declaration from an instantiated definition with /// no initializer. + LLVM_PREFERRED_TYPE(bool) unsigned IsCompleteDefinition : 1; protected: diff --git a/clang/include/clang/AST/DependentDiagnostic.h b/clang/include/clang/AST/DependentDiagnostic.h index 18276d54d540471473c736d8b64b2f045c428d32..cadf970620041e61e7c492f7aa0a39ca047c2de0 100644 --- a/clang/include/clang/AST/DependentDiagnostic.h +++ b/clang/include/clang/AST/DependentDiagnostic.h @@ -113,7 +113,9 @@ private: struct { SourceLocation Loc; + LLVM_PREFERRED_TYPE(AccessSpecifier) unsigned Access : 2; + LLVM_PREFERRED_TYPE(bool) unsigned IsMember : 1; NamedDecl *TargetDecl; CXXRecordDecl *NamingClass; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 638f886edd095f1e527f5585d822fd8773c3d16a..a9c4c67a60e8e8e607eb9308075cf298c47afd34 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1049,6 +1049,9 @@ public: } }; +/// Describes the kind of result that can be tail-allocated. +enum class ConstantResultStorageKind { None, Int64, APValue }; + /// ConstantExpr - An expression that occurs in a constant context and /// optionally the result of evaluating the expression. class ConstantExpr final @@ -1061,20 +1064,15 @@ class ConstantExpr final friend class ASTStmtReader; friend class ASTStmtWriter; -public: - /// Describes the kind of result that can be tail-allocated. - enum ResultStorageKind { RSK_None, RSK_Int64, RSK_APValue }; - -private: size_t numTrailingObjects(OverloadToken) const { - return ConstantExprBits.ResultKind == ConstantExpr::RSK_APValue; + return getResultStorageKind() == ConstantResultStorageKind::APValue; } size_t numTrailingObjects(OverloadToken) const { - return ConstantExprBits.ResultKind == ConstantExpr::RSK_Int64; + return getResultStorageKind() == ConstantResultStorageKind::Int64; } uint64_t &Int64Result() { - assert(ConstantExprBits.ResultKind == ConstantExpr::RSK_Int64 && + assert(getResultStorageKind() == ConstantResultStorageKind::Int64 && "invalid accessor"); return *getTrailingObjects(); } @@ -1082,7 +1080,7 @@ private: return const_cast(this)->Int64Result(); } APValue &APValueResult() { - assert(ConstantExprBits.ResultKind == ConstantExpr::RSK_APValue && + assert(getResultStorageKind() == ConstantResultStorageKind::APValue && "invalid accessor"); return *getTrailingObjects(); } @@ -1090,22 +1088,23 @@ private: return const_cast(this)->APValueResult(); } - ConstantExpr(Expr *SubExpr, ResultStorageKind StorageKind, + ConstantExpr(Expr *SubExpr, ConstantResultStorageKind StorageKind, bool IsImmediateInvocation); - ConstantExpr(EmptyShell Empty, ResultStorageKind StorageKind); + ConstantExpr(EmptyShell Empty, ConstantResultStorageKind StorageKind); public: static ConstantExpr *Create(const ASTContext &Context, Expr *E, const APValue &Result); - static ConstantExpr *Create(const ASTContext &Context, Expr *E, - ResultStorageKind Storage = RSK_None, - bool IsImmediateInvocation = false); + static ConstantExpr * + Create(const ASTContext &Context, Expr *E, + ConstantResultStorageKind Storage = ConstantResultStorageKind::None, + bool IsImmediateInvocation = false); static ConstantExpr *CreateEmpty(const ASTContext &Context, - ResultStorageKind StorageKind); + ConstantResultStorageKind StorageKind); - static ResultStorageKind getStorageKind(const APValue &Value); - static ResultStorageKind getStorageKind(const Type *T, - const ASTContext &Context); + static ConstantResultStorageKind getStorageKind(const APValue &Value); + static ConstantResultStorageKind getStorageKind(const Type *T, + const ASTContext &Context); SourceLocation getBeginLoc() const LLVM_READONLY { return SubExpr->getBeginLoc(); @@ -1126,8 +1125,8 @@ public: APValue::ValueKind getResultAPValueKind() const { return static_cast(ConstantExprBits.APValueKind); } - ResultStorageKind getResultStorageKind() const { - return static_cast(ConstantExprBits.ResultKind); + ConstantResultStorageKind getResultStorageKind() const { + return static_cast(ConstantExprBits.ResultKind); } bool isImmediateInvocation() const { return ConstantExprBits.IsImmediateInvocation; @@ -1621,26 +1620,18 @@ class FixedPointLiteral : public Expr, public APIntStorage { } }; -class CharacterLiteral : public Expr { -public: - enum CharacterKind { - Ascii, - Wide, - UTF8, - UTF16, - UTF32 - }; +enum class CharacterLiteralKind { Ascii, Wide, UTF8, UTF16, UTF32 }; -private: +class CharacterLiteral : public Expr { unsigned Value; SourceLocation Loc; public: // type should be IntTy - CharacterLiteral(unsigned value, CharacterKind kind, QualType type, + CharacterLiteral(unsigned value, CharacterLiteralKind kind, QualType type, SourceLocation l) : Expr(CharacterLiteralClass, type, VK_PRValue, OK_Ordinary), Value(value), Loc(l) { - CharacterLiteralBits.Kind = kind; + CharacterLiteralBits.Kind = llvm::to_underlying(kind); setDependence(ExprDependence::None); } @@ -1648,8 +1639,8 @@ public: CharacterLiteral(EmptyShell Empty) : Expr(CharacterLiteralClass, Empty) { } SourceLocation getLocation() const { return Loc; } - CharacterKind getKind() const { - return static_cast(CharacterLiteralBits.Kind); + CharacterLiteralKind getKind() const { + return static_cast(CharacterLiteralBits.Kind); } SourceLocation getBeginLoc() const LLVM_READONLY { return Loc; } @@ -1658,14 +1649,16 @@ public: unsigned getValue() const { return Value; } void setLocation(SourceLocation Location) { Loc = Location; } - void setKind(CharacterKind kind) { CharacterLiteralBits.Kind = kind; } + void setKind(CharacterLiteralKind kind) { + CharacterLiteralBits.Kind = llvm::to_underlying(kind); + } void setValue(unsigned Val) { Value = Val; } static bool classof(const Stmt *T) { return T->getStmtClass() == CharacterLiteralClass; } - static void print(unsigned val, CharacterKind Kind, raw_ostream &OS); + static void print(unsigned val, CharacterLiteralKind Kind, raw_ostream &OS); // Iterators child_range children() { @@ -1787,6 +1780,15 @@ public: } }; +enum class StringLiteralKind { + Ordinary, + Wide, + UTF8, + UTF16, + UTF32, + Unevaluated +}; + /// StringLiteral - This represents a string literal expression, e.g. "foo" /// or L"bar" (wide strings). The actual string data can be obtained with /// getBytes() and is NOT null-terminated. The length of the string data is @@ -1825,10 +1827,6 @@ class StringLiteral final /// /// * An array of getByteLength() char used to store the string data. -public: - enum StringKind { Ordinary, Wide, UTF8, UTF16, UTF32, Unevaluated }; - -private: unsigned numTrailingObjects(OverloadToken) const { return 1; } unsigned numTrailingObjects(OverloadToken) const { return getNumConcatenated(); @@ -1850,7 +1848,7 @@ private: } /// Build a string literal. - StringLiteral(const ASTContext &Ctx, StringRef Str, StringKind Kind, + StringLiteral(const ASTContext &Ctx, StringRef Str, StringLiteralKind Kind, bool Pascal, QualType Ty, const SourceLocation *Loc, unsigned NumConcatenated); @@ -1859,7 +1857,8 @@ private: unsigned CharByteWidth); /// Map a target and string kind to the appropriate character width. - static unsigned mapCharByteWidth(TargetInfo const &Target, StringKind SK); + static unsigned mapCharByteWidth(TargetInfo const &Target, + StringLiteralKind SK); /// Set one of the string literal token. void setStrTokenLoc(unsigned TokNum, SourceLocation L) { @@ -1871,13 +1870,13 @@ public: /// This is the "fully general" constructor that allows representation of /// strings formed from multiple concatenated tokens. static StringLiteral *Create(const ASTContext &Ctx, StringRef Str, - StringKind Kind, bool Pascal, QualType Ty, + StringLiteralKind Kind, bool Pascal, QualType Ty, const SourceLocation *Loc, unsigned NumConcatenated); /// Simple constructor for string literals made from one token. static StringLiteral *Create(const ASTContext &Ctx, StringRef Str, - StringKind Kind, bool Pascal, QualType Ty, + StringLiteralKind Kind, bool Pascal, QualType Ty, SourceLocation Loc) { return Create(Ctx, Str, Kind, Pascal, Ty, &Loc, 1); } @@ -1919,16 +1918,16 @@ public: unsigned getLength() const { return *getTrailingObjects(); } unsigned getCharByteWidth() const { return StringLiteralBits.CharByteWidth; } - StringKind getKind() const { - return static_cast(StringLiteralBits.Kind); + StringLiteralKind getKind() const { + return static_cast(StringLiteralBits.Kind); } - bool isOrdinary() const { return getKind() == Ordinary; } - bool isWide() const { return getKind() == Wide; } - bool isUTF8() const { return getKind() == UTF8; } - bool isUTF16() const { return getKind() == UTF16; } - bool isUTF32() const { return getKind() == UTF32; } - bool isUnevaluated() const { return getKind() == Unevaluated; } + bool isOrdinary() const { return getKind() == StringLiteralKind::Ordinary; } + bool isWide() const { return getKind() == StringLiteralKind::Wide; } + bool isUTF8() const { return getKind() == StringLiteralKind::UTF8; } + bool isUTF16() const { return getKind() == StringLiteralKind::UTF16; } + bool isUTF32() const { return getKind() == StringLiteralKind::UTF32; } + bool isUnevaluated() const { return getKind() == StringLiteralKind::Unevaluated; } bool isPascal() const { return StringLiteralBits.IsPascal; } bool containsNonAscii() const { @@ -1996,6 +1995,19 @@ public: } }; +enum class PredefinedIdentKind { + Func, + Function, + LFunction, // Same as Function, but as wide string. + FuncDName, + FuncSig, + LFuncSig, // Same as FuncSig, but as wide string + PrettyFunction, + /// The same as PrettyFunction, except that the + /// 'virtual' keyword is omitted for virtual member functions. + PrettyFunctionNoVirtual +}; + /// [C99 6.4.2.2] - A predefined identifier such as __func__. class PredefinedExpr final : public Expr, @@ -2007,22 +2019,7 @@ class PredefinedExpr final // "Stmt *" for the predefined identifier. It is present if and only if // hasFunctionName() is true and is always a "StringLiteral *". -public: - enum IdentKind { - Func, - Function, - LFunction, // Same as Function, but as wide string. - FuncDName, - FuncSig, - LFuncSig, // Same as FuncSig, but as wide string - PrettyFunction, - /// The same as PrettyFunction, except that the - /// 'virtual' keyword is omitted for virtual member functions. - PrettyFunctionNoVirtual - }; - -private: - PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK, + PredefinedExpr(SourceLocation L, QualType FNTy, PredefinedIdentKind IK, bool IsTransparent, StringLiteral *SL); explicit PredefinedExpr(EmptyShell Empty, bool HasFunctionName); @@ -2042,15 +2039,15 @@ public: /// If IsTransparent, the PredefinedExpr is transparently handled as a /// StringLiteral. static PredefinedExpr *Create(const ASTContext &Ctx, SourceLocation L, - QualType FNTy, IdentKind IK, bool IsTransparent, - StringLiteral *SL); + QualType FNTy, PredefinedIdentKind IK, + bool IsTransparent, StringLiteral *SL); /// Create an empty PredefinedExpr. static PredefinedExpr *CreateEmpty(const ASTContext &Ctx, bool HasFunctionName); - IdentKind getIdentKind() const { - return static_cast(PredefinedExprBits.Kind); + PredefinedIdentKind getIdentKind() const { + return static_cast(PredefinedExprBits.Kind); } bool isTransparent() const { return PredefinedExprBits.IsTransparent; } @@ -2070,12 +2067,13 @@ public: : nullptr; } - static StringRef getIdentKindName(IdentKind IK); + static StringRef getIdentKindName(PredefinedIdentKind IK); StringRef getIdentKindName() const { return getIdentKindName(getIdentKind()); } - static std::string ComputeName(IdentKind IK, const Decl *CurrentDecl); + static std::string ComputeName(PredefinedIdentKind IK, + const Decl *CurrentDecl); SourceLocation getBeginLoc() const { return getLocation(); } SourceLocation getEndLoc() const { return getLocation(); } @@ -4735,6 +4733,16 @@ public: } }; +enum class SourceLocIdentKind { + Function, + FuncSig, + File, + FileName, + Line, + Column, + SourceLocStruct +}; + /// Represents a function call to one of __builtin_LINE(), __builtin_COLUMN(), /// __builtin_FUNCTION(), __builtin_FUNCSIG(), __builtin_FILE(), /// __builtin_FILE_NAME() or __builtin_source_location(). @@ -4743,19 +4751,9 @@ class SourceLocExpr final : public Expr { DeclContext *ParentContext; public: - enum IdentKind { - Function, - FuncSig, - File, - FileName, - Line, - Column, - SourceLocStruct - }; - - SourceLocExpr(const ASTContext &Ctx, IdentKind Type, QualType ResultTy, - SourceLocation BLoc, SourceLocation RParenLoc, - DeclContext *Context); + SourceLocExpr(const ASTContext &Ctx, SourceLocIdentKind Type, + QualType ResultTy, SourceLocation BLoc, + SourceLocation RParenLoc, DeclContext *Context); /// Build an empty call expression. explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} @@ -4768,20 +4766,20 @@ public: /// Return a string representing the name of the specific builtin function. StringRef getBuiltinStr() const; - IdentKind getIdentKind() const { - return static_cast(SourceLocExprBits.Kind); + SourceLocIdentKind getIdentKind() const { + return static_cast(SourceLocExprBits.Kind); } bool isIntType() const { switch (getIdentKind()) { - case File: - case FileName: - case Function: - case FuncSig: - case SourceLocStruct: + case SourceLocIdentKind::File: + case SourceLocIdentKind::FileName: + case SourceLocIdentKind::Function: + case SourceLocIdentKind::FuncSig: + case SourceLocIdentKind::SourceLocStruct: return false; - case Line: - case Column: + case SourceLocIdentKind::Line: + case SourceLocIdentKind::Column: return true; } llvm_unreachable("unknown source location expression kind"); @@ -5114,6 +5112,7 @@ private: /// Whether this designated initializer used the GNU deprecated /// syntax rather than the C99 '=' syntax. + LLVM_PREFERRED_TYPE(bool) unsigned GNUSyntax : 1; /// The number of designators in this initializer expression. @@ -5745,6 +5744,7 @@ class GenericSelectionExpr final /// if and only if the generic selection expression is result-dependent. unsigned NumAssocs : 15; unsigned ResultIndex : 15; // NB: ResultDependentIndex is tied to this width. + LLVM_PREFERRED_TYPE(bool) unsigned IsExprPredicate : 1; enum : unsigned { ResultDependentIndex = 0x7FFF diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 798c98cfcf2d4db36473ed77205c38bc51989f49..24278016431837b71678479fea8c8a9629594782 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -1519,19 +1519,17 @@ public: } }; +enum class CXXConstructionKind { + Complete, + NonVirtualBase, + VirtualBase, + Delegating +}; + /// Represents a call to a C++ constructor. class CXXConstructExpr : public Expr { friend class ASTStmtReader; -public: - enum ConstructionKind { - CK_Complete, - CK_NonVirtualBase, - CK_VirtualBase, - CK_Delegating - }; - -private: /// A pointer to the constructor which will be ultimately called. CXXConstructorDecl *Constructor; @@ -1567,7 +1565,7 @@ protected: CXXConstructorDecl *Ctor, bool Elidable, ArrayRef Args, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, - bool ZeroInitialization, ConstructionKind ConstructKind, + bool ZeroInitialization, CXXConstructionKind ConstructKind, SourceRange ParenOrBraceRange); /// Build an empty C++ construction expression. @@ -1586,7 +1584,7 @@ public: CXXConstructorDecl *Ctor, bool Elidable, ArrayRef Args, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, bool ZeroInitialization, - ConstructionKind ConstructKind, SourceRange ParenOrBraceRange); + CXXConstructionKind ConstructKind, SourceRange ParenOrBraceRange); /// Create an empty C++ construction expression. static CXXConstructExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumArgs); @@ -1640,11 +1638,12 @@ public: /// Determine whether this constructor is actually constructing /// a base class (rather than a complete object). - ConstructionKind getConstructionKind() const { - return static_cast(CXXConstructExprBits.ConstructionKind); + CXXConstructionKind getConstructionKind() const { + return static_cast( + CXXConstructExprBits.ConstructionKind); } - void setConstructionKind(ConstructionKind CK) { - CXXConstructExprBits.ConstructionKind = CK; + void setConstructionKind(CXXConstructionKind CK) { + CXXConstructExprBits.ConstructionKind = llvm::to_underlying(CK); } using arg_iterator = ExprIterator; @@ -1727,10 +1726,12 @@ private: SourceLocation Loc; /// Whether this is the construction of a virtual base. + LLVM_PREFERRED_TYPE(bool) unsigned ConstructsVirtualBase : 1; /// Whether the constructor is inherited from a virtual base class of the /// class that we construct. + LLVM_PREFERRED_TYPE(bool) unsigned InheritedFromVirtualBase : 1; public: @@ -1759,9 +1760,9 @@ public: /// Determine whether this constructor is actually constructing /// a base class (rather than a complete object). bool constructsVBase() const { return ConstructsVirtualBase; } - CXXConstructExpr::ConstructionKind getConstructionKind() const { - return ConstructsVirtualBase ? CXXConstructExpr::CK_VirtualBase - : CXXConstructExpr::CK_NonVirtualBase; + CXXConstructionKind getConstructionKind() const { + return ConstructsVirtualBase ? CXXConstructionKind::VirtualBase + : CXXConstructionKind::NonVirtualBase; } /// Determine whether the inherited constructor is inherited from a @@ -2205,6 +2206,20 @@ public: } }; +enum class CXXNewInitializationStyle { + /// New-expression has no initializer as written. + None, + + /// New-expression has no written initializer, but has an implicit one. + Implicit, + + /// New-expression has a C++98 paren-delimited initializer. + Call, + + /// New-expression has a C++11 list-initializer. + List +}; + /// Represents a new-expression for memory allocation and constructor /// calls, e.g: "new CXXNewExpr(foo)". class CXXNewExpr final @@ -2258,25 +2273,12 @@ class CXXNewExpr final return isParenTypeId(); } -public: - enum InitializationStyle { - /// New-expression has no initializer as written. - NoInit, - - /// New-expression has a C++98 paren-delimited initializer. - CallInit, - - /// New-expression has a C++11 list-initializer. - ListInit - }; - -private: /// Build a c++ new expression. CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, SourceRange TypeIdParens, std::optional ArraySize, - InitializationStyle InitializationStyle, Expr *Initializer, + CXXNewInitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, SourceRange DirectInitRange); @@ -2291,7 +2293,7 @@ public: FunctionDecl *OperatorDelete, bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, SourceRange TypeIdParens, std::optional ArraySize, - InitializationStyle InitializationStyle, Expr *Initializer, + CXXNewInitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, SourceRange DirectInitRange); @@ -2387,15 +2389,21 @@ public: /// Whether this new-expression has any initializer at all. bool hasInitializer() const { - return CXXNewExprBits.StoredInitializationStyle > 0; + switch (getInitializationStyle()) { + case CXXNewInitializationStyle::None: + return false; + case CXXNewInitializationStyle::Implicit: + case CXXNewInitializationStyle::Call: + case CXXNewInitializationStyle::List: + return true; + } + llvm_unreachable("Unknown initializer"); } /// The kind of initializer this new-expression has. - InitializationStyle getInitializationStyle() const { - if (CXXNewExprBits.StoredInitializationStyle == 0) - return NoInit; - return static_cast( - CXXNewExprBits.StoredInitializationStyle - 1); + CXXNewInitializationStyle getInitializationStyle() const { + return static_cast( + CXXNewExprBits.StoredInitializationStyle); } /// The initializer of this new-expression. @@ -2610,6 +2618,7 @@ class CXXPseudoDestructorExpr : public Expr { /// Whether the operator was an arrow ('->'); otherwise, it was a /// period ('.'). + LLVM_PREFERRED_TYPE(bool) bool IsArrow : 1; /// The location of the '.' or '->' operator. @@ -2839,6 +2848,7 @@ public: /// \endcode class ArrayTypeTraitExpr : public Expr { /// The trait. An ArrayTypeTrait enum in MSVC compat unsigned. + LLVM_PREFERRED_TYPE(ArrayTypeTrait) unsigned ATT : 2; /// The value of the type trait. Unspecified if dependent. @@ -2909,9 +2919,11 @@ public: /// \endcode class ExpressionTraitExpr : public Expr { /// The trait. A ExpressionTrait enum in MSVC compatible unsigned. + LLVM_PREFERRED_TYPE(ExpressionTrait) unsigned ET : 31; /// The value of the type trait. Unspecified if dependent. + LLVM_PREFERRED_TYPE(bool) unsigned Value : 1; /// The location of the type trait keyword. diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h index 4f34799bad05021b3c395fa06ef9fb3e150211d6..29913fd84c58b4d001b92131c40793d53f2bee66 100644 --- a/clang/include/clang/AST/ExprConcepts.h +++ b/clang/include/clang/AST/ExprConcepts.h @@ -173,8 +173,11 @@ public: private: const RequirementKind Kind; // FIXME: use RequirementDependence to model dependence? + LLVM_PREFERRED_TYPE(bool) bool Dependent : 1; + LLVM_PREFERRED_TYPE(bool) bool ContainsUnexpandedParameterPack : 1; + LLVM_PREFERRED_TYPE(bool) bool Satisfied : 1; public: struct SubstitutionDiagnostic { diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h index 55bb5fa06b8b7ab4a737ba334ab7edd55ad8de8b..f833916c91aa5443d25400b375b58429895a5145 100644 --- a/clang/include/clang/AST/ExprObjC.h +++ b/clang/include/clang/AST/ExprObjC.h @@ -317,6 +317,7 @@ class ObjCDictionaryLiteral final /// key/value pairs, which provide the locations of the ellipses (if /// any) and number of elements in the expansion (if known). If /// there are no pack expansions, we optimize away this storage. + LLVM_PREFERRED_TYPE(bool) unsigned HasPackExpansions : 1; SourceRange Range; @@ -554,9 +555,11 @@ class ObjCIvarRefExpr : public Expr { SourceLocation OpLoc; // True if this is "X->F", false if this is "X.F". + LLVM_PREFERRED_TYPE(bool) bool IsArrow : 1; // True if ivar reference has no base (self assumed). + LLVM_PREFERRED_TYPE(bool) bool IsFreeIvar : 1; public: @@ -940,6 +943,23 @@ private: class ObjCMessageExpr final : public Expr, private llvm::TrailingObjects { +public: + /// The kind of receiver this message is sending to. + enum ReceiverKind { + /// The receiver is a class. + Class = 0, + + /// The receiver is an object instance. + Instance, + + /// The receiver is a superclass. + SuperClass, + + /// The receiver is the instance of the superclass object. + SuperInstance + }; + +private: /// Stores either the selector that this message is sending /// to (when \c HasMethod is zero) or an \c ObjCMethodDecl pointer /// referring to the method that we type-checked against. @@ -955,6 +975,7 @@ class ObjCMessageExpr final /// ReceiverKind values. /// /// We pad this out to a byte to avoid excessive masking and shifting. + LLVM_PREFERRED_TYPE(ReceiverKind) unsigned Kind : 8; /// Whether we have an actual method prototype in \c @@ -962,18 +983,22 @@ class ObjCMessageExpr final /// /// When non-zero, we have a method declaration; otherwise, we just /// have a selector. + LLVM_PREFERRED_TYPE(bool) unsigned HasMethod : 1; /// Whether this message send is a "delegate init call", /// i.e. a call of an init method on self from within an init method. + LLVM_PREFERRED_TYPE(bool) unsigned IsDelegateInitCall : 1; /// Whether this message send was implicitly generated by /// the implementation rather than explicitly written by the user. + LLVM_PREFERRED_TYPE(bool) unsigned IsImplicit : 1; /// Whether the locations of the selector identifiers are in a /// "standard" position, a enum SelectorLocationsKind. + LLVM_PREFERRED_TYPE(SelectorLocationsKind) unsigned SelLocsKind : 2; /// When the message expression is a send to 'super', this is @@ -1082,21 +1107,6 @@ public: friend class ASTStmtWriter; friend TrailingObjects; - /// The kind of receiver this message is sending to. - enum ReceiverKind { - /// The receiver is a class. - Class = 0, - - /// The receiver is an object instance. - Instance, - - /// The receiver is a superclass. - SuperClass, - - /// The receiver is the instance of the superclass object. - SuperInstance - }; - /// Create a message send to super. /// /// \param Context The ASTContext in which this expression will be created. @@ -1631,6 +1641,7 @@ class ObjCBridgedCastExpr final SourceLocation LParenLoc; SourceLocation BridgeKeywordLoc; + LLVM_PREFERRED_TYPE(ObjCBridgeCastKind) unsigned Kind : 2; public: diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 549f12e87df597a48cc5f9b6cc28e5808c0deffd..924ca189381ba86cb79213bec49ad48e94f210d6 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -2513,6 +2513,89 @@ public: } }; +/// This represents 'fail' clause in the '#pragma omp atomic' +/// directive. +/// +/// \code +/// #pragma omp atomic compare fail +/// \endcode +/// In this example directive '#pragma omp atomic compare' has 'fail' clause. +class OMPFailClause final : public OMPClause { + + // FailParameter is a memory-order-clause. Storing the ClauseKind is + // sufficient for our purpose. + OpenMPClauseKind FailParameter = llvm::omp::Clause::OMPC_unknown; + SourceLocation FailParameterLoc; + SourceLocation LParenLoc; + + friend class OMPClauseReader; + + /// Sets the location of '(' in fail clause. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + + /// Sets the location of memoryOrder clause argument in fail clause. + void setFailParameterLoc(SourceLocation Loc) { FailParameterLoc = Loc; } + + /// Sets the mem_order clause for 'atomic compare fail' directive. + void setFailParameter(OpenMPClauseKind FailParameter) { + this->FailParameter = FailParameter; + assert(checkFailClauseParameter(FailParameter) && + "Invalid fail clause parameter"); + } + +public: + /// Build 'fail' clause. + /// + /// \param StartLoc Starting location of the clause. + /// \param EndLoc Ending location of the clause. + OMPFailClause(SourceLocation StartLoc, SourceLocation EndLoc) + : OMPClause(llvm::omp::OMPC_fail, StartLoc, EndLoc) {} + + OMPFailClause(OpenMPClauseKind FailParameter, SourceLocation FailParameterLoc, + SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc) + : OMPClause(llvm::omp::OMPC_fail, StartLoc, EndLoc), + FailParameterLoc(FailParameterLoc), LParenLoc(LParenLoc) { + + setFailParameter(FailParameter); + } + + /// Build an empty clause. + OMPFailClause() + : OMPClause(llvm::omp::OMPC_fail, SourceLocation(), SourceLocation()) {} + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_fail; + } + + /// Gets the location of '(' (for the parameter) in fail clause. + SourceLocation getLParenLoc() const { + return LParenLoc; + } + + /// Gets the location of Fail Parameter (type memory-order-clause) in + /// fail clause. + SourceLocation getFailParameterLoc() const { return FailParameterLoc; } + + /// Gets the parameter (type memory-order-clause) in Fail clause. + OpenMPClauseKind getFailParameter() const { return FailParameter; } +}; + /// This represents clause 'private' in the '#pragma omp ...' directives. /// /// \code @@ -7776,10 +7859,10 @@ public: /// \param MLoc Location of the modifier OMPOrderClause(OpenMPOrderClauseKind A, SourceLocation ALoc, SourceLocation StartLoc, SourceLocation LParenLoc, - SourceLocation EndLoc, OpenMPOrderClauseModifier M, + SourceLocation EndLoc, OpenMPOrderClauseModifier Modifier, SourceLocation MLoc) : OMPClause(llvm::omp::OMPC_order, StartLoc, EndLoc), - LParenLoc(LParenLoc), Kind(A), KindKwLoc(ALoc), Modifier(M), + LParenLoc(LParenLoc), Kind(A), KindKwLoc(ALoc), Modifier(Modifier), ModifierKwLoc(MLoc) {} /// Build an empty clause. diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index cee3cce7729c30f0f1b4c0d8785044b4074fcc7a..da276e26049b00a1c801b412ece3366378c64a53 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -106,6 +106,7 @@ struct PrintingPolicy { /// declaration for "x", so that we will print "int *x"; it will be /// \c true when we print "y", so that we suppress printing the /// "const int" type specifier and instead only print the "*y". + LLVM_PREFERRED_TYPE(bool) unsigned SuppressSpecifiers : 1; /// Whether type printing should skip printing the tag keyword. @@ -116,6 +117,7 @@ struct PrintingPolicy { /// \code /// struct Geometry::Point; /// \endcode + LLVM_PREFERRED_TYPE(bool) unsigned SuppressTagKeyword : 1; /// When true, include the body of a tag definition. @@ -126,22 +128,27 @@ struct PrintingPolicy { /// \code /// typedef struct { int x, y; } Point; /// \endcode + LLVM_PREFERRED_TYPE(bool) unsigned IncludeTagDefinition : 1; /// Suppresses printing of scope specifiers. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressScope : 1; /// Suppress printing parts of scope specifiers that are never /// written, e.g., for anonymous namespaces. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressUnwrittenScope : 1; /// Suppress printing parts of scope specifiers that correspond /// to inline namespaces, where the name is unambiguous with the specifier /// removed. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressInlineNamespace : 1; /// Ignore qualifiers and tag keywords as specified by elaborated type sugar, /// instead letting the underlying type print as normal. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressElaboration : 1; /// Suppress printing of variable initializers. @@ -155,6 +162,7 @@ struct PrintingPolicy { /// /// SuppressInitializers will be true when printing "auto x", so that the /// internal initializer constructed for x will not be printed. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressInitializers : 1; /// Whether we should print the sizes of constant array expressions as written @@ -173,53 +181,67 @@ struct PrintingPolicy { /// int a[104]; /// char a[9] = "A string"; /// \endcode + LLVM_PREFERRED_TYPE(bool) unsigned ConstantArraySizeAsWritten : 1; /// When printing an anonymous tag name, also print the location of that /// entity (e.g., "enum "). Otherwise, just prints /// "(anonymous)" for the name. + LLVM_PREFERRED_TYPE(bool) unsigned AnonymousTagLocations : 1; /// When true, suppress printing of the __strong lifetime qualifier in ARC. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressStrongLifetime : 1; /// When true, suppress printing of lifetime qualifier in ARC. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressLifetimeQualifiers : 1; /// When true, suppresses printing template arguments in names of C++ /// constructors. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressTemplateArgsInCXXConstructors : 1; /// When true, attempt to suppress template arguments that match the default /// argument for the parameter. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressDefaultTemplateArgs : 1; /// Whether we can use 'bool' rather than '_Bool' (even if the language /// doesn't actually have 'bool', because, e.g., it is defined as a macro). + LLVM_PREFERRED_TYPE(bool) unsigned Bool : 1; /// Whether we should use 'nullptr' rather than '0' as a null pointer /// constant. + LLVM_PREFERRED_TYPE(bool) unsigned Nullptr : 1; /// Whether 'nullptr_t' is in namespace 'std' or not. + LLVM_PREFERRED_TYPE(bool) unsigned NullptrTypeInNamespace : 1; /// Whether we can use 'restrict' rather than '__restrict'. + LLVM_PREFERRED_TYPE(bool) unsigned Restrict : 1; /// Whether we can use 'alignof' rather than '__alignof'. + LLVM_PREFERRED_TYPE(bool) unsigned Alignof : 1; /// Whether we can use '_Alignof' rather than '__alignof'. + LLVM_PREFERRED_TYPE(bool) unsigned UnderscoreAlignof : 1; /// Whether we should use '(void)' rather than '()' for a function prototype /// with zero parameters. + LLVM_PREFERRED_TYPE(bool) unsigned UseVoidForZeroParams : 1; /// Whether nested templates must be closed like 'a\ \>' rather than /// 'a\\>'. + LLVM_PREFERRED_TYPE(bool) unsigned SplitTemplateClosers : 1; /// Provide a 'terse' output. @@ -227,27 +249,33 @@ struct PrintingPolicy { /// For example, in this mode we don't print function bodies, class members, /// declarations inside namespaces etc. Effectively, this should print /// only the requested declaration. + LLVM_PREFERRED_TYPE(bool) unsigned TerseOutput : 1; /// When true, do certain refinement needed for producing proper declaration /// tag; such as, do not print attributes attached to the declaration. /// + LLVM_PREFERRED_TYPE(bool) unsigned PolishForDeclaration : 1; /// When true, print the half-precision floating-point type as 'half' /// instead of '__fp16' + LLVM_PREFERRED_TYPE(bool) unsigned Half : 1; /// When true, print the built-in wchar_t type as __wchar_t. For use in /// Microsoft mode when wchar_t is not available. + LLVM_PREFERRED_TYPE(bool) unsigned MSWChar : 1; /// When true, include newlines after statements like "break", etc. + LLVM_PREFERRED_TYPE(bool) unsigned IncludeNewlines : 1; /// Use whitespace and punctuation like MSVC does. In particular, this prints /// anonymous namespaces as `anonymous namespace' and does not insert spaces /// after template arguments. + LLVM_PREFERRED_TYPE(bool) unsigned MSVCFormatting : 1; /// Whether we should print the constant expressions as written in the @@ -266,42 +294,52 @@ struct PrintingPolicy { /// 0x10 /// 2.5e3 /// \endcode + LLVM_PREFERRED_TYPE(bool) unsigned ConstantsAsWritten : 1; /// When true, don't print the implicit 'self' or 'this' expressions. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressImplicitBase : 1; /// When true, print the fully qualified name of function declarations. /// This is the opposite of SuppressScope and thus overrules it. + LLVM_PREFERRED_TYPE(bool) unsigned FullyQualifiedName : 1; /// Whether to print types as written or canonically. + LLVM_PREFERRED_TYPE(bool) unsigned PrintCanonicalTypes : 1; /// Whether to print an InjectedClassNameType with template arguments or as /// written. When a template argument is unnamed, printing it results in /// invalid C++ code. + LLVM_PREFERRED_TYPE(bool) unsigned PrintInjectedClassNameWithArguments : 1; /// Whether to use C++ template preferred_name attributes when printing /// templates. + LLVM_PREFERRED_TYPE(bool) unsigned UsePreferredNames : 1; /// Whether to use type suffixes (eg: 1U) on integral non-type template /// parameters. + LLVM_PREFERRED_TYPE(bool) unsigned AlwaysIncludeTypeForTemplateArgument : 1; /// Whether to strip underscores when printing reserved parameter names. /// e.g. std::vector becomes std::vector. /// This only affects parameter names, and so describes a compatible API. + LLVM_PREFERRED_TYPE(bool) unsigned CleanUglifiedParameters : 1; /// Whether to print the entire array initializers, especially on non-type /// template parameters, no matter how many elements there are. + LLVM_PREFERRED_TYPE(bool) unsigned EntireContentsOfLargeArray : 1; /// Whether to print enumerator non-type template parameters with a matching /// enumerator name or via cast of an integer. + LLVM_PREFERRED_TYPE(bool) unsigned UseEnumerators : 1; /// Callbacks to use to allow the behavior of printing to be customized. diff --git a/clang/include/clang/AST/RawCommentList.h b/clang/include/clang/AST/RawCommentList.h index 2f44a77d4503d31a13bc2ad15d57fb22acdcca40..53aae24fa7bbc1b66159162923568bbf141f055b 100644 --- a/clang/include/clang/AST/RawCommentList.h +++ b/clang/include/clang/AST/RawCommentList.h @@ -178,6 +178,7 @@ private: mutable bool RawTextValid : 1; ///< True if RawText is valid mutable bool BriefTextValid : 1; ///< True if BriefText is valid + LLVM_PREFERRED_TYPE(CommentKind) unsigned Kind : 3; /// True if comment is attached to a declaration in ASTContext. diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 53bc15e1b19f6685f73d7a24ae6600761c1d9e6c..c501801b95bd9558b72a01951a175a511d6f8cfe 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3398,6 +3398,11 @@ bool RecursiveASTVisitor::VisitOMPCompareClause(OMPCompareClause *) { return true; } +template +bool RecursiveASTVisitor::VisitOMPFailClause(OMPFailClause *) { + return true; +} + template bool RecursiveASTVisitor::VisitOMPSeqCstClause(OMPSeqCstClause *) { return true; diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 69a764480ed568f8d6a3a6e0e77396e676a2a0ba..da7b37ce0e1211f87bb3639c0fb16708d2eeacfd 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -13,15 +13,20 @@ #ifndef LLVM_CLANG_AST_STMT_H #define LLVM_CLANG_AST_STMT_H +#include "clang/AST/APValue.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/DependenceFlags.h" +#include "clang/AST/OperationKinds.h" #include "clang/AST/StmtIterator.h" #include "clang/Basic/CapturedStmt.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/Lambda.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/OperatorKinds.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" +#include "clang/Basic/TypeTraits.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" @@ -62,6 +67,13 @@ class SourceManager; class StringLiteral; class Token; class VarDecl; +enum class CharacterLiteralKind; +enum class ConstantResultStorageKind; +enum class CXXConstructionKind; +enum class CXXNewInitializationStyle; +enum class PredefinedIdentKind; +enum class SourceLocIdentKind; +enum class StringLiteralKind; //===----------------------------------------------------------------------===// // AST classes for statements. @@ -103,6 +115,7 @@ protected: friend class Stmt; /// The statement class. + LLVM_PREFERRED_TYPE(StmtClass) unsigned sClass : 8; }; enum { NumStmtBits = 8 }; @@ -112,6 +125,7 @@ protected: friend class ASTStmtWriter; friend class NullStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// True if the null statement was preceded by an empty macro, e.g: @@ -119,6 +133,7 @@ protected: /// #define CALL(x) /// CALL(0); /// @endcode + LLVM_PREFERRED_TYPE(bool) unsigned HasLeadingEmptyMacro : 1; /// The location of the semi-colon. @@ -129,10 +144,12 @@ protected: friend class ASTStmtReader; friend class CompoundStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// True if the compound statement has one or more pragmas that set some /// floating-point features. + LLVM_PREFERRED_TYPE(bool) unsigned HasFPFeatures : 1; unsigned NumStmts; @@ -141,6 +158,7 @@ protected: class LabelStmtBitfields { friend class LabelStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; SourceLocation IdentLoc; @@ -150,6 +168,7 @@ protected: friend class ASTStmtReader; friend class AttributedStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// Number of attributes. @@ -163,18 +182,23 @@ protected: friend class ASTStmtReader; friend class IfStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// Whether this is a constexpr if, or a consteval if, or neither. + LLVM_PREFERRED_TYPE(IfStatementKind) unsigned Kind : 3; /// True if this if statement has storage for an else statement. + LLVM_PREFERRED_TYPE(bool) unsigned HasElse : 1; /// True if this if statement has storage for a variable declaration. + LLVM_PREFERRED_TYPE(bool) unsigned HasVar : 1; /// True if this if statement has storage for an init statement. + LLVM_PREFERRED_TYPE(bool) unsigned HasInit : 1; /// The location of the "if". @@ -184,17 +208,21 @@ protected: class SwitchStmtBitfields { friend class SwitchStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// True if the SwitchStmt has storage for an init statement. + LLVM_PREFERRED_TYPE(bool) unsigned HasInit : 1; /// True if the SwitchStmt has storage for a condition variable. + LLVM_PREFERRED_TYPE(bool) unsigned HasVar : 1; /// If the SwitchStmt is a switch on an enum value, records whether all /// the enum values were covered by CaseStmts. The coverage information /// value is meant to be a hint for possible clients. + LLVM_PREFERRED_TYPE(bool) unsigned AllEnumCasesCovered : 1; /// The location of the "switch". @@ -205,9 +233,11 @@ protected: friend class ASTStmtReader; friend class WhileStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// True if the WhileStmt has storage for a condition variable. + LLVM_PREFERRED_TYPE(bool) unsigned HasVar : 1; /// The location of the "while". @@ -217,6 +247,7 @@ protected: class DoStmtBitfields { friend class DoStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// The location of the "do". @@ -226,6 +257,7 @@ protected: class ForStmtBitfields { friend class ForStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// The location of the "for". @@ -236,6 +268,7 @@ protected: friend class GotoStmt; friend class IndirectGotoStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// The location of the "goto". @@ -245,6 +278,7 @@ protected: class ContinueStmtBitfields { friend class ContinueStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// The location of the "continue". @@ -254,6 +288,7 @@ protected: class BreakStmtBitfields { friend class BreakStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// The location of the "break". @@ -263,9 +298,11 @@ protected: class ReturnStmtBitfields { friend class ReturnStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// True if this ReturnStmt has storage for an NRVO candidate. + LLVM_PREFERRED_TYPE(bool) unsigned HasNRVOCandidate : 1; /// The location of the "return". @@ -276,10 +313,12 @@ protected: friend class SwitchCase; friend class CaseStmt; + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; /// Used by CaseStmt to store whether it is a case statement /// of the form case LHS ... RHS (a GNU extension). + LLVM_PREFERRED_TYPE(bool) unsigned CaseStmtIsGNURange : 1; /// The location of the "case" or "default" keyword. @@ -312,11 +351,15 @@ protected: friend class PseudoObjectExpr; // ctor friend class ShuffleVectorExpr; // ctor + LLVM_PREFERRED_TYPE(StmtBitfields) unsigned : NumStmtBits; + LLVM_PREFERRED_TYPE(ExprValueKind) unsigned ValueKind : 2; + LLVM_PREFERRED_TYPE(ExprObjectKind) unsigned ObjectKind : 3; - unsigned /*ExprDependence*/ Dependent : llvm::BitWidth; + LLVM_PREFERRED_TYPE(ExprDependence) + unsigned Dependent : llvm::BitWidth; }; enum { NumExprBits = NumStmtBits + 5 + llvm::BitWidth }; @@ -325,28 +368,35 @@ protected: friend class ASTStmtWriter; friend class ConstantExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The kind of result that is tail-allocated. + LLVM_PREFERRED_TYPE(ConstantResultStorageKind) unsigned ResultKind : 2; - /// The kind of Result as defined by APValue::Kind. + /// The kind of Result as defined by APValue::ValueKind. + LLVM_PREFERRED_TYPE(APValue::ValueKind) unsigned APValueKind : 4; - /// When ResultKind == RSK_Int64, true if the tail-allocated integer is - /// unsigned. + /// When ResultKind == ConstantResultStorageKind::Int64, true if the + /// tail-allocated integer is unsigned. + LLVM_PREFERRED_TYPE(bool) unsigned IsUnsigned : 1; - /// When ResultKind == RSK_Int64. the BitWidth of the tail-allocated - /// integer. 7 bits because it is the minimal number of bits to represent a - /// value from 0 to 64 (the size of the tail-allocated integer). + /// When ResultKind == ConstantResultStorageKind::Int64. the BitWidth of the + /// tail-allocated integer. 7 bits because it is the minimal number of bits + /// to represent a value from 0 to 64 (the size of the tail-allocated + /// integer). unsigned BitWidth : 7; - /// When ResultKind == RSK_APValue, true if the ASTContext will cleanup the - /// tail-allocated APValue. + /// When ResultKind == ConstantResultStorageKind::APValue, true if the + /// ASTContext will cleanup the tail-allocated APValue. + LLVM_PREFERRED_TYPE(bool) unsigned HasCleanup : 1; /// True if this ConstantExpr was created for immediate invocation. + LLVM_PREFERRED_TYPE(bool) unsigned IsImmediateInvocation : 1; }; @@ -354,18 +404,20 @@ protected: friend class ASTStmtReader; friend class PredefinedExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; - /// The kind of this PredefinedExpr. One of the enumeration values - /// in PredefinedExpr::IdentKind. + LLVM_PREFERRED_TYPE(PredefinedIdentKind) unsigned Kind : 4; /// True if this PredefinedExpr has a trailing "StringLiteral *" /// for the predefined identifier. + LLVM_PREFERRED_TYPE(bool) unsigned HasFunctionName : 1; /// True if this PredefinedExpr should be treated as a StringLiteral (for /// MSVC compatibility). + LLVM_PREFERRED_TYPE(bool) unsigned IsTransparent : 1; /// The location of this PredefinedExpr. @@ -376,15 +428,24 @@ protected: friend class ASTStmtReader; // deserialization friend class DeclRefExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(bool) unsigned HasQualifier : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasTemplateKWAndArgsInfo : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasFoundDecl : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HadMultipleCandidates : 1; + LLVM_PREFERRED_TYPE(bool) unsigned RefersToEnclosingVariableOrCapture : 1; + LLVM_PREFERRED_TYPE(bool) unsigned CapturedByCopyInLambdaWithExplicitObjectParameter : 1; + LLVM_PREFERRED_TYPE(NonOdrUseReason) unsigned NonOdrUseReason : 2; + LLVM_PREFERRED_TYPE(bool) unsigned IsImmediateEscalating : 1; /// The location of the declaration name itself. @@ -395,12 +456,15 @@ protected: class FloatingLiteralBitfields { friend class FloatingLiteral; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; static_assert( llvm::APFloat::S_MaxSemantics < 16, "Too many Semantics enum values to fit in bitfield of size 4"); + LLVM_PREFERRED_TYPE(llvm::APFloat::Semantics) unsigned Semantics : 4; // Provides semantics for APFloat construction + LLVM_PREFERRED_TYPE(bool) unsigned IsExact : 1; }; @@ -408,10 +472,12 @@ protected: friend class ASTStmtReader; friend class StringLiteral; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The kind of this string literal. /// One of the enumeration values of StringLiteral::StringKind. + LLVM_PREFERRED_TYPE(StringLiteralKind) unsigned Kind : 3; /// The width of a single character in bytes. Only values of 1, 2, @@ -419,6 +485,7 @@ protected: /// the target + string kind to the appropriate CharByteWidth. unsigned CharByteWidth : 3; + LLVM_PREFERRED_TYPE(bool) unsigned IsPascal : 1; /// The number of concatenated token this string is made of. @@ -429,22 +496,28 @@ protected: class CharacterLiteralBitfields { friend class CharacterLiteral; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(CharacterLiteralKind) unsigned Kind : 3; }; class UnaryOperatorBitfields { friend class UnaryOperator; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(UnaryOperatorKind) unsigned Opc : 5; + LLVM_PREFERRED_TYPE(bool) unsigned CanOverflow : 1; // /// This is only meaningful for operations on floating point /// types when additional values need to be in trailing storage. /// It is 0 otherwise. + LLVM_PREFERRED_TYPE(bool) unsigned HasFPFeatures : 1; SourceLocation Loc; @@ -453,9 +526,12 @@ protected: class UnaryExprOrTypeTraitExprBitfields { friend class UnaryExprOrTypeTraitExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(UnaryExprOrTypeTrait) unsigned Kind : 3; + LLVM_PREFERRED_TYPE(bool) unsigned IsType : 1; // true if operand is a type, false if an expression. }; @@ -463,6 +539,7 @@ protected: friend class ArraySubscriptExpr; friend class MatrixSubscriptExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; SourceLocation RBracketLoc; @@ -471,14 +548,17 @@ protected: class CallExprBitfields { friend class CallExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; unsigned NumPreArgs : 1; /// True if the callee of the call expression was found using ADL. + LLVM_PREFERRED_TYPE(bool) unsigned UsesADL : 1; /// True if the call expression has some floating-point features. + LLVM_PREFERRED_TYPE(bool) unsigned HasFPFeatures : 1; /// Padding used to align OffsetToTrailingObjects to a byte multiple. @@ -495,15 +575,18 @@ protected: friend class ASTStmtReader; friend class MemberExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// IsArrow - True if this is "X->F", false if this is "X.F". + LLVM_PREFERRED_TYPE(bool) unsigned IsArrow : 1; /// True if this member expression used a nested-name-specifier to /// refer to the member, e.g., "x->Base::f", or found its member via /// a using declaration. When true, a MemberExprNameQualifier /// structure is allocated immediately after the MemberExpr. + LLVM_PREFERRED_TYPE(bool) unsigned HasQualifierOrFoundDecl : 1; /// True if this member expression specified a template keyword @@ -511,15 +594,18 @@ protected: /// x->template f, x->template f. /// When true, an ASTTemplateKWAndArgsInfo structure and its /// TemplateArguments (if any) are present. + LLVM_PREFERRED_TYPE(bool) unsigned HasTemplateKWAndArgsInfo : 1; /// True if this member expression refers to a method that /// was resolved from an overloaded set having size greater than 1. + LLVM_PREFERRED_TYPE(bool) unsigned HadMultipleCandidates : 1; /// Value of type NonOdrUseReason indicating why this MemberExpr does /// not constitute an odr-use of the named declaration. Meaningful only /// when naming a static member. + LLVM_PREFERRED_TYPE(NonOdrUseReason) unsigned NonOdrUseReason : 2; /// This is the location of the -> or . in the expression. @@ -530,12 +616,16 @@ protected: friend class CastExpr; friend class ImplicitCastExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(CastKind) unsigned Kind : 7; + LLVM_PREFERRED_TYPE(bool) unsigned PartOfExplicitCast : 1; // Only set for ImplicitCastExpr. /// True if the call expression has some floating-point features. + LLVM_PREFERRED_TYPE(bool) unsigned HasFPFeatures : 1; /// The number of CXXBaseSpecifiers in the cast. 14 bits would be enough @@ -546,13 +636,16 @@ protected: class BinaryOperatorBitfields { friend class BinaryOperator; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(BinaryOperatorKind) unsigned Opc : 6; /// This is only meaningful for operations on floating point /// types when additional values need to be in trailing storage. /// It is 0 otherwise. + LLVM_PREFERRED_TYPE(bool) unsigned HasFPFeatures : 1; SourceLocation OpLoc; @@ -561,10 +654,12 @@ protected: class InitListExprBitfields { friend class InitListExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether this initializer list originally had a GNU array-range /// designator in it. This is a temporary marker used by CodeGen. + LLVM_PREFERRED_TYPE(bool) unsigned HadArrayRangeDesignator : 1; }; @@ -572,6 +667,7 @@ protected: friend class ASTStmtReader; friend class ParenListExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The number of expressions in the paren list. @@ -582,6 +678,7 @@ protected: friend class ASTStmtReader; friend class GenericSelectionExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The location of the "_Generic". @@ -592,6 +689,7 @@ protected: friend class ASTStmtReader; // deserialization friend class PseudoObjectExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; unsigned NumSubExprs : 16; @@ -602,10 +700,12 @@ protected: friend class ASTStmtReader; friend class SourceLocExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The kind of source location builtin represented by the SourceLocExpr. /// Ex. __builtin_LINE, __builtin_FUNCTION, etc. + LLVM_PREFERRED_TYPE(SourceLocIdentKind) unsigned Kind : 3; }; @@ -613,6 +713,7 @@ protected: friend class ASTStmtReader; friend class StmtExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The number of levels of template parameters enclosing this statement @@ -627,10 +728,12 @@ protected: friend class ASTStmtReader; friend class CXXOperatorCallExpr; + LLVM_PREFERRED_TYPE(CallExprBitfields) unsigned : NumCallExprBits; /// The kind of this overloaded operator. One of the enumerator /// value of OverloadedOperatorKind. + LLVM_PREFERRED_TYPE(OverloadedOperatorKind) unsigned OperatorKind : 6; }; @@ -638,17 +741,21 @@ protected: friend class ASTStmtReader; friend class CXXRewrittenBinaryOperator; + LLVM_PREFERRED_TYPE(CallExprBitfields) unsigned : NumCallExprBits; + LLVM_PREFERRED_TYPE(bool) unsigned IsReversed : 1; }; class CXXBoolLiteralExprBitfields { friend class CXXBoolLiteralExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The value of the boolean literal. + LLVM_PREFERRED_TYPE(bool) unsigned Value : 1; /// The location of the boolean literal. @@ -658,6 +765,7 @@ protected: class CXXNullPtrLiteralExprBitfields { friend class CXXNullPtrLiteralExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The location of the null pointer literal. @@ -667,9 +775,11 @@ protected: class CXXThisExprBitfields { friend class CXXThisExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether this is an implicit "this". + LLVM_PREFERRED_TYPE(bool) unsigned IsImplicit : 1; /// The location of the "this". @@ -680,9 +790,11 @@ protected: friend class ASTStmtReader; friend class CXXThrowExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether the thrown variable (if any) is in scope. + LLVM_PREFERRED_TYPE(bool) unsigned IsThrownVariableInScope : 1; /// The location of the "throw". @@ -693,9 +805,11 @@ protected: friend class ASTStmtReader; friend class CXXDefaultArgExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether this CXXDefaultArgExpr rewrote its argument and stores a copy. + LLVM_PREFERRED_TYPE(bool) unsigned HasRewrittenInit : 1; /// The location where the default argument expression was used. @@ -706,10 +820,12 @@ protected: friend class ASTStmtReader; friend class CXXDefaultInitExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether this CXXDefaultInitExprBitfields rewrote its argument and stores /// a copy. + LLVM_PREFERRED_TYPE(bool) unsigned HasRewrittenInit : 1; /// The location where the default initializer expression was used. @@ -720,6 +836,7 @@ protected: friend class ASTStmtReader; friend class CXXScalarValueInitExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; SourceLocation RParenLoc; @@ -730,28 +847,35 @@ protected: friend class ASTStmtWriter; friend class CXXNewExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Was the usage ::new, i.e. is the global new to be used? + LLVM_PREFERRED_TYPE(bool) unsigned IsGlobalNew : 1; /// Do we allocate an array? If so, the first trailing "Stmt *" is the /// size expression. + LLVM_PREFERRED_TYPE(bool) unsigned IsArray : 1; /// Should the alignment be passed to the allocation function? + LLVM_PREFERRED_TYPE(bool) unsigned ShouldPassAlignment : 1; /// If this is an array allocation, does the usual deallocation /// function for the allocated type want to know the allocated size? + LLVM_PREFERRED_TYPE(bool) unsigned UsualArrayDeleteWantsSize : 1; /// What kind of initializer do we have? Could be none, parens, or braces. /// In storage, we distinguish between "none, and no initializer expr", and /// "none, but an implicit initializer expr". + LLVM_PREFERRED_TYPE(CXXNewInitializationStyle) unsigned StoredInitializationStyle : 2; /// True if the allocated type was expressed as a parenthesized type-id. + LLVM_PREFERRED_TYPE(bool) unsigned IsParenTypeId : 1; /// The number of placement new arguments. @@ -762,21 +886,26 @@ protected: friend class ASTStmtReader; friend class CXXDeleteExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Is this a forced global delete, i.e. "::delete"? + LLVM_PREFERRED_TYPE(bool) unsigned GlobalDelete : 1; /// Is this the array form of delete, i.e. "delete[]"? + LLVM_PREFERRED_TYPE(bool) unsigned ArrayForm : 1; /// ArrayFormAsWritten can be different from ArrayForm if 'delete' is /// applied to pointer-to-array type (ArrayFormAsWritten will be false /// while ArrayForm will be true). + LLVM_PREFERRED_TYPE(bool) unsigned ArrayFormAsWritten : 1; /// Does the usual deallocation function for the element type require /// a size_t argument? + LLVM_PREFERRED_TYPE(bool) unsigned UsualArrayDeleteWantsSize : 1; /// Location of the expression. @@ -788,13 +917,16 @@ protected: friend class ASTStmtWriter; friend class TypeTraitExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The kind of type trait, which is a value of a TypeTrait enumerator. + LLVM_PREFERRED_TYPE(TypeTrait) unsigned Kind : 8; /// If this expression is not value-dependent, this indicates whether /// the trait evaluated true or false. + LLVM_PREFERRED_TYPE(bool) unsigned Value : 1; /// The number of arguments to this type trait. According to [implimits] @@ -808,10 +940,12 @@ protected: friend class ASTStmtWriter; friend class DependentScopeDeclRefExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether the name includes info for explicit template /// keyword and arguments. + LLVM_PREFERRED_TYPE(bool) unsigned HasTemplateKWAndArgsInfo : 1; }; @@ -819,14 +953,22 @@ protected: friend class ASTStmtReader; friend class CXXConstructExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(bool) unsigned Elidable : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HadMultipleCandidates : 1; + LLVM_PREFERRED_TYPE(bool) unsigned ListInitialization : 1; + LLVM_PREFERRED_TYPE(bool) unsigned StdInitListInitialization : 1; + LLVM_PREFERRED_TYPE(bool) unsigned ZeroInitialization : 1; + LLVM_PREFERRED_TYPE(CXXConstructionKind) unsigned ConstructionKind : 3; + LLVM_PREFERRED_TYPE(bool) unsigned IsImmediateEscalating : 1; SourceLocation Loc; @@ -836,9 +978,11 @@ protected: friend class ASTStmtReader; // deserialization friend class ExprWithCleanups; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; // When false, it must not have side effects. + LLVM_PREFERRED_TYPE(bool) unsigned CleanupsHaveSideEffects : 1; unsigned NumObjects : 32 - 1 - NumExprBits; @@ -848,6 +992,7 @@ protected: friend class ASTStmtReader; friend class CXXUnresolvedConstructExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The number of arguments used to construct the type. @@ -858,18 +1003,22 @@ protected: friend class ASTStmtReader; friend class CXXDependentScopeMemberExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether this member expression used the '->' operator or /// the '.' operator. + LLVM_PREFERRED_TYPE(bool) unsigned IsArrow : 1; /// Whether this member expression has info for explicit template /// keyword and arguments. + LLVM_PREFERRED_TYPE(bool) unsigned HasTemplateKWAndArgsInfo : 1; /// See getFirstQualifierFoundInScope() and the comment listing /// the trailing objects. + LLVM_PREFERRED_TYPE(bool) unsigned HasFirstQualifierFoundInScope : 1; /// The location of the '->' or '.' operator. @@ -880,10 +1029,12 @@ protected: friend class ASTStmtReader; friend class OverloadExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// Whether the name includes info for explicit template /// keyword and arguments. + LLVM_PREFERRED_TYPE(bool) unsigned HasTemplateKWAndArgsInfo : 1; /// Padding used by the derived classes to store various bits. If you @@ -900,14 +1051,17 @@ protected: friend class ASTStmtReader; friend class UnresolvedLookupExpr; + LLVM_PREFERRED_TYPE(OverloadExprBitfields) unsigned : NumOverloadExprBits; /// True if these lookup results should be extended by /// argument-dependent lookup if this is the operand of a function call. + LLVM_PREFERRED_TYPE(bool) unsigned RequiresADL : 1; /// True if these lookup results are overloaded. This is pretty trivially /// rederivable if we urgently need to kill this field. + LLVM_PREFERRED_TYPE(bool) unsigned Overloaded : 1; }; static_assert(sizeof(UnresolvedLookupExprBitfields) <= 4, @@ -918,13 +1072,16 @@ protected: friend class ASTStmtReader; friend class UnresolvedMemberExpr; + LLVM_PREFERRED_TYPE(OverloadExprBitfields) unsigned : NumOverloadExprBits; /// Whether this member expression used the '->' operator or /// the '.' operator. + LLVM_PREFERRED_TYPE(bool) unsigned IsArrow : 1; /// Whether the lookup results contain an unresolved using declaration. + LLVM_PREFERRED_TYPE(bool) unsigned HasUnresolvedUsing : 1; }; static_assert(sizeof(UnresolvedMemberExprBitfields) <= 4, @@ -935,8 +1092,10 @@ protected: friend class ASTStmtReader; friend class CXXNoexceptExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(bool) unsigned Value : 1; }; @@ -944,6 +1103,7 @@ protected: friend class ASTStmtReader; friend class SubstNonTypeTemplateParmExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The location of the non-type template parameter reference. @@ -955,17 +1115,21 @@ protected: friend class ASTStmtWriter; friend class LambdaExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The default capture kind, which is a value of type /// LambdaCaptureDefault. + LLVM_PREFERRED_TYPE(LambdaCaptureDefault) unsigned CaptureDefault : 2; /// Whether this lambda had an explicit parameter list vs. an /// implicit (and empty) parameter list. + LLVM_PREFERRED_TYPE(bool) unsigned ExplicitParams : 1; /// Whether this lambda had the result type explicitly specified. + LLVM_PREFERRED_TYPE(bool) unsigned ExplicitResultType : 1; /// The number of captures. @@ -977,8 +1141,10 @@ protected: friend class ASTStmtWriter; friend class RequiresExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(bool) unsigned IsSatisfied : 1; SourceLocation RequiresKWLoc; }; @@ -988,8 +1154,10 @@ protected: class CoawaitExprBitfields { friend class CoawaitExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(bool) unsigned IsImplicit : 1; }; @@ -998,8 +1166,10 @@ protected: class ObjCIndirectCopyRestoreExprBitfields { friend class ObjCIndirectCopyRestoreExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; + LLVM_PREFERRED_TYPE(bool) unsigned ShouldCopy : 1; }; @@ -1009,10 +1179,12 @@ protected: friend class ASTStmtReader; friend class OpaqueValueExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) unsigned : NumExprBits; /// The OVE is a unique semantic reference to its source expression if this /// bit is set to true. + LLVM_PREFERRED_TYPE(bool) unsigned IsUnique : 1; SourceLocation Loc; diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index 2725747e051e728e7e73eaa2b51dd89d4132a72e..62164339153573cb5b98d2860246fff14727cc43 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -994,7 +994,7 @@ public: unsigned getNumAssociatedLoops() const { return getLoopsNumber(); } /// Return the number of loops generated by this loop transformation. - unsigned getNumGeneratedLoops() { return NumGeneratedLoops; } + unsigned getNumGeneratedLoops() const { return NumGeneratedLoops; } /// Get the de-sugared statements after the loop transformation. /// diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index 8e6b4d8197406428047500efe04fd8368434670c..b7cd71f17c9442c931c0e441c36c39bfaf352c75 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -103,18 +103,23 @@ private: /// The kind of template argument we're storing. struct DA { + LLVM_PREFERRED_TYPE(ArgKind) unsigned Kind : 31; + LLVM_PREFERRED_TYPE(bool) unsigned IsDefaulted : 1; void *QT; ValueDecl *D; }; struct I { + LLVM_PREFERRED_TYPE(ArgKind) unsigned Kind : 31; + LLVM_PREFERRED_TYPE(bool) unsigned IsDefaulted : 1; // We store a decomposed APSInt with the data allocated by ASTContext if // BitWidth > 64. The memory may be shared between multiple // TemplateArgument instances. unsigned BitWidth : 31; + LLVM_PREFERRED_TYPE(bool) unsigned IsUnsigned : 1; union { /// Used to store the <= 64 bits integer value. @@ -126,19 +131,25 @@ private: void *Type; }; struct A { + LLVM_PREFERRED_TYPE(ArgKind) unsigned Kind : 31; + LLVM_PREFERRED_TYPE(bool) unsigned IsDefaulted : 1; unsigned NumArgs; const TemplateArgument *Args; }; struct TA { + LLVM_PREFERRED_TYPE(ArgKind) unsigned Kind : 31; + LLVM_PREFERRED_TYPE(bool) unsigned IsDefaulted : 1; unsigned NumExpansions; void *Name; }; struct TV { + LLVM_PREFERRED_TYPE(ArgKind) unsigned Kind : 31; + LLVM_PREFERRED_TYPE(bool) unsigned IsDefaulted : 1; uintptr_t V; }; diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index d56361b50059637e0c18e32a4bdffc7f16b84f13..b7732e54ba1079f2d037e451f45bbe2e9dcec725 100644 --- a/clang/include/clang/AST/TemplateName.h +++ b/clang/include/clang/AST/TemplateName.h @@ -55,7 +55,7 @@ protected: }; struct BitsTag { - /// A Kind. + LLVM_PREFERRED_TYPE(Kind) unsigned Kind : 2; // The template parameter index. diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 42e9b8f94b4e7b649ee3bdd6e0dd3b925abf3f7d..6c147eb8f64062334c969b7c0006a058676184fd 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1672,7 +1672,7 @@ protected: /// Storage class qualifiers from declarations like /// 'int X[static restrict 4]'. For function parameters only. - /// Actually an ArraySizeModifier. + LLVM_PREFERRED_TYPE(ArraySizeModifier) unsigned SizeModifier : 3; }; enum { NumArrayTypeBits = NumTypeBits + 6 }; @@ -2386,7 +2386,7 @@ public: bool isRVVType() const; - bool isRVVType(unsigned Bitwidth, bool IsFloat) const; + bool isRVVType(unsigned Bitwidth, bool IsFloat, bool IsBFloat = false) const; /// Return the implicit lifetime for this type, which must not be dependent. Qualifiers::ObjCLifetime getObjCARCImplicitLifetime() const; @@ -6663,6 +6663,7 @@ public: /// A fixed int type of a specified bitwidth. class BitIntType final : public Type, public llvm::FoldingSetNode { friend class ASTContext; + LLVM_PREFERRED_TYPE(bool) unsigned IsUnsigned : 1; unsigned NumBits : 24; @@ -7294,19 +7295,20 @@ inline bool Type::isRVVType() const { inline bool Type::isRVVType(unsigned ElementCount) const { bool Ret = false; #define RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, \ - IsFP) \ + IsFP, IsBF) \ if (NumEls == ElementCount) \ Ret |= isSpecificBuiltinType(BuiltinType::Id); #include "clang/Basic/RISCVVTypes.def" return Ret; } -inline bool Type::isRVVType(unsigned Bitwidth, bool IsFloat) const { +inline bool Type::isRVVType(unsigned Bitwidth, bool IsFloat, + bool IsBFloat) const { bool Ret = false; #define RVV_TYPE(Name, Id, SingletonId) #define RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, \ - IsFP) \ - if (ElBits == Bitwidth && IsFloat == IsFP) \ + IsFP, IsBF) \ + if (ElBits == Bitwidth && IsFloat == IsFP && IsBFloat == IsBF) \ Ret |= isSpecificBuiltinType(BuiltinType::Id); #include "clang/Basic/RISCVVTypes.def" return Ret; diff --git a/clang/include/clang/Analysis/FlowSensitive/Arena.h b/clang/include/clang/Analysis/FlowSensitive/Arena.h index 4be308c43fb76757cc916987eff0c5b7c362cab0..394ce054e65f11c99e03e4c1e1122255f0848a13 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Arena.h +++ b/clang/include/clang/Analysis/FlowSensitive/Arena.h @@ -29,8 +29,8 @@ public: /// Creates a `T` (some subclass of `StorageLocation`), forwarding `args` to /// the constructor, and returns a reference to it. /// - /// The `DataflowAnalysisContext` takes ownership of the created object. The - /// object will be destroyed when the `DataflowAnalysisContext` is destroyed. + /// The `Arena` takes ownership of the created object. The object will be + /// destroyed when the `Arena` is destroyed. template std::enable_if_t::value, T &> create(Args &&...args) { @@ -45,8 +45,8 @@ public: /// Creates a `T` (some subclass of `Value`), forwarding `args` to the /// constructor, and returns a reference to it. /// - /// The `DataflowAnalysisContext` takes ownership of the created object. The - /// object will be destroyed when the `DataflowAnalysisContext` is destroyed. + /// The `Arena` takes ownership of the created object. The object will be + /// destroyed when the `Arena` is destroyed. template std::enable_if_t::value, T &> create(Args &&...args) { diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 963197b728f42730e1b3b9971083a0fa7e407da2..7c1f5491096326b48f77f0e43d639f68f35ad993 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -544,9 +544,6 @@ public: /// Record a fact that must be true if this point in the program is reached. void assume(const Formula &); - /// Deprecated synonym for `assume()`. - void addToFlowCondition(const Formula &F) { assume(F); } - /// Returns true if the formula is always true when this point is reached. /// Returns false if the formula may be false (or the flow condition isn't /// sufficiently precise to prove that it is true) or if the solver times out. @@ -563,9 +560,6 @@ public: /// (or the flow condition is overly constraining) or if the solver times out. bool allows(const Formula &) const; - /// Deprecated synonym for `proves()`. - bool flowConditionImplies(const Formula &F) const { return proves(F); } - /// Returns the `DeclContext` of the block being analysed, if any. Otherwise, /// returns null. const DeclContext *getDeclCtx() const { return CallStack.back(); } diff --git a/clang/include/clang/Analysis/FlowSensitive/SimplifyConstraints.h b/clang/include/clang/Analysis/FlowSensitive/SimplifyConstraints.h new file mode 100644 index 0000000000000000000000000000000000000000..fadb3caf0a4c8e424ef9d42a8ab450af25e31b2d --- /dev/null +++ b/clang/include/clang/Analysis/FlowSensitive/SimplifyConstraints.h @@ -0,0 +1,49 @@ +//===-- SimplifyConstraints.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SIMPLIFYCONSTRAINTS_H +#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SIMPLIFYCONSTRAINTS_H + +#include "clang/Analysis/FlowSensitive/Arena.h" +#include "clang/Analysis/FlowSensitive/Formula.h" +#include "llvm/ADT/SetVector.h" + +namespace clang { +namespace dataflow { + +/// Information on the way a set of constraints was simplified. +struct SimplifyConstraintsInfo { + /// List of equivalence classes of atoms. For each equivalence class, the + /// original constraints imply that all atoms in it must be equivalent. + /// Simplification replaces all occurrences of atoms in an equivalence class + /// with a single representative atom from the class. + /// Does not contain equivalence classes with just one member or atoms + /// contained in `TrueAtoms` or `FalseAtoms`. + llvm::SmallVector> EquivalentAtoms; + /// Atoms that the original constraints imply must be true. + /// Simplification replaces all occurrences of these atoms by a true literal + /// (which may enable additional simplifications). + llvm::SmallVector TrueAtoms; + /// Atoms that the original constraints imply must be false. + /// Simplification replaces all occurrences of these atoms by a false literal + /// (which may enable additional simplifications). + llvm::SmallVector FalseAtoms; +}; + +/// Simplifies a set of constraints (implicitly connected by "and") in a way +/// that does not change satisfiability of the constraints. This does _not_ mean +/// that the set of solutions is the same before and after simplification. +/// `Info`, if non-null, will be populated with information about the +/// simplifications that were made to the formula (e.g. to display to the user). +void simplifyConstraints(llvm::SetVector &Constraints, + Arena &arena, SimplifyConstraintsInfo *Info = nullptr); + +} // namespace dataflow +} // namespace clang + +#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_SIMPLIFYCONSTRAINTS_H diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 60b549999c155e501db57dbfa10a0c28da432a9c..1800f584c7e1088ac1b0f354f587ad2338dd6042 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1082,6 +1082,42 @@ def CFConsumed : InheritableParamAttr { let Documentation = [RetainBehaviorDocs]; } + +// coro_only_destroy_when_complete indicates the coroutines whose return type +// is marked by coro_only_destroy_when_complete can only be destroyed when the +// coroutine completes. Then the space for the destroy functions can be saved. +def CoroOnlyDestroyWhenComplete : InheritableAttr { + let Spellings = [Clang<"coro_only_destroy_when_complete">]; + let Subjects = SubjectList<[CXXRecord]>; + let LangOpts = [CPlusPlus]; + let Documentation = [CoroOnlyDestroyWhenCompleteDocs]; + let SimpleHandler = 1; +} + +def CoroReturnType : InheritableAttr { + let Spellings = [Clang<"coro_return_type">]; + let Subjects = SubjectList<[CXXRecord]>; + let LangOpts = [CPlusPlus]; + let Documentation = [CoroReturnTypeAndWrapperDoc]; + let SimpleHandler = 1; +} + +def CoroWrapper : InheritableAttr { + let Spellings = [Clang<"coro_wrapper">]; + let Subjects = SubjectList<[Function]>; + let LangOpts = [CPlusPlus]; + let Documentation = [CoroReturnTypeAndWrapperDoc]; + let SimpleHandler = 1; +} + +def CoroLifetimeBound : InheritableAttr { + let Spellings = [Clang<"coro_lifetimebound">]; + let Subjects = SubjectList<[CXXRecord]>; + let LangOpts = [CPlusPlus]; + let Documentation = [CoroLifetimeBoundDoc]; + let SimpleHandler = 1; +} + // OSObject-based attributes. def OSConsumed : InheritableParamAttr { let Spellings = [Clang<"os_consumed">]; @@ -4217,6 +4253,18 @@ def HLSLGroupSharedAddressSpace : TypeAttr { let Documentation = [HLSLGroupSharedAddressSpaceDocs]; } +def HLSLParamModifier : TypeAttr { + let Spellings = [CustomKeyword<"in">, CustomKeyword<"inout">, CustomKeyword<"out">]; + let Accessors = [Accessor<"isIn", [CustomKeyword<"in">]>, + Accessor<"isInOut", [CustomKeyword<"inout">]>, + Accessor<"isOut", [CustomKeyword<"out">]>, + Accessor<"isAnyOut", [CustomKeyword<"out">, CustomKeyword<"inout">]>, + Accessor<"isAnyIn", [CustomKeyword<"in">, CustomKeyword<"inout">]>]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [HLSLParamQualifierDocs]; + let Args = [DefaultBoolArgument<"MergedSpelling", /*default*/0, /*fake*/1>]; +} + def RandomizeLayout : InheritableAttr { let Spellings = [GCC<"randomize_layout">]; let Subjects = SubjectList<[Record]>; @@ -4285,3 +4333,15 @@ def PreferredType: InheritableAttr { let Args = [TypeArgument<"Type", 1>]; let Documentation = [PreferredTypeDocumentation]; } + +def CodeAlign: StmtAttr { + let Spellings = [Clang<"code_align">]; + let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt], + ErrorDiag, "'for', 'while', and 'do' statements">; + let Args = [ExprArgument<"Alignment">]; + let Documentation = [CodeAlignAttrDocs]; + let AdditionalMembers = [{ + static constexpr int MinimumAlignment = 1; + static constexpr int MaximumAlignment = 4096; + }]; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 05703df2129f6125fbeb74f8f44c59a345b605b0..f2c4eb51b443ddb83b0c059a40fdd9fc68bc05a1 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6701,7 +6701,7 @@ def ArmSmeStreamingCompatibleDocs : Documentation { let Category = DocCatArmSmeAttributes; let Content = [{ The ``__arm_streaming_compatible`` keyword applies to prototyped function types and -specifies that the function has a “streaming compatible interface”. This +specifies that the function has a "streaming compatible interface". This means that: * the function may be entered in either non-streaming mode (PSTATE.SM=0) or @@ -7061,6 +7061,26 @@ The full documentation is available here: https://learn.microsoft.com/en-us/wind }]; } +def HLSLParamQualifierDocs : Documentation { + let Category = DocCatVariable; + let Heading = "HLSL Parameter Modifiers"; + let Content = [{ +HLSL function parameters are passed by value. Parameter declarations support +three qualifiers to denote parameter passing behavior. The three qualifiers are +`in`, `out` and `inout`. + +Parameters annotated with `in` or with no annotation are passed by value from +the caller to the callee. + +Parameters annotated with `out` are written to the argument after the callee +returns (Note: arguments values passed into `out` parameters *are not* copied +into the callee). + +Parameters annotated with `inout` are copied into the callee via a temporary, +and copied back to the argument after the callee returns. + }]; +} + def AnnotateTypeDocs : Documentation { let Category = DocCatType; let Heading = "annotate_type"; @@ -7295,7 +7315,7 @@ this usage is debugger-dependent. } def CleanupDocs : Documentation { - let Category = DocCatType; + let Category = DocCatVariable; let Content = [{ This attribute allows a function to be run when a local variable goes out of scope. The attribute takes the identifier of a function with a parameter type @@ -7416,3 +7436,226 @@ that ``p->array`` must have at least ``p->count`` number of elements available: }]; } + +def CoroOnlyDestroyWhenCompleteDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +The `coro_only_destroy_when_complete` attribute should be marked on a C++ class. The coroutines +whose return type is marked with the attribute are assumed to be destroyed only after the coroutine has +reached the final suspend point. + +This is helpful for the optimizers to reduce the size of the destroy function for the coroutines. + +For example, + +.. code-block:: c++ + + A foo() { + dtor d; + co_await something(); + dtor d1; + co_await something(); + dtor d2; + co_return 43; + } + +The compiler may generate the following pseudocode: + +.. code-block:: c++ + + void foo.destroy(foo.Frame *frame) { + switch(frame->suspend_index()) { + case 1: + frame->d.~dtor(); + break; + case 2: + frame->d.~dtor(); + frame->d1.~dtor(); + break; + case 3: + frame->d.~dtor(); + frame->d1.~dtor(); + frame->d2.~dtor(); + break; + default: // coroutine completed or haven't started + break; + } + + frame->promise.~promise_type(); + delete frame; + } + +The `foo.destroy()` function's purpose is to release all of the resources +initialized for the coroutine when it is destroyed in a suspended state. +However, if the coroutine is only ever destroyed at the final suspend state, +the rest of the conditions are superfluous. + +The user can use the `coro_only_destroy_when_complete` attributo suppress +generation of the other destruction cases, optimizing the above `foo.destroy` to: + +.. code-block:: c++ + + void foo.destroy(foo.Frame *frame) { + frame->promise.~promise_type(); + delete frame; + } + + }]; +} + +def CoroReturnTypeAndWrapperDoc : Documentation { + let Category = DocCatDecl; + let Content = [{ +The ``[[clang::coro_return_type]]`` attribute is used to help static analyzers to recognize +coroutines from the function signatures. + +The ``coro_return_type`` attribute should be marked on a C++ class to mark it as +a **coroutine return type (CRT)**. + +A function ``R func(P1, .., PN)`` has a coroutine return type (CRT) ``R`` if ``R`` +is marked by ``[[clang::coro_return_type]]`` and ``R`` has a promise type associated to it +(i.e., std::coroutine_traits::promise_type is a valid promise type). + +If the return type of a function is a ``CRT`` then the function must be a coroutine. +Otherwise the program is invalid. It is allowed for a non-coroutine to return a ``CRT`` +if the function is marked with ``[[clang::coro_wrapper]]``. + +The ``[[clang::coro_wrapper]]`` attribute should be marked on a C++ function to mark it as +a **coroutine wrapper**. A coroutine wrapper is a function which returns a ``CRT``, +is not a coroutine itself and is marked with ``[[clang::coro_wrapper]]``. + +Clang will enforce that all functions that return a ``CRT`` are either coroutines or marked +with ``[[clang::coro_wrapper]]``. Clang will enforce this with an error. + +From a language perspective, it is not possible to differentiate between a coroutine and a +function returning a CRT by merely looking at the function signature. + +Coroutine wrappers, in particular, are susceptible to capturing +references to temporaries and other lifetime issues. This allows to avoid such lifetime +issues with coroutine wrappers. + +For example, + +.. code-block:: c++ + + // This is a CRT. + template struct [[clang::coro_return_type]] Task { + using promise_type = some_promise_type; + }; + + Task increment(int a) { co_return a + 1; } // Fine. This is a coroutine. + Task foo() { return increment(1); } // Error. foo is not a coroutine. + + // Fine for a coroutine wrapper to return a CRT. + Task [[clang::coro_wrapper]] foo() { return increment(1); } + + void bar() { + // Invalid. This intantiates a function which returns a CRT but is not marked as + // a coroutine wrapper. + std::function(int)> f = increment; + } + +Note: ``a_promise_type::get_return_object`` is exempted from this analysis as it is a necessary +implementation detail of any coroutine library. +}]; +} + +def CodeAlignAttrDocs : Documentation { + let Category = DocCatVariable; + let Heading = "clang::code_align"; + let Content = [{ +The ``clang::code_align(N)`` attribute applies to a loop and specifies the byte +alignment for a loop. The attribute accepts a positive integer constant +initialization expression indicating the number of bytes for the minimum +alignment boundary. Its value must be a power of 2, between 1 and 4096 +(inclusive). + +.. code-block:: c++ + + void foo() { + int var = 0; + [[clang::code_align(16)]] for (int i = 0; i < 10; ++i) var++; + } + + void Array(int *array, size_t n) { + [[clang::code_align(64)]] for (int i = 0; i < n; ++i) array[i] = 0; + } + + void count () { + int a1[10], int i = 0; + [[clang::code_align(32)]] while (i < 10) { a1[i] += 3; } + } + + void check() { + int a = 10; + [[clang::code_align(8)]] do { + a = a + 1; + } while (a < 20); + } + + template + void func() { + [[clang::code_align(A)]] for(;;) { } + } + + }]; +} + +def CoroLifetimeBoundDoc : Documentation { + let Category = DocCatDecl; + let Content = [{ +The ``[[clang::coro_lifetimebound]]`` is a class attribute which can be applied +to a coroutine return type (`CRT`_) (i.e. +it should also be annotated with ``[[clang::coro_return_type]]``). + +All parameters of a function are considered to be lifetime bound. See `documentation`_ +of ``[[clang::lifetimebound]]`` for more details. +if the function returns a coroutine return type (CRT) annotated with ``[[clang::coro_lifetimebound]]``. + +Reference parameters of a coroutine are susceptible to capturing references to temporaries or local variables. + +For example, + +.. code-block:: c++ + + task coro(const int& a) { co_return a + 1; } + task dangling_refs(int a) { + // `coro` captures reference to a temporary. `foo` would now contain a dangling reference to `a`. + auto foo = coro(1); + // `coro` captures reference to local variable `a` which is destroyed after the return. + return coro(a); + } + +Lifetime bound static analysis can be used to detect such instances when coroutines capture references +which may die earlier than the coroutine frame itself. In the above example, if the CRT `task` is annotated with +``[[clang::coro_lifetimebound]]``, then lifetime bound analysis would detect capturing reference to +temporaries or return address of a local variable. + +Both coroutines and coroutine wrappers are part of this analysis. + +.. code-block:: c++ + + template struct [[clang::coro_return_type, clang::coro_lifetimebound]] Task { + using promise_type = some_promise_type; + }; + + Task coro(const int& a) { co_return a + 1; } + Task [[clang::coro_wrapper]] coro_wrapper(const int& a, const int& b) { + return a > b ? coro(a) : coro(b); + } + Task temporary_reference() { + auto foo = coro(1); // warning: capturing reference to a temporary which would die after the expression. + + int a = 1; + auto bar = coro_wrapper(a, 0); // warning: `b` captures reference to a temporary. + + co_return co_await coro(1); // fine. + } + [[clang::coro_wrapper]] Task stack_reference(int a) { + return coro(a); // warning: returning address of stack variable `a`. + } + +.. _`documentation`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +.. _`CRT`: https://clang.llvm.org/docs/AttributeReference.html#coro-return-type +}]; +} diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index 7dc05418498d0ae11d3f77effbab3d4da3f00f84..018b92fdc11f55988d98f26c5f3e9dd0a4df3940 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -74,11 +74,16 @@ private: SourceRange AttrRange; const SourceLocation ScopeLoc; // Corresponds to the Kind enum. + LLVM_PREFERRED_TYPE(Kind) unsigned AttrKind : 16; /// Corresponds to the Syntax enum. + LLVM_PREFERRED_TYPE(Syntax) unsigned SyntaxUsed : 4; + LLVM_PREFERRED_TYPE(bool) unsigned SpellingIndex : 4; + LLVM_PREFERRED_TYPE(bool) unsigned IsAlignas : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsRegularKeywordAttribute : 1; protected: @@ -123,9 +128,12 @@ public: : SyntaxUsed(SyntaxUsed), SpellingIndex(SpellingNotCalculated), IsAlignas(0), IsRegularKeywordAttribute(0) {} + LLVM_PREFERRED_TYPE(Syntax) unsigned SyntaxUsed : 4; unsigned SpellingIndex : 4; + LLVM_PREFERRED_TYPE(bool) unsigned IsAlignas : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsRegularKeywordAttribute : 1; }; @@ -169,6 +177,7 @@ public: IsRegularKeywordAttribute); } const IdentifierInfo *getAttrName() const { return AttrName; } + void setAttrName(const IdentifierInfo *AttrNameII) { AttrName = AttrNameII; } SourceLocation getLoc() const { return AttrRange.getBegin(); } SourceRange getRange() const { return AttrRange; } void setRange(SourceRange R) { AttrRange = R; } @@ -192,6 +201,15 @@ public: bool isC23Attribute() const { return SyntaxUsed == AS_C23; } + bool isAlignas() const { + // FIXME: In the current state, the IsAlignas member variable is only true + // with the C++ `alignas` keyword but not `_Alignas`. The following + // expression works around the otherwise lost information so it will return + // true for `alignas` or `_Alignas` while still returning false for things + // like `__attribute__((aligned))`. + return (getParsedKind() == AT_Aligned && isKeywordAttribute()); + } + /// The attribute is spelled [[]] in either C or C++ mode, including standard /// attributes spelled with a keyword, like alignas. bool isStandardAttributeSyntax() const { diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index 532a91fd903e87c944b1afc8899ddcd04a464746..a19c8bd5f219ec6a2fb1f9b624b1fdb94d64db94 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -292,6 +292,8 @@ TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_f16_w32, "V8fV16hV16hV8f", "nc TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_bf16_w32, "V8fV16sV16sV8f", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_w32, "V16hV16hV16hV16hIb", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_w32, "V16sV16sV16sV16sIb", "nc", "gfx11-insts") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_tied_w32, "V16hV16hV16hV16hIb", "nc", "gfx11-insts") +TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_tied_w32, "V16sV16sV16sV16sIb", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu8_w32, "V8iIbV4iIbV4iV8iIb", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu4_w32, "V8iIbV2iIbV2iV8iIb", "nc", "gfx11-insts") @@ -299,6 +301,8 @@ TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_f16_w64, "V4fV16hV16hV4f", "nc TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_bf16_w64, "V4fV16sV16sV4f", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_w64, "V8hV16hV16hV8hIb", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_w64, "V8sV16sV16sV8sIb", "nc", "gfx11-insts") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_tied_w64, "V8hV16hV16hV8hIb", "nc", "gfx11-insts") +TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_tied_w64, "V8sV16sV16sV8sIb", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu8_w64, "V4iIbV4iIbV4iV4iIb", "nc", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu4_w64, "V4iIbV2iIbV2iV4iIb", "nc", "gfx11-insts") diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index d7588fd430764cb611fe32c0da2e5051f7d69280..675645cd534ed76358500294191fe97b0e3d2fb2 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -197,6 +197,7 @@ CODEGENOPT(HIPCorrectlyRoundedDivSqrt, 1, 1) ///< -fno-hip-fp32-correctly-rounde CODEGENOPT(HIPSaveKernelArgName, 1, 0) ///< Set when -fhip-kernel-arg-name is enabled. CODEGENOPT(UniqueInternalLinkageNames, 1, 0) ///< Internal Linkage symbols get unique names. CODEGENOPT(SplitMachineFunctions, 1, 0) ///< Split machine functions using profile information. +CODEGENOPT(PPCUseFullRegisterNames, 1, 0) ///< Print full register names in assembly /// When false, this attempts to generate code as if the result of an /// overflowing conversion matches the overflowing behavior of a target's native @@ -425,7 +426,7 @@ ENUM_CODEGENOPT(Inlining, InliningMethod, 2, NormalInlining) VALUE_CODEGENOPT(InlineMaxStackSize, 32, UINT_MAX) // Vector functions library to use. -ENUM_CODEGENOPT(VecLib, VectorLibrary, 3, NoLibrary) +ENUM_CODEGENOPT(VecLib, llvm::driver::VectorLibrary, 3, llvm::driver::VectorLibrary::NoLibrary) /// The default TLS model to use. ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel) diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 12be4e0025a705458de413659b828dfad4860dc6..b202d01af0ed6ce13dbaa09b245456b255fdee05 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -17,6 +17,7 @@ #include "clang/Basic/XRayInstr.h" #include "llvm/ADT/FloatingPointMode.h" #include "llvm/Frontend/Debug/Options.h" +#include "llvm/Frontend/Driver/CodeGenOptions.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Regex.h" #include "llvm/Target/TargetOptions.h" @@ -26,6 +27,9 @@ #include #include +namespace llvm { +class PassBuilder; +} namespace clang { /// Bitfields of CodeGenOptions, split out from CodeGenOptions to ensure @@ -55,17 +59,6 @@ public: OnlyAlwaysInlining // Only run the always inlining pass. }; - enum VectorLibrary { - NoLibrary, // Don't use any vector library. - Accelerate, // Use the Accelerate framework. - LIBMVEC, // GLIBC vector math library. - MASSV, // IBM MASS vector library. - SVML, // Intel short vector math library. - SLEEF, // SLEEF SIMD Library for Evaluating Elementary Functions. - Darwin_libsystem_m, // Use Darwin's libsytem_m vector functions. - ArmPL // Arm Performance Libraries. - }; - enum ObjCDispatchMethodKind { Legacy = 0, NonLegacy = 1, @@ -408,6 +401,9 @@ public: /// List of dynamic shared object files to be loaded as pass plugins. std::vector PassPlugins; + /// List of pass builder callbacks. + std::vector> PassBuilderCallbacks; + /// Path to allowlist file specifying which objects /// (files, functions) should exclusively be instrumented /// by sanitizer coverage pass. diff --git a/clang/include/clang/Basic/Cuda.h b/clang/include/clang/Basic/Cuda.h index 878f8d70f90c0a93baeb297d96b6f9c0f16d3a93..2d912bdbbd1bc59eda37053faa87be5304803b1f 100644 --- a/clang/include/clang/Basic/Cuda.h +++ b/clang/include/clang/Basic/Cuda.h @@ -113,6 +113,8 @@ enum class CudaArch { GFX1103, GFX1150, GFX1151, + GFX1200, + GFX1201, Generic, // A processor model named 'generic' if the target backend defines a // public one. LAST, diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 3df037b793b3946ee4331bacb636e84a50476955..0c7836c2ea569ccfa3a77a364198e9eea292630a 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -314,18 +314,23 @@ private: // "Global" configuration state that can actually vary between modules. // Ignore all warnings: -w + LLVM_PREFERRED_TYPE(bool) unsigned IgnoreAllWarnings : 1; // Enable all warnings. + LLVM_PREFERRED_TYPE(bool) unsigned EnableAllWarnings : 1; // Treat warnings like errors. + LLVM_PREFERRED_TYPE(bool) unsigned WarningsAsErrors : 1; // Treat errors like fatal errors. + LLVM_PREFERRED_TYPE(bool) unsigned ErrorsAsFatal : 1; // Suppress warnings in system headers. + LLVM_PREFERRED_TYPE(bool) unsigned SuppressSystemWarnings : 1; // Map extensions to warnings or errors? @@ -1822,12 +1827,17 @@ public: struct TemplateDiffTypes { intptr_t FromType; intptr_t ToType; + LLVM_PREFERRED_TYPE(bool) unsigned PrintTree : 1; + LLVM_PREFERRED_TYPE(bool) unsigned PrintFromType : 1; + LLVM_PREFERRED_TYPE(bool) unsigned ElideType : 1; + LLVM_PREFERRED_TYPE(bool) unsigned ShowColors : 1; // The printer sets this variable to true if the template diff was used. + LLVM_PREFERRED_TYPE(bool) unsigned TemplateDiffUsed : 1; }; diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 9f0ccd255a321485747c717127d935fb57caef43..65a33f61a6948ad532f3b428d99838f3906f4578 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -260,8 +260,6 @@ def ext_clang_diagnose_if : Extension<"'diagnose_if' is a clang extension">, InGroup; def err_too_large_for_fixed_point : Error< "this value is too large for this fixed point type">; -def err_fixed_point_not_enabled : Error<"compile with " - "'-ffixed-point' to enable fixed point types">; def err_unimplemented_conversion_with_fixed_point_type : Error< "conversion between fixed point and %0 is not yet supported">; @@ -346,6 +344,8 @@ def err_opt_not_valid_on_target : Error< "option '%0' cannot be specified on this target">; def err_invalid_feature_combination : Error< "invalid feature combination: %0">; +def warn_invalid_feature_combination : Warning< + "invalid feature combination: %0">, InGroup>; def warn_target_unrecognized_env : Warning< "mismatch between architecture and environment in target triple '%0'; did you mean '%1'?">, InGroup; @@ -390,6 +390,19 @@ def note_mt_message : Note<"[rewriter] %0">; def warn_arcmt_nsalloc_realloc : Warning<"[rewriter] call returns pointer to GC managed memory; it will become unmanaged in ARC">; def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to be used with an object with ownership other than __unsafe_unretained">; +// API notes +def err_apinotes_message : Error<"%0">; +def warn_apinotes_message : Warning<"%0">, InGroup>; +def note_apinotes_message : Note<"%0">; + +class NonportablePrivateAPINotesPath : Warning< + "private API notes file for module '%0' should be named " + "'%0_private.apinotes', not '%1'">; +def warn_apinotes_private_case : NonportablePrivateAPINotesPath, + InGroup>; +def warn_apinotes_private_case_system : NonportablePrivateAPINotesPath, + DefaultIgnore, InGroup>; + // C++ for OpenCL. def err_openclcxx_not_supported : Error< "'%0' is not supported in C++ for OpenCL">; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 37559c7ff7724689f42ea497ee84a0886bb61102..ff028bbbf74261e0829ca0294e27acaaf2ac9e80 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1315,6 +1315,10 @@ def OpenMP : DiagGroup<"openmp", [ OpenMPMapping, OpenMP51Ext, OpenMPExtensions, OpenMPTargetException ]>; +// OpenACC warnings. +def SourceUsesOpenACC : DiagGroup<"source-uses-openacc">; +def OpenACC : DiagGroup<"openacc", [SourceUsesOpenACC]>; + // Backend warnings. def BackendInlineAsm : DiagGroup<"inline-asm">; def BackendSourceMgr : DiagGroup<"source-mgr">; diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 06ef1c6904c31d1b4c348631aa285a44e08b22c9..0cdda42793f6f0a0ac08bce4ca55b2d83ea06655 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -100,11 +100,17 @@ namespace clang { } class DiagnosticMapping { + LLVM_PREFERRED_TYPE(diag::Severity) unsigned Severity : 3; + LLVM_PREFERRED_TYPE(bool) unsigned IsUser : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsPragma : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasNoWarningAsError : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasNoErrorAsFatal : 1; + LLVM_PREFERRED_TYPE(bool) unsigned WasUpgradedFromWarning : 1; public: diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index de180344fcc5c74bd6399d56b2df4e7f1e06b77f..21fe6066d5876105928a8560508ccf216331002f 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -299,6 +299,12 @@ def note_missing_selector_name : Note< def note_force_empty_selector_name : Note< "or insert whitespace before ':' to use %0 as parameter name " "and have an empty entry in the selector">; +def ext_c_label_followed_by_declaration : ExtWarn< + "label followed by a declaration is a C23 extension">, + InGroup; +def warn_c23_compat_label_followed_by_declaration : Warning< + "label followed by a declaration is incompatible with C standards before " + "C23">, InGroup, DefaultIgnore; def ext_c_label_end_of_compound_statement : ExtWarn< "label at end of compound statement is a C23 extension">, InGroup; @@ -1342,6 +1348,25 @@ def err_opencl_logical_exclusive_or : Error< def err_openclcxx_virtual_function : Error< "virtual functions are not supported in C++ for OpenCL">; +// OpenACC Support. +def warn_pragma_acc_ignored + : Warning<"unexpected '#pragma acc ...' in program">, + InGroup, + DefaultIgnore; +def err_acc_unexpected_directive + : Error<"unexpected OpenACC directive %select{|'#pragma acc %1'}0">; +def warn_pragma_acc_unimplemented + : Warning<"OpenACC directives not yet implemented, pragma ignored">, + InGroup; +def warn_pragma_acc_unimplemented_clause_parsing + : Warning<"OpenACC clause parsing not yet implemented">, + InGroup; +def err_acc_invalid_directive + : Error<"invalid OpenACC directive '%select{%1|%1 %2}0'">; +def err_acc_missing_directive : Error<"expected OpenACC directive">; +def err_acc_invalid_open_paren + : Error<"expected clause-list or newline in OpenACC directive">; + // OpenMP support. def warn_pragma_omp_ignored : Warning< "unexpected '#pragma omp ...' in program">, InGroup, DefaultIgnore; @@ -1569,12 +1594,13 @@ def note_pragma_loop_invalid_vectorize_option : Note< "vectorize_width(X, scalable) where X is an integer, or vectorize_width('fixed' or 'scalable')">; def err_pragma_fp_invalid_option : Error< - "%select{invalid|missing}0 option%select{ %1|}0; expected 'contract', 'reassociate' or 'exceptions'">; + "%select{invalid|missing}0 option%select{ %1|}0; expected 'contract', 'reassociate', 'reciprocal', or 'exceptions'">; def err_pragma_fp_invalid_argument : Error< "unexpected argument '%0' to '#pragma clang fp %1'; expected " "%select{" "'fast' or 'on' or 'off'|" "'on' or 'off'|" + "'on' or 'off'|" "'ignore', 'maytrap' or 'strict'|" "'source', 'double' or 'extended'}2">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 474afc2fb99c1f0aa4515dbb1287417c91ae4857..85988b227ef02078b505bb8b5c787827aaed97e0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3177,6 +3177,10 @@ def warn_unsupported_target_attribute def err_attribute_unsupported : Error<"%0 attribute is not supported on targets missing %1;" " specify an appropriate -march= or -mcpu=">; +def err_duplicate_target_attribute + : Error<"%select{unsupported|duplicate|unknown}0%select{| CPU|" + " tune CPU}1 '%2' in the '%select{target|target_clones|target_version}3' " + "attribute string; ">; // The err_*_attribute_argument_not_int are separate because they're used by // VerifyIntegerConstantExpression. def err_aligned_attribute_argument_not_int : Error< @@ -4926,6 +4930,8 @@ def err_template_param_shadow : Error< def ext_template_param_shadow : ExtWarn< err_template_param_shadow.Summary>, InGroup; def note_template_param_here : Note<"template parameter is declared here">; +def note_template_param_external : Note< + "template parameter from hidden source: %0">; def warn_template_export_unsupported : Warning< "exported templates are unsupported">; def err_template_outside_namespace_or_class_scope : Error< @@ -5054,6 +5060,8 @@ def err_template_arg_list_different_arity : Error< "%select{class template|function template|variable template|alias template|" "template template parameter|concept|template}1 %2">; def note_template_decl_here : Note<"template is declared here">; +def note_template_decl_external : Note< + "template declaration from hidden source: %0">; def err_template_arg_must_be_type : Error< "template argument for template type parameter must be a type">; def err_template_arg_must_be_type_suggest : Error< @@ -5655,9 +5663,9 @@ def err_unexpanded_parameter_pack : Error< "%select{expression|base type|declaration type|data member type|bit-field " "size|static assertion|fixed underlying type|enumerator value|" "using declaration|friend declaration|qualifier|initializer|default argument|" - "non-type template parameter type|exception type|partial specialization|" - "__if_exists name|__if_not_exists name|lambda|block|type constraint|" - "requirement|requires clause}0 " + "non-type template parameter type|exception type|explicit specialization|" + "partial specialization|__if_exists name|__if_not_exists name|lambda|block|" + "type constraint|requirement|requires clause}0 " "contains%plural{0: an|:}1 unexpanded parameter pack" "%plural{0:|1: %2|2:s %2 and %3|:s %2, %3, ...}1">; @@ -6412,7 +6420,7 @@ def warn_superclass_variable_sized_type_not_at_end : Warning< " in superclass %3">, InGroup; def err_counted_by_attr_not_on_flexible_array_member : Error< - "'counted_by' only applies to flexible array members">; + "'counted_by' only applies to C99 flexible array members">; def err_counted_by_attr_refers_to_flexible_array : Error< "'counted_by' cannot refer to the flexible array %0">; def err_counted_by_must_be_in_structure : Error< @@ -6755,7 +6763,7 @@ def warn_floatingpoint_eq : Warning< def err_setting_eval_method_used_in_unsafe_context : Error < "%select{'#pragma clang fp eval_method'|option 'ffp-eval-method'}0 cannot be used with " - "%select{option 'fapprox-func'|option 'mreassociate'|option 'freciprocal'|option 'ffp-eval-method'|'#pragma clang fp reassociate'}1">; + "%select{option 'fapprox-func'|option 'mreassociate'|option 'freciprocal'|option 'ffp-eval-method'|'#pragma clang fp reassociate'|'#pragma clang fp reciprocal'}1">; def warn_remainder_division_by_zero : Warning< "%select{remainder|division}0 by zero is undefined">, @@ -7216,6 +7224,11 @@ def warn_arith_conv_enum_float_cxx20 : Warning< "%plural{2:with|4:from|:and}0 " "%select{enumeration|floating-point}1 type %3 is deprecated">, InGroup; +def err_arith_conv_enum_float_cxx26 : Error< + "invalid %sub{select_arith_conv_kind}0 " + "%select{floating-point|enumeration}1 type %2 " + "%plural{2:with|4:from|:and}0 " + "%select{enumeration|floating-point}1 type %3">; def warn_arith_conv_mixed_enum_types : Warning< "%sub{select_arith_conv_kind}0 " "different enumeration types%diff{ ($ and $)|}1,2">, @@ -7224,6 +7237,10 @@ def warn_arith_conv_mixed_enum_types_cxx20 : Warning< "%sub{select_arith_conv_kind}0 " "different enumeration types%diff{ ($ and $)|}1,2 is deprecated">, InGroup; +def err_conv_mixed_enum_types_cxx26 : Error< + "invalid %sub{select_arith_conv_kind}0 " + "different enumeration types%diff{ ($ and $)|}1,2">; + def warn_arith_conv_mixed_anon_enum_types : Warning< warn_arith_conv_mixed_enum_types.Summary>, InGroup, DefaultIgnore; @@ -7977,6 +7994,8 @@ def err_return_in_constructor_handler : Error< def warn_cdtor_function_try_handler_mem_expr : Warning< "cannot refer to a non-static member from the handler of a " "%select{constructor|destructor}0 function try block">, InGroup; +def err_throw_object_throwing_dtor : Error< + "cannot throw object of type %0 with a potentially-throwing destructor">; let CategoryName = "Lambda Issue" in { def err_capture_more_than_once : Error< @@ -10023,6 +10042,11 @@ def err_duplicate_case_differing_expr : Error< def warn_case_empty_range : Warning<"empty case range specified">; def warn_missing_case_for_condition : Warning<"no case matching constant switch condition '%0'">; +def err_loop_attr_conflict : Error< + "conflicting loop attribute %0">; +def err_attribute_power_of_two_in_range : Error< + "%0 attribute requires an integer argument which is a constant power of two " + "between %1 and %2 inclusive; provided argument was %3">; def warn_def_missing_case : Warning<"%plural{" "1:enumeration value %1 not explicitly handled in switch|" @@ -10960,6 +10984,8 @@ def note_omp_atomic_compare: Note< "expect binary operator in conditional expression|expect '<', '>' or '==' as order operator|expect comparison in a form of 'x == e', 'e == x', 'x ordop expr', or 'expr ordop x'|" "expect lvalue for result value|expect scalar value|expect integer value|unexpected 'else' statement|expect '==' operator|expect an assignment statement 'v = x'|" "expect a 'if' statement|expect no more than two statements|expect a compound statement|expect 'else' statement|expect a form 'r = x == e; if (r) ...'}0">; +def err_omp_atomic_fail_wrong_or_no_clauses : Error<"expected a memory order clause">; +def err_omp_atomic_fail_no_compare : Error<"expected 'compare' clause with the 'fail' modifier">; def err_omp_atomic_several_clauses : Error< "directive '#pragma omp atomic' cannot contain more than one 'read', 'write', 'update', 'capture', or 'compare' clause">; def err_omp_several_mem_order_clauses : Error< @@ -11589,6 +11615,10 @@ def err_conflicting_aligned_options : Error < def err_coro_invalid_addr_of_label : Error< "the GNU address of label extension is not allowed in coroutines." >; +def err_coroutine_return_type : Error< + "function returns a type %0 marked with [[clang::coro_return_type]] but is neither a coroutine nor a coroutine wrapper; " + "non-coroutines should be marked with [[clang::coro_wrapper]] to allow returning coroutine return type" +>; } // end of coroutines issue category let CategoryName = "Documentation Issue" in { @@ -11987,6 +12017,7 @@ def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numt def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">; def err_hlsl_missing_numthreads : Error<"missing numthreads attribute for %0 shader entry">; def err_hlsl_attribute_param_mismatch : Error<"%0 attribute parameters do not match the previous declaration">; +def err_hlsl_duplicate_parameter_modifier : Error<"duplicate parameter modifier %0">; def err_hlsl_missing_semantic_annotation : Error< "semantic annotations must be present for all parameters of an entry " "function or patch constant function">; @@ -12002,6 +12033,9 @@ def err_hlsl_pointers_unsupported : Error< def err_hlsl_operator_unsupported : Error< "the '%select{&|*|->}0' operator is unsupported in HLSL">; +def err_hlsl_param_qualifier_mismatch : + Error<"conflicting parameter qualifier %0 on parameter %1">; + // Layout randomization diagnostics. def err_non_designated_init_used : Error< "a randomized struct can only be initialized with a designated initializer">; diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index cf626d0120cc7c7e5f1eb4d056eb22dbc10bb475..adaf2e413f2f6deaf118197e96d1b2fb58ec7a4b 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -277,12 +277,14 @@ EXTENSION(gnu_asm_goto_with_outputs_full, LangOpts.GNUAsm) EXTENSION(matrix_types, LangOpts.MatrixTypes) EXTENSION(matrix_types_scalar_division, true) EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11) +EXTENSION(datasizeof, LangOpts.CPlusPlus) FEATURE(builtin_headers_in_system_modules, LangOpts.BuiltinHeadersInSystemModules) FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables) // CUDA/HIP Features FEATURE(cuda_noinline_keyword, LangOpts.CUDA) +EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates) #undef EXTENSION #undef FEATURE diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index 2eafe5938406749fe45a5417590d663c5be6e85e..0898e7d39dd7dee20301aa9926e66cefdfae7e0f 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -110,6 +110,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { friend class IdentifierTable; // Front-end token ID or tok::identifier. + LLVM_PREFERRED_TYPE(tok::TokenKind) unsigned TokenID : 9; // ObjC keyword ('protocol' in '@protocol') or builtin (__builtin_inf). @@ -118,58 +119,75 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { unsigned ObjCOrBuiltinID : ObjCOrBuiltinIDBits; // True if there is a #define for this. + LLVM_PREFERRED_TYPE(bool) unsigned HasMacro : 1; // True if there was a #define for this. + LLVM_PREFERRED_TYPE(bool) unsigned HadMacro : 1; // True if the identifier is a language extension. + LLVM_PREFERRED_TYPE(bool) unsigned IsExtension : 1; // True if the identifier is a keyword in a newer or proposed Standard. + LLVM_PREFERRED_TYPE(bool) unsigned IsFutureCompatKeyword : 1; // True if the identifier is poisoned. + LLVM_PREFERRED_TYPE(bool) unsigned IsPoisoned : 1; // True if the identifier is a C++ operator keyword. + LLVM_PREFERRED_TYPE(bool) unsigned IsCPPOperatorKeyword : 1; // Internal bit set by the member function RecomputeNeedsHandleIdentifier. // See comment about RecomputeNeedsHandleIdentifier for more info. + LLVM_PREFERRED_TYPE(bool) unsigned NeedsHandleIdentifier : 1; // True if the identifier was loaded (at least partially) from an AST file. + LLVM_PREFERRED_TYPE(bool) unsigned IsFromAST : 1; // True if the identifier has changed from the definition // loaded from an AST file. + LLVM_PREFERRED_TYPE(bool) unsigned ChangedAfterLoad : 1; // True if the identifier's frontend information has changed from the // definition loaded from an AST file. + LLVM_PREFERRED_TYPE(bool) unsigned FEChangedAfterLoad : 1; // True if revertTokenIDToIdentifier was called. + LLVM_PREFERRED_TYPE(bool) unsigned RevertedTokenID : 1; // True if there may be additional information about // this identifier stored externally. + LLVM_PREFERRED_TYPE(bool) unsigned OutOfDate : 1; // True if this is the 'import' contextual keyword. + LLVM_PREFERRED_TYPE(bool) unsigned IsModulesImport : 1; // True if this is a mangled OpenMP variant name. + LLVM_PREFERRED_TYPE(bool) unsigned IsMangledOpenMPVariantName : 1; // True if this is a deprecated macro. + LLVM_PREFERRED_TYPE(bool) unsigned IsDeprecatedMacro : 1; // True if this macro is unsafe in headers. + LLVM_PREFERRED_TYPE(bool) unsigned IsRestrictExpansion : 1; // True if this macro is final. + LLVM_PREFERRED_TYPE(bool) unsigned IsFinal : 1; // 22 bits left in a 64-bit word. diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index c0ea4ecb9806a5b03f02ae436a5ca4b7bbfbdf88..c3d5399905a3fdae2580eadcfdf47c520513df14 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -145,6 +145,7 @@ ENUM_LANGOPT(ExceptionHandling, ExceptionHandlingKind, 3, ExceptionHandlingKind::None, "exception handling") LANGOPT(IgnoreExceptions , 1, 0, "ignore exceptions") LANGOPT(ExternCNoUnwind , 1, 0, "Assume extern C functions don't unwind") +LANGOPT(AssumeNothrowExceptionDtor , 1, 0, "Assume exception object's destructor is nothrow") LANGOPT(TraditionalCPP , 1, 0, "traditional CPP emulation") LANGOPT(RTTI , 1, 1, "run-time type information") LANGOPT(RTTIData , 1, 1, "emit run-time type information data") @@ -268,6 +269,7 @@ LANGOPT(CUDAAllowVariadicFunctions, 1, 0, "allowing variadic functions in CUDA d LANGOPT(CUDAHostDeviceConstexpr, 1, 1, "treating unattributed constexpr functions as __host__ __device__") LANGOPT(GPUDeviceApproxTranscendentals, 1, 0, "using approximate transcendental functions") LANGOPT(GPURelocatableDeviceCode, 1, 0, "generate relocatable device code") +LANGOPT(OffloadImplicitHostDeviceTemplates, 1, 0, "assume template functions to be implicitly host device by default for CUDA/HIP") LANGOPT(GPUAllowDeviceInit, 1, 0, "allowing device side global init functions for HIP") LANGOPT(GPUMaxThreadsPerBlock, 32, 1024, "default max threads per block for kernel launch bounds for HIP") LANGOPT(GPUDeferDiag, 1, 0, "defer host/device related diagnostic messages for CUDA/HIP") @@ -283,6 +285,8 @@ LANGOPT(OffloadUniformBlock, 1, 0, "Assume that kernels are launched with unifor LANGOPT(HIPStdPar, 1, 0, "Enable Standard Parallel Algorithm Acceleration for HIP (experimental)") LANGOPT(HIPStdParInterposeAlloc, 1, 0, "Replace allocations / deallocations with HIP RT calls when Standard Parallel Algorithm Acceleration for HIP is enabled (Experimental)") +LANGOPT(OpenACC , 1, 0, "OpenACC Enabled") + LANGOPT(SizedDeallocation , 1, 0, "sized deallocation") LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable") @@ -347,9 +351,9 @@ LANGOPT( "type's inheritance model would be determined under the Microsoft ABI") ENUM_LANGOPT(GC, GCMode, 2, NonGC, "Objective-C Garbage Collection mode") -ENUM_LANGOPT(ValueVisibilityMode, Visibility, 3, DefaultVisibility, +BENIGN_ENUM_LANGOPT(ValueVisibilityMode, Visibility, 3, DefaultVisibility, "default visibility for functions and variables [-fvisibility]") -ENUM_LANGOPT(TypeVisibilityMode, Visibility, 3, DefaultVisibility, +BENIGN_ENUM_LANGOPT(TypeVisibilityMode, Visibility, 3, DefaultVisibility, "default visibility for types [-ftype-visibility]") LANGOPT(SetVisibilityForExternDecls, 1, 0, "apply global symbol visibility to external declarations without an explicit visibility") @@ -400,6 +404,9 @@ LANGOPT(XLPragmaPack, 1, 0, "IBM XL #pragma pack handling") LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST") +LANGOPT(APINotes, 1, 0, "use external API notes") +LANGOPT(APINotesModules, 1, 0, "use module-based external API notes") + LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " "field padding (0: none, 1:least " "aggressive, 2: more aggressive)") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 20a8ada60e0fe5138a7642e4be028d2f0cc68759..2d167dd2bdf1287a4a4fa8a8e7a84697b961ca29 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -502,6 +502,11 @@ public: // received as a result of a standard operator new (-fcheck-new) bool CheckNew = false; + // In OpenACC mode, contains a user provided override for the _OPENACC macro. + // This exists so that we can override the macro value and test our incomplete + // implementation on real-world examples. + std::string OpenACCMacroOverride; + LangOptions(); /// Set language defaults for the given input language and @@ -597,6 +602,9 @@ public: return !requiresStrictPrototypes() && !OpenCL; } + /// Returns true if the language supports calling the 'atexit' function. + bool hasAtExit() const { return !(OpenMP && OpenMPIsTargetDevice); } + /// Returns true if implicit int is part of the language requirements. bool isImplicitIntRequired() const { return !CPlusPlus && !C99; } diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index 6a7423938bdb8fa7a19e463532fedaf4ac6dce26..d29cc0b45d583e049e614de254761d99ac2efb2d 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -178,9 +178,11 @@ public: /// eventually be exposed, for use in "private" modules. std::string ExportAsModule; - /// Does this Module scope describe part of the purview of a standard named - /// C++ module? - bool isModulePurview() const { + /// For the debug info, the path to this module's .apinotes file, if any. + std::string APINotesFile; + + /// Does this Module is a named module of a standard named module? + bool isNamedModule() const { switch (Kind) { case ModuleInterfaceUnit: case ModuleImplementationUnit: @@ -298,50 +300,62 @@ public: /// Whether this module has declared itself unimportable, either because /// it's missing a requirement from \p Requirements or because it's been /// shadowed by another module. + LLVM_PREFERRED_TYPE(bool) unsigned IsUnimportable : 1; /// Whether we tried and failed to load a module file for this module. + LLVM_PREFERRED_TYPE(bool) unsigned HasIncompatibleModuleFile : 1; /// Whether this module is available in the current translation unit. /// /// If the module is missing headers or does not meet all requirements then /// this bit will be 0. + LLVM_PREFERRED_TYPE(bool) unsigned IsAvailable : 1; /// Whether this module was loaded from a module file. + LLVM_PREFERRED_TYPE(bool) unsigned IsFromModuleFile : 1; /// Whether this is a framework module. + LLVM_PREFERRED_TYPE(bool) unsigned IsFramework : 1; /// Whether this is an explicit submodule. + LLVM_PREFERRED_TYPE(bool) unsigned IsExplicit : 1; /// Whether this is a "system" module (which assumes that all /// headers in it are system headers). + LLVM_PREFERRED_TYPE(bool) unsigned IsSystem : 1; /// Whether this is an 'extern "C"' module (which implicitly puts all /// headers in it within an 'extern "C"' block, and allows the module to be /// imported within such a block). + LLVM_PREFERRED_TYPE(bool) unsigned IsExternC : 1; /// Whether this is an inferred submodule (module * { ... }). + LLVM_PREFERRED_TYPE(bool) unsigned IsInferred : 1; /// Whether we should infer submodules for this module based on /// the headers. /// /// Submodules can only be inferred for modules with an umbrella header. + LLVM_PREFERRED_TYPE(bool) unsigned InferSubmodules : 1; /// Whether, when inferring submodules, the inferred submodules /// should be explicit. + LLVM_PREFERRED_TYPE(bool) unsigned InferExplicitSubmodules : 1; /// Whether, when inferring submodules, the inferr submodules should /// export all modules they import (e.g., the equivalent of "export *"). + LLVM_PREFERRED_TYPE(bool) unsigned InferExportWildcard : 1; /// Whether the set of configuration macros is exhaustive. @@ -349,18 +363,22 @@ public: /// When the set of configuration macros is exhaustive, meaning /// that no identifier not in this list should affect how the module is /// built. + LLVM_PREFERRED_TYPE(bool) unsigned ConfigMacrosExhaustive : 1; /// Whether files in this module can only include non-modular headers /// and headers from used modules. + LLVM_PREFERRED_TYPE(bool) unsigned NoUndeclaredIncludes : 1; /// Whether this module came from a "private" module map, found next /// to a regular (public) module map. + LLVM_PREFERRED_TYPE(bool) unsigned ModuleMapIsPrivate : 1; /// Whether this C++20 named modules doesn't need an initializer. /// This is only meaningful for C++20 modules. + LLVM_PREFERRED_TYPE(bool) unsigned NamedModuleHasInit : 1; /// Describes the visibility of the various names within a diff --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h new file mode 100644 index 0000000000000000000000000000000000000000..1a5bf7e0e831ce35db7480d5e61efac4c395fc6d --- /dev/null +++ b/clang/include/clang/Basic/OpenACCKinds.h @@ -0,0 +1,74 @@ +//===--- OpenACCKinds.h - OpenACC Enums -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines some OpenACC-specific enums and functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_OPENACCKINDS_H +#define LLVM_CLANG_BASIC_OPENACCKINDS_H + +namespace clang { +// Represents the Construct/Directive kind of a pragma directive. Note the +// OpenACC standard is inconsistent between calling these Construct vs +// Directive, but we're calling it a Directive to be consistent with OpenMP. +enum class OpenACCDirectiveKind { + // Compute Constructs. + Parallel, + Serial, + Kernels, + + // Data Environment. "enter data" and "exit data" are also referred to in the + // Executable Directives section, but just as a back reference to the Data + // Environment. + Data, + EnterData, + ExitData, + HostData, + + // Misc. + Loop, + // FIXME: 'cache' + + // Combined Constructs. + ParallelLoop, + SerialLoop, + KernelsLoop, + + // Atomic Construct. + Atomic, + + // Declare Directive. + Declare, + + // Executable Directives. "wait" is first referred to here, but ends up being + // in its own section after "routine". + Init, + Shutdown, + Set, + Update, + // FIXME: wait construct. + + // Procedure Calls in Compute Regions. + Routine, + + // Invalid. + Invalid, +}; + +enum class OpenACCAtomicKind { + Read, + Write, + Update, + Capture, + Invalid, +}; +} // namespace clang + +#endif // LLVM_CLANG_BASIC_OPENACCKINDS_H diff --git a/clang/include/clang/Basic/OpenMPKinds.def b/clang/include/clang/Basic/OpenMPKinds.def index 272537b769742b597875942ccfe1e0044430bf68..f46a92d5ecfd44a53808956fdf3baa4721de28d4 100644 --- a/clang/include/clang/Basic/OpenMPKinds.def +++ b/clang/include/clang/Basic/OpenMPKinds.def @@ -41,6 +41,9 @@ #ifndef OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND #define OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND(Name) #endif +#ifndef OPENMP_ATOMIC_FAIL_MODIFIER +#define OPENMP_ATOMIC_FAIL_MODIFIER(Name) +#endif #ifndef OPENMP_AT_KIND #define OPENMP_AT_KIND(Name) #endif @@ -138,6 +141,11 @@ OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND(seq_cst) OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND(acq_rel) OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND(relaxed) +// Modifiers for atomic 'fail' clause. +OPENMP_ATOMIC_FAIL_MODIFIER(seq_cst) +OPENMP_ATOMIC_FAIL_MODIFIER(acquire) +OPENMP_ATOMIC_FAIL_MODIFIER(relaxed) + // Modifiers for 'at' clause. OPENMP_AT_KIND(compilation) OPENMP_AT_KIND(execution) @@ -226,6 +234,7 @@ OPENMP_DOACROSS_MODIFIER(source_omp_cur_iteration) #undef OPENMP_SCHEDULE_MODIFIER #undef OPENMP_SCHEDULE_KIND #undef OPENMP_ATOMIC_DEFAULT_MEM_ORDER_KIND +#undef OPENMP_ATOMIC_FAIL_MODIFIER #undef OPENMP_AT_KIND #undef OPENMP_SEVERITY_KIND #undef OPENMP_MAP_KIND diff --git a/clang/include/clang/Basic/OpenMPKinds.h b/clang/include/clang/Basic/OpenMPKinds.h index ac1b3cdfff145bc022e7f4d15ec25c76e89d631a..d127498774c7fa4fb441401f7bd8e3a244c14cd1 100644 --- a/clang/include/clang/Basic/OpenMPKinds.h +++ b/clang/include/clang/Basic/OpenMPKinds.h @@ -363,6 +363,11 @@ bool isOpenMPCombinedParallelADirective(OpenMPDirectiveKind DKind); /// \return true - if the above condition is met for this directive /// otherwise - false. bool needsTaskBasedThreadLimit(OpenMPDirectiveKind DKind); + +/// Checks if the parameter to the fail clause in "#pragma atomic compare fail" +/// is restricted only to memory order clauses of "OMPC_acquire", +/// "OMPC_relaxed" and "OMPC_seq_cst". +bool checkFailClauseParameter(OpenMPClauseKind FailClauseParameter); } #endif diff --git a/clang/include/clang/Basic/ParsedAttrInfo.h b/clang/include/clang/Basic/ParsedAttrInfo.h index 3950d484ffd32bc71bf0ab70dead58c399d1acef..537d8f3391d5898705317fd5637c450ec8c00625 100644 --- a/clang/include/clang/Basic/ParsedAttrInfo.h +++ b/clang/include/clang/Basic/ParsedAttrInfo.h @@ -33,6 +33,7 @@ class TargetInfo; struct ParsedAttrInfo { /// Corresponds to the Kind enum. + LLVM_PREFERRED_TYPE(AttributeCommonInfo::Kind) unsigned AttrKind : 16; /// The number of required arguments of this attribute. unsigned NumArgs : 4; @@ -41,18 +42,25 @@ struct ParsedAttrInfo { /// The number of non-fake arguments specified in the attribute definition. unsigned NumArgMembers : 4; /// True if the parsing does not match the semantic content. + LLVM_PREFERRED_TYPE(bool) unsigned HasCustomParsing : 1; // True if this attribute accepts expression parameter pack expansions. + LLVM_PREFERRED_TYPE(bool) unsigned AcceptsExprPack : 1; /// True if this attribute is only available for certain targets. + LLVM_PREFERRED_TYPE(bool) unsigned IsTargetSpecific : 1; /// True if this attribute applies to types. + LLVM_PREFERRED_TYPE(bool) unsigned IsType : 1; /// True if this attribute applies to statements. + LLVM_PREFERRED_TYPE(bool) unsigned IsStmt : 1; /// True if this attribute has any spellings that are known to gcc. + LLVM_PREFERRED_TYPE(bool) unsigned IsKnownToGCC : 1; /// True if this attribute is supported by #pragma clang attribute. + LLVM_PREFERRED_TYPE(bool) unsigned IsSupportedByPragmaAttribute : 1; /// The syntaxes supported by this attribute and how they're spelled. struct Spelling { diff --git a/clang/include/clang/Basic/PragmaKinds.h b/clang/include/clang/Basic/PragmaKinds.h index 176bbc9ac7caaecd0902d8e991ab3279930363a9..42f049f7323d2d4171c1e735269de699895b5ba9 100644 --- a/clang/include/clang/Basic/PragmaKinds.h +++ b/clang/include/clang/Basic/PragmaKinds.h @@ -34,6 +34,14 @@ enum PragmaFloatControlKind { PFC_Push, // #pragma float_control(push) PFC_Pop // #pragma float_control(pop) }; + +enum PragmaFPKind { + PFK_Contract, // #pragma clang fp contract + PFK_Reassociate, // #pragma clang fp reassociate + PFK_Reciprocal, // #pragma clang fp reciprocal + PFK_Exceptions, // #pragma clang fp exceptions + PFK_EvalMethod // #pragma clang fp eval_method +}; } #endif diff --git a/clang/include/clang/Basic/RISCVVTypes.def b/clang/include/clang/Basic/RISCVVTypes.def index 575bca58b51e023aef898625bef5f49432e13e04..6620de8ad50e01e1f65df99e0428c5b9a1e117e0 100644 --- a/clang/include/clang/Basic/RISCVVTypes.def +++ b/clang/include/clang/Basic/RISCVVTypes.def @@ -12,7 +12,8 @@ // A builtin type that has not been covered by any other #define // Defining this macro covers all the builtins. // -// - RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, IsSigned, IsFP) +// - RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, IsSigned, IsFP, +// IsBF) // A RISC-V V scalable vector. // // - RVV_PREDICATE_TYPE(Name, Id, SingletonId, NumEls) @@ -45,7 +46,8 @@ #endif #ifndef RVV_VECTOR_TYPE -#define RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, IsFP)\ +#define RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, \ + IsFP, IsBF) \ RVV_TYPE(Name, Id, SingletonId) #endif @@ -55,13 +57,20 @@ #endif #ifndef RVV_VECTOR_TYPE_INT -#define RVV_VECTOR_TYPE_INT(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned) \ - RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, false) +#define RVV_VECTOR_TYPE_INT(Name, Id, SingletonId, NumEls, ElBits, NF, \ + IsSigned) \ + RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, false, \ + false) #endif #ifndef RVV_VECTOR_TYPE_FLOAT -#define RVV_VECTOR_TYPE_FLOAT(Name, Id, SingletonId, NumEls, ElBits, NF) \ - RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, false, true) +#define RVV_VECTOR_TYPE_FLOAT(Name, Id, SingletonId, NumEls, ElBits, NF) \ + RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, false, true, false) +#endif + +#ifndef RVV_VECTOR_TYPE_BFLOAT +#define RVV_VECTOR_TYPE_BFLOAT(Name, Id, SingletonId, NumEls, ElBits, NF) \ + RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, false, false, true) #endif //===- Vector types -------------------------------------------------------===// @@ -125,6 +134,19 @@ RVV_VECTOR_TYPE_FLOAT("__rvv_float16m2_t", RvvFloat16m2, RvvFloat16m2Ty, 8, 16, RVV_VECTOR_TYPE_FLOAT("__rvv_float16m4_t", RvvFloat16m4, RvvFloat16m4Ty, 16, 16, 1) RVV_VECTOR_TYPE_FLOAT("__rvv_float16m8_t", RvvFloat16m8, RvvFloat16m8Ty, 32, 16, 1) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4_t", RvvBFloat16mf4, RvvBFloat16mf4Ty, + 1, 16, 1) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2_t", RvvBFloat16mf2, RvvBFloat16mf2Ty, + 2, 16, 1) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1_t", RvvBFloat16m1, RvvBFloat16m1Ty, 4, + 16, 1) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m2_t", RvvBFloat16m2, RvvBFloat16m2Ty, 8, + 16, 1) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m4_t", RvvBFloat16m4, RvvBFloat16m4Ty, 16, + 16, 1) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m8_t", RvvBFloat16m8, RvvBFloat16m8Ty, 32, + 16, 1) + RVV_VECTOR_TYPE_FLOAT("__rvv_float32mf2_t",RvvFloat32mf2,RvvFloat32mf2Ty,1, 32, 1) RVV_VECTOR_TYPE_FLOAT("__rvv_float32m1_t", RvvFloat32m1, RvvFloat32m1Ty, 2, 32, 1) RVV_VECTOR_TYPE_FLOAT("__rvv_float32m2_t", RvvFloat32m2, RvvFloat32m2Ty, 4, 32, 1) @@ -430,6 +452,63 @@ RVV_VECTOR_TYPE_FLOAT("__rvv_float64m2x4_t", RvvFloat64m2x4, RvvFloat64m2x4Ty, 2 RVV_VECTOR_TYPE_FLOAT("__rvv_float64m4x2_t", RvvFloat64m4x2, RvvFloat64m4x2Ty, 4, 64, 2) +//===- BFloat16 tuple types -------------------------------------------------===// +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4x2_t", RvvBFloat16mf4x2, RvvBFloat16mf4x2Ty, + 1, 16, 2) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4x3_t", RvvBFloat16mf4x3, RvvBFloat16mf4x3Ty, + 1, 16, 3) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4x4_t", RvvBFloat16mf4x4, RvvBFloat16mf4x4Ty, + 1, 16, 4) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4x5_t", RvvBFloat16mf4x5, RvvBFloat16mf4x5Ty, + 1, 16, 5) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4x6_t", RvvBFloat16mf4x6, RvvBFloat16mf4x6Ty, + 1, 16, 6) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4x7_t", RvvBFloat16mf4x7, RvvBFloat16mf4x7Ty, + 1, 16, 7) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf4x8_t", RvvBFloat16mf4x8, RvvBFloat16mf4x8Ty, + 1, 16, 8) + +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2x2_t", RvvBFloat16mf2x2, RvvBFloat16mf2x2Ty, + 2, 16, 2) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2x3_t", RvvBFloat16mf2x3, RvvBFloat16mf2x3Ty, + 2, 16, 3) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2x4_t", RvvBFloat16mf2x4, RvvBFloat16mf2x4Ty, + 2, 16, 4) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2x5_t", RvvBFloat16mf2x5, RvvBFloat16mf2x5Ty, + 2, 16, 5) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2x6_t", RvvBFloat16mf2x6, RvvBFloat16mf2x6Ty, + 2, 16, 6) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2x7_t", RvvBFloat16mf2x7, RvvBFloat16mf2x7Ty, + 2, 16, 7) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16mf2x8_t", RvvBFloat16mf2x8, RvvBFloat16mf2x8Ty, + 2, 16, 8) + +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1x2_t", RvvBFloat16m1x2, RvvBFloat16m1x2Ty, + 4, 16, 2) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1x3_t", RvvBFloat16m1x3, RvvBFloat16m1x3Ty, + 4, 16, 3) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1x4_t", RvvBFloat16m1x4, RvvBFloat16m1x4Ty, + 4, 16, 4) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1x5_t", RvvBFloat16m1x5, RvvBFloat16m1x5Ty, + 4, 16, 5) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1x6_t", RvvBFloat16m1x6, RvvBFloat16m1x6Ty, + 4, 16, 6) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1x7_t", RvvBFloat16m1x7, RvvBFloat16m1x7Ty, + 4, 16, 7) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m1x8_t", RvvBFloat16m1x8, RvvBFloat16m1x8Ty, + 4, 16, 8) + +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m2x2_t", RvvBFloat16m2x2, RvvBFloat16m2x2Ty, + 8, 16, 2) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m2x3_t", RvvBFloat16m2x3, RvvBFloat16m2x3Ty, + 8, 16, 3) +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m2x4_t", RvvBFloat16m2x4, RvvBFloat16m2x4Ty, + 8, 16, 4) + +RVV_VECTOR_TYPE_BFLOAT("__rvv_bfloat16m4x2_t", RvvBFloat16m4x2, RvvBFloat16m4x2Ty, + 16, 16, 2) + +#undef RVV_VECTOR_TYPE_BFLOAT #undef RVV_VECTOR_TYPE_FLOAT #undef RVV_VECTOR_TYPE_INT #undef RVV_VECTOR_TYPE diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index 420fda111ad13698aab4d5ca7ebcba61736d8d4b..985ea6354b82199f097fedb044c2829b5e9b1aea 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -167,17 +167,21 @@ public: /// /// When true, the original entry may be a virtual file that does not /// exist. + LLVM_PREFERRED_TYPE(bool) unsigned BufferOverridden : 1; /// True if this content cache was initially created for a source file /// considered to be volatile (likely to change between stat and open). + LLVM_PREFERRED_TYPE(bool) unsigned IsFileVolatile : 1; /// True if this file may be transient, that is, if it might not /// exist at some later point in time when this content entry is used, /// after serialization and deserialization. + LLVM_PREFERRED_TYPE(bool) unsigned IsTransient : 1; + LLVM_PREFERRED_TYPE(bool) mutable unsigned IsBufferInvalid : 1; ContentCache() @@ -305,6 +309,7 @@ class FileInfo { unsigned NumCreatedFIDs : 31; /// Whether this FileInfo has any \#line directives. + LLVM_PREFERRED_TYPE(bool) unsigned HasLineDirectives : 1; /// The content cache and the characteristic of the file. @@ -476,6 +481,7 @@ static_assert(sizeof(FileInfo) <= sizeof(ExpansionInfo), class SLocEntry { static constexpr int OffsetBits = 8 * sizeof(SourceLocation::UIntTy) - 1; SourceLocation::UIntTy Offset : OffsetBits; + LLVM_PREFERRED_TYPE(bool) SourceLocation::UIntTy IsExpansion : 1; union { FileInfo File; diff --git a/clang/include/clang/Basic/SourceMgrAdapter.h b/clang/include/clang/Basic/SourceMgrAdapter.h new file mode 100644 index 0000000000000000000000000000000000000000..be7f9d5051fbf4c0433691cfb174c5fc3cade6fb --- /dev/null +++ b/clang/include/clang/Basic/SourceMgrAdapter.h @@ -0,0 +1,85 @@ +//=== SourceMgrAdapter.h - SourceMgr to SourceManager Adapter ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides an adapter that maps diagnostics from llvm::SourceMgr +// to Clang's SourceManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SOURCEMGRADAPTER_H +#define LLVM_CLANG_SOURCEMGRADAPTER_H + +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/SourceMgr.h" +#include +#include + +namespace clang { + +class DiagnosticsEngine; +class FileEntry; + +/// An adapter that can be used to translate diagnostics from one or more +/// llvm::SourceMgr instances to a , +class SourceMgrAdapter { + /// Clang source manager. + SourceManager &SrcMgr; + + /// Clang diagnostics engine. + DiagnosticsEngine &Diagnostics; + + /// Diagnostic IDs for errors, warnings, and notes. + unsigned ErrorDiagID, WarningDiagID, NoteDiagID; + + /// The default file to use when mapping buffers. + OptionalFileEntryRef DefaultFile; + + /// A mapping from (LLVM source manager, buffer ID) pairs to the + /// corresponding file ID within the Clang source manager. + llvm::DenseMap, FileID> + FileIDMapping; + + /// Diagnostic handler. + static void handleDiag(const llvm::SMDiagnostic &Diag, void *Context); + +public: + /// Create a new \c SourceMgr adaptor that maps to the given source + /// manager and diagnostics engine. + SourceMgrAdapter(SourceManager &SM, DiagnosticsEngine &Diagnostics, + unsigned ErrorDiagID, unsigned WarningDiagID, + unsigned NoteDiagID, + OptionalFileEntryRef DefaultFile = std::nullopt); + + ~SourceMgrAdapter(); + + /// Map a source location in the given LLVM source manager to its + /// corresponding location in the Clang source manager. + SourceLocation mapLocation(const llvm::SourceMgr &LLVMSrcMgr, + llvm::SMLoc Loc); + + /// Map a source range in the given LLVM source manager to its corresponding + /// range in the Clang source manager. + SourceRange mapRange(const llvm::SourceMgr &LLVMSrcMgr, llvm::SMRange Range); + + /// Handle the given diagnostic from an LLVM source manager. + void handleDiag(const llvm::SMDiagnostic &Diag); + + /// Retrieve the diagnostic handler to use with the underlying SourceMgr. + llvm::SourceMgr::DiagHandlerTy getDiagHandler() { + return &SourceMgrAdapter::handleDiag; + } + + /// Retrieve the context to use with the diagnostic handler produced by + /// \c getDiagHandler(). + void *getDiagContext() { return this; } +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index 0add24d53b21e3440694f05104e184e4d6500c03..87f29c8ae10bd9a05252dc920e73cc314870eb2e 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -103,9 +103,13 @@ namespace clang { /// were written in a particular type specifier sequence. struct WrittenBuiltinSpecs { static_assert(TST_error < 1 << 7, "Type bitfield not wide enough for TST"); - /*DeclSpec::TST*/ unsigned Type : 7; - /*DeclSpec::TSS*/ unsigned Sign : 2; - /*TypeSpecifierWidth*/ unsigned Width : 2; + LLVM_PREFERRED_TYPE(TypeSpecifierType) + unsigned Type : 7; + LLVM_PREFERRED_TYPE(TypeSpecifierSign) + unsigned Sign : 2; + LLVM_PREFERRED_TYPE(TypeSpecifierWidth) + unsigned Width : 2; + LLVM_PREFERRED_TYPE(bool) unsigned ModeAttr : 1; }; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index b3c5cbfb319f01f4e075dcf1f39909aaa916c8b3..41f3c2e403cbef6d0fbd7c9640f810f418d7da43 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -156,6 +156,7 @@ protected: /// /// Otherwise, when this flag is not set, the normal built-in boolean type is /// used. + LLVM_PREFERRED_TYPE(bool) unsigned UseSignedCharForObjCBool : 1; /// Control whether the alignment of bit-field types is respected when laying @@ -163,6 +164,7 @@ protected: /// used to (a) impact the alignment of the containing structure, and (b) /// ensure that the individual bit-field will not straddle an alignment /// boundary. + LLVM_PREFERRED_TYPE(bool) unsigned UseBitFieldTypeAlignment : 1; /// Whether zero length bitfields (e.g., int : 0;) force alignment of @@ -171,13 +173,16 @@ protected: /// If the alignment of the zero length bitfield is greater than the member /// that follows it, `bar', `bar' will be aligned as the type of the /// zero-length bitfield. + LLVM_PREFERRED_TYPE(bool) unsigned UseZeroLengthBitfieldAlignment : 1; /// Whether zero length bitfield alignment is respected if they are the /// leading members. + LLVM_PREFERRED_TYPE(bool) unsigned UseLeadingZeroLengthBitfield : 1; /// Whether explicit bit field alignment attributes are honored. + LLVM_PREFERRED_TYPE(bool) unsigned UseExplicitBitFieldAlignment : 1; /// If non-zero, specifies a fixed alignment value for bitfields that follow @@ -239,20 +244,29 @@ protected: mutable StringRef PlatformName; mutable VersionTuple PlatformMinVersion; + LLVM_PREFERRED_TYPE(bool) unsigned HasAlignMac68kSupport : 1; + LLVM_PREFERRED_TYPE(FloatModeKind) unsigned RealTypeUsesObjCFPRetMask : llvm::BitWidth; + LLVM_PREFERRED_TYPE(bool) unsigned ComplexLongDoubleUsesFP2Ret : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasBuiltinMSVaList : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsRenderScriptTarget : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasAArch64SVETypes : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasRISCVVTypes : 1; + LLVM_PREFERRED_TYPE(bool) unsigned AllowAMDGPUUnsafeFPAtomics : 1; + LLVM_PREFERRED_TYPE(bool) unsigned ARMCDECoprocMask : 8; unsigned MaxOpenCLWorkGroupSize; diff --git a/clang/include/clang/Basic/TargetOptions.h b/clang/include/clang/Basic/TargetOptions.h index 8bb03249b7f8308c79bdf2e9e2ea2c18375e3b09..2049f03b28893fde01bce0ba3cc8d8330356be72 100644 --- a/clang/include/clang/Basic/TargetOptions.h +++ b/clang/include/clang/Basic/TargetOptions.h @@ -78,17 +78,9 @@ public: /// \brief If enabled, allow AMDGPU unsafe floating point atomics. bool AllowAMDGPUUnsafeFPAtomics = false; - /// \brief Enumeration value for AMDGPU code object version, which is the - /// code object version times 100. - enum CodeObjectVersionKind { - COV_None, - COV_2 = 200, // Unsupported. - COV_3 = 300, - COV_4 = 400, - COV_5 = 500, - }; /// \brief Code object version for AMDGPU. - CodeObjectVersionKind CodeObjectVersion = CodeObjectVersionKind::COV_None; + llvm::CodeObjectVersionKind CodeObjectVersion = + llvm::CodeObjectVersionKind::COV_None; /// \brief Enumeration values for AMDGPU printf lowering scheme enum class AMDGPUPrintfKind { diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 3ce317d318f9bb6bf94e469064244b708fd2e87a..5f9915d210221c4f8619f581c030368908077a6b 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -284,6 +284,8 @@ PUNCTUATOR(caretcaret, "^^") // HALFSUPPORT - This is a keyword if 'half' is a built-in type // WCHARSUPPORT - This is a keyword if 'wchar_t' is a built-in type // CHAR8SUPPORT - This is a keyword if 'char8_t' is a built-in type +// KEYFIXEDPOINT - This is a keyword according to the N1169 fixed point +// extension. // KEYWORD(auto , KEYALL) KEYWORD(break , KEYALL) @@ -310,6 +312,7 @@ KEYWORD(return , KEYALL) KEYWORD(short , KEYALL) KEYWORD(signed , KEYALL) UNARY_EXPR_OR_TYPE_TRAIT(sizeof, SizeOf, KEYALL) +UNARY_EXPR_OR_TYPE_TRAIT(__datasizeof, DataSizeOf, KEYCXX) KEYWORD(static , KEYALL) KEYWORD(struct , KEYALL) KEYWORD(switch , KEYALL) @@ -423,9 +426,9 @@ C23_KEYWORD(typeof , KEYGNU) C23_KEYWORD(typeof_unqual , 0) // ISO/IEC JTC1 SC22 WG14 N1169 Extension -KEYWORD(_Accum , KEYNOCXX) -KEYWORD(_Fract , KEYNOCXX) -KEYWORD(_Sat , KEYNOCXX) +KEYWORD(_Accum , KEYFIXEDPOINT) +KEYWORD(_Fract , KEYFIXEDPOINT) +KEYWORD(_Sat , KEYFIXEDPOINT) // GNU Extensions (in impl-reserved namespace) KEYWORD(_Decimal32 , KEYALL) @@ -625,6 +628,9 @@ KEYWORD(__noinline__ , KEYCUDA) KEYWORD(cbuffer , KEYHLSL) KEYWORD(tbuffer , KEYHLSL) KEYWORD(groupshared , KEYHLSL) +KEYWORD(in , KEYHLSL) +KEYWORD(inout , KEYHLSL) +KEYWORD(out , KEYHLSL) // OpenMP Type Traits UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL) @@ -943,6 +949,12 @@ ANNOTATION(attr_openmp) PRAGMA_ANNOTATION(pragma_openmp) PRAGMA_ANNOTATION(pragma_openmp_end) +// Annotations for OpenACC pragma directives - #pragma acc. +// Like with OpenMP, these are produced by the lexer when it parses a +// #pragma acc directive so it can be handled during parsing of the directives. +PRAGMA_ANNOTATION(pragma_openacc) +PRAGMA_ANNOTATION(pragma_openacc_end) + // Annotations for loop pragma directives #pragma clang loop ... // The lexer produces these so that they only take effect when the parser // handles #pragma loop ... directives. diff --git a/clang/include/clang/Basic/arm_sme.td b/clang/include/clang/Basic/arm_sme.td index 8d85327a86b1aafcb6e2d6e65036ee5cf4ac4bf0..d55deeaa40bbcd54615682c5e00aa7d70146cf07 100644 --- a/clang/include/clang/Basic/arm_sme.td +++ b/clang/include/clang/Basic/arm_sme.td @@ -263,3 +263,54 @@ multiclass ZAFPOuterProd { defm SVMOPA : ZAFPOuterProd<"mopa">; defm SVMOPS : ZAFPOuterProd<"mops">; + +//////////////////////////////////////////////////////////////////////////////// +// SME2 - ADD, SUB + +multiclass ZAAddSub { + let TargetGuard = "sme2" in { + def NAME # _WRITE_SINGLE_ZA32_VG1X2_I32 : Inst<"sv" # n_suffix # "_write[_single]_za32[_{d}]_vg1x2", "vm2d", "iUi", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x2", [IsStreaming, IsSharedZA], []>; + def NAME # _WRITE_SINGLE_ZA32_VG1X4_I32 : Inst<"sv" # n_suffix # "_write[_single]_za32[_{d}]_vg1x4", "vm4d", "iUi", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x4", [IsStreaming, IsSharedZA], []>; + + def NAME # _WRITE_ZA32_VG1X2_I32 : Inst<"sv" # n_suffix # "_write_za32[_{d}]_vg1x2", "vm22", "iUi", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x2", [IsStreaming, IsSharedZA], []>; + def NAME # _WRITE_ZA32_VG1X4_I32 : Inst<"sv" # n_suffix # "_write_za32[_{d}]_vg1x4", "vm44", "iUi", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x4", [IsStreaming, IsSharedZA], []>; + + def NAME # _ZA32_VG1x2_I32 : Inst<"sv" # n_suffix # "_za32[_{d}]_vg1x2", "vm2", "iUif", MergeNone, "aarch64_sme_" # n_suffix # "_za32_vg1x2", [IsStreaming, IsSharedZA], []>; + def NAME # _ZA32_VG1X4_I32 : Inst<"sv" # n_suffix # "_za32[_{d}]_vg1x4", "vm4", "iUif", MergeNone, "aarch64_sme_" # n_suffix # "_za32_vg1x4", [IsStreaming, IsSharedZA], []>; + + let TargetGuard = "sme-i16i64" in { + def NAME # _WRITE_SINGLE_ZA64_VG1X2_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x2", "vm2d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x2", [IsStreaming, IsSharedZA], []>; + def NAME # _WRITE_SINGLE_ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_write[_single]_za64[_{d}]_vg1x4", "vm4d", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_single_za_vg1x4", [IsStreaming, IsSharedZA], []>; + + def NAME # _WRITE_ZA64_VG1x2_I64 : Inst<"sv" # n_suffix # "_write_za64[_{d}]_vg1x2", "vm22", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x2", [IsStreaming, IsSharedZA], []>; + def NAME # _WRITE_ZA64_VG1x4_I64 : Inst<"sv" # n_suffix # "_write_za64[_{d}]_vg1x4", "vm44", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_write_za_vg1x4", [IsStreaming, IsSharedZA], []>; + + def NAME # _ZA64_VG1X2_I64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x2", "vm2", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x2", [IsStreaming, IsSharedZA], []>; + def NAME # _ZA64_VG1X4_I64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "lUl", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsSharedZA], []>; + } + + let TargetGuard = "sme-f64f64" in { + def NAME # _ZA64_VG1X2_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x2", "vm2", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x2", [IsStreaming, IsSharedZA], []>; + def NAME # _ZA64_VG1X4_F64 : Inst<"sv" # n_suffix # "_za64[_{d}]_vg1x4", "vm4", "d", MergeNone, "aarch64_sme_" # n_suffix # "_za64_vg1x4", [IsStreaming, IsSharedZA], []>; + } + } +} + +defm SVADD : ZAAddSub<"add">; +defm SVSUB : ZAAddSub<"sub">; + +// +// Outer product and accumulate/subtract +// + +let TargetGuard = "sme2" in { + def SVSMOPA : Inst<"svmopa_za32[_{d}]_m", "viPPdd", "s", MergeNone, "aarch64_sme_smopa_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; + def SVUSMOPA : Inst<"svmopa_za32[_{d}]_m", "viPPdd", "Us", MergeNone, "aarch64_sme_umopa_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; + + def SVSMOPS : Inst<"svmops_za32[_{d}]_m", "viPPdd", "s", MergeNone, "aarch64_sme_smops_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; + def SVUSMOPS : Inst<"svmops_za32[_{d}]_m", "viPPdd", "Us", MergeNone, "aarch64_sme_umops_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; + + def SVBMOPA : Inst<"svbmopa_za32[_{d}]_m", "viPPdd", "iUi", MergeNone, "aarch64_sme_bmopa_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; + + def SVBMOPS : Inst<"svbmops_za32[_{d}]_m", "viPPdd", "iUi", MergeNone, "aarch64_sme_bmops_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; +} diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td index b5baafedd13960290befe191ae1b255915b5ad9f..4fcc9327f22fe614fa09d431a7926314644a33d1 100644 --- a/clang/include/clang/Basic/arm_sve.td +++ b/clang/include/clang/Basic/arm_sve.td @@ -298,6 +298,38 @@ let TargetGuard = "sve,bf16" in { def SVBFMLALT_LANE : SInst<"svbfmlalt_lane[_{0}]", "MMddi", "b", MergeNone, "aarch64_sve_bfmlalt_lane_v2", [IsOverloadNone], [ImmCheck<3, ImmCheck0_7>]>; } +let TargetGuard = "sve2p1" in { + // Contiguous zero-extending load to quadword (single vector). + def SVLD1UWQ : MInst<"svld1uwq[_{d}]", "dPc", "iUif", [IsLoad], MemEltTyInt32, "aarch64_sve_ld1uwq">; + def SVLD1UWQ_VNUM : MInst<"svld1uwq_vnum[_{d}]", "dPcl", "iUif", [IsLoad], MemEltTyInt32, "aarch64_sve_ld1uwq">; + + def SVLD1UDQ : MInst<"svld1udq[_{d}]", "dPc", "lUld", [IsLoad], MemEltTyInt64, "aarch64_sve_ld1udq">; + def SVLD1UDQ_VNUM : MInst<"svld1udq_vnum[_{d}]", "dPcl", "lUld", [IsLoad], MemEltTyInt64, "aarch64_sve_ld1udq">; + + // Load one vector (vector base + scalar offset) + def SVLD1Q_GATHER_U64BASE_OFFSET : MInst<"svld1q_gather[_{2}base]_offset_{d}", "dPgl", "cUcsUsiUilUlfhdb", [IsGatherLoad, IsByteIndexed], MemEltTyDefault, "aarch64_sve_ld1q_gather_scalar_offset">; + def SVLD1Q_GATHER_U64BASE : MInst<"svld1q_gather[_{2}base]_{d}", "dPg", "cUcsUsiUilUlfhdb", [IsGatherLoad, IsByteIndexed], MemEltTyDefault, "aarch64_sve_ld1q_gather_scalar_offset">; + + // Load one vector (scalar base + vector offset) + def SVLD1Q_GATHER_U64OFFSET : MInst<"svld1q_gather_[{3}]offset[_{d}]", "dPcg", "cUcsUsiUilUlfhdb", [IsGatherLoad, IsByteIndexed], MemEltTyDefault, "aarch64_sve_ld1q_gather_vector_offset">; + + // Load N-element structure into N vectors (scalar base) + defm SVLD2Q : StructLoad<"svld2q[_{2}]", "2Pc", "aarch64_sve_ld2q_sret">; + defm SVLD3Q : StructLoad<"svld3q[_{2}]", "3Pc", "aarch64_sve_ld3q_sret">; + defm SVLD4Q : StructLoad<"svld4q[_{2}]", "4Pc", "aarch64_sve_ld4q_sret">; + + // Load N-element structure into N vectors (scalar base, VL displacement) + defm SVLD2Q_VNUM : StructLoad<"svld2q_vnum[_{2}]", "2Pcl", "aarch64_sve_ld2q_sret">; + defm SVLD3Q_VNUM : StructLoad<"svld3q_vnum[_{2}]", "3Pcl", "aarch64_sve_ld3q_sret">; + defm SVLD4Q_VNUM : StructLoad<"svld4q_vnum[_{2}]", "4Pcl", "aarch64_sve_ld4q_sret">; + + // Load quadwords (scalar base + vector index) + def SVLD1Q_GATHER_INDICES_U : MInst<"svld1q_gather_[{3}]index[_{d}]", "dPcg", "sUsiUilUlbhfd", [IsGatherLoad], MemEltTyDefault, "aarch64_sve_ld1q_gather_index">; + + // Load quadwords (vector base + scalar index) + def SVLD1Q_GATHER_INDEX_S : MInst<"svld1q_gather[_{2}base]_index_{d}", "dPgl", "sUsiUilUlbhfd", [IsGatherLoad], MemEltTyDefault, "aarch64_sve_ld1q_gather_scalar_offset">; +} + //////////////////////////////////////////////////////////////////////////////// // Stores @@ -420,6 +452,38 @@ let TargetGuard = "sve,bf16" in { def SVSTNT1_VNUM_BF : MInst<"svstnt1_vnum[_{d}]", "vPpld", "b", [IsStore], MemEltTyDefault, "aarch64_sve_stnt1">; } +let TargetGuard = "sve2p1" in { + // Contiguous truncating store from quadword (single vector). + def SVST1UWQ : MInst<"svst1uwq[_{d}]", "vPcd", "iUif", [IsStore], MemEltTyInt32, "aarch64_sve_st1uwq">; + def SVST1UWQ_VNUM : MInst<"svst1uwq_vnum[_{d}]", "vPcld", "iUif", [IsStore], MemEltTyInt32, "aarch64_sve_st1uwq">; + + def SVST1UDQ : MInst<"svst1udq[_{d}]", "vPcd", "lUld", [IsStore], MemEltTyInt64, "aarch64_sve_st1udq">; + def SVST1UDQ_VNUM : MInst<"svst1udq_vnum[_{d}]", "vPcld", "lUld", [IsStore], MemEltTyInt64, "aarch64_sve_st1udq">; + + // Store one vector (vector base + scalar offset) + def SVST1Q_SCATTER_U64BASE_OFFSET : MInst<"svst1q_scatter[_{2}base]_offset[_{d}]", "vPgld", "cUcsUsiUilUlfhdb", [IsScatterStore, IsByteIndexed], MemEltTyDefault, "aarch64_sve_st1q_scatter_scalar_offset">; + def SVST1Q_SCATTER_U64BASE : MInst<"svst1q_scatter[_{2}base][_{d}]", "vPgd", "cUcsUsiUilUlfhdb", [IsScatterStore, IsByteIndexed], MemEltTyDefault, "aarch64_sve_st1q_scatter_scalar_offset">; + + // Store one vector (scalar base + vector offset) + def SVST1Q_SCATTER_U64OFFSET : MInst<"svst1q_scatter_[{3}]offset[_{d}]", "vPpgd", "cUcsUsiUilUlfhdb", [IsScatterStore, IsByteIndexed], MemEltTyDefault, "aarch64_sve_st1q_scatter_vector_offset">; + + // Store N vectors into N-element structure (scalar base) + defm SVST2Q : StructStore<"svst2q[_{d}]", "vPc2", "aarch64_sve_st2q">; + defm SVST3Q : StructStore<"svst3q[_{d}]", "vPc3", "aarch64_sve_st3q">; + defm SVST4Q : StructStore<"svst4q[_{d}]", "vPc4", "aarch64_sve_st4q">; + + // Store N vectors into N-element structure (scalar base, VL displacement) + defm SVST2Q_VNUM : StructStore<"svst2q_vnum[_{d}]", "vPcl2", "aarch64_sve_st2q">; + defm SVST3Q_VNUM : StructStore<"svst3q_vnum[_{d}]", "vPcl3", "aarch64_sve_st3q">; + defm SVST4Q_VNUM : StructStore<"svst4q_vnum[_{d}]", "vPcl4", "aarch64_sve_st4q">; + + // Scatter store quadwords (scalar base + vector index) + def SVST1Q_SCATTER_INDICES_U : MInst<"svst1q_scatter_[{3}]index[_{d}]", "vPpgd", "sUsiUilUlbhfd", [IsScatterStore], MemEltTyDefault, "aarch64_sve_st1q_scatter_index">; + + // Scatter store quadwords (vector base + scalar index) + def SVST1Q_SCATTER_INDEX_S : MInst<"svst1q_scatter[_{2}base]_index[_{d}]", "vPgld", "sUsiUilUlbhfd", [IsScatterStore], MemEltTyDefault, "aarch64_sve_st1q_scatter_scalar_offset">; +} + //////////////////////////////////////////////////////////////////////////////// // Prefetches @@ -1980,3 +2044,108 @@ def SVCNTP_COUNT : SInst<"svcntp_{d}", "n}i", "QcQsQiQl", MergeNone, "aarch64_sv defm SVREVD : SInstZPZ<"svrevd", "csilUcUsUiUl", "aarch64_sve_revd">; } + + +let TargetGuard = "sve2p1,b16b16" in { +defm SVMUL_BF : SInstZPZZ<"svmul", "b", "aarch64_sve_fmul", "aarch64_sve_fmul_u">; +defm SVADD_BF : SInstZPZZ<"svadd", "b", "aarch64_sve_fadd", "aarch64_sve_fadd_u">; +defm SVSUB_BF : SInstZPZZ<"svsub", "b", "aarch64_sve_fsub", "aarch64_sve_fsub_u">; +defm SVMAXNM_BF : SInstZPZZ<"svmaxnm","b", "aarch64_sve_fmaxnm", "aarch64_sve_fmaxnm_u">; +defm SVMINNM_BF : SInstZPZZ<"svminnm","b", "aarch64_sve_fminnm", "aarch64_sve_fminnm_u">; +defm SVMAX_BF : SInstZPZZ<"svmax", "b", "aarch64_sve_fmax", "aarch64_sve_fmax_u">; +defm SVMIN_BF : SInstZPZZ<"svmin", "b", "aarch64_sve_fmin", "aarch64_sve_fmin_u">; +defm SVMLA_BF : SInstZPZZZ<"svmla", "b", "aarch64_sve_fmla", "aarch64_sve_fmla_u", []>; +defm SVMLS_BF : SInstZPZZZ<"svmls", "b", "aarch64_sve_fmls", "aarch64_sve_fmls_u", []>; +def SVMLA_LANE_BF : SInst<"svmla_lane[_{d}]", "ddddi", "b", MergeNone, "aarch64_sve_fmla_lane", [], [ImmCheck<3, ImmCheckLaneIndex, 2>]>; +def SVMLS_LANE_BF : SInst<"svmls_lane[_{d}]", "ddddi", "b", MergeNone, "aarch64_sve_fmls_lane", [], [ImmCheck<3, ImmCheckLaneIndex, 2>]>; +def SVMUL_LANE_BF : SInst<"svmul_lane[_{d}]", "dddi", "b", MergeNone, "aarch64_sve_fmul_lane", [], [ImmCheck<2, ImmCheckLaneIndex, 1>]>; +def SVFCLAMP_BF : SInst<"svclamp[_{d}]", "dddd", "b", MergeNone, "aarch64_sve_fclamp", [], []>; +} //sve2p1,b16b16 + +// SME2 + +// SME intrinsics which operate only on vectors and do not require ZA should be added here, +// as they could possibly become SVE instructions in the future. + +multiclass MinMaxIntr { + def SVS # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "csil", MergeNone, "aarch64_sve_s" # i # zm # "_" # mul, [IsStreaming], []>; + def SVU # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "UcUsUiUl", MergeNone, "aarch64_sve_u" # i # zm # "_" # mul, [IsStreaming], []>; + def SVF # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "hfd", MergeNone, "aarch64_sve_f" # i # zm # "_" # mul, [IsStreaming], []>; +} + +let TargetGuard = "sme2" in { +// == SMAX / UMAX / FMAX == + defm MAX_SINGLE_X2 : MinMaxIntr<"max", "_single", "x2", "22d">; + defm MAX_MULTI_X2 : MinMaxIntr<"max", "", "x2", "222">; + defm MAX_SINGLE_X4 : MinMaxIntr<"max", "_single", "x4", "44d">; + defm MAX_MULTI_X4 : MinMaxIntr<"max", "", "x4", "444">; + +// == SMIN / UMIN / FMIN == + defm MIN_SINGLE_X2 : MinMaxIntr<"min", "_single", "x2", "22d">; + defm MIN_MULTI_X2 : MinMaxIntr<"min", "", "x2", "222">; + defm MIN_SINGLE_X4 : MinMaxIntr<"min", "_single", "x4", "44d">; + defm MIN_MULTI_X4 : MinMaxIntr<"min", "", "x4", "444">; +} + +multiclass SInstMinMaxByVector { + def NAME # _SINGLE_X2 : SInst<"sv" # name # "nm[_single_{d}_x2]", "22d", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_single_x2", [IsStreaming], []>; + def NAME # _SINGLE_X4 : SInst<"sv" # name # "nm[_single_{d}_x4]", "44d", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_single_x4", [IsStreaming], []>; + + def NAME # _X2 : SInst<"sv" # name # "nm[_{d}_x2]", "222", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_x2", [IsStreaming], []>; + def NAME # _X4 : SInst<"sv" # name # "nm[_{d}_x4]", "444", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_x4", [IsStreaming], []>; +} + +let TargetGuard = "sme2" in { +// == FMINNM / FMAXNM == + defm SVMINNM : SInstMinMaxByVector<"min">; + defm SVMAXNM : SInstMinMaxByVector<"max">; +} + +let TargetGuard = "sme2" in { + def SVSCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "csil", MergeNone, "aarch64_sve_sclamp_single_x2", [IsStreaming], []>; + def SVUCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp_single_x2", [IsStreaming], []>; + def SVFCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "hfd", MergeNone, "aarch64_sve_fclamp_single_x2", [IsStreaming], []>; + + def SVSCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "csil", MergeNone, "aarch64_sve_sclamp_single_x4", [IsStreaming], []>; + def SVUCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "UcUsUiUl", MergeNone, "aarch64_sve_uclamp_single_x4", [IsStreaming], []>; + def SVFCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "hfd", MergeNone, "aarch64_sve_fclamp_single_x4", [IsStreaming], []>; +} + +let TargetGuard = "sme2" in { +// == ADD (vectors) == + def SVADD_SINGLE_X2 : SInst<"svadd[_single_{d}_x2]", "22d", "cUcsUsiUilUl", MergeNone, "aarch64_sve_add_single_x2", [IsStreaming], []>; + def SVADD_SINGLE_X4 : SInst<"svadd[_single_{d}_x4]", "44d", "cUcsUsiUilUl", MergeNone, "aarch64_sve_add_single_x4", [IsStreaming], []>; +} + +let TargetGuard = "sve2p1" in { + // ZIPQ1, ZIPQ2, UZPQ1, UZPQ2 + def SVZIPQ1 : SInst<"svzipq1[_{d}]", "ddd", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_zipq1", [], []>; + def SVZIPQ2 : SInst<"svzipq2[_{d}]", "ddd", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_zipq2", [], []>; + def SVUZPQ1 : SInst<"svuzpq1[_{d}]", "ddd", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_uzpq1", [], []>; + def SVUZPQ2 : SInst<"svuzpq2[_{d}]", "ddd", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_uzpq2", [], []>; + // TBLQ, TBXQ + def SVTBLQ : SInst<"svtblq[_{d}]", "ddu", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_tblq">; + def SVTBXQ : SInst<"svtbxq[_{d}]", "dddu", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_tbxq">; + // EXTQ + def EXTQ : SInst<"svextq_lane[_{d}]", "dddk", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_extq_lane", [], [ImmCheck<2, ImmCheck0_15>]>; + // PMOV + // Move to Pred + multiclass PMOV_TO_PRED flags=[], ImmCheckType immCh > { + def _LANE : Inst]>; + def _LANE_ZERO : SInst; + } + defm SVPMOV_B_TO_PRED : PMOV_TO_PRED<"svpmov", "cUc", "aarch64_sve_pmov_to_pred_lane", [], ImmCheck0_0>; + defm SVPMOV_H_TO_PRED : PMOV_TO_PRED<"svpmov", "sUs", "aarch64_sve_pmov_to_pred_lane", [], ImmCheck0_1>; + defm SVPMOV_S_TO_PRED : PMOV_TO_PRED<"svpmov", "iUi", "aarch64_sve_pmov_to_pred_lane", [], ImmCheck0_3>; + defm SVPMOV_D_TO_PRED : PMOV_TO_PRED<"svpmov", "lUl", "aarch64_sve_pmov_to_pred_lane", [], ImmCheck0_7>; + + // Move to Vector + multiclass PMOV_TO_VEC flags=[], ImmCheckType immCh > { + def _M : SInst]>; + def _Z : SInst; + } + def SVPMOV_TO_VEC_LANE_B : SInst<"svpmov_{d}_z", "dP", "cUc", MergeNone, "aarch64_sve_pmov_to_vector_lane_zeroing", [], []>; + defm SVPMOV_TO_VEC_LANE_H : PMOV_TO_VEC<"svpmov", "sUs", "aarch64_sve_pmov_to_vector_lane", [], ImmCheck1_1>; + defm SVPMOV_TO_VEC_LANE_S : PMOV_TO_VEC<"svpmov", "iUi", "aarch64_sve_pmov_to_vector_lane", [], ImmCheck1_3>; + defm SVPMOV_TO_VEC_LANE_D : PMOV_TO_VEC<"svpmov", "lUl", "aarch64_sve_pmov_to_vector_lane" ,[], ImmCheck1_7>; +} diff --git a/clang/include/clang/Basic/arm_sve_sme_incl.td b/clang/include/clang/Basic/arm_sve_sme_incl.td index 3a7a5b51b25801e6b826fc65ab96c895fe43a00a..21dac067ab66e615354da7701e08162bc06f886e 100644 --- a/clang/include/clang/Basic/arm_sve_sme_incl.td +++ b/clang/include/clang/Basic/arm_sve_sme_incl.td @@ -249,6 +249,9 @@ def ImmCheck0_0 : ImmCheckType<16>; // 0..0 def ImmCheck0_15 : ImmCheckType<17>; // 0..15 def ImmCheck0_255 : ImmCheckType<18>; // 0..255 def ImmCheck2_4_Mul2 : ImmCheckType<19>; // 2, 4 +def ImmCheck1_1 : ImmCheckType<20>; // 1..1 +def ImmCheck1_3 : ImmCheckType<21>; // 1..3 +def ImmCheck1_7 : ImmCheckType<22>; // 1..7 class ImmCheck { int Arg = arg; @@ -257,7 +260,7 @@ class ImmCheck { } class Inst ft, list ch, MemEltType met> { + list ft, list ch, MemEltType met = MemEltTyDefault> { string Name = n; string Prototype = p; string Types = t; diff --git a/clang/include/clang/Basic/riscv_sifive_vector.td b/clang/include/clang/Basic/riscv_sifive_vector.td index 1e081c734d4941b13f7cc12383a4bb728d11f83c..bb54e26641861480e41c517853f321fb65a0712b 100644 --- a/clang/include/clang/Basic/riscv_sifive_vector.td +++ b/clang/include/clang/Basic/riscv_sifive_vector.td @@ -78,29 +78,29 @@ let SupportOverloading = false in { defm sf_vc_iv : RVVVCIXBuiltinSet<["csi", "l"], "0KzKzUvKz", [0, 2, 3], UseGPR=0>; defm sf_vc_vv : RVVVCIXBuiltinSet<["csi", "l"], "0KzKzUvUv", [0, 2, 3], UseGPR=0>; defm sf_vc_fv : RVVVCIXBuiltinSet<["si", "l"], "0KzKzUvFe", [0, 2, 3], UseGPR=0>; - defm sf_vc_xvv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvUe", [0, 1, 3], UseGPR=1>; - defm sf_vc_ivv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvKz", [0, 1, 3], UseGPR=0>; - defm sf_vc_vvv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvUv", [0, 1, 3], UseGPR=0>; - defm sf_vc_fvv : RVVVCIXBuiltinSet<["si", "l"], "0KzUvUvFe", [0, 1, 3], UseGPR=0>; + defm sf_vc_xvv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvUe", [0, 1, 2, 3], UseGPR=1>; + defm sf_vc_ivv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvKz", [0, 1, 2, 3], UseGPR=0>; + defm sf_vc_vvv : RVVVCIXBuiltinSet<["csi", "l"], "0KzUvUvUv", [0, 1, 2, 3], UseGPR=0>; + defm sf_vc_fvv : RVVVCIXBuiltinSet<["si", "l"], "0KzUvUvFe", [0, 1, 2, 3], UseGPR=0>; defm sf_vc_v_x : RVVVCIXBuiltinSet<["csi", "l"], "UvKzKzUe", [-1, 1, 2], UseGPR=1>; defm sf_vc_v_i : RVVVCIXBuiltinSet<["csi", "l"], "UvKzKzKz", [-1, 1, 2], UseGPR=0>; - defm sf_vc_v_xv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUe", [-1, 0, 2], UseGPR=1>; - defm sf_vc_v_iv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvKz", [-1, 0, 2], UseGPR=0>; - defm sf_vc_v_vv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUv", [-1, 0, 2], UseGPR=0>; - defm sf_vc_v_fv : RVVVCIXBuiltinSet<["si", "l"], "UvKzUvFe", [-1, 0, 2], UseGPR=0>; - defm sf_vc_v_xvv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvUe", [-1, 0, 3], UseGPR=1>; - defm sf_vc_v_ivv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvKz", [-1, 0, 3], UseGPR=0>; - defm sf_vc_v_vvv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvUv", [-1, 0, 3], UseGPR=0>; - defm sf_vc_v_fvv : RVVVCIXBuiltinSet<["si", "l"], "UvKzUvUvFe", [-1, 0, 3], UseGPR=0>; + defm sf_vc_v_xv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUe", [-1, 0, 1, 2], UseGPR=1>; + defm sf_vc_v_iv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvKz", [-1, 0, 1, 2], UseGPR=0>; + defm sf_vc_v_vv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUv", [-1, 0, 1, 2], UseGPR=0>; + defm sf_vc_v_fv : RVVVCIXBuiltinSet<["si", "l"], "UvKzUvFe", [-1, 0, 1, 2], UseGPR=0>; + defm sf_vc_v_xvv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvUe", [-1, 0, 1, 2, 3], UseGPR=1>; + defm sf_vc_v_ivv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvKz", [-1, 0, 1, 2, 3], UseGPR=0>; + defm sf_vc_v_vvv : RVVVCIXBuiltinSet<["csi", "l"], "UvKzUvUvUv", [-1, 0, 1, 2, 3], UseGPR=0>; + defm sf_vc_v_fvv : RVVVCIXBuiltinSet<["si", "l"], "UvKzUvUvFe", [-1, 0, 1, 2, 3], UseGPR=0>; let Log2LMUL = [-3, -2, -1, 0, 1, 2] in { defm sf_vc_xvw : RVVVCIXBuiltinSet<["csi"], "0KzUwUvUe", [0, 1, 2, 3], UseGPR=1>; defm sf_vc_ivw : RVVVCIXBuiltinSet<["csi"], "0KzUwUvKz", [0, 1, 2, 3], UseGPR=0>; defm sf_vc_vvw : RVVVCIXBuiltinSet<["csi"], "0KzUwUvUv", [0, 1, 2, 3], UseGPR=0>; defm sf_vc_fvw : RVVVCIXBuiltinSet<["si"], "0KzUwUvFe", [0, 1, 2, 3], UseGPR=0>; - defm sf_vc_v_xvw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvUe", [-1, 0, 2, 3], UseGPR=1>; - defm sf_vc_v_ivw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvKz", [-1, 0, 2, 3], UseGPR=0>; - defm sf_vc_v_vvw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvUv", [-1, 0, 2, 3], UseGPR=0>; - defm sf_vc_v_fvw : RVVVCIXBuiltinSet<["si"], "UwKzUwUvFe", [-1, 0, 2, 3], UseGPR=0>; + defm sf_vc_v_xvw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvUe", [-1, 0, 1, 2, 3], UseGPR=1>; + defm sf_vc_v_ivw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvKz", [-1, 0, 1, 2, 3], UseGPR=0>; + defm sf_vc_v_vvw : RVVVCIXBuiltinSet<["csi"], "UwKzUwUvUv", [-1, 0, 1, 2, 3], UseGPR=0>; + defm sf_vc_v_fvw : RVVVCIXBuiltinSet<["si"], "UwKzUwUvFe", [-1, 0, 1, 2, 3], UseGPR=0>; } } @@ -109,7 +109,7 @@ multiclass RVVVFWMACCBuiltinSet> suffixes_prototypes> { Name = NAME, HasMasked = false, Log2LMUL = [-2, -1, 0, 1, 2] in - defm NAME : RVVOutOp1Op2BuiltinSet; + defm NAME : RVVOutOp1Op2BuiltinSet; } multiclass RVVVQMACCBuiltinSet> suffixes_prototypes> { @@ -146,7 +146,7 @@ let UnMaskedPolicyScheme = HasPolicyOperand in let UnMaskedPolicyScheme = HasPolicyOperand in let RequiredFeatures = ["Xsfvfwmaccqqq"] in - defm sf_vfwmacc_4x4x4 : RVVVFWMACCBuiltinSet<[["", "w", "wwSvv"]]>; + defm sf_vfwmacc_4x4x4 : RVVVFWMACCBuiltinSet<[["", "Fw", "FwFwSvv"]]>; let UnMaskedPolicyScheme = HasPassthruOperand, RequiredFeatures = ["Xsfvfnrclipxfqf"] in { let ManualCodegen = [{ diff --git a/clang/include/clang/Basic/riscv_vector.td b/clang/include/clang/Basic/riscv_vector.td index 1d24b4e75f9dd5f70424c403e9737341d8ff892b..682f1d5c8af68c08e376b6226582d687930bdd0b 100644 --- a/clang/include/clang/Basic/riscv_vector.td +++ b/clang/include/clang/Basic/riscv_vector.td @@ -69,7 +69,6 @@ multiclass RVVVLEFFBuiltin types> { Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType)); IntrinsicTypes = {ResultType, Ops[3]->getType()}; } - Ops[1] = Builder.CreateBitCast(Ops[1], ResultType->getPointerTo()); Value *NewVL = Ops[2]; Ops.erase(Ops.begin() + 2); llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes); @@ -150,7 +149,6 @@ let HasMaskedOffOperand = false, // Builtin: (ptr, value, vl). Intrinsic: (value, ptr, vl) std::swap(Ops[0], Ops[1]); } - Ops[1] = Builder.CreateBitCast(Ops[1], Ops[0]->getType()->getPointerTo()); if (IsMasked) IntrinsicTypes = {Ops[0]->getType(), Ops[3]->getType()}; else @@ -189,7 +187,6 @@ multiclass RVVVSSEBuiltin types> { // Builtin: (ptr, stride, value, vl). Intrinsic: (value, ptr, stride, vl) std::rotate(Ops.begin(), Ops.begin() + 2, Ops.begin() + 3); } - Ops[1] = Builder.CreateBitCast(Ops[1], Ops[0]->getType()->getPointerTo()); if (IsMasked) IntrinsicTypes = {Ops[0]->getType(), Ops[4]->getType()}; else @@ -215,7 +212,6 @@ multiclass RVVIndexedStore { // Builtin: (ptr, index, value, vl). Intrinsic: (value, ptr, index, vl) std::rotate(Ops.begin(), Ops.begin() + 2, Ops.begin() + 3); } - Ops[1] = Builder.CreateBitCast(Ops[1], Ops[0]->getType()->getPointerTo()); if (IsMasked) IntrinsicTypes = {Ops[0]->getType(), Ops[2]->getType(), Ops[4]->getType()}; else diff --git a/clang/include/clang/Basic/riscv_vector_common.td b/clang/include/clang/Basic/riscv_vector_common.td index 326c3883f0a8409cbf888496f888919afb5e3519..4036ce8e6903f426c81adbfa840a2a90c9123dcc 100644 --- a/clang/include/clang/Basic/riscv_vector_common.td +++ b/clang/include/clang/Basic/riscv_vector_common.td @@ -41,6 +41,7 @@ // x: float16_t (half) // f: float32_t (float) // d: float64_t (double) +// b: bfloat16_t (bfloat16) // // This way, given an LMUL, a record with a TypeRange "sil" will cause the // definition of 3 builtins. Each type "t" in the TypeRange (in this example diff --git a/clang/include/clang/CodeGen/BackendUtil.h b/clang/include/clang/CodeGen/BackendUtil.h index cdbfe4ca5e654bdd1b3ea2b3ac16cbc8ff94ae1a..fc8ed4f011f922f506a526804c972360a10f6e9b 100644 --- a/clang/include/clang/CodeGen/BackendUtil.h +++ b/clang/include/clang/CodeGen/BackendUtil.h @@ -30,6 +30,7 @@ namespace clang { class CodeGenOptions; class TargetOptions; class LangOptions; + class BackendConsumer; enum BackendAction { Backend_EmitAssembly, ///< Emit native assembly files @@ -45,7 +46,8 @@ namespace clang { const TargetOptions &TOpts, const LangOptions &LOpts, StringRef TDesc, llvm::Module *M, BackendAction Action, llvm::IntrusiveRefCntPtr VFS, - std::unique_ptr OS); + std::unique_ptr OS, + BackendConsumer *BC = nullptr); void EmbedBitcode(llvm::Module *M, const CodeGenOptions &CGOpts, llvm::MemoryBufferRef Buf); diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 9e0d0fa0d8e824349fee74274d04a8e118bc4687..498acfd38013183eeac88c7440cb28c00b7c01be 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -204,11 +204,6 @@ public: add(llvm::ConstantPointerNull::get(ptrTy)); } - /// Add a bitcast of a value to a specific type. - void addBitCast(llvm::Constant *value, llvm::Type *type) { - add(llvm::ConstantExpr::getBitCast(value, type)); - } - /// Add a bunch of new values to this initializer. void addAll(llvm::ArrayRef values) { assert(!Finished && "cannot add more values after finishing builder"); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 9c0dcaa8b3f6276aca19d6323560f1e76834c5aa..7dd2755350f7a56a765f0cec8e0d6317b4b8ee51 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1146,6 +1146,14 @@ defm gpu_rdc : BoolFOption<"gpu-rdc", "Generate relocatable device code, also known as separate compilation mode">, NegFlag>; +defm offload_implicit_host_device_templates : + BoolFOption<"offload-implicit-host-device-templates", + LangOpts<"OffloadImplicitHostDeviceTemplates">, DefaultFalse, + PosFlag, + NegFlag>; + def fgpu_default_stream_EQ : Joined<["-"], "fgpu-default-stream=">, HelpText<"Specify default stream. The default value is 'legacy'. (CUDA/HIP only)">, Visibility<[ClangOption, CC1Option]>, @@ -1351,6 +1359,19 @@ def fno_hip_emit_relocatable : Flag<["-"], "fno-hip-emit-relocatable">, HelpText<"Do not override toolchain to compile HIP source to relocatable">; } +// Clang specific/exclusive options for OpenACC. +def openacc_macro_override + : Separate<["-"], "fexperimental-openacc-macro-override">, + Visibility<[ClangOption, CC1Option]>, + Group, + HelpText<"Overrides the _OPENACC macro value for experimental testing " + "during OpenACC support development">; +def openacc_macro_override_EQ + : Joined<["-"], "fexperimental-openacc-macro-override=">, + Alias; + +// End Clang specific/exclusive options for OpenACC. + def libomptarget_amdgpu_bc_path_EQ : Joined<["--"], "libomptarget-amdgpu-bc-path=">, Group, HelpText<"Path to libomptarget-amdgcn bitcode library">; def libomptarget_amdgcn_bc_path_EQ : Joined<["--"], "libomptarget-amdgcn-bc-path=">, Group, @@ -1431,7 +1452,7 @@ def extract_api_ignores_EQ: CommaJoined<["--"], "extract-api-ignores=">, Visibility<[ClangOption, CC1Option]>, HelpText<"Comma separated list of files containing a new line separated list of API symbols to ignore when extracting API information.">, MarshallingInfoStringVector>; -def e : JoinedOrSeparate<["-"], "e">, Flags<[LinkerInput]>, Group; +def e : Separate<["-"], "e">, Flags<[LinkerInput]>, Group; def fmax_tokens_EQ : Joined<["-"], "fmax-tokens=">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Max total number of preprocessed tokens for -Wmax-tokens.">, @@ -1733,6 +1754,18 @@ def fswift_async_fp_EQ : Joined<["-"], "fswift-async-fp=">, NormalizedValuesScope<"CodeGenOptions::SwiftAsyncFramePointerKind">, NormalizedValues<["Auto", "Always", "Never"]>, MarshallingInfoEnum, "Always">; +defm apinotes : BoolOption<"f", "apinotes", + LangOpts<"APINotes">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], "external API notes support">>, + Group; +defm apinotes_modules : BoolOption<"f", "apinotes-modules", + LangOpts<"APINotesModules">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], "module-based external API notes support">>, + Group; def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, Group, Visibility<[ClangOption, CC1Option]>, MetaVarName<"">, @@ -1997,6 +2030,10 @@ def fignore_exceptions : Flag<["-"], "fignore-exceptions">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Enable support for ignoring exception handling constructs">, MarshallingInfoFlag>; +defm assume_nothrow_exception_dtor: BoolFOption<"assume-nothrow-exception-dtor", + LangOpts<"AssumeNothrowExceptionDtor">, DefaultFalse, + PosFlag, + NegFlag>; def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group, Visibility<[ClangOption, CLOption]>, HelpText<"Allows control over excess precision on targets where native " @@ -2105,7 +2142,7 @@ defm fixed_point : BoolFOption<"fixed-point", LangOpts<"FixedPoint">, DefaultFalse, PosFlag, NegFlag, - BothFlags<[], [ClangOption], " fixed point types">>, ShouldParseIf; + BothFlags<[], [ClangOption], " fixed point types">>; defm cxx_static_destructors : BoolFOption<"c++-static-destructors", LangOpts<"RegisterStaticDestructors">, DefaultTrue, NegFlag; def fms_runtime_lib_EQ : Joined<["-"], "fms-runtime-lib=">, Group, - Flags<[]>, Visibility<[ClangOption, CLOption]>, + Flags<[]>, Visibility<[ClangOption, CLOption, FlangOption]>, Values<"static,static_dbg,dll,dll_dbg">, HelpText<"Select Windows run-time library">, DocBrief<[{ @@ -3114,10 +3151,10 @@ def fno_global_isel : Flag<["-"], "fno-global-isel">, Group, def fno_experimental_isel : Flag<["-"], "fno-experimental-isel">, Group, Alias; def fveclib : Joined<["-"], "fveclib=">, Group, - Visibility<[ClangOption, CC1Option]>, + Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>, HelpText<"Use the given vector functions library">, Values<"Accelerate,libmvec,MASSV,SVML,SLEEF,Darwin_libsystem_m,ArmPL,none">, - NormalizedValuesScope<"CodeGenOptions">, + NormalizedValuesScope<"llvm::driver::VectorLibrary">, NormalizedValues<["Accelerate", "LIBMVEC", "MASSV", "SVML", "SLEEF", "Darwin_libsystem_m", "ArmPL", "NoLibrary"]>, MarshallingInfoEnum, "NoLibrary">; @@ -3330,6 +3367,14 @@ def fno_openmp_target_debug : Flag<["-"], "fno-openmp-target-debug">; } // let Visibility = [ClangOption, CC1Option, FC1Option] } // let Flags = [NoArgumentUnused] +//===----------------------------------------------------------------------===// +// FlangOption + FC1 + ClangOption + CC1Option +//===----------------------------------------------------------------------===// +let Visibility = [FC1Option, FlangOption, CC1Option, ClangOption] in { +def fopenacc : Flag<["-"], "fopenacc">, Group, + HelpText<"Enable OpenACC">; +} // let Visibility = [FC1Option, FlangOption, CC1Option, ClangOption] + //===----------------------------------------------------------------------===// // Optimisation remark options //===----------------------------------------------------------------------===// @@ -4676,10 +4721,10 @@ defm amdgpu_ieee : BoolOption<"m", "amdgpu-ieee", def mcode_object_version_EQ : Joined<["-"], "mcode-object-version=">, Group, HelpText<"Specify code object ABI version. Defaults to 4. (AMDGPU only)">, - Visibility<[ClangOption, CC1Option]>, - Values<"none,3,4,5">, - NormalizedValuesScope<"TargetOptions">, - NormalizedValues<["COV_None", "COV_3", "COV_4", "COV_5"]>, + Visibility<[ClangOption, FlangOption, CC1Option, FC1Option]>, + Values<"none,4,5">, + NormalizedValuesScope<"llvm::CodeObjectVersionKind">, + NormalizedValues<["COV_None", "COV_4", "COV_5"]>, MarshallingInfoEnum, "COV_4">; defm cumode : SimpleMFlag<"cumode", @@ -4785,6 +4830,12 @@ def mrop_protect : Flag<["-"], "mrop-protect">, Group; def mprivileged : Flag<["-"], "mprivileged">, Group; + +defm regnames : BoolOption<"m", "regnames", + CodeGenOpts<"PPCUseFullRegisterNames">, DefaultFalse, + PosFlag, + NegFlag>, + Group; } // let Flags = [TargetSpecific] def maix_small_local_exec_tls : Flag<["-"], "maix-small-local-exec-tls">, Group, @@ -5119,7 +5170,7 @@ def nofixprebinding : Flag<["-"], "nofixprebinding">; def nolibc : Flag<["-"], "nolibc">; def nomultidefs : Flag<["-"], "nomultidefs">; def nopie : Flag<["-"], "nopie">, Visibility<[ClangOption, FlangOption]>; -def no_pie : Flag<["-"], "no-pie">, Visibility<[ClangOption, FlangOption]>, Alias; +def no_pie : Flag<["-"], "no-pie">, Visibility<[ClangOption, FlangOption]>; def noprebind : Flag<["-"], "noprebind">; def noprofilelib : Flag<["-"], "noprofilelib">; def noseglinkedit : Flag<["-"], "noseglinkedit">; @@ -5235,7 +5286,8 @@ def resource_dir : Separate<["-"], "resource-dir">, def resource_dir_EQ : Joined<["-"], "resource-dir=">, Flags<[NoXarchOption]>, Visibility<[ClangOption, CLOption, DXCOption]>, Alias; -def rpath : Separate<["-"], "rpath">, Flags<[LinkerInput]>, Group; +def rpath : Separate<["-"], "rpath">, Flags<[LinkerInput]>, Group, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>; def rtlib_EQ : Joined<["-", "--"], "rtlib=">, Visibility<[ClangOption, CLOption]>, HelpText<"Compiler runtime library to use">; def frtlib_add_rpath: Flag<["-"], "frtlib-add-rpath">, Flags<[NoArgumentUnused]>, @@ -5286,7 +5338,8 @@ def segs__read__only__addr : Separate<["-"], "segs_read_only_addr">; def segs__read__write__addr : Separate<["-"], "segs_read_write_addr">; def segs__read__ : Joined<["-"], "segs_read_">; def shared_libgcc : Flag<["-"], "shared-libgcc">; -def shared : Flag<["-", "--"], "shared">, Group; +def shared : Flag<["-", "--"], "shared">, Group, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>; def single__module : Flag<["-"], "single_module">; def specs_EQ : Joined<["-", "--"], "specs=">, Group; def specs : Separate<["-", "--"], "specs">, Flags<[Unsupported]>; @@ -5296,6 +5349,7 @@ def start_no_unused_arguments : Flag<["--"], "start-no-unused-arguments">, def static_libgcc : Flag<["-"], "static-libgcc">; def static_libstdcxx : Flag<["-"], "static-libstdc++">; def static : Flag<["-", "--"], "static">, Group, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, Flags<[NoArgumentUnused]>; def std_default_EQ : Joined<["-"], "std-default=">; def std_EQ : Joined<["-", "--"], "std=">, @@ -6200,6 +6254,10 @@ def flang_experimental_hlfir : Flag<["-"], "flang-experimental-hlfir">, Flags<[HelpHidden]>, Visibility<[FlangOption, FC1Option]>, HelpText<"Use HLFIR lowering (experimental)">; +def flang_deprecated_no_hlfir : Flag<["-"], "flang-deprecated-no-hlfir">, + Flags<[HelpHidden]>, Visibility<[FlangOption, FC1Option]>, + HelpText<"Do not use HLFIR lowering (deprecated)">; + def flang_experimental_polymorphism : Flag<["-"], "flang-experimental-polymorphism">, Flags<[HelpHidden]>, Visibility<[FlangOption, FC1Option]>, HelpText<"Enable Fortran 2003 polymorphism (experimental)">; @@ -6241,8 +6299,6 @@ file}]>; def ffixed_line_length_VALUE : Joined<["-"], "ffixed-line-length-">, Group, Alias; def fconvert_EQ : Joined<["-"], "fconvert=">, Group, HelpText<"Set endian conversion of data for unformatted files">; -def fopenacc : Flag<["-"], "fopenacc">, Group, - HelpText<"Enable OpenACC">; def fdefault_double_8 : Flag<["-"],"fdefault-double-8">, Group, HelpText<"Set the default double precision kind to an 8 byte wide type">; def fdefault_integer_8 : Flag<["-"],"fdefault-integer-8">, Group, @@ -6774,9 +6830,6 @@ def vectorize_loops : Flag<["-"], "vectorize-loops">, def vectorize_slp : Flag<["-"], "vectorize-slp">, HelpText<"Run the SLP vectorization passes">, MarshallingInfoFlag>; -def dependent_lib : Joined<["--"], "dependent-lib=">, - HelpText<"Add dependent library">, - MarshallingInfoStringVector>; def linker_option : Joined<["--"], "linker-option=">, HelpText<"Add linker option">, MarshallingInfoStringVector>; @@ -6944,9 +6997,6 @@ let Visibility = [CC1Option] in { def sys_header_deps : Flag<["-"], "sys-header-deps">, HelpText<"Include system headers in dependency output">, MarshallingInfoFlag>; -def canonical_system_headers : Flag<["-"], "canonical-system-headers">, - HelpText<"Canonicalize system headers in dependency output">, - MarshallingInfoFlag>; def module_file_deps : Flag<["-"], "module-file-deps">, HelpText<"Include module files in dependency output">, MarshallingInfoFlag>; @@ -7350,6 +7400,11 @@ def pic_is_pie : Flag<["-"], "pic-is-pie">, HelpText<"File is for a position independent executable">, MarshallingInfoFlag>; + +def dependent_lib : Joined<["--"], "dependent-lib=">, + HelpText<"Add dependent library">, + MarshallingInfoStringVector>; + } // let Visibility = [CC1Option, FC1Option] let Visibility = [CC1Option] in { diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h index 316d83df13e93594b7ec548e5f25eb962bf4c51a..d719196b9a43ecbc60fc9398559571f722578c90 100644 --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -24,6 +24,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/TypeLoc.h" #include "clang/Basic/Specifiers.h" #include "clang/Lex/MacroInfo.h" #include "llvm/ADT/SmallVector.h" @@ -410,6 +411,11 @@ private: /// Build DeclarationFragments for a parameter variable declaration /// ParmVarDecl. static DeclarationFragments getFragmentsForParam(const ParmVarDecl *); + + static DeclarationFragments + getFragmentsForBlock(const NamedDecl *BlockDecl, FunctionTypeLoc &Block, + FunctionProtoTypeLoc &BlockProto, + DeclarationFragments &After); }; template diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 3e9d1915badd87f9c250e6aac46b470ffe40441a..4fbcc4ef0705b6a10e8bad76afa734065ffc748b 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1424,33 +1424,103 @@ struct FormatStyle { /// \version 3.8 BraceWrappingFlags BraceWrapping; + /// Break between adjacent string literals. + /// \code + /// true: + /// return "Code" + /// "\0\52\26\55\55\0" + /// "x013" + /// "\02\xBA"; + /// false: + /// return "Code" "\0\52\26\55\55\0" "x013" "\02\xBA"; + /// \endcode + /// \version 18 + bool BreakAdjacentStringLiterals; + /// Different ways to break after attributes. enum AttributeBreakingStyle : int8_t { /// Always break after attributes. /// \code + /// [[maybe_unused]] + /// const int i; + /// [[gnu::const]] [[maybe_unused]] + /// int j; + /// /// [[nodiscard]] /// inline int f(); /// [[gnu::const]] [[nodiscard]] /// int g(); + /// + /// [[likely]] + /// if (a) + /// f(); + /// else + /// g(); + /// + /// switch (b) { + /// [[unlikely]] + /// case 1: + /// ++b; + /// break; + /// [[likely]] + /// default: + /// return; + /// } /// \endcode ABS_Always, /// Leave the line breaking after attributes as is. /// \code + /// [[maybe_unused]] const int i; + /// [[gnu::const]] [[maybe_unused]] + /// int j; + /// /// [[nodiscard]] inline int f(); /// [[gnu::const]] [[nodiscard]] /// int g(); + /// + /// [[likely]] if (a) + /// f(); + /// else + /// g(); + /// + /// switch (b) { + /// [[unlikely]] case 1: + /// ++b; + /// break; + /// [[likely]] + /// default: + /// return; + /// } /// \endcode ABS_Leave, /// Never break after attributes. /// \code + /// [[maybe_unused]] const int i; + /// [[gnu::const]] [[maybe_unused]] int j; + /// /// [[nodiscard]] inline int f(); /// [[gnu::const]] [[nodiscard]] int g(); + /// + /// [[likely]] if (a) + /// f(); + /// else + /// g(); + /// + /// switch (b) { + /// [[unlikely]] case 1: + /// ++b; + /// break; + /// [[likely]] default: + /// return; + /// } /// \endcode ABS_Never, }; - /// Break after a group of C++11 attributes before a function - /// declaration/definition name. + /// Break after a group of C++11 attributes before variable or function + /// (including constructor/destructor) declaration/definition names or before + /// control statements, i.e. ``if``, ``switch`` (including ``case`` and + /// ``default`` labels), ``for``, and ``while`` statements. /// \version 16 AttributeBreakingStyle BreakAfterAttributes; @@ -2964,7 +3034,9 @@ struct FormatStyle { bool isJson() const { return Language == LK_Json; } bool isJavaScript() const { return Language == LK_JavaScript; } bool isVerilog() const { return Language == LK_Verilog; } - bool isProto() const { return Language == LK_Proto; } + bool isProto() const { + return Language == LK_Proto || Language == LK_TextProto; + } /// Language, this format style is targeted at. /// \version 3.5 @@ -4688,6 +4760,7 @@ struct FormatStyle { BinPackParameters == R.BinPackParameters && BitFieldColonSpacing == R.BitFieldColonSpacing && BracedInitializerIndentWidth == R.BracedInitializerIndentWidth && + BreakAdjacentStringLiterals == R.BreakAdjacentStringLiterals && BreakAfterAttributes == R.BreakAfterAttributes && BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && BreakArrays == R.BreakArrays && diff --git a/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h index c6d0d4d7e90233ec2766e3d94985ea89952ececc..fe99b3d5adbfa0a45c936e4e06bc8c26f973d3cf 100644 --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/include/clang/Frontend/ASTUnit.h @@ -355,6 +355,7 @@ private: /// Bit used by CIndex to mark when a translation unit may be in an /// inconsistent state, and is not safe to free. + LLVM_PREFERRED_TYPE(bool) unsigned UnsafeToFree : 1; /// \brief Enumerator specifying the scope for skipping function bodies. @@ -690,18 +691,16 @@ public: /// lifetime is expected to extend past that of the returned ASTUnit. /// /// \returns - The initialized ASTUnit or null if the AST failed to load. - static std::unique_ptr - LoadFromASTFile(const std::string &Filename, - const PCHContainerReader &PCHContainerRdr, WhatToLoad ToLoad, - IntrusiveRefCntPtr Diags, - const FileSystemOptions &FileSystemOpts, - std::shared_ptr HSOpts, - bool UseDebugInfo = false, bool OnlyLocalDecls = false, - CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::None, - bool AllowASTWithCompilerErrors = false, - bool UserFilesAreVolatile = false, - IntrusiveRefCntPtr VFS = - llvm::vfs::getRealFileSystem()); + static std::unique_ptr LoadFromASTFile( + const std::string &Filename, const PCHContainerReader &PCHContainerRdr, + WhatToLoad ToLoad, IntrusiveRefCntPtr Diags, + const FileSystemOptions &FileSystemOpts, + std::shared_ptr HSOpts, bool OnlyLocalDecls = false, + CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::None, + bool AllowASTWithCompilerErrors = false, + bool UserFilesAreVolatile = false, + IntrusiveRefCntPtr VFS = + llvm::vfs::getRealFileSystem()); private: /// Helper function for \c LoadFromCompilerInvocation() and diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index d26a452cf94cc3bcb02df4bbf8f983239c97872d..ac2f940769fbe903f76171e36f0b6a416d504db9 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -304,6 +304,11 @@ public: return Invocation->getHeaderSearchOptsPtr(); } + APINotesOptions &getAPINotesOpts() { return Invocation->getAPINotesOpts(); } + const APINotesOptions &getAPINotesOpts() const { + return Invocation->getAPINotesOpts(); + } + LangOptions &getLangOpts() { return Invocation->getLangOpts(); } const LangOptions &getLangOpts() const { return Invocation->getLangOpts(); } diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h index e4b26d92647d0a7db3e449e51c6feb11faf5cfad..d92a87d78d7c5a0e8d8a558e93d5efd9303ba528 100644 --- a/clang/include/clang/Frontend/DependencyOutputOptions.h +++ b/clang/include/clang/Frontend/DependencyOutputOptions.h @@ -33,15 +33,19 @@ enum ExtraDepKind { /// file generation. class DependencyOutputOptions { public: + LLVM_PREFERRED_TYPE(bool) unsigned IncludeSystemHeaders : 1; ///< Include system header dependencies. - unsigned - CanonicalSystemHeaders : 1; ///< canonicalize system header dependencies. + LLVM_PREFERRED_TYPE(bool) unsigned ShowHeaderIncludes : 1; ///< Show header inclusions (-H). + LLVM_PREFERRED_TYPE(bool) unsigned UsePhonyTargets : 1; ///< Include phony targets for each /// dependency, which can avoid some 'make' /// problems. + LLVM_PREFERRED_TYPE(bool) unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list + LLVM_PREFERRED_TYPE(bool) unsigned IncludeModuleFiles : 1; ///< Include module file dependencies. + LLVM_PREFERRED_TYPE(bool) unsigned ShowSkippedHeaderIncludes : 1; ///< With ShowHeaderIncludes, show /// also includes that were skipped /// due to the "include guard @@ -84,11 +88,10 @@ public: public: DependencyOutputOptions() - : IncludeSystemHeaders(0), CanonicalSystemHeaders(0), - ShowHeaderIncludes(0), UsePhonyTargets(0), AddMissingHeaderDeps(0), - IncludeModuleFiles(0), ShowSkippedHeaderIncludes(0), - HeaderIncludeFormat(HIFMT_Textual), HeaderIncludeFiltering(HIFIL_None) { - } + : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0), + AddMissingHeaderDeps(0), IncludeModuleFiles(0), + ShowSkippedHeaderIncludes(0), HeaderIncludeFormat(HIFMT_Textual), + HeaderIncludeFiltering(HIFIL_None) {} }; } // end namespace clang diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index 117e35de6f76c4cf9730293759800a981edecf5f..53a8681cfdbba04a667e80677a95a00d6baa948c 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -147,13 +147,6 @@ enum ActionKind { /// The kind of a file that we've been handed as an input. class InputKind { -private: - Language Lang; - unsigned Fmt : 3; - unsigned Preprocessed : 1; - unsigned HeaderUnit : 3; - unsigned IsHeader : 1; - public: /// The input file format. enum Format { @@ -172,6 +165,18 @@ public: HeaderUnit_Abs }; +private: + Language Lang; + LLVM_PREFERRED_TYPE(Format) + unsigned Fmt : 3; + LLVM_PREFERRED_TYPE(bool) + unsigned Preprocessed : 1; + LLVM_PREFERRED_TYPE(HeaderUnitKind) + unsigned HeaderUnit : 3; + LLVM_PREFERRED_TYPE(bool) + unsigned IsHeader : 1; + +public: constexpr InputKind(Language L = Language::Unknown, Format F = Source, bool PP = false, HeaderUnitKind HU = HeaderUnit_None, bool HD = false) @@ -266,88 +271,116 @@ public: class FrontendOptions { public: /// Disable memory freeing on exit. + LLVM_PREFERRED_TYPE(bool) unsigned DisableFree : 1; /// When generating PCH files, instruct the AST writer to create relocatable /// PCH files. + LLVM_PREFERRED_TYPE(bool) unsigned RelocatablePCH : 1; /// Show the -help text. + LLVM_PREFERRED_TYPE(bool) unsigned ShowHelp : 1; /// Show frontend performance metrics and statistics. + LLVM_PREFERRED_TYPE(bool) unsigned ShowStats : 1; + LLVM_PREFERRED_TYPE(bool) unsigned AppendStats : 1; /// print the supported cpus for the current target + LLVM_PREFERRED_TYPE(bool) unsigned PrintSupportedCPUs : 1; /// Print the supported extensions for the current target. + LLVM_PREFERRED_TYPE(bool) unsigned PrintSupportedExtensions : 1; /// Show the -version text. + LLVM_PREFERRED_TYPE(bool) unsigned ShowVersion : 1; /// Apply fixes even if there are unfixable errors. + LLVM_PREFERRED_TYPE(bool) unsigned FixWhatYouCan : 1; /// Apply fixes only for warnings. + LLVM_PREFERRED_TYPE(bool) unsigned FixOnlyWarnings : 1; /// Apply fixes and recompile. + LLVM_PREFERRED_TYPE(bool) unsigned FixAndRecompile : 1; /// Apply fixes to temporary files. + LLVM_PREFERRED_TYPE(bool) unsigned FixToTemporaries : 1; /// Emit ARC errors even if the migrator can fix them. + LLVM_PREFERRED_TYPE(bool) unsigned ARCMTMigrateEmitARCErrors : 1; /// Skip over function bodies to speed up parsing in cases you do not need /// them (e.g. with code completion). + LLVM_PREFERRED_TYPE(bool) unsigned SkipFunctionBodies : 1; /// Whether we can use the global module index if available. + LLVM_PREFERRED_TYPE(bool) unsigned UseGlobalModuleIndex : 1; /// Whether we can generate the global module index if needed. + LLVM_PREFERRED_TYPE(bool) unsigned GenerateGlobalModuleIndex : 1; /// Whether we include declaration dumps in AST dumps. + LLVM_PREFERRED_TYPE(bool) unsigned ASTDumpDecls : 1; /// Whether we deserialize all decls when forming AST dumps. + LLVM_PREFERRED_TYPE(bool) unsigned ASTDumpAll : 1; /// Whether we include lookup table dumps in AST dumps. + LLVM_PREFERRED_TYPE(bool) unsigned ASTDumpLookups : 1; /// Whether we include declaration type dumps in AST dumps. + LLVM_PREFERRED_TYPE(bool) unsigned ASTDumpDeclTypes : 1; /// Whether we are performing an implicit module build. + LLVM_PREFERRED_TYPE(bool) unsigned BuildingImplicitModule : 1; /// Whether to use a filesystem lock when building implicit modules. + LLVM_PREFERRED_TYPE(bool) unsigned BuildingImplicitModuleUsesLock : 1; /// Whether we should embed all used files into the PCM file. + LLVM_PREFERRED_TYPE(bool) unsigned ModulesEmbedAllFiles : 1; /// Whether timestamps should be written to the produced PCH file. + LLVM_PREFERRED_TYPE(bool) unsigned IncludeTimestamps : 1; /// Should a temporary file be used during compilation. + LLVM_PREFERRED_TYPE(bool) unsigned UseTemporary : 1; /// When using -emit-module, treat the modulemap as a system module. + LLVM_PREFERRED_TYPE(bool) unsigned IsSystemModule : 1; /// Output (and read) PCM files regardless of compiler errors. + LLVM_PREFERRED_TYPE(bool) unsigned AllowPCMWithCompilerErrors : 1; /// Whether to share the FileManager when building modules. + LLVM_PREFERRED_TYPE(bool) unsigned ModulesShareFileManager : 1; CodeCompleteOptions CodeCompleteOpts; diff --git a/clang/include/clang/Frontend/MigratorOptions.h b/clang/include/clang/Frontend/MigratorOptions.h index cf50ffcf0c4f5ad4b0f77e9631f30bde612bf58e..da899643f0b4b2b7861540240d08edaba5ad7ebe 100644 --- a/clang/include/clang/Frontend/MigratorOptions.h +++ b/clang/include/clang/Frontend/MigratorOptions.h @@ -14,11 +14,15 @@ #ifndef LLVM_CLANG_FRONTEND_MIGRATOROPTIONS_H #define LLVM_CLANG_FRONTEND_MIGRATOROPTIONS_H +#include "llvm/Support/Compiler.h" + namespace clang { class MigratorOptions { public: + LLVM_PREFERRED_TYPE(bool) unsigned NoNSAllocReallocError : 1; + LLVM_PREFERRED_TYPE(bool) unsigned NoFinalizeRemoval : 1; MigratorOptions() { NoNSAllocReallocError = 0; diff --git a/clang/include/clang/Frontend/PreprocessorOutputOptions.h b/clang/include/clang/Frontend/PreprocessorOutputOptions.h index db2ec9f2ae20698f2401079a0235aacbbd857198..6e19cae33cf283d50e8134b96482caa8f1c26289 100644 --- a/clang/include/clang/Frontend/PreprocessorOutputOptions.h +++ b/clang/include/clang/Frontend/PreprocessorOutputOptions.h @@ -9,23 +9,37 @@ #ifndef LLVM_CLANG_FRONTEND_PREPROCESSOROUTPUTOPTIONS_H #define LLVM_CLANG_FRONTEND_PREPROCESSOROUTPUTOPTIONS_H +#include + namespace clang { /// PreprocessorOutputOptions - Options for controlling the C preprocessor /// output (e.g., -E). class PreprocessorOutputOptions { public: + LLVM_PREFERRED_TYPE(bool) unsigned ShowCPP : 1; ///< Print normal preprocessed output. + LLVM_PREFERRED_TYPE(bool) unsigned ShowComments : 1; ///< Show comments. + LLVM_PREFERRED_TYPE(bool) unsigned ShowLineMarkers : 1; ///< Show \#line markers. + LLVM_PREFERRED_TYPE(bool) unsigned UseLineDirectives : 1; ///< Use \#line instead of GCC-style \# N. + LLVM_PREFERRED_TYPE(bool) unsigned ShowMacroComments : 1; ///< Show comments, even in macros. + LLVM_PREFERRED_TYPE(bool) unsigned ShowMacros : 1; ///< Print macro definitions. + LLVM_PREFERRED_TYPE(bool) unsigned ShowIncludeDirectives : 1; ///< Print includes, imports etc. within preprocessed output. + LLVM_PREFERRED_TYPE(bool) unsigned RewriteIncludes : 1; ///< Preprocess include directives only. + LLVM_PREFERRED_TYPE(bool) unsigned RewriteImports : 1; ///< Include contents of transitively-imported modules. + LLVM_PREFERRED_TYPE(bool) unsigned MinimizeWhitespace : 1; ///< Ignore whitespace from input. + LLVM_PREFERRED_TYPE(bool) unsigned DirectivesOnly : 1; ///< Process directives but do not expand macros. + LLVM_PREFERRED_TYPE(bool) unsigned KeepSystemIncludes : 1; ///< Do not expand system headers. public: diff --git a/clang/include/clang/Frontend/TextDiagnosticPrinter.h b/clang/include/clang/Frontend/TextDiagnosticPrinter.h index ba756fa18c301b69f27d4fd023f753a7961d1dc7..2610bde7513a1a0194e0cc91206b063dd4de3fee 100644 --- a/clang/include/clang/Frontend/TextDiagnosticPrinter.h +++ b/clang/include/clang/Frontend/TextDiagnosticPrinter.h @@ -34,6 +34,7 @@ class TextDiagnosticPrinter : public DiagnosticConsumer { /// A string to prefix to error messages. std::string Prefix; + LLVM_PREFERRED_TYPE(bool) unsigned OwnsOutputStream : 1; public: diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index 8300e45d15fe5f61ef7a085ecc25ab16f2d30508..143cf4359f00b50bfd62c6eccb48faeac8b01463 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -41,7 +41,6 @@ class ExternalSemaSource; class FrontendOptions; class PCHContainerReader; class Preprocessor; -class FileManager; class PreprocessorOptions; class PreprocessorOutputOptions; @@ -80,14 +79,11 @@ public: /// Return true if system files should be passed to sawDependency(). virtual bool needSystemDependencies() { return false; } - /// Return true if system files should be canonicalized. - virtual bool shouldCanonicalizeSystemDependencies() { return false; } - /// Add a dependency \p Filename if it has not been seen before and /// sawDependency() returns true. virtual void maybeAddDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, - FileManager *FileMgr, bool IsMissing); + bool IsMissing); protected: /// Return true if the filename was added to the list of dependencies, false @@ -116,10 +112,6 @@ public: bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, bool IsModuleFile, bool IsMissing) final; - bool shouldCanonicalizeSystemDependencies() override { - return CanonicalSystemHeaders; - } - protected: void outputDependencyFile(llvm::raw_ostream &OS); @@ -129,7 +121,6 @@ private: std::string OutputFile; std::vector Targets; bool IncludeSystemHeaders; - bool CanonicalSystemHeaders; bool PhonyTarget; bool AddMissingHeaderDeps; bool SeenMissingHeader; diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h index 500a7e11ab9ac2ad24cc4a30832f75dd5d2ae0f2..ddfae2666c4c397ee9a2dffff6cfe0f13f0ece84 100644 --- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h +++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h @@ -32,156 +32,8 @@ class TextDiagnosticBuffer; /// VerifyDiagnosticConsumer - Create a diagnostic client which will use /// markers in the input source to check that all the emitted diagnostics match -/// those expected. -/// -/// INVOKING THE DIAGNOSTIC CHECKER: -/// -/// VerifyDiagnosticConsumer is typically invoked via the "-verify" option to -/// "clang -cc1". "-verify" is equivalent to "-verify=expected", so all -/// diagnostics are typically specified with the prefix "expected". For -/// example: -/// -/// \code -/// int A = B; // expected-error {{use of undeclared identifier 'B'}} -/// \endcode -/// -/// Custom prefixes can be specified as a comma-separated sequence. Each -/// prefix must start with a letter and contain only alphanumeric characters, -/// hyphens, and underscores. For example, given just "-verify=foo,bar", -/// the above diagnostic would be ignored, but the following diagnostics would -/// be recognized: -/// -/// \code -/// int A = B; // foo-error {{use of undeclared identifier 'B'}} -/// int C = D; // bar-error {{use of undeclared identifier 'D'}} -/// \endcode -/// -/// Multiple occurrences accumulate prefixes. For example, -/// "-verify -verify=foo,bar -verify=baz" is equivalent to -/// "-verify=expected,foo,bar,baz". -/// -/// SPECIFYING DIAGNOSTICS: -/// -/// Indicating that a line expects an error or a warning is simple. Put a -/// comment on the line that has the diagnostic, use: -/// -/// \code -/// expected-{error,warning,remark,note} -/// \endcode -/// -/// to tag if it's an expected error, remark or warning, and place the expected -/// text between {{ and }} markers. The full text doesn't have to be included, -/// only enough to ensure that the correct diagnostic was emitted. -/// -/// Here's an example: -/// -/// \code -/// int A = B; // expected-error {{use of undeclared identifier 'B'}} -/// \endcode -/// -/// You can place as many diagnostics on one line as you wish. To make the code -/// more readable, you can use slash-newline to separate out the diagnostics. -/// -/// Alternatively, it is possible to specify the line on which the diagnostic -/// should appear by appending "@" to "expected-", for example: -/// -/// \code -/// #warning some text -/// // expected-warning@10 {{some text}} -/// \endcode -/// -/// The line number may be absolute (as above), or relative to the current -/// line by prefixing the number with either '+' or '-'. -/// -/// If the diagnostic is generated in a separate file, for example in a shared -/// header file, it may be beneficial to be able to declare the file in which -/// the diagnostic will appear, rather than placing the expected-* directive in -/// the actual file itself. This can be done using the following syntax: -/// -/// \code -/// // expected-error@path/include.h:15 {{error message}} -/// \endcode -/// -/// The path can be absolute or relative and the same search paths will be used -/// as for #include directives. The line number in an external file may be -/// substituted with '*' meaning that any line number will match (useful where -/// the included file is, for example, a system header where the actual line -/// number may change and is not critical). -/// -/// As an alternative to specifying a fixed line number, the location of a -/// diagnostic can instead be indicated by a marker of the form "#". -/// Markers are specified by including them in a comment, and then referenced -/// by appending the marker to the diagnostic with "@#": -/// -/// \code -/// #warning some text // #1 -/// // expected-warning@#1 {{some text}} -/// \endcode -/// -/// The name of a marker used in a directive must be unique within the -/// compilation. -/// -/// The simple syntax above allows each specification to match exactly one -/// error. You can use the extended syntax to customize this. The extended -/// syntax is "expected- {{diag text}}", where \ is one of -/// "error", "warning" or "note", and \ is a positive integer. This allows -/// the diagnostic to appear as many times as specified. Example: -/// -/// \code -/// void f(); // expected-note 2 {{previous declaration is here}} -/// \endcode -/// -/// Where the diagnostic is expected to occur a minimum number of times, this -/// can be specified by appending a '+' to the number. Example: -/// -/// \code -/// void f(); // expected-note 0+ {{previous declaration is here}} -/// void g(); // expected-note 1+ {{previous declaration is here}} -/// \endcode -/// -/// In the first example, the diagnostic becomes optional, i.e. it will be -/// swallowed if it occurs, but will not generate an error if it does not -/// occur. In the second example, the diagnostic must occur at least once. -/// As a short-hand, "one or more" can be specified simply by '+'. Example: -/// -/// \code -/// void g(); // expected-note + {{previous declaration is here}} -/// \endcode -/// -/// A range can also be specified by "-". Example: -/// -/// \code -/// void f(); // expected-note 0-1 {{previous declaration is here}} -/// \endcode -/// -/// In this example, the diagnostic may appear only once, if at all. -/// -/// Regex matching mode may be selected by appending '-re' to type and -/// including regexes wrapped in double curly braces in the directive, such as: -/// -/// \code -/// expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}} -/// \endcode -/// -/// Examples matching error: "variable has incomplete type 'struct s'" -/// -/// \code -/// // expected-error {{variable has incomplete type 'struct s'}} -/// // expected-error {{variable has incomplete type}} -/// -/// // expected-error-re {{variable has type 'struct {{.}}'}} -/// // expected-error-re {{variable has type 'struct {{.*}}'}} -/// // expected-error-re {{variable has type 'struct {{(.*)}}'}} -/// // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}} -/// \endcode -/// -/// VerifyDiagnosticConsumer expects at least one expected-* directive to -/// be found inside the source code. If no diagnostics are expected the -/// following directive can be used to indicate this: -/// -/// \code -/// // expected-no-diagnostics -/// \endcode +/// those expected. See clang/docs/InternalsManual.rst for details about how to +/// write tests to verify diagnostics. /// class VerifyDiagnosticConsumer: public DiagnosticConsumer, public CommentHandler { diff --git a/clang/include/clang/Lex/DirectoryLookup.h b/clang/include/clang/Lex/DirectoryLookup.h index d668a830db38fd1ede2c840e892b198f3669d6fd..81680d3b271e08108660bff614a3fc3a4c17e576 100644 --- a/clang/include/clang/Lex/DirectoryLookup.h +++ b/clang/include/clang/Lex/DirectoryLookup.h @@ -50,17 +50,21 @@ private: /// DirCharacteristic - The type of directory this is: this is an instance of /// SrcMgr::CharacteristicKind. + LLVM_PREFERRED_TYPE(SrcMgr::CharacteristicKind) unsigned DirCharacteristic : 3; /// LookupType - This indicates whether this DirectoryLookup object is a /// normal directory, a framework, or a headermap. + LLVM_PREFERRED_TYPE(LookupType_t) unsigned LookupType : 2; /// Whether this is a header map used when building a framework. + LLVM_PREFERRED_TYPE(bool) unsigned IsIndexHeaderMap : 1; /// Whether we've performed an exhaustive search for module maps /// within the subdirectories of this directory. + LLVM_PREFERRED_TYPE(bool) unsigned SearchedAllModuleMaps : 1; public: diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h index a212eea7cfd0cb9a158942877f8de32015085d99..a100598c80155fa5d5259bcd3601b0890c032744 100644 --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -59,29 +59,36 @@ struct HeaderFileInfo { // TODO: Whether the file was imported is not a property of the file itself. // It's a preprocessor state, move it there. /// True if this is a \#import'd file. + LLVM_PREFERRED_TYPE(bool) unsigned isImport : 1; /// True if this is a \#pragma once file. + LLVM_PREFERRED_TYPE(bool) unsigned isPragmaOnce : 1; /// Keep track of whether this is a system header, and if so, /// whether it is C++ clean or not. This can be set by the include paths or /// by \#pragma gcc system_header. This is an instance of /// SrcMgr::CharacteristicKind. + LLVM_PREFERRED_TYPE(SrcMgr::CharacteristicKind) unsigned DirInfo : 3; /// Whether this header file info was supplied by an external source, /// and has not changed since. + LLVM_PREFERRED_TYPE(bool) unsigned External : 1; /// Whether this header is part of a module. + LLVM_PREFERRED_TYPE(bool) unsigned isModuleHeader : 1; /// Whether this header is part of the module that we are building. + LLVM_PREFERRED_TYPE(bool) unsigned isCompilingModuleHeader : 1; /// Whether this structure is considered to already have been /// "resolved", meaning that it was loaded from the external source. + LLVM_PREFERRED_TYPE(bool) unsigned Resolved : 1; /// Whether this is a header inside a framework that is currently @@ -91,9 +98,11 @@ struct HeaderFileInfo { /// into the appropriate framework subdirectories, and therefore are /// provided via a header map. This bit indicates when this is one of /// those framework headers. + LLVM_PREFERRED_TYPE(bool) unsigned IndexHeaderMapHeader : 1; /// Whether this file has been looked up as a header. + LLVM_PREFERRED_TYPE(bool) unsigned IsValid : 1; /// The ID number of the controlling macro. diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h index 7f162efe4f0d4b3ba8dd60dddedf1d5deb0d47dd..fa2d0b502d72c19cea7fe92ec9c87cf2d53f5aa9 100644 --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -70,11 +70,13 @@ public: struct Entry { std::string Path; frontend::IncludeDirGroup Group; + LLVM_PREFERRED_TYPE(bool) unsigned IsFramework : 1; /// IgnoreSysRoot - This is false if an absolute path should be treated /// relative to the sysroot, or true if it should always be the absolute /// path. + LLVM_PREFERRED_TYPE(bool) unsigned IgnoreSysRoot : 1; Entry(StringRef path, frontend::IncludeDirGroup group, bool isFramework, @@ -128,10 +130,12 @@ public: /// module cache. /// /// Note: Only used for testing! + LLVM_PREFERRED_TYPE(bool) unsigned DisableModuleHash : 1; /// Implicit module maps. This option is enabld by default when /// modules is enabled. + LLVM_PREFERRED_TYPE(bool) unsigned ImplicitModuleMaps : 1; /// Set the 'home directory' of a module map file to the current @@ -141,16 +145,19 @@ public: // /// The home directory is where we look for files named in the module map /// file. + LLVM_PREFERRED_TYPE(bool) unsigned ModuleMapFileHomeIsCwd : 1; /// Set the base path of a built module file to be the current working /// directory. This is useful for sharing module files across machines /// that build with different paths without having to rewrite all /// modulemap files to have working directory relative paths. + LLVM_PREFERRED_TYPE(bool) unsigned ModuleFileHomeIsCwd : 1; /// Also search for prebuilt implicit modules in the prebuilt module cache /// path. + LLVM_PREFERRED_TYPE(bool) unsigned EnablePrebuiltImplicitModules : 1; /// The interval (in seconds) between pruning operations. @@ -185,48 +192,67 @@ public: std::vector VFSOverlayFiles; /// Include the compiler builtin includes. + LLVM_PREFERRED_TYPE(bool) unsigned UseBuiltinIncludes : 1; /// Include the system standard include search directories. + LLVM_PREFERRED_TYPE(bool) unsigned UseStandardSystemIncludes : 1; /// Include the system standard C++ library include search directories. + LLVM_PREFERRED_TYPE(bool) unsigned UseStandardCXXIncludes : 1; /// Use libc++ instead of the default libstdc++. + LLVM_PREFERRED_TYPE(bool) unsigned UseLibcxx : 1; /// Whether header search information should be output as for -v. + LLVM_PREFERRED_TYPE(bool) unsigned Verbose : 1; /// If true, skip verifying input files used by modules if the /// module was already verified during this build session (see /// \c BuildSessionTimestamp). + LLVM_PREFERRED_TYPE(bool) unsigned ModulesValidateOncePerBuildSession : 1; /// Whether to validate system input files when a module is loaded. + LLVM_PREFERRED_TYPE(bool) unsigned ModulesValidateSystemHeaders : 1; // Whether the content of input files should be hashed and used to // validate consistency. + LLVM_PREFERRED_TYPE(bool) unsigned ValidateASTInputFilesContent : 1; // Whether the input files from C++20 Modules should be checked. + LLVM_PREFERRED_TYPE(bool) unsigned ForceCheckCXX20ModulesInputFiles : 1; /// Whether the module includes debug information (-gmodules). + LLVM_PREFERRED_TYPE(bool) unsigned UseDebugInfo : 1; + LLVM_PREFERRED_TYPE(bool) unsigned ModulesValidateDiagnosticOptions : 1; /// Whether to entirely skip writing diagnostic options. /// Primarily used to speed up deserialization during dependency scanning. + LLVM_PREFERRED_TYPE(bool) unsigned ModulesSkipDiagnosticOptions : 1; /// Whether to entirely skip writing header search paths. /// Primarily used to speed up deserialization during dependency scanning. + LLVM_PREFERRED_TYPE(bool) unsigned ModulesSkipHeaderSearchPaths : 1; + /// Whether to entirely skip writing pragma diagnostic mappings. + /// Primarily used to speed up deserialization during dependency scanning. + LLVM_PREFERRED_TYPE(bool) + unsigned ModulesSkipPragmaDiagnosticMappings : 1; + + LLVM_PREFERRED_TYPE(bool) unsigned ModulesHashContent : 1; /// Whether we should include all things that could impact the module in the @@ -234,6 +260,7 @@ public: /// /// This includes things like the full header search path, and enabled /// diagnostics. + LLVM_PREFERRED_TYPE(bool) unsigned ModulesStrictContextHash : 1; HeaderSearchOptions(StringRef _Sysroot = "/") @@ -248,7 +275,8 @@ public: ForceCheckCXX20ModulesInputFiles(false), UseDebugInfo(false), ModulesValidateDiagnosticOptions(true), ModulesSkipDiagnosticOptions(false), - ModulesSkipHeaderSearchPaths(false), ModulesHashContent(false), + ModulesSkipHeaderSearchPaths(false), + ModulesSkipPragmaDiagnosticMappings(false), ModulesHashContent(false), ModulesStrictContextHash(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index 00c1c3866bbd9caf0f0ce3672d784879f1e08c5a..1237fc62eb6cf392e2c5831e0f9bdebd831c9b87 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -325,15 +325,18 @@ protected: SourceLocation Loc; /// MacroDirective kind. + LLVM_PREFERRED_TYPE(Kind) unsigned MDKind : 2; /// True if the macro directive was loaded from a PCH file. + LLVM_PREFERRED_TYPE(bool) unsigned IsFromPCH : 1; // Used by VisibilityMacroDirective ----------------------------------------// /// Whether the macro has public visibility (when described in a /// module). + LLVM_PREFERRED_TYPE(bool) unsigned IsPublic : 1; MacroDirective(Kind K, SourceLocation Loc) diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 69ec8f34a3242550093a91ac860fc054dfc011d1..32e7e8f899e502cf6953f094121b16e91dc71177 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -232,16 +232,20 @@ private: /// The set of attributes that can be attached to a module. struct Attributes { /// Whether this is a system module. + LLVM_PREFERRED_TYPE(bool) unsigned IsSystem : 1; /// Whether this is an extern "C" module. + LLVM_PREFERRED_TYPE(bool) unsigned IsExternC : 1; /// Whether this is an exhaustive set of configuration macros. + LLVM_PREFERRED_TYPE(bool) unsigned IsExhaustive : 1; /// Whether files in this module can only include non-modular headers /// and headers from used modules. + LLVM_PREFERRED_TYPE(bool) unsigned NoUndeclaredIncludes : 1; Attributes() @@ -252,6 +256,7 @@ private: /// A directory for which framework modules can be inferred. struct InferredDirectory { /// Whether to infer modules from this directory. + LLVM_PREFERRED_TYPE(bool) unsigned InferModules : 1; /// The attributes to use for inferred modules. @@ -551,8 +556,8 @@ public: /// parent. Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc, Module *Parent = nullptr); - Module *createImplicitGlobalModuleFragmentForModuleUnit( - SourceLocation Loc, bool IsExported, Module *Parent = nullptr); + Module *createImplicitGlobalModuleFragmentForModuleUnit(SourceLocation Loc, + Module *Parent); /// Create a global module fragment for a C++ module interface unit. Module *createPrivateModuleFragmentForInterfaceUnit(Module *Parent, diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index 00f1bfd7394b2a18351c548c426ef4061ea0b52d..5ddf024186f86553a73ba6c0ae21cab812ab65cd 100644 --- a/clang/include/clang/Lex/PreprocessingRecord.h +++ b/clang/include/clang/Lex/PreprocessingRecord.h @@ -228,15 +228,18 @@ class Token; /// Whether the file name was in quotation marks; otherwise, it was /// in angle brackets. + LLVM_PREFERRED_TYPE(bool) unsigned InQuotes : 1; /// The kind of inclusion directive we have. /// /// This is a value of type InclusionKind. + LLVM_PREFERRED_TYPE(InclusionKind) unsigned Kind : 2; /// Whether the inclusion directive was automatically turned into /// a module import. + LLVM_PREFERRED_TYPE(bool) unsigned ImportedModule : 1; /// The file that was included. diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 4a99447e757c6ac96c8e7e0698a56d066c0d1490..4ec21a8b6be2c852e921b53f627c50fe30bf9ef0 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -751,13 +751,8 @@ private: std::unique_ptr CurTokenLexer; /// The kind of lexer we're currently working with. - enum CurLexerKind { - CLK_Lexer, - CLK_TokenLexer, - CLK_CachingLexer, - CLK_DependencyDirectivesLexer, - CLK_LexAfterModuleImport - } CurLexerKind = CLK_Lexer; + typedef bool (*LexerCallback)(Preprocessor &, Token &); + LexerCallback CurLexerCallback = &CLK_Lexer; /// If the current lexer is for a submodule that is being built, this /// is that submodule. @@ -767,7 +762,7 @@ private: /// \#included, and macros currently being expanded from, not counting /// CurLexer/CurTokenLexer. struct IncludeStackInfo { - enum CurLexerKind CurLexerKind; + LexerCallback CurLexerCallback; Module *TheSubmodule; std::unique_ptr TheLexer; PreprocessorLexer *ThePPLexer; @@ -776,12 +771,12 @@ private: // The following constructors are completely useless copies of the default // versions, only needed to pacify MSVC. - IncludeStackInfo(enum CurLexerKind CurLexerKind, Module *TheSubmodule, + IncludeStackInfo(LexerCallback CurLexerCallback, Module *TheSubmodule, std::unique_ptr &&TheLexer, PreprocessorLexer *ThePPLexer, std::unique_ptr &&TheTokenLexer, ConstSearchDirIterator TheDirLookup) - : CurLexerKind(std::move(CurLexerKind)), + : CurLexerCallback(std::move(CurLexerCallback)), TheSubmodule(std::move(TheSubmodule)), TheLexer(std::move(TheLexer)), ThePPLexer(std::move(ThePPLexer)), TheTokenLexer(std::move(TheTokenLexer)), @@ -1901,7 +1896,7 @@ public: /// Determine whether it's possible for a future call to Lex to produce an /// annotation token created by a previous call to EnterAnnotationToken. bool mightHavePendingAnnotationTokens() { - return CurLexerKind != CLK_Lexer; + return CurLexerCallback != CLK_Lexer; } /// Update the current token to represent the provided @@ -2430,8 +2425,9 @@ private: friend void TokenLexer::ExpandFunctionArguments(); void PushIncludeMacroStack() { - assert(CurLexerKind != CLK_CachingLexer && "cannot push a caching lexer"); - IncludeMacroStack.emplace_back(CurLexerKind, CurLexerSubmodule, + assert(CurLexerCallback != CLK_CachingLexer && + "cannot push a caching lexer"); + IncludeMacroStack.emplace_back(CurLexerCallback, CurLexerSubmodule, std::move(CurLexer), CurPPLexer, std::move(CurTokenLexer), CurDirLookup); CurPPLexer = nullptr; @@ -2443,7 +2439,7 @@ private: CurTokenLexer = std::move(IncludeMacroStack.back().TheTokenLexer); CurDirLookup = IncludeMacroStack.back().TheDirLookup; CurLexerSubmodule = IncludeMacroStack.back().TheSubmodule; - CurLexerKind = IncludeMacroStack.back().CurLexerKind; + CurLexerCallback = IncludeMacroStack.back().CurLexerCallback; IncludeMacroStack.pop_back(); } @@ -2899,6 +2895,26 @@ public: /// \return true iff this PP is currently in a "-Wunsafe-buffer-usage" /// opt-out region bool isPPInSafeBufferOptOutRegion(SourceLocation &StartLoc); + +private: + /// Helper functions to forward lexing to the actual lexer. They all share the + /// same signature. + static bool CLK_Lexer(Preprocessor &P, Token &Result) { + return P.CurLexer->Lex(Result); + } + static bool CLK_TokenLexer(Preprocessor &P, Token &Result) { + return P.CurTokenLexer->Lex(Result); + } + static bool CLK_CachingLexer(Preprocessor &P, Token &Result) { + P.CachingLex(Result); + return true; + } + static bool CLK_DependencyDirectivesLexer(Preprocessor &P, Token &Result) { + return P.CurLexer->LexDependencyDirectiveToken(Result); + } + static bool CLK_LexAfterModuleImport(Preprocessor &P, Token &Result) { + return P.LexAfterModuleImport(Result); + } }; /// Abstract base class that describes a handler that will receive diff --git a/clang/include/clang/Lex/VariadicMacroSupport.h b/clang/include/clang/Lex/VariadicMacroSupport.h index 119f02201fc614404e9acd2da1545252a4a1d675..cf86a00c6d66370cd6b43cfb394b34a7bd7545a3 100644 --- a/clang/include/clang/Lex/VariadicMacroSupport.h +++ b/clang/include/clang/Lex/VariadicMacroSupport.h @@ -129,11 +129,16 @@ namespace clang { // the function-like macro's new replacement list. int NumOfTokensPriorToVAOpt = -1; + LLVM_PREFERRED_TYPE(bool) unsigned LeadingSpaceForStringifiedToken : 1; + LLVM_PREFERRED_TYPE(bool) unsigned StringifyBefore : 1; + LLVM_PREFERRED_TYPE(bool) unsigned CharifyBefore : 1; + LLVM_PREFERRED_TYPE(bool) unsigned BeginsWithPlaceholder : 1; + LLVM_PREFERRED_TYPE(bool) unsigned EndsWithPlaceholder : 1; bool hasStringifyBefore() const { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 30e0352c868637b15f9b22bf3d2305fa97ea6763..ca29ce46873e8a4a9fef861fb5c6a0548a934385 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -53,6 +53,7 @@ namespace clang { class Parser : public CodeCompletionHandler { friend class ColonProtectionRAIIObject; friend class ParsingOpenMPDirectiveRAII; + friend class ParsingOpenACCDirectiveRAII; friend class InMessageExpressionRAIIObject; friend class OffsetOfStateRAIIObject; friend class PoisonSEHIdentifiersRAIIObject; @@ -175,6 +176,7 @@ class Parser : public CodeCompletionHandler { std::unique_ptr FPContractHandler; std::unique_ptr OpenCLExtensionHandler; std::unique_ptr OpenMPHandler; + std::unique_ptr OpenACCHandler; std::unique_ptr PCSectionHandler; std::unique_ptr MSCommentHandler; std::unique_ptr MSDetectMismatchHandler; @@ -229,6 +231,9 @@ class Parser : public CodeCompletionHandler { /// Parsing OpenMP directive mode. bool OpenMPDirectiveParsing = false; + /// Parsing OpenACC directive mode. + bool OpenACCDirectiveParsing = false; + /// When true, we are directly inside an Objective-C message /// send expression. /// @@ -410,18 +415,15 @@ class Parser : public CodeCompletionHandler { /// Flags describing a context in which we're parsing a statement. enum class ParsedStmtContext { - /// This context permits declarations in language modes where declarations - /// are not statements. - AllowDeclarationsInC = 0x1, /// This context permits standalone OpenMP directives. - AllowStandaloneOpenMPDirectives = 0x2, + AllowStandaloneOpenMPDirectives = 0x1, /// This context is at the top level of a GNU statement expression. - InStmtExpr = 0x4, + InStmtExpr = 0x2, /// The context of a regular substatement. SubStmt = 0, /// The context of a compound-statement. - Compound = AllowDeclarationsInC | AllowStandaloneOpenMPDirectives, + Compound = AllowStandaloneOpenMPDirectives, LLVM_MARK_AS_BITMASK_ENUM(InStmtExpr) }; @@ -3524,6 +3526,20 @@ public: /// where, map-type-modifier ::= always | close | mapper(mapper-identifier) bool parseMapTypeModifiers(Sema::OpenMPVarListDataTy &Data); + //===--------------------------------------------------------------------===// + // OpenACC Parsing. + + /// Placeholder for now, should just ignore the directives after emitting a + /// diagnostic. Eventually will be split into a few functions to parse + /// different situations. +public: + DeclGroupPtrTy ParseOpenACCDirectiveDecl(); + StmtResult ParseOpenACCDirectiveStmt(); + +private: + void ParseOpenACCDirective(); + ExprResult ParseOpenACCRoutineName(); + private: //===--------------------------------------------------------------------===// // C++ 14: Templates [temp] diff --git a/clang/include/clang/Parse/RAIIObjectsForParser.h b/clang/include/clang/Parse/RAIIObjectsForParser.h index cb525c9d0edd6bce326f09705cd6d8406bc5b205..e1626a7870bb7aed790e9c5e50ec9415b8eca6fc 100644 --- a/clang/include/clang/Parse/RAIIObjectsForParser.h +++ b/clang/include/clang/Parse/RAIIObjectsForParser.h @@ -309,6 +309,25 @@ namespace clang { ~ParsingOpenMPDirectiveRAII() { restore(); } }; + /// Activates OpenACC parsing mode to preseve OpenACC specific annotation + /// tokens. + class ParsingOpenACCDirectiveRAII { + Parser &P; + bool OldVal; + + public: + ParsingOpenACCDirectiveRAII(Parser &P, bool Value = true) + : P(P), OldVal(P.OpenACCDirectiveParsing) { + P.OpenACCDirectiveParsing = Value; + } + + /// This can be used to restore the state early, before the dtor + /// is run. + void restore() { P.OpenMPDirectiveParsing = OldVal; } + + ~ParsingOpenACCDirectiveRAII() { restore(); } + }; + /// RAII object that makes '>' behave either as an operator /// or as the closing angle bracket for a template argument list. class GreaterThanIsOperatorScope { diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index a97968dc7b20967d8779ede200bc7788675ceb74..6ccabad3af54468a73388183cc281403df491027 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -192,6 +192,9 @@ class Sema; /// C-only conversion between pointers with incompatible types ICK_Incompatible_Pointer_Conversion, + /// Fixed point type conversions according to N1169. + ICK_Fixed_Point_Conversion, + /// The number of conversion kinds ICK_Num_Conversion_Kinds, }; @@ -254,10 +257,7 @@ class Sema; /// sequence (C++ 13.3.3.1.1). A standard conversion sequence /// contains between zero and three conversions. If a particular /// conversion is not needed, it will be set to the identity conversion - /// (ICK_Identity). Note that the three conversions are - /// specified as separate members (rather than in an array) so that - /// we can keep the size of a standard conversion sequence to a - /// single word. + /// (ICK_Identity). class StandardConversionSequence { public: /// First -- The first conversion can be an lvalue-to-rvalue diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index daed24be0a86d11f4035f233d0dd9d98438e21ca..ee2ed674f06bdd84ff42efa7b8a07e716c556b06 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_SEMA_SEMA_H #define LLVM_CLANG_SEMA_SEMA_H +#include "clang/APINotes/APINotesManager.h" #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTFwd.h" #include "clang/AST/Attr.h" @@ -408,6 +409,7 @@ public: ASTConsumer &Consumer; DiagnosticsEngine &Diags; SourceManager &SourceMgr; + api_notes::APINotesManager APINotes; /// Flag indicating whether or not to collect detailed statistics. bool CollectStats; @@ -710,9 +712,13 @@ public: return result; } + // Saves the current floating-point pragma stack and clear it in this Sema. class FpPragmaStackSaveRAII { public: - FpPragmaStackSaveRAII(Sema &S) : S(S), SavedStack(S.FpPragmaStack) {} + FpPragmaStackSaveRAII(Sema &S) + : S(S), SavedStack(std::move(S.FpPragmaStack)) { + S.FpPragmaStack.Stack.clear(); + } ~FpPragmaStackSaveRAII() { S.FpPragmaStack = std::move(SavedStack); } private: @@ -722,7 +728,6 @@ public: void resetFPOptions(FPOptions FPO) { CurFPFeatures = FPO; - FpPragmaStack.Stack.clear(); FpPragmaStack.CurrentValue = FPO.getChangesFrom(FPOptions(LangOpts)); } @@ -2096,6 +2101,9 @@ public: QualType BuildAddressSpaceAttr(QualType &T, Expr *AddrSpace, SourceLocation AttrLoc); + CodeAlignAttr *BuildCodeAlignAttr(const AttributeCommonInfo &CI, Expr *E); + bool CheckRebuiltCodeAlignStmtAttributes(ArrayRef Attrs); + bool CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc); bool CheckFunctionReturnType(QualType T, SourceLocation Loc); @@ -2303,7 +2311,6 @@ private: struct ModuleScope { SourceLocation BeginLoc; clang::Module *Module = nullptr; - bool ModuleInterface = false; VisibleModuleSet OuterVisibleModules; }; /// The modules we're currently parsing. @@ -2318,14 +2325,9 @@ private: clang::Module *TheGlobalModuleFragment = nullptr; /// The implicit global module fragments of the current translation unit. - /// We would only create at most two implicit global module fragments to - /// avoid performance penalties when there are many language linkage - /// exports. /// - /// The contents in the implicit global module fragment can't be discarded - /// no matter if it is exported or not. + /// The contents in the implicit global module fragment can't be discarded. clang::Module *TheImplicitGlobalModuleFragment = nullptr; - clang::Module *TheExportedImplicitGlobalModuleFragment = nullptr; /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet DeferredExportedNamespaces; @@ -2337,9 +2339,7 @@ private: /// Helper function to judge if we are in module purview. /// Return false if we are not in a module. - bool isCurrentModulePurview() const { - return getCurrentModule() ? getCurrentModule()->isModulePurview() : false; - } + bool isCurrentModulePurview() const; /// Enter the scope of the explicit global module fragment. Module *PushGlobalModuleFragment(SourceLocation BeginLoc); @@ -2347,8 +2347,7 @@ private: void PopGlobalModuleFragment(); /// Enter the scope of an implicit global module fragment. - Module *PushImplicitGlobalModuleFragment(SourceLocation BeginLoc, - bool IsExported); + Module *PushImplicitGlobalModuleFragment(SourceLocation BeginLoc); /// Leave the scope of an implicit global module fragment. void PopImplicitGlobalModuleFragment(); @@ -2367,9 +2366,11 @@ public: return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; } - /// Is the module scope we are an interface? - bool currentModuleIsInterface() const { - return ModuleScopes.empty() ? false : ModuleScopes.back().ModuleInterface; + /// Is the module scope we are an implementation unit? + bool currentModuleIsImplementation() const { + return ModuleScopes.empty() + ? false + : ModuleScopes.back().Module->isModuleImplementation(); } /// Is the module scope we are in a C++ Header Unit? @@ -3736,6 +3737,9 @@ public: int X, int Y, int Z); HLSLShaderAttr *mergeHLSLShaderAttr(Decl *D, const AttributeCommonInfo &AL, HLSLShaderAttr::ShaderType ShaderType); + HLSLParamModifierAttr * + mergeHLSLParamModifierAttr(Decl *D, const AttributeCommonInfo &AL, + HLSLParamModifierAttr::Spelling Spelling); void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); @@ -3848,6 +3852,12 @@ public: const FunctionProtoType *NewType, unsigned *ArgPos = nullptr, bool Reversed = false); + + bool FunctionNonObjectParamTypesAreEqual(const FunctionDecl *OldFunction, + const FunctionDecl *NewFunction, + unsigned *ArgPos = nullptr, + bool Reversed = false); + void HandleFunctionTypeMismatch(PartialDiagnostic &PDiag, QualType FromType, QualType ToType); @@ -5456,7 +5466,7 @@ public: bool DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *PD, ObjCMethodDecl *Getter, SourceLocation Loc); - void DiagnoseSentinelCalls(NamedDecl *D, SourceLocation Loc, + void DiagnoseSentinelCalls(const NamedDecl *D, SourceLocation Loc, ArrayRef Args); void PushExpressionEvaluationContext( @@ -5723,8 +5733,7 @@ public: // __FUNCTION__) are replaced (expanded) with string-literal Tokens. std::vector ExpandFunctionLocalPredefinedMacros(ArrayRef Toks); - ExprResult BuildPredefinedExpr(SourceLocation Loc, - PredefinedExpr::IdentKind IK); + ExprResult BuildPredefinedExpr(SourceLocation Loc, PredefinedIdentKind IK); ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val); @@ -6086,14 +6095,13 @@ public: // __builtin_LINE(), __builtin_FUNCTION(), __builtin_FUNCSIG(), // __builtin_FILE(), __builtin_COLUMN(), __builtin_source_location() - ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind, + ExprResult ActOnSourceLocExpr(SourceLocIdentKind Kind, SourceLocation BuiltinLoc, SourceLocation RPLoc); // Build a potentially resolved SourceLocExpr. - ExprResult BuildSourceLocExpr(SourceLocExpr::IdentKind Kind, - QualType ResultTy, SourceLocation BuiltinLoc, - SourceLocation RPLoc, + ExprResult BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy, + SourceLocation BuiltinLoc, SourceLocation RPLoc, DeclContext *ParentContext); // __null @@ -6325,36 +6333,30 @@ public: /// including handling of its default argument expressions. /// /// \param ConstructKind - a CXXConstructExpr::ConstructionKind - ExprResult - BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, - NamedDecl *FoundDecl, - CXXConstructorDecl *Constructor, MultiExprArg Exprs, - bool HadMultipleCandidates, bool IsListInitialization, - bool IsStdInitListInitialization, - bool RequiresZeroInit, unsigned ConstructKind, - SourceRange ParenRange); + ExprResult BuildCXXConstructExpr( + SourceLocation ConstructLoc, QualType DeclInitType, NamedDecl *FoundDecl, + CXXConstructorDecl *Constructor, MultiExprArg Exprs, + bool HadMultipleCandidates, bool IsListInitialization, + bool IsStdInitListInitialization, bool RequiresZeroInit, + CXXConstructionKind ConstructKind, SourceRange ParenRange); /// Build a CXXConstructExpr whose constructor has already been resolved if /// it denotes an inherited constructor. - ExprResult - BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, - CXXConstructorDecl *Constructor, bool Elidable, - MultiExprArg Exprs, - bool HadMultipleCandidates, bool IsListInitialization, - bool IsStdInitListInitialization, - bool RequiresZeroInit, unsigned ConstructKind, - SourceRange ParenRange); + ExprResult BuildCXXConstructExpr( + SourceLocation ConstructLoc, QualType DeclInitType, + CXXConstructorDecl *Constructor, bool Elidable, MultiExprArg Exprs, + bool HadMultipleCandidates, bool IsListInitialization, + bool IsStdInitListInitialization, bool RequiresZeroInit, + CXXConstructionKind ConstructKind, SourceRange ParenRange); // FIXME: Can we remove this and have the above BuildCXXConstructExpr check if // the constructor can be elidable? - ExprResult - BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, - NamedDecl *FoundDecl, - CXXConstructorDecl *Constructor, bool Elidable, - MultiExprArg Exprs, bool HadMultipleCandidates, - bool IsListInitialization, - bool IsStdInitListInitialization, bool RequiresZeroInit, - unsigned ConstructKind, SourceRange ParenRange); + ExprResult BuildCXXConstructExpr( + SourceLocation ConstructLoc, QualType DeclInitType, NamedDecl *FoundDecl, + CXXConstructorDecl *Constructor, bool Elidable, MultiExprArg Exprs, + bool HadMultipleCandidates, bool IsListInitialization, + bool IsStdInitListInitialization, bool RequiresZeroInit, + CXXConstructionKind ConstructKind, SourceRange ParenRange); ExprResult ConvertMemberDefaultInitExpression(FieldDecl *FD, Expr *InitExpr, SourceLocation InitLoc); @@ -8476,6 +8478,8 @@ public: ArrayRef SugaredConverted, ArrayRef CanonicalConverted, bool &HasDefaultArg); + SourceLocation getTopMostPointOfInstantiation(const NamedDecl *) const; + /// Specifies the context in which a particular template /// argument is being checked. enum CheckTemplateArgumentKind { @@ -8552,6 +8556,10 @@ public: TemplateParameterList *Params, TemplateArgumentLoc &Arg); + void NoteTemplateLocation(const NamedDecl &Decl, + std::optional ParamRange = {}); + void NoteTemplateParameterLocation(const NamedDecl &Decl); + ExprResult BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, QualType ParamType, @@ -8854,6 +8862,9 @@ public: /// The type of an exception. UPPC_ExceptionType, + /// Explicit specialization. + UPPC_ExplicitSpecialization, + /// Partial specialization. UPPC_PartialSpecialization, @@ -10998,7 +11009,10 @@ public: /// Called on well formed /// \#pragma clang fp reassociate - void ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled); + /// or + /// \#pragma clang fp reciprocal + void ActOnPragmaFPValueChangingOption(SourceLocation Loc, PragmaFPKind Kind, + bool IsEnabled); /// ActOnPragmaFenvAccess - Called on well formed /// \#pragma STDC FENV_ACCESS @@ -11198,6 +11212,12 @@ public: bool buildCoroutineParameterMoves(SourceLocation Loc); VarDecl *buildCoroutinePromise(SourceLocation Loc); void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body); + + // As a clang extension, enforces that a non-coroutine function must be marked + // with [[clang::coro_wrapper]] if it returns a type marked with + // [[clang::coro_return_type]]. + // Expects that FD is not a coroutine. + void CheckCoroutineWrapper(FunctionDecl *FD); /// Lookup 'coroutine_traits' in std namespace and std::experimental /// namespace. The namespace found is recorded in Namespace. ClassTemplateDecl *lookupCoroutineTraits(SourceLocation KwLoc, @@ -12194,6 +12214,13 @@ public: /// Called on well-formed 'compare' clause. OMPClause *ActOnOpenMPCompareClause(SourceLocation StartLoc, SourceLocation EndLoc); + /// Called on well-formed 'fail' clause. + OMPClause *ActOnOpenMPFailClause(SourceLocation StartLoc, + SourceLocation EndLoc); + OMPClause *ActOnOpenMPFailClause( + OpenMPClauseKind Kind, SourceLocation KindLoc, + SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-formed 'seq_cst' clause. OMPClause *ActOnOpenMPSeqCstClause(SourceLocation StartLoc, SourceLocation EndLoc); @@ -13492,6 +13519,10 @@ public: /// host or device attribute. void CUDASetLambdaAttrs(CXXMethodDecl *Method); + /// Record \p FD if it is a CUDA/HIP implicit host device function used on + /// device side in device compilation. + void CUDARecordImplicitHostDeviceFuncUsedByDevice(const FunctionDecl *FD); + /// Finds a function in \p Matches with highest calling priority /// from \p Caller context and erases all functions with lower /// calling priority. @@ -14038,7 +14069,9 @@ public: /// If true, \c Type should be compared with other expression's types for /// layout-compatibility. + LLVM_PREFERRED_TYPE(bool) unsigned LayoutCompatible : 1; + LLVM_PREFERRED_TYPE(bool) unsigned MustBeNull : 1; }; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5c32fbc079c9a651a741a0e91c9bd5845d5f9ff2..fdd64f2abbe93752510ff51d6d99796d2ffa8c04 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1101,7 +1101,7 @@ enum PredefinedTypeIDs { /// /// Type IDs for non-predefined types will start at /// NUM_PREDEF_TYPE_IDs. -const unsigned NUM_PREDEF_TYPE_IDS = 500; +const unsigned NUM_PREDEF_TYPE_IDS = 502; // Ensure we do not overrun the predefined types we reserved // in the enum PredefinedTypeIDs above. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index bafbe779d60acff0e9f431399f58c260e4320a9b..7eefdca6815cdadb81a8fd34bbdae8c2d0760800 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -312,9 +312,6 @@ public: StringRef SpecificModuleCachePath, bool Complain) override; void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override; - -private: - void Error(const char *Msg); }; /// ASTReaderListenter implementation to set SuggestedPredefines of diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h index d643e54e3d923a7a03f7cc97e5331c24dcb06990..80a1359fd16aa02b9f1b8bb5d5f1bc303fda95b0 100644 --- a/clang/include/clang/Serialization/ASTRecordReader.h +++ b/clang/include/clang/Serialization/ASTRecordReader.h @@ -370,10 +370,6 @@ private: uint64_t Offset; }; -inline void PCHValidator::Error(const char *Msg) { - Reader.Error(Msg); -} - } // namespace clang #endif diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 3019bbc2ddc9cc76eb4e7d4dd8a167dd1fbf8118..a56929ef0245ee9733e3d5afa80848837cc501fa 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -746,7 +746,7 @@ public: ASTReader *getChain() const { return Chain; } bool isWritingStdCXXNamedModules() const { - return WritingModule && WritingModule->isModulePurview(); + return WritingModule && WritingModule->isNamedModule(); } private: diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index b6e9f0fae1c7f484c0bb65914af151a5dc0ca847..1d224786372e823a88e0caf66d83a7fc9bdfff27 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -71,10 +71,12 @@ def InsecureAPI : Package<"insecureAPI">, ParentPackage; def SecurityAlpha : Package<"security">, ParentPackage; def Taint : Package<"taint">, ParentPackage; -def CERT : Package<"cert">, ParentPackage; -def POS : Package<"pos">, ParentPackage; +def CERT : Package<"cert">, ParentPackage; def ENV : Package<"env">, ParentPackage; +def CERTAlpha : Package<"cert">, ParentPackage; +def POSAlpha : Package<"pos">, ParentPackage; + def Unix : Package<"unix">; def UnixAlpha : Package<"unix">, ParentPackage; def CString : Package<"cstring">, ParentPackage; @@ -318,6 +320,10 @@ def C11LockChecker : Checker<"C11Lock">, Dependencies<[PthreadLockBase]>, Documentation; +def StdVariantChecker : Checker<"StdVariant">, + HelpText<"Check for bad type access for std::variant.">, + Documentation; + } // end "alpha.core" //===----------------------------------------------------------------------===// @@ -517,6 +523,18 @@ def DynamicMemoryModeling: Checker<"DynamicMemoryModeling">, Documentation, Hidden; +def ErrnoChecker : Checker<"Errno">, + HelpText<"Check for improper use of 'errno'">, + Dependencies<[ErrnoModeling]>, + CheckerOptions<[ + CmdLineOption, + ]>, + Documentation; + def MallocChecker: Checker<"Malloc">, HelpText<"Check for memory leaks, double free, and use-after-free problems. " "Traces memory managed by malloc()/free().">, @@ -561,18 +579,6 @@ def VforkChecker : Checker<"Vfork">, let ParentPackage = UnixAlpha in { -def ErrnoChecker : Checker<"Errno">, - HelpText<"Check for improper use of 'errno'">, - Dependencies<[ErrnoModeling]>, - CheckerOptions<[ - CmdLineOption, - ]>, - Documentation; - def ChrootChecker : Checker<"Chroot">, HelpText<"Check improper use of chroot">, Documentation; @@ -989,15 +995,6 @@ def FloatLoopCounter : Checker<"FloatLoopCounter">, } // end "security" -let ParentPackage = POS in { - - def PutenvWithAuto : Checker<"34c">, - HelpText<"Finds calls to the 'putenv' function which pass a pointer to " - "an automatic variable as the argument.">, - Documentation; - -} // end "alpha.cert.pos" - let ParentPackage = ENV in { def InvalidPtrChecker : Checker<"InvalidPtr">, @@ -1009,11 +1006,20 @@ let ParentPackage = ENV in { "standard), which can lead to false positives depending on " "implementation.", "false", - InAlpha>, + Released>, ]>, Documentation; -} // end "alpha.cert.env" +} // end "security.cert.env" + +let ParentPackage = POSAlpha in { + + def PutenvWithAuto : Checker<"34c">, + HelpText<"Finds calls to the 'putenv' function which pass a pointer to " + "an automatic variable as the argument.">, + Documentation; + +} // end "alpha.cert.pos" let ParentPackage = SecurityAlpha in { diff --git a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h index 3ae28c1dba3eb5aa58fa9822b68d134961d4e215..43a70f596a4da28dbd38ea91b628d8bbe8671a23 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h +++ b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h @@ -65,7 +65,7 @@ public: return "undefined value"; } - std::string VisitLocMemRegionVal(loc::MemRegionVal V) { + std::string VisitMemRegionVal(loc::MemRegionVal V) { const MemRegion *R = V.getRegion(); // Avoid the weird "pointer to pointee of ...". if (auto SR = dyn_cast(R)) { @@ -76,7 +76,7 @@ public: return "pointer to " + Visit(R); } - std::string VisitLocConcreteInt(loc::ConcreteInt V) { + std::string VisitConcreteInt(loc::ConcreteInt V) { const llvm::APSInt &I = V.getValue(); std::string Str; llvm::raw_string_ostream OS(Str); @@ -84,11 +84,11 @@ public: return Str; } - std::string VisitNonLocSymbolVal(nonloc::SymbolVal V) { + std::string VisitSymbolVal(nonloc::SymbolVal V) { return Visit(V.getSymbol()); } - std::string VisitNonLocConcreteInt(nonloc::ConcreteInt V) { + std::string VisitConcreteInt(nonloc::ConcreteInt V) { const llvm::APSInt &I = V.getValue(); std::string Str; llvm::raw_string_ostream OS(Str); @@ -97,7 +97,7 @@ public: return Str; } - std::string VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) { + std::string VisitLazyCompoundVal(nonloc::LazyCompoundVal V) { return "lazily frozen compound value of " + Visit(V.getRegion()); } diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index 826370c2648c287c7b0833e7eddfe6c6a3c3fa14..8956552e7bfc21e4c0f46ecbb7e0285f0bb6f032 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -26,7 +26,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" #include "llvm/ADT/SmallSet.h" diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 8129ebc8fdc6937cb4f213447ea9ca586896320c..0d36587484bf9c30c567ba242ab0df647ebc020d 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -78,7 +78,7 @@ enum CallEventKind { class CallEvent; -template +template class CallEventRef : public IntrusiveRefCntPtr { public: CallEventRef(const T *Call) : IntrusiveRefCntPtr(Call) {} @@ -94,8 +94,7 @@ public: // Allow implicit conversions to a superclass type, since CallEventRef // behaves like a pointer-to-const. - template - operator CallEventRef () const { + template operator CallEventRef() const { return this->get(); } }; @@ -124,9 +123,9 @@ class RuntimeDefinition { public: RuntimeDefinition() = default; - RuntimeDefinition(const Decl *InD): D(InD) {} + RuntimeDefinition(const Decl *InD) : D(InD) {} RuntimeDefinition(const Decl *InD, bool Foreign) : D(InD), Foreign(Foreign) {} - RuntimeDefinition(const Decl *InD, const MemRegion *InR): D(InD), R(InR) {} + RuntimeDefinition(const Decl *InD, const MemRegion *InR) : D(InD), R(InR) {} const Decl *getDecl() { return D; } bool isForeign() const { return Foreign; } @@ -207,8 +206,9 @@ protected: /// Used to specify non-argument regions that will be invalidated as a /// result of this call. - virtual void getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const {} + virtual void + getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const {} public: CallEvent &operator=(const CallEvent &) = delete; @@ -231,14 +231,10 @@ public: void setForeign(bool B) const { Foreign = B; } /// The state in which the call is being evaluated. - const ProgramStateRef &getState() const { - return State; - } + const ProgramStateRef &getState() const { return State; } /// The context in which the call is being evaluated. - const LocationContext *getLocationContext() const { - return LCtx; - } + const LocationContext *getLocationContext() const { return LCtx; } const CFGBlock::ConstCFGElementRef &getCFGElementRef() const { return ElemRef; @@ -270,7 +266,7 @@ public: SourceLocation Loc = D->getLocation(); if (Loc.isValid()) { const SourceManager &SM = - getState()->getStateManager().getContext().getSourceManager(); + getState()->getStateManager().getContext().getSourceManager(); return SM.isInSystemHeader(D->getLocation()); } @@ -324,9 +320,7 @@ public: // NOTE: The exact semantics of this are still being defined! // We don't really want a list of hardcoded exceptions in the long run, // but we don't want duplicated lists of known APIs in the short term either. - virtual bool argumentsMayEscape() const { - return hasNonZeroCallbackArg(); - } + virtual bool argumentsMayEscape() const { return hasNonZeroCallbackArg(); } /// Returns true if the callee is an externally-visible function in the /// top-level namespace, such as \c malloc. @@ -456,6 +450,14 @@ public: /// can be retrieved from its construction context. std::optional getReturnValueUnderConstruction() const; + // Returns the CallEvent representing the caller of this function + const CallEventRef<> getCaller() const; + + // Returns true if the function was called from a standard library function. + // If not or could not get the caller (it may be a top level function) + // returns false. + bool isCalledFromSystemHeader() const; + // Iterator access to formal parameters and their types. private: struct GetTypeFn { @@ -579,8 +581,9 @@ protected: void cloneTo(void *Dest) const override { new (Dest) BlockCall(*this); } - void getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const override; + void getExtraInvalidatedValues( + ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; public: const CallExpr *getOriginExpr() const override { @@ -650,14 +653,12 @@ public: // the block body and analyze the operator() method on the captured lambda. const VarDecl *LambdaVD = getRegionStoringCapturedLambda()->getDecl(); const CXXRecordDecl *LambdaDecl = LambdaVD->getType()->getAsCXXRecordDecl(); - CXXMethodDecl* LambdaCallOperator = LambdaDecl->getLambdaCallOperator(); + CXXMethodDecl *LambdaCallOperator = LambdaDecl->getLambdaCallOperator(); return RuntimeDefinition(LambdaCallOperator); } - bool argumentsMayEscape() const override { - return true; - } + bool argumentsMayEscape() const override { return true; } void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const override; @@ -684,8 +685,9 @@ protected: : AnyFunctionCall(D, St, LCtx, ElemRef) {} CXXInstanceCall(const CXXInstanceCall &Other) = default; - void getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const override; + void getExtraInvalidatedValues( + ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; public: /// Returns the expression representing the implicit 'this' object. @@ -843,7 +845,9 @@ protected: CXXDestructorCall(const CXXDestructorCall &Other) = default; - void cloneTo(void *Dest) const override {new (Dest) CXXDestructorCall(*this);} + void cloneTo(void *Dest) const override { + new (Dest) CXXDestructorCall(*this); + } public: SourceRange getSourceRange() const override { return Location; } @@ -880,8 +884,9 @@ protected: Data = Target; } - void getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const override; + void getExtraInvalidatedValues( + ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const override; @@ -921,7 +926,9 @@ protected: CXXConstructorCall(const CXXConstructorCall &Other) = default; - void cloneTo(void *Dest) const override { new (Dest) CXXConstructorCall(*this); } + void cloneTo(void *Dest) const override { + new (Dest) CXXConstructorCall(*this); + } public: const CXXConstructExpr *getOriginExpr() const override { @@ -1040,7 +1047,9 @@ protected: : AnyFunctionCall(E, St, LCtx, ElemRef) {} CXXAllocatorCall(const CXXAllocatorCall &Other) = default; - void cloneTo(void *Dest) const override { new (Dest) CXXAllocatorCall(*this); } + void cloneTo(void *Dest) const override { + new (Dest) CXXAllocatorCall(*this); + } public: const CXXNewExpr *getOriginExpr() const override { @@ -1154,11 +1163,7 @@ public: // // Note to maintainers: OCM_Message should always be last, since it does not // need to fit in the Data field's low bits. -enum ObjCMessageKind { - OCM_PropertyAccess, - OCM_Subscript, - OCM_Message -}; +enum ObjCMessageKind { OCM_PropertyAccess, OCM_Subscript, OCM_Message }; /// Represents any expression that calls an Objective-C method. /// @@ -1180,8 +1185,9 @@ protected: void cloneTo(void *Dest) const override { new (Dest) ObjCMethodCall(*this); } - void getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const override; + void getExtraInvalidatedValues( + ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; /// Check if the selector may have multiple definitions (may have overrides). virtual bool canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, @@ -1196,9 +1202,7 @@ public: return getOriginExpr()->getMethodDecl(); } - unsigned getNumArgs() const override { - return getOriginExpr()->getNumArgs(); - } + unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs(); } const Expr *getArgExpr(unsigned Index) const override { return getOriginExpr()->getArg(Index); @@ -1212,9 +1216,7 @@ public: return getOriginExpr()->getMethodFamily(); } - Selector getSelector() const { - return getOriginExpr()->getSelector(); - } + Selector getSelector() const { return getOriginExpr()->getSelector(); } SourceRange getSourceRange() const override; @@ -1262,7 +1264,7 @@ public: void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const override; - ArrayRef parameters() const override; + ArrayRef parameters() const override; Kind getKind() const override { return CE_ObjCMessage; } StringRef getKindAsString() const override { return "ObjCMethodCall"; } @@ -1336,8 +1338,8 @@ public: CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {} /// Gets an outside caller given a callee context. - CallEventRef<> - getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State); + CallEventRef<> getCaller(const StackFrameContext *CalleeCtx, + ProgramStateRef State); /// Gets a call event for a function call, Objective-C method call, /// a 'new', or a 'delete' call. @@ -1433,11 +1435,10 @@ inline void CallEvent::Release() const { namespace llvm { // Support isa<>, cast<>, and dyn_cast<> for CallEventRef. -template struct simplify_type< clang::ento::CallEventRef> { +template struct simplify_type> { using SimpleType = const T *; - static SimpleType - getSimplifiedValue(clang::ento::CallEventRef Val) { + static SimpleType getSimplifiedValue(clang::ento::CallEventRef Val) { return Val.get(); } }; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h index adbfa47c0c3c304f782a204e9c799872823aeef8..52d1526b1acf44be3bcfaf91b0f0d259481658a6 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h @@ -22,7 +22,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" -#include "llvm/ADT/ImmutableMap.h" namespace clang { namespace ento { diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index 692c4384586569ebff6e7bcb2f00670ef3506c78..a64cf7ae4efcb82db450d0f67ed792c461f406af 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -215,6 +215,15 @@ public: const LocationContext *LCtx, QualType type, unsigned Count); + /// Create an SVal representing the result of an alloca()-like call, that is, + /// an AllocaRegion on the stack. + /// + /// After calling this function, it's a good idea to set the extent of the + /// returned AllocaRegion. + loc::MemRegionVal getAllocaRegionVal(const Expr *E, + const LocationContext *LCtx, + unsigned Count); + DefinedOrUnknownSVal getDerivedRegionValueSymbolVal( SymbolRef parentSymbol, const TypedValueRegion *region); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h index fc83e26183b3e7d083f9fd58e5566ca1be7c955d..b10f416f44356ffe40c117dd03ab96a317f5f4c8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h @@ -14,9 +14,9 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALVISITOR_H +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" namespace clang { @@ -25,49 +25,40 @@ namespace ento { /// SValVisitor - this class implements a simple visitor for SVal /// subclasses. template class SValVisitor { -public: - -#define DISPATCH(NAME, CLASS) \ - return static_cast(this)->Visit ## NAME(V.castAs()) + ImplClass &derived() { return *static_cast(this); } +public: RetTy Visit(SVal V) { // Dispatch to VisitFooVal for each FooVal. - // Take namespaces (loc:: and nonloc::) into account. - switch (V.getBaseKind()) { -#define BASIC_SVAL(Id, Parent) case SVal::Id ## Kind: DISPATCH(Id, Id); + switch (V.getKind()) { +#define BASIC_SVAL(Id, Parent) \ + case SVal::Id##Kind: \ + return derived().Visit##Id(V.castAs()); +#define LOC_SVAL(Id, Parent) \ + case SVal::Loc##Id##Kind: \ + return derived().Visit##Id(V.castAs()); +#define NONLOC_SVAL(Id, Parent) \ + case SVal::NonLoc##Id##Kind: \ + return derived().Visit##Id(V.castAs()); #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" - case SVal::LocKind: - switch (V.getSubKind()) { -#define LOC_SVAL(Id, Parent) \ - case loc::Id ## Kind: DISPATCH(Loc ## Id, loc :: Id); -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" - } - llvm_unreachable("Unknown Loc sub-kind!"); - case SVal::NonLocKind: - switch (V.getSubKind()) { -#define NONLOC_SVAL(Id, Parent) \ - case nonloc::Id ## Kind: DISPATCH(NonLoc ## Id, nonloc :: Id); -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" - } - llvm_unreachable("Unknown NonLoc sub-kind!"); } llvm_unreachable("Unknown SVal kind!"); } -#define BASIC_SVAL(Id, Parent) \ - RetTy Visit ## Id(Id V) { DISPATCH(Parent, Id); } -#define ABSTRACT_SVAL(Id, Parent) \ - BASIC_SVAL(Id, Parent) -#define LOC_SVAL(Id, Parent) \ - RetTy VisitLoc ## Id(loc::Id V) { DISPATCH(Parent, Parent); } -#define NONLOC_SVAL(Id, Parent) \ - RetTy VisitNonLoc ## Id(nonloc::Id V) { DISPATCH(Parent, Parent); } + // Dispatch to the more generic handler as a default implementation. +#define BASIC_SVAL(Id, Parent) \ + RetTy Visit##Id(Id V) { return derived().Visit##Parent(V.castAs()); } +#define ABSTRACT_SVAL(Id, Parent) BASIC_SVAL(Id, Parent) +#define LOC_SVAL(Id, Parent) \ + RetTy Visit##Id(loc::Id V) { return derived().VisitLoc(V.castAs()); } +#define NONLOC_SVAL(Id, Parent) \ + RetTy Visit##Id(nonloc::Id V) { \ + return derived().VisitNonLoc(V.castAs()); \ + } #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" // Base case, ignore it. :) RetTy VisitSVal(SVal V) { return RetTy(); } - -#undef DISPATCH }; /// SymExprVisitor - this class implements a simple visitor for SymExpr diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def index eb05de6d993334273c5ec62892cab4916ba54dd5..36d2425d155a92998eee42cb63e51c8e7004e77f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def @@ -6,28 +6,24 @@ // //===----------------------------------------------------------------------===// // -// The list of symbolic values (SVal kinds and sub-kinds) used in the Static -// Analyzer. The distinction between loc:: and nonloc:: SVal namespaces is +// The list of symbolic values (SVal kinds) used in the Static Analyzer. +// The distinction between `loc::` and `nonloc::` SVal namespaces is // currently hardcoded, because it is too peculiar and explicit to be handled // uniformly. In order to use this information, users of this file must define // one or more of the following macros: // -// BASIC_SVAL(Id, Parent) - for specific SVal sub-kinds, which are -// neither in loc:: nor in nonloc:: namespace; these classes occupy -// their own base kind IdKind. +// BASIC_SVAL(Id, Parent) - for specific SVal kinds, which are +// neither in `loc::` nor in `nonloc::` namespace. // // ABSTRACT_SVAL(Id, Parent) - for abstract SVal classes which are -// neither in loc:: nor in nonloc:: namespace, +// neither in `loc::` nor in `nonloc::` namespace, // -// ABSTRACT_SVAL_WITH_KIND(Id, Parent) - for SVal classes which are also -// neither in loc:: nor in nonloc:: namespace, but occupy a whole base kind -// identifier IdKind, much like BASIC_SVALs. +// LOC_SVAL(Id, Parent) - for values in `loc::` namespace. // -// LOC_SVAL(Id, Parent) - for values in loc:: namespace, which occupy a sub-kind -// loc::IdKind. +// NONLOC_SVAL(Id, Parent) - for values in `nonloc::` namespace. // -// NONLOC_SVAL(Id, Parent) - for values in nonloc:: namespace, which occupy a -// sub-kind nonloc::IdKind. +// SVAL_RANGE(Id, First, Last) - for defining range of subtypes of +// the abstract class `Id`. // //===----------------------------------------------------------------------===// @@ -39,10 +35,6 @@ #define ABSTRACT_SVAL(Id, Parent) #endif -#ifndef ABSTRACT_SVAL_WITH_KIND -#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) ABSTRACT_SVAL(Id, Parent) -#endif - #ifndef LOC_SVAL #define LOC_SVAL(Id, Parent) #endif @@ -51,24 +43,30 @@ #define NONLOC_SVAL(Id, Parent) #endif +#ifndef SVAL_RANGE +#define SVAL_RANGE(Id, First, Last) +#endif + BASIC_SVAL(UndefinedVal, SVal) ABSTRACT_SVAL(DefinedOrUnknownSVal, SVal) BASIC_SVAL(UnknownVal, DefinedOrUnknownSVal) ABSTRACT_SVAL(DefinedSVal, DefinedOrUnknownSVal) - ABSTRACT_SVAL_WITH_KIND(Loc, DefinedSVal) + ABSTRACT_SVAL(Loc, DefinedSVal) LOC_SVAL(ConcreteInt, Loc) LOC_SVAL(GotoLabel, Loc) LOC_SVAL(MemRegionVal, Loc) - ABSTRACT_SVAL_WITH_KIND(NonLoc, DefinedSVal) + SVAL_RANGE(Loc, ConcreteInt, MemRegionVal) + ABSTRACT_SVAL(NonLoc, DefinedSVal) NONLOC_SVAL(CompoundVal, NonLoc) NONLOC_SVAL(ConcreteInt, NonLoc) NONLOC_SVAL(LazyCompoundVal, NonLoc) NONLOC_SVAL(LocAsInteger, NonLoc) NONLOC_SVAL(SymbolVal, NonLoc) NONLOC_SVAL(PointerToMember, NonLoc) + SVAL_RANGE(NonLoc, CompoundVal, PointerToMember) +#undef SVAL_RANGE #undef NONLOC_SVAL #undef LOC_SVAL -#undef ABSTRACT_SVAL_WITH_KIND #undef ABSTRACT_SVAL #undef BASIC_SVAL diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index 00cce21151a70732117966c6f86ec2992c0e47e6..c60528b7685fe8297ff331aeef0f2a28f940b810 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -18,9 +18,11 @@ #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Casting.h" #include @@ -47,50 +49,30 @@ class PointerToMemberData; class SValBuilder; class TypedValueRegion; -namespace nonloc { - -/// Sub-kinds for NonLoc values. -enum Kind { -#define NONLOC_SVAL(Id, Parent) Id ## Kind, -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" -}; - -} // namespace nonloc - -namespace loc { - -/// Sub-kinds for Loc values. -enum Kind { -#define LOC_SVAL(Id, Parent) Id ## Kind, -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" -}; - -} // namespace loc - /// SVal - This represents a symbolic expression, which can be either /// an L-value or an R-value. /// class SVal { public: - enum BaseKind { - // The enumerators must be representable using 2 bits. -#define BASIC_SVAL(Id, Parent) Id ## Kind, -#define ABSTRACT_SVAL_WITH_KIND(Id, Parent) Id ## Kind, + enum SValKind : unsigned char { +#define BASIC_SVAL(Id, Parent) Id##Kind, +#define LOC_SVAL(Id, Parent) Loc##Id##Kind, +#define NONLOC_SVAL(Id, Parent) NonLoc##Id##Kind, +#define SVAL_RANGE(Id, First, Last) \ + BEGIN_##Id = Id##First##Kind, END_##Id = Id##Last##Kind, #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" }; - enum { BaseBits = 2, BaseMask = 0b11 }; protected: const void *Data = nullptr; + SValKind Kind = UndefinedValKind; - /// The lowest 2 bits are a BaseKind (0 -- 3). - /// The higher bits are an unsigned "kind" value. - unsigned Kind = 0; - - explicit SVal(const void *d, bool isLoc, unsigned ValKind) - : Data(d), Kind((isLoc ? LocKind : NonLocKind) | (ValKind << BaseBits)) {} + explicit SVal(SValKind Kind, const void *Data = nullptr) + : Data(Data), Kind(Kind) {} - explicit SVal(BaseKind k, const void *D = nullptr) : Data(D), Kind(k) {} + template const T *castDataAs() const { + return static_cast(Data); + } public: explicit SVal() = default; @@ -105,38 +87,25 @@ public: return llvm::dyn_cast(*this); } - unsigned getRawKind() const { return Kind; } - BaseKind getBaseKind() const { return (BaseKind) (Kind & BaseMask); } - unsigned getSubKind() const { return Kind >> BaseBits; } + SValKind getKind() const { return Kind; } // This method is required for using SVal in a FoldingSetNode. It // extracts a unique signature for this SVal object. void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) getRawKind()); ID.AddPointer(Data); + ID.AddInteger(llvm::to_underlying(getKind())); } - bool operator==(SVal R) const { - return getRawKind() == R.getRawKind() && Data == R.Data; - } - + bool operator==(SVal R) const { return Kind == R.Kind && Data == R.Data; } bool operator!=(SVal R) const { return !(*this == R); } - bool isUnknown() const { - return getRawKind() == UnknownValKind; - } + bool isUnknown() const { return getKind() == UnknownValKind; } - bool isUndef() const { - return getRawKind() == UndefinedValKind; - } + bool isUndef() const { return getKind() == UndefinedValKind; } - bool isUnknownOrUndef() const { - return getRawKind() <= UnknownValKind; - } + bool isUnknownOrUndef() const { return isUnknown() || isUndef(); } - bool isValid() const { - return getRawKind() > UnknownValKind; - } + bool isValid() const { return !isUnknownOrUndef(); } bool isConstant() const; @@ -207,10 +176,24 @@ inline raw_ostream &operator<<(raw_ostream &os, clang::ento::SVal V) { return os; } +namespace nonloc { +/// Sub-kinds for NonLoc values. +#define NONLOC_SVAL(Id, Parent) \ + inline constexpr auto Id##Kind = SVal::SValKind::NonLoc##Id##Kind; +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +} // namespace nonloc + +namespace loc { +/// Sub-kinds for Loc values. +#define LOC_SVAL(Id, Parent) \ + inline constexpr auto Id##Kind = SVal::SValKind::Loc##Id##Kind; +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.def" +} // namespace loc + class UndefinedVal : public SVal { public: UndefinedVal() : SVal(UndefinedValKind) {} - static bool classof(SVal V) { return V.getBaseKind() == UndefinedValKind; } + static bool classof(SVal V) { return V.getKind() == UndefinedValKind; } }; class DefinedOrUnknownSVal : public SVal { @@ -223,16 +206,15 @@ public: static bool classof(SVal V) { return !V.isUndef(); } protected: - explicit DefinedOrUnknownSVal(const void *d, bool isLoc, unsigned ValKind) - : SVal(d, isLoc, ValKind) {} - explicit DefinedOrUnknownSVal(BaseKind k, void *D = nullptr) : SVal(k, D) {} + explicit DefinedOrUnknownSVal(SValKind Kind, const void *Data = nullptr) + : SVal(Kind, Data) {} }; class UnknownVal : public DefinedOrUnknownSVal { public: explicit UnknownVal() : DefinedOrUnknownSVal(UnknownValKind) {} - static bool classof(SVal V) { return V.getBaseKind() == UnknownValKind; } + static bool classof(SVal V) { return V.getKind() == UnknownValKind; } }; class DefinedSVal : public DefinedOrUnknownSVal { @@ -246,22 +228,21 @@ public: static bool classof(SVal V) { return !V.isUnknownOrUndef(); } protected: - explicit DefinedSVal(const void *d, bool isLoc, unsigned ValKind) - : DefinedOrUnknownSVal(d, isLoc, ValKind) {} + explicit DefinedSVal(SValKind Kind, const void *Data) + : DefinedOrUnknownSVal(Kind, Data) {} }; /// Represents an SVal that is guaranteed to not be UnknownVal. class KnownSVal : public SVal { public: - KnownSVal(const DefinedSVal &V) : SVal(V) {} - KnownSVal(const UndefinedVal &V) : SVal(V) {} + /*implicit*/ KnownSVal(DefinedSVal V) : SVal(V) {} + /*implicit*/ KnownSVal(UndefinedVal V) : SVal(V) {} static bool classof(SVal V) { return !V.isUnknown(); } }; class NonLoc : public DefinedSVal { protected: - explicit NonLoc(unsigned SubKind, const void *d) - : DefinedSVal(d, false, SubKind) {} + NonLoc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {} public: void dumpToStream(raw_ostream &Out) const; @@ -271,13 +252,14 @@ public: T->isAnyComplexType() || T->isVectorType(); } - static bool classof(SVal V) { return V.getBaseKind() == NonLocKind; } + static bool classof(SVal V) { + return BEGIN_NonLoc <= V.getKind() && V.getKind() <= END_NonLoc; + } }; class Loc : public DefinedSVal { protected: - explicit Loc(unsigned SubKind, const void *D) - : DefinedSVal(const_cast(D), true, SubKind) {} + Loc(SValKind Kind, const void *Data) : DefinedSVal(Kind, Data) {} public: void dumpToStream(raw_ostream &Out) const; @@ -287,7 +269,9 @@ public: T->isReferenceType() || T->isNullPtrType(); } - static bool classof(SVal V) { return V.getBaseKind() == LocKind; } + static bool classof(SVal V) { + return BEGIN_Loc <= V.getKind() && V.getKind() <= END_Loc; + } }; //==------------------------------------------------------------------------==// @@ -300,9 +284,9 @@ namespace nonloc { class SymbolVal : public NonLoc { public: SymbolVal() = delete; - SymbolVal(SymbolRef sym) : NonLoc(SymbolValKind, sym) { - assert(sym); - assert(!Loc::isLocType(sym->getType())); + explicit SymbolVal(SymbolRef Sym) : NonLoc(SymbolValKind, Sym) { + assert(Sym); + assert(!Loc::isLocType(Sym->getType())); } LLVM_ATTRIBUTE_RETURNS_NONNULL @@ -314,27 +298,17 @@ public: return !isa(getSymbol()); } - static bool classof(SVal V) { - return V.getBaseKind() == NonLocKind && V.getSubKind() == SymbolValKind; - } - - static bool classof(NonLoc V) { return V.getSubKind() == SymbolValKind; } + static bool classof(SVal V) { return V.getKind() == SymbolValKind; } }; /// Value representing integer constant. class ConcreteInt : public NonLoc { public: - explicit ConcreteInt(const llvm::APSInt& V) : NonLoc(ConcreteIntKind, &V) {} - - const llvm::APSInt& getValue() const { - return *static_cast(Data); - } + explicit ConcreteInt(const llvm::APSInt &V) : NonLoc(ConcreteIntKind, &V) {} - static bool classof(SVal V) { - return V.getBaseKind() == NonLocKind && V.getSubKind() == ConcreteIntKind; - } + const llvm::APSInt &getValue() const { return *castDataAs(); } - static bool classof(NonLoc V) { return V.getSubKind() == ConcreteIntKind; } + static bool classof(SVal V) { return V.getKind() == ConcreteIntKind; } }; class LocAsInteger : public NonLoc { @@ -344,29 +318,20 @@ class LocAsInteger : public NonLoc { : NonLoc(LocAsIntegerKind, &data) { // We do not need to represent loc::ConcreteInt as LocAsInteger, // as it'd collapse into a nonloc::ConcreteInt instead. - assert(data.first.getBaseKind() == LocKind && - (data.first.getSubKind() == loc::MemRegionValKind || - data.first.getSubKind() == loc::GotoLabelKind)); + [[maybe_unused]] SValKind K = data.first.getKind(); + assert(K == loc::MemRegionValKind || K == loc::GotoLabelKind); } public: Loc getLoc() const { - const std::pair *D = - static_cast *>(Data); - return D->first.castAs(); + return castDataAs>()->first.castAs(); } unsigned getNumBits() const { - const std::pair *D = - static_cast *>(Data); - return D->second; + return castDataAs>()->second; } - static bool classof(SVal V) { - return V.getBaseKind() == NonLocKind && V.getSubKind() == LocAsIntegerKind; - } - - static bool classof(NonLoc V) { return V.getSubKind() == LocAsIntegerKind; } + static bool classof(SVal V) { return V.getKind() == LocAsIntegerKind; } }; class CompoundVal : public NonLoc { @@ -379,19 +344,14 @@ class CompoundVal : public NonLoc { public: LLVM_ATTRIBUTE_RETURNS_NONNULL const CompoundValData* getValue() const { - return static_cast(Data); + return castDataAs(); } using iterator = llvm::ImmutableList::iterator; - iterator begin() const; iterator end() const; - static bool classof(SVal V) { - return V.getBaseKind() == NonLocKind && V.getSubKind() == CompoundValKind; - } - - static bool classof(NonLoc V) { return V.getSubKind() == CompoundValKind; } + static bool classof(SVal V) { return V.getKind() == CompoundValKind; } }; class LazyCompoundVal : public NonLoc { @@ -405,7 +365,7 @@ class LazyCompoundVal : public NonLoc { public: LLVM_ATTRIBUTE_RETURNS_NONNULL const LazyCompoundValData *getCVData() const { - return static_cast(Data); + return castDataAs(); } /// It might return null. @@ -414,14 +374,7 @@ public: LLVM_ATTRIBUTE_RETURNS_NONNULL const TypedValueRegion *getRegion() const; - static bool classof(SVal V) { - return V.getBaseKind() == NonLocKind && - V.getSubKind() == LazyCompoundValKind; - } - - static bool classof(NonLoc V) { - return V.getSubKind() == LazyCompoundValKind; - } + static bool classof(SVal V) { return V.getKind() == LazyCompoundValKind; } }; /// Value representing pointer-to-member. @@ -459,14 +412,7 @@ public: iterator begin() const; iterator end() const; - static bool classof(SVal V) { - return V.getBaseKind() == NonLocKind && - V.getSubKind() == PointerToMemberKind; - } - - static bool classof(NonLoc V) { - return V.getSubKind() == PointerToMemberKind; - } + static bool classof(SVal V) { return V.getKind() == PointerToMemberKind; } private: explicit PointerToMember(const PTMDataType D) @@ -487,29 +433,23 @@ public: assert(Label); } - const LabelDecl *getLabel() const { - return static_cast(Data); - } + const LabelDecl *getLabel() const { return castDataAs(); } - static bool classof(SVal V) { - return V.getBaseKind() == LocKind && V.getSubKind() == GotoLabelKind; - } - - static bool classof(Loc V) { return V.getSubKind() == GotoLabelKind; } + static bool classof(SVal V) { return V.getKind() == GotoLabelKind; } }; class MemRegionVal : public Loc { public: - explicit MemRegionVal(const MemRegion* r) : Loc(MemRegionValKind, r) { + explicit MemRegionVal(const MemRegion *r) : Loc(MemRegionValKind, r) { assert(r); } /// Get the underlining region. - const MemRegion *getRegion() const { - return static_cast(Data); - } + LLVM_ATTRIBUTE_RETURNS_NONNULL + const MemRegion *getRegion() const { return castDataAs(); } /// Get the underlining region and strip casts. + LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion* stripCasts(bool StripBaseCasts = true) const; template @@ -525,26 +465,16 @@ public: return getRegion() != R.getRegion(); } - static bool classof(SVal V) { - return V.getBaseKind() == LocKind && V.getSubKind() == MemRegionValKind; - } - - static bool classof(Loc V) { return V.getSubKind() == MemRegionValKind; } + static bool classof(SVal V) { return V.getKind() == MemRegionValKind; } }; class ConcreteInt : public Loc { public: - explicit ConcreteInt(const llvm::APSInt& V) : Loc(ConcreteIntKind, &V) {} + explicit ConcreteInt(const llvm::APSInt &V) : Loc(ConcreteIntKind, &V) {} - const llvm::APSInt &getValue() const { - return *static_cast(Data); - } - - static bool classof(SVal V) { - return V.getBaseKind() == LocKind && V.getSubKind() == ConcreteIntKind; - } + const llvm::APSInt &getValue() const { return *castDataAs(); } - static bool classof(Loc V) { return V.getSubKind() == ConcreteIntKind; } + static bool classof(SVal V) { return V.getKind() == ConcreteIntKind; } }; } // namespace loc diff --git a/clang/include/clang/Support/RISCVVIntrinsicUtils.h b/clang/include/clang/Support/RISCVVIntrinsicUtils.h index 7904658576e5d509cfaf70dcf9a11aebe486af3c..49ce32553da81eb1c45a18c12e366e7f914b6885 100644 --- a/clang/include/clang/Support/RISCVVIntrinsicUtils.h +++ b/clang/include/clang/Support/RISCVVIntrinsicUtils.h @@ -97,13 +97,14 @@ enum class TypeModifier : uint8_t { UnsignedInteger = 1 << 3, SignedInteger = 1 << 4, Float = 1 << 5, + BFloat = 1 << 6, // LMUL1 should be kind of VectorTypeModifier, but that might come with // Widening2XVector for widening reduction. // However that might require VectorTypeModifier become bitmask rather than // simple enum, so we decide keek LMUL1 in TypeModifier for code size // optimization of clang binary size. - LMUL1 = 1 << 6, - MaxOffset = 6, + LMUL1 = 1 << 7, + MaxOffset = 7, LLVM_MARK_AS_BITMASK_ENUM(LMUL1), }; @@ -207,10 +208,11 @@ enum class BasicType : uint8_t { Int16 = 1 << 1, Int32 = 1 << 2, Int64 = 1 << 3, - Float16 = 1 << 4, - Float32 = 1 << 5, - Float64 = 1 << 6, - MaxOffset = 6, + BFloat16 = 1 << 4, + Float16 = 1 << 5, + Float32 = 1 << 6, + Float64 = 1 << 7, + MaxOffset = 7, LLVM_MARK_AS_BITMASK_ENUM(Float64), }; @@ -225,6 +227,7 @@ enum ScalarTypeKind : uint8_t { SignedInteger, UnsignedInteger, Float, + BFloat, Invalid, Undefined, }; @@ -300,6 +303,7 @@ public: return isVector() && ElementBitwidth == Width; } bool isFloat() const { return ScalarType == ScalarTypeKind::Float; } + bool isBFloat() const { return ScalarType == ScalarTypeKind::BFloat; } bool isSignedInteger() const { return ScalarType == ScalarTypeKind::SignedInteger; } diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h index dbe219b6dd8d72325fbd95f0ae03a85388391a2c..9a2aea5d6efa1708ac4527321a26a4805b909537 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -11,7 +11,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Lex/DependencyDirectivesScanner.h" -#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/ErrorOr.h" diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h index 109cf049a65231efb16e47777e0ade8125181e94..dcdf1c171f6d7315ddedb4048b1f12c3f4e7fa8f 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGSERVICE_H #include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" +#include "llvm/ADT/BitmaskEnum.h" namespace clang { namespace tooling { @@ -44,19 +45,34 @@ enum class ScanningOutputFormat { P1689, }; +enum class ScanningOptimizations { + None = 0, + + /// Remove unused header search paths including header maps. + HeaderSearch = 1, + + /// Remove warnings from system modules. + SystemWarnings = 2, + + LLVM_MARK_AS_BITMASK_ENUM(SystemWarnings), + All = HeaderSearch | SystemWarnings, + Default = All +}; + /// The dependency scanning service contains shared configuration and state that /// is used by the individual dependency scanning workers. class DependencyScanningService { public: - DependencyScanningService(ScanningMode Mode, ScanningOutputFormat Format, - bool OptimizeArgs = false, - bool EagerLoadModules = false); + DependencyScanningService( + ScanningMode Mode, ScanningOutputFormat Format, + ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default, + bool EagerLoadModules = false); ScanningMode getMode() const { return Mode; } ScanningOutputFormat getFormat() const { return Format; } - bool canOptimizeArgs() const { return OptimizeArgs; } + ScanningOptimizations getOptimizeArgs() const { return OptimizeArgs; } bool shouldEagerLoadModules() const { return EagerLoadModules; } @@ -68,7 +84,7 @@ private: const ScanningMode Mode; const ScanningOutputFormat Format; /// Whether to optimize the modules' command-line arguments. - const bool OptimizeArgs; + const ScanningOptimizations OptimizeArgs; /// Whether to set up command-lines to load PCM files eagerly. const bool EagerLoadModules; /// The global file system cache. diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index 778d579bfb50154664121d21b9603295f6ab4295..0f607862194b316039ce09f60ff9bd2a8189441f 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -116,7 +116,7 @@ private: llvm::IntrusiveRefCntPtr DepFS; ScanningOutputFormat Format; /// Whether to optimize the modules' command-line arguments. - bool OptimizeArgs; + ScanningOptimizations OptimizeArgs; /// Whether to set up command-lines to load PCM files eagerly. bool EagerLoadModules; }; diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h index f5dbb26452da4e6459d076c60939260a757974da..051363b075de9972b2101fb42279aff39044d4d5 100644 --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -16,6 +16,7 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Serialization/ASTReader.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringSet.h" @@ -211,8 +212,9 @@ public: ModuleDepCollector(std::unique_ptr Opts, CompilerInstance &ScanInstance, DependencyConsumer &C, DependencyActionController &Controller, - CompilerInvocation OriginalCI, bool OptimizeArgs, - bool EagerLoadModules, bool IsStdModuleP1689Format); + CompilerInvocation OriginalCI, + ScanningOptimizations OptimizeArgs, bool EagerLoadModules, + bool IsStdModuleP1689Format); void attachToPreprocessor(Preprocessor &PP) override; void attachToASTReader(ASTReader &R) override; @@ -254,7 +256,7 @@ private: /// for each individual module. CowCompilerInvocation CommonInvocation; /// Whether to optimize the modules' command-line arguments. - bool OptimizeArgs; + ScanningOptimizations OptimizeArgs; /// Whether to set up command-lines to load PCM files eagerly. bool EagerLoadModules; /// If we're generating dependency output in P1689 format diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 5897b45d3796d0e723d40f23abae7e87cbfbbc60..615314c46f09cac62bffcac147c040a670c8417c 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -247,7 +247,7 @@ using EnumConstantDataLayout = /// A stored Objective-C selector. struct StoredObjCSelector { - unsigned NumPieces; + unsigned NumArgs; llvm::SmallVector Identifiers; }; @@ -302,7 +302,7 @@ template <> struct DenseMapInfo { static unsigned getHashValue(const clang::api_notes::StoredObjCSelector &Selector) { - auto hash = llvm::hash_value(Selector.NumPieces); + auto hash = llvm::hash_value(Selector.NumArgs); hash = hash_combine(hash, Selector.Identifiers.size()); for (auto piece : Selector.Identifiers) hash = hash_combine(hash, static_cast(piece)); @@ -313,7 +313,7 @@ template <> struct DenseMapInfo { static bool isEqual(const clang::api_notes::StoredObjCSelector &LHS, const clang::api_notes::StoredObjCSelector &RHS) { - return LHS.NumPieces == RHS.NumPieces && LHS.Identifiers == RHS.Identifiers; + return LHS.NumArgs == RHS.NumArgs && LHS.Identifiers == RHS.Identifiers; } }; diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec1fb3ffa961c95cbfba635388ff19f8751401dc --- /dev/null +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -0,0 +1,458 @@ +//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/APINotes/APINotesManager.h" +#include "clang/APINotes/APINotesReader.h" +#include "clang/APINotes/APINotesYAMLCompiler.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SourceMgrAdapter.h" +#include "clang/Basic/Version.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" + +using namespace clang; +using namespace api_notes; + +#define DEBUG_TYPE "API Notes" +STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded"); +STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded"); +STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded"); +STATISTIC(NumFrameworksSearched, "frameworks searched"); +STATISTIC(NumDirectoriesSearched, "header directories searched"); +STATISTIC(NumDirectoryCacheHits, "directory cache hits"); + +namespace { +/// Prints two successive strings, which much be kept alive as long as the +/// PrettyStackTrace entry. +class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry { + StringRef First, Second; + +public: + PrettyStackTraceDoubleString(StringRef First, StringRef Second) + : First(First), Second(Second) {} + void print(raw_ostream &OS) const override { OS << First << Second; } +}; +} // namespace + +APINotesManager::APINotesManager(SourceManager &SM, const LangOptions &LangOpts) + : SM(SM), ImplicitAPINotes(LangOpts.APINotes) {} + +APINotesManager::~APINotesManager() { + // Free the API notes readers. + for (const auto &Entry : Readers) { + if (auto Reader = Entry.second.dyn_cast()) + delete Reader; + } + + delete CurrentModuleReaders[ReaderKind::Public]; + delete CurrentModuleReaders[ReaderKind::Private]; +} + +std::unique_ptr +APINotesManager::loadAPINotes(FileEntryRef APINotesFile) { + PrettyStackTraceDoubleString Trace("Loading API notes from ", + APINotesFile.getName()); + + // Open the source file. + auto SourceFileID = SM.getOrCreateFileID(APINotesFile, SrcMgr::C_User); + auto SourceBuffer = SM.getBufferOrNone(SourceFileID, SourceLocation()); + if (!SourceBuffer) + return nullptr; + + // Compile the API notes source into a buffer. + // FIXME: Either propagate OSType through or, better yet, improve the binary + // APINotes format to maintain complete availability information. + // FIXME: We don't even really need to go through the binary format at all; + // we're just going to immediately deserialize it again. + llvm::SmallVector APINotesBuffer; + std::unique_ptr CompiledBuffer; + { + SourceMgrAdapter SMAdapter( + SM, SM.getDiagnostics(), diag::err_apinotes_message, + diag::warn_apinotes_message, diag::note_apinotes_message, APINotesFile); + llvm::raw_svector_ostream OS(APINotesBuffer); + if (api_notes::compileAPINotes( + SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS, + SMAdapter.getDiagHandler(), SMAdapter.getDiagContext())) + return nullptr; + + // Make a copy of the compiled form into the buffer. + CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + } + + // Load the binary form we just compiled. + auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); + assert(Reader && "Could not load the API notes we just generated?"); + return Reader; +} + +std::unique_ptr +APINotesManager::loadAPINotes(StringRef Buffer) { + llvm::SmallVector APINotesBuffer; + std::unique_ptr CompiledBuffer; + SourceMgrAdapter SMAdapter( + SM, SM.getDiagnostics(), diag::err_apinotes_message, + diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt); + llvm::raw_svector_ostream OS(APINotesBuffer); + + if (api_notes::compileAPINotes(Buffer, nullptr, OS, + SMAdapter.getDiagHandler(), + SMAdapter.getDiagContext())) + return nullptr; + + CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); + assert(Reader && "Could not load the API notes we just generated?"); + return Reader; +} + +bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, + FileEntryRef APINotesFile) { + assert(Readers.find(HeaderDir) == Readers.end()); + if (auto Reader = loadAPINotes(APINotesFile)) { + Readers[HeaderDir] = Reader.release(); + return false; + } + + Readers[HeaderDir] = nullptr; + return true; +} + +OptionalFileEntryRef +APINotesManager::findAPINotesFile(DirectoryEntryRef Directory, + StringRef Basename, bool WantPublic) { + FileManager &FM = SM.getFileManager(); + + llvm::SmallString<128> Path(Directory.getName()); + + StringRef Suffix = WantPublic ? "" : "_private"; + + // Look for the source API notes file. + llvm::sys::path::append(Path, llvm::Twine(Basename) + Suffix + "." + + SOURCE_APINOTES_EXTENSION); + return FM.getOptionalFileRef(Path, /*Open*/ true); +} + +OptionalDirectoryEntryRef APINotesManager::loadFrameworkAPINotes( + llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) { + FileManager &FM = SM.getFileManager(); + + llvm::SmallString<128> Path(FrameworkPath); + unsigned FrameworkNameLength = Path.size(); + + StringRef Suffix = Public ? "" : "_private"; + + // Form the path to the APINotes file. + llvm::sys::path::append(Path, "APINotes"); + llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + Suffix + "." + + SOURCE_APINOTES_EXTENSION)); + + // Try to open the APINotes file. + auto APINotesFile = FM.getOptionalFileRef(Path); + if (!APINotesFile) + return std::nullopt; + + // Form the path to the corresponding header directory. + Path.resize(FrameworkNameLength); + llvm::sys::path::append(Path, Public ? "Headers" : "PrivateHeaders"); + + // Try to access the header directory. + auto HeaderDir = FM.getOptionalDirectoryRef(Path); + if (!HeaderDir) + return std::nullopt; + + // Try to load the API notes. + if (loadAPINotes(*HeaderDir, *APINotesFile)) + return std::nullopt; + + // Success: return the header directory. + if (Public) + ++NumPublicFrameworkAPINotes; + else + ++NumPrivateFrameworkAPINotes; + return *HeaderDir; +} + +static void checkPrivateAPINotesName(DiagnosticsEngine &Diags, + const FileEntry *File, const Module *M) { + if (File->tryGetRealPathName().empty()) + return; + + StringRef RealFileName = + llvm::sys::path::filename(File->tryGetRealPathName()); + StringRef RealStem = llvm::sys::path::stem(RealFileName); + if (RealStem.endswith("_private")) + return; + + unsigned DiagID = diag::warn_apinotes_private_case; + if (M->IsSystem) + DiagID = diag::warn_apinotes_private_case_system; + + Diags.Report(SourceLocation(), DiagID) << M->Name << RealFileName; +} + +/// \returns true if any of \p module's immediate submodules are defined in a +/// private module map +static bool hasPrivateSubmodules(const Module *M) { + return llvm::any_of(M->submodules(), [](const Module *Submodule) { + return Submodule->ModuleMapIsPrivate; + }); +} + +llvm::SmallVector +APINotesManager::getCurrentModuleAPINotes(Module *M, bool LookInModule, + ArrayRef SearchPaths) { + FileManager &FM = SM.getFileManager(); + auto ModuleName = M->getTopLevelModuleName(); + llvm::SmallVector APINotes; + + // First, look relative to the module itself. + if (LookInModule) { + // Local function to try loading an API notes file in the given directory. + auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) { + if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) { + if (!WantPublic) + checkPrivateAPINotesName(SM.getDiagnostics(), *File, M); + + APINotes.push_back(*File); + } + }; + + if (M->IsFramework) { + // For frameworks, we search in the "Headers" or "PrivateHeaders" + // subdirectory. + // + // Public modules: + // - Headers/Foo.apinotes + // - PrivateHeaders/Foo_private.apinotes (if there are private submodules) + // Private modules: + // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has + // the word "Private" in it in practice) + llvm::SmallString<128> Path(M->Directory->getName()); + + if (!M->ModuleMapIsPrivate) { + unsigned PathLen = Path.size(); + + llvm::sys::path::append(Path, "Headers"); + if (auto APINotesDir = FM.getOptionalDirectoryRef(Path)) + tryAPINotes(*APINotesDir, /*wantPublic=*/true); + + Path.resize(PathLen); + } + + if (M->ModuleMapIsPrivate || hasPrivateSubmodules(M)) { + llvm::sys::path::append(Path, "PrivateHeaders"); + if (auto PrivateAPINotesDir = FM.getOptionalDirectoryRef(Path)) + tryAPINotes(*PrivateAPINotesDir, + /*wantPublic=*/M->ModuleMapIsPrivate); + } + } else { + // Public modules: + // - Foo.apinotes + // - Foo_private.apinotes (if there are private submodules) + // Private modules: + // - Bar.apinotes (except that 'Bar' probably already has the word + // "Private" in it in practice) + tryAPINotes(*M->Directory, /*wantPublic=*/true); + if (!M->ModuleMapIsPrivate && hasPrivateSubmodules(M)) + tryAPINotes(*M->Directory, /*wantPublic=*/false); + } + + if (!APINotes.empty()) + return APINotes; + } + + // Second, look for API notes for this module in the module API + // notes search paths. + for (const auto &SearchPath : SearchPaths) { + if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath)) { + if (auto File = findAPINotesFile(*SearchDir, ModuleName)) { + APINotes.push_back(*File); + return APINotes; + } + } + } + + // Didn't find any API notes. + return APINotes; +} + +bool APINotesManager::loadCurrentModuleAPINotes( + Module *M, bool LookInModule, ArrayRef SearchPaths) { + assert(!CurrentModuleReaders[ReaderKind::Public] && + "Already loaded API notes for the current module?"); + + auto APINotes = getCurrentModuleAPINotes(M, LookInModule, SearchPaths); + unsigned NumReaders = 0; + for (auto File : APINotes) { + CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release(); + if (!getCurrentModuleReaders().empty()) + M->APINotesFile = File.getName().str(); + } + + return NumReaders > 0; +} + +bool APINotesManager::loadCurrentModuleAPINotesFromBuffer( + ArrayRef Buffers) { + unsigned NumReader = 0; + for (auto Buf : Buffers) { + auto Reader = loadAPINotes(Buf); + assert(Reader && "Could not load the API notes we just generated?"); + + CurrentModuleReaders[NumReader++] = Reader.release(); + } + return NumReader; +} + +llvm::SmallVector +APINotesManager::findAPINotes(SourceLocation Loc) { + llvm::SmallVector Results; + + // If there are readers for the current module, return them. + if (!getCurrentModuleReaders().empty()) { + Results.append(getCurrentModuleReaders().begin(), + getCurrentModuleReaders().end()); + return Results; + } + + // If we're not allowed to implicitly load API notes files, we're done. + if (!ImplicitAPINotes) + return Results; + + // If we don't have source location information, we're done. + if (Loc.isInvalid()) + return Results; + + // API notes are associated with the expansion location. Retrieve the + // file for this location. + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); + FileID ID = SM.getFileID(ExpansionLoc); + if (ID.isInvalid()) + return Results; + OptionalFileEntryRef File = SM.getFileEntryRefForID(ID); + if (!File) + return Results; + + // Look for API notes in the directory corresponding to this file, or one of + // its its parent directories. + OptionalDirectoryEntryRef Dir = File->getDir(); + FileManager &FileMgr = SM.getFileManager(); + llvm::SetVector, + llvm::SmallPtrSet> + DirsVisited; + do { + // Look for an API notes reader for this header search directory. + auto Known = Readers.find(*Dir); + + // If we already know the answer, chase it. + if (Known != Readers.end()) { + ++NumDirectoryCacheHits; + + // We've been redirected to another directory for answers. Follow it. + if (Known->second && Known->second.is()) { + DirsVisited.insert(*Dir); + Dir = Known->second.get(); + continue; + } + + // We have the answer. + if (auto Reader = Known->second.dyn_cast()) + Results.push_back(Reader); + break; + } + + // Look for API notes corresponding to this directory. + StringRef Path = Dir->getName(); + if (llvm::sys::path::extension(Path) == ".framework") { + // If this is a framework directory, check whether there are API notes + // in the APINotes subdirectory. + auto FrameworkName = llvm::sys::path::stem(Path); + ++NumFrameworksSearched; + + // Look for API notes for both the public and private headers. + OptionalDirectoryEntryRef PublicDir = + loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true); + OptionalDirectoryEntryRef PrivateDir = + loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false); + + if (PublicDir || PrivateDir) { + // We found API notes: don't ever look past the framework directory. + Readers[*Dir] = nullptr; + + // Pretend we found the result in the public or private directory, + // as appropriate. All headers should be in one of those two places, + // but be defensive here. + if (!DirsVisited.empty()) { + if (PublicDir && DirsVisited.back() == *PublicDir) { + DirsVisited.pop_back(); + Dir = *PublicDir; + } else if (PrivateDir && DirsVisited.back() == *PrivateDir) { + DirsVisited.pop_back(); + Dir = *PrivateDir; + } + } + + // Grab the result. + if (auto Reader = Readers[*Dir].dyn_cast()) + Results.push_back(Reader); + break; + } + } else { + // Look for an APINotes file in this directory. + llvm::SmallString<128> APINotesPath(Dir->getName()); + llvm::sys::path::append( + APINotesPath, (llvm::Twine("APINotes.") + SOURCE_APINOTES_EXTENSION)); + + // If there is an API notes file here, try to load it. + ++NumDirectoriesSearched; + if (auto APINotesFile = FileMgr.getOptionalFileRef(APINotesPath)) { + if (!loadAPINotes(*Dir, *APINotesFile)) { + ++NumHeaderAPINotes; + if (auto Reader = Readers[*Dir].dyn_cast()) + Results.push_back(Reader); + break; + } + } + } + + // We didn't find anything. Look at the parent directory. + if (!DirsVisited.insert(*Dir)) { + Dir = std::nullopt; + break; + } + + StringRef ParentPath = llvm::sys::path::parent_path(Path); + while (llvm::sys::path::stem(ParentPath) == "..") + ParentPath = llvm::sys::path::parent_path(ParentPath); + + Dir = ParentPath.empty() ? std::nullopt + : FileMgr.getOptionalDirectoryRef(ParentPath); + } while (Dir); + + // Path compression for all of the directories we visited, redirecting + // them to the directory we ended on. If no API notes were found, the + // resulting directory will be NULL, indicating no API notes. + for (const auto Visited : DirsVisited) + Readers[Visited] = Dir ? ReaderEntry(*Dir) : ReaderEntry(); + + return Results; +} diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 2cbf5fd3bf50301fd28b21c0fecca875230ba53b..ff9b95d9bf75e3d392a12973b7ab51773659dcff 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -421,7 +421,7 @@ public: static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { internal_key_type Key; - Key.NumPieces = + Key.NumArgs = endian::readNext(Data); unsigned NumIdents = (Length - sizeof(uint16_t)) / sizeof(uint32_t); for (unsigned i = 0; i != NumIdents; ++i) { @@ -741,6 +741,7 @@ APINotesReader::Implementation::getSelector(ObjCSelectorRef Selector) { // Translate the identifiers. StoredObjCSelector Key; + Key.NumArgs = Selector.NumArgs; for (auto Ident : Selector.Identifiers) { if (auto IdentID = getIdentifier(Ident)) { Key.Identifiers.push_back(*IdentID); diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 770d78e22050c01d3d09c27fa86fdb085a293227..62a2ab1799913a10e624216c65229cdfaadb0d5b 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -823,7 +823,7 @@ public: void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { llvm::support::endian::Writer writer(OS, llvm::endianness::little); - writer.write(Key.NumPieces); + writer.write(Key.NumArgs); for (auto Identifier : Key.Identifiers) writer.write(Identifier); } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 647455111214c59b8fd8c9a2cbcf28999601070b..4dfd01dae05f2ca8944b57d778de967e4f3b6533 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -14,14 +14,17 @@ // #include "clang/APINotes/APINotesYAMLCompiler.h" +#include "clang/APINotes/APINotesWriter.h" #include "clang/APINotes/Types.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/Specifiers.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/SourceMgr.h" #include "llvm/Support/VersionTuple.h" -#include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" #include #include + using namespace clang; using namespace api_notes; @@ -635,3 +638,476 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI, return false; } + +namespace { +using namespace api_notes; + +class YAMLConverter { + const Module &M; + APINotesWriter Writer; + llvm::raw_ostream &OS; + llvm::SourceMgr::DiagHandlerTy DiagHandler; + void *DiagHandlerCtxt; + bool ErrorOccured; + + /// Emit a diagnostic + bool emitError(llvm::Twine Message) { + DiagHandler( + llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Message.str()), + DiagHandlerCtxt); + ErrorOccured = true; + return true; + } + +public: + YAMLConverter(const Module &TheModule, const FileEntry *SourceFile, + llvm::raw_ostream &OS, + llvm::SourceMgr::DiagHandlerTy DiagHandler, + void *DiagHandlerCtxt) + : M(TheModule), Writer(TheModule.Name, SourceFile), OS(OS), + DiagHandler(DiagHandler), DiagHandlerCtxt(DiagHandlerCtxt), + ErrorOccured(false) {} + + void convertAvailability(const AvailabilityItem &Availability, + CommonEntityInfo &CEI, llvm::StringRef APIName) { + // Populate the unavailability information. + CEI.Unavailable = (Availability.Mode == APIAvailability::None); + CEI.UnavailableInSwift = (Availability.Mode == APIAvailability::NonSwift); + if (CEI.Unavailable || CEI.UnavailableInSwift) { + CEI.UnavailableMsg = std::string(Availability.Msg); + } else { + if (!Availability.Msg.empty()) + emitError(llvm::Twine("availability message for available API '") + + APIName + "' will not be used"); + } + } + + void convertParams(const ParamsSeq &Params, FunctionInfo &OutInfo) { + for (const auto &P : Params) { + ParamInfo PI; + if (P.Nullability) + PI.setNullabilityAudited(*P.Nullability); + PI.setNoEscape(P.NoEscape); + PI.setType(std::string(P.Type)); + PI.setRetainCountConvention(P.RetainCountConvention); + if (OutInfo.Params.size() <= P.Position) + OutInfo.Params.resize(P.Position + 1); + OutInfo.Params[P.Position] |= PI; + } + } + + void convertNullability(const NullabilitySeq &Nullability, + std::optional ReturnNullability, + FunctionInfo &OutInfo, llvm::StringRef APIName) { + if (Nullability.size() > FunctionInfo::getMaxNullabilityIndex()) { + emitError(llvm::Twine("nullability info for '") + APIName + + "' does not fit"); + return; + } + + bool audited = false; + unsigned int idx = 1; + for (const auto &N : Nullability) + OutInfo.addTypeInfo(idx++, N); + audited = Nullability.size() > 0 || ReturnNullability; + if (audited) + OutInfo.addTypeInfo(0, ReturnNullability ? *ReturnNullability + : NullabilityKind::NonNull); + if (!audited) + return; + OutInfo.NullabilityAudited = audited; + OutInfo.NumAdjustedNullable = idx; + } + + /// Convert the common parts of an entity from YAML. + template + void convertCommonEntity(const T &Common, CommonEntityInfo &Info, + StringRef APIName) { + convertAvailability(Common.Availability, Info, APIName); + Info.setSwiftPrivate(Common.SwiftPrivate); + Info.SwiftName = std::string(Common.SwiftName); + } + + /// Convert the common parts of a type entity from YAML. + template + void convertCommonType(const T &Common, CommonTypeInfo &Info, + StringRef APIName) { + convertCommonEntity(Common, Info, APIName); + if (Common.SwiftBridge) + Info.setSwiftBridge(std::string(*Common.SwiftBridge)); + Info.setNSErrorDomain(Common.NSErrorDomain); + } + + // Translate from Method into ObjCMethodInfo and write it out. + void convertMethod(const Method &M, ContextID ClassID, StringRef ClassName, + VersionTuple SwiftVersion) { + ObjCMethodInfo MI; + convertCommonEntity(M, MI, M.Selector); + + // Check if the selector ends with ':' to determine if it takes arguments. + bool takesArguments = M.Selector.endswith(":"); + + // Split the selector into pieces. + llvm::SmallVector Args; + M.Selector.split(Args, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false); + if (!takesArguments && Args.size() > 1) { + emitError("selector '" + M.Selector + "' is missing a ':' at the end"); + return; + } + + // Construct ObjCSelectorRef. + api_notes::ObjCSelectorRef Selector; + Selector.NumArgs = !takesArguments ? 0 : Args.size(); + Selector.Identifiers = Args; + + // Translate the initializer info. + MI.DesignatedInit = M.DesignatedInit; + MI.RequiredInit = M.Required; + if (M.FactoryAsInit != FactoryAsInitKind::Infer) + emitError("'FactoryAsInit' is no longer valid; use 'SwiftName' instead"); + + MI.ResultType = std::string(M.ResultType); + + // Translate parameter information. + convertParams(M.Params, MI); + + // Translate nullability info. + convertNullability(M.Nullability, M.NullabilityOfRet, MI, M.Selector); + + MI.setRetainCountConvention(M.RetainCountConvention); + + // Write it. + Writer.addObjCMethod(ClassID, Selector, M.Kind == MethodKind::Instance, MI, + SwiftVersion); + } + + void convertContext(std::optional ParentContextID, const Class &C, + ContextKind Kind, VersionTuple SwiftVersion) { + // Write the class. + ObjCContextInfo CI; + convertCommonType(C, CI, C.Name); + + if (C.AuditedForNullability) + CI.setDefaultNullability(NullabilityKind::NonNull); + if (C.SwiftImportAsNonGeneric) + CI.setSwiftImportAsNonGeneric(*C.SwiftImportAsNonGeneric); + if (C.SwiftObjCMembers) + CI.setSwiftObjCMembers(*C.SwiftObjCMembers); + + ContextID CtxID = + Writer.addObjCContext(ParentContextID, C.Name, Kind, CI, SwiftVersion); + + // Write all methods. + llvm::StringMap> KnownMethods; + for (const auto &method : C.Methods) { + // Check for duplicate method definitions. + bool IsInstanceMethod = method.Kind == MethodKind::Instance; + bool &Known = IsInstanceMethod ? KnownMethods[method.Selector].first + : KnownMethods[method.Selector].second; + if (Known) { + emitError(llvm::Twine("duplicate definition of method '") + + (IsInstanceMethod ? "-" : "+") + "[" + C.Name + " " + + method.Selector + "]'"); + continue; + } + Known = true; + + convertMethod(method, CtxID, C.Name, SwiftVersion); + } + + // Write all properties. + llvm::StringSet<> KnownInstanceProperties; + llvm::StringSet<> KnownClassProperties; + for (const auto &Property : C.Properties) { + // Check for duplicate property definitions. + if ((!Property.Kind || *Property.Kind == MethodKind::Instance) && + !KnownInstanceProperties.insert(Property.Name).second) { + emitError(llvm::Twine("duplicate definition of instance property '") + + C.Name + "." + Property.Name + "'"); + continue; + } + + if ((!Property.Kind || *Property.Kind == MethodKind::Class) && + !KnownClassProperties.insert(Property.Name).second) { + emitError(llvm::Twine("duplicate definition of class property '") + + C.Name + "." + Property.Name + "'"); + continue; + } + + // Translate from Property into ObjCPropertyInfo. + ObjCPropertyInfo PI; + convertAvailability(Property.Availability, PI, Property.Name); + PI.setSwiftPrivate(Property.SwiftPrivate); + PI.SwiftName = std::string(Property.SwiftName); + if (Property.Nullability) + PI.setNullabilityAudited(*Property.Nullability); + if (Property.SwiftImportAsAccessors) + PI.setSwiftImportAsAccessors(*Property.SwiftImportAsAccessors); + PI.setType(std::string(Property.Type)); + + // Add both instance and class properties with this name. + if (Property.Kind) { + Writer.addObjCProperty(CtxID, Property.Name, + *Property.Kind == MethodKind::Instance, PI, + SwiftVersion); + } else { + Writer.addObjCProperty(CtxID, Property.Name, true, PI, SwiftVersion); + Writer.addObjCProperty(CtxID, Property.Name, false, PI, SwiftVersion); + } + } + } + + void convertNamespaceContext(std::optional ParentContextID, + const Namespace &TheNamespace, + VersionTuple SwiftVersion) { + // Write the namespace. + ObjCContextInfo CI; + convertCommonEntity(TheNamespace, CI, TheNamespace.Name); + + ContextID CtxID = + Writer.addObjCContext(ParentContextID, TheNamespace.Name, + ContextKind::Namespace, CI, SwiftVersion); + + convertTopLevelItems(Context(CtxID, ContextKind::Namespace), + TheNamespace.Items, SwiftVersion); + } + + void convertTopLevelItems(std::optional Ctx, + const TopLevelItems &TLItems, + VersionTuple SwiftVersion) { + std::optional CtxID = + Ctx ? std::optional(Ctx->id) : std::nullopt; + + // Write all classes. + llvm::StringSet<> KnownClasses; + for (const auto &Class : TLItems.Classes) { + // Check for duplicate class definitions. + if (!KnownClasses.insert(Class.Name).second) { + emitError(llvm::Twine("multiple definitions of class '") + Class.Name + + "'"); + continue; + } + + convertContext(CtxID, Class, ContextKind::ObjCClass, SwiftVersion); + } + + // Write all protocols. + llvm::StringSet<> KnownProtocols; + for (const auto &Protocol : TLItems.Protocols) { + // Check for duplicate protocol definitions. + if (!KnownProtocols.insert(Protocol.Name).second) { + emitError(llvm::Twine("multiple definitions of protocol '") + + Protocol.Name + "'"); + continue; + } + + convertContext(CtxID, Protocol, ContextKind::ObjCProtocol, SwiftVersion); + } + + // Write all namespaces. + llvm::StringSet<> KnownNamespaces; + for (const auto &Namespace : TLItems.Namespaces) { + // Check for duplicate namespace definitions. + if (!KnownNamespaces.insert(Namespace.Name).second) { + emitError(llvm::Twine("multiple definitions of namespace '") + + Namespace.Name + "'"); + continue; + } + + convertNamespaceContext(CtxID, Namespace, SwiftVersion); + } + + // Write all global variables. + llvm::StringSet<> KnownGlobals; + for (const auto &Global : TLItems.Globals) { + // Check for duplicate global variables. + if (!KnownGlobals.insert(Global.Name).second) { + emitError(llvm::Twine("multiple definitions of global variable '") + + Global.Name + "'"); + continue; + } + + GlobalVariableInfo GVI; + convertAvailability(Global.Availability, GVI, Global.Name); + GVI.setSwiftPrivate(Global.SwiftPrivate); + GVI.SwiftName = std::string(Global.SwiftName); + if (Global.Nullability) + GVI.setNullabilityAudited(*Global.Nullability); + GVI.setType(std::string(Global.Type)); + Writer.addGlobalVariable(Ctx, Global.Name, GVI, SwiftVersion); + } + + // Write all global functions. + llvm::StringSet<> KnownFunctions; + for (const auto &Function : TLItems.Functions) { + // Check for duplicate global functions. + if (!KnownFunctions.insert(Function.Name).second) { + emitError(llvm::Twine("multiple definitions of global function '") + + Function.Name + "'"); + continue; + } + + GlobalFunctionInfo GFI; + convertAvailability(Function.Availability, GFI, Function.Name); + GFI.setSwiftPrivate(Function.SwiftPrivate); + GFI.SwiftName = std::string(Function.SwiftName); + convertParams(Function.Params, GFI); + convertNullability(Function.Nullability, Function.NullabilityOfRet, GFI, + Function.Name); + GFI.ResultType = std::string(Function.ResultType); + GFI.setRetainCountConvention(Function.RetainCountConvention); + Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion); + } + + // Write all enumerators. + llvm::StringSet<> KnownEnumConstants; + for (const auto &EnumConstant : TLItems.EnumConstants) { + // Check for duplicate enumerators + if (!KnownEnumConstants.insert(EnumConstant.Name).second) { + emitError(llvm::Twine("multiple definitions of enumerator '") + + EnumConstant.Name + "'"); + continue; + } + + EnumConstantInfo ECI; + convertAvailability(EnumConstant.Availability, ECI, EnumConstant.Name); + ECI.setSwiftPrivate(EnumConstant.SwiftPrivate); + ECI.SwiftName = std::string(EnumConstant.SwiftName); + Writer.addEnumConstant(EnumConstant.Name, ECI, SwiftVersion); + } + + // Write all tags. + llvm::StringSet<> KnownTags; + for (const auto &Tag : TLItems.Tags) { + // Check for duplicate tag definitions. + if (!KnownTags.insert(Tag.Name).second) { + emitError(llvm::Twine("multiple definitions of tag '") + Tag.Name + + "'"); + continue; + } + + TagInfo TI; + convertCommonType(Tag, TI, Tag.Name); + + if ((Tag.SwiftRetainOp || Tag.SwiftReleaseOp) && !Tag.SwiftImportAs) { + emitError(llvm::Twine("should declare SwiftImportAs to use " + "SwiftRetainOp and SwiftReleaseOp (for ") + + Tag.Name + ")"); + continue; + } + if (Tag.SwiftReleaseOp.has_value() != Tag.SwiftRetainOp.has_value()) { + emitError(llvm::Twine("should declare both SwiftReleaseOp and " + "SwiftRetainOp (for ") + + Tag.Name + ")"); + continue; + } + + if (Tag.SwiftImportAs) + TI.SwiftImportAs = Tag.SwiftImportAs; + if (Tag.SwiftRetainOp) + TI.SwiftRetainOp = Tag.SwiftRetainOp; + if (Tag.SwiftReleaseOp) + TI.SwiftReleaseOp = Tag.SwiftReleaseOp; + + if (Tag.EnumConvenienceKind) { + if (Tag.EnumExtensibility) { + emitError( + llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") + + Tag.Name + ")"); + continue; + } + if (Tag.FlagEnum) { + emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") + + Tag.Name + ")"); + continue; + } + switch (*Tag.EnumConvenienceKind) { + case EnumConvenienceAliasKind::None: + TI.EnumExtensibility = EnumExtensibilityKind::None; + TI.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFEnum: + TI.EnumExtensibility = EnumExtensibilityKind::Open; + TI.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFOptions: + TI.EnumExtensibility = EnumExtensibilityKind::Open; + TI.setFlagEnum(true); + break; + case EnumConvenienceAliasKind::CFClosedEnum: + TI.EnumExtensibility = EnumExtensibilityKind::Closed; + TI.setFlagEnum(false); + break; + } + } else { + TI.EnumExtensibility = Tag.EnumExtensibility; + TI.setFlagEnum(Tag.FlagEnum); + } + + Writer.addTag(Ctx, Tag.Name, TI, SwiftVersion); + } + + // Write all typedefs. + llvm::StringSet<> KnownTypedefs; + for (const auto &Typedef : TLItems.Typedefs) { + // Check for duplicate typedef definitions. + if (!KnownTypedefs.insert(Typedef.Name).second) { + emitError(llvm::Twine("multiple definitions of typedef '") + + Typedef.Name + "'"); + continue; + } + + TypedefInfo TInfo; + convertCommonType(Typedef, TInfo, Typedef.Name); + TInfo.SwiftWrapper = Typedef.SwiftType; + + Writer.addTypedef(Ctx, Typedef.Name, TInfo, SwiftVersion); + } + } + + bool convertModule() { + // Write the top-level items. + convertTopLevelItems(/* context */ std::nullopt, M.TopLevel, + VersionTuple()); + + // Convert the versioned information. + for (const auto &Versioned : M.SwiftVersions) + convertTopLevelItems(/* context */ std::nullopt, Versioned.Items, + Versioned.Version); + + if (!ErrorOccured) + Writer.writeToStream(OS); + + return ErrorOccured; + } +}; +} // namespace + +static bool compile(const Module &M, const FileEntry *SourceFile, + llvm::raw_ostream &OS, + llvm::SourceMgr::DiagHandlerTy DiagHandler, + void *DiagHandlerCtxt) { + YAMLConverter C(M, SourceFile, OS, DiagHandler, DiagHandlerCtxt); + return C.convertModule(); +} + +/// Simple diagnostic handler that prints diagnostics to standard error. +static void printDiagnostic(const llvm::SMDiagnostic &Diag, void *Context) { + Diag.print(nullptr, llvm::errs()); +} + +bool api_notes::compileAPINotes(StringRef YAMLInput, + const FileEntry *SourceFile, + llvm::raw_ostream &OS, + llvm::SourceMgr::DiagHandlerTy DiagHandler, + void *DiagHandlerCtxt) { + Module TheModule; + + if (!DiagHandler) + DiagHandler = &printDiagnostic; + + if (parseAPINotes(YAMLInput, TheModule, DiagHandler, DiagHandlerCtxt)) + return true; + + return compile(TheModule, SourceFile, OS, DiagHandler, DiagHandlerCtxt); +} diff --git a/clang/lib/APINotes/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt index dec596ea160c68f70c34910905be3119c5b4470f..dc83edd911ce2022b3abd0a4290397af5081e557 100644 --- a/clang/lib/APINotes/CMakeLists.txt +++ b/clang/lib/APINotes/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS BitstreamReader Support) add_clang_library(clangAPINotes + APINotesManager.cpp APINotesReader.cpp APINotesTypes.cpp APINotesWriter.cpp diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index da90136752210b62dbfaaff3c039bc8dbb4be24f..a7cee3b7ba2b0db398bb4425d2fc4f4cee5979ac 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1096,7 +1096,7 @@ ArrayRef ASTContext::getModuleInitializers(Module *M) { } void ASTContext::setCurrentNamedModule(Module *M) { - assert(M->isModulePurview()); + assert(M->isNamedModule()); assert(!CurrentCXXNamedModule && "We should set named module for ASTContext for only once"); CurrentCXXNamedModule = M; @@ -1680,14 +1680,16 @@ CharUnits ASTContext::getDeclAlign(const Decl *D, bool ForAlignof) const { Align = std::max(Align, getPreferredTypeAlign(T.getTypePtr())); if (BaseT.getQualifiers().hasUnaligned()) Align = Target->getCharWidth(); - if (const auto *VD = dyn_cast(D)) { - if (VD->hasGlobalStorage() && !ForAlignof) { - uint64_t TypeSize = getTypeSize(T.getTypePtr()); - Align = std::max(Align, getTargetInfo().getMinGlobalAlign(TypeSize)); - } - } } + // Ensure miminum alignment for global variables. + if (const auto *VD = dyn_cast(D)) + if (VD->hasGlobalStorage() && !ForAlignof) { + uint64_t TypeSize = + !BaseT->isIncompleteType() ? getTypeSize(T.getTypePtr()) : 0; + Align = std::max(Align, getTargetInfo().getMinGlobalAlign(TypeSize)); + } + // Fields can be subject to extra alignment constraints, like if // the field is packed, the struct is packed, or the struct has a // a max-field-alignment constraint (#pragma pack). So calculate @@ -2177,7 +2179,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { break; #include "clang/Basic/PPCTypes.def" #define RVV_VECTOR_TYPE(Name, Id, SingletonId, ElKind, ElBits, NF, IsSigned, \ - IsFP) \ + IsFP, IsBF) \ case BuiltinType::Id: \ Width = 0; \ Align = ElBits; \ @@ -3939,6 +3941,9 @@ ASTContext::getBuiltinVectorTypeInfo(const BuiltinType *Ty) const { case BuiltinType::Id: \ return {ElBits == 16 ? Float16Ty : (ElBits == 32 ? FloatTy : DoubleTy), \ llvm::ElementCount::getScalable(NumEls), NF}; +#define RVV_VECTOR_TYPE_BFLOAT(Name, Id, SingletonId, NumEls, ElBits, NF) \ + case BuiltinType::Id: \ + return {BFloat16Ty, llvm::ElementCount::getScalable(NumEls), NF}; #define RVV_PREDICATE_TYPE(Name, Id, SingletonId, NumEls) \ case BuiltinType::Id: \ return {BoolTy, llvm::ElementCount::getScalable(NumEls), 1}; @@ -3986,11 +3991,14 @@ QualType ASTContext::getScalableVectorType(QualType EltTy, unsigned NumElts, } else if (Target->hasRISCVVTypes()) { uint64_t EltTySize = getTypeSize(EltTy); #define RVV_VECTOR_TYPE(Name, Id, SingletonId, NumEls, ElBits, NF, IsSigned, \ - IsFP) \ + IsFP, IsBF) \ if (!EltTy->isBooleanType() && \ ((EltTy->hasIntegerRepresentation() && \ EltTy->hasSignedIntegerRepresentation() == IsSigned) || \ - (EltTy->hasFloatingRepresentation() && IsFP)) && \ + (EltTy->hasFloatingRepresentation() && !EltTy->isBFloat16Type() && \ + IsFP && !IsBF) || \ + (EltTy->hasFloatingRepresentation() && EltTy->isBFloat16Type() && \ + IsBF && !IsFP)) && \ EltTySize == ElBits && NumElts == NumEls && NumFields == NF) \ return SingletonId; #define RVV_PREDICATE_TYPE(Name, Id, SingletonId, NumEls) \ @@ -4008,8 +4016,8 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts, assert(vecType->isBuiltinType() || (vecType->isBitIntType() && // Only support _BitInt elements with byte-sized power of 2 NumBits. - llvm::isPowerOf2_32(vecType->getAs()->getNumBits()) && - vecType->getAs()->getNumBits() >= 8)); + llvm::isPowerOf2_32(vecType->castAs()->getNumBits()) && + vecType->castAs()->getNumBits() >= 8)); // Check if we've already instantiated a vector of this type. llvm::FoldingSetNodeID ID; @@ -8535,14 +8543,12 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, } } - unsigned i = 0; for (FieldDecl *Field : RDecl->fields()) { if (!Field->isZeroLengthBitField(*this) && Field->isZeroSize(*this)) continue; - uint64_t offs = layout.getFieldOffset(i); + uint64_t offs = layout.getFieldOffset(Field->getFieldIndex()); FieldOrBaseOffsets.insert(FieldOrBaseOffsets.upper_bound(offs), std::make_pair(offs, Field)); - ++i; } if (CXXRec && includeVBases) { @@ -12221,7 +12227,7 @@ ASTContext::getPredefinedStringLiteralFromCache(StringRef Key) const { StringLiteral *&Result = StringLiteralCache[Key]; if (!Result) Result = StringLiteral::Create( - *this, Key, StringLiteral::Ordinary, + *this, Key, StringLiteralKind::Ordinary, /*Pascal*/ false, getStringLiteralArrayType(CharTy, Key.size()), SourceLocation()); return Result; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index c4e931e220f69b5d2402e33a3108ac8c315e701a..f1f335118f37a4d1837f9b96fe148e6924bee292 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3513,6 +3513,14 @@ public: return {}; } + std::optional + VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) { + // The "associated declaration" can be the same as ParentDC. + if (isAncestorDeclContextOf(ParentDC, T->getAssociatedDecl())) + return true; + return {}; + } + std::optional VisitConstantArrayType(const ConstantArrayType *T) { if (T->getSizeExpr() && isAncestorDeclContextOf(ParentDC, T->getSizeExpr())) return true; @@ -3573,6 +3581,8 @@ private: }; } // namespace +/// This function checks if the function has 'auto' return type that contains +/// a reference (in any way) to a declaration inside the same function. bool ASTNodeImporter::hasAutoReturnTypeDeclaredInside(FunctionDecl *D) { QualType FromTy = D->getType(); const auto *FromFPT = FromTy->getAs(); @@ -9063,6 +9073,7 @@ public: ToAttr = FromAttr->clone(Importer.getToContext()); ToAttr->setRange(ToRange); + ToAttr->setAttrName(Importer.Import(FromAttr->getAttrName())); } // Get the result of the previous import attempt (can be used only once). diff --git a/clang/lib/AST/Comment.cpp b/clang/lib/AST/Comment.cpp index 2bb6bb5cbcb67d4f6b5aed7f7c3f34114b727bf6..cce8b12170f21fdd31fc0b4ba7aed828ebb0cb57 100644 --- a/clang/lib/AST/Comment.cpp +++ b/clang/lib/AST/Comment.cpp @@ -34,10 +34,11 @@ static_assert(std::is_trivially_destructible_v, const char *Comment::getCommentKindName() const { switch (getCommentKind()) { - case NoCommentKind: return "NoCommentKind"; + case CommentKind::None: + return "None"; #define ABSTRACT_COMMENT(COMMENT) -#define COMMENT(CLASS, PARENT) \ - case CLASS##Kind: \ +#define COMMENT(CLASS, PARENT) \ + case CommentKind::CLASS: \ return #CLASS; #include "clang/AST/CommentNodes.inc" #undef COMMENT @@ -81,10 +82,11 @@ static inline void CheckCommentASTNodes() { Comment::child_iterator Comment::child_begin() const { switch (getCommentKind()) { - case NoCommentKind: llvm_unreachable("comment without a kind"); + case CommentKind::None: + llvm_unreachable("comment without a kind"); #define ABSTRACT_COMMENT(COMMENT) -#define COMMENT(CLASS, PARENT) \ - case CLASS##Kind: \ +#define COMMENT(CLASS, PARENT) \ + case CommentKind::CLASS: \ return static_cast(this)->child_begin(); #include "clang/AST/CommentNodes.inc" #undef COMMENT @@ -95,10 +97,11 @@ Comment::child_iterator Comment::child_begin() const { Comment::child_iterator Comment::child_end() const { switch (getCommentKind()) { - case NoCommentKind: llvm_unreachable("comment without a kind"); + case CommentKind::None: + llvm_unreachable("comment without a kind"); #define ABSTRACT_COMMENT(COMMENT) -#define COMMENT(CLASS, PARENT) \ - case CLASS##Kind: \ +#define COMMENT(CLASS, PARENT) \ + case CommentKind::CLASS: \ return static_cast(this)->child_end(); #include "clang/AST/CommentNodes.inc" #undef COMMENT @@ -184,13 +187,14 @@ static bool getFunctionTypeLoc(TypeLoc TL, FunctionTypeLoc &ResFTL) { return false; } -const char *ParamCommandComment::getDirectionAsString(PassDirection D) { +const char * +ParamCommandComment::getDirectionAsString(ParamCommandPassDirection D) { switch (D) { - case ParamCommandComment::In: + case ParamCommandPassDirection::In: return "[in]"; - case ParamCommandComment::Out: + case ParamCommandPassDirection::Out: return "[out]"; - case ParamCommandComment::InOut: + case ParamCommandPassDirection::InOut: return "[in,out]"; } llvm_unreachable("unknown PassDirection"); diff --git a/clang/lib/AST/CommentSema.cpp b/clang/lib/AST/CommentSema.cpp index 66660512d6af5ce753c750de6b6e28943fbc0408..bc01baa1d917b578bc0b14638bb803b6a8466a54 100644 --- a/clang/lib/AST/CommentSema.cpp +++ b/clang/lib/AST/CommentSema.cpp @@ -219,12 +219,12 @@ void Sema::checkContainerDecl(const BlockCommandComment *Comment) { /// Turn a string into the corresponding PassDirection or -1 if it's not /// valid. -static int getParamPassDirection(StringRef Arg) { - return llvm::StringSwitch(Arg) - .Case("[in]", ParamCommandComment::In) - .Case("[out]", ParamCommandComment::Out) - .Cases("[in,out]", "[out,in]", ParamCommandComment::InOut) - .Default(-1); +static ParamCommandPassDirection getParamPassDirection(StringRef Arg) { + return llvm::StringSwitch(Arg) + .Case("[in]", ParamCommandPassDirection::In) + .Case("[out]", ParamCommandPassDirection::Out) + .Cases("[in,out]", "[out,in]", ParamCommandPassDirection::InOut) + .Default(static_cast(-1)); } void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command, @@ -232,25 +232,25 @@ void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command, SourceLocation ArgLocEnd, StringRef Arg) { std::string ArgLower = Arg.lower(); - int Direction = getParamPassDirection(ArgLower); + ParamCommandPassDirection Direction = getParamPassDirection(ArgLower); - if (Direction == -1) { + if (Direction == static_cast(-1)) { // Try again with whitespace removed. llvm::erase_if(ArgLower, clang::isWhitespace); Direction = getParamPassDirection(ArgLower); SourceRange ArgRange(ArgLocBegin, ArgLocEnd); - if (Direction != -1) { - const char *FixedName = ParamCommandComment::getDirectionAsString( - (ParamCommandComment::PassDirection)Direction); + if (Direction != static_cast(-1)) { + const char *FixedName = + ParamCommandComment::getDirectionAsString(Direction); Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction) << ArgRange << FixItHint::CreateReplacement(ArgRange, FixedName); } else { Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) << ArgRange; - Direction = ParamCommandComment::In; // Sane fall back. + Direction = ParamCommandPassDirection::In; // Sane fall back. } } - Command->setDirection((ParamCommandComment::PassDirection)Direction, + Command->setDirection(Direction, /*Explicit=*/true); } @@ -263,7 +263,8 @@ void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command, if (!Command->isDirectionExplicit()) { // User didn't provide a direction argument. - Command->setDirection(ParamCommandComment::In, /* Explicit = */ false); + Command->setDirection(ParamCommandPassDirection::In, + /* Explicit = */ false); } auto *A = new (Allocator) Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg}; @@ -380,9 +381,7 @@ InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, unsigned CommandID) { ArrayRef Args; return new (Allocator) InlineCommandComment( - LocBegin, LocEnd, CommandID, - InlineCommandComment::RenderNormal, - Args); + LocBegin, LocEnd, CommandID, InlineCommandRenderKind::Normal, Args); } TextComment *Sema::actOnText(SourceLocation LocBegin, @@ -1108,16 +1107,15 @@ StringRef Sema::correctTypoInTParamReference( return StringRef(); } -InlineCommandComment::RenderKind -Sema::getInlineCommandRenderKind(StringRef Name) const { +InlineCommandRenderKind Sema::getInlineCommandRenderKind(StringRef Name) const { assert(Traits.getCommandInfo(Name)->IsInlineCommand); - return llvm::StringSwitch(Name) - .Case("b", InlineCommandComment::RenderBold) - .Cases("c", "p", InlineCommandComment::RenderMonospaced) - .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized) - .Case("anchor", InlineCommandComment::RenderAnchor) - .Default(InlineCommandComment::RenderNormal); + return llvm::StringSwitch(Name) + .Case("b", InlineCommandRenderKind::Bold) + .Cases("c", "p", InlineCommandRenderKind::Monospaced) + .Cases("a", "e", "em", InlineCommandRenderKind::Emphasized) + .Case("anchor", InlineCommandRenderKind::Anchor) + .Default(InlineCommandRenderKind::Normal); } } // end namespace comments diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index ed848e57423346862d72573be9b471065d336307..c5c2edf1bfe3aba2337aa4a5afa57bf54f2cb476 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -579,27 +579,6 @@ static bool isSingleLineLanguageLinkage(const Decl &D) { return false; } -/// Determine whether D is declared in the purview of a named module. -static bool isInModulePurview(const NamedDecl *D) { - if (auto *M = D->getOwningModule()) - return M->isModulePurview(); - return false; -} - -static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) { - // FIXME: Handle isModulePrivate. - switch (D->getModuleOwnershipKind()) { - case Decl::ModuleOwnershipKind::Unowned: - case Decl::ModuleOwnershipKind::ReachableWhenImported: - case Decl::ModuleOwnershipKind::ModulePrivate: - return false; - case Decl::ModuleOwnershipKind::Visible: - case Decl::ModuleOwnershipKind::VisibleWhenImported: - return isInModulePurview(D); - } - llvm_unreachable("unexpected module ownership kind"); -} - static bool isDeclaredInModuleInterfaceOrPartition(const NamedDecl *D) { if (auto *M = D->getOwningModule()) return M->isInterfaceOrPartition(); @@ -1191,6 +1170,27 @@ Linkage NamedDecl::getLinkageInternal() const { .getLinkage(); } +/// Determine whether D is attached to a named module. +static bool isInNamedModule(const NamedDecl *D) { + if (auto *M = D->getOwningModule()) + return M->isNamedModule(); + return false; +} + +static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) { + // FIXME: Handle isModulePrivate. + switch (D->getModuleOwnershipKind()) { + case Decl::ModuleOwnershipKind::Unowned: + case Decl::ModuleOwnershipKind::ReachableWhenImported: + case Decl::ModuleOwnershipKind::ModulePrivate: + return false; + case Decl::ModuleOwnershipKind::Visible: + case Decl::ModuleOwnershipKind::VisibleWhenImported: + return isInNamedModule(D); + } + llvm_unreachable("unexpected module ownership kind"); +} + /// Get the linkage from a semantic point of view. Entities in /// anonymous namespaces are external (in c++98). Linkage NamedDecl::getFormalLinkage() const { @@ -1204,7 +1204,7 @@ Linkage NamedDecl::getFormalLinkage() const { // [basic.namespace.general]/p2 // A namespace is never attached to a named module and never has a name with // module linkage. - if (isInModulePurview(this) && InternalLinkage == Linkage::External && + if (isInNamedModule(this) && InternalLinkage == Linkage::External && !isExportedFromModuleInterfaceUnit( cast(this->getCanonicalDecl())) && !isa(this)) diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 3fd4751d6d1f31dabca618a8cd268beab4c80955..e4d7169752bc857e03a0d65aece37e5ed91b17cd 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -421,9 +421,6 @@ bool Decl::isFlexibleArrayMemberLike( using FAMKind = LangOptions::StrictFlexArraysLevelKind; llvm::APInt Size = CAT->getSize(); - FAMKind StrictFlexArraysLevel = - Ctx.getLangOpts().getStrictFlexArraysLevel(); - if (StrictFlexArraysLevel == FAMKind::IncompleteOnly) return false; @@ -1113,7 +1110,7 @@ bool Decl::isInAnotherModuleUnit() const { if (M->isGlobalModule()) return false; - assert(M->isModulePurview() && "New module kind?"); + assert(M->isNamedModule() && "New module kind?"); return M != getASTContext().getCurrentNamedModule(); } diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 2c88d05dc07ea9a01b64043d0b1f5bca06f57ff2..962f503306a0f0df585e4116c240dff26a5fd822 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1194,7 +1194,7 @@ void ObjCMethodDecl::createImplicitParams(ASTContext &Context, getSelfType(Context, OID, selfIsPseudoStrong, selfIsConsumed); auto *Self = ImplicitParamDecl::Create(Context, this, SourceLocation(), &Context.Idents.get("self"), selfTy, - ImplicitParamDecl::ObjCSelf); + ImplicitParamKind::ObjCSelf); setSelfDecl(Self); if (selfIsConsumed) @@ -1205,7 +1205,7 @@ void ObjCMethodDecl::createImplicitParams(ASTContext &Context, setCmdDecl(ImplicitParamDecl::Create( Context, this, SourceLocation(), &Context.Idents.get("_cmd"), - Context.getObjCSelType(), ImplicitParamDecl::ObjCCmd)); + Context.getObjCSelType(), ImplicitParamKind::ObjCCmd)); } ObjCInterfaceDecl *ObjCMethodDecl::getClassInterface() { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 5d3b510df1ef9b3c8bb7c58163f52ca211640b38..55c6b732b7081f40c1584d14fb05949873978712 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -281,85 +281,86 @@ SourceLocation Expr::getExprLoc() const { // Primary Expressions. //===----------------------------------------------------------------------===// -static void AssertResultStorageKind(ConstantExpr::ResultStorageKind Kind) { - assert((Kind == ConstantExpr::RSK_APValue || - Kind == ConstantExpr::RSK_Int64 || Kind == ConstantExpr::RSK_None) && +static void AssertResultStorageKind(ConstantResultStorageKind Kind) { + assert((Kind == ConstantResultStorageKind::APValue || + Kind == ConstantResultStorageKind::Int64 || + Kind == ConstantResultStorageKind::None) && "Invalid StorageKind Value"); (void)Kind; } -ConstantExpr::ResultStorageKind -ConstantExpr::getStorageKind(const APValue &Value) { +ConstantResultStorageKind ConstantExpr::getStorageKind(const APValue &Value) { switch (Value.getKind()) { case APValue::None: case APValue::Indeterminate: - return ConstantExpr::RSK_None; + return ConstantResultStorageKind::None; case APValue::Int: if (!Value.getInt().needsCleanup()) - return ConstantExpr::RSK_Int64; + return ConstantResultStorageKind::Int64; [[fallthrough]]; default: - return ConstantExpr::RSK_APValue; + return ConstantResultStorageKind::APValue; } } -ConstantExpr::ResultStorageKind +ConstantResultStorageKind ConstantExpr::getStorageKind(const Type *T, const ASTContext &Context) { if (T->isIntegralOrEnumerationType() && Context.getTypeInfo(T).Width <= 64) - return ConstantExpr::RSK_Int64; - return ConstantExpr::RSK_APValue; + return ConstantResultStorageKind::Int64; + return ConstantResultStorageKind::APValue; } -ConstantExpr::ConstantExpr(Expr *SubExpr, ResultStorageKind StorageKind, +ConstantExpr::ConstantExpr(Expr *SubExpr, ConstantResultStorageKind StorageKind, bool IsImmediateInvocation) : FullExpr(ConstantExprClass, SubExpr) { - ConstantExprBits.ResultKind = StorageKind; + ConstantExprBits.ResultKind = llvm::to_underlying(StorageKind); ConstantExprBits.APValueKind = APValue::None; ConstantExprBits.IsUnsigned = false; ConstantExprBits.BitWidth = 0; ConstantExprBits.HasCleanup = false; ConstantExprBits.IsImmediateInvocation = IsImmediateInvocation; - if (StorageKind == ConstantExpr::RSK_APValue) + if (StorageKind == ConstantResultStorageKind::APValue) ::new (getTrailingObjects()) APValue(); } ConstantExpr *ConstantExpr::Create(const ASTContext &Context, Expr *E, - ResultStorageKind StorageKind, + ConstantResultStorageKind StorageKind, bool IsImmediateInvocation) { assert(!isa(E)); AssertResultStorageKind(StorageKind); unsigned Size = totalSizeToAlloc( - StorageKind == ConstantExpr::RSK_APValue, - StorageKind == ConstantExpr::RSK_Int64); + StorageKind == ConstantResultStorageKind::APValue, + StorageKind == ConstantResultStorageKind::Int64); void *Mem = Context.Allocate(Size, alignof(ConstantExpr)); return new (Mem) ConstantExpr(E, StorageKind, IsImmediateInvocation); } ConstantExpr *ConstantExpr::Create(const ASTContext &Context, Expr *E, const APValue &Result) { - ResultStorageKind StorageKind = getStorageKind(Result); + ConstantResultStorageKind StorageKind = getStorageKind(Result); ConstantExpr *Self = Create(Context, E, StorageKind); Self->SetResult(Result, Context); return Self; } -ConstantExpr::ConstantExpr(EmptyShell Empty, ResultStorageKind StorageKind) +ConstantExpr::ConstantExpr(EmptyShell Empty, + ConstantResultStorageKind StorageKind) : FullExpr(ConstantExprClass, Empty) { - ConstantExprBits.ResultKind = StorageKind; + ConstantExprBits.ResultKind = llvm::to_underlying(StorageKind); - if (StorageKind == ConstantExpr::RSK_APValue) + if (StorageKind == ConstantResultStorageKind::APValue) ::new (getTrailingObjects()) APValue(); } ConstantExpr *ConstantExpr::CreateEmpty(const ASTContext &Context, - ResultStorageKind StorageKind) { + ConstantResultStorageKind StorageKind) { AssertResultStorageKind(StorageKind); unsigned Size = totalSizeToAlloc( - StorageKind == ConstantExpr::RSK_APValue, - StorageKind == ConstantExpr::RSK_Int64); + StorageKind == ConstantResultStorageKind::APValue, + StorageKind == ConstantResultStorageKind::Int64); void *Mem = Context.Allocate(Size, alignof(ConstantExpr)); return new (Mem) ConstantExpr(EmptyShell(), StorageKind); } @@ -368,15 +369,15 @@ void ConstantExpr::MoveIntoResult(APValue &Value, const ASTContext &Context) { assert((unsigned)getStorageKind(Value) <= ConstantExprBits.ResultKind && "Invalid storage for this value kind"); ConstantExprBits.APValueKind = Value.getKind(); - switch (ConstantExprBits.ResultKind) { - case RSK_None: + switch (getResultStorageKind()) { + case ConstantResultStorageKind::None: return; - case RSK_Int64: + case ConstantResultStorageKind::Int64: Int64Result() = *Value.getInt().getRawData(); ConstantExprBits.BitWidth = Value.getInt().getBitWidth(); ConstantExprBits.IsUnsigned = Value.getInt().isUnsigned(); return; - case RSK_APValue: + case ConstantResultStorageKind::APValue: if (!ConstantExprBits.HasCleanup && Value.needsCleanup()) { ConstantExprBits.HasCleanup = true; Context.addDestruction(&APValueResult()); @@ -388,10 +389,10 @@ void ConstantExpr::MoveIntoResult(APValue &Value, const ASTContext &Context) { } llvm::APSInt ConstantExpr::getResultAsAPSInt() const { - switch (ConstantExprBits.ResultKind) { - case ConstantExpr::RSK_APValue: + switch (getResultStorageKind()) { + case ConstantResultStorageKind::APValue: return APValueResult().getInt(); - case ConstantExpr::RSK_Int64: + case ConstantResultStorageKind::Int64: return llvm::APSInt(llvm::APInt(ConstantExprBits.BitWidth, Int64Result()), ConstantExprBits.IsUnsigned); default: @@ -401,14 +402,14 @@ llvm::APSInt ConstantExpr::getResultAsAPSInt() const { APValue ConstantExpr::getAPValueResult() const { - switch (ConstantExprBits.ResultKind) { - case ConstantExpr::RSK_APValue: + switch (getResultStorageKind()) { + case ConstantResultStorageKind::APValue: return APValueResult(); - case ConstantExpr::RSK_Int64: + case ConstantResultStorageKind::Int64: return APValue( llvm::APSInt(llvm::APInt(ConstantExprBits.BitWidth, Int64Result()), ConstantExprBits.IsUnsigned)); - case ConstantExpr::RSK_None: + case ConstantResultStorageKind::None: if (ConstantExprBits.APValueKind == APValue::Indeterminate) return APValue::IndeterminateValue(); return APValue(); @@ -603,10 +604,11 @@ std::string SYCLUniqueStableNameExpr::ComputeName(ASTContext &Context, return Out.str(); } -PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, IdentKind IK, - bool IsTransparent, StringLiteral *SL) +PredefinedExpr::PredefinedExpr(SourceLocation L, QualType FNTy, + PredefinedIdentKind IK, bool IsTransparent, + StringLiteral *SL) : Expr(PredefinedExprClass, FNTy, VK_LValue, OK_Ordinary) { - PredefinedExprBits.Kind = IK; + PredefinedExprBits.Kind = llvm::to_underlying(IK); assert((getIdentKind() == IK) && "IdentKind do not fit in PredefinedExprBitfields!"); bool HasFunctionName = SL != nullptr; @@ -624,7 +626,7 @@ PredefinedExpr::PredefinedExpr(EmptyShell Empty, bool HasFunctionName) } PredefinedExpr *PredefinedExpr::Create(const ASTContext &Ctx, SourceLocation L, - QualType FNTy, IdentKind IK, + QualType FNTy, PredefinedIdentKind IK, bool IsTransparent, StringLiteral *SL) { bool HasFunctionName = SL != nullptr; void *Mem = Ctx.Allocate(totalSizeToAlloc(HasFunctionName), @@ -639,23 +641,23 @@ PredefinedExpr *PredefinedExpr::CreateEmpty(const ASTContext &Ctx, return new (Mem) PredefinedExpr(EmptyShell(), HasFunctionName); } -StringRef PredefinedExpr::getIdentKindName(PredefinedExpr::IdentKind IK) { +StringRef PredefinedExpr::getIdentKindName(PredefinedIdentKind IK) { switch (IK) { - case Func: + case PredefinedIdentKind::Func: return "__func__"; - case Function: + case PredefinedIdentKind::Function: return "__FUNCTION__"; - case FuncDName: + case PredefinedIdentKind::FuncDName: return "__FUNCDNAME__"; - case LFunction: + case PredefinedIdentKind::LFunction: return "L__FUNCTION__"; - case PrettyFunction: + case PredefinedIdentKind::PrettyFunction: return "__PRETTY_FUNCTION__"; - case FuncSig: + case PredefinedIdentKind::FuncSig: return "__FUNCSIG__"; - case LFuncSig: + case PredefinedIdentKind::LFuncSig: return "L__FUNCSIG__"; - case PrettyFunctionNoVirtual: + case PredefinedIdentKind::PrettyFunctionNoVirtual: break; } llvm_unreachable("Unknown ident kind for PredefinedExpr"); @@ -663,10 +665,11 @@ StringRef PredefinedExpr::getIdentKindName(PredefinedExpr::IdentKind IK) { // FIXME: Maybe this should use DeclPrinter with a special "print predefined // expr" policy instead. -std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { +std::string PredefinedExpr::ComputeName(PredefinedIdentKind IK, + const Decl *CurrentDecl) { ASTContext &Context = CurrentDecl->getASTContext(); - if (IK == PredefinedExpr::FuncDName) { + if (IK == PredefinedIdentKind::FuncDName) { if (const NamedDecl *ND = dyn_cast(CurrentDecl)) { std::unique_ptr MC; MC.reset(Context.createMangleContext()); @@ -711,15 +714,17 @@ std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { return std::string(Out.str()); } if (const FunctionDecl *FD = dyn_cast(CurrentDecl)) { - if (IK != PrettyFunction && IK != PrettyFunctionNoVirtual && - IK != FuncSig && IK != LFuncSig) + if (IK != PredefinedIdentKind::PrettyFunction && + IK != PredefinedIdentKind::PrettyFunctionNoVirtual && + IK != PredefinedIdentKind::FuncSig && + IK != PredefinedIdentKind::LFuncSig) return FD->getNameAsString(); SmallString<256> Name; llvm::raw_svector_ostream Out(Name); if (const CXXMethodDecl *MD = dyn_cast(FD)) { - if (MD->isVirtual() && IK != PrettyFunctionNoVirtual) + if (MD->isVirtual() && IK != PredefinedIdentKind::PrettyFunctionNoVirtual) Out << "virtual "; if (MD->isStatic()) Out << "static "; @@ -751,7 +756,8 @@ std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { if (FD->hasWrittenPrototype()) FT = dyn_cast(AFT); - if (IK == FuncSig || IK == LFuncSig) { + if (IK == PredefinedIdentKind::FuncSig || + IK == PredefinedIdentKind::LFuncSig) { switch (AFT->getCallConv()) { case CC_C: POut << "__cdecl "; break; case CC_X86StdCall: POut << "__stdcall "; break; @@ -776,7 +782,8 @@ std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { if (FT->isVariadic()) { if (FD->getNumParams()) POut << ", "; POut << "..."; - } else if ((IK == FuncSig || IK == LFuncSig || + } else if ((IK == PredefinedIdentKind::FuncSig || + IK == PredefinedIdentKind::LFuncSig || !Context.getLangOpts().CPlusPlus) && !Decl->getNumParams()) { POut << "void"; @@ -901,7 +908,8 @@ std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { return std::string(Name); } - if (isa(CurrentDecl) && IK == PrettyFunction) { + if (isa(CurrentDecl) && + IK == PredefinedIdentKind::PrettyFunction) { // __PRETTY_FUNCTION__ -> "top level", the others produce an empty string. return "top level"; } @@ -981,21 +989,21 @@ std::string FixedPointLiteral::getValueAsString(unsigned Radix) const { return std::string(S.str()); } -void CharacterLiteral::print(unsigned Val, CharacterKind Kind, +void CharacterLiteral::print(unsigned Val, CharacterLiteralKind Kind, raw_ostream &OS) { switch (Kind) { - case CharacterLiteral::Ascii: + case CharacterLiteralKind::Ascii: break; // no prefix. - case CharacterLiteral::Wide: + case CharacterLiteralKind::Wide: OS << 'L'; break; - case CharacterLiteral::UTF8: + case CharacterLiteralKind::UTF8: OS << "u8"; break; - case CharacterLiteral::UTF16: + case CharacterLiteralKind::UTF16: OS << 'u'; break; - case CharacterLiteral::UTF32: + case CharacterLiteralKind::UTF32: OS << 'U'; break; } @@ -1008,7 +1016,7 @@ void CharacterLiteral::print(unsigned Val, CharacterKind Kind, // would result in an invalid \U escape sequence. // FIXME: multicharacter literals such as '\xFF\xFF\xFF\xFF' // are not correctly handled. - if ((Val & ~0xFFu) == ~0xFFu && Kind == CharacterLiteral::Ascii) + if ((Val & ~0xFFu) == ~0xFFu && Kind == CharacterLiteralKind::Ascii) Val &= 0xFFu; if (Val < 256 && isPrintable((unsigned char)Val)) OS << "'" << (char)Val << "'"; @@ -1059,23 +1067,23 @@ double FloatingLiteral::getValueAsApproximateDouble() const { } unsigned StringLiteral::mapCharByteWidth(TargetInfo const &Target, - StringKind SK) { + StringLiteralKind SK) { unsigned CharByteWidth = 0; switch (SK) { - case Ordinary: - case UTF8: + case StringLiteralKind::Ordinary: + case StringLiteralKind::UTF8: CharByteWidth = Target.getCharWidth(); break; - case Wide: + case StringLiteralKind::Wide: CharByteWidth = Target.getWCharWidth(); break; - case UTF16: + case StringLiteralKind::UTF16: CharByteWidth = Target.getChar16Width(); break; - case UTF32: + case StringLiteralKind::UTF32: CharByteWidth = Target.getChar32Width(); break; - case Unevaluated: + case StringLiteralKind::Unevaluated: return sizeof(char); // Host; } assert((CharByteWidth & 7) == 0 && "Assumes character size is byte multiple"); @@ -1086,17 +1094,17 @@ unsigned StringLiteral::mapCharByteWidth(TargetInfo const &Target, } StringLiteral::StringLiteral(const ASTContext &Ctx, StringRef Str, - StringKind Kind, bool Pascal, QualType Ty, + StringLiteralKind Kind, bool Pascal, QualType Ty, const SourceLocation *Loc, unsigned NumConcatenated) : Expr(StringLiteralClass, Ty, VK_LValue, OK_Ordinary) { unsigned Length = Str.size(); - StringLiteralBits.Kind = Kind; + StringLiteralBits.Kind = llvm::to_underlying(Kind); StringLiteralBits.NumConcatenated = NumConcatenated; - if (Kind != StringKind::Unevaluated) { + if (Kind != StringLiteralKind::Unevaluated) { assert(Ctx.getAsConstantArrayType(Ty) && "StringLiteral must be of constant array type!"); unsigned CharByteWidth = mapCharByteWidth(Ctx.getTargetInfo(), Kind); @@ -1151,8 +1159,8 @@ StringLiteral::StringLiteral(EmptyShell Empty, unsigned NumConcatenated, } StringLiteral *StringLiteral::Create(const ASTContext &Ctx, StringRef Str, - StringKind Kind, bool Pascal, QualType Ty, - const SourceLocation *Loc, + StringLiteralKind Kind, bool Pascal, + QualType Ty, const SourceLocation *Loc, unsigned NumConcatenated) { void *Mem = Ctx.Allocate(totalSizeToAlloc( 1, NumConcatenated, Str.size()), @@ -1174,13 +1182,21 @@ StringLiteral *StringLiteral::CreateEmpty(const ASTContext &Ctx, void StringLiteral::outputString(raw_ostream &OS) const { switch (getKind()) { - case Unevaluated: - case Ordinary: + case StringLiteralKind::Unevaluated: + case StringLiteralKind::Ordinary: break; // no prefix. - case Wide: OS << 'L'; break; - case UTF8: OS << "u8"; break; - case UTF16: OS << 'u'; break; - case UTF32: OS << 'U'; break; + case StringLiteralKind::Wide: + OS << 'L'; + break; + case StringLiteralKind::UTF8: + OS << "u8"; + break; + case StringLiteralKind::UTF16: + OS << 'u'; + break; + case StringLiteralKind::UTF32: + OS << 'U'; + break; } OS << '"'; static const char Hex[] = "0123456789ABCDEF"; @@ -1194,8 +1210,8 @@ void StringLiteral::outputString(raw_ostream &OS) const { // Convert UTF-16 surrogate pairs back to codepoints before rendering. // Leave invalid surrogates alone; we'll use \x for those. - if (getKind() == UTF16 && I != N - 1 && Char >= 0xd800 && - Char <= 0xdbff) { + if (getKind() == StringLiteralKind::UTF16 && I != N - 1 && + Char >= 0xd800 && Char <= 0xdbff) { uint32_t Trail = getCodeUnit(I + 1); if (Trail >= 0xdc00 && Trail <= 0xdfff) { Char = 0x10000 + ((Char - 0xd800) << 10) + (Trail - 0xdc00); @@ -1207,7 +1223,7 @@ void StringLiteral::outputString(raw_ostream &OS) const { // If this is a wide string, output characters over 0xff using \x // escapes. Otherwise, this is a UTF-16 or UTF-32 string, and Char is a // codepoint: use \x escapes for invalid codepoints. - if (getKind() == Wide || + if (getKind() == StringLiteralKind::Wide || (Char >= 0xd800 && Char <= 0xdfff) || Char >= 0x110000) { // FIXME: Is this the best way to print wchar_t? OS << "\\x"; @@ -1284,9 +1300,9 @@ StringLiteral::getLocationOfByte(unsigned ByteNo, const SourceManager &SM, const LangOptions &Features, const TargetInfo &Target, unsigned *StartToken, unsigned *StartTokenByteOffset) const { - assert((getKind() == StringLiteral::Ordinary || - getKind() == StringLiteral::UTF8 || - getKind() == StringLiteral::Unevaluated) && + assert((getKind() == StringLiteralKind::Ordinary || + getKind() == StringLiteralKind::UTF8 || + getKind() == StringLiteralKind::Unevaluated) && "Only narrow string literals are currently supported"); // Loop over all of the tokens in this string until we find the one that @@ -2196,31 +2212,31 @@ bool BinaryOperator::isNullPointerArithmeticExtension(ASTContext &Ctx, return true; } -SourceLocExpr::SourceLocExpr(const ASTContext &Ctx, IdentKind Kind, +SourceLocExpr::SourceLocExpr(const ASTContext &Ctx, SourceLocIdentKind Kind, QualType ResultTy, SourceLocation BLoc, SourceLocation RParenLoc, DeclContext *ParentContext) : Expr(SourceLocExprClass, ResultTy, VK_PRValue, OK_Ordinary), BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) { - SourceLocExprBits.Kind = Kind; + SourceLocExprBits.Kind = llvm::to_underlying(Kind); setDependence(ExprDependence::None); } StringRef SourceLocExpr::getBuiltinStr() const { switch (getIdentKind()) { - case File: + case SourceLocIdentKind::File: return "__builtin_FILE"; - case FileName: + case SourceLocIdentKind::FileName: return "__builtin_FILE_NAME"; - case Function: + case SourceLocIdentKind::Function: return "__builtin_FUNCTION"; - case FuncSig: + case SourceLocIdentKind::FuncSig: return "__builtin_FUNCSIG"; - case Line: + case SourceLocIdentKind::Line: return "__builtin_LINE"; - case Column: + case SourceLocIdentKind::Column: return "__builtin_COLUMN"; - case SourceLocStruct: + case SourceLocIdentKind::SourceLocStruct: return "__builtin_source_location"; } llvm_unreachable("unexpected IdentKind!"); @@ -2255,7 +2271,7 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, }; switch (getIdentKind()) { - case SourceLocExpr::FileName: { + case SourceLocIdentKind::FileName: { // __builtin_FILE_NAME() is a Clang-specific extension that expands to the // the last part of __builtin_FILE(). SmallString<256> FileName; @@ -2263,26 +2279,26 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, FileName, PLoc, Ctx.getLangOpts(), Ctx.getTargetInfo()); return MakeStringLiteral(FileName); } - case SourceLocExpr::File: { + case SourceLocIdentKind::File: { SmallString<256> Path(PLoc.getFilename()); clang::Preprocessor::processPathForFileMacro(Path, Ctx.getLangOpts(), Ctx.getTargetInfo()); return MakeStringLiteral(Path); } - case SourceLocExpr::Function: - case SourceLocExpr::FuncSig: { + case SourceLocIdentKind::Function: + case SourceLocIdentKind::FuncSig: { const auto *CurDecl = dyn_cast(Context); - const auto Kind = getIdentKind() == SourceLocExpr::Function - ? PredefinedExpr::Function - : PredefinedExpr::FuncSig; + const auto Kind = getIdentKind() == SourceLocIdentKind::Function + ? PredefinedIdentKind::Function + : PredefinedIdentKind::FuncSig; return MakeStringLiteral( CurDecl ? PredefinedExpr::ComputeName(Kind, CurDecl) : std::string("")); } - case SourceLocExpr::Line: + case SourceLocIdentKind::Line: return APValue(Ctx.MakeIntValue(PLoc.getLine(), Ctx.UnsignedIntTy)); - case SourceLocExpr::Column: + case SourceLocIdentKind::Column: return APValue(Ctx.MakeIntValue(PLoc.getColumn(), Ctx.UnsignedIntTy)); - case SourceLocExpr::SourceLocStruct: { + case SourceLocIdentKind::SourceLocStruct: { // Fill in a std::source_location::__impl structure, by creating an // artificial file-scoped CompoundLiteralExpr, and returning a pointer to // that. @@ -2308,7 +2324,7 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, Value.getStructField(F->getFieldIndex()) = MakeStringLiteral( CurDecl && !isa(CurDecl) ? StringRef(PredefinedExpr::ComputeName( - PredefinedExpr::PrettyFunction, CurDecl)) + PredefinedIdentKind::PrettyFunction, CurDecl)) : ""); } else if (Name == "_M_line") { llvm::APSInt IntVal = Ctx.MakeIntValue(PLoc.getLine(), F->getType()); @@ -4967,10 +4983,10 @@ QualType OMPArraySectionExpr::getBaseOriginalType(const Expr *Base) { for (unsigned Cnt = 0; Cnt < ArraySectionCount; ++Cnt) { if (OriginalTy->isAnyPointerType()) OriginalTy = OriginalTy->getPointeeType(); - else { - assert (OriginalTy->isArrayType()); + else if (OriginalTy->isArrayType()) OriginalTy = OriginalTy->castAsArrayTypeUnsafe()->getElementType(); - } + else + return {}; } return OriginalTy; } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index b9a004acc5ad06ffed58eb7cc367e681e8b81e39..83af7998f68338244d9e28598045691f5ac418b8 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -184,7 +184,7 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, SourceRange TypeIdParens, std::optional ArraySize, - InitializationStyle InitializationStyle, + CXXNewInitializationStyle InitializationStyle, Expr *Initializer, QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, SourceRange DirectInitRange) @@ -193,7 +193,9 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, AllocatedTypeInfo(AllocatedTypeInfo), Range(Range), DirectInitRange(DirectInitRange) { - assert((Initializer != nullptr || InitializationStyle == NoInit) && + assert((Initializer != nullptr || + InitializationStyle == CXXNewInitializationStyle::None || + InitializationStyle == CXXNewInitializationStyle::Implicit) && "Only NoInit can have no initializer!"); CXXNewExprBits.IsGlobalNew = IsGlobalNew; @@ -201,7 +203,7 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, CXXNewExprBits.ShouldPassAlignment = ShouldPassAlignment; CXXNewExprBits.UsualArrayDeleteWantsSize = UsualArrayDeleteWantsSize; CXXNewExprBits.StoredInitializationStyle = - Initializer ? InitializationStyle + 1 : 0; + llvm::to_underlying(InitializationStyle); bool IsParenTypeId = TypeIdParens.isValid(); CXXNewExprBits.IsParenTypeId = IsParenTypeId; CXXNewExprBits.NumPlacementArgs = PlacementArgs.size(); @@ -217,10 +219,10 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, getTrailingObjects()[0] = TypeIdParens; switch (getInitializationStyle()) { - case CallInit: + case CXXNewInitializationStyle::Call: this->Range.setEnd(DirectInitRange.getEnd()); break; - case ListInit: + case CXXNewInitializationStyle::List: this->Range.setEnd(getInitializer()->getSourceRange().getEnd()); break; default: @@ -240,15 +242,14 @@ CXXNewExpr::CXXNewExpr(EmptyShell Empty, bool IsArray, CXXNewExprBits.IsParenTypeId = IsParenTypeId; } -CXXNewExpr * -CXXNewExpr::Create(const ASTContext &Ctx, bool IsGlobalNew, - FunctionDecl *OperatorNew, FunctionDecl *OperatorDelete, - bool ShouldPassAlignment, bool UsualArrayDeleteWantsSize, - ArrayRef PlacementArgs, SourceRange TypeIdParens, - std::optional ArraySize, - InitializationStyle InitializationStyle, Expr *Initializer, - QualType Ty, TypeSourceInfo *AllocatedTypeInfo, - SourceRange Range, SourceRange DirectInitRange) { +CXXNewExpr *CXXNewExpr::Create( + const ASTContext &Ctx, bool IsGlobalNew, FunctionDecl *OperatorNew, + FunctionDecl *OperatorDelete, bool ShouldPassAlignment, + bool UsualArrayDeleteWantsSize, ArrayRef PlacementArgs, + SourceRange TypeIdParens, std::optional ArraySize, + CXXNewInitializationStyle InitializationStyle, Expr *Initializer, + QualType Ty, TypeSourceInfo *AllocatedTypeInfo, SourceRange Range, + SourceRange DirectInitRange) { bool IsArray = ArraySize.has_value(); bool HasInit = Initializer != nullptr; unsigned NumPlacementArgs = PlacementArgs.size(); @@ -1063,7 +1064,7 @@ CXXTemporaryObjectExpr::CXXTemporaryObjectExpr( CXXTemporaryObjectExprClass, Ty, TSI->getTypeLoc().getBeginLoc(), Cons, /* Elidable=*/false, Args, HadMultipleCandidates, ListInitialization, StdInitListInitialization, ZeroInitialization, - CXXConstructExpr::CK_Complete, ParenOrBraceRange), + CXXConstructionKind::Complete, ParenOrBraceRange), TSI(TSI) { setDependence(computeDependence(this)); } @@ -1111,7 +1112,7 @@ CXXConstructExpr *CXXConstructExpr::Create( CXXConstructorDecl *Ctor, bool Elidable, ArrayRef Args, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, bool ZeroInitialization, - ConstructionKind ConstructKind, SourceRange ParenOrBraceRange) { + CXXConstructionKind ConstructKind, SourceRange ParenOrBraceRange) { unsigned SizeOfTrailingObjects = sizeOfTrailingObjects(Args.size()); void *Mem = Ctx.Allocate(sizeof(CXXConstructExpr) + SizeOfTrailingObjects, alignof(CXXConstructExpr)); @@ -1134,7 +1135,7 @@ CXXConstructExpr::CXXConstructExpr( StmtClass SC, QualType Ty, SourceLocation Loc, CXXConstructorDecl *Ctor, bool Elidable, ArrayRef Args, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, - bool ZeroInitialization, ConstructionKind ConstructKind, + bool ZeroInitialization, CXXConstructionKind ConstructKind, SourceRange ParenOrBraceRange) : Expr(SC, Ty, VK_PRValue, OK_Ordinary), Constructor(Ctor), ParenOrBraceRange(ParenOrBraceRange), NumArgs(Args.size()) { @@ -1143,7 +1144,7 @@ CXXConstructExpr::CXXConstructExpr( CXXConstructExprBits.ListInitialization = ListInitialization; CXXConstructExprBits.StdInitListInitialization = StdInitListInitialization; CXXConstructExprBits.ZeroInitialization = ZeroInitialization; - CXXConstructExprBits.ConstructionKind = ConstructKind; + CXXConstructExprBits.ConstructionKind = llvm::to_underlying(ConstructKind); CXXConstructExprBits.IsImmediateEscalating = false; CXXConstructExprBits.Loc = Loc; diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h new file mode 100644 index 0000000000000000000000000000000000000000..a97eac85abc69e9653c9e84362d806f6a98efe75 --- /dev/null +++ b/clang/lib/AST/ExprConstShared.h @@ -0,0 +1,59 @@ +//===--- ExprConstShared.h - Shared consetxpr functionality ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Shared functionality between the new constant expression +// interpreter (AST/Interp/) and the current one (ExprConstant.cpp). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H +#define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H + +namespace clang { +class QualType; +class LangOptions; +} // namespace clang +using namespace clang; +/// Values returned by __builtin_classify_type, chosen to match the values +/// produced by GCC's builtin. +enum class GCCTypeClass { + None = -1, + Void = 0, + Integer = 1, + // GCC reserves 2 for character types, but instead classifies them as + // integers. + Enum = 3, + Bool = 4, + Pointer = 5, + // GCC reserves 6 for references, but appears to never use it (because + // expressions never have reference type, presumably). + PointerToDataMember = 7, + RealFloat = 8, + Complex = 9, + // GCC reserves 10 for functions, but does not use it since GCC version 6 due + // to decay to pointer. (Prior to version 6 it was only used in C++ mode). + // GCC claims to reserve 11 for pointers to member functions, but *actually* + // uses 12 for that purpose, same as for a class or struct. Maybe it + // internally implements a pointer to member as a struct? Who knows. + PointerToMemberFunction = 12, // Not a bug, see above. + ClassOrStruct = 12, + Union = 13, + // GCC reserves 14 for arrays, but does not use it since GCC version 6 due to + // decay to pointer. (Prior to version 6 it was only used in C++ mode). + // GCC reserves 15 for strings, but actually uses 5 (pointer) for string + // literals. + // Lang = 16, + // OpaqueType = 17, + BitInt = 18, + Vector = 19 +}; + +GCCTypeClass EvaluateBuiltinClassifyType(QualType T, + const LangOptions &LangOpts); + +#endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index b6b1e6617dffaa97938608d3c9da4d10661f7cbd..16697e5f076a8f8d718190a45f5d99f337ffaf4d 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -32,6 +32,7 @@ // //===----------------------------------------------------------------------===// +#include "ExprConstShared.h" #include "Interp/Context.h" #include "Interp/Frame.h" #include "Interp/State.h" @@ -3184,9 +3185,14 @@ static bool HandleLValueIndirectMember(EvalInfo &Info, const Expr *E, return true; } +enum class SizeOfType { + SizeOf, + DataSizeOf, +}; + /// Get the size of the given type in char units. -static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, - QualType Type, CharUnits &Size) { +static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, QualType Type, + CharUnits &Size, SizeOfType SOT = SizeOfType::SizeOf) { // sizeof(void), __alignof__(void), sizeof(function) = 1 as a gcc // extension. if (Type->isVoidType() || Type->isFunctionType()) { @@ -3206,7 +3212,10 @@ static bool HandleSizeof(EvalInfo &Info, SourceLocation Loc, return false; } - Size = Info.Ctx.getTypeSizeInChars(Type); + if (SOT == SizeOfType::SizeOf) + Size = Info.Ctx.getTypeSizeInChars(Type); + else + Size = Info.Ctx.getTypeInfoDataSizeInChars(Type).Width; return true; } @@ -9043,7 +9052,7 @@ public: CharTy, Size, nullptr, ArraySizeModifier::Normal, 0); StringLiteral *SL = - StringLiteral::Create(Info.Ctx, ResultStr, StringLiteral::Ordinary, + StringLiteral::Create(Info.Ctx, ResultStr, StringLiteralKind::Ordinary, /*Pascal*/ false, ArrayTy, E->getLocation()); evaluateLValue(SL, Result); @@ -11484,40 +11493,10 @@ bool IntExprEvaluator::CheckReferencedDecl(const Expr* E, const Decl* D) { return false; } -/// Values returned by __builtin_classify_type, chosen to match the values -/// produced by GCC's builtin. -enum class GCCTypeClass { - None = -1, - Void = 0, - Integer = 1, - // GCC reserves 2 for character types, but instead classifies them as - // integers. - Enum = 3, - Bool = 4, - Pointer = 5, - // GCC reserves 6 for references, but appears to never use it (because - // expressions never have reference type, presumably). - PointerToDataMember = 7, - RealFloat = 8, - Complex = 9, - // GCC reserves 10 for functions, but does not use it since GCC version 6 due - // to decay to pointer. (Prior to version 6 it was only used in C++ mode). - // GCC claims to reserve 11 for pointers to member functions, but *actually* - // uses 12 for that purpose, same as for a class or struct. Maybe it - // internally implements a pointer to member as a struct? Who knows. - PointerToMemberFunction = 12, // Not a bug, see above. - ClassOrStruct = 12, - Union = 13, - // GCC reserves 14 for arrays, but does not use it since GCC version 6 due to - // decay to pointer. (Prior to version 6 it was only used in C++ mode). - // GCC reserves 15 for strings, but actually uses 5 (pointer) for string - // literals. -}; - /// EvaluateBuiltinClassifyType - Evaluate __builtin_classify_type the same way /// as GCC. -static GCCTypeClass -EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { +GCCTypeClass EvaluateBuiltinClassifyType(QualType T, + const LangOptions &LangOpts) { assert(!T->isDependentType() && "unexpected dependent type"); QualType CanTy = T.getCanonicalType(); @@ -11636,19 +11615,23 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { return EvaluateBuiltinClassifyType( CanTy->castAs()->getValueType(), LangOpts); - case Type::BlockPointer: case Type::Vector: case Type::ExtVector: + return GCCTypeClass::Vector; + + case Type::BlockPointer: case Type::ConstantMatrix: case Type::ObjCObject: case Type::ObjCInterface: case Type::ObjCObjectPointer: case Type::Pipe: - case Type::BitInt: - // GCC classifies vectors as None. We follow its lead and classify all - // other types that don't fit into the regular classification the same way. + // Classify all other types that don't fit into the regular + // classification the same way. return GCCTypeClass::None; + case Type::BitInt: + return GCCTypeClass::BitInt; + case Type::LValueReference: case Type::RValueReference: llvm_unreachable("invalid type for expression"); @@ -13689,6 +13672,7 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( return Success(1, E); } + case UETT_DataSizeOf: case UETT_SizeOf: { QualType SrcTy = E->getTypeOfArgument(); // C++ [expr.sizeof]p2: "When applied to a reference or a reference type, @@ -13697,8 +13681,11 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( SrcTy = Ref->getPointeeType(); CharUnits Sizeof; - if (!HandleSizeof(Info, E->getExprLoc(), SrcTy, Sizeof)) + if (!HandleSizeof(Info, E->getExprLoc(), SrcTy, Sizeof, + E->getKind() == UETT_DataSizeOf ? SizeOfType::DataSizeOf + : SizeOfType::SizeOf)) { return false; + } return Success(Sizeof, E); } case UETT_OpenMPRequiredSimdAlign: @@ -15660,12 +15647,14 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, // this doesn't escape. MaterializeTemporaryExpr BaseMTE(T, const_cast(this), true); APValue::LValueBase Base(&BaseMTE); - Info.setEvaluatingDecl(Base, Result.Val); - LValue LVal; - LVal.set(Base); - { + if (Info.EnableNewConstInterp) { + if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, this, Result.Val)) + return false; + } else { + LValue LVal; + LVal.set(Base); // C++23 [intro.execution]/p5 // A full-expression is [...] a constant-expression // So we need to make sure temporary objects are destroyed after having @@ -15674,10 +15663,10 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, if (!::EvaluateInPlace(Result.Val, Info, LVal, this) || Result.HasSideEffects || !Scope.destroy()) return false; - } - if (!Info.discardCleanups()) - llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + } if (!CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this), Result.Val, Kind)) diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index c8abb7c17a38ba2454b7cbd9e660b55d77899fd1..89b7708c0c2a12fcdcdfdf84fc0158d1a95b3dfd 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -14,6 +14,7 @@ #include "Program.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/DeclCXX.h" +#include "clang/Basic/Builtins.h" #include using namespace clang; @@ -84,10 +85,16 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { // Create a handle over the emitted code. Function *Func = P.getFunction(FuncDecl); - if (!Func) - Func = P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), - std::move(ParamDescriptors), - std::move(ParamOffsets), HasThisPointer, HasRVO); + if (!Func) { + bool IsUnevaluatedBuiltin = false; + if (unsigned BI = FuncDecl->getBuiltinID()) + IsUnevaluatedBuiltin = Ctx.getASTContext().BuiltinInfo.isUnevaluated(BI); + + Func = + P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors), std::move(ParamOffsets), + HasThisPointer, HasRVO, IsUnevaluatedBuiltin); + } assert(Func); // For not-yet-defined functions, we only create a Function instance and diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 485893d58f487ae6b967ae5c3fa29652ce3b0be5..f45e8624a7741a536c82564f910a698387a1f8ed 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -75,7 +75,7 @@ private: template bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { - auto *SubExpr = CE->getSubExpr(); + const Expr *SubExpr = CE->getSubExpr(); switch (CE->getCastKind()) { case CK_LValueToRValue: { @@ -200,16 +200,13 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { if (!this->visit(SubExpr)) return false; - if (FromT == ToT) { - assert(ToT != PT_IntAP && ToT != PT_IntAPS); - return true; - } - if (ToT == PT_IntAP) return this->emitCastAP(*FromT, Ctx.getBitWidth(CE->getType()), CE); if (ToT == PT_IntAPS) return this->emitCastAPS(*FromT, Ctx.getBitWidth(CE->getType()), CE); + if (FromT == ToT) + return true; return this->emitCast(*FromT, *ToT, CE); } @@ -617,6 +614,29 @@ bool ByteCodeExprGen::visitInitList(ArrayRef Inits, return true; } +/// Pointer to the array(not the element!) must be on the stack when calling +/// this. +template +bool ByteCodeExprGen::visitArrayElemInit(unsigned ElemIndex, + const Expr *Init) { + if (std::optional T = classify(Init->getType())) { + // Visit the primitive element like normal. + if (!this->visit(Init)) + return false; + return this->emitInitElem(*T, ElemIndex, Init); + } + + // Advance the pointer currently on the stack to the given + // dimension. + if (!this->emitConstUint32(ElemIndex, Init)) + return false; + if (!this->emitArrayElemPtrUint32(Init)) + return false; + if (!this->visitInitializer(Init)) + return false; + return this->emitPopPtr(Init); +} + template bool ByteCodeExprGen::VisitInitListExpr(const InitListExpr *E) { // Handle discarding first. @@ -645,25 +665,8 @@ bool ByteCodeExprGen::VisitInitListExpr(const InitListExpr *E) { // FIXME: Array fillers. unsigned ElementIndex = 0; for (const Expr *Init : E->inits()) { - if (std::optional T = classify(Init->getType())) { - // Visit the primitive element like normal. - if (!this->visit(Init)) - return false; - if (!this->emitInitElem(*T, ElementIndex, Init)) - return false; - } else { - // Advance the pointer currently on the stack to the given - // dimension. - if (!this->emitConstUint32(ElementIndex, Init)) - return false; - if (!this->emitArrayElemPtrUint32(Init)) - return false; - if (!this->visitInitializer(Init)) - return false; - if (!this->emitPopPtr(Init)) - return false; - } - + if (!this->visitArrayElemInit(ElementIndex, Init)) + return false; ++ElementIndex; } return true; @@ -834,7 +837,6 @@ bool ByteCodeExprGen::VisitArrayInitLoopExpr( const Expr *SubExpr = E->getSubExpr(); const Expr *CommonExpr = E->getCommonExpr(); size_t Size = E->getArraySize().getZExtValue(); - std::optional ElemT = classify(SubExpr->getType()); // If the common expression is an opaque expression, we visit it // here once so we have its value cached. @@ -851,22 +853,8 @@ bool ByteCodeExprGen::VisitArrayInitLoopExpr( ArrayIndexScope IndexScope(this, I); BlockScope BS(this); - if (ElemT) { - if (!this->visit(SubExpr)) - return false; - if (!this->emitInitElem(*ElemT, I, E)) - return false; - } else { - // Get to our array element and recurse into visitInitializer() - if (!this->emitConstUint64(I, SubExpr)) - return false; - if (!this->emitArrayElemPtrUint64(SubExpr)) - return false; - if (!visitInitializer(SubExpr)) - return false; - if (!this->emitPopPtr(E)) - return false; - } + if (!this->visitArrayElemInit(I, SubExpr)) + return false; } return true; } @@ -1627,6 +1615,11 @@ bool ByteCodeExprGen::VisitCXXScalarValueInitExpr( return this->visitZeroInitializer(classifyPrim(Ty), Ty, E); } +template +bool ByteCodeExprGen::VisitSizeOfPackExpr(const SizeOfPackExpr *E) { + return this->emitConst(E->getPackLength(), E); +} + template bool ByteCodeExprGen::discard(const Expr *E) { if (E->containsErrors()) return false; @@ -2122,27 +2115,33 @@ bool ByteCodeExprGen::visitDecl(const VarDecl *VD) { if (!this->visitVarDecl(VD)) return false; + std::optional VarT = classify(VD->getType()); // Get a pointer to the variable if (Context::shouldBeGloballyIndexed(VD)) { auto GlobalIndex = P.getGlobal(VD); assert(GlobalIndex); // visitVarDecl() didn't return false. - if (!this->emitGetPtrGlobal(*GlobalIndex, VD)) - return false; + if (VarT) { + if (!this->emitGetGlobal(*VarT, *GlobalIndex, VD)) + return false; + } else { + if (!this->emitGetPtrGlobal(*GlobalIndex, VD)) + return false; + } } else { auto Local = Locals.find(VD); assert(Local != Locals.end()); // Same here. - if (!this->emitGetPtrLocal(Local->second.Offset, VD)) - return false; + if (VarT) { + if (!this->emitGetLocal(*VarT, Local->second.Offset, VD)) + return false; + } else { + if (!this->emitGetPtrLocal(Local->second.Offset, VD)) + return false; + } } // Return the value - if (std::optional VarT = classify(VD->getType())) { - if (!this->emitLoadPop(*VarT, VD)) - return false; - + if (VarT) return this->emitRet(*VarT, VD); - } - return this->emitRetValue(VD); } @@ -2223,10 +2222,12 @@ bool ByteCodeExprGen::VisitBuiltinCallExpr(const CallExpr *E) { if (!Func) return false; - // Put arguments on the stack. - for (const auto *Arg : E->arguments()) { - if (!this->visit(Arg)) - return false; + if (!Func->isUnevaluatedBuiltin()) { + // Put arguments on the stack. + for (const auto *Arg : E->arguments()) { + if (!this->visit(Arg)) + return false; + } } if (!this->emitCallBI(Func, E, E)) @@ -2261,13 +2262,17 @@ bool ByteCodeExprGen::VisitCallExpr(const CallExpr *E) { } } else { assert(Initializing); - if (!isa(E)) { - if (!this->emitDupPtr(E)) - return false; - } + if (!this->emitDupPtr(E)) + return false; } } + // Add the (optional, implicit) This pointer. + if (const auto *MC = dyn_cast(E)) { + if (!this->visit(MC->getImplicitObjectArgument())) + return false; + } + // Put arguments on the stack. for (const auto *Arg : E->arguments()) { if (!this->visit(Arg)) @@ -2324,22 +2329,6 @@ bool ByteCodeExprGen::VisitCallExpr(const CallExpr *E) { return true; } -template -bool ByteCodeExprGen::VisitCXXMemberCallExpr( - const CXXMemberCallExpr *E) { - if (Initializing) { - // If we're initializing, the current stack top is the pointer to - // initialize, so dup that so this call has its own version. - if (!this->emitDupPtr(E)) - return false; - } - - if (!this->visit(E->getImplicitObjectArgument())) - return false; - - return VisitCallExpr(E); -} - template bool ByteCodeExprGen::VisitCXXDefaultInitExpr( const CXXDefaultInitExpr *E) { diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 83986d3dd579ed6a67930e4b1f91edfe7372bc5d..bc1d5d11a1151356e75620d448d0627ff60ba9a0 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -68,7 +68,6 @@ public: bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); bool VisitCallExpr(const CallExpr *E); bool VisitBuiltinCallExpr(const CallExpr *E); - bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *E); bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E); bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E); bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E); @@ -107,6 +106,7 @@ public: bool VisitSourceLocExpr(const SourceLocExpr *E); bool VisitOffsetOfExpr(const OffsetOfExpr *E); bool VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E); + bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -129,7 +129,13 @@ protected: /// Classifies a type. std::optional classify(const Expr *E) const { - return E->isGLValue() ? PT_Ptr : classify(E->getType()); + if (E->isGLValue()) { + if (E->getType()->isFunctionType()) + return PT_FnPtr; + return PT_Ptr; + } + + return classify(E->getType()); } std::optional classify(QualType Ty) const { return Ctx.classify(Ty); @@ -203,6 +209,7 @@ protected: } bool visitInitList(ArrayRef Inits, const Expr *E); + bool visitArrayElemInit(unsigned ElemIndex, const Expr *Init); /// Creates a local primitive value. unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst, diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp index 357aff7fe6229b94f3c9eb5b87d607a56903394a..1d04998d5dd158815a51843c43983e46aad9fa73 100644 --- a/clang/lib/AST/Interp/Function.cpp +++ b/clang/lib/AST/Interp/Function.cpp @@ -20,11 +20,12 @@ Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, llvm::SmallVectorImpl &&ParamTypes, llvm::DenseMap &&Params, llvm::SmallVectorImpl &&ParamOffsets, - bool HasThisPointer, bool HasRVO) + bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin) : P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize), ParamTypes(std::move(ParamTypes)), Params(std::move(Params)), ParamOffsets(std::move(ParamOffsets)), HasThisPointer(HasThisPointer), - HasRVO(HasRVO), Variadic(F->isVariadic()) {} + HasRVO(HasRVO), Variadic(F->isVariadic()), + IsUnevaluatedBuiltin(UnevaluatedBuiltin) {} Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { auto It = Params.find(Offset); @@ -48,9 +49,3 @@ bool Function::isVirtual() const { return M->isVirtual(); return false; } - -bool Function::needsRuntimeArgPop(const ASTContext &Ctx) const { - if (!isBuiltin()) - return false; - return Ctx.BuiltinInfo.hasCustomTypechecking(getBuiltinID()); -} diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h index be9b1733635f72523e9ba9dc494b73d5b218024e..7c3e0f630249089ea10d4b5ed84c7919f561fd2a 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -179,9 +179,7 @@ public: bool isBuiltin() const { return F->getBuiltinID() != 0; } - /// Does this function need its arguments to be classified at runtime - /// rather than at bytecode-compile-time? - bool needsRuntimeArgPop(const ASTContext &Ctx) const; + bool isUnevaluatedBuiltin() const { return IsUnevaluatedBuiltin; } unsigned getNumParams() const { return ParamTypes.size(); } @@ -195,7 +193,7 @@ private: llvm::SmallVectorImpl &&ParamTypes, llvm::DenseMap &&Params, llvm::SmallVectorImpl &&ParamOffsets, bool HasThisPointer, - bool HasRVO); + bool HasRVO, bool UnevaluatedBuiltin); /// Sets the code of a function. void setCode(unsigned NewFrameSize, std::vector &&NewCode, @@ -254,6 +252,7 @@ private: bool HasBody = false; bool Defined = false; bool Variadic = false; + bool IsUnevaluatedBuiltin = false; public: /// Dumps the disassembled bytecode to \c llvm::errs(). diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h index 9aefea6d0c47ed96fb372100ca3c2ccb3274755f..9019f32e6cef2a34077c5499ef1f30eeb2cf5de3 100644 --- a/clang/lib/AST/Interp/IntegralAP.h +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -102,7 +102,12 @@ public: template static IntegralAP from(IntegralAP V, unsigned NumBits = 0) { - return IntegralAP(V.V); + if (NumBits == 0) + NumBits = V.bitWidth(); + + if constexpr (InputSigned) + return IntegralAP(V.V.sextOrTrunc(NumBits)); + return IntegralAP(V.V.zextOrTrunc(NumBits)); } template @@ -119,8 +124,16 @@ public: constexpr unsigned bitWidth() const { return V.getBitWidth(); } - APSInt toAPSInt(unsigned Bits = 0) const { return APSInt(V, Signed); } - APValue toAPValue() const { return APValue(APSInt(V, Signed)); } + APSInt toAPSInt(unsigned Bits = 0) const { + if (Bits == 0) + Bits = bitWidth(); + + if constexpr (Signed) + return APSInt(V.sext(Bits), !Signed); + else + return APSInt(V.zext(Bits), !Signed); + } + APValue toAPValue() const { return APValue(toAPSInt()); } bool isZero() const { return V.isZero(); } bool isPositive() const { return V.isNonNegative(); } @@ -140,9 +153,8 @@ public: return NameStr; } - IntegralAP truncate(unsigned bitWidth) const { - assert(false); - return V; + IntegralAP truncate(unsigned BitWidth) const { + return IntegralAP(V.trunc(BitWidth)); } IntegralAP toUnsigned() const { @@ -184,18 +196,15 @@ public: } static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { - return CheckAddUB(A, B, OpBits, R); + return CheckAddSubMulUB(A, B, OpBits, R); } static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { - /// FIXME: Gotta check if the result fits into OpBits bits. - return CheckSubUB(A, B, R); + return CheckAddSubMulUB(A, B, OpBits, R); } static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { - // FIXME: Implement. - assert(false); - return false; + return CheckAddSubMulUB(A, B, OpBits, R); } static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { @@ -212,21 +221,19 @@ public: static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { - // FIXME: Implement. - assert(false); + *R = IntegralAP(A.V & B.V); return false; } static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { - assert(false); + *R = IntegralAP(A.V | B.V); return false; } static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { - // FIXME: Implement. - assert(false); + *R = IntegralAP(A.V ^ B.V); return false; } @@ -249,32 +256,29 @@ public: static void shiftRight(const IntegralAP A, const IntegralAP B, unsigned OpBits, IntegralAP *R) { - *R = IntegralAP(A.V.ashr(B.V.getZExtValue())); + unsigned ShiftAmount = B.V.getZExtValue(); + if constexpr (Signed) + *R = IntegralAP(A.V.ashr(ShiftAmount)); + else + *R = IntegralAP(A.V.lshr(ShiftAmount)); } private: - static bool CheckAddUB(const IntegralAP &A, const IntegralAP &B, - unsigned BitWidth, IntegralAP *R) { - if (!A.isSigned()) { - R->V = A.V + B.V; + template