Commit dc2ede84 by Bernd Schmidt Committed by Richard Henderson

flow.c (life_analysis_1): Break out some functions.

Fri Oct  9 15:57:51 1998  Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
        * flow.c (life_analysis_1): Break out some functions.
        (find_basic_blocks_1): Likewise.  Also move some variables out and
        make them static.
        Rename NONLOCAL_LABEL_LIST arg to NONLOCAL_LABELS and initialize
        new static var nonlocal_label_list with it.
        (active_eh_region, nested_eh_region, label_value_list,
        nonlocal_label_list): New static variables.
        (make_edges, delete_unreachable_blocks, delete_block): New static
        functions, broken out of find_basic_blocks_1.
        (record_volatile_insns, mark_regs_live_at_end, set_noop_p,
        noop_move_p): New static functions, broken out of life_analysis_1.

From-SVN: r22963
parent d006aa54
Fri Oct 9 15:57:51 1998 Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
* flow.c (life_analysis_1): Break out some functions.
(find_basic_blocks_1): Likewise. Also move some variables out and
make them static.
Rename NONLOCAL_LABEL_LIST arg to NONLOCAL_LABELS and initialize
new static var nonlocal_label_list with it.
(active_eh_region, nested_eh_region, label_value_list,
nonlocal_label_list): New static variables.
(make_edges, delete_unreachable_blocks, delete_block): New static
functions, broken out of find_basic_blocks_1.
(record_volatile_insns, mark_regs_live_at_end, set_noop_p,
noop_move_p): New static functions, broken out of life_analysis_1.
Fri Oct 9 15:49:29 1998 Richard Henderson <rth@cygnus.com> Fri Oct 9 15:49:29 1998 Richard Henderson <rth@cygnus.com>
* expmed.c (store_bit_field): Pun non-integral str_rtx modes. * expmed.c (store_bit_field): Pun non-integral str_rtx modes.
......
...@@ -233,11 +233,6 @@ static char *basic_block_drops_in; ...@@ -233,11 +233,6 @@ static char *basic_block_drops_in;
static short *basic_block_loop_depth; static short *basic_block_loop_depth;
/* Element N nonzero if basic block N can actually be reached.
Vector exists only during find_basic_blocks. */
static char *block_live_static;
/* Depth within loops of basic block being scanned for lifetime analysis, /* Depth within loops of basic block being scanned for lifetime analysis,
plus one. This is the weight attached to references to registers. */ plus one. This is the weight attached to references to registers. */
...@@ -259,11 +254,18 @@ static HARD_REG_SET elim_reg_set; ...@@ -259,11 +254,18 @@ static HARD_REG_SET elim_reg_set;
/* Forward declarations */ /* Forward declarations */
static void find_basic_blocks_1 PROTO((rtx, rtx, int)); static void find_basic_blocks_1 PROTO((rtx, rtx, int));
static void make_edges PROTO((int));
static void mark_label_ref PROTO((rtx, rtx, int)); static void mark_label_ref PROTO((rtx, rtx, int));
static int delete_unreachable_blocks PROTO((void));
static int delete_block PROTO((int));
static void life_analysis_1 PROTO((rtx, int)); static void life_analysis_1 PROTO((rtx, int));
static void propagate_block PROTO((regset, rtx, rtx, int, static void propagate_block PROTO((regset, rtx, rtx, int,
regset, int)); regset, int));
static rtx flow_delete_insn PROTO((rtx)); static rtx flow_delete_insn PROTO((rtx));
static int set_noop_p PROTO((rtx));
static int noop_move_p PROTO((rtx));
static void record_volatile_insns PROTO((rtx));
static void mark_regs_live_at_end PROTO((regset));
static int insn_dead_p PROTO((rtx, regset, int)); static int insn_dead_p PROTO((rtx, regset, int));
static int libcall_dead_p PROTO((rtx, regset, rtx, rtx)); static int libcall_dead_p PROTO((rtx, regset, rtx, rtx));
static void mark_set_regs PROTO((regset, regset, rtx, static void mark_set_regs PROTO((regset, regset, rtx,
...@@ -403,12 +405,30 @@ find_basic_blocks (f, nregs, file, live_reachable_p) ...@@ -403,12 +405,30 @@ find_basic_blocks (f, nregs, file, live_reachable_p)
find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p); find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p);
} }
/* For communication between find_basic_blocks_1 and its subroutines. */
/* An array of CODE_LABELs, indexed by UID for the start of the active
EH handler for each insn in F. */
static int *active_eh_region;
static int *nested_eh_region;
/* Element N nonzero if basic block N can actually be reached. */
static char *block_live_static;
/* List of label_refs to all labels whose addresses are taken
and used as data. */
static rtx label_value_list;
/* a list of non-local labels in the function. */
static rtx nonlocal_label_list;
/* Find all basic blocks of the function whose first insn is F. /* Find all basic blocks of the function whose first insn is F.
Store the correct data in the tables that describe the basic blocks, Store the correct data in the tables that describe the basic blocks,
set up the chains of references for each CODE_LABEL, and set up the chains of references for each CODE_LABEL, and
delete any entire basic blocks that cannot be reached. delete any entire basic blocks that cannot be reached.
NONLOCAL_LABEL_LIST is a list of non-local labels in the function. NONLOCAL_LABELS is a list of non-local labels in the function.
Blocks that are otherwise unreachable may be reachable with a non-local Blocks that are otherwise unreachable may be reachable with a non-local
goto. goto.
LIVE_REACHABLE_P is non-zero if the caller needs all live blocks to LIVE_REACHABLE_P is non-zero if the caller needs all live blocks to
...@@ -416,31 +436,24 @@ find_basic_blocks (f, nregs, file, live_reachable_p) ...@@ -416,31 +436,24 @@ find_basic_blocks (f, nregs, file, live_reachable_p)
information to be inaccurate and not suitable for passes like GCSE. */ information to be inaccurate and not suitable for passes like GCSE. */
static void static void
find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) find_basic_blocks_1 (f, nonlocal_labels, live_reachable_p)
rtx f, nonlocal_label_list; rtx f, nonlocal_labels;
int live_reachable_p; int live_reachable_p;
{ {
register rtx insn; register rtx insn;
register int i; register int i;
register char *block_live = (char *) alloca (n_basic_blocks); register char *block_live = (char *) alloca (n_basic_blocks);
register char *block_marked = (char *) alloca (n_basic_blocks); register char *block_marked = (char *) alloca (n_basic_blocks);
/* An array of CODE_LABELs, indexed by UID for the start of the active rtx note, eh_note;
EH handler for each insn in F. */
int *active_eh_region;
int *nested_eh_region;
/* List of label_refs to all labels whose addresses are taken
and used as data. */
rtx label_value_list;
rtx x, note, eh_note;
enum rtx_code prev_code, code; enum rtx_code prev_code, code;
int depth, pass; int depth, pass;
int in_libcall_block = 0; int in_libcall_block = 0;
int deleted_handler = 0;
int call_had_abnormal_edge = 0; int call_had_abnormal_edge = 0;
pass = 1; pass = 1;
active_eh_region = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int)); active_eh_region = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int));
nested_eh_region = (int *) alloca ((max_label_num () + 1) * sizeof (int)); nested_eh_region = (int *) alloca ((max_label_num () + 1) * sizeof (int));
nonlocal_label_list = nonlocal_labels;
restart: restart:
label_value_list = 0; label_value_list = 0;
...@@ -617,51 +630,125 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -617,51 +630,125 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{ {
block_marked[i] = 1; block_marked[i] = 1;
something_marked = 1; something_marked = 1;
make_edges (i);
}
}
/* This should never happen. If it does that means we've computed an
incorrect flow graph, which can lead to aborts/crashes later in the
compiler or incorrect code generation.
We used to try and continue here, but that's just asking for trouble
later during the compile or at runtime. It's easier to debug the
problem here than later! */
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])
abort ();
deleted = delete_unreachable_blocks ();
/* 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;
}
}
}
/* Record INSN's block number as BB. */
void
set_block_num (insn, bb)
rtx insn;
int bb;
{
if (INSN_UID (insn) >= max_uid_for_flow)
{
/* Add one-eighth the size so we don't keep calling xrealloc. */
max_uid_for_flow = INSN_UID (insn) + (INSN_UID (insn) + 7) / 8;
uid_block_number = (int *)
xrealloc (uid_block_number, (max_uid_for_flow + 1) * sizeof (int));
}
BLOCK_NUM (insn) = bb;
}
/* Subroutines of find_basic_blocks. */
/* For basic block I, make edges and mark live all blocks which are reachable
from it. */
static void
make_edges (i)
int i;
{
rtx insn, x;
if (i + 1 < n_basic_blocks && basic_block_drops_in[i + 1]) if (i + 1 < n_basic_blocks && basic_block_drops_in[i + 1])
block_live[i + 1] = 1; block_live_static[i + 1] = 1;
insn = basic_block_end[i]; insn = basic_block_end[i];
if (GET_CODE (insn) == JUMP_INSN) if (GET_CODE (insn) == JUMP_INSN)
mark_label_ref (PATTERN (insn), insn, 0); mark_label_ref (PATTERN (insn), insn, 0);
/* If we have any forced labels, mark them as potentially /* If we have any forced labels, mark them as potentially reachable from
reachable from this block. */ this block. */
for (x = forced_labels; x; x = XEXP (x, 1)) for (x = forced_labels; x; x = XEXP (x, 1))
if (! LABEL_REF_NONLOCAL_P (x)) if (! LABEL_REF_NONLOCAL_P (x))
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)), mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
insn, 0); insn, 0);
/* Now scan the insns for this block, we may need to make /* Now scan the insns for this block, we may need to make edges for some of
edges for some of them to various non-obvious locations them to various non-obvious locations (exception handlers, nonlocal
(exception handlers, nonlocal labels, etc). */ labels, etc). */
for (insn = basic_block_head[i]; for (insn = basic_block_head[i];
insn != NEXT_INSN (basic_block_end[i]); insn != NEXT_INSN (basic_block_end[i]);
insn = NEXT_INSN (insn)) insn = NEXT_INSN (insn))
{ {
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{ {
/* References to labels in non-jumping insns have rtx note;
REG_LABEL notes attached to them. /* References to labels in non-jumping insns have REG_LABEL notes
attached to them.
This can happen for computed gotos; we don't care
about them here since the values are also on the This can happen for computed gotos; we don't care about them
label_value_list and will be marked live if we find here since the values are also on the label_value_list and will
a live computed goto. be marked live if we find a live computed goto.
This can also happen when we take the address of This can also happen when we take the address of a label to pass
a label to pass as an argument to __throw. Note as an argument to __throw. Note throw only uses the value to
throw only uses the value to determine what handler determine what handler should be called -- ie the label is not
should be called -- ie the label is not used as used as a jump target, it just marks regions in the code.
a jump target, it just marks regions in the code.
In theory we should be able to ignore the REG_LABEL notes, but
In theory we should be able to ignore the REG_LABEL we have to make sure that the label and associated insns aren't
notes, but we have to make sure that the label and marked dead, so we make the block in question live and create an
associated insns aren't marked dead, so we make edge from this insn to the label. This is not strictly correct,
the block in question live and create an edge from but it is close enough for now.
this insn to the label. This is not strictly
correct, but it is close enough for now. See below for code that handles the eh_stub label specially. */
See below for code that handles the eh_stub labels
specially. */
for (note = REG_NOTES (insn); for (note = REG_NOTES (insn);
note; note;
note = XEXP (note, 1)) note = XEXP (note, 1))
...@@ -670,15 +757,14 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -670,15 +757,14 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
&& XEXP (note, 0) != eh_return_stub_label) && XEXP (note, 0) != eh_return_stub_label)
{ {
x = XEXP (note, 0); x = XEXP (note, 0);
block_live[BLOCK_NUM (x)] = 1; block_live_static[BLOCK_NUM (x)] = 1;
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, x), mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, x),
insn, 0); insn, 0);
} }
} }
/* If this is a computed jump, then mark it as /* If this is a computed jump, then mark it as reaching everything
reaching everything on the label_value_list on the label_value_list and forced_labels list. */
and forced_labels list. */
if (computed_jump_p (insn)) if (computed_jump_p (insn))
{ {
current_function_has_computed_jump = 1; current_function_has_computed_jump = 1;
...@@ -686,8 +772,7 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -686,8 +772,7 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{ {
int b = BLOCK_NUM (XEXP (x, 0)); int b = BLOCK_NUM (XEXP (x, 0));
basic_block_computed_jump_target[b] = 1; basic_block_computed_jump_target[b] = 1;
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
XEXP (x, 0)),
insn, 0); insn, 0);
} }
...@@ -695,23 +780,19 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -695,23 +780,19 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{ {
int b = BLOCK_NUM (XEXP (x, 0)); int b = BLOCK_NUM (XEXP (x, 0));
basic_block_computed_jump_target[b] = 1; basic_block_computed_jump_target[b] = 1;
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
XEXP (x, 0)),
insn, 0); insn, 0);
} }
} }
/* If this is a CALL_INSN, then mark it as reaching /* If this is a CALL_INSN, then mark it as reaching the active EH
the active EH handler for this CALL_INSN. If handler for this CALL_INSN. If we're handling asynchronous
we're handling asynchronous exceptions mark every exceptions mark every insn as reaching the active EH handler.
insn as reaching the active EH handler.
Also mark the CALL_INSN as reaching any nonlocal Also mark the CALL_INSN as reaching any nonlocal goto sites. */
goto sites. */
else if (asynchronous_exceptions else if (asynchronous_exceptions
|| (GET_CODE (insn) == CALL_INSN || (GET_CODE (insn) == CALL_INSN
&& ! find_reg_note (insn, REG_RETVAL, && ! find_reg_note (insn, REG_RETVAL, NULL_RTX)))
NULL_RTX)))
{ {
if (active_eh_region[INSN_UID (insn)]) if (active_eh_region[INSN_UID (insn)])
{ {
...@@ -723,80 +804,159 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -723,80 +804,159 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{ {
ptr = get_first_handler (region); ptr = get_first_handler (region);
for ( ; ptr ; ptr = ptr->next) for ( ; ptr ; ptr = ptr->next)
mark_label_ref (gen_rtx_LABEL_REF mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
(VOIDmode, ptr->handler_label), insn, 0); ptr->handler_label),
insn, 0);
} }
} }
if (!asynchronous_exceptions) if (! asynchronous_exceptions)
{ {
for (x = nonlocal_label_list; for (x = nonlocal_label_list; x; x = XEXP (x, 1))
x; mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
x = XEXP (x, 1))
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)),
insn, 0); insn, 0);
} }
/* ??? This could be made smarter: /* ??? This could be made smarter: in some cases it's possible
in some cases it's possible to tell that to tell that certain calls will not do a nonlocal goto.
certain calls will not do a nonlocal goto.
For example, if the nested functions that For example, if the nested functions that do the nonlocal
do the nonlocal gotos do not have their gotos do not have their addresses taken, then only calls to
addresses taken, then only calls to those those functions or to other nested functions that use them
functions or to other nested functions that could possibly do nonlocal gotos. */
use them could possibly do nonlocal gotos. */
} }
} }
} }
/* We know something about the structure of the function /* We know something about the structure of the function __throw in
__throw in libgcc2.c. It is the only function that ever libgcc2.c. It is the only function that ever contains eh_stub labels.
contains eh_stub labels. It modifies its return address It modifies its return address so that the last block returns to one of
so that the last block returns to one of the eh_stub labels the eh_stub labels within it. So we have to make additional edges in
within it. So we have to make additional edges in the the flow graph. */
flow graph. */ if (i + 1 == n_basic_blocks && eh_return_stub_label != 0)
if (i + 1 == n_basic_blocks
&& eh_return_stub_label != 0)
{ {
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, eh_return_stub_label),
eh_return_stub_label),
basic_block_end[i], 0); basic_block_end[i], 0);
} }
} }
}
/* This should never happen. If it does that means we've computed an /* Check expression X for label references;
incorrect flow graph, which can lead to aborts/crashes later in the if one is found, add INSN to the label's chain of references.
compiler or incorrect code generation.
We used to try and continue here, but that's just asking for trouble CHECKDUP means check for and avoid creating duplicate references
later during the compile or at runtime. It's easier to debug the from the same insn. Such duplicates do no serious harm but
problem here than later! */ can slow life analysis. CHECKDUP is set only when duplicates
for (i = 1; i < n_basic_blocks; i++) are likely. */
if (block_live[i] && ! basic_block_drops_in[i]
&& GET_CODE (basic_block_head[i]) == CODE_LABEL static void
&& LABEL_REFS (basic_block_head[i]) == basic_block_head[i]) mark_label_ref (x, insn, checkdup)
rtx x, insn;
int checkdup;
{
register RTX_CODE code;
register int i;
register char *fmt;
/* We can be called with NULL when scanning label_value_list. */
if (x == 0)
return;
code = GET_CODE (x);
if (code == LABEL_REF)
{
register rtx label = XEXP (x, 0);
register rtx y;
if (GET_CODE (label) != CODE_LABEL)
abort (); abort ();
/* If the label was never emitted, this insn is junk,
but avoid a crash trying to refer to BLOCK_NUM (label).
This can happen as a result of a syntax error
and a diagnostic has already been printed. */
if (INSN_UID (label) == 0)
return;
CONTAINING_INSN (x) = insn;
/* if CHECKDUP is set, check for duplicate ref from same insn
and don't insert. */
if (checkdup)
for (y = LABEL_REFS (label); y != label; y = LABEL_NEXTREF (y))
if (CONTAINING_INSN (y) == insn)
return;
LABEL_NEXTREF (x) = LABEL_REFS (label);
LABEL_REFS (label) = x;
block_live_static[BLOCK_NUM (label)] = 1;
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
mark_label_ref (XEXP (x, i), insn, 0);
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
mark_label_ref (XVECEXP (x, i, j), insn, 1);
}
}
}
/* Now delete the code for any basic blocks that can't be reached. /* Now delete the code for any basic blocks that can't be reached.
They can occur because jump_optimize does not recognize They can occur because jump_optimize does not recognize unreachable loops
unreachable loops as unreachable. */ as unreachable.
Return the number of deleted blocks. */
static int
delete_unreachable_blocks ()
{
int deleted_handler = 0;
int deleted = 0;
int i;
rtx insn;
deleted = 0;
for (i = 0; i < n_basic_blocks; i++) for (i = 0; i < n_basic_blocks; i++)
if (!block_live[i]) if (! block_live_static[i])
{ {
deleted++; deleted++;
/* Delete the insns in a (non-live) block. We physically delete deleted_handler |= delete_block (i);
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. /* If we deleted an exception handler, we may have EH region
We use to "delete" the insns by turning them into notes, but begin/end blocks to remove as well. */
we may be deleting lots of insns that subsequent passes would if (deleted_handler)
otherwise have to process. Secondly, lots of deleted blocks in for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
a row can really slow down propagate_block since it will if (GET_CODE (insn) == NOTE)
otherwise process insn-turned-notes multiple times when it {
looks for loop begin/end notes. */ if ((NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) ||
(NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
{
int num = CODE_LABEL_NUMBER (insn);
/* A NULL handler indicates a region is no longer needed */
if (get_first_handler (num) == NULL)
{
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
}
}
return 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.
Return nonzero if we deleted an exception handler. */
static int
delete_block (i)
int i;
{
int deleted_handler = 0;
rtx insn;
if (basic_block_head[i] != basic_block_end[i]) if (basic_block_head[i] != basic_block_end[i])
{ {
/* It would be quicker to delete all of these with a single /* It would be quicker to delete all of these with a single
...@@ -813,6 +973,7 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -813,6 +973,7 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
insn = NEXT_INSN (insn); insn = NEXT_INSN (insn);
} }
} }
insn = basic_block_head[i]; insn = basic_block_head[i];
if (GET_CODE (insn) != NOTE) if (GET_CODE (insn) != NOTE)
{ {
...@@ -877,11 +1038,11 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -877,11 +1038,11 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
see if there is a jump around them that is see if there is a jump around them that is
being turned into a no-op. If so, delete it. */ being turned into a no-op. If so, delete it. */
if (block_live[i - 1]) if (block_live_static[i - 1])
{ {
register int j; register int j;
for (j = i + 1; j < n_basic_blocks; j++) for (j = i + 1; j < n_basic_blocks; j++)
if (block_live[j]) if (block_live_static[j])
{ {
rtx label; rtx label;
insn = basic_block_end[i - 1]; insn = basic_block_end[i - 1];
...@@ -912,138 +1073,12 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) ...@@ -912,138 +1073,12 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
break; break;
} }
} }
}
/* If we deleted an exception handler, we may have EH region
begin/end blocks to remove as well. */
if (deleted_handler)
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == NOTE)
{
if ((NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) ||
(NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
{
int num = CODE_LABEL_NUMBER (insn);
/* A NULL handler indicates a region is no longer needed */
if (get_first_handler (num) == NULL)
{
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
}
}
/* There are pathological cases where one function calling hundreds of return deleted_handler;
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;
}
}
} }
/* Record INSN's block number as BB. */ /* Delete INSN by patching it out.
Return the next insn. */
void
set_block_num (insn, bb)
rtx insn;
int bb;
{
if (INSN_UID (insn) >= max_uid_for_flow)
{
/* Add one-eighth the size so we don't keep calling xrealloc. */
max_uid_for_flow = INSN_UID (insn) + (INSN_UID (insn) + 7) / 8;
uid_block_number = (int *)
xrealloc (uid_block_number, (max_uid_for_flow + 1) * sizeof (int));
}
BLOCK_NUM (insn) = bb;
}
/* Subroutines of find_basic_blocks. */
/* Check expression X for label references;
if one is found, add INSN to the label's chain of references.
CHECKDUP means check for and avoid creating duplicate references
from the same insn. Such duplicates do no serious harm but
can slow life analysis. CHECKDUP is set only when duplicates
are likely. */
static void
mark_label_ref (x, insn, checkdup)
rtx x, insn;
int checkdup;
{
register RTX_CODE code;
register int i;
register char *fmt;
/* We can be called with NULL when scanning label_value_list. */
if (x == 0)
return;
code = GET_CODE (x);
if (code == LABEL_REF)
{
register rtx label = XEXP (x, 0);
register rtx y;
if (GET_CODE (label) != CODE_LABEL)
abort ();
/* If the label was never emitted, this insn is junk,
but avoid a crash trying to refer to BLOCK_NUM (label).
This can happen as a result of a syntax error
and a diagnostic has already been printed. */
if (INSN_UID (label) == 0)
return;
CONTAINING_INSN (x) = insn;
/* if CHECKDUP is set, check for duplicate ref from same insn
and don't insert. */
if (checkdup)
for (y = LABEL_REFS (label); y != label; y = LABEL_NEXTREF (y))
if (CONTAINING_INSN (y) == insn)
return;
LABEL_NEXTREF (x) = LABEL_REFS (label);
LABEL_REFS (label) = x;
block_live_static[BLOCK_NUM (label)] = 1;
return;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
mark_label_ref (XEXP (x, i), insn, 0);
if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
mark_label_ref (XVECEXP (x, i, j), insn, 1);
}
}
}
/* Delete INSN by patching it out.
Return the next insn. */
static rtx static rtx
flow_delete_insn (insn) flow_delete_insn (insn)
...@@ -1130,6 +1165,151 @@ free_basic_block_vars (keep_head_end_p) ...@@ -1130,6 +1165,151 @@ free_basic_block_vars (keep_head_end_p)
} }
} }
/* Return nonzero if the destination of SET equals the source. */
static int
set_noop_p (set)
rtx set;
{
rtx src = SET_SRC (set);
rtx dst = SET_DEST (set);
if (GET_CODE (src) == REG && GET_CODE (dst) == REG
&& REGNO (src) == REGNO (dst))
return 1;
if (GET_CODE (src) != SUBREG || GET_CODE (dst) != SUBREG
|| SUBREG_WORD (src) != SUBREG_WORD (dst))
return 0;
src = SUBREG_REG (src);
dst = SUBREG_REG (dst);
if (GET_CODE (src) == REG && GET_CODE (dst) == REG
&& REGNO (src) == REGNO (dst))
return 1;
return 0;
}
/* Return nonzero if an insn consists only of SETs, each of which only sets a
value to itself. */
static int
noop_move_p (insn)
rtx insn;
{
rtx pat = PATTERN (insn);
/* Insns carrying these notes are useful later on. */
if (find_reg_note (insn, REG_EQUAL, NULL_RTX))
return 0;
if (GET_CODE (pat) == SET && set_noop_p (pat))
return 1;
if (GET_CODE (pat) == PARALLEL)
{
int i;
/* If nothing but SETs of registers to themselves,
this insn can also be deleted. */
for (i = 0; i < XVECLEN (pat, 0); i++)
{
rtx tem = XVECEXP (pat, 0, i);
if (GET_CODE (tem) == USE
|| GET_CODE (tem) == CLOBBER)
continue;
if (GET_CODE (tem) != SET || ! set_noop_p (tem))
return 0;
}
return 1;
}
return 0;
}
/* 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. */
static void
record_volatile_insns (f)
rtx f;
{
rtx insn;
for (insn = f; insn; insn = NEXT_INSN (insn))
{
enum rtx_code code1 = GET_CODE (insn);
if (code1 == CALL_INSN)
INSN_VOLATILE (insn) = 1;
else if (code1 == INSN || code1 == JUMP_INSN)
{
if (GET_CODE (PATTERN (insn)) != USE
&& volatile_refs_p (PATTERN (insn)))
INSN_VOLATILE (insn) = 1;
/* A SET that makes space on the stack cannot be dead.
(Such SETs occur only for allocating variable-size data,
so they will always have a PLUS or MINUS according to the
direction of stack growth.)
Even if this function never uses this stack pointer value,
signal handlers do! */
else if (code1 == INSN && GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == stack_pointer_rtx
#ifdef STACK_GROWS_DOWNWARD
&& GET_CODE (SET_SRC (PATTERN (insn))) == MINUS
#else
&& GET_CODE (SET_SRC (PATTERN (insn))) == PLUS
#endif
&& XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx)
INSN_VOLATILE (insn) = 1;
/* Delete (in effect) any obvious no-op moves. */
else if (noop_move_p (insn))
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
}
}
}
/* Mark those regs which are needed at the end of the function as live
at the end of the last basic block. */
static void
mark_regs_live_at_end (set)
regset set;
{
int i;
#ifdef EXIT_IGNORE_STACK
if (! EXIT_IGNORE_STACK
|| (! FRAME_POINTER_REQUIRED
&& ! current_function_calls_alloca
&& flag_omit_frame_pointer))
#endif
/* If exiting needs the right stack value,
consider the stack pointer live at the end of the function. */
SET_REGNO_REG_SET (set, STACK_POINTER_REGNUM);
/* Mark the frame pointer is needed at the end of the function. If
we end up eliminating it, it will be removed from the live list
of each basic block by reload. */
SET_REGNO_REG_SET (set, FRAME_POINTER_REGNUM);
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
/* If they are different, also mark the hard frame pointer as live */
SET_REGNO_REG_SET (set, HARD_FRAME_POINTER_REGNUM);
#endif
/* 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. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (global_regs[i]
#ifdef EPILOGUE_USES
|| EPILOGUE_USES (i)
#endif
)
SET_REGNO_REG_SET (set, i);
}
/* Determine which registers are live at the start of each /* Determine which registers are live at the start of each
basic block of the function whose first insn is F. basic block of the function whose first insn is F.
NREGS is the number of registers used in F. NREGS is the number of registers used in F.
...@@ -1198,146 +1378,13 @@ life_analysis_1 (f, nregs) ...@@ -1198,146 +1378,13 @@ life_analysis_1 (f, nregs)
= (regset *) alloca (n_basic_blocks * sizeof (regset)); = (regset *) alloca (n_basic_blocks * sizeof (regset));
init_regset_vector (basic_block_significant, n_basic_blocks, &flow_obstack); init_regset_vector (basic_block_significant, n_basic_blocks, &flow_obstack);
/* Record which insns refer to any volatile memory record_volatile_insns (f);
or for any reason can't be deleted just because they are dead stores.
Also, delete any insns that copy a register to itself. */
for (insn = f; insn; insn = NEXT_INSN (insn))
{
enum rtx_code code1 = GET_CODE (insn);
if (code1 == CALL_INSN)
INSN_VOLATILE (insn) = 1;
else if (code1 == INSN || code1 == JUMP_INSN)
{
/* Delete (in effect) any obvious no-op moves. */
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))))
/* 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))
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
}
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
/* If nothing but SETs of registers to themselves,
this insn can also be deleted. */
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
rtx tem = XVECEXP (PATTERN (insn), 0, i);
if (GET_CODE (tem) == USE
|| GET_CODE (tem) == CLOBBER)
continue;
if (GET_CODE (tem) != SET
|| GET_CODE (SET_DEST (tem)) != REG
|| GET_CODE (SET_SRC (tem)) != REG
|| REGNO (SET_DEST (tem)) != REGNO (SET_SRC (tem)))
break;
}
if (i == XVECLEN (PATTERN (insn), 0)
/* 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;
}
else
INSN_VOLATILE (insn) = volatile_refs_p (PATTERN (insn));
}
else if (GET_CODE (PATTERN (insn)) != USE)
INSN_VOLATILE (insn) = volatile_refs_p (PATTERN (insn));
/* A SET that makes space on the stack cannot be dead.
(Such SETs occur only for allocating variable-size data,
so they will always have a PLUS or MINUS according to the
direction of stack growth.)
Even if this function never uses this stack pointer value,
signal handlers do! */
else if (code1 == INSN && GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == stack_pointer_rtx
#ifdef STACK_GROWS_DOWNWARD
&& GET_CODE (SET_SRC (PATTERN (insn))) == MINUS
#else
&& GET_CODE (SET_SRC (PATTERN (insn))) == PLUS
#endif
&& XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx)
INSN_VOLATILE (insn) = 1;
}
}
if (n_basic_blocks > 0)
#ifdef EXIT_IGNORE_STACK
if (! EXIT_IGNORE_STACK
|| (! FRAME_POINTER_REQUIRED
&& ! current_function_calls_alloca
&& flag_omit_frame_pointer))
#endif
{
/* If exiting needs the right stack value,
consider the stack pointer live at the end of the function. */
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
we end up eliminating it, it will be removed from the live list
of each basic block by reload. */
if (n_basic_blocks > 0) if (n_basic_blocks > 0)
{ {
SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1], mark_regs_live_at_end (basic_block_live_at_end[n_basic_blocks - 1]);
FRAME_POINTER_REGNUM); COPY_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1], basic_block_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 */
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 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]
#ifdef EPILOGUE_USES
|| EPILOGUE_USES (i)
#endif
)
{
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 /* Propagate life info through the basic blocks
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment