diff mbox

[ARM] TLS Descriptor support

Message ID 4DC8DFF6.4000600@codesourcery.com
State New
Headers show

Commit Message

Nathan Sidwell May 10, 2011, 6:49 a.m. UTC
This patch implements TLS descriptor support in GCC.  TLS descriptors are 
described at http://www.codesourcery.com/publications/RFC-TLSDESC-ARM.txt and 
blessed by ARM, who have reserved the relocation numbers.

Binutils and GLIBC patches are already committed (there is an orthogonal glibc 
patch to do with make dependencies that I need to post though).

This patch adds a --with-tls={arm|gnu} configuration option, to specify the 
default scheme.  It can be overridden with a -mtls-dialect={arm|gnu} option 
(this is the name used by the x86 backend, which also has tlsdescriptor 
support).  I have not added --with-tls support to the x86 bits of config.gcc 
etc, but it would be simple to do so.

The arm.{c,h,md} changes are fairly mechanical -- a new tls pattern and smarts 
to emit it appropriately.

This patch has been tested for both default arm and default gnu tls schemes 
using the gcc and glibc testsuites for an arm-linux-gnueabi target.

ok?

nathan

Comments

Joseph Myers May 10, 2011, 11:36 a.m. UTC | #1
On Tue, 10 May 2011, Nathan Sidwell wrote:

> +@item -mtls-dialect=@var{dialect}
> +@opindex mtls-dialect
> +Specify the dialect to use for accessing thread local storage.  Two
> +dialects are supported - @option{arm} and @option{gnu}.  The

Use "---" (em dash, unspaced) instead of " - ".

> +@option{arm} dialect selects the ARM EABI scheme for supporting local
> +and global dynamic tls models.  The @option{gnu} dialect selects the

"TLS" not "tls".

> +Specify the default tls dialect, for systems were there is a choice.

Likewise.

> +	with_tls=${with_tls:arm}

Do you mean ${with_tls:-arm}?

> @@ -1755,6 +1759,17 @@ arm_option_override (void)
>  	error ("invalid thread pointer option: -mtp=%s", target_thread_switch);
>      }
>  
> +  if (target_tls_dialect_switch)
> +    {
> +      if (strcmp (target_tls_dialect_switch, "arm") == 0)
> +	target_tls_dialect = TLS_ARM;
> +      else if (strcmp (target_tls_dialect_switch, "gnu") == 0)
> +	target_tls_dialect = TLS_GNU;
> +      else
> +	error ("invalid thread dialect option: -mtls-dialect=%s",
> +	       target_tls_dialect_switch);
> +    }

Rather than checking for strings manually like this, it's now better to 
use the .opt Enum facility for options such as this with a fixed 
enumerated set of values, so target_tls_dialect can be set directly from 
the .opt file using Var and Enum.  (If you want a custom error message, 
you can use UnknownError on the enumeration record to specify it.)
Nathan Sidwell May 11, 2011, 2:15 p.m. UTC | #2
On 05/10/11 12:36, Joseph S. Myers wrote:

thanks for the review.  I've addressed these issues, but will wait for 
arm-specific review before reposting (I presume you want the chance to check the 
non-mechanical opt-machinery changes)

nathan
Nathan Sidwell May 26, 2011, 8:34 a.m. UTC | #3
On 05/10/11 07:49, Nathan Sidwell wrote:
> This patch implements TLS descriptor support in GCC. TLS descriptors are
> described at http://www.codesourcery.com/publications/RFC-TLSDESC-ARM.txt and
> blessed by ARM, who have reserved the relocation numbers.

http://gcc.gnu.org/ml/gcc-patches/2011-05/msg00708.html
ping?

nathan
Ramana Radhakrishnan May 27, 2011, 12:47 a.m. UTC | #4
Thanks for this and sorry about the length of time in reviewing this .

