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


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

thread-local storage: x86 backend


So now at least one platform can actually start playing with it.  


r~

	* configure.in (HAVE_AS_TLS): New test.
	* config.in, configure: Rebuild.
	* config/i386/i386.c (TARGET_HAVE_TLS): Set if HAVE_AS_TLS.
	(ix86_tls_dialect_string, ix86_tls_dialect): New.
	(override_options): Set it.
	(tls_model_chars, tls_symbolic_operand): New.
	(tls_symbolic_operand_1, global_dynamic_symbolic_operand): New.
	(local_dynamic_symbolic_operand, initial_exec_symbolic_operand): New.
	(local_exec_symbolic_operand): New.
	(get_pic_label_name): Merge into output_set_got.
	(ix86_asm_file_end): Emit pic_label_name if defined.
	(legitimate_constant_p, constant_address_p): New.
	(legitimate_pic_operand_p): New.
	(legitimate_pic_address_disp_p): Handle GOTTPOFF, NTPOFF, DTPOFF.
	(legitimate_address_p): Likewise.
	(ix86_encode_section_info): Rename from i386_; handle tls decls.
	(ix86_strip_name_encoding): New.
	(get_thread_pointer): New.
	(legitimize_address): Handle tls symbols.
	(output_pic_addr_const): Handle GOTTPOFF, TPOFF, NTPOFF, DTPOFF.
	Remove UNSPEC_PLT.
	(struct machine_function): Add some_ld_name.
	(get_some_local_dynamic_name, get_some_local_dynamic_name_1): Set it.
	(print_operand) [&]: Use it.  Handle UNSPEC_TP.
	(output_addr_const_extra): New.
	(maybe_get_pool_constant): New.
	(ix86_split_to_parts): Use it.
	(ix86_expand_move): Handle tls symbols.
	(ix86_tls_get_addr): New.
	* config/i386/i386.h (TARGET_GNU_TLS, TARGET_SUN_TLS): New.
	(TARGET_OPTIONS): Add tls-dialect.
	(CONSTANT_ADDRESS_P): Use new out-of-line function.
	(LEGITIMATE_CONSTANT_P): Likewise.
	(LEGITIMATE_PIC_OPERAND_P): Likewise.
	(TARGET_STRIP_NAME_ENCODING): New.
	(ASM_OUTPUT_LABELREF): New.
	(PRINT_OPERAND_PUNCT_VALID_P): Add '&'.
	(OUTPUT_ADDR_CONST_EXTRA): New.
	(PREDICATE_CODES): Update.
	(ix86_tls_dialect, ix86_tls_dialect_string): New.
	* config/i386/i386.md: Regroup and renumber unspec constants.
	(tls_global_dynamic_gnu, tls_global_dynamic_sun): New.
	(tls_local_dynamic_base_gnu, tls_local_dynamic_base_sun): New.
	(tls_global_dynamic, tls_local_dynamic_base): New.
	(tls_local_dynamic_once): New.
	* config/i386/i386-protos.h: Update.

Index: configure.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/configure.in,v
retrieving revision 1.591
diff -c -p -d -u -r1.591 configure.in
--- configure.in	23 May 2002 08:11:49 -0000	1.591
+++ configure.in	23 May 2002 21:35:41 -0000
@@ -1718,6 +1718,55 @@ if test x"$gcc_cv_as_shf_merge" = xyes; 
 fi
 AC_MSG_RESULT($gcc_cv_as_shf_merge)
 
+AC_MSG_CHECKING(assembler thread-local storage support)
+gcc_cv_as_tls=no
+conftest_s=
+tls_first_major=
+tls_first_minor=
+case "$target" in
+changequote(,)dnl
+  i[34567]86-*-*)
+changequote([,])dnl
+    conftest_s='
+	.section ".tdata","awT",@progbits
+foo:	.long	25
+	.text
+	movl	%gs:0, %eax
+	leal	foo@TLSGD(,%ebx,1), %eax
+	leal	foo@TLSLDM(%ebx), %eax
+	leal	foo@DTPOFF(%eax), %edx
+	movl	foo@GOTTPOFF(%ebx), %eax
+	subl	foo@GOTTPOFF(%ebx), %eax
+	movl	$foo@TPOFF, %eax
+	subl	$foo@TPOFF, %eax
+	leal	foo@NTPOFF(%ecx), %eax'
+	tls_first_major=2
+	tls_first_minor=13
+	;;
+esac
+if test -z "$tls_first_major"; then
+  :
+elif test x$gcc_cv_gas_major_version != x -a x$gcc_cv_gas_minor_version != x
+then
+  if test "$gcc_cv_gas_major_version" -eq "$tls_first_major" \
+	  -a "$gcc_cv_gas_minor_version" -ge "$tls_first_minor" \
+	  -o "$gcc_cv_gas_major_version" -gt "$tls_first_major"; then
+    gcc_cv_as_tls=yes
+  fi
+elif test x$gcc_cv_as != x; then
+  echo "$conftest_s" > conftest.s
+  if $gcc_cv_as --fatal-warnings -o conftest.o conftest.s > /dev/null 2>&1
+  then
+    gcc_cv_as_tls=yes
+  fi
+  rm -f conftest.s conftest.o
+fi
+if test "$gcc_cv_as_tls" = yes; then
+  AC_DEFINE(HAVE_AS_TLS, 1,
+	    [Define if your assembler supports thread-local storage.])
+fi
+AC_MSG_RESULT($gcc_cv_as_tls)
+
 case "$target" in
   # All TARGET_ABI_OSF targets.
   alpha*-*-osf* | alpha*-*-linux* | alpha*-*-*bsd*)
Index: config/i386/i386-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386-protos.h,v
retrieving revision 1.73
diff -c -p -d -u -r1.73 i386-protos.h
--- config/i386/i386-protos.h	23 May 2002 07:43:11 -0000	1.73
+++ config/i386/i386-protos.h	23 May 2002 21:35:41 -0000
@@ -50,6 +50,11 @@ extern int x86_64_immediate_operand PARA
 extern int x86_64_zext_immediate_operand PARAMS ((rtx, enum machine_mode));
 extern int const_int_1_operand PARAMS ((rtx, enum machine_mode));
 extern int symbolic_operand PARAMS ((rtx, enum machine_mode));
+extern int tls_symbolic_operand PARAMS ((rtx, enum machine_mode));
+extern int global_dynamic_symbolic_operand PARAMS ((rtx, enum machine_mode));
+extern int local_dynamic_symbolic_operand PARAMS ((rtx, enum machine_mode));
+extern int initial_exec_symbolic_operand PARAMS ((rtx, enum machine_mode));
+extern int local_exec_symbolic_operand PARAMS ((rtx, enum machine_mode));
 extern int pic_symbolic_operand PARAMS ((rtx, enum machine_mode));
 extern int call_insn_operand PARAMS ((rtx, enum machine_mode));
 extern int constant_call_address_operand PARAMS ((rtx, enum machine_mode));
