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


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

Re: [4.5][PATCH][M68K] GCC support for TLS


Andreas Schwab wrote:
There is a problem with emitting @TLS decoration in the assembler
output: the decoration is emitted before the addend, which the assembler
can't parse.  The normal way for @GOT decoration is to emit the addend
before it.  If you try building libgomp with tls forced to be enabled
you get assembler errors when compiling team.c:

/tmp/ccJozhax.s: Assembler messages:
/tmp/ccJozhax.s:108: Error: syntax error -- statement `pea gomp_tls_data@TLSLE+40(%a0)' ignored
/tmp/ccJozhax.s:183: Error: syntax error -- statement `move.l gomp_tls_data@TLSLE+56(%a0),%a3' ignored
/tmp/ccJozhax.s:252: Error: syntax error -- statement `clr.l gomp_tls_data@TLSLE+56(%a0)' ignored
/tmp/ccJozhax.s:257: Error: syntax error -- statement `move.l gomp_tls_data@TLSLE+36(%a0),%a2' ignored
/tmp/ccJozhax.s:729: Error: syntax error -- statement `move.l gomp_tls_data@TLSLE+36(%a0),%d4' ignored
/tmp/ccJozhax.s:826: Error: syntax error -- statement `move.l gomp_tls_data@TLSLE+28(%a0),28(%a2)' ignored
/tmp/ccJozhax.s:920: Error: syntax error -- statement `move.l gomp_tls_data@TLSLE+28(%a0),28(%a2)' ignored

Andreas,


I apologize for such a long delay.

It turned out that there is no simple fix for the above problem. After trying a few hacks here and there, I came to using FINAL_PRESCAN_INSN macro, which looks like the best way of handling the problem; for the details, see the comment in m68k_final_prescan_insn() function.

Along the way of fixing the problem with decorations, I've cleaned up handling of GOT symbols, now they are alse wrapped into (const (unspec [(symbol)])) during legitimization process.

I've updated the patch to the latest trunk and retested it; no new regressions appeared.

In case you've reviewed the majority of changes already, fsf-nptl-gcc-23.patch is a diff between previous and current patches; fsf-nptl-gcc-3.patch is the full patch.

OK to apply?

Thanks,

--
Maxim K.
CodeSourcery
diff -rup -x .svn ./gcc-mainline-2/gcc/config/m68k/m68k.c ./gcc-mainline/gcc/config/m68k/m68k.c
--- ./gcc-mainline-2/gcc/config/m68k/m68k.c	2009-05-15 08:43:46.000000000 -0700
+++ ./gcc-mainline/gcc/config/m68k/m68k.c	2009-05-15 05:06:12.000000000 -0700
@@ -1862,7 +1862,7 @@ m68k_illegitimate_symbolic_constant_p (r
 	  && !offset_within_block_p (base, INTVAL (offset)))
 	return true;
     }
-  return m68k_tls_referenced_p (x);
+  return m68k_tls_reference_p (x, false);
 }
 
 /* Return true if X is a legitimate constant address that can reach
@@ -1890,7 +1890,7 @@ m68k_legitimate_constant_address_p (rtx 
 	return false;
     }
 
-  return !m68k_tls_referenced_p (x);
+  return !m68k_tls_reference_p (x, false);
 }
 
 /* Return true if X is a LABEL_REF for a jump table.  Assume that unplaced
@@ -1910,40 +1910,6 @@ m68k_jump_table_ref_p (rtx x)
   return x && JUMP_TABLE_DATA_P (x);
 }
 
-/* Unwrap symbol from UNSPEC_RELOC16 and, if unwrap_reloc32_p,
-   UNSPEC_RELOC32 wrappers.  */
-
-rtx
-m68k_unwrap_symbol (rtx orig, bool unwrap_reloc32_p)
-{
-  if (GET_CODE (orig) == CONST)
-    {
-      rtx x;
-
-      x = XEXP (orig, 0);
-
-      if (GET_CODE (x) == UNSPEC)
-	{
-	  switch (XINT (x, 1))
-	    {
-	    case UNSPEC_RELOC16:
-	      orig = XVECEXP (x, 0, 0);
-	      break;
-
-	    case UNSPEC_RELOC32:
-	      if (unwrap_reloc32_p)
-		orig = XVECEXP (x, 0, 0);
-	      break;
-
-	    default:
-	      break;
-	    }
-	}
-    }
-
-  return orig;
-}
-
 /* Return true if X is a legitimate address for values of mode MODE.
    STRICT_P says whether strict checking is needed.  If the address
    is valid, describe its components in *ADDRESS.  */
@@ -1991,18 +1957,12 @@ m68k_decompose_address (enum machine_mod
   /* Check for GOT loads.  These are (bd,An,Xn) addresses if
      TARGET_68020 && flag_pic == 2, otherwise they are (d16,An)
      addresses.  */
-  if (pic_offset_table_rtx != NULL_RTX
-      && GET_CODE (x) == PLUS
+  if (GET_CODE (x) == PLUS
       && XEXP (x, 0) == pic_offset_table_rtx)
     {
-      rtx sym;
-
-      /* As we are processing a PLUS, do not unwrap RELOC32
-	 symbols here; they are invalid in this context.  */
-      sym = m68k_unwrap_symbol (XEXP (x, 1), false);
-
-      if (GET_CODE (sym) == SYMBOL_REF
-	  || GET_CODE (sym) == LABEL_REF)
+      /* As we are processing a PLUS, do not unwrap RELOC32 symbols --
+	 they are invalid in this context.  */
+      if (m68k_unwrap_symbol (XEXP (x, 1), false) != XEXP (x, 1))
 	{
 	  address->base = XEXP (x, 0);
 	  address->offset = XEXP (x, 1);
@@ -2173,7 +2133,6 @@ enum m68k_reloc { RELOC_GOT, RELOC_TLSGD
 #define TLS_RELOC_P(RELOC) ((RELOC) != RELOC_GOT)
 
 /* Wrap symbol X into unspec representing relocation RELOC.
-   If USE_X_P, use 32-bit relocations, otherwise use 16-bit relocs.
    BASE_REG - register that should be added to the result.
    TEMP_REG - if non-null, temporary register.  */
 
@@ -2208,16 +2167,9 @@ m68k_wrap_symbol (rtx x, enum m68k_reloc
     }
   else
     {
-      /* ??? It would be simplier to wrap 16-bit GOT relocs into UNSPEC too,
-	 historically, we don't do this, but I'm not aware of any downside
-	 of such a change.  */
-      if (reloc != RELOC_GOT)
-	/* Wrap X into (const (unspec (X))).  */
-	{
-	  x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)),
-			      UNSPEC_RELOC16);
-	  x = gen_rtx_CONST (Pmode, x);
-	}
+      x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)),
+			  UNSPEC_RELOC16);
+      x = gen_rtx_CONST (Pmode, x);
 
       x = gen_rtx_PLUS (Pmode, base_reg, x);
     }
@@ -2225,6 +2177,127 @@ m68k_wrap_symbol (rtx x, enum m68k_reloc
   return x;
 }
 
+/* Helper for m68k_unwrap_symbol.
+   Also, if unwrapping was successful (that is if (ORIG != <return value>)),
+   sets *RELOC_PTR to relocation type for the symbol.  */
+
+static rtx
+m68k_unwrap_symbol_1 (rtx orig, bool unwrap_reloc32_p,
+		      enum m68k_reloc *reloc_ptr)
+{
+  if (GET_CODE (orig) == CONST)
+    {
+      rtx x;
+      enum m68k_reloc dummy;
+
+      x = XEXP (orig, 0);
+
+      if (reloc_ptr == NULL)
+	reloc_ptr = &dummy;
+
+      /* Handle an addend.  */
+      if ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS)
+	  && CONST_INT_P (XEXP (x, 1)))
+	x = XEXP (x, 0);
+
+      if (GET_CODE (x) == UNSPEC)
+	{
+	  switch (XINT (x, 1))
+	    {
+	    case UNSPEC_RELOC16:
+	      orig = XVECEXP (x, 0, 0);
+	      *reloc_ptr = (enum m68k_reloc) INTVAL (XVECEXP (x, 0, 1));
+	      break;
+
+	    case UNSPEC_RELOC32:
+	      if (unwrap_reloc32_p)
+		{
+		  orig = XVECEXP (x, 0, 0);
+		  *reloc_ptr = (enum m68k_reloc) INTVAL (XVECEXP (x, 0, 1));
+		}
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+    }
+
+  return orig;
+}
+
+/* Unwrap symbol from UNSPEC_RELOC16 and, if unwrap_reloc32_p,
+   UNSPEC_RELOC32 wrappers.  */
+
+rtx
+m68k_unwrap_symbol (rtx orig, bool unwrap_reloc32_p)
+{
+  return m68k_unwrap_symbol_1 (orig, unwrap_reloc32_p, NULL);
+}
+
+/* Prescan insn before outputing assembler for it.  */
+
+void
+m68k_final_prescan_insn (rtx insn ATTRIBUTE_UNUSED,
+			 rtx *operands, int n_operands)
+{
+  int i;
+
+  /* Combine and, possibly, other optimizations may do good job
+     converting
+       (const (unspec [(symbol)]))
+     into
+       (const (plus (unspec [(symbol)])
+                    (const_int N))).
+     The problem with this is emitting @TLS or @GOT decorations.
+     The decoration is emitted when processing (unspec), so the
+     result would be "#symbol@TLSLE+N" instead of "#symbol+N@TLSLE".
+
+     It seems that the easiest solution to this is to convert such
+     operands to
+       (const (unspec [(plus (symbol)
+                             (const_int N))])).
+     Note, that the top level of operand remains intact, so we don't have
+     to patch up anything outside of the operand.  */
+
+  for (i = 0; i < n_operands; ++i)
+    {
+      rtx op;
+
+      op = operands[i];
+
+      if (m68k_unwrap_symbol (op, true) != op)
+	{
+	  rtx plus;
+
+	  gcc_assert (GET_CODE (op) == CONST);
+	  plus = XEXP (op, 0);
+
+	  if (GET_CODE (plus) == PLUS || GET_CODE (plus) == MINUS)
+	    {
+	      rtx unspec;
+	      rtx addend;
+
+	      unspec = XEXP (plus, 0);
+	      gcc_assert (GET_CODE (unspec) == UNSPEC);
+	      addend = XEXP (plus, 1);
+	      gcc_assert (CONST_INT_P (addend));
+
+	      /* We now have all the pieces, rearrange them.  */
+
+	      /* Move symbol to plus.  */
+	      XEXP (plus, 0) = XVECEXP (unspec, 0, 0);
+
+	      /* Move plus inside unspec.  */
+	      XVECEXP (unspec, 0, 0) = plus;
+
+	      /* Move unspec to top level of const.  */
+	      XEXP (op, 0) = unspec;
+	    }
+	}
+    }
+}
+
 /* Move X to a register and add REG_EQUAL note pointing to ORIG.
    If REG is non-null, use it; generate new pseudo otherwise.  */
 
@@ -2320,11 +2393,6 @@ legitimize_pic_address (rtx orig, enum m
       rtx base;
 
       /* Make sure this has not already been legitimized.  */
-      if (GET_CODE (XEXP (orig, 0)) == PLUS
-	  && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
-	return orig;
-
-      /* Handle the case where we have: const (UNSPEC_RELOC??).  */
       if (m68k_unwrap_symbol (orig, true) != orig)
 	return orig;
 
@@ -2555,49 +2623,37 @@ m68k_tls_symbol_p (rtx x)
 /* Helper for m68k_tls_referenced_p.  */
 
 static int
-m68k_tls_referenced_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+m68k_tls_reference_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
 {
+  /* Note: this is not the same as m68k_tls_symbol_p.  */
   if (GET_CODE (*x) == SYMBOL_REF)
-    return SYMBOL_REF_TLS_MODEL (*x) != 0;
+    return SYMBOL_REF_TLS_MODEL (*x) != 0 ? 1 : 0;
 
-  /* Don't recurse into UNSPEC_TLS looking for TLS symbols; these are
-     TLS offsets, not real symbol references.  */
-  if (GET_CODE (*x) == UNSPEC
-      && (XINT (*x, 1) == UNSPEC_RELOC16 || XINT (*x, 1) == UNSPEC_RELOC32)
-      && TLS_RELOC_P (INTVAL (XVECEXP (*x, 0, 1))))
+  /* Don't recurse into legitimate TLS references.  */
+  if (m68k_tls_reference_p (*x, true))
     return -1;
 
   return 0;
 }
 
-/* Return true if X contains any TLS symbol references.  */
+/* If !LEGITIMATE_P, return true if X is a TLS symbol reference,
+   though illegitimate one.
+   If LEGITIMATE_P, return true if X is a legitimate TLS symbol reference.  */
 
 bool
-m68k_tls_referenced_p (rtx x)
+m68k_tls_reference_p (rtx x, bool legitimate_p)
 {
   if (!TARGET_HAVE_TLS)
     return false;
 
-  return for_each_rtx (&x, m68k_tls_referenced_p_1, NULL);
-}
-
-/* Return true if X is legitimate TLS symbol reference.  */
-
-bool
-m68k_tls_mentioned_p (rtx x)
-{
-  switch (GET_CODE (x))
+  if (!legitimate_p)
+    return for_each_rtx (&x, m68k_tls_reference_p_1, NULL) == 1 ? true : false;
+  else
     {
-    case CONST:
-      return m68k_tls_mentioned_p (XEXP (x, 0));
+      enum m68k_reloc reloc = RELOC_GOT;
 
-    case UNSPEC:
-      if ((XINT (x, 1) == UNSPEC_RELOC16 || XINT (x, 1) == UNSPEC_RELOC32)
-	  && TLS_RELOC_P (INTVAL (XVECEXP (x, 0, 1))))
-	return 1;
-
-    default:
-      return 0;
+      return (m68k_unwrap_symbol_1 (x, true, &reloc) != x
+	      && TLS_RELOC_P (reloc));
     }
 }
 
@@ -4383,11 +4439,10 @@ print_operand (FILE *file, rtx op, int l
   else
     {
       /* Use `print_operand_address' instead of `output_addr_const'
