summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2012-10-08 14:45:37 +0000
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>2012-10-08 14:45:37 +0000
commit462819c8e20aab1124e95392974dd55cd70350f3 (patch)
treebc70ed3cb42f2f1614f8ad5ac0a05170d55fe604
parentdb019d30742cffe8f8d86560ea33e61b35269321 (diff)
downloadgcc-462819c8e20aab1124e95392974dd55cd70350f3.tar.gz
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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@192211 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/cp/ChangeLog18
-rw-r--r--gcc/cp/call.c10
-rw-r--r--gcc/cp/cp-tree.h6
-rw-r--r--gcc/cp/decl.c15
-rw-r--r--gcc/cp/decl2.c256
-rw-r--r--gcc/cp/mangle.c59
-rw-r--r--gcc/cp/semantics.c12
-rw-r--r--gcc/testsuite/ChangeLog20
-rw-r--r--gcc/testsuite/g++.dg/gomp/tls-5.C12
-rw-r--r--gcc/testsuite/g++.dg/gomp/tls-wrap1.C13
-rw-r--r--gcc/testsuite/g++.dg/gomp/tls-wrap2.C16
-rw-r--r--gcc/testsuite/g++.dg/gomp/tls-wrap3.C14
-rw-r--r--gcc/testsuite/g++.dg/gomp/tls-wrap4.C13
-rw-r--r--gcc/testsuite/g++.dg/gomp/tls-wrapper-cse.C18
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local-cse.C20
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local-order1.C25
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local-order2.C28
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local-wrap1.C13
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local-wrap2.C16
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local-wrap3.C14
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local-wrap4.C13
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local2g.C29
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local3g.C35
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local4g.C45
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local5g.C45
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local6g.C31
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local7g.C13
-rw-r--r--include/ChangeLog5
-rw-r--r--include/demangle.h3
-rw-r--r--libgomp/ChangeLog4
-rw-r--r--libgomp/testsuite/libgomp.c++/tls-init1.C26
-rw-r--r--libiberty/ChangeLog5
-rw-r--r--libiberty/cp-demangle.c26
33 files changed, 859 insertions, 19 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 1807f5b0e43..7f5d45f90a2 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,23 @@
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.
(register_dtor_fn): Use it for TLS.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 9c8de39e92d..3351a585f2b 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -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);
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;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 51c8d566e90..370f07230f0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4385,6 +4385,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 };
@@ -5197,6 +5199,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);
@@ -5992,6 +5995,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 */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 7dc13fb94a3..0b936ea1a8b 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.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. */
@@ -6838,16 +6841,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. */
@@ -6976,6 +6969,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);
}
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index f7db1d81b5d..688a72332ce 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -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;
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index eee44a1bae2..f448932e6ea 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -3684,23 +3684,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. */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 7174927094c..4b06f30ffa8 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3270,7 +3270,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()));
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index d575489d2d5..00e4696bdbf 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,25 @@
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_local4.C: New.
* g++.dg/tls/thread_local5.C: New.
diff --git a/gcc/testsuite/g++.dg/gomp/tls-5.C b/gcc/testsuite/g++.dg/gomp/tls-5.C
new file mode 100644
index 00000000000..74e4faaa884
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/tls-5.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/tls-wrap1.C b/gcc/testsuite/g++.dg/gomp/tls-wrap1.C
new file mode 100644
index 00000000000..91c9f863125
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/tls-wrap1.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/tls-wrap2.C b/gcc/testsuite/g++.dg/gomp/tls-wrap2.C
new file mode 100644
index 00000000000..7aa13711c0b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/tls-wrap2.C
@@ -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;
diff --git a/gcc/testsuite/g++.dg/gomp/tls-wrap3.C b/gcc/testsuite/g++.dg/gomp/tls-wrap3.C
new file mode 100644
index 00000000000..2504d99d1c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/tls-wrap3.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/tls-wrap4.C b/gcc/testsuite/g++.dg/gomp/tls-wrap4.C
new file mode 100644
index 00000000000..130114811f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/tls-wrap4.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/tls-wrapper-cse.C b/gcc/testsuite/g++.dg/gomp/tls-wrapper-cse.C
new file mode 100644
index 00000000000..af2de2f1fcc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/tls-wrapper-cse.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local-cse.C b/gcc/testsuite/g++.dg/tls/thread_local-cse.C
new file mode 100644
index 00000000000..47c6aede339
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local-cse.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local-order1.C b/gcc/testsuite/g++.dg/tls/thread_local-order1.C
new file mode 100644
index 00000000000..6557e938c79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local-order1.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local-order2.C b/gcc/testsuite/g++.dg/tls/thread_local-order2.C
new file mode 100644
index 00000000000..eb9c7690160
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local-order2.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local-wrap1.C b/gcc/testsuite/g++.dg/tls/thread_local-wrap1.C
new file mode 100644
index 00000000000..56177da1f9f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local-wrap1.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local-wrap2.C b/gcc/testsuite/g++.dg/tls/thread_local-wrap2.C
new file mode 100644
index 00000000000..1e8078fa8d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local-wrap2.C
@@ -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;
diff --git a/gcc/testsuite/g++.dg/tls/thread_local-wrap3.C b/gcc/testsuite/g++.dg/tls/thread_local-wrap3.C
new file mode 100644
index 00000000000..19e6ab8d0d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local-wrap3.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local-wrap4.C b/gcc/testsuite/g++.dg/tls/thread_local-wrap4.C
new file mode 100644
index 00000000000..130114811f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local-wrap4.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local2g.C b/gcc/testsuite/g++.dg/tls/thread_local2g.C
new file mode 100644
index 00000000000..36451d2db50
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local2g.C
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local3g.C b/gcc/testsuite/g++.dg/tls/thread_local3g.C
new file mode 100644
index 00000000000..d5e83e86f73
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local3g.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local4g.C b/gcc/testsuite/g++.dg/tls/thread_local4g.C
new file mode 100644
index 00000000000..574d2671481
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local4g.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local5g.C b/gcc/testsuite/g++.dg/tls/thread_local5g.C
new file mode 100644
index 00000000000..badab4fa641
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local5g.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local6g.C b/gcc/testsuite/g++.dg/tls/thread_local6g.C
new file mode 100644
index 00000000000..ff8d608e538
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local6g.C
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local7g.C b/gcc/testsuite/g++.dg/tls/thread_local7g.C
new file mode 100644
index 00000000000..6960598173a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local7g.C
@@ -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;
+}
diff --git a/include/ChangeLog b/include/ChangeLog
index 4d998edb0e7..e5d3e87b920 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -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>
PR other/54411
diff --git a/include/demangle.h b/include/demangle.h
index 34b3ed3cde9..5da79d85221 100644
--- a/include/demangle.h
+++ b/include/demangle.h
@@ -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,
diff --git a/libgomp/ChangeLog b/libgomp/ChangeLog
index ce8384d108d..8ed6abc207c 100644
--- a/libgomp/ChangeLog
+++ b/libgomp/ChangeLog
@@ -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>
* configure: Regenerated.
diff --git a/libgomp/testsuite/libgomp.c++/tls-init1.C b/libgomp/testsuite/libgomp.c++/tls-init1.C
new file mode 100644
index 00000000000..4cbaccb9851
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/tls-init1.C
@@ -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 ();
+}
diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog
index 3f601a8546e..303dda23cae 100644
--- a/libiberty/ChangeLog
+++ b/libiberty/ChangeLog
@@ -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>
* strnlen.c: New file.
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 258aaa71550..32df38c6024 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -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));