Hi Richard,
here are the patches for MIPS16 TLS. Some highlights of the implementation:

1) For the common -mcode-readable=yes case, TP/DTP offset constants are
loaded pc-relative (this saves insn count and size). HI16/LO16
constructing is used only as a fall-back when text is not readable.

2) To support the Initial/Local-Exec TLS access models, direct access to
the TP is needed. However, MIPS16 mode cannot access the 'rdhwr $3,$29'
instruction used to do that, thus a new __mips16_rdhwr() helper routine
is added to libgcc.a

3) Uses of this __mips16_rdhwr() routine are only generated by by (a)
IE/LE accesses, and (b) calls to the (new added)
__builtin_thread_pointer() builtin function. To facilitate lower
overhead, such calls are represented as UNSPECs instead of actual
function calls, and doesn't really follow the standard ABI (e.g. value
returned in $3). The __mips16_rdhwr() function is not intended for
general use, just tightly coupled compiler runtime support; therefore,
it is only linked statically from libgcc.a, not exported from shared libgcc.

4) Due to containing such a static only support routine, mips*-linux*
targets have been adjusted to use config/t-slibgcc-libgcc in
libgcc/config.host (or else static -lgcc would not be included when
linking with -shared -shared-libgcc)

5) The libgomp MIPS futex.h header has been adjusted; sys_futex0() has
been modified to be static, non-inlined, nomips16 under MIPS16 mode (for
sake of using 'syscall'). The inline assembly has also been fixed, as
Maciej noticed a possible violation of the MIPS syscall restart
convention; the 'li $2, #syscall_number' must be right before the
syscall insn.

Only O32 support has been attempted right now, though the code should
not have anything really old-ABI specific. We were able to build glibc
as MIPS16 code with this TLS implementation, so I believe things should
be fairly stable.

Anyways, I believe this will be going in only after 4.8-stage1 opens, so
you should have plenty time to review it :)

Thanks, and Happy New Year
Chung-Lin

2012-01-06  Chung-Lin Tang  <clt...@codesourcery.com>

        gcc/
        * config/mips/mips.h (CRT_CALL_STATIC_FUNCTION): Define versions
        for MIPS16 O32.
        * config/mips/mips-protos.h (mips_symbol_type): Add
        SYMBOL_DTPREL_HI,SYMBOL_TPREL_HI. Update in comments.
        (mips_output_tls_reloc_directive): New prototype.
        * config/mips/mips-ftypes.def: Add (0, (POINTER)) entry.
        * config/mips/predicates.md (tls_reloc_operand): New predicate.
        * config/mips/mips.md (V0_REGNUM,PIC_JUMP_REGNUM): New constant.
        (MIPS16 %dtprel_hi,%tprel_hi split pattern): New.
        (consttable_tls_reloc): New.
        (tls_get_tp_<mode>_mips16): New insn and split pattern.
        (*tls_get_tp_<mode>_mips16_rdhwr): New insn pattern.

        libgcc/
        * config.host
        (mips64*-*-linux*,mipsisa64*-*-linux*,mips*-*-linux*):
        Use config/t-slibgcc-libgcc.
        * config/mips/t-mips16 (LIB1ASMFUNCS): Add _m16rdhwr.
        * config/mips/crtfastmath.c (set_fast_math): Add 'nomips16'
        attribute.
        * config/mips/mips16.S (__mips16_rdhwr): New.

        libgomp/
        * config/linux/mips/futex.h (sys_futex0): Change to static
        function with noinline, nomips16 attributes under MIPS16. Adjust
        asm statement to place 'li v0,SYS_futex' immediately before
        syscall insn.

Index: gcc/config/mips/mips-ftypes.def
===================================================================
--- gcc/config/mips/mips-ftypes.def     (revision 182952)
+++ gcc/config/mips/mips-ftypes.def     (working copy)
@@ -51,6 +51,8 @@ DEF_MIPS_FTYPE (2, (INT, SF, SF))
 DEF_MIPS_FTYPE (2, (INT, V2SF, V2SF))
 DEF_MIPS_FTYPE (4, (INT, V2SF, V2SF, V2SF, V2SF))
 
+DEF_MIPS_FTYPE (0, (POINTER))
+
 DEF_MIPS_FTYPE (2, (SI, DI, SI))
 DEF_MIPS_FTYPE (2, (SI, POINTER, SI))
 DEF_MIPS_FTYPE (2, (DI, POINTER, SI))
Index: gcc/config/mips/predicates.md
===================================================================
--- gcc/config/mips/predicates.md       (revision 182952)
+++ gcc/config/mips/predicates.md       (working copy)
@@ -312,6 +312,14 @@
          && type == SYMBOL_GOT_PAGE_OFST);
 })
 
+(define_predicate "tls_reloc_operand"
+  (match_code "const,symbol_ref,label_ref")
+{
+  enum mips_symbol_type type;
+  return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &type)
+         && (type == SYMBOL_DTPREL || type == SYMBOL_TPREL));
+})
+
 (define_predicate "symbol_ref_operand"
   (match_code "symbol_ref"))
 
Index: gcc/config/mips/mips.md
===================================================================
--- gcc/config/mips/mips.md     (revision 182952)
+++ gcc/config/mips/mips.md     (working copy)
@@ -134,7 +134,9 @@
 ])
 
 (define_constants
-  [(TLS_GET_TP_REGNUM          3)
+  [(V0_REGNUM                  2)
+   (TLS_GET_TP_REGNUM          3)
+   (PIC_JUMP_REGNUM            25)
    (RETURN_ADDR_REGNUM         31)
    (CPRESTORE_SLOT_REGNUM      76)
    (GOT_VERSION_REGNUM         79)
@@ -3933,6 +3935,23 @@
   operands[2] = mips_unspec_address (operands[1], SYMBOL_32_HIGH);
 })
 
+;; MIPS16 %dtprel_hi,%tprel_hi split pattern. Similar transform
+;; as above, for supporting MIPS16 TLS.
+(define_split
+  [(set (match_operand:SI 0 "d_operand")
+       (high:SI (match_operand:SI 1 "tls_reloc_operand")))]
+  "TARGET_MIPS16 && reload_completed"
+  [(set (match_dup 0) (match_dup 2))
+   (set (match_dup 0) (ashift:SI (match_dup 0) (const_int 16)))]
+{
+  /* SYMBOL_DTPREL_HI/TPREL_HI are ordered immediately after
+     SYMBOL_DTPREL/TPREL respectively, so use unspec_type + 1.  */
+  rtx unspec = XEXP (operands[1], 0);
+  int unspec_type = XINT (unspec, 1);
+  operands[2] = mips_unspec_address (XVECEXP (unspec, 0, 0),
+                                    unspec_type + 1 - UNSPEC_ADDRESS_FIRST);
+})
+
 ;; Insns to fetch a symbol from a big GOT.
 
 (define_insn_and_split "*xgot_hi<mode>"
@@ -6492,6 +6511,17 @@
 ;;  ....................
 ;;
 
+(define_insn "consttable_tls_reloc"
+  [(unspec_volatile [(match_operand 0 "tls_reloc_operand" "")
+                    (match_operand 1 "const_int_operand" "")]
+                   UNSPEC_CONSTTABLE_INT)]
+  "TARGET_MIPS16_PCREL_LOADS"
+{
+  mips_output_tls_reloc_directive (operands[0], operands[1]);
+  return "";
+}
+  [(set (attr "length") (symbol_ref "INTVAL (operands[1])"))])
+
 (define_insn "consttable_int"
   [(unspec_volatile [(match_operand 0 "consttable_operand" "")
                     (match_operand 1 "const_int_operand" "")]
@@ -6593,6 +6623,60 @@
    ; See tls_get_tp_<mode>
    (set_attr "can_delay" "no")
    (set_attr "mode" "<MODE>")])
+
+;; In MIPS16 mode, the TLS base pointer is accessed by a
+;; libgcc helper function __mips16_rdhwr(), as 'rdhwr' is not
+;; accessible in MIPS16.
+;;
+;; This is not represented as a call insn, to avoid the
+;; unnecesarry clobbering of caller-save registers by a
+;; function consisting only of: "rdhwr $3,$29; j $31; nop;"
+;;
+;; A $25 clobber is added to cater for a $25 load stub added by the
+;; linker to __mips16_rdhwr when the call is made from non-PIC code.
+
+(define_insn_and_split "tls_get_tp_<mode>_mips16"
+  [(set (match_operand:P 0 "register_operand" "=d")
+       (unspec:P [(const_int 0)] UNSPEC_TLS_GET_TP))
+   (clobber (reg:P TLS_GET_TP_REGNUM))
+   (clobber (reg:P PIC_JUMP_REGNUM))
+   (clobber (reg:P RETURN_ADDR_REGNUM))]
+  "HAVE_AS_TLS && TARGET_MIPS16"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (reg:P TLS_GET_TP_REGNUM)
+             (unspec:P [(match_dup 1)] UNSPEC_TLS_GET_TP))
+             (clobber (reg:P PIC_JUMP_REGNUM))
+             (clobber (reg:P RETURN_ADDR_REGNUM))])
+   (set (match_dup 0) (reg:P TLS_GET_TP_REGNUM)) ]
+  {
+    /* UNSPEC operand decides on direct/indirect pattern below.  */
+    rtx sym = gen_rtx_SYMBOL_REF (Pmode, "__mips16_rdhwr");
+    if (TARGET_ABSOLUTE_JUMPS)
+      operands[1] = sym;
+    else
+      {
+       operands[1] = gen_rtx_REG (Pmode, TLS_GET_TP_REGNUM);
+       mips_emit_move (operands[1], sym);
+      }
+  }
+  [(set_attr "type" "unknown")
+   (set_attr "can_delay" "no")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "*tls_get_tp_<mode>_mips16_rdhwr"
+  [(set (reg:P TLS_GET_TP_REGNUM)
+       (unspec:P [(match_operand:P 0 "")] UNSPEC_TLS_GET_TP))
+       (clobber (reg:P PIC_JUMP_REGNUM))
+       (clobber (reg:P RETURN_ADDR_REGNUM))]
+  "HAVE_AS_TLS && TARGET_MIPS16"
+  {
+    return MIPS_CALL ("jal", operands, 0, -1);
+  }
+  [(set_attr "type" "call")
+   (set_attr "can_delay" "no")
+   (set_attr "mode" "<MODE>")])
+
 
 ;; Synchronization instructions.
 
Index: gcc/config/mips/mips-protos.h
===================================================================
--- gcc/config/mips/mips-protos.h       (revision 182952)
+++ gcc/config/mips/mips-protos.h       (working copy)
@@ -89,8 +89,10 @@ enum mips_symbol_context {
    SYMBOL_TLSGD
    SYMBOL_TLSLDM
    SYMBOL_DTPREL
+   SYMBOL_DTPREL_HI
    SYMBOL_GOTTPREL
    SYMBOL_TPREL
+   SYMBOL_TPREL_HI
        UNSPEC wrappers around SYMBOL_TLS, corresponding to the
        thread-local storage relocation operators.
 
@@ -127,8 +129,10 @@ enum mips_symbol_type {
   SYMBOL_TLSGD,
   SYMBOL_TLSLDM,
   SYMBOL_DTPREL,
+  SYMBOL_DTPREL_HI,
   SYMBOL_GOTTPREL,
   SYMBOL_TPREL,
+  SYMBOL_TPREL_HI,
   SYMBOL_32_HIGH,
   SYMBOL_64_HIGH,
   SYMBOL_64_MID,
@@ -341,6 +345,7 @@ extern bool mips_epilogue_uses (unsigned int);
 extern void mips_final_prescan_insn (rtx, rtx *, int);
 extern int mips_trampoline_code_size (void);
 extern void mips_function_profiler (FILE *);
+extern void mips_output_tls_reloc_directive (rtx, rtx);
 
 typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx);
 #ifdef RTX_CODE
Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c      (revision 182952)
+++ gcc/config/mips/mips.c      (working copy)
@@ -183,6 +183,7 @@ enum mips_address_type {
 };
 
 /* Macros to create an enumeration identifier for a function prototype.  */
+#define MIPS_FTYPE_NAME0(A) MIPS_##A##_FTYPE_VOID
 #define MIPS_FTYPE_NAME1(A, B) MIPS_##A##_FTYPE_##B
 #define MIPS_FTYPE_NAME2(A, B, C) MIPS_##A##_FTYPE_##B##_##C
 #define MIPS_FTYPE_NAME3(A, B, C, D) MIPS_##A##_FTYPE_##B##_##C##_##D
@@ -233,7 +234,10 @@ enum mips_builtin_type {
   MIPS_BUILTIN_CMP_SINGLE,
 
   /* For generating bposge32 branch instructions in MIPS32 DSP ASE.  */
-  MIPS_BUILTIN_BPOSGE32
+  MIPS_BUILTIN_BPOSGE32,
+
+  /* For generating accesses to the TLS thread pointer.  */
+  MIPS_BUILTIN_THREAD_POINTER
 };
 
 /* Invoke MACRO (COND) for each C.cond.fmt condition.  */
@@ -1763,6 +1767,8 @@ mips_symbolic_constant_p (rtx x, enum mips_symbol_
     case SYMBOL_GOTTPREL:
     case SYMBOL_TLS:
     case SYMBOL_HALF:
+    case SYMBOL_TPREL_HI:
+    case SYMBOL_DTPREL_HI:
       return false;
     }
   gcc_unreachable ();
@@ -1857,8 +1863,10 @@ mips_symbol_insns_1 (enum mips_symbol_type type, e
     case SYMBOL_TLSGD:
     case SYMBOL_TLSLDM:
     case SYMBOL_DTPREL:
+    case SYMBOL_DTPREL_HI:
     case SYMBOL_GOTTPREL:
     case SYMBOL_TPREL:
+    case SYMBOL_TPREL_HI:
     case SYMBOL_HALF:
       /* A 16-bit constant formed by a single relocation, or a 32-bit
         constant formed from a high 16-bit relocation and a low 16-bit
@@ -1928,14 +1936,23 @@ mips_cannot_force_const_mem (enum machine_mode mod
   if (mips_symbolic_constant_p (base, SYMBOL_CONTEXT_LEA, &type)
       && type != SYMBOL_FORCE_TO_MEM)
     {
+      if (TARGET_MIPS16_PCREL_LOADS)
+       {
+         /* Under MIPS16, TLS DTP/TP-relative offsets are loaded from the
+            constant pool, as this saves some code size compared to hi/lo
+            constructing.  */
+         if (type == SYMBOL_DTPREL || type == SYMBOL_TPREL)
+           return false;
+
+         /* If MIPS16 constant pools live in the text section, they should
+            not refer to anything that might need run-time relocation.  */
+         if (mips_got_symbol_type_p (type))
+           return true;
+       }
+
       /* The same optimization as for CONST_INT.  */
       if (SMALL_INT (offset) && mips_symbol_insns (type, MAX_MACHINE_MODE) > 0)
        return true;
-
-      /* If MIPS16 constant pools live in the text section, they should
-        not refer to anything that might need run-time relocation.  */
-      if (TARGET_MIPS16_PCREL_LOADS && mips_got_symbol_type_p (type))
-       return true;
     }
 
   /* TLS symbols must be computed by mips_legitimize_move.  */
