This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH/RFA] SH TLS support
- From: kaz Kojima <kkojima at rr dot iij4u dot or dot jp>
- To: gcc-patches at gcc dot gnu dot org
- Cc: joern dot rennecke at superh dot com, aoliva at redhat dot com
- Date: Tue, 18 Feb 2003 09:45:13 +0900
- Subject: [PATCH/RFA] SH TLS support
Hi,
This adds the TLS support for sh[34]-unknown-linux-gnu targets.
I've used it for glibc and nptl work for a while and could find no
obvious flaws.
It bootstrapped on i686-pc-linux-gnu and sh4-unknown-linux-gnu without
no new regressions. All TLS tests pass on sh4-unknown-linux-gnu. It
was also regtested on cross sh64-unknown-linux-gnu.
Ok for mainline?
Regards,
kaz
--
2003-02-18 Kaz Kojima <kkojima@gcc.gnu.org>
* config/sh/sh.c (TARGET_HAVE_TLS): Conditionally define.
(prepare_move_operands): Handle TLS operands.
(tls_symbolic_operand): New.
(nonpic_symbol_mentioned_p): Handle TLS UNSPECs.
(legitimize_pic_address): Do nothing for the TLS symbol.
(sh_encode_section_info): Handle TLS case.
(sh_strip_name_encoding): Drop TLS encoding.
* config/sh/sh-protos.h (tls_symbolic_operand): Add prototype.
* config/sh/sh.h (SH_TLS_ENCODING): Define.
(TLS_SYMNAME_P, STRIP_TLS_ENCODING): Likewise.
(ASM_OUTPUT_LABELREF): Drop TLS encoding.
(OUTPUT_ADDR_CONST_EXTRA): Handle TLS UNSPECs.
* config/sh/sh.md: Define TLS UNSPEC constants.
(type): Add tls_load.
("tls_global_dynamic", "tls_local_dynamic"): New insns.
("sym2DTPOFF", "symDTPOFF2reg", "sym2GOTTPOFF"): New expanders.
("tls_initial_exec"): New insn.
("sym2TPOFF", "symTPOFF2reg"): New expanders.
("load_gbr"): New insn.
* configure.in (HAVE_AS_TLS): Add sh-*-* and sh[34]*-*-* cases.
* configure: Regenerate.
diff -u3prN ORIG/gcc/gcc/config/sh/sh-protos.h LOCAL/gcc/gcc/config/sh/sh-protos.h
--- ORIG/gcc/gcc/config/sh/sh-protos.h Fri Feb 14 12:32:23 2003
+++ LOCAL/gcc/gcc/config/sh/sh-protos.h Mon Feb 17 10:49:06 2003
@@ -75,6 +75,7 @@ extern void fixup_addr_diff_vecs PARAMS
extern int get_dest_uid PARAMS ((rtx, int));
extern void final_prescan_insn PARAMS ((rtx, rtx *, int));
extern int symbol_ref_operand PARAMS ((rtx, enum machine_mode));
+extern int tls_symbolic_operand PARAMS ((rtx, enum machine_mode));
extern int system_reg_operand PARAMS ((rtx, enum machine_mode));
extern int general_movsrc_operand PARAMS ((rtx, enum machine_mode));
extern int general_movdst_operand PARAMS ((rtx, enum machine_mode));
diff -u3prN ORIG/gcc/gcc/config/sh/sh.c LOCAL/gcc/gcc/config/sh/sh.c
--- ORIG/gcc/gcc/config/sh/sh.c Sun Feb 16 19:06:33 2003
+++ LOCAL/gcc/gcc/config/sh/sh.c Mon Feb 17 10:53:49 2003
@@ -280,6 +280,11 @@ static int sh_address_cost PARAMS ((rtx)
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST sh_address_cost
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+#endif
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Print the operand address in x to the stream. */
@@ -708,7 +713,10 @@ prepare_move_operands (operands, mode)
rtx operands[];
enum machine_mode mode;
{
- if ((mode == SImode || mode == DImode) && flag_pic)
+ if ((mode == SImode || mode == DImode)
+ && flag_pic
+ && ! ((mode == Pmode || mode == ptr_mode)
+ && tls_symbolic_operand (operands[1], Pmode) != 0))
{
rtx temp;
if (SYMBOLIC_CONST_P (operands[1]))
@@ -758,6 +766,73 @@ prepare_move_operands (operands, mode)
operands[1] = copy_to_mode_reg (mode, operands[1]);
}
+ if (mode == Pmode || mode == ptr_mode)
+ {
+ rtx op0, op1;
+ enum tls_model tls_kind;
+
+ op0 = operands[0];
+ op1 = operands[1];
+ if ((tls_kind = tls_symbolic_operand (op1, Pmode)))
+ {
+ rtx tga_op1, tga_ret, tmp, tmp2;
+
+
+ switch (tls_kind)
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ emit_insn (gen_tls_global_dynamic (tga_ret, op1));
+ op1 = tga_ret;
+ break;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ emit_insn (gen_tls_local_dynamic (tga_ret, op1));
+
+ tmp = gen_reg_rtx (Pmode);
+ emit_move_insn (tmp, tga_ret);
+
+ if (register_operand (op0, Pmode))
+ tmp2 = op0;
+ else
+ tmp2 = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symDTPOFF2reg (tmp2, op1, tmp));
+ op1 = tmp2;
+ break;
+
+ case TLS_MODEL_INITIAL_EXEC:
+ if (! flag_pic)
+ emit_insn (gen_GOTaddr2picreg ());
+ tga_op1 = gen_reg_rtx (Pmode);
+ tmp = gen_sym2GOTTPOFF (op1);
+ emit_insn (gen_tls_initial_exec (tga_op1, tmp));
+ op1 = tga_op1;
+ break;
+
+ case TLS_MODEL_LOCAL_EXEC:
+ tmp2 = gen_reg_rtx (Pmode);
+ emit_insn (gen_load_gbr (tmp2));
+ tmp = gen_reg_rtx (Pmode);
+ emit_insn (gen_symTPOFF2reg (tmp, op1));
+ RTX_UNCHANGING_P (tmp) = 1;
+
+ if (register_operand (op0, Pmode))
+ op1 = op0;
+ else
+ op1 = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_addsi3 (op1, tmp, tmp2));
+ break;
+
+ default:
+ abort ();
+ }
+ operands[1] = op1;
+ }
+ }
+
return 0;
}
@@ -6427,6 +6502,36 @@ symbol_ref_operand (op, mode)
return (GET_CODE (op) == SYMBOL_REF);
}
+/* Return the TLS type for TLS symbols, 0 for otherwise. */
+int
+tls_symbolic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ const char *str;
+
+ if (GET_CODE (op) != SYMBOL_REF)
+ return 0;
+
+ str = XSTR (op, 0);
+ STRIP_DATALABEL_ENCODING(str, str);
+ if (! TLS_SYMNAME_P (str))
+ return 0;
+
+ switch (str[1])
+ {
+ case 'G':
+ return TLS_MODEL_GLOBAL_DYNAMIC;
+ case 'L':
+ return TLS_MODEL_LOCAL_DYNAMIC;
+ case 'i':
+ return TLS_MODEL_INITIAL_EXEC;
+ case 'l':
+ return TLS_MODEL_LOCAL_EXEC;
+ }
+ return 0;
+}
+
int
commutative_float_operator (op, mode)
rtx op;
@@ -7172,6 +7277,8 @@ nonpic_symbol_mentioned_p (x)
|| XINT (x, 1) == UNSPEC_GOT
|| XINT (x, 1) == UNSPEC_GOTOFF
|| XINT (x, 1) == UNSPEC_GOTPLT
+ || XINT (x, 1) == UNSPEC_GOTTPOFF
+ || XINT (x, 1) == UNSPEC_DTPOFF
|| XINT (x, 1) == UNSPEC_PLT))
return 0;
@@ -7201,6 +7308,9 @@ legitimize_pic_address (orig, mode, reg)
enum machine_mode mode ATTRIBUTE_UNUSED;
rtx reg;
{
+ if (tls_symbolic_operand (orig, Pmode))
+ return orig;
+
if (GET_CODE (orig) == LABEL_REF
|| (GET_CODE (orig) == SYMBOL_REF
&& (CONSTANT_POOL_ADDRESS_P (orig)
@@ -7553,6 +7663,60 @@ sh_encode_section_info (decl, first)
if (flag_pic)
SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
+ if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
+ {
+ const char *symbol_str, *orig_str;
+ bool is_local;
+ enum tls_model kind;
+ char encoding;
+ char *newstr;
+ size_t len, dlen;
+
+ orig_str = XSTR (symbol, 0);
+ is_local = (*targetm.binds_local_p) (decl);
+
+ if (! flag_pic)
+ {
+ if (is_local)
+ kind = TLS_MODEL_LOCAL_EXEC;
+ else
+ kind = TLS_MODEL_INITIAL_EXEC;
+ }
+ else if (is_local)
+ kind = TLS_MODEL_LOCAL_DYNAMIC;
+ else
+ kind = TLS_MODEL_GLOBAL_DYNAMIC;
+ if (kind < flag_tls_default)
+ kind = flag_tls_default;
+
+ STRIP_DATALABEL_ENCODING (symbol_str, orig_str);
+ dlen = symbol_str - orig_str;
+
+ encoding = " GLil"[kind];
+ if (TLS_SYMNAME_P (symbol_str))
+ {
+ if (encoding == symbol_str[1])
+ return;
+ /* Handle the changes from initial-exec to local-exec and
+ from global-dynamic to local-dynamic. */
+ if ((encoding == 'l' && symbol_str[1] == 'i')
+ || (encoding == 'L' && symbol_str[1] == 'G'))
+ symbol_str += 2;
+ else
+ abort ();
+ }
+
+ len = strlen (symbol_str);
+ newstr = alloca (dlen + len + 3);
+ if (dlen)
+ memcpy (newstr, orig_str, dlen);
+ newstr[dlen + 0] = SH_TLS_ENCODING[0];
+ newstr[dlen + 1] = encoding;
+ memcpy (newstr + dlen + 2, symbol_str, len + 1);
+
+ XSTR (symbol, 0) = ggc_alloc_string (newstr, dlen + len + 2);
+ }
+
if (TARGET_SH5 && first && TREE_CODE (decl) != FUNCTION_DECL)
XEXP (rtl, 0) = gen_datalabel_ref (symbol);
}
@@ -7564,6 +7728,7 @@ sh_strip_name_encoding (str)
const char *str;
{
STRIP_DATALABEL_ENCODING (str, str);
+ STRIP_TLS_ENCODING (str, str);
str += *str == '*';
return str;
}
diff -u3prN ORIG/gcc/gcc/config/sh/sh.h LOCAL/gcc/gcc/config/sh/sh.h
--- ORIG/gcc/gcc/config/sh/sh.h Mon Feb 17 09:22:10 2003
+++ LOCAL/gcc/gcc/config/sh/sh.h Mon Feb 17 09:23:39 2003
@@ -2752,6 +2752,20 @@ while (0)
((GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == LABEL_REF) \
&& nonpic_symbol_mentioned_p (X))
+/* TLS. */
+
+/* The prefix used to mark SYMBOL_REFs that refer to TLS symbols. */
+#define SH_TLS_ENCODING "@"
+
+/* Return true if SYM_NAME starts with SH_TLS_ENCODING. */
+#define TLS_SYMNAME_P(SYM_NAME) \
+ ((SYM_NAME)[0] == SH_TLS_ENCODING[0])
+
+/* Skip an optional SH_TLS_ENCODING in the beginning of SYM_NAME. */
+#define STRIP_TLS_ENCODING(VAR, SYM_NAME) \
+ (VAR) = (SYM_NAME) + (TLS_SYMNAME_P (SYM_NAME) \
+ ? strlen (SH_TLS_ENCODING) + 1 : 0)
+
/* Compute extra cost of moving data between one register class
and another. */
@@ -2915,6 +2929,7 @@ while (0)
const char * lname; \
\
STRIP_DATALABEL_ENCODING (lname, (NAME)); \
+ STRIP_TLS_ENCODING (lname, lname); \
if (lname[0] == '*') \
fputs (lname + 1, (FILE)); \
else \
@@ -3052,6 +3067,18 @@ while (0)
case UNSPEC_GOTPLT: \
output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
fputs ("@GOTPLT", (STREAM)); \
+ break; \
+ case UNSPEC_DTPOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@DTPOFF", (STREAM)); \
+ break; \
+ case UNSPEC_GOTTPOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@GOTTPOFF", (STREAM)); \
+ break; \
+ case UNSPEC_TPOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@TPOFF", (STREAM)); \
break; \
case UNSPEC_CALLER: \
{ \
diff -u3prN ORIG/gcc/gcc/config/sh/sh.md LOCAL/gcc/gcc/config/sh/sh.md
--- ORIG/gcc/gcc/config/sh/sh.md Fri Feb 14 12:32:23 2003
+++ LOCAL/gcc/gcc/config/sh/sh.md Sun Feb 16 14:41:22 2003
@@ -136,6 +136,12 @@
(UNSPEC_NSB 17)
(UNSPEC_ALLOCO 18)
(UNSPEC_EH_RETURN 19)
+ (UNSPEC_TLSGD 20)
+ (UNSPEC_TLSLDM 21)
+ (UNSPEC_TLSIE 22)
+ (UNSPEC_DTPOFF 23)
+ (UNSPEC_GOTTPOFF 24)
+ (UNSPEC_TPOFF 25)
;; These are used with unspec_volatile.
(UNSPECV_BLOCKAGE 0)
@@ -211,6 +217,7 @@
;; ftrc_s fix_truncsfsi2_i4
;; dfdiv double precision floating point divide (or square root)
;; cwb ic_invalidate_line_i
+;; tls_load load TLS related address
;; arith_media SHmedia arithmetic, logical, and shift instructions
;; cbranch_media SHmedia conditional branch instructions
;; cmp_media SHmedia compare instructions
@@ -241,7 +248,7 @@
;; nil no-op move, will be deleted.
(define_attr "type"
- "mt_group,cbranch,jump,jump_ind,arith,arith3,arith3b,dyn_shift,load,load_si,fload,store,move,fmove,smpy,dmpy,return,pload,prset,pstore,prget,pcload,pcload_si,pcfload,rte,sfunc,call,fp,fdiv,ftrc_s,dfp_arith,dfp_cmp,dfp_conv,dfdiv,gp_fpul,fpul_gp,mac_gp,mem_fpscr,gp_fpscr,cwb,arith_media,cbranch_media,cmp_media,dfdiv_media,dfmul_media,dfparith_media,dfpconv_media,dmpy_media,fcmp_media,fdiv_media,fload_media,fmove_media,fparith_media,fpconv_media,fstore_media,gettr_media,invalidate_line_media,jump_media,load_media,pt_media,ptabs_media,store_media,mcmp_media,mac_media,d2mpy_media,atrans_media,ustore_media,nil,other"
+ "mt_group,cbranch,jump,jump_ind,arith,arith3,arith3b,dyn_shift,load,load_si,fload,store,move,fmove,smpy,dmpy,return,pload,prset,pstore,prget,pcload,pcload_si,pcfload,rte,sfunc,call,fp,fdiv,ftrc_s,dfp_arith,dfp_cmp,dfp_conv,dfdiv,gp_fpul,fpul_gp,mac_gp,mem_fpscr,gp_fpscr,cwb,tls_load,arith_media,cbranch_media,cmp_media,dfdiv_media,dfmul_media,dfparith_media,dfpconv_media,dmpy_media,fcmp_media,fdiv_media,fload_media,fmove_media,fparith_media,fpconv_media,fstore_media,gettr_media,invalidate_line_media,jump_media,load_media,pt_media,ptabs_media,store_media,mcmp_media,mac_media,d2mpy_media,atrans_media,ustore_media,nil,other"
(const_string "other"))
;; We define a new attribute namely "insn_class".We use
@@ -6800,6 +6807,138 @@
[(const (unspec [(match_operand:SI 0 "" "")] UNSPEC_PIC))]
""
"")
+
+;; TLS code generation.
+
+(define_insn "tls_global_dynamic"
+ [(set (match_operand:SI 0 "register_operand" "=&z")
+ (unspec:SI [(match_operand:SI 1 "" "")]
+ UNSPEC_TLSGD))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (scratch:SI))]
+ "TARGET_SH1"
+ "*
+{
+ return \"\\
+mov.l\\t1f,r4\\n\\
+\\tmova\\t2f,r0\\n\\
+\\tmov.l\\t2f,r1\\n\\
+\\tadd\\tr0,r1\\n\\
+\\tjsr\\t@r1\\n\\
+\\tadd\\tr12,r4\\n\\
+\\tbra\\t3f\\n\\
+\\tnop\\n\\
+\\t.align\\t2\\n\\
+1:\\t.long\\t%a1@TLSGD\\n\\
+2:\\t.long\\t__tls_get_addr@PLT\\n\\
+3:\";
+}"
+ [(set_attr "type" "tls_load")
+ (set_attr "length" "26")])
+
+(define_insn "tls_local_dynamic"
+ [(set (match_operand:SI 0 "register_operand" "=&z")
+ (unspec:SI [(match_operand:SI 1 "" "")]
+ UNSPEC_TLSLDM))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (scratch:SI))]
+ "TARGET_SH1"
+ "*
+{
+ return \"\\
+mov.l\\t1f,r4\\n\\
+\\tmova\\t2f,r0\\n\\
+\\tmov.l\\t2f,r1\\n\\
+\\tadd\\tr0,r1\\n\\
+\\tjsr\\t@r1\\n\\
+\\tadd\\tr12,r4\\n\\
+\\tbra\\t3f\\n\\
+\\tnop\\n\\
+\\t.align\\t2\\n\\
+1:\\t.long\\t%a1@TLSLDM\\n\\
+2:\\t.long\\t__tls_get_addr@PLT\\n\\
+3:\";
+}"
+ [(set_attr "type" "tls_load")
+ (set_attr "length" "26")])
+
+(define_expand "sym2DTPOFF"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_DTPOFF))]
+ ""
+ "")
+
+(define_expand "symDTPOFF2reg"
+ [(match_operand 0 "" "") (match_operand 1 "" "") (match_operand 2 "" "")]
+ ""
+ "
+{
+ rtx dtpoffsym, insn;
+ rtx t = no_new_pseudos ? operands[0] : gen_reg_rtx (GET_MODE (operands[0]));
+
+ dtpoffsym = gen_sym2DTPOFF (operands[1]);
+ PUT_MODE (dtpoffsym, Pmode);
+ emit_move_insn (t, dtpoffsym);
+ insn = emit_move_insn (operands[0],
+ gen_rtx_PLUS (Pmode, t, operands[2]));
+ DONE;
+}")
+
+(define_expand "sym2GOTTPOFF"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_GOTTPOFF))]
+ ""
+ "")
+
+(define_insn "tls_initial_exec"
+ [(set (match_operand:SI 0 "register_operand" "=&r")
+ (unspec:SI [(match_operand:SI 1 "" "")]
+ UNSPEC_TLSIE))
+ (use (reg:SI GBR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI R0_REG))]
+ ""
+ "*
+{
+ return \"\\
+mov.l\\t1f,r0\\n\\
+\\tstc\\tgbr,%0\\n\\
+\\tmov.l\\t@(r0,r12),r0\\n\\
+\\tbra\\t2f\\n\\
+\\tadd\\tr0,%0\\n\\
+\\t.align\\t2\\n\\
+1:\\t.long\\t%a1\\n\\
+2:\";
+}"
+ [(set_attr "type" "tls_load")
+ (set_attr "length" "16")])
+
+(define_expand "sym2TPOFF"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_TPOFF))]
+ ""
+ "")
+
+(define_expand "symTPOFF2reg"
+ [(match_operand 0 "" "") (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx tpoffsym, insn;
+
+ tpoffsym = gen_sym2TPOFF (operands[1]);
+ PUT_MODE (tpoffsym, Pmode);
+ insn = emit_move_insn (operands[0], tpoffsym);
+ DONE;
+}")
+
+(define_insn "load_gbr"
+ [(set (match_operand:SI 0 "register_operand" "") (reg:SI GBR_REG))
+ (use (reg:SI GBR_REG))]
+ ""
+ "stc gbr,%0"
+ [(set_attr "type" "tls_load")])
;; case instruction for switch statements.
diff -u3prN ORIG/gcc/gcc/configure.in LOCAL/gcc/gcc/configure.in
--- ORIG/gcc/gcc/configure.in Sat Feb 15 18:39:53 2003
+++ LOCAL/gcc/gcc/configure.in Sun Feb 16 14:41:22 2003
@@ -1911,7 +1911,6 @@ foo: .long 25
tls_first_minor=13
;;
i[34567]86-*-*)
-changequote([,])dnl
conftest_s='
.section ".tdata","awT",@progbits
foo: .long 25
@@ -1996,6 +1995,20 @@ foo: .long 25
tls_first_major=2
tls_first_minor=14
;;
+ sh-*-* | sh[34]-*-*)
+ conftest_s='
+ .section ".tdata","awT",@progbits
+foo: .long 25
+ .text
+ .long foo@TLSGD
+ .long foo@TLSLDM
+ .long foo@DTPOFF
+ .long foo@GOTTPOFF
+ .long foo@TPOFF
+ tls_first_major=2
+ tls_first_minor=13
+ ;;
+changequote([,])dnl
esac
if test -z "$tls_first_major"; then
: