summaryrefslogtreecommitdiff
path: root/gcc/cp/decl2.c
diff options
context:
space:
mode:
authorbstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4>2012-10-10 08:25:17 +0000
committerbstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4>2012-10-10 08:25:17 +0000
commitd640c29f86f8993c04dd9ea0fb673fab0714739b (patch)
tree614c0136821a4b0cc1d908634346cf7b2b65ca18 /gcc/cp/decl2.c
parentb95e3f379ca88868cf3b683362c321c81a5b943f (diff)
downloadgcc-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.c257
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;