diff mbox series

[5/6,RS6000] Use standard call patterns for __tls_get_addr calls

Message ID 20181113125243.GU29784@bubble.grove.modra.org
State New
Headers show
Series [1/6,RS6000] rs6000_call_template for external call insn assembly output | expand

Commit Message

Alan Modra Nov. 13, 2018, 12:52 p.m. UTC
Version 2.

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_aix, tls_gdld_sysv): New insns, replacing above.
	(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.

Comments

Segher Boessenkool Nov. 27, 2018, 4:29 p.m. UTC | #1
Hi!

On Tue, Nov 13, 2018 at 11:22:43PM +1030, Alan Modra wrote:
> Version 2.
> 
> 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_aix, tls_gdld_sysv): New insns, replacing above.
> 	(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.


> +/* 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;

Typo (s/_Tls/_tls/).

> +(define_insn "*tls_gdld_aix<P:bits>"
> +  [(match_parallel 3 ""

A match_parallel without predicate...  Does this work?!  Does this not
accidentally pick up the wrong things?

Do you think we should to deprecate -mtls-markers in GCC 9?

Please test with -mtls-markers, too, if you can, and test on AIX.

Looks fine.  Thank you for the cleanup!  Okay for trunk, but please do the
extra testing.


Segher
Alan Modra Nov. 28, 2018, 1:07 a.m. UTC | #2
On Tue, Nov 27, 2018 at 10:29:29AM -0600, Segher Boessenkool wrote:
> Hi!

Thanks for the review!

> > +(define_insn "*tls_gdld_aix<P:bits>"
> > +  [(match_parallel 3 ""
> 
> A match_parallel without predicate...  Does this work?!

Yes.  In fact, rs6000/predicates.md any_parallel_operand is useless
except as documentation.  The only thing it checks is that its operand
is a parallel, but that has already been checked.

>  Does this not
> accidentally pick up the wrong things?

No.  The purpose of the predicate is to match anything beyond the
vector of expressions.  So tls_gdld_aix* matches insns that look like:

     (set (match_operand:P 0 "gpc_reg_operand" "=b")
	  (call (mem:SI (match_operand:P 1))
		(match_operand:P 2 "unspec_tls")))
     (match_dup 2)
     ...

This is sufficiently different from other calls, by virtue of the
"(match_dup 2)".

Incidentally, I think tls_gdld_aix and tls_gdld_sysv could be merged
at the expense of complicating the length attribute expression.

> Do you think we should to deprecate -mtls-markers in GCC 9?

Support for the TLS marker relocs was added to binutils in 2009 (git
commit 727fc41e077), so yes, the option is not likely to be useful
nowadays.

> Please test with -mtls-markers, too, if you can, and test on AIX.

Presumably you mean -mno-tls-markers.  -mtls-markers is the default.

> Looks fine.  Thank you for the cleanup!  Okay for trunk, but please do the
> extra testing.

Huh, local testing of -mno-tls-markers showed a lack of a
TARGET_TLS_MARKERS check in rs6000_call_template_1.  Likely this would
blow up on AIX.  I'll test with the following delta.

diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 56ca117a0a0..5f4fcee3b33 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -8622,7 +8622,7 @@ edit_tls_call_insn (rtx arg)
 }
 
 /* Passes the tls arg value for global dynamic and local dynamic
-   emit_library_call_value in rs6000_legitimize_Tls_address to
+   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;
@@ -21429,7 +21429,7 @@ rs6000_call_template_1 (rtx *operands, unsigned int funop, bool sibcall)
 
   char arg[12];
   arg[0] = 0;
-  if (GET_CODE (operands[funop + 1]) == UNSPEC)
+  if (TARGET_TLS_MARKERS && GET_CODE (operands[funop + 1]) == UNSPEC)
     {
       if (XINT (operands[funop + 1], 1) == UNSPEC_TLSGD)
 	sprintf (arg, "(%%%u@tlsgd)", funop + 1);
Segher Boessenkool Nov. 28, 2018, 1:32 p.m. UTC | #3
On Wed, Nov 28, 2018 at 11:37:42AM +1030, Alan Modra wrote:
> On Tue, Nov 27, 2018 at 10:29:29AM -0600, Segher Boessenkool wrote:
> > Hi!
> 
> Thanks for the review!
> 
> > > +(define_insn "*tls_gdld_aix<P:bits>"
> > > +  [(match_parallel 3 ""
> > 
> > A match_parallel without predicate...  Does this work?!
> 
> Yes.  In fact, rs6000/predicates.md any_parallel_operand is useless
> except as documentation.  The only thing it checks is that its operand
> is a parallel, but that has already been checked.

Right.  I grepped if there were other parallels without predicate, not
finding any, but I did not notice that half of the predicates used are
any_parallel_operand.

> >  Does this not
> > accidentally pick up the wrong things?
> 
> No.  The purpose of the predicate is to match anything beyond the
> vector of expressions.  So tls_gdld_aix* matches insns that look like:
> 
>      (set (match_operand:P 0 "gpc_reg_operand" "=b")
> 	  (call (mem:SI (match_operand:P 1))
> 		(match_operand:P 2 "unspec_tls")))
>      (match_dup 2)
>      ...
> 
> This is sufficiently different from other calls, by virtue of the
> "(match_dup 2)".

And the "unspec_tls".  Right.

> Incidentally, I think tls_gdld_aix and tls_gdld_sysv could be merged
> at the expense of complicating the length attribute expression.
> 
> > Do you think we should to deprecate -mtls-markers in GCC 9?
> 
> Support for the TLS marker relocs was added to binutils in 2009 (git
> commit 727fc41e077), so yes, the option is not likely to be useful
> nowadays.

Well, but do we gain anything if it is eventually deleted?  Simpler code?

> > Please test with -mtls-markers, too, if you can, and test on AIX.
> 
> Presumably you mean -mno-tls-markers.  -mtls-markers is the default.

Yeah, the ! case.

> > Looks fine.  Thank you for the cleanup!  Okay for trunk, but please do the
> > extra testing.
> 
> Huh, local testing of -mno-tls-markers showed a lack of a
> TARGET_TLS_MARKERS check in rs6000_call_template_1.  Likely this would
> blow up on AIX.  I'll test with the following delta.

Sounds good.


Segher
Alan Modra Nov. 28, 2018, 11:08 p.m. UTC | #4
On Wed, Nov 28, 2018 at 07:32:50AM -0600, Segher Boessenkool wrote:
> On Wed, Nov 28, 2018 at 11:37:42AM +1030, Alan Modra wrote:
> > On Tue, Nov 27, 2018 at 10:29:29AM -0600, Segher Boessenkool wrote:
> > > Do you think we should to deprecate -mtls-markers in GCC 9?
> > 
> > Support for the TLS marker relocs was added to binutils in 2009 (git
> > commit 727fc41e077), so yes, the option is not likely to be useful
> > nowadays.
> 
> Well, but do we gain anything if it is eventually deleted?  Simpler code?

Not much.  Unfortunately we still need to keep TARGET_TLS_MARKERS (or
replace throughout with !HAVE_AS_TLS_MARKERS) for AIX.  Deprecate AIX
perhaps?  :-)
Alan Modra Nov. 29, 2018, 6:11 a.m. UTC | #5
On Wed, Nov 28, 2018 at 07:32:50AM -0600, Segher Boessenkool wrote:
> On Wed, Nov 28, 2018 at 11:37:42AM +1030, Alan Modra wrote:
> > On Tue, Nov 27, 2018 at 10:29:29AM -0600, Segher Boessenkool wrote:
> > > Looks fine.  Thank you for the cleanup!  Okay for trunk, but please do the
> > > extra testing.
> > 
> > Huh, local testing of -mno-tls-markers showed a lack of a
> > TARGET_TLS_MARKERS check in rs6000_call_template_1.  Likely this would
> > blow up on AIX.  I'll test with the following delta.
> 
> Sounds good.

For the record, this is the patch I committed.  Besides the delta
posted previously, I managed to combine the tls_gdld_aix and
tls_gdld_sysv insns into a single insn, tls_gdld_nomark.

	* 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.

diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index 5589ea19519..2c297fc45e8 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -997,6 +997,13 @@ (define_predicate "rs6000_tls_symbol_ref"
   (and (match_code "symbol_ref")
        (match_test "RS6000_SYMBOL_REF_TLS_P (op)")))
 
+;; Return 1 for the UNSPEC used in TLS call operands
+(define_predicate "unspec_tls"
+  (match_code "unspec")
+{
+  return XINT (op, 1) == UNSPEC_TLSGD || XINT (op, 1) == UNSPEC_TLSLD;
+})
+
 ;; Return 1 if the operand, used inside a MEM, is a valid first argument
 ;; to CALL.  This is a SYMBOL_REF, a pseudo-register, LR or CTR.
 (define_predicate "call_operand"
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index f1a294a3617..dd930bb2da6 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -105,8 +105,8 @@ extern int ccr_bit (rtx, int);
 extern void rs6000_output_function_entry (FILE *, const char *);
 extern void print_operand (FILE *, rtx, int);
 extern void print_operand_address (FILE *, rtx);
-extern const char *rs6000_call_template (rtx *, unsigned int, const char *);
-extern const char *rs6000_sibcall_template (rtx *, unsigned int, const char *);
+extern const char *rs6000_call_template (rtx *, unsigned int);
+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 enum rtx_code rs6000_reverse_condition (machine_mode,
@@ -130,7 +130,6 @@ extern void rs6000_expand_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx);
 extern void rs6000_emit_swdiv (rtx, rtx, rtx, bool);
 extern void rs6000_emit_swsqrt (rtx, rtx, bool);
 extern void output_toc (FILE *, rtx, int, machine_mode);
-extern rtx rs6000_longcall_ref (rtx);
 extern void rs6000_fatal_bad_address (rtx);
 extern rtx create_TOC_reference (rtx, rtx);
 extern void rs6000_split_multireg_move (rtx, rtx);
@@ -198,6 +197,7 @@ extern void rs6000_split_stack_space_check (rtx, rtx);
 extern void rs6000_emit_eh_reg_restore (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_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);
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 497a157b89c..f3376065ee1 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.  */
 
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index b223e0b266d..ac87bb96436 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -9422,74 +9422,51 @@ (define_peephole2
 
 ;; TLS support.
 
-(define_insn_and_split "tls_gd_aix<bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 3 "symbol_ref_operand" "s"))
-	      (match_operand 4)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-	      (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
-{
-  if (TARGET_CMODEL != CMODEL_SMALL)
-    output_asm_insn ("addis %0,%1,%2@got@tlsgd@ha\;"
-		     "addi %0,%0,%2@got@tlsgd@l", operands);
+(define_insn "*tls_gdld_nomark<bits>"
+  [(match_parallel 3 ""
+    [(set (match_operand:P 0 "gpc_reg_operand" "=b")
+	  (call (mem:SI (match_operand:P 1))
+		(match_operand:P 2 "unspec_tls")))
+     (match_dup 2)])]
+  "HAVE_AS_TLS && !TARGET_TLS_MARKERS && DEFAULT_ABI != ABI_DARWIN"
+{
+  rtx op[3];
+  op[0] = operands[0];
+  op[1] = XVECEXP (operands[2], 0, 0);
+  if (XINT (operands[2], 1) == UNSPEC_TLSGD)
+    {
+      op[2] = XVECEXP (operands[2], 0, 1);
+      if (TARGET_CMODEL != CMODEL_SMALL)
+	output_asm_insn ("addis %0,%2,%1@got@tlsgd@ha\;"
+			 "addi %0,%0,%1@got@tlsgd@l", op);
+      else
+	output_asm_insn ("addi %0,%2,%1@got@tlsgd", op);
+    }
   else
-    output_asm_insn ("addi %0,%1,%2@got@tlsgd", operands);
-  return rs6000_call_template (operands, 3, "");
+    {
+      if (TARGET_CMODEL != CMODEL_SMALL)
+	output_asm_insn ("addis %0,%1,%&@got@tlsld@ha\;"
+			 "addi %0,%0,%&@got@tlsld@l", op);
+      else
+	output_asm_insn ("addi %0,%1,%&@got@tlsld", op);
+    }
+  return rs6000_call_template (operands, 1);
 }
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)
-		   (match_dup 2)]
-		  UNSPEC_TLSGD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 3))
-			 (match_dup 4)))
-	      (unspec:P [(match_dup 2)] UNSPEC_TLSGD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
   [(set_attr "type" "two")
    (set (attr "length")
-     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
-		   (const_int 16)
-		   (const_int 12)))])
-
-(define_insn_and_split "tls_gd_sysv<mode>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 3 "symbol_ref_operand" "s"))
-	      (match_operand 4)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-	      (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4"
-{
-  output_asm_insn ("addi %0,%1,%2@got@tlsgd", operands);
-  return rs6000_call_template (operands, 3, "");
-}
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)
-		   (match_dup 2)]
-		  UNSPEC_TLSGD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 3))
-			 (match_dup 4)))
-	      (unspec:P [(match_dup 2)] UNSPEC_TLSGD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
-  [(set_attr "type" "two")
-   (set_attr "length" "8")])
+     (cond [(match_test "TARGET_CMODEL != CMODEL_SMALL")
+		(const_int 16)
+	    (match_test "DEFAULT_ABI != ABI_V4")
+		(const_int 12)]
+	(const_int 8)))])
 
 (define_insn_and_split "*tls_gd<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-		   (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+	(unspec:P [(match_operand:P 1 "rs6000_tls_symbol_ref" "")
+		   (match_operand:P 2 "gpc_reg_operand" "b")]
 		  UNSPEC_TLSGD))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS"
