From 38475469c32d0b66a793399839b363109aea3cc2 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 27 Apr 2008 15:35:19 +0000 Subject: [PATCH] * targhooks.h (default_emutls_var_fields, default_emutls_var_init): Declare. * tree.h (DECL_THREAD_LOCAL): Compare against TLS_MODEL_REAL. * target.h (struct gcc_target): Add struct emutls member. * target-def.h (TARGET_EMUTLS_GET_ADDRESS, TARGET_EMUTLS_REGISTER_COMMON, TARGET_EMUTLS_VAR_SECTION, TARGET_EMUTLS_TMPL_SECTION, TARGET_EMUTLS_VAR_PREFIX, TARGET_EMUTLS_TMPL_PREFIX, TARGET_EMUTLS_VAR_FIELDS, TARGET_EMUTLS_VAR_INIT, TARGET_EMUTLS_DEBUG_FORM_TLS_ADDRESS, TARGET_EMUTLS_VAR_ALIGN_FIXED, TARGET_EMUTLS): New. (TARGET_INITIALIZER): Add TARGET_EMUTLS. * builtins.def (BUILT_IN_EMUTLS_GET_ADDRESS, BUILT_IN_EMUTLS_REGISTER_COMMON): Get name from targetm structure. * dwarf2out.c (loc_descriptor_from_tree_1): Check if emutls can emit debug information. * coretypes.h (tls_model): Add TLS_MODEL_EMULATED, TLS_MODEL_REAL. * varasm.c: Include targhooks.h. (emutls_object_section, emutls_tmpl_section): New. (EMUTLS_VAR_PREFIX, EMUTLS_TMPL_PREFIX): Remove. (EMUTLS_SEPARATOR): New. (prefix_name): New. (get_emutls_object_name): New. (default_emutls_var_fields): New, broken out of ... (get_emutls_object_type): ... here. Adjust to use target hooks. (get_emutls_init_templ_addr): Adjust to use target hooks. (emutls_decl): Adjust to use target hooks. (emutls_finish): Likewise. (default_emutls_var_init): New, broken out of ... (assemble_variable): ... here. Adjust to use target hooks. * output.h (enum section_category): Add SECCAT_EMUTLS_VAR, SECCAT_EMUTLS_TMPL. * c-common.c (handle_section_attribute): Prevent overriding sections for emulated tls with special sections. * config/i386/i386.c (x86_64_elf_select_section): Add SECCAT_EMUTLS_VAR and SECCAT_EMUTLS_TMPL. (x86_64_elf_unique_section): Likewise. * config/vxworks.c: Include tree.h. (vxworks_emutls_var_fields, vxworks_emutls_var_init): New. (vxworks_override_options): Set TLS scheme. * gcc/doc/tm.texi (Emulated TLS): New node. gcc/testsuite/ * gcc.dg/tls/section-2.c: New. * gcc.dg/tls/emutls-1.c: New. * lib/target-supports.exp (check_effective_target_tls_native): Exclude vxworks. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@134729 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 43 +++++ gcc/builtins.def | 12 +- gcc/c-common.c | 7 + gcc/config/i386/i386.c | 34 ++-- gcc/config/vxworks.c | 77 +++++++++ gcc/coretypes.h | 4 +- gcc/doc/tm.texi | 79 ++++++++++ gcc/dwarf2out.c | 41 +++-- gcc/output.h | 5 +- gcc/target-def.h | 56 +++++++ gcc/target.h | 28 ++++ gcc/targhooks.h | 2 + gcc/testsuite/ChangeLog | 7 + gcc/testsuite/gcc.dg/tls/emutls-1.c | 21 +++ gcc/testsuite/gcc.dg/tls/section-2.c | 7 + gcc/testsuite/lib/target-supports.exp | 7 + gcc/tree.h | 2 +- gcc/varasm.c | 287 +++++++++++++++++++++------------- 18 files changed, 584 insertions(+), 135 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tls/emutls-1.c create mode 100644 gcc/testsuite/gcc.dg/tls/section-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6453c50..37d6752 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,46 @@ +2008-04-27 Nathan Sidwell + + * targhooks.h (default_emutls_var_fields, + default_emutls_var_init): Declare. + * tree.h (DECL_THREAD_LOCAL): Compare against TLS_MODEL_REAL. + * target.h (struct gcc_target): Add struct emutls member. + * target-def.h (TARGET_EMUTLS_GET_ADDRESS, + TARGET_EMUTLS_REGISTER_COMMON, TARGET_EMUTLS_VAR_SECTION, + TARGET_EMUTLS_TMPL_SECTION, TARGET_EMUTLS_VAR_PREFIX, + TARGET_EMUTLS_TMPL_PREFIX, TARGET_EMUTLS_VAR_FIELDS, + TARGET_EMUTLS_VAR_INIT, TARGET_EMUTLS_DEBUG_FORM_TLS_ADDRESS, + TARGET_EMUTLS_VAR_ALIGN_FIXED, TARGET_EMUTLS): New. + (TARGET_INITIALIZER): Add TARGET_EMUTLS. + * builtins.def (BUILT_IN_EMUTLS_GET_ADDRESS, + BUILT_IN_EMUTLS_REGISTER_COMMON): Get name from targetm structure. + * dwarf2out.c (loc_descriptor_from_tree_1): Check if emutls can + emit debug information. + * coretypes.h (tls_model): Add TLS_MODEL_EMULATED, TLS_MODEL_REAL. + * varasm.c: Include targhooks.h. + (emutls_object_section, emutls_tmpl_section): New. + (EMUTLS_VAR_PREFIX, EMUTLS_TMPL_PREFIX): Remove. + (EMUTLS_SEPARATOR): New. + (prefix_name): New. + (get_emutls_object_name): New. + (default_emutls_var_fields): New, broken out of ... + (get_emutls_object_type): ... here. Adjust to use target hooks. + (get_emutls_init_templ_addr): Adjust to use target hooks. + (emutls_decl): Adjust to use target hooks. + (emutls_finish): Likewise. + (default_emutls_var_init): New, broken out of ... + (assemble_variable): ... here. Adjust to use target hooks. + * output.h (enum section_category): Add SECCAT_EMUTLS_VAR, + SECCAT_EMUTLS_TMPL. + * c-common.c (handle_section_attribute): Prevent overriding + sections for emulated tls with special sections. + * config/i386/i386.c (x86_64_elf_select_section): Add + SECCAT_EMUTLS_VAR and SECCAT_EMUTLS_TMPL. + (x86_64_elf_unique_section): Likewise. + * config/vxworks.c: Include tree.h. + (vxworks_emutls_var_fields, vxworks_emutls_var_init): New. + (vxworks_override_options): Set TLS scheme. + * gcc/doc/tm.texi (Emulated TLS): New node. + 2008-04-26 Simon Baldwin PR c/35652 diff --git a/gcc/builtins.def b/gcc/builtins.def index dd6741e..722f81c 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -745,8 +745,16 @@ 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) +DEF_BUILTIN (BUILT_IN_EMUTLS_GET_ADDRESS, targetm.emutls.get_address, + BUILT_IN_NORMAL, + BT_FN_PTR_PTR, BT_FN_PTR_PTR, + true, true, true, ATTR_CONST_NOTHROW_NONNULL, false, + !targetm.have_tls) +DEF_BUILTIN (BUILT_IN_EMUTLS_REGISTER_COMMON, + targetm.emutls.register_common, BUILT_IN_NORMAL, + BT_FN_VOID_PTR_WORD_WORD_PTR, BT_FN_VOID_PTR_WORD_WORD_PTR, + true, true, true, ATTR_NOTHROW_LIST, false, + !targetm.have_tls) /* Synchronization Primitives. */ #include "sync-builtins.def" diff --git a/gcc/c-common.c b/gcc/c-common.c index d110b32..5858523 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -5476,6 +5476,13 @@ handle_section_attribute (tree *node, tree ARG_UNUSED (name), tree args, *node); *no_add_attrs = true; } + else if (TREE_CODE (decl) == VAR_DECL + && !targetm.have_tls && targetm.emutls.tmpl_section + && DECL_THREAD_LOCAL_P (decl)) + { + error ("section of %q+D cannot be overridden", *node); + *no_add_attrs = true; + } else DECL_SECTION_NAME (decl) = TREE_VALUE (args); } diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index dee2410..43c669f 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -2854,6 +2854,9 @@ x86_64_elf_select_section (tree decl, int reloc, /* We don't split these for medium model. Place them into default sections and hope for best. */ break; + case SECCAT_EMUTLS_VAR: + case SECCAT_EMUTLS_TMPL: + gcc_unreachable (); } if (sname) { @@ -2890,16 +2893,16 @@ x86_64_elf_unique_section (tree decl, int reloc) case SECCAT_DATA_REL_LOCAL: case SECCAT_DATA_REL_RO: case SECCAT_DATA_REL_RO_LOCAL: - prefix = one_only ? ".gnu.linkonce.ld." : ".ldata."; + prefix = one_only ? ".ld" : ".ldata"; break; case SECCAT_BSS: - prefix = one_only ? ".gnu.linkonce.lb." : ".lbss."; + prefix = one_only ? ".lb" : ".lbss"; break; case SECCAT_RODATA: case SECCAT_RODATA_MERGE_STR: case SECCAT_RODATA_MERGE_STR_INIT: case SECCAT_RODATA_MERGE_CONST: - prefix = one_only ? ".gnu.linkonce.lr." : ".lrodata."; + prefix = one_only ? ".lr" : ".lrodata"; break; case SECCAT_SRODATA: case SECCAT_SDATA: @@ -2911,23 +2914,28 @@ x86_64_elf_unique_section (tree decl, int reloc) /* We don't split these for medium model. Place them into default sections and hope for best. */ break; + case SECCAT_EMUTLS_VAR: + prefix = targetm.emutls.var_section; + break; + case SECCAT_EMUTLS_TMPL: + prefix = targetm.emutls.tmpl_section; + break; } if (prefix) { - const char *name; - size_t nlen, plen; + const char *name, *linkonce; char *string; - plen = strlen (prefix); name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); name = targetm.strip_name_encoding (name); - nlen = strlen (name); - - string = (char *) alloca (nlen + plen + 1); - memcpy (string, prefix, plen); - memcpy (string + plen, name, nlen + 1); - - DECL_SECTION_NAME (decl) = build_string (nlen + plen, string); + + /* If we're using one_only, then there needs to be a .gnu.linkonce + prefix to the section name. */ + linkonce = one_only ? ".gnu.linkonce" : ""; + + string = ACONCAT ((linkonce, prefix, ".", name, NULL)); + + DECL_SECTION_NAME (decl) = build_string (strlen (string), string); return; } } diff --git a/gcc/config/vxworks.c b/gcc/config/vxworks.c index 4fde6ad..709791b 100644 --- a/gcc/config/vxworks.c +++ b/gcc/config/vxworks.c @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "toplev.h" #include "output.h" #include "tm.h" +#include "tree.h" /* Like default_named_section_asm_out_constructor, except that even constructors with DEFAULT_INIT_PRIORITY must go in a numbered @@ -56,11 +57,87 @@ vxworks_asm_out_destructor (rtx symbol, int priority) assemble_addr_to_section (symbol, sec); } +/* Return the list of FIELD_DECLs that make up an emulated TLS + variable's control object. TYPE is the structure these are fields + of and *NAME will be filled in with the structure tag that should + be used. */ + +static tree +vxworks_emutls_var_fields (tree type, tree *name) +{ + tree field, next_field; + + *name = get_identifier ("__tls_var"); + + field = build_decl (FIELD_DECL, get_identifier ("size"), + unsigned_type_node); + DECL_CONTEXT (field) = type; + next_field = field; + + field = build_decl (FIELD_DECL, get_identifier ("module_id"), + unsigned_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + next_field = field; + + field = build_decl (FIELD_DECL, get_identifier ("offset"), + unsigned_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + + return field; +} + +/* Return the CONSTRUCTOR to initialize an emulated TLS control + object. VAR is the control object. DECL is the TLS object itself + and TMPL_ADDR is the address (an ADDR_EXPR) of the initializer for + that object. */ + +static tree +vxworks_emutls_var_init (tree var, tree decl, tree tmpl_addr) +{ + VEC(constructor_elt,gc) *v = VEC_alloc (constructor_elt, gc, 3); + constructor_elt *elt; + + tree type = TREE_TYPE (var); + tree field = TYPE_FIELDS (type); + + elt = VEC_quick_push (constructor_elt, v, NULL); + elt->index = field; + elt->value = fold_convert (TREE_TYPE (field), tmpl_addr); + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = TREE_CHAIN (field); + elt->index = field; + elt->value = build_int_cst (TREE_TYPE (field), 0); + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = TREE_CHAIN (field); + elt->index = field; + elt->value = fold_convert (TREE_TYPE (field), DECL_SIZE_UNIT (decl)); + + return build_constructor (type, v); +} + /* Do VxWorks-specific parts of OVERRIDE_OPTIONS. */ void vxworks_override_options (void) { + /* We don't support __thread via target hooks. */ + targetm.have_tls = false; + + targetm.emutls.get_address = "__builtin___tls_lookup"; + targetm.emutls.register_common = NULL; + targetm.emutls.var_section = ".tls_vars"; + targetm.emutls.tmpl_section = ".tls_data"; + targetm.emutls.var_prefix = "__tls__"; + targetm.emutls.tmpl_prefix = ""; + targetm.emutls.var_fields = vxworks_emutls_var_fields; + targetm.emutls.var_init = vxworks_emutls_var_init; + targetm.emutls.var_align_fixed = true; + targetm.emutls.debug_form_tls_address = true; + /* We can use .ctors/.dtors sections only in RTP mode. */ targetm.have_ctors_dtors = TARGET_VXWORKS_RTP; diff --git a/gcc/coretypes.h b/gcc/coretypes.h index 03eb0de..4fe480f 100644 --- a/gcc/coretypes.h +++ b/gcc/coretypes.h @@ -69,7 +69,9 @@ struct cpp_reader; to it, so it's here. */ enum tls_model { TLS_MODEL_NONE, - TLS_MODEL_GLOBAL_DYNAMIC, + TLS_MODEL_EMULATED, + TLS_MODEL_REAL, + TLS_MODEL_GLOBAL_DYNAMIC = TLS_MODEL_REAL, TLS_MODEL_LOCAL_DYNAMIC, TLS_MODEL_INITIAL_EXEC, TLS_MODEL_LOCAL_EXEC diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 869bb1b..6ce3b4a 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -50,6 +50,7 @@ through the macros defined in the @file{.h} file. * Floating Point:: Handling floating point for cross-compilers. * Mode Switching:: Insertion of mode-switching instructions. * Target Attributes:: Defining target-specific uses of @code{__attribute__}. +* Emulated TLS:: Emulated TLS support. * MIPS Coprocessors:: MIPS coprocessor support and how to customize it. * PCH Target:: Validity checking for precompiled headers. * C++ ABI:: Controlling C++ ABI changes. @@ -9192,6 +9193,84 @@ attributes, @code{false} otherwise. By default, if a function has a target specific attribute attached to it, it will not be inlined. @end deftypefn +@node Emulated TLS +@section Emulating TLS +@cindex Emulated TLS + +For targets whose psABI does not provide Thread Local Storage via +specific relocations and instruction sequences, an emulation layer is +used. A set of target hooks allows this emulation layer to be +configured for the requirements of a particular target. For instance +the psABI may infact specify TLS support in terms of an emulation +layer. + +The emulation layer works by creating a control object for every TLS +object. To access the TLS object, a lookup function is provided +which, when given the address of the control object, will return the +address of the current thread's instance of the TLS object. + +@deftypevr {Target Hook} {const char *} TARGET_EMUTLS_GET_ADDRESS +Contains the name of the helper function that uses a TLS control +object to locate a TLS instance. The default causes libgcc's +emulated TLS helper function to be used. +@end deftypevr + +@deftypevr {Target Hook} {const char *} TARGET_EMUTLS_REGISTER_COMMON +Contains the name of the helper function that should be used at +program startup to register TLS objects that are implicitly +initialized to zero. If this is @code{NULL}, all TLS objects will +have explicit initializers. The default causes libgcc's emulated TLS +registration function to be used. +@end deftypevr + +@deftypevr {Target Hook} {const char *} TARGET_EMUTLS_VAR_SECTION +Contains the name of the section in which TLS control variables should +be placed. The default of @code{NULL} allows these to be placed in +any section. +@end deftypevr + +@deftypevr {Target Hook} {const char *} TARGET_EMUTLS_TMPL_SECTION +Contains the name of the section in which TLS initializers should be +placed. The default of @code{NULL} allows these to be placed in any +section. +@end deftypevr + +@deftypevr {Target Hook} {const char *} TARGET_EMUTLS_VAR_PREFIX +Contains the prefix to be prepended to TLS control variable names. +The default of @code{NULL} uses a target-specific prefix. +@end deftypevr + +@deftypevr {Target Hook} {const char *} TARGET_EMUTLS_TMPL_PREFIX +Contains the prefix to be prepended to TLS initializer objects. The +default of @code{NULL} uses a target-specific prefix. +@end deftypevr + +@deftypefn {Target Hook} tree TARGET_EMUTLS_VAR_FIELDS (tree @var{type}, tree *@var{name}) +Specifies a function that generates the FIELD_DECLs for a TLS control +object type. @var{type} is the RECORD_TYPE the fields are for and +@var{name} should be filled with the structure tag, if the default of +@code{__emutls_object} is unsuitable. The default creates a type suitable +for libgcc's emulated TLS function. +@end deftypefn + +@deftypefn {Target Hook} tree TARGET_EMUTLS_VAR_INIT (tree @var{var}, tree @var{decl}, tree @var{tmpl_addr}) +Specifies a function that generates the CONSTRUCTOR to initialize a +TLS control object. @var{var} is the TLS control object, @var{decl} +is the TLS object and @var{tmpl_addr} is the address of the +initializer. The default initializes libgcc's emulated TLS control object. +@end deftypefn + +@deftypevr {Target Hook} {bool} TARGET_EMUTLS_VAR_ALIGN_FIXED +Specifies whether the alignment of TLS control variable objects is +fixed and should not be increased as some backends may do to optimize +single objects. The default is false. +@end deftypevr + +@deftypevr {Target Hook} {bool} TARGET_EMUTLS_DEBUG_FORM_TLS_ADDRESS +Specifies whether a DWARF @code{DW_OP_form_tls_address} location descriptor +may be used to describe emulated TLS control objects. +@end deftypevr + @node MIPS Coprocessors @section Defining coprocessor specifics for MIPS targets. @cindex MIPS coprocessor-definition macros diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 7625947..83b3fb5 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -9494,16 +9494,33 @@ loc_descriptor_from_tree_1 (tree loc, int want_address) if (DECL_THREAD_LOCAL_P (loc)) { rtx rtl; + unsigned first_op; + unsigned second_op; - /* If this is not defined, we have no way to emit the data. */ - 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 - look up addresses of objects in the current module. */ - if (DECL_EXTERNAL (loc)) - return 0; - + if (targetm.have_tls) + { + /* If this is not defined, we have no way to emit the + data. */ + if (!targetm.asm_out.output_dwarf_dtprel) + return 0; + + /* The way DW_OP_GNU_push_tls_address is specified, we + can only look up addresses of objects in the current + module. */ + if (DECL_EXTERNAL (loc)) + return 0; + first_op = INTERNAL_DW_OP_tls_addr; + second_op = DW_OP_GNU_push_tls_address; + } + else + { + if (!targetm.emutls.debug_form_tls_address) + return 0; + loc = emutls_decl (loc); + first_op = DW_OP_addr; + second_op = DW_OP_form_tls_address; + } + rtl = rtl_for_decl_location (loc); if (rtl == NULL_RTX) return 0; @@ -9514,11 +9531,11 @@ loc_descriptor_from_tree_1 (tree loc, int want_address) if (! CONSTANT_P (rtl)) return 0; - ret = new_loc_descr (INTERNAL_DW_OP_tls_addr, 0, 0); + ret = new_loc_descr (first_op, 0, 0); ret->dw_loc_oprnd1.val_class = dw_val_class_addr; ret->dw_loc_oprnd1.v.val_addr = rtl; - - ret1 = new_loc_descr (DW_OP_GNU_push_tls_address, 0, 0); + + ret1 = new_loc_descr (second_op, 0, 0); add_loc_descr (&ret, ret1); have_address = 1; diff --git a/gcc/output.h b/gcc/output.h index ae8404e..15daf5b 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -460,7 +460,10 @@ enum section_category SECCAT_BSS, SECCAT_SBSS, - SECCAT_TBSS + SECCAT_TBSS, + + SECCAT_EMUTLS_VAR, + SECCAT_EMUTLS_TMPL }; /* Information that is provided by all instances of the section type. */ diff --git a/gcc/target-def.h b/gcc/target-def.h index 118e979..8566786 100644 --- a/gcc/target-def.h +++ b/gcc/target-def.h @@ -692,6 +692,61 @@ TARGET_CXX_ADJUST_CLASS_AT_DEFINITION \ } +/* EMUTLS specific */ +#ifndef TARGET_EMUTLS_GET_ADDRESS +#define TARGET_EMUTLS_GET_ADDRESS "__builtin___emutls_get_address" +#endif + +#ifndef TARGET_EMUTLS_REGISTER_COMMON +#define TARGET_EMUTLS_REGISTER_COMMON "__builtin___emutls_register_common" +#endif + +#ifndef TARGET_EMUTLS_VAR_SECTION +#define TARGET_EMUTLS_VAR_SECTION NULL +#endif + +#ifndef TARGET_EMUTLS_TMPL_SECTION +#define TARGET_EMUTLS_TMPL_SECTION NULL +#endif + +#ifndef TARGET_EMUTLS_VAR_PREFIX +#define TARGET_EMUTLS_VAR_PREFIX NULL +#endif + +#ifndef TARGET_EMUTLS_TMPL_PREFIX +#define TARGET_EMUTLS_TMPL_PREFIX NULL +#endif + +#ifndef TARGET_EMUTLS_VAR_FIELDS +#define TARGET_EMUTLS_VAR_FIELDS default_emutls_var_fields +#endif + +#ifndef TARGET_EMUTLS_VAR_INIT +#define TARGET_EMUTLS_VAR_INIT default_emutls_var_init +#endif + +#ifndef TARGET_EMUTLS_VAR_ALIGN_FIXED +#define TARGET_EMUTLS_VAR_ALIGN_FIXED false +#endif + +#ifndef TARGET_EMUTLS_DEBUG_FORM_TLS_ADDRESS +#define TARGET_EMUTLS_DEBUG_FORM_TLS_ADDRESS false +#endif + +#define TARGET_EMUTLS \ + { \ + TARGET_EMUTLS_GET_ADDRESS, \ + TARGET_EMUTLS_REGISTER_COMMON, \ + TARGET_EMUTLS_VAR_SECTION, \ + TARGET_EMUTLS_TMPL_SECTION, \ + TARGET_EMUTLS_VAR_PREFIX, \ + TARGET_EMUTLS_TMPL_PREFIX, \ + TARGET_EMUTLS_VAR_FIELDS, \ + TARGET_EMUTLS_VAR_INIT, \ + TARGET_EMUTLS_VAR_ALIGN_FIXED, \ + TARGET_EMUTLS_DEBUG_FORM_TLS_ADDRESS \ + } + /* The whole shebang. */ #define TARGET_INITIALIZER \ { \ @@ -783,6 +838,7 @@ TARGET_INSTANTIATE_DECLS, \ TARGET_C, \ TARGET_CXX, \ + TARGET_EMUTLS, \ TARGET_EXTRA_LIVE_ON_ENTRY, \ TARGET_UNWIND_TABLES_DEFAULT, \ TARGET_HAVE_NAMED_SECTIONS, \ diff --git a/gcc/target.h b/gcc/target.h index 128a029..c331020 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -917,6 +917,34 @@ struct gcc_target void (*adjust_class_at_definition) (tree type); } cxx; + /* Functions and data for emulated TLS support. */ + struct emutls { + /* Name of the address and common functions. */ + const char *get_address; + const char *register_common; + + /* Prefixes for proxy variable and template. */ + const char *var_section; + const char *tmpl_section; + + /* Prefixes for proxy variable and template. */ + const char *var_prefix; + const char *tmpl_prefix; + + /* Function to generate field definitions of the proxy variable. */ + tree (*var_fields) (tree, tree *); + + /* Function to initialize a proxy variable. */ + tree (*var_init) (tree, tree, tree); + + /* Whether we are allowed to alter the usual alignment of the + proxy variable. */ + bool var_align_fixed; + + /* Whether we can emit debug information for TLS vars. */ + bool debug_form_tls_address; + } emutls; + /* For targets that need to mark extra registers as live on entry to the function, they should define this target hook and set their bits in the bitmap passed in. */ diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 5576f98..c94dd06 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -94,3 +94,5 @@ extern void hook_void_bitmap (bitmap); extern bool default_handle_c_option (size_t, const char *, int); extern int default_reloc_rw_mask (void); extern tree default_mangle_decl_assembler_name (tree, tree); +extern tree default_emutls_var_fields (tree, tree *); +extern tree default_emutls_var_init (tree, tree, tree); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d99b845..4c1397c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2008-04-27 Nathan Sidwell + + * gcc.dg/tls/section-2.c: New. + * gcc.dg/tls/emutls-1.c: New. + * lib/target-supports.exp (check_effective_target_tls_native): + Exclude vxworks. + 2008-04-26 H.J. Lu PR testsuite/36053 diff --git a/gcc/testsuite/gcc.dg/tls/emutls-1.c b/gcc/testsuite/gcc.dg/tls/emutls-1.c new file mode 100644 index 0000000..a0dea50 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tls/emutls-1.c @@ -0,0 +1,21 @@ +/* { dg-do run { target *-wrs-vxworks } } */ +/* { dg-require-effective-target tls } */ + +/* vxworks' TLS model requires no extra padding on the tls proxy + objects. */ + +__thread int i; +__thread int j; + +extern int __tls__i; +extern int __tls__j; + +int main () +{ + int delta = ((char *)&__tls__j - (char *)&__tls__i); + + if (delta < 0) + delta = -delta; + + return delta != 12; +} diff --git a/gcc/testsuite/gcc.dg/tls/section-2.c b/gcc/testsuite/gcc.dg/tls/section-2.c new file mode 100644 index 0000000..c823f2d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tls/section-2.c @@ -0,0 +1,7 @@ +/* Verify that we get errors for trying to put TLS data in + sections which can't work. */ +/* { dg-require-effective-target tls } */ + +#define A(X) __attribute__((section(X))) + +__thread int i A("foo"); /* { dg-error "cannot be overridden" } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 180ce86..5309ee2 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -518,6 +518,13 @@ proc check_effective_target_tls {} { # This won't change for different subtargets so cache the result. proc check_effective_target_tls_native {} { + # VxWorks uses emulated TLS machinery, but with non-standard helper + # functions, so we fail to automatically detect it. + global target_triplet + if { [regexp ".*-.*-vxworks.*" $target_triplet] } { + return 0 + } + return [check_no_messages_and_pattern tls_native "!emutls" assembly { __thread int i; int f (void) { return i; } diff --git a/gcc/tree.h b/gcc/tree.h index 5821da7..c63ee94 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -3178,7 +3178,7 @@ extern void decl_fini_priority_insert (tree, priority_type); /* In a VAR_DECL, nonzero if the data should be allocated from thread-local storage. */ #define DECL_THREAD_LOCAL_P(NODE) \ - (VAR_DECL_CHECK (NODE)->decl_with_vis.tls_model != TLS_MODEL_NONE) + (VAR_DECL_CHECK (NODE)->decl_with_vis.tls_model >= TLS_MODEL_REAL) struct tree_var_decl GTY(()) { diff --git a/gcc/varasm.c b/gcc/varasm.c index 7c860d6..0d22fc3 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see #include "tm_p.h" #include "debug.h" #include "target.h" +#include "targhooks.h" #include "tree-mudflap.h" #include "cgraph.h" #include "cfglayout.h" @@ -194,68 +195,95 @@ static GTY(()) struct rtx_constant_pool *shared_constant_pool; 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$" +/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED. This + macro can be used on them to distinguish the control variable from + the initialization template. */ +#define DECL_EMUTLS_VAR_P(D) (TREE_TYPE (D) == emutls_object_type) + +#if !defined (NO_DOT_IN_LABEL) +# define EMUTLS_SEPARATOR "." +#elif !defined (NO_DOLLAR_IN_LABEL) +# define EMUTLS_SEPARATOR "$" #else -# define EMUTLS_VAR_PREFIX "__emutls_v_" -# define EMUTLS_TMPL_PREFIX "__emutls_t_" +# define EMUTLS_SEPARATOR "_" #endif -/* Create an identifier for the struct __emutls_object, given an identifier - of the DECL_ASSEMBLY_NAME of the original object. */ +/* Create an IDENTIFIER_NODE by prefixing PREFIX to the + IDENTIFIER_NODE NAME's name. */ static tree -get_emutls_object_name (tree name) +prefix_name (const char *prefix, 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)); + unsigned plen = strlen (prefix); + unsigned nlen = strlen (IDENTIFIER_POINTER (name)); + char *toname = alloca (plen + nlen + 1); + + memcpy (toname, prefix, plen); + memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1); 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. */ +/* 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_type (void) +get_emutls_object_name (tree name) { - 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; + const char *prefix = (targetm.emutls.var_prefix + ? targetm.emutls.var_prefix + : "__emutls_v" EMUTLS_SEPARATOR); + return prefix_name (prefix, name); +} +tree +default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED) +{ + tree word_type_node, field, next_field; + 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); + + 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); + 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; + return field; +} + +/* 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 (TYPE_DECL, type_name, type); + TYPE_NAME (type) = type_name; TYPE_FIELDS (type) = field; layout_type (type); @@ -269,26 +297,30 @@ static tree get_emutls_init_templ_addr (tree decl) { tree name, to; - char *toname; - - if (!DECL_INITIAL (decl)) + + if (targetm.emutls.register_common && !DECL_INITIAL (decl) + && !DECL_SECTION_NAME (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); + 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 (VAR_DECL, name, TREE_TYPE (decl)); SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); - + DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED; 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_WEAK (to) = DECL_WEAK (decl); if (DECL_ONE_ONLY (decl)) { @@ -348,14 +380,18 @@ emutls_decl (tree decl) h->to = to; *(struct tree_map **) loc = h; + DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED; 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); + if (targetm.emutls.var_align_fixed) + /* If we're not allowed to change the proxy object's + alignment, pretend it's been set by the user. */ + DECL_USER_ALIGN (to) = 1; } /* Note that these fields may need to be updated from time to time from @@ -413,16 +449,19 @@ emutls_common_1 (void **loc, void *xstmts) void emutls_finish (void) { - tree body = NULL_TREE; - - if (emutls_htab == NULL) - return; + if (!targetm.emutls.register_common) + { + tree body = NULL_TREE; - htab_traverse_noresize (emutls_htab, emutls_common_1, &body); - if (body == NULL_TREE) - return; + if (emutls_htab == NULL) + return; - cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY); + 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. */ @@ -1125,7 +1164,12 @@ get_variable_section (tree decl, bool prefer_noswitch_p) { if (DECL_THREAD_LOCAL_P (decl)) return tls_comm_section; - if (TREE_PUBLIC (decl) && bss_initializer_p (decl)) + /* This cannot be common bss for an emulated TLS object without + a register_common hook. */ + else if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED + && !targetm.emutls.register_common) + ; + else if (TREE_PUBLIC (decl) && bss_initializer_p (decl)) return comm_section; } @@ -1950,6 +1994,40 @@ assemble_variable_contents (tree decl, const char *name, } } +/* Initialize emulated tls object TO, which refers to TLS variable + DECL and is initialized by PROXY. */ + +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 = 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 = proxy; + + return build_constructor (type, v); +} + /* Assemble everything that is needed for a variable or function declaration. Not used for automatic variables, and not used for function definitions. Should not be called for variables of incomplete structure type. @@ -1984,32 +2062,8 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, || (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); + DECL_INITIAL (to) = targetm.emutls.var_init + (to, decl, get_emutls_init_templ_addr (decl)); /* Make sure the template is marked as needed early enough. Without this, if the variable is placed in a @@ -5786,13 +5840,26 @@ categorize_decl_for_section (const_tree decl, int reloc) ret = SECCAT_RODATA; /* There are no read-only thread-local sections. */ - if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl)) + if (TREE_CODE (decl) == VAR_DECL && DECL_TLS_MODEL (decl)) { + if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED) + { + if (DECL_EMUTLS_VAR_P (decl)) + { + if (targetm.emutls.var_section) + ret = SECCAT_EMUTLS_VAR; + } + else + { + if (targetm.emutls.tmpl_prefix) + ret = SECCAT_EMUTLS_TMPL; + } + } /* Note that this would be *just* SECCAT_BSS, except that there's no concept of a read-only thread-local-data section. */ - if (ret == SECCAT_BSS - || (flag_zero_initialized_in_bss - && initializer_zerop (DECL_INITIAL (decl)))) + else if (ret == SECCAT_BSS + || (flag_zero_initialized_in_bss + && initializer_zerop (DECL_INITIAL (decl)))) ret = SECCAT_TBSS; else ret = SECCAT_TDATA; @@ -5884,6 +5951,12 @@ default_elf_select_section (tree decl, int reloc, case SECCAT_TBSS: sname = ".tbss"; break; + case SECCAT_EMUTLS_VAR: + sname = targetm.emutls.var_section; + break; + case SECCAT_EMUTLS_TMPL: + sname = targetm.emutls.tmpl_section; + break; default: gcc_unreachable (); } @@ -5901,69 +5974,73 @@ default_unique_section (tree decl, int reloc) { /* We only need to use .gnu.linkonce if we don't have COMDAT groups. */ bool one_only = DECL_ONE_ONLY (decl) && !HAVE_COMDAT_GROUP; - const char *prefix, *name; - size_t nlen, plen; + const char *prefix, *name, *linkonce; char *string; switch (categorize_decl_for_section (decl, reloc)) { case SECCAT_TEXT: - prefix = one_only ? ".gnu.linkonce.t." : ".text."; + prefix = one_only ? ".t" : ".text"; break; case SECCAT_RODATA: case SECCAT_RODATA_MERGE_STR: case SECCAT_RODATA_MERGE_STR_INIT: case SECCAT_RODATA_MERGE_CONST: - prefix = one_only ? ".gnu.linkonce.r." : ".rodata."; + prefix = one_only ? ".r" : ".rodata"; break; case SECCAT_SRODATA: - prefix = one_only ? ".gnu.linkonce.s2." : ".sdata2."; + prefix = one_only ? ".s2" : ".sdata2"; break; case SECCAT_DATA: - prefix = one_only ? ".gnu.linkonce.d." : ".data."; + prefix = one_only ? ".d" : ".data"; break; case SECCAT_DATA_REL: - prefix = one_only ? ".gnu.linkonce.d.rel." : ".data.rel."; + prefix = one_only ? ".d.rel" : ".data.rel"; break; case SECCAT_DATA_REL_LOCAL: - prefix = one_only ? ".gnu.linkonce.d.rel.local." : ".data.rel.local."; + prefix = one_only ? ".d.rel.local" : ".data.rel.local"; break; case SECCAT_DATA_REL_RO: - prefix = one_only ? ".gnu.linkonce.d.rel.ro." : ".data.rel.ro."; + prefix = one_only ? ".d.rel.ro" : ".data.rel.ro"; break; case SECCAT_DATA_REL_RO_LOCAL: - prefix = one_only ? ".gnu.linkonce.d.rel.ro.local." - : ".data.rel.ro.local."; + prefix = one_only ? ".d.rel.ro.local" : ".data.rel.ro.local"; break; case SECCAT_SDATA: - prefix = one_only ? ".gnu.linkonce.s." : ".sdata."; + prefix = one_only ? ".s" : ".sdata"; break; case SECCAT_BSS: - prefix = one_only ? ".gnu.linkonce.b." : ".bss."; + prefix = one_only ? ".b" : ".bss"; break; case SECCAT_SBSS: - prefix = one_only ? ".gnu.linkonce.sb." : ".sbss."; + prefix = one_only ? ".sb" : ".sbss"; break; case SECCAT_TDATA: - prefix = one_only ? ".gnu.linkonce.td." : ".tdata."; + prefix = one_only ? ".td" : ".tdata"; break; case SECCAT_TBSS: - prefix = one_only ? ".gnu.linkonce.tb." : ".tbss."; + prefix = one_only ? ".tb" : ".tbss"; + break; + case SECCAT_EMUTLS_VAR: + prefix = targetm.emutls.var_section; + break; + case SECCAT_EMUTLS_TMPL: + prefix = targetm.emutls.tmpl_section; break; default: gcc_unreachable (); } - plen = strlen (prefix); name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); name = targetm.strip_name_encoding (name); - nlen = strlen (name); - string = alloca (nlen + plen + 1); - memcpy (string, prefix, plen); - memcpy (string + plen, name, nlen + 1); + /* If we're using one_only, then there needs to be a .gnu.linkonce + prefix to the section name. */ + linkonce = one_only ? ".gnu.linkonce" : ""; + + string = ACONCAT ((linkonce, prefix, ".", name, NULL)); - DECL_SECTION_NAME (decl) = build_string (nlen + plen, string); + DECL_SECTION_NAME (decl) = build_string (strlen (string), string); } /* Like compute_reloc_for_constant, except for an RTX. The return value -- 2.7.4