This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PowerPC prologue and epilogue 6


This patch adds out-of-line vector saves and restores.  To do this I
made some infrastructure changes to various functions like
rs6000_emit_savres_rtx that currently take boolean parameters (savep,
gpr, and lr).  Rather than add yet another boolean to specify vector
regs, I chose to lump them all together in a bitmask.  This made the
patch a little larger but overall is a better interface, I think.

I also revert a change I made in
http://gcc.gnu.org/ml/gcc-patches/2012-04/msg01014.html to always use
r11 as a frame reg whenever abiv4 emits out-of-line saves.  Code
quality in functions with small frames is better without that
particular change.  This however meant some changes are required later
when setting up pointer regs for gpr and fpr out-of-line saves.

What else is here?  Improved register selection when saving vrsave in
the prologue and when restoring cr in the epilogue, allowing better
scheduling.  A fix to rs6000_output_function_prologue to output the
correct .extern for ELF, then deciding we don't need such things
anyway.  And various other little code cleanups.  Bootstrapped and
regression tested powerpc-linux.

gcc/
	* config/rs6000/rs6000 (SAVE_INLINE_VRS, REST_INLINE_VRS,
	V_SAVE_INLINE, SAVRES_LR, SAVRES_SAVE, SAVRES_REG,
	SAVRES_GPR, SAVRES_FPR, SAVRES_VR): Define.
	(no_global_regs_above): Delete.
	(no_global_regs): New function.
	(rs6000_savres_strategy): Handle vector regs.  Use proper lr_save_p
	value for load multiple test.
	(savres_routine_syms): Increase size.
	(rs6000_savres_routine_name, rs6000_savres_routine_sym,
	ptr_regno_for_savres, rs6000_emit_savres_rtx): Pass in int selector
	rather than a number of boolean flags.  Update all callers.
	(rs6000_savres_routine_name): Generate vector save/restore names.
	(rs6000_savres_routine_sym): Handle vector regs.  Delete forward decl.
	(ptr_regno_for_savres, rs6000_emit_savres_rtx): Likewise.
	(rs6000_emit_prologue): Delete saving_FPRs_inline, saving_GPRs_inline
	and using_store_multiple.  Expand uses.  Don't always use r11 as
	frame reg when needed for out-of-line saves.  Set up initial offset
	for out-of-line vector saves when buying stack frame.  Handle pointer
	reg setup for out-of-line fp save.  Emit call to out-of-line vector
	save function.  Choose r11 or r12 for vrsave reg when available for
	better scheduling.
	(rs6000_output_function_prologue): Don't emit .extern for ELF.
	(rs6000_emit_epilogue): Choose a better frame reg when restoring
	from back-chain to suit out-of-line vector restore functions.  Emit
	call to out-of-line vector restore function.  Adjust register used
	for cr restore.  Tweak pointer register setup for gpr restore.
	* config/rs6000/rs6000.h (FIRST_SAVED_GP_REGNO): Take into account
	FIXED_R13.
	* config/rs6000/sysv4.h (FP_SAVE_INLINE, GP_SAVE_INLINE): Simplify.
	(V_SAVE_INLINE): Define.
	* config/rs6000/altivec.md (save_vregs_*, restore_vregs_*): New insns.
libgcc/
	* config/rs6000/crtsavevr.S: New file.
	* config/rs6000/crtrestvr.S: New file.
	* config/rs6000/t-savresfgpr: Build the above.
	* config/rs6000/t-netbsd: Likewise.

diff -urpN gcc-alan5/gcc/config/rs6000/rs6000.c gcc-alan6/gcc/config/rs6000/rs6000.c
--- gcc-alan5/gcc/config/rs6000/rs6000.c	2012-04-19 20:55:02.214727782 +0930
+++ gcc-alan6/gcc/config/rs6000/rs6000.c	2012-04-21 15:47:44.193462791 +0930
@@ -937,7 +937,6 @@ static bool legitimate_small_data_p (enu
 static bool legitimate_lo_sum_address_p (enum machine_mode, rtx, int);
 static struct machine_function * rs6000_init_machine_status (void);
 static bool rs6000_assemble_integer (rtx, unsigned int, int);
-static bool no_global_regs_above (int, bool);
 #if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
 static void rs6000_assemble_visibility (tree, int);
 #endif
@@ -950,7 +949,6 @@ static tree rs6000_handle_struct_attribu
 static void rs6000_eliminate_indexed_memrefs (rtx operands[2]);
 static const char *rs6000_mangle_type (const_tree);
 static void rs6000_set_default_type_attributes (tree);
-static rtx rs6000_savres_routine_sym (rs6000_stack_t *, bool, bool, bool);
 static bool rs6000_reg_live_or_pic_offset_p (int);
 static tree rs6000_builtin_vectorized_libmass (tree, tree, tree);
 static tree rs6000_builtin_vectorized_function (tree, tree, tree);
@@ -17405,6 +17403,21 @@ is_altivec_return_reg (rtx reg, void *xy
 }
 
 
+/* Look for user-defined global regs in the range FIRST to LAST-1.
+   We should not restore these, and so cannot use lmw or out-of-line
+   restore functions if there are any.  We also can't save them
+   (well, emit frame notes for them), because frame unwinding during
+   exception handling will restore saved registers.  */
+
+static bool
+global_regs_p (unsigned first, unsigned last)
+{
+  while (first < last)
+    if (global_regs[first++])
+      return true;
+  return false;
+}
+
 /* Determine the strategy for savings/restoring registers.  */
 
 enum {
@@ -17415,41 +17428,54 @@ enum {
   REST_INLINE_GPRS = 0x10,
   SAVE_NOINLINE_GPRS_SAVES_LR = 0x20,
   SAVE_NOINLINE_FPRS_SAVES_LR = 0x40,
-  REST_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x80
+  REST_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x80,
+  SAVE_INLINE_VRS = 0x100,
+  REST_INLINE_VRS = 0x200
 };
 
+#ifndef V_SAVE_INLINE
+#define V_SAVE_INLINE(FIRST_REG) 1
+#endif
+
 static int
 rs6000_savres_strategy (rs6000_stack_t *info,
 			bool using_static_chain_p)
 {
   int strategy = 0;
+  bool lr_save_p;
 
   if (TARGET_MULTIPLE
       && !TARGET_POWERPC64
       && !(TARGET_SPE_ABI && info->spe_64bit_regs_used)
       && info->first_gp_reg_save < 31
-      && no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true))
+      && !global_regs_p (info->first_gp_reg_save, 32))
     strategy |= SAVRES_MULTIPLE;
 
   if (crtl->calls_eh_return
       || cfun->machine->ra_need_lr)
     strategy |= (SAVE_INLINE_FPRS | REST_INLINE_FPRS
-		 | SAVE_INLINE_GPRS | REST_INLINE_GPRS);
+		 | SAVE_INLINE_GPRS | REST_INLINE_GPRS
+		 | SAVE_INLINE_VRS | REST_INLINE_VRS);
 
   if (info->first_fp_reg_save == 64
       || FP_SAVE_INLINE (info->first_fp_reg_save)
       /* The out-of-line FP routines use double-precision stores;
 	 we can't use those routines if we don't have such stores.  */
       || (TARGET_HARD_FLOAT && !TARGET_DOUBLE_FLOAT)
-      || !no_global_regs_above (info->first_fp_reg_save, /*gpr=*/false))
+      || global_regs_p (info->first_fp_reg_save, 64))
     strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
 
   if (info->first_gp_reg_save == 32
       || GP_SAVE_INLINE (info->first_gp_reg_save)
-      || !((strategy & SAVRES_MULTIPLE)
-	   || no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true)))
+      || (!(strategy & SAVRES_MULTIPLE)
+	  && global_regs_p (info->first_gp_reg_save, 32)))
     strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
 
