/* Data flow analysis for GNU compiler.
- Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 92-96, 1997 Free Software Foundation, Inc.
This file is part of GNU CC.
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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
/* This file contains the data flow analysis pass of the compiler.
#include "hard-reg-set.h"
#include "flags.h"
#include "output.h"
+#include "except.h"
#include "obstack.h"
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
+/* The contents of the current function definition are allocated
+ in this obstack, and all are freed at the end of the function.
+ For top-level functions, this is temporary_obstack.
+ Separate obstacks are made for nested functions. */
+
+extern struct obstack *function_obstack;
+
/* List of labels that must never be deleted. */
extern rtx forced_labels;
int max_regno;
-/* Maximum number of SCRATCH rtx's used in any basic block of this function. */
+/* Maximum number of SCRATCH rtx's used in any basic block of this
+ function. */
int max_scratch;
static int num_scratch;
-/* Indexed by n, gives number of basic block that (REG n) is used in.
- If the value is REG_BLOCK_GLOBAL (-2),
- it means (REG n) is used in more than one basic block.
- REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know.
- This information remains valid for the rest of the compilation
- of the current function; it is used to control register allocation. */
-
-int *reg_basic_block;
-
-/* Indexed by n, gives number of times (REG n) is used or set, each
- weighted by its loop-depth.
- This information remains valid for the rest of the compilation
- of the current function; it is used to control register allocation. */
-
-int *reg_n_refs;
-
-/* Indexed by N, gives number of places register N dies.
- This information remains valid for the rest of the compilation
- of the current function; it is used to control register allocation. */
-
-short *reg_n_deaths;
-
-/* Indexed by N, gives 1 if that reg is live across any CALL_INSNs.
- This information remains valid for the rest of the compilation
- of the current function; it is used to control register allocation. */
-
-int *reg_n_calls_crossed;
-
-/* Total number of instructions at which (REG n) is live.
- The larger this is, the less priority (REG n) gets for
- allocation in a real register.
- This information remains valid for the rest of the compilation
- of the current function; it is used to control register allocation.
-
- local-alloc.c may alter this number to change the priority.
-
- Negative values are special.
- -1 is used to mark a pseudo reg which has a constant or memory equivalent
- and is used infrequently enough that it should not get a hard register.
- -2 is used to mark a pseudo reg for a parameter, when a frame pointer
- is not required. global.c makes an allocno for this but does
- not try to assign a hard register to it. */
+/* Indexed by n, giving various register information */
-int *reg_live_length;
+reg_info *reg_n_info;
/* Element N is the next insn that uses (hard or pseudo) register number N
within the current basic block; or zero, if there is no such insn.
/* Forward declarations */
static void find_basic_blocks PROTO((rtx, rtx));
-static int uses_reg_or_mem PROTO((rtx));
+static int jmp_uses_reg_or_mem PROTO((rtx));
static void mark_label_ref PROTO((rtx, rtx, int));
static void life_analysis PROTO((rtx, int));
void allocate_for_life_analysis PROTO((void));
-static void init_regset_vector PROTO((regset *, regset, int, int));
+static void init_regset_vector PROTO((regset *, int, int, struct obstack *));
static void propagate_block PROTO((regset, rtx, rtx, int,
regset, int));
+static rtx flow_delete_insn PROTO((rtx));
static int insn_dead_p PROTO((rtx, regset, int));
static int libcall_dead_p PROTO((rtx, regset, rtx, rtx));
static void mark_set_regs PROTO((regset, regset, rtx,
#endif
/* Record which registers will be eliminated. We use this in
- mark_used_regs. */
+ mark_used_regs. */
CLEAR_HARD_REG_SET (elim_reg_set);
|| (GET_RTX_CLASS (code) == 'i'
&& (prev_code == JUMP_INSN
|| (prev_code == CALL_INSN
- && nonlocal_label_list != 0
- /* Ignore a CLOBBER after a CALL_INSN here. */
- && ! (code == INSN
- && GET_CODE (PATTERN (insn)) == CLOBBER))
+ && nonlocal_label_list != 0)
|| prev_code == BARRIER)))
i++;
- if (code != NOTE
- /* Skip a CLOBBER after a CALL_INSN. See similar code in
- find_basic_blocks. */
- && ! (prev_code == CALL_INSN
- && code == INSN && GET_CODE (PATTERN (insn)) == CLOBBER))
+
+ if (code == CALL_INSN && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ code = INSN;
+
+ if (code != NOTE)
prev_code = code;
}
}
register char *block_marked = (char *) alloca (n_basic_blocks);
/* List of label_refs to all labels whose addresses are taken
and used as data. */
- rtx label_value_list = 0;
+ rtx label_value_list;
+ int label_value_list_marked_live;
rtx x, note;
enum rtx_code prev_code, code;
- int depth;
+ int depth, pass;
+
+ pass = 1;
+ restart:
+ label_value_list = 0;
+ label_value_list_marked_live = 0;
block_live_static = block_live;
bzero (block_live, n_basic_blocks);
bzero (block_marked, n_basic_blocks);
&& (prev_code == JUMP_INSN
|| (prev_code == CALL_INSN
&& nonlocal_label_list != 0
- /* Ignore if CLOBBER since we consider this
- part of the CALL. See below. */
- && ! (code == INSN
- && GET_CODE (PATTERN (insn)) == CLOBBER))
+ && ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
|| prev_code == BARRIER)))
{
basic_block_head[++i] = insn;
BLOCK_NUM (insn) = i;
- /* Don't separate a CALL_INSN from following CLOBBER insns. This is a
- kludge that will go away when each CALL_INSN records its USE and
- CLOBBERs. */
-
- if (code != NOTE
- && ! (prev_code == CALL_INSN && code == INSN
- && GET_CODE (PATTERN (insn)) == CLOBBER))
+ if (code != NOTE)
prev_code = code;
}
- if (i + 1 != n_basic_blocks)
+ /* During the second pass, `n_basic_blocks' is only an upper bound.
+ Only perform the sanity check for the first pass, and on the second
+ pass ensure `n_basic_blocks' is set to the correct value. */
+ if (pass == 1 && i + 1 != n_basic_blocks)
abort ();
-
- /* Don't delete the labels (in this function)
- that are referenced by non-jump instructions. */
-
- for (x = label_value_list; x; x = XEXP (x, 1))
- if (! LABEL_REF_NONLOCAL_P (x))
- block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+ n_basic_blocks = i + 1;
for (x = forced_labels; x; x = XEXP (x, 1))
if (! LABEL_REF_NONLOCAL_P (x))
block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+ for (x = exception_handler_labels; x; x = XEXP (x, 1))
+ block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+
/* Record which basic blocks control can drop in to. */
for (i = 0; i < n_basic_blocks; i++)
if (n_basic_blocks > 0)
{
int something_marked = 1;
+ int deleted;
/* Find all indirect jump insns and mark them as possibly jumping to all
the labels whose addresses are explicitly used. This is because,
for (i = len - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (pat, 0, i)) == SET
&& SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx
- && uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i))))
+ && jmp_uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i))))
computed_jump = 1;
}
else if (GET_CODE (pat) == SET
&& SET_DEST (pat) == pc_rtx
- && uses_reg_or_mem (SET_SRC (pat)))
+ && jmp_uses_reg_or_mem (SET_SRC (pat)))
computed_jump = 1;
if (computed_jump)
{
+ if (label_value_list_marked_live == 0)
+ {
+ label_value_list_marked_live = 1;
+
+ /* This could be made smarter by only considering
+ these live, if the computed goto is live. */
+
+ /* Don't delete the labels (in this function) that
+ are referenced by non-jump instructions. */
+
+ for (x = label_value_list; x; x = XEXP (x, 1))
+ if (! LABEL_REF_NONLOCAL_P (x))
+ block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+ }
+
for (x = label_value_list; x; x = XEXP (x, 1))
mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
insn, 0);
to all the nonlocal goto handler labels. */
for (insn = f; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == CALL_INSN)
+ if (GET_CODE (insn) == CALL_INSN
+ && ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
for (x = nonlocal_label_list; x; x = XEXP (x, 1))
- /* Don't try marking labels that
- were deleted as unreferenced. */
- if (GET_CODE (XEXP (x, 0)) == CODE_LABEL)
- mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
- insn, 0);
+ mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
+ insn, 0);
/* ??? This could be made smarter:
in some cases it's possible to tell that certain
gotos. */
}
+ /* All blocks associated with labels in label_value_list are
+ trivially considered as marked live, if the list is empty.
+ We do this to speed up the below code. */
+
+ if (label_value_list == 0)
+ label_value_list_marked_live = 1;
+
/* Pass over all blocks, marking each block that is reachable
and has not yet been marked.
Keep doing this until, in one pass, no blocks have been marked.
insn = basic_block_end[i];
if (GET_CODE (insn) == JUMP_INSN)
mark_label_ref (PATTERN (insn), insn, 0);
+
+ if (label_value_list_marked_live == 0)
+ /* Now that we know that this block is live, mark as
+ live, all the blocks that we might be able to get
+ to as live. */
+
+ for (insn = basic_block_head[i];
+ insn != NEXT_INSN (basic_block_end[i]);
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ for (note = REG_NOTES (insn);
+ note;
+ note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_LABEL)
+ {
+ x = XEXP (note, 0);
+ block_live[BLOCK_NUM (x)] = 1;
+ }
+ }
+ }
}
}
+ /* ??? See if we have a "live" basic block that is not reachable.
+ This can happen if it is headed by a label that is preserved or
+ in one of the label lists, but no call or computed jump is in
+ the loop. It's not clear if we can delete the block or not,
+ but don't for now. However, we will mess up register status if
+ it remains unreachable, so add a fake reachability from the
+ previous block. */
+
+ for (i = 1; i < n_basic_blocks; i++)
+ if (block_live[i] && ! basic_block_drops_in[i]
+ && GET_CODE (basic_block_head[i]) == CODE_LABEL
+ && LABEL_REFS (basic_block_head[i]) == basic_block_head[i])
+ basic_block_drops_in[i] = 1;
+
/* Now delete the code for any basic blocks that can't be reached.
They can occur because jump_optimize does not recognize
unreachable loops as unreachable. */
+ deleted = 0;
for (i = 0; i < n_basic_blocks; i++)
if (!block_live[i])
{
+ deleted++;
+
+ /* Delete the insns in a (non-live) block. We physically delete
+ every non-note insn except the start and end (so
+ basic_block_head/end needn't be updated), we turn the latter
+ into NOTE_INSN_DELETED notes.
+ We use to "delete" the insns by turning them into notes, but
+ we may be deleting lots of insns that subsequent passes would
+ otherwise have to process. Secondly, lots of deleted blocks in
+ a row can really slow down propagate_block since it will
+ otherwise process insn-turned-notes multiple times when it
+ looks for loop begin/end notes. */
+ if (basic_block_head[i] != basic_block_end[i])
+ {
+ /* It would be quicker to delete all of these with a single
+ unchaining, rather than one at a time, but we need to keep
+ the NOTE's. */
+ insn = NEXT_INSN (basic_block_head[i]);
+ while (insn != basic_block_end[i])
+ {
+ if (GET_CODE (insn) == BARRIER)
+ abort ();
+ else if (GET_CODE (insn) != NOTE)
+ insn = flow_delete_insn (insn);
+ else
+ insn = NEXT_INSN (insn);
+ }
+ }
insn = basic_block_head[i];
- while (1)
+ if (GET_CODE (insn) != NOTE)
{
+ /* Turn the head into a deleted insn note. */
if (GET_CODE (insn) == BARRIER)
abort ();
- if (GET_CODE (insn) != NOTE)
- {
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- }
- if (insn == basic_block_end[i])
- {
- /* BARRIERs are between basic blocks, not part of one.
- Delete a BARRIER if the preceding jump is deleted.
- We cannot alter a BARRIER into a NOTE
- because it is too short; but we can really delete
- it because it is not part of a basic block. */
- if (NEXT_INSN (insn) != 0
- && GET_CODE (NEXT_INSN (insn)) == BARRIER)
- delete_insn (NEXT_INSN (insn));
- break;
- }
- insn = NEXT_INSN (insn);
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ insn = basic_block_end[i];
+ if (GET_CODE (insn) != NOTE)
+ {
+ /* Turn the tail into a deleted insn note. */
+ if (GET_CODE (insn) == BARRIER)
+ abort ();
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
}
+ /* BARRIERs are between basic blocks, not part of one.
+ Delete a BARRIER if the preceding jump is deleted.
+ We cannot alter a BARRIER into a NOTE
+ because it is too short; but we can really delete
+ it because it is not part of a basic block. */
+ if (NEXT_INSN (insn) != 0
+ && GET_CODE (NEXT_INSN (insn)) == BARRIER)
+ delete_insn (NEXT_INSN (insn));
+
/* Each time we delete some basic blocks,
see if there is a jump around them that is
being turned into a no-op. If so, delete it. */
if (block_live[i - 1])
{
register int j;
- for (j = i; j < n_basic_blocks; j++)
+ for (j = i + 1; j < n_basic_blocks; j++)
if (block_live[j])
{
rtx label;
}
}
}
+
+ /* There are pathological cases where one function calling hundreds of
+ nested inline functions can generate lots and lots of unreachable
+ blocks that jump can't delete. Since we don't use sparse matrices
+ a lot of memory will be needed to compile such functions.
+ Implementing sparse matrices is a fair bit of work and it is not
+ clear that they win more than they lose (we don't want to
+ unnecessarily slow down compilation of normal code). By making
+ another pass for the pathological case, we can greatly speed up
+ their compilation without hurting normal code. This works because
+ all the insns in the unreachable blocks have either been deleted or
+ turned into notes.
+ Note that we're talking about reducing memory usage by 10's of
+ megabytes and reducing compilation time by several minutes. */
+ /* ??? The choice of when to make another pass is a bit arbitrary,
+ and was derived from empirical data. */
+ if (pass == 1
+ && deleted > 200)
+ {
+ pass++;
+ n_basic_blocks -= deleted;
+ /* `n_basic_blocks' may not be correct at this point: two previously
+ separate blocks may now be merged. That's ok though as we
+ recalculate it during the second pass. It certainly can't be
+ any larger than the current value. */
+ goto restart;
+ }
}
}
\f
-/* Return 1 if X contain a REG or MEM that is not in the constant pool. */
+/* Subroutines of find_basic_blocks. */
+
+/* Return 1 if X, the SRC_SRC of SET of (pc) contain a REG or MEM that is
+ not in the constant pool and not in the condition of an IF_THEN_ELSE. */
static int
-uses_reg_or_mem (x)
+jmp_uses_reg_or_mem (x)
rtx x;
{
enum rtx_code code = GET_CODE (x);
int i, j;
char *fmt;
- if (code == REG
- || (code == MEM
- && ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
- && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))))
- return 1;
+ switch (code)
+ {
+ case CONST:
+ case LABEL_REF:
+ case PC:
+ return 0;
+
+ case REG:
+ return 1;
+
+ case MEM:
+ return ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)));
+
+ case IF_THEN_ELSE:
+ return (jmp_uses_reg_or_mem (XEXP (x, 1))
+ || jmp_uses_reg_or_mem (XEXP (x, 2)));
+
+ case PLUS: case MINUS: case MULT:
+ return (jmp_uses_reg_or_mem (XEXP (x, 0))
+ || jmp_uses_reg_or_mem (XEXP (x, 1)));
+ }
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e'
- && uses_reg_or_mem (XEXP (x, i)))
+ && jmp_uses_reg_or_mem (XEXP (x, i)))
return 1;
if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
- if (uses_reg_or_mem (XVECEXP (x, i, j)))
+ if (jmp_uses_reg_or_mem (XVECEXP (x, i, j)))
return 1;
}
return 0;
}
-\f
+
/* Check expression X for label references;
if one is found, add INSN to the label's chain of references.
}
}
}
+
+/* Delete INSN by patching it out.
+ Return the next insn. */
+
+static rtx
+flow_delete_insn (insn)
+ rtx insn;
+{
+ /* ??? For the moment we assume we don't have to watch for NULLs here
+ since the start/end of basic blocks aren't deleted like this. */
+ NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
+ PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
+ return NEXT_INSN (insn);
+}
\f
/* Determine which registers are live at the start of each
basic block of the function whose first insn is F.
rtx f;
int nregs;
{
- register regset tem;
int first_pass;
int changed;
/* For each basic block, a bitmask of regs
allocate_for_life_analysis ();
reg_next_use = (rtx *) alloca (nregs * sizeof (rtx));
- bzero (reg_next_use, nregs * sizeof (rtx));
+ bzero ((char *) reg_next_use, nregs * sizeof (rtx));
/* Set up several regset-vectors used internally within this function.
Their meanings are documented above, with their declarations. */
- basic_block_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset));
+ basic_block_live_at_end
+ = (regset *) alloca (n_basic_blocks * sizeof (regset));
+
/* Don't use alloca since that leads to a crash rather than an error message
if there isn't enough space.
Don't use oballoc since we may need to allocate other things during
this function on the temporary obstack. */
- tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
- bzero (tem, n_basic_blocks * regset_bytes);
- init_regset_vector (basic_block_live_at_end, tem, n_basic_blocks, regset_bytes);
+ init_regset_vector (basic_block_live_at_end, n_basic_blocks, regset_bytes,
+ &flow_obstack);
- basic_block_new_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset));
- tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
- bzero (tem, n_basic_blocks * regset_bytes);
- init_regset_vector (basic_block_new_live_at_end, tem, n_basic_blocks, regset_bytes);
+ basic_block_new_live_at_end
+ = (regset *) alloca (n_basic_blocks * sizeof (regset));
+ init_regset_vector (basic_block_new_live_at_end, n_basic_blocks, regset_bytes,
+ &flow_obstack);
- basic_block_significant = (regset *) alloca (n_basic_blocks * sizeof (regset));
- tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
- bzero (tem, n_basic_blocks * regset_bytes);
- init_regset_vector (basic_block_significant, tem, n_basic_blocks, regset_bytes);
+ basic_block_significant
+ = (regset *) alloca (n_basic_blocks * sizeof (regset));
+ init_regset_vector (basic_block_significant, n_basic_blocks, regset_bytes,
+ &flow_obstack);
/* Record which insns refer to any volatile memory
or for any reason can't be deleted just because they are dead stores.
- Also, delete any insns that copy a register to itself. */
+ Also, delete any insns that copy a register to itself. */
for (insn = f; insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (PATTERN (insn)) == SET
&& GET_CODE (SET_DEST (PATTERN (insn))) == REG
&& GET_CODE (SET_SRC (PATTERN (insn))) == REG
- && REGNO (SET_DEST (PATTERN (insn))) ==
- REGNO (SET_SRC (PATTERN (insn)))
+ && (REGNO (SET_DEST (PATTERN (insn)))
+ == REGNO (SET_SRC (PATTERN (insn))))
+ /* Insns carrying these notes are useful later on. */
+ && ! find_reg_note (insn, REG_EQUAL, NULL_RTX))
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ /* Delete (in effect) any obvious no-op moves. */
+ else if (GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_DEST (PATTERN (insn))) == SUBREG
+ && GET_CODE (SUBREG_REG (SET_DEST (PATTERN (insn)))) == REG
+ && GET_CODE (SET_SRC (PATTERN (insn))) == SUBREG
+ && GET_CODE (SUBREG_REG (SET_SRC (PATTERN (insn)))) == REG
+ && (REGNO (SUBREG_REG (SET_DEST (PATTERN (insn))))
+ == REGNO (SUBREG_REG (SET_SRC (PATTERN (insn)))))
+ && SUBREG_WORD (SET_DEST (PATTERN (insn))) ==
+ SUBREG_WORD (SET_SRC (PATTERN (insn)))
/* Insns carrying these notes are useful later on. */
&& ! find_reg_note (insn, REG_EQUAL, NULL_RTX))
{
{
/* If exiting needs the right stack value,
consider the stack pointer live at the end of the function. */
- basic_block_live_at_end[n_basic_blocks - 1]
- [STACK_POINTER_REGNUM / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
- basic_block_new_live_at_end[n_basic_blocks - 1]
- [STACK_POINTER_REGNUM / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
+ SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1],
+ STACK_POINTER_REGNUM);
+ SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
+ STACK_POINTER_REGNUM);
}
/* Mark the frame pointer is needed at the end of the function. If
if (n_basic_blocks > 0)
{
- basic_block_live_at_end[n_basic_blocks - 1]
- [FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS);
- basic_block_new_live_at_end[n_basic_blocks - 1]
- [FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS);
+ SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1],
+ FRAME_POINTER_REGNUM);
+ SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
+ FRAME_POINTER_REGNUM);
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
/* If they are different, also mark the hard frame pointer as live */
- basic_block_live_at_end[n_basic_blocks - 1]
- [HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM
- % REGSET_ELT_BITS);
- basic_block_new_live_at_end[n_basic_blocks - 1]
- [HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM
- % REGSET_ELT_BITS);
+ SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1],
+ HARD_FRAME_POINTER_REGNUM);
+ SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
+ HARD_FRAME_POINTER_REGNUM);
#endif
}
- /* Mark all global registers as being live at the end of the function
- since they may be referenced by our caller. */
+ /* Mark all global registers and all registers used by the epilogue
+ as being live at the end of the function since they may be
+ referenced by our caller. */
if (n_basic_blocks > 0)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (global_regs[i])
+ if (global_regs[i]
+#ifdef EPILOGUE_USES
+ || EPILOGUE_USES (i)
+#endif
+ )
{
- basic_block_live_at_end[n_basic_blocks - 1]
- [i / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
- basic_block_new_live_at_end[n_basic_blocks - 1]
- [i / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+ SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1], i);
+ SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1], i);
}
/* Propagate life info through the basic blocks
reg that is live at the end now but was not live there before
is one of the significant regs of this basic block). */
- for (j = 0; j < regset_size; j++)
- {
- register REGSET_ELT_TYPE x
- = (basic_block_new_live_at_end[i][j]
- & ~basic_block_live_at_end[i][j]);
- if (x)
- consider = 1;
- if (x & basic_block_significant[i][j])
- {
- must_rescan = 1;
- consider = 1;
- break;
- }
- }
-
+ EXECUTE_IF_AND_COMPL_IN_REG_SET (basic_block_new_live_at_end[i],
+ basic_block_live_at_end[i],
+ 0, j,
+ {
+ consider = 1;
+ if (REGNO_REG_SET_P (basic_block_significant[i], j))
+ {
+ must_rescan = 1;
+ goto done;
+ }
+ });
+ done:
if (! consider)
continue;
}
/* No complete rescan needed;
just record those variables newly known live at end
as live at start as well. */
- for (j = 0; j < regset_size; j++)
- {
- register REGSET_ELT_TYPE x
- = (basic_block_new_live_at_end[i][j]
- & ~basic_block_live_at_end[i][j]);
- basic_block_live_at_start[i][j] |= x;
- basic_block_live_at_end[i][j] |= x;
- }
+ IOR_AND_COMPL_REG_SET (basic_block_live_at_start[i],
+ basic_block_new_live_at_end[i],
+ basic_block_live_at_end[i]);
+
+ IOR_AND_COMPL_REG_SET (basic_block_live_at_end[i],
+ basic_block_new_live_at_end[i],
+ basic_block_live_at_end[i]);
}
else
{
/* Update the basic_block_live_at_start
by propagation backwards through the block. */
- bcopy (basic_block_new_live_at_end[i],
- basic_block_live_at_end[i], regset_bytes);
- bcopy (basic_block_live_at_end[i],
- basic_block_live_at_start[i], regset_bytes);
+ COPY_REG_SET (basic_block_live_at_end[i],
+ basic_block_new_live_at_end[i]);
+ COPY_REG_SET (basic_block_live_at_start[i],
+ basic_block_live_at_end[i]);
propagate_block (basic_block_live_at_start[i],
basic_block_head[i], basic_block_end[i], 0,
first_pass ? basic_block_significant[i]
{
register rtx jump, head;
+
/* Update the basic_block_new_live_at_end's of the block
that falls through into this one (if any). */
head = basic_block_head[i];
- jump = PREV_INSN (head);
if (basic_block_drops_in[i])
- {
- register int from_block = BLOCK_NUM (jump);
- register int j;
- for (j = 0; j < regset_size; j++)
- basic_block_new_live_at_end[from_block][j]
- |= basic_block_live_at_start[i][j];
- }
+ IOR_REG_SET (basic_block_new_live_at_end[i-1],
+ basic_block_live_at_start[i]);
+
/* Update the basic_block_new_live_at_end's of
all the blocks that jump to this one. */
if (GET_CODE (head) == CODE_LABEL)
jump = LABEL_NEXTREF (jump))
{
register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
- register int j;
- for (j = 0; j < regset_size; j++)
- basic_block_new_live_at_end[from_block][j]
- |= basic_block_live_at_start[i][j];
+ IOR_REG_SET (basic_block_new_live_at_end[from_block],
+ basic_block_live_at_start[i]);
}
}
#ifdef USE_C_ALLOCA
one basic block. */
if (n_basic_blocks > 0)
- for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
- if (basic_block_live_at_start[0][i / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS)))
- reg_basic_block[i] = REG_BLOCK_GLOBAL;
+ EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[0],
+ FIRST_PSEUDO_REGISTER, i,
+ {
+ REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL;
+ });
/* Now the life information is accurate.
Make one more pass over each basic block
But we don't need to do this for the user's variables, since
ANSI says only volatile variables need this. */
#ifdef LONGJMP_RESTORE_FROM_STACK
- for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++)
- if (regs_live_at_setjmp[i / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS))
- && regno_reg_rtx[i] != 0 && ! REG_USERVAR_P (regno_reg_rtx[i]))
- {
- reg_live_length[i] = -1;
- reg_basic_block[i] = -1;
- }
+ EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp,
+ FIRST_PSEUDO_REGISTER, i,
+ {
+ if (regno_reg_rtx[i] != 0
+ && ! REG_USERVAR_P (regno_reg_rtx[i]))
+ {
+ REG_LIVE_LENGTH (i) = -1;
+ REG_BASIC_BLOCK (i) = -1;
+ }
+ });
#endif
#endif
If the pseudo goes in a hard reg, some other value may occupy
that hard reg where this pseudo is dead, thus clobbering the pseudo.
Conclusion: such a pseudo must not go in a hard reg. */
- for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++)
- if ((regs_live_at_setjmp[i / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS)))
- && regno_reg_rtx[i] != 0)
- {
- reg_live_length[i] = -1;
- reg_basic_block[i] = -1;
- }
+ EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp,
+ FIRST_PSEUDO_REGISTER, i,
+ {
+ if (regno_reg_rtx[i] != 0)
+ {
+ REG_LIVE_LENGTH (i) = -1;
+ REG_BASIC_BLOCK (i) = -1;
+ }
+ });
obstack_free (&flow_obstack, NULL_PTR);
}
allocate_for_life_analysis ()
{
register int i;
- register regset tem;
regset_size = ((max_regno + REGSET_ELT_BITS - 1) / REGSET_ELT_BITS);
- regset_bytes = regset_size * sizeof (*(regset)0);
-
- reg_n_refs = (int *) oballoc (max_regno * sizeof (int));
- bzero (reg_n_refs, max_regno * sizeof (int));
-
- reg_n_sets = (short *) oballoc (max_regno * sizeof (short));
- bzero (reg_n_sets, max_regno * sizeof (short));
+ regset_bytes = regset_size * sizeof (*(regset) 0);
- reg_n_deaths = (short *) oballoc (max_regno * sizeof (short));
- bzero (reg_n_deaths, max_regno * sizeof (short));
+ /* Because both reg_scan and flow_analysis want to set up the REG_N_SETS
+ information, explicitly reset it here. The allocation should have
+ already happened on the previous reg_scan pass. Make sure in case
+ some more registers were allocated. */
+ allocate_reg_info (max_regno, FALSE, FALSE);
- reg_live_length = (int *) oballoc (max_regno * sizeof (int));
- bzero (reg_live_length, max_regno * sizeof (int));
-
- reg_n_calls_crossed = (int *) oballoc (max_regno * sizeof (int));
- bzero (reg_n_calls_crossed, max_regno * sizeof (int));
-
- reg_basic_block = (int *) oballoc (max_regno * sizeof (int));
for (i = 0; i < max_regno; i++)
- reg_basic_block[i] = REG_BLOCK_UNKNOWN;
+ REG_N_SETS (i) = 0;
- basic_block_live_at_start = (regset *) oballoc (n_basic_blocks * sizeof (regset));
- tem = (regset) oballoc (n_basic_blocks * regset_bytes);
- bzero (tem, n_basic_blocks * regset_bytes);
- init_regset_vector (basic_block_live_at_start, tem, n_basic_blocks, regset_bytes);
+ basic_block_live_at_start
+ = (regset *) oballoc (n_basic_blocks * sizeof (regset));
+ init_regset_vector (basic_block_live_at_start, n_basic_blocks, regset_bytes,
+ function_obstack);
- regs_live_at_setjmp = (regset) oballoc (regset_bytes);
- bzero (regs_live_at_setjmp, regset_bytes);
+ regs_live_at_setjmp = OBSTACK_ALLOC_REG_SET (function_obstack);
+ CLEAR_REG_SET (regs_live_at_setjmp);
}
/* Make each element of VECTOR point at a regset,
BYTES_PER_ELT is the number of bytes in one regset. */
static void
-init_regset_vector (vector, space, nelts, bytes_per_elt)
+init_regset_vector (vector, nelts, bytes_per_elt, alloc_obstack)
regset *vector;
- regset space;
int nelts;
int bytes_per_elt;
+ struct obstack *alloc_obstack;
{
register int i;
- register regset p = space;
for (i = 0; i < nelts; i++)
{
- vector[i] = p;
- p += bytes_per_elt / sizeof (*p);
+ vector[i] = OBSTACK_ALLOC_REG_SET (alloc_obstack);
+ CLEAR_REG_SET (vector[i]);
}
}
/* The following variables are used only if FINAL is nonzero. */
/* This vector gets one element for each reg that has been live
at any point in the basic block that has been scanned so far.
- SOMETIMES_MAX says how many elements are in use so far.
- In each element, OFFSET is the byte-number within a regset
- for the register described by the element, and BIT is a mask
- for that register's bit within the byte. */
- register struct sometimes { short offset; short bit; } *regs_sometimes_live;
+ SOMETIMES_MAX says how many elements are in use so far. */
+ register int *regs_sometimes_live;
int sometimes_max = 0;
/* This regset has 1 for each reg that we have seen live so far.
It and REGS_SOMETIMES_LIVE are updated together. */
current basic block, and adjust as we pass ends and starts of loops. */
loop_depth = basic_block_loop_depth[bnum];
- dead = (regset) alloca (regset_bytes);
- live = (regset) alloca (regset_bytes);
+ dead = ALLOCA_REG_SET ();
+ live = ALLOCA_REG_SET ();
cc0_live = 0;
last_mem_set = 0;
if (final)
{
- register int i, offset;
- REGSET_ELT_TYPE bit;
+ register int i;
num_scratch = 0;
- maxlive = (regset) alloca (regset_bytes);
- bcopy (old, maxlive, regset_bytes);
- regs_sometimes_live
- = (struct sometimes *) alloca (max_regno * sizeof (struct sometimes));
+ maxlive = ALLOCA_REG_SET ();
+ COPY_REG_SET (maxlive, old);
+ regs_sometimes_live = (int *) alloca (max_regno * sizeof (int));
/* Process the regs live at the end of the block.
Enter them in MAXLIVE and REGS_SOMETIMES_LIVE.
- Also mark them as not local to any one basic block. */
-
- for (offset = 0, i = 0; offset < regset_size; offset++)
- for (bit = 1; bit; bit <<= 1, i++)
- {
- if (i == max_regno)
- break;
- if (old[offset] & bit)
- {
- reg_basic_block[i] = REG_BLOCK_GLOBAL;
- regs_sometimes_live[sometimes_max].offset = offset;
- regs_sometimes_live[sometimes_max].bit = i % REGSET_ELT_BITS;
- sometimes_max++;
- }
- }
+ Also mark them as not local to any one basic block. */
+ EXECUTE_IF_SET_IN_REG_SET (old, 0, i,
+ {
+ REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL;
+ regs_sometimes_live[sometimes_max] = i;
+ sometimes_max++;
+ });
}
/* Scan the block an insn at a time from end to beginning. */
{
prev = PREV_INSN (insn);
- /* Look for loop boundaries, remembering that we are going backwards. */
- if (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
- loop_depth++;
- else if (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
- loop_depth--;
-
- /* If we have LOOP_DEPTH == 0, there has been a bookkeeping error.
- Abort now rather than setting register status incorrectly. */
- if (loop_depth == 0)
- abort ();
-
- /* If this is a call to `setjmp' et al,
- warn if any non-volatile datum is live. */
-
- if (final && GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+ if (GET_CODE (insn) == NOTE)
{
- int i;
- for (i = 0; i < regset_size; i++)
- regs_live_at_setjmp[i] |= old[i];
+ /* Look for loop boundaries, remembering that we are going
+ backwards. */
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+ loop_depth++;
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ loop_depth--;
+
+ /* If we have LOOP_DEPTH == 0, there has been a bookkeeping error.
+ Abort now rather than setting register status incorrectly. */
+ if (loop_depth == 0)
+ abort ();
+
+ /* If this is a call to `setjmp' et al,
+ warn if any non-volatile datum is live. */
+
+ if (final && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+ IOR_REG_SET (regs_live_at_setjmp, old);
}
/* Update the life-status of regs for this insn.
are those live after, with DEAD regs turned off,
and then LIVE regs turned on. */
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
register int i;
rtx note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
goto flushed;
}
- for (i = 0; i < regset_size; i++)
- {
- dead[i] = 0; /* Faster than bzero here */
- live[i] = 0; /* since regset_size is usually small */
- }
+ CLEAR_REG_SET (dead);
+ CLEAR_REG_SET (live);
/* See if this is an increment or decrement that can be
merged into a following memory address. */
{
register int i;
+ rtx note;
+
+ for (note = CALL_INSN_FUNCTION_USAGE (insn);
+ note;
+ note = XEXP (note, 1))
+ if (GET_CODE (XEXP (note, 0)) == USE)
+ mark_used_regs (old, live, SET_DEST (XEXP (note, 0)),
+ final, insn);
+
/* Each call clobbers all call-clobbered regs that are not
- global. Note that the function-value reg is a
+ global or fixed. Note that the function-value reg is a
call-clobbered reg, and mark_set_regs has already had
a chance to handle it. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (call_used_regs[i] && ! global_regs[i])
- dead[i / REGSET_ELT_BITS]
- |= ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS));
+ if (call_used_regs[i] && ! global_regs[i]
+ && ! fixed_regs[i])
+ SET_REGNO_REG_SET (dead, i);
/* The stack ptr is used (honorarily) by a CALL insn. */
- live[STACK_POINTER_REGNUM / REGSET_ELT_BITS]
- |= ((REGSET_ELT_TYPE) 1
- << (STACK_POINTER_REGNUM % REGSET_ELT_BITS));
+ SET_REGNO_REG_SET (live, STACK_POINTER_REGNUM);
/* Calls may also reference any of the global registers,
so they are made live. */
-
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (global_regs[i])
- live[i / REGSET_ELT_BITS]
- |= ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS));
+ mark_used_regs (old, live,
+ gen_rtx (REG, reg_raw_mode[i], i),
+ final, insn);
/* Calls also clobber memory. */
last_mem_set = 0;
}
/* Update OLD for the registers used or set. */
- for (i = 0; i < regset_size; i++)
- {
- old[i] &= ~dead[i];
- old[i] |= live[i];
- }
+ AND_COMPL_REG_SET (old, dead);
+ IOR_REG_SET (old, live);
if (GET_CODE (insn) == CALL_INSN && final)
{
must not go in a register clobbered by calls.
Find all regs now live and record this for them. */
- register struct sometimes *p = regs_sometimes_live;
+ register int *p = regs_sometimes_live;
for (i = 0; i < sometimes_max; i++, p++)
- if (old[p->offset] & ((REGSET_ELT_TYPE) 1 << p->bit))
- reg_n_calls_crossed[p->offset * REGSET_ELT_BITS + p->bit]+= 1;
+ if (REGNO_REG_SET_P (old, *p))
+ REG_N_CALLS_CROSSED (*p)++;
}
}
if (final)
{
- for (i = 0; i < regset_size; i++)
- {
- register REGSET_ELT_TYPE diff = live[i] & ~maxlive[i];
+ register int regno;
+ register int *p;
- if (diff)
- {
- register int regno;
- maxlive[i] |= diff;
- for (regno = 0; diff && regno < REGSET_ELT_BITS; regno++)
- if (diff & ((REGSET_ELT_TYPE) 1 << regno))
- {
- regs_sometimes_live[sometimes_max].offset = i;
- regs_sometimes_live[sometimes_max].bit = regno;
- diff &= ~ ((REGSET_ELT_TYPE) 1 << regno);
- sometimes_max++;
- }
- }
- }
+ EXECUTE_IF_AND_COMPL_IN_REG_SET (live, maxlive, 0, regno,
+ {
+ regs_sometimes_live[sometimes_max++] = regno;
+ SET_REGNO_REG_SET (maxlive, regno);
+ });
- {
- register struct sometimes *p = regs_sometimes_live;
- for (i = 0; i < sometimes_max; i++, p++)
- {
- if (old[p->offset] & ((REGSET_ELT_TYPE) 1 << p->bit))
- reg_live_length[p->offset * REGSET_ELT_BITS + p->bit]++;
- }
- }
+ p = regs_sometimes_live;
+ for (i = 0; i < sometimes_max; i++)
+ {
+ regno = *p++;
+ if (REGNO_REG_SET_P (old, regno))
+ REG_LIVE_LENGTH (regno)++;
+ }
}
}
flushed: ;
if (GET_CODE (r) == REG)
{
register int regno = REGNO (r);
- register int offset = regno / REGSET_ELT_BITS;
- register REGSET_ELT_TYPE bit
- = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
/* Don't delete insns to set global regs. */
if ((regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
/* Make sure insns to set arg pointer are never deleted
(if the arg pointer isn't fixed, there will be a USE for
- it, so we can treat it normally). */
+ it, so we can treat it normally). */
|| (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
#endif
- || (needed[offset] & bit) != 0)
+ || REGNO_REG_SET_P (needed, regno))
return 0;
/* If this is a hard register, verify that subsequent words are
int n = HARD_REGNO_NREGS (regno, GET_MODE (r));
while (--n > 0)
- if ((needed[(regno + n) / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1
- << ((regno + n) % REGSET_ELT_BITS))) != 0)
+ if (REGNO_REG_SET_P (needed, regno+n))
return 0;
}
/* Return 1 if register REGNO was used before it was set.
In other words, if it is live at function entry.
- Don't count global regster variables, though. */
+ Don't count global register variables or variables in registers
+ that can be used for function arg passing, though. */
int
regno_uninitialized (regno)
int regno;
{
if (n_basic_blocks == 0
- || (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]))
+ || (regno < FIRST_PSEUDO_REGISTER
+ && (global_regs[regno] || FUNCTION_ARG_REGNO_P (regno))))
return 0;
- return (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)));
+ return REGNO_REG_SET_P (basic_block_live_at_start[0], regno);
}
/* 1 if register REGNO was alive at a place where `setjmp' was called
if (n_basic_blocks == 0)
return 0;
- return ((reg_n_sets[regno] > 1
- || (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS))))
- && (regs_live_at_setjmp[regno / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS))));
+ return ((REG_N_SETS (regno) > 1
+ || REGNO_REG_SET_P (basic_block_live_at_start[0], regno))
+ && REGNO_REG_SET_P (regs_live_at_setjmp, regno));
}
\f
/* Process the registers that are set within X.
&& ! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]))
/* && regno != STACK_POINTER_REGNUM) -- let's try without this. */
{
- register int offset = regno / REGSET_ELT_BITS;
- register REGSET_ELT_TYPE bit
- = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
- REGSET_ELT_TYPE all_needed = (needed[offset] & bit);
- REGSET_ELT_TYPE some_needed = (needed[offset] & bit);
+ int some_needed = REGNO_REG_SET_P (needed, regno);
+ int some_not_needed = ! some_needed;
/* Mark it as a significant register for this basic block. */
if (significant)
- significant[offset] |= bit;
+ SET_REGNO_REG_SET (significant, regno);
/* Mark it as as dead before this insn. */
- dead[offset] |= bit;
+ SET_REGNO_REG_SET (dead, regno);
/* A hard reg in a wide mode may really be multiple registers.
If so, mark all of them just like the first. */
n = HARD_REGNO_NREGS (regno, GET_MODE (reg));
while (--n > 0)
{
+ int regno_n = regno + n;
+ int needed_regno = REGNO_REG_SET_P (needed, regno_n);
if (significant)
- significant[(regno + n) / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS);
- dead[(regno + n) / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS);
- some_needed
- |= (needed[(regno + n) / REGSET_ELT_BITS]
- & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
- all_needed
- &= (needed[(regno + n) / REGSET_ELT_BITS]
- & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
+ SET_REGNO_REG_SET (significant, regno_n);
+
+ SET_REGNO_REG_SET (dead, regno_n);
+ some_needed |= needed_regno;
+ some_not_needed |= ! needed_regno;
}
}
/* Additional data to record if this is the final pass. */
register rtx y = reg_next_use[regno];
register int blocknum = BLOCK_NUM (insn);
- /* The next use is no longer "next", since a store intervenes. */
- reg_next_use[regno] = 0;
-
/* If this is a hard reg, record this function uses the reg. */
if (regno < FIRST_PSEUDO_REGISTER)
for (i = regno; i < endregno; i++)
{
+ /* The next use is no longer "next", since a store
+ intervenes. */
+ reg_next_use[i] = 0;
+
regs_ever_live[i] = 1;
- reg_n_sets[i]++;
+ REG_N_SETS (i)++;
}
}
else
{
+ /* The next use is no longer "next", since a store
+ intervenes. */
+ reg_next_use[regno] = 0;
+
/* Keep track of which basic blocks each reg appears in. */
- if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
- reg_basic_block[regno] = blocknum;
- else if (reg_basic_block[regno] != blocknum)
- reg_basic_block[regno] = REG_BLOCK_GLOBAL;
+ if (REG_BASIC_BLOCK (regno) == REG_BLOCK_UNKNOWN)
+ REG_BASIC_BLOCK (regno) = blocknum;
+ else if (REG_BASIC_BLOCK (regno) != blocknum)
+ REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL;
/* Count (weighted) references, stores, etc. This counts a
register twice if it is modified, but that is correct. */
- reg_n_sets[regno]++;
+ REG_N_SETS (regno)++;
- reg_n_refs[regno] += loop_depth;
+ REG_N_REFS (regno) += loop_depth;
/* The insns where a reg is live are normally counted
elsewhere, but we want the count to include the insn
where the reg is set, and the normal counting mechanism
would not count it. */
- reg_live_length[regno]++;
+ REG_LIVE_LENGTH (regno)++;
}
- if (all_needed)
+ if (! some_not_needed)
{
/* Make a logical link from the next following insn
that uses this register, back to this insn.
Indicate this by marking the reg being set as dying here. */
REG_NOTES (insn)
= gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn));
- reg_n_deaths[REGNO (reg)]++;
+ REG_N_DEATHS (REGNO (reg))++;
}
else
{
for (i = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1;
i >= 0; i--)
- if ((needed[(regno + i) / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1
- << ((regno + i) % REGSET_ELT_BITS))) == 0)
+ if (!REGNO_REG_SET_P (needed, regno + i))
REG_NOTES (insn)
= gen_rtx (EXPR_LIST, REG_UNUSED,
- gen_rtx (REG, word_mode, regno + i),
+ gen_rtx (REG, reg_raw_mode[regno + i],
+ regno + i),
REG_NOTES (insn));
}
}
&& (use = find_use_as_address (PATTERN (insn), addr, offset),
use != 0 && use != (rtx) 1))
{
- int win = 0;
rtx q = SET_DEST (set);
+ enum rtx_code inc_code = (INTVAL (XEXP (y, 1)) == size
+ ? (offset ? PRE_INC : POST_INC)
+ : (offset ? PRE_DEC : POST_DEC));
if (dead_or_set_p (incr, addr))
- win = 1;
+ {
+ /* This is the simple case. Try to make the auto-inc. If
+ we can't, we are done. Otherwise, we will do any
+ needed updates below. */
+ if (! validate_change (insn, &XEXP (x, 0),
+ gen_rtx (inc_code, Pmode, addr),
+ 0))
+ return;
+ }
else if (GET_CODE (q) == REG
/* PREV_INSN used here to check the semi-open interval
[insn,incr). */
- && ! reg_used_between_p (q, PREV_INSN (insn), incr))
+ && ! reg_used_between_p (q, PREV_INSN (insn), incr)
+ /* We must also check for sets of q as q may be
+ a call clobbered hard register and there may
+ be a call between PREV_INSN (insn) and incr. */
+ && ! reg_set_between_p (q, PREV_INSN (insn), incr))
{
/* We have *p followed sometime later by q = p+size.
Both p and q must be live afterward,
BLOCK_NUM (temp) = BLOCK_NUM (insn);
}
+ /* If we can't make the auto-inc, or can't make the
+ replacement into Y, exit. There's no point in making
+ the change below if we can't do the auto-inc and doing
+ so is not correct in the pre-inc case. */
+
+ validate_change (insn, &XEXP (x, 0),
+ gen_rtx (inc_code, Pmode, q),
+ 1);
+ validate_change (incr, &XEXP (y, 0), q, 1);
+ if (! apply_change_group ())
+ return;
+
+ /* We now know we'll be doing this change, so emit the
+ new insn(s) and do the updates. */
emit_insns_before (insns, insn);
if (basic_block_head[BLOCK_NUM (insn)] == insn)
basic_block_head[BLOCK_NUM (insn)] = insns;
- XEXP (x, 0) = q;
- XEXP (y, 0) = q;
-
/* INCR will become a NOTE and INSN won't contain a
use of ADDR. If a use of ADDR was just placed in
the insn before INSN, make that the next use.
addr = q;
regno = REGNO (q);
- win = 1;
/* REGNO is now used in INCR which is below INSN, but
it previously wasn't live here. If we don't mark
it as needed, we'll put a REG_DEAD note for it
on this insn, which is incorrect. */
- needed[regno / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+ SET_REGNO_REG_SET (needed, regno);
/* If there are any calls between INSN and INCR, show
that REGNO now crosses them. */
for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
if (GET_CODE (temp) == CALL_INSN)
- reg_n_calls_crossed[regno]++;
+ REG_N_CALLS_CROSSED (regno)++;
}
+ else
+ return;
+
+ /* If we haven't returned, it means we were able to make the
+ auto-inc, so update the status. First, record that this insn
+ has an implicit side effect. */
- if (win)
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_INC, addr, REG_NOTES (insn));
+
+ /* Modify the old increment-insn to simply copy
+ the already-incremented value of our register. */
+ if (! validate_change (incr, &SET_SRC (set), addr, 0))
+ abort ();
+
+ /* If that makes it a no-op (copying the register into itself) delete
+ it so it won't appear to be a "use" and a "set" of this
+ register. */
+ if (SET_DEST (set) == addr)
{
- /* We have found a suitable auto-increment: do POST_INC around
- the register here, and patch out the increment instruction
- that follows. */
- XEXP (x, 0) = gen_rtx ((INTVAL (XEXP (y, 1)) == size
- ? (offset ? PRE_INC : POST_INC)
- : (offset ? PRE_DEC : POST_DEC)),
- Pmode, addr);
-
- /* Record that this insn has an implicit side effect. */
- REG_NOTES (insn)
- = gen_rtx (EXPR_LIST, REG_INC, addr, REG_NOTES (insn));
-
- /* Modify the old increment-insn to simply copy
- the already-incremented value of our register. */
- SET_SRC (set) = addr;
- /* Indicate insn must be re-recognized. */
- INSN_CODE (incr) = -1;
-
- /* If that makes it a no-op (copying the register into itself)
- then delete it so it won't appear to be a "use" and a "set"
- of this register. */
- if (SET_DEST (set) == addr)
- {
- PUT_CODE (incr, NOTE);
- NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (incr) = 0;
- }
+ PUT_CODE (incr, NOTE);
+ NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (incr) = 0;
+ }
- if (regno >= FIRST_PSEUDO_REGISTER)
- {
- /* Count an extra reference to the reg. When a reg is
- incremented, spilling it is worse, so we want to make
- that less likely. */
- reg_n_refs[regno] += loop_depth;
- /* Count the increment as a setting of the register,
- even though it isn't a SET in rtl. */
- reg_n_sets[regno]++;
- }
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ /* Count an extra reference to the reg. When a reg is
+ incremented, spilling it is worse, so we want to make
+ that less likely. */
+ REG_N_REFS (regno) += loop_depth;
+
+ /* Count the increment as a setting of the register,
+ even though it isn't a SET in rtl. */
+ REG_N_SETS (regno)++;
}
}
}
return;
case MEM:
- /* Invalidate the data for the last MEM stored. We could do this only
- if the addresses conflict, but this doesn't seem worthwhile. */
- last_mem_set = 0;
+ /* CYGNUS LOCAL dje/8176 */
+ /* Invalidate the data for the last MEM stored, but only if MEM is
+ something that can be stored into. */
+ if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
+ ; /* needn't clear last_mem_set */
+ else
+ last_mem_set = 0;
+ /* END CYGNUS LOCAL */
#ifdef AUTO_INC_DEC
if (final)
#endif
break;
+ case SUBREG:
+ if (GET_CODE (SUBREG_REG (x)) == REG
+ && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER
+ && (GET_MODE_SIZE (GET_MODE (x))
+ != GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
+ REG_CHANGES_SIZE (REGNO (SUBREG_REG (x))) = 1;
+
+ /* While we're here, optimize this case. */
+ x = SUBREG_REG (x);
+
+ /* In case the SUBREG is not of a register, don't optimize */
+ if (GET_CODE (x) != REG)
+ {
+ mark_used_regs (needed, live, x, final, insn);
+ return;
+ }
+
+ /* ... fall through ... */
+
case REG:
/* See a register other than being set
=> mark it as needed. */
regno = REGNO (x);
{
- register int offset = regno / REGSET_ELT_BITS;
- register REGSET_ELT_TYPE bit
- = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
- REGSET_ELT_TYPE all_needed = needed[offset] & bit;
- REGSET_ELT_TYPE some_needed = needed[offset] & bit;
+ REGSET_ELT_TYPE some_needed = REGNO_REG_SET_P (needed, regno);
+ REGSET_ELT_TYPE some_not_needed = ! some_needed;
+
+ SET_REGNO_REG_SET (live, regno);
- live[offset] |= bit;
/* A hard reg in a wide mode may really be multiple registers.
If so, mark all of them just like the first. */
if (regno < FIRST_PSEUDO_REGISTER)
n = HARD_REGNO_NREGS (regno, GET_MODE (x));
while (--n > 0)
{
- live[(regno + n) / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS);
- some_needed
- |= (needed[(regno + n) / REGSET_ELT_BITS]
- & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
- all_needed
- &= (needed[(regno + n) / REGSET_ELT_BITS]
- & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
+ int regno_n = regno + n;
+ int needed_regno = REGNO_REG_SET_P (needed, regno_n);
+
+ SET_REGNO_REG_SET (live, regno_n);
+ some_needed |= needed_regno;
+ some_not_needed |= ! needed_regno;
}
}
if (final)
register int blocknum = BLOCK_NUM (insn);
- if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
- reg_basic_block[regno] = blocknum;
- else if (reg_basic_block[regno] != blocknum)
- reg_basic_block[regno] = REG_BLOCK_GLOBAL;
+ if (REG_BASIC_BLOCK (regno) == REG_BLOCK_UNKNOWN)
+ REG_BASIC_BLOCK (regno) = blocknum;
+ else if (REG_BASIC_BLOCK (regno) != blocknum)
+ REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL;
/* Count (weighted) number of uses of each reg. */
- reg_n_refs[regno] += loop_depth;
+ REG_N_REFS (regno) += loop_depth;
}
/* Record and count the insns in which a reg dies.
we do not make a REG_DEAD note; likewise if we already
made such a note. */
- if (! all_needed
+ if (some_not_needed
&& ! dead_or_set_p (insn, x)
#if 0
&& (regno >= FIRST_PSEUDO_REGISTER || ! fixed_regs[regno])
#endif
)
{
+ /* Check for the case where the register dying partially
+ overlaps the register set by this insn. */
+ if (regno < FIRST_PSEUDO_REGISTER
+ && HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
+ {
+ int n = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ while (--n >= 0)
+ some_needed |= dead_or_set_regno_p (insn, regno + n);
+ }
+
/* If none of the words in X is needed, make a REG_DEAD
note. Otherwise, we must make partial REG_DEAD notes. */
if (! some_needed)
{
REG_NOTES (insn)
= gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (insn));
- reg_n_deaths[regno]++;
+ REG_N_DEATHS (regno)++;
}
else
{
for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
i >= 0; i--)
- if ((needed[(regno + i) / REGSET_ELT_BITS]
- & ((REGSET_ELT_TYPE) 1
- << ((regno + i) % REGSET_ELT_BITS))) == 0
+ if (!REGNO_REG_SET_P (needed, regno + i)
&& ! dead_or_set_regno_p (insn, regno + i))
REG_NOTES (insn)
= gen_rtx (EXPR_LIST, REG_DEAD,
- gen_rtx (REG, word_mode, regno + i),
+ gen_rtx (REG, reg_raw_mode[regno + i],
+ regno + i),
REG_NOTES (insn));
}
}
|| GET_CODE (testreg) == SIGN_EXTRACT
|| GET_CODE (testreg) == SUBREG)
{
+ if (GET_CODE (testreg) == SUBREG
+ && GET_CODE (SUBREG_REG (testreg)) == REG
+ && REGNO (SUBREG_REG (testreg)) >= FIRST_PSEUDO_REGISTER
+ && (GET_MODE_SIZE (GET_MODE (testreg))
+ != GET_MODE_SIZE (GET_MODE (SUBREG_REG (testreg)))))
+ REG_CHANGES_SIZE (REGNO (SUBREG_REG (testreg))) = 1;
+
/* Modifying a single register in an alternate mode
does not use any of the old value. But these other
ways of storing in a register do use the old value. */
case RETURN:
/* If exiting needs the right stack value, consider this insn as
using the stack pointer. In any event, consider it as using
- all global registers. */
+ all global registers and all registers used by return. */
#ifdef EXIT_IGNORE_STACK
if (! EXIT_IGNORE_STACK
|| (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer))
#endif
- live[STACK_POINTER_REGNUM / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
+ SET_REGNO_REG_SET (live, STACK_POINTER_REGNUM);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (global_regs[i])
- live[i / REGSET_ELT_BITS]
- |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+ if (global_regs[i]
+#ifdef EPILOGUE_USES
+ || EPILOGUE_USES (i)
+#endif
+ )
+ SET_REGNO_REG_SET (live, i);
break;
}
rtx y = reg_next_use[regno];
if (y != 0
&& BLOCK_NUM (y) == BLOCK_NUM (insn)
+ /* Don't do this if the reg dies, or gets set in y; a standard addressing
+ mode would be better. */
+ && ! dead_or_set_p (y, SET_DEST (x))
&& try_pre_increment (y, SET_DEST (PATTERN (insn)),
amount))
{
less likely. */
if (regno >= FIRST_PSEUDO_REGISTER)
{
- reg_n_refs[regno] += loop_depth;
- reg_n_sets[regno]++;
+ REG_N_REFS (regno) += loop_depth;
+ REG_N_SETS (regno)++;
}
return 1;
}
if (GET_MODE_SIZE (GET_MODE (use)) != (amount > 0 ? amount : - amount))
return 0;
- XEXP (use, 0) = gen_rtx (amount > 0
- ? (do_post ? POST_INC : PRE_INC)
- : (do_post ? POST_DEC : PRE_DEC),
- Pmode, reg);
+ /* See if this combination of instruction and addressing mode exists. */
+ if (! validate_change (insn, &XEXP (use, 0),
+ gen_rtx (amount > 0
+ ? (do_post ? POST_INC : PRE_INC)
+ : (do_post ? POST_DEC : PRE_DEC),
+ Pmode, reg), 0))
+ return 0;
/* Record that this insn now has an implicit side effect on X. */
REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_INC, reg, REG_NOTES (insn));
fprintf (file, "%d registers.\n", max_regno);
for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
- if (reg_n_refs[i])
+ if (REG_N_REFS (i))
{
enum reg_class class, altclass;
fprintf (file, "\nRegister %d used %d times across %d insns",
- i, reg_n_refs[i], reg_live_length[i]);
- if (reg_basic_block[i] >= 0)
- fprintf (file, " in block %d", reg_basic_block[i]);
- if (reg_n_deaths[i] != 1)
- fprintf (file, "; dies in %d places", reg_n_deaths[i]);
- if (reg_n_calls_crossed[i] == 1)
+ i, REG_N_REFS (i), REG_LIVE_LENGTH (i));
+ if (REG_BASIC_BLOCK (i) >= 0)
+ fprintf (file, " in block %d", REG_BASIC_BLOCK (i));
+ if (REG_N_DEATHS (i) != 1)
+ fprintf (file, "; dies in %d places", REG_N_DEATHS (i));
+ if (REG_N_CALLS_CROSSED (i) == 1)
fprintf (file, "; crosses 1 call");
- else if (reg_n_calls_crossed[i])
- fprintf (file, "; crosses %d calls", reg_n_calls_crossed[i]);
+ else if (REG_N_CALLS_CROSSED (i))
+ fprintf (file, "; crosses %d calls", REG_N_CALLS_CROSSED (i));
if (PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD)
fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i));
class = reg_preferred_class (i);
}
fprintf (file, "\nRegisters live at start:");
for (regno = 0; regno < max_regno; regno++)
- {
- register int offset = regno / REGSET_ELT_BITS;
- register REGSET_ELT_TYPE bit
- = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
- if (basic_block_live_at_start[i][offset] & bit)
- fprintf (file, " %d", regno);
- }
+ if (REGNO_REG_SET_P (basic_block_live_at_start[i], regno))
+ fprintf (file, " %d", regno);
fprintf (file, "\n");
}
fprintf (file, "\n");