Skip to content

Conversation

mstorsjo
Copy link
Member

@mstorsjo mstorsjo commented Oct 8, 2025

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 mstorsjo/llvm-mingw#522.

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.

Handle this case in `TargetCodeGenInfo::getSizeOfUnwindException`,
fixing the code generation for catching pointers by reference.

This fixes mstorsjo/llvm-mingw#522.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Oct 8, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 8, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Martin Storsjö (mstorsjo)

Changes

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 mstorsjo/llvm-mingw#522.


Full diff: https://github.com/llvm/llvm-project/pull/162546.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/TargetInfo.cpp (+2)
  • (modified) clang/test/CodeGenCXX/sizeof-unwind-exception.cpp (+8)
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 1e58c3f217812..342a3af0ac1ee 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -82,6 +82,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().getCodeGenOpts().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 }

@efriedma-quic
Copy link
Collaborator

(See the SEH ifdef in _Unwind_Exception in clang/lib/Headers/unwind.h.)

The header also checks that __USING_SJLJ_EXCEPTIONS__ is not defined. Is that just redundant?

@mstorsjo
Copy link
Member Author

mstorsjo commented Oct 8, 2025

(See the SEH ifdef in _Unwind_Exception in clang/lib/Headers/unwind.h.)

The header also checks that __USING_SJLJ_EXCEPTIONS__ is not defined. Is that just redundant?

Possibly, yes. I think those ifdefs stem from the corresponding cases in GCC's unwind.h, and we've tried to stay compatible with that. I'm not sure if it's possible to somehow end up having both __USING_SJLJ_EXCEPTIONS__ and __SEH__ defined at the same time there (but if it is, the interpretation is that SJLJ takes precedence). Within Clang I don't think that's possible; the ExceptionHandling field in CodeGenOptions is an enum that can only have one of the values at a time at least.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rjmccall Can you briefly look to make sure this makes sense? (This is Itanium unwind on Windows.)

For why we're doing this, see 5add20c ... but I don't really have enough context to say much beyond the explanation in the code.

@mstorsjo
Copy link
Member Author

@rjmccall Can you briefly look to make sure this makes sense? (This is Itanium unwind on Windows.)

For why we're doing this, see 5add20c ... but I don't really have enough context to say much beyond the explanation in the code.

Not sure if there's much extra explanation needed here; sizeof(_Unwind_Header) is different on the SEH targets, and this updates it to match that.

@mstorsjo
Copy link
Member Author

If others don't mind, I'll go ahead and merge this one soon.

@mstorsjo mstorsjo merged commit 10be254 into llvm:main Oct 16, 2025
11 of 12 checks passed
@mstorsjo mstorsjo deleted the exception-catch-ptr-ref branch October 16, 2025 06:49
@mstorsjo mstorsjo added this to the LLVM 21.x Release milestone Oct 16, 2025
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status Oct 16, 2025
@mstorsjo
Copy link
Member Author

/cherry-pick 10be254

@llvmbot
Copy link
Member

llvmbot commented Oct 16, 2025

/pull-request #163714

@llvmbot llvmbot moved this from Needs Triage to Done in LLVM Release Status Oct 16, 2025
@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 16, 2025

LLVM Buildbot has detected a new failure on builder clang-aarch64-quick running on linaro-clang-aarch64-quick while building clang at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/24069

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'Clangd Unit Tests :: ./ClangdTests/114/334' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests-Clangd Unit Tests-1008218-114-334.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=334 GTEST_SHARD_INDEX=114 /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests
--

Note: This is test shard 115 of 334.
[==========] Running 4 tests from 4 test suites.
[----------] Global test environment set-up.
[----------] 1 test from CompletionTest
[ RUN      ] CompletionTest.CommentsOnMembersFromHeader
ASTWorker building file /clangd-test/foo.cpp version null with command 
[/clangd-test]
clang -ffreestanding /clangd-test/foo.cpp
Driver produced command: cc1 -cc1 -triple aarch64-unknown-linux-gnu -fsyntax-only -disable-free -clear-ast-before-backend -main-file-name foo.cpp -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=non-leaf -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -ffreestanding -enable-tlsdesc -target-cpu generic -target-feature +v8a -target-feature +fp-armv8 -target-feature +neon -target-abi aapcs -debugger-tuning=gdb -fdebug-compilation-dir=/clangd-test -fcoverage-compilation-dir=/clangd-test -resource-dir lib/clang/22 -internal-isystem lib/clang/22/include -internal-isystem /usr/local/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -ferror-limit 19 -fno-signed-char -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -no-round-trip-args -target-feature -fmv -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -x c++ /clangd-test/foo.cpp
Building first preamble for /clangd-test/foo.cpp version null
not idle after addDocument
UNREACHABLE executed at ../llvm/clang-tools-extra/clangd/unittests/SyncAPI.cpp:22!
 #0 0x0000b88b6650fd7c llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xcffd7c)
 #1 0x0000b88b6650d86c llvm::sys::RunSignalHandlers() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xcfd86c)
 #2 0x0000b88b66510bbc SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x0000ed1af39338f8 (linux-vdso.so.1+0x8f8)
 #4 0x0000ed1af3452008 __pthread_kill_implementation ./nptl/./nptl/pthread_kill.c:44:76
 #5 0x0000ed1af340a83c gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x0000ed1af33f7134 abort ./stdlib/./stdlib/abort.c:81:7
 #7 0x0000b88b664bbd94 llvm::RTTIRoot::anchor() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xcabd94)
 #8 0x0000b88b66352a44 clang::clangd::runCodeComplete(clang::clangd::ClangdServer&, llvm::StringRef, clang::clangd::Position, clang::clangd::CodeCompleteOptions) (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xb42a44)
 #9 0x0000b88b65e887b4 clang::clangd::(anonymous namespace)::CompletionTest_CommentsOnMembersFromHeader_Test::TestBody() CodeCompleteTests.cpp:0:0
#10 0x0000b88b6656a908 testing::Test::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xd5a908)
#11 0x0000b88b6656bc18 testing::TestInfo::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xd5bc18)
#12 0x0000b88b6656c7e0 testing::TestSuite::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xd5c7e0)
#13 0x0000b88b6657d528 testing::internal::UnitTestImpl::RunAllTests() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xd6d528)
#14 0x0000b88b6657ce94 testing::UnitTest::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xd6ce94)
#15 0x0000b88b66556cf4 main (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xd46cf4)
#16 0x0000ed1af33f7400 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#17 0x0000ed1af33f74d8 call_init ./csu/../csu/libc-start.c:128:20
#18 0x0000ed1af33f74d8 __libc_start_main ./csu/../csu/libc-start.c:379:5
#19 0x0000b88b65c9f3b0 _start (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0x48f3b0)

--
exit: -6
--
shard JSON output does not exist: /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests-Clangd Unit Tests-1008218-114-334.json
********************


mstorsjo added a commit to llvmbot/llvm-project that referenced this pull request Oct 17, 2025
)

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 mstorsjo/llvm-mingw#522.

(cherry picked from commit 10be254)
c-rhodes pushed a commit to llvmbot/llvm-project that referenced this pull request Oct 17, 2025
)

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 mstorsjo/llvm-mingw#522.

(cherry picked from commit 10be254)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category

Projects

Development

Successfully merging this pull request may close these issues.

Incompatible behavior when catching exceptions

5 participants