diff options
46 files changed, 677 insertions, 82 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 584726dbebe..22d12f55f10 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,40 @@ +2007-02-10 Richard Henderson <rth@redhat.com>, Jakub Jelinek <jakub@redhat.com>, Alexandre Oliva <aoliva@redhat.com> + + * Makefile.in (libgcc-support, libgcc.mvars): 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. + * 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. + * varpool.c (decide_is_variable_needed): Look at force_output. + Recurse for emulated tls. + (cgraph_varpool_remove_unreferenced_decls): Remove checks redundant + with decide_is_variable_needed. + * emutls.c: New file. + * config/sparc/sol2.h (ASM_DECLARE_OBJECT_NAME): Only emit + tls_object for real tls. + 2007-02-10 Kaz Kojima <kkojima@gcc.gnu.org> PR rtl-optimization/29599 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 6b8882608d3..7bb1434b0e9 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1566,7 +1566,7 @@ GCC_EXTRA_PARTS := $(sort $(EXTRA_MULTILIB_PARTS) $(EXTRA_PARTS)) libgcc-support: libgcc.mvars stmp-int-hdrs $(STMP_FIXPROTO) $(TCONFIG_H) \ $(MACHMODE_H) $(FPBIT) $(DPBIT) $(TPBIT) $(LIB2ADD) \ - $(LIB2ADD_ST) $(LIB2ADDEH) gcov-iov.h $(SFP_MACHINE) + $(LIB2ADD_ST) $(LIB2ADDEH) $(srcdir)/emutls.c gcov-iov.h $(SFP_MACHINE) libgcc.mvars: config.status Makefile $(LIB2ADD) $(LIB2ADD_ST) specs \ xgcc$(exeext) stamp-as stamp-collect-ld stamp-nm @@ -1578,9 +1578,9 @@ libgcc.mvars: config.status Makefile $(LIB2ADD) $(LIB2ADD_ST) specs \ echo LIBGCOV = '$(LIBGCOV)' >> tmp-libgcc.mvars echo LIB2ADD = '$(call srcdirify,$(LIB2ADD))' >> tmp-libgcc.mvars echo LIB2ADD_ST = '$(call srcdirify,$(LIB2ADD_ST))' >> tmp-libgcc.mvars - echo LIB2ADDEH = '$(call srcdirify,$(LIB2ADDEH))' >> tmp-libgcc.mvars - echo LIB2ADDEHSTATIC = '$(call srcdirify,$(LIB2ADDEHSTATIC))' >> tmp-libgcc.mvars - echo LIB2ADDEHSHARED = '$(call srcdirify,$(LIB2ADDEHSHARED))' >> tmp-libgcc.mvars + echo LIB2ADDEH = '$(call srcdirify,$(LIB2ADDEH) $(srcdir)/emutls.c)' >> tmp-libgcc.mvars + echo LIB2ADDEHSTATIC = '$(call srcdirify,$(LIB2ADDEHSTATIC) $(srcdir)/emutls.c)' >> tmp-libgcc.mvars + echo LIB2ADDEHSHARED = '$(call srcdirify,$(LIB2ADDEHSHARED) $(srcdir)/emutls.c)' >> tmp-libgcc.mvars echo LIB2_SIDITI_CONV_FUNCS = '$(LIB2_SIDITI_CONV_FUNCS)' >> tmp-libgcc.mvars echo LIBUNWIND = '$(call srcdirify,$(LIBUNWIND))' >> tmp-libgcc.mvars echo SHLIBUNWIND_LINK = '$(SHLIBUNWIND_LINK)' >> tmp-libgcc.mvars diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index bac7480438c..ea0ff8e915e 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -77,7 +77,7 @@ DEF_PRIMITIVE_TYPE (BT_INTMAX, intmax_type_node) DEF_PRIMITIVE_TYPE (BT_UINTMAX, uintmax_type_node) DEF_PRIMITIVE_TYPE (BT_UINT32, uint32_type_node) DEF_PRIMITIVE_TYPE (BT_UINT64, uint64_type_node) -DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 0)) +DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 1)) DEF_PRIMITIVE_TYPE (BT_FLOAT, float_type_node) DEF_PRIMITIVE_TYPE (BT_DOUBLE, double_type_node) DEF_PRIMITIVE_TYPE (BT_LONGDOUBLE, long_double_type_node) @@ -388,6 +388,8 @@ DEF_FUNCTION_TYPE_4 (BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING, BT_VALIST_ARG) DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT, BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT) +DEF_FUNCTION_TYPE_4 (BT_FN_VOID_PTR_WORD_WORD_PTR, + BT_VOID, BT_PTR, BT_WORD, BT_WORD, BT_PTR) DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG, BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING, diff --git a/gcc/builtins.def b/gcc/builtins.def index b5a88cd2bf1..48c97bf912e 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -1,6 +1,6 @@ /* This file contains the definitions and documentation for the builtins used in the GNU compiler. - Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GCC. @@ -729,6 +729,10 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_VPRINTF_CHK, "__vprintf_chk", BT_FN_INT_INT_CON DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_ENTER, "profile_func_enter") DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit") +/* TLS emulation. */ +DEF_EXT_LIB_BUILTIN (BUILT_IN_EMUTLS_GET_ADDRESS, "__emutls_get_address", BT_FN_PTR_PTR, ATTR_CONST_NOTHROW_NONNULL) +DEF_EXT_LIB_BUILTIN (BUILT_IN_EMUTLS_REGISTER_COMMON, "__emutls_register_common", BT_FN_VOID_PTR_WORD_WORD_PTR, ATTR_NOTHROW_LIST) + /* Synchronization Primitives. */ #include "sync-builtins.def" diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 3bdf70e12ba..648c805828d 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -4927,14 +4927,7 @@ grokdeclarator (const struct c_declarator *declarator, } if (threadp) - { - if (targetm.have_tls) - DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); - else - /* A mere warning is sure to result in improper semantics - at runtime. Don't bother to allow this to compile. */ - error ("thread-local storage not supported for this target"); - } + DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); } if (storage_class == csc_extern diff --git a/gcc/c-parser.c b/gcc/c-parser.c index b688b15673f..665b494e231 100644 --- a/gcc/c-parser.c +++ b/gcc/c-parser.c @@ -7783,9 +7783,6 @@ c_parser_omp_threadprivate (c_parser *parser) c_parser_consume_pragma (parser); vars = c_parser_omp_var_list_parens (parser, 0, NULL); - if (!targetm.have_tls) - sorry ("threadprivate variables not supported in this target"); - /* Mark every variable in VARS to be assigned thread local storage. */ for (t = vars; t; t = TREE_CHAIN (t)) { diff --git a/gcc/config/sparc/sol2.h b/gcc/config/sparc/sol2.h index d07e0c63685..7a79a7d7112 100644 --- a/gcc/config/sparc/sol2.h +++ b/gcc/config/sparc/sol2.h @@ -89,7 +89,7 @@ Boston, MA 02110-1301, USA. */ { \ HOST_WIDE_INT size; \ \ - if (DECL_THREAD_LOCAL_P (DECL)) \ + if (targetm.have_tls && DECL_THREAD_LOCAL_P (DECL)) \ ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "tls_object"); \ else \ ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "object"); \ diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 59f7260dfb0..55739107945 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,9 @@ +2007-02-10 Richard Henderson <rth@redhat.com>, Jakub Jelinek <jakub@redhat.com> + + * decl.c (grokvardecl): Don't error if !have_tls. + (grokdeclarator): Likewise. + * parser.c (cp_parser_omp_threadprivate): Likewise. + 2007-02-07 Jakub Jelinek <jakub@redhat.com> PR c++/30703 diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index e6f06640037..b6d0e5387f7 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6363,14 +6363,7 @@ grokvardecl (tree type, } if (declspecs->specs[(int)ds_thread]) - { - if (targetm.have_tls) - DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); - else - /* A mere warning is sure to result in improper semantics - at runtime. Don't bother to allow this to compile. */ - error ("thread-local storage not supported for this target"); - } + DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); if (TREE_PUBLIC (decl)) { @@ -8539,15 +8532,7 @@ grokdeclarator (const cp_declarator *declarator, DECL_EXTERNAL (decl) = 1; if (thread_p) - { - if (targetm.have_tls) - DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); - else - /* A mere warning is sure to result in improper - semantics at runtime. Don't bother to allow this to - compile. */ - error ("thread-local storage not supported for this target"); - } + DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); } else { diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 6aef931fc7d..8e130b64cfd 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -19312,9 +19312,6 @@ cp_parser_omp_threadprivate (cp_parser *parser, cp_token *pragma_tok) vars = cp_parser_omp_var_list (parser, 0, NULL); cp_parser_require_pragma_eol (parser, pragma_tok); - if (!targetm.have_tls) - sorry ("threadprivate variables not supported in this target"); - finish_omp_threadprivate (vars); } diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index d529d8c879d..5516f3f2617 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -9226,7 +9226,7 @@ loc_descriptor_from_tree_1 (tree loc, int want_address) rtx rtl; /* If this is not defined, we have no way to emit the data. */ - if (!targetm.asm_out.output_dwarf_dtprel) + if (!targetm.have_tls || !targetm.asm_out.output_dwarf_dtprel) return 0; /* The way DW_OP_GNU_push_tls_address is specified, we can only diff --git a/gcc/emutls.c b/gcc/emutls.c index e69de29bb2d..f26d21772e3 100644 --- a/gcc/emutls.c +++ b/gcc/emutls.c @@ -0,0 +1,193 @@ +/* TLS emulation. + Copyright (C) 2006 Free Software Foundation, Inc. + Contributed by Jakub Jelinek <jakub@redhat.com>. + +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 2, or (at your option) any later +version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "coretypes.h" +#include "tm.h" +#include "gthr.h" + +typedef unsigned int word __attribute__((mode(word))); +typedef unsigned int pointer __attribute__((mode(pointer))); + +struct __emutls_object +{ + word size; + word align; + union { + pointer offset; + void *ptr; + } loc; + void *templ; +}; + +#ifdef __GTHREADS +#ifdef __GTHREAD_MUTEX_INIT +static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT; +#else +static __gthread_mutex_t emutls_mutex; +#endif +static __gthread_key_t emutls_key; +static pointer emutls_size; + +static void +emutls_destroy (void *ptr) +{ + void ***arr = (void ***) ptr; + unsigned long int size = (unsigned long int) arr[0]; + ++arr; + while (--size) + { + if (*arr) + free ((*arr)[-1]); + ++arr; + } + free (ptr); +} + +static void +emutls_init (void) +{ +#ifndef __GTHREAD_MUTEX_INIT + __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex); +#endif + if (__gthread_key_create (&emutls_key, emutls_destroy) != 0) + abort (); +} +#endif + +static void * +emutls_alloc (struct __emutls_object *obj) +{ + void *ptr; + void *ret; + + /* We could use here posix_memalign if available and adjust + emutls_destroy accordingly. */ + if (obj->align <= sizeof (void *)) + { + ptr = malloc (obj->size + sizeof (void *)); + if (ptr == NULL) + abort (); + ((void **) ptr)[0] = ptr; + ret = ptr + sizeof (void *); + } + else + { + ptr = malloc (obj->size + sizeof (void *) + obj->align - 1); + if (ptr == NULL) + abort (); + ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1)) + & ~(pointer)(obj->align - 1)); + ((void **) ret)[-1] = ptr; + } + + if (obj->templ) + memcpy (ret, obj->templ, obj->size); + else + memset (ret, 0, obj->size); + + return ret; +} + +void * +__emutls_get_address (struct __emutls_object *obj) +{ + if (! __gthread_active_p ()) + { + if (__builtin_expect (obj->loc.ptr == NULL, 0)) + obj->loc.ptr = emutls_alloc (obj); + return obj->loc.ptr; + } + +#ifndef __GTHREADS + abort (); +#else + pointer offset; + + if (__builtin_expect (obj->loc.offset == 0, 0)) + { + static __gthread_once_t once = __GTHREAD_ONCE_INIT; + __gthread_once (&once, emutls_init); + __gthread_mutex_lock (&emutls_mutex); + offset = ++emutls_size; + obj->loc.offset = offset; + __gthread_mutex_unlock (&emutls_mutex); + } + else + offset = obj->loc.offset; + + void **arr = (void **) __gthread_getspecific (emutls_key); + if (__builtin_expect (arr == NULL, 0)) + { + pointer size = offset + 32; + arr = calloc (size, sizeof (void *)); + if (arr == NULL) + abort (); + arr[0] = (void *) size; + __gthread_setspecific (emutls_key, (void *) arr); + } + else if (__builtin_expect (offset >= (pointer) arr[0], 0)) + { + pointer orig_size = (pointer) arr[0]; + pointer size = orig_size * 2; + if (offset >= size) + size = offset + 32; + arr = realloc (arr, size * sizeof (void *)); + if (arr == NULL) + abort (); + memset (arr + orig_size, 0, (size - orig_size) * sizeof (void *)); + __gthread_setspecific (emutls_key, (void *) arr); + } + + void *ret = arr[offset]; + if (__builtin_expect (ret == NULL, 0)) + { + ret = emutls_alloc (obj); + arr[offset] = ret; + } + return ret; +#endif +} + +void +__emutls_register_common (struct __emutls_object *obj, + word size, word align, void *templ) +{ + if (obj->size < size) + { + obj->size = size; + obj->templ = NULL; + } + if (obj->align < align) + obj->align = align; + if (templ && size == obj->size) + obj->templ = templ; +} diff --git a/gcc/expr.c b/gcc/expr.c index a04e931acd0..09a67891eef 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -6421,6 +6421,19 @@ highest_pow2_factor_for_target (tree target, tree exp) return MAX (factor, target_align); } +/* Return &VAR expression for emulated thread local VAR. */ + +static tree +emutls_var_address (tree var) +{ + tree emuvar = emutls_decl (var); + tree fn = built_in_decls [BUILT_IN_EMUTLS_GET_ADDRESS]; + tree arg = build_fold_addr_expr_with_type (emuvar, ptr_type_node); + tree arglist = build_tree_list (NULL_TREE, arg); + tree call = build_function_call_expr (fn, arglist); + return fold_convert (build_pointer_type (TREE_TYPE (var)), call); +} + /* Expands variable VAR. */ void @@ -6549,6 +6562,18 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode, inner = TREE_OPERAND (exp, 0); break; + case VAR_DECL: + /* TLS emulation hook - replace __thread VAR's &VAR with + __emutls_get_address (&_emutls.VAR). */ + if (! targetm.have_tls + && TREE_CODE (exp) == VAR_DECL + && DECL_THREAD_LOCAL_P (exp)) + { + exp = emutls_var_address (exp); + return expand_expr (exp, target, tmode, modifier); + } + /* Fall through. */ + default: /* If the object is a DECL, then expand it for its rtl. Don't bypass expand_expr, as that can have various side effects; LABEL_DECLs for @@ -6924,6 +6949,16 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, && (TREE_STATIC (exp) || DECL_EXTERNAL (exp))) layout_decl (exp, 0); + /* TLS emulation hook - replace __thread vars with + *__emutls_get_address (&_emutls.var). */ + if (! targetm.have_tls + && TREE_CODE (exp) == VAR_DECL + && DECL_THREAD_LOCAL_P (exp)) + { + exp = build_fold_indirect_ref (emutls_var_address (exp)); + return expand_expr_real_1 (exp, target, tmode, modifier, NULL); + } + /* ... fall through ... */ case FUNCTION_DECL: diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index da3698537c3..a0cf78f670b 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,13 @@ +2007-02-10 Richard Henderson <rth@redhat.com>, Jakub Jelinek <jakub@redhat.com> + + * 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. + 2007-02-09 Tobias Burnus <burnus@net-b.de> PR fortran/30512 diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c index bf0ae81cc46..8f9c206c77f 100644 --- a/gcc/fortran/f95-lang.c +++ b/gcc/fortran/f95-lang.c @@ -1136,6 +1136,14 @@ gfc_init_builtin_functions (void) BUILT_IN_TRAP, NULL, false); TREE_THIS_VOLATILE (built_in_decls[BUILT_IN_TRAP]) = 1; + gfc_define_builtin ("__emutls_get_address", + builtin_types[BT_FN_PTR_PTR], BUILT_IN_EMUTLS_GET_ADDRESS, + "__emutls_get_address", true); + gfc_define_builtin ("__emutls_register_common", + builtin_types[BT_FN_VOID_PTR_WORD_WORD_PTR], + BUILT_IN_EMUTLS_REGISTER_COMMON, + "__emutls_register_common", false); + build_common_builtin_nodes (); targetm.init_builtins (); } diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c index 9694c89743f..42b5aa15dba 100644 --- a/gcc/fortran/openmp.c +++ b/gcc/fortran/openmp.c @@ -469,12 +469,6 @@ gfc_match_omp_threadprivate (void) if (m != MATCH_YES) return m; - if (!targetm.have_tls) - { - sorry ("threadprivate variables not supported in this target"); - goto cleanup; - } - for (;;) { m = gfc_match_symbol (&sym, 0); diff --git a/gcc/fortran/trans-common.c b/gcc/fortran/trans-common.c index 83da32f5234..ea73537471e 100644 --- a/gcc/fortran/trans-common.c +++ b/gcc/fortran/trans-common.c @@ -388,7 +388,7 @@ build_common_decl (gfc_common_head *com, tree union_type, bool is_init) gfc_set_decl_location (decl, &com->where); - if (com->threadprivate && targetm.have_tls) + if (com->threadprivate) DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); /* Place the back end declaration for this common block in diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c index 3b52b9d830b..1bf11e30c29 100644 --- a/gcc/fortran/trans-decl.c +++ b/gcc/fortran/trans-decl.c @@ -538,7 +538,7 @@ gfc_finish_var_decl (tree decl, gfc_symbol * sym) TREE_STATIC (decl) = 1; /* Handle threadprivate variables. */ - if (sym->attr.threadprivate && targetm.have_tls + if (sym->attr.threadprivate && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))) DECL_TLS_MODEL (decl) = decl_default_tls_model (decl); } diff --git a/gcc/fortran/types.def b/gcc/fortran/types.def index 5a3e5d72221..0abd8459d96 100644 --- a/gcc/fortran/types.def +++ b/gcc/fortran/types.def @@ -55,6 +55,7 @@ DEF_PRIMITIVE_TYPE (BT_BOOL, boolean_type_node) DEF_PRIMITIVE_TYPE (BT_INT, integer_type_node) DEF_PRIMITIVE_TYPE (BT_UINT, unsigned_type_node) DEF_PRIMITIVE_TYPE (BT_LONG, long_integer_type_node) +DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 1)) DEF_PRIMITIVE_TYPE (BT_I1, builtin_type_for_size (BITS_PER_UNIT*1, 1)) DEF_PRIMITIVE_TYPE (BT_I2, builtin_type_for_size (BITS_PER_UNIT*2, 1)) @@ -81,6 +82,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_VOID_PTR, BT_VOID, BT_PTR) DEF_FUNCTION_TYPE_1 (BT_FN_VOID_PTRPTR, BT_VOID, BT_PTR_PTR) DEF_FUNCTION_TYPE_1 (BT_FN_VOID_VPTR, BT_VOID, BT_VOLATILE_PTR) DEF_FUNCTION_TYPE_1 (BT_FN_UINT_UINT, BT_UINT, BT_UINT) +DEF_FUNCTION_TYPE_1 (BT_FN_PTR_PTR, BT_PTR, BT_PTR) DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR) @@ -113,6 +115,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_OMPFN_PTR_UINT, BT_VOID, BT_PTR_FN_VOID_PTR, DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT, BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT) +DEF_FUNCTION_TYPE_4 (BT_FN_VOID_PTR_WORD_WORD_PTR, + BT_VOID, BT_PTR, BT_WORD, BT_WORD, BT_PTR) DEF_FUNCTION_TYPE_5 (BT_FN_BOOL_LONG_LONG_LONG_LONGPTR_LONGPTR, BT_BOOL, BT_LONG, BT_LONG, BT_LONG, diff --git a/gcc/libgcc-std.ver b/gcc/libgcc-std.ver index 6e747ed7bc1..4b707944e80 100644 --- a/gcc/libgcc-std.ver +++ b/gcc/libgcc-std.ver @@ -280,4 +280,6 @@ GCC_4.3.0 { # byte swapping routines __bswapsi2 __bswapdi2 + __emutls_get_address + __emutls_register_common } diff --git a/gcc/output.h b/gcc/output.h index 8340d412c69..4e7ccf61308 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -158,6 +158,9 @@ extern void merge_weak (tree, tree); /* Emit any pending weak declarations. */ extern void weak_finish (void); +/* Emit any pending emutls declarations and initializations. */ +extern void emutls_finish (void); + /* Decode an `asm' spec for a declaration as a register name. Return the register number, or -1 if nothing specified, or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6f3a283203e..0998a308522 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2007-02-10 Richard Henderson <rth@redhat.com> + + * 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. + 2007-02-10 Zdenek Dvorak <dvorakz@suse.cz> * gcc.dg/tree-ssa/loop-25.c: Verify the result in the profile pass. diff --git a/gcc/testsuite/gcc.dg/tls/alias-1.c b/gcc/testsuite/gcc.dg/tls/alias-1.c index 28cb47e5041..1098190ebd0 100644 --- a/gcc/testsuite/gcc.dg/tls/alias-1.c +++ b/gcc/testsuite/gcc.dg/tls/alias-1.c @@ -1,7 +1,6 @@ /* { dg-do link } */ /* { dg-require-alias "" } */ /* { dg-require-visibility "" } */ -/* { dg-require-effective-target tls } */ /* Test that encode_section_info handles the change from externally defined to locally defined (via hidden). Extracted from glibc. */ diff --git a/gcc/testsuite/gcc.dg/tls/asm-1.c b/gcc/testsuite/gcc.dg/tls/asm-1.c index b77e550d7bf..476fe7cbb72 100644 --- a/gcc/testsuite/gcc.dg/tls/asm-1.c +++ b/gcc/testsuite/gcc.dg/tls/asm-1.c @@ -1,5 +1,4 @@ /* { dg-options "-Werror" } */ -/* { dg-require-effective-target tls } */ __thread int i; int foo () diff --git a/gcc/testsuite/gcc.dg/tls/debug-1.c b/gcc/testsuite/gcc.dg/tls/debug-1.c index 67d7be69cc5..719f0645771 100644 --- a/gcc/testsuite/gcc.dg/tls/debug-1.c +++ b/gcc/testsuite/gcc.dg/tls/debug-1.c @@ -1,5 +1,4 @@ /* { dg-do assemble } */ /* { dg-options "-g" } */ -/* { dg-require-effective-target tls } */ __thread int i; diff --git a/gcc/testsuite/gcc.dg/tls/diag-1.c b/gcc/testsuite/gcc.dg/tls/diag-1.c index 56b570c94a5..ae4f3d4a3c2 100644 --- a/gcc/testsuite/gcc.dg/tls/diag-1.c +++ b/gcc/testsuite/gcc.dg/tls/diag-1.c @@ -1,5 +1,4 @@ /* Valid __thread specifiers. */ -/* { dg-require-effective-target tls } */ __thread int g1; extern __thread int g2; diff --git a/gcc/testsuite/gcc.dg/tls/diag-2.c b/gcc/testsuite/gcc.dg/tls/diag-2.c index 8276cb3be49..5e7e17bee5a 100644 --- a/gcc/testsuite/gcc.dg/tls/diag-2.c +++ b/gcc/testsuite/gcc.dg/tls/diag-2.c @@ -1,5 +1,4 @@ /* Invalid __thread specifiers. */ -/* { dg-require-effective-target tls } */ __thread extern int g1; /* { dg-error "'__thread' before 'extern'" } */ __thread static int g2; /* { dg-error "'__thread' before 'static'" } */ diff --git a/gcc/testsuite/gcc.dg/tls/diag-3.c b/gcc/testsuite/gcc.dg/tls/diag-3.c index 45d89b43722..f1ce06b70d8 100644 --- a/gcc/testsuite/gcc.dg/tls/diag-3.c +++ b/gcc/testsuite/gcc.dg/tls/diag-3.c @@ -1,5 +1,4 @@ /* Report invalid extern and __thread combinations. */ -/* { dg-require-effective-target tls } */ extern int j; /* { dg-error "previous declaration" } */ __thread int j; /* { dg-error "follows non-thread-local" } */ diff --git a/gcc/testsuite/gcc.dg/tls/diag-4.c b/gcc/testsuite/gcc.dg/tls/diag-4.c index fed2f3accd3..df3705d04ee 100644 --- a/gcc/testsuite/gcc.dg/tls/diag-4.c +++ b/gcc/testsuite/gcc.dg/tls/diag-4.c @@ -1,6 +1,5 @@ /* Invalid __thread specifiers. As diag-4.c but some cases in different orders. */ -/* { dg-require-effective-target tls } */ __thread typedef int g4; /* { dg-error "'__thread' used with 'typedef'" } */ diff --git a/gcc/testsuite/gcc.dg/tls/diag-5.c b/gcc/testsuite/gcc.dg/tls/diag-5.c index 219396d768a..623832c3812 100644 --- a/gcc/testsuite/gcc.dg/tls/diag-5.c +++ b/gcc/testsuite/gcc.dg/tls/diag-5.c @@ -1,4 +1,3 @@ /* __thread specifiers on empty declarations. */ -/* { dg-require-effective-target tls } */ __thread struct foo; /* { dg-warning "warning: useless '__thread' in empty declaration" } */ diff --git a/gcc/testsuite/gcc.dg/tls/init-1.c b/gcc/testsuite/gcc.dg/tls/init-1.c index fa4208dce0c..97258643bf2 100644 --- a/gcc/testsuite/gcc.dg/tls/init-1.c +++ b/gcc/testsuite/gcc.dg/tls/init-1.c @@ -1,5 +1,4 @@ /* Invalid initializations. */ -/* { dg-require-effective-target tls } */ extern __thread int i; int *p = &i; /* { dg-error "initializer element is not constant" } */ diff --git a/gcc/testsuite/gcc.dg/tls/nonpic-1.c b/gcc/testsuite/gcc.dg/tls/nonpic-1.c index 9c592a98556..0896df60b56 100644 --- a/gcc/testsuite/gcc.dg/tls/nonpic-1.c +++ b/gcc/testsuite/gcc.dg/tls/nonpic-1.c @@ -1,6 +1,5 @@ /* { dg-do compile } */ /* { dg-options "-O2 -ftls-model=initial-exec" } */ -/* { dg-require-effective-target tls } */ extern __thread long e1; extern __thread int e2; diff --git a/gcc/testsuite/gcc.dg/tls/opt-10.c b/gcc/testsuite/gcc.dg/tls/opt-10.c index a710a062ca3..f31c1fff816 100644 --- a/gcc/testsuite/gcc.dg/tls/opt-10.c +++ b/gcc/testsuite/gcc.dg/tls/opt-10.c @@ -1,6 +1,5 @@ /* { dg-do compile } */ /* { dg-options "-O3 -fpic" } */ -/* { dg-require-effective-target tls } */ /* The web pass was creating unrecognisable pic_load_dot_plus_four insns on ARM. */ diff --git a/gcc/testsuite/gcc.dg/tls/opt-5.c b/gcc/testsuite/gcc.dg/tls/opt-5.c index 0604f3253c1..d8a686ddb46 100644 --- a/gcc/testsuite/gcc.dg/tls/opt-5.c +++ b/gcc/testsuite/gcc.dg/tls/opt-5.c @@ -1,6 +1,5 @@ /* { dg-do compile } */ /* { dg-options "-O2" } */ -/* { dg-require-effective-target tls } */ /* Sched1 moved {load_tp} pattern between strlen call and the copy of the hard return value to its pseudo. This resulted in a reload abort, since the hard register was not spillable. */ diff --git a/gcc/testsuite/gcc.dg/tls/opt-6.c b/gcc/testsuite/gcc.dg/tls/opt-6.c index 8a01c019c10..de04c1cb3fc 100644 --- a/gcc/testsuite/gcc.dg/tls/opt-6.c +++ b/gcc/testsuite/gcc.dg/tls/opt-6.c @@ -1,6 +1,5 @@ /* { dg-do compile } */ /* { dg-options "-O2" } */ -/* { dg-require-effective-target tls } */ extern void abort (void); extern void exit (int); diff --git a/gcc/testsuite/gcc.dg/tls/opt-8.c b/gcc/testsuite/gcc.dg/tls/opt-8.c index a7331115352..dec0eabcb4c 100644 --- a/gcc/testsuite/gcc.dg/tls/opt-8.c +++ b/gcc/testsuite/gcc.dg/tls/opt-8.c @@ -1,7 +1,6 @@ /* PR 18910 */ /* { dg-do compile } */ /* { dg-options "-O2" } */ -/* { dg-require-effective-target tls } */ static __thread void *foo [2]; void diff --git a/gcc/testsuite/gcc.dg/tls/opt-9.c b/gcc/testsuite/gcc.dg/tls/opt-9.c index cc62ef57a5d..3829c66fc55 100644 --- a/gcc/testsuite/gcc.dg/tls/opt-9.c +++ b/gcc/testsuite/gcc.dg/tls/opt-9.c @@ -1,7 +1,6 @@ /* PR 21412 */ /* { dg-do compile */ /* { dg-options "-O2 -fPIC" } */ -/* { dg-require-effective-target tls } */ struct S { int x[10]; }; extern __thread struct S s; diff --git a/gcc/testsuite/gcc.dg/tls/pic-1.c b/gcc/testsuite/gcc.dg/tls/pic-1.c index bcd42bd8572..f5b020b7db6 100644 --- a/gcc/testsuite/gcc.dg/tls/pic-1.c +++ b/gcc/testsuite/gcc.dg/tls/pic-1.c @@ -1,6 +1,5 @@ /* { dg-do compile } */ /* { dg-options "-O2 -fpic -ftls-model=global-dynamic" } */ -/* { dg-require-effective-target tls } */ extern __thread long e1; extern __thread int e2; diff --git a/gcc/testsuite/gcc.dg/tls/struct-1.c b/gcc/testsuite/gcc.dg/tls/struct-1.c index 5fd6be43905..11151236d90 100644 --- a/gcc/testsuite/gcc.dg/tls/struct-1.c +++ b/gcc/testsuite/gcc.dg/tls/struct-1.c @@ -2,7 +2,6 @@ to allow addends for @dtpoff relocs or not. */ /* { dg-do compile } */ /* { dg-options "-O2 -fpic" } */ -/* { dg-require-effective-target tls } */ struct S { int s0, s1, s2, s3; diff --git a/gcc/testsuite/gcc.dg/tls/trivial.c b/gcc/testsuite/gcc.dg/tls/trivial.c index 96b8e49a665..1fd70631f33 100644 --- a/gcc/testsuite/gcc.dg/tls/trivial.c +++ b/gcc/testsuite/gcc.dg/tls/trivial.c @@ -1,3 +1 @@ -/* { dg-require-effective-target tls } */ - __thread int i; diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index d1daf0fecf0..ec63866614e 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -384,7 +384,7 @@ proc check_effective_target_pcc_bitfield_type_matters { } { }] } -# Return 1 if thread local storage (TLS) is supported, 0 otherwise. +# Return 1 if *native* thread local storage (TLS) is supported, 0 otherwise. # # This won't change for different subtargets so cache the result. @@ -406,11 +406,19 @@ proc check_effective_target_tls {} { close $f # Test for thread-local data supported by the platform. - set comp_output \ - [${tool}_target_compile $src $asm assembly ""] + set comp_output [${tool}_target_compile $src $asm assembly ""] file delete $src if { [string match "*not supported*" $comp_output] } { set et_tls_saved 0 + } else { + set fd [open $asm r] + set text [read $fd] + close $fd + if { [string match "*emutls*" $text]} { + set et_tls_saved 0 + } else { + set et_tls_saved 1 + } } remove-build-file $asm } diff --git a/gcc/toplev.c b/gcc/toplev.c index 56a78b44e60..4ad6f1d983e 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1048,11 +1048,14 @@ compile_file (void) if (flag_mudflap) mudflap_finish_file (); + /* Likewise for emulated thread-local storage. */ + if (!targetm.have_tls) + emutls_finish (); + output_shared_constant_pool (); output_object_blocks (); /* Write out any pending weak symbol declarations. */ - weak_finish (); /* Do dbx symbols. */ diff --git a/gcc/tree-ssa-address.c b/gcc/tree-ssa-address.c index 69a41a48edf..ef3bfb7cbfb 100644 --- a/gcc/tree-ssa-address.c +++ b/gcc/tree-ssa-address.c @@ -1,5 +1,5 @@ /* Memory address lowering and addressing mode selection. - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GCC. @@ -135,10 +135,15 @@ gen_addr_rtx (rtx symbol, rtx base, rtx index, rtx step, rtx offset, act_elem = symbol; if (offset) { - act_elem = gen_rtx_CONST (Pmode, - gen_rtx_PLUS (Pmode, act_elem, offset)); + act_elem = gen_rtx_PLUS (Pmode, act_elem, offset); + if (offset_p) - *offset_p = &XEXP (XEXP (act_elem, 0), 1); + *offset_p = &XEXP (act_elem, 1); + + if (GET_CODE (symbol) == SYMBOL_REF + || GET_CODE (symbol) == LABEL_REF + || GET_CODE (symbol) == CONST) + act_elem = gen_rtx_CONST (Pmode, act_elem); } if (*addr) diff --git a/gcc/tree.h b/gcc/tree.h index 55117805e3d..26e0676d0c2 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -4599,6 +4599,7 @@ extern void set_user_assembler_name (tree, const char *); extern void process_pending_assemble_externals (void); extern void finish_aliases_1 (void); extern void finish_aliases_2 (void); +extern tree emutls_decl (tree); /* In stmt.c */ extern void expand_computed_goto (tree); diff --git a/gcc/varasm.c b/gcc/varasm.c index c2ae6e23f1c..48aba4e4a16 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -1,6 +1,6 @@ /* Output variables, constants and external declarations, for GNU compiler. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, - 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. @@ -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 @@ -199,6 +200,242 @@ 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; + + 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) + || (DECL_INITIAL (h->from) + && DECL_INITIAL (h->from) != error_mark_node)) + return 1; + + word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); + + /* The idea was to call get_emutls_init_templ_addr here, but if we + do this and there is an initializer, -fanchor_section loses, + because it would be too late to ensure the template is + output. */ + x = null_pointer_node; + 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 +1970,59 @@ 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) + || (DECL_INITIAL (decl) + && DECL_INITIAL (decl) != error_mark_node))) + { + 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); + + /* Make sure the template is marked as needed early enough. + Without this, if the variable is placed in a + section-anchored block, the template will only be marked + when it's too late. */ + record_references_in_initializer (to); + } + + decl = to; + } + if (lang_hooks.decls.prepare_assemble_variable) lang_hooks.decls.prepare_assemble_variable (decl); @@ -4856,6 +5146,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 +5171,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. */ @@ -5760,7 +6066,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; diff --git a/gcc/varpool.c b/gcc/varpool.c index a3af8c7c2f5..65c22d491b4 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -1,5 +1,5 @@ /* Callgraph handling code. - Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Contributed by Jan Hubicka This file is part of GCC. @@ -218,7 +218,7 @@ bool decide_is_variable_needed (struct varpool_node *node, tree decl) { /* If the user told us it is used, then it must be so. */ - if (node->externally_visible) + if (node->externally_visible || node->force_output) return true; if (!flag_unit_at_a_time && lookup_attribute ("used", DECL_ATTRIBUTES (decl))) @@ -242,6 +242,17 @@ decide_is_variable_needed (struct varpool_node *node, tree decl) && !DECL_EXTERNAL (decl)) return true; + /* When emulating tls, we actually see references to the control + variable, rather than the user-level variable. */ + if (!targetm.have_tls + && TREE_CODE (decl) == VAR_DECL + && DECL_THREAD_LOCAL_P (decl)) + { + tree control = emutls_decl (decl); + if (decide_is_variable_needed (varpool_node (control), control)) + return true; + } + /* When not reordering top level variables, we have to assume that we are going to keep everything. */ if (flag_unit_at_a_time && flag_toplevel_reorder) @@ -374,10 +385,7 @@ varpool_remove_unreferenced_decls (void) node->needed = 0; if (node->finalized - && ((DECL_ASSEMBLER_NAME_SET_P (decl) - && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) - || node->force_output - || decide_is_variable_needed (node, decl) + && (decide_is_variable_needed (node, decl) /* ??? Cgraph does not yet rule the world with an iron hand, and does not control the emission of debug information. After a variable has its DECL_RTL set, we must assume that |