thread-local storage: c++ front end

Richard Henderson rth@redhat.com
Tue May 21 20:05:00 GMT 2002


Could someone more fluent in C++ than I look this over to see what bits
I've missed?  I'm most interested in kinds of initializations that result
in code being generated, rather than raw data.

Also, I'm not sure how to handle class members.  I want non-static class
members to reject the __thread specifier, but it seems reasonable to
support thread-local static class members.  Similarly with static members
of template classes.  (Those are just normal global data with complex
names, after all.)

I'll poke at this some more over the next couple of days if someone
doesn't immediately chime in with "oh you should look here".


r~
-------------- next part --------------
	* decl.c (check_tag_decl): Handle RID_THREAD.
	(check_for_tls_addr): New.
	(check_initializer): Use it to verify no thread-local addresses.
	Verify no thread-local storage initialized by constructor.
	(grokvardecl): Handle RID_THREAD.
	(grokdeclarator): Likewise.
	* lex.c (reswords): Add __thread.
	(rid_to_yy): Map RID_THREAD to SCSPEC.

testsuite/
	* g++.dg/dg.exp: Prune the tls subdirectory.
	* g++.dg/tls/tls.exp, g++.dg/tls/trivial.C: New.
	* g++.dg/tls/diag-1.C, g++.dg/tls/diag-2.C: New.
	* g++.dg/tls/init-1.C: New.

Index: cp/decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl.c,v
retrieving revision 1.908
diff -c -p -d -r1.908 decl.c
*** cp/decl.c	14 May 2002 17:20:45 -0000	1.908
--- cp/decl.c	22 May 2002 02:00:06 -0000
*************** static void bad_specifiers PARAMS ((tree
*** 89,94 ****
--- 89,95 ----
  				  int));
  static tree maybe_process_template_type_declaration PARAMS ((tree, int, struct binding_level*));
  static void check_for_uninitialized_const_var PARAMS ((tree));
+ static tree check_for_tls_addr PARAMS ((tree *, int *, void *));
  static unsigned long typename_hash PARAMS ((hash_table_key));
  static bool typename_compare PARAMS ((hash_table_key, hash_table_key));
  static void push_binding PARAMS ((tree, tree, struct binding_level*));
*************** check_tag_decl (declspecs)
*** 7106,7112 ****
  	       || value == ridpointers[(int) RID_VIRTUAL]
  	       || value == ridpointers[(int) RID_CONST]
  	       || value == ridpointers[(int) RID_VOLATILE]
! 	       || value == ridpointers[(int) RID_EXPLICIT])
  	ob_modifier = value;
      }
  
--- 7107,7114 ----
  	       || value == ridpointers[(int) RID_VIRTUAL]
  	       || value == ridpointers[(int) RID_CONST]
  	       || value == ridpointers[(int) RID_VOLATILE]
! 	       || value == ridpointers[(int) RID_EXPLICIT]
! 	       || value == ridpointers[(int) RID_THREAD])
  	ob_modifier = value;
      }
  
*************** check_for_uninitialized_const_var (decl)
*** 7780,7785 ****
--- 7782,7808 ----
      error ("uninitialized const `%D'", decl);
  }
  
+ /* Search an initalizer for the address of a thread-local variable.  */
+ 
+ static tree
+ check_for_tls_addr (tp, walk_subtrees, data)
+      tree *tp;
+      int *walk_subtrees;
+      void *data ATTRIBUTE_UNUSED;
+ {
+   tree t = *tp;
+ 
+   if (TREE_CODE (t) == ADDR_EXPR)
+     {
+       t = TREE_OPERAND (t, 0);
+       if (TREE_CODE (t) == VAR_DECL && DECL_THREAD_LOCAL (t))
+ 	return t;
+       *walk_subtrees = 0;
+     }
+ 
+   return NULL_TREE;
+ }
+ 
  /* Verify INIT (the initializer for DECL), and record the
     initialization in DECL_INITIAL, if appropriate.  Returns a new
     value for INIT.  */
