public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [Patch, updated] Make emulated TLS lto-friendly.
@ 2010-07-03 14:11 IainS
  2010-07-07 23:22 ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: IainS @ 2010-07-03 14:11 UTC (permalink / raw)
  To: GCC Patches; +Cc: Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 3419 bytes --]

This is an update of a patch originally posted and discussed in this  
thread:
http://gcc.gnu.org/ml/gcc-patches/2010-06/threads.html#00995

I guess it needs a global approver - so cc-ing Diego who, IIRC,  
approved the previous EMUTLS changes.

changes from the previous thread.
(a) emutls.c is no longer modified in any way.
(b) I have taken on board the comments about not rearranging code.
(c) the cases where we should not have emutls variables are now asserts.

tested on {i686,powerpc*}-apple-darwin{8,9},  x86_64-apple-darwin10 -  
EMUTLS target without aliases.
    {i686,x86_64}unknown-linux-gnu - native TLS targets (i.e. for a  
null response)
   cris-elf(sim)  - EMUTLS target with aliases
  + armel-linux-eabi(compile-only), mipsabi64-elf(sim), s390x(sompile  
only).

The test-suite additions are motivated by:
(a) the fact that initialization of the emults vars was a weakness  
w.r.t. optimization,
(b) to include the test that is used as the configury check for TLS  
compliance.


OK for trunk now? (and eventually 4.5)?

Iain

gcc/Changelog (email addresses omitted)
	
	Iain Sandoe
	Jan Hubika

	PR target/44132
	* expr.c (emutls_var_address): Remove.
	(expand_expr_addr_expr_1): Remove TLS emulation hook.
	(expand_expr_real_1): Ditto.

	* gimplify.c (emutls_var_address): Add proc.
	(gimplify_decl_expr): expand TLS vars.
	(gimplify_var_or_parm_decl): Ditto.
	(omp_notice_variable): Recognize TLS_MODEL_EMULATED.

	* passes.c (rest_of_decl_compilation): Substitute TLS control vars  
for the master.

	* varasm.c (get_emutls_init_templ_addr): Do not reset the caller's  
DECL_INITIAL.
	(emutls_decl): copy DECL_PRESERVE_P instead of setting it, copy
	TREE_ADDRESSABLE.  Create the init template as soon as we see a valid  
initializer.
	 Mark the original var with error_mark_node so that re- 
initializations will be diagnosed
	properly.  Mark the original var as not to be output.
	(emutls_common_1): Do not rely on DECL_COMMON as a decision on  
whether a constructor
	is needed, it is also required for lcomm.   Ensure that the var is  
laid out before the constructor
	is built.
	(emutls_finalize_control_var): Assert on incorrect type, copy USED.
	(asm_output_bss): Assert on DECL_THREAD_LOCAL_P for EMUTLS targets.
	(asm_output_aligned_bss): Likewise.
	(assemble_variable): Remove TLS var. substitution.  Back up  
TREE_ASM_WRITTEN so that
	assemble_variable() has no side-effects for unwritten vars.  Assert  
on DECL_THREAD_LOCAL_P
	for EMUTLS targets.
	(do_assemble_alias): Assert on DECL_THREAD_LOCAL_P for EMUTLS targets.
	(var_decl_for_asm): New.
	(finish_aliases_1): Walk alias pairs substituting tls controls for  
originals.

	* passes.c (rest_of_decl_compilation): Substitute TLS control vars  
for the master.

	* varpool.c (varpool_mark_needed_node): Do not handle TLS  
substitution here.
	(decide_is_variable_needed): Or here.
	(varpool_finalize_decl): Handle TLS substitution.
	Remove early calls to varpool_assemble_pending_decls().
	Check enqueuing of vars after all tests for need are complete.
	(varpool_analyze_pending_decls): Do not record references if the  
initializer is error_mark.

testsuite:
	
	PR target/44132
	* gcc.dg/tls/thr-init-1.c: New.
	* gcc.dg/tls/thr-init-2.c: New.
	* gcc.dg/torture/tls New.
	* gcc.dg/torture/tls/tls-test.c: New.
	* gcc.dg/torture/tls/thr-init-1.c: New.
	* gcc.dg/torture/tls/tls.exp: New.
	* gcc.dg/torture/tls/thr-init-2.c: New.


[-- Attachment #2: 161773-emutls-lto.txt --]
[-- Type: text/plain, Size: 21739 bytes --]

Index: gcc/expr.c
===================================================================
--- gcc/expr.c	(revision 161773)
+++ gcc/expr.c	(working copy)
@@ -6862,21 +6862,7 @@ highest_pow2_factor_for_target (const_tree target,
 
   return MAX (factor, talign);
 }
-\f
-/* 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 (UNKNOWN_LOCATION, fn, arglist);
-  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
-}
-\f
-
 /* Subroutine of expand_expr.  Expand the two operands of a binary
    expression EXP0 and EXP1 placing the results in OP0 and OP1.
    The value may be stored in TARGET if TARGET is nonzero.  The
@@ -6980,17 +6966,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enu
       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
@@ -8428,16 +8403,6 @@ expand_expr_real_1 (tree exp, rtx target, enum mac
 	  && (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_loc (loc, emutls_var_address (exp));
-	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
-	}
-
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
Index: gcc/gimplify.c
===================================================================
--- gcc/gimplify.c	(revision 161773)
+++ gcc/gimplify.c	(working copy)
@@ -1341,7 +1341,19 @@ gimplify_vla_decl (tree decl, gimple_seq *seq_p)
   gimplify_ctxp->save_stack = true;
 }
 
+/* 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 (UNKNOWN_LOCATION, fn, arglist);
+  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
+}
+
 /* Gimplifies a DECL_EXPR node *STMT_P by making any necessary allocation
    and initialization explicit.  */
 
@@ -1356,6 +1368,18 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_
   if (TREE_TYPE (decl) == error_mark_node)
     return GS_ERROR;
 
+  /* TLS emulation hook - replace __thread VAR's &VAR with
+     __emutls_get_address (&_emutls.VAR). We then ignore the original
+     var.  */
+  if (! targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      stmt = build_fold_indirect_ref (emutls_var_address (decl));
+      gimplify_and_add (stmt, seq_p);
+      return GS_ALL_DONE;
+    }
+
   if ((TREE_CODE (decl) == TYPE_DECL
        || TREE_CODE (decl) == VAR_DECL)
       && !TYPE_SIZES_GIMPLIFIED (TREE_TYPE (decl)))
@@ -1875,6 +1899,17 @@ gimplify_var_or_parm_decl (tree *expr_p)
       return GS_ERROR;
     }
 
+  /* TLS emulation hook - replace __thread VAR's &VAR with
+     __emutls_get_address (&_emutls.VAR).  */
+  if (! targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      gcc_assert (!DECL_HAS_VALUE_EXPR_P (decl)) ;
+      *expr_p = build_fold_indirect_ref (emutls_var_address (decl));
+      return GS_OK ;
+    }
+
   /* When within an OpenMP context, notice uses of variables.  */
   if (gimplify_omp_ctxp && omp_notice_variable (gimplify_omp_ctxp, decl, true))
     return GS_ALL_DONE;
@@ -5555,14 +5590,15 @@ omp_notice_variable (struct gimplify_omp_ctx *ctx,
   /* Threadprivate variables are predetermined.  */
   if (is_global_var (decl))
     {
-      if (DECL_THREAD_LOCAL_P (decl))
+      if ((DECL_TLS_MODEL (decl) >= TLS_MODEL_EMULATED))
 	return omp_notice_threadprivate_variable (ctx, decl, NULL_TREE);
 
       if (DECL_HAS_VALUE_EXPR_P (decl))
 	{
 	  tree value = get_base_address (DECL_VALUE_EXPR (decl));
 
-	  if (value && DECL_P (value) && DECL_THREAD_LOCAL_P (value))
+	  if (value && DECL_P (value) && 
+	      (DECL_TLS_MODEL (value) >= TLS_MODEL_EMULATED))
 	    return omp_notice_threadprivate_variable (ctx, decl, value);
 	}
     }
Index: gcc/varasm.c
===================================================================
--- gcc/varasm.c	(revision 161773)
+++ gcc/varasm.c	(working copy)
@@ -319,9 +319,11 @@ get_emutls_init_templ_addr (tree decl)
   TREE_USED (to) = TREE_USED (decl);
   TREE_READONLY (to) = 1;
   DECL_IGNORED_P (to) = 1;
+
+  DECL_PRESERVE_P (to) = 1;
+  
   DECL_CONTEXT (to) = DECL_CONTEXT (decl);
   DECL_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
-  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
 
   DECL_WEAK (to) = DECL_WEAK (decl);
   if (DECL_ONE_ONLY (decl))
@@ -336,7 +338,6 @@ get_emutls_init_templ_addr (tree decl)
 
   DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
   DECL_INITIAL (to) = DECL_INITIAL (decl);
-  DECL_INITIAL (decl) = NULL;
 
   varpool_finalize_decl (to);
   return build_fold_addr_expr (to);
@@ -361,6 +362,8 @@ emutls_decl (tree decl)
   if (! emutls_htab)
     emutls_htab = htab_create_ggc (512, tree_map_hash, tree_map_eq, 0);
 
+  gcc_assert (DECL_NAME (decl)) ;
+  
   name = DECL_ASSEMBLER_NAME (decl);
 
   /* Note that we use the hash of the decl's name, rather than a hash
@@ -387,8 +390,7 @@ emutls_decl (tree decl)
       DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
       DECL_ARTIFICIAL (to) = 1;
       DECL_IGNORED_P (to) = 1;
-      /* FIXME: work around PR44132.  */
-      DECL_PRESERVE_P (to) = 1;
+
       TREE_READONLY (to) = 0;
       SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
       if (DECL_ONE_ONLY (decl))
@@ -412,18 +414,42 @@ emutls_decl (tree decl)
   TREE_STATIC (to) = TREE_STATIC (decl);
   TREE_USED (to) = TREE_USED (decl);
   TREE_PUBLIC (to) = TREE_PUBLIC (decl);
+  TREE_ADDRESSABLE (to) = TREE_ADDRESSABLE (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);
   DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
   
   /* Fortran might pass this to us.  */
   DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
+  
+  /* As soon as we see an initializer (and providing one is not already
+     present) we can setup the init. template.  */
+  if (!DECL_INITIAL (to) && 
+      DECL_INITIAL (decl) && (DECL_INITIAL (decl) != error_mark_node)) 
+    if (! DECL_EXTERNAL (to) && ! DECL_COMMON (to))
+      {
+	DECL_INITIAL (to) = targetm.emutls.var_init
+		(to, decl, get_emutls_init_templ_addr (decl));
 
+	/* Make sure the template is marked as needed early enough.
+	   Without this, if the variable is placed in a
+	   section-anchored block, the template will only be marked
+	   when it's too late.*/
+	record_references_in_initializer (to, false);
+	/* We leave this marked, so that any attempt at duplicate
+	   or re-initialization will give the appropriate error.  */
+	DECL_INITIAL (decl) = error_mark_node ;
+      }
+  /* Say we are not interested in emitting this Var.  */
+  TREE_ASM_WRITTEN (decl) = 1;
   return to;
 }
 
+/* Add static constructors for emutls vars, where required.  */
+
 static int
 emutls_common_1 (void **loc, void *xstmts)
 {
@@ -431,17 +457,21 @@ emutls_common_1 (void **loc, void *xstmts)
   tree args, x, *pstmts = (tree *) xstmts;
   tree word_type_node;
 
-  if (! DECL_COMMON (h->base.from)
-      || (DECL_INITIAL (h->base.from)
-	  && DECL_INITIAL (h->base.from) != error_mark_node))
+  /* ??? It is not enough to check DECL_COMMON because variables might be 
+     allocated in other uninitialized sections.  However, this might still
+     not be an adequate test.  */
+  if (DECL_INITIAL (h->to))
     return 1;
+  
+  /* Refuse to build a zero-sized item, first make sure there's
+     a valid layout.  */
+  if (DECL_SIZE (h->base.from) == 0)
+    layout_decl (h->base.from, 0);
+	
+  gcc_assert (DECL_SIZE (h->base.from) != 0);
 
   word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
 
-  /* The idea was to call get_emutls_init_templ_addr here, but if we
-     do this and there is an initializer, -fanchor_section loses,
-     because it would be too late to ensure the template is
-     output.  */
   x = null_pointer_node;
   args = tree_cons (NULL, x, NULL);
   x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
@@ -468,6 +498,9 @@ emutls_finalize_control_var (void **loc,
   if (h != NULL) 
     {
       struct varpool_node *node = varpool_node (h->to);
+      gcc_assert (! DECL_THREAD_LOCAL_P (h->to));
+      if (TREE_USED (h->base.from)) 
+	TREE_USED (h->to) = 1;
       /* Because varpool_finalize_decl () has side-effects,
          only apply to un-finalized vars.  */
       if (node && !node->finalized) 
@@ -791,6 +824,10 @@ asm_output_bss (FILE *file, tree decl ATTRIBUTE_UN
   gcc_assert (strcmp (XSTR (XEXP (DECL_RTL (decl), 0), 0), name) == 0);
   targetm.asm_out.globalize_decl_name (file, decl);
   switch_to_section (bss_section);
+  /* We don't emit the userland vars for emulated TLS, just the control.  */
+  gcc_assert (targetm.have_tls  
+	      || (TREE_CODE (decl) != VAR_DECL)
+	      || !DECL_THREAD_LOCAL_P (decl));
 #ifdef ASM_DECLARE_OBJECT_NAME
   last_assemble_variable_decl = decl;
   ASM_DECLARE_OBJECT_NAME (file, name, decl);
@@ -817,6 +854,10 @@ asm_output_aligned_bss (FILE *file, tree decl ATTR
 {
   switch_to_section (bss_section);
   ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
+  /* We don't emit the userland vars for emulated TLS, just the control.  */
+  gcc_assert (targetm.have_tls  
+	      || (TREE_CODE (decl) != VAR_DECL)
+	      || !DECL_THREAD_LOCAL_P (decl));
 #ifdef ASM_DECLARE_OBJECT_NAME
   last_assemble_variable_decl = decl;
   ASM_DECLARE_OBJECT_NAME (file, name, decl);
@@ -1232,7 +1273,12 @@ get_variable_section (tree decl, bool prefer_noswi
   if (IN_NAMED_SECTION (decl))
     return get_named_section (decl, NULL, reloc);
 
-  if (ADDR_SPACE_GENERIC_P (as)
+  /* This cannot be lcomm for an emulated TLS object, without
+     a register_common hook.  */
+  if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
+      && !targetm.emutls.register_common)
+    ;
+  else if (ADDR_SPACE_GENERIC_P (as)
       && !DECL_THREAD_LOCAL_P (decl)
       && !(prefer_noswitch_p && targetm.have_switchable_bss_sections)
       && bss_initializer_p (decl))
@@ -2152,35 +2198,6 @@ assemble_variable (tree decl, int top_level ATTRIB
   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)
-	      || (DECL_INITIAL (decl)
-		  && DECL_INITIAL (decl) != error_mark_node)))
-	{
-	  DECL_INITIAL (to) = targetm.emutls.var_init
-	    (to, decl, get_emutls_init_templ_addr (decl));
-
-	  /* Make sure the template is marked as needed early enough.
-	     Without this, if the variable is placed in a
-	     section-anchored block, the template will only be marked
-	     when it's too late.  */
-	  record_references_in_initializer (to, false);
-	}
-
-      decl = to;
-    }
-
   last_assemble_variable_decl = 0;
 
   /* Normally no need to say anything here for external references,
@@ -2196,6 +2213,14 @@ assemble_variable (tree decl, int top_level ATTRIB
   if (TREE_CODE (decl) == FUNCTION_DECL)
     return;
 
+  /* The first declaration of a variable that comes through this function
+     decides whether it is global (in C, has external linkage)
+     or local (in C, has internal linkage).  So do nothing more
+     if this function has already run.  */
+
+  if (TREE_ASM_WRITTEN (decl))
+    return;
+
   /* Do nothing for global register variables.  */
   if (DECL_RTL_SET_P (decl) && REG_P (DECL_RTL (decl)))
     {
@@ -2219,14 +2244,6 @@ assemble_variable (tree decl, int top_level ATTRIB
       return;
     }
 
-  /* The first declaration of a variable that comes through this function
-     decides whether it is global (in C, has external linkage)
-     or local (in C, has internal linkage).  So do nothing more
-     if this function has already run.  */
-
-  if (TREE_ASM_WRITTEN (decl))
-    return;
-
   /* Make sure targetm.encode_section_info is invoked before we set
      ASM_WRITTEN.  */
   decl_rtl = DECL_RTL (decl);
@@ -2237,6 +2254,12 @@ assemble_variable (tree decl, int top_level ATTRIB
   if (flag_syntax_only)
     return;
 
+  /* We don't emit the userland vars for emulated TLS - they should never
+     get to here, only the control vars should be emitted.  */
+  gcc_assert (targetm.have_tls  
+	      || (TREE_CODE (decl) != VAR_DECL)
+	      || !DECL_THREAD_LOCAL_P (decl));
+
   if (! dont_output_data
       && ! host_integerp (DECL_SIZE_UNIT (decl), 1))
     {
@@ -5700,18 +5723,14 @@ do_assemble_alias (tree decl, tree target)
   TREE_ASM_WRITTEN (decl) = 1;
   TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1;
 
+  gcc_assert (targetm.have_tls  
+	      || (TREE_CODE (decl) != VAR_DECL)
+	      || !DECL_THREAD_LOCAL_P (decl));
+
   if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
     {
       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);
 
@@ -5730,14 +5749,6 @@ do_assemble_alias (tree decl, tree target)
       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.  */
 
@@ -5819,6 +5830,15 @@ remove_unreachable_alias_pairs (void)
     }
 }
 
+/* Lookup the decl for a symbol in the varpool.  */
+static tree
+var_decl_for_asm (tree symbol)
+{
+  struct varpool_node *vnode = varpool_node_for_asm  (symbol);
+  if (vnode) 
+    return vnode->decl;
+  return NULL;
+}
 
 /* First pass of completing pending aliases.  Make sure that cgraph knows
    which symbols will be required.  */
@@ -5831,8 +5851,55 @@ finish_aliases_1 (void)
 
   for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++)
     {
-      tree target_decl;
+      tree target_decl=NULL;
 
+      /* When emulated TLS is in effect, redirect aliases so that they 
+         are registered between the control vars.  */
+      if (!targetm.have_tls 
+          && TREE_CODE (p->decl) == VAR_DECL
+          && (DECL_TLS_MODEL (p->decl) >= TLS_MODEL_EMULATED))
+	{
+	  tree tsym = p->target ;
+	  target_decl = var_decl_for_asm (tsym) ;
+	  if (!target_decl) 
+	    {
+	      /* If we didn't find the user's symbol, it could
+	         be because the alias really refers to a control 
+	         var.  */
+	      tsym = get_emutls_object_name (p->target);
+	      target_decl = var_decl_for_asm (tsym);
+	    }
+	  if (target_decl) 
+	    {
+	      struct varpool_node *vnode;
+	      /* If it hasn't been done already, substitute the control
+	         var for the original.  */
+	      if (DECL_THREAD_LOCAL_P (p->decl))
+		p->decl = emutls_decl (p->decl);
+	      /* If not TLS target, we've made a mistake.  */
+	      if (DECL_TLS_MODEL (target_decl) < TLS_MODEL_EMULATED)
+		error ("TLS symbol %q+D aliased to non-TLS symbol %qE",
+			p->decl, p->target);
+	      /* If it's the original we need to substitute the contol.  */
+	      else if (DECL_THREAD_LOCAL_P (target_decl))
+		{
+		  target_decl = emutls_decl (target_decl);
+		  tsym = get_emutls_object_name (p->target);
+		}
+	      /* else it's already the emulation control.  */
+	      /* Mark the var needed.  */
+	      vnode = varpool_node (target_decl);
+	      if (vnode) 
+	        {
+		  varpool_mark_needed_node (vnode);
+		  vnode->force_output = 1;
+	        }
+	      p->target = tsym;
+	    }
+	  /* Else we didn't find a decl for the symbol, which is an error
+	     unless there's a weak ref.  */
+	} 
+      else
       target_decl = find_decl_and_mark_needed (p->decl, p->target);
       if (target_decl == NULL)
 	{
Index: gcc/passes.c
===================================================================
--- gcc/passes.c	(revision 161773)
+++ gcc/passes.c	(working copy)
@@ -152,6 +152,16 @@ rest_of_decl_compilation (tree decl,
 			  int top_level,
 			  int at_end)
 {
+  if (! targetm.have_tls 
+	&& !in_lto_p 
+	&& TREE_CODE (decl) == VAR_DECL
+	&& DECL_THREAD_LOCAL_P (decl))
+    {
+      /* Substitute the control var. for the user one.  */
+      rest_of_decl_compilation (emutls_decl (decl), top_level, at_end);
+      return;
+    }
+
   /* We deferred calling assemble_alias so that we could collect
      other attributes such as visibility.  Emit the alias now.  */
   {
Index: gcc/varpool.c
===================================================================
--- gcc/varpool.c	(revision 161773)
+++ gcc/varpool.c	(working copy)
@@ -312,6 +312,14 @@ varpool_mark_needed_node (struct varpool_node *nod
       && !TREE_ASM_WRITTEN (node->decl))
     varpool_enqueue_needed_node (node);
   node->needed = 1;
+  /* If we need the var, and it's an emulated TLS entity, that
+     means we need the control var.  */
+  if (!targetm.have_tls && DECL_THREAD_LOCAL_P (node->decl))
+    {
+      struct varpool_node *cv_node;
+      cv_node = varpool_node (emutls_decl (node->decl)) ;
+      varpool_mark_needed_node (cv_node);
+    }
 }
 
 /* Reset the queue of needed nodes.  */
@@ -346,17 +354,6 @@ decide_is_variable_needed (struct varpool_node *no
       && !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 (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_toplevel_reorder)
@@ -381,14 +378,19 @@ varpool_finalize_decl (tree decl)
      or local (in C, has internal linkage).  So do nothing more
      if this function has already run.  */
   if (node->finalized)
+      return;
+
+  /* For emulated TLS vars, if we are in a position to finalize the userland
+     var, then we should be able to finalize the control var too.  */
+  if (!targetm.have_tls && decl 
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
     {
-      if (cgraph_global_info_ready)
-	varpool_assemble_pending_decls ();
+      varpool_finalize_decl (emutls_decl (decl)) ;
+      node->finalized = true;    
       return;
     }
-  if (node->needed)
-    varpool_enqueue_needed_node (node);
-  node->finalized = true;
+
   if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl))
     node->force_output = true;
 
@@ -399,8 +401,11 @@ varpool_finalize_decl (tree decl)
      there.  */
   else if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
     varpool_mark_needed_node (node);
-  if (cgraph_global_info_ready)
-    varpool_assemble_pending_decls ();
+
+  if (node->needed)
+    varpool_enqueue_needed_node (node);
+
+  node->finalized = true;
 }
 
 /* Return variable availability.  See cgraph.h for description of individual
@@ -449,7 +454,7 @@ varpool_analyze_pending_decls (void)
 	     already informed about increased alignment.  */
           align_variable (decl, 0);
 	}
-      if (DECL_INITIAL (decl))
+      if (DECL_INITIAL (decl) && (DECL_INITIAL (decl) != error_mark_node))
 	record_references_in_initializer (decl, analyzed);
       if (node->same_comdat_group)
 	{

[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




[-- Attachment #4: 161773-tls-testsuite-additions.txt --]
[-- Type: text/plain, Size: 5294 bytes --]

Index: gcc/testsuite/gcc.dg/tls/thr-init-1.c
===================================================================
--- gcc/testsuite/gcc.dg/tls/thr-init-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/tls/thr-init-1.c	(revision 0)
@@ -0,0 +1,8 @@
+/* { dg-require-effective-target tls } */
+/* { dg-do compile } */
+
+static __thread int fstat ;
+static __thread int fstat = 1 ;
+static __thread int fstat ;
+static __thread int fstat = 2; /* { dg-error "redefinition of 'fstat'" } */
+				/* { dg-message "note: previous definition of 'fstat' was here" "" { target *-*-* } 5 } */
Index: gcc/testsuite/gcc.dg/tls/thr-init-2.c
===================================================================
--- gcc/testsuite/gcc.dg/tls/thr-init-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/tls/thr-init-2.c	(revision 0)
@@ -0,0 +1,23 @@
+/* { dg-require-effective-target tls } */
+/* { dg-do run } */
+
+extern void abort() ;
+
+static __thread int fstat ;
+static __thread int fstat = 1;
+
+int test_code(int b)
+{
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ((a != 2) || (fstat != 2))
+    abort () ;
+  
+  return 0;
+}
Index: gcc/testsuite/gcc.dg/torture/tls/tls-test.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/tls-test.c	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/tls-test.c	(revision 0)
@@ -0,0 +1,51 @@
+/* { dg-do run }  */
+/* { dg-require-effective-target tls  }  */
+/* { dg-require-effective-target pthread } */
+
+#include <pthread.h>
+extern int printf (char *,...);
+__thread int a = 5; 
+int *volatile a_in_other_thread = (int *) 12345;
+
+static void *
+thread_func (void *arg)
+{
+  a_in_other_thread = &a;
+  a+=5;
+  *((int *) arg) = a;
+  return (void *)0;
+}
+
+int
+main ()
+{
+  pthread_t thread;
+  void *thread_retval;
+  int *volatile a_in_main_thread;
+  int *volatile again ;
+  int thr_a;
+
+  a_in_main_thread = &a;
+
+  if (pthread_create (&thread, (pthread_attr_t *)0, thread_func, &thr_a))
+    return 0;
+
+  if (pthread_join (thread, &thread_retval))
+    return 0;
+
+  again = &a;
+  if (again != a_in_main_thread)
+    {
+      printf ("FAIL: main thread addy changed from 0x%0x to 0x%0x\n", 
+		a_in_other_thread, again);
+      return 1;
+    }
+
+  if (a != 5 || thr_a != 10 || (a_in_other_thread == a_in_main_thread))
+    {
+      printf ("FAIL: a= %d, thr_a = %d Addr = 0x%0x\n", 
+		a, thr_a, a_in_other_thread);
+      return 1;
+    }
+  return 0;
+}
Index: gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c	(revision 0)
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-require-effective-target tls } */
+
+extern int printf (char *,...);
+extern void abort() ;
+
+int test_code(int b)
+{
+static __thread int fstat = 1;
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ( a != 2 )
+    {
+      printf ("a=%d\n", a) ;
+      abort ();
+    }
+  
+  return 0;
+}
Index: gcc/testsuite/gcc.dg/torture/tls/tls.exp
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/tls.exp	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/tls.exp	(revision 0)
@@ -0,0 +1,36 @@
+#   Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+        $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
Index: gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c	(revision 0)
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-effective-target tls } */
+
+extern int printf (char *,...);
+extern void abort() ;
+
+static __thread int fstat ;
+static __thread int fstat = 1;
+static __thread int fstat ;
+
+int test_code(int b)
+{
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ( a != 2 || fstat != 2 )
+    {
+    printf ("a=%d fstat=%d\n", a, fstat) ;
+    abort ();
+    }
+  
+  return 0;
+}

[-- Attachment #5: Type: text/plain, Size: 1 bytes --]



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-03 14:11 [Patch, updated] Make emulated TLS lto-friendly IainS
@ 2010-07-07 23:22 ` Richard Henderson
  2010-07-08  8:42   ` IainS
                     ` (2 more replies)
  0 siblings, 3 replies; 35+ messages in thread
From: Richard Henderson @ 2010-07-07 23:22 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On 07/03/2010 07:10 AM, IainS wrote:
> +      return GS_OK ;

s/ ;/;/g

There are at least 2 occurrences.

> +      if ((DECL_TLS_MODEL (decl) >= TLS_MODEL_EMULATED))

Extra ().

Possibly better written as != TLS_MODEL_NONE as well.

> +	  if (value && DECL_P (value) && 
> +	      (DECL_TLS_MODEL (value) >= TLS_MODEL_EMULATED))

> +  if (!DECL_INITIAL (to) && 
> +      DECL_INITIAL (decl) && (DECL_INITIAL (decl) != error_mark_node)) 
> +    if (! DECL_EXTERNAL (to) && ! DECL_COMMON (to))

Coding style -- && should be on the new line.
Extra ().
Merge the unnecessarily nested ifs.

> +  /* ??? It is not enough to check DECL_COMMON because variables might be 
> +     allocated in other uninitialized sections.  However, this might still
> +     not be an adequate test.  */
> +  if (DECL_INITIAL (h->to))

Huh?  Why is DECL_COMMON not sufficient?

> +  /* Refuse to build a zero-sized item, first make sure there's
> +     a valid layout.  */
> +  if (DECL_SIZE (h->base.from) == 0)
> +    layout_decl (h->base.from, 0);

Really?  What is a testcase for which the decl is not layed out?
That really sounds like a bug elsewhere.

> +  gcc_assert (DECL_SIZE (h->base.from) != 0);

I'm pretty sure you can declare zero sized objects as a gcc extension.

$ cat z.c
__thread int x[0] __attribute__((common));
$ cat z.s
	.file	"z.c"
	.tls_common	x,0,4
	.ident	"GCC: (GNU) 4.4.4 20100630 (Red Hat 4.4.4-10)"
	.section	.note.GNU-stack,"",@progbits

So here's a test case that will trigger that abort.

> +  /* We don't emit the userland vars for emulated TLS - they should never
> +     get to here, only the control vars should be emitted.  */
> +  gcc_assert (targetm.have_tls  
> +	      || (TREE_CODE (decl) != VAR_DECL)
> +	      || !DECL_THREAD_LOCAL_P (decl));

Extra ().  I'm sure there are more, but I won't comment again.
This pattern occurs many places.  Pull this out to a function.

> +  /* If we need the var, and it's an emulated TLS entity, that
> +     means we need the control var.  */
> +  if (!targetm.have_tls && DECL_THREAD_LOCAL_P (node->decl))
> +    {
> +      struct varpool_node *cv_node;
> +      cv_node = varpool_node (emutls_decl (node->decl)) ;
> +      varpool_mark_needed_node (cv_node);
> +    }

Do you really need this kind of thing anymore, since you're exposing
the use of the control variable so early?  I would have thought that
varpool.c would no longer need any special-casing for !have_tls.

Finally, as a follow-up, I think the alias oracle needs to be taught what 
__builtin_emutls_get_address does.  Namely, return the address of the
associated TLS variable.  This is the only way I can think of to avoid
the optimization penalty that you're introducing because of exposing the
control variable and the function call at this point.


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-07 23:22 ` Richard Henderson
@ 2010-07-08  8:42   ` IainS
  2010-07-08  9:32     ` Jakub Jelinek
  2010-07-08 16:14     ` Richard Henderson
  2010-07-08  9:25   ` Richard Guenther
  2010-07-08 19:07   ` IainS
  2 siblings, 2 replies; 35+ messages in thread
From: IainS @ 2010-07-08  8:42 UTC (permalink / raw)
  To: Richard Henderson; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

Hello Richard,
Thanks for the review and sorry about the typographical nits...
I'll re-work the other things and investigate the alias oracle.

one question tho:

On 8 Jul 2010, at 00:22, Richard Henderson wrote:

>> +  /* ??? It is not enough to check DECL_COMMON because variables  
>> might be
>> +     allocated in other uninitialized sections.  However, this  
>> might still
>> +     not be an adequate test.  */
>> +  if (DECL_INITIAL (h->to))
>
> Huh?  Why is DECL_COMMON not sufficient?

int foo (void)
{
   static __thread int a;
   return a;
}

places __emutls_v.a.1700  in .lcomm and therefore needs an  
initializer, but the decl is not marked DECL_COMMON.
do you believe that indicates a bug elsewhere, or is it a reasonable  
explanation?

thanks
Iain

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-07 23:22 ` Richard Henderson
  2010-07-08  8:42   ` IainS
@ 2010-07-08  9:25   ` Richard Guenther
  2010-07-08 11:59     ` [Patch, testsuite] " IainS
  2010-07-08 19:07   ` IainS
  2 siblings, 1 reply; 35+ messages in thread
From: Richard Guenther @ 2010-07-08  9:25 UTC (permalink / raw)
  To: Richard Henderson
  Cc: IainS, GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On Thu, Jul 8, 2010 at 1:22 AM, Richard Henderson <rth@redhat.com> wrote:

> Finally, as a follow-up, I think the alias oracle needs to be taught what
> __builtin_emutls_get_address does.  Namely, return the address of the
> associated TLS variable.  This is the only way I can think of to avoid
> the optimization penalty that you're introducing because of exposing the
> control variable and the function call at this point.

While teaching the oracle about __builtin_emutls_get_address will
help disambiguating against emutls accesses it will not be enough
to trigger CSE.  So it would be nice to have a testcase that shows
we can properly CSE emutls accesses in FRE (in theory it shuld
work as __builtin_emutls_get_address is const).

Richard.

>
>
> r~
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08  8:42   ` IainS
@ 2010-07-08  9:32     ` Jakub Jelinek
  2010-07-08 12:01       ` IainS
  2010-07-08 16:14     ` Richard Henderson
  1 sibling, 1 reply; 35+ messages in thread
From: Jakub Jelinek @ 2010-07-08  9:32 UTC (permalink / raw)
  To: IainS; +Cc: Richard Henderson, GCC Patches, Diego Novillo, Jan Hubicka

On Thu, Jul 08, 2010 at 09:42:16AM +0100, IainS wrote:
> >Huh?  Why is DECL_COMMON not sufficient?
> 
> int foo (void)
> {
>   static __thread int a;
>   return a;
> }

This testcase should be optimized into just
int foo (void)
{
  return 0;
}
with no TLS vars at all, at least it is with non-emulated TLS.
Please make sure it doesn't regress for emulated TLS with your patch.

	Jakub

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [Patch, testsuite] Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08  9:25   ` Richard Guenther
@ 2010-07-08 11:59     ` IainS
  2010-07-08 12:05       ` IainS
  0 siblings, 1 reply; 35+ messages in thread
From: IainS @ 2010-07-08 11:59 UTC (permalink / raw)
  To: Richard Guenther
  Cc: Richard Henderson, GCC Patches, Diego Novillo, Jan Hubicka,
	Jakub Jelinek


On 8 Jul 2010, at 10:25, Richard Guenther wrote:

> On Thu, Jul 8, 2010 at 1:22 AM, Richard Henderson <rth@redhat.com>  
> wrote:
>
>> Finally, as a follow-up, I think the alias oracle needs to be  
>> taught what
>> __builtin_emutls_get_address does.  Namely, return the address of the
>> associated TLS variable.  This is the only way I can think of to  
>> avoid
>> the optimization penalty that you're introducing because of  
>> exposing the
>> control variable and the function call at this point.
>
> While teaching the oracle about __builtin_emutls_get_address will
> help disambiguating against emutls accesses it will not be enough
> to trigger CSE.  So it would be nice to have a testcase that shows
> we can properly CSE emutls accesses in FRE (in theory it shuld
> work as __builtin_emutls_get_address is const).

How about this?
  (which fails on trunk and passes on the patched version BTW)
(and I've no means to test vx-works so the conditional there is dead- 
reckoning from grepping that code)

Obv. I'm not suggesting it should be applied until trunk would pass.

Iain

testsuite/ChangeLog:

	* lib/target-supports.exp (check_effective_target_tls_native): New.
	* gcc.dg/tls/thr-cse-1.c: New.

---

Index: gcc/testsuite/lib/target-supports.exp
===================================================================
--- gcc/testsuite/lib/target-supports.exp	(revision 161874)
+++ gcc/testsuite/lib/target-supports.exp	(working copy)
@@ -596,6 +596,23 @@ proc check_effective_target_tls_native {} {
      }]
  }

+# Return 1 if *emulated* thread local storage (TLS) is supported, 0  
otherwise.
+
+proc check_effective_target_tls_emulated {} {
+    # VxWorks uses emulated TLS machinery, but with non-standard helper
+    # functions, so we fail to automatically detect it.
+    global target_triplet
+    if { [regexp ".*-.*-vxworks.*" $target_triplet] } {
+	return 1
+    }
+
+    return [check_no_messages_and_pattern tls_emulated "emutls"  
assembly {
+	__thread int i;
+	int f (void) { return i; }
+	void g (int j) { i = j; }
+    }]
+}
+
  # Return 1 if TLS executables can run correctly, 0 otherwise.

  proc check_effective_target_tls_runtime {} {
Index: gcc/testsuite/gcc.dg/tls/thr-cse-1.c
===================================================================
--- gcc/testsuite/gcc.dg/tls/thr-cse-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/tls/thr-cse-1.c	(revision 0)
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+/* { dg-require-effective-target tls_emulated } */
+
+/* Test that we only get one call to emutls_get_address when CSE is
+   active.  Note that the var _must_ be initialized for the scan asm
+   to work, since otherwise there will be an initializer which will,
+   correctly, call emutls_get_address.  */
+int foo (int b, int c, int d)
+{
+  static __thread int a=1;
+  a += b;
+  a -= c;
+  a += d;
+  return a;
+}
+
+/* { dg-final { scan-assembler-not  
"emutls_get_address.*emutls_get_address.*" { target { ! *-wrs- 
vxworks } } } } */
+/* { dg-final { scan-assembler-not  
"tls_lookup.*tls_lookup.*" { target *-wrs-vxworks } } } */
+



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08  9:32     ` Jakub Jelinek
@ 2010-07-08 12:01       ` IainS
  2010-07-08 17:10         ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: IainS @ 2010-07-08 12:01 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Henderson, GCC Patches, Diego Novillo, Jan Hubicka


On 8 Jul 2010, at 10:34, Jakub Jelinek wrote:

> On Thu, Jul 08, 2010 at 09:42:16AM +0100, IainS wrote:
>>> Huh?  Why is DECL_COMMON not sufficient?
>>
>> int foo (void)
>> {
>>  static __thread int a;
>>  return a;
>> }
>
> This testcase should be optimized into just
> int foo (void)
> {
>  return 0;
> }
> with no TLS vars at all, at least it is with non-emulated TLS.
> Please make sure it doesn't regress for emulated TLS with your patch.

Well, this wasn't the purpose of the example, O0 is sufficient for that.

However, it *does* regress on this test, so I've got more work to do  
there.
Thanks for catching that.

Iain.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, testsuite] Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 11:59     ` [Patch, testsuite] " IainS
@ 2010-07-08 12:05       ` IainS
  0 siblings, 0 replies; 35+ messages in thread
From: IainS @ 2010-07-08 12:05 UTC (permalink / raw)
  To: Richard Guenther; +Cc: GCC Patches


On 8 Jul 2010, at 12:58, IainS wrote:

>
> testsuite/ChangeLog:
>
> 	* lib/target-supports.exp (check_effective_target_tls_native): New.

pasto...

* lib/target-supports.exp (check_effective_target_tls_emulated): New.

Iain

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08  8:42   ` IainS
  2010-07-08  9:32     ` Jakub Jelinek
@ 2010-07-08 16:14     ` Richard Henderson
  2010-07-08 16:21       ` IainS
  1 sibling, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-08 16:14 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On 07/08/2010 01:42 AM, IainS wrote:
>> Huh?  Why is DECL_COMMON not sufficient?
> 
> int foo (void)
> {
>   static __thread int a;
>   return a;
> }
> 
> places __emutls_v.a.1700  in .lcomm and therefore needs an initializer,
> but the decl is not marked DECL_COMMON.
> do you believe that indicates a bug elsewhere, or is it a reasonable
> explanation?

The purpose of __emutls_register_common is to merge COMMON block sizes,
as would normally be done in the linker.  The variable A is not a COMMON
variable (as you note by DECL_COMMON not being set).  No initialization
is needed.

You've simply been confused about .lcomm for zero-initialization of the
control variable and true COMMON block variables.


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 16:14     ` Richard Henderson
@ 2010-07-08 16:21       ` IainS
  2010-07-08 17:04         ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: IainS @ 2010-07-08 16:21 UTC (permalink / raw)
  To: Richard Henderson; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek


On 8 Jul 2010, at 17:14, Richard Henderson wrote:

> On 07/08/2010 01:42 AM, IainS wrote:
>>> Huh?  Why is DECL_COMMON not sufficient?
>>
>> int foo (void)
>> {
>>  static __thread int a;
>>  return a;
>> }
>>
>> places __emutls_v.a.1700  in .lcomm and therefore needs an  
>> initializer,
>> but the decl is not marked DECL_COMMON.
>> do you believe that indicates a bug elsewhere, or is it a reasonable
>> explanation?
>
> The purpose of __emutls_register_common is to merge COMMON block  
> sizes,
> as would normally be done in the linker.  The variable A is not a  
> COMMON
> variable (as you note by DECL_COMMON not being set).  No  
> initialization
> is needed.
>
> You've simply been confused about .lcomm for zero-initialization of  
> the
> control variable and true COMMON block variables.

I'm sure it's confusion on my part - but :

__emutls_v.a
has size and align fields which cannot be 0.

so, either it should not be placed into .lcomm (I infer this is  
probably the case from your response), or these fields must be  
initialized by the emutls_register_common hook.

Iain



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 16:21       ` IainS
@ 2010-07-08 17:04         ` Richard Henderson
  0 siblings, 0 replies; 35+ messages in thread
From: Richard Henderson @ 2010-07-08 17:04 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On 07/08/2010 09:20 AM, IainS wrote:
> I'm sure it's confusion on my part - but :
> 
> __emutls_v.a
> has size and align fields which cannot be 0.
> 
> so, either it should not be placed into .lcomm (I infer this is probably
> the case from your response), or these fields must be initialized by the
> emutls_register_common hook.

Err, that's true.  In this case I'd expect __emutls_v.a to *not*
reside in .lcomm and not be initialized by the runtime.  Certainly
that's what happens with current HEAD.


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 12:01       ` IainS
@ 2010-07-08 17:10         ` Richard Henderson
  2010-07-08 17:21           ` Jan Hubicka
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-08 17:10 UTC (permalink / raw)
  To: IainS; +Cc: Jakub Jelinek, GCC Patches, Diego Novillo, Jan Hubicka

On 07/08/2010 05:00 AM, IainS wrote:
> On 8 Jul 2010, at 10:34, Jakub Jelinek wrote:
>> with no TLS vars at all, at least it is with non-emulated TLS.
>> Please make sure it doesn't regress for emulated TLS with your patch.
> 
> Well, this wasn't the purpose of the example, O0 is sufficient for that.
> 
> However, it *does* regress on this test, so I've got more work to do there.
> Thanks for catching that.

I very much doubt you'll be able to fix this (minor) regression.

The only reasonable way to prevent that is to *not* lower the TLS
variable so early, so that the optimizers see DECLs where they 
expect to see DECLs.  And that goal appears to be at odds with
making emulated tls LTO friendly.


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 17:10         ` Richard Henderson
@ 2010-07-08 17:21           ` Jan Hubicka
  2010-07-08 17:34             ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: Jan Hubicka @ 2010-07-08 17:21 UTC (permalink / raw)
  To: Richard Henderson
  Cc: IainS, Jakub Jelinek, GCC Patches, Diego Novillo, Jan Hubicka

> On 07/08/2010 05:00 AM, IainS wrote:
> > On 8 Jul 2010, at 10:34, Jakub Jelinek wrote:
> >> with no TLS vars at all, at least it is with non-emulated TLS.
> >> Please make sure it doesn't regress for emulated TLS with your patch.
> > 
> > Well, this wasn't the purpose of the example, O0 is sufficient for that.
> > 
> > However, it *does* regress on this test, so I've got more work to do there.
> > Thanks for catching that.
> 
> I very much doubt you'll be able to fix this (minor) regression.
> 
> The only reasonable way to prevent that is to *not* lower the TLS
> variable so early, so that the optimizers see DECLs where they 
> expect to see DECLs.  And that goal appears to be at odds with
> making emulated tls LTO friendly.

There is option to have emultls lowering pass somewhere at the end of early
optimization queue.  I am not however sure it is worth a special purpose pass.
Not lowering TLS is also leading to code quality problems, for instance inliner
will not take into account htat functions using TLS are rather expensive etc.

Honza
> 
> 
> r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 17:21           ` Jan Hubicka
@ 2010-07-08 17:34             ` Richard Henderson
  0 siblings, 0 replies; 35+ messages in thread
From: Richard Henderson @ 2010-07-08 17:34 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: IainS, Jakub Jelinek, GCC Patches, Diego Novillo

On 07/08/2010 10:20 AM, Jan Hubicka wrote:
> There is option to have emultls lowering pass somewhere at the end of early
> optimization queue.  I am not however sure it is worth a special purpose pass.

I'm not sure it's worth it either.  I can't think of any interesting early
optimizations that doesn't work equally well with the const function call.

> Not lowering TLS is also leading to code quality problems, for instance inliner
> will not take into account htat functions using TLS are rather expensive etc.

Well, *that* should simply be a matter of adjusting the costs.  Because
we'll never be able to lower real TLS -- the code sequences required for
linker optimization are extremely specific, padding prefixes and all.


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-07 23:22 ` Richard Henderson
  2010-07-08  8:42   ` IainS
  2010-07-08  9:25   ` Richard Guenther
@ 2010-07-08 19:07   ` IainS
  2010-07-08 19:19     ` Richard Henderson
  2 siblings, 1 reply; 35+ messages in thread
From: IainS @ 2010-07-08 19:07 UTC (permalink / raw)
  To: Richard Henderson; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek


On 8 Jul 2010, at 00:22, Richard Henderson wrote:

>
>> +  /* If we need the var, and it's an emulated TLS entity, that
>> +     means we need the control var.  */
>> +  if (!targetm.have_tls && DECL_THREAD_LOCAL_P (node->decl))
>> +    {
>> +      struct varpool_node *cv_node;
>> +      cv_node = varpool_node (emutls_decl (node->decl)) ;
>> +      varpool_mark_needed_node (cv_node);
>> +    }
>
> Do you really need this kind of thing anymore, since you're exposing
> the use of the control variable so early?  I would have thought that
> varpool.c would no longer need any special-casing for !have_tls.

this is my understanding (which might be flawed &| incomplete).

Whilst we are on the parse side - and building varpool & cgraph, the  
relationship is not fully exposed (that happens when gimplication is  
done).

So my reasoning was that the "ghost/proxy" vars should be made to  
track the user-land ones until then.

It would be possible to put all the checks for 'used-ness' and  
finalization in emutls_finish() but it seemed more elegant to do so in  
the places I put it.

I'm happy either way ..

Iain


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 19:07   ` IainS
@ 2010-07-08 19:19     ` Richard Henderson
  2010-07-09 12:11       ` IainS
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-08 19:19 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On 07/08/2010 12:07 PM, IainS wrote:
> 
> On 8 Jul 2010, at 00:22, Richard Henderson wrote:
>> Do you really need this kind of thing anymore, since you're exposing
>> the use of the control variable so early?  I would have thought that
>> varpool.c would no longer need any special-casing for !have_tls.
> 
> this is my understanding (which might be flawed &| incomplete).
> 
> Whilst we are on the parse side - and building varpool & cgraph, the
> relationship is not fully exposed (that happens when gimplication is done).
> 
> So my reasoning was that the "ghost/proxy" vars should be made to track
> the user-land ones until then.

Hmm.  So what you're saying is that there's extra cleanup work that
needs to happen while lowering the representation.  It's not merely
a matter of code substitution during gimplification.

In which case I wonder if it wouldn't be better to do as Honza
suggested and separate all of this out into a new pass_lower_emutls.
Perhaps to be placed just after pass_lower_vector.  That placement
is before pass_build_cgraph_edges, which I believe means you would
not have to fix up the cgraph edges for the new function calls.  All
you'd need to do is transform the tls variable references and fix up
the varpool references.

Honza, thoughts here?


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-08 19:19     ` Richard Henderson
@ 2010-07-09 12:11       ` IainS
  2010-07-12 14:48         ` Jack Howarth
  2010-07-12 15:18         ` Richard Henderson
  0 siblings, 2 replies; 35+ messages in thread
From: IainS @ 2010-07-09 12:11 UTC (permalink / raw)
  To: Richard Henderson; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 5118 bytes --]


On 8 Jul 2010, at 20:19, Richard Henderson wrote:

> On 07/08/2010 12:07 PM, IainS wrote:
>>
>> On 8 Jul 2010, at 00:22, Richard Henderson wrote:
>>> Do you really need this kind of thing anymore, since you're exposing
>>> the use of the control variable so early?  I would have thought that
>>> varpool.c would no longer need any special-casing for !have_tls.
>>
>> this is my understanding (which might be flawed &| incomplete).
>>
>> Whilst we are on the parse side - and building varpool & cgraph, the
>> relationship is not fully exposed (that happens when gimplication  
>> is done).
>>
>> So my reasoning was that the "ghost/proxy" vars should be made to  
>> track
>> the user-land ones until then.
>
> Hmm.  So what you're saying is that there's extra cleanup work that
> needs to happen while lowering the representation.  It's not merely
> a matter of code substitution during gimplification.

Attached is an updated patch which addresses the points you raised -  
less my response above.

IMO it is not the world's most elegant solution..
... perhaps in part owing to me not know the "Best Places" to do some  
things
... but I suspect also because the whole thing doesn't sit comfortably..

I'm not sure where we sit in terms of applying this..
I guess it remains an interim solution to LTO on emuTLS targets, with  
the observation that perhaps a different approach is needed now we  
have LTO.

I've tested on i686-apple-darwin9 and cris-elf that the changes don't  
regress.
(in fact, my local tls testsuite is somewhat wider than the one in  
trunk)

If it is intended to apply this then, of course, I'd test on linux  
first (should be a nil response).

Known Minus:
  it fails the regression Jakub pointed out.

Known Plus:
   it allows lto
  it actually performs better on CSE than trunk does (test added).

I'd hazard that the CSE improvement is worth more than the lost  
trivial zero return.

> In which case I wonder if it wouldn't be better to do as Honza
> suggested and separate all of this out into a new pass_lower_emutls.
> Perhaps to be placed just after pass_lower_vector.  That placement
> is before pass_build_cgraph_edges, which I believe means you would
> not have to fix up the cgraph edges for the new function calls.  All
> you'd need to do is transform the tls variable references and fix up
> the varpool references.

My £0.02 having tangled with this for the last few weeks...

1/ a pass behind a gate of ! targetm.have_tls is a whole lot less  
intrusive to non-emutls targets than what we have
2/ it's likely more efficient for emutls targets since there's less to- 
ing and fro-ing.
3/ probably more maintainable.
4/ certainly more transparent.
5/ (probably) loses all reference to emutls from varasm, varpool,  
expr, & gimplify..

mind you, that doesn't mean I know how to write such a pass ...  ;-)

Iain.

gcc:

	PR target/44132
	* expr.c (emutls_var_address): Remove.
	(expand_expr_addr_expr_1): Remove TLS emulation hook.
	(expand_expr_real_1): Ditto.

	* gimplify.c (emutls_var_address): Add proc.
	(gimplify_decl_expr): expand TLS vars.
	(gimplify_var_or_parm_decl): Ditto.
	(omp_notice_variable): Recognize TLS_MODEL_EMULATED.

	* passes.c (rest_of_decl_compilation): Substitute TLS control vars  
for the master.

	* varasm.c (decl_needs_tls_emulation_p): New.
	(get_emutls_init_templ_addr): Adjust DECL_PRESERVE_P and DECL_INITIAL.
	(emutls_decl): Copy TREE_ADDRESSABLE, DECL_PRESERVE_P, create an
	initializer when one is present on the user-var.
	(emutls_common_1): Remove comment.
	(emutls_finalize_control_var): Copy TREE_USED, add an initializer for  
size and
	align fields for cases with un-initialized user vars.
	(emutls_find_user_var_cb): New.
	(emutls_add_base_initializer): New.
	(asm_output_bss): Assert not a tls var needing emulation.
	(asm_output_aligned_bss): Ditto.
	(assemble_variable): Remove control var init. code.  Assert not a tls  
var needing
	emulation.  Provide a trivial initializer for size and align fields  
if one is not already
	set. (do_assemble_alias): Do not handle emutls vars here.
	(var_decl_for_asm): New.
	(finish_aliases_1): Walk the alias pairs substituting emutls controls  
for the user
	counterparts.

	* varpool.c (varpool_mark_needed_node): Do not handle TLS  
substitution here.
	(decide_is_variable_needed): Or here.
	(varpool_finalize_decl): Handle TLS substitution.
	Remove early calls to varpool_assemble_pending_decls().
	Check enqueuing of vars after all tests for need are complete.
	(varpool_analyze_pending_decls): Do not record references if the  
initializer is error_mark.

testsuite:
	
	PR target/44132
	* gcc.dg/tls/thr-init-1.c: New.
	* gcc.dg/tls/thr-init-2.c: New.
	* gcc.dg/torture/tls New.
	* gcc.dg/torture/tls/tls-test.c: New.
	* gcc.dg/torture/tls/thr-init-1.c: New.
	* gcc.dg/torture/tls/tls.exp: New.
	* gcc.dg/torture/tls/thr-init-2.c: New.

	* lib/target-supports.exp (check_effective_target_tls_emulated): New.
	* gcc.dg/tls/thr-cse-1.c: New.



[-- Attachment #2: 161974-emutls-lto.txt --]
[-- Type: text/plain, Size: 21904 bytes --]

Index: gcc/expr.c
===================================================================
--- gcc/expr.c	(revision 161974)
+++ gcc/expr.c	(working copy)
@@ -6828,21 +6828,7 @@ highest_pow2_factor_for_target (const_tree target,
 
   return MAX (factor, talign);
 }
-\f
-/* 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 (UNKNOWN_LOCATION, fn, arglist);
-  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
-}
-\f
-
 /* Subroutine of expand_expr.  Expand the two operands of a binary
    expression EXP0 and EXP1 placing the results in OP0 and OP1.
    The value may be stored in TARGET if TARGET is nonzero.  The
@@ -6946,17 +6932,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enu
       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
@@ -8394,16 +8369,6 @@ expand_expr_real_1 (tree exp, rtx target, enum mac
 	  && (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_loc (loc, emutls_var_address (exp));
-	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
-	}
-
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
Index: gcc/gimplify.c
===================================================================
--- gcc/gimplify.c	(revision 161974)
+++ gcc/gimplify.c	(working copy)
@@ -1339,7 +1339,19 @@ gimplify_vla_decl (tree decl, gimple_seq *seq_p)
   gimplify_ctxp->save_stack = true;
 }
 
+/* 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 (UNKNOWN_LOCATION, fn, arglist);
+  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
+}
+
 /* Gimplifies a DECL_EXPR node *STMT_P by making any necessary allocation
    and initialization explicit.  */
 
@@ -1354,6 +1366,18 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_
   if (TREE_TYPE (decl) == error_mark_node)
     return GS_ERROR;
 
+  /* TLS emulation hook - replace __thread VAR's &VAR with
+     __emutls_get_address (&_emutls.VAR). We then ignore the original
+     var.  */
+  if (! targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      stmt = build_fold_indirect_ref (emutls_var_address (decl));
+      gimplify_and_add (stmt, seq_p);
+      return GS_ALL_DONE;
+    }
+
   if ((TREE_CODE (decl) == TYPE_DECL
        || TREE_CODE (decl) == VAR_DECL)
       && !TYPE_SIZES_GIMPLIFIED (TREE_TYPE (decl)))
@@ -1873,6 +1897,17 @@ gimplify_var_or_parm_decl (tree *expr_p)
       return GS_ERROR;
     }
 
+  /* TLS emulation hook - replace __thread VAR's &VAR with
+     __emutls_get_address (&_emutls.VAR).  */
+  if (! targetm.have_tls
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      gcc_assert (!DECL_HAS_VALUE_EXPR_P (decl));
+      *expr_p = build_fold_indirect_ref (emutls_var_address (decl));
+      return GS_OK;
+    }
+
   /* When within an OpenMP context, notice uses of variables.  */
   if (gimplify_omp_ctxp && omp_notice_variable (gimplify_omp_ctxp, decl, true))
     return GS_ALL_DONE;
@@ -5553,14 +5588,15 @@ omp_notice_variable (struct gimplify_omp_ctx *ctx,
   /* Threadprivate variables are predetermined.  */
   if (is_global_var (decl))
     {
-      if (DECL_THREAD_LOCAL_P (decl))
+      if (DECL_TLS_MODEL (decl) != TLS_MODEL_NONE)
 	return omp_notice_threadprivate_variable (ctx, decl, NULL_TREE);
 
       if (DECL_HAS_VALUE_EXPR_P (decl))
 	{
 	  tree value = get_base_address (DECL_VALUE_EXPR (decl));
 
-	  if (value && DECL_P (value) && DECL_THREAD_LOCAL_P (value))
+	  if (value && DECL_P (value) 
+	      && (DECL_TLS_MODEL (value) != TLS_MODEL_NONE))
 	    return omp_notice_threadprivate_variable (ctx, decl, value);
 	}
     }
Index: gcc/varasm.c
===================================================================
--- gcc/varasm.c	(revision 161974)
+++ gcc/varasm.c	(working copy)
@@ -204,6 +204,14 @@ static GTY (()) tree emutls_object_type;
 # define EMUTLS_SEPARATOR	"_"
 #endif
 
+static int
+decl_needs_tls_emulation_p (tree decl)
+{
+  return !targetm.have_tls 
+	  && TREE_CODE (decl) == VAR_DECL 
+	  && DECL_THREAD_LOCAL_P (decl);
+}
+
 /* Create an IDENTIFIER_NODE by prefixing PREFIX to the
    IDENTIFIER_NODE NAME's name.  */
 
@@ -322,7 +330,7 @@ get_emutls_init_templ_addr (tree decl)
   DECL_IGNORED_P (to) = 1;
   DECL_CONTEXT (to) = DECL_CONTEXT (decl);
   DECL_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
-  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+  DECL_PRESERVE_P (to) = 1;
 
   DECL_WEAK (to) = DECL_WEAK (decl);
   if (DECL_ONE_ONLY (decl))
@@ -337,7 +345,6 @@ get_emutls_init_templ_addr (tree decl)
 
   DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
   DECL_INITIAL (to) = DECL_INITIAL (decl);
-  DECL_INITIAL (decl) = NULL;
 
   varpool_finalize_decl (to);
   return build_fold_addr_expr (to);
@@ -388,8 +395,7 @@ emutls_decl (tree decl)
       DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
       DECL_ARTIFICIAL (to) = 1;
       DECL_IGNORED_P (to) = 1;
-      /* FIXME: work around PR44132.  */
-      DECL_PRESERVE_P (to) = 1;
+
       TREE_READONLY (to) = 0;
       SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
       if (DECL_ONE_ONLY (decl))
@@ -413,18 +419,42 @@ emutls_decl (tree decl)
   TREE_STATIC (to) = TREE_STATIC (decl);
   TREE_USED (to) = TREE_USED (decl);
   TREE_PUBLIC (to) = TREE_PUBLIC (decl);
+  TREE_ADDRESSABLE (to) = TREE_ADDRESSABLE (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);
   DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
   
   /* Fortran might pass this to us.  */
   DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
+  
+  /* As soon as we see an initializer (and providing one is not already
+     present) we can setup the init. template.  */
+  if (!DECL_INITIAL (to) 
+       && DECL_INITIAL (decl) 
+       && DECL_INITIAL (decl) != error_mark_node 
+       && !DECL_EXTERNAL (to) 
+       && !DECL_COMMON (to))
+    {
+      DECL_INITIAL (to) = targetm.emutls.var_init
+			  (to, decl, get_emutls_init_templ_addr (decl));
 
+      /* Make sure the template is marked as needed early enough.
+	 Without this, if the variable is placed in a
+	 section-anchored block, the template will only be marked
+	 when it's too late.*/
+      record_references_in_initializer (to, false);
+    }
+
+  /* Say we are not interested in emitting this Var.  */
+  TREE_ASM_WRITTEN (decl) = 1;
   return to;
 }
 
+/* Add static constructors for emutls vars, where required.  */
+
 static int
 emutls_common_1 (void **loc, void *xstmts)
 {
@@ -439,10 +469,6 @@ emutls_common_1 (void **loc, void *xstmts)
 
   word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
 
-  /* The idea was to call get_emutls_init_templ_addr here, but if we
-     do this and there is an initializer, -fanchor_section loses,
-     because it would be too late to ensure the template is
-     output.  */
   x = null_pointer_node;
   args = tree_cons (NULL, x, NULL);
   x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
@@ -469,8 +495,22 @@ emutls_finalize_control_var (void **loc,
   if (h != NULL) 
     {
       struct varpool_node *node = varpool_node (h->to);
+      if (TREE_USED (h->base.from)) 
+	TREE_USED (h->to) = 1;
+
+      /* We must ensure that the size and align fields are initialized
+         in control vars, even when the value is not.  */
+      if ((!DECL_INITIAL (h->base.from)
+            || DECL_INITIAL (h->base.from) == error_mark_node)
+	    && !DECL_COMMON (h->base.from)
+	    && !DECL_EXTERNAL (h->base.from))
+	{
+	  DECL_INITIAL (h->to) = targetm.emutls.var_init
+			(h->to, h->base.from, null_pointer_node);
+	}
+
       /* Because varpool_finalize_decl () has side-effects,
-         only apply to un-finalized vars.  */
+         only call it for un-finalized vars.  */
       if (node && !node->finalized) 
 	varpool_finalize_decl (h->to);
     }
@@ -500,6 +540,39 @@ emutls_finish (void)
     }
 }
 
+/* Callback to check an htab entry against a supplied control var and
+   subsitute the original user var if they match.  */
+static int
+emutls_find_user_var_cb (void **loc, void *uvar)
+{
+  struct tree_map *h = *(struct tree_map **) loc;
+  if (h != NULL
+      && h->to == *((tree *)uvar))
+    {
+       *((tree *)uvar) = h->base.from;
+       return 0;
+    }
+  return 1;
+}
+
+/* In the case of uninitialized global vars, we have to find our way back
+   to the user-var in order to build a trivial initializer for the size
+   and align fields.  */
+
+static void
+emutls_add_base_initializer (tree control)
+{
+  tree uvar = control;
+  if (emutls_htab == NULL)
+    return;
+  htab_traverse_noresize (emutls_htab, emutls_find_user_var_cb, &uvar);
+  /* If we didn't find the user var from the control, something is broken.  */
+  gcc_assert (uvar != control);
+  DECL_INITIAL (control) = targetm.emutls.var_init
+			(control, uvar, null_pointer_node);
+}
+
+
 /* Helper routines for maintaining section_htab.  */
 
 static int
@@ -792,6 +865,8 @@ asm_output_bss (FILE *file, tree decl ATTRIBUTE_UN
   gcc_assert (strcmp (XSTR (XEXP (DECL_RTL (decl), 0), 0), name) == 0);
   targetm.asm_out.globalize_decl_name (file, decl);
   switch_to_section (bss_section);
+  /* We don't emit the userland vars for emulated TLS, just the control.  */
+  gcc_assert (!decl_needs_tls_emulation_p (decl));
 #ifdef ASM_DECLARE_OBJECT_NAME
   last_assemble_variable_decl = decl;
   ASM_DECLARE_OBJECT_NAME (file, name, decl);
@@ -818,6 +893,8 @@ asm_output_aligned_bss (FILE *file, tree decl ATTR
 {
   switch_to_section (bss_section);
   ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
+  /* We don't emit the userland vars for emulated TLS, just the control.  */
+  gcc_assert (!decl_needs_tls_emulation_p (decl));
 #ifdef ASM_DECLARE_OBJECT_NAME
   last_assemble_variable_decl = decl;
   ASM_DECLARE_OBJECT_NAME (file, name, decl);
@@ -1233,7 +1310,10 @@ get_variable_section (tree decl, bool prefer_noswi
   if (IN_NAMED_SECTION (decl))
     return get_named_section (decl, NULL, reloc);
 
-  if (ADDR_SPACE_GENERIC_P (as)
+  /* This should not be bss for an emulated TLS object.  */
+  if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED)
+    ;
+  else if (ADDR_SPACE_GENERIC_P (as)
       && !DECL_THREAD_LOCAL_P (decl)
       && !(prefer_noswitch_p && targetm.have_switchable_bss_sections)
       && bss_initializer_p (decl))
@@ -2153,35 +2233,6 @@ assemble_variable (tree decl, int top_level ATTRIB
   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)
-	      || (DECL_INITIAL (decl)
-		  && DECL_INITIAL (decl) != error_mark_node)))
-	{
-	  DECL_INITIAL (to) = targetm.emutls.var_init
-	    (to, decl, get_emutls_init_templ_addr (decl));
-
-	  /* Make sure the template is marked as needed early enough.
-	     Without this, if the variable is placed in a
-	     section-anchored block, the template will only be marked
-	     when it's too late.  */
-	  record_references_in_initializer (to, false);
-	}
-
-      decl = to;
-    }
-
   last_assemble_variable_decl = 0;
 
   /* Normally no need to say anything here for external references,
@@ -2238,6 +2289,19 @@ assemble_variable (tree decl, int top_level ATTRIB
   if (flag_syntax_only)
     return;
 
+  /* We don't emit the userland vars for emulated TLS - they should never
+     get to here, only the control vars should be emitted.  */
+  gcc_assert (! decl_needs_tls_emulation_p (decl));
+  
+  /* However, for the emutls control vars must ensure that the size and
+     align fields are initialized, even if the value is not.  */
+  if (!targetm.have_tls 
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
+      && !DECL_INITIAL (decl)
+      && !DECL_COMMON (decl))
+    emutls_add_base_initializer (decl);
+
   if (! dont_output_data
       && ! host_integerp (DECL_SIZE_UNIT (decl), 1))
     {
@@ -5701,18 +5765,12 @@ do_assemble_alias (tree decl, tree target)
   TREE_ASM_WRITTEN (decl) = 1;
   TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1;
 
+  gcc_assert (! decl_needs_tls_emulation_p (decl));
+
   if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
     {
       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);
 
@@ -5731,14 +5789,6 @@ do_assemble_alias (tree decl, tree target)
       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.  */
 
@@ -5820,6 +5870,15 @@ remove_unreachable_alias_pairs (void)
     }
 }
 
+/* Lookup the decl for a symbol in the varpool.  */
+static tree
+var_decl_for_asm (tree symbol)
+{
+  struct varpool_node *vnode = varpool_node_for_asm  (symbol);
+  if (vnode) 
+    return vnode->decl;
+  return NULL;
+}
 
 /* First pass of completing pending aliases.  Make sure that cgraph knows
    which symbols will be required.  */
@@ -5832,8 +5891,55 @@ finish_aliases_1 (void)
 
   for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++)
     {
-      tree target_decl;
+      tree target_decl=NULL;
 
+      /* When emulated TLS is in effect, redirect aliases so that they 
+         are registered between the control vars.  */
+      if (!targetm.have_tls 
+          && TREE_CODE (p->decl) == VAR_DECL
+          && DECL_TLS_MODEL (p->decl) != TLS_MODEL_NONE)
+	{
+	  tree tsym = p->target ;
+	  target_decl = var_decl_for_asm (tsym) ;
+	  if (!target_decl) 
+	    {
+	      /* If we didn't find the user's symbol, it could
+	         be because the alias really refers to a control 
+	         var.  */
+	      tsym = get_emutls_object_name (p->target);
+	      target_decl = var_decl_for_asm (tsym);
+	    }
+	  if (target_decl) 
+	    {
+	      struct varpool_node *vnode;
+	      /* If it hasn't been done already, substitute the control
+	         var for the original.  */
+	      if (DECL_THREAD_LOCAL_P (p->decl))
+		p->decl = emutls_decl (p->decl);
+	      /* If not TLS target, we've made a mistake.  */
+	      if (DECL_TLS_MODEL (target_decl) < TLS_MODEL_EMULATED)
+		error ("TLS symbol %q+D aliased to non-TLS symbol %qE",
+			p->decl, p->target);
+	      /* If it's the original we need to substitute the contol.  */
+	      else if (DECL_THREAD_LOCAL_P (target_decl))
+		{
+		  target_decl = emutls_decl (target_decl);
+		  tsym = get_emutls_object_name (p->target);
+		}
+	      /* else it's already the emulation control.  */
+	      /* Mark the var needed.  */
+	      vnode = varpool_node (target_decl);
+	      if (vnode) 
+	        {
+		  varpool_mark_needed_node (vnode);
+		  vnode->force_output = 1;
+	        }
+	      p->target = tsym;
+	    }
+	  /* Else we didn't find a decl for the symbol, which is an error
+	     unless there's a weak ref.  */
+	} 
+      else
       target_decl = find_decl_and_mark_needed (p->decl, p->target);
       if (target_decl == NULL)
 	{
Index: gcc/passes.c
===================================================================
--- gcc/passes.c	(revision 161974)
+++ gcc/passes.c	(working copy)
@@ -152,6 +152,16 @@ rest_of_decl_compilation (tree decl,
 			  int top_level,
 			  int at_end)
 {
+  if (! targetm.have_tls 
+	&& !in_lto_p 
+	&& TREE_CODE (decl) == VAR_DECL
+	&& DECL_THREAD_LOCAL_P (decl))
+    {
+      /* Substitute the control var. for the user one.  */
+      rest_of_decl_compilation (emutls_decl (decl), top_level, at_end);
+      return;
+    }
+
   /* We deferred calling assemble_alias so that we could collect
      other attributes such as visibility.  Emit the alias now.  */
   {
Index: gcc/varpool.c
===================================================================
--- gcc/varpool.c	(revision 161974)
+++ gcc/varpool.c	(working copy)
@@ -312,6 +312,14 @@ varpool_mark_needed_node (struct varpool_node *nod
       && !TREE_ASM_WRITTEN (node->decl))
     varpool_enqueue_needed_node (node);
   node->needed = 1;
+  /* If we need the var, and it's an emulated TLS entity, that
+     means we need the control var.  */
+  if (!targetm.have_tls && DECL_THREAD_LOCAL_P (node->decl))
+    {
+      struct varpool_node *cv_node;
+      cv_node = varpool_node (emutls_decl (node->decl)) ;
+      varpool_mark_needed_node (cv_node);
+    }
 }
 
 /* Reset the queue of needed nodes.  */
@@ -346,17 +354,6 @@ decide_is_variable_needed (struct varpool_node *no
       && !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 (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_toplevel_reorder)
@@ -381,14 +378,31 @@ varpool_finalize_decl (tree decl)
      or local (in C, has internal linkage).  So do nothing more
      if this function has already run.  */
   if (node->finalized)
+      return;
+
+  /* For emulated TLS vars, if we are in a position to finalize the userland
+     var, then we should be able to finalize the control var too.  */
+  if (!targetm.have_tls 
+      && TREE_CODE (decl) == VAR_DECL
+      && DECL_THREAD_LOCAL_P (decl))
     {
-      if (cgraph_global_info_ready)
-	varpool_assemble_pending_decls ();
+      tree control = emutls_decl (decl);
+      /* If we didn't create an initializer in the preceding line,  then we 
+         must now add a minimal one that sets the size and align fields.  */
+      if (!DECL_INITIAL (control) 
+	   && !DECL_COMMON (control)
+	   && !DECL_EXTERNAL (control))
+	{
+	  DECL_INITIAL (control) = targetm.emutls.var_init
+			(control, decl, null_pointer_node);
+	  record_references_in_initializer (control, false);
+	}
+
+      varpool_finalize_decl (control) ;
+      node->finalized = true;    
       return;
     }
-  if (node->needed)
-    varpool_enqueue_needed_node (node);
-  node->finalized = true;
+
   if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl))
     node->force_output = true;
 
@@ -399,8 +413,11 @@ varpool_finalize_decl (tree decl)
      there.  */
   else if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
     varpool_mark_needed_node (node);
-  if (cgraph_global_info_ready)
-    varpool_assemble_pending_decls ();
+
+  if (node->needed)
+    varpool_enqueue_needed_node (node);
+
+  node->finalized = true;
 }
 
 /* Return variable availability.  See cgraph.h for description of individual
@@ -449,7 +466,7 @@ varpool_analyze_pending_decls (void)
 	     already informed about increased alignment.  */
           align_variable (decl, 0);
 	}
-      if (DECL_INITIAL (decl))
+      if (DECL_INITIAL (decl) && (DECL_INITIAL (decl) != error_mark_node))
 	record_references_in_initializer (decl, analyzed);
       if (node->same_comdat_group)
 	{

[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




[-- Attachment #4: 161974-emutls-lto-testsuite.txt --]
[-- Type: text/plain, Size: 7201 bytes --]

Index: gcc/testsuite/lib/target-supports.exp
===================================================================
--- gcc/testsuite/lib/target-supports.exp	(revision 161974)
+++ gcc/testsuite/lib/target-supports.exp	(working copy)
@@ -596,6 +596,23 @@ proc check_effective_target_tls_native {} {
     }]
 }
 
+# Return 1 if *emulated* thread local storage (TLS) is supported, 0 otherwise.
+
+proc check_effective_target_tls_emulated {} {
+    # VxWorks uses emulated TLS machinery, but with non-standard helper
+    # functions, so we fail to automatically detect it.
+    global target_triplet
+    if { [regexp ".*-.*-vxworks.*" $target_triplet] } {
+	return 1
+    }
+    
+    return [check_no_messages_and_pattern tls_emulated "emutls" assembly {
+	__thread int i;
+	int f (void) { return i; }
+	void g (int j) { i = j; }
+    }]
+}
+
 # Return 1 if TLS executables can run correctly, 0 otherwise.
 
 proc check_effective_target_tls_runtime {} {
Index: gcc/testsuite/gcc.dg/tls/thr-cse-1.c
===================================================================
--- gcc/testsuite/gcc.dg/tls/thr-cse-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/tls/thr-cse-1.c	(revision 0)
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+/* { dg-require-effective-target tls_emulated } */
+
+/* Test that we only get one call to emutls_get_address when CSE is
+   active.  Note that the var _must_ be initialized for the scan asm
+   to work, since otherwise there will be an initializer which will,
+   correctly, call emutls_get_address.  */
+int foo (int b, int c, int d)
+{
+  static __thread int a=1;
+  a += b;
+  a -= c;
+  a += d;
+  return a;
+}
+
+/* { dg-final { scan-assembler-not "emutls_get_address.*emutls_get_address.*" { target { ! *-wrs-vxworks } } } } */
+/* { dg-final { scan-assembler-not "tls_lookup.*tls_lookup.*" { target *-wrs-vxworks } } } */
+
Index: gcc/testsuite/gcc.dg/tls/thr-init-1.c
===================================================================
--- gcc/testsuite/gcc.dg/tls/thr-init-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/tls/thr-init-1.c	(revision 0)
@@ -0,0 +1,8 @@
+/* { dg-require-effective-target tls } */
+/* { dg-do compile } */
+
+static __thread int fstat ;
+static __thread int fstat = 1 ;
+static __thread int fstat ;
+static __thread int fstat = 2; /* { dg-error "redefinition of 'fstat'" } */
+				/* { dg-message "note: previous definition of 'fstat' was here" "" { target *-*-* } 5 } */
Index: gcc/testsuite/gcc.dg/tls/thr-init-2.c
===================================================================
--- gcc/testsuite/gcc.dg/tls/thr-init-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/tls/thr-init-2.c	(revision 0)
@@ -0,0 +1,23 @@
+/* { dg-require-effective-target tls } */
+/* { dg-do run } */
+
+extern void abort() ;
+
+static __thread int fstat ;
+static __thread int fstat = 1;
+
+int test_code(int b)
+{
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ((a != 2) || (fstat != 2))
+    abort () ;
+  
+  return 0;
+}
Index: gcc/testsuite/gcc.dg/torture/tls/tls-test.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/tls-test.c	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/tls-test.c	(revision 0)
@@ -0,0 +1,52 @@
+/* { dg-do run }  */
+/* { dg-require-effective-target tls  }  */
+/* { dg-require-effective-target pthread } */
+/* { dg-options "-pthread" } */
+
+#include <pthread.h>
+extern int printf (char *,...);
+__thread int a = 5; 
+int *volatile a_in_other_thread = (int *) 12345;
+
+static void *
+thread_func (void *arg)
+{
+  a_in_other_thread = &a;
+  a+=5;
+  *((int *) arg) = a;
+  return (void *)0;
+}
+
+int
+main ()
+{
+  pthread_t thread;
+  void *thread_retval;
+  int *volatile a_in_main_thread;
+  int *volatile again ;
+  int thr_a;
+
+  a_in_main_thread = &a;
+
+  if (pthread_create (&thread, (pthread_attr_t *)0, thread_func, &thr_a))
+    return 0;
+
+  if (pthread_join (thread, &thread_retval))
+    return 0;
+
+  again = &a;
+  if (again != a_in_main_thread)
+    {
+      printf ("FAIL: main thread addy changed from 0x%0x to 0x%0x\n", 
+		a_in_other_thread, again);
+      return 1;
+    }
+
+  if (a != 5 || thr_a != 10 || (a_in_other_thread == a_in_main_thread))
+    {
+      printf ("FAIL: a= %d, thr_a = %d Addr = 0x%0x\n", 
+		a, thr_a, a_in_other_thread);
+      return 1;
+    }
+  return 0;
+}
Index: gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c	(revision 0)
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-require-effective-target tls } */
+
+extern int printf (char *,...);
+extern void abort() ;
+
+int test_code(int b)
+{
+static __thread int fstat = 1;
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ( a != 2 )
+    {
+      printf ("a=%d\n", a) ;
+      abort ();
+    }
+  
+  return 0;
+}
Index: gcc/testsuite/gcc.dg/torture/tls/tls.exp
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/tls.exp	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/tls.exp	(revision 0)
@@ -0,0 +1,36 @@
+#   Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+        $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
Index: gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c	(revision 0)
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-effective-target tls } */
+
+extern int printf (char *,...);
+extern void abort() ;
+
+static __thread int fstat ;
+static __thread int fstat = 1;
+static __thread int fstat ;
+
+int test_code(int b)
+{
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ( a != 2 || fstat != 2 )
+    {
+    printf ("a=%d fstat=%d\n", a, fstat) ;
+    abort ();
+    }
+  
+  return 0;
+}

[-- Attachment #5: Type: text/plain, Size: 1 bytes --]



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-09 12:11       ` IainS
@ 2010-07-12 14:48         ` Jack Howarth
  2010-07-12 15:18         ` Richard Henderson
  1 sibling, 0 replies; 35+ messages in thread
From: Jack Howarth @ 2010-07-12 14:48 UTC (permalink / raw)
  To: IainS
  Cc: Richard Henderson, GCC Patches, Diego Novillo, Jan Hubicka,
	Jakub Jelinek

On Fri, Jul 09, 2010 at 01:10:16PM +0100, IainS wrote:
>
> On 8 Jul 2010, at 20:19, Richard Henderson wrote:
>
>> On 07/08/2010 12:07 PM, IainS wrote:
>>>
>>> On 8 Jul 2010, at 00:22, Richard Henderson wrote:
>>>> Do you really need this kind of thing anymore, since you're exposing
>>>> the use of the control variable so early?  I would have thought that
>>>> varpool.c would no longer need any special-casing for !have_tls.
>>>
>>> this is my understanding (which might be flawed &| incomplete).
>>>
>>> Whilst we are on the parse side - and building varpool & cgraph, the
>>> relationship is not fully exposed (that happens when gimplication is 
>>> done).
>>>
>>> So my reasoning was that the "ghost/proxy" vars should be made to  
>>> track
>>> the user-land ones until then.
>>
>> Hmm.  So what you're saying is that there's extra cleanup work that
>> needs to happen while lowering the representation.  It's not merely
>> a matter of code substitution during gimplification.
>
> Attached is an updated patch which addresses the points you raised -  
> less my response above.

Richard,
   Could Iain just commit this current version of the patch with
a TODO for converting it into another pass before gcc 4.6 is released?
           Jack

>
> IMO it is not the world's most elegant solution..
> ... perhaps in part owing to me not know the "Best Places" to do some  
> things
> ... but I suspect also because the whole thing doesn't sit comfortably..
>
> I'm not sure where we sit in terms of applying this..
> I guess it remains an interim solution to LTO on emuTLS targets, with  
> the observation that perhaps a different approach is needed now we have 
> LTO.
>
> I've tested on i686-apple-darwin9 and cris-elf that the changes don't  
> regress.
> (in fact, my local tls testsuite is somewhat wider than the one in  
> trunk)
>
> If it is intended to apply this then, of course, I'd test on linux first 
> (should be a nil response).
>
> Known Minus:
>  it fails the regression Jakub pointed out.
>
> Known Plus:
>   it allows lto
>  it actually performs better on CSE than trunk does (test added).
>
> I'd hazard that the CSE improvement is worth more than the lost trivial 
> zero return.
>
>> In which case I wonder if it wouldn't be better to do as Honza
>> suggested and separate all of this out into a new pass_lower_emutls.
>> Perhaps to be placed just after pass_lower_vector.  That placement
>> is before pass_build_cgraph_edges, which I believe means you would
>> not have to fix up the cgraph edges for the new function calls.  All
>> you'd need to do is transform the tls variable references and fix up
>> the varpool references.
>
> My £0.02 having tangled with this for the last few weeks...
>
> 1/ a pass behind a gate of ! targetm.have_tls is a whole lot less  
> intrusive to non-emutls targets than what we have
> 2/ it's likely more efficient for emutls targets since there's less to- 
> ing and fro-ing.
> 3/ probably more maintainable.
> 4/ certainly more transparent.
> 5/ (probably) loses all reference to emutls from varasm, varpool, expr, & 
> gimplify..
>
> mind you, that doesn't mean I know how to write such a pass ...  ;-)
>
> Iain.
>
> gcc:
>
> 	PR target/44132
> 	* expr.c (emutls_var_address): Remove.
> 	(expand_expr_addr_expr_1): Remove TLS emulation hook.
> 	(expand_expr_real_1): Ditto.
>
> 	* gimplify.c (emutls_var_address): Add proc.
> 	(gimplify_decl_expr): expand TLS vars.
> 	(gimplify_var_or_parm_decl): Ditto.
> 	(omp_notice_variable): Recognize TLS_MODEL_EMULATED.
>
> 	* passes.c (rest_of_decl_compilation): Substitute TLS control vars for 
> the master.
>
> 	* varasm.c (decl_needs_tls_emulation_p): New.
> 	(get_emutls_init_templ_addr): Adjust DECL_PRESERVE_P and DECL_INITIAL.
> 	(emutls_decl): Copy TREE_ADDRESSABLE, DECL_PRESERVE_P, create an
> 	initializer when one is present on the user-var.
> 	(emutls_common_1): Remove comment.
> 	(emutls_finalize_control_var): Copy TREE_USED, add an initializer for  
> size and
> 	align fields for cases with un-initialized user vars.
> 	(emutls_find_user_var_cb): New.
> 	(emutls_add_base_initializer): New.
> 	(asm_output_bss): Assert not a tls var needing emulation.
> 	(asm_output_aligned_bss): Ditto.
> 	(assemble_variable): Remove control var init. code.  Assert not a tls  
> var needing
> 	emulation.  Provide a trivial initializer for size and align fields if 
> one is not already
> 	set. (do_assemble_alias): Do not handle emutls vars here.
> 	(var_decl_for_asm): New.
> 	(finish_aliases_1): Walk the alias pairs substituting emutls controls  
> for the user
> 	counterparts.
>
> 	* varpool.c (varpool_mark_needed_node): Do not handle TLS substitution 
> here.
> 	(decide_is_variable_needed): Or here.
> 	(varpool_finalize_decl): Handle TLS substitution.
> 	Remove early calls to varpool_assemble_pending_decls().
> 	Check enqueuing of vars after all tests for need are complete.
> 	(varpool_analyze_pending_decls): Do not record references if the  
> initializer is error_mark.
>
> testsuite:
> 	
> 	PR target/44132
> 	* gcc.dg/tls/thr-init-1.c: New.
> 	* gcc.dg/tls/thr-init-2.c: New.
> 	* gcc.dg/torture/tls New.
> 	* gcc.dg/torture/tls/tls-test.c: New.
> 	* gcc.dg/torture/tls/thr-init-1.c: New.
> 	* gcc.dg/torture/tls/tls.exp: New.
> 	* gcc.dg/torture/tls/thr-init-2.c: New.
>
> 	* lib/target-supports.exp (check_effective_target_tls_emulated): New.
> 	* gcc.dg/tls/thr-cse-1.c: New.
>
>

> Index: gcc/expr.c
> ===================================================================
> --- gcc/expr.c	(revision 161974)
> +++ gcc/expr.c	(working copy)
> @@ -6828,21 +6828,7 @@ highest_pow2_factor_for_target (const_tree target,
>  
>    return MAX (factor, talign);
>  }
> -\f
> -/* 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 (UNKNOWN_LOCATION, fn, arglist);
> -  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
> -}
> -\f
> -
>  /* Subroutine of expand_expr.  Expand the two operands of a binary
>     expression EXP0 and EXP1 placing the results in OP0 and OP1.
>     The value may be stored in TARGET if TARGET is nonzero.  The
> @@ -6946,17 +6932,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enu
>        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
> @@ -8394,16 +8369,6 @@ expand_expr_real_1 (tree exp, rtx target, enum mac
>  	  && (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_loc (loc, emutls_var_address (exp));
> -	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
> -	}
> -
>        /* ... fall through ...  */
>  
>      case FUNCTION_DECL:
> Index: gcc/gimplify.c
> ===================================================================
> --- gcc/gimplify.c	(revision 161974)
> +++ gcc/gimplify.c	(working copy)
> @@ -1339,7 +1339,19 @@ gimplify_vla_decl (tree decl, gimple_seq *seq_p)
>    gimplify_ctxp->save_stack = true;
>  }
>  
> +/* 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 (UNKNOWN_LOCATION, fn, arglist);
> +  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
> +}
> +
>  /* Gimplifies a DECL_EXPR node *STMT_P by making any necessary allocation
>     and initialization explicit.  */
>  
> @@ -1354,6 +1366,18 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_
>    if (TREE_TYPE (decl) == error_mark_node)
>      return GS_ERROR;
>  
> +  /* TLS emulation hook - replace __thread VAR's &VAR with
> +     __emutls_get_address (&_emutls.VAR). We then ignore the original
> +     var.  */
> +  if (! targetm.have_tls
> +      && TREE_CODE (decl) == VAR_DECL
> +      && DECL_THREAD_LOCAL_P (decl))
> +    {
> +      stmt = build_fold_indirect_ref (emutls_var_address (decl));
> +      gimplify_and_add (stmt, seq_p);
> +      return GS_ALL_DONE;
> +    }
> +
>    if ((TREE_CODE (decl) == TYPE_DECL
>         || TREE_CODE (decl) == VAR_DECL)
>        && !TYPE_SIZES_GIMPLIFIED (TREE_TYPE (decl)))
> @@ -1873,6 +1897,17 @@ gimplify_var_or_parm_decl (tree *expr_p)
>        return GS_ERROR;
>      }
>  
> +  /* TLS emulation hook - replace __thread VAR's &VAR with
> +     __emutls_get_address (&_emutls.VAR).  */
> +  if (! targetm.have_tls
> +      && TREE_CODE (decl) == VAR_DECL
> +      && DECL_THREAD_LOCAL_P (decl))
> +    {
> +      gcc_assert (!DECL_HAS_VALUE_EXPR_P (decl));
> +      *expr_p = build_fold_indirect_ref (emutls_var_address (decl));
> +      return GS_OK;
> +    }
> +
>    /* When within an OpenMP context, notice uses of variables.  */
>    if (gimplify_omp_ctxp && omp_notice_variable (gimplify_omp_ctxp, decl, true))
>      return GS_ALL_DONE;
> @@ -5553,14 +5588,15 @@ omp_notice_variable (struct gimplify_omp_ctx *ctx,
>    /* Threadprivate variables are predetermined.  */
>    if (is_global_var (decl))
>      {
> -      if (DECL_THREAD_LOCAL_P (decl))
> +      if (DECL_TLS_MODEL (decl) != TLS_MODEL_NONE)
>  	return omp_notice_threadprivate_variable (ctx, decl, NULL_TREE);
>  
>        if (DECL_HAS_VALUE_EXPR_P (decl))
>  	{
>  	  tree value = get_base_address (DECL_VALUE_EXPR (decl));
>  
> -	  if (value && DECL_P (value) && DECL_THREAD_LOCAL_P (value))
> +	  if (value && DECL_P (value) 
> +	      && (DECL_TLS_MODEL (value) != TLS_MODEL_NONE))
>  	    return omp_notice_threadprivate_variable (ctx, decl, value);
>  	}
>      }
> Index: gcc/varasm.c
> ===================================================================
> --- gcc/varasm.c	(revision 161974)
> +++ gcc/varasm.c	(working copy)
> @@ -204,6 +204,14 @@ static GTY (()) tree emutls_object_type;
>  # define EMUTLS_SEPARATOR	"_"
>  #endif
>  
> +static int
> +decl_needs_tls_emulation_p (tree decl)
> +{
> +  return !targetm.have_tls 
> +	  && TREE_CODE (decl) == VAR_DECL 
> +	  && DECL_THREAD_LOCAL_P (decl);
> +}
> +
>  /* Create an IDENTIFIER_NODE by prefixing PREFIX to the
>     IDENTIFIER_NODE NAME's name.  */
>  
> @@ -322,7 +330,7 @@ get_emutls_init_templ_addr (tree decl)
>    DECL_IGNORED_P (to) = 1;
>    DECL_CONTEXT (to) = DECL_CONTEXT (decl);
>    DECL_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
> -  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
> +  DECL_PRESERVE_P (to) = 1;
>  
>    DECL_WEAK (to) = DECL_WEAK (decl);
>    if (DECL_ONE_ONLY (decl))
> @@ -337,7 +345,6 @@ get_emutls_init_templ_addr (tree decl)
>  
>    DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
>    DECL_INITIAL (to) = DECL_INITIAL (decl);
> -  DECL_INITIAL (decl) = NULL;
>  
>    varpool_finalize_decl (to);
>    return build_fold_addr_expr (to);
> @@ -388,8 +395,7 @@ emutls_decl (tree decl)
>        DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
>        DECL_ARTIFICIAL (to) = 1;
>        DECL_IGNORED_P (to) = 1;
> -      /* FIXME: work around PR44132.  */
> -      DECL_PRESERVE_P (to) = 1;
> +
>        TREE_READONLY (to) = 0;
>        SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
>        if (DECL_ONE_ONLY (decl))
> @@ -413,18 +419,42 @@ emutls_decl (tree decl)
>    TREE_STATIC (to) = TREE_STATIC (decl);
>    TREE_USED (to) = TREE_USED (decl);
>    TREE_PUBLIC (to) = TREE_PUBLIC (decl);
> +  TREE_ADDRESSABLE (to) = TREE_ADDRESSABLE (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);
>    DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
> +  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
>    
>    /* Fortran might pass this to us.  */
>    DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
> +  
> +  /* As soon as we see an initializer (and providing one is not already
> +     present) we can setup the init. template.  */
> +  if (!DECL_INITIAL (to) 
> +       && DECL_INITIAL (decl) 
> +       && DECL_INITIAL (decl) != error_mark_node 
> +       && !DECL_EXTERNAL (to) 
> +       && !DECL_COMMON (to))
> +    {
> +      DECL_INITIAL (to) = targetm.emutls.var_init
> +			  (to, decl, get_emutls_init_templ_addr (decl));
>  
> +      /* Make sure the template is marked as needed early enough.
> +	 Without this, if the variable is placed in a
> +	 section-anchored block, the template will only be marked
> +	 when it's too late.*/
> +      record_references_in_initializer (to, false);
> +    }
> +
> +  /* Say we are not interested in emitting this Var.  */
> +  TREE_ASM_WRITTEN (decl) = 1;
>    return to;
>  }
>  
> +/* Add static constructors for emutls vars, where required.  */
> +
>  static int
>  emutls_common_1 (void **loc, void *xstmts)
>  {
> @@ -439,10 +469,6 @@ emutls_common_1 (void **loc, void *xstmts)
>  
>    word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
>  
> -  /* The idea was to call get_emutls_init_templ_addr here, but if we
> -     do this and there is an initializer, -fanchor_section loses,
> -     because it would be too late to ensure the template is
> -     output.  */
>    x = null_pointer_node;
>    args = tree_cons (NULL, x, NULL);
>    x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
> @@ -469,8 +495,22 @@ emutls_finalize_control_var (void **loc,
>    if (h != NULL) 
>      {
>        struct varpool_node *node = varpool_node (h->to);
> +      if (TREE_USED (h->base.from)) 
> +	TREE_USED (h->to) = 1;
> +
> +      /* We must ensure that the size and align fields are initialized
> +         in control vars, even when the value is not.  */
> +      if ((!DECL_INITIAL (h->base.from)
> +            || DECL_INITIAL (h->base.from) == error_mark_node)
> +	    && !DECL_COMMON (h->base.from)
> +	    && !DECL_EXTERNAL (h->base.from))
> +	{
> +	  DECL_INITIAL (h->to) = targetm.emutls.var_init
> +			(h->to, h->base.from, null_pointer_node);
> +	}
> +
>        /* Because varpool_finalize_decl () has side-effects,
> -         only apply to un-finalized vars.  */
> +         only call it for un-finalized vars.  */
>        if (node && !node->finalized) 
>  	varpool_finalize_decl (h->to);
>      }
> @@ -500,6 +540,39 @@ emutls_finish (void)
>      }
>  }
>  
> +/* Callback to check an htab entry against a supplied control var and
> +   subsitute the original user var if they match.  */
> +static int
> +emutls_find_user_var_cb (void **loc, void *uvar)
> +{
> +  struct tree_map *h = *(struct tree_map **) loc;
> +  if (h != NULL
> +      && h->to == *((tree *)uvar))
> +    {
> +       *((tree *)uvar) = h->base.from;
> +       return 0;
> +    }
> +  return 1;
> +}
> +
> +/* In the case of uninitialized global vars, we have to find our way back
> +   to the user-var in order to build a trivial initializer for the size
> +   and align fields.  */
> +
> +static void
> +emutls_add_base_initializer (tree control)
> +{
> +  tree uvar = control;
> +  if (emutls_htab == NULL)
> +    return;
> +  htab_traverse_noresize (emutls_htab, emutls_find_user_var_cb, &uvar);
> +  /* If we didn't find the user var from the control, something is broken.  */
> +  gcc_assert (uvar != control);
> +  DECL_INITIAL (control) = targetm.emutls.var_init
> +			(control, uvar, null_pointer_node);
> +}
> +
> +
>  /* Helper routines for maintaining section_htab.  */
>  
>  static int
> @@ -792,6 +865,8 @@ asm_output_bss (FILE *file, tree decl ATTRIBUTE_UN
>    gcc_assert (strcmp (XSTR (XEXP (DECL_RTL (decl), 0), 0), name) == 0);
>    targetm.asm_out.globalize_decl_name (file, decl);
>    switch_to_section (bss_section);
> +  /* We don't emit the userland vars for emulated TLS, just the control.  */
> +  gcc_assert (!decl_needs_tls_emulation_p (decl));
>  #ifdef ASM_DECLARE_OBJECT_NAME
>    last_assemble_variable_decl = decl;
>    ASM_DECLARE_OBJECT_NAME (file, name, decl);
> @@ -818,6 +893,8 @@ asm_output_aligned_bss (FILE *file, tree decl ATTR
>  {
>    switch_to_section (bss_section);
>    ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
> +  /* We don't emit the userland vars for emulated TLS, just the control.  */
> +  gcc_assert (!decl_needs_tls_emulation_p (decl));
>  #ifdef ASM_DECLARE_OBJECT_NAME
>    last_assemble_variable_decl = decl;
>    ASM_DECLARE_OBJECT_NAME (file, name, decl);
> @@ -1233,7 +1310,10 @@ get_variable_section (tree decl, bool prefer_noswi
>    if (IN_NAMED_SECTION (decl))
>      return get_named_section (decl, NULL, reloc);
>  
> -  if (ADDR_SPACE_GENERIC_P (as)
> +  /* This should not be bss for an emulated TLS object.  */
> +  if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED)
> +    ;
> +  else if (ADDR_SPACE_GENERIC_P (as)
>        && !DECL_THREAD_LOCAL_P (decl)
>        && !(prefer_noswitch_p && targetm.have_switchable_bss_sections)
>        && bss_initializer_p (decl))
> @@ -2153,35 +2233,6 @@ assemble_variable (tree decl, int top_level ATTRIB
>    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)
> -	      || (DECL_INITIAL (decl)
> -		  && DECL_INITIAL (decl) != error_mark_node)))
> -	{
> -	  DECL_INITIAL (to) = targetm.emutls.var_init
> -	    (to, decl, get_emutls_init_templ_addr (decl));
> -
> -	  /* Make sure the template is marked as needed early enough.
> -	     Without this, if the variable is placed in a
> -	     section-anchored block, the template will only be marked
> -	     when it's too late.  */
> -	  record_references_in_initializer (to, false);
> -	}
> -
> -      decl = to;
> -    }
> -
>    last_assemble_variable_decl = 0;
>  
>    /* Normally no need to say anything here for external references,
> @@ -2238,6 +2289,19 @@ assemble_variable (tree decl, int top_level ATTRIB
>    if (flag_syntax_only)
>      return;
>  
> +  /* We don't emit the userland vars for emulated TLS - they should never
> +     get to here, only the control vars should be emitted.  */
> +  gcc_assert (! decl_needs_tls_emulation_p (decl));
> +  
> +  /* However, for the emutls control vars must ensure that the size and
> +     align fields are initialized, even if the value is not.  */
> +  if (!targetm.have_tls 
> +      && TREE_CODE (decl) == VAR_DECL
> +      && DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
> +      && !DECL_INITIAL (decl)
> +      && !DECL_COMMON (decl))
> +    emutls_add_base_initializer (decl);
> +
>    if (! dont_output_data
>        && ! host_integerp (DECL_SIZE_UNIT (decl), 1))
>      {
> @@ -5701,18 +5765,12 @@ do_assemble_alias (tree decl, tree target)
>    TREE_ASM_WRITTEN (decl) = 1;
>    TREE_ASM_WRITTEN (DECL_ASSEMBLER_NAME (decl)) = 1;
>  
> +  gcc_assert (! decl_needs_tls_emulation_p (decl));
> +
>    if (lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)))
>      {
>        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);
>  
> @@ -5731,14 +5789,6 @@ do_assemble_alias (tree decl, tree target)
>        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.  */
>  
> @@ -5820,6 +5870,15 @@ remove_unreachable_alias_pairs (void)
>      }
>  }
>  
> +/* Lookup the decl for a symbol in the varpool.  */
> +static tree
> +var_decl_for_asm (tree symbol)
> +{
> +  struct varpool_node *vnode = varpool_node_for_asm  (symbol);
> +  if (vnode) 
> +    return vnode->decl;
> +  return NULL;
> +}
>  
>  /* First pass of completing pending aliases.  Make sure that cgraph knows
>     which symbols will be required.  */
> @@ -5832,8 +5891,55 @@ finish_aliases_1 (void)
>  
>    for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); i++)
>      {
> -      tree target_decl;
> +      tree target_decl=NULL;
>  
> +      /* When emulated TLS is in effect, redirect aliases so that they 
> +         are registered between the control vars.  */
> +      if (!targetm.have_tls 
> +          && TREE_CODE (p->decl) == VAR_DECL
> +          && DECL_TLS_MODEL (p->decl) != TLS_MODEL_NONE)
> +	{
> +	  tree tsym = p->target ;
> +	  target_decl = var_decl_for_asm (tsym) ;
> +	  if (!target_decl) 
> +	    {
> +	      /* If we didn't find the user's symbol, it could
> +	         be because the alias really refers to a control 
> +	         var.  */
> +	      tsym = get_emutls_object_name (p->target);
> +	      target_decl = var_decl_for_asm (tsym);
> +	    }
> +	  if (target_decl) 
> +	    {
> +	      struct varpool_node *vnode;
> +	      /* If it hasn't been done already, substitute the control
> +	         var for the original.  */
> +	      if (DECL_THREAD_LOCAL_P (p->decl))
> +		p->decl = emutls_decl (p->decl);
> +	      /* If not TLS target, we've made a mistake.  */
> +	      if (DECL_TLS_MODEL (target_decl) < TLS_MODEL_EMULATED)
> +		error ("TLS symbol %q+D aliased to non-TLS symbol %qE",
> +			p->decl, p->target);
> +	      /* If it's the original we need to substitute the contol.  */
> +	      else if (DECL_THREAD_LOCAL_P (target_decl))
> +		{
> +		  target_decl = emutls_decl (target_decl);
> +		  tsym = get_emutls_object_name (p->target);
> +		}
> +	      /* else it's already the emulation control.  */
> +	      /* Mark the var needed.  */
> +	      vnode = varpool_node (target_decl);
> +	      if (vnode) 
> +	        {
> +		  varpool_mark_needed_node (vnode);
> +		  vnode->force_output = 1;
> +	        }
> +	      p->target = tsym;
> +	    }
> +	  /* Else we didn't find a decl for the symbol, which is an error
> +	     unless there's a weak ref.  */
> +	} 
> +      else
>        target_decl = find_decl_and_mark_needed (p->decl, p->target);
>        if (target_decl == NULL)
>  	{
> Index: gcc/passes.c
> ===================================================================
> --- gcc/passes.c	(revision 161974)
> +++ gcc/passes.c	(working copy)
> @@ -152,6 +152,16 @@ rest_of_decl_compilation (tree decl,
>  			  int top_level,
>  			  int at_end)
>  {
> +  if (! targetm.have_tls 
> +	&& !in_lto_p 
> +	&& TREE_CODE (decl) == VAR_DECL
> +	&& DECL_THREAD_LOCAL_P (decl))
> +    {
> +      /* Substitute the control var. for the user one.  */
> +      rest_of_decl_compilation (emutls_decl (decl), top_level, at_end);
> +      return;
> +    }
> +
>    /* We deferred calling assemble_alias so that we could collect
>       other attributes such as visibility.  Emit the alias now.  */
>    {
> Index: gcc/varpool.c
> ===================================================================
> --- gcc/varpool.c	(revision 161974)
> +++ gcc/varpool.c	(working copy)
> @@ -312,6 +312,14 @@ varpool_mark_needed_node (struct varpool_node *nod
>        && !TREE_ASM_WRITTEN (node->decl))
>      varpool_enqueue_needed_node (node);
>    node->needed = 1;
> +  /* If we need the var, and it's an emulated TLS entity, that
> +     means we need the control var.  */
> +  if (!targetm.have_tls && DECL_THREAD_LOCAL_P (node->decl))
> +    {
> +      struct varpool_node *cv_node;
> +      cv_node = varpool_node (emutls_decl (node->decl)) ;
> +      varpool_mark_needed_node (cv_node);
> +    }
>  }
>  
>  /* Reset the queue of needed nodes.  */
> @@ -346,17 +354,6 @@ decide_is_variable_needed (struct varpool_node *no
>        && !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 (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_toplevel_reorder)
> @@ -381,14 +378,31 @@ varpool_finalize_decl (tree decl)
>       or local (in C, has internal linkage).  So do nothing more
>       if this function has already run.  */
>    if (node->finalized)
> +      return;
> +
> +  /* For emulated TLS vars, if we are in a position to finalize the userland
> +     var, then we should be able to finalize the control var too.  */
> +  if (!targetm.have_tls 
> +      && TREE_CODE (decl) == VAR_DECL
> +      && DECL_THREAD_LOCAL_P (decl))
>      {
> -      if (cgraph_global_info_ready)
> -	varpool_assemble_pending_decls ();
> +      tree control = emutls_decl (decl);
> +      /* If we didn't create an initializer in the preceding line,  then we 
> +         must now add a minimal one that sets the size and align fields.  */
> +      if (!DECL_INITIAL (control) 
> +	   && !DECL_COMMON (control)
> +	   && !DECL_EXTERNAL (control))
> +	{
> +	  DECL_INITIAL (control) = targetm.emutls.var_init
> +			(control, decl, null_pointer_node);
> +	  record_references_in_initializer (control, false);
> +	}
> +
> +      varpool_finalize_decl (control) ;
> +      node->finalized = true;    
>        return;
>      }
> -  if (node->needed)
> -    varpool_enqueue_needed_node (node);
> -  node->finalized = true;
> +
>    if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl))
>      node->force_output = true;
>  
> @@ -399,8 +413,11 @@ varpool_finalize_decl (tree decl)
>       there.  */
>    else if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
>      varpool_mark_needed_node (node);
> -  if (cgraph_global_info_ready)
> -    varpool_assemble_pending_decls ();
> +
> +  if (node->needed)
> +    varpool_enqueue_needed_node (node);
> +
> +  node->finalized = true;
>  }
>  
>  /* Return variable availability.  See cgraph.h for description of individual
> @@ -449,7 +466,7 @@ varpool_analyze_pending_decls (void)
>  	     already informed about increased alignment.  */
>            align_variable (decl, 0);
>  	}
> -      if (DECL_INITIAL (decl))
> +      if (DECL_INITIAL (decl) && (DECL_INITIAL (decl) != error_mark_node))
>  	record_references_in_initializer (decl, analyzed);
>        if (node->same_comdat_group)
>  	{

>
>

> Index: gcc/testsuite/lib/target-supports.exp
> ===================================================================
> --- gcc/testsuite/lib/target-supports.exp	(revision 161974)
> +++ gcc/testsuite/lib/target-supports.exp	(working copy)
> @@ -596,6 +596,23 @@ proc check_effective_target_tls_native {} {
>      }]
>  }
>  
> +# Return 1 if *emulated* thread local storage (TLS) is supported, 0 otherwise.
> +
> +proc check_effective_target_tls_emulated {} {
> +    # VxWorks uses emulated TLS machinery, but with non-standard helper
> +    # functions, so we fail to automatically detect it.
> +    global target_triplet
> +    if { [regexp ".*-.*-vxworks.*" $target_triplet] } {
> +	return 1
> +    }
> +    
> +    return [check_no_messages_and_pattern tls_emulated "emutls" assembly {
> +	__thread int i;
> +	int f (void) { return i; }
> +	void g (int j) { i = j; }
> +    }]
> +}
> +
>  # Return 1 if TLS executables can run correctly, 0 otherwise.
>  
>  proc check_effective_target_tls_runtime {} {
> Index: gcc/testsuite/gcc.dg/tls/thr-cse-1.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/tls/thr-cse-1.c	(revision 0)
> +++ gcc/testsuite/gcc.dg/tls/thr-cse-1.c	(revision 0)
> @@ -0,0 +1,20 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O1" } */
> +/* { dg-require-effective-target tls_emulated } */
> +
> +/* Test that we only get one call to emutls_get_address when CSE is
> +   active.  Note that the var _must_ be initialized for the scan asm
> +   to work, since otherwise there will be an initializer which will,
> +   correctly, call emutls_get_address.  */
> +int foo (int b, int c, int d)
> +{
> +  static __thread int a=1;
> +  a += b;
> +  a -= c;
> +  a += d;
> +  return a;
> +}
> +
> +/* { dg-final { scan-assembler-not "emutls_get_address.*emutls_get_address.*" { target { ! *-wrs-vxworks } } } } */
> +/* { dg-final { scan-assembler-not "tls_lookup.*tls_lookup.*" { target *-wrs-vxworks } } } */
> +
> Index: gcc/testsuite/gcc.dg/tls/thr-init-1.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/tls/thr-init-1.c	(revision 0)
> +++ gcc/testsuite/gcc.dg/tls/thr-init-1.c	(revision 0)
> @@ -0,0 +1,8 @@
> +/* { dg-require-effective-target tls } */
> +/* { dg-do compile } */
> +
> +static __thread int fstat ;
> +static __thread int fstat = 1 ;
> +static __thread int fstat ;
> +static __thread int fstat = 2; /* { dg-error "redefinition of 'fstat'" } */
> +				/* { dg-message "note: previous definition of 'fstat' was here" "" { target *-*-* } 5 } */
> Index: gcc/testsuite/gcc.dg/tls/thr-init-2.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/tls/thr-init-2.c	(revision 0)
> +++ gcc/testsuite/gcc.dg/tls/thr-init-2.c	(revision 0)
> @@ -0,0 +1,23 @@
> +/* { dg-require-effective-target tls } */
> +/* { dg-do run } */
> +
> +extern void abort() ;
> +
> +static __thread int fstat ;
> +static __thread int fstat = 1;
> +
> +int test_code(int b)
> +{
> +  fstat += b ;
> +  return fstat;
> +}
> +
> +int main (int ac, char *av[])
> +{
> +  int a = test_code(1);
> +  
> +  if ((a != 2) || (fstat != 2))
> +    abort () ;
> +  
> +  return 0;
> +}
> Index: gcc/testsuite/gcc.dg/torture/tls/tls-test.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/torture/tls/tls-test.c	(revision 0)
> +++ gcc/testsuite/gcc.dg/torture/tls/tls-test.c	(revision 0)
> @@ -0,0 +1,52 @@
> +/* { dg-do run }  */
> +/* { dg-require-effective-target tls  }  */
> +/* { dg-require-effective-target pthread } */
> +/* { dg-options "-pthread" } */
> +
> +#include <pthread.h>
> +extern int printf (char *,...);
> +__thread int a = 5; 
> +int *volatile a_in_other_thread = (int *) 12345;
> +
> +static void *
> +thread_func (void *arg)
> +{
> +  a_in_other_thread = &a;
> +  a+=5;
> +  *((int *) arg) = a;
> +  return (void *)0;
> +}
> +
> +int
> +main ()
> +{
> +  pthread_t thread;
> +  void *thread_retval;
> +  int *volatile a_in_main_thread;
> +  int *volatile again ;
> +  int thr_a;
> +
> +  a_in_main_thread = &a;
> +
> +  if (pthread_create (&thread, (pthread_attr_t *)0, thread_func, &thr_a))
> +    return 0;
> +
> +  if (pthread_join (thread, &thread_retval))
> +    return 0;
> +
> +  again = &a;
> +  if (again != a_in_main_thread)
> +    {
> +      printf ("FAIL: main thread addy changed from 0x%0x to 0x%0x\n", 
> +		a_in_other_thread, again);
> +      return 1;
> +    }
> +
> +  if (a != 5 || thr_a != 10 || (a_in_other_thread == a_in_main_thread))
> +    {
> +      printf ("FAIL: a= %d, thr_a = %d Addr = 0x%0x\n", 
> +		a, thr_a, a_in_other_thread);
> +      return 1;
> +    }
> +  return 0;
> +}
> Index: gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c	(revision 0)
> +++ gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c	(revision 0)
> @@ -0,0 +1,25 @@
> +/* { dg-do run } */
> +/* { dg-require-effective-target tls } */
> +
> +extern int printf (char *,...);
> +extern void abort() ;
> +
> +int test_code(int b)
> +{
> +static __thread int fstat = 1;
> +  fstat += b ;
> +  return fstat;
> +}
> +
> +int main (int ac, char *av[])
> +{
> +  int a = test_code(1);
> +  
> +  if ( a != 2 )
> +    {
> +      printf ("a=%d\n", a) ;
> +      abort ();
> +    }
> +  
> +  return 0;
> +}
> Index: gcc/testsuite/gcc.dg/torture/tls/tls.exp
> ===================================================================
> --- gcc/testsuite/gcc.dg/torture/tls/tls.exp	(revision 0)
> +++ gcc/testsuite/gcc.dg/torture/tls/tls.exp	(revision 0)
> @@ -0,0 +1,36 @@
> +#   Copyright (C) 2010 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +# 
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +# 
> +# You should have received a copy of the GNU General Public License
> +# along with GCC; see the file COPYING3.  If not see
> +# <http://www.gnu.org/licenses/>.
> +
> +# GCC testsuite that uses the `dg.exp' driver.
> +
> +# Load support procs.
> +load_lib gcc-dg.exp
> +
> +# If a testcase doesn't have special options, use these.
> +global DEFAULT_CFLAGS
> +if ![info exists DEFAULT_CFLAGS] then {
> +    set DEFAULT_CFLAGS " -ansi -pedantic-errors"
> +}
> +
> +# Initialize `dg'.
> +dg-init
> +
> +# Main loop.
> +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
> +        $DEFAULT_CFLAGS
> +
> +# All done.
> +dg-finish
> Index: gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c	(revision 0)
> +++ gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c	(revision 0)
> @@ -0,0 +1,28 @@
> +/* { dg-do run } */
> +/* { dg-require-effective-target tls } */
> +
> +extern int printf (char *,...);
> +extern void abort() ;
> +
> +static __thread int fstat ;
> +static __thread int fstat = 1;
> +static __thread int fstat ;
> +
> +int test_code(int b)
> +{
> +  fstat += b ;
> +  return fstat;
> +}
> +
> +int main (int ac, char *av[])
> +{
> +  int a = test_code(1);
> +  
> +  if ( a != 2 || fstat != 2 )
> +    {
> +    printf ("a=%d fstat=%d\n", a, fstat) ;
> +    abort ();
> +    }
> +  
> +  return 0;
> +}

>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-09 12:11       ` IainS
  2010-07-12 14:48         ` Jack Howarth
@ 2010-07-12 15:18         ` Richard Henderson
  2010-07-12 17:04           ` Richard Henderson
  1 sibling, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-12 15:18 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 669 bytes --]

On 07/09/2010 05:10 AM, IainS wrote:
> 1/ a pass behind a gate of ! targetm.have_tls is a whole lot less
> intrusive to non-emutls targets than what we have
> 2/ it's likely more efficient for emutls targets since there's less
> to-ing and fro-ing.
> 3/ probably more maintainable.
> 4/ certainly more transparent.
> 5/ (probably) loses all reference to emutls from varasm, varpool, expr,
> & gimplify..
> 
> mind you, that doesn't mean I know how to write such a pass ...  ;-)

The following almost certainly doesn't work with aliases yet -- there are
too many paths that create them.  But see how far this gets with other 
tests; whether variables get lost etc.


r~

[-- Attachment #2: emutls-1 --]
[-- Type: text/plain, Size: 37899 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a807e8e..555f336 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1352,6 +1352,7 @@ OBJS-common = \
 	tree-diagnostic.o \
 	tree-dump.o \
 	tree-eh.o \
+	tree-emutls.o \
 	tree-if-conv.o \
 	tree-into-ssa.o \
 	tree-iterator.o \
@@ -3135,6 +3136,8 @@ tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
 tree-complex.o : tree-complex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
     $(TM_H) $(FLAGS_H) $(TREE_FLOW_H) $(GIMPLE_H) \
     tree-iterator.h $(TREE_PASS_H) tree-ssa-propagate.h $(DIAGNOSTIC_H)
+tree-emutls.o : tree-emutls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
+    $(GIMPLE_H) $(TREE_PASS_H) $(TARGET_H) gt-tree-emutls.h
 tree-vect-generic.o : tree-vect-generic.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
     $(TM_H) $(TREE_FLOW_H) $(GIMPLE_H) tree-iterator.h $(TREE_PASS_H) \
     $(FLAGS_H) $(OPTABS_H) $(MACHMODE_H) $(EXPR_H) \
@@ -3734,6 +3737,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \
   $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c \
+  $(srcdir)/tree-emutls.c \
   $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \
   $(srcdir)/tree-chrec.h \
   $(srcdir)/tree-scalar-evolution.c \
diff --git a/gcc/expr.c b/gcc/expr.c
index 00ebfdc..03b879c 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6829,20 +6829,6 @@ highest_pow2_factor_for_target (const_tree target, const_tree exp)
   return MAX (factor, talign);
 }
 \f
-/* 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 (UNKNOWN_LOCATION, fn, arglist);
-  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
-}
-\f
-
 /* Subroutine of expand_expr.  Expand the two operands of a binary
    expression EXP0 and EXP1 placing the results in OP0 and OP1.
    The value may be stored in TARGET if TARGET is nonzero.  The
@@ -6945,18 +6931,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
       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
@@ -8394,16 +8368,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 	  && (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_loc (loc, emutls_var_address (exp));
-	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
-	}
-
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
diff --git a/gcc/passes.c b/gcc/passes.c
index 8828967..ecd45b2 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -746,6 +746,7 @@ init_optimization_passes (void)
   /* Interprocedural optimization passes.  */
   p = &all_small_ipa_passes;
   NEXT_PASS (pass_ipa_free_lang_data);
+  NEXT_PASS (pass_ipa_lower_emutls);
   NEXT_PASS (pass_ipa_function_and_variable_visibility);
   NEXT_PASS (pass_ipa_early_inline);
     {
diff --git a/gcc/tree-emutls.c b/gcc/tree-emutls.c
new file mode 100644
index 0000000..b1b9e4d
--- /dev/null
+++ b/gcc/tree-emutls.c
@@ -0,0 +1,609 @@
+/* Lower TLS operations to emulation functions.
+   Copyright (C) 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
+
+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 3, or (at your option) any
+later version.
+
+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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "tree-flow.h"
+#include "cgraph.h"
+#include "langhooks.h"
+#include "target.h"
+
+/* ??? Should go.  */
+#include "targhooks.h"
+#include "tree-iterator.h"
+#include "output.h"
+
+/* TODO: Get rid of the EMUTLS hooks that no one uses.  */
+
+/* Whenever a target does not support thread-local storage (TLS) natively,
+   we can emulate it with some run-time support in libgcc.  This will in
+   turn rely on "keyed storage" a-la pthread_key_create; essentially all
+   thread libraries provide such functionality.
+
+   In order to coordinate with the libgcc runtime, each TLS variable is
+   described by a "control variable".  This control variable records the
+   required size, alignment, and initial value of the TLS variable for
+   instantiation at runtime.  It also stores an integer token to be used
+   by the runtime to find the address of the variable within each thread.
+
+   On the compiler side, this means that we need to replace all instances
+   of "tls_var" in the code with "*__emutls_get_addr(&control_var)".  We
+   also need to eliminate "tls_var" from the symbol table and introduce
+   "control_var".
+
+   We used to perform all of the transformations during conversion to rtl,
+   and the variable substitutions magically within assemble_variable.
+   However, this late fiddling of the symbol table conflicts with LTO and
+   whole-program compilation.  Therefore we must now make all the changes
+   to the symbol table early in the GIMPLE optimization path, before we
+   write things out to LTO intermediate files.  */
+
+/* These two vectors, once fully populated, are kept in lock-step so that
+   the index of a TLS variable equals the index of its control variable in
+   the other vector.  */
+static GTY (()) varpool_node_set tls_vars;
+static GTY (()) varpool_node_set control_vars;
+
+/* The type of the control structure, shared with the emutls.c runtime.  */
+/* ??? See if we can eliminate the one query via decl_emutls_var_p from
+   varasm.c.  With that gone, this need not be live outside the ipa pass.  */
+static GTY (()) tree emutls_object_type;
+
+/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
+   macro can be used on them to distinguish the control variable from
+   the initialization template.  */
+
+bool
+decl_emutls_var_p(tree decl)
+{
+  return TREE_TYPE (decl) == emutls_object_type;
+}
+
+#if !defined (NO_DOT_IN_LABEL)
+# define EMUTLS_SEPARATOR	"."
+#elif !defined (NO_DOLLAR_IN_LABEL)
+# define EMUTLS_SEPARATOR	"$"
+#else
+# define EMUTLS_SEPARATOR	"_"
+#endif
+
+/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
+   IDENTIFIER_NODE NAME's name.  */
+
+static tree
+prefix_name (const char *prefix, tree name)
+{
+  unsigned plen = strlen (prefix);
+  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
+  char *toname = (char *) alloca (plen + nlen + 1);
+
+  memcpy (toname, prefix, plen);
+  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
+
+  return get_identifier (toname);
+}
+
+/* Create an identifier for the struct __emutls_object, given an identifier
+   of the DECL_ASSEMBLY_NAME of the original object.  */
+
+tree
+get_emutls_object_name (tree name)
+{
+  const char *prefix = (targetm.emutls.var_prefix
+			? targetm.emutls.var_prefix
+			: "__emutls_v" EMUTLS_SEPARATOR);
+  return prefix_name (prefix, name);
+}
+
+tree
+default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
+{
+  tree word_type_node, field, next_field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
+  DECL_CONTEXT (field) = type;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      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 (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__align"),
+		      word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__size"), word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+
+  return field;
+}
+
+/* Initialize emulated tls object TO, which refers to TLS variable
+   DECL and is initialized by PROXY.  */
+
+tree
+default_emutls_var_init (tree to, tree decl, tree proxy)
+{
+  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 = proxy;
+
+  return build_constructor (type, v);
+}
+
+/* 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;
+
+  type = emutls_object_type;
+  if (type)
+    return type;
+
+  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
+  type_name = NULL;
+  field = targetm.emutls.var_fields (type, &type_name);
+  if (!type_name)
+    type_name = get_identifier ("__emutls_object");
+  type_name = build_decl (UNKNOWN_LOCATION,
+			  TYPE_DECL, type_name, type);
+  TYPE_NAME (type) = type_name;
+  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;
+
+  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
+      && !DECL_SECTION_NAME (decl))
+    return null_pointer_node;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
+    {
+      const char *prefix = (targetm.emutls.tmpl_prefix
+			    ? targetm.emutls.tmpl_prefix
+			    : "__emutls_t" EMUTLS_SEPARATOR);
+      name = prefix_name (prefix, name);
+    }
+
+  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  if (DECL_ONE_ONLY (decl))
+    {
+      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_INITIAL (to) = DECL_INITIAL (decl);
+  DECL_INITIAL (decl) = NULL;
+
+  varpool_finalize_decl (to);
+  return build_fold_addr_expr (to);
+}
+
+static tree
+new_emutls_decl (tree decl)
+{
+  tree name, to;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  to = build_decl (DECL_SOURCE_LOCATION (decl), VAR_DECL,
+                   get_emutls_object_name (name),
+                   get_emutls_object_type ());
+
+  SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
+
+  DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
+  DECL_ARTIFICIAL (to) = 1;
+  DECL_IGNORED_P (to) = 1;
+  TREE_READONLY (to) = 0;
+  TREE_STATIC (to) = 1;
+
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+  DECL_CONTEXT (to) = DECL_CONTEXT (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);
+  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
+  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
+
+  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
+
+  if (DECL_ONE_ONLY (decl))
+    make_decl_one_only (to, DECL_ASSEMBLER_NAME (to));
+
+  /* ??? What in the world is this for?  */
+  if (targetm.emutls.var_align_fixed)
+    /* If we're not allowed to change the proxy object's
+       alignment, pretend it's been set by the user.  */
+    DECL_USER_ALIGN (to) = 1;
+
+  /* If this variable is defined locally, then we need to initialize the
+     control structure with size and alignment information.  Initialization
+     of COMMON block variables happens elsewhere via a constructor.  */
+  if (!DECL_EXTERNAL (to)
+      && (!DECL_COMMON (to)
+          || (DECL_INITIAL (decl)
+              && DECL_INITIAL (decl) != error_mark_node)))
+    {
+      tree tmpl = get_emutls_init_templ_addr (decl);
+      DECL_INITIAL (to) = targetm.emutls.var_init (to, decl, tmpl);
+      record_references_in_initializer (to, false);
+    }
+
+  varpool_finalize_decl (to);
+  return to;
+}
+
+tree
+emutls_decl (tree decl)
+{
+  varpool_node_set_iterator i;
+  struct varpool_node *var;
+  
+  i = varpool_node_set_find (tls_vars, varpool_get_node (decl));
+  if (i.index == ~0u)
+    return NULL;
+
+  var = VEC_index (varpool_node_ptr, control_vars->nodes, i.index);
+  return var->decl;
+}
+
+static void
+emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts)
+{
+  tree args, x;
+  tree word_type_node;
+
+  if (! DECL_COMMON (tls_decl)
+      || (DECL_INITIAL (tls_decl)
+	  && DECL_INITIAL (tls_decl) != error_mark_node))
+    return;
+
+  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+
+  /* ??? The idea was to call get_emutls_init_templ_addr here, but if we
+     do this and there is an initializer, -fanchor_section loses, because
+     it would be too late to ensure the template is output.  */
+  /* ??? Except that if we *can* do this during the IPA pass, then it
+     isn't too late.  */
+  x = null_pointer_node;
+  args = tree_cons (NULL, x, NULL);
+  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = fold_convert (word_type_node, DECL_SIZE_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = build_fold_addr_expr (control_decl);
+  args = tree_cons (NULL, x, args);
+
+  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
+  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);
+
+  append_to_statement_list (x, pstmts);
+}
+
+/* Finalize emutls control vars and add a static constructor if required.  */
+/* ??? Can this be done as early as the IPA pass?  */
+
+void
+emutls_finish (void)
+{
+  tree body = NULL_TREE;
+  unsigned int i, n;
+
+  n = VEC_length (varpool_node_ptr, tls_vars->nodes);
+  for (i = 0; i < n; ++i)
+    emutls_common_1 (VEC_index (varpool_node_ptr, tls_vars->nodes, i)->decl,
+		     VEC_index (varpool_node_ptr, control_vars->nodes, i)->decl,
+                     &body);
+
+  if (body == NULL_TREE)
+    return;
+
+  cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY);
+}
+
+\f
+
+struct lower_emutls_data
+{
+  struct cgraph_node *cfun_node;
+  struct cgraph_node *builtin_node;
+  tree builtin_decl;
+  basic_block bb;
+  int bb_freq;
+  gimple_stmt_iterator gsi;
+  gimple stmt;
+};
+
+/* Given an operand *PTR within STMT at *GSI, if the operand references
+   a TLS variable, then lower the reference to a call to the runtime.  */
+
+static bool
+lower_emutls_1 (struct lower_emutls_data *d, tree *ptr)
+{
+  tree t = *ptr;
+  tree control, addr;
+  gimple x;
+
+  /* In the case of "&var" we don't want to generate "&*addr",
+     we'd prefer to simply emit "addr".  */
+  if (TREE_CODE (t) == ADDR_EXPR)
+    {
+      if (lower_emutls_1 (d, &TREE_OPERAND (t, 0)))
+	{
+	  if (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
+	      && integer_zerop (TREE_OPERAND (TREE_OPERAND (t, 0), 1)))
+	    *ptr = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+	  return true;
+	}
+      return false;
+    }
+
+  /* Look through all components.  */
+  while (handled_component_p (t))
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* If at the end we don't have a TLS variable, nothing to do.  */
+  if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t))
+    return false;
+
+  /* Compute the address of the TLS variable with help from runtime.  */
+  control = emutls_decl (t);
+  TREE_ADDRESSABLE (control) = 1;
+  addr = create_tmp_var (build_pointer_type (TREE_TYPE (t)), NULL);
+  x = gimple_build_call (d->builtin_decl, 1,
+	                 build_fold_addr_expr (control));
+  gimple_call_set_lhs (x, addr);
+  gsi_insert_before (&d->gsi, x, GSI_SAME_STMT);
+  gimple_set_location (x, gimple_location (d->stmt));
+
+  cgraph_create_edge (d->cfun_node, d->builtin_node, x,
+                      d->bb->count, d->bb_freq, d->bb->loop_depth);
+
+  /* Replace "var" with "*addr" in the statement.  */
+  t = build2 (MEM_REF, TREE_TYPE (t), addr,
+	      build_int_cst (TREE_TYPE (addr), 0));
+  *ptr = t;
+  gimple_set_modified (d->stmt, true);
+
+  return true;
+}
+
+static void
+lower_emutls_function_body (struct cgraph_node *node)
+{
+  struct lower_emutls_data d;
+
+  current_function_decl = node->decl;
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+
+  d.cfun_node = node;
+  d.builtin_decl = built_in_decls[BUILT_IN_EMUTLS_GET_ADDRESS];
+  d.builtin_node = cgraph_node (d.builtin_decl);
+
+  FOR_EACH_BB (d.bb)
+    {
+      d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb);
+
+      for (d.gsi = gsi_start_bb (d.bb); !gsi_end_p (d.gsi); gsi_next (&d.gsi))
+	{
+	  gimple stmt = gsi_stmt (d.gsi);
+
+          d.stmt = stmt;
+	  if (gimple_assign_single_p (stmt))
+	    {
+	      lower_emutls_1 (&d, gimple_assign_lhs_ptr (stmt));
+	      lower_emutls_1 (&d, gimple_assign_rhs1_ptr (stmt));
+	    }
+	  else if (is_gimple_call (stmt))
+	    {
+	      unsigned i, n = gimple_call_num_args (stmt);
+	      for (i = 0; i < n; ++i)
+		lower_emutls_1 (&d, gimple_call_arg_ptr (stmt, i));
+	      lower_emutls_1 (&d, gimple_call_lhs_ptr (stmt));
+	    }
+	  else
+	    continue;
+
+	  update_stmt_if_modified (stmt);
+	}
+    }
+
+  pop_cfun ();
+  current_function_decl = NULL;
+}
+
+static unsigned int
+ipa_lower_emutls (void)
+{
+  struct varpool_node *var;
+  struct cgraph_node *func;
+  bool any_aliases = false;
+  unsigned int i;
+
+  tls_vars = varpool_node_set_new ();
+
+  /* Examine all global variables for TLS variables.  */
+  for (var = varpool_nodes; var ; var = var->next)
+    if (DECL_THREAD_LOCAL_P (var->decl))
+      {
+	gcc_checking_assert (TREE_STATIC (var->decl));
+        if (var->alias)
+	  any_aliases = true;
+        else
+	  varpool_node_set_add (tls_vars, var);
+      }
+
+  /* If we found no TLS variables, then there is no further work to do.  */
+  if (tls_vars->nodes == NULL)
+    {
+      tls_vars = NULL;
+      if (dump_file)
+	fprintf (dump_file, "No TLS variables found.\n");
+      return 0;
+    }
+
+  /* If there were any aliases, add them to the set last.  In this way
+     when we create the control variables the control variable for the
+     alias base will have been created first.  */
+  if (any_aliases)
+    for (var = varpool_nodes; var ; var = var->next)
+      if (DECL_THREAD_LOCAL_P (var->decl) && var->alias)
+        varpool_node_set_add (tls_vars, var);
+
+  /* Create the control variables for each TLS variable.  */
+  control_vars = varpool_node_set_new ();
+  for (i = 0; VEC_iterate (varpool_node_ptr, tls_vars->nodes, i, var); ++i)
+    {
+      tree cdecl;
+      struct varpool_node *cvar;
+
+      var = VEC_index (varpool_node_ptr, tls_vars->nodes, i);
+      cdecl = new_emutls_decl (var->decl);
+
+      /* If there was an alias between the TLS variables,
+         mirror that in the control variables.  */
+      /* ??? There appears to be an alias_pairs data structure that
+         holds similar information.  */
+      if (var->alias)
+        {
+	  tree cbase = emutls_decl (var->extra_name->decl);
+          bool ok = varpool_extra_name_alias (cdecl, cbase);
+          gcc_assert (ok);
+        }
+
+      cvar = varpool_get_node (cdecl);
+      varpool_node_set_add (control_vars, cvar);
+
+      /* Indicate that the value of the TLS variable may be found elsewhere.
+	 This also prevents the variable from re-appearing in the GIMPLE.  */
+      /* ??? Unfortuantely, there's no decent actual value to put here;
+	 there's nothing we can emit for the debugger at the moment.  */
+      SET_DECL_VALUE_EXPR (var->decl, error_mark_node);
+      DECL_HAS_VALUE_EXPR_P (var->decl) = 1;
+    }
+
+  /* Adjust all uses of TLS variables within the function bodies.  */
+  for (func = cgraph_nodes; func; func = func->next)
+    if (func->reachable && func->lowered)
+      lower_emutls_function_body (func);
+
+  return TODO_dump_func | TODO_ggc_collect | TODO_verify_stmts;
+}
+
+/* If the target supports TLS natively, we need do nothing here.  */
+
+static bool
+gate_emutls (void)
+{
+  return !targetm.have_tls;
+}
+
+struct simple_ipa_opt_pass pass_ipa_lower_emutls =
+{
+ {
+  SIMPLE_IPA_PASS,
+  "emutls",				/* name */
+  gate_emutls,				/* gate */
+  ipa_lower_emutls,			/* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_NONE,				/* tv_id */
+  PROP_cfg,				/* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  0,					/* todo_flags_finish */
+ }
+};
+
+#include "gt-tree-emutls.h"
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a4c97b3..b309be8 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -445,6 +445,7 @@ extern struct gimple_opt_pass pass_warn_unused_result;
 extern struct gimple_opt_pass pass_split_functions;
 
 /* IPA Passes */
+extern struct simple_ipa_opt_pass pass_ipa_lower_emutls;
 extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility;
 extern struct simple_ipa_opt_pass pass_ipa_early_inline;
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 960ee7d..28bc47a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5257,9 +5257,13 @@ extern void set_user_assembler_name (tree, const char *);
 extern void process_pending_assemble_externals (void);
 extern void finish_aliases_1 (void);
 extern void finish_aliases_2 (void);
-extern tree emutls_decl (tree);
 extern void remove_unreachable_alias_pairs (void);
 
+/* tree-emutls.c */
+extern tree emutls_decl (tree);
+extern tree get_emutls_object_name (tree);
+extern bool decl_emutls_var_p (tree);
+
 /* In stmt.c */
 extern void expand_computed_goto (tree);
 extern bool parse_output_constraint (const char **, int, int, int,
diff --git a/gcc/varasm.c b/gcc/varasm.c
index de78bd0..f15ef0f 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -186,320 +186,6 @@ 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;
-/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
-   macro can be used on them to distinguish the control variable from
-   the initialization template.  */
-#define DECL_EMUTLS_VAR_P(D)  (TREE_TYPE (D) == emutls_object_type)
-
-#if !defined (NO_DOT_IN_LABEL)
-# define EMUTLS_SEPARATOR	"."
-#elif !defined (NO_DOLLAR_IN_LABEL)
-# define EMUTLS_SEPARATOR	"$"
-#else
-# define EMUTLS_SEPARATOR	"_"
-#endif
-
-/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
-   IDENTIFIER_NODE NAME's name.  */
-
-static tree
-prefix_name (const char *prefix, tree name)
-{
-  unsigned plen = strlen (prefix);
-  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
-  char *toname = (char *) alloca (plen + nlen + 1);
-
-  memcpy (toname, prefix, plen);
-  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
-
-  return get_identifier (toname);
-}
-
-/* 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)
-{
-  const char *prefix = (targetm.emutls.var_prefix
-			? targetm.emutls.var_prefix
-			: "__emutls_v" EMUTLS_SEPARATOR);
-  return prefix_name (prefix, name);
-}
-
-tree
-default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
-{
-  tree word_type_node, field, next_field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
-  DECL_CONTEXT (field) = type;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      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 (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__align"),
-		      word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__size"), word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-
-  return field;
-}
-
-/* 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;
-
-  type = emutls_object_type;
-  if (type)
-    return type;
-
-  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
-  type_name = NULL;
-  field = targetm.emutls.var_fields (type, &type_name);
-  if (!type_name)
-    type_name = get_identifier ("__emutls_object");
-  type_name = build_decl (UNKNOWN_LOCATION,
-			  TYPE_DECL, type_name, type);
-  TYPE_NAME (type) = type_name;
-  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;
-
-  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
-      && !DECL_SECTION_NAME (decl))
-    return null_pointer_node;
-
-  name = DECL_ASSEMBLER_NAME (decl);
-  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
-    {
-      const char *prefix = (targetm.emutls.tmpl_prefix
-			    ? targetm.emutls.tmpl_prefix
-			    : "__emutls_t" EMUTLS_SEPARATOR);
-      name = prefix_name (prefix, name);
-    }
-
-  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
-  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
-
-  DECL_WEAK (to) = DECL_WEAK (decl);
-  if (DECL_ONE_ONLY (decl))
-    {
-      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  DECL_INITIAL (to) = DECL_INITIAL (decl);
-  DECL_INITIAL (decl) = NULL;
-
-  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 = IDENTIFIER_HASH_VALUE (name);
-  in.base.from = decl;
-  loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT);
-  h = (struct tree_map *) *loc;
-  if (h != NULL)
-    to = h->to;
-  else
-    {
-      to = build_decl (DECL_SOURCE_LOCATION (decl),
-		       VAR_DECL, get_emutls_object_name (name),
-		       get_emutls_object_type ());
-
-      h = ggc_alloc_tree_map ();
-      h->hash = in.hash;
-      h->base.from = decl;
-      h->to = to;
-      *(struct tree_map **) loc = h;
-
-      DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
-      DECL_ARTIFICIAL (to) = 1;
-      DECL_IGNORED_P (to) = 1;
-      /* FIXME: work around PR44132.  */
-      DECL_PRESERVE_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_ASSEMBLER_NAME (to));
-      DECL_CONTEXT (to) = DECL_CONTEXT (decl);
-      if (targetm.emutls.var_align_fixed)
-	/* If we're not allowed to change the proxy object's
-	   alignment, pretend it's been set by the user.  */
-	DECL_USER_ALIGN (to) = 1;
-    }
-
-  /* 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.  */
-  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
-  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
-
-  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);
-  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  
-  /* Fortran might pass this to us.  */
-  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (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->base.from)
-      || (DECL_INITIAL (h->base.from)
-	  && DECL_INITIAL (h->base.from) != error_mark_node))
-    return 1;
-
-  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
-
-  /* The idea was to call get_emutls_init_templ_addr here, but if we
-     do this and there is an initializer, -fanchor_section loses,
-     because it would be too late to ensure the template is
-     output.  */
-  x = null_pointer_node;
-  args = tree_cons (NULL, x, NULL);
-  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
-  args = tree_cons (NULL, x, args);
-  x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->base.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 (UNKNOWN_LOCATION, x, args);
-
-  append_to_statement_list (x, pstmts);
-  return 1;
-}
-
-/* Callback to finalize one emutls control variable.  */
-
-static int
-emutls_finalize_control_var (void **loc, 
-				void *unused ATTRIBUTE_UNUSED)
-{
-  struct tree_map *h = *(struct tree_map **) loc;
-  if (h != NULL) 
-    {
-      struct varpool_node *node = varpool_node (h->to);
-      /* Because varpool_finalize_decl () has side-effects,
-         only apply to un-finalized vars.  */
-      if (node && !node->finalized) 
-	varpool_finalize_decl (h->to);
-    }
-  return 1;
-}
-
-/* Finalize emutls control vars and add a static constructor if
-   required.  */
-
-void
-emutls_finish (void)
-{
-  if (emutls_htab == NULL)
-    return;
-  htab_traverse_noresize (emutls_htab, 
-			  emutls_finalize_control_var, NULL);
-
-  if (targetm.emutls.register_common)
-    {
-      tree body = NULL_TREE;
-
-      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
@@ -1213,11 +899,6 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
 		  && ADDR_SPACE_GENERIC_P (as));
       if (DECL_THREAD_LOCAL_P (decl))
 	return tls_comm_section;
-      /* This cannot be common bss for an emulated TLS object without
-	 a register_common hook.  */
-      else if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
-	       && !targetm.emutls.register_common)
-	;
       else if (TREE_PUBLIC (decl) && bss_initializer_p (decl))
 	return comm_section;
     }
