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]

[csl-arm] Thread-local storage support


This patch, originally created by Phil Blundell and since adjusted by me,
adds thread-local storage support for ARM.  It has been extensively tested
on arm-linux-gnu and arm-linux-gnueabi, using a to-be-submitted port of
NPTL and the binutils support I posted earlier today.

This patch implements three models (everything but LD), and three ways to
access the thread pointer (via mrc to a new register in the latest ARM
cores, via ldr at a magic address, and via a function call to an EABI
support library).  An arm-eabi toolchain defaults to the portable option;
with -mabi=aapcs-linux, GCC defaults to the ldr based approach, which
current Linux kernels implement.  A builtin is also provided to access the
thread pointer (__builtin_thread_pointer ()).

The support routine is assumed to have a special calling convention, which
clobbers only IP, LR, CC, and R0.

Phil also implemented improved PC-relative symbol access for local symbols.
This allows static and hidden variables to be referenced without going
through the GOT.

The patch isn't quite ready for mainline; I want to implement the missing
local dynamic support, and clean up a couple of other details.  However,
I have high confidence that the implemented bits work correctly.

I'd like to apply this to csl-arm-branch.  OK?

-- 
Daniel Jacobowitz
CodeSourcery, LLC

2005-03-25  Daniel Jacobowitz  <dan@codesourcery.com>
	    Phil Blundell  <philb@gnu.org>

	* configure.ac: Add test for ARM TLS support.
	* configure: Regenerated.
	* haifa-sched.c (schedule_block): Only count INSNs.
	* jump.c (duplicate_loop_exit_test): Check targetm.cannot_copy_insn_p.
	* postreload.c (reload_cse_simplify_operands): Handle deleted labels.
	* config/arm/arm-protos.h (legitimize_tls_address)
	(thumb_legitimize_address, tls_symbolic_operand, arm_tls_operand_p)
	(tls_mentioned_p, arm_output_addr_const_extra): New prototypes.
	(thumb_legitimize_pic_address): Delete.
	* config/arm/arm.c: Include "gt-arm.h".
	(arm_cannot_copy_insn_p, load_tls_operand)
	(pcrel_constant_p, get_tls_get_addr, arm_load_tp)
	(legitimize_tls_address, thumb_legitimize_address)
	(arm_tls_operand_p, tls_symbolic_operand, move_input_operand)
	(tls_mentioned_p, arm_init_tls_builtins)
	(arm_emit_tls_decoration, arm_output_addr_const_extra): New functions.
	(TARGET_CANNOT_COPY_INSN_P, TARGET_CANNOT_FORCE_CONST_MEM): Define.
	(target_thread_switch, target_thread_pointer): New.
	(arm_pic_register): Change to unsigned.
	(arm_override_options): Handle -mtp=.
	(legitimize_pic_address): Ignore UNSPECs.
	(arm_legitimate_address_p): Handle TLS and pcrel symbols.
	(thumb_legitimate_address_p): Handle TLS and pcrel symbols.
	(arm_legitimize_address): Handle TLS.
	(arm_regno_class): Handle RETURN_REG.
	(arm_init_builtins): Call arm_init_tls_builtins.
	(arm_expand_builtin): Handle ARM_BUILTIN_THREAD_POINTER.
	(arm_encode_section_info): Call default_encode_section_info.
	* config/arm/arm.h (target_thread_switch): Add declaration.
	(TARGET_HARD_TP, TARGET_SOFT_TP, TARGET_LINUX_TP): Define.
	(TARGET_OPTIONS): Add -mtp=.
	(enum arm_tp_type): New.
	(target_thread_pointer): Add declaration.
	(enum reg_class, REG_CLASS_NAMES, REG_CLASS_CONTENTS)
	(REG_CLASS_FROM_LETTER): Add RETURN_REG.
	(LEGITIMATE_CONSTANT_P): Handle TLS.
	(THUMB_LEGITIMIZE_ADDRESS): Call thumb_legitimize_address.
	(arm_pic_register): Change type to unsigned.
	(LEGITIMATE_PIC_OPERAND_P): Handle TLS.
	(OUTPUT_ADDR_CONST_EXTRA): Call arm_output_addr_const_extra.
	(PREDICATE_CODES): Add move_input_operand.
	(enum arm_builtins): Add ARM_BUILTIN_THREAD_POINTER.
	* config/arm/arm.md: Add UNSPEC_TLS.
	(movsi): Handle TLS.
	(arm_movsi_insn, thumb_movsi_insn): Use move_input_operand.
	(pic_add_dot_plus_four, pic_add_dot_plus_eight): Allow for
	non-PIC.
	(tls_load_dot_plus_four, tls_load_dot_plus_eight): New insns
	and new peepholes to create them.
	(load_tp_hard, load_tp_soft): New insns.

Index: gcc/gcc/configure.ac
===================================================================
--- gcc.orig/gcc/configure.ac	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/configure.ac	2005-03-25 13:27:50.382577331 -0500
@@ -1,7 +1,7 @@
 # configure.ac for GCC
 # Process this file with autoconf to generate a configuration script.
 
-# Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+# Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
 # Free Software Foundation, Inc.
 
 #This file is part of GCC.
@@ -2102,6 +2102,19 @@ foo:	.long	25
 	tls_first_minor=13
 	tls_as_opt=--fatal-warnings
 	;;
+  arm*-*-*)
+    conftest_s='
+	.section ".tdata","awT",%progbits
+foo:	.long	25
+	.text
+.word foo(gottpoff)
+.word foo(tpoff)
+.word foo(tlsgd)
+.word foo(tlsldm)
+.word foo(tlsldo)'
+	tls_first_major=2
+	tls_first_minor=17
+	;;
   i[34567]86-*-*)
     conftest_s='
 	.section ".tdata","awT",@progbits