@@ -2820,11 +2837,20 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_
 /* Return a pseudo register that contains the current thread pointer.  */
 
 static rtx
-mips_get_tp (void)
+mips_get_tp (rtx target)
 {
-  rtx tp;
+  rtx tp = (target != NULL_RTX && REG_P (target)
+           ? target : gen_reg_rtx (Pmode));
 
-  tp = gen_reg_rtx (Pmode);
+  if (TARGET_MIPS16)
+    {
+      if (Pmode == DImode)
+       emit_insn (gen_tls_get_tp_di_mips16 (tp));
+      else
+       emit_insn (gen_tls_get_tp_si_mips16 (tp));
+      return tp;
+    }
+
   if (Pmode == DImode)
     emit_insn (gen_tls_get_tp_di (tp));
   else
@@ -2842,12 +2868,6 @@ mips_legitimize_tls_address (rtx loc)
   rtx dest, insn, v0, tp, tmp1, tmp2, eqv;
   enum tls_model model;
 
-  if (TARGET_MIPS16)
-    {
-      sorry ("MIPS16 TLS");
-      return gen_reg_rtx (Pmode);
-    }
-
   model = SYMBOL_REF_TLS_MODEL (loc);
   /* Only TARGET_ABICALLS code can have more than one module; other
      code must be be static and should not use a GOT.  All TLS models
@@ -2875,13 +2895,23 @@ mips_legitimize_tls_address (rtx loc)
                            UNSPEC_TLS_LDM);
       emit_libcall_block (insn, tmp1, v0, eqv);
 
-      tmp2 = mips_unspec_offset_high (NULL, tmp1, loc, SYMBOL_DTPREL);
-      dest = gen_rtx_LO_SUM (Pmode, tmp2,
-                            mips_unspec_address (loc, SYMBOL_DTPREL));
+      if (TARGET_MIPS16_PCREL_LOADS)
+       {
+         tmp2 = mips_force_temporary (NULL,
+                                      mips_unspec_address (loc,
+                                                           SYMBOL_DTPREL));
+         dest = gen_rtx_PLUS (Pmode, tmp1, tmp2);
+       }
+      else
+       {
+         tmp2 = mips_unspec_offset_high (NULL, tmp1, loc, SYMBOL_DTPREL);
+         dest = gen_rtx_LO_SUM (Pmode, tmp2,
+                                mips_unspec_address (loc, SYMBOL_DTPREL));
+       }
       break;
 
     case TLS_MODEL_INITIAL_EXEC:
-      tp = mips_get_tp ();
+      tp = mips_get_tp (NULL_RTX);
       tmp1 = gen_reg_rtx (Pmode);
       tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL);
       if (Pmode == DImode)
@@ -2893,10 +2923,20 @@ mips_legitimize_tls_address (rtx loc)
       break;
 
     case TLS_MODEL_LOCAL_EXEC:
-      tp = mips_get_tp ();
-      tmp1 = mips_unspec_offset_high (NULL, tp, loc, SYMBOL_TPREL);
-      dest = gen_rtx_LO_SUM (Pmode, tmp1,
-                            mips_unspec_address (loc, SYMBOL_TPREL));
+      tp = mips_get_tp (NULL_RTX);
+      if (TARGET_MIPS16_PCREL_LOADS)
+       {
+         tmp1 = mips_force_temporary (NULL,
+                                      mips_unspec_address (loc,
+                                                           SYMBOL_TPREL));
+         dest = gen_rtx_PLUS (Pmode, tp, tmp1);
+       }
+      else
+       {
+         tmp1 = mips_unspec_offset_high (NULL, tp, loc, SYMBOL_TPREL);
+         dest = gen_rtx_LO_SUM (Pmode, tmp1,
+                                mips_unspec_address (loc, SYMBOL_TPREL));
+       }
       break;
 
     default:
@@ -7291,16 +7331,24 @@ mips_init_relocs (void)
   mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
   mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
 
-  mips_split_p[SYMBOL_DTPREL] = true;
-  mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
-  mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
+  if (! TARGET_MIPS16_PCREL_LOADS)
+    {
+      mips_split_p[SYMBOL_DTPREL] = true;
+      mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
+      mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
 
-  mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
+      mips_split_p[SYMBOL_TPREL] = true;
+      mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
+      mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
 
-  mips_split_p[SYMBOL_TPREL] = true;
-  mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
-  mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
+      if (TARGET_MIPS16)
+       {
+         mips_lo_relocs[SYMBOL_TPREL_HI] = "%tprel_hi(";
+         mips_lo_relocs[SYMBOL_DTPREL_HI] = "%dtprel_hi(";
+       }
+    }
 
+  mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
   mips_lo_relocs[SYMBOL_HALF] = "%half(";
 }
 
@@ -12734,7 +12782,8 @@ struct mips_builtin_description {
   /* The function's prototype.  */
   enum mips_function_type function_type;
 
-  /* Whether the function is available.  */
+  /* Whether the function is available.  A NULL pointer value here
+     means the builtin is always available.  */
   unsigned int (*avail) (void);
 };
 
@@ -12904,6 +12953,12 @@ AVAIL_NON_MIPS16 (cache, TARGET_CACHE_BUILTIN)
 #define CODE_FOR_loongson_psubush CODE_FOR_ussubv4hi3
 #define CODE_FOR_loongson_psubusb CODE_FOR_ussubv8qi3
 
+/* Define a MIPS_BUILTIN_THREAD_POINTER builtin.  The parameters are fixed,
+   but we allow both __builtin* and __builtin_mips* prefixes below.  */
+#define THREAD_POINTER_BUILTIN(NAME)                                   \
+  { CODE_FOR_nothing, MIPS_FP_COND_f, #NAME,                           \
+    MIPS_BUILTIN_THREAD_POINTER, MIPS_POINTER_FTYPE_VOID, NULL }
+
 static const struct mips_builtin_description mips_builtins[] = {
   DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
   DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
@@ -13187,7 +13242,11 @@ static const struct mips_builtin_description mips_
   LOONGSON_BUILTIN_SUFFIX (punpcklwd, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
 
   /* Sundry other built-in functions.  */
-  DIRECT_NO_TARGET_BUILTIN (cache, MIPS_VOID_FTYPE_SI_CVPOINTER, cache)
+  DIRECT_NO_TARGET_BUILTIN (cache, MIPS_VOID_FTYPE_SI_CVPOINTER, cache),
+
+  /* TLS thread pointer built-in functions.  */
+  THREAD_POINTER_BUILTIN (__builtin_mips_thread_pointer),
+  THREAD_POINTER_BUILTIN (__builtin_thread_pointer)
 };
 
 /* Index I is the function declaration for mips_builtins[I], or null if the
@@ -13258,6 +13317,8 @@ mips_build_cvpointer_type (void)
 
 /* MIPS_FTYPE_ATYPESN takes N MIPS_FTYPES-like type codes and lists
    their associated MIPS_ATYPEs.  */
+#define MIPS_FTYPE_ATYPES0(A) MIPS_ATYPE_##A
+
 #define MIPS_FTYPE_ATYPES1(A, B) \
   MIPS_ATYPE_##A, MIPS_ATYPE_##B
 
@@ -13309,11 +13370,19 @@ mips_init_builtins (void)
   for (i = 0; i < ARRAY_SIZE (mips_builtins); i++)
     {
       d = &mips_builtins[i];
-      if (d->avail ())
-       mips_builtin_decls[i]
-         = add_builtin_function (d->name,
-                                 mips_build_function_type (d->function_type),
-                                 i, BUILT_IN_MD, NULL, NULL);
+      if (d->avail && !d->avail ())
+       continue;
+
+      mips_builtin_decls[i]
+       = add_builtin_function (d->name,
+                               mips_build_function_type (d->function_type),
+                               i, BUILT_IN_MD, NULL, NULL);
+
+      if (d->builtin_type == MIPS_BUILTIN_THREAD_POINTER)
+       {
+         TREE_NOTHROW (mips_builtin_decls[i]) = 1;
+         TREE_READONLY (mips_builtin_decls[i]) = 1;
+       }
     }
 }
 
@@ -13544,20 +13613,21 @@ mips_expand_builtin (tree exp, rtx target, rtx sub
                     enum machine_mode mode, int ignore)
 {
   tree fndecl;
-  unsigned int fcode, avail;
+  unsigned int fcode;
   const struct mips_builtin_description *d;
 
   fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
   fcode = DECL_FUNCTION_CODE (fndecl);
   gcc_assert (fcode < ARRAY_SIZE (mips_builtins));
   d = &mips_builtins[fcode];
-  avail = d->avail ();
-  gcc_assert (avail != 0);
-  if (TARGET_MIPS16)
+  if (d->avail && !d->avail ())
     {
-      error ("built-in function %qE not supported for MIPS16",
-            DECL_NAME (fndecl));
-      return ignore ? const0_rtx : CONST0_RTX (mode);
+      if (TARGET_MIPS16)
+       {
+         error ("built-in function %qE not supported for MIPS16",
+                DECL_NAME (fndecl));
+         return ignore ? const0_rtx : CONST0_RTX (mode);
+       }
     }
   switch (d->builtin_type)
     {
@@ -13582,6 +13652,9 @@ mips_expand_builtin (tree exp, rtx target, rtx sub
 
     case MIPS_BUILTIN_BPOSGE32:
       return mips_expand_builtin_bposge (d->builtin_type, target);
+
+    case MIPS_BUILTIN_THREAD_POINTER:
+      return mips_get_tp (target);
     }
   gcc_unreachable ();
 }
@@ -13771,7 +13844,7 @@ mips16_rewrite_pool_refs (rtx *x, void *data)
   struct mips16_rewrite_pool_refs_info *info =
     (struct mips16_rewrite_pool_refs_info *) data;
 
-  if (force_to_mem_operand (*x, Pmode))
+  if (force_to_mem_operand (*x, Pmode) || tls_reloc_operand (*x, Pmode))
     {
       rtx mem = force_const_mem (GET_MODE (*x), *x);
       validate_change (info->insn, x, mem, false);
@@ -13783,6 +13856,11 @@ mips16_rewrite_pool_refs (rtx *x, void *data)
       return -1;
     }
 
+  if (GET_CODE (*x) == UNSPEC
+      && XINT (*x, 1) == UNSPEC_TLS_GET_TP
+      && GET_CODE (XVECEXP (*x, 0, 0)) == SYMBOL_REF)
+    return -1;
+
   if (TARGET_MIPS16_TEXT_LOADS)
     mips16_rewrite_pool_constant (info->pool, x);
 
@@ -17108,6 +17186,28 @@ mips_expand_vec_minmax (rtx target, rtx op0, rtx o
   x = gen_rtx_IOR (vmode, t0, t1);
   emit_insn (gen_rtx_SET (VOIDmode, target, x));
 }
+
+/* Output a DTP/TP-relative relocation, used in MIPS16 TLS.  */
+
+void
+mips_output_tls_reloc_directive (rtx x, rtx size)
+{
+  const char *dir = NULL;
+  if (GET_CODE (x) == CONST)
+    x = XEXP (x, 0);
+  switch (UNSPEC_ADDRESS_TYPE (x))
+    {
+    case SYMBOL_DTPREL:
+      dir = INTVAL (size) == 4 ? ".dtprelword" : ".dtpreldword";
+      break;
+    case SYMBOL_TPREL:
+      dir = INTVAL (size) == 4 ? ".tprelword" : ".tpreldword";
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  fprintf (asm_out_file, "\t%s\t%s\n", dir, XSTR (UNSPEC_ADDRESS (x), 0));
+}
 
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
Index: gcc/config/mips/mips.h
===================================================================
--- gcc/config/mips/mips.h      (revision 182952)
+++ gcc/config/mips/mips.h      (working copy)
@@ -2841,8 +2841,32 @@ while (0)
        jal " USER_LABEL_PREFIX #FUNC "\n\
        " TEXT_SECTION_ASM_OP);
 #endif
+
+#else
+#if (defined _ABIO32 && _MIPS_SIM == _ABIO32)
+#ifdef __PIC__
+/* For MIPS16 PIC, construct the GP-value in $2 using PC-relative insns.  */
+#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC)     \
+   asm (SECTION_OP "\n\
+       li $2,%hi(_gp_disp)\n\
+       addiu $3,$pc,%lo(_gp_disp)\n\
+       sll $2,16\n\
+       addu $2,$3\n\
+       lw $2,%got(" USER_LABEL_PREFIX #FUNC ")($2)\n\
+       addiu $2,%lo(" USER_LABEL_PREFIX #FUNC ")\n\
+       move $25,$2\n\
+       jalr $2\n\
+       " TEXT_SECTION_ASM_OP);
+#else
+#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC)     \
+   asm (SECTION_OP "\n\
+       jal " USER_LABEL_PREFIX #FUNC "\n\
+       " TEXT_SECTION_ASM_OP);
 #endif
+#endif
 
+#endif /* __mips16 */
+
 #ifndef HAVE_AS_TLS
 #define HAVE_AS_TLS 0
 #endif
Index: libgcc/config.host
===================================================================
--- libgcc/config.host  (revision 182952)
+++ libgcc/config.host  (working copy)
@@ -740,12 +740,12 @@ mips*-*-netbsd*)                  # NetBSD/mips, either 
endian.
        ;;
 mips64*-*-linux* | mipsisa64*-*-linux*)
        extra_parts="$extra_parts crtfastmath.o"
-       tmake_file="${tmake_file} t-crtfm mips/t-mips16 mips/t-tpbit"
+       tmake_file="${tmake_file} t-crtfm mips/t-mips16 mips/t-tpbit 
t-slibgcc-libgcc"
        md_unwind_header=mips/linux-unwind.h
        ;;
 mips*-*-linux*)                                # Linux MIPS, either endian.
        extra_parts="$extra_parts crtfastmath.o"
-       tmake_file="${tmake_file} t-crtfm mips/t-mips16"
+       tmake_file="${tmake_file} t-crtfm mips/t-mips16 t-slibgcc-libgcc"
        md_unwind_header=mips/linux-unwind.h
        ;;
 mips*-*-openbsd*)
