diff mbox series

[6/6,RS6000] inline plt call sequences

Message ID 20181113125343.GV29784@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:53 p.m. UTC
Version 2.

Finally, the point of the previous patches in this series, support for
inline PLT calls, keyed off -fno-plt.  This emits code using new
relocations that tie all insns in the sequence together, so that the
linker can edit the sequence back to a direct call should the call
target turn out to be local.  An example of ELFv2 code to call puts is
as follows:

     .reloc .,R_PPC64_PLTSEQ,puts
        std 2,24(1)
     .reloc .,R_PPC64_PLT16_HA,puts
        addis 12,2,0
     .reloc .,R_PPC64_PLT16_LO_DS,puts
        ld 12,0(12)
     .reloc .,R_PPC64_PLTSEQ,puts
        mtctr 12
     .reloc .,R_PPC64_PLTCALL,puts
        bctrl
        ld 2,24(1)

"addis 12,2,puts@plt@ha" and "ld 12,puts@plt@l(12)" are also supported
by the assembler.  gcc instead uses the explicit R_PPC64_PLT16_HA and
R_PPC64_PLT16_LO_DS relocs because when the call is to __tls_get_addr
an extra reloc is emitted at every place where one is shown above, to
specify the __tls_get_addr arg.  The linker expects the extra reloc to
come first.  .reloc enforces that ordering.

The patch also changes code emitted for longcalls if the assembler
supports the new marker relocs, so that these too can be edited.  One
side effect of longcalls using PLT16 relocs is that they can now be
resolved lazily by ld.so.

I don't support lazy inline PLT calls for ELFv1, because ELFv1 would
need barriers to reliably load both the function address and toc
pointer from the PLT.  ELFv1 -fno-plt uses the longcall sequence
instead, which isn't edited by GNU ld.

	* config.in (HAVE_AS_PLTSEQ): Add.
	* config/rs6000/predicates.md (indirect_call_operand): New.
	* config/rs6000/rs6000-protos.h (rs6000_pltseq_template),
	(rs6000_sibcall_sysv): Declare.
	* config/rs6000/rs6000.c (init_cumulative_args): Set cookie
	CALL_LONG for -fno-plt.
	(print_operand <T, z, 0>): Handle UNSPEC_PLTSEQ.
	(rs6000_indirect_call_template_1): Emit .reloc directives for
	UNSPEC_PLTSEQ calls.
	(rs6000_pltseq_template): New function.
	(rs6000_longcall_ref): Add arg parameter.  Use PLT16 insns if
	relocs supported by assembler.  Move SYMBOL_REF test to callers.
	(rs6000_call_aix): Adjust rs6000_longcall_ref call.  Package
	insns in UNSPEC_PLTSEQ, preserving original func_desc.
	(rs6000_call_sysv): Likewise.
	(rs6000_sibcall_sysv): New function.
	* config/rs6000/rs6000.h (HAVE_AS_PLTSEQ): Provide default.
	* config/rs6000/rs6000.md (UNSPEC_PLTSEQ, UNSPEC_PLT16_HA,
	UNSPEC_PLT16_LO): New.
	(pltseq_tocsave, pltseq_plt16_ha, pltseq_plt16_lo, pltseq_mtctr): New.
	(call_indirect_nonlocal_sysv): Don't differentiate zero from non-zero
	cookie in constraints.  Test explicitly for flags in length attr.
	Handle unspec operand 1.
	(call_value_indirect_nonlocal_sysv): Likewise.
	(call_indirect_aix, call_value_indirect_aix): Handle unspec operand 1.
	(call_indirect_elfv2, call_value_indirect_elfv2): Likewise.
	(sibcall, sibcall_value): Use rs6000_sibcall_sysv.
	(sibcall_indirect_nonlocal_sysv): New pattern.
	(sibcall_value_indirect_nonlocal_sysv): Likewise.
	(sibcall_nonlocal_sysv, sibcall_value_nonlocal_sysv): Remove indirect
	call alternatives.
	* configure.ac: Check for gas plt sequence marker support.
	* configure: Regenerate.

Comments

Segher Boessenkool Nov. 27, 2018, 5:17 p.m. UTC | #1
Hi Alan,

On Tue, Nov 13, 2018 at 11:23:43PM +1030, Alan Modra wrote:
> Finally, the point of the previous patches in this series, support for
> inline PLT calls, keyed off -fno-plt.  This emits code using new
> relocations that tie all insns in the sequence together, so that the
> linker can edit the sequence back to a direct call should the call
> target turn out to be local.  An example of ELFv2 code to call puts is
> as follows:
> 
>      .reloc .,R_PPC64_PLTSEQ,puts
>         std 2,24(1)
>      .reloc .,R_PPC64_PLT16_HA,puts
>         addis 12,2,0
>      .reloc .,R_PPC64_PLT16_LO_DS,puts
>         ld 12,0(12)
>      .reloc .,R_PPC64_PLTSEQ,puts
>         mtctr 12
>      .reloc .,R_PPC64_PLTCALL,puts
>         bctrl
>         ld 2,24(1)
> 
> "addis 12,2,puts@plt@ha" and "ld 12,puts@plt@l(12)" are also supported
> by the assembler.  gcc instead uses the explicit R_PPC64_PLT16_HA and
> R_PPC64_PLT16_LO_DS relocs because when the call is to __tls_get_addr
> an extra reloc is emitted at every place where one is shown above, to
> specify the __tls_get_addr arg.  The linker expects the extra reloc to
> come first.  .reloc enforces that ordering.
> 
> The patch also changes code emitted for longcalls if the assembler
> supports the new marker relocs, so that these too can be edited.  One
> side effect of longcalls using PLT16 relocs is that they can now be
> resolved lazily by ld.so.
> 
> I don't support lazy inline PLT calls for ELFv1, because ELFv1 would
> need barriers to reliably load both the function address and toc
> pointer from the PLT.  ELFv1 -fno-plt uses the longcall sequence
> instead, which isn't edited by GNU ld.

That all sounds great :-)  Has this been tested with older binutils, too?


