You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
/* This file contains the data flow analysis pass of the compiler. It
computes data flow information which tells combine_instructions
life_analysis fills in certain vectors containing information about
register usage: REG_N_REFS, REG_N_DEATHS, REG_N_SETS, REG_LIVE_LENGTH,
- REG_N_CALLS_CROSSED and REG_BASIC_BLOCK.
+ REG_N_CALLS_CROSSED, REG_N_THROWING_CALLS_CROSSED and REG_BASIC_BLOCK.
life_analysis sets current_function_sp_is_unchanging if the function
doesn't modify the stack pointer. */
#include "obstack.h"
#include "splay-tree.h"
+#include "tree-pass.h"
#ifndef HAVE_epilogue
#define HAVE_epilogue 0
allocate_bb_life_data ();
/* Find the set of registers live on function exit. */
- mark_regs_live_at_end (EXIT_BLOCK_PTR->global_live_at_start);
+ mark_regs_live_at_end (EXIT_BLOCK_PTR->il.rtl->global_live_at_start);
/* "Update" life info from zero. It'd be nice to begin the
relaxation with just the exit and noreturn blocks, but that set
{
/* After reload, there are no pseudos, nor subregs of multi-word
registers. The regsets should exactly match. */
- if (! REG_SET_EQUAL_P (new_live_at_start, bb->global_live_at_start))
+ if (! REG_SET_EQUAL_P (new_live_at_start,
+ bb->il.rtl->global_live_at_start))
{
if (dump_file)
{
reg_set_iterator rsi;
/* Find the set of changed registers. */
- XOR_REG_SET (new_live_at_start, bb->global_live_at_start);
+ XOR_REG_SET (new_live_at_start, bb->il.rtl->global_live_at_start);
EXECUTE_IF_SET_IN_REG_SET (new_live_at_start, 0, i, rsi)
{
/* No registers should die. */
- if (REGNO_REG_SET_P (bb->global_live_at_start, i))
+ if (REGNO_REG_SET_P (bb->il.rtl->global_live_at_start, i))
{
if (dump_file)
{
int prop_flags)
{
regset tmp;
- unsigned i;
+ unsigned i = 0;
int stabilized_prop_flags = prop_flags;
basic_block bb;
in turn may allow for further dead code detection / removal. */
FOR_EACH_BB_REVERSE (bb)
{
- COPY_REG_SET (tmp, bb->global_live_at_end);
+ COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end);
changed |= propagate_block (bb, tmp, NULL, NULL,
prop_flags & (PROP_SCAN_DEAD_CODE
| PROP_SCAN_DEAD_STORES
in the code being marked live at entry. */
FOR_EACH_BB (bb)
{
- CLEAR_REG_SET (bb->global_live_at_start);
- CLEAR_REG_SET (bb->global_live_at_end);
+ CLEAR_REG_SET (bb->il.rtl->global_live_at_start);
+ CLEAR_REG_SET (bb->il.rtl->global_live_at_end);
}
}
if (blocks)
{
- EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
+ sbitmap_iterator sbi;
+
+ EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi)
{
bb = BASIC_BLOCK (i);
- COPY_REG_SET (tmp, bb->global_live_at_end);
+ COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end);
propagate_block (bb, tmp, NULL, NULL, stabilized_prop_flags);
if (extent == UPDATE_LIFE_LOCAL)
verify_local_live_at_start (tmp, bb);
- });
+ };
}
else
{
FOR_EACH_BB_REVERSE (bb)
{
- COPY_REG_SET (tmp, bb->global_live_at_end);
+ COPY_REG_SET (tmp, bb->il.rtl->global_live_at_end);
propagate_block (bb, tmp, NULL, NULL, stabilized_prop_flags);
are those that were not set anywhere in the function. local-alloc
doesn't know how to handle these correctly, so mark them as not
local to any one basic block. */
- EXECUTE_IF_SET_IN_REG_SET (ENTRY_BLOCK_PTR->global_live_at_end,
+ EXECUTE_IF_SET_IN_REG_SET (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end,
FIRST_PSEUDO_REGISTER, i, rsi)
REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL;
label_to_block_map = NULL;
ENTRY_BLOCK_PTR->aux = NULL;
- ENTRY_BLOCK_PTR->global_live_at_end = NULL;
+ ENTRY_BLOCK_PTR->il.rtl->global_live_at_end = NULL;
EXIT_BLOCK_PTR->aux = NULL;
- EXIT_BLOCK_PTR->global_live_at_start = NULL;
+ EXIT_BLOCK_PTR->il.rtl->global_live_at_start = NULL;
}
/* Delete any insns that copy a register to itself. */
In other words, regs that are set only as part of a COND_EXEC. */
regset *cond_local_sets;
- int i;
+ unsigned int i;
/* Some passes used to forget clear aux field of basic block causing
sick behavior here. */
confused by sibling call edges, which crashes reg-stack. */
if (e->flags & EDGE_EH)
bitmap_ior_and_compl_into (new_live_at_end,
- sb->global_live_at_start,
+ sb->il.rtl->global_live_at_start,
invalidated_by_call);
else
- IOR_REG_SET (new_live_at_end, sb->global_live_at_start);
+ IOR_REG_SET (new_live_at_end, sb->il.rtl->global_live_at_start);
/* If a target saves one register in another (instead of on
the stack) the save register will need to be live for EH. */
if (bb == ENTRY_BLOCK_PTR)
{
- COPY_REG_SET (bb->global_live_at_end, new_live_at_end);
+ COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end);
continue;
}
rescan the block. This wouldn't be necessary if we had
precalculated local_live, however with PROP_SCAN_DEAD_CODE
local_live is really dependent on live_at_end. */
- rescan = bitmap_intersect_compl_p (bb->global_live_at_end,
+ rescan = bitmap_intersect_compl_p (bb->il.rtl->global_live_at_end,
new_live_at_end);
if (!rescan)
/* Find the set of changed bits. Take this opportunity
to notice that this set is empty and early out. */
- bitmap_xor (tmp, bb->global_live_at_end, new_live_at_end);
+ bitmap_xor (tmp, bb->il.rtl->global_live_at_end, new_live_at_end);
if (bitmap_empty_p (tmp))
continue;
/* Add to live_at_start the set of all registers in
new_live_at_end that aren't in the old live_at_end. */
- changed = bitmap_ior_and_compl_into (bb->global_live_at_start,
+ changed = bitmap_ior_and_compl_into (bb->il.rtl->global_live_at_start,
new_live_at_end,
- bb->global_live_at_end);
- COPY_REG_SET (bb->global_live_at_end, new_live_at_end);
+ bb->il.rtl->global_live_at_end);
+ COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end);
if (! changed)
continue;
}
else
{
- COPY_REG_SET (bb->global_live_at_end, new_live_at_end);
+ COPY_REG_SET (bb->il.rtl->global_live_at_end, new_live_at_end);
/* Rescan the block insn by insn to turn (a copy of) live_at_end
into live_at_start. */
flags);
/* If live_at start didn't change, no need to go farther. */
- if (REG_SET_EQUAL_P (bb->global_live_at_start, new_live_at_end))
+ if (REG_SET_EQUAL_P (bb->il.rtl->global_live_at_start,
+ new_live_at_end))
continue;
if (failure_strategy_required)
{
/* Get the list of registers that were removed from the
bb->global_live_at_start set. */
- bitmap_and_compl (tmp, bb->global_live_at_start,
+ bitmap_and_compl (tmp, bb->il.rtl->global_live_at_start,
new_live_at_end);
if (!bitmap_empty_p (tmp))
{
pbb_changed = false;
pbb_changed
- |= bitmap_and_compl_into (pbb->global_live_at_start,
- registers_made_dead);
+ |= bitmap_and_compl_into
+ (pbb->il.rtl->global_live_at_start,
+ registers_made_dead);
pbb_changed
- |= bitmap_and_compl_into (pbb->global_live_at_end,
- registers_made_dead);
+ |= bitmap_and_compl_into
+ (pbb->il.rtl->global_live_at_end,
+ registers_made_dead);
if (!pbb_changed)
continue;
}
} /* end of failure_strategy_required */
- COPY_REG_SET (bb->global_live_at_start, new_live_at_end);
+ COPY_REG_SET (bb->il.rtl->global_live_at_start, new_live_at_end);
}
/* Queue all predecessors of BB so that we may re-examine
if (blocks_out)
{
- EXECUTE_IF_SET_IN_SBITMAP (blocks_out, 0, i,
+ sbitmap_iterator sbi;
+
+ EXECUTE_IF_SET_IN_SBITMAP (blocks_out, 0, i, sbi)
{
basic_block bb = BASIC_BLOCK (i);
FREE_REG_SET (local_sets[bb->index - (INVALID_BLOCK + 1)]);
FREE_REG_SET (cond_local_sets[bb->index - (INVALID_BLOCK + 1)]);
- });
+ };
}
else
{
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
{
basic_block bb = e->dest;
- regset map = bb->global_live_at_start;
+ regset map = bb->il.rtl->global_live_at_start;
reg_set_iterator rsi;
EXECUTE_IF_SET_IN_REG_SET (map, FIRST_PSEUDO_REGISTER, reg, rsi)
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{
- bb->global_live_at_start = ALLOC_REG_SET (®_obstack);
- bb->global_live_at_end = ALLOC_REG_SET (®_obstack);
+ bb->il.rtl->global_live_at_start = ALLOC_REG_SET (®_obstack);
+ bb->il.rtl->global_live_at_end = ALLOC_REG_SET (®_obstack);
}
regs_live_at_setjmp = ALLOC_REG_SET (®_obstack);
REG_N_REFS (i) = 0;
REG_N_DEATHS (i) = 0;
REG_N_CALLS_CROSSED (i) = 0;
+ REG_N_THROWING_CALLS_CROSSED (i) = 0;
REG_LIVE_LENGTH (i) = 0;
REG_FREQ (i) = 0;
REG_BASIC_BLOCK (i) = REG_BLOCK_UNKNOWN;
fatal_insn ("Attempt to delete prologue/epilogue insn:", insn);
/* Record sets. Do this even for dead instructions, since they
- would have killed the values if they hadn't been deleted. */
+ would have killed the values if they hadn't been deleted. To
+ be consistent, we also have to emit a clobber when we delete
+ an insn that clobbers a live register. */
+ pbi->flags |= PROP_DEAD_INSN;
mark_set_regs (pbi, PATTERN (insn), insn);
+ pbi->flags &= ~PROP_DEAD_INSN;
/* CC0 is now known to be dead. Either this insn used it,
in which case it doesn't anymore, or clobbered it,
}
else
{
- rtx note;
/* Any regs live at the time of a call instruction must not go
in a register clobbered by calls. Find all regs now live and
record this for them. */
reg_set_iterator rsi;
EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi)
REG_N_CALLS_CROSSED (i)++;
+ if (can_throw_internal (insn))
+ EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i, rsi)
+ REG_N_THROWING_CALLS_CROSSED (i)++;
}
/* Record sets. Do this even for dead instructions, since they
except for return values. */
sibcall_p = SIBLING_CALL_P (insn);
- live_at_end = EXIT_BLOCK_PTR->global_live_at_start;
+ live_at_end = EXIT_BLOCK_PTR->il.rtl->global_live_at_start;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)
&& ! (sibcall_p
/* Record uses. */
if (! insn_is_dead)
mark_used_regs (pbi, PATTERN (insn), NULL_RTX, insn);
- if ((flags & PROP_EQUAL_NOTES)
- && ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX))
- || (note = find_reg_note (insn, REG_EQUIV, NULL_RTX))))
- mark_used_regs (pbi, XEXP (note, 0), NULL_RTX, insn);
/* Sometimes we may have inserted something before INSN (such as a move)
when we make an auto-inc. So ensure we will scan those insns. */
}
/* Compute which register lead different lives in the successors. */
- bitmap_xor (diff, bb_true->global_live_at_start,
- bb_false->global_live_at_start);
+ bitmap_xor (diff, bb_true->il.rtl->global_live_at_start,
+ bb_false->il.rtl->global_live_at_start);
if (!bitmap_empty_p (diff))
{
rcli = xmalloc (sizeof (*rcli));
- if (REGNO_REG_SET_P (bb_true->global_live_at_start, i))
+ if (REGNO_REG_SET_P (bb_true->il.rtl->global_live_at_start,
+ i))
cond = cond_false;
else
cond = cond_true;
return 0;
return ((REG_N_SETS (regno) > 1
- || REGNO_REG_SET_P (ENTRY_BLOCK_PTR->global_live_at_end, regno))
+ || REGNO_REG_SET_P (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end,
+ regno))
&& REGNO_REG_SET_P (regs_live_at_setjmp, regno));
}
\f
}
CLEAR_REGNO_REG_SET (pbi->reg_live, i);
}
+ if (flags & PROP_DEAD_INSN)
+ emit_insn_after (gen_rtx_CLOBBER (VOIDmode, reg), insn);
}
}
else if (REG_P (reg))
that REGNO now crosses them. */
for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
if (CALL_P (temp))
- REG_N_CALLS_CROSSED (regno)++;
+ {
+ REG_N_CALLS_CROSSED (regno)++;
+ if (can_throw_internal (temp))
+ REG_N_THROWING_CALLS_CROSSED (regno)++;
+ }
/* Invalidate alias info for Q since we just changed its value. */
clear_reg_alias_info (q);
recompute_reg_usage (void)
{
allocate_reg_life_data ();
- /* 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. */
+ /* 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 die. To solve this update the DEATH_NOTES
+ here. */
update_life_info (NULL, UPDATE_LIFE_LOCAL, PROP_REG_INFO | PROP_DEATH_NOTES);
+
+ if (dump_file)
+ dump_flow_info (dump_file);
}
+struct tree_opt_pass pass_recompute_reg_usage =
+{
+ "life2", /* name */
+ NULL, /* gate */
+ recompute_reg_usage, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func, /* todo_flags_finish */
+ 'f' /* letter */
+};
+
/* Optionally removes all the REG_DEAD and REG_UNUSED notes from a set of
blocks. If BLOCKS is NULL, assume the universal set. Returns a count
of the number of registers that died. */
count_or_remove_death_notes (sbitmap blocks, int kill)
{
int count = 0;
- int i;
+ unsigned int i = 0;
basic_block bb;
/* This used to be a loop over all the blocks with a membership test
than an sbitmap. */
if (blocks)
{
- EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
+ sbitmap_iterator sbi;
+
+ EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi)
{
count += count_or_remove_death_notes_bb (BASIC_BLOCK (i), kill);
- });
+ };
}
else
{
clear_log_links (sbitmap blocks)
{
rtx insn;
- int i;
if (!blocks)
{
free_INSN_LIST_list (&LOG_LINKS (insn));
}
else
- EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
- {
- basic_block bb = BASIC_BLOCK (i);
+ {
+ unsigned int i = 0;
+ sbitmap_iterator sbi;
+
+ EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i, sbi)
+ {
+ basic_block bb = BASIC_BLOCK (i);
- 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));
- });
+ 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));
+ }
+ }
}
/* Given a register bitmap, turn on the bits in a HARD_REG_SET that
SET_HARD_REG_BIT (*to, i);
}
}
+\f
+
+static bool
+gate_remove_death_notes (void)
+{
+ return flag_profile_values;
+}
+
+static void
+rest_of_handle_remove_death_notes (void)
+{
+ count_or_remove_death_notes (NULL, 1);
+}
+
+struct tree_opt_pass pass_remove_death_notes =
+{
+ "ednotes", /* name */
+ gate_remove_death_notes, /* gate */
+ rest_of_handle_remove_death_notes, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ 0, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+ 0 /* letter */
+};
+
+/* Perform life analysis. */
+static void
+rest_of_handle_life (void)
+{
+ regclass_init ();
+
+ life_analysis (dump_file, PROP_FINAL);
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE | CLEANUP_LOG_LINKS
+ | (flag_thread_jumps ? CLEANUP_THREADING : 0));
+
+ if (extra_warnings)
+ {
+ setjmp_vars_warning (DECL_INITIAL (current_function_decl));
+ setjmp_args_warning ();
+ }
+
+ if (optimize)
+ {
+ if (initialize_uninitialized_subregs ())
+ {
+ /* Insns were inserted, and possibly pseudos created, so
+ things might look a bit different. */
+ allocate_reg_life_data ();
+ update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES,
+ PROP_LOG_LINKS | PROP_REG_INFO | PROP_DEATH_NOTES);
+ }
+ }
+
+ no_new_pseudos = 1;
+}
+
+struct tree_opt_pass pass_life =
+{
+ "life1", /* name */
+ NULL, /* gate */
+ rest_of_handle_life, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_FLOW, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ TODO_verify_flow, /* todo_flags_start */
+ TODO_dump_func |
+ TODO_ggc_collect, /* todo_flags_finish */
+ 'f' /* letter */
+};
+
+static void
+rest_of_handle_flow2 (void)
+{
+ /* If optimizing, then go ahead and split insns now. */
+#ifndef STACK_REGS
+ if (optimize > 0)
+#endif
+ split_all_insns (0);
+
+ if (flag_branch_target_load_optimize)
+ branch_target_load_optimize (epilogue_completed);
+
+ if (optimize)
+ cleanup_cfg (CLEANUP_EXPENSIVE);
+
+ /* On some machines, the prologue and epilogue code, or parts thereof,
+ can be represented as RTL. Doing so lets us schedule insns between
+ it and the rest of the code and also allows delayed branch
+ scheduling to operate in the epilogue. */
+ thread_prologue_and_epilogue_insns (get_insns ());
+ epilogue_completed = 1;
+ flow2_completed = 1;
+}
+
+struct tree_opt_pass pass_flow2 =
+{
+ "flow2", /* name */
+ NULL, /* gate */
+ rest_of_handle_flow2, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_FLOW2, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ TODO_verify_flow, /* todo_flags_start */
+ TODO_dump_func |
+ TODO_ggc_collect, /* todo_flags_finish */
+ 'w' /* letter */
+};
+