Index: gcc/gcc/haifa-sched.c
===================================================================
--- gcc.orig/gcc/haifa-sched.c	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/haifa-sched.c	2005-03-25 13:28:07.291608913 -0500
@@ -1,6 +1,6 @@
 /* Instruction scheduling pass.
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by,
    and currently maintained by, Jim Wilson (wilson@cygnus.com)
 
@@ -2563,8 +2563,8 @@ schedule_block (int b, int rgn_n_insns)
 					       insn, can_issue_more);
 	  /* A naked CLOBBER or USE generates no instruction, so do
 	     not count them against the issue rate.  */
-	  else if (GET_CODE (PATTERN (insn)) != USE
-		   && GET_CODE (PATTERN (insn)) != CLOBBER)
+	  else if (INSN_P (insn) && (GET_CODE (PATTERN (insn)) != USE
+				     && GET_CODE (PATTERN (insn)) != CLOBBER))
 	    can_issue_more--;
 
 	  advance = schedule_insn (insn, &ready, clock_var);
Index: gcc/gcc/jump.c
===================================================================
--- gcc.orig/gcc/jump.c	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/jump.c	2005-03-25 13:28:17.139297085 -0500
@@ -1,6 +1,6 @@
 /* Optimize jump instructions, for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997
-   1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   1998, 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -55,6 +55,7 @@ Software Foundation, 59 Temple Place - S
 #include "reload.h"
 #include "predict.h"
 #include "timevar.h"
+#include "target.h"
 
 /* Optimize jump y; x: ... y: jumpif... x?
    Don't know if it is worth bothering with.  */