> 	* config.in (HAVE_AS_PLTSEQ): Add.
> 	* config/rs6000/predicates.md (indirect_call_operand): New.
> 	* config/rs6000/rs6000-protos.h (rs6000_pltseq_template),
> 	(rs6000_sibcall_sysv): Declare.
> 	* config/rs6000/rs6000.c (init_cumulative_args): Set cookie
> 	CALL_LONG for -fno-plt.
> 	(print_operand <T, z, 0>): Handle UNSPEC_PLTSEQ.
> 	(rs6000_indirect_call_template_1): Emit .reloc directives for
> 	UNSPEC_PLTSEQ calls.
> 	(rs6000_pltseq_template): New function.
> 	(rs6000_longcall_ref): Add arg parameter.  Use PLT16 insns if
> 	relocs supported by assembler.  Move SYMBOL_REF test to callers.
> 	(rs6000_call_aix): Adjust rs6000_longcall_ref call.  Package
> 	insns in UNSPEC_PLTSEQ, preserving original func_desc.
> 	(rs6000_call_sysv): Likewise.
> 	(rs6000_sibcall_sysv): New function.
> 	* config/rs6000/rs6000.h (HAVE_AS_PLTSEQ): Provide default.
> 	* config/rs6000/rs6000.md (UNSPEC_PLTSEQ, UNSPEC_PLT16_HA,
> 	UNSPEC_PLT16_LO): New.
> 	(pltseq_tocsave, pltseq_plt16_ha, pltseq_plt16_lo, pltseq_mtctr): New.
> 	(call_indirect_nonlocal_sysv): Don't differentiate zero from non-zero
> 	cookie in constraints.  Test explicitly for flags in length attr.
> 	Handle unspec operand 1.
> 	(call_value_indirect_nonlocal_sysv): Likewise.
> 	(call_indirect_aix, call_value_indirect_aix): Handle unspec operand 1.
> 	(call_indirect_elfv2, call_value_indirect_elfv2): Likewise.
> 	(sibcall, sibcall_value): Use rs6000_sibcall_sysv.
> 	(sibcall_indirect_nonlocal_sysv): New pattern.
> 	(sibcall_value_indirect_nonlocal_sysv): Likewise.
> 	(sibcall_nonlocal_sysv, sibcall_value_nonlocal_sysv): Remove indirect
> 	call alternatives.
> 	* configure.ac: Check for gas plt sequence marker support.
> 	* configure: Regenerate.


> @@ -10727,10 +10727,20 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
>      cum->nargs_prototype = n_named_args;
>  
>    /* Check for a longcall attribute.  */
> -  if ((!fntype && rs6000_default_long_calls)
> -      || (fntype
> -	  && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
> -	  && !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype))))
> +  if (((!fntype && rs6000_default_long_calls)
> +       || (fntype
> +	   && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
> +	   && !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype))))
> +      || (DEFAULT_ABI != ABI_DARWIN
> +	  && !(fndecl
> +	       && !DECL_EXTERNAL (fndecl)
> +	       && !DECL_WEAK (fndecl)
> +	       && (*targetm.binds_local_p) (fndecl))
> +	  && (flag_plt
> +	      ? (fntype
> +		 && lookup_attribute ("noplt", TYPE_ATTRIBUTES (fntype)))
> +	      : !(fntype
> +		  && lookup_attribute ("plt", TYPE_ATTRIBUTES (fntype))))))
>      cum->call_cookie |= CALL_LONG;

Could you split this into a few cases?  Maybe with some extra locals.  It
is hard to understand the code as it is.

I mean like

  if (...)
    cum->call_cookie |= CALL_LONG;
  if (...)
    cum->call_cookie |= CALL_LONG;
  if (...)
    cum->call_cookie |= CALL_LONG;


> +(define_insn "*sibcall_indirect_nonlocal_sysv<mode>"

> +   (set (attr "length")
> +	(cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
> +			 (match_test "which_alternative != 1"))
> +		    (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
> +		  (const_string "12")
> +	       (ior (and (match_test "!rs6000_speculate_indirect_jumps")
> +			 (match_test "which_alternative != 1"))
> +		   (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
> +		  (const_string "8")]
> +	      (const_string "4")))])

Please use set_attr_alternative for these.


Looks fine modulo those nits.  Okay for trunk.  Thanks!


Segher
Alan Modra Nov. 28, 2018, 1:34 a.m. UTC | #2
On Tue, Nov 27, 2018 at 11:17:48AM -0600, Segher Boessenkool wrote:
> That all sounds great :-)  Has this been tested with older binutils, too?

Yes, lots of times.  Sometimes by accident when I forgot to install
a new binutils.  :-)

> > +(define_insn "*sibcall_indirect_nonlocal_sysv<mode>"
> 
> > +   (set (attr "length")
> > +	(cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
> > +			 (match_test "which_alternative != 1"))
> > +		    (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
> > +		  (const_string "12")
> > +	       (ior (and (match_test "!rs6000_speculate_indirect_jumps")
> > +			 (match_test "which_alternative != 1"))
> > +		   (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
> > +		  (const_string "8")]
> > +	      (const_string "4")))])
> 
> Please use set_attr_alternative for these.