+  if (info->first_altivec_reg_save == LAST_ALTIVEC_REGNO + 1
+      || V_SAVE_INLINE (info->first_altivec_reg_save)
+      || global_regs_p (info->first_altivec_reg_save, LAST_ALTIVEC_REGNO + 1))
+    strategy |= SAVE_INLINE_VRS | REST_INLINE_VRS;
+
   /* Don't bother to try to save things out-of-line if r11 is occupied
      by the static chain.  It would require too much fiddling and the
      static chain is rarely used anyway.  FPRs are saved w.r.t the stack
@@ -17457,7 +17483,8 @@ rs6000_savres_strategy (rs6000_stack_t *
   if (using_static_chain_p && DEFAULT_ABI != ABI_AIX)
     strategy |= ((DEFAULT_ABI == ABI_DARWIN
 		  ? 0 : SAVE_INLINE_FPRS | REST_INLINE_FPRS)
-		 | SAVE_INLINE_GPRS);
+		 | SAVE_INLINE_GPRS
+		 | SAVE_INLINE_VRS | REST_INLINE_VRS);
 
   /* If we are going to use store multiple, then don't even bother
      with the out-of-line routines, since the store-multiple
@@ -17465,6 +17492,16 @@ rs6000_savres_strategy (rs6000_stack_t *
   if ((strategy & SAVRES_MULTIPLE))
     strategy |= SAVE_INLINE_GPRS;
 
+  /* info->lr_save_p isn't yet set if the only reason lr needs to be
+     saved is an out-of-line save or restore.  Set up the value for
+     the next test (excluding out-of-line gpr restore).  */
+  lr_save_p = (info->lr_save_p
+	       || !(strategy & SAVE_INLINE_GPRS)
+	       || !(strategy & SAVE_INLINE_FPRS)
+	       || !(strategy & SAVE_INLINE_VRS)
+	       || !(strategy & REST_INLINE_FPRS)
+	       || !(strategy & REST_INLINE_VRS));
+
   /* The situation is more complicated with load multiple.  We'd
      prefer to use the out-of-line routines for restores, since the
      "exit" out-of-line routines can handle the restore of LR and the
@@ -17474,7 +17511,7 @@ rs6000_savres_strategy (rs6000_stack_t *
      have saved some fprs; In those cases it is advantageous to use
      load multiple when available.  */
   if ((strategy & SAVRES_MULTIPLE)
-      && (!info->lr_save_p
+      && (!lr_save_p
 	  || info->first_fp_reg_save != 64))
     strategy |= REST_INLINE_GPRS;
 
@@ -17868,8 +17905,10 @@ rs6000_stack_info (void)
 
   if (!(info_ptr->savres_strategy & SAVE_INLINE_GPRS)
       || !(info_ptr->savres_strategy & SAVE_INLINE_FPRS)
+      || !(info_ptr->savres_strategy & SAVE_INLINE_VRS)
       || !(info_ptr->savres_strategy & REST_INLINE_GPRS)
-      || !(info_ptr->savres_strategy & REST_INLINE_FPRS))
+      || !(info_ptr->savres_strategy & REST_INLINE_FPRS)
+      || !(info_ptr->savres_strategy & REST_INLINE_VRS))
     info_ptr->lr_save_p = 1;
 
   if (info_ptr->lr_save_p)
@@ -18965,30 +19004,25 @@ gen_frame_mem_offset (enum machine_mode
   return gen_frame_mem (mode, gen_rtx_PLUS (Pmode, reg, offset_rtx));
 }
 
-/* Look for user-defined global regs.  We should not save and restore these,
-   and cannot use stmw/lmw if there are any in its range.  */
-
-static bool
-no_global_regs_above (int first, bool gpr)
-{
-  int i;
-  int last = gpr ? 32 : 64;
-  for (i = first; i < last; i++)
-    if (global_regs[i])
-      return false;
-  return true;
-}
-
 #ifndef TARGET_FIX_AND_CONTINUE
 #define TARGET_FIX_AND_CONTINUE 0
 #endif
 
-/* It's really GPR 13 and FPR 14, but we need the smaller of the two.  */
+/* It's really GPR 13 or 14, FPR 14 and VR 20.  We need the smallest.  */
 #define FIRST_SAVRES_REGISTER FIRST_SAVED_GP_REGNO
 #define LAST_SAVRES_REGISTER 31
 #define N_SAVRES_REGISTERS (LAST_SAVRES_REGISTER - FIRST_SAVRES_REGISTER + 1)
 
-static GTY(()) rtx savres_routine_syms[N_SAVRES_REGISTERS][8];
+enum {
+  SAVRES_LR = 0x1,
+  SAVRES_SAVE = 0x2,
+  SAVRES_REG = 0x0c,
+  SAVRES_GPR = 0,
+  SAVRES_FPR = 4,
+  SAVRES_VR  = 8
+};
+
+static GTY(()) rtx savres_routine_syms[N_SAVRES_REGISTERS][12];
 
 /* Temporary holding space for an out-of-line register save/restore
    routine name.  */
@@ -18998,8 +19032,7 @@ static char savres_routine_name[30];
    We are saving/restoring GPRs if GPR is true.  */
 
 static char *
