summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog34
-rw-r--r--gcc/config/rs6000/altivec.md62
-rw-r--r--gcc/config/rs6000/rs6000.c656
-rw-r--r--gcc/config/rs6000/rs6000.h4
-rw-r--r--gcc/config/rs6000/sysv4.h15
-rw-r--r--libgcc/ChangeLog7
-rw-r--r--libgcc/config/rs6000/crtrestvr.S88
-rw-r--r--libgcc/config/rs6000/crtsavevr.S88
-rw-r--r--libgcc/config/rs6000/t-netbsd4
-rw-r--r--libgcc/config/rs6000/t-savresfgpr2
10 files changed, 749 insertions, 211 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 8926b3f25f6..4daecb4776e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,39 @@
2012-04-25 Alan Modra <amodra@gmail.com>
+ * 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.
+
+2012-04-25 Alan Modra <amodra@gmail.com>
+
* config/rs6000/rs6000.c (rs6000_savres_strategy): Allow
out-of-line save/restore for large frames. Don't disable
out-of-line saves on ABI_AIX when using static chain reg.
diff --git a/gcc/config/rs6000/altivec.md b/gcc/config/rs6000/altivec.md
index 54ca3694133..42e8dd6a9d4 100644
--- a/gcc/config/rs6000/altivec.md
+++ b/gcc/config/rs6000/altivec.md
@@ -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 --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 31c671188f8..eccbbdfc45a 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -937,7 +937,6 @@ static bool legitimate_small_data_p (enum machine_mode, rtx);
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_attribute (tree *, tree, tree, int, bool *);
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 *xyes)
}
+/* 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 *info,
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 *info,
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 *info,
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 mode, rtx reg, int offset)
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_t *info, int regno,
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_t *info, int regno,
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_t *info, int regno,
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_t *info, int regno,
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_t *info, bool savep,
{
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 *info,
}
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 *info,
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 *info,
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 *info,
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);
@@ -19560,7 +19632,6 @@ rs6000_emit_prologue (void)
&& !(strategy & (SAVE_INLINE_GPRS
| SAVE_NOINLINE_GPRS_SAVES_LR))
? 11 : 12);
- cr_save_regno = DEFAULT_ABI == ABI_AIX && !saving_GPRs_inline ? 11 : 12;
if (!WORLD_SAVE_P (info)
&& info->cr_save_p
&& REGNO (frame_reg_rtx) != cr_save_regno
@@ -19585,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++)
@@ -19601,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);
@@ -19635,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)
{
@@ -19649,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;
@@ -19674,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))
@@ -19688,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);
@@ -19709,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;
@@ -19741,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;
@@ -19908,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);
@@ -19940,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))
{
@@ -19987,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));
@@ -20105,21 +20234,25 @@ rs6000_output_function_prologue (FILE *file,
/* 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);
}
}
@@ -20398,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 & REST_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;
@@ -20409,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 & REST_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 & REST_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);
}
}
@@ -20541,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 & REST_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 & REST_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);
}
}
@@ -20599,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);
}
@@ -20705,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)
{
@@ -20714,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;
@@ -20734,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)
{
@@ -20873,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);
@@ -20951,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 --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index 0676915cdd2..561c623d44f 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -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 --git a/gcc/config/rs6000/sysv4.h b/gcc/config/rs6000/sysv4.h
index aed2a29f5d3..8737bb14ee0 100644
--- a/gcc/config/rs6000/sysv4.h
+++ b/gcc/config/rs6000/sysv4.h
@@ -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 --git a/libgcc/ChangeLog b/libgcc/ChangeLog
index 638994aa3bc..831fa12203a 100644
--- a/libgcc/ChangeLog
+++ b/libgcc/ChangeLog
@@ -1,3 +1,10 @@
+2012-04-25 Alan Modra <amodra@gmail.com>
+
+ * config/rs6000/crtsavevr.S: New file.
+ * config/rs6000/crtrestvr.S: New file.
+ * config/rs6000/t-savresfgpr: Build the above.
+ * config/rs6000/t-netbsd: Likewise.
+
2012-04-24 Sriraman Tallam <tmsriram@google.com>
* libgcc/config/i386/i386-cpuinfo.c: Set __cpu_vendor always.
diff --git a/libgcc/config/rs6000/crtrestvr.S b/libgcc/config/rs6000/crtrestvr.S
new file mode 100644
index 00000000000..21c4ed08931
--- /dev/null
+++ b/libgcc/config/rs6000/crtrestvr.S
@@ -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 --git a/libgcc/config/rs6000/crtsavevr.S b/libgcc/config/rs6000/crtsavevr.S
new file mode 100644
index 00000000000..072c7b8eb9a
--- /dev/null
+++ b/libgcc/config/rs6000/crtsavevr.S
@@ -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 --git a/libgcc/config/rs6000/t-netbsd b/libgcc/config/rs6000/t-netbsd
index 3b4ba32a215..7be8e5e6700 100644
--- a/libgcc/config/rs6000/t-netbsd
+++ b/libgcc/config/rs6000/t-netbsd
@@ -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 --git a/libgcc/config/rs6000/t-savresfgpr b/libgcc/config/rs6000/t-savresfgpr
index e2a951abd3f..a8455ae1af9 100644
--- a/libgcc/config/rs6000/t-savresfgpr
+++ b/libgcc/config/rs6000/t-savresfgpr
@@ -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 \