Are you sure about that?  The expression blows out to something like
(completely untested):

   (set_attr_alternative "length"
     [(cond [(and (match_test "!rs6000_speculate_indirect_jumps")
		  (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
		(const_string "12")
	     (ior (match_test "!rs6000_speculate_indirect_jumps")
		  (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
		(const_string "8")]
	   (const_string "4"))
      (cond [(match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))")
		(const_string "8")]
	   (const_string "4"))
      (cond [(and (match_test "!rs6000_speculate_indirect_jumps")
		  (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
		(const_string "12")
	     (ior (match_test "!rs6000_speculate_indirect_jumps")
		  (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
		(const_string "8")]
	   (const_string "4"))])
Segher Boessenkool Nov. 28, 2018, 1:39 p.m. UTC | #3
On Wed, Nov 28, 2018 at 12:04:01PM +1030, Alan Modra wrote:
> On Tue, Nov 27, 2018 at 11:17:48AM -0600, Segher Boessenkool wrote:
> > That all sounds great :-)  Has this been tested with older binutils, too?
> 
> Yes, lots of times.  Sometimes by accident when I forgot to install
> a new binutils.  :-)
> 
> > > +(define_insn "*sibcall_indirect_nonlocal_sysv<mode>"
> > 
> > > +   (set (attr "length")
> > > +	(cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
> > > +			 (match_test "which_alternative != 1"))
> > > +		    (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
> > > +		  (const_string "12")
> > > +	       (ior (and (match_test "!rs6000_speculate_indirect_jumps")
> > > +			 (match_test "which_alternative != 1"))
> > > +		   (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
> > > +		  (const_string "8")]
> > > +	      (const_string "4")))])
> > 
> > Please use set_attr_alternative for these.
> 
> Are you sure about that?  The expression blows out to something like
> (completely untested):

But you don't need "cond", something with if_then_else is simpler.  And
(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))
could be put into a macro or inline or even a predicate.

Those apply to both with set_attr_alternative and without, of course;
please use what is easiest to read and understand.

(Just committing without changes is fine as well of course).

Thanks,


Segher
diff mbox series

Patch

diff --git a/gcc/config.in b/gcc/config.in
index 67a1e6cfc4c..86ff5e8636b 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -577,6 +577,12 @@ 
 #endif
 
 
+/* Define if your assembler supports R_PPC*_PLTSEQ relocations. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_AS_PLTSEQ
+#endif
+
+
 /* Define if your assembler supports .ref */
 #ifndef USED_FOR_TARGET
 #undef HAVE_AS_REF
diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index 7e45d2f0371..1af01935b5e 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -1055,6 +1055,24 @@  (define_predicate "call_operand"
 		  || REGNO (op) >= FIRST_PSEUDO_REGISTER")
      (match_code "symbol_ref")))
 
+;; Return 1 if the operand, used inside a MEM, is a valid first argument
+;; to an indirect CALL.  This is LR, CTR, or a PLTSEQ unspec using CTR.
+(define_predicate "indirect_call_operand"
+  (match_code "reg,unspec")
+{
+  if (REG_P (op))
+    return (REGNO (op) == LR_REGNO
+	    || REGNO (op) == CTR_REGNO);
+  if (GET_CODE (op) == UNSPEC)
+    {
+      if (XINT (op, 1) != UNSPEC_PLTSEQ)
+	return false;
+      op = XVECEXP (op, 0, 0);
+      return REG_P (op) && REGNO (op) == CTR_REGNO;
+    }
+  return false;
+})
+
 ;; Return 1 if the operand is a SYMBOL_REF for a function known to be in
 ;; this file.
 (define_predicate "current_file_function_operand"
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index 3fd89dc20db..35209d4525d 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -115,6 +115,7 @@  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 const char *rs6000_pltseq_template (rtx *, int);
 extern enum rtx_code rs6000_reverse_condition (machine_mode,
 					       enum rtx_code);
 extern rtx rs6000_emit_eqne (machine_mode, rtx, rtx, rtx);
@@ -204,6 +205,7 @@  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_sibcall_sysv (rtx, rtx, rtx, rtx);
 extern void rs6000_aix_asm_output_dwarf_table_ref (char *);
 extern void get_ppc476_thunk_name (char name[32]);
 extern bool rs6000_overloaded_builtin_p (enum rs6000_builtins);
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 5fd6e17bdda..c4008a87ed7 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -10706,7 +10706,7 @@  void
 init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
 		      rtx libname ATTRIBUTE_UNUSED, int incoming,
 		      int libcall, int n_named_args,
-		      tree fndecl ATTRIBUTE_UNUSED,
+		      tree fndecl,
 		      machine_mode return_mode ATTRIBUTE_UNUSED)
 {
   static CUMULATIVE_ARGS zero_cumulative;
@@ -10727,10 +10727,20 @@  init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
     cum->nargs_prototype = n_named_args;
 
   /* Check for a longcall attribute.  */
-  if ((!fntype && rs6000_default_long_calls)
-      || (fntype
-	  && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
-	  && !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype))))
+  if (((!fntype && rs6000_default_long_calls)
+       || (fntype
+	   && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))
+	   && !lookup_attribute ("shortcall", TYPE_ATTRIBUTES (fntype))))
+      || (DEFAULT_ABI != ABI_DARWIN
+	  && !(fndecl
+	       && !DECL_EXTERNAL (fndecl)
+	       && !DECL_WEAK (fndecl)
+	       && (*targetm.binds_local_p) (fndecl))
+	  && (flag_plt
+	      ? (fntype
+		 && lookup_attribute ("noplt", TYPE_ATTRIBUTES (fntype)))
+	      : !(fntype
+		  && lookup_attribute ("plt", TYPE_ATTRIBUTES (fntype))))))
     cum->call_cookie |= CALL_LONG;
 
   if (TARGET_DEBUG_ARG)
@@ -20955,6 +20965,8 @@  print_operand (FILE *file, rtx x, int code)
 
     case 'T':
       /* Print the symbolic name of a branch target register.  */
+      if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+	x = XVECEXP (x, 0, 0);
       if (GET_CODE (x) != REG || (REGNO (x) != LR_REGNO
 				  && REGNO (x) != CTR_REGNO))
 	output_operand_lossage ("invalid %%T value");
@@ -21098,6 +21110,8 @@  print_operand (FILE *file, rtx x, int code)
       return;
 
     case 'z':
+      if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+	x = XVECEXP (x, 0, 1);
       /* X is a SYMBOL_REF.  Write out the name preceded by a
 	 period and without any trailing data in brackets.  Used for function
 	 names.  If we are configured for System V (or the embedded ABI) on
@@ -21213,6 +21227,8 @@  print_operand (FILE *file, rtx x, int code)
 	output_addr_const (file, CONST_CAST_RTX (tocrel_base_oac));
       else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD)
 	output_addr_const (file, XVECEXP (x, 0, 0));
+      else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PLTSEQ)
+	output_addr_const (file, XVECEXP (x, 0, 1));
       else
 	output_addr_const (file, x);
       return;
@@ -21462,9 +21478,15 @@  rs6000_indirect_call_template_1 (rtx *operands, unsigned int funop,
       might produce 10 digits.  */
   gcc_assert (funop <= MAX_RECOG_OPERANDS);
 
-  static char str[144];
+  static char str[144];  /* 1 spare */
+  char *s = str;
   const char *ptrload = TARGET_64BIT ? "d" : "wz";
 