On 10/05/11 07:49, Nathan Sidwell wrote:
> This patch implements TLS descriptor support in GCC. TLS descriptors are
> described at
> http://www.codesourcery.com/publications/RFC-TLSDESC-ARM.txt and blessed
> by ARM, who have reserved the relocation numbers.
>
> Binutils and GLIBC patches are already committed (there is an orthogonal
> glibc patch to do with make dependencies that I need to post though).
>
> This patch adds a --with-tls={arm|gnu} configuration option, to specify
> the default scheme. It can be overridden with a -mtls-dialect={arm|gnu}
> option (this is the name used by the x86 backend, which also has
> tlsdescriptor support). I have not added --with-tls support to the x86
> bits of config.gcc etc, but it would be simple to do so.

Could you consider adding a check in the configury to test if a binutils 
version of recent vintage is being used when --with-tls=gnu is in ?

Could you also use R0_REGNUM, R1_REGNUM instead of 0 and 1 in the 
"tlscall" pattern ?


>
> This patch has been tested for both default arm and default gnu tls
> schemes using the gcc and glibc testsuites for an arm-linux-gnueabi target.

Presumably for v7-a and v5te and with this as default ? On hardware ?


cheers
Ramana
Nathan Sidwell May 31, 2011, 10:03 a.m. UTC | #5
On 05/27/11 01:47, Ramana Radhakrishnan wrote:

> Could you consider adding a check in the configury to test if a binutils version
> of recent vintage is being used when --with-tls=gnu is in ?

I thought about that and it didn't seem worth it.  We're not autodetecting 
whether to default to gnu-style tls and you'll find out soon enough if your 
binutils is too old.

>
> Could you also use R0_REGNUM, R1_REGNUM instead of 0 and 1 in the "tlscall"
> pattern ?

>> This patch has been tested for both default arm and default gnu tls
>> schemes using the gcc and glibc testsuites for an arm-linux-gnueabi target.
>
> Presumably for v7-a and v5te and with this as default ? On hardware ?

Hm, I see our testing for fsf build uses qemu -- this patch has been tested on 
hardware in our releases.  Just not this exact version of the patch.

nathan
Ramana Radhakrishnan June 3, 2011, 10:50 p.m. UTC | #6
On 31 May 2011 11:03, Nathan Sidwell <nathan@codesourcery.com> wrote:
> On 05/27/11 01:47, Ramana Radhakrishnan wrote:
>
>> Could you consider adding a check in the configury to test if a binutils
>> version
>> of recent vintage is being used when --with-tls=gnu is in ?
>
> I thought about that and it didn't seem worth it.  We're not autodetecting
> whether to default to gnu-style tls and you'll find out soon enough if your
> binutils is too old.


I noticed this today - and maybe I don't quite understand it yet.

>   /* The + is to avoid an assembly parse ambiguity with symbols that
> +       look like register names, which is unsuccessfully recovered from.  */
> +    return TARGET_THUMB2 ? "blx\\t%c0(tlscall)" : "bl\\t+%c0(tlscall)";


How are things handled for Thumb1 in case someone builds a routine for
Thumb1 which uses tlsdesc ? I went and read the doc and didn't see any
difference between T1 and T2 in the specification . Would the linker
and everyone else do the right thing or should we have the blx
instruction for TARGET_THUMB and v5t . I have a feeling that I'm
missing something here .




cheers
Ramana

>
> nathan
>
> --
> Nathan Sidwell
>
>
Nathan Sidwell June 6, 2011, 9:49 a.m. UTC | #7
On 06/03/11 23:50, Ramana Radhakrishnan wrote:

> How are things handled for Thumb1 in case someone builds a routine for
> Thumb1 which uses tlsdesc ? I went and read the doc and didn't see any
> difference between T1 and T2 in the specification . Would the linker
> and everyone else do the right thing or should we have the blx
> instruction for TARGET_THUMB and v5t . I have a feeling that I'm
> missing something here .

You're asking how does the thumb1 bl instruction get handled?  The linker emits 
a veneer -- a variant of the t<->a veneers it already deals with.

