/* Instruction scheduling pass. This file computes dependencies between
instructions.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by,
and currently maintained by, Jim Wilson (wilson@cygnus.com)
#include "sched-int.h"
#include "params.h"
#include "cselib.h"
+#include "df.h"
extern char *reg_known_equiv_p;
extern rtx *reg_known_value;
static regset reg_pending_sets;
static regset reg_pending_clobbers;
static regset reg_pending_uses;
-static bool reg_pending_barrier;
+
+/* The following enumeration values tell us what dependencies we
+ should use to implement the barrier. We use true-dependencies for
+ TRUE_BARRIER and anti-dependencies for MOVE_BARRIER. */
+enum reg_pending_barrier_mode
+{
+ NOT_A_BARRIER = 0,
+ MOVE_BARRIER,
+ TRUE_BARRIER
+};
+
+static enum reg_pending_barrier_mode reg_pending_barrier;
/* To speed up the test for duplicate dependency links we keep a
record of dependencies created by add_dependence when the average
static sbitmap *forward_dependency_cache;
#endif
-static int deps_may_trap_p PARAMS ((rtx));
-static void add_dependence_list PARAMS ((rtx, rtx, enum reg_note));
-static void add_dependence_list_and_free PARAMS ((rtx, rtx *, enum reg_note));
-static void set_sched_group_p PARAMS ((rtx));
+static int deps_may_trap_p (rtx);
+static void add_dependence_list (rtx, rtx, enum reg_note);
+static void add_dependence_list_and_free (rtx, rtx *, enum reg_note);
+static void set_sched_group_p (rtx);
-static void flush_pending_lists PARAMS ((struct deps *, rtx, int, int));
-static void sched_analyze_1 PARAMS ((struct deps *, rtx, rtx));
-static void sched_analyze_2 PARAMS ((struct deps *, rtx, rtx));
-static void sched_analyze_insn PARAMS ((struct deps *, rtx, rtx, rtx));
+static void flush_pending_lists (struct deps *, rtx, int, int);
+static void sched_analyze_1 (struct deps *, rtx, rtx);
+static void sched_analyze_2 (struct deps *, rtx, rtx);
+static void sched_analyze_insn (struct deps *, rtx, rtx, rtx);
-static rtx get_condition PARAMS ((rtx));
-static int conditions_mutex_p PARAMS ((rtx, rtx));
+static rtx get_condition (rtx);
+static int conditions_mutex_p (rtx, rtx);
\f
/* Return nonzero if a load of the memory reference MEM can cause a trap. */
static int
-deps_may_trap_p (mem)
- rtx mem;
+deps_may_trap_p (rtx mem)
{
rtx addr = XEXP (mem, 0);
if LIST does not contain INSN. */
rtx
-find_insn_list (insn, list)
- rtx insn;
- rtx list;
+find_insn_list (rtx insn, rtx list)
{
while (list)
{
/* Find the condition under which INSN is executed. */
static rtx
-get_condition (insn)
- rtx insn;
+get_condition (rtx insn)
{
rtx pat = PATTERN (insn);
rtx cond;
/* Return nonzero if conditions COND1 and COND2 can never be both true. */
static int
-conditions_mutex_p (cond1, cond2)
- rtx cond1, cond2;
+conditions_mutex_p (rtx cond1, rtx cond2)
{
if (GET_RTX_CLASS (GET_CODE (cond1)) == '<'
&& GET_RTX_CLASS (GET_CODE (cond2)) == '<'
nonzero if a new entry has been added to insn's LOG_LINK. */
int
-add_dependence (insn, elem, dep_type)
- rtx insn;
- rtx elem;
- enum reg_note dep_type;
+add_dependence (rtx insn, rtx elem, enum reg_note dep_type)
{
rtx link;
int present_p;
abort ();
}
#endif
-
+
/* If this is a more restrictive type of dependence than the existing
one, then change the existing dependence to this type. */
if ((int) dep_type < (int) REG_NOTE_KIND (link))
/* A convenience wrapper to operate on an entire list. */
static void
-add_dependence_list (insn, list, dep_type)
- rtx insn, list;
- enum reg_note dep_type;
+add_dependence_list (rtx insn, rtx list, enum reg_note dep_type)
{
for (; list; list = XEXP (list, 1))
add_dependence (insn, XEXP (list, 0), dep_type);
/* Similar, but free *LISTP at the same time. */
static void
-add_dependence_list_and_free (insn, listp, dep_type)
- rtx insn;
- rtx *listp;
- enum reg_note dep_type;
+add_dependence_list_and_free (rtx insn, rtx *listp, enum reg_note dep_type)
{
rtx list, next;
for (list = *listp, *listp = NULL; list ; list = next)
goes along with that. */
static void
-set_sched_group_p (insn)
- rtx insn;
+set_sched_group_p (rtx insn)
{
rtx prev;
so that we can do memory aliasing on it. */
void
-add_insn_mem_dependence (deps, insn_list, mem_list, insn, mem)
- struct deps *deps;
- rtx *insn_list, *mem_list, insn, mem;
+add_insn_mem_dependence (struct deps *deps, rtx *insn_list, rtx *mem_list,
+ rtx insn, rtx mem)
{
rtx link;
dependencies for a read operation, similarly with FOR_WRITE. */
static void
-flush_pending_lists (deps, insn, for_read, for_write)
- struct deps *deps;
- rtx insn;
- int for_read, for_write;
+flush_pending_lists (struct deps *deps, rtx insn, int for_read,
+ int for_write)
{
if (for_write)
{
destination of X, and reads of everything mentioned. */
static void
-sched_analyze_1 (deps, x, insn)
- struct deps *deps;
- rtx x;
- rtx insn;
+sched_analyze_1 (struct deps *deps, rtx x, rtx insn)
{
int regno;
rtx dest = XEXP (x, 0);
while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
|| GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
+ if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || read_modify_subreg_p (dest))
+ {
+ /* These both read and modify the result. We must handle
+ them as writes to get proper dependencies for following
+ instructions. We must handle them as reads to get proper
+ dependencies from this to previous instructions.
+ Thus we need to call sched_analyze_2. */
+
+ sched_analyze_2 (deps, XEXP (dest, 0), insn);
+ }
if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
{
/* The second and third arguments are values read by this insn. */
/* Analyze the uses of memory and registers in rtx X in INSN. */
static void
-sched_analyze_2 (deps, x, insn)
- struct deps *deps;
- rtx x;
- rtx insn;
+sched_analyze_2 (struct deps *deps, rtx x, rtx insn)
{
int i;
int j;
mode. An insn should not be moved across this even if it only uses
pseudo-regs because it might give an incorrectly rounded result. */
if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
- reg_pending_barrier = true;
+ reg_pending_barrier = TRUE_BARRIER;
/* For all ASM_OPERANDS, we must traverse the vector of input operands.
We can not just fall through here since then we would be confused
/* Analyze an INSN with pattern X to find all dependencies. */
static void
-sched_analyze_insn (deps, x, insn, loop_notes)
- struct deps *deps;
- rtx x, insn;
- rtx loop_notes;
+sched_analyze_insn (struct deps *deps, rtx x, rtx insn, rtx loop_notes)
{
RTX_CODE code = GET_CODE (x);
rtx link;
sched_analyze_2 (deps, XEXP (link, 0), insn);
}
if (find_reg_note (insn, REG_SETJMP, NULL))
- reg_pending_barrier = true;
+ reg_pending_barrier = MOVE_BARRIER;
}
if (GET_CODE (insn) == JUMP_INSN)
rtx next;
next = next_nonnote_insn (insn);
if (next && GET_CODE (next) == BARRIER)
- reg_pending_barrier = true;
+ reg_pending_barrier = TRUE_BARRIER;
else
{
rtx pending, pending_mem;
- regset_head tmp;
- INIT_REG_SET (&tmp);
+ regset_head tmp_uses, tmp_sets;
+ INIT_REG_SET (&tmp_uses);
+ INIT_REG_SET (&tmp_sets);
- (*current_sched_info->compute_jump_reg_dependencies) (insn, &tmp);
+ (*current_sched_info->compute_jump_reg_dependencies)
+ (insn, &deps->reg_conditional_sets, &tmp_uses, &tmp_sets);
/* Make latency of jump equal to 0 by using anti-dependence. */
- EXECUTE_IF_SET_IN_REG_SET (&tmp, 0, i,
+ EXECUTE_IF_SET_IN_REG_SET (&tmp_uses, 0, i,
{
struct deps_reg *reg_last = &deps->reg_last[i];
add_dependence_list (insn, reg_last->sets, REG_DEP_ANTI);
reg_last->uses_length++;
reg_last->uses = alloc_INSN_LIST (insn, reg_last->uses);
});
- CLEAR_REG_SET (&tmp);
+ IOR_REG_SET (reg_pending_sets, &tmp_sets);
+
+ CLEAR_REG_SET (&tmp_uses);
+ CLEAR_REG_SET (&tmp_sets);
/* All memory writes and volatile reads must happen before the
jump. Non-volatile reads must happen before the jump iff
|| INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_END
|| INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_BEG
|| INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_END)
- reg_pending_barrier = true;
+ reg_pending_barrier = MOVE_BARRIER;
link = XEXP (link, 1);
}
where block boundaries fall. This is mighty confusing elsewhere.
Therefore, prevent such an instruction from being moved. */
if (can_throw_internal (insn))
- reg_pending_barrier = true;
+ reg_pending_barrier = MOVE_BARRIER;
/* Add dependencies if a scheduling barrier was found. */
if (reg_pending_barrier)
{
struct deps_reg *reg_last = &deps->reg_last[i];
add_dependence_list (insn, reg_last->uses, REG_DEP_ANTI);
- add_dependence_list (insn, reg_last->sets, REG_DEP_ANTI);
- add_dependence_list (insn, reg_last->clobbers, REG_DEP_ANTI);
+ add_dependence_list
+ (insn, reg_last->sets,
+ reg_pending_barrier == TRUE_BARRIER ? 0 : REG_DEP_ANTI);
+ add_dependence_list
+ (insn, reg_last->clobbers,
+ reg_pending_barrier == TRUE_BARRIER ? 0 : REG_DEP_ANTI);
});
}
else
struct deps_reg *reg_last = &deps->reg_last[i];
add_dependence_list_and_free (insn, ®_last->uses,
REG_DEP_ANTI);
- add_dependence_list_and_free (insn, ®_last->sets,
- REG_DEP_ANTI);
- add_dependence_list_and_free (insn, ®_last->clobbers,
- REG_DEP_ANTI);
+ add_dependence_list_and_free
+ (insn, ®_last->sets,
+ reg_pending_barrier == TRUE_BARRIER ? 0 : REG_DEP_ANTI);
+ add_dependence_list_and_free
+ (insn, ®_last->clobbers,
+ reg_pending_barrier == TRUE_BARRIER ? 0 : REG_DEP_ANTI);
reg_last->uses_length = 0;
reg_last->clobbers_length = 0;
});
}
flush_pending_lists (deps, insn, true, true);
- reg_pending_barrier = false;
+ CLEAR_REG_SET (&deps->reg_conditional_sets);
+ reg_pending_barrier = NOT_A_BARRIER;
}
else
{
add_dependence_list (insn, reg_last->clobbers, REG_DEP_OUTPUT);
add_dependence_list (insn, reg_last->uses, REG_DEP_ANTI);
reg_last->sets = alloc_INSN_LIST (insn, reg_last->sets);
+ SET_REGNO_REG_SET (&deps->reg_conditional_sets, i);
});
}
else
reg_last->sets = alloc_INSN_LIST (insn, reg_last->sets);
reg_last->uses_length = 0;
reg_last->clobbers_length = 0;
+ CLEAR_REGNO_REG_SET (&deps->reg_conditional_sets, i);
});
}
for every dependency. */
void
-sched_analyze (deps, head, tail)
- struct deps *deps;
- rtx head, tail;
+sched_analyze (struct deps *deps, rtx head, rtx tail)
{
rtx insn;
rtx loop_notes = 0;
{
/* This is setjmp. Assume that all registers, not just
hard registers, may be clobbered by this call. */
- reg_pending_barrier = true;
+ reg_pending_barrier = MOVE_BARRIER;
}
else
{
/* Now that we have completed handling INSN, check and see if it is
a CLOBBER beginning a libcall block. If it is, record the
- end of the libcall sequence.
+ end of the libcall sequence.
We want to schedule libcall blocks as a unit before reload. While
this restricts scheduling, it preserves the meaning of a libcall
a libcall block. */
if (!reload_completed
/* Note we may have nested libcall sequences. We only care about
- the outermost libcall sequence. */
+ the outermost libcall sequence. */
&& deps->libcall_block_tail_insn == 0
/* The sequence must start with a clobber of a register. */
&& GET_CODE (insn) == INSN
given DEP_TYPE. The forward dependence should be not exist before. */
void
-add_forward_dependence (from, to, dep_type)
- rtx from;
- rtx to;
- enum reg_note dep_type;
+add_forward_dependence (rtx from, rtx to, enum reg_note dep_type)
{
rtx new_link;
/* If add_dependence is working properly there should never
be notes, deleted insns or duplicates in the backward
links. Thus we need not check for them here.
-
+
However, if we have enabled checking we might as well go
ahead and verify that add_dependence worked properly. */
if (GET_CODE (from) == NOTE
SET_BIT (forward_dependency_cache[INSN_LUID (from)],
INSN_LUID (to));
#endif
-
+
new_link = alloc_INSN_LIST (to, INSN_DEPEND (from));
-
+
PUT_REG_NOTE_KIND (new_link, dep_type);
-
+
INSN_DEPEND (from) = new_link;
INSN_DEP_COUNT (to) += 1;
}
INSN_DEPEND. */
void
-compute_forward_dependences (head, tail)
- rtx head, tail;
+compute_forward_dependences (rtx head, rtx tail)
{
rtx insn, link;
rtx next_tail;
n_bbs is the number of region blocks. */
void
-init_deps (deps)
- struct deps *deps;
+init_deps (struct deps *deps)
{
int max_reg = (reload_completed ? FIRST_PSEUDO_REGISTER : max_reg_num ());
deps->max_reg = max_reg;
- deps->reg_last = (struct deps_reg *)
- xcalloc (max_reg, sizeof (struct deps_reg));
+ deps->reg_last = xcalloc (max_reg, sizeof (struct deps_reg));
INIT_REG_SET (&deps->reg_last_in_use);
+ INIT_REG_SET (&deps->reg_conditional_sets);
deps->pending_read_insns = 0;
deps->pending_read_mems = 0;
/* Free insn lists found in DEPS. */
void
-free_deps (deps)
- struct deps *deps;
+free_deps (struct deps *deps)
{
int i;
free_INSN_LIST_list (®_last->clobbers);
});
CLEAR_REG_SET (&deps->reg_last_in_use);
+ CLEAR_REG_SET (&deps->reg_conditional_sets);
free (deps->reg_last);
}
it is used in the estimate of profitability. */
void
-init_dependency_caches (luid)
- int luid;
+init_dependency_caches (int luid)
{
/* ?!? We could save some memory by computing a per-region luid mapping
which could reduce both the number of vectors in the cache and the size
/* Free the caches allocated in init_dependency_caches. */
void
-free_dependency_caches ()
+free_dependency_caches (void)
{
if (true_dependency_cache)
{
code. */
void
-init_deps_global ()
+init_deps_global (void)
{
reg_pending_sets = INITIALIZE_REG_SET (reg_pending_sets_head);
reg_pending_clobbers = INITIALIZE_REG_SET (reg_pending_clobbers_head);
reg_pending_uses = INITIALIZE_REG_SET (reg_pending_uses_head);
- reg_pending_barrier = false;
+ reg_pending_barrier = NOT_A_BARRIER;
}
/* Free everything used by the dependency analysis code. */
void
-finish_deps_global ()
+finish_deps_global (void)
{
FREE_REG_SET (reg_pending_sets);
FREE_REG_SET (reg_pending_clobbers);