From 087397aa335c082f26156d19fd316cd4e069815d Mon Sep 17 00:00:00 2001
From: Gopi <gopi@linux69.(none)>
Date: Mon, 15 Mar 2010 12:46:09 +0530
Subject: [PATCH] [Patch, microblaze]: Add support for TLS in MicroBlaze

Changelog

2013-03-14 Edgar E. Iglesias <edgar.iglesias@xilinx.com>
           David Holsgrove <david.holsgrove@xilinx.com>

 * configure.ac: Add MicroBlaze TLS support detection.
 * configure: Regenerate.
 * config/microblaze/microblaze-protos.h: (microblaze_cannot_force_const_mem,
   microblaze_tls_referenced_p, symbol_mentioned_p,
   label_mentioned_p): Add prototypes.
 * config/microblaze/microblaze.c (microblaze_address_type): Add ADDRESS_TLS
   and tls_reloc address types.
   (microblaze_address_info): Add tls_reloc.
   (TARGET_HAVE_TLS): Define.
   (get_tls_get_addr, microblaze_tls_symbol_p, microblaze_tls_operand_p_1,
    microblaze_tls_referenced_p, microblaze_cannot_force_const_mem,
    symbol_mentioned_p, label_mentioned_p, tls_mentioned_p, load_tls_operand,
    microblaze_call_tls_get_addr, microblaze_legitimize_tls_address): New functions.
   (microblaze_classify_unspec): Handle UNSPEC_TLS.
   (get_base_reg): Use microblaze_tls_symbol_p.
   (microblaze_classify_address): Handle TLS.
   (microblaze_legitimate_pic_operand): Use symbol_mentioned_p, label_mentioned_p
    and microblaze_tls_referenced_p.
   (microblaze_legitimize_address): Handle TLS.
   (microblaze_address_insns): Handle ADDRESS_TLS.
   (pic_address_needs_scratch): Handle TLS.
   (print_operand_address): Handle TLS.
   (microblaze_expand_prologue): Check TLS_NEEDS_GOT.
   (microblaze_expand_move): Handle TLS.
   (microblaze_legitimate_constant_p): Check microblaze_cannot_force_const_mem
    and microblaze_tls_symbol_p.
   (TARGET_CANNOT_FORCE_CONST_MEM): Define.
 * config/microblaze/microblaze.h (TLS_NEEDS_GOT): Define
   (PIC_OFFSET_TABLE_REGNUM): Set.
 * config/microblaze/linux.h (TLS_NEEDS_GOT): Define.
 * config/microblaze/microblaze.md (UNSPEC_TLS): Define.
   (addsi3, movsi_internal2, movdf_internal): Update constraints
 * config/microblaze/predicates.md (arith_plus_operand): Define
   (move_operand): Redefine as move_src_operand, check microblaze_tls_referenced_p.

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Signed-off-by: David Holsgrove <david.holsgrove@xilinx.com>
---
 gcc/config/microblaze/linux.h             |    3 +
 gcc/config/microblaze/microblaze-protos.h |    4 +
 gcc/config/microblaze/microblaze.c        |  598 +++++++++++++++++++++++------
 gcc/config/microblaze/microblaze.h        |    7 +-
 gcc/config/microblaze/microblaze.md       |   19 +-
 gcc/config/microblaze/predicates.md       |   57 +++-
 gcc/configure                             |   12 +
 gcc/configure.ac                          |   12 +
 8 files changed, 587 insertions(+), 125 deletions(-)

diff --git a/gcc/config/microblaze/linux.h b/gcc/config/microblaze/linux.h
index eca7776..a7b7a1a 100644
--- a/gcc/config/microblaze/linux.h
+++ b/gcc/config/microblaze/linux.h
@@ -22,6 +22,9 @@
 #undef TARGET_SUPPORTS_PIC
 #define TARGET_SUPPORTS_PIC 1
 
+#undef TLS_NEEDS_GOT
+#define TLS_NEEDS_GOT 1
+
 #define DYNAMIC_LINKER "/lib/ld.so.1"
 #undef  SUBTARGET_EXTRA_SPECS
 #define SUBTARGET_EXTRA_SPECS \
diff --git a/gcc/config/microblaze/microblaze-protos.h b/gcc/config/microblaze/microblaze-protos.h
index e19939f..34be76f 100644
--- a/gcc/config/microblaze/microblaze-protos.h
+++ b/gcc/config/microblaze/microblaze-protos.h
@@ -50,6 +50,10 @@ extern void microblaze_declare_object (FILE *, const char *, const char *,
    const char *, int);
 extern void microblaze_asm_output_ident (const char *);
 extern int microblaze_legitimate_pic_operand (rtx);
+extern bool microblaze_tls_referenced_p (rtx);
+extern int symbol_mentioned_p (rtx);
+extern int label_mentioned_p (rtx);
+extern bool microblaze_cannot_force_const_mem (enum machine_mode, rtx);
 #endif  /* RTX_CODE */
 
 /* Declare functions in microblaze-c.c.  */