If you're asking something else, can you rephrase the question?

nathan
Ramana Radhakrishnan June 6, 2011, 9:53 a.m. UTC | #8
> If you're asking something else, can you rephrase the question?

Sorry if I wasn't too clear.  My question really should have read "why
do we have to special case Thumb2" ? The linker should be able to
veneer the t-> a calls either through the veneering sequence (in case
of T1 without blx) or convert the bl to a blx (modulo the case with
out of range branches). As I said I'm probably missing something here.

cheers
Ramana

>
> nathan
>
> --
> Nathan Sidwell
>
>
Nathan Sidwell June 6, 2011, 9:56 a.m. UTC | #9
On 06/06/11 10:53, Ramana Radhakrishnan wrote:
>> If you're asking something else, can you rephrase the question?
>
> Sorry if I wasn't too clear.  My question really should have read "why
> do we have to special case Thumb2" ? The linker should be able to
> veneer the t->  a calls either through the veneering sequence (in case
> of T1 without blx) or convert the bl to a blx (modulo the case with
> out of range branches). As I said I'm probably missing something here.

Maybe we don't.  It just seems neater to emit blx on arches that have it.

nathan
Ramana Radhakrishnan June 6, 2011, 4:09 p.m. UTC | #10
On 6 June 2011 10:56, Nathan Sidwell <nathan@codesourcery.com> wrote:
> On 06/06/11 10:53, Ramana Radhakrishnan wrote:
>>>
>>> If you're asking something else, can you rephrase the question?
>>
>> Sorry if I wasn't too clear.  My question really should have read "why
>> do we have to special case Thumb2" ? The linker should be able to
>> veneer the t->  a calls either through the veneering sequence (in case
>> of T1 without blx) or convert the bl to a blx (modulo the case with
>> out of range branches). As I said I'm probably missing something here.
>
> Maybe we don't.  It just seems neater to emit blx on arches that have it.
>

In which case the test should probably read -

(arm_arch5 && TARGET_THUMB) rather than (TARGET_THUMB2) since blx
exists since v5t

cheers
Ramana
Richard Earnshaw June 6, 2011, 4:27 p.m. UTC | #11
On 05/10/11 07:49, Nathan Sidwell wrote:
> This patch implements TLS descriptor support in GCC.  TLS descriptors 
> are described at 
> http://www.codesourcery.com/publications/RFC-TLSDESC-ARM.txt and 
> blessed by ARM, who have reserved the relocation numbers.
>
> Binutils and GLIBC patches are already committed (there is an 
> orthogonal glibc patch to do with make dependencies that I need to 
> post though).
>
> This patch adds a --with-tls={arm|gnu} configuration option, to 
> specify the default scheme.  It can be overridden with a 
> -mtls-dialect={arm|gnu} option (this is the name used by the x86 
> backend, which also has tlsdescriptor support).  I have not added 
> --with-tls support to the x86 bits of config.gcc etc, but it would be 
> simple to do so.
>
> The arm.{c,h,md} changes are fairly mechanical -- a new tls pattern 
> and smarts to emit it appropriately.
>
> This patch has been tested for both default arm and default gnu tls 
> schemes using the gcc and glibc testsuites for an arm-linux-gnueabi 
> target.
>
> ok?
>
> nathan
>
+    /* The + is to avoid an assembly parse ambiguity with symbols that
+       look like register names, which is unsuccessfully recovered 
from.  */
+    return TARGET_THUMB2 ? "blx\\t%c0(tlscall)" : "bl\\t+%c0(tlscall)";


Eh?  This is backwards.  There is blx <reg>, but no bl <reg>.  If the 
assembler gets confused with 'bl r0' then it needs to be fixed urgently.

Further, both the assembler and the linker know how to change bl to blx 
when needed, so it's best to just put out bl and be done with it.

R.
Nathan Sidwell June 7, 2011, 6:11 a.m. UTC | #12
On 06/06/11 17:27, Richard Earnshaw wrote:

