This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

emulating tls for systems without


Jakub, I've finished up that WIP patch you sent me this morning.

For everyone else, we've come up with a scheme to emulate tls 
with the __gthread_[gs]etspecific routines that thread-enabled
systems are supposed to provide in their gcc port.  It uses a
control block (struct __emutls_object) that describes the shape
and position within the block referenced by __gthread_getspecific.
It has the same visibility as the original variable, so the same
lookup rules apply.  For COMMON blocks, this requires a bit of
extra help, since we can't get the linker to do the sizing for us.
This is handled by making the control block COMMON (so that the
right unification rules apply), and then using a constructor to
set the size (which then applies size maximization rules).

As far as I can tell, it works.  My test case is

__thread int p;
__thread int pi = 1;
static __thread int s;
static __thread int si = 1;
__thread int c __attribute__((common));
__thread int ci __attribute__((common)) = 1;

int f() { return p+pi+s+si+c+ci; }
int main()
{
  if (f() != 3) abort ();
  return 1;
}

It would be good if some windows or other similarly tls deprived
systems could test to see if openmp threadprivate tests work as
expected.

We'll probably want to adjust some config tests before this goes in,
since I'm not sure that libstdc++, libjava, libmudflap, and libgomp
actually benefit when using this sort of emulation.


r~


gcc/
	* Makefile.in (libgcc.mk, LIBGCC_DEPS): Add emutls.c.
	* builtin-types.def (BT_WORD): Make unsigned.
	(BT_FN_VOID_PTR_WORD_WORD_PTR): New.
	* builtins.def (BUILT_IN_EMUTLS_GET_ADDRESS): New.
	(BUILT_IN_EMUTLS_REGISTER_COMMON): New.
	* c-decl.c (grokdeclarator): Don't error if !have_tls.
	* c-parser.c (c_parser_omp_threadprivate): Likewise.
	* cgraph.c (dump_cgraph_varpool_node): Don't ICE if
	cgraph_function_flags_ready unset.
	* cgraphunit.c (cgraph_build_static_cdtor): Flush variable queue.
	* dwarf2out.c (loc_descriptor_from_tree_1): Don't do anything for
	emulated tls.
	* expr.c (expand_expr_real_1): Expand emulated tls.
	* libgcc-std.ver: Add __emutls_get_address, __emutls_register_common.
	* output.h (emutls_finish): Declare.
	* toplev.c (compile_file): Call it.
	* tree.h (emutls_decl): Declare.
	* varasm.c (emutls_htab, emutls_object_type): New.
	(EMUTLS_VAR_PREFIX, EMUTLS_TMPL_PREFIX): New.
	(get_emutls_object_type, get_emutls_init_templ_addr): New.
	(emutls_decl, emutls_common_1, emutls_finish): New.
	(assemble_variable): When emulating tls, swap decls.
	(default_encode_section_info): Don't add SYMBOL_FLAG_TLS_SHIFT
	for emulated tls.
	* emutls.c: New file.

	* config/sparc/sol2.h (ASM_DECLARE_OBJECT_NAME): Only emit
	tls_object for real tls.

gcc/cp/
	* decl.c (grokvardecl): Don't error if !have_tls.
	(grokdeclarator): Likewise.
	* parser.c (cp_parser_omp_threadprivate): Likewise.

gcc/fortran/
	* f95-lang.c (gfc_init_builtin_functions): Add __emutls_get_address
	and __emutls_register_common.
	* openmp.c (gfc_match_omp_threadprivate): Don't error if !have_tls.
	* trans-common.c (build_common_decl): Don't check have_tls.
	* trans-decl.c (gfc_finish_var_decl): Likewise.
	* types.def (BT_WORD, BT_FN_PTR_PTR): New.
	(BT_FN_VOID_PTR_WORD_WORD_PTR): New.

=== gcc/Makefile.in
==================================================================
--- gcc/Makefile.in	(revision 117099)
+++ gcc/Makefile.in	(local)
@@ -1422,9 +1422,9 @@
 	LIBGCOV='$(LIBGCOV)' \
 	LIB2ADD='$(LIB2ADD)' \
 	LIB2ADD_ST='$(LIB2ADD_ST)' \
-	LIB2ADDEH='$(LIB2ADDEH)' \
-	LIB2ADDEHSTATIC='$(LIB2ADDEHSTATIC)' \
-	LIB2ADDEHSHARED='$(LIB2ADDEHSHARED)' \
+	LIB2ADDEH='$(LIB2ADDEH) $(srcdir)/emutls.c' \
+	LIB2ADDEHSTATIC='$(LIB2ADDEHSTATIC) $(srcdir)/emutls.c' \
+	LIB2ADDEHSHARED='$(LIB2ADDEHSHARED) $(srcdir)/emutls.c' \
 	LIB2ADDEHDEP='$(LIB2ADDEHDEP)' \
 	LIB2_SIDITI_CONV_FUNCS='$(LIB2_SIDITI_CONV_FUNCS)' \
 	LIBUNWIND='$(LIBUNWIND)' \
@@ -1469,8 +1469,8 @@
 	$(MACHMODE_H) longlong.h gbl-ctors.h config.status $(srcdir)/libgcc2.h \
 	tsystem.h $(FPBIT) $(DPBIT) $(TPBIT) $(LIB2ADD) \
 	config/dfp-bit.h config/dfp-bit.c \
-	$(LIB2ADD_ST) $(LIB2ADDEH) $(LIB2ADDEHDEP) $(EXTRA_PARTS) \
-	$(srcdir)/config/$(LIB1ASMSRC) \
+	$(LIB2ADD_ST) $(LIB2ADDEH) $(srcdir)/emutls.c $(LIB2ADDEHDEP) \
+	$(EXTRA_PARTS) $(srcdir)/config/$(LIB1ASMSRC) \
 	$(srcdir)/gcov-io.h $(srcdir)/gcov-io.c gcov-iov.h $(SFP_MACHINE)
 
 libgcov.a: libgcc.a; @true
=== gcc/builtin-types.def
==================================================================
--- gcc/builtin-types.def	(revision 117099)
+++ gcc/builtin-types.def	(local)
@@ -74,7 +74,7 @@
 DEF_PRIMITIVE_TYPE (BT_ULONGLONG, long_long_unsigned_type_node)
 DEF_PRIMITIVE_TYPE (BT_INTMAX, intmax_type_node)
 DEF_PRIMITIVE_TYPE (BT_UINTMAX, uintmax_type_node)
-DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 0))
+DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 1))
 DEF_PRIMITIVE_TYPE (BT_FLOAT, float_type_node)
 DEF_PRIMITIVE_TYPE (BT_DOUBLE, double_type_node)
 DEF_PRIMITIVE_TYPE (BT_LONGDOUBLE, long_double_type_node)
@@ -375,6 +375,8 @@
 		     BT_INT, BT_FILEPTR, BT_INT, BT_CONST_STRING, BT_VALIST_ARG)
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT,
 		     BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT)
+DEF_FUNCTION_TYPE_4 (BT_FN_VOID_PTR_WORD_WORD_PTR,
+		     BT_VOID, BT_PTR, BT_WORD, BT_WORD, BT_PTR)
 
 DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG,
 		     BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING,
=== gcc/builtins.def
==================================================================
--- gcc/builtins.def	(revision 117099)
+++ gcc/builtins.def	(local)
@@ -1,6 +1,6 @@
 /* This file contains the definitions and documentation for the
    builtins used in the GNU compiler.
-   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005
+   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -718,6 +718,10 @@
 DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_ENTER, "profile_func_enter")
 DEF_BUILTIN_STUB (BUILT_IN_PROFILE_FUNC_EXIT, "profile_func_exit")
 
+/* TLS emulation.  */
+DEF_EXT_LIB_BUILTIN (BUILT_IN_EMUTLS_GET_ADDRESS, "__emutls_get_address", BT_FN_PTR_PTR, ATTR_CONST_NOTHROW_NONNULL)
+DEF_EXT_LIB_BUILTIN (BUILT_IN_EMUTLS_REGISTER_COMMON, "__emutls_register_common", BT_FN_VOID_PTR_WORD_WORD_PTR, ATTR_NOTHROW_LIST)
+
 /* Synchronization Primitives.  */
 #include "sync-builtins.def"
 
=== gcc/c-decl.c
==================================================================
--- gcc/c-decl.c	(revision 117099)
+++ gcc/c-decl.c	(local)
@@ -4804,14 +4804,7 @@
 	  }
 
 	if (threadp)
