/* 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;
/* 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;
}
}
+ 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 psuedo, and subsitute 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 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.
-
- 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. */
+ /* 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). */
+ else if (insert_temp
+ && (optimize_size || 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. */
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;
/* Out-of-range regs can occur when we are called from unrolling.
These have always been created by the unroller and are set in
- the loop, hence are never invariant. */
+ the loop, hence are never invariant. */
if (REGNO (x) >= (unsigned) regs->num)
return 0;
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);
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;
}
}
}
-#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);
}