From 28633bbd10d6d729708c7bf4de2c1aeae3b4e75e Mon Sep 17 00:00:00 2001 From: Claudiu Zissulescu <claziss@synopsys.com> Date: Thu, 28 Apr 2016 13:53:46 +0200 Subject: [PATCH] [ARC] Add TLS support. gcc/ 2016-04-28 Claudiu Zissulescu <claziss@synopsys.com> Joern Rennecke <joern.rennecke@embecosm.com> * config/arc/arc-protos.h (arc_legitimize_pic_address): Remove declaration. (emit_pic_move): Remove. (arc_eh_uses, insn_is_tls_gd_dispatch): Declare. * config/arc/arc.c (emit_pic_move): Removed. (TARGET_HAVE_TLS): Define. (arc_conditional_register_usage): Test for arc_tp_regno. (arc_print_operand, arc_print_operand_address): Handle TLS unspecs. (arc_needs_pcl_p): New function. (arc_legitimate_pc_offset_p): Use arc_needs_pcl_p. (arc_legitimate_pic_addr_p): Handle TLS unspecs. (arc_raw_symbolic_reference_mentioned_p): Likewise. (arc_get_tp, arc_emit_call_tls_get_addr): New function. (arc_legitimize_tls_address): Likewise. (DTPOFF_ZERO_SYM): Define. (arc_legitimize_pic_address): Make it static, handle TLS cases. (arc_output_pic_addr_const): Print TLS unspecs. (prepare_pic_move): New function, replaces emit_pic_move. (arc_legitimate_constant_p): Handle TLS unspecs. (arc_legitimate_address_p): Likewise. (arc_rewrite_small_data_p): Use assert for TLS constants. (prepare_move_operands): Use prepare_pic_move. (arc_legitimize_address): Legitimize tls addresses. (arc_epilogue_uses): Check for arc_tp_regno. (arc_eh_uses, insn_is_tls_gd_dispatch): New function. * config/arc/arc.h [DEFAULT_LIBC != LIBC_UCLIBC] (EXTRA_SPECS): Define. [DEFAULT_LIBC != LIBC_UCLIBC] (ARC_TLS_EXTRA_START_SPEC): Likewise. [DEFAULT_LIBC != LIBC_UCLIBC] (STARTFILE_SPEC): Add %(arc_tls_extra_start_spec). (TARGET_CPU_CPP_BUILTINS): Define __ARC_TLS_REGNO__. (REGNO_OK_FOR_BASE_P): Check for arc_tp_regno. (EH_USES): Define. (INSN_REFERENCES_ARE_DELAYED): Use insn_is_tls_gd_dispatch. * config/arc/arc.md (UNSPEC_TLS_GD, UNSPEC_TLS_LD, UNSPEC_TLS_IE) (UNSPEC_TLS_OFF): Add. (R10_REG): Define. (tls_load_tp_soft, tls_gd_load, tls_gd_get_addr, tls_gd_dispatch) (get_thread_pointersi): New patterns. * config/arc/arc.opt (mtp-regno): New option. * config/arc/predicates.md (move_src_operand): Handle TLS symbols. (move_dest_operand): Likewise. * configure: Regenerate. * configure.ac: Add arc*-*-* case to test for tls. * doc/invoke.texi (ARC options): Document mtp-regno. Co-Authored-By: Joern Rennecke <joern.rennecke@embecosm.com> From-SVN: r235559 --- gcc/ChangeLog | 51 ++++ gcc/config/arc/arc-protos.h | 4 +- gcc/config/arc/arc.c | 505 +++++++++++++++++++++++++---------- gcc/config/arc/arc.h | 22 +- gcc/config/arc/arc.md | 71 +++++ gcc/config/arc/arc.opt | 7 + gcc/config/arc/predicates.md | 15 +- gcc/configure | 6 + gcc/configure.ac | 6 + gcc/doc/invoke.texi | 6 +- 10 files changed, 545 insertions(+), 148 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f557c9ff4537..367eb8bff75a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,54 @@ +2016-04-28 Claudiu Zissulescu <claziss@synopsys.com> + Joern Rennecke <joern.rennecke@embecosm.com> + + * config/arc/arc-protos.h (arc_legitimize_pic_address): Remove + declaration. + (emit_pic_move): Remove. + (arc_eh_uses, insn_is_tls_gd_dispatch): Declare. + * config/arc/arc.c (emit_pic_move): Removed. + (TARGET_HAVE_TLS): Define. + (arc_conditional_register_usage): Test for arc_tp_regno. + (arc_print_operand, arc_print_operand_address): Handle TLS + unspecs. + (arc_needs_pcl_p): New function. + (arc_legitimate_pc_offset_p): Use arc_needs_pcl_p. + (arc_legitimate_pic_addr_p): Handle TLS unspecs. + (arc_raw_symbolic_reference_mentioned_p): Likewise. + (arc_get_tp, arc_emit_call_tls_get_addr): New function. + (arc_legitimize_tls_address): Likewise. + (DTPOFF_ZERO_SYM): Define. + (arc_legitimize_pic_address): Make it static, handle TLS cases. + (arc_output_pic_addr_const): Print TLS unspecs. + (prepare_pic_move): New function, replaces emit_pic_move. + (arc_legitimate_constant_p): Handle TLS unspecs. + (arc_legitimate_address_p): Likewise. + (arc_rewrite_small_data_p): Use assert for TLS constants. + (prepare_move_operands): Use prepare_pic_move. + (arc_legitimize_address): Legitimize tls addresses. + (arc_epilogue_uses): Check for arc_tp_regno. + (arc_eh_uses, insn_is_tls_gd_dispatch): New function. + * config/arc/arc.h [DEFAULT_LIBC != LIBC_UCLIBC] (EXTRA_SPECS): + Define. + [DEFAULT_LIBC != LIBC_UCLIBC] (ARC_TLS_EXTRA_START_SPEC): + Likewise. + [DEFAULT_LIBC != LIBC_UCLIBC] (STARTFILE_SPEC): Add + %(arc_tls_extra_start_spec). + (TARGET_CPU_CPP_BUILTINS): Define __ARC_TLS_REGNO__. + (REGNO_OK_FOR_BASE_P): Check for arc_tp_regno. + (EH_USES): Define. + (INSN_REFERENCES_ARE_DELAYED): Use insn_is_tls_gd_dispatch. + * config/arc/arc.md (UNSPEC_TLS_GD, UNSPEC_TLS_LD, UNSPEC_TLS_IE) + (UNSPEC_TLS_OFF): Add. + (R10_REG): Define. + (tls_load_tp_soft, tls_gd_load, tls_gd_get_addr, tls_gd_dispatch) + (get_thread_pointersi): New patterns. + * config/arc/arc.opt (mtp-regno): New option. + * config/arc/predicates.md (move_src_operand): Handle TLS symbols. + (move_dest_operand): Likewise. + * configure: Regenerate. + * configure.ac: Add arc*-*-* case to test for tls. + * doc/invoke.texi (ARC options): Document mtp-regno. + 2016-04-28 Claudiu Zissulescu <claziss@synopsys.com> * config/arc/arc.c (arc_vector_mode_supported_p): Add support for diff --git a/gcc/config/arc/arc-protos.h b/gcc/config/arc/arc-protos.h index f487291d35d7..3bf28a088bd9 100644 --- a/gcc/config/arc/arc-protos.h +++ b/gcc/config/arc/arc-protos.h @@ -57,7 +57,6 @@ extern unsigned int arc_compute_frame_size (int); extern bool arc_ccfsm_branch_deleted_p (void); extern void arc_ccfsm_record_branch_deleted (void); -extern rtx arc_legitimize_pic_address (rtx, rtx); void arc_asm_output_aligned_decl_local (FILE *, tree, const char *, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, @@ -68,7 +67,6 @@ extern bool check_if_valid_sleep_operand (rtx *, int); extern bool arc_legitimate_constant_p (machine_mode, rtx); extern bool arc_legitimate_pc_offset_p (rtx); extern bool arc_legitimate_pic_addr_p (rtx); -extern void emit_pic_move (rtx *, machine_mode); extern bool arc_raw_symbolic_reference_mentioned_p (rtx, bool); extern bool arc_legitimate_pic_operand_p (rtx); extern bool arc_is_longcall_p (rtx); @@ -118,8 +116,10 @@ extern bool arc_text_label (rtx_insn *insn); extern int arc_decl_pretend_args (tree decl); extern bool arc_short_comparison_p (rtx, int); extern bool arc_epilogue_uses (int regno); +extern bool arc_eh_uses (int regno); /* insn-attrtab.c doesn't include reload.h, which declares regno_clobbered_p. */ extern int regno_clobbered_p (unsigned int, rtx_insn *, machine_mode, int); extern int arc_return_slot_offset (void); extern bool arc_legitimize_reload_address (rtx *, machine_mode, int, int); extern void arc_secondary_reload_conv (rtx, rtx, rtx, bool); +extern bool insn_is_tls_gd_dispatch (rtx_insn *); diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c index d120946a5f2f..d40d54deb34a 100644 --- a/gcc/config/arc/arc.c +++ b/gcc/config/arc/arc.c @@ -217,7 +217,6 @@ static rtx arc_expand_builtin (tree, rtx, rtx, machine_mode, int); static int branch_dest (rtx); static void arc_output_pic_addr_const (FILE *, rtx, int); -void emit_pic_move (rtx *, machine_mode); bool arc_legitimate_pic_operand_p (rtx); static bool arc_function_ok_for_sibcall (tree, tree); static rtx arc_function_value (const_tree, const_tree, bool); @@ -457,6 +456,11 @@ static void arc_finalize_pic (void); #undef TARGET_ASM_ALIGNED_SI_OP #define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" +#ifdef HAVE_AS_TLS +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS HAVE_AS_TLS +#endif + #undef TARGET_DWARF_REGISTER_SPAN #define TARGET_DWARF_REGISTER_SPAN arc_dwarf_register_span @@ -1322,6 +1326,14 @@ arc_conditional_register_usage (void) strcpy (rname58, TARGET_BIG_ENDIAN ? "mhi" : "mlo"); strcpy (rname59, TARGET_BIG_ENDIAN ? "mlo" : "mhi"); } + + /* The nature of arc_tp_regno is actually something more like a global + register, however globalize_reg requires a declaration. + We use EPILOGUE_USES to compensate so that sets from + __builtin_set_frame_pointer are not deleted. */ + if (arc_tp_regno != -1) + fixed_regs[arc_tp_regno] = call_used_regs[arc_tp_regno] = 1; + if (TARGET_MULMAC_32BY16_SET) { fix_start = 56; @@ -3411,7 +3423,16 @@ arc_print_operand (FILE *file, rtx x, int code) } /* Fall through. Let output_addr_const deal with it. */ default : - if (flag_pic) + if (flag_pic + || (GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == UNSPEC + && (XINT (XEXP (x, 0), 1) == UNSPEC_TLS_OFF + || XINT (XEXP (x, 0), 1) == UNSPEC_TLS_GD)) + || (GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == UNSPEC + && (XINT (XEXP (XEXP (x, 0), 0), 1) == UNSPEC_TLS_OFF + || XINT (XEXP (XEXP (x, 0), 0), 1) == UNSPEC_TLS_GD))) arc_output_pic_addr_const (file, x, code); else { @@ -3476,6 +3497,17 @@ arc_print_operand_address (FILE *file , rtx addr) { rtx c = XEXP (addr, 0); + if ((GET_CODE (c) == UNSPEC + && (XINT (c, 1) == UNSPEC_TLS_OFF + || XINT (c, 1) == UNSPEC_TLS_IE)) + || (GET_CODE (c) == PLUS + && GET_CODE (XEXP (c, 0)) == UNSPEC + && (XINT (XEXP (c, 0), 1) == UNSPEC_TLS_OFF))) + { + arc_output_pic_addr_const (file, c, 0); + break; + } + gcc_assert (GET_CODE (c) == PLUS); gcc_assert (GET_CODE (XEXP (c, 0)) == SYMBOL_REF); gcc_assert (GET_CODE (XEXP (c, 1)) == CONST_INT); @@ -4547,6 +4579,44 @@ arc_rtx_costs (rtx x, machine_mode mode, int outer_code, } } +/* Helper used by arc_legitimate_pc_offset_p. */ + +static bool +arc_needs_pcl_p (rtx x) +{ + register const char *fmt; + register int i, j; + + if ((GET_CODE (x) == UNSPEC) + && (XVECLEN (x, 0) == 1) + && (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF)) + switch (XINT (x, 1)) + { + case ARC_UNSPEC_GOT: + case UNSPEC_TLS_GD: + case UNSPEC_TLS_IE: + return true; + default: + break; + } + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (arc_needs_pcl_p (XEXP (x, i))) + return true; + } + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (arc_needs_pcl_p (XVECEXP (x, i, j))) + return true; + } + + return false; +} + /* Return true if ADDR is an address that needs to be expressed as an explicit sum of pcl + offset. */ @@ -4555,17 +4625,8 @@ arc_legitimate_pc_offset_p (rtx addr) { if (GET_CODE (addr) != CONST) return false; - addr = XEXP (addr, 0); - if (GET_CODE (addr) == PLUS) - { - if (GET_CODE (XEXP (addr, 1)) != CONST_INT) - return false; - addr = XEXP (addr, 0); - } - return (GET_CODE (addr) == UNSPEC - && XVECLEN (addr, 0) == 1 - && XINT (addr, 1) == ARC_UNSPEC_GOT - && GET_CODE (XVECEXP (addr, 0, 0)) == SYMBOL_REF); + + return arc_needs_pcl_p (addr); } /* Return true if ADDR is a valid pic address. @@ -4594,9 +4655,11 @@ arc_legitimate_pic_addr_p (rtx addr) || XVECLEN (addr, 0) != 1) return false; - /* Must be @GOT or @GOTOFF. */ + /* Must be one of @GOT, @GOTOFF, @tlsgd, tlsie. */ if (XINT (addr, 1) != ARC_UNSPEC_GOT - && XINT (addr, 1) != ARC_UNSPEC_GOTOFF) + && XINT (addr, 1) != ARC_UNSPEC_GOTOFF + && XINT (addr, 1) != UNSPEC_TLS_GD + && XINT (addr, 1) != UNSPEC_TLS_IE) return false; if (GET_CODE (XVECEXP (addr, 0, 0)) != SYMBOL_REF @@ -4654,6 +4717,10 @@ arc_raw_symbolic_reference_mentioned_p (rtx op, bool skip_local) if (GET_CODE (op) == SYMBOL_REF) { + if (SYMBOL_REF_TLS_MODEL (op)) + return true; + if (!flag_pic) + return false; tree decl = SYMBOL_REF_DECL (op); return !skip_local || !decl || !default_binds_local_p (decl); } @@ -4680,11 +4747,114 @@ arc_raw_symbolic_reference_mentioned_p (rtx op, bool skip_local) return false; } +/* Get the thread pointer. */ + +static rtx +arc_get_tp (void) +{ + /* If arc_tp_regno has been set, we can use that hard register + directly as a base register. */ + if (arc_tp_regno != -1) + return gen_rtx_REG (Pmode, arc_tp_regno); + + /* Otherwise, call __read_tp. Copy the result to a pseudo to avoid + conflicts with function arguments / results. */ + rtx reg = gen_reg_rtx (Pmode); + emit_insn (gen_tls_load_tp_soft ()); + emit_move_insn (reg, gen_rtx_REG (Pmode, R0_REG)); + return reg; +} + +/* Helper to be used by TLS Global dynamic model. */ + +static rtx +arc_emit_call_tls_get_addr (rtx sym, int reloc, rtx eqv) +{ + rtx r0 = gen_rtx_REG (Pmode, R0_REG); + rtx insns; + rtx call_fusage = NULL_RTX; + + start_sequence (); + + rtx x = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym), reloc); + x = gen_rtx_CONST (Pmode, x); + emit_move_insn (r0, x); + use_reg (&call_fusage, r0); + + gcc_assert (reloc == UNSPEC_TLS_GD); + rtx call_insn = emit_call_insn (gen_tls_gd_get_addr (sym)); + /* Should we set RTL_CONST_CALL_P? We read memory, but not in a + way that the application should care. */ + RTL_PURE_CALL_P (call_insn) = 1; + add_function_usage_to (call_insn, call_fusage); + + insns = get_insns (); + end_sequence (); + + rtx dest = gen_reg_rtx (Pmode); + emit_libcall_block (insns, dest, r0, eqv); + return dest; +} + +#define DTPOFF_ZERO_SYM ".tdata" + +/* Return a legitimized address for ADDR, + which is a SYMBOL_REF with tls_model MODEL. */ + +static rtx +arc_legitimize_tls_address (rtx addr, enum tls_model model) +{ + if (!flag_pic && model == TLS_MODEL_LOCAL_DYNAMIC) + model = TLS_MODEL_LOCAL_EXEC; + + switch (model) + { + case TLS_MODEL_LOCAL_DYNAMIC: + rtx base; + tree decl; + const char *base_name; + rtvec v; + + decl = SYMBOL_REF_DECL (addr); + base_name = DTPOFF_ZERO_SYM; + if (decl && bss_initializer_p (decl)) + base_name = ".tbss"; + + base = gen_rtx_SYMBOL_REF (Pmode, base_name); + if (strcmp (base_name, DTPOFF_ZERO_SYM) == 0) + { + if (!flag_pic) + goto local_exec; + v = gen_rtvec (1, addr); + } + else + v = gen_rtvec (2, addr, base); + addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_TLS_OFF); + addr = gen_rtx_CONST (Pmode, addr); + base = arc_legitimize_tls_address (base, TLS_MODEL_GLOBAL_DYNAMIC); + return gen_rtx_PLUS (Pmode, force_reg (Pmode, base), addr); + case TLS_MODEL_GLOBAL_DYNAMIC: + return arc_emit_call_tls_get_addr (addr, UNSPEC_TLS_GD, addr); + case TLS_MODEL_INITIAL_EXEC: + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLS_IE); + addr = gen_rtx_CONST (Pmode, addr); + addr = copy_to_mode_reg (Pmode, gen_const_mem (Pmode, addr)); + return gen_rtx_PLUS (Pmode, arc_get_tp (), addr); + case TLS_MODEL_LOCAL_EXEC: + local_exec: + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLS_OFF); + addr = gen_rtx_CONST (Pmode, addr); + return gen_rtx_PLUS (Pmode, arc_get_tp (), addr); + default: + gcc_unreachable (); + } +} + /* Legitimize a pic address reference in ORIG. The return value is the legitimated address. If OLDX is non-zero, it is the target to assign the address to first. */ -rtx +static rtx arc_legitimize_pic_address (rtx orig, rtx oldx) { rtx addr = orig; @@ -4696,40 +4866,36 @@ arc_legitimize_pic_address (rtx orig, rtx oldx) if (GET_CODE (addr) == LABEL_REF) ; /* Do nothing. */ - else if (GET_CODE (addr) == SYMBOL_REF - && (CONSTANT_POOL_ADDRESS_P (addr) - || SYMBOL_REF_LOCAL_P (addr))) + else if (GET_CODE (addr) == SYMBOL_REF) { - /* This symbol may be referenced via a displacement from the PIC - base address (@GOTOFF). */ - - /* FIXME: if we had a way to emit pc-relative adds that don't - create a GOT entry, we could do without the use of the gp register. */ - crtl->uses_pic_offset_table = 1; - pat = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), ARC_UNSPEC_GOTOFF); - pat = gen_rtx_CONST (Pmode, pat); - pat = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pat); - - if (oldx == NULL) - oldx = gen_reg_rtx (Pmode); - - if (oldx != 0) + enum tls_model model = SYMBOL_REF_TLS_MODEL (addr); + if (model != 0) + return arc_legitimize_tls_address (addr, model); + else if (!flag_pic) + return orig; + else if (CONSTANT_POOL_ADDRESS_P (addr) || SYMBOL_REF_LOCAL_P (addr)) { - emit_move_insn (oldx, pat); - pat = oldx; + /* This symbol may be referenced via a displacement from the + PIC base address (@GOTOFF). */ + + /* FIXME: if we had a way to emit pc-relative adds that + don't create a GOT entry, we could do without the use of + the gp register. */ + crtl->uses_pic_offset_table = 1; + pat = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), ARC_UNSPEC_GOTOFF); + pat = gen_rtx_CONST (Pmode, pat); + pat = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pat); + } + else + { + /* This symbol must be referenced via a load from the + Global Offset Table (@GOTPC). */ + pat = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), ARC_UNSPEC_GOT); + pat = gen_rtx_CONST (Pmode, pat); + pat = gen_const_mem (Pmode, pat); } - } - else if (GET_CODE (addr) == SYMBOL_REF) - { - /* This symbol must be referenced via a load from the - Global Offset Table (@GOTPC). */ - - pat = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), ARC_UNSPEC_GOT); - pat = gen_rtx_CONST (Pmode, pat); - pat = gen_const_mem (Pmode, pat); - - if (oldx == 0) + if (oldx == NULL) oldx = gen_reg_rtx (Pmode); emit_move_insn (oldx, pat); @@ -4752,45 +4918,23 @@ arc_legitimize_pic_address (rtx orig, rtx oldx) { rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1); - /* Check first to see if this is a constant offset from a @GOTOFF - symbol reference. */ - if ((GET_CODE (op0) == LABEL_REF - || (GET_CODE (op0) == SYMBOL_REF - && (CONSTANT_POOL_ADDRESS_P (op0) - || SYMBOL_REF_LOCAL_P (op0)))) - && GET_CODE (op1) == CONST_INT) - { - /* FIXME: like above, could do without gp reference. */ - crtl->uses_pic_offset_table = 1; - pat - = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), ARC_UNSPEC_GOTOFF); - pat = gen_rtx_PLUS (Pmode, pat, op1); - pat = gen_rtx_CONST (Pmode, pat); - pat = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pat); - - if (oldx != 0) - { - emit_move_insn (oldx, pat); - pat = oldx; - } - } - else - { - base = arc_legitimize_pic_address (XEXP (addr, 0), oldx); - pat = arc_legitimize_pic_address (XEXP (addr, 1), + base = arc_legitimize_pic_address (op0, oldx); + pat = arc_legitimize_pic_address (op1, base == oldx ? NULL_RTX : oldx); - if (GET_CODE (pat) == CONST_INT) - pat = plus_constant (Pmode, base, INTVAL (pat)); - else + if (base == op0 && pat == op1) + return orig; + + if (GET_CODE (pat) == CONST_INT) + pat = plus_constant (Pmode, base, INTVAL (pat)); + else + { + if (GET_CODE (pat) == PLUS && CONSTANT_P (XEXP (pat, 1))) { - if (GET_CODE (pat) == PLUS && CONSTANT_P (XEXP (pat, 1))) - { - base = gen_rtx_PLUS (Pmode, base, XEXP (pat, 0)); - pat = XEXP (pat, 1); - } - pat = gen_rtx_PLUS (Pmode, base, pat); + base = gen_rtx_PLUS (Pmode, base, XEXP (pat, 0)); + pat = XEXP (pat, 1); } + pat = gen_rtx_PLUS (Pmode, base, pat); } } } @@ -4906,26 +5050,47 @@ arc_output_pic_addr_const (FILE * file, rtx x, int code) case UNSPEC: - gcc_assert (XVECLEN (x, 0) == 1); - if (XINT (x, 1) == ARC_UNSPEC_GOT) - fputs ("pcl,", file); - arc_output_pic_addr_const (file, XVECEXP (x, 0, 0), code); + const char *suffix; + bool pcrel; pcrel = false; + rtx base; base = NULL; + gcc_assert (XVECLEN (x, 0) >= 1); switch (XINT (x, 1)) { case ARC_UNSPEC_GOT: - fputs ("@gotpc", file); + suffix = "@gotpc", pcrel = true; break; case ARC_UNSPEC_GOTOFF: - fputs ("@gotoff", file); + suffix = "@gotoff"; break; case ARC_UNSPEC_PLT: - fputs ("@plt", file); + suffix = "@plt"; + break; + case UNSPEC_TLS_GD: + suffix = "@tlsgd", pcrel = true; + break; + case UNSPEC_TLS_IE: + suffix = "@tlsie", pcrel = true; + break; + case UNSPEC_TLS_OFF: + if (XVECLEN (x, 0) == 2) + base = XVECEXP (x, 0, 1); + if (SYMBOL_REF_TLS_MODEL (XVECEXP (x, 0, 0)) == TLS_MODEL_LOCAL_EXEC + || (!flag_pic && !base)) + suffix = "@tpoff"; + else + suffix = "@dtpoff"; break; default: output_operand_lossage ("invalid UNSPEC as operand: %d", XINT (x,1)); break; } - break; + if (pcrel) + fputs ("pcl,", file); + arc_output_pic_addr_const (file, XVECEXP (x, 0, 0), code); + fputs (suffix, file); + if (base) + arc_output_pic_addr_const (file, base, code); + break; default: output_operand_lossage ("invalid expression as operand"); @@ -4939,15 +5104,18 @@ arc_output_pic_addr_const (FILE * file, rtx x, int code) /* Emit insns to move operands[1] into operands[0]. */ -void -emit_pic_move (rtx *operands, machine_mode) +static void +prepare_pic_move (rtx *operands, machine_mode) { - rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode); - - if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1])) + if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]) + && flag_pic) operands[1] = force_reg (Pmode, operands[1]); else - operands[1] = arc_legitimize_pic_address (operands[1], temp); + { + rtx temp = (reload_in_progress ? operands[0] + : flag_pic? gen_reg_rtx (Pmode) : NULL_RTX); + operands[1] = arc_legitimize_pic_address (operands[1], temp); + } } @@ -5149,9 +5317,12 @@ arc_legitimate_pic_operand_p (rtx x) satisfies CONSTANT_P. */ bool -arc_legitimate_constant_p (machine_mode, rtx x) +arc_legitimate_constant_p (machine_mode mode, rtx x) { - if (!flag_pic) + if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x)) + return false; + + if (!flag_pic && mode != Pmode) return true; switch (GET_CODE (x)) @@ -5161,7 +5332,9 @@ arc_legitimate_constant_p (machine_mode, rtx x) if (GET_CODE (x) == PLUS) { - if (GET_CODE (XEXP (x, 1)) != CONST_INT) + if (flag_pic + ? GET_CODE (XEXP (x, 1)) != CONST_INT + : !arc_legitimate_constant_p (mode, XEXP (x, 1))) return false; x = XEXP (x, 0); } @@ -5173,6 +5346,9 @@ arc_legitimate_constant_p (machine_mode, rtx x) case ARC_UNSPEC_PLT: case ARC_UNSPEC_GOTOFF: case ARC_UNSPEC_GOT: + case UNSPEC_TLS_GD: + case UNSPEC_TLS_IE: + case UNSPEC_TLS_OFF: case UNSPEC_PROF: return true; @@ -5187,9 +5363,14 @@ arc_legitimate_constant_p (machine_mode, rtx x) /* Return true. */ break; - case LABEL_REF: case SYMBOL_REF: - return false; + if (SYMBOL_REF_TLS_MODEL (x)) + return false; + /* Fall through. */ + case LABEL_REF: + if (flag_pic) + return false; + /* Fall through. */ default: break; @@ -5212,12 +5393,29 @@ arc_legitimate_address_p (machine_mode mode, rtx x, bool strict) return true; if (GET_CODE (x) == CONST_INT && LARGE_INT (INTVAL (x))) return true; - if ((GET_MODE_SIZE (mode) != 16) - && (GET_CODE (x) == SYMBOL_REF - || GET_CODE (x) == LABEL_REF - || GET_CODE (x) == CONST)) + + /* When we compile for size avoid const (@sym + offset) + addresses. */ + if (!flag_pic && optimize_size && !reload_completed + && (GET_CODE (x) == CONST) + && (GET_CODE (XEXP (x, 0)) == PLUS) + && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF) + && SYMBOL_REF_TLS_MODEL (XEXP (XEXP (x, 0), 0)) == 0 + && !SYMBOL_REF_FUNCTION_P (XEXP (XEXP (x, 0), 0))) { - if (!flag_pic || arc_legitimate_pic_addr_p (x)) + rtx addend = XEXP (XEXP (x, 0), 1); + gcc_assert (CONST_INT_P (addend)); + HOST_WIDE_INT offset = INTVAL (addend); + + /* Allow addresses having a large offset to pass. Anyhow they + will end in a limm. */ + return !(offset > -1024 && offset < 1020); + } + + if ((GET_MODE_SIZE (mode) != 16) && CONSTANT_P (x)) + { + if (flag_pic ? arc_legitimate_pic_addr_p (x) + : arc_legitimate_constant_p (Pmode, x)) return true; } if ((GET_CODE (x) == PRE_DEC || GET_CODE (x) == PRE_INC @@ -6860,8 +7058,12 @@ arc_rewrite_small_data_p (const_rtx x) x = XEXP (x, 0); } - return (GET_CODE (x) == SYMBOL_REF - && SYMBOL_REF_SMALL_P(x)); + if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_SMALL_P (x)) + { + gcc_assert (SYMBOL_REF_TLS_MODEL (x) == 0); + return true; + } + return false; } /* If possible, rewrite OP so that it refers to small data using @@ -7280,40 +7482,39 @@ prepare_move_operands (rtx *operands, machine_mode mode) { /* We used to do this only for MODE_INT Modes, but addresses to floating point variables may well be in the small data section. */ - if (1) + if (!TARGET_NO_SDATA_SET && small_data_pattern (operands[0], Pmode)) + operands[0] = arc_rewrite_small_data (operands[0]); + + if (mode == SImode && SYMBOLIC_CONST (operands[1])) { - if (!TARGET_NO_SDATA_SET && small_data_pattern (operands[0], Pmode)) - operands[0] = arc_rewrite_small_data (operands[0]); - else if (mode == SImode && flag_pic && SYMBOLIC_CONST (operands[1])) - { - emit_pic_move (operands, SImode); + prepare_pic_move (operands, SImode); - /* Disable any REG_EQUALs associated with the symref - otherwise the optimization pass undoes the work done - here and references the variable directly. */ - } - else if (GET_CODE (operands[0]) != MEM - && !TARGET_NO_SDATA_SET - && small_data_pattern (operands[1], Pmode)) - { - /* This is to take care of address calculations involving sdata - variables. */ - operands[1] = arc_rewrite_small_data (operands[1]); - - emit_insn (gen_rtx_SET (operands[0],operands[1])); - /* ??? This note is useless, since it only restates the set itself. - We should rather use the original SYMBOL_REF. However, there is - the problem that we are lying to the compiler about these - SYMBOL_REFs to start with. symbol@sda should be encoded specially - so that we can tell it apart from an actual symbol. */ - set_unique_reg_note (get_last_insn (), REG_EQUAL, operands[1]); - - /* Take care of the REG_EQUAL note that will be attached to mark the - output reg equal to the initial symbol_ref after this code is - executed. */ - emit_move_insn (operands[0], operands[0]); - return true; - } + /* Disable any REG_EQUALs associated with the symref + otherwise the optimization pass undoes the work done + here and references the variable directly. */ + } + + if (GET_CODE (operands[0]) != MEM + && !TARGET_NO_SDATA_SET + && small_data_pattern (operands[1], Pmode)) + { + /* This is to take care of address calculations involving sdata + variables. */ + operands[1] = arc_rewrite_small_data (operands[1]); + + emit_insn (gen_rtx_SET (operands[0],operands[1])); + /* ??? This note is useless, since it only restates the set itself. + We should rather use the original SYMBOL_REF. However, there is + the problem that we are lying to the compiler about these + SYMBOL_REFs to start with. symbol@sda should be encoded specially + so that we can tell it apart from an actual symbol. */ + set_unique_reg_note (get_last_insn (), REG_EQUAL, operands[1]); + + /* Take care of the REG_EQUAL note that will be attached to mark the + output reg equal to the initial symbol_ref after this code is + executed. */ + emit_move_insn (operands[0], operands[0]); + return true; } if (MEM_P (operands[0]) @@ -8311,6 +8512,13 @@ arc_legitimize_address_0 (rtx x, rtx oldx ATTRIBUTE_UNUSED, static rtx arc_legitimize_address (rtx orig_x, rtx oldx, machine_mode mode) { + if (GET_CODE (orig_x) == SYMBOL_REF) + { + enum tls_model model = SYMBOL_REF_TLS_MODEL (orig_x); + if (model != 0) + return arc_legitimize_tls_address (orig_x, model); + } + rtx new_x = arc_legitimize_address_0 (orig_x, oldx, mode); if (new_x) @@ -9070,6 +9278,8 @@ arc_can_follow_jump (const rtx_insn *follower, const rtx_insn *followee) bool arc_epilogue_uses (int regno) { + if (regno == arc_tp_regno) + return true; if (reload_completed) { if (ARC_INTERRUPT_P (cfun->machine->fn_type)) @@ -9085,6 +9295,16 @@ arc_epilogue_uses (int regno) return regno == arc_return_address_regs[arc_compute_function_type (cfun)]; } +/* Helper for EH_USES macro. */ + +bool +arc_eh_uses (int regno) +{ + if (regno == arc_tp_regno) + return true; + return false; +} + #ifndef TARGET_NO_LRA #define TARGET_NO_LRA !TARGET_LRA #endif @@ -9590,6 +9810,13 @@ arc_dwarf_register_span (rtx rtl) return p; } +/* We can't inline this in INSN_REFERENCES_ARE_DELAYED because + resource.h doesn't include the required header files. */ +bool +insn_is_tls_gd_dispatch (rtx_insn *insn) +{ + return recog_memoized (insn) == CODE_FOR_tls_gd_dispatch; +} struct gcc_target targetm = TARGET_INITIALIZER; diff --git a/gcc/config/arc/arc.h b/gcc/config/arc/arc.h index 5100a5b8f821..de6c6d11974a 100644 --- a/gcc/config/arc/arc.h +++ b/gcc/config/arc/arc.h @@ -109,6 +109,8 @@ along with GCC; see the file COPYING3. If not see builtin_define ("__ARC_SIMD__"); \ if (TARGET_BARREL_SHIFTER) \ builtin_define ("__Xbarrel_shifter");\ + builtin_define_with_int_value ("__ARC_TLS_REGNO__", \ + arc_tp_regno); \ builtin_assert ("cpu=arc"); \ builtin_assert ("machine=arc"); \ builtin_define (TARGET_BIG_ENDIAN \ @@ -201,7 +203,13 @@ along with GCC; see the file COPYING3. If not see #endif #if DEFAULT_LIBC != LIBC_UCLIBC -#define STARTFILE_SPEC "%{!shared:crt0.o%s} crti%O%s %{pg|p:crtg.o%s} crtbegin.o%s" +#define ARC_TLS_EXTRA_START_SPEC "crttls.o%s" + +#define EXTRA_SPECS \ + { "arc_tls_extra_start_spec", ARC_TLS_EXTRA_START_SPEC }, \ + +#define STARTFILE_SPEC "%{!shared:crt0.o%s} crti%O%s %{pg|p:crtg.o%s} " \ + "%(arc_tls_extra_start_spec) crtbegin.o%s" #else #define STARTFILE_SPEC "%{!shared:%{!mkernel:crt1.o%s}} crti.o%s \ %{!shared:%{pg|p|profile:crtg.o%s} crtbegin.o%s} %{shared:crtbeginS.o%s}" @@ -748,9 +756,10 @@ extern enum reg_class arc_regno_reg_class[]; or a pseudo reg currently allocated to a suitable hard reg. Since they use reg_renumber, they are safe only once reg_renumber has been allocated, which happens in local-alloc.c. */ -#define REGNO_OK_FOR_BASE_P(REGNO) \ -((REGNO) < 29 || ((REGNO) == ARG_POINTER_REGNUM) || ((REGNO) == 63) ||\ - (unsigned) reg_renumber[REGNO] < 29) +#define REGNO_OK_FOR_BASE_P(REGNO) \ + ((REGNO) < 29 || ((REGNO) == ARG_POINTER_REGNUM) || ((REGNO) == 63) \ + || ((unsigned) reg_renumber[REGNO] < 29) \ + || ((unsigned) (REGNO) == (unsigned) arc_tp_regno)) #define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_BASE_P(REGNO) @@ -937,6 +946,8 @@ arc_return_addr_rtx(COUNT,FRAME) #define EPILOGUE_USES(REGNO) arc_epilogue_uses ((REGNO)) +#define EH_USES(REGNO) arc_eh_uses((REGNO)) + /* Definitions for register eliminations. This is an array of structures. Each structure initializes one pair @@ -1657,7 +1668,8 @@ extern enum arc_function_type arc_compute_function_type (struct function *); && GET_CODE (PATTERN (X)) != CLOBBER \ && (get_attr_type (X) == TYPE_CALL || get_attr_type (X) == TYPE_SFUNC)) -#define INSN_REFERENCES_ARE_DELAYED(insn) INSN_SETS_ARE_DELAYED (insn) +#define INSN_REFERENCES_ARE_DELAYED(insn) \ + (INSN_SETS_ARE_DELAYED (insn) && !insn_is_tls_gd_dispatch (insn)) #define CALL_ATTR(X, NAME) \ ((CALL_P (X) || NONJUMP_INSN_P (X)) \ diff --git a/gcc/config/arc/arc.md b/gcc/config/arc/arc.md index 4193d2610f19..4a7287b05365 100644 --- a/gcc/config/arc/arc.md +++ b/gcc/config/arc/arc.md @@ -111,6 +111,10 @@ ARC_UNSPEC_PLT ARC_UNSPEC_GOT ARC_UNSPEC_GOTOFF + UNSPEC_TLS_GD + UNSPEC_TLS_LD + UNSPEC_TLS_IE + UNSPEC_TLS_OFF UNSPEC_ARC_NORM UNSPEC_ARC_NORMW UNSPEC_ARC_SWAP @@ -169,6 +173,7 @@ (R1_REG 1) (R2_REG 2) (R3_REG 3) + (R10_REG 10) (R12_REG 12) (SP_REG 28) (ILINK1_REGNUM 29) @@ -5277,6 +5282,72 @@ [(set_attr "type" "call") (set_attr "is_SIBCALL" "yes")]) +(define_insn "tls_load_tp_soft" + [(set (reg:SI R0_REG) (unspec:SI [(const_int 0)] UNSPEC_TLS_OFF)) + (clobber (reg:SI RETURN_ADDR_REGNUM))] + "" + "*return arc_output_libcall (\"__read_tp\");" + [(set_attr "is_sfunc" "yes") + (set_attr "predicable" "yes")]) + +(define_insn "tls_gd_load" + [(set (match_operand:SI 0 "dest_reg_operand" "=Rcq#q,c") + (unspec:SI [(match_operand:SI 1 "register_operand" "Rcq#q,c") + (match_operand:SI 2 "symbolic_operand" "X,X")] + UNSPEC_TLS_GD))] + "" + ".tls_gd_ld %2`ld%? %0,[%1]" + [(set_attr "type" "load") + ; if the linker has to patch this into IE, we need a long insn + ; (FIXME: or two short insn, ld_s / jl_s. missing -Os optimization.) + (set_attr_alternative "iscompact" + [(cond [(ne (symbol_ref "arc_tp_regno == 30") (const_int 0)) + (const_string "*")] (const_string "maybe")) + (const_string "*")])]) + +(define_insn "tls_gd_get_addr" + [(set (reg:SI R0_REG) + (call:SI (mem:SI (unspec:SI [(match_operand:SI 0 + "symbolic_operand" "X,X")] + UNSPEC_TLS_GD)) + (const_int 0))) + (clobber (reg:SI RETURN_ADDR_REGNUM))] + "" + ".tls_gd_ld %0`bl%* __tls_get_addr@plt" + [(set_attr "type" "call") + ; With TARGET_MEDIUM_CALLS, plt calls are not predicable. + (set_attr "predicable" "no")]) + +; We make this call specific to the tls symbol to avoid commoning this +; with calls for other symbols; we want the linker to be able to +(define_insn "tls_gd_dispatch" + [(set (reg:SI R0_REG) + (unspec:SI + [(reg:SI R0_REG) + (call (mem:SI (match_operand:SI 0 "register_operand" "Rcq,q,c")) + (const_int 0)) + (match_operand:SI 1 "symbolic_operand" "X,X,X")] + UNSPEC_TLS_GD)) + (clobber (reg:SI RETURN_ADDR_REGNUM)) + (clobber (reg:DI R10_REG)) + (clobber (reg:SI R12_REG))] + "" + ".tls_gd_call %1`jl%!%* [%0]" + [(set_attr "type" "call") + (set_attr "iscompact" "maybe,false,*") + (set_attr "predicable" "no,no,yes")]) + +;; For thread pointer builtins +(define_expand "get_thread_pointersi" + [(set (match_operand:SI 0 "register_operand") (match_dup 1))] + "" + "operands[1] = gen_rtx_REG (Pmode, arc_tp_regno);") + +(define_expand "set_thread_pointersi" + [(set (match_dup 1) (match_operand:SI 0 "register_operand"))] + "" + "operands[1] = gen_rtx_REG (Pmode, arc_tp_regno);") + ;; If hardware floating point is available, don't define a negdf pattern; ;; it would be something like: ;;(define_insn "negdf2" diff --git a/gcc/config/arc/arc.opt b/gcc/config/arc/arc.opt index 2227b7554c30..76f66a2988b2 100644 --- a/gcc/config/arc/arc.opt +++ b/gcc/config/arc/arc.opt @@ -456,3 +456,10 @@ Enum(arc_fpu) String(fpus_all) Value(FPU_SP | FPU_SC | FPU_SF | FPU_SD) EnumValue Enum(arc_fpu) String(fpud_all) Value(FPU_SP | FPU_SC | FPU_SF | FPU_SD | FPU_DP | FPU_DC | FPU_DF | FPU_DD) + +mtp-regno= +Target RejectNegative Joined UInteger Var(arc_tp_regno) Init(25) +Specify thread pointer register number + +mtp-regno=none +Target RejectNegative Var(arc_tp_regno,-1) diff --git a/gcc/config/arc/predicates.md b/gcc/config/arc/predicates.md index 85bbf8435588..3c657c67f0d3 100644 --- a/gcc/config/arc/predicates.md +++ b/gcc/config/arc/predicates.md @@ -351,9 +351,12 @@ switch (GET_CODE (op)) { case SYMBOL_REF : + if (SYMBOL_REF_TLS_MODEL (op)) + return 0; case LABEL_REF : + return 1; case CONST : - return (!flag_pic || arc_legitimate_pic_operand_p(op)); + return arc_legitimate_constant_p (mode, op); case CONST_INT : return (LARGE_INT (INTVAL (op))); case CONST_DOUBLE : @@ -451,6 +454,16 @@ && (GET_CODE (XEXP (addr, 1)) != PLUS || !CONST_INT_P (XEXP (XEXP (addr, 1), 1)))) return 0; + /* CONST_INT / CONST_DOUBLE is fine, but the PIC CONST ([..] UNSPEC)) + constructs are effectively indexed. */ + if (flag_pic) + { + rtx ad0 = addr; + while (GET_CODE (ad0) == PLUS) + ad0 = XEXP (ad0, 0); + if (GET_CODE (ad0) == CONST || GET_CODE (ad0) == UNSPEC) + return 0; + } return address_operand (addr, mode); } default : diff --git a/gcc/configure b/gcc/configure index d4d5ddf69311..99fdea172f64 100755 --- a/gcc/configure +++ b/gcc/configure @@ -23855,6 +23855,12 @@ foo: .long 25 tls_first_minor=13 tls_as_opt=--fatal-warnings ;; + arc*-*-*) + conftest_s=' + add_s r0,r0, @foo@tpoff' + tls_first_major=2 + tls_first_minor=23 + ;; cris-*-*|crisv32-*-*) conftest_s=' .section ".tdata","awT",@progbits diff --git a/gcc/configure.ac b/gcc/configure.ac index 0328a2cd024b..e40d82a83060 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -3086,6 +3086,12 @@ foo: .long 25 tls_first_minor=13 tls_as_opt=--fatal-warnings ;; + arc*-*-*) + conftest_s=' + add_s r0,r0, @foo@tpoff' + tls_first_major=2 + tls_first_minor=23 + ;; cris-*-*|crisv32-*-*) conftest_s=' .section ".tdata","awT",@progbits diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f1ac25770602..515d948ce710 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -593,7 +593,7 @@ Objective-C and Objective-C++ Dialects}. -mcrc -mdsp-packa -mdvbf -mlock -mmac-d16 -mmac-24 -mrtsc -mswape @gol -mtelephony -mxy -misize -mannotate-align -marclinux -marclinux_prof @gol -mlong-calls -mmedium-calls -msdata @gol --mucb-mcount -mvolatile-cache @gol +-mucb-mcount -mvolatile-cache -mtp-regno=@var{regno} @gol -malign-call -mauto-modify-reg -mbbit-peephole -mno-brcc @gol -mcase-vector-pcrel -mcompact-casesi -mno-cond-exec -mearly-cbranchsi @gol -mexpand-adddi -mindexed-loads -mlra -mlra-priority-none @gol @@ -13360,6 +13360,10 @@ Enable code density instructions for ARC EM, default on for ARC HS. @opindex mll64 Enable double load/store operations for ARC HS cores. +@item -mtp-regno=@var{regno} +@opindex mtp-regno +Specify thread pointer register number. + @item -mmpy-option=@var{multo} @opindex mmpy-option Compile ARCv2 code with a multiplier design option. @samp{wlh1} is -- GitLab