+  if (DEFAULT_ABI == ABI_AIX)
+    s += sprintf (s,
+		  "l%s 2,%%%u\n\t",
+		  ptrload, funop + 2);
+
   /* We don't need the extra code to stop indirect call speculation if
      calling via LR.  */
   bool speculate = (TARGET_MACHO
@@ -21472,32 +21494,61 @@  rs6000_indirect_call_template_1 (rtx *operands, unsigned int funop,
 		    || (REG_P (operands[funop])
 			&& REGNO (operands[funop]) == LR_REGNO));
 
+  if (!TARGET_MACHO && HAVE_AS_PLTSEQ && GET_CODE (operands[funop]) == UNSPEC)
+    {
+      const char *rel64 = TARGET_64BIT ? "64" : "";
+      char tls[29];
+      tls[0] = 0;
+      if (GET_CODE (operands[funop + 1]) == UNSPEC)
+	{
+	  if (XINT (operands[funop + 1], 1) == UNSPEC_TLSGD)
+	    sprintf (tls, ".reloc .,R_PPC%s_TLSGD,%%%u\n\t",
+		     rel64, funop + 1);
+	  else if (XINT (operands[funop + 1], 1) == UNSPEC_TLSLD)
+	    sprintf (tls, ".reloc .,R_PPC%s_TLSLD,%%&\n\t",
+		     rel64);
+	  else
+	    gcc_unreachable ();
+	}
+
+      const char *addend = (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT
+			    && flag_pic == 2 ? "+32768" : "");
+      if (!speculate)
+	{
+	  s += sprintf (s,
+			"%s.reloc .,R_PPC%s_PLTSEQ,%%z%u%s\n\t",
+			tls, rel64, funop, addend);
+	  s += sprintf (s, "crset 2\n\t");
+	}
+      s += sprintf (s,
+		    "%s.reloc .,R_PPC%s_PLTCALL,%%z%u%s\n\t",
+		    tls, rel64, funop, addend);
+    }
+  else if (!speculate)
+    s += sprintf (s, "crset 2\n\t");
+
   if (DEFAULT_ABI == ABI_AIX)
     {
       if (speculate)
-	sprintf (str,
-		 "l%s 2,%%%u\n\t"
+	sprintf (s,
 		 "b%%T%ul\n\t"
 		 "l%s 2,%%%u(1)",
-		 ptrload, funop + 2, funop, ptrload, funop + 3);
+		 funop, ptrload, funop + 3);
       else
-	sprintf (str,
-		 "crset 2\n\t"
-		 "l%s 2,%%%u\n\t"
+	sprintf (s,
 		 "beq%%T%ul-\n\t"
 		 "l%s 2,%%%u(1)",
-		 ptrload, funop + 2, funop, ptrload, funop + 3);
+		 funop, ptrload, funop + 3);
     }
   else if (DEFAULT_ABI == ABI_ELFv2)
     {
       if (speculate)
-	sprintf (str,
+	sprintf (s,
 		 "b%%T%ul\n\t"
 		 "l%s 2,%%%u(1)",
 		 funop, ptrload, funop + 2);
       else
-	sprintf (str,
-		 "crset 2\n\t"
+	sprintf (s,
 		 "beq%%T%ul-\n\t"
 		 "l%s 2,%%%u(1)",
 		 funop, ptrload, funop + 2);
@@ -21505,12 +21556,11 @@  rs6000_indirect_call_template_1 (rtx *operands, unsigned int funop,
   else
     {
       if (speculate)
-	sprintf (str,
+	sprintf (s,
 		 "b%%T%u%s",
 		 funop, sibcall ? "" : "l");
       else
-	sprintf (str,
-		 "crset 2\n\t"
+	sprintf (s,
 		 "beq%%T%u%s-%s",
 		 funop, sibcall ? "" : "l", sibcall ? "\n\tb $" : "");
     }
@@ -21529,6 +21579,72 @@  rs6000_indirect_sibcall_template (rtx *operands, unsigned int funop)
   return rs6000_indirect_call_template_1 (operands, funop, true);
 }
 
+#if HAVE_AS_PLTSEQ
+/* Output indirect call insns.
+   WHICH is 0 for tocsave, 1 for plt16_ha, 2 for plt16_lo, 3 for mtctr.  */
+const char *
+rs6000_pltseq_template (rtx *operands, int which)
+{
+  const char *rel64 = TARGET_64BIT ? "64" : "";
+  char tls[28];
+  tls[0] = 0;
+  if (GET_CODE (operands[3]) == UNSPEC)
+    {
+      if (XINT (operands[3], 1) == UNSPEC_TLSGD)
+	sprintf (tls, ".reloc .,R_PPC%s_TLSGD,%%3\n\t",
+		 rel64);
+      else if (XINT (operands[3], 1) == UNSPEC_TLSLD)
+	sprintf (tls, ".reloc .,R_PPC%s_TLSLD,%%&\n\t",
+		 rel64);
+      else
+	gcc_unreachable ();
+    }
+
+  gcc_assert (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4);
+  static char str[96];  /* 15 spare */
+  const char *off = WORDS_BIG_ENDIAN ? "+2" : "";
+  const char *addend = (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT
+			&& flag_pic == 2 ? "+32768" : "");
+  switch (which)
+    {
+    case 0:
+      sprintf (str,
+	       "%s.reloc .,R_PPC%s_PLTSEQ,%%z2\n\t"
+	       "st%s",
+	       tls, rel64, TARGET_64BIT ? "d 2,24(1)" : "w 2,12(1)");
+      break;
+    case 1:
+      if (DEFAULT_ABI == ABI_V4 && !flag_pic)
+	sprintf (str,
+		 "%s.reloc .%s,R_PPC%s_PLT16_HA,%%z2\n\t"
+		 "lis %%0,0",
+		 tls, off, rel64);
+      else
+	sprintf (str,
+		 "%s.reloc .%s,R_PPC%s_PLT16_HA,%%z2%s\n\t"
+		 "addis %%0,%%1,0",
+		 tls, off, rel64, addend);
+      break;
+    case 2:
+      sprintf (str,
+	       "%s.reloc .%s,R_PPC%s_PLT16_LO%s,%%z2%s\n\t"
+	       "l%s %%0,0(%%1)",
+	       tls, off, rel64, TARGET_64BIT ? "_DS" : "", addend,
+	       TARGET_64BIT ? "d" : "wz");
+      break;
+    case 3:
+      sprintf (str,
+	       "%s.reloc .,R_PPC%s_PLTSEQ,%%z2%s\n\t"
+	       "mtctr %%1",
+	       tls, rel64, addend);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  return str;
+}
+#endif
+
 #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 /* Emit an assembler directive to set symbol visibility for DECL to
    VISIBILITY_TYPE.  */
@@ -32540,11 +32656,8 @@  rs6000_set_default_type_attributes (tree type)
    longcall attribute.  */
 
 static rtx
-rs6000_longcall_ref (rtx call_ref)
+rs6000_longcall_ref (rtx call_ref, rtx arg)
 {
-  if (GET_CODE (call_ref) != SYMBOL_REF)
-    return call_ref;
-
   /* System V adds '.' to the internal name, so skip them.  */
   const char *call_name = XSTR (call_ref, 0);
   if (*call_name == '.')
@@ -32556,6 +32669,36 @@  rs6000_longcall_ref (rtx call_ref)
       call_ref = gen_rtx_SYMBOL_REF (VOIDmode, IDENTIFIER_POINTER (node));
     }
 
+  if (HAVE_AS_PLTSEQ
+      && TARGET_TLS_MARKERS
+      && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4))
+    {
+      rtx base = const0_rtx;
+      int regno;
+      if (DEFAULT_ABI == ABI_ELFv2)
+	{
+	  base = gen_rtx_REG (Pmode, TOC_REGISTER);
+	  regno = 12;
+	}
+      else
+	{
+	  if (flag_pic)
+	    base = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM);
+	  regno = 11;
+	}
+      /* Reg must match that used by linker PLT stubs.  For ELFv2, r12
+	 may be used by a function global entry point.  For SysV4, r11
+	 is used by __glink_PLTresolve lazy resolver entry.  */
+      rtx reg = gen_rtx_REG (Pmode, regno);
+      rtx hi = gen_rtx_UNSPEC (Pmode, gen_rtvec (3, base, call_ref, arg),
+			       UNSPEC_PLT16_HA);
+      rtx lo = gen_rtx_UNSPEC (Pmode, gen_rtvec (3, reg, call_ref, arg),
+			       UNSPEC_PLT16_LO);
+      emit_insn (gen_rtx_SET (reg, hi));
+      emit_insn (gen_rtx_SET (reg, lo));
+      return reg;
+    }
+
   return force_reg (Pmode, call_ref);
 }
 
@@ -37526,8 +37669,7 @@  chain_already_loaded (rtx_insn *last)
 void
 rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 {
-  const bool direct_call_p
-    = GET_CODE (func_desc) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (func_desc);
+  rtx func = func_desc;
   rtx toc_reg = gen_rtx_REG (Pmode, TOC_REGNUM);
   rtx toc_load = NULL_RTX;
   rtx toc_restore = NULL_RTX;
@@ -37541,12 +37683,13 @@  rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
     tlsarg = global_tlsarg;
 
   /* Handle longcall attributes.  */
-  if (INTVAL (cookie) & CALL_LONG)
-    func_desc = rs6000_longcall_ref (func_desc);
+  if ((INTVAL (cookie) & CALL_LONG) != 0
+      && GET_CODE (func_desc) == SYMBOL_REF)
+    func = rs6000_longcall_ref (func_desc, tlsarg);
 
   /* Handle indirect calls.  */
-  if (GET_CODE (func_desc) != SYMBOL_REF
-      || (DEFAULT_ABI == ABI_AIX && !SYMBOL_REF_FUNCTION_P (func_desc)))
+  if (GET_CODE (func) != SYMBOL_REF
+      || (DEFAULT_ABI == ABI_AIX && !SYMBOL_REF_FUNCTION_P (func)))
     {
       /* Save the TOC into its reserved slot before the call,
 	 and prepare to restore it after the call.  */
@@ -37567,7 +37710,17 @@  rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 					     gen_rtx_PLUS (Pmode, stack_ptr,
 							   stack_toc_offset));
 	  MEM_VOLATILE_P (stack_toc_mem) = 1;
-	  emit_move_insn (stack_toc_mem, toc_reg);
+	  if (HAVE_AS_PLTSEQ
+	      && TARGET_TLS_MARKERS
+	      && DEFAULT_ABI == ABI_ELFv2
+	      && GET_CODE (func_desc) == SYMBOL_REF)
+	    {
+	      rtvec v = gen_rtvec (3, toc_reg, func_desc, tlsarg);
+	      rtx mark_toc_reg = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+	      emit_insn (gen_rtx_SET (stack_toc_mem, mark_toc_reg));
+	    }
+	  else
+	    emit_move_insn (stack_toc_mem, toc_reg);
 	}
 
       if (DEFAULT_ABI == ABI_ELFv2)
@@ -37575,9 +37728,25 @@  rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, 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);
-	  if (!rtx_equal_p (func_addr, func_desc))
-	    emit_move_insn (func_addr, func_desc);
+	  if (!rtx_equal_p (func_addr, func))
+	    emit_move_insn (func_addr, func);
 	  abi_reg = func_addr;
+	  /* Indirect calls via CTR are strongly preferred over indirect
+	     calls via LR, so move the address there.  Needed to mark
+	     this insn for linker plt sequence editing too.  */
+	  func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+	  if (HAVE_AS_PLTSEQ
+	      && TARGET_TLS_MARKERS
+	      && GET_CODE (func_desc) == SYMBOL_REF)
+	    {
+	      rtvec v = gen_rtvec (3, abi_reg, func_desc, tlsarg);
+	      rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+	      emit_insn (gen_rtx_SET (func_addr, mark_func));
+	      v = gen_rtvec (2, func_addr, func_desc);
+	      func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+	    }
+	  else
+	    emit_move_insn (func_addr, abi_reg);
 	}
       else
 	{
@@ -37589,9 +37758,15 @@  rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 	     not have any executable code.  */
 
 	  /* Load up address of the actual function.  */
-	  func_desc = force_reg (Pmode, func_desc);
+	  func = force_reg (Pmode, func);
 	  func_addr = gen_reg_rtx (Pmode);
-	  emit_move_insn (func_addr, gen_rtx_MEM (Pmode, func_desc));
+	  emit_move_insn (func_addr, gen_rtx_MEM (Pmode, func));
+
+	  /* Indirect calls via CTR are strongly preferred over indirect
+	     calls via LR, so move the address there.  */
+	  rtx ctr_reg = gen_rtx_REG (Pmode, CTR_REGNO);
+	  emit_move_insn (ctr_reg, func_addr);
+	  func_addr = ctr_reg;
 
 	  /* Prepare to load the TOC of the called function.  Note that the
 	     TOC load must happen immediately before the actual call so
@@ -37599,7 +37774,7 @@  rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 	     comment in frob_update_context.  */
 	  rtx func_toc_offset = GEN_INT (GET_MODE_SIZE (Pmode));
 	  rtx func_toc_mem = gen_rtx_MEM (Pmode,
-					  gen_rtx_PLUS (Pmode, func_desc,
+					  gen_rtx_PLUS (Pmode, func,
 							func_toc_offset));
 	  toc_load = gen_rtx_USE (VOIDmode, func_toc_mem);
 
@@ -37607,14 +37782,15 @@  rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 	     originally direct, the 3rd word has not been written since no
 	     trampoline has been built, so we ought not to load it, lest we
 	     override a static chain value.  */
-	  if (!direct_call_p
+	  if (!(GET_CODE (func_desc) == SYMBOL_REF
+		&& SYMBOL_REF_FUNCTION_P (func_desc))
 	      && TARGET_POINTERS_TO_NESTED_FUNCTIONS
 	      && !chain_already_loaded (get_current_sequence ()->next->last))
 	    {
 	      rtx sc_reg = gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM);
 	      rtx func_sc_offset = GEN_INT (2 * GET_MODE_SIZE (Pmode));
 	      rtx func_sc_mem = gen_rtx_MEM (Pmode,
-					     gen_rtx_PLUS (Pmode, func_desc,
+					     gen_rtx_PLUS (Pmode, func,
 							   func_sc_offset));
 	      emit_move_insn (sc_reg, func_sc_mem);
 	      abi_reg = sc_reg;
@@ -37627,7 +37803,7 @@  rs6000_call_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 	 assume the TOC register is set; for non-local calls, the
 	 PLT stub needs the TOC register.  */
       abi_reg = toc_reg;
-      func_addr = func_desc;
+      func_addr = func;
     }
 
   /* Create the call.  */
@@ -37682,22 +37858,51 @@  rs6000_sibcall_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 /* Expand code to perform a call under the SYSV4 ABI.  */
 
 void
-rs6000_call_sysv (rtx value, rtx func, rtx tlsarg, rtx cookie)
+rs6000_call_sysv (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
 {
+  rtx func = func_desc;
   rtx func_addr;
   rtx call[3];
   rtx insn;
+  rtx abi_reg = NULL_RTX;
 
   if (global_tlsarg)
     tlsarg = global_tlsarg;
 
   /* Handle longcall attributes.  */
-  if (INTVAL (cookie) & CALL_LONG)
-    func = rs6000_longcall_ref (func);
+  if ((INTVAL (cookie) & CALL_LONG) != 0
+      && GET_CODE (func_desc) == SYMBOL_REF)
+    {
+      func = rs6000_longcall_ref (func_desc, tlsarg);
+      /* If the longcall was implemented using PLT16 relocs, then r11
+	 needs to be valid at the call for lazy linking.  */
+      if (HAVE_AS_PLTSEQ
+	  && TARGET_TLS_MARKERS)
+	abi_reg = func;
+    }
 
   /* Handle indirect calls.  */
   if (GET_CODE (func) != SYMBOL_REF)
-    func_addr = force_reg (Pmode, func);
+    {
+      func = force_reg (Pmode, func);
+
+      /* Indirect calls via CTR are strongly preferred over indirect
+	 calls via LR, so move the address there.  Needed to mark
+	 this insn for linker plt sequence editing too.  */
+      func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+      if (HAVE_AS_PLTSEQ
+	  && TARGET_TLS_MARKERS
+	  && GET_CODE (func_desc) == SYMBOL_REF)
+	{
+	  rtvec v = gen_rtvec (3, func, func_desc, tlsarg);
+	  rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+	  emit_insn (gen_rtx_SET (func_addr, mark_func));
+	  v = gen_rtvec (2, func_addr, func_desc);
+	  func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+	}
+      else
+	emit_move_insn (func_addr, func);
+    }
   else
     func_addr = func;
 
@@ -37713,6 +37918,73 @@  rs6000_call_sysv (rtx value, rtx func, rtx tlsarg, rtx cookie)
 
   insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
   insn = emit_call_insn (insn);
+  if (abi_reg)
+    use_reg (&CALL_INSN_FUNCTION_USAGE (insn), abi_reg);
+}
+
+/* Expand code to perform a sibling call under the SysV4 ABI.  */
+
+void
+rs6000_sibcall_sysv (rtx value, rtx func_desc, rtx tlsarg, rtx cookie)
+{
+  rtx func = func_desc;
+  rtx func_addr;
+  rtx call[3];
+  rtx insn;
+  rtx abi_reg = NULL_RTX;
+
+  if (global_tlsarg)
+    tlsarg = global_tlsarg;
+
+  /* Handle longcall attributes.  */
+  if ((INTVAL (cookie) & CALL_LONG) != 0
+      && GET_CODE (func_desc) == SYMBOL_REF)
+    {
+      func = rs6000_longcall_ref (func_desc, tlsarg);
+      /* If the longcall was implemented using PLT16 relocs, then r11
+	 needs to be valid at the call for lazy linking.  */
+      if (HAVE_AS_PLTSEQ
+	  && TARGET_TLS_MARKERS)
+	abi_reg = func;
+    }
+
+  /* Handle indirect calls.  */
+  if (GET_CODE (func) != SYMBOL_REF)
+    {
+      func = force_reg (Pmode, func);
+
+      /* Indirect sibcalls must go via CTR.  Needed to mark
+	 this insn for linker plt sequence editing too.  */
+      func_addr = gen_rtx_REG (Pmode, CTR_REGNO);
+      if (HAVE_AS_PLTSEQ
+	  && TARGET_TLS_MARKERS
+	  && GET_CODE (func_desc) == SYMBOL_REF)
+	{
+	  rtvec v = gen_rtvec (3, func, func_desc, tlsarg);
+	  rtx mark_func = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+	  emit_insn (gen_rtx_SET (func_addr, mark_func));
+	  v = gen_rtvec (2, func_addr, func_desc);
+	  func_addr = gen_rtx_UNSPEC (Pmode, v, UNSPEC_PLTSEQ);
+	}
+      else
+	emit_move_insn (func_addr, func);
+    }
+  else
+    func_addr = func;
+
+  /* Create the call.  */
+  call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), tlsarg);
+  if (value != NULL_RTX)
+    call[0] = gen_rtx_SET (value, call[0]);
+
+  unsigned int mask = CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS;
+  call[1] = gen_rtx_USE (VOIDmode, GEN_INT (INTVAL (cookie) & mask));
+  call[2] = simple_return_rtx;
+
+  insn = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (3, call));
+  insn = emit_call_insn (insn);
+  if (abi_reg)
+    use_reg (&CALL_INSN_FUNCTION_USAGE (insn), abi_reg);
 }
 
 /* Return whether we need to always update the saved TOC pointer when we update
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index f59c0b6b685..c7934c601ed 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -224,6 +224,10 @@  extern const char *host_detect_local_cpu (int argc, const char **argv);
 #define HAVE_AS_TLS 0
 #endif
 
+#ifndef HAVE_AS_PLTSEQ
+#define HAVE_AS_PLTSEQ 0
+#endif
+
 #ifndef TARGET_LINK_STACK
 #define TARGET_LINK_STACK 0
 #endif
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 336d42335cb..02f194c7d33 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -147,6 +147,9 @@  (define_c_enum "unspec"
    UNSPEC_SIGNBIT
    UNSPEC_SF_FROM_SI
    UNSPEC_SI_FROM_SF
+   UNSPEC_PLTSEQ
+   UNSPEC_PLT16_HA
+   UNSPEC_PLT16_LO
   ])
 
 ;;
@@ -10231,6 +10234,55 @@  (define_insn "elf_low"
 		   (match_operand 2 "" "")))]
    "TARGET_ELF && !TARGET_64BIT && !flag_pic"
    "la %0,%2@l(%1)")
+
+(define_insn "*pltseq_tocsave_<mode>"
+  [(set (match_operand:P 0 "memory_operand" "=m")
+	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
+		   (match_operand:P 2 "symbol_ref_operand" "s")
+		   (match_operand:P 3 "" "")]
+		  UNSPEC_PLTSEQ))]
+  "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+   && DEFAULT_ABI == ABI_ELFv2"
+{
+  return rs6000_pltseq_template (operands, 0);
+})
+
+(define_insn "*pltseq_plt16_ha_<mode>"
+  [(set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(unspec:P [(match_operand:P 1 "" "")
+		   (match_operand:P 2 "symbol_ref_operand" "s")
+		   (match_operand:P 3 "" "")]
+		  UNSPEC_PLT16_HA))]
+  "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+   && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4)"
+{
+  return rs6000_pltseq_template (operands, 1);
+})
+
+(define_insn "*pltseq_plt16_lo_<mode>"
+  [(set (match_operand:P 0 "gpc_reg_operand" "=r")
+	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "b")
+		   (match_operand:P 2 "symbol_ref_operand" "s")
+		   (match_operand:P 3 "" "")]
+		  UNSPEC_PLT16_LO))]
+  "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+   && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4)"
+{
+  return rs6000_pltseq_template (operands, 2);
+}
+  [(set_attr "type" "load")])
+
+(define_insn "*pltseq_mtctr_<mode>"
+  [(set (match_operand:P 0 "register_operand" "=c")
+	(unspec:P [(match_operand:P 1 "gpc_reg_operand" "r")
+		   (match_operand:P 2 "symbol_ref_operand" "s")
+		   (match_operand:P 3 "" "")]
+		  UNSPEC_PLTSEQ))]
+  "HAVE_AS_PLTSEQ && TARGET_TLS_MARKERS
+   && (DEFAULT_ABI == ABI_ELFv2 || DEFAULT_ABI == ABI_V4)"
+{
+  return rs6000_pltseq_template (operands, 3);
+})
 
 ;; Call and call_value insns
 (define_expand "call"
@@ -10387,9 +10439,9 @@  (define_insn "*call_value_local64"
 ;; which indicates how to set cr1
 
 (define_insn "*call_indirect_nonlocal_sysv<mode>"
-  [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l,c,*l"))
+  [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
 	 (match_operand 1))
-   (use (match_operand:SI 2 "immediate_operand" "O,O,n,n"))
+   (use (match_operand:SI 2 "immediate_operand" "n,n,n"))
    (clobber (reg:SI LR_REGNO))]
   "DEFAULT_ABI == ABI_V4
    || DEFAULT_ABI == ABI_DARWIN"
@@ -10402,18 +10454,17 @@  (define_insn "*call_indirect_nonlocal_sysv<mode>"
 
   return rs6000_indirect_call_template (operands, 0);
 }
-  [(set_attr "type" "jmpreg,jmpreg,jmpreg,jmpreg")
-   (set_attr_alternative "length"
-     [(if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "8")
-		    (const_string "4"))
-      (const_string "4")
-      (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "12")
-		    (const_string "8"))
-      (const_string "8")])])
+  [(set_attr "type" "jmpreg")
+   (set (attr "length")
+	(cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		    (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "12")
+	       (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		    (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "8")]
+	      (const_string "4")))])
 
 (define_insn_and_split "*call_nonlocal_sysv<mode>"
   [(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s,s"))
@@ -10473,9 +10524,9 @@  (define_insn "*call_nonlocal_sysv_secure<mode>"
 
 (define_insn "*call_value_indirect_nonlocal_sysv<mode>"
   [(set (match_operand 0 "" "")
-	(call (mem:SI (match_operand:P 1 "register_operand" "c,*l,c,*l"))
+	(call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
 	      (match_operand 2)))
-   (use (match_operand:SI 3 "immediate_operand" "O,O,n,n"))
+   (use (match_operand:SI 3 "immediate_operand" "n,n,n"))
    (clobber (reg:SI LR_REGNO))]
   "DEFAULT_ABI == ABI_V4
    || DEFAULT_ABI == ABI_DARWIN"
@@ -10488,18 +10539,17 @@  (define_insn "*call_value_indirect_nonlocal_sysv<mode>"
 
   return rs6000_indirect_call_template (operands, 1);
 }
-  [(set_attr "type" "jmpreg,jmpreg,jmpreg,jmpreg")
-   (set_attr_alternative "length"
-     [(if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "8")
-		    (const_string "4"))
-      (const_string "4")
-      (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "12")
-		    (const_string "8"))
-      (const_string "8")])])
+  [(set_attr "type" "jmpreg")
+   (set (attr "length")
+	(cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		    (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "12")
+	       (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		   (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "8")]
+	      (const_string "4")))])
 
 (define_insn_and_split "*call_value_nonlocal_sysv<mode>"
   [(set (match_operand 0 "" "")
@@ -10612,10 +10662,10 @@  (define_insn "*call_value_nonlocal_aix<mode>"
 ;; Operand3 is the offset of the stack location holding the current TOC pointer
 
 (define_insn "*call_indirect_aix<mode>"
-  [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l"))
+  [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
 	 (match_operand 1))
-   (use (match_operand:P 2 "memory_operand" "<ptrm>,<ptrm>"))
-   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+   (use (match_operand:P 2 "memory_operand" "<ptrm>,<ptrm>,<ptrm>"))
+   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_AIX"
 {
@@ -10630,10 +10680,10 @@  (define_insn "*call_indirect_aix<mode>"
 
 (define_insn "*call_value_indirect_aix<mode>"
   [(set (match_operand 0 "" "")
-	(call (mem:SI (match_operand:P 1 "register_operand" "c,*l"))
+	(call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
 	      (match_operand 2)))
-   (use (match_operand:P 3 "memory_operand" "<ptrm>,<ptrm>"))
-   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 4 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+   (use (match_operand:P 3 "memory_operand" "<ptrm>,<ptrm>,<ptrm>"))
+   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 4 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_AIX"
 {
@@ -10651,9 +10701,9 @@  (define_insn "*call_value_indirect_aix<mode>"
 ;; Operand2 is the offset of the stack location holding the current TOC pointer
 
 (define_insn "*call_indirect_elfv2<mode>"
-  [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l"))
+  [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
 	 (match_operand 1))
-   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 2 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 2 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_ELFv2"
 {
@@ -10668,9 +10718,9 @@  (define_insn "*call_indirect_elfv2<mode>"
 
 (define_insn "*call_value_indirect_elfv2<mode>"
   [(set (match_operand 0 "" "")
-	(call (mem:SI (match_operand:P 1 "register_operand" "c,*l"))
+	(call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
 	      (match_operand 2)))
-   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n")] UNSPEC_TOCSLOT))
+   (set (reg:P TOC_REGNUM) (unspec:P [(match_operand:P 3 "const_int_operand" "n,n,n")] UNSPEC_TOCSLOT))
    (clobber (reg:P LR_REGNO))]
   "DEFAULT_ABI == ABI_ELFv2"
 {
@@ -10733,6 +10783,12 @@  (define_expand "sibcall"
       rs6000_sibcall_aix (NULL_RTX, operands[0], operands[1], operands[2]);
       DONE;
     }
+
+  if (DEFAULT_ABI == ABI_V4)
+    {
+      rs6000_sibcall_sysv (NULL_RTX, operands[0], operands[1], operands[2]);
+      DONE;
+    }
 })
 
 (define_expand "sibcall_value"
@@ -10758,6 +10814,12 @@  (define_expand "sibcall_value"
       rs6000_sibcall_aix (operands[0], operands[1], operands[2], operands[3]);
       DONE;
     }
+
+  if (DEFAULT_ABI == ABI_V4)
+    {
+      rs6000_sibcall_sysv (operands[0], operands[1], operands[2], operands[3]);
+      DONE;
+    }
 })
 
 (define_insn "*sibcall_local32"
@@ -10834,10 +10896,38 @@  (define_insn "*sibcall_value_local64"
   [(set_attr "type" "branch")
    (set_attr "length" "4,8")])
 
+(define_insn "*sibcall_indirect_nonlocal_sysv<mode>"
+  [(call (mem:SI (match_operand:P 0 "indirect_call_operand" "c,*l,X"))
+	 (match_operand 1))
+   (use (match_operand:SI 2 "immediate_operand" "n,n,n"))
+   (simple_return)]
+  "DEFAULT_ABI == ABI_V4
+   || DEFAULT_ABI == ABI_DARWIN"
+{
+  if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS)
+    output_asm_insn ("crxor 6,6,6", operands);
+
+  else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS)
+    output_asm_insn ("creqv 6,6,6", operands);
+
+  return rs6000_indirect_sibcall_template (operands, 0);
+}
+  [(set_attr "type" "jmpreg")
+   (set (attr "length")
+	(cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		    (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "12")
+	       (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		   (match_test "(INTVAL (operands[2]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "8")]
+	      (const_string "4")))])
+
 (define_insn "*sibcall_nonlocal_sysv<mode>"
-  [(call (mem:SI (match_operand:P 0 "call_operand" "s,s,c,c"))
-	 (match_operand 1 "" ""))
-   (use (match_operand 2 "immediate_operand" "O,n,O,n"))
+  [(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s,s"))
+	 (match_operand 1))
+   (use (match_operand 2 "immediate_operand" "O,n"))
    (simple_return)]
   "(DEFAULT_ABI == ABI_DARWIN
     || DEFAULT_ABI == ABI_V4)
@@ -10849,29 +10939,45 @@  (define_insn "*sibcall_nonlocal_sysv<mode>"
   else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS)
     output_asm_insn ("creqv 6,6,6", operands);
 
-  if (which_alternative >= 2)
-    return rs6000_indirect_sibcall_template (operands, 0);
-  else
-    return rs6000_sibcall_template (operands, 0);
+  return rs6000_sibcall_template (operands, 0);
 }
   [(set_attr "type" "branch")
-   (set_attr_alternative "length"
-     [(const_string "4")
-      (const_string "8")
-      (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "12")
-		    (const_string "4"))
-      (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "16")
-		    (const_string "8"))])])
+   (set_attr "length" "4,8")])
+
+(define_insn "*sibcall_value_indirect_nonlocal_sysv<mode>"
+  [(set (match_operand 0 "" "")
+	(call (mem:SI (match_operand:P 1 "indirect_call_operand" "c,*l,X"))
+	      (match_operand 2)))
+   (use (match_operand:SI 3 "immediate_operand" "n,n,n"))
+   (simple_return)]
+  "DEFAULT_ABI == ABI_V4
+   || DEFAULT_ABI == ABI_DARWIN"
+{
+  if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS)
+    output_asm_insn ("crxor 6,6,6", operands);
+
+  else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS)
+    output_asm_insn ("creqv 6,6,6", operands);
+
+  return rs6000_indirect_sibcall_template (operands, 1);
+}
+  [(set_attr "type" "jmpreg")
+   (set (attr "length")
+	(cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		    (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "12")
+	       (ior (and (match_test "!rs6000_speculate_indirect_jumps")
+			 (match_test "which_alternative != 1"))
+		   (match_test "(INTVAL (operands[3]) & (CALL_V4_SET_FP_ARGS | CALL_V4_CLEAR_FP_ARGS))"))
+		  (const_string "8")]
+	      (const_string "4")))])
 
 (define_insn "*sibcall_value_nonlocal_sysv<mode>"
   [(set (match_operand 0 "" "")
-	(call (mem:SI (match_operand:P 1 "call_operand" "s,s,c,c"))
-	      (match_operand 2 "" "")))
-   (use (match_operand:SI 3 "immediate_operand" "O,n,O,n"))
+	(call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s,s"))
+	      (match_operand 2)))
+   (use (match_operand:SI 3 "immediate_operand" "O,n"))
    (simple_return)]
   "(DEFAULT_ABI == ABI_DARWIN
     || DEFAULT_ABI == ABI_V4)
@@ -10883,29 +10989,10 @@  (define_insn "*sibcall_value_nonlocal_sysv<mode>"
   else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS)
     output_asm_insn ("creqv 6,6,6", operands);
 
-  if (which_alternative >= 2)
-    {
-      if (rs6000_speculate_indirect_jumps)
-	return "b%T1";
-      else
-	/* Can use CR0 since it is volatile across sibcalls.  */
-	return "crset 2\;beq%T1-\;b $";
-    }
-  else
-    return rs6000_sibcall_template (operands, 1);
+  return rs6000_sibcall_template (operands, 1);
 }
   [(set_attr "type" "branch")
-   (set_attr_alternative "length"
-     [(const_string "4")
-      (const_string "8")
-      (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "12")
-		    (const_string "4"))
-      (if_then_else (eq (symbol_ref "rs6000_speculate_indirect_jumps")
-			(const_int 0))
-		    (const_string "16")
-		    (const_string "8"))])])
+   (set_attr "length" "4,8")])
 
 ;; AIX ABI sibling call patterns.
 
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 260d987dadd..c0043469aeb 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -4648,6 +4648,12 @@  LCF0:
       [AC_DEFINE(HAVE_AS_ENTRY_MARKERS, 1,
 	  [Define if your assembler supports the R_PPC64_ENTRY relocation.])])
 
+    gcc_GAS_CHECK_FEATURE([plt sequence marker support],
+      gcc_cv_as_powerpc_pltseq_markers, [2,31,0],-a32 --fatal-warnings,
+      [ .reloc .,R_PPC_PLTSEQ; nop],,
+      [AC_DEFINE(HAVE_AS_PLTSEQ, 1,
+	  [Define if your assembler supports R_PPC*_PLTSEQ relocations.])])
+
     case $target in
       *-*-aix*)
 	gcc_GAS_CHECK_FEATURE([AIX .ref support],