@@ -83,6 +88,9 @@ extern int ix86_expand_movstr PARAMS ((r
 extern int ix86_expand_clrstr PARAMS ((rtx, rtx, rtx));
 extern int ix86_expand_strlen PARAMS ((rtx, rtx, rtx, rtx));
 
+extern bool legitimate_constant_p PARAMS ((rtx));
+extern bool constant_address_p PARAMS ((rtx));
+extern bool legitimate_pic_operand_p PARAMS ((rtx));
 extern int legitimate_pic_address_disp_p PARAMS ((rtx));
 extern int legitimate_address_p PARAMS ((enum machine_mode, rtx, int));
 extern rtx legitimize_pic_address PARAMS ((rtx, rtx));
@@ -91,6 +99,7 @@ extern rtx legitimize_address PARAMS ((r
 extern void print_reg PARAMS ((rtx, int, FILE*));
 extern void print_operand PARAMS ((FILE*, rtx, int));
 extern void print_operand_address PARAMS ((FILE*, rtx));
+extern bool output_addr_const_extra PARAMS ((FILE*, rtx));
 
 extern void split_di PARAMS ((rtx[], int, rtx[], rtx[]));
 extern void split_ti PARAMS ((rtx[], int, rtx[], rtx[]));
@@ -197,6 +206,8 @@ extern unsigned int i386_pe_section_type
 extern void i386_pe_asm_named_section PARAMS ((const char *, unsigned int));
 extern void x86_output_mi_thunk PARAMS ((FILE *, int, tree));
 #endif
+
+extern rtx ix86_tls_get_addr PARAMS ((void));
 
 /* In winnt.c  */
 extern void i386_pe_encode_section_info PARAMS ((tree, int));
Index: config/i386/i386.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386.c,v
retrieving revision 1.407
diff -c -p -d -u -r1.407 i386.c
--- config/i386/i386.c	23 May 2002 21:25:04 -0000	1.407
+++ config/i386/i386.c	23 May 2002 21:35:41 -0000
@@ -544,6 +544,10 @@ int const svr4_dbx_register_map[FIRST_PS
 rtx ix86_compare_op0 = NULL_RTX;
 rtx ix86_compare_op1 = NULL_RTX;
 
+/* The encoding characters for the four TLS models present in ELF.  */
+
+static char const tls_model_chars[] = "GLil";
+
 #define MAX_386_STACK_LOCALS 3
 /* Size of the register save area.  */
 #define X86_64_VARARGS_SIZE (REGPARM_MAX * UNITS_PER_WORD + SSE_REGPARM_MAX * 16)
@@ -552,6 +556,7 @@ rtx ix86_compare_op1 = NULL_RTX;
 struct machine_function
 {
   rtx stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
+  const char *some_ld_name;
   int save_varrargs_registers;
   int accesses_prev_frame;
 };
@@ -604,6 +609,9 @@ enum cmodel ix86_cmodel;
 /* Asm dialect.  */
 const char *ix86_asm_string;
 enum asm_dialect ix86_asm_dialect = ASM_ATT;
+/* TLS dialext.  */
+const char *ix86_tls_dialect_string;
+enum tls_dialect ix86_tls_dialect = TLS_DIALECT_GNU;
 
 /* Which unit we are generating floating point math for.  */
 enum fpmath_unit ix86_fpmath;
@@ -653,13 +661,17 @@ static char internal_label_prefix[16];
 static int internal_label_prefix_len;
 
 static int local_symbolic_operand PARAMS ((rtx, enum machine_mode));
-static const char *get_pic_label_name PARAMS ((void));
+static int tls_symbolic_operand_1 PARAMS ((rtx, enum tls_model));
 static void output_pic_addr_const PARAMS ((FILE *, rtx, int));
 static void put_condition_code PARAMS ((enum rtx_code, enum machine_mode,
 				       int, int, FILE *));
+static const char *get_some_local_dynamic_name PARAMS ((void));
+static int get_some_local_dynamic_name_1 PARAMS ((rtx *, void *));
+static rtx maybe_get_pool_constant PARAMS ((rtx));
 static rtx ix86_expand_int_compare PARAMS ((enum rtx_code, rtx, rtx));
 static enum rtx_code ix86_prepare_fp_compare_args PARAMS ((enum rtx_code,
 							   rtx *, rtx *));
+static rtx get_thread_pointer PARAMS ((void));
 static rtx gen_push PARAMS ((rtx));
 static int memory_address_length PARAMS ((rtx addr));
 static int ix86_flags_dependant PARAMS ((rtx, rtx, enum attr_type));
@@ -698,7 +710,9 @@ struct ix86_address
 
 static int ix86_decompose_address PARAMS ((rtx, struct ix86_address *));
 
-static void i386_encode_section_info PARAMS ((tree, int)) ATTRIBUTE_UNUSED;
+static void ix86_encode_section_info PARAMS ((tree, int)) ATTRIBUTE_UNUSED;
+static const char *ix86_strip_name_encoding PARAMS ((const char *))
+     ATTRIBUTE_UNUSED;
 
 struct builtin_description;
 static rtx ix86_expand_sse_comi PARAMS ((const struct builtin_description *,
@@ -831,6 +845,11 @@ static enum x86_64_reg_class merge_class
 #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
   ia32_multipass_dfa_lookahead
 
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+#endif
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 /* Sometimes certain combinations of command options do not make
@@ -1120,6 +1139,17 @@ override_options ()
 	ix86_branch_cost = i;
     }
 
+  if (ix86_tls_dialect_string)
+    {
+      if (strcmp (ix86_tls_dialect_string, "gnu") == 0)
+	ix86_tls_dialect = TLS_DIALECT_GNU;
+      else if (strcmp (ix86_tls_dialect_string, "sun") == 0)
+	ix86_tls_dialect = TLS_DIALECT_SUN;
+      else
+	error ("bad value (%s) for -mtls-dialect= switch",
+	       ix86_tls_dialect_string);
+    }
+
   /* Keep nonleaf frame pointers.  */
   if (TARGET_OMIT_LEAF_FRAME_POINTER)
     flag_omit_frame_pointer = 1;
@@ -2962,6 +2992,70 @@ local_symbolic_operand (op, mode)
   return 0;
 }
 
+/* Test for various thread-local symbols.  See ix86_encode_section_info. */
+
+int
+tls_symbolic_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  const char *symbol_str;
+
+  if (GET_CODE (op) != SYMBOL_REF)
+    return 0;
+  symbol_str = XSTR (op, 0);
+
+  if (symbol_str[0] != '%')
+    return 0;
+  return strchr (tls_model_chars, symbol_str[1]) - tls_model_chars + 1;
+}
+
+static int
+tls_symbolic_operand_1 (op, kind)
+     rtx op;
+     enum tls_model kind;
+{
+  const char *symbol_str;
+
+  if (GET_CODE (op) != SYMBOL_REF)
+    return 0;
+  symbol_str = XSTR (op, 0);
+
+  return symbol_str[0] == '%' && symbol_str[1] == tls_model_chars[kind];
+}
+
+int
+global_dynamic_symbolic_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return tls_symbolic_operand_1 (op, TLS_MODEL_GLOBAL_DYNAMIC);
+}
+
+int
+local_dynamic_symbolic_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return tls_symbolic_operand_1 (op, TLS_MODEL_LOCAL_DYNAMIC);
+}
+
+int
+initial_exec_symbolic_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return tls_symbolic_operand_1 (op, TLS_MODEL_INITIAL_EXEC);
+}
+
+int
+local_exec_symbolic_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return tls_symbolic_operand_1 (op, TLS_MODEL_LOCAL_EXEC);
+}
+
 /* Test for a valid operand for a call instruction.  Don't allow the
    arg pointer register or virtual regs since they may decay into
    reg + const, which the patterns can't handle.  */
@@ -3802,14 +3896,6 @@ ix86_setup_frame_addresses ()
 
 static char pic_label_name[32];
 
-static const char *
-get_pic_label_name ()
-{
-  if (! pic_label_name[0])
-    ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", 0);
-  return pic_label_name;
-}
-
 /* This function generates code for -fpic that loads %ebx with
    the return address of the caller and then returns.  */
 
@@ -3819,7 +3905,7 @@ ix86_asm_file_end (file)
 {
   rtx xops[2];
 
-  if (! TARGET_DEEP_BRANCH_PREDICTION || pic_label_name[0] == 0)
+  if (pic_label_name[0] == 0)
     return;
 
   /* ??? Binutils 2.10 and earlier has a linkonce elimination bug related
@@ -3890,7 +3976,10 @@ output_set_got (dest)
     }
   else
     {
-      xops[2] = gen_rtx_SYMBOL_REF (Pmode, get_pic_label_name ());
+      if (! pic_label_name[0])
+	ASM_GENERATE_INTERNAL_LABEL (pic_label_name, "LPR", 0);
+
+      xops[2] = gen_rtx_SYMBOL_REF (Pmode, pic_label_name);
       xops[2] = gen_rtx_MEM (QImode, xops[2]);
       output_asm_insn ("call\t%X2", xops);
     }
@@ -4676,6 +4765,114 @@ ix86_find_base_term (x)
   return term;
 }
 
+/* Determine if a given RTX is a valid constant.  We already know this
+   satisfies CONSTANT_P.  */
+
+bool
+legitimate_constant_p (x)
+     rtx x;
+{
+  rtx inner;
+
+  switch (GET_CODE (x))
+    {
+    case SYMBOL_REF:
+      /* TLS symbols are not constant.  */
+      if (tls_symbolic_operand (x, Pmode))
+	return false;
+      break;
+
+    case CONST:
+      inner = XEXP (x, 0);
+
+      /* Offsets of TLS symbols are never valid.
+	 Discourage CSE from creating them.  */
+      if (GET_CODE (inner) == PLUS
+	  && tls_symbolic_operand (XEXP (inner, 0), Pmode))
+	return false;
+
+      /* Only some unspecs are valid as "constants".  */
+      if (GET_CODE (inner) == UNSPEC)
+	switch (XINT (inner, 1))
+	  {
+	  case UNSPEC_TPOFF:
+	    return local_exec_symbolic_operand (XVECEXP (inner, 0, 0), Pmode);
+	  case UNSPEC_TP:
+	    return true;
+	  default:
+	    return false;
+	  }
+      break;
+
+    default:
+      break;
+    }
+
+  /* Otherwise we handle everything else in the move patterns.  */
+  return true;
+}
+
+/* Determine if a given RTX is a valid constant address.  */
+
+bool
+constant_address_p (x)
+     rtx x;
+{
+  switch (GET_CODE (x))
+    {
+    case LABEL_REF:
+    case CONST_INT:
+      return true;
+
+    case CONST_DOUBLE:
+      return TARGET_64BIT;
+
+    case CONST:
+    case SYMBOL_REF:
+      return !flag_pic && legitimate_constant_p (x);
+
+    default:
+      return false;
+    }
+}
+
+/* Nonzero if the constant value X is a legitimate general operand
+   when generating PIC code.  It is given that flag_pic is on and 
+   that X satisfies CONSTANT_P or is a CONST_DOUBLE.  */
+
+bool
+legitimate_pic_operand_p (x)
+     rtx x;
+{
+  rtx inner;
+
+  switch (GET_CODE (x))
+    {
+    case CONST:
+      inner = XEXP (x, 0);
+
+      /* Only some unspecs are valid as "constants".  */
+      if (GET_CODE (inner) == UNSPEC)
+	switch (XINT (inner, 1))
+	  {
+	  case UNSPEC_TPOFF:
+	    return local_exec_symbolic_operand (XVECEXP (inner, 0, 0), Pmode);
+	  case UNSPEC_TP:
+	    return true;
+	  default:
+	    return false;
+	  }
+      /* FALLTHRU */
+
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return legitimate_pic_address_disp_p (x);
+
+    default:
+      return true;
+    }
+}
+
 /* Determine if a given CONST RTX is a valid memory displacement
    in PIC mode.  */
 
@@ -4683,6 +4880,8 @@ int
 legitimate_pic_address_disp_p (disp)
      register rtx disp;
 {
+  bool saw_plus;
+
   /* In 64bit mode we can allow direct addresses of symbols and labels
      when they are not dynamic symbols.  */
   if (TARGET_64BIT)
@@ -4718,23 +4917,40 @@ legitimate_pic_address_disp_p (disp)
       return 1;
     }
 
+  saw_plus = false;
   if (GET_CODE (disp) == PLUS)
     {
       if (GET_CODE (XEXP (disp, 1)) != CONST_INT)
 	return 0;
       disp = XEXP (disp, 0);
+      saw_plus = true;
     }
 
   if (GET_CODE (disp) != UNSPEC)
     return 0;
 
-  /* Must be @GOT or @GOTOFF.  */
   switch (XINT (disp, 1))
     {
     case UNSPEC_GOT:
+      if (saw_plus)
+	return false;
       return GET_CODE (XVECEXP (disp, 0, 0)) == SYMBOL_REF;
     case UNSPEC_GOTOFF:
       return local_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
+    case UNSPEC_GOTTPOFF:
+      if (saw_plus)
+	return false;
+      return initial_exec_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
+    case UNSPEC_NTPOFF:
+      /* ??? Could support offset here.  */
+      if (saw_plus)
+	return false;
+      return local_exec_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
+    case UNSPEC_DTPOFF:
+      /* ??? Could support offset here.  */
+      if (saw_plus)
+	return false;
+      return local_dynamic_symbolic_operand (XVECEXP (disp, 0, 0), Pmode);
     }
     
   return 0;
@@ -4873,12 +5089,6 @@ legitimate_address_p (mode, addr, strict
     {
       reason_rtx = disp;
 
-      if (!CONSTANT_ADDRESS_P (disp))
-	{
-	  reason = "displacement is not constant";
-	  goto report_error;
-	}
-
       if (TARGET_64BIT)
 	{
 	  if (!x86_64_sign_extended_value (disp))
@@ -4896,8 +5106,30 @@ legitimate_address_p (mode, addr, strict
 	    }
 	}
 
-      if (flag_pic && SYMBOLIC_CONST (disp))
+      if (GET_CODE (disp) == CONST
+	  && GET_CODE (XEXP (disp, 0)) == UNSPEC)
+	switch (XINT (XEXP (disp, 0), 1))
+	  {
+	  case UNSPEC_GOT:
+	  case UNSPEC_GOTOFF:
+	  case UNSPEC_GOTPCREL:
+	    if (!flag_pic)
+	      abort ();
+	    goto is_legitimate_pic;
+
+	  case UNSPEC_GOTTPOFF:
+	  case UNSPEC_NTPOFF:
+	  case UNSPEC_DTPOFF:
+	    break;
+
+	  default:
+	    reason = "invalid address unspec";
+	    goto report_error;
+	  }
+
+      else if (flag_pic && SYMBOLIC_CONST (disp))
 	{
+	is_legitimate_pic:
 	  if (TARGET_64BIT && (index || base))
 	    {
 	      reason = "non-constant pic memory reference";
@@ -4940,6 +5172,11 @@ legitimate_address_p (mode, addr, strict
 	      goto report_error;
 	    }
 	}
+      else if (!CONSTANT_ADDRESS_P (disp))
+	{
+	  reason = "displacement is not constant";
+	  goto report_error;
+	}
     }
 
   /* Everything looks valid.  */
@@ -5122,23 +5359,100 @@ legitimize_pic_address (orig, reg)
   return new;
 }
 
-/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
-   may access it directly in the GOT.  */
-
 static void
-i386_encode_section_info (decl, first)
+ix86_encode_section_info (decl, first)
      tree decl;
      int first ATTRIBUTE_UNUSED;
 {
+  bool local_p = (*targetm.binds_local_p) (decl);
+  rtx rtl, symbol;
+
+  rtl = DECL_P (decl) ? DECL_RTL (decl) : TREE_CST_RTL (decl);
+  if (GET_CODE (rtl) != MEM)
+    return;
+  symbol = XEXP (rtl, 0);
+  if (GET_CODE (symbol) != SYMBOL_REF)
+    return;
+
+  /* For basic x86, if using PIC, mark a SYMBOL_REF for a non-global
+     symbol so that we may access it directly in the GOT.  */
+
   if (flag_pic)
+    SYMBOL_REF_FLAG (symbol) = local_p;
+
+  /* For ELF, encode thread-local data with %[GLil] for "global dynamic",
+     "local dynamic", "initial exec" or "local exec" TLS models
+     respectively.  */
+
+  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
     {
-      rtx rtl = DECL_P (decl) ? DECL_RTL (decl) : TREE_CST_RTL (decl);
+      const char *symbol_str;
+      char *newstr;
+      size_t len;
+      enum tls_model kind;
 
-      if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF)
-	SYMBOL_REF_FLAG (XEXP (rtl, 0)) = (*targetm.binds_local_p) (decl);
+      if (!flag_pic)
+	{
+	  if (local_p)
+	    kind = TLS_MODEL_LOCAL_EXEC;
+	  else
+	    kind = TLS_MODEL_INITIAL_EXEC;
+	}
+      /* Local dynamic is inefficient when we're not combining the
+	 parts of the address.  */
+      else if (optimize && local_p)
+	kind = TLS_MODEL_LOCAL_DYNAMIC;
+      else
+	kind = TLS_MODEL_GLOBAL_DYNAMIC;
+      if (kind < flag_tls_default)
+	kind = flag_tls_default;
+
+      symbol_str = XSTR (symbol, 0);
+
+      if (symbol_str[0] == '%')
+	{
+	  if (symbol_str[1] == tls_model_chars[kind])
+	    return;
+	  symbol_str += 2;
+	}
+      len = strlen (symbol_str) + 1;
+      newstr = alloca (len + 2);
+
+      newstr[0] = '%';
+      newstr[1] = tls_model_chars[kind];
+      memcpy (newstr + 2, symbol_str, len);
+
+      XSTR (symbol, 0) = ggc_alloc_string (newstr, len + 2 - 1);
     }
 }
+
+/* Undo the above when printing symbol names.  */
+
+static const char *
+ix86_strip_name_encoding (str)
+     const char *str;
+{
+  if (str[0] == '%')
+    str += 2;
+  if (str [0] == '*')
+    str += 1;
+  return str;
+}
 
+/* Load the thread pointer into a register.  */
+
+static rtx
+get_thread_pointer ()
+{
+  rtx tp;
+
+  tp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TP);
+  tp = gen_rtx_CONST (Pmode, tp);
+  tp = force_reg (Pmode, tp);
+
+  return tp;
+}
+  
 /* Try machine-dependent ways of modifying an illegitimate address
    to be legitimate.  If we find one, return the new, valid address.
    This macro is used in only one place: `memory_address' in explow.c.
@@ -5176,6 +5490,85 @@ legitimize_address (x, oldx, mode)
       debug_rtx (x);
     }
 
+  /* Note that tls_symbolic_operand return is biased by 1 to return true.  */
+  log = tls_symbolic_operand (x, mode);
+  if (log)
+    {
+      rtx dest, base, off, pic;
+
+      switch (log - 1)
+        {
+        case TLS_MODEL_GLOBAL_DYNAMIC:
+	  dest = gen_reg_rtx (Pmode);
+          emit_insn (gen_tls_global_dynamic (dest, x));
+	  break;
+
+        case TLS_MODEL_LOCAL_DYNAMIC:
+	  base = gen_reg_rtx (Pmode);
+	  emit_insn (gen_tls_local_dynamic_base (base));
+
+	  off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPOFF);
+	  off = gen_rtx_CONST (Pmode, off);
+
+	  return gen_rtx_PLUS (Pmode, base, off);
+
+        case TLS_MODEL_INITIAL_EXEC:
+	  if (flag_pic)
+	    {
+	      current_function_uses_pic_offset_table = 1;
+	      pic = pic_offset_table_rtx;
+	    }
+	  else
+	    {
+	      pic = gen_reg_rtx (Pmode);
+	      emit_insn (gen_set_got (pic));
+	    }
+
+	  base = get_thread_pointer ();
+
+	  off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_GOTTPOFF);
+	  off = gen_rtx_CONST (Pmode, off);
+	  off = gen_rtx_PLUS (Pmode, pic, off);
+	  off = gen_rtx_MEM (Pmode, off);
+	  RTX_UNCHANGING_P (off) = 1;
+	  set_mem_alias_set (off, ix86_GOT_alias_set ());
+
+	  /* Damn Sun for specifing a set of dynamic relocations without
+	     considering the two-operand nature of the architecture!
+	     We'd be much better off with a "GOTNTPOFF" relocation that
+	     already contained the negated constant.  */
+	  /* ??? Using negl and reg+reg addressing appears to be a lose
+	     size-wise.  The negl is two bytes, just like the extra movl
+	     incurred by the two-operand subl, but reg+reg addressing
+	     uses the two-byte modrm form, unlike plain reg.  */
+
+	  dest = gen_reg_rtx (Pmode);
+	  emit_insn (gen_subsi3 (dest, base, off));
+	  break;
+
+        case TLS_MODEL_LOCAL_EXEC:
+	  base = get_thread_pointer ();
+
+	  off = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x),
+				TARGET_GNU_TLS ? UNSPEC_NTPOFF : UNSPEC_TPOFF);
+	  off = gen_rtx_CONST (Pmode, off);
+
+	  if (TARGET_GNU_TLS)
+	    return gen_rtx_PLUS (Pmode, base, off);
+	  else
+	    {
+	      dest = gen_reg_rtx (Pmode);
+	      emit_insn (gen_subsi3 (dest, base, off));
+	    }
+	  break;
+
+	default:
+	  abort ();
+        }
+
+      return dest;
+    }
+
   if (flag_pic && SYMBOLIC_CONST (x))
     return legitimize_pic_address (x, 0);
 
