llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: RobinTF
<details>
<summary>Changes</summary>
Hi, so this seems to be a long standing issue I stumbled upon that seemed easy
to fix (with heavy AI help), so here's my best effort attempt to provide a
working fix for it. I'm pretty much convinced that this is the correct fix, but
I have no idea how the tests work, so I'm completely trusting the LLM on that
one, I'd appreciate any suggestions on that. Thank you!
Under ABIs whose destructors return `this` (ARM, WebAssembly), a destructor can
only be registered directly (with `__cxa_atexit` etc.) if the target tolerates
the resulting function type mismatch. Clang already checks this for global
variables and registers a helper function instead, but temporaries that are
lifetime-extended by a reference with static or thread storage duration always
had their destructor registered directly. On WebAssembly, which does not
tolerate the mismatch, this traps with "function signature mismatch" when the
exit-time destructors run. This PR changes the if condition to use the destroy
helper that is already used for arrays if the destructor cannot be registered
directly.
Previously the following code would result in this:
```cpp
#include <cstdio>
struct B { B() {} ~B() { std::puts("~B"); } };
const B& b = B();
int main() {}
```
```
$ em++ -O2 -sEXIT_RUNTIME=1 repro.cpp -o repro.js && node repro.js
RuntimeError: function signature mismatch
<node stack trace>
```
with this patch applied it simply prints "~B".
Fixes #<!-- -->45221
Assisted-by: Claude Opus 5.5
---
Full diff: https://github.com/llvm/llvm-project/pull/226619.diff
2 Files Affected:
- (modified) clang/lib/CodeGen/CGExpr.cpp (+8-1)
- (modified) clang/test/CodeGenCXX/static-destructor.cpp (+28)
``````````diff
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 4a481c01f6a68b..f61fd0619af6b9 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -442,9 +442,16 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const
MaterializeTemporaryExpr *M,
if (!ReferenceTemporaryDtor)
return;
+ // Like in `EmitDeclDestroy`, destructors that return `this` need a
helper
+ // if the target does not tolerate the mismatch (e.g. WebAssembly).
+ bool CanRegisterDestructor =
+ !CGF.CGM.getCXXABI().HasThisReturn(
+ GlobalDecl(ReferenceTemporaryDtor, Dtor_Complete)) ||
+ CGF.CGM.getCXXABI().canCallMismatchedFunctionType();
+
llvm::FunctionCallee CleanupFn;
llvm::Constant *CleanupArg;
- if (E->getType()->isArrayType()) {
+ if (E->getType()->isArrayType() || !CanRegisterDestructor) {
CleanupFn = CodeGenFunction(CGF.CGM).generateDestroyHelper(
ReferenceTemporary, E->getType(),
CodeGenFunction::destroyCXXObject,
CGF.getLangOpts().Exceptions,
diff --git a/clang/test/CodeGenCXX/static-destructor.cpp
b/clang/test/CodeGenCXX/static-destructor.cpp
index eea5a2e1765535..0bef52601a02fb 100644
--- a/clang/test/CodeGenCXX/static-destructor.cpp
+++ b/clang/test/CodeGenCXX/static-destructor.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -emit-llvm -o - | FileCheck
--check-prefix=X86 %s
// RUN: %clang_cc1 %s -triple=wasm32 -emit-llvm -o - | FileCheck
--check-prefix=WASM %s
// RUN: %clang_cc1 %s -triple=armv7-apple-darwin9 -emit-llvm -o - | FileCheck
--check-prefix=ARM %s
+// RUN: %clang_cc1 %s -triple=wasm32 -emit-llvm -fno-use-cxa-atexit -DTLS -o -
| FileCheck --check-prefix=WASM-TLS %s
// Test that destructors are not passed directly to __cxa_atexit when their
// signatures do not match the type of its first argument.
@@ -31,3 +32,30 @@ Foo global;
// WASM: define internal void @__cxx_global_array_dtor(ptr noundef %0)
// WASM: %call = call noundef ptr @_ZN3FooD1Ev(ptr {{[^,]*}} @global)
+
+// The same holds for temporaries that are lifetime-extended by a reference
with
+// static storage duration.
+const Foo &global_ref = Foo();
+
+// X86: define internal void @__cxx_global_var_init.1()
+// X86: call i32 @__cxa_atexit(ptr @_ZN3FooD1Ev, ptr @_ZGR10global_ref_, ptr
@__dso_handle)
+
+// ARM: define internal void @__cxx_global_var_init.1()
+// ARM: call i32 @__cxa_atexit(ptr @_ZN3FooD1Ev, ptr @_ZGR10global_ref_, ptr
@__dso_handle)
+
+// WASM: define internal void @__cxx_global_var_init.1()
+// WASM: call i32 @__cxa_atexit(ptr
@[[REF_DTOR:__cxx_global_array_dtor[.0-9]*]], ptr null, ptr @__dso_handle)
+
+// WASM: define internal void @[[REF_DTOR]](ptr noundef %0)
+// WASM: %call = call noundef ptr @_ZN3FooD1Ev(ptr {{[^,]*}}
@_ZGR10global_ref_)
+
+// Thread-local ones are registered with `__cxa_thread_atexit`, even with
+// `-fno-use-cxa-atexit`.
+#ifdef TLS
+thread_local const Foo &tls_ref = Foo();
+#endif
+
+// WASM-TLS: call i32 @__cxa_thread_atexit(ptr
@[[TLS_DTOR:__cxx_global_array_dtor[.0-9]*]], ptr null, ptr @__dso_handle)
+
+// WASM-TLS: define internal void @[[TLS_DTOR]](ptr noundef %0)
+// WASM-TLS: %call = call noundef ptr @_ZN3FooD1Ev(ptr {{[^,]*}}
@_ZGR7tls_ref_)
``````````
</details>
https://github.com/llvm/llvm-project/pull/226619
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits