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]

Re: emulating tls for systems without


On Mon, Sep 25, 2006 at 01:49:02PM +1200, Danny Smith wrote:
> /develop/svn/trunk/gcc/libgomp/testsuite/libgomp.fortran/omp_parse3.f90:65:
> undefined reference to `__emutls_v.z.967.1517'

This should be fixed in this version.

I'm seeing race condition failures in some tests, and I'm not
sure where they could be coming from.  Of course they go away
when debugging...



r~



--- gcc/Makefile.in	(revision 117204)
+++ gcc/Makefile.in	(local)
@@ -1422,9 +1422,9 @@ libgcc.mk: config.status Makefile mklibg
 	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 @@ LIBGCC_DEPS = $(GCC_PASSES) stmp-int-hdr
 	$(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	(revision 117204)
+++ gcc/builtin-types.def	(local)
@@ -74,7 +74,7 @@ DEF_PRIMITIVE_TYPE (BT_LONGLONG, long_lo
 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 @@ DEF_FUNCTION_TYPE_4 (BT_FN_INT_FILEPTR_I
 		     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	(revision 117204)
+++ 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_EXT_LIB_BUILTIN    (BUILT_IN_VPRINTF
 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	(revision 117204)
+++ gcc/c-decl.c	(local)
@@ -4804,14 +4804,7 @@ grokdeclarator (const struct c_declarato
 	  }
 
 	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	(revision 117204)
+++ gcc/c-parser.c	(local)
@@ -7801,9 +7801,6 @@ c_parser_omp_threadprivate (c_parser *pa
   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	(revision 117213)
+++ gcc/cgraph.c	(local)
@@ -939,7 +939,7 @@ bool
 decide_is_variable_needed (struct cgraph_varpool_node *node, tree decl)
 {
   /* If the user told us it is used, then it must be so.  */
-  if (node->externally_visible)
+  if (node->externally_visible || node->force_output)
     return true;
   if (!flag_unit_at_a_time
       && lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
@@ -963,6 +963,17 @@ decide_is_variable_needed (struct cgraph
       && !DECL_EXTERNAL (decl))
     return true;
 
+  /* When emulating tls, we actually see references to the control
+     variable, rather than the user-level variable.  */
+  if (!targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      tree control = emutls_decl (decl);
+      if (decide_is_variable_needed (cgraph_varpool_node (control), control))
+	return true;
+    }
+
   /* When not reordering top level variables, we have to assume that
      we are going to keep everything.  */
   if (flag_unit_at_a_time && flag_toplevel_reorder)
--- gcc/cgraphunit.c	(revision 117204)
+++ gcc/cgraphunit.c	(local)
@@ -328,10 +328,7 @@ cgraph_varpool_remove_unreferenced_decls
       node->needed = 0;
 
       if (node->finalized
-	  && ((DECL_ASSEMBLER_NAME_SET_P (decl)
-	       && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
-	      || node->force_output
-	      || decide_is_variable_needed (node, decl)
+	  && (decide_is_variable_needed (node, decl)
 	      /* ??? Cgraph does not yet rule the world with an iron hand,
 		 and does not control the emission of debug information.
 		 After a variable has its DECL_RTL set, we must assume that
@@ -1709,6 +1706,7 @@ cgraph_build_static_cdtor (char which, t
     {
       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	(revision 117204)
+++ gcc/config/sparc/sol2.h	(local)
@@ -89,7 +89,7 @@ Boston, MA 02110-1301, USA.  */
     {								\
       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	(revision 117204)
+++ gcc/cp/decl.c	(local)
@@ -6321,14 +6321,7 @@ grokvardecl (tree type,
     }
 
   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))
     {
@@ -8456,15 +8449,7 @@ grokdeclarator (const cp_declarator *dec
 		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	(revision 117204)
+++ gcc/cp/parser.c	(local)
@@ -18921,9 +18921,6 @@ cp_parser_omp_threadprivate (cp_parser *
   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	(revision 117204)
+++ gcc/dwarf2out.c	(local)
@@ -9130,7 +9130,7 @@ loc_descriptor_from_tree_1 (tree loc, in
 	  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	(revision 117204)
+++ gcc/expr.c	(local)
@@ -6360,6 +6360,19 @@ highest_pow2_factor_for_target (tree tar
   return MAX (factor, target_align);
 }
 
+/* Return &VAR expression for emulated thread local VAR.  */
+
+static tree
+emutls_var_address (tree var)
+{
+  tree emuvar = emutls_decl (var);
+  tree fn = built_in_decls [BUILT_IN_EMUTLS_GET_ADDRESS];
+  tree arg = build_fold_addr_expr_with_type (emuvar, ptr_type_node);
+  tree arglist = build_tree_list (NULL_TREE, arg);
+  tree call = build_function_call_expr (fn, arglist);
+  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
+}
+
 /* Expands variable VAR.  */
 
 void
@@ -6488,6 +6501,18 @@ expand_expr_addr_expr_1 (tree exp, rtx t
       inner = TREE_OPERAND (exp, 0);
       break;
 
+    case VAR_DECL:
+      /* TLS emulation hook - replace __thread VAR's &VAR with
+	 __emutls_get_address (&_emutls.VAR).  */
+      if (! targetm.have_tls
+	  && TREE_CODE (exp) == VAR_DECL
+	  && DECL_THREAD_LOCAL_P (exp))
+	{
+	  exp = emutls_var_address (exp);
+	  return expand_expr (exp, target, tmode, modifier);
+	}
+      /* Fall through.  */
+
     default:
       /* If the object is a DECL, then expand it for its rtl.  Don't bypass
 	 expand_expr, as that can have various side effects; LABEL_DECLs for
@@ -6853,6 +6878,16 @@ expand_expr_real_1 (tree exp, rtx target
 	  && (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))
+	{
+	  exp = build_fold_indirect_ref (emutls_var_address (exp));
+	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
+	}
+
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
--- gcc/fortran/f95-lang.c	(revision 117204)
+++ gcc/fortran/f95-lang.c	(local)
@@ -1102,6 +1102,14 @@ gfc_init_builtin_functions (void)
 		      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	(revision 117204)
+++ gcc/fortran/openmp.c	(local)
@@ -465,12 +465,6 @@ gfc_match_omp_threadprivate (void)
   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	(revision 117204)
+++ gcc/fortran/trans-common.c	(local)
@@ -388,7 +388,7 @@ build_common_decl (gfc_common_head *com,
 
       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	(revision 117204)
+++ gcc/fortran/trans-decl.c	(local)
@@ -515,7 +515,7 @@ gfc_finish_var_decl (tree decl, gfc_symb
     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	(revision 117204)
+++ gcc/fortran/types.def	(local)
@@ -55,6 +55,7 @@ DEF_PRIMITIVE_TYPE (BT_BOOL, boolean_typ
 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_PTR, BT_
 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_3 (BT_FN_VOID_OMPFN_PT
 
 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	(revision 117204)
+++ gcc/libgcc-std.ver	(local)
@@ -273,4 +273,6 @@ GCC_4.2.0 {
   __floatuntixf
   __floatuntitf
   _Unwind_GetIPInfo
+  __emutls_get_address
+  __emutls_register_common
 }
--- gcc/output.h	(revision 117204)
+++ gcc/output.h	(local)
@@ -158,6 +158,9 @@ extern void merge_weak (tree, tree);
 /* 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	(revision 117204)
+++ gcc/toplev.c	(local)
@@ -1056,11 +1056,14 @@ compile_file (void)
   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-ssa-address.c	(revision 117204)
+++ gcc/tree-ssa-address.c	(local)
@@ -1,5 +1,5 @@
 /* Memory address lowering and addressing mode selection.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
    
 This file is part of GCC.
    
@@ -134,10 +134,15 @@ gen_addr_rtx (rtx symbol, rtx base, rtx 
       act_elem = symbol;
       if (offset)
 	{
-	  act_elem = gen_rtx_CONST (Pmode,
-				    gen_rtx_PLUS (Pmode, act_elem, offset));
+	  act_elem = gen_rtx_PLUS (Pmode, act_elem, offset);
+
 	  if (offset_p)
-	    *offset_p = &XEXP (XEXP (act_elem, 0), 1);
+	    *offset_p = &XEXP (act_elem, 1);
+
+	  if (GET_CODE (symbol) == SYMBOL_REF
+	      || GET_CODE (symbol) == LABEL_REF
+	      || GET_CODE (symbol) == CONST)
+	    act_elem = gen_rtx_CONST (Pmode, act_elem);
 	}
 
       if (*addr)
--- gcc/tree.h	(revision 117204)
+++ gcc/tree.h	(local)
@@ -4457,6 +4457,7 @@ extern void set_user_assembler_name (tre
 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	(revision 117204)
+++ gcc/varasm.c	(local)
@@ -53,6 +53,7 @@ Software Foundation, 51 Franklin Street,
 #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,236 @@ static GTY(()) int anchor_labelno;
 /* 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 an identifier for the struct __emutls_object, given an identifier
+   of the DECL_ASSEMBLY_NAME of the original object.  */
+
+static tree
+get_emutls_object_name (tree name)
+{
+  char *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));
+
+  return get_identifier (toname);
+}
+
+/* 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));
+  name = get_identifier (toname);
+
+  to = build_decl (VAR_DECL, name, TREE_TYPE (decl));
+  SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
+
+  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);
+      DECL_VISIBILITY (to) = DECL_VISIBILITY (decl);
+    }
+  else
+    TREE_STATIC (to) = 1;
+      
+  DECL_INITIAL (to) = DECL_INITIAL (decl);
+  DECL_INITIAL (decl) = NULL;
+
+  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;
+  struct tree_map *h, in;
+  void **loc;
+
+  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)
+    to = h->to;
+  else
+    {
+      to = build_decl (VAR_DECL, get_emutls_object_name (name),
+		       get_emutls_object_type ());
+
+      h = ggc_alloc (sizeof (struct tree_map));
+      h->hash = in.hash;
+      h->from = decl;
+      h->to = to;
+      *(struct tree_map **) loc = h;
+
+      DECL_ARTIFICIAL (to) = 1;
+      DECL_IGNORED_P (to) = 1;
+      TREE_READONLY (to) = 0;
+
+      SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
+      if (DECL_ONE_ONLY (decl))
+	make_decl_one_only (to);
+      DECL_CONTEXT (to) = DECL_CONTEXT (decl);
+    }
+
+  /* Note that these fields may need to be updated from time to time from
+     the original decl.  Consider:
+	extern __thread int i;
+	int foo() { return i; }
+	__thread int i = 1;
+     in which I goes from external to locally defined and initialized.  */
+
+  TREE_STATIC (to) = TREE_STATIC (decl);
+  TREE_USED (to) = TREE_USED (decl);
+  TREE_PUBLIC (to) = TREE_PUBLIC (decl);
+  DECL_EXTERNAL (to) = DECL_EXTERNAL (decl);
+  DECL_COMMON (to) = DECL_COMMON (decl);
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  DECL_VISIBILITY (to) = DECL_VISIBILITY (decl);
+
+  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 +1964,50 @@ assemble_variable (tree decl, int top_le
   rtx decl_rtl, symbol;
   section *sect;
 
+  if (! targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      tree to = emutls_decl (decl);
+
+      /* If this variable is defined locally, then we need to initialize the
+         control structure with size and alignment information.  We do this
+	 at the last moment because tentative definitions can take a locally
+	 defined but uninitialized variable and initialize it later, which
+	 would result in incorrect contents.  */
+      if (! DECL_EXTERNAL (to) && ! DECL_COMMON (to))
+	{
+	  VEC(constructor_elt,gc) *v = VEC_alloc (constructor_elt, gc, 4);
+	  constructor_elt *elt;
+	  tree type = TREE_TYPE (to);
+	  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);
+	}
+
+      decl = to;
+    }
+
   if (lang_hooks.decls.prepare_assemble_variable)
     lang_hooks.decls.prepare_assemble_variable (decl);
 
@@ -4856,6 +5131,14 @@ do_assemble_alias (tree decl, tree targe
     {
       ultimate_transparent_alias_target (&target);
 
+      if (!targetm.have_tls
+	  && TREE_CODE (decl) == VAR_DECL
+	  && DECL_THREAD_LOCAL_P (decl))
+	{
+	  decl = emutls_decl (decl);
+	  target = get_emutls_object_name (target);
+	}
+
       if (!TREE_SYMBOL_REFERENCED (target))
 	weakref_targets = tree_cons (decl, target, weakref_targets);
 
@@ -4873,6 +5156,14 @@ do_assemble_alias (tree decl, tree targe
       return;
     }
 
+  if (!targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      decl = emutls_decl (decl);
+      target = get_emutls_object_name (target);
+    }
+
 #ifdef ASM_OUTPUT_DEF
   /* Make name accessible from other files, if appropriate.  */
 
@@ -5755,7 +6046,8 @@ default_encode_section_info (tree decl, 
     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;
--- gcc/emutls.c	(revision 117204)
+++ gcc/emutls.c	(local)
@@ -0,0 +1,193 @@
+/* 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 "coretypes.h"
+#include "tm.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]