diff options
author | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-07-26 22:53:50 +0000 |
---|---|---|
committer | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-07-26 22:53:50 +0000 |
commit | cffbbb9de6dd1d57639ccf61d76209e46c9bcbdb (patch) | |
tree | 566634468015c713398a1fcd59ec66da7138545a /gcc/tree-emutls.c | |
parent | d55d9a48e0263efc2c2fbaa0ae8a428ea6e5f13e (diff) | |
download | gcc-cffbbb9de6dd1d57639ccf61d76209e46c9bcbdb.tar.gz |
PR target/44132
Emulated TLS rewrite.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@162549 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-emutls.c')
-rw-r--r-- | gcc/tree-emutls.c | 802 |
1 files changed, 802 insertions, 0 deletions
diff --git a/gcc/tree-emutls.c b/gcc/tree-emutls.c new file mode 100644 index 00000000000..17f97be1104 --- /dev/null +++ b/gcc/tree-emutls.c @@ -0,0 +1,802 @@ +/* Lower TLS operations to emulation functions. + Copyright (C) 2006, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "tree-flow.h" +#include "cgraph.h" +#include "langhooks.h" +#include "target.h" +#include "targhooks.h" +#include "tree-iterator.h" + + +/* Whenever a target does not support thread-local storage (TLS) natively, + we can emulate it with some run-time support in libgcc. This will in + turn rely on "keyed storage" a-la pthread_key_create; essentially all + thread libraries provide such functionality. + + In order to coordinate with the libgcc runtime, each TLS variable is + described by a "control variable". This control variable records the + required size, alignment, and initial value of the TLS variable for + instantiation at runtime. It also stores an integer token to be used + by the runtime to find the address of the variable within each thread. + + On the compiler side, this means that we need to replace all instances + of "tls_var" in the code with "*__emutls_get_addr(&control_var)". We + also need to eliminate "tls_var" from the symbol table and introduce + "control_var". + + We used to perform all of the transformations during conversion to rtl, + and the variable substitutions magically within assemble_variable. + However, this late fiddling of the symbol table conflicts with LTO and + whole-program compilation. Therefore we must now make all the changes + to the symbol table early in the GIMPLE optimization path, before we + write things out to LTO intermediate files. */ + +/* These two vectors, once fully populated, are kept in lock-step so that + the index of a TLS variable equals the index of its control variable in + the other vector. */ +static varpool_node_set tls_vars; +static VEC(varpool_node_ptr, heap) *control_vars; + +/* For the current basic block, an SSA_NAME that has computed the address + of the TLS variable at the corresponding index. */ +static VEC(tree, heap) *access_vars; + +/* The type of the control structure, shared with the emutls.c runtime. */ +static tree emutls_object_type; + +#if !defined (NO_DOT_IN_LABEL) +# define EMUTLS_SEPARATOR "." +#elif !defined (NO_DOLLAR_IN_LABEL) +# define EMUTLS_SEPARATOR "$" +#else +# define EMUTLS_SEPARATOR "_" +#endif + +/* Create an IDENTIFIER_NODE by prefixing PREFIX to the + IDENTIFIER_NODE NAME's name. */ + +static tree +prefix_name (const char *prefix, tree name) +{ + unsigned plen = strlen (prefix); + unsigned nlen = strlen (IDENTIFIER_POINTER (name)); + char *toname = (char *) alloca (plen + nlen + 1); + + memcpy (toname, prefix, plen); + memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1); + + return get_identifier (toname); +} + +/* Create an identifier for the struct __emutls_object, given an identifier + of the DECL_ASSEMBLY_NAME of the original object. */ + +static tree +get_emutls_object_name (tree name) +{ + const char *prefix = (targetm.emutls.var_prefix + ? targetm.emutls.var_prefix + : "__emutls_v" EMUTLS_SEPARATOR); + return prefix_name (prefix, name); +} + +/* Create the fields of the type for the control variables. Ordinarily + this must match struct __emutls_object defined in emutls.c. However + this is a target hook so that VxWorks can define its own layout. */ + +tree +default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED) +{ + tree word_type_node, field, next_field; + + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__templ"), ptr_type_node); + DECL_CONTEXT (field) = type; + next_field = field; + + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__offset"), + ptr_type_node); + DECL_CONTEXT (field) = type; + DECL_CHAIN (field) = next_field; + next_field = field; + + word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__align"), + word_type_node); + DECL_CONTEXT (field) = type; + DECL_CHAIN (field) = next_field; + next_field = field; + + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__size"), word_type_node); + DECL_CONTEXT (field) = type; + DECL_CHAIN (field) = next_field; + + return field; +} + +/* Initialize emulated tls object TO, which refers to TLS variable DECL and + is initialized by PROXY. As above, this is the default implementation of + a target hook overridden by VxWorks. */ + +tree +default_emutls_var_init (tree to, tree decl, tree proxy) +{ + VEC(constructor_elt,gc) *v = VEC_alloc (constructor_elt, gc, 4); + constructor_elt *elt; + tree type = TREE_TYPE (to); + tree field = TYPE_FIELDS (type); + + elt = VEC_quick_push (constructor_elt, v, NULL); + elt->index = field; + elt->value = fold_convert (TREE_TYPE (field), DECL_SIZE_UNIT (decl)); + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = DECL_CHAIN (field); + elt->index = field; + elt->value = build_int_cst (TREE_TYPE (field), + DECL_ALIGN_UNIT (decl)); + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = DECL_CHAIN (field); + elt->index = field; + elt->value = null_pointer_node; + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = DECL_CHAIN (field); + elt->index = field; + elt->value = proxy; + + return build_constructor (type, v); +} + +/* Create the structure for struct __emutls_object. This should match the + structure at the top of emutls.c, modulo the union there. */ + +static tree +get_emutls_object_type (void) +{ + tree type, type_name, field; + + type = emutls_object_type; + if (type) + return type; + + emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE); + type_name = NULL; + field = targetm.emutls.var_fields (type, &type_name); + if (!type_name) + type_name = get_identifier ("__emutls_object"); + type_name = build_decl (UNKNOWN_LOCATION, + TYPE_DECL, type_name, type); + TYPE_NAME (type) = type_name; + TYPE_FIELDS (type) = field; + layout_type (type); + + return type; +} + +/* Create a read-only variable like DECL, with the same DECL_INITIAL. + This will be used for initializing the emulated tls data area. */ + +static tree +get_emutls_init_templ_addr (tree decl) +{ + tree name, to; + + if (targetm.emutls.register_common && !DECL_INITIAL (decl) + && !DECL_SECTION_NAME (decl)) + return null_pointer_node; + + name = DECL_ASSEMBLER_NAME (decl); + if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0]) + { + const char *prefix = (targetm.emutls.tmpl_prefix + ? targetm.emutls.tmpl_prefix + : "__emutls_t" EMUTLS_SEPARATOR); + name = prefix_name (prefix, name); + } + + to = build_decl (DECL_SOURCE_LOCATION (decl), + VAR_DECL, name, TREE_TYPE (decl)); + SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); + + DECL_ARTIFICIAL (to) = 1; + TREE_USED (to) = TREE_USED (decl); + TREE_READONLY (to) = 1; + DECL_IGNORED_P (to) = 1; + DECL_CONTEXT (to) = DECL_CONTEXT (decl); + DECL_SECTION_NAME (to) = DECL_SECTION_NAME (decl); + DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl); + + DECL_WEAK (to) = DECL_WEAK (decl); + if (DECL_ONE_ONLY (decl)) + { + make_decl_one_only (to, DECL_ASSEMBLER_NAME (to)); + TREE_STATIC (to) = TREE_STATIC (decl); + TREE_PUBLIC (to) = TREE_PUBLIC (decl); + DECL_VISIBILITY (to) = DECL_VISIBILITY (decl); + } + else + TREE_STATIC (to) = 1; + + DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl); + DECL_INITIAL (to) = DECL_INITIAL (decl); + DECL_INITIAL (decl) = NULL; + + if (targetm.emutls.tmpl_section) + { + DECL_SECTION_NAME (to) + = build_string (strlen (targetm.emutls.tmpl_section), + targetm.emutls.tmpl_section); + } + + varpool_finalize_decl (to); + return build_fold_addr_expr (to); +} + +/* Create and return the control variable for the TLS variable DECL. */ + +static tree +new_emutls_decl (tree decl) +{ + tree name, to; + + name = DECL_ASSEMBLER_NAME (decl); + to = build_decl (DECL_SOURCE_LOCATION (decl), VAR_DECL, + get_emutls_object_name (name), + get_emutls_object_type ()); + + SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); + + DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED; + DECL_ARTIFICIAL (to) = 1; + DECL_IGNORED_P (to) = 1; + TREE_READONLY (to) = 0; + TREE_STATIC (to) = 1; + + DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl); + DECL_CONTEXT (to) = DECL_CONTEXT (decl); + TREE_USED (to) = TREE_USED (decl); + TREE_PUBLIC (to) = TREE_PUBLIC (decl); + DECL_EXTERNAL (to) = DECL_EXTERNAL (decl); + DECL_COMMON (to) = DECL_COMMON (decl); + DECL_WEAK (to) = DECL_WEAK (decl); + DECL_VISIBILITY (to) = DECL_VISIBILITY (decl); + DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl); + DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl); + DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl); + + DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to); + + if (DECL_ONE_ONLY (decl)) + make_decl_one_only (to, DECL_ASSEMBLER_NAME (to)); + + /* If we're not allowed to change the proxy object's alignment, + pretend it has been set by the user. */ + if (targetm.emutls.var_align_fixed) + DECL_USER_ALIGN (to) = 1; + + /* If the target wants the control variables grouped, do so. */ + if (!DECL_COMMON (to) && targetm.emutls.var_section) + { + DECL_SECTION_NAME (to) + = build_string (strlen (targetm.emutls.tmpl_section), + targetm.emutls.tmpl_section); + } + + /* If this variable is defined locally, then we need to initialize the + control structure with size and alignment information. Initialization + of COMMON block variables happens elsewhere via a constructor. */ + if (!DECL_EXTERNAL (to) + && (!DECL_COMMON (to) + || (DECL_INITIAL (decl) + && DECL_INITIAL (decl) != error_mark_node))) + { + tree tmpl = get_emutls_init_templ_addr (decl); + DECL_INITIAL (to) = targetm.emutls.var_init (to, decl, tmpl); + record_references_in_initializer (to, false); + } + + varpool_finalize_decl (to); + return to; +} + +/* Look up the index of the TLS variable DECL. This index can then be + used in both the control_vars and access_vars arrays. */ + +static unsigned int +emutls_index (tree decl) +{ + varpool_node_set_iterator i; + + i = varpool_node_set_find (tls_vars, varpool_get_node (decl)); + gcc_assert (i.index != ~0u); + + return i.index; +} + +/* Look up the control variable for the TLS variable DECL. */ + +static tree +emutls_decl (tree decl) +{ + struct varpool_node *var; + unsigned int i; + + i = emutls_index (decl); + var = VEC_index (varpool_node_ptr, control_vars, i); + return var->decl; +} + +/* Generate a call statement to initialize CONTROL_DECL for TLS_DECL. + This only needs to happen for TLS COMMON variables; non-COMMON + variables can be initialized statically. Insert the generated + call statement at the end of PSTMTS. */ + +static void +emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts) +{ + tree x; + tree word_type_node; + + if (! DECL_COMMON (tls_decl) + || (DECL_INITIAL (tls_decl) + && DECL_INITIAL (tls_decl) != error_mark_node)) + return; + + word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); + + x = build_call_expr (built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON], 4, + build_fold_addr_expr (control_decl), + fold_convert (word_type_node, + DECL_SIZE_UNIT (tls_decl)), + build_int_cst (word_type_node, + DECL_ALIGN_UNIT (tls_decl)), + get_emutls_init_templ_addr (tls_decl)); + + append_to_statement_list (x, pstmts); +} + +struct lower_emutls_data +{ + struct cgraph_node *cfun_node; + struct cgraph_node *builtin_node; + tree builtin_decl; + basic_block bb; + int bb_freq; + location_t loc; + gimple_seq seq; +}; + +/* Given a TLS variable DECL, return an SSA_NAME holding its address. + Append any new computation statements required to D->SEQ. */ + +static tree +gen_emutls_addr (tree decl, struct lower_emutls_data *d) +{ + unsigned int index; + tree addr; + + /* Compute the address of the TLS variable with help from runtime. */ + index = emutls_index (decl); + addr = VEC_index (tree, access_vars, index); + if (addr == NULL) + { + struct varpool_node *cvar; + tree cdecl; + gimple x; + + cvar = VEC_index (varpool_node_ptr, control_vars, index); + cdecl = cvar->decl; + TREE_ADDRESSABLE (cdecl) = 1; + + addr = create_tmp_var (build_pointer_type (TREE_TYPE (decl)), NULL); + x = gimple_build_call (d->builtin_decl, 1, build_fold_addr_expr (cdecl)); + gimple_set_location (x, d->loc); + + addr = make_ssa_name (addr, x); + gimple_call_set_lhs (x, addr); + + gimple_seq_add_stmt (&d->seq, x); + + cgraph_create_edge (d->cfun_node, d->builtin_node, x, + d->bb->count, d->bb_freq, d->bb->loop_depth); + + /* We may be adding a new reference to a new variable to the function. + This means we have to play with the ipa-reference web. */ + ipa_record_reference (d->cfun_node, NULL, NULL, cvar, IPA_REF_ADDR, x); + + /* Record this ssa_name for possible use later in the basic block. */ + VEC_replace (tree, access_vars, index, addr); + } + + return addr; +} + +/* Callback for walk_gimple_op. D = WI->INFO is a struct lower_emutls_data. + Given an operand *PTR within D->STMT, if the operand references a TLS + variable, then lower the reference to a call to the runtime. Insert + any new statements required into D->SEQ; the caller is responsible for + placing those appropriately. */ + +static tree +lower_emutls_1 (tree *ptr, int *walk_subtrees, void *cb_data) +{ + struct walk_stmt_info *wi = (struct walk_stmt_info *) cb_data; + struct lower_emutls_data *d = (struct lower_emutls_data *) wi->info; + tree t = *ptr; + bool is_addr = false; + tree addr; + + *walk_subtrees = 0; + + switch (TREE_CODE (t)) + { + case ADDR_EXPR: + /* If this is not a straight-forward "&var", but rather something + like "&var.a", then we may need special handling. */ + if (TREE_CODE (TREE_OPERAND (t, 0)) != VAR_DECL) + { + bool save_changed; + + /* If we're allowed more than just is_gimple_val, continue. */ + if (!wi->val_only) + { + *walk_subtrees = 1; + return NULL_TREE; + } + + /* See if any substitution would be made. */ + save_changed = wi->changed; + wi->changed = false; + wi->val_only = false; + walk_tree (&TREE_OPERAND (t, 0), lower_emutls_1, wi, NULL); + wi->val_only = true; + + /* If so, then extract this entire sub-expression "&p->a" into a + new assignment statement, and substitute yet another SSA_NAME. */ + if (wi->changed) + { + gimple x; + + addr = create_tmp_var (TREE_TYPE (t), NULL); + x = gimple_build_assign (addr, t); + gimple_set_location (x, d->loc); + + addr = make_ssa_name (addr, x); + gimple_assign_set_lhs (x, addr); + + gimple_seq_add_stmt (&d->seq, x); + + *ptr = addr; + } + else + wi->changed = save_changed; + + return NULL_TREE; + } + + t = TREE_OPERAND (t, 0); + is_addr = true; + /* FALLTHRU */ + + case VAR_DECL: + if (!DECL_THREAD_LOCAL_P (t)) + return NULL_TREE; + break; + + default: + /* We're not interested in other decls or types, only subexpressions. */ + if (EXPR_P (t)) + *walk_subtrees = 1; + /* FALLTHRU */ + + case SSA_NAME: + /* Special-case the return of SSA_NAME, since it's so common. */ + return NULL_TREE; + } + + addr = gen_emutls_addr (t, d); + if (is_addr) + { + /* Replace "&var" with "addr" in the statement. */ + *ptr = addr; + } + else + { + /* Replace "var" with "*addr" in the statement. */ + t = build2 (MEM_REF, TREE_TYPE (t), addr, + build_int_cst (TREE_TYPE (addr), 0)); + *ptr = t; + } + + wi->changed = true; + return NULL_TREE; +} + +/* Lower all of the operands of STMT. */ + +static void +lower_emutls_stmt (gimple stmt, struct lower_emutls_data *d) +{ + struct walk_stmt_info wi; + + d->loc = gimple_location (stmt); + + memset (&wi, 0, sizeof (wi)); + wi.info = d; + wi.val_only = true; + walk_gimple_op (stmt, lower_emutls_1, &wi); + + if (wi.changed) + update_stmt (stmt); +} + +/* Lower the I'th operand of PHI. */ + +static void +lower_emutls_phi_arg (gimple phi, unsigned int i, struct lower_emutls_data *d) +{ + struct walk_stmt_info wi; + struct phi_arg_d *pd = gimple_phi_arg (phi, i); + + /* Early out for a very common case we don't care about. */ + if (TREE_CODE (pd->def) == SSA_NAME) + return; + + d->loc = pd->locus; + + memset (&wi, 0, sizeof (wi)); + wi.info = d; + wi.val_only = true; + walk_tree (&pd->def, lower_emutls_1, &wi, NULL); + + /* For normal statements, we let update_stmt do its job. But for phi + nodes, we have to manipulate the immediate use list by hand. */ + if (wi.changed) + { + gcc_assert (TREE_CODE (pd->def) == SSA_NAME); + link_imm_use_stmt (&pd->imm_use, pd->def, phi); + } +} + +/* Clear the ACCESS_VARS array, in order to begin a new block. */ + +static inline void +clear_access_vars (void) +{ + memset (VEC_address (tree, access_vars), 0, + VEC_length (tree, access_vars) * sizeof(tree)); +} + +/* Lower the entire function NODE. */ + +static void +lower_emutls_function_body (struct cgraph_node *node) +{ + struct lower_emutls_data d; + bool any_edge_inserts = false; + + current_function_decl = node->decl; + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + + d.cfun_node = node; + d.builtin_decl = built_in_decls[BUILT_IN_EMUTLS_GET_ADDRESS]; + d.builtin_node = cgraph_node (d.builtin_decl); + + FOR_EACH_BB (d.bb) + { + gimple_stmt_iterator gsi; + unsigned int i, nedge; + + /* Lower each of the PHI nodes of the block, as we may have + propagated &tlsvar into a PHI argument. These loops are + arranged so that we process each edge at once, and each + PHI argument for that edge. */ + if (!gimple_seq_empty_p (phi_nodes (d.bb))) + { + /* The calls will be inserted on the edges, and the frequencies + will be computed during the commit process. */ + d.bb_freq = 0; + + nedge = EDGE_COUNT (d.bb->preds); + for (i = 0; i < nedge; ++i) + { + edge e = EDGE_PRED (d.bb, i); + + /* We can re-use any SSA_NAME created on this edge. */ + clear_access_vars (); + d.seq = NULL; + + for (gsi = gsi_start_phis (d.bb); + !gsi_end_p (gsi); + gsi_next (&gsi)) + lower_emutls_phi_arg (gsi_stmt (gsi), i, &d); + + /* Insert all statements generated by all phi nodes for this + particular edge all at once. */ + if (d.seq) + { + gsi_insert_seq_on_edge (e, d.seq); + any_edge_inserts = true; + } + } + } + + d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb); + + /* We can re-use any SSA_NAME created during this basic block. */ + clear_access_vars (); + + /* Lower each of the statements of the block. */ + for (gsi = gsi_start_bb (d.bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + d.seq = NULL; + lower_emutls_stmt (gsi_stmt (gsi), &d); + + /* If any new statements were created, insert them immediately + before the first use. This prevents variable lifetimes from + becoming unnecessarily long. */ + if (d.seq) + gsi_insert_seq_before (&gsi, d.seq, GSI_SAME_STMT); + } + } + + if (any_edge_inserts) + gsi_commit_edge_inserts (); + + pop_cfun (); + current_function_decl = NULL; +} + +/* Main entry point to the tls lowering pass. */ + +static unsigned int +ipa_lower_emutls (void) +{ + struct varpool_node *var; + struct cgraph_node *func; + bool any_aliases = false; + tree ctor_body = NULL; + unsigned int i, n_tls; + + tls_vars = varpool_node_set_new (); + + /* Examine all global variables for TLS variables. */ + for (var = varpool_nodes; var ; var = var->next) + if (DECL_THREAD_LOCAL_P (var->decl)) + { + gcc_checking_assert (TREE_STATIC (var->decl) + || DECL_EXTERNAL (var->decl)); + varpool_node_set_add (tls_vars, var); + } + + /* If we found no TLS variables, then there is no further work to do. */ + if (tls_vars->nodes == NULL) + { + tls_vars = NULL; + if (dump_file) + fprintf (dump_file, "No TLS variables found.\n"); + return 0; + } + + /* Allocate the on-the-side arrays that share indicies with the TLS vars. */ + n_tls = VEC_length (varpool_node_ptr, tls_vars->nodes); + control_vars = VEC_alloc (varpool_node_ptr, heap, n_tls); + access_vars = VEC_alloc (tree, heap, n_tls); + VEC_safe_grow (tree, heap, access_vars, n_tls); + + /* Create the control variables for each TLS variable. */ + for (i = 0; VEC_iterate (varpool_node_ptr, tls_vars->nodes, i, var); ++i) + { + tree cdecl; + struct varpool_node *cvar; + + var = VEC_index (varpool_node_ptr, tls_vars->nodes, i); + cdecl = new_emutls_decl (var->decl); + + cvar = varpool_get_node (cdecl); + VEC_quick_push (varpool_node_ptr, control_vars, cvar); + + if (var->alias) + { + any_aliases = true; + cvar->alias = true; + } + else + { + /* Make sure the COMMON block control variable gets initialized. + Note that there's no point in doing this for aliases; we only + need to do this once for the main variable. */ + emutls_common_1 (var->decl, cdecl, &ctor_body); + } + + /* Indicate that the value of the TLS variable may be found elsewhere, + preventing the variable from re-appearing in the GIMPLE. We cheat + and use the control variable here (rather than a full call_expr), + which is special-cased inside the DWARF2 output routines. */ + SET_DECL_VALUE_EXPR (var->decl, cdecl); + DECL_HAS_VALUE_EXPR_P (var->decl) = 1; + } + + /* If there were any aliases, then frob the alias_pairs vector. */ + if (any_aliases) + { + alias_pair *p; + for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); ++i) + if (DECL_THREAD_LOCAL_P (p->decl)) + { + p->decl = emutls_decl (p->decl); + p->target = get_emutls_object_name (p->target); + } + } + + /* Adjust all uses of TLS variables within the function bodies. */ + for (func = cgraph_nodes; func; func = func->next) + if (func->reachable && func->lowered) + lower_emutls_function_body (func); + + /* Generate the constructor for any COMMON control variables created. */ + if (ctor_body) + cgraph_build_static_cdtor ('I', ctor_body, DEFAULT_INIT_PRIORITY); + + VEC_free (varpool_node_ptr, heap, control_vars); + VEC_free (tree, heap, access_vars); + tls_vars = NULL; + + return TODO_dump_func | TODO_ggc_collect | TODO_verify_all; +} + +/* If the target supports TLS natively, we need do nothing here. */ + +static bool +gate_emutls (void) +{ + return !targetm.have_tls; +} + +struct simple_ipa_opt_pass pass_ipa_lower_emutls = +{ + { + SIMPLE_IPA_PASS, + "emutls", /* name */ + gate_emutls, /* gate */ + ipa_lower_emutls, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_NONE, /* tv_id */ + PROP_cfg | PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + } +}; |