/* 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 Free Software Foundation, Inc.
This file is part of GNU CC.
#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
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 PARAMS ((rtx, rtx, void *));
+static void mark_referenced_regs PARAMS ((rtx));
+static int insert_save PARAMS ((struct insn_chain *, int, int,
+ HARD_REG_SET *));
+static int insert_restore PARAMS ((struct insn_chain *, int, int,
+ int));
+static struct insn_chain *insert_one_insn PARAMS ((struct insn_chain *, int,
+ enum insn_code, rtx));
+static void add_stored_regs PARAMS ((rtx, rtx, void *));
\f
/* Initialize for caller-save.
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);
+ compute_use_by_pseudos (&hard_regs_to_save,
+ &chain->live_throughout);
/* 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);
been assigned hard regs have had their register number changed already,
so we can ignore pseudos. */
static void
-mark_set_regs (reg, setter)
+mark_set_regs (reg, setter, data)
rtx reg;
rtx setter ATTRIBUTE_UNUSED;
+ void *data ATTRIBUTE_UNUSED;
{
register int regno, endregno, i;
enum machine_mode mode = GET_MODE (reg);
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 (reg, setter, data)
+ rtx reg;
+ rtx setter;
+ void *data;
+{
+ register int regno, endregno, i;
+ enum machine_mode mode = GET_MODE (reg);
+ int word = 0;
+
+ if (GET_CODE (setter) == CLOBBER)
+ return;
+
+ while (GET_CODE (reg) == SUBREG)
+ {
+ word += SUBREG_WORD (reg);
+ reg = SUBREG_REG (reg);
+ }
+
+ if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ regno = REGNO (reg) + word;
+ endregno = regno + HARD_REGNO_NREGS (regno, mode);
+
+ for (i = regno; i < endregno; 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;
{
enum rtx_code code = GET_CODE (x);
- char *fmt;
+ const char *fmt;
int i, j;
if (code == SET)
int regno;
int maxrestore;
{
- int i;
+ int i, k;
rtx pat = NULL_RTX;
enum insn_code code = CODE_FOR_nothing;
int numregs = 0;
+ struct insn_chain *new;
/* 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)
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);
+ pat = gen_rtx_SET (VOIDmode,
+ gen_rtx_REG (GET_MODE (regno_save_mem[regno][numregs]),
+ regno),
+ regno_save_mem[regno][numregs]);
+ code = reg_restore_code[regno][numregs];
+ 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;
int regno;
HARD_REG_SET *to_save;
{
- int i;
+ int i, k;
rtx pat = NULL_RTX;
enum insn_code code = CODE_FOR_nothing;
int numregs = 0;
+ struct insn_chain *new;
/* 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
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;
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);
+ pat = gen_rtx_SET (VOIDmode, regno_save_mem[regno][numregs],
+ gen_rtx_REG (GET_MODE (regno_save_mem[regno][numregs]),
+ regno));
+ code = reg_save_code[regno][numregs];
+ 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
+static struct insn_chain *
insert_one_insn (chain, before_p, code, pat)
struct insn_chain *chain;
int before_p;
new = new_insn_chain ();
if (before_p)
{
+ rtx link;
+
new->prev = chain->prev;
if (new->prev != 0)
new->prev->next = new;
new->insn = emit_insn_before (pat, insn);
/* ??? It would be nice if we could exclude the already / still saved
registers from the live sets. */
- COPY_REG_SET (new->live_before, chain->live_before);
- COPY_REG_SET (new->live_after, chain->live_before);
- if (chain->insn == basic_block_head[chain->block])
- basic_block_head[chain->block] = new->insn;
+ 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
{
new->insn = emit_insn_after (pat, 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_before, chain->live_after);
- COPY_REG_SET (new->live_after, chain->live_after);
- if (chain->insn == basic_block_end[chain->block])
- basic_block_end[chain->block] = new->insn;
+ 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;
}