-	 to ensure that we print relevant PIC or TLS stuff.  */
+	 to ensure that we print relevant PIC stuff.  */
       asm_fprintf (file, "%I");
-      if ((TARGET_PCREL
-	   && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST))
-	  || m68k_unwrap_symbol (op, true) != op)
+      if (TARGET_PCREL
+	  && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST))
 	print_operand_address (file, op);
       else
 	output_addr_const (file, op);
@@ -4399,10 +4454,34 @@ print_operand (FILE *file, rtx op, int l
 static const char *
 m68k_get_reloc_decoration (enum m68k_reloc reloc)
 {
+  /* To my knowledge, !MOTOROLA assemblers don't support TLS.  */
+  gcc_assert (MOTOROLA || reloc == RELOC_GOT);
+
   switch (reloc)
     {
     case RELOC_GOT:
-      return "@GOT";
+      if (MOTOROLA)
+	{
+	  if (flag_pic == 1 && TARGET_68020)
+	    return "@GOT.w";
+	  else
+	    return "@GOT";
+	}
+      else
+	{
+	  if (TARGET_68020)
+	    {
+	      switch (flag_pic)
+		{
+		case 1:
+		  return ":w";
+		case 2:
+		  return ":l";
+		default:
+		  return "";
+		}
+	    }
+	}
 
     case RELOC_TLSGD:
       return "@TLSGD";
@@ -4433,10 +4512,6 @@ m68k_output_addr_const_extra (FILE *file
     {
       switch (XINT (x, 1))
 	{
-	  /* ??? It would be cleaner to wrap normal GOT references into
-	     UNSPEC_GOT too, then we won't have to handle them separately
-	     in print_operand_address.  I'm not aware of any downside of
-	     such clean up.  */
 	case UNSPEC_RELOC16:
 	case UNSPEC_RELOC32:
 	  output_addr_const (file, XVECEXP (x, 0, 0));
@@ -4549,17 +4624,8 @@ print_operand_address (FILE *file, rtx a
 	  else
 	    {
 	      if (address.offset)
-		{
-		  output_addr_const (file, address.offset);
-		  if (flag_pic && address.base == pic_offset_table_rtx
-		      && (m68k_unwrap_symbol (address.offset, false)
-			  == address.offset))
-		    {
-		      fprintf (file, "@GOT");
-		      if (flag_pic == 1 && TARGET_68020)
-			fprintf (file, ".w");
-		    }
-		}
+		output_addr_const (file, address.offset);
+
 	      putc ('(', file);
 	      if (address.base)
 		fputs (M68K_REGNAME (REGNO (address.base)), file);
@@ -4592,19 +4658,7 @@ print_operand_address (FILE *file, rtx a
 		    fputs (M68K_REGNAME (REGNO (address.base)), file);
 		  fprintf (file, "@(");
 		  if (address.offset)
-		    {
-		      output_addr_const (file, address.offset);
-		      if (address.base == pic_offset_table_rtx && TARGET_68020)
-			switch (flag_pic)
-			  {
-			  case 1:
-			    fprintf (file, ":w"); break;
-			  case 2:
-			    fprintf (file, ":l"); break;
-			  default:
-			    break;
-			  }
-		    }
+		    output_addr_const (file, address.offset);
 		}
 	      /* Print the ",index" component, if any.  */
 	      if (address.index)
diff -rup -x .svn ./gcc-mainline-2/gcc/config/m68k/m68k.h ./gcc-mainline/gcc/config/m68k/m68k.h
--- ./gcc-mainline-2/gcc/config/m68k/m68k.h	2009-05-15 08:43:46.000000000 -0700
+++ ./gcc-mainline/gcc/config/m68k/m68k.h	2009-05-15 03:56:33.000000000 -0700
@@ -750,7 +750,7 @@ __transfer_from_trampoline ()					\
 #define LEGITIMATE_PIC_OPERAND_P(X)				\
   (!symbolic_operand (X, VOIDmode)				\
    || (TARGET_PCREL && REG_STRICT_P)				\
-   || m68k_tls_mentioned_p (X))
+   || m68k_tls_reference_p (X, true))
 
 #define REG_OK_FOR_BASE_P(X) \
   m68k_legitimate_base_reg_p (X, REG_STRICT_P)
@@ -975,6 +975,9 @@ do { if (cc_prev_status.flags & CC_IN_68
   assemble_name ((FILE), (NAME)),		\
   fprintf ((FILE), ",%u\n", (int)(ROUNDED)))
 
+#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \
+  m68k_final_prescan_insn (INSN, OPVEC, NOPERANDS)
+
 /* On the 68000, we use several CODE characters:
    '.' for dot needed in Motorola-style opcode names.
    '-' for an operand pushing on the stack:
diff -rup -x .svn ./gcc-mainline-2/gcc/config/m68k/m68k.md ./gcc-mainline/gcc/config/m68k/m68k.md
--- ./gcc-mainline-2/gcc/config/m68k/m68k.md	2009-05-15 08:43:46.000000000 -0700
+++ ./gcc-mainline/gcc/config/m68k/m68k.md	2009-05-15 03:58:54.000000000 -0700
@@ -872,7 +872,7 @@
 
   /* Recognize the case where operand[1] is a reference to thread-local
      data and load its address to a register.  */
-  if (!TARGET_PCREL && m68k_tls_referenced_p (operands[1]))
+  if (!TARGET_PCREL && m68k_tls_reference_p (operands[1], false))
     {
       rtx tmp = operands[1];
       rtx addend = NULL;
diff -rup -x .svn ./gcc-mainline-2/gcc/config/m68k/m68k-protos.h ./gcc-mainline/gcc/config/m68k/m68k-protos.h
--- ./gcc-mainline-2/gcc/config/m68k/m68k-protos.h	2009-05-15 08:43:46.000000000 -0700
+++ ./gcc-mainline/gcc/config/m68k/m68k-protos.h	2009-05-15 03:56:06.000000000 -0700
@@ -61,14 +61,14 @@ extern bool m68k_matches_q_p (rtx);
 extern bool m68k_matches_u_p (rtx);
 extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx);
 extern rtx m68k_legitimize_tls_address (rtx);
-extern bool m68k_tls_referenced_p (rtx);
-extern bool m68k_tls_mentioned_p (rtx);
+extern bool m68k_tls_reference_p (rtx, bool);
 extern int valid_dbcc_comparison_p_2 (rtx, enum machine_mode);
 extern rtx m68k_libcall_value (enum machine_mode);
 extern rtx m68k_function_value (const_tree, const_tree);
 extern int emit_move_sequence (rtx *, enum machine_mode, rtx);
 extern bool m68k_movem_pattern_p (rtx, rtx, HOST_WIDE_INT, bool);
 extern const char *m68k_output_movem (rtx *, rtx, HOST_WIDE_INT, bool);
+extern void m68k_final_prescan_insn (rtx, rtx *, int);
 
 /* Functions from m68k.c used in constraints.md.  */
 extern rtx m68k_unwrap_symbol (rtx, bool);
2009-01-23  Maxim Kuvyrkov  <maxim@codesourcery.com>

	M68K TLS support.
	
	gcc/
	* configure.ac (m68k-*-*): Check if binutils support TLS.
	* configure: Regenerate.
	* config/m68k/predicates.md (symbolic_operand): Extend comment.
	* config/m68k/constraints.md (Cu): New constraint.
	* config/m68k/m68k.md (UNSPEC_GOTOFF): Remove.
	(UNSPEC_RELOC16, UNSPEC_RELOC32): New constants.
	(movsi): Handle TLS symbols.
	(addsi3_5200): Handle XTLS symbols, indent.
	* config/m68k/m68k-protos.h (m68k_legitimize_tls_address): Declare.
	(m68k_tls_reference_p): Declare.
	(m68k_legitimize_address): Declare.
	(m68k_unwrap_symbol): Declare.
	* config/m68k/m68k.opt (mxtls): New option.
	* config/m68k/m68k.c (ggc.h): Include.
	(m68k_output_dwarf_dtprel): Implement hook.
	(TARGET_HAVE_TLS, TARGET_ASM_OUTPUT_DWARF_DTPREL): Define.
	(m68k_expand_prologue): Load GOT pointer when function needs it.
	(m68k_illegitimate_symbolic_constant_p): Handle TLS symbols.
	(m68k_legitimate_constant_address_p): Same.
	(m68k_decompose_address): Handle TLS references.
	(m68k_get_gp): New static function.
	(enum m68k_reloc): New contants.
	(TLS_RELOC_P): New macro.
	(m68k_wrap_symbol): New static function.
	(m68k_unwrap_symbol, m68k_final_prescan_insn): New functions.
	(m68k_move_to_reg, m68k_wrap_symbol_into_got_ref): New static
	functions.
	(legitimize_pic_address): Handle TLS references..
	(m68k_tls_get_addr, m68k_get_tls_get_addr)
	(m68k_libcall_value_in_a0_p)
	(m68k_call_tls_get_addr, m68k_read_tp, m68k_get_m68k_read_tp)
	(m68k_call_m68k_read_tp): Helper variables and functions for ...
	(m68k_legitimize_tls_address): Handle TLS references.
	(m68k_tls_symbol_p, m68k_tls_reference_p_1, m68k_tls_reference_p):
	New functions.
	(m68k_legitimize_address): Handle TLS symbols.
	(m68k_get_reloc_decoration): New static function.
	(m68k_output_addr_const_extra): Handle UNSPEC_RELOC16 and
	UNSPEC_RELOC32.
	(m68k_output_dwarf_dtprel): Implement hook.
	(print_operand_address): Handle UNSPEC_RELOC16 adn UNSPEC_RELOC32.
	(m68k_libcall_value): Return result in A0 instead of D0 when asked by
	m68k_call_* routines.
	(sched_attr_op_type): Handle TLS symbols.
	(gt-m68k.h): Include.
	* config/m68k/m68k.h (FINAL_PRESCAN_INSN): Define.
	(LEGITIMATE_PIC_OPERAND_P): Support TLS.

	gcc/testsuite/
	* gcc.target/m68k/tls-ie.c: New test.
	* gcc.target/m68k/tls-le.c: New test.
	* gcc.target/m68k/tls-gd.c: New test.
	* gcc.target/m68k/tls-ld.c: New test.
	* gcc.target/m68k/tls-ie-xgot.c: New test.
	* gcc.target/m68k/tls-le-xtls.c: New test.
	* gcc.target/m68k/tls-gd-xgot.c: New test.
	* gcc.target/m68k/tls-ld-xgot.c: New test.
	* gcc.target/m68k/tls-ld-xtls.c: New test.
	* gcc.target/m68k/tls-ld-xgot-xtls.c: New test.
Index: gcc-mainline/gcc/configure
===================================================================
--- gcc-mainline/gcc/configure	(revision 147467)
+++ gcc-mainline/gcc/configure	(working copy)
@@ -21984,6 +21984,22 @@ x:
 	tls_first_minor=16
 	tls_as_opt='-32 --fatal-warnings'
 	;;
+  m68k-*-*)
+    conftest_s='
+	.section .tdata,"awT",@progbits
+x:
+	.word 2
+	.text
+foo:
+	move.l x@TLSGD(%a5),%a0
+	move.l x@TLSLDM(%a5),%a0
+	move.l x@TLSLDO(%a5),%a0
+	move.l x@TLSIE(%a5),%a0
+	move.l x@TLSLE(%a5),%a0'
+	tls_first_major=2
+	tls_first_minor=19
+	tls_as_opt='--fatal-warnings'
+	;;
   powerpc-*-*)
     conftest_s='
 	.section ".tdata","awT",@progbits
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld.c	(revision 0)
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -fpic" } */
+/* { dg-final { scan-assembler "foo@TLSLDM\\(\%a5\\)" } } */
+/* { dg-final { scan-assembler "bsr.l __tls_get_addr@PLTPC" } } */
+/* { dg-final { scan-assembler "lea \\(foo@TLSLDO,\%a0\\)" } } */
+
+static int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-le.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-le.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-le.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "jsr __m68k_read_tp" } } */
+/* { dg-final { scan-assembler "lea \\(foo@TLSLE,\%a0\\)" } } */
+
+static int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xgot-xtls.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xgot-xtls.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xgot-xtls.c	(revision 0)
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -fpic -mxgot -mxtls" } */
+/* { dg-final { scan-assembler "#foo@TLSLDM,\%\[ad\]\[0-7\]" } } */
+/* { dg-final { scan-assembler "bsr.l __tls_get_addr@PLTPC" } } */
+/* { dg-final { scan-assembler "#foo@TLSLDO,\%\[ad\]\[0-7\]" } } */
+
+static int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-gd-xgot.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-gd-xgot.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-gd-xgot.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -fpic -mxgot" } */
+/* { dg-final { scan-assembler "#foo@TLSGD,\%\[ad\]\[0-7\]" } } */
+/* { dg-final { scan-assembler "bsr.l __tls_get_addr@PLTPC" } } */
+
+extern int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ie-xgot.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ie-xgot.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ie-xgot.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -mxgot" } */
+/* { dg-final { scan-assembler "jsr __m68k_read_tp" } } */
+/* { dg-final { scan-assembler "#foo@TLSIE,\%\[ad\]\[0-7\]" } } */
+
+extern int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xgot.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xgot.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xgot.c	(revision 0)
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -fpic -mxgot" } */
+/* { dg-final { scan-assembler "#foo@TLSLDM,\%\[ad\]\[0-7\]" } } */
+/* { dg-final { scan-assembler "bsr.l __tls_get_addr@PLTPC" } } */
+/* { dg-final { scan-assembler "lea \\(foo@TLSLDO,\%a0\\)" } } */
+
+static int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xtls.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xtls.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ld-xtls.c	(revision 0)
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -fpic -mxtls" } */
+/* { dg-final { scan-assembler "foo@TLSLDM\\(\%a5\\)" } } */
+/* { dg-final { scan-assembler "bsr.l __tls_get_addr@PLTPC" } } */
+/* { dg-final { scan-assembler "#foo@TLSLDO,\%\[ad\]\[0-7\]" } } */
+
+static int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-le-xtls.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-le-xtls.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-le-xtls.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -mxtls" } */
+/* { dg-final { scan-assembler "jsr __m68k_read_tp" } } */
+/* { dg-final { scan-assembler "#foo@TLSLE,\%\[ad\]\[0-7\]" } } */
+
+static int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-gd.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-gd.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-gd.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2 -fpic" } */
+/* { dg-final { scan-assembler "foo@TLSGD\\(\%a5\\)" } } */
+/* { dg-final { scan-assembler "bsr.l __tls_get_addr@PLTPC" } } */
+
+extern int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ie.c
===================================================================
--- gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ie.c	(revision 0)
+++ gcc-mainline/gcc/testsuite/gcc.target/m68k/tls-ie.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { ! *-linux-* } { "*" } { "" } } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "jsr __m68k_read_tp" } } */
+/* { dg-final { scan-assembler "foo@TLSIE\\(\%a5\\)" } } */
+
+extern int __thread foo;
+
+int *
+bar (void)
+{
+  return &foo;
+}
Index: gcc-mainline/gcc/configure.ac
===================================================================
--- gcc-mainline/gcc/configure.ac	(revision 147467)
+++ gcc-mainline/gcc/configure.ac	(working copy)
@@ -2570,6 +2570,22 @@ x:
 	tls_first_minor=16
 	tls_as_opt='-32 --fatal-warnings'
 	;;
+  m68k-*-*)
+    conftest_s='
+	.section .tdata,"awT",@progbits
+x:
+	.word 2
+	.text
+foo:
+	move.l x@TLSGD(%a5),%a0
+	move.l x@TLSLDM(%a5),%a0
+	move.l x@TLSLDO(%a5),%a0
+	move.l x@TLSIE(%a5),%a0
+	move.l x@TLSLE(%a5),%a0'
+	tls_first_major=2
+	tls_first_minor=19
+	tls_as_opt='--fatal-warnings'
+	;;
   powerpc-*-*)
     conftest_s='
 	.section ".tdata","awT",@progbits