@@ -2101,40 +1782,6 @@ assemble_variable_contents (tree decl, const char *name,
     }
 }
 
-/* Initialize emulated tls object TO, which refers to TLS variable
-   DECL and is initialized by PROXY.  */
-
-tree
-default_emutls_var_init (tree to, tree decl, tree proxy)
-{
-  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 = proxy;
-
-  return build_constructor (type, v);
-}
-
 /* Assemble everything that is needed for a variable or function declaration.
    Not used for automatic variables, and not used for function definitions.
    Should not be called for variables of incomplete structure type.
@@ -2153,35 +1800,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
   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)
-	      || (DECL_INITIAL (decl)
-		  && DECL_INITIAL (decl) != error_mark_node)))
-	{
-	  DECL_INITIAL (to) = targetm.emutls.var_init
-	    (to, decl, get_emutls_init_templ_addr (decl));
-
-	  /* Make sure the template is marked as needed early enough.
-	     Without this, if the variable is placed in a
-	     section-anchored block, the template will only be marked
-	     when it's too late.  */
-	  record_references_in_initializer (to, false);
-	}
-
-      decl = to;
-    }
-
+  /* Emulated TLS had better not get this far.  */
+  gcc_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl));
+              
   last_assemble_variable_decl = 0;
 
   /* Normally no need to say anything here for external references,
@@ -6410,7 +6031,7 @@ categorize_decl_for_section (const_tree decl, int reloc)
     {
       if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED)
 	{
-	  if (DECL_EMUTLS_VAR_P (decl))
+	  if (decl_emutls_var_p (decl))
 	    {
 	      if (targetm.emutls.var_section)
 		ret = SECCAT_EMUTLS_VAR;

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-12 15:18         ` Richard Henderson
@ 2010-07-12 17:04           ` Richard Henderson
  2010-07-12 20:08             ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-12 17:04 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 268 bytes --]

Test #2.  Changes from previous:

* Move the ipa pass to the end of all_small_ipa_passes.  This should handle the
  problematic cases of OMP and profiling, as discussed on IRC.

* Fold emutls_finish into the IPA pass.  This turned out to be easier than expected.


r~

[-- Attachment #2: emutls-2 --]
[-- Type: text/plain, Size: 38520 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a807e8e..555f336 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1352,6 +1352,7 @@ OBJS-common = \
 	tree-diagnostic.o \
 	tree-dump.o \
 	tree-eh.o \
+	tree-emutls.o \
 	tree-if-conv.o \
 	tree-into-ssa.o \
 	tree-iterator.o \
@@ -3135,6 +3136,8 @@ tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
 tree-complex.o : tree-complex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
     $(TM_H) $(FLAGS_H) $(TREE_FLOW_H) $(GIMPLE_H) \
     tree-iterator.h $(TREE_PASS_H) tree-ssa-propagate.h $(DIAGNOSTIC_H)
+tree-emutls.o : tree-emutls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
+    $(GIMPLE_H) $(TREE_PASS_H) $(TARGET_H) gt-tree-emutls.h
 tree-vect-generic.o : tree-vect-generic.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
     $(TM_H) $(TREE_FLOW_H) $(GIMPLE_H) tree-iterator.h $(TREE_PASS_H) \
     $(FLAGS_H) $(OPTABS_H) $(MACHMODE_H) $(EXPR_H) \
@@ -3734,6 +3737,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \
   $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c \
+  $(srcdir)/tree-emutls.c \
   $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \
   $(srcdir)/tree-chrec.h \
   $(srcdir)/tree-scalar-evolution.c \
diff --git a/gcc/expr.c b/gcc/expr.c
index 00ebfdc..03b879c 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6829,20 +6829,6 @@ highest_pow2_factor_for_target (const_tree target, const_tree exp)
   return MAX (factor, talign);
 }
 \f
-/* 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 (UNKNOWN_LOCATION, fn, arglist);
-  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
-}
-\f
-
 /* Subroutine of expand_expr.  Expand the two operands of a binary
    expression EXP0 and EXP1 placing the results in OP0 and OP1.
    The value may be stored in TARGET if TARGET is nonzero.  The
@@ -6945,18 +6931,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
       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
@@ -8394,16 +8368,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 	  && (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_loc (loc, emutls_var_address (exp));
-	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
-	}
-
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
diff --git a/gcc/output.h b/gcc/output.h
index d1e5f24..1756efc 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -165,9 +165,6 @@ 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);
-
 /* Return the default TLS model for a given variable.  */
 extern enum tls_model decl_default_tls_model (const_tree);
 