@@ -335,6 +336,7 @@ duplicate_loop_exit_test (rtx loop_start
 	 is a CODE_LABEL
 	 has a REG_RETVAL or REG_LIBCALL note (hard to adjust)
 	 is a NOTE_INSN_LOOP_BEG because this means we have a nested loop
+         is not copyable
 
      We also do not do this if we find an insn with ASM_OPERANDS.  While
      this restriction should not be necessary, copying an insn with
@@ -348,6 +350,10 @@ duplicate_loop_exit_test (rtx loop_start
 	     && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END);
        insn = NEXT_INSN (insn))
     {
+      if (targetm.cannot_copy_insn_p
+	  && (*targetm.cannot_copy_insn_p) (insn))
+	return 0;
+
       switch (GET_CODE (insn))
 	{
 	case CODE_LABEL:
Index: gcc/gcc/postreload.c
===================================================================
--- gcc.orig/gcc/postreload.c	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/postreload.c	2005-03-25 13:28:24.687524752 -0500
@@ -1,6 +1,6 @@
 /* Perform simple optimizations to clean up the result of reload.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -410,6 +410,8 @@ reload_cse_simplify_operands (rtx insn, 
 	 right, so avoid the problem here.  Likewise if we have a constant
          and the insn pattern doesn't tell us the mode we need.  */
       if (GET_CODE (recog_data.operand[i]) == CODE_LABEL
+	  || (GET_CODE (recog_data.operand[i]) == NOTE
+	      && NOTE_LINE_NUMBER (recog_data.operand[i]) == NOTE_INSN_DELETED_LABEL)
 	  || (CONSTANT_P (recog_data.operand[i])
 	      && recog_data.operand_mode[i] == VOIDmode))
 	continue;
Index: gcc/gcc/config/arm/arm-protos.h
===================================================================
--- gcc.orig/gcc/config/arm/arm-protos.h	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/config/arm/arm-protos.h	2005-03-25 13:27:36.373863989 -0500
@@ -1,5 +1,5 @@
 /* Prototypes for exported functions defined in arm.c and pe.c
-   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
+   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
    Free Software Foundation, Inc.
    Contributed by Richard Earnshaw (rearnsha@arm.com)
    Minor hacks by Nick Clifton (nickc@cygnus.com)
@@ -52,13 +52,17 @@ extern int arm_split_constant (RTX_CODE,
 extern RTX_CODE arm_canonicalize_comparison (RTX_CODE, rtx *);
 extern int legitimate_pic_operand_p (rtx);
 extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx);
+extern rtx legitimize_tls_address (rtx, unsigned int, rtx);
 extern int arm_legitimate_address_p  (enum machine_mode, rtx, RTX_CODE, int);
 extern int thumb_legitimate_address_p (enum machine_mode, rtx, int);
 extern int thumb_legitimate_offset_p (enum machine_mode, HOST_WIDE_INT);
 extern rtx arm_legitimize_address (rtx, rtx, enum machine_mode);
+extern rtx thumb_legitimize_address (rtx, rtx, enum machine_mode);
 extern int arm_const_double_rtx (rtx);
 extern int neg_const_double_rtx_ok_for_fpa (rtx);
 extern enum reg_class vfp_secondary_reload_class (enum machine_mode, rtx);
+extern int tls_symbolic_operand (rtx, enum machine_mode);
+extern bool arm_tls_operand_p (rtx x);
 
 /* Predicates.  */
 extern int s_register_operand (rtx, enum machine_mode);
@@ -108,6 +112,7 @@ extern int arm_no_early_alu_shift_dep (r
 extern int arm_no_early_alu_shift_value_dep (rtx, rtx);
 extern int arm_no_early_mul_dep (rtx, rtx);
 
+extern int tls_mentioned_p (rtx);
 extern int symbol_mentioned_p (rtx);
 extern int label_mentioned_p (rtx);
 extern RTX_CODE minmax_code (rtx);
@@ -153,6 +158,8 @@ extern const char * arm_output_load_gr (
 extern const char *vfp_output_fstmx (rtx *);
 extern void arm_set_return_address (rtx, rtx);
 
+extern bool arm_output_addr_const_extra (FILE *, rtx);
+
 #if defined TREE_CODE
 extern rtx arm_function_arg (CUMULATIVE_ARGS *, enum machine_mode, tree, int);
 extern void arm_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree);
@@ -196,7 +203,6 @@ extern const char *thumb_output_move_mem
 extern void thumb_expand_movstrqi (rtx *);
 extern int thumb_cmp_operand (rtx, enum machine_mode);
 extern int thumb_cbrch_target_operand (rtx, enum machine_mode);
-extern rtx *thumb_legitimize_pic_address (rtx, enum machine_mode, rtx);
 extern int thumb_go_if_legitimate_address (enum machine_mode, rtx);
 extern rtx arm_return_addr (int, rtx);
 extern void thumb_reload_out_hi (rtx *);
Index: gcc/gcc/config/arm/arm.c
===================================================================
--- gcc.orig/gcc/config/arm/arm.c	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/config/arm/arm.c	2005-03-25 13:35:18.882869922 -0500
@@ -1,6 +1,6 @@
 /* Output routines for GCC for ARM.
    Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-   2002, 2003, 2004  Free Software Foundation, Inc.
+   2002, 2003, 2004, 2005  Free Software Foundation, Inc.
    Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
    and Martin Simmons (@harleqn.co.uk).
    More major hacks by Richard Earnshaw (rearnsha@arm.com).
@@ -144,6 +144,9 @@ static rtx arm_expand_binop_builtin (enu
 static rtx arm_expand_unop_builtin (enum insn_code, tree, rtx, int);
 static rtx arm_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
 static void emit_constant_insn (rtx cond, rtx pattern);
+static bool arm_cannot_copy_insn_p (rtx);
+
+static rtx load_tls_operand (rtx, rtx);
 
 #ifdef OBJECT_FORMAT_ELF
 static void arm_elf_asm_constructor (rtx, int);
@@ -316,6 +319,17 @@ static bool arm_return_in_msb (tree);
 #undef TARGET_RETURN_IN_MSB
 #define TARGET_RETURN_IN_MSB arm_return_in_msb
 
+#undef  TARGET_CANNOT_COPY_INSN_P
+#define TARGET_CANNOT_COPY_INSN_P arm_cannot_copy_insn_p
+
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+#endif
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM arm_tls_operand_p
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 /* Obstack for minipool constant handling.  */
@@ -368,6 +382,11 @@ const char * target_float_switch = NULL;
 /* Set by the -mabi=... option.  */
 const char * target_abi_name = NULL;
 
+/* Set by the -mtp=... option.  */
+const char * target_thread_switch = NULL;
+
+enum arm_tp_type target_thread_pointer;
+
 /* Used to parse -mstructure_size_boundary command line option.  */
 const char * structure_size_string = NULL;
 int    arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY;
@@ -467,7 +486,7 @@ enum machine_mode output_memory_referenc
 
 /* The register number to be used for the PIC offset register.  */
 const char * arm_pic_register_string = NULL;
-int arm_pic_register = INVALID_REGNUM;
+unsigned int arm_pic_register = INVALID_REGNUM;
 
 /* Set to 1 when a return insn is output, this means that the epilogue
    is not needed.  */
@@ -1108,7 +1127,28 @@ arm_override_options (void)
        || arm_fpu_tune == FPUTYPE_FPA_EMU3)
       && (tune_flags & FL_MODE32) == 0)
     flag_schedule_insns = flag_schedule_insns_after_reload = 0;
-  
+
+  /* Default to the appropriate thread pointer access method.  */
+  if (arm_abi == ARM_ABI_AAPCS_LINUX)
+    target_thread_pointer = TP_LINUX;
+  else
+    target_thread_pointer = TP_SOFT;
+
+  if (target_thread_switch)
+    {
+      if (strcmp (target_thread_switch, "soft") == 0)
+	target_thread_pointer = TP_SOFT;
+      else if (strcmp (target_thread_switch, "cp15") == 0)
+	target_thread_pointer = TP_CP15;
+      else if (strcmp (target_thread_switch, "linux") == 0)
+	target_thread_pointer = TP_LINUX;
+      else
+	error ("invalid thread pointer option: -mtp=%s", target_thread_switch);
+    }
+
+  if (TARGET_HARD_TP && TARGET_THUMB)
+    error ("can not use -mtp=cp15 with -mthumb");
+
   /* Override the default structure alignment for AAPCS ABI.  */
   if (TARGET_AAPCS_BASED)
     arm_structure_size_boundary = 8;
@@ -3120,6 +3160,9 @@ legitimize_pic_address (rtx orig, enum m
 	  && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
 	return orig;
 
+      if (GET_CODE (XEXP (orig, 0)) == UNSPEC)
+	return orig;
+
       if (reg == 0)
 	{
 	  if (no_new_pseudos)
@@ -3267,6 +3310,24 @@ arm_address_register_rtx_p (rtx x, int s
 	  || regno == ARG_POINTER_REGNUM);
 }
 
+/* Return TRUE if this rtx is the difference of a symbol and a label,
+   and will reduce to a PC-relative relocation in the object file.
+   Expressions like this can be left alone when generating PIC, rather
+   than forced through the GOT.  */
+static int
+pcrel_constant_p (rtx x)
+{
+  if (GET_CODE (x) == MINUS)
+    return symbol_mentioned_p (XEXP (x, 0)) && label_mentioned_p (XEXP (x, 1));
+
+  if (GET_CODE (x) == UNSPEC
+      && XINT (x, 1) == UNSPEC_TLS
+      && INTVAL (XVECEXP (x, 0, 1)) == TLS_MODEL_LOCAL_EXEC)
+    return 1;			/* This is actually an offset from $tp.  */
+
+  return FALSE;
+}
+
 /* Return nonzero if X is a valid ARM state address operand.  */
 int
 arm_legitimate_address_p (enum machine_mode mode, rtx x, RTX_CODE outer,
@@ -3344,8 +3405,10 @@ arm_legitimate_address_p (enum machine_m
   else if (GET_MODE_CLASS (mode) != MODE_FLOAT
 	   && code == SYMBOL_REF
 	   && CONSTANT_POOL_ADDRESS_P (x)
+	   && ! tls_symbolic_operand (x, VOIDmode)
 	   && ! (flag_pic
-		 && symbol_mentioned_p (get_pool_constant (x))))
+		 && (symbol_mentioned_p (get_pool_constant (x))
+		     && ! pcrel_constant_p (get_pool_constant (x)))))
     return 1;
 
   return 0;
@@ -3508,7 +3571,7 @@ thumb_legitimate_address_p (enum machine
   /* This is PC relative data before arm_reorg runs.  */
   else if (GET_MODE_SIZE (mode) >= 4 && CONSTANT_P (x)
 	   && GET_CODE (x) == SYMBOL_REF
-           && CONSTANT_POOL_ADDRESS_P (x) && ! flag_pic)
+           && CONSTANT_POOL_ADDRESS_P (x) && ! flag_pic && ! tls_symbolic_operand (x, VOIDmode))
     return 1;
 
   /* This is PC relative data after arm_reorg runs.  */
@@ -3570,8 +3633,10 @@ thumb_legitimate_address_p (enum machine
 	   && GET_MODE_SIZE (mode) == 4
 	   && GET_CODE (x) == SYMBOL_REF
 	   && CONSTANT_POOL_ADDRESS_P (x)
-	   && !(flag_pic
-		&& symbol_mentioned_p (get_pool_constant (x))))
+	   && ! tls_symbolic_operand (x, VOIDmode)
+	   && ! (flag_pic
+		 && (symbol_mentioned_p (get_pool_constant (x))
+		     && ! pcrel_constant_p (get_pool_constant (x)))))
     return 1;
 
   return 0;
@@ -3597,11 +3662,151 @@ thumb_legitimate_offset_p (enum machine_
     }
 }
 