Index: gcc-mainline/gcc/config/m68k/predicates.md
===================================================================
--- gcc-mainline/gcc/config/m68k/predicates.md	(revision 147467)
+++ gcc-mainline/gcc/config/m68k/predicates.md	(working copy)
@@ -135,7 +135,9 @@
   (match_code "sign_extend,zero_extend"))
 
 ;; Returns true if OP is either a symbol reference or a sum of a
-;; symbol reference and a constant.
+;; symbol reference and a constant.  This predicate is for "raw"
+;; symbol references not yet processed by legitimize*_address,
+;; hence we do not handle UNSPEC_{XGOT, TLS, XTLS} here.
 
 (define_predicate "symbolic_operand"
   (match_code "symbol_ref,label_ref,const")
Index: gcc-mainline/gcc/config/m68k/m68k.md
===================================================================
--- gcc-mainline/gcc/config/m68k/m68k.md	(revision 147467)
+++ gcc-mainline/gcc/config/m68k/m68k.md	(working copy)
@@ -116,7 +116,8 @@
    (UNSPEC_GOT 3)
    (UNSPEC_IB 4)
    (UNSPEC_TIE 5)
-   (UNSPEC_GOTOFF 6)
+   (UNSPEC_RELOC16 6)
+   (UNSPEC_RELOC32 7)
   ])
 
 ;; UNSPEC_VOLATILE usage:
