Allow dynamic initialization of thread_locals.
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 8 Oct 2012 14:45:37 +0000 (14:45 +0000)
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 8 Oct 2012 14:45:37 +0000 (14:45 +0000)
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

33 files changed:
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/decl2.c
gcc/cp/mangle.c
gcc/cp/semantics.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/gomp/tls-5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/tls-wrap1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/tls-wrap2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/tls-wrap3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/tls-wrap4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/tls-wrapper-cse.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local-cse.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local-order1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local-order2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local-wrap1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local-wrap2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local-wrap3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local-wrap4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local2g.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local3g.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local4g.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local5g.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local6g.C [new file with mode: 0644]
gcc/testsuite/g++.dg/tls/thread_local7g.C [new file with mode: 0644]
include/ChangeLog
include/demangle.h
libgomp/ChangeLog
libgomp/testsuite/libgomp.c++/tls-init1.C [new file with mode: 0644]
libiberty/ChangeLog
libiberty/cp-demangle.c

index 1807f5b..7f5d45f 100644 (file)
@@ -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.
 
index 9c8de39..3351a58 100644 (file)
@@ -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;
index 51c8d56..370f072 100644 (file)
@@ -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 */
index 7dc13fb..0b936ea 100644 (file)
@@ -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);
 }
index f7db1d8..688a723 100644 (file)
@@ -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;
 
index eee44a1..f448932 100644 (file)
@@ -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.  */
index 7174927..4b06f30 100644 (file)
@@ -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()));
index d575489..00e4696 100644 (file)
@@ -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 (file)
index 0000000..74e4faa
--- /dev/null
@@ -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 (file)
index 0000000..91c9f86
--- /dev/null
@@ -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 (file)
index 0000000..7aa1371
--- /dev/null
@@ -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 (file)
index 0000000..2504d99
--- /dev/null
@@ -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 (file)
index 0000000..1301148
--- /dev/null
@@ -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 (file)
index 0000000..af2de2f
--- /dev/null
@@ -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 (file)
index 0000000..47c6aed
--- /dev/null
@@ -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 (file)
index 0000000..6557e93
--- /dev/null
@@ -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 (file)
index 0000000..eb9c769
--- /dev/null
@@ -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 (file)
index 0000000..56177da
--- /dev/null
@@ -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 (file)
index 0000000..1e8078f
--- /dev/null
@@ -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 (file)
index 0000000..19e6ab8
--- /dev/null
@@ -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 (file)
index 0000000..1301148
--- /dev/null
@@ -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 (file)
index 0000000..36451d2
--- /dev/null
@@ -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 (file)
index 0000000..d5e83e8
--- /dev/null
@@ -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 (file)
index 0000000..574d267
--- /dev/null
@@ -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 (file)
index 0000000..badab4f
--- /dev/null
@@ -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 (file)
index 0000000..ff8d608
--- /dev/null
@@ -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 (file)
index 0000000..6960598
--- /dev/null
@@ -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;
+}
index 4d998ed..e5d3e87 100644 (file)
@@ -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
index 34b3ed3..5da79d8 100644 (file)
@@ -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,
index ce8384d..8ed6abc 100644 (file)
@@ -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 (file)
index 0000000..4cbaccb
--- /dev/null
@@ -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 ();
+}
index 3f601a8..303dda2 100644 (file)
@@ -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.
index 258aaa7..32df38c 100644 (file)
@@ -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));