-rs6000_savres_routine_name (rs6000_stack_t *info, int regno,
-			    bool savep, bool gpr, bool lr)
+rs6000_savres_routine_name (rs6000_stack_t *info, int regno, int sel)
 {
   const char *prefix = "";
   const char *suffix = "";
@@ -19035,14 +19068,14 @@ rs6000_savres_routine_name (rs6000_stack
   if (TARGET_SPE)
     {
       /* No floating point saves on the SPE.  */
-      gcc_assert (gpr);
+      gcc_assert ((sel & SAVRES_REG) == SAVRES_GPR);
 
-      if (savep)
+      if ((sel & SAVRES_SAVE))
 	prefix = info->spe_64bit_regs_used ? "_save64gpr_" : "_save32gpr_";
       else
 	prefix = info->spe_64bit_regs_used ? "_rest64gpr_" : "_rest32gpr_";
 
-      if (lr)
+      if ((sel & SAVRES_LR))
 	suffix = "_x";
     }
   else if (DEFAULT_ABI == ABI_V4)
@@ -19050,35 +19083,46 @@ rs6000_savres_routine_name (rs6000_stack
       if (TARGET_64BIT)
 	goto aix_names;
 
-      if (gpr)
-	prefix = savep ? "_savegpr_" : "_restgpr_";
+      if ((sel & SAVRES_REG) == SAVRES_GPR)
+	prefix = (sel & SAVRES_SAVE) ? "_savegpr_" : "_restgpr_";
+      else if ((sel & SAVRES_REG) == SAVRES_FPR)
+	prefix = (sel & SAVRES_SAVE) ? "_savefpr_" : "_restfpr_";
+      else if ((sel & SAVRES_REG) == SAVRES_VR)
+	prefix = (sel & SAVRES_SAVE) ? "_savevr_" : "_restvr_";
       else
-	prefix = savep ? "_savefpr_" : "_restfpr_";
+	abort ();
 
-      if (lr)
+      if ((sel & SAVRES_LR))
 	suffix = "_x";
     }
   else if (DEFAULT_ABI == ABI_AIX)
     {
 #if !defined (POWERPC_LINUX) && !defined (POWERPC_FREEBSD)
       /* No out-of-line save/restore routines for GPRs on AIX.  */
-      gcc_assert (!TARGET_AIX || !gpr);
+      gcc_assert (!TARGET_AIX || (sel & SAVRES_REG) != SAVRES_GPR);
 #endif
 
     aix_names:
-      if (gpr)
-	prefix = (savep
-		  ? (lr ? "_savegpr0_" : "_savegpr1_")
-		  : (lr ? "_restgpr0_" : "_restgpr1_"));
+      if ((sel & SAVRES_REG) == SAVRES_GPR)
+	prefix = ((sel & SAVRES_SAVE)
+		  ? ((sel & SAVRES_LR) ? "_savegpr0_" : "_savegpr1_")
+		  : ((sel & SAVRES_LR) ? "_restgpr0_" : "_restgpr1_"));
+      else if ((sel & SAVRES_REG) == SAVRES_FPR)
+	{
 #if defined (POWERPC_LINUX) || defined (POWERPC_FREEBSD)
-      else if (lr)
-	prefix = (savep ? "_savefpr_" : "_restfpr_");
+	  if ((sel & SAVRES_LR))
+	    prefix = ((sel & SAVRES_SAVE) ? "_savefpr_" : "_restfpr_");
+	  else
 #endif
-      else
-	{
-	  prefix = savep ? SAVE_FP_PREFIX : RESTORE_FP_PREFIX;
-	  suffix = savep ? SAVE_FP_SUFFIX : RESTORE_FP_SUFFIX;
+	    {
+	      prefix = (sel & SAVRES_SAVE) ? SAVE_FP_PREFIX : RESTORE_FP_PREFIX;
+	      suffix = (sel & SAVRES_SAVE) ? SAVE_FP_SUFFIX : RESTORE_FP_SUFFIX;
+	    }
 	}
+      else if ((sel & SAVRES_REG) == SAVRES_VR)
+	prefix = (sel & SAVRES_SAVE) ? "_savevr_" : "_restvr_";
+      else
+	abort ();
     }
 
    if (DEFAULT_ABI == ABI_DARWIN)
@@ -19088,14 +19132,19 @@ rs6000_savres_routine_name (rs6000_stack
 	 single symbol for the start of save sequence, and the code here
 	 embeds an offset into that code on the basis of the first register
 	 to be saved.  */
-      prefix = savep ? "save" : "rest" ;
-      if (gpr)
-	sprintf (savres_routine_name, "*%sGPR%s%s%.0d ; %s r%d-r31",
-	       prefix, (lr ? "x" : ""), (regno == 13 ? "" : "+"),
-	       (regno-13) * 4, prefix, regno);
+      prefix = (sel & SAVRES_SAVE) ? "save" : "rest" ;
+      if ((sel & SAVRES_REG) == SAVRES_GPR)
+	sprintf (savres_routine_name, "*%sGPR%s%s%.0d ; %s r%d-r31", prefix,
+		 ((sel & SAVRES_LR) ? "x" : ""), (regno == 13 ? "" : "+"),
+		 (regno - 13) * 4, prefix, regno);
+      else if ((sel & SAVRES_REG) == SAVRES_FPR)
+	sprintf (savres_routine_name, "*%sFP%s%.0d ; %s f%d-f31", prefix,
+		 (regno == 14 ? "" : "+"), (regno - 14) * 4, prefix, regno);
+      else if ((sel & SAVRES_REG) == SAVRES_VR)
+	sprintf (savres_routine_name, "*%sVEC%s%.0d ; %s v%d-v31", prefix,
+		 (regno == 20 ? "" : "+"), (regno - 20) * 8, prefix, regno);
       else
-	sprintf (savres_routine_name, "*%sFP%s%.0d ; %s f%d-f31",
-	       prefix, (regno == 14 ? "" : "+"),  (regno-14) * 4, prefix, regno);
+	abort ();
     }
   else
     sprintf (savres_routine_name, "%s%d%s", prefix, regno, suffix);
@@ -19107,22 +19156,28 @@ rs6000_savres_routine_name (rs6000_stack
    We are saving/restoring GPRs if GPR is true.  */
 
 static rtx
-rs6000_savres_routine_sym (rs6000_stack_t *info, bool savep,
-			   bool gpr, bool lr)
+rs6000_savres_routine_sym (rs6000_stack_t *info, int sel)
 {
-  int regno = gpr ? info->first_gp_reg_save : (info->first_fp_reg_save - 32);
+  int regno = ((sel & SAVRES_REG) == SAVRES_GPR
+	       ? info->first_gp_reg_save
+	       : (sel & SAVRES_REG) == SAVRES_FPR
+	       ? info->first_fp_reg_save - 32
+	       : (sel & SAVRES_REG) == SAVRES_VR
+	       ? info->first_altivec_reg_save - FIRST_ALTIVEC_REGNO
+	       : -1);
   rtx sym;
-  int select = ((savep ? 1 : 0) << 2
-		| ((TARGET_SPE_ABI
-		    /* On the SPE, we never have any FPRs, but we do have
-		       32/64-bit versions of the routines.  */
-		    ? (info->spe_64bit_regs_used ? 1 : 0)
-		    : (gpr ? 1 : 0)) << 1)
-		| (lr ? 1: 0));
+  int select = sel;
+
+  /* On the SPE, we never have any FPRs, but we do have 32/64-bit
+     versions of the gpr routines.  */
+  if (TARGET_SPE_ABI && (sel & SAVRES_REG) == SAVRES_GPR
+      && info->spe_64bit_regs_used)
+    select ^= SAVRES_FPR ^ SAVRES_GPR;
 
   /* Don't generate bogus routine names.  */
   gcc_assert (FIRST_SAVRES_REGISTER <= regno
-	      && regno <= LAST_SAVRES_REGISTER);
+	      && regno <= LAST_SAVRES_REGISTER
+	      && select >= 0 && select <= 12);
 
   sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select];
 
@@ -19130,7 +19185,7 @@ rs6000_savres_routine_sym (rs6000_stack_
     {
       char *name;
 
-      name = rs6000_savres_routine_name (info, regno, savep, gpr, lr);
+      name = rs6000_savres_routine_name (info, regno, sel);
 
       sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select]
 	= gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
@@ -19176,11 +19231,11 @@ rs6000_emit_stack_reset (rs6000_stack_t
 }
 
 static inline unsigned
-ptr_regno_for_savres (bool gpr, bool lr)
+ptr_regno_for_savres (int sel)
 {
   if (DEFAULT_ABI == ABI_AIX)
-    return !gpr || lr ? 1 : 12;
-  return DEFAULT_ABI == ABI_DARWIN && !gpr ? 1 : 11;
+    return (sel & SAVRES_REG) == SAVRES_FPR || (sel & SAVRES_LR) ? 1 : 12;
+  return DEFAULT_ABI == ABI_DARWIN && (sel & SAVRES_REG) == SAVRES_FPR ? 1 : 11;
 }
 
 /* Construct a parallel rtx describing the effect of a call to an
@@ -19190,8 +19245,7 @@ ptr_regno_for_savres (bool gpr, bool lr)
 static rtx
 rs6000_emit_savres_rtx (rs6000_stack_t *info,
 			rtx frame_reg_rtx, int save_area_offset, int lr_offset,
-			enum machine_mode reg_mode,
-			bool savep, bool gpr, bool lr)
+			enum machine_mode reg_mode, int sel)
 {
   int i;
   int offset, start_reg, end_reg, n_regs, use_reg;
@@ -19201,25 +19255,46 @@ rs6000_emit_savres_rtx (rs6000_stack_t *
   rtx par, insn;
 
   offset = 0;
-  start_reg = (gpr
+  start_reg = ((sel & SAVRES_REG) == SAVRES_GPR
 	       ? info->first_gp_reg_save
-	       : info->first_fp_reg_save);
-  end_reg = gpr ? 32 : 64;
+	       : (sel & SAVRES_REG) == SAVRES_FPR
+	       ? info->first_fp_reg_save
+	       : (sel & SAVRES_REG) == SAVRES_VR
+	       ? info->first_altivec_reg_save
+	       : -1);
+  end_reg = ((sel & SAVRES_REG) == SAVRES_GPR
+	     ? 32
+	     : (sel & SAVRES_REG) == SAVRES_FPR
+	     ? 64
+	     : (sel & SAVRES_REG) == SAVRES_VR
+	     ? LAST_ALTIVEC_REGNO + 1
+	     : -1);
   n_regs = end_reg - start_reg;
-  p = rtvec_alloc ((lr ? 4 : 3) + n_regs);
+  p = rtvec_alloc (3 + ((sel & SAVRES_LR) ? 1 : 0)
+		   + ((sel & SAVRES_REG) == SAVRES_VR ? 1 : 0)
+		   + n_regs);
 
-  if (!savep && lr)
+  if (!(sel & SAVRES_SAVE) && (sel & SAVRES_LR))
     RTVEC_ELT (p, offset++) = ret_rtx;
 
   RTVEC_ELT (p, offset++)
     = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, LR_REGNO));
 
-  sym = rs6000_savres_routine_sym (info, savep, gpr, lr);
+  sym = rs6000_savres_routine_sym (info, sel);
   RTVEC_ELT (p, offset++) = gen_rtx_USE (VOIDmode, sym);
-  use_reg = ptr_regno_for_savres (gpr, lr);
-  RTVEC_ELT (p, offset++)
-    = gen_rtx_USE (VOIDmode,
-		   gen_rtx_REG (Pmode, use_reg));
+
+  use_reg = ptr_regno_for_savres (sel);
+  if ((sel & SAVRES_REG) == SAVRES_VR)
+    {
+      /* Vector regs are saved/restored using [reg+reg] addressing.  */
+      RTVEC_ELT (p, offset++)
+	= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, use_reg));
+      RTVEC_ELT (p, offset++)
+	= gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 0));
+    }
+  else
+    RTVEC_ELT (p, offset++)
+      = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, use_reg));
 
   for (i = 0; i < end_reg - start_reg; i++)
     {
@@ -19230,11 +19305,11 @@ rs6000_emit_savres_rtx (rs6000_stack_t *
       mem = gen_frame_mem (reg_mode, addr);
 
       RTVEC_ELT (p, i + offset) = gen_rtx_SET (VOIDmode,
-					       savep ? mem : reg,
-					       savep ? reg : mem);
+					       (sel & SAVRES_SAVE) ? mem : reg,
+					       (sel & SAVRES_SAVE) ? reg : mem);
     }
 