*************** check_initializer (decl, init)
*** 7827,7832 ****
--- 7850,7860 ----
  	  TREE_TYPE (decl) = error_mark_node;
  	  init = NULL_TREE;
  	}
+       else if (walk_tree (&init, check_for_tls_addr, NULL, NULL))
+ 	{
+ 	  error ("initializer element addresses thread-local storage");
+ 	  init = NULL_TREE;
+ 	}
      }
  
    if (TREE_CODE (decl) == CONST_DECL)
*************** check_initializer (decl, init)
*** 7870,7875 ****
--- 7898,7912 ----
  	      else
  		goto dont_use_constructor;
  	    }
+ 
+ 	  /* The thread-local storage area is initialized via block copy
+ 	     inside the dynamic loader or other appropriate system library.
+ 	     We cannot support anything that requires complexity.  */
+ 	  if (DECL_THREAD_LOCAL (decl))
+ 	    {
+ 	      error ("thread-local storage cannot be initialized by constructor");
+ 	      init = error_mark_node;
+ 	    }
  	}
        else
  	{
*************** check_initializer (decl, init)
*** 7902,7909 ****
        check_for_uninitialized_const_var (decl);
  
        if (COMPLETE_TYPE_P (type) && TYPE_NEEDS_CONSTRUCTING (type))
! 	init = obscure_complex_init (decl, NULL_TREE);
! 
      }
    else
      check_for_uninitialized_const_var (decl);
--- 7939,7953 ----
        check_for_uninitialized_const_var (decl);
  
        if (COMPLETE_TYPE_P (type) && TYPE_NEEDS_CONSTRUCTING (type))
! 	{
! 	  if (DECL_THREAD_LOCAL (decl))
! 	    {
! 	      error ("thread-local storage cannot be initialized by constructor");
! 	      init = error_mark_node;
! 	    }
! 	  else
! 	    init = obscure_complex_init (decl, NULL_TREE);
! 	}
      }
    else
      check_for_uninitialized_const_var (decl);
*************** grokvardecl (type, declarator, specbits_
*** 9274,9279 ****
--- 9318,9333 ----
        TREE_PUBLIC (decl) = DECL_EXTERNAL (decl);
      }
  
+   if (RIDBIT_SETP (RID_THREAD, specbits))
+     {
+       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");
+     }
+ 
    if (TREE_PUBLIC (decl))
      {
        /* [basic.link]: A name with no linkage (notably, the name of a class
*************** grokdeclarator (declarator, declspecs, d
*** 10176,10185 ****
--- 10230,10251 ----
  		    }
  		  else if (RIDBIT_SETP (i, specbits))
  		    pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id));
+ 
+ 		  /* Diagnose "__thread extern".  Recall that this list
+ 		     is in the reverse order seen in the text.  */
+ 		  if (i == (int)RID_THREAD)
+ 		    {
+ 		      if (RIDBIT_SETP (RID_EXTERN, specbits))
+ 			error ("`__thread' before `extern'");
+ 		      if (RIDBIT_SETP (RID_STATIC, specbits))
+ 			error ("`__thread' before `static'");
+ 		    }
+ 
  		  if (i == (int)RID_EXTERN
  		      && TREE_PURPOSE (spec) == error_mark_node)
  		    /* This extern was part of a language linkage.  */
  		    extern_langp = 1;
+ 
  		  RIDBIT_SET (i, specbits);
  		  goto found;
  		}