diff --git a/gcc/config/microblaze/microblaze.c b/gcc/config/microblaze/microblaze.c
index 3a52994..0556365 100644
--- a/gcc/config/microblaze/microblaze.c
+++ b/gcc/config/microblaze/microblaze.c
@@ -84,7 +84,8 @@ enum microblaze_address_type
   ADDRESS_CONST_INT,
   ADDRESS_SYMBOLIC,
   ADDRESS_GOTOFF,
-  ADDRESS_PLT
+  ADDRESS_PLT,
+  ADDRESS_TLS
 };
 
 /* Classifies symbols
@@ -98,6 +99,15 @@ enum microblaze_symbol_type
   SYMBOL_TYPE_GENERAL
 };
 
+/* TLS Address Type.  */
+enum tls_reloc {
+  TLS_GD,
+  TLS_LDM,
+  TLS_DTPREL,
+  TLS_IE,
+  TLS_LE
+};
+
 /* Classification of a MicroBlaze address.  */
 struct microblaze_address_info
 {
@@ -108,6 +118,7 @@ struct microblaze_address_info
   rtx offset; 	/* Contains valid values on ADDRESS_CONST_INT and ADDRESS_REG.  */
   rtx symbol; 	/* Contains valid values on ADDRESS_SYMBOLIC.  */
   enum microblaze_symbol_type symbol_type;
+  enum tls_reloc tls_type;
 };
 
 /* Structure to be filled in by compute_frame_size with register
@@ -215,6 +226,11 @@ static int microblaze_interrupt_function_p (tree);
 
 section *sdata2_section;
 
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+#endif
+
 /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant.  */
 static bool
 microblaze_const_double_ok (rtx op, enum machine_mode mode)
@@ -287,6 +303,9 @@ simple_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
       plus0 = XEXP (addr, 0);
       plus1 = XEXP (addr, 1);
 
+      if (GET_CODE (plus0) != REG)
+        return 0;
+
       if (GET_CODE (plus0) == REG && GET_CODE (plus1) == CONST_INT
 	  && SMALL_INT (plus1))
 	{
@@ -386,6 +405,227 @@ microblaze_valid_base_register_p (rtx x,
 	  && microblaze_regno_ok_for_base_p (REGNO (x), strict));
 }
 
+/* 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;
+}
+
+/* Return TRUE if X is a thread-local symbol.  */
+bool
+microblaze_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;
+}
+
+static int
+microblaze_tls_operand_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+  if (GET_CODE (*x) == SYMBOL_REF)
+    return SYMBOL_REF_TLS_MODEL (*x) != 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_TLS)
+    return -1;
+
+  return 0;
+}
+
+/* Return TRUE if X contains any TLS symbol references.  */
+
+bool
+microblaze_tls_referenced_p (rtx x)
+{
+  if (!TARGET_HAVE_TLS)
+    return false;
+
+  return for_each_rtx (&x, microblaze_tls_operand_p_1, NULL);
+}
+
+bool
+microblaze_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
+{
+  return microblaze_tls_referenced_p(x);
+}
+
+/* Return TRUE if X references a SYMBOL_REF.  */
+int
+symbol_mentioned_p (rtx x)
+{
+  const char * fmt;
+  int i;
+
+  if (GET_CODE (x) == SYMBOL_REF)
+    return 1;
+
+  /* UNSPEC entries for a symbol include the SYMBOL_REF, but they
+     are constant offsets, not symbols.  */
+  if (GET_CODE (x) == UNSPEC)
+    return 0;
+
+  fmt = GET_RTX_FORMAT (GET_CODE (x));
+
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+        {
+          int j;
+
+          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+            if (symbol_mentioned_p (XVECEXP (x, i, j)))
+              return 1;
+        }
+      else if (fmt[i] == 'e' && symbol_mentioned_p (XEXP (x, i)))
+        return 1;
+    }
+
+  return 0;
+}
+
+/* Return TRUE if X references a LABEL_REF.  */
+int
+label_mentioned_p (rtx x)
+{
+  const char * fmt;
+  int i;
+
+  if (GET_CODE (x) == LABEL_REF)
+    return 1;
+
+  /* UNSPEC entries for a symbol include a LABEL_REF for the referencing
+     instruction, but they are constant offsets, not symbols.  */
+  if (GET_CODE (x) == UNSPEC)
+    return 0;
+
+  fmt = GET_RTX_FORMAT (GET_CODE (x));
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+        {
+          int j;
+
+          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+            if (label_mentioned_p (XVECEXP (x, i, j)))
+              return 1;
+        }
+      else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i)))
+        return 1;
+    }
+
+  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;
+    }
+}
+
+static rtx
+load_tls_operand (rtx x, rtx reg)
+{
+  rtx tmp;
+
+  if (reg == NULL_RTX)
+    reg = gen_reg_rtx (Pmode);
+
+  tmp = gen_rtx_CONST (Pmode, x);
+
+  emit_insn (gen_rtx_SET (VOIDmode, reg,
+                          gen_rtx_PLUS (Pmode, pic_offset_table_rtx, tmp)));
+
+  return reg;
+}
+
+static rtx
+microblaze_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
+{
+  rtx insns, tls_entry;
+
+  df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+  start_sequence ();
+
+  tls_entry = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)),
+                                     UNSPEC_TLS);
+
+  reg = load_tls_operand (tls_entry, reg);
+
+  *valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX,
+                                     LCT_PURE, /* LCT_CONST?  */
+                                     Pmode, 1, reg, Pmode);
+
+  insns = get_insns ();
+  end_sequence ();
+
+  return insns;
+}
+
+rtx
+microblaze_legitimize_tls_address(rtx x, rtx reg)
+{
+  rtx dest, insns, ret, eqv, addend;
+  enum tls_model model;
+  model = SYMBOL_REF_TLS_MODEL (x);
+
+  switch (model)
+    {
+       case TLS_MODEL_LOCAL_DYNAMIC:
+       case TLS_MODEL_GLOBAL_DYNAMIC:
+       case TLS_MODEL_INITIAL_EXEC:
+           insns = microblaze_call_tls_get_addr (x, reg, &ret, TLS_GD);
+           dest = gen_reg_rtx (Pmode);
+           emit_libcall_block (insns, dest, ret, x);
+           break;
+
+       case TLS_MODEL_LOCAL_EXEC:
+           insns = microblaze_call_tls_get_addr (x, reg, &ret, TLS_LDM);
+
+           /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
+             share the LDM result with other LD model accesses.  */
+           eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx),
+                    UNSPEC_TLS);
+           dest = gen_reg_rtx (Pmode);
+           emit_libcall_block (insns, dest, ret, eqv);
+
+           /* Load the addend.  */
+           addend = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x,
+                                           GEN_INT (TLS_DTPREL)),
+                                           UNSPEC_TLS);
+           addend = force_reg (SImode, gen_rtx_CONST (SImode, addend));
+           dest = gen_rtx_PLUS (Pmode, dest, addend);
+           break;
+
+       default:
+           gcc_unreachable ();
+    }
+  return dest;
+}
+
 static bool
 microblaze_classify_unspec (struct microblaze_address_info *info, rtx x)
 {
@@ -401,6 +641,11 @@ microblaze_classify_unspec (struct microblaze_address_info *info, rtx x)
     {
       info->type = ADDRESS_PLT;
     }
+  else if (XINT (x, 1) == UNSPEC_TLS)
+    {
+      info->type = ADDRESS_TLS;
+      info->tls_type = tls_reloc INTVAL(XVECEXP(x, 0, 1));
+    }
   else
     {
       return false;
@@ -431,7 +676,12 @@ static int
 get_base_reg (rtx x)
 {
   tree decl;
-  int base_reg = (flag_pic ? MB_ABI_PIC_ADDR_REGNUM : MB_ABI_BASE_REGNUM);
+  int base_reg;
+
+  if (!flag_pic || microblaze_tls_symbol_p(x))
+    base_reg = MB_ABI_BASE_REGNUM;
+  else if (flag_pic)
+    base_reg = MB_ABI_PIC_ADDR_REGNUM;
 
   if (TARGET_XLGPOPT
       && GET_CODE (x) == SYMBOL_REF
@@ -509,28 +759,61 @@ microblaze_classify_address (struct microblaze_address_info *info, rtx x,
 	      }
 	    else if (GET_CODE (xplus1) == UNSPEC)
 	      {
+		/* Need offsettable address.  */
+		if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+		  return false;
+
 		return microblaze_classify_unspec (info, xplus1);
 	      }
 	    else if ((GET_CODE (xplus1) == SYMBOL_REF ||
-		      GET_CODE (xplus1) == LABEL_REF) && flag_pic == 2)
-	      {
-		return false;
-	      }
-	    else if (GET_CODE (xplus1) == SYMBOL_REF ||
-		     GET_CODE (xplus1) == LABEL_REF ||
-		     GET_CODE (xplus1) == CONST)
+		      GET_CODE (xplus1) == LABEL_REF))
 	      {
-		if (GET_CODE (XEXP (xplus1, 0)) == UNSPEC)
-		  return microblaze_classify_unspec (info, XEXP (xplus1, 0));
-		else if (flag_pic == 2)
-		  {
-		    return false;
-		  }
+		if (flag_pic == 2 || microblaze_tls_symbol_p(xplus1))
+		  return false;
 		info->type = ADDRESS_SYMBOLIC;
 		info->symbol = xplus1;
 		info->symbol_type = SYMBOL_TYPE_GENERAL;
 		return true;
 	      }
+	    else if (GET_CODE (xplus1) == CONST)
+	      {
+		rtx xconst0 = XEXP(xplus1, 0);
+
+		/* base + unspec.  */
+		if (GET_CODE (xconst0) == UNSPEC)
+		  {
+		    /* Need offsettable address.  */
+		    if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+		      return false;
+		    return microblaze_classify_unspec(info, xconst0);
+		  }
+
+		/* for (plus x const_int) just look at x.  */
+		if (GET_CODE (xconst0) == PLUS
+		    && GET_CODE (XEXP (xconst0, 1)) == CONST_INT
+		    && SMALL_INT (XEXP (xconst0, 1)))
+		  {
+		    /* This is ok as info->symbol is set to xplus1 the full
+		       const-expression below.  */
+		    xconst0 = XEXP (xconst0, 0);
+		  }
+
+		if (GET_CODE (xconst0) == SYMBOL_REF
+		    || GET_CODE (xconst0) == LABEL_REF)
+		  {
+		    if (flag_pic == 2 || microblaze_tls_symbol_p(xconst0))
+		      return false;
+
+		    info->type = ADDRESS_SYMBOLIC;
+		    info->symbol = xplus1;
+		    info->symbol_type = SYMBOL_TYPE_GENERAL;
+		    return true;
+		  }
+
+		/* Not base + symbol || base + UNSPEC.  */
+		return false;
+
+	      }
 	    else if (GET_CODE (xplus1) == REG
 		     && microblaze_valid_index_register_p (xplus1, mode,
 							   strict)
@@ -562,13 +845,20 @@ microblaze_classify_address (struct microblaze_address_info *info, rtx x,
 
 	if (GET_CODE (x) == CONST)
 	  {
-	    return !(flag_pic && pic_address_needs_scratch (x));
-	  }
-	else if (flag_pic == 2)
-	  {
-	    return false;
+	    if (GET_CODE (XEXP (x, 0)) == UNSPEC)
+	     {
+		info->regA = gen_rtx_raw_REG (mode,
+				  get_base_reg (XVECEXP (XEXP (x,0), 0, 0)));
+		return microblaze_classify_unspec (info, XEXP (x, 0));
+	     }
+	     return !(flag_pic && pic_address_needs_scratch (x));
 	  }
 
+	if (flag_pic == 2)
+	  return false;
+	else if (microblaze_tls_symbol_p(x))
+	  return false;
+
 	return true;
       }
 
@@ -616,11 +906,10 @@ microblaze_valid_pic_const (rtx x)
 int
 microblaze_legitimate_pic_operand (rtx x)
 {
-  struct microblaze_address_info addr;
-
-  if (pic_address_needs_scratch (x))
+  if (flag_pic == 2 && (symbol_mentioned_p(x) || label_mentioned_p(x)))
     return 0;
-  if (!microblaze_valid_pic_const(x))
+
+  if (microblaze_tls_referenced_p(x))
     return 0;
 
   return 1;
@@ -706,7 +995,7 @@ microblaze_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
 	  return result;
 	}
 
-      if (code0 == REG && REG_OK_FOR_BASE_P (xplus0) && flag_pic == 2)
+      if (code0 == REG && REG_OK_FOR_BASE_P (xplus0))
 	{
 	  if (reload_in_progress)
 	    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
@@ -717,26 +1006,58 @@ microblaze_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
 	    }
 	  if (code1 == SYMBOL_REF)
 	    {
-	      result =
-		gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xplus1), UNSPEC_GOTOFF);
-	      result = gen_rtx_CONST (Pmode, result);
-	      result = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, result);
-	      result = gen_const_mem (Pmode, result);
-	      result = gen_rtx_PLUS (Pmode, xplus0, result);
-	      return result;
+	      if (microblaze_tls_symbol_p(xplus1) )
+		{
+		  rtx tls_ref, reg;
+		  reg = gen_reg_rtx (Pmode);
+
+		  tls_ref = microblaze_legitimize_tls_address (xplus1,
+							       NULL_RTX);
+		  emit_move_insn (reg, tls_ref);
+
+		  result = gen_rtx_PLUS (Pmode, xplus0, reg);
+
+		  return result;
+		}
+	      else if (flag_pic == 2)
+		{
+		  rtx pic_ref, reg;
+		  reg = gen_reg_rtx (Pmode);
+
+		  pic_ref = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xplus1),
+					    UNSPEC_GOTOFF);
+		  pic_ref = gen_rtx_CONST (Pmode, pic_ref);
+		  pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pic_ref);
+		  pic_ref = gen_const_mem (Pmode, pic_ref);
+		  emit_move_insn (reg, pic_ref);
+		  result = gen_rtx_PLUS (Pmode, xplus0, reg);
+		  return result;
+		}
 	    }
 	}
     }
 
   if (GET_CODE (xinsn) == SYMBOL_REF)
     {
-      if (reload_in_progress)
-	df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
-      result = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xinsn), UNSPEC_GOTOFF);
-      result = gen_rtx_CONST (Pmode, result);
-      result = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, result);
-      result = gen_const_mem (Pmode, result);
-      return result;
+	rtx reg;
+	if (microblaze_tls_symbol_p(xinsn) )
+	  {
+	    reg = microblaze_legitimize_tls_address (xinsn, NULL_RTX);
+	  }
+	else
+	  {
+	    rtx pic_ref;
+
+	    if (reload_in_progress)
+	      df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+	    pic_ref = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xinsn), UNSPEC_GOTOFF);
+	    pic_ref = gen_rtx_CONST (Pmode, pic_ref);
+	    pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pic_ref);
+	    pic_ref = gen_const_mem (Pmode, pic_ref);
+	    reg = pic_ref;
+	  }
+	return reg;
     }
 
   return x;