-  if (savep && lr)
+  if ((sel & SAVRES_SAVE) && (sel & SAVRES_LR))
     {
       rtx addr, reg, mem;
       reg = gen_rtx_REG (Pmode, 0);
@@ -19246,7 +19321,7 @@ rs6000_emit_savres_rtx (rs6000_stack_t *
 
   par = gen_rtx_PARALLEL (VOIDmode, p);
 
-  if (!savep && lr)
+  if (!(sel & SAVRES_SAVE) && (sel & SAVRES_LR))
     {
       insn = emit_jump_insn (par);
       JUMP_LABEL (insn) = ret_rtx;
@@ -19291,9 +19366,6 @@ rs6000_emit_prologue (void)
   rtx cr_save_rtx = NULL_RTX;
   rtx insn;
   int strategy;
-  int saving_FPRs_inline;
-  int saving_GPRs_inline;
-  int using_store_multiple;
   int using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
 			      && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
 			      && call_used_regs[STATIC_CHAIN_REGNUM]);
@@ -19469,9 +19541,6 @@ rs6000_emit_prologue (void)
     }
 
   strategy = info->savres_strategy;
-  using_store_multiple = strategy & SAVRES_MULTIPLE;
-  saving_FPRs_inline = strategy & SAVE_INLINE_FPRS;
-  saving_GPRs_inline = strategy & SAVE_INLINE_GPRS;
 
   /* For V.4, update stack before we do any saving and set back pointer.  */
   if (! WORLD_SAVE_P (info)
@@ -19480,18 +19549,19 @@ rs6000_emit_prologue (void)
 	  || crtl->calls_eh_return))
     {
       bool need_r11 = (TARGET_SPE
-		       ? (!saving_GPRs_inline
+		       ? (!(strategy & SAVE_INLINE_GPRS)
 			  && info->spe_64bit_regs_used == 0)
-		       : (!saving_FPRs_inline || !saving_GPRs_inline));
+		       : (!(strategy & SAVE_INLINE_FPRS)
+			  || !(strategy & SAVE_INLINE_GPRS)
+			  || !(strategy & SAVE_INLINE_VRS)));
+      int ptr_regno = -1;
       rtx ptr_reg = NULL_RTX;
+      int ptr_off = 0;
 
-      if (need_r11)
-	{
-	  ptr_reg = gen_rtx_REG (Pmode, 11);
-	  START_USE (11);
-	}
-      else if (info->total_size < 32767)
+      if (info->total_size < 32767)
 	frame_off = info->total_size;
+      else if (need_r11)
+	ptr_regno = 11;
       else if (info->cr_save_p
 	       || info->lr_save_p
 	       || info->first_fp_reg_save < 64
@@ -19499,10 +19569,7 @@ rs6000_emit_prologue (void)
 	       || info->altivec_size != 0
 	       || info->vrsave_mask != 0
 	       || crtl->calls_eh_return)
-	{
-	  ptr_reg = gen_rtx_REG (Pmode, 12);
-	  START_USE (12);
-	}
+	ptr_regno = 12;
       else
 	{
 	  /* The prologue won't be saving any regs so there is no need
@@ -19512,17 +19579,22 @@ rs6000_emit_prologue (void)
 	     changes to this function.  */
 	  frame_off = info->total_size;
 	}
-      if (ptr_reg != NULL_RTX)
+      if (ptr_regno != -1)
 	{
 	  /* Set up the frame offset to that needed by the first
 	     out-of-line save function.  */
+	  START_USE (ptr_regno);
+	  ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
 	  frame_reg_rtx = ptr_reg;
-	  if (!saving_FPRs_inline && info->first_fp_reg_save < 64)
+	  if (!(strategy & SAVE_INLINE_FPRS) && info->fp_size != 0)
 	    gcc_checking_assert (info->fp_save_offset + info->fp_size == 0);
-	  else if (!saving_GPRs_inline && info->first_gp_reg_save < 32)
-	    frame_off = -(info->gp_save_offset + info->gp_size);
+	  else if (!(strategy & SAVE_INLINE_GPRS) && info->first_gp_reg_save < 32)
+	    ptr_off = info->gp_save_offset + info->gp_size;
+	  else if (!(strategy & SAVE_INLINE_VRS) && info->altivec_size != 0)
+	    ptr_off = info->altivec_save_offset + info->altivec_size;
+	  frame_off = -ptr_off;
 	}
-      rs6000_emit_allocate_stack (info->total_size, ptr_reg, -frame_off);
+      rs6000_emit_allocate_stack (info->total_size, ptr_reg, ptr_off);
       sp_off = info->total_size;
       if (frame_reg_rtx != sp_reg_rtx)
 	rs6000_emit_stack_tie (frame_reg_rtx, false);
@@ -19584,7 +19656,7 @@ rs6000_emit_prologue (void)
 
   /* Do any required saving of fpr's.  If only one or two to save, do
      it ourselves.  Otherwise, call function.  */
-  if (!WORLD_SAVE_P (info) && saving_FPRs_inline)
+  if (!WORLD_SAVE_P (info) && (strategy & SAVE_INLINE_FPRS))
     {
       int i;
       for (i = 0; i < 64 - info->first_fp_reg_save; i++)
@@ -19600,17 +19672,24 @@ rs6000_emit_prologue (void)
   else if (!WORLD_SAVE_P (info) && info->first_fp_reg_save != 64)
     {
       bool lr = (strategy & SAVE_NOINLINE_FPRS_SAVES_LR) != 0;
-      unsigned ptr_regno = ptr_regno_for_savres (/*gpr=*/false, lr);
+      int sel = SAVRES_SAVE | SAVRES_FPR | (lr ? SAVRES_LR : 0);
+      unsigned ptr_regno = ptr_regno_for_savres (sel);
+      rtx ptr_reg = frame_reg_rtx;
 
-      gcc_checking_assert (ptr_regno == REGNO (frame_reg_rtx)
-			   && info->fp_save_offset + info->fp_size == 0
-			   && frame_off == 0);
-      insn = rs6000_emit_savres_rtx (info, frame_reg_rtx,
+      if (REGNO (frame_reg_rtx) == ptr_regno)
+	gcc_checking_assert (frame_off == 0);
+      else
+	{
+	  ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
+	  NOT_INUSE (ptr_regno);
+	  emit_insn (gen_add3_insn (ptr_reg,
+				    frame_reg_rtx, GEN_INT (frame_off)));
+	}
+      insn = rs6000_emit_savres_rtx (info, ptr_reg,
 				     info->fp_save_offset,
 				     info->lr_save_offset,
-				     DFmode,
-				     /*savep=*/true, /*gpr=*/false, lr);
-      rs6000_frame_related (insn, frame_reg_rtx, sp_off,
+				     DFmode, sel);
+      rs6000_frame_related (insn, ptr_reg, sp_off,
 			    NULL_RTX, NULL_RTX);
       if (lr)
 	END_USE (0);
@@ -19634,7 +19713,7 @@ rs6000_emit_prologue (void)
       int spe_regs_addressable
 	= (SPE_CONST_OFFSET_OK (info->spe_gp_save_offset + frame_off
 				+ reg_size * (32 - info->first_gp_reg_save - 1))
-	   && saving_GPRs_inline);
+	   && (strategy & SAVE_INLINE_GPRS));
 
       if (spe_regs_addressable)
 	{
@@ -19648,7 +19727,7 @@ rs6000_emit_prologue (void)
 	     it is, then temporarily save it in r0.  */
 	  HOST_WIDE_INT offset;
 
-	  if (!saving_GPRs_inline)
+	  if (!(strategy & SAVE_INLINE_GPRS))
 	    ool_adjust = 8 * (info->first_gp_reg_save
 			      - (FIRST_SAVRES_REGISTER + 1));
 	  offset = info->spe_gp_save_offset + frame_off - ool_adjust;
@@ -19673,7 +19752,7 @@ rs6000_emit_prologue (void)
 	    frame_off = -info->spe_gp_save_offset + ool_adjust;
 	}
 
-      if (saving_GPRs_inline)
+      if ((strategy & SAVE_INLINE_GPRS))
 	{
 	  for (i = 0; i < 32 - info->first_gp_reg_save; i++)
 	    if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
@@ -19687,10 +19766,8 @@ rs6000_emit_prologue (void)
 	{
 	  insn = rs6000_emit_savres_rtx (info, spe_save_area_ptr,
 					 info->spe_gp_save_offset + save_off,
-					 info->lr_save_offset + save_off,
-					 reg_mode,
-					 /*savep=*/true, /*gpr=*/true,
-					 /*lr=*/false);
+					 0, reg_mode,
+					 SAVRES_SAVE | SAVRES_GPR);
 
 	  rs6000_frame_related (insn, spe_save_area_ptr, sp_off - save_off,
 				NULL_RTX, NULL_RTX);
@@ -19708,10 +19785,11 @@ rs6000_emit_prologue (void)
 	    END_USE (11);
 	}
     }
-  else if (!WORLD_SAVE_P (info) && !saving_GPRs_inline)
+  else if (!WORLD_SAVE_P (info) && !(strategy & SAVE_INLINE_GPRS))
     {
       bool lr = (strategy & SAVE_NOINLINE_GPRS_SAVES_LR) != 0;
-      unsigned ptr_regno = ptr_regno_for_savres (/*gpr=*/true, lr);
+      int sel = SAVRES_SAVE | SAVRES_GPR | (lr ? SAVRES_LR : 0);
+      unsigned ptr_regno = ptr_regno_for_savres (sel);
       rtx ptr_reg = frame_reg_rtx;
       bool ptr_set_up = REGNO (ptr_reg) == ptr_regno;
       int end_save = info->gp_save_offset + info->gp_size;
@@ -19740,14 +19818,13 @@ rs6000_emit_prologue (void)
       insn = rs6000_emit_savres_rtx (info, ptr_reg,
 				     info->gp_save_offset + ptr_off,
 				     info->lr_save_offset + ptr_off,
-				     reg_mode,
-				     /*savep=*/true, /*gpr=*/true, lr);
+				     reg_mode, sel);
       rs6000_frame_related (insn, ptr_reg, sp_off - ptr_off,
 			    NULL_RTX, NULL_RTX);
       if (lr)
 	END_USE (0);
     }
-  else if (!WORLD_SAVE_P (info) && using_store_multiple)
+  else if (!WORLD_SAVE_P (info) && (strategy & SAVRES_MULTIPLE))
     {
       rtvec p;
       int i;
@@ -19907,24 +19984,31 @@ rs6000_emit_prologue (void)
       && !(DEFAULT_ABI == ABI_V4 || crtl->calls_eh_return))
     {
       rtx ptr_reg = NULL;
+      int ptr_off = 0;
 
       /* If saving altivec regs we need to be able to address all save
 	 locations using a 16-bit offset.  */
-      if ((info->altivec_size != 0
-	   && (info->altivec_save_offset + info->altivec_size - 16
-	       + info->total_size - frame_off) > 32767)
+      if ((strategy & SAVE_INLINE_VRS) == 0
+	  || (info->altivec_size != 0
+	      && (info->altivec_save_offset + info->altivec_size - 16
+		  + info->total_size - frame_off) > 32767)
 	  || (info->vrsave_mask != 0
 	      && (info->vrsave_save_offset
 		  + info->total_size - frame_off) > 32767))
 	{
-	  START_USE (12);
-	  ptr_reg = gen_rtx_REG (Pmode, 12);
+	  int sel = SAVRES_SAVE | SAVRES_VR;
+	  unsigned ptr_regno = ptr_regno_for_savres (sel);
+
+	  if (REGNO (frame_reg_rtx) != ptr_regno)
+	    START_USE (ptr_regno);
+	  ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
 	  frame_reg_rtx = ptr_reg;
-	  frame_off = -(info->altivec_save_offset + info->altivec_size);
+	  ptr_off = info->altivec_save_offset + info->altivec_size;
+	  frame_off = -ptr_off;
 	}
       else if (REGNO (frame_reg_rtx) == 1)
 	frame_off = info->total_size;
-      rs6000_emit_allocate_stack (info->total_size, ptr_reg, -frame_off);
+      rs6000_emit_allocate_stack (info->total_size, ptr_reg, ptr_off);
       sp_off = info->total_size;
       if (frame_reg_rtx != sp_reg_rtx)
 	rs6000_emit_stack_tie (frame_reg_rtx, false);
@@ -19939,13 +20023,48 @@ rs6000_emit_prologue (void)
     }
 
   /* Save AltiVec registers if needed.  Save here because the red zone does
-     not include AltiVec registers.  */
-  if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI && info->altivec_size != 0)
+     not always include AltiVec registers.  */
+  if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI
+      && info->altivec_size != 0 && (strategy & SAVE_INLINE_VRS) == 0)
+    {
+      int end_save = info->altivec_save_offset + info->altivec_size;
+      int ptr_off;
+      /* Oddly, the vector save/restore functions point r0 at the end
+	 of the save area, then use r11 or r12 to load offsets for
+	 [reg+reg] addressing.  */
+      rtx ptr_reg = gen_rtx_REG (Pmode, 0);
+      int scratch_regno = ptr_regno_for_savres (SAVRES_SAVE | SAVRES_VR);
+      rtx scratch_reg = gen_rtx_REG (Pmode, scratch_regno);
+
+      gcc_checking_assert (scratch_regno == 11 || scratch_regno == 12);
+      NOT_INUSE (0);
+      if (end_save + frame_off != 0)
+	{
+	  rtx offset = GEN_INT (end_save + frame_off);
+
+	  emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx, offset));
+	}
+      else
+	emit_move_insn (ptr_reg, frame_reg_rtx);
+
+      ptr_off = -end_save;
+      insn = rs6000_emit_savres_rtx (info, scratch_reg,
+				     info->altivec_save_offset + ptr_off,
+				     0, V4SImode, SAVRES_SAVE | SAVRES_VR);
+      rs6000_frame_related (insn, scratch_reg, sp_off - ptr_off,
+			    NULL_RTX, NULL_RTX);
+      if (REGNO (frame_reg_rtx) == REGNO (scratch_reg))
+	{
+	  /* The oddity mentioned above clobbered our frame reg.  */
+	  emit_move_insn (frame_reg_rtx, ptr_reg);
+	  frame_off = ptr_off;
+	}
+    }
+  else if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI
+	   && info->altivec_size != 0)
     {
       int i;
 
-      /* There should be a non inline version of this, for when we
-	 are saving lots of vector registers.  */
       for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
 	if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
 	  {
@@ -19986,12 +20105,23 @@ rs6000_emit_prologue (void)
     {
       rtx reg, mem, vrsave;
       int offset;
+      int save_regno;
 
       /* Get VRSAVE onto a GPR.  Note that ABI_V4 might be using r12
 	 as frame_reg_rtx and r11 as the static chain pointer for
 	 nested functions.  */
-      NOT_INUSE (0);
-      reg = gen_rtx_REG (SImode, 0);
+      save_regno = 12;
+      if (DEFAULT_ABI == ABI_AIX && !using_static_chain_p)
+	save_regno = 11;
+      else if (REGNO (frame_reg_rtx) == 12)
+	{
+	  save_regno = 11;
+	  if (using_static_chain_p)
+	    save_regno = 0;
+	}
+
+      NOT_INUSE (save_regno);
+      reg = gen_rtx_REG (SImode, save_regno);
       vrsave = gen_rtx_REG (SImode, VRSAVE_REGNO);
       if (TARGET_MACHO)
 	emit_insn (gen_get_vrsave_internal (reg));
@@ -20104,21 +20234,25 @@ rs6000_output_function_prologue (FILE *f
   /* Write .extern for any function we will call to save and restore
      fp values.  */
   if (info->first_fp_reg_save < 64
-      && !TARGET_MACHO)
+      && !TARGET_MACHO
+      && !TARGET_ELF)
     {
       char *name;
       int regno = info->first_fp_reg_save - 32;
 
       if ((info->savres_strategy & SAVE_INLINE_FPRS) == 0)
 	{
-	  name = rs6000_savres_routine_name (info, regno, /*savep=*/true,
-					     /*gpr=*/false, /*lr=*/false);
+	  bool lr = (info->savres_strategy & SAVE_NOINLINE_FPRS_SAVES_LR) != 0;
+	  int sel = SAVRES_SAVE | SAVRES_FPR | (lr ? SAVRES_LR : 0);
+	  name = rs6000_savres_routine_name (info, regno, sel);
 	  fprintf (file, "\t.extern %s\n", name);
 	}
       if ((info->savres_strategy & REST_INLINE_FPRS) == 0)
 	{
-	  name = rs6000_savres_routine_name (info, regno, /*savep=*/false,
-					     /*gpr=*/false, /*lr=*/true);
+	  bool lr = (info->savres_strategy
+		     & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
+	  int sel = SAVRES_FPR | (lr ? SAVRES_LR : 0);
+	  name = rs6000_savres_routine_name (info, regno, sel);
 	  fprintf (file, "\t.extern %s\n", name);
 	}
     }
@@ -20397,10 +20531,20 @@ rs6000_emit_epilogue (int sibcall)
 	      && offset_below_red_zone_p (info->altivec_save_offset))))
     {
       int i;
+      int scratch_regno = ptr_regno_for_savres (SAVRES_VR);
 
+      gcc_checking_assert (scratch_regno == 11 || scratch_regno == 12);
       if (use_backchain_to_restore_sp)
 	{
-	  frame_reg_rtx = gen_rtx_REG (Pmode, 11);
+	  int frame_regno = 11;
+
+	  if ((strategy & SAVE_INLINE_VRS) == 0)
+	    {
+	      /* Of r11 and r12, select the one not clobbered by an
+		 out-of-line restore function for the frame register.  */
+	      frame_regno = 11 + 12 - scratch_regno;
+	    }
+	  frame_reg_rtx = gen_rtx_REG (Pmode, frame_regno);
 	  emit_move_insn (frame_reg_rtx,
 			  gen_rtx_MEM (Pmode, sp_reg_rtx));
 	  frame_off = 0;
@@ -20408,29 +20552,59 @@ rs6000_emit_epilogue (int sibcall)
       else if (frame_pointer_needed)
 	frame_reg_rtx = hard_frame_pointer_rtx;
 
-      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
-	if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
-	  {
-	    rtx addr, areg, mem, reg;
+      if ((strategy & SAVE_INLINE_VRS) == 0)
+	{
+	  int end_save = info->altivec_save_offset + info->altivec_size;
+	  int ptr_off;
+	  rtx ptr_reg = gen_rtx_REG (Pmode, 0);
+	  rtx scratch_reg = gen_rtx_REG (Pmode, scratch_regno);
 
-	    areg = gen_rtx_REG (Pmode, 0);
-	    emit_move_insn
-	      (areg, GEN_INT (info->altivec_save_offset
-			      + frame_off
-			      + 16 * (i - info->first_altivec_reg_save)));
+	  if (end_save + frame_off != 0)
+	    {
+	      rtx offset = GEN_INT (end_save + frame_off);
 
-	    /* AltiVec addressing mode is [reg+reg].  */
-	    addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
-	    mem = gen_frame_mem (V4SImode, addr);
+	      emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx, offset));
+	    }
+	  else
+	    emit_move_insn (ptr_reg, frame_reg_rtx);
 
-	    reg = gen_rtx_REG (V4SImode, i);
-	    emit_move_insn (reg, mem);
-	    if (flag_shrink_wrap
-		|| offset_below_red_zone_p (info->altivec_save_offset
-					    + (i - info->first_altivec_reg_save)
-					    * 16))
-	      cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg,
-					     cfa_restores);
+	  ptr_off = -end_save;
+	  insn = rs6000_emit_savres_rtx (info, scratch_reg,
+					 info->altivec_save_offset + ptr_off,
+					 0, V4SImode, SAVRES_VR);
+	}
+      else
+	{
+	  for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+	    if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
+	      {
+		rtx addr, areg, mem, reg;
+
+		areg = gen_rtx_REG (Pmode, 0);
+		emit_move_insn
+		  (areg, GEN_INT (info->altivec_save_offset
+				  + frame_off
+				  + 16 * (i - info->first_altivec_reg_save)));
+
+		/* AltiVec addressing mode is [reg+reg].  */
+		addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
+		mem = gen_frame_mem (V4SImode, addr);
+
+		reg = gen_rtx_REG (V4SImode, i);
+		emit_move_insn (reg, mem);
+	      }
+	}
+
+      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+	if (((strategy & SAVE_INLINE_VRS) == 0
+	     || (info->vrsave_mask & ALTIVEC_REG_BIT (i)) != 0)
+	    && (flag_shrink_wrap
+		|| (offset_below_red_zone_p
+		    (info->altivec_save_offset
+		     + 16 * (i - info->first_altivec_reg_save)))))
+	  {
+	    rtx reg = gen_rtx_REG (V4SImode, i);
+	    cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
 	  }
     }
 
