Commit 3ec2b590 by J"orn Rennecke Committed by Joern Rennecke

rtl.h (insn_first_p): Declare.

	* rtl.h (insn_first_p): Declare.
	* rtlanal.c (insn_first_p): New function.
	* loop.h (varray.h): Include.
	(struct induction): Change combined_with to unsigned.
	New members derived, ix and last_use.
	(reg_iv_type, reg_iv_info): Now varray_type.  All references changed.
	(REG_IV_TYPE, REG_IV_INFO): Define.
	(first_increment_giv, last_increment_giv): Declare.
	* loop.c (loop_number_loop_cont): New static variable.
	(loop_number_cont_dominator): Likewise.
	(reg_iv_type, reg_iv_info): Now varray_type.
	(first_increment_giv, last_increment_giv): New variables.
	(compute_luids, verify_dominator, find_life_end): New functions.
	(cmp_recombine_givs_stats, recombine_givs): Likewise.
	(loop_optimize): Allocate loop_number_loop_cont and
	loop_number_cont_dominator.  Use compute_luids.
	(find_and_verify_loops): Initialize loop_number_loop_cont and
	loop_number_cont_dominator.
	(strength_reduce): Try to find bivs that can be expressed as givs
	of another biv, and to convert biv increments into givs.
	Call recombine_givs.  Handle derived givs.
	(record_biv): New argument location.  All callers changed.
	(record_giv): Initialize derived and last_use fields.
	(basic_induction_var): New argument location.  All callers changed.
	(combine_givs): Don't combine a DEST_REG giv with a DEST_ADDR giv.
	Increment combined_with instead of setting to 1.
	* unroll.c (derived_regs): New static variable.
	(unroll_loop): Initialize it.
	Allocate local_regno according to max_reg_num.
	(copy_loop_body): Cope with derived givs.
	(find_splittable_givs): Check for Givs made from biv increments.
	Set derived_regs for givs.
	* Makefile.in (stmt.o, loop.o, unroll.o): Depend on loop.h .

