diff --git a/.ci/monolithic-linux.sh b/.ci/monolithic-linux.sh index 311488b2c2878..f0577d1069d5d 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 00c3037c4c4fd..7ac806a0b399a 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 e2635ffb3eaf0..b9a28edc9daf4 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 e4bc53e60066e..e4ae2b7c839f9 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 6329d777a1820..9c695e714edd6 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/**' @@ -24,10 +27,14 @@ on: - '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/**' @@ -37,6 +44,7 @@ on: - 'openmp/docs/**' - 'polly/docs/**' - 'flang/docs/**' + - 'flang/include/flang/Optimizer/Dialect/FIROps.td' jobs: check-docs-build: @@ -62,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: @@ -80,6 +91,7 @@ jobs: - '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 diff --git a/.github/workflows/issue-subscriber.yml b/.github/workflows/issue-subscriber.yml index 589142b20607c..9a9c8f9c65162 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 0000000000000..018f0e45a2441 --- /dev/null +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -0,0 +1,212 @@ +# 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' + schedule: + # Run nightly at 8 AM UTC (or roughly 3 AM eastern) + - cron: '0 3 * * *' + +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', + # TODO Find a better place for the benchmark and bootstrapping builds to live. They're either very expensive + # or don't provide much value since the benchmark run results are too noise on the bots. + 'benchmarks', + 'bootstrapping-build' + ] + machine: [ 'libcxx-runners-8' ] + 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 a8f696294d878..c27c282eb2a19 100644 --- a/.github/workflows/pr-code-format.yml +++ b/.github/workflows/pr-code-format.yml @@ -40,9 +40,11 @@ jobs: 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 diff --git a/.github/workflows/pr-subscriber.yml b/.github/workflows/pr-subscriber.yml index ef2ef7b9e4a35..99f9be771588d 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 e52e52f5d3f36..75d4f419ab1fd 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 270f58f777ce8..b8e8ab26c3ffa 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 c328a9133e179..0000000000000 --- 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 4c787a4560399..867af2c55f3bc 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/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index a4e84cb93c093..c7672285b06c0 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -611,6 +611,14 @@ class BinaryContext { /// Indicates if the binary contains split functions. bool HasSplitFunctions{false}; + /// Indicates if the function ordering of the binary is finalized. + bool HasFinalizedFunctionOrder{false}; + + /// Indicates if a separate .text.warm section is needed that contains + /// function fragments with + /// FunctionFragment::getFragmentNum() == FragmentNum::warm() + bool HasWarmSection{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. @@ -927,6 +935,8 @@ class BinaryContext { const char *getMainCodeSectionName() const { return ".text"; } + const char *getWarmCodeSectionName() const { return ".text.warm"; } + const char *getColdCodeSectionName() const { return ".text.cold"; } const char *getHotTextMoverSectionName() const { return ".text.mover"; } @@ -1230,6 +1240,9 @@ class BinaryContext { /// /// 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); @@ -1290,6 +1303,9 @@ class BinaryContext { /// 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 63c0b118db657..182d5ff049c37 100644 --- a/bolt/include/bolt/Core/BinaryFunction.h +++ b/bolt/include/bolt/Core/BinaryFunction.h @@ -319,10 +319,6 @@ class BinaryFunction { /// 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 @@ -1240,6 +1236,8 @@ class BinaryFunction { return SmallString<32>(CodeSectionName); if (Fragment == FragmentNum::cold()) return SmallString<32>(ColdCodeSectionName); + if (BC.HasWarmSection && Fragment == FragmentNum::warm()) + return SmallString<32>(BC.getWarmCodeSectionName()); return formatv("{0}.{1}", ColdCodeSectionName, Fragment.get() - 1); } diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h index fb86e59468b83..1c5252142d4eb 100644 --- a/bolt/include/bolt/Core/DIEBuilder.h +++ b/bolt/include/bolt/Core/DIEBuilder.h @@ -273,7 +273,8 @@ class DIEBuilder { 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/FunctionLayout.h b/bolt/include/bolt/Core/FunctionLayout.h index 904da3a4a93aa..2e4c184ba4511 100644 --- a/bolt/include/bolt/Core/FunctionLayout.h +++ b/bolt/include/bolt/Core/FunctionLayout.h @@ -63,6 +63,7 @@ class FragmentNum { static constexpr FragmentNum main() { return FragmentNum(0); } static constexpr FragmentNum cold() { return FragmentNum(1); } + static constexpr FragmentNum warm() { return FragmentNum(2); } }; /// A freestanding subset of contiguous blocks of a function. diff --git a/bolt/include/bolt/Passes/SplitFunctions.h b/bolt/include/bolt/Passes/SplitFunctions.h index 4058f3317dfbd..28e9e79d1b8f8 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 @@ class SplitStrategy { 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/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h index 5d16812441ee7..495a3f1d7b7b4 100644 --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -400,12 +400,6 @@ class RewriteInstance { /// 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[]; @@ -550,7 +544,6 @@ class RewriteInstance { } /// 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 fd4d09964b725..7b78c9ba30a30 100644 --- a/bolt/lib/Core/BinaryContext.cpp +++ b/bolt/lib/Core/BinaryContext.cpp @@ -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) { @@ -1926,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); } } @@ -2305,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 67d139d7eb5a6..3bff3125a57a8 100644 --- a/bolt/lib/Core/BinaryEmitter.cpp +++ b/bolt/lib/Core/BinaryEmitter.cpp @@ -287,7 +287,10 @@ void BinaryEmitter::emitFunctions() { // Mark the end of hot text. if (opts::HotText) { - Streamer.switchSection(BC.getTextSection()); + if (BC.HasWarmSection) + Streamer.switchSection(BC.getCodeSection(BC.getWarmCodeSectionName())); + else + Streamer.switchSection(BC.getTextSection()); Streamer.emitLabel(BC.getHotTextEndSymbol()); } } @@ -507,6 +510,18 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, Streamer.emitLabel(InstrLabel); } + // 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); } diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index 9bbdd0deaff2d..76bac2f624419 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"), @@ -431,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()) @@ -513,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'; @@ -1117,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); @@ -1893,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. @@ -3289,7 +3296,7 @@ void BinaryFunction::fixBranches() { BB->eraseInstruction(BB->findInstruction(UncondBranch)); // Basic block that follows the current one in the final layout. - const BinaryBasicBlock *NextBB = + const BinaryBasicBlock *const NextBB = Layout.getBasicBlockAfter(BB, /*IgnoreSplits=*/false); if (BB->succ_size() == 1) { @@ -3306,39 +3313,54 @@ void BinaryFunction::fixBranches() { assert(CondBranch && "conditional branch expected"); const BinaryBasicBlock *TSuccessor = BB->getConditionalSuccessor(true); const BinaryBasicBlock *FSuccessor = BB->getConditionalSuccessor(false); - // Check whether we support reversing this branch direction - const bool IsSupported = !MIB->isUnsupportedBranch(*CondBranch); - if (NextBB && NextBB == TSuccessor && IsSupported) { + + // Eliminate unnecessary conditional branch. + if (TSuccessor == FSuccessor) { + BB->removeDuplicateConditionalSuccessor(CondBranch); + if (TSuccessor != NextBB) + BB->addBranchInstruction(TSuccessor); + continue; + } + + // Reverse branch condition and swap successors. + auto swapSuccessors = [&]() { + if (MIB->isUnsupportedBranch(*CondBranch)) + return false; std::swap(TSuccessor, FSuccessor); - { - auto L = BC.scopeLock(); - MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), Ctx); - } BB->swapConditionalSuccessors(); - } else { + auto L = BC.scopeLock(); + MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), Ctx); + return true; + }; + + // Check whether the next block is a "taken" target and try to swap it + // with a "fall-through" target. + if (TSuccessor == NextBB && swapSuccessors()) + continue; + + // Update conditional branch destination if needed. + if (MIB->getTargetSymbol(*CondBranch) != TSuccessor->getLabel()) { auto L = BC.scopeLock(); MIB->replaceBranchTarget(*CondBranch, TSuccessor->getLabel(), Ctx); } - if (TSuccessor == FSuccessor) - BB->removeDuplicateConditionalSuccessor(CondBranch); - if (!NextBB || - ((NextBB != TSuccessor || !IsSupported) && NextBB != FSuccessor)) { - // If one of the branches is guaranteed to be "long" while the other - // could be "short", then prioritize short for "taken". This will - // generate a sequence 1 byte shorter on x86. - if (IsSupported && BC.isX86() && - TSuccessor->getFragmentNum() != FSuccessor->getFragmentNum() && - BB->getFragmentNum() != TSuccessor->getFragmentNum()) { - std::swap(TSuccessor, FSuccessor); - { - auto L = BC.scopeLock(); - MIB->reverseBranchCondition(*CondBranch, TSuccessor->getLabel(), - Ctx); - } - BB->swapConditionalSuccessors(); - } - BB->addBranchInstruction(FSuccessor); + + // No need for the unconditional branch. + if (FSuccessor == NextBB) + continue; + + if (BC.isX86()) { + // We are going to generate two branches. Check if their targets are in + // the same fragment as this block. If only one target is in the same + // fragment, make it the destination of the conditional branch. There + // is a chance it will be a short branch which takes 5 bytes fewer than + // a long conditional branch. For unconditional branch, the difference + // is 4 bytes. + if (BB->getFragmentNum() != TSuccessor->getFragmentNum() && + BB->getFragmentNum() == FSuccessor->getFragmentNum()) + swapSuccessors(); } + + BB->addBranchInstruction(FSuccessor); } // Cases where the number of successors is 0 (block ends with a // terminator) or more than 2 (switch table) don't require branch @@ -3630,7 +3652,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( @@ -4137,10 +4159,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; @@ -4188,6 +4206,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 { diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp index 223ae714440d9..b809b2935ee9e 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/Exceptions.cpp b/bolt/lib/Core/Exceptions.cpp index 657c3137ae9d6..993f3a7770aa8 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) diff --git a/bolt/lib/Core/HashUtilities.cpp b/bolt/lib/Core/HashUtilities.cpp index 6c7570dcc44e8..d40159b2e216d 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/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index 83c7138e5fe5c..4e1343e2c30be 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -608,12 +608,15 @@ void LowerAnnotations::runOnFunctions(BinaryContext &BC) { 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); } diff --git a/bolt/lib/Passes/ReorderFunctions.cpp b/bolt/lib/Passes/ReorderFunctions.cpp index 92c50f49d20cf..7e67296909813 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/SplitFunctions.cpp b/bolt/lib/Passes/SplitFunctions.cpp index 34973cecdf491..79da4cc3b04ba 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,50 @@ 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 zero or not. + for (size_t Index = 0; Index < BlockOrder.size(); Index++) { + BinaryBasicBlock *BB = BlockOrder[Index]; + if (Index <= BestSplitIndex) + BB->setFragmentNum(FragmentNum::main()); + else + BB->setFragmentNum(BB->getKnownExecutionCount() > 0 + ? FragmentNum::warm() + : FragmentNum::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 +190,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 +217,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 +264,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 +293,27 @@ 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; + BC.HasWarmSection = 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/ValidateInternalCalls.cpp b/bolt/lib/Passes/ValidateInternalCalls.cpp index 22dadf4f6403b..516f91acb5084 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/StaleProfileMatching.cpp b/bolt/lib/Profile/StaleProfileMatching.cpp index d00bf87ffc8ad..6fb6f380f71ee 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 06cad69754816..079cb352d36e7 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 cd27c71ba2aed..9946608c96d8e 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/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index 360b82f45bd77..0beb865bd48cb 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -481,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. @@ -498,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()) @@ -707,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)); @@ -963,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()); diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index ce3402c5827ae..8cda0b7fcca9f 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -1784,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); @@ -1829,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"); @@ -3200,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()); + } } } @@ -3480,11 +3478,21 @@ std::vector RewriteInstance::getCodeSections() { if (B->getName() == BC->getHotTextMoverSectionName()) return false; - // Depending on the option, put main text at the beginning or at the end. - if (opts::HotFunctionsAtEnd) - return B->getName() == BC->getMainCodeSectionName(); - else - return A->getName() == BC->getMainCodeSectionName(); + // Depending on opts::HotFunctionsAtEnd, place main and warm sections in + // order. + if (opts::HotFunctionsAtEnd) { + if (B->getName() == BC->getMainCodeSectionName()) + return true; + if (A->getName() == BC->getMainCodeSectionName()) + return false; + return (B->getName() == BC->getWarmCodeSectionName()); + } else { + if (A->getName() == BC->getMainCodeSectionName()) + return true; + if (B->getName() == BC->getMainCodeSectionName()) + return false; + return (A->getName() == BC->getWarmCodeSectionName()); + } }; // Determine the order of sections. @@ -3526,6 +3534,9 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) { // Allocate sections starting at a given Address. auto allocateAt = [&](uint64_t Address) { + const char *LastNonColdSectionName = BC->HasWarmSection + ? BC->getWarmCodeSectionName() + : BC->getMainCodeSectionName(); for (BinarySection *Section : CodeSections) { Address = alignTo(Address, Section->getAlignment()); Section->setOutputAddress(Address); @@ -3534,13 +3545,13 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) { // Hugify: Additional huge page from right side due to // weird ASLR mapping addresses (4KB aligned) if (opts::Hugify && !BC->HasFixedLoadAddress && - Section->getName() == BC->getMainCodeSectionName()) + Section->getName() == LastNonColdSectionName) Address = alignTo(Address, Section->getAlignment()); } // Make sure we allocate enough space for huge pages. ErrorOr TextSection = - BC->getUniqueSectionByName(BC->getMainCodeSectionName()); + BC->getUniqueSectionByName(LastNonColdSectionName); if (opts::HotText && TextSection && TextSection->hasValidSectionID()) { uint64_t HotTextEnd = TextSection->getOutputAddress() + TextSection->getOutputSize(); @@ -4398,6 +4409,21 @@ void RewriteInstance::updateELFSymbolTable( return NewIndex; }; + // Get the extra symbol name of a split fragment; used in addExtraSymbols. + auto getSplitSymbolName = [&](const FunctionFragment &FF, + const ELFSymTy &FunctionSymbol) { + SmallString<256> SymbolName; + if (BC->HasWarmSection) + SymbolName = + formatv("{0}.{1}", cantFail(FunctionSymbol.getName(StringSection)), + FF.getFragmentNum() == FragmentNum::warm() ? "warm" : "cold"); + else + SymbolName = formatv("{0}.cold.{1}", + cantFail(FunctionSymbol.getName(StringSection)), + FF.getFragmentNum().get() - 1); + return SymbolName; + }; + // Add extra symbols for the function. // // Note that addExtraSymbols() could be called multiple times for the same @@ -4425,9 +4451,8 @@ void RewriteInstance::updateELFSymbolTable( Function.getLayout().getSplitFragments()) { if (FF.getAddress()) { ELFSymTy NewColdSym = FunctionSymbol; - const SmallString<256> SymbolName = formatv( - "{0}.cold.{1}", cantFail(FunctionSymbol.getName(StringSection)), - FF.getFragmentNum().get() - 1); + const SmallString<256> SymbolName = + getSplitSymbolName(FF, FunctionSymbol); NewColdSym.st_name = AddToStrTab(SymbolName); NewColdSym.st_shndx = Function.getCodeSection(FF.getFragmentNum())->getIndex(); diff --git a/bolt/test/AArch64/got-ld64-relaxation.test b/bolt/test/AArch64/got-ld64-relaxation.test index 01c17cb01a65a..ec6042a2ca826 100644 --- a/bolt/test/AArch64/got-ld64-relaxation.test +++ b/bolt/test/AArch64/got-ld64-relaxation.test @@ -2,7 +2,7 @@ // 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/skip-got-rel.test b/bolt/test/AArch64/skip-got-rel.test index 3c36df00b1c82..eaa6ec4b78d74 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/Inputs/lsda.ldscript b/bolt/test/Inputs/lsda.ldscript new file mode 100644 index 0000000000000..aa608ecd97e8c --- /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 bf8191f25744c..f83ca51579156 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 477c4693cd6dd..1a8ac1571f0be 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 5a9785571c808..052c5a3879a44 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 5b728f092b3c9..956e0410ece42 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 5995562cf130b..450c763944529 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 43991149af8c8..6a8b5a28e19d9 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 8c7f5498c2925..37cfd993b95d2 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 dcf9d0ea3ffbf..e7e85fddfb1cb 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 427e8f230d89a..ce54265fac05e 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 a97a1454eee86..c882df0be61d9 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 3ad3015a0a57f..c7e41747b4db9 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 34221c167d865..87c16d8023faf 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 21b10bab09c7c..2aae9c3c8e8e5 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 44195818277f5..a4c7bd06de907 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 f5abaed3da394..43b75c99656f1 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-df-types-dup-helper.s b/bolt/test/X86/Inputs/dwarf5-df-types-dup-helper.s new file mode 100644 index 0000000000000..cf0bdad3bbda1 --- /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 0000000000000..d991094cf0a08 --- /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/block-reordering.test b/bolt/test/X86/block-reordering.test index a18a2109d37e2..f3a3390e27cb9 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 5d6ff92d18e28..0c64caaee8a50 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 540b6790d01e9..c88e4d0043b46 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 0000000000000..b1d05b83cb87c --- /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 3644bf5d962d0..3d6308d9e6f83 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 adf28a9e0811f..635d560ae7533 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 afa44111ead49..89aa4f78d52d1 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 256682655d6d3..00b5bf83e25db 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-rel.s b/bolt/test/X86/double-rel.s index 7de842d9dd318..f7754f77e782b 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/dwarf4-deleted-range.s b/bolt/test/X86/dwarf4-deleted-range.s new file mode 100644 index 0000000000000..7938bc001c007 --- /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-deleted-range.s b/bolt/test/X86/dwarf5-deleted-range.s new file mode 100644 index 0000000000000..241c694ce89b3 --- /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-types-dup-dwp-input.test b/bolt/test/X86/dwarf5-df-types-dup-dwp-input.test new file mode 100644 index 0000000000000..036d4c9168ee5 --- /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/exceptions-args.test b/bolt/test/X86/exceptions-args.test index fe9423896f68d..3a4fa2f0eac13 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/fptr-addend-pcrel.s b/bolt/test/X86/fptr-addend-pcrel.s index 0e84a8fd36004..054d177ed55fe 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 090a433143156..ddac5ebda0caf 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 318318f96215c..3c863833449fa 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 e2565ff889327..bbc11e7d33171 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 dca7b03a61380..df12986efc42d 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 b512001c3c446..cc6107f478127 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 5a007c626eca8..4e0f9b16818d3 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-pic-order.test b/bolt/test/X86/jump-table-pic-order.test index 0206e831f2441..59c0af252b07b 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 6a019b26cf63d..9d33c0d5e7271 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 0000000000000..37da2ff07b9b7 --- /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 bec0ef01c85f6..3d24e1aad139a 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 9c9d98df2f1db..b039962643d40 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 32d2e0ae40dbe..92aad7d5ec62c 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 8f2e2c41a4c34..a2638e1388c9a 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 720d179d1c6b8..e8c3f64239a27 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 4d5c2b527188f..ad8d475c83724 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 5231032f4f4a7..ad0945b149340 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 cc0bfe43511d3..308b97e30bb56 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 a4568aca14144..1b581df237490 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 cf23b38249ace..0e235564dc3bd 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 fce69bf86a466..69c8c45428444 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 b449be1b33892..00f5ee429b635 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 cf1723c2563ae..545cf37263da5 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/tailcall.test b/bolt/test/X86/tailcall.test index 09a31a6016078..83b69bd25ab92 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 bfbc23efb9dcd..5df4f3da04214 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 d94f8dec02f14..5684da4226be6 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 6c1d8cc58dad0..fadc5590ea86f 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 0000000000000..41fb176658219 --- /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 9e22705081e6b..34993eb330cbd 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 0000000000000..6c7044cc6b67f --- /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 0000000000000..66d925a05bebc --- /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 0000000000000..05f41ff333b0e --- /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 0000000000000..44db899ab30de --- /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 e8aaa91b7de7d..0ce2576ee401c 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 38f3cf9131aa0..2c436d708df82 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/X86/asm-dump.c b/bolt/test/runtime/X86/asm-dump.c index fdd448e0c408d..e5383b5235159 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 0000000000000..b487ff0fa2f59 --- /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/yaml-profile-kind.c b/bolt/test/yaml-profile-kind.c index b7983c672c748..3bf2a2619f01f 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 64af859128c76..7ac1c14357596 100644 --- a/bolt/unittests/Core/BinaryContext.cpp +++ b/bolt/unittests/Core/BinaryContext.cpp @@ -123,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 index 694ffd88fe577..72a21e0e24a1a 100755 --- a/bolt/utils/nfc-stat-parser.py +++ b/bolt/utils/nfc-stat-parser.py @@ -19,7 +19,7 @@ def main(): ) parser.add_argument( "--check_longer_than", - default=0.5, + default=1, type=float, help="Only warn on tests longer than X seconds for at least one side", ) diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp index b36144d1912fc..221e924c10f62 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 07a987359d4d8..43bedd4f73ef4 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/misc/CoroutineHostileRAIICheck.cpp b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp index e820cd39d83d2..a0e8700b0522b 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 a5e9cb89ef676..be925097692a4 100644 --- a/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.h +++ b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.h @@ -43,6 +43,9 @@ class CoroutineHostileRAIICheck : public ClangTidyCheck { // 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/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp index 066057fa7208d..18c5e144e46fe 100644 --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -872,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])*$"), }; diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp index 69e6d73c4fcd7..f0fca30de3b3c 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/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index f49c412118e7d..6d5f49dc06254 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -208,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 @@ -315,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 @@ -405,12 +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. + 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 8224c37a087b8..c82c38772a5c9 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 5d125a89e7f43..789235980ad7b 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 ffef01ca5d4eb..ccc2a7b34efe5 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 381fb365e800e..cfc6e9b6ec347 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 b8698ba3de853..a39c1853b313c 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/include-cleaner/lib/Record.cpp b/clang-tools-extra/include-cleaner/lib/Record.cpp index 7a8e10a9c6754..6e00ff93a7fe2 100644 --- a/clang-tools-extra/include-cleaner/lib/Record.cpp +++ b/clang-tools-extra/include-cleaner/lib/Record.cpp @@ -240,20 +240,10 @@ class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler { // 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/unittests/RecordTest.cpp b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp index 36850731d5145..0f2ded5f18345 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/test/clang-tidy/checkers/Inputs/Headers/string b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string index 6f569655e6762..d0aac7b78ec93 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 e51568077eda1..417598790bc00 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 6c41e1e320ade..c11638aa823aa 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/suspicious-enum-usage-strict.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp index 405dec22eea77..ec214945539eb 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage-strict.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: true}}" -- +// RUN: %check_clang_tidy -std=c++17 %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: true}}" -- enum A { A = 1, @@ -71,7 +71,7 @@ int trigger() { unsigned p = R; PP pp = Q; p |= pp; - + enum X x = Z; p = x | Z; return 0; diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp index 257c82e88f9a3..de7eb989bd2d4 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/suspicious-enum-usage.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: false}}" -- +// RUN: %check_clang_tidy -std=c++17 %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: {bugprone-suspicious-enum-usage.StrictMode: false}}" enum Empty { }; @@ -79,7 +79,7 @@ int dont_trigger() { int d = c | H, e = b * a; a = B | C; b = X | Z; - + if (Tuesday != Monday + 1 || Friday - Thursday != 1 || Sunday + Wednesday == (Sunday | Wednesday)) 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 7b5ccabdd6ef6..794578ceeeba8 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 2d022e21c85d5..55a7e4b8f2954 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/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 0000000000000..f692b01923455 --- /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 e375aa098972b..d2e89a7c9855c 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 @@ -423,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 323cf813c0470..f7f5d506a9ce0 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/docs/AutomaticReferenceCounting.rst b/clang/docs/AutomaticReferenceCounting.rst index 820ad3978d5f2..bcac73215c9d3 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 ff424828ff63c..37d76d5c2dd58 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2046,6 +2046,21 @@ 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:`¶ ` diff --git a/clang/docs/ClangOffloadBundler.rst b/clang/docs/ClangOffloadBundler.rst index 1e21d3e7264d5..1d163db1a9a63 100644 --- a/clang/docs/ClangOffloadBundler.rst +++ b/clang/docs/ClangOffloadBundler.rst @@ -30,58 +30,139 @@ includes an ``init`` function that will use the runtime corresponding to the offload kind (see :ref:`clang-offload-kind-table`) to load the offload code objects appropriate to the devices present when the host program is executed. +:program:`clang-offload-bundler` is located in +`clang/tools/clang-offload-bundler`. + +.. code-block:: console + + $ clang-offload-bundler -help + OVERVIEW: A tool to bundle several input files of the specified type + referring to the same source file but different targets into a single + one. The resulting file can also be unbundled into different files by + this tool if -unbundle is provided. + + USAGE: clang-offload-bundler [options] + + OPTIONS: + + Generic Options: + + --help - Display available options (--help-hidden for more) + --help-list - Display list of available options (--help-list-hidden for more) + --version - Display the version of this program + + clang-offload-bundler options: + + --### - Print any external commands that are to be executed instead of actually executing them - for testing purposes. + --allow-missing-bundles - Create empty files if bundles are missing when unbundling. + --bundle-align= - Alignment of bundle for binary files + --check-input-archive - Check if input heterogeneous archive is valid in terms of TargetID rules. + --inputs= - [,...] + --list - List bundle IDs in the bundled file. + --outputs= - [,...] + --targets= - [-,...] + --type= - Type of the files to be bundled/unbundled. + Current supported types are: + i - cpp-output + ii - c++-cpp-output + cui - cuda/hip-output + d - dependency + ll - llvm + bc - llvm-bc + s - assembler + o - object + a - archive of bundled files + gch - precompiled-header + ast - clang AST file + --unbundle - Unbundle bundled file into several output files. + +Usage +===== + +This tool can be used as follows for bundling: + +:: + + clang-offload-bundler -targets=triple1,triple2 -type=ii -inputs=a.triple1.ii,a.triple2.ii -outputs=a.ii + +or, it can be used as follows for unbundling: + +:: + + clang-offload-bundler -targets=triple1,triple2 -type=ii -outputs=a.triple1.ii,a.triple2.ii -inputs=a.ii -unbundle + + Supported File Formats ====================== -Several text and binary file formats are supported for bundling/unbundling. See -:ref:`supported-file-formats-table` for a list of currently supported formats. + +Multiple text and binary file formats are supported for bundling/unbundling. See +:ref:`supported-file-formats-table` for a list of currently supported input +formats. Use the ``File Type`` column to determine the value to pass to the +``--type`` option based on the type of input files while bundling/unbundling. .. table:: Supported File Formats :name: supported-file-formats-table - +--------------------+----------------+-------------+ - | File Format | File Extension | Text/Binary | - +====================+================+=============+ - | CPP output | i | Text | - +--------------------+----------------+-------------+ - | C++ CPP output | ii | Text | - +--------------------+----------------+-------------+ - | CUDA/HIP output | cui | Text | - +--------------------+----------------+-------------+ - | Dependency | d | Text | - +--------------------+----------------+-------------+ - | LLVM | ll | Text | - +--------------------+----------------+-------------+ - | LLVM Bitcode | bc | Binary | - +--------------------+----------------+-------------+ - | Assembler | s | Text | - +--------------------+----------------+-------------+ - | Object | o | Binary | - +--------------------+----------------+-------------+ - | Archive of objects | a | Binary | - +--------------------+----------------+-------------+ - | Precompiled header | gch | Binary | - +--------------------+----------------+-------------+ - | Clang AST file | ast | Binary | - +--------------------+----------------+-------------+ + +--------------------------+----------------+-------------+ + | File Format | File Type | Text/Binary | + +==========================+================+=============+ + | CPP output | i | Text | + +--------------------------+----------------+-------------+ + | C++ CPP output | ii | Text | + +--------------------------+----------------+-------------+ + | CUDA/HIP output | cui | Text | + +--------------------------+----------------+-------------+ + | Dependency | d | Text | + +--------------------------+----------------+-------------+ + | LLVM | ll | Text | + +--------------------------+----------------+-------------+ + | LLVM Bitcode | bc | Binary | + +--------------------------+----------------+-------------+ + | Assembler | s | Text | + +--------------------------+----------------+-------------+ + | Object | o | Binary | + +--------------------------+----------------+-------------+ + | Archive of bundled files | a | Binary | + +--------------------------+----------------+-------------+ + | Precompiled header | gch | Binary | + +--------------------------+----------------+-------------+ + | Clang AST file | ast | Binary | + +--------------------------+----------------+-------------+ .. _clang-bundled-code-object-layout-text: Bundled Text File Layout ======================== -The format of the bundled files is currently very simple: text formats are -concatenated with comments that have a magic string and bundle entry ID in -between. +The text file formats are concatenated with comments that have a magic string +and bundle entry ID in between. The BNF syntax to represent a code object +bundle file is: :: - "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ 1st Bundle Entry ID" - Bundle 1 - "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ 1st Bundle Entry ID" - ... - "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ Nth Bundle Entry ID" - Bundle N - "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ 1st Bundle Entry ID" + ::== | + ::== + end + ::== OFFLOAD_BUNDLER_MAGIC_STR__START__ + ::== OFFLOAD_BUNDLER_MAGIC_STR__END__ + +**comment** + The symbol used for starting single-line comment in the file type of + constituting bundles. E.g. it is ";" for ll ``File Type`` and "#" for "s" + ``File Type``. + +**bundle_id** + The :ref:`clang-bundle-entry-id` for the enclosing bundle. + +**eol** + The end of line character. + +**bundle** + The code object stored in one of the supported text file formats. + +**OFFLOAD_BUNDLER_MAGIC_STR__** + Magic string that marks the existence of offloading data i.e. + "__CLANG_OFFLOAD_BUNDLE__". .. _clang-bundled-code-object-layout: @@ -126,8 +207,8 @@ The layout of a bundled code object is defined by the following table: Bundle Entry ID =============== -Each entry in a bundled code object (see -:ref:`clang-bundled-code-object-layout`) has a bundle entry ID that indicates +Each entry in a bundled code object (see :ref:`clang-bundled-code-object-layout-text` +and :ref:`clang-bundled-code-object-layout`) has a bundle entry ID that indicates the kind of the entry's code object and the runtime that manages it. Bundle entry ID syntax is defined by the following BNF syntax: @@ -193,11 +274,30 @@ Where: The canonical target ID of the code object. Present only if the target supports a target ID. See :ref:`clang-target-id`. -Each entry of a bundled code object must have a different bundle entry ID. There -can be multiple entries for the same processor provided they differ in target -feature settings. If there is an entry with a target feature specified as *Any*, -then all entries must specify that target feature as *Any* for the same -processor. There may be additional target specific restrictions. +.. _code-object-composition: + +Bundled Code Object Composition +------------------------------- + + * Each entry of a bundled code object must have a different bundle entry ID. + * There can be multiple entries for the same processor provided they differ + in target feature settings. + * If there is an entry with a target feature specified as *Any*, then all + entries must specify that target feature as *Any* for the same processor. + +There may be additional target specific restrictions. + +.. _compatibility-bundle-entry-id: + +Compatibility Rules for Bundle Entry ID +--------------------------------------- + + A code object, specified using its Bundle Entry ID, can be loaded and + executed on a target processor, if: + + * Their offload kinds are the same. + * Their target triples are compatible. + * Their Target IDs are compatible as defined in :ref:`compatibility-target-id`. .. _clang-target-id: @@ -247,6 +347,17 @@ Where: object compiled with a target ID specifying a target feature off can only be loaded on a processor configured with the target feature off. +.. _compatibility-target-id: + +Compatibility Rules for Target ID +--------------------------------- + + A code object compiled for a Target ID is considered compatible for a + target, if: + + * Their processor is same. + * Their feature set is compatible as defined above. + There are two forms of target ID: *Non-Canonical Form* @@ -279,14 +390,14 @@ Most other targets do not support target IDs. Archive Unbundling ================== -Unbundling of heterogeneous device archive is done to create device specific -archives. Heterogeneous Device Archive is in a format compatible with GNU ar -utility and contains a collection of bundled device binaries where each bundle -file will contain device binaries for a host and one or more targets. The -output device specific archive is in a format compatible with GNU ar utility -and contains a collection of device binaries for a specific target. +Unbundling of a heterogeneous device archive (HDA) is done to create device specific +archives. HDA is in a format compatible with GNU ``ar`` utility and contains a +collection of bundled device binaries where each bundle file will contain +device binaries for a host and one or more targets. The output device-specific +archive is in a format compatible with GNU ``ar`` utility and contains a +collection of device binaries for a specific target. -.. code:: +:: Heterogeneous Device Archive, HDA = {F1.X, F2.X, ..., FN.Y} where, Fi = Bundle{Host-DeviceBinary, T1-DeviceBinary, T2-DeviceBinary, ..., @@ -299,16 +410,101 @@ and contains a collection of device binaries for a specific target. where, Fi-Tj-DeviceBinary.X represents device binary of i-th bundled device binary file for target Tj. -clang-offload-bundler extracts compatible device binaries for a given target +The clang-offload-bundler extracts compatible device binaries for a given target from the bundled device binaries in a heterogeneous device archive and creates -a target specific device archive without bundling. +a target-specific device archive without bundling. + +The clang-offload-bundler determines whether a device binary is compatible +with a target by comparing bundle IDs. Two bundle IDs are considered +compatible if: + + * Their offload kinds are the same + * Their target triples are the same + * Their Target IDs are the same + +Creating a Heterogeneous Device Archive +--------------------------------------- + +1. Compile source file(s) to generate object file(s) + + :: + + clang -O2 -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa,amdgcn-amd-amdhsa,\ + nvptx64-nvidia-cuda, nvptx64-nvidia-cuda \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc-:xnack+ \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc+:xnack+ \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_70 \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_80 \ + -c func_1.c -o func_1.o + + clang -O2 -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa,amdgcn-amd-amdhsa, + nvptx64-nvidia-cuda, nvptx64-nvidia-cuda \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc-:xnack+ \ + -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx906:sramecc+:xnack+ \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_70 \ + -Xopenmp-target=nvptx64-nvidia-cuda -march=sm_80 \ + -c func_2.c -o func_2.o + +2. Create a heterogeneous device archive by combining all the object file(s) + + :: + + llvm-ar cr libFatArchive.a func_1.o func_2.o + +Extracting a Device Specific Archive +------------------------------------ + +UnbundleArchive takes a heterogeneous device archive file (".a") as input +containing bundled device binary files, and a list of offload targets (not +host), and extracts the device binaries into a new archive file for each +offload target. Each resulting archive file contains all device binaries +compatible with that particular offload target. Compatibility between a +device binary in HDA and a target is based on the compatibility between their +bundle entry IDs as defined in :ref:`compatibility-bundle-entry-id`. + +Following cases may arise during compatibility testing: + +* A binary is compatible with one or more targets: Insert the binary into the + device-specific archive of each compatible target. +* A binary is not compatible with any target: Skip the binary. +* One or more binaries are compatible with a target: Insert all binaries into + the device-specific archive of the target. The insertion need not be ordered. +* No binary is compatible with a target: If ``allow-missing-bundles`` option is + present then create an empty archive for the target. Otherwise, produce an + error without creating an archive. + +The created archive file does not contain an index of the symbols and device +binary files are named as <->, +with ':' replaced with '_'. + +Usage +----- + +:: + + clang-offload-bundler --unbundle --inputs=libFatArchive.a -type=a \ + -targets=openmp-amdgcn-amdhsa-gfx906:sramecc+:xnack+, \ + openmp-amdgcn-amdhsa-gfx908:sramecc-:xnack+ \ + -outputs=devicelib-gfx906.a,deviceLib-gfx908.a + +.. _additional-options-archive-unbundling: + +Additional Options while Archive Unbundling +------------------------------------------- + +**-allow-missing-bundles** + Create an empty archive file if no compatible device binary is found. + +**-check-input-archive** + Check if input heterogeneous device archive follows rules for composition + as defined in :ref:`code-object-composition` before creating device-specific + archive(s). -clang-offload-bundler determines whether a device binary is compatible with a -target by comparing bundle ID's. Two bundle ID's are considered compatible if: - * Their offload kind are the same - * Their target triple are the same - * Their GPUArch are the same +**-debug-only=CodeObjectCompatibility** + Verbose printing of matched/unmatched comparisons between bundle entry id of + a device binary from HDA and bundle entry ID of a given target processor + (see :ref:`compatibility-bundle-entry-id`). Compression and Decompression ============================= diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index b7d88d3d67d0a..f8e3da5f97368 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 294210c6ac140..8e01ef6cbb399 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4629,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 ed1a978b5382d..748e2db2f8507 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 ------------------------------------------------- @@ -219,6 +227,10 @@ Non-comprehensive list of changes in this release 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 ------------------ @@ -239,6 +251,8 @@ New Compiler Flags 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 ------------------------- @@ -302,6 +316,32 @@ Attribute Changes in Clang 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 @@ -440,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 ---------------------------------- @@ -555,8 +615,39 @@ 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. +- Fixed false positive error emitted by clang when performing qualified name + lookup and the current class instantiation has dependent bases. + Fixes (`#13826 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -681,6 +772,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 `_) @@ -697,6 +793,14 @@ Bug Fixes to C++ Support completes (except deduction guides). Fixes: (`#59827 `_) +- Fix crash when parsing nested requirement. Fixes: + (`#73112 `_) + +- Clang now immediately instantiates function template specializations + at the end of the definition of the corresponding function template + when the definition appears after the first point of instantiation. + (`#73232 `_) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. @@ -729,6 +833,17 @@ Miscellaneous Clang Crashes Fixed - 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 ----------------------- @@ -760,6 +875,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 ^^^^^^^^^^^^^^^ @@ -856,11 +977,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 --------------- @@ -868,6 +992,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/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 43137f4b020f9..f7b48e64e324f 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 48cd800bffd00..18512b1a7bf6b 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/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h new file mode 100644 index 0000000000000..18375c9e51a17 --- /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/Types.h b/clang/include/clang/APINotes/Types.h index 79595abcf7d02..1d116becf06c8 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 { diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 549f12e87df59..924ca189381ba 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -2513,6 +2513,89 @@ class OMPRelaxedClause final : public OMPClause { } }; +/// 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 @@ class OMPOrderClause final : public OMPClause { /// \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/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 53bc15e1b19f6..c501801b95bd9 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/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 963197b728f42..7c1f549109632 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -544,9 +544,6 @@ class Environment { /// 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 @@ class Environment { /// (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/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 31434565becae..1800f584c7e10 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1094,6 +1094,30 @@ def CoroOnlyDestroyWhenComplete : InheritableAttr { 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">]; @@ -4229,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]>; @@ -4297,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 fa6f6acd0c30e..f2c4eb51b443d 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 @@ -7482,3 +7502,160 @@ generation of the other destruction cases, optimizing the above `foo.destroy` to }]; } + +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 3140d1a838afc..018b92fdc11f5 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -177,6 +177,7 @@ class AttributeCommonInfo { 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; } diff --git a/clang/include/clang/Basic/Cuda.h b/clang/include/clang/Basic/Cuda.h index 878f8d70f90c0..2d912bdbbd1bc 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/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 906ca6b8e47c5..65a33f61a6948 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -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 37559c7ff7724..ff028bbbf7426 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/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index de180344fcc5c..21fe6066d5876 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 4614324babb1c..6dfb2d7195203 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3148,6 +3148,9 @@ def err_attribute_bad_sve_vector_size : Error< def err_attribute_arm_feature_sve_bits_unsupported : Error< "%0 is only supported when '-msve-vector-bits=' is specified with a " "value of 128, 256, 512, 1024 or 2048.">; +def warn_attribute_arm_sm_incompat_builtin : Warning< + "builtin call has undefined behaviour when called from a %0 function">, + InGroup>; def err_sve_vector_in_non_sve_target : Error< "SVE vector type %0 cannot be used in a target without sve">; def err_attribute_riscv_rvv_bits_unsupported : Error< @@ -3177,6 +3180,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< @@ -5655,9 +5662,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 +6419,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 +6762,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 +7223,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 +7236,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; @@ -10025,6 +10041,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|" @@ -10962,6 +10983,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< @@ -11591,6 +11614,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 { @@ -11989,6 +12016,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">; @@ -12004,6 +12032,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/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index c541ccefdd5fb..c3d5399905a3f 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -285,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") @@ -349,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") @@ -402,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 ae99357eeea7f..2d167dd2bdf12 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -502,6 +502,11 @@ class LangOptions : public LangOptionsBase { // 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 diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index 08b153e8c1c9d..d29cc0b45d583 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -178,6 +178,9 @@ class alignas(8) Module { /// eventually be exposed, for use in "private" modules. std::string ExportAsModule; + /// 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) { diff --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h new file mode 100644 index 0000000000000..1a5bf7e0e831c --- /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 272537b769742..f46a92d5ecfd4 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 ac1b3cdfff145..d127498774c7f 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/PragmaKinds.h b/clang/include/clang/Basic/PragmaKinds.h index 176bbc9ac7caa..42f049f7323d2 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 af44cdcd53e5b..6620de8ad50e0 100644 --- a/clang/include/clang/Basic/RISCVVTypes.def +++ b/clang/include/clang/Basic/RISCVVTypes.def @@ -452,6 +452,62 @@ 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 diff --git a/clang/include/clang/Basic/SourceMgrAdapter.h b/clang/include/clang/Basic/SourceMgrAdapter.h new file mode 100644 index 0000000000000..be7f9d5051fbf --- /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/TargetOptions.h b/clang/include/clang/Basic/TargetOptions.h index ba3acd0295871..2049f03b28893 100644 --- a/clang/include/clang/Basic/TargetOptions.h +++ b/clang/include/clang/Basic/TargetOptions.h @@ -78,17 +78,9 @@ class TargetOptions { /// \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, // Unsupported. - 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 82a503d01068d..5f9915d210221 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -628,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) @@ -946,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 b5655afdf419e..d55deeaa40bbc 100644 --- a/clang/include/clang/Basic/arm_sme.td +++ b/clang/include/clang/Basic/arm_sme.td @@ -298,3 +298,19 @@ multiclass ZAAddSub { 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 40e474d5f0a8f..4fcc9327f22fe 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 @@ -1981,7 +2045,23 @@ 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, @@ -2007,8 +2087,65 @@ let TargetGuard = "sme2" in { 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 22a2a3c5434d6..21dac067ab66e 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; diff --git a/clang/include/clang/Basic/riscv_sifive_vector.td b/clang/include/clang/Basic/riscv_sifive_vector.td index d4c22769d9b95..bb54e26641861 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>; } } diff --git a/clang/include/clang/Basic/riscv_vector.td b/clang/include/clang/Basic/riscv_vector.td index 1d24b4e75f9dd..682f1d5c8af68 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/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 9e0d0fa0d8e82..498acfd380131 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -204,11 +204,6 @@ class ConstantAggregateBuilderBase { 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/OffloadBundler.h b/clang/include/clang/Driver/OffloadBundler.h index 17df31d31071d..84349abe185fa 100644 --- a/clang/include/clang/Driver/OffloadBundler.h +++ b/clang/include/clang/Driver/OffloadBundler.h @@ -66,7 +66,7 @@ class OffloadBundler { llvm::Error UnbundleArchive(); }; -/// Obtain the offload kind, real machine triple, and an optional GPUArch +/// Obtain the offload kind, real machine triple, and an optional TargetID /// out of the target information specified by the user. /// Bundle Entry ID (or, Offload Target String) has following components: /// * Offload Kind - Host, OpenMP, or HIP diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d1b67a448b2a5..7dd2755350f7a 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1359,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, @@ -1439,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.">, @@ -1741,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<"">, @@ -2117,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; } // 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 //===----------------------------------------------------------------------===// @@ -4688,9 +4721,9 @@ 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]>, + Visibility<[ClangOption, FlangOption, CC1Option, FC1Option]>, Values<"none,4,5">, - NormalizedValuesScope<"TargetOptions">, + NormalizedValuesScope<"llvm::CodeObjectVersionKind">, NormalizedValues<["COV_None", "COV_4", "COV_5"]>, MarshallingInfoEnum, "COV_4">; @@ -5137,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">; @@ -5253,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]>, @@ -5304,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]>; @@ -5314,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=">, @@ -6263,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, @@ -6796,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>; @@ -7369,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 316d83df13e93..d719196b9a43e 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 @@ class DeclarationFragmentsBuilder { /// 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 bc412611ef624..4fbcc4ef0705b 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1424,6 +1424,19 @@ 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. @@ -3021,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 @@ -4745,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 bcb28634a2b88..fe99b3d5adbfa 100644 --- a/clang/include/clang/Frontend/ASTUnit.h +++ b/clang/include/clang/Frontend/ASTUnit.h @@ -691,18 +691,16 @@ class ASTUnit { /// 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 d26a452cf94cc..ac2f940769fbe 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -304,6 +304,11 @@ class CompilerInstance : public ModuleLoader { 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/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h index 500a7e11ab9ac..ddfae2666c4c3 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/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 30e0352c86863..ca29ce46873e8 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 @@ class Parser : public CodeCompletionHandler { /// 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 cb525c9d0edd6..e1626a7870bb7 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/ExternalSemaSource.h b/clang/include/clang/Sema/ExternalSemaSource.h index 22d1ee2df115a..8b41c5483458a 100644 --- a/clang/include/clang/Sema/ExternalSemaSource.h +++ b/clang/include/clang/Sema/ExternalSemaSource.h @@ -181,6 +181,9 @@ class ExternalSemaSource : public ExternalASTSource { SmallVectorImpl > &Pending) {} + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls){}; + /// Read the set of late parsed template functions for this source. /// /// The external source should insert its own late parsed template functions diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h index 2bf91cb5212c5..6054ef39e54ff 100644 --- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h @@ -319,6 +319,9 @@ class MultiplexExternalSemaSource : public ExternalSemaSource { void ReadPendingInstantiations( SmallVectorImpl >& Pending) override; + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls) override; + /// Read the set of late parsed template functions for this source. /// /// The external source should insert its own late parsed template functions diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index a97968dc7b209..6ccabad3af544 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 38377f01a1008..6de1a098e067a 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" @@ -58,6 +59,7 @@ #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -408,6 +410,7 @@ class Sema final { ASTConsumer &Consumer; DiagnosticsEngine &Diags; SourceManager &SourceMgr; + api_notes::APINotesManager APINotes; /// Flag indicating whether or not to collect detailed statistics. bool CollectStats; @@ -2099,6 +2102,9 @@ class Sema final { 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); @@ -3732,6 +3738,9 @@ class Sema final { 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); @@ -3844,6 +3853,12 @@ class Sema final { 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); @@ -5452,7 +5467,7 @@ class Sema final { 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( @@ -8464,6 +8479,8 @@ class Sema final { 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 { @@ -8842,6 +8859,9 @@ class Sema final { /// The type of an exception. UPPC_ExceptionType, + /// Explicit specialization. + UPPC_ExplicitSpecialization, + /// Partial specialization. UPPC_PartialSpecialization, @@ -10068,6 +10088,12 @@ class Sema final { /// but have not yet been performed. std::deque PendingInstantiations; + /// Track constexpr functions referenced before they are (lexically) defined. + /// The key is the pattern, associated with a list of specialisations that + /// need to be instantiated when the pattern is defined. + llvm::DenseMap> + PendingInstantiationsOfConstexprEntities; + /// Queue of implicit template instantiations that cannot be performed /// eagerly. SmallVector LateParsedInstantiations; @@ -10386,6 +10412,9 @@ class Sema final { bool Recursive = false, bool DefinitionRequired = false, bool AtEndOfTU = false); + + void PerformPendingInstantiationsOfConstexprFunctions(FunctionDecl *Template); + VarTemplateSpecializationDecl *BuildVarTemplateInstantiation( VarTemplateDecl *VarTemplate, VarDecl *FromVar, const TemplateArgumentList &TemplateArgList, @@ -10986,7 +11015,10 @@ class Sema final { /// 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 @@ -11186,6 +11218,12 @@ class Sema final { 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, @@ -12182,6 +12220,13 @@ class Sema final { /// 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); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5c32fbc079c9a..08642889b0cf3 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -695,6 +695,10 @@ enum ASTRecordTypes { /// Record code for an unterminated \#pragma clang assume_nonnull begin /// recorded in a preamble. PP_ASSUME_NONNULL_LOC = 67, + + /// Record code for constexpr templated entities that have been used but not + /// yet instantiated. + PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES = 68, }; /// Record types used within a source manager block. @@ -1101,7 +1105,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 7eefdca6815cd..407fc614f483e 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -814,6 +814,9 @@ class ASTReader /// is the instantiation location. SmallVector PendingInstantiations; + llvm::DenseMap> + PendingInstantiationsOfConstexprEntities; + //@} /// \name DiagnosticsEngine-relevant special data @@ -2101,6 +2104,9 @@ class ASTReader SmallVectorImpl> &Pending) override; + virtual void ReadPendingInstantiationsOfConstexprEntity( + const NamedDecl *D, llvm::SmallSetVector &Decls) override; + void ReadLateParsedTemplates( llvm::MapVector> &LPTMap) override; diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index b6e9f0fae1c7f..1d224786372e8 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/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 8129ebc8fdc69..0d36587484bf9 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 @@ class CallEventRef : public IntrusiveRefCntPtr { // 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 @@ class CallEvent { /// 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 @@ class CallEvent { 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 @@ class CallEvent { 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 @@ class CallEvent { // 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 @@ class CallEvent { /// 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 @@ class BlockCall : public CallEvent { 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 @@ class BlockCall : public CallEvent { // 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 @@ class CXXInstanceCall : public AnyFunctionCall { : 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 @@ class CXXDestructorCall : public CXXInstanceCall { 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 @@ class AnyCXXConstructorCall : public AnyFunctionCall { 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 @@ class CXXConstructorCall : public AnyCXXConstructorCall { 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 @@ class CXXAllocatorCall : public AnyFunctionCall { : 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 @@ class CXXDeallocatorCall : public AnyFunctionCall { // // 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 @@ class ObjCMethodCall : public CallEvent { 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 @@ class ObjCMethodCall : public CallEvent { 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 @@ class ObjCMethodCall : public CallEvent { return getOriginExpr()->getMethodFamily(); } - Selector getSelector() const { - return getOriginExpr()->getSelector(); - } + Selector getSelector() const { return getOriginExpr()->getSelector(); } SourceRange getSourceRange() const override; @@ -1262,7 +1264,7 @@ class ObjCMethodCall : public CallEvent { 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 @@ class CallEventManager { 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/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index 692c438458656..a64cf7ae4efcb 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -215,6 +215,15 @@ class SValBuilder { 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/Support/RISCVVIntrinsicUtils.h b/clang/include/clang/Support/RISCVVIntrinsicUtils.h index cd620a8fb2b5c..49ce32553da81 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), }; diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp new file mode 100644 index 0000000000000..ec1fb3ffa961c --- /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/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt index dec596ea160c6..dc83edd911ce2 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 4f54791b4c1e5..e877f903b34c6 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1627,28 +1627,22 @@ const llvm::fltSemantics &ASTContext::getFloatTypeSemantics(QualType T) const { CharUnits ASTContext::getDeclAlign(const Decl *D, bool ForAlignof) const { unsigned Align = Target->getCharWidth(); - bool UseAlignAttrOnly = false; - if (unsigned AlignFromAttr = D->getMaxAlignment()) { + const unsigned AlignFromAttr = D->getMaxAlignment(); + if (AlignFromAttr) Align = AlignFromAttr; - // __attribute__((aligned)) can increase or decrease alignment - // *except* on a struct or struct member, where it only increases - // alignment unless 'packed' is also specified. - // - // It is an error for alignas to decrease alignment, so we can - // ignore that possibility; Sema should diagnose it. - if (isa(D)) { - UseAlignAttrOnly = D->hasAttr() || - cast(D)->getParent()->hasAttr(); - } else { - UseAlignAttrOnly = true; - } - } - else if (isa(D)) - UseAlignAttrOnly = - D->hasAttr() || - cast(D)->getParent()->hasAttr(); - + // __attribute__((aligned)) can increase or decrease alignment + // *except* on a struct or struct member, where it only increases + // alignment unless 'packed' is also specified. + // + // It is an error for alignas to decrease alignment, so we can + // ignore that possibility; Sema should diagnose it. + bool UseAlignAttrOnly; + if (const FieldDecl *FD = dyn_cast(D)) + UseAlignAttrOnly = + FD->hasAttr() || FD->getParent()->hasAttr(); + else + UseAlignAttrOnly = AlignFromAttr != 0; // If we're using the align attribute only, just ignore everything // else about the declaration and its type. if (UseAlignAttrOnly) { @@ -1680,14 +1674,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 @@ -4014,8 +4010,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; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index c4e931e220f69..f1f335118f37a 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3513,6 +3513,14 @@ class IsTypeDeclaredInsideVisitor 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 @@ class IsTypeDeclaredInsideVisitor }; } // 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 @@ class AttrImporter { 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/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 4fcc2e7302c03..e4d7169752bc8 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; diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h new file mode 100644 index 0000000000000..a97eac85abc69 --- /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 4aa8045bc93be..2aafe5bd5289f 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" @@ -4622,11 +4623,13 @@ struct IncDecSubobjectHandler { if (Old) *Old = APValue(Value); APFloat One(Value.getSemantics(), 1); + llvm::RoundingMode RM = getActiveRoundingMode(Info, E); + APFloat::opStatus St; if (AccessKind == AK_Increment) - Value.add(One, APFloat::rmNearestTiesToEven); + St = Value.add(One, RM); else - Value.subtract(One, APFloat::rmNearestTiesToEven); - return true; + St = Value.subtract(One, RM); + return checkFloatingPointResult(Info, E, St); } bool foundPointer(APValue &Subobj, QualType SubobjType) { if (!checkConst(SubobjType)) @@ -11492,40 +11495,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(); @@ -11644,19 +11617,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"); @@ -15672,12 +15649,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 @@ -15686,10 +15665,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 c8abb7c17a38b..89b7708c0c2a1 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 c8d3c1243fc10..f45e8624a7741 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -75,7 +75,7 @@ template class OptionScope final { template bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { - auto *SubExpr = CE->getSubExpr(); + const Expr *SubExpr = CE->getSubExpr(); switch (CE->getCastKind()) { case CK_LValueToRValue: { @@ -614,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. @@ -642,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; @@ -831,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. @@ -848,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; } @@ -2231,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)) @@ -2269,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)) @@ -2332,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 ec9b6bb140845..bc1d5d11a1151 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -68,7 +68,6 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, 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); @@ -130,7 +129,13 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, /// 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); @@ -204,6 +209,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, } 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 69ab1e57b6330..1d04998d5dd15 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); diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h index 94eb2a611771b..7c3e0f6302490 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -179,6 +179,8 @@ class Function final { bool isBuiltin() const { return F->getBuiltinID() != 0; } + bool isUnevaluatedBuiltin() const { return IsUnevaluatedBuiltin; } + unsigned getNumParams() const { return ParamTypes.size(); } unsigned getParamOffset(unsigned ParamIndex) const { @@ -191,7 +193,7 @@ class Function final { 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, @@ -250,6 +252,7 @@ class Function final { 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 88de1f1392e68..9019f32e6cef2 100644 --- a/clang/lib/AST/Interp/IntegralAP.h +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -102,7 +102,12 @@ template class IntegralAP final { 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 @@ -191,18 +196,15 @@ template class IntegralAP final { } 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) { @@ -219,21 +221,19 @@ template class IntegralAP final { 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; } @@ -264,28 +264,21 @@ template class IntegralAP final { } private: - static bool CheckAddUB(const IntegralAP &A, const IntegralAP &B, - unsigned BitWidth, IntegralAP *R) { - if (!A.isSigned()) { - R->V = A.V + B.V; + template