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