@@ -869,7 +870,41 @@
 {
   rtx tmp, base, offset;
 
-  if (flag_pic && !TARGET_PCREL && symbolic_operand (operands[1], SImode))
+  /* Recognize the case where operand[1] is a reference to thread-local
+     data and load its address to a register.  */
+  if (!TARGET_PCREL && m68k_tls_reference_p (operands[1], false))
+    {
+      rtx tmp = operands[1];
+      rtx addend = NULL;
+
+      if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS)
+        {
+          addend = XEXP (XEXP (tmp, 0), 1);
+          tmp = XEXP (XEXP (tmp, 0), 0);
+        }
+
+      gcc_assert (GET_CODE (tmp) == SYMBOL_REF);
+      gcc_assert (SYMBOL_REF_TLS_MODEL (tmp) != 0);
+
+      tmp = m68k_legitimize_tls_address (tmp);
+
+      if (addend)
+        {
+	  if (!REG_P (tmp))
+	    {
+	      rtx reg;
+
+	      reg = gen_reg_rtx (Pmode);
+	      emit_move_insn (reg, tmp);
+	      tmp = reg;
+	    }
+
+          tmp = gen_rtx_PLUS (SImode, tmp, addend);
+	}
+
+      operands[1] = tmp;
+    }
+  else if (flag_pic && !TARGET_PCREL && symbolic_operand (operands[1], SImode))
     {
       /* The source is an address which requires PIC relocation.
          Call legitimize_pic_address with the source, mode, and a relocation
@@ -2428,9 +2463,9 @@
   "* return output_addsi3 (operands);")
 
 (define_insn_and_split "*addsi3_5200"
-  [(set (match_operand:SI 0 "nonimmediate_operand"         "=mr,mr,a,m,r,  ?a, ?a,?a,?a")
-	(plus:SI (match_operand:SI 1 "general_operand"     "%0, 0, 0,0,0,   a,  a, r, a")
-		 (match_operand:SI 2 "general_src_operand" " I, L, J,d,mrKi,Cj, r, a, J")))]
+  [(set (match_operand:SI 0 "nonimmediate_operand"         "=mr,mr,a,  m,r,  ?a, ?a,?a,?a")
+	(plus:SI (match_operand:SI 1 "general_operand"     "%0, 0, 0,  0,0,   a,  a, r, a")
+		 (match_operand:SI 2 "general_src_operand" " I, L, JCu,d,mrKi,Cj, r, a, JCu")))]
   "TARGET_COLDFIRE"
 {
   switch (which_alternative)
@@ -2472,9 +2507,9 @@
 	(plus:SI (match_dup 0)
 		 (match_dup 1)))]
   ""
-  [(set_attr "type" "aluq_l,aluq_l,lea,alu_l,alu_l,*,lea,lea,lea")
-   (set_attr "opy" "2,2,*,2,2,*,*,*,*")
-   (set_attr "opy_type" "*,*,mem5,*,*,*,mem6,mem6,mem5")])
+  [(set_attr "type"     "aluq_l,aluq_l,lea, alu_l,alu_l,*,lea, lea, lea")
+   (set_attr "opy"      "2,     2,     *,   2,    2,    *,*,   *,   *")
+   (set_attr "opy_type" "*,     *,     mem5,*,    *,    *,mem6,mem6,mem5")])
 
 (define_insn ""
   [(set (match_operand:SI 0 "nonimmediate_operand" "=a")
Index: gcc-mainline/gcc/config/m68k/m68k-protos.h
===================================================================
--- gcc-mainline/gcc/config/m68k/m68k-protos.h	(revision 147467)
+++ gcc-mainline/gcc/config/m68k/m68k-protos.h	(working copy)
@@ -60,13 +60,20 @@ extern bool m68k_legitimate_address_p (e
 extern bool m68k_matches_q_p (rtx);
 extern bool m68k_matches_u_p (rtx);
 extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx);
+extern rtx m68k_legitimize_tls_address (rtx);
+extern bool m68k_tls_reference_p (rtx, bool);
 extern int valid_dbcc_comparison_p_2 (rtx, enum machine_mode);
 extern rtx m68k_libcall_value (enum machine_mode);
 extern rtx m68k_function_value (const_tree, const_tree);
 extern int emit_move_sequence (rtx *, enum machine_mode, rtx);
 extern bool m68k_movem_pattern_p (rtx, rtx, HOST_WIDE_INT, bool);
 extern const char *m68k_output_movem (rtx *, rtx, HOST_WIDE_INT, bool);
+extern void m68k_final_prescan_insn (rtx, rtx *, int);
 
+/* Functions from m68k.c used in constraints.md.  */
+extern rtx m68k_unwrap_symbol (rtx, bool);
+
+/* Functions from m68k.c used in genattrtab.  */
 #ifdef HAVE_ATTR_cpu
 extern enum attr_cpu m68k_sched_cpu;
 extern enum attr_mac m68k_sched_mac;
Index: gcc-mainline/gcc/config/m68k/m68k.opt
===================================================================
--- gcc-mainline/gcc/config/m68k/m68k.opt	(revision 147467)
+++ gcc-mainline/gcc/config/m68k/m68k.opt	(working copy)
@@ -182,3 +182,7 @@ Tune for the specified target CPU or arc
 mxgot
 Target Report Mask(XGOT)
 Support more than 8192 GOT entries on ColdFire
+
+mxtls
+Target Report Mask(XTLS)
+Support TLS segment larger than 64K
Index: gcc-mainline/gcc/config/m68k/m68k.c
===================================================================
--- gcc-mainline/gcc/config/m68k/m68k.c	(revision 147467)
+++ gcc-mainline/gcc/config/m68k/m68k.c	(working copy)
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  
 /* ??? Need to add a dependency between m68k.o and sched-int.h.  */
 #include "sched-int.h"
 #include "insn-codes.h"
+#include "ggc.h"
 
 enum reg_class regno_reg_class[] =
 {
@@ -143,11 +144,13 @@ static tree m68k_handle_fndecl_attribute
 static void m68k_compute_frame_layout (void);
 static bool m68k_save_reg (unsigned int regno, bool interrupt_handler);
 static bool m68k_ok_for_sibcall_p (tree, tree);
+static bool m68k_tls_symbol_p (rtx);
 static rtx m68k_legitimize_address (rtx, rtx, enum machine_mode);
 static bool m68k_rtx_costs (rtx, int, int, int *, bool);
 #if M68K_HONOR_TARGET_STRICT_ALIGNMENT
 static bool m68k_return_in_memory (const_tree, const_tree);
 #endif
+static void m68k_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 
 
 /* Specify the identification number of the library being built */
@@ -248,6 +251,14 @@ const char *m68k_library_id_string = "_c
 #define TARGET_RETURN_IN_MEMORY m68k_return_in_memory
 #endif
 
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS (true)
+
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL m68k_output_dwarf_dtprel
+#endif
+
 static const struct attribute_spec m68k_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
@@ -1145,8 +1156,7 @@ m68k_expand_prologue (void)
 			    current_frame.reg_mask, true, true));
     }
 