-	  {
-	    if (targetm.have_tls)
-	      DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
-	    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");
-	  }
+	  DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
       }
 
     if (storage_class == csc_extern
=== gcc/c-parser.c
==================================================================
--- gcc/c-parser.c	(revision 117099)
+++ gcc/c-parser.c	(local)
@@ -7801,9 +7801,6 @@
   c_parser_consume_pragma (parser);
   vars = c_parser_omp_var_list_parens (parser, 0, NULL);
 
-  if (!targetm.have_tls)
-    sorry ("threadprivate variables not supported in this target");
-
   /* Mark every variable in VARS to be assigned thread local storage.  */
   for (t = vars; t; t = TREE_CHAIN (t))
     {
=== gcc/cgraph.c
==================================================================
--- gcc/cgraph.c	(revision 117099)
+++ gcc/cgraph.c	(local)
@@ -786,7 +786,10 @@
 dump_cgraph_varpool_node (FILE *f, struct cgraph_varpool_node *node)
 {
   fprintf (f, "%s:", cgraph_varpool_node_name (node));
-  fprintf (f, " availability:%s", availability_names [cgraph_variable_initializer_availability (node)]);
+  fprintf (f, " availability:%s",
+	   cgraph_function_flags_ready
+	   ? availability_names[cgraph_variable_initializer_availability (node)]
+	   : "not-ready");
   if (DECL_INITIAL (node->decl))
     fprintf (f, " initialized");
   if (node->needed)
=== gcc/cgraphunit.c
==================================================================
--- gcc/cgraphunit.c	(revision 117099)
+++ gcc/cgraphunit.c	(local)
@@ -1709,6 +1709,7 @@
     {
       tree_lowering_passes (decl);
       tree_rest_of_compilation (decl);
+      cgraph_varpool_assemble_pending_decls ();
     }
   else
     cgraph_finalize_function (decl, 0);
=== gcc/config/sparc/sol2.h
==================================================================
--- gcc/config/sparc/sol2.h	(revision 117099)
+++ gcc/config/sparc/sol2.h	(local)
@@ -89,7 +89,7 @@
     {								\
       HOST_WIDE_INT size;					\
 								\
-      if (DECL_THREAD_LOCAL_P (DECL))				\
+      if (targetm.have_tls && DECL_THREAD_LOCAL_P (DECL))	\
 	ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "tls_object");	\
       else							\
 	ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "object");	\
=== gcc/cp/decl.c
==================================================================
--- gcc/cp/decl.c	(revision 117099)
+++ gcc/cp/decl.c	(local)
@@ -6320,14 +6320,7 @@
     }
 
   if (declspecs->specs[(int)ds_thread])
-    {
-      if (targetm.have_tls)
-	DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
-      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");
-    }
+    DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
 
   if (TREE_PUBLIC (decl))
     {
@@ -8451,15 +8444,7 @@
 		DECL_EXTERNAL (decl) = 1;
 
 		if (thread_p)
-		  {
-		    if (targetm.have_tls)
-		      DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
-		    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");
-		  }
+		  DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
 	      }
 	    else
 	      {
=== gcc/cp/parser.c
==================================================================
--- gcc/cp/parser.c	(revision 117099)
+++ gcc/cp/parser.c	(local)
@@ -18921,9 +18921,6 @@
   vars = cp_parser_omp_var_list (parser, 0, NULL);
   cp_parser_require_pragma_eol (parser, pragma_tok);
 
-  if (!targetm.have_tls)
-    sorry ("threadprivate variables not supported in this target");
-
   finish_omp_threadprivate (vars);
 }
 
=== gcc/dwarf2out.c
==================================================================
--- gcc/dwarf2out.c	(revision 117099)
+++ gcc/dwarf2out.c	(local)
@@ -9130,7 +9130,7 @@
 	  rtx rtl;
 
 	  /* If this is not defined, we have no way to emit the data.  */
-	  if (!targetm.asm_out.output_dwarf_dtprel)
+	  if (!targetm.have_tls || !targetm.asm_out.output_dwarf_dtprel)
 	    return 0;
 
 	  /* The way DW_OP_GNU_push_tls_address is specified, we can only
=== gcc/expr.c
==================================================================
--- gcc/expr.c	(revision 117099)
+++ gcc/expr.c	(local)
@@ -6853,6 +6853,22 @@
 	  && (TREE_STATIC (exp) || DECL_EXTERNAL (exp)))
 	layout_decl (exp, 0);
 
+      /* TLS emulation hook - replace __thread vars with
+	 *__emutls_get_address (&_emutls.var).  */
+      if (! targetm.have_tls
+	  && TREE_CODE (exp) == VAR_DECL
+	  && DECL_THREAD_LOCAL_P (exp))
+	{
+	  tree et = emutls_decl (exp);
+	  tree fn = built_in_decls [BUILT_IN_EMUTLS_GET_ADDRESS];
+	  tree arg = build_fold_addr_expr_with_type (et, ptr_type_node);
+	  tree arglist = build_tree_list (NULL_TREE, arg);
+	  tree call = build_function_call_expr (fn, arglist);
+	  exp = fold_convert (build_pointer_type (TREE_TYPE (exp)), call);
+	  exp = build_fold_indirect_ref (exp);
+	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
+	}
+
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
=== gcc/fortran/f95-lang.c
==================================================================
--- gcc/fortran/f95-lang.c	(revision 117099)
+++ gcc/fortran/f95-lang.c	(local)
@@ -1102,6 +1102,14 @@
 		      BUILT_IN_TRAP, NULL, false);
   TREE_THIS_VOLATILE (built_in_decls[BUILT_IN_TRAP]) = 1;
 