diff --git a/gcc/passes.c b/gcc/passes.c
index 8828967..781420c 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -806,6 +806,7 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_ipa_increase_alignment);
   NEXT_PASS (pass_ipa_matrix_reorg);
+  NEXT_PASS (pass_ipa_lower_emutls);
   *p = NULL;
 
   p = &all_regular_ipa_passes;
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 276ae7e..a21a1dc 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -999,11 +999,6 @@ compile_file (void)
   if (seen_error ())
     return;
 
-  /* Ensure that emulated TLS control vars are finalized and build 
-     a static constructor for them, when it is required.  */
-  if (!targetm.have_tls)
-    emutls_finish ();
-
   varpool_assemble_pending_decls ();
   finish_aliases_2 ();
 
diff --git a/gcc/tree-emutls.c b/gcc/tree-emutls.c
new file mode 100644
index 0000000..2041d0e
--- /dev/null
+++ b/gcc/tree-emutls.c
@@ -0,0 +1,599 @@
+/* Lower TLS operations to emulation functions.
+   Copyright (C) 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
+
+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 3, or (at your option) any
+later version.
+
+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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "tree-flow.h"
+#include "cgraph.h"
+#include "langhooks.h"
+#include "target.h"
+
+/* ??? Should go.  */
+#include "targhooks.h"
+#include "tree-iterator.h"
+#include "output.h"
+
+/* TODO: Get rid of the EMUTLS hooks that no one uses.  */
+
+/* Whenever a target does not support thread-local storage (TLS) natively,
+   we can emulate it with some run-time support in libgcc.  This will in
+   turn rely on "keyed storage" a-la pthread_key_create; essentially all
+   thread libraries provide such functionality.
+
+   In order to coordinate with the libgcc runtime, each TLS variable is
+   described by a "control variable".  This control variable records the
+   required size, alignment, and initial value of the TLS variable for
+   instantiation at runtime.  It also stores an integer token to be used
+   by the runtime to find the address of the variable within each thread.
+
+   On the compiler side, this means that we need to replace all instances
+   of "tls_var" in the code with "*__emutls_get_addr(&control_var)".  We
+   also need to eliminate "tls_var" from the symbol table and introduce
+   "control_var".
+
+   We used to perform all of the transformations during conversion to rtl,
+   and the variable substitutions magically within assemble_variable.
+   However, this late fiddling of the symbol table conflicts with LTO and
+   whole-program compilation.  Therefore we must now make all the changes
+   to the symbol table early in the GIMPLE optimization path, before we
+   write things out to LTO intermediate files.  */
+
+/* These two vectors, once fully populated, are kept in lock-step so that
+   the index of a TLS variable equals the index of its control variable in
+   the other vector.  */
+static GTY (()) varpool_node_set tls_vars;
+static GTY (()) varpool_node_set control_vars;
+
+/* The type of the control structure, shared with the emutls.c runtime.  */
+/* ??? See if we can eliminate the one query via decl_emutls_var_p from
+   varasm.c.  With that gone, this need not be live outside the ipa pass.  */
+static GTY (()) tree emutls_object_type;
+
+/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
+   macro can be used on them to distinguish the control variable from
+   the initialization template.  */
+
+bool
+decl_emutls_var_p(tree decl)
+{
+  return TREE_TYPE (decl) == emutls_object_type;
+}
+
+#if !defined (NO_DOT_IN_LABEL)
+# define EMUTLS_SEPARATOR	"."
+#elif !defined (NO_DOLLAR_IN_LABEL)
+# define EMUTLS_SEPARATOR	"$"
+#else
+# define EMUTLS_SEPARATOR	"_"
+#endif
+
+/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
+   IDENTIFIER_NODE NAME's name.  */
+
+static tree
+prefix_name (const char *prefix, tree name)
+{
+  unsigned plen = strlen (prefix);
+  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
+  char *toname = (char *) alloca (plen + nlen + 1);
+
+  memcpy (toname, prefix, plen);
+  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
+
+  return get_identifier (toname);
+}
+
+/* Create an identifier for the struct __emutls_object, given an identifier
+   of the DECL_ASSEMBLY_NAME of the original object.  */
+
+tree
+get_emutls_object_name (tree name)
+{
+  const char *prefix = (targetm.emutls.var_prefix
+			? targetm.emutls.var_prefix
+			: "__emutls_v" EMUTLS_SEPARATOR);
+  return prefix_name (prefix, name);
+}
+
+tree
+default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
+{
+  tree word_type_node, field, next_field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
+  DECL_CONTEXT (field) = type;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      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 (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__align"),
+		      word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__size"), word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+
+  return field;
+}
+
+/* Initialize emulated tls object TO, which refers to TLS variable
+   DECL and is initialized by PROXY.  */
+
+tree
+default_emutls_var_init (tree to, tree decl, tree proxy)
+{
+  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 = proxy;
+
+  return build_constructor (type, v);
+}
+
+/* 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;
+
+  type = emutls_object_type;
+  if (type)
+    return type;
+
+  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
+  type_name = NULL;
+  field = targetm.emutls.var_fields (type, &type_name);
+  if (!type_name)
+    type_name = get_identifier ("__emutls_object");
+  type_name = build_decl (UNKNOWN_LOCATION,
+			  TYPE_DECL, type_name, type);
+  TYPE_NAME (type) = type_name;
+  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;
+
+  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
+      && !DECL_SECTION_NAME (decl))
+    return null_pointer_node;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
+    {
+      const char *prefix = (targetm.emutls.tmpl_prefix
+			    ? targetm.emutls.tmpl_prefix
+			    : "__emutls_t" EMUTLS_SEPARATOR);
+      name = prefix_name (prefix, name);
+    }
+
+  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  if (DECL_ONE_ONLY (decl))
+    {
+      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_INITIAL (to) = DECL_INITIAL (decl);
+  DECL_INITIAL (decl) = NULL;
+
+  varpool_finalize_decl (to);
+  return build_fold_addr_expr (to);
+}
+
+/* Create and return the control variable for the TLS variable DECL.  */
+
+static tree
+new_emutls_decl (tree decl)
+{
+  tree name, to;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  to = build_decl (DECL_SOURCE_LOCATION (decl), VAR_DECL,
+                   get_emutls_object_name (name),
+                   get_emutls_object_type ());
+
+  SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
+
+  DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
+  DECL_ARTIFICIAL (to) = 1;
+  DECL_IGNORED_P (to) = 1;
+  TREE_READONLY (to) = 0;
+  TREE_STATIC (to) = 1;
+
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+  DECL_CONTEXT (to) = DECL_CONTEXT (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);
+  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
+  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
+
+  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
+
+  if (DECL_ONE_ONLY (decl))
+    make_decl_one_only (to, DECL_ASSEMBLER_NAME (to));
+
+  /* ??? What in the world is this for?  */
+  if (targetm.emutls.var_align_fixed)
+    /* If we're not allowed to change the proxy object's
+       alignment, pretend it's been set by the user.  */
+    DECL_USER_ALIGN (to) = 1;
+
+  /* If this variable is defined locally, then we need to initialize the
+     control structure with size and alignment information.  Initialization
+     of COMMON block variables happens elsewhere via a constructor.  */
+  if (!DECL_EXTERNAL (to)
+      && (!DECL_COMMON (to)
+          || (DECL_INITIAL (decl)
+              && DECL_INITIAL (decl) != error_mark_node)))
+    {
+      tree tmpl = get_emutls_init_templ_addr (decl);
+      DECL_INITIAL (to) = targetm.emutls.var_init (to, decl, tmpl);
+      record_references_in_initializer (to, false);
+    }
+
+  varpool_finalize_decl (to);
+  return to;
+}
+
+/* Look up the control variable for the TLS variable DECL.  */
+
+tree
+emutls_decl (tree decl)
+{
+  varpool_node_set_iterator i;
+  struct varpool_node *var;
+  
+  i = varpool_node_set_find (tls_vars, varpool_get_node (decl));
+  if (i.index == ~0u)
+    return NULL;
+
+  var = VEC_index (varpool_node_ptr, control_vars->nodes, i.index);
+  return var->decl;
+}
+
+/* Generate a call statement to initialize CONTROL_DECL for TLS_DECL.
+   This only needs to happen for TLS COMMON variables; non-COMMON
+   variables can be initialized statically.  Insert the generated
+   call statement at the end of PSTMTS.  */
+   
+static void
+emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts)
+{
+  tree args, x;
+  tree word_type_node;
+
+  if (! DECL_COMMON (tls_decl)
+      || (DECL_INITIAL (tls_decl)
+	  && DECL_INITIAL (tls_decl) != error_mark_node))
+    return;
+
+  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+
+  x = get_emutls_init_templ_addr (tls_decl);
+  args = tree_cons (NULL, x, NULL);
+  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = fold_convert (word_type_node, DECL_SIZE_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = build_fold_addr_expr (control_decl);
+  args = tree_cons (NULL, x, args);
+
+  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
+  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);
+
+  append_to_statement_list (x, pstmts);
+}
+
+struct lower_emutls_data
+{
+  struct cgraph_node *cfun_node;
+  struct cgraph_node *builtin_node;
+  tree builtin_decl;
+  basic_block bb;
+  int bb_freq;
+  gimple_stmt_iterator gsi;
+  gimple stmt;
+};
+
+/* Given an operand *PTR within STMT at *GSI, if the operand references
+   a TLS variable, then lower the reference to a call to the runtime.  */
+
+static bool
+lower_emutls_1 (struct lower_emutls_data *d, tree *ptr)
+{
+  tree t = *ptr;
+  tree control, addr;
+  gimple x;
+
+  /* In the case of "&var" we don't want to generate "&*addr",
+     we'd prefer to simply emit "addr".  */
+  if (TREE_CODE (t) == ADDR_EXPR)
+    {
+      if (lower_emutls_1 (d, &TREE_OPERAND (t, 0)))
+	{
+	  if (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
+	      && integer_zerop (TREE_OPERAND (TREE_OPERAND (t, 0), 1)))
+	    *ptr = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+	  return true;
+	}
+      return false;
+    }
+
+  /* Look through all components.  */
+  while (handled_component_p (t))
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* If at the end we don't have a TLS variable, nothing to do.  */
+  if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t))
+    return false;
+
+  /* Compute the address of the TLS variable with help from runtime.  */
+  control = emutls_decl (t);
+  TREE_ADDRESSABLE (control) = 1;
+  addr = create_tmp_var (build_pointer_type (TREE_TYPE (t)), NULL);
+  x = gimple_build_call (d->builtin_decl, 1,
+	                 build_fold_addr_expr (control));
+  gsi_insert_before (&d->gsi, x, GSI_SAME_STMT);
+  gimple_set_location (x, gimple_location (d->stmt));
+
+  addr = make_ssa_name (addr, x);
+  gimple_call_set_lhs (x, addr);
+
+  cgraph_create_edge (d->cfun_node, d->builtin_node, x,
+                      d->bb->count, d->bb_freq, d->bb->loop_depth);
+
+  /* Replace "var" with "*addr" in the statement.  */
+  t = build2 (MEM_REF, TREE_TYPE (t), addr,
+	      build_int_cst (TREE_TYPE (addr), 0));
+  *ptr = t;
+  gimple_set_modified (d->stmt, true);
+
+  return true;
+}
+
+static void
+lower_emutls_function_body (struct cgraph_node *node)
+{
+  struct lower_emutls_data d;
+
+  current_function_decl = node->decl;
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+
+  d.cfun_node = node;
+  d.builtin_decl = built_in_decls[BUILT_IN_EMUTLS_GET_ADDRESS];
+  d.builtin_node = cgraph_node (d.builtin_decl);
+
+  FOR_EACH_BB (d.bb)
+    {
+      d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb);
+
+      for (d.gsi = gsi_start_bb (d.bb); !gsi_end_p (d.gsi); gsi_next (&d.gsi))
+	{
+	  gimple stmt = gsi_stmt (d.gsi);
+
+          d.stmt = stmt;
+	  if (gimple_assign_single_p (stmt))
+	    {
+	      lower_emutls_1 (&d, gimple_assign_lhs_ptr (stmt));
+	      lower_emutls_1 (&d, gimple_assign_rhs1_ptr (stmt));
+	    }
+	  else if (is_gimple_call (stmt))
+	    {
+	      unsigned i, n = gimple_call_num_args (stmt);
+	      for (i = 0; i < n; ++i)
+		lower_emutls_1 (&d, gimple_call_arg_ptr (stmt, i));
+	      lower_emutls_1 (&d, gimple_call_lhs_ptr (stmt));
+	    }
+	  else
+	    continue;
+
+	  update_stmt_if_modified (stmt);
+	}
+    }
+
+  pop_cfun ();
+  current_function_decl = NULL;
+}
+
+static unsigned int
+ipa_lower_emutls (void)
+{
+  struct varpool_node *var;
+  struct cgraph_node *func;
+  bool any_aliases = false;
+  tree ctor_body = NULL;
+  unsigned int i;
+
+  tls_vars = varpool_node_set_new ();
+
+  /* Examine all global variables for TLS variables.  */
+  for (var = varpool_nodes; var ; var = var->next)
+    if (DECL_THREAD_LOCAL_P (var->decl))
+      {
+	gcc_checking_assert (TREE_STATIC (var->decl));
+        if (var->alias)
+	  any_aliases = true;
+        else
+	  varpool_node_set_add (tls_vars, var);
+      }
+
+  /* If we found no TLS variables, then there is no further work to do.  */
+  if (tls_vars->nodes == NULL)
+    {
+      tls_vars = NULL;
+      if (dump_file)
+	fprintf (dump_file, "No TLS variables found.\n");
+      return 0;
+    }
+
+  /* If there were any aliases, add them to the set last.  In this way
+     when we create the control variables the control variable for the
+     alias base will have been created first.  */
+  if (any_aliases)
+    for (var = varpool_nodes; var ; var = var->next)
+      if (DECL_THREAD_LOCAL_P (var->decl) && var->alias)
+        varpool_node_set_add (tls_vars, var);
+
+  /* Create the control variables for each TLS variable.  */
+  control_vars = varpool_node_set_new ();
+  for (i = 0; VEC_iterate (varpool_node_ptr, tls_vars->nodes, i, var); ++i)
+    {
+      tree cdecl;
+      struct varpool_node *cvar;
+
+      var = VEC_index (varpool_node_ptr, tls_vars->nodes, i);
+      cdecl = new_emutls_decl (var->decl);
+
+      /* If there was an alias between the TLS variables,
+         mirror that in the control variables.  */
+      /* ??? There appears to be an alias_pairs data structure that
+         holds similar information.  */
+      if (var->alias)
+        {
+	  tree cbase = emutls_decl (var->extra_name->decl);
+          bool ok = varpool_extra_name_alias (cdecl, cbase);
+          gcc_assert (ok);
+        }
+
+      cvar = varpool_get_node (cdecl);
+      varpool_node_set_add (control_vars, cvar);
+
+      emutls_common_1 (var->decl, cdecl, &ctor_body);
+
+      /* Indicate that the value of the TLS variable may be found elsewhere.
+	 This also prevents the variable from re-appearing in the GIMPLE.  */
+      /* ??? Unfortuantely, there's no decent actual value to put here;
+	 there's nothing we can emit for the debugger at the moment.  */
+      SET_DECL_VALUE_EXPR (var->decl, error_mark_node);
+      DECL_HAS_VALUE_EXPR_P (var->decl) = 1;
+    }
+
+  /* Adjust all uses of TLS variables within the function bodies.  */
+  for (func = cgraph_nodes; func; func = func->next)
+    if (func->reachable && func->lowered)
+      lower_emutls_function_body (func);
+
+  /* Generate the constructor for any COMMON control variables created.  */
+  if (ctor_body)
+    cgraph_build_static_cdtor ('I', ctor_body, DEFAULT_INIT_PRIORITY);
+
+  return TODO_dump_func | TODO_ggc_collect | TODO_verify_stmts;
+}
+
+/* If the target supports TLS natively, we need do nothing here.  */
+
+static bool
+gate_emutls (void)
+{
+  return !targetm.have_tls;
+}
+
+struct simple_ipa_opt_pass pass_ipa_lower_emutls =
+{
+ {
+  SIMPLE_IPA_PASS,
+  "emutls",				/* name */
+  gate_emutls,				/* gate */
+  ipa_lower_emutls,			/* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_NONE,				/* tv_id */
+  PROP_cfg,				/* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  0,					/* todo_flags_finish */
+ }
+};
+
+#include "gt-tree-emutls.h"
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a4c97b3..b309be8 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -445,6 +445,7 @@ extern struct gimple_opt_pass pass_warn_unused_result;
 extern struct gimple_opt_pass pass_split_functions;
 
 /* IPA Passes */