@@ -5425,12 +5818,21 @@ output_pic_addr_const (file, x, code)
 	case UNSPEC_GOTOFF:
 	  fputs ("@GOTOFF", file);
 	  break;
-	case UNSPEC_PLT:
-	  fputs ("@PLT", file);
-	  break;
 	case UNSPEC_GOTPCREL:
 	  fputs ("@GOTPCREL(%RIP)", file);
 	  break;
+	case UNSPEC_GOTTPOFF:
+	  fputs ("@GOTTPOFF", file);
+	  break;
+	case UNSPEC_TPOFF:
+	  fputs ("@TPOFF", file);
+	  break;
+	case UNSPEC_NTPOFF:
+	  fputs ("@NTPOFF", file);
+	  break;
+	case UNSPEC_DTPOFF:
+	  fputs ("@DTPOFF", file);
+	  break;
 	default:
 	  output_operand_lossage ("invalid UNSPEC as operand");
 	  break;
@@ -5720,6 +6122,43 @@ print_reg (x, code, file)
     }
 }
 
+/* Locate some local-dynamic symbol still in use by this function
+   so that we can print its name in some tls_local_dynamic_base
+   pattern.  */
+
+static const char *
+get_some_local_dynamic_name ()
+{
+  rtx insn;
+
+  if (cfun->machine->some_ld_name)
+    return cfun->machine->some_ld_name;
+
+  for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+    if (INSN_P (insn)
+	&& for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
+      return cfun->machine->some_ld_name;
+
+  abort ();
+}
+
+static int
+get_some_local_dynamic_name_1 (px, data)
+     rtx *px;
+     void *data ATTRIBUTE_UNUSED;
+{
+  rtx x = *px;
+
+  if (GET_CODE (x) == SYMBOL_REF
+      && local_dynamic_symbolic_operand (x, Pmode))
+    {
+      cfun->machine->some_ld_name = XSTR (x, 0);
+      return 1;
+    }
+
+  return 0;
+}
+
 /* Meaning of CODE:
    L,W,B,Q,S,T -- print the opcode suffix for specified size of operand.
    C -- print opcode suffix for set/cmov insn.
@@ -5744,6 +6183,7 @@ print_reg (x, code, file)
    D -- print condition for SSE cmp instruction.
    P -- if PIC, print an @PLT suffix.
    X -- don't print any sort of PIC '@' suffix for a symbol.
+   & -- print some in-use local-dynamic symbol name.
  */
 
 void
@@ -5761,6 +6201,10 @@ print_operand (file, x, code)
 	    putc ('*', file);
 	  return;
 
+	case '&':
+	  assemble_name (file, get_some_local_dynamic_name ());
+	  return;
+
 	case 'A':
 	  if (ASSEMBLER_DIALECT == ASM_ATT)
 	    putc ('*', file);
@@ -6090,6 +6534,18 @@ print_operand (file, x, code)
       REAL_VALUE_TO_DECIMAL (r, "%.22e", dstr);
       fprintf (file, "%s", dstr);
     }