Index: libgcc/config/mips/t-mips16
===================================================================
--- libgcc/config/mips/t-mips16 (revision 182952)
+++ libgcc/config/mips/t-mips16 (working copy)
@@ -36,7 +36,8 @@ LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m
        _m16stubsc0 _m16stubsc1 _m16stubsc2 _m16stubsc5 _m16stubsc6 \
        _m16stubsc9 _m16stubsc10 \
        _m16stubdc0 _m16stubdc1 _m16stubdc2 _m16stubdc5 _m16stubdc6 \
-       _m16stubdc9 _m16stubdc10
+       _m16stubdc9 _m16stubdc10 \
+       _m16rdhwr
 
 SYNC = yes
 SYNC_CFLAGS = -mno-mips16
Index: libgcc/config/mips/mips16.S
===================================================================
--- libgcc/config/mips/mips16.S (revision 182952)
+++ libgcc/config/mips/mips16.S (working copy)
@@ -709,4 +709,18 @@ CALL_STUB_RET (__mips16_call_stub_dc_9, 9, DC)
 CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC)
 #endif
 #endif /* !__mips_single_float */
+
+#ifdef L_m16rdhwr
+STARTFN (__mips16_rdhwr)
+       /* Forced always hidden, to avoid exporting from shared libgcc.  */
+       .hidden __mips16_rdhwr
+       .set    push
+       .set    mips32r2
+       .set    noreorder
+       rdhwr   $3,$29
+       .set    pop
+       j       $31
+       ENDFN (__mips16_rdhwr)
 #endif