@@ -20540,26 +20714,94 @@ rs6000_emit_epilogue (int sibcall)
     {
       int i;
 
-      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
-	if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
-	  {
-	    rtx addr, areg, mem, reg;
+      if ((strategy & SAVE_INLINE_VRS) == 0)
+	{
+	  int end_save = info->altivec_save_offset + info->altivec_size;
+	  int ptr_off;
+	  rtx ptr_reg = gen_rtx_REG (Pmode, 0);
+	  int scratch_regno = ptr_regno_for_savres (SAVRES_VR);
+	  rtx scratch_reg = gen_rtx_REG (Pmode, scratch_regno);
 
-	    areg = gen_rtx_REG (Pmode, 0);
-	    emit_move_insn
-	      (areg, GEN_INT (info->altivec_save_offset
-			      + frame_off
-			      + 16 * (i - info->first_altivec_reg_save)));
+	  if (end_save + frame_off != 0)
+	    {
+	      rtx offset = GEN_INT (end_save + frame_off);
 
-	    /* AltiVec addressing mode is [reg+reg].  */
-	    addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
-	    mem = gen_frame_mem (V4SImode, addr);
+	      emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx, offset));
+	    }
+	  else
+	    emit_move_insn (ptr_reg, frame_reg_rtx);
 
-	    reg = gen_rtx_REG (V4SImode, i);
-	    emit_move_insn (reg, mem);
-	    if (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap)
-	      cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg,
-					     cfa_restores);
+	  ptr_off = -end_save;
+	  insn = rs6000_emit_savres_rtx (info, scratch_reg,
+					 info->altivec_save_offset + ptr_off,
+					 0, V4SImode, SAVRES_VR);
+	  if (REGNO (frame_reg_rtx) == REGNO (scratch_reg))
+	    {
+	      /* Frame reg was clobbered by out-of-line save.  Restore it
+		 from ptr_reg, and if we are calling out-of-line gpr or
+		 fpr restore set up the correct pointer and offset.  */
+	      unsigned newptr_regno = 1;
+	      if (!restoring_GPRs_inline)
+		{
+		  bool lr = info->gp_save_offset + info->gp_size == 0;
+		  int sel = SAVRES_GPR | (lr ? SAVRES_LR : 0);
+		  newptr_regno = ptr_regno_for_savres (sel);
+		  end_save = info->gp_save_offset + info->gp_size;
+		}
+	      else if (!restoring_FPRs_inline)
+		{
+		  bool lr = !(strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR);
+		  int sel = SAVRES_FPR | (lr ? SAVRES_LR : 0);
+		  newptr_regno = ptr_regno_for_savres (sel);
+		  end_save = info->gp_save_offset + info->gp_size;
+		}
+
+	      if (newptr_regno != 1 && REGNO (frame_reg_rtx) != newptr_regno)
+		frame_reg_rtx = gen_rtx_REG (Pmode, newptr_regno);
+		
+	      if (end_save + ptr_off != 0)
+		{
+		  rtx offset = GEN_INT (end_save + ptr_off);
+
+		  frame_off = -end_save;
+		  emit_insn (gen_add3_insn (frame_reg_rtx, ptr_reg, offset));
+		}
+	      else
+		{
+		  frame_off = ptr_off;
+		  emit_move_insn (frame_reg_rtx, ptr_reg);
+		}
+	    }
+	}
+      else
+	{
+	  for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+	    if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
+	      {
+		rtx addr, areg, mem, reg;
+
+		areg = gen_rtx_REG (Pmode, 0);
+		emit_move_insn
+		  (areg, GEN_INT (info->altivec_save_offset
+				  + frame_off
+				  + 16 * (i - info->first_altivec_reg_save)));
+
+		/* AltiVec addressing mode is [reg+reg].  */
+		addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
+		mem = gen_frame_mem (V4SImode, addr);
+
+		reg = gen_rtx_REG (V4SImode, i);
+		emit_move_insn (reg, mem);
+	      }
+	}
+
+      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+	if (((strategy & SAVE_INLINE_VRS) == 0
+	     || (info->vrsave_mask & ALTIVEC_REG_BIT (i)) != 0)
+	    && (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap))
+	  {
+	    rtx reg = gen_rtx_REG (V4SImode, i);
+	    cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
 	  }
     }
 