+extern struct simple_ipa_opt_pass pass_ipa_lower_emutls;
 extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility;
 extern struct simple_ipa_opt_pass pass_ipa_early_inline;
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 960ee7d..28bc47a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5257,9 +5257,13 @@ extern void set_user_assembler_name (tree, const char *);
 extern void process_pending_assemble_externals (void);
 extern void finish_aliases_1 (void);
 extern void finish_aliases_2 (void);
-extern tree emutls_decl (tree);
 extern void remove_unreachable_alias_pairs (void);
 
+/* tree-emutls.c */
+extern tree emutls_decl (tree);
+extern tree get_emutls_object_name (tree);
+extern bool decl_emutls_var_p (tree);
+
 /* In stmt.c */
 extern void expand_computed_goto (tree);
 extern bool parse_output_constraint (const char **, int, int, int,
diff --git a/gcc/varasm.c b/gcc/varasm.c
index de78bd0..f15ef0f 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -186,320 +186,6 @@ 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;
-/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
-   macro can be used on them to distinguish the control variable from
-   the initialization template.  */
-#define DECL_EMUTLS_VAR_P(D)  (TREE_TYPE (D) == emutls_object_type)
-
-#if !defined (NO_DOT_IN_LABEL)
-# define EMUTLS_SEPARATOR	"."
-#elif !defined (NO_DOLLAR_IN_LABEL)
-# define EMUTLS_SEPARATOR	"$"
-#else
-# define EMUTLS_SEPARATOR	"_"
-#endif
-
-/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
-   IDENTIFIER_NODE NAME's name.  */
-
-static tree
-prefix_name (const char *prefix, tree name)
-{
-  unsigned plen = strlen (prefix);
-  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
-  char *toname = (char *) alloca (plen + nlen + 1);
-
-  memcpy (toname, prefix, plen);
-  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
-
-  return get_identifier (toname);
-}
-
-/* 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)
-{
-  const char *prefix = (targetm.emutls.var_prefix
-			? targetm.emutls.var_prefix
-			: "__emutls_v" EMUTLS_SEPARATOR);
-  return prefix_name (prefix, name);
-}
-
-tree
-default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
-{
-  tree word_type_node, field, next_field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
-  DECL_CONTEXT (field) = type;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      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 (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__align"),
-		      word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__size"), word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-
-  return field;
-}
-
-/* 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;
-
-  type = emutls_object_type;
-  if (type)
-    return type;
-
-  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
-  type_name = NULL;
-  field = targetm.emutls.var_fields (type, &type_name);
-  if (!type_name)
-    type_name = get_identifier ("__emutls_object");
-  type_name = build_decl (UNKNOWN_LOCATION,
-			  TYPE_DECL, type_name, type);
-  TYPE_NAME (type) = type_name;
-  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;
-
-  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
-      && !DECL_SECTION_NAME (decl))
-    return null_pointer_node;
-
-  name = DECL_ASSEMBLER_NAME (decl);
-  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
-    {
-      const char *prefix = (targetm.emutls.tmpl_prefix
-			    ? targetm.emutls.tmpl_prefix
-			    : "__emutls_t" EMUTLS_SEPARATOR);
-      name = prefix_name (prefix, name);
-    }
-
-  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
-  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
-
-  DECL_WEAK (to) = DECL_WEAK (decl);
-  if (DECL_ONE_ONLY (decl))
-    {
-      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  DECL_INITIAL (to) = DECL_INITIAL (decl);
-  DECL_INITIAL (decl) = NULL;
-
-  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 = IDENTIFIER_HASH_VALUE (name);
-  in.base.from = decl;
-  loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT);
-  h = (struct tree_map *) *loc;
-  if (h != NULL)
-    to = h->to;
-  else
-    {
-      to = build_decl (DECL_SOURCE_LOCATION (decl),
-		       VAR_DECL, get_emutls_object_name (name),
-		       get_emutls_object_type ());
-
-      h = ggc_alloc_tree_map ();
-      h->hash = in.hash;
-      h->base.from = decl;
-      h->to = to;
-      *(struct tree_map **) loc = h;
-
-      DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
-      DECL_ARTIFICIAL (to) = 1;
-      DECL_IGNORED_P (to) = 1;
-      /* FIXME: work around PR44132.  */
-      DECL_PRESERVE_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_ASSEMBLER_NAME (to));
-      DECL_CONTEXT (to) = DECL_CONTEXT (decl);
-      if (targetm.emutls.var_align_fixed)
-	/* If we're not allowed to change the proxy object's
-	   alignment, pretend it's been set by the user.  */
-	DECL_USER_ALIGN (to) = 1;
-    }
-
-  /* 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.  */
-  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
-  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
-
-  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);
-  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  
-  /* Fortran might pass this to us.  */
-  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (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->base.from)
-      || (DECL_INITIAL (h->base.from)
-	  && DECL_INITIAL (h->base.from) != error_mark_node))
-    return 1;
-
-  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
-
-  /* The idea was to call get_emutls_init_templ_addr here, but if we
-     do this and there is an initializer, -fanchor_section loses,
-     because it would be too late to ensure the template is
-     output.  */
-  x = null_pointer_node;
-  args = tree_cons (NULL, x, NULL);
-  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
-  args = tree_cons (NULL, x, args);
-  x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->base.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 (UNKNOWN_LOCATION, x, args);
-
-  append_to_statement_list (x, pstmts);
-  return 1;
-}
-
-/* Callback to finalize one emutls control variable.  */
-
-static int
-emutls_finalize_control_var (void **loc, 
-				void *unused ATTRIBUTE_UNUSED)
-{
-  struct tree_map *h = *(struct tree_map **) loc;
-  if (h != NULL) 
-    {
-      struct varpool_node *node = varpool_node (h->to);
-      /* Because varpool_finalize_decl () has side-effects,
-         only apply to un-finalized vars.  */
-      if (node && !node->finalized) 
-	varpool_finalize_decl (h->to);
-    }
-  return 1;
-}
-
-/* Finalize emutls control vars and add a static constructor if
-   required.  */
-
-void
-emutls_finish (void)
-{
-  if (emutls_htab == NULL)
-    return;
-  htab_traverse_noresize (emutls_htab, 
-			  emutls_finalize_control_var, NULL);
-
-  if (targetm.emutls.register_common)
-    {
-      tree body = NULL_TREE;
-
-      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
@@ -1213,11 +899,6 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
 		  && ADDR_SPACE_GENERIC_P (as));
       if (DECL_THREAD_LOCAL_P (decl))
 	return tls_comm_section;
-      /* This cannot be common bss for an emulated TLS object without
-	 a register_common hook.  */
-      else if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
-	       && !targetm.emutls.register_common)
-	;
       else if (TREE_PUBLIC (decl) && bss_initializer_p (decl))
 	return comm_section;
     }
@@ -2101,40 +1782,6 @@ assemble_variable_contents (tree decl, const char *name,
     }
 }
 
-/* Initialize emulated tls object TO, which refers to TLS variable
-   DECL and is initialized by PROXY.  */
-
-tree
-default_emutls_var_init (tree to, tree decl, tree proxy)
-{
-  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 = proxy;
-
-  return build_constructor (type, v);
-}
-
 /* Assemble everything that is needed for a variable or function declaration.
    Not used for automatic variables, and not used for function definitions.
    Should not be called for variables of incomplete structure type.
@@ -2153,35 +1800,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
   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)
-	      || (DECL_INITIAL (decl)
-		  && DECL_INITIAL (decl) != error_mark_node)))
-	{
-	  DECL_INITIAL (to) = targetm.emutls.var_init
-	    (to, decl, get_emutls_init_templ_addr (decl));
-
-	  /* Make sure the template is marked as needed early enough.
-	     Without this, if the variable is placed in a
-	     section-anchored block, the template will only be marked
-	     when it's too late.  */
-	  record_references_in_initializer (to, false);
-	}
-
-      decl = to;
-    }
-
+  /* Emulated TLS had better not get this far.  */
+  gcc_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl));
+              
   last_assemble_variable_decl = 0;
 
   /* Normally no need to say anything here for external references,
@@ -6410,7 +6031,7 @@ categorize_decl_for_section (const_tree decl, int reloc)
     {
       if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED)
 	{
-	  if (DECL_EMUTLS_VAR_P (decl))
+	  if (decl_emutls_var_p (decl))
 	    {
 	      if (targetm.emutls.var_section)
 		ret = SECCAT_EMUTLS_VAR;

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-12 17:04           ` Richard Henderson
@ 2010-07-12 20:08             ` Richard Henderson
  2010-07-13 15:47               ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-12 20:08 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 133 bytes --]

Test #3.  Fixed up a bunch of failures visible in the testsuite.
Aliasing isn't supposed to work yet, so ignore failures there.


r~

[-- Attachment #2: emutls-3 --]
[-- Type: text/plain, Size: 41174 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a807e8e..9f96925 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1352,6 +1352,7 @@ OBJS-common = \
 	tree-diagnostic.o \
 	tree-dump.o \
 	tree-eh.o \
+	tree-emutls.o \
 	tree-if-conv.o \
 	tree-into-ssa.o \
 	tree-iterator.o \
@@ -3135,6 +3136,9 @@ tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
 tree-complex.o : tree-complex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
     $(TM_H) $(FLAGS_H) $(TREE_FLOW_H) $(GIMPLE_H) \
     tree-iterator.h $(TREE_PASS_H) tree-ssa-propagate.h $(DIAGNOSTIC_H)
+tree-emutls.o : tree-emutls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
+    $(GIMPLE_H) $(TREE_PASS_H) $(TREE_FLOW_H) $(CGRAPH_H) langhooks.h \
+    $(TARGET_H) targhooks.h tree-iterator.h output.h gt-tree-emutls.h
 tree-vect-generic.o : tree-vect-generic.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
     $(TM_H) $(TREE_FLOW_H) $(GIMPLE_H) tree-iterator.h $(TREE_PASS_H) \
     $(FLAGS_H) $(OPTABS_H) $(MACHMODE_H) $(EXPR_H) \
@@ -3734,6 +3738,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \
   $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c \
+  $(srcdir)/tree-emutls.c \
   $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \
   $(srcdir)/tree-chrec.h \
   $(srcdir)/tree-scalar-evolution.c \
diff --git a/gcc/expr.c b/gcc/expr.c
index 00ebfdc..03b879c 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6829,20 +6829,6 @@ highest_pow2_factor_for_target (const_tree target, const_tree exp)
   return MAX (factor, talign);
 }
 \f
-/* 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 (UNKNOWN_LOCATION, fn, arglist);
-  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
-}
-\f
-
 /* Subroutine of expand_expr.  Expand the two operands of a binary
    expression EXP0 and EXP1 placing the results in OP0 and OP1.
    The value may be stored in TARGET if TARGET is nonzero.  The
@@ -6945,18 +6931,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
       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
@@ -8394,16 +8368,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 	  && (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_loc (loc, emutls_var_address (exp));
-	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
-	}
-
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
diff --git a/gcc/output.h b/gcc/output.h
index d1e5f24..1756efc 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -165,9 +165,6 @@ 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);
-
 /* Return the default TLS model for a given variable.  */
 extern enum tls_model decl_default_tls_model (const_tree);
 
diff --git a/gcc/passes.c b/gcc/passes.c
index 8828967..781420c 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -806,6 +806,7 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_ipa_increase_alignment);
   NEXT_PASS (pass_ipa_matrix_reorg);
+  NEXT_PASS (pass_ipa_lower_emutls);
   *p = NULL;
 
   p = &all_regular_ipa_passes;
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 276ae7e..a21a1dc 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -999,11 +999,6 @@ compile_file (void)
   if (seen_error ())
     return;
 
-  /* Ensure that emulated TLS control vars are finalized and build 
-     a static constructor for them, when it is required.  */
-  if (!targetm.have_tls)
-    emutls_finish ();
-
   varpool_assemble_pending_decls ();
   finish_aliases_2 ();
 
