summaryrefslogtreecommitdiff
path: root/gcc/caller-save.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/caller-save.c')
-rw-r--r--gcc/caller-save.c278
1 files changed, 166 insertions, 112 deletions
diff --git a/gcc/caller-save.c b/gcc/caller-save.c
index 60011f71c82..acb9ba43241 100644
--- a/gcc/caller-save.c
+++ b/gcc/caller-save.c
@@ -82,9 +82,14 @@ int n_regs_saved;
static void set_reg_live PROTO((rtx, rtx));
static void clear_reg_live PROTO((rtx));
-static void restore_referenced_regs PROTO((rtx, rtx, enum machine_mode));
-static int insert_save_restore PROTO((rtx, int, int,
+static void restore_referenced_regs PROTO((rtx, rtx, enum machine_mode,
+ int));
+static int insert_restore PROTO((rtx, int, int,
+ enum machine_mode, int, int));
+static int insert_save PROTO((rtx, int, int,
enum machine_mode, int));
+static void insert_one_insn PROTO((rtx, int, enum rtx_code,
+ enum machine_mode, rtx, int));
/* Initialize for caller-save.
@@ -358,7 +363,6 @@ save_call_clobbered_regs (insn_mode)
for (b = 0; b < n_basic_blocks; b++)
{
regset regs_live = basic_block_live_at_start[b];
- rtx prev_block_last = PREV_INSN (basic_block_head[b]);
int i, j;
int regno;
@@ -399,7 +403,7 @@ save_call_clobbered_regs (insn_mode)
any of them. We must restore them before the insn if so. */
if (n_regs_saved)
- restore_referenced_regs (PATTERN (insn), insn, insn_mode);
+ restore_referenced_regs (PATTERN (insn), insn, insn_mode, b);
/* NB: the normal procedure is to first enliven any
registers set by insn, then deaden any registers that
@@ -449,8 +453,7 @@ save_call_clobbered_regs (insn_mode)
/* It must not be set by this instruction. */
&& ! TEST_HARD_REG_BIT (this_call_sets, regno)
&& ! TEST_HARD_REG_BIT (hard_regs_saved, regno))
- regno += insert_save_restore (insn, 1, regno,
- insn_mode, 0);
+ regno += insert_save (insn, 1, regno, insn_mode, b);
/* Put the information for this CALL_INSN on top of what
we already had. */
@@ -489,13 +492,9 @@ save_call_clobbered_regs (insn_mode)
if (n_regs_saved)
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (hard_regs_need_restore, regno))
- regno += insert_save_restore ((GET_CODE (insn) == JUMP_INSN
- ? insn : NEXT_INSN (insn)), 0,
- regno, insn_mode, MOVE_MAX / UNITS_PER_WORD);
-
- /* If we added any insns at the start of the block, update the start
- of the block to point at those insns. */
- basic_block_head[b] = NEXT_INSN (prev_block_last);
+ regno += insert_restore (insn, GET_CODE (insn) == JUMP_INSN,
+ regno, insn_mode,
+ MOVE_MAX / UNITS_PER_WORD, b);
}
}
@@ -562,10 +561,11 @@ clear_reg_live (reg)
INSN_MODE is the mode to assign to any insns that we add. */
static void
-restore_referenced_regs (x, insn, insn_mode)
+restore_referenced_regs (x, insn, insn_mode, block)
rtx x;
rtx insn;
enum machine_mode insn_mode;
+ int block;
{
enum rtx_code code = GET_CODE (x);
char *fmt;
@@ -584,11 +584,11 @@ restore_referenced_regs (x, insn, insn_mode)
if (regno >= FIRST_PSEUDO_REGISTER
&& reg_equiv_mem[regno] != 0)
restore_referenced_regs (XEXP (reg_equiv_mem[regno], 0),
- insn, insn_mode);
+ insn, insn_mode, block);
else if (regno >= FIRST_PSEUDO_REGISTER
&& reg_equiv_address[regno] != 0)
restore_referenced_regs (reg_equiv_address[regno],
- insn, insn_mode);
+ insn, insn_mode, block);
/* Otherwise if this is a hard register, restore any piece of it that
is currently saved. */
@@ -597,13 +597,13 @@ restore_referenced_regs (x, insn, insn_mode)
{
int numregs = HARD_REGNO_NREGS (regno, GET_MODE (x));
/* Save at most SAVEREGS at a time. This can not be larger than
- MOVE_MAX, because that causes insert_save_restore to fail. */
+ MOVE_MAX, because that causes insert_restore to fail. */
int saveregs = MIN (numregs, MOVE_MAX / UNITS_PER_WORD);
int endregno = regno + numregs;
for (i = regno; i < endregno; i++)
if (TEST_HARD_REG_BIT (hard_regs_need_restore, i))
- i += insert_save_restore (insn, 0, i, insn_mode, saveregs);
+ i += insert_restore (insn, 1, i, insn_mode, saveregs, block);
}
return;
@@ -613,18 +613,18 @@ restore_referenced_regs (x, insn, insn_mode)
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- restore_referenced_regs (XEXP (x, i), insn, insn_mode);
+ restore_referenced_regs (XEXP (x, i), insn, insn_mode, block);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- restore_referenced_regs (XVECEXP (x, i, j), insn, insn_mode);
+ restore_referenced_regs (XVECEXP (x, i, j), insn, insn_mode, block);
}
}
-/* Insert a sequence of insns to save or restore, SAVE_P says which,
- REGNO. Place these insns in front of INSN. INSN_MODE is the mode
+/* Insert a sequence of insns to restore REGNO. Place these insns in front
+ of or after INSN (determined by BEFORE_P). INSN_MODE is the mode
to assign to these insns. MAXRESTORE is the maximum number of registers
- which should be restored during this call (when SAVE_P == 0). It should
- never be less than 1 since we only work with entire registers.
+ which should be restored during this call. It should never be less than
+ 1 since we only work with entire registers.
Note that we have verified in init_caller_save that we can do this
with a simple SET, so use it. Set INSN_CODE to what we save there
@@ -635,16 +635,19 @@ restore_referenced_regs (x, insn, insn_mode)
Return the extra number of registers saved. */
static int
-insert_save_restore (insn, save_p, regno, insn_mode, maxrestore)
+insert_restore (insn, before_p, regno, insn_mode, maxrestore, block)
rtx insn;
- int save_p;
+ int before_p;
int regno;
enum machine_mode insn_mode;
int maxrestore;
+ int block;
{
rtx pat = NULL_RTX;
enum insn_code code = CODE_FOR_nothing;
int numregs = 0;
+ int i, j, k;
+ int ok;
/* 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
@@ -656,108 +659,159 @@ insert_save_restore (insn, save_p, regno, insn_mode, maxrestore)
if (regno_save_mem[regno][1] == 0)
abort ();
-#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
- insn that references CC0 is for a restore, and doing a restore earlier
- isn't a problem. We do, however, assume here that CALL_INSNs don't
- reference CC0. Guard against non-INSN's like CODE_LABEL. */
-
- if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
- && reg_referenced_p (cc0_rtx, PATTERN (insn)))
- insn = prev_nonnote_insn (insn);
-#endif
-
/* Get the pattern to emit and update our status. */
- if (save_p)
+
+ /* See if we can restore `maxrestore' registers at once. Work
+ backwards to the single register case. */
+ for (i = maxrestore; i > 0; i--)
{
- int i, j, k;
- int ok;
+ ok = 1;
+ if (regno_save_mem[regno][i])
+ for (j = 0; j < i; j++)
+ {
+ if (! TEST_HARD_REG_BIT (hard_regs_need_restore, regno + j))
+ ok = 0;
+ }
+ else
+ continue;
+
+ /* 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];
+
- /* See if we can save several registers with a single instruction.
- Work backwards to the single register case. */
- for (i = MOVE_MAX / UNITS_PER_WORD; i > 0; i--)
+ /* Clear status for all registers we restored. */
+ for (k = 0; k < i; k++)
{
- ok = 1;
- if (regno_save_mem[regno][i] != 0)
- for (j = 0; j < i; j++)
- {
- if (! call_used_regs[regno + j] || call_fixed_regs[regno + j]
- || ! TEST_HARD_REG_BIT (hard_regs_live, regno + j)
- || TEST_HARD_REG_BIT (hard_regs_saved, regno + j))
- ok = 0;
- }
- else
- continue;
+ CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k);
+ n_regs_saved--;
+ }
- /* Must do this one save at a time */
- if (! ok)
- continue;
+ numregs = i;
+ break;
+ }
- 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];
+ insert_one_insn (insn, before_p, code, insn_mode, pat, block);
- /* 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);
- SET_HARD_REG_BIT (hard_regs_need_restore, regno + k);
- n_regs_saved++;
- }
+ /* Tell our callers how many extra registers we saved/restored */
+ return numregs - 1;
+}
- numregs = i;
- break;
- }
- }
- else
- {
- int i, j, k;
- int ok;
+/* Like insert_restore, but emit code to save REGNO. */
+static int
+insert_save (insn, before_p, regno, insn_mode, block)
+ rtx insn;
+ int before_p;
+ int regno;
+ enum machine_mode insn_mode;
+ int block;
+{
+ rtx pat = NULL_RTX;
+ enum insn_code code = CODE_FOR_nothing;
+ int numregs = 0;
+ int i, j, k;
+ int ok;
- /* See if we can restore `maxrestore' registers at once. Work
- backwards to the single register case. */
- for (i = maxrestore; i > 0; i--)
- {
- ok = 1;
- if (regno_save_mem[regno][i])
- for (j = 0; j < i; j++)
- {
- if (! TEST_HARD_REG_BIT (hard_regs_need_restore, regno + j))
- ok = 0;
- }
- else
- continue;
+ /* 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
+ save. That will cause us to write an insn with a (nil) SET_DEST
+ or SET_SRC. Instead of doing so and causing a crash later, check
+ for this common case and abort here instead. This will remove one
+ step in debugging such problems. */
- /* 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];
+ if (regno_save_mem[regno][1] == 0)
+ abort ();
+ /* Get the pattern to emit and update our status. */
- /* Clear status for all registers we restored. */
- for (k = 0; k < i; k++)
- {
- CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k);
- n_regs_saved--;
- }
+ /* See if we can save several registers with a single instruction.
+ Work backwards to the single register case. */
+ for (i = MOVE_MAX / UNITS_PER_WORD; i > 0; i--)
+ {
+ ok = 1;
+ if (regno_save_mem[regno][i] != 0)
+ for (j = 0; j < i; j++)
+ {
+ if (! call_used_regs[regno + j] || call_fixed_regs[regno + j]
+ || ! TEST_HARD_REG_BIT (hard_regs_live, regno + j)
+ || TEST_HARD_REG_BIT (hard_regs_saved, regno + j))
+ ok = 0;
+ }
+ else
+ continue;
- numregs = i;
- break;
- }
+ /* 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);
+ SET_HARD_REG_BIT (hard_regs_need_restore, regno + k);
+ n_regs_saved++;
+ }
+
+ numregs = i;
+ break;
}
- /* Emit the insn and set the code and mode. */
- insn = emit_insn_before (pat, insn);
- PUT_MODE (insn, insn_mode);
- INSN_CODE (insn) = code;
+ insert_one_insn (insn, before_p, code, insn_mode, pat, block);
/* Tell our callers how many extra registers we saved/restored */
return numregs - 1;
}
+
+/* Emit one insn, set the code and mode, and update basic block
+ boundaries. */
+static void
+insert_one_insn (insn, before_p, code, mode, pat, block)
+ rtx insn;
+ int before_p;
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx pat;
+ int block;
+{
+ rtx insert_point = insn;
+ rtx 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
+ insn that references CC0 is for a restore, and doing a restore earlier
+ isn't a problem. We do, however, assume here that CALL_INSNs don't
+ reference CC0. Guard against non-INSN's like CODE_LABEL. */
+
+ if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+ && before_p
+ && reg_referenced_p (cc0_rtx, PATTERN (insn)))
+ insert_point = prev_nonnote_insn (insn);
+#endif
+
+ if (before_p)
+ {
+ new = emit_insn_before (pat, insert_point);
+ if (insert_point == basic_block_head[block])
+ basic_block_head[block] = new;
+ }
+ else
+ {
+ new = emit_insn_after (pat, insert_point);
+ if (insert_point == basic_block_end[block])
+ basic_block_end[block] = new;
+ }
+
+ PUT_MODE (new, mode);
+ INSN_CODE (new) = code;
+}