@@ -20598,12 +20840,24 @@ rs6000_emit_epilogue (int sibcall)
       rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
 			       GEN_INT (info->cr_save_offset + frame_off));
       rtx mem = gen_frame_mem (SImode, addr);
+      unsigned cr_save_regno = 12;
+
+      if (!restoring_GPRs_inline)
+	{
+	  /* Ensure we don't use the register used by the out-of-line
+	     gpr register restore below.  */
+	  bool lr = info->gp_save_offset + info->gp_size == 0;
+	  int sel = SAVRES_GPR | (lr ? SAVRES_LR : 0);
+	  int gpr_ptr_regno = ptr_regno_for_savres (sel);
+
+	  if (gpr_ptr_regno == 12)
+	    cr_save_regno = 11;
+	  gcc_checking_assert (REGNO (frame_reg_rtx) != cr_save_regno);
+	}
+      else if (REGNO (frame_reg_rtx) == 12)
+	cr_save_regno = 11;
 
-      cr_save_reg = gen_rtx_REG (SImode,
-				 DEFAULT_ABI == ABI_AIX
-				 && !restoring_GPRs_inline
-				 && info->first_fp_reg_save < 64
-				 ? 11 : 12);
+      cr_save_reg = gen_rtx_REG (SImode, cr_save_regno);
       emit_move_insn (cr_save_reg, mem);
     }
 