diff --git a/gcc/tree-emutls.c b/gcc/tree-emutls.c
new file mode 100644
index 0000000..959aa54
--- /dev/null
+++ b/gcc/tree-emutls.c
@@ -0,0 +1,612 @@
+/* Lower TLS operations to emulation functions.
+   Copyright (C) 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
+
+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 3, or (at your option) any
+later version.
+
+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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "tree-flow.h"
+#include "cgraph.h"
+#include "langhooks.h"
+#include "target.h"
+
+/* ??? Should go.  */
+#include "targhooks.h"
+#include "tree-iterator.h"
+#include "output.h"
+
+/* TODO: Get rid of the EMUTLS hooks that no one uses.  */
+
+/* Whenever a target does not support thread-local storage (TLS) natively,
+   we can emulate it with some run-time support in libgcc.  This will in
+   turn rely on "keyed storage" a-la pthread_key_create; essentially all
+   thread libraries provide such functionality.
+
+   In order to coordinate with the libgcc runtime, each TLS variable is
+   described by a "control variable".  This control variable records the
+   required size, alignment, and initial value of the TLS variable for
+   instantiation at runtime.  It also stores an integer token to be used
+   by the runtime to find the address of the variable within each thread.
+
+   On the compiler side, this means that we need to replace all instances
+   of "tls_var" in the code with "*__emutls_get_addr(&control_var)".  We
+   also need to eliminate "tls_var" from the symbol table and introduce
+   "control_var".
+
+   We used to perform all of the transformations during conversion to rtl,
+   and the variable substitutions magically within assemble_variable.
+   However, this late fiddling of the symbol table conflicts with LTO and
+   whole-program compilation.  Therefore we must now make all the changes
+   to the symbol table early in the GIMPLE optimization path, before we
+   write things out to LTO intermediate files.  */
+
+/* These two vectors, once fully populated, are kept in lock-step so that
+   the index of a TLS variable equals the index of its control variable in
+   the other vector.  */
+static GTY (()) varpool_node_set tls_vars;
+static GTY (()) varpool_node_set control_vars;
+
+/* The type of the control structure, shared with the emutls.c runtime.  */
+/* ??? See if we can eliminate the one query via decl_emutls_var_p from
+   varasm.c.  With that gone, this need not be live outside the ipa pass.  */
+static GTY (()) tree emutls_object_type;
+
+/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
+   macro can be used on them to distinguish the control variable from
+   the initialization template.  */
+
+bool
+decl_emutls_var_p(tree decl)
+{
+  return TREE_TYPE (decl) == emutls_object_type;
+}
+
+#if !defined (NO_DOT_IN_LABEL)
+# define EMUTLS_SEPARATOR	"."
+#elif !defined (NO_DOLLAR_IN_LABEL)
+# define EMUTLS_SEPARATOR	"$"
+#else
+# define EMUTLS_SEPARATOR	"_"
+#endif
+
+/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
+   IDENTIFIER_NODE NAME's name.  */
+
+static tree
+prefix_name (const char *prefix, tree name)
+{
+  unsigned plen = strlen (prefix);
+  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
+  char *toname = (char *) alloca (plen + nlen + 1);
+
+  memcpy (toname, prefix, plen);
+  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
+
+  return get_identifier (toname);
+}
+
+/* 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)
+{
+  const char *prefix = (targetm.emutls.var_prefix
+			? targetm.emutls.var_prefix
+			: "__emutls_v" EMUTLS_SEPARATOR);
+  return prefix_name (prefix, name);
+}
+
+tree
+default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
+{
+  tree word_type_node, field, next_field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
+  DECL_CONTEXT (field) = type;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      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 (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__align"),
+		      word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__size"), word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+
+  return field;
+}
+
+/* Initialize emulated tls object TO, which refers to TLS variable
+   DECL and is initialized by PROXY.  */
+
+tree
+default_emutls_var_init (tree to, tree decl, tree proxy)
+{
+  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 = proxy;
+
+  return build_constructor (type, v);
+}
+
+/* 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;
+
+  type = emutls_object_type;
+  if (type)
+    return type;
+
+  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
+  type_name = NULL;
+  field = targetm.emutls.var_fields (type, &type_name);
+  if (!type_name)
+    type_name = get_identifier ("__emutls_object");
+  type_name = build_decl (UNKNOWN_LOCATION,
+			  TYPE_DECL, type_name, type);
+  TYPE_NAME (type) = type_name;
+  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;
+
+  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
+      && !DECL_SECTION_NAME (decl))
+    return null_pointer_node;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
+    {
+      const char *prefix = (targetm.emutls.tmpl_prefix
+			    ? targetm.emutls.tmpl_prefix
+			    : "__emutls_t" EMUTLS_SEPARATOR);
+      name = prefix_name (prefix, name);
+    }
+
+  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  if (DECL_ONE_ONLY (decl))
+    {
+      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_INITIAL (to) = DECL_INITIAL (decl);
+  DECL_INITIAL (decl) = NULL;
+
+  varpool_finalize_decl (to);
+  return build_fold_addr_expr (to);
+}
+
+/* Create and return the control variable for the TLS variable DECL.  */
+
+static tree
+new_emutls_decl (tree decl)
+{
+  tree name, to;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  to = build_decl (DECL_SOURCE_LOCATION (decl), VAR_DECL,
+                   get_emutls_object_name (name),
+                   get_emutls_object_type ());
+
+  SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
+
+  DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
+  DECL_ARTIFICIAL (to) = 1;
+  DECL_IGNORED_P (to) = 1;
+  TREE_READONLY (to) = 0;
+  TREE_STATIC (to) = 1;
+
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+  DECL_CONTEXT (to) = DECL_CONTEXT (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);
+  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
+  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
+
+  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
+
+  if (DECL_ONE_ONLY (decl))
+    make_decl_one_only (to, DECL_ASSEMBLER_NAME (to));
+
+  /* ??? What in the world is this for?  */
+  if (targetm.emutls.var_align_fixed)
+    /* If we're not allowed to change the proxy object's
+       alignment, pretend it's been set by the user.  */
+    DECL_USER_ALIGN (to) = 1;
+
+  /* If this variable is defined locally, then we need to initialize the
+     control structure with size and alignment information.  Initialization
+     of COMMON block variables happens elsewhere via a constructor.  */
+  if (!DECL_EXTERNAL (to)
+      && (!DECL_COMMON (to)
+          || (DECL_INITIAL (decl)
+              && DECL_INITIAL (decl) != error_mark_node)))
+    {
+      tree tmpl = get_emutls_init_templ_addr (decl);
+      DECL_INITIAL (to) = targetm.emutls.var_init (to, decl, tmpl);
+      record_references_in_initializer (to, false);
+    }
+
+  varpool_finalize_decl (to);
+  return to;
+}
+
+/* Look up the control variable for the TLS variable DECL.  */
+
+tree
+emutls_decl (tree decl)
+{
+  varpool_node_set_iterator i;
+  struct varpool_node *var;
+  
+  i = varpool_node_set_find (tls_vars, varpool_get_node (decl));
+  if (i.index == ~0u)
+    return NULL;
+
+  var = VEC_index (varpool_node_ptr, control_vars->nodes, i.index);
+  return var->decl;
+}
+
+/* Generate a call statement to initialize CONTROL_DECL for TLS_DECL.
+   This only needs to happen for TLS COMMON variables; non-COMMON
+   variables can be initialized statically.  Insert the generated
+   call statement at the end of PSTMTS.  */
+   
+static void
+emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts)
+{
+  tree args, x;
+  tree word_type_node;
+
+  if (! DECL_COMMON (tls_decl)
+      || (DECL_INITIAL (tls_decl)
+	  && DECL_INITIAL (tls_decl) != error_mark_node))
+    return;
+
+  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+
+  x = get_emutls_init_templ_addr (tls_decl);
+  args = tree_cons (NULL, x, NULL);
+  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = fold_convert (word_type_node, DECL_SIZE_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = build_fold_addr_expr (control_decl);
+  args = tree_cons (NULL, x, args);
+
+  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
+  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);
+
+  append_to_statement_list (x, pstmts);
+}
+
+struct lower_emutls_data
+{
+  struct cgraph_node *cfun_node;
+  struct cgraph_node *builtin_node;
+  tree builtin_decl;
+  basic_block bb;
+  int bb_freq;
+  gimple_stmt_iterator gsi;
+  gimple stmt;
+};
+
+/* Given an operand *PTR within STMT at *GSI, if the operand references
+   a TLS variable, then lower the reference to a call to the runtime.  */
+
+static bool
+lower_emutls_1 (struct lower_emutls_data *d, tree *ptr)
+{
+  tree t = *ptr;
+  tree control, addr;
+  gimple x;
+
+  /* Look through all components.  */
+  while (handled_component_p (t))
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* Note that MEM_REF is not a "component" per-se, but may contain
+     ADDR_EXPR of a symbol, which we need to handle.  */
+  if (TREE_CODE (t) == MEM_REF)
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* In the case of "&var" we don't want to generate "&*addr",
+     we'd prefer to simply emit "addr".  */
+  if (TREE_CODE (t) == ADDR_EXPR)
+    {
+      if (lower_emutls_1 (d, &TREE_OPERAND (t, 0)))
+	{
+	  if (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
+	      && integer_zerop (TREE_OPERAND (TREE_OPERAND (t, 0), 1)))
+	    *ptr = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+	  return true;
+	}
+      return false;
+    }
+
+  /* If at the end we don't have a TLS variable, nothing to do.  */
+  if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t))
+    return false;
+
+  /* Compute the address of the TLS variable with help from runtime.  */
+  control = emutls_decl (t);
+  TREE_ADDRESSABLE (control) = 1;
+  addr = create_tmp_var (build_pointer_type (TREE_TYPE (t)), NULL);
+  x = gimple_build_call (d->builtin_decl, 1,
+	                 build_fold_addr_expr (control));
+  gsi_insert_before (&d->gsi, x, GSI_SAME_STMT);
+  gimple_set_location (x, gimple_location (d->stmt));
+
+  addr = make_ssa_name (addr, x);
+  gimple_call_set_lhs (x, addr);
+
+  cgraph_create_edge (d->cfun_node, d->builtin_node, x,
+                      d->bb->count, d->bb_freq, d->bb->loop_depth);
+
+  /* Replace "var" with "*addr" in the statement.  */
+  t = build2 (MEM_REF, TREE_TYPE (t), addr,
+	      build_int_cst (TREE_TYPE (addr), 0));
+  *ptr = t;
+  gimple_set_modified (d->stmt, true);
+
+  return true;
+}
+
+static void
+lower_emutls_function_body (struct cgraph_node *node)
+{
+  struct lower_emutls_data d;
+
+  current_function_decl = node->decl;
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+
+  d.cfun_node = node;
+  d.builtin_decl = built_in_decls[BUILT_IN_EMUTLS_GET_ADDRESS];
+  d.builtin_node = cgraph_node (d.builtin_decl);
+
+  FOR_EACH_BB (d.bb)
+    {
+      d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb);
+
+      for (d.gsi = gsi_start_bb (d.bb); !gsi_end_p (d.gsi); gsi_next (&d.gsi))
+	{
+	  gimple stmt = gsi_stmt (d.gsi);
+
+          d.stmt = stmt;
+	  if (gimple_assign_single_p (stmt))
+	    {
+	      lower_emutls_1 (&d, gimple_assign_lhs_ptr (stmt));
+	      lower_emutls_1 (&d, gimple_assign_rhs1_ptr (stmt));
+	    }
+	  else if (is_gimple_call (stmt))
+	    {
+	      unsigned i, n = gimple_call_num_args (stmt);
+	      for (i = 0; i < n; ++i)
+		lower_emutls_1 (&d, gimple_call_arg_ptr (stmt, i));
+	      if (gimple_call_lhs (stmt))
+	        lower_emutls_1 (&d, gimple_call_lhs_ptr (stmt));
+	    }
+	  else
+	    continue;
+
+	  update_stmt_if_modified (stmt);
+	}
+    }
+
+  pop_cfun ();
+  current_function_decl = NULL;
+}
+
+static unsigned int
+ipa_lower_emutls (void)
+{
+  struct varpool_node *var;
+  struct cgraph_node *func;
+  bool any_aliases = false;
+  tree ctor_body = NULL;
+  unsigned int i;
+
+  tls_vars = varpool_node_set_new ();
+
+  /* Examine all global variables for TLS variables.  */
+  for (var = varpool_nodes; var ; var = var->next)
+    if (DECL_THREAD_LOCAL_P (var->decl))
+      {
+        /* ??? We really should be more consistent about setting these
+	   sorts of flags.  TREE_STATIC != C "static" keyword, and thus
+	   it should be set *with* DECL_EXTERNAL.  */
+	gcc_checking_assert (TREE_STATIC (var->decl)
+			     || DECL_EXTERNAL (var->decl));
+        if (var->alias)
+	  any_aliases = true;
+        else
+	  varpool_node_set_add (tls_vars, var);
+      }
+
+  /* If we found no TLS variables, then there is no further work to do.  */
+  if (tls_vars->nodes == NULL)
+    {
+      tls_vars = NULL;
+      if (dump_file)
+	fprintf (dump_file, "No TLS variables found.\n");
+      return 0;
+    }
+
+  /* If there were any aliases, add them to the set last.  In this way
+     when we create the control variables the control variable for the
+     alias base will have been created first.  */
+  if (any_aliases)
+    for (var = varpool_nodes; var ; var = var->next)
+      if (DECL_THREAD_LOCAL_P (var->decl) && var->alias)
+        varpool_node_set_add (tls_vars, var);
+
+  /* Create the control variables for each TLS variable.  */
+  control_vars = varpool_node_set_new ();
+  for (i = 0; VEC_iterate (varpool_node_ptr, tls_vars->nodes, i, var); ++i)
+    {
+      tree cdecl;
+      struct varpool_node *cvar;
+
+      var = VEC_index (varpool_node_ptr, tls_vars->nodes, i);
+      cdecl = new_emutls_decl (var->decl);
+
+      /* If there was an alias between the TLS variables,
+         mirror that in the control variables.  */
+      /* ??? There appears to be an alias_pairs data structure that
+         holds similar information.  */
+      if (var->alias)
+        {
+	  tree cbase = emutls_decl (var->extra_name->decl);
+          bool ok = varpool_extra_name_alias (cdecl, cbase);
+          gcc_assert (ok);
+        }
+
+      cvar = varpool_get_node (cdecl);
+      varpool_node_set_add (control_vars, cvar);
+
+      emutls_common_1 (var->decl, cdecl, &ctor_body);
+
+      /* Indicate that the value of the TLS variable may be found elsewhere.
+	 This also prevents the variable from re-appearing in the GIMPLE.  */
+      /* ??? Unfortuantely, there's no decent actual value to put here;
+	 there's nothing we can emit for the debugger at the moment.  */
+      SET_DECL_VALUE_EXPR (var->decl, error_mark_node);
+      DECL_HAS_VALUE_EXPR_P (var->decl) = 1;
+    }
+
+  /* Adjust all uses of TLS variables within the function bodies.  */
+  for (func = cgraph_nodes; func; func = func->next)
+    if (func->reachable && func->lowered)
+      lower_emutls_function_body (func);
+
+  /* Generate the constructor for any COMMON control variables created.  */
+  if (ctor_body)
+    cgraph_build_static_cdtor ('I', ctor_body, DEFAULT_INIT_PRIORITY);
+
+  return TODO_dump_func | TODO_ggc_collect | TODO_verify_stmts;
+}
+
+/* If the target supports TLS natively, we need do nothing here.  */
+
+static bool
+gate_emutls (void)
+{
+  return !targetm.have_tls;
+}
+
+struct simple_ipa_opt_pass pass_ipa_lower_emutls =
+{
+ {
+  SIMPLE_IPA_PASS,
+  "emutls",				/* name */
+  gate_emutls,				/* gate */
+  ipa_lower_emutls,			/* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_NONE,				/* tv_id */
+  PROP_cfg,				/* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  0,					/* todo_flags_finish */
+ }
+};
+
+#include "gt-tree-emutls.h"
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a4c97b3..b309be8 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -445,6 +445,7 @@ extern struct gimple_opt_pass pass_warn_unused_result;
 extern struct gimple_opt_pass pass_split_functions;
 
 /* IPA Passes */
+extern struct simple_ipa_opt_pass pass_ipa_lower_emutls;
 extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility;
 extern struct simple_ipa_opt_pass pass_ipa_early_inline;
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 960ee7d..8cc4167 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5257,9 +5257,12 @@ extern void set_user_assembler_name (tree, const char *);
 extern void process_pending_assemble_externals (void);
 extern void finish_aliases_1 (void);
 extern void finish_aliases_2 (void);
-extern tree emutls_decl (tree);
 extern void remove_unreachable_alias_pairs (void);
 
+/* tree-emutls.c */
+extern tree emutls_decl (tree);
+extern bool decl_emutls_var_p (tree);
+
 /* In stmt.c */
 extern void expand_computed_goto (tree);
 extern bool parse_output_constraint (const char **, int, int, int,
diff --git a/gcc/varasm.c b/gcc/varasm.c
index de78bd0..078efc2 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -186,320 +186,6 @@ 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;
-/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
-   macro can be used on them to distinguish the control variable from
-   the initialization template.  */
-#define DECL_EMUTLS_VAR_P(D)  (TREE_TYPE (D) == emutls_object_type)
-
-#if !defined (NO_DOT_IN_LABEL)
-# define EMUTLS_SEPARATOR	"."
-#elif !defined (NO_DOLLAR_IN_LABEL)
-# define EMUTLS_SEPARATOR	"$"
-#else
-# define EMUTLS_SEPARATOR	"_"
-#endif
-
-/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
-   IDENTIFIER_NODE NAME's name.  */
-
-static tree
-prefix_name (const char *prefix, tree name)
-{
-  unsigned plen = strlen (prefix);
-  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
-  char *toname = (char *) alloca (plen + nlen + 1);
-
-  memcpy (toname, prefix, plen);
-  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
-
-  return get_identifier (toname);
-}
-
-/* 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)
-{
-  const char *prefix = (targetm.emutls.var_prefix
-			? targetm.emutls.var_prefix
-			: "__emutls_v" EMUTLS_SEPARATOR);
-  return prefix_name (prefix, name);
-}
-
-tree
-default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
-{
-  tree word_type_node, field, next_field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
-  DECL_CONTEXT (field) = type;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      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 (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__align"),
-		      word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__size"), word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-
-  return field;
-}
-
-/* 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;
-
-  type = emutls_object_type;
-  if (type)
-    return type;
-
-  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
-  type_name = NULL;
-  field = targetm.emutls.var_fields (type, &type_name);
-  if (!type_name)
-    type_name = get_identifier ("__emutls_object");
-  type_name = build_decl (UNKNOWN_LOCATION,
-			  TYPE_DECL, type_name, type);
-  TYPE_NAME (type) = type_name;
-  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;
-
-  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
-      && !DECL_SECTION_NAME (decl))
-    return null_pointer_node;
-
-  name = DECL_ASSEMBLER_NAME (decl);
-  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
-    {
-      const char *prefix = (targetm.emutls.tmpl_prefix
-			    ? targetm.emutls.tmpl_prefix
-			    : "__emutls_t" EMUTLS_SEPARATOR);
-      name = prefix_name (prefix, name);
-    }
-
-  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
-  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
-
-  DECL_WEAK (to) = DECL_WEAK (decl);
-  if (DECL_ONE_ONLY (decl))
-    {
-      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  DECL_INITIAL (to) = DECL_INITIAL (decl);
-  DECL_INITIAL (decl) = NULL;
-
-  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 = IDENTIFIER_HASH_VALUE (name);
-  in.base.from = decl;
-  loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT);
-  h = (struct tree_map *) *loc;
-  if (h != NULL)
-    to = h->to;
-  else
-    {
-      to = build_decl (DECL_SOURCE_LOCATION (decl),
-		       VAR_DECL, get_emutls_object_name (name),
-		       get_emutls_object_type ());
-
-      h = ggc_alloc_tree_map ();
-      h->hash = in.hash;
-      h->base.from = decl;
-      h->to = to;
-      *(struct tree_map **) loc = h;
-
-      DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
-      DECL_ARTIFICIAL (to) = 1;
-      DECL_IGNORED_P (to) = 1;
-      /* FIXME: work around PR44132.  */
-      DECL_PRESERVE_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_ASSEMBLER_NAME (to));
-      DECL_CONTEXT (to) = DECL_CONTEXT (decl);
-      if (targetm.emutls.var_align_fixed)
-	/* If we're not allowed to change the proxy object's
-	   alignment, pretend it's been set by the user.  */
-	DECL_USER_ALIGN (to) = 1;
-    }
-
-  /* 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.  */
-  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
-  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
-
-  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);
-  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  
-  /* Fortran might pass this to us.  */
-  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (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->base.from)
-      || (DECL_INITIAL (h->base.from)
-	  && DECL_INITIAL (h->base.from) != error_mark_node))
-    return 1;
-
-  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
-
-  /* The idea was to call get_emutls_init_templ_addr here, but if we
-     do this and there is an initializer, -fanchor_section loses,
-     because it would be too late to ensure the template is
-     output.  */
-  x = null_pointer_node;
-  args = tree_cons (NULL, x, NULL);
-  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
-  args = tree_cons (NULL, x, args);
-  x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->base.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 (UNKNOWN_LOCATION, x, args);
-
-  append_to_statement_list (x, pstmts);
-  return 1;
-}
-
-/* Callback to finalize one emutls control variable.  */
-
-static int
-emutls_finalize_control_var (void **loc, 
-				void *unused ATTRIBUTE_UNUSED)
-{
-  struct tree_map *h = *(struct tree_map **) loc;
-  if (h != NULL) 
-    {
-      struct varpool_node *node = varpool_node (h->to);
-      /* Because varpool_finalize_decl () has side-effects,
-         only apply to un-finalized vars.  */
-      if (node && !node->finalized) 
-	varpool_finalize_decl (h->to);
-    }
-  return 1;
-}
-
-/* Finalize emutls control vars and add a static constructor if
-   required.  */
-
-void
-emutls_finish (void)
-{
-  if (emutls_htab == NULL)
-    return;
-  htab_traverse_noresize (emutls_htab, 
-			  emutls_finalize_control_var, NULL);
-
-  if (targetm.emutls.register_common)
-    {
-      tree body = NULL_TREE;
-
-      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
@@ -1213,11 +899,6 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
 		  && ADDR_SPACE_GENERIC_P (as));
       if (DECL_THREAD_LOCAL_P (decl))
 	return tls_comm_section;
-      /* This cannot be common bss for an emulated TLS object without
-	 a register_common hook.  */
-      else if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
-	       && !targetm.emutls.register_common)
-	;
       else if (TREE_PUBLIC (decl) && bss_initializer_p (decl))
 	return comm_section;
     }
@@ -2101,40 +1782,6 @@ assemble_variable_contents (tree decl, const char *name,
     }
 }
 
-/* Initialize emulated tls object TO, which refers to TLS variable
-   DECL and is initialized by PROXY.  */
-
-tree
-default_emutls_var_init (tree to, tree decl, tree proxy)
-{
-  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 = proxy;
-
-  return build_constructor (type, v);
-}
-
 /* Assemble everything that is needed for a variable or function declaration.
    Not used for automatic variables, and not used for function definitions.
    Should not be called for variables of incomplete structure type.
@@ -2153,35 +1800,6 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
   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)
-	      || (DECL_INITIAL (decl)
-		  && DECL_INITIAL (decl) != error_mark_node)))
-	{
-	  DECL_INITIAL (to) = targetm.emutls.var_init
-	    (to, decl, get_emutls_init_templ_addr (decl));
-
-	  /* Make sure the template is marked as needed early enough.
-	     Without this, if the variable is placed in a
-	     section-anchored block, the template will only be marked
-	     when it's too late.  */
-	  record_references_in_initializer (to, false);
-	}
-
-      decl = to;
-    }
-
   last_assemble_variable_decl = 0;
 
   /* Normally no need to say anything here for external references,
@@ -2204,6 +1822,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
       return;
     }
 
+  /* Emulated TLS had better not get this far.  */
+  gcc_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl));
+
   /* If type was incomplete when the variable was declared,
      see if it is complete now.  */
 
@@ -5691,6 +5312,11 @@ find_decl_and_mark_needed (tree decl, tree target)
 static void
 do_assemble_alias (tree decl, tree target)
 {
+  /* Emulated TLS had better not get this var.  */
+  gcc_assert(!(!targetm.have_tls
+	       && TREE_CODE (decl) == VAR_DECL
+	       && DECL_THREAD_LOCAL_P (decl)));
+
   if (TREE_ASM_WRITTEN (decl))
     return;
 
@@ -5705,14 +5331,6 @@ do_assemble_alias (tree decl, tree target)
     {
       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);
 
@@ -5731,14 +5349,6 @@ do_assemble_alias (tree decl, tree target)
       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.  */
 
@@ -6410,7 +6020,7 @@ categorize_decl_for_section (const_tree decl, int reloc)
     {
       if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED)
 	{
-	  if (DECL_EMUTLS_VAR_P (decl))
+	  if (decl_emutls_var_p (decl))
 	    {
 	      if (targetm.emutls.var_section)
 		ret = SECCAT_EMUTLS_VAR;
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 94c949e..dcf3518 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -346,17 +346,6 @@ decide_is_variable_needed (struct varpool_node *node, tree decl)
       && !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 (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_toplevel_reorder)

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-12 20:08             ` Richard Henderson
@ 2010-07-13 15:47               ` Richard Henderson
  2010-07-13 18:56                 ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-13 15:47 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 891 bytes --]

Test #4.

When we moved the emutls pass later, to allow omp and profile run first so
that they could create new TLS variables, we exposed tls addresses to the
early optimizers.  Which allowed them to propagate &tlsvar into PHI arguments.

Lowering PHI arguments sounded like a royal pain, so I decided to prevent it
by declaring emulated tls addresses to be !is_gimple_min_invariant.  This had
all sorts of unanticipated follow-on effects; in the end the simplest bubble
to squash appears to be in forwprop, where we were blindly propagating
ADDR_EXPR without looking to see if it might be valid.

Also fixed (aka hacked around) is a ggc corruption bug caused by an explicit
ggc_free in varpool_remove_node.  I'm not quite sure how it would be best to
fix that one properly.

Also fixed is maintaining the IPA web.  We should no longer incorrectly 
discard control variables as unused.


r~

[-- Attachment #2: emutls-4 --]
[-- Type: text/plain, Size: 43425 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c6f199f..d609126 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1354,6 +1354,7 @@ OBJS-common = \
 	tree-diagnostic.o \
 	tree-dump.o \
 	tree-eh.o \
+	tree-emutls.o \
 	tree-if-conv.o \
 	tree-into-ssa.o \
 	tree-iterator.o \
@@ -3142,6 +3143,9 @@ tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
 tree-complex.o : tree-complex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
     $(TM_H) $(FLAGS_H) $(TREE_FLOW_H) $(GIMPLE_H) \
     tree-iterator.h $(TREE_PASS_H) tree-ssa-propagate.h $(DIAGNOSTIC_H)
+tree-emutls.o : tree-emutls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
+    $(GIMPLE_H) $(TREE_PASS_H) $(TREE_FLOW_H) $(CGRAPH_H) langhooks.h \
+    $(TARGET_H) targhooks.h tree-iterator.h output.h gt-tree-emutls.h
 tree-vect-generic.o : tree-vect-generic.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
     $(TM_H) $(TREE_FLOW_H) $(GIMPLE_H) tree-iterator.h $(TREE_PASS_H) \
     $(FLAGS_H) $(OPTABS_H) $(MACHMODE_H) $(EXPR_H) \
@@ -3746,6 +3750,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \
   $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c \
+  $(srcdir)/tree-emutls.c \
   $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \
   $(srcdir)/tree-chrec.h \
   $(srcdir)/tree-scalar-evolution.c \
diff --git a/gcc/expr.c b/gcc/expr.c
index 7788461..07e2d7e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6819,20 +6819,6 @@ highest_pow2_factor_for_target (const_tree target, const_tree exp)
   return MAX (factor, talign);
 }
 \f
-/* 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 (UNKNOWN_LOCATION, fn, arglist);
-  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
-}
-\f
-
 /* Subroutine of expand_expr.  Expand the two operands of a binary
    expression EXP0 and EXP1 placing the results in OP0 and OP1.
    The value may be stored in TARGET if TARGET is nonzero.  The
@@ -6935,18 +6921,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
       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
@@ -8384,16 +8358,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 	  && (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_loc (loc, emutls_var_address (exp));
-	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
-	}
-
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
diff --git a/gcc/output.h b/gcc/output.h
index d1e5f24..1756efc 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -165,9 +165,6 @@ 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);
-
 /* Return the default TLS model for a given variable.  */
 extern enum tls_model decl_default_tls_model (const_tree);
 
diff --git a/gcc/passes.c b/gcc/passes.c
index 8828967..781420c 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -806,6 +806,7 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_ipa_increase_alignment);
   NEXT_PASS (pass_ipa_matrix_reorg);
+  NEXT_PASS (pass_ipa_lower_emutls);
   *p = NULL;
 
   p = &all_regular_ipa_passes;
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 964669f..60ca5dc 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -992,11 +992,6 @@ compile_file (void)
   if (seen_error ())
     return;
 
-  /* Ensure that emulated TLS control vars are finalized and build 
-     a static constructor for them, when it is required.  */
-  if (!targetm.have_tls)
-    emutls_finish ();
-
   varpool_assemble_pending_decls ();
   finish_aliases_2 ();
 
diff --git a/gcc/tree-emutls.c b/gcc/tree-emutls.c
new file mode 100644
index 0000000..fedbaad
--- /dev/null
+++ b/gcc/tree-emutls.c
@@ -0,0 +1,628 @@
+/* Lower TLS operations to emulation functions.
+   Copyright (C) 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
+
+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 3, or (at your option) any
+later version.
+
+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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "tree-flow.h"
+#include "cgraph.h"
+#include "langhooks.h"
+#include "target.h"
+
+/* ??? Should go.  */
+#include "targhooks.h"
+#include "tree-iterator.h"
+#include "output.h"
+
+/* TODO: Get rid of the EMUTLS hooks that no one uses.  */
+
+/* Whenever a target does not support thread-local storage (TLS) natively,
+   we can emulate it with some run-time support in libgcc.  This will in
+   turn rely on "keyed storage" a-la pthread_key_create; essentially all
+   thread libraries provide such functionality.
+
+   In order to coordinate with the libgcc runtime, each TLS variable is
+   described by a "control variable".  This control variable records the
+   required size, alignment, and initial value of the TLS variable for
+   instantiation at runtime.  It also stores an integer token to be used
+   by the runtime to find the address of the variable within each thread.
+
+   On the compiler side, this means that we need to replace all instances
+   of "tls_var" in the code with "*__emutls_get_addr(&control_var)".  We
+   also need to eliminate "tls_var" from the symbol table and introduce
+   "control_var".
+
+   We used to perform all of the transformations during conversion to rtl,
+   and the variable substitutions magically within assemble_variable.
+   However, this late fiddling of the symbol table conflicts with LTO and
+   whole-program compilation.  Therefore we must now make all the changes
+   to the symbol table early in the GIMPLE optimization path, before we
+   write things out to LTO intermediate files.  */
+
+/* These two vectors, once fully populated, are kept in lock-step so that
+   the index of a TLS variable equals the index of its control variable in
+   the other vector.  */
+static GTY (()) varpool_node_set tls_vars;
+static GTY (()) varpool_node_set control_vars;
+
+/* The type of the control structure, shared with the emutls.c runtime.  */
+/* ??? See if we can eliminate the one query via decl_emutls_var_p from
+   varasm.c.  With that gone, this need not be live outside the ipa pass.  */
+static GTY (()) tree emutls_object_type;
+
+/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
+   macro can be used on them to distinguish the control variable from
+   the initialization template.  */
+
+bool
+decl_emutls_var_p(const_tree decl)
+{
+  return TREE_TYPE (decl) == emutls_object_type;
+}
+
+#if !defined (NO_DOT_IN_LABEL)
+# define EMUTLS_SEPARATOR	"."
+#elif !defined (NO_DOLLAR_IN_LABEL)
+# define EMUTLS_SEPARATOR	"$"
+#else
+# define EMUTLS_SEPARATOR	"_"
+#endif
+
+/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
+   IDENTIFIER_NODE NAME's name.  */
+
+static tree
+prefix_name (const char *prefix, tree name)
+{
+  unsigned plen = strlen (prefix);
+  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
+  char *toname = (char *) alloca (plen + nlen + 1);
+
+  memcpy (toname, prefix, plen);
+  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
+
+  return get_identifier (toname);
+}
+
+/* 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)
+{
+  const char *prefix = (targetm.emutls.var_prefix
+			? targetm.emutls.var_prefix
+			: "__emutls_v" EMUTLS_SEPARATOR);
+  return prefix_name (prefix, name);
+}
+
+tree
+default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
+{
+  tree word_type_node, field, next_field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
+  DECL_CONTEXT (field) = type;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      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 (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__align"),
+		      word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__size"), word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+
+  return field;
+}
+
+/* Initialize emulated tls object TO, which refers to TLS variable
+   DECL and is initialized by PROXY.  */
+
+tree
+default_emutls_var_init (tree to, tree decl, tree proxy)
+{
+  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 = proxy;
+
+  return build_constructor (type, v);
+}
+
+/* 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;
+
+  type = emutls_object_type;
+  if (type)
+    return type;
+
+  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
+  type_name = NULL;
+  field = targetm.emutls.var_fields (type, &type_name);
+  if (!type_name)
+    type_name = get_identifier ("__emutls_object");
+  type_name = build_decl (UNKNOWN_LOCATION,
+			  TYPE_DECL, type_name, type);
+  TYPE_NAME (type) = type_name;
+  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;
+
+  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
+      && !DECL_SECTION_NAME (decl))
+    return null_pointer_node;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
+    {
+      const char *prefix = (targetm.emutls.tmpl_prefix
+			    ? targetm.emutls.tmpl_prefix
+			    : "__emutls_t" EMUTLS_SEPARATOR);
+      name = prefix_name (prefix, name);
+    }
+
+  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  if (DECL_ONE_ONLY (decl))
+    {
+      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_INITIAL (to) = DECL_INITIAL (decl);
+  DECL_INITIAL (decl) = NULL;
+
+  varpool_finalize_decl (to);
+  return build_fold_addr_expr (to);
+}
+
+/* Create and return the control variable for the TLS variable DECL.  */
+
+static tree
+new_emutls_decl (tree decl)
+{
+  tree name, to;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  to = build_decl (DECL_SOURCE_LOCATION (decl), VAR_DECL,
+                   get_emutls_object_name (name),
+                   get_emutls_object_type ());
+
+  SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
+
+  DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
+  DECL_ARTIFICIAL (to) = 1;
+  DECL_IGNORED_P (to) = 1;
+  TREE_READONLY (to) = 0;
+  TREE_STATIC (to) = 1;
+
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+  DECL_CONTEXT (to) = DECL_CONTEXT (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);
+  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
+  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
+
+  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
+
+  if (DECL_ONE_ONLY (decl))
+    make_decl_one_only (to, DECL_ASSEMBLER_NAME (to));
+
+  /* ??? What in the world is this for?  */
+  if (targetm.emutls.var_align_fixed)
+    /* If we're not allowed to change the proxy object's
+       alignment, pretend it's been set by the user.  */
+    DECL_USER_ALIGN (to) = 1;
+
+  /* If this variable is defined locally, then we need to initialize the
+     control structure with size and alignment information.  Initialization
+     of COMMON block variables happens elsewhere via a constructor.  */
+  if (!DECL_EXTERNAL (to)
+      && (!DECL_COMMON (to)
+          || (DECL_INITIAL (decl)
+              && DECL_INITIAL (decl) != error_mark_node)))
+    {
+      tree tmpl = get_emutls_init_templ_addr (decl);
+      DECL_INITIAL (to) = targetm.emutls.var_init (to, decl, tmpl);
+      record_references_in_initializer (to, false);
+    }
+
+  varpool_finalize_decl (to);
+  return to;
+}
+
+/* Look up the control variable for the TLS variable DECL.  */
+
+static struct varpool_node *
+emutls_node (tree decl)
+{
+  varpool_node_set_iterator i;
+  struct varpool_node *var;
+  
+  i = varpool_node_set_find (tls_vars, varpool_get_node (decl));
+  if (i.index == ~0u)
+    return NULL;
+
+  var = VEC_index (varpool_node_ptr, control_vars->nodes, i.index);
+  return var;
+}
+
+/* ??? The only remaining user is in dwarf2out.c.  Figure out how to
+   eliminate that too.  */
+
+tree
+emutls_decl (tree decl)
+{
+  struct varpool_node *var = emutls_node (decl);
+  return var->decl;
+}
+
+/* Generate a call statement to initialize CONTROL_DECL for TLS_DECL.
+   This only needs to happen for TLS COMMON variables; non-COMMON
+   variables can be initialized statically.  Insert the generated
+   call statement at the end of PSTMTS.  */
+   
+static void
+emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts)
+{
+  tree args, x;
+  tree word_type_node;
+
+  if (! DECL_COMMON (tls_decl)
+      || (DECL_INITIAL (tls_decl)
+	  && DECL_INITIAL (tls_decl) != error_mark_node))
+    return;
+
+  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+
+  x = get_emutls_init_templ_addr (tls_decl);
+  args = tree_cons (NULL, x, NULL);
+  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = fold_convert (word_type_node, DECL_SIZE_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = build_fold_addr_expr (control_decl);
+  args = tree_cons (NULL, x, args);
+
+  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
+  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);
+
+  append_to_statement_list (x, pstmts);
+}
+
+struct lower_emutls_data
+{
+  struct cgraph_node *cfun_node;
+  struct cgraph_node *builtin_node;
+  tree builtin_decl;
+  basic_block bb;
+  int bb_freq;
+  gimple_stmt_iterator gsi;
+  gimple stmt;
+};
+
+/* Given an operand *PTR within STMT at *GSI, if the operand references
+   a TLS variable, then lower the reference to a call to the runtime.  */
+
+static bool
+lower_emutls_1 (struct lower_emutls_data *d, tree *ptr)
+{
+  tree t = *ptr;
+  struct varpool_node *cvar;
+  tree cdecl, addr;
+  gimple x;
+
+  /* Look through all components.  */
+  while (handled_component_p (t))
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* Note that MEM_REF is not a "component" per-se, but may contain
+     ADDR_EXPR of a symbol, which we need to handle.  */
+  if (TREE_CODE (t) == MEM_REF)
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* In the case of "&var" we don't want to generate "&*addr",
+     we'd prefer to simply emit "addr".  */
+  if (TREE_CODE (t) == ADDR_EXPR)
+    {
+      if (lower_emutls_1 (d, &TREE_OPERAND (t, 0)))
+	{
+	  if (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
+	      && integer_zerop (TREE_OPERAND (TREE_OPERAND (t, 0), 1)))
+	    *ptr = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+	  return true;
+	}
+      return false;
+    }
+
+  /* If at the end we don't have a TLS variable, nothing to do.  */
+  if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t))
+    return false;
+
+  /* Compute the address of the TLS variable with help from runtime.  */
+  cvar = emutls_node (t);
+  cdecl = cvar->decl;
+
+  TREE_ADDRESSABLE (cdecl) = 1;
+  addr = create_tmp_var (build_pointer_type (TREE_TYPE (t)), NULL);
+  x = gimple_build_call (d->builtin_decl, 1, build_fold_addr_expr (cdecl));
+  gsi_insert_before (&d->gsi, x, GSI_SAME_STMT);
+  gimple_set_location (x, gimple_location (d->stmt));
+
+  addr = make_ssa_name (addr, x);
+  gimple_call_set_lhs (x, addr);
+
+  cgraph_create_edge (d->cfun_node, d->builtin_node, x,
+                      d->bb->count, d->bb_freq, d->bb->loop_depth);
+
+  /* Replace "var" with "*addr" in the statement.  */
+  t = build2 (MEM_REF, TREE_TYPE (t), addr,
+	      build_int_cst (TREE_TYPE (addr), 0));
+  *ptr = t;
+  gimple_set_modified (d->stmt, true);
+
+  /* We may be adding a new reference to a new variable to the function.
+     This means we have to play with the ipa-reference web.  */
+  ipa_record_reference (d->cfun_node, NULL, NULL, cvar, IPA_REF_ADDR, x);
+
+  return true;
+}
+
+static void
+lower_emutls_function_body (struct cgraph_node *node)
+{
+  struct lower_emutls_data d;
+
+  current_function_decl = node->decl;
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+
+  d.cfun_node = node;
+  d.builtin_decl = built_in_decls[BUILT_IN_EMUTLS_GET_ADDRESS];
+  d.builtin_node = cgraph_node (d.builtin_decl);
+
+  FOR_EACH_BB (d.bb)
+    {
+      d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb);
+
+      for (d.gsi = gsi_start_bb (d.bb); !gsi_end_p (d.gsi); gsi_next (&d.gsi))
+	{
+	  gimple stmt = gsi_stmt (d.gsi);
+
+          d.stmt = stmt;
+	  if (gimple_assign_single_p (stmt))
+	    {
+	      lower_emutls_1 (&d, gimple_assign_lhs_ptr (stmt));
+	      lower_emutls_1 (&d, gimple_assign_rhs1_ptr (stmt));
+	    }
+	  else if (is_gimple_call (stmt))
+	    {
+	      unsigned i, n = gimple_call_num_args (stmt);
+	      for (i = 0; i < n; ++i)
+		lower_emutls_1 (&d, gimple_call_arg_ptr (stmt, i));
+	      if (gimple_call_lhs (stmt))
+	        lower_emutls_1 (&d, gimple_call_lhs_ptr (stmt));
+	    }
+	  else
+	    continue;
+
+	  update_stmt_if_modified (stmt);
+	}
+    }
+
+  pop_cfun ();
+  current_function_decl = NULL;
+}
+
+static unsigned int
+ipa_lower_emutls (void)
+{
+  struct varpool_node *var;
+  struct cgraph_node *func;
+  bool any_aliases = false;
+  tree ctor_body = NULL;
+  unsigned int i;
+
+  tls_vars = varpool_node_set_new ();
+
+  /* Examine all global variables for TLS variables.  */
+  for (var = varpool_nodes; var ; var = var->next)
+    if (DECL_THREAD_LOCAL_P (var->decl))
+      {
+        /* ??? We really should be more consistent about setting these
+	   sorts of flags.  TREE_STATIC != C "static" keyword, and thus
+	   it should be set *with* DECL_EXTERNAL.  */
+	gcc_checking_assert (TREE_STATIC (var->decl)
+			     || DECL_EXTERNAL (var->decl));
+        if (var->alias)
+	  any_aliases = true;
+        else
+	  varpool_node_set_add (tls_vars, var);
+      }
+
+  /* If we found no TLS variables, then there is no further work to do.  */
+  if (tls_vars->nodes == NULL)
+    {
+      tls_vars = NULL;
+      if (dump_file)
+	fprintf (dump_file, "No TLS variables found.\n");
+      return 0;
+    }
+
+  /* If there were any aliases, add them to the set last.  In this way
+     when we create the control variables the control variable for the
+     alias base will have been created first.  */
+  if (any_aliases)
+    for (var = varpool_nodes; var ; var = var->next)
+      if (DECL_THREAD_LOCAL_P (var->decl) && var->alias)
+        varpool_node_set_add (tls_vars, var);
+
+  /* Create the control variables for each TLS variable.  */
+  control_vars = varpool_node_set_new ();
+  for (i = 0; VEC_iterate (varpool_node_ptr, tls_vars->nodes, i, var); ++i)
+    {
+      tree cdecl;
+      struct varpool_node *cvar;
+
+      var = VEC_index (varpool_node_ptr, tls_vars->nodes, i);
+      cdecl = new_emutls_decl (var->decl);
+
+      /* If there was an alias between the TLS variables,
+         mirror that in the control variables.  */
+      /* ??? There appears to be an alias_pairs data structure that
+         holds similar information.  */
+      if (var->alias)
+        {
+	  tree cbase = emutls_decl (var->extra_name->decl);
+          bool ok = varpool_extra_name_alias (cdecl, cbase);
+          gcc_assert (ok);
+        }
+
+      cvar = varpool_get_node (cdecl);
+      varpool_node_set_add (control_vars, cvar);
+
+      emutls_common_1 (var->decl, cdecl, &ctor_body);
+
+      /* Indicate that the value of the TLS variable may be found elsewhere.
+	 This also prevents the variable from re-appearing in the GIMPLE.  */
+      /* ??? Unfortuantely, there's no decent actual value to put here;
+	 there's nothing we can emit for the debugger at the moment.  */
+      SET_DECL_VALUE_EXPR (var->decl, error_mark_node);
+      DECL_HAS_VALUE_EXPR_P (var->decl) = 1;
+    }
+
+  /* Adjust all uses of TLS variables within the function bodies.  */
+  for (func = cgraph_nodes; func; func = func->next)
+    if (func->reachable && func->lowered)
+      lower_emutls_function_body (func);
+
+  /* Generate the constructor for any COMMON control variables created.  */
+  if (ctor_body)
+    cgraph_build_static_cdtor ('I', ctor_body, DEFAULT_INIT_PRIORITY);
+
+  return TODO_dump_func | TODO_ggc_collect | TODO_verify_stmts;
+}
+
+/* If the target supports TLS natively, we need do nothing here.  */
+
+static bool
+gate_emutls (void)
+{
+  return !targetm.have_tls;
+}
+
+struct simple_ipa_opt_pass pass_ipa_lower_emutls =
+{
+ {
+  SIMPLE_IPA_PASS,
+  "emutls",				/* name */
+  gate_emutls,				/* gate */
+  ipa_lower_emutls,			/* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_NONE,				/* tv_id */
+  PROP_cfg,				/* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  0,					/* todo_flags_finish */
+ }
+};
+
+#include "gt-tree-emutls.h"
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a4c97b3..b309be8 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -445,6 +445,7 @@ extern struct gimple_opt_pass pass_warn_unused_result;
 extern struct gimple_opt_pass pass_split_functions;
 
 /* IPA Passes */