+
+  else if (GET_CODE (x) == CONST
+	   && GET_CODE (XEXP (x, 0)) == UNSPEC
+	   && XINT (XEXP (x, 0), 1) == UNSPEC_TP)
+    {
+      if (ASSEMBLER_DIALECT == ASM_INTEL)
+	fputs ("DWORD PTR ", file);
+      if (ASSEMBLER_DIALECT == ASM_ATT || USER_LABEL_PREFIX[0] == 0)
+	putc ('%', file);
+      fputs ("gs:0", file);
+    }
+
   else
     {
       if (code != 'P')
@@ -6238,6 +6694,43 @@ print_operand_address (file, addr)
 	}
     }
 }
+
+bool
+output_addr_const_extra (file, x)
+     FILE *file;
+     rtx x;
+{
+  rtx op;
+
+  if (GET_CODE (x) != UNSPEC)
+    return false;
+
+  op = XVECEXP (x, 0, 0);
+  switch (XINT (x, 1))
+    {
+    case UNSPEC_GOTTPOFF:
+      output_addr_const (file, op);
+      fputs ("@GOTTPOFF", file);
+      break;
+    case UNSPEC_TPOFF:
+      output_addr_const (file, op);
+      fputs ("@TPOFF", file);
+      break;
+    case UNSPEC_NTPOFF:
+      output_addr_const (file, op);
+      fputs ("@NTPOFF", file);
+      break;
+    case UNSPEC_DTPOFF:
+      output_addr_const (file, op);
+      fputs ("@DTPOFF", file);
+      break;
+
+    default:
+      return false;
+    }
+
+  return true;
+}
 
 /* Split one or more DImode RTL references into pairs of SImode
    references.  The RTL can be REG, offsettable MEM, integer constant, or
@@ -6775,51 +7268,117 @@ ix86_expand_clear (dest)
   emit_insn (tmp);
 }
 
+/* X is an unchanging MEM.  If it is a constant pool reference, return
+   the constant pool rtx, else NULL.  */
+
+static rtx
+maybe_get_pool_constant (x)
+     rtx x;
+{
+  x = XEXP (x, 0);
+
+  if (flag_pic)
+    {
+      if (GET_CODE (x) != PLUS)
+	return NULL_RTX;
+      if (XEXP (x, 0) != pic_offset_table_rtx)
+	return NULL_RTX;
+      x = XEXP (x, 1);
+      if (GET_CODE (x) != CONST)
+	return NULL_RTX;
+      x = XEXP (x, 0);
+      if (GET_CODE (x) != UNSPEC)
+	return NULL_RTX;
+      if (XINT (x, 1) != UNSPEC_GOTOFF)
+	return NULL_RTX;
+      x = XVECEXP (x, 0, 0);
+    }
+
+  if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
+    return get_pool_constant (x);
+
+  return NULL_RTX;
+}
+
 void
 ix86_expand_move (mode, operands)
      enum machine_mode mode;
      rtx operands[];
 {
   int strict = (reload_in_progress || reload_completed);
-  rtx insn;
+  rtx insn, op0, op1, tmp;
 
-  if (flag_pic && mode == Pmode && symbolic_operand (operands[1], Pmode))
+  op0 = operands[0];
+  op1 = operands[1];
+
+  /* ??? We have a slight problem.  We need to say that tls symbols are
+     not legitimate constants so that reload does not helpfully reload
+     these constants from a REG_EQUIV, which we cannot handle.  (Recall
+     that general- and local-dynamic address resolution requires a
+     function call.)
+
+     However, if we say that tls symbols are not legitimate constants,
+     then emit_move_insn helpfully drop them into the constant pool.
+
+     It is far easier to work around emit_move_insn than reload.  Recognize
+     the MEM that we would have created and extract the symbol_ref.  */
+
+  if (mode == Pmode
+      && GET_CODE (op1) == MEM
+      && RTX_UNCHANGING_P (op1))
     {
-      /* Emit insns to move operands[1] into operands[0].  */
+      tmp = maybe_get_pool_constant (op1);
+      /* Note that we only care about symbolic constants here, which
+	 unlike CONST_INT will always have a proper mode.  */
+      if (tmp && GET_MODE (tmp) == Pmode)
+	op1 = tmp;
+    }
 
-      if (GET_CODE (operands[0]) == MEM)
-	operands[1] = force_reg (Pmode, operands[1]);
+  if (tls_symbolic_operand (op1, Pmode))
+    {
+      op1 = legitimize_address (op1, op1, VOIDmode);
+      if (GET_CODE (op0) == MEM)
+	{
+	  tmp = gen_reg_rtx (mode);
+	  emit_insn (gen_rtx_SET (VOIDmode, tmp, op1));
+	  op1 = tmp;
+	}
+    }
+  else if (flag_pic && mode == Pmode && symbolic_operand (op1, Pmode))
+    {
+      if (GET_CODE (op0) == MEM)
+	op1 = force_reg (Pmode, op1);
       else
 	{
-	  rtx temp = operands[0];
+	  rtx temp = op0;
 	  if (GET_CODE (temp) != REG)
 	    temp = gen_reg_rtx (Pmode);
-	  temp = legitimize_pic_address (operands[1], temp);
-	  if (temp == operands[0])
+	  temp = legitimize_pic_address (op1, temp);
+	  if (temp == op0)
 	    return;
-	  operands[1] = temp;
+	  op1 = temp;
 	}
     }
   else
     {
-      if (GET_CODE (operands[0]) == MEM
+      if (GET_CODE (op0) == MEM
 	  && (PUSH_ROUNDING (GET_MODE_SIZE (mode)) != GET_MODE_SIZE (mode)
-	      || !push_operand (operands[0], mode))
-	  && GET_CODE (operands[1]) == MEM)
-	operands[1] = force_reg (mode, operands[1]);
+	      || !push_operand (op0, mode))
+	  && GET_CODE (op1) == MEM)
+	op1 = force_reg (mode, op1);
 
-      if (push_operand (operands[0], mode)
-	  && ! general_no_elim_operand (operands[1], mode))
-	operands[1] = copy_to_mode_reg (mode, operands[1]);
+      if (push_operand (op0, mode)
+	  && ! general_no_elim_operand (op1, mode))
+	op1 = copy_to_mode_reg (mode, op1);
 
       /* Force large constants in 64bit compilation into register
 	 to get them CSEed.  */
       if (TARGET_64BIT && mode == DImode
-	  && immediate_operand (operands[1], mode)
-	  && !x86_64_zero_extended_value (operands[1])
-	  && !register_operand (operands[0], mode)
+	  && immediate_operand (op1, mode)
+	  && !x86_64_zero_extended_value (op1)
+	  && !register_operand (op0, mode)
 	  && optimize && !reload_completed && !reload_in_progress)
-	operands[1] = copy_to_mode_reg (mode, operands[1]);
+	op1 = copy_to_mode_reg (mode, op1);
 
       if (FLOAT_MODE_P (mode))
 	{
@@ -6829,13 +7388,13 @@ ix86_expand_move (mode, operands)
 
 	  if (strict)
 	    ;
-	  else if (GET_CODE (operands[1]) == CONST_DOUBLE
-		   && register_operand (operands[0], mode))
-	    operands[1] = validize_mem (force_const_mem (mode, operands[1]));
+	  else if (GET_CODE (op1) == CONST_DOUBLE
+		   && register_operand (op0, mode))
+	    op1 = validize_mem (force_const_mem (mode, op1));
 	}
     }
 
