/* Save and restore call-clobbered registers which are live across a call.
- Copyright (C) 1989, 1992, 94-95, 1997, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1989, 1992, 1994, 1995, 1997, 1998,
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC 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 2, or (at your option)
-any later version.
+GCC 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 2, or (at your option) any later
+version.
-GNU CC 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.
+GCC 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.
You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+along with GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "insn-config.h"
#include "flags.h"
#include "recog.h"
#include "basic-block.h"
#include "reload.h"
+#include "function.h"
#include "expr.h"
#include "toplev.h"
+#include "tm_p.h"
#ifndef MAX_MOVE_MAX
#define MAX_MOVE_MAX MOVE_MAX
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
when we emit them, the addresses might not be valid, so they might not
be recognized. */
-static enum insn_code
- reg_save_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
-static enum insn_code
- reg_restore_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
+static int
+ reg_save_code[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
+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 PROTO((rtx, rtx));
-static void mark_referenced_regs PROTO((rtx));
-static int insert_save PROTO((struct insn_chain *, int, int,
- HARD_REG_SET *));
-static int insert_restore PROTO((struct insn_chain *, int, int,
- int));
-static void insert_one_insn PROTO((struct insn_chain *, int,
- enum insn_code, rtx));
+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)
{
- char *first_obj = (char *) oballoc (0);
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,
{
for (j = 1; j <= MOVE_MAX_WORDS; j++)
{
- regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j);
+ regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j,
+ VOIDmode);
if (regno_save_mode[i][j] == VOIDmode && j == 1)
{
call_fixed_regs[i] = 1;
that register in every mode we will use to save registers. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (TEST_HARD_REG_BIT (reg_class_contents[(int) BASE_REG_CLASS], i))
+ if (TEST_HARD_REG_BIT
+ (reg_class_contents
+ [(int) MODE_BASE_REG_CLASS (regno_save_mode [i][1])], i))
break;
if (i == FIRST_PSEUDO_REGISTER)
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 (j = 1; j <= MOVE_MAX_WORDS; j++)
- if (regno_save_mode[i][j] != VOIDmode)
+ for (mode = 0 ; mode < MAX_MACHINE_MODE; mode++)
+ if (HARD_REGNO_MODE_OK (i, mode))
{
- rtx mem = gen_rtx_MEM (regno_save_mode[i][j], address);
- rtx reg = gen_rtx_REG (regno_save_mode[i][j], 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;
- reg_save_code[i][j] = recog_memoized (saveinsn);
- reg_restore_code[i][j] = recog_memoized (restinsn);
+ /* 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);
/* Now extract both insns and see if we can meet their
constraints. */
- ok = (reg_save_code[i][j] != (enum insn_code)-1
- && reg_restore_code[i][j] != (enum insn_code)-1);
+ ok = (reg_save_code[i][mode] != -1
+ && reg_restore_code[i][mode] != -1);
if (ok)
{
- insn_extract (saveinsn);
- ok = constrain_operands (reg_save_code[i][j], 1);
- insn_extract (restinsn);
- ok &= constrain_operands (reg_restore_code[i][j], 1);
+ extract_insn (saveinsn);
+ ok = constrain_operands (1);
+ extract_insn (restinsn);
+ ok &= constrain_operands (1);
}
if (! ok)
{
- regno_save_mode[i][j] = VOIDmode;
- if (j == 1)
- {
- call_fixed_regs[i] = 1;
- SET_HARD_REG_BIT (call_fixed_reg_set, i);
- }
+ reg_save_code[i][mode] = -1;
+ reg_restore_code[i][mode] = -1;
}
- }
-
- end_sequence ();
+ }
+ else
+ {
+ reg_save_code[i][mode] = -1;
+ reg_restore_code[i][mode] = -1;
+ }
- obfree (first_obj);
+ 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)
+ {
+ regno_save_mode[i][j] = VOIDmode;
+ if (j == 1)
+ {
+ call_fixed_regs[i] = 1;
+ SET_HARD_REG_BIT (call_fixed_reg_set, i);
+ }
+ }
}
\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;
HARD_REG_SET hard_regs_used;
/* Allocate space in the save area for the largest multi-register
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
if (reg_renumber[i] >= 0 && REG_N_CALLS_CROSSED (i) > 0)
{
- int regno = reg_renumber[i];
- int endregno
+ unsigned int regno = reg_renumber[i];
+ unsigned int endregno
= regno + HARD_REGNO_NREGS (regno, GET_MODE (regno_reg_rtx[i]));
- int nregs = endregno - regno;
- for (j = 0; j < nregs; j++)
- {
- if (call_used_regs[regno+j])
- SET_HARD_REG_BIT (hard_regs_used, regno+j);
- }
+ for (r = regno; r < endregno; r++)
+ if (call_used_regs[r])
+ SET_HARD_REG_BIT (hard_regs_used, r);
}
/* Now run through all the call-used hard-registers and allocate
/* Setup single word save area just in case... */
for (k = 0; k < j; k++)
- {
- /* This should not depend on WORDS_BIG_ENDIAN.
- The order of words in regs is the same as in memory. */
- rtx temp = gen_rtx_MEM (regno_save_mode[i+k][1],
- XEXP (regno_save_mem[i][j], 0));
-
- regno_save_mem[i+k][1]
- = adj_offsettable_operand (temp, k * UNITS_PER_WORD);
- }
+ /* This should not depend on WORDS_BIG_ENDIAN.
+ The order of words in regs is the same as in memory. */
+ regno_save_mem[i + k][1]
+ = adjust_address_nv (regno_save_mem[i][j],
+ regno_save_mode[i + k][1],
+ k * UNITS_PER_WORD);
}
+
+ /* Now loop again and set the alias set of any save areas we made to
+ the alias set used to represent frame objects. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (j = MOVE_MAX_WORDS; j > 0; j--)
+ if (regno_save_mem[i][j] != 0)
+ set_mem_alias_set (regno_save_mem[i][j], get_frame_alias_set ());
}
\f
/* 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];
CLEAR_HARD_REG_SET (hard_regs_saved);
n_regs_saved = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (referenced_regs, regno))
- regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS);
+ regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS, save_mode);
}
if (code == CALL_INSN)
{
- rtx x;
- int regno, nregs;
+ int regno;
HARD_REG_SET hard_regs_to_save;
/* Use the register life information in CHAIN to compute which
- regs are live before the call. */
- REG_SET_TO_HARD_REG_SET (hard_regs_to_save, chain->live_before);
- compute_use_by_pseudos (&hard_regs_to_save, chain->live_before);
+ regs are live during the call. */
+ REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
+ &chain->live_throughout);
+ /* Save hard registers always in the widest mode available. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+ save_mode [regno] = regno_save_mode [regno][1];
+ else
+ save_mode [regno] = VOIDmode;
+
+ /* Look through all live pseudos, mark their hard registers
+ and choose proper mode for saving. */
+ EXECUTE_IF_SET_IN_REG_SET
+ (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno,
+ {
+ int r = reg_renumber[regno];
+ int nregs;
+
+ if (r >= 0)
+ {
+ enum machine_mode mode;
+
+ nregs = HARD_REGNO_NREGS (r, PSEUDO_REGNO_MODE (regno));
+ mode = HARD_REGNO_CALLER_SAVE_MODE
+ (r, nregs, PSEUDO_REGNO_MODE (regno));
+ if (GET_MODE_BITSIZE (mode)
+ > GET_MODE_BITSIZE (save_mode[r]))
+ save_mode[r] = mode;
+ while (nregs-- > 0)
+ SET_HARD_REG_BIT (hard_regs_to_save, r + nregs);
+ }
+ else
+ abort ();
+ });
/* Record all registers set in this call insn. These don't need
- to be saved. */
+ to be saved. N.B. the call insn might set a subreg of a
+ multi-hard-reg pseudo; then the pseudo is considered live
+ during the call, but the subreg that is set isn't. */
CLEAR_HARD_REG_SET (this_insn_sets);
- note_stores (PATTERN (insn), mark_set_regs);
+ note_stores (PATTERN (insn), mark_set_regs, NULL);
/* Compute which hard regs must be saved before this call. */
AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set);
AND_COMPL_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
AND_HARD_REG_SET (hard_regs_to_save, call_used_reg_set);
- /* Registers used for function parameters need not be saved. */
- for (x = CALL_INSN_FUNCTION_USAGE (insn); x != 0;
- x = XEXP (x, 1))
- {
- rtx y;
-
- if (GET_CODE (XEXP (x, 0)) != USE)
- continue;
- y = XEXP (XEXP (x, 0), 0);
- if (GET_CODE (y) != REG)
- abort ();
- regno = REGNO (y);
- if (REGNO (y) >= FIRST_PSEUDO_REGISTER)
- abort ();
- nregs = HARD_REGNO_NREGS (regno, GET_MODE (y));
- while (nregs-- > 0)
- CLEAR_HARD_REG_BIT (hard_regs_to_save, regno + nregs);
- }
-
- /* Neither do registers for which we find a death note. */
- for (x = REG_NOTES (insn); x != 0; x = XEXP (x, 1))
- {
- rtx y = XEXP (x, 0);
-
- if (REG_NOTE_KIND (x) != REG_DEAD)
- continue;
- if (GET_CODE (y) != REG)
- abort ();
- regno = REGNO (y);
-
- if (regno >= FIRST_PSEUDO_REGISTER)
- regno = reg_renumber[regno];
- if (regno < 0)
- continue;
- nregs = HARD_REGNO_NREGS (regno, GET_MODE (y));
- while (nregs-- > 0)
- CLEAR_HARD_REG_BIT (hard_regs_to_save, regno + nregs);
- }
-
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
- regno += insert_save (chain, 1, regno, &hard_regs_to_save);
+ regno += insert_save (chain, 1, regno, &hard_regs_to_save, save_mode);
/* Must recompute n_regs_saved. */
n_regs_saved = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (hard_regs_saved, regno))
regno += insert_restore (chain, GET_CODE (insn) == JUMP_INSN,
- regno, MOVE_MAX_WORDS);
+ 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)
- rtx reg;
- rtx setter ATTRIBUTE_UNUSED;
+mark_set_regs (rtx reg, rtx setter ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
{
- register int regno, endregno, i;
+ int regno, endregno, i;
enum machine_mode mode = GET_MODE (reg);
- int word = 0;
if (GET_CODE (reg) == SUBREG)
{
- word = SUBREG_WORD (reg);
+ rtx inner = SUBREG_REG (reg);
+ if (GET_CODE (inner) != REG || REGNO (inner) >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ regno = subreg_hard_regno (reg, 1);
+ }
+ else if (GET_CODE (reg) == REG
+ && REGNO (reg) < FIRST_PSEUDO_REGISTER)
+ regno = REGNO (reg);
+ else
+ return;
+
+ endregno = regno + HARD_REGNO_NREGS (regno, mode);
+
+ for (i = regno; i < endregno; i++)
+ SET_HARD_REG_BIT (this_insn_sets, i);
+}
+
+/* Here from note_stores when an insn stores a value in a register.
+ Set the proper bit or bits in the passed regset. All pseudos that have
+ been assigned hard regs have had their register number changed already,
+ so we can ignore pseudos. */
+static void
+add_stored_regs (rtx reg, rtx setter, void *data)
+{
+ int regno, endregno, i;
+ enum machine_mode mode = GET_MODE (reg);
+ int offset = 0;
+
+ if (GET_CODE (setter) == CLOBBER)
+ return;
+
+ if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
+ {
+ offset = subreg_regno_offset (REGNO (SUBREG_REG (reg)),
+ GET_MODE (SUBREG_REG (reg)),
+ SUBREG_BYTE (reg),
+ GET_MODE (reg));
reg = SUBREG_REG (reg);
}
if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
return;
- regno = REGNO (reg) + word;
+ regno = REGNO (reg) + offset;
endregno = regno + HARD_REGNO_NREGS (regno, mode);
for (i = regno; i < endregno; i++)
- SET_HARD_REG_BIT (this_insn_sets, i);
+ SET_REGNO_REG_SET ((regset) data, i);
}
/* 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);
- char *fmt;
+ const char *fmt;
int i, j;
if (code == SET)
x = SET_DEST (x);
code = GET_CODE (x);
if (code == REG || code == PC || code == CC0
- || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG))
+ || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
+ /* 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. */
+ && ((GET_MODE_SIZE (GET_MODE (x))
+ >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ || (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+ <= UNITS_PER_WORD))))
return;
}
if (code == MEM || code == SUBREG)
Return the extra number of registers saved. */
static int
-insert_restore (chain, before_p, regno, maxrestore)
- struct insn_chain *chain;
- int before_p;
- int regno;
- int maxrestore;
+insert_restore (struct insn_chain *chain, int before_p, int regno,
+ int maxrestore, enum machine_mode *save_mode)
{
- int i;
+ int i, k;
rtx pat = NULL_RTX;
- enum insn_code code = CODE_FOR_nothing;
- int numregs = 0;
+ int code;
+ unsigned int numregs = 0;
+ struct insn_chain *new;
+ rtx mem;
/* A common failure mode if register status is not correct in the RTL
is for this routine to be called with a REGNO we didn't expect to
backwards to the single register case. */
for (i = maxrestore; i > 0; i--)
{
- int j, k;
+ int j;
int ok = 1;
if (regno_save_mem[regno][i] == 0)
ok = 0;
break;
}
- /* Must do this one restore at a time */
+ /* Must do this one restore at a time. */
if (! ok)
continue;
- pat = gen_rtx_SET (VOIDmode,
- gen_rtx_REG (GET_MODE (regno_save_mem[regno][i]),
- regno),
- regno_save_mem[regno][i]);
- code = reg_restore_code[regno][i];
-
- /* Clear status for all registers we restored. */
- for (k = 0; k < i; k++)
- {
- CLEAR_HARD_REG_BIT (hard_regs_saved, regno + k);
- n_regs_saved--;
- }
-
numregs = i;
break;
}
- insert_one_insn (chain, before_p, code, pat);
+ mem = regno_save_mem [regno][numregs];
+ if (save_mode [regno] != VOIDmode
+ && save_mode [regno] != GET_MODE (mem)
+ && 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),
+ regno), mem);
+ code = reg_restore_code[regno][GET_MODE (mem)];
+ new = insert_one_insn (chain, before_p, code, pat);
+
+ /* Clear status for all registers we restored. */
+ for (k = 0; k < i; k++)
+ {
+ CLEAR_HARD_REG_BIT (hard_regs_saved, regno + k);
+ SET_REGNO_REG_SET (&new->dead_or_set, regno + k);
+ n_regs_saved--;
+ }
/* 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)
- struct insn_chain *chain;
- int before_p;
- int regno;
- HARD_REG_SET *to_save;
+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;
rtx pat = NULL_RTX;
- enum insn_code code = CODE_FOR_nothing;
- int numregs = 0;
+ int code;
+ unsigned int numregs = 0;
+ struct insn_chain *new;
+ rtx mem;
/* A common failure mode if register status is not correct in the RTL
is for this routine to be called with a REGNO we didn't expect to
/* 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--)
{
- int j, k;
+ int j;
int ok = 1;
if (regno_save_mem[regno][i] == 0)
continue;
ok = 0;
break;
}
- /* Must do this one save at a time */
+ /* Must do this one save at a time. */
if (! ok)
continue;
- pat = gen_rtx_SET (VOIDmode, regno_save_mem[regno][i],
- gen_rtx_REG (GET_MODE (regno_save_mem[regno][i]),
- regno));
- code = reg_save_code[regno][i];
-
- /* Set hard_regs_saved for all the registers we saved. */
- for (k = 0; k < i; k++)
- {
- SET_HARD_REG_BIT (hard_regs_saved, regno + k);
- n_regs_saved++;
- }
-
numregs = i;
break;
}
- insert_one_insn (chain, before_p, code, pat);
+ mem = regno_save_mem [regno][numregs];
+ if (save_mode [regno] != VOIDmode
+ && save_mode [regno] != GET_MODE (mem)
+ && 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),
+ regno));
+ code = reg_save_code[regno][GET_MODE (mem)];
+ new = insert_one_insn (chain, before_p, code, pat);
+
+ /* Set hard_regs_saved and dead_or_set for all the registers we saved. */
+ for (k = 0; k < numregs; k++)
+ {
+ SET_HARD_REG_BIT (hard_regs_saved, regno + k);
+ SET_REGNO_REG_SET (&new->dead_or_set, regno + k);
+ n_regs_saved++;
+ }
/* Tell our callers how many extra registers we saved/restored */
return numregs - 1;
}
/* Emit a new caller-save insn and set the code. */
-static void
-insert_one_insn (chain, before_p, code, pat)
- struct insn_chain *chain;
- int before_p;
- enum insn_code code;
- rtx pat;
+static struct insn_chain *
+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
new = new_insn_chain ();
if (before_p)
{
+ rtx link;
+
new->prev = chain->prev;
if (new->prev != 0)
new->prev->next = new;
chain->prev = new;
new->next = chain;
new->insn = emit_insn_before (pat, insn);
- if (chain->insn == basic_block_head[chain->block])
- basic_block_head[chain->block] = new->insn;
+ /* ??? It would be nice if we could exclude the already / still saved
+ registers from the live sets. */
+ COPY_REG_SET (&new->live_throughout, &chain->live_throughout);
+ /* Registers that die in CHAIN->INSN still live in the new insn. */
+ for (link = REG_NOTES (chain->insn); link; link = XEXP (link, 1))
+ {
+ if (REG_NOTE_KIND (link) == REG_DEAD)
+ {
+ rtx reg = XEXP (link, 0);
+ int regno, i;
+
+ if (GET_CODE (reg) != REG)
+ abort ();
+
+ regno = REGNO (reg);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ regno = reg_renumber[regno];
+ if (regno < 0)
+ continue;
+ for (i = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1;
+ i >= 0; i--)
+ SET_REGNO_REG_SET (&new->live_throughout, regno + i);
+ }
+ }
+ CLEAR_REG_SET (&new->dead_or_set);
+ if (chain->insn == BLOCK_HEAD (chain->block))
+ BLOCK_HEAD (chain->block) = new->insn;
}
else
{
chain->next = new;
new->prev = chain;
new->insn = emit_insn_after (pat, insn);
- if (chain->insn == basic_block_end[chain->block])
- basic_block_end[chain->block] = new->insn;
+ /* ??? It would be nice if we could exclude the already / still saved
+ registers from the live sets, and observe REG_UNUSED notes. */
+ COPY_REG_SET (&new->live_throughout, &chain->live_throughout);
+ /* Registers that are set in CHAIN->INSN live in the new insn.
+ (Unless there is a REG_UNUSED note for them, but we don't
+ look for them here.) */
+ 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;
}
new->block = chain->block;
new->is_caller_save_insn = 1;
INSN_CODE (new->insn) = code;
+ return new;
}