mirror of git://gcc.gnu.org/git/gcc.git
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. From-SVN: r192211
This commit is contained in:
parent
5b031b9b56
commit
7c424acd9a
|
|
@ -1,5 +1,23 @@
|
||||||
2012-10-08 Jason Merrill <jason@redhat.com>
|
2012-10-08 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
Allow dynamic initialization of thread_locals.
|
||||||
|
* 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.
|
||||||
|
|
||||||
* decl.c (get_thread_atexit_node): New.
|
* decl.c (get_thread_atexit_node): New.
|
||||||
(register_dtor_fn): Use it for TLS.
|
(register_dtor_fn): Use it for TLS.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8860,8 +8860,14 @@ set_up_extended_ref_temp (tree decl, tree expr, VEC(tree,gc) **cleanups,
|
||||||
{
|
{
|
||||||
rest_of_decl_compilation (var, /*toplev=*/1, at_eof);
|
rest_of_decl_compilation (var, /*toplev=*/1, at_eof);
|
||||||
if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
|
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;
|
*initp = init;
|
||||||
|
|
|
||||||
|
|
@ -4385,6 +4385,8 @@ extern int at_eof;
|
||||||
in the TREE_VALUE slot and the initializer is stored in the
|
in the TREE_VALUE slot and the initializer is stored in the
|
||||||
TREE_PURPOSE slot. */
|
TREE_PURPOSE slot. */
|
||||||
extern GTY(()) tree static_aggregates;
|
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 };
|
enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
|
||||||
|
|
||||||
|
|
@ -5197,6 +5199,7 @@ extern tree cp_build_parm_decl (tree, tree);
|
||||||
extern tree get_guard (tree);
|
extern tree get_guard (tree);
|
||||||
extern tree get_guard_cond (tree);
|
extern tree get_guard_cond (tree);
|
||||||
extern tree set_guard (tree);
|
extern tree set_guard (tree);
|
||||||
|
extern tree get_tls_wrapper_fn (tree);
|
||||||
extern void mark_needed (tree);
|
extern void mark_needed (tree);
|
||||||
extern bool decl_needed_p (tree);
|
extern bool decl_needed_p (tree);
|
||||||
extern void note_vague_linkage_fn (tree);
|
extern void note_vague_linkage_fn (tree);
|
||||||
|
|
@ -5992,6 +5995,9 @@ extern tree mangle_ctor_vtbl_for_type (tree, tree);
|
||||||
extern tree mangle_thunk (tree, int, tree, tree);
|
extern tree mangle_thunk (tree, int, tree, tree);
|
||||||
extern tree mangle_conv_op_name_for_type (tree);
|
extern tree mangle_conv_op_name_for_type (tree);
|
||||||
extern tree mangle_guard_variable (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);
|
extern tree mangle_ref_init_variable (tree);
|
||||||
|
|
||||||
/* in dump.c */
|
/* in dump.c */
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,9 @@ tree global_scope_name;
|
||||||
in the TREE_PURPOSE slot. */
|
in the TREE_PURPOSE slot. */
|
||||||
tree static_aggregates;
|
tree static_aggregates;
|
||||||
|
|
||||||
|
/* Like static_aggregates, but for thread_local variables. */
|
||||||
|
tree tls_aggregates;
|
||||||
|
|
||||||
/* -- end of C++ */
|
/* -- end of C++ */
|
||||||
|
|
||||||
/* A node for the integer constant 2. */
|
/* A node for the integer constant 2. */
|
||||||
|
|
@ -6838,16 +6841,6 @@ expand_static_init (tree decl, tree init)
|
||||||
return;
|
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. */
|
||||||
|
|
@ -6976,6 +6969,8 @@ expand_static_init (tree decl, tree init)
|
||||||
finish_if_stmt (if_stmt);
|
finish_if_stmt (if_stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (DECL_THREAD_LOCAL_P (decl))
|
||||||
|
tls_aggregates = tree_cons (init, decl, tls_aggregates);
|
||||||
else
|
else
|
||||||
static_aggregates = tree_cons (init, decl, static_aggregates);
|
static_aggregates = tree_cons (init, decl, static_aggregates);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
256
gcc/cp/decl2.c
256
gcc/cp/decl2.c
|
|
@ -2781,6 +2781,187 @@ set_guard (tree guard)
|
||||||
tf_warning_or_error);
|
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
|
/* Start the process of running a particular set of global constructors
|
||||||
or destructors. Subroutine of do_[cd]tors. */
|
or destructors. Subroutine of do_[cd]tors. */
|
||||||
|
|
||||||
|
|
@ -3668,6 +3849,75 @@ clear_decl_external (struct cgraph_node *node, void * /*data*/)
|
||||||
return false;
|
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.
|
/* This routine is called at the end of compilation.
|
||||||
Its job is to create all the code needed to initialize and
|
Its job is to create all the code needed to initialize and
|
||||||
destroy the global aggregates. We do the destruction
|
destroy the global aggregates. We do the destruction
|
||||||
|
|
@ -3845,6 +4095,9 @@ cp_write_global_declarations (void)
|
||||||
/* ??? was: locus.line++; */
|
/* ??? 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
|
/* Go through the set of inline functions whose bodies have not
|
||||||
been emitted yet. If out-of-line copies of these functions
|
been emitted yet. If out-of-line copies of these functions
|
||||||
are required, emit them. */
|
are required, emit them. */
|
||||||
|
|
@ -3869,6 +4122,9 @@ cp_write_global_declarations (void)
|
||||||
reconsider = true;
|
reconsider = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!DECL_INITIAL (decl) && decl_tls_wrapper_p (decl))
|
||||||
|
generate_tls_wrapper (decl);
|
||||||
|
|
||||||
if (!DECL_SAVED_TREE (decl))
|
if (!DECL_SAVED_TREE (decl))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3684,6 +3684,20 @@ mangle_conv_op_name_for_type (const tree type)
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Write out the appropriate string for this variable when generating
|
||||||
|
another mangled name based on this one. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_guarded_var_name (const tree variable)
|
||||||
|
{
|
||||||
|
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
|
/* Return an identifier for the name of an initialization guard
|
||||||
variable for indicated VARIABLE. */
|
variable for indicated VARIABLE. */
|
||||||
|
|
||||||
|
|
@ -3692,15 +3706,48 @@ mangle_guard_variable (const tree variable)
|
||||||
{
|
{
|
||||||
start_mangling (variable);
|
start_mangling (variable);
|
||||||
write_string ("_ZGV");
|
write_string ("_ZGV");
|
||||||
if (strncmp (IDENTIFIER_POINTER (DECL_NAME (variable)), "_ZGR", 4) == 0)
|
write_guarded_var_name (variable);
|
||||||
/* 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 finish_mangling_get_identifier (/*warn=*/false);
|
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
|
/* 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
|
initialize a static reference. This isn't part of the ABI, but we might
|
||||||
as well call them something readable. */
|
as well call them something readable. */
|
||||||
|
|
|
||||||
|
|
@ -3270,7 +3270,17 @@ finish_id_expression (tree id_expression,
|
||||||
*non_integral_constant_expression_p = true;
|
*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 = (adjust_result_of_qualified_name_lookup
|
||||||
(decl, scope, current_nonlambda_class_type()));
|
(decl, scope, current_nonlambda_class_type()));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,25 @@
|
||||||
2012-10-08 Jason Merrill <jason@redhat.com>
|
2012-10-08 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
* g++.dg/gomp/tls-5.C: New.
|
||||||
|
* g++.dg/gomp/tls-wrap1.C: New.
|
||||||
|
* g++.dg/gomp/tls-wrap2.C: New.
|
||||||
|
* g++.dg/gomp/tls-wrap3.C: New.
|
||||||
|
* g++.dg/gomp/tls-wrap4.C: New.
|
||||||
|
* g++.dg/gomp/tls-wrapper-cse.C: New.
|
||||||
|
* g++.dg/tls/thread_local-cse.C: New.
|
||||||
|
* g++.dg/tls/thread_local-order1.C: New.
|
||||||
|
* g++.dg/tls/thread_local-order2.C: New.
|
||||||
|
* g++.dg/tls/thread_local-wrap1.C: New.
|
||||||
|
* g++.dg/tls/thread_local-wrap2.C: New.
|
||||||
|
* g++.dg/tls/thread_local-wrap3.C: New.
|
||||||
|
* g++.dg/tls/thread_local-wrap4.C: New.
|
||||||
|
* g++.dg/tls/thread_local2g.C: New.
|
||||||
|
* g++.dg/tls/thread_local3g.C: New.
|
||||||
|
* g++.dg/tls/thread_local4g.C: New.
|
||||||
|
* g++.dg/tls/thread_local5g.C: New.
|
||||||
|
* g++.dg/tls/thread_local6g.C: New.
|
||||||
|
* g++.dg/tls/thread_local7g.C: New.
|
||||||
|
|
||||||
* g++.dg/tls/thread_local3.C: New.
|
* g++.dg/tls/thread_local3.C: New.
|
||||||
* g++.dg/tls/thread_local4.C: New.
|
* g++.dg/tls/thread_local4.C: New.
|
||||||
* g++.dg/tls/thread_local5.C: New.
|
* g++.dg/tls/thread_local5.C: New.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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 ();
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
2012-10-08 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
* demangle.h (enum demangle_component_type): Add
|
||||||
|
DEMANGLE_COMPONENT_TLS_INIT and DEMANGLE_COMPONENT_TLS_WRAPPER.
|
||||||
|
|
||||||
2012-09-18 Florian Weimer <fweimer@redhat.com>
|
2012-09-18 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
PR other/54411
|
PR other/54411
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,9 @@ enum demangle_component_type
|
||||||
/* A guard variable. This has one subtree, the name for which this
|
/* A guard variable. This has one subtree, the name for which this
|
||||||
is a guard variable. */
|
is a guard variable. */
|
||||||
DEMANGLE_COMPONENT_GUARD,
|
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
|
/* A reference temporary. This has one subtree, the name for which
|
||||||
this is a temporary. */
|
this is a temporary. */
|
||||||
DEMANGLE_COMPONENT_REFTEMP,
|
DEMANGLE_COMPONENT_REFTEMP,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
2012-10-04 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
* testsuite/libgomp.c++/tls-init1.C: New.
|
||||||
|
|
||||||
2012-09-14 David Edelsohn <dje.gcc@gmail.com>
|
2012-09-14 David Edelsohn <dje.gcc@gmail.com>
|
||||||
|
|
||||||
* configure: Regenerated.
|
* configure: Regenerated.
|
||||||
|
|
|
||||||
|
|
@ -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 ();
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
2012-10-08 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
* cp-demangle.c (d_special_name, d_dump): Handle TH and TW.
|
||||||
|
(d_make_comp, d_print_comp): Likewise.
|
||||||
|
|
||||||
2012-09-18 Ian Lance Taylor <iant@google.com>
|
2012-09-18 Ian Lance Taylor <iant@google.com>
|
||||||
|
|
||||||
* strnlen.c: New file.
|
* strnlen.c: New file.
|
||||||
|
|
|
||||||
|
|
@ -696,6 +696,12 @@ d_dump (struct demangle_component *dc, int indent)
|
||||||
case DEMANGLE_COMPONENT_PACK_EXPANSION:
|
case DEMANGLE_COMPONENT_PACK_EXPANSION:
|
||||||
printf ("pack expansion\n");
|
printf ("pack expansion\n");
|
||||||
break;
|
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);
|
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_COVARIANT_THUNK:
|
||||||
case DEMANGLE_COMPONENT_JAVA_CLASS:
|
case DEMANGLE_COMPONENT_JAVA_CLASS:
|
||||||
case DEMANGLE_COMPONENT_GUARD:
|
case DEMANGLE_COMPONENT_GUARD:
|
||||||
|
case DEMANGLE_COMPONENT_TLS_INIT:
|
||||||
|
case DEMANGLE_COMPONENT_TLS_WRAPPER:
|
||||||
case DEMANGLE_COMPONENT_REFTEMP:
|
case DEMANGLE_COMPONENT_REFTEMP:
|
||||||
case DEMANGLE_COMPONENT_HIDDEN_ALIAS:
|
case DEMANGLE_COMPONENT_HIDDEN_ALIAS:
|
||||||
case DEMANGLE_COMPONENT_TRANSACTION_CLONE:
|
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,
|
return d_make_comp (di, DEMANGLE_COMPONENT_JAVA_CLASS,
|
||||||
cplus_demangle_type (di), NULL);
|
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:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -4072,6 +4088,16 @@ d_print_comp (struct d_print_info *dpi, int options,
|
||||||
d_print_comp (dpi, options, d_left (dc));
|
d_print_comp (dpi, options, d_left (dc));
|
||||||
return;
|
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:
|
case DEMANGLE_COMPONENT_REFTEMP:
|
||||||
d_append_string (dpi, "reference temporary #");
|
d_append_string (dpi, "reference temporary #");
|
||||||
d_print_comp (dpi, options, d_right (dc));
|
d_print_comp (dpi, options, d_right (dc));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue