flow.c (count_reg_sets): New function.

* flow.c (count_reg_sets): New function.
        (count_reg_sets_1, count_ref_references): Likewise.
        (recompute_reg_usage): Likewise.
        * rtl.h (recompute_reg_usage): Add prototype.
        * toplev.c (rest_of_compilation): Call recompute_reg_usage just
        before local register allocation.

From-SVN: r20756
This commit is contained in:
Jeffrey A Law 1998-06-27 15:51:49 +00:00 committed by Jeff Law
parent ea56ab2ae0
commit 4c649323e3
4 changed files with 293 additions and 0 deletions

View File

@ -1,3 +1,12 @@
Sat Jun 27 16:45:42 1998 Jeffrey A Law (law@cygnus.com)
* flow.c (count_reg_sets): New function.
(count_reg_sets_1, count_ref_references): Likewise.
(recompute_reg_usage): Likewise.
* rtl.h (recompute_reg_usage): Add prototype.
* toplev.c (rest_of_compilation): Call recompute_reg_usage just
before local register allocation.
Sat Jun 27 13:15:30 1998 Richard Henderson <rth@cygnus.com>
* alpha.md (negsf, negdf): Revert Jan 22 change.

View File

@ -284,6 +284,9 @@ static int_list_ptr add_int_list_node PROTO ((int_list_block **,
int_list **, int));
static void init_regset_vector PROTO ((regset *, int,
struct obstack *));
static void count_reg_sets_1 PROTO ((rtx));
static void count_reg_sets PROTO ((rtx));
static void count_reg_references PROTO ((rtx));
/* Find basic blocks of the current function.
F is the first insn of the function and NREGS the number of register numbers
@ -3997,3 +4000,281 @@ compute_dominators (dominators, post_dominators, s_preds, s_succs)
free (temp_bitmap);
}
/* Count for a single SET rtx, X. */
static void
count_reg_sets_1 (x)
rtx x;
{
register int regno;
register rtx reg = SET_DEST (x);
/* Find the register that's set/clobbered. */
while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
|| GET_CODE (reg) == SIGN_EXTRACT
|| GET_CODE (reg) == STRICT_LOW_PART)
reg = XEXP (reg, 0);
if (GET_CODE (reg) == REG)
{
regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER)
{
/* Count (weighted) references, stores, etc. This counts a
register twice if it is modified, but that is correct. */
REG_N_SETS (regno)++;
REG_N_REFS (regno) += loop_depth;
}
}
}
/* Increment REG_N_SETS for each SET or CLOBBER found in X; also increment
REG_N_REFS by the current loop depth for each SET or CLOBBER found. */
static void
count_reg_sets (x)
rtx x;
{
register RTX_CODE code = GET_CODE (x);
if (code == SET || code == CLOBBER)
count_reg_sets_1 (x);
else if (code == PARALLEL)
{
register int i;
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
{
code = GET_CODE (XVECEXP (x, 0, i));
if (code == SET || code == CLOBBER)
count_reg_sets_1 (XVECEXP (x, 0, i));
}
}
}
/* Increment REG_N_REFS by the current loop depth each register reference
found in X. */
static void
count_reg_references (x)
rtx x;
{
register RTX_CODE code;
register int regno;
int i;
retry:
code = GET_CODE (x);
switch (code)
{
case LABEL_REF:
case SYMBOL_REF:
case CONST_INT:
case CONST:
case CONST_DOUBLE:
case PC:
case ADDR_VEC:
case ADDR_DIFF_VEC:
case ASM_INPUT:
return;
#ifdef HAVE_cc0
case CC0:
return;
#endif
case CLOBBER:
/* If we are clobbering a MEM, mark any registers inside the address
as being used. */
if (GET_CODE (XEXP (x, 0)) == MEM)
count_reg_references (XEXP (XEXP (x, 0), 0));
return;
case SUBREG:
/* While we're here, optimize this case. */
x = SUBREG_REG (x);
/* In case the SUBREG is not of a register, don't optimize */
if (GET_CODE (x) != REG)
{
count_reg_references (x);
return;
}
/* ... fall through ... */
case REG:
if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
REG_N_REFS (REGNO (x)) += loop_depth;
return;
case SET:
{
register rtx testreg = SET_DEST (x);
int mark_dest = 0;
/* If storing into MEM, don't show it as being used. But do
show the address as being used. */
if (GET_CODE (testreg) == MEM)
{
count_reg_references (XEXP (testreg, 0));
count_reg_references (SET_SRC (x));
return;
}
/* Storing in STRICT_LOW_PART is like storing in a reg
in that this SET might be dead, so ignore it in TESTREG.
but in some other ways it is like using the reg.
Storing in a SUBREG or a bit field is like storing the entire
register in that if the register's value is not used
then this SET is not needed. */
while (GET_CODE (testreg) == STRICT_LOW_PART
|| GET_CODE (testreg) == ZERO_EXTRACT
|| GET_CODE (testreg) == SIGN_EXTRACT
|| GET_CODE (testreg) == SUBREG)
{
/* Modifying a single register in an alternate mode
does not use any of the old value. But these other
ways of storing in a register do use the old value. */
if (GET_CODE (testreg) == SUBREG
&& !(REG_SIZE (SUBREG_REG (testreg)) > REG_SIZE (testreg)))
;
else
mark_dest = 1;
testreg = XEXP (testreg, 0);
}
/* If this is a store into a register,
recursively scan the value being stored. */
if (GET_CODE (testreg) == REG)
{
count_reg_references (SET_SRC (x));
if (mark_dest)
count_reg_references (SET_DEST (x));
return;
}
}
break;
default:
break;
}
/* Recursively scan the operands of this expression. */
{
register char *fmt = GET_RTX_FORMAT (code);
register int i;
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
/* Tail recursive case: save a function call level. */
if (i == 0)
{
x = XEXP (x, 0);
goto retry;
}
count_reg_references (XEXP (x, i));
}
else if (fmt[i] == 'E')
{
register int j;
for (j = 0; j < XVECLEN (x, i); j++)
count_reg_references (XVECEXP (x, i, j));
}
}
}
}
/* Recompute register set/reference counts immediately prior to register
allocation.
This avoids problems with set/reference counts changing to/from values
which have special meanings to the register allocators.
Additionally, the reference counts are the primary component used by the
register allocators to prioritize pseudos for allocation to hard regs.
More accurate reference counts generally lead to better register allocation.
It might be worthwhile to update REG_LIVE_LENGTH, REG_BASIC_BLOCK and
possibly other information which is used by the register allocators. */
int
recompute_reg_usage (f)
rtx f;
{
rtx insn;
int i, max_reg;
/* Clear out the old data. */
max_reg = max_reg_num ();
for (i = FIRST_PSEUDO_REGISTER; i < max_reg; i++)
{
REG_N_SETS (i) = 0;
REG_N_REFS (i) = 0;
}
/* Scan each insn in the chain and count how many times each register is
set/used. */
loop_depth = 1;
for (insn = f; insn; insn = NEXT_INSN (insn))
{
/* Keep track of loop depth. */
if (GET_CODE (insn) == NOTE)
{
/* Look for loop boundaries. */
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
loop_depth--;
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
loop_depth++;
/* If we have LOOP_DEPTH == 0, there has been a bookkeeping error.
Abort now rather than setting register status incorrectly. */
if (loop_depth == 0)
abort ();
}
else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
{
rtx links;
/* This call will increment REG_N_SETS for each SET or CLOBBER
of a register in INSN. It will also increment REG_N_REFS
by the loop depth for each set of a register in INSN. */
count_reg_sets (PATTERN (insn));
/* count_reg_sets does not detect autoincrement address modes, so
detect them here by looking at the notes attached to INSN. */
for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
{
if (REG_NOTE_KIND (links) == REG_INC)
/* Count (weighted) references, stores, etc. This counts a
register twice if it is modified, but that is correct. */
REG_N_SETS (REGNO (XEXP (links, 0)))++;
}
/* This call will increment REG_N_REFS by the current loop depth for
each reference to a register in INSN. */
count_reg_references (PATTERN (insn));
/* count_reg_references will not include counts for arguments to
function calls, so detect them here by examining the
CALL_INSN_FUNCTION_USAGE data. */
if (GET_CODE (insn) == CALL_INSN)
{
rtx note;
for (note = CALL_INSN_FUNCTION_USAGE (insn);
note;
note = XEXP (note, 1))
if (GET_CODE (XEXP (note, 0)) == USE)
count_reg_references (SET_DEST (XEXP (note, 0)));
}
}
}
}

View File

@ -1407,6 +1407,7 @@ extern void stupid_life_analysis PROTO ((rtx, int, FILE *));
/* In flow.c */
extern void allocate_for_life_analysis PROTO ((void));
extern int recompute_reg_usage PROTO ((rtx));
#ifdef BUFSIZ
extern void dump_flow_info PROTO ((FILE *));
#endif
@ -1543,4 +1544,5 @@ extern void init_alias_analysis PROTO ((void));
extern void end_alias_analysis PROTO ((void));
extern void record_base_value PROTO ((int, rtx, int));
#endif /* _RTL_H */

View File

@ -3481,6 +3481,7 @@ rest_of_compilation (decl)
if (!obey_regdecls)
TIMEVAR (local_alloc_time,
{
recompute_reg_usage (insns);
regclass (insns, max_reg_num ());
local_alloc ();
});