+  gfc_define_builtin ("__emutls_get_address",
+		      builtin_types[BT_FN_PTR_PTR], BUILT_IN_EMUTLS_GET_ADDRESS,
+		      "__emutls_get_address", true);
+  gfc_define_builtin ("__emutls_register_common",
+		      builtin_types[BT_FN_VOID_PTR_WORD_WORD_PTR],
+		      BUILT_IN_EMUTLS_REGISTER_COMMON,
+		      "__emutls_register_common", false);
+
   build_common_builtin_nodes ();
   targetm.init_builtins ();
 }
=== gcc/fortran/openmp.c
==================================================================
--- gcc/fortran/openmp.c	(revision 117099)
+++ gcc/fortran/openmp.c	(local)
@@ -465,12 +465,6 @@
   if (m != MATCH_YES)
     return m;
 
-  if (!targetm.have_tls)
-    {
-      sorry ("threadprivate variables not supported in this target");
-      goto cleanup;
-    }
-
   for (;;)
     {
       m = gfc_match_symbol (&sym, 0);
=== gcc/fortran/trans-common.c
==================================================================
--- gcc/fortran/trans-common.c	(revision 117099)
+++ gcc/fortran/trans-common.c	(local)
@@ -388,7 +388,7 @@
 
       gfc_set_decl_location (decl, &com->where);
 
-      if (com->threadprivate && targetm.have_tls)
+      if (com->threadprivate)
 	DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
 
       /* Place the back end declaration for this common block in
=== gcc/fortran/trans-decl.c
==================================================================
--- gcc/fortran/trans-decl.c	(revision 117099)
+++ gcc/fortran/trans-decl.c	(local)
@@ -515,7 +515,7 @@
     TREE_STATIC (decl) = 1;
 
   /* Handle threadprivate variables.  */
-  if (sym->attr.threadprivate && targetm.have_tls
+  if (sym->attr.threadprivate
       && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
     DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
 }
=== gcc/fortran/types.def
==================================================================
--- gcc/fortran/types.def	(revision 117099)
+++ gcc/fortran/types.def	(local)
@@ -55,6 +55,7 @@
 DEF_PRIMITIVE_TYPE (BT_INT, integer_type_node)
 DEF_PRIMITIVE_TYPE (BT_UINT, unsigned_type_node)
 DEF_PRIMITIVE_TYPE (BT_LONG, long_integer_type_node)
+DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 1))
 
 DEF_PRIMITIVE_TYPE (BT_I1, builtin_type_for_size (BITS_PER_UNIT*1, 1))
 DEF_PRIMITIVE_TYPE (BT_I2, builtin_type_for_size (BITS_PER_UNIT*2, 1))
@@ -81,6 +82,7 @@
 DEF_FUNCTION_TYPE_1 (BT_FN_VOID_PTRPTR, BT_VOID, BT_PTR_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_VOID_VPTR, BT_VOID, BT_VOLATILE_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_UINT_UINT, BT_UINT, BT_UINT)
