diff options
author | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-10-04 21:09:20 +0000 |
---|---|---|
committer | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-10-04 21:09:20 +0000 |
commit | 2551f8e0be582a9e3fa959425842978d4c6772dc (patch) | |
tree | c4b6efa5601a2114eda9c1a31ae3155935113ab0 /gcc/varasm.c | |
parent | 227bbb0869285c1d34fc4e4e7b1ed064875692a4 (diff) | |
download | gcc-2551f8e0be582a9e3fa959425842978d4c6772dc.tar.gz |
gcc/
* Makefile.in (libgcc.mk, LIBGCC_DEPS): Add emutls.c.
* builtin-types.def (BT_WORD): Make unsigned.
(BT_FN_VOID_PTR_WORD_WORD_PTR): New.
* builtins.def (BUILT_IN_EMUTLS_GET_ADDRESS): New.
(BUILT_IN_EMUTLS_REGISTER_COMMON): New.
* c-decl.c (grokdeclarator): Don't error if !have_tls.
* c-parser.c (c_parser_omp_threadprivate): Likewise.
* cgraph.c (decide_is_variable_needed): Look at force_output.
Recurse for emulated tls.
* cgraphunit.c (cgraph_varpool_remove_unreferenced_decls): Remove
checks redundant with decide_is_variable_needed.
(cgraph_build_static_cdtor): Do cgraph_varpool_assemble_pending_decls.
* dwarf2out.c (loc_descriptor_from_tree_1): Don't do anything for
emulated tls.
* expr.c (emutls_var_address): New.
(expand_expr_real_1): Expand emulated tls.
(expand_expr_addr_expr_1): Likewise.
* libgcc-std.ver: Add __emutls_get_address, __emutls_register_common.
* output.h (emutls_finish): Declare.
* toplev.c (compile_file): Call it.
* tree-ssa-address.c (gen_addr_rtx): Check for const-ness of the
address before wrapping in CONST.
* varasm.c (emutls_htab, emutls_object_type): New.
(EMUTLS_VAR_PREFIX, EMUTLS_TMPL_PREFIX): New.
(get_emutls_object_name, get_emutls_object_type): New.
(get_emutls_init_templ_addr, emutls_decl): New.
(emutls_common_1, emutls_finish): New.
(assemble_variable): When emulating tls, swap decls; generate
constructor for the emutls objects.
(do_assemble_alias): When emulating tls, swap decl and target name.
(default_encode_section_info): Don't add SYMBOL_FLAG_TLS_SHIFT
for emulated tls.
* emutls.c: New file.
* config/sparc/sol2.h (ASM_DECLARE_OBJECT_NAME): Only emit
tls_object for real tls.
gcc/cp/
* decl.c (grokvardecl): Don't error if !have_tls.
(grokdeclarator): Likewise.
* parser.c (cp_parser_omp_threadprivate): Likewise.
gcc/fortran/
* f95-lang.c (gfc_init_builtin_functions): Add __emutls_get_address
and __emutls_register_common.
* openmp.c (gfc_match_omp_threadprivate): Don't error if !have_tls.
* trans-common.c (build_common_decl): Don't check have_tls.
* trans-decl.c (gfc_finish_var_decl): Likewise.
* types.def (BT_WORD, BT_FN_PTR_PTR): New.
(BT_FN_VOID_PTR_WORD_WORD_PTR): New.
gcc/testsuite/
* lib/target-supports.exp (check_effective_target_tls): Redefine
to mean non-emulated tls.
* gcc.dg/tls/alias-1.c: Remove tls requirement.
* gcc.dg/tls/asm-1.c, gcc.dg/tls/debug-1.c, gcc.dg/tls/diag-1.c,
gcc.dg/tls/diag-2.c, gcc.dg/tls/diag-3.c, gcc.dg/tls/diag-4.c,
gcc.dg/tls/diag-5.c, gcc.dg/tls/init-1.c, gcc.dg/tls/nonpic-1.c,
gcc.dg/tls/opt-10.c, gcc.dg/tls/opt-5.c, gcc.dg/tls/opt-6.c,
gcc.dg/tls/opt-8.c, gcc.dg/tls/opt-9.c, gcc.dg/tls/pic-1.c,
gcc.dg/tls/struct-1.c, gcc.dg/tls/trivial.c: Likewise.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@117440 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/varasm.c')
-rw-r--r-- | gcc/varasm.c | 294 |
1 files changed, 293 insertions, 1 deletions
diff --git a/gcc/varasm.c b/gcc/varasm.c index c45a0f74b7e..ff3e443ef2c 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -53,6 +53,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "cgraph.h" #include "cfglayout.h" #include "basic-block.h" +#include "tree-iterator.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data @@ -200,6 +201,236 @@ static GTY(()) int anchor_labelno; /* A pool of constants that can be shared between functions. */ static GTY(()) struct rtx_constant_pool *shared_constant_pool; +/* TLS emulation. */ + +static GTY ((if_marked ("tree_map_marked_p"), param_is (struct tree_map))) + htab_t emutls_htab; +static GTY (()) tree emutls_object_type; + +#ifndef NO_DOT_IN_LABEL +# define EMUTLS_VAR_PREFIX "__emutls_v." +# define EMUTLS_TMPL_PREFIX "__emutls_t." +#elif !defined NO_DOLLAR_IN_LABEL +# define EMUTLS_VAR_PREFIX "__emutls_v$" +# define EMUTLS_TMPL_PREFIX "__emutls_t$" +#else +# define EMUTLS_VAR_PREFIX "__emutls_v_" +# define EMUTLS_TMPL_PREFIX "__emutls_t_" +#endif + +/* 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) +{ + char *toname = alloca (strlen (IDENTIFIER_POINTER (name)) + + sizeof (EMUTLS_VAR_PREFIX)); + strcpy (toname, EMUTLS_VAR_PREFIX); + strcpy (toname + sizeof (EMUTLS_VAR_PREFIX) - 1, IDENTIFIER_POINTER (name)); + + return get_identifier (toname); +} + +/* 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, next_field, word_type_node; + + type = emutls_object_type; + if (type) + return type; + + emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE); + type_name = get_identifier ("__emutls_object"); + type_name = build_decl (TYPE_DECL, type_name, type); + TYPE_NAME (type) = type_name; + + field = build_decl (FIELD_DECL, get_identifier ("__templ"), ptr_type_node); + DECL_CONTEXT (field) = type; + next_field = field; + + field = build_decl (FIELD_DECL, get_identifier ("__offset"), ptr_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + next_field = field; + + word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); + field = build_decl (FIELD_DECL, get_identifier ("__align"), word_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + next_field = field; + + field = build_decl (FIELD_DECL, get_identifier ("__size"), word_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + + 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; + char *toname; + + if (!DECL_INITIAL (decl)) + return null_pointer_node; + + name = DECL_ASSEMBLER_NAME (decl); + toname = alloca (strlen (IDENTIFIER_POINTER (name)) + + sizeof (EMUTLS_TMPL_PREFIX)); + strcpy (toname, EMUTLS_TMPL_PREFIX); + strcpy (toname + sizeof (EMUTLS_TMPL_PREFIX) - 1, IDENTIFIER_POINTER (name)); + name = get_identifier (toname); + + to = build_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_WEAK (to) = DECL_WEAK (decl); + if (DECL_ONE_ONLY (decl)) + { + make_decl_one_only (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_INITIAL (to) = DECL_INITIAL (decl); + DECL_INITIAL (decl) = NULL; + + cgraph_varpool_finalize_decl (to); + return build_fold_addr_expr (to); +} + +/* When emulating tls, we use a control structure for use by the runtime. + Create and return this structure. */ + +tree +emutls_decl (tree decl) +{ + tree name, to; + struct tree_map *h, in; + void **loc; + + if (targetm.have_tls || decl == NULL || decl == error_mark_node + || TREE_CODE (decl) != VAR_DECL || ! DECL_THREAD_LOCAL_P (decl)) + return decl; + + /* Look up the object in the hash; return the control structure if + it has already been created. */ + if (! emutls_htab) + emutls_htab = htab_create_ggc (512, tree_map_hash, tree_map_eq, 0); + + name = DECL_ASSEMBLER_NAME (decl); + + /* Note that we use the hash of the decl's name, rather than a hash + of the decl's pointer. In emutls_finish we iterate through the + hash table, and we want this traversal to be predictable. */ + in.hash = htab_hash_string (IDENTIFIER_POINTER (name)); + in.from = decl; + loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT); + h = *loc; + if (h != NULL) + to = h->to; + else + { + to = build_decl (VAR_DECL, get_emutls_object_name (name), + get_emutls_object_type ()); + + h = ggc_alloc (sizeof (struct tree_map)); + h->hash = in.hash; + h->from = decl; + h->to = to; + *(struct tree_map **) loc = h; + + DECL_ARTIFICIAL (to) = 1; + DECL_IGNORED_P (to) = 1; + TREE_READONLY (to) = 0; + + SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); + if (DECL_ONE_ONLY (decl)) + make_decl_one_only (to); + DECL_CONTEXT (to) = DECL_CONTEXT (decl); + } + + /* Note that these fields may need to be updated from time to time from + the original decl. Consider: + extern __thread int i; + int foo() { return i; } + __thread int i = 1; + in which I goes from external to locally defined and initialized. */ + + TREE_STATIC (to) = TREE_STATIC (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); + + return to; +} + +static int +emutls_common_1 (void **loc, void *xstmts) +{ + struct tree_map *h = *(struct tree_map **) loc; + tree args, x, *pstmts = (tree *) xstmts; + tree word_type_node; + + if (!DECL_COMMON (h->from)) + return 1; + + word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); + + x = get_emutls_init_templ_addr (h->from); + args = tree_cons (NULL, x, NULL); + x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->from)); + args = tree_cons (NULL, x, args); + x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->from)); + args = tree_cons (NULL, x, args); + x = build_fold_addr_expr (h->to); + args = tree_cons (NULL, x, args); + + x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON]; + x = build_function_call_expr (x, args); + + append_to_statement_list (x, pstmts); + return 1; +} + +void +emutls_finish (void) +{ + tree body = NULL_TREE; + + if (emutls_htab == NULL) + return; + + htab_traverse_noresize (emutls_htab, emutls_common_1, &body); + if (body == NULL_TREE) + return; + + cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY); +} + /* Helper routines for maintaining section_htab. */ static int @@ -1733,6 +1964,50 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, rtx decl_rtl, symbol; section *sect; + if (! targetm.have_tls + && TREE_CODE (decl) == VAR_DECL + && DECL_THREAD_LOCAL_P (decl)) + { + tree to = emutls_decl (decl); + + /* If this variable is defined locally, then we need to initialize the + control structure with size and alignment information. We do this + at the last moment because tentative definitions can take a locally + defined but uninitialized variable and initialize it later, which + would result in incorrect contents. */ + if (! DECL_EXTERNAL (to) && ! DECL_COMMON (to)) + { + 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 = TREE_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 = TREE_CHAIN (field); + elt->index = field; + elt->value = null_pointer_node; + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = TREE_CHAIN (field); + elt->index = field; + elt->value = get_emutls_init_templ_addr (decl); + + DECL_INITIAL (to) = build_constructor (type, v); + } + + decl = to; + } + if (lang_hooks.decls.prepare_assemble_variable) lang_hooks.decls.prepare_assemble_variable (decl); @@ -4856,6 +5131,14 @@ do_assemble_alias (tree decl, tree target) { ultimate_transparent_alias_target (&target); + if (!targetm.have_tls + && TREE_CODE (decl) == VAR_DECL + && DECL_THREAD_LOCAL_P (decl)) + { + decl = emutls_decl (decl); + target = get_emutls_object_name (target); + } + if (!TREE_SYMBOL_REFERENCED (target)) weakref_targets = tree_cons (decl, target, weakref_targets); @@ -4873,6 +5156,14 @@ do_assemble_alias (tree decl, tree target) return; } + if (!targetm.have_tls + && TREE_CODE (decl) == VAR_DECL + && DECL_THREAD_LOCAL_P (decl)) + { + decl = emutls_decl (decl); + target = get_emutls_object_name (target); + } + #ifdef ASM_OUTPUT_DEF /* Make name accessible from other files, if appropriate. */ @@ -5755,7 +6046,8 @@ default_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED) flags |= SYMBOL_FLAG_FUNCTION; if (targetm.binds_local_p (decl)) flags |= SYMBOL_FLAG_LOCAL; - if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) + if (targetm.have_tls && TREE_CODE (decl) == VAR_DECL + && DECL_THREAD_LOCAL_P (decl)) flags |= DECL_TLS_MODEL (decl) << SYMBOL_FLAG_TLS_SHIFT; else if (targetm.in_small_data_p (decl)) flags |= SYMBOL_FLAG_SMALL; |