Submitted By: Jim Gifford (patches at jg555 dot com) Date: 2005-09-12 Initial Package Version: 4.0.1 Origin: Daniel Jacobowitz Upstream Status: GCC Dev Mailling lists Description: Adds support for TLS on MIPS diff -Naur gcc-4.0.1.orig/gcc/config/mips/mips-protos.h gcc-4.0.1/gcc/config/mips/mips-protos.h --- gcc-4.0.1.orig/gcc/config/mips/mips-protos.h 2004-12-30 03:07:56.000000000 +0000 +++ gcc-4.0.1/gcc/config/mips/mips-protos.h 2005-09-12 23:27:07.000000000 +0000 @@ -61,6 +61,17 @@ An UNSPEC wrapper around a function's address. It represents the offset of _gp from the start of the function. + SYMBOL_TLS + A thread-local symbol. + + SYMBOL_TLSGD + SYMBOL_TLSLDM + SYMBOL_DTPREL + SYMBOL_GOTTPREL + SYMBOL_TPREL + UNSPEC wrappers around SYMBOL_TLS, corresponding to the + thread-local storage relocation operators. + SYMBOL_64_HIGH For a 64-bit symbolic address X, this is the value of (%highest(X) << 16) + %higher(X). @@ -82,6 +93,12 @@ SYMBOL_GOTOFF_GLOBAL, SYMBOL_GOTOFF_CALL, SYMBOL_GOTOFF_LOADGP, + SYMBOL_TLS, + SYMBOL_TLSGD, + SYMBOL_TLSLDM, + SYMBOL_DTPREL, + SYMBOL_GOTTPREL, + SYMBOL_TPREL, SYMBOL_64_HIGH, SYMBOL_64_MID, SYMBOL_64_LOW diff -Naur gcc-4.0.1.orig/gcc/config/mips/mips.c gcc-4.0.1/gcc/config/mips/mips.c --- gcc-4.0.1.orig/gcc/config/mips/mips.c 2005-05-08 11:56:53.000000000 +0000 +++ gcc-4.0.1/gcc/config/mips/mips.c 2005-09-12 23:27:07.000000000 +0000 @@ -236,6 +236,7 @@ static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode); static bool mips_classify_address (struct mips_address_info *, rtx, enum machine_mode, int); +static bool mips_cannot_force_const_mem (rtx); static int mips_symbol_insns (enum mips_symbol_type); static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx); static rtx mips_force_temporary (rtx, rtx); @@ -601,7 +602,7 @@ /* Map hard register number to register class */ const enum reg_class mips_regno_to_class[] = { - LEA_REGS, LEA_REGS, M16_NA_REGS, M16_NA_REGS, + LEA_REGS, LEA_REGS, M16_NA_REGS, V1_REG, M16_REGS, M16_REGS, M16_REGS, M16_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, @@ -817,6 +818,12 @@ #undef TARGET_EXPAND_BUILTIN #define TARGET_EXPAND_BUILTIN mips_expand_builtin +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS HAVE_AS_TLS + +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM mips_cannot_force_const_mem + struct gcc_target targetm = TARGET_INITIALIZER; /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */ @@ -835,6 +842,9 @@ gcc_assert (GET_CODE (x) == SYMBOL_REF); + if (SYMBOL_REF_TLS_MODEL (x)) + return SYMBOL_TLS; + if (CONSTANT_POOL_ADDRESS_P (x)) { if (TARGET_MIPS16) @@ -943,7 +953,11 @@ if (UNSPEC_ADDRESS_P (x)) *symbol_type = UNSPEC_ADDRESS_TYPE (x); else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) - *symbol_type = mips_classify_symbol (x); + { + *symbol_type = mips_classify_symbol (x); + if (*symbol_type == SYMBOL_TLS) + return false; + } else return false; @@ -993,6 +1007,12 @@ case SYMBOL_GOTOFF_GLOBAL: case SYMBOL_GOTOFF_CALL: case SYMBOL_GOTOFF_LOADGP: + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: + case SYMBOL_DTPREL: + case SYMBOL_TPREL: + case SYMBOL_GOTTPREL: + case SYMBOL_TLS: return false; } gcc_unreachable (); @@ -1090,6 +1110,14 @@ /* The address will have to be loaded from the GOT first. */ return false; + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: + case SYMBOL_DTPREL: + case SYMBOL_TPREL: + case SYMBOL_GOTTPREL: + case SYMBOL_TLS: + return false; + case SYMBOL_GOTOFF_PAGE: case SYMBOL_GOTOFF_GLOBAL: case SYMBOL_GOTOFF_CALL: @@ -1102,6 +1130,33 @@ gcc_unreachable (); } +/* Return true if X is a thread-local symbol. */ + +static bool +mips_tls_operand_p (rtx x) +{ + return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0; +} + +/* Return true if X can not be forced into a constant pool. */ + +static int +mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED) +{ + return mips_tls_operand_p (*x); +} + +/* Return true if X can not be forced into a constant pool. */ + +static bool +mips_cannot_force_const_mem (rtx x) +{ + if (! TARGET_HAVE_TLS) + return false; + + return for_each_rtx (&x, &mips_tls_symbol_ref_1, 0); +} + /* Return true if X is a valid address for machine mode MODE. If it is, fill in INFO appropriately. STRICT is true if we should only accept @@ -1223,8 +1278,19 @@ case SYMBOL_64_HIGH: case SYMBOL_64_MID: case SYMBOL_64_LOW: + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: + case SYMBOL_DTPREL: + case SYMBOL_GOTTPREL: + case SYMBOL_TPREL: /* Check whether the offset is a 16- or 32-bit value. */ return mips_split_p[type] ? 2 : 1; + + case SYMBOL_TLS: + /* We don't treat a bare TLS symbol as a constant. */ + return 0; + + return 1; } gcc_unreachable (); } @@ -1521,6 +1587,114 @@ return plus_constant (reg, offset); } +/* Emit a call to __tls_get_addr. SYM is the TLS symbol we are + referencing, and TYPE is the symbol type to use (either global + dynamic or local dynamic). V0 is an RTX for the return value + location. The entire insn sequence is returned. */ + +static GTY(()) rtx mips_tls_symbol; + +static rtx +mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) +{ + rtx insn, loc, tga, a0; + + a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); + + if (!mips_tls_symbol) + mips_tls_symbol = init_one_libfunc ("__tls_get_addr"); + + loc = mips_unspec_address (sym, type); + + start_sequence (); + + emit_insn (gen_rtx_SET (Pmode, a0, + gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc))); + tga = gen_rtx_MEM (Pmode, mips_tls_symbol); + insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx)); + CONST_OR_PURE_CALL_P (insn) = 1; + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), v0); + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); + insn = get_insns (); + + end_sequence (); + + return insn; +} + +/* Generate the code to access LOC, a thread local SYMBOL_REF. The + return value will be a valid address and move_operand (either a REG + or a LO_SUM). */ + +static rtx +mips_legitimize_tls_address (rtx loc) +{ + rtx dest, insn, v0, v1, tmp1, tmp2, eqv; + enum tls_model model; + + v0 = gen_rtx_REG (Pmode, GP_RETURN); + v1 = gen_rtx_REG (Pmode, GP_RETURN + 1); + + model = SYMBOL_REF_TLS_MODEL (loc); + + switch (model) + { + case TLS_MODEL_GLOBAL_DYNAMIC: + insn = mips_call_tls_get_addr (loc, SYMBOL_TLSGD, v0); + dest = gen_reg_rtx (Pmode); + emit_libcall_block (insn, dest, v0, loc); + break; + + case TLS_MODEL_LOCAL_DYNAMIC: + insn = mips_call_tls_get_addr (loc, SYMBOL_TLSLDM, v0); + tmp1 = gen_reg_rtx (Pmode); + + /* 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_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)); + break; + + case TLS_MODEL_INITIAL_EXEC: + tmp1 = gen_reg_rtx (Pmode); + tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL); + if (Pmode == DImode) + { + emit_insn (gen_tls_get_tp_di (v1)); + emit_insn (gen_load_gotdi (tmp1, pic_offset_table_rtx, tmp2)); + } + else + { + emit_insn (gen_tls_get_tp_si (v1)); + emit_insn (gen_load_gotsi (tmp1, pic_offset_table_rtx, tmp2)); + } + dest = gen_reg_rtx (Pmode); + emit_insn (gen_add3_insn (dest, tmp1, v1)); + break; + + case TLS_MODEL_LOCAL_EXEC: + + if (Pmode == DImode) + emit_insn (gen_tls_get_tp_di (v1)); + else + emit_insn (gen_tls_get_tp_si (v1)); + + tmp1 = mips_unspec_offset_high (NULL, v1, loc, SYMBOL_TPREL); + dest = gen_rtx_LO_SUM (Pmode, tmp1, + mips_unspec_address (loc, SYMBOL_TPREL)); + break; + + default: + abort (); + } + + return dest; +} /* This function is used to implement LEGITIMIZE_ADDRESS. If *XLOC can be legitimized in a way that the generic machinery might not expect, @@ -1532,6 +1706,12 @@ { enum mips_symbol_type symbol_type; + if (mips_tls_operand_p (*xloc)) + { + *xloc = mips_legitimize_tls_address (*xloc); + return true; + } + /* See if the address can split into a high part and a LO_SUM. */ if (mips_symbolic_constant_p (*xloc, &symbol_type) && mips_symbolic_address_p (symbol_type, mode) @@ -1707,6 +1887,12 @@ return; } + if (mips_tls_operand_p (src)) + { + emit_move_insn (dest, mips_legitimize_tls_address (src)); + return; + } + /* See if the symbol can be split. For mips16, this is often worse than forcing it in the constant pool since it needs the single-register form of addiu or daddiu. */ @@ -4321,6 +4507,7 @@ GR_REGS); mips_char_to_class['e'] = LEA_REGS; mips_char_to_class['j'] = PIC_FN_ADDR_REG; + mips_char_to_class['v'] = V1_REG; mips_char_to_class['y'] = GR_REGS; mips_char_to_class['z'] = ST_REGS; mips_char_to_class['B'] = COP0_REGS; @@ -4509,6 +4696,22 @@ mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel("; } + /* Thread-local relocation operators. */ + mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd("; + mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm("; + mips_split_p[SYMBOL_DTPREL] = 1; + 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] = 1; + mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi("; + mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo("; + + /* We don't have a thread pointer access instruction on MIPS16, or + appropriate TLS relocations. */ + if (TARGET_MIPS16) + targetm.have_tls = false; + /* Default to working around R4000 errata only if the processor was selected explicitly. */ if ((target_flags_explicit & MASK_FIX_R4000) == 0 diff -Naur gcc-4.0.1.orig/gcc/config/mips/mips.h gcc-4.0.1/gcc/config/mips/mips.h --- gcc-4.0.1.orig/gcc/config/mips/mips.h 2005-04-15 07:00:18.000000000 +0000 +++ gcc-4.0.1/gcc/config/mips/mips.h 2005-09-12 23:27:07.000000000 +0000 @@ -1714,6 +1714,7 @@ T_REG, /* mips16 T register ($24) */ M16_T_REGS, /* mips16 registers plus T register */ PIC_FN_ADDR_REG, /* SVR4 PIC function address register */ + V1_REG, /* Register $v1 ($3) used for TLS access. */ LEA_REGS, /* Every GPR except $25 */ GR_REGS, /* integer registers */ FP_REGS, /* floating point registers */ @@ -1752,6 +1753,7 @@ "T_REG", \ "M16_T_REGS", \ "PIC_FN_ADDR_REG", \ + "V1_REG", \ "LEA_REGS", \ "GR_REGS", \ "FP_REGS", \ @@ -1793,7 +1795,8 @@ { 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 T register */ \ { 0x010300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 and T regs */ \ { 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* SVR4 PIC function address register */ \ - { 0xfdffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* Every other GPR */ \ + { 0x00000008, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* only $v1 */ \ + { 0xfdffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* Every other GPR except $25 */ \ { 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* integer registers */ \ { 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* floating registers*/ \ { 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, /* hi register */ \ @@ -1849,6 +1852,7 @@ #define GR_REG_CLASS_P(CLASS) \ ((CLASS) == GR_REGS || (CLASS) == M16_REGS || (CLASS) == T_REG \ || (CLASS) == M16_T_REGS || (CLASS) == M16_NA_REGS \ + || (CLASS) == V1_REG \ || (CLASS) == PIC_FN_ADDR_REG || (CLASS) == LEA_REGS) /* This macro is also used later on in the file. */ @@ -1896,6 +1900,7 @@ 'f' Floating point registers 'h' Hi register 'l' Lo register + 'v' $v1 only 'x' Multiply/divide registers 'z' FP Status register 'B' Cop0 register @@ -3017,3 +3022,7 @@ " TEXT_SECTION_ASM_OP); #endif #endif + +#ifndef HAVE_AS_TLS +#define HAVE_AS_TLS 0 +#endif diff -Naur gcc-4.0.1.orig/gcc/config/mips/mips.md gcc-4.0.1/gcc/config/mips/mips.md --- gcc-4.0.1.orig/gcc/config/mips/mips.md 2005-05-08 11:56:58.000000000 +0000 +++ gcc-4.0.1/gcc/config/mips/mips.md 2005-09-12 23:29:17.000000000 +0000 @@ -45,6 +45,8 @@ (UNSPEC_LOAD_GOT 24) (UNSPEC_GP 25) (UNSPEC_MFHILO 26) + (UNSPEC_TLS_LDM 27) + (UNSPEC_TLS_GET_TP 28) (UNSPEC_ADDRESS_FIRST 100) @@ -3027,12 +3029,12 @@ ;; We could use MEMs, but an unspec gives more optimization ;; opportunities. -(define_insn "*load_got" +(define_insn "load_got" [(set (match_operand:P 0 "register_operand" "=d") (unspec:P [(match_operand:P 1 "register_operand" "d") (match_operand:P 2 "immediate_operand" "")] UNSPEC_LOAD_GOT))] - "TARGET_ABICALLS" + "" "\t%0,%R2(%1)" [(set_attr "type" "load") (set_attr "mode" "") @@ -5296,6 +5298,25 @@ [(match_dup 0)] { operands[0] = mips_rewrite_small_data (operands[0]); }) +; Thread-Local Storage + +; The TLS base pointer is acessed via "rdhwr $v1, $29". No current +; MIPS architecture defines this register, and no current +; implementation provides it; instead, any OS which supports TLS is +; expected to trap and emulate this instruction. rdhwr is part of the +; MIPS 32r2 specification, but we use it on any architecture because +; we expect it to be emulated. Use .set to force the assembler to +; accept it. + +(define_insn "tls_get_tp_" + [(set (match_operand:P 0 "register_operand" "=v") + (unspec:P [(const_int 0)] + UNSPEC_TLS_GET_TP))] + "HAVE_AS_TLS && !TARGET_MIPS16" + ".set\tpush\;.set\tmips32r2\t\;rdhwr\t%0,$29\;.set\tpop" + [(set_attr "type" "unknown") + (set_attr "mode" "")]) + ; The MIPS Paired-Single Floating Point and MIPS-3D Instructions. (include "mips-ps-3d.md") diff -Naur gcc-4.0.1.orig/gcc/configure gcc-4.0.1/gcc/configure --- gcc-4.0.1.orig/gcc/configure 2005-07-07 20:44:40.000000000 +0000 +++ gcc-4.0.1/gcc/configure 2005-09-12 23:27:07.000000000 +0000 @@ -13779,6 +13779,23 @@ tls_first_minor=13 tls_as_opt=--fatal-warnings ;; + mips*-*-*) + conftest_s=' + .section .tdata,"awT",@progbits +x: + .word 2 + .text + addiu $4, $28, %tlsgd(x) + addiu $4, $28, %tlsldm(x) + lui $4, %dtprel_hi(x) + addiu $4, $4, %dtprel_lo(x) + lw $4, %gottprel(x)($28) + lui $4, %tprel_hi(x) + addiu $4, $4, %tprel_lo(x)' + tls_first_major=2 + tls_first_minor=16 + tls_as_opt='-32 --fatal-warnings' + ;; powerpc-*-*) conftest_s=' .section ".tdata","awT",@progbits diff -Naur gcc-4.0.1.orig/gcc/configure.ac gcc-4.0.1/gcc/configure.ac --- gcc-4.0.1.orig/gcc/configure.ac 2005-06-01 08:14:33.000000000 +0000 +++ gcc-4.0.1/gcc/configure.ac 2005-09-12 23:27:07.000000000 +0000 @@ -2349,6 +2349,23 @@ tls_first_minor=13 tls_as_opt=--fatal-warnings ;; + mips*-*-*) + conftest_s=' + .section .tdata,"awT",@progbits +x: + .word 2 + .text + addiu $4, $28, %tlsgd(x) + addiu $4, $28, %tlsldm(x) + lui $4, %dtprel_hi(x) + addiu $4, $4, %dtprel_lo(x) + lw $4, %gottprel(x)($28) + lui $4, %tprel_hi(x) + addiu $4, $4, %tprel_lo(x)' + tls_first_major=2 + tls_first_minor=16 + tls_as_opt='-32 --fatal-warnings' + ;; powerpc-*-*) conftest_s=' .section ".tdata","awT",@progbits diff -Naur gcc-4.0.1.orig/gcc/testsuite/gcc.dg/tls/offset-1.c gcc-4.0.1/gcc/testsuite/gcc.dg/tls/offset-1.c --- gcc-4.0.1.orig/gcc/testsuite/gcc.dg/tls/offset-1.c 1970-01-01 00:00:00.000000000 +0000 +++ gcc-4.0.1/gcc/testsuite/gcc.dg/tls/offset-1.c 2005-09-12 23:27:07.000000000 +0000 @@ -0,0 +1,15 @@ +/* { dg-do link } */ +/* Test that we do not emit a TLS reference into a constant pool. */ + +__thread int x[2]; + +int * __attribute__((noinline)) +foo (void) +{ + return &x[1]; +} + +int main (void) +{ + return *foo (); +}