+DEF_FUNCTION_TYPE_1 (BT_FN_PTR_PTR, BT_PTR, BT_PTR)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR)
 
@@ -113,6 +115,8 @@
 
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_OMPFN_PTR_UINT_UINT,
                      BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT, BT_UINT)
+DEF_FUNCTION_TYPE_4 (BT_FN_VOID_PTR_WORD_WORD_PTR,
+		     BT_VOID, BT_PTR, BT_WORD, BT_WORD, BT_PTR)
 
 DEF_FUNCTION_TYPE_5 (BT_FN_BOOL_LONG_LONG_LONG_LONGPTR_LONGPTR,
                      BT_BOOL, BT_LONG, BT_LONG, BT_LONG,
=== gcc/libgcc-std.ver
==================================================================
--- gcc/libgcc-std.ver	(revision 117099)
+++ gcc/libgcc-std.ver	(local)
@@ -273,4 +273,6 @@
   __floatuntixf
   __floatuntitf
   _Unwind_GetIPInfo
+  __emutls_get_address
+  __emutls_register_common
 }
=== gcc/output.h
==================================================================
--- gcc/output.h	(revision 117099)
+++ gcc/output.h	(local)
@@ -158,6 +158,9 @@
 /* Emit any pending weak declarations.  */
 extern void weak_finish (void);
 
+/* Emit any pending emutls declarations and initializations.  */
+extern void emutls_finish (void);
+
 /* Decode an `asm' spec for a declaration as a register name.
    Return the register number, or -1 if nothing specified,
    or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized,
=== gcc/toplev.c
==================================================================
--- gcc/toplev.c	(revision 117099)
+++ gcc/toplev.c	(local)
@@ -1056,11 +1056,14 @@
   if (flag_mudflap)
     mudflap_finish_file ();
 
+  /* Likewise for emulated thread-local storage.  */
+  if (!targetm.have_tls)
+    emutls_finish ();
+
   output_shared_constant_pool ();
   output_object_blocks ();
 
   /* Write out any pending weak symbol declarations.  */
-
   weak_finish ();
 
   /* Do dbx symbols.  */
=== gcc/tree.h
==================================================================
--- gcc/tree.h	(revision 117099)
+++ gcc/tree.h	(local)
@@ -4457,6 +4457,7 @@
 extern void process_pending_assemble_externals (void);
 extern void finish_aliases_1 (void);
 extern void finish_aliases_2 (void);
+extern tree emutls_decl (tree);
 
 /* In stmt.c */
 extern void expand_computed_goto (tree);
=== gcc/varasm.c
==================================================================
--- gcc/varasm.c	(revision 117099)
+++ gcc/varasm.c	(local)
@@ -53,6 +53,7 @@
 #include "cgraph.h"
 #include "cfglayout.h"
 #include "basic-block.h"
+#include "tree-iterator.h"
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"		/* Needed for external data
@@ -200,6 +201,238 @@
 /* A pool of constants that can be shared between functions.  */
 static GTY(()) struct rtx_constant_pool *shared_constant_pool;
 
