/* Save and restore call-clobbered registers which are live across a call.
Copyright (C) 1989, 1992, 1994, 1995, 1997, 1998,
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "insn-config.h"
#include "flags.h"
register because it is live we first try to save in multi-register modes.
If that is not possible the save is done one register at a time. */
-static enum machine_mode
+static enum machine_mode
regno_save_mode[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
/* For each hard register, a place on the stack where it can be saved,
if needed. */
-static rtx
+static rtx
regno_save_mem[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
/* We will only make a register eligible for caller-save if it can be
static int
reg_save_code[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
-static int
+static int
reg_restore_code[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
/* Set of hard regs currently residing in save area (during insn scan). */
static HARD_REG_SET this_insn_sets;
-static void mark_set_regs PARAMS ((rtx, rtx, void *));
-static void mark_referenced_regs PARAMS ((rtx));
-static int insert_save PARAMS ((struct insn_chain *, int, int,
- HARD_REG_SET *,
- enum machine_mode *));
-static int insert_restore PARAMS ((struct insn_chain *, int, int,
- int, enum machine_mode *));
-static struct insn_chain *insert_one_insn PARAMS ((struct insn_chain *, int,
- int, rtx));
-static void add_stored_regs PARAMS ((rtx, rtx, void *));
+static void mark_set_regs (rtx, rtx, void *);
+static void mark_referenced_regs (rtx);
+static int insert_save (struct insn_chain *, int, int, HARD_REG_SET *,
+ enum machine_mode *);
+static int insert_restore (struct insn_chain *, int, int, int,
+ enum machine_mode *);
+static struct insn_chain *insert_one_insn (struct insn_chain *, int, int,
+ rtx);
+static void add_stored_regs (rtx, rtx, void *);
\f
/* Initialize for caller-save.
Look at all the hard registers that are used by a call and for which
regclass.c has not already excluded from being used across a call.
- Ensure that we can find a mode to save the register and that there is a
+ Ensure that we can find a mode to save the register and that there is a
simple insn to save and restore the register. This latter check avoids
problems that would occur if we tried to save the MQ register of some
machines directly into memory. */
void
-init_caller_save ()
+init_caller_save (void)
{
rtx addr_reg;
int offset;
rtx address;
int i, j;
enum machine_mode mode;
+ rtx savepat, restpat;
+ rtx test_reg, test_mem;
+ rtx saveinsn, restinsn;
/* First find all the registers that we need to deal with and all
the modes that they can have. If we can't find a mode to use,
address = addr_reg;
/* Next we try to form an insn to save and restore the register. We
- see if such an insn is recognized and meets its constraints. */
+ see if such an insn is recognized and meets its constraints.
+
+ To avoid lots of unnecessary RTL allocation, we construct all the RTL
+ once, then modify the memory and register operands in-place. */
- start_sequence ();
+ test_reg = gen_rtx_REG (VOIDmode, 0);
+ test_mem = gen_rtx_MEM (VOIDmode, address);
+ savepat = gen_rtx_SET (VOIDmode, test_mem, test_reg);
+ restpat = gen_rtx_SET (VOIDmode, test_reg, test_mem);
+
+ saveinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, savepat, -1, 0, 0);
+ restinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, restpat, -1, 0, 0);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
for (mode = 0 ; mode < MAX_MACHINE_MODE; mode++)
if (HARD_REGNO_MODE_OK (i, mode))
{
- rtx mem = gen_rtx_MEM (mode, address);
- rtx reg = gen_rtx_REG (mode, i);
- rtx savepat = gen_rtx_SET (VOIDmode, mem, reg);
- rtx restpat = gen_rtx_SET (VOIDmode, reg, mem);
- rtx saveinsn = emit_insn (savepat);
- rtx restinsn = emit_insn (restpat);
int ok;
+ /* Update the register number and modes of the register
+ and memory operand. */
+ REGNO (test_reg) = i;
+ PUT_MODE (test_reg, mode);
+ PUT_MODE (test_mem, mode);
+
+ /* Force re-recognition of the modified insns. */
+ INSN_CODE (saveinsn) = -1;
+ INSN_CODE (restinsn) = -1;
+
reg_save_code[i][mode] = recog_memoized (saveinsn);
reg_restore_code[i][mode] = recog_memoized (restinsn);
reg_save_code[i][mode] = -1;
reg_restore_code[i][mode] = -1;
}
+
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
for (j = 1; j <= MOVE_MAX_WORDS; j++)
if (reg_save_code [i][regno_save_mode[i][j]] == -1)
SET_HARD_REG_BIT (call_fixed_reg_set, i);
}
}
-
- end_sequence ();
}
\f
/* Initialize save areas by showing that we haven't allocated any yet. */
void
-init_save_areas ()
+init_save_areas (void)
{
int i, j;
Future work:
In the fallback case we should iterate backwards across all possible
- modes for the save, choosing the largest available one instead of
+ modes for the save, choosing the largest available one instead of
falling back to the smallest mode immediately. (eg TF -> DF -> SF).
We do not try to use "move multiple" instructions that exist
- on some machines (such as the 68k moveml). It could be a win to try
+ on some machines (such as the 68k moveml). It could be a win to try
and use them when possible. The hard part is doing it in a way that is
- machine independent since they might be saving non-consecutive
+ machine independent since they might be saving non-consecutive
registers. (imagine caller-saving d0,d1,a0,a1 on the 68k) */
void
-setup_save_areas ()
+setup_save_areas (void)
{
int i, j, k;
unsigned int r;
if (reg_renumber[i] >= 0 && REG_N_CALLS_CROSSED (i) > 0)
{
unsigned int regno = reg_renumber[i];
- unsigned int endregno
+ unsigned int endregno
= regno + HARD_REGNO_NREGS (regno, GET_MODE (regno_reg_rtx[i]));
for (r = regno; r < endregno; r++)
/* Find the places where hard regs are live across calls and save them. */
void
-save_call_clobbered_regs ()
+save_call_clobbered_regs (void)
{
struct insn_chain *chain, *next;
enum machine_mode save_mode [FIRST_PSEUDO_REGISTER];
regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS, save_mode);
}
- if (code == CALL_INSN)
+ if (code == CALL_INSN && ! find_reg_note (insn, REG_NORETURN, NULL))
{
int regno;
HARD_REG_SET hard_regs_to_save;
regno += insert_restore (chain, GET_CODE (insn) == JUMP_INSN,
regno, MOVE_MAX_WORDS, save_mode);
}
- }
+ }
}
/* Here from note_stores when an insn stores a value in a register.
been assigned hard regs have had their register number changed already,
so we can ignore pseudos. */
static void
-mark_set_regs (reg, setter, data)
- rtx reg;
- rtx setter ATTRIBUTE_UNUSED;
- void *data ATTRIBUTE_UNUSED;
+mark_set_regs (rtx reg, rtx setter ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
{
int regno, endregno, i;
enum machine_mode mode = GET_MODE (reg);
been assigned hard regs have had their register number changed already,
so we can ignore pseudos. */
static void
-add_stored_regs (reg, setter, data)
- rtx reg;
- rtx setter;
- void *data;
+add_stored_regs (rtx reg, rtx setter, void *data)
{
int regno, endregno, i;
enum machine_mode mode = GET_MODE (reg);
/* Walk X and record all referenced registers in REFERENCED_REGS. */
static void
-mark_referenced_regs (x)
- rtx x;
+mark_referenced_regs (rtx x)
{
enum rtx_code code = GET_CODE (x);
const char *fmt;
{
x = SET_DEST (x);
code = GET_CODE (x);
- if (code == REG || code == PC || code == CC0
+ if ((code == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
+ || code == PC || code == CC0
|| (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
+ && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
/* If we're setting only part of a multi-word register,
we shall mark it as referenced, because the words
that are not being set should be restored. */
Return the extra number of registers saved. */
static int
-insert_restore (chain, before_p, regno, maxrestore, save_mode)
- struct insn_chain *chain;
- int before_p;
- int regno;
- int maxrestore;
- enum machine_mode *save_mode;
+insert_restore (struct insn_chain *chain, int before_p, int regno,
+ int maxrestore, enum machine_mode *save_mode)
{
int i, k;
rtx pat = NULL_RTX;
ok = 0;
break;
}
- /* Must do this one restore at a time */
+ /* Must do this one restore at a time. */
if (! ok)
continue;
mem = regno_save_mem [regno][numregs];
if (save_mode [regno] != VOIDmode
&& save_mode [regno] != GET_MODE (mem)
- && numregs == HARD_REGNO_NREGS (regno, save_mode [regno]))
+ && numregs == (unsigned int) HARD_REGNO_NREGS (regno, save_mode [regno]))
mem = adjust_address (mem, save_mode[regno], 0);
pat = gen_rtx_SET (VOIDmode,
- gen_rtx_REG (GET_MODE (mem),
+ gen_rtx_REG (GET_MODE (mem),
regno), mem);
code = reg_restore_code[regno][GET_MODE (mem)];
new = insert_one_insn (chain, before_p, code, pat);
n_regs_saved--;
}
-
-
- /* Tell our callers how many extra registers we saved/restored */
+ /* Tell our callers how many extra registers we saved/restored. */
return numregs - 1;
}
/* Like insert_restore above, but save registers instead. */
+
static int
-insert_save (chain, before_p, regno, to_save, save_mode)
- struct insn_chain *chain;
- int before_p;
- int regno;
- HARD_REG_SET *to_save;
- enum machine_mode *save_mode;
+insert_save (struct insn_chain *chain, int before_p, int regno,
+ HARD_REG_SET (*to_save), enum machine_mode *save_mode)
{
int i;
unsigned int k;
/* Get the pattern to emit and update our status.
- See if we can save several registers with a single instruction.
+ See if we can save several registers with a single instruction.
Work backwards to the single register case. */
for (i = MOVE_MAX_WORDS; i > 0; i--)
{
ok = 0;
break;
}
- /* Must do this one save at a time */
+ /* Must do this one save at a time. */
if (! ok)
continue;
mem = regno_save_mem [regno][numregs];
if (save_mode [regno] != VOIDmode
&& save_mode [regno] != GET_MODE (mem)
- && numregs == HARD_REGNO_NREGS (regno, save_mode [regno]))
+ && numregs == (unsigned int) HARD_REGNO_NREGS (regno, save_mode [regno]))
mem = adjust_address (mem, save_mode[regno], 0);
pat = gen_rtx_SET (VOIDmode, mem,
gen_rtx_REG (GET_MODE (mem),
n_regs_saved++;
}
- /* Tell our callers how many extra registers we saved/restored */
+ /* Tell our callers how many extra registers we saved/restored. */
return numregs - 1;
}
/* Emit a new caller-save insn and set the code. */
static struct insn_chain *
-insert_one_insn (chain, before_p, code, pat)
- struct insn_chain *chain;
- int before_p;
- int code;
- rtx pat;
+insert_one_insn (struct insn_chain *chain, int before_p, int code, rtx pat)
{
rtx insn = chain->insn;
struct insn_chain *new;
-
+
#ifdef HAVE_cc0
/* If INSN references CC0, put our insns in front of the insn that sets
CC0. This is always safe, since the only way we could be passed an
}
}
CLEAR_REG_SET (&new->dead_or_set);
- if (chain->insn == BLOCK_HEAD (chain->block))
- BLOCK_HEAD (chain->block) = new->insn;
+ if (chain->insn == BB_HEAD (BASIC_BLOCK (chain->block)))
+ BB_HEAD (BASIC_BLOCK (chain->block)) = new->insn;
}
else
{
note_stores (PATTERN (chain->insn), add_stored_regs,
&new->live_throughout);
CLEAR_REG_SET (&new->dead_or_set);
- if (chain->insn == BLOCK_END (chain->block))
- BLOCK_END (chain->block) = new->insn;
+ if (chain->insn == BB_END (BASIC_BLOCK (chain->block)))
+ BB_END (BASIC_BLOCK (chain->block)) = new->insn;
}
new->block = chain->block;
new->is_caller_save_insn = 1;