aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/rs6000/rs6000.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2018-11-29 15:17:51 +1030
committerAlan Modra <amodra@gcc.gnu.org>2018-11-29 15:17:51 +1030
commitece3bca2bd08bd02028f9ac6b0a2b6b8fa111305 (patch)
tree04fa524ca73637833e033031b264e7c37c53c227 /gcc/config/rs6000/rs6000.c
parent7a24893b2eb79af5785a7334126a82618a549c1e (diff)
downloadgcc-ece3bca2bd08bd02028f9ac6b0a2b6b8fa111305.zip
gcc-ece3bca2bd08bd02028f9ac6b0a2b6b8fa111305.tar.gz
gcc-ece3bca2bd08bd02028f9ac6b0a2b6b8fa111305.tar.bz2
[RS6000] Use standard call patterns for __tls_get_addr calls
The current code handling __tls_get_addr calls for powerpc*-linux generates a call then overwrites the call insn with a special tls_{gd,ld}_{aix,sysv} pattern. It's done that way to support !TARGET_TLS_MARKERS, where the arg setup insns need to be emitted immediately before the branch and link. When TARGET_TLS_MARKERS, the arg setup insns are split from the actual call, but we then have a non-standard call pattern that needs to be carried through to output. This patch changes that scheme, to instead use the standard call patterns for __tls_get_addr calls, except for the now rare !TARGET_TLS_MARKERS case. Doing it this way should be better for maintenance as the !TARGET_TLS_MARKERS code can eventually disappear. It also makes it possible to support longcalls (and in following patches, inline plt calls) for __tls_get_addr without introducing yet more special call patterns. __tls_get_addr calls do however need to be different to standard calls, because when TARGET_TLS_MARKERS the calls are decorated with an argument specifier, eg. "bl __tls_get_addr(thread_var@tlsgd)" that causes a reloc to be emitted by the assembler tying the call to its arg setup insns. I chose to smuggle the arg in the currently unused stack size rtl. I've also introduced rs6000_call_sysv to generate rtl for sysv calls, as rs6000_call_aix does for aix and elfv2 calls. This allows rs6000_longcall_ref to be local to rs6000.c since the calls in the expanders never did anything for darwin. * config/rs6000/predicates.md (unspec_tls): New. * config/rs6000/rs6000-protos.h (rs6000_call_template), (rs6000_sibcall_template): Update prototype. (rs6000_longcall_ref): Delete. (rs6000_call_sysv): Declare. * config/rs6000/rs6000.c (edit_tls_call_insn): New function. (global_tlsarg): New variable. (rs6000_legitimize_tls_address): Rewrite __tls_get_addr call handling. (print_operand): Extract UNSPEC_TLSGD address operand. (rs6000_call_template, rs6000_sibcall_template): Remove arg parameter, extract from second call operand instead. (rs6000_longcall_ref): Make static, localize vars. (rs6000_call_aix): Rename parameter to reflect new usage. Take tlsarg from global_tlsarg. Don't create unused rtl or nop insns. (rs6000_sibcall_aix): Rename parameter to reflect new usage. Take tlsarg from global_tlsarg. (rs6000_call_sysv): New function. * config/rs6000/rs6000.md: Adjust rs6000_call_template and rs6000_sibcall_template throughout. (tls_gd_aix, tls_gd_sysv, tls_gd_call_aix, tls_gd_call_sysv): Delete. (tls_ld_aix, tls_ld_sysv, tls_ld_call_aix, tls_ld_call_sysv): Delete. (tls_gdld_nomark): New insn. (tls_gd): Swap operand order. Simplify mode selection. (tls_gd_high, tls_gd_low): Swap operand order. (tls_ld): Remove const_int 0 vector element from UNSPEC_TLSLD. Simplify mode selection. (tls_ld_high, tls_ld_low): Similarly adjust UNSPEC_TLSLD. (call, call_value): Don't assert for second call operand. Use rs6000_call_sysv. From-SVN: r266604
Diffstat (limited to 'gcc/config/rs6000/rs6000.c')
-rw-r--r--gcc/config/rs6000/rs6000.c224
1 files changed, 150 insertions, 74 deletions
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 497a157..f337606 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -8566,6 +8566,43 @@ rs6000_legitimize_tls_address_aix (rtx addr, enum tls_model model)
return dest;
}
+/* Mess with a call, to make it look like the tls_gdld insns when
+ !TARGET_TLS_MARKERS. These insns have an extra unspec to
+ differentiate them from standard calls, because they need to emit
+ the arg setup insns as well as the actual call. That keeps the
+ arg setup insns immediately adjacent to the branch and link. */
+
+static void
+edit_tls_call_insn (rtx arg)
+{
+ rtx call_insn = last_call_insn ();
+ if (!TARGET_TLS_MARKERS)
+ {
+ rtx patt = PATTERN (call_insn);
+ gcc_assert (GET_CODE (patt) == PARALLEL);
+ rtvec orig = XVEC (patt, 0);
+ rtvec v = rtvec_alloc (GET_NUM_ELEM (orig) + 1);
+ gcc_assert (GET_NUM_ELEM (orig) > 0);
+ /* The (set (..) (call (mem ..))). */
+ RTVEC_ELT (v, 0) = RTVEC_ELT (orig, 0);
+ /* The extra unspec. */
+ RTVEC_ELT (v, 1) = arg;
+ /* All other assorted call pattern pieces. */
+ for (int i = 1; i < GET_NUM_ELEM (orig); i++)
+ RTVEC_ELT (v, i + 1) = RTVEC_ELT (orig, i);
+ XVEC (patt, 0) = v;
+ }
+ if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn),
+ pic_offset_table_rtx);
+}
+
+/* Passes the tls arg value for global dynamic and local dynamic
+ emit_library_call_value in rs6000_legitimize_tls_address to
+ rs6000_call_aix and rs6000_call_sysv. This is used to emit the
+ marker relocs put on __tls_get_addr calls. */
+static rtx global_tlsarg;
+
/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute
this (thread-local) address. */
@@ -8618,7 +8655,7 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
}
else
{
- rtx r3, got, tga, tmp1, tmp2, call_insn;
+ rtx got, tga, tmp1, tmp2;
/* We currently use relocations like @got@tlsgd for tls, which
means the linker will handle allocation of tls entries, placing
@@ -8658,52 +8695,42 @@ rs6000_legitimize_tls_address (rtx addr, enum tls_model model)
if (model == TLS_MODEL_GLOBAL_DYNAMIC)
{
+ rtx arg = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, addr, got),
+ UNSPEC_TLSGD);
+ global_tlsarg = arg;
+ rtx argreg = const0_rtx;
+ if (TARGET_TLS_MARKERS)
+ {
+ argreg = gen_rtx_REG (Pmode, 3);
+ emit_insn (gen_rtx_SET (argreg, arg));
+ }
+
tga = rs6000_tls_get_addr ();
emit_library_call_value (tga, dest, LCT_CONST, Pmode,
- const0_rtx, Pmode);
+ argreg, Pmode);
+ global_tlsarg = NULL_RTX;
- r3 = gen_rtx_REG (Pmode, 3);
- if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
- {
- if (TARGET_64BIT)
- insn = gen_tls_gd_aix64 (r3, got, addr, tga, const0_rtx);
- else
- insn = gen_tls_gd_aix32 (r3, got, addr, tga, const0_rtx);
- }
- else if (DEFAULT_ABI == ABI_V4)
- insn = gen_tls_gd_sysvsi (r3, got, addr, tga, const0_rtx);
- else
- gcc_unreachable ();
- call_insn = last_call_insn ();
- PATTERN (call_insn) = insn;
- if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn),
- pic_offset_table_rtx);
+ edit_tls_call_insn (arg);
}
else if (model == TLS_MODEL_LOCAL_DYNAMIC)
{
+ rtx arg = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got),
+ UNSPEC_TLSLD);
+ global_tlsarg = arg;
+ rtx argreg = const0_rtx;
+ if (TARGET_TLS_MARKERS)
+ {
+ argreg = gen_rtx_REG (Pmode, 3);
+ emit_insn (gen_rtx_SET (argreg, arg));
+ }
+
tga = rs6000_tls_get_addr ();
tmp1 = gen_reg_rtx (Pmode);
emit_library_call_value (tga, tmp1, LCT_CONST, Pmode,
- const0_rtx, Pmode);
+ argreg, Pmode);
+ global_tlsarg = NULL_RTX;
- r3 = gen_rtx_REG (Pmode, 3);
- if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
- {
- if (TARGET_64BIT)
- insn = gen_tls_ld_aix64 (r3, got, tga, const0_rtx);
- else
- insn = gen_tls_ld_aix32 (r3, got, tga, const0_rtx);
- }
- else if (DEFAULT_ABI == ABI_V4)
- insn = gen_tls_ld_sysvsi (r3, got, tga, const0_rtx);
- else
- gcc_unreachable ();
- call_insn = last_call_insn ();
- PATTERN (call_insn) = insn;
- if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
- use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn),
- pic_offset_table_rtx);
+ edit_tls_call_insn (arg);
if (rs6000_tls_size == 16)
{
@@ -21170,19 +21197,19 @@ print_operand (FILE *file, rtx x, int code)
else
output_address (GET_MODE (x), XEXP (x, 0));
}
+ else if (toc_relative_expr_p (x, false,
+ &tocrel_base_oac, &tocrel_offset_oac))
+ /* This hack along with a corresponding hack in
+ rs6000_output_addr_const_extra arranges to output addends
+ where the assembler expects to find them. eg.
+ (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 4)
+ without this hack would be output as "x@toc+4". We
+ want "x+4@toc". */
+ 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 (toc_relative_expr_p (x, false, &tocrel_base_oac, &tocrel_offset_oac))
- /* This hack along with a corresponding hack in
- rs6000_output_addr_const_extra arranges to output addends
- where the assembler expects to find them. eg.
- (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 4)
- without this hack would be output as "x@toc+4". We
- want "x+4@toc". */
- output_addr_const (file, CONST_CAST_RTX (tocrel_base_oac));
- else
- output_addr_const (file, x);
- }
+ output_addr_const (file, x);
return;
case '&':
@@ -21368,18 +21395,27 @@ rs6000_assemble_integer (rtx x, unsigned int size, int aligned_p)
}
/* Return a template string for assembly to emit when making an
- external call. FUNOP is the call mem argument operand number,
- ARG is either NULL or a @TLSGD or @TLSLD __tls_get_addr argument
- specifier. */
+ external call. FUNOP is the call mem argument operand number. */
static const char *
-rs6000_call_template_1 (rtx *operands ATTRIBUTE_UNUSED, unsigned int funop,
- bool sibcall, const char *arg)
+rs6000_call_template_1 (rtx *operands, unsigned int funop, bool sibcall)
{
/* -Wformat-overflow workaround, without which gcc thinks that %u
might produce 10 digits. */
gcc_assert (funop <= MAX_RECOG_OPERANDS);
+ char arg[12];
+ arg[0] = 0;
+ if (TARGET_TLS_MARKERS && GET_CODE (operands[funop + 1]) == UNSPEC)
+ {
+ if (XINT (operands[funop + 1], 1) == UNSPEC_TLSGD)
+ sprintf (arg, "(%%%u@tlsgd)", funop + 1);
+ else if (XINT (operands[funop + 1], 1) == UNSPEC_TLSLD)
+ sprintf (arg, "(%%&@tlsld)");
+ else
+ gcc_unreachable ();
+ }
+
/* The magic 32768 offset here corresponds to the offset of
r30 in .got2, as given by LCTOC1. See sysv4.h:toc_section. */
char z[11];
@@ -21387,7 +21423,7 @@ rs6000_call_template_1 (rtx *operands ATTRIBUTE_UNUSED, unsigned int funop,
(DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic == 2
? "+32768" : ""));
- static char str[32]; /* 4 spare */
+ static char str[32]; /* 2 spare */
if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
sprintf (str, "b%s %s%s%s", sibcall ? "" : "l", z, arg,
sibcall ? "" : "\n\tnop");
@@ -21400,15 +21436,15 @@ rs6000_call_template_1 (rtx *operands ATTRIBUTE_UNUSED, unsigned int funop,
}
const char *
-rs6000_call_template (rtx *operands, unsigned int funop, const char *arg)
+rs6000_call_template (rtx *operands, unsigned int funop)
{
- return rs6000_call_template_1 (operands, funop, false, arg);
+ return rs6000_call_template_1 (operands, funop, false);
}
const char *
-rs6000_sibcall_template (rtx *operands, unsigned int funop, const char *arg)
+rs6000_sibcall_template (rtx *operands, unsigned int funop)
{
- return rs6000_call_template_1 (operands, funop, true, arg);
+ return rs6000_call_template_1 (operands, funop, true);
}
/* As above, for indirect calls. */
@@ -32498,23 +32534,20 @@ rs6000_set_default_type_attributes (tree type)
/* Return a reference suitable for calling a function with the
longcall attribute. */
-rtx
+static rtx
rs6000_longcall_ref (rtx call_ref)
{
- const char *call_name;
- tree node;
-
if (GET_CODE (call_ref) != SYMBOL_REF)
return call_ref;
/* System V adds '.' to the internal name, so skip them. */
- call_name = XSTR (call_ref, 0);
+ const char *call_name = XSTR (call_ref, 0);
if (*call_name == '.')
{
while (*call_name == '.')
call_name++;
- node = get_identifier (call_name);
+ tree node = get_identifier (call_name);
call_ref = gen_rtx_SYMBOL_REF (VOIDmode, IDENTIFIER_POINTER (node));
}
@@ -37485,7 +37518,7 @@ chain_already_loaded (rtx_insn *last)
/* Expand code to perform a call under the AIX or ELFv2 ABI. */
void
-rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
+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);
@@ -37498,6 +37531,9 @@ rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
int n_call;
rtx insn;
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
+
/* Handle longcall attributes. */
if (INTVAL (cookie) & CALL_LONG)
func_desc = rs6000_longcall_ref (func_desc);
@@ -37508,11 +37544,7 @@ rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
{
/* Save the TOC into its reserved slot before the call,
and prepare to restore it after the call. */
- rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
rtx stack_toc_offset = GEN_INT (RS6000_TOC_SAVE_SLOT);
- rtx stack_toc_mem = gen_frame_mem (Pmode,
- gen_rtx_PLUS (Pmode, stack_ptr,
- stack_toc_offset));
rtx stack_toc_unspec = gen_rtx_UNSPEC (Pmode,
gen_rtvec (1, stack_toc_offset),
UNSPEC_TOCSLOT);
@@ -37524,6 +37556,10 @@ rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
cfun->machine->save_toc_in_prologue = true;
else
{
+ rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+ rtx stack_toc_mem = gen_frame_mem (Pmode,
+ gen_rtx_PLUS (Pmode, stack_ptr,
+ stack_toc_offset));
MEM_VOLATILE_P (stack_toc_mem) = 1;
emit_move_insn (stack_toc_mem, toc_reg);
}
@@ -37533,7 +37569,8 @@ rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
/* 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);
- emit_move_insn (func_addr, func_desc);
+ if (!rtx_equal_p (func_addr, func_desc))
+ emit_move_insn (func_addr, func_desc);
abi_reg = func_addr;
}
else
@@ -37588,7 +37625,7 @@ rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
}
/* Create the call. */
- call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), flag);
+ 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]);
n_call = 1;
@@ -37612,15 +37649,18 @@ rs6000_call_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
/* Expand code to perform a sibling call under the AIX or ELFv2 ABI. */
void
-rs6000_sibcall_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
+rs6000_sibcall_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
{
rtx call[2];
rtx insn;
gcc_assert (INTVAL (cookie) == 0);
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
+
/* Create the call. */
- call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_desc), flag);
+ call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_desc), tlsarg);
if (value != NULL_RTX)
call[0] = gen_rtx_SET (value, call[0]);
@@ -37633,6 +37673,42 @@ rs6000_sibcall_aix (rtx value, rtx func_desc, rtx flag, rtx cookie)
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), gen_rtx_REG (Pmode, TOC_REGNUM));
}
+/* Expand code to perform a call under the SYSV4 ABI. */
+
+void
+rs6000_call_sysv (rtx value, rtx func, rtx tlsarg, rtx cookie)
+{
+ rtx func_addr;
+ rtx call[3];
+ rtx insn;
+
+ if (global_tlsarg)
+ tlsarg = global_tlsarg;
+
+ /* Handle longcall attributes. */
+ if (INTVAL (cookie) & CALL_LONG)
+ func = rs6000_longcall_ref (func);
+
+ /* Handle indirect calls. */
+ if (GET_CODE (func) != SYMBOL_REF)
+ func_addr = force_reg (Pmode, 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] = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, LR_REGNO));
+
+ insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
+ insn = emit_call_insn (insn);
+}
+
/* Return whether we need to always update the saved TOC pointer when we update
the stack pointer. */