diff options
author | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-10-10 08:25:17 +0000 |
---|---|---|
committer | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-10-10 08:25:17 +0000 |
commit | d640c29f86f8993c04dd9ea0fb673fab0714739b (patch) | |
tree | 614c0136821a4b0cc1d908634346cf7b2b65ca18 /gcc/cp/decl2.c | |
parent | b95e3f379ca88868cf3b683362c321c81a5b943f (diff) | |
download | gcc-d640c29f86f8993c04dd9ea0fb673fab0714739b.tar.gz |
2012-10-10 Basile Starynkevitch <basile@starynkevitch.net>
MELT branch merged with trunk rev 192289 using svnmerge.py
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@192291 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/cp/decl2.c')
-rw-r--r-- | gcc/cp/decl2.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index aad3d0b26f7..688a72332ce 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -2696,6 +2696,7 @@ get_guard (tree decl) TREE_STATIC (guard) = TREE_STATIC (decl); DECL_COMMON (guard) = DECL_COMMON (decl); DECL_COMDAT (guard) = DECL_COMDAT (decl); + DECL_TLS_MODEL (guard) = DECL_TLS_MODEL (decl); if (DECL_ONE_ONLY (decl)) make_decl_one_only (guard, cxx_comdat_group (guard)); if (TREE_PUBLIC (decl)) @@ -2780,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. */ @@ -3667,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 @@ -3844,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. */ @@ -3868,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; |