@@ -1061,10 +1382,22 @@ microblaze_address_insns (rtx x, enum machine_mode mode)
 	  else
 	    return 2;
 	case ADDRESS_REG_INDEX:
-	case ADDRESS_SYMBOLIC:
 	  return 1;
+	case ADDRESS_SYMBOLIC:
 	case ADDRESS_GOTOFF:
 	  return 2;
+	case ADDRESS_TLS:
+	  switch (addr.tls_type)
+	    {
+	      case TLS_GD:
+		return 2;
+	      case TLS_LDM:
+		return 2;
+	      case TLS_DTPREL:
+		return 1;
+	      default :
+		abort();
+	    }
 	default:
 	  break;
 	}
@@ -1088,13 +1421,18 @@ microblaze_address_cost (rtx addr, enum machine_mode mode ATTRIBUTE_UNUSED,
 int
 pic_address_needs_scratch (rtx x)
 {
-  /* An address which is a symbolic plus a non SMALL_INT needs a temp reg.  */
-  if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
-      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
-      && (flag_pic == 2 || !SMALL_INT (XEXP (XEXP (x, 0), 1))))
-    return 1;
+  if (GET_CODE (x) == CONST && GET_CODE (XEXP (x,0)) == PLUS)
+    {
+     rtx p0, p1;
 
+      p0 = XEXP (XEXP (x, 0), 0);
+      p1 = XEXP (XEXP (x, 0), 1);
+
+      if ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
+          && (GET_CODE (p1) == CONST_INT)
+          && (flag_pic == 2 || microblaze_tls_symbol_p (p0) || !SMALL_INT (p1)))
+        return 1;
+    }
   return 0;
 }
 