> Eh? This is backwards. There is blx <reg>, but no bl <reg>. If the assembler
> gets confused with 'bl r0' then it needs to be fixed urgently.

Are you requiring the assembler be fixed before this patch can be committed 
(without the '+'?)

nathan
Richard Earnshaw June 7, 2011, 1:07 p.m. UTC | #13
On 06/07/11 07:11, Nathan Sidwell wrote:
> On 06/06/11 17:27, Richard Earnshaw wrote:
> 
>> Eh? This is backwards. There is blx <reg>, but no bl <reg>. If the assembler
>> gets confused with 'bl r0' then it needs to be fixed urgently.
> 
> Are you requiring the assembler be fixed before this patch can be committed 
> (without the '+'?)
> 
> nathan
> 

Yes, the assembler output here should be "bl\\t%c0(tlsdesc)" for all
variants.

I'd also much prefer that the named variant be GNU and GNU2, as they are
for x86.  It's much less confusing to users that way (the fact that the
first (GNU) variant is documented in the ARM ABI specification is simply
down to a lack of external documentation to refer to, but the underlying
model is the same as that used elsewere for the first GNU version).

R.
diff mbox

Patch

2011-05-10  Nathan Sidwell  <nathan@codesourcery.com>

	* doc/invoke.texi (ARM Options): Document -mtls-dialect option.
	* doc/install.texi (Configuration): Document --with-tls.
	* config.gcc (arm*-*-linux*): Default to arm-style tls.
	(arm*-*-*): Add --with-tls option.
	(all_defaults): Add 'tls'.
	* config/arm/arm.c (target_tls_dialect): New.
	(enum tls_reloc): Add TLS_DESCSEQ.
	(arm_override_options): Process target_tls_dialect_switch value.
	(arm_call_tls_get_addr): Clean up. Assert not tls descriptor.
	(arm_tls_descseq_addr): New.
	(legitimize_tls_address): Add tlsdesc support.
	(arm_cannot_copy_insn_p): Check for tlscall.
	(arm_emit_tls_decoration): Likewise.
	* config/arm/arm.h (TARGET_ARM_TLS, TARGET_GNU_TLS): New.
	(OPTION_DEFAULT_SPECS): Add with-tls support.
	(enum arm_tls_type): New.
	(target_tls_dialect): New.
	* config/arm/arm.opt (mtls-dialect): New switch.
	* config/arm/arm.md (tlscall): New.

	testsuite/
	* gcc.target/arm/tlscall.c: New.

Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 172962)
+++ doc/invoke.texi	(working copy)
@@ -469,7 +469,7 @@  Objective-C and Objective-C++ Dialects}.
 -mthumb  -marm @gol
 -mtpcs-frame  -mtpcs-leaf-frame @gol
 -mcaller-super-interworking  -mcallee-super-interworking @gol
--mtp=@var{name} @gol
+-mtp=@var{name} -mtls-dialect=@var{dialect} @gol
 -mword-relocations @gol
 -mfix-cortex-m3-ldrd}
 
@@ -10332,6 +10332,17 @@  models are @option{soft}, which generate
 best available method for the selected processor.  The default setting is
 @option{auto}.
 
+@item -mtls-dialect=@var{dialect}
+@opindex mtls-dialect
+Specify the dialect to use for accessing thread local storage.  Two
+dialects are supported - @option{arm} and @option{gnu}.  The
+@option{arm} dialect selects the ARM EABI scheme for supporting local
+and global dynamic tls models.  The @option{gnu} dialect selects the
+experimental GNU scheme.  The GNU scheme is compatible with the ARM
+scheme, but does require new assembler, linker and library
+support.  Initial and local exec TLS models are unaffected by this
+option and use the ARM EABI scheme.
+
 @item -mword-relocations
 @opindex mword-relocations
 Only generate absolute relocations on word sized values (i.e. R_ARM_ABS32).
