commit 972a5ac7c33d93b27d86d010af7c154da1281e6b
Author: Jason Merrill <jason@redhat.com>
Date: Wed Sep 19 11:23:24 2012 -0400
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.
@@ -542,6 +542,7 @@ const struct c_common_resword c_common_reswords[] =
{ "switch", RID_SWITCH, 0 },
{ "template", RID_TEMPLATE, 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 },
{ "true", RID_TRUE, D_CXXONLY | D_CXXWARN },
{ "try", RID_TRY, D_CXX_OBJC | D_CXXWARN },
@@ -8718,9 +8718,9 @@ perform_direct_initialization_if_possible (tree type,
The next several functions are involved in this lifetime extension. */
-/* DECL is a VAR_DECL whose type is a REFERENCE_TYPE. The reference
- is being bound to a temporary. Create and return a new VAR_DECL
- with the indicated TYPE; this variable will store the value to
+/* DECL is a VAR_DECL or FIELD_DECL whose type is a REFERENCE_TYPE. The
+ reference is being bound to a temporary. Create and return a new
+ VAR_DECL with the indicated TYPE; this variable will store the value to
which the reference is bound. */
tree
@@ -8732,13 +8732,15 @@ make_temporary_var_for_ref_to_temp (tree decl, tree type)
var = create_temporary_var (type);
/* 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. */
/* FIXME share comdat with decl? */
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);
DECL_NAME (var) = name;
SET_DECL_ASSEMBLER_NAME (var, name);
@@ -56,6 +56,7 @@ c-common.h, not after.
AGGR_INIT_VIA_CTOR_P (in AGGR_INIT_EXPR)
PTRMEM_OK_P (in ADDR_EXPR, OFFSET_REF, SCOPE_REF)
PAREN_STRING_LITERAL (in STRING_CST)
+ DECL_GNU_TLS_P (in VAR_DECL)
KOENIG_LOOKUP_P (in CALL_EXPR)
STATEMENT_LIST_NO_SCOPE (in STATEMENT_LIST).
EXPR_STMT_STMT_EXPR_RESULT (in EXPR_STMT)
@@ -2422,6 +2423,11 @@ struct GTY((variable_size)) lang_decl {
(DECL_NAME (NODE) \
&& !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
class where a virtual function instance is actually defined. */
#define DECL_CLASS_CONTEXT(NODE) \
@@ -4722,6 +4728,8 @@ typedef struct cp_decl_specifier_seq {
BOOL_BITFIELD explicit_int128_p : 1;
/* True iff "char" was explicitly provided. */
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;
/* The various kinds of declarators. */
@@ -6194,13 +6194,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
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,
register it now. We must do this before processing the
initializer for the variable, since the initialization might
@@ -6246,13 +6239,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
}
cleanups = make_tree_vector ();
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
constant. Although we check in the parser that the
@@ -6701,6 +6687,12 @@ register_dtor_fn (tree decl)
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. */
mark_used (cleanup);
cleanup = build_address (cleanup);
@@ -6764,6 +6756,36 @@ expand_static_init (tree decl, tree init)
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
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))
{
/* Emit code to perform this initialization but once. */
@@ -6771,6 +6793,9 @@ expand_static_init (tree decl, tree init)
tree then_clause = NULL_TREE, inner_then_clause = NULL_TREE;
tree guard, guard_addr;
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
looks like:
@@ -6809,7 +6834,7 @@ expand_static_init (tree decl, tree init)
/* This optimization isn't safe on targets with relaxed memory
consistency. On such targets we force synchronization in
__cxa_guard_acquire. */
- if (!targetm.relaxed_ordering || !flag_threadsafe_statics)
+ if (!targetm.relaxed_ordering || !thread_guard)
{
/* Begin the conditional initialization. */
if_stmt = begin_if_stmt ();
@@ -6817,7 +6842,7 @@ expand_static_init (tree decl, tree init)
then_clause = begin_compound_stmt (BCS_NO_SCOPE);
}
- if (flag_threadsafe_statics)
+ if (thread_guard)
{
tree vfntype = NULL_TREE;
tree acquire_name, release_name, abort_name;
@@ -6875,14 +6900,14 @@ expand_static_init (tree decl, tree init)
finish_expr_stmt (init);
- if (flag_threadsafe_statics)
+ if (thread_guard)
{
finish_compound_stmt (inner_then_clause);
finish_then_clause (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_then_clause (if_stmt);
@@ -7699,7 +7724,11 @@ grokvardecl (tree type,
}
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
notice that in mark_used. */
@@ -8386,7 +8415,7 @@ check_var_type (tree identifier, tree type)
tree
grokdeclarator (const cp_declarator *declarator,
- const cp_decl_specifier_seq *declspecs,
+ cp_decl_specifier_seq *declspecs,
enum decl_context decl_context,
int initialized,
tree* attrlist)
@@ -9100,9 +9129,15 @@ grokdeclarator (const cp_declarator *declarator,
&& storage_class != sc_extern
&& storage_class != sc_static)
{
- error ("function-scope %qs implicitly auto and declared %<__thread%>",
- name);
- thread_p = false;
+ if (declspecs->gnu_thread_keyword_p)
+ pedwarn (input_location, 0, "function-scope %qs implicitly auto and "
+ "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)
@@ -10337,7 +10372,14 @@ grokdeclarator (const cp_declarator *declarator,
else if (storage_class == sc_register)
error ("storage class %<register%> invalid for function %qs", name);
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)
error ("virt-specifiers in %qs not allowed outside a class definition", name);
@@ -34,7 +34,7 @@ enum decl_context
/* We need this in here to get the decl_context definition. */
extern tree grokdeclarator (const cp_declarator *,
- const cp_decl_specifier_seq *,
+ cp_decl_specifier_seq *,
enum decl_context, int, tree*);
/* States indicating how grokdeclarator() should handle declspecs marked
@@ -2696,6 +2696,7 @@ get_guard (tree decl)
TREE_STATIC (guard) = TREE_STATIC (decl);
DECL_COMMON (guard) = DECL_COMMON (decl);
DECL_COMDAT (guard) = DECL_COMDAT (decl);
+ DECL_TLS_MODEL (guard) = DECL_TLS_MODEL (decl);
if (DECL_ONE_ONLY (decl))
make_decl_one_only (guard, cxx_comdat_group (guard));
if (TREE_PUBLIC (decl))
@@ -2215,12 +2215,12 @@ static tree cp_parser_trait_expr
static bool cp_parser_declares_only_class_p
(cp_parser *);
static void cp_parser_set_storage_class
- (cp_parser *, cp_decl_specifier_seq *, enum rid, location_t);
+ (cp_parser *, cp_decl_specifier_seq *, enum rid, cp_token *);
static void cp_parser_set_decl_spec_type
- (cp_decl_specifier_seq *, tree, location_t, bool);
+ (cp_decl_specifier_seq *, tree, cp_token *, bool);
static void set_and_check_decl_spec_loc
(cp_decl_specifier_seq *decl_specs,
- cp_decl_spec ds, source_location location);
+ cp_decl_spec ds, cp_token *);
static bool cp_parser_friend_p
(const cp_decl_specifier_seq *);
static void cp_parser_required_error
@@ -10672,7 +10672,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
/* Set the storage class anyway. */
cp_parser_set_storage_class (parser, decl_specs, RID_AUTO,
- token->location);
+ token);
}
else
/* C++0x auto type-specifier. */
@@ -10686,7 +10686,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
/* Consume the token. */
cp_lexer_consume_token (parser->lexer);
cp_parser_set_storage_class (parser, decl_specs, token->keyword,
- token->location);
+ token);
break;
case RID_THREAD:
/* Consume the token. */
@@ -10706,7 +10706,7 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
error ("decl-specifier invalid in condition");
if (ds != ds_last)
- set_and_check_decl_spec_loc (decl_specs, ds, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds, token);
/* Constructors are a special case. The `S' in `S()' is not a
decl-specifier; it is the beginning of the declarator. */
@@ -10855,7 +10855,7 @@ cp_parser_function_specifier_opt (cp_parser* parser,
switch (token->keyword)
{
case RID_INLINE:
- set_and_check_decl_spec_loc (decl_specs, ds_inline, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds_inline, token);
break;
case RID_VIRTUAL:
@@ -10864,11 +10864,11 @@ cp_parser_function_specifier_opt (cp_parser* parser,
A member function template shall not be virtual. */
if (PROCESSING_REAL_TEMPLATE_DECL_P ())
error_at (token->location, "templates may not be %<virtual%>");
- set_and_check_decl_spec_loc (decl_specs, ds_virtual, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds_virtual, token);
break;
case RID_EXPLICIT:
- set_and_check_decl_spec_loc (decl_specs, ds_explicit, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds_explicit, token);
break;
default:
@@ -13372,7 +13372,7 @@ cp_parser_type_specifier (cp_parser* parser,
if (decl_specs)
cp_parser_set_decl_spec_type (decl_specs,
type_spec,
- token->location,
+ token,
/*type_definition_p=*/true);
return type_spec;
}
@@ -13401,7 +13401,7 @@ cp_parser_type_specifier (cp_parser* parser,
if (decl_specs)
cp_parser_set_decl_spec_type (decl_specs,
type_spec,
- token->location,
+ token,
/*type_definition_p=*/true);
return type_spec;
}
@@ -13423,7 +13423,7 @@ cp_parser_type_specifier (cp_parser* parser,
if (decl_specs)
cp_parser_set_decl_spec_type (decl_specs,
type_spec,
- token->location,
+ token,
/*type_definition_p=*/false);
return type_spec;
@@ -13459,7 +13459,7 @@ cp_parser_type_specifier (cp_parser* parser,
{
if (decl_specs)
{
- set_and_check_decl_spec_loc (decl_specs, ds, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds, token);
decl_specs->any_specifiers_p = true;
}
return cp_lexer_consume_token (parser->lexer)->u.value;
@@ -13550,7 +13550,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
type = boolean_type_node;
break;
case RID_SHORT:
- set_and_check_decl_spec_loc (decl_specs, ds_short, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds_short, token);
type = short_integer_type_node;
break;
case RID_INT:
@@ -13567,15 +13567,15 @@ cp_parser_simple_type_specifier (cp_parser* parser,
break;
case RID_LONG:
if (decl_specs)
- set_and_check_decl_spec_loc (decl_specs, ds_long, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds_long, token);
type = long_integer_type_node;
break;
case RID_SIGNED:
- set_and_check_decl_spec_loc (decl_specs, ds_signed, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds_signed, token);
type = integer_type_node;
break;
case RID_UNSIGNED:
- set_and_check_decl_spec_loc (decl_specs, ds_unsigned, token->location);
+ set_and_check_decl_spec_loc (decl_specs, ds_unsigned, token);
type = unsigned_type_node;
break;
case RID_FLOAT:
@@ -13613,7 +13613,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
if (decl_specs)
cp_parser_set_decl_spec_type (decl_specs, type,
- token->location,
+ token,
/*type_definition_p=*/false);
return type;
@@ -13622,7 +13622,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
type = cp_parser_trait_expr (parser, RID_UNDERLYING_TYPE);
if (decl_specs)
cp_parser_set_decl_spec_type (decl_specs, type,
- token->location,
+ token,
/*type_definition_p=*/false);
return type;
@@ -13632,7 +13632,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
type = cp_parser_trait_expr (parser, token->keyword);
if (decl_specs)
cp_parser_set_decl_spec_type (decl_specs, type,
- token->location,
+ token,
/*type_definition_p=*/false);
return type;
default:
@@ -13647,7 +13647,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
type = token->u.value;
if (decl_specs)
cp_parser_set_decl_spec_type (decl_specs, type,
- token->location,
+ token,
/*type_definition_p=*/false);
cp_lexer_consume_token (parser->lexer);
return type;
@@ -13664,7 +13664,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
&& token->keyword != RID_LONG))
cp_parser_set_decl_spec_type (decl_specs,
type,
- token->location,
+ token,
/*type_definition_p=*/false);
if (decl_specs)
decl_specs->any_specifiers_p = true;
@@ -13741,7 +13741,7 @@ cp_parser_simple_type_specifier (cp_parser* parser,
type = NULL_TREE;
if (type && decl_specs)
cp_parser_set_decl_spec_type (decl_specs, type,
- token->location,
+ token,
/*type_definition_p=*/false);
}
@@ -15092,21 +15092,24 @@ static tree
cp_parser_alias_declaration (cp_parser* parser)
{
tree id, type, decl, pushed_scope = NULL_TREE, attributes;
- location_t id_location, using_location, attrs_location = 0;
+ location_t id_location;
cp_declarator *declarator;
cp_decl_specifier_seq decl_specs;
bool member_p;
const char *saved_message = NULL;
/* Look for the `using' keyword. */
- using_location = cp_lexer_peek_token (parser->lexer)->location;
- cp_parser_require_keyword (parser, RID_USING, RT_USING);
+ cp_token *using_token
+ = cp_parser_require_keyword (parser, RID_USING, RT_USING);
+ if (using_token == NULL)
+ return error_mark_node;
+
id_location = cp_lexer_peek_token (parser->lexer)->location;
id = cp_parser_identifier (parser);
if (id == error_mark_node)
return error_mark_node;
- attrs_location = cp_lexer_peek_token (parser->lexer)->location;
+ cp_token *attrs_token = cp_lexer_peek_token (parser->lexer);
attributes = cp_parser_attributes_opt (parser);
if (attributes == error_mark_node)
return error_mark_node;
@@ -15163,14 +15166,14 @@ cp_parser_alias_declaration (cp_parser* parser)
decl_specs.attributes = attributes;
set_and_check_decl_spec_loc (&decl_specs,
ds_attribute,
- attrs_location);
+ attrs_token);
}
set_and_check_decl_spec_loc (&decl_specs,
ds_typedef,
- using_location);
+ using_token);
set_and_check_decl_spec_loc (&decl_specs,
ds_alias,
- using_location);
+ using_token);
declarator = make_id_declarator (NULL_TREE, id, sfk_none);
declarator->id_loc = id_location;
@@ -22054,13 +22057,13 @@ static void
cp_parser_set_storage_class (cp_parser *parser,
cp_decl_specifier_seq *decl_specs,
enum rid keyword,
- location_t location)
+ cp_token *token)
{
cp_storage_class storage_class;
if (parser->in_unbraced_linkage_specification_p)
{
- error_at (location, "invalid use of %qD in linkage specification",
+ error_at (token->location, "invalid use of %qD in linkage specification",
ridpointers[keyword]);
return;
}
@@ -22071,11 +22074,11 @@ cp_parser_set_storage_class (cp_parser *parser,
}
if ((keyword == RID_EXTERN || keyword == RID_STATIC)
- && decl_spec_seq_has_spec_p (decl_specs, ds_thread))
+ && decl_spec_seq_has_spec_p (decl_specs, ds_thread)
+ && decl_specs->gnu_thread_keyword_p)
{
- error_at (decl_specs->locations[ds_thread],
+ pedwarn (decl_specs->locations[ds_thread], 0,
"%<__thread%> before %qD", ridpointers[keyword]);
- decl_specs->locations[ds_thread] = 0;
}
switch (keyword)
@@ -22099,7 +22102,7 @@ cp_parser_set_storage_class (cp_parser *parser,
gcc_unreachable ();
}
decl_specs->storage_class = storage_class;
- set_and_check_decl_spec_loc (decl_specs, ds_storage_class, location);
+ set_and_check_decl_spec_loc (decl_specs, ds_storage_class, token);
/* A storage class specifier cannot be applied alongside a typedef
specifier. If there is a typedef specifier present then set
@@ -22115,7 +22118,7 @@ cp_parser_set_storage_class (cp_parser *parser,
static void
cp_parser_set_decl_spec_type (cp_decl_specifier_seq *decl_specs,
tree type_spec,
- location_t location,
+ cp_token *token,
bool type_definition_p)
{
decl_specs->any_specifiers_p = true;
@@ -22140,12 +22143,12 @@ cp_parser_set_decl_spec_type (cp_decl_specifier_seq *decl_specs,
decl_specs->redefined_builtin_type = type_spec;
set_and_check_decl_spec_loc (decl_specs,
ds_redefined_builtin_type_spec,
- location);
+ token);
if (!decl_specs->type)
{
decl_specs->type = type_spec;
decl_specs->type_definition_p = false;
- set_and_check_decl_spec_loc (decl_specs,ds_type_spec, location);
+ set_and_check_decl_spec_loc (decl_specs,ds_type_spec, token);
}
}
else if (decl_specs->type)
@@ -22155,10 +22158,19 @@ cp_parser_set_decl_spec_type (cp_decl_specifier_seq *decl_specs,
decl_specs->type = type_spec;
decl_specs->type_definition_p = type_definition_p;
decl_specs->redefined_builtin_type = NULL_TREE;
- set_and_check_decl_spec_loc (decl_specs, ds_type_spec, location);
+ set_and_check_decl_spec_loc (decl_specs, ds_type_spec, token);
}
}
+/* True iff TOKEN is the GNU keyword __thread. */
+
+static bool
+token_is__thread (cp_token *token)
+{
+ gcc_assert (token->keyword == RID_THREAD);
+ return !strcmp (IDENTIFIER_POINTER (token->u.value), "__thread");
+}
+
/* Set the location for a declarator specifier and check if it is
duplicated.
@@ -22173,15 +22185,21 @@ cp_parser_set_decl_spec_type (cp_decl_specifier_seq *decl_specs,
static void
set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
- cp_decl_spec ds, source_location location)
+ cp_decl_spec ds, cp_token *token)
{
gcc_assert (ds < ds_last);
if (decl_specs == NULL)
return;
+ source_location location = token->location;
+
if (decl_specs->locations[ds] == 0)
- decl_specs->locations[ds] = location;
+ {
+ decl_specs->locations[ds] = location;
+ if (ds == ds_thread)
+ decl_specs->gnu_thread_keyword_p = token_is__thread (token);
+ }
else
{
if (ds == ds_long)
@@ -22197,6 +22215,15 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
"ISO C++ 1998 does not support %<long long%>");
}
}
+ else if (ds == ds_thread)
+ {
+ bool gnu = token_is__thread (token);
+ if (gnu != decl_specs->gnu_thread_keyword_p)
+ error_at (location,
+ "both %<__thread%> and %<thread_local%> specified");
+ else
+ error_at (location, "duplicate %qD", token->u.value);
+ }
else
{
static const char *const decl_spec_names[] = {
@@ -22214,8 +22241,7 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
"typedef",
"using",
"constexpr",
- "__complex",
- "__thread"
+ "__complex"
};
error_at (location,
"duplicate %qs", decl_spec_names[ds]);
@@ -24056,7 +24082,7 @@ cp_parser_objc_class_ivars (cp_parser* parser)
declspecs.storage_class = sc_none;
}
- /* __thread. */
+ /* thread_local. */
if (decl_spec_seq_has_spec_p (&declspecs, ds_thread))
{
cp_parser_error (parser, "invalid type for instance variable");
@@ -24635,7 +24661,7 @@ cp_parser_objc_struct_declaration (cp_parser *parser)
declspecs.storage_class = sc_none;
}
- /* __thread. */
+ /* thread_local. */
if (decl_spec_seq_has_spec_p (&declspecs, ds_thread))
{
cp_parser_error (parser, "invalid type for property");
@@ -2,13 +2,13 @@
/* { dg-require-effective-target tls } */
extern __thread int i;
-__thread int *p = &i; /* { dg-error "dynamically initialized" } */
+__thread int *p = &i; /* { dg-error "dynamic initialization" } */
extern int f();
-__thread int j = f(); /* { dg-error "dynamically initialized" } */
+__thread int j = f(); /* { dg-error "dynamic initialization" } */
struct S
{
S();
};
-__thread S s; /* { dg-error "" } two errors here */
+__thread S s; /* { dg-error "dynamic initialization" } */
new file mode 100644
@@ -0,0 +1,21 @@
+// { 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;
+}
new file mode 100644
@@ -0,0 +1,27 @@
+// { 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 ();
+}
new file mode 100644
@@ -0,0 +1,10 @@
+// { 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;
+}
commit 18c01be0ec8b7a3cda6a16e86356e8e434c12f89
Author: Jason Merrill <jason@redhat.com>
Date: Thu Sep 20 16:00:08 2012 -0400
Support C++11 thread_local destructors.
gcc/cp/
* decl.c (get_thread_atexit_node): New.
(register_dtor_fn): Use it for TLS.
libstdc++-v3/
* libsupc++/cxxabi.h: Declare __cxa_thread_atexit.
* libsupc++/atexit_thread.cc: New.
* libsupc++/Makefile.am (nested_exception.lo): Add it.
* config/abi/pre/gnu.ver: Add __cxa_thread_atexit.
@@ -6542,6 +6542,24 @@ get_atexit_node (void)
return atexit_node;
}
+/* Like get_atexit_node, but for thread-local cleanups. */
+
+static tree
+get_thread_atexit_node (void)
+{
+ /* The declaration for `__cxa_thread_atexit' is:
+
+ int __cxa_atexit (void (*)(void *), void *) */
+ tree fn_type = build_function_type_list (integer_type_node,
+ get_atexit_fn_ptr_type (),
+ ptr_type_node, ptr_type_node,
+ NULL_TREE);
+
+ /* Now, build the function declaration. */
+ tree atexit_fndecl = build_library_fn_ptr ("__cxa_thread_atexit", fn_type);
+ return decay_conversion (atexit_fndecl, tf_warning_or_error);
+}
+
/* Returns the __dso_handle VAR_DECL. */
static tree
@@ -6633,23 +6651,27 @@ tree
register_dtor_fn (tree decl)
{
tree cleanup;
+ tree addr;
tree compound_stmt;
tree fcall;
tree type;
- bool use_dtor;
- tree arg0, arg1 = NULL_TREE, arg2 = NULL_TREE;
+ bool ob_parm, dso_parm, use_dtor;
+ tree arg0, arg1, arg2;
+ tree atex_node;
type = TREE_TYPE (decl);
if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
return void_zero_node;
- /* If we're using "__cxa_atexit" (or "__aeabi_atexit"), and DECL is
- a class object, we can just pass the destructor to
- "__cxa_atexit"; we don't have to build a temporary function to do
- the cleanup. */
- use_dtor = (flag_use_cxa_atexit
- && !targetm.cxx.use_atexit_for_cxa_atexit ()
- && CLASS_TYPE_P (type));
+ /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
+ "__aeabi_atexit"), and DECL is a class object, we can just pass the
+ destructor to "__cxa_atexit"; we don't have to build a temporary
+ function to do the cleanup. */
+ ob_parm = (DECL_THREAD_LOCAL_P (decl)
+ || (flag_use_cxa_atexit
+ && !targetm.cxx.use_atexit_for_cxa_atexit ()));
+ dso_parm = ob_parm;
+ use_dtor = ob_parm && CLASS_TYPE_P (type);
if (use_dtor)
{
int idx;
@@ -6687,44 +6709,48 @@ register_dtor_fn (tree decl)
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. */
mark_used (cleanup);
cleanup = build_address (cleanup);
- if (flag_use_cxa_atexit && !targetm.cxx.use_atexit_for_cxa_atexit ())
+
+ if (DECL_THREAD_LOCAL_P (decl))
+ atex_node = get_thread_atexit_node ();
+ else
+ atex_node = get_atexit_node ();
+
+ if (use_dtor)
{
- tree addr;
+ /* We must convert CLEANUP to the type that "__cxa_atexit"
+ expects. */
+ cleanup = build_nop (get_atexit_fn_ptr_type (), cleanup);
+ /* "__cxa_atexit" will pass the address of DECL to the
+ cleanup function. */
+ mark_used (decl);
+ addr = build_address (decl);
+ /* The declared type of the parameter to "__cxa_atexit" is
+ "void *". For plain "T*", we could just let the
+ machinery in cp_build_function_call convert it -- but if the
+ type is "cv-qualified T *", then we need to convert it
+ before passing it in, to avoid spurious errors. */
+ addr = build_nop (ptr_type_node, addr);
+ }
+ else if (ob_parm)
+ /* Since the cleanup functions we build ignore the address
+ they're given, there's no reason to pass the actual address
+ in, and, in general, it's cheaper to pass NULL than any
+ other value. */
+ addr = null_pointer_node;
+
+ if (dso_parm)
+ arg2 = cp_build_addr_expr (get_dso_handle_node (),
+ tf_warning_or_error);
+ else
+ arg2 = NULL_TREE;
- if (use_dtor)
- {
- /* We must convert CLEANUP to the type that "__cxa_atexit"
- expects. */
- cleanup = build_nop (get_atexit_fn_ptr_type (), cleanup);
- /* "__cxa_atexit" will pass the address of DECL to the
- cleanup function. */
- mark_used (decl);
- addr = build_address (decl);
- /* The declared type of the parameter to "__cxa_atexit" is
- "void *". For plain "T*", we could just let the
- machinery in cp_build_function_call convert it -- but if the
- type is "cv-qualified T *", then we need to convert it
- before passing it in, to avoid spurious errors. */
- addr = build_nop (ptr_type_node, addr);
- }
- else
- /* Since the cleanup functions we build ignore the address
- they're given, there's no reason to pass the actual address
- in, and, in general, it's cheaper to pass NULL than any
- other value. */
- addr = null_pointer_node;
- arg2 = cp_build_addr_expr (get_dso_handle_node (),
- tf_warning_or_error);
- if (targetm.cxx.use_aeabi_atexit ())
+ if (ob_parm)
+ {
+ if (!DECL_THREAD_LOCAL_P (decl)
+ && targetm.cxx.use_aeabi_atexit ())
{
arg1 = cleanup;
arg0 = addr;
@@ -6736,8 +6762,11 @@ register_dtor_fn (tree decl)
}
}
else
- arg0 = cleanup;
- return cp_build_function_call_nary (get_atexit_node (), tf_warning_or_error,
+ {
+ arg0 = cleanup;
+ arg1 = NULL_TREE;
+ }
+ return cp_build_function_call_nary (atex_node, tf_warning_or_error,
arg0, arg1, arg2, NULL_TREE);
}
new file mode 100644
@@ -0,0 +1,37 @@
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+#include <pthread.h>
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
new file mode 100644
@@ -0,0 +1,47 @@
+// Test for cleanups with pthread_cancel.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+ while (true)
+ {
+ pthread_testcancel();
+ sleep (1);
+ }
+}
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
new file mode 100644
@@ -0,0 +1,47 @@
+// Test for cleanups in the main thread, too.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() {
+ if (++d == 3)
+ _exit (0);
+ }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+int main()
+{
+ pthread_t thread;
+ thread_main(0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ if (c != 3 || d != 2)
+ __builtin_abort();
+ return 1;
+}
new file mode 100644
@@ -0,0 +1,33 @@
+// Test for cleanups in the main thread without -pthread.
+
+// { dg-do run }
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls_runtime }
+
+extern "C" void _exit (int);
+
+int c;
+struct A
+{
+ A() { ++c; }
+ ~A() { if (c == 1) _exit(0); }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+int main()
+{
+ thread_main(0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ return 1;
+}
@@ -1531,6 +1531,10 @@ CXXABI_1.3.6 {
} CXXABI_1.3.5;
+CXXABI_1.3.7 {
+ __cxa_thread_atexit;
+} CXXABI_1.3.6;
+
# Symbols in the support library (libsupc++) supporting transactional memory.
CXXABI_TM_1 {
@@ -48,6 +48,7 @@ endif
sources = \
array_type_info.cc \
atexit_arm.cc \
+ atexit_thread.cc \
bad_alloc.cc \
bad_cast.cc \
bad_typeid.cc \
@@ -123,6 +124,11 @@ guard.lo: guard.cc
guard.o: guard.cc
$(CXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.lo: atexit_thread.cc
+ $(LTCXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.o: atexit_thread.cc
+ $(CXXCOMPILE) -std=gnu++0x -c $<
+
nested_exception.lo: nested_exception.cc
$(LTCXXCOMPILE) -std=gnu++0x -c $<
nested_exception.o: nested_exception.cc
@@ -90,7 +90,7 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(bitsdir)" \
"$(DESTDIR)$(stddir)"
LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES)
libsupc___la_LIBADD =
-am__objects_1 = array_type_info.lo atexit_arm.lo bad_alloc.lo \
+am__objects_1 = array_type_info.lo atexit_arm.lo atexit_thread.lo bad_alloc.lo \
bad_cast.lo bad_typeid.lo class_type_info.lo del_op.lo \
del_opnt.lo del_opv.lo del_opvnt.lo dyncast.lo eh_alloc.lo \
eh_arm.lo eh_aux_runtime.lo eh_call.lo eh_catch.lo \
@@ -362,6 +362,7 @@ headers = $(std_HEADERS) $(bits_HEADERS)
sources = \
array_type_info.cc \
atexit_arm.cc \
+ atexit_thread.cc \
bad_alloc.cc \
bad_cast.cc \
bad_typeid.cc \
@@ -800,6 +801,11 @@ guard.lo: guard.cc
guard.o: guard.cc
$(CXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.lo: atexit_thread.cc
+ $(LTCXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.o: atexit_thread.cc
+ $(CXXCOMPILE) -std=gnu++0x -c $<
+
nested_exception.lo: nested_exception.cc
$(LTCXXCOMPILE) -std=gnu++0x -c $<
nested_exception.o: nested_exception.cc
new file mode 100644
@@ -0,0 +1,134 @@
+// Copyright (C) 2012 Free Software Foundation, Inc.
+//
+// This file is part of GCC.
+//
+// GCC is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// GCC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <cxxabi.h>
+#include <new>
+#include "bits/gthr.h"
+
+namespace {
+ // Data structure for the list of destructors: Singly-linked list
+ // of arrays.
+ class list
+ {
+ struct elt
+ {
+ void *object;
+ void (*destructor)(void *);
+ };
+
+ static const int max_nelts = 32;
+
+ list *next;
+ int nelts;
+ elt array[max_nelts];
+
+ elt *allocate_elt();
+ public:
+ void run();
+ static void run(void *p);
+ int add_elt(void (*)(void *), void *);
+ };
+
+ // Return the address of an open slot.
+ list::elt *
+ list::allocate_elt()
+ {
+ if (nelts < max_nelts)
+ return &array[nelts++];
+ if (!next)
+ next = new (std::nothrow) list();
+ if (!next)
+ return 0;
+ return next->allocate_elt();
+ }
+
+ // Run all the cleanups in the list.
+ void
+ list::run()
+ {
+ for (int i = nelts - 1; i >= 0; --i)
+ array[i].destructor (array[i].object);
+ if (next)
+ next->run();
+ }
+
+ // Static version to use as a callback to __gthread_key_create.
+ void
+ list::run(void *p)
+ {
+ static_cast<list *>(p)->run();
+ }
+
+ // The list of cleanups is per-thread.
+ thread_local list first;
+
+ // The pthread data structures for actually running the destructors at
+ // thread exit are shared. The constructor of the thread-local sentinel
+ // object in add_elt performs the initialization.
+ __gthread_key_t key;
+ __gthread_once_t once = __GTHREAD_ONCE_INIT;
+ void run_current (void *) { first.run(); }
+ void key_init() {
+ __gthread_key_create (&key, list::run);
+ // Also make sure the destructors are run by std::exit.
+ // FIXME TLS cleanups should run before static cleanups and atexit
+ // cleanups.
+ abi::__cxa_atexit (run_current, NULL, NULL);
+ }
+ struct sentinel
+ {
+ sentinel()
+ {
+ if (__gthread_active_p ())
+ {
+ __gthread_once (&once, key_init);
+ __gthread_setspecific (key, &first);
+ }
+ else
+ abi::__cxa_atexit (run_current, NULL, NULL);
+ }
+ };
+
+ // Actually insert an element.
+ int
+ list::add_elt(void (*dtor)(void *), void *obj)
+ {
+ thread_local sentinel s;
+ elt *e = allocate_elt ();
+ if (!e)
+ return -1;
+ e->object = obj;
+ e->destructor = dtor;
+ return 0;
+ }
+}
+
+namespace __cxxabiv1
+{
+ extern "C" int
+ __cxa_thread_atexit (void (*dtor)(void *), void *obj, void *dso_handle)
+ _GLIBCXX_NOTHROW
+ {
+ return first.add_elt (dtor, obj);
+ }
+}
@@ -134,6 +134,10 @@ namespace __cxxabiv1
int
__cxa_finalize(void*);
+ // TLS destruction.
+ int
+ __cxa_thread_atexit(void (*)(void*), void*, void *) _GLIBCXX_NOTHROW;
+
// Pure virtual functions.
void
__cxa_pure_virtual(void) __attribute__ ((__noreturn__));
commit c20f7291032d68f0afe249f1d1a67707acab720d
Author: Jason Merrill <jason@redhat.com>
Date: Thu Sep 27 06:15:22 2012 -0400
Allow dynamic initialization of thread_locals.
gcc/cp/
* decl.c: Define tls_aggregates.
(expand_static_init): Remove sorry. Add to tls_aggregates.
* cp-tree.h: Declare tls_aggregates.
* call.c (set_up_extended_ref_temp): Add to tls_aggregates.
* decl2.c (var_needs_tls_wrapper): New.
(var_defined_without_dynamic_init): New.
(get_tls_init_fn, get_tls_wrapper_fn): New.
(generate_tls_wrapper, handle_tls_init): New.
(cp_write_global_declarations): Call handle_tls_init and
enerate_tls_wrapper.
* mangle.c (write_guarded_var_name): Split out from..
(mangle_guard_variable): ...here.
(mangle_tls_init_fn, mangle_tls_wrapper_fn): Use it.
(decl_tls_wrapper_p): New.
* semantics.c (finish_id_expression): Replace use of thread_local
variable with a call to its wrapper.
libiberty/
* cp-demangle.c (d_special_name, d_dump): Handle TH and TW.
(d_make_comp, d_print_comp): Likewise.
include/
* demangle.h (enum demangle_component_type): Add
DEMANGLE_COMPONENT_TLS_INIT and DEMANGLE_COMPONENT_TLS_WRAPPER.
@@ -8859,8 +8859,14 @@ set_up_extended_ref_temp (tree decl, tree expr, VEC(tree,gc) **cleanups,
{
rest_of_decl_compilation (var, /*toplev=*/1, at_eof);
if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
- static_aggregates = tree_cons (NULL_TREE, var,
- static_aggregates);
+ {
+ if (DECL_THREAD_LOCAL_P (var))
+ tls_aggregates = tree_cons (NULL_TREE, var,
+ tls_aggregates);
+ else
+ static_aggregates = tree_cons (NULL_TREE, var,
+ static_aggregates);
+ }
}
*initp = init;
@@ -4378,6 +4378,8 @@ extern int at_eof;
in the TREE_VALUE slot and the initializer is stored in the
TREE_PURPOSE slot. */
extern GTY(()) tree static_aggregates;
+/* Likewise, for thread local storage. */
+extern GTY(()) tree tls_aggregates;
enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
@@ -5179,6 +5181,7 @@ extern tree cp_build_parm_decl (tree, tree);
extern tree get_guard (tree);
extern tree get_guard_cond (tree);
extern tree set_guard (tree);
+extern tree get_tls_wrapper_fn (tree);
extern void mark_needed (tree);
extern bool decl_needed_p (tree);
extern void note_vague_linkage_fn (tree);
@@ -5973,6 +5976,9 @@ extern tree mangle_ctor_vtbl_for_type (tree, tree);
extern tree mangle_thunk (tree, int, tree, tree);
extern tree mangle_conv_op_name_for_type (tree);
extern tree mangle_guard_variable (tree);
+extern tree mangle_tls_init_fn (tree);
+extern tree mangle_tls_wrapper_fn (tree);
+extern bool decl_tls_wrapper_p (tree);
extern tree mangle_ref_init_variable (tree);
/* in dump.c */
@@ -169,6 +169,9 @@ tree global_scope_name;
in the TREE_PURPOSE slot. */
tree static_aggregates;
+/* Like static_aggregates, but for thread_local variables. */
+tree tls_aggregates;
+
/* -- end of C++ */
/* A node for the integer constant 2. */
@@ -6805,16 +6808,6 @@ expand_static_init (tree decl, tree init)
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))
{
/* Emit code to perform this initialization but once. */
@@ -6943,6 +6936,8 @@ expand_static_init (tree decl, tree init)
finish_if_stmt (if_stmt);
}
}
+ else if (DECL_THREAD_LOCAL_P (decl))
+ tls_aggregates = tree_cons (init, decl, tls_aggregates);
else
static_aggregates = tree_cons (init, decl, static_aggregates);
}
@@ -2781,6 +2781,187 @@ set_guard (tree guard)
tf_warning_or_error);
}
+/* Returns true iff we can tell that VAR does not have a dynamic
+ initializer. */
+
+static bool
+var_defined_without_dynamic_init (tree var)
+{
+ /* If it's defined in another TU, we can't tell. */
+ if (DECL_EXTERNAL (var))
+ return false;
+ /* If it has a non-trivial destructor, registering the destructor
+ counts as dynamic initialization. */
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var)))
+ return false;
+ /* If it's in this TU, its initializer has been processed. */
+ gcc_assert (DECL_INITIALIZED_P (var));
+ /* If it has no initializer or a constant one, it's not dynamic. */
+ return (!DECL_NONTRIVIALLY_INITIALIZED_P (var)
+ || DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var));
+}
+
+/* Returns true iff VAR is a variable that needs uses to be
+ wrapped for possible dynamic initialization. */
+
+static bool
+var_needs_tls_wrapper (tree var)
+{
+ return (DECL_THREAD_LOCAL_P (var)
+ && !DECL_GNU_TLS_P (var)
+ && !DECL_FUNCTION_SCOPE_P (var)
+ && !var_defined_without_dynamic_init (var));
+}
+
+/* Get a FUNCTION_DECL for the init function for the thread_local
+ variable VAR. The init function will be an alias to the function
+ that initializes all the non-local TLS variables in the translation
+ unit. The init function is only used by the wrapper function. */
+
+static tree
+get_tls_init_fn (tree var)
+{
+ /* Only C++11 TLS vars need this init fn. */
+ if (!var_needs_tls_wrapper (var))
+ return NULL_TREE;
+
+ tree sname = mangle_tls_init_fn (var);
+ tree fn = IDENTIFIER_GLOBAL_VALUE (sname);
+ if (!fn)
+ {
+ fn = build_lang_decl (FUNCTION_DECL, sname,
+ build_function_type (void_type_node,
+ void_list_node));
+ SET_DECL_LANGUAGE (fn, lang_c);
+ TREE_PUBLIC (fn) = TREE_PUBLIC (var);
+ DECL_ARTIFICIAL (fn) = true;
+ DECL_COMDAT (fn) = DECL_COMDAT (var);
+ DECL_EXTERNAL (fn) = true;
+ if (DECL_ONE_ONLY (var))
+ make_decl_one_only (fn, cxx_comdat_group (fn));
+ if (TREE_PUBLIC (var))
+ {
+ tree obtype = strip_array_types (non_reference (TREE_TYPE (var)));
+ /* If the variable might have static initialization, make the
+ init function a weak reference. */
+ if ((!TYPE_NEEDS_CONSTRUCTING (obtype)
+ || TYPE_HAS_CONSTEXPR_CTOR (obtype))
+ && TARGET_SUPPORTS_WEAK)
+ declare_weak (fn);
+ else
+ DECL_WEAK (fn) = DECL_WEAK (var);
+ }
+ DECL_VISIBILITY (fn) = DECL_VISIBILITY (var);
+ DECL_VISIBILITY_SPECIFIED (fn) = DECL_VISIBILITY_SPECIFIED (var);
+ DECL_DLLIMPORT_P (fn) = DECL_DLLIMPORT_P (var);
+ DECL_IGNORED_P (fn) = 1;
+ mark_used (fn);
+
+ DECL_BEFRIENDING_CLASSES (fn) = var;
+
+ SET_IDENTIFIER_GLOBAL_VALUE (sname, fn);
+ }
+ return fn;
+}
+
+/* Get a FUNCTION_DECL for the init wrapper function for the thread_local
+ variable VAR. The wrapper function calls the init function (if any) for
+ VAR and then returns a reference to VAR. The wrapper function is used
+ in place of VAR everywhere VAR is mentioned. */
+
+tree
+get_tls_wrapper_fn (tree var)
+{
+ /* Only C++11 TLS vars need this wrapper fn. */
+ if (!var_needs_tls_wrapper (var))
+ return NULL_TREE;
+
+ tree sname = mangle_tls_wrapper_fn (var);
+ tree fn = IDENTIFIER_GLOBAL_VALUE (sname);
+ if (!fn)
+ {
+ /* A named rvalue reference is an lvalue, so the wrapper should
+ always return an lvalue reference. */
+ tree type = non_reference (TREE_TYPE (var));
+ type = build_reference_type (type);
+ tree fntype = build_function_type (type, void_list_node);
+ fn = build_lang_decl (FUNCTION_DECL, sname, fntype);
+ SET_DECL_LANGUAGE (fn, lang_c);
+ TREE_PUBLIC (fn) = TREE_PUBLIC (var);
+ DECL_ARTIFICIAL (fn) = true;
+ DECL_IGNORED_P (fn) = 1;
+ /* The wrapper is inline and emitted everywhere var is used. */
+ DECL_DECLARED_INLINE_P (fn) = true;
+ if (TREE_PUBLIC (var))
+ {
+ comdat_linkage (fn);
+#ifdef HAVE_GAS_HIDDEN
+ /* Make the wrapper bind locally; there's no reason to share
+ the wrapper between multiple shared objects. */
+ DECL_VISIBILITY (fn) = VISIBILITY_INTERNAL;
+ DECL_VISIBILITY_SPECIFIED (fn) = true;
+#endif
+ }
+ if (!TREE_PUBLIC (fn))
+ DECL_INTERFACE_KNOWN (fn) = true;
+ mark_used (fn);
+ note_vague_linkage_fn (fn);
+
+#if 0
+ /* We want CSE to commonize calls to the wrapper, but marking it as
+ pure is unsafe since it has side-effects. I guess we need a new
+ ECF flag even weaker than ECF_PURE. FIXME! */
+ DECL_PURE_P (fn) = true;
+#endif
+
+ DECL_BEFRIENDING_CLASSES (fn) = var;
+
+ SET_IDENTIFIER_GLOBAL_VALUE (sname, fn);
+ }
+ return fn;
+}
+
+/* At EOF, generate the definition for the TLS wrapper function FN:
+
+ T& var_wrapper() {
+ if (init_fn) init_fn();
+ return var;
+ } */
+
+static void
+generate_tls_wrapper (tree fn)
+{
+ tree var = DECL_BEFRIENDING_CLASSES (fn);
+
+ start_preparsed_function (fn, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
+ tree body = begin_function_body ();
+ /* Only call the init fn if there might be one. */
+ if (tree init_fn = get_tls_init_fn (var))
+ {
+ tree if_stmt = NULL_TREE;
+ /* If init_fn is a weakref, make sure it exists before calling. */
+ if (lookup_attribute ("weak", DECL_ATTRIBUTES (init_fn)))
+ {
+ if_stmt = begin_if_stmt ();
+ tree addr = cp_build_addr_expr (init_fn, tf_warning_or_error);
+ tree cond = cp_build_binary_op (DECL_SOURCE_LOCATION (var),
+ NE_EXPR, addr, nullptr_node,
+ tf_warning_or_error);
+ finish_if_stmt_cond (cond, if_stmt);
+ }
+ finish_expr_stmt (build_cxx_call
+ (init_fn, 0, NULL, tf_warning_or_error));
+ if (if_stmt)
+ {
+ finish_then_clause (if_stmt);
+ finish_if_stmt (if_stmt);
+ }
+ }
+ finish_return_stmt (convert_from_reference (var));
+ finish_function_body (body);
+ expand_or_defer_fn (finish_function (0));
+}
+
/* Start the process of running a particular set of global constructors
or destructors. Subroutine of do_[cd]tors. */
@@ -3668,6 +3849,75 @@ clear_decl_external (struct cgraph_node *node, void * /*data*/)
return false;
}
+/* Build up the function to run dynamic initializers for thread_local
+ variables in this translation unit and alias the init functions for the
+ individual variables to it. */
+
+static void
+handle_tls_init (void)
+{
+ tree vars = prune_vars_needing_no_initialization (&tls_aggregates);
+ if (vars == NULL_TREE)
+ return;
+
+ location_t loc = DECL_SOURCE_LOCATION (TREE_VALUE (vars));
+
+ #ifndef ASM_OUTPUT_DEF
+ /* This currently requires alias support. FIXME other targets could use
+ small thunks instead of aliases. */
+ input_location = loc;
+ sorry ("dynamic initialization of non-function-local thread_local "
+ "variables not supported on this target");
+ return;
+ #endif
+
+ write_out_vars (vars);
+
+ tree guard = build_decl (loc, VAR_DECL, get_identifier ("__tls_guard"),
+ boolean_type_node);
+ TREE_PUBLIC (guard) = false;
+ TREE_STATIC (guard) = true;
+ DECL_ARTIFICIAL (guard) = true;
+ DECL_IGNORED_P (guard) = true;
+ TREE_USED (guard) = true;
+ DECL_TLS_MODEL (guard) = decl_default_tls_model (guard);
+ pushdecl_top_level_and_finish (guard, NULL_TREE);
+
+ tree fn = build_lang_decl (FUNCTION_DECL,
+ get_identifier ("__tls_init"),
+ build_function_type (void_type_node,
+ void_list_node));
+ SET_DECL_LANGUAGE (fn, lang_c);
+ TREE_PUBLIC (fn) = false;
+ DECL_ARTIFICIAL (fn) = true;
+ mark_used (fn);
+ start_preparsed_function (fn, NULL_TREE, SF_PRE_PARSED);
+ tree body = begin_function_body ();
+ tree if_stmt = begin_if_stmt ();
+ tree cond = cp_build_unary_op (TRUTH_NOT_EXPR, guard, false,
+ tf_warning_or_error);
+ finish_if_stmt_cond (cond, if_stmt);
+ finish_expr_stmt (cp_build_modify_expr (guard, NOP_EXPR, boolean_true_node,
+ tf_warning_or_error));
+ for (; vars; vars = TREE_CHAIN (vars))
+ {
+ tree var = TREE_VALUE (vars);
+ tree init = TREE_PURPOSE (vars);
+ one_static_initialization_or_destruction (var, init, true);
+
+ tree single_init_fn = get_tls_init_fn (var);
+ cgraph_node *alias
+ = cgraph_same_body_alias (cgraph_get_create_node (fn),
+ single_init_fn, fn);
+ gcc_assert (alias != NULL);
+ }
+
+ finish_then_clause (if_stmt);
+ finish_if_stmt (if_stmt);
+ finish_function_body (body);
+ expand_or_defer_fn (finish_function (0));
+}
+
/* This routine is called at the end of compilation.
Its job is to create all the code needed to initialize and
destroy the global aggregates. We do the destruction
@@ -3845,6 +4095,9 @@ cp_write_global_declarations (void)
/* ??? was: locus.line++; */
}
+ /* Now do the same for thread_local variables. */
+ handle_tls_init ();
+
/* Go through the set of inline functions whose bodies have not
been emitted yet. If out-of-line copies of these functions
are required, emit them. */
@@ -3869,6 +4122,9 @@ cp_write_global_declarations (void)
reconsider = true;
}
+ if (!DECL_INITIAL (decl) && decl_tls_wrapper_p (decl))
+ generate_tls_wrapper (decl);
+
if (!DECL_SAVED_TREE (decl))
continue;
@@ -3678,23 +3678,70 @@ mangle_conv_op_name_for_type (const tree type)
return identifier;
}
-/* Return an identifier for the name of an initialization guard
- variable for indicated VARIABLE. */
+/* Write out the appropriate string for this variable when generating
+ another mangled name based on this one. */
-tree
-mangle_guard_variable (const tree variable)
+static void
+write_guarded_var_name (const tree variable)
{
- start_mangling (variable);
- write_string ("_ZGV");
if (strncmp (IDENTIFIER_POINTER (DECL_NAME (variable)), "_ZGR", 4) == 0)
/* The name of a guard variable for a reference temporary should refer
to the reference, not the temporary. */
write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
else
write_name (variable, /*ignore_local_scope=*/0);
+}
+
+/* Return an identifier for the name of an initialization guard
+ variable for indicated VARIABLE. */
+
+tree
+mangle_guard_variable (const tree variable)
+{
+ start_mangling (variable);
+ write_string ("_ZGV");
+ write_guarded_var_name (variable);
+ return finish_mangling_get_identifier (/*warn=*/false);
+}
+
+/* Return an identifier for the name of a thread_local initialization
+ function for VARIABLE. */
+
+tree
+mangle_tls_init_fn (const tree variable)
+{
+ start_mangling (variable);
+ write_string ("_ZTH");
+ write_guarded_var_name (variable);
+ return finish_mangling_get_identifier (/*warn=*/false);
+}
+
+/* Return an identifier for the name of a thread_local wrapper
+ function for VARIABLE. */
+
+#define TLS_WRAPPER_PREFIX "_ZTW"
+
+tree
+mangle_tls_wrapper_fn (const tree variable)
+{
+ start_mangling (variable);
+ write_string (TLS_WRAPPER_PREFIX);
+ write_guarded_var_name (variable);
return finish_mangling_get_identifier (/*warn=*/false);
}
+/* Return true iff FN is a thread_local wrapper function. */
+
+bool
+decl_tls_wrapper_p (const tree fn)
+{
+ if (TREE_CODE (fn) != FUNCTION_DECL)
+ return false;
+ tree name = DECL_NAME (fn);
+ return strncmp (IDENTIFIER_POINTER (name), TLS_WRAPPER_PREFIX,
+ strlen (TLS_WRAPPER_PREFIX)) == 0;
+}
+
/* Return an identifier for the name of a temporary variable used to
initialize a static reference. This isn't part of the ABI, but we might
as well call them something readable. */
@@ -3254,7 +3254,17 @@ finish_id_expression (tree id_expression,
*non_integral_constant_expression_p = true;
}
- if (scope)
+ tree wrap;
+ if (TREE_CODE (decl) == VAR_DECL
+ && !cp_unevaluated_operand
+ && DECL_THREAD_LOCAL_P (decl)
+ && (wrap = get_tls_wrapper_fn (decl)))
+ {
+ /* Replace an evaluated use of the thread_local variable with
+ a call to its wrapper. */
+ decl = build_cxx_call (wrap, 0, NULL, tf_warning_or_error);
+ }
+ else if (scope)
{
decl = (adjust_result_of_qualified_name_lookup
(decl, scope, current_nonlambda_class_type()));
new file mode 100644
@@ -0,0 +1,12 @@
+// The reference temp should be TLS, not normal data.
+// { dg-require-effective-target c++11 }
+// { dg-final { scan-assembler-not "\\.data" } }
+
+extern int&& ir;
+#pragma omp threadprivate (ir)
+int&& ir = 42;
+
+void f()
+{
+ ir = 24;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// If we can see the definition at the use site, we don't need to bother
+// with a wrapper.
+
+// { dg-require-effective-target tls }
+// { dg-final { scan-assembler-not "_ZTW1i" } }
+
+int i = 42;
+#pragma omp threadprivate (i)
+
+int main()
+{
+ return i - 42;
+}
new file mode 100644
@@ -0,0 +1,16 @@
+// If we can't see the definition at the use site, but it's in this translation
+// unit, we build a wrapper but don't bother with an init function.
+
+// { dg-require-effective-target tls }
+// { dg-final { scan-assembler "_ZTW1i" } }
+// { dg-final { scan-assembler-not "_ZTH1i" } }
+
+extern int i;
+#pragma omp threadprivate (i)
+
+int main()
+{
+ return i - 42;
+}
+
+int i = 42;
new file mode 100644
@@ -0,0 +1,14 @@
+// If we can't see the definition at all, we need to assume there might be
+// an init function.
+
+// { dg-require-effective-target tls }
+// { dg-final { scan-assembler "_ZTW1i" } }
+// { dg-final { scan-assembler "_ZTH1i" } }
+
+extern int i;
+#pragma omp threadprivate (i)
+
+int main()
+{
+ return i - 42;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// We don't need to call the wrapper through the PLT; we can use a separate
+// copy per shared object.
+
+// { dg-require-effective-target tls }
+// { dg-options "-std=c++11 -fPIC" }
+// { dg-final { scan-assembler-not "_ZTW1i@PLT" { target i?86-*-* x86_64-*-* } } }
+
+extern thread_local int i;
+
+int main()
+{
+ return i - 42;
+}
new file mode 100644
@@ -0,0 +1,18 @@
+// Test for CSE of the wrapper function: we should only call it once
+// for the two references to ir.
+// { dg-options "-fopenmp -O -fno-inline" }
+// { dg-require-effective-target tls }
+// { dg-final { scan-assembler-times "call *_ZTW2ir" 1 { xfail *-*-* } } }
+
+// XFAILed until the back end supports a way to mark a function as cseable
+// though not pure.
+
+int f() { return 42; }
+
+int ir = f();
+#pragma omp threadprivate (ir)
+
+int main()
+{
+ return ir + ir - 84;
+}
new file mode 100644
@@ -0,0 +1,20 @@
+// Test for CSE of the wrapper function: we should only call it once
+// for the two references to ir.
+// { dg-options "-std=c++11 -O -fno-inline -save-temps" }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-alias }
+// { dg-final { scan-assembler-times "call *_ZTW2ir" 1 { xfail *-*-* } } }
+// { dg-final cleanup-saved-temps }
+// { dg-do run }
+
+// XFAILed until the back end supports a way to mark a function as cseable
+// though not pure.
+
+int f() { return 42; }
+
+thread_local int ir = f();
+
+int main()
+{
+ return ir + ir - 84;
+}
new file mode 100644
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-alias }
+
+extern "C" void abort();
+extern "C" int printf (const char *, ...);
+#define printf(...)
+
+int c;
+struct A {
+ int i;
+ A(int i): i(i) { printf ("A(%d)\n", i); if (i != c++) abort (); }
+ ~A() { printf("~A(%d)\n", i); if (i != --c) abort(); }
+};
+
+A a0(0);
+thread_local A a1(1);
+thread_local A a2(2);
+A* ap = &a1;
+
+int main()
+{
+ if (c != 3) abort();
+}
new file mode 100644
@@ -0,0 +1,28 @@
+// The standard says that a1 should be destroyed before a0 even though
+// that isn't reverse order of construction. We need to move
+// __cxa_thread_atexit into glibc to get this right.
+
+// { dg-do run { xfail *-*-* } }
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-alias }
+
+extern "C" void abort();
+extern "C" int printf (const char *, ...);
+#define printf(...)
+
+int c;
+struct A {
+ int i;
+ A(int i): i(i) { printf ("A(%d)\n", i); ++c; }
+ ~A() { printf("~A(%d)\n", i); if (i != --c) abort(); }
+};
+
+thread_local A a1(1);
+A* ap = &a1;
+A a0(0);
+
+int main()
+{
+ if (c != 2) abort();
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// If we can see the definition at the use site, we don't need to bother
+// with a wrapper.
+
+// { dg-require-effective-target tls }
+// { dg-options "-std=c++11" }
+// { dg-final { scan-assembler-not "_ZTW1i" } }
+
+thread_local int i = 42;
+
+int main()
+{
+ return i - 42;
+}
new file mode 100644
@@ -0,0 +1,16 @@
+// If we can't see the definition at the use site, but it's in this translation
+// unit, we build a wrapper but don't bother with an init function.
+
+// { dg-require-effective-target tls }
+// { dg-options "-std=c++11" }
+// { dg-final { scan-assembler "_ZTW1i" } }
+// { dg-final { scan-assembler-not "_ZTH1i" } }
+
+extern thread_local int i;
+
+int main()
+{
+ return i - 42;
+}
+
+thread_local int i = 42;
new file mode 100644
@@ -0,0 +1,14 @@
+// If we can't see the definition at all, we need to assume there might be
+// an init function.
+
+// { dg-require-effective-target tls }
+// { dg-options "-std=c++11" }
+// { dg-final { scan-assembler "_ZTW1i" } }
+// { dg-final { scan-assembler "_ZTH1i" } }
+
+extern thread_local int i;
+
+int main()
+{
+ return i - 42;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// We don't need to call the wrapper through the PLT; we can use a separate
+// copy per shared object.
+
+// { dg-require-effective-target tls }
+// { dg-options "-std=c++11 -fPIC" }
+// { dg-final { scan-assembler-not "_ZTW1i@PLT" { target i?86-*-* x86_64-*-* } } }
+
+extern thread_local int i;
+
+int main()
+{
+ return i - 42;
+}
new file mode 100644
@@ -0,0 +1,29 @@
+// { dg-do run }
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-alias }
+
+extern "C" void abort();
+
+struct A
+{
+ A();
+ int i;
+};
+
+thread_local A a;
+
+A &f()
+{
+ return a;
+}
+
+int j;
+A::A(): i(j) { }
+
+int main()
+{
+ j = 42;
+ if (f().i != 42)
+ abort ();
+}
new file mode 100644
@@ -0,0 +1,35 @@
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-require-alias }
+// { dg-options -pthread }
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+thread_local A a;
+
+void *thread_main(void *)
+{
+ A* ap = &a;
+}
+
+#include <pthread.h>
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
new file mode 100644
@@ -0,0 +1,45 @@
+// Test for cleanups with pthread_cancel.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-require-alias }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+thread_local A a;
+
+void *thread_main(void *)
+{
+ A *ap = &a;
+ while (true)
+ {
+ pthread_testcancel();
+ sleep (1);
+ }
+}
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
new file mode 100644
@@ -0,0 +1,45 @@
+// Test for cleanups in the main thread, too.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-require-alias }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() {
+ if (++d == 3)
+ _exit (0);
+ }
+};
+
+thread_local A a;
+
+void *thread_main(void *)
+{
+ A* ap = &a;
+}
+
+int main()
+{
+ pthread_t thread;
+ thread_main(0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ if (c != 3 || d != 2)
+ __builtin_abort();
+ return 1;
+}
new file mode 100644
@@ -0,0 +1,31 @@
+// Test for cleanups in the main thread without -pthread.
+
+// { dg-do run }
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-alias }
+
+extern "C" void _exit (int);
+
+int c;
+struct A
+{
+ A() { ++c; }
+ ~A() { if (c == 1) _exit(0); }
+};
+
+thread_local A a;
+
+void *thread_main(void *)
+{
+ A* ap = &a;
+}
+
+int main()
+{
+ thread_main(0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ return 1;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls }
+// { dg-require-alias }
+
+// The reference temp should be TLS, not normal data.
+// { dg-final { scan-assembler-not "\\.data" } }
+
+thread_local int&& ir = 42;
+
+void f()
+{
+ ir = 24;
+}
@@ -272,6 +272,9 @@ enum demangle_component_type
/* A guard variable. This has one subtree, the name for which this
is a guard variable. */
DEMANGLE_COMPONENT_GUARD,
+ /* The init and wrapper functions for C++11 thread_local variables. */
+ DEMANGLE_COMPONENT_TLS_INIT,
+ DEMANGLE_COMPONENT_TLS_WRAPPER,
/* A reference temporary. This has one subtree, the name for which
this is a temporary. */
DEMANGLE_COMPONENT_REFTEMP,
new file mode 100644
@@ -0,0 +1,26 @@
+extern "C" void abort();
+
+struct A
+{
+ A();
+ int i;
+};
+
+extern A a;
+#pragma omp threadprivate (a)
+A a;
+
+A &f()
+{
+ return a;
+}
+
+int j;
+A::A(): i(j) { }
+
+int main()
+{
+ j = 42;
+ if (f().i != 42)
+ abort ();
+}
@@ -696,6 +696,12 @@ d_dump (struct demangle_component *dc, int indent)
case DEMANGLE_COMPONENT_PACK_EXPANSION:
printf ("pack expansion\n");
break;
+ case DEMANGLE_COMPONENT_TLS_INIT:
+ printf ("tls init function\n");
+ break;
+ case DEMANGLE_COMPONENT_TLS_WRAPPER:
+ printf ("tls wrapper function\n");
+ break;
}
d_dump (d_left (dc), indent + 2);
@@ -832,6 +838,8 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
case DEMANGLE_COMPONENT_COVARIANT_THUNK:
case DEMANGLE_COMPONENT_JAVA_CLASS:
case DEMANGLE_COMPONENT_GUARD:
+ case DEMANGLE_COMPONENT_TLS_INIT:
+ case DEMANGLE_COMPONENT_TLS_WRAPPER:
case DEMANGLE_COMPONENT_REFTEMP:
case DEMANGLE_COMPONENT_HIDDEN_ALIAS:
case DEMANGLE_COMPONENT_TRANSACTION_CLONE:
@@ -1867,6 +1875,14 @@ d_special_name (struct d_info *di)
return d_make_comp (di, DEMANGLE_COMPONENT_JAVA_CLASS,
cplus_demangle_type (di), NULL);
+ case 'H':
+ return d_make_comp (di, DEMANGLE_COMPONENT_TLS_INIT,
+ d_name (di), NULL);
+
+ case 'W':
+ return d_make_comp (di, DEMANGLE_COMPONENT_TLS_WRAPPER,
+ d_name (di), NULL);
+
default:
return NULL;
}
@@ -4072,6 +4088,16 @@ d_print_comp (struct d_print_info *dpi, int options,
d_print_comp (dpi, options, d_left (dc));
return;
+ case DEMANGLE_COMPONENT_TLS_INIT:
+ d_append_string (dpi, "TLS init function for ");
+ d_print_comp (dpi, options, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_TLS_WRAPPER:
+ d_append_string (dpi, "TLS wrapper function for ");
+ d_print_comp (dpi, options, d_left (dc));
+ return;
+
case DEMANGLE_COMPONENT_REFTEMP:
d_append_string (dpi, "reference temporary #");
d_print_comp (dpi, options, d_right (dc));