===================================================================
@@ -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))
===================================================================
@@ -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"))
===================================================================
@@ -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.
===================================================================
@@ -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
===================================================================
@@ -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
===================================================================
@@ -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