regmove.c (discover_flags_reg): New function.
* regmove.c (discover_flags_reg): New function. (flags_set_1, mark_flags_life_zones): New functions. (regmove_optimize): Call them. (fixup_match_1): Use insn modes rather than sets_cc0_p. From-SVN: r25332
This commit is contained in:
parent
419ff8e1c2
commit
dc2cb19139
2 changed files with 183 additions and 7 deletions
|
@ -1,3 +1,10 @@
|
|||
Fri Feb 19 23:02:02 1999 Richard Henderson <rth@cygnus.com>
|
||||
|
||||
* regmove.c (discover_flags_reg): New function.
|
||||
(flags_set_1, mark_flags_life_zones): New functions.
|
||||
(regmove_optimize): Call them.
|
||||
(fixup_match_1): Use insn modes rather than sets_cc0_p.
|
||||
|
||||
Fri Feb 19 22:47:01 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
|
||||
|
||||
* rtlanal.c (insn_first_p): Fix return value for insn == reference.
|
||||
|
|
183
gcc/regmove.c
183
gcc/regmove.c
|
@ -53,6 +53,10 @@ struct match {
|
|||
int early_clobber[MAX_RECOG_OPERANDS];
|
||||
};
|
||||
|
||||
static rtx discover_flags_reg PROTO((void));
|
||||
static void mark_flags_life_zones PROTO((rtx));
|
||||
static void flags_set_1 PROTO((rtx, rtx));
|
||||
|
||||
static int try_auto_increment PROTO((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
|
||||
static int find_matches PROTO((rtx, struct match *));
|
||||
static int fixup_match_1 PROTO((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
|
||||
|
@ -150,7 +154,172 @@ try_auto_increment (insn, inc_insn, inc_insn_set, reg, increment, pre)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determine if the pattern generated by add_optab has a clobber,
|
||||
such as might be issued for a flags hard register. To make the
|
||||
code elsewhere simpler, we handle cc0 in this same framework.
|
||||
|
||||
Return the register if one was discovered. Return NULL_RTX if
|
||||
if no flags were found. Return pc_rtx if we got confused. */
|
||||
|
||||
static rtx
|
||||
discover_flags_reg ()
|
||||
{
|
||||
rtx tmp;
|
||||
tmp = gen_rtx_REG (SImode, 10000);
|
||||
tmp = gen_add3_insn (tmp, tmp, GEN_INT (2));
|
||||
|
||||
/* If we get something that isn't a simple set, or a
|
||||
[(set ..) (clobber ..)], this whole function will go wrong. */
|
||||
if (GET_CODE (tmp) == SET)
|
||||
return NULL_RTX;
|
||||
else if (GET_CODE (tmp) == PARALLEL)
|
||||
{
|
||||
int found;
|
||||
|
||||
if (XVECLEN (tmp, 0) != 2)
|
||||
return pc_rtx;
|
||||
tmp = XVECEXP (tmp, 0, 1);
|
||||
if (GET_CODE (tmp) != CLOBBER)
|
||||
return pc_rtx;
|
||||
tmp = XEXP (tmp, 0);
|
||||
|
||||
/* Don't do anything foolish if the md wanted to clobber a
|
||||
scratch or something. We only care about hard regs.
|
||||
Moreover we don't like the notion of subregs of hard regs. */
|
||||
if (GET_CODE (tmp) == SUBREG
|
||||
&& GET_CODE (SUBREG_REG (tmp)) == REG
|
||||
&& REGNO (SUBREG_REG (tmp)) < FIRST_PSEUDO_REGISTER)
|
||||
return pc_rtx;
|
||||
found = (GET_CODE (tmp) == REG && REGNO (tmp) < FIRST_PSEUDO_REGISTER);
|
||||
|
||||
#ifdef HAVE_cc0
|
||||
/* If we're cc0, and we found a potential flags reg, bail. */
|
||||
return (found ? pc_rtx : cc0_rtx);
|
||||
#else
|
||||
return (found ? tmp : NULL_RTX);
|
||||
#endif
|
||||
}
|
||||
|
||||
return pc_rtx;
|
||||
}
|
||||
|
||||
/* It is a tedious task identifying when the flags register is live and
|
||||
when it is safe to optimize. Since we process the instruction stream
|
||||
multiple times, locate and record these live zones by marking the
|
||||
mode of the instructions --
|
||||
|
||||
QImode is used on the instruction at which the flags becomes live.
|
||||
|
||||
HImode is used within the range (exclusive) that the flags are
|
||||
live. Thus the user of the flags is not marked.
|
||||
|
||||
All other instructions are cleared to VOIDmode. */
|
||||
|
||||
/* Used to communicate with flags_set_1. */
|
||||
static rtx flags_set_1_rtx;
|
||||
static int flags_set_1_set;
|
||||
|
||||
static void
|
||||
mark_flags_life_zones (flags)
|
||||
rtx flags;
|
||||
{
|
||||
int flags_regno;
|
||||
int flags_nregs;
|
||||
int block;
|
||||
|
||||
/* Simple cases first: if no flags, clear all modes. If confusing,
|
||||
mark the entire function as being in a flags shadow. */
|
||||
if (flags == NULL_RTX || flags == pc_rtx)
|
||||
{
|
||||
enum machine_mode mode = (flags ? HImode : VOIDmode);
|
||||
rtx insn;
|
||||
for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
|
||||
PUT_MODE (insn, mode);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_cc0
|
||||
flags_regno = -1;
|
||||
flags_nregs = 1;
|
||||
#else
|
||||
flags_regno = REGNO (flags);
|
||||
flags_nregs = HARD_REGNO_NREGS (flags_regno, GET_MODE (flags));
|
||||
#endif
|
||||
flags_set_1_rtx = flags;
|
||||
|
||||
/* Process each basic block. */
|
||||
for (block = n_basic_blocks - 1; block >= 0; block--)
|
||||
{
|
||||
rtx insn, end;
|
||||
int live;
|
||||
|
||||
insn = BLOCK_HEAD (block);
|
||||
end = BLOCK_END (block);
|
||||
|
||||
/* Look out for the (unlikely) case of flags being live across
|
||||
basic block boundaries. */
|
||||
live = 0;
|
||||
#ifndef HAVE_cc0
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < flags_nregs; ++i)
|
||||
live |= REGNO_REG_SET_P (basic_block_live_at_start[block],
|
||||
flags_regno + i);
|
||||
}
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* Process liveness in reverse order of importance --
|
||||
alive, death, birth. This lets more important info
|
||||
overwrite the mode of lesser info. */
|
||||
|
||||
if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
|
||||
{
|
||||
#ifdef HAVE_cc0
|
||||
/* In the cc0 case, death is not marked in reg notes,
|
||||
but is instead the mere use of cc0 when it is alive. */
|
||||
if (live && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
|
||||
live = 0;
|
||||
#else
|
||||
/* In the hard reg case, we watch death notes. */
|
||||
if (live && find_regno_note (insn, REG_DEAD, flags_regno))
|
||||
live = 0;
|
||||
#endif
|
||||
PUT_MODE (insn, (live ? HImode : VOIDmode));
|
||||
|
||||
/* In either case, birth is denoted simply by it's presence
|
||||
as the destination of a set. */
|
||||
flags_set_1_set = 0;
|
||||
note_stores (PATTERN (insn), flags_set_1);
|
||||
if (flags_set_1_set)
|
||||
{
|
||||
live = 1;
|
||||
PUT_MODE (insn, QImode);
|
||||
}
|
||||
}
|
||||
else
|
||||
PUT_MODE (insn, (live ? HImode : VOIDmode));
|
||||
|
||||
if (insn == end)
|
||||
break;
|
||||
insn = NEXT_INSN (insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A subroutine of mark_flags_life_zones, called through note_stores. */
|
||||
|
||||
static void
|
||||
flags_set_1 (x, pat)
|
||||
rtx x, pat;
|
||||
{
|
||||
if (GET_CODE (pat) == SET
|
||||
&& reg_overlap_mentioned_p (x, flags_set_1_rtx))
|
||||
flags_set_1_set = 1;
|
||||
}
|
||||
|
||||
static int *regno_src_regno;
|
||||
|
||||
/* Indicate how good a choice REG (which appears as a source) is to replace
|
||||
|
@ -908,6 +1077,10 @@ regmove_optimize (f, nregs, regmove_dump_file)
|
|||
int i;
|
||||
rtx copy_src, copy_dst;
|
||||
|
||||
/* Find out where a potential flags register is live, and so that we
|
||||
can supress some optimizations in those zones. */
|
||||
mark_flags_life_zones (discover_flags_reg ());
|
||||
|
||||
regno_src_regno = (int *)alloca (sizeof *regno_src_regno * nregs);
|
||||
for (i = nregs; --i >= 0; ) regno_src_regno[i] = -1;
|
||||
|
||||
|
@ -1617,13 +1790,9 @@ fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
|
|||
&& GET_CODE (SET_DEST (single_set (p))) == REG
|
||||
&& (REGNO (SET_DEST (single_set (p)))
|
||||
< FIRST_PSEUDO_REGISTER))
|
||||
#ifdef HAVE_cc0
|
||||
/* We may not emit an insn directly
|
||||
after P if the latter sets CC0. */
|
||||
&& ! sets_cc0_p (PATTERN (p))
|
||||
#endif
|
||||
)
|
||||
|
||||
/* We may only emit an insn directly after P if we
|
||||
are not in the shadow of a live flags register. */
|
||||
&& GET_MODE (p) == VOIDmode)
|
||||
{
|
||||
search_end = q;
|
||||
q = insn;
|
||||
|
|
Loading…
Add table
Reference in a new issue