Index: doc/install.texi
===================================================================
--- doc/install.texi	(revision 172962)
+++ doc/install.texi	(working copy)
@@ -1003,6 +1003,12 @@  information normally used on 386 SVR4 pl
 workable alternative.  This requires gas and gdb, as the normal SVR4
 tools can not generate or interpret stabs.
 
+@item --with-tls=@var{dialect}
+Specify the default tls dialect, for systems were there is a choice.
+For ARM targets, possible values for @var{dialect} are @code{arm} or
+@code{gnu}, which select between the ARM EABI dialect and the GNU TLS
+descriptor-based dialect.
+
 @item --disable-multilib
 Specify that multiple target
 libraries to support different target variants, calling
Index: testsuite/gcc.target/arm/tlscall.c
===================================================================
--- testsuite/gcc.target/arm/tlscall.c	(revision 0)
+++ testsuite/gcc.target/arm/tlscall.c	(revision 0)
@@ -0,0 +1,31 @@ 
+/* Test non-duplication of tlscall insn */
+
+/* { dg-do assemble } */
+/* { dg-options "-O2 -fPIC -mtls-dialect=gnu" } */
+
+typedef struct _IO_FILE FILE;
+
+extern int foo(void);
+extern int bar(void);
+
+void uuid__generate_time()
+{
+ static int has_init = 0;
+ static __thread int state_fd = -2;
+ static __thread FILE *state_f;
+
+ if (!has_init) {
+   foo();
+   has_init = 1;
+ }
+
+ if (state_fd == -2) {
+  if (!state_f) {
+   state_fd = -1;
+  }
+ }
+ if (state_fd >= 0) {
+  while (bar() < 0) {}
+ }
+
+}
Index: config.gcc
===================================================================
--- config.gcc	(revision 172962)
+++ config.gcc	(working copy)
@@ -813,6 +813,7 @@  arm*-*-linux*)			# ARM GNU/Linux with EL
 	    tmake_file="$tmake_file arm/t-linux"
 	    ;;
 	esac
+	with_tls=${with_tls:arm}
 	tm_file="$tm_file arm/aout.h arm/arm.h"
 	tmake_file="${tmake_file} arm/t-arm-softfp soft-fp/t-softfp"
 	;;
@@ -3064,7 +3065,7 @@  case "${target}" in
 		;;
 
 	arm*-*-*)
-		supported_defaults="arch cpu float tune fpu abi mode"
+		supported_defaults="arch cpu float tune fpu abi mode tls"
 		for which in cpu tune; do
 			# See if it matches any of the entries in arm-cores.def
 			eval "val=\$with_$which"
@@ -3147,6 +3148,17 @@  case "${target}" in
 			;;
 		esac
 
+		case "$with_tls" in
+		"" \
+		| arm | gnu)
+			# OK
+			;;
+		*)
+			echo "Unknown TLS method used in --with-tls=$with_tls" 1>&2
+			exit 1
+			;;
+		esac
+
 		if test "x$with_arch" != x && test "x$with_cpu" != x; then
 			echo "Warning: --with-arch overrides --with-cpu=$with_cpu" 1>&2
 		fi
@@ -3624,7 +3636,7 @@  case ${target} in
 esac
 
 t=
-all_defaults="abi cpu cpu_32 cpu_64 arch arch_32 arch_64 tune tune_32 tune_64 schedule float mode fpu divide llsc mips-plt synci"
+all_defaults="abi cpu cpu_32 cpu_64 arch arch_32 arch_64 tune tune_32 tune_64 schedule float mode fpu divide llsc mips-plt synci tls"
 for option in $all_defaults
 do
 	eval "val=\$with_"`echo $option | sed s/-/_/g`
Index: config/arm/arm.c
===================================================================
--- config/arm/arm.c	(revision 172962)
+++ config/arm/arm.c	(working copy)
@@ -644,6 +644,9 @@  enum arm_abi_type arm_abi;
 /* Which thread pointer model to use.  */
 enum arm_tp_type target_thread_pointer = TP_AUTO;
 