-  insn = gen_rtx_SET (VOIDmode, operands[0], operands[1]);
+  insn = gen_rtx_SET (VOIDmode, op0, op1);
 
   emit_insn (insn);
 }
@@ -8684,13 +9243,14 @@ ix86_split_to_parts (operand, parts, mod
   if (size < 2 || size > 3)
     abort ();
 
-  /* Optimize constant pool reference to immediates.  This is used by fp moves,
-     that force all constants to memory to allow combining.  */
-
-  if (GET_CODE (operand) == MEM
-      && GET_CODE (XEXP (operand, 0)) == SYMBOL_REF
-      && CONSTANT_POOL_ADDRESS_P (XEXP (operand, 0)))
-    operand = get_pool_constant (XEXP (operand, 0));
+  /* Optimize constant pool reference to immediates.  This is used by fp
+     moves, that force all constants to memory to allow combining.  */
+  if (GET_CODE (operand) == MEM && RTX_UNCHANGING_P (operand))
+    {
+      rtx tmp = maybe_get_pool_constant (operand);
+      if (tmp)
+	operand = tmp;
+    }
 
   if (GET_CODE (operand) == MEM && !offsettable_memref_p (operand))
     {
@@ -9992,6 +10552,24 @@ assign_386_stack_local (mode, n)
       = assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
 
   return ix86_stack_locals[(int) mode][n];
+}
+
+/* Construct the SYMBOL_REF for the tls_get_addr function.  */
+
+rtx
+ix86_tls_get_addr ()
+{
+  static rtx symbol;
+
+  if (!symbol)
+    {
+      symbol = gen_rtx_SYMBOL_REF (Pmode, (TARGET_GNU_TLS
+					   ? "___tls_get_addr"
+					   : "__tls_get_addr"));
+      ggc_add_rtx_root (&symbol, 1);
+    }
+
+  return symbol;
 }
 
 /* Calculate the length of the memory address in the instruction
Index: config/i386/i386.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386.h,v
retrieving revision 1.262
diff -c -p -d -u -r1.262 i386.h
--- config/i386/i386.h	23 May 2002 08:17:14 -0000	1.262
+++ config/i386/i386.h	23 May 2002 21:35:41 -0000
@@ -282,6 +282,9 @@ extern int x86_prefetch_sse;
 
 #define TARGET_RED_ZONE (!(target_flags & MASK_NO_RED_ZONE))
 
+#define TARGET_GNU_TLS (ix86_tls_dialect == TLS_DIALECT_GNU)
+#define TARGET_SUN_TLS (ix86_tls_dialect == TLS_DIALECT_SUN)
+
 /* WARNING: Do not mark empty strings for translation, as calling
             gettext on an empty string does NOT return an empty
             string. */
@@ -426,6 +429,8 @@ extern int x86_prefetch_sse;
     "" /* Undocumented. */ },					\
   { "asm=", &ix86_asm_string,					\
     N_("Use given assembler dialect") },			\
