| /* Lower TLS operations to emulation functions. |
| Copyright (C) 2006-2022 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 "backend.h" |
| #include "target.h" |
| #include "tree.h" |
| #include "gimple.h" |
| #include "tree-pass.h" |
| #include "ssa.h" |
| #include "cgraph.h" |
| #include "fold-const.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "gimple-iterator.h" |
| #include "gimple-walk.h" |
| #include "langhooks.h" |
| #include "tree-iterator.h" |
| #include "gimplify.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. */ |
| |
| /* Value for TLS varpool node where a pointer to control variable and |
| access variable are stored. */ |
| struct tls_var_data |
| { |
| varpool_node *control_var; |
| tree access; |
| }; |
| |
| /* TLS map accesses mapping between a TLS varpool node and a pair |
| made by control variable and access variable. */ |
| static hash_map<varpool_node *, tls_var_data> *tls_map = NULL; |
| |
| /* 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, va_gc> *v; |
| vec_alloc (v, 4); |
| constructor_elt elt; |
| tree type = TREE_TYPE (to); |
| tree field = TYPE_FIELDS (type); |
| |
| elt.index = field; |
| elt.value = fold_convert (TREE_TYPE (field), DECL_SIZE_UNIT (decl)); |
| v->quick_push (elt); |
| |
| field = DECL_CHAIN (field); |
| elt.index = field; |
| elt.value = build_int_cst (TREE_TYPE (field), |
| DECL_ALIGN_UNIT (decl)); |
| v->quick_push (elt); |
| |
| field = DECL_CHAIN (field); |
| elt.index = field; |
| elt.value = null_pointer_node; |
| v->quick_push (elt); |
| |
| field = DECL_CHAIN (field); |
| elt.index = field; |
| elt.value = proxy; |
| v->quick_push (elt); |
| |
| 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_PRESERVE_P (to) = DECL_PRESERVE_P (decl); |
| |
| DECL_WEAK (to) = DECL_WEAK (decl); |
| if (DECL_ONE_ONLY (decl) || DECL_WEAK (decl)) |
| { |
| TREE_STATIC (to) = TREE_STATIC (decl); |
| TREE_PUBLIC (to) = TREE_PUBLIC (decl); |
| DECL_VISIBILITY (to) = DECL_VISIBILITY (decl); |
| } |
| else |
| TREE_STATIC (to) = 1; |
| |
| if (DECL_ONE_ONLY (decl)) |
| make_decl_one_only (to, DECL_ASSEMBLER_NAME (to)); |
| |
| DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl); |
| DECL_INITIAL (to) = DECL_INITIAL (decl); |
| DECL_INITIAL (decl) = NULL; |
| |
| if (targetm.emutls.tmpl_section) |
| set_decl_section_name (to, targetm.emutls.tmpl_section); |
| else |
| set_decl_section_name (to, decl); |
| |
| /* Create varpool node for the new variable and finalize it if it is |
| not external one. */ |
| if (DECL_EXTERNAL (to)) |
| varpool_node::get_create (to); |
| else |
| varpool_node::add (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 alias_of) |
| { |
| 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_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_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)); |
| |
| set_decl_tls_model (to, TLS_MODEL_EMULATED); |
| |
| /* 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) |
| { |
| set_decl_section_name (to, targetm.emutls.var_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) || !targetm.emutls.register_common |
| || (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); |
| } |
| |
| /* Create varpool node for the new variable and finalize it if it is |
| not external one. */ |
| if (DECL_EXTERNAL (to)) |
| varpool_node::get_create (to); |
| else if (!alias_of) |
| varpool_node::add (to); |
| else |
| { |
| varpool_node *n; |
| varpool_node *t = varpool_node::get_for_asmname |
| (DECL_ASSEMBLER_NAME (DECL_VALUE_EXPR (alias_of))); |
| |
| n = varpool_node::create_alias (to, t->decl); |
| n->resolve_alias (t); |
| } |
| return to; |
| } |
| |
| /* 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) || !targetm.emutls.register_common |
| || (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 (builtin_decl_explicit (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; |
| 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, bool for_debug) |
| { |
| /* Compute the address of the TLS variable with help from runtime. */ |
| tls_var_data *data = tls_map->get (varpool_node::get (decl)); |
| tree addr = data->access; |
| |
| if (addr == NULL && !for_debug) |
| { |
| varpool_node *cvar; |
| tree cdecl; |
| gcall *x; |
| |
| cvar = data->control_var; |
| cdecl = cvar->decl; |
| TREE_ADDRESSABLE (cdecl) = 1; |
| |
| addr = create_tmp_var (build_pointer_type (TREE_TYPE (decl))); |
| 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); |
| |
| d->cfun_node->create_edge (d->builtin_node, x, d->bb->count); |
| |
| /* 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. */ |
| d->cfun_node->create_reference (cvar, IPA_REF_ADDR, x); |
| |
| /* Record this ssa_name for possible use later in the basic block. */ |
| data->access = addr; |
| } |
| |
| return addr; |
| } |
| |
| /* Callback for lower_emutls_1, return non-NULL if there is any TLS |
| VAR_DECL in the subexpressions. */ |
| |
| static tree |
| lower_emutls_2 (tree *ptr, int *walk_subtrees, void *) |
| { |
| tree t = *ptr; |
| if (TREE_CODE (t) == VAR_DECL) |
| return DECL_THREAD_LOCAL_P (t) ? t : NULL_TREE; |
| else if (!EXPR_P (t)) |
| *walk_subtrees = 0; |
| return NULL_TREE; |
| } |
| |
| /* 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; |
| |
| /* Gimple invariants are shareable trees, so before changing |
| anything in them if we will need to change anything, unshare |
| them. */ |
| if (is_gimple_min_invariant (t) |
| && walk_tree (&TREE_OPERAND (t, 0), lower_emutls_2, NULL, NULL)) |
| *ptr = t = unshare_expr (t); |
| |
| /* If we're allowed more than just is_gimple_val, continue. */ |
| if (!wi->val_only || is_gimple_debug (wi->stmt)) |
| { |
| *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)); |
| 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, is_gimple_debug (wi->stmt)); |
| if (!addr) |
| { |
| gimple_debug_bind_reset_value (wi->stmt); |
| update_stmt (wi->stmt); |
| wi->changed = false; |
| /* Stop walking operands. */ |
| return error_mark_node; |
| } |
| 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 (gphi *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; |
| wi.stmt = phi; |
| 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); |
| } |
| } |
| |
| /* Reset access variable for a given TLS variable data DATA. */ |
| |
| bool |
| reset_access (varpool_node * const &, tls_var_data *data, void *) |
| { |
| data->access = NULL; |
| |
| return true; |
| } |
| |
| /* Clear the access variables, in order to begin a new block. */ |
| |
| static inline void |
| clear_access_vars (void) |
| { |
| tls_map->traverse<void *, reset_access> (NULL); |
| } |
| |
| /* 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; |
| |
| push_cfun (DECL_STRUCT_FUNCTION (node->decl)); |
| |
| d.cfun_node = node; |
| d.builtin_decl = builtin_decl_explicit (BUILT_IN_EMUTLS_GET_ADDRESS); |
| /* This is where we introduce the declaration to the IL and so we have to |
| create a node for it. */ |
| d.builtin_node = cgraph_node::get_create (d.builtin_decl); |
| |
| FOR_EACH_BB_FN (d.bb, cfun) |
| { |
| 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))) |
| { |
| 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 (gphi_iterator gsi = gsi_start_phis (d.bb); |
| !gsi_end_p (gsi); |
| gsi_next (&gsi)) |
| lower_emutls_phi_arg (gsi.phi (), 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; |
| } |
| } |
| } |
| |
| /* We can re-use any SSA_NAME created during this basic block. */ |
| clear_access_vars (); |
| |
| /* Lower each of the statements of the block. */ |
| for (gimple_stmt_iterator 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 (); |
| } |
| |
| /* Create emutls variable for VAR, DATA is pointer to static |
| ctor body we can add constructors to. |
| Callback for varpool_for_variable_and_aliases. */ |
| |
| static bool |
| create_emultls_var (varpool_node *var, void *data) |
| { |
| tree cdecl; |
| tls_var_data value; |
| |
| cdecl = new_emutls_decl (var->decl, |
| var->alias && var->analyzed |
| ? var->get_alias_target ()->decl : NULL); |
| |
| varpool_node *cvar = varpool_node::get (cdecl); |
| |
| if (!var->alias) |
| { |
| /* 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, (tree *)data); |
| } |
| if (var->alias && !var->analyzed) |
| cvar->alias = true; |
| |
| /* 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; |
| |
| value.control_var = cvar; |
| tls_map->put (var, value); |
| |
| return false; |
| } |
| |
| /* Main entry point to the tls lowering pass. */ |
| |
| static unsigned int |
| ipa_lower_emutls (void) |
| { |
| varpool_node *var; |
| cgraph_node *func; |
| bool any_aliases = false; |
| tree ctor_body = NULL; |
| hash_set <varpool_node *> visited; |
| auto_vec <varpool_node *> tls_vars; |
| |
| /* Examine all global variables for TLS variables. */ |
| FOR_EACH_VARIABLE (var) |
| if (DECL_THREAD_LOCAL_P (var->decl) |
| && !visited.add (var)) |
| { |
| gcc_checking_assert (TREE_STATIC (var->decl) |
| || DECL_EXTERNAL (var->decl)); |
| tls_vars.safe_push (var); |
| if (var->alias && var->definition |
| && !visited.add (var->ultimate_alias_target ())) |
| tls_vars.safe_push (var->ultimate_alias_target ()); |
| } |
| |
| /* If we found no TLS variables, then there is no further work to do. */ |
| if (tls_vars.is_empty ()) |
| { |
| if (dump_file) |
| fprintf (dump_file, "No TLS variables found.\n"); |
| return 0; |
| } |
| |
| tls_map = new hash_map <varpool_node *, tls_var_data> (); |
| |
| /* Create the control variables for each TLS variable. */ |
| for (unsigned i = 0; i < tls_vars.length (); i++) |
| { |
| var = tls_vars[i]; |
| |
| if (var->alias && !var->analyzed) |
| any_aliases = true; |
| else if (!var->alias) |
| var->call_for_symbol_and_aliases (create_emultls_var, &ctor_body, true); |
| } |
| |
| /* If there were any aliases, then frob the alias_pairs vector. */ |
| if (any_aliases) |
| { |
| alias_pair *p; |
| unsigned int i; |
| FOR_EACH_VEC_SAFE_ELT (alias_pairs, i, p) |
| if (DECL_THREAD_LOCAL_P (p->decl)) |
| { |
| p->decl = tls_map->get |
| (varpool_node::get (p->decl))->control_var->decl; |
| p->target = get_emutls_object_name (p->target); |
| } |
| } |
| |
| /* Adjust all uses of TLS variables within the function bodies. */ |
| FOR_EACH_DEFINED_FUNCTION (func) |
| if (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); |
| |
| delete tls_map; |
| |
| return 0; |
| } |
| |
| namespace { |
| |
| const pass_data pass_data_ipa_lower_emutls = |
| { |
| SIMPLE_IPA_PASS, /* type */ |
| "emutls", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_IPA_OPT, /* tv_id */ |
| ( PROP_cfg | PROP_ssa ), /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| 0, /* todo_flags_finish */ |
| }; |
| |
| class pass_ipa_lower_emutls : public simple_ipa_opt_pass |
| { |
| public: |
| pass_ipa_lower_emutls (gcc::context *ctxt) |
| : simple_ipa_opt_pass (pass_data_ipa_lower_emutls, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| virtual bool gate (function *) |
| { |
| /* If the target supports TLS natively, we need do nothing here. */ |
| return !targetm.have_tls; |
| } |
| |
| virtual unsigned int execute (function *) { return ipa_lower_emutls (); } |
| |
| }; // class pass_ipa_lower_emutls |
| |
| } // anon namespace |
| |
| simple_ipa_opt_pass * |
| make_pass_ipa_lower_emutls (gcc::context *ctxt) |
| { |
| return new pass_ipa_lower_emutls (ctxt); |
| } |