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>
* expmed.c (store_bit_field): Pun non-integral str_rtx modes.
......
......@@ -233,11 +233,6 @@ static char *basic_block_drops_in;
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,
plus one. This is the weight attached to references to registers. */
......@@ -259,11 +254,18 @@ static HARD_REG_SET elim_reg_set;
/* Forward declarations */
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 int delete_unreachable_blocks PROTO((void));
static int delete_block PROTO((int));
static void life_analysis_1 PROTO((rtx, int));
static void propagate_block PROTO((regset, rtx, rtx, int,
regset, int));
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 libcall_dead_p PROTO((rtx, regset, rtx, rtx));
static void mark_set_regs PROTO((regset, regset, rtx,
......@@ -403,12 +405,30 @@ find_basic_blocks (f, nregs, file, 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.
Store the correct data in the tables that describe the basic blocks,
set up the chains of references for each CODE_LABEL, and
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
goto.
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)
information to be inaccurate and not suitable for passes like GCSE. */
static void
find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
rtx f, nonlocal_label_list;
find_basic_blocks_1 (f, nonlocal_labels, live_reachable_p)
rtx f, nonlocal_labels;
int live_reachable_p;
{
register rtx insn;
register int i;
register char *block_live = (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
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;
rtx note, eh_note;
enum rtx_code prev_code, code;
int depth, pass;
int in_libcall_block = 0;
int deleted_handler = 0;
int call_had_abnormal_edge = 0;
pass = 1;
active_eh_region = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int));
nested_eh_region = (int *) alloca ((max_label_num () + 1) * sizeof (int));
nonlocal_label_list = nonlocal_labels;
restart:
label_value_list = 0;
......@@ -617,51 +630,125 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{
block_marked[i] = 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])
block_live[i + 1] = 1;
block_live_static[i + 1] = 1;
insn = basic_block_end[i];
if (GET_CODE (insn) == JUMP_INSN)
mark_label_ref (PATTERN (insn), insn, 0);
/* If we have any forced labels, mark them as potentially
reachable from this block. */
/* If we have any forced labels, mark them as potentially reachable from
this block. */
for (x = forced_labels; x; x = XEXP (x, 1))
if (! LABEL_REF_NONLOCAL_P (x))
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
insn, 0);
/* Now scan the insns for this block, we may need to make
edges for some of them to various non-obvious locations
(exception handlers, nonlocal labels, etc). */
/* Now scan the insns for this block, we may need to make edges for some of
them to various non-obvious locations (exception handlers, nonlocal
labels, etc). */
for (insn = basic_block_head[i];
insn != NEXT_INSN (basic_block_end[i]);
insn = NEXT_INSN (insn))
{
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
/* 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
label_value_list and will be marked live if we find
a live computed goto.
This can also happen when we take the address of
a label to pass as an argument to __throw. Note
throw only uses the value to determine what handler
should be called -- ie the label is not used as
a jump target, it just marks regions in the code.
In theory we should be able to ignore the REG_LABEL
notes, but we have to make sure that the label and
associated insns aren't marked dead, so we make
the block in question live and create an edge from
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 labels
specially. */
rtx note;
/* 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 label_value_list and will
be marked live if we find a live computed goto.
This can also happen when we take the address of a label to pass
as an argument to __throw. Note throw only uses the value to
determine what handler should be called -- ie the label is not
used as a jump target, it just marks regions in the code.
In theory we should be able to ignore the REG_LABEL notes, but
we have to make sure that the label and associated insns aren't
marked dead, so we make the block in question live and create an
edge from 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. */
for (note = REG_NOTES (insn);
note;
note = XEXP (note, 1))
......@@ -670,15 +757,14 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
&& XEXP (note, 0) != eh_return_stub_label)
{
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),
insn, 0);
}
}
/* If this is a computed jump, then mark it as
reaching everything on the label_value_list
and forced_labels list. */
/* If this is a computed jump, then mark it as reaching everything
on the label_value_list and forced_labels list. */
if (computed_jump_p (insn))
{
current_function_has_computed_jump = 1;
......@@ -686,8 +772,7 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{
int b = BLOCK_NUM (XEXP (x, 0));
basic_block_computed_jump_target[b] = 1;
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)),
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
insn, 0);
}
......@@ -695,23 +780,19 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{
int b = BLOCK_NUM (XEXP (x, 0));
basic_block_computed_jump_target[b] = 1;
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)),
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
insn, 0);
}
}
/* If this is a CALL_INSN, then mark it as reaching
the active EH handler for this CALL_INSN. If
we're handling asynchronous exceptions mark every
insn as reaching the active EH handler.
/* If this is a CALL_INSN, then mark it as reaching the active EH
handler for this CALL_INSN. If we're handling asynchronous
exceptions mark every insn as reaching the active EH handler.
Also mark the CALL_INSN as reaching any nonlocal
goto sites. */
Also mark the CALL_INSN as reaching any nonlocal goto sites. */
else if (asynchronous_exceptions
|| (GET_CODE (insn) == CALL_INSN
&& ! find_reg_note (insn, REG_RETVAL,
NULL_RTX)))
&& ! find_reg_note (insn, REG_RETVAL, NULL_RTX)))
{
if (active_eh_region[INSN_UID (insn)])
{
......@@ -723,80 +804,159 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
{
ptr = get_first_handler (region);
for ( ; ptr ; ptr = ptr->next)
mark_label_ref (gen_rtx_LABEL_REF
(VOIDmode, ptr->handler_label), insn, 0);
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
ptr->handler_label),
insn, 0);
}
}
if (!asynchronous_exceptions)
if (! asynchronous_exceptions)
{
for (x = nonlocal_label_list;
x;
x = XEXP (x, 1))
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
XEXP (x, 0)),
for (x = nonlocal_label_list; x; x = XEXP (x, 1))
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
insn, 0);
}
/* ??? This could be made smarter:
in some cases it's possible to tell that
certain calls will not do a nonlocal goto.
/* ??? This could be made smarter: in some cases it's possible
to tell that certain calls will not do a nonlocal goto.
For example, if the nested functions that
do the nonlocal gotos do not have their
addresses taken, then only calls to those
functions or to other nested functions that
use them could possibly do nonlocal gotos. */
For example, if the nested functions that do the nonlocal
gotos do not have their addresses taken, then only calls to
those functions or to other nested functions that use them
could possibly do nonlocal gotos. */
}
}
}
/* We know something about the structure of the function
__throw in libgcc2.c. It is the only function that ever
contains eh_stub labels. It modifies its return address
so that the last block returns to one of the eh_stub labels
within it. So we have to make additional edges in the
flow graph. */
if (i + 1 == n_basic_blocks
&& eh_return_stub_label != 0)
/* We know something about the structure of the function __throw in
libgcc2.c. It is the only function that ever contains eh_stub labels.
It modifies its return address so that the last block returns to one of
the eh_stub labels within it. So we have to make additional edges in
the flow graph. */
if (i + 1 == n_basic_blocks && eh_return_stub_label != 0)
{
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
eh_return_stub_label),
mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, eh_return_stub_label),
basic_block_end[i], 0);
}
}
}
}
/* 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.
/* Check expression X for label references;
if one is found, add INSN to the label's chain of references.
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])
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);
}
}
}
/* Now delete the code for any basic blocks that can't be reached.
They can occur because jump_optimize does not recognize
unreachable loops as unreachable. */
/* Now delete the code for any basic blocks that can't be reached.
They can occur because jump_optimize does not recognize unreachable loops
as unreachable.
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++)
if (!block_live[i])
if (! block_live_static[i])
{
deleted++;
/* Delete the insns in a (non-live) block. We physically delete
every non-note insn except the start and end (so
basic_block_head/end needn't be updated), we turn the latter
into NOTE_INSN_DELETED notes.
We use to "delete" the insns by turning them into notes, but
we may be deleting lots of insns that subsequent passes would
otherwise have to process. Secondly, lots of deleted blocks in
a row can really slow down propagate_block since it will
otherwise process insn-turned-notes multiple times when it
looks for loop begin/end notes. */
deleted_handler |= delete_block (i);
}
/* 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;
}
}
}
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])
{
/* 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)
insn = NEXT_INSN (insn);
}
}
insn = basic_block_head[i];
if (GET_CODE (insn) != NOTE)
{
......@@ -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
being turned into a no-op. If so, delete it. */
if (block_live[i - 1])
if (block_live_static[i - 1])
{
register int j;
for (j = i + 1; j < n_basic_blocks; j++)
if (block_live[j])
if (block_live_static[j])
{
rtx label;
insn = basic_block_end[i - 1];
......@@ -912,138 +1073,12 @@ find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
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
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;
}
}
return deleted_handler;
}
/* 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. */
/* 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. */
/* Delete INSN by patching it out.
Return the next insn. */
static rtx
flow_delete_insn (insn)
......@@ -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
basic block of the function whose first insn is F.
NREGS is the number of registers used in F.
......@@ -1198,146 +1378,13 @@ life_analysis_1 (f, nregs)
= (regset *) alloca (n_basic_blocks * sizeof (regset));
init_regset_vector (basic_block_significant, n_basic_blocks, &flow_obstack);
/* Record which insns refer to any volatile memory
or for any reason can't be deleted just because they are dead stores.
Also, delete any insns that copy a register to itself. */
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. */
record_volatile_insns (f);
if (n_basic_blocks > 0)
{
SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1],
FRAME_POINTER_REGNUM);
SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
FRAME_POINTER_REGNUM);
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
/* If they are different, also mark the hard frame pointer as live */
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);
mark_regs_live_at_end (basic_block_live_at_end[n_basic_blocks - 1]);
COPY_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
basic_block_live_at_end[n_basic_blocks - 1]);
}
/* 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