-  "addi %0,%1,%2@got@tlsgd"
+  "addi %0,%2,%1@got@tlsgd"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 3)
 	(high:P
@@ -9498,7 +9475,7 @@ (define_insn_and_split "*tls_gd<bits>"
 	(lo_sum:P (match_dup 3)
 	    (unspec:P [(match_dup 1) (match_dup 2)] UNSPEC_TLSGD)))]
 {
-  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+  operands[3] = gen_reg_rtx (<MODE>mode);
 }
   [(set (attr "length")
      (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
@@ -9508,105 +9485,21 @@ (define_insn_and_split "*tls_gd<bits>"
 (define_insn "*tls_gd_high<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (high:P
-       (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-		  (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+       (unspec:P [(match_operand:P 1 "rs6000_tls_symbol_ref" "")
+		  (match_operand:P 2 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSGD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
-  "addis %0,%1,%2@got@tlsgd@ha")
+  "addis %0,%2,%1@got@tlsgd@ha")
 
 (define_insn "*tls_gd_low<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (lo_sum:P (match_operand:P 1 "gpc_reg_operand" "b")
-       (unspec:P [(match_operand:P 3 "gpc_reg_operand" "b")
-		  (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+       (unspec:P [(match_operand:P 2 "rs6000_tls_symbol_ref" "")
+		  (match_operand:P 3 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSGD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addi %0,%1,%2@got@tlsgd@l")
 
-(define_insn "*tls_gd_call_aix<bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(match_operand:P 3 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && TARGET_TLS_MARKERS
-   && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
-{
-  return rs6000_call_template (operands, 1, "(%3@tlsgd)");
-}
-  [(set_attr "type" "branch")
-   (set_attr "length" "8")])
-
-(define_insn "*tls_gd_call_sysv<bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(match_operand:P 3 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4 && TARGET_TLS_MARKERS"
-{
-  return rs6000_call_template (operands, 1, "(%3@tlsgd)");
-}
-  [(set_attr "type" "branch")])
-
-(define_insn_and_split "tls_ld_aix<bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 2 "symbol_ref_operand" "s"))
-	      (match_operand 3)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
-	     UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
-{
-  if (TARGET_CMODEL != CMODEL_SMALL)
-    output_asm_insn ("addis %0,%1,%&@got@tlsld@ha\;"
-		     "addi %0,%0,%&@got@tlsld@l", operands);
-  else
-    output_asm_insn ("addi %0,%1,%&@got@tlsld", operands);
-  return rs6000_call_template (operands, 2, "");
-}
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)]
-		  UNSPEC_TLSLD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 2))
-			 (match_dup 3)))
-	      (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
-  [(set_attr "type" "two")
-   (set (attr "length")
-     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
-		   (const_int 16)
-		   (const_int 12)))])
-
-(define_insn_and_split "tls_ld_sysv<mode>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 2 "symbol_ref_operand" "s"))
-	      (match_operand 3)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
-	     UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4"
-{
-  output_asm_insn ("addi %0,%1,%&@got@tlsld", operands);
-  return rs6000_call_template (operands, 2, "");
-}
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)]
-		  UNSPEC_TLSLD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 2))
-			 (match_dup 3)))
-	      (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
-  [(set_attr "length" "8")])
-
 (define_insn_and_split "*tls_ld<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
 	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
@@ -9616,12 +9509,12 @@ (define_insn_and_split "*tls_ld<bits>"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 2)
 	(high:P
-	    (unspec:P [(const_int 0) (match_dup 1)] UNSPEC_TLSLD)))
+	    (unspec:P [(match_dup 1)] UNSPEC_TLSLD)))
    (set (match_dup 0)
 	(lo_sum:P (match_dup 2)
-	    (unspec:P [(const_int 0) (match_dup 1)] UNSPEC_TLSLD)))]
+	    (unspec:P [(match_dup 1)] UNSPEC_TLSLD)))]
 {
-  operands[2] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+  operands[2] = gen_reg_rtx (<MODE>mode);
 }
   [(set (attr "length")
      (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
@@ -9631,8 +9524,7 @@ (define_insn_and_split "*tls_ld<bits>"
 (define_insn "*tls_ld_high<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (high:P
-       (unspec:P [(const_int 0)
-		  (match_operand:P 1 "gpc_reg_operand" "b")]
+       (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSLD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addis %0,%1,%&@got@tlsld@ha")
@@ -9640,38 +9532,11 @@ (define_insn "*tls_ld_high<bits>"
 (define_insn "*tls_ld_low<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (lo_sum:P (match_operand:P 1 "gpc_reg_operand" "b")
-       (unspec:P [(const_int 0)
-		  (match_operand:P 2 "gpc_reg_operand" "b")]
+       (unspec:P [(match_operand:P 2 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSLD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addi %0,%1,%&@got@tlsld@l")
 
-(define_insn "*tls_ld_call_aix<bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && TARGET_TLS_MARKERS
-   && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
-{
-  return rs6000_call_template (operands, 1, "(%&@tlsld)");
-}
-  [(set_attr "type" "branch")
-   (set_attr "length" "8")])
-
-(define_insn "*tls_ld_call_sysv<bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4 && TARGET_TLS_MARKERS"
-{
-  return rs6000_call_template (operands, 1, "(%&@tlsld)");
-}
-  [(set_attr "type" "branch")])
-
 (define_insn "tls_dtprel_<bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=r")
 	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
@@ -10345,7 +10210,6 @@ (define_expand "call"
 #endif
 
   gcc_assert (GET_CODE (operands[0]) == MEM);
-  gcc_assert (GET_CODE (operands[1]) == CONST_INT);
 
   operands[0] = XEXP (operands[0], 0);
 
@@ -10355,23 +10219,14 @@ (define_expand "call"
       DONE;
     }
 
-  if (GET_CODE (operands[0]) != SYMBOL_REF
-      || (DEFAULT_ABI != ABI_DARWIN && (INTVAL (operands[2]) & CALL_LONG) != 0))
+  if (DEFAULT_ABI == ABI_V4)
     {
-      if (INTVAL (operands[2]) & CALL_LONG)
-	operands[0] = rs6000_longcall_ref (operands[0]);
-
-      switch (DEFAULT_ABI)
-        {
-	case ABI_V4:
-	case ABI_DARWIN:
-	  operands[0] = force_reg (Pmode, operands[0]);
-	  break;
-
-	default:
-	  gcc_unreachable ();
-	}
+      rs6000_call_sysv (NULL_RTX, operands[0], operands[1], operands[2]);
+      DONE;
     }
+
+  if (GET_CODE (operands[0]) != SYMBOL_REF)
+    operands[0] = force_reg (Pmode, operands[0]);
 })
 
 (define_expand "call_value"
@@ -10388,7 +10243,6 @@ (define_expand "call_value"
 #endif
 
   gcc_assert (GET_CODE (operands[1]) == MEM);
-  gcc_assert (GET_CODE (operands[2]) == CONST_INT);
 
   operands[1] = XEXP (operands[1], 0);
 
@@ -10398,23 +10252,14 @@ (define_expand "call_value"
       DONE;
     }
 
-  if (GET_CODE (operands[1]) != SYMBOL_REF
-      || (DEFAULT_ABI != ABI_DARWIN && (INTVAL (operands[3]) & CALL_LONG) != 0))
+  if (DEFAULT_ABI == ABI_V4)
     {
-      if (INTVAL (operands[3]) & CALL_LONG)
-	operands[1] = rs6000_longcall_ref (operands[1]);
-
-      switch (DEFAULT_ABI)
-        {
-	case ABI_V4:
-	case ABI_DARWIN:
-	  operands[1] = force_reg (Pmode, operands[1]);
-	  break;
-
-	default:
-	  gcc_unreachable ();
-	}
+      rs6000_call_sysv (operands[0], operands[1], operands[2], operands[3]);
+      DONE;
     }
+
+  if (GET_CODE (operands[1]) != SYMBOL_REF)
+    operands[1] = force_reg (Pmode, operands[1]);
 })
 
 ;; Call to function in current module.  No TOC pointer reload needed.
@@ -10501,7 +10346,7 @@ (define_insn "*call_value_local64"
 
 ;; A function pointer under System V is just a normal pointer
 ;; operands[0] is the function pointer
-;; operands[1] is the stack size to clean up
+;; operands[1] is the tls call arg
 ;; operands[2] is the value FUNCTION_ARG returns for the VOID argument
 ;; which indicates how to set cr1
 
@@ -10552,7 +10397,7 @@ (define_insn_and_split "*call_nonlocal_sysv<mode>"
 #if TARGET_MACHO
   return macho_call_template (insn, operands, 0, 2);
 #else
-  return rs6000_call_template (operands, 0, "");
+  return rs6000_call_template (operands, 0);
 #endif
 }
   "DEFAULT_ABI == ABI_V4
@@ -10585,7 +10430,7 @@ (define_insn "*call_nonlocal_sysv_secure<mode>"
   else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS)
     output_asm_insn ("creqv 6,6,6", operands);
 
-  return rs6000_call_template (operands, 0, "");
+  return rs6000_call_template (operands, 0);
 }
   [(set_attr "type" "branch,branch")
    (set_attr "length" "4,8")])
@@ -10639,7 +10484,7 @@ (define_insn_and_split "*call_value_nonlocal_sysv<mode>"
 #if TARGET_MACHO
   return macho_call_template (insn, operands, 1, 3);
 #else
-  return rs6000_call_template (operands, 1, "");
+  return rs6000_call_template (operands, 1);
 #endif
 }
   "DEFAULT_ABI == ABI_V4
@@ -10674,7 +10519,7 @@ (define_insn "*call_value_nonlocal_sysv_secure<mode>"
   else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS)
     output_asm_insn ("creqv 6,6,6", operands);
 
-  return rs6000_call_template (operands, 1, "");
+  return rs6000_call_template (operands, 1);
 }
   [(set_attr "type" "branch,branch")
    (set_attr "length" "4,8")])
@@ -10708,7 +10553,7 @@ (define_insn "*call_nonlocal_aix<mode>"
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
-  return rs6000_call_template (operands, 0, "");
+  return rs6000_call_template (operands, 0);
 }
   [(set_attr "type" "branch")
    (set_attr "length" "8")])
@@ -10720,7 +10565,7 @@ (define_insn "*call_value_nonlocal_aix<mode>"
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
-  return rs6000_call_template (operands, 1, "");
+  return rs6000_call_template (operands, 1);
 }
   [(set_attr "type" "branch")
    (set_attr "length" "8")])
@@ -10971,7 +10816,7 @@ (define_insn "*sibcall_nonlocal_sysv<mode>"
   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"
@@ -11011,7 +10856,7 @@ (define_insn "*sibcall_value_nonlocal_sysv<mode>"
 	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"
@@ -11035,7 +10880,7 @@ (define_insn "*sibcall_aix<mode>"
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
   if (which_alternative == 0)
-    return rs6000_sibcall_template (operands, 0, "");
+    return rs6000_sibcall_template (operands, 0);
   else
     return "b%T0";
 }
@@ -11049,7 +10894,7 @@ (define_insn "*sibcall_value_aix<mode>"
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
   if (which_alternative == 0)
-    return rs6000_sibcall_template (operands, 1, "");
+    return rs6000_sibcall_template (operands, 1);
   else
     return "b%T1";
 }
diff mbox series

Patch

diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index b80c278d742..7e45d2f0371 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -1039,6 +1039,13 @@  (define_predicate "rs6000_tls_symbol_ref"
   (and (match_code "symbol_ref")
        (match_test "RS6000_SYMBOL_REF_TLS_P (op)")))
 
+;; Return 1 for the UNSPEC used in TLS call operands
+(define_predicate "unspec_tls"
+  (match_code "unspec")
+{
+  return XINT (op, 1) == UNSPEC_TLSGD || XINT (op, 1) == UNSPEC_TLSLD;
+})
+
 ;; Return 1 if the operand, used inside a MEM, is a valid first argument
 ;; to CALL.  This is a SYMBOL_REF, a pseudo-register, LR or CTR.
 (define_predicate "call_operand"
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index 967f65e2d94..3fd89dc20db 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -111,8 +111,8 @@  extern int ccr_bit (rtx, int);
 extern void rs6000_output_function_entry (FILE *, const char *);
 extern void print_operand (FILE *, rtx, int);
 extern void print_operand_address (FILE *, rtx);
-extern const char *rs6000_call_template (rtx *, unsigned int, const char *);
-extern const char *rs6000_sibcall_template (rtx *, unsigned int, const char *);
+extern const char *rs6000_call_template (rtx *, unsigned int);
+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 enum rtx_code rs6000_reverse_condition (machine_mode,
@@ -136,7 +136,6 @@  extern void rs6000_expand_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx);
 extern void rs6000_emit_swdiv (rtx, rtx, rtx, bool);
 extern void rs6000_emit_swsqrt (rtx, rtx, bool);
 extern void output_toc (FILE *, rtx, int, machine_mode);
-extern rtx rs6000_longcall_ref (rtx);
 extern void rs6000_fatal_bad_address (rtx);
 extern rtx create_TOC_reference (rtx, rtx);
 extern void rs6000_split_multireg_move (rtx, rtx);
@@ -204,6 +203,7 @@  extern void rs6000_split_stack_space_check (rtx, rtx);
 extern void rs6000_emit_eh_reg_restore (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_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);
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index cd1ab95166e..5fd6e17bdda 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -8583,6 +8583,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.  */
 
@@ -8635,7 +8672,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
@@ -8675,52 +8712,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)
 	    {
@@ -21175,19 +21202,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 '&':
@@ -21373,18 +21400,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 (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];
@@ -21392,7 +21428,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");
@@ -21405,15 +21441,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.  */
@@ -32503,23 +32539,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));
     }
 
@@ -37491,7 +37524,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);
@@ -37504,6 +37537,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);
@@ -37514,11 +37550,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);
@@ -37530,6 +37562,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);
 	}
@@ -37539,7 +37575,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
@@ -37594,7 +37631,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;
@@ -37618,15 +37655,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]);
 
@@ -37639,6 +37679,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.  */
 
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index c261c8bb9c1..336d42335cb 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -9436,74 +9436,73 @@  (define_peephole2
 
 ;; TLS support.
 
-(define_insn_and_split "tls_gd_aix<P:bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 3 "symbol_ref_operand" "s"))
-	      (match_operand 4)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-	      (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
+(define_insn "*tls_gdld_aix<P:bits>"
+  [(match_parallel 3 ""
+    [(set (match_operand:P 0 "gpc_reg_operand" "=b")
+	  (call (mem:SI (match_operand:P 1))
+		(match_operand:P 2 "unspec_tls")))
+     (match_dup 2)])]
+  "HAVE_AS_TLS && !TARGET_TLS_MARKERS
+   && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
 {
-  if (TARGET_CMODEL != CMODEL_SMALL)
-    output_asm_insn ("addis %0,%1,%2@got@tlsgd@ha\;"
-		     "addi %0,%0,%2@got@tlsgd@l", operands);
+  rtx op[3];
+  op[0] = operands[0];
+  op[1] = XVECEXP (operands[2], 0, 0);
+  if (XINT (operands[2], 1) == UNSPEC_TLSGD)
+    {
+      op[2] = XVECEXP (operands[2], 0, 1);
+      if (TARGET_CMODEL != CMODEL_SMALL)
+	output_asm_insn ("addis %0,%2,%1@got@tlsgd@ha\;"
+			 "addi %0,%0,%1@got@tlsgd@l", op);
+      else
+	output_asm_insn ("addi %0,%2,%1@got@tlsgd", op);
+    }
   else
-    output_asm_insn ("addi %0,%1,%2@got@tlsgd", operands);
-  return rs6000_call_template (operands, 3, "");
+    {
+      if (TARGET_CMODEL != CMODEL_SMALL)
+	output_asm_insn ("addis %0,%1,%&@got@tlsld@ha\;"
+			 "addi %0,%0,%&@got@tlsld@l", op);
+      else
+	output_asm_insn ("addi %0,%1,%&@got@tlsld", op);
+    }
+  return rs6000_call_template (operands, 1);
 }
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)
-		   (match_dup 2)]
-		  UNSPEC_TLSGD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 3))
-			 (match_dup 4)))
-	      (unspec:P [(match_dup 2)] UNSPEC_TLSGD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
   [(set_attr "type" "two")
    (set (attr "length")
      (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
 		   (const_int 16)
 		   (const_int 12)))])
 