+/* TLS emulation.  */
+
+static GTY ((if_marked ("tree_map_marked_p"), param_is (struct tree_map)))
+     htab_t emutls_htab;
+static GTY (()) tree emutls_object_type;
+
+#ifndef NO_DOT_IN_LABEL
+# define EMUTLS_VAR_PREFIX	"__emutls_v."
+# define EMUTLS_TMPL_PREFIX	"__emutls_t."
+#elif !defined NO_DOLLAR_IN_LABEL
+# define EMUTLS_VAR_PREFIX	"__emutls_v$"
+# define EMUTLS_TMPL_PREFIX	"__emutls_t$"
+#else
+# define EMUTLS_VAR_PREFIX	"__emutls_v_"
+# define EMUTLS_TMPL_PREFIX	"__emutls_t_"
+#endif
+
+/* Create the structure for struct __emutls_object.  This should match the
+   structure at the top of emutls.c, modulo the union there.  */
+
+static tree
+get_emutls_object_type (void)
+{
+  tree type, type_name, field, next_field, word_type_node;
+
+  type = emutls_object_type;
+  if (type)
+    return type;
+
+  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
+  type_name = get_identifier ("__emutls_object");
+  type_name = build_decl (TYPE_DECL, type_name, type);
+  TYPE_NAME (type) = type_name;
+
+  field = build_decl (FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
+  DECL_CONTEXT (field) = type;
+  next_field = field;
+
+  field = build_decl (FIELD_DECL, get_identifier ("__offset"), ptr_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+  next_field = field;
+
+  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+  field = build_decl (FIELD_DECL, get_identifier ("__align"), word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+  next_field = field;
+
+  field = build_decl (FIELD_DECL, get_identifier ("__size"), word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+
+  TYPE_FIELDS (type) = field;
+  layout_type (type);
+
+  return type;
+}
+
+/* Create a read-only variable like DECL, with the same DECL_INITIAL.
+   This will be used for initializing the emulated tls data area.  */
+
+static tree
+get_emutls_init_templ_addr (tree decl)
+{
+  tree name, to;
+  char *toname;
+
+  if (!DECL_INITIAL (decl))
+    return null_pointer_node;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  toname = alloca (strlen (IDENTIFIER_POINTER (name))
+		   + sizeof (EMUTLS_TMPL_PREFIX));
+  strcpy (toname, EMUTLS_TMPL_PREFIX);
+  strcpy (toname + sizeof (EMUTLS_TMPL_PREFIX) - 1, IDENTIFIER_POINTER (name));
+  to = build_decl (VAR_DECL, get_identifier (toname), TREE_TYPE (decl));
+  DECL_ARTIFICIAL (to) = 1;
+  TREE_USED (to) = TREE_USED (decl);
+  TREE_READONLY (to) = 1;
+  DECL_IGNORED_P (to) = 1;
+  DECL_CONTEXT (to) = DECL_CONTEXT (decl);
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  if (DECL_ONE_ONLY (decl))
+    {
+      make_decl_one_only (to);
+      TREE_STATIC (to) = TREE_STATIC (decl);
+      TREE_PUBLIC (to) = TREE_PUBLIC (decl);
+    }
+  else
+    TREE_STATIC (to) = 1;
+      
+  DECL_INITIAL (to) = DECL_INITIAL (decl);
+
+  cgraph_varpool_finalize_decl (to);
+  return build_fold_addr_expr (to);
+}
+
+/* When emulating tls, we use a control structure for use by the runtime.
+   Create and return this structure.  */
+
+tree
+emutls_decl (tree decl)
+{
+  tree name, to, type;
+  struct tree_map *h, in;
+  void **loc;
+  char *toname;
+
+  if (targetm.have_tls || decl == NULL || decl == error_mark_node
+      || TREE_CODE (decl) != VAR_DECL || ! DECL_THREAD_LOCAL_P (decl))
+    return decl;
+
+  /* Look up the object in the hash; return the control structure if
+     it has already been created.  */
+  if (! emutls_htab)
+    emutls_htab = htab_create_ggc (512, tree_map_hash, tree_map_eq, 0);
+
+  name = DECL_ASSEMBLER_NAME (decl);
+
+  /* Note that we use the hash of the decl's name, rather than a hash
+     of the decl's pointer.  In emutls_finish we iterate through the
+     hash table, and we want this traversal to be predictable.  */
+  in.hash = htab_hash_string (IDENTIFIER_POINTER (name));
+  in.from = decl;
+  loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT);
+  h = *loc;
+  if (h != NULL)
+    return h->to;
+
+  /* The control object has not been created; do so now.  */
+  toname = alloca (strlen (IDENTIFIER_POINTER (name))
+		   + sizeof (EMUTLS_VAR_PREFIX));
+  strcpy (toname, EMUTLS_VAR_PREFIX);
+  strcpy (toname + sizeof (EMUTLS_VAR_PREFIX) - 1, IDENTIFIER_POINTER (name));
+  type = get_emutls_object_type ();
+  to = build_decl (VAR_DECL, get_identifier (toname), type);
+  DECL_ARTIFICIAL (to) = 1;
+  TREE_STATIC (to) = TREE_STATIC (decl);
+  TREE_USED (to) = TREE_USED (decl);
+  TREE_PUBLIC (to) = TREE_PUBLIC (decl);
+  TREE_READONLY (to) = 0;
+  DECL_IGNORED_P (to) = 1;
+  DECL_EXTERNAL (to) = DECL_EXTERNAL (decl);
+  DECL_CONTEXT (to) = DECL_CONTEXT (decl);
+  DECL_COMMON (to) = DECL_COMMON (decl);
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  if (DECL_ONE_ONLY (decl))
+    make_decl_one_only (to);
+
+  /* If this variable is defined locally, then we need to initialize the
+     control structure with size and alignment information.  */
+  if (! DECL_EXTERNAL (to) && ! DECL_COMMON (to))
+    {
+      VEC(constructor_elt,gc) *v = VEC_alloc (constructor_elt, gc, 4);
+      constructor_elt *elt;
+      tree field = TYPE_FIELDS (type);
+
+      elt = VEC_quick_push (constructor_elt, v, NULL);
+      elt->index = field;
+      elt->value = fold_convert (TREE_TYPE (field), DECL_SIZE_UNIT (decl));
+
+      elt = VEC_quick_push (constructor_elt, v, NULL);
+      field = TREE_CHAIN (field);
+      elt->index = field;
+      elt->value = build_int_cst (TREE_TYPE (field), DECL_ALIGN_UNIT (decl));
+
+      elt = VEC_quick_push (constructor_elt, v, NULL);
+      field = TREE_CHAIN (field);
+      elt->index = field;
+      elt->value = null_pointer_node;
+
+      elt = VEC_quick_push (constructor_elt, v, NULL);
+      field = TREE_CHAIN (field);
+      elt->index = field;
+      elt->value = get_emutls_init_templ_addr (decl);
+
+      DECL_INITIAL (to) = build_constructor (type, v);
+    }
+
+  h = ggc_alloc (sizeof (struct tree_map));
+  h->hash = in.hash;
+  h->from = decl;
+  h->to = to;
+  *(struct tree_map **) loc = h;
+
+  return to;
+}
+
+static int
+emutls_common_1 (void **loc, void *xstmts)
+{
+  struct tree_map *h = *(struct tree_map **) loc;
+  tree args, x, *pstmts = (tree *) xstmts;
+  tree word_type_node;
+
+  if (!DECL_COMMON (h->from))
+    return 1;
+
+  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+  
+  x = get_emutls_init_templ_addr (h->from);
+  args = tree_cons (NULL, x, NULL);
+  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->from));
+  args = tree_cons (NULL, x, args);
+  x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->from));
+  args = tree_cons (NULL, x, args);
+  x = build_fold_addr_expr (h->to);
+  args = tree_cons (NULL, x, args);
+
+  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
+  x = build_function_call_expr (x, args);
+
+  append_to_statement_list (x, pstmts);
+  return 1;
+}
+
+void
+emutls_finish (void)
+{
+  tree body = NULL_TREE;
+
+  if (emutls_htab == NULL)
+    return;
+
+  htab_traverse_noresize (emutls_htab, emutls_common_1, &body);
+  if (body == NULL_TREE)
+    return;
+
+  cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY);
+}
+
 /* Helper routines for maintaining section_htab.  */
 
 static int