@@ -20704,8 +20958,7 @@ rs6000_emit_epilogue (int sibcall)
 				info->spe_gp_save_offset + frame_off,
 				info->lr_save_offset + frame_off,
 				reg_mode,
-				/*savep=*/false, /*gpr=*/true,
-				/*lr=*/true);
+				SAVRES_GPR | SAVRES_LR);
     }
   else if (!restoring_GPRs_inline)
     {
@@ -20713,16 +20966,19 @@ rs6000_emit_epilogue (int sibcall)
       rtx ptr_reg;
       int end_save = info->gp_save_offset + info->gp_size;
       bool can_use_exit = end_save == 0;
+      int sel = SAVRES_GPR | (can_use_exit ? SAVRES_LR : 0);
       int ptr_off;
 
       /* Emit stack reset code if we need it.  */
-      ptr_regno = ptr_regno_for_savres (/*gpr=*/true, can_use_exit);
+      ptr_regno = ptr_regno_for_savres (sel);
       ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
       if (can_use_exit)
 	rs6000_emit_stack_reset (info, frame_reg_rtx, frame_off, ptr_regno);
-      else
+      else if (end_save + frame_off != 0)
 	emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx,
 				  GEN_INT (end_save + frame_off)));
+      else if (REGNO (frame_reg_rtx) != ptr_regno)
+	emit_move_insn (ptr_reg, frame_reg_rtx);
       if (REGNO (frame_reg_rtx) == ptr_regno)
 	frame_off = -end_save;
 
@@ -20733,9 +20989,7 @@ rs6000_emit_epilogue (int sibcall)
       rs6000_emit_savres_rtx (info, ptr_reg,
 			      info->gp_save_offset + ptr_off,
 			      info->lr_save_offset + ptr_off,
-			      reg_mode,
-			      /*savep=*/false, /*gpr=*/true,
-			      /*lr=*/can_use_exit);
+			      reg_mode, sel);
     }
   else if (using_load_multiple)
     {
@@ -20872,7 +21126,8 @@ rs6000_emit_epilogue (int sibcall)
   if (!restoring_FPRs_inline)
     {
       bool lr = (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
-      ptr_regno = ptr_regno_for_savres (/*gpr=*/false, lr);
+      int sel = SAVRES_FPR | (lr ? SAVRES_LR : 0);
+      ptr_regno = ptr_regno_for_savres (sel);
     }
 
   insn = rs6000_emit_stack_reset (info, frame_reg_rtx, frame_off, ptr_regno);
@@ -20950,9 +21205,7 @@ rs6000_emit_epilogue (int sibcall)
 					   cfa_restores);
 
 	  sym = rs6000_savres_routine_sym (info,
-					   /*savep=*/false,
-					   /*gpr=*/false,
-					   /*lr=*/lr);
+					   SAVRES_FPR | (lr ? SAVRES_LR : 0));
 	  RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode, sym);
 	  RTVEC_ELT (p, 3) = gen_rtx_USE (VOIDmode,
 					  gen_rtx_REG (Pmode,
diff -urpN gcc-alan5/gcc/config/rs6000/rs6000.h gcc-alan6/gcc/config/rs6000/rs6000.h
--- gcc-alan5/gcc/config/rs6000/rs6000.h	2012-04-18 14:02:34.816129379 +0930
+++ gcc-alan6/gcc/config/rs6000/rs6000.h	2012-04-20 12:56:24.208754281 +0930
@@ -909,8 +909,8 @@ extern unsigned rs6000_pointer_size;
 #define TOTAL_ALTIVEC_REGS	(LAST_ALTIVEC_REGNO - FIRST_ALTIVEC_REGNO + 1)
 
 #define FIRST_SAVED_ALTIVEC_REGNO (FIRST_ALTIVEC_REGNO+20)
-#define FIRST_SAVED_FP_REGNO    (14+32)
-#define FIRST_SAVED_GP_REGNO 13
+#define FIRST_SAVED_FP_REGNO	  (14+32)
+#define FIRST_SAVED_GP_REGNO	  (FIXED_R13 ? 14 : 13)
 
 /* List the order in which to allocate registers.  Each register must be
    listed once, even those in FIXED_REGISTERS.
diff -urpN gcc-alan5/gcc/config/rs6000/sysv4.h gcc-alan6/gcc/config/rs6000/sysv4.h
--- gcc-alan5/gcc/config/rs6000/sysv4.h	2012-04-18 14:02:34.864127368 +0930
+++ gcc-alan6/gcc/config/rs6000/sysv4.h	2012-04-20 12:56:24.076759807 +0930
@@ -245,13 +245,16 @@ do {									\
 
 /* Define cutoff for using external functions to save floating point.
    When optimizing for size, use external functions when profitable.  */
-#define FP_SAVE_INLINE(FIRST_REG) (optimize_size			\
-				   ? ((FIRST_REG) == 62			\
-				      || (FIRST_REG) == 63)		\
-				   : (FIRST_REG) < 64)
+#define FP_SAVE_INLINE(FIRST_REG) ((FIRST_REG) == 62			\
+				   || (FIRST_REG) == 63			\
+				   || !optimize_size)
+
 /* And similarly for general purpose registers.  */
-#define GP_SAVE_INLINE(FIRST_REG) ((FIRST_REG) < 32	\
-				   && !optimize_size)
+#define GP_SAVE_INLINE(FIRST_REG) (!optimize_size)
+
+/* And vector registers.  */
+#define V_SAVE_INLINE(FIRST_REG) ((FIRST_REG) == LAST_ALTIVEC_REGNO	\
+				  || !optimize_size)
 
 /* Put jump tables in read-only memory, rather than in .text.  */
 #define JUMP_TABLES_IN_TEXT_SECTION 0
diff -urpN gcc-alan5/gcc/config/rs6000/altivec.md gcc-alan6/gcc/config/rs6000/altivec.md
--- gcc-alan5/gcc/config/rs6000/altivec.md	2012-04-18 01:13:00.597291534 +0930
+++ gcc-alan6/gcc/config/rs6000/altivec.md	2012-04-20 13:06:23.379624233 +0930
@@ -313,6 +313,68 @@
  "TARGET_MACHO && (DEFAULT_ABI == ABI_DARWIN) && TARGET_32BIT"
  "b %z1")
 
+;; The save_vregs and restore_vregs patterns don't use memory_operand
+;; because (plus (reg) (const_int)) is not a valid vector address.
+;; This way is more compact than describing exactly what happens in
+;; the out-of-line functions, ie. loading the constant into r11/r12
+;; then using indexed addressing, and requires less editing of rtl
+;; to describe the operation to dwarf2out_frame_debug_expr.
+(define_insn "*save_vregs_<mode>_r11"
+  [(match_parallel 0 "any_parallel_operand"
+     [(clobber (reg:P 65))
+      (use (match_operand:P 1 "symbol_ref_operand" "s"))
+      (clobber (reg:P 11))
+      (use (reg:P 0))
+      (set (mem:V4SI (plus:P (match_operand:P 2 "gpc_reg_operand" "b")
+			     (match_operand:P 3 "short_cint_operand" "I")))
+	   (match_operand:V4SI 4 "gpc_reg_operand" "v"))])]
+  ""
+  "bl %1"
+  [(set_attr "type" "branch")
+   (set_attr "length" "4")])
+
+(define_insn "*save_vregs_<mode>_r12"
+  [(match_parallel 0 "any_parallel_operand"
+     [(clobber (reg:P 65))
+      (use (match_operand:P 1 "symbol_ref_operand" "s"))
+      (clobber (reg:P 12))
+      (use (reg:P 0))
+      (set (mem:V4SI (plus:P (match_operand:P 2 "gpc_reg_operand" "b")
+			     (match_operand:P 3 "short_cint_operand" "I")))
+	   (match_operand:V4SI 4 "gpc_reg_operand" "v"))])]
+  ""
+  "bl %1"
+  [(set_attr "type" "branch")
+   (set_attr "length" "4")])
+
+(define_insn "*restore_vregs_<mode>_r11"
+  [(match_parallel 0 "any_parallel_operand"
+     [(clobber (reg:P 65))
+      (use (match_operand:P 1 "symbol_ref_operand" "s"))
+      (clobber (reg:P 11))
+      (use (reg:P 0))
+      (set (match_operand:V4SI 2 "gpc_reg_operand" "=v")
+	   (mem:V4SI (plus:P (match_operand:P 3 "gpc_reg_operand" "b")
+			     (match_operand:P 4 "short_cint_operand" "I"))))])]
+  ""
+  "bl %1"
+  [(set_attr "type" "branch")
+   (set_attr "length" "4")])
+
+(define_insn "*restore_vregs_<mode>_r12"
+  [(match_parallel 0 "any_parallel_operand"
+     [(clobber (reg:P 65))
+      (use (match_operand:P 1 "symbol_ref_operand" "s"))
+      (clobber (reg:P 12))
+      (use (reg:P 0))
+      (set (match_operand:V4SI 2 "gpc_reg_operand" "=v")
+	   (mem:V4SI (plus:P (match_operand:P 3 "gpc_reg_operand" "b")
+			     (match_operand:P 4 "short_cint_operand" "I"))))])]
+  ""
+  "bl %1"
+  [(set_attr "type" "branch")
+   (set_attr "length" "4")])
+
 ;; Simple binary operations.
 
 ;; add