-(define_insn_and_split "tls_gd_sysv<P:mode>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 3 "symbol_ref_operand" "s"))
-	      (match_operand 4)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-	      (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4"
+(define_insn "*tls_gdld_sysv<P:bits>"
+  [(match_parallel 3 ""
+    [(set (match_operand:P 0 "gpc_reg_operand" "=b")
+	  (call (mem:SI (match_operand:P 1))
+		(match_operand:P 2 "unspec_tls")))
+     (match_dup 2)])]
+  "HAVE_AS_TLS && !TARGET_TLS_MARKERS && DEFAULT_ABI == ABI_V4"
 {
-  output_asm_insn ("addi %0,%1,%2@got@tlsgd", operands);
-  return rs6000_call_template (operands, 3, "");
+  rtx op[3];
+  op[0] = operands[0];
+  op[1] = XVECEXP (operands[2], 0, 0);
+  if (XINT (operands[2], 1) == UNSPEC_TLSGD)
+    {
+      op[2] = XVECEXP (operands[2], 0, 1);
+      output_asm_insn ("addi %0,%2,%1@got@tlsgd", op);
+    }
+  else
+    output_asm_insn ("addi %0,%1,%&@got@tlsld", op);
+  return rs6000_call_template (operands, 1);
 }
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)
-		   (match_dup 2)]
-		  UNSPEC_TLSGD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 3))
-			 (match_dup 4)))
-	      (unspec:P [(match_dup 2)] UNSPEC_TLSGD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
   [(set_attr "type" "two")
    (set_attr "length" "8")])
 
 (define_insn_and_split "*tls_gd<P:bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-		   (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+	(unspec:P [(match_operand:P 1 "rs6000_tls_symbol_ref" "")
+		   (match_operand:P 2 "gpc_reg_operand" "b")]
 		  UNSPEC_TLSGD))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS"