+
+#endif
Index: libgcc/config/mips/crtfastmath.c
===================================================================
--- libgcc/config/mips/crtfastmath.c    (revision 182952)
+++ libgcc/config/mips/crtfastmath.c    (working copy)
@@ -39,7 +39,7 @@
 #define _FPU_GETCW(cw) __asm__ ("cfc1 %0,$31" : "=r" (cw))
 #define _FPU_SETCW(cw) __asm__ ("ctc1 %0,$31" : : "r" (cw))
 
-static void __attribute__((constructor))
+static void __attribute__ ((constructor,nomips16))
 set_fast_math (void)
 {
   unsigned int fcr;
Index: libgomp/config/linux/mips/futex.h
===================================================================
--- libgomp/config/linux/mips/futex.h   (revision 182952)
+++ libgomp/config/linux/mips/futex.h   (working copy)
@@ -28,20 +28,26 @@
 #define FUTEX_WAIT 0
 #define FUTEX_WAKE 1
 
+#ifdef __mips16
+static void __attribute__((noinline,nomips16))
+#else
 static inline void
+#endif
 sys_futex0 (int *addr, int op, int val)
 {
-  register unsigned long __v0 asm("$2") = (unsigned long) SYS_futex;
+  register unsigned long __v0 asm("$2");
   register unsigned long __a0 asm("$4") = (unsigned long) addr;
   register unsigned long __a1 asm("$5") = (unsigned long) op;
   register unsigned long __a2 asm("$6") = (unsigned long) val;
   register unsigned long __a3 asm("$7") = 0;
 
-  __asm volatile ("syscall"
+  __asm volatile ("li $2, %6\n\t"
+                 "syscall"
                  /* returns $a3 (errno), $v0 (return value) */
                  : "=r" (__v0), "=r" (__a3)
-                 /* arguments in v0 (syscall) a0-a3 */
-                 : "r" (__v0), "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a3)
+                 /* arguments in a0-a3, and syscall number */
+                 : "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a3),
+                    "IK" (SYS_futex)
                  /* clobbers at, v1, t0-t9, memory */
                  : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14",
                    "$15", "$24", "$25", "memory");

Reply via email to