This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[patch] TLS for mn10300 (am33)
- From: DJ Delorie <dj at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Tue, 30 May 2006 19:51:05 -0400
- Subject: [patch] TLS for mn10300 (am33)
Initial patch to add TLS to mn10300 (am33 only), goes along with the
binutils patch here: http://sourceware.org/ml/binutils/2006-05/msg00224.html
Note that am33 doesn't currently build libgcc2 even without this
patch, so I haven't been able to fully test it, although I've run
various tests on cc1 manually.
[Yes, I know we're not in stage1 yet, but why wait to at least send
the patch in? ;]
2006-05-24 DJ Delorie <dj@redhat.com>
* config/mn10300/mn10300.c (REG_SAVED): New.
(REG_SAVE_BYTES): Use it.
(fp_regs_to_save): Use it.
(can_use_return_insn): Use it.
(mn10300_get_live_callee_saved_regs): Use it.
(expand_prologue): Use it.
(expand_epilogue): Use it.
(initial_offset): Use it.
* config/mn10300/mn10300.c (expand_prologue): Mark insns
backwards, in case the list is empty.
* config/mn10300/mn10300.c (TARGET_HAVE_TLS): Define.
(TARGET_ASM_OUTPUT_DWARF_DTPREL): Define.
(TARGET_CANNOT_FORCE_CONST_MEM): Define.
(mn10300_override_options): Shut off TLS if no TLS register.
(mn10300_init_machine_status): New.
(mn10300_init_expanders): New.
(tls_symbolic_operand): New.
(tls_symbolic_operand_1): New.
(mn10300_output_dwarf_dtprel): New.
(get_some_local_dynamic_name_1): New.
(get_some_local_dynamic_name): New.
(print_operand): Add '&' modifier.
(mn10300_tls_get_addr): New.
(legitimize_address): Check for TLS symbols.
(pic_nonpic_got_ptr): New.
(legitimize_tls_address): New.
(mn10300_constant_address_p): New.
(legitimate_constant_p): New.
(mn10300_cannot_force_const_mem): New.
(legitimate_pic_operand_p): Check for TLS symbols.
(legitimate_tls_operand_p): New.
(nontls_general_operand): New.
(nontls_nonmemory_operand): New.
(legitimate_address_p): Check for TLS symbols.
(mn10300_encode_section_info): Call the default handler too.
* config/mn10300/mn10300.h (struct machine_function): New.
(INIT_EXPANDERS): Define.
(FIXED_REGISTERS): Fix $r5 as the TLS base register.
(reg_class): Add D0_REGS and A0_REGS.
(REG_CLASS_NAMES): Likewise.
(REG_CLASS_CONTENTS): Likewise.
(REG_CLASS_FROM_LETTER): 'B' for D0_REGS, 'C' for A0_REGS.
(CONSTANT_ADDRESS_P): Call legitimate_constant_p.
(OUTPUT_ADDR_CONST_EXTRA): Add TLS unspecs.
(PRINT_OPERAND_PUNCT_VALID_P): New.
(OVERRIDE_OPTIONS): New.
* config/mn10300/mn10300-protos.h (legitimize_tls_address): Prototype.
(mn10300_tls_get_addr): Prototype.
(mn10300_output_dwarf_dtprel): Prototype.
(legitimate_tls_operand_p): Prototype.
(legitimate_constant_p): Prototype.
(mn10300_constant_address_p): Prototype.
(tls_symbolic_operand): Prototype.
(nontls_general_operand): Prototype.
(nontls_nonmemory_operand): Prototype.
(mn10300_override_options): Prototype.
* config/mn10300/mn10300.md (TLS_REG): define.
(UNSPEC_TLSGD, UNSPEC_TLSLDM, UNSPEC_DTPOFF, UNSPEC_GOTNTPOFF,
UNSPEC_INDNTPOFF, UNSPEC_TPOFF, UNSPEC_TLS_GD, UNSPEC_TLS_LD_BASE): Define.
(movsi): Legitimize TLS variables.
(movsi_internal): Preclude unwrapped TLS symbols.
(am33_loadPC_anyreg): New.
(am33_set_got): New.
(add_GOT_to_any_reg): New.
(sym2PIC): Add SI mode.
(sym2PLT): Likewise.
(tls_global_dynamic_i): New.
(tls_global_dynamic): New.
(tls_local_dynamic_base_i): New.
(tls_local_dynamic_base): New.
(tls_initial_exec): New.
(tls_initial_exec_1): New.
(tls_initial_exec_2): New.
Index: config/mn10300/mn10300.c
===================================================================
--- config/mn10300/mn10300.c (revision 114250)
+++ config/mn10300/mn10300.c (working copy)
@@ -1,5 +1,5 @@
/* Subroutines for insn-output.c for Matsushita MN10300 series
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
Contributed by Jeff Law (law@cygnus.com).
@@ -38,6 +38,7 @@ Boston, MA 02110-1301, USA. */
#include "expr.h"
#include "optabs.h"
#include "function.h"
+#include "ggc.h"
#include "obstack.h"
#include "toplev.h"
#include "tm_p.h"
@@ -57,15 +58,20 @@ int mn10300_protect_label;
/* The selected processor. */
enum processor_type mn10300_processor = PROCESSOR_DEFAULT;
+/* Returns non-zero if reg REGNO is saved in the prologue. We
+ centralize this as call_used_regs reflects any user-space
+ -ffixed-reg-N (etc) options. */
+#define REG_SAVED(REGNO) (regs_ever_live[REGNO] && ! call_used_regs[REGNO])
+
/* The size of the callee register save area. Right now we save everything
on entry since it costs us nothing in code size. It does cost us from a
speed standpoint, so we want to optimize this sooner or later. */
-#define REG_SAVE_BYTES (4 * regs_ever_live[2] \
- + 4 * regs_ever_live[3] \
- + 4 * regs_ever_live[6] \
- + 4 * regs_ever_live[7] \
- + 16 * (regs_ever_live[14] || regs_ever_live[15] \
- || regs_ever_live[16] || regs_ever_live[17]))
+#define REG_SAVE_BYTES (4 * REG_SAVED(2) \
+ + 4 * REG_SAVED(3) \
+ + 4 * REG_SAVED(6) \
+ + 4 * REG_SAVED(7) \
+ + 16 * (REG_SAVED(14) || REG_SAVED(15) \
+ || REG_SAVED(16) || REG_SAVED(17)))
static bool mn10300_handle_option (size_t, const char *, int);
@@ -79,6 +85,7 @@ static bool mn10300_pass_by_reference (C
tree, bool);
static int mn10300_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
+static bool mn10300_cannot_force_const_mem (rtx);
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
@@ -116,6 +123,15 @@ static int mn10300_arg_partial_bytes (CU
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS mn10300_builtin_saveregs
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL mn10300_output_dwarf_dtprel
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM mn10300_cannot_force_const_mem
+
static void mn10300_encode_section_info (tree, rtx, int);
struct gcc_target targetm = TARGET_INITIALIZER;
@@ -148,6 +164,10 @@ mn10300_override_options (void)
{
if (TARGET_AM33)
target_flags &= ~MASK_MULT_BUG;
+
+ /* We can't do TLS if we don't have the TLS register. */
+ if (!TARGET_AM33)
+ targetm.have_tls = false;
}
static void
@@ -162,6 +182,97 @@ mn10300_file_start (void)
}
+/* Defining data structures for per-function information */
+
+/* The usual; we set up our machine_function data. */
+static struct machine_function *
+mn10300_init_machine_status (void)
+{
+ struct machine_function *machine;
+ machine =
+ (machine_function *) ggc_alloc_cleared (sizeof (machine_function));
+
+ return machine;
+}
+
+/* Implements INIT_EXPANDERS. We just set up to call the above
+ function. */
+void
+mn10300_init_expanders (void)
+{
+ init_machine_status = mn10300_init_machine_status;
+}
+
+
+/* Returns non-zero if OP is a symbol with TLS linkage. Specifically,
+ it returns the TLS model. */
+int
+tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (op) == CONST)
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == PLUS
+ && GET_CODE (XEXP (op, 1)) == CONST_INT)
+ op = XEXP (op, 0);
+ if (GET_CODE (op) != SYMBOL_REF)
+ return 0;
+ return SYMBOL_REF_TLS_MODEL (op);
+}
+
+/* Returns non-zero if OP has the KIND tls model. */
+static inline int
+tls_symbolic_operand_1 (rtx op, enum tls_model kind)
+{
+ if (GET_CODE (op) != SYMBOL_REF)
+ return 0;
+ return SYMBOL_REF_TLS_MODEL (op) == kind;
+}
+
+/* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL.
+ We need to emit DTP-relative relocations. */
+
+void
+mn10300_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+ fputs ("\t.4byte\t", file);
+ output_addr_const (file, x);
+ fputs ("@dtpoff", file);
+}
+
+/* Locate some local-dynamic symbol still in use by this function
+ so that we can print its name in some tls_local_dynamic_base
+ pattern. This is used by "%&" in print_operand(). */
+static int
+get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
+{
+ rtx x = *px;
+
+ if (GET_CODE (x) == SYMBOL_REF
+ && tls_symbolic_operand_1 (x, TLS_MODEL_LOCAL_DYNAMIC))
+ {
+ cfun->machine->some_ld_name = XSTR (x, 0);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const char *
+get_some_local_dynamic_name (void)
+{
+ rtx insn;
+
+ if (cfun->machine->some_ld_name)
+ return cfun->machine->some_ld_name;
+
+ for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+ if (INSN_P (insn)
+ && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
+ return cfun->machine->some_ld_name;
+
+ abort ();
+}
+
/* Print operand X using operand code CODE to assembly language output file
FILE. */
@@ -428,6 +539,10 @@ print_operand (FILE *file, rtx x, int co
fprintf (file, "%d", (int)(INTVAL (x) & 0xff));
break;
+ case '&':
+ assemble_name (file, get_some_local_dynamic_name ());
+ return;
+
/* For shift counts. The hardware ignores the upper bits of
any immediate, but the assembler will flag an out of range
shift count as an error. So we mask off the high bits
@@ -537,7 +652,7 @@ fp_regs_to_save (void)
return 0;
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (REG_SAVED(i))
++n;
return n;
@@ -590,14 +705,14 @@ can_use_return_insn (void)
return (reload_completed
&& size == 0
- && !regs_ever_live[2]
- && !regs_ever_live[3]
- && !regs_ever_live[6]
- && !regs_ever_live[7]
- && !regs_ever_live[14]
- && !regs_ever_live[15]
- && !regs_ever_live[16]
- && !regs_ever_live[17]
+ && !REG_SAVED(2)
+ && !REG_SAVED(3)
+ && !REG_SAVED(6)
+ && !REG_SAVED(7)
+ && !REG_SAVED(14)
+ && !REG_SAVED(15)
+ && !REG_SAVED(16)
+ && !REG_SAVED(17)
&& fp_regs_to_save () == 0
&& !frame_pointer_needed);
}
@@ -614,11 +729,14 @@ mn10300_get_live_callee_saved_regs (void
mask = 0;
for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (REG_SAVED(i))
mask |= (1 << i);
if ((mask & 0x3c000) != 0)
mask |= 0x3c000;
+ /* The "original" call_used_regs. */
+ mask &= 0x3c0cc;
+
return mask;
}
@@ -907,7 +1025,7 @@ expand_prologue (void)
/* Now actually save the FP registers. */
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (REG_SAVED(i))
{
rtx addr;
@@ -954,13 +1072,13 @@ expand_prologue (void)
delete the initialization too. */
do
{
- insn = NEXT_INSN (insn);
-
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
+ REG_NOTES (last) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
const0_rtx,
- REG_NOTES (insn));
+ REG_NOTES (last));
+ last = PREV_INSN (last);
+
}
- while (insn != last);
+ while (last && insn != last);
}
}
@@ -1127,7 +1245,7 @@ expand_epilogue (void)
reg = gen_rtx_POST_INC (SImode, reg);
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (REG_SAVED(i))
{
rtx addr;
@@ -1189,10 +1307,10 @@ expand_epilogue (void)
}
/* Adjust the stack and restore callee-saved registers, if any. */
- if (size || regs_ever_live[2] || regs_ever_live[3]
- || regs_ever_live[6] || regs_ever_live[7]
- || regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17]
+ if (size || REG_SAVED(2) || REG_SAVED(3)
+ || REG_SAVED(6) || REG_SAVED(7)
+ || REG_SAVED(14) || REG_SAVED(15)
+ || REG_SAVED(16) || REG_SAVED(17)
|| frame_pointer_needed)
emit_jump_insn (gen_return_internal_regs
(GEN_INT (size + REG_SAVE_BYTES)));
@@ -1400,10 +1518,10 @@ initial_offset (int from, int to)
is the size of the callee register save area. */
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
{
- if (regs_ever_live[2] || regs_ever_live[3]
- || regs_ever_live[6] || regs_ever_live[7]
- || regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17]
+ if (REG_SAVED(2) || REG_SAVED(3)
+ || REG_SAVED(6) || REG_SAVED(7)
+ || REG_SAVED(14) || REG_SAVED(15)
+ || REG_SAVED(16) || REG_SAVED(17)
|| fp_regs_to_save ()
|| frame_pointer_needed)
return REG_SAVE_BYTES
@@ -1417,10 +1535,10 @@ initial_offset (int from, int to)
area, and the fixed stack space needed for function calls (if any). */
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
{
- if (regs_ever_live[2] || regs_ever_live[3]
- || regs_ever_live[6] || regs_ever_live[7]
- || regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17]
+ if (REG_SAVED(2) || REG_SAVED(3)
+ || REG_SAVED(6) || REG_SAVED(7)
+ || REG_SAVED(14) || REG_SAVED(15)
+ || REG_SAVED(16) || REG_SAVED(17)
|| fp_regs_to_save ()
|| frame_pointer_needed)
return (get_frame_size () + REG_SAVE_BYTES
@@ -1756,6 +1874,13 @@ mask_ok_for_mem_btst (int len, int bit)
|| ((mask & 0xff000000) == mask));
}
+/* Encapsulates the chosen TLS base register. */
+rtx
+mn10300_tls_get_addr (void)
+{
+ return gen_rtx_REG (Pmode, TLS_REG);
+}
+
/* Return 1 if X contains a symbolic expression. We know these
expressions will have one of a few well defined forms, so
we need only check those forms. */
@@ -1796,7 +1921,9 @@ rtx
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
- if (flag_pic && ! legitimate_pic_operand_p (x))
+ if (tls_symbolic_operand (x, Pmode))
+ x = legitimize_tls_address (oldx);
+ else if (flag_pic && ! legitimate_pic_operand_p (x))
x = legitimize_pic_address (oldx, NULL_RTX);
/* Uh-oh. We might have an address for x[n-100000]. This needs
@@ -1856,6 +1983,194 @@ legitimize_pic_address (rtx orig, rtx re
return orig;
}
+/* Regardless of whether we're in PIC mode or not, return an RTX that
+ refers to the GOT base. In PIC mode, we return the PIC register.
+ In non-PIC mode, we calculate the GOT base into a pseudo and cache
+ it. */
+static rtx
+pic_nonpic_got_ptr (void)
+{
+ rtx uns, insns, note, got, tmp;
+ if (flag_pic)
+ return gen_rtx_REG (SImode, PIC_REG);
+ else
+ {
+ if (cfun->machine->got_ptr == NULL)
+ {
+ tmp = gen_reg_rtx (SImode);
+ got = gen_reg_rtx (SImode);
+ start_sequence ();
+ emit_insn (gen_am33_set_got (tmp));
+ insns = get_insns ();
+ end_sequence ();
+ note = gen_rtx_EXPR_LIST (VOIDmode, const0_rtx, NULL);
+ uns = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);
+ note = gen_rtx_EXPR_LIST (VOIDmode, uns, note);
+ emit_libcall_block (insns, got, tmp, note);
+ cfun->machine->got_ptr = got;
+ }
+ return cfun->machine->got_ptr;
+ }
+}
+
+/* Given a symbol X that has TLS linkage, convert it into a (usually)
+ unspec pattern that reflects the appropriate TLS access type. */
+rtx
+legitimize_tls_address (rtx x)
+{
+ rtx dest, base, off, uns, a0, insns, note, got;
+
+ if (GET_CODE (x) == CONST)
+ return legitimize_tls_address (XEXP (x, 0));
+
+ if (GET_CODE (x) == PLUS)
+ return gen_rtx_PLUS (GET_MODE (x),
+ legitimize_tls_address (XEXP (x, 0)),
+ XEXP (x, 1));
+
+ switch (SYMBOL_REF_TLS_MODEL (x))
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ got = pic_nonpic_got_ptr ();
+ a0 = gen_rtx_REG (Pmode, FIRST_ADDRESS_REGNUM);
+ start_sequence ();
+ dest = gen_reg_rtx (Pmode);
+ emit_call_insn (gen_tls_global_dynamic (a0, x, got));
+ insns = get_insns ();
+ end_sequence ();
+ note = gen_rtx_EXPR_LIST (VOIDmode, const0_rtx, NULL);
+ uns = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TLS_GD);
+ note = gen_rtx_EXPR_LIST (VOIDmode, uns, note);
+ emit_libcall_block (insns, dest, a0, note);
+ if (current_function_outgoing_args_size == 0)
+ current_function_outgoing_args_size = 8;
+ break;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ got = pic_nonpic_got_ptr ();
+ a0 = gen_rtx_REG (Pmode, FIRST_ADDRESS_REGNUM);
+ start_sequence ();
+ dest = gen_reg_rtx (Pmode);
+ base = gen_reg_rtx (Pmode);
+ emit_call_insn (gen_tls_local_dynamic_base (a0, x, got));
+ emit_move_insn (base, a0);
+ insns = get_insns ();
+ end_sequence ();
+ note = gen_rtx_EXPR_LIST (VOIDmode, const0_rtx, NULL);
+ note = gen_rtx_EXPR_LIST (VOIDmode, GEN_INT (UNSPEC_TLS_LD_BASE), note);
+ emit_libcall_block (insns, base, a0, note);
+
+ uns = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPOFF);
+ uns = gen_rtx_CONST (Pmode, uns);
+ off = gen_reg_rtx (Pmode);
+ emit_move_insn (off, uns);
+ emit_insn (gen_addsi3 (dest, off, base));
+ if (current_function_outgoing_args_size == 0)
+ current_function_outgoing_args_size = 8;
+ break;
+
+ case TLS_MODEL_INITIAL_EXEC:
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tls_initial_exec (dest, x));
+ insns = emit_insn (gen_addsi3 (dest, dest, mn10300_tls_get_addr ()));
+ break;
+
+ case TLS_MODEL_LOCAL_EXEC:
+ uns = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPOFF);
+ uns = gen_rtx_CONST (Pmode, uns);
+ off = gen_reg_rtx (Pmode);
+ emit_move_insn (off, uns);
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_addsi3 (dest, off, mn10300_tls_get_addr ()));
+ break;
+
+ default:
+ abort ();
+ }
+
+ return dest;
+}
+
+/* Returns TRUE if X is a valid constant address. Used by
+ CONSTANT_ADDRESS_P(). This definition makes sure we don't put TLS
+ symbols in initializers and such. */
+bool
+mn10300_constant_address_p (rtx x)
+{
+ if (tls_symbolic_operand (x, Pmode))
+ return false;
+ return CONSTANT_P (x);
+}
+
+/* Used by CONSTANT_P(). Returns TRUE if X is a valid constant. Note
+ that some "constants" aren't valid, such as TLS symbols and
+ unconverted GOT-based references, so we eliminate those here. */
+bool
+legitimate_constant_p (rtx x)
+{
+ switch (GET_CODE (x))
+ {
+ case CONST:
+ x = XEXP (x, 0);
+
+ if (GET_CODE (x) == PLUS)
+ {
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ return false;
+ x = XEXP (x, 0);
+ }
+
+ /* Only some unspecs are valid as "constants". */
+ if (GET_CODE (x) == UNSPEC)
+ {
+ rtx sym = XVECEXP (x, 0, 0);
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_DTPOFF:
+ return tls_symbolic_operand_1 (sym, TLS_MODEL_LOCAL_DYNAMIC);
+ case UNSPEC_GOTNTPOFF:
+ case UNSPEC_INDNTPOFF:
+ return tls_symbolic_operand_1 (sym, TLS_MODEL_INITIAL_EXEC);
+ case UNSPEC_TPOFF:
+ return tls_symbolic_operand_1 (sym, TLS_MODEL_LOCAL_EXEC);
+ case UNSPEC_INT_LABEL:
+ case UNSPEC_PIC:
+ case UNSPEC_GOT:
+ case UNSPEC_GOTOFF:
+ case UNSPEC_PLT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /* We must have drilled down to a symbol. */
+ if (!symbolic_operand (x, Pmode))
+ return false;
+ /* FALLTHRU */
+
+ case SYMBOL_REF:
+ if (tls_symbolic_operand (x, Pmode))
+ return false;
+
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/* Determine if it's legal to put X into the constant pool. This
+ is not possible for the address of thread-local symbols, which
+ is checked above. */
+
+static bool
+mn10300_cannot_force_const_mem (rtx x)
+{
+ return !legitimate_constant_p (x);
+}
+
/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
isn't protected by a PIC unspec; nonzero otherwise. */
int
@@ -1864,6 +2179,9 @@ legitimate_pic_operand_p (rtx x)
register const char *fmt;
register int i;
+ if (legitimate_tls_operand_p (x))
+ return 1;
+
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
return 0;
@@ -1892,15 +2210,82 @@ legitimate_pic_operand_p (rtx x)
return 1;
}
+/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a TLS unspec; nonzero otherwise. */
+int
+legitimate_tls_operand_p (rtx x)
+{
+ register const char *fmt;
+ register int i;
+
+ if (GET_CODE (x) == CONST)
+ x = XEXP (x, 0);
+
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ return 0;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_TLSGD
+ || XINT (x, 1) == UNSPEC_TLSLDM
+ || XINT (x, 1) == UNSPEC_DTPOFF
+ || XINT (x, 1) == UNSPEC_GOTNTPOFF
+ || XINT (x, 1) == UNSPEC_INDNTPOFF
+ || XINT (x, 1) == UNSPEC_TPOFF
+ ))
+ return 1;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (! legitimate_tls_operand_p (XVECEXP (x, i, j)))
+ return 0;
+ }
+ else if (fmt[i] == 'e' && ! legitimate_tls_operand_p (XEXP (x, i)))
+ return 0;
+ }
+
+
+}
+
+int
+nontls_general_operand (rtx x, enum machine_mode mode)
+{
+ if (tls_symbolic_operand (x, mode))
+ return FALSE;
+ return general_operand (x, mode);
+}
+
+int
+nontls_nonmemory_operand (rtx x, enum machine_mode mode)
+{
+ if (tls_symbolic_operand (x, mode))
+ return FALSE;
+ return nonmemory_operand (x, mode);
+}
+
/* Return TRUE if the address X, taken from a (MEM:MODE X) rtx, is
legitimate, and FALSE otherwise. */
bool
legitimate_address_p (enum machine_mode mode, rtx x, int strict)
{
- if (CONSTANT_ADDRESS_P (x)
+ int i;
+
+ if ((i = tls_symbolic_operand (x, Pmode)))
+ return FALSE;
+
+ if (CONSTANT_P (x)
&& (! flag_pic || legitimate_pic_operand_p (x)))
return TRUE;
+ if (CONSTANT_P (x)
+ && legitimate_tls_operand_p (x))
+ return TRUE;
+
if (RTX_OK_FOR_BASE_P (x, strict))
return TRUE;
@@ -1933,10 +2318,16 @@ legitimate_address_p (enum machine_mode
if (GET_CODE (index) == CONST_INT)
return TRUE;
if (GET_CODE (index) == CONST
- && GET_CODE (XEXP (index, 0)) != PLUS
- && (! flag_pic
- || legitimate_pic_operand_p (index)))
- return TRUE;
+ && GET_CODE (XEXP (index, 0)) != PLUS)
+ {
+ if (legitimate_tls_operand_p (index))
+ return TRUE;
+ if (tls_symbolic_operand (index, Pmode))
+ return FALSE;
+ if (! flag_pic
+ || legitimate_pic_operand_p (index))
+ return TRUE;
+ }
}
}
@@ -2126,4 +2517,6 @@ mn10300_encode_section_info (tree decl,
if (flag_pic)
SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
+
+ default_encode_section_info (decl, rtl, first);
}
Index: config/mn10300/mn10300.h
===================================================================
--- config/mn10300/mn10300.h (revision 114250)
+++ config/mn10300/mn10300.h (working copy)
@@ -1,6 +1,6 @@
/* Definitions of target machine for GNU compiler.
Matsushita MN10300 series
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
Contributed by Jeff Law (law@cygnus.com).
@@ -65,6 +65,19 @@ extern enum processor_type mn10300_proce
#define TARGET_VERSION fprintf (stderr, " (MN10300)");
+/* Defining data structures for per-function information */
+
+typedef struct machine_function GTY (())
+{
+ /* For non-pic use. */
+ rtx got_ptr;
+ /* For TLS local dynamic. */
+ rtx some_ld_name;
+}
+machine_function;
+
+#define INIT_EXPANDERS mn10300_init_expanders ()
+
/* Target machine storage layout */
@@ -151,7 +164,7 @@ extern enum processor_type mn10300_proce
and are not available for the register allocator. */
#define FIXED_REGISTERS \
- { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 \
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \
}
@@ -251,7 +264,7 @@ extern enum processor_type mn10300_proce
class that represents their union. */
enum reg_class {
- NO_REGS, DATA_REGS, ADDRESS_REGS, SP_REGS,
+ NO_REGS, D0_REGS, DATA_REGS, A0_REGS, ADDRESS_REGS, SP_REGS,
DATA_OR_ADDRESS_REGS, SP_OR_ADDRESS_REGS,
EXTENDED_REGS, DATA_OR_EXTENDED_REGS, ADDRESS_OR_EXTENDED_REGS,
SP_OR_EXTENDED_REGS, SP_OR_ADDRESS_OR_EXTENDED_REGS,
@@ -264,7 +277,7 @@ enum reg_class {
/* Give names of register classes as strings for dump file. */
#define REG_CLASS_NAMES \
-{ "NO_REGS", "DATA_REGS", "ADDRESS_REGS", \
+{ "NO_REGS", "D0_REGS", "DATA_REGS", "A0_REGS", "ADDRESS_REGS", \
"SP_REGS", "DATA_OR_ADDRESS_REGS", "SP_OR_ADDRESS_REGS", \
"EXTENDED_REGS", \
"DATA_OR_EXTENDED_REGS", "ADDRESS_OR_EXTENDED_REGS", \
@@ -278,7 +291,9 @@ enum reg_class {
#define REG_CLASS_CONTENTS \
{ { 0, 0 }, /* No regs */ \
+ { 0x00001, 0 }, /* D0_REGS */ \
{ 0x0000f, 0 }, /* DATA_REGS */ \
+ { 0x00010, 0 }, /* A0_REGS */ \
{ 0x001f0, 0 }, /* ADDRESS_REGS */ \
{ 0x00200, 0 }, /* SP_REGS */ \
{ 0x001ff, 0 }, /* DATA_OR_ADDRESS_REGS */\
@@ -300,7 +315,9 @@ enum reg_class {
or could index an array. */
#define REGNO_REG_CLASS(REGNO) \
- ((REGNO) <= LAST_DATA_REGNUM ? DATA_REGS : \
+ ((REGNO) == FIRST_DATA_REGNUM ? D0_REGS : \
+ (REGNO) <= LAST_DATA_REGNUM ? DATA_REGS : \
+ (REGNO) == FIRST_ADDRESS_REGNUM ? A0_REGS : \
(REGNO) <= LAST_ADDRESS_REGNUM ? ADDRESS_REGS : \
(REGNO) == STACK_POINTER_REGNUM ? SP_REGS : \
(REGNO) <= LAST_EXTENDED_REGNUM ? EXTENDED_REGS : \
@@ -316,6 +333,8 @@ enum reg_class {
#define REG_CLASS_FROM_LETTER(C) \
((C) == 'd' ? DATA_REGS : \
(C) == 'a' ? ADDRESS_REGS : \
+ (C) == 'B' ? D0_REGS : \
+ (C) == 'C' ? A0_REGS : \
(C) == 'y' ? SP_REGS : \
! TARGET_AM33 ? NO_REGS : \
(C) == 'x' ? EXTENDED_REGS : \
@@ -672,7 +691,7 @@ struct cum_arg {int nbytes; };
/* 1 if X is an rtx for a constant that is a valid address. */
-#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X)
+#define CONSTANT_ADDRESS_P(X) mn10300_constant_address_p(X)
/* Extra constraints. */
@@ -784,7 +803,7 @@ while (0)
/* Nonzero if the constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
-#define LEGITIMATE_CONSTANT_P(X) 1
+#define LEGITIMATE_CONSTANT_P(X) (legitimate_constant_p (X))
/* Zero if this needs fixing up to become PIC. */
@@ -832,6 +851,30 @@ while (0)
output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
fputs ("@PLT", (STREAM)); \
break; \
+ case UNSPEC_TLSGD: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@tlsgd", (STREAM)); \
+ break; \
+ case UNSPEC_TLSLDM: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@tlsldm", (STREAM)); \
+ break; \
+ case UNSPEC_DTPOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@dtpoff", (STREAM)); \
+ break; \
+ case UNSPEC_GOTNTPOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@gotntpoff", (STREAM)); \
+ break; \
+ case UNSPEC_INDNTPOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@indntpoff", (STREAM)); \
+ break; \
+ case UNSPEC_TPOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@tpoff", (STREAM)); \
+ break; \
default: \
goto FAIL; \
} \
@@ -958,6 +1001,11 @@ while (0)
#define PRINT_OPERAND(FILE, X, CODE) print_operand(FILE,X,CODE)
+/* %& prints some local dynamic TLS symbol. */
+
+#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
+ ((CODE) == '&')
+
/* Print a memory operand whose address is X, on file FILE.
This uses a function in output-vax.c. */
@@ -1062,3 +1110,5 @@ cc_status_mdep;
#define CC_STATUS_MDEP cc_status_mdep
#define CC_STATUS_MDEP_INIT (cc_status.mdep.fpCC = 0)
+
+#define OVERRIDE_OPTIONS mn10300_override_options ()
Index: config/mn10300/mn10300-protos.h
===================================================================
--- config/mn10300/mn10300-protos.h (revision 114250)
+++ config/mn10300/mn10300-protos.h (working copy)
@@ -28,8 +28,14 @@ extern void mn10300_va_start (tree, rtx)
extern void mn10300_override_options (void);
extern struct rtx_def *legitimize_address (rtx, rtx, enum machine_mode);
extern rtx legitimize_pic_address (rtx, rtx);
+extern rtx legitimize_tls_address (rtx);
+extern rtx mn10300_tls_get_addr (void);
+extern void mn10300_output_dwarf_dtprel (FILE *, int, rtx);
extern int legitimate_pic_operand_p (rtx);
+extern int legitimate_tls_operand_p (rtx);
extern bool legitimate_address_p (enum machine_mode, rtx, int);
+extern bool legitimate_constant_p (rtx);
+extern bool mn10300_constant_address_p (rtx);
extern void print_operand (FILE *, rtx, int);
extern void print_operand_address (FILE *, rtx);
extern void mn10300_print_reg_list (FILE *, int);
@@ -41,7 +47,10 @@ extern enum reg_class mn10300_secondary_
extern const char *output_tst (rtx, rtx);
extern int store_multiple_operation (rtx, enum machine_mode);
extern int symbolic_operand (rtx, enum machine_mode);
+extern int tls_symbolic_operand (rtx, enum machine_mode);
extern int impossible_plus_operand (rtx, enum machine_mode);
+extern int nontls_general_operand (rtx, enum machine_mode);
+extern int nontls_nonmemory_operand (rtx, enum machine_mode);
extern bool mn10300_wide_const_load_uses_clr (rtx operands[2]);
#endif /* RTX_CODE */
@@ -57,3 +66,4 @@ extern void expand_epilogue (void);
extern int initial_offset (int, int);
extern int can_use_return_insn (void);
extern int mask_ok_for_mem_btst (int, int);
+extern void mn10300_override_options (void);
Index: config/mn10300/mn10300.md
===================================================================
--- config/mn10300/mn10300.md (revision 114250)
+++ config/mn10300/mn10300.md (working copy)
@@ -40,12 +40,25 @@
(define_constants [
(PIC_REG 6)
(SP_REG 9)
+ (TLS_REG 12)
(UNSPEC_INT_LABEL 0)
(UNSPEC_PIC 1)
(UNSPEC_GOT 2)
(UNSPEC_GOTOFF 3)
(UNSPEC_PLT 4)
+
+ ;; These put the corresponding @op suffix on TLS variables.
+ (UNSPEC_TLSGD 100)
+ (UNSPEC_TLSLDM 101)
+ (UNSPEC_DTPOFF 102)
+ (UNSPEC_GOTNTPOFF 103)
+ (UNSPEC_INDNTPOFF 104)
+ (UNSPEC_TPOFF 105)
+
+ ;; These are used to differentiate tls-specific insns.
+ (UNSPEC_TLS_GD 110)
+ (UNSPEC_TLS_LD_BASE 111)
])
(include "predicates.md")
@@ -293,6 +306,15 @@
""
"
{
+ int model;
+ model = tls_symbolic_operand (operands[1], Pmode);
+ if (model)
+ {
+ operands[1] = legitimize_tls_address (operands[1]);
+ operands[1] = force_operand (operands[1], operands[0]);
+ if (operands[1] == operands[0])
+ DONE;
+ }
/* One of the ops has to be in a register */
if (!register_operand (operand1, SImode)
&& !register_operand (operand0, SImode))
@@ -326,10 +348,10 @@
}
}")
-(define_insn ""
+(define_insn "movsi_internal"
[(set (match_operand:SI 0 "nonimmediate_operand"
"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax,axR,!*y,*f,*f,dxaQ")
- (match_operand:SI 1 "general_operand"
+ (match_operand:SI 1 "nontls_general_operand"
"0,0,I,I,dx,ax,dx,ax,dixm,aixm,dixm,aixm,!*y,axR,0,dxaQi*f,*f"))]
"register_operand (operands[0], SImode)
|| register_operand (operands[1], SImode)"
@@ -898,7 +920,7 @@
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,a,x,a,dax,!*y,!dax")
(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0,dax")
- (match_operand:SI 2 "nonmemory_operand" "J,J,L,L,daxi,i,dax")))]
+ (match_operand:SI 2 "nontls_nonmemory_operand" "J,J,L,L,daxi,i,dax")))]
"TARGET_AM33"
"*
{
@@ -2569,6 +2591,13 @@
"TARGET_AM33"
"%0:\;mov pc,a2")
+(define_insn "am33_loadPC_anyreg"
+ [(parallel
+ [(set (match_operand 0 "register_operand" "=a") (pc))
+ (use (match_operand 1 "" ""))])]
+ "TARGET_AM33"
+ "%1:\;mov pc,%0")
+
(define_insn_and_split "mn10300_loadPC"
[(parallel
@@ -2597,6 +2626,20 @@
DONE;
}")
+;; used for non-pic code; will store in any register.
+(define_expand "am33_set_got"
+ [(match_operand:SI 0 "" "")
+ (match_dup 1)]
+ "TARGET_AM33" "
+{
+ rtx tmp = gen_reg_rtx (SImode);
+ operands[1] = gen_int_label (GEN_INT (mn10300_unspec_int_label_counter++));
+ emit_insn (gen_am33_loadPC_anyreg (tmp, operands[1]));
+ emit_insn (gen_add_GOT_to_any_reg (operands[0], tmp, operands[1]));
+ DONE;
+}
+")
+
(define_insn "call_next_insn"
[(parallel
[(set (mem:SI (reg:SI SP_REG)) (pc))
@@ -2621,6 +2664,23 @@
operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);
}")
+(define_expand "add_GOT_to_any_reg"
+ [(set (match_operand:SI 0 "" "=r")
+ (plus:SI
+ (match_operand:SI 1 "" "0")
+ (const
+ (unspec [(minus:SI
+ (match_dup 3)
+ (const (minus:SI
+ (const (match_operand:SI 2 "" ""))
+ (pc))))
+ ] UNSPEC_PIC))))]
+ ""
+ "
+{
+ operands[3] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);
+}")
+
(define_expand "symGOT2reg"
[(match_operand:SI 0 "" "")
(match_operand:SI 1 "" "")]
@@ -2666,9 +2726,123 @@
"")
(define_expand "sym2PIC"
- [(unspec [(match_operand:SI 0 "" "")] UNSPEC_PIC)]
+ [(unspec:SI [(match_operand:SI 0 "" "")] UNSPEC_PIC)]
"" "")
(define_expand "sym2PLT"
- [(unspec [(match_operand:SI 0 "" "")] UNSPEC_PLT)]
+ [(unspec:SI [(match_operand:SI 0 "" "")] UNSPEC_PLT)]
"" "")
+
+; We clobber d0 instead of passing it as a parameter so that gcc will
+; know that it doesn't have to set a value in it. We use constraints
+; to specify it as reload avoids regs that are mentioned explicitly.
+; Whoever emits this is responsible for moving the result out of a0
+; into a pseudo, else reload complains sometimes. We use PIC_REG so
+; that gcc won't delete the prologue insns that set it. We don't
+; split the insns because relaxation requires this specific insn
+; sequence.
+
+(define_insn "tls_global_dynamic_i"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=C")
+ (call (unspec:SI
+ [(match_operand:SI 1 "tls_symbolic_operand" "")
+ (match_operand:SI 2 "register_operand" "a")
+ (match_operand 4 "register_operand" "")]
+ UNSPEC_TLS_GD)
+ (const_int 0)))
+ (clobber (match_operand 3 "register_operand" "=B"))])
+ ]
+ ""
+ {
+ if (flag_pic)
+ return "mov %1@tlsgd, %3\;add %2, %3\;call __tls_get_addr@PLT,[],0";
+ return "mov %1@tlsgd, %3\;add %2, %3\;call __tls_get_addr,[],0";
+ }
+ )
+
+(define_expand "tls_global_dynamic"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "")
+ (call (unspec:SI
+ [(match_operand:SI 1 "tls_symbolic_operand" "")
+ (match_operand:SI 2 "register_operand" "")
+ (match_dup 4)]
+ UNSPEC_TLS_GD)
+ (const_int 0)))
+ (clobber (match_dup 3))])
+ ]
+ ""
+{
+ operands[3] = gen_reg_rtx (SImode);
+ operands[4] = mn10300_tls_get_addr ();
+})
+
+(define_insn "tls_local_dynamic_base_i"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=C")
+ (call (unspec:SI
+ [(match_operand:SI 1 "tls_symbolic_operand" "")
+ (match_operand:SI 2 "register_operand" "a")
+ (match_operand 4 "register_operand" "")]
+ UNSPEC_TLS_LD_BASE)
+ (const_int 0)))
+ (clobber (match_operand 3 "register_operand" "=B"))])
+ ]
+ ""
+ {
+ if (flag_pic)
+ return "mov %&@tlsldm, %3\;add %2, %3\;call __tls_get_addr@PLT,[],0";
+ return "mov %&@tlsldm, %3\;add %2, %3\;call __tls_get_addr,[],0";
+ }
+ )
+
+(define_expand "tls_local_dynamic_base"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "")
+ (call (unspec:SI
+ [(match_operand:SI 1 "tls_symbolic_operand" "")
+ (match_operand:SI 2 "register_operand" "")
+ (match_dup 4)]
+ UNSPEC_TLS_LD_BASE)
+ (const_int 0)))
+ (clobber (match_dup 3))])
+ ]
+ ""
+{
+ operands[3] = gen_reg_rtx (SImode);
+ operands[4] = mn10300_tls_get_addr ();
+})
+
+(define_expand "tls_initial_exec"
+ [(match_operand:SI 0 "" "")
+ (match_operand:SI 1 "" "")]
+ ""
+ "
+{
+ rtx insn;
+
+ if (flag_pic)
+ insn = emit_insn (gen_tls_initial_exec_1 (operands[0], operands[1]));
+ else
+ insn = emit_insn (gen_tls_initial_exec_2 (operands[0], operands[1]));
+
+ MEM_READONLY_P (SET_SRC (PATTERN (insn))) = 1;
+
+ DONE;
+}")
+
+(define_expand "tls_initial_exec_1"
+ [(set (match_operand:SI 0 "" "")
+ (mem:SI (plus:SI (reg:SI PIC_REG)
+ (const (unspec [(match_operand:SI 1 "" "")]
+ UNSPEC_GOTNTPOFF)))))]
+ ""
+ "")
+
+(define_expand "tls_initial_exec_2"
+ [(set (match_operand:SI 0 "" "")
+ (mem:SI (const (unspec [(match_operand:SI 1 "" "")]
+ UNSPEC_INDNTPOFF))))]
+ ""
+ "")