thread-local storage: c front end and generic backend patch
Richard Henderson
rth@redhat.com
Tue May 21 18:54:00 GMT 2002
The following adds support in the C front end for a new
storage specifier keyword "__thread" that marks a variable
to be allocated in storage private to every extant thread.
A similar patch for the C++ front end will follow directly;
I wanted to split that out for ease of review by the C++
front end folk.
Joseph, the extend.texi documentation has some user-level
description of the extension. I've tried to come up with
a set of edits for C99, but I'm not sure where to put them,
or exactly what form they should take. Thoughts?
There is a fledgeling testsuite here, but it won't get run
until you have target support as well. I have x86 support
completed, and it'll get submitted as soon as I clean it up
properly and add some autoconf detection logic for binutils
support.
r~
-------------- next part --------------
ISO/IEC 9899:1999 edits for thread-local storage:
6.2.4 Storage durations of objects
P3: Add new paragraph before
An object whose identifier is declared with the storage-class
specifier @code{__thread} has @dfn{thread storage duration}.
Its lifetime is the entire execution of the thread, and its
stored value is initialized only once, prior to thread startup.
6.5.3.2 Address and indirection operators
P3:
[ I don't think any change is required here. The explicit
semantics of @code{&} require that the result be the address
of the tls variable within the current thread, but I don't
see that at odds with "returns the address of its operand". ]
6.6 Constant expressions
P9:
[ No change here, since we've defined @code{__thread} variables
to have thread storage duration, not static storage duration,
and that isn't listed as legal. ]
6.7.1 Storage-class specifiers
P1: Add @code{__thread}.
P2: Change to
With the exception of @code{__thread}, at most one storage-class
specifier may be given [...]. The @code{__thread} specifier may
be used alone, or immediately following @code{extern} or
@code{static}.
P6: Add new paragraph after
The declaration of an identifier for a variable that has
block scope that specifies @code{__thread} shall also
specify either @code{extern} or @code{static}.
The @code{__thread} specifier shall be used only with
variables.
-------------- next part --------------
* 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.
cp/
* lex.c (rid_to_yy): Add RID_THREAD.
testsuite/
* gcc.dg/tls/tls.exp, gcc.dg/tls/trivial.c, gcc.dg/tls/diag-1.c,
gcc.dg/tls/diag-2.c, gcc.dg/tls/init-1.c: New directory and files.
Index: c-common.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.h,v
retrieving revision 1.136
diff -c -p -d -r1.136 c-common.h
*** c-common.h 18 May 2002 19:02:01 -0000 1.136
--- c-common.h 21 May 2002 22:10:18 -0000
*************** enum rid
*** 58,64 ****
RID_VOLATILE, RID_SIGNED, RID_AUTO, RID_RESTRICT,
/* C extensions */
! RID_BOUNDED, RID_UNBOUNDED, RID_COMPLEX,
/* C++ */
RID_FRIEND, RID_VIRTUAL, RID_EXPLICIT, RID_EXPORT, RID_MUTABLE,
--- 58,64 ----
RID_VOLATILE, RID_SIGNED, RID_AUTO, RID_RESTRICT,
/* C extensions */
! RID_BOUNDED, RID_UNBOUNDED, RID_COMPLEX, RID_THREAD,
/* C++ */
RID_FRIEND, RID_VIRTUAL, RID_EXPLICIT, RID_EXPORT, RID_MUTABLE,
Index: c-decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-decl.c,v
retrieving revision 1.326
diff -c -p -d -r1.326 c-decl.c
*** c-decl.c 18 May 2002 19:02:02 -0000 1.326
--- c-decl.c 21 May 2002 22:10:19 -0000
*************** start_decl (declarator, declspecs, initi
*** 3350,3358 ****
/* 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)))
DECL_COMMON (decl) = 1;
/* Set attributes here so if duplicate decl, will have proper attributes. */
--- 3350,3368 ----
/* 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, 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. */
*************** grokdeclarator (declarator, declspecs, d
*** 3933,3939 ****
enum rid i = C_RID_CODE (id);
if ((int) i <= (int) RID_LAST_MODIFIER)
{
! if (i == RID_LONG && (specbits & (1 << (int) i)))
{
if (longlong)
error ("`long long long' is too long for GCC");
--- 3943,3949 ----
enum rid i = C_RID_CODE (id);
if ((int) i <= (int) RID_LAST_MODIFIER)
{
! if (i == RID_LONG && (specbits & (1 << (int) RID_LONG)))
{
if (longlong)
error ("`long long long' is too long for GCC");
*************** grokdeclarator (declarator, declspecs, d
*** 3947,3952 ****
--- 3957,3975 ----
}
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;
}
*************** grokdeclarator (declarator, declspecs, d
*** 4196,4201 ****
--- 4219,4230 ----
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.). */
*************** grokdeclarator (declarator, declspecs, d
*** 4205,4211 ****
&& (specbits
& ((1 << (int) RID_REGISTER)
| (1 << (int) RID_AUTO)
! | (1 << (int) RID_TYPEDEF))))
{
if (specbits & 1 << (int) RID_AUTO
&& (pedantic || current_binding_level == global_binding_level))
--- 4234,4241 ----
&& (specbits
& ((1 << (int) RID_REGISTER)
| (1 << (int) RID_AUTO)
! | (1 << (int) RID_TYPEDEF)
! | (1 << (int) RID_THREAD))))
{
if (specbits & 1 << (int) RID_AUTO
&& (pedantic || current_binding_level == global_binding_level))
*************** grokdeclarator (declarator, declspecs, d
*** 4214,4221 ****
error ("function definition declared `register'");
if (specbits & 1 << (int) RID_TYPEDEF)
error ("function definition declared `typedef'");
specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
! | (1 << (int) RID_AUTO));
}
else if (decl_context != NORMAL && nclasses > 0)
{
--- 4244,4253 ----
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_THREAD));
}
else if (decl_context != NORMAL && nclasses > 0)
{
*************** grokdeclarator (declarator, declspecs, d
*** 4238,4244 ****
}
specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
| (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC)
! | (1 << (int) RID_EXTERN));
}
}
else if (specbits & 1 << (int) RID_EXTERN && initialized && ! funcdef_flag)
--- 4270,4276 ----
}
specbits &= ~((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
| (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC)
! | (1 << (int) RID_EXTERN) | (1 << (int) RID_THREAD));
}
}
else if (specbits & 1 << (int) RID_EXTERN && initialized && ! funcdef_flag)
*************** grokdeclarator (declarator, declspecs, d
*** 4249,4260 ****
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);
}
/* Now figure out the structure of the declarator proper.
--- 4281,4305 ----
else
error ("`%s' has both `extern' and initializer", 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.
*************** grokdeclarator (declarator, declspecs, d
*** 4842,4847 ****
--- 4887,4894 ----
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. */
*************** grokdeclarator (declarator, declspecs, d
*** 4934,4955 ****
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);
}
/* 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);
}
}
--- 4981,5012 ----
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) = !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) = 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");
}
}
Index: c-parse.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-parse.in,v
retrieving revision 1.139
diff -c -p -d -r1.139 c-parse.in
*** c-parse.in 27 Apr 2002 06:53:06 -0000 1.139
--- c-parse.in 21 May 2002 22:10:19 -0000
*************** static const struct resword reswords[] =
*** 3343,3348 ****
--- 3343,3349 ----
{ "__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 },
*************** static const short rid_to_yy[RID_MAX] =
*** 3438,3443 ****
--- 3439,3445 ----
/* RID_BOUNDED */ TYPE_QUAL,
/* RID_UNBOUNDED */ TYPE_QUAL,
/* RID_COMPLEX */ TYPESPEC,
+ /* RID_THREAD */ SCSPEC,
/* C++ */
/* RID_FRIEND */ 0,
Index: flags.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/flags.h,v
retrieving revision 1.84
diff -c -p -d -r1.84 flags.h
*** flags.h 15 May 2002 09:00:01 -0000 1.84
--- flags.h 21 May 2002 22:10:19 -0000
*************** extern int flag_dump_unnumbered;
*** 458,467 ****
extern int flag_pedantic_errors;
! /* Nonzero means generate position-independent code.
! This is not fully implemented yet. */
extern int flag_pic;
/* Nonzero means generate extra code for exception handling and enable
exception handling. */
--- 458,478 ----
extern int flag_pedantic_errors;
! /* 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. */
Index: output.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/output.h,v
retrieving revision 1.105
diff -c -p -d -r1.105 output.h
*** output.h 19 May 2002 09:50:11 -0000 1.105
--- output.h 21 May 2002 22:10:19 -0000
*************** extern void no_asm_to_stream PARAMS ((FI
*** 507,513 ****
#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 */
extern unsigned int get_named_section_flags PARAMS ((const char *));
extern bool set_named_section_flags PARAMS ((const char *, unsigned int));
--- 507,514 ----
#define SECTION_STRINGS 0x10000 /* contains zero terminated strings without
embedded zeros */
#define SECTION_OVERRIDE 0x20000 /* allow override of default flags */
! #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));
Index: print-tree.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/print-tree.c,v
retrieving revision 1.57
diff -c -p -d -r1.57 print-tree.c
*** print-tree.c 20 May 2002 18:06:54 -0000 1.57
--- print-tree.c 21 May 2002 22:10:19 -0000
*************** print_node (file, prefix, node, indent)
*** 352,357 ****
--- 352,359 ----
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);
Index: target-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target-def.h,v
retrieving revision 1.28
diff -c -p -d -r1.28 target-def.h
*** target-def.h 19 May 2002 09:50:12 -0000 1.28
--- target-def.h 21 May 2002 22:10:19 -0000
*************** Foundation, 59 Temple Place - Suite 330,
*** 110,115 ****
--- 110,119 ----
#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
*************** Foundation, 59 Temple Place - Suite 330,
*** 244,249 ****
--- 248,254 ----
TARGET_STRIP_NAME_ENCODING, \
TARGET_HAVE_NAMED_SECTIONS, \
TARGET_HAVE_CTORS_DTORS, \
+ TARGET_HAVE_TLS \
}
#include "hooks.h"
Index: target.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target.h,v
retrieving revision 1.30
diff -c -p -d -r1.30 target.h
*** target.h 19 May 2002 09:50:14 -0000 1.30
--- target.h 21 May 2002 22:10:19 -0000
*************** struct gcc_target
*** 256,261 ****
--- 256,264 ----
/* 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;
Index: toplev.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/toplev.c,v
retrieving revision 1.628
diff -c -p -d -r1.628 toplev.c
*** toplev.c 19 May 2002 08:31:47 -0000 1.628
--- toplev.c 21 May 2002 22:10:19 -0000
*************** int flag_shared_data;
*** 685,696 ****
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. */
int flag_pic;
/* Nonzero means generate extra code for exception handling and enable
exception handling. */
--- 685,699 ----
int flag_delayed_branch;
/* Nonzero if we are compiling pure (sharable) code.
! 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. */
*************** display_help ()
*** 3547,3552 ****
--- 3550,3556 ----
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--;)
{
*************** decode_f_option (arg)
*** 3824,3829 ****
--- 3828,3846 ----
read_integral_parameter (option_value, arg - 2,
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=")))
Index: tree.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.c,v
retrieving revision 1.257
diff -c -p -d -r1.257 tree.c
*** tree.c 9 May 2002 22:48:33 -0000 1.257
--- tree.c 21 May 2002 22:10:19 -0000
*************** staticp (arg)
*** 1349,1360 ****
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);
case VAR_DECL:
! return (TREE_STATIC (arg) || DECL_EXTERNAL (arg))
! && ! DECL_NON_ADDR_CONST_P (arg);
case CONSTRUCTOR:
return TREE_STATIC (arg);
--- 1349,1361 ----
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));
case VAR_DECL:
! return ((TREE_STATIC (arg) || DECL_EXTERNAL (arg))
! && ! DECL_THREAD_LOCAL (arg)
! && ! DECL_NON_ADDR_CONST_P (arg));
case CONSTRUCTOR:
return TREE_STATIC (arg);
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.336
diff -c -p -d -r1.336 tree.h
*** tree.h 12 May 2002 21:42:00 -0000 1.336
--- tree.h 21 May 2002 22:10:19 -0000
*************** struct tree_type
*** 1615,1620 ****
--- 1615,1624 ----
/* 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. */
*************** struct tree_decl
*** 1793,1799 ****
unsigned non_addressable : 1;
unsigned user_align : 1;
unsigned uninlinable : 1;
! /* Three unused bits. */
unsigned lang_flag_0 : 1;
unsigned lang_flag_1 : 1;
--- 1797,1804 ----
unsigned non_addressable : 1;
unsigned user_align : 1;
unsigned uninlinable : 1;
! unsigned thread_local_flag : 1;
! /* Two unused bits. */
unsigned lang_flag_0 : 1;
unsigned lang_flag_1 : 1;
Index: varasm.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/varasm.c,v
retrieving revision 1.283
diff -c -p -d -r1.283 varasm.c
*** varasm.c 19 May 2002 20:17:50 -0000 1.283
--- varasm.c 21 May 2002 22:10:19 -0000
*************** assemble_variable (decl, top_level, at_e
*** 1586,1604 ****
/* 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)
#endif
! && DECL_SECTION_NAME (decl) == NULL_TREE
! && ! dont_output_data)
{
unsigned HOST_WIDE_INT size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
unsigned HOST_WIDE_INT rounded = size;
--- 1586,1613 ----
/* Handle uninitialized definitions. */
! /* 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
! 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;
*************** default_section_type_flags (decl, name,
*** 5101,5109 ****
|| strncmp (name, ".gnu.linkonce.b.", 16) == 0
|| strcmp (name, ".sbss") == 0
|| strncmp (name, ".sbss.", 6) == 0
! || strncmp (name, ".gnu.linkonce.sb.", 17) == 0)
flags |= SECTION_BSS;
return flags;
}
--- 5110,5123 ----
|| strncmp (name, ".gnu.linkonce.b.", 16) == 0
|| strcmp (name, ".sbss") == 0
|| strncmp (name, ".sbss.", 6) == 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;
}
*************** default_elf_asm_named_section (name, fla
*** 5146,5151 ****
--- 5160,5167 ----
*f++ = 'M';
if (flags & SECTION_STRINGS)
*f++ = 'S';
+ if (flags & SECTION_TLS)
+ *f++ = 'T';
*f = '\0';
if (flags & SECTION_BSS)
*************** categorize_decl_for_section (decl, reloc
*** 5353,5360 ****
else
ret = SECCAT_RODATA;
/* If the target uses small data sections, select it. */
! if ((*targetm.in_small_data_p) (decl))
{
if (ret == SECCAT_BSS)
ret = SECCAT_SBSS;
--- 5369,5385 ----
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. */
! else if ((*targetm.in_small_data_p) (decl))
{
if (ret == SECCAT_BSS)
ret = SECCAT_SBSS;
Index: cp/lex.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/lex.c,v
retrieving revision 1.277
diff -c -p -d -r1.277 lex.c
*** cp/lex.c 25 Apr 2002 06:24:34 -0000 1.277
--- cp/lex.c 21 May 2002 22:10:21 -0000
*************** const short rid_to_yy[RID_MAX] =
*** 474,479 ****
--- 474,480 ----
/* RID_BOUNDED */ 0,
/* RID_UNBOUNDED */ 0,
/* RID_COMPLEX */ TYPESPEC,
+ /* RID_THREAD */ 0,
/* C++ */
/* RID_FRIEND */ SCSPEC,
Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.70
diff -c -p -d -r1.70 extend.texi
*** doc/extend.texi 11 May 2002 16:25:04 -0000 1.70
--- doc/extend.texi 21 May 2002 22:10:22 -0000
*************** extensions, accepted by GCC in C89 mode
*** 432,437 ****
--- 432,438 ----
* 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
*************** struct @{
*** 6164,6169 ****
--- 6165,6219 ----
It is ambiguous which @code{a} is being referred to with @samp{foo.a}.
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
Index: doc/invoke.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/invoke.texi,v
retrieving revision 1.146
diff -c -p -d -r1.146 invoke.texi
*** doc/invoke.texi 18 May 2002 19:02:02 -0000 1.146
--- doc/invoke.texi 21 May 2002 22:10:22 -0000
*************** in the following sections.
*** 677,683 ****
-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}
@end table
@menu
--- 677,683 ----
-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 -ftls-model=@var{model}}
@end table
@menu
*************** is to help link with legacy assembly cod
*** 9915,9920 ****
--- 9915,9928 ----
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
Index: fixinc/inclhack.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/fixinc/inclhack.def,v
retrieving revision 1.128
diff -c -p -d -r1.128 inclhack.def
*** fixinc/inclhack.def 14 May 2002 00:33:14 -0000 1.128
--- fixinc/inclhack.def 21 May 2002 22:10:23 -0000
***************
*** 1,4 ****
-
/* -*- Mode: C -*- */
autogen definitions fixincl;
--- 1,3 ----
*************** fix = {
*** 2885,2890 ****
--- 2884,2903 ----
"extern char*\tbsearch(void*,size_t,size_t);\n";
};
+
+ /*
+ * __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
Index: testsuite/gcc.dg/tls/diag-1.c
===================================================================
RCS file: testsuite/gcc.dg/tls/diag-1.c
diff -N testsuite/gcc.dg/tls/diag-1.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.dg/tls/diag-1.c 21 May 2002 22:51:13 -0000
***************
*** 0 ****
--- 1,11 ----
+ /* Valid __thread specifiers. */
+
+ __thread int g1;
+ extern __thread int g2;
+ static __thread int g3;
+
+ void foo()
+ {
+ extern __thread int l1;
+ static __thread int l2;
+ }
Index: testsuite/gcc.dg/tls/diag-2.c
===================================================================
RCS file: testsuite/gcc.dg/tls/diag-2.c
diff -N testsuite/gcc.dg/tls/diag-2.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.dg/tls/diag-2.c 21 May 2002 22:51:13 -0000
***************
*** 0 ****
--- 1,21 ----
+ /* Invalid __thread specifiers. */
+
+ __thread extern int g1; /* { dg-error "`__thread' before `extern'" } */
+ __thread static int g2; /* { dg-error "`__thread' before `static'" } */
+ __thread __thread int g3; /* { dg-error "duplicate `__thread'" } */
+ typedef __thread int g4; /* { dg-error "multiple storage classes" } */
+
+ void foo()
+ {
+ __thread int l1; /* { dg-error "implicitly auto and declared `__thread'" } */
+ auto __thread int l2; /* { dg-error "multiple storage classes" } */
+ __thread extern int l3; /* { dg-error "`__thread' before `extern'" } */
+ register __thread int l4; /* { dg-error "multiple storage classes" } */
+ }
+
+ __thread void f1 (); /* { dg-error "invalid storage class for function" } */
+ extern __thread void f2 (); /* { dg-error "invalid storage class for function" } */
+ static __thread void f3 (); /* { dg-error "invalid storage class for function" } */
+ __thread void f4 () { } /* { dg-error "function definition declared `__thread'" } */
+
+ void bar(__thread int p1); /* { dg-error "storage class specified for parameter" } */
Index: testsuite/gcc.dg/tls/init-1.c
===================================================================
RCS file: testsuite/gcc.dg/tls/init-1.c
diff -N testsuite/gcc.dg/tls/init-1.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.dg/tls/init-1.c 21 May 2002 22:51:13 -0000
***************
*** 0 ****
--- 1,4 ----
+ /* Invalid initializations. */
+
+ extern __thread int i;
+ int *p = &i; /* { dg-error "initializer element is not constant" } */
Index: testsuite/gcc.dg/tls/tls.exp
===================================================================
RCS file: testsuite/gcc.dg/tls/tls.exp
diff -N testsuite/gcc.dg/tls/tls.exp
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.dg/tls/tls.exp 21 May 2002 22:51:13 -0000
***************
*** 0 ****
--- 1,45 ----
+ # Copyright (C) 2002 Free Software Foundation, Inc.
+
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation; either version 2 of the License, or
+ # (at your option) any later version.
+ #
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ # GNU General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ # GCC testsuite that uses the `dg.exp' driver.
+
+ # Load support procs.
+ load_lib gcc-dg.exp
+
+ # Test for thread-local data supported by the platform. If it
+ # isn't, everything will fail with the "not supported" message.
+
+ set comp_output [gcc_target_compile \
+ "$srcdir/$subdir/trivial.c" "trivial.S" assembly ""]
+ if { [string match "*not supported*" $comp_output] } {
+ return 0
+ }
+
+ # If a testcase doesn't have special options, use these.
+ global DEFAULT_CFLAGS
+ if ![info exists DEFAULT_CFLAGS] then {
+ set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+ }
+
+ # Initialize `dg'.
+ dg-init
+
+ # Main loop.
+ dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+ "" $DEFAULT_CFLAGS
+
+ # All done.
+ dg-finish
Index: testsuite/gcc.dg/tls/trivial.c
===================================================================
RCS file: testsuite/gcc.dg/tls/trivial.c
diff -N testsuite/gcc.dg/tls/trivial.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.dg/tls/trivial.c 21 May 2002 22:51:13 -0000
***************
*** 0 ****
--- 1 ----
+ __thread int i;
More information about the Gcc-patches
mailing list