mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
PR rtl-optimization/20070 / part1
PR rtl-optimization/20070 / part1 * flow.c (update_life_info): If PROP_POST_REGSTACK is set, call count_or_remove_death_notes with kill == -1. (mark_set_1): Don't add REG_DEAD / REG_UNUSED notes for stack registers if PROP_POST_REGSTACK is set. (mark_used_reg): Likewise. (count_or_remove_death_notes): If kill is -1, don't remove REG_DEAD / REG_UNUSED notes for stack regs. * cfgcleanup.c (condjump_equiv_p): Change parameters and processing to match rtx_equiv_p machinery. Change caller. (outgoing_edges_match): Likewise. (try_crossjump_to_edge): Use struct_equiv_block_eq instead of flow_find_cross_jump. * basic-block.h (PROP_POST_REGSTACK, STRUCT_EQUIV_START): Define. (STRUCT_EQUIV_RERUN, STRUCT_EQUIV_FINAL): Likewise. (STRUCT_EQUIV_NEED_FULL_BLOCK, STRUCT_EQUIV_MATCH_JUMPS): Likewise. (STRUCT_EQUIV_MAX_LOCAL): Likewise. (struct struct_equiv_checkpoint, struct equiv_info): Likewise. (insns_match_p): Update prototype. (flow_find_cross_jump): Remove prototype. (struct_equiv_block_eq, struct_equiv_init): Declare. (rtx_equiv_p, condjump_equiv_p): Likewise. * struct-equiv.c: Include reload.h. (IMPOSSIBLE_MOVE_FACTOR): Define. (assign_reg_reg_set, struct_equiv_make_checkpoint): New functions. (struct_equiv_improve_checkpoint): Likewise. (struct_equiv_restore_checkpoint, rtx_equiv_p): Likewise. (set_dest_equiv_p, set_dest_addr_equiv_p, struct_equiv_init): Likewise. (struct_equiv_merge, find_dying_input): Likewise. (resolve_input_conflict, note_local_live): Likewise. (death_notes_match_p): Change parameters and processing to match rtx_equiv_p machinery. Change caller. (insns_match_p): Likewise. (flow_find_cross_jump): Replace with: (struct_equiv_block_eq). Back out this change: 2005-03-07 Kazu Hirata <kazu@cs.umass.edu> * recog.c (verify_changes): Make it static. * recog.h: Remove the corresponding prototype. From-SVN: r108480
This commit is contained in:
parent
80e6edb051
commit
7d22e8989c
@ -1,3 +1,46 @@
|
||||
2005-12-13 J"orn Rennecke <joern.rennecke@st.com>
|
||||
|
||||
PR rtl-optimization/20070 / part1
|
||||
* flow.c (update_life_info): If PROP_POST_REGSTACK is set, call
|
||||
count_or_remove_death_notes with kill == -1.
|
||||
(mark_set_1): Don't add REG_DEAD / REG_UNUSED notes for stack
|
||||
registers if PROP_POST_REGSTACK is set.
|
||||
(mark_used_reg): Likewise.
|
||||
(count_or_remove_death_notes): If kill is -1, don't remove REG_DEAD /
|
||||
REG_UNUSED notes for stack regs.
|
||||
* cfgcleanup.c (condjump_equiv_p): Change parameters and processing
|
||||
to match rtx_equiv_p machinery. Change caller.
|
||||
(outgoing_edges_match): Likewise.
|
||||
(try_crossjump_to_edge): Use struct_equiv_block_eq
|
||||
instead of flow_find_cross_jump.
|
||||
* basic-block.h (PROP_POST_REGSTACK, STRUCT_EQUIV_START): Define.
|
||||
(STRUCT_EQUIV_RERUN, STRUCT_EQUIV_FINAL): Likewise.
|
||||
(STRUCT_EQUIV_NEED_FULL_BLOCK, STRUCT_EQUIV_MATCH_JUMPS): Likewise.
|
||||
(STRUCT_EQUIV_MAX_LOCAL): Likewise.
|
||||
(struct struct_equiv_checkpoint, struct equiv_info): Likewise.
|
||||
(insns_match_p): Update prototype.
|
||||
(flow_find_cross_jump): Remove prototype.
|
||||
(struct_equiv_block_eq, struct_equiv_init): Declare.
|
||||
(rtx_equiv_p, condjump_equiv_p): Likewise.
|
||||
* struct-equiv.c: Include reload.h.
|
||||
(IMPOSSIBLE_MOVE_FACTOR): Define.
|
||||
(assign_reg_reg_set, struct_equiv_make_checkpoint): New functions.
|
||||
(struct_equiv_improve_checkpoint): Likewise.
|
||||
(struct_equiv_restore_checkpoint, rtx_equiv_p): Likewise.
|
||||
(set_dest_equiv_p, set_dest_addr_equiv_p, struct_equiv_init): Likewise.
|
||||
(struct_equiv_merge, find_dying_input): Likewise.
|
||||
(resolve_input_conflict, note_local_live): Likewise.
|
||||
(death_notes_match_p): Change parameters and processing
|
||||
to match rtx_equiv_p machinery. Change caller.
|
||||
(insns_match_p): Likewise.
|
||||
(flow_find_cross_jump): Replace with:
|
||||
(struct_equiv_block_eq).
|
||||
|
||||
Back out this change:
|
||||
2005-03-07 Kazu Hirata <kazu@cs.umass.edu>
|
||||
* recog.c (verify_changes): Make it static.
|
||||
* recog.h: Remove the corresponding prototype.
|
||||
|
||||
2005-12-13 J"orn Rennecke <joern.rennecke@st.com>
|
||||
|
||||
* rtlhooks.c (gen_lowpart_general): Handle SUBREGs of floating point
|
||||
|
@ -807,6 +807,9 @@ enum update_life_extent
|
||||
to flag analysis of asms. */
|
||||
#define PROP_DEAD_INSN 1024 /* Internal flag used within flow.c
|
||||
to flag analysis of dead insn. */
|
||||
#define PROP_POST_REGSTACK 2048 /* We run after reg-stack and need
|
||||
to preserve REG_DEAD notes for
|
||||
stack regs. */
|
||||
#define PROP_FINAL (PROP_DEATH_NOTES | PROP_LOG_LINKS \
|
||||
| PROP_REG_INFO | PROP_KILL_DEAD_CODE \
|
||||
| PROP_SCAN_DEAD_CODE | PROP_AUTOINC \
|
||||
@ -831,6 +834,17 @@ enum update_life_extent
|
||||
#define CLEANUP_CFGLAYOUT 128 /* Do cleanup in cfglayout mode. */
|
||||
#define CLEANUP_LOG_LINKS 256 /* Update log links. */
|
||||
|
||||
/* The following are ORed in on top of the CLEANUP* flags in calls to
|
||||
struct_equiv_block_eq. */
|
||||
#define STRUCT_EQUIV_START 512 /* Initializes the search range. */
|
||||
#define STRUCT_EQUIV_RERUN 1024 /* Rerun to find register use in
|
||||
found equivalence. */
|
||||
#define STRUCT_EQUIV_FINAL 2048 /* Make any changes necessary to get
|
||||
actual equivalence. */
|
||||
#define STRUCT_EQUIV_NEED_FULL_BLOCK 4096 /* struct_equiv_block_eq is required
|
||||
to match only full blocks */
|
||||
#define STRUCT_EQUIV_MATCH_JUMPS 8192 /* Also include the jumps at the end of the block in the comparison. */
|
||||
|
||||
extern void life_analysis (FILE *, int);
|
||||
extern int update_life_info (sbitmap, enum update_life_extent, int);
|
||||
extern int update_life_info_in_dirty_blocks (enum update_life_extent, int);
|
||||
@ -992,7 +1006,168 @@ extern basic_block get_bb_copy (basic_block);
|
||||
#include "cfghooks.h"
|
||||
|
||||
/* In struct-equiv.c */
|
||||
extern bool insns_match_p (int, rtx, rtx);
|
||||
extern int flow_find_cross_jump (int, basic_block, basic_block, rtx *, rtx *);
|
||||
|
||||
/* Constants used to size arrays in struct equiv_info (currently only one).
|
||||
When these limits are exceeded, struct_equiv returns zero.
|
||||
The maximum number of pseudo registers that are different in the two blocks,
|
||||
but appear in equivalent places and are dead at the end (or where one of
|
||||
a pair is dead at the end). */
|
||||
#define STRUCT_EQUIV_MAX_LOCAL 16
|
||||
/* The maximum number of references to an input register that struct_equiv
|
||||
can handle. */
|
||||
|
||||
/* Structure used to track state during struct_equiv that can be rolled
|
||||
back when we find we can't match an insn, or if we want to match part
|
||||
of it in a different way.
|
||||
This information pertains to the pair of partial blocks that has been
|
||||
matched so far. Since this pair is structurally equivalent, this is
|
||||
conceptually just one partial block expressed in two potentially
|
||||
different ways. */
|
||||
struct struct_equiv_checkpoint
|
||||
{
|
||||
int ninsns; /* Insns are matched so far. */
|
||||
int local_count; /* Number of block-local registers. */
|
||||
int input_count; /* Number of inputs to the block. */
|
||||
|
||||
/* X_START and Y_START are the first insns (in insn stream order)
|
||||
of the partial blocks that have been considered for matching so far.
|
||||
Since we are scanning backwards, they are also the instructions that
|
||||
are currently considered - or the last ones that have been considered -
|
||||
for matching (Unless we tracked back to these because a preceding
|
||||
instruction failed to match). */
|
||||
rtx x_start, y_start;
|
||||
|
||||
/* INPUT_VALID indicates if we have actually set up X_INPUT / Y_INPUT
|
||||
during the current pass; we keep X_INPUT / Y_INPUT around between passes
|
||||
so that we can match REG_EQUAL / REG_EQUIV notes referring to these. */
|
||||
bool input_valid;
|
||||
|
||||
/* Some information would be expensive to exactly checkpoint, so we
|
||||
merely increment VERSION any time information about local
|
||||
registers, inputs and/or register liveness changes. When backtracking,
|
||||
it is decremented for changes that can be undone, and if a discrepancy
|
||||
remains, NEED_RERUN in the relevant struct equiv_info is set to indicate
|
||||
that a new pass should be made over the entire block match to get
|
||||
accurate register information. */
|
||||
int version;
|
||||
};
|
||||
|
||||
/* A struct equiv_info is used to pass information to struct_equiv and
|
||||
to gather state while two basic blocks are checked for structural
|
||||
equivalence. */
|
||||
|
||||
struct equiv_info
|
||||
{
|
||||
/* Fields set up by the caller to struct_equiv_block_eq */
|
||||
|
||||
basic_block x_block, y_block; /* The two blocks being matched. */
|
||||
|
||||
/* MODE carries the mode bits from cleanup_cfg if we are called from
|
||||
try_crossjump_to_edge, and additionally it carries the
|
||||
STRUCT_EQUIV_* bits described above. */
|
||||
int mode;
|
||||
|
||||
/* INPUT_COST is the cost that adding an extra input to the matched blocks
|
||||
is supposed to have, and is taken into account when considering if the
|
||||
matched sequence should be extended backwards. input_cost < 0 means
|
||||
don't accept any inputs at all. */
|
||||
int input_cost;
|
||||
|
||||
|
||||
/* Fields to track state inside of struct_equiv_block_eq. Some of these
|
||||
are also outputs. */
|
||||
|
||||
/* X_INPUT and Y_INPUT are used by struct_equiv to record a register that
|
||||
is used as an input parameter, i.e. where different registers are used
|
||||
as sources. This is only used for a register that is live at the end
|
||||
of the blocks, or in some identical code at the end of the blocks;
|
||||
Inputs that are dead at the end go into X_LOCAL / Y_LOCAL. */
|
||||
rtx x_input, y_input;
|
||||
/* When a previous pass has identified a valid input, INPUT_REG is set
|
||||
by struct_equiv_block_eq, and it is henceforth replaced in X_BLOCK
|
||||
for the input. */
|
||||
rtx input_reg;
|
||||
|
||||
/* COMMON_LIVE keeps track of the registers which are currently live
|
||||
(as we scan backwards from the end) and have the same numbers in both
|
||||
blocks. N.B. a register that is in common_live is unsuitable to become
|
||||
a local reg. */
|
||||
regset common_live;
|
||||
/* Likewise, X_LOCAL_LIVE / Y_LOCAL_LIVE keep track of registers that are
|
||||
local to one of the blocks; these registers must not be accepted as
|
||||
identical when encountered in both blocks. */
|
||||
regset x_local_live, y_local_live;
|
||||
|
||||
/* EQUIV_USED indicates for which insns a REG_EQUAL or REG_EQUIV note is
|
||||
being used, to avoid having to backtrack in the next pass, so that we
|
||||
get accurate life info for this insn then. For each such insn,
|
||||
the bit with the number corresponding to the CUR.NINSNS value at the
|
||||
time of scanning is set. */
|
||||
bitmap equiv_used;
|
||||
|
||||
/* Current state that can be saved & restored easily. */
|
||||
struct struct_equiv_checkpoint cur;
|
||||
/* BEST_MATCH is used to store the best match so far, weighing the
|
||||
cost of matched insns COSTS_N_INSNS (CUR.NINSNS) against the cost
|
||||
CUR.INPUT_COUNT * INPUT_COST of setting up the inputs. */
|
||||
struct struct_equiv_checkpoint best_match;
|
||||
/* If a checkpoint restore failed, or an input conflict newly arises,
|
||||
NEED_RERUN is set. This has to be tested by the caller to re-run
|
||||
the comparison if the match appears otherwise sound. The state kept in
|
||||
x_start, y_start, equiv_used and check_input_conflict ensures that
|
||||
we won't loop indefinetly. */
|
||||
bool need_rerun;
|
||||
/* If there is indication of an input conflict at the end,
|
||||
CHECK_INPUT_CONFLICT is set so that we'll check for input conflicts
|
||||
for each insn in the next pass. This is needed so that we won't discard
|
||||
a partial match if there is a longer match that has to be abandoned due
|
||||
to an input conflict. */
|
||||
bool check_input_conflict;
|
||||
/* HAD_INPUT_CONFLICT is set if CHECK_INPUT_CONFLICT was already set and we
|
||||
have passed a point where there were multiple dying inputs. This helps
|
||||
us decide if we should set check_input_conflict for the next pass. */
|
||||
bool had_input_conflict;
|
||||
|
||||
/* LIVE_UPDATE controls if we want to change any life info at all. We
|
||||
set it to false during REG_EQUAL / REG_EUQIV note comparison of the final
|
||||
pass so that we don't introduce new registers just for the note; if we
|
||||
can't match the notes without the current register information, we drop
|
||||
them. */
|
||||
bool live_update;
|
||||
|
||||
/* X_LOCAL and Y_LOCAL are used to gather register numbers of register pairs
|
||||
that are local to X_BLOCK and Y_BLOCK, with CUR.LOCAL_COUNT being the index
|
||||
to the next free entry. */
|
||||
rtx x_local[STRUCT_EQUIV_MAX_LOCAL], y_local[STRUCT_EQUIV_MAX_LOCAL];
|
||||
/* LOCAL_RVALUE is nonzero if the corresponding X_LOCAL / Y_LOCAL entry
|
||||
was a source operand (including STRICT_LOW_PART) for the last invocation
|
||||
of struct_equiv mentioning it, zero if it was a destination-only operand.
|
||||
Since we are scanning backwards, this means the register is input/local
|
||||
for the (partial) block scanned so far. */
|
||||
bool local_rvalue[STRUCT_EQUIV_MAX_LOCAL];
|
||||
|
||||
|
||||
/* Additional fields that are computed for the convenience of the caller. */
|
||||
|
||||
/* DYING_INPUTS is set to the number of local registers that turn out
|
||||
to be inputs to the (possibly partial) block. */
|
||||
int dying_inputs;
|
||||
/* X_END and Y_END are the last insns in X_BLOCK and Y_BLOCK, respectively,
|
||||
that are being compared. A final jump insn will not be included. */
|
||||
rtx x_end, y_end;
|
||||
|
||||
/* If we are matching tablejumps, X_LABEL in X_BLOCK coresponds to
|
||||
Y_LABEL in Y_BLOCK. */
|
||||
rtx x_label, y_label;
|
||||
|
||||
};
|
||||
|
||||
extern bool insns_match_p (rtx, rtx, struct equiv_info *);
|
||||
extern int struct_equiv_block_eq (int, struct equiv_info *);
|
||||
extern bool struct_equiv_init (int, struct equiv_info *);
|
||||
extern bool rtx_equiv_p (rtx *, rtx, int, struct equiv_info *);
|
||||
|
||||
/* In cfgrtl.c */
|
||||
extern bool condjump_equiv_p (struct equiv_info *, bool);
|
||||
|
||||
#endif /* GCC_BASIC_BLOCK_H */
|
||||
|
205
gcc/cfgcleanup.c
205
gcc/cfgcleanup.c
@ -60,7 +60,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
static bool first_pass;
|
||||
static bool try_crossjump_to_edge (int, edge, edge);
|
||||
static bool try_crossjump_bb (int, basic_block);
|
||||
static bool outgoing_edges_match (int, basic_block, basic_block);
|
||||
static bool outgoing_edges_match (int *, struct equiv_info *);
|
||||
|
||||
static void merge_blocks_move_predecessor_nojumps (basic_block, basic_block);
|
||||
static void merge_blocks_move_successor_nojumps (basic_block, basic_block);
|
||||
@ -879,20 +879,20 @@ merge_blocks_move (edge e, basic_block b, basic_block c, int mode)
|
||||
}
|
||||
|
||||
/* Return true iff the condbranches at the end of BB1 and BB2 match. */
|
||||
static bool
|
||||
condjump_equiv_p (basic_block bb1, basic_block bb2)
|
||||
bool
|
||||
condjump_equiv_p (struct equiv_info *info, bool call_init)
|
||||
{
|
||||
edge b1, f1, b2, f2;
|
||||
basic_block bb1 = info->x_block;
|
||||
basic_block bb2 = info->y_block;
|
||||
edge b1 = BRANCH_EDGE (bb1);
|
||||
edge b2 = BRANCH_EDGE (bb2);
|
||||
edge f1 = FALLTHRU_EDGE (bb1);
|
||||
edge f2 = FALLTHRU_EDGE (bb2);
|
||||
bool reverse, match;
|
||||
rtx set1, set2, cond1, cond2;
|
||||
rtx src1, src2;
|
||||
enum rtx_code code1, code2;
|
||||
|
||||
|
||||
b1 = BRANCH_EDGE (bb1);
|
||||
b2 = BRANCH_EDGE (bb2);
|
||||
f1 = FALLTHRU_EDGE (bb1);
|
||||
f2 = FALLTHRU_EDGE (bb2);
|
||||
|
||||
/* Get around possible forwarders on fallthru edges. Other cases
|
||||
should be optimized out already. */
|
||||
if (FORWARDER_BLOCK_P (f1->dest))
|
||||
@ -923,8 +923,10 @@ condjump_equiv_p (basic_block bb1, basic_block bb2)
|
||||
!= (XEXP (SET_SRC (set2), 1) == pc_rtx))
|
||||
reverse = !reverse;
|
||||
|
||||
cond1 = XEXP (SET_SRC (set1), 0);
|
||||
cond2 = XEXP (SET_SRC (set2), 0);
|
||||
src1 = SET_SRC (set1);
|
||||
src2 = SET_SRC (set2);
|
||||
cond1 = XEXP (src1, 0);
|
||||
cond2 = XEXP (src2, 0);
|
||||
code1 = GET_CODE (cond1);
|
||||
if (reverse)
|
||||
code2 = reversed_comparison_code (cond2, BB_END (bb2));
|
||||
@ -934,15 +936,35 @@ condjump_equiv_p (basic_block bb1, basic_block bb2)
|
||||
if (code2 == UNKNOWN)
|
||||
return false;
|
||||
|
||||
if (call_init && !struct_equiv_init (STRUCT_EQUIV_START | info->mode, info))
|
||||
gcc_unreachable ();
|
||||
/* Make the sources of the pc sets unreadable so that when we call
|
||||
insns_match_p it won't process them.
|
||||
The death_notes_match_p from insns_match_p won't see the local registers
|
||||
used for the pc set, but that could only cause missed optimizations when
|
||||
there are actually condjumps that use stack registers. */
|
||||
SET_SRC (set1) = pc_rtx;
|
||||
SET_SRC (set2) = pc_rtx;
|
||||
/* Verify codes and operands match. */
|
||||
match = ((code1 == code2
|
||||
&& rtx_renumbered_equal_p (XEXP (cond1, 0), XEXP (cond2, 0))
|
||||
&& rtx_renumbered_equal_p (XEXP (cond1, 1), XEXP (cond2, 1)))
|
||||
|| (code1 == swap_condition (code2)
|
||||
&& rtx_renumbered_equal_p (XEXP (cond1, 1),
|
||||
XEXP (cond2, 0))
|
||||
&& rtx_renumbered_equal_p (XEXP (cond1, 0),
|
||||
XEXP (cond2, 1))));
|
||||
if (code1 == code2)
|
||||
{
|
||||
match = (insns_match_p (BB_END (bb1), BB_END (bb2), info)
|
||||
&& rtx_equiv_p (&XEXP (cond1, 0), XEXP (cond2, 0), 1, info)
|
||||
&& rtx_equiv_p (&XEXP (cond1, 1), XEXP (cond2, 1), 1, info));
|
||||
|
||||
}
|
||||
else if (code1 == swap_condition (code2))
|
||||
{
|
||||
match = (insns_match_p (BB_END (bb1), BB_END (bb2), info)
|
||||
&& rtx_equiv_p (&XEXP (cond1, 1), XEXP (cond2, 0), 1, info)
|
||||
&& rtx_equiv_p (&XEXP (cond1, 0), XEXP (cond2, 1), 1, info));
|
||||
|
||||
}
|
||||
else
|
||||
match = false;
|
||||
SET_SRC (set1) = src1;
|
||||
SET_SRC (set2) = src2;
|
||||
match &= verify_changes (0);
|
||||
|
||||
/* If we return true, we will join the blocks. Which means that
|
||||
we will only have one branch prediction bit to work with. Thus
|
||||
@ -971,7 +993,7 @@ condjump_equiv_p (basic_block bb1, basic_block bb2)
|
||||
"Outcomes of branch in bb %i and %i differ too much (%i %i)\n",
|
||||
bb1->index, bb2->index, b1->probability, prob2);
|
||||
|
||||
return false;
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -979,17 +1001,25 @@ condjump_equiv_p (basic_block bb1, basic_block bb2)
|
||||
fprintf (dump_file, "Conditionals in bb %i and %i match.\n",
|
||||
bb1->index, bb2->index);
|
||||
|
||||
if (!match)
|
||||
cancel_changes (0);
|
||||
return match;
|
||||
}
|
||||
/* Return true iff outgoing edges of BB1 and BB2 match, together with
|
||||
the branch instruction. This means that if we commonize the control
|
||||
flow before end of the basic block, the semantic remains unchanged.
|
||||
|
||||
/* Return true iff outgoing edges of INFO->y_block and INFO->x_block match,
|
||||
together with the branch instruction. This means that if we commonize the
|
||||
control flow before end of the basic block, the semantic remains unchanged.
|
||||
If we need to compare jumps, we set STRUCT_EQUIV_MATCH_JUMPS in *MODE,
|
||||
and pass *MODE to struct_equiv_init or assign it to INFO->mode, as
|
||||
appropriate.
|
||||
|
||||
We may assume that there exists one edge with a common destination. */
|
||||
|
||||
static bool
|
||||
outgoing_edges_match (int mode, basic_block bb1, basic_block bb2)
|
||||
outgoing_edges_match (int *mode, struct equiv_info *info)
|
||||
{
|
||||
basic_block bb1 = info->y_block;
|
||||
basic_block bb2 = info->x_block;
|
||||
int nehedges1 = 0, nehedges2 = 0;
|
||||
edge fallthru1 = 0, fallthru2 = 0;
|
||||
edge e1, e2;
|
||||
@ -1005,6 +1035,7 @@ outgoing_edges_match (int mode, basic_block bb1, basic_block bb2)
|
||||
& (EDGE_COMPLEX | EDGE_FAKE)) == 0
|
||||
&& (!JUMP_P (BB_END (bb2)) || simplejump_p (BB_END (bb2))));
|
||||
|
||||
*mode |= STRUCT_EQUIV_MATCH_JUMPS;
|
||||
/* Match conditional jumps - this may get tricky when fallthru and branch
|
||||
edges are crossed. */
|
||||
if (EDGE_COUNT (bb1->succs) == 2
|
||||
@ -1015,7 +1046,8 @@ outgoing_edges_match (int mode, basic_block bb1, basic_block bb2)
|
||||
|| !any_condjump_p (BB_END (bb2))
|
||||
|| !onlyjump_p (BB_END (bb2)))
|
||||
return false;
|
||||
return condjump_equiv_p (bb1, bb2);
|
||||
info->mode = *mode;
|
||||
return condjump_equiv_p (info, true);
|
||||
}
|
||||
|
||||
/* Generic case - we are seeing a computed jump, table jump or trapping
|
||||
@ -1063,31 +1095,22 @@ outgoing_edges_match (int mode, basic_block bb1, basic_block bb2)
|
||||
identical = false;
|
||||
}
|
||||
|
||||
if (identical)
|
||||
if (identical
|
||||
&& struct_equiv_init (STRUCT_EQUIV_START | *mode, info))
|
||||
{
|
||||
replace_label_data rr;
|
||||
bool match;
|
||||
|
||||
/* Temporarily replace references to LABEL1 with LABEL2
|
||||
/* Indicate that LABEL1 is to be replaced with LABEL2
|
||||
in BB1->END so that we could compare the instructions. */
|
||||
rr.r1 = label1;
|
||||
rr.r2 = label2;
|
||||
rr.update_label_nuses = false;
|
||||
for_each_rtx (&BB_END (bb1), replace_label, &rr);
|
||||
info->y_label = label1;
|
||||
info->x_label = label2;
|
||||
|
||||
match = insns_match_p (mode, BB_END (bb1), BB_END (bb2));
|
||||
match = insns_match_p (BB_END (bb1), BB_END (bb2), info);
|
||||
if (dump_file && match)
|
||||
fprintf (dump_file,
|
||||
"Tablejumps in bb %i and %i match.\n",
|
||||
bb1->index, bb2->index);
|
||||
|
||||
/* Set the original label in BB1->END because when deleting
|
||||
a block whose end is a tablejump, the tablejump referenced
|
||||
from the instruction is deleted too. */
|
||||
rr.r1 = label2;
|
||||
rr.r2 = label1;
|
||||
for_each_rtx (&BB_END (bb1), replace_label, &rr);
|
||||
|
||||
return match;
|
||||
}
|
||||
}
|
||||
@ -1097,7 +1120,8 @@ outgoing_edges_match (int mode, basic_block bb1, basic_block bb2)
|
||||
|
||||
/* First ensure that the instructions match. There may be many outgoing
|
||||
edges so this test is generally cheaper. */
|
||||
if (!insns_match_p (mode, BB_END (bb1), BB_END (bb2)))
|
||||
if (!struct_equiv_init (STRUCT_EQUIV_START | *mode, info)
|
||||
|| !insns_match_p (BB_END (bb1), BB_END (bb2), info))
|
||||
return false;
|
||||
|
||||
/* Search the outgoing edges, ensure that the counts do match, find possible
|
||||
@ -1163,14 +1187,13 @@ outgoing_edges_match (int mode, basic_block bb1, basic_block bb2)
|
||||
static bool
|
||||
try_crossjump_to_edge (int mode, edge e1, edge e2)
|
||||
{
|
||||
int nmatch;
|
||||
int nmatch, i;
|
||||
basic_block src1 = e1->src, src2 = e2->src;
|
||||
basic_block redirect_to, redirect_from, to_remove;
|
||||
rtx newpos1, newpos2;
|
||||
edge s;
|
||||
edge_iterator ei;
|
||||
|
||||
newpos1 = newpos2 = NULL_RTX;
|
||||
struct equiv_info info;
|
||||
rtx x_active, y_active;
|
||||
|
||||
/* If we have partitioned hot/cold basic blocks, it is a bad idea
|
||||
to try this optimization.
|
||||
@ -1217,19 +1240,66 @@ try_crossjump_to_edge (int mode, edge e1, edge e2)
|
||||
return false;
|
||||
|
||||
/* Look for the common insn sequence, part the first ... */
|
||||
if (!outgoing_edges_match (mode, src1, src2))
|
||||
info.x_block = src2;
|
||||
info.y_block = src1;
|
||||
if (!outgoing_edges_match (&mode, &info))
|
||||
return false;
|
||||
|
||||
/* ... and part the second. */
|
||||
nmatch = flow_find_cross_jump (mode, src1, src2, &newpos1, &newpos2);
|
||||
info.input_cost = optimize_size ? COSTS_N_INSNS (1) : -1;
|
||||
nmatch = struct_equiv_block_eq (STRUCT_EQUIV_START | mode, &info);
|
||||
|
||||
/* Don't proceed with the crossjump unless we found a sufficient number
|
||||
of matching instructions or the 'from' block was totally matched
|
||||
(such that its predecessors will hopefully be redirected and the
|
||||
block removed). */
|
||||
if ((nmatch < PARAM_VALUE (PARAM_MIN_CROSSJUMP_INSNS))
|
||||
&& (newpos1 != BB_HEAD (src1)))
|
||||
if (!nmatch)
|
||||
return false;
|
||||
if ((nmatch -info.cur.input_count < PARAM_VALUE (PARAM_MIN_CROSSJUMP_INSNS))
|
||||
&& (info.cur.y_start != BB_HEAD (src1)))
|
||||
return false;
|
||||
while (info.need_rerun)
|
||||
{
|
||||
nmatch = struct_equiv_block_eq (STRUCT_EQUIV_RERUN | mode, &info);
|
||||
if (!nmatch)
|
||||
return false;
|
||||
if ((nmatch -info.cur.input_count < PARAM_VALUE (PARAM_MIN_CROSSJUMP_INSNS))
|
||||
&& (info.cur.y_start != BB_HEAD (src1)))
|
||||
return false;
|
||||
}
|
||||
nmatch = struct_equiv_block_eq (STRUCT_EQUIV_FINAL | mode, &info);
|
||||
if ((nmatch -info.cur.input_count < PARAM_VALUE (PARAM_MIN_CROSSJUMP_INSNS))
|
||||
&& (info.cur.y_start != BB_HEAD (src1)))
|
||||
return false;
|
||||
|
||||
/* Skip possible basic block header. */
|
||||
x_active = info.cur.x_start;
|
||||
if (LABEL_P (x_active))
|
||||
x_active = NEXT_INSN (x_active);
|
||||
if (NOTE_P (x_active))
|
||||
x_active = NEXT_INSN (x_active);
|
||||
|
||||
y_active = info.cur.y_start;
|
||||
if (LABEL_P (y_active))
|
||||
y_active = NEXT_INSN (y_active);
|
||||
if (NOTE_P (y_active))
|
||||
y_active = NEXT_INSN (y_active);
|
||||
|
||||
/* In order for this code to become active, either we have to be called
|
||||
before reload, or struct_equiv_block_eq needs to add register scavenging
|
||||
code to allocate input_reg after reload. */
|
||||
if (info.input_reg)
|
||||
{
|
||||
emit_insn_before (gen_move_insn (info.input_reg, info.x_input),
|
||||
x_active);
|
||||
emit_insn_before (gen_move_insn (info.input_reg, info.y_input),
|
||||
y_active);
|
||||
}
|
||||
|
||||
for (i = 0; i < info.cur.local_count; i++)
|
||||
if (info.local_rvalue[i])
|
||||
emit_insn_before (gen_move_insn (info.x_local[i], info.y_local[i]),
|
||||
y_active);
|
||||
|
||||
/* Here we know that the insns in the end of SRC1 which are common with SRC2
|
||||
will be deleted.
|
||||
@ -1265,30 +1335,36 @@ try_crossjump_to_edge (int mode, edge e1, edge e2)
|
||||
/* Avoid splitting if possible. We must always split when SRC2 has
|
||||
EH predecessor edges, or we may end up with basic blocks with both
|
||||
normal and EH predecessor edges. */
|
||||
if (newpos2 == BB_HEAD (src2)
|
||||
if (info.cur.x_start == BB_HEAD (src2)
|
||||
&& !(EDGE_PRED (src2, 0)->flags & EDGE_EH))
|
||||
redirect_to = src2;
|
||||
else
|
||||
{
|
||||
if (newpos2 == BB_HEAD (src2))
|
||||
if (info.cur.x_start == BB_HEAD (src2))
|
||||
{
|
||||
/* Skip possible basic block header. */
|
||||
if (LABEL_P (newpos2))
|
||||
newpos2 = NEXT_INSN (newpos2);
|
||||
if (NOTE_P (newpos2))
|
||||
newpos2 = NEXT_INSN (newpos2);
|
||||
if (LABEL_P (info.cur.x_start))
|
||||
info.cur.x_start = NEXT_INSN (info.cur.x_start);
|
||||
if (NOTE_P (info.cur.x_start))
|
||||
info.cur.x_start = NEXT_INSN (info.cur.x_start);
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Splitting bb %i before %i insns\n",
|
||||
src2->index, nmatch);
|
||||
redirect_to = split_block (src2, PREV_INSN (newpos2))->dest;
|
||||
redirect_to = split_block (src2, PREV_INSN (info.cur.x_start))->dest;
|
||||
COPY_REG_SET (info.y_block->il.rtl->global_live_at_end,
|
||||
info.x_block->il.rtl->global_live_at_end);
|
||||
}
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"Cross jumping from bb %i to bb %i; %i common insns\n",
|
||||
src1->index, src2->index, nmatch);
|
||||
{
|
||||
fprintf (dump_file, "Cross jumping from bb %i to bb %i; %i common insns",
|
||||
src1->index, src2->index, nmatch);
|
||||
if (info.cur.local_count)
|
||||
fprintf (dump_file, ", %i local registers", info.cur.local_count);
|
||||
fprintf (dump_file, "\n");
|
||||
}
|
||||
|
||||
redirect_to->count += src1->count;
|
||||
redirect_to->frequency += src1->frequency;
|
||||
@ -1352,14 +1428,7 @@ try_crossjump_to_edge (int mode, edge e1, edge e2)
|
||||
|
||||
/* Edit SRC1 to go to REDIRECT_TO at NEWPOS1. */
|
||||
|
||||
/* Skip possible basic block header. */
|
||||
if (LABEL_P (newpos1))
|
||||
newpos1 = NEXT_INSN (newpos1);
|
||||
|
||||
if (NOTE_P (newpos1))
|
||||
newpos1 = NEXT_INSN (newpos1);
|
||||
|
||||
redirect_from = split_block (src1, PREV_INSN (newpos1))->src;
|
||||
redirect_from = split_block (src1, PREV_INSN (y_active))->src;
|
||||
to_remove = single_succ (redirect_from);
|
||||
|
||||
redirect_edge_and_branch_force (single_succ_edge (redirect_from), redirect_to);
|
||||
|
43
gcc/flow.c
43
gcc/flow.c
@ -643,7 +643,8 @@ update_life_info (sbitmap blocks, enum update_life_extent extent,
|
||||
|
||||
/* If asked, remove notes from the blocks we'll update. */
|
||||
if (extent == UPDATE_LIFE_GLOBAL_RM_NOTES)
|
||||
count_or_remove_death_notes (blocks, 1);
|
||||
count_or_remove_death_notes (blocks,
|
||||
prop_flags & PROP_POST_REGSTACK ? -1 : 1);
|
||||
}
|
||||
|
||||
/* Clear log links in case we are asked to (re)compute them. */
|
||||
@ -2926,7 +2927,13 @@ mark_set_1 (struct propagate_block_info *pbi, enum rtx_code code, rtx reg, rtx c
|
||||
if (flags & PROP_REG_INFO)
|
||||
REG_N_DEATHS (regno_first) += 1;
|
||||
|
||||
if (flags & PROP_DEATH_NOTES)
|
||||
if (flags & PROP_DEATH_NOTES
|
||||
#ifdef STACK_REGS
|
||||
&& (!(flags & PROP_POST_REGSTACK)
|
||||
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG,
|
||||
LAST_STACK_REG))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
/* Note that dead stores have already been deleted
|
||||
when possible. If we get here, we have found a
|
||||
@ -2939,7 +2946,13 @@ mark_set_1 (struct propagate_block_info *pbi, enum rtx_code code, rtx reg, rtx c
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags & PROP_DEATH_NOTES)
|
||||
if (flags & PROP_DEATH_NOTES
|
||||
#ifdef STACK_REGS
|
||||
&& (!(flags & PROP_POST_REGSTACK)
|
||||
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG,
|
||||
LAST_STACK_REG))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
/* This is a case where we have a multi-word hard register
|
||||
and some, but not all, of the words of the register are
|
||||
@ -2998,7 +3011,12 @@ mark_set_1 (struct propagate_block_info *pbi, enum rtx_code code, rtx reg, rtx c
|
||||
here and count it. */
|
||||
else if (GET_CODE (reg) == SCRATCH)
|
||||
{
|
||||
if (flags & PROP_DEATH_NOTES)
|
||||
if (flags & PROP_DEATH_NOTES
|
||||
#ifdef STACK_REGS
|
||||
&& (!(flags & PROP_POST_REGSTACK)
|
||||
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG, LAST_STACK_REG))
|
||||
#endif
|
||||
)
|
||||
REG_NOTES (insn)
|
||||
= alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
|
||||
}
|
||||
@ -3764,6 +3782,10 @@ mark_used_reg (struct propagate_block_info *pbi, rtx reg,
|
||||
if (! some_was_live)
|
||||
{
|
||||
if ((pbi->flags & PROP_DEATH_NOTES)
|
||||
#ifdef STACK_REGS
|
||||
&& (!(pbi->flags & PROP_POST_REGSTACK)
|
||||
|| !IN_RANGE (REGNO (reg), FIRST_STACK_REG, LAST_STACK_REG))
|
||||
#endif
|
||||
&& ! find_regno_note (insn, REG_DEAD, regno_first))
|
||||
REG_NOTES (insn)
|
||||
= alloc_EXPR_LIST (REG_DEAD, reg, REG_NOTES (insn));
|
||||
@ -4385,7 +4407,9 @@ struct tree_opt_pass pass_recompute_reg_usage =
|
||||
|
||||
/* 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. */
|
||||
of the number of registers that died.
|
||||
If KILL is 1, remove old REG_DEAD / REG_UNUSED notes. If it is 0, don't.
|
||||
if it is -1, remove them unless they pertain to a stack reg. */
|
||||
|
||||
int
|
||||
count_or_remove_death_notes (sbitmap blocks, int kill)
|
||||
@ -4457,7 +4481,14 @@ count_or_remove_death_notes_bb (basic_block bb, int kill)
|
||||
/* Fall through. */
|
||||
|
||||
case REG_UNUSED:
|
||||
if (kill)
|
||||
if (kill > 0
|
||||
|| (kill
|
||||
#ifdef STACK_REGS
|
||||
&& (!REG_P (XEXP (link, 0))
|
||||
|| !IN_RANGE (REGNO (XEXP (link, 0)),
|
||||
FIRST_STACK_REG, LAST_STACK_REG))
|
||||
#endif
|
||||
))
|
||||
{
|
||||
rtx next = XEXP (link, 1);
|
||||
free_EXPR_LIST_node (link);
|
||||
|
@ -339,7 +339,7 @@ num_changes_pending (void)
|
||||
/* Tentatively apply the changes numbered NUM and up.
|
||||
Return 1 if all changes are valid, zero otherwise. */
|
||||
|
||||
static int
|
||||
int
|
||||
verify_changes (int num)
|
||||
{
|
||||
int i;
|
||||
|
@ -76,6 +76,7 @@ extern int asm_operand_ok (rtx, const char *);
|
||||
extern int validate_change (rtx, rtx *, rtx, int);
|
||||
extern int validate_change_maybe_volatile (rtx, rtx *, rtx);
|
||||
extern int insn_invalid_p (rtx);
|
||||
extern int verify_changes (int);
|
||||
extern void confirm_change_group (void);
|
||||
extern int apply_change_group (void);
|
||||
extern int num_validated_changes (void);
|
||||
|
1224
gcc/struct-equiv.c
1224
gcc/struct-equiv.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user