-  if (flag_pic
-      && !TARGET_SEP_DATA
+  if (!TARGET_SEP_DATA
       && crtl->uses_pic_offset_table)
     insn = emit_insn (gen_load_got (pic_offset_table_rtx));
 }
@@ -1431,6 +1441,9 @@ m68k_legitimize_sibcall_address (rtx x)
 rtx
 m68k_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
 {
+  if (m68k_tls_symbol_p (x))
+    return m68k_legitimize_tls_address (x);
+
   if (GET_CODE (x) == PLUS)
     {
       int ch = (x) != (oldx);
@@ -1849,7 +1862,7 @@ m68k_illegitimate_symbolic_constant_p (r
 	  && !offset_within_block_p (base, INTVAL (offset)))
 	return true;
     }
-  return false;
+  return m68k_tls_reference_p (x, false);
 }
 
 /* Return true if X is a legitimate constant address that can reach
@@ -1877,7 +1890,7 @@ m68k_legitimate_constant_address_p (rtx 
 	return false;
     }
 
-  return true;
+  return !m68k_tls_reference_p (x, false);
 }
 
 /* Return true if X is a LABEL_REF for a jump table.  Assume that unplaced
@@ -1944,15 +1957,17 @@ m68k_decompose_address (enum machine_mod
   /* Check for GOT loads.  These are (bd,An,Xn) addresses if
      TARGET_68020 && flag_pic == 2, otherwise they are (d16,An)
      addresses.  */
-  if (flag_pic
-      && GET_CODE (x) == PLUS
-      && XEXP (x, 0) == pic_offset_table_rtx
-      && (GET_CODE (XEXP (x, 1)) == SYMBOL_REF
-	  || GET_CODE (XEXP (x, 1)) == LABEL_REF))
+  if (GET_CODE (x) == PLUS
+      && XEXP (x, 0) == pic_offset_table_rtx)
     {
-      address->base = XEXP (x, 0);
-      address->offset = XEXP (x, 1);
-      return true;
+      /* As we are processing a PLUS, do not unwrap RELOC32 symbols --
+	 they are invalid in this context.  */
+      if (m68k_unwrap_symbol (XEXP (x, 1), false) != XEXP (x, 1))
+	{
+	  address->base = XEXP (x, 0);
+	  address->offset = XEXP (x, 1);
+	  return true;
+	}
     }
 
   /* The ColdFire FPU only accepts addressing modes 2-5.  */
@@ -2097,6 +2112,228 @@ m68k_matches_u_p (rtx x)
 	  && !address.index);
 }
 
