Commit b1db7f91 by Jason Merrill Committed by Jason Merrill

Partial implementation of C++11 thread_local.

c-family/
	* c-common.c (c_common_reswords): Add thread_local.
cp/
	* decl.c (cp_finish_decl): Remove errors about non-trivial
	initialization and destruction of TLS variables.
	(register_dtor_fn): Add sorry about TLS variables.
	(expand_static_init): Add sorry about non-local TLS variables,
	or error with __thread.
	Don't emit thread-safety guards for local TLS variables.
	(grokdeclarator): thread_local in a function implies static.
	* decl.h: Adjust prototype.
	* decl2.c (get_guard): Copy DECL_TLS_MODEL.
	* parser.c (cp_parser_set_storage_class, cp_parser_set_decl_spec_type)
	(set_and_check_decl_spec_loc): Take the token rather than the location.
	Distinguish between __thread and thread_local.
	(cp_parser_set_storage_class): Don't complain about thread_local before
	extern/static.
	(token_is__thread): New.
	* call.c (make_temporary_var_for_ref_to_temp): Handle TLS.
	* cp-tree.h (DECL_GNU_TLS_P): New.
	(cp_decl_specifier_seq): Add gnu_thread_keyword_p.

From-SVN: r192209
parent 2991392b
2012-10-08 Jason Merrill <jason@redhat.com>
* c-common.c (c_common_reswords): Add thread_local.
2012-10-08 Dodji Seketeli <dodji@redhat.com> 2012-10-08 Dodji Seketeli <dodji@redhat.com>
PR c++/53528 C++11 attribute support PR c++/53528 C++11 attribute support
......
...@@ -543,6 +543,7 @@ const struct c_common_resword c_common_reswords[] = ...@@ -543,6 +543,7 @@ const struct c_common_resword c_common_reswords[] =
{ "switch", RID_SWITCH, 0 }, { "switch", RID_SWITCH, 0 },
{ "template", RID_TEMPLATE, D_CXXONLY | D_CXXWARN }, { "template", RID_TEMPLATE, D_CXXONLY | D_CXXWARN },
{ "this", RID_THIS, D_CXXONLY | D_CXXWARN }, { "this", RID_THIS, D_CXXONLY | D_CXXWARN },
{ "thread_local", RID_THREAD, D_CXXONLY | D_CXX0X | D_CXXWARN },
{ "throw", RID_THROW, D_CXX_OBJC | D_CXXWARN }, { "throw", RID_THROW, D_CXX_OBJC | D_CXXWARN },
{ "true", RID_TRUE, D_CXXONLY | D_CXXWARN }, { "true", RID_TRUE, D_CXXONLY | D_CXXWARN },
{ "try", RID_TRY, D_CXX_OBJC | D_CXXWARN }, { "try", RID_TRY, D_CXX_OBJC | D_CXXWARN },
......
2012-10-08 Jason Merrill <jason@redhat.com>
Partial implementation of C++11 thread_local.
* decl.c (cp_finish_decl): Remove errors about non-trivial
initialization and destruction of TLS variables.
(register_dtor_fn): Add sorry about TLS variables.
(expand_static_init): Add sorry about non-local TLS variables,
or error with __thread.
Don't emit thread-safety guards for local TLS variables.
(grokdeclarator): thread_local in a function implies static.
* decl.h: Adjust prototype.
* decl2.c (get_guard): Copy DECL_TLS_MODEL.
* parser.c (cp_parser_set_storage_class, cp_parser_set_decl_spec_type)
(set_and_check_decl_spec_loc): Take the token rather than the location.
Distinguish between __thread and thread_local.
(cp_parser_set_storage_class): Don't complain about thread_local before
extern/static.
(token_is__thread): New.
* call.c (make_temporary_var_for_ref_to_temp): Handle TLS.
* cp-tree.h (DECL_GNU_TLS_P): New.
(cp_decl_specifier_seq): Add gnu_thread_keyword_p.
2012-10-08 Dodji Seketeli <dodji@redhat.com> 2012-10-08 Dodji Seketeli <dodji@redhat.com>
PR c++/53528 C++11 attribute support PR c++/53528 C++11 attribute support
......
...@@ -8719,9 +8719,9 @@ perform_direct_initialization_if_possible (tree type, ...@@ -8719,9 +8719,9 @@ perform_direct_initialization_if_possible (tree type,
The next several functions are involved in this lifetime extension. */ The next several functions are involved in this lifetime extension. */
/* DECL is a VAR_DECL whose type is a REFERENCE_TYPE. The reference /* DECL is a VAR_DECL or FIELD_DECL whose type is a REFERENCE_TYPE. The
is being bound to a temporary. Create and return a new VAR_DECL reference is being bound to a temporary. Create and return a new
with the indicated TYPE; this variable will store the value to VAR_DECL with the indicated TYPE; this variable will store the value to
which the reference is bound. */ which the reference is bound. */
tree tree
...@@ -8733,13 +8733,15 @@ make_temporary_var_for_ref_to_temp (tree decl, tree type) ...@@ -8733,13 +8733,15 @@ make_temporary_var_for_ref_to_temp (tree decl, tree type)
var = create_temporary_var (type); var = create_temporary_var (type);
/* Register the variable. */ /* Register the variable. */
if (TREE_STATIC (decl)) if (TREE_CODE (decl) == VAR_DECL
&& (TREE_STATIC (decl) || DECL_THREAD_LOCAL_P (decl)))
{ {
/* Namespace-scope or local static; give it a mangled name. */ /* Namespace-scope or local static; give it a mangled name. */
/* FIXME share comdat with decl? */ /* FIXME share comdat with decl? */
tree name; tree name;
TREE_STATIC (var) = 1; TREE_STATIC (var) = TREE_STATIC (decl);
DECL_TLS_MODEL (var) = DECL_TLS_MODEL (decl);
name = mangle_ref_init_variable (decl); name = mangle_ref_init_variable (decl);
DECL_NAME (var) = name; DECL_NAME (var) = name;
SET_DECL_ASSEMBLER_NAME (var, name); SET_DECL_ASSEMBLER_NAME (var, name);
......
...@@ -56,6 +56,7 @@ c-common.h, not after. ...@@ -56,6 +56,7 @@ c-common.h, not after.
AGGR_INIT_VIA_CTOR_P (in AGGR_INIT_EXPR) AGGR_INIT_VIA_CTOR_P (in AGGR_INIT_EXPR)
PTRMEM_OK_P (in ADDR_EXPR, OFFSET_REF, SCOPE_REF) PTRMEM_OK_P (in ADDR_EXPR, OFFSET_REF, SCOPE_REF)
PAREN_STRING_LITERAL (in STRING_CST) PAREN_STRING_LITERAL (in STRING_CST)
DECL_GNU_TLS_P (in VAR_DECL)
KOENIG_LOOKUP_P (in CALL_EXPR) KOENIG_LOOKUP_P (in CALL_EXPR)
STATEMENT_LIST_NO_SCOPE (in STATEMENT_LIST). STATEMENT_LIST_NO_SCOPE (in STATEMENT_LIST).
EXPR_STMT_STMT_EXPR_RESULT (in EXPR_STMT) EXPR_STMT_STMT_EXPR_RESULT (in EXPR_STMT)
...@@ -2425,6 +2426,11 @@ struct GTY((variable_size)) lang_decl { ...@@ -2425,6 +2426,11 @@ struct GTY((variable_size)) lang_decl {
(DECL_NAME (NODE) \ (DECL_NAME (NODE) \
&& !strcmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__PRETTY_FUNCTION__")) && !strcmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__PRETTY_FUNCTION__"))
/* Nonzero if the thread-local variable was declared with __thread
as opposed to thread_local. */
#define DECL_GNU_TLS_P(NODE) \
(TREE_LANG_FLAG_0 (VAR_DECL_CHECK (NODE)))
/* The _TYPE context in which this _DECL appears. This field holds the /* The _TYPE context in which this _DECL appears. This field holds the
class where a virtual function instance is actually defined. */ class where a virtual function instance is actually defined. */
#define DECL_CLASS_CONTEXT(NODE) \ #define DECL_CLASS_CONTEXT(NODE) \
...@@ -4732,6 +4738,8 @@ typedef struct cp_decl_specifier_seq { ...@@ -4732,6 +4738,8 @@ typedef struct cp_decl_specifier_seq {
BOOL_BITFIELD explicit_int128_p : 1; BOOL_BITFIELD explicit_int128_p : 1;
/* True iff "char" was explicitly provided. */ /* True iff "char" was explicitly provided. */
BOOL_BITFIELD explicit_char_p : 1; BOOL_BITFIELD explicit_char_p : 1;
/* True iff ds_thread is set for __thread, not thread_local. */
BOOL_BITFIELD gnu_thread_keyword_p : 1;
} cp_decl_specifier_seq; } cp_decl_specifier_seq;
/* The various kinds of declarators. */ /* The various kinds of declarators. */
......
...@@ -6227,13 +6227,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, ...@@ -6227,13 +6227,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
if (TREE_CODE (decl) == VAR_DECL) if (TREE_CODE (decl) == VAR_DECL)
{ {
/* Only variables with trivial initialization and destruction can
have thread-local storage. */
if (DECL_THREAD_LOCAL_P (decl)
&& (type_has_nontrivial_default_init (TREE_TYPE (decl))
|| TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (decl))))
error ("%qD cannot be thread-local because it has non-trivial "
"type %qT", decl, TREE_TYPE (decl));
/* If this is a local variable that will need a mangled name, /* If this is a local variable that will need a mangled name,
register it now. We must do this before processing the register it now. We must do this before processing the
initializer for the variable, since the initialization might initializer for the variable, since the initialization might
...@@ -6279,13 +6272,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, ...@@ -6279,13 +6272,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
} }
cleanups = make_tree_vector (); cleanups = make_tree_vector ();
init = check_initializer (decl, init, flags, &cleanups); init = check_initializer (decl, init, flags, &cleanups);
/* Thread-local storage cannot be dynamically initialized. */
if (DECL_THREAD_LOCAL_P (decl) && init)
{
error ("%qD is thread-local and so cannot be dynamically "
"initialized", decl);
init = NULL_TREE;
}
/* Check that the initializer for a static data member was a /* Check that the initializer for a static data member was a
constant. Although we check in the parser that the constant. Although we check in the parser that the
...@@ -6734,6 +6720,12 @@ register_dtor_fn (tree decl) ...@@ -6734,6 +6720,12 @@ register_dtor_fn (tree decl)
end_cleanup_fn (); end_cleanup_fn ();
} }
if (DECL_THREAD_LOCAL_P (decl))
/* We don't have a thread-local atexit yet. FIXME write one using
pthread_key_create and friends. */
sorry ("thread-local variable %q#D with non-trivial "
"destructor", decl);
/* Call atexit with the cleanup function. */ /* Call atexit with the cleanup function. */
mark_used (cleanup); mark_used (cleanup);
cleanup = build_address (cleanup); cleanup = build_address (cleanup);
...@@ -6797,6 +6789,36 @@ expand_static_init (tree decl, tree init) ...@@ -6797,6 +6789,36 @@ expand_static_init (tree decl, tree init)
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))) && TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
return; return;
if (DECL_THREAD_LOCAL_P (decl) && DECL_GNU_TLS_P (decl)
&& !DECL_FUNCTION_SCOPE_P (decl))
{
if (init)
error ("non-local variable %qD declared %<__thread%> "
"needs dynamic initialization", decl);
else
error ("non-local variable %qD declared %<__thread%> "
"has a non-trivial destructor", decl);
static bool informed;
if (!informed)
{
inform (DECL_SOURCE_LOCATION (decl),
"C++11 %<thread_local%> allows dynamic initialization "
"and destruction");
informed = true;
}
return;
}
if (DECL_THREAD_LOCAL_P (decl) && !DECL_FUNCTION_SCOPE_P (decl))
{
/* We haven't implemented dynamic initialization of non-local
thread-local storage yet. FIXME transform to singleton
function. */
sorry ("thread-local variable %qD with dynamic initialization outside "
"function scope", decl);
return;
}
if (DECL_FUNCTION_SCOPE_P (decl)) if (DECL_FUNCTION_SCOPE_P (decl))
{ {
/* Emit code to perform this initialization but once. */ /* Emit code to perform this initialization but once. */
...@@ -6804,6 +6826,9 @@ expand_static_init (tree decl, tree init) ...@@ -6804,6 +6826,9 @@ expand_static_init (tree decl, tree init)
tree then_clause = NULL_TREE, inner_then_clause = NULL_TREE; tree then_clause = NULL_TREE, inner_then_clause = NULL_TREE;
tree guard, guard_addr; tree guard, guard_addr;
tree flag, begin; tree flag, begin;
/* We don't need thread-safety code for thread-local vars. */
bool thread_guard = (flag_threadsafe_statics
&& !DECL_THREAD_LOCAL_P (decl));
/* Emit code to perform this initialization but once. This code /* Emit code to perform this initialization but once. This code
looks like: looks like:
...@@ -6842,7 +6867,7 @@ expand_static_init (tree decl, tree init) ...@@ -6842,7 +6867,7 @@ expand_static_init (tree decl, tree init)
/* This optimization isn't safe on targets with relaxed memory /* This optimization isn't safe on targets with relaxed memory
consistency. On such targets we force synchronization in consistency. On such targets we force synchronization in
__cxa_guard_acquire. */ __cxa_guard_acquire. */
if (!targetm.relaxed_ordering || !flag_threadsafe_statics) if (!targetm.relaxed_ordering || !thread_guard)
{ {
/* Begin the conditional initialization. */ /* Begin the conditional initialization. */
if_stmt = begin_if_stmt (); if_stmt = begin_if_stmt ();
...@@ -6850,7 +6875,7 @@ expand_static_init (tree decl, tree init) ...@@ -6850,7 +6875,7 @@ expand_static_init (tree decl, tree init)
then_clause = begin_compound_stmt (BCS_NO_SCOPE); then_clause = begin_compound_stmt (BCS_NO_SCOPE);
} }
if (flag_threadsafe_statics) if (thread_guard)
{ {
tree vfntype = NULL_TREE; tree vfntype = NULL_TREE;
tree acquire_name, release_name, abort_name; tree acquire_name, release_name, abort_name;
...@@ -6908,14 +6933,14 @@ expand_static_init (tree decl, tree init) ...@@ -6908,14 +6933,14 @@ expand_static_init (tree decl, tree init)
finish_expr_stmt (init); finish_expr_stmt (init);
if (flag_threadsafe_statics) if (thread_guard)
{ {
finish_compound_stmt (inner_then_clause); finish_compound_stmt (inner_then_clause);
finish_then_clause (inner_if_stmt); finish_then_clause (inner_if_stmt);
finish_if_stmt (inner_if_stmt); finish_if_stmt (inner_if_stmt);
} }
if (!targetm.relaxed_ordering || !flag_threadsafe_statics) if (!targetm.relaxed_ordering || !thread_guard)
{ {
finish_compound_stmt (then_clause); finish_compound_stmt (then_clause);
finish_then_clause (if_stmt); finish_then_clause (if_stmt);
...@@ -7732,7 +7757,11 @@ grokvardecl (tree type, ...@@ -7732,7 +7757,11 @@ grokvardecl (tree type,
} }
if (decl_spec_seq_has_spec_p (declspecs, ds_thread)) if (decl_spec_seq_has_spec_p (declspecs, ds_thread))
DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); {
DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
if (declspecs->gnu_thread_keyword_p)
DECL_GNU_TLS_P (decl) = true;
}
/* If the type of the decl has no linkage, make sure that we'll /* If the type of the decl has no linkage, make sure that we'll
notice that in mark_used. */ notice that in mark_used. */
...@@ -8462,7 +8491,7 @@ check_var_type (tree identifier, tree type) ...@@ -8462,7 +8491,7 @@ check_var_type (tree identifier, tree type)
tree tree
grokdeclarator (const cp_declarator *declarator, grokdeclarator (const cp_declarator *declarator,
const cp_decl_specifier_seq *declspecs, cp_decl_specifier_seq *declspecs,
enum decl_context decl_context, enum decl_context decl_context,
int initialized, int initialized,
tree* attrlist) tree* attrlist)
...@@ -9176,9 +9205,15 @@ grokdeclarator (const cp_declarator *declarator, ...@@ -9176,9 +9205,15 @@ grokdeclarator (const cp_declarator *declarator,
&& storage_class != sc_extern && storage_class != sc_extern
&& storage_class != sc_static) && storage_class != sc_static)
{ {
error ("function-scope %qs implicitly auto and declared %<__thread%>", if (declspecs->gnu_thread_keyword_p)
name); pedwarn (input_location, 0, "function-scope %qs implicitly auto and "
thread_p = false; "declared %<__thread%>", name);
/* When thread_local is applied to a variable of block scope the
storage-class-specifier static is implied if it does not appear
explicitly. */
storage_class = declspecs->storage_class = sc_static;
staticp = 1;
} }
if (storage_class && friendp) if (storage_class && friendp)
...@@ -10454,7 +10489,14 @@ grokdeclarator (const cp_declarator *declarator, ...@@ -10454,7 +10489,14 @@ grokdeclarator (const cp_declarator *declarator,
else if (storage_class == sc_register) else if (storage_class == sc_register)
error ("storage class %<register%> invalid for function %qs", name); error ("storage class %<register%> invalid for function %qs", name);
else if (thread_p) else if (thread_p)
error ("storage class %<__thread%> invalid for function %qs", name); {
if (declspecs->gnu_thread_keyword_p)
error ("storage class %<__thread%> invalid for function %qs",
name);
else
error ("storage class %<thread_local%> invalid for function %qs",
name);
}
if (virt_specifiers) if (virt_specifiers)
error ("virt-specifiers in %qs not allowed outside a class definition", name); error ("virt-specifiers in %qs not allowed outside a class definition", name);
......
...@@ -34,7 +34,7 @@ enum decl_context ...@@ -34,7 +34,7 @@ enum decl_context
/* We need this in here to get the decl_context definition. */ /* We need this in here to get the decl_context definition. */
extern tree grokdeclarator (const cp_declarator *, extern tree grokdeclarator (const cp_declarator *,
const cp_decl_specifier_seq *, cp_decl_specifier_seq *,
enum decl_context, int, tree*); enum decl_context, int, tree*);
/* States indicating how grokdeclarator() should handle declspecs marked /* States indicating how grokdeclarator() should handle declspecs marked
......
...@@ -2696,6 +2696,7 @@ get_guard (tree decl) ...@@ -2696,6 +2696,7 @@ get_guard (tree decl)
TREE_STATIC (guard) = TREE_STATIC (decl); TREE_STATIC (guard) = TREE_STATIC (decl);
DECL_COMMON (guard) = DECL_COMMON (decl); DECL_COMMON (guard) = DECL_COMMON (decl);
DECL_COMDAT (guard) = DECL_COMDAT (decl); DECL_COMDAT (guard) = DECL_COMDAT (decl);
DECL_TLS_MODEL (guard) = DECL_TLS_MODEL (decl);
if (DECL_ONE_ONLY (decl)) if (DECL_ONE_ONLY (decl))
make_decl_one_only (guard, cxx_comdat_group (guard)); make_decl_one_only (guard, cxx_comdat_group (guard));
if (TREE_PUBLIC (decl)) if (TREE_PUBLIC (decl))
......
2012-10-08 Jason Merrill <jason@redhat.com>
* g++.dg/tls/init-2.C: Tweak errors.
* g++.dg/tls/thread_local1.C: New.
* g++.dg/tls/thread_local2.C: New.
* g++.dg/tls/thread_local7.C: New.
2012-10-08 Oleg Endo <olegendo@gcc.gnu.org> 2012-10-08 Oleg Endo <olegendo@gcc.gnu.org>
PR target/54685 PR target/54685
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
/* { dg-require-effective-target tls } */ /* { dg-require-effective-target tls } */
extern __thread int i; extern __thread int i;
__thread int *p = &i; /* { dg-error "dynamically initialized" } */ __thread int *p = &i; /* { dg-error "dynamic initialization" } */
extern int f(); extern int f();
__thread int j = f(); /* { dg-error "dynamically initialized" } */ __thread int j = f(); /* { dg-error "dynamic initialization" } */
struct S struct S
{ {
S(); S();
}; };
__thread S s; /* { dg-error "" } two errors here */ __thread S s; /* { dg-error "dynamic initialization" } */
// { dg-options "-std=c++11" }
// { dg-require-effective-target tls }
// The variable should have a guard.
// { dg-final { scan-assembler "_ZGVZ1fvE1a" } }
// But since it's thread local we don't need to guard against
// simultaneous execution.
// { dg-final { scan-assembler-not "cxa_guard" } }
// The guard should be TLS, not local common.
// { dg-final { scan-assembler-not "\.comm" } }
struct A
{
A();
};
A &f()
{
thread_local A a;
return a;
}
// { dg-do run }
// { dg-options "-std=c++11" }
// { dg-require-effective-target tls_runtime }
extern "C" void abort();
struct A
{
A();
int i;
};
A &f()
{
thread_local A a;
return a;
}
int j;
A::A(): i(j) { }
int main()
{
j = 42;
if (f().i != 42)
abort ();
}
// { dg-options "-std=c++11" }
// { dg-require-effective-target tls }
// The reference temp should be TLS, not normal data.
// { dg-final { scan-assembler-not "\\.data" } }
void f()
{
thread_local int&& ir = 42;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment