diff options
author | jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-10-08 14:45:37 +0000 |
---|---|---|
committer | jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-10-08 14:45:37 +0000 |
commit | 462819c8e20aab1124e95392974dd55cd70350f3 (patch) | |
tree | bc70ed3cb42f2f1614f8ad5ac0a05170d55fe604 | |
parent | db019d30742cffe8f8d86560ea33e61b35269321 (diff) | |
download | gcc-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
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)); |