/* Data flow analysis for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GCC.
#include "toplev.h"
#include "recog.h"
#include "expr.h"
-#include "ssa.h"
#include "timevar.h"
#include "obstack.h"
#include "splay-tree.h"
-/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
- the stack pointer does not matter. The value is tested only in
- functions that have frame pointers.
- No definition is equivalent to always zero. */
-#ifndef EXIT_IGNORE_STACK
-#define EXIT_IGNORE_STACK 0
-#endif
-
#ifndef HAVE_epilogue
#define HAVE_epilogue 0
#endif
#define HAVE_sibcall_epilogue 0
#endif
-#ifndef LOCAL_REGNO
-#define LOCAL_REGNO(REGNO) 0
-#endif
#ifndef EPILOGUE_USES
#define EPILOGUE_USES(REGNO) 0
#endif
are another pair, etc. */
rtx regs_may_share;
-/* Callback that determines if it's ok for a function to have no
- noreturn attribute. */
-int (*lang_missing_noreturn_ok_p) (tree);
-
/* Set of registers that may be eliminable. These are handled specially
in updating regs_ever_live. */
/* Flags controlling the set of information propagate_block collects. */
int flags;
+ /* Index of instruction being processed. */
+ int insn_num;
};
/* Number of dead insns removed. */
static int ndead;
+/* When PROP_REG_INFO set, array contains pbi->insn_num of instruction
+ where given register died. When the register is marked alive, we use the
+ information to compute amount of instructions life range cross.
+ (remember, we are walking backward). This can be computed as current
+ pbi->insn_num - reg_deaths[regno].
+ At the end of processing each basic block, the remaining live registers
+ are inspected and liferanges are increased same way so liverange of global
+ registers are computed correctly.
+
+ The array is maintained clear for dead registers, so it can be safely reused
+ for next basic block without expensive memset of the whole array after
+ reseting pbi->insn_num to 0. */
+
+static int *reg_deaths;
+
/* Maximum length of pbi->mem_set_list before we start dropping
new elements on the floor. */
#define MAX_MEM_SET_LIST_LEN 100
static void verify_wide_reg (int, basic_block);
static void verify_local_live_at_start (regset, basic_block);
static void notice_stack_pointer_modification_1 (rtx, rtx, void *);
-static void notice_stack_pointer_modification (rtx);
+static void notice_stack_pointer_modification (void);
static void mark_reg (rtx, void *);
static void mark_regs_live_at_end (regset);
-static int set_phi_alternative_reg (rtx, int, int, void *);
static void calculate_global_regs_live (sbitmap, sbitmap, int);
static void propagate_block_delete_insn (rtx);
static rtx propagate_block_delete_libcall (rtx, rtx);
static int invalidate_mems_from_autoinc (rtx *, void *);
static void invalidate_mems_from_set (struct propagate_block_info *, rtx);
static void clear_log_links (sbitmap);
-\f
-
-void
-check_function_return_warnings (void)
-{
- if (warn_missing_noreturn
- && !TREE_THIS_VOLATILE (cfun->decl)
- && EXIT_BLOCK_PTR->pred == NULL
- && (lang_missing_noreturn_ok_p
- && !lang_missing_noreturn_ok_p (cfun->decl)))
- warning ("function might be possible candidate for attribute `noreturn'");
-
- /* If we have a path to EXIT, then we do return. */
- if (TREE_THIS_VOLATILE (cfun->decl)
- && EXIT_BLOCK_PTR->pred != NULL)
- warning ("`noreturn' function does return");
-
- /* If the clobber_return_insn appears in some basic block, then we
- do reach the end without returning a value. */
- else if (warn_return_type
- && cfun->x_clobber_return_insn != NULL
- && EXIT_BLOCK_PTR->pred != NULL)
- {
- int max_uid = get_max_uid ();
-
- /* If clobber_return_insn was excised by jump1, then renumber_insns
- can make max_uid smaller than the number still recorded in our rtx.
- That's fine, since this is a quick way of verifying that the insn
- is no longer in the chain. */
- if (INSN_UID (cfun->x_clobber_return_insn) < max_uid)
- {
- rtx insn;
-
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- if (insn == cfun->x_clobber_return_insn)
- {
- warning ("control reaches end of non-void function");
- break;
- }
- }
- }
-}
+static int count_or_remove_death_notes_bb (basic_block, int);
\f
/* Return the INSN immediately following the NOTE_INSN_BASIC_BLOCK
note associated with the BLOCK. */
rtx insn;
/* Get the first instruction in the block. */
- insn = block->head;
+ insn = BB_HEAD (block);
if (insn == NULL_RTX)
return NULL_RTX;
return NEXT_INSN (insn);
}
\f
-/* Perform data flow analysis.
- F is the first insn of the function; FLAGS is a set of PROP_* flags
- to be used in accumulating flow info. */
+/* Perform data flow analysis for the whole control flow graph.
+ FLAGS is a set of PROP_* flags to be used in accumulating flow info. */
void
-life_analysis (rtx f, FILE *file, int flags)
+life_analysis (FILE *file, int flags)
{
#ifdef ELIMINABLE_REGS
int i;
/* Always remove no-op moves. Do this before other processing so
that we don't have to keep re-scanning them. */
- delete_noop_moves (f);
+ delete_noop_moves ();
/* Some targets can emit simpler epilogues if they know that sp was
not ever modified during the function. After reload, of course,
we've already emitted the epilogue so there's no sense searching. */
if (! reload_completed)
- notice_stack_pointer_modification (f);
+ notice_stack_pointer_modification ();
/* Allocate and zero out data structures that will record the
data from lifetime analysis. */
is not immediately handy. */
if (flags & PROP_REG_INFO)
- memset (regs_ever_live, 0, sizeof (regs_ever_live));
+ {
+ memset (regs_ever_live, 0, sizeof (regs_ever_live));
+ memset (regs_asm_clobbered, 0, sizeof (regs_asm_clobbered));
+ }
update_life_info (NULL, UPDATE_LIFE_GLOBAL, flags);
+ if (reg_deaths)
+ {
+ free (reg_deaths);
+ reg_deaths = NULL;
+ }
/* Clean up. */
if (optimize && (flags & PROP_SCAN_DEAD_STORES))
if (file)
dump_flow_info (file);
- free_basic_block_vars (1);
-
- /* Removing dead insns should've made jumptables really dead. */
+ /* Removing dead insns should have made jumptables really dead. */
delete_dead_jumptables ();
}
static void
verify_wide_reg (int regno, basic_block bb)
{
- rtx head = bb->head, end = bb->end;
+ rtx head = BB_HEAD (bb), end = BB_END (bb);
while (1)
{
head = NEXT_INSN (head);
}
- if (rtl_dump_file)
+ if (dump_file)
{
- fprintf (rtl_dump_file, "Register %d died unexpectedly.\n", regno);
- dump_bb (bb, rtl_dump_file);
+ fprintf (dump_file, "Register %d died unexpectedly.\n", regno);
+ dump_bb (bb, dump_file, 0);
}
abort ();
}
registers. The regsets should exactly match. */
if (! REG_SET_EQUAL_P (new_live_at_start, bb->global_live_at_start))
{
- if (rtl_dump_file)
+ if (dump_file)
{
- fprintf (rtl_dump_file,
+ fprintf (dump_file,
"live_at_start mismatch in bb %d, aborting\nNew:\n",
bb->index);
- debug_bitmap_file (rtl_dump_file, new_live_at_start);
- fputs ("Old:\n", rtl_dump_file);
- dump_bb (bb, rtl_dump_file);
+ debug_bitmap_file (dump_file, new_live_at_start);
+ fputs ("Old:\n", dump_file);
+ dump_bb (bb, dump_file, 0);
}
abort ();
}
/* No registers should die. */
if (REGNO_REG_SET_P (bb->global_live_at_start, i))
{
- if (rtl_dump_file)
+ if (dump_file)
{
- fprintf (rtl_dump_file,
+ fprintf (dump_file,
"Register %d died unexpectedly.\n", i);
- dump_bb (bb, rtl_dump_file);
+ dump_bb (bb, dump_file, 0);
}
abort ();
}
generates subregs of a multi-word pseudo, current life analysis will
lose the kill. So we _can_ have a pseudo go live. How irritating.
+ It is also not true when a peephole decides that it doesn't need one
+ or more of the inputs.
+
Including PROP_REG_INFO does not properly refresh regs_ever_live
unless the caller resets it to zero. */
tmp = INITIALIZE_REG_SET (tmp_head);
ndead = 0;
+ if ((prop_flags & PROP_REG_INFO) && !reg_deaths)
+ reg_deaths = xcalloc (sizeof (*reg_deaths), max_regno);
+
timevar_push ((extent == UPDATE_LIFE_LOCAL || blocks)
? TV_LIFE_UPDATE : TV_LIFE);
/* Zap the life information from the last round. If we don't
do this, we can wind up with registers that no longer appear
- in the code being marked live at entry, which twiggs bogus
- warnings from regno_uninitialized. */
+ in the code being marked live at entry. */
FOR_EACH_BB (bb)
{
CLEAR_REG_SET (bb->global_live_at_start);
}
});
}
+ if (reg_deaths)
+ {
+ free (reg_deaths);
+ reg_deaths = NULL;
+ }
timevar_pop ((extent == UPDATE_LIFE_LOCAL || blocks)
? TV_LIFE_UPDATE : TV_LIFE);
- if (ndead && rtl_dump_file)
- fprintf (rtl_dump_file, "deleted %i dead insns\n", ndead);
+ if (ndead && dump_file)
+ fprintf (dump_file, "deleted %i dead insns\n", ndead);
return ndead;
}
return retval;
}
-/* Free the variables allocated by find_basic_blocks.
-
- KEEP_HEAD_END_P is nonzero if basic_block_info is not to be freed. */
+/* Free the variables allocated by find_basic_blocks. */
void
-free_basic_block_vars (int keep_head_end_p)
+free_basic_block_vars (void)
{
- if (! keep_head_end_p)
+ if (basic_block_info)
{
- if (basic_block_info)
- {
- clear_edges ();
- VARRAY_FREE (basic_block_info);
- }
- n_basic_blocks = 0;
- last_basic_block = 0;
-
- ENTRY_BLOCK_PTR->aux = NULL;
- ENTRY_BLOCK_PTR->global_live_at_end = NULL;
- EXIT_BLOCK_PTR->aux = NULL;
- EXIT_BLOCK_PTR->global_live_at_start = NULL;
+ clear_edges ();
+ basic_block_info = NULL;
}
+ n_basic_blocks = 0;
+ last_basic_block = 0;
+
+ ENTRY_BLOCK_PTR->aux = NULL;
+ ENTRY_BLOCK_PTR->global_live_at_end = NULL;
+ EXIT_BLOCK_PTR->aux = NULL;
+ EXIT_BLOCK_PTR->global_live_at_start = NULL;
}
/* Delete any insns that copy a register to itself. */
int
-delete_noop_moves (rtx f ATTRIBUTE_UNUSED)
+delete_noop_moves (void)
{
rtx insn, next;
basic_block bb;
FOR_EACH_BB (bb)
{
- for (insn = bb->head; insn != NEXT_INSN (bb->end); insn = next)
+ for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = next)
{
next = NEXT_INSN (insn);
if (INSN_P (insn) && noop_move_p (insn))
}
}
}
- if (nnoops && rtl_dump_file)
- fprintf (rtl_dump_file, "deleted %i noop moves", nnoops);
+ if (nnoops && dump_file)
+ fprintf (dump_file, "deleted %i noop moves", nnoops);
return nnoops;
}
&& (GET_CODE (PATTERN (next)) == ADDR_VEC
|| GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Dead jumptable %i removed\n", INSN_UID (insn));
+ if (dump_file)
+ fprintf (dump_file, "Dead jumptable %i removed\n", INSN_UID (insn));
delete_insn (NEXT_INSN (insn));
delete_insn (insn);
next = NEXT_INSN (next);
of a push until later in flow. See the comments in rtl.texi
regarding Embedded Side-Effects on Addresses. */
|| (GET_CODE (x) == MEM
- && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == 'a'
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == RTX_AUTOINC
&& XEXP (XEXP (x, 0), 0) == stack_pointer_rtx))
current_function_sp_is_unchanging = 0;
}
static void
-notice_stack_pointer_modification (rtx f)
+notice_stack_pointer_modification (void)
{
+ basic_block bb;
rtx insn;
/* Assume that the stack pointer is unchanging if alloca hasn't
if (! current_function_sp_is_unchanging)
return;
- for (insn = f; insn; insn = NEXT_INSN (insn))
- {
- if (INSN_P (insn))
- {
- /* Check if insn modifies the stack pointer. */
- note_stores (PATTERN (insn), notice_stack_pointer_modification_1,
- NULL);
- if (! current_function_sp_is_unchanging)
- return;
- }
- }
+ FOR_EACH_BB (bb)
+ FOR_BB_INSNS (bb, insn)
+ {
+ if (INSN_P (insn))
+ {
+ /* Check if insn modifies the stack pointer. */
+ note_stores (PATTERN (insn),
+ notice_stack_pointer_modification_1,
+ NULL);
+ if (! current_function_sp_is_unchanging)
+ return;
+ }
+ }
}
/* Mark a register in SET. Hard registers in large modes get all
SET_REGNO_REG_SET (set, regno);
if (regno < FIRST_PSEUDO_REGISTER)
{
- int n = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ int n = hard_regno_nregs[regno][GET_MODE (reg)];
while (--n > 0)
SET_REGNO_REG_SET (set, regno + n);
}
diddle_return_value (mark_reg, set);
}
-/* Callback function for for_each_successor_phi. DATA is a regset.
- Sets the SRC_REGNO, the regno of the phi alternative for phi node
- INSN, in the regset. */
-
-static int
-set_phi_alternative_reg (rtx insn ATTRIBUTE_UNUSED,
- int dest_regno ATTRIBUTE_UNUSED, int src_regno,
- void *data)
-{
- regset live = (regset) data;
- SET_REGNO_REG_SET (live, src_regno);
- return 0;
-}
-
/* Propagate global life info around the graph of basic blocks. Begin
considering blocks with their corresponding bit set in BLOCKS_IN.
If BLOCKS_IN is null, consider it the universal set.
SET_REGNO_REG_SET (new_live_at_end, PIC_OFFSET_TABLE_REGNUM);
}
- /* Regs used in phi nodes are not included in
- global_live_at_start, since they are live only along a
- particular edge. Set those regs that are live because of a
- phi node alternative corresponding to this particular block. */
- if (in_ssa_form)
- for_each_successor_phi (bb, &set_phi_alternative_reg,
- new_live_at_end);
-
if (bb == ENTRY_BLOCK_PTR)
{
COPY_REG_SET (bb->global_live_at_end, new_live_at_end);
int i;
max_regno = max_reg_num ();
+ if (reg_deaths)
+ abort ();
+ reg_deaths = xcalloc (sizeof (*reg_deaths), max_regno);
/* Recalculate the register space, in case it has grown. Old style
vector oriented regsets would set regset_{size,bytes} here also. */
&& GET_CODE (SET_SRC (PATTERN (insn))) == PLUS
&& XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx
&& GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == CONST_INT)
- /* We have an insn to pop a constant amount off the stack.
- (Such insns use PLUS regardless of the direction of the stack,
- and any insn to adjust the stack by a constant is always a pop.)
- These insns, if not dead stores, have no effect on life, though
- they do have an effect on the memory stores we are tracking. */
- invalidate_mems_from_set (pbi, stack_pointer_rtx);
+ {
+ /* We have an insn to pop a constant amount off the stack.
+ (Such insns use PLUS regardless of the direction of the stack,
+ and any insn to adjust the stack by a constant is always a pop
+ or part of a push.)
+ These insns, if not dead stores, have no effect on life, though
+ they do have an effect on the memory stores we are tracking. */
+ invalidate_mems_from_set (pbi, stack_pointer_rtx);
+ /* Still, we need to update local_set, lest ifcvt.c:dead_or_predicable
+ concludes that the stack pointer is not modified. */
+ mark_set_regs (pbi, PATTERN (insn), insn);
+ }
else
{
rtx note;
current_function_return_rtx,
(rtx *) 0)))
{
+ enum rtx_code code = global_regs[i] ? SET : CLOBBER;
/* We do not want REG_UNUSED notes for these registers. */
- mark_set_1 (pbi, CLOBBER, regno_reg_rtx[i], cond, insn,
+ mark_set_1 (pbi, code, regno_reg_rtx[i], cond, insn,
pbi->flags & ~(PROP_DEATH_NOTES | PROP_REG_INFO));
}
}
mark_used_regs (pbi, XEXP (XEXP (note, 0), 0), cond, insn);
/* The stack ptr is used (honorarily) by a CALL insn. */
+ if ((flags & PROP_REG_INFO)
+ && !REGNO_REG_SET_P (pbi->reg_live, STACK_POINTER_REGNUM))
+ reg_deaths[STACK_POINTER_REGNUM] = pbi->insn_num;
SET_REGNO_REG_SET (pbi->reg_live, STACK_POINTER_REGNUM);
/* Calls may also reference any of the global registers,
}
}
- /* On final pass, update counts of how many insns in which each reg
- is live. */
- if (flags & PROP_REG_INFO)
- EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
- { REG_LIVE_LENGTH (i)++; });
+ pbi->insn_num++;
return prev;
}
pbi->cond_local_set = cond_local_set;
pbi->cc0_live = 0;
pbi->flags = flags;
+ pbi->insn_num = 0;
if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
pbi->reg_next_use = xcalloc (max_reg_num (), sizeof (rtx));
free_reg_cond_life_info);
pbi->reg_cond_reg = BITMAP_XMALLOC ();
- /* If this block ends in a conditional branch, for each register live
- from one side of the branch and not the other, record the register
- as conditionally dead. */
- if (GET_CODE (bb->end) == JUMP_INSN
- && any_condjump_p (bb->end))
+ /* If this block ends in a conditional branch, for each register
+ live from one side of the branch and not the other, record the
+ register as conditionally dead. */
+ if (GET_CODE (BB_END (bb)) == JUMP_INSN
+ && any_condjump_p (BB_END (bb)))
{
regset_head diff_head;
regset diff = INITIALIZE_REG_SET (diff_head);
basic_block bb_true, bb_false;
- rtx cond_true, cond_false, set_src;
int i;
/* Identify the successor blocks. */
else
{
/* This can happen with a conditional jump to the next insn. */
- if (JUMP_LABEL (bb->end) != bb_true->head)
+ if (JUMP_LABEL (BB_END (bb)) != BB_HEAD (bb_true))
abort ();
/* Simplest way to do nothing. */
bb_false = bb_true;
}
- /* Extract the condition from the branch. */
- set_src = SET_SRC (pc_set (bb->end));
- cond_true = XEXP (set_src, 0);
- cond_false = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond_true)),
- GET_MODE (cond_true), XEXP (cond_true, 0),
- XEXP (cond_true, 1));
- if (GET_CODE (XEXP (set_src, 1)) == PC)
- {
- rtx t = cond_false;
- cond_false = cond_true;
- cond_true = t;
- }
-
/* Compute which register lead different lives in the successors. */
if (bitmap_operation (diff, bb_true->global_live_at_start,
bb_false->global_live_at_start, BITMAP_XOR))
{
+ /* Extract the condition from the branch. */
+ rtx set_src = SET_SRC (pc_set (BB_END (bb)));
+ rtx cond_true = XEXP (set_src, 0);
rtx reg = XEXP (cond_true, 0);
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);
- if (GET_CODE (reg) != REG)
- abort ();
+ /* We can only track conditional lifetimes if the condition is
+ in the form of a comparison of a register against zero.
+ If the condition is more complex than that, then it is safe
+ not to record any information. */
+ if (GET_CODE (reg) == REG
+ && XEXP (cond_true, 1) == const0_rtx)
+ {
+ rtx cond_false
+ = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond_true)),
+ GET_MODE (cond_true), XEXP (cond_true, 0),
+ XEXP (cond_true, 1));
+ if (GET_CODE (XEXP (set_src, 1)) == PC)
+ {
+ rtx t = cond_false;
+ cond_false = cond_true;
+ cond_true = t;
+ }
- SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (reg));
+ SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (reg));
- /* For each such register, mark it conditionally dead. */
- EXECUTE_IF_SET_IN_REG_SET
- (diff, 0, i,
- {
- struct reg_cond_life_info *rcli;
- rtx cond;
+ /* For each such register, mark it conditionally dead. */
+ EXECUTE_IF_SET_IN_REG_SET
+ (diff, 0, i,
+ {
+ struct reg_cond_life_info *rcli;
+ rtx cond;
- rcli = xmalloc (sizeof (*rcli));
+ rcli = xmalloc (sizeof (*rcli));
- if (REGNO_REG_SET_P (bb_true->global_live_at_start, i))
- cond = cond_false;
- else
- cond = cond_true;
- rcli->condition = cond;
- rcli->stores = const0_rtx;
- rcli->orig_condition = cond;
+ if (REGNO_REG_SET_P (bb_true->global_live_at_start, i))
+ cond = cond_false;
+ else
+ cond = cond_true;
+ rcli->condition = cond;
+ rcli->stores = const0_rtx;
+ rcli->orig_condition = cond;
- splay_tree_insert (pbi->reg_cond_dead, i,
- (splay_tree_value) rcli);
- });
+ splay_tree_insert (pbi->reg_cond_dead, i,
+ (splay_tree_value) rcli);
+ });
+ }
}
FREE_REG_SET (diff);
&& ! current_function_calls_eh_return)))
{
rtx insn, set;
- for (insn = bb->end; insn != bb->head; insn = PREV_INSN (insn))
+ for (insn = BB_END (bb); insn != BB_HEAD (bb); insn = PREV_INSN (insn))
if (GET_CODE (insn) == INSN
&& (set = single_set (insn))
&& GET_CODE (SET_DEST (set)) == MEM)
rtx mem = SET_DEST (set);
rtx canon_mem = canon_rtx (mem);
- /* This optimization is performed by faking a store to the
- memory at the end of the block. This doesn't work for
- unchanging memories because multiple stores to unchanging
- memory is illegal and alias analysis doesn't consider it. */
- if (RTX_UNCHANGING_P (canon_mem))
- continue;
-
if (XEXP (canon_mem, 0) == frame_pointer_rtx
|| (GET_CODE (XEXP (canon_mem, 0)) == PLUS
&& XEXP (XEXP (canon_mem, 0), 0) == frame_pointer_rtx
BITMAP_XFREE (pbi->reg_cond_reg);
#endif
+ if (pbi->flags & PROP_REG_INFO)
+ {
+ int num = pbi->insn_num;
+ int i;
+
+ EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
+ { REG_LIVE_LENGTH (i) += num - reg_deaths[i];
+ reg_deaths[i] = 0;
+ });
+ }
if (pbi->reg_next_use)
free (pbi->reg_next_use);
/* Scan the block an insn at a time from end to beginning. */
changed = 0;
- for (insn = bb->end;; insn = prev)
+ for (insn = BB_END (bb); ; insn = prev)
{
/* If this is a call to `setjmp' et al, warn if any
non-volatile datum is live. */
else
changed |= NEXT_INSN (prev) != insn;
- if (insn == bb->head)
+ if (insn == BB_HEAD (bb))
break;
}
rtx_equal_p does not check the alias set or flags, we also
must have the potential for them to conflict (anti_dependence). */
for (temp = pbi->mem_set_list; temp != 0; temp = XEXP (temp, 1))
- if (anti_dependence (r, XEXP (temp, 0)))
+ if (unchanging_anti_dependence (r, XEXP (temp, 0)))
{
rtx mem = XEXP (temp, 0);
words are not needed. */
if (regno < FIRST_PSEUDO_REGISTER)
{
- int n = HARD_REGNO_NREGS (regno, GET_MODE (r));
+ int n = hard_regno_nregs[regno][GET_MODE (r)];
while (--n > 0)
if (REGNO_REG_SET_P (pbi->reg_live, regno+n))
}
/* A CLOBBER of a pseudo-register that is dead serves no purpose. That
- is not necessarily true for hard registers. */
- else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == REG
- && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER
- && ! REGNO_REG_SET_P (pbi->reg_live, REGNO (XEXP (x, 0))))
- return 1;
-
- /* We do not check other CLOBBER or USE here. An insn consisting of just
- a CLOBBER or just a USE should not be deleted. */
+ is not necessarily true for hard registers until after reload. */
+ else if (code == CLOBBER)
+ {
+ if (GET_CODE (XEXP (x, 0)) == REG
+ && (REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER
+ || reload_completed)
+ && ! REGNO_REG_SET_P (pbi->reg_live, REGNO (XEXP (x, 0))))
+ return 1;
+ }
+
+ /* ??? A base USE is a historical relic. It ought not be needed anymore.
+ Instances where it is still used are either (1) temporary and the USE
+ escaped the pass, (2) cruft and the USE need not be emitted anymore,
+ or (3) hiding bugs elsewhere that are not properly representing data
+ flow. */
+
return 0;
}
return 1;
}
-/* Return 1 if register REGNO was used before it was set, i.e. if it is
- live at function entry. Don't count global register variables, variables
- in registers that can be used for function arg passing, or variables in
- fixed hard registers. */
-
-int
-regno_uninitialized (unsigned int regno)
-{
- if (n_basic_blocks == 0
- || (regno < FIRST_PSEUDO_REGISTER
- && (global_regs[regno]
- || fixed_regs[regno]
- || FUNCTION_ARG_REGNO_P (regno))))
- return 0;
-
- return REGNO_REG_SET_P (ENTRY_BLOCK_PTR->global_live_at_end, regno);
-}
-
/* 1 if register REGNO was alive at a place where `setjmp' was called
and was set more than once or is an argument.
Such regs may be clobbered by `longjmp'. */
rtx x = *px;
struct propagate_block_info *pbi = data;
- if (GET_RTX_CLASS (GET_CODE (x)) == 'a')
+ if (GET_RTX_CLASS (GET_CODE (x)) == RTX_AUTOINC)
{
invalidate_mems_from_set (pbi, XEXP (x, 0));
return -1;
rtx cond = NULL_RTX;
rtx link;
enum rtx_code code;
+ int flags = pbi->flags;
if (insn)
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
mark_set_1 (pbi, SET, XEXP (link, 0),
(GET_CODE (x) == COND_EXEC
? COND_EXEC_TEST (x) : NULL_RTX),
- insn, pbi->flags);
+ insn, flags);
}
retry:
switch (code = GET_CODE (x))
{
case SET:
+ if (GET_CODE (XEXP (x, 1)) == ASM_OPERANDS)
+ flags |= PROP_ASM_SCAN;
+ /* Fall through */
case CLOBBER:
- mark_set_1 (pbi, code, SET_DEST (x), cond, insn, pbi->flags);
+ mark_set_1 (pbi, code, SET_DEST (x), cond, insn, flags);
return;
case COND_EXEC:
{
int i;
- for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ /* We must scan forwards. If we have an asm, we need to set
+ the PROP_ASM_SCAN flag before scanning the clobbers. */
+ for (i = 0; i < XVECLEN (x, 0); i++)
{
rtx sub = XVECEXP (x, 0, i);
switch (code = GET_CODE (sub))
cond = COND_EXEC_TEST (sub);
sub = COND_EXEC_CODE (sub);
- if (GET_CODE (sub) != SET && GET_CODE (sub) != CLOBBER)
- break;
- /* Fall through. */
+ if (GET_CODE (sub) == SET)
+ goto mark_set;
+ if (GET_CODE (sub) == CLOBBER)
+ goto mark_clob;
+ break;
case SET:
+ mark_set:
+ if (GET_CODE (XEXP (sub, 1)) == ASM_OPERANDS)
+ flags |= PROP_ASM_SCAN;
+ /* Fall through */
case CLOBBER:
- mark_set_1 (pbi, code, SET_DEST (sub), cond, insn, pbi->flags);
+ mark_clob:
+ mark_set_1 (pbi, code, SET_DEST (sub), cond, insn, flags);
+ break;
+
+ case ASM_OPERANDS:
+ flags |= PROP_ASM_SCAN;
break;
default:
case REG:
regno_last = regno_first = REGNO (reg);
if (regno_first < FIRST_PSEUDO_REGISTER)
- regno_last += HARD_REGNO_NREGS (regno_first, GET_MODE (reg)) - 1;
+ regno_last += hard_regno_nregs[regno_first][GET_MODE (reg)] - 1;
break;
case SUBREG:
SUBREG_BYTE (reg),
outer_mode);
regno_last = (regno_first
- + HARD_REGNO_NREGS (regno_first, outer_mode) - 1);
+ + hard_regno_nregs[regno_first][outer_mode] - 1);
/* Since we've just adjusted the register number ranges, make
sure REG matches. Otherwise some_was_live will be clear
{
for (i = regno_first; i <= regno_last; i++)
regs_ever_live[i] = 1;
+ if (flags & PROP_ASM_SCAN)
+ for (i = regno_first; i <= regno_last; i++)
+ regs_asm_clobbered[i] = 1;
}
else
{
in ASM_OPERANDs. If these registers get replaced,
we might wind up changing the semantics of the insn,
even if reload can make what appear to be valid
- assignments later. */
+ assignments later.
+
+ We don't build a LOG_LINK for global registers to
+ or from a function call. We don't want to let
+ combine think that it knows what is going on with
+ global registers. */
if (y && (BLOCK_NUM (y) == blocknum)
&& (regno_first >= FIRST_PSEUDO_REGISTER
- || asm_noperands (PATTERN (y)) < 0))
+ || (asm_noperands (PATTERN (y)) < 0
+ && ! ((GET_CODE (insn) == CALL_INSN
+ || GET_CODE (y) == CALL_INSN)
+ && global_regs[regno_first]))))
LOG_LINKS (y) = alloc_INSN_LIST (insn, LOG_LINKS (y));
}
}
{
for (i = regno_first; i <= regno_last; ++i)
if (!(not_dead & (((unsigned long) 1) << (i - regno_first))))
- CLEAR_REGNO_REG_SET (pbi->reg_live, i);
+ {
+ if ((pbi->flags & PROP_REG_INFO)
+ && REGNO_REG_SET_P (pbi->reg_live, i))
+ {
+ REG_LIVE_LENGTH (i) += pbi->insn_num - reg_deaths[i];
+ reg_deaths[i] = 0;
+ }
+ CLEAR_REGNO_REG_SET (pbi->reg_live, i);
+ }
}
}
else if (GET_CODE (reg) == REG)
{
if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
pbi->reg_next_use[regno_first] = 0;
+
+ if ((flags & PROP_REG_INFO) != 0
+ && (flags & PROP_ASM_SCAN) != 0
+ && regno_first < FIRST_PSEUDO_REGISTER)
+ {
+ for (i = regno_first; i <= regno_last; i++)
+ regs_asm_clobbered[i] = 1;
+ }
}
/* If this is the last pass and this is a SCRATCH, show it will be dying
{
rtx op0, op1;
- if (GET_RTX_CLASS (GET_CODE (old)) == '<')
+ if (COMPARISON_P (old))
{
- if (GET_RTX_CLASS (GET_CODE (x)) == '<'
+ if (COMPARISON_P (x)
&& REVERSE_CONDEXEC_PREDICATES_P (GET_CODE (x), GET_CODE (old))
&& REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0)))
return const1_rtx;
x_code = GET_CODE (x);
if (x_code == NOT)
return XEXP (x, 0);
- if (GET_RTX_CLASS (x_code) == '<'
+ if (COMPARISON_P (x)
&& GET_CODE (XEXP (x, 0)) == REG)
{
if (XEXP (x, 1) != const0_rtx)
{
rtx op0, op1;
- if (GET_RTX_CLASS (GET_CODE (old)) == '<')
+ if (COMPARISON_P (old))
{
- if (GET_RTX_CLASS (GET_CODE (x)) == '<'
+ if (COMPARISON_P (x)
&& GET_CODE (x) == reverse_condition (GET_CODE (old))
&& REGNO (XEXP (x, 0)) == REGNO (XEXP (old, 0)))
return const0_rtx;
{
rtx op0, op1;
- if (GET_RTX_CLASS (GET_CODE (x)) == '<')
+ if (COMPARISON_P (x))
{
if (REGNO (XEXP (x, 0)) == regno)
return const0_rtx;
new insn(s) and do the updates. */
emit_insn_before (insns, insn);
- if (pbi->bb->head == insn)
- pbi->bb->head = insns;
+ if (BB_HEAD (pbi->bb) == insn)
+ BB_HEAD (pbi->bb) = insns;
/* INCR will become a NOTE and INSN won't contain a
use of INCR_REG. If a use of INCR_REG was just placed in
incr_reg = q;
regno = REGNO (q);
+ if ((pbi->flags & PROP_REG_INFO)
+ && !REGNO_REG_SET_P (pbi->reg_live, regno))
+ reg_deaths[regno] = pbi->insn_num;
+
/* REGNO is now used in INCR which is below INSN, but
it previously wasn't live here. If we don't mark
it as live, we'll put a REG_DEAD note for it
{
remove_note (incr, note);
if (XEXP (note, 0) != incr_reg)
- CLEAR_REGNO_REG_SET (pbi->reg_live, REGNO (XEXP (note, 0)));
+ {
+ unsigned int regno = REGNO (XEXP (note, 0));
+
+ if ((pbi->flags & PROP_REG_INFO)
+ && REGNO_REG_SET_P (pbi->reg_live, regno))
+ {
+ REG_LIVE_LENGTH (regno) += pbi->insn_num - reg_deaths[regno];
+ reg_deaths[regno] = 0;
+ }
+ CLEAR_REGNO_REG_SET (pbi->reg_live, REGNO (XEXP (note, 0)));
+ }
}
PUT_CODE (incr, NOTE);
regno_last = regno_first = REGNO (reg);
if (regno_first < FIRST_PSEUDO_REGISTER)
- regno_last += HARD_REGNO_NREGS (regno_first, GET_MODE (reg)) - 1;
+ regno_last += hard_regno_nregs[regno_first][GET_MODE (reg)] - 1;
/* Find out if any of this register is live after this instruction. */
some_was_live = some_was_dead = 0;
REG_FREQ (regno_first) += REG_FREQ_FROM_BB (pbi->bb);
REG_N_REFS (regno_first)++;
}
+ for (i = regno_first; i <= regno_last; ++i)
+ if (! REGNO_REG_SET_P (pbi->reg_live, i))
+ {
+#ifdef ENABLE_CHECKING
+ if (reg_deaths[i])
+ abort ();
+#endif
+ reg_deaths[i] = pbi->insn_num;
+ }
}
/* Record and count the insns in which a reg dies. If it is used in
while (temp)
{
next = XEXP (temp, 1);
- if (anti_dependence (XEXP (temp, 0), x))
+ if (unchanging_anti_dependence (XEXP (temp, 0), x))
{
/* Splice temp out of the list. */
if (prev)
x = COND_EXEC_CODE (x);
goto retry;
- case PHI:
- /* We _do_not_ want to scan operands of phi nodes. Operands of
- a phi function are evaluated only when control reaches this
- block along a particular edge. Therefore, regs that appear
- as arguments to phi should not be added to the global live at
- start. */
- return;
-
default:
break;
}
recompute_reg_usage (rtx f ATTRIBUTE_UNUSED, int loop_step ATTRIBUTE_UNUSED)
{
allocate_reg_life_data ();
- update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO);
+ /* distribute_notes in combiner fails to convert some of the REG_UNUSED notes
+ to REG_DEAD notes. This causes CHECK_DEAD_NOTES in sched1 to abort. To
+ solve this update the DEATH_NOTES here. */
+ update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO | PROP_DEATH_NOTES);
}
/* Optionally removes all the REG_DEAD and REG_UNUSED notes from a set of
count_or_remove_death_notes (sbitmap blocks, int kill)
{
int count = 0;
+ int i;
basic_block bb;
- FOR_EACH_BB_REVERSE (bb)
+ /* This used to be a loop over all the blocks with a membership test
+ inside the loop. That can be amazingly expensive on a large CFG
+ when only a small number of bits are set in BLOCKs (for example,
+ the calls from the scheduler typically have very few bits set).
+
+ For extra credit, someone should convert BLOCKS to a bitmap rather
+ than an sbitmap. */
+ if (blocks)
{
- rtx insn;
+ EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
+ {
+ count += count_or_remove_death_notes_bb (BASIC_BLOCK (i), kill);
+ });
+ }
+ else
+ {
+ FOR_EACH_BB (bb)
+ {
+ count += count_or_remove_death_notes_bb (bb, kill);
+ }
+ }
+
+ return count;
+}
+
+/* Optionally removes all the REG_DEAD and REG_UNUSED notes from basic
+ block BB. Returns a count of the number of registers that died. */
- if (blocks && ! TEST_BIT (blocks, bb->index))
- continue;
+static int
+count_or_remove_death_notes_bb (basic_block bb, int kill)
+{
+ int count = 0;
+ rtx insn;
- for (insn = bb->head;; insn = NEXT_INSN (insn))
+ for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
{
- if (INSN_P (insn))
- {
- rtx *pprev = ®_NOTES (insn);
- rtx link = *pprev;
+ rtx *pprev = ®_NOTES (insn);
+ rtx link = *pprev;
- while (link)
+ while (link)
+ {
+ switch (REG_NOTE_KIND (link))
{
- switch (REG_NOTE_KIND (link))
+ case REG_DEAD:
+ if (GET_CODE (XEXP (link, 0)) == REG)
+ {
+ rtx reg = XEXP (link, 0);
+ int n;
+
+ if (REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ n = 1;
+ else
+ n = hard_regno_nregs[REGNO (reg)][GET_MODE (reg)];
+ count += n;
+ }
+
+ /* Fall through. */
+
+ case REG_UNUSED:
+ if (kill)
{
- case REG_DEAD:
- if (GET_CODE (XEXP (link, 0)) == REG)
- {
- rtx reg = XEXP (link, 0);
- int n;
-
- if (REGNO (reg) >= FIRST_PSEUDO_REGISTER)
- n = 1;
- else
- n = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg));
- count += n;
- }
- /* Fall through. */
-
- case REG_UNUSED:
- if (kill)
- {
- rtx next = XEXP (link, 1);
- free_EXPR_LIST_node (link);
- *pprev = link = next;
- break;
- }
- /* Fall through. */
-
- default:
- pprev = &XEXP (link, 1);
- link = *pprev;
+ rtx next = XEXP (link, 1);
+ free_EXPR_LIST_node (link);
+ *pprev = link = next;
break;
}
+ /* Fall through. */
+
+ default:
+ pprev = &XEXP (link, 1);
+ link = *pprev;
+ break;
}
}
-
- if (insn == bb->end)
- break;
}
+
+ if (insn == BB_END (bb))
+ break;
}
return count;
}
+
/* Clear LOG_LINKS fields of insns in a selected blocks or whole chain
if blocks is NULL. */
{
basic_block bb = BASIC_BLOCK (i);
- for (insn = bb->head; insn != NEXT_INSN (bb->end);
+ for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
insn = NEXT_INSN (insn))
if (INSN_P (insn))
free_INSN_LIST_list (&LOG_LINKS (insn));