mips-protos.h (mips16e_save_restore_info): New struct.

gcc/
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.

gcc/testsuite/
	* 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.

From-SVN: r126207
This commit is contained in:
Richard Sandiford 2007-07-02 14:37:15 +00:00
parent 8ad0217501
commit e12605764d
10 changed files with 738 additions and 57 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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.
value $sp has when we save and 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 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.
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)
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;
/* Deallocate the final bit of the frame. */
if (step2 > 0)
emit_insn (gen_add3_insn (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (step2)));
/* 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)));
}
/* 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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */

View File

@ -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" } } */