diff -urpN gcc-alan5/libgcc/config/rs6000/crtrestvr.S gcc-alan6/libgcc/config/rs6000/crtrestvr.S
--- gcc-alan5/libgcc/config/rs6000/crtrestvr.S	1970-01-01 09:30:00.000000000 +0930
+++ gcc-alan6/libgcc/config/rs6000/crtrestvr.S	2012-04-20 13:09:59.642535365 +0930
@@ -0,0 +1,88 @@
+/* Routines for restoring vector registers.
+
+   Copyright (C) 2012
+   Free Software Foundation, Inc.
+   Written by Alan Modra, IBM
+
+   This file is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 3, or (at your option) any
+   later version.
+
+   This file is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* On PowerPC64 Linux, these functions are provided by the linker.  */
+#ifndef __powerpc64__
+
+#undef __ALTIVEC__
+#define __ALTIVEC__ 1
+	#include "ppc-asm.h"
+
+/* Called with r0 pointing just beyond the end of the vector save area.  */
+
+	.section ".text"
+CFI_STARTPROC
+HIDDEN_FUNC(_restvr_20)
+	li r11,-192
+	lvx v20,r11,r0
+HIDDEN_FUNC(_restvr_21)
+	li r11,-176
+	lvx v21,r11,r0
+HIDDEN_FUNC(_restvr_22)
+	li r11,-160
+	lvx v22,r11,r0
+HIDDEN_FUNC(_restvr_23)
+	li r11,-144
+	lvx v23,r11,r0
+HIDDEN_FUNC(_restvr_24)
+	li r11,-128
+	lvx v24,r11,r0
+HIDDEN_FUNC(_restvr_25)
+	li r11,-112
+	lvx v25,r11,r0
+HIDDEN_FUNC(_restvr_26)
+	li r11,-96
+	lvx v26,r11,r0
+HIDDEN_FUNC(_restvr_27)
+	li r11,-80
+	lvx v27,r11,r0
+HIDDEN_FUNC(_restvr_28)
+	li r11,-64
+	lvx v28,r11,r0
+HIDDEN_FUNC(_restvr_29)
+	li r11,-48
+	lvx v29,r11,r0
+HIDDEN_FUNC(_restvr_30)
+	li r11,-32
+	lvx v30,r11,r0
+HIDDEN_FUNC(_restvr_31)
+	li r11,-16
+	lvx v31,r11,r0
+	blr
+FUNC_END(_restvr_31)
+FUNC_END(_restvr_30)
+FUNC_END(_restvr_29)
+FUNC_END(_restvr_28)
+FUNC_END(_restvr_27)
+FUNC_END(_restvr_26)
+FUNC_END(_restvr_25)
+FUNC_END(_restvr_24)
+FUNC_END(_restvr_23)
+FUNC_END(_restvr_22)
+FUNC_END(_restvr_21)
+FUNC_END(_restvr_20)
+CFI_ENDPROC
+
+#endif
diff -urpN gcc-alan5/libgcc/config/rs6000/crtsavevr.S gcc-alan6/libgcc/config/rs6000/crtsavevr.S
--- gcc-alan5/libgcc/config/rs6000/crtsavevr.S	1970-01-01 09:30:00.000000000 +0930
+++ gcc-alan6/libgcc/config/rs6000/crtsavevr.S	2012-04-20 13:09:59.702532848 +0930
@@ -0,0 +1,88 @@
+/* Routines for saving vector registers.
+
+   Copyright (C) 2012
+   Free Software Foundation, Inc.
+   Written by Alan Modra, IBM
+
+   This file is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 3, or (at your option) any
+   later version.
+
+   This file is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* On PowerPC64 Linux, these functions are provided by the linker.  */
+#ifndef __powerpc64__
+
+#undef __ALTIVEC__
+#define __ALTIVEC__ 1
+	#include "ppc-asm.h"
+
+/* Called with r0 pointing just beyond the end of the vector save area.  */
+
+	.section ".text"
+CFI_STARTPROC
+HIDDEN_FUNC(_savevr_20)
+	li r11,-192
+	stvx v20,r11,r0
+HIDDEN_FUNC(_savevr_21)
+	li r11,-176
+	stvx v21,r11,r0
+HIDDEN_FUNC(_savevr_22)
+	li r11,-160
+	stvx v22,r11,r0
+HIDDEN_FUNC(_savevr_23)
+	li r11,-144
+	stvx v23,r11,r0
+HIDDEN_FUNC(_savevr_24)
+	li r11,-128
+	stvx v24,r11,r0
+HIDDEN_FUNC(_savevr_25)
+	li r11,-112
+	stvx v25,r11,r0
+HIDDEN_FUNC(_savevr_26)
+	li r11,-96
+	stvx v26,r11,r0
+HIDDEN_FUNC(_savevr_27)
+	li r11,-80
+	stvx v27,r11,r0
+HIDDEN_FUNC(_savevr_28)
+	li r11,-64
+	stvx v28,r11,r0
+HIDDEN_FUNC(_savevr_29)
+	li r11,-48
+	stvx v29,r11,r0
+HIDDEN_FUNC(_savevr_30)
+	li r11,-32
+	stvx v30,r11,r0
+HIDDEN_FUNC(_savevr_31)
+	li r11,-16
+	stvx v31,r11,r0
+	blr
+FUNC_END(_savevr_31)
+FUNC_END(_savevr_30)
+FUNC_END(_savevr_29)
+FUNC_END(_savevr_28)
+FUNC_END(_savevr_27)
+FUNC_END(_savevr_26)
+FUNC_END(_savevr_25)
+FUNC_END(_savevr_24)
+FUNC_END(_savevr_23)
+FUNC_END(_savevr_22)
+FUNC_END(_savevr_21)
+FUNC_END(_savevr_20)
+CFI_ENDPROC
+
+#endif
diff -urpN gcc-alan5/libgcc/config/rs6000/t-netbsd gcc-alan6/libgcc/config/rs6000/t-netbsd
--- gcc-alan5/libgcc/config/rs6000/t-netbsd	2011-11-03 12:52:56.000000000 +1030
+++ gcc-alan6/libgcc/config/rs6000/t-netbsd	2012-04-20 10:00:45.181520105 +0930
@@ -6,4 +6,6 @@ LIB2ADD_ST = \
 	$(srcdir)/config/rs6000/crtsavgpr.S \
 	$(srcdir)/config/rs6000/crtresgpr.S \
 	$(srcdir)/config/rs6000/crtresxfpr.S \
-	$(srcdir)/config/rs6000/crtresxgpr.S
+	$(srcdir)/config/rs6000/crtresxgpr.S \
+	$(srcdir)/config/rs6000/crtsavevr.S \
+	$(srcdir)/config/rs6000/crtrestvr.S
diff -urpN gcc-alan5/libgcc/config/rs6000/t-savresfgpr gcc-alan6/libgcc/config/rs6000/t-savresfgpr
--- gcc-alan5/libgcc/config/rs6000/t-savresfgpr	2011-11-30 11:11:42.000000000 +1030
+++ gcc-alan6/libgcc/config/rs6000/t-savresfgpr	2012-04-20 10:00:48.741366921 +0930
@@ -6,6 +6,8 @@ LIB2ADD_ST += \
 	   $(srcdir)/config/rs6000/crtresgpr.S \
 	   $(srcdir)/config/rs6000/crtresxfpr.S \
 	   $(srcdir)/config/rs6000/crtresxgpr.S \
+	   $(srcdir)/config/rs6000/crtsavevr.S \
+	   $(srcdir)/config/rs6000/crtrestvr.S \
 	   $(srcdir)/config/rs6000/e500crtres32gpr.S \
 	   $(srcdir)/config/rs6000/e500crtres64gpr.S \
 	   $(srcdir)/config/rs6000/e500crtres64gprctr.S \

-- 
Alan Modra
Australia Development Lab, IBM


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]