+/* Return GOT pointer.  */
+
+static rtx
+m68k_get_gp (void)
+{
+  if (pic_offset_table_rtx == NULL_RTX)
+    pic_offset_table_rtx = gen_rtx_REG (Pmode, PIC_REG);
+
+  crtl->uses_pic_offset_table = 1;
+
+  return pic_offset_table_rtx;
+}
+
+/* M68K relocations, used to distinguish GOT and TLS relocations in UNSPEC
+   wrappers.  */
+enum m68k_reloc { RELOC_GOT, RELOC_TLSGD, RELOC_TLSLDM, RELOC_TLSLDO,
+		  RELOC_TLSIE, RELOC_TLSLE };
+
+#define TLS_RELOC_P(RELOC) ((RELOC) != RELOC_GOT)
+
+/* Wrap symbol X into unspec representing relocation RELOC.
+   BASE_REG - register that should be added to the result.
+   TEMP_REG - if non-null, temporary register.  */
+
+static rtx
+m68k_wrap_symbol (rtx x, enum m68k_reloc reloc, rtx base_reg, rtx temp_reg)
+{
+  bool use_x_p;
+
+  use_x_p = (base_reg == pic_offset_table_rtx) ? TARGET_XGOT : TARGET_XTLS;
+
+  if (TARGET_COLDFIRE && use_x_p)
+    /* When compiling with -mx{got, tls} switch the code will look like this:
+
+       move.l <X>@<RELOC>,<TEMP_REG>
+       add.l <BASE_REG>,<TEMP_REG>  */
+    {
+      /* Wrap X in UNSPEC_??? to tip m68k_output_addr_const_extra
+	 to put @RELOC after reference.  */
+      x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)),
+			  UNSPEC_RELOC32);
+      x = gen_rtx_CONST (Pmode, x);
+
+      if (temp_reg == NULL)
+	{
+	  gcc_assert (can_create_pseudo_p ());
+	  temp_reg = gen_reg_rtx (Pmode);
+	}
+
+      emit_move_insn (temp_reg, x);
+      emit_insn (gen_addsi3 (temp_reg, temp_reg, base_reg));
+      x = temp_reg;
+    }
+  else
+    {
+      x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)),
+			  UNSPEC_RELOC16);
+      x = gen_rtx_CONST (Pmode, x);
+
+      x = gen_rtx_PLUS (Pmode, base_reg, x);
+    }
+
+  return x;
+}
+
+/* Helper for m68k_unwrap_symbol.
+   Also, if unwrapping was successful (that is if (ORIG != <return value>)),
+   sets *RELOC_PTR to relocation type for the symbol.  */
+
+static rtx
+m68k_unwrap_symbol_1 (rtx orig, bool unwrap_reloc32_p,
+		      enum m68k_reloc *reloc_ptr)
+{
+  if (GET_CODE (orig) == CONST)
+    {
+      rtx x;
+      enum m68k_reloc dummy;
+
+      x = XEXP (orig, 0);
+
+      if (reloc_ptr == NULL)
+	reloc_ptr = &dummy;
+
+      /* Handle an addend.  */
+      if ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS)
+	  && CONST_INT_P (XEXP (x, 1)))
+	x = XEXP (x, 0);
+
+      if (GET_CODE (x) == UNSPEC)
+	{
+	  switch (XINT (x, 1))
+	    {
+	    case UNSPEC_RELOC16:
+	      orig = XVECEXP (x, 0, 0);
+	      *reloc_ptr = (enum m68k_reloc) INTVAL (XVECEXP (x, 0, 1));
+	      break;
+
+	    case UNSPEC_RELOC32:
+	      if (unwrap_reloc32_p)
+		{
+		  orig = XVECEXP (x, 0, 0);
+		  *reloc_ptr = (enum m68k_reloc) INTVAL (XVECEXP (x, 0, 1));
+		}
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+    }
+
+  return orig;
+}
+
+/* Unwrap symbol from UNSPEC_RELOC16 and, if unwrap_reloc32_p,
+   UNSPEC_RELOC32 wrappers.  */
+
+rtx
+m68k_unwrap_symbol (rtx orig, bool unwrap_reloc32_p)
+{
+  return m68k_unwrap_symbol_1 (orig, unwrap_reloc32_p, NULL);
+}
+
+/* Prescan insn before outputing assembler for it.  */
+
+void
+m68k_final_prescan_insn (rtx insn ATTRIBUTE_UNUSED,
+			 rtx *operands, int n_operands)
+{
+  int i;
+
+  /* Combine and, possibly, other optimizations may do good job
+     converting
+       (const (unspec [(symbol)]))
+     into
+       (const (plus (unspec [(symbol)])
+                    (const_int N))).
+     The problem with this is emitting @TLS or @GOT decorations.
+     The decoration is emitted when processing (unspec), so the
+     result would be "#symbol@TLSLE+N" instead of "#symbol+N@TLSLE".
+
+     It seems that the easiest solution to this is to convert such
+     operands to
+       (const (unspec [(plus (symbol)
+                             (const_int N))])).
+     Note, that the top level of operand remains intact, so we don't have
+     to patch up anything outside of the operand.  */
+
+  for (i = 0; i < n_operands; ++i)
+    {
+      rtx op;
+
+      op = operands[i];
+
+      if (m68k_unwrap_symbol (op, true) != op)
+	{
+	  rtx plus;
+
+	  gcc_assert (GET_CODE (op) == CONST);
+	  plus = XEXP (op, 0);
+
+	  if (GET_CODE (plus) == PLUS || GET_CODE (plus) == MINUS)
+	    {
+	      rtx unspec;
+	      rtx addend;
+
+	      unspec = XEXP (plus, 0);
+	      gcc_assert (GET_CODE (unspec) == UNSPEC);
+	      addend = XEXP (plus, 1);
+	      gcc_assert (CONST_INT_P (addend));
+
+	      /* We now have all the pieces, rearrange them.  */
+
+	      /* Move symbol to plus.  */
+	      XEXP (plus, 0) = XVECEXP (unspec, 0, 0);
+
+	      /* Move plus inside unspec.  */
+	      XVECEXP (unspec, 0, 0) = plus;
+
+	      /* Move unspec to top level of const.  */
+	      XEXP (op, 0) = unspec;
+	    }
+	}
+    }
+}
+
+/* Move X to a register and add REG_EQUAL note pointing to ORIG.
+   If REG is non-null, use it; generate new pseudo otherwise.  */
+
+static rtx
+m68k_move_to_reg (rtx x, rtx orig, rtx reg)
+{
+  rtx insn;
+
+  if (reg == NULL_RTX)
+    {
+      gcc_assert (can_create_pseudo_p ());
+      reg = gen_reg_rtx (Pmode);
+    }
+
+  insn = emit_move_insn (reg, x);
+  /* Put a REG_EQUAL note on this insn, so that it can be optimized
+     by loop.  */
+  set_unique_reg_note (insn, REG_EQUAL, orig);
+
+  return reg;
+}
+
+/* Does the same as m68k_wrap_symbol, but returns a memory reference to
+   GOT slot.  */
+
+static rtx
+m68k_wrap_symbol_into_got_ref (rtx x, enum m68k_reloc reloc, rtx temp_reg)
+{
+  x = m68k_wrap_symbol (x, reloc, m68k_get_gp (), temp_reg);
+
+  x = gen_rtx_MEM (Pmode, x);
+  MEM_READONLY_P (x) = 1;
+
+  return x;
+}
+
 /* Legitimize PIC addresses.  If the address is already
    position-independent, we return ORIG.  Newly generated
    position-independent addresses go to REG.  If we need more
@@ -2148,42 +2385,15 @@ legitimize_pic_address (rtx orig, enum m
     {
       gcc_assert (reg);
 
-      if (TARGET_COLDFIRE && TARGET_XGOT)
-	/* When compiling with -mxgot switch the code for the above
-	   example will look like this:
-
-	   movel a5, a0
-	   addl _foo@GOT, a0
-	   movel a0@, a0
-	   movel #12345, a0@  */
-	{
-	  rtx pic_offset;
-
-	  /* Wrap ORIG in UNSPEC_GOTOFF to tip m68k_output_addr_const_extra
-	     to put @GOT after reference.  */
-	  pic_offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, orig),
-				       UNSPEC_GOTOFF);
-	  pic_offset = gen_rtx_CONST (Pmode, pic_offset);
-	  emit_move_insn (reg, pic_offset);
-	  emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx));
-	  pic_ref = gen_rtx_MEM (Pmode, reg);
-	}
-      else
-	pic_ref = gen_rtx_MEM (Pmode,
-			       gen_rtx_PLUS (Pmode,
-					     pic_offset_table_rtx, orig));
-      crtl->uses_pic_offset_table = 1;
-      MEM_READONLY_P (pic_ref) = 1;
-      emit_move_insn (reg, pic_ref);
-      return reg;
+      pic_ref = m68k_wrap_symbol_into_got_ref (orig, RELOC_GOT, reg);
+      pic_ref = m68k_move_to_reg (pic_ref, orig, reg);
     }
   else if (GET_CODE (orig) == CONST)
     {
       rtx base;
 
       /* Make sure this has not already been legitimized.  */
-      if (GET_CODE (XEXP (orig, 0)) == PLUS
-	  && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
+      if (m68k_unwrap_symbol (orig, true) != orig)
 	return orig;
 
       gcc_assert (reg);
@@ -2196,13 +2406,257 @@ legitimize_pic_address (rtx orig, enum m
 				     base == reg ? 0 : reg);
 
       if (GET_CODE (orig) == CONST_INT)
-	return plus_constant (base, INTVAL (orig));
-      pic_ref = gen_rtx_PLUS (Pmode, base, orig);
-      /* Likewise, should we set special REG_NOTEs here?  */
+	pic_ref = plus_constant (base, INTVAL (orig));
+      else
+	pic_ref = gen_rtx_PLUS (Pmode, base, orig);
     }
+
   return pic_ref;
 }
 
