From 2760508875396b6c09eb6e4799c26d5593bc0252 Mon Sep 17 00:00:00 2001 From: nerix Date: Thu, 18 Sep 2025 11:27:25 +0200 Subject: [PATCH 01/47] [LLDB][ProcessWindows] Set exit status on instance rather than going through all targets (#159308) When quitting LLDB on Windows while a process was still running, LLDB would take unusually long to exit. This was due to a temporary deadlock: The main thread was destroying the processes. In doing so, it iterated over the target list: https://github.com/llvm/llvm-project/blob/88c64f76ed2ca226da99b99f60d316b1519fc7d8/lldb/source/Core/Debugger.cpp#L1095-L1098 This locks the list for the whole iteration. Finalizing the process would eventually lead to `DebuggerThread::StopDebugging`, which terminates the process and waits for it to exit: https://github.com/llvm/llvm-project/blob/88c64f76ed2ca226da99b99f60d316b1519fc7d8/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp#L196 The debugger loop (on a separate thread) would see that the process exited and call [`ProcessWindows::OnExitProcess`](https://github.com/llvm/llvm-project/blob/88c64f76ed2ca226da99b99f60d316b1519fc7d8/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp#L656-L673). This calls the static function [`Process::SetProcessExitStatus`](https://github.com/llvm/llvm-project/blob/0a7a7d56fc882653335beba0d1f8ea9f26089c22/lldb/source/Target/Process.cpp#L1098-L1126). This tries to find the process by its ID from the debugger's target list. Doing so requires locking the list, so the debugger thread would then be stuck on https://github.com/llvm/llvm-project/blob/0a7a7d56fc882653335beba0d1f8ea9f26089c22/lldb/source/Target/TargetList.cpp#L403 After 5s, the main thread would give up waiting. So every exit where the process was still running would be delayed by about 5s. Since `ProcessWindows` would find itself when calling `SetProcessExitStatus`, we can call `SetExitStatus` directly. This can also make some tests run faster. For example, the DIA PDB tests previously took 15s to run on my PC (24 jobs) and now take 5s. For all shell tests, the difference isn't that big (only about 3s), because most don't run into this and the tests run in parallel. (cherry picked from commit a868f28c6e9beecb2b3fbe8acfbe0d272fabd14d) --- lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 27530f032ce51..0fecefe23b88e 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -666,7 +666,7 @@ void ProcessWindows::OnExitProcess(uint32_t exit_code) { target->ModulesDidUnload(unloaded_modules, true); } - SetProcessExitStatus(GetID(), true, 0, exit_code); + SetExitStatus(exit_code, /*exit_string=*/""); SetPrivateState(eStateExited); ProcessDebugger::OnExitProcess(exit_code); From c000f3226bdf469ccf71f3257840493f21ceaead Mon Sep 17 00:00:00 2001 From: Jade Marker Date: Thu, 25 Sep 2025 02:20:25 +0100 Subject: [PATCH 02/47] [Mips] Fixed libunwind::Registers_mips_o32::jumpto to allow for load delay (#152942) Fix #152922 MIPS III also has load delay, so libunwind::Registers_mips_newabi::jumpto() is also fixed. (cherry picked from commit a3d7c468bdc328f04da720088b2e542ef1f33ffc) --- libunwind/src/UnwindRegistersRestore.S | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index 5e199188945df..1bcd205be260d 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -1044,9 +1044,10 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv) lw $27, (4 * 27)($4) lw $28, (4 * 28)($4) lw $29, (4 * 29)($4) - lw $30, (4 * 30)($4) // load new pc into ra lw $31, (4 * 32)($4) + // MIPS 1 has load delay slot. Ensure lw $31 and jr are separated by an instruction. + lw $30, (4 * 30)($4) // jump to ra, load a0 in the delay slot jr $31 lw $4, (4 * 4)($4) @@ -1082,11 +1083,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv) ld $2, (8 * 2)($4) ld $3, (8 * 3)($4) // skip a0 for now - .irp i,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 + .irp i,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 ld $\i, (8 * \i)($4) .endr // load new pc into ra ld $31, (8 * 32)($4) + // MIPS 1 has load delay slot. Ensure lw $31 and jr are separated by an instruction. + ld $30, (8 * 30)($4) // jump to ra, load a0 in the delay slot jr $31 ld $4, (8 * 4)($4) From 0d1b9249d189f9fdb7b6b743ff9eb517e16a1295 Mon Sep 17 00:00:00 2001 From: Aiden Grossman Date: Wed, 8 Oct 2025 08:07:34 -0700 Subject: [PATCH 03/47] [CI] Add dyung and c-rhodes to the Release Asset List (#162478) We have a list of people authorized to create release assets to prevent someone adding a release asset to a release without someone getting alerted. Add dyung and c-rhodes now that they are also release managers. Fixes #162463 (maybe after backport). (cherry picked from commit 348ffe8276c2fa0ea2f3df7ca449f4b2b49ec68b) --- .github/workflows/release-asset-audit.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-asset-audit.py b/.github/workflows/release-asset-audit.py index 23b901a476dc0..0cff6c695921f 100644 --- a/.github/workflows/release-asset-audit.py +++ b/.github/workflows/release-asset-audit.py @@ -54,6 +54,8 @@ def _get_uploaders(release_version): "tru", "tstellar", "github-actions[bot]", + "c-rhodes", + "dyung", ] ) From 13bee3a798b16fea0f81f75dfbd4d82e04df818b Mon Sep 17 00:00:00 2001 From: yingopq <115543042+yingopq@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:40:22 +0800 Subject: [PATCH 04/47] [Mips] Fix clang crashes when assembling invalid MIPS beql instructions with --arch=mips (#156413) From clang version 4, mips append new instruction BeqImm and BEQLImm, the second operand format of instruction is imm64:$imm. 1.When Mips process `beql $t0, ($t0), 1`, it think the second operand was an imm, so match success. Then mips backend process expandBranchImm, check the second operand `$t0` was not imm, reported asserts. We can strengthen the second operand matching restrictions. 2.Similarly, when Mips process `beql $t0, (1), 1`, it think the second was an imm. so match success. Then mips backend process expandBranchImm, check the third operand `1` was not expression, reported asserts. Permit the third operand of `beql` to be imm. Fixes #151453. (cherry picked from commit 51eee2010d3184f21502d12d69c0234549ee3fb4) --- llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp | 2 +- llvm/lib/Target/Mips/MipsInstrInfo.td | 16 +++++++++++++--- llvm/test/MC/Mips/branch-pseudos-bad.s | 8 ++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp index 01e4d17f6236d..602b89a117595 100644 --- a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp +++ b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp @@ -3676,7 +3676,7 @@ bool MipsAsmParser::expandBranchImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, Out, STI)) return true; - if (IsLikely) { + if (IsLikely && MemOffsetOp.isExpr()) { TOut.emitRRX(OpCode, DstRegOp.getReg(), ATReg, MCOperand::createExpr(MemOffsetOp.getExpr()), IDLoc, STI); TOut.emitRRI(Mips::SLL, Mips::ZERO, Mips::ZERO, 0, IDLoc, STI); diff --git a/llvm/lib/Target/Mips/MipsInstrInfo.td b/llvm/lib/Target/Mips/MipsInstrInfo.td index b6125b972717a..255fd838a72a2 100644 --- a/llvm/lib/Target/Mips/MipsInstrInfo.td +++ b/llvm/lib/Target/Mips/MipsInstrInfo.td @@ -858,6 +858,16 @@ def calltarget : Operand { def imm64: Operand; +def ConstantImmAsmOperandClass : AsmOperandClass { + let Name = "ConstantImm"; + let PredicateMethod = "isConstantImm"; + let RenderMethod = "addImmOperands"; +} + +def ConstantImm64: Operand { + let ParserMatchClass = ConstantImmAsmOperandClass; +} + def simm19_lsl2 : Operand { let EncoderMethod = "getSimm19Lsl2Encoding"; let DecoderMethod = "DecodeSimm19Lsl2"; @@ -2950,10 +2960,10 @@ def : MipsInstAlias<"nor\t$rs, $imm", (NORImm GPR32Opnd:$rs, GPR32Opnd:$rs, let hasDelaySlot = 1, isCTI = 1 in { def BneImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), - (ins imm64:$imm64, brtarget:$offset), + (ins ConstantImm64:$imm64, brtarget:$offset), "bne\t$rt, $imm64, $offset">; def BeqImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), - (ins imm64:$imm64, brtarget:$offset), + (ins ConstantImm64:$imm64, brtarget:$offset), "beq\t$rt, $imm64, $offset">; class CondBranchPseudo : @@ -2981,7 +2991,7 @@ def BGTUL: CondBranchPseudo<"bgtul">, ISA_MIPS2_NOT_32R6_64R6; let isCTI = 1 in class CondBranchImmPseudo : - MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, imm64:$imm, brtarget:$offset), + MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, ConstantImm64:$imm, brtarget:$offset), !strconcat(instr_asm, "\t$rs, $imm, $offset")>; def BEQLImmMacro : CondBranchImmPseudo<"beql">, ISA_MIPS2_NOT_32R6_64R6; diff --git a/llvm/test/MC/Mips/branch-pseudos-bad.s b/llvm/test/MC/Mips/branch-pseudos-bad.s index c23164d904619..9633414d84f4a 100644 --- a/llvm/test/MC/Mips/branch-pseudos-bad.s +++ b/llvm/test/MC/Mips/branch-pseudos-bad.s @@ -1,5 +1,13 @@ # RUN: not llvm-mc %s -triple=mips -mcpu=mips32 2>&1 | FileCheck %s +# CHECK: error: invalid operand for instruction + beql $t0, ($t0), 1 +# CHECK: error: invalid operand for instruction + bne $t0, ($t0), 1 +# CHECK: error: invalid operand for instruction + beq $t0, ($t0), 1 + + # Check for errors when using conditional branch pseudos after .set noat. .set noat local_label: From 18593ab316f6c5b9236cbda4eb48b10ecfbdfc90 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Fri, 10 Oct 2025 12:58:24 -0700 Subject: [PATCH 05/47] workflows/release-binaries: Run tests on the same runner as the build (#162421) Also, ignore the test results since they almost always fail. This allows us to simplify the build process and skip uploading and downloading the build and source directories which are huge. (cherry picked from commit 9f0f6e8dda5a3f87458acbb4daf09c2bfbac25f2) --- .../release-binaries-save-stage/action.yml | 44 ---------- .../release-binaries-setup-stage/action.yml | 59 ------------- .github/workflows/release-binaries.yml | 86 +++++-------------- 3 files changed, 21 insertions(+), 168 deletions(-) delete mode 100644 .github/workflows/release-binaries-save-stage/action.yml delete mode 100644 .github/workflows/release-binaries-setup-stage/action.yml diff --git a/.github/workflows/release-binaries-save-stage/action.yml b/.github/workflows/release-binaries-save-stage/action.yml deleted file mode 100644 index f08088c7bc56f..0000000000000 --- a/.github/workflows/release-binaries-save-stage/action.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Save Stage -description: >- - Upload the source and binary directories from a build stage so that they - can be re-used in the next stage. This action is used to the release - binaries workflow into multiple stages to avoid the 6 hour timeout on - the GitHub hosted runners. -inputs: - build-prefix: - description: "Directory containing the build directory." - required: true - type: 'string' - -permissions: - contents: read - -runs: - using: "composite" - steps: - # We need to create an archive of the build directory, because it has too - # many files to upload. - - name: Package Build and Source Directories - shell: bash - run: | - # Remove .git/config to avoid leaking GITHUB_TOKEN stored there. - # See https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/ - rm -Rf .git/config - # Windows does not support symlinks, so we need to dereference them. - tar --exclude build/ ${{ (runner.os == 'Windows' && '-h') || '' }} -c . | zstd -T0 -c > ../llvm-project.tar.zst - mv ../llvm-project.tar.zst . - tar -C ${{ inputs.build-prefix }} -c build/ | zstd -T0 -c > build.tar.zst - - - name: Upload Stage 1 Source - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-source - path: llvm-project.tar.zst - retention-days: 2 - - - name: Upload Stage 1 Build Dir - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: ${{ runner.os}}-${{ runner.arch }}-${{ github.job }}-build - path: build.tar.zst - retention-days: 2 diff --git a/.github/workflows/release-binaries-setup-stage/action.yml b/.github/workflows/release-binaries-setup-stage/action.yml deleted file mode 100644 index f5e5db27e6595..0000000000000 --- a/.github/workflows/release-binaries-setup-stage/action.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Setup Stage -description: >- - Setup the next stage of the release binaries workflow. This sets up the - environment correctly for a new stage of the release binaries workflow - and also restores the source and build directory from the previous stage. - -inputs: - previous-artifact: - description: >- - A unique descriptor for the artifact from the previous stage. This will - be used to construct the final artifact pattern, which is: - $RUNNER_OS-$RUNNER_ARCH-$PREVIOUS_ARTIFACT-* - required: false - type: 'string' - -outputs: - build-prefix: - description: "Directory containing the build directory." - value: ${{ steps.build-prefix.outputs.build-prefix }} - -runs: - using: "composite" - steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main - - - name: Setup Windows - if: startsWith(runner.os, 'Windows') - uses: llvm/actions/setup-windows@main - with: - arch: amd64 - - - name: Set Build Prefix - id: build-prefix - shell: bash - run: | - build_prefix=`pwd` - if [ "${{ runner.os }}" = "Linux" ]; then - sudo chown $USER:$USER /mnt/ - build_prefix=/mnt/ - fi - echo "build-prefix=$build_prefix" >> $GITHUB_OUTPUT - - - name: Download Previous Stage Artifact - if: ${{ inputs.previous-artifact }} - id: download - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 - with: - pattern: ${{ runner.os }}-${{ runner.arch }}-${{ inputs.previous-artifact }}-* - merge-multiple: true - - - name: Unpack Artifact - if: ${{ steps.download.outputs.download-path }} - shell: bash - run: | - tar --zstd -xf llvm-project.tar.zst - rm llvm-project.tar.zst - tar --zstd -C ${{ steps.build-prefix.outputs.build-prefix}} -xf build.tar.zst - rm build.tar.zst diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index c113b42dc8ed4..765cd06469977 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -194,40 +194,30 @@ jobs: runs-on: ${{ needs.prepare.outputs.build-runs-on }} steps: - - name: Checkout Actions - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - # Check out outside of working directory so the source checkout doesn't - # remove it. - path: workflows - - # actions/checkout does not support paths outside of the GITHUB_WORKSPACE. - # Also, anything that we put inside of GITHUB_WORKSPACE will be overwritten - # by future actions/checkout steps. Therefore, in order to checkout the - # latest actions from main, we need to first checkout out the actions inside of - # GITHUB_WORKSPACE (see previous step), then use actions/checkout to checkout - # the code being built and the move the actions from main back into GITHUB_WORKSPACE, - # becasue the uses on composite actions only reads workflows from inside GITHUB_WORKSPACE. - - shell: bash - run: mv workflows ../workflows-main - - name: Checkout LLVM uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ needs.prepare.outputs.ref }} - - name: Copy main workflows - shell: bash - run: | - mv ../workflows-main . + - name: Install Ninja + uses: llvm/actions/install-ninja@a1ea791b03c8e61f53a0e66f2f73db283aa0f01e # main + + - name: Setup Windows + if: startsWith(runner.os, 'Windows') + uses: llvm/actions/setup-windows@main + with: + arch: amd64 - - name: Setup Stage + - name: Set Build Prefix id: setup-stage - uses: ./workflows-main/.github/workflows/release-binaries-setup-stage + shell: bash + run: | + build_prefix=`pwd` + if [ "${{ runner.os }}" = "Linux" ]; then + sudo chown $USER:$USER /mnt/ + build_prefix=/mnt/ + fi + echo "build-prefix=$build_prefix" >> $GITHUB_OUTPUT - name: Configure id: build @@ -258,17 +248,11 @@ jobs: path: | ${{ needs.prepare.outputs.release-binary-filename }} - # Clean up some build files to reduce size of artifact. - - name: Clean Up Build Directory - shell: bash + - name: Run Tests + # These almost always fail so don't let them fail the build and prevent the uploads. + continue-on-error: true run: | - find ${{ steps.setup-stage.outputs.build-prefix }}/build -iname ${{ needs.prepare.outputs.release-binary-filename }} -delete - find ${{ steps.setup-stage.outputs.build-prefix }}/build -iname _CPack_Packages -prune -exec rm -r {} + - - - name: Save Stage - uses: ./workflows-main/.github/workflows/release-binaries-save-stage - with: - build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-check-all upload-release-binaries: name: "Upload Release Binaries" @@ -327,31 +311,3 @@ jobs: --release ${{ needs.prepare.outputs.release-version }} \ upload \ --files ${{ needs.prepare.outputs.release-binary-filename }}* - - test-release: - name: "Test Release" - needs: - - prepare - - build-release-package - if: >- - github.repository_owner == 'llvm' - runs-on: ${{ needs.prepare.outputs.test-runs-on }} - steps: - - name: Checkout Actions - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} - sparse-checkout: | - .github/workflows/ - sparse-checkout-cone-mode: false - path: workflows - - name: Setup Stage - id: setup-stage - uses: ./workflows/.github/workflows/release-binaries-setup-stage - with: - previous-artifact: build-release-package - - - name: Run Tests - shell: bash - run: | - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-check-all From 5386abc82ab871522dceed5bc79c3ad40f5f6e2b Mon Sep 17 00:00:00 2001 From: lbonn Date: Sat, 27 Sep 2025 16:49:54 +0200 Subject: [PATCH 06/47] [libc++][ranges] Fix `ranges::join_view` segmented iterator trait (#158347) The outer iterator needs to move to the next segment when calling __compose. Without this change, `find_segment_if` would never reach the end of the join_view which caused erroneous result when calling `ranges::find` on a join_view of bidirectional ranges. Other specializations using the segmented iterator trait were likely to be affected as well. Fixes #158279 Fixes #93180 (cherry picked from commit d1b5607dc113016b74d0a58e95fed00ea9ad7950) --- libcxx/include/__ranges/join_view.h | 9 +- .../alg.find/ranges.find.pass.cpp | 121 ++++++++++++------ 2 files changed, 89 insertions(+), 41 deletions(-) diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h index 327b349f476a7..364f056d8d2cf 100644 --- a/libcxx/include/__ranges/join_view.h +++ b/libcxx/include/__ranges/join_view.h @@ -410,8 +410,13 @@ struct __segmented_iterator_traits<_JoinViewIterator> { static constexpr _LIBCPP_HIDE_FROM_ABI _JoinViewIterator __compose(__segment_iterator __seg_iter, __local_iterator __local_iter) { - return _JoinViewIterator( - std::move(__seg_iter).__get_data(), std::move(__seg_iter).__get_iter(), std::move(__local_iter)); + auto&& __parent = std::move(__seg_iter).__get_data(); + auto&& __outer = std::move(__seg_iter).__get_iter(); + if (__local_iter == ranges::end(*__outer)) { + ++__outer; + return _JoinViewIterator(*__parent, __outer); + } + return _JoinViewIterator(__parent, __outer, std::move(__local_iter)); } }; diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp index d7e6be9928a2d..5f730f0f5bba8 100644 --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp @@ -272,57 +272,100 @@ class Comparable { friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; } }; -void test_deque() { - { // empty deque - std::deque data; - assert(std::ranges::find(data, 4) == data.end()); - assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); - } - - { // single element - match - std::deque data = {4}; - assert(std::ranges::find(data, 4) == data.begin()); - assert(std::ranges::find(data.begin(), data.end(), 4) == data.begin()); - } - - { // single element - no match - std::deque data = {3}; - assert(std::ranges::find(data, 4) == data.end()); - assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); - } - - // many elements - for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) { - { // last element match +void test_segmented_iterator_types() { + // Test the optimized find algorithm for types that implement the segment iterator trait + // deque + { + { // empty deque std::deque data; - data.resize(size); - std::fill(data.begin(), data.end(), 3); - data[size - 1] = 4; - assert(std::ranges::find(data, 4) == data.end() - 1); - assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 1); + assert(std::ranges::find(data, 4) == data.end()); + assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); } - { // second-last element match - std::deque data; - data.resize(size); - std::fill(data.begin(), data.end(), 3); - data[size - 2] = 4; - assert(std::ranges::find(data, 4) == data.end() - 2); - assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 2); + { // single element - match + std::deque data = {4}; + assert(std::ranges::find(data, 4) == data.begin()); + assert(std::ranges::find(data.begin(), data.end(), 4) == data.begin()); } - { // no match - std::deque data; - data.resize(size); - std::fill(data.begin(), data.end(), 3); + { // single element - no match + std::deque data = {3}; assert(std::ranges::find(data, 4) == data.end()); assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); } + + // many elements + for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) { + { // last element match + std::deque data; + data.resize(size); + std::fill(data.begin(), data.end(), 3); + data[size - 1] = 4; + assert(std::ranges::find(data, 4) == data.end() - 1); + assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 1); + } + + { // second-last element match + std::deque data; + data.resize(size); + std::fill(data.begin(), data.end(), 3); + data[size - 2] = 4; + assert(std::ranges::find(data, 4) == data.end() - 2); + assert(std::ranges::find(data.begin(), data.end(), 4) == data.end() - 2); + } + + { // no match + std::deque data; + data.resize(size); + std::fill(data.begin(), data.end(), 3); + assert(std::ranges::find(data, 4) == data.end()); + assert(std::ranges::find(data.begin(), data.end(), 4) == data.end()); + } + } + } + // join_view ranges adaptor + { + { // single element - match + int data[1][1] = {{4}}; + auto joined = std::views::join(data); + assert(std::ranges::find(joined, 4) == std::ranges::begin(joined)); + } + { // single element - no match + // (reproducer for https://llvm.org/PR158279, where the iterator would never reach the end sentinel) + int data[1][1] = {{3}}; + auto joined = std::views::join(data); + assert(std::ranges::find(joined, 4) == std::ranges::end(joined)); + } + { // several sub-arrays of size 1 - match + int data[3][1] = {{0}, {4}, {0}}; + auto joined = std::views::join(data); + assert(std::ranges::find(joined, 4) == std::next(std::ranges::begin(joined))); + } + { // several sub-arrays of size 2 - match in second element of an array + int data[3][2] = {{0, 0}, {0, 4}, {0, 0}}; + auto joined = std::views::join(data); + assert(std::ranges::find(joined, 4) == std::ranges::next(std::ranges::begin(joined), 3)); + } + { // vector of empty vectors + std::vector> data = {{}, {}}; + auto joined = std::views::join(data); + assert(std::ranges::find(joined, 4) == std::ranges::end(joined)); + } + { // vector of variably sized vectors - match + std::vector> data = {{}, {}, {3, 4}, {}, {}}; + auto joined = std::views::join(data); + assert(std::ranges::find(joined, 4) == std::ranges::next(std::ranges::begin(joined))); + } + { // vector of variably sized vectors - no match + std::vector> data = {{}, {}, {3, 5}, {}, {}}; + auto joined = std::views::join(data); + assert(std::ranges::find(joined, 4) == std::ranges::end(joined)); + } } } int main(int, char**) { - test_deque(); + test_segmented_iterator_types(); test(); static_assert(test()); From caef7619d5fde96d1e3153f7ac55ccae0351b487 Mon Sep 17 00:00:00 2001 From: owenca Date: Sat, 11 Oct 2025 14:40:26 -0700 Subject: [PATCH 07/47] [clang-format] Fix a bug in OneLineFormatOffRegex (#162961) Fixes #162402 (cherry picked from commit 72d6d6e25a33bdea389002c699734e5ee68fe75a) --- clang/lib/Format/FormatTokenLexer.cpp | 13 +++++++------ clang/unittests/Format/FormatTest.cpp | 5 +++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 49da3160daf50..165ede2d04b2c 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -93,12 +93,6 @@ ArrayRef FormatTokenLexer::lex() { auto &Tok = *Tokens.back(); const auto NewlinesBefore = Tok.NewlinesBefore; switch (FormatOff) { - case FO_CurrentLine: - if (NewlinesBefore == 0) - Tok.Finalized = true; - else - FormatOff = FO_None; - break; case FO_NextLine: if (NewlinesBefore > 1) { FormatOff = FO_None; @@ -107,6 +101,13 @@ ArrayRef FormatTokenLexer::lex() { FormatOff = FO_CurrentLine; } break; + case FO_CurrentLine: + if (NewlinesBefore == 0) { + Tok.Finalized = true; + break; + } + FormatOff = FO_None; + [[fallthrough]]; default: if (!FormattingDisabled && FormatOffRegex.match(Tok.TokenText)) { if (Tok.is(tok::comment) && diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 95682f2d8cfd4..bda88115f09f7 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -25081,6 +25081,11 @@ TEST_F(FormatTest, OneLineFormatOffRegex) { " } while (0 )", Style); + Style.OneLineFormatOffRegex = "MACRO_TEST"; + verifyNoChange(" MACRO_TEST1 ( ) ;\n" + " MACRO_TEST2( );", + Style); + Style.ColumnLimit = 50; Style.OneLineFormatOffRegex = "^LogErrorPrint$"; verifyFormat(" myproject::LogErrorPrint(logger, \"Don't split me!\");\n" From c03b58bb091ed8f2c171c485b833f9ced6cba72e Mon Sep 17 00:00:00 2001 From: Ruoyu Zhong Date: Mon, 13 Oct 2025 14:04:51 +0800 Subject: [PATCH 08/47] [clangd] Fix code action kind for readability-identifier-naming fixes (#162808) https://github.com/llvm/llvm-project/pull/78454 converted readability-identifier-naming fixes to use rename mechanism to rename the symbol project-wide. However, it set the code action kind to "refactor" instead of "quickfix", which caused the fixes to be filtered out when editors (like VS Code) request quick fixes for diagnostics. On VS Code, for example, users would see "No quick fixes available" despite the diagnostic indicating "(fix available)". Fix that by changing the code action kind back to "quickfix". Signed-off-by: Ruoyu Zhong --- clang-tools-extra/clangd/ClangdLSPServer.cpp | 2 +- clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index a703009e2b467..e83af299bbd18 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -81,7 +81,7 @@ CodeAction toCodeAction(const ClangdServer::CodeActionResult::Rename &R, const URIForFile &File) { CodeAction CA; CA.title = R.FixMessage; - CA.kind = std::string(CodeAction::REFACTOR_KIND); + CA.kind = std::string(CodeAction::QUICKFIX_KIND); CA.command.emplace(); CA.command->title = R.FixMessage; CA.command->command = std::string(ApplyRenameCommand); diff --git a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp index 2c7f50d8c9e4c..95bf5e54fc792 100644 --- a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp @@ -235,7 +235,8 @@ TEST_F(LSPTest, ClangTidyRename) { .takeValue() .getAsArray())[0]; - ASSERT_EQ((*RenameCommand.getAsObject())["title"], "change 'foo' to 'Foo'"); + ASSERT_EQ((*RenameCommand.getAsObject())["title"], + "Apply fix: change 'foo' to 'Foo'"); Client.expectServerCall("workspace/applyEdit"); Client.call("workspace/executeCommand", RenameCommand); From a86b1e397e9017f75f01f4d3a7802c2e3908e92b Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Thu, 14 Aug 2025 12:24:12 -0700 Subject: [PATCH 09/47] compiler-rt: Make the tests pass on AArch64 and with page size != 4096. This makes the tests pass on my AArch64 machine with 16K pages. Not sure why some of the AArch64-specific test failures don't seem to occur on sanitizer-aarch64-linux. I could also reproduce them by running buildbot_cmake.sh on my machine. Pull Request: https://github.com/llvm/llvm-project/pull/153860 --- compiler-rt/lib/gwp_asan/tests/basic.cpp | 11 ++++++----- .../lib/gwp_asan/tests/never_allocated.cpp | 10 ++++++---- .../asan/TestCases/Linux/release_to_os_test.cpp | 1 + compiler-rt/test/cfi/cross-dso/lit.local.cfg.py | 4 ++++ compiler-rt/test/lit.common.cfg.py | 17 +++++++++++++++++ compiler-rt/test/msan/dtls_test.c | 1 + .../TestCases/Linux/odd_stack_size.cpp | 1 + .../TestCases/Linux/release_to_os_test.cpp | 3 +++ .../TestCases/Linux/resize_tls_dynamic.cpp | 3 +++ .../TestCases/Linux/tls_get_addr.c | 3 +++ 10 files changed, 45 insertions(+), 9 deletions(-) diff --git a/compiler-rt/lib/gwp_asan/tests/basic.cpp b/compiler-rt/lib/gwp_asan/tests/basic.cpp index 88e7ed14a5c2f..7d36a2ee1f947 100644 --- a/compiler-rt/lib/gwp_asan/tests/basic.cpp +++ b/compiler-rt/lib/gwp_asan/tests/basic.cpp @@ -65,11 +65,12 @@ TEST_F(DefaultGuardedPoolAllocator, NonPowerOfTwoAlignment) { // Added multi-page slots? You'll need to expand this test. TEST_F(DefaultGuardedPoolAllocator, TooBigForSinglePageSlots) { - EXPECT_EQ(nullptr, GPA.allocate(0x1001, 0)); - EXPECT_EQ(nullptr, GPA.allocate(0x1001, 1)); - EXPECT_EQ(nullptr, GPA.allocate(0x1001, 0x1000)); - EXPECT_EQ(nullptr, GPA.allocate(1, 0x2000)); - EXPECT_EQ(nullptr, GPA.allocate(0, 0x2000)); + size_t PageSize = sysconf(_SC_PAGESIZE); + EXPECT_EQ(nullptr, GPA.allocate(PageSize + 1, 0)); + EXPECT_EQ(nullptr, GPA.allocate(PageSize + 1, 1)); + EXPECT_EQ(nullptr, GPA.allocate(PageSize + 1, PageSize)); + EXPECT_EQ(nullptr, GPA.allocate(1, 2 * PageSize)); + EXPECT_EQ(nullptr, GPA.allocate(0, 2 * PageSize)); } TEST_F(CustomGuardedPoolAllocator, AllocAllSlots) { diff --git a/compiler-rt/lib/gwp_asan/tests/never_allocated.cpp b/compiler-rt/lib/gwp_asan/tests/never_allocated.cpp index 2f695b4379861..37a4b384e4ac0 100644 --- a/compiler-rt/lib/gwp_asan/tests/never_allocated.cpp +++ b/compiler-rt/lib/gwp_asan/tests/never_allocated.cpp @@ -13,8 +13,10 @@ #include "gwp_asan/tests/harness.h" TEST_P(BacktraceGuardedPoolAllocatorDeathTest, NeverAllocated) { + size_t PageSize = sysconf(_SC_PAGESIZE); + SCOPED_TRACE(""); - void *Ptr = GPA.allocate(0x1000); + void *Ptr = GPA.allocate(PageSize); GPA.deallocate(Ptr); std::string DeathNeedle = @@ -23,7 +25,7 @@ TEST_P(BacktraceGuardedPoolAllocatorDeathTest, NeverAllocated) { // Trigger a guard page in a completely different slot that's never allocated. // Previously, there was a bug that this would result in nullptr-dereference // in the posix crash handler. - char *volatile NeverAllocatedPtr = static_cast(Ptr) + 0x3000; + char *volatile NeverAllocatedPtr = static_cast(Ptr) + 3 * PageSize; if (!Recoverable) { EXPECT_DEATH(*NeverAllocatedPtr = 0, DeathNeedle); return; @@ -37,8 +39,8 @@ TEST_P(BacktraceGuardedPoolAllocatorDeathTest, NeverAllocated) { GetOutputBuffer().clear(); for (size_t i = 0; i < 100; ++i) { *NeverAllocatedPtr = 0; - *(NeverAllocatedPtr + 0x2000) = 0; - *(NeverAllocatedPtr + 0x3000) = 0; + *(NeverAllocatedPtr + 2 * PageSize) = 0; + *(NeverAllocatedPtr + 3 * PageSize) = 0; ASSERT_TRUE(GetOutputBuffer().empty()); } diff --git a/compiler-rt/test/asan/TestCases/Linux/release_to_os_test.cpp b/compiler-rt/test/asan/TestCases/Linux/release_to_os_test.cpp index 3e28ffde46ab6..dc3ead9e8436c 100644 --- a/compiler-rt/test/asan/TestCases/Linux/release_to_os_test.cpp +++ b/compiler-rt/test/asan/TestCases/Linux/release_to_os_test.cpp @@ -6,6 +6,7 @@ // RUN: %env_asan_opts=allocator_release_to_os_interval_ms=-1 %run %t force 2>&1 | FileCheck %s --check-prefix=FORCE_RELEASE // REQUIRES: x86_64-target-arch +// REQUIRES: page-size-4096 #include #include diff --git a/compiler-rt/test/cfi/cross-dso/lit.local.cfg.py b/compiler-rt/test/cfi/cross-dso/lit.local.cfg.py index dceb7cde7218b..5f5486af3779f 100644 --- a/compiler-rt/test/cfi/cross-dso/lit.local.cfg.py +++ b/compiler-rt/test/cfi/cross-dso/lit.local.cfg.py @@ -12,3 +12,7 @@ def getRoot(config): # Android O (API level 26) has support for cross-dso cfi in libdl.so. if config.android and "android-26" not in config.available_features: config.unsupported = True + +# The runtime library only supports 4K pages. +if "page-size-4096" not in config.available_features: + config.unsupported = True diff --git a/compiler-rt/test/lit.common.cfg.py b/compiler-rt/test/lit.common.cfg.py index f5576ce0e013d..13ae9e7753001 100644 --- a/compiler-rt/test/lit.common.cfg.py +++ b/compiler-rt/test/lit.common.cfg.py @@ -965,6 +965,23 @@ def is_windows_lto_supported(): else: config.available_features.add("memprof-shadow-scale-3") + +def target_page_size(): + try: + proc = subprocess.Popen( + f"{emulator or ''} python3", + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + out, err = proc.communicate(b'import os; print(os.sysconf("SC_PAGESIZE"))') + return int(out) + except: + return 4096 + + +config.available_features.add(f"page-size-{target_page_size()}") + if config.expensive_checks: config.available_features.add("expensive_checks") diff --git a/compiler-rt/test/msan/dtls_test.c b/compiler-rt/test/msan/dtls_test.c index 3c384256147a0..0e49ac9feb9fe 100644 --- a/compiler-rt/test/msan/dtls_test.c +++ b/compiler-rt/test/msan/dtls_test.c @@ -11,6 +11,7 @@ // Reports use-of-uninitialized-value, not analyzed XFAIL: target={{.*netbsd.*}} + XFAIL: aarch64-target-arch */ diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/odd_stack_size.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/odd_stack_size.cpp index 9d7d46b462a88..cc76804aed210 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/odd_stack_size.cpp +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/odd_stack_size.cpp @@ -1,4 +1,5 @@ // RUN: %clangxx -O1 %s -o %t && %run %t +// REQUIRES: page-size-4096 // UNSUPPORTED: android // Fail on powerpc64 bots with: diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/release_to_os_test.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/release_to_os_test.cpp index 0fa77200bf1cc..c7a5534696361 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/release_to_os_test.cpp +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/release_to_os_test.cpp @@ -11,6 +11,9 @@ // FIXME: This mode uses 32bit allocator without purge. // UNSUPPORTED: hwasan-aliasing +// Page size is hardcoded below, but test still fails even if not hardcoded. +// REQUIRES: page-size-4096 + #include #include #include diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/resize_tls_dynamic.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/resize_tls_dynamic.cpp index c288e1d69baf9..3e9ff924a3c4a 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/resize_tls_dynamic.cpp +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/resize_tls_dynamic.cpp @@ -11,6 +11,9 @@ // FIXME: Investigate // UNSUPPORTED: target=powerpc64{{.*}} +// Fails because AArch64 uses TLSDESC instead of __tls_get_addr. +// UNSUPPORTED: aarch64-target-arch + #include #ifndef BUILD_DSO diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/tls_get_addr.c b/compiler-rt/test/sanitizer_common/TestCases/Linux/tls_get_addr.c index 0aff6039ac4e8..a4a4f64ed3706 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/tls_get_addr.c +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/tls_get_addr.c @@ -13,6 +13,9 @@ // FIXME: Fails for unknown reasons. // UNSUPPORTED: powerpc64le-target-arch +// Fails because AArch64 uses TLSDESC instead of __tls_get_addr. +// UNSUPPORTED: aarch64-target-arch + #ifndef BUILD_SO # include # include From 68f118f265c933f6d19b115c09faad0aaa75e589 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 15 Aug 2025 16:29:33 -0700 Subject: [PATCH 10/47] Switch dtls_test.c from XFAIL to UNSUPPORTED on aarch64. It passes on some buildbots, so we can't expect failure. --- compiler-rt/test/msan/dtls_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-rt/test/msan/dtls_test.c b/compiler-rt/test/msan/dtls_test.c index 0e49ac9feb9fe..6daaab0ae0b8d 100644 --- a/compiler-rt/test/msan/dtls_test.c +++ b/compiler-rt/test/msan/dtls_test.c @@ -11,7 +11,7 @@ // Reports use-of-uninitialized-value, not analyzed XFAIL: target={{.*netbsd.*}} - XFAIL: aarch64-target-arch + UNSUPPORTED: aarch64-target-arch */ From c9fbd571b52c1659b915d6276a8d522df18cbc65 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 15 Aug 2025 13:43:16 -0700 Subject: [PATCH 11/47] dfsan: Fix test with gcc 15. With gcc 15 we end up emitting a reference to the std::__glibcxx_assert_fail function because of this change: https://github.com/gcc-mirror/gcc/commit/361d230fd7800a7e749aba8ed020f54f5c26d504 combined with assertion checks in the std::atomic implementation. This reference is undefined with dfsan causing the test to fail. Fix it by defining the macro that disables assertions. Pull Request: https://github.com/llvm/llvm-project/pull/153873 --- compiler-rt/test/dfsan/atomic.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler-rt/test/dfsan/atomic.cpp b/compiler-rt/test/dfsan/atomic.cpp index 22ee323c752f8..73e1cbd17a7cd 100644 --- a/compiler-rt/test/dfsan/atomic.cpp +++ b/compiler-rt/test/dfsan/atomic.cpp @@ -1,9 +1,12 @@ -// RUN: %clangxx_dfsan %s -fno-exceptions -o %t && %run %t -// RUN: %clangxx_dfsan -DORIGIN_TRACKING -mllvm -dfsan-track-origins=1 %s -fno-exceptions -o %t && %run %t +// RUN: %clangxx_dfsan %s -fno-exceptions -D_GLIBCXX_NO_ASSERTIONS -o %t && %run %t +// RUN: %clangxx_dfsan -DORIGIN_TRACKING -mllvm -dfsan-track-origins=1 %s -fno-exceptions -D_GLIBCXX_NO_ASSERTIONS -o %t && %run %t // // Use -fno-exceptions to turn off exceptions to avoid instrumenting // __cxa_begin_catch, std::terminate and __gxx_personality_v0. // +// Use -D_GLIBCXX_NO_ASSERTIONS to avoid depending on +// std::__glibcxx_assert_fail with gcc >= 15. +// // TODO: Support builtin atomics. For example, https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html // DFSan instrumentation pass cannot identify builtin callsites yet. From b54051ac74cb0b8a8bd9a39a80c9a004774587c9 Mon Sep 17 00:00:00 2001 From: owenca Date: Thu, 28 Aug 2025 07:40:42 -0700 Subject: [PATCH 12/47] [clang-format] Correctly annotate RequiresExpressionLBrace (#155773) Fixes #155746 (cherry picked from commit c62a5bf52de2953cbfb73e1df64092f37d3fc192) --- clang/lib/Format/TokenAnnotator.cpp | 2 +- clang/unittests/Format/TokenAnnotatorTest.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 580996e870f54..3ab3c45bbb052 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4007,7 +4007,7 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const { auto *Tok = Line.Last->Previous; while (Tok->isNot(tok::r_brace)) Tok = Tok->Previous; - if (auto *LBrace = Tok->MatchingParen; LBrace) { + if (auto *LBrace = Tok->MatchingParen; LBrace && LBrace->is(TT_Unknown)) { assert(LBrace->is(tok::l_brace)); Tok->setBlockKind(BK_Block); LBrace->setBlockKind(BK_Block); diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 259d7e54133a1..5ef7f5ba25a12 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1349,6 +1349,14 @@ TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) { EXPECT_EQ(Tokens[21]->MatchingParen, Tokens[15]); EXPECT_TRUE(Tokens[21]->ClosesRequiresClause); + Tokens = annotate("template \n" + "void Fun(const Foo &F)\n" + " requires requires(Foo F) {\n" + " { F.Bar() } -> std::same_as;\n" + " };"); + ASSERT_EQ(Tokens.size(), 38u) << Tokens; + EXPECT_TOKEN(Tokens[19], tok::l_brace, TT_RequiresExpressionLBrace); + Tokens = annotate("template concept C =" "std::same_as, std::iter_value_t>;"); From e14b5e82244e05115d077eefb8163e205064e244 Mon Sep 17 00:00:00 2001 From: owenca Date: Mon, 29 Sep 2025 21:11:20 -0700 Subject: [PATCH 13/47] [clang-format] Fix a bug in wrapping { after else (#161048) Fixes #160775 (cherry picked from commit 56d920d5868596f48a3a779dc88825594a646af3) --- clang/lib/Format/FormatToken.h | 2 +- clang/lib/Format/TokenAnnotator.cpp | 44 +++++++++++++++------------ clang/unittests/Format/FormatTest.cpp | 21 +++++++++++++ 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 9252a795a0b5e..c2000a971c898 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -55,7 +55,7 @@ namespace format { TYPE(ConflictAlternative) \ TYPE(ConflictEnd) \ TYPE(ConflictStart) \ - /* l_brace of if/for/while */ \ + /* l_brace of if/for/while/switch/catch */ \ TYPE(ControlStatementLBrace) \ TYPE(ControlStatementRBrace) \ TYPE(CppCastLParen) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 3ab3c45bbb052..ed8aa514aaf31 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4000,29 +4000,28 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const { } } - if (IsCpp && - (LineIsFunctionDeclaration || - (FirstNonComment && FirstNonComment->is(TT_CtorDtorDeclName))) && - Line.endsWith(tok::semi, tok::r_brace)) { - auto *Tok = Line.Last->Previous; - while (Tok->isNot(tok::r_brace)) - Tok = Tok->Previous; - if (auto *LBrace = Tok->MatchingParen; LBrace && LBrace->is(TT_Unknown)) { - assert(LBrace->is(tok::l_brace)); - Tok->setBlockKind(BK_Block); - LBrace->setBlockKind(BK_Block); - LBrace->setFinalizedType(TT_FunctionLBrace); + if (IsCpp) { + if ((LineIsFunctionDeclaration || + (FirstNonComment && FirstNonComment->is(TT_CtorDtorDeclName))) && + Line.endsWith(tok::semi, tok::r_brace)) { + auto *Tok = Line.Last->Previous; + while (Tok->isNot(tok::r_brace)) + Tok = Tok->Previous; + if (auto *LBrace = Tok->MatchingParen; LBrace && LBrace->is(TT_Unknown)) { + assert(LBrace->is(tok::l_brace)); + Tok->setBlockKind(BK_Block); + LBrace->setBlockKind(BK_Block); + LBrace->setFinalizedType(TT_FunctionLBrace); + } } - } - if (IsCpp && SeenName && AfterLastAttribute && - mustBreakAfterAttributes(*AfterLastAttribute, Style)) { - AfterLastAttribute->MustBreakBefore = true; - if (LineIsFunctionDeclaration) - Line.ReturnTypeWrapped = true; - } + if (SeenName && AfterLastAttribute && + mustBreakAfterAttributes(*AfterLastAttribute, Style)) { + AfterLastAttribute->MustBreakBefore = true; + if (LineIsFunctionDeclaration) + Line.ReturnTypeWrapped = true; + } - if (IsCpp) { if (!LineIsFunctionDeclaration) { // Annotate */&/&& in `operator` function calls as binary operators. for (const auto *Tok = FirstNonComment; Tok; Tok = Tok->Next) { @@ -4068,6 +4067,11 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const { } } + if (First->is(TT_ElseLBrace)) { + First->CanBreakBefore = true; + First->MustBreakBefore = true; + } + bool InFunctionDecl = Line.MightBeFunctionDecl; bool InParameterList = false; for (auto *Current = First->Next; Current; Current = Current->Next) { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index bda88115f09f7..c209b301ba6e5 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -1364,6 +1364,27 @@ TEST_F(FormatTest, FormatIfWithoutCompoundStatementButElseWith) { AllowsMergedIf); } +TEST_F(FormatTest, WrapMultipleStatementIfAndElseBraces) { + auto Style = getLLVMStyle(); + Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always; + Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_AllIfsAndElse; + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Always; + Style.BraceWrapping.BeforeElse = true; + + verifyFormat("if (x)\n" + "{\n" + " ++x;\n" + " --y;\n" + "}\n" + "else\n" + "{\n" + " --x;\n" + " ++y;\n" + "}", + Style); +} + TEST_F(FormatTest, FormatLoopsWithoutCompoundStatement) { verifyFormat("while (true)\n" " ;"); From a847f1832857a944e17dce054ee3ddaa68fda88b Mon Sep 17 00:00:00 2001 From: Fateme Hosseini <136356764+fhossein-quic@users.noreply.github.com> Date: Fri, 3 Oct 2025 13:44:35 -0500 Subject: [PATCH 14/47] [Hexagon] Support lowering of setuo & seto for vector types in Hexagon (#158740) Resolves instruction selection failure for v64f16 and v32f32 vector types. Patch by: Fateme Hosseini --------- Co-authored-by: Kaushik Kulkarni --- .../Target/Hexagon/HexagonISelLoweringHVX.cpp | 4 + .../test/CodeGen/Hexagon/inst_setcc_uno_uo.ll | 93 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 llvm/test/CodeGen/Hexagon/inst_setcc_uno_uo.ll diff --git a/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp b/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp index f1fa40c1b9036..48918fa00ae07 100644 --- a/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp +++ b/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp @@ -355,6 +355,8 @@ HexagonTargetLowering::initializeHVXLowering() { setCondCodeAction(ISD::SETULE, MVT::v64f16, Expand); setCondCodeAction(ISD::SETUGE, MVT::v64f16, Expand); setCondCodeAction(ISD::SETULT, MVT::v64f16, Expand); + setCondCodeAction(ISD::SETUO, MVT::v64f16, Expand); + setCondCodeAction(ISD::SETO, MVT::v64f16, Expand); setCondCodeAction(ISD::SETNE, MVT::v32f32, Expand); setCondCodeAction(ISD::SETLE, MVT::v32f32, Expand); @@ -368,6 +370,8 @@ HexagonTargetLowering::initializeHVXLowering() { setCondCodeAction(ISD::SETULE, MVT::v32f32, Expand); setCondCodeAction(ISD::SETUGE, MVT::v32f32, Expand); setCondCodeAction(ISD::SETULT, MVT::v32f32, Expand); + setCondCodeAction(ISD::SETUO, MVT::v32f32, Expand); + setCondCodeAction(ISD::SETO, MVT::v32f32, Expand); // Boolean vectors. diff --git a/llvm/test/CodeGen/Hexagon/inst_setcc_uno_uo.ll b/llvm/test/CodeGen/Hexagon/inst_setcc_uno_uo.ll new file mode 100644 index 0000000000000..8b121c539229d --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/inst_setcc_uno_uo.ll @@ -0,0 +1,93 @@ +;; RUN: llc --mtriple=hexagon -mattr=+hvxv79,+hvx-length128b %s -o - | FileCheck %s + +define dso_local void @store_isnan_f32(ptr %a, ptr %b, ptr %isnan_cmp) local_unnamed_addr { +entry: + %arrayidx_a = getelementptr inbounds nuw float, ptr %a, i32 0 + %arrayidx_b = getelementptr inbounds nuw float, ptr %b, i32 0 + %0 = load <32 x float>, ptr %arrayidx_a, align 4 + %1 = load <32 x float>, ptr %arrayidx_b, align 4 + %.vectorized = fcmp uno <32 x float> %0, %1 + %.LS.instance = zext <32 x i1> %.vectorized to <32 x i32> + %arrayidx1 = getelementptr inbounds nuw i32, ptr %isnan_cmp, i32 0 + store <32 x i32> %.LS.instance, ptr %arrayidx1, align 4 + ret void +} + +; CHECK: store_isnan_f32 +; CHECK: [[RONE32:r[0-9]+]] = #1 +; CHECK: [[VOP2_F32:v[0-9]+]] = vxor([[VOP2_F32]],[[VOP2_F32]]) +; CHECK: [[VOP1_F32:v[0-9]+]] = vmemu(r0+#0) +; CHECK: [[VONES32:v[0-9]+]] = vsplat([[RONE32]]) +; CHECK: [[Q1_F32:q[0-9]+]] = vcmp.eq([[VOP1_F32]].w,[[VOP1_F32]].w) +; CHECK: [[VOP3_F32:v[0-9]+]] = vmemu(r1+#0) +; CHECK: [[Q1_F32]] &= vcmp.eq([[VOP3_F32]].w,[[VOP3_F32]].w) +; CHECK: [[VOUT_F32:v[0-9]+]] = vmux([[Q1_F32]],[[VOP2_F32]],[[VONES32]]) +; CHECK: vmemu(r2+#0) = [[VOUT_F32]] + +define dso_local void @store_isnan_f16(ptr %a, ptr %b, ptr %isnan_cmp) local_unnamed_addr { +entry: + %arrayidx_a = getelementptr inbounds nuw half, ptr %a, i32 0 + %arrayidx_b = getelementptr inbounds nuw half, ptr %b, i32 0 + %0 = load <64 x half>, ptr %arrayidx_a, align 2 + %1 = load <64 x half>, ptr %arrayidx_b, align 2 + %.vectorized = fcmp uno <64 x half> %0, %1 + %conv.LS.instance = zext <64 x i1> %.vectorized to <64 x i16> + %arrayidx1 = getelementptr inbounds nuw i16, ptr %isnan_cmp, i32 0 + store <64 x i16> %conv.LS.instance, ptr %arrayidx1, align 2 + ret void +} +; CHECK-LABEL: store_isnan_f16 +; CHECK: [[RONE16:r[0-9]+]] = #1 +; CHECK: [[VOP2_F16:v[0-9]+]] = vxor([[VOP2_F16]],[[VOP2_F16]]) +; CHECK: [[VOP1_F16:v[0-9]+]] = vmemu(r0+#0) +; CHECK: [[VONES16:v[0-9]+]].h = vsplat([[RONE16]]) +; CHECK: [[Q1_F16:q[0-9]+]] = vcmp.eq([[VOP1_F16]].h,[[VOP1_F16]].h) +; CHECK: [[VOP3_F16:v[0-9]+]] = vmemu(r1+#0) +; CHECK: [[Q1_F16]] &= vcmp.eq([[VOP3_F16]].h,[[VOP3_F16]].h) +; CHECK: [[VOUT_F16:v[0-9]+]] = vmux([[Q1_F16]],[[VOP2_F16]],[[VONES16]]) +; CHECK: vmemu(r2+#0) = [[VOUT_F32]] + +define dso_local void @store_isordered_f32(ptr %a, ptr %b, ptr %isordered_cmp) local_unnamed_addr { +entry: + %arrayidx_a = getelementptr inbounds nuw float, ptr %a, i32 0 + %arrayidx_b = getelementptr inbounds nuw float, ptr %b, i32 0 + %0 = load <32 x float>, ptr %arrayidx_a, align 4 + %1 = load <32 x float>, ptr %arrayidx_b, align 4 + %.vectorized = fcmp ord <32 x float> %0, %1 + %.LS.instance = zext <32 x i1> %.vectorized to <32 x i32> + %arrayidx1 = getelementptr inbounds nuw i32, ptr %isordered_cmp, i32 0 + store <32 x i32> %.LS.instance, ptr %arrayidx1, align 4 + ret void +} +; CHECK-LABEL: store_isordered_f32 +; CHECK: [[VOP2_ORD_F32:v[0-9]+]] = vxor([[VOP2_ORD_F32]],[[VOP2_ORD_F32]]) +; CHECK: [[VOP1_ORD_F32:v[0-9]+]] = vmemu(r0+#0) +; CHECK: [[VONES_ORD_F32:v[0-9]+]] = vsplat([[RONE32]]) +; CHECK: [[Q1_ORD_F32:q[0-9]+]] = vcmp.eq([[VOP1_ORD_F32]].w,[[VOP1_ORD_F32]].w) +; CHECK: [[VOP3_ORD_F32:v[0-9]+]] = vmemu(r1+#0) +; CHECK: [[Q1_ORD_F32]] &= vcmp.eq([[VOP3_ORD_F32]].w,[[VOP3_ORD_F32]].w) +; CHECK: [[VOUT_ORD_F32:v[0-9]+]] = vmux([[Q1_ORD_F32]],[[VONES_ORD_F32]],[[VOP2_ORD_F32]]) +; CHECK: vmemu(r2+#0) = [[VOUT_ORD_F32]] + + +define dso_local void @store_isordered_f16(ptr %a, ptr %b, ptr %isordered_cmp) local_unnamed_addr { +entry: + %arrayidx_a = getelementptr inbounds nuw half, ptr %a, i32 0 + %arrayidx_b = getelementptr inbounds nuw half, ptr %b, i32 0 + %0 = load <64 x half>, ptr %arrayidx_a, align 2 + %1 = load <64 x half>, ptr %arrayidx_b, align 2 + %.vectorized = fcmp ord <64 x half> %0, %1 + %conv.LS.instance = zext <64 x i1> %.vectorized to <64 x i16> + %arrayidx1 = getelementptr inbounds nuw i16, ptr %isordered_cmp, i32 0 + store <64 x i16> %conv.LS.instance, ptr %arrayidx1, align 2 + ret void +} +; CHECK-LABEL: store_isordered_f16 +; CHECK: [[VOP2_ORD_F16:v[0-9]+]] = vxor([[VOP2_ORD_F16]],[[VOP2_ORD_F16]]) +; CHECK: [[VOP1_ORD_F16:v[0-9]+]] = vmemu(r0+#0) +; CHECK: [[VONES_ORD_F16:v[0-9]+]].h = vsplat([[RONE16]]) +; CHECK: [[Q1_ORD_F16:q[0-9]+]] = vcmp.eq([[VOP1_ORD_F16]].h,[[VOP1_ORD_F16]].h) +; CHECK: [[VOP3_ORD_F16:v[0-9]+]] = vmemu(r1+#0) +; CHECK: [[Q1_ORD_F16]] &= vcmp.eq([[VOP3_ORD_F16]].h,[[VOP3_ORD_F16]].h) +; CHECK: [[VOUT_ORD_F16:v[0-9]+]] = vmux([[Q1_ORD_F16]],[[VONES_ORD_F16]],[[VOP2_ORD_F16]]) +; CHECK: vmemu(r2+#0) = [[VOUT_ORD_F16]] From 7b785dcb70f6ab2b8ecf1b75a9fc7cf2512b8ab6 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Fri, 3 Oct 2025 17:17:23 +0200 Subject: [PATCH 15/47] [LLD][COFF] Fix tailMergeARM64 delayload thunk 128 MB range limitation (#161844) lld would fail with "error: relocation out of range" if the thunk was laid out more than 128 MB away from __delayLoadHelper2. This patch changes the call sequence to load the offset into a register and call through that, allowing for 32-bit offsets. Fixes #161812 (cherry picked from commit 69b8d6d4ead01b88fb8d6642914ca7492e32fdb6) --- lld/COFF/DLL.cpp | 10 ++++-- lld/test/COFF/arm64-delayimport.yaml | 26 +++++++------- lld/test/COFF/arm64x-delayimport.test | 52 ++++++++++++++------------- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp index 3ce8853adb2a2..f4284efee8d4d 100644 --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -333,7 +333,9 @@ static const uint8_t tailMergeARM64[] = { 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR - 0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2 + 0x02, 0x00, 0x00, 0x90, // adrp x2, #0 __delayLoadHelper2 + 0x42, 0x00, 0x00, 0x91, // add x2, x2, #0 :lo12:__delayLoadHelper2 + 0x40, 0x00, 0x3f, 0xd6, // blr x2 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176] 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144] @@ -556,8 +558,10 @@ class TailMergeChunkARM64 : public NonSectionCodeChunk { memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64)); applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12); applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0); - if (helper) - applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52); + if (helper) { + applyArm64Addr(buf + 52, helper->getRVA(), rva + 52, 12); + applyArm64Imm(buf + 56, helper->getRVA() & 0xfff, 0); + } } Chunk *desc = nullptr; diff --git a/lld/test/COFF/arm64-delayimport.yaml b/lld/test/COFF/arm64-delayimport.yaml index abb9f25d5c379..7090206dea38a 100644 --- a/lld/test/COFF/arm64-delayimport.yaml +++ b/lld/test/COFF/arm64-delayimport.yaml @@ -21,18 +21,20 @@ # DISASM: 140001048: aa1103e1 mov x1, x17 # DISASM: 14000104c: b0000000 adrp x0, 0x140002000 # DISASM: 140001050: 91000000 add x0, x0, #0 -# DISASM: 140001054: 97ffffeb bl 0x140001000 <.text> -# DISASM: 140001058: aa0003f0 mov x16, x0 -# DISASM: 14000105c: ad459fe6 ldp q6, q7, [sp, #176] -# DISASM: 140001060: ad4497e4 ldp q4, q5, [sp, #144] -# DISASM: 140001064: ad438fe2 ldp q2, q3, [sp, #112] -# DISASM: 140001068: ad4287e0 ldp q0, q1, [sp, #80] -# DISASM: 14000106c: a9441fe6 ldp x6, x7, [sp, #64] -# DISASM: 140001070: a94317e4 ldp x4, x5, [sp, #48] -# DISASM: 140001074: a9420fe2 ldp x2, x3, [sp, #32] -# DISASM: 140001078: a94107e0 ldp x0, x1, [sp, #16] -# DISASM: 14000107c: a8cd7bfd ldp x29, x30, [sp], #208 -# DISASM: 140001080: d61f0200 br x16 +# DISASM: 140001054: 90000002 adrp x2, 0x140001000 <.text> +# DISASM: 140001058: 91000042 add x2, x2, #0 +# DISASM: 14000105c: d63f0040 blr x2 +# DISASM: 140001060: aa0003f0 mov x16, x0 +# DISASM: 140001064: ad459fe6 ldp q6, q7, [sp, #176] +# DISASM: 140001068: ad4497e4 ldp q4, q5, [sp, #144] +# DISASM: 14000106c: ad438fe2 ldp q2, q3, [sp, #112] +# DISASM: 140001070: ad4287e0 ldp q0, q1, [sp, #80] +# DISASM: 140001074: a9441fe6 ldp x6, x7, [sp, #64] +# DISASM: 140001078: a94317e4 ldp x4, x5, [sp, #48] +# DISASM: 14000107c: a9420fe2 ldp x2, x3, [sp, #32] +# DISASM: 140001080: a94107e0 ldp x0, x1, [sp, #16] +# DISASM: 140001084: a8cd7bfd ldp x29, x30, [sp], #208 +# DISASM: 140001088: d61f0200 br x16 # IMPORTS: Format: COFF-ARM64 # IMPORTS: Arch: aarch64 diff --git a/lld/test/COFF/arm64x-delayimport.test b/lld/test/COFF/arm64x-delayimport.test index 2a68bce79baad..e22cc6d5c42fc 100644 --- a/lld/test/COFF/arm64x-delayimport.test +++ b/lld/test/COFF/arm64x-delayimport.test @@ -74,18 +74,20 @@ DISASM-NEXT: 180001044: ad059fe6 stp q6, q7, [sp, #0xb0] DISASM-NEXT: 180001048: aa1103e1 mov x1, x17 DISASM-NEXT: 18000104c: f0000000 adrp x0, 0x180004000 DISASM-NEXT: 180001050: 910d2000 add x0, x0, #0x348 -DISASM-NEXT: 180001054: 97ffffeb bl 0x180001000 <.text> -DISASM-NEXT: 180001058: aa0003f0 mov x16, x0 -DISASM-NEXT: 18000105c: ad459fe6 ldp q6, q7, [sp, #0xb0] -DISASM-NEXT: 180001060: ad4497e4 ldp q4, q5, [sp, #0x90] -DISASM-NEXT: 180001064: ad438fe2 ldp q2, q3, [sp, #0x70] -DISASM-NEXT: 180001068: ad4287e0 ldp q0, q1, [sp, #0x50] -DISASM-NEXT: 18000106c: a9441fe6 ldp x6, x7, [sp, #0x40] -DISASM-NEXT: 180001070: a94317e4 ldp x4, x5, [sp, #0x30] -DISASM-NEXT: 180001074: a9420fe2 ldp x2, x3, [sp, #0x20] -DISASM-NEXT: 180001078: a94107e0 ldp x0, x1, [sp, #0x10] -DISASM-NEXT: 18000107c: a8cd7bfd ldp x29, x30, [sp], #0xd0 -DISASM-NEXT: 180001080: d61f0200 br x16 +DISASM-NEXT: 180001054: 90000002 adrp x2, 0x180001000 <.text> +DISASM-NEXT: 180001058: 91000042 add x2, x2, #0x0 +DISASM-NEXT: 18000105c: d63f0040 blr x2 +DISASM-NEXT: 180001060: aa0003f0 mov x16, x0 +DISASM-NEXT: 180001064: ad459fe6 ldp q6, q7, [sp, #0xb0] +DISASM-NEXT: 180001068: ad4497e4 ldp q4, q5, [sp, #0x90] +DISASM-NEXT: 18000106c: ad438fe2 ldp q2, q3, [sp, #0x70] +DISASM-NEXT: 180001070: ad4287e0 ldp q0, q1, [sp, #0x50] +DISASM-NEXT: 180001074: a9441fe6 ldp x6, x7, [sp, #0x40] +DISASM-NEXT: 180001078: a94317e4 ldp x4, x5, [sp, #0x30] +DISASM-NEXT: 18000107c: a9420fe2 ldp x2, x3, [sp, #0x20] +DISASM-NEXT: 180001080: a94107e0 ldp x0, x1, [sp, #0x10] +DISASM-NEXT: 180001084: a8cd7bfd ldp x29, x30, [sp], #0xd0 +DISASM-NEXT: 180001088: d61f0200 br x16 DISASM-NEXT: ... DISASM-NEXT: 180002000: 52800040 mov w0, #0x2 // =2 DISASM-NEXT: 180002004: d65f03c0 ret @@ -197,18 +199,20 @@ NATIVE-DISASM-NEXT: 180001044: ad059fe6 stp q6, q7, [sp, #0xb0] NATIVE-DISASM-NEXT: 180001048: aa1103e1 mov x1, x17 NATIVE-DISASM-NEXT: 18000104c: d0000000 adrp x0, 0x180003000 NATIVE-DISASM-NEXT: 180001050: 910cc000 add x0, x0, #0x330 -NATIVE-DISASM-NEXT: 180001054: 97ffffeb bl 0x180001000 <.text> -NATIVE-DISASM-NEXT: 180001058: aa0003f0 mov x16, x0 -NATIVE-DISASM-NEXT: 18000105c: ad459fe6 ldp q6, q7, [sp, #0xb0] -NATIVE-DISASM-NEXT: 180001060: ad4497e4 ldp q4, q5, [sp, #0x90] -NATIVE-DISASM-NEXT: 180001064: ad438fe2 ldp q2, q3, [sp, #0x70] -NATIVE-DISASM-NEXT: 180001068: ad4287e0 ldp q0, q1, [sp, #0x50] -NATIVE-DISASM-NEXT: 18000106c: a9441fe6 ldp x6, x7, [sp, #0x40] -NATIVE-DISASM-NEXT: 180001070: a94317e4 ldp x4, x5, [sp, #0x30] -NATIVE-DISASM-NEXT: 180001074: a9420fe2 ldp x2, x3, [sp, #0x20] -NATIVE-DISASM-NEXT: 180001078: a94107e0 ldp x0, x1, [sp, #0x10] -NATIVE-DISASM-NEXT: 18000107c: a8cd7bfd ldp x29, x30, [sp], #0xd0 -NATIVE-DISASM-NEXT: 180001080: d61f0200 br x16 +NATIVE-DISASM-NEXT: 180001054: 90000002 adrp x2, 0x180001000 <.text> +NATIVE-DISASM-NEXT: 180001058: 91000042 add x2, x2, #0x0 +NATIVE-DISASM-NEXT: 18000105c: d63f0040 blr x2 +NATIVE-DISASM-NEXT: 180001060: aa0003f0 mov x16, x0 +NATIVE-DISASM-NEXT: 180001064: ad459fe6 ldp q6, q7, [sp, #0xb0] +NATIVE-DISASM-NEXT: 180001068: ad4497e4 ldp q4, q5, [sp, #0x90] +NATIVE-DISASM-NEXT: 18000106c: ad438fe2 ldp q2, q3, [sp, #0x70] +NATIVE-DISASM-NEXT: 180001070: ad4287e0 ldp q0, q1, [sp, #0x50] +NATIVE-DISASM-NEXT: 180001074: a9441fe6 ldp x6, x7, [sp, #0x40] +NATIVE-DISASM-NEXT: 180001078: a94317e4 ldp x4, x5, [sp, #0x30] +NATIVE-DISASM-NEXT: 18000107c: a9420fe2 ldp x2, x3, [sp, #0x20] +NATIVE-DISASM-NEXT: 180001080: a94107e0 ldp x0, x1, [sp, #0x10] +NATIVE-DISASM-NEXT: 180001084: a8cd7bfd ldp x29, x30, [sp], #0xd0 +NATIVE-DISASM-NEXT: 180001088: d61f0200 br x16 RUN: llvm-readobj --coff-load-config out-native.dll | FileCheck --check-prefix=NATIVE-LOADCFG %s NATIVE-LOADCFG: AuxiliaryDelayloadIAT: 0x4000 From bd9bc536b4ac7147142d1fc21915c4e6868f7d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Mon, 13 Oct 2025 23:01:17 +0300 Subject: [PATCH 16/47] [LLD] [COFF] Fix aarch64 delayimport of sret arguments (#163096) For sret arguments on aarch64, the x8 register is used as input parameter to functions, even though x8 normally isn't an input parameter register. When delayloading a DLL, the first call of a delayloaded function ends up calling a helper which resolves the function. Therefore, any input arguments to the actual function to be called need to be backed up and restored - this also includes x8. This matches how MS link.exe also changed its delayloading trampoline, between MSVC 2019 16.7 and 16.8 (between link.exe 14.27.29110.0 and 14.28.29333.0). This fixes running LLDB on aarch64 mingw, after ec28b95b7491bc2fbb6ec66cdbfd939e71255c42 and 93d326038959fd87fb666a8bf97d774d0abb3591. Those commits make LLDB load liblldb.dll with delayloading, and the first function to be called, SBDebugger::InitializeWithErrorHandling(), returns an SBError, which in the itanium C++ ABI is returned as an sret via a pointer in x8. (cherry picked from commit 7e690517bceea62a6b9c7e05622fb48bb6316efc) --- lld/COFF/DLL.cpp | 30 +++++---- lld/test/COFF/arm64-delayimport.yaml | 46 +++++++------- lld/test/COFF/arm64x-delayimport.test | 92 ++++++++++++++------------- 3 files changed, 88 insertions(+), 80 deletions(-) diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp index f4284efee8d4d..10bc898244a4a 100644 --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -320,16 +320,17 @@ static const uint8_t thunkARM64[] = { }; static const uint8_t tailMergeARM64[] = { - 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]! + 0xfd, 0x7b, 0xb2, 0xa9, // stp x29, x30, [sp, #-224]! 0xfd, 0x03, 0x00, 0x91, // mov x29, sp 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16] 0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32] 0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48] 0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64] - 0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80] - 0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112] - 0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144] - 0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176] + 0xe8, 0x2b, 0x00, 0xf9, // str x8, [sp, #80] + 0xe0, 0x07, 0x03, 0xad, // stp q0, q1, [sp, #96] + 0xe2, 0x0f, 0x04, 0xad, // stp q2, q3, [sp, #128] + 0xe4, 0x17, 0x05, 0xad, // stp q4, q5, [sp, #160] + 0xe6, 0x1f, 0x06, 0xad, // stp q6, q7, [sp, #192] 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR @@ -337,15 +338,16 @@ static const uint8_t tailMergeARM64[] = { 0x42, 0x00, 0x00, 0x91, // add x2, x2, #0 :lo12:__delayLoadHelper2 0x40, 0x00, 0x3f, 0xd6, // blr x2 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0 - 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176] - 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144] - 0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112] - 0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80] + 0xe6, 0x1f, 0x46, 0xad, // ldp q6, q7, [sp, #192] + 0xe4, 0x17, 0x45, 0xad, // ldp q4, q5, [sp, #160] + 0xe2, 0x0f, 0x44, 0xad, // ldp q2, q3, [sp, #128] + 0xe0, 0x07, 0x43, 0xad, // ldp q0, q1, [sp, #96] + 0xe8, 0x2b, 0x40, 0xf9, // ldr x8, [sp, #80] 0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64] 0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48] 0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32] 0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16] - 0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208 + 0xfd, 0x7b, 0xce, 0xa8, // ldp x29, x30, [sp], #224 0x00, 0x02, 0x1f, 0xd6, // br x16 }; @@ -556,11 +558,11 @@ class TailMergeChunkARM64 : public NonSectionCodeChunk { void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64)); - applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12); - applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0); + applyArm64Addr(buf + 48, desc->getRVA(), rva + 48, 12); + applyArm64Imm(buf + 52, desc->getRVA() & 0xfff, 0); if (helper) { - applyArm64Addr(buf + 52, helper->getRVA(), rva + 52, 12); - applyArm64Imm(buf + 56, helper->getRVA() & 0xfff, 0); + applyArm64Addr(buf + 56, helper->getRVA(), rva + 56, 12); + applyArm64Imm(buf + 60, helper->getRVA() & 0xfff, 0); } } diff --git a/lld/test/COFF/arm64-delayimport.yaml b/lld/test/COFF/arm64-delayimport.yaml index 7090206dea38a..5d26978db8be7 100644 --- a/lld/test/COFF/arm64-delayimport.yaml +++ b/lld/test/COFF/arm64-delayimport.yaml @@ -8,33 +8,35 @@ # DISASM: 140001014: d0000011 adrp x17, 0x140003000 # DISASM: 140001018: 91002231 add x17, x17, #8 # DISASM: 14000101c: 14000001 b 0x140001020 <.text+0x20> -# DISASM: 140001020: a9b37bfd stp x29, x30, [sp, #-208]! +# DISASM: 140001020: a9b27bfd stp x29, x30, [sp, #-224]! # DISASM: 140001024: 910003fd mov x29, sp # DISASM: 140001028: a90107e0 stp x0, x1, [sp, #16] # DISASM: 14000102c: a9020fe2 stp x2, x3, [sp, #32] # DISASM: 140001030: a90317e4 stp x4, x5, [sp, #48] # DISASM: 140001034: a9041fe6 stp x6, x7, [sp, #64] -# DISASM: 140001038: ad0287e0 stp q0, q1, [sp, #80] -# DISASM: 14000103c: ad038fe2 stp q2, q3, [sp, #112] -# DISASM: 140001040: ad0497e4 stp q4, q5, [sp, #144] -# DISASM: 140001044: ad059fe6 stp q6, q7, [sp, #176] -# DISASM: 140001048: aa1103e1 mov x1, x17 -# DISASM: 14000104c: b0000000 adrp x0, 0x140002000 -# DISASM: 140001050: 91000000 add x0, x0, #0 -# DISASM: 140001054: 90000002 adrp x2, 0x140001000 <.text> -# DISASM: 140001058: 91000042 add x2, x2, #0 -# DISASM: 14000105c: d63f0040 blr x2 -# DISASM: 140001060: aa0003f0 mov x16, x0 -# DISASM: 140001064: ad459fe6 ldp q6, q7, [sp, #176] -# DISASM: 140001068: ad4497e4 ldp q4, q5, [sp, #144] -# DISASM: 14000106c: ad438fe2 ldp q2, q3, [sp, #112] -# DISASM: 140001070: ad4287e0 ldp q0, q1, [sp, #80] -# DISASM: 140001074: a9441fe6 ldp x6, x7, [sp, #64] -# DISASM: 140001078: a94317e4 ldp x4, x5, [sp, #48] -# DISASM: 14000107c: a9420fe2 ldp x2, x3, [sp, #32] -# DISASM: 140001080: a94107e0 ldp x0, x1, [sp, #16] -# DISASM: 140001084: a8cd7bfd ldp x29, x30, [sp], #208 -# DISASM: 140001088: d61f0200 br x16 +# DISASM: 140001038: f9002be8 str x8, [sp, #80] +# DISASM: 14000103c: ad0307e0 stp q0, q1, [sp, #96] +# DISASM: 140001040: ad040fe2 stp q2, q3, [sp, #128] +# DISASM: 140001044: ad0517e4 stp q4, q5, [sp, #160] +# DISASM: 140001048: ad061fe6 stp q6, q7, [sp, #192] +# DISASM: 14000104c: aa1103e1 mov x1, x17 +# DISASM: 140001050: b0000000 adrp x0, 0x140002000 +# DISASM: 140001054: 91000000 add x0, x0, #0 +# DISASM: 140001058: 90000002 adrp x2, 0x140001000 <.text> +# DISASM: 14000105c: 91000042 add x2, x2, #0 +# DISASM: 140001060: d63f0040 blr x2 +# DISASM: 140001064: aa0003f0 mov x16, x0 +# DISASM: 140001068: ad461fe6 ldp q6, q7, [sp, #192] +# DISASM: 14000106c: ad4517e4 ldp q4, q5, [sp, #160] +# DISASM: 140001070: ad440fe2 ldp q2, q3, [sp, #128] +# DISASM: 140001074: ad4307e0 ldp q0, q1, [sp, #96] +# DISASM: 140001078: f9402be8 ldr x8, [sp, #80] +# DISASM: 14000107c: a9441fe6 ldp x6, x7, [sp, #64] +# DISASM: 140001080: a94317e4 ldp x4, x5, [sp, #48] +# DISASM: 140001084: a9420fe2 ldp x2, x3, [sp, #32] +# DISASM: 140001088: a94107e0 ldp x0, x1, [sp, #16] +# DISASM: 14000108c: a8ce7bfd ldp x29, x30, [sp], #224 +# DISASM: 140001090: d61f0200 br x16 # IMPORTS: Format: COFF-ARM64 # IMPORTS: Arch: aarch64 diff --git a/lld/test/COFF/arm64x-delayimport.test b/lld/test/COFF/arm64x-delayimport.test index e22cc6d5c42fc..e705fb0efc455 100644 --- a/lld/test/COFF/arm64x-delayimport.test +++ b/lld/test/COFF/arm64x-delayimport.test @@ -61,33 +61,35 @@ DISASM-NEXT: 180001010: d61f0200 br x16 DISASM-NEXT: 180001014: b0000031 adrp x17, 0x180006000 DISASM-NEXT: 180001018: 91022231 add x17, x17, #0x88 DISASM-NEXT: 18000101c: 14000001 b 0x180001020 <.text+0x20> -DISASM-NEXT: 180001020: a9b37bfd stp x29, x30, [sp, #-0xd0]! +DISASM-NEXT: 180001020: a9b27bfd stp x29, x30, [sp, #-0xe0]! DISASM-NEXT: 180001024: 910003fd mov x29, sp DISASM-NEXT: 180001028: a90107e0 stp x0, x1, [sp, #0x10] DISASM-NEXT: 18000102c: a9020fe2 stp x2, x3, [sp, #0x20] DISASM-NEXT: 180001030: a90317e4 stp x4, x5, [sp, #0x30] DISASM-NEXT: 180001034: a9041fe6 stp x6, x7, [sp, #0x40] -DISASM-NEXT: 180001038: ad0287e0 stp q0, q1, [sp, #0x50] -DISASM-NEXT: 18000103c: ad038fe2 stp q2, q3, [sp, #0x70] -DISASM-NEXT: 180001040: ad0497e4 stp q4, q5, [sp, #0x90] -DISASM-NEXT: 180001044: ad059fe6 stp q6, q7, [sp, #0xb0] -DISASM-NEXT: 180001048: aa1103e1 mov x1, x17 -DISASM-NEXT: 18000104c: f0000000 adrp x0, 0x180004000 -DISASM-NEXT: 180001050: 910d2000 add x0, x0, #0x348 -DISASM-NEXT: 180001054: 90000002 adrp x2, 0x180001000 <.text> -DISASM-NEXT: 180001058: 91000042 add x2, x2, #0x0 -DISASM-NEXT: 18000105c: d63f0040 blr x2 -DISASM-NEXT: 180001060: aa0003f0 mov x16, x0 -DISASM-NEXT: 180001064: ad459fe6 ldp q6, q7, [sp, #0xb0] -DISASM-NEXT: 180001068: ad4497e4 ldp q4, q5, [sp, #0x90] -DISASM-NEXT: 18000106c: ad438fe2 ldp q2, q3, [sp, #0x70] -DISASM-NEXT: 180001070: ad4287e0 ldp q0, q1, [sp, #0x50] -DISASM-NEXT: 180001074: a9441fe6 ldp x6, x7, [sp, #0x40] -DISASM-NEXT: 180001078: a94317e4 ldp x4, x5, [sp, #0x30] -DISASM-NEXT: 18000107c: a9420fe2 ldp x2, x3, [sp, #0x20] -DISASM-NEXT: 180001080: a94107e0 ldp x0, x1, [sp, #0x10] -DISASM-NEXT: 180001084: a8cd7bfd ldp x29, x30, [sp], #0xd0 -DISASM-NEXT: 180001088: d61f0200 br x16 +DISASM-NEXT: 180001038: f9002be8 str x8, [sp, #0x50] +DISASM-NEXT: 18000103c: ad0307e0 stp q0, q1, [sp, #0x60] +DISASM-NEXT: 180001040: ad040fe2 stp q2, q3, [sp, #0x80] +DISASM-NEXT: 180001044: ad0517e4 stp q4, q5, [sp, #0xa0] +DISASM-NEXT: 180001048: ad061fe6 stp q6, q7, [sp, #0xc0] +DISASM-NEXT: 18000104c: aa1103e1 mov x1, x17 +DISASM-NEXT: 180001050: f0000000 adrp x0, 0x180004000 +DISASM-NEXT: 180001054: 910d2000 add x0, x0, #0x348 +DISASM-NEXT: 180001058: 90000002 adrp x2, 0x180001000 <.text> +DISASM-NEXT: 18000105c: 91000042 add x2, x2, #0x0 +DISASM-NEXT: 180001060: d63f0040 blr x2 +DISASM-NEXT: 180001064: aa0003f0 mov x16, x0 +DISASM-NEXT: 180001068: ad461fe6 ldp q6, q7, [sp, #0xc0] +DISASM-NEXT: 18000106c: ad4517e4 ldp q4, q5, [sp, #0xa0] +DISASM-NEXT: 180001070: ad440fe2 ldp q2, q3, [sp, #0x80] +DISASM-NEXT: 180001074: ad4307e0 ldp q0, q1, [sp, #0x60] +DISASM-NEXT: 180001078: f9402be8 ldr x8, [sp, #0x50] +DISASM-NEXT: 18000107c: a9441fe6 ldp x6, x7, [sp, #0x40] +DISASM-NEXT: 180001080: a94317e4 ldp x4, x5, [sp, #0x30] +DISASM-NEXT: 180001084: a9420fe2 ldp x2, x3, [sp, #0x20] +DISASM-NEXT: 180001088: a94107e0 ldp x0, x1, [sp, #0x10] +DISASM-NEXT: 18000108c: a8ce7bfd ldp x29, x30, [sp], #0xe0 +DISASM-NEXT: 180001090: d61f0200 br x16 DISASM-NEXT: ... DISASM-NEXT: 180002000: 52800040 mov w0, #0x2 // =2 DISASM-NEXT: 180002004: d65f03c0 ret @@ -186,33 +188,35 @@ NATIVE-DISASM-NEXT: 180001010: d61f0200 br x16 NATIVE-DISASM-NEXT: 180001014: 90000031 adrp x17, 0x180005000 NATIVE-DISASM-NEXT: 180001018: 91022231 add x17, x17, #0x88 NATIVE-DISASM-NEXT: 18000101c: 14000001 b 0x180001020 <.text+0x20> -NATIVE-DISASM-NEXT: 180001020: a9b37bfd stp x29, x30, [sp, #-0xd0]! +NATIVE-DISASM-NEXT: 180001020: a9b27bfd stp x29, x30, [sp, #-0xe0]! NATIVE-DISASM-NEXT: 180001024: 910003fd mov x29, sp NATIVE-DISASM-NEXT: 180001028: a90107e0 stp x0, x1, [sp, #0x10] NATIVE-DISASM-NEXT: 18000102c: a9020fe2 stp x2, x3, [sp, #0x20] NATIVE-DISASM-NEXT: 180001030: a90317e4 stp x4, x5, [sp, #0x30] NATIVE-DISASM-NEXT: 180001034: a9041fe6 stp x6, x7, [sp, #0x40] -NATIVE-DISASM-NEXT: 180001038: ad0287e0 stp q0, q1, [sp, #0x50] -NATIVE-DISASM-NEXT: 18000103c: ad038fe2 stp q2, q3, [sp, #0x70] -NATIVE-DISASM-NEXT: 180001040: ad0497e4 stp q4, q5, [sp, #0x90] -NATIVE-DISASM-NEXT: 180001044: ad059fe6 stp q6, q7, [sp, #0xb0] -NATIVE-DISASM-NEXT: 180001048: aa1103e1 mov x1, x17 -NATIVE-DISASM-NEXT: 18000104c: d0000000 adrp x0, 0x180003000 -NATIVE-DISASM-NEXT: 180001050: 910cc000 add x0, x0, #0x330 -NATIVE-DISASM-NEXT: 180001054: 90000002 adrp x2, 0x180001000 <.text> -NATIVE-DISASM-NEXT: 180001058: 91000042 add x2, x2, #0x0 -NATIVE-DISASM-NEXT: 18000105c: d63f0040 blr x2 -NATIVE-DISASM-NEXT: 180001060: aa0003f0 mov x16, x0 -NATIVE-DISASM-NEXT: 180001064: ad459fe6 ldp q6, q7, [sp, #0xb0] -NATIVE-DISASM-NEXT: 180001068: ad4497e4 ldp q4, q5, [sp, #0x90] -NATIVE-DISASM-NEXT: 18000106c: ad438fe2 ldp q2, q3, [sp, #0x70] -NATIVE-DISASM-NEXT: 180001070: ad4287e0 ldp q0, q1, [sp, #0x50] -NATIVE-DISASM-NEXT: 180001074: a9441fe6 ldp x6, x7, [sp, #0x40] -NATIVE-DISASM-NEXT: 180001078: a94317e4 ldp x4, x5, [sp, #0x30] -NATIVE-DISASM-NEXT: 18000107c: a9420fe2 ldp x2, x3, [sp, #0x20] -NATIVE-DISASM-NEXT: 180001080: a94107e0 ldp x0, x1, [sp, #0x10] -NATIVE-DISASM-NEXT: 180001084: a8cd7bfd ldp x29, x30, [sp], #0xd0 -NATIVE-DISASM-NEXT: 180001088: d61f0200 br x16 +NATIVE-DISASM-NEXT: 180001038: f9002be8 str x8, [sp, #0x50] +NATIVE-DISASM-NEXT: 18000103c: ad0307e0 stp q0, q1, [sp, #0x60] +NATIVE-DISASM-NEXT: 180001040: ad040fe2 stp q2, q3, [sp, #0x80] +NATIVE-DISASM-NEXT: 180001044: ad0517e4 stp q4, q5, [sp, #0xa0] +NATIVE-DISASM-NEXT: 180001048: ad061fe6 stp q6, q7, [sp, #0xc0] +NATIVE-DISASM-NEXT: 18000104c: aa1103e1 mov x1, x17 +NATIVE-DISASM-NEXT: 180001050: d0000000 adrp x0, 0x180003000 +NATIVE-DISASM-NEXT: 180001054: 910cc000 add x0, x0, #0x330 +NATIVE-DISASM-NEXT: 180001058: 90000002 adrp x2, 0x180001000 <.text> +NATIVE-DISASM-NEXT: 18000105c: 91000042 add x2, x2, #0x0 +NATIVE-DISASM-NEXT: 180001060: d63f0040 blr x2 +NATIVE-DISASM-NEXT: 180001064: aa0003f0 mov x16, x0 +NATIVE-DISASM-NEXT: 180001068: ad461fe6 ldp q6, q7, [sp, #0xc0] +NATIVE-DISASM-NEXT: 18000106c: ad4517e4 ldp q4, q5, [sp, #0xa0] +NATIVE-DISASM-NEXT: 180001070: ad440fe2 ldp q2, q3, [sp, #0x80] +NATIVE-DISASM-NEXT: 180001074: ad4307e0 ldp q0, q1, [sp, #0x60] +NATIVE-DISASM-NEXT: 180001078: f9402be8 ldr x8, [sp, #0x50] +NATIVE-DISASM-NEXT: 18000107c: a9441fe6 ldp x6, x7, [sp, #0x40] +NATIVE-DISASM-NEXT: 180001080: a94317e4 ldp x4, x5, [sp, #0x30] +NATIVE-DISASM-NEXT: 180001084: a9420fe2 ldp x2, x3, [sp, #0x20] +NATIVE-DISASM-NEXT: 180001088: a94107e0 ldp x0, x1, [sp, #0x10] +NATIVE-DISASM-NEXT: 18000108c: a8ce7bfd ldp x29, x30, [sp], #0xe0 +NATIVE-DISASM-NEXT: 180001090: d61f0200 br x16 RUN: llvm-readobj --coff-load-config out-native.dll | FileCheck --check-prefix=NATIVE-LOADCFG %s NATIVE-LOADCFG: AuxiliaryDelayloadIAT: 0x4000 From dfdee9a929aa2d3ca75534da48f25076bc634bc5 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Mon, 13 Oct 2025 13:38:38 -0700 Subject: [PATCH 17/47] [clang][modules] Derive mtime from PCM timestamps, not PCM files (#162965) #137363 was supposed to be NFC for the `CrossProcessModuleCache` (a.k.a normal implicit module builds), but accidentally passed the wrong path to `sys::fs::status`. Then, #141358 removed the correct path that should've been passed instead. (The variable was flagged as unused.) None of our existing tests caught this regression, we only found out due to a SourceKit-LSP benchmark getting slower. This PR re-implements the original behavior, adds new remark to Clang for PCM input file validation, and uses it to create more reliable tests of the `-fmodules-validate-once-per-build-session` flag. (cherry picked from commit ce8abef25e242e65e459bcb7d9998a4e85ae03d2) --- clang/include/clang/Basic/DiagnosticGroups.td | 1 + .../Basic/DiagnosticSerializationKinds.td | 4 + clang/lib/Serialization/ASTReader.cpp | 4 + clang/lib/Serialization/ModuleCache.cpp | 4 +- ...fmodules-validate-once-per-build-session.c | 235 ++++++++++-------- 5 files changed, 137 insertions(+), 111 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index c28a919e35d08..76f9addab47d8 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -624,6 +624,7 @@ def MissingFieldInitializers : DiagGroup<"missing-field-initializers", def ModuleLock : DiagGroup<"module-lock">; def ModuleBuild : DiagGroup<"module-build">; def ModuleImport : DiagGroup<"module-import">; +def ModuleValidation : DiagGroup<"module-validation">; def ModuleConflict : DiagGroup<"module-conflict">; def ModuleFileExtension : DiagGroup<"module-file-extension">; def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">; diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 584c8d62280bf..6494f3415b7a5 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -82,6 +82,10 @@ def remark_module_import : Remark< "importing module '%0'%select{| into '%3'}2 from '%1'">, ShowInSystemHeader, InGroup; +def remark_module_validation : Remark< + "validating %0 input files in module '%1' from '%2'">, + ShowInSystemHeader, + InGroup; def err_imported_module_not_found : Error< "module '%0' in precompiled file '%1' %select{(imported by precompiled file '%2') |}4" diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 30e0973149594..3e7ccfdd9db5d 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3103,6 +3103,10 @@ ASTReader::ReadControlBlock(ModuleFile &F, F.Kind == MK_ImplicitModule) N = ForceValidateUserInputs ? NumUserInputs : 0; + if (N != 0) + Diag(diag::remark_module_validation) + << N << F.ModuleName << F.FileName; + for (unsigned I = 0; I < N; ++I) { InputFile IF = getInputFile(F, I+1, Complain); if (!IF.getFile() || IF.isOutOfDate()) diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index f42bdc16d815d..88ad8dd6495dd 100644 --- a/clang/lib/Serialization/ModuleCache.cpp +++ b/clang/lib/Serialization/ModuleCache.cpp @@ -34,8 +34,10 @@ class CrossProcessModuleCache : public ModuleCache { } std::time_t getModuleTimestamp(StringRef ModuleFilename) override { + std::string TimestampFilename = + serialization::ModuleFile::getTimestampFilename(ModuleFilename); llvm::sys::fs::file_status Status; - if (llvm::sys::fs::status(ModuleFilename, Status) != std::error_code{}) + if (llvm::sys::fs::status(TimestampFilename, Status) != std::error_code{}) return 0; return llvm::sys::toTimeT(Status.getLastModificationTime()); } diff --git a/clang/test/Modules/fmodules-validate-once-per-build-session.c b/clang/test/Modules/fmodules-validate-once-per-build-session.c index d9d79b001e30c..2348ca1381e8a 100644 --- a/clang/test/Modules/fmodules-validate-once-per-build-session.c +++ b/clang/test/Modules/fmodules-validate-once-per-build-session.c @@ -1,119 +1,134 @@ -#include "foo.h" -#include "bar.h" - -// Clear the module cache. -// RUN: rm -rf %t -// RUN: mkdir -p %t/Inputs -// RUN: mkdir -p %t/modules-to-compare +// This tests the behavior of -fmodules-validate-once-per-build-session with +// different combinations of flags and states of the module cache. -// === -// Create a module. We will use -I or -isystem to determine whether to treat -// foo.h as a system header. -// RUN: echo 'void meow(void);' > %t/Inputs/foo.h -// RUN: echo 'void woof(void);' > %t/Inputs/bar.h -// RUN: echo 'module Foo { header "foo.h" }' > %t/Inputs/module.modulemap -// RUN: echo 'extern module Bar "bar.modulemap"' >> %t/Inputs/module.modulemap -// RUN: echo 'module Bar { header "bar.h" }' > %t/Inputs/bar.modulemap +// Note: The `sleep 1` commands sprinkled throughout this test make the strict +// comparisons of epoch mtimes work as expected. Some may be unnecessary, +// but make the intent clearer. -// === -// Compile the module. -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache -fsyntax-only -isystem %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-user -fsyntax-only -I %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-user-no-force -fsyntax-only -I %t/Inputs -fno-modules-force-validate-user-headers -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Bar.pcm.timestamp -// RUN: cp %t/modules-cache/Foo.pcm %t/modules-to-compare/Foo-before.pcm -// RUN: cp %t/modules-cache/Bar.pcm %t/modules-to-compare/Bar-before.pcm -// RUN: cp %t/modules-cache-user/Foo.pcm %t/modules-to-compare/Foo-before-user.pcm -// RUN: cp %t/modules-cache-user/Bar.pcm %t/modules-to-compare/Bar-before-user.pcm -// RUN: cp %t/modules-cache-user-no-force/Foo.pcm %t/modules-to-compare/Foo-before-user-no-force.pcm -// RUN: cp %t/modules-cache-user-no-force/Bar.pcm %t/modules-to-compare/Bar-before-user-no-force.pcm - -// === -// Use it, and make sure that we did not recompile it. -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache -fsyntax-only -isystem %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-user -fsyntax-only -I %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-use-no-force -fsyntax-only -I %t/Inputs -fno-modules-force-validate-user-headers -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Bar.pcm.timestamp -// RUN: cp %t/modules-cache/Foo.pcm %t/modules-to-compare/Foo-after.pcm -// RUN: cp %t/modules-cache/Bar.pcm %t/modules-to-compare/Bar-after.pcm -// RUN: cp %t/modules-cache-user/Foo.pcm %t/modules-to-compare/Foo-after-user.pcm -// RUN: cp %t/modules-cache-user/Bar.pcm %t/modules-to-compare/Bar-after-user.pcm -// RUN: cp %t/modules-cache-user-no-force/Foo.pcm %t/modules-to-compare/Foo-after-user-no-force.pcm -// RUN: cp %t/modules-cache-user-no-force/Bar.pcm %t/modules-to-compare/Bar-after-user-no-force.pcm +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: echo "-fsyntax-only -fmodules -fmodules-cache-path=%/t/module-cache" > %t/ctx.rsp +// RUN: echo "-fbuild-session-file=%/t/module-cache/session.timestamp" >> %t/ctx.rsp +// RUN: echo "-fmodules-validate-once-per-build-session" >> %t/ctx.rsp +// RUN: echo "-Rmodule-build -Rmodule-validation" >> %t/ctx.rsp -// RUN: diff %t/modules-to-compare/Foo-before.pcm %t/modules-to-compare/Foo-after.pcm -// RUN: diff %t/modules-to-compare/Bar-before.pcm %t/modules-to-compare/Bar-after.pcm -// RUN: diff %t/modules-to-compare/Foo-before-user.pcm %t/modules-to-compare/Foo-after-user.pcm -// RUN: diff %t/modules-to-compare/Bar-before-user.pcm %t/modules-to-compare/Bar-after-user.pcm -// RUN: diff %t/modules-to-compare/Foo-before-user-no-force.pcm %t/modules-to-compare/Foo-after-user-no-force.pcm -// RUN: diff %t/modules-to-compare/Bar-before-user-no-force.pcm %t/modules-to-compare/Bar-after-user-no-force.pcm +//--- include/foo.h +//--- include/module.modulemap +module Foo { header "foo.h" } -// === -// Change the sources. +//--- clean.c +// Clean module cache. Modules will get compiled regardless of validation settings. +// RUN: mkdir %t/module-cache // RUN: sleep 1 -// RUN: echo 'void meow2(void);' > %t/Inputs/foo.h -// RUN: echo 'module Bar { header "bar.h" export * }' > %t/Inputs/bar.modulemap +// RUN: touch %t/module-cache/session.timestamp +// RUN: sleep 1 +// RUN: %clang @%t/ctx.rsp %t/clean.c -DCTX=1 \ +// RUN: -isystem %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/clean.c +// RUN: %clang @%t/ctx.rsp %t/clean.c -DCTX=2 \ +// RUN: -I %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/clean.c +// RUN: %clang @%t/ctx.rsp %t/clean.c -DCTX=3 \ +// RUN: -I %t/include -fmodules-validate-system-headers -Xclang -fno-modules-force-validate-user-headers \ +// RUN: 2>&1 | FileCheck %t/clean.c +#include "foo.h" +// CHECK: building module 'Foo' -// === -// Use the module, and make sure that we did not recompile it if foo.h or -// module.modulemap are system files or user files with force validation disabled, -// even though the sources changed. -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache -fsyntax-only -isystem %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-user -fsyntax-only -I %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-user-no-force -fsyntax-only -I %t/Inputs -fno-modules-force-validate-user-headers -fmodules-validate-system-headers -fbuild-session-timestamp=1390000000 -fmodules-validate-once-per-build-session %s -// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Bar.pcm.timestamp -// RUN: cp %t/modules-cache/Foo.pcm %t/modules-to-compare/Foo-after.pcm -// RUN: cp %t/modules-cache/Bar.pcm %t/modules-to-compare/Bar-after.pcm -// RUN: cp %t/modules-cache-user/Foo.pcm %t/modules-to-compare/Foo-after-user.pcm -// RUN: cp %t/modules-cache-user/Bar.pcm %t/modules-to-compare/Bar-after-user.pcm -// RUN: cp %t/modules-cache-user-no-force/Foo.pcm %t/modules-to-compare/Foo-after-user-no-force.pcm -// RUN: cp %t/modules-cache-user-no-force/Bar.pcm %t/modules-to-compare/Bar-after-user-no-force.pcm +//--- no-change-same-session.c +// Populated module cache in the same build session with unchanged inputs. +// Validation only happens when it's forced for user headers. No compiles. +// RUN: sleep 1 +// RUN: %clang @%t/ctx.rsp %t/no-change-same-session.c -DCTX=1 \ +// RUN: -isystem %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-same-session.c --check-prefix=CHECK-NO-VALIDATION-OR-BUILD --allow-empty +// RUN: %clang @%t/ctx.rsp %t/no-change-same-session.c -DCTX=2 \ +// RUN: -I %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-same-session.c --check-prefix=CHECK-VALIDATION-ONLY +// RUN: %clang @%t/ctx.rsp %t/no-change-same-session.c -DCTX=3 \ +// RUN: -I %t/include -fmodules-validate-system-headers -Xclang -fno-modules-force-validate-user-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-same-session.c --check-prefix=CHECK-NO-VALIDATION-OR-BUILD --allow-empty +#include "foo.h" +// CHECK-NO-VALIDATION-OR-BUILD-NOT: validating {{[0-9]+}} input files in module 'Foo' +// CHECK-NO-VALIDATION-OR-BUILD-NOT: building module 'Foo' +// CHECK-VALIDATION-ONLY: validating {{[0-9]+}} input files in module 'Foo' +// CHECK-VALIDATION-ONLY-NOT: building module 'Foo' -// RUN: diff %t/modules-to-compare/Foo-before.pcm %t/modules-to-compare/Foo-after.pcm -// RUN: diff %t/modules-to-compare/Bar-before.pcm %t/modules-to-compare/Bar-after.pcm -// When foo.h is an user header, we will validate it by default. -// RUN: not diff %t/modules-to-compare/Foo-before-user.pcm %t/modules-to-compare/Foo-after-user.pcm -// RUN: not diff %t/modules-to-compare/Bar-before-user.pcm %t/modules-to-compare/Bar-after-user.pcm -// When foo.h is an user header, we will not validate it if force validation is turned off. -// RUN: diff %t/modules-to-compare/Foo-before-user-no-force.pcm %t/modules-to-compare/Foo-after-user-no-force.pcm -// RUN: diff %t/modules-to-compare/Bar-before-user-no-force.pcm %t/modules-to-compare/Bar-after-user-no-force.pcm +//--- change-same-session.c +// Populated module cache in the same build session with changed inputs. +// Validation only happens when it's forced for user headers and results in compilation. +// RUN: sleep 1 +// RUN: touch %t/include/foo.h +// RUN: sleep 1 +// RUN: %clang @%t/ctx.rsp %t/change-same-session.c -DCTX=1 \ +// RUN: -isystem %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/change-same-session.c --check-prefix=CHECK-NO-VALIDATION-OR-BUILD --allow-empty +// RUN: %clang @%t/ctx.rsp %t/change-same-session.c -DCTX=2 \ +// RUN: -I %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/change-same-session.c --check-prefix=CHECK-VALIDATION-AND-BUILD +// RUN: %clang @%t/ctx.rsp %t/change-same-session.c -DCTX=3 \ +// RUN: -I %t/include -fmodules-validate-system-headers -Xclang -fno-modules-force-validate-user-headers \ +// RUN: 2>&1 | FileCheck %t/change-same-session.c --check-prefix=CHECK-NO-VALIDATION-OR-BUILD --allow-empty +#include "foo.h" +// CHECK-NO-VALIDATION-OR-BUILD-NOT: validating {{[0-9]+}} input files in module 'Foo' +// CHECK-NO-VALIDATION-OR-BUILD-NOT: building module 'Foo' +// CHECK-VALIDATION-AND-BUILD: validating {{[0-9]+}} input files in module 'Foo' +// CHECK-VALIDATION-AND-BUILD: building module 'Foo' -// === -// Recompile the module if the today's date is before 01 January 2100. -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache -fsyntax-only -isystem %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=4102441200 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-user -fsyntax-only -I %t/Inputs -fmodules-validate-system-headers -fbuild-session-timestamp=4102441200 -fmodules-validate-once-per-build-session %s -// RUN: %clang_cc1 -cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache-user-no-force -fsyntax-only -I %t/Inputs -fno-modules-force-validate-user-headers -fmodules-validate-system-headers -fbuild-session-timestamp=4102441200 -fmodules-validate-once-per-build-session %s -// RUN: ls -R %t/modules-cache | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user | grep Bar.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Foo.pcm.timestamp -// RUN: ls -R %t/modules-cache-user-no-force | grep Bar.pcm.timestamp -// RUN: cp %t/modules-cache/Foo.pcm %t/modules-to-compare/Foo-after.pcm -// RUN: cp %t/modules-cache/Bar.pcm %t/modules-to-compare/Bar-after.pcm -// RUN: cp %t/modules-cache-user/Foo.pcm %t/modules-to-compare/Foo-after-user.pcm -// RUN: cp %t/modules-cache-user/Bar.pcm %t/modules-to-compare/Bar-after-user.pcm -// RUN: cp %t/modules-cache-user-no-force/Foo.pcm %t/modules-to-compare/Foo-after-user-no-force.pcm -// RUN: cp %t/modules-cache-user-no-force/Bar.pcm %t/modules-to-compare/Bar-after-user-no-force.pcm +//--- change-new-session.c +// Populated module cache in a new build session with changed inputs. +// All configurations validate and recompile. +// RUN: sleep 1 +// RUN: touch %t/include/foo.h +// RUN: sleep 1 +// RUN: touch %t/module-cache/session.timestamp +// RUN: sleep 1 +// RUN: %clang @%t/ctx.rsp %t/change-new-session.c -DCTX=1 \ +// RUN: -isystem %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/change-new-session.c --check-prefixes=CHECK,CHECK-VALIDATE-ONCE +// NOTE: Forced user headers validation causes redundant validation of the just-built module. +// RUN: %clang @%t/ctx.rsp %t/change-new-session.c -DCTX=2 \ +// RUN: -I %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/change-new-session.c --check-prefixes=CHECK,CHECK-FORCE-VALIDATE-TWICE +// RUN: %clang @%t/ctx.rsp %t/change-new-session.c -DCTX=3 \ +// RUN: -I %t/include -fmodules-validate-system-headers -Xclang -fno-modules-force-validate-user-headers \ +// RUN: 2>&1 | FileCheck %t/change-new-session.c --check-prefixes=CHECK,CHECK-VALIDATE-ONCE +#include "foo.h" +// CHECK: validating {{[0-9]+}} input files in module 'Foo' +// CHECK: building module 'Foo' +// CHECK-VALIDATE-ONCE-NOT: validating {{[0-9]+}} input files in module 'Foo' +// CHECK-FORCE-VALIDATE-TWICE: validating {{[0-9]+}} input files in module 'Foo' -// RUN: not diff %t/modules-to-compare/Foo-before.pcm %t/modules-to-compare/Foo-after.pcm -// RUN: not diff %t/modules-to-compare/Bar-before.pcm %t/modules-to-compare/Bar-after.pcm -// RUN: not diff %t/modules-to-compare/Foo-before-user.pcm %t/modules-to-compare/Foo-after-user.pcm -// RUN: not diff %t/modules-to-compare/Bar-before-user.pcm %t/modules-to-compare/Bar-after-user.pcm -// RUN: not diff %t/modules-to-compare/Foo-before-user-no-force.pcm %t/modules-to-compare/Foo-after-user-no-force.pcm -// RUN: not diff %t/modules-to-compare/Bar-before-user-no-force.pcm %t/modules-to-compare/Bar-after-user-no-force.pcm +//--- no-change-new-session-twice.c +// Populated module cache in a new build session with unchanged inputs. +// At first, all configurations validate but don't recompile. +// RUN: sleep 1 +// RUN: touch %t/module-cache/session.timestamp +// RUN: sleep 1 +// RUN: %clang @%t/ctx.rsp %t/no-change-new-session-twice.c -DCTX=1 \ +// RUN: -isystem %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-new-session-twice.c --check-prefix=CHECK-ONCE +// RUN: %clang @%t/ctx.rsp %t/no-change-new-session-twice.c -DCTX=2 \ +// RUN: -I %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-new-session-twice.c --check-prefix=CHECK-ONCE +// RUN: %clang @%t/ctx.rsp %t/no-change-new-session-twice.c -DCTX=3 \ +// RUN: -I %t/include -fmodules-validate-system-headers -Xclang -fno-modules-force-validate-user-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-new-session-twice.c --check-prefix=CHECK-ONCE +// +// Then, only the forced user header validation performs redundant validation (but no compilation). +// All other configurations do not validate and do not compile. +// RUN: sleep 1 +// RUN: %clang @%t/ctx.rsp %t/no-change-new-session-twice.c -DCTX=1 \ +// RUN: -isystem %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-new-session-twice.c --check-prefix=CHECK-NOT-TWICE --allow-empty +// NOTE: Forced user headers validation causes redundant validation of the just-validated module. +// RUN: %clang @%t/ctx.rsp %t/no-change-new-session-twice.c -DCTX=2 \ +// RUN: -I %t/include -fmodules-validate-system-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-new-session-twice.c --check-prefix=CHECK-ONCE +// RUN: %clang @%t/ctx.rsp %t/no-change-new-session-twice.c -DCTX=3 \ +// RUN: -I %t/include -fmodules-validate-system-headers -Xclang -fno-modules-force-validate-user-headers \ +// RUN: 2>&1 | FileCheck %t/no-change-new-session-twice.c --check-prefix=CHECK-NOT-TWICE --allow-empty +#include "foo.h" +// CHECK-ONCE: validating {{[0-9]+}} input files in module 'Foo' +// CHECK-ONCE-NOT: building module 'Foo' +// CHECK-NOT-TWICE-NOT: validating {{[0-9]+}} input files in module 'Foo' +// CHECK-NOT-TWICE-NOT: building module 'Foo' From a2e93dce5f2b86b0b95197576791c12787bcf201 Mon Sep 17 00:00:00 2001 From: quic-areg Date: Wed, 15 Oct 2025 19:11:03 -0500 Subject: [PATCH 18/47] [Hexagon][llvm-objdump] Start a fresh packet at symbol boundaries. (#163466) Hexagon packets can visually straddle labels when data (e.g. jump tables) in the text section does not carry end-of-packet bits. In such cases the next instruction, even at a new symbol, appears to continue the previous packet. This patch resets packet state when encountering a new symbol so that packets at symbol starts are guaranteed to start in their own packet. (cherry picked from commit 0cdebdaf59d20f34158d97d193381e95a0ba90b0) --- .../Disassembler/HexagonDisassembler.cpp | 16 +++++++++++++ .../ELF/Hexagon/packet-reset-on-label.s | 23 +++++++++++++++++++ llvm/tools/llvm-objdump/llvm-objdump.cpp | 8 +++++++ 3 files changed, 47 insertions(+) create mode 100644 llvm/test/tools/llvm-objdump/ELF/Hexagon/packet-reset-on-label.s diff --git a/llvm/lib/Target/Hexagon/Disassembler/HexagonDisassembler.cpp b/llvm/lib/Target/Hexagon/Disassembler/HexagonDisassembler.cpp index bcddb540d35dc..c48cf5e6353ac 100644 --- a/llvm/lib/Target/Hexagon/Disassembler/HexagonDisassembler.cpp +++ b/llvm/lib/Target/Hexagon/Disassembler/HexagonDisassembler.cpp @@ -64,6 +64,10 @@ class HexagonDisassembler : public MCDisassembler { void remapInstruction(MCInst &Instr) const; + Expected onSymbolStart(SymbolInfoTy &Symbol, uint64_t &Size, + ArrayRef Bytes, + uint64_t Address) const override; + private: bool makeBundle(ArrayRef Bytes, uint64_t Address, uint64_t &BytesToSkip, raw_ostream &CS) const; @@ -604,6 +608,18 @@ DecodeStatus HexagonDisassembler::getSingleInstruction(MCInst &MI, MCInst &MCB, return Result; } +Expected HexagonDisassembler::onSymbolStart(SymbolInfoTy &Symbol, + uint64_t &Size, + ArrayRef Bytes, + uint64_t Address) const { + // At the start of a symbol, force a fresh packet by resetting any + // in-progress bundle state. This prevents packets from straddling label + // boundaries when data (e.g. jump tables) appears in between. + Size = 0; + resetBundle(); + return true; +} + static DecodeStatus DecodeRegisterClass(MCInst &Inst, unsigned RegNo, ArrayRef Table) { if (RegNo < Table.size()) { diff --git a/llvm/test/tools/llvm-objdump/ELF/Hexagon/packet-reset-on-label.s b/llvm/test/tools/llvm-objdump/ELF/Hexagon/packet-reset-on-label.s new file mode 100644 index 0000000000000..02a52bbb3fbd8 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/Hexagon/packet-reset-on-label.s @@ -0,0 +1,23 @@ +// RUN: llvm-mc -triple=hexagon -mcpu=hexagonv75 -filetype=obj %s \ +// RUN: | llvm-objdump -d - \ +// RUN: | FileCheck %s + +foo: + { nop } + /// a nop without end-of-packet bits set to simulate data that is + /// not a proper packet end. + .long 0x7f004000 +bar: + { nop + nop + } + +// CHECK-LABEL: : +// CHECK: { nop } +// CHECK-NEXT: { nop + +/// The instruction starting after should start in a new packet. +// CHECK-LABEL: : +// CHECK: { nop +// CHECK-NEXT: nop } + diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 74eb9033c8e2c..221b884e0c06c 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -717,11 +717,17 @@ class PrettyPrinter { } while (!Comments.empty()); FOS.flush(); } + + // Hook invoked when starting to disassemble a symbol at the current position. + // Default is no-op. + virtual void onSymbolStart() {} }; PrettyPrinter PrettyPrinterInst; class HexagonPrettyPrinter : public PrettyPrinter { public: + void onSymbolStart() override { reset(); } + void printLead(ArrayRef Bytes, uint64_t Address, formatted_raw_ostream &OS) { if (LeadingAddr) @@ -2216,6 +2222,8 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, Start += Size; break; } + // Allow targets to reset any per-symbol state. + DT->Printer->onSymbolStart(); formatted_raw_ostream FOS(OS); Index = Start; if (SectionAddr < StartAddress) From c6af6be3cd1cbfa0dcd05ff9b8bda457a1902ba0 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 16 Oct 2025 19:46:19 +0800 Subject: [PATCH 19/47] [libc++][docs] Add missing entry for P3379R0 to `21.rst` P3379R0 was implemented in LLVM 21 (13c464be84d9715f0825387f30e455eea7ef75f7) but the entry for release note is still missing. A following-up PR (4a509f853fa4821ecdb0f6bc3b90ddd48794cc8c) fixed this as drive-by but backport was not accepted. This patch only adds the missing entry. --- libcxx/docs/ReleaseNotes/21.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index 1410223d56a6f..2b1aa28b62090 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -53,6 +53,7 @@ Implemented Papers - P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github `__) - P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github `__) - P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github `__) +- P3379R0: Constrain ``std::expected`` equality operators (`Github `__) Improvements and New Features ----------------------------- From 570c4c9443387b756ed3e4cb94ca708841f2472a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 16 Oct 2025 09:49:38 +0300 Subject: [PATCH 20/47] [clang] Fix catching pointers by reference on mingw targets (#162546) For this specific case, when catching a pointer data type, by reference, Clang generates a special code pattern, which directly accesses the exception data by skipping past the `_Unwind_Exception` manually (rather than using the return value of `__cxa_begin_catch`). On most platforms, `_Unwind_Exception` is 32 bytes, but in some configurations it's different. (ARM EHABI is one preexisting case.) In the case of SEH, it's also different - it is 48 bytes in 32 bit mode and 64 bytes in 64 bit mode. (See the SEH ifdef in `_Unwind_Exception` in `clang/lib/Headers/unwind.h`.) Handle this case in `TargetCodeGenInfo::getSizeOfUnwindException`, fixing the code generation for catching pointers by reference. This fixes https://github.com/mstorsjo/llvm-mingw/issues/522. (cherry picked from commit 10be254587da24d56e2c6817b382beaca612b6c3) --- clang/lib/CodeGen/TargetInfo.cpp | 2 ++ clang/test/CodeGenCXX/sizeof-unwind-exception.cpp | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 277d69daf493c..af711c14d4edb 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -75,6 +75,8 @@ TargetCodeGenInfo::~TargetCodeGenInfo() = default; // If someone can figure out a general rule for this, that would be great. // It's probably just doomed to be platform-dependent, though. unsigned TargetCodeGenInfo::getSizeOfUnwindException() const { + if (getABIInfo().getContext().getLangOpts().hasSEHExceptions()) + return getABIInfo().getDataLayout().getPointerSizeInBits() > 32 ? 64 : 48; // Verified for: // x86-64 FreeBSD, Linux, Darwin // x86-32 FreeBSD, Linux, Darwin diff --git a/clang/test/CodeGenCXX/sizeof-unwind-exception.cpp b/clang/test/CodeGenCXX/sizeof-unwind-exception.cpp index 4fb977a5367e7..e40b2d7ae43ea 100644 --- a/clang/test/CodeGenCXX/sizeof-unwind-exception.cpp +++ b/clang/test/CodeGenCXX/sizeof-unwind-exception.cpp @@ -3,6 +3,8 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fcxx-exceptions -fexceptions %s -O2 -o - | FileCheck %s --check-prefix=ARM-DARWIN // RUN: %clang_cc1 -triple arm-unknown-gnueabi -emit-llvm -fcxx-exceptions -fexceptions %s -O2 -o - | FileCheck %s --check-prefix=ARM-EABI // RUN: %clang_cc1 -triple mipsel-unknown-unknown -emit-llvm -fcxx-exceptions -fexceptions %s -O2 -o - | FileCheck %s --check-prefix=MIPS +// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -fcxx-exceptions -fexceptions -exception-model=seh %s -O2 -o - | FileCheck %s --check-prefix=MINGW-X86-64 +// RUN: %clang_cc1 -triple thumbv7-windows-gnu -emit-llvm -fcxx-exceptions -fexceptions -exception-model=seh %s -O2 -o - | FileCheck %s --check-prefix=MINGW-ARMV7 void foo(); void test() { @@ -25,9 +27,15 @@ void test() { // ARM-EABI-NEXT: [[T1:%.*]] = getelementptr i8, ptr [[EXN]], i32 88 // MIPS: [[T0:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[EXN:%.*]]) [[NUW:#[0-9]+]] // MIPS-NEXT: [[T1:%.*]] = getelementptr i8, ptr [[EXN]], i32 24 +// MINGW-X86-64: [[T0:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[EXN:%.*]]) [[NUW:#[0-9]+]] +// MINGW-X86-64-NEXT:[[T1:%.*]] = getelementptr i8, ptr [[EXN]], i64 64 +// MINGW-ARMV7: [[T0:%.*]] = tail call arm_aapcs_vfpcc ptr @__cxa_begin_catch(ptr [[EXN:%.*]]) [[NUW:#[0-9]+]] +// MINGW-ARMV7-NEXT: [[T1:%.*]] = getelementptr i8, ptr [[EXN]], i32 48 // X86-64: attributes [[NUW]] = { nounwind } // X86-32: attributes [[NUW]] = { nounwind } // ARM-DARWIN: attributes [[NUW]] = { nounwind } // ARM-EABI: attributes [[NUW]] = { nounwind } // MIPS: attributes [[NUW]] = { nounwind } +// MINGW-X86-64: attributes [[NUW]] = { nounwind } +// MINGW-ARMV7: attributes [[NUW]] = { nounwind } From ffa6b0c365ec014d362f52e6558251c347cef6ad Mon Sep 17 00:00:00 2001 From: Ryotaro Kasuga Date: Thu, 9 Oct 2025 19:19:45 +0900 Subject: [PATCH 21/47] [MachinePipeliner] Limit the number of stores in BB (#154940) The dependency analysis in MachinePipeliner checks dependencies for every pair of store instructions in the target basic block. This means the time complexity of the analysis is `O(N^2)`, where `N` is the number of store instructions. Therefore, compilation time can become significantly long when there are too many store instructions. To mitigate it, this patch introduces logic to count the number of store instructions at the beginning of the pipeliner and bail out if it exceeds the threshold. The default value if the threshold should be large enough. Thus, in most practical cases where the pipeliner is beneficial, this patch should not cause any performance regression. Related issue: #150262 (cherry picked from commit 22b79fb3b9d051a83520eaa9d03abad782697448) --- llvm/lib/CodeGen/MachinePipeliner.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/llvm/lib/CodeGen/MachinePipeliner.cpp b/llvm/lib/CodeGen/MachinePipeliner.cpp index 90005bd181f3a..0e7cb0c980d40 100644 --- a/llvm/lib/CodeGen/MachinePipeliner.cpp +++ b/llvm/lib/CodeGen/MachinePipeliner.cpp @@ -110,6 +110,7 @@ STATISTIC(NumFailZeroMII, "Pipeliner abort due to zero MII"); STATISTIC(NumFailNoSchedule, "Pipeliner abort due to no schedule found"); STATISTIC(NumFailZeroStage, "Pipeliner abort due to zero stage"); STATISTIC(NumFailLargeMaxStage, "Pipeliner abort due to too many stages"); +STATISTIC(NumFailTooManyStores, "Pipeliner abort due to too many stores"); /// A command line option to turn software pipelining on or off. static cl::opt EnableSWP("enable-pipeliner", cl::Hidden, cl::init(true), @@ -193,6 +194,13 @@ static cl::opt MVECodeGen("pipeliner-mve-cg", cl::Hidden, cl::init(false), cl::desc("Use the MVE code generator for software pipelining")); +/// A command line argument to limit the number of store instructions in the +/// target basic block. +static cl::opt SwpMaxNumStores( + "pipeliner-max-num-stores", + cl::desc("Maximum number of stores allwed in the target loop."), cl::Hidden, + cl::init(200)); + namespace llvm { // A command line option to enable the CopyToPhi DAG mutation. @@ -544,6 +552,23 @@ bool MachinePipeliner::canPipelineLoop(MachineLoop &L) { return false; } + unsigned NumStores = 0; + for (MachineInstr &MI : *L.getHeader()) + if (MI.mayStore()) + ++NumStores; + if (NumStores > SwpMaxNumStores) { + LLVM_DEBUG(dbgs() << "Too many stores\n"); + NumFailTooManyStores++; + ORE->emit([&]() { + return MachineOptimizationRemarkAnalysis(DEBUG_TYPE, "canPipelineLoop", + L.getStartLoc(), L.getHeader()) + << "Too many store instructions in the loop: " + << ore::NV("NumStores", NumStores) << " > " + << ore::NV("SwpMaxNumStores", SwpMaxNumStores) << "."; + }); + return false; + } + // Remove any subregisters from inputs to phi nodes. preprocessPhiNodes(*L.getHeader()); return true; From 464d75ad5f26b5467f3d598c11e8e6acda5c5bb8 Mon Sep 17 00:00:00 2001 From: Ryotaro Kasuga Date: Thu, 16 Oct 2025 17:21:10 +0900 Subject: [PATCH 22/47] [MachinePipeliner] Add test missed in #154940 (NFC) (#163350) This PR adds a testcase where pipeliner bails out early because the number of the store instructions exceeds the threshold set by `pipeliner-max-num-stores`. The test should have been added in #154940, but it was missed. --- llvm/test/CodeGen/Hexagon/swp-many-stores.mir | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 llvm/test/CodeGen/Hexagon/swp-many-stores.mir diff --git a/llvm/test/CodeGen/Hexagon/swp-many-stores.mir b/llvm/test/CodeGen/Hexagon/swp-many-stores.mir new file mode 100644 index 0000000000000..bf14dcf3c4fb3 --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/swp-many-stores.mir @@ -0,0 +1,88 @@ +# RUN: llc -run-pass pipeliner -debug-only=pipeliner %s -o /dev/null -pipeliner-max-num-stores=5 2>&1 | FileCheck %s +# REQUIRES: asserts + +# This loop has six stores, which exceeds the limit set by +# `pipeliner-max-num-stores`. + +# CHECK: Too many stores + +--- | + target datalayout = "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048" + target triple = "hexagon-unknown-linux-musl" + + define void @f(ptr %a, i32 %n) #0 { + entry: + %guard = icmp sgt i32 %n, 0 + %btc = sub nsw i32 %n, 1 + br i1 %guard, label %loop.preheader, label %exit + + loop.preheader: ; preds = %entry + %0 = add i32 %n, 1 + %cgep = getelementptr i8, ptr %a, i32 %0 + br label %loop + + loop: ; preds = %loop.preheader, %loop + %lsr.iv = phi ptr [ %cgep, %loop.preheader ], [ %cgep8, %loop ] + %i = phi i32 [ %i.dec, %loop ], [ %btc, %loop.preheader ] + %cgep7 = getelementptr i8, ptr %lsr.iv, i32 -2 + store i8 0, ptr %cgep7, align 1 + %cgep8 = getelementptr i8, ptr %lsr.iv, i32 -1 + store i8 1, ptr %cgep8, align 1 + store i8 2, ptr %lsr.iv, align 1 + %cgep9 = getelementptr i8, ptr %lsr.iv, i32 1 + store i8 3, ptr %cgep9, align 1 + %cgep10 = getelementptr i8, ptr %lsr.iv, i32 2 + store i8 4, ptr %cgep10, align 1 + %cgep11 = getelementptr i8, ptr %lsr.iv, i32 3 + store i8 5, ptr %cgep11, align 1 + %i.dec = sub i32 %i, 1 + %ec = icmp eq i32 %i.dec, 0 + br i1 %ec, label %exit, label %loop + + exit: ; preds = %loop, %entry + ret void + } + + attributes #0 = { "target-cpu"="hexagonv79" } +... +--- +name: f +tracksRegLiveness: true +body: | + bb.0.entry: + successors: %bb.1(0x50000000), %bb.3(0x30000000) + liveins: $r0, $r1 + + %7:intregs = COPY $r1 + %6:intregs = COPY $r0 + %8:predregs = C2_cmpgti %7, 0 + J2_jumpf %8, %bb.3, implicit-def dead $pc + J2_jump %bb.1, implicit-def dead $pc + + bb.1.loop.preheader: + successors: %bb.2(0x80000000) + + %0:intregs = A2_addi %7, -1 + %1:intregs = S4_addaddi %7, %6, 1 + %10:intregs = A2_tfrsi 0 + %11:intregs = A2_tfrsi 1 + %14:intregs = COPY %0 + J2_loop0r %bb.2, %14, implicit-def $lc0, implicit-def $sa0, implicit-def $usr + + bb.2.loop (machine-block-address-taken): + successors: %bb.3(0x04000000), %bb.2(0x7c000000) + + %2:intregs = PHI %1, %bb.1, %4, %bb.2 + S2_storerb_io %2, -2, %10 :: (store (s8) into %ir.cgep7) + %4:intregs = A2_addi %2, -1 + S2_storerb_io %2, -1, %11 :: (store (s8) into %ir.cgep8) + S4_storeirb_io %2, 0, 2 :: (store (s8) into %ir.lsr.iv) + S4_storeirb_io %2, 1, 3 :: (store (s8) into %ir.cgep9) + S4_storeirb_io %2, 2, 4 :: (store (s8) into %ir.cgep10) + S4_storeirb_io %2, 3, 5 :: (store (s8) into %ir.cgep11) + ENDLOOP0 %bb.2, implicit-def $pc, implicit-def $lc0, implicit $sa0, implicit $lc0 + J2_jump %bb.3, implicit-def dead $pc + + bb.3.exit: + PS_jmpret $r31, implicit-def dead $pc +... From 0d819a9104b28f4fdd459d55d96228d78dc93a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 16 Oct 2025 22:47:08 +0300 Subject: [PATCH 23/47] [libunwind] Fix aarch64 SEH unwinding with a debugger attached (#162867) See https://github.com/LuaJIT/LuaJIT/issues/593#issuecomment-1717728494 for the original explanation of the problem. In short; when a debugger is attached, there's a function KiUserExceptionDispatcher in the stack that is being unwound. The function KiUserExceptionDispatcher contains a CONTEXT, with a copy of the context from where the exception was raised. When unwinding through this function, this whole CONTEXT gets restored. This CONTEXT is what we receive a pointer to in the callbacks, as the ms_ctx pointer. When we unwind manually using RtlUnwindEx, the unwinding overwrites the CONTEXT that is passed to it. Thus, to avoid clobbering the CONTEXT that needs to be restored by KiUserExceptionDispatcher, we could either declare a new temporary CONTEXT on the stack before calling RtlUnwindEx, or just use disp->ContextRecord as we already have available. Fixes: https://github.com/llvm/llvm-project/issues/161851 Co-authored-by: Peter Cawley Co-authored-by: Hannes Domani (cherry picked from commit a17afee7ec41e53292f074fc967d264452e4363b) --- libunwind/src/Unwind-seh.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libunwind/src/Unwind-seh.cpp b/libunwind/src/Unwind-seh.cpp index 8b83f10615f22..110c5987c3f1a 100644 --- a/libunwind/src/Unwind-seh.cpp +++ b/libunwind/src/Unwind-seh.cpp @@ -174,7 +174,8 @@ _GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx, } // FIXME: Indicate target frame in foreign case! // phase 2: the clean up phase - RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable); + RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, disp->ContextRecord, + disp->HistoryTable); _LIBUNWIND_ABORT("RtlUnwindEx() failed"); case _URC_INSTALL_CONTEXT: { // If we were called by __libunwind_seh_personality(), indicate that From c5a3aa8934b032c5e171508756f3808debc3f7d3 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Thu, 16 Oct 2025 23:40:58 -0400 Subject: [PATCH 24/47] [libc++] Properly implement array cookies in the ARM ABI (#160182) When we implemented array cookie support for hardening std::unique_ptr, the implementation was only done for the Itanium ABI. I did not initially realize that ARM was using a different ABI for array cookies, so unique_ptr should not have been hardened on ARM. However, we were also incorrectly setting the ABI-detection macro: we were pretending to be using a vanilla Itanium ABI when in reality the (similar but different) ARM ABI was in use. As a result, unique_ptr was using the wrong representation for array cookies on ARM, which fortunately only mattered in the case of overaligned types. This patch fixes that. rdar://160852193 (cherry picked from commit b3a199469c9d54fb3bdabf6dff5677b401f8fef6) --- libcxx/include/__configuration/abi.h | 12 +++ libcxx/include/__memory/array_cookie.h | 84 +++++++++++++++++-- .../assert.subscript.pass.cpp | 48 +++++++++++ 3 files changed, 136 insertions(+), 8 deletions(-) diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h index a75cd0a675339..3f758d97394f4 100644 --- a/libcxx/include/__configuration/abi.h +++ b/libcxx/include/__configuration/abi.h @@ -30,8 +30,20 @@ #elif _LIBCPP_ABI_FORCE_MICROSOFT # define _LIBCPP_ABI_MICROSOFT #else +// Windows uses the Microsoft ABI # if defined(_WIN32) && defined(_MSC_VER) # define _LIBCPP_ABI_MICROSOFT + +// 32-bit ARM uses the Itanium ABI with a few differences (array cookies, etc), +// and so does 64-bit ARM on Apple platforms. +# elif defined(__arm__) || (defined(__APPLE__) && defined(__aarch64__)) +# define _LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES + +// Non-Apple 64-bit ARM uses the vanilla Itanium ABI +# elif defined(__aarch64__) +# define _LIBCPP_ABI_ITANIUM + +// We assume that other architectures also use the vanilla Itanium ABI too # else # define _LIBCPP_ABI_ITANIUM # endif diff --git a/libcxx/include/__memory/array_cookie.h b/libcxx/include/__memory/array_cookie.h index 806a9e99ecafe..be59f365aa80c 100644 --- a/libcxx/include/__memory/array_cookie.h +++ b/libcxx/include/__memory/array_cookie.h @@ -13,6 +13,7 @@ #include <__config> #include <__configuration/abi.h> #include <__cstddef/size_t.h> +#include <__memory/addressof.h> #include <__type_traits/integral_constant.h> #include <__type_traits/is_trivially_destructible.h> #include <__type_traits/negation.h> @@ -26,14 +27,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD // Trait representing whether a type requires an array cookie at the start of its allocation when // allocated as `new T[n]` and deallocated as `delete[] array`. // -// Under the Itanium C++ ABI [1], we know that an array cookie is available unless `T` is trivially -// destructible and the call to `operator delete[]` is not a sized operator delete. Under ABIs other -// than the Itanium ABI, we assume there are no array cookies. +// Under the Itanium C++ ABI [1] and the ARM ABI which derives from it, we know that an array cookie is available +// unless `T` is trivially destructible and the call to `operator delete[]` is not a sized operator delete. Under +// other ABIs, we assume there are no array cookies. // // [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies -#ifdef _LIBCPP_ABI_ITANIUM +#if defined(_LIBCPP_ABI_ITANIUM) || defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES) // TODO: Use a builtin instead -// TODO: We should factor in the choice of the usual deallocation function in this determination. +// TODO: We should factor in the choice of the usual deallocation function in this determination: +// a cookie may be available in more cases but we ignore those for now. template struct __has_array_cookie : _Not > {}; #else @@ -41,13 +43,79 @@ template struct __has_array_cookie : false_type {}; #endif +struct __itanium_array_cookie { + size_t __element_count; +}; + +template +struct [[__gnu__::__aligned__(_LIBCPP_ALIGNOF(_Tp))]] __arm_array_cookie { + size_t __element_size; + size_t __element_count; +}; + +// Return the element count in the array cookie located before the given pointer. +// +// In the Itanium ABI [1] +// ---------------------- +// The element count is stored immediately before the first element of the array. If the preferred alignment +// of array elements (which is different from the ABI alignment) is more than that of size_t, additional +// padding bytes exist before the array cookie. Assuming array elements of size and alignment 16 bytes, that +// gives us the following layout: +// +// |ooooooooxxxxxxxxaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd| +// ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// | ^^^^^^^^ | +// | | array elements +// padding | +// element count +// +// +// In the Itanium ABI with ARM differences [2] +// ------------------------------------------- +// The array cookie is stored at the very start of the allocation and it has the following form: +// +// struct array_cookie { +// std::size_t element_size; // element_size != 0 +// std::size_t element_count; +// }; +// +// Assuming elements of size and alignment 32 bytes, this gives us the following layout: +// +// |xxxxxxxxXXXXXXXXooooooooooooooooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| +// ^^^^^^^^ ^^^^^^^^^^^^^^^^ +// | ^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// element size | padding | +// element count array elements +// +// We must be careful to take into account the alignment of the array cookie, which may result in padding +// bytes between the element count and the first element of the array. Note that for ARM, the compiler +// aligns the array cookie using the ABI alignment, not the preferred alignment of array elements. +// +// [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies +// [2]: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-C++-differences template // Avoid failures when -fsanitize-address-poison-custom-array-cookie is enabled -_LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") size_t __get_array_cookie(_Tp const* __ptr) { +_LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") size_t __get_array_cookie([[__maybe_unused__]] _Tp const* __ptr) { static_assert( __has_array_cookie<_Tp>::value, "Trying to access the array cookie of a type that is not guaranteed to have one"); - size_t const* __cookie = reinterpret_cast(__ptr) - 1; // TODO: Use a builtin instead - return *__cookie; + +#if defined(_LIBCPP_ABI_ITANIUM) + using _ArrayCookie = __itanium_array_cookie; +#elif defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES) + using _ArrayCookie = __arm_array_cookie<_Tp>; +#else + static_assert(false, "The array cookie layout is unknown on this ABI"); + struct _ArrayCookie { // dummy definition required to make the function parse + size_t element_count; + }; +#endif + + char const* __array_cookie_start = reinterpret_cast(__ptr) - sizeof(_ArrayCookie); + _ArrayCookie __cookie; + // This is necessary to avoid violating strict aliasing. It's valid because _ArrayCookie is an + // implicit lifetime type. + __builtin_memcpy(std::addressof(__cookie), __array_cookie_start, sizeof(_ArrayCookie)); + return __cookie.__element_count; } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp index b7cc12350027b..f7390ef5eb5d2 100644 --- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp +++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp @@ -58,15 +58,18 @@ void test() { { { std::unique_ptr ptr(new WithCookie[5]); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr::operator[](index): index out of range"); } { std::unique_ptr ptr = std::make_unique(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr::operator[](index): index out of range"); } #if TEST_STD_VER >= 20 { std::unique_ptr ptr = std::make_unique_for_overwrite(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = WithCookie(), "unique_ptr::operator[](index): index out of range"); } #endif @@ -82,11 +85,13 @@ void test() { { { std::unique_ptr ptr = std::make_unique(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr::operator[](index): index out of range"); } # if TEST_STD_VER >= 20 { std::unique_ptr ptr = std::make_unique_for_overwrite(5); + assert(&ptr[1] == ptr.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = NoCookie(), "unique_ptr::operator[](index): index out of range"); } # endif @@ -101,6 +106,7 @@ void test() { { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other(std::move(ptr)); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } @@ -109,6 +115,7 @@ void test() { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other; other = std::move(ptr); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } @@ -116,6 +123,7 @@ void test() { { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other(std::move(ptr)); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } @@ -124,6 +132,7 @@ void test() { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other; other = std::move(ptr); + assert(&other[1] == other.get() + 1); // ensure no assertion TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } }); @@ -144,6 +153,34 @@ struct WithCookie { char padding[Size]; }; +template +struct alignas(128) OveralignedNoCookie { + char padding[Size]; +}; + +template +struct alignas(128) OveralignedWithCookie { + OveralignedWithCookie() = default; + OveralignedWithCookie(OveralignedWithCookie const&) {} + OveralignedWithCookie& operator=(OveralignedWithCookie const&) { return *this; } + ~OveralignedWithCookie() {} + char padding[Size]; +}; + +// These types have a different ABI alignment (alignof) and preferred alignment (__alignof) on some platforms. +// Make sure things work with these types because array cookies can be sensitive to preferred alignment on some +// platforms. +struct WithCookiePreferredAlignment { + WithCookiePreferredAlignment() = default; + WithCookiePreferredAlignment(WithCookiePreferredAlignment const&) {} + WithCookiePreferredAlignment& operator=(WithCookiePreferredAlignment const&) { return *this; } + ~WithCookiePreferredAlignment() {} + long double data; +}; +struct NoCookiePreferredAlignment { + long double data; +}; + int main(int, char**) { test, NoCookie<1>>(); test, NoCookie<2>>(); @@ -153,7 +190,18 @@ int main(int, char**) { test, NoCookie<16>>(); test, NoCookie<32>>(); test, NoCookie<256>>(); + + test, OveralignedNoCookie<1>>(); + test, OveralignedNoCookie<2>>(); + test, OveralignedNoCookie<3>>(); + test, OveralignedNoCookie<4>>(); + test, OveralignedNoCookie<8>>(); + test, OveralignedNoCookie<16>>(); + test, OveralignedNoCookie<32>>(); + test, OveralignedNoCookie<256>>(); + test(); + test(); return 0; } From faca424bc5f7cafa47b2e6b6547df843370a4f3f Mon Sep 17 00:00:00 2001 From: owenca Date: Thu, 25 Sep 2025 00:44:33 -0700 Subject: [PATCH 25/47] [clang-format] Correctly handle backward compatibility of C headers (#159908) This in effect reverts 05fb8408de23c3ccb6125b6886742177755bd757 and 7e1a88b9d1431e263258e3ff0f729c1fdce342d3, the latter of which erroneously changed the behavior of formatting `ObjC` header files when both the default and `ObjC` styles were absent. Now the previous behavior of treating that as an error is restored. Fixes #158704 (cherry picked from commit d7921de8027eec19a9d272bf445944973e6758b1) --- clang/lib/Format/Format.cpp | 91 +++++++++++++--------- clang/unittests/Format/ConfigParseTest.cpp | 9 ++- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 161a6c4b47e7f..d4e3a1989cd71 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -2133,47 +2133,68 @@ std::error_code parseConfiguration(llvm::MemoryBufferRef Config, if (Input.error()) return Input.error(); - for (unsigned i = 0; i < Styles.size(); ++i) { - // Ensures that only the first configuration can skip the Language option. - if (Styles[i].Language == FormatStyle::LK_None && i != 0) + assert(!Styles.empty()); + const auto StyleCount = Styles.size(); + + // Start from the second style as (only) the first one may be the default. + for (unsigned I = 1; I < StyleCount; ++I) { + const auto Lang = Styles[I].Language; + if (Lang == FormatStyle::LK_None) return make_error_code(ParseError::Error); // Ensure that each language is configured at most once. - for (unsigned j = 0; j < i; ++j) { - if (Styles[i].Language == Styles[j].Language) { + for (unsigned J = 0; J < I; ++J) { + if (Lang == Styles[J].Language) { LLVM_DEBUG(llvm::dbgs() << "Duplicate languages in the config file on positions " - << j << " and " << i << "\n"); + << J << " and " << I << '\n'); return make_error_code(ParseError::Error); } } } - // Look for a suitable configuration starting from the end, so we can - // find the configuration for the specific language first, and the default - // configuration (which can only be at slot 0) after it. - FormatStyle::FormatStyleSet StyleSet; - bool LanguageFound = false; - for (const FormatStyle &Style : llvm::reverse(Styles)) { - const auto Lang = Style.Language; - if (Lang != FormatStyle::LK_None) - StyleSet.Add(Style); - if (Lang == Language || - // For backward compatibility. - (Lang == FormatStyle::LK_Cpp && Language == FormatStyle::LK_C)) { - LanguageFound = true; - } else if (IsDotHFile && Language == FormatStyle::LK_Cpp && - (Lang == FormatStyle::LK_C || Lang == FormatStyle::LK_ObjC)) { - Language = Lang; - LanguageFound = true; + + int LanguagePos = -1; // Position of the style for Language. + int CppPos = -1; // Position of the style for C++. + int CPos = -1; // Position of the style for C. + + // Search Styles for Language and store the positions of C++ and C styles in + // case Language is not found. + for (unsigned I = 0; I < StyleCount; ++I) { + const auto Lang = Styles[I].Language; + if (Lang == Language) { + LanguagePos = I; + break; } - } - if (!LanguageFound) { - if (Styles.empty() || Styles[0].Language != FormatStyle::LK_None) + if (Lang == FormatStyle::LK_Cpp) + CppPos = I; + else if (Lang == FormatStyle::LK_C) + CPos = I; + } + + // If Language is not found, use the default style if there is one. Otherwise, + // use the C style for C++ .h files and for backward compatibility, the C++ + // style for .c files. + if (LanguagePos < 0) { + if (Styles[0].Language == FormatStyle::LK_None) // Default style. + LanguagePos = 0; + else if (IsDotHFile && Language == FormatStyle::LK_Cpp) + LanguagePos = CPos; + else if (!IsDotHFile && Language == FormatStyle::LK_C) + LanguagePos = CppPos; + if (LanguagePos < 0) return make_error_code(ParseError::Unsuitable); - FormatStyle DefaultStyle = Styles[0]; - DefaultStyle.Language = Language; - StyleSet.Add(std::move(DefaultStyle)); } - *Style = *StyleSet.Get(Language); + + for (const auto &S : llvm::reverse(llvm::drop_begin(Styles))) + Style->StyleSet.Add(S); + + *Style = Styles[LanguagePos]; + + if (LanguagePos == 0) { + if (Style->Language == FormatStyle::LK_None) // Default style. + Style->Language = Language; + Style->StyleSet.Add(*Style); + } + if (Style->InsertTrailingCommas != FormatStyle::TCS_None && Style->BinPackArguments) { // See comment on FormatStyle::TSC_Wrapped. @@ -2204,14 +2225,8 @@ FormatStyle::FormatStyleSet::Get(FormatStyle::LanguageKind Language) const { if (!Styles) return std::nullopt; auto It = Styles->find(Language); - if (It == Styles->end()) { - if (Language != FormatStyle::LK_C) - return std::nullopt; - // For backward compatibility. - It = Styles->find(FormatStyle::LK_Cpp); - if (It == Styles->end()) - return std::nullopt; - } + if (It == Styles->end()) + return std::nullopt; FormatStyle Style = It->second; Style.StyleSet = *this; return Style; diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 2b17c36f6aa84..bbe1923e19ee1 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -1269,7 +1269,7 @@ TEST(ConfigParseTest, AllowCppForC) { ParseError::Success); } -TEST(ConfigParseTest, HandleNonCppDotHFile) { +TEST(ConfigParseTest, HandleDotHFile) { FormatStyle Style = {}; Style.Language = FormatStyle::LK_Cpp; EXPECT_EQ(parseConfiguration("Language: C", &Style, @@ -1280,11 +1280,14 @@ TEST(ConfigParseTest, HandleNonCppDotHFile) { Style = {}; Style.Language = FormatStyle::LK_Cpp; - EXPECT_EQ(parseConfiguration("Language: ObjC", &Style, + EXPECT_EQ(parseConfiguration("Language: Cpp\n" + "...\n" + "Language: C", + &Style, /*AllowUnknownOptions=*/false, /*IsDotHFile=*/true), ParseError::Success); - EXPECT_EQ(Style.Language, FormatStyle::LK_ObjC); + EXPECT_EQ(Style.Language, FormatStyle::LK_Cpp); } TEST(ConfigParseTest, UsesLanguageForBasedOnStyle) { From 7e153f5372edb8f53b8576932cf5969187bf0484 Mon Sep 17 00:00:00 2001 From: owenca Date: Thu, 16 Oct 2025 21:40:59 -0700 Subject: [PATCH 26/47] [clang-format] Fix an assertion failure on comment-only config files (#163111) (cherry picked from commit 059f2df74898ff7f394dc8e60f746323499ae32b) --- clang/lib/Format/Format.cpp | 3 ++- clang/unittests/Format/ConfigParseTest.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d4e3a1989cd71..5bdb810a3925b 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -2132,8 +2132,9 @@ std::error_code parseConfiguration(llvm::MemoryBufferRef Config, Input >> Styles; if (Input.error()) return Input.error(); + if (Styles.empty()) + return make_error_code(ParseError::Success); - assert(!Styles.empty()); const auto StyleCount = Styles.size(); // Start from the second style as (only) the first one may be the default. diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index bbe1923e19ee1..ff42f09b90cf3 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -1249,6 +1249,13 @@ TEST(ConfigParseTest, ParsesConfigurationWithLanguages) { IndentWidth, 56u); } +TEST(ConfigParseTest, AllowCommentOnlyConfigFile) { + FormatStyle Style = {}; + Style.Language = FormatStyle::LK_Cpp; + EXPECT_EQ(parseConfiguration("#Language: C", &Style), ParseError::Success); + EXPECT_EQ(Style.Language, FormatStyle::LK_Cpp); +} + TEST(ConfigParseTest, AllowCppForC) { FormatStyle Style = {}; Style.Language = FormatStyle::LK_C; From ceeb93096c79db891174e1bccbd8daf5ee6c6b6a Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Mon, 13 Oct 2025 22:56:54 +0900 Subject: [PATCH 27/47] [libclang/python] Return None instead of null cursors from Token.cursor (#163183) Since https://github.com/llvm/llvm-project/pull/138103 , the `Cursor` class throws an error when any of its methods is called on a null cursor. Simultaneously, we adapted all methods to return `None` instead of a null cursor, so users should not encounter these. We have overlooked one way to end up with null cursors, namely the `Token.cursor` property, which may return null cursors under some circumstances. Fixes #163180 --- clang/bindings/python/clang/cindex.py | 2 ++ clang/bindings/python/tests/cindex/test_tokens.py | 6 ++++++ clang/docs/ReleaseNotes.rst | 1 + 3 files changed, 9 insertions(+) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 824674309d262..5ce7b5781bcb4 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -3853,6 +3853,8 @@ def cursor(self): cursor._tu = self._tu conf.lib.clang_annotateTokens(self._tu, byref(self), 1, byref(cursor)) + if cursor.is_null(): + return None return cursor diff --git a/clang/bindings/python/tests/cindex/test_tokens.py b/clang/bindings/python/tests/cindex/test_tokens.py index b6c1fc8b83600..6658579c63835 100644 --- a/clang/bindings/python/tests/cindex/test_tokens.py +++ b/clang/bindings/python/tests/cindex/test_tokens.py @@ -53,3 +53,9 @@ def test_token_extent(self): self.assertEqual(extent.start.offset, 4) self.assertEqual(extent.end.offset, 7) + + def test_null_cursor(self): + """Ensure that the cursor property converts null cursors to None""" + tu = get_tu("int i = 5;") + tokens = list(tu.get_tokens(extent=tu.cursor.extent)) + self.assertEqual(tokens[-1].cursor, None) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 43529b0f28c3d..364ea632b40cb 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -94,6 +94,7 @@ Clang Frontend Potentially Breaking Changes Clang Python Bindings Potentially Breaking Changes -------------------------------------------------- +- Return ``None`` instead of null cursors from ``Token.cursor`` - ``Cursor.from_location`` now returns ``None`` instead of a null cursor. This eliminates the last known source of null cursors. - Almost all ``Cursor`` methods now assert that they are called on non-null cursors. From 54cdd973782e85b111df5888ac137a0fd9ea114d Mon Sep 17 00:00:00 2001 From: owenca Date: Sat, 18 Oct 2025 12:13:25 -0700 Subject: [PATCH 28/47] [clang-format] Annotate ::operator and Foo::operator correctly (#164048) This effectively reverts commit b5f6689dc93216f9272e790e787548cf29250566 and fixes #111011 more narrowly. Fixes #160513 (cherry picked from commit 3bb9d4a24e40eea1988f6bdc6a79e7a128a2fad9) --- clang/lib/Format/TokenAnnotator.cpp | 10 +++------- clang/unittests/Format/TokenAnnotatorTest.cpp | 5 +++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index ed8aa514aaf31..96b11d023295a 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3775,18 +3775,12 @@ static bool isFunctionDeclarationName(const LangOptions &LangOpts, if (Current.is(TT_FunctionDeclarationName)) return true; - if (!Current.Tok.getIdentifierInfo()) + if (Current.isNoneOf(tok::identifier, tok::kw_operator)) return false; const auto *Prev = Current.getPreviousNonComment(); assert(Prev); - if (Prev->is(tok::coloncolon)) - Prev = Prev->Previous; - - if (!Prev) - return false; - const auto &Previous = *Prev; if (const auto *PrevPrev = Previous.getPreviousNonComment(); @@ -3835,6 +3829,8 @@ static bool isFunctionDeclarationName(const LangOptions &LangOpts, // Find parentheses of parameter list. if (Current.is(tok::kw_operator)) { + if (Line.startsWith(tok::kw_friend)) + return true; if (Previous.Tok.getIdentifierInfo() && !Previous.isOneOf(tok::kw_return, tok::kw_co_return)) { return true; diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 5ef7f5ba25a12..04dc69180960c 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1129,6 +1129,11 @@ TEST_F(TokenAnnotatorTest, UnderstandsOverloadedOperators) { ASSERT_EQ(Tokens.size(), 7u) << Tokens; // Not TT_FunctionDeclarationName. EXPECT_TOKEN(Tokens[3], tok::kw_operator, TT_Unknown); + + Tokens = annotate("SomeAPI::operator()();"); + ASSERT_EQ(Tokens.size(), 9u) << Tokens; + // Not TT_FunctionDeclarationName. + EXPECT_TOKEN(Tokens[2], tok::kw_operator, TT_Unknown); } TEST_F(TokenAnnotatorTest, OverloadedOperatorInTemplate) { From 3333dd88a4931e9efc766a045c2fe02043a10fe7 Mon Sep 17 00:00:00 2001 From: owenca Date: Sat, 18 Oct 2025 13:18:42 -0700 Subject: [PATCH 29/47] Update clang/lib/Format/TokenAnnotator.cpp --- clang/lib/Format/TokenAnnotator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 96b11d023295a..57b2872566a47 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3775,7 +3775,7 @@ static bool isFunctionDeclarationName(const LangOptions &LangOpts, if (Current.is(TT_FunctionDeclarationName)) return true; - if (Current.isNoneOf(tok::identifier, tok::kw_operator)) + if (!Current.isOneOf(tok::identifier, tok::kw_operator)) return false; const auto *Prev = Current.getPreviousNonComment(); From 480a90482e5b73479c7976d43dc871397ff9d67d Mon Sep 17 00:00:00 2001 From: Owen Pan Date: Sat, 18 Oct 2025 13:48:37 -0700 Subject: [PATCH 30/47] release/21.x: [clang-format] Fix a crash on BAS_BlockIndent (#164047) Backport 44a77f21045906d39fc8740a323e0ce63e15a12c --- clang/lib/Format/ContinuationIndenter.cpp | 2 +- clang/lib/Format/FormatToken.cpp | 3 ++- clang/unittests/Format/FormatTest.cpp | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 099994695dec5..38104f6c78a62 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -411,7 +411,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { } if (CurrentState.BreakBeforeClosingBrace && (Current.closesBlockOrBlockTypeList(Style) || - (Current.is(tok::r_brace) && + (Current.is(tok::r_brace) && Current.MatchingParen && Current.isBlockIndentedInitRBrace(Style)))) { return true; } diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp index 0d8ae1c4a77eb..a32175ecc02bc 100644 --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -53,12 +53,13 @@ bool FormatToken::isTypeOrIdentifier(const LangOptions &LangOpts) const { bool FormatToken::isBlockIndentedInitRBrace(const FormatStyle &Style) const { assert(is(tok::r_brace)); + assert(MatchingParen); + assert(MatchingParen->is(tok::l_brace)); if (!Style.Cpp11BracedListStyle || Style.AlignAfterOpenBracket != FormatStyle::BAS_BlockIndent) { return false; } const auto *LBrace = MatchingParen; - assert(LBrace && LBrace->is(tok::l_brace)); if (LBrace->is(BK_BracedInit)) return true; if (LBrace->Previous && LBrace->Previous->is(tok::equal)) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index c209b301ba6e5..8db0500573ec0 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -9727,6 +9727,19 @@ TEST_F(FormatTest, ParenthesesAndOperandAlignment) { Style); } +TEST_F(FormatTest, BlockIndentAndNamespace) { + auto Style = getLLVMStyleWithColumns(120); + Style.AllowShortNamespacesOnASingleLine = true; + Style.AlignAfterOpenBracket = FormatStyle::BAS_BlockIndent; + + verifyNoCrash( + "namespace {\n" + "void xxxxxxxxxxxxxxxxxxxxx(nnnnn::TTTTTTTTTTTTT const *mmmm,\n" + " YYYYYYYYYYYYYYYYY &yyyyyyyyyyyyyy);\n" + "} //", + Style); +} + TEST_F(FormatTest, BreaksConditionalExpressions) { verifyFormat( "aaaa(aaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaa\n" From 222fc11f2b8f25f6a0f4976272ef1bb7bf49521d Mon Sep 17 00:00:00 2001 From: Cullen Rhodes Date: Tue, 21 Oct 2025 08:14:55 +0000 Subject: [PATCH 31/47] Bump version to 21.1.4 --- cmake/Modules/LLVMVersion.cmake | 2 +- libcxx/include/__config | 2 +- llvm/utils/gn/secondary/llvm/version.gni | 2 +- llvm/utils/lit/lit/__init__.py | 2 +- llvm/utils/mlgo-utils/mlgo/__init__.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake index 3653f5266cccf..dedd5527c74ad 100644 --- a/cmake/Modules/LLVMVersion.cmake +++ b/cmake/Modules/LLVMVersion.cmake @@ -7,7 +7,7 @@ if(NOT DEFINED LLVM_VERSION_MINOR) set(LLVM_VERSION_MINOR 1) endif() if(NOT DEFINED LLVM_VERSION_PATCH) - set(LLVM_VERSION_PATCH 3) + set(LLVM_VERSION_PATCH 4) endif() if(NOT DEFINED LLVM_VERSION_SUFFIX) set(LLVM_VERSION_SUFFIX) diff --git a/libcxx/include/__config b/libcxx/include/__config index 329edcb8dce15..8a1893c13165b 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -28,7 +28,7 @@ // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM. // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is // defined to XXYYZZ. -# define _LIBCPP_VERSION 210103 +# define _LIBCPP_VERSION 210104 # define _LIBCPP_CONCAT_IMPL(_X, _Y) _X##_Y # define _LIBCPP_CONCAT(_X, _Y) _LIBCPP_CONCAT_IMPL(_X, _Y) diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni index c9c06ce565222..6e72f3131f700 100644 --- a/llvm/utils/gn/secondary/llvm/version.gni +++ b/llvm/utils/gn/secondary/llvm/version.gni @@ -1,4 +1,4 @@ llvm_version_major = 21 llvm_version_minor = 1 -llvm_version_patch = 3 +llvm_version_patch = 4 llvm_version = "$llvm_version_major.$llvm_version_minor.$llvm_version_patch" diff --git a/llvm/utils/lit/lit/__init__.py b/llvm/utils/lit/lit/__init__.py index 19bd3d8c43811..26ffad29a9ea4 100644 --- a/llvm/utils/lit/lit/__init__.py +++ b/llvm/utils/lit/lit/__init__.py @@ -2,7 +2,7 @@ __author__ = "Daniel Dunbar" __email__ = "daniel@minormatter.com" -__versioninfo__ = (21, 1, 3) +__versioninfo__ = (21, 1, 4) __version__ = ".".join(str(v) for v in __versioninfo__) + "dev" __all__ = [] diff --git a/llvm/utils/mlgo-utils/mlgo/__init__.py b/llvm/utils/mlgo-utils/mlgo/__init__.py index 801747b87d0df..69a86d02b05a7 100644 --- a/llvm/utils/mlgo-utils/mlgo/__init__.py +++ b/llvm/utils/mlgo-utils/mlgo/__init__.py @@ -4,7 +4,7 @@ from datetime import timezone, datetime -__versioninfo__ = (21, 1, 3) +__versioninfo__ = (21, 1, 4) __version__ = ( ".".join(str(v) for v in __versioninfo__) + "dev" From 45afac62e373ef239e4f6bceb420a2d0924223ff Mon Sep 17 00:00:00 2001 From: Cullen Rhodes Date: Thu, 23 Oct 2025 07:43:06 +0000 Subject: [PATCH 32/47] Bump version to 21.1.5 --- cmake/Modules/LLVMVersion.cmake | 2 +- libcxx/include/__config | 2 +- llvm/utils/gn/secondary/llvm/version.gni | 2 +- llvm/utils/lit/lit/__init__.py | 2 +- llvm/utils/mlgo-utils/mlgo/__init__.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake index dedd5527c74ad..d77919b57409c 100644 --- a/cmake/Modules/LLVMVersion.cmake +++ b/cmake/Modules/LLVMVersion.cmake @@ -7,7 +7,7 @@ if(NOT DEFINED LLVM_VERSION_MINOR) set(LLVM_VERSION_MINOR 1) endif() if(NOT DEFINED LLVM_VERSION_PATCH) - set(LLVM_VERSION_PATCH 4) + set(LLVM_VERSION_PATCH 5) endif() if(NOT DEFINED LLVM_VERSION_SUFFIX) set(LLVM_VERSION_SUFFIX) diff --git a/libcxx/include/__config b/libcxx/include/__config index 8a1893c13165b..982fa6e04288d 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -28,7 +28,7 @@ // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM. // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is // defined to XXYYZZ. -# define _LIBCPP_VERSION 210104 +# define _LIBCPP_VERSION 210105 # define _LIBCPP_CONCAT_IMPL(_X, _Y) _X##_Y # define _LIBCPP_CONCAT(_X, _Y) _LIBCPP_CONCAT_IMPL(_X, _Y) diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni index 6e72f3131f700..6758b86fd47bc 100644 --- a/llvm/utils/gn/secondary/llvm/version.gni +++ b/llvm/utils/gn/secondary/llvm/version.gni @@ -1,4 +1,4 @@ llvm_version_major = 21 llvm_version_minor = 1 -llvm_version_patch = 4 +llvm_version_patch = 5 llvm_version = "$llvm_version_major.$llvm_version_minor.$llvm_version_patch" diff --git a/llvm/utils/lit/lit/__init__.py b/llvm/utils/lit/lit/__init__.py index 26ffad29a9ea4..9fee5b71ed779 100644 --- a/llvm/utils/lit/lit/__init__.py +++ b/llvm/utils/lit/lit/__init__.py @@ -2,7 +2,7 @@ __author__ = "Daniel Dunbar" __email__ = "daniel@minormatter.com" -__versioninfo__ = (21, 1, 4) +__versioninfo__ = (21, 1, 5) __version__ = ".".join(str(v) for v in __versioninfo__) + "dev" __all__ = [] diff --git a/llvm/utils/mlgo-utils/mlgo/__init__.py b/llvm/utils/mlgo-utils/mlgo/__init__.py index 69a86d02b05a7..e274c2c3cc5f6 100644 --- a/llvm/utils/mlgo-utils/mlgo/__init__.py +++ b/llvm/utils/mlgo-utils/mlgo/__init__.py @@ -4,7 +4,7 @@ from datetime import timezone, datetime -__versioninfo__ = (21, 1, 4) +__versioninfo__ = (21, 1, 5) __version__ = ( ".".join(str(v) for v in __versioninfo__) + "dev" From 9faef12c7209a951d7cb757e031f53eacc886a51 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 9 Oct 2025 00:49:57 -0700 Subject: [PATCH 33/47] [sancov] Fix stack-depth tracking to use debug locations (#162428) As fixed in commits llvm/llvm-project@913f7e9, llvm/llvm-project@4a8b124, and llvm/llvm-project@4eef2e3, also fix the stack-depth tracking code to use InstrumentationIRBuilder, and set the Call's Debug location to EntryLoc. https://github.com/ClangBuiltLinux/linux/issues/2125 cc @nathanchance @melver @JustinStitt @bwendling (cherry picked from commit 28b7f669e5c6427402f36c5a08def4b34089c7cd) --- .../Instrumentation/SanitizerCoverage.cpp | 13 ++- .../SanitizerCoverage/missing_dbg.ll | 92 +++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp index 5b8ea1547ca2f..b74a0708b67ae 100644 --- a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -1084,8 +1084,10 @@ void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, auto ThenTerm = SplitBlockAndInsertIfThen( IRB.CreateIsNull(Load), &*IP, false, MDBuilder(IRB.getContext()).createUnlikelyBranchWeights()); - IRBuilder<> ThenIRB(ThenTerm); + InstrumentationIRBuilder ThenIRB(ThenTerm); auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr); + if (EntryLoc) + Store->setDebugLoc(EntryLoc); Load->setNoSanitizeMetadata(); Store->setNoSanitizeMetadata(); } @@ -1131,7 +1133,10 @@ void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, EstimatedStackSize >= Options.StackDepthCallbackMin) { if (InsertBefore) IRB.SetInsertPoint(InsertBefore); - IRB.CreateCall(SanCovStackDepthCallback)->setCannotMerge(); + auto Call = IRB.CreateCall(SanCovStackDepthCallback); + if (EntryLoc) + Call->setDebugLoc(EntryLoc); + Call->setCannotMerge(); } } else { // Check stack depth. If it's the deepest so far, record it. @@ -1144,8 +1149,10 @@ void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, auto ThenTerm = SplitBlockAndInsertIfThen( IsStackLower, &*IP, false, MDBuilder(IRB.getContext()).createUnlikelyBranchWeights()); - IRBuilder<> ThenIRB(ThenTerm); + InstrumentationIRBuilder ThenIRB(ThenTerm); auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack); + if (EntryLoc) + Store->setDebugLoc(EntryLoc); LowestStack->setNoSanitizeMetadata(); Store->setNoSanitizeMetadata(); } diff --git a/llvm/test/Instrumentation/SanitizerCoverage/missing_dbg.ll b/llvm/test/Instrumentation/SanitizerCoverage/missing_dbg.ll index 35684346c4d5a..07b9a1ce496d9 100644 --- a/llvm/test/Instrumentation/SanitizerCoverage/missing_dbg.ll +++ b/llvm/test/Instrumentation/SanitizerCoverage/missing_dbg.ll @@ -1,5 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=2 -S | FileCheck %s +; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -sanitizer-coverage-stack-depth-callback-min=1 -S | FileCheck %s --check-prefix=CHECK-STACK-CALLBACK +; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-stack-depth -S | FileCheck %s --check-prefix=CHECK-STACK-DEPTH target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" @@ -55,6 +57,86 @@ entry: ret i32 %t } +define i32 @with_dbg_stack_callback(ptr %a) !dbg !8 { +; CHECK-STACK-CALLBACK-LABEL: define i32 @with_dbg_stack_callback( +; CHECK-STACK-CALLBACK-SAME: ptr [[A:%.*]]) !dbg [[DBG8:![0-9]+]] { +; CHECK-STACK-CALLBACK-NEXT: entry: +; CHECK-STACK-CALLBACK-NEXT: [[BUF:%.*]] = alloca [64 x i8], align 1 +; CHECK-STACK-CALLBACK-NEXT: call void @__sanitizer_cov_stack_depth() #[[ATTR1:[0-9]+]], !dbg [[DBG9:![0-9]+]] +; CHECK-STACK-CALLBACK-NEXT: %t = load i32, ptr [[A]], align 4 +; CHECK-STACK-CALLBACK-NEXT: call void @external_func() +; CHECK-STACK-CALLBACK-NEXT: ret i32 %t +; +entry: + %buf = alloca [64 x i8], align 1 + %t = load i32, ptr %a, align 4 + call void @external_func() + ret i32 %t +} + +define i32 @with_dbg_stack_depth(ptr %a) !dbg !10 { +; CHECK-STACK-DEPTH-LABEL: define i32 @with_dbg_stack_depth( +; CHECK-STACK-DEPTH-SAME: ptr [[A:%.*]]) !dbg [[DBG10:![0-9]+]] { +; CHECK-STACK-DEPTH-NEXT: entry: +; CHECK-STACK-DEPTH-NEXT: [[BUF:%.*]] = alloca [64 x i8], align 1 +; CHECK-STACK-DEPTH-NEXT: [[TMP1:%.*]] = call ptr @llvm.frameaddress.p0(i32 0) +; CHECK-STACK-DEPTH-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64 +; CHECK-STACK-DEPTH-NEXT: [[TMP3:%.*]] = load i64, ptr @__sancov_lowest_stack, align 8 +; CHECK-STACK-DEPTH-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP2]], [[TMP3]] +; CHECK-STACK-DEPTH-NEXT: br i1 [[TMP4]], label {{%.*}}, label {{%.*}} +; CHECK-STACK-DEPTH: store i64 [[TMP2]], ptr @__sancov_lowest_stack, align 8, !dbg [[DBG11:![0-9]+]], {{.*}}!nosanitize +; CHECK-STACK-DEPTH: %t = load i32, ptr [[A]], align 4 +; CHECK-STACK-DEPTH-NEXT: call void @external_func() +; CHECK-STACK-DEPTH-NEXT: ret i32 %t +; +entry: + %buf = alloca [64 x i8], align 1 + %t = load i32, ptr %a, align 4 + call void @external_func() + ret i32 %t +} + +define i32 @without_dbg_stack_callback(ptr %a) { +; CHECK-STACK-CALLBACK-LABEL: define i32 @without_dbg_stack_callback( +; CHECK-STACK-CALLBACK-SAME: ptr [[A:%.*]]) { +; CHECK-STACK-CALLBACK-NEXT: entry: +; CHECK-STACK-CALLBACK-NEXT: [[BUF:%.*]] = alloca [64 x i8], align 1 +; CHECK-STACK-CALLBACK-NEXT: call void @__sanitizer_cov_stack_depth() #[[ATTR1]] +; CHECK-STACK-CALLBACK-NEXT: %t = load i32, ptr [[A]], align 4 +; CHECK-STACK-CALLBACK-NEXT: call void @external_func() +; CHECK-STACK-CALLBACK-NEXT: ret i32 %t +; +entry: + %buf = alloca [64 x i8], align 1 + %t = load i32, ptr %a, align 4 + call void @external_func() + ret i32 %t +} + +define i32 @without_dbg_stack_depth(ptr %a) { +; CHECK-STACK-DEPTH-LABEL: define i32 @without_dbg_stack_depth( +; CHECK-STACK-DEPTH-SAME: ptr [[A:%.*]]) { +; CHECK-STACK-DEPTH-NEXT: entry: +; CHECK-STACK-DEPTH-NEXT: [[BUF:%.*]] = alloca [64 x i8], align 1 +; CHECK-STACK-DEPTH-NEXT: [[TMP1:%.*]] = call ptr @llvm.frameaddress.p0(i32 0) +; CHECK-STACK-DEPTH-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64 +; CHECK-STACK-DEPTH-NEXT: [[TMP3:%.*]] = load i64, ptr @__sancov_lowest_stack, align 8 +; CHECK-STACK-DEPTH-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP2]], [[TMP3]] +; CHECK-STACK-DEPTH-NEXT: br i1 [[TMP4]], label {{%.*}}, label {{%.*}} +; CHECK-STACK-DEPTH: store i64 [[TMP2]], ptr @__sancov_lowest_stack, align 8, {{.*}}!nosanitize +; CHECK-STACK-DEPTH: %t = load i32, ptr [[A]], align 4 +; CHECK-STACK-DEPTH-NEXT: call void @external_func() +; CHECK-STACK-DEPTH-NEXT: ret i32 %t +; +entry: + %buf = alloca [64 x i8], align 1 + %t = load i32, ptr %a, align 4 + call void @external_func() + ret i32 %t +} + +declare void @external_func() + !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!2} @@ -66,6 +148,10 @@ entry: !5 = !{} !6 = !DILocation(line: 192, scope: !3) !7 = !DILocation(line: 0, scope: !3) +!8 = distinct !DISubprogram(name: "with_dbg_stack_callback", scope: !1, file: !1, line: 200, type: !4, scopeLine: 200, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) +!9 = !DILocation(line: 200, scope: !8) +!10 = distinct !DISubprogram(name: "with_dbg_stack_depth", scope: !1, file: !1, line: 210, type: !4, scopeLine: 210, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0) +!11 = !DILocation(line: 210, scope: !10) ;. ; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C89, file: [[META1:![0-9]+]], isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None) @@ -76,3 +162,9 @@ entry: ; CHECK: [[DBG6]] = !DILocation(line: 192, scope: [[DBG3]]) ; CHECK: [[DBG7]] = !DILocation(line: 0, scope: [[DBG3]]) ;. +; CHECK-STACK-CALLBACK: [[DBG8]] = distinct !DISubprogram(name: "with_dbg_stack_callback", scope: {{.*}}, file: {{.*}}, line: 200 +; CHECK-STACK-CALLBACK: [[DBG9]] = !DILocation(line: 200, scope: [[DBG8]]) +;. +; CHECK-STACK-DEPTH: [[DBG10]] = distinct !DISubprogram(name: "with_dbg_stack_depth", scope: {{.*}}, file: {{.*}}, line: 210 +; CHECK-STACK-DEPTH: [[DBG11]] = !DILocation(line: 210, scope: [[DBG10]]) +;. From 2cdfa9d9383c165f68a41a145c21d7f523485a8d Mon Sep 17 00:00:00 2001 From: Abhishek Kaushik Date: Sat, 18 Oct 2025 02:30:18 +0530 Subject: [PATCH 34/47] [X86] Use pseudo instructions to zero registers in `buildClearRegister` (#163358) In `buildClearRegister` use the correct pseudo-opcode for each register class: - For `VR128`, use `V_SET0` - For `VR256`, use `AVX_SET0` - For `VR512`, use `AVX512_512_SET0` - For `VK*`, use `KSET0Q/KSET0W` This avoids illegal register/opcode pairings and machine verifier errors when clearing call-used registers under `-fzero-call-used-regs=used`. Fixes: #163053 --------- Co-authored-by: Simon Pilgrim (cherry picked from commit 228dae786b94bb85fb34bc157a43ca6c16932b6d) --- llvm/lib/Target/X86/X86InstrInfo.cpp | 22 +- .../CodeGen/X86/zero-call-used-regs-simd.ll | 216 ++++++++++++++++++ 2 files changed, 221 insertions(+), 17 deletions(-) create mode 100644 llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp index abf365eedec39..9bf58dd3458cd 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.cpp +++ b/llvm/lib/Target/X86/X86InstrInfo.cpp @@ -10739,39 +10739,27 @@ void X86InstrInfo::buildClearRegister(Register Reg, MachineBasicBlock &MBB, if (!ST.hasSSE1()) return; - // PXOR is safe to use because it doesn't affect flags. - BuildMI(MBB, Iter, DL, get(X86::PXORrr), Reg) - .addReg(Reg, RegState::Undef) - .addReg(Reg, RegState::Undef); + BuildMI(MBB, Iter, DL, get(X86::V_SET0), Reg); } else if (X86::VR256RegClass.contains(Reg)) { // YMM# if (!ST.hasAVX()) return; - // VPXOR is safe to use because it doesn't affect flags. - BuildMI(MBB, Iter, DL, get(X86::VPXORrr), Reg) - .addReg(Reg, RegState::Undef) - .addReg(Reg, RegState::Undef); + BuildMI(MBB, Iter, DL, get(X86::AVX_SET0), Reg); } else if (X86::VR512RegClass.contains(Reg)) { // ZMM# if (!ST.hasAVX512()) return; - // VPXORY is safe to use because it doesn't affect flags. - BuildMI(MBB, Iter, DL, get(X86::VPXORYrr), Reg) - .addReg(Reg, RegState::Undef) - .addReg(Reg, RegState::Undef); + BuildMI(MBB, Iter, DL, get(X86::AVX512_512_SET0), Reg); } else if (X86::VK1RegClass.contains(Reg) || X86::VK2RegClass.contains(Reg) || X86::VK4RegClass.contains(Reg) || X86::VK8RegClass.contains(Reg) || X86::VK16RegClass.contains(Reg)) { if (!ST.hasVLX()) return; - // KXOR is safe to use because it doesn't affect flags. - unsigned Op = ST.hasBWI() ? X86::KXORQkk : X86::KXORWkk; - BuildMI(MBB, Iter, DL, get(Op), Reg) - .addReg(Reg, RegState::Undef) - .addReg(Reg, RegState::Undef); + unsigned Op = ST.hasBWI() ? X86::KSET0Q : X86::KSET0W; + BuildMI(MBB, Iter, DL, get(Op), Reg); } } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll new file mode 100644 index 0000000000000..d9253e0ca127b --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-simd.ll @@ -0,0 +1,216 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; RUN: llc < %s -mtriple=x86_64-- -mattr=+sse2 -verify-machineinstrs | FileCheck %s --check-prefixes=SSE +; RUN: llc < %s -mtriple=x86_64-- -mattr=+avx -verify-machineinstrs | FileCheck %s --check-prefixes=AVX,AVX1 +; RUN: llc < %s -mtriple=x86_64-- -mattr=+avx2 -verify-machineinstrs | FileCheck %s --check-prefixes=AVX,AVX2 +; RUN: llc < %s -mtriple=x86_64-- -mattr=+avx512f,+avx512vl -verify-machineinstrs | FileCheck %s --check-prefixes=AVX512,AVX512VL +; RUN: llc < %s -mtriple=x86_64-- -mattr=+avx512f,+avx512vl,+avx512bw -verify-machineinstrs | FileCheck %s --check-prefixes=AVX512,AVX512BW + +define void @zero_xmm(<4 x i32> %arg) #0 { +; SSE-LABEL: zero_xmm: +; SSE: # %bb.0: +; SSE-NEXT: movaps %xmm0, 0 +; SSE-NEXT: xorps %xmm0, %xmm0 +; SSE-NEXT: retq +; +; AVX-LABEL: zero_xmm: +; AVX: # %bb.0: +; AVX-NEXT: vmovaps %xmm0, 0 +; AVX-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX-NEXT: retq +; +; AVX512-LABEL: zero_xmm: +; AVX512: # %bb.0: +; AVX512-NEXT: vmovaps %xmm0, 0 +; AVX512-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX512-NEXT: retq + store <4 x i32> %arg, ptr null, align 32 + ret void +} + +define void @zero_ymm(<8 x i32> %arg) #0 { +; SSE-LABEL: zero_ymm: +; SSE: # %bb.0: +; SSE-NEXT: movaps %xmm1, 16 +; SSE-NEXT: movaps %xmm0, 0 +; SSE-NEXT: xorps %xmm0, %xmm0 +; SSE-NEXT: xorps %xmm1, %xmm1 +; SSE-NEXT: retq +; +; AVX-LABEL: zero_ymm: +; AVX: # %bb.0: +; AVX-NEXT: vmovaps %ymm0, 0 +; AVX-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX-NEXT: vzeroupper +; AVX-NEXT: retq +; +; AVX512-LABEL: zero_ymm: +; AVX512: # %bb.0: +; AVX512-NEXT: vmovaps %ymm0, 0 +; AVX512-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX512-NEXT: vzeroupper +; AVX512-NEXT: retq + store <8 x i32> %arg, ptr null, align 32 + ret void +} + +define void @zero_zmm(<16 x i32> %arg) #0 { +; SSE-LABEL: zero_zmm: +; SSE: # %bb.0: +; SSE-NEXT: movaps %xmm3, 48 +; SSE-NEXT: movaps %xmm2, 32 +; SSE-NEXT: movaps %xmm1, 16 +; SSE-NEXT: movaps %xmm0, 0 +; SSE-NEXT: xorps %xmm0, %xmm0 +; SSE-NEXT: xorps %xmm1, %xmm1 +; SSE-NEXT: xorps %xmm2, %xmm2 +; SSE-NEXT: xorps %xmm3, %xmm3 +; SSE-NEXT: retq +; +; AVX-LABEL: zero_zmm: +; AVX: # %bb.0: +; AVX-NEXT: vmovaps %ymm1, 32 +; AVX-NEXT: vmovaps %ymm0, 0 +; AVX-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX-NEXT: vxorps %xmm1, %xmm1, %xmm1 +; AVX-NEXT: vzeroupper +; AVX-NEXT: retq +; +; AVX512-LABEL: zero_zmm: +; AVX512: # %bb.0: +; AVX512-NEXT: vmovups %zmm0, 0 +; AVX512-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX512-NEXT: vzeroupper +; AVX512-NEXT: retq + store <16 x i32> %arg, ptr null, align 32 + ret void +} + +define void @zero_k(<8 x i32> %arg, <8 x i1> %mask) #0 { +; SSE-LABEL: zero_k: +; SSE: # %bb.0: +; SSE-NEXT: psllw $15, %xmm2 +; SSE-NEXT: packsswb %xmm2, %xmm2 +; SSE-NEXT: pmovmskb %xmm2, %eax +; SSE-NEXT: testb $1, %al +; SSE-NEXT: jne .LBB3_1 +; SSE-NEXT: # %bb.2: # %else +; SSE-NEXT: testb $2, %al +; SSE-NEXT: jne .LBB3_3 +; SSE-NEXT: .LBB3_4: # %else2 +; SSE-NEXT: testb $4, %al +; SSE-NEXT: jne .LBB3_5 +; SSE-NEXT: .LBB3_6: # %else4 +; SSE-NEXT: testb $8, %al +; SSE-NEXT: jne .LBB3_7 +; SSE-NEXT: .LBB3_8: # %else6 +; SSE-NEXT: testb $16, %al +; SSE-NEXT: jne .LBB3_9 +; SSE-NEXT: .LBB3_10: # %else8 +; SSE-NEXT: testb $32, %al +; SSE-NEXT: jne .LBB3_11 +; SSE-NEXT: .LBB3_12: # %else10 +; SSE-NEXT: testb $64, %al +; SSE-NEXT: jne .LBB3_13 +; SSE-NEXT: .LBB3_14: # %else12 +; SSE-NEXT: testb $-128, %al +; SSE-NEXT: je .LBB3_16 +; SSE-NEXT: .LBB3_15: # %cond.store13 +; SSE-NEXT: pshufd {{.*#+}} xmm0 = xmm1[3,3,3,3] +; SSE-NEXT: movd %xmm0, 28 +; SSE-NEXT: .LBB3_16: # %else14 +; SSE-NEXT: xorl %eax, %eax +; SSE-NEXT: pxor %xmm0, %xmm0 +; SSE-NEXT: pxor %xmm1, %xmm1 +; SSE-NEXT: pxor %xmm2, %xmm2 +; SSE-NEXT: retq +; SSE-NEXT: .LBB3_1: # %cond.store +; SSE-NEXT: movd %xmm0, 0 +; SSE-NEXT: testb $2, %al +; SSE-NEXT: je .LBB3_4 +; SSE-NEXT: .LBB3_3: # %cond.store1 +; SSE-NEXT: pshufd {{.*#+}} xmm2 = xmm0[1,1,1,1] +; SSE-NEXT: movd %xmm2, 4 +; SSE-NEXT: testb $4, %al +; SSE-NEXT: je .LBB3_6 +; SSE-NEXT: .LBB3_5: # %cond.store3 +; SSE-NEXT: pshufd {{.*#+}} xmm2 = xmm0[2,3,2,3] +; SSE-NEXT: movd %xmm2, 8 +; SSE-NEXT: testb $8, %al +; SSE-NEXT: je .LBB3_8 +; SSE-NEXT: .LBB3_7: # %cond.store5 +; SSE-NEXT: pshufd {{.*#+}} xmm0 = xmm0[3,3,3,3] +; SSE-NEXT: movd %xmm0, 12 +; SSE-NEXT: testb $16, %al +; SSE-NEXT: je .LBB3_10 +; SSE-NEXT: .LBB3_9: # %cond.store7 +; SSE-NEXT: movd %xmm1, 16 +; SSE-NEXT: testb $32, %al +; SSE-NEXT: je .LBB3_12 +; SSE-NEXT: .LBB3_11: # %cond.store9 +; SSE-NEXT: pshufd {{.*#+}} xmm0 = xmm1[1,1,1,1] +; SSE-NEXT: movd %xmm0, 20 +; SSE-NEXT: testb $64, %al +; SSE-NEXT: je .LBB3_14 +; SSE-NEXT: .LBB3_13: # %cond.store11 +; SSE-NEXT: pshufd {{.*#+}} xmm0 = xmm1[2,3,2,3] +; SSE-NEXT: movd %xmm0, 24 +; SSE-NEXT: testb $-128, %al +; SSE-NEXT: jne .LBB3_15 +; SSE-NEXT: jmp .LBB3_16 +; +; AVX1-LABEL: zero_k: +; AVX1: # %bb.0: +; AVX1-NEXT: vpmovzxwd {{.*#+}} xmm2 = xmm1[0],zero,xmm1[1],zero,xmm1[2],zero,xmm1[3],zero +; AVX1-NEXT: vpslld $31, %xmm2, %xmm2 +; AVX1-NEXT: vpunpckhwd {{.*#+}} xmm1 = xmm1[4,4,5,5,6,6,7,7] +; AVX1-NEXT: vpslld $31, %xmm1, %xmm1 +; AVX1-NEXT: vinsertf128 $1, %xmm1, %ymm2, %ymm1 +; AVX1-NEXT: vmaskmovps %ymm0, %ymm1, 0 +; AVX1-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX1-NEXT: vxorps %xmm1, %xmm1, %xmm1 +; AVX1-NEXT: vpxor %xmm2, %xmm2, %xmm2 +; AVX1-NEXT: vxorps %xmm0, %xmm0, %xmm0 +; AVX1-NEXT: vxorps %xmm1, %xmm1, %xmm1 +; AVX1-NEXT: vpxor %xmm2, %xmm2, %xmm2 +; AVX1-NEXT: vzeroupper +; AVX1-NEXT: retq +; +; AVX2-LABEL: zero_k: +; AVX2: # %bb.0: +; AVX2-NEXT: vpmovzxwd {{.*#+}} ymm1 = xmm1[0],zero,xmm1[1],zero,xmm1[2],zero,xmm1[3],zero,xmm1[4],zero,xmm1[5],zero,xmm1[6],zero,xmm1[7],zero +; AVX2-NEXT: vpslld $31, %ymm1, %ymm1 +; AVX2-NEXT: vpmaskmovd %ymm0, %ymm1, 0 +; AVX2-NEXT: vpxor %xmm1, %xmm1, %xmm1 +; AVX2-NEXT: vpxor %xmm0, %xmm0, %xmm0 +; AVX2-NEXT: vpxor %xmm1, %xmm1, %xmm1 +; AVX2-NEXT: vzeroupper +; AVX2-NEXT: retq +; +; AVX512VL-LABEL: zero_k: +; AVX512VL: # %bb.0: +; AVX512VL-NEXT: vpmovsxwd %xmm1, %ymm1 +; AVX512VL-NEXT: vpslld $31, %ymm1, %ymm1 +; AVX512VL-NEXT: vptestmd %ymm1, %ymm1, %k1 +; AVX512VL-NEXT: vmovdqa32 %ymm0, 0 {%k1} +; AVX512VL-NEXT: vpxor %xmm1, %xmm1, %xmm1 +; AVX512VL-NEXT: vpxor %xmm0, %xmm0, %xmm0 +; AVX512VL-NEXT: vpxor %xmm1, %xmm1, %xmm1 +; AVX512VL-NEXT: kxorw %k0, %k0, %k1 +; AVX512VL-NEXT: vzeroupper +; AVX512VL-NEXT: retq +; +; AVX512BW-LABEL: zero_k: +; AVX512BW: # %bb.0: +; AVX512BW-NEXT: vpsllw $15, %xmm1, %xmm1 +; AVX512BW-NEXT: vpmovw2m %xmm1, %k1 +; AVX512BW-NEXT: vmovdqa32 %ymm0, 0 {%k1} +; AVX512BW-NEXT: vpxor %xmm1, %xmm1, %xmm1 +; AVX512BW-NEXT: vpxor %xmm0, %xmm0, %xmm0 +; AVX512BW-NEXT: kxorq %k0, %k0, %k1 +; AVX512BW-NEXT: vzeroupper +; AVX512BW-NEXT: retq + tail call void @llvm.masked.store.v8i32.p0(<8 x i32> %arg, ptr null, i32 32, <8 x i1> %mask) + ret void +} + +attributes #0 = { "zero-call-used-regs"="used" } From cd7d2db5a50cad76ad991d803ee645ba733c88a0 Mon Sep 17 00:00:00 2001 From: David Spickett Date: Tue, 21 Oct 2025 09:54:16 +0000 Subject: [PATCH 35/47] [lld][test] Fix AArch64 build attribute test cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test has been reported to fail on a release bot, but nothing in the content of the test on the release branch should cause this problem: ld.lld: error: cannot open output file /home/buildbot/as-worker-92/clang-with-thin-lto-ubuntu-rel/build/stage1/tools/lld/test/ELF/Output/aarch64-build-attributes.s.tmp: Is a directory The reason this is happening is that if the same builder happened to run the extended version of this test, from a build of main, it would create a directory with that name. The test on main stashes the temporary files in that directory. $ tree tools/lld/test/ tools/lld/test/ ├── CMakeFiles ├── ELF │   └── Output │   ├── aarch64-build-attributes.s.tmp │   │   ├── pauth-bti-gcs.s │   │   └── pauth-bti-pac.s │   ├── aarch64-build-attributes.s.tmp.merged.o │   ├── aarch64-build-attributes.s.tmp1.o │   ├── aarch64-build-attributes.s.tmp2.o │   └── aarch64-build-attributes.s.tmp3.o ├── Unit │   └── lit.site.cfg.py ├── cmake_install.cmake └── lit.site.cfg.py If you then had a 21.x build run, it would find that pre-existing directory and try to write to it as if it were a file. ld.lld: error: cannot open output file /home/david.spickett/build-llvm-aarch64/tools/lld/test/ELF/Output/aarch64-build-attributes.s.tmp: Is a directory To fix this, remove the file or directory names we're going to use, before the test starts. (if this were main I'd put all the files in one direcory, but in the interest of keeping release changes small I'm just adding the rm line) --- lld/test/ELF/aarch64-build-attributes.s | 1 + 1 file changed, 1 insertion(+) diff --git a/lld/test/ELF/aarch64-build-attributes.s b/lld/test/ELF/aarch64-build-attributes.s index 24e15f94e3d4a..815aed32f2aaa 100644 --- a/lld/test/ELF/aarch64-build-attributes.s +++ b/lld/test/ELF/aarch64-build-attributes.s @@ -1,4 +1,5 @@ // REQUIRES: aarch64 +// RUN: rm -rf %t %t.o %t.so %t2.o // RUN: llvm-mc -triple=aarch64 %s -filetype=obj -o %t.o // RUN: ld.lld %t.o --shared -o %t.so // RUN: llvm-readelf --sections %t.so | FileCheck %s From 1600cf0405844880def09cef3290f77d76a8e64b Mon Sep 17 00:00:00 2001 From: Aiden Grossman Date: Tue, 21 Oct 2025 14:04:03 -0700 Subject: [PATCH 36/47] [Github] Only look at previous commit for MacOS Premerge (#164483) Otherwise we're looking for a commit that does not exist given the fetch depth. Fixes #164430. (cherry picked from commit 288ef04d2f19c5b0e4ce3cae450c63a365ab11e9) --- .github/workflows/premerge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/premerge.yaml b/.github/workflows/premerge.yaml index d0518fa6879e2..8594e1e91ee99 100644 --- a/.github/workflows/premerge.yaml +++ b/.github/workflows/premerge.yaml @@ -149,7 +149,7 @@ jobs: uses: llvm/actions/install-ninja@main - name: Build and Test run: | - source <(git diff --name-only HEAD~2..HEAD | python3 .ci/compute_projects.py) + source <(git diff --name-only HEAD~1...HEAD | python3 .ci/compute_projects.py) if [[ "${projects_to_build}" == "" ]]; then echo "No projects to build" From e94561caef487fbbadb50ad66ef2b21529aa388a Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Tue, 21 Oct 2025 19:26:56 -0700 Subject: [PATCH 37/47] workflows/release-documentation: Allow secrets pass through from calling workflow (#162765) This should fix the part of the workflow that creates a PR with the new documentation. (cherry picked from commit 59d4d5c1c3b6f0f81ac51bea26605466268f83e9) --- .github/workflows/release-documentation.yml | 4 ++++ .github/workflows/release-tasks.yml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml index 5a0aa063d32ac..e07da3d9f3009 100644 --- a/.github/workflows/release-documentation.yml +++ b/.github/workflows/release-documentation.yml @@ -25,6 +25,10 @@ on: description: 'Upload documentation' required: false type: boolean + secrets: + WWW_RELEASES_TOKEN: + description: "Secret used to create a PR with the documentation changes." + required: false jobs: release-documentation: diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml index c9ae7e1ce97c3..894661a5112d5 100644 --- a/.github/workflows/release-tasks.yml +++ b/.github/workflows/release-tasks.yml @@ -54,6 +54,9 @@ jobs: with: release-version: ${{ needs.validate-tag.outputs.release-version }} upload: true + # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. + secrets: + WWW_RELEASES_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }} release-doxygen: name: Build and Upload Release Doxygen From 5c802f9eac663ceb59d2fa68c1afa6bdd2aac680 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Sun, 19 Oct 2025 09:26:22 +0200 Subject: [PATCH 38/47] [Clang] Do not warn on UTF-16 -> UTF-32 conversions. (#163927) UTF-16 to UTF-16 conversions seems widespread, and lone surrogate have a distinct representation in UTF-32. Lets not warn on this case to make the warning easier to adopt. This follows SG-16 guideline https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3695r2.html#changes-since-r1 Fixes #163719 --- clang/lib/Sema/SemaChecking.cpp | 9 ++++++++- clang/test/SemaCXX/warn-implicit-unicode-conversions.cpp | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index dd5b710d7e1d4..41bcf8fd493fc 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -12014,13 +12014,20 @@ static void DiagnoseMixedUnicodeImplicitConversion(Sema &S, const Type *Source, SourceLocation CC) { assert(Source->isUnicodeCharacterType() && Target->isUnicodeCharacterType() && Source != Target); + + // Lone surrogates have a distinct representation in UTF-32. + // Converting between UTF-16 and UTF-32 codepoints seems very widespread, + // so don't warn on such conversion. + if (Source->isChar16Type() && Target->isChar32Type()) + return; + Expr::EvalResult Result; if (E->EvaluateAsInt(Result, S.getASTContext(), Expr::SE_AllowSideEffects, S.isConstantEvaluatedContext())) { llvm::APSInt Value(32); Value = Result.Val.getInt(); bool IsASCII = Value <= 0x7F; - bool IsBMP = Value <= 0xD7FF || (Value >= 0xE000 && Value <= 0xFFFF); + bool IsBMP = Value <= 0xDFFF || (Value >= 0xE000 && Value <= 0xFFFF); bool ConversionPreservesSemantics = IsASCII || (!Source->isChar8Type() && !Target->isChar8Type() && IsBMP); diff --git a/clang/test/SemaCXX/warn-implicit-unicode-conversions.cpp b/clang/test/SemaCXX/warn-implicit-unicode-conversions.cpp index fcff006d0e028..f17f20ca25295 100644 --- a/clang/test/SemaCXX/warn-implicit-unicode-conversions.cpp +++ b/clang/test/SemaCXX/warn-implicit-unicode-conversions.cpp @@ -14,7 +14,7 @@ void test(char8_t u8, char16_t u16, char32_t u32) { c16(u32); // expected-warning {{implicit conversion from 'char32_t' to 'char16_t' may lose precision and change the meaning of the represented code unit}} c32(u8); // expected-warning {{implicit conversion from 'char8_t' to 'char32_t' may change the meaning of the represented code unit}} - c32(u16); // expected-warning {{implicit conversion from 'char16_t' to 'char32_t' may change the meaning of the represented code unit}} + c32(u16); c32(u32); @@ -30,7 +30,7 @@ void test(char8_t u8, char16_t u16, char32_t u32) { c16(char32_t(0x7f)); c16(char32_t(0x80)); c16(char32_t(0xD7FF)); - c16(char32_t(0xD800)); // expected-warning {{implicit conversion from 'char32_t' to 'char16_t' changes the meaning of the code unit '<0xD800>'}} + c16(char32_t(0xD800)); c16(char32_t(0xE000)); c16(char32_t(U'🐉')); // expected-warning {{implicit conversion from 'char32_t' to 'char16_t' changes the meaning of the code point '🐉'}} @@ -44,8 +44,8 @@ void test(char8_t u8, char16_t u16, char32_t u32) { c32(char16_t(0x80)); c32(char16_t(0xD7FF)); - c32(char16_t(0xD800)); // expected-warning {{implicit conversion from 'char16_t' to 'char32_t' changes the meaning of the code unit '<0xD800>'}} - c32(char16_t(0xDFFF)); // expected-warning {{implicit conversion from 'char16_t' to 'char32_t' changes the meaning of the code unit '<0xDFFF>'}} + c32(char16_t(0xD800)); + c32(char16_t(0xDFFF)); c32(char16_t(0xE000)); c32(char16_t(u'☕')); From 04102d998d0e4019c26654cf0b545a0af19819c7 Mon Sep 17 00:00:00 2001 From: Jonathan Thackray Date: Fri, 24 Oct 2025 00:13:53 +0100 Subject: [PATCH 39/47] [AArch64][llvm] Relax mandatory features for Armv9.6-A (#163973) `FEAT_FPRCVT` is removed from being mandatory in Armv9.6-A `FEAT_SVE2p2` is removed from being mandatory in Armv9.6-A (cherry picked from commit 0e8781100357b46c9ec6cd2e31a635ad2b2b3211) --- clang/test/Driver/aarch64-v96a.c | 4 ++-- llvm/lib/Target/AArch64/AArch64Features.td | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/test/Driver/aarch64-v96a.c b/clang/test/Driver/aarch64-v96a.c index de7890140ebd3..e0081bbbdabfe 100644 --- a/clang/test/Driver/aarch64-v96a.c +++ b/clang/test/Driver/aarch64-v96a.c @@ -6,7 +6,7 @@ // RUN: %clang -target aarch64 -mlittle-endian -march=armv9.6-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A %s // RUN: %clang -target aarch64_be -mlittle-endian -march=armv9.6a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A %s // RUN: %clang -target aarch64_be -mlittle-endian -march=armv9.6-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A %s -// GENERICV96A: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-cpu" "generic" "-target-feature" "+v9.6a"{{.*}} "-target-feature" "+cmpbr"{{.*}} "-target-feature" "+fprcvt"{{.*}} "-target-feature" "+sve2p2" +// GENERICV96A: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-cpu" "generic" "-target-feature" "+v9.6a"{{.*}} "-target-feature" "+cmpbr"{{.*}} // RUN: %clang -target aarch64_be -march=armv9.6a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A-BE %s // RUN: %clang -target aarch64_be -march=armv9.6-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A-BE %s @@ -14,7 +14,7 @@ // RUN: %clang -target aarch64 -mbig-endian -march=armv9.6-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A-BE %s // RUN: %clang -target aarch64_be -mbig-endian -march=armv9.6a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A-BE %s // RUN: %clang -target aarch64_be -mbig-endian -march=armv9.6-a -### -c %s 2>&1 | FileCheck -check-prefix=GENERICV96A-BE %s -// GENERICV96A-BE: "-cc1"{{.*}} "-triple" "aarch64_be{{.*}}" "-target-cpu" "generic" "-target-feature" "+v9.6a"{{.*}} "-target-feature" "+cmpbr"{{.*}} "-target-feature" "+fprcvt"{{.*}} "-target-feature" "+sve2p2" +// GENERICV96A-BE: "-cc1"{{.*}} "-triple" "aarch64_be{{.*}}" "-target-cpu" "generic" "-target-feature" "+v9.6a"{{.*}} "-target-feature" "+cmpbr"{{.*}} // ===== Features supported on aarch64 ===== diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td index 9973df865ea17..12159a9519737 100644 --- a/llvm/lib/Target/AArch64/AArch64Features.td +++ b/llvm/lib/Target/AArch64/AArch64Features.td @@ -923,8 +923,8 @@ def HasV9_5aOps : Architecture64<9, 5, "a", "v9.5a", [HasV9_4aOps, FeatureCPA], !listconcat(HasV9_4aOps.DefaultExts, [FeatureCPA, FeatureLUT, FeatureFAMINMAX])>; def HasV9_6aOps : Architecture64<9, 6, "a", "v9.6a", - [HasV9_5aOps, FeatureCMPBR, FeatureFPRCVT, FeatureSVE2p2, FeatureLSUI, FeatureOCCMO], - !listconcat(HasV9_5aOps.DefaultExts, [FeatureCMPBR, FeatureFPRCVT, FeatureSVE2p2, + [HasV9_5aOps, FeatureCMPBR, FeatureLSUI, FeatureOCCMO], + !listconcat(HasV9_5aOps.DefaultExts, [FeatureCMPBR, FeatureLSUI, FeatureOCCMO])>; def HasV8_0rOps : Architecture64<8, 0, "r", "v8r", [ //v8.1 From a2178ebcb02ad72c9063a0d1168c3953e8de61a6 Mon Sep 17 00:00:00 2001 From: Alexey Karyakin Date: Mon, 20 Oct 2025 19:42:16 -0500 Subject: [PATCH 40/47] [Hexagon] Incorrect MIR after "hexinsert" pass (#164021) The "hexinsert" pass tries to replace bitfield operations with Hexagon `insert` instructions. To limit memory usage, there is a limit on the internal table size. When the limit is reached, the state is not correctly cleaned up so defs from from new `insert` instructions are not deleted after processing the basic block. Later, these defs can be incorrectly used in other basic blocks even they are not reachable. Then compiler will crash with: *** Bad machine code: Virtual register defs don't dominate all uses. *** Fixes: #163774 (cherry picked from commit f3a60cf8dd602958f706c11db66e401257364be7) --- llvm/lib/Target/Hexagon/HexagonGenInsert.cpp | 8 +++- llvm/test/CodeGen/Hexagon/insert-big.ll | 45 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/Hexagon/insert-big.ll diff --git a/llvm/lib/Target/Hexagon/HexagonGenInsert.cpp b/llvm/lib/Target/Hexagon/HexagonGenInsert.cpp index a9201460d8e2e..2399e2a28eb22 100644 --- a/llvm/lib/Target/Hexagon/HexagonGenInsert.cpp +++ b/llvm/lib/Target/Hexagon/HexagonGenInsert.cpp @@ -921,6 +921,10 @@ void HexagonGenInsert::collectInBlock(MachineBasicBlock *B, // successors have been processed. RegisterSet BlockDefs, InsDefs; for (MachineInstr &MI : *B) { + // Stop if the map size is too large. + if (IFMap.size() >= MaxIFMSize) + break; + InsDefs.clear(); getInstrDefs(&MI, InsDefs); // Leave those alone. They are more transparent than "insert". @@ -943,8 +947,8 @@ void HexagonGenInsert::collectInBlock(MachineBasicBlock *B, findRecordInsertForms(VR, AVs); // Stop if the map size is too large. - if (IFMap.size() > MaxIFMSize) - return; + if (IFMap.size() >= MaxIFMSize) + break; } } diff --git a/llvm/test/CodeGen/Hexagon/insert-big.ll b/llvm/test/CodeGen/Hexagon/insert-big.ll new file mode 100644 index 0000000000000..a298930b6da6d --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/insert-big.ll @@ -0,0 +1,45 @@ +; Check that llc does not abort, which happened due to incorrect MIR. +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=1 < %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=2 < %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=3 < %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=4 < %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=5 < %s + +; Look for this symptom, in case llc does not check invalid IR. +; CHECK-NOT: insert(%14,%5,#5,#5) + +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=1 -debug-only=hexinsert -stop-after hexinsert < %s 2>&1 | FileCheck %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=2 -debug-only=hexinsert -stop-after hexinsert < %s 2>&1 | FileCheck %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=3 -debug-only=hexinsert -stop-after hexinsert < %s 2>&1 | FileCheck %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=4 -debug-only=hexinsert -stop-after hexinsert < %s 2>&1 | FileCheck %s +; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=5 -debug-only=hexinsert -stop-after hexinsert < %s 2>&1 | FileCheck %s + +define i32 @f(i32 %0, i32 %1, i32 %2) { +entry: + switch i32 %0, label %common.ret1 [ + i32 8907, label %3 + i32 4115, label %6 + ] + +common.ret1: + %common.ret1.op = phi i32 [ %5, %3 ], [ %526, %6 ], [ 0, %entry ] + ret i32 %common.ret1.op + +3: + %4 = shl i32 %2, 5 + %5 = and i32 %4, 992 + br label %common.ret1 + +6: + %7 = shl i32 %0, 10 + %8 = and i32 %7, 7168 + %9 = shl i32 %0, 5 + %10 = and i32 %9, 992 + %11 = or i32 %10, %8 + %12 = and i32 %0, 1 + %13 = or i32 %11, %12 + %14 = shl i32 %1, 1 + %15 = and i32 %14, 2031616 + %526 = or i32 %13, %15 + br label %common.ret1 +} From 495a2ab653aecdb59b41ccf63de3ab17df043589 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 21 Oct 2025 09:39:18 +0200 Subject: [PATCH 41/47] [Hexagon] Add REQUIRES: asserts to test This test uses -debug-only, so needs an assertion-enabled build. --- llvm/test/CodeGen/Hexagon/insert-big.ll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/test/CodeGen/Hexagon/insert-big.ll b/llvm/test/CodeGen/Hexagon/insert-big.ll index a298930b6da6d..8735a6679bf54 100644 --- a/llvm/test/CodeGen/Hexagon/insert-big.ll +++ b/llvm/test/CodeGen/Hexagon/insert-big.ll @@ -14,6 +14,8 @@ ; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=4 -debug-only=hexinsert -stop-after hexinsert < %s 2>&1 | FileCheck %s ; RUN: llc -O2 -mtriple=hexagon -insert-max-ifmap=5 -debug-only=hexinsert -stop-after hexinsert < %s 2>&1 | FileCheck %s +; REQUIRES: asserts + define i32 @f(i32 %0, i32 %1, i32 %2) { entry: switch i32 %0, label %common.ret1 [ From 11ef7520536c51b8ccb2c713631ae83f743ba7cd Mon Sep 17 00:00:00 2001 From: Michal R Date: Mon, 20 Oct 2025 20:07:55 +0200 Subject: [PATCH 42/47] [BPF] Support for `DW_TAG_variant_part` in BTF generation (#155783) Variant part, represented by `DW_TAG_variant_part` is a structure with a discriminant and different variants, from which only one can be active and valid at the same time. The discriminant is the main difference between variant parts and unions represented by `DW_TAG_union` type. Variant parts are used by Rust enums, which look like: ```rust pub enum MyEnum { First { a: u32, b: i32 }, Second(u32), } ``` This type's debug info is the following `DICompositeType` with `DW_TAG_structure_type` tag: ```llvm !4 = !DICompositeType(tag: DW_TAG_structure_type, name: "MyEnum", scope: !2, file: !5, size: 96, align: 32, flags: DIFlagPublic, elements: !6, templateParams: !16, identifier: "faba668fd9f71e9b7cf3b9ac5e8b93cb") ``` With one element being also a `DICompositeType`, but with `DW_TAG_variant_part` tag: ```llvm !6 = !{!7} !7 = !DICompositeType(tag: DW_TAG_variant_part, scope: !4, file: !5, size: 96, align: 32, elements: !8, templateParams: !16, identifier: "e4aee046fc86d111657622fdcb8c42f7", discriminator: !21) ``` Which has a discriminator: ```llvm !21 = !DIDerivedType(tag: DW_TAG_member, scope: !4, file: !5, baseType: !13, size: 32, align: 32, flags: DIFlagArtificial) ``` Which then holds different variants as `DIDerivedType` elements with `DW_TAG_member` tag: ```llvm !8 = !{!9, !17} !9 = !DIDerivedType(tag: DW_TAG_member, name: "First", scope: !7, file: !5, baseType: !10, size: 96, align: 32, extraData: i32 0) !10 = !DICompositeType(tag: DW_TAG_structure_type, name: "First", scope: !4, file: !5, size: 96, align: 32, flags: DIFlagPublic, elements: !11, templateParams: !16, identifier: "cc7748c842e275452db4205b190c8ff7") !11 = !{!12, !14} !12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !10, file: !5, baseType: !13, size: 32, align: 32, offset: 32, flags: DIFlagPublic) !13 = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) !14 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !10, file: !5, baseType: !15, size: 32, align: 32, offset: 64, flags: DIFlagPublic) !15 = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) !16 = !{} !17 = !DIDerivedType(tag: DW_TAG_member, name: "Second", scope: !7, file: !5, baseType: !18, size: 96, align: 32, extraData: i32 1) !18 = !DICompositeType(tag: DW_TAG_structure_type, name: "Second", scope: !4, file: !5, size: 96, align: 32, flags: DIFlagPublic, elements: !19, templateParams: !16, identifier: "a2094b1381f3082d504fbd0903aa7c06") !19 = !{!20} !20 = !DIDerivedType(tag: DW_TAG_member, name: "__0", scope: !18, file: !5, baseType: !13, size: 32, align: 32, offset: 32, flags: DIFlagPublic) ``` BPF backend was assuming that all the elements of any `DICompositeType` have tag `DW_TAG_member` and are instances of `DIDerivedType`. However, the single element of the outer composite type `!4` has tag `DW_TAG_variant_part` and is an instance of `DICompositeType`. The unconditional call of `cast` on all elements was causing an assertion failure when any Rust code with enums was compiled to the BPF target. Fix that by: * Handling `DW_TAG_variant_part` in `visitStructType`. * Replacing unconditional call of `cast` over `DICompositeType` elements with a `switch` statement, handling both `DW_TAG_member` and `DW_TAG_variant_part` and casting the element to an appropriate type (`DIDerivedType` or `DICompositeType`). Fixes: https://github.com/llvm/llvm-project/issues/155778 --- llvm/lib/Target/BPF/BTFDebug.cpp | 112 ++++++++++++++++++---- llvm/test/CodeGen/BPF/BTF/variant-part.ll | 87 +++++++++++++++++ 2 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 llvm/test/CodeGen/BPF/BTF/variant-part.ll diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp index 1e29a0f1e85a1..4ec7a93891aef 100644 --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/Target/BPF/BTFDebug.cpp @@ -14,6 +14,7 @@ #include "BPF.h" #include "BPFCORE.h" #include "MCTargetDesc/BPFMCTargetDesc.h" +#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineModuleInfo.h" @@ -23,6 +24,7 @@ #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Target/TargetLoweringObjectFile.h" @@ -301,21 +303,59 @@ void BTFTypeStruct::completeType(BTFDebug &BDebug) { BTFType.NameOff = BDebug.addString(STy->getName()); + if (STy->getTag() == dwarf::DW_TAG_variant_part) { + // Variant parts might have a discriminator, which has its own memory + // location, and variants, which share the memory location afterwards. LLVM + // DI doesn't consider discriminator as an element and instead keeps + // it as a separate reference. + // To keep BTF simple, let's represent the structure as an union with + // discriminator as the first element. + // The offsets inside variant types are already handled correctly in the + // DI. + const auto *DTy = STy->getDiscriminator(); + if (DTy) { + struct BTF::BTFMember Discriminator; + + Discriminator.NameOff = BDebug.addString(DTy->getName()); + Discriminator.Offset = DTy->getOffsetInBits(); + const auto *BaseTy = DTy->getBaseType(); + Discriminator.Type = BDebug.getTypeId(BaseTy); + + Members.push_back(Discriminator); + } + } + // Add struct/union members. const DINodeArray Elements = STy->getElements(); for (const auto *Element : Elements) { struct BTF::BTFMember BTFMember; - const auto *DDTy = cast(Element); - BTFMember.NameOff = BDebug.addString(DDTy->getName()); - if (HasBitField) { - uint8_t BitFieldSize = DDTy->isBitField() ? DDTy->getSizeInBits() : 0; - BTFMember.Offset = BitFieldSize << 24 | DDTy->getOffsetInBits(); - } else { - BTFMember.Offset = DDTy->getOffsetInBits(); + switch (Element->getTag()) { + case dwarf::DW_TAG_member: { + const auto *DDTy = cast(Element); + + BTFMember.NameOff = BDebug.addString(DDTy->getName()); + if (HasBitField) { + uint8_t BitFieldSize = DDTy->isBitField() ? DDTy->getSizeInBits() : 0; + BTFMember.Offset = BitFieldSize << 24 | DDTy->getOffsetInBits(); + } else { + BTFMember.Offset = DDTy->getOffsetInBits(); + } + const auto *BaseTy = tryRemoveAtomicType(DDTy->getBaseType()); + BTFMember.Type = BDebug.getTypeId(BaseTy); + break; + } + case dwarf::DW_TAG_variant_part: { + const auto *DCTy = dyn_cast(Element); + + BTFMember.NameOff = BDebug.addString(DCTy->getName()); + BTFMember.Offset = DCTy->getOffsetInBits(); + BTFMember.Type = BDebug.getTypeId(DCTy); + break; + } + default: + llvm_unreachable("Unexpected DI tag of a struct/union element"); } - const auto *BaseTy = tryRemoveAtomicType(DDTy->getBaseType()); - BTFMember.Type = BDebug.getTypeId(BaseTy); Members.push_back(BTFMember); } } @@ -672,16 +712,28 @@ void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct, uint32_t &TypeId) { const DINodeArray Elements = CTy->getElements(); uint32_t VLen = Elements.size(); + // Variant parts might have a discriminator. LLVM DI doesn't consider it as + // an element and instead keeps it as a separate reference. But we represent + // it as an element in BTF. + if (CTy->getTag() == dwarf::DW_TAG_variant_part) { + const auto *DTy = CTy->getDiscriminator(); + if (DTy) { + visitTypeEntry(DTy); + VLen++; + } + } if (VLen > BTF::MAX_VLEN) return; // Check whether we have any bitfield members or not bool HasBitField = false; for (const auto *Element : Elements) { - auto E = cast(Element); - if (E->isBitField()) { - HasBitField = true; - break; + if (Element->getTag() == dwarf::DW_TAG_member) { + auto E = cast(Element); + if (E->isBitField()) { + HasBitField = true; + break; + } } } @@ -696,9 +748,22 @@ void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct, // Visit all struct members. int FieldNo = 0; for (const auto *Element : Elements) { - const auto Elem = cast(Element); - visitTypeEntry(Elem); - processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo); + switch (Element->getTag()) { + case dwarf::DW_TAG_member: { + const auto Elem = cast(Element); + visitTypeEntry(Elem); + processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo); + break; + } + case dwarf::DW_TAG_variant_part: { + const auto Elem = cast(Element); + visitTypeEntry(Elem); + processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo); + break; + } + default: + llvm_unreachable("Unexpected DI tag of a struct/union element"); + } FieldNo++; } } @@ -781,16 +846,25 @@ void BTFDebug::visitFwdDeclType(const DICompositeType *CTy, bool IsUnion, void BTFDebug::visitCompositeType(const DICompositeType *CTy, uint32_t &TypeId) { auto Tag = CTy->getTag(); - if (Tag == dwarf::DW_TAG_structure_type || Tag == dwarf::DW_TAG_union_type) { + switch (Tag) { + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_variant_part: // Handle forward declaration differently as it does not have members. if (CTy->isForwardDecl()) visitFwdDeclType(CTy, Tag == dwarf::DW_TAG_union_type, TypeId); else visitStructType(CTy, Tag == dwarf::DW_TAG_structure_type, TypeId); - } else if (Tag == dwarf::DW_TAG_array_type) + break; + case dwarf::DW_TAG_array_type: visitArrayType(CTy, TypeId); - else if (Tag == dwarf::DW_TAG_enumeration_type) + break; + case dwarf::DW_TAG_enumeration_type: visitEnumType(CTy, TypeId); + break; + default: + llvm_unreachable("Unexpected DI tag of a composite type"); + } } bool BTFDebug::IsForwardDeclCandidate(const DIType *Base) { diff --git a/llvm/test/CodeGen/BPF/BTF/variant-part.ll b/llvm/test/CodeGen/BPF/BTF/variant-part.ll new file mode 100644 index 0000000000000..1071e618f601b --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/variant-part.ll @@ -0,0 +1,87 @@ +; RUN: llc -mtriple=bpfel -filetype=obj -o %t1 %s +; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1 +; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s +; RUN: llc -mtriple=bpfeb -filetype=obj -o %t1 %s +; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1 +; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s +; +; Source: +; #![no_std] +; #![no_main] +; +; pub enum MyEnum { +; First { a: u32, b: i32 }, +; Second(u32), +; } +; +; #[unsafe(no_mangle)] +; pub static X: MyEnum = MyEnum::First { a: 54, b: -23 }; +; +; #[cfg(not(test))] +; #[panic_handler] +; fn panic(_info: &core::panic::PanicInfo) -> ! { +; loop {} +; } +; Compilation flag: +; cargo +nightly rustc -Zbuild-std=core --target=bpfel-unknown-none -- --emit=llvm-bc +; llvm-extract --glob=X $(find target/ -name "*.bc" | head -n 1) -o variant-part.bc +; llvm-dis variant-part.bc -o variant-part.ll + +; ModuleID = 'variant-part.bc' +source_filename = "c0znihgkvro8hs0n88fgrtg6x" +target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" +target triple = "bpfel" + +@X = constant [12 x i8] c"\00\00\00\006\00\00\00\E9\FF\FF\FF", align 4, !dbg !0 + +!llvm.module.flags = !{!22, !23, !24, !25} +!llvm.ident = !{!26} +!llvm.dbg.cu = !{!27} + +; CHECK-BTF: [1] STRUCT 'MyEnum' size=12 vlen=1 +; CHECK-BTF-NEXT: '(anon)' type_id=3 bits_offset=0 +; CHECK-BTF-NEXT: [2] INT 'u32' size=4 bits_offset=0 nr_bits=32 encoding=(none) +; CHECK-BTF-NEXT: [3] UNION '(anon)' size=12 vlen=3 +; CHECK-BTF-NEXT: '(anon)' type_id=2 bits_offset=0 +; CHECK-BTF-NEXT: 'First' type_id=4 bits_offset=0 +; CHECK-BTF-NEXT: 'Second' type_id=6 bits_offset=0 +; CHECK-BTF-NEXT: [4] STRUCT 'First' size=12 vlen=2 +; CHECK-BTF-NEXT: 'a' type_id=2 bits_offset=32 +; CHECK-BTF-NEXT: 'b' type_id=5 bits_offset=64 +; CHECK-BTF-NEXT: [5] INT 'i32' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK-BTF-NEXT: [6] STRUCT 'Second' size=12 vlen=1 +; CHECK-BTF-NEXT: '__0' type_id=2 bits_offset=32 +; CHECK-BTF-NEXT: [7] VAR 'X' type_id=1, linkage=global +; CHECK-BTF-NEXT: [8] DATASEC '.rodata' size=0 vlen=1 +; CHECK-BTF-NEXT: type_id=7 offset=0 size=12 + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "X", scope: !2, file: !3, line: 10, type: !4, isLocal: false, isDefinition: true, align: 32) +!2 = !DINamespace(name: "variant_part", scope: null) +!3 = !DIFile(filename: "variant-part/src/main.rs", directory: "/tmp/variant-part", checksumkind: CSK_MD5, checksum: "b94cd53886ea8f14cbc116b36bc7dd36") +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "MyEnum", scope: !2, file: !5, size: 96, align: 32, flags: DIFlagPublic, elements: !6, templateParams: !16, identifier: "faba668fd9f71e9b7cf3b9ac5e8b93cb") +!5 = !DIFile(filename: "", directory: "") +!6 = !{!7} +!7 = !DICompositeType(tag: DW_TAG_variant_part, scope: !4, file: !5, size: 96, align: 32, elements: !8, templateParams: !16, identifier: "e4aee046fc86d111657622fdcb8c42f7", discriminator: !21) +!8 = !{!9, !17} +!9 = !DIDerivedType(tag: DW_TAG_member, name: "First", scope: !7, file: !5, baseType: !10, size: 96, align: 32, extraData: i32 0) +!10 = !DICompositeType(tag: DW_TAG_structure_type, name: "First", scope: !4, file: !5, size: 96, align: 32, flags: DIFlagPublic, elements: !11, templateParams: !16, identifier: "cc7748c842e275452db4205b190c8ff7") +!11 = !{!12, !14} +!12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !10, file: !5, baseType: !13, size: 32, align: 32, offset: 32, flags: DIFlagPublic) +!13 = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) +!14 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !10, file: !5, baseType: !15, size: 32, align: 32, offset: 64, flags: DIFlagPublic) +!15 = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) +!16 = !{} +!17 = !DIDerivedType(tag: DW_TAG_member, name: "Second", scope: !7, file: !5, baseType: !18, size: 96, align: 32, extraData: i32 1) +!18 = !DICompositeType(tag: DW_TAG_structure_type, name: "Second", scope: !4, file: !5, size: 96, align: 32, flags: DIFlagPublic, elements: !19, templateParams: !16, identifier: "a2094b1381f3082d504fbd0903aa7c06") +!19 = !{!20} +!20 = !DIDerivedType(tag: DW_TAG_member, name: "__0", scope: !18, file: !5, baseType: !13, size: 32, align: 32, offset: 32, flags: DIFlagPublic) +!21 = !DIDerivedType(tag: DW_TAG_member, scope: !4, file: !5, baseType: !13, size: 32, align: 32, flags: DIFlagArtificial) +!22 = !{i32 8, !"PIC Level", i32 2} +!23 = !{i32 7, !"PIE Level", i32 2} +!24 = !{i32 7, !"Dwarf Version", i32 4} +!25 = !{i32 2, !"Debug Info Version", i32 3} +!26 = !{!"rustc version 1.91.0-nightly (160e7623e 2025-08-26)"} +!27 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !28, producer: "clang LLVM (rustc version 1.91.0-nightly (160e7623e 2025-08-26))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !29, splitDebugInlining: false, nameTableKind: None) +!28 = !DIFile(filename: "variant-part/src/main.rs/@/c0znihgkvro8hs0n88fgrtg6x", directory: "/tmp/variant-part") +!29 = !{!0} From c346c16f93d5a3fcb2b1a74501279959878136c6 Mon Sep 17 00:00:00 2001 From: Michal R Date: Wed, 22 Oct 2025 16:30:33 +0200 Subject: [PATCH 43/47] [BPF] Do not emit names for PTR, CONST, VOLATILE and RESTRICT BTF types (#163174) We currently raise a warning in `print_btf.py` when any of these types have a name. Linux kernel doesn't allow names in these types either.[0] However, there is nothing stopping frontends from giving names to these types. To make sure that they are always anonymous, explicitly skip the name emission. [0] https://elixir.bootlin.com/linux/v6.17.1/source/kernel/bpf/btf.c#L2586 --- llvm/lib/Target/BPF/BTFDebug.cpp | 19 +++++- llvm/test/CodeGen/BPF/BTF/ptr-named-2.ll | 59 +++++++++++++++++++ llvm/test/CodeGen/BPF/BTF/ptr-named.ll | 75 ++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 llvm/test/CodeGen/BPF/BTF/ptr-named-2.ll create mode 100644 llvm/test/CodeGen/BPF/BTF/ptr-named.ll diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp index 4ec7a93891aef..9bf14ab59c7c8 100644 --- a/llvm/lib/Target/BPF/BTFDebug.cpp +++ b/llvm/lib/Target/BPF/BTFDebug.cpp @@ -95,7 +95,24 @@ void BTFTypeDerived::completeType(BTFDebug &BDebug) { return; IsCompleted = true; - BTFType.NameOff = BDebug.addString(Name); + switch (Kind) { + case BTF::BTF_KIND_PTR: + case BTF::BTF_KIND_CONST: + case BTF::BTF_KIND_VOLATILE: + case BTF::BTF_KIND_RESTRICT: + // Debug info might contain names for these types, but given that we want + // to keep BTF minimal and naming reference types doesn't bring any value + // (what matters is the completeness of the base type), we don't emit them. + // + // Furthermore, the Linux kernel refuses to load BPF programs that contain + // BTF with these types named: + // https://elixir.bootlin.com/linux/v6.17.1/source/kernel/bpf/btf.c#L2586 + BTFType.NameOff = 0; + break; + default: + BTFType.NameOff = BDebug.addString(Name); + break; + } if (NeedsFixup || !DTy) return; diff --git a/llvm/test/CodeGen/BPF/BTF/ptr-named-2.ll b/llvm/test/CodeGen/BPF/BTF/ptr-named-2.ll new file mode 100644 index 0000000000000..df0cbeb3dd625 --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/ptr-named-2.ll @@ -0,0 +1,59 @@ +; RUN: llc -mtriple=bpfel -filetype=obj -o %t1 %s +; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1 +; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s +; RUN: llc -mtriple=bpfeb -filetype=obj -o %t1 %s +; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1 +; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s +; +; This IR is hand-written. + +; ModuleID = 'ptr-named-2.ll' +source_filename = "ptr-named-2.ll" +target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" +target triple = "bpfel-unknown-none" + +%struct.TypeExamples = type { i32*, i32, i32, i32* } + +@type_examples = internal global %struct.TypeExamples zeroinitializer, align 8, !dbg !0 + +!llvm.dbg.cu = !{!1} +!llvm.module.flags = !{!2, !3, !4} +!llvm.ident = !{!21} + +; CHECK-BTF: [1] STRUCT 'TypeExamples' size=32 vlen=4 +; CHECK-BTF-NEXT: 'ptr' type_id=2 bits_offset=0 +; CHECK-BTF-NEXT: 'volatile' type_id=4 bits_offset=64 +; CHECK-BTF-NEXT: 'const' type_id=5 bits_offset=128 +; CHECK-BTF-NEXT: 'restrict_ptr' type_id=6 bits_offset=192 +; CHECK-BTF-NEXT: [2] PTR '(anon)' type_id=3 +; CHECK-BTF-NEXT: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED +; CHECK-BTF-NEXT: [4] VOLATILE '(anon)' type_id=3 +; CHECK-BTF-NEXT: [5] CONST '(anon)' type_id=3 +; CHECK-BTF-NEXT: [6] RESTRICT '(anon)' type_id=7 +; CHECK-BTF-NEXT: [7] PTR '(anon)' type_id=3 +; CHECK-BTF-NEXT: [8] VAR 'type_examples' type_id=1, linkage=static +; CHECK-BTF-NEXT: [9] DATASEC '.bss' size=0 vlen=1 +; CHECK-BTF-NEXT: type_id=8 offset=0 size=24 + +!0 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) +!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !7, globals: !8, splitDebugInlining: false, nameTableKind: None) +!2 = !{i32 2, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = distinct !DIGlobalVariable(name: "type_examples", scope: !1, file: !6, line: 12, type: !9, isLocal: true, isDefinition: true) +!6 = !DIFile(filename: "ptr-named-2.ll", directory: "/tmp") +!7 = !{} +!8 = !{!0} +!9 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TypeExamples", file: !6, line: 5, size: 256, elements: !10) +!10 = !{!11, !12, !13, !14} +!11 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !9, file: !6, line: 6, baseType: !15, size: 64) +!12 = !DIDerivedType(tag: DW_TAG_member, name: "volatile", scope: !9, file: !6, line: 7, baseType: !17, size: 64, offset: 64) +!13 = !DIDerivedType(tag: DW_TAG_member, name: "const", scope: !9, file: !6, line: 8, baseType: !18, size: 64, offset: 128) +!14 = !DIDerivedType(tag: DW_TAG_member, name: "restrict_ptr", scope: !9, file: !6, line: 9, baseType: !19, size: 64, offset: 192) +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*int", baseType: !16, size: 64) +!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!17 = !DIDerivedType(tag: DW_TAG_volatile_type, name: "volatile int", baseType: !16) +!18 = !DIDerivedType(tag: DW_TAG_const_type, name: "const int", baseType: !16) +!19 = !DIDerivedType(tag: DW_TAG_restrict_type, name: "*int restrict", baseType: !20) +!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) +!21 = !{!"my hand-written IR"} diff --git a/llvm/test/CodeGen/BPF/BTF/ptr-named.ll b/llvm/test/CodeGen/BPF/BTF/ptr-named.ll new file mode 100644 index 0000000000000..675c34e976abb --- /dev/null +++ b/llvm/test/CodeGen/BPF/BTF/ptr-named.ll @@ -0,0 +1,75 @@ +; RUN: llc -mtriple=bpfel -filetype=obj -o %t1 %s +; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1 +; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s +; RUN: llc -mtriple=bpfeb -filetype=obj -o %t1 %s +; RUN: llvm-objcopy --dump-section='.BTF'=%t2 %t1 +; RUN: %python %p/print_btf.py %t2 | FileCheck -check-prefixes=CHECK-BTF %s +; +; Source: +; #![no_std] +; #![no_main] +; +; pub struct MyType { +; ptr: *const u32, +; } +; +; impl MyType { +; pub const fn new() -> Self { +; let ptr = core::ptr::null(); +; Self { ptr } +; } +; } +; +; unsafe impl Sync for MyType {} +; +; #[unsafe(no_mangle)] +; pub static X: MyType = MyType::new(); +; +; #[cfg(not(test))] +; #[panic_handler] +; fn panic(_info: &core::panic::PanicInfo) -> ! { +; loop {} +; } +; Compilation flag: +; cargo +nightly rustc -Zbuild-std=core --target=bpfel-unknown-none -- --emit=llvm-bc +; llvm-extract --glob=X $(find target/ -name "*.bc" | head -n 1) -o ptr-named.bc +; llvm-dis ptr-named.bc -o ptr-named.ll + +; ModuleID = 'ptr-named.bc' +source_filename = "1m2uqe50qkwxmo53ydydvou91" +target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" +target triple = "bpfel" + +@X = constant [8 x i8] zeroinitializer, align 8, !dbg !0 + +!llvm.module.flags = !{!11, !12, !13, !14} +!llvm.ident = !{!15} +!llvm.dbg.cu = !{!16} + +; CHECK-BTF: [1] STRUCT 'MyType' size=8 vlen=1 +; CHECK-BTF-NEXT: 'ptr' type_id=2 bits_offset=0 +; CHECK-BTF-NEXT: [2] PTR '(anon)' type_id=3 +; CHECK-BTF-NEXT: [3] INT 'u32' size=4 bits_offset=0 nr_bits=32 encoding=(none) +; CHECK-BTF-NEXT: [4] VAR 'X' type_id=1, linkage=global +; CHECK-BTF-NEXT: [5] DATASEC '.rodata' size=0 vlen=1 +; CHECK-BTF-NEXT: type_id=4 offset=0 size=8 + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "X", scope: !2, file: !3, line: 19, type: !4, isLocal: false, isDefinition: true, align: 64) +!2 = !DINamespace(name: "ptr_named", scope: null) +!3 = !DIFile(filename: "ptr-named/src/main.rs", directory: "/tmp/ptr-named", checksumkind: CSK_MD5, checksum: "e37168304600b30cbb5ba168f0384932") +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "MyType", scope: !2, file: !5, size: 64, align: 64, flags: DIFlagPublic, elements: !6, templateParams: !10, identifier: "7609fa40332dd486922f074276a171c3") +!5 = !DIFile(filename: "", directory: "") +!6 = !{!7} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !4, file: !5, baseType: !8, size: 64, align: 64, flags: DIFlagPrivate) +!8 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const u32", baseType: !9, size: 64, align: 64, dwarfAddressSpace: 0) +!9 = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) +!10 = !{} +!11 = !{i32 8, !"PIC Level", i32 2} +!12 = !{i32 7, !"PIE Level", i32 2} +!13 = !{i32 7, !"Dwarf Version", i32 4} +!14 = !{i32 2, !"Debug Info Version", i32 3} +!15 = !{!"rustc version 1.92.0-nightly (c8905eaa6 2025-09-28)"} +!16 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !17, producer: "clang LLVM (rustc version 1.92.0-nightly (c8905eaa6 2025-09-28))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !18, splitDebugInlining: false, nameTableKind: None) +!17 = !DIFile(filename: "ptr-named/src/main.rs/@/1m2uqe50qkwxmo53ydydvou91", directory: "/tmp/ptr-named") +!18 = !{!0} From 0921531330add39df5d83c4802eb76adf2aac744 Mon Sep 17 00:00:00 2001 From: Evgenii Kudriashov Date: Wed, 3 Sep 2025 23:54:21 +0200 Subject: [PATCH 44/47] [llvm-objcopy][COFF] Update .symidx values after stripping (#153322) After deleting debug sections, symbol indices are shifted but sections consisting of .symidx directives are completely ignored. Update symbol indices as well. (cherry picked from commit 74d52f9639ca7588c622c0790ca18fa5bff66837) --- llvm/lib/ObjCopy/COFF/COFFObject.cpp | 2 + llvm/lib/ObjCopy/COFF/COFFObject.h | 2 + llvm/lib/ObjCopy/COFF/COFFWriter.cpp | 75 +++++++ llvm/lib/ObjCopy/COFF/COFFWriter.h | 1 + .../COFF/strip-invalid-symidx-section.test | 188 ++++++++++++++++++ .../COFF/strip-update-symidx-section.test | 173 ++++++++++++++++ 6 files changed, 441 insertions(+) create mode 100644 llvm/test/tools/llvm-objcopy/COFF/strip-invalid-symidx-section.test create mode 100644 llvm/test/tools/llvm-objcopy/COFF/strip-update-symidx-section.test diff --git a/llvm/lib/ObjCopy/COFF/COFFObject.cpp b/llvm/lib/ObjCopy/COFF/COFFObject.cpp index 5fa13391c908f..91cf7e32a7396 100644 --- a/llvm/lib/ObjCopy/COFF/COFFObject.cpp +++ b/llvm/lib/ObjCopy/COFF/COFFObject.cpp @@ -18,6 +18,8 @@ using namespace object; void Object::addSymbols(ArrayRef NewSymbols) { for (Symbol S : NewSymbols) { S.UniqueId = NextSymbolUniqueId++; + S.OriginalRawIndex = NextSymbolOriginalIndex; + NextSymbolOriginalIndex += 1 + S.Sym.NumberOfAuxSymbols; Symbols.emplace_back(S); } updateSymbols(); diff --git a/llvm/lib/ObjCopy/COFF/COFFObject.h b/llvm/lib/ObjCopy/COFF/COFFObject.h index cdd1f17fc6055..6b70add1bb1b7 100644 --- a/llvm/lib/ObjCopy/COFF/COFFObject.h +++ b/llvm/lib/ObjCopy/COFF/COFFObject.h @@ -89,6 +89,7 @@ struct Symbol { std::optional WeakTargetSymbolId; size_t UniqueId; size_t RawIndex; + size_t OriginalRawIndex; bool Referenced; }; @@ -140,6 +141,7 @@ struct Object { DenseMap SymbolMap; size_t NextSymbolUniqueId = 0; + size_t NextSymbolOriginalIndex = 0; std::vector
Sections; DenseMap SectionMap; diff --git a/llvm/lib/ObjCopy/COFF/COFFWriter.cpp b/llvm/lib/ObjCopy/COFF/COFFWriter.cpp index 350c4aec572c9..fed67d67f13a7 100644 --- a/llvm/lib/ObjCopy/COFF/COFFWriter.cpp +++ b/llvm/lib/ObjCopy/COFF/COFFWriter.cpp @@ -12,6 +12,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include @@ -92,6 +94,77 @@ Error COFFWriter::finalizeSymbolContents() { return Error::success(); } +Error COFFWriter::finalizeSymIdxContents() { + // CFGuards shouldn't be present in PE. + if (Obj.IsPE) + return Error::success(); + + // Currently handle only sections consisting only of .symidx. + // TODO: other sections such as .impcall and .hybmp$x require more complex + // handling as they have more complex layout. + auto IsSymIdxSection = [](StringRef Name) { + return Name == ".gljmp$y" || Name == ".giats$y" || Name == ".gfids$y" || + Name == ".gehcont$y"; + }; + + DenseMap SymIdMap; + SmallDenseMap SecIdMap; + for (Symbol &Sym : Obj.getMutableSymbols()) { + SymIdMap[Sym.OriginalRawIndex] = Sym.RawIndex; + + // We collect only definition symbols of the sections to update the + // checksums. + if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && + Sym.Sym.NumberOfAuxSymbols == 1 && Sym.Sym.Value == 0 && + IsSymIdxSection(Sym.Name)) + SecIdMap[Sym.TargetSectionId] = + reinterpret_cast( + Sym.AuxData[0].Opaque); + } + + for (Section &Sec : Obj.getMutableSections()) { + if (!IsSymIdxSection(Sec.Name)) + continue; + + ArrayRef RawIds = Sec.getContents(); + // Nothing to do and also the checksum will be -1 instead of 0 if we + // recalculate it on empty input. + if (RawIds.size() == 0) + continue; + + auto SecDefIt = SecIdMap.find(Sec.UniqueId); + if (SecDefIt == SecIdMap.end()) + return createStringError(object_error::invalid_symbol_index, + "section '%s' does not have the corresponding " + "symbol or the symbol has unexpected format", + Sec.Name.str().c_str()); + + // Create updated content. + ArrayRef Ids( + reinterpret_cast(RawIds.data()), + RawIds.size() / 4); + std::vector NewIds; + for (support::ulittle32_t Id : Ids) { + auto SymIdIt = SymIdMap.find(Id); + if (SymIdIt == SymIdMap.end()) + return createStringError(object_error::invalid_symbol_index, + "section '%s' contains a .symidx (%d) that is " + "incorrect or was stripped", + Sec.Name.str().c_str(), Id.value()); + NewIds.push_back(support::ulittle32_t(SymIdIt->getSecond())); + } + ArrayRef NewRawIds(reinterpret_cast(NewIds.data()), + RawIds.size()); + // Update the checksum. + JamCRC JC(/*Init=*/0); + JC.update(NewRawIds); + SecDefIt->getSecond()->CheckSum = JC.getCRC(); + // Set new content. + Sec.setOwnedContents(NewRawIds.vec()); + } + return Error::success(); +} + void COFFWriter::layoutSections() { for (auto &S : Obj.getMutableSections()) { if (S.Header.SizeOfRawData > 0) @@ -183,6 +256,8 @@ Error COFFWriter::finalize(bool IsBigObj) { return E; if (Error E = finalizeSymbolContents()) return E; + if (Error E = finalizeSymIdxContents()) + return E; size_t SizeOfHeaders = 0; FileAlignment = 1; diff --git a/llvm/lib/ObjCopy/COFF/COFFWriter.h b/llvm/lib/ObjCopy/COFF/COFFWriter.h index b7dca69e9a81a..66d7f01c87f18 100644 --- a/llvm/lib/ObjCopy/COFF/COFFWriter.h +++ b/llvm/lib/ObjCopy/COFF/COFFWriter.h @@ -34,6 +34,7 @@ class COFFWriter { template std::pair finalizeSymbolTable(); Error finalizeRelocTargets(); Error finalizeSymbolContents(); + Error finalizeSymIdxContents(); void layoutSections(); Expected finalizeStringTable(); diff --git a/llvm/test/tools/llvm-objcopy/COFF/strip-invalid-symidx-section.test b/llvm/test/tools/llvm-objcopy/COFF/strip-invalid-symidx-section.test new file mode 100644 index 0000000000000..2b01116800091 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/COFF/strip-invalid-symidx-section.test @@ -0,0 +1,188 @@ +## Test that we bail out if a section consisting of symidx is invalid. + +## In this case, the symbol .gfids$y is not present at all. +# RUN: yaml2obj %s --docnum=1 -o %t1.in.o +# RUN: not llvm-objcopy --strip-debug %t1.in.o %t1.out.o 2>&1 | FileCheck %s --check-prefix=ERROR-NOSYM -DFILE=%t1.out.o + +# ERROR-NOSYM: error: '[[FILE]]': section '.gfids$y' does not have the corresponding symbol or the symbol has unexpected format + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + - Name: '.gfids$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '04000000' + SizeOfRawData: 8 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 4 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: foo + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... + +## In this case, the symbol .giats$y has a non-zero offset. +# RUN: yaml2obj %s --docnum=2 -o %t2.in.o +# RUN: not llvm-objcopy --strip-debug %t2.in.o %t2.out.o 2>&1 | FileCheck %s --check-prefix=ERROR-OFFSET -DFILE=%t2.out.o + +# ERROR-OFFSET: error: '[[FILE]]': section '.giats$y' does not have the corresponding symbol or the symbol has unexpected format + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + - Name: '.giats$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0600000010000000' + SizeOfRawData: 8 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: '.giats$y' + Value: 42 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 1167279533 + Number: 5 + - Name: foo + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... + +## In this case, the symbol .gljmp$y has a non-static storage class. +# RUN: yaml2obj %s --docnum=3 -o %t3.in.o +# RUN: not llvm-objcopy --strip-debug %t3.in.o %t3.out.o 2>&1 | FileCheck %s --check-prefix=ERROR-EXTERNAL -DFILE=%t3.out.o + +# ERROR-EXTERNAL: error: '[[FILE]]': section '.gljmp$y' does not have the corresponding symbol or the symbol has unexpected format + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + - Name: '.gljmp$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0600000010000000' + SizeOfRawData: 8 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: '.gljmp$y' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: foo + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... + +## In this case, .gfids$y contains a symbol index that is not present in the +## symbol table. Generally the behavior should be the same for every section consisting +## of .symidx directives, e.g .giats$y, .gljmp$y and .gehcont$y. +# RUN: yaml2obj %s --docnum=4 -o %t4.in.o +# RUN: not llvm-objcopy --strip-debug %t4.in.o %t4.out.o 2>&1 | FileCheck %s --check-prefix=ERROR-SYMIDX -DFILE=%t4.out.o + +# ERROR-SYMIDX: error: '[[FILE]]': section '.gfids$y' contains a .symidx (16) that is incorrect or was stripped +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + - Name: '.gfids$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0400000010000000' + SizeOfRawData: 8 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: '.gfids$y' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 1167279533 + Number: 5 + - Name: foo + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/llvm/test/tools/llvm-objcopy/COFF/strip-update-symidx-section.test b/llvm/test/tools/llvm-objcopy/COFF/strip-update-symidx-section.test new file mode 100644 index 0000000000000..04ec26afb644d --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/COFF/strip-update-symidx-section.test @@ -0,0 +1,173 @@ +## Check sections consisting only of .symidx directives. The test checks that +## indices in the sections are updated after stripping as the symbol table could +## be changed during stripping. +# RUN: yaml2obj %s -o %t.in.o + +# RUN: llvm-objcopy --strip-debug %t.in.o %t.out.o +# RUN: llvm-readobj -s -x '.gehcont$y' -x '.gfids$y' -x '.giats$y' -x '.gljmp$y' %t.out.o | FileCheck %s + +# CHECK: Symbols [ +# CHECK: Name: .text +# CHECK: Name: .gehcont$y +# CHECK: AuxSectionDef { +# CHECK: Checksum: 0x82EA2D2 +# CHECK: } +# CHECK: Name: $ehgcr_0_1 +# CHECK: Name: .gfids$y +# CHECK: AuxSectionDef { +# CHECK: Checksum: 0xAF00C48B +# CHECK: } +# CHECK: Name: .giats$y +# CHECK: AuxSectionDef { +# CHECK: Checksum: 0x4AD6BFB8 +# CHECK: } +# CHECK: Name: .gljmp$y +# CHECK: AuxSectionDef { +# CHECK: Checksum: 0xD457699C +# CHECK: } +# CHECK: Name: foo +# CHECK: ] + +# CHECK: Hex dump of section '.gehcont$y': +# CHECK-NEXT: 0x00000000 04000000 04000000 04000000 + +# CHECK: Hex dump of section '.gfids$y': +# CHECK-NEXT: 0x00000000 0b000000 0d000000 + +# CHECK: Hex dump of section '.giats$y': +# CHECK-NEXT: 0x00000000 0c000000 + +# CHECK: Hex dump of section '.gljmp$y': +# CHECK-NEXT: 0x00000000 0b000000 0c000000 0d000000 + + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + - Name: '.debug$S' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 04000000F100000044656275672073656374696F6E20746F20626520737472697070656400 + SizeOfRawData: 37 + - Name: '.gehcont$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '060000000600000006000000' + SizeOfRawData: 12 + - Name: '.gfids$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0d0000000f000000' + SizeOfRawData: 8 + - Name: '.giats$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0e000000' + SizeOfRawData: 4 + - Name: '.gljmp$y' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0d0000000e0000000f000000' + SizeOfRawData: 12 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: '.debug$S' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 37 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 820498156 + Number: 2 + - Name: '.gehcont$y' + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 12 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0x30E7CEEC + Number: 3 + - Name: '$ehgcr_0_1' + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '.gfids$y' + Value: 0 + SectionNumber: 4 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0x459345AD + Number: 4 + - Name: '.giats$y' + Value: 0 + SectionNumber: 5 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0x31852256 + Number: 5 + - Name: '.gljmp$y' + Value: 0 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 16 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0xC608680B + Number: 6 + - Name: foo + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: bar + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: baz + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... From 6bb6ffef0a5dc166f8e50ed0a5ea98846227e738 Mon Sep 17 00:00:00 2001 From: Andrew Ng Date: Wed, 29 Oct 2025 10:35:20 +0000 Subject: [PATCH 45/47] [LLD][COFF] Fix manifest UAC trustInfo namespace (#165285) Fix manifest `trustInfo` to use the `urn:schemas-microsoft-com:asm.v3` namespace. Fixes https://github.com/llvm/llvm-project/issues/120394. (cherry picked from commit 6ab8e8fa031e0a22c0244c1aa8f54581ed9bffd1) --- lld/COFF/DriverUtils.cpp | 2 +- lld/test/COFF/Inputs/manifest-uac.test | 11 +++++ lld/test/COFF/manifest-uac.test | 33 +++++++++++++ lld/test/COFF/manifest.test | 65 ++++++++++++++------------ lld/test/COFF/manifestinput.test | 35 +++++++------- 5 files changed, 96 insertions(+), 50 deletions(-) create mode 100644 lld/test/COFF/Inputs/manifest-uac.test create mode 100644 lld/test/COFF/manifest-uac.test diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index d8b41c7f45400..5ef41c4c0a086 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -387,7 +387,7 @@ std::string LinkerDriver::createDefaultXml() { << "\n"; if (ctx.config.manifestUAC) { - os << " \n" + os << " \n" << " \n" << " \n" << " + + + + + + + diff --git a/lld/test/COFF/manifest-uac.test b/lld/test/COFF/manifest-uac.test new file mode 100644 index 0000000000000..d3a17c7282716 --- /dev/null +++ b/lld/test/COFF/manifest-uac.test @@ -0,0 +1,33 @@ +# REQUIRES: libxml2 + +# RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj +# RUN: lld-link /out:%t.exe /entry:main \ +# RUN: /manifest:embed \ +# RUN: /manifestinput:%p/Inputs/manifest-uac.test %t.obj +# RUN: llvm-readobj --coff-resources %t.exe | FileCheck %s + +CHECK: Data ( +CHECK-NEXT: 0000: 3C3F786D 6C207665 7273696F 6E3D2231 |.| +CHECK-NEXT: 0070: 0A20203C 74727573 74496E66 6F20786D |. . . . . <| +CHECK-NEXT: 0120: 2F726571 75657374 65645072 6976696C |/requestedPrivil| +CHECK-NEXT: 0130: 65676573 3E0A2020 20203C2F 73656375 |eges>. . .| +CHECK-NEXT: 0160: 0A |.| +CHECK-NEXT: ) diff --git a/lld/test/COFF/manifest.test b/lld/test/COFF/manifest.test index 4910600bd3a17..09de96e9bccfa 100644 --- a/lld/test/COFF/manifest.test +++ b/lld/test/COFF/manifest.test @@ -10,7 +10,7 @@ MANIFEST: MANIFEST: -MANIFEST: +MANIFEST: MANIFEST: MANIFEST: MANIFEST: @@ -26,7 +26,7 @@ MANIFEST: UAC: UAC: -UAC: +UAC: UAC: UAC: UAC: @@ -43,7 +43,7 @@ UAC: DEPENDENCY: DEPENDENCY: -DEPENDENCY: +DEPENDENCY: DEPENDENCY: DEPENDENCY: DEPENDENCY: @@ -90,7 +90,7 @@ NOUACNODEP: SEVERALDEPS: SEVERALDEPS: -SEVERALDEPS: +SEVERALDEPS: SEVERALDEPS: SEVERALDEPS: SEVERALDEPS: @@ -139,31 +139,34 @@ EMBED: 0040: 6D61732D 6D696372 6F736F66 742D636F |mas-microsoft-co| EMBED: 0050: 6D3A6173 6D2E7631 220A2020 20202020 |m:asm.v1". | EMBED: 0060: 20202020 6D616E69 66657374 56657273 | manifestVers| EMBED: 0070: 696F6E3D 22312E30 223E0A20 203C7472 |ion="1.0">. . . . . | -EMBED: 0100: 203C2F72 65717565 73746564 50726976 | . . . . . | -EMBED: 0160: 20202020 3C617373 656D626C 79496465 | . . . . <| -EMBED: 01C0: 64657065 6E64656E 74417373 656D626C |dependentAssembl| -EMBED: 01D0: 793E0A20 20202020 203C6173 73656D62 |y>. . . ..| +EMBED: 0080: 75737449 6E666F20 786D6C6E 733D2275 |ustInfo xmlns="u| +EMBED: 0090: 726E3A73 6368656D 61732D6D 6963726F |rn:schemas-micro| +EMBED: 00A0: 736F6674 2D636F6D 3A61736D 2E763322 |soft-com:asm.v3"| +EMBED: 00B0: 3E0A2020 20203C73 65637572 6974793E |>. | +EMBED: 00C0: 0A202020 2020203C 72657175 65737465 |. . | +EMBED: 00E0: 20202020 20203C72 65717565 73746564 | . | +EMBED: 0140: 0A202020 203C2F73 65637572 6974793E |. | +EMBED: 0150: 0A20203C 2F747275 7374496E 666F3E0A |. .| +EMBED: 0160: 20203C64 6570656E 64656E63 793E0A20 | . | +EMBED: 0170: 2020203C 64657065 6E64656E 74417373 | . . | +EMBED: 01B0: 3C2F6465 70656E64 656E7441 7373656D |. . . . | +EMBED: 0200: 20203C61 7373656D 626C7949 64656E74 | . . ..| EMBED: ) diff --git a/lld/test/COFF/manifestinput.test b/lld/test/COFF/manifestinput.test index 04af80a13312d..cbf27b1ea96b5 100644 --- a/lld/test/COFF/manifestinput.test +++ b/lld/test/COFF/manifestinput.test @@ -5,22 +5,21 @@ # RUN: /manifest:embed \ # RUN: /manifestuac:"level='requireAdministrator'" \ # RUN: /manifestinput:%p/Inputs/manifestinput.test %t.obj -# RUN: llvm-readobj --coff-resources --file-headers %t.exe | FileCheck %s \ -# RUN: -check-prefix TEST_EMBED +# RUN: llvm-readobj --coff-resources --file-headers %t.exe | FileCheck %s -TEST_EMBED: ResourceTableRVA: 0x2000 -TEST_EMBED-NEXT: ResourceTableSize: 0x2A0 -TEST_EMBED-DAG: Resources [ -TEST_EMBED-NEXT: Total Number of Resources: 1 -TEST_EMBED-DAG: Number of String Entries: 0 -TEST_EMBED-NEXT: Number of ID Entries: 1 -TEST_EMBED-NEXT: Type: MANIFEST (ID 24) [ -TEST_EMBED-NEXT: Table Offset: 0x18 -TEST_EMBED-NEXT: Number of String Entries: 0 -TEST_EMBED-NEXT: Number of ID Entries: 1 -TEST_EMBED-NEXT: Name: (ID 1) [ -TEST_EMBED-NEXT: Table Offset: 0x30 -TEST_EMBED-NEXT: Number of String Entries: 0 -TEST_EMBED-NEXT: Number of ID Entries: 1 -TEST_EMBED-NEXT: Language: (ID 1033) [ -TEST_EMBED-NEXT: Entry Offset: 0x48 +CHECK: ResourceTableRVA: 0x2000 +CHECK-NEXT: ResourceTableSize: 0x2C8 +CHECK-DAG: Resources [ +CHECK-NEXT: Total Number of Resources: 1 +CHECK-DAG: Number of String Entries: 0 +CHECK-NEXT: Number of ID Entries: 1 +CHECK-NEXT: Type: MANIFEST (ID 24) [ +CHECK-NEXT: Table Offset: 0x18 +CHECK-NEXT: Number of String Entries: 0 +CHECK-NEXT: Number of ID Entries: 1 +CHECK-NEXT: Name: (ID 1) [ +CHECK-NEXT: Table Offset: 0x30 +CHECK-NEXT: Number of String Entries: 0 +CHECK-NEXT: Number of ID Entries: 1 +CHECK-NEXT: Language: (ID 1033) [ +CHECK-NEXT: Entry Offset: 0x48 From df3145bf4239824619ad36ffaf523fdcceac88c5 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Wed, 29 Oct 2025 20:55:15 -0400 Subject: [PATCH 46/47] [clang-shlib] Fix linking libclang-cpp on Haiku (#156401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Haiku requires linking in libnetwork. Co-authored-by: Jérôme Duval (cherry picked from commit 21bcd00e54416b0950da19fe8adb0628a19bf66f) --- clang/tools/clang-shlib/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/tools/clang-shlib/CMakeLists.txt b/clang/tools/clang-shlib/CMakeLists.txt index 945076e1ad810..a4d0aa5779a7e 100644 --- a/clang/tools/clang-shlib/CMakeLists.txt +++ b/clang/tools/clang-shlib/CMakeLists.txt @@ -41,6 +41,10 @@ if (CLANG_LINK_CLANG_DYLIB) set(INSTALL_WITH_TOOLCHAIN INSTALL_WITH_TOOLCHAIN) endif() +if (HAIKU) + list(APPEND _DEPS network) +endif() + add_clang_library(clang-cpp SHARED ${INSTALL_WITH_TOOLCHAIN} From 8e2cd28cd4ba46613a46467b0c91b1cabead26cd Mon Sep 17 00:00:00 2001 From: Peter Arzt Date: Wed, 15 Oct 2025 10:58:41 +0200 Subject: [PATCH 47/47] [OpenMP] Fix preprocessor mismatches between include and usages of hwloc (#158349) Fix https://github.com/llvm/llvm-project/issues/156679 There is a mismatch between the preprocessor guards around the include of `hwloc.h` and those protecting its usages, leading to build failures on Darwin: https://github.com/spack/spack-packages/pull/1212 This change introduces `KMP_HWLOC_ENABLED` that reflects whether hwloc is actually used. (cherry picked from commit cd24d108a2c19c23c4ac80b501fa7361963cca3d) --- openmp/runtime/src/kmp.h | 23 ++++++++++++---------- openmp/runtime/src/kmp_affinity.cpp | 24 +++++++++++------------ openmp/runtime/src/kmp_affinity.h | 6 +++--- openmp/runtime/src/kmp_alloc.cpp | 30 ++++++++++++++--------------- openmp/runtime/src/kmp_dispatch.h | 4 ++-- openmp/runtime/src/kmp_global.cpp | 4 ++-- openmp/runtime/src/kmp_settings.cpp | 20 +++++++++---------- 7 files changed, 57 insertions(+), 54 deletions(-) diff --git a/openmp/runtime/src/kmp.h b/openmp/runtime/src/kmp.h index f62cabee6ea84..197cf54765285 100644 --- a/openmp/runtime/src/kmp.h +++ b/openmp/runtime/src/kmp.h @@ -106,12 +106,15 @@ class kmp_stats_list; // OMPD_SKIP_HWLOC used in libompd/omp-icv.cpp to avoid OMPD depending on hwloc #if KMP_USE_HWLOC && KMP_AFFINITY_SUPPORTED && !defined(OMPD_SKIP_HWLOC) #include "hwloc.h" +#define KMP_HWLOC_ENABLED 1 #ifndef HWLOC_OBJ_NUMANODE #define HWLOC_OBJ_NUMANODE HWLOC_OBJ_NODE #endif #ifndef HWLOC_OBJ_PACKAGE #define HWLOC_OBJ_PACKAGE HWLOC_OBJ_SOCKET #endif +#else +#define KMP_HWLOC_ENABLED 0 #endif #if KMP_ARCH_X86 || KMP_ARCH_X86_64 @@ -692,10 +695,10 @@ typedef BOOL (*kmp_SetThreadGroupAffinity_t)(HANDLE, const GROUP_AFFINITY *, extern kmp_SetThreadGroupAffinity_t __kmp_SetThreadGroupAffinity; #endif /* KMP_OS_WINDOWS */ -#if KMP_USE_HWLOC && !defined(OMPD_SKIP_HWLOC) +#if KMP_HWLOC_ENABLED extern hwloc_topology_t __kmp_hwloc_topology; extern int __kmp_hwloc_error; -#endif +#endif // KMP_HWLOC_ENABLED extern size_t __kmp_affin_mask_size; #define KMP_AFFINITY_CAPABLE() (__kmp_affin_mask_size > 0) @@ -804,10 +807,10 @@ class KMPAffinity { static void destroy_api(); enum api_type { NATIVE_OS -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED , HWLOC -#endif +#endif // KMP_HWLOC_ENABLED }; virtual api_type get_api_type() const { KMP_ASSERT(0); @@ -876,9 +879,9 @@ enum affinity_top_method { affinity_top_method_group, #endif /* KMP_GROUP_AFFINITY */ affinity_top_method_flat, -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED affinity_top_method_hwloc, -#endif +#endif // KMP_HWLOC_ENABLED affinity_top_method_default }; @@ -1145,9 +1148,9 @@ typedef struct kmp_allocator_t { omp_alloctrait_value_t target_access; omp_alloctrait_value_t atomic_scope; size_t part_size; -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED omp_alloctrait_value_t membind; -#endif +#endif // KMP_HWLOC_ENABLED } kmp_allocator_t; extern omp_allocator_handle_t __kmpc_init_allocator(int gtid, @@ -2107,12 +2110,12 @@ typedef struct dispatch_shared_info { #if KMP_USE_HIER_SCHED void *hier; #endif -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED // When linking with libhwloc, the ORDERED EPCC test slows down on big // machines (> 48 cores). Performance analysis showed that a cache thrash // was occurring and this padding helps alleviate the problem. char padding[64]; -#endif +#endif // KMP_HWLOC_ENABLED } dispatch_shared_info_t; typedef struct kmp_disp { diff --git a/openmp/runtime/src/kmp_affinity.cpp b/openmp/runtime/src/kmp_affinity.cpp index a6065fe792d55..50389502d3b45 100644 --- a/openmp/runtime/src/kmp_affinity.cpp +++ b/openmp/runtime/src/kmp_affinity.cpp @@ -19,13 +19,13 @@ #if KMP_USE_HIER_SCHED #include "kmp_dispatch_hier.h" #endif -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED // Copied from hwloc #define HWLOC_GROUP_KIND_INTEL_MODULE 102 #define HWLOC_GROUP_KIND_INTEL_TILE 103 #define HWLOC_GROUP_KIND_INTEL_DIE 104 #define HWLOC_GROUP_KIND_WINDOWS_PROCESSOR_GROUP 220 -#endif +#endif // KMP_HWLOC_ENABLED #include // The machine topology @@ -1438,7 +1438,7 @@ void KMPAffinity::pick_api() { KMPAffinity *affinity_dispatch; if (picked_api) return; -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED // Only use Hwloc if affinity isn't explicitly disabled and // user requests Hwloc topology method if (__kmp_affinity_top_method == affinity_top_method_hwloc && @@ -1446,7 +1446,7 @@ void KMPAffinity::pick_api() { affinity_dispatch = new KMPHwlocAffinity(); __kmp_hwloc_available = true; } else -#endif +#endif // KMP_HWLOC_ENABLED { affinity_dispatch = new KMPNativeAffinity(); } @@ -1697,7 +1697,7 @@ kmp_affin_mask_t *__kmp_affin_fullMask = NULL; // Original mask is a subset of full mask in multiple processor groups topology kmp_affin_mask_t *__kmp_affin_origMask = NULL; -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED static inline bool __kmp_hwloc_is_cache_type(hwloc_obj_t obj) { #if HWLOC_API_VERSION >= 0x00020000 return hwloc_obj_type_is_cache(obj->type); @@ -2005,7 +2005,7 @@ static bool __kmp_affinity_create_hwloc_map(kmp_i18n_id_t *const msg_id) { __kmp_topology->sort_ids(); return true; } -#endif // KMP_USE_HWLOC +#endif // KMP_HWLOC_ENABLED // If we don't know how to retrieve the machine's processor topology, or // encounter an error in doing so, this routine is called to form a "flat" @@ -4845,7 +4845,7 @@ static bool __kmp_aux_affinity_initialize_topology(kmp_affinity_t &affinity) { // In the default code path, errors are not fatal - we just try using // another method. We only emit a warning message if affinity is on, or the // verbose flag is set, an the nowarnings flag was not set. -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED if (!success && __kmp_affinity_dispatch->get_api_type() == KMPAffinity::HWLOC) { if (!__kmp_hwloc_error) { @@ -4857,7 +4857,7 @@ static bool __kmp_aux_affinity_initialize_topology(kmp_affinity_t &affinity) { KMP_INFORM(AffIgnoringHwloc, env_var); } } -#endif +#endif // KMP_HWLOC_ENABLED #if KMP_ARCH_X86 || KMP_ARCH_X86_64 if (!success) { @@ -4905,7 +4905,7 @@ static bool __kmp_aux_affinity_initialize_topology(kmp_affinity_t &affinity) { // If the user has specified that a paricular topology discovery method is to be // used, then we abort if that method fails. The exception is group affinity, // which might have been implicitly set. -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED else if (__kmp_affinity_top_method == affinity_top_method_hwloc) { KMP_ASSERT(__kmp_affinity_dispatch->get_api_type() == KMPAffinity::HWLOC); success = __kmp_affinity_create_hwloc_map(&msg_id); @@ -4914,7 +4914,7 @@ static bool __kmp_aux_affinity_initialize_topology(kmp_affinity_t &affinity) { KMP_FATAL(MsgExiting, __kmp_i18n_catgets(msg_id)); } } -#endif // KMP_USE_HWLOC +#endif // KMP_HWLOC_ENABLED #if KMP_ARCH_X86 || KMP_ARCH_X86_64 else if (__kmp_affinity_top_method == affinity_top_method_x2apicid || @@ -5308,12 +5308,12 @@ void __kmp_affinity_uninitialize(void) { __kmp_free(__kmp_osid_to_hwthread_map); __kmp_osid_to_hwthread_map = NULL; } -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED if (__kmp_hwloc_topology != NULL) { hwloc_topology_destroy(__kmp_hwloc_topology); __kmp_hwloc_topology = NULL; } -#endif +#endif // KMP_HWLOC_ENABLED if (__kmp_hw_subset) { kmp_hw_subset_t::deallocate(__kmp_hw_subset); __kmp_hw_subset = nullptr; diff --git a/openmp/runtime/src/kmp_affinity.h b/openmp/runtime/src/kmp_affinity.h index dc3191caae634..fa69585f7e2d5 100644 --- a/openmp/runtime/src/kmp_affinity.h +++ b/openmp/runtime/src/kmp_affinity.h @@ -18,7 +18,7 @@ #include #if KMP_AFFINITY_SUPPORTED -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED class KMPHwlocAffinity : public KMPAffinity { public: class Mask : public KMPAffinity::Mask { @@ -109,7 +109,7 @@ class KMPHwlocAffinity : public KMPAffinity { } return error; } -#endif +#endif // KMP_OS_WINDOWS int get_proc_group() const override { int group = -1; #if KMP_OS_WINDOWS @@ -191,7 +191,7 @@ class KMPHwlocAffinity : public KMPAffinity { } api_type get_api_type() const override { return HWLOC; } }; -#endif /* KMP_USE_HWLOC */ +#endif /* KMP_HWLOC_ENABLED */ #if KMP_OS_LINUX || KMP_OS_FREEBSD || KMP_OS_NETBSD || KMP_OS_DRAGONFLY || \ KMP_OS_AIX diff --git a/openmp/runtime/src/kmp_alloc.cpp b/openmp/runtime/src/kmp_alloc.cpp index 051f88c5a0996..d43daefb6ef1e 100644 --- a/openmp/runtime/src/kmp_alloc.cpp +++ b/openmp/runtime/src/kmp_alloc.cpp @@ -14,7 +14,7 @@ #include "kmp_io.h" #include "kmp_wrapper_malloc.h" -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED #if HWLOC_API_VERSION > 0x00020300 #define KMP_HWLOC_LOCATION_TYPE_CPUSET HWLOC_LOCATION_TYPE_CPUSET #elif HWLOC_API_VERSION == 0x00020300 @@ -26,7 +26,7 @@ enum hwloc_memattr_id_e { HWLOC_MEMATTR_ID_CAPACITY }; #endif -#endif // KMP_USE_HWLOC +#endif // KMP_HWLOC_ENABLED // Disable bget when it is not used #if KMP_USE_BGET @@ -1545,7 +1545,7 @@ void __kmp_fini_memkind() { #endif } -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED static bool __kmp_is_hwloc_membind_supported(hwloc_membind_policy_t policy) { #if HWLOC_API_VERSION >= 0x00020300 const hwloc_topology_support *support; @@ -1561,7 +1561,7 @@ static bool __kmp_is_hwloc_membind_supported(hwloc_membind_policy_t policy) { return false; #else return false; -#endif +#endif // KMP_HWLOC_ENABLED } void *__kmp_hwloc_alloc_membind(hwloc_memattr_id_e attr, size_t size, @@ -1611,7 +1611,7 @@ void *__kmp_hwloc_membind_policy(omp_memspace_handle_t ms, size_t size, return NULL; #endif } -#endif // KMP_USE_HWLOC +#endif // KMP_HWLOC_ENABLED void __kmp_init_target_mem() { *(void **)(&kmp_target_alloc_host) = KMP_DLSYM("llvm_omp_target_alloc_host"); @@ -1680,13 +1680,13 @@ omp_allocator_handle_t __kmpc_init_allocator(int gtid, omp_memspace_handle_t ms, al->fb_data = RCAST(kmp_allocator_t *, traits[i].value); break; case omp_atk_partition: -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED al->membind = (omp_alloctrait_value_t)traits[i].value; KMP_DEBUG_ASSERT(al->membind == omp_atv_environment || al->membind == omp_atv_nearest || al->membind == omp_atv_blocked || al->membind == omp_atv_interleaved); -#endif +#endif // KMP_HWLOC_ENABLED al->memkind = RCAST(void **, traits[i].value); break; case omp_atk_pin_device: @@ -1980,7 +1980,7 @@ void *__kmp_alloc(int gtid, size_t algn, size_t size, } } -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED if (__kmp_hwloc_available) { if (__kmp_is_hwloc_membind_supported(HWLOC_MEMBIND_BIND)) { if (allocator < kmp_max_mem_alloc) { @@ -2074,7 +2074,7 @@ void *__kmp_alloc(int gtid, size_t algn, size_t size, ptr = hwloc_alloc(__kmp_hwloc_topology, desc.size_a); } } else { -#endif +#endif // KMP_HWLOC_ENABLED if (__kmp_memkind_available) { if (allocator < kmp_max_mem_alloc) { // pre-defined allocator @@ -2201,9 +2201,9 @@ void *__kmp_alloc(int gtid, size_t algn, size_t size, KMP_ASSERT(0); // abort fallback requested } // no sense to look for another fallback because of same internal alloc } -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED } -#endif +#endif // KMP_HWLOC_ENABLED KE_TRACE(10, ("__kmp_alloc: T#%d %p=alloc(%d)\n", gtid, ptr, desc.size_a)); if (ptr == NULL) return NULL; @@ -2339,7 +2339,7 @@ void ___kmpc_free(int gtid, void *ptr, omp_allocator_handle_t allocator) { kmp_target_unlock_mem(desc.ptr_alloc, device); } -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED if (__kmp_hwloc_available) { if (oal > kmp_max_mem_alloc && al->pool_size > 0) { kmp_uint64 used = @@ -2349,7 +2349,7 @@ void ___kmpc_free(int gtid, void *ptr, omp_allocator_handle_t allocator) { } hwloc_free(__kmp_hwloc_topology, desc.ptr_alloc, desc.size_a); } else { -#endif +#endif // KMP_HWLOC_ENABLED if (__kmp_memkind_available) { if (oal < kmp_max_mem_alloc) { // pre-defined allocator @@ -2378,9 +2378,9 @@ void ___kmpc_free(int gtid, void *ptr, omp_allocator_handle_t allocator) { } __kmp_thread_free(__kmp_thread_from_gtid(gtid), desc.ptr_alloc); } -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED } -#endif +#endif // KMP_HWLOC_ENABLED } /* If LEAK_MEMORY is defined, __kmp_free() will *not* free memory. It causes diff --git a/openmp/runtime/src/kmp_dispatch.h b/openmp/runtime/src/kmp_dispatch.h index cf19eb52662ce..f161a801700f4 100644 --- a/openmp/runtime/src/kmp_dispatch.h +++ b/openmp/runtime/src/kmp_dispatch.h @@ -182,12 +182,12 @@ template struct dispatch_shared_info_template { #if KMP_USE_HIER_SCHED kmp_hier_t *hier; #endif -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED // When linking with libhwloc, the ORDERED EPCC test slowsdown on big // machines (> 48 cores). Performance analysis showed that a cache thrash // was occurring and this padding helps alleviate the problem. char padding[64]; -#endif +#endif // KMP_HWLOC_ENABLED }; /* ------------------------------------------------------------------------ */ diff --git a/openmp/runtime/src/kmp_global.cpp b/openmp/runtime/src/kmp_global.cpp index 87c0a66a16c0a..f19d83bb5d053 100644 --- a/openmp/runtime/src/kmp_global.cpp +++ b/openmp/runtime/src/kmp_global.cpp @@ -250,10 +250,10 @@ enum mic_type __kmp_mic_type = non_mic; KMPAffinity *__kmp_affinity_dispatch = NULL; -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED int __kmp_hwloc_error = FALSE; hwloc_topology_t __kmp_hwloc_topology = NULL; -#endif +#endif // KMP_HWLOC_ENABLED #if KMP_OS_WINDOWS #if KMP_GROUP_AFFINITY diff --git a/openmp/runtime/src/kmp_settings.cpp b/openmp/runtime/src/kmp_settings.cpp index 392a02ebbd9aa..d806b02ade4df 100644 --- a/openmp/runtime/src/kmp_settings.cpp +++ b/openmp/runtime/src/kmp_settings.cpp @@ -1069,10 +1069,10 @@ static void __kmp_stg_print_warnings(kmp_str_buf_t *buffer, char const *name, static void __kmp_stg_parse_nesting_mode(char const *name, char const *value, void *data) { __kmp_stg_parse_int(name, value, 0, INT_MAX, &__kmp_nesting_mode); -#if KMP_AFFINITY_SUPPORTED && KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED if (__kmp_nesting_mode > 0) __kmp_affinity_top_method = affinity_top_method_hwloc; -#endif +#endif // KMP_HWLOC_ENABLED } // __kmp_stg_parse_nesting_mode static void __kmp_stg_print_nesting_mode(kmp_str_buf_t *buffer, @@ -3291,11 +3291,11 @@ static void __kmp_stg_parse_topology_method(char const *name, char const *value, if (__kmp_str_match("all", 1, value)) { __kmp_affinity_top_method = affinity_top_method_all; } -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED else if (__kmp_str_match("hwloc", 1, value)) { __kmp_affinity_top_method = affinity_top_method_hwloc; } -#endif +#endif // KMP_HWLOC_ENABLED #if KMP_ARCH_X86 || KMP_ARCH_X86_64 else if (__kmp_str_match("cpuid_leaf31", 12, value) || __kmp_str_match("cpuid 1f", 8, value) || @@ -3399,11 +3399,11 @@ static void __kmp_stg_print_topology_method(kmp_str_buf_t *buffer, break; #endif /* KMP_ARCH_X86 || KMP_ARCH_X86_64 */ -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED case affinity_top_method_hwloc: value = "hwloc"; break; -#endif +#endif // KMP_HWLOC_ENABLED case affinity_top_method_cpuinfo: value = "cpuinfo"; @@ -6289,7 +6289,7 @@ void __kmp_env_initialize(char const *string) { #if KMP_AFFINITY_SUPPORTED if (!TCR_4(__kmp_init_middle)) { -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED // Force using hwloc when either tiles or numa nodes requested within // KMP_HW_SUBSET or granularity setting and no other topology method // is requested @@ -6304,12 +6304,12 @@ void __kmp_env_initialize(char const *string) { if (__kmp_affinity.gran == KMP_HW_NUMA || __kmp_affinity.gran == KMP_HW_TILE) __kmp_affinity_top_method = affinity_top_method_hwloc; -#endif +#endif // KMP_HWLOC_ENABLED // Determine if the machine/OS is actually capable of supporting // affinity. const char *var = "KMP_AFFINITY"; KMPAffinity::pick_api(); -#if KMP_USE_HWLOC +#if KMP_HWLOC_ENABLED // If Hwloc topology discovery was requested but affinity was also disabled, // then tell user that Hwloc request is being ignored and use default // topology discovery method. @@ -6318,7 +6318,7 @@ void __kmp_env_initialize(char const *string) { KMP_WARNING(AffIgnoringHwloc, var); __kmp_affinity_top_method = affinity_top_method_all; } -#endif +#endif // KMP_HWLOC_ENABLED if (__kmp_affinity.type == affinity_disabled) { KMP_AFFINITY_DISABLE(); } else if (!KMP_AFFINITY_CAPABLE()) {