@@ -1918,6 +2256,7 @@ print_operand (FILE * file, rtx op, int letter)
 	    case ADDRESS_CONST_INT:
 	    case ADDRESS_SYMBOLIC:
 	    case ADDRESS_GOTOFF:
+	    case ADDRESS_TLS:
 	      fputs ("i", file);
 	      break;
 	    case ADDRESS_REG_INDEX:
@@ -2034,10 +2373,19 @@ print_operand (FILE * file, rtx op, int letter)
   else if (letter == 't')
     fputs (code == EQ ? "t" : "f", file);
 
-  else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
+  else if (code == CONST
+           && ((GET_CODE (XEXP (op, 0)) == REG)
+               || (GET_CODE (XEXP (op, 0)) == UNSPEC)))
     {
       print_operand (file, XEXP (op, 0), letter);
     }
+  else if (code == CONST
+           && (GET_CODE (XEXP (op, 0)) == PLUS)
+           && (GET_CODE (XEXP (XEXP (op, 0), 0)) == REG)
+           && (GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST))
+    {
+      print_operand_address (file, XEXP (op, 0) );
+    }
   else if (letter == 'm')
     fprintf (file, HOST_WIDE_INT_PRINT_DEC, (1L << INTVAL (op)));
   else
@@ -2098,6 +2446,7 @@ print_operand_address (FILE * file, rtx addr)
     case ADDRESS_SYMBOLIC:
     case ADDRESS_GOTOFF:
     case ADDRESS_PLT:
+    case ADDRESS_TLS:
       if (info.regA)
 	fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
       output_addr_const (file, info.symbol);
@@ -2109,6 +2458,24 @@ print_operand_address (FILE * file, rtx addr)
 	{
 	  fputs ("@PLT", file);
 	}
+      else if (type == ADDRESS_TLS)
+	{
+	  switch (info.tls_type)
+	    {
+	      case TLS_GD:
+		fputs ("@TLSGD", file);
+		break;
+	      case TLS_LDM:
+		fputs ("@TLSLDM", file);
+		break;
+	      case TLS_DTPREL:
+		fputs ("@TLSDTPREL", file);
+		break;
+	      default :
+		abort();
+		break;
+	    }
+	}
       break;
     case ADDRESS_INVALID:
       fatal_insn ("invalid address", addr);
@@ -2471,7 +2838,8 @@ microblaze_expand_prologue (void)
 	}
     }
 