+/* Build the SYMBOL_REF for __tls_get_addr.  */
+
+static GTY(()) rtx tls_get_addr_libfunc;
+
+static rtx
+get_tls_get_addr (void)
+{
+  if (!tls_get_addr_libfunc)
+    tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr");
+  return tls_get_addr_libfunc;
+}
+
+static rtx
+arm_load_tp (rtx target)
+{
+  if (TARGET_HARD_TP)
+    {
+      /* Can return in any reg.  */
+      if (!target)
+	target = gen_reg_rtx (SImode);
+      
+      emit_insn (gen_load_tp_hard (target));
+    }
+  else if (TARGET_LINUX_TP)
+    {
+      rtx tp;
+
+      /* Can return in any reg.  */
+      if (!target)
+	target = gen_reg_rtx (SImode);
+
+      tp = gen_rtx_MEM (Pmode, gen_int_mode (0xffff0ffc, Pmode));
+      RTX_UNCHANGING_P (tp) = 1;
+      emit_move_insn (target, tp);
+    }
+  else
+    {
+      /* Always returned in R0 */
+      target = gen_rtx_REG (SImode, 0);
+      
+      emit_insn (gen_load_tp_soft (target));
+    }
+  return target;
+}
+
+/* Load a TLS operand from memory.  */
+static rtx
+load_tls_operand (rtx x, rtx reg)
+{
+  rtx tmp;
+
+  if (reg == NULL_RTX)
+    reg = gen_reg_rtx (SImode);
+
+  tmp = gen_rtx_CONST (SImode, x);
+
+  emit_move_insn (reg, tmp);
+
+  return reg;
+}
+
+rtx
+legitimize_tls_address (rtx x, unsigned int model, rtx reg)
+{
+  rtx dest, tp, tmp, label, dummy_label, sum, insn;
+
+  switch (model)
+    {
+    case TLS_MODEL_LOCAL_DYNAMIC:		/* XXX */
+    case TLS_MODEL_GLOBAL_DYNAMIC:
+      label = gen_label_rtx ();
+      dummy_label = gen_label_rtx ();
+      LABEL_PRESERVE_P (dummy_label) = 1;
+      LABEL_NUSES (dummy_label) = 1;
+
+      sum = gen_rtx_UNSPEC (Pmode, 
+			    gen_rtvec (4, x, GEN_INT (model),
+				       gen_rtx_LABEL_REF (Pmode, label),
+				       GEN_INT (TARGET_ARM ? 8 : 4)),
+			    UNSPEC_TLS);
+      reg = load_tls_operand (sum, reg);
+
+      if (TARGET_ARM)
+	insn = emit_insn (gen_pic_add_dot_plus_eight (reg, label));
+      else
+	insn = emit_insn (gen_pic_add_dot_plus_four (reg, label));
+
+      emit_label_before (dummy_label, insn);
+
+      return emit_library_call_value (get_tls_get_addr (), 0, LCT_PURE,
+				      Pmode, 1, reg, Pmode);
+
+    case TLS_MODEL_INITIAL_EXEC:
+      label = gen_label_rtx ();
+      dummy_label = gen_label_rtx ();
+      LABEL_PRESERVE_P (dummy_label) = 1;
+      LABEL_NUSES (dummy_label) = 1;
+
+      sum = gen_rtx_UNSPEC (Pmode, 
+			    gen_rtvec (4, x, GEN_INT (model),
+				       gen_rtx_LABEL_REF (Pmode, label),
+				       GEN_INT (TARGET_ARM ? 8 : 4)),
+			    UNSPEC_TLS);
+      reg = load_tls_operand (sum, reg);
+
+      if (TARGET_ARM)
+	insn = emit_insn (gen_tls_load_dot_plus_eight (reg, reg, label, dummy_label));
+      else
+	insn = emit_insn (gen_tls_load_dot_plus_four (reg, reg, label, dummy_label));
+
+      emit_label_before (dummy_label, insn);
+
+      tp = arm_load_tp (NULL_RTX);
+
+      return gen_rtx_PLUS (Pmode, tp, reg);
+
+    case TLS_MODEL_LOCAL_EXEC:
+      tp = arm_load_tp (NULL_RTX);
+
+      tmp = gen_rtx_UNSPEC (Pmode, 
+			    gen_rtvec (2, x, GEN_INT (model)), 
+			    UNSPEC_TLS);
+
+      reg = force_reg (SImode, gen_rtx_CONST (SImode, tmp));
+
+      return gen_rtx_PLUS (Pmode, tp, reg);
+
+    default:
+      abort ();
+    }
+
+  return dest;
+}
+
 /* Try machine-dependent ways of modifying an illegitimate address
    to be legitimate.  If we find one, return the new, valid address.  */
 rtx
 arm_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode)
 {
+  unsigned tls;
+
+  tls = tls_symbolic_operand (x, mode);
+  if (tls)
+    return legitimize_tls_address (x, tls, NULL_RTX);
+  
   if (GET_CODE (x) == PLUS)
     {
       rtx xop0 = XEXP (x, 0);
@@ -3681,6 +3886,51 @@ arm_legitimize_address (rtx x, rtx orig_
   return x;
 }
 
+rtx
+thumb_legitimize_address (rtx x, rtx orig_x ATTRIBUTE_UNUSED, enum machine_mode mode)
+{
+  unsigned tls;
+
+  tls = tls_symbolic_operand (x, mode);
+  if (tls)
+    return legitimize_tls_address (x, tls, NULL_RTX);
+
+  if (flag_pic)
+    return legitimize_pic_address (x, mode, NULL_RTX);
+
+  return x;
+}
+
+/* Test for various thread-local symbols.  */
+
+/* Return 1 if X is a thread-local symbol.  */
+
+bool
+arm_tls_operand_p (rtx x)
+{
+  if (! TARGET_HAVE_TLS)
+    return false;
+
+  return tls_symbolic_operand (x, SImode);
+}
+
+int
+tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  if (GET_CODE (op) != SYMBOL_REF)
+    return 0;
+  return SYMBOL_REF_TLS_MODEL (op);
+}
+
+/* Valid input to a move instruction.  */
+int
+move_input_operand (rtx op, enum machine_mode mode)
+{
+  if (tls_symbolic_operand (op, mode))
+    return 0;
+  return general_operand (op, mode);
+}
+
 
 
 #define REG_OR_SUBREG_REG(X)						\
@@ -5413,6 +5663,47 @@ label_mentioned_p (rtx x)
   return 0;
 }
 
