OSDN Git Service

* targhooks.h (default_emutls_var_fields,
authornathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 27 Apr 2008 15:35:19 +0000 (15:35 +0000)
committernathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 27 Apr 2008 15:35:19 +0000 (15:35 +0000)
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

18 files changed:
gcc/ChangeLog
gcc/builtins.def
gcc/c-common.c
gcc/config/i386/i386.c
gcc/config/vxworks.c
gcc/coretypes.h
gcc/doc/tm.texi
gcc/dwarf2out.c
gcc/output.h
gcc/target-def.h
gcc/target.h
gcc/targhooks.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tls/emutls-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tls/section-2.c [new file with mode: 0644]
gcc/testsuite/lib/target-supports.exp
gcc/tree.h
gcc/varasm.c

index 6453c50..37d6752 100644 (file)
@@ -1,3 +1,46 @@
+2008-04-27  Nathan Sidwell  <nathan@codesourcery.com>
+
+       * 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 <simonb@google.com>
 
        PR c/35652
index dd6741e..722f81c 100644 (file)
@@ -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"
index d110b32..5858523 100644 (file)
@@ -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);
        }
index dee2410..43c669f 100644 (file)
@@ -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;
        }
     }
index 4fde6ad..709791b 100644 (file)
@@ -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;
 
index 03eb0de..4fe480f 100644 (file)
@@ -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
index 869bb1b..6ce3b4a 100644 (file)
@@ -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
index 7625947..83b3fb5 100644 (file)
@@ -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;
index ae8404e..15daf5b 100644 (file)
@@ -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.  */
index 118e979..8566786 100644 (file)
     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                     \
 {                                              \
   TARGET_INSTANTIATE_DECLS,                    \
   TARGET_C,                                    \
   TARGET_CXX,                                  \
+  TARGET_EMUTLS,                               \
   TARGET_EXTRA_LIVE_ON_ENTRY,                  \
   TARGET_UNWIND_TABLES_DEFAULT,                        \
   TARGET_HAVE_NAMED_SECTIONS,                  \
index 128a029..c331020 100644 (file)
@@ -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. */  
index 5576f98..c94dd06 100644 (file)
@@ -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);
index d99b845..4c1397c 100644 (file)
@@ -1,3 +1,10 @@
+2008-04-27  Nathan Sidwell  <nathan@codesourcery.com>
+
+       * 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  <hongjiu.lu@intel.com>
 
        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 (file)
index 0000000..a0dea50
--- /dev/null
@@ -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 (file)
index 0000000..c823f2d
--- /dev/null
@@ -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" } */
index 180ce86..5309ee2 100644 (file)
@@ -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; }
index 5821da7..c63ee94 100644 (file)
@@ -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(())
 {
index 7c860d6..0d22fc3 100644 (file)
@@ -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