From-SVN: r24889
parent a9c70c22
Wed Jan 27 23:39:53 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* rtl.h (insn_first_p): Declare.
* rtlanal.c (insn_first_p): New function.
* loop.h (varray.h): Include.
(struct induction): Change combined_with to unsigned.
New members derived, ix and last_use.
(reg_iv_type, reg_iv_info): Now varray_type. All references changed.
(REG_IV_TYPE, REG_IV_INFO): Define.
(first_increment_giv, last_increment_giv): Declare.
* loop.c (loop_number_loop_cont): New static variable.
(loop_number_cont_dominator): Likewise.
(reg_iv_type, reg_iv_info): Now varray_type.
(first_increment_giv, last_increment_giv): New variables.
(compute_luids, verify_dominator, find_life_end): New functions.
(cmp_recombine_givs_stats, recombine_givs): Likewise.
(loop_optimize): Allocate loop_number_loop_cont and
loop_number_cont_dominator. Use compute_luids.
(find_and_verify_loops): Initialize loop_number_loop_cont and
loop_number_cont_dominator.
(strength_reduce): Try to find bivs that can be expressed as givs
of another biv, and to convert biv increments into givs.
Call recombine_givs. Handle derived givs.
(record_biv): New argument location. All callers changed.
(record_giv): Initialize derived and last_use fields.
(basic_induction_var): New argument location. All callers changed.
(combine_givs): Don't combine a DEST_REG giv with a DEST_ADDR giv.
Increment combined_with instead of setting to 1.
* unroll.c (derived_regs): New static variable.
(unroll_loop): Initialize it.
Allocate local_regno according to max_reg_num.
(copy_loop_body): Cope with derived givs.
(find_splittable_givs): Check for Givs made from biv increments.
Set derived_regs for givs.
* Makefile.in (stmt.o, loop.o, unroll.o): Depend on loop.h .
Wed Jan 27 19:31:36 1999 J"orn Rennecke <amylaar@cygnus.co.uk>
* function.c (purge_addressof_1): Handle case when a register
......
......@@ -1474,7 +1474,7 @@ function.o : function.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
insn-config.h $(RECOG_H) output.h toplev.h except.h
stmt.o : stmt.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h function.h \
insn-flags.h insn-config.h insn-codes.h hard-reg-set.h $(EXPR_H) except.h \
loop.h $(RECOG_H) toplev.h output.h
loop.h $(RECOG_H) toplev.h output.h varray.h
except.o : except.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \
function.h insn-flags.h $(EXPR_H) $(REGS_H) hard-reg-set.h \
insn-config.h $(RECOG_H) output.h except.h toplev.h
......@@ -1527,9 +1527,9 @@ profile.o : profile.c $(CONFIG_H) system.h $(RTL_H) flags.h insn-flags.h \
gcov-io.h $(TREE_H) output.h $(REGS_H) toplev.h insn-config.h
loop.o : loop.c $(CONFIG_H) system.h $(RTL_H) flags.h loop.h insn-config.h \
insn-flags.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) real.h \
toplev.h
toplev.h varray.h
unroll.o : unroll.c $(CONFIG_H) system.h $(RTL_H) insn-config.h \
integrate.h $(REGS_H) $(RECOG_H) flags.h $(EXPR_H) loop.h toplev.h
integrate.h $(REGS_H) $(RECOG_H) flags.h $(EXPR_H) loop.h toplev.h varray.h
flow.o : flow.c $(CONFIG_H) system.h $(RTL_H) flags.h insn-config.h \
$(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h recog.h
combine.o : combine.c $(CONFIG_H) system.h $(RTL_H) flags.h \
......
......@@ -78,6 +78,16 @@ static int max_loop_num;
static rtx *loop_number_loop_starts, *loop_number_loop_ends;
/* Likewise for the continue insn */
static rtx *loop_number_loop_cont;
/* The first code_label that is reached in every loop iteration.
0 when not computed yet, initially const0_rtx if a jump couldn't be
followed.
Also set to 0 when there is no such label before the NOTE_INSN_LOOP_CONT
of this loop, or in verify_dominator, if a jump couldn't be followed. */
static rtx *loop_number_cont_dominator;
/* For each loop, gives the containing loop number, -1 if none. */
int *loop_outer_loop;
......@@ -267,6 +277,7 @@ FILE *loop_dump_stream;
/* Forward declarations. */
static void verify_dominator PROTO((int));
static void find_and_verify_loops PROTO((rtx));
static void mark_loop_jump PROTO((rtx, int));
static void prescan_loop PROTO((rtx, rtx));
......@@ -298,12 +309,12 @@ static void strength_reduce PROTO((rtx, rtx, rtx, int, rtx, rtx, int, int));
static void find_single_use_in_loop PROTO((rtx, rtx, varray_type));
static int valid_initial_value_p PROTO((rtx, rtx, int, rtx));
static void find_mem_givs PROTO((rtx, rtx, int, rtx, rtx));
static void record_biv PROTO((struct induction *, rtx, rtx, rtx, rtx, int, int));
static void record_biv PROTO((struct induction *, rtx, rtx, rtx, rtx, rtx *, int, int));
static void check_final_value PROTO((struct induction *, rtx, rtx,
unsigned HOST_WIDE_INT));
static void record_giv PROTO((struct induction *, rtx, rtx, rtx, rtx, rtx, int, enum g_types, int, rtx *, rtx, rtx));
static void update_giv_derive PROTO((rtx));
static int basic_induction_var PROTO((rtx, enum machine_mode, rtx, rtx, rtx *, rtx *));
static int basic_induction_var PROTO((rtx, enum machine_mode, rtx, rtx, rtx *, rtx *, rtx **));
static rtx simplify_giv_expr PROTO((rtx, int *));
static int general_induction_var PROTO((rtx, rtx *, rtx *, rtx *, int, int *));
static int consec_sets_giv PROTO((int, rtx, rtx, rtx, rtx *, rtx *, rtx *));
......@@ -312,6 +323,9 @@ static rtx express_from_1 PROTO((rtx, rtx, rtx));
static rtx express_from PROTO((struct induction *, struct induction *));
static rtx combine_givs_p PROTO((struct induction *, struct induction *));
static void combine_givs PROTO((struct iv_class *));
struct recombine_givs_stats;
static int find_life_end (rtx, struct recombine_givs_stats *, rtx, rtx);
static void recombine_givs PROTO((struct iv_class *, rtx, rtx));
static int product_cheap_p PROTO((rtx, rtx));
static int maybe_eliminate_biv PROTO((struct iv_class *, rtx, rtx, int, int, int));
static int maybe_eliminate_biv_1 PROTO((rtx, rtx, struct iv_class *, int, rtx));
......@@ -355,6 +369,7 @@ static void instrument_loop_bct PROTO((rtx, rtx, rtx));
int indirect_jump_in_function = 0;
static int indirect_jump_in_function_p PROTO((rtx));
static int compute_luids PROTO ((rtx, rtx, int));
/* Relative gain of eliminating various kinds of operations. */
static int add_cost;
......@@ -398,6 +413,35 @@ init_loop ()
gcc_obstack_init (&temp_obstack);
}
/* Compute the mapping from uids to luids.
LUIDs are numbers assigned to insns, like uids,
except that luids increase monotonically through the code.
Start at insn START and stop just before END. Assign LUIDs
starting with PREV_LUID + 1. Return the last assigned LUID + 1. */
static int
compute_luids (start, end, prev_luid)
rtx start, end;
int prev_luid;
{
int i;
rtx insn;
for (insn = start, i = prev_luid; insn != end; insn = NEXT_INSN (insn))
{
if (INSN_UID (insn) >= max_uid_for_loop)
continue;
/* Don't assign luids to line-number NOTEs, so that the distance in
luids between two insns is not affected by -g. */
if (GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) <= 0)
uid_luid[INSN_UID (insn)] = ++i;
else
/* Give a line number note the same luid as preceding insn. */
uid_luid[INSN_UID (insn)] = i;
}
return i + 1;
}
/* Entry point of this file. Perform loop optimization
on the current function. F is the first insn of the function
and DUMPFILE is a stream for output of a trace of actions taken
......@@ -412,7 +456,6 @@ loop_optimize (f, dumpfile, unroll_p, bct_p)
{
register rtx insn;
register int i;
rtx last_insn;
loop_dump_stream = dumpfile;
......@@ -453,6 +496,8 @@ loop_optimize (f, dumpfile, unroll_p, bct_p)
not be zeroed. */
loop_number_loop_starts = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_number_loop_ends = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_number_loop_cont = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_number_cont_dominator = (rtx *) alloca (max_loop_num * sizeof (rtx));
loop_outer_loop = (int *) alloca (max_loop_num * sizeof (int));
loop_invalid = (char *) alloca (max_loop_num * sizeof (char));
loop_number_exit_labels = (rtx *) alloca (max_loop_num * sizeof (rtx));
......@@ -486,24 +531,9 @@ loop_optimize (f, dumpfile, unroll_p, bct_p)
/* Now reset it to the actual size we need. See above. */
max_uid_for_loop = get_max_uid () + 1;
/* Compute the mapping from uids to luids.
LUIDs are numbers assigned to insns, like uids,
except that luids increase monotonically through the code.
Don't assign luids to line-number NOTEs, so that the distance in luids
between two insns is not affected by -g. */
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
{
last_insn = insn;
if (GET_CODE (insn) != NOTE
|| NOTE_LINE_NUMBER (insn) <= 0)
uid_luid[INSN_UID (insn)] = ++i;
else
/* Give a line number note the same luid as preceding insn. */
uid_luid[INSN_UID (insn)] = i;
}
max_luid = i + 1;
/* find_and_verify_loops has already called compute_luids, but it might
have rearranged code afterwards, so we need to recompute the luids now. */
max_luid = compute_luids (f, NULL_RTX, 0);
/* Don't leave gaps in uid_luid for insns that have been
deleted. It is possible that the first or last insn
......@@ -1121,7 +1151,7 @@ scan_loop (loop_start, end, unroll_p, bct_p)
if (VARRAY_INT (set_in_loop, i) < 0)
VARRAY_INT (set_in_loop, i) = VARRAY_INT (n_times_set, i);
/* Now that we've moved some things out of the loop, we able to
/* Now that we've moved some things out of the loop, we might be able to
hoist even more memory references. There's no need to pass
reg_single_usage this time, since we're done with it. */
load_mems_and_recount_loop_regs_set (scan_start, end, loop_top,
......@@ -2487,6 +2517,53 @@ prescan_loop (start, end)
for_each_rtx (&insn, insert_loop_mem, 0);
}
/* LOOP_NUMBER_CONT_DOMINATOR is now the last label between the loop start
and the continue note that is a the destination of a (cond)jump after
the continue note. If there is any (cond)jump between the loop start
and what we have so far as LOOP_NUMBER_CONT_DOMINATOR that has a
target between LOOP_DOMINATOR and the continue note, move
LOOP_NUMBER_CONT_DOMINATOR forward to that label; if a jump's
destination cannot be determined, clear LOOP_NUMBER_CONT_DOMINATOR. */
static void
verify_dominator (loop_number)
int loop_number;
{
rtx insn;
if (! loop_number_cont_dominator[loop_number])
/* This can happen for an empty loop, e.g. in
gcc.c-torture/compile/920410-2.c */
return;
if (loop_number_cont_dominator[loop_number] == const0_rtx)
{
loop_number_cont_dominator[loop_number] = 0;
return;
}
for (insn = loop_number_loop_starts[loop_number];
insn != loop_number_cont_dominator[loop_number];
insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) != RETURN)
{
rtx label = JUMP_LABEL (insn);
int label_luid = INSN_LUID (label);
if (! condjump_p (insn)
&& ! condjump_in_parallel_p (insn))
{
loop_number_cont_dominator[loop_number] = NULL_RTX;
return;
}
if (label_luid < INSN_LUID (loop_number_loop_cont[loop_number])
&& (label_luid
> INSN_LUID (loop_number_cont_dominator[loop_number])))
loop_number_cont_dominator[loop_number] = label;
}
}
}
/* Scan the function looking for loops. Record the start and end of each loop.
Also mark as invalid loops any loops that contain a setjmp or are branched
to from outside the loop. */
......@@ -2500,6 +2577,8 @@ find_and_verify_loops (f)
int next_loop = -1;
int loop;
compute_luids (f, NULL_RTX, 0);
/* If there are jumps to undefined labels,
treat them as jumps out of any/all loops.
This also avoids writing past end of tables when there are no loops. */
......@@ -2516,6 +2595,8 @@ find_and_verify_loops (f)
case NOTE_INSN_LOOP_BEG:
loop_number_loop_starts[++next_loop] = insn;
loop_number_loop_ends[next_loop] = 0;
loop_number_loop_cont[next_loop] = 0;
loop_number_cont_dominator[next_loop] = 0;
loop_outer_loop[next_loop] = current_loop;
loop_invalid[next_loop] = 0;
loop_number_exit_labels[next_loop] = 0;
......@@ -2536,17 +2617,63 @@ find_and_verify_loops (f)
}
break;
case NOTE_INSN_LOOP_CONT:
loop_number_loop_cont[current_loop] = insn;
break;
case NOTE_INSN_LOOP_END:
if (current_loop == -1)
abort ();
loop_number_loop_ends[current_loop] = insn;
verify_dominator (current_loop);
current_loop = loop_outer_loop[current_loop];
break;
default:
break;
}
/* If for any loop, this is a jump insn between the NOTE_INSN_LOOP_CONT
and NOTE_INSN_LOOP_END notes, update loop_number_loop_dominator. */
else if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) != RETURN
&& current_loop >= 0)
{
int this_loop;
rtx label = JUMP_LABEL (insn);
if (! condjump_p (insn) && ! condjump_in_parallel_p (insn))
label = NULL_RTX;
this_loop = current_loop;
do
{
/* First see if we care about this loop. */
if (loop_number_loop_cont[this_loop]
&& loop_number_cont_dominator[this_loop] != const0_rtx)
{
/* If the jump destination is not known, invalidate
loop_number_const_dominator. */
if (! label)
loop_number_cont_dominator[this_loop] = const0_rtx;
else
/* Check if the destination is between loop start and
cont. */
if ((INSN_LUID (label)
< INSN_LUID (loop_number_loop_cont[this_loop]))
&& (INSN_LUID (label)
> INSN_LUID (loop_number_loop_starts[this_loop]))
/* And if there is no later destination already
recorded. */
&& (! loop_number_cont_dominator[this_loop]
|| (INSN_LUID (label)
> INSN_LUID (loop_number_cont_dominator
[this_loop]))))
loop_number_cont_dominator[this_loop] = label;
}
this_loop = loop_outer_loop[this_loop];
}
while (this_loop >= 0);
}
/* Note that this will mark the NOTE_INSN_LOOP_END note as being in the
enclosing loop, but this doesn't matter. */
......@@ -3433,13 +3560,13 @@ loop_reg_used_before_p (set, insn, loop_start, scan_start, loop_end)
/* Indexed by register number, indicates whether or not register is an
induction variable, and if so what type. */
enum iv_mode *reg_iv_type;
varray_type reg_iv_type;
/* Indexed by register number, contains pointer to `struct induction'
if register is an induction variable. This holds general info for
all induction variables. */
struct induction **reg_iv_info;
varray_type reg_iv_info;
/* Indexed by register number, contains pointer to `struct iv_class'
if register is a basic induction variable. This holds info describing
......@@ -3453,6 +3580,11 @@ struct iv_class **reg_biv_class;
struct iv_class *loop_iv_list;
/* Givs made from biv increments are always splittable for loop unrolling.
Since there is no regscan info for them, we have to keep track of them
separately. */
int first_increment_giv, last_increment_giv;
/* Communication with routines called via `note_stores'. */
static rtx note_insn;
......@@ -3517,6 +3649,7 @@ strength_reduce (scan_start, end, loop_top, insn_count,
rtx inc_val;
rtx mult_val;
rtx dest_reg;
rtx *location;
/* This is 1 if current insn is not executed at least once for every loop
iteration. */
int not_every_iteration = 0;
......@@ -3537,6 +3670,7 @@ strength_reduce (scan_start, end, loop_top, insn_count,
rtx test;
rtx end_insert_before;
int loop_depth = 0;
int n_extra_increment;
struct loop_info loop_iteration_info;
struct loop_info *loop_info = &loop_iteration_info;
......@@ -3545,13 +3679,8 @@ strength_reduce (scan_start, end, loop_top, insn_count,
if (prev_nonnote_insn (scan_start) != prev_nonnote_insn (loop_start))
maybe_multiple = back_branch_in_range_p (scan_start, loop_start, loop_end);
reg_iv_type = (enum iv_mode *) alloca (max_reg_before_loop
* sizeof (enum iv_mode));
bzero ((char *) reg_iv_type, max_reg_before_loop * sizeof (enum iv_mode));
reg_iv_info = (struct induction **)
alloca (max_reg_before_loop * sizeof (struct induction *));
bzero ((char *) reg_iv_info, (max_reg_before_loop
* sizeof (struct induction *)));
VARRAY_INT_INIT (reg_iv_type, max_reg_before_loop, "reg_iv_type");
VARRAY_GENERIC_PTR_INIT (reg_iv_info, max_reg_before_loop, "reg_iv_info");
reg_biv_class = (struct iv_class **)
alloca (max_reg_before_loop * sizeof (struct iv_class *));
bzero ((char *) reg_biv_class, (max_reg_before_loop
......@@ -3585,10 +3714,11 @@ strength_reduce (scan_start, end, loop_top, insn_count,
dest_reg = SET_DEST (set);
if (REGNO (dest_reg) < max_reg_before_loop
&& REGNO (dest_reg) >= FIRST_PSEUDO_REGISTER
&& reg_iv_type[REGNO (dest_reg)] != NOT_BASIC_INDUCT)
&& REG_IV_TYPE (REGNO (dest_reg)) != NOT_BASIC_INDUCT)
{
if (basic_induction_var (SET_SRC (set), GET_MODE (SET_SRC (set)),
dest_reg, p, &inc_val, &mult_val))
dest_reg, p, &inc_val, &mult_val,
&location))
{
/* It is a possible basic induction variable.
Create and initialize an induction structure for it. */
......@@ -3596,12 +3726,12 @@ strength_reduce (scan_start, end, loop_top, insn_count,
struct induction *v
= (struct induction *) alloca (sizeof (struct induction));
record_biv (v, p, dest_reg, inc_val, mult_val,
record_biv (v, p, dest_reg, inc_val, mult_val, location,
not_every_iteration, maybe_multiple);
reg_iv_type[REGNO (dest_reg)] = BASIC_INDUCT;
REG_IV_TYPE (REGNO (dest_reg)) = BASIC_INDUCT;
}
else if (REGNO (dest_reg) < max_reg_before_loop)
reg_iv_type[REGNO (dest_reg)] = NOT_BASIC_INDUCT;
REG_IV_TYPE (REGNO (dest_reg)) = NOT_BASIC_INDUCT;
}
}
......@@ -3711,7 +3841,7 @@ strength_reduce (scan_start, end, loop_top, insn_count,
Make a sanity check against n_times_set. */
for (backbl = &loop_iv_list, bl = *backbl; bl; bl = bl->next)
{
if (reg_iv_type[bl->regno] != BASIC_INDUCT
if (REG_IV_TYPE (bl->regno) != BASIC_INDUCT
/* Above happens if register modified by subreg, etc. */
/* Make sure it is not recognized as a basic induction var: */
|| VARRAY_INT (n_times_set, bl->regno) != bl->biv_count
......@@ -3722,12 +3852,12 @@ strength_reduce (scan_start, end, loop_top, insn_count,
if (loop_dump_stream)
fprintf (loop_dump_stream, "Reg %d: biv discarded, %s\n",
bl->regno,
(reg_iv_type[bl->regno] != BASIC_INDUCT
(REG_IV_TYPE (bl->regno) != BASIC_INDUCT
? "not induction variable"
: (! bl->incremented ? "never incremented"
: "count error")));
reg_iv_type[bl->regno] = NOT_BASIC_INDUCT;
REG_IV_TYPE (bl->regno) = NOT_BASIC_INDUCT;
*backbl = bl->next;
}
else
......@@ -3794,7 +3924,7 @@ strength_reduce (scan_start, end, loop_top, insn_count,
/* Look at the each biv and see if we can say anything better about its
initial value from any initializing insns set up above. (This is done
in two passes to avoid missing SETs in a PARALLEL.) */
for (bl = loop_iv_list; bl; bl = bl->next)
for (backbl = &loop_iv_list; bl = *backbl; backbl = &bl->next)
{
rtx src;
rtx note;
......@@ -3839,14 +3969,278 @@ strength_reduce (scan_start, end, loop_top, insn_count,
}
else
{
/* Biv initial value is not simple move,
so let it keep initial value of "itself". */
struct iv_class *bl2 = 0;
rtx increment;
/* Biv initial value is not a simple move. If it is the sum of
another biv and a constant, check if both bivs are incremented
in lockstep. Then we are actually looking at a giv.
For simplicity, we only handle the case where there is but a
single increment, and the register is not used elsewhere. */
if (bl->biv_count == 1
&& bl->regno < max_reg_before_loop
&& uid_luid[REGNO_LAST_UID (bl->regno)] < INSN_LUID (loop_end)
&& GET_CODE (src) == PLUS
&& GET_CODE (XEXP (src, 0)) == REG
&& CONSTANT_P (XEXP (src, 1))
&& ((increment = biv_total_increment (bl, loop_start, loop_end))
!= NULL_RTX))
{
int regno = REGNO (XEXP (src, 0));
if (loop_dump_stream)
for (bl2 = loop_iv_list; bl2; bl2 = bl2->next)
if (bl2->regno == regno)
break;
}
/* Now, can we transform this biv into a giv? */
if (bl2
&& bl2->biv_count == 1
&& rtx_equal_p (increment,
biv_total_increment (bl2, loop_start, loop_end))
/* init_insn is only set to insns that are before loop_start
without any intervening labels. */
&& ! reg_set_between_p (bl2->biv->src_reg,
PREV_INSN (bl->init_insn), loop_start)
/* The register from BL2 must be set before the register from
BL is set, or we must be able to move the latter set after
the former set. Currently there can't be any labels
in-between when biv_toal_increment returns nonzero both times
but we test it here in case some day some real cfg analysis
gets used to set always_computable. */
&& ((insn_first_p (bl2->biv->insn, bl->biv->insn)
&& no_labels_between_p (bl2->biv->insn, bl->biv->insn))
|| (! reg_used_between_p (bl->biv->src_reg, bl->biv->insn,
bl2->biv->insn)
&& no_jumps_between_p (bl->biv->insn, bl2->biv->insn)))
&& validate_change (bl->biv->insn,
&SET_SRC (single_set (bl->biv->insn)),
copy_rtx (src), 0))
{
int loop_num = uid_loop_num[INSN_UID (loop_start)];
rtx dominator = loop_number_cont_dominator[loop_num];
rtx cont = loop_number_loop_cont[loop_num];
rtx giv = bl->biv->src_reg;
rtx giv_insn = bl->biv->insn;
rtx after_giv = NEXT_INSN (giv_insn);
if (loop_dump_stream)
fprintf (loop_dump_stream, "is giv of biv %d\n", bl2->regno);
/* Let this giv be discovered by the generic code. */
REG_IV_TYPE (bl->regno) = UNKNOWN_INDUCT;
/* We can get better optimization if we can move the giv setting
before the first giv use. */
if (dominator
&& ! reg_set_between_p (bl2->biv->src_reg, loop_start,
dominator)
&& ! reg_used_between_p (giv, loop_start, dominator)
&& ! reg_used_between_p (giv, giv_insn, loop_end))
{
rtx p;
for (;;)
{
rtx next = NEXT_INSN (dominator);
if ((GET_RTX_CLASS (GET_CODE (next)) == 'i'
&& (reg_mentioned_p (giv, PATTERN (next))
|| reg_set_p (bl2->biv->src_reg, next)))
|| GET_CODE (next) == JUMP_INSN)
break;
#ifdef HAVE_cc0
if (GET_RTX_CLASS (GET_CODE (next)) != 'i'
|| ! sets_cc0_p (PATTERN (next)))
#endif
dominator = next;
}
if (loop_dump_stream)
fprintf (loop_dump_stream, "move after insn %d\n",
INSN_UID (dominator));
/* Avoid problems with luids by actually moving the insn
and adjusting all luids in the range. */
reorder_insns (giv_insn, giv_insn, dominator);
for (p = dominator; INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
compute_luids (giv_insn, after_giv, INSN_LUID (p));
/* If the only purpose of the init insn is to initialize
this giv, delete it. */
if (single_set (bl->init_insn)
&& ! reg_used_between_p (giv, bl->init_insn, loop_start))
delete_insn (bl->init_insn);
}
else if (! insn_first_p (bl2->biv->insn, bl->biv->insn))
{
rtx p = PREV_INSN (giv_insn);
while (INSN_UID (p) >= max_uid_for_loop)
p = PREV_INSN (p);
reorder_insns (giv_insn, giv_insn, bl2->biv->insn);
compute_luids (after_giv, NEXT_INSN (giv_insn),
INSN_LUID (p));
}
/* Remove this biv from the chain. */
if (bl->next)
*bl = *bl->next;
else
{
*backbl = 0;
break;
}
}
/* If we can't make it a giv,
let biv keep initial value of "itself". */
else if (loop_dump_stream)
fprintf (loop_dump_stream, "is complex\n");
}
}
/* If a biv is unconditionally incremented several times in a row, convert
all but the last increment into a giv. */
/* Get an upper bound for the number of registers
we might have after all bivs have been processed. */
first_increment_giv = max_reg_num ();
for (n_extra_increment = 0, bl = loop_iv_list; bl; bl = bl->next)
n_extra_increment += bl->biv_count - 1;
if (n_extra_increment)
{
int nregs = first_increment_giv + n_extra_increment;
/* Reallocate reg_iv_type and reg_iv_info. */
VARRAY_GROW (reg_iv_type, nregs);
VARRAY_GROW (reg_iv_info, nregs);
for (bl = loop_iv_list; bl; bl = bl->next)
{
struct induction **vp, *v, *next;
/* The biv increments lists are in reverse order. Fix this first. */
for (v = bl->biv, bl->biv = 0; v; v = next)
{
next = v->next_iv;
v->next_iv = bl->biv;
bl->biv = v;
}
for (vp = &bl->biv, next = *vp; v = next, next = v->next_iv;)
{
HOST_WIDE_INT offset;
rtx set, add_val, old_reg, dest_reg, last_use_insn;
int old_regno, new_regno;
if (! v->always_executed
|| v->maybe_multiple
|| GET_CODE (v->add_val) != CONST_INT
|| ! next->always_executed
|| next->maybe_multiple
|| ! CONSTANT_P (next->add_val))
{
vp = &v->next_iv;
continue;
}
offset = INTVAL (v->add_val);
set = single_set (v->insn);
add_val = plus_constant (next->add_val, offset);
old_reg = v->dest_reg;
dest_reg = gen_reg_rtx (v->mode);
if ((unsigned) max_reg_num () > n_times_set->num_elements)
{
int nregs = max_reg_before_loop + n_extra_increment;
/* Grow all the arrays. */
VARRAY_GROW (set_in_loop, nregs);
VARRAY_GROW (n_times_set, nregs);
VARRAY_GROW (may_not_optimize, nregs);
}
validate_change (v->insn, &SET_DEST (set), dest_reg, 1);
validate_change (next->insn, next->location, add_val, 1);
if (! apply_change_group ())
{
vp = &v->next_iv;
continue;
}
next->add_val = add_val;
v->dest_reg = dest_reg;
v->giv_type = DEST_REG;
v->location = &SET_SRC (set);
v->cant_derive = 0;
v->combined_with = 0;
v->maybe_dead = 0;
v->derive_adjustment = 0;
v->same = 0;
v->ignore = 0;
v->new_reg = 0;
v->final_value = 0;
v->same_insn = 0;
v->auto_inc_opt = 0;
v->unrolled = 0;
v->shared = 0;
v->derived = 0;
v->always_computable = 1;
v->always_executed = 1;
v->replaceable = 1;
v->no_const_addval = 0;
old_regno = REGNO (old_reg);
new_regno = REGNO (dest_reg);
VARRAY_INT (set_in_loop, old_regno)--;
VARRAY_INT (set_in_loop, new_regno) = 1;
VARRAY_INT (n_times_set, old_regno)--;
VARRAY_INT (n_times_set, new_regno) = 1;
VARRAY_CHAR (may_not_optimize, new_regno) = 0;
REG_IV_TYPE (new_regno) = GENERAL_INDUCT;
REG_IV_INFO (new_regno) = v;
/* Remove the increment from the list of biv increments,
and record it as a giv. */
*vp = next;
bl->biv_count--;
v->next_iv = bl->giv;
bl->giv = v;
bl->giv_count++;
v->benefit = rtx_cost (SET_SRC (set), SET);
bl->total_benefit += v->benefit;
/* Now replace the biv with DEST_REG in all insns between
the replaced increment and the next increment, and
remember the last insn that needed a replacement. */
for (last_use_insn = v->insn, p = NEXT_INSN (v->insn);
p != next->insn;
p = next_insn_in_loop (p, scan_start, end, loop_top))
{
rtx note;
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
if (reg_mentioned_p (old_reg, PATTERN (p)))
{
last_use_insn = p;
if (! validate_replace_rtx (old_reg, dest_reg, p))
abort ();
}
for (note = REG_NOTES (p); note; note = XEXP (note, 1))
{
if (GET_CODE (note) == EXPR_LIST)
XEXP (note, 0)
= replace_rtx (XEXP (note, 0), old_reg, dest_reg);
}
}
v->last_use = last_use_insn;
v->lifetime = INSN_LUID (v->insn) - INSN_LUID (last_use_insn);
/* If the lifetime is zero, it means that this register is really
a dead store. So mark this as a giv that can be ignored.
This will not prevent the biv from being eliminated. */
if (v->lifetime == 0)
v->ignore = 1;
}
}
}
last_increment_giv = max_reg_num () - 1;
/* Search the loop for general induction variables. */
/* A register is a giv if: it is only set once, it is a function of a
......@@ -4188,6 +4582,10 @@ strength_reduce (scan_start, end, loop_top, insn_count,
}
}
/* Now that we know which givs will be reduced, try to rearrange the
combinations to reduce register pressure. */
recombine_givs (bl, loop_start, loop_end);
/* Reduce each giv that we decided to reduce. */
for (v = bl->giv; v; v = v->next_iv)
......@@ -4199,6 +4597,29 @@ strength_reduce (scan_start, end, loop_top, insn_count,
v->new_reg = gen_reg_rtx (v->mode);
if (v->derived)
{
PATTERN (v->insn)
= replace_rtx (PATTERN (v->insn), v->dest_reg, v->new_reg);
if (bl->biv_count != 1)
{
/* For each place where the biv is incremented, add an
insn to set the new, reduced reg for the giv. */
for (tv = bl->biv; tv; tv = tv->next_iv)
{
/* We always emit reduced giv increments before the
biv increment when bl->biv_count != 1. So by
emitting the add insns for derived givs after the
biv increment, they pick up the updated value of
the reduced giv. */
emit_insn_after (copy_rtx (PATTERN (v->insn)),
tv->insn);
}
}
continue;
}
#ifdef AUTO_INC_DEC
/* If the target has auto-increment addressing modes, and
this is an address giv, then try to put the increment
......@@ -4324,7 +4745,15 @@ strength_reduce (scan_start, end, loop_top, insn_count,
if (v->ignore)
continue;
if (v->giv_type == DEST_REG
if (v->last_use)
{
struct induction *v1;
for (v1 = bl->giv; v1; v1 = v1->next_iv)
if (v->last_use == v1->insn)
v->maybe_dead = 1;
}
else if (v->giv_type == DEST_REG
&& REGNO_FIRST_UID (REGNO (v->dest_reg)) == INSN_UID (v->insn))
{
struct induction *v1;
......@@ -4539,6 +4968,8 @@ strength_reduce (scan_start, end, loop_top, insn_count,
if (loop_dump_stream)
fprintf (loop_dump_stream, "\n");
VARRAY_FREE (reg_iv_type);
VARRAY_FREE (reg_iv_info);
}
/* Return 1 if X is a valid source for an initial value (or as value being
......@@ -4678,13 +5109,14 @@ find_mem_givs (x, insn, not_every_iteration, loop_start, loop_end)
executed exactly once per iteration. */
static void
record_biv (v, insn, dest_reg, inc_val, mult_val,
record_biv (v, insn, dest_reg, inc_val, mult_val, location,
not_every_iteration, maybe_multiple)
struct induction *v;
rtx insn;
rtx dest_reg;
rtx inc_val;
rtx mult_val;
rtx *location;
int not_every_iteration;
int maybe_multiple;
{
......@@ -4695,6 +5127,7 @@ record_biv (v, insn, dest_reg, inc_val, mult_val,
v->dest_reg = dest_reg;
v->mult_val = mult_val;
v->add_val = inc_val;
v->location = location;
v->mode = GET_MODE (dest_reg);
v->always_computable = ! not_every_iteration;
v->always_executed = ! not_every_iteration;
......@@ -4815,6 +5248,8 @@ record_giv (v, insn, src_reg, dest_reg, mult_val, add_val, benefit,
v->auto_inc_opt = 0;
v->unrolled = 0;
v->shared = 0;
v->derived = 0;
v->last_use = 0;
/* The v->always_computable field is used in update_giv_derive, to
determine whether a giv can be used to derive another giv. For a
......@@ -4849,8 +5284,8 @@ record_giv (v, insn, src_reg, dest_reg, mult_val, add_val, benefit,
if (v->lifetime == 0)
v->ignore = 1;
reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT;
reg_iv_info[REGNO (dest_reg)] = v;
REG_IV_TYPE (REGNO (dest_reg)) = GENERAL_INDUCT;
REG_IV_INFO (REGNO (dest_reg)) = v;
}
/* Add the giv to the class of givs computed from one biv. */
......@@ -5265,7 +5700,8 @@ update_giv_derive (p)
REG = INVARIANT + REG
If X is suitable, we return 1, set *MULT_VAL to CONST1_RTX,
and store the additive term into *INC_VAL.
store the additive term into *INC_VAL, and store the place where
we found the additive term into *LOCATION.
If X is an assignment of an invariant into DEST_REG, we set
*MULT_VAL to CONST0_RTX, and store the invariant into *INC_VAL.
......@@ -5292,16 +5728,17 @@ update_giv_derive (p)
If we cannot find a biv, we return 0. */
static int
basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val)
basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val, location)
register rtx x;
enum machine_mode mode;
rtx p;
rtx dest_reg;
rtx *inc_val;
rtx *mult_val;
rtx **location;
{
register enum rtx_code code;
rtx arg;
rtx *argp, arg;
rtx insn, set = 0;
code = GET_CODE (x);
......@@ -5312,20 +5749,26 @@ basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val)
|| (GET_CODE (XEXP (x, 0)) == SUBREG
&& SUBREG_PROMOTED_VAR_P (XEXP (x, 0))
&& SUBREG_REG (XEXP (x, 0)) == dest_reg))
arg = XEXP (x, 1);
{
argp = &XEXP (x, 1);
}
else if (rtx_equal_p (XEXP (x, 1), dest_reg)
|| (GET_CODE (XEXP (x, 1)) == SUBREG
&& SUBREG_PROMOTED_VAR_P (XEXP (x, 1))
&& SUBREG_REG (XEXP (x, 1)) == dest_reg))
arg = XEXP (x, 0);
{
argp = &XEXP (x, 0);
}
else
return 0;
arg = *argp;
if (invariant_p (arg) != 1)
return 0;
*inc_val = convert_modes (GET_MODE (dest_reg), GET_MODE (x), arg, 0);
*mult_val = const1_rtx;
*location = argp;
return 1;
case SUBREG:
......@@ -5333,7 +5776,7 @@ basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val)
value. */
if (SUBREG_PROMOTED_VAR_P (x))
return basic_induction_var (SUBREG_REG (x), GET_MODE (SUBREG_REG (x)),
dest_reg, p, inc_val, mult_val);
dest_reg, p, inc_val, mult_val, location);
return 0;
case REG:
......@@ -5364,7 +5807,7 @@ basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val)
? GET_MODE (x)
: GET_MODE (SET_SRC (set))),
dest_reg, insn,
inc_val, mult_val))
inc_val, mult_val, location))
return 1;
}
/* ... fall through ... */
......@@ -5396,7 +5839,7 @@ basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val)
case SIGN_EXTEND:
return basic_induction_var (XEXP (x, 0), GET_MODE (XEXP (x, 0)),
dest_reg, p, inc_val, mult_val);
dest_reg, p, inc_val, mult_val, location);
case ASHIFTRT:
/* Similar, since this can be a sign extension. */
......@@ -5416,7 +5859,8 @@ basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val)
&& XEXP (x, 1) == XEXP (SET_SRC (set), 1))
return basic_induction_var (XEXP (SET_SRC (set), 0),
GET_MODE (XEXP (x, 0)),
dest_reg, insn, inc_val, mult_val);
dest_reg, insn, inc_val, mult_val,
location);
return 0;
default:
......@@ -5783,13 +6227,13 @@ simplify_giv_expr (x, benefit)
return 0;
/* Check for biv or giv. */
switch (reg_iv_type[REGNO (x)])
switch (REG_IV_TYPE (REGNO (x)))
{
case BASIC_INDUCT:
return x;
case GENERAL_INDUCT:
{
struct induction *v = reg_iv_info[REGNO (x)];
struct induction *v = REG_IV_INFO (REGNO (x));
/* Form expression from giv and add benefit. Ensure this giv
can derive another and subtract any needed adjustment if so. */
......@@ -6000,8 +6444,8 @@ consec_sets_giv (first_benefit, p, src_reg, dest_reg,
v->cant_derive = 0;
v->derive_adjustment = 0;
reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT;
reg_iv_info[REGNO (dest_reg)] = v;
REG_IV_TYPE (REGNO (dest_reg)) = GENERAL_INDUCT;
REG_IV_INFO (REGNO (dest_reg)) = v;
count = VARRAY_INT (n_times_set, REGNO (dest_reg)) - 1;
......@@ -6045,7 +6489,7 @@ consec_sets_giv (first_benefit, p, src_reg, dest_reg,
&& CONSTANT_P (SET_SRC (set)))
continue;
reg_iv_type[REGNO (dest_reg)] = UNKNOWN_INDUCT;
REG_IV_TYPE (REGNO (dest_reg)) = UNKNOWN_INDUCT;
return 0;
}
}
......@@ -6231,7 +6675,10 @@ combine_givs_p (g1, g2)
/* If these givs are identical, they can be combined. We use the results
of express_from because the addends are not in a canonical form, so
rtx_equal_p is a weaker test. */
if (tem == g1->dest_reg)
/* But don't combine a DEST_REG giv with a DEST_ADDR giv; we want the
combination to be the other way round. */
if (tem == g1->dest_reg
&& (g1->giv_type == DEST_REG || g2->giv_type == DEST_ADDR))
{
return g1->dest_reg;
}
......@@ -6411,7 +6858,7 @@ restart:
g2->new_reg = can_combine[i*giv_count + j];
g2->same = g1;
g1->combined_with = 1;
g1->combined_with++;
g1->lifetime += g2->lifetime;
g1_add_benefit += combine_givs_benefit_from (g1, g2);
......@@ -6470,6 +6917,387 @@ restart:
}
}
struct recombine_givs_stats
{
int giv_number;
int start_luid, end_luid;
};
/* Used below as comparison function for qsort. We want a ascending luid
when scanning the array starting at the end, thus the arguments are
used in reverse. */
static int
cmp_recombine_givs_stats (x, y)
struct recombine_givs_stats *x, *y;
{
int d;
d = y->start_luid - x->start_luid;
/* Stabilize the sort. */
if (!d)
d = y->giv_number - x->giv_number;
return d;
}
/* Scan X, which is a part of INSN, for the end of life of a giv. Also
look for the start of life of a giv where the start has not been seen
yet to unlock the search for the end of its life.
Only consider givs that belong to BIV.
Return the total number of lifetime ends that have been found. */
static int
find_life_end (x, stats, insn, biv)
rtx x, insn, biv;
struct recombine_givs_stats *stats;
{
enum rtx_code code;
char *fmt;
int i, j;
int retval;
code = GET_CODE (x);
switch (code)
{
case SET:
{
rtx reg = SET_DEST (x);
if (GET_CODE (reg) == REG)
{
int regno = REGNO (reg);
struct induction *v = REG_IV_INFO (regno);
if (REG_IV_TYPE (regno) == GENERAL_INDUCT
&& ! v->ignore
&& v->src_reg == biv
&& stats[v->ix].end_luid <= 0)
{
/* If we see a 0 here for end_luid, it means that we have
scanned the entire loop without finding any use at all.
We must not predicate this code on a start_luid match
since that would make the test fail for givs that have
been hoisted out of inner loops. */
if (stats[v->ix].end_luid == 0)
{
stats[v->ix].end_luid = stats[v->ix].start_luid;
return 1 + find_life_end (SET_SRC (x), stats, insn, biv);
}
else if (stats[v->ix].start_luid == INSN_LUID (insn))
stats[v->ix].end_luid = 0;
}
return find_life_end (SET_SRC (x), stats, insn, biv);
}
break;
}
case REG:
{
int regno = REGNO (x);
struct induction *v = REG_IV_INFO (regno);
if (REG_IV_TYPE (regno) == GENERAL_INDUCT
&& ! v->ignore
&& v->src_reg == biv
&& stats[v->ix].end_luid == 0)
{
while (INSN_UID (insn) >= max_uid_for_loop)
insn = NEXT_INSN (insn);
stats[v->ix].end_luid = INSN_LUID (insn);
return 1;
}
return 0;
}
case LABEL_REF:
case CONST_DOUBLE:
case CONST_INT:
case CONST:
return 0;
default:
break;
}
fmt = GET_RTX_FORMAT (code);
retval = 0;
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
retval += find_life_end (XEXP (x, i), stats, insn, biv);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
retval += find_life_end (XVECEXP (x, i, j), stats, insn, biv);
}
return retval;
}
/* For each giv that has been combined with another, look if
we can combine it with the most recently used one instead.
This tends to shorten giv lifetimes, and helps the next step:
try to derive givs from other givs. */
static void
recombine_givs (bl, loop_start, loop_end)
struct iv_class *bl;
rtx loop_start, loop_end;
{
struct induction *v, **giv_array, *last_giv;
struct recombine_givs_stats *stats;
int giv_count;
int i, rescan;
int ends_need_computing;
for (giv_count = 0, v = bl->giv; v; v = v->next_iv)
{
if (! v->ignore)
giv_count++;
}
giv_array
= (struct induction **) alloca (giv_count * sizeof (struct induction *));
stats = (struct recombine_givs_stats *) alloca (giv_count * sizeof *stats);
/* Initialize stats and set up the ix field for each giv in stats to name
the corresponding index into stats. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
rtx p;
if (v->ignore)
continue;
giv_array[i] = v;
stats[i].giv_number = i;
/* If this giv has been hoisted out of an inner loop, use the luid of
the previous insn. */
for (p = v->insn; INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
stats[i].start_luid = INSN_LUID (p);
v->ix = i;
i++;
}
qsort (stats, giv_count, sizeof(*stats), cmp_recombine_givs_stats);
/* Do the actual most-recently-used recombination. */
for (last_giv = 0, i = giv_count - 1; i >= 0; i--)
{
v = giv_array[stats[i].giv_number];
if (v->same)
{
struct induction *old_same = v->same;
rtx new_combine;
/* combine_givs_p actually says if we can make this transformation.
The other tests are here only to avoid keeping a giv alive
that could otherwise be eliminated. */
if (last_giv
&& ((old_same->maybe_dead && ! old_same->combined_with)
|| ! last_giv->maybe_dead
|| last_giv->combined_with)
&& (new_combine = combine_givs_p (last_giv, v)))
{
old_same->combined_with--;
v->new_reg = new_combine;
v->same = last_giv;
last_giv->combined_with++;
/* No need to update lifetimes / benefits here since we have
already decided what to reduce. */
continue;
}
v = v->same;
}
else if (v->giv_type != DEST_REG)
continue;
if (! last_giv
|| (last_giv->maybe_dead && ! last_giv->combined_with)
|| ! v->maybe_dead
|| v->combined_with)
last_giv = v;
}
ends_need_computing = 0;
/* For each DEST_REG giv, compute lifetime starts, and try to compute
lifetime ends from regscan info. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
if (v->ignore)
continue;
if (v->giv_type == DEST_ADDR)
{
/* Loop unrolling of an inner loop can even create new DEST_REG
givs. */
rtx p;
for (p = v->insn; INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
stats[i].start_luid = stats[i].end_luid = INSN_LUID (p);
if (p != v->insn)
stats[i].end_luid++;
}
else /* v->giv_type == DEST_REG */
{
if (v->last_use)
{
stats[i].start_luid = INSN_LUID (v->insn);
stats[i].end_luid = INSN_LUID (v->last_use);
}
else if (INSN_UID (v->insn) >= max_uid_for_loop)
{
rtx p;
/* This insn has been created by loop optimization on an inner
loop. We don't have a proper start_luid that will match
when we see the first set. But we do know that there will
be no use before the set, so we can set end_luid to 0 so that
we'll start looking for the last use right away. */
for (p = PREV_INSN (v->insn); INSN_UID (p) >= max_uid_for_loop; )
p = PREV_INSN (p);
stats[i].start_luid = INSN_LUID (p);
stats[i].end_luid = 0;
ends_need_computing++;
}
else
{
int regno = REGNO (v->dest_reg);
int count = VARRAY_INT (n_times_set, regno) - 1;
rtx p = v->insn;
/* Find the first insn that sets the giv, so that we can verify
if this giv's lifetime wraps around the loop. We also need
the luid of the first setting insn in order to detect the
last use properly. */
while (count)
{
p = prev_nonnote_insn (p);
if (reg_set_p (v->dest_reg, p))
count--;
}
stats[i].start_luid = INSN_LUID (p);
if (stats[i].start_luid > uid_luid[REGNO_FIRST_UID (regno)])
{
stats[i].end_luid = -1;
ends_need_computing++;
}
else
{
stats[i].end_luid = uid_luid[REGNO_LAST_UID (regno)];
if (stats[i].end_luid > INSN_LUID (loop_end))
{
stats[i].end_luid = -1;
ends_need_computing++;
}
}
}
}
i++;
}
/* If the regscan information was unconclusive for one or more DEST_REG
givs, scan the all insn in the loop to find out lifetime ends. */
if (ends_need_computing)
{
rtx biv = bl->biv->src_reg;
rtx p = loop_end;
do
{
if (p == loop_start)
p = loop_end;
p = PREV_INSN (p);
if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
continue;
ends_need_computing -= find_life_end (PATTERN (p), stats, p, biv);
}
while (ends_need_computing);
}
/* Set start_luid back to the last insn that sets the giv. This allows
more combinations. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
if (v->ignore)
continue;
if (INSN_UID (v->insn) < max_uid_for_loop)
stats[i].start_luid = INSN_LUID (v->insn);
i++;
}
/* Now adjust lifetime ends by taking combined givs into account. */
for (i = 0, v = bl->giv; v; v = v->next_iv)
{
unsigned luid;
int j;
if (v->ignore)
continue;
if (v->same && ! v->same->ignore)
{
j = v->same->ix;
luid = stats[i].start_luid;
/* Use unsigned arithmetic to model loop wrap-around. */
if (luid - stats[j].start_luid
> (unsigned) stats[j].end_luid - stats[j].start_luid)
stats[j].end_luid = luid;
}
i++;
}
qsort (stats, giv_count, sizeof(*stats), cmp_recombine_givs_stats);
/* Try to derive DEST_REG givs from previous DEST_REG givs with the
same mult_val and non-overlapping lifetime. This reduces register
pressure.
Once we find a DEST_REG giv that is suitable to derive others from,
we set last_giv to this giv, and try to derive as many other DEST_REG
givs from it without joining overlapping lifetimes. If we then
encounter a DEST_REG giv that we can't derive, we set rescan to the
index for this giv (unless rescan is already set).
When we are finished with the current LAST_GIV (i.e. the inner loop
terminates), we start again with rescan, which then becomes the new
LAST_GIV. */
for (i = giv_count - 1; i >= 0; i = rescan)
{
int life_start, life_end;
for (last_giv = 0, rescan = -1; i >= 0; i--)
{
rtx sum;
v = giv_array[stats[i].giv_number];
if (v->giv_type != DEST_REG || v->derived)
continue;
if (! last_giv)
{
if (! v->same)
{
last_giv = v;
life_start = stats[i].start_luid;
life_end = stats[i].end_luid;
}
continue;
}
/* Use unsigned arithmetic to model loop wrap around. */
if (((unsigned) stats[i].start_luid - life_start
>= (unsigned) life_end - life_start)
&& ((unsigned) stats[i].end_luid - life_start
>= (unsigned) life_end - life_start)
&& rtx_equal_p (last_giv->mult_val, v->mult_val)
/* ??? Could handle libcalls, but would need more logic. */
&& ! find_reg_note (v->insn, REG_RETVAL, NULL_RTX)
/* We would really like to know if for any giv that v
is combined with, v->insn or any intervening biv increment
dominates that combined giv. However, we
don't have this detailed control flow information.
N.B. since last_giv will be reduced, it is valid
anywhere in the loop, so we don't need to check the
validity of last_giv. */
&& (v->always_executed || ! v->combined_with)
&& (sum = express_from (last_giv, v))
&& validate_change (v->insn, &PATTERN (v->insn),
gen_rtx_SET (GET_MODE (v->dest_reg),
v->dest_reg, sum), 0))
{
v->derived = 1;
v->new_reg = v->dest_reg;
life_end = stats[i].end_luid;
}
else if (rescan < 0)
rescan = i;
}
}
}
/* EMIT code before INSERT_BEFORE to set REG = B * M + A. */
void
......@@ -7473,7 +8301,7 @@ maybe_eliminate_biv_1 (x, insn, bl, eliminate_p, where)
#if 0
/* Otherwise the reg compared with had better be a biv. */
if (GET_CODE (arg) != REG
|| reg_iv_type[REGNO (arg)] != BASIC_INDUCT)
|| REG_IV_TYPE (REGNO (arg)) != BASIC_INDUCT)
return 0;
/* Look for a pair of givs, one for each biv,
......@@ -7586,7 +8414,7 @@ record_initial (dest, set)
if (GET_CODE (dest) != REG
|| REGNO (dest) >= max_reg_before_loop
|| reg_iv_type[REGNO (dest)] != BASIC_INDUCT)
|| REG_IV_TYPE (REGNO (dest)) != BASIC_INDUCT)
return;
bl = reg_biv_class[REGNO (dest)];
......
......@@ -18,6 +18,8 @@ along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "varray.h"
/* Get the luid of an insn. Catch the error of trying to reference the LUID
of an insn added during loop, since these don't have LUIDs. */
......@@ -54,6 +56,8 @@ struct induction
For a DEST_ADDR type giv, this is 0. */
rtx *location; /* Place in the insn where this giv occurs.
If GIV_TYPE is DEST_REG, this is 0. */
/* For a biv, this is the place where add_val
was found. */
enum machine_mode mode; /* The mode of this biv or giv */
enum machine_mode mem_mode; /* For DEST_ADDR, mode of the memory object. */
rtx mult_val; /* Multiplicative factor for src_reg. */
......@@ -63,6 +67,9 @@ struct induction
final value could be calculated, it is put
here, and the giv is made replaceable. Set
the giv to this value before the loop. */
unsigned combined_with; /* The number of givs this giv has been
combined with. If nonzero, this giv
cannot combine with any other giv. */
unsigned replaceable : 1; /* 1 if we can substitute the strength-reduced
variable for the original variable.
0 means they must be kept separate and the
......@@ -85,8 +92,6 @@ struct induction
another giv. This occurs in many cases
where a giv's lifetime spans an update to
a biv. */
unsigned combined_with : 1; /* 1 if this giv has been combined with. It
then cannot combine with any other giv. */
unsigned maybe_dead : 1; /* 1 if this giv might be dead. In that case,
we won't use it to eliminate a biv, it
would probably lose. */
......@@ -96,6 +101,8 @@ struct induction
initialized in unrolled loop. */
unsigned shared : 1;
unsigned no_const_addval : 1; /* 1 if add_val does not contain a const. */
unsigned derived : 1; /* For a giv, 1 if we decided to derive this
giv from another one. */
int lifetime; /* Length of life of this giv */
rtx derive_adjustment; /* If nonzero, is an adjustment to be
subtracted from add_val when this giv
......@@ -112,10 +119,14 @@ struct induction
is split, and a constant is eliminated from
the address, the -constant is stored here
for later use. */
int ix; /* Used by recombine_givs, as n index into
the stats array. */
struct induction *same_insn; /* If there are multiple identical givs in
the same insn, then all but one have this
field set, and they all point to the giv
that doesn't have this field set. */
rtx last_use; /* For a giv made from a biv increment, this is
a substitute for the lifetime information. */
};
/* A `struct iv_class' is created for each biv. */
......@@ -197,11 +208,19 @@ extern int max_reg_before_loop;
extern FILE *loop_dump_stream;
extern enum iv_mode *reg_iv_type;
extern struct induction **reg_iv_info;
extern varray_type reg_iv_type;
extern varray_type reg_iv_info;
#define REG_IV_TYPE(n) \
(*(enum iv_mode *) &VARRAY_INT(reg_iv_type, (n)))
#define REG_IV_INFO(n) \
(*(struct induction **) &VARRAY_GENERIC_PTR(reg_iv_info, (n)))
extern struct iv_class **reg_biv_class;
extern struct iv_class *loop_iv_list;
extern int first_increment_giv, last_increment_giv;
/* Forward declarations for non-static functions declared in loop.c and
unroll.c. */
int invariant_p PROTO((rtx));
......
......@@ -1008,6 +1008,7 @@ extern int reg_set_between_p PROTO((rtx, rtx, rtx));
extern int regs_set_between_p PROTO((rtx, rtx, rtx));
extern int modified_between_p PROTO((rtx, rtx, rtx));
extern int no_labels_between_p PROTO((rtx, rtx));
extern int no_jumps_between_p PROTO((rtx, rtx));
extern int modified_in_p PROTO((rtx, rtx));
extern int reg_set_p PROTO((rtx, rtx));
extern rtx single_set PROTO((rtx));
......@@ -1035,6 +1036,7 @@ extern rtx replace_regs PROTO((rtx, rtx *, int, int));
extern int computed_jump_p PROTO((rtx));
typedef int (*rtx_function) PROTO((rtx *, void *));
extern int for_each_rtx PROTO((rtx *, rtx_function, void *));
extern int insn_first_p PROTO((rtx, rtx));
/* flow.c */
......
......@@ -326,6 +326,20 @@ no_labels_between_p (beg, end)
return 1;
}
/* Return 1 if in between BEG and END, exclusive of BEG and END, there is
no JUMP_INSN insn. */
int
no_jumps_between_p (beg, end)
rtx beg, end;
{
register rtx p;
for (p = NEXT_INSN (beg); p != end; p = NEXT_INSN (p))
if (GET_CODE (p) == JUMP_INSN)
return 0;
return 1;
}
/* Nonzero if register REG is used in an insn between
FROM_INSN and TO_INSN (exclusive of those two). */
......@@ -2187,3 +2201,20 @@ for_each_rtx (x, f, data)
return 0;
}
/* INSN and REFERENCE are instructions in the same insn chain.
Return non-zero if INSN is first. */
int
insn_first_p (insn, reference)
rtx insn, reference;
{
rtx p, q;
for (p = insn, q = reference; ; p = NEXT_INSN (p), q = NEXT_INSN (q))
{
if (p == reference || ! q)
return 1;
if (q == insn || ! p)
return 0;
}
}
......@@ -180,6 +180,10 @@ static struct induction **addr_combined_regs;
static rtx *splittable_regs;
/* Indexed by register number, if this is a splittable induction variable,
this indicates if it was made from a derived giv. */
static char *derived_regs;
/* Indexed by register number, if this is a splittable induction variable,
then this will hold the number of instructions in the loop that modify
the induction variable. Used to ensure that only the last insn modifying
a split iv will update the original iv of the dest. */
......@@ -761,16 +765,15 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
splittable_regs = (rtx *) alloca (maxregnum * sizeof (rtx));
bzero ((char *) splittable_regs, maxregnum * sizeof (rtx));
derived_regs = alloca (maxregnum);
bzero (derived_regs, maxregnum);
splittable_regs_updates = (int *) alloca (maxregnum * sizeof (int));
bzero ((char *) splittable_regs_updates, maxregnum * sizeof (int));
addr_combined_regs
= (struct induction **) alloca (maxregnum * sizeof (struct induction *));
bzero ((char *) addr_combined_regs, maxregnum * sizeof (struct induction *));
/* We must limit it to max_reg_before_loop, because only these pseudo
registers have valid regno_first_uid info. Any register created after
that is unlikely to be local to the loop anyways. */
local_regno = (char *) alloca (max_reg_before_loop);
bzero (local_regno, max_reg_before_loop);
local_regno = (char *) alloca (maxregnum);
bzero (local_regno, maxregnum);
/* Mark all local registers, i.e. the ones which are referenced only
inside the loop. */
......@@ -793,6 +796,8 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
/* If a pseudo's lifetime is entirely contained within this loop, then we
can use a different pseudo in each unrolled copy of the loop. This
results in better code. */
/* We must limit the generic test to max_reg_before_loop, because only
these pseudo registers have valid regno_first_uid info. */
for (j = FIRST_PSEUDO_REGISTER; j < max_reg_before_loop; ++j)
if (REGNO_FIRST_UID (j) > 0 && REGNO_FIRST_UID (j) <= max_uid_for_loop
&& uid_luid[REGNO_FIRST_UID (j)] >= copy_start_luid
......@@ -821,6 +826,14 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
j);
}
}
/* Givs that have been created from multiple biv increments always have
local registers. */
for (j = first_increment_giv; j <= last_increment_giv; j++)
{
local_regno[j] = 1;
if (loop_dump_stream)
fprintf (loop_dump_stream, "Marked reg %d as local\n", j);
}
}
/* If this loop requires exit tests when unrolled, check to see if we
......@@ -1041,7 +1054,7 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
if (local_label[j])
set_label_in_map (map, j, gen_label_rtx ());
for (j = FIRST_PSEUDO_REGISTER; j < max_reg_before_loop; j++)
for (j = FIRST_PSEUDO_REGISTER; j < maxregnum; j++)
if (local_regno[j])
{
map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j]));
......@@ -1197,7 +1210,7 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
if (local_label[j])
set_label_in_map (map, j, gen_label_rtx ());
for (j = FIRST_PSEUDO_REGISTER; j < max_reg_before_loop; j++)
for (j = FIRST_PSEUDO_REGISTER; j < maxregnum; j++)
if (local_regno[j])
{
map->reg_map[j] = gen_reg_rtx (GET_MODE (regno_reg_rtx[j]));
......@@ -1701,7 +1714,8 @@ copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
we might accidentally delete insns generated immediately
below by emit_unrolled_add. */
giv_inc = calculate_giv_inc (set, insn, regno);
if (! derived_regs[regno])
giv_inc = calculate_giv_inc (set, insn, regno);
/* Now find all address giv's that were combined with this
giv 'v'. */
......@@ -1780,16 +1794,25 @@ copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
&& splittable_regs[REGNO (SET_DEST (set))])
{
int regno = REGNO (SET_DEST (set));
int src_regno;
dest_reg_was_split = 1;
/* Compute the increment value for the giv, if it wasn't
already computed above. */
if (giv_inc == 0)
giv_inc = calculate_giv_inc (set, insn, regno);
giv_dest_reg = SET_DEST (set);
giv_src_reg = SET_DEST (set);
if (derived_regs[regno])
{
giv_src_reg = XEXP (SET_SRC (set), 0);
giv_inc = XEXP (SET_SRC (set), 1);
}
else
{
giv_src_reg = giv_dest_reg;
/* Compute the increment value for the giv, if it wasn't
already computed above. */
if (giv_inc == 0)
giv_inc = calculate_giv_inc (set, insn, regno);
}
src_regno = REGNO (giv_src_reg);
if (unroll_type == UNROLL_COMPLETELY)
{
......@@ -1799,7 +1822,8 @@ copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
/* The value in splittable_regs may be an invariant
value, so we must use plus_constant here. */
splittable_regs[regno]
= plus_constant (splittable_regs[regno], INTVAL (giv_inc));
= plus_constant (splittable_regs[src_regno],
INTVAL (giv_inc));
if (GET_CODE (splittable_regs[regno]) == PLUS)
{
......@@ -1830,7 +1854,7 @@ copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
induction entry by find_splittable_regs. */
if (regno < max_reg_before_loop
&& reg_iv_type[regno] == BASIC_INDUCT)
&& REG_IV_TYPE (regno) == BASIC_INDUCT)
{
giv_src_reg = reg_biv_class[regno]->biv->src_reg;
giv_dest_reg = giv_src_reg;
......@@ -1844,7 +1868,7 @@ copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
splittable_regs[regno]
= GEN_INT (INTVAL (giv_inc)
+ INTVAL (splittable_regs[regno]));
+ INTVAL (splittable_regs[src_regno]));
giv_inc = splittable_regs[regno];
/* Now split the induction variable by changing the dest
......@@ -2338,7 +2362,7 @@ iteration_info (iteration_var, initial_value, increment, loop_start, loop_end)
/* If this is a new register, can't handle it since we don't have any
reg_iv_type entry for it. */
if (REGNO (iteration_var) >= max_reg_before_loop)
if ((unsigned) REGNO (iteration_var) > reg_iv_type->num_elements)
{
if (loop_dump_stream)
fprintf (loop_dump_stream,
......@@ -2364,7 +2388,7 @@ iteration_info (iteration_var, initial_value, increment, loop_start, loop_end)
"Loop unrolling: Iteration var not an integer.\n");
return;
}
else if (reg_iv_type[REGNO (iteration_var)] == BASIC_INDUCT)
else if (REG_IV_TYPE (REGNO (iteration_var)) == BASIC_INDUCT)
{
/* Grab initial value, only useful if it is a constant. */
bl = reg_biv_class[REGNO (iteration_var)];
......@@ -2372,7 +2396,7 @@ iteration_info (iteration_var, initial_value, increment, loop_start, loop_end)
*increment = biv_total_increment (bl, loop_start, loop_end);
}
else if (reg_iv_type[REGNO (iteration_var)] == GENERAL_INDUCT)
else if (REG_IV_TYPE (REGNO (iteration_var)) == GENERAL_INDUCT)
{
#if 1
/* ??? The code below does not work because the incorrect number of
......@@ -2390,7 +2414,7 @@ iteration_info (iteration_var, initial_value, increment, loop_start, loop_end)
#else
/* Initial value is mult_val times the biv's initial value plus
add_val. Only useful if it is a constant. */
v = reg_iv_info[REGNO (iteration_var)];
v = REG_IV_INFO (REGNO (iteration_var));
bl = reg_biv_class[REGNO (v->src_reg)];
*initial_value = fold_rtx_mult_add (v->mult_val, bl->initial_value,
v->add_val, v->mode);
......@@ -2708,6 +2732,10 @@ find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
/* Line above always fails if INSN was moved by loop opt. */
|| (uid_luid[REGNO_LAST_UID (REGNO (v->dest_reg))]
>= INSN_LUID (loop_end)))
/* Givs made from biv increments are missed by the above test, so
test explicitly for them. */
&& (REGNO (v->dest_reg) < first_increment_giv
|| REGNO (v->dest_reg) > last_increment_giv)
&& ! (final_value = v->final_value))
continue;
......@@ -2808,6 +2836,7 @@ find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
}
splittable_regs[REGNO (v->new_reg)] = value;
derived_regs[REGNO (v->new_reg)] = v->derived;
}
else
{
......@@ -2991,6 +3020,7 @@ find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
Make sure that it's giv is marked as splittable here. */
splittable_regs[REGNO (v->new_reg)] = value;
derived_regs[REGNO (v->new_reg)] = v->derived;
/* Make it appear to depend upon itself, so that the
giv will be properly split in the main loop above. */
......@@ -3902,7 +3932,7 @@ remap_split_bivs (x)
have to remap those givs also. */
#endif
if (REGNO (x) < max_reg_before_loop
&& reg_iv_type[REGNO (x)] == BASIC_INDUCT)
&& REG_IV_TYPE (REGNO (x)) == BASIC_INDUCT)
return reg_biv_class[REGNO (x)]->biv->src_reg;
break;
......
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