c++: Drop TREE_READONLY on vars (possibly) initialized by tls wrapper [PR109164]

The following two testcases are miscompiled, because we keep TREE_READONLY
on the vars even when they are (possibly) dynamically initialized by a TLS
wrapper function.  Normally cp_finish_decl drops TREE_READONLY from vars
which need dynamic initialization, but for TLS we do this kind of
initialization upon every access to those variables.  Keeping them
TREE_READONLY means e.g. PRE can hoist loads from those before loops
which contain the TLS wrapper calls, so we can access the TLS variables
before they are initialized.

2023-03-20  Jakub Jelinek  <jakub@redhat.com>

	PR c++/109164
	* cp-tree.h (var_needs_tls_wrapper): Declare.
	* decl2.cc (var_needs_tls_wrapper): No longer static.
	* decl.cc (cp_finish_decl): Clear TREE_READONLY on TLS variables
	for which a TLS wrapper will be needed.

	* g++.dg/tls/thread_local13.C: New test.
	* g++.dg/tls/thread_local13-aux.cc: New file.
	* g++.dg/tls/thread_local14.C: New test.
	* g++.dg/tls/thread_local14-aux.cc: New file.
This commit is contained in:
Jakub Jelinek 2023-03-20 20:29:47 +01:00
parent c67f312d20
commit 0a846340b9
7 changed files with 115 additions and 1 deletions

View file

@ -6989,6 +6989,7 @@ extern void copy_linkage (tree, tree);
extern tree get_guard (tree);
extern tree get_guard_cond (tree, bool);
extern tree set_guard (tree);
extern bool var_needs_tls_wrapper (tree);
extern tree maybe_get_tls_wrapper_call (tree);
extern void mark_needed (tree);
extern bool decl_needed_p (tree);

View file

@ -8706,6 +8706,18 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
if (!decl_maybe_constant_destruction (decl, type))
TREE_READONLY (decl) = 0;
}
else if (VAR_P (decl)
&& CP_DECL_THREAD_LOCAL_P (decl)
&& (!DECL_EXTERNAL (decl) || flag_extern_tls_init)
&& (was_readonly || TREE_READONLY (decl))
&& var_needs_tls_wrapper (decl))
{
/* TLS variables need dynamic initialization by the TLS wrapper
function, we don't want to hoist accesses to it before the
wrapper. */
was_readonly = 0;
TREE_READONLY (decl) = 0;
}
make_rtl_for_nonlocal_decl (decl, init, asmspec);

View file

@ -3623,7 +3623,7 @@ var_defined_without_dynamic_init (tree var)
/* Returns true iff VAR is a variable that needs uses to be
wrapped for possible dynamic initialization. */
static bool
bool
var_needs_tls_wrapper (tree var)
{
return (!error_operand_p (var)

View file

@ -0,0 +1,35 @@
// PR c++/109164
struct S { virtual void foo (); int s; };
extern bool baz ();
void
S::foo ()
{
if (s != 42)
__builtin_abort ();
}
S s;
S &
qux ()
{
s.s = 42;
return s;
}
thread_local S &t = qux ();
bool
bar ()
{
return false;
}
int
main ()
{
if (baz ())
__builtin_abort ();
}

View file

@ -0,0 +1,21 @@
// PR c++/109164
// { dg-do run { target c++11 } }
// { dg-options "-O2" }
// { dg-add-options tls }
// { dg-require-effective-target tls_runtime }
// { dg-additional-sources "thread_local13-aux.cc" }
struct S { virtual void foo (); int s; };
extern thread_local S &t;
bool bar ();
bool
baz ()
{
while (1)
{
t.foo ();
if (!bar ())
return false;
}
}

View file

@ -0,0 +1,26 @@
// PR c++/109164
extern bool baz ();
int
qux ()
{
return 42;
}
extern thread_local const int t = qux ();
bool
bar (int x)
{
if (x != 42)
__builtin_abort ();
return false;
}
int
main ()
{
if (baz ())
__builtin_abort ();
}

View file

@ -0,0 +1,19 @@
// PR c++/109164
// { dg-do run { target c++11 } }
// { dg-options "-O2" }
// { dg-add-options tls }
// { dg-require-effective-target tls_runtime }
// { dg-additional-sources "thread_local14-aux.cc" }
extern thread_local const int t;
bool bar (int);
bool
baz ()
{
while (1)
{
if (!bar (t))
return false;
}
}