+2018-11-29 Alan Modra <amodra@gmail.com>
+
+ * config.in (HAVE_AS_PLTSEQ): Add.
+ * config/rs6000/predicates.md (indirect_call_operand): New.
+ * config/rs6000/rs6000-protos.h (rs6000_pltseq_template),
+ (rs6000_sibcall_sysv): Declare.
+ * config/rs6000/rs6000.c (init_cumulative_args): Set cookie
+ CALL_LONG for -fno-plt.
+ (print_operand <T, z, 0>): Handle UNSPEC_PLTSEQ.
+ (rs6000_indirect_call_template_1): Emit .reloc directives for
+ UNSPEC_PLTSEQ calls.
+ (rs6000_pltseq_template): New function.
+ (rs6000_longcall_ref): Add arg parameter. Use PLT16 insns if
+ relocs supported by assembler. Move SYMBOL_REF test to callers.
+ (rs6000_call_aix): Adjust rs6000_longcall_ref call. Package
+ insns in UNSPEC_PLTSEQ, preserving original func_desc.
+ (rs6000_call_sysv): Likewise.
+ (rs6000_sibcall_sysv): New function.
+ * config/rs6000/rs6000.h (HAVE_AS_PLTSEQ): Provide default.
+ * config/rs6000/rs6000.md (UNSPEC_PLTSEQ, UNSPEC_PLT16_HA,
+ UNSPEC_PLT16_LO): New.
+ (pltseq_tocsave, pltseq_plt16_ha, pltseq_plt16_lo, pltseq_mtctr): New.
+ (call_indirect_nonlocal_sysv): Don't differentiate zero from non-zero
+ cookie in constraints. Test explicitly for flags in length attr.
+ Handle unspec operand 1.
+ (call_value_indirect_nonlocal_sysv): Likewise.
+ (call_indirect_aix, call_value_indirect_aix): Handle unspec operand 1.
+ (call_indirect_elfv2, call_value_indirect_elfv2): Likewise.
+ (sibcall, sibcall_value): Use rs6000_sibcall_sysv.
+ (sibcall_indirect_nonlocal_sysv): New pattern.
+ (sibcall_value_indirect_nonlocal_sysv): Likewise.
+ (sibcall_nonlocal_sysv, sibcall_value_nonlocal_sysv): Remove indirect
+ call alternatives.
+ * configure.ac: Check for gas plt sequence marker support.
+ * configure: Regenerate.
+
2018-11-29 Alan Modra <amodra@gmail.com>
* config/rs6000/predicates.md (unspec_tls): New.
#endif
+/* Define if your assembler supports R_PPC*_PLTSEQ relocations. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_AS_PLTSEQ
+#endif
+
+
/* Define if your assembler supports .ref */
#ifndef USED_FOR_TARGET
#undef HAVE_AS_REF
|| REGNO (op) >= FIRST_PSEUDO_REGISTER")
(match_code "symbol_ref")))
+;; Return 1 if the operand, used inside a MEM, is a valid first argument
+;; to an indirect CALL. This is LR, CTR, or a PLTSEQ unspec using CTR.
+(define_predicate "indirect_call_operand"
+ (match_code "reg,unspec")
+{
+ if (REG_P (op))
+ return (REGNO (op) == LR_REGNO
+ || REGNO (op) == CTR_REGNO);
+ if (GET_CODE (op) == UNSPEC)
+ {
+ if (XINT (op, 1) != UNSPEC_PLTSEQ)
+ return false;
+ op = XVECEXP (op, 0, 0);
+ return REG_P (op) && REGNO (op) == CTR_REGNO;
+ }
+ return false;
+})
+
;; Return 1 if the operand is a SYMBOL_REF for a function known to be in
;; this file.
(define_predicate "current_file_function_operand"
extern const char *rs6000_sibcall_template (rtx *, unsigned int);
extern const char *rs6000_indirect_call_template (rtx *, unsigned int);
extern const char *rs6000_indirect_sibcall_template (rtx *, unsigned int);
+extern const char *rs6000_pltseq_template (rtx *, int);
extern enum rtx_code rs6000_reverse_condition (machine_mode,
enum rtx_code);
extern rtx rs6000_emit_eqne (machine_mode, rtx, rtx, rtx);
extern void rs6000_call_aix (rtx, rtx, rtx, rtx);
extern void rs6000_sibcall_aix (rtx, rtx, rtx, rtx);
extern void rs6000_call_sysv (rtx, rtx, rtx, rtx);
+extern void rs6000_sibcall_sysv (rtx, rtx, rtx, rtx);
extern void rs6000_aix_asm_output_dwarf_table_ref (char *);
extern void get_ppc476_thunk_name (char name[32]);
extern bool rs6000_overloaded_builtin_p (enum rs6000_builtins);
init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
rtx libname ATTRIBUTE_UNUSED, int incoming,
int libcall, int n_named_args,
- tree fndecl ATTRIBUTE_UNUSED,
+ tree fndecl,
machine_mode return_mode ATTRIBUTE_UNUSED)
{
static CUMULATIVE_ARGS zero_cumulative;
&& lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
&& !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype))))
cum->call_cookie |= CALL_LONG;
+ else if (DEFAULT_ABI != ABI_DARWIN)
+ {
+ bool is_local = (fndecl
+ && !DECL_EXTERNAL (fndecl)
+ && !DECL_WEAK (fndecl)
+ && (*targetm.binds_local_p) (fndecl));
+ if (is_local)
+ ;
+ else if (flag_plt)
+ {
+ if (fntype
+ && lookup_attribute ("noplt", TYPE_ATTRIBUTES (fntype)))
+ cum->call_cookie |= CALL_LONG;
+ }
+ else
+ {
+ if (!(fntype
+ && lookup_attribute ("plt", TYPE_ATTRIBUTES (fntype))))
+ cum->call_cookie |= CALL_LONG;
+ }
+ }
if (TARGET_DEBUG_ARG)
{
case 'T':
/* Print the symbolic name of a branch target register. */
+ if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+ x = XVECEXP (x, 0, 0);
if (GET_CODE (x) != REG || (REGNO (x) != LR_REGNO
&& REGNO (x) != CTR_REGNO))
output_operand_lossage ("invalid %%T value");
return;
case 'z':
+ if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+ x = XVECEXP (x, 0, 1);
/* X is a SYMBOL_REF. Write out the name preceded by a
period and without any trailing data in brackets. Used for function
names. If we are configured for System V (or the embedded ABI) on
output_addr_const (file, CONST_CAST_RTX (tocrel_base_oac));
else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD)
output_addr_const (file, XVECEXP (x, 0, 0));
+ else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+ output_addr_const (file, XVECEXP (x, 0, 1));
else
output_addr_const (file, x);
return;
might produce 10 digits. */
gcc_assert (funop <= MAX_RECOG_OPERANDS);
- static char str[144];
+ static char str[144]; /* 1 spare */
+ char *s = str;
const char *ptrload = TARGET_64BIT ? "d" : "wz";
+ if (DEFAULT_ABI == ABI_AIX)
+ s += sprintf (s,
+ "l%s 2,%%%u\n\t",
+ ptrload, funop + 2);
+
/* We don't need the extra code to stop indirect call speculation if
calling via LR. */
bool speculate = (TARGET_MACHO
|| (REG_P (operands[funop])
&& REGNO (operands[funop]) == LR_REGNO));
+ if (!TARGET_MACHO && HAVE_AS_PLTSEQ && GET_CODE (operands[funop]) == UNSPEC)
+ {
+ const char *rel64 = TARGET_64BIT ? "64" : "";
+ char tls[29];
+ tls[0] = 0;
+ if (GET_CODE (operands[funop + 1]) == UNSPEC)
+ {
+ if (XINT (operands[funop + 1], 1) == UNSPEC_TLSGD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSGD,%%%u\n\t",
+ rel64, funop + 1);
+ else if (XINT (operands[funop + 1], 1) == UNSPEC_TLSLD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSLD,%%&\n\t",
+ rel64);
+ else
+ gcc_unreachable ();
+ }
+
+ const char *addend = (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT
+ && flag_pic == 2 ? "+32768" : "");
+ if (!speculate)
+ {
+ s += sprintf (s,
+ "%s.reloc .,R_PPC%s_PLTSEQ,%%z%u%s\n\t",
+ tls, rel64, funop, addend);
+ s += sprintf (s, "crset 2\n\t");
+ }
+ s += sprintf (s,
+ "%s.reloc .,R_PPC%s_PLTCALL,%%z%u%s\n\t",
+ tls, rel64, funop, addend);
+ }
+ else if (!speculate)
+ s += sprintf (s, "crset 2\n\t");
+
if (DEFAULT_ABI == ABI_AIX)
{
if (speculate)
- sprintf (str,
- "l%s 2,%%%u\n\t"
+ sprintf (s,
"b%%T%ul\n\t"
"l%s 2,%%%u(1)",
- ptrload, funop + 2, funop, ptrload, funop + 3);
+ funop, ptrload, funop + 3);
else
- sprintf (str,
- "crset 2\n\t"
- "l%s 2,%%%u\n\t"
+ sprintf (s,
"beq%%T%ul-\n\t"
"l%s 2,%%%u(1)",
- ptrload, funop + 2, funop, ptrload, funop + 3);
+ funop, ptrload, funop + 3);
}
else if (DEFAULT_ABI == ABI_ELFv2)
{
if (speculate)
- sprintf (str,
+ sprintf (s,
"b%%T%ul\n\t"
"l%s 2,%%%u(1)",
funop, ptrload, funop + 2);
else
- sprintf (str,
- "crset 2\n\t"
+ sprintf (s,
"beq%%T%ul-\n\t"
"l%s 2,%%%u(1)",
funop, ptrload, funop + 2);
else
{
if (speculate)
- sprintf (str,
+ sprintf (s,
"b%%T%u%s",
funop, sibcall ? "" : "l");
else
- sprintf (str,
- "crset 2\n\t"
+ sprintf (s,
"beq%%T%u%s-%s",
funop, sibcall ? "" : "l", sibcall ? "\n\tb $" : "");
}
return rs6000_indirect_call_template_1 (operands, funop, true);
}
+#if HAVE_AS_PLTSEQ
+/* Output indirect call insns.
+ WHICH is 0 for tocsave, 1 for plt16_ha, 2 for plt16_lo, 3 for mtctr. */
+const char *
+rs6000_pltseq_template (rtx *operands, int which)
+{
+ const char *rel64 = TARGET_64BIT ? "64" : "";
+ char tls[28];
+ tls[0] = 0;
+ if (GET_CODE (operands[3]) == UNSPEC)
+ {
+ if (XINT (operands[3], 1) == UNSPEC_TLSGD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSGD,%%3\n\t",
+ rel64);
+ else if (XINT (operands[3], 1) == UNSPEC_TLSLD)
+ sprintf (tls, ".reloc .,R_PPC%s_TLSLD,%%&\n\t",
+ rel64);
+ else
+ gcc_unreachable ();
+ }
+
+ gcc_assert (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4);
+ static char str[96]; /* 15 spare */
+ const char *off = WORDS_BIG_ENDIAN ? "+2" : "";
+ const char *addend = (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT
+ && flag_pic == 2 ? "+32768" : "");
+ switch (which)
+ {
+ case 0:
+ sprintf (str,
+ "%s.reloc .,R_PPC%s_PLTSEQ,%%z2\n\t"
+ "st%s",
+ tls, rel64, TARGET_64BIT ? "d 2,24(1)" : "w 2,12(1)");
+ break;
+ case 1:
+ if (DEFAULT_ABI == ABI_V4 && !flag_pic)
+ sprintf (str,
+ "%s.reloc .%s,R_PPC%s_PLT16_HA,%%z2\n\t"
+ "lis %%0,0",
+ tls, off, rel64);
+ else
+ sprintf (str,
+ "%s.reloc .%s,R_PPC%s_PLT16_HA,%%z2%s\n\t"
+ "addis %%0,%%1,0",
+ tls, off, rel64, addend);
+ break;
+ case 2:
+ sprintf (str,
+ "%s.reloc .%s,R_PPC%s_PLT16_LO%s,%%z2%s\n\t"
+ "l%s %%0,0(%%1)",
+ tls, off, rel64, TARGET_64BIT ? "_DS" : "", addend,
+ TARGET_64BIT ? "d" : "wz");
+ break;
+ case 3:
+ sprintf (str,
+ "%s.reloc .,R_PPC%s_PLTSEQ,%%z2%s\n\t"
+ "mtctr %%1",
+ tls, rel64, addend);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return str;
+}
+#endif
+
#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
/* Emit an assembler directive to set symbol visibility for DECL to
VISIBILITY_TYPE. */
longcall attribute. */
static rtx
-rs6000_longcall_ref (rtx call_ref)
+rs6000_longcall_ref (rtx call_ref, rtx arg)
{
- if (GET_CODE (call_ref) != SYMBOL_REF)
- return call_ref;
-
/* System V adds '.' to the internal name, so skip them. */
const char *call_name = XSTR (call_ref, 0);
if (*call_name == '.')
call_ref = gen_rtx_SYMBOL_REF (VOIDmode, IDENTIFIER_POINTER (node));
}
+ if (HAVE_AS_PLTSEQ
+ && TARGET_TLS_MARKERS
+ && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4))
+ {
+ rtx base = const0_rtx;
+ int regno;
+ if (DEFAULT_ABI == ABI_ELFv2)
+ {
+ base = gen_rtx_REG (Pmode, TOC_REGISTER);
+ regno = 12;
+ }
+ else
+ {
+ if (flag_pic)
+ base = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM);
+ regno = 11;
+ }
+ /* Reg must match that used by linker PLT stubs. For ELFv2, r12
+ may be used by a function global entry point. For SysV4, r11
+ is used by __glink_PLTresolve lazy resolver entry. */
+ rtx reg = gen_rtx_REG (Pmode, regno);
+ rtx hi = gen_rtx_UNSPEC (Pmode, gen_rtvec (3, base, call_ref, arg),
+ UNSPEC_PLT16_HA);
+ rtx lo = gen_rtx_UNSPEC (Pmode, gen_rtvec (3, reg, call_ref, arg),
+ UNSPEC_PLT16_LO);
+ emit_insn (gen_rtx_SET (reg, hi));
+ emit_insn (gen_rtx_SET (reg, lo));
+ return reg;
+ }
+
return force_reg (Pmode, call_ref);
}
\f
void
rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
{
- const bool direct_call_p
- = GET_CODE (func_desc) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (func_desc);
+ rtx func = func_desc;
rtx toc_reg = gen_rtx_REG (Pmode, TOC_REGNUM);
rtx toc_load = NULL_RTX;
rtx toc_restore = NULL_RTX;
tlsarg = global_tlsarg;
/* Handle longcall attributes. */
- if (INTVAL (cookie) & CALL_LONG)
- func_desc = rs6000_longcall_ref (func_desc);
+ if ((INTVAL (cookie) & CALL_LONG) != 0
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ func = rs6000_longcall_ref (func_desc, tlsarg);
/* Handle indirect calls. */
- if (GET_CODE (func_desc) != SYMBOL_REF
- || (DEFAULT_ABI == ABI_AIX && !SYMBOL_REF_FUNCTION_P (func_desc)))
+ if (GET_CODE (func) != SYMBOL_REF
+ || (DEFAULT_ABI == ABI_AIX && !SYMBOL_REF_FUNCTION_P (func)))
{
/* Save the TOC into its reserved slot before the call,
and prepare to restore it after the call. */
gen_rtx_PLUS (Pmode, stack_ptr,
stack_toc_offset));
MEM_VOLATILE_P (stack_toc_mem) = 1;
- emit_move_insn (stack_toc_mem, toc_reg);
+ if (HAVE_AS_PLTSEQ
+ && TARGET_TLS_MARKERS
+ && DEFAULT_ABI == ABI_ELFv2
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ rtvec v = gen_rtvec (3, toc_reg, func_desc, tlsarg);
+ rtx mark_toc_reg = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (stack_toc_mem, mark_toc_reg));
+ }
+ else
+ emit_move_insn (stack_toc_mem, toc_reg);
}
if (DEFAULT_ABI == ABI_ELFv2)
/* A function pointer in the ELFv2 ABI is just a plain address, but
the ABI requires it to be loaded into r12 before the call. */
func_addr = gen_rtx_REG (Pmode, 12);
- if (!rtx_equal_p (func_addr, func_desc))
- emit_move_insn (func_addr, func_desc);
+ if (!rtx_equal_p (func_addr, func))
+ emit_move_insn (func_addr, func);
abi_reg = func_addr;
+ /* Indirect calls via CTR are strongly preferred over indirect
+ calls via LR, so move the address there. Needed to mark
+ this insn for linker plt sequence editing too. */
+ func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+ if (HAVE_AS_PLTSEQ
+ && TARGET_TLS_MARKERS
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ rtvec v = gen_rtvec (3, abi_reg, func_desc, tlsarg);
+ rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (func_addr, mark_func));
+ v = gen_rtvec (2, func_addr, func_desc);
+ func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ }
+ else
+ emit_move_insn (func_addr, abi_reg);
}
else
{
not have any executable code. */
/* Load up address of the actual function. */
- func_desc = force_reg (Pmode, func_desc);
+ func = force_reg (Pmode, func);
func_addr = gen_reg_rtx (Pmode);
- emit_move_insn (func_addr, gen_rtx_MEM (Pmode, func_desc));
+ emit_move_insn (func_addr, gen_rtx_MEM (Pmode, func));
+
+ /* Indirect calls via CTR are strongly preferred over indirect
+ calls via LR, so move the address there. */
+ rtx ctr_reg = gen_rtx_REG (Pmode, CTR_REGNO);
+ emit_move_insn (ctr_reg, func_addr);
+ func_addr = ctr_reg;
/* Prepare to load the TOC of the called function. Note that the
TOC load must happen immediately before the actual call so
comment in frob_update_context. */
rtx func_toc_offset = GEN_INT (GET_MODE_SIZE (Pmode));
rtx func_toc_mem = gen_rtx_MEM (Pmode,
- gen_rtx_PLUS (Pmode, func_desc,
+ gen_rtx_PLUS (Pmode, func,
func_toc_offset));
toc_load = gen_rtx_USE (VOIDmode, func_toc_mem);
originally direct, the 3rd word has not been written since no
trampoline has been built, so we ought not to load it, lest we
override a static chain value. */
- if (!direct_call_p
+ if (!(GET_CODE (func_desc) == SYMBOL_REF
+ && SYMBOL_REF_FUNCTION_P (func_desc))
&& TARGET_POINTERS_TO_NESTED_FUNCTIONS
&& !chain_already_loaded (get_current_sequence ()->next->last))
{
rtx sc_reg = gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM);
rtx func_sc_offset = GEN_INT (2 * GET_MODE_SIZE (Pmode));
rtx func_sc_mem = gen_rtx_MEM (Pmode,
- gen_rtx_PLUS (Pmode, func_desc,
+ gen_rtx_PLUS (Pmode, func,
func_sc_offset));
emit_move_insn (sc_reg, func_sc_mem);
abi_reg = sc_reg;
assume the TOC register is set; for non-local calls, the
PLT stub needs the TOC register. */
abi_reg = toc_reg;
- func_addr = func_desc;
+ func_addr = func;
}
/* Create the call. */
/* Expand code to perform a call under the SYSV4 ABI. */
void
-rs6000_call_sysv (rtx value, rtx func, rtx tlsarg, rtx cookie)
+rs6000_call_sysv (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
{
+ rtx func = func_desc;
rtx func_addr;
rtx call[3];
rtx insn;
+ rtx abi_reg = NULL_RTX;
if (global_tlsarg)
tlsarg = global_tlsarg;
/* Handle longcall attributes. */
- if (INTVAL (cookie) & CALL_LONG)
- func = rs6000_longcall_ref (func);
+ if ((INTVAL (cookie) & CALL_LONG) != 0
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ func = rs6000_longcall_ref (func_desc, tlsarg);
+ /* If the longcall was implemented using PLT16 relocs, then r11
+ needs to be valid at the call for lazy linking. */
+ if (HAVE_AS_PLTSEQ
+ && TARGET_TLS_MARKERS)
+ abi_reg = func;
+ }
/* Handle indirect calls. */
if (GET_CODE (func) != SYMBOL_REF)
- func_addr = force_reg (Pmode, func);
+ {
+ func = force_reg (Pmode, func);
+
+ /* Indirect calls via CTR are strongly preferred over indirect
+ calls via LR, so move the address there. Needed to mark
+ this insn for linker plt sequence editing too. */
+ func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+ if (HAVE_AS_PLTSEQ
+ && TARGET_TLS_MARKERS
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ rtvec v = gen_rtvec (3, func, func_desc, tlsarg);
+ rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (func_addr, mark_func));
+ v = gen_rtvec (2, func_addr, func_desc);
+ func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ }
+ else
+ emit_move_insn (func_addr, func);
+ }
else
func_addr = func;
insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
insn = emit_call_insn (insn);
+ if (abi_reg)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), abi_reg);
+}
+
+/* Expand code to perform a sibling call under the SysV4 ABI. */
+
+void
+rs6000_sibcall_sysv (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
+{
+ rtx func = func_desc;
+ rtx func_addr;
+ rtx call[3];
+ rtx insn;
+ rtx abi_reg = NULL_RTX;
+
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
+
+ /* Handle longcall attributes. */
+ if ((INTVAL (cookie) & CALL_LONG) != 0
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ func = rs6000_longcall_ref (func_desc, tlsarg);
+ /* If the longcall was implemented using PLT16 relocs, then r11
+ needs to be valid at the call for lazy linking. */
+ if (HAVE_AS_PLTSEQ
+ && TARGET_TLS_MARKERS)
+ abi_reg = func;
+ }
+
+ /* Handle indirect calls. */
+ if (GET_CODE (func) != SYMBOL_REF)
+ {
+ func = force_reg (Pmode, func);
+
+ /* Indirect sibcalls must go via CTR. Needed to mark
+ this insn for linker plt sequence editing too. */
+ func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+ if (HAVE_AS_PLTSEQ
+ && TARGET_TLS_MARKERS
+ && GET_CODE (func_desc) == SYMBOL_REF)
+ {
+ rtvec v = gen_rtvec (3, func, func_desc, tlsarg);
+ rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ emit_insn (gen_rtx_SET (func_addr, mark_func));
+ v = gen_rtvec (2, func_addr, func_desc);
+ func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+ }
+ else
+ emit_move_insn (func_addr, func);
+ }
+ else
+ func_addr = func;
+
+ /* Create the call. */
+ call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), tlsarg);
+ if (value != NULL_RTX)
+ call[0] = gen_rtx_SET (value, call[0]);
+
+ unsigned int mask = CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS;
+ call[1] = gen_rtx_USE (VOIDmode, GEN_INT (INTVAL (cookie) & mask));
+ call[2] = simple_return_rtx;
+
+ insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
+ insn = emit_call_insn (insn);
+ if (abi_reg)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), abi_reg);
}
/* Return whether we need to always update the saved TOC pointer when we update
#define HAVE_AS_TLS 0
#endif
+#ifndef HAVE_AS_PLTSEQ
+#define HAVE_AS_PLTSEQ 0
+#endif
+
#ifndef TARGET_LINK_STACK
#define TARGET_LINK_STACK 0
#endif
UNSPEC_SIGNBIT
UNSPEC_SF_FROM_SI
UNSPEC_SI_FROM_SF
+ UNSPEC_PLTSEQ
+ UNSPEC_PLT16_HA
+ UNSPEC_PLT16_LO
])
;;
(match_operand 2 "" "")))]
"TARGET_ELF && !TARGET_64BIT && !flag_pic"
"la %0,%2@l(%1)")
+
+(define_insn "*pltseq_tocsave_<mode>"
+ [(set (match_operand:P 0 "memory_operand" "=m")
+ (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
+ (match_operand:P 2 "symbol_ref_operand" "s")
+ (match_operand:P 3 "" "")]
+ UNSPEC_PLTSEQ))]
+ "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+ && DEFAULT_ABI == ABI_ELFv2"
+{
+ return rs6000_pltseq_template (operands, 0);
+})
+
+(define_insn "*pltseq_plt16_ha_<mode>"
+ [(set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (unspec:P [(match_operand:P 1 "" "")
+ (match_operand:P 2 "symbol_ref_operand" "s")
+ (match_operand:P 3 "" "")]
+ UNSPEC_PLT16_HA))]
+ "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+ && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4)"
+{
+ return rs6000_pltseq_template (operands, 1);
+})
+
+(define_insn "*pltseq_plt16_lo_<mode>"
+ [(set (match_operand:P 0 "gpc_reg_operand" "=r")
+ (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
+ (match_operand:P 2 "symbol_ref_operand" "s")
+ (match_operand:P 3 "" "")]
+ UNSPEC_PLT16_LO))]
+ "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+ && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4)"
+{
+ return rs6000_pltseq_template (operands, 2);
+}
+ [(set_attr "type" "load")])
+
+(define_insn "*pltseq_mtctr_<mode>"
+ [(set (match_operand:P 0 "register_operand" "=c")
+ (unspec:P [(match_operand:P 1 "gpc_reg_operand" "r")
+ (match_operand:P 2 "symbol_ref_operand" "s")
+ (match_operand:P 3 "" "")]
+ UNSPEC_PLTSEQ))]
+ "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+ && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4)"
+{
+ return rs6000_pltseq_template (operands, 3);
+})
\f
;; Call and call_value insns
(define_expand "call"
;; which indicates how to set cr1
(define_insn "*call_indirect_nonlocal_sysv<mode>"
- [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l,c,*l"))
+ [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
(match_operand 1))
- (use (match_operand:SI 2 "immediate_operand" "O,O,n,n"))
+ (use (match_operand:SI 2 "immediate_operand" "n,n,n"))
(clobber (reg:SI LR_REGNO))]
"DEFAULT_ABI == ABI_V4
|| DEFAULT_ABI == ABI_DARWIN"
return rs6000_indirect_call_template (operands, 0);
}
- [(set_attr "type" "jmpreg,jmpreg,jmpreg,jmpreg")
- (set_attr_alternative "length"
- [(if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "8")
- (const_string "4"))
- (const_string "4")
- (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "12")
- (const_string "8"))
- (const_string "8")])])
+ [(set_attr "type" "jmpreg")
+ (set (attr "length")
+ (cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "12")
+ (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "8")]
+ (const_string "4")))])
(define_insn_and_split "*call_nonlocal_sysv<mode>"
[(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s,s"))
(define_insn "*call_value_indirect_nonlocal_sysv<mode>"
[(set (match_operand 0 "" "")
- (call (mem:SI (match_operand:P 1 "register_operand" "c,*l,c,*l"))
+ (call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
(match_operand 2)))
- (use (match_operand:SI 3 "immediate_operand" "O,O,n,n"))
+ (use (match_operand:SI 3 "immediate_operand" "n,n,n"))
(clobber (reg:SI LR_REGNO))]
"DEFAULT_ABI == ABI_V4
|| DEFAULT_ABI == ABI_DARWIN"
return rs6000_indirect_call_template (operands, 1);
}
- [(set_attr "type" "jmpreg,jmpreg,jmpreg,jmpreg")
- (set_attr_alternative "length"
- [(if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "8")
- (const_string "4"))
- (const_string "4")
- (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "12")
- (const_string "8"))
- (const_string "8")])])
+ [(set_attr "type" "jmpreg")
+ (set (attr "length")
+ (cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "12")
+ (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "8")]
+ (const_string "4")))])
(define_insn_and_split "*call_value_nonlocal_sysv<mode>"
[(set (match_operand 0 "" "")
;; Operand3 is the offset of the stack location holding the current TOC pointer
(define_insn "*call_indirect_aix<mode>"
- [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l"))
+ [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
(match_operand 1))
- (use (match_operand:P 2 "memory_operand" "<ptrm>,<ptrm>"))
- (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+ (use (match_operand:P 2 "memory_operand" "<ptrm>,<ptrm>,<ptrm>"))
+ (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
(clobber (reg:P LR_REGNO))]
"DEFAULT_ABI == ABI_AIX"
{
(define_insn "*call_value_indirect_aix<mode>"
[(set (match_operand 0 "" "")
- (call (mem:SI (match_operand:P 1 "register_operand" "c,*l"))
+ (call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
(match_operand 2)))
- (use (match_operand:P 3 "memory_operand" "<ptrm>,<ptrm>"))
- (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 4 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+ (use (match_operand:P 3 "memory_operand" "<ptrm>,<ptrm>,<ptrm>"))
+ (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 4 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
(clobber (reg:P LR_REGNO))]
"DEFAULT_ABI == ABI_AIX"
{
;; Operand2 is the offset of the stack location holding the current TOC pointer
(define_insn "*call_indirect_elfv2<mode>"
- [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l"))
+ [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
(match_operand 1))
- (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 2 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+ (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 2 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
(clobber (reg:P LR_REGNO))]
"DEFAULT_ABI == ABI_ELFv2"
{
(define_insn "*call_value_indirect_elfv2<mode>"
[(set (match_operand 0 "" "")
- (call (mem:SI (match_operand:P 1 "register_operand" "c,*l"))
+ (call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
(match_operand 2)))
- (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+ (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
(clobber (reg:P LR_REGNO))]
"DEFAULT_ABI == ABI_ELFv2"
{
rs6000_sibcall_aix (NULL_RTX, operands[0], operands[1], operands[2]);
DONE;
}
+
+ if (DEFAULT_ABI == ABI_V4)
+ {
+ rs6000_sibcall_sysv (NULL_RTX, operands[0], operands[1], operands[2]);
+ DONE;
+ }
})
(define_expand "sibcall_value"
rs6000_sibcall_aix (operands[0], operands[1], operands[2], operands[3]);
DONE;
}
+
+ if (DEFAULT_ABI == ABI_V4)
+ {
+ rs6000_sibcall_sysv (operands[0], operands[1], operands[2], operands[3]);
+ DONE;
+ }
})
(define_insn "*sibcall_local32"
[(set_attr "type" "branch")
(set_attr "length" "4,8")])
+(define_insn "*sibcall_indirect_nonlocal_sysv<mode>"
+ [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
+ (match_operand 1))
+ (use (match_operand:SI 2 "immediate_operand" "n,n,n"))
+ (simple_return)]
+ "DEFAULT_ABI == ABI_V4
+ || DEFAULT_ABI == ABI_DARWIN"
+{
+ if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS)
+ output_asm_insn ("crxor 6,6,6", operands);
+
+ else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS)
+ output_asm_insn ("creqv 6,6,6", operands);
+
+ return rs6000_indirect_sibcall_template (operands, 0);
+}
+ [(set_attr "type" "jmpreg")
+ (set (attr "length")
+ (cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "12")
+ (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "8")]
+ (const_string "4")))])
+
(define_insn "*sibcall_nonlocal_sysv<mode>"
- [(call (mem:SI (match_operand:P 0 "call_operand" "s,s,c,c"))
- (match_operand 1 "" ""))
- (use (match_operand 2 "immediate_operand" "O,n,O,n"))
+ [(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s,s"))
+ (match_operand 1))
+ (use (match_operand 2 "immediate_operand" "O,n"))
(simple_return)]
"(DEFAULT_ABI == ABI_DARWIN
|| DEFAULT_ABI == ABI_V4)
else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS)
output_asm_insn ("creqv 6,6,6", operands);
- if (which_alternative >= 2)
- return rs6000_indirect_sibcall_template (operands, 0);
- else
- return rs6000_sibcall_template (operands, 0);
+ return rs6000_sibcall_template (operands, 0);
}
[(set_attr "type" "branch")
- (set_attr_alternative "length"
- [(const_string "4")
- (const_string "8")
- (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "12")
- (const_string "4"))
- (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "16")
- (const_string "8"))])])
+ (set_attr "length" "4,8")])
+
+(define_insn "*sibcall_value_indirect_nonlocal_sysv<mode>"
+ [(set (match_operand 0 "" "")
+ (call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
+ (match_operand 2)))
+ (use (match_operand:SI 3 "immediate_operand" "n,n,n"))
+ (simple_return)]
+ "DEFAULT_ABI == ABI_V4
+ || DEFAULT_ABI == ABI_DARWIN"
+{
+ if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS)
+ output_asm_insn ("crxor 6,6,6", operands);
+
+ else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS)
+ output_asm_insn ("creqv 6,6,6", operands);
+
+ return rs6000_indirect_sibcall_template (operands, 1);
+}
+ [(set_attr "type" "jmpreg")
+ (set (attr "length")
+ (cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "12")
+ (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+ (match_test "which_alternative != 1"))
+ (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+ (const_string "8")]
+ (const_string "4")))])
(define_insn "*sibcall_value_nonlocal_sysv<mode>"
[(set (match_operand 0 "" "")
- (call (mem:SI (match_operand:P 1 "call_operand" "s,s,c,c"))
- (match_operand 2 "" "")))
- (use (match_operand:SI 3 "immediate_operand" "O,n,O,n"))
+ (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s,s"))
+ (match_operand 2)))
+ (use (match_operand:SI 3 "immediate_operand" "O,n"))
(simple_return)]
"(DEFAULT_ABI == ABI_DARWIN
|| DEFAULT_ABI == ABI_V4)
else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS)
output_asm_insn ("creqv 6,6,6", operands);
- if (which_alternative >= 2)
- {
- if (rs6000_speculate_indirect_jumps)
- return "b%T1";
- else
- /* Can use CR0 since it is volatile across sibcalls. */
- return "crset 2\;beq%T1-\;b $";
- }
- else
- return rs6000_sibcall_template (operands, 1);
+ return rs6000_sibcall_template (operands, 1);
}
[(set_attr "type" "branch")
- (set_attr_alternative "length"
- [(const_string "4")
- (const_string "8")
- (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "12")
- (const_string "4"))
- (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
- (const_int 0))
- (const_string "16")
- (const_string "8"))])])
+ (set_attr "length" "4,8")])
;; AIX ABI sibling call patterns.
;;
or1k*-*-*)
conftest_s='
- .section ".tdata","awT",@progbits
-foo: .long 25
- .text
- l.movhi r3, tpoffha(foo)
- l.add r3, r3, r10
- l.lwz r4, tpofflo(foo)(r3)'
+ .section ".tdata","awT",@progbits
+foo: .long 25
+ .text
+ l.movhi r3, tpoffha(foo)
+ l.add r3, r3, r10
+ l.lwz r4, tpofflo(foo)(r3)'
tls_first_major=2
tls_first_minor=30
tls_as_opt=--fatal-warnings
$as_echo "#define HAVE_AS_ENTRY_MARKERS 1" >>confdefs.h
+fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for plt sequence marker support" >&5
+$as_echo_n "checking assembler for plt sequence marker support... " >&6; }
+if ${gcc_cv_as_powerpc_pltseq_markers+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gcc_cv_as_powerpc_pltseq_markers=no
+ if test $in_tree_gas = yes; then
+ if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 31 \) \* 1000 + 0`
+ then gcc_cv_as_powerpc_pltseq_markers=yes
+fi
+ elif test x$gcc_cv_as != x; then
+ $as_echo ' .reloc .,R_PPC_PLTSEQ; nop' > conftest.s
+ if { ac_try='$gcc_cv_as $gcc_cv_as_flags -a32 --fatal-warnings -o conftest.o conftest.s >&5'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }
+ then
+ gcc_cv_as_powerpc_pltseq_markers=yes
+ else
+ echo "configure: failed program was" >&5
+ cat conftest.s >&5
+ fi
+ rm -f conftest.o conftest.s
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_powerpc_pltseq_markers" >&5
+$as_echo "$gcc_cv_as_powerpc_pltseq_markers" >&6; }
+if test $gcc_cv_as_powerpc_pltseq_markers = yes; then
+
+$as_echo "#define HAVE_AS_PLTSEQ 1" >>confdefs.h
+
fi
[AC_DEFINE(HAVE_AS_ENTRY_MARKERS, 1,
[Define if your assembler supports the R_PPC64_ENTRY relocation.])])
+ gcc_GAS_CHECK_FEATURE([plt sequence marker support],
+ gcc_cv_as_powerpc_pltseq_markers, [2,31,0],-a32 --fatal-warnings,
+ [ .reloc .,R_PPC_PLTSEQ; nop],,
+ [AC_DEFINE(HAVE_AS_PLTSEQ, 1,
+ [Define if your assembler supports R_PPC*_PLTSEQ relocations.])])
+
case $target in
*-*-aix*)
gcc_GAS_CHECK_FEATURE([AIX .ref support],