Commit 7c424acd by Jason Merrill Committed by Jason Merrill

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
parent 5b031b9b
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, ...@@ -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; ...@@ -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); ...@@ -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); ...@@ -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; ...@@ -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) ...@@ -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) ...@@ -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);
} }
......
...@@ -2781,6 +2781,187 @@ set_guard (tree guard) ...@@ -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*/) ...@@ -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) ...@@ -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) ...@@ -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,23 +3684,70 @@ mangle_conv_op_name_for_type (const tree type) ...@@ -3684,23 +3684,70 @@ mangle_conv_op_name_for_type (const tree type)
return identifier; return identifier;
} }
/* Return an identifier for the name of an initialization guard /* Write out the appropriate string for this variable when generating
variable for indicated VARIABLE. */ another mangled name based on this one. */
tree static void
mangle_guard_variable (const tree variable) write_guarded_var_name (const tree variable)
{ {
start_mangling (variable);
write_string ("_ZGV");
if (strncmp (IDENTIFIER_POINTER (DECL_NAME (variable)), "_ZGR", 4) == 0) if (strncmp (IDENTIFIER_POINTER (DECL_NAME (variable)), "_ZGR", 4) == 0)
/* The name of a guard variable for a reference temporary should refer /* The name of a guard variable for a reference temporary should refer
to the reference, not the temporary. */ to the reference, not the temporary. */
write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4); write_string (IDENTIFIER_POINTER (DECL_NAME (variable)) + 4);
else else
write_name (variable, /*ignore_local_scope=*/0); 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 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, ...@@ -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()));
......
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.
......
// 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;
}
// 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;
}
// 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;
// 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;
}
// 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;
}
// 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;
}
// 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;
}
// { 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();
}
// 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();
}
// 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;
}
// 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;
// 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;
}
// 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;
}
// { 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 ();
}
// { 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();
}
// 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();
}
// 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;
}
// 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;
}
// { 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;
}
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 ...@@ -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,
......
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.
......
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 ();
}
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) ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment