aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog30
-rw-r--r--gcc/config/mips/mips-protos.h6
-rw-r--r--gcc/config/mips/mips.c670
-rw-r--r--gcc/config/mips/mips.h2
-rw-r--r--gcc/config/mips/mips.md21
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/gcc.target/mips/save-restore-1.c19
-rw-r--r--gcc/testsuite/gcc.target/mips/save-restore-2.c14
-rw-r--r--gcc/testsuite/gcc.target/mips/save-restore-3.c19
-rw-r--r--gcc/testsuite/gcc.target/mips/save-restore-4.c11
10 files changed, 740 insertions, 59 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 585c547..99acea3 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,33 @@
+2007-07-02 Sandra Loosemore <sandra@codesourcery.com>
+ Richard Sandiford <richard@codesourcery.com>
+ Nigel Stephens <nigel@mips.com>
+
+ * config/mips/mips-protos.h (mips16e_save_restore_info): New struct.
+ (mips16e_output_save_restore): Declare.
+ (mips16e_save_restore_pattern_p): Likewise.
+ * config/mips/mips.h (GENERATE_MIPS16E_SAVE_RESTORE): New macro.
+ * config/mips/mips.c (MIPS_MAX_FIRST_STACK_STEP): Return 0x7f8
+ for GENERATE_MIPS16E_SAVE_RESTORE. Return 0x400 for TARGET_MIPS16
+ && !GENERATE_MIPS16E_SAVE_RESTORE && !TARGET_64BIT.
+ (BITSET_P): New global macro, extracted from...
+ (mips_for_each_saved_reg): ...here.
+ (mips16e_save_restore_info): New struct.
+ (mips16e_s2_s8_regs, mips16e_a0_a3_regs): New variables.
+ (mips16e_save_restore_regs): New variable.
+ (mips_split_plus, mips16e_find_first_register): New functions.
+ (mips16e_mask_registers): New function.
+ (compute_frame_size): Expand the commentary before the function.
+ Enforce the MIPS16e save and restore register range restrictions.
+ Pad the general register save area at the low end.
+ (mips16e_save_restore_reg, mips16e_build_save_restore)
+ (mips16e_save_restore_pattern_p, mips16e_add_register_range)
+ (mips16e_output_save_restore, mips16e_collect_propagate_value)
+ (mips16e_collect_argument_save, mips16e_collect_argument_saves):
+ New functions.
+ (mips_expand_prologue, mips_expand_epilogue): Handle
+ GENERATE_MIPS16E_SAVE_RESTORE.
+ * config/mips/mips.md (*mips16e_save_restore): New pattern.
+
2007-07-02 Uros Bizjak <ubizjak@gmail.com>
PR tree-optimization/31966
diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
index 94fe227..86b5f03 100644
--- a/gcc/config/mips/mips-protos.h
+++ b/gcc/config/mips/mips-protos.h
@@ -136,6 +136,8 @@ enum mips_loadgp_style {
LOADGP_RTP
};
+struct mips16e_save_restore_info;
+
extern bool mips_symbolic_constant_p (rtx, enum mips_symbol_type *);
extern int mips_regno_mode_ok_for_base_p (int, enum machine_mode, int);
extern bool mips_stack_address_p (rtx, enum machine_mode);
@@ -261,4 +263,8 @@ extern const char *current_section_name (void);
extern unsigned int current_section_flags (void);
extern bool mips_use_ins_ext_p (rtx, rtx, rtx);
+extern const char *mips16e_output_save_restore (rtx, HOST_WIDE_INT);
+extern bool mips16e_save_restore_pattern_p (rtx, HOST_WIDE_INT,
+ struct mips16e_save_restore_info *);
+
#endif /* ! GCC_MIPS_PROTOS_H */
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index a675499..575b328 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -74,15 +74,25 @@ Boston, MA 02110-1301, USA. */
((enum mips_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST))
/* The maximum distance between the top of the stack frame and the
- value $sp has when we save & restore registers.
-
- Use a maximum gap of 0x100 in the mips16 case. We can then use
- unextended instructions to save and restore registers, and to
- allocate and deallocate the top part of the frame.
-
- The value in the !mips16 case must be a SMALL_OPERAND and must
- preserve the maximum stack alignment. */
-#define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7ff0)
+ value $sp has when we save and restore registers.
+
+ The value for normal-mode code must be a SMALL_OPERAND and must
+ preserve the maximum stack alignment. We therefore use a value
+ of 0x7ff0 in this case.
+
+ MIPS16e SAVE and RESTORE instructions can adjust the stack pointer by
+ up to 0x7f8 bytes and can usually save or restore all the registers
+ that we need to save or restore. (Note that we can only use these
+ instructions for o32, for which the stack alignment is 8 bytes.)
+
+ We use a maximum gap of 0x100 or 0x400 for MIPS16 code when SAVE and
+ RESTORE are not available. We can then use unextended instructions
+ to save and restore registers, and to allocate and deallocate the top
+ part of the frame. */
+#define MIPS_MAX_FIRST_STACK_STEP \
+ (!TARGET_MIPS16 ? 0x7ff0 \
+ : GENERATE_MIPS16E_SAVE_RESTORE ? 0x7f8 \
+ : TARGET_64BIT ? 0x100 : 0x400)
/* True if INSN is a mips.md pattern or asm statement. */
#define USEFUL_INSN_P(INSN) \
@@ -112,6 +122,9 @@ Boston, MA 02110-1301, USA. */
(SUBINSN) != NEXT_INSN (SEQ_END (INSN)); \
(SUBINSN) = NEXT_INSN (SUBINSN))
+/* True if bit BIT is set in VALUE. */
+#define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
+
/* Classifies an address.
ADDRESS_REG
@@ -540,6 +553,18 @@ struct mips_integer_op {
an extra SLL at the end. */
#define MIPS_MAX_INTEGER_OPS 7
+/* Information about a MIPS16e SAVE or RESTORE instruction. */
+struct mips16e_save_restore_info {
+ /* The number of argument registers saved by a SAVE instruction.
+ 0 for RESTORE instructions. */
+ unsigned int nargs;
+
+ /* Bit X is set if the instruction saves or restores GPR X. */
+ unsigned int mask;
+
+ /* The total number of bytes to allocate. */
+ HOST_WIDE_INT size;
+};
/* Global variables for machine-dependent things. */
@@ -1113,6 +1138,21 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
},
};
+/* If a MIPS16e SAVE or RESTORE instruction saves or restores register
+ mips16e_s2_s8_regs[X], it must also save the registers in indexes
+ X + 1 onwards. Likewise mips16e_a0_a3_regs. */
+static const unsigned char mips16e_s2_s8_regs[] = {
+ 30, 23, 22, 21, 20, 19, 18
+};
+static const unsigned char mips16e_a0_a3_regs[] = {
+ 4, 5, 6, 7
+};
+
+/* A list of the registers that can be saved by the MIPS16e SAVE instruction,
+ ordered from the uppermost in memory to the lowest in memory. */
+static const unsigned char mips16e_save_restore_regs[] = {
+ 31, 30, 23, 22, 21, 20, 19, 18, 17, 16, 7, 6, 5, 4
+};
/* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT. */
#ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT
@@ -1296,6 +1336,24 @@ mips_comp_type_attributes (tree type1, tree type2)
return 1;
}
+/* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
+ and *OFFSET_PTR. Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise. */
+
+static void
+mips_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
+{
+ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ *base_ptr = XEXP (x, 0);
+ *offset_ptr = INTVAL (XEXP (x, 1));
+ }
+ else
+ {
+ *base_ptr = x;
+ *offset_ptr = 0;
+ }
+}
+
/* Return true if SYMBOL_REF X is associated with a global symbol
(in the STB_GLOBAL sense). */
@@ -6455,6 +6513,41 @@ mips_save_reg_p (unsigned int regno)
return false;
}
+/* Return the index of the lowest X in the range [0, SIZE) for which
+ bit REGS[X] is set in MASK. Return SIZE if there is no such X. */
+
+static unsigned int
+mips16e_find_first_register (unsigned int mask, const unsigned char *regs,
+ unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i++)
+ if (BITSET_P (mask, regs[i]))
+ break;
+
+ return i;
+}
+
+/* *MASK_PTR is a mask of general purpose registers and *GP_REG_SIZE_PTR
+ is the number of bytes that they occupy. If *MASK_PTR contains REGS[X]
+ for some X in [0, SIZE), adjust *MASK_PTR and *GP_REG_SIZE_PTR so that
+ the same is true for all indexes (X, SIZE). */
+
+static void
+mips16e_mask_registers (unsigned int *mask_ptr, const unsigned char *regs,
+ unsigned int size, HOST_WIDE_INT *gp_reg_size_ptr)
+{
+ unsigned int i;
+
+ i = mips16e_find_first_register (*mask_ptr, regs, size);
+ for (i++; i < size; i++)
+ if (!BITSET_P (*mask_ptr, regs[i]))
+ {
+ *gp_reg_size_ptr += GET_MODE_SIZE (gpr_mode);
+ *mask_ptr |= 1 << regs[i];
+ }
+}
/* Return the bytes needed to compute the frame pointer from the current
stack pointer. SIZE is the size (in bytes) of the local variables.
@@ -6462,10 +6555,9 @@ mips_save_reg_p (unsigned int regno)
MIPS stack frames look like:
Before call After call
- +-----------------------+ +-----------------------+
- high | | | |
- mem. | | | |
- | caller's temps. | | caller's temps. |
+ high +-----------------------+ +-----------------------+
+ mem. | | | |
+ | caller's temps. | | caller's temps. |
| | | |
+-----------------------+ +-----------------------+
| | | |
@@ -6475,37 +6567,40 @@ mips_save_reg_p (unsigned int regno)
| 4 words to save | | 4 words to save |
| arguments passed | | arguments passed |
| in registers, even | | in registers, even |
- SP->| if not passed. | VFP->| if not passed. |
- +-----------------------+ +-----------------------+
- | |
- | fp register save |
- | |
+ | if not passed. | | if not passed. |
+ SP->+-----------------------+ VFP->+-----------------------+
+ (VFP = SP+fp_sp_offset) | |\
+ | fp register save | | fp_reg_size
+ | |/
+ SP+gp_sp_offset->+-----------------------+
+ /| |\
+ | | gp register save | | gp_reg_size
+ gp_reg_rounded | | |/
+ | +-----------------------+
+ \| alignment padding |
+-----------------------+
- | |
- | gp register save |
- | |
+ | |\
+ | local variables | | var_size
+ | |/
+-----------------------+
| |
- | local variables |
+ | alloca allocations |
| |
+-----------------------+
- | |
- | alloca allocations |
- | |
+ /| |
+ cprestore_size | | GP save for V.4 abi |
+ \| |
+-----------------------+
- | |
- | GP save for V.4 abi |
- | |
- +-----------------------+
- | |
- | arguments on stack |
- | |
- +-----------------------+
- | 4 words to save |
- | arguments passed |
- | in registers, even |
- low SP->| if not passed. |
- memory +-----------------------+
+ | |\
+ | arguments on stack | |
+ | | |
+ +-----------------------+ |
+ | 4 words to save | | args_size
+ | arguments passed | |
+ | in registers, even | |
+ | if not passed. | |
+ low | (TARGET_OLDABI only) |/
+ memory SP->+-----------------------+
*/
@@ -6571,6 +6666,17 @@ compute_frame_size (HOST_WIDE_INT size)
}
}
+ /* The MIPS16e SAVE and RESTORE instructions have two ranges of registers:
+ $a3-$a0 and $s2-$s8. If we save one register in the range, we must
+ save all later registers too. */
+ if (GENERATE_MIPS16E_SAVE_RESTORE)
+ {
+ mips16e_mask_registers (&mask, mips16e_s2_s8_regs,
+ ARRAY_SIZE (mips16e_s2_s8_regs), &gp_reg_size);
+ mips16e_mask_registers (&mask, mips16e_a0_a3_regs,
+ ARRAY_SIZE (mips16e_a0_a3_regs), &gp_reg_size);
+ }
+
/* This loop must iterate over the same space as its companion in
mips_for_each_saved_reg. */
for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
@@ -6609,8 +6715,12 @@ compute_frame_size (HOST_WIDE_INT size)
{
HOST_WIDE_INT offset;
- offset = (args_size + cprestore_size + var_size
- + gp_reg_size - GET_MODE_SIZE (gpr_mode));
+ /* MIPS16e SAVE and RESTORE instructions require the GP save area
+ to be aligned at the high end with any padding at the low end,
+ so do it that way all the time. */
+ offset = (total_size
+ - MIPS_STACK_ALIGN (fp_reg_size)
+ - GET_MODE_SIZE (gpr_mode));
cfun->machine->frame.gp_sp_offset = offset;
cfun->machine->frame.gp_save_offset = offset - total_size;
}
@@ -6707,8 +6817,6 @@ mips_save_restore_reg (enum machine_mode mode, int regno,
static void
mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
{
-#define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0)
-
enum machine_mode fpr_mode;
HOST_WIDE_INT offset;
int regno;
@@ -6737,7 +6845,6 @@ mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
mips_save_restore_reg (fpr_mode, regno, offset, fn);
offset -= GET_MODE_SIZE (fpr_mode);
}
-#undef BITSET_P
}
/* If we're generating n32 or n64 abicalls, and the current function
@@ -6984,6 +7091,395 @@ mips_save_reg (rtx reg, rtx mem)
}
}
+/* Return a move between register REGNO and memory location SP + OFFSET.
+ Make the move a load if RESTORE_P, otherwise make it a frame-related
+ store. */
+
+static rtx
+mips16e_save_restore_reg (bool restore_p, HOST_WIDE_INT offset,
+ unsigned int regno)
+{
+ rtx reg, mem;
+
+ mem = gen_frame_mem (SImode, plus_constant (stack_pointer_rtx, offset));
+ reg = gen_rtx_REG (SImode, regno);
+ return (restore_p
+ ? gen_rtx_SET (VOIDmode, reg, mem)
+ : mips_frame_set (mem, reg));
+}
+
+/* Return RTL for a MIPS16e SAVE or RESTORE instruction; RESTORE_P says which.
+ The instruction must:
+
+ - Allocate or deallocate SIZE bytes in total; SIZE is known
+ to be nonzero.
+
+ - Save or restore as many registers in *MASK_PTR as possible.
+ The instruction saves the first registers at the top of the
+ allocated area, with the other registers below it.
+
+ - Save NARGS argument registers above the allocated area.
+
+ (NARGS is always zero if RESTORE_P.)
+
+ The SAVE and RESTORE instructions cannot save and restore all general
+ registers, so there may be some registers left over for the caller to
+ handle. Destructively modify *MASK_PTR so that it contains the registers
+ that still need to be saved or restored. The caller can save these
+ registers in the memory immediately below *OFFSET_PTR, which is a
+ byte offset from the bottom of the allocated stack area. */
+
+static rtx
+mips16e_build_save_restore (bool restore_p, unsigned int *mask_ptr,
+ HOST_WIDE_INT *offset_ptr, unsigned int nargs,
+ HOST_WIDE_INT size)
+{
+ rtx pattern, set;
+ HOST_WIDE_INT offset, top_offset;
+ unsigned int i, regno;
+ int n;
+
+ gcc_assert (cfun->machine->frame.fp_reg_size == 0);
+
+ /* Calculate the number of elements in the PARALLEL. We need one element
+ for the stack adjustment, one for each argument register save, and one
+ for each additional register move. */
+ n = 1 + nargs;
+ for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+ if (BITSET_P (*mask_ptr, mips16e_save_restore_regs[i]))
+ n++;
+
+ /* Create the final PARALLEL. */
+ pattern = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (n));
+ n = 0;
+
+ /* Add the stack pointer adjustment. */
+ set = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (stack_pointer_rtx,
+ restore_p ? size : -size));
+ RTX_FRAME_RELATED_P (set) = 1;
+ XVECEXP (pattern, 0, n++) = set;
+
+ /* Stack offsets in the PARALLEL are relative to the old stack pointer. */
+ top_offset = restore_p ? size : 0;
+
+ /* Save the arguments. */
+ for (i = 0; i < nargs; i++)
+ {
+ offset = top_offset + i * GET_MODE_SIZE (gpr_mode);
+ set = mips16e_save_restore_reg (restore_p, offset, GP_ARG_FIRST + i);
+ XVECEXP (pattern, 0, n++) = set;
+ }
+
+ /* Then fill in the other register moves. */
+ offset = top_offset;
+ for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+ {
+ regno = mips16e_save_restore_regs[i];
+ if (BITSET_P (*mask_ptr, regno))
+ {
+ offset -= UNITS_PER_WORD;
+ set = mips16e_save_restore_reg (restore_p, offset, regno);
+ XVECEXP (pattern, 0, n++) = set;
+ *mask_ptr &= ~(1 << regno);
+ }
+ }
+
+ /* Tell the caller what offset it should use for the remaining registers. */
+ *offset_ptr = size + (offset - top_offset) + size;
+
+ gcc_assert (n == XVECLEN (pattern, 0));
+
+ return pattern;
+}
+
+/* PATTERN is a PARALLEL whose first element adds ADJUST to the stack
+ pointer. Return true if PATTERN matches the kind of instruction
+ generated by mips16e_build_save_restore. If INFO is nonnull,
+ initialize it when returning true. */
+
+bool
+mips16e_save_restore_pattern_p (rtx pattern, HOST_WIDE_INT adjust,
+ struct mips16e_save_restore_info *info)
+{
+ unsigned int i, nargs, mask;
+ HOST_WIDE_INT top_offset, save_offset, offset, extra;
+ rtx set, reg, mem, base;
+ int n;
+
+ if (!GENERATE_MIPS16E_SAVE_RESTORE)
+ return false;
+
+ /* Stack offsets in the PARALLEL are relative to the old stack pointer. */
+ top_offset = adjust > 0 ? adjust : 0;
+
+ /* Interpret all other members of the PARALLEL. */
+ save_offset = top_offset - GET_MODE_SIZE (gpr_mode);
+ mask = 0;
+ nargs = 0;
+ i = 0;
+ for (n = 1; n < XVECLEN (pattern, 0); n++)
+ {
+ /* Check that we have a SET. */
+ set = XVECEXP (pattern, 0, n);
+ if (GET_CODE (set) != SET)
+ return false;
+
+ /* Check that the SET is a load (if restoring) or a store
+ (if saving). */
+ mem = adjust > 0 ? SET_SRC (set) : SET_DEST (set);
+ if (!MEM_P (mem))
+ return false;
+
+ /* Check that the address is the sum of the stack pointer and a
+ possibly-zero constant offset. */
+ mips_split_plus (XEXP (mem, 0), &base, &offset);
+ if (base != stack_pointer_rtx)
+ return false;
+
+ /* Check that SET's other operand is a register. */
+ reg = adjust > 0 ? SET_DEST (set) : SET_SRC (set);
+ if (!REG_P (reg))
+ return false;
+
+ /* Check for argument saves. */
+ if (offset == top_offset + nargs * GET_MODE_SIZE (gpr_mode)
+ && REGNO (reg) == GP_ARG_FIRST + nargs)
+ nargs++;
+ else if (offset == save_offset)
+ {
+ while (mips16e_save_restore_regs[i++] != REGNO (reg))
+ if (i == ARRAY_SIZE (mips16e_save_restore_regs))
+ return false;
+
+ mask |= 1 << REGNO (reg);
+ save_offset -= GET_MODE_SIZE (gpr_mode);
+ }
+ else
+ return false;
+ }
+
+ /* Check that the restrictions on register ranges are met. */
+ extra = 0;
+ mips16e_mask_registers (&mask, mips16e_s2_s8_regs,
+ ARRAY_SIZE (mips16e_s2_s8_regs), &extra);
+ mips16e_mask_registers (&mask, mips16e_a0_a3_regs,
+ ARRAY_SIZE (mips16e_a0_a3_regs), &extra);
+ if (extra != 0)
+ return false;
+
+ /* Pass back information, if requested. */
+ if (info)
+ {
+ info->nargs = nargs;
+ info->mask = mask;
+ info->size = (adjust > 0 ? adjust : -adjust);
+ }
+
+ return true;
+}
+
+/* Add a MIPS16e SAVE or RESTORE register-range argument to string S
+ for the register range [MIN_REG, MAX_REG]. Return a pointer to
+ the null terminator. */
+
+static char *
+mips16e_add_register_range (char *s, unsigned int min_reg,
+ unsigned int max_reg)
+{
+ if (min_reg != max_reg)
+ s += sprintf (s, ",%s-%s", reg_names[min_reg], reg_names[max_reg]);
+ else
+ s += sprintf (s, ",%s", reg_names[min_reg]);
+ return s;
+}
+
+/* Return the assembly instruction for a MIPS16e SAVE or RESTORE instruction.
+ PATTERN and ADJUST are as for mips16e_save_restore_pattern_p. */
+
+const char *
+mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
+{
+ static char buffer[300];
+
+ struct mips16e_save_restore_info info;
+ unsigned int i, end;
+ char *s;
+
+ /* Parse the pattern. */
+ if (!mips16e_save_restore_pattern_p (pattern, adjust, &info))
+ gcc_unreachable ();
+
+ /* Add the mnemonic. */
+ s = strcpy (buffer, adjust > 0 ? "restore\t" : "save\t");
+ s += strlen (s);
+
+ /* Save the arguments. */
+ if (info.nargs > 1)
+ s += sprintf (s, "%s-%s,", reg_names[GP_ARG_FIRST],
+ reg_names[GP_ARG_FIRST + info.nargs - 1]);
+ else if (info.nargs == 1)
+ s += sprintf (s, "%s,", reg_names[GP_ARG_FIRST]);
+
+ /* Emit the amount of stack space to allocate or deallocate. */
+ s += sprintf (s, "%d", (int) info.size);
+
+ /* Save or restore $16. */
+ if (BITSET_P (info.mask, 16))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 16]);
+
+ /* Save or restore $17. */
+ if (BITSET_P (info.mask, 17))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 17]);
+
+ /* Save or restore registers in the range $s2...$s8, which
+ mips16e_s2_s8_regs lists in decreasing order. Note that this
+ is a software register range; the hardware registers are not
+ numbered consecutively. */
+ end = ARRAY_SIZE (mips16e_s2_s8_regs);
+ i = mips16e_find_first_register (info.mask, mips16e_s2_s8_regs, end);
+ if (i < end)
+ s = mips16e_add_register_range (s, mips16e_s2_s8_regs[end - 1],
+ mips16e_s2_s8_regs[i]);
+
+ /* Save or restore registers in the range $a0...$a3. */
+ end = ARRAY_SIZE (mips16e_a0_a3_regs);
+ i = mips16e_find_first_register (info.mask, mips16e_a0_a3_regs, end);
+ if (i < end)
+ s = mips16e_add_register_range (s, mips16e_a0_a3_regs[i],
+ mips16e_a0_a3_regs[end - 1]);
+
+ /* Save or restore $31. */
+ if (BITSET_P (info.mask, 31))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
+
+ return buffer;
+}
+
+/* Return a simplified form of X using the register values in REG_VALUES.
+ REG_VALUES[R] is the last value assigned to hard register R, or null
+ if R has not been modified.
+
+ This function is rather limited, but is good enough for our purposes. */
+
+static rtx
+mips16e_collect_propagate_value (rtx x, rtx *reg_values)
+{
+ rtx x0, x1;
+
+ x = avoid_constant_pool_reference (x);
+
+ if (UNARY_P (x))
+ {
+ x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+ return simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+ x0, GET_MODE (XEXP (x, 0)));
+ }
+
+ if (ARITHMETIC_P (x))
+ {
+ x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+ x1 = mips16e_collect_propagate_value (XEXP (x, 1), reg_values);
+ return simplify_gen_binary (GET_CODE (x), GET_MODE (x), x0, x1);
+ }
+
+ if (REG_P (x)
+ && reg_values[REGNO (x)]
+ && !rtx_unstable_p (reg_values[REGNO (x)]))
+ return reg_values[REGNO (x)];
+
+ return x;
+}
+
+/* Return true if (set DEST SRC) stores an argument register into its
+ caller-allocated save slot. If the register is not included in
+ [GP_ARG_FIRST, GP_ARG_LAST + *NARGS_PTR), destructively modify
+ *NARGS_PTR such that this condition holds. REG_VALUES is as for
+ mips16e_collect_propagate_value. */
+
+static bool
+mips16e_collect_argument_save (rtx dest, rtx src, rtx *reg_values,
+ unsigned int *nargs_ptr)
+{
+ unsigned int argno, regno;
+ HOST_WIDE_INT offset, required_offset;
+ rtx addr, base;
+
+ /* Check that this is a word-mode store. */
+ if (!MEM_P (dest) || !REG_P (src) || GET_MODE (dest) != word_mode)
+ return false;
+
+ /* Check that the register being saved is an unmodified argument
+ register. */
+ regno = REGNO (src);
+ if (regno < GP_ARG_FIRST || regno > GP_ARG_LAST || reg_values[regno])
+ return false;
+ argno = regno - GP_ARG_FIRST;
+
+ /* Check whether the address is an appropriate stack pointer or
+ frame pointer access. The frame pointer is offset from the
+ stack pointer by the size of the outgoing arguments. */
+ addr = mips16e_collect_propagate_value (XEXP (dest, 0), reg_values);
+ mips_split_plus (addr, &base, &offset);
+ required_offset = cfun->machine->frame.total_size + argno * UNITS_PER_WORD;
+ if (base == hard_frame_pointer_rtx)
+ required_offset -= cfun->machine->frame.args_size;
+ else if (base != stack_pointer_rtx)
+ return false;
+ if (offset != required_offset)
+ return false;
+
+ /* Make sure that *NARGS_PTR is big enough. */
+ if (*nargs_ptr <= argno)
+ *nargs_ptr = argno + 1;
+
+ return true;
+}
+
+/* A subroutine of mips_expand_prologue, called only when generating
+ MIPS16e SAVE instructions. Search the start of the function for any
+ instructions that save argument registers into their caller-allocated
+ save slots. Delete such instructions and return a value N such that
+ saving [GP_ARG_FIRST, GP_ARG_FIRST + N) would make all the deleted
+ instructions redundant. */
+
+static unsigned int
+mips16e_collect_argument_saves (void)
+{
+ rtx reg_values[FIRST_PSEUDO_REGISTER];
+ rtx insn, next, set, dest, src;
+ unsigned int nargs;
+
+ push_topmost_sequence ();
+ nargs = 0;
+ memset (reg_values, 0, sizeof (reg_values));
+ for (insn = get_insns (); insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (NOTE_P (insn))
+ continue;
+
+ if (!INSN_P (insn))
+ break;
+
+ set = PATTERN (insn);
+ if (GET_CODE (set) != SET)
+ break;
+
+ dest = SET_DEST (set);
+ src = SET_SRC (set);
+ if (mips16e_collect_argument_save (dest, src, reg_values, &nargs))
+ delete_insn (insn);
+ else if (REG_P (dest) && GET_MODE (dest) == word_mode)
+ reg_values[REGNO (dest)]
+ = mips16e_collect_propagate_value (src, reg_values);
+ else
+ break;
+ }
+ pop_topmost_sequence ();
+
+ return nargs;
+}
/* Expand the prologue into a bunch of separate insns. */
@@ -6991,6 +7487,8 @@ void
mips_expand_prologue (void)
{
HOST_WIDE_INT size;
+ unsigned int nargs;
+ rtx insn;
if (cfun->machine->global_pointer > 0)
SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
@@ -7005,11 +7503,39 @@ mips_expand_prologue (void)
HOST_WIDE_INT step1;
step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
- RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
- stack_pointer_rtx,
- GEN_INT (-step1)))) = 1;
- size -= step1;
- mips_for_each_saved_reg (size, mips_save_reg);
+
+ if (GENERATE_MIPS16E_SAVE_RESTORE)
+ {
+ HOST_WIDE_INT offset;
+ unsigned int mask, regno;
+
+ /* Try to merge argument stores into the save instruction. */
+ nargs = mips16e_collect_argument_saves ();
+
+ /* Build the save instruction. */
+ mask = cfun->machine->frame.mask;
+ insn = mips16e_build_save_restore (false, &mask, &offset,
+ nargs, step1);
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ size -= step1;
+
+ /* Check if we need to save other registers. */
+ for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+ if (BITSET_P (mask, regno - GP_REG_FIRST))
+ {
+ offset -= GET_MODE_SIZE (gpr_mode);
+ mips_save_restore_reg (gpr_mode, regno, offset, mips_save_reg);
+ }
+ }
+ else
+ {
+ insn = gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-step1));
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ size -= step1;
+ mips_for_each_saved_reg (size, mips_save_reg);
+ }
}
/* Allocate the rest of the frame. */
@@ -7249,15 +7775,40 @@ mips_expand_epilogue (int sibcall_p)
if (TARGET_CALL_SAVED_GP && !TARGET_EXPLICIT_RELOCS)
emit_insn (gen_blockage ());
- /* Restore the registers. */
- mips_for_each_saved_reg (cfun->machine->frame.total_size - step2,
- mips_restore_reg);
+ if (GENERATE_MIPS16E_SAVE_RESTORE && cfun->machine->frame.mask != 0)
+ {
+ unsigned int regno, mask;
+ HOST_WIDE_INT offset;
+ rtx restore;
+
+ /* Generate the restore instruction. */
+ mask = cfun->machine->frame.mask;
+ restore = mips16e_build_save_restore (true, &mask, &offset, 0, step2);
+
+ /* Restore any other registers manually. */
+ for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+ if (BITSET_P (mask, regno - GP_REG_FIRST))
+ {
+ offset -= GET_MODE_SIZE (gpr_mode);
+ mips_save_restore_reg (gpr_mode, regno, offset, mips_restore_reg);
+ }
+
+ /* Restore the remaining registers and deallocate the final bit
+ of the frame. */
+ emit_insn (restore);
+ }
+ else
+ {
+ /* Restore the registers. */
+ mips_for_each_saved_reg (cfun->machine->frame.total_size - step2,
+ mips_restore_reg);
- /* Deallocate the final bit of the frame. */
- if (step2 > 0)
- emit_insn (gen_add3_insn (stack_pointer_rtx,
- stack_pointer_rtx,
- GEN_INT (step2)));
+ /* Deallocate the final bit of the frame. */
+ if (step2 > 0)
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (step2)));
+ }
/* Add in the __builtin_eh_return stack adjustment. We need to
use a temporary in mips16 code. */
@@ -7279,8 +7830,11 @@ mips_expand_epilogue (int sibcall_p)
if (!sibcall_p)
{
- /* The mips16 loads the return address into $7, not $31. */
- if (TARGET_MIPS16 && (cfun->machine->frame.mask & RA_MASK) != 0)
+ /* When generating MIPS16 code, the normal mips_for_each_saved_reg
+ path will restore the return address into $7 rather than $31. */
+ if (TARGET_MIPS16
+ && !GENERATE_MIPS16E_SAVE_RESTORE
+ && (cfun->machine->frame.mask & RA_MASK) != 0)
emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
GP_REG_FIRST + 7)));
else
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index ffd9130..809c86c 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -209,6 +209,8 @@ extern const struct mips_rtx_cost_data *mips_cost;
#define TARGET_MIPS16 ((target_flags & MASK_MIPS16) != 0)
/* Generate mips16e code. Default 16bit ASE for mips32/mips32r2/mips64 */
#define GENERATE_MIPS16E (TARGET_MIPS16 && mips_isa >= 32)
+/* Generate mips16e register save/restore sequences. */
+#define GENERATE_MIPS16E_SAVE_RESTORE (GENERATE_MIPS16E && mips_abi == ABI_32)
/* Generic ISA defines. */
#define ISA_MIPS1 (mips_isa == 1)
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 00b2489..8a72ae5 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -5583,7 +5583,26 @@
"reload_completed"
[(match_dup 0)]
{ operands[0] = mips_rewrite_small_data (operands[0]); })
-
+
+;;
+;; ....................
+;;
+;; MIPS16e Save/Restore
+;;
+;; ....................
+;;
+
+(define_insn "*mips16e_save_restore"
+ [(match_parallel 0 ""
+ [(set (match_operand:SI 1 "register_operand")
+ (plus:SI (match_dup 1)
+ (match_operand:SI 2 "const_int_operand")))])]
+ "operands[1] == stack_pointer_rtx
+ && mips16e_save_restore_pattern_p (operands[0], INTVAL (operands[2]), NULL)"
+ { return mips16e_output_save_restore (operands[0], INTVAL (operands[2])); }
+ [(set_attr "type" "arith")
+ (set_attr "extended_mips16" "yes")])
+
; Thread-Local Storage
; The TLS base pointer is accessed via "rdhwr $v1, $29". No current
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 496a40f..4eec59e 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2007-07-02 Richard Sandiford <richard@codesourcery.com>
+
+ * gcc.target/mips/save-restore-1.c: New test.
+ * gcc.target/mips/save-restore-2.c: Likewise.
+ * gcc.target/mips/save-restore-3.c: Likewise.
+ * gcc.target/mips/save-restore-4.c: Likewise.
+
2007-07-02 Uros Bizjak <ubizjak@gmail.com>
PR tree-optimization/31966
diff --git a/gcc/testsuite/gcc.target/mips/save-restore-1.c b/gcc/testsuite/gcc.target/mips/save-restore-1.c
new file mode 100644
index 0000000..95689b5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/save-restore-1.c
@@ -0,0 +1,19 @@
+/* Check that we can use the save instruction to save varargs. */
+/* { dg-mips-options "-mips32r2 -mgp32 -mips16 -O2" } */
+#include <stdarg.h>
+
+int bar (int, va_list ap);
+
+int
+foo (int n, ...)
+{
+ va_list ap;
+ int i;
+
+ va_start (ap, n);
+ i = bar (n, ap);
+ va_end (ap);
+ return i + 1;
+}
+/* { dg-final { scan-assembler "\tsave\t\\\$4-\\\$7" } } */
+/* { dg-final { scan-assembler "\trestore\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/save-restore-2.c b/gcc/testsuite/gcc.target/mips/save-restore-2.c
new file mode 100644
index 0000000..9a73843
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/save-restore-2.c
@@ -0,0 +1,14 @@
+/* Check that we can use the save instruction to save spilled arguments. */
+/* { dg-mips-options "-mips32r2 -mgp32 -mips16 -O2" } */
+void
+foo (int *a, int b, int c)
+{
+ asm volatile ("" ::: "$2", "$3", "$4", "$5", "$6", "$7", "$8",
+ "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$16",
+ "$17", "$18", "$19", "$20", "$21", "$22", "$23", "$24",
+ "$25", "$30", "memory");
+ a[b] = 1;
+ a[c] = 1;
+}
+/* { dg-final { scan-assembler "\tsave\t\\\$4-\\\$6," } } */
+/* { dg-final { scan-assembler "\trestore\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/save-restore-3.c b/gcc/testsuite/gcc.target/mips/save-restore-3.c
new file mode 100644
index 0000000..4221236
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/save-restore-3.c
@@ -0,0 +1,19 @@
+/* Check that we can use the save instruction to save spilled arguments
+ when the argument save area is out of range of a direct load or store. */
+/* { dg-mips-options "-mips32r2 -mgp32 -mips16 -O2" } */
+void bar (int *);
+
+void
+foo (int *a, int b, int c)
+{
+ int x[0x4000];
+ asm volatile ("" ::: "$2", "$3", "$4", "$5", "$6", "$7", "$8",
+ "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$16",
+ "$17", "$18", "$19", "$20", "$21", "$22", "$23", "$24",
+ "$25", "$30", "memory");
+ bar (x);
+ a[b] = 1;
+ a[c] = 1;
+}
+/* { dg-final { scan-assembler "\tsave\t\\\$4-\\\$6," } } */
+/* { dg-final { scan-assembler "\trestore\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/save-restore-4.c b/gcc/testsuite/gcc.target/mips/save-restore-4.c
new file mode 100644
index 0000000..10bf141
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/save-restore-4.c
@@ -0,0 +1,11 @@
+/* Check that we can use the save instruction to save $16, $17 and $31. */
+/* { dg-mips-options "-mips32r2 -mgp32 -mips16 -O2" } */
+void bar (void);
+void
+foo (void)
+{
+ bar ();
+ asm volatile ("" ::: "$16", "$17");
+}
+/* { dg-final { scan-assembler "\tsave\t\[0-9\]*,\\\$16,\\\$17,\\\$31" } } */
+/* { dg-final { scan-assembler "\trestore\t\[0-9\]*,\\\$16,\\\$17,\\\$31" } } */