@@ -1733,6 +1966,11 @@
   rtx decl_rtl, symbol;
   section *sect;
 
+  if (! targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    decl = emutls_decl (decl);
+
   if (lang_hooks.decls.prepare_assemble_variable)
     lang_hooks.decls.prepare_assemble_variable (decl);
 
@@ -5755,7 +5993,8 @@
     flags |= SYMBOL_FLAG_FUNCTION;
   if (targetm.binds_local_p (decl))
     flags |= SYMBOL_FLAG_LOCAL;
-  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
+  if (targetm.have_tls && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
     flags |= DECL_TLS_MODEL (decl) << SYMBOL_FLAG_TLS_SHIFT;
   else if (targetm.in_small_data_p (decl))
     flags |= SYMBOL_FLAG_SMALL;
--- /dev/null	2006-09-19 10:30:36.490481000 -0700
+++ gcc/emutls.c	2006-09-20 12:44:32.000000000 -0700
@@ -0,0 +1,191 @@
+/* TLS emulation.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Contributed by Jakub Jelinek <jakub@redhat.com>.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+GCC 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 GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "gthr.h"
+
+typedef unsigned int word __attribute__((mode(word)));
+typedef unsigned int pointer __attribute__((mode(pointer)));
+
+struct __emutls_object
+{
+  word size;
+  word align;
+  union {
+    pointer offset;
+    void *ptr;
+  } loc;
+  void *templ;
+};
+
+#ifdef __GTHREADS
+#ifdef __GTHREAD_MUTEX_INIT
+static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
+#else
+static __gthread_mutex_t emutls_mutex;
+#endif
+static __gthread_key_t emutls_key;
+static pointer emutls_size;
+
+static void
+emutls_destroy (void *ptr)
+{
+  void ***arr = (void ***) ptr;
+  unsigned long int size = (unsigned long int) arr[0];
+  ++arr;
+  while (--size)
+    {
+      if (*arr)
+	free ((*arr)[-1]);
+      ++arr;
+    }
+  free (ptr);
+}
+
+static void
+emutls_init (void)
+{
+#ifndef __GTHREAD_MUTEX_INIT
+  __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex);
+#endif
+  if (__gthread_key_create (&emutls_key, emutls_destroy) != 0)
+    abort ();
+}
+#endif
+
+static void *
+emutls_alloc (struct __emutls_object *obj)
+{
+  void *ptr;
+  void *ret;
+
+  /* We could use here posix_memalign if available and adjust
+     emutls_destroy accordingly.  */
+  if (obj->align <= sizeof (void *))
+    {
+      ptr = malloc (obj->size + sizeof (void *));
+      if (ptr == NULL)
+	abort ();
+      ((void **) ptr)[0] = ptr;
+      ret = ptr + sizeof (void *);
+    }
+  else
+    {
+      ptr = malloc (obj->size + sizeof (void *) + obj->align - 1);
+      if (ptr == NULL)
+	abort ();
+      ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1))
+		      & ~(pointer)(obj->align - 1));
+      ((void **) ret)[-1] = ptr;
+    }
+
+  if (obj->templ)
+    memcpy (ret, obj->templ, obj->size);
+  else
+    memset (ret, 0, obj->size);
+
+  return ret;
+}
+
+void *
+__emutls_get_address (struct __emutls_object *obj)
+{
+  if (! __gthread_active_p ())
+    {
+      if (__builtin_expect (obj->loc.ptr == NULL, 0))
+	obj->loc.ptr = emutls_alloc (obj);
+      return obj->loc.ptr;
+    }
+
+#ifndef __GTHREADS
+  abort ();
+#else
+  pointer offset;
+
+  if (__builtin_expect (obj->loc.offset == 0, 0))
+    {
+      static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+      __gthread_once (&once, emutls_init);
+      __gthread_mutex_lock (&emutls_mutex);
+      offset = ++emutls_size;
+      obj->loc.offset = offset;
+      __gthread_mutex_unlock (&emutls_mutex);
+    }
+  else
+    offset = obj->loc.offset;
+
+  void **arr = (void **) __gthread_getspecific (emutls_key);
+  if (__builtin_expect (arr == NULL, 0))
+    {
+      pointer size = offset + 32;
+      arr = calloc (size, sizeof (void *));
+      if (arr == NULL)
+	abort ();
+      arr[0] = (void *) size;
+      __gthread_setspecific (emutls_key, (void *) arr);
+    }
+  else if (__builtin_expect (offset >= (pointer) arr[0], 0))
+    {
+      pointer orig_size = (pointer) arr[0];
+      pointer size = orig_size * 2;
+      if (offset >= size)
+	size = offset + 32;
+      arr = realloc (arr, size * sizeof (void *));
+      if (arr == NULL)
+	abort ();
+      memset (arr + orig_size, 0, (size - orig_size) * sizeof (void *));
+      __gthread_setspecific (emutls_key, (void *) arr);
+    }
+
+  void *ret = arr[offset];
+  if (__builtin_expect (ret == NULL, 0))
+    {
+      ret = emutls_alloc (obj);
+      arr[offset] = ret;
+    }
+  return ret;
+#endif
+}
+
+void
+__emutls_register_common (struct __emutls_object *obj,
+			  word size, word align, void *templ)
+{
+  if (obj->size < size)
+    {
+      obj->size = size;
+      obj->templ = NULL;
+    }
+  if (obj->align < align)
+    obj->align = align;
+  if (templ && size == obj->size)
+    obj->templ = templ;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]