+/* Which tls dialect to use.  */
+enum arm_tls_type target_tls_dialect = TLS_ARM;
+
 /* Used to parse -mstructure_size_boundary command line option.  */
 int    arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY;
 
@@ -1031,7 +1034,8 @@  enum tls_reloc {
   TLS_LDM32,
   TLS_LDO32,
   TLS_IE32,
-  TLS_LE32
+  TLS_LE32,
+  TLS_DESCSEQ	/* GNU scheme */
 };
 
 /* The maximum number of insns to be used when loading a constant.  */
@@ -1755,6 +1759,17 @@  arm_option_override (void)
 	error ("invalid thread pointer option: -mtp=%s", target_thread_switch);
     }
 
+  if (target_tls_dialect_switch)
+    {
+      if (strcmp (target_tls_dialect_switch, "arm") == 0)
+	target_tls_dialect = TLS_ARM;
+      else if (strcmp (target_tls_dialect_switch, "gnu") == 0)
+	target_tls_dialect = TLS_GNU;
+      else
+	error ("invalid thread dialect option: -mtls-dialect=%s",
+	       target_tls_dialect_switch);
+    }
+
   /* Use the cp15 method if it is available.  */
   if (target_thread_pointer == TP_AUTO)
     {
@@ -5957,6 +5972,7 @@  arm_call_tls_get_addr (rtx x, rtx reg, r
 {
   rtx insns, label, labelno, sum;
 
+  gcc_assert (reloc != TLS_DESCSEQ);
   start_sequence ();
 
   labelno = GEN_INT (pic_labelno++);
@@ -5971,20 +5987,42 @@  arm_call_tls_get_addr (rtx x, rtx reg, r
 
   if (TARGET_ARM)
     emit_insn (gen_pic_add_dot_plus_eight (reg, reg, labelno));
-  else if (TARGET_THUMB2)
-    emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
-  else /* TARGET_THUMB1 */
+  else
     emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
-
-  *valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX, LCT_PURE, /* LCT_CONST?  */
+  
+  *valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX,
+				     LCT_PURE, /* LCT_CONST?  */
 				     Pmode, 1, reg, Pmode);
-
+  
   insns = get_insns ();
   end_sequence ();
 
   return insns;
 }
 
+static rtx
+arm_tls_descseq_addr (rtx x, rtx reg)
+{
+  rtx labelno = GEN_INT (pic_labelno++);
+  rtx label = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_PIC_LABEL);
+  rtx sum = gen_rtx_UNSPEC (Pmode,
+			    gen_rtvec (4, x, GEN_INT (TLS_DESCSEQ),
+				       gen_rtx_CONST (VOIDmode, label),
+				       GEN_INT (!TARGET_ARM)),
+			    UNSPEC_TLS);
+  rtx reg0 = load_tls_operand (sum, gen_rtx_REG (SImode, 0));
+  
+  emit_insn (gen_tlscall (x, labelno));
+  if (!reg)
+    reg = gen_reg_rtx (SImode);
+  else
+    gcc_assert (REGNO (reg) != 0);
+
+  emit_move_insn (reg, reg0);
+
+  return reg;
+}
+
 rtx
 legitimize_tls_address (rtx x, rtx reg)
 {
@@ -5994,26 +6032,49 @@  legitimize_tls_address (rtx x, rtx reg)
   switch (model)
     {
     case TLS_MODEL_GLOBAL_DYNAMIC:
-      insns = arm_call_tls_get_addr (x, reg, &ret, TLS_GD32);
-      dest = gen_reg_rtx (Pmode);
-      emit_libcall_block (insns, dest, ret, x);
-      return dest;
+      if (TARGET_ARM_TLS)
+	{
+	  insns = arm_call_tls_get_addr (x, reg, &ret, TLS_GD32);
+	  dest = gen_reg_rtx (Pmode);
+	  emit_libcall_block (insns, dest, ret, x);
+	  return dest;
+	}
+      else
+	{
+	  reg = arm_tls_descseq_addr (x, reg);
 
-    case TLS_MODEL_LOCAL_DYNAMIC:
-      insns = arm_call_tls_get_addr (x, reg, &ret, TLS_LDM32);
+	  tp = arm_load_tp (NULL_RTX);
+	  
+	  return gen_rtx_PLUS (Pmode, tp, reg);
+	}
 
-      /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
-	 share the LDM result with other LD model accesses.  */
-      eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx),
-			    UNSPEC_TLS);
-      dest = gen_reg_rtx (Pmode);
-      emit_libcall_block (insns, dest, ret, eqv);
+    case TLS_MODEL_LOCAL_DYNAMIC:
+      if (TARGET_ARM_TLS)
+	{
+	  insns = arm_call_tls_get_addr (x, reg, &ret, TLS_LDM32);
+	  
+	  /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
+	     share the LDM result with other LD model accesses.  */
+	  eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx),
+				UNSPEC_TLS);
+	  dest = gen_reg_rtx (Pmode);
+	  emit_libcall_block (insns, dest, ret, eqv);
+	  
+	  /* Load the addend.  */
+	  addend = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x,
+						     GEN_INT (TLS_LDO32)),
+				   UNSPEC_TLS);
+	  addend = force_reg (SImode, gen_rtx_CONST (SImode, addend));
+	  return gen_rtx_PLUS (Pmode, dest, addend);
+	}
+      else
+	{
+	  reg = arm_tls_descseq_addr (x, reg);
 
-      /* Load the addend.  */
-      addend = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (TLS_LDO32)),
-			       UNSPEC_TLS);
-      addend = force_reg (SImode, gen_rtx_CONST (SImode, addend));
-      return gen_rtx_PLUS (Pmode, dest, addend);
+	  tp = arm_load_tp (NULL_RTX);
+	  
+	  return gen_rtx_PLUS (Pmode, tp, reg);
+	}
 
     case TLS_MODEL_INITIAL_EXEC:
       labelno = GEN_INT (pic_labelno++);
