This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[csl-arm] Thread-local storage support
- From: Daniel Jacobowitz <drow at false dot org>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Richard Earnshaw <Richard dot Earnshaw at arm dot com>,Paul Brook <paul at codesourcery dot com>, Phil Blundell <philb at gnu dot org>
- Date: Fri, 25 Mar 2005 14:26:28 -0500
- Subject: [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