+int
+tls_mentioned_p (rtx x)
+{
+  switch (GET_CODE (x))
+    {
+    case CONST:
+      return tls_mentioned_p (XEXP (x, 0));
+
+    case UNSPEC:
+      if (XINT (x, 1) == UNSPEC_TLS)
+	return 1;
+
+    default:
+      return 0;
+    }
+}
+
+/* Must not copy a SET whose source operand is PC-relative.  */
+
+bool
+arm_cannot_copy_insn_p (rtx insn)
+{
+  if (INSN_P (insn))
+    {
+      rtx pat = PATTERN (insn);
+
+      if (GET_CODE (pat) == PARALLEL
+	  && GET_CODE (XVECEXP (pat, 0, 0)) == SET)
+	{
+	  rtx rhs = SET_SRC (XVECEXP (pat, 0, 0));
+	  
+	  if (GET_CODE (rhs) == MEM
+	      && GET_CODE (XEXP (rhs, 0)) == UNSPEC
+	      && XINT (XEXP (rhs, 0), 1) == UNSPEC_PIC_BASE)
+	    return TRUE;
+	}
+    }
+  
+  return FALSE;
+}
+
 enum rtx_code
 minmax_code (rtx x)
 {
@@ -11787,6 +12078,9 @@ arm_hard_regno_mode_ok (unsigned int reg
 int
 arm_regno_class (int regno)
 {
+  if (regno == 0)
+    return RETURN_REG;
+
   if (TARGET_THUMB)
     {
       if (regno == STACK_POINTER_REGNUM)
@@ -12369,8 +12663,23 @@ arm_init_iwmmxt_builtins (void)
 }
 
 static void
+arm_init_tls_builtins (void)
+{
+  tree ftype;
+  tree nothrow = tree_cons (get_identifier ("nothrow"), NULL, NULL);
+  tree const_nothrow = tree_cons (get_identifier ("const"), NULL, nothrow);
+  
+  ftype = build_function_type (ptr_type_node, void_list_node);
+  builtin_function ("__builtin_thread_pointer", ftype,
+		    ARM_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
+		    NULL, const_nothrow);
+}
+
+static void
 arm_init_builtins (void)
 {
+  arm_init_tls_builtins ();
+
   if (TARGET_REALLY_IWMMXT)
     arm_init_iwmmxt_builtins ();
 }
@@ -12678,6 +12987,9 @@ arm_expand_builtin (tree exp,
       emit_insn (gen_iwmmxt_clrdi (target));
       return target;
 
+    case ARM_BUILTIN_THREAD_POINTER:
+      return arm_load_tp (target);
+
     default:
       break;
     }
@@ -14456,6 +14768,8 @@ arm_encode_section_info (tree decl, rtx 
       else if (! TREE_PUBLIC (decl))
         arm_encode_call_attribute (decl, SHORT_CALL_FLAG_CHAR);
     }
+
+  default_encode_section_info (decl, rtl, first);
 }
 #endif /* !ARM_PE */
 
@@ -15164,3 +15478,59 @@ arm_cxx_unwind_resume_name (void)
 #endif
   return default_unwind_resume_name ();
 }
+
+static bool
+arm_emit_tls_decoration (FILE *fp, rtx x)
+{
+  int model;
+  rtx val;
+
+  val = XVECEXP (x, 0, 0);
+  model = INTVAL (XVECEXP (x, 0, 1));
+
+  output_addr_const (fp, val);
+
+  switch (model)
+    {
+    case TLS_MODEL_GLOBAL_DYNAMIC:
+    case TLS_MODEL_LOCAL_DYNAMIC:		/* XXX */
+      fputs ("(tlsgd)", fp);
+      break;
+    case TLS_MODEL_INITIAL_EXEC:
+      fputs ("(gottpoff)", fp);
+      break;
+    case TLS_MODEL_LOCAL_EXEC:
+      fputs ("(tpoff)", fp);
+      break;
+    default:
+      abort ();
+    }
+
+  switch (model)
+    {
+    case TLS_MODEL_GLOBAL_DYNAMIC:
+    case TLS_MODEL_LOCAL_DYNAMIC:		/* XXX */
+    case TLS_MODEL_INITIAL_EXEC:
+      fputs (" + (. - ", fp);
+      output_addr_const (fp, XVECEXP (x, 0, 2));
+      fputs (" - ", fp);
+      output_addr_const (fp, XVECEXP (x, 0, 3));
+      fputc (')', fp);
+      break;
+    }
+
+  return TRUE;
+}
+
+bool
+arm_output_addr_const_extra (FILE *fp, rtx x)
+{
+  if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLS)
+    return arm_emit_tls_decoration (fp, x);
+  else if (GET_CODE (x) == CONST_VECTOR)
+    return arm_emit_vector_const (fp, x);
+  
+  return FALSE;
+}
+
+#include "gt-arm.h"
Index: gcc/gcc/config/arm/arm.h
===================================================================
--- gcc.orig/gcc/config/arm/arm.h	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/config/arm/arm.h	2005-03-25 13:46:21.831718309 -0500
@@ -1,6 +1,6 @@
 /* Definitions of target machine for GNU compiler, for ARM.
    Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
    and Martin Simmons (@harleqn.co.uk).
    More major hacks by Richard Earnshaw (rearnsha@arm.com)
@@ -127,6 +127,8 @@ extern const char *target_fpe_name;
 extern const char *target_float_abi_name;
 /* For -m{soft,hard}-float.  */
 extern const char *target_float_switch;