+extern struct simple_ipa_opt_pass pass_ipa_lower_emutls;
 extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility;
 extern struct simple_ipa_opt_pass pass_ipa_early_inline;
 
diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c
index 5044aff..d874d6a 100644
--- a/gcc/tree-ssa-forwprop.c
+++ b/gcc/tree-ssa-forwprop.c
@@ -1058,6 +1058,11 @@ forward_propagate_addr_expr (tree name, tree rhs)
   bool all = true;
   bool single_use_p = has_single_use (name);
 
+  /* Certain addresses, including emulated TLS and DLLIMPORT, are
+     excluded from "invariant" and cannot be propagated at will.  */
+  if (!is_gimple_invariant_address (rhs))
+    return false;
+
   FOR_EACH_IMM_USE_STMT (use_stmt, iter, name)
     {
       bool result;
diff --git a/gcc/tree.c b/gcc/tree.c
index cca171c..d46fe2f 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -2466,9 +2466,14 @@ decl_address_invariant_p (const_tree op)
       return true;
 
     case VAR_DECL:
+      /* We exclude emulated TLS addresses to prevent them from being
+	 propagated into PHI arguments, where it would become significantly
+	 more difficult to lower them.  */
+      if (DECL_THREAD_LOCAL_P (op))
+	return targetm.have_tls;
+      /* ??? Explain why DLLIMPORT addresses are special cased.  */
       if (((TREE_STATIC (op) || DECL_EXTERNAL (op))
            && !DECL_DLLIMPORT_P (op))
-          || DECL_THREAD_LOCAL_P (op)
           || DECL_CONTEXT (op) == current_function_decl
           || decl_function_context (op) == current_function_decl)
         return true;
diff --git a/gcc/tree.h b/gcc/tree.h
index 960ee7d..d9687ae 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5257,9 +5257,12 @@ extern void set_user_assembler_name (tree, const char *);
 extern void process_pending_assemble_externals (void);
 extern void finish_aliases_1 (void);
 extern void finish_aliases_2 (void);
-extern tree emutls_decl (tree);
 extern void remove_unreachable_alias_pairs (void);
 
+/* tree-emutls.c */
+extern tree emutls_decl (tree);
+extern bool decl_emutls_var_p (const_tree);
+
 /* In stmt.c */
 extern void expand_computed_goto (tree);
 extern bool parse_output_constraint (const char **, int, int, int,
diff --git a/gcc/varasm.c b/gcc/varasm.c
index de78bd0..078efc2 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -186,320 +186,6 @@ 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;
-/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
-   macro can be used on them to distinguish the control variable from
-   the initialization template.  */
-#define DECL_EMUTLS_VAR_P(D)  (TREE_TYPE (D) == emutls_object_type)
-
-#if !defined (NO_DOT_IN_LABEL)
-# define EMUTLS_SEPARATOR	"."
-#elif !defined (NO_DOLLAR_IN_LABEL)
-# define EMUTLS_SEPARATOR	"$"
-#else
-# define EMUTLS_SEPARATOR	"_"
-#endif
-
-/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
-   IDENTIFIER_NODE NAME's name.  */
-
-static tree
-prefix_name (const char *prefix, tree name)
-{
-  unsigned plen = strlen (prefix);
-  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
-  char *toname = (char *) alloca (plen + nlen + 1);
-
-  memcpy (toname, prefix, plen);
-  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
-
-  return get_identifier (toname);
-}
-
-/* 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)
-{
-  const char *prefix = (targetm.emutls.var_prefix
-			? targetm.emutls.var_prefix
-			: "__emutls_v" EMUTLS_SEPARATOR);
-  return prefix_name (prefix, name);
-}
-
-tree
-default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
-{
-  tree word_type_node, field, next_field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
-  DECL_CONTEXT (field) = type;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      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 (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__align"),
-		      word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__size"), word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-
-  return field;
-}
-
-/* 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;
-
-  type = emutls_object_type;
-  if (type)
-    return type;
-
-  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
-  type_name = NULL;
-  field = targetm.emutls.var_fields (type, &type_name);
-  if (!type_name)
-    type_name = get_identifier ("__emutls_object");
-  type_name = build_decl (UNKNOWN_LOCATION,
-			  TYPE_DECL, type_name, type);
-  TYPE_NAME (type) = type_name;
-  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;
-
-  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
-      && !DECL_SECTION_NAME (decl))
-    return null_pointer_node;
-
-  name = DECL_ASSEMBLER_NAME (decl);
-  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
-    {
-      const char *prefix = (targetm.emutls.tmpl_prefix
-			    ? targetm.emutls.tmpl_prefix
-			    : "__emutls_t" EMUTLS_SEPARATOR);
-      name = prefix_name (prefix, name);
-    }
-
-  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
-  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
-
-  DECL_WEAK (to) = DECL_WEAK (decl);
-  if (DECL_ONE_ONLY (decl))
-    {
-      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  DECL_INITIAL (to) = DECL_INITIAL (decl);
-  DECL_INITIAL (decl) = NULL;
-
-  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 = IDENTIFIER_HASH_VALUE (name);
-  in.base.from = decl;
-  loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT);
-  h = (struct tree_map *) *loc;
-  if (h != NULL)
-    to = h->to;
-  else
-    {
-      to = build_decl (DECL_SOURCE_LOCATION (decl),
-		       VAR_DECL, get_emutls_object_name (name),
-		       get_emutls_object_type ());
-
-      h = ggc_alloc_tree_map ();
-      h->hash = in.hash;
-      h->base.from = decl;
-      h->to = to;
-      *(struct tree_map **) loc = h;
-
-      DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
-      DECL_ARTIFICIAL (to) = 1;
-      DECL_IGNORED_P (to) = 1;
-      /* FIXME: work around PR44132.  */
-      DECL_PRESERVE_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_ASSEMBLER_NAME (to));
-      DECL_CONTEXT (to) = DECL_CONTEXT (decl);
-      if (targetm.emutls.var_align_fixed)
-	/* If we're not allowed to change the proxy object's
-	   alignment, pretend it's been set by the user.  */
-	DECL_USER_ALIGN (to) = 1;
-    }
-
-  /* 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.  */
-  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
-  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
-
-  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);
-  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  
-  /* Fortran might pass this to us.  */
-  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (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->base.from)
-      || (DECL_INITIAL (h->base.from)
-	  && DECL_INITIAL (h->base.from) != error_mark_node))
-    return 1;
-
-  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
-
-  /* The idea was to call get_emutls_init_templ_addr here, but if we
-     do this and there is an initializer, -fanchor_section loses,
-     because it would be too late to ensure the template is
-     output.  */
-  x = null_pointer_node;
-  args = tree_cons (NULL, x, NULL);
-  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
-  args = tree_cons (NULL, x, args);
-  x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->base.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 (UNKNOWN_LOCATION, x, args);
-
-  append_to_statement_list (x, pstmts);
-  return 1;
-}
-
-/* Callback to finalize one emutls control variable.  */
-
-static int
-emutls_finalize_control_var (void **loc, 
-				void *unused ATTRIBUTE_UNUSED)
-{
-  struct tree_map *h = *(struct tree_map **) loc;
-  if (h != NULL) 
-    {
-      struct varpool_node *node = varpool_node (h->to);
-      /* Because varpool_finalize_decl () has side-effects,
-         only apply to un-finalized vars.  */
-      if (node && !node->finalized) 
-	varpool_finalize_decl (h->to);
-    }
-  return 1;
-}
-
-/* Finalize emutls control vars and add a static constructor if
-   required.  */
-
-void
-emutls_finish (void)
-{
-  if (emutls_htab == NULL)
-    return;
-  htab_traverse_noresize (emutls_htab, 
-			  emutls_finalize_control_var, NULL);
-
-  if (targetm.emutls.register_common)
-    {
-      tree body = NULL_TREE;
-
-      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
@@ -1213,11 +899,6 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
 		  && ADDR_SPACE_GENERIC_P (as));
       if (DECL_THREAD_LOCAL_P (decl))
 	return tls_comm_section;
-      /* This cannot be common bss for an emulated TLS object without
-	 a register_common hook.  */
-      else if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
-	       && !targetm.emutls.register_common)
-	;
       else if (TREE_PUBLIC (decl) && bss_initializer_p (decl))
 	return comm_section;
     }
@@ -2101,40 +1782,6 @@ assemble_variable_contents (tree decl, const char *name,
     }
 }
 
-/* Initialize emulated tls object TO, which refers to TLS variable
-   DECL and is initialized by PROXY.  */
-
-tree
-default_emutls_var_init (tree to, tree decl, tree proxy)
-{
-  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 = proxy;
-
-  return build_constructor (type, v);
-}
-
 /* Assemble everything that is needed for a variable or function declaration.
    Not used for automatic variables, and not used for function definitions.
    Should not be called for variables of incomplete structure type.
@@ -2153,35 +1800,6 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
   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)
-	      || (DECL_INITIAL (decl)
-		  && DECL_INITIAL (decl) != error_mark_node)))
-	{
-	  DECL_INITIAL (to) = targetm.emutls.var_init
-	    (to, decl, get_emutls_init_templ_addr (decl));
-
-	  /* Make sure the template is marked as needed early enough.
-	     Without this, if the variable is placed in a
-	     section-anchored block, the template will only be marked
-	     when it's too late.  */
-	  record_references_in_initializer (to, false);
-	}
-
-      decl = to;
-    }
-
   last_assemble_variable_decl = 0;
 
   /* Normally no need to say anything here for external references,
@@ -2204,6 +1822,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
       return;
     }
 
+  /* Emulated TLS had better not get this far.  */
+  gcc_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl));
+
   /* If type was incomplete when the variable was declared,
      see if it is complete now.  */
 
@@ -5691,6 +5312,11 @@ find_decl_and_mark_needed (tree decl, tree target)
 static void
 do_assemble_alias (tree decl, tree target)
 {
+  /* Emulated TLS had better not get this var.  */
+  gcc_assert(!(!targetm.have_tls
+	       && TREE_CODE (decl) == VAR_DECL
+	       && DECL_THREAD_LOCAL_P (decl)));
+
   if (TREE_ASM_WRITTEN (decl))
     return;
 
@@ -5705,14 +5331,6 @@ do_assemble_alias (tree decl, tree target)
     {
       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);
 
@@ -5731,14 +5349,6 @@ do_assemble_alias (tree decl, tree target)
       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.  */
 
@@ -6410,7 +6020,7 @@ categorize_decl_for_section (const_tree decl, int reloc)
     {
       if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED)
 	{
-	  if (DECL_EMUTLS_VAR_P (decl))
+	  if (decl_emutls_var_p (decl))
 	    {
 	      if (targetm.emutls.var_section)
 		ret = SECCAT_EMUTLS_VAR;
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 94c949e..3843d9c 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -211,7 +211,9 @@ varpool_remove_node (struct varpool_node *node)
     }
   ipa_remove_all_references (&node->ref_list);
   ipa_remove_all_refering (&node->ref_list);
-  ggc_free (node);
+  /* ??? We need to remove the reference in emutls data structures.  Perhaps
+     it would be better to simply add the xref to the varpool node.  */
+  /* ggc_free (node); */
 }
 
 /* Dump given cgraph node.  */
@@ -346,17 +348,6 @@ decide_is_variable_needed (struct varpool_node *node, tree decl)
       && !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 (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_toplevel_reorder)

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 15:47               ` Richard Henderson
@ 2010-07-13 18:56                 ` Richard Henderson
  2010-07-13 20:01                   ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-13 18:56 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 329 bytes --]

Test #5, delta from #4.

Moving the generation of the constructor function from the end of
compilation into the IPA pass, while simplifying things generally,
revealed to latent bugs:

(1) The cdtor generator function failed to zero out all the state.
(2) Cgraph failed to run all of the required passes on the new function.


r~

[-- Attachment #2: emutls-5 --]
[-- Type: text/plain, Size: 1464 bytes --]

diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index da1f983..0d5e792 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -418,12 +418,17 @@ cgraph_process_new_functions (void)
 	  push_cfun (DECL_STRUCT_FUNCTION (fndecl));
 	  current_function_decl = fndecl;
 	  compute_inline_parameters (node);
-	  if ((cgraph_state == CGRAPH_STATE_IPA_SSA
-	      && !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
-	      /* When not optimizing, be sure we run early local passes anyway
-		 to expand OMP.  */
-	      || !optimize)
+
+	  /* ??? Honza: what's the real difference between IPA and IPA_SSA?
+	     We seem to be assuming that the "real" ipa passes require SSA
+	     but that the "small" ipa passes do not.  This is false.  Any
+	     new function created by a "small" ipa pass *must* have the
+	     early local passes run so that (at least) init_datastructures
+	     gets executed.  Failure to do so results in an immediate crash
+	     once we get to pass_all_optimizations.  */
+	  if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
 	    execute_pass_list (pass_early_local_passes.pass.sub);
+
 	  free_dominance_info (CDI_POST_DOMINATORS);
 	  free_dominance_info (CDI_DOMINATORS);
 	  pop_cfun ();
@@ -2064,7 +2069,9 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
 
   cgraph_add_new_function (decl, false);
   cgraph_mark_needed_node (cgraph_node (decl));
+
   set_cfun (NULL);
+  current_function_decl = NULL;
 }
 
 void

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 18:56                 ` Richard Henderson
@ 2010-07-13 20:01                   ` Richard Henderson
  2010-07-13 20:04                     ` Nathan Froyd
  2010-07-13 20:46                     ` Richard Guenther
  0 siblings, 2 replies; 35+ messages in thread
From: Richard Henderson @ 2010-07-13 20:01 UTC (permalink / raw)
  To: IainS; +Cc: GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 57 bytes --]

Test #6.  Final?

Aliases are supposed to work now.


r~

[-- Attachment #2: emutls-6 --]
[-- Type: text/plain, Size: 49786 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c6f199f..d609126 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1354,6 +1354,7 @@ OBJS-common = \
 	tree-diagnostic.o \
 	tree-dump.o \
 	tree-eh.o \
+	tree-emutls.o \
 	tree-if-conv.o \
 	tree-into-ssa.o \
 	tree-iterator.o \
@@ -3142,6 +3143,9 @@ tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
 tree-complex.o : tree-complex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
     $(TM_H) $(FLAGS_H) $(TREE_FLOW_H) $(GIMPLE_H) \
     tree-iterator.h $(TREE_PASS_H) tree-ssa-propagate.h $(DIAGNOSTIC_H)
+tree-emutls.o : tree-emutls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
+    $(GIMPLE_H) $(TREE_PASS_H) $(TREE_FLOW_H) $(CGRAPH_H) langhooks.h \
+    $(TARGET_H) targhooks.h tree-iterator.h output.h gt-tree-emutls.h
 tree-vect-generic.o : tree-vect-generic.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
     $(TM_H) $(TREE_FLOW_H) $(GIMPLE_H) tree-iterator.h $(TREE_PASS_H) \
     $(FLAGS_H) $(OPTABS_H) $(MACHMODE_H) $(EXPR_H) \
@@ -3746,6 +3750,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \
   $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c \
+  $(srcdir)/tree-emutls.c \
   $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \
   $(srcdir)/tree-chrec.h \
   $(srcdir)/tree-scalar-evolution.c \
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index da1f983..0d5e792 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -418,12 +418,17 @@ cgraph_process_new_functions (void)
 	  push_cfun (DECL_STRUCT_FUNCTION (fndecl));
 	  current_function_decl = fndecl;
 	  compute_inline_parameters (node);
-	  if ((cgraph_state == CGRAPH_STATE_IPA_SSA
-	      && !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
-	      /* When not optimizing, be sure we run early local passes anyway
-		 to expand OMP.  */
-	      || !optimize)
+
+	  /* ??? Honza: what's the real difference between IPA and IPA_SSA?
+	     We seem to be assuming that the "real" ipa passes require SSA
+	     but that the "small" ipa passes do not.  This is false.  Any
+	     new function created by a "small" ipa pass *must* have the
+	     early local passes run so that (at least) init_datastructures
+	     gets executed.  Failure to do so results in an immediate crash
+	     once we get to pass_all_optimizations.  */
+	  if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
 	    execute_pass_list (pass_early_local_passes.pass.sub);
+
 	  free_dominance_info (CDI_POST_DOMINATORS);
 	  free_dominance_info (CDI_DOMINATORS);
 	  pop_cfun ();
@@ -2064,7 +2069,9 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
 
   cgraph_add_new_function (decl, false);
   cgraph_mark_needed_node (cgraph_node (decl));
+
   set_cfun (NULL);
+  current_function_decl = NULL;
 }
 
 void
diff --git a/gcc/expr.c b/gcc/expr.c
index 7788461..07e2d7e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6819,20 +6819,6 @@ highest_pow2_factor_for_target (const_tree target, const_tree exp)
   return MAX (factor, talign);
 }
 \f
-/* 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 (UNKNOWN_LOCATION, fn, arglist);
-  return fold_convert (build_pointer_type (TREE_TYPE (var)), call);
-}
-\f
-
 /* Subroutine of expand_expr.  Expand the two operands of a binary
    expression EXP0 and EXP1 placing the results in OP0 and OP1.
    The value may be stored in TARGET if TARGET is nonzero.  The
@@ -6935,18 +6921,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
       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
@@ -8384,16 +8358,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 	  && (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_loc (loc, emutls_var_address (exp));
-	  return expand_expr_real_1 (exp, target, tmode, modifier, NULL);
-	}
-
       /* ... fall through ...  */
 
     case FUNCTION_DECL:
diff --git a/gcc/output.h b/gcc/output.h
index d1e5f24..1756efc 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -165,9 +165,6 @@ 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);
-
 /* Return the default TLS model for a given variable.  */
 extern enum tls_model decl_default_tls_model (const_tree);
 
diff --git a/gcc/passes.c b/gcc/passes.c
index 8828967..781420c 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -806,6 +806,7 @@ init_optimization_passes (void)
     }
   NEXT_PASS (pass_ipa_increase_alignment);
   NEXT_PASS (pass_ipa_matrix_reorg);
+  NEXT_PASS (pass_ipa_lower_emutls);
   *p = NULL;
 
   p = &all_regular_ipa_passes;
diff --git a/gcc/testsuite/gcc.dg/tls/thr-init-1.c b/gcc/testsuite/gcc.dg/tls/thr-init-1.c
new file mode 100644
index 0000000..de273d9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tls/thr-init-1.c
@@ -0,0 +1,8 @@
+/* { dg-require-effective-target tls } */
+/* { dg-do compile } */
+
+static __thread int fstat ;
+static __thread int fstat = 1 ;
+static __thread int fstat ;
+static __thread int fstat = 2; /* { dg-error "redefinition of 'fstat'" } */
+				/* { dg-message "note: previous definition of 'fstat' was here" "" { target *-*-* } 5 } */
diff --git a/gcc/testsuite/gcc.dg/tls/thr-init-2.c b/gcc/testsuite/gcc.dg/tls/thr-init-2.c
new file mode 100644
index 0000000..6d00d8c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tls/thr-init-2.c
@@ -0,0 +1,23 @@
+/* { dg-require-effective-target tls } */
+/* { dg-do run } */
+
+extern void abort() ;
+
+static __thread int fstat ;
+static __thread int fstat = 1;
+
+int test_code(int b)
+{
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ((a != 2) || (fstat != 2))
+    abort () ;
+  
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c b/gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c
new file mode 100644
index 0000000..89725c3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-require-effective-target tls } */
+
+extern int printf (char *,...);
+extern void abort() ;
+
+int test_code(int b)
+{
+static __thread int fstat = 1;
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ( a != 2 )
+    {
+      printf ("a=%d\n", a) ;
+      abort ();
+    }
+  
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c b/gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c
new file mode 100644
index 0000000..9d09319
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-require-effective-target tls } */
+
+extern int printf (char *,...);
+extern void abort() ;
+
+static __thread int fstat ;
+static __thread int fstat = 1;
+static __thread int fstat ;
+
+int test_code(int b)
+{
+  fstat += b ;
+  return fstat;
+}
+
+int main (int ac, char *av[])
+{
+  int a = test_code(1);
+  
+  if ( a != 2 || fstat != 2 )
+    {
+    printf ("a=%d fstat=%d\n", a, fstat) ;
+    abort ();
+    }
+  
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/tls/tls-test.c b/gcc/testsuite/gcc.dg/torture/tls/tls-test.c
new file mode 100644
index 0000000..a40e15e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/tls/tls-test.c
@@ -0,0 +1,51 @@
+/* { dg-do run }  */
+/* { dg-require-effective-target tls  }  */
+/* { dg-require-effective-target pthread } */
+
+#include <pthread.h>
+extern int printf (char *,...);
+__thread int a = 5; 
+int *volatile a_in_other_thread = (int *) 12345;
+
+static void *
+thread_func (void *arg)
+{
+  a_in_other_thread = &a;
+  a+=5;
+  *((int *) arg) = a;
+  return (void *)0;
+}
+
+int
+main ()
+{
+  pthread_t thread;
+  void *thread_retval;
+  int *volatile a_in_main_thread;
+  int *volatile again ;
+  int thr_a;
+
+  a_in_main_thread = &a;
+
+  if (pthread_create (&thread, (pthread_attr_t *)0, thread_func, &thr_a))
+    return 0;
+
+  if (pthread_join (thread, &thread_retval))
+    return 0;
+
+  again = &a;
+  if (again != a_in_main_thread)
+    {
+      printf ("FAIL: main thread addy changed from 0x%0x to 0x%0x\n", 
+		a_in_other_thread, again);
+      return 1;
+    }
+
+  if (a != 5 || thr_a != 10 || (a_in_other_thread == a_in_main_thread))
+    {
+      printf ("FAIL: a= %d, thr_a = %d Addr = 0x%0x\n", 
+		a, thr_a, a_in_other_thread);
+      return 1;
+    }
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/tls/tls.exp b/gcc/testsuite/gcc.dg/torture/tls/tls.exp
new file mode 100644
index 0000000..91c8843
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/tls/tls.exp
@@ -0,0 +1,36 @@
+#   Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+        $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 964669f..60ca5dc 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -992,11 +992,6 @@ compile_file (void)
   if (seen_error ())
     return;
 
-  /* Ensure that emulated TLS control vars are finalized and build 
-     a static constructor for them, when it is required.  */
-  if (!targetm.have_tls)
-    emutls_finish ();
-
   varpool_assemble_pending_decls ();
   finish_aliases_2 ();
 
diff --git a/gcc/tree-emutls.c b/gcc/tree-emutls.c
new file mode 100644
index 0000000..34b3f99
--- /dev/null
+++ b/gcc/tree-emutls.c
@@ -0,0 +1,629 @@
+/* Lower TLS operations to emulation functions.
+   Copyright (C) 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
+
+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 3, or (at your option) any
+later version.
+
+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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "tree-flow.h"
+#include "cgraph.h"
+#include "langhooks.h"
+#include "target.h"
+
+/* ??? Should go.  */
+#include "targhooks.h"
+#include "tree-iterator.h"
+#include "output.h"
+
+/* TODO: Get rid of the EMUTLS hooks that no one uses.  */
+
+/* Whenever a target does not support thread-local storage (TLS) natively,
+   we can emulate it with some run-time support in libgcc.  This will in
+   turn rely on "keyed storage" a-la pthread_key_create; essentially all
+   thread libraries provide such functionality.
+
+   In order to coordinate with the libgcc runtime, each TLS variable is
+   described by a "control variable".  This control variable records the
+   required size, alignment, and initial value of the TLS variable for
+   instantiation at runtime.  It also stores an integer token to be used
+   by the runtime to find the address of the variable within each thread.
+
+   On the compiler side, this means that we need to replace all instances
+   of "tls_var" in the code with "*__emutls_get_addr(&control_var)".  We
+   also need to eliminate "tls_var" from the symbol table and introduce
+   "control_var".
+
+   We used to perform all of the transformations during conversion to rtl,
+   and the variable substitutions magically within assemble_variable.
+   However, this late fiddling of the symbol table conflicts with LTO and
+   whole-program compilation.  Therefore we must now make all the changes
+   to the symbol table early in the GIMPLE optimization path, before we
+   write things out to LTO intermediate files.  */
+
+/* These two vectors, once fully populated, are kept in lock-step so that
+   the index of a TLS variable equals the index of its control variable in
+   the other vector.  */
+static GTY (()) varpool_node_set tls_vars;
+static GTY (()) varpool_node_set control_vars;
+
+/* The type of the control structure, shared with the emutls.c runtime.  */
+/* ??? See if we can eliminate the one query via decl_emutls_var_p from
+   varasm.c.  With that gone, this need not be live outside the ipa pass.  */
+static GTY (()) tree emutls_object_type;
+
+/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
+   macro can be used on them to distinguish the control variable from
+   the initialization template.  */
+
+bool
+decl_emutls_var_p(const_tree decl)
+{
+  return TREE_TYPE (decl) == emutls_object_type;
+}
+
+#if !defined (NO_DOT_IN_LABEL)
+# define EMUTLS_SEPARATOR	"."
+#elif !defined (NO_DOLLAR_IN_LABEL)
+# define EMUTLS_SEPARATOR	"$"
+#else
+# define EMUTLS_SEPARATOR	"_"
+#endif
+
+/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
+   IDENTIFIER_NODE NAME's name.  */
+
+static tree
+prefix_name (const char *prefix, tree name)
+{
+  unsigned plen = strlen (prefix);
+  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
+  char *toname = (char *) alloca (plen + nlen + 1);
+
+  memcpy (toname, prefix, plen);
+  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
+
+  return get_identifier (toname);
+}
+
+/* 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)
+{
+  const char *prefix = (targetm.emutls.var_prefix
+			? targetm.emutls.var_prefix
+			: "__emutls_v" EMUTLS_SEPARATOR);
+  return prefix_name (prefix, name);
+}
+
+tree
+default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
+{
+  tree word_type_node, field, next_field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
+  DECL_CONTEXT (field) = type;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      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 (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__align"),
+		      word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+  next_field = field;
+
+  field = build_decl (UNKNOWN_LOCATION,
+		      FIELD_DECL, get_identifier ("__size"), word_type_node);
+  DECL_CONTEXT (field) = type;
+  TREE_CHAIN (field) = next_field;
+
+  return field;
+}
+
+/* Initialize emulated tls object TO, which refers to TLS variable
+   DECL and is initialized by PROXY.  */
+
+tree
+default_emutls_var_init (tree to, tree decl, tree proxy)
+{
+  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 = proxy;
+
+  return build_constructor (type, v);
+}
+
+/* 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;
+
+  type = emutls_object_type;
+  if (type)
+    return type;
+
+  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
+  type_name = NULL;
+  field = targetm.emutls.var_fields (type, &type_name);
+  if (!type_name)
+    type_name = get_identifier ("__emutls_object");
+  type_name = build_decl (UNKNOWN_LOCATION,
+			  TYPE_DECL, type_name, type);
+  TYPE_NAME (type) = type_name;
+  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;
+
+  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
+      && !DECL_SECTION_NAME (decl))
+    return null_pointer_node;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
+    {
+      const char *prefix = (targetm.emutls.tmpl_prefix
+			    ? targetm.emutls.tmpl_prefix
+			    : "__emutls_t" EMUTLS_SEPARATOR);
+      name = prefix_name (prefix, name);
+    }
+
+  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+
+  DECL_WEAK (to) = DECL_WEAK (decl);
+  if (DECL_ONE_ONLY (decl))
+    {
+      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_INITIAL (to) = DECL_INITIAL (decl);
+  DECL_INITIAL (decl) = NULL;
+
+  varpool_finalize_decl (to);
+  return build_fold_addr_expr (to);
+}
+
+/* Create and return the control variable for the TLS variable DECL.  */
+
+static tree
+new_emutls_decl (tree decl)
+{
+  tree name, to;
+
+  name = DECL_ASSEMBLER_NAME (decl);
+  to = build_decl (DECL_SOURCE_LOCATION (decl), VAR_DECL,
+                   get_emutls_object_name (name),
+                   get_emutls_object_type ());
+
+  SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to));
+
+  DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
+  DECL_ARTIFICIAL (to) = 1;
+  DECL_IGNORED_P (to) = 1;
+  TREE_READONLY (to) = 0;
+  TREE_STATIC (to) = 1;
+
+  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
+  DECL_CONTEXT (to) = DECL_CONTEXT (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);
+  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
+  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl);
+  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
+
+  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
+
+  if (DECL_ONE_ONLY (decl))
+    make_decl_one_only (to, DECL_ASSEMBLER_NAME (to));
+
+  /* ??? What in the world is this for?  */
+  if (targetm.emutls.var_align_fixed)
+    /* If we're not allowed to change the proxy object's
+       alignment, pretend it's been set by the user.  */
+    DECL_USER_ALIGN (to) = 1;
+
+  /* If this variable is defined locally, then we need to initialize the
+     control structure with size and alignment information.  Initialization
+     of COMMON block variables happens elsewhere via a constructor.  */
+  if (!DECL_EXTERNAL (to)
+      && (!DECL_COMMON (to)
+          || (DECL_INITIAL (decl)
+              && DECL_INITIAL (decl) != error_mark_node)))
+    {
+      tree tmpl = get_emutls_init_templ_addr (decl);
+      DECL_INITIAL (to) = targetm.emutls.var_init (to, decl, tmpl);
+      record_references_in_initializer (to, false);
+    }
+
+  varpool_finalize_decl (to);
+  return to;
+}
+
+/* Look up the control variable for the TLS variable DECL.  */
+
+static struct varpool_node *
+emutls_node (tree decl)
+{
+  varpool_node_set_iterator i;
+  struct varpool_node *var;
+  
+  i = varpool_node_set_find (tls_vars, varpool_get_node (decl));
+  if (i.index == ~0u)
+    return NULL;
+
+  var = VEC_index (varpool_node_ptr, control_vars->nodes, i.index);
+  return var;
+}
+
+/* ??? The only remaining user is in dwarf2out.c.  Figure out how to
+   eliminate that too.  */
+
+tree
+emutls_decl (tree decl)
+{
+  struct varpool_node *var = emutls_node (decl);
+  return var->decl;
+}
+
+/* Generate a call statement to initialize CONTROL_DECL for TLS_DECL.
+   This only needs to happen for TLS COMMON variables; non-COMMON
+   variables can be initialized statically.  Insert the generated
+   call statement at the end of PSTMTS.  */
+   
+static void
+emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts)
+{
+  tree args, x;
+  tree word_type_node;
+
+  if (! DECL_COMMON (tls_decl)
+      || (DECL_INITIAL (tls_decl)
+	  && DECL_INITIAL (tls_decl) != error_mark_node))
+    return;
+
+  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+
+  x = get_emutls_init_templ_addr (tls_decl);
+  args = tree_cons (NULL, x, NULL);
+  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = fold_convert (word_type_node, DECL_SIZE_UNIT (tls_decl));
+  args = tree_cons (NULL, x, args);
+  x = build_fold_addr_expr (control_decl);
+  args = tree_cons (NULL, x, args);
+
+  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
+  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);
+
+  append_to_statement_list (x, pstmts);
+}
+
+struct lower_emutls_data
+{
+  struct cgraph_node *cfun_node;
+  struct cgraph_node *builtin_node;
+  tree builtin_decl;
+  basic_block bb;
+  int bb_freq;
+  gimple_stmt_iterator gsi;
+  gimple stmt;
+};
+
+/* Given an operand *PTR within STMT at *GSI, if the operand references
+   a TLS variable, then lower the reference to a call to the runtime.  */
+
+static bool
+lower_emutls_1 (struct lower_emutls_data *d, tree *ptr)
+{
+  tree t = *ptr;
+  struct varpool_node *cvar;
+  tree cdecl, addr;
+  gimple x;
+
+  /* Look through all components.  */
+  while (handled_component_p (t))
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* Note that MEM_REF is not a "component" per-se, but may contain
+     ADDR_EXPR of a symbol, which we need to handle.  */
+  if (TREE_CODE (t) == MEM_REF)
+    {
+      ptr = &TREE_OPERAND (t, 0);
+      t = *ptr;
+    }
+
+  /* In the case of "&var" we don't want to generate "&*addr",
+     we'd prefer to simply emit "addr".  */
+  if (TREE_CODE (t) == ADDR_EXPR)
+    {
+      if (lower_emutls_1 (d, &TREE_OPERAND (t, 0)))
+	{
+	  if (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
+	      && integer_zerop (TREE_OPERAND (TREE_OPERAND (t, 0), 1)))
+	    *ptr = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+	  return true;
+	}
+      return false;
+    }
+
+  /* If at the end we don't have a TLS variable, nothing to do.  */
+  if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t))
+    return false;
+
+  /* Compute the address of the TLS variable with help from runtime.  */
+  cvar = emutls_node (t);
+  cdecl = cvar->decl;
+
+  TREE_ADDRESSABLE (cdecl) = 1;
+  addr = create_tmp_var (build_pointer_type (TREE_TYPE (t)), NULL);
+  x = gimple_build_call (d->builtin_decl, 1, build_fold_addr_expr (cdecl));
+  gsi_insert_before (&d->gsi, x, GSI_SAME_STMT);
+  gimple_set_location (x, gimple_location (d->stmt));
+
+  addr = make_ssa_name (addr, x);
+  gimple_call_set_lhs (x, addr);
+
+  cgraph_create_edge (d->cfun_node, d->builtin_node, x,
+                      d->bb->count, d->bb_freq, d->bb->loop_depth);
+
+  /* Replace "var" with "*addr" in the statement.  */
+  t = build2 (MEM_REF, TREE_TYPE (t), addr,
+	      build_int_cst (TREE_TYPE (addr), 0));
+  *ptr = t;
+  gimple_set_modified (d->stmt, true);
+
+  /* We may be adding a new reference to a new variable to the function.
+     This means we have to play with the ipa-reference web.  */
+  ipa_record_reference (d->cfun_node, NULL, NULL, cvar, IPA_REF_ADDR, x);
+
+  return true;
+}
+
+static void
+lower_emutls_function_body (struct cgraph_node *node)
+{
+  struct lower_emutls_data d;
+
+  current_function_decl = node->decl;
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+
+  d.cfun_node = node;
+  d.builtin_decl = built_in_decls[BUILT_IN_EMUTLS_GET_ADDRESS];
+  d.builtin_node = cgraph_node (d.builtin_decl);
+
+  FOR_EACH_BB (d.bb)
+    {
+      d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb);
+
+      for (d.gsi = gsi_start_bb (d.bb); !gsi_end_p (d.gsi); gsi_next (&d.gsi))
+	{
+	  gimple stmt = gsi_stmt (d.gsi);
+
+          d.stmt = stmt;
+	  if (gimple_assign_single_p (stmt))
+	    {
+	      lower_emutls_1 (&d, gimple_assign_lhs_ptr (stmt));
+	      lower_emutls_1 (&d, gimple_assign_rhs1_ptr (stmt));
+	    }
+	  else if (is_gimple_call (stmt))
+	    {
+	      unsigned i, n = gimple_call_num_args (stmt);
+	      for (i = 0; i < n; ++i)
+		lower_emutls_1 (&d, gimple_call_arg_ptr (stmt, i));
+	      if (gimple_call_lhs (stmt))
+	        lower_emutls_1 (&d, gimple_call_lhs_ptr (stmt));
+	    }
+	  else
+	    continue;
+
+	  update_stmt_if_modified (stmt);
+	}
+    }
+
+  pop_cfun ();
+  current_function_decl = NULL;
+}
+
+static unsigned int
+ipa_lower_emutls (void)
+{
+  struct varpool_node *var;
+  struct cgraph_node *func;
+  bool any_aliases = false;
+  tree ctor_body = NULL;
+  unsigned int i;
+
+  tls_vars = varpool_node_set_new ();
+
+  /* Examine all global variables for TLS variables.  */
+  for (var = varpool_nodes; var ; var = var->next)
+    if (DECL_THREAD_LOCAL_P (var->decl))
+      {
+        /* ??? We really should be more consistent about setting these
+	   sorts of flags.  TREE_STATIC != C "static" keyword, and thus
+	   it should be set *with* DECL_EXTERNAL.  */
+	gcc_checking_assert (TREE_STATIC (var->decl)
+			     || DECL_EXTERNAL (var->decl));
+	varpool_node_set_add (tls_vars, var);
+      }
+
+  /* If we found no TLS variables, then there is no further work to do.  */
+  if (tls_vars->nodes == NULL)
+    {
+      tls_vars = NULL;
+      if (dump_file)
+	fprintf (dump_file, "No TLS variables found.\n");
+      return 0;
+    }
+
+  /* Create the control variables for each TLS variable.  */
+  control_vars = varpool_node_set_new ();
+  for (i = 0; VEC_iterate (varpool_node_ptr, tls_vars->nodes, i, var); ++i)
+    {
+      tree cdecl;
+      struct varpool_node *cvar;
+
+      var = VEC_index (varpool_node_ptr, tls_vars->nodes, i);
+      cdecl = new_emutls_decl (var->decl);
+
+      cvar = varpool_get_node (cdecl);
+      varpool_node_set_add (control_vars, cvar);
+
+      if (var->alias)
+	{
+	  any_aliases = true;
+	  cvar->alias = true;
+	}
+      else
+	{
+	  /* Make sure the COMMON block control variable gets initialized.
+	     Note that there's no point in doing this for aliases; we only
+	     need to do this once for the main variable.  */
+          emutls_common_1 (var->decl, cdecl, &ctor_body);
+	}
+
+      /* Indicate that the value of the TLS variable may be found elsewhere.
+	 This also prevents the variable from re-appearing in the GIMPLE.  */
+      /* ??? Unfortuantely, there's no decent actual value to put here;
+	 there's nothing we can emit for the debugger at the moment.  */
+      SET_DECL_VALUE_EXPR (var->decl, error_mark_node);
+      DECL_HAS_VALUE_EXPR_P (var->decl) = 1;
+    }
+
+  /* If there were any aliases, then frob the alias_pairs vector.  */
+  if (any_aliases)
+    {
+      alias_pair *p;
+      for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); ++i)
+	if (DECL_THREAD_LOCAL_P (p->decl))
+	  {
+	    p->decl = emutls_decl (p->decl);
+	    p->target = get_emutls_object_name (p->target);
+	  }
+    }
+
+  /* Adjust all uses of TLS variables within the function bodies.  */
+  for (func = cgraph_nodes; func; func = func->next)
+    if (func->reachable && func->lowered)
+      lower_emutls_function_body (func);
+
+  /* Generate the constructor for any COMMON control variables created.  */
+  if (ctor_body)
+    cgraph_build_static_cdtor ('I', ctor_body, DEFAULT_INIT_PRIORITY);
+
+  return TODO_dump_func | TODO_ggc_collect | TODO_verify_stmts;
+}
+
+/* If the target supports TLS natively, we need do nothing here.  */
+
+static bool
+gate_emutls (void)
+{
+  return !targetm.have_tls;
+}
+
+struct simple_ipa_opt_pass pass_ipa_lower_emutls =
+{
+ {
+  SIMPLE_IPA_PASS,
+  "emutls",				/* name */
+  gate_emutls,				/* gate */
+  ipa_lower_emutls,			/* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_NONE,				/* tv_id */
+  PROP_cfg,				/* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  0,					/* todo_flags_finish */
+ }
+};
+
+#include "gt-tree-emutls.h"
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a4c97b3..b309be8 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -445,6 +445,7 @@ extern struct gimple_opt_pass pass_warn_unused_result;
 extern struct gimple_opt_pass pass_split_functions;
 
 /* IPA Passes */
