|
|
a46658 |
c++: Optimize constinit thread_local vars [PR101786]
|
|
|
a46658 |
|
|
|
a46658 |
The paper that introduced constinit mentioned in rationale that constinit
|
|
|
a46658 |
can be used on externs as well and that it can be used to avoid the
|
|
|
a46658 |
thread_local initialization wrappers, because the standard requires that
|
|
|
a46658 |
if constinit is present on any declaration, it is also present on the
|
|
|
a46658 |
initialization declaration, even if it is in some other TU etc.
|
|
|
a46658 |
|
|
|
a46658 |
There is a small problem though, we use the tls wrappers not just if
|
|
|
a46658 |
the thread_local variable needs dynamic initialization, but also when
|
|
|
a46658 |
it has static initialization, but non-trivial destructor, as the
|
|
|
a46658 |
"dynamic initialization" in that case needs to register the destructor.
|
|
|
a46658 |
|
|
|
a46658 |
So, the following patch optimizes constinit thread_local vars only
|
|
|
a46658 |
if we can prove they will not have non-trivial destructors. That includes
|
|
|
a46658 |
the case where we have incomplete type where we don't know and need to
|
|
|
a46658 |
conservatively assume the type will have non-trivial destructor at the
|
|
|
a46658 |
initializing declaration side.
|
|
|
a46658 |
|
|
|
a46658 |
2021-08-11 Jakub Jelinek <jakub@redhat.com>
|
|
|
a46658 |
|
|
|
a46658 |
PR c++/101786
|
|
|
a46658 |
* decl2.c (var_defined_without_dynamic_init): Return true for
|
|
|
a46658 |
DECL_DECLARED_CONSTINIT_P with complete type and trivial destructor.
|
|
|
a46658 |
|
|
|
a46658 |
* g++.dg/cpp2a/constinit16.C: New test.
|
|
|
a46658 |
|
|
|
a46658 |
--- gcc/cp/decl2.c
|
|
|
a46658 |
+++ gcc/cp/decl2.c
|
|
|
a46658 |
@@ -3447,6 +3447,12 @@ set_guard (tree guard)
|
|
|
a46658 |
static bool
|
|
|
a46658 |
var_defined_without_dynamic_init (tree var)
|
|
|
a46658 |
{
|
|
|
a46658 |
+ /* constinit vars are guaranteed to not have dynamic initializer,
|
|
|
a46658 |
+ but still registering the destructor counts as dynamic initialization. */
|
|
|
a46658 |
+ if (DECL_DECLARED_CONSTINIT_P (var)
|
|
|
a46658 |
+ && COMPLETE_TYPE_P (TREE_TYPE (var))
|
|
|
a46658 |
+ && !TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var)))
|
|
|
a46658 |
+ return true;
|
|
|
a46658 |
/* If it's defined in another TU, we can't tell. */
|
|
|
a46658 |
if (DECL_EXTERNAL (var))
|
|
|
a46658 |
return false;
|
|
|
a46658 |
--- gcc/testsuite/g++.dg/cpp2a/constinit16.C
|
|
|
a46658 |
+++ gcc/testsuite/g++.dg/cpp2a/constinit16.C
|
|
|
a46658 |
@@ -0,0 +1,21 @@
|
|
|
a46658 |
+// PR c++/101786
|
|
|
a46658 |
+// { dg-do compile { target c++20 } }
|
|
|
a46658 |
+// { dg-add-options tls }
|
|
|
a46658 |
+// { dg-require-alias "" }
|
|
|
a46658 |
+// { dg-require-effective-target tls_runtime }
|
|
|
a46658 |
+// { dg-final { scan-assembler-not "_ZTH17mythreadlocalvar1" } }
|
|
|
a46658 |
+// { dg-final { scan-assembler "_ZTH17mythreadlocalvar2" } }
|
|
|
a46658 |
+// { dg-final { scan-assembler-not "_ZTH17mythreadlocalvar3" } }
|
|
|
a46658 |
+// { dg-final { scan-assembler "_ZTH17mythreadlocalvar4" } }
|
|
|
a46658 |
+
|
|
|
a46658 |
+extern thread_local constinit int mythreadlocalvar1;
|
|
|
a46658 |
+struct S;
|
|
|
a46658 |
+extern thread_local constinit S mythreadlocalvar2;
|
|
|
a46658 |
+struct T { int t; };
|
|
|
a46658 |
+extern thread_local constinit T mythreadlocalvar3;
|
|
|
a46658 |
+struct U { int u; ~U (); };
|
|
|
a46658 |
+extern thread_local constinit U mythreadlocalvar4;
|
|
|
a46658 |
+int foo () { return mythreadlocalvar1; }
|
|
|
a46658 |
+S *bar () { return &mythreadlocalvar2; }
|
|
|
a46658 |
+T *baz () { return &mythreadlocalvar3; }
|
|
|
a46658 |
+U *qux () { return &mythreadlocalvar4; }
|