-  if (flag_pic == 2 && df_regs_ever_live_p (MB_ABI_PIC_ADDR_REGNUM))
+  if ((flag_pic == 2 || TLS_NEEDS_GOT )
+      && df_regs_ever_live_p (MB_ABI_PIC_ADDR_REGNUM))
     {
       SET_REGNO (pic_offset_table_rtx, MB_ABI_PIC_ADDR_REGNUM);
       emit_insn (gen_set_got (pic_offset_table_rtx));	/* setting GOT.  */
@@ -2701,83 +3069,62 @@ expand_pic_symbol_ref (enum machine_mode mode ATTRIBUTE_UNUSED, rtx op)
 bool
 microblaze_expand_move (enum machine_mode mode, rtx operands[])
 {
+  rtx op0, op1;
+
+  op0 = operands[0];
+  op1 = operands[1];
+
+  if (!register_operand (op0, SImode)
+      && !register_operand (op1, SImode)
+      && (GET_CODE (op1) != CONST_INT || INTVAL (op1) != 0))
+    {
+      rtx temp = force_reg (SImode, op1);
+      emit_move_insn (op0, temp);
+      return true;
+    }
   /* If operands[1] is a constant address invalid for pic, then we need to
      handle it just like LEGITIMIZE_ADDRESS does.  */
-  if (flag_pic)
+  if (GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF)
     {
-      if (GET_CODE (operands[0]) == MEM)
+      rtx result;
+      if (microblaze_tls_symbol_p(op1))
 	{
-	  rtx addr = XEXP (operands[0], 0);
-	  if (GET_CODE (addr) == SYMBOL_REF)
-	    {
-	      rtx ptr_reg, result;
-
-	      if (reload_in_progress)
-		df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
-
-	      addr = expand_pic_symbol_ref (mode, addr);
-	      ptr_reg = gen_reg_rtx (Pmode);
-	      emit_move_insn (ptr_reg, addr);
-	      result = gen_rtx_MEM (mode, ptr_reg);
-	      operands[0] = result;
-	    }
+	  result = microblaze_legitimize_tls_address (op1, NULL_RTX);
+	  emit_move_insn (op0, result);
+	  return true;
 	}
-      if (GET_CODE (operands[1]) == SYMBOL_REF
-	  || GET_CODE (operands[1]) == LABEL_REF)
+      else if (flag_pic)
 	{
-	  rtx result;
 	  if (reload_in_progress)
 	    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
-	  result = expand_pic_symbol_ref (mode, operands[1]);
-	  if (GET_CODE (operands[0]) != REG)
-	    {
-	      rtx ptr_reg = gen_reg_rtx (Pmode);
-	      emit_move_insn (ptr_reg, result);
-	      emit_move_insn (operands[0], ptr_reg);
-	    }
-	  else
-	    {
-	      emit_move_insn (operands[0], result);
-	    }
+	  result = expand_pic_symbol_ref (mode, op1);
+	  emit_move_insn (op0, result);
 	  return true;
 	}
-      else if (GET_CODE (operands[1]) == MEM &&
-	       GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
-	{
-	  rtx result;
-	  rtx ptr_reg;
-	  if (reload_in_progress)
-	    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
-	  result = expand_pic_symbol_ref (mode, XEXP (operands[1], 0));
+    }
+  /* Handle Case of (const (plus symbol const_int)).  */
+  if (GET_CODE (op1) == CONST && GET_CODE (XEXP (op1,0)) == PLUS)
+    {
+      rtx p0, p1;
 
-	  ptr_reg = gen_reg_rtx (Pmode);
+      p0 = XEXP (XEXP (op1, 0), 0);
+      p1 = XEXP (XEXP (op1, 0), 1);
 
-	  emit_move_insn (ptr_reg, result);
-	  result = gen_rtx_MEM (mode, ptr_reg);
-	  emit_move_insn (operands[0], result);
-	  return true;
-	}
-      else if (pic_address_needs_scratch (operands[1]))
+      if ((GET_CODE (p1) == CONST_INT)
+	  && ((GET_CODE (p0) == UNSPEC)
+	      || ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
+	          && (flag_pic == 2 || microblaze_tls_symbol_p (p0)
+		      || !SMALL_INT (p1)))))
 	{
-	  rtx temp = force_reg (SImode, XEXP (XEXP (operands[1], 0), 0));
-	  rtx temp2 = XEXP (XEXP (operands[1], 0), 1);
+	  rtx temp = force_reg (SImode, p0);
+	  rtx temp2 = p1;
 
-	  if (reload_in_progress)
+	  if (flag_pic && reload_in_progress)
 	    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
-	  emit_move_insn (operands[0], gen_rtx_PLUS (SImode, temp, temp2));
+	  emit_move_insn (op0, gen_rtx_PLUS (SImode, temp, temp2));
 	  return true;
 	}
     }
-
-  if ((reload_in_progress | reload_completed) == 0
-      && !register_operand (operands[0], SImode)
-      && !register_operand (operands[1], SImode)
-      && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0))
-    {
-      rtx temp = force_reg (SImode, operands[1]);
-      emit_move_insn (operands[0], temp);
-      return true;
-    }
   return false;
 }
 
