/* Perform various loop optimizations, including strength reduction.
Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "predict.h"
#include "insn-flags.h"
#include "optabs.h"
+#include "cfgloop.h"
/* Not really meaningful values, but at least something. */
#ifndef SIMULTANEOUS_PREFETCHES
short savings; /* Number of insns we can move for this reg,
including other movables that force this
or match this one. */
+ ENUM_BITFIELD(machine_mode) savemode : 8; /* Nonzero means it is a mode for
+ a low part that we should avoid changing when
+ clearing the rest of the reg. */
unsigned int cond : 1; /* 1 if only conditionally movable */
unsigned int force : 1; /* 1 means MUST move this insn */
unsigned int global : 1; /* 1 means reg is live outside this loop */
unsigned int move_insn_first:1;/* Same as above, if this is necessary for the
first insn of a consecutive sets group. */
unsigned int is_equiv : 1; /* 1 means a REG_EQUIV is present on INSN. */
- enum machine_mode savemode; /* Nonzero means it is a mode for a low part
- that we should avoid changing when clearing
- the rest of the reg. */
+ unsigned int insert_temp : 1; /* 1 means we copy to a new pseudo and replace
+ the original insn with a copy from that
+ pseudo, rather than deleting it. */
struct movable *match; /* First entry for same value */
struct movable *forces; /* An insn that must be moved if this is */
struct movable *next;
static rtx next_insn_in_loop PARAMS ((const struct loop *, rtx));
static void loop_regs_scan PARAMS ((const struct loop *, int));
static int count_insns_in_loop PARAMS ((const struct loop *));
+static int find_mem_in_note_1 PARAMS ((rtx *, void *));
+static rtx find_mem_in_note PARAMS ((rtx));
static void load_mems PARAMS ((const struct loop *));
static int insert_loop_mem PARAMS ((rtx *, void *));
static int replace_loop_mem PARAMS ((rtx *, void *));
-static void replace_loop_mems PARAMS ((rtx, rtx, rtx));
+static void replace_loop_mems PARAMS ((rtx, rtx, rtx, int));
static int replace_loop_reg PARAMS ((rtx *, void *));
static void replace_loop_regs PARAMS ((rtx insn, rtx, rtx));
static void note_reg_stored PARAMS ((rtx, rtx, void *));
static void try_copy_prop PARAMS ((const struct loop *, rtx, unsigned int));
static void try_swap_copy_prop PARAMS ((const struct loop *, rtx,
unsigned int));
-static int replace_label PARAMS ((rtx *, void *));
static rtx check_insn_for_givs PARAMS((struct loop *, rtx, int, int));
static rtx check_insn_for_bivs PARAMS((struct loop *, rtx, int, int));
static rtx gen_add_mult PARAMS ((rtx, rtx, rtx, rtx));
void debug_loop PARAMS ((const struct loop *));
void debug_loops PARAMS ((const struct loops *));
-typedef struct rtx_pair
-{
- rtx r1;
- rtx r2;
-} rtx_pair;
-
typedef struct loop_replace_args
{
rtx match;
/* Allocate and initialize auxiliary loop information. */
loops_info = xcalloc (loops->num, sizeof (struct loop_info));
- for (i = 0; i < loops->num; i++)
+ for (i = 0; i < (int) loops->num; i++)
loops->array[i].aux = loops_info + i;
/* Now find all register lifetimes. This must be done after
int tem1 = 0;
int tem2 = 0;
int move_insn = 0;
+ int insert_temp = 0;
rtx src = SET_SRC (set);
rtx dependencies = 0;
}
}
- /* Don't try to optimize a register that was made
- by loop-optimization for an inner loop.
- We don't know its life-span, so we can't compute
- the benefit. */
- if (REGNO (SET_DEST (set)) >= max_reg_before_loop)
- ;
- else if (/* The register is used in basic blocks other
- than the one where it is set (meaning that
- something after this point in the loop might
- depend on its value before the set). */
- ! reg_in_basic_block_p (p, SET_DEST (set))
- /* And the set is not guaranteed to be executed once
- the loop starts, or the value before the set is
- needed before the set occurs...
-
- ??? Note we have quadratic behavior here, mitigated
- by the fact that the previous test will often fail for
- large loops. Rather than re-scanning the entire loop
- each time for register usage, we should build tables
- of the register usage and use them here instead. */
- && (maybe_never
- || loop_reg_used_before_p (loop, set, p)))
- /* It is unsafe to move the set.
+ if (/* The register is used in basic blocks other
+ than the one where it is set (meaning that
+ something after this point in the loop might
+ depend on its value before the set). */
+ ! reg_in_basic_block_p (p, SET_DEST (set))
+ /* And the set is not guaranteed to be executed once
+ the loop starts, or the value before the set is
+ needed before the set occurs...
+
+ ??? Note we have quadratic behavior here, mitigated
+ by the fact that the previous test will often fail for
+ large loops. Rather than re-scanning the entire loop
+ each time for register usage, we should build tables
+ of the register usage and use them here instead. */
+ && (maybe_never
+ || loop_reg_used_before_p (loop, set, p)))
+ /* It is unsafe to move the set. However, it may be OK to
+ move the source into a new pseudo, and substitute a
+ reg-to-reg copy for the original insn.
This code used to consider it OK to move a set of a variable
which was not created by the user and not used in an exit
test.
That behavior is incorrect and was removed. */
+ insert_temp = 1;
+
+ /* Don't try to optimize a MODE_CC set with a constant
+ source. It probably will be combined with a conditional
+ jump. */
+ if (GET_MODE_CLASS (GET_MODE (SET_DEST (set))) == MODE_CC
+ && CONSTANT_P (src))
+ ;
+ /* Don't try to optimize a register that was made
+ by loop-optimization for an inner loop.
+ We don't know its life-span, so we can't compute
+ the benefit. */
+ else if (REGNO (SET_DEST (set)) >= max_reg_before_loop)
+ ;
+ /* Don't move the source and add a reg-to-reg copy:
+ - with -Os (this certainly increases size),
+ - if the mode doesn't support copy operations (obviously),
+ - if the source is already a reg (the motion will gain nothing),
+ - if the source is a legitimate constant (likewise). */
+ else if (insert_temp
+ && (optimize_size
+ || ! can_copy_p (GET_MODE (SET_SRC (set)))
+ || GET_CODE (SET_SRC (set)) == REG
+ || (CONSTANT_P (SET_SRC (set))
+ && LEGITIMATE_CONSTANT_P (SET_SRC (set)))))
;
else if ((tem = loop_invariant_p (loop, src))
&& (dependencies == 0
m->partial = 0;
m->move_insn = move_insn;
m->move_insn_first = 0;
+ m->insert_temp = insert_temp;
m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0);
m->savemode = VOIDmode;
m->regno = regno;
m->forces = 0;
m->move_insn = 0;
m->move_insn_first = 0;
+ m->insert_temp = insert_temp;
m->partial = 1;
/* If the insn may not be executed on some cycles,
we can't clear the whole reg; clear just high part.
/* Now consider each movable insn to decide whether it is worth moving.
Store 0 in regs->array[I].set_in_loop for each reg I that is moved.
- Generally this increases code size, so do not move moveables when
- optimizing for code size. */
+ For machines with few registers this increases code size, so do not
+ move moveables when optimizing for code size on such machines.
+ (The 18 below is the value for i386.) */
- if (! optimize_size)
+ if (!optimize_size
+ || (reg_class_size[GENERAL_REGS] > 18 && !loop_info->has_call))
{
move_movables (loop, movables, threshold, insn_count);
for (m = movables->head; m; m = m->next)
if (m->match == 0 && regs->array[m->regno].n_times_set == 1
&& m->regno >= FIRST_PSEUDO_REGISTER
+ && !m->insert_temp
&& !m->partial)
{
struct movable *m1;
one match any later ones. So start this loop at m->next. */
for (m1 = m->next; m1; m1 = m1->next)
if (m != m1 && m1->match == 0
+ && !m1->insert_temp
&& regs->array[m1->regno].n_times_set == 1
&& m1->regno >= FIRST_PSEUDO_REGISTER
/* A reg used outside the loop mustn't be eliminated. */
int count;
struct movable *m1;
rtx first = NULL_RTX;
+ rtx newreg = NULL_RTX;
+
+ if (m->insert_temp)
+ newreg = gen_reg_rtx (GET_MODE (m->set_dest));
/* Now move the insns that set the reg. */
insn stream. */
while (p && GET_CODE (p) == NOTE)
p = NEXT_INSN (temp) = NEXT_INSN (p);
+
+ if (m->insert_temp)
+ {
+ /* Replace the original insn with a move from
+ our newly created temp. */
+ start_sequence ();
+ emit_move_insn (m->set_dest, newreg);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn_before (seq, p);
+ }
}
start_sequence ();
- emit_move_insn (m->set_dest, m->set_src);
+ emit_move_insn (m->insert_temp ? newreg : m->set_dest,
+ m->set_src);
seq = get_insns ();
end_sequence ();
set_unique_reg_note (i1, m->is_equiv ? REG_EQUIV
: REG_EQUAL, m->set_src);
}
+ else if (m->insert_temp)
+ {
+ rtx *reg_map2 = (rtx *) xcalloc (REGNO (newreg),
+ sizeof(rtx));
+ reg_map2 [m->regno] = newreg;
+
+ i1 = loop_insn_hoist (loop, copy_rtx (PATTERN (p)));
+ replace_regs (i1, reg_map2, REGNO (newreg), 1);
+ free (reg_map2);
+ }
else
i1 = loop_insn_hoist (loop, PATTERN (p));
insn stream. */
while (p && GET_CODE (p) == NOTE)
p = NEXT_INSN (temp) = NEXT_INSN (p);
+
+ if (m->insert_temp)
+ {
+ rtx seq;
+ /* Replace the original insn with a move from
+ our newly created temp. */
+ start_sequence ();
+ emit_move_insn (m->set_dest, newreg);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn_before (seq, p);
+ }
}
/* The more regs we move, the less we like moving them. */
threshold -= 3;
}
- /* Any other movable that loads the same register
- MUST be moved. */
- already_moved[regno] = 1;
-
- /* This reg has been moved out of one loop. */
- regs->array[regno].moved_once = 1;
+ m->done = 1;
- /* The reg set here is now invariant. */
- if (! m->partial)
+ if (!m->insert_temp)
{
- int i;
- for (i = 0; i < LOOP_REGNO_NREGS (regno, m->set_dest); i++)
- regs->array[regno+i].set_in_loop = 0;
- }
+ /* Any other movable that loads the same register
+ MUST be moved. */
+ already_moved[regno] = 1;
- m->done = 1;
+ /* This reg has been moved out of one loop. */
+ regs->array[regno].moved_once = 1;
- /* Change the length-of-life info for the register
- to say it lives at least the full length of this loop.
- This will help guide optimizations in outer loops. */
+ /* The reg set here is now invariant. */
+ if (! m->partial)
+ {
+ int i;
+ for (i = 0; i < LOOP_REGNO_NREGS (regno, m->set_dest); i++)
+ regs->array[regno+i].set_in_loop = 0;
+ }
- if (REGNO_FIRST_LUID (regno) > INSN_LUID (loop_start))
- /* This is the old insn before all the moved insns.
- We can't use the moved insn because it is out of range
- in uid_luid. Only the old insns have luids. */
- REGNO_FIRST_UID (regno) = INSN_UID (loop_start);
- if (REGNO_LAST_LUID (regno) < INSN_LUID (loop_end))
- REGNO_LAST_UID (regno) = INSN_UID (loop_end);
+ /* Change the length-of-life info for the register
+ to say it lives at least the full length of this loop.
+ This will help guide optimizations in outer loops. */
+
+ if (REGNO_FIRST_LUID (regno) > INSN_LUID (loop_start))
+ /* This is the old insn before all the moved insns.
+ We can't use the moved insn because it is out of range
+ in uid_luid. Only the old insns have luids. */
+ REGNO_FIRST_UID (regno) = INSN_UID (loop_start);
+ if (REGNO_LAST_LUID (regno) < INSN_LUID (loop_end))
+ REGNO_LAST_UID (regno) = INSN_UID (loop_end);
+ }
/* Combine with this moved insn any other matching movables. */
loop_info->has_call = 1;
if (can_throw_internal (insn))
loop_info->has_multiple_exit_targets = 1;
+
+ /* Calls initializing constant objects have CLOBBER of MEM /u in the
+ attached FUNCTION_USAGE expression list, not accounted for by the
+ code above. We should note these to avoid missing dependencies in
+ later references. */
+ {
+ rtx fusage_entry;
+
+ for (fusage_entry = CALL_INSN_FUNCTION_USAGE (insn);
+ fusage_entry; fusage_entry = XEXP (fusage_entry, 1))
+ {
+ rtx fusage = XEXP (fusage_entry, 0);
+
+ if (GET_CODE (fusage) == CLOBBER
+ && GET_CODE (XEXP (fusage, 0)) == MEM
+ && RTX_UNCHANGING_P (XEXP (fusage, 0)))
+ {
+ note_stores (fusage, note_addr_stored, loop_info);
+ if (! loop_info->first_loop_store_insn
+ && loop_info->store_mems)
+ loop_info->first_loop_store_insn = insn;
+ }
+ }
+ }
break;
case JUMP_INSN:
We don't know the loop bounds here though, so just fail for all
labels. */
- if (flag_unroll_loops)
+ if (flag_old_unroll_loops)
return 0;
else
return 1;
" density: %d%%; bytes_accessed: %u; total_bytes: %u\n",
(int) (info[i].bytes_accessed * 100 / info[i].stride),
info[i].bytes_accessed, info[i].total_bytes);
- fprintf (loop_dump_stream, " index: ");
- fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, info[i].index);
- fprintf (loop_dump_stream, "; stride: ");
- fprintf (loop_dump_stream, HOST_WIDE_INT_PRINT_DEC, info[i].stride);
- fprintf (loop_dump_stream, "; address: ");
+ fprintf (loop_dump_stream, " index: " HOST_WIDE_INT_PRINT_DEC
+ "; stride: " HOST_WIDE_INT_PRINT_DEC "; address: ",
+ info[i].index, info[i].stride);
print_rtl (loop_dump_stream, info[i].base_address);
fprintf (loop_dump_stream, "\n");
}
non-constant INIT_VAL to have the same mode as REG, which
in this case we know to be Pmode. */
if (GET_MODE (init_val) != Pmode && !CONSTANT_P (init_val))
- init_val = convert_to_mode (Pmode, init_val, 0);
+ {
+ rtx seq;
+
+ start_sequence ();
+ init_val = convert_to_mode (Pmode, init_val, 0);
+ seq = get_insns ();
+ end_sequence ();
+ loop_insn_emit_before (loop, 0, loop_start, seq);
+ }
loop_iv_add_mult_emit_before (loop, init_val,
info[i].giv->mult_val,
add_val, reg, 0, loop_start);
{
rtx insert_before;
+ /* Skip if location is the same as a previous one. */
+ if (tv->same)
+ continue;
if (! auto_inc_opt)
insert_before = NEXT_INSN (tv->insn);
else if (auto_inc_opt == 1)
}
}
-#ifndef DONT_REDUCE_ADDR
/* Look for givs which are memory addresses. */
- /* This resulted in worse code on a VAX 8600. I wonder if it
- still does. */
if (GET_CODE (p) == INSN)
find_mem_givs (loop, PATTERN (p), p, not_every_iteration,
maybe_multiple);
-#endif
/* Update the status of whether giv can derive other givs. This can
change when we pass a label or an insn that updates a biv. */
v->always_computable = ! not_every_iteration;
v->always_executed = ! not_every_iteration;
v->maybe_multiple = maybe_multiple;
+ v->same = 0;
/* Add this to the reg's iv_class, creating a class
if this is the first incrementation of the reg. */
/* Put it in the array of biv register classes. */
REG_IV_CLASS (ivs, REGNO (dest_reg)) = bl;
}
+ else
+ {
+ /* Check if location is the same as a previous one. */
+ struct induction *induction;
+ for (induction = bl->biv; induction; induction = induction->next_iv)
+ if (location == induction->location)
+ {
+ v->same = induction;
+ break;
+ }
+ }
/* Update IV_CLASS entry for this biv. */
v->next_iv = bl->biv;
return 0;
case SIGN_EXTEND:
+ /* Ignore this BIV if signed arithmetic overflow is defined. */
+ if (flag_wrapv)
+ return 0;
return basic_induction_var (loop, XEXP (x, 0), GET_MODE (XEXP (x, 0)),
dest_reg, p, inc_val, mult_val, location);
the expression of G2 in terms of G1 can be used. */
if (ret != NULL_RTX
&& g2->giv_type == DEST_ADDR
- && memory_address_p (GET_MODE (g2->mem), ret)
- /* ??? Looses, especially with -fforce-addr, where *g2->location
- will always be a register, and so anything more complicated
- gets discarded. */
-#if 0
-#ifdef ADDRESS_COST
- && ADDRESS_COST (tem) <= ADDRESS_COST (*g2->location)
-#else
- && rtx_cost (tem, MEM) <= rtx_cost (*g2->location, MEM)
-#endif
-#endif
- )
- {
- return ret;
- }
+ && memory_address_p (GET_MODE (g2->mem), ret))
+ return ret;
return NULL_RTX;
}
&& REGNO (SET_DEST (set)) == bl->regno)
/* An insn that sets the biv is okay. */
;
- else if ((p == prev_nonnote_insn (prev_nonnote_insn (loop_end))
- || p == prev_nonnote_insn (loop_end))
- && reg_mentioned_p (bivreg, PATTERN (p)))
+ else if (!reg_mentioned_p (bivreg, PATTERN (p)))
+ /* An insn that doesn't mention the biv is okay. */
+ ;
+ else if (p == prev_nonnote_insn (prev_nonnote_insn (loop_end))
+ || p == prev_nonnote_insn (loop_end))
{
/* If either of these insns uses the biv and sets a pseudo
that has more than one usage, then the biv has uses
break;
}
}
- else if (reg_mentioned_p (bivreg, PATTERN (p)))
+ else
{
no_use_except_counting = 0;
break;
}
}
-#ifdef HAVE_cc0
/* Never return CC0; return zero instead. */
- if (op0 == cc0_rtx)
+ if (CC0_P (op0))
return 0;
-#endif
return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
}
else
/* Replace the memory reference with the shadow register. */
replace_loop_mems (p, loop_info->mems[i].mem,
- loop_info->mems[i].reg);
+ loop_info->mems[i].reg, written);
}
if (GET_CODE (p) == CODE_LABEL
{
/* Now, we need to replace all references to the previous exit
label with the new one. */
- rtx_pair rr;
+ replace_label_data rr;
rr.r1 = end_label;
rr.r2 = label;
+ rr.update_label_nuses = true;
for (p = loop->start; p != loop->end; p = NEXT_INSN (p))
{
for_each_rtx (&p, replace_label, &rr);
-
- /* If this is a JUMP_INSN, then we also need to fix the JUMP_LABEL
- field. This is not handled by for_each_rtx because it doesn't
- handle unprinted ('0') fields. We need to update JUMP_LABEL
- because the immediately following unroll pass will use it.
- replace_label would not work anyways, because that only handles
- LABEL_REFs. */
- if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == end_label)
- JUMP_LABEL (p) = label;
}
}
}
}
+/* Worker function for find_mem_in_note, called via for_each_rtx. */
+
+static int
+find_mem_in_note_1 (x, data)
+ rtx *x;
+ void *data;
+{
+ if (*x != NULL_RTX && GET_CODE (*x) == MEM)
+ {
+ rtx *res = (rtx *) data;
+ *res = *x;
+ return 1;
+ }
+ return 0;
+}
+
+/* Returns the first MEM found in NOTE by depth-first search. */
+
+static rtx
+find_mem_in_note (note)
+ rtx note;
+{
+ if (note && for_each_rtx (¬e, find_mem_in_note_1, ¬e))
+ return note;
+ return NULL_RTX;
+}
+
/* Replace MEM with its associated pseudo register. This function is
called from load_mems via for_each_rtx. DATA is actually a pointer
to a structure describing the instruction currently being scanned
}
static void
-replace_loop_mems (insn, mem, reg)
+replace_loop_mems (insn, mem, reg, written)
rtx insn;
rtx mem;
rtx reg;
+ int written;
{
loop_replace_args args;
args.replacement = reg;
for_each_rtx (&insn, replace_loop_mem, &args);
+
+ /* If we hoist a mem write out of the loop, then REG_EQUAL
+ notes referring to the mem are no longer valid. */
+ if (written)
+ {
+ rtx note, sub;
+ rtx *link;
+
+ for (link = ®_NOTES (insn); (note = *link); link = &XEXP (note, 1))
+ {
+ if (REG_NOTE_KIND (note) == REG_EQUAL
+ && (sub = find_mem_in_note (note))
+ && true_dependence (mem, VOIDmode, sub, rtx_varies_p))
+ {
+ /* Remove the note. */
+ validate_change (NULL_RTX, link, XEXP (note, 1), 1);
+ break;
+ }
+ }
+ }
}
/* Replace one register with another. Called through for_each_rtx; PX points
for_each_rtx (&insn, replace_loop_reg, &args);
}
-
-/* Replace occurrences of the old exit label for the loop with the new
- one. DATA is an rtx_pair containing the old and new labels,
- respectively. */
-
-static int
-replace_label (x, data)
- rtx *x;
- void *data;
-{
- rtx l = *x;
- rtx old_label = ((rtx_pair *) data)->r1;
- rtx new_label = ((rtx_pair *) data)->r2;
-
- if (l == NULL_RTX)
- return 0;
-
- if (GET_CODE (l) != LABEL_REF)
- return 0;
-
- if (XEXP (l, 0) != old_label)
- return 0;
-
- XEXP (l, 0) = new_label;
- ++LABEL_NUSES (new_label);
- --LABEL_NUSES (old_label);
-
- return 0;
-}
\f
/* Emit insn for PATTERN after WHERE_INSN in basic block WHERE_BB
(ignored in the interim). */