+/* For -mtp={soft,cp15,linux}.  */
+extern const char *target_thread_switch;
 /* Which ABI to use.  */
 extern const char *target_abi_name;
 /* Define the information needed to generate branch insns.  This is
@@ -297,6 +299,10 @@ extern GTY(()) rtx aof_pic_label;
 #define TARGET_AAPCS_BASED \
     (arm_abi != ARM_ABI_APCS && arm_abi != ARM_ABI_ATPCS)
 
+#define TARGET_HARD_TP			(target_thread_pointer == TP_CP15)
+#define TARGET_SOFT_TP			(target_thread_pointer == TP_SOFT)
+#define TARGET_LINUX_TP			(target_thread_pointer == TP_LINUX)
+
 /* True iff the full BPABI is being used.  If TARGET_BPABI is true,
    then TARGET_AAPCS_BASED must be true -- but the converse does not
    hold.  TARGET_BPABI implies the use of the BPABI runtime library,
@@ -396,7 +402,9 @@ extern GTY(()) rtx aof_pic_label;
   {"soft-float", &target_float_switch,					\
    N_("Alias for -mfloat-abi=soft"), "s"},				\
   {"hard-float", &target_float_switch,					\
-   N_("Alias for -mfloat-abi=hard"), "h"}				\
+   N_("Alias for -mfloat-abi=hard"), "h"},				\
+  {"tp=", &target_thread_switch,					\
+   N_("Specify how to access the thread pointer"), 0}			\
 }
 
 /* Support for a compile-time default CPU, et cetera.  The rules are:
@@ -500,6 +508,15 @@ extern enum arm_abi_type arm_abi;
 #define ARM_DEFAULT_ABI ARM_ABI_APCS
 #endif
 
+/* Which thread pointer access sequence to use.  */
+enum arm_tp_type {
+  TP_SOFT,
+  TP_CP15,
+  TP_LINUX
+};
+
+extern enum arm_tp_type target_thread_pointer;
+
 /* Nonzero if this chip supports the ARM Architecture 3M extensions.  */
 extern int arm_arch3m;
 
@@ -1171,6 +1188,7 @@ enum reg_class
   VFP_REGS,
   IWMMXT_GR_REGS,
   IWMMXT_REGS,
+  RETURN_REG,
   LO_REGS,
   STACK_REG,
   BASE_REGS,
@@ -1193,6 +1211,7 @@ enum reg_class
   "VFP_REGS",		\
   "IWMMXT_GR_REGS",	\
   "IWMMXT_REGS",	\
+  "RETURN_REG",		\
   "LO_REGS",		\
   "STACK_REG",		\
   "BASE_REGS",		\
@@ -1214,6 +1233,7 @@ enum reg_class
   { 0x00000000, 0x80000000, 0x7FFFFFFF }, /* VFP_REGS  */	\
   { 0x00000000, 0x00007800, 0x00000000 }, /* IWMMXT_GR_REGS */	\
   { 0x00000000, 0x7FFF8000, 0x00000000 }, /* IWMMXT_REGS */	\
+  { 0x00000001, 0x00000000, 0x00000000 }, /* RETURN_REG */	\
   { 0x000000FF, 0x00000000, 0x00000000 }, /* LO_REGS */		\
   { 0x00002000, 0x00000000, 0x00000000 }, /* STACK_REG */	\
   { 0x000020FF, 0x00000000, 0x00000000 }, /* BASE_REGS */	\
@@ -1275,6 +1295,7 @@ enum reg_class
    : (C) == 'y' ? IWMMXT_REGS		\
    : (C) == 'z' ? IWMMXT_GR_REGS	\
    : (C) == 'l' ? (TARGET_ARM ? GENERAL_REGS : LO_REGS)	\
+   : (C) == 'Z' ? RETURN_REG		\
    : TARGET_ARM ? NO_REGS		\
    : (C) == 'h' ? HI_REGS		\
    : (C) == 'b' ? BASE_REGS		\
@@ -2103,8 +2124,10 @@ typedef struct
   || CONSTANT_ADDRESS_P (X)		\
   || flag_pic)
 
-#define LEGITIMATE_CONSTANT_P(X)	\
-  (TARGET_ARM ? ARM_LEGITIMATE_CONSTANT_P (X) : THUMB_LEGITIMATE_CONSTANT_P (X))
+#define LEGITIMATE_CONSTANT_P(X)			\
+  (!arm_tls_operand_p (X)				\
+   && (TARGET_ARM ? ARM_LEGITIMATE_CONSTANT_P (X)	\
+		  : THUMB_LEGITIMATE_CONSTANT_P (X)))
 
 /* Special characters prefixed to function names
    in order to encode attribute like information.
@@ -2294,10 +2317,12 @@ do {							\
     goto WIN;						\
 } while (0)
 
-#define THUMB_LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN)		\
-do {								\
-  if (flag_pic)							\
-    (X) = legitimize_pic_address (OLDX, MODE, NULL_RTX);	\
+#define THUMB_LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN)	\
+do {							\
+  X = thumb_legitimize_address (X, OLDX, MODE);		\
+							\
+  if (memory_address_p (MODE, X))			\
+    goto WIN;						\
 } while (0)
 
 #define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN)		\
@@ -2407,7 +2432,7 @@ do {							\
 /* We decide which register to use based on the compilation options and
    the assembler in use; this is more general than the APCS restriction of
    using sb (r9) all the time.  */