*************** grokdeclarator (declarator, declspecs, d
*** 10476,10481 ****
--- 10542,10548 ----
      {
        if (RIDBIT_SETP (RID_STATIC, specbits)) nclasses++;
        if (RIDBIT_SETP (RID_EXTERN, specbits) && !extern_langp) nclasses++;
+       if (RIDBIT_SETP (RID_THREAD, specbits)) nclasses++;
        if (decl_context == PARM && nclasses > 0)
  	error ("storage class specifiers invalid in parameter declarations");
        if (RIDBIT_SETP (RID_TYPEDEF, specbits))
*************** grokdeclarator (declarator, declspecs, d
*** 10507,10512 ****
--- 10574,10586 ----
    /* Warn about storage classes that are invalid for certain
       kinds of declarations (parameters, typenames, etc.).  */
  
+   /* "static __thread" and "extern __thread" are allowed.  */
+   if (nclasses == 2
+       && RIDBIT_SETP (RID_THREAD, specbits)
+       && (RIDBIT_SETP (RID_EXTERN, specbits)
+ 	  || RIDBIT_SETP (RID_STATIC, specbits)))
+     nclasses = 1;
+     
    if (nclasses > 1)
      error ("multiple storage classes in declaration of `%s'", name);
    else if (decl_context != NORMAL && nclasses > 0)
*************** grokdeclarator (declarator, declspecs, d
*** 10562,10567 ****
--- 10636,10642 ----
  	  RIDBIT_RESET (RID_REGISTER, specbits);
  	  RIDBIT_RESET (RID_AUTO, specbits);
  	  RIDBIT_RESET (RID_EXTERN, specbits);
+ 	  RIDBIT_RESET (RID_THREAD, specbits);
  	}
      }
    else if (RIDBIT_SETP (RID_EXTERN, specbits) && initialized && !funcdef_flag)
*************** grokdeclarator (declarator, declspecs, d
*** 10584,10589 ****
--- 10659,10672 ----
        if (RIDBIT_SETP (RID_AUTO, specbits))
  	error ("top-level declaration of `%s' specifies `auto'", name);
      }
+   else if (RIDBIT_SETP (RID_THREAD, specbits)
+ 	   && !RIDBIT_SETP (RID_EXTERN, specbits)
+ 	   && !RIDBIT_SETP (RID_STATIC, specbits))
+     {
+       error ("function-scope `%s' implicitly auto and declared `__thread'",
+ 	     name);
+       RIDBIT_RESET (RID_THREAD, specbits);
+     }
  
    if (nclasses > 0 && friendp)
      error ("storage class specifiers invalid in friend function declarations");
*************** friend declaration requires class-key, i
*** 11784,11789 ****
--- 11867,11874 ----
  	  error ("storage class `auto' invalid for function `%s'", name);
  	else if (RIDBIT_SETP (RID_REGISTER, specbits))
  	  error ("storage class `register' invalid for function `%s'", name);
+ 	else if (RIDBIT_SETP (RID_THREAD, specbits))
+ 	  error ("storage class `__thread' invalid for function `%s'", name);
  
  	/* Function declaration not at top level.
  	   Storage classes other than `extern' are not allowed
Index: cp/lex.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/lex.c,v
retrieving revision 1.278
diff -c -p -d -r1.278 lex.c
*** cp/lex.c	22 May 2002 01:11:20 -0000	1.278
--- cp/lex.c	22 May 2002 02:00:06 -0000
*************** static const struct resword reswords[] =
*** 369,374 ****
--- 369,375 ----
    { "__restrict__",	RID_RESTRICT,	0 },
    { "__signed",		RID_SIGNED,	0 },
    { "__signed__",	RID_SIGNED,	0 },
+   { "__thread",		RID_THREAD,	0 },
    { "__typeof",		RID_TYPEOF,	0 },
    { "__typeof__",	RID_TYPEOF,	0 },
    { "__volatile",	RID_VOLATILE,	0 },
*************** const short rid_to_yy[RID_MAX] =
*** 474,480 ****
    /* RID_BOUNDED */	0,
    /* RID_UNBOUNDED */	0,
    /* RID_COMPLEX */	TYPESPEC,
!   /* RID_THREAD */	0,
  
    /* C++ */
    /* RID_FRIEND */	SCSPEC,
--- 475,481 ----
    /* RID_BOUNDED */	0,
    /* RID_UNBOUNDED */	0,
    /* RID_COMPLEX */	TYPESPEC,
!   /* RID_THREAD */	SCSPEC,
  
    /* C++ */
    /* RID_FRIEND */	SCSPEC,
Index: testsuite/g++.dg/dg.exp
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.dg/dg.exp,v
retrieving revision 1.5
diff -c -p -d -r1.5 dg.exp
*** testsuite/g++.dg/dg.exp	29 Mar 2002 21:55:36 -0000	1.5
--- testsuite/g++.dg/dg.exp	22 May 2002 02:00:06 -0000
*************** dg-init
*** 31,40 ****
  # Gather a list of all tests, with the exception of those in directories
  # that are handled specially.
  set all [lsort [find $srcdir/$subdir *.C]]
! set tests [prune [prune [prune [prune $all $srcdir/$subdir/special/*] \
!                                $srcdir/$subdir/debug/*] \
!                         $srcdir/$subdir/gcov/*] \
!                  $srcdir/$subdir/bprob/*]
  
  # Main loop.
  dg-runtest $tests "" $DEFAULT_CXXFLAGS
--- 31,41 ----
  # Gather a list of all tests, with the exception of those in directories
  # that are handled specially.
  set all [lsort [find $srcdir/$subdir *.C]]
! set tests [prune $tests $srcdir/$subdir/bprob/*]
! set tests [prune $tests $srcdir/$subdir/debug/*]
! set tests [prune $tests $srcdir/$subdir/gcov/*]
! set tests [prune $tests $srcdir/$subdir/special/*]
! set tests [prune $tests $srcdir/$subdir/tls/*]
  
  # Main loop.
  dg-runtest $tests "" $DEFAULT_CXXFLAGS
Index: testsuite/g++.dg/tls/diag-1.C
===================================================================
RCS file: testsuite/g++.dg/tls/diag-1.C
diff -N testsuite/g++.dg/tls/diag-1.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/tls/diag-1.C	22 May 2002 02:00:06 -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/g++.dg/tls/diag-2.C
===================================================================
RCS file: testsuite/g++.dg/tls/diag-2.C
diff -N testsuite/g++.dg/tls/diag-2.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/tls/diag-2.C	22 May 2002 02:00:06 -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 for function" } */
+ extern __thread void f2 ();	/* { dg-error "invalid for function" } */
+ static __thread void f3 ();	/* { dg-error "invalid for function" } */
+ __thread void f4 () { }		/* { dg-error "invalid for function" } */
+ 
+ void bar(__thread int p1);	/* { dg-error "(invalid in parameter)|(specified for parameter)" } */
Index: testsuite/g++.dg/tls/init-1.C
===================================================================
RCS file: testsuite/g++.dg/tls/init-1.C
diff -N testsuite/g++.dg/tls/init-1.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/tls/init-1.C	22 May 2002 02:00:06 -0000
***************
*** 0 ****
--- 1,11 ----
+ /* Invalid initializations.  */
+ 
+ extern __thread int i;
+ int *p = &i;	/* { dg-error "initializer element addresses thread-local storage" } */
+ 
+ struct S
+ {
+   S();
+ };
+ 
+ __thread S s;	/* { dg-error "thread-local storage cannot be initialized by constructor" } */
Index: testsuite/g++.dg/tls/tls.exp
===================================================================
RCS file: testsuite/g++.dg/tls/tls.exp
diff -N testsuite/g++.dg/tls/tls.exp
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/tls/tls.exp	22 May 2002 02:00:06 -0000
***************
*** 0 ****
--- 1,44 ----
+ #   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 g++-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 [g++_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_CXXFLAGS
+ if ![info exists DEFAULT_CXXFLAGS] then {
+     set DEFAULT_CXXFLAGS " -ansi -pedantic-errors -Wno-long-long"
+ }
+ 
+ # Initialize `dg'.
+ dg-init
+ 
+ # Main loop.
+ dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] "" $DEFAULT_CXXFLAGS
+ 
+ # All done.
+ dg-finish
Index: testsuite/g++.dg/tls/trivial.C
===================================================================
RCS file: testsuite/g++.dg/tls/trivial.C
diff -N testsuite/g++.dg/tls/trivial.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/tls/trivial.C	22 May 2002 02:00:06 -0000
***************
*** 0 ****
--- 1 ----
+ __thread int i;


More information about the Gcc-patches mailing list