+  { "tls-dialect=", &ix86_tls_dialect_string,			\
+    N_("Use given thread-local storage dialect") },		\
   SUBTARGET_OPTIONS						\
 }
 
@@ -1876,15 +1881,12 @@ do {									\
 
 #define MAX_REGS_PER_ADDRESS 2
 
-#define CONSTANT_ADDRESS_P(X)					\
-  (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF	\
-   || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST	\
-   || GET_CODE (X) == CONST_DOUBLE)
+#define CONSTANT_ADDRESS_P(X)  constant_address_p (X)
 
 /* Nonzero if the constant value X is a legitimate general operand.
    It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.  */
 
-#define LEGITIMATE_CONSTANT_P(X) 1
+#define LEGITIMATE_CONSTANT_P(X)  legitimate_constant_p (X)
 
 #ifdef REG_OK_STRICT
 #define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR)				\
@@ -1947,9 +1949,7 @@ do {									\
    when generating PIC code.  It is given that flag_pic is on and 
    that X satisfies CONSTANT_P or is a CONST_DOUBLE.  */
 
-#define LEGITIMATE_PIC_OPERAND_P(X)		\
-  (! SYMBOLIC_CONST (X)				\
-   || legitimate_pic_address_disp_p (X))
+#define LEGITIMATE_PIC_OPERAND_P(X) legitimate_pic_operand_p (X)
 
 #define SYMBOLIC_CONST(X)	\
   (GET_CODE (X) == SYMBOL_REF						\
@@ -2392,7 +2392,20 @@ enum ix86_builtins
   IX86_BUILTIN_MAX
 };
 
-#define TARGET_ENCODE_SECTION_INFO  i386_encode_section_info
+#define TARGET_ENCODE_SECTION_INFO  ix86_encode_section_info
+#define TARGET_STRIP_NAME_ENCODING  ix86_strip_name_encoding
+
+#define ASM_OUTPUT_LABELREF(FILE,NAME)		\
+  do {						\
+    const char *xname = (NAME);			\
+    if (xname[0] == '%')			\
+      xname += 2;				\
+    if (xname[0] == '*')			\
+      xname += 1;				\
+    else					\
+      fputs (user_label_prefix, FILE);		\
+    fputs (xname, FILE);			\
+  } while (0)
 
 /* The `FINALIZE_PIC' macro serves as a hook to emit these special
    codes once the function is being compiled into assembly code, but
@@ -3047,7 +3060,7 @@ extern int const svr4_dbx_register_map[F
    print_operand function.  */
 
 #define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
-  ((CODE) == '*' || (CODE) == '+')
+  ((CODE) == '*' || (CODE) == '+' || (CODE) == '&')
 
 /* Print the name of a register based on its machine mode and number.
    If CODE is 'w', pretend the mode is HImode.
@@ -3066,6 +3079,12 @@ extern int const svr4_dbx_register_map[F
 #define PRINT_OPERAND_ADDRESS(FILE, ADDR)  \
   print_operand_address ((FILE), (ADDR))
 
+#define OUTPUT_ADDR_CONST_EXTRA(FILE, X, FAIL)	\
+do {						\
+  if (! output_addr_const_extra (FILE, (X)))	\
+    goto FAIL;					\
+} while (0);
+
 /* Print the name of a register for based on its machine mode and number.
    This macro is used to print debugging output.
    This macro is different from PRINT_REG in that it may be used in
@@ -3195,7 +3214,12 @@ extern int const svr4_dbx_register_map[F
   {"memory_displacement_operand", {MEM}},				\
   {"cmpsi_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,	\
 		     LABEL_REF, SUBREG, REG, MEM, AND}},		\
-  {"long_memory_operand", {MEM}},
+  {"long_memory_operand", {MEM}},					\
+  {"tls_symbolic_operand", {SYMBOL_REF}},				\
+  {"global_dynamic_symbolic_operand", {SYMBOL_REF}},			\
+  {"local_dynamic_symbolic_operand", {SYMBOL_REF}},			\
+  {"initial_exec_symbolic_operand", {SYMBOL_REF}},			\
+  {"local_exec_symbolic_operand", {SYMBOL_REF}},
 
 /* A list of predicates that do special things with modes, and so
    should not elicit warnings for VOIDmode match_operand.  */
@@ -3232,6 +3256,15 @@ enum fpmath_unit
 
 extern enum fpmath_unit ix86_fpmath;
 extern const char *ix86_fpmath_string;
+
+enum tls_dialect
+{
+  TLS_DIALECT_GNU,
+  TLS_DIALECT_SUN
+};
+
+extern enum tls_dialect ix86_tls_dialect;
+extern const char *ix86_tls_dialect_string;
 
 enum cmodel {
   CM_32,	/* The traditional 32-bit ABI.  */
Index: config/i386/i386.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386.md,v
retrieving revision 1.366
diff -c -p -d -u -r1.366 i386.md
--- config/i386/i386.md	23 May 2002 09:29:24 -0000	1.366
+++ config/i386/i386.md	23 May 2002 21:35:42 -0000
@@ -53,23 +53,36 @@
 ;; UNSPEC usage:
 
 (define_constants
-  [(UNSPEC_SCAS			0)
-   (UNSPEC_SIN			1)
-   (UNSPEC_COS			2)
-   (UNSPEC_STACK_PROBE		3)
-   (UNSPEC_STACK_ALLOC		4)
-   (UNSPEC_BSF			5)
-   (UNSPEC_GOT			6)
-   (UNSPEC_GOTOFF		7)
-   (UNSPEC_PLT			8)
-   (UNSPEC_FNSTSW		9)
-   (UNSPEC_SAHF			10)
-   (UNSPEC_FSTCW		11)
-   (UNSPEC_ADD_CARRY		12)
+  [; Relocation specifiers
+   (UNSPEC_GOT			0)
+   (UNSPEC_GOTOFF		1)
+   (UNSPEC_GOTPCREL		2)
+   (UNSPEC_GOTTPOFF		3)
+   (UNSPEC_TPOFF		4)
+   (UNSPEC_NTPOFF		5)
+   (UNSPEC_DTPOFF		6)
+
+   ; Prologue support
+   (UNSPEC_STACK_PROBE		10)
+   (UNSPEC_STACK_ALLOC		11)
+   (UNSPEC_SET_GOT		12)
    (UNSPEC_SSE_PROLOGUE_SAVE	13)
-   (UNSPEC_FLDCW		14)
-   (UNSPEC_GOTPCREL		15)
-   (UNSPEC_SET_GOT		16)
+
+   ; TLS support
+   (UNSPEC_TP			15)
+   (UNSPEC_TLS_GD		16)
+   (UNSPEC_TLS_LD_BASE		17)
+
+   ; Other random patterns
+   (UNSPEC_SCAS			20)
+   (UNSPEC_SIN			21)
+   (UNSPEC_COS			22)
+   (UNSPEC_BSF			23)
+   (UNSPEC_FNSTSW		24)
+   (UNSPEC_SAHF			25)
+   (UNSPEC_FSTCW		26)
+   (UNSPEC_ADD_CARRY		27)
+   (UNSPEC_FLDCW		28)
 
    ; For SSE/MMX support:
    (UNSPEC_FIX			30)
@@ -13329,6 +13342,127 @@
 
 ;; ffshi2 is not useful -- 4 word prefix ops are needed, which is larger
 ;; and slower than the two-byte movzx insn needed to do the work in SImode.
+
+;; Thread-local storage patterns for ELF.
+;;
+;; Note that these code sequences must appear exactly as shown
+;; in order to allow linker relaxation.
+
+(define_insn "*tls_global_dynamic_gnu"
+  [(set (match_operand:SI 0 "register_operand" "=a")
+	(unspec:SI [(match_operand:SI 1 "register_operand" "b")
+		    (match_operand:SI 2 "tls_symbolic_operand" "")
+		    (match_operand:SI 3 "call_insn_operand" "")]
+		    UNSPEC_TLS_GD))
+   (clobber (match_scratch:SI 4 "=d"))
+   (clobber (match_scratch:SI 5 "=c"))
+   (clobber (reg:CC 17))]
+  "TARGET_GNU_TLS"
+  "lea{l}\t{%a2@TLSGD(,%1,1), %0|%0, %a2@TLSGD[%1*1]}\;call\t%P3"
+  [(set_attr "type" "multi")
+   (set_attr "length" "12")])
+
+(define_insn "*tls_global_dynamic_sun"
+  [(set (match_operand:SI 0 "register_operand" "=a")
+	(unspec:SI [(match_operand:SI 1 "register_operand" "b")
+		    (match_operand:SI 2 "tls_symbolic_operand" "")
+		    (match_operand:SI 3 "call_insn_operand" "")]
+		    UNSPEC_TLS_GD))
+   (clobber (match_scratch:SI 4 "=d"))
+   (clobber (match_scratch:SI 5 "=c"))
+   (clobber (reg:CC 17))]
+  "TARGET_SUN_TLS"
+  "lea{l}\t{%a2@DTLNDX(%1), %4|%4, %a2@DTLNDX[%1]}
+	push{l}\t%4\;call\t%a2@TLSPLT\;pop{l}\t%4\;nop"
+  [(set_attr "type" "multi")
+   (set_attr "length" "14")])
+
+(define_expand "tls_global_dynamic"
+  [(parallel [(set (match_operand:SI 0 "register_operand" "")
+		   (unspec:SI
+		    [(match_dup 2)
+		     (match_operand:SI 1 "tls_symbolic_operand" "")
+		     (match_dup 3)]
+		    UNSPEC_TLS_GD))
+	      (clobber (match_scratch:SI 4 ""))
+	      (clobber (match_scratch:SI 5 ""))
+	      (clobber (reg:CC 17))])]
+  ""
+{
+  if (!flag_pic)
+    abort ();
+  current_function_uses_pic_offset_table = 1;
+  operands[2] = pic_offset_table_rtx;
+  operands[3] = ix86_tls_get_addr ();
+})
+
+(define_insn "*tls_local_dynamic_base_gnu"
+  [(set (match_operand:SI 0 "register_operand" "=a")
+	(unspec:SI [(match_operand:SI 1 "register_operand" "b")
+                    (match_operand:SI 2 "call_insn_operand" "")]
+		   UNSPEC_TLS_LD_BASE))
+   (clobber (match_scratch:SI 3 "=d"))
+   (clobber (match_scratch:SI 4 "=c"))
+   (clobber (reg:CC 17))]
+  "TARGET_GNU_TLS"
+  "lea{l}\t{%&@TLSLDM(%1), %0|%0, %&@TLSLDM[%1]}\;call\t%P2"
+  [(set_attr "type" "multi")
+   (set_attr "length" "11")])
+
+(define_insn "*tls_local_dynamic_base_sun"
+  [(set (match_operand:SI 0 "register_operand" "=a")
+	(unspec:SI [(match_operand:SI 1 "register_operand" "b")
+                    (match_operand:SI 2 "call_insn_operand" "")]
+		   UNSPEC_TLS_LD_BASE))
+   (clobber (match_scratch:SI 3 "=d"))
+   (clobber (match_scratch:SI 4 "=c"))
+   (clobber (reg:CC 17))]
+  "TARGET_SUN_TLS"
+  "lea{l}\t{%&@TMDNX(%1), %3|%3, %&@TMDNX[%1]}
+	push{l}\t%3\;call\t%&@TLSPLT\;pop{l}\t%3"
+  [(set_attr "type" "multi")
+   (set_attr "length" "13")])
+
+(define_expand "tls_local_dynamic_base"
+  [(parallel [(set (match_operand:SI 0 "register_operand" "")
+		   (unspec:SI [(match_dup 1) (match_dup 2)]
+			      UNSPEC_TLS_LD_BASE))
+	      (clobber (match_scratch:SI 3 ""))
+	      (clobber (match_scratch:SI 4 ""))
+	      (clobber (reg:CC 17))])]
+  ""
+{
+  if (!flag_pic)
+    abort ();
+  current_function_uses_pic_offset_table = 1;
+  operands[1] = pic_offset_table_rtx;
+  operands[2] = ix86_tls_get_addr ();
+})
+
+;; Local dynamic of a single variable is a lose.  Show combine how
+;; to convert that back to global dynamic.
+
+(define_insn_and_split "*tls_local_dynamic_once"
+  [(set (match_operand:SI 0 "register_operand" "=a")
+	(plus:SI (unspec:SI [(match_operand:SI 1 "register_operand" "b")
+			     (match_operand:SI 2 "call_insn_operand" "")]
+			    UNSPEC_TLS_LD_BASE)
+		 (const:SI (unspec:SI
+			    [(match_operand:SI 3 "tls_symbolic_operand" "")]
+			    UNSPEC_DTPOFF))))
+   (clobber (match_scratch:SI 4 "=d"))
+   (clobber (match_scratch:SI 5 "=c"))
+   (clobber (reg:CC 17))]
+  ""
+  "#"
+  ""
+  [(parallel [(set (match_dup 0)
+		   (unspec:SI [(match_dup 1) (match_dup 3) (match_dup 2)]
+			      UNSPEC_TLS_GD))
+	      (clobber (match_dup 4))
+	      (clobber (match_dup 5))
+	      (clobber (reg:CC 17))])]
+  "")
 
 ;; These patterns match the binary 387 instructions for addM3, subM3,
 ;; mulM3 and divM3.  There are three patterns for each of DFmode and


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