-extern int arm_pic_register;
+extern unsigned int arm_pic_register;
 
 /* Used when parsing command line option -mpic-register=.  */
 extern const char * arm_pic_register_string;
@@ -2419,12 +2444,13 @@ extern const char * arm_pic_register_str
 /* We can't directly access anything that contains a symbol,
    nor can we indirect via the constant pool.  */
 #define LEGITIMATE_PIC_OPERAND_P(X)					\
-	(!(symbol_mentioned_p (X)					\
+        (tls_mentioned_p (X)						\
+	 || (!(symbol_mentioned_p (X)					\
 	   || label_mentioned_p (X)					\
 	   || (GET_CODE (X) == SYMBOL_REF				\
 	       && CONSTANT_POOL_ADDRESS_P (X)				\
 	       && (symbol_mentioned_p (get_pool_constant (X))		\
-		   || label_mentioned_p (get_pool_constant (X))))))
+		   || label_mentioned_p (get_pool_constant (X)))))))
 
 /* We need to know when we are making a constant pool; this determines
    whether data needs to be in the GOT or can be referenced via a GOT
@@ -2701,10 +2727,9 @@ extern int making_const_table;
   else						\
     THUMB_PRINT_OPERAND_ADDRESS (STREAM, X)
 
-#define OUTPUT_ADDR_CONST_EXTRA(FILE, X, FAIL)	\
-  if (GET_CODE (X) != CONST_VECTOR		\
-      || ! arm_emit_vector_const (FILE, X))	\
-    goto FAIL;
+#define OUTPUT_ADDR_CONST_EXTRA(file, x, fail)		\
+  if (arm_output_addr_const_extra (file, x) == FALSE)	\
+    goto fail
 
 /* A C expression whose value is RTL representing the value of the return
    address for the frame COUNT steps up from the current frame.  */
@@ -2774,6 +2799,8 @@ extern int making_const_table;
   {"cirrus_register_operand", {REG}},					\
   {"cirrus_fp_register", {REG}},					\
   {"cirrus_shift_const", {CONST_INT}},					\
+  {"move_input_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,	\
+			  LABEL_REF, SUBREG, REG, MEM, ADDRESSOF}},	\
   {"dominant_cc_register", {REG}},					\
   {"arm_float_compare_operand", {REG, CONST_DOUBLE}},			\
   {"vfp_compare_operand", {REG, CONST_DOUBLE}},
@@ -2947,6 +2974,8 @@ enum arm_builtins
   ARM_BUILTIN_WUNPCKELUH,
   ARM_BUILTIN_WUNPCKELUW,
 
+  ARM_BUILTIN_THREAD_POINTER,
+
   ARM_BUILTIN_MAX
 };
 #endif /* ! GCC_ARM_H */
Index: gcc/gcc/config/arm/arm.md
===================================================================
--- gcc.orig/gcc/config/arm/arm.md	2005-03-25 11:40:08.000000000 -0500
+++ gcc/gcc/config/arm/arm.md	2005-03-25 13:28:39.481050403 -0500
@@ -1,6 +1,6 @@
 ;;- Machine description for ARM for GNU compiler
 ;;  Copyright 1991, 1993, 1994, 1995, 1996, 1996, 1997, 1998, 1999, 2000,
-;;  2001, 2002, 2003, 2004  Free Software Foundation, Inc.
+;;  2001, 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
 ;;  Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
 ;;  and Martin Simmons (@harleqn.co.uk).
 ;;  More major hacks by Richard Earnshaw (rearnsha@arm.com).
@@ -87,6 +87,7 @@
    (UNSPEC_CLRDI    17) ; Used by the intrinsic form of the iWMMXt CLRDI instruction.
    (UNSPEC_WMADDS   18) ; Used by the intrinsic form of the iWMMXt WMADDS instruction.
    (UNSPEC_WMADDU   19) ; Used by the intrinsic form of the iWMMXt WMADDU instruction.
+   (UNSPEC_TLS      20) ; A symbol that has been treated properly for TLS usage.
   ]
 )
 
@@ -4166,19 +4167,28 @@
 	    operands[1] = force_reg (SImode, operands[1]);
         }
     }
-    
-  if (flag_pic
-      && (CONSTANT_P (operands[1])
-	 || symbol_mentioned_p (operands[1])
-	 || label_mentioned_p (operands[1])))
-    operands[1] = legitimize_pic_address (operands[1], SImode,
-					  (no_new_pseudos ? operands[0] : 0));
+
+  {
+    unsigned long model;
+
+    model = tls_symbolic_operand (operands[1], SImode);
+    if (model)
+      operands[1] = legitimize_tls_address (operands[1], model,
+					    (no_new_pseudos ? operands[0] : 0));
+    else if (flag_pic
+        && (CONSTANT_P (operands[1])
+	   || symbol_mentioned_p (operands[1])
+	   || label_mentioned_p (operands[1]))
+	&& ! tls_mentioned_p (operands[1]))
+      operands[1] = legitimize_pic_address (operands[1], SImode,
+					    (no_new_pseudos ? operands[0] : 0));
+  } 
   "
 )
 
 (define_insn "*arm_movsi_insn"
   [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r, m")
-	(match_operand:SI 1 "general_operand"      "rI,K,mi,r"))]
+	(match_operand:SI 1 "move_input_operand"   "rI,K,mi,r"))]
   "TARGET_ARM && ! TARGET_IWMMXT
    && !(TARGET_HARD_FLOAT && TARGET_VFP)
    && (   register_operand (operands[0], SImode)
@@ -4210,7 +4220,7 @@
 
 (define_insn "*thumb_movsi_insn"
   [(set (match_operand:SI 0 "nonimmediate_operand" "=l,l,l,l,l,>,l, m,*lh")
-	(match_operand:SI 1 "general_operand"      "l, I,J,K,>,l,mi,l,*lh"))]
+	(match_operand:SI 1 "move_input_operand"   "l, I,J,K,>,l,mi,l,*lh"))]
   "TARGET_THUMB
    && (   register_operand (operands[0], SImode) 
        || register_operand (operands[1], SImode))"
