* basic-block.h (CLEANUP_PRE_SIBCALL): New constant.
* except.c (finish_eh_generation): Update call of cleanup_cfg;
do rebuild_jump_labels instead of jump_optimize
* sibcall.c (optimize_sibling_and_tail_recursive_call): Likewise.
* toplev.c (rest_of_compulation): Likewise for -Wreturn_type.
* flow.c (try_optimize_cfg): Remove unneeded code_labels.
* flow.c: Include timevar.h
(find_basic_block): Push/pop timevar;
(cleanup_cfg): Likewise.
* timevar.def (TV_CFG, TV_CLEANUP_CFG): New.
* Makefile: Add dependencies on timevar.h
* integrate.c (save_for_inline): Kill all BASIC_BLOCK notes.
(copy_insn_list): Avoid killing of BASIC_BLOCK notes.
* rtl.h (delete_trivially_dead_insns): Add new parameter.
* toplev.c (rest_of_compilation): Update calls.
* cse.c (set_live_p, insn_live_p, dead_libcall_p): Break out from ...
(delete_trivially_dead_insns): ... here; accept new argument
preserve_basic_blocks; preserve basic blocks if set.
* reg-stack.c (stack_regs_mentioned): Return 0 if
stack_regs_mentioned_data is not initialized.
(reg_to_stack): Make stack_regs_mentioned survive after the
reg-stack is completted; do not call cleanup_cfg.
* toplev.c (rest_of_compilation): Do cleanup_cfg before bb-reorder;
make cleanup_cfg after bb-reorder to output to debug file.
From-SVN: r44056
+Mon Jul 16 22:48:00 CEST 2001 Jan Hubicka <jh@suse.cz>
+
+ * basic-block.h (CLEANUP_PRE_SIBCALL): New constant.
+ * except.c (finish_eh_generation): Update call of cleanup_cfg;
+ do rebuild_jump_labels instead of jump_optimize
+ * sibcall.c (optimize_sibling_and_tail_recursive_call): Likewise.
+ * toplev.c (rest_of_compulation): Likewise for -Wreturn_type.
+ * flow.c (try_optimize_cfg): Remove unneeded code_labels.
+
+ * flow.c: Include timevar.h
+ (find_basic_block): Push/pop timevar;
+ (cleanup_cfg): Likewise.
+ * timevar.def (TV_CFG, TV_CLEANUP_CFG): New.
+ * Makefile: Add dependencies on timevar.h
+
+ * integrate.c (save_for_inline): Kill all BASIC_BLOCK notes.
+ (copy_insn_list): Avoid killing of BASIC_BLOCK notes.
+
+ * rtl.h (delete_trivially_dead_insns): Add new parameter.
+ * toplev.c (rest_of_compilation): Update calls.
+ * cse.c (set_live_p, insn_live_p, dead_libcall_p): Break out from ...
+ (delete_trivially_dead_insns): ... here; accept new argument
+ preserve_basic_blocks; preserve basic blocks if set.
+
+ * reg-stack.c (stack_regs_mentioned): Return 0 if
+ stack_regs_mentioned_data is not initialized.
+ (reg_to_stack): Make stack_regs_mentioned survive after the
+ reg-stack is completted; do not call cleanup_cfg.
+ * toplev.c (rest_of_compilation): Do cleanup_cfg before bb-reorder;
+ make cleanup_cfg after bb-reorder to output to debug file.
+
2001-07-16 Richard Henderson <rth@redhat.com>
* regclass.c (init_reg_sets): Use only 32 bits per initializer
#define CLEANUP_CROSSJUMP 2 /* Do crossjumping. */
#define CLEANUP_POST_REGSTACK 4 /* We run after reg-stack and need
to care REG_DEAD notes. */
+#define CLEANUP_PRE_SIBCALL 8 /* Do not get confused by code hidden
+ inside call_placeholders.. */
/* Flags for loop discovery. */
#define LOOP_TREE 1 /* Build loop hierarchy tree. */
-/* config.in. Generated automatically from configure.in by autoheader 2.13. */
+/* config.in. Generated automatically from configure.in by autoheader. */
/* Define to empty if the keyword does not work. */
#undef const
static int check_dependence PARAMS ((rtx *, void *));
static void flush_hash_table PARAMS ((void));
+static bool insn_live_p PARAMS ((rtx, int *));
+static bool set_live_p PARAMS ((rtx, int *));
+static bool dead_libcall_p PARAMS ((rtx, int *));
\f
/* Dump the expressions in the equivalence class indicated by CLASSP.
This function is used only for debugging. */
}
}
\f
+/* Return true if set is live. */
+static bool
+set_live_p (set, counts)
+ rtx set;
+ int *counts;
+{
+#ifdef HAVE_cc0
+ rtx tem;
+#endif
+
+ if (set_noop_p (set))
+ ;
+
+#ifdef HAVE_cc0
+ else if (GET_CODE (SET_DEST (set)) == CC0
+ && !side_effects_p (SET_SRC (set))
+ && ((tem = next_nonnote_insn (insn)) == 0
+ || !INSN_P (tem)
+ || !reg_referenced_p (cc0_rtx, PATTERN (tem))))
+ return false;
+#endif
+ else if (GET_CODE (SET_DEST (set)) != REG
+ || REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER
+ || counts[REGNO (SET_DEST (set))] != 0
+ || side_effects_p (SET_SRC (set))
+ /* An ADDRESSOF expression can turn into a use of the
+ internal arg pointer, so always consider the
+ internal arg pointer live. If it is truly dead,
+ flow will delete the initializing insn. */
+ || (SET_DEST (set) == current_function_internal_arg_pointer))
+ return true;
+ return false;
+}
+
+/* Return true if insn is live. */
+
+static bool
+insn_live_p (insn, counts)
+ rtx insn;
+ int *counts;
+{
+ int i;
+ if (GET_CODE (PATTERN (insn)) == SET)
+ return set_live_p (PATTERN (insn), counts);
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ {
+ rtx elt = XVECEXP (PATTERN (insn), 0, i);
+
+ if (GET_CODE (elt) == SET)
+ {
+ if (set_live_p (elt, counts))
+ return true;
+ }
+ else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
+ return true;
+ }
+ else
+ return true;
+}
+
+/* Return true if libcall is dead as a whole. */
+
+static bool
+dead_libcall_p (insn, counts)
+ rtx insn;
+ int *counts;
+{
+ rtx note;
+ /* See if there's a REG_EQUAL note on this insn and try to
+ replace the source with the REG_EQUAL expression.
+
+ We assume that insns with REG_RETVALs can only be reg->reg
+ copies at this point. */
+ note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+ if (note)
+ {
+ rtx set = single_set (insn);
+ rtx new = simplify_rtx (XEXP (note, 0));
+
+ if (!new)
+ new = XEXP (note, 0);
+
+ if (set && validate_change (insn, &SET_SRC (set), new, 0))
+ {
+ remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
+ return true;
+ }
+ }
+ return false;
+}
+
/* Scan all the insns and delete any that are dead; i.e., they store a register
that is never used or they copy a register to itself.
remaining passes of the compilation are also sped up. */
void
-delete_trivially_dead_insns (insns, nreg)
+delete_trivially_dead_insns (insns, nreg, preserve_basic_blocks)
rtx insns;
int nreg;
+ int preserve_basic_blocks;
{
int *counts;
rtx insn, prev;
-#ifdef HAVE_cc0
- rtx tem;
-#endif
int i;
int in_libcall = 0, dead_libcall = 0;
+ basic_block bb;
/* First count the number of times each register is used. */
counts = (int *) xcalloc (nreg, sizeof (int));
if (! INSN_P (insn))
insn = prev_real_insn (insn);
- for (; insn; insn = prev)
- {
- int live_insn = 0;
- rtx note;
-
- prev = prev_real_insn (insn);
+ if (!preserve_basic_blocks)
+ for (; insn; insn = prev)
+ {
+ int live_insn = 0;
+ rtx note;
- /* Don't delete any insns that are part of a libcall block unless
- we can delete the whole libcall block.
+ prev = prev_real_insn (insn);
- Flow or loop might get confused if we did that. Remember
- that we are scanning backwards. */
- if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
- {
- in_libcall = 1;
- live_insn = 1;
- dead_libcall = 0;
+ /* Don't delete any insns that are part of a libcall block unless
+ we can delete the whole libcall block.
- /* See if there's a REG_EQUAL note on this insn and try to
- replace the source with the REG_EQUAL expression.
+ Flow or loop might get confused if we did that. Remember
+ that we are scanning backwards. */
+ if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ {
+ in_libcall = 1;
+ live_insn = 1;
+ dead_libcall = dead_libcall_p (insn, counts);
+ }
+ else if (in_libcall)
+ live_insn = ! dead_libcall;
+ else
+ live_insn = insn_live_p (insn, counts);
- We assume that insns with REG_RETVALs can only be reg->reg
- copies at this point. */
- note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
- if (note)
- {
- rtx set = single_set (insn);
- rtx new = simplify_rtx (XEXP (note, 0));
+ /* If this is a dead insn, delete it and show registers in it aren't
+ being used. */
- if (!new)
- new = XEXP (note, 0);
+ if (! live_insn)
+ {
+ count_reg_usage (insn, counts, NULL_RTX, -1);
+ delete_insn (insn);
+ }
- if (set && validate_change (insn, &SET_SRC (set), new, 0))
- {
- remove_note (insn,
- find_reg_note (insn, REG_RETVAL, NULL_RTX));
- dead_libcall = 1;
- }
- }
- }
- else if (in_libcall)
- live_insn = ! dead_libcall;
- else if (GET_CODE (PATTERN (insn)) == SET)
+ if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+ {
+ in_libcall = 0;
+ dead_libcall = 0;
+ }
+ }
+ else
+ for (i = 0; i < n_basic_blocks; i++)
+ for (bb = BASIC_BLOCK (i), insn = bb->end; insn != bb->head; insn = prev)
{
- if (set_noop_p (PATTERN (insn)))
- ;
+ int live_insn = 0;
+ rtx note;
-#ifdef HAVE_cc0
- else if (GET_CODE (SET_DEST (PATTERN (insn))) == CC0
- && ! side_effects_p (SET_SRC (PATTERN (insn)))
- && ((tem = next_nonnote_insn (insn)) == 0
- || ! INSN_P (tem)
- || ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
- ;
-#endif
- else if (GET_CODE (SET_DEST (PATTERN (insn))) != REG
- || REGNO (SET_DEST (PATTERN (insn))) < FIRST_PSEUDO_REGISTER
- || counts[REGNO (SET_DEST (PATTERN (insn)))] != 0
- || side_effects_p (SET_SRC (PATTERN (insn)))
- /* An ADDRESSOF expression can turn into a use of the
- internal arg pointer, so always consider the
- internal arg pointer live. If it is truly dead,
- flow will delete the initializing insn. */
- || (SET_DEST (PATTERN (insn))
- == current_function_internal_arg_pointer))
- live_insn = 1;
- }
- else if (GET_CODE (PATTERN (insn)) == PARALLEL)
- for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
- {
- rtx elt = XVECEXP (PATTERN (insn), 0, i);
+ prev = PREV_INSN (insn);
+ if (!INSN_P (insn))
+ continue;
- if (GET_CODE (elt) == SET)
- {
- if (set_noop_p (elt))
- ;
+ /* Don't delete any insns that are part of a libcall block unless
+ we can delete the whole libcall block.
-#ifdef HAVE_cc0
- else if (GET_CODE (SET_DEST (elt)) == CC0
- && ! side_effects_p (SET_SRC (elt))
- && ((tem = next_nonnote_insn (insn)) == 0
- || ! INSN_P (tem)
- || ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
- ;
-#endif
- else if (GET_CODE (SET_DEST (elt)) != REG
- || REGNO (SET_DEST (elt)) < FIRST_PSEUDO_REGISTER
- || counts[REGNO (SET_DEST (elt))] != 0
- || side_effects_p (SET_SRC (elt))
- /* An ADDRESSOF expression can turn into a use of the
- internal arg pointer, so always consider the
- internal arg pointer live. If it is truly dead,
- flow will delete the initializing insn. */
- || (SET_DEST (elt)
- == current_function_internal_arg_pointer))
- live_insn = 1;
- }
- else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
+ Flow or loop might get confused if we did that. Remember
+ that we are scanning backwards. */
+ if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ {
+ in_libcall = 1;
live_insn = 1;
- }
- else
- live_insn = 1;
+ dead_libcall = dead_libcall_p (insn, counts);
+ }
+ else if (in_libcall)
+ live_insn = ! dead_libcall;
+ else
+ live_insn = insn_live_p (insn, counts);
- /* If this is a dead insn, delete it and show registers in it aren't
- being used. */
+ /* If this is a dead insn, delete it and show registers in it aren't
+ being used. */
- if (! live_insn)
- {
- count_reg_usage (insn, counts, NULL_RTX, -1);
- delete_insn (insn);
- }
+ if (! live_insn)
+ {
+ count_reg_usage (insn, counts, NULL_RTX, -1);
+ if (insn == bb->end)
+ bb->end = PREV_INSN (insn);
+ flow_delete_insn (insn);
+ }
- if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
- {
- in_libcall = 0;
- dead_libcall = 0;
+ if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+ {
+ in_libcall = 0;
+ dead_libcall = 0;
+ }
}
- }
/* Clean up. */
free (counts);
connect many of the handlers, and then type information will not
be effective. Still, this is a win over previous implementations. */
- jump_optimize_minimal (get_insns ());
+ rebuild_jump_labels (get_insns ());
find_basic_blocks (get_insns (), max_reg_num (), 0);
cleanup_cfg (0);
/* We've totally changed the CFG. Start over. */
find_exception_handler_labels ();
- jump_optimize_minimal (get_insns ());
+ rebuild_jump_labels (get_insns ());
find_basic_blocks (get_insns (), max_reg_num (), 0);
cleanup_cfg (0);
}
#include "recog.h"
#include "expr.h"
#include "ssa.h"
+#include "timevar.h"
#include "obstack.h"
#include "splay-tree.h"
FILE *file ATTRIBUTE_UNUSED;
{
int max_uid;
+ timevar_push (TV_CFG);
/* Flush out existing data. */
if (basic_block_info != NULL)
#ifdef ENABLE_CHECKING
verify_flow_info ();
#endif
+ timevar_pop (TV_CFG);
}
void
cleanup_cfg (mode)
int mode;
{
+ timevar_push (TV_CLEANUP_CFG);
delete_unreachable_blocks ();
if (try_optimize_cfg (mode))
delete_unreachable_blocks ();
/* Kill the data we won't maintain. */
free_EXPR_LIST_list (&label_value_list);
free_EXPR_LIST_list (&tail_recursion_label_list);
+ timevar_pop (TV_CLEANUP_CFG);
}
/* Create a new basic block consisting of the instructions between
int c_has_outgoing_fallthru;
int b_has_incoming_fallthru;
+ /* Avoid overactive code motion, as the forwarder blocks should eb
+ eliminated by the edge redirection instead. Only exception is the
+ case b is an forwarder block and c has no fallthru edge, but no
+ optimizers should be confused by this extra jump and we are about
+ to kill the jump in bb_reorder pass instead. */
+ if (forwarder_block_p (b) || forwarder_block_p (c))
+ return 0;
+
/* We must make sure to not munge nesting of exception regions,
lexical blocks, and loop notes.
b = c;
}
+ /* Remove code labels no longer used.
+ Don't do the optimization before sibling calls are discovered,
+ as some branches may be hidden inside CALL_PLACEHOLDERs. */
+ if (!(mode & CLEANUP_PRE_SIBCALL)
+ && b->pred->pred_next == NULL
+ && (b->pred->flags & EDGE_FALLTHRU)
+ && GET_CODE (b->head) == CODE_LABEL
+ /* If previous block does end with condjump jumping to next BB,
+ we can't delete the label. */
+ && (b->pred->src == ENTRY_BLOCK_PTR
+ || !reg_mentioned_p (b->head, b->pred->src->end)))
+ {
+ rtx label = b->head;
+ b->head = NEXT_INSN (b->head);
+ flow_delete_insn_chain (label, label);
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file, "Deleted label in block %i.\n",
+ b->index);
+ }
+
/* A loop because chains of blocks might be combineable. */
while ((s = b->succ) != NULL
&& s->succ_next == NULL
argvec = initialize_for_inline (fndecl);
+ /* Delete basic block notes created by early run of find_basic_block.
+ The notes would be later used by find_basic_blocks to reuse the memory
+ for basic_block structures on already freed obstack. */
+ for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK)
+ delete_insn (insn);
+
/* If there are insns that copy parms from the stack into pseudo registers,
those insns are not copied. `expand_inline_function' must
emit the correct code to handle such things. */
discarded because it is important to have only one of
each in the current function.
- NOTE_INSN_DELETED notes aren't useful.
-
- NOTE_INSN_BASIC_BLOCK is discarded because the saved bb
- pointer (which will soon be dangling) confuses flow's
- attempts to preserve bb structures during the compilation
- of a function. */
+ NOTE_INSN_DELETED notes aren't useful. */
if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BASIC_BLOCK)
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
{
copy = emit_note (NOTE_SOURCE_FILE (insn),
NOTE_LINE_NUMBER (insn));
unsigned int uid, max;
int test;
- if (! INSN_P (insn))
+ if (! INSN_P (insn) || !stack_regs_mentioned_data)
return 0;
uid = INSN_UID (insn);
int max_uid;
block_info bi;
+ /* Clean up previous run. */
+ if (stack_regs_mentioned_data)
+ {
+ VARRAY_FREE (stack_regs_mentioned_data);
+ stack_regs_mentioned_data = 0;
+ }
+
if (!optimize)
split_all_insns (0);
VARRAY_CHAR_INIT (stack_regs_mentioned_data, max_uid + 1,
"stack_regs_mentioned cache");
- if (convert_regs (file) && optimize)
- cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_CROSSJUMP | CLEANUP_POST_REGSTACK);
+ convert_regs (file);
- /* Clean up. */
- VARRAY_FREE (stack_regs_mentioned_data);
free (bi);
}
\f
extern int rtx_cost PARAMS ((rtx, enum rtx_code));
extern int address_cost PARAMS ((rtx, enum machine_mode));
-extern void delete_trivially_dead_insns PARAMS ((rtx, int));
+extern void delete_trivially_dead_insns PARAMS ((rtx, int, int));
#ifdef BUFSIZ
extern int cse_main PARAMS ((rtx, int, int, FILE *));
#endif
ahead and find all the EH labels. */
find_exception_handler_labels ();
- jump_optimize_minimal (insns);
+ rebuild_jump_labels (insns);
/* We need cfg information to determine which blocks are succeeded
only by the epilogue. */
find_basic_blocks (insns, max_reg_num (), 0);
- cleanup_cfg (0);
+ cleanup_cfg (CLEANUP_PRE_SIBCALL);
/* If there are no basic blocks, then there is nothing to do. */
if (n_basic_blocks == 0)
/* Time spent generating dump files. */
DEFTIMEVAR (TV_DUMP , "dump files")
+/* Time spent by constructing CFG. */
+DEFTIMEVAR (TV_CFG , "cfg construction")
+/* Time spent by cleaning up CFG. */
+DEFTIMEVAR (TV_CLEANUP_CFG , "cfg cleanup")
/* Timing in various stages of the compiler. */
DEFTIMEVAR (TV_CPP , "preprocessing")
DEFTIMEVAR (TV_LEX , "lexical analysis")
if (DECL_DEFER_OUTPUT (decl))
{
/* If -Wreturn-type, we have to do a bit of compilation. We just
- want to call jump_optimize to figure out whether or not we can
+ want to call cleanup the cfg to figure out whether or not we can
fall off the end of the function; we do the minimum amount of
- work necessary to make that safe. And, we set optimize to zero
- to keep jump_optimize from working too hard. */
+ work necessary to make that safe. */
if (warn_return_type)
{
int saved_optimize = optimize;
optimize = 0;
+ rebuild_jump_labels (insns);
find_exception_handler_labels ();
- jump_optimize (insns, !JUMP_NOOP_MOVES, !JUMP_AFTER_REGSCAN);
+ find_basic_blocks (insns, max_reg_num (), rtl_dump_file);
+ cleanup_cfg (CLEANUP_PRE_SIBCALL);
optimize = saved_optimize;
}
/* Run this after jump optmizations remove all the unreachable code
so that unreachable code will not keep values live. */
- delete_trivially_dead_insns (insns, max_reg_num ());
+ delete_trivially_dead_insns (insns, max_reg_num (), 0);
/* Try to identify useless null pointer tests and delete them. */
if (flag_delete_null_pointer_checks)
trivially dead. We delete those instructions now in the
hope that doing so will make the heuristics in loop work
better and possibly speed up compilation. */
- delete_trivially_dead_insns (insns, max_reg_num ());
+ delete_trivially_dead_insns (insns, max_reg_num (), 0);
/* The regscan pass is currently necessary as the alias
analysis code depends on this information. */
trivially dead. We delete those instructions now in the
hope that doing so will make the heuristics in jump work
better and possibly speed up compilation. */
- delete_trivially_dead_insns (insns, max_reg_num ());
+ delete_trivially_dead_insns (insns, max_reg_num (), 0);
reg_scan (insns, max_reg_num (), 0);
jump_optimize (insns, !JUMP_NOOP_MOVES, JUMP_AFTER_REGSCAN);
ggc_collect ();
#endif
- if (optimize > 0 && flag_reorder_blocks)
+ if (optimize > 0)
{
timevar_push (TV_REORDER_BLOCKS);
open_dump_file (DFI_bbro, decl);
- reorder_basic_blocks ();
+ /* Last attempt to optimize CFG, as life analyzis possibly removed
+ some instructions. */
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK
+ | CLEANUP_CROSSJUMP);
+ if (flag_reorder_blocks)
+ {
+ reorder_basic_blocks ();
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK);
+ }
close_dump_file (DFI_bbro, print_rtl_with_bb, insns);
- cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_POST_REGSTACK);
timevar_pop (TV_REORDER_BLOCKS);
}