Commit 541f7d56 by Bernd Schmidt Committed by Richard Henderson

regrename.c: Rewrite to handle multi-register modes and cond_exec instructions.

        * regrename.c: Rewrite to handle multi-register modes and
        cond_exec instructions.
        * Makefile.in (regrename.o): Update dependancies.
        * recog.h (struct operand_alternative): Add is_address.
        * recog.c (preprocess_constraints) [case 'p']: Set it.

Co-Authored-By: Richard Henderson <rth@redhat.com>

From-SVN: r37089
parent b8dad04b
2000-10-27 Bernd Schmidt <bernds@redhat.co.uk>
Richard Henderson <rth@redhat.com>
* regrename.c: Rewrite to handle multi-register modes and
cond_exec instructions.
* Makefile.in (regrename.o): Update dependancies.
* recog.h (struct operand_alternative): Add is_address.
* recog.c (preprocess_constraints) [case 'p']: Set it.
2000-10-27 Zack Weinberg <zack@wolery.stanford.edu> 2000-10-27 Zack Weinberg <zack@wolery.stanford.edu>
* configure.in: If not NO_MINUS_C_MINUS_O, substitute * configure.in: If not NO_MINUS_C_MINUS_O, substitute
......
...@@ -1452,9 +1452,9 @@ bb-reorder.o : bb-reorder.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \ ...@@ -1452,9 +1452,9 @@ bb-reorder.o : bb-reorder.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h \ insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h \
$(RECOG_H) insn-flags.h function.h except.h $(EXPR_H) $(RECOG_H) insn-flags.h function.h except.h $(EXPR_H)
timevar.o : timevar.c $(CONFIG_H) system.h $(TIMEVAR_H) flags.h intl.h timevar.o : timevar.c $(CONFIG_H) system.h $(TIMEVAR_H) flags.h intl.h
regrename.o : regrename.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \ regrename.o : regrename.c $(CONFIG_H) system.h $(RTL_H) insn-config.h \
insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h \ $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h $(RECOG_H) function.h \
$(RECOG_H) function.h resource.h resource.h $(OBSTACK_H) flags.h
ifcvt.o : ifcvt.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) \ ifcvt.o : ifcvt.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) \
flags.h insn-config.h function.h $(RECOG_H) $(BASIC_BLOCK_H) $(EXPR_H) \ flags.h insn-config.h function.h $(RECOG_H) $(BASIC_BLOCK_H) $(EXPR_H) \
output.h output.h
......
...@@ -2272,6 +2272,7 @@ preprocess_constraints () ...@@ -2272,6 +2272,7 @@ preprocess_constraints ()
break; break;
case 'p': case 'p':
op_alt[j].is_address = 1;
op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) BASE_REG_CLASS]; op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) BASE_REG_CLASS];
break; break;
......
...@@ -63,6 +63,8 @@ struct operand_alternative ...@@ -63,6 +63,8 @@ struct operand_alternative
unsigned int decmem_ok:1; unsigned int decmem_ok:1;
/* Nonzero if '>' was found in the constraint string. */ /* Nonzero if '>' was found in the constraint string. */
unsigned int incmem_ok:1; unsigned int incmem_ok:1;
/* Nonzero if 'p' was found in the constraint string. */
unsigned int is_address:1;
/* Nonzero if 'X' was found in the constraint string, or if the constraint /* Nonzero if 'X' was found in the constraint string, or if the constraint
string for this alternative was empty. */ string for this alternative was empty. */
unsigned int anything_ok:1; unsigned int anything_ok:1;
......
...@@ -18,1050 +18,787 @@ ...@@ -18,1050 +18,787 @@
the Free Software Foundation, 59 Temple Place - Suite 330, the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */ Boston, MA 02111-1307, USA. */
#define REG_OK_STRICT
#include "config.h" #include "config.h"
#include "system.h" #include "system.h"
#include "tree.h"
#include "rtl.h" #include "rtl.h"
#include "hard-reg-set.h" #include "tm_p.h"
#include "basic-block.h"
#include "insn-config.h" #include "insn-config.h"
#include "regs.h" #include "regs.h"
#include "flags.h" #include "hard-reg-set.h"
#include "basic-block.h"
#include "reload.h"
#include "output.h" #include "output.h"
#include "function.h" #include "function.h"
#include "recog.h" #include "recog.h"
#include "resource.h" #include "flags.h"
#include "obstack.h"
static const char *const reg_class_names[] = REG_CLASS_NAMES;
/* ??? Consider a more sparse data structure? */
typedef struct def_uses
{
/* high bound of defs and uses */
int high_bound;
/* 1 if insn y defines a reg whose use crosses a call
y is the ordinal position of the insn within the block */
sbitmap require_call_save_reg;
/* REGNO x INSN y 1 if insn y sets reg x */ #define obstack_chunk_alloc xmalloc
sbitmap *defs; #define obstack_chunk_free free
/* REGNO x INSN y The register class for this def */ #ifndef REGNO_MODE_OK_FOR_BASE_P
enum reg_class *def_class; #define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) REGNO_OK_FOR_BASE_P (REGNO)
#endif
/* REGNO x INSN y 1 if insn y uses reg x */ #ifndef REG_MODE_OK_FOR_BASE_P
sbitmap *uses; #define REG_MODE_OK_FOR_BASE_P(REGNO, MODE) REG_OK_FOR_BASE_P (REGNO)
#endif
/* REGNO x INSN y The register class for this use */ static const char *const reg_class_names[] = REG_CLASS_NAMES;
enum reg_class *use_class;
}
def_uses;
#define DU_REG_CLASS(rc,r,high_bound,i) (rc[r * high_bound + i]) struct du_chain
{
struct du_chain *next_chain;
struct du_chain *next_use;
typedef struct ext_basic_blocks rtx insn;
{ rtx *loc;
/* n_basic_blocks x n_basic_blocks y 1 if bb y is in extended bb enum reg_class class;
having entry x */ unsigned int need_caller_save_reg:1;
sbitmap *basic_block; };
/* n_basic_blocks x n_basic_blocks y 1 if bb y is an exit block */ enum scan_actions
sbitmap *exit; {
} note_reference,
ext_basic_blocks; terminate_all_read,
terminate_overlapping_read,
#define UID_RUID_HIGH_BOUND 64 terminate_write,
#define DESTINATION 1 terminate_dead,
#define SOURCE 2 mark_read,
mark_write
static void build_def_use PARAMS ((int, ext_basic_blocks *, };
HARD_REG_SET *, def_uses *,
sbitmap *)); static const char * const scan_actions_name[] =
static int replace_reg_in_block PARAMS ((def_uses *, varray_type *, {
int, rtx, unsigned int)); "note_reference",
static int consider_def PARAMS ((rtx, int, def_uses *, int)); "terminate_all_read",
static int consider_available PARAMS ((rtx, int, HARD_REG_SET *, "terminate_overlapping_read",
int, def_uses *, int)); "terminate_write",
static void rr_replace_reg PARAMS ((rtx, rtx, int, rtx, int *)); "terminate_dead",
static int consider_use PARAMS ((rtx, int, int, int)); "mark_read",
static int condmove_p PARAMS ((rtx)); "mark_write"
static void dump_def_use_chain PARAMS ((HARD_REG_SET *, def_uses *, };
varray_type *));
static void dump_ext_bb_info PARAMS ((int, ext_basic_blocks *)); static struct obstack rename_obstack;
static void find_ext_basic_blocks PARAMS ((ext_basic_blocks *));
static void find_one_ext_basic_block PARAMS ((int, basic_block, sbitmap *, static void do_replace PARAMS ((struct du_chain *, int));
ext_basic_blocks *)); static void scan_rtx_reg PARAMS ((rtx, rtx *, enum reg_class,
static enum reg_class get_reg_class PARAMS ((rtx, rtx, int, enum scan_actions, enum op_type));
enum reg_class)); static void scan_rtx_address PARAMS ((rtx, rtx *, enum reg_class,
static rtx regno_first_use_in PARAMS ((unsigned int, rtx)); enum scan_actions));
static void scan_rtx PARAMS ((rtx, rtx *, enum reg_class,
enum scan_actions, enum op_type));
static struct du_chain *build_def_use PARAMS ((basic_block, HARD_REG_SET *));
static void dump_def_use_chain PARAMS ((struct du_chain *));
void void
regrename_optimize () regrename_optimize ()
{ {
int b, eb, i, inum, r, rc, replace_ok; int b;
rtx insn; char *first_obj;
def_uses du;
ext_basic_blocks ebb;
/* Registers used in a given class */
HARD_REG_SET class_regs;
/* Registers available for use as renaming registers */
HARD_REG_SET avail_regs;
/* Registers used in the block */
HARD_REG_SET regs_used;
/* Registers which have been used as renaming registers */
HARD_REG_SET renamed_regs;
HARD_REG_SET global_live_at_end, global_live_at_start;
HARD_REG_SET null_bitmap, tmp_bitmap;
/* 1 if insn y sets a register which is live at the end of the block */
sbitmap defs_live_exit;
/* Mapping from insn y (ordinal position in block) to INSN_UID */
varray_type uid_ruid;
/* Mapping from insn y (ordinal position in block) to block id */
varray_type uid_rbid;
/* Ordinal position in block of defining insn */
int *def_idx;
VARRAY_RTX_INIT (uid_ruid, UID_RUID_HIGH_BOUND + 1, "uid_ruid");
VARRAY_LONG_INIT (uid_rbid, UID_RUID_HIGH_BOUND + 1, "uid_rbid");
ebb.basic_block
= sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
sbitmap_vector_zero (ebb.basic_block, n_basic_blocks);
ebb.exit
= sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
sbitmap_vector_zero (ebb.exit, n_basic_blocks);
find_ext_basic_blocks (&ebb);
du.def_class = du.use_class = 0; gcc_obstack_init (&rename_obstack);
first_obj = (char *) obstack_alloc (&rename_obstack, 0);
/* Build uid_ruid and uid_rbid for this extended basic block */
for (b = 0; b < n_basic_blocks; b++) for (b = 0; b < n_basic_blocks; b++)
if (TEST_BIT (ebb.basic_block[b], b))
{ {
for (eb = du.high_bound = 0; eb < n_basic_blocks; eb++) basic_block bb = BASIC_BLOCK (b);
if (TEST_BIT (ebb.basic_block[b], eb)) struct du_chain *all_chains = 0;
{ HARD_REG_SET regs_used;
basic_block bb = BASIC_BLOCK (eb); HARD_REG_SET unavailable;
HARD_REG_SET regs_seen;
/* Calculate high bound for uid_ruid and allocate if necessary */
for (insn = bb->head;
insn != NEXT_INSN (bb->end);
du.high_bound++, insn = NEXT_INSN (insn))
{
int uid_ruid_high_bound = VARRAY_SIZE (uid_ruid);
if (du.high_bound + 4 >= uid_ruid_high_bound) CLEAR_HARD_REG_SET (regs_used);
{ CLEAR_HARD_REG_SET (unavailable);
VARRAY_GROW (uid_ruid, uid_ruid_high_bound * 2);
VARRAY_GROW (uid_rbid, uid_ruid_high_bound * 2);
}
VARRAY_RTX (uid_ruid, du.high_bound) = insn; if (rtl_dump_file)
VARRAY_LONG (uid_rbid, du.high_bound) = eb; fprintf (rtl_dump_file, "\nBasic block %d:\n", b);
}
}
CLEAR_HARD_REG_SET (null_bitmap); all_chains = build_def_use (bb, &regs_used);
CLEAR_HARD_REG_SET (class_regs);
CLEAR_HARD_REG_SET (regs_used);
CLEAR_HARD_REG_SET (avail_regs);
CLEAR_HARD_REG_SET (tmp_bitmap);
CLEAR_HARD_REG_SET (renamed_regs);
du.defs
= sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1);
sbitmap_vector_zero (du.defs, FIRST_PSEUDO_REGISTER);
du.uses
= sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1);
sbitmap_vector_zero (du.uses, FIRST_PSEUDO_REGISTER);
du.require_call_save_reg = sbitmap_alloc (du.high_bound + 1);
sbitmap_zero (du.require_call_save_reg);
defs_live_exit = sbitmap_alloc (du.high_bound + 1);
sbitmap_zero (defs_live_exit);
du.def_class
= xrealloc (du.def_class,
(sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER
* du.high_bound));
du.use_class
= xrealloc (du.use_class,
(sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER
* du.high_bound));
build_def_use (b, &ebb, &regs_used, &du, &defs_live_exit);
if (rtl_dump_file) if (rtl_dump_file)
{ dump_def_use_chain (all_chains);
dump_ext_bb_info (b, &ebb);
dump_def_use_chain (&global_live_at_end, &du, &uid_ruid);
}
/* Available registers are not: used in the block, live at the start, /* Available registers are not: used in the block, live at the start
live at the end, a register we've renamed to. */ live at the end, a register we've renamed to. */
/* ??? The current algorithm is pessimistic for extended basic blocks REG_SET_TO_HARD_REG_SET (unavailable, bb->global_live_at_start);
as it just treats them as a big basic block. */ REG_SET_TO_HARD_REG_SET (regs_seen, bb->global_live_at_end);
IOR_HARD_REG_SET (unavailable, regs_seen);
COPY_HARD_REG_SET (tmp_bitmap, regs_used); IOR_HARD_REG_SET (unavailable, regs_used);
REG_SET_TO_HARD_REG_SET (global_live_at_start,
BASIC_BLOCK (b)->global_live_at_start);
IOR_HARD_REG_SET (tmp_bitmap, global_live_at_start);
for (eb = 0; eb < n_basic_blocks; eb++)
if (TEST_BIT (ebb.basic_block[b], eb))
{
basic_block bb = BASIC_BLOCK (eb);
REG_SET_TO_HARD_REG_SET (global_live_at_end, /* Don't clobber traceback for noreturn functions. */
bb->global_live_at_end); if (frame_pointer_needed)
IOR_HARD_REG_SET (tmp_bitmap, global_live_at_end); {
SET_HARD_REG_BIT (unavailable, FRAME_POINTER_REGNUM);
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
SET_HARD_REG_BIT (unavailable, HARD_FRAME_POINTER_REGNUM);
#endif
} }
def_idx = xcalloc (du.high_bound, sizeof (int)); CLEAR_HARD_REG_SET (regs_seen);
while (all_chains)
/* Only consider registers in this extended block and in this class
that are defined more than once. Replace them if permissible. */
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
{ {
int avail_reg, ar_idx, def, def_cnt = 0, use_idx, call_idx; int n_uses;
struct du_chain *this = all_chains;
if (!TEST_HARD_REG_BIT (regs_used, r) struct du_chain *tmp, *last;
|| fixed_regs[r] HARD_REG_SET this_unavailable;
|| r == FRAME_POINTER_REGNUM) int reg = REGNO (*this->loc), treg;
continue;
/* Find def_idx[N] where hbound of N is the number of all_chains = this->next_chain;
definitions of this register in this block. and def_idx
is the ordinal position of this insn in the block. */
for (i = 0, def_idx[def_cnt] = 0; i < du.high_bound; i++)
if (TEST_BIT (du.defs[r], i)
&& consider_def (VARRAY_RTX (uid_ruid, i), r, &du, i))
{
int first_use = 1;
def_idx[def_cnt] = i;
/* Only consider definitions that have a use. */ /* Only rename once we've seen the reg more than once. */
for (use_idx = i + 1; use_idx < du.high_bound; use_idx++) if (! TEST_HARD_REG_BIT (regs_seen, reg))
{
if (TEST_BIT (du.uses[r], use_idx))
{
if (consider_use (VARRAY_RTX (uid_ruid, use_idx), r,
VARRAY_LONG (uid_rbid, i),
VARRAY_LONG (uid_rbid, use_idx)))
{
if (first_use)
{ {
first_use = 0; SET_HARD_REG_BIT (regs_seen, reg);
def_cnt++; continue;
}
} }
else
{
/* Don't consider def if we don't want this
use. */
if (!first_use)
def_cnt--;
break; if (fixed_regs[reg] || global_regs[reg])
} continue;
}
if (TEST_BIT (du.defs[r], use_idx)) COPY_HARD_REG_SET (this_unavailable, unavailable);
break;
}
/* Scan until the next def to avoid renaming /* Find last entry on chain (which has the need_caller_save bit),
parameter registers. */ count number of uses, and narrow the set of registers we can
/* ??? consider using CALL_INSN_FUNCTION_USAGE */ use for renaming. */
for (call_idx = i; call_idx <= use_idx; call_idx++) n_uses = 0;
if (VARRAY_RTX (uid_ruid, call_idx) for (last = this; last->next_use; last = last->next_use)
&& (GET_CODE (VARRAY_RTX (uid_ruid, call_idx)) {
== CALL_INSN)) n_uses++;
SET_BIT (du.require_call_save_reg, i); IOR_COMPL_HARD_REG_SET (this_unavailable,
reg_class_contents[last->class]);
} }
if (n_uses < 1)
if (def_cnt < 2)
continue; continue;
/* We have more than one def so rename until we exhaust IOR_COMPL_HARD_REG_SET (this_unavailable,
renaming registers. */ reg_class_contents[last->class]);
/* ??? Should we continue renaming round robin when we exhaust
renaming registers? */ if (last->need_caller_save_reg)
for (def = 0; def < def_cnt - 1; def++) IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
{
if (!TEST_BIT (defs_live_exit, def_idx[def]) /* Now potential_regs is a reasonable approximation, let's
&& (GET_RTX_CLASS have a closer look at each register still in there. */
(GET_CODE (VARRAY_RTX (uid_ruid, for (treg = 0; treg < FIRST_PSEUDO_REGISTER; treg++)
def_idx[def]))) == 'i'))
{ {
rtx reg_use if (TEST_HARD_REG_BIT (this_unavailable, treg)
= regno_first_use_in || fixed_regs[treg]
(r, PATTERN (VARRAY_RTX (uid_ruid, def_idx[def]))); || global_regs[treg]
/* Can't use regs which aren't saved by the prologue. */
|| (! regs_ever_live[treg]
&& ! call_used_regs[treg])
#ifdef HARD_REGNO_RENAME_OK
|| ! HARD_REGNO_RENAME_OK (reg, treg)
#endif
)
continue;
if (!reg_use) /* See whether it accepts all modes that occur in
definition and uses. */
for (tmp = this; tmp; tmp = tmp->next_use)
if (! HARD_REGNO_MODE_OK (treg, GET_MODE (*tmp->loc)))
break; break;
#ifdef STACK_REGS if (! tmp)
/* Don't bother with stacked float registers */
if (GET_MODE_CLASS (GET_MODE (reg_use)) == MODE_FLOAT)
break; break;
#endif
rc = (int) DU_REG_CLASS (du.def_class,
r, du.high_bound, def_idx[def]);
COPY_HARD_REG_SET (avail_regs,
reg_class_contents[(enum reg_class) rc]);
AND_COMPL_HARD_REG_SET (avail_regs, tmp_bitmap);
AND_COMPL_HARD_REG_SET (avail_regs, renamed_regs);
/* No available registers in this class */
GO_IF_HARD_REG_EQUAL (avail_regs, null_bitmap,
no_available_regs);
for (ar_idx = 0; ar_idx < FIRST_PSEUDO_REGISTER
&& TEST_HARD_REG_BIT (avail_regs, ar_idx); ar_idx++)
;
if (ar_idx == FIRST_PSEUDO_REGISTER)
goto no_available_regs;
/* Only try register renaming if there is an available
register in this class. */
for (ar_idx = 0; ar_idx < FIRST_PSEUDO_REGISTER; ar_idx++)
{
#ifdef REG_ALLOC_ORDER
avail_reg = reg_alloc_order[ar_idx];
#else
avail_reg = ar_idx;
#endif
if (consider_available (reg_use, avail_reg,
&avail_regs, rc, &du,
def_idx[def]))
goto found_avail_reg;
} }
if (rtl_dump_file) if (rtl_dump_file)
{ {
fprintf (rtl_dump_file, "Register %s in class %s", fprintf (rtl_dump_file, "Register %s in insn %d",
reg_names[r], reg_class_names[rc]); reg_names[reg], INSN_UID (last->insn));
fprintf (rtl_dump_file, " in insn %d", if (last->need_caller_save_reg)
INSN_UID (VARRAY_RTX (uid_ruid,
def_idx[def])));
if (TEST_BIT (du.require_call_save_reg,
def_idx[def]))
fprintf (rtl_dump_file, " crosses a call"); fprintf (rtl_dump_file, " crosses a call");
fprintf (rtl_dump_file, ". No available registers\n");
} }
goto try_next_def;
found_avail_reg:
SET_HARD_REG_BIT (renamed_regs, avail_reg);
CLEAR_HARD_REG_BIT (avail_regs, avail_reg);
/* Replace in destination. Replace in source for if (treg == FIRST_PSEUDO_REGISTER)
remainder of block until new register is defined
again */
replace_ok
= replace_reg_in_block (&du, &uid_ruid, def_idx[def],
reg_use, avail_reg);
/* Replace failed, so restore previous register */
if (!replace_ok)
{ {
replace_reg_in_block (&du, &uid_ruid, def_idx[def],
gen_rtx_REG (GET_MODE (reg_use),
avail_reg),
REGNO (reg_use));
if (rtl_dump_file) if (rtl_dump_file)
{ fprintf (rtl_dump_file, "; no available registers\n");
fprintf (rtl_dump_file, continue;
"Register %s in class %s Renaming as %s ",
reg_names[r], reg_class_names[rc],
reg_names[avail_reg]);
fprintf (rtl_dump_file,
"would not satisfy constraints\n");
}
} }
else if (rtl_dump_file) SET_HARD_REG_BIT (unavailable, treg);
{ do_replace (this, treg);
fprintf (rtl_dump_file,
"Register %s in class %s Renamed as %s ",
reg_names[r], reg_class_names[rc],
reg_names[avail_reg]);
fprintf (rtl_dump_file, "at insn %d\n",
INSN_UID (VARRAY_RTX (uid_ruid,
def_idx[def])));
}
}
try_next_def: if (rtl_dump_file)
continue; fprintf (rtl_dump_file, ", renamed as %s\n", reg_names[treg]);
} }
sbitmap_zero (du.defs[r]); obstack_free (&rename_obstack, first_obj);
no_available_regs:
continue;
} }
free (def_idx); obstack_free (&rename_obstack, NULL);
sbitmap_vector_free (du.defs);
sbitmap_vector_free (du.uses);
sbitmap_free (du.require_call_save_reg);
sbitmap_free (defs_live_exit);
CLEAR_HARD_REG_SET (regs_used);
CLEAR_HARD_REG_SET (renamed_regs);
for (inum = 0; inum < (int) VARRAY_SIZE (uid_ruid); inum++) if (rtl_dump_file)
VARRAY_RTX (uid_ruid, inum) = (rtx) 0; fputc ('\n', rtl_dump_file);
}
sbitmap_vector_free (ebb.basic_block); count_or_remove_death_notes (NULL, 1);
sbitmap_vector_free (ebb.exit); update_life_info (NULL, UPDATE_LIFE_LOCAL,
PROP_REG_INFO | PROP_DEATH_NOTES);
} }
/* Build def/use chain DU for extended basic block EBB having root B.
Also determine which regs are used, REGS_USED, and which insns define
a live at exit def, DEFS_LIVE_EXIT */
static void static void
build_def_use (b, ebb, regs_used, du, defs_live_exit) do_replace (chain, reg)
int b; struct du_chain *chain;
ext_basic_blocks *ebb; int reg;
HARD_REG_SET *regs_used;
def_uses *du;
sbitmap *defs_live_exit;
{ {
rtx insn; while (chain)
int eb, inum;
unsigned int r;
inum = 0;
for (eb = 0; eb < n_basic_blocks; eb++)
{ {
basic_block bb = BASIC_BLOCK (eb); *chain->loc = gen_rtx_REG (GET_MODE (*chain->loc), reg);
chain = chain->next_use;
}
}
if (!TEST_BIT (ebb->basic_block[b], eb))
continue;
for (insn = bb->head; static HARD_REG_SET *referenced_regs;
insn != NEXT_INSN (bb->end); static struct du_chain *open_chains;
inum++, insn = NEXT_INSN (insn)) static struct du_chain *closed_chains;
{
struct resources insn_res;
struct resources insn_sets;
if (! INSN_P (insn)) static void
continue; scan_rtx_reg (insn, loc, class, action, type)
rtx insn;
CLEAR_RESOURCE (&insn_sets); rtx *loc;
mark_set_resources (insn, &insn_sets, 0, MARK_DEST); enum reg_class class;
enum scan_actions action;
enum op_type type;
{
struct du_chain **p;
rtx x = *loc;
enum machine_mode mode = GET_MODE (x);
int this_regno = REGNO (x);
int this_nregs = HARD_REGNO_NREGS (this_regno, mode);
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) if (action == note_reference)
{ {
if (!TEST_HARD_REG_BIT (insn_sets.regs, r)) while (this_nregs-- > 0)
continue; SET_HARD_REG_BIT (*referenced_regs, this_regno + this_nregs);
return;
SET_HARD_REG_BIT (*regs_used, r);
if (REGNO_REG_SET_P (bb->global_live_at_end, r))
SET_BIT (*defs_live_exit, inum);
if (!insn_sets.memory)
SET_BIT (du->defs[r], inum);
DU_REG_CLASS (du->def_class, r, du->high_bound, inum)
= get_reg_class (insn, regno_first_use_in (r, PATTERN (insn)),
DESTINATION, NO_REGS);
} }
CLEAR_RESOURCE (&insn_res); if (action == mark_write)
mark_referenced_resources (insn, &insn_res, 0);
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
{ {
if (!TEST_HARD_REG_BIT (insn_res.regs, r)) if (type == OP_OUT)
continue; {
struct du_chain *this = (struct du_chain *)
SET_HARD_REG_BIT (*regs_used, r); obstack_alloc (&rename_obstack, sizeof (struct du_chain));
SET_BIT (du->uses[r], inum); this->next_use = 0;
DU_REG_CLASS (du->use_class, r, du->high_bound, inum) this->next_chain = open_chains;
= get_reg_class (insn, regno_use_in (r, PATTERN (insn)), this->loc = loc;
SOURCE, NO_REGS); this->insn = insn;
} this->class = class;
this->need_caller_save_reg = 0;
open_chains = this;
} }
return;
} }
free_resource_info (); if ((type == OP_OUT && action != terminate_write)
} || (type != OP_OUT && action == terminate_write))
return;
/* Return nonzero if regno AVAIL_REG can replace REG_DEF for insns in UID_RUID
starting at insn DEF in def/use chain DU. */
static int for (p = &open_chains; *p;)
replace_reg_in_block (du, uid_ruid, def, reg_def, avail_reg) {
def_uses *du; struct du_chain *this = *p;
varray_type *uid_ruid; int regno = REGNO (*this->loc);
int def; int nregs = HARD_REGNO_NREGS (regno, GET_MODE (*this->loc));
rtx reg_def; int exact_match = (regno == this_regno && nregs == this_nregs);
unsigned int avail_reg;
{ if (regno + nregs <= this_regno
int du_idx, status = 1; || this_regno + this_nregs <= regno)
int last_replaced_insn; p = &this->next_chain;
unsigned int r = REGNO (reg_def); else if (action == mark_read)
rtx death_note; {
rtx reg_notes; if (! exact_match)
rtx reg_use = 0; abort ();
rtx new_reg = gen_rtx_REG (GET_MODE (reg_def), avail_reg); if (class == NO_REGS)
abort ();
rr_replace_reg (reg_def, new_reg, DESTINATION,
VARRAY_RTX (*uid_ruid, def), &status); this = (struct du_chain *)
obstack_alloc (&rename_obstack, sizeof (struct du_chain));
if (!status) this->next_use = *p;
return status; this->next_chain = (*p)->next_chain;
this->loc = loc;
death_note = 0; this->insn = insn;
/* This typically happens if a constraint check failed and the register this->class = class;
changes are being reversed. */ this->need_caller_save_reg = 0;
for (reg_notes = REG_NOTES (VARRAY_RTX (*uid_ruid, def)); *p = this;
reg_notes; reg_notes = XEXP (reg_notes, 1)) return;
{
if (REG_NOTE_KIND (reg_notes) == REG_DEAD
&& REGNO (XEXP (reg_notes, 0)) == avail_reg)
death_note = reg_notes;
} }
else if (action != terminate_overlapping_read || ! exact_match)
if (death_note)
remove_note (VARRAY_RTX (*uid_ruid, def), death_note);
/* The old destination is now dead if it is also a source. */
if (regno_use_in (r, PATTERN (VARRAY_RTX (*uid_ruid, def))))
REG_NOTES (VARRAY_RTX (*uid_ruid, def))
= gen_rtx_EXPR_LIST (REG_DEAD, reg_def,
REG_NOTES (VARRAY_RTX (*uid_ruid,
def)));
last_replaced_insn = 0;
/* Now replace in the uses. */
for (du_idx = def + 1; du_idx < du->high_bound; du_idx++)
{ {
if (! INSN_P (VARRAY_RTX (*uid_ruid, du_idx))) struct du_chain *next = this->next_chain;
continue;
reg_use = regno_use_in (r, PATTERN (VARRAY_RTX (*uid_ruid, du_idx))); /* Whether the terminated chain can be used for renaming
depends on the action and this being an exact match.
if (reg_use && TEST_BIT (du->uses[r], du_idx)) In either case, we remove this element from open_chains. */
{
new_reg = gen_rtx_REG (GET_MODE (reg_use), avail_reg);
rr_replace_reg (reg_use, new_reg, SOURCE, if ((action == terminate_dead || action == terminate_write)
VARRAY_RTX (*uid_ruid, du_idx), &status); && exact_match)
death_note = find_reg_note (VARRAY_RTX (*uid_ruid, du_idx),
REG_DEAD, reg_use);
if (death_note)
{ {
REG_NOTES (VARRAY_RTX (*uid_ruid, du_idx)) this->next_chain = closed_chains;
= gen_rtx_EXPR_LIST (REG_DEAD, new_reg, closed_chains = this;
REG_NOTES (VARRAY_RTX (*uid_ruid, if (rtl_dump_file)
du_idx))); fprintf (rtl_dump_file,
remove_note (VARRAY_RTX (*uid_ruid, du_idx), "Closing chain %s at insn %d (%s)\n",
find_reg_note (VARRAY_RTX (*uid_ruid, du_idx), reg_names[REGNO (*this->loc)], INSN_UID (insn),
REG_DEAD, reg_use)); scan_actions_name[(int) action]);
}
} }
else
/* This insn may contain shared rtl replaced in the previous iteration.
Treat this equivalent to the rr_replace_reg case. */
if (TEST_BIT (du->uses[r], du_idx))
{ {
last_replaced_insn = du_idx; if (rtl_dump_file)
fprintf (rtl_dump_file,
SET_BIT (du->uses[avail_reg], du_idx); "Discarding chain %s at insn %d (%s)\n",
RESET_BIT (du->uses[r], du_idx); reg_names[REGNO (*this->loc)], INSN_UID (insn),
if (!status) scan_actions_name[(int) action]);
return status;
}
if (TEST_BIT (du->defs[r], du_idx))
break;
} }
*p = next;
/* Add REG_DEAD note for replaced register at last use. */
if (last_replaced_insn)
{
new_reg = regno_use_in (avail_reg,
PATTERN (VARRAY_RTX (*uid_ruid,
last_replaced_insn)));
if (new_reg
&& ! find_reg_note (VARRAY_RTX (*uid_ruid, last_replaced_insn),
REG_DEAD, new_reg))
{
REG_NOTES (VARRAY_RTX (*uid_ruid, last_replaced_insn))
= gen_rtx_EXPR_LIST (REG_DEAD, new_reg,
REG_NOTES (VARRAY_RTX (*uid_ruid,
last_replaced_insn)));
remove_note (VARRAY_RTX (*uid_ruid, last_replaced_insn),
find_reg_note (VARRAY_RTX (*uid_ruid, last_replaced_insn),
REG_DEAD, reg_use));
} }
else
p = &this->next_chain;
} }
return status;
} }
/* Try to replace REG_USE in X with REG_SUB if INSN has a REPLACE_TYPE. /* Adapted from find_reloads_address_1. CLASS is INDEX_REG_CLASS or
STATUS is zero if the resulting pattern is not valid. */ BASE_REG_CLASS depending on how the register is being considered. */
static void static void
rr_replace_reg (reg_use, reg_sub, replace_type, insn, status) scan_rtx_address (insn, loc, class, action)
rtx reg_use;
rtx reg_sub;
int replace_type;
rtx insn; rtx insn;
int *status; rtx *loc;
enum reg_class class;
enum scan_actions action;
{ {
int i; rtx x = *loc;
int changed = 0; RTX_CODE code = GET_CODE (x);
const char *fmt;
int i, j;
/* We only perform replacements on operands, since everything else if (action == mark_write)
is by definition hard-coded. Begin by extracting insn information return;
so that we know where the operands and dups exist. */
extract_insn (insn);
for (i = recog_data.n_operands - 1; i >= 0; --i) switch (code)
{ {
rtx op; case PLUS:
/* Match replace_type with operand_type and skip those we aren't
supposed to touch. Note that OP_INOUT does _not_ match either
replace_type. */
if (replace_type == DESTINATION && recog_data.operand_type[i] != OP_OUT)
continue;
if (replace_type == SOURCE && recog_data.operand_type[i] != OP_IN)
continue;
op = recog_data.operand[i];
if (GET_CODE (op) != REG)
continue;
if (REGNO (op) == REGNO (reg_use))
{ {
rtx new = reg_sub; rtx orig_op0 = XEXP (x, 0);
if (GET_MODE (op) != GET_MODE (reg_use)) rtx orig_op1 = XEXP (x, 1);
new = gen_rtx_REG (GET_MODE (op), REGNO (reg_sub)); RTX_CODE code0 = GET_CODE (orig_op0);
RTX_CODE code1 = GET_CODE (orig_op1);
validate_change (insn, recog_data.operand_loc[i], new, 1); rtx op0 = orig_op0;
recog_data.operand[i] = new; rtx op1 = orig_op1;
rtx *locI = NULL;
rtx *locB = NULL;
changed |= 1 << i; if (GET_CODE (op0) == SUBREG)
} {
op0 = SUBREG_REG (op0);
code0 = GET_CODE (op0);
} }
/* Any MATCH_DUP's for changed operands must also be changed. */ if (GET_CODE (op1) == SUBREG)
/* ??? This more or less assumes that operand_type is correct, in
that the dup will be of the appropriate replace_type. */
for (i = recog_data.n_dups - 1; i >= 0; i--)
{ {
int opno = recog_data.dup_num[i]; op1 = SUBREG_REG (op1);
if ((changed >> opno) & 1) code1 = GET_CODE (op1);
validate_change (insn, recog_data.dup_loc[i],
recog_data.operand[i], 1);
} }
/* Verify that the changes applied so far result in a recognizable insn. */ if (code0 == MULT || code0 == SIGN_EXTEND || code0 == TRUNCATE
if (! apply_change_group ()) || code0 == ZERO_EXTEND || code1 == MEM)
{ {
*status = 0; locI = &XEXP (x, 0);
return; locB = &XEXP (x, 1);
} }
else if (code1 == MULT || code1 == SIGN_EXTEND || code1 == TRUNCATE
/* Verify that there are no other references of the given type to the || code1 == ZERO_EXTEND || code0 == MEM)
register in question. That is, there are no hard-coded references
to this hard register left in the insn. */
if (replace_type == DESTINATION)
{ {
if (reg_set_p (reg_use, insn)) locI = &XEXP (x, 1);
*status = 0; locB = &XEXP (x, 0);
} }
else if (code0 == CONST_INT || code0 == CONST
|| code0 == SYMBOL_REF || code0 == LABEL_REF)
locB = &XEXP (x, 1);
else if (code1 == CONST_INT || code1 == CONST
|| code1 == SYMBOL_REF || code1 == LABEL_REF)
locB = &XEXP (x, 0);
else if (code0 == REG && code1 == REG)
{
int index_op;
if (REG_OK_FOR_INDEX_P (op0)
&& REG_MODE_OK_FOR_BASE_P (op1, mode))
index_op = 0;
else if (REG_OK_FOR_INDEX_P (op1)
&& REG_MODE_OK_FOR_BASE_P (op0, mode))
index_op = 1;
else if (REG_MODE_OK_FOR_BASE_P (op1, mode))
index_op = 0;
else if (REG_MODE_OK_FOR_BASE_P (op0, mode))
index_op = 1;
else if (REG_OK_FOR_INDEX_P (op1))
index_op = 1;
else else
index_op = 0;
locI = &XEXP (x, index_op);
locB = &XEXP (x, !index_op);
}
else if (code0 == REG)
{ {
if (reg_referenced_p (reg_use, PATTERN (insn))) locI = &XEXP (x, 0);
*status = 0; locB = &XEXP (x, 1);
}
else if (code1 == REG)
{
locI = &XEXP (x, 1);
locB = &XEXP (x, 0);
} }
}
/* Can REGNO in INSN be considered for renaming, given def INUM in d/u if (locI)
chain DU? */ scan_rtx_address (insn, locI, INDEX_REG_CLASS, action);
if (locB)
scan_rtx_address (insn, locB, BASE_REG_CLASS, action);
return;
}
static int case POST_INC:
consider_def (insn, regno, du, inum) case POST_DEC:
rtx insn; case POST_MODIFY:
int regno; case PRE_INC:
def_uses *du ATTRIBUTE_UNUSED; case PRE_DEC:
int inum ATTRIBUTE_UNUSED; case PRE_MODIFY:
{ #ifndef AUTO_INC_DEC
/* Don't rename windowed registers across a call */ class = NO_REGS;
#ifdef INCOMING_REGNO
if (TEST_BIT (du->require_call_save_reg, inum)
&& INCOMING_REGNO (regno) != regno)
return 0;
#endif #endif
break;
/* Don't consider conditional moves. Predicate architectures may case MEM:
use two complementary conditional moves and the regno shouldn't change */ scan_rtx_address (insn, &XEXP (x, 0), BASE_REG_CLASS, action);
if (condmove_p (insn)) return;
return 0;
/* Don't rename call used registers across a call */ case REG:
if (!(GET_CODE (insn) == CALL_INSN scan_rtx_reg (insn, loc, class, action, OP_IN);
&& TEST_HARD_REG_BIT (call_used_reg_set, regno))) return;
return 1;
else
return 0;
}
/* Can the use of REGNO in INSN of block USE_BLOCK be considered for renaming default:
for a def in def_block? */ break;
}
static int fmt = GET_RTX_FORMAT (code);
consider_use (insn, regno, def_block, use_block) for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
rtx insn;
int regno;
int def_block;
int use_block;
{
rtx reg_use;
edge e;
basic_block ub = BASIC_BLOCK (use_block);
if (! INSN_P (insn))
return 0;
/* If a use's basic block is different than the def's basic block,
then insure another predecessor does not also define this register */
if (def_block != use_block)
for (e = ub->pred; e; e = e->pred_next)
if (e->src->index != def_block
&& e->src->index != -1
&& REGNO_REG_SET_P (BASIC_BLOCK (e->src->index)->global_live_at_end,
regno))
return 0;
/* Don't consider conditional moves. Predicate architectures may
use two complementary conditional moves and the regno shouldn't change */
if (condmove_p (insn))
return 0;
reg_use = regno_first_use_in (regno, PATTERN (insn));
if (reg_use)
{ {
/* Don't consider multi-reg values. */ if (fmt[i] == 'e')
if (HARD_REGNO_NREGS (regno, GET_MODE (reg_use)) != 1 scan_rtx_address (insn, &XEXP (x, i), class, action);
&& GET_MODE (reg_use) != CCmode) else if (fmt[i] == 'E')
return 0; for (j = XVECLEN (x, i) - 1; j >= 0; j--)
scan_rtx_address (insn, &XVECEXP (x, i, j), class, action);
/* Don't consider register if the only use is in a USE */
return ! reg_mentioned_p (gen_rtx_USE (VOIDmode, reg_use),
PATTERN (insn));
} }
else
return 0;
} }
/* Can REG_USE be replaced by regno AVAIL_REG if it is in AVAIL_REGS static void
and it is in regclass RC, given insn INUM of def/use chain DU? */ scan_rtx (insn, loc, class, action, type)
rtx insn;
static int rtx *loc;
consider_available (reg_use, avail_reg, avail_regs, rc, du, inum) enum reg_class class;
rtx reg_use; enum scan_actions action;
int avail_reg; enum op_type type;
HARD_REG_SET *avail_regs;
int rc;
def_uses *du;
int inum;
{ {
if (!TEST_HARD_REG_BIT (*avail_regs, avail_reg)) const char *fmt;
return 0; rtx x = *loc;
enum rtx_code code = GET_CODE (x);
int i, j;
if (fixed_regs[avail_reg]) code = GET_CODE (x);
return 0; switch (code)
{
case CONST:
case CONST_INT:
case CONST_DOUBLE:
case SYMBOL_REF:
case LABEL_REF:
case CC0:
case PC:
return;
#ifdef HARD_REGNO_RENAME_OK case REG:
if (!HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg)) scan_rtx_reg (insn, loc, class, action, type);
return 0; return;
#endif
/* Don't consider windowed leaf registers which will be renamed by case MEM:
leaf_renumber_regs */ scan_rtx_address (insn, &XEXP (x, 0), BASE_REG_CLASS, action);
#ifdef LEAF_REG_REMAP return;
if (current_function_uses_only_leaf_regs)
if (LEAF_REG_REMAP (avail_reg) < 0)
return 0;
#endif
/* A register is considered available if it is available at the beginning of case SET:
the basic block. We may want to refine this to when a register becomes scan_rtx (insn, &SET_SRC (x), class, action, OP_IN);
available within the block. We don't consider multi-reg values. */ scan_rtx (insn, &SET_DEST (x), class, action, OP_OUT);
/* ??? Consider a representation that would allow multi-reg support? */ return;
if (!TEST_HARD_REG_BIT (reg_class_contents[(enum reg_class) rc], avail_reg)
|| !HARD_REGNO_MODE_OK (avail_reg, GET_MODE (reg_use))
|| (HARD_REGNO_NREGS (avail_reg, GET_MODE (reg_use)) != 1
&& GET_MODE (reg_use) != CCmode)
|| (call_fixed_regs[avail_reg]
#ifdef HARD_REGNO_RENAME_OK
&& !HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg)
#endif
)
|| (TEST_BIT (du->require_call_save_reg, inum)
&& (call_used_regs[avail_reg] || call_used_regs[REGNO (reg_use)])))
return 0;
/* If register is a callee-saved register it must be saved in the frame.
call saved registers can not be added to regs_ever_live after reload,
as it would invalidate most elimination offsets */
return regs_ever_live[avail_reg] || call_used_regs[avail_reg];
}
/* Return 1 if INSN is a conditional move */ case STRICT_LOW_PART:
scan_rtx (insn, &XEXP (x, 0), class, action, OP_INOUT);
return;
static int case ZERO_EXTRACT:
condmove_p (insn) case SIGN_EXTRACT:
rtx insn; scan_rtx (insn, &XEXP (x, 0), class, action,
{ type == OP_IN ? OP_IN : OP_INOUT);
return (GET_CODE (insn) == INSN scan_rtx (insn, &XEXP (x, 1), class, action, OP_IN);
&& GET_CODE (PATTERN (insn)) == SET scan_rtx (insn, &XEXP (x, 2), class, action, OP_IN);
&& GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE); return;
}
/* Searches X for the first reference to REGNO, returning the rtx of the case POST_INC:
reference found if any. Otherwise, returns NULL_RTX. */ case PRE_INC:
case POST_DEC:
case PRE_DEC:
case POST_MODIFY:
case PRE_MODIFY:
/* Should only happen inside MEM. */
abort ();
case CLOBBER:
scan_rtx (insn, &SET_DEST (x), class, action, OP_OUT);
return;
static rtx case EXPR_LIST:
regno_first_use_in (regno, x) scan_rtx (insn, &XEXP (x, 0), class, action, type);
unsigned int regno; if (XEXP (x, 1))
rtx x; scan_rtx (insn, &XEXP (x, 1), class, action, type);
{ return;
register const char *fmt;
int i, j;
rtx tem;
if (GET_CODE (x) == REG && REGNO (x) == regno) default:
return x; break;
}
fmt = GET_RTX_FORMAT (GET_CODE (x)); fmt = GET_RTX_FORMAT (code);
for (i = 0; i <= GET_RTX_LENGTH (GET_CODE (x)) - 1; i++) for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{ {
if (fmt[i] == 'e') if (fmt[i] == 'e')
{ scan_rtx (insn, &XEXP (x, i), class, action, type);
if ((tem = regno_first_use_in (regno, XEXP (x, i))))
return tem;
}
else if (fmt[i] == 'E') else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--) for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if ((tem = regno_first_use_in (regno, XVECEXP (x, i, j)))) scan_rtx (insn, &XVECEXP (x, i, j), class, action, type);
return tem;
} }
return 0;
} }
/* Dump def/use chain DU to RTL_DUMP_FILE, given insns in UID_RUID and /* Build def/use chain */
which regs are live at end, GLOBAL_LIVE_AT_END */
static void static struct du_chain *
dump_def_use_chain (global_live_at_end, du, uid_ruid) build_def_use (bb, regs_used)
HARD_REG_SET *global_live_at_end; basic_block bb;
def_uses *du; HARD_REG_SET *regs_used;
varray_type *uid_ruid;
{ {
unsigned int r; rtx insn;
int inum;
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) open_chains = closed_chains = NULL;
{ referenced_regs = regs_used;
int set = 0;
for (inum = 0; inum <= du->high_bound; inum++) for (insn = bb->head; ; insn = NEXT_INSN (insn))
{ {
rtx insn = VARRAY_RTX (*uid_ruid, inum); if (INSN_P (insn))
#if 0
if (!insn
|| GET_RTX_CLASS (GET_CODE
(insn)) != 'i')
continue;
reg_use = regno_first_use_in (r, PATTERN (insn));
if (!reg_use)
continue;
#endif
if (!set && (TEST_BIT (du->defs[r], inum)
|| TEST_BIT (du->uses[r], inum)))
{ {
fprintf (rtl_dump_file, "Register %s: ", reg_names[r]); int n_ops;
if (fixed_regs[r]) rtx note;
fprintf (rtl_dump_file, "Fixed "); rtx old_operands[MAX_RECOG_OPERANDS];
else if (call_fixed_regs[r]) rtx old_dups[MAX_DUP_OPERANDS];
fprintf (rtl_dump_file, "Call Fixed "); int i;
if (TEST_HARD_REG_BIT (*global_live_at_end, r)) int alt;
fprintf (rtl_dump_file, "Live at Exit "); int predicated;
set = 1;
} /* Record all mentioned registers in regs_used. */
scan_rtx (insn, &PATTERN (insn), NO_REGS, note_reference, OP_IN);
/* Process the insn, determining its effect on the def-use
chains. We perform the following steps with the register
references in the insn:
(1) Any read that overlaps an open chain, but doesn't exactly
match, causes that chain to be closed. We can't deal
with overlaps yet.
(2) Any read outside an operand causes any chain it overlaps
with to be closed, since we can't replace it.
(3) Any read inside an operand is added if there's already
an open chain for it.
(4) For any REG_DEAD note we find, close open chains that
overlap it.
(5) For any write we find, close open chains that overlap it.
(6) For any write we find in an operand, make a new chain.
(7) For any REG_UNUSED, close any chains we just opened. */
if (TEST_BIT (du->defs[r], inum)) extract_insn (insn);
fprintf (rtl_dump_file, "=%d ", INSN_UID (insn)); constrain_operands (1);
if (TEST_BIT (du->uses[r], inum)) preprocess_constraints ();
fprintf (rtl_dump_file, "%d ", INSN_UID (insn)); alt = which_alternative;
} n_ops = recog_data.n_operands;
if (set) /* Simplify the code below by rewriting things to reflect
fprintf (rtl_dump_file, "\n"); matching constraints. Also promote OP_OUT to OP_INOUT
in predicated instructions. */
predicated = GET_CODE (PATTERN (insn)) == COND_EXEC;
for (i = 0; i < n_ops; ++i)
{
int matches = recog_op_alt[i][alt].matches;
if (matches >= 0)
recog_op_alt[i][alt].class = recog_op_alt[matches][alt].class;
if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
|| (predicated && recog_data.operand_type[i] == OP_OUT))
recog_data.operand_type[i] = OP_INOUT;
} }
}
/* Dump info for extended basic block EBB having root EB */ /* Step 1: Close chains for which we have overlapping reads. */
for (i = 0; i < n_ops; i++)
scan_rtx (insn, recog_data.operand_loc[i],
NO_REGS, terminate_overlapping_read,
recog_data.operand_type[i]);
static void /* Step 2: Close chains for which we have reads outside operands.
dump_ext_bb_info (eb, ebb) We do this by munging all operands into CC0, and closing
int eb; everything remaining. */
ext_basic_blocks *ebb;
{
int b;
int have_ebb = 0;
for (b = 0; b < n_basic_blocks; b++) for (i = 0; i < n_ops; i++)
{
if (TEST_BIT (ebb->basic_block[eb], b))
{
if (!have_ebb)
{ {
#ifndef RENAME_EXTENDED_BLOCKS old_operands[i] = recog_data.operand[i];
fprintf (rtl_dump_file, "\nBasic block %d: ", b); /* Don't squash match_operator or match_parallel here, since
#else we don't know that all of the contained registers are
fprintf (rtl_dump_file, "\nExtended basic block %d: ", b); reachable by proper operands. */
#endif if (recog_data.constraints[i][0] == '\0')
have_ebb = 1; continue;
*recog_data.operand_loc[i] = cc0_rtx;
} }
fprintf (rtl_dump_file, "%d ", b); for (i = 0; i < recog_data.n_dups; i++)
{
old_dups[i] = *recog_data.dup_loc[i];
*recog_data.dup_loc[i] = cc0_rtx;
} }
if (TEST_BIT (ebb->exit[eb], b)) scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read, OP_IN);
fprintf (rtl_dump_file, "(exit) ");
}
if (have_ebb) for (i = 0; i < recog_data.n_dups; i++)
fprintf (rtl_dump_file, "\n"); *recog_data.dup_loc[i] = old_dups[i];
} for (i = 0; i < n_ops; i++)
*recog_data.operand_loc[i] = old_operands[i];
/* Initialize EBB with extended basic block info if RENAME_EXTENDED_BLOCKS is /* Step 2B: Can't rename function call argument registers. */
defined. Otherwise just use basic blocks */ if (GET_CODE (insn) == CALL_INSN && CALL_INSN_FUNCTION_USAGE (insn))
scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
NO_REGS, terminate_all_read, OP_IN);
static void /* Step 3: Append to chains for reads inside operands. */
find_ext_basic_blocks (ebb) for (i = 0; i < n_ops + recog_data.n_dups; i++)
ext_basic_blocks *ebb; {
{ int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
sbitmap bb_processed; rtx *loc = (i < n_ops
int b; ? recog_data.operand_loc[opn]
: recog_data.dup_loc[i - n_ops]);
enum reg_class class = recog_op_alt[opn][alt].class;
enum op_type type = recog_data.operand_type[opn];
bb_processed = sbitmap_alloc (n_basic_blocks); /* Don't scan match_operand here, since we've no reg class
sbitmap_zero (bb_processed); information to pass down. Any operands that we could
substitute in will be represented elsewhere. */
if (recog_data.constraints[opn][0] == '\0')
continue;
#ifndef RENAME_EXTENDED_BLOCKS if (recog_op_alt[opn][alt].is_address)
for (b = 0; b < n_basic_blocks; b++) scan_rtx_address (insn, loc, class, mark_read);
{ else
basic_block bb = BASIC_BLOCK (b); scan_rtx (insn, loc, class, mark_read, type);
SET_BIT (ebb->basic_block[bb->index], bb->index);
} }
#else
for (b = 0; b < n_basic_blocks; b++)
{
basic_block bb = BASIC_BLOCK (b); /* Step 4: Close chains for registers that die here. */
if (!TEST_BIT (bb_processed, b)) for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if (REG_NOTE_KIND (note) == REG_DEAD)
scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead, OP_IN);
/* Step 4B: If this is a call, any chain live at this point
requires a caller-saved reg. */
if (GET_CODE (insn) == CALL_INSN)
{ {
find_one_ext_basic_block (bb->index, bb, &bb_processed, ebb); struct du_chain *p;
for (p = open_chains; p; p = p->next_chain)
{
struct du_chain *p2;
for (p2 = p; p2->next_use; p2 = p2->next_use)
/* nothing */;
p2->need_caller_save_reg = 1;
} }
} }
#endif
sbitmap_free (bb_processed);
}
/* Find one extended basic block EBB having root ENTRY containing block
BB */
static void /* Step 5: Close open chains that overlap writes. Similar to
find_one_ext_basic_block (entry, bb, bb_processed, ebb) step 2, we hide in-out operands, since we do not want to
int entry; close these chains. */
basic_block bb;
sbitmap *bb_processed;
ext_basic_blocks *ebb;
{
edge e;
if (!TEST_BIT (*bb_processed, bb->index)) for (i = 0; i < n_ops; i++)
{ {
SET_BIT (ebb->basic_block[entry], bb->index); old_operands[i] = recog_data.operand[i];
SET_BIT (*bb_processed, bb->index); if (recog_data.operand_type[i] == OP_INOUT)
*recog_data.operand_loc[i] = cc0_rtx;
} }
for (i = 0; i < recog_data.n_dups; i++)
for (e = bb->succ; e; e = e->succ_next)
if (!TEST_BIT (*bb_processed, e->dest->index))
{ {
if (!e->dest->pred->pred_next int opn = recog_data.dup_num[i];
&& (!TEST_BIT (*bb_processed, e->dest->index))) old_dups[i] = *recog_data.dup_loc[i];
find_one_ext_basic_block (entry, e->dest, bb_processed, ebb); if (recog_data.operand_type[opn] == OP_INOUT)
else *recog_data.dup_loc[i] = cc0_rtx;
SET_BIT (ebb->exit[entry], bb->index);
} }
}
/* Find the register class for register REG_USE having TYPE (DESTINATION or scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN);
SOURCE) in INSN. Use DEFAULT_CLASS if we cannot determine a class. */
static enum reg_class for (i = 0; i < recog_data.n_dups; i++)
get_reg_class (insn, reg_use, type, default_class) *recog_data.dup_loc[i] = old_dups[i];
rtx insn; for (i = 0; i < n_ops; i++)
rtx reg_use; *recog_data.operand_loc[i] = old_operands[i];
int type;
enum reg_class default_class;
{
int alt, id = 0;
extract_insn (insn); /* Step 6: Begin new chains for writes inside operands. */
constrain_operands (1); /* ??? Many targets have output constraints on the SET_DEST
alt = which_alternative; of a call insn, which is stupid, since these are certainly
ABI defined hard registers. Don't change calls at all. */
if (GET_CODE (insn) != CALL_INSN)
for (i = 0; i < n_ops + recog_data.n_dups; i++)
{
int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
rtx *loc = (i < n_ops
? recog_data.operand_loc[opn]
: recog_data.dup_loc[i - n_ops]);
enum reg_class class = recog_op_alt[opn][alt].class;
preprocess_constraints (); if (recog_data.operand_type[opn] == OP_OUT)
scan_rtx (insn, loc, class, mark_write, OP_OUT);
}
if (type == DESTINATION) /* Step 7: Close chains for registers that were never
{ really used here. */
for (id = 0; id < recog_data.n_operands; id++) for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if (rtx_equal_p (recog_data.operand[id], reg_use)) if (REG_NOTE_KIND (note) == REG_UNUSED)
scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead, OP_IN);
}
if (insn == bb->end)
break; break;
} }
else if (type == SOURCE) /* Since we close every chain when we find a REG_DEAD note, anything that
for (id = recog_data.n_operands - 1; id >= 0; id--) is still open lives past the basic block, so it can't be renamed. */
if (rtx_equal_p (recog_data.operand[id], reg_use)) return closed_chains;
break; }
if (id == -1 || id == recog_data.n_operands) /* Dump all def/use chains in CHAINS to RTL_DUMP_FILE. They are
return default_class; printed in reverse order as that's how we build them. */
return recog_op_alt[id][alt].class; static void
dump_def_use_chain (chains)
struct du_chain *chains;
{
while (chains)
{
struct du_chain *this = chains;
int r = REGNO (*this->loc);
int nregs = HARD_REGNO_NREGS (r, GET_MODE (*this->loc));
fprintf (rtl_dump_file, "Register %s (%d):", reg_names[r], nregs);
while (this)
{
fprintf (rtl_dump_file, " %d [%s]", INSN_UID (this->insn),
reg_class_names[this->class]);
this = this->next_use;
}
fprintf (rtl_dump_file, "\n");
chains = chains->next_chain;
}
} }
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