@@ -4330,7 +4340,7 @@
 			     (const (plus:SI (pc) (const_int 4))))]
 		   UNSPEC_PIC_BASE))
    (use (label_ref (match_operand 1 "" "")))]
-  "TARGET_THUMB && flag_pic"
+  "TARGET_THUMB"
   "*
   (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
 			     CODE_LABEL_NUMBER (operands[1]));
@@ -4345,7 +4355,7 @@
 			     (const (plus:SI (pc) (const_int 8))))]
 		   UNSPEC_PIC_BASE))
    (use (label_ref (match_operand 1 "" "")))]
-  "TARGET_ARM && flag_pic"
+  "TARGET_ARM"
   "*
     (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
 			       CODE_LABEL_NUMBER (operands[1]));
@@ -4354,6 +4364,74 @@
   [(set_attr "predicable" "yes")]
 )
 
+(define_insn "tls_load_dot_plus_four"
+  [(set (match_operand:SI 0 "register_operand" "+r")
+	(mem:SI (unspec:SI [(plus:SI (match_operand 1 "register_operand" "r")
+			     (const (plus:SI (pc) (const_int 4))))]
+		   UNSPEC_PIC_BASE)))	
+   (use (label_ref (match_operand 2 "" "")))
+   (use (label_ref (match_operand 3 "" "")))]
+  "TARGET_THUMB"
+  "*
+    (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+			       CODE_LABEL_NUMBER (operands[2]));
+    return \"ldr\\t%0, [%|pc, %1]\t\t@ tls_load_dot_plus_four\";
+  "
+  [(set_attr "length" "2")]
+)
+
+(define_insn "tls_load_dot_plus_eight"
+  [(set (match_operand:SI 0 "register_operand" "+r")
+	(mem:SI (unspec:SI [(plus:SI (match_operand 1 "register_operand" "r")
+			     (const (plus:SI (pc) (const_int 8))))]
+		   UNSPEC_PIC_BASE)))	
+   (use (label_ref (match_operand 2 "" "")))
+   (use (label_ref (match_operand 3 "" "")))]
+  "TARGET_ARM"
+  "*
+    (*targetm.asm_out.internal_label) (asm_out_file, \"L\",
+			       CODE_LABEL_NUMBER (operands[2]));
+    return \"ldr%?\\t%0, [%|pc, %1]\t\t@ tls_load_dot_plus_eight\";
+  "
+  [(set_attr "predicable" "yes")]
+)
+
+;; PIC references to local variables can generate pic_add_dot_plus_x
+;; followed by a load.  These sequences can be crunched down to 
+;; tls_load_dot_plus_x by a peephole.
+
+(define_peephole2
+  [(parallel [(set (match_operand:SI 0 "register_operand" "+r")
+		   (unspec:SI [(plus:SI (match_dup 0)
+			     	 	(const (plus:SI (pc) (const_int 8))))]
+		      UNSPEC_PIC_BASE))
+   	      (use (label_ref (match_operand 1 "" "")))])
+   (set (match_operand:SI 2 "register_operand" "+r") (mem:SI (match_dup 0)))]
+  "TARGET_ARM"
+  [(parallel [(set (match_operand:SI 2 "register_operand" "+r")
+			(mem:SI (unspec:SI [(plus:SI (match_dup 0)
+			     			     (const (plus:SI (pc) (const_int 8))))]
+		   		 UNSPEC_PIC_BASE)))	
+   	      (use (label_ref (match_operand 1 "" "")))])]
+  ""
+)
+
+(define_peephole2
+  [(parallel [(set (match_operand:SI 0 "register_operand" "+r")
+		   (unspec:SI [(plus:SI (match_dup 0)
+			     	 	(const (plus:SI (pc) (const_int 4))))]
+		      UNSPEC_PIC_BASE))
+   	      (use (label_ref (match_operand 1 "" "")))])
+   (set (match_operand:SI 2 "register_operand" "+r") (mem:SI (match_dup 0)))]
+  "TARGET_THUMB"
+  [(parallel [(set (match_operand:SI 2 "register_operand" "+r")
+			(mem:SI (unspec:SI [(plus:SI (match_dup 0)
+			     			     (const (plus:SI (pc) (const_int 4))))]
+		   		 UNSPEC_PIC_BASE)))	
+   	      (use (label_ref (match_operand 1 "" "")))])]
+  ""
+)
+
 (define_expand "builtin_setjmp_receiver"
   [(label_ref (match_operand 0 "" ""))]
   "flag_pic"
@@ -10179,6 +10257,28 @@
   }"
 )
 
+
+;; TLS support
+
+(define_insn "load_tp_hard"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec:SI [(const_int 0)] UNSPEC_TLS))]
+  "TARGET_HARD_TP"
+  "mrc%?\\tp15, 0, %0, c13, c0, 2\\t@ load_tp_hard"
+  [(set_attr "predicable" "yes")]
+)
+
+;; Doesn't clobber R1-R3
+(define_insn "load_tp_soft"
+  [(set (match_operand:SI 0 "register_operand" "=Z")
+	     		(unspec:SI [(const_int 0)] UNSPEC_TLS))
+   (clobber (reg:SI LR_REGNUM))
+   (clobber (reg:SI IP_REGNUM))
+   (clobber (reg:CC CC_REGNUM))]
+  "TARGET_SOFT_TP"
+  "bl\\t__aeabi_read_tp\\t@ load_tp_soft"
+)
+
 ;; Load the FPA co-processor patterns
 (include "fpa.md")
 ;; Load the Maverick co-processor patterns


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