@@ -3048,10 +3395,40 @@ microblaze_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
    At present, GAS doesn't understand li.[sd], so don't allow it
    to be generated at present.  */
 static bool
-microblaze_legitimate_constant_p (enum machine_mode mode, rtx x)
+microblaze_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
 {
-  return GET_CODE (x) != CONST_DOUBLE || microblaze_const_double_ok (x, mode);
+
+  if (microblaze_cannot_force_const_mem(mode, x))
+        return false;
+
+  if (GET_CODE (x) == CONST_DOUBLE)
+    {
+      return microblaze_const_double_ok (x, GET_MODE (x));
+    }
+
+   /* Handle Case of (const (plus unspec const_int)).  */
+   if (GET_CODE (x) == CONST && GET_CODE (XEXP (x,0)) == PLUS)
+     {
+        rtx p0, p1;
+
+        p0 = XEXP (XEXP (x, 0), 0);
+        p1 = XEXP (XEXP (x, 0), 1);
+
+        if (GET_CODE(p1) == CONST_INT)
+          {
+            /* Const offset from UNSPEC is not supported.  */
+            if ((GET_CODE (p0) == UNSPEC))
+              return false;
+
+            if ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
+                 && (microblaze_tls_symbol_p (p0) || !SMALL_INT (p1)))
+              return false;
+          }
+      }
+
+  return true;
 }
+
 
 #undef TARGET_ENCODE_SECTION_INFO
 #define TARGET_ENCODE_SECTION_INFO      microblaze_encode_section_info
@@ -3068,6 +3445,9 @@ microblaze_legitimate_constant_p (enum machine_mode mode, rtx x)
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS                microblaze_rtx_costs
 
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM   microblaze_cannot_force_const_mem
+
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST             microblaze_address_cost
 
diff --git a/gcc/config/microblaze/microblaze.h b/gcc/config/microblaze/microblaze.h
index 8fbe5bf..bc4d9a1 100644
--- a/gcc/config/microblaze/microblaze.h
+++ b/gcc/config/microblaze/microblaze.h
@@ -65,6 +65,9 @@ extern enum pipeline_type microblaze_pipe;
 /* The default is to support PIC.  */
 #define TARGET_SUPPORTS_PIC 1
 
+/* The default is to not need GOT for TLS.  */
+#define TLS_NEEDS_GOT 0
+
 /* What is the default setting for -mcpu= . We set it to v4.00.a even though 
    we are actually ahead. This is safest version that has generate code 
    compatible for the original ISA */
@@ -326,9 +329,7 @@ extern char microblaze_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
 
 #define NO_FUNCTION_CSE                 1
 
-#define PIC_OFFSET_TABLE_REGNUM         \
-        (flag_pic ? (GP_REG_FIRST + MB_ABI_PIC_ADDR_REGNUM) : \
-        INVALID_REGNUM)
+#define PIC_OFFSET_TABLE_REGNUM   (GP_REG_FIRST + MB_ABI_PIC_ADDR_REGNUM)
 
 enum reg_class
 {
diff --git a/gcc/config/microblaze/microblaze.md b/gcc/config/microblaze/microblaze.md
index 0285787..3618cad 100644
--- a/gcc/config/microblaze/microblaze.md
+++ b/gcc/config/microblaze/microblaze.md
@@ -40,6 +40,7 @@
   (UNSPEC_PLT           103)    ;; jump table
   (UNSPEC_CMP		104)    ;; signed compare
   (UNSPEC_CMPU		105)    ;; unsigned compare
+  (UNSPEC_TLS           106)    ;; jump table
 ])
 
 
@@ -459,7 +460,7 @@
 (define_insn "addsi3"
   [(set (match_operand:SI 0 "register_operand" "=d,d,d")
 	(plus:SI (match_operand:SI 1 "reg_or_0_operand" "%dJ,dJ,dJ")
-		 (match_operand:SI 2 "arith_operand" "d,I,i")))]
+		 (match_operand:SI 2 "arith_plus_operand" "d,I,i")))]
   ""
   "@
    addk\t%0,%z1,%2