+extern struct simple_ipa_opt_pass pass_ipa_lower_emutls;
 extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility;
 extern struct simple_ipa_opt_pass pass_ipa_early_inline;
 
diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c
index 5044aff..d874d6a 100644
--- a/gcc/tree-ssa-forwprop.c
+++ b/gcc/tree-ssa-forwprop.c
@@ -1058,6 +1058,11 @@ forward_propagate_addr_expr (tree name, tree rhs)
   bool all = true;
   bool single_use_p = has_single_use (name);
 
+  /* Certain addresses, including emulated TLS and DLLIMPORT, are
+     excluded from "invariant" and cannot be propagated at will.  */
+  if (!is_gimple_invariant_address (rhs))
+    return false;
+
   FOR_EACH_IMM_USE_STMT (use_stmt, iter, name)
     {
       bool result;
diff --git a/gcc/tree.c b/gcc/tree.c
index cca171c..d46fe2f 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -2466,9 +2466,14 @@ decl_address_invariant_p (const_tree op)
       return true;
 
     case VAR_DECL:
+      /* We exclude emulated TLS addresses to prevent them from being
+	 propagated into PHI arguments, where it would become significantly
+	 more difficult to lower them.  */
+      if (DECL_THREAD_LOCAL_P (op))
+	return targetm.have_tls;
+      /* ??? Explain why DLLIMPORT addresses are special cased.  */
       if (((TREE_STATIC (op) || DECL_EXTERNAL (op))
            && !DECL_DLLIMPORT_P (op))
-          || DECL_THREAD_LOCAL_P (op)
           || DECL_CONTEXT (op) == current_function_decl
           || decl_function_context (op) == current_function_decl)
         return true;
diff --git a/gcc/tree.h b/gcc/tree.h
index 960ee7d..d9687ae 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5257,9 +5257,12 @@ extern void set_user_assembler_name (tree, const char *);
 extern void process_pending_assemble_externals (void);
 extern void finish_aliases_1 (void);
 extern void finish_aliases_2 (void);
-extern tree emutls_decl (tree);
 extern void remove_unreachable_alias_pairs (void);
 
+/* tree-emutls.c */
+extern tree emutls_decl (tree);
+extern bool decl_emutls_var_p (const_tree);
+
 /* In stmt.c */
 extern void expand_computed_goto (tree);
 extern bool parse_output_constraint (const char **, int, int, int,
diff --git a/gcc/varasm.c b/gcc/varasm.c
index de78bd0..078efc2 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -186,320 +186,6 @@ 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;
-/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED.  This
-   macro can be used on them to distinguish the control variable from
-   the initialization template.  */
-#define DECL_EMUTLS_VAR_P(D)  (TREE_TYPE (D) == emutls_object_type)
-
-#if !defined (NO_DOT_IN_LABEL)
-# define EMUTLS_SEPARATOR	"."
-#elif !defined (NO_DOLLAR_IN_LABEL)
-# define EMUTLS_SEPARATOR	"$"
-#else
-# define EMUTLS_SEPARATOR	"_"
-#endif
-
-/* Create an IDENTIFIER_NODE by prefixing PREFIX to the
-   IDENTIFIER_NODE NAME's name.  */
-
-static tree
-prefix_name (const char *prefix, tree name)
-{
-  unsigned plen = strlen (prefix);
-  unsigned nlen = strlen (IDENTIFIER_POINTER (name));
-  char *toname = (char *) alloca (plen + nlen + 1);
-
-  memcpy (toname, prefix, plen);
-  memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1);
-
-  return get_identifier (toname);
-}
-
-/* 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)
-{
-  const char *prefix = (targetm.emutls.var_prefix
-			? targetm.emutls.var_prefix
-			: "__emutls_v" EMUTLS_SEPARATOR);
-  return prefix_name (prefix, name);
-}
-
-tree
-default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED)
-{
-  tree word_type_node, field, next_field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
-  DECL_CONTEXT (field) = type;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      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 (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__align"),
-		      word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-  next_field = field;
-
-  field = build_decl (UNKNOWN_LOCATION,
-		      FIELD_DECL, get_identifier ("__size"), word_type_node);
-  DECL_CONTEXT (field) = type;
-  TREE_CHAIN (field) = next_field;
-
-  return field;
-}
-
-/* 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;
-
-  type = emutls_object_type;
-  if (type)
-    return type;
-
-  emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
-  type_name = NULL;
-  field = targetm.emutls.var_fields (type, &type_name);
-  if (!type_name)
-    type_name = get_identifier ("__emutls_object");
-  type_name = build_decl (UNKNOWN_LOCATION,
-			  TYPE_DECL, type_name, type);
-  TYPE_NAME (type) = type_name;
-  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;
-
-  if (targetm.emutls.register_common && !DECL_INITIAL (decl)
-      && !DECL_SECTION_NAME (decl))
-    return null_pointer_node;
-
-  name = DECL_ASSEMBLER_NAME (decl);
-  if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0])
-    {
-      const char *prefix = (targetm.emutls.tmpl_prefix
-			    ? targetm.emutls.tmpl_prefix
-			    : "__emutls_t" EMUTLS_SEPARATOR);
-      name = prefix_name (prefix, name);
-    }
-
-  to = build_decl (DECL_SOURCE_LOCATION (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_SECTION_NAME (to) = DECL_SECTION_NAME (decl);
-  DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl);
-
-  DECL_WEAK (to) = DECL_WEAK (decl);
-  if (DECL_ONE_ONLY (decl))
-    {
-      make_decl_one_only (to, DECL_ASSEMBLER_NAME (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_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  DECL_INITIAL (to) = DECL_INITIAL (decl);
-  DECL_INITIAL (decl) = NULL;
-
-  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 = IDENTIFIER_HASH_VALUE (name);
-  in.base.from = decl;
-  loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT);
-  h = (struct tree_map *) *loc;
-  if (h != NULL)
-    to = h->to;
-  else
-    {
-      to = build_decl (DECL_SOURCE_LOCATION (decl),
-		       VAR_DECL, get_emutls_object_name (name),
-		       get_emutls_object_type ());
-
-      h = ggc_alloc_tree_map ();
-      h->hash = in.hash;
-      h->base.from = decl;
-      h->to = to;
-      *(struct tree_map **) loc = h;
-
-      DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED;
-      DECL_ARTIFICIAL (to) = 1;
-      DECL_IGNORED_P (to) = 1;
-      /* FIXME: work around PR44132.  */
-      DECL_PRESERVE_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_ASSEMBLER_NAME (to));
-      DECL_CONTEXT (to) = DECL_CONTEXT (decl);
-      if (targetm.emutls.var_align_fixed)
-	/* If we're not allowed to change the proxy object's
-	   alignment, pretend it's been set by the user.  */
-	DECL_USER_ALIGN (to) = 1;
-    }
-
-  /* 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.  */
-  DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl);
-  DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to);
-
-  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);
-  DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl);
-  
-  /* Fortran might pass this to us.  */
-  DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (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->base.from)
-      || (DECL_INITIAL (h->base.from)
-	  && DECL_INITIAL (h->base.from) != error_mark_node))
-    return 1;
-
-  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
-
-  /* The idea was to call get_emutls_init_templ_addr here, but if we
-     do this and there is an initializer, -fanchor_section loses,
-     because it would be too late to ensure the template is
-     output.  */
-  x = null_pointer_node;
-  args = tree_cons (NULL, x, NULL);
-  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from));
-  args = tree_cons (NULL, x, args);
-  x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->base.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 (UNKNOWN_LOCATION, x, args);
-
-  append_to_statement_list (x, pstmts);
-  return 1;
-}
-
-/* Callback to finalize one emutls control variable.  */
-
-static int
-emutls_finalize_control_var (void **loc, 
-				void *unused ATTRIBUTE_UNUSED)
-{
-  struct tree_map *h = *(struct tree_map **) loc;
-  if (h != NULL) 
-    {
-      struct varpool_node *node = varpool_node (h->to);
-      /* Because varpool_finalize_decl () has side-effects,
-         only apply to un-finalized vars.  */
-      if (node && !node->finalized) 
-	varpool_finalize_decl (h->to);
-    }
-  return 1;
-}
-
-/* Finalize emutls control vars and add a static constructor if
-   required.  */
-
-void
-emutls_finish (void)
-{
-  if (emutls_htab == NULL)
-    return;
-  htab_traverse_noresize (emutls_htab, 
-			  emutls_finalize_control_var, NULL);
-
-  if (targetm.emutls.register_common)
-    {
-      tree body = NULL_TREE;
-
-      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
@@ -1213,11 +899,6 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
 		  && ADDR_SPACE_GENERIC_P (as));
       if (DECL_THREAD_LOCAL_P (decl))
 	return tls_comm_section;
-      /* This cannot be common bss for an emulated TLS object without
-	 a register_common hook.  */
-      else if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED
-	       && !targetm.emutls.register_common)
-	;
       else if (TREE_PUBLIC (decl) && bss_initializer_p (decl))
 	return comm_section;
     }
@@ -2101,40 +1782,6 @@ assemble_variable_contents (tree decl, const char *name,
     }
 }
 
-/* Initialize emulated tls object TO, which refers to TLS variable
-   DECL and is initialized by PROXY.  */
-
-tree
-default_emutls_var_init (tree to, tree decl, tree proxy)
-{
-  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 = proxy;
-
-  return build_constructor (type, v);
-}
-
 /* Assemble everything that is needed for a variable or function declaration.
    Not used for automatic variables, and not used for function definitions.
    Should not be called for variables of incomplete structure type.
@@ -2153,35 +1800,6 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
   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)
-	      || (DECL_INITIAL (decl)
-		  && DECL_INITIAL (decl) != error_mark_node)))
-	{
-	  DECL_INITIAL (to) = targetm.emutls.var_init
-	    (to, decl, get_emutls_init_templ_addr (decl));
-
-	  /* Make sure the template is marked as needed early enough.
-	     Without this, if the variable is placed in a
-	     section-anchored block, the template will only be marked
-	     when it's too late.  */
-	  record_references_in_initializer (to, false);
-	}
-
-      decl = to;
-    }
-
   last_assemble_variable_decl = 0;
 
   /* Normally no need to say anything here for external references,
@@ -2204,6 +1822,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,
       return;
     }
 
+  /* Emulated TLS had better not get this far.  */
+  gcc_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl));
+
   /* If type was incomplete when the variable was declared,
      see if it is complete now.  */
 
@@ -5691,6 +5312,11 @@ find_decl_and_mark_needed (tree decl, tree target)
 static void
 do_assemble_alias (tree decl, tree target)
 {
+  /* Emulated TLS had better not get this var.  */
+  gcc_assert(!(!targetm.have_tls
+	       && TREE_CODE (decl) == VAR_DECL
+	       && DECL_THREAD_LOCAL_P (decl)));
+
   if (TREE_ASM_WRITTEN (decl))
     return;
 
@@ -5705,14 +5331,6 @@ do_assemble_alias (tree decl, tree target)
     {
       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);
 
@@ -5731,14 +5349,6 @@ do_assemble_alias (tree decl, tree target)
       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.  */
 
@@ -6410,7 +6020,7 @@ categorize_decl_for_section (const_tree decl, int reloc)
     {
       if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED)
 	{
-	  if (DECL_EMUTLS_VAR_P (decl))
+	  if (decl_emutls_var_p (decl))
 	    {
 	      if (targetm.emutls.var_section)
 		ret = SECCAT_EMUTLS_VAR;
diff --git a/gcc/varpool.c b/gcc/varpool.c
index 94c949e..3843d9c 100644
--- a/gcc/varpool.c
+++ b/gcc/varpool.c
@@ -211,7 +211,9 @@ varpool_remove_node (struct varpool_node *node)
     }
   ipa_remove_all_references (&node->ref_list);
   ipa_remove_all_refering (&node->ref_list);
-  ggc_free (node);
+  /* ??? We need to remove the reference in emutls data structures.  Perhaps
+     it would be better to simply add the xref to the varpool node.  */
+  /* ggc_free (node); */
 }
 
 /* Dump given cgraph node.  */
@@ -346,17 +348,6 @@ decide_is_variable_needed (struct varpool_node *node, tree decl)
       && !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 (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_toplevel_reorder)

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 20:01                   ` Richard Henderson
@ 2010-07-13 20:04                     ` Nathan Froyd
  2010-07-13 21:20                       ` Richard Henderson
  2010-07-13 20:46                     ` Richard Guenther
  1 sibling, 1 reply; 35+ messages in thread
From: Nathan Froyd @ 2010-07-13 20:04 UTC (permalink / raw)
  To: Richard Henderson
  Cc: IainS, GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On Tue, Jul 13, 2010 at 01:01:06PM -0700, Richard Henderson wrote:
> +emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts)
> +{
> +  tree args, x;
> +  tree word_type_node;
> +
> +  if (! DECL_COMMON (tls_decl)
> +      || (DECL_INITIAL (tls_decl)
> +	  && DECL_INITIAL (tls_decl) != error_mark_node))
> +    return;
> +
> +  word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
> +
> +  x = get_emutls_init_templ_addr (tls_decl);
> +  args = tree_cons (NULL, x, NULL);
> +  x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (tls_decl));
> +  args = tree_cons (NULL, x, args);
> +  x = fold_convert (word_type_node, DECL_SIZE_UNIT (tls_decl));
> +  args = tree_cons (NULL, x, args);
> +  x = build_fold_addr_expr (control_decl);
> +  args = tree_cons (NULL, x, args);
> +
> +  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
> +  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);

Don't forget to update this for the removal of build_function_call_expr.

-Nathan

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 20:01                   ` Richard Henderson
  2010-07-13 20:04                     ` Nathan Froyd
@ 2010-07-13 20:46                     ` Richard Guenther
  2010-07-13 21:19                       ` Richard Henderson
  1 sibling, 1 reply; 35+ messages in thread
From: Richard Guenther @ 2010-07-13 20:46 UTC (permalink / raw)
  To: Richard Henderson
  Cc: IainS, GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On Tue, Jul 13, 2010 at 10:01 PM, Richard Henderson <rth@redhat.com> wrote:
> Test #6.  Final?
>
> Aliases are supposed to work now.

The forwprop piece disables propagating &p->foo like addresses which
is useful and important.  Also forwprop doesn't propagate into PHIs,
so I don't understand what's the issue here (CCP does, though).

Richard.

>
> r~
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 20:46                     ` Richard Guenther
@ 2010-07-13 21:19                       ` Richard Henderson
  2010-07-13 21:39                         ` Andrew Pinski
  2010-07-14  8:11                         ` Richard Guenther
  0 siblings, 2 replies; 35+ messages in thread
From: Richard Henderson @ 2010-07-13 21:19 UTC (permalink / raw)
  To: Richard Guenther
  Cc: IainS, GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On 07/13/2010 01:46 PM, Richard Guenther wrote:
> On Tue, Jul 13, 2010 at 10:01 PM, Richard Henderson <rth@redhat.com> wrote:
>> Test #6.  Final?
>>
>> Aliases are supposed to work now.
> 
> The forwprop piece disables propagating &p->foo like addresses which
> is useful and important.

Hum.  Perhaps the check needs to be pushed farther down into
forward_propagate_addr_expr{,_1}.  But...

> Also forwprop doesn't propagate into PHIs,
> so I don't understand what's the issue here (CCP does, though).

The best way I can figure to prevent propagation into PHIs is to declare
&tlsvar (without native support) to be !is_gimple_{val,min_invariant}.

That change, however, alters the valid arguments to MEM_REF.  In that
&tlsvar is no longer valid.  Now, one could spend forever slightly 
tweeking all of the myriad predicates involved, but what it comes down
to is that forwprop does not have the same checks that verify_expr does.



r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 20:04                     ` Nathan Froyd
@ 2010-07-13 21:20                       ` Richard Henderson
  2010-07-13 21:23                         ` Nathan Froyd
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-13 21:20 UTC (permalink / raw)
  To: Nathan Froyd
  Cc: IainS, GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On 07/13/2010 01:04 PM, Nathan Froyd wrote:
>> +  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
>> +  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);
> 
> Don't forget to update this for the removal of build_function_call_expr.

What's the preferred variant now?


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 21:20                       ` Richard Henderson
@ 2010-07-13 21:23                         ` Nathan Froyd
  0 siblings, 0 replies; 35+ messages in thread
From: Nathan Froyd @ 2010-07-13 21:23 UTC (permalink / raw)
  To: Richard Henderson
  Cc: IainS, GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On Tue, Jul 13, 2010 at 02:19:59PM -0700, Richard Henderson wrote:
> On 07/13/2010 01:04 PM, Nathan Froyd wrote:
> >> +  x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON];
> >> +  x = build_function_call_expr (UNKNOWN_LOCATION, x, args);
> > 
> > Don't forget to update this for the removal of build_function_call_expr.
> 
> What's the preferred variant now?

varasm.c:emutls_common_1 uses build_call_expr now.  I'd hesitate to call
it "preferred", though.

-Nathan

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 21:19                       ` Richard Henderson
@ 2010-07-13 21:39                         ` Andrew Pinski
  2010-07-13 21:48                           ` Richard Henderson
  2010-07-14  8:11                         ` Richard Guenther
  1 sibling, 1 reply; 35+ messages in thread
From: Andrew Pinski @ 2010-07-13 21:39 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Richard Guenther, IainS, GCC Patches, Diego Novillo, Jan Hubicka,
	Jakub Jelinek

On Tue, Jul 13, 2010 at 2:19 PM, Richard Henderson <rth@redhat.com> wrote:
>
> That change, however, alters the valid arguments to MEM_REF.  In that
> &tlsvar is no longer valid.  Now, one could spend forever slightly
> tweeking all of the myriad predicates involved, but what it comes down
> to is that forwprop does not have the same checks that verify_expr does.

Hmm, this sounds like
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44824 where there are
problems with ADDR_EXPR being inside MEM_REF in some other cases.

-- Pinski

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 21:39                         ` Andrew Pinski
@ 2010-07-13 21:48                           ` Richard Henderson
  2010-07-14  8:26                             ` Richard Guenther
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-13 21:48 UTC (permalink / raw)
  To: Andrew Pinski
  Cc: Richard Guenther, IainS, GCC Patches, Diego Novillo, Jan Hubicka,
	Jakub Jelinek

On 07/13/2010 02:39 PM, Andrew Pinski wrote:
> On Tue, Jul 13, 2010 at 2:19 PM, Richard Henderson <rth@redhat.com> wrote:
>>
>> That change, however, alters the valid arguments to MEM_REF.  In that
>> &tlsvar is no longer valid.  Now, one could spend forever slightly
>> tweeking all of the myriad predicates involved, but what it comes down
>> to is that forwprop does not have the same checks that verify_expr does.
> 
> Hmm, this sounds like
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44824 where there are
> problems with ADDR_EXPR being inside MEM_REF in some other cases.

Yes, that's exactly the same problem: DLLIMPORT_P variables are
also !is_gimple_min_invariant, via !decl_address_invariant_p.

(Although for the life of me I can't tell you why -- the expansion
to *__imp_foo doesn't happen until rtl expansion time, and the value
is in fact function invariant (initialized at load time, like .got).
I tried to do a bit of spelunking to figure out the change history,
but svn blame -rN doesn't seem to work with files that are no longer
present at HEAD.)


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 21:19                       ` Richard Henderson
  2010-07-13 21:39                         ` Andrew Pinski
@ 2010-07-14  8:11                         ` Richard Guenther
  1 sibling, 0 replies; 35+ messages in thread
From: Richard Guenther @ 2010-07-14  8:11 UTC (permalink / raw)
  To: Richard Henderson
  Cc: IainS, GCC Patches, Diego Novillo, Jan Hubicka, Jakub Jelinek

On Tue, Jul 13, 2010 at 11:19 PM, Richard Henderson <rth@redhat.com> wrote:
> On 07/13/2010 01:46 PM, Richard Guenther wrote:
>> On Tue, Jul 13, 2010 at 10:01 PM, Richard Henderson <rth@redhat.com> wrote:
>>> Test #6.  Final?
>>>
>>> Aliases are supposed to work now.
>>
>> The forwprop piece disables propagating &p->foo like addresses which
>> is useful and important.
>
> Hum.  Perhaps the check needs to be pushed farther down into
> forward_propagate_addr_expr{,_1}.  But...
>
>> Also forwprop doesn't propagate into PHIs,
>> so I don't understand what's the issue here (CCP does, though).
>
> The best way I can figure to prevent propagation into PHIs is to declare
> &tlsvar (without native support) to be !is_gimple_{val,min_invariant}.
>
> That change, however, alters the valid arguments to MEM_REF.  In that
> &tlsvar is no longer valid.  Now, one could spend forever slightly
> tweeking all of the myriad predicates involved, but what it comes down
> to is that forwprop does not have the same checks that verify_expr does.

I don't think making TLS vars addresses not function invariant is a
good thing to do.

Why not deal with lowering TLS vars in PHI nodes?  It shold be as simple
as inserting code on the edge.

Richard.

>
>
> r~
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-13 21:48                           ` Richard Henderson
@ 2010-07-14  8:26                             ` Richard Guenther
  2010-07-14 16:00                               ` Richard Henderson
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Guenther @ 2010-07-14  8:26 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Andrew Pinski, IainS, GCC Patches, Diego Novillo, Jan Hubicka,
	Jakub Jelinek

On Tue, Jul 13, 2010 at 11:48 PM, Richard Henderson <rth@redhat.com> wrote:
> On 07/13/2010 02:39 PM, Andrew Pinski wrote:
>> On Tue, Jul 13, 2010 at 2:19 PM, Richard Henderson <rth@redhat.com> wrote:
>>>
>>> That change, however, alters the valid arguments to MEM_REF.  In that
>>> &tlsvar is no longer valid.  Now, one could spend forever slightly
>>> tweeking all of the myriad predicates involved, but what it comes down
>>> to is that forwprop does not have the same checks that verify_expr does.
>>
>> Hmm, this sounds like
>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44824 where there are
>> problems with ADDR_EXPR being inside MEM_REF in some other cases.
>
> Yes, that's exactly the same problem: DLLIMPORT_P variables are
> also !is_gimple_min_invariant, via !decl_address_invariant_p.

Hm, I'll look at that PR (though the DLLIMPORT_P exclusion is odd).

> (Although for the life of me I can't tell you why -- the expansion
> to *__imp_foo doesn't happen until rtl expansion time, and the value
> is in fact function invariant (initialized at load time, like .got).
> I tried to do a bit of spelunking to figure out the change history,
> but svn blame -rN doesn't seem to work with files that are no longer
> present at HEAD.)

Well.  Are there any VAR_DECLs whose addresses are not function
invariant?

As for DLLIMPORT_P I'd suggest simply making it so and watching for
the fallout.

Richard.

>
> r~
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-14  8:26                             ` Richard Guenther
@ 2010-07-14 16:00                               ` Richard Henderson
  2010-07-14 16:25                                 ` Richard Guenther
  0 siblings, 1 reply; 35+ messages in thread
From: Richard Henderson @ 2010-07-14 16:00 UTC (permalink / raw)
  To: Richard Guenther
  Cc: Andrew Pinski, IainS, GCC Patches, Diego Novillo, Jan Hubicka,
	Jakub Jelinek

On 07/14/2010 01:26 AM, Richard Guenther wrote:
> Well.  Are there any VAR_DECLs whose addresses are not function
> invariant?

Variable sized ones, particularly those allocated inside loops?
Although those should have been lowered by this point as well,
so I'm really not sure.


r~

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Patch, updated] Make emulated TLS lto-friendly.
  2010-07-14 16:00                               ` Richard Henderson
@ 2010-07-14 16:25                                 ` Richard Guenther
  0 siblings, 0 replies; 35+ messages in thread
From: Richard Guenther @ 2010-07-14 16:25 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Andrew Pinski, IainS, GCC Patches, Diego Novillo, Jan Hubicka,
	Jakub Jelinek

On Wed, Jul 14, 2010 at 6:00 PM, Richard Henderson <rth@redhat.com> wrote:
> On 07/14/2010 01:26 AM, Richard Guenther wrote:
>> Well.  Are there any VAR_DECLs whose addresses are not function
>> invariant?
>
> Variable sized ones, particularly those allocated inside loops?
> Although those should have been lowered by this point as well,
> so I'm really not sure.

Hm, indeed.  It's a generic predicate in tree.c and so might be used
before that lowering.

I've dealt with the forwprop problem btw, so maybe that already helps.

Richard.

>
>
> r~
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2010-07-14 16:25 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-07-03 14:11 [Patch, updated] Make emulated TLS lto-friendly IainS
2010-07-07 23:22 ` Richard Henderson
2010-07-08  8:42   ` IainS
2010-07-08  9:32     ` Jakub Jelinek
2010-07-08 12:01       ` IainS
2010-07-08 17:10         ` Richard Henderson
2010-07-08 17:21           ` Jan Hubicka
2010-07-08 17:34             ` Richard Henderson
2010-07-08 16:14     ` Richard Henderson
2010-07-08 16:21       ` IainS
2010-07-08 17:04         ` Richard Henderson
2010-07-08  9:25   ` Richard Guenther
2010-07-08 11:59     ` [Patch, testsuite] " IainS
2010-07-08 12:05       ` IainS
2010-07-08 19:07   ` IainS
2010-07-08 19:19     ` Richard Henderson
2010-07-09 12:11       ` IainS
2010-07-12 14:48         ` Jack Howarth
2010-07-12 15:18         ` Richard Henderson
2010-07-12 17:04           ` Richard Henderson
2010-07-12 20:08             ` Richard Henderson
2010-07-13 15:47               ` Richard Henderson
2010-07-13 18:56                 ` Richard Henderson
2010-07-13 20:01                   ` Richard Henderson
2010-07-13 20:04                     ` Nathan Froyd
2010-07-13 21:20                       ` Richard Henderson
2010-07-13 21:23                         ` Nathan Froyd
2010-07-13 20:46                     ` Richard Guenther
2010-07-13 21:19                       ` Richard Henderson
2010-07-13 21:39                         ` Andrew Pinski
2010-07-13 21:48                           ` Richard Henderson
2010-07-14  8:26                             ` Richard Guenther
2010-07-14 16:00                               ` Richard Henderson
2010-07-14 16:25                                 ` Richard Guenther
2010-07-14  8:11                         ` Richard Guenther

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).