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


Having finally worked out the tcl bits, and a couple of non-regressions
that showed up during a merge, I've committed the following.


r~


        * 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 (decide_is_variable_needed): Look at force_output.
        Recurse for emulated tls.
        * cgraphunit.c (cgraph_varpool_remove_unreferenced_decls): Remove
        checks redundant with decide_is_variable_needed.
        (cgraph_build_static_cdtor): Do cgraph_varpool_assemble_pending_decls.
        * dwarf2out.c (loc_descriptor_from_tree_1): Don't do anything for
        emulated tls.
        * expr.c (emutls_var_address): New.
        (expand_expr_real_1): Expand emulated tls.
        (expand_expr_addr_expr_1): Likewise.
        * libgcc-std.ver: Add __emutls_get_address, __emutls_register_common.
        * output.h (emutls_finish): Declare.
        * toplev.c (compile_file): Call it.
        * tree-ssa-address.c (gen_addr_rtx): Check for const-ness of the
        address before wrapping in CONST.
        * varasm.c (emutls_htab, emutls_object_type): New.
        (EMUTLS_VAR_PREFIX, EMUTLS_TMPL_PREFIX): New.
        (get_emutls_object_name, get_emutls_object_type): New.
        (get_emutls_init_templ_addr, emutls_decl): New.
        (emutls_common_1, emutls_finish): New.
        (assemble_variable): When emulating tls, swap decls; generate
        constructor for the emutls objects.
        (do_assemble_alias): When emulating tls, swap decl and target name.
        (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.

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

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.

testsuite/
        * lib/target-supports.exp (check_effective_target_tls): Redefine
        to mean non-emulated tls.
        * gcc.dg/tls/alias-1.c: Remove tls requirement.
        * gcc.dg/tls/asm-1.c, gcc.dg/tls/debug-1.c, gcc.dg/tls/diag-1.c,
        gcc.dg/tls/diag-2.c, gcc.dg/tls/diag-3.c, gcc.dg/tls/diag-4.c,
        gcc.dg/tls/diag-5.c, gcc.dg/tls/init-1.c, gcc.dg/tls/nonpic-1.c,
        gcc.dg/tls/opt-10.c, gcc.dg/tls/opt-5.c, gcc.dg/tls/opt-6.c,
        gcc.dg/tls/opt-8.c, gcc.dg/tls/opt-9.c, gcc.dg/tls/pic-1.c,
        gcc.dg/tls/struct-1.c, gcc.dg/tls/trivial.c: Likewise.


--- gcc/Makefile.in	(revision 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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.
@@ -723,6 +723,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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ gcc/cp/parser.c	(local)
@@ -18964,9 +18964,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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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 117445)
+++ 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/testsuite/gcc.dg/tls/alias-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/alias-1.c	(local)
@@ -1,7 +1,6 @@
 /* { dg-do link } */
 /* { dg-require-alias "" } */
 /* { dg-require-visibility "" } */
-/* { dg-require-effective-target tls } */
 /* Test that encode_section_info handles the change from externally
    defined to locally defined (via hidden).   Extracted from glibc.  */
 
--- gcc/testsuite/gcc.dg/tls/asm-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/asm-1.c	(local)
@@ -1,5 +1,4 @@
 /* { dg-options "-Werror" } */
-/* { dg-require-effective-target tls } */
 __thread int i;
 
 int foo ()
--- gcc/testsuite/gcc.dg/tls/debug-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/debug-1.c	(local)
@@ -1,5 +1,4 @@
 /* { dg-do assemble } */
 /* { dg-options "-g" } */
-/* { dg-require-effective-target tls } */
 
 __thread int i;
--- gcc/testsuite/gcc.dg/tls/diag-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/diag-1.c	(local)
@@ -1,5 +1,4 @@
 /* Valid __thread specifiers.  */
-/* { dg-require-effective-target tls } */
 
 __thread int g1;
 extern __thread int g2;
--- gcc/testsuite/gcc.dg/tls/diag-2.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/diag-2.c	(local)
@@ -1,5 +1,4 @@
 /* Invalid __thread specifiers.  */
-/* { dg-require-effective-target tls } */
 
 __thread extern int g1;		/* { dg-error "'__thread' before 'extern'" } */
 __thread static int g2;		/* { dg-error "'__thread' before 'static'" } */
--- gcc/testsuite/gcc.dg/tls/diag-3.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/diag-3.c	(local)
@@ -1,5 +1,4 @@
 /* Report invalid extern and __thread combinations.  */
-/* { dg-require-effective-target tls } */
 
 extern int j;		/* { dg-error "previous declaration" } */
 __thread int j;		/* { dg-error "follows non-thread-local" } */
--- gcc/testsuite/gcc.dg/tls/diag-4.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/diag-4.c	(local)
@@ -1,6 +1,5 @@
 /* Invalid __thread specifiers.  As diag-4.c but some cases in
    different orders.  */
-/* { dg-require-effective-target tls } */
 
 __thread typedef int g4;	/* { dg-error "'__thread' used with 'typedef'" } */
 
--- gcc/testsuite/gcc.dg/tls/diag-5.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/diag-5.c	(local)
@@ -1,4 +1,3 @@
 /* __thread specifiers on empty declarations.  */
-/* { dg-require-effective-target tls } */
 
 __thread struct foo; /* { dg-warning "warning: useless '__thread' in empty declaration" } */
--- gcc/testsuite/gcc.dg/tls/init-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/init-1.c	(local)
@@ -1,5 +1,4 @@
 /* Invalid initializations.  */
-/* { dg-require-effective-target tls } */
 
 extern __thread int i;
 int *p = &i;	/* { dg-error "initializer element is not constant" } */
--- gcc/testsuite/gcc.dg/tls/nonpic-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/nonpic-1.c	(local)
@@ -1,6 +1,5 @@
 /* { dg-do compile } */
 /* { dg-options "-O2 -ftls-model=initial-exec" } */
-/* { dg-require-effective-target tls } */
 
 extern __thread long e1;
 extern __thread int e2;
--- gcc/testsuite/gcc.dg/tls/opt-10.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/opt-10.c	(local)
@@ -1,6 +1,5 @@
 /* { dg-do compile } */
 /* { dg-options "-O3 -fpic" } */
-/* { dg-require-effective-target tls } */
 
 /* The web pass was creating unrecognisable pic_load_dot_plus_four insns
    on ARM.  */
--- gcc/testsuite/gcc.dg/tls/opt-5.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/opt-5.c	(local)
@@ -1,6 +1,5 @@
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
-/* { dg-require-effective-target tls } */
 /* Sched1 moved {load_tp} pattern between strlen call and the copy
    of the hard return value to its pseudo.  This resulted in a
    reload abort, since the hard register was not spillable.  */
--- gcc/testsuite/gcc.dg/tls/opt-6.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/opt-6.c	(local)
@@ -1,6 +1,5 @@
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
-/* { dg-require-effective-target tls } */
 
 extern void abort (void);
 extern void exit (int);
--- gcc/testsuite/gcc.dg/tls/opt-8.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/opt-8.c	(local)
@@ -1,7 +1,6 @@
 /* PR 18910 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
-/* { dg-require-effective-target tls } */
 
 static __thread void *foo [2];
 void
--- gcc/testsuite/gcc.dg/tls/opt-9.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/opt-9.c	(local)
@@ -1,7 +1,6 @@
 /* PR 21412 */
 /* { dg-do compile */
 /* { dg-options "-O2 -fPIC" } */
-/* { dg-require-effective-target tls } */
 
 struct S { int x[10]; };
 extern __thread struct S s;
--- gcc/testsuite/gcc.dg/tls/pic-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/pic-1.c	(local)
@@ -1,6 +1,5 @@
 /* { dg-do compile } */
 /* { dg-options "-O2 -fpic -ftls-model=global-dynamic" } */
-/* { dg-require-effective-target tls } */
 
 extern __thread long e1;
 extern __thread int e2;
--- gcc/testsuite/gcc.dg/tls/struct-1.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/struct-1.c	(local)
@@ -2,7 +2,6 @@
    to allow addends for @dtpoff relocs or not.  */
 /* { dg-do compile } */
 /* { dg-options "-O2 -fpic" } */
-/* { dg-require-effective-target tls } */
 
 struct S {
   int s0, s1, s2, s3;
--- gcc/testsuite/gcc.dg/tls/trivial.c	(revision 117445)
+++ gcc/testsuite/gcc.dg/tls/trivial.c	(local)
@@ -1,3 +1 @@
-/* { dg-require-effective-target tls } */
-
 __thread int i;
--- gcc/testsuite/lib/target-supports.exp	(revision 117445)
+++ gcc/testsuite/lib/target-supports.exp	(local)
@@ -384,7 +384,7 @@ proc check_effective_target_pcc_bitfield
     }]
 }
 
-# Return 1 if thread local storage (TLS) is supported, 0 otherwise.
+# Return 1 if *native* thread local storage (TLS) is supported, 0 otherwise.
 #
 # This won't change for different subtargets so cache the result.
 
@@ -406,11 +406,19 @@ proc check_effective_target_tls {} {
 	close $f
 
 	# Test for thread-local data supported by the platform.
-	set comp_output \
-	    [${tool}_target_compile $src $asm assembly ""]
+	set comp_output [${tool}_target_compile $src $asm assembly ""]
 	file delete $src
 	if { [string match "*not supported*" $comp_output] } {
 	    set et_tls_saved 0
+	} else {
+	    set fd [open $asm r]
+	    set text [read $fd]
+	    close $fd
+	    if { [string match "*emutls*" $text]} {
+		set et_tls_saved 0
+	    } else {
+		set et_tls_saved 1
+	    }
 	}
 	remove-build-file $asm
     }
--- gcc/toplev.c	(revision 117445)
+++ 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 117445)
+++ 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 117445)
+++ gcc/tree.h	(local)
@@ -4460,6 +4460,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 117445)
+++ 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 117445)
+++ 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]