@@ -892,8 +893,8 @@
 
 
 (define_insn "*movdi_internal"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,R,m")
-	(match_operand:DI 1 "general_operand"      " d,i,J,R,m,d,d"))]
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,R,o")
+	(match_operand:DI 1 "general_operand"      " d,i,J,R,o,d,d"))]
   ""
   { 
     switch (which_alternative)
@@ -999,13 +1000,9 @@
   (set_attr "length"	"4")])
 
 (define_insn "*movsi_internal2"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,   d,d,R, T")
-	(match_operand:SI 1 "move_operand"         " d,I,Mnis,R,m,dJ,dJ"))]
-  "(register_operand (operands[0], SImode)
-    || register_operand (operands[1], SImode) 
-    || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))
-    && (flag_pic != 2 || (GET_CODE (operands[1]) != SYMBOL_REF 
-                         && GET_CODE (operands[1]) != LABEL_REF))"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,   d,d,R,m")
+	(match_operand:SI 1 "move_src_operand"         " d,I,Mnis,R,m,dJ,dJ"))]
+  ""
   "@
    addk\t%0,%1,r0
    addik\t%0,r0,%1\t# %X1
@@ -1196,7 +1193,7 @@
 ;; Applies to both TARGET_SOFT_FLOAT and TARGET_HARD_FLOAT
 ;;
 (define_insn "*movdf_internal"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,d,To")
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,d,o")
         (match_operand:DF 1 "general_operand" "dG,o,F,T,d"))]
   ""
   {
diff --git a/gcc/config/microblaze/predicates.md b/gcc/config/microblaze/predicates.md
index e6e9983..5fd1bd4 100644
--- a/gcc/config/microblaze/predicates.md
+++ b/gcc/config/microblaze/predicates.md
@@ -30,6 +30,52 @@
        (and (match_code "const_int,const_double")
 	    (match_test "LARGE_INT (op)"))))
 
+(define_predicate "arith_plus_operand"
+ (match_operand 0 "general_operand")
+{
+  switch (GET_CODE (op))
+    {
+      default:
+        return 0;
+      case CONST_INT:
+      case REG:
+        return 1;
+      case SYMBOL_REF:
+      case LABEL_REF:
+        if (flag_pic || microblaze_tls_referenced_p(op))
+          return 0;
+        return 1;
+      case CONST:
+        {
+          rtx const0;
+          const0 = XEXP (op, 0);
+
+          switch (GET_CODE(const0))
+            {
+              default:
+                return 0;
+              case UNSPEC :
+                return 1;
+
+              case PLUS :
+                {
+                  rtx p0, p1;
+                  p0 = XEXP (const0, 0);
+                  p1 = XEXP (const0, 1);
+
+                  if ((GET_CODE(p0) == SYMBOL_REF
+                       || GET_CODE (p0) == LABEL_REF)
+                      && GET_CODE(p1) == CONST_INT)
+                    {
+                      return arith_plus_operand (p0, GET_MODE(p0));
+                    }
+                }
+            }
+        }
+    }
+  return 0;
+})
+
 (define_predicate "const_0_operand"
   (and (match_code "const_int,const_double")
        (match_test "op == CONST0_RTX (GET_MODE (op))")))
@@ -54,14 +100,21 @@
        (match_test "GET_CODE (op) == REG || GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST_INT")))
 
 ;; Return if OPERAND is valid as a source operand for a move instruction.
-(define_predicate "move_operand"
+(define_predicate "move_src_operand"
   (and (
      not (
        and (match_code "plus")
            (not (match_test "(GET_CODE (XEXP (op, 0)) == REG) ^ (GET_CODE (XEXP (op,1)) == REG)"))
 	 )
        )
-       (match_operand 0 "general_operand")))
+       (match_operand 0 "general_operand"))
+{
+  if (microblaze_tls_referenced_p(op)
+      || (flag_pic && (symbol_mentioned_p(op) || label_mentioned_p(op))))
+    return false;
+
+  return true;
+})
 
 ;; Test for valid PIC call operand
 (define_predicate "call_insn_plt_operand"
diff --git a/gcc/configure b/gcc/configure
index d4f49bb..e12a180 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -23205,6 +23205,18 @@ foo:	data8	25
 	tls_first_minor=13
 	tls_as_opt=--fatal-warnings
 	;;
+  microblaze*-*-*)
+    conftest_s='
+	.section .tdata,"awT",@progbits
+x:
+	.word 2
+	.text
+	addik r5,r20,x@TLSGD
+	addik r5,r20,x@TLSLDM'
+	tls_first_major=2
+	tls_first_minor=20
+	tls_as_opt='--fatal-warnings'
+	;;
   mips*-*-*)
     conftest_s='
 	.section .tdata,"awT",@progbits
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 811c296..eba3577 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -2968,6 +2968,18 @@ foo:	data8	25
 	tls_first_minor=13
 	tls_as_opt=--fatal-warnings
 	;;
+  microblaze*-*-*)
+    conftest_s='
+	.section .tdata,"awT",@progbits
+x:
+	.word 2
+	.text
+	addik r5,r20,x@TLSGD
+	addik r5,r20,x@TLSLDM'
+	tls_first_major=2
+	tls_first_minor=20
+	tls_as_opt='--fatal-warnings'
+	;;
   mips*-*-*)
     conftest_s='
 	.section .tdata,"awT",@progbits
-- 
1.7.0.4

