+2002-05-21 Richard Henderson <rth@redhat.com>
+
+ * c-common.h (enum rid): Add RID_THREAD.
+ * c-decl.c (start_decl): Do not set DECL_COMMON for tls variables.
+ (grokdeclarator): Grok __thread.
+ * c-parse.in (reswords): Add __thread.
+ (rid_to_yy): Add RID_THREAD.
+
+ * tree.h (DECL_THREAD_LOCAL): New.
+ (struct tree_decl): Add thread_local_flag.
+ * print-tree.c (print_node): Dump DECL_THREAD_LOCAL.
+ * tree.c (staticp): TLS variables are not static.
+
+ * target-def.h (TARGET_HAVE_TLS): New.
+ * target.h (have_tls): New.
+ * output.h (SECTION_TLS): New.
+ * varasm.c (assemble_variable): TLS variables can't be common for now.
+ (default_section_type_flags): Handle .tdata and .tbss.
+ (default_elf_asm_named_section): Handle SECTION_TLS.
+ (categorize_decl_for_section): Handle DECL_THREAD_LOCAL.
+
+ * flags.h (flag_tls_default): Declare.
+ * toplev.c (flag_tls_default): Define.
+ (display_help): Display help for it.
+ (decode_f_option): Set it.
+
+ * doc/extend.texi (Thread-Local): New node describing language-level
+ thread-local storage.
+ * doc/invoke.texi (-ftls-model): Document.
+
+ * fixinc/inclhack.def (thread_keyword): New.
+ * fixinc/fixincl.x: Rebuild.
+
2002-05-21 Jeffrey A Law <law@redhat.com>
* i386.c (ix86_sched_reorder_ppro): Fix typo/thinko.
RID_VOLATILE, RID_SIGNED, RID_AUTO, RID_RESTRICT,
/* C extensions */
- RID_BOUNDED, RID_UNBOUNDED, RID_COMPLEX,
+ RID_BOUNDED, RID_UNBOUNDED, RID_COMPLEX, RID_THREAD,
/* C++ */
RID_FRIEND, RID_VIRTUAL, RID_EXPLICIT, RID_EXPORT, RID_MUTABLE,
/* ANSI specifies that a tentative definition which is not merged with
a non-tentative definition behaves exactly like a definition with an
initializer equal to zero. (Section 3.7.2)
- -fno-common gives strict ANSI behavior. Usually you don't want it.
- This matters only for variables with external linkage. */
- if (!initialized && (! flag_no_common || ! TREE_PUBLIC (decl)))
+
+ -fno-common gives strict ANSI behavior, though this tends to break
+ a large body of code that grew up without this rule.
+
+ Thread-local variables are never common, since there's no entrenched
+ body of code to break, and it allows more efficient variable references
+ in the presense of dynamic linking. */
+
+ if (TREE_CODE (decl) == VAR_DECL
+ && !initialized
+ && TREE_PUBLIC (decl)
+ && !DECL_THREAD_LOCAL (decl)
+ && !flag_no_common)
DECL_COMMON (decl) = 1;
/* Set attributes here so if duplicate decl, will have proper attributes. */
enum rid i = C_RID_CODE (id);
if ((int) i <= (int) RID_LAST_MODIFIER)
{
- if (i == RID_LONG && (specbits & (1 << (int) i)))
+ if (i == RID_LONG && (specbits & (1 << (int) RID_LONG)))
{
if (longlong)
error ("`long long long' is too long for GCC");
}
else if (specbits & (1 << (int) i))
pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id));
+
+ /* Diagnose "__thread extern". Recall that this list
+ is in the reverse order seen in the text. */
+ if (i == RID_THREAD
+ && (specbits & (1 << (int) RID_EXTERN
+ | 1 << (int) RID_STATIC)))
+ {
+ if (specbits & 1 << (int) RID_EXTERN)
+ error ("`__thread' before `extern'");
+ else
+ error ("`__thread' before `static'");
+ }
+
specbits |= 1 << (int) i;
goto found;
}
if (specbits & 1 << (int) RID_REGISTER) nclasses++;
if (specbits & 1 << (int) RID_TYPEDEF) nclasses++;
+ /* "static __thread" and "extern __thread" are allowed. */
+ if ((specbits & (1 << (int) RID_THREAD
+ | 1 << (int) RID_STATIC
+ | 1 << (int) RID_EXTERN)) == (1 << (int) RID_THREAD))
+ nclasses++;
+
/* Warn about storage classes that are invalid for certain
kinds of declarations (parameters, typenames, etc.). */
&& (specbits
& ((1 << (int) RID_REGISTER)
| (1 << (int) RID_AUTO)
- | (1 << (int) RID_TYPEDEF))))
+ | (1 << (int) RID_TYPEDEF)
+ | (1 << (int) RID_THREAD))))
{
if (specbits & 1 << (int) RID_AUTO
&& (pedantic || current_binding_level == global_binding_level))
error ("function definition declared `register'");
if (specbits & 1 << (int) RID_TYPEDEF)
error ("function definition declared `typedef'");
+ if (specbits & 1 << (int) RID_THREAD)
+ error ("function definition declared `__thread'");
specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
- | (1 << (int) RID_AUTO));
+ | (1 << (int) RID_AUTO) | (1 << (int) RID_THREAD));
}
else if (decl_context != NORMAL && nclasses > 0)
{
}
specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
| (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC)
- | (1 << (int) RID_EXTERN));
+ | (1 << (int) RID_EXTERN) | (1 << (int) RID_THREAD));
}
}
else if (specbits & 1 << (int) RID_EXTERN && initialized && ! funcdef_flag)
else
error ("`%s' has both `extern' and initializer", name);
}
- else if (specbits & 1 << (int) RID_EXTERN && funcdef_flag
- && current_binding_level != global_binding_level)
- error ("nested function `%s' declared `extern'", name);
- else if (current_binding_level == global_binding_level
- && specbits & (1 << (int) RID_AUTO))
- error ("top-level declaration of `%s' specifies `auto'", name);
+ else if (current_binding_level == global_binding_level)
+ {
+ if (specbits & 1 << (int) RID_AUTO)
+ error ("top-level declaration of `%s' specifies `auto'", name);
+ }
+ else
+ {
+ if (specbits & 1 << (int) RID_EXTERN && funcdef_flag)
+ error ("nested function `%s' declared `extern'", name);
+ else if ((specbits & (1 << (int) RID_THREAD
+ | 1 << (int) RID_EXTERN
+ | 1 << (int) RID_STATIC))
+ == (1 << (int) RID_THREAD))
+ {
+ error ("function-scope `%s' implicitly auto and declared `__thread'",
+ name);
+ specbits &= ~(1 << (int) RID_THREAD);
+ }
+ }
}
/* Now figure out the structure of the declarator proper.
pedwarn ("invalid storage class for function `%s'", name);
if (specbits & (1 << (int) RID_REGISTER))
error ("invalid storage class for function `%s'", name);
+ if (specbits & (1 << (int) RID_THREAD))
+ error ("invalid storage class for function `%s'", name);
/* Function declaration not at top level.
Storage classes other than `extern' are not allowed
and `extern' makes no difference. */
pedwarn_with_decl (decl, "variable `%s' declared `inline'");
DECL_EXTERNAL (decl) = extern_ref;
+
/* At top level, the presence of a `static' or `register' storage
class specifier, or the absence of all storage class specifiers
makes this declaration a definition (perhaps tentative). Also,
the absence of both `static' and `register' makes it public. */
if (current_binding_level == global_binding_level)
{
- TREE_PUBLIC (decl)
- = !(specbits
- & ((1 << (int) RID_STATIC) | (1 << (int) RID_REGISTER)));
- TREE_STATIC (decl) = ! DECL_EXTERNAL (decl);
+ TREE_PUBLIC (decl) = !(specbits & ((1 << (int) RID_STATIC)
+ | (1 << (int) RID_REGISTER)));
+ TREE_STATIC (decl) = !extern_ref;
}
/* Not at top level, only `static' makes a static definition. */
else
{
TREE_STATIC (decl) = (specbits & (1 << (int) RID_STATIC)) != 0;
- TREE_PUBLIC (decl) = DECL_EXTERNAL (decl);
+ TREE_PUBLIC (decl) = extern_ref;
+ }
+
+ if (specbits & 1 << (int) RID_THREAD)
+ {
+ if (targetm.have_tls)
+ DECL_THREAD_LOCAL (decl) = 1;
+ 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");
}
}
{ "__restrict__", RID_RESTRICT, 0 },
{ "__signed", RID_SIGNED, 0 },
{ "__signed__", RID_SIGNED, 0 },
+ { "__thread", RID_THREAD, 0 },
{ "__typeof", RID_TYPEOF, 0 },
{ "__typeof__", RID_TYPEOF, 0 },
{ "__unbounded", RID_UNBOUNDED, 0 },
/* RID_BOUNDED */ TYPE_QUAL,
/* RID_UNBOUNDED */ TYPE_QUAL,
/* RID_COMPLEX */ TYPESPEC,
+ /* RID_THREAD */ SCSPEC,
/* C++ */
/* RID_FRIEND */ 0,
+2002-05-21 Richard Henderson <rth@redhat.com>
+
+ * lex.c (rid_to_yy): Add RID_THREAD.
+
2002-05-21 Alexandre Oliva <aoliva@redhat.com>
* init.c (build_vec_init): Test for trivial copy-assignment when
/* RID_BOUNDED */ 0,
/* RID_UNBOUNDED */ 0,
/* RID_COMPLEX */ TYPESPEC,
+ /* RID_THREAD */ 0,
/* C++ */
/* RID_FRIEND */ SCSPEC,
* Target Builtins:: Built-in functions specific to particular targets.
* Pragmas:: Pragmas accepted by GCC.
* Unnamed Fields:: Unnamed struct/union fields within structs/unions.
+* Thread-Local:: Per-thread variables.
@end menu
@node Statement Exprs
Such constructs are not supported and must be avoided. In the future,
such constructs may be detected and treated as compilation errors.
+@node Thread-Local
+@section Thread-Local Storage
+@cindex Thread-Local Storage
+@cindex TLS
+@cindex __thread
+
+Thread-local storage (TLS) is a mechanism by which variables are
+allocated such that there is one instance of the variable per extant
+thread. The run-time model GCC uses to implement this originates
+in the IA-64 processor-specific ABI, but has since been migrated
+to other processors as well. It requires significant support from
+the linker (@command{ld}), dynamic linker (@command{ld.so}), and
+system libraries (@file{libc.so} and @file{libpthread.so}), so it
+is not supported everywhere.
+
+At the user level, the extension is visible with a new storage
+class keyword: @code{__thread}. For example:
+
+@example
+__thread int i;
+extern __thread struct state s;
+static __thread char *p;
+@end example
+
+The @code{__thread} specifier may be used alone, with the @code{extern}
+or @code{static} specifiers, but with no other storage class specifier.
+When used with @code{extern} or @code{static}, @code{__thread} must appear
+immediately after the other storage class specifier.
+
+The @code{__thread} specifier may be applied to any global, file-scoped
+static, function-scoped static, or class-scoped static variable. It may
+not be applied to function-scoped automatic or class-scoped member variables.
+
+When the address-of operator is applied to a thread-local variable, it is
+evaluated at run-time and returns the address of the current thread's
+instance of that variable. An address so obtained may be used by any
+thread. When a thread terminates, any pointers to thread-local variables
+in that thread become invalid.
+
+No static initialization may refer to the address of a thread-local variable.
+
+In C++, a thread-local variable may not be initialized by a static
+constructor.
+
+See @uref{http://people.redhat.com/drepper/tls.pdf,
+ELF Handling For Thread-Local Storage} for a detailed explanation of
+the four thread-local storage addressing models, and how the run-time
+is expected to function.
+
@node C++ Extensions
@chapter Extensions to the C++ Language
@cindex extensions, C++ language
-fverbose-asm -fpack-struct -fstack-check @gol
-fstack-limit-register=@var{reg} -fstack-limit-symbol=@var{sym} @gol
-fargument-alias -fargument-noalias @gol
--fargument-noalias-global -fleading-underscore}
+-fargument-noalias-global -fleading-underscore -ftls-model=@var{model}}
@end table
@menu
Be warned that you should know what you are doing when invoking this
option, and that not all targets provide complete support for it.
+
+@item -ftls-model=@var{model}
+Alter the thread-local storage model to be used (@pxref{Thread-Local}).
+The @var{model} argument should be one of @code{global-dynamic},
+@code{local-dynamic}, @code{initial-exec} or @code{local-exec}.
+
+The default without @option{-fpic} is @code{initial-exec}; with
+@option{-fpic} the default is @code{global-dynamic}.
@end table
@c man end
* files which are fixed to work correctly with ANSI C and placed in a
* directory that GNU C will search.
*
- * This file contains 145 fixup descriptions.
+ * This file contains 146 fixup descriptions.
*
* See README for more information.
*
/* * * * * * * * * * * * * * * * * * * * * * * * * *
*
+ * Description of Thread_Keyword fix
+ */
+tSCC zThread_KeywordName[] =
+ "thread_keyword";
+
+/*
+ * File name selection pattern
+ */
+tSCC zThread_KeywordList[] =
+ "|bits/sigthread.h|pthread.h|";
+/*
+ * Machine/OS name selection pattern
+ */
+#define apzThread_KeywordMachs (const char**)NULL
+
+/*
+ * content selection pattern - do fix if pattern found
+ */
+tSCC zThread_KeywordSelect0[] =
+ "__thread";
+
+#define THREAD_KEYWORD_TEST_CT 1
+static tTestDesc aThread_KeywordTests[] = {
+ { TT_EGREP, zThread_KeywordSelect0, (regex_t*)NULL }, };
+
+/*
+ * Fix Command Arguments for Thread_Keyword
+ */
+static const char* apzThread_KeywordPatch[] = { "sed",
+ "-e", "s/\\([^a-z0-9_]\\)__thread\\([^a-z0-9_]\\)/\\1__thr\\2/g",
+ (char*)NULL };
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
* Description of Tinfo_Cplusplus fix
*/
tSCC zTinfo_CplusplusName[] =
*
* List of all fixes
*/
-#define REGEX_COUNT 152
+#define REGEX_COUNT 153
#define MACH_LIST_SIZE_LIMIT 279
-#define FIX_COUNT 145
+#define FIX_COUNT 146
/*
* Enumerate the fixes
SVR4_PROFIL_FIXIDX,
SYSV68_STRING_FIXIDX,
SYSZ_STDLIB_FOR_SUN_FIXIDX,
+ THREAD_KEYWORD_FIXIDX,
TINFO_CPLUSPLUS_FIXIDX,
ULTRIX_ATEXIT_PARAM_FIXIDX,
ULTRIX_ATOF_PARAM_FIXIDX,
SYSZ_STDLIB_FOR_SUN_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
aSysz_Stdlib_For_SunTests, apzSysz_Stdlib_For_SunPatch, 0 },
+ { zThread_KeywordName, zThread_KeywordList,
+ apzThread_KeywordMachs,
+ THREAD_KEYWORD_TEST_CT, FD_MACH_ONLY,
+ aThread_KeywordTests, apzThread_KeywordPatch, 0 },
+
{ zTinfo_CplusplusName, zTinfo_CplusplusList,
apzTinfo_CplusplusMachs,
TINFO_CPLUSPLUS_TEST_CT, FD_MACH_ONLY | FD_SUBROUTINE,
-
/* -*- Mode: C -*- */
autogen definitions fixincl;
/*
+ * __thread is now a keyword.
+ */
+fix = {
+ hackname = thread_keyword;
+ files = "pthread.h";
+ files = "bits/sigthread.h";
+ select = "pthread_t __thread";
+
+ sed = "s/pthread_t __thread\\([^a-z0-9_]\\)/pthread_t __thr\\1/";
+
+ test_text = "extern int pthread_kill (pthread_t __thread, int __signo);";
+};
+
+/*
* if the #if says _cplusplus, not the double underscore __cplusplus
* that it should be
*/
extern int flag_pedantic_errors;
-/* Nonzero means generate position-independent code.
- This is not fully implemented yet. */
+/* Nonzero means generate position-independent code. 1 vs 2 for a
+ target-dependent "small" or "large" mode. */
extern int flag_pic;
+/* Set to the default thread-local storage (tls) model to use. */
+
+enum tls_model {
+ TLS_MODEL_GLOBAL_DYNAMIC,
+ TLS_MODEL_LOCAL_DYNAMIC,
+ TLS_MODEL_INITIAL_EXEC,
+ TLS_MODEL_LOCAL_EXEC
+};
+
+extern enum tls_model flag_tls_default;
+
/* Nonzero means generate extra code for exception handling and enable
exception handling. */
#define SECTION_STRINGS 0x10000 /* contains zero terminated strings without
embedded zeros */
#define SECTION_OVERRIDE 0x20000 /* allow override of default flags */
-#define SECTION_MACH_DEP 0x40000 /* subsequent bits reserved for target */
+#define SECTION_TLS 0x40000 /* contains thread-local storage */
+#define SECTION_MACH_DEP 0x80000 /* subsequent bits reserved for target */
extern unsigned int get_named_section_flags PARAMS ((const char *));
extern bool set_named_section_flags PARAMS ((const char *, unsigned int));
if (TREE_CODE (node) == VAR_DECL && DECL_IN_TEXT_SECTION (node))
fputs (" in-text-section", file);
+ if (TREE_CODE (node) == VAR_DECL && DECL_THREAD_LOCAL (node))
+ fputs (" thread-local", file);
if (TREE_CODE (node) == PARM_DECL && DECL_TRANSPARENT_UNION (node))
fputs (" transparent-union", file);
#define TARGET_HAVE_NAMED_SECTIONS false
#endif
+#ifndef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS false
+#endif
+
#ifndef TARGET_ASM_EXCEPTION_SECTION
#define TARGET_ASM_EXCEPTION_SECTION default_exception_section
#endif
TARGET_STRIP_NAME_ENCODING, \
TARGET_HAVE_NAMED_SECTIONS, \
TARGET_HAVE_CTORS_DTORS, \
+ TARGET_HAVE_TLS \
}
#include "hooks.h"
/* True if "native" constructors and destructors are supported,
false if we're using collect2 for the job. */
bool have_ctors_dtors;
+
+ /* True if thread-local storage is supported. */
+ bool have_tls;
};
extern struct gcc_target targetm;
int flag_delayed_branch;
/* Nonzero if we are compiling pure (sharable) code.
- Value is 1 if we are doing reasonable (i.e. simple
- offset into offset table) pic. Value is 2 if we can
- only perform register offsets. */
+ Value is 1 if we are doing "small" pic; value is 2 if we're doing
+ "large" pic. */
int flag_pic;
+/* Set to the default thread-local storage (tls) model to use. */
+
+enum tls_model flag_tls_default;
+
/* Nonzero means generate extra code for exception handling and enable
exception handling. */
printf (_(" -finline-limit=<number> Limits the size of inlined functions to <number>\n"));
printf (_(" -fmessage-length=<number> Limits diagnostics messages lengths to <number> characters per line. 0 suppresses line-wrapping\n"));
printf (_(" -fdiagnostics-show-location=[once | every-line] Indicates how often source location information should be emitted, as prefix, at the beginning of diagnostics when line-wrapping\n"));
+ printf (_(" -ftls-model=[global-dynamic | local-dynamic | initial-exec | local-exec] Indicates the default thread-local storage code generation model\n"));
for (i = ARRAY_SIZE (f_options); i--;)
{
MAX_INLINE_INSNS);
set_param_value ("max-inline-insns", val);
}
+ else if ((option_value = skip_leading_substring (arg, "tls-model=")))
+ {
+ if (strcmp (option_value, "global-dynamic") == 0)
+ flag_tls_default = TLS_MODEL_GLOBAL_DYNAMIC;
+ else if (strcmp (option_value, "local-dynamic") == 0)
+ flag_tls_default = TLS_MODEL_LOCAL_DYNAMIC;
+ else if (strcmp (option_value, "initial-exec") == 0)
+ flag_tls_default = TLS_MODEL_INITIAL_EXEC;
+ else if (strcmp (option_value, "local-exec") == 0)
+ flag_tls_default = TLS_MODEL_LOCAL_EXEC;
+ else
+ warning ("`%s': unknown tls-model option", arg - 2);
+ }
#ifdef INSN_SCHEDULING
else if ((option_value = skip_leading_substring (arg, "sched-verbose=")))
fix_sched_param ("verbose", option_value);
case FUNCTION_DECL:
/* Nested functions aren't static, since taking their address
involves a trampoline. */
- return (decl_function_context (arg) == 0 || DECL_NO_STATIC_CHAIN (arg))
- && ! DECL_NON_ADDR_CONST_P (arg);
+ return ((decl_function_context (arg) == 0 || DECL_NO_STATIC_CHAIN (arg))
+ && ! DECL_NON_ADDR_CONST_P (arg));
case VAR_DECL:
- return (TREE_STATIC (arg) || DECL_EXTERNAL (arg))
- && ! DECL_NON_ADDR_CONST_P (arg);
+ return ((TREE_STATIC (arg) || DECL_EXTERNAL (arg))
+ && ! DECL_THREAD_LOCAL (arg)
+ && ! DECL_NON_ADDR_CONST_P (arg));
case CONSTRUCTOR:
return TREE_STATIC (arg);
/* In a FUNCTION_DECL, nonzero if the function cannot be inlined. */
#define DECL_UNINLINABLE(NODE) (FUNCTION_DECL_CHECK (NODE)->decl.uninlinable)
+/* In a VAR_DECL, nonzero if the data should be allocated from
+ thread-local storage. */
+#define DECL_THREAD_LOCAL(NODE) (VAR_DECL_CHECK (NODE)->decl.thread_local_flag)
+
/* In a FUNCTION_DECL, the saved representation of the body of the
entire function. Usually a COMPOUND_STMT, but in C++ this may also
be a RETURN_INIT, CTOR_INITIALIZER, or TRY_BLOCK. */
unsigned non_addressable : 1;
unsigned user_align : 1;
unsigned uninlinable : 1;
- /* Three unused bits. */
+ unsigned thread_local_flag : 1;
+ /* Two unused bits. */
unsigned lang_flag_0 : 1;
unsigned lang_flag_1 : 1;
/* Handle uninitialized definitions. */
- if ((DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node
-#if defined ASM_EMIT_BSS
- || (flag_zero_initialized_in_bss
- && initializer_zerop (DECL_INITIAL (decl)))
-#endif
- )
- /* If the target can't output uninitialized but not common global data
- in .bss, then we have to use .data. */
-#if ! defined ASM_EMIT_BSS
- && DECL_COMMON (decl)
+ /* If the decl has been given an explicit section name, then it
+ isn't common, and shouldn't be handled as such. */
+ if (DECL_SECTION_NAME (decl) || dont_output_data)
+ ;
+ /* We don't implement common thread-local data at present. */
+ else if (DECL_THREAD_LOCAL (decl))
+ {
+ if (DECL_COMMON (decl))
+ sorry ("thread-local COMMON data not implemented");
+ }
+#ifndef ASM_EMIT_BSS
+ /* If the target can't output uninitialized but not common global data
+ in .bss, then we have to use .data. */
+ /* ??? We should handle .bss via select_section mechanisms rather than
+ via special target hooks. That would eliminate this special case. */
+ else if (!DECL_COMMON (decl))
+ ;
#endif
- && DECL_SECTION_NAME (decl) == NULL_TREE
- && ! dont_output_data)
+ else if (DECL_INITIAL (decl) == 0
+ || DECL_INITIAL (decl) == error_mark_node
+ || (flag_zero_initialized_in_bss
+ && initializer_zerop (DECL_INITIAL (decl))))
{
unsigned HOST_WIDE_INT size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
unsigned HOST_WIDE_INT rounded = size;
|| strncmp (name, ".gnu.linkonce.b.", 16) == 0
|| strcmp (name, ".sbss") == 0
|| strncmp (name, ".sbss.", 6) == 0
- || strncmp (name, ".gnu.linkonce.sb.", 17) == 0)
+ || strncmp (name, ".gnu.linkonce.sb.", 17) == 0
+ || strcmp (name, ".tbss") == 0)
flags |= SECTION_BSS;
+ if (strcmp (name, ".tdata") == 0
+ || strcmp (name, ".tbss") == 0)
+ flags |= SECTION_TLS;
+
return flags;
}
*f++ = 'M';
if (flags & SECTION_STRINGS)
*f++ = 'S';
+ if (flags & SECTION_TLS)
+ *f++ = 'T';
*f = '\0';
if (flags & SECTION_BSS)
else
ret = SECCAT_RODATA;
+ /* There are no read-only thread-local sections. */
+ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+ {
+ if (ret == SECCAT_BSS)
+ ret = SECCAT_TBSS;
+ else
+ ret = SECCAT_TDATA;
+ }
+
/* If the target uses small data sections, select it. */
- if ((*targetm.in_small_data_p) (decl))
+ else if ((*targetm.in_small_data_p) (decl))
{
if (ret == SECCAT_BSS)
ret = SECCAT_SBSS;