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;
&& (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 psuedo, and subsitute a
+ 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
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. */
- if (REGNO (SET_DEST (set)) >= max_reg_before_loop)
+ 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) or if the source is
- already a reg (the motion will gain nothing). */
+ /* 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 || GET_CODE (SET_SRC (set)) == REG
+ && (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)))))
;
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:
" 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");
}
{
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);
&& 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;
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). */