@@ -9445,6 +9506,11 @@  arm_note_pic_base (rtx *x, void *date AT
 static bool
 arm_cannot_copy_insn_p (rtx insn)
 {
+  /* The tls call insn cannot be copied, as it is paired with a data
+     word.  */
+  if (recog_memoized (insn) == CODE_FOR_tlscall)
+    return true;
+  
   return for_each_rtx (&PATTERN (insn), arm_note_pic_base, NULL);
 }
 
@@ -22985,6 +23051,9 @@  arm_emit_tls_decoration (FILE *fp, rtx x
     case TLS_LE32:
       fputs ("(tpoff)", fp);
       break;
+    case TLS_DESCSEQ:
+      fputs ("(tlsdesc)", fp);
+      break;
     default:
       gcc_unreachable ();
     }
@@ -22994,9 +23063,11 @@  arm_emit_tls_decoration (FILE *fp, rtx x
     case TLS_GD32:
     case TLS_LDM32:
     case TLS_IE32:
+    case TLS_DESCSEQ:
       fputs (" + (. - ", fp);
       output_addr_const (fp, XVECEXP (x, 0, 2));
-      fputs (" - ", fp);
+      /* For DESCSEQ the 3rd operand encodes thumbness, and is added */
+      fputs (reloc == TLS_DESCSEQ ? " + " : " - ", fp);
       output_addr_const (fp, XVECEXP (x, 0, 3));
       fputc (')', fp);
       break;
Index: config/arm/arm.h
===================================================================
--- config/arm/arm.h	(revision 172962)
+++ config/arm/arm.h	(working copy)
@@ -218,6 +218,8 @@  extern void (*arm_lang_output_object_att
 
 #define TARGET_HARD_TP			(target_thread_pointer == TP_CP15)
 #define TARGET_SOFT_TP			(target_thread_pointer == TP_SOFT)
+#define TARGET_ARM_TLS			(target_tls_dialect == TLS_ARM)
+#define TARGET_GNU_TLS			(target_tls_dialect == TLS_GNU)
 
 /* Only 16-bit thumb code.  */
 #define TARGET_THUMB1			(TARGET_THUMB && !arm_arch_thumb2)
@@ -306,7 +308,8 @@  extern void (*arm_lang_output_object_att
      by -march).
    --with-float is ignored if -mfloat-abi is specified.
    --with-fpu is ignored if -mfpu is specified.
-   --with-abi is ignored is -mabi is specified.  */
+   --with-abi is ignored if -mabi is specified.
+   --with-tls is ignored if -mtls-dialect is specified. */
 #define OPTION_DEFAULT_SPECS \
   {"arch", "%{!march=*:%{!mcpu=*:-march=%(VALUE)}}" }, \
   {"cpu", "%{!march=*:%{!mcpu=*:-mcpu=%(VALUE)}}" }, \
@@ -314,7 +317,8 @@  extern void (*arm_lang_output_object_att
   {"float", "%{!mfloat-abi=*:-mfloat-abi=%(VALUE)}" }, \
   {"fpu", "%{!mfpu=*:-mfpu=%(VALUE)}"}, \
   {"abi", "%{!mabi=*:-mabi=%(VALUE)}"}, \
-  {"mode", "%{!marm:%{!mthumb:-m%(VALUE)}}"},
+  {"mode", "%{!marm:%{!mthumb:-m%(VALUE)}}"}, \
+  {"tls", "%{!mtls-dialect:-mtls-dialect=%(VALUE)}"},
 
 /* Which floating point model to use.  */
 enum arm_fp_model
@@ -400,7 +404,13 @@  enum arm_tp_type {
   TP_CP15
 };
 
+enum arm_tls_type {
+  TLS_ARM,
+  TLS_GNU
+};
+
 extern enum arm_tp_type target_thread_pointer;
+extern enum arm_tls_type target_tls_dialect;
 
 /* Nonzero if this chip supports the ARM Architecture 3M extensions.  */
 extern int arm_arch3m;
Index: config/arm/arm.opt
===================================================================
--- config/arm/arm.opt	(revision 172962)
+++ config/arm/arm.opt	(working copy)
@@ -138,6 +138,10 @@  mthumb-interwork
 Target Report Mask(INTERWORK)
 Support calls between Thumb and ARM instruction sets
 
+mtls-dialect=
+Target RejectNegative Joined Var(target_tls_dialect_switch)
+Specify thread local storage scheme
+
 mtp=
 Target RejectNegative Joined Var(target_thread_switch)
 Specify how to access the thread pointer
Index: config/arm/arm.md
===================================================================
--- config/arm/arm.md	(revision 172962)
+++ config/arm/arm.md	(working copy)
@@ -10620,6 +10620,28 @@ 
   [(set_attr "conds" "clob")]
 )
 
+;; tls descriptor call
+(define_insn "tlscall"
+  [(set (reg:SI 0) (unspec:SI [(reg:SI 0)
+                               (match_operand:SI 0 "" "X")
+			       (match_operand 1 "" "")] UNSPEC_TLS))
+   (clobber (reg:SI 1))
+   (clobber (reg:SI LR_REGNUM))
+   (clobber (reg:SI CC_REGNUM))]
+  "TARGET_GNU_TLS"
+  {
+    targetm.asm_out.internal_label (asm_out_file, "LPIC",
+				    INTVAL (operands[1]));
+    /* The + is to avoid an assembly parse ambiguity with symbols that
+       look like register names, which is unsuccessfully recovered from.  */
+    return TARGET_THUMB2 ? "blx\\t%c0(tlscall)" : "bl\\t+%c0(tlscall)";
+  }
+  [(set_attr "conds" "clob")
+   (set_attr "length" "4")]
+)
+
+;;
+
 ;; We only care about the lower 16 bits of the constant 
 ;; being inserted into the upper 16 bits of the register.
 (define_insn "*arm_movtas_ze"