+/* The __tls_get_addr symbol.  */
+static GTY(()) rtx m68k_tls_get_addr;
+
+/* Return SYMBOL_REF for __tls_get_addr.  */
+
+static rtx
+m68k_get_tls_get_addr (void)
+{
+  if (m68k_tls_get_addr == NULL_RTX)
+    m68k_tls_get_addr = init_one_libfunc ("__tls_get_addr");
+
+  return m68k_tls_get_addr;
+}
+
+/* Return libcall result in A0 instead of usual D0.  */
+static bool m68k_libcall_value_in_a0_p = false;
+
+/* Emit instruction sequence that calls __tls_get_addr.  X is
+   the TLS symbol we are referencing and RELOC is the symbol type to use
+   (either TLSGD or TLSLDM).  EQV is the REG_EQUAL note for the sequence
+   emitted.  A pseudo register with result of __tls_get_addr call is
+   returned.  */
+
+static rtx
+m68k_call_tls_get_addr (rtx x, rtx eqv, enum m68k_reloc reloc)
+{
+  rtx a0;
+  rtx insns;
+  rtx dest;
+
+  /* Emit the call sequence.  */
+  start_sequence ();
+
+  /* FIXME: Unfortunately, emit_library_call_value does not
+     consider (plus (%a5) (const (unspec))) to be a good enough
+     operand for push, so it forces it into a register.  The bad
+     thing about this is that combiner, due to copy propagation and other
+     optimizations, sometimes can not later fix this.  As a consequence,
+     additional register may be allocated resulting in a spill.
+     For reference, see args processing loops in
+     calls.c:emit_library_call_value_1.
+     For testcase, see gcc.target/m68k/tls-{gd, ld}.c  */
+  x = m68k_wrap_symbol (x, reloc, m68k_get_gp (), NULL_RTX);
+
+  /* __tls_get_addr() is not a libcall, but emitting a libcall_value
+     is the simpliest way of generating a call.  The difference between
+     __tls_get_addr() and libcall is that the result is returned in D0
+     instead of A0.  To workaround this, we use m68k_libcall_value_in_a0_p
+     which temporarily switches returning the result to A0.  */ 
+
+  m68k_libcall_value_in_a0_p = true;
+  a0 = emit_library_call_value (m68k_get_tls_get_addr (), NULL_RTX, LCT_PURE,
+				Pmode, 1, x, Pmode);
+  m68k_libcall_value_in_a0_p = false;
+  
+  insns = get_insns ();
+  end_sequence ();
+
+  gcc_assert (can_create_pseudo_p ());
+  dest = gen_reg_rtx (Pmode);
+  emit_libcall_block (insns, dest, a0, eqv);
+
+  return dest;
+}
+
+/* The __tls_get_addr symbol.  */
+static GTY(()) rtx m68k_read_tp;
+
+/* Return SYMBOL_REF for __m68k_read_tp.  */
+
+static rtx
+m68k_get_m68k_read_tp (void)
+{
+  if (m68k_read_tp == NULL_RTX)
+    m68k_read_tp = init_one_libfunc ("__m68k_read_tp");
+
+  return m68k_read_tp;
+}
+
+/* Emit instruction sequence that calls __m68k_read_tp.
+   A pseudo register with result of __m68k_read_tp call is returned.  */
+
+static rtx 
+m68k_call_m68k_read_tp (void)
+{
+  rtx a0;
+  rtx eqv;
+  rtx insns;
+  rtx dest;
+
+  start_sequence ();
+
+  /* __m68k_read_tp() is not a libcall, but emitting a libcall_value
+     is the simpliest way of generating a call.  The difference between
+     __m68k_read_tp() and libcall is that the result is returned in D0
+     instead of A0.  To workaround this, we use m68k_libcall_value_in_a0_p
+     which temporarily switches returning the result to A0.  */ 
+
+  /* Emit the call sequence.  */
+  m68k_libcall_value_in_a0_p = true;
+  a0 = emit_library_call_value (m68k_get_m68k_read_tp (), NULL_RTX, LCT_PURE,
+				Pmode, 0);
+  m68k_libcall_value_in_a0_p = false;
+  insns = get_insns ();
+  end_sequence ();
+
+  /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
+     share the m68k_read_tp result with other IE/LE model accesses.  */
+  eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx), UNSPEC_RELOC32);
+
+  gcc_assert (can_create_pseudo_p ());
+  dest = gen_reg_rtx (Pmode);
+  emit_libcall_block (insns, dest, a0, eqv);
+
+  return dest;
+}
+
+/* Return a legitimized address for accessing TLS SYMBOL_REF X.
+   For explanations on instructions sequences see TLS/NPTL ABI for m68k and
+   ColdFire.  */
+
+rtx
+m68k_legitimize_tls_address (rtx orig)
+{
+  switch (SYMBOL_REF_TLS_MODEL (orig))
+    {
+    case TLS_MODEL_GLOBAL_DYNAMIC:
+      orig = m68k_call_tls_get_addr (orig, orig, RELOC_TLSGD);
+      break;
+
+    case TLS_MODEL_LOCAL_DYNAMIC:
+      {
+	rtx eqv;
+	rtx a0;
+	rtx x;
+ 
+	/* 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, const0_rtx),
+			      UNSPEC_RELOC32);
+
+	a0 = m68k_call_tls_get_addr (orig, eqv, RELOC_TLSLDM);
+
+	x = m68k_wrap_symbol (orig, RELOC_TLSLDO, a0, NULL_RTX);
+
+	if (can_create_pseudo_p ())
+	  x = m68k_move_to_reg (x, orig, NULL_RTX);
+
+	orig = x;
+	break;
+      }
+
+    case TLS_MODEL_INITIAL_EXEC:
+      {
+	rtx a0;
+	rtx x;
+
+	a0 = m68k_call_m68k_read_tp ();
+
+	x = m68k_wrap_symbol_into_got_ref (orig, RELOC_TLSIE, NULL_RTX);
+	x = gen_rtx_PLUS (Pmode, x, a0);
+
+	if (can_create_pseudo_p ())
+	  x = m68k_move_to_reg (x, orig, NULL_RTX);
+
+	orig = x;
+	break;
+      }
+
+    case TLS_MODEL_LOCAL_EXEC:
+      {
+	rtx a0;
+	rtx x;
+
+	a0 = m68k_call_m68k_read_tp ();
+
+	x = m68k_wrap_symbol (orig, RELOC_TLSLE, a0, NULL_RTX);
+
+	if (can_create_pseudo_p ())
+	  x = m68k_move_to_reg (x, orig, NULL_RTX);
+
+	orig = x;
+	break;
+      }
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return orig;
+}
+
+/* Return true if X is a TLS symbol.  */
+
+static bool
+m68k_tls_symbol_p (rtx x)
+{
+  if (!TARGET_HAVE_TLS)
+    return false;
+
+  if (GET_CODE (x) != SYMBOL_REF)
+    return false;
+
+  return SYMBOL_REF_TLS_MODEL (x) != 0;
+}
+
+/* Helper for m68k_tls_referenced_p.  */
+
+static int
+m68k_tls_reference_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+  /* Note: this is not the same as m68k_tls_symbol_p.  */
+  if (GET_CODE (*x) == SYMBOL_REF)
+    return SYMBOL_REF_TLS_MODEL (*x) != 0 ? 1 : 0;
+
+  /* Don't recurse into legitimate TLS references.  */
+  if (m68k_tls_reference_p (*x, true))
+    return -1;
+
+  return 0;
+}
+
+/* If !LEGITIMATE_P, return true if X is a TLS symbol reference,
+   though illegitimate one.
+   If LEGITIMATE_P, return true if X is a legitimate TLS symbol reference.  */
+
+bool
+m68k_tls_reference_p (rtx x, bool legitimate_p)
+{
+  if (!TARGET_HAVE_TLS)
+    return false;
+
+  if (!legitimate_p)
+    return for_each_rtx (&x, m68k_tls_reference_p_1, NULL) == 1 ? true : false;
+  else
+    {
+      enum m68k_reloc reloc = RELOC_GOT;
+
+      return (m68k_unwrap_symbol_1 (x, true, &reloc) != x
+	      && TLS_RELOC_P (reloc));
+    }
+}
+
 
 
 #define USE_MOVQ(i)	((unsigned) ((i) + 128) <= 255)
@@ -3995,18 +4449,92 @@ print_operand (FILE *file, rtx op, int l
     }
 }
 
+/* Return string for TLS relocation RELOC.  */
+
+static const char *
+m68k_get_reloc_decoration (enum m68k_reloc reloc)
+{
+  /* To my knowledge, !MOTOROLA assemblers don't support TLS.  */
+  gcc_assert (MOTOROLA || reloc == RELOC_GOT);
+
+  switch (reloc)
+    {
+    case RELOC_GOT:
+      if (MOTOROLA)
+	{
+	  if (flag_pic == 1 && TARGET_68020)
+	    return "@GOT.w";
+	  else
+	    return "@GOT";
+	}
+      else
+	{
+	  if (TARGET_68020)
+	    {
+	      switch (flag_pic)
+		{
+		case 1:
+		  return ":w";
+		case 2:
+		  return ":l";
+		default:
+		  return "";
+		}
+	    }
+	}
+
+    case RELOC_TLSGD:
+      return "@TLSGD";
+
+    case RELOC_TLSLDM:
+      return "@TLSLDM";
+
+    case RELOC_TLSLDO:
+      return "@TLSLDO";
+
+    case RELOC_TLSIE:
+      return "@TLSIE";
+
+    case RELOC_TLSLE:
+      return "@TLSLE";
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* m68k implementation of OUTPUT_ADDR_CONST_EXTRA.  */
 
 bool
 m68k_output_addr_const_extra (FILE *file, rtx x)
 {
-  if (GET_CODE (x) != UNSPEC || XINT (x, 1) != UNSPEC_GOTOFF)
-    return false;
+  if (GET_CODE (x) == UNSPEC)
+    {
+      switch (XINT (x, 1))
+	{
+	case UNSPEC_RELOC16:
+	case UNSPEC_RELOC32:
+	  output_addr_const (file, XVECEXP (x, 0, 0));
+	  fputs (m68k_get_reloc_decoration (INTVAL (XVECEXP (x, 0, 1))), file);
+	  return true;
 
-  output_addr_const (file, XVECEXP (x, 0, 0));
-  /* ??? What is the non-MOTOROLA syntax?  */
-  fputs ("@GOT", file);
-  return true;
+	default:
+	  break;
+	}
+    }
+
+  return false;
+}
+
+/* M68K implementation of TARGET_ASM_OUTPUT_DWARF_DTPREL.  */
+
+static void
+m68k_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+  gcc_assert (size == 4);
+  fputs ("\t.long\t", file);
+  output_addr_const (file, x);
+  fputs ("@TLSLDO+0x8000", file);
 }
 
 
@@ -4096,15 +4624,8 @@ print_operand_address (FILE *file, rtx a
 	  else
 	    {
 	      if (address.offset)
-		{
-		  output_addr_const (file, address.offset);
-		  if (flag_pic && address.base == pic_offset_table_rtx)
-		    {
-		      fprintf (file, "@GOT");
-		      if (flag_pic == 1 && TARGET_68020)
-			fprintf (file, ".w");
-		    }
-		}
+		output_addr_const (file, address.offset);
+
 	      putc ('(', file);
 	      if (address.base)
 		fputs (M68K_REGNAME (REGNO (address.base)), file);
@@ -4137,19 +4658,7 @@ print_operand_address (FILE *file, rtx a
 		    fputs (M68K_REGNAME (REGNO (address.base)), file);
 		  fprintf (file, "@(");
 		  if (address.offset)
-		    {
-		      output_addr_const (file, address.offset);
-		      if (address.base == pic_offset_table_rtx && TARGET_68020)
-			switch (flag_pic)
-			  {
-			  case 1:
-			    fprintf (file, ":w"); break;
-			  case 2:
-			    fprintf (file, ":l"); break;
-			  default:
-			    break;
-			  }
-		    }
+		    output_addr_const (file, address.offset);
 		}
 	      /* Print the ",index" component, if any.  */
 	      if (address.index)
@@ -4637,7 +5146,8 @@ m68k_libcall_value (enum machine_mode mo
   default:
     break;
   }
-  return gen_rtx_REG (mode, D0_REG);
+
+  return gen_rtx_REG (mode, m68k_libcall_value_in_a0_p ? A0_REG : D0_REG);
 }
 
 rtx
@@ -4903,9 +5413,8 @@ sched_attr_op_type (rtx insn, bool opx_p
 	  return OP_TYPE_IMM_L;
 
 	default:
-	  if (GET_CODE (op) == SYMBOL_REF)
-	    /* ??? Just a guess.  Probably we can guess better using length
-	       attribute of the instructions.  */
+	  if (symbolic_operand (m68k_unwrap_symbol (op, false), VOIDmode))
+	    /* Just a guess.  */
 	    return OP_TYPE_IMM_W;
 
 	  return OP_TYPE_IMM_L;
@@ -5850,3 +6359,5 @@ m68k_sched_indexed_address_bypass_p (rtx
       return 0;
     }
 }
+
+#include "gt-m68k.h"
Index: gcc-mainline/gcc/config/m68k/constraints.md
===================================================================
--- gcc-mainline/gcc/config/m68k/constraints.md	(revision 147467)
+++ gcc-mainline/gcc/config/m68k/constraints.md	(working copy)
@@ -129,6 +129,11 @@
   (and (match_code "const_int")
        (match_test "ival < -0x8000 || ival > 0x7FFF")))
 
+(define_constraint "Cu"
+  "16-bit offset for wrapped symbols"
+  (and (match_code "const")
+       (match_test "m68k_unwrap_symbol (op, false) != op")))
+
 (define_constraint "CQ"
   "Integers valid for mvq."
   (and (match_code "const_int")
Index: gcc-mainline/gcc/config/m68k/m68k.h
===================================================================
--- gcc-mainline/gcc/config/m68k/m68k.h	(revision 147467)
+++ gcc-mainline/gcc/config/m68k/m68k.h	(working copy)
@@ -749,7 +749,8 @@ __transfer_from_trampoline ()					\
 
 #define LEGITIMATE_PIC_OPERAND_P(X)				\
   (!symbolic_operand (X, VOIDmode)				\
-   || (TARGET_PCREL && REG_STRICT_P))
+   || (TARGET_PCREL && REG_STRICT_P)				\
+   || m68k_tls_reference_p (X, true))
 
 #define REG_OK_FOR_BASE_P(X) \
   m68k_legitimate_base_reg_p (X, REG_STRICT_P)
@@ -974,6 +975,9 @@ do { if (cc_prev_status.flags & CC_IN_68
   assemble_name ((FILE), (NAME)),		\
   fprintf ((FILE), ",%u\n", (int)(ROUNDED)))
 
+#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \
+  m68k_final_prescan_insn (INSN, OPVEC, NOPERANDS)
+
 /* On the 68000, we use several CODE characters:
    '.' for dot needed in Motorola-style opcode names.
    '-' for an operand pushing on the stack:

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