-  "addi %0,%1,%2@got@tlsgd"
+  "addi %0,%2,%1@got@tlsgd"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 3)
 	(high:P
@@ -9512,7 +9511,7 @@  (define_insn_and_split "*tls_gd<P:bits>"
 	(lo_sum:P (match_dup 3)
 	    (unspec:P [(match_dup 1) (match_dup 2)] UNSPEC_TLSGD)))]
 {
-  operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+  operands[3] = gen_reg_rtx (<MODE>mode);
 }
   [(set (attr "length")
      (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
@@ -9522,105 +9521,21 @@  (define_insn_and_split "*tls_gd<P:bits>"
 (define_insn "*tls_gd_high<P:bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (high:P
-       (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
-		  (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+       (unspec:P [(match_operand:P 1 "rs6000_tls_symbol_ref" "")
+		  (match_operand:P 2 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSGD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
-  "addis %0,%1,%2@got@tlsgd@ha")
+  "addis %0,%2,%1@got@tlsgd@ha")
 
 (define_insn "*tls_gd_low<P:bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (lo_sum:P (match_operand:P 1 "gpc_reg_operand" "b")
-       (unspec:P [(match_operand:P 3 "gpc_reg_operand" "b")
-		  (match_operand:P 2 "rs6000_tls_symbol_ref" "")]
+       (unspec:P [(match_operand:P 2 "rs6000_tls_symbol_ref" "")
+		  (match_operand:P 3 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSGD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addi %0,%1,%2@got@tlsgd@l")
 
-(define_insn "*tls_gd_call_aix<P:bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(match_operand:P 3 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && TARGET_TLS_MARKERS
-   && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
-{
-  return rs6000_call_template (operands, 1, "(%3@tlsgd)");
-}
-  [(set_attr "type" "branch")
-   (set_attr "length" "8")])
-
-(define_insn "*tls_gd_call_sysv<P:bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(match_operand:P 3 "rs6000_tls_symbol_ref" "")]
-	     UNSPEC_TLSGD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4 && TARGET_TLS_MARKERS"
-{
-  return rs6000_call_template (operands, 1, "(%3@tlsgd)");
-}
-  [(set_attr "type" "branch")])
-
-(define_insn_and_split "tls_ld_aix<P:bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 2 "symbol_ref_operand" "s"))
-	      (match_operand 3)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
-	     UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
-{
-  if (TARGET_CMODEL != CMODEL_SMALL)
-    output_asm_insn ("addis %0,%1,%&@got@tlsld@ha\;"
-		     "addi %0,%0,%&@got@tlsld@l", operands);
-  else
-    output_asm_insn ("addi %0,%1,%&@got@tlsld", operands);
-  return rs6000_call_template (operands, 2, "");
-}
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)]
-		  UNSPEC_TLSLD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 2))
-			 (match_dup 3)))
-	      (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
-  [(set_attr "type" "two")
-   (set (attr "length")
-     (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
-		   (const_int 16)
-		   (const_int 12)))])
-
-(define_insn_and_split "tls_ld_sysv<P:mode>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 2 "symbol_ref_operand" "s"))
-	      (match_operand 3)))
-   (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
-	     UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4"
-{
-  output_asm_insn ("addi %0,%1,%&@got@tlsld", operands);
-  return rs6000_call_template (operands, 2, "");
-}
-  "&& TARGET_TLS_MARKERS"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 1)]
-		  UNSPEC_TLSLD))
-   (parallel [(set (match_dup 0)
-		   (call (mem:SI (match_dup 2))
-			 (match_dup 3)))
-	      (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-	      (clobber (reg:SI LR_REGNO))])]
-  ""
-  [(set_attr "length" "8")])
-
 (define_insn_and_split "*tls_ld<P:bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
 	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
@@ -9630,12 +9545,12 @@  (define_insn_and_split "*tls_ld<P:bits>"
   "&& TARGET_CMODEL != CMODEL_SMALL"
   [(set (match_dup 2)
 	(high:P
-	    (unspec:P [(const_int 0) (match_dup 1)] UNSPEC_TLSLD)))
+	    (unspec:P [(match_dup 1)] UNSPEC_TLSLD)))
    (set (match_dup 0)
 	(lo_sum:P (match_dup 2)
-	    (unspec:P [(const_int 0) (match_dup 1)] UNSPEC_TLSLD)))]
+	    (unspec:P [(match_dup 1)] UNSPEC_TLSLD)))]
 {
-  operands[2] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode);
+  operands[2] = gen_reg_rtx (<MODE>mode);
 }
   [(set (attr "length")
      (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL"))
@@ -9645,8 +9560,7 @@  (define_insn_and_split "*tls_ld<P:bits>"
 (define_insn "*tls_ld_high<P:bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (high:P
-       (unspec:P [(const_int 0)
-		  (match_operand:P 1 "gpc_reg_operand" "b")]
+       (unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSLD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addis %0,%1,%&@got@tlsld@ha")
@@ -9654,38 +9568,11 @@  (define_insn "*tls_ld_high<P:bits>"
 (define_insn "*tls_ld_low<P:bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=b")
      (lo_sum:P (match_operand:P 1 "gpc_reg_operand" "b")
-       (unspec:P [(const_int 0)
-		  (match_operand:P 2 "gpc_reg_operand" "b")]
+       (unspec:P [(match_operand:P 2 "gpc_reg_operand" "b")]
 		 UNSPEC_TLSLD)))]
   "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL"
   "addi %0,%1,%&@got@tlsld@l")
 
-(define_insn "*tls_ld_call_aix<P:bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && TARGET_TLS_MARKERS
-   && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)"
-{
-  return rs6000_call_template (operands, 1, "(%&@tlsld)");
-}
-  [(set_attr "type" "branch")
-   (set_attr "length" "8")])
-
-(define_insn "*tls_ld_call_sysv<P:bits>"
-  [(set (match_operand:P 0 "gpc_reg_operand" "=b")
-        (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s"))
-	      (match_operand 2)))
-   (unspec:P [(const_int 0)] UNSPEC_TLSLD)
-   (clobber (reg:SI LR_REGNO))]
-  "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4 && TARGET_TLS_MARKERS"
-{
-  return rs6000_call_template (operands, 1, "(%&@tlsld)");
-}
-  [(set_attr "type" "branch")])
-
 (define_insn "tls_dtprel_<P:bits>"
   [(set (match_operand:P 0 "gpc_reg_operand" "=r")
 	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
@@ -10359,7 +10246,6 @@  (define_expand "call"
 #endif
 
   gcc_assert (GET_CODE (operands[0]) == MEM);
-  gcc_assert (GET_CODE (operands[1]) == CONST_INT);
 
   operands[0] = XEXP (operands[0], 0);
 
@@ -10369,23 +10255,14 @@  (define_expand "call"
       DONE;
     }
 
-  if (GET_CODE (operands[0]) != SYMBOL_REF
-      || (DEFAULT_ABI != ABI_DARWIN && (INTVAL (operands[2]) & CALL_LONG) != 0))
+  if (DEFAULT_ABI == ABI_V4)
     {
-      if (INTVAL (operands[2]) & CALL_LONG)
-	operands[0] = rs6000_longcall_ref (operands[0]);
-
-      switch (DEFAULT_ABI)
-        {
-	case ABI_V4:
-	case ABI_DARWIN:
-	  operands[0] = force_reg (Pmode, operands[0]);
-	  break;
-
-	default:
-	  gcc_unreachable ();
-	}
+      rs6000_call_sysv (NULL_RTX, operands[0], operands[1], operands[2]);
+      DONE;
     }
+
+  if (GET_CODE (operands[0]) != SYMBOL_REF)
+    operands[0] = force_reg (Pmode, operands[0]);
 })
 
 (define_expand "call_value"
@@ -10402,7 +10279,6 @@  (define_expand "call_value"
 #endif
 
   gcc_assert (GET_CODE (operands[1]) == MEM);
-  gcc_assert (GET_CODE (operands[2]) == CONST_INT);
 
   operands[1] = XEXP (operands[1], 0);
 
@@ -10412,23 +10288,14 @@  (define_expand "call_value"
       DONE;
     }
 
-  if (GET_CODE (operands[1]) != SYMBOL_REF
-      || (DEFAULT_ABI != ABI_DARWIN && (INTVAL (operands[3]) & CALL_LONG) != 0))
+  if (DEFAULT_ABI == ABI_V4)
     {
-      if (INTVAL (operands[3]) & CALL_LONG)
-	operands[1] = rs6000_longcall_ref (operands[1]);
-
-      switch (DEFAULT_ABI)
-        {
-	case ABI_V4:
-	case ABI_DARWIN:
-	  operands[1] = force_reg (Pmode, operands[1]);
-	  break;
-
-	default:
-	  gcc_unreachable ();
-	}
+      rs6000_call_sysv (operands[0], operands[1], operands[2], operands[3]);
+      DONE;
     }
+
+  if (GET_CODE (operands[1]) != SYMBOL_REF)
+    operands[1] = force_reg (Pmode, operands[1]);
 })
 
 ;; Call to function in current module.  No TOC pointer reload needed.
@@ -10515,7 +10382,7 @@  (define_insn "*call_value_local64"
 
 ;; A function pointer under System V is just a normal pointer
 ;; operands[0] is the function pointer
-;; operands[1] is the stack size to clean up
+;; operands[1] is the tls call arg
 ;; operands[2] is the value FUNCTION_ARG returns for the VOID argument
 ;; which indicates how to set cr1
 
@@ -10566,7 +10433,7 @@  (define_insn_and_split "*call_nonlocal_sysv<mode>"
 #if TARGET_MACHO
   return macho_call_template (insn, operands, 0, 2);
 #else
-  return rs6000_call_template (operands, 0, "");
+  return rs6000_call_template (operands, 0);
 #endif
 }
   "DEFAULT_ABI == ABI_V4
@@ -10599,7 +10466,7 @@  (define_insn "*call_nonlocal_sysv_secure<mode>"
   else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS)
     output_asm_insn ("creqv 6,6,6", operands);
 
-  return rs6000_call_template (operands, 0, "");
+  return rs6000_call_template (operands, 0);
 }
   [(set_attr "type" "branch,branch")
    (set_attr "length" "4,8")])
@@ -10653,7 +10520,7 @@  (define_insn_and_split "*call_value_nonlocal_sysv<mode>"
 #if TARGET_MACHO
   return macho_call_template (insn, operands, 1, 3);
 #else
-  return rs6000_call_template (operands, 1, "");
+  return rs6000_call_template (operands, 1);
 #endif
 }
   "DEFAULT_ABI == ABI_V4
@@ -10688,7 +10555,7 @@  (define_insn "*call_value_nonlocal_sysv_secure<mode>"
   else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS)
     output_asm_insn ("creqv 6,6,6", operands);
 
-  return rs6000_call_template (operands, 1, "");
+  return rs6000_call_template (operands, 1);
 }
   [(set_attr "type" "branch,branch")
    (set_attr "length" "4,8")])
@@ -10722,7 +10589,7 @@  (define_insn "*call_nonlocal_aix<mode>"
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
-  return rs6000_call_template (operands, 0, "");
+  return rs6000_call_template (operands, 0);
 }
   [(set_attr "type" "branch")
    (set_attr "length" "8")])
@@ -10734,7 +10601,7 @@  (define_insn "*call_value_nonlocal_aix<mode>"
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
-  return rs6000_call_template (operands, 1, "");
+  return rs6000_call_template (operands, 1);
 }
   [(set_attr "type" "branch")
    (set_attr "length" "8")])
@@ -10985,7 +10852,7 @@  (define_insn "*sibcall_nonlocal_sysv<mode>"
   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"
@@ -11025,7 +10892,7 @@  (define_insn "*sibcall_value_nonlocal_sysv<mode>"
 	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"
@@ -11049,7 +10916,7 @@  (define_insn "*sibcall_aix<mode>"
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
   if (which_alternative == 0)
-    return rs6000_sibcall_template (operands, 0, "");
+    return rs6000_sibcall_template (operands, 0);
   else
     return "b%T0";
 }
@@ -11063,7 +10930,7 @@  (define_insn "*sibcall_value_aix<mode>"
   "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2"
 {
   if (which_alternative == 0)
-    return rs6000_sibcall_template (operands, 1, "");
+    return rs6000_sibcall_template (operands, 1);
   else
     return "b%T1";
 }