mirror of git://gcc.gnu.org/git/gcc.git
regrename.c (struct du_head): Make nregs signed.
* regrename.c (struct du_head): Make nregs signed. (closed_chains): Remove. (create_new_chain): Return the new chain. (chain_from_id): New static function. (dump_def_use_chain): Change argument to be an int, indicating the first ID to print. All callers changed. (merge_overlapping_regs): Use chain_from_id. Assert that chains don't conflict with themselves. (rename_chains): Take no argument. Iterate over id_to_chain rather to find chains to rename. Clear tick before the main loop. (struct incoming_reg_info): New struct. (struct bb_rename_info): New struct. (init_rename_info, set_incoming_from_chain, merge_chains): New static functions. (regrename_analyze): New static function, broken out of regrename_optimize. Record and make use of open chain information at basic block boundaries, and merge chains where possible. (scan_rtx_reg): Make this_nregs signed. Don't update closed_chains. (build_def_use): Return a bool to indicate success. All callers changed. Don't initialize global data here. (regrename_optimize): Move most code out of here into regrename_analyze. * regs.h (add_range_to_hard_reg_set, remove_range_from_hard_reg_set, range_overlaps_hard_reg_set_p, range_in_hard_reg_set_p): New static inline functions. * vec.h (FOR_EACH_VEC_ELT_FROM): New macro. From-SVN: r178645
This commit is contained in:
parent
a81462f103
commit
8ffa0351a5
|
|
@ -1,3 +1,34 @@
|
||||||
|
2011-09-07 Bernd Schmidt <bernds@codesourcery.com>
|
||||||
|
|
||||||
|
* regrename.c (struct du_head): Make nregs signed.
|
||||||
|
(closed_chains): Remove.
|
||||||
|
(create_new_chain): Return the new chain.
|
||||||
|
(chain_from_id): New static function.
|
||||||
|
(dump_def_use_chain): Change argument to be an int, indicating
|
||||||
|
the first ID to print. All callers changed.
|
||||||
|
(merge_overlapping_regs): Use chain_from_id. Assert that
|
||||||
|
chains don't conflict with themselves.
|
||||||
|
(rename_chains): Take no argument. Iterate over id_to_chain
|
||||||
|
rather to find chains to rename. Clear tick before the main
|
||||||
|
loop.
|
||||||
|
(struct incoming_reg_info): New struct.
|
||||||
|
(struct bb_rename_info): New struct.
|
||||||
|
(init_rename_info, set_incoming_from_chain, merge_chains): New
|
||||||
|
static functions.
|
||||||
|
(regrename_analyze): New static function, broken out of
|
||||||
|
regrename_optimize. Record and make use of open chain information
|
||||||
|
at basic block boundaries, and merge chains where possible.
|
||||||
|
(scan_rtx_reg): Make this_nregs signed. Don't update
|
||||||
|
closed_chains.
|
||||||
|
(build_def_use): Return a bool to indicate success. All callers
|
||||||
|
changed. Don't initialize global data here.
|
||||||
|
(regrename_optimize): Move most code out of here into
|
||||||
|
regrename_analyze.
|
||||||
|
* regs.h (add_range_to_hard_reg_set, remove_range_from_hard_reg_set,
|
||||||
|
range_overlaps_hard_reg_set_p, range_in_hard_reg_set_p): New
|
||||||
|
static inline functions.
|
||||||
|
* vec.h (FOR_EACH_VEC_ELT_FROM): New macro.
|
||||||
|
|
||||||
2011-09-07 Martin Jambor <mjambor@suse.cz>
|
2011-09-07 Martin Jambor <mjambor@suse.cz>
|
||||||
|
|
||||||
PR middle-end/50301
|
PR middle-end/50301
|
||||||
|
|
|
||||||
544
gcc/regrename.c
544
gcc/regrename.c
|
|
@ -47,18 +47,24 @@
|
||||||
|
|
||||||
1. Local def/use chains are built: within each basic block, chains are
|
1. Local def/use chains are built: within each basic block, chains are
|
||||||
opened and closed; if a chain isn't closed at the end of the block,
|
opened and closed; if a chain isn't closed at the end of the block,
|
||||||
it is dropped.
|
it is dropped. We pre-open chains if we have already examined a
|
||||||
|
predecessor block and found chains live at the end which match
|
||||||
|
live registers at the start of the new block.
|
||||||
|
|
||||||
2. For each chain, the set of possible renaming registers is computed.
|
2. We try to combine the local chains across basic block boundaries by
|
||||||
|
comparing chains that were open at the start or end of a block to
|
||||||
|
those in successor/predecessor blocks.
|
||||||
|
|
||||||
|
3. For each chain, the set of possible renaming registers is computed.
|
||||||
This takes into account the renaming of previously processed chains.
|
This takes into account the renaming of previously processed chains.
|
||||||
Optionally, a preferred class is computed for the renaming register.
|
Optionally, a preferred class is computed for the renaming register.
|
||||||
|
|
||||||
3. The best renaming register is computed for the chain in the above set,
|
4. The best renaming register is computed for the chain in the above set,
|
||||||
using a round-robin allocation. If a preferred class exists, then the
|
using a round-robin allocation. If a preferred class exists, then the
|
||||||
round-robin allocation is done within the class first, if possible.
|
round-robin allocation is done within the class first, if possible.
|
||||||
The round-robin allocation of renaming registers itself is global.
|
The round-robin allocation of renaming registers itself is global.
|
||||||
|
|
||||||
4. If a renaming register has been found, it is substituted in the chain.
|
5. If a renaming register has been found, it is substituted in the chain.
|
||||||
|
|
||||||
Targets can parameterize the pass by specifying a preferred class for the
|
Targets can parameterize the pass by specifying a preferred class for the
|
||||||
renaming register for a given (super)class of registers to be renamed. */
|
renaming register for a given (super)class of registers to be renamed. */
|
||||||
|
|
@ -75,8 +81,9 @@ struct du_head
|
||||||
struct du_head *next_chain;
|
struct du_head *next_chain;
|
||||||
/* The first and last elements of this chain. */
|
/* The first and last elements of this chain. */
|
||||||
struct du_chain *first, *last;
|
struct du_chain *first, *last;
|
||||||
/* Describes the register being tracked. */
|
/* Describe the register being tracked, register number and count. */
|
||||||
unsigned regno, nregs;
|
unsigned regno;
|
||||||
|
int nregs;
|
||||||
|
|
||||||
/* A unique id to be used as an index into the conflicts bitmaps. */
|
/* A unique id to be used as an index into the conflicts bitmaps. */
|
||||||
unsigned id;
|
unsigned id;
|
||||||
|
|
@ -140,6 +147,7 @@ static struct obstack rename_obstack;
|
||||||
static void do_replace (struct du_head *, int);
|
static void do_replace (struct du_head *, int);
|
||||||
static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
|
static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
|
||||||
enum op_type);
|
enum op_type);
|
||||||
|
static bool build_def_use (basic_block);
|
||||||
|
|
||||||
typedef struct du_head *du_head_p;
|
typedef struct du_head *du_head_p;
|
||||||
DEF_VEC_P (du_head_p);
|
DEF_VEC_P (du_head_p);
|
||||||
|
|
@ -151,9 +159,8 @@ static unsigned current_id;
|
||||||
/* A mapping of unique id numbers to chains. */
|
/* A mapping of unique id numbers to chains. */
|
||||||
static VEC(du_head_p, heap) *id_to_chain;
|
static VEC(du_head_p, heap) *id_to_chain;
|
||||||
|
|
||||||
/* List of currently open chains, and closed chains that can be renamed. */
|
/* List of currently open chains. */
|
||||||
static struct du_head *open_chains;
|
static struct du_head *open_chains;
|
||||||
static struct du_head *closed_chains;
|
|
||||||
|
|
||||||
/* Bitmap of open chains. The bits set always match the list found in
|
/* Bitmap of open chains. The bits set always match the list found in
|
||||||
open_chains. */
|
open_chains. */
|
||||||
|
|
@ -166,14 +173,33 @@ static HARD_REG_SET live_in_chains;
|
||||||
between this and live_in_chains is empty. */
|
between this and live_in_chains is empty. */
|
||||||
static HARD_REG_SET live_hard_regs;
|
static HARD_REG_SET live_hard_regs;
|
||||||
|
|
||||||
/* Dump all def/use chains in CHAINS to DUMP_FILE. */
|
/* Return the chain corresponding to id number ID. Take into account that
|
||||||
|
chains may have been merged. */
|
||||||
|
static du_head_p
|
||||||
|
chain_from_id (unsigned int id)
|
||||||
|
{
|
||||||
|
du_head_p first_chain = VEC_index (du_head_p, id_to_chain, id);
|
||||||
|
du_head_p chain = first_chain;
|
||||||
|
while (chain->id != id)
|
||||||
|
{
|
||||||
|
id = chain->id;
|
||||||
|
chain = VEC_index (du_head_p, id_to_chain, id);
|
||||||
|
}
|
||||||
|
first_chain->id = id;
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump all def/use chains, starting at id FROM. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dump_def_use_chain (struct du_head *head)
|
dump_def_use_chain (int from)
|
||||||
{
|
{
|
||||||
while (head)
|
du_head_p head;
|
||||||
|
int i;
|
||||||
|
FOR_EACH_VEC_ELT_FROM (du_head_p, id_to_chain, i, head, from)
|
||||||
{
|
{
|
||||||
struct du_chain *this_du = head->first;
|
struct du_chain *this_du = head->first;
|
||||||
|
|
||||||
fprintf (dump_file, "Register %s (%d):",
|
fprintf (dump_file, "Register %s (%d):",
|
||||||
reg_names[head->regno], head->nregs);
|
reg_names[head->regno], head->nregs);
|
||||||
while (this_du)
|
while (this_du)
|
||||||
|
|
@ -215,7 +241,7 @@ mark_conflict (struct du_head *chains, unsigned id)
|
||||||
and record its occurrence in *LOC, which is being written to in INSN.
|
and record its occurrence in *LOC, which is being written to in INSN.
|
||||||
This access requires a register of class CL. */
|
This access requires a register of class CL. */
|
||||||
|
|
||||||
static void
|
static du_head_p
|
||||||
create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
|
create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
|
||||||
rtx insn, enum reg_class cl)
|
rtx insn, enum reg_class cl)
|
||||||
{
|
{
|
||||||
|
|
@ -224,7 +250,6 @@ create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
|
||||||
int nregs;
|
int nregs;
|
||||||
|
|
||||||
head->next_chain = open_chains;
|
head->next_chain = open_chains;
|
||||||
open_chains = head;
|
|
||||||
head->regno = this_regno;
|
head->regno = this_regno;
|
||||||
head->nregs = this_nregs;
|
head->nregs = this_nregs;
|
||||||
head->need_caller_save_reg = 0;
|
head->need_caller_save_reg = 0;
|
||||||
|
|
@ -264,7 +289,7 @@ create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
|
||||||
if (insn == NULL_RTX)
|
if (insn == NULL_RTX)
|
||||||
{
|
{
|
||||||
head->first = head->last = NULL;
|
head->first = head->last = NULL;
|
||||||
return;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
this_du = XOBNEW (&rename_obstack, struct du_chain);
|
this_du = XOBNEW (&rename_obstack, struct du_chain);
|
||||||
|
|
@ -274,6 +299,7 @@ create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
|
||||||
this_du->loc = loc;
|
this_du->loc = loc;
|
||||||
this_du->insn = insn;
|
this_du->insn = insn;
|
||||||
this_du->cl = cl;
|
this_du->cl = cl;
|
||||||
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For a def-use chain HEAD, find which registers overlap its lifetime and
|
/* For a def-use chain HEAD, find which registers overlap its lifetime and
|
||||||
|
|
@ -287,8 +313,9 @@ merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head)
|
||||||
IOR_HARD_REG_SET (*pset, head->hard_conflicts);
|
IOR_HARD_REG_SET (*pset, head->hard_conflicts);
|
||||||
EXECUTE_IF_SET_IN_BITMAP (&head->conflicts, 0, i, bi)
|
EXECUTE_IF_SET_IN_BITMAP (&head->conflicts, 0, i, bi)
|
||||||
{
|
{
|
||||||
du_head_p other = VEC_index (du_head_p, id_to_chain, i);
|
du_head_p other = chain_from_id (i);
|
||||||
unsigned j = other->nregs;
|
unsigned j = other->nregs;
|
||||||
|
gcc_assert (other != head);
|
||||||
while (j-- > 0)
|
while (j-- > 0)
|
||||||
SET_HARD_REG_BIT (*pset, other->regno + j);
|
SET_HARD_REG_BIT (*pset, other->regno + j);
|
||||||
}
|
}
|
||||||
|
|
@ -341,12 +368,15 @@ check_new_reg_p (int reg ATTRIBUTE_UNUSED, int new_reg,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process the closed chains starting with ALL_CHAINS and rename
|
/* Perform register renaming on the current function. */
|
||||||
registers if possible. */
|
|
||||||
static void
|
static void
|
||||||
rename_chains (du_head_p all_chains)
|
rename_chains (void)
|
||||||
{
|
{
|
||||||
HARD_REG_SET unavailable;
|
HARD_REG_SET unavailable;
|
||||||
|
du_head_p this_head;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset (tick, 0, sizeof tick);
|
||||||
|
|
||||||
CLEAR_HARD_REG_SET (unavailable);
|
CLEAR_HARD_REG_SET (unavailable);
|
||||||
/* Don't clobber traceback for noreturn functions. */
|
/* Don't clobber traceback for noreturn functions. */
|
||||||
|
|
@ -358,11 +388,10 @@ rename_chains (du_head_p all_chains)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
while (all_chains)
|
FOR_EACH_VEC_ELT (du_head_p, id_to_chain, i, this_head)
|
||||||
{
|
{
|
||||||
int new_reg, best_new_reg, best_nregs;
|
int new_reg, best_new_reg, best_nregs;
|
||||||
int n_uses;
|
int n_uses;
|
||||||
struct du_head *this_head = all_chains;
|
|
||||||
struct du_chain *tmp;
|
struct du_chain *tmp;
|
||||||
HARD_REG_SET this_unavailable;
|
HARD_REG_SET this_unavailable;
|
||||||
int reg = this_head->regno;
|
int reg = this_head->regno;
|
||||||
|
|
@ -371,8 +400,6 @@ rename_chains (du_head_p all_chains)
|
||||||
enum reg_class preferred_class;
|
enum reg_class preferred_class;
|
||||||
bool has_preferred_class;
|
bool has_preferred_class;
|
||||||
|
|
||||||
all_chains = this_head->next_chain;
|
|
||||||
|
|
||||||
if (this_head->cannot_rename)
|
if (this_head->cannot_rename)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -488,14 +515,425 @@ rename_chains (du_head_p all_chains)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A structure to record information for each hard register at the start of
|
||||||
|
a basic block. */
|
||||||
|
struct incoming_reg_info {
|
||||||
|
/* Holds the number of registers used in the chain that gave us information
|
||||||
|
about this register. Zero means no information known yet, while a
|
||||||
|
negative value is used for something that is part of, but not the first
|
||||||
|
register in a multi-register value. */
|
||||||
|
int nregs;
|
||||||
|
/* Set to true if we have accesses that conflict in the number of registers
|
||||||
|
used. */
|
||||||
|
bool unusable;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A structure recording information about each basic block. It is saved
|
||||||
|
and restored around basic block boundaries.
|
||||||
|
A pointer to such a structure is stored in each basic block's aux field
|
||||||
|
during regrename_analyze, except for blocks we know can't be optimized
|
||||||
|
(such as entry and exit blocks). */
|
||||||
|
struct bb_rename_info
|
||||||
|
{
|
||||||
|
/* The basic block corresponding to this structure. */
|
||||||
|
basic_block bb;
|
||||||
|
/* Copies of the global information. */
|
||||||
|
bitmap_head open_chains_set;
|
||||||
|
bitmap_head incoming_open_chains_set;
|
||||||
|
struct incoming_reg_info incoming[FIRST_PSEUDO_REGISTER];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize a rename_info structure P for basic block BB, which starts a new
|
||||||
|
scan. */
|
||||||
|
static void
|
||||||
|
init_rename_info (struct bb_rename_info *p, basic_block bb)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
df_ref *def_rec;
|
||||||
|
HARD_REG_SET start_chains_set;
|
||||||
|
|
||||||
|
p->bb = bb;
|
||||||
|
bitmap_initialize (&p->open_chains_set, &bitmap_default_obstack);
|
||||||
|
bitmap_initialize (&p->incoming_open_chains_set, &bitmap_default_obstack);
|
||||||
|
|
||||||
|
open_chains = NULL;
|
||||||
|
bitmap_clear (&open_chains_set);
|
||||||
|
|
||||||
|
CLEAR_HARD_REG_SET (live_in_chains);
|
||||||
|
REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
|
||||||
|
for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
|
||||||
|
{
|
||||||
|
df_ref def = *def_rec;
|
||||||
|
if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
|
||||||
|
SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open chains based on information from (at least one) predecessor
|
||||||
|
block. This gives us a chance later on to combine chains across
|
||||||
|
basic block boundaries. Inconsistencies (in access sizes) will
|
||||||
|
be caught normally and dealt with conservatively by disabling the
|
||||||
|
chain for renaming, and there is no risk of losing optimization
|
||||||
|
opportunities by opening chains either: if we did not open the
|
||||||
|
chains, we'd have to track the live register as a hard reg, and
|
||||||
|
we'd be unable to rename it in any case. */
|
||||||
|
CLEAR_HARD_REG_SET (start_chains_set);
|
||||||
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||||
|
{
|
||||||
|
struct incoming_reg_info *iri = p->incoming + i;
|
||||||
|
if (iri->nregs > 0 && !iri->unusable
|
||||||
|
&& range_in_hard_reg_set_p (live_hard_regs, i, iri->nregs))
|
||||||
|
{
|
||||||
|
SET_HARD_REG_BIT (start_chains_set, i);
|
||||||
|
remove_range_from_hard_reg_set (&live_hard_regs, i, iri->nregs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||||
|
{
|
||||||
|
struct incoming_reg_info *iri = p->incoming + i;
|
||||||
|
if (TEST_HARD_REG_BIT (start_chains_set, i))
|
||||||
|
{
|
||||||
|
du_head_p chain;
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "opening incoming chain\n");
|
||||||
|
chain = create_new_chain (i, iri->nregs, NULL, NULL_RTX, NO_REGS);
|
||||||
|
bitmap_set_bit (&p->incoming_open_chains_set, chain->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record in RI that the block corresponding to it has an incoming
|
||||||
|
live value, described by CHAIN. */
|
||||||
|
static void
|
||||||
|
set_incoming_from_chain (struct bb_rename_info *ri, du_head_p chain)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int incoming_nregs = ri->incoming[chain->regno].nregs;
|
||||||
|
int nregs;
|
||||||
|
|
||||||
|
/* If we've recorded the same information before, everything is fine. */
|
||||||
|
if (incoming_nregs == chain->nregs)
|
||||||
|
{
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "reg %d/%d already recorded\n",
|
||||||
|
chain->regno, chain->nregs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have no information for any of the involved registers, update
|
||||||
|
the incoming array. */
|
||||||
|
nregs = chain->nregs;
|
||||||
|
while (nregs-- > 0)
|
||||||
|
if (ri->incoming[chain->regno + nregs].nregs != 0
|
||||||
|
|| ri->incoming[chain->regno + nregs].unusable)
|
||||||
|
break;
|
||||||
|
if (nregs < 0)
|
||||||
|
{
|
||||||
|
nregs = chain->nregs;
|
||||||
|
ri->incoming[chain->regno].nregs = nregs;
|
||||||
|
while (nregs-- > 1)
|
||||||
|
ri->incoming[chain->regno + nregs].nregs = -nregs;
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "recorded reg %d/%d\n",
|
||||||
|
chain->regno, chain->nregs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There must be some kind of conflict. Prevent both the old and
|
||||||
|
new ranges from being used. */
|
||||||
|
if (incoming_nregs < 0)
|
||||||
|
ri->incoming[chain->regno + incoming_nregs].unusable = true;
|
||||||
|
for (i = 0; i < chain->nregs; i++)
|
||||||
|
ri->incoming[chain->regno + i].unusable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Merge the two chains C1 and C2 so that all conflict information is
|
||||||
|
recorded and C1, and the id of C2 is changed to that of C1. */
|
||||||
|
static void
|
||||||
|
merge_chains (du_head_p c1, du_head_p c2)
|
||||||
|
{
|
||||||
|
if (c1 == c2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (c2->first != NULL)
|
||||||
|
{
|
||||||
|
if (c1->first == NULL)
|
||||||
|
c1->first = c2->first;
|
||||||
|
else
|
||||||
|
c1->last->next_use = c2->first;
|
||||||
|
c1->last = c2->last;
|
||||||
|
}
|
||||||
|
|
||||||
|
c2->first = c2->last = NULL;
|
||||||
|
c2->id = c1->id;
|
||||||
|
|
||||||
|
IOR_HARD_REG_SET (c1->hard_conflicts, c2->hard_conflicts);
|
||||||
|
bitmap_ior_into (&c1->conflicts, &c2->conflicts);
|
||||||
|
|
||||||
|
c1->need_caller_save_reg |= c2->need_caller_save_reg;
|
||||||
|
c1->cannot_rename |= c2->cannot_rename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Analyze the current function and build chains for renaming. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
regrename_analyze (void)
|
||||||
|
{
|
||||||
|
struct bb_rename_info *rename_info;
|
||||||
|
int i;
|
||||||
|
basic_block bb;
|
||||||
|
int n_bbs;
|
||||||
|
int *inverse_postorder;
|
||||||
|
|
||||||
|
inverse_postorder = XNEWVEC (int, last_basic_block);
|
||||||
|
n_bbs = pre_and_rev_post_order_compute (NULL, inverse_postorder, false);
|
||||||
|
|
||||||
|
/* Gather some information about the blocks in this function. */
|
||||||
|
rename_info = XCNEWVEC (struct bb_rename_info, n_basic_blocks);
|
||||||
|
i = 0;
|
||||||
|
FOR_EACH_BB (bb)
|
||||||
|
{
|
||||||
|
struct bb_rename_info *ri = rename_info + i;
|
||||||
|
ri->bb = bb;
|
||||||
|
bb->aux = ri;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_id = 0;
|
||||||
|
id_to_chain = VEC_alloc (du_head_p, heap, 0);
|
||||||
|
bitmap_initialize (&open_chains_set, &bitmap_default_obstack);
|
||||||
|
|
||||||
|
/* The order in which we visit blocks ensures that whenever
|
||||||
|
possible, we only process a block after at least one of its
|
||||||
|
predecessors, which provides a "seeding" effect to make the logic
|
||||||
|
in set_incoming_from_chain and init_rename_info useful. */
|
||||||
|
|
||||||
|
for (i = 0; i < n_bbs; i++)
|
||||||
|
{
|
||||||
|
basic_block bb1 = BASIC_BLOCK (inverse_postorder[i]);
|
||||||
|
struct bb_rename_info *this_info;
|
||||||
|
bool success;
|
||||||
|
edge e;
|
||||||
|
edge_iterator ei;
|
||||||
|
int old_length = VEC_length (du_head_p, id_to_chain);
|
||||||
|
|
||||||
|
this_info = (struct bb_rename_info *) bb1->aux;
|
||||||
|
if (this_info == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "\nprocessing block %d:\n", bb1->index);
|
||||||
|
|
||||||
|
init_rename_info (this_info, bb1);
|
||||||
|
|
||||||
|
success = build_def_use (bb1);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "failed\n");
|
||||||
|
bb1->aux = NULL;
|
||||||
|
VEC_truncate (du_head_p, id_to_chain, old_length);
|
||||||
|
current_id = old_length;
|
||||||
|
bitmap_clear (&this_info->incoming_open_chains_set);
|
||||||
|
open_chains = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dump_file)
|
||||||
|
dump_def_use_chain (old_length);
|
||||||
|
bitmap_copy (&this_info->open_chains_set, &open_chains_set);
|
||||||
|
|
||||||
|
/* Add successor blocks to the worklist if necessary, and record
|
||||||
|
data about our own open chains at the end of this block, which
|
||||||
|
will be used to pre-open chains when processing the successors. */
|
||||||
|
FOR_EACH_EDGE (e, ei, bb1->succs)
|
||||||
|
{
|
||||||
|
struct bb_rename_info *dest_ri;
|
||||||
|
struct du_head *chain;
|
||||||
|
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "successor block %d\n", e->dest->index);
|
||||||
|
|
||||||
|
if (e->flags & (EDGE_EH | EDGE_ABNORMAL))
|
||||||
|
continue;
|
||||||
|
dest_ri = (struct bb_rename_info *)e->dest->aux;
|
||||||
|
if (dest_ri == NULL)
|
||||||
|
continue;
|
||||||
|
for (chain = open_chains; chain; chain = chain->next_chain)
|
||||||
|
set_incoming_from_chain (dest_ri, chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free (inverse_postorder);
|
||||||
|
|
||||||
|
/* Now, combine the chains data we have gathered across basic block
|
||||||
|
boundaries.
|
||||||
|
|
||||||
|
For every basic block, there may be chains open at the start, or at the
|
||||||
|
end. Rather than exclude them from renaming, we look for open chains
|
||||||
|
with matching registers at the other side of the CFG edge.
|
||||||
|
|
||||||
|
For a given chain using register R, open at the start of block B, we
|
||||||
|
must find an open chain using R on the other side of every edge leading
|
||||||
|
to B, if the register is live across this edge. In the code below,
|
||||||
|
N_PREDS_USED counts the number of edges where the register is live, and
|
||||||
|
N_PREDS_JOINED counts those where we found an appropriate chain for
|
||||||
|
joining.
|
||||||
|
|
||||||
|
We perform the analysis for both incoming and outgoing edges, but we
|
||||||
|
only need to merge once (in the second part, after verifying outgoing
|
||||||
|
edges). */
|
||||||
|
FOR_EACH_BB (bb)
|
||||||
|
{
|
||||||
|
struct bb_rename_info *bb_ri = (struct bb_rename_info *) bb->aux;
|
||||||
|
unsigned j;
|
||||||
|
bitmap_iterator bi;
|
||||||
|
|
||||||
|
if (bb_ri == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "processing bb %d in edges\n", bb->index);
|
||||||
|
|
||||||
|
EXECUTE_IF_SET_IN_BITMAP (&bb_ri->incoming_open_chains_set, 0, j, bi)
|
||||||
|
{
|
||||||
|
edge e;
|
||||||
|
edge_iterator ei;
|
||||||
|
struct du_head *chain = chain_from_id (j);
|
||||||
|
int n_preds_used = 0, n_preds_joined = 0;
|
||||||
|
|
||||||
|
FOR_EACH_EDGE (e, ei, bb->preds)
|
||||||
|
{
|
||||||
|
struct bb_rename_info *src_ri;
|
||||||
|
unsigned k;
|
||||||
|
bitmap_iterator bi2;
|
||||||
|
HARD_REG_SET live;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
REG_SET_TO_HARD_REG_SET (live, df_get_live_out (e->src));
|
||||||
|
if (!range_overlaps_hard_reg_set_p (live, chain->regno,
|
||||||
|
chain->nregs))
|
||||||
|
continue;
|
||||||
|
n_preds_used++;
|
||||||
|
|
||||||
|
if (e->flags & (EDGE_EH | EDGE_ABNORMAL))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
src_ri = (struct bb_rename_info *)e->src->aux;
|
||||||
|
if (src_ri == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
EXECUTE_IF_SET_IN_BITMAP (&src_ri->open_chains_set,
|
||||||
|
0, k, bi2)
|
||||||
|
{
|
||||||
|
struct du_head *outgoing_chain = chain_from_id (k);
|
||||||
|
|
||||||
|
if (outgoing_chain->regno == chain->regno
|
||||||
|
&& outgoing_chain->nregs == chain->nregs)
|
||||||
|
{
|
||||||
|
n_preds_joined++;
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!success && dump_file)
|
||||||
|
fprintf (dump_file, "failure to match with pred block %d\n",
|
||||||
|
e->src->index);
|
||||||
|
}
|
||||||
|
if (n_preds_joined < n_preds_used)
|
||||||
|
{
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "cannot rename chain %d\n", j);
|
||||||
|
chain->cannot_rename = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FOR_EACH_BB (bb)
|
||||||
|
{
|
||||||
|
struct bb_rename_info *bb_ri = (struct bb_rename_info *) bb->aux;
|
||||||
|
unsigned j;
|
||||||
|
bitmap_iterator bi;
|
||||||
|
|
||||||
|
if (bb_ri == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "processing bb %d out edges\n", bb->index);
|
||||||
|
|
||||||
|
EXECUTE_IF_SET_IN_BITMAP (&bb_ri->open_chains_set, 0, j, bi)
|
||||||
|
{
|
||||||
|
edge e;
|
||||||
|
edge_iterator ei;
|
||||||
|
struct du_head *chain = chain_from_id (j);
|
||||||
|
int n_succs_used = 0, n_succs_joined = 0;
|
||||||
|
|
||||||
|
FOR_EACH_EDGE (e, ei, bb->succs)
|
||||||
|
{
|
||||||
|
bool printed = false;
|
||||||
|
struct bb_rename_info *dest_ri;
|
||||||
|
unsigned k;
|
||||||
|
bitmap_iterator bi2;
|
||||||
|
HARD_REG_SET live;
|
||||||
|
|
||||||
|
REG_SET_TO_HARD_REG_SET (live, df_get_live_in (e->dest));
|
||||||
|
if (!range_overlaps_hard_reg_set_p (live, chain->regno,
|
||||||
|
chain->nregs))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
n_succs_used++;
|
||||||
|
|
||||||
|
dest_ri = (struct bb_rename_info *)e->dest->aux;
|
||||||
|
if (dest_ri == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
EXECUTE_IF_SET_IN_BITMAP (&dest_ri->incoming_open_chains_set,
|
||||||
|
0, k, bi2)
|
||||||
|
{
|
||||||
|
struct du_head *incoming_chain = chain_from_id (k);
|
||||||
|
|
||||||
|
if (incoming_chain->regno == chain->regno
|
||||||
|
&& incoming_chain->nregs == chain->nregs)
|
||||||
|
{
|
||||||
|
if (dump_file)
|
||||||
|
{
|
||||||
|
if (!printed)
|
||||||
|
fprintf (dump_file,
|
||||||
|
"merging blocks for edge %d -> %d\n",
|
||||||
|
e->src->index, e->dest->index);
|
||||||
|
printed = true;
|
||||||
|
fprintf (dump_file,
|
||||||
|
" merging chains %d (->%d) and %d (->%d) [%s]\n",
|
||||||
|
k, incoming_chain->id, j, chain->id,
|
||||||
|
reg_names[incoming_chain->regno]);
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_chains (chain, incoming_chain);
|
||||||
|
n_succs_joined++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n_succs_joined < n_succs_used)
|
||||||
|
{
|
||||||
|
if (dump_file)
|
||||||
|
fprintf (dump_file, "cannot rename chain %d\n",
|
||||||
|
j);
|
||||||
|
chain->cannot_rename = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free (rename_info);
|
||||||
|
|
||||||
|
FOR_EACH_BB (bb)
|
||||||
|
bb->aux = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
do_replace (struct du_head *head, int reg)
|
do_replace (struct du_head *head, int reg)
|
||||||
{
|
{
|
||||||
struct du_chain *chain;
|
struct du_chain *chain;
|
||||||
unsigned int base_regno = head->regno;
|
unsigned int base_regno = head->regno;
|
||||||
|
|
||||||
gcc_assert (! DEBUG_INSN_P (head->first->insn));
|
|
||||||
|
|
||||||
for (chain = head->first; chain; chain = chain->next_use)
|
for (chain = head->first; chain; chain = chain->next_use)
|
||||||
{
|
{
|
||||||
unsigned int regno = ORIGINAL_REGNO (*chain->loc);
|
unsigned int regno = ORIGINAL_REGNO (*chain->loc);
|
||||||
|
|
@ -591,7 +1029,7 @@ scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
|
||||||
rtx x = *loc;
|
rtx x = *loc;
|
||||||
enum machine_mode mode = GET_MODE (x);
|
enum machine_mode mode = GET_MODE (x);
|
||||||
unsigned this_regno = REGNO (x);
|
unsigned this_regno = REGNO (x);
|
||||||
unsigned this_nregs = hard_regno_nregs[this_regno][mode];
|
int this_nregs = hard_regno_nregs[this_regno][mode];
|
||||||
|
|
||||||
if (action == mark_write)
|
if (action == mark_write)
|
||||||
{
|
{
|
||||||
|
|
@ -691,8 +1129,6 @@ scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
|
||||||
|
|
||||||
if (subset && !superset)
|
if (subset && !superset)
|
||||||
head->cannot_rename = 1;
|
head->cannot_rename = 1;
|
||||||
head->next_chain = closed_chains;
|
|
||||||
closed_chains = head;
|
|
||||||
bitmap_clear_bit (&open_chains_set, head->id);
|
bitmap_clear_bit (&open_chains_set, head->id);
|
||||||
|
|
||||||
nregs = head->nregs;
|
nregs = head->nregs;
|
||||||
|
|
@ -1078,28 +1514,14 @@ record_out_operands (rtx insn, bool earlyclobber)
|
||||||
|
|
||||||
/* Build def/use chain. */
|
/* Build def/use chain. */
|
||||||
|
|
||||||
static struct du_head *
|
static bool
|
||||||
build_def_use (basic_block bb)
|
build_def_use (basic_block bb)
|
||||||
{
|
{
|
||||||
rtx insn;
|
rtx insn;
|
||||||
df_ref *def_rec;
|
|
||||||
unsigned HOST_WIDE_INT untracked_operands;
|
unsigned HOST_WIDE_INT untracked_operands;
|
||||||
|
|
||||||
open_chains = closed_chains = NULL;
|
|
||||||
|
|
||||||
fail_current_block = false;
|
fail_current_block = false;
|
||||||
|
|
||||||
current_id = 0;
|
|
||||||
bitmap_initialize (&open_chains_set, &bitmap_default_obstack);
|
|
||||||
CLEAR_HARD_REG_SET (live_in_chains);
|
|
||||||
REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
|
|
||||||
for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
|
|
||||||
{
|
|
||||||
df_ref def = *def_rec;
|
|
||||||
if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
|
|
||||||
SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
|
for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
|
||||||
{
|
{
|
||||||
if (NONDEBUG_INSN_P (insn))
|
if (NONDEBUG_INSN_P (insn))
|
||||||
|
|
@ -1338,14 +1760,10 @@ build_def_use (basic_block bb)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap_clear (&open_chains_set);
|
|
||||||
|
|
||||||
if (fail_current_block)
|
if (fail_current_block)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
/* Since we close every chain when we find a REG_DEAD note, anything that
|
return true;
|
||||||
is still open lives past the basic block, so it can't be renamed. */
|
|
||||||
return closed_chains;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform register renaming on the current function. */
|
/* Perform register renaming on the current function. */
|
||||||
|
|
@ -1353,44 +1771,20 @@ build_def_use (basic_block bb)
|
||||||
static unsigned int
|
static unsigned int
|
||||||
regrename_optimize (void)
|
regrename_optimize (void)
|
||||||
{
|
{
|
||||||
basic_block bb;
|
|
||||||
char *first_obj;
|
|
||||||
|
|
||||||
df_set_flags (DF_LR_RUN_DCE);
|
df_set_flags (DF_LR_RUN_DCE);
|
||||||
df_note_add_problem ();
|
df_note_add_problem ();
|
||||||
df_analyze ();
|
df_analyze ();
|
||||||
df_set_flags (DF_DEFER_INSN_RESCAN);
|
df_set_flags (DF_DEFER_INSN_RESCAN);
|
||||||
|
|
||||||
memset (tick, 0, sizeof tick);
|
|
||||||
|
|
||||||
gcc_obstack_init (&rename_obstack);
|
gcc_obstack_init (&rename_obstack);
|
||||||
first_obj = XOBNEWVAR (&rename_obstack, char, 0);
|
|
||||||
|
|
||||||
FOR_EACH_BB (bb)
|
regrename_analyze ();
|
||||||
{
|
|
||||||
struct du_head *all_chains = 0;
|
|
||||||
|
|
||||||
id_to_chain = VEC_alloc (du_head_p, heap, 0);
|
rename_chains ();
|
||||||
|
|
||||||
if (dump_file)
|
|
||||||
fprintf (dump_file, "\nBasic block %d:\n", bb->index);
|
|
||||||
|
|
||||||
all_chains = build_def_use (bb);
|
|
||||||
|
|
||||||
if (dump_file)
|
|
||||||
dump_def_use_chain (all_chains);
|
|
||||||
|
|
||||||
rename_chains (all_chains);
|
|
||||||
|
|
||||||
free_chain_data ();
|
|
||||||
obstack_free (&rename_obstack, first_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
free_chain_data ();
|
||||||
obstack_free (&rename_obstack, NULL);
|
obstack_free (&rename_obstack, NULL);
|
||||||
|
|
||||||
if (dump_file)
|
|
||||||
fputc ('\n', dump_file);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
44
gcc/regs.h
44
gcc/regs.h
|
|
@ -397,4 +397,48 @@ overlaps_hard_reg_set_p (const HARD_REG_SET regs, enum machine_mode mode,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Like add_to_hard_reg_set, but use a REGNO/NREGS range instead of
|
||||||
|
REGNO and MODE. */
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
add_range_to_hard_reg_set (HARD_REG_SET *regs, unsigned int regno,
|
||||||
|
int nregs)
|
||||||
|
{
|
||||||
|
while (nregs-- > 0)
|
||||||
|
SET_HARD_REG_BIT (*regs, regno + nregs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Likewise, but remove the registers. */
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
remove_range_from_hard_reg_set (HARD_REG_SET *regs, unsigned int regno,
|
||||||
|
int nregs)
|
||||||
|
{
|
||||||
|
while (nregs-- > 0)
|
||||||
|
CLEAR_HARD_REG_BIT (*regs, regno + nregs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like overlaps_hard_reg_set_p, but use a REGNO/NREGS range instead of
|
||||||
|
REGNO and MODE. */
|
||||||
|
static inline bool
|
||||||
|
range_overlaps_hard_reg_set_p (const HARD_REG_SET set, unsigned regno,
|
||||||
|
int nregs)
|
||||||
|
{
|
||||||
|
while (nregs-- > 0)
|
||||||
|
if (TEST_HARD_REG_BIT (set, regno + nregs))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like in_hard_reg_set_p, but use a REGNO/NREGS range instead of
|
||||||
|
REGNO and MODE. */
|
||||||
|
static inline bool
|
||||||
|
range_in_hard_reg_set_p (const HARD_REG_SET set, unsigned regno, int nregs)
|
||||||
|
{
|
||||||
|
while (nregs-- > 0)
|
||||||
|
if (!TEST_HARD_REG_BIT (set, regno + nregs))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* GCC_REGS_H */
|
#endif /* GCC_REGS_H */
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,11 @@ along with GCC; see the file COPYING3. If not see
|
||||||
#define FOR_EACH_VEC_ELT(T, V, I, P) \
|
#define FOR_EACH_VEC_ELT(T, V, I, P) \
|
||||||
for (I = 0; VEC_iterate (T, (V), (I), (P)); ++(I))
|
for (I = 0; VEC_iterate (T, (V), (I), (P)); ++(I))
|
||||||
|
|
||||||
|
/* Likewise, but start from FROM rather than 0. */
|
||||||
|
|
||||||
|
#define FOR_EACH_VEC_ELT_FROM(T, V, I, P, FROM) \
|
||||||
|
for (I = (FROM); VEC_iterate (T, (V), (I), (P)); ++(I))
|
||||||
|
|
||||||
/* Convenience macro for reverse iteration. */
|
/* Convenience macro for reverse iteration. */
|
||||||
|
|
||||||
#define FOR_EACH_VEC_ELT_REVERSE(T,V,I,P) \
|
#define FOR_EACH_VEC_ELT_REVERSE(T,V,I,P) \
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue