aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/gcc/sel-sched-ir.c
diff options
context:
space:
mode:
authorBen Cheng <bccheng@google.com>2014-03-25 22:37:19 -0700
committerBen Cheng <bccheng@google.com>2014-03-25 22:37:19 -0700
commit1bc5aee63eb72b341f506ad058502cd0361f0d10 (patch)
treec607e8252f3405424ff15bc2d00aa38dadbb2518 /gcc-4.9/gcc/sel-sched-ir.c
parent283a0bf58fcf333c58a2a92c3ebbc41fb9eb1fdb (diff)
downloadtoolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.gz
toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.bz2
toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.zip
Initial checkin of GCC 4.9.0 from trunk (r208799).
Change-Id: I48a3c08bb98542aa215912a75f03c0890e497dba
Diffstat (limited to 'gcc-4.9/gcc/sel-sched-ir.c')
-rw-r--r--gcc-4.9/gcc/sel-sched-ir.c6425
1 files changed, 6425 insertions, 0 deletions
diff --git a/gcc-4.9/gcc/sel-sched-ir.c b/gcc-4.9/gcc/sel-sched-ir.c
new file mode 100644
index 000000000..f5a4ee035
--- /dev/null
+++ b/gcc-4.9/gcc/sel-sched-ir.c
@@ -0,0 +1,6425 @@
+/* Instruction scheduling pass. Selective scheduler and pipeliner.
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "diagnostic-core.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "regs.h"
+#include "function.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "except.h"
+#include "recog.h"
+#include "params.h"
+#include "target.h"
+#include "sched-int.h"
+#include "ggc.h"
+#include "tree.h"
+#include "vec.h"
+#include "langhooks.h"
+#include "rtlhooks-def.h"
+#include "emit-rtl.h" /* FIXME: Can go away once crtl is moved to rtl.h. */
+
+#ifdef INSN_SCHEDULING
+#include "sel-sched-ir.h"
+/* We don't have to use it except for sel_print_insn. */
+#include "sel-sched-dump.h"
+
+/* A vector holding bb info for whole scheduling pass. */
+vec<sel_global_bb_info_def>
+ sel_global_bb_info = vNULL;
+
+/* A vector holding bb info. */
+vec<sel_region_bb_info_def>
+ sel_region_bb_info = vNULL;
+
+/* A pool for allocating all lists. */
+alloc_pool sched_lists_pool;
+
+/* This contains information about successors for compute_av_set. */
+struct succs_info current_succs;
+
+/* Data structure to describe interaction with the generic scheduler utils. */
+static struct common_sched_info_def sel_common_sched_info;
+
+/* The loop nest being pipelined. */
+struct loop *current_loop_nest;
+
+/* LOOP_NESTS is a vector containing the corresponding loop nest for
+ each region. */
+static vec<loop_p> loop_nests = vNULL;
+
+/* Saves blocks already in loop regions, indexed by bb->index. */
+static sbitmap bbs_in_loop_rgns = NULL;
+
+/* CFG hooks that are saved before changing create_basic_block hook. */
+static struct cfg_hooks orig_cfg_hooks;
+
+
+/* Array containing reverse topological index of function basic blocks,
+ indexed by BB->INDEX. */
+static int *rev_top_order_index = NULL;
+
+/* Length of the above array. */
+static int rev_top_order_index_len = -1;
+
+/* A regset pool structure. */
+static struct
+{
+ /* The stack to which regsets are returned. */
+ regset *v;
+
+ /* Its pointer. */
+ int n;
+
+ /* Its size. */
+ int s;
+
+ /* In VV we save all generated regsets so that, when destructing the
+ pool, we can compare it with V and check that every regset was returned
+ back to pool. */
+ regset *vv;
+
+ /* The pointer of VV stack. */
+ int nn;
+
+ /* Its size. */
+ int ss;
+
+ /* The difference between allocated and returned regsets. */
+ int diff;
+} regset_pool = { NULL, 0, 0, NULL, 0, 0, 0 };
+
+/* This represents the nop pool. */
+static struct
+{
+ /* The vector which holds previously emitted nops. */
+ insn_t *v;
+
+ /* Its pointer. */
+ int n;
+
+ /* Its size. */
+ int s;
+} nop_pool = { NULL, 0, 0 };
+
+/* The pool for basic block notes. */
+static rtx_vec_t bb_note_pool;
+
+/* A NOP pattern used to emit placeholder insns. */
+rtx nop_pattern = NULL_RTX;
+/* A special instruction that resides in EXIT_BLOCK.
+ EXIT_INSN is successor of the insns that lead to EXIT_BLOCK. */
+rtx exit_insn = NULL_RTX;
+
+/* TRUE if while scheduling current region, which is loop, its preheader
+ was removed. */
+bool preheader_removed = false;
+
+
+/* Forward static declarations. */
+static void fence_clear (fence_t);
+
+static void deps_init_id (idata_t, insn_t, bool);
+static void init_id_from_df (idata_t, insn_t, bool);
+static expr_t set_insn_init (expr_t, vinsn_t, int);
+
+static void cfg_preds (basic_block, insn_t **, int *);
+static void prepare_insn_expr (insn_t, int);
+static void free_history_vect (vec<expr_history_def> &);
+
+static void move_bb_info (basic_block, basic_block);
+static void remove_empty_bb (basic_block, bool);
+static void sel_merge_blocks (basic_block, basic_block);
+static void sel_remove_loop_preheader (void);
+static bool bb_has_removable_jump_to_p (basic_block, basic_block);
+
+static bool insn_is_the_only_one_in_bb_p (insn_t);
+static void create_initial_data_sets (basic_block);
+
+static void free_av_set (basic_block);
+static void invalidate_av_set (basic_block);
+static void extend_insn_data (void);
+static void sel_init_new_insn (insn_t, int);
+static void finish_insns (void);
+
+/* Various list functions. */
+
+/* Copy an instruction list L. */
+ilist_t
+ilist_copy (ilist_t l)
+{
+ ilist_t head = NULL, *tailp = &head;
+
+ while (l)
+ {
+ ilist_add (tailp, ILIST_INSN (l));
+ tailp = &ILIST_NEXT (*tailp);
+ l = ILIST_NEXT (l);
+ }
+
+ return head;
+}
+
+/* Invert an instruction list L. */
+ilist_t
+ilist_invert (ilist_t l)
+{
+ ilist_t res = NULL;
+
+ while (l)
+ {
+ ilist_add (&res, ILIST_INSN (l));
+ l = ILIST_NEXT (l);
+ }
+
+ return res;
+}
+
+/* Add a new boundary to the LP list with parameters TO, PTR, and DC. */
+void
+blist_add (blist_t *lp, insn_t to, ilist_t ptr, deps_t dc)
+{
+ bnd_t bnd;
+
+ _list_add (lp);
+ bnd = BLIST_BND (*lp);
+
+ BND_TO (bnd) = to;
+ BND_PTR (bnd) = ptr;
+ BND_AV (bnd) = NULL;
+ BND_AV1 (bnd) = NULL;
+ BND_DC (bnd) = dc;
+}
+
+/* Remove the list note pointed to by LP. */
+void
+blist_remove (blist_t *lp)
+{
+ bnd_t b = BLIST_BND (*lp);
+
+ av_set_clear (&BND_AV (b));
+ av_set_clear (&BND_AV1 (b));
+ ilist_clear (&BND_PTR (b));
+
+ _list_remove (lp);
+}
+
+/* Init a fence tail L. */
+void
+flist_tail_init (flist_tail_t l)
+{
+ FLIST_TAIL_HEAD (l) = NULL;
+ FLIST_TAIL_TAILP (l) = &FLIST_TAIL_HEAD (l);
+}
+
+/* Try to find fence corresponding to INSN in L. */
+fence_t
+flist_lookup (flist_t l, insn_t insn)
+{
+ while (l)
+ {
+ if (FENCE_INSN (FLIST_FENCE (l)) == insn)
+ return FLIST_FENCE (l);
+
+ l = FLIST_NEXT (l);
+ }
+
+ return NULL;
+}
+
+/* Init the fields of F before running fill_insns. */
+static void
+init_fence_for_scheduling (fence_t f)
+{
+ FENCE_BNDS (f) = NULL;
+ FENCE_PROCESSED_P (f) = false;
+ FENCE_SCHEDULED_P (f) = false;
+}
+
+/* Add new fence consisting of INSN and STATE to the list pointed to by LP. */
+static void
+flist_add (flist_t *lp, insn_t insn, state_t state, deps_t dc, void *tc,
+ insn_t last_scheduled_insn, vec<rtx, va_gc> *executing_insns,
+ int *ready_ticks, int ready_ticks_size, insn_t sched_next,
+ int cycle, int cycle_issued_insns, int issue_more,
+ bool starts_cycle_p, bool after_stall_p)
+{
+ fence_t f;
+
+ _list_add (lp);
+ f = FLIST_FENCE (*lp);
+
+ FENCE_INSN (f) = insn;
+
+ gcc_assert (state != NULL);
+ FENCE_STATE (f) = state;
+
+ FENCE_CYCLE (f) = cycle;
+ FENCE_ISSUED_INSNS (f) = cycle_issued_insns;
+ FENCE_STARTS_CYCLE_P (f) = starts_cycle_p;
+ FENCE_AFTER_STALL_P (f) = after_stall_p;
+
+ gcc_assert (dc != NULL);
+ FENCE_DC (f) = dc;
+
+ gcc_assert (tc != NULL || targetm.sched.alloc_sched_context == NULL);
+ FENCE_TC (f) = tc;
+
+ FENCE_LAST_SCHEDULED_INSN (f) = last_scheduled_insn;
+ FENCE_ISSUE_MORE (f) = issue_more;
+ FENCE_EXECUTING_INSNS (f) = executing_insns;
+ FENCE_READY_TICKS (f) = ready_ticks;
+ FENCE_READY_TICKS_SIZE (f) = ready_ticks_size;
+ FENCE_SCHED_NEXT (f) = sched_next;
+
+ init_fence_for_scheduling (f);
+}
+
+/* Remove the head node of the list pointed to by LP. */
+static void
+flist_remove (flist_t *lp)
+{
+ if (FENCE_INSN (FLIST_FENCE (*lp)))
+ fence_clear (FLIST_FENCE (*lp));
+ _list_remove (lp);
+}
+
+/* Clear the fence list pointed to by LP. */
+void
+flist_clear (flist_t *lp)
+{
+ while (*lp)
+ flist_remove (lp);
+}
+
+/* Add ORIGINAL_INSN the def list DL honoring CROSSES_CALL. */
+void
+def_list_add (def_list_t *dl, insn_t original_insn, bool crosses_call)
+{
+ def_t d;
+
+ _list_add (dl);
+ d = DEF_LIST_DEF (*dl);
+
+ d->orig_insn = original_insn;
+ d->crosses_call = crosses_call;
+}
+
+
+/* Functions to work with target contexts. */
+
+/* Bulk target context. It is convenient for debugging purposes to ensure
+ that there are no uninitialized (null) target contexts. */
+static tc_t bulk_tc = (tc_t) 1;
+
+/* Target hooks wrappers. In the future we can provide some default
+ implementations for them. */
+
+/* Allocate a store for the target context. */
+static tc_t
+alloc_target_context (void)
+{
+ return (targetm.sched.alloc_sched_context
+ ? targetm.sched.alloc_sched_context () : bulk_tc);
+}
+
+/* Init target context TC.
+ If CLEAN_P is true, then make TC as it is beginning of the scheduler.
+ Overwise, copy current backend context to TC. */
+static void
+init_target_context (tc_t tc, bool clean_p)
+{
+ if (targetm.sched.init_sched_context)
+ targetm.sched.init_sched_context (tc, clean_p);
+}
+
+/* Allocate and initialize a target context. Meaning of CLEAN_P is the same as
+ int init_target_context (). */
+tc_t
+create_target_context (bool clean_p)
+{
+ tc_t tc = alloc_target_context ();
+
+ init_target_context (tc, clean_p);
+ return tc;
+}
+
+/* Copy TC to the current backend context. */
+void
+set_target_context (tc_t tc)
+{
+ if (targetm.sched.set_sched_context)
+ targetm.sched.set_sched_context (tc);
+}
+
+/* TC is about to be destroyed. Free any internal data. */
+static void
+clear_target_context (tc_t tc)
+{
+ if (targetm.sched.clear_sched_context)
+ targetm.sched.clear_sched_context (tc);
+}
+
+/* Clear and free it. */
+static void
+delete_target_context (tc_t tc)
+{
+ clear_target_context (tc);
+
+ if (targetm.sched.free_sched_context)
+ targetm.sched.free_sched_context (tc);
+}
+
+/* Make a copy of FROM in TO.
+ NB: May be this should be a hook. */
+static void
+copy_target_context (tc_t to, tc_t from)
+{
+ tc_t tmp = create_target_context (false);
+
+ set_target_context (from);
+ init_target_context (to, false);
+
+ set_target_context (tmp);
+ delete_target_context (tmp);
+}
+
+/* Create a copy of TC. */
+static tc_t
+create_copy_of_target_context (tc_t tc)
+{
+ tc_t copy = alloc_target_context ();
+
+ copy_target_context (copy, tc);
+
+ return copy;
+}
+
+/* Clear TC and initialize it according to CLEAN_P. The meaning of CLEAN_P
+ is the same as in init_target_context (). */
+void
+reset_target_context (tc_t tc, bool clean_p)
+{
+ clear_target_context (tc);
+ init_target_context (tc, clean_p);
+}
+
+/* Functions to work with dependence contexts.
+ Dc (aka deps context, aka deps_t, aka struct deps_desc *) is short for dependence
+ context. It accumulates information about processed insns to decide if
+ current insn is dependent on the processed ones. */
+
+/* Make a copy of FROM in TO. */
+static void
+copy_deps_context (deps_t to, deps_t from)
+{
+ init_deps (to, false);
+ deps_join (to, from);
+}
+
+/* Allocate store for dep context. */
+static deps_t
+alloc_deps_context (void)
+{
+ return XNEW (struct deps_desc);
+}
+
+/* Allocate and initialize dep context. */
+static deps_t
+create_deps_context (void)
+{
+ deps_t dc = alloc_deps_context ();
+
+ init_deps (dc, false);
+ return dc;
+}
+
+/* Create a copy of FROM. */
+static deps_t
+create_copy_of_deps_context (deps_t from)
+{
+ deps_t to = alloc_deps_context ();
+
+ copy_deps_context (to, from);
+ return to;
+}
+
+/* Clean up internal data of DC. */
+static void
+clear_deps_context (deps_t dc)
+{
+ free_deps (dc);
+}
+
+/* Clear and free DC. */
+static void
+delete_deps_context (deps_t dc)
+{
+ clear_deps_context (dc);
+ free (dc);
+}
+
+/* Clear and init DC. */
+static void
+reset_deps_context (deps_t dc)
+{
+ clear_deps_context (dc);
+ init_deps (dc, false);
+}
+
+/* This structure describes the dependence analysis hooks for advancing
+ dependence context. */
+static struct sched_deps_info_def advance_deps_context_sched_deps_info =
+ {
+ NULL,
+
+ NULL, /* start_insn */
+ NULL, /* finish_insn */
+ NULL, /* start_lhs */
+ NULL, /* finish_lhs */
+ NULL, /* start_rhs */
+ NULL, /* finish_rhs */
+ haifa_note_reg_set,
+ haifa_note_reg_clobber,
+ haifa_note_reg_use,
+ NULL, /* note_mem_dep */
+ NULL, /* note_dep */
+
+ 0, 0, 0
+ };
+
+/* Process INSN and add its impact on DC. */
+void
+advance_deps_context (deps_t dc, insn_t insn)
+{
+ sched_deps_info = &advance_deps_context_sched_deps_info;
+ deps_analyze_insn (dc, insn);
+}
+
+
+/* Functions to work with DFA states. */
+
+/* Allocate store for a DFA state. */
+static state_t
+state_alloc (void)
+{
+ return xmalloc (dfa_state_size);
+}
+
+/* Allocate and initialize DFA state. */
+static state_t
+state_create (void)
+{
+ state_t state = state_alloc ();
+
+ state_reset (state);
+ advance_state (state);
+ return state;
+}
+
+/* Free DFA state. */
+static void
+state_free (state_t state)
+{
+ free (state);
+}
+
+/* Make a copy of FROM in TO. */
+static void
+state_copy (state_t to, state_t from)
+{
+ memcpy (to, from, dfa_state_size);
+}
+
+/* Create a copy of FROM. */
+static state_t
+state_create_copy (state_t from)
+{
+ state_t to = state_alloc ();
+
+ state_copy (to, from);
+ return to;
+}
+
+
+/* Functions to work with fences. */
+
+/* Clear the fence. */
+static void
+fence_clear (fence_t f)
+{
+ state_t s = FENCE_STATE (f);
+ deps_t dc = FENCE_DC (f);
+ void *tc = FENCE_TC (f);
+
+ ilist_clear (&FENCE_BNDS (f));
+
+ gcc_assert ((s != NULL && dc != NULL && tc != NULL)
+ || (s == NULL && dc == NULL && tc == NULL));
+
+ free (s);
+
+ if (dc != NULL)
+ delete_deps_context (dc);
+
+ if (tc != NULL)
+ delete_target_context (tc);
+ vec_free (FENCE_EXECUTING_INSNS (f));
+ free (FENCE_READY_TICKS (f));
+ FENCE_READY_TICKS (f) = NULL;
+}
+
+/* Init a list of fences with successors of OLD_FENCE. */
+void
+init_fences (insn_t old_fence)
+{
+ insn_t succ;
+ succ_iterator si;
+ bool first = true;
+ int ready_ticks_size = get_max_uid () + 1;
+
+ FOR_EACH_SUCC_1 (succ, si, old_fence,
+ SUCCS_NORMAL | SUCCS_SKIP_TO_LOOP_EXITS)
+ {
+
+ if (first)
+ first = false;
+ else
+ gcc_assert (flag_sel_sched_pipelining_outer_loops);
+
+ flist_add (&fences, succ,
+ state_create (),
+ create_deps_context () /* dc */,
+ create_target_context (true) /* tc */,
+ NULL_RTX /* last_scheduled_insn */,
+ NULL, /* executing_insns */
+ XCNEWVEC (int, ready_ticks_size), /* ready_ticks */
+ ready_ticks_size,
+ NULL_RTX /* sched_next */,
+ 1 /* cycle */, 0 /* cycle_issued_insns */,
+ issue_rate, /* issue_more */
+ 1 /* starts_cycle_p */, 0 /* after_stall_p */);
+ }
+}
+
+/* Merges two fences (filling fields of fence F with resulting values) by
+ following rules: 1) state, target context and last scheduled insn are
+ propagated from fallthrough edge if it is available;
+ 2) deps context and cycle is propagated from more probable edge;
+ 3) all other fields are set to corresponding constant values.
+
+ INSN, STATE, DC, TC, LAST_SCHEDULED_INSN, EXECUTING_INSNS,
+ READY_TICKS, READY_TICKS_SIZE, SCHED_NEXT, CYCLE, ISSUE_MORE
+ and AFTER_STALL_P are the corresponding fields of the second fence. */
+static void
+merge_fences (fence_t f, insn_t insn,
+ state_t state, deps_t dc, void *tc,
+ rtx last_scheduled_insn, vec<rtx, va_gc> *executing_insns,
+ int *ready_ticks, int ready_ticks_size,
+ rtx sched_next, int cycle, int issue_more, bool after_stall_p)
+{
+ insn_t last_scheduled_insn_old = FENCE_LAST_SCHEDULED_INSN (f);
+
+ gcc_assert (sel_bb_head_p (FENCE_INSN (f))
+ && !sched_next && !FENCE_SCHED_NEXT (f));
+
+ /* Check if we can decide which path fences came.
+ If we can't (or don't want to) - reset all. */
+ if (last_scheduled_insn == NULL
+ || last_scheduled_insn_old == NULL
+ /* This is a case when INSN is reachable on several paths from
+ one insn (this can happen when pipelining of outer loops is on and
+ there are two edges: one going around of inner loop and the other -
+ right through it; in such case just reset everything). */
+ || last_scheduled_insn == last_scheduled_insn_old)
+ {
+ state_reset (FENCE_STATE (f));
+ state_free (state);
+
+ reset_deps_context (FENCE_DC (f));
+ delete_deps_context (dc);
+
+ reset_target_context (FENCE_TC (f), true);
+ delete_target_context (tc);
+
+ if (cycle > FENCE_CYCLE (f))
+ FENCE_CYCLE (f) = cycle;
+
+ FENCE_LAST_SCHEDULED_INSN (f) = NULL;
+ FENCE_ISSUE_MORE (f) = issue_rate;
+ vec_free (executing_insns);
+ free (ready_ticks);
+ if (FENCE_EXECUTING_INSNS (f))
+ FENCE_EXECUTING_INSNS (f)->block_remove (0,
+ FENCE_EXECUTING_INSNS (f)->length ());
+ if (FENCE_READY_TICKS (f))
+ memset (FENCE_READY_TICKS (f), 0, FENCE_READY_TICKS_SIZE (f));
+ }
+ else
+ {
+ edge edge_old = NULL, edge_new = NULL;
+ edge candidate;
+ succ_iterator si;
+ insn_t succ;
+
+ /* Find fallthrough edge. */
+ gcc_assert (BLOCK_FOR_INSN (insn)->prev_bb);
+ candidate = find_fallthru_edge_from (BLOCK_FOR_INSN (insn)->prev_bb);
+
+ if (!candidate
+ || (candidate->src != BLOCK_FOR_INSN (last_scheduled_insn)
+ && candidate->src != BLOCK_FOR_INSN (last_scheduled_insn_old)))
+ {
+ /* No fallthrough edge leading to basic block of INSN. */
+ state_reset (FENCE_STATE (f));
+ state_free (state);
+
+ reset_target_context (FENCE_TC (f), true);
+ delete_target_context (tc);
+
+ FENCE_LAST_SCHEDULED_INSN (f) = NULL;
+ FENCE_ISSUE_MORE (f) = issue_rate;
+ }
+ else
+ if (candidate->src == BLOCK_FOR_INSN (last_scheduled_insn))
+ {
+ /* Would be weird if same insn is successor of several fallthrough
+ edges. */
+ gcc_assert (BLOCK_FOR_INSN (insn)->prev_bb
+ != BLOCK_FOR_INSN (last_scheduled_insn_old));
+
+ state_free (FENCE_STATE (f));
+ FENCE_STATE (f) = state;
+
+ delete_target_context (FENCE_TC (f));
+ FENCE_TC (f) = tc;
+
+ FENCE_LAST_SCHEDULED_INSN (f) = last_scheduled_insn;
+ FENCE_ISSUE_MORE (f) = issue_more;
+ }
+ else
+ {
+ /* Leave STATE, TC and LAST_SCHEDULED_INSN fields untouched. */
+ state_free (state);
+ delete_target_context (tc);
+
+ gcc_assert (BLOCK_FOR_INSN (insn)->prev_bb
+ != BLOCK_FOR_INSN (last_scheduled_insn));
+ }
+
+ /* Find edge of first predecessor (last_scheduled_insn_old->insn). */
+ FOR_EACH_SUCC_1 (succ, si, last_scheduled_insn_old,
+ SUCCS_NORMAL | SUCCS_SKIP_TO_LOOP_EXITS)
+ {
+ if (succ == insn)
+ {
+ /* No same successor allowed from several edges. */
+ gcc_assert (!edge_old);
+ edge_old = si.e1;
+ }
+ }
+ /* Find edge of second predecessor (last_scheduled_insn->insn). */
+ FOR_EACH_SUCC_1 (succ, si, last_scheduled_insn,
+ SUCCS_NORMAL | SUCCS_SKIP_TO_LOOP_EXITS)
+ {
+ if (succ == insn)
+ {
+ /* No same successor allowed from several edges. */
+ gcc_assert (!edge_new);
+ edge_new = si.e1;
+ }
+ }
+
+ /* Check if we can choose most probable predecessor. */
+ if (edge_old == NULL || edge_new == NULL)
+ {
+ reset_deps_context (FENCE_DC (f));
+ delete_deps_context (dc);
+ vec_free (executing_insns);
+ free (ready_ticks);
+
+ FENCE_CYCLE (f) = MAX (FENCE_CYCLE (f), cycle);
+ if (FENCE_EXECUTING_INSNS (f))
+ FENCE_EXECUTING_INSNS (f)->block_remove (0,
+ FENCE_EXECUTING_INSNS (f)->length ());
+ if (FENCE_READY_TICKS (f))
+ memset (FENCE_READY_TICKS (f), 0, FENCE_READY_TICKS_SIZE (f));
+ }
+ else
+ if (edge_new->probability > edge_old->probability)
+ {
+ delete_deps_context (FENCE_DC (f));
+ FENCE_DC (f) = dc;
+ vec_free (FENCE_EXECUTING_INSNS (f));
+ FENCE_EXECUTING_INSNS (f) = executing_insns;
+ free (FENCE_READY_TICKS (f));
+ FENCE_READY_TICKS (f) = ready_ticks;
+ FENCE_READY_TICKS_SIZE (f) = ready_ticks_size;
+ FENCE_CYCLE (f) = cycle;
+ }
+ else
+ {
+ /* Leave DC and CYCLE untouched. */
+ delete_deps_context (dc);
+ vec_free (executing_insns);
+ free (ready_ticks);
+ }
+ }
+
+ /* Fill remaining invariant fields. */
+ if (after_stall_p)
+ FENCE_AFTER_STALL_P (f) = 1;
+
+ FENCE_ISSUED_INSNS (f) = 0;
+ FENCE_STARTS_CYCLE_P (f) = 1;
+ FENCE_SCHED_NEXT (f) = NULL;
+}
+
+/* Add a new fence to NEW_FENCES list, initializing it from all
+ other parameters. */
+static void
+add_to_fences (flist_tail_t new_fences, insn_t insn,
+ state_t state, deps_t dc, void *tc, rtx last_scheduled_insn,
+ vec<rtx, va_gc> *executing_insns, int *ready_ticks,
+ int ready_ticks_size, rtx sched_next, int cycle,
+ int cycle_issued_insns, int issue_rate,
+ bool starts_cycle_p, bool after_stall_p)
+{
+ fence_t f = flist_lookup (FLIST_TAIL_HEAD (new_fences), insn);
+
+ if (! f)
+ {
+ flist_add (FLIST_TAIL_TAILP (new_fences), insn, state, dc, tc,
+ last_scheduled_insn, executing_insns, ready_ticks,
+ ready_ticks_size, sched_next, cycle, cycle_issued_insns,
+ issue_rate, starts_cycle_p, after_stall_p);
+
+ FLIST_TAIL_TAILP (new_fences)
+ = &FLIST_NEXT (*FLIST_TAIL_TAILP (new_fences));
+ }
+ else
+ {
+ merge_fences (f, insn, state, dc, tc, last_scheduled_insn,
+ executing_insns, ready_ticks, ready_ticks_size,
+ sched_next, cycle, issue_rate, after_stall_p);
+ }
+}
+
+/* Move the first fence in the OLD_FENCES list to NEW_FENCES. */
+void
+move_fence_to_fences (flist_t old_fences, flist_tail_t new_fences)
+{
+ fence_t f, old;
+ flist_t *tailp = FLIST_TAIL_TAILP (new_fences);
+
+ old = FLIST_FENCE (old_fences);
+ f = flist_lookup (FLIST_TAIL_HEAD (new_fences),
+ FENCE_INSN (FLIST_FENCE (old_fences)));
+ if (f)
+ {
+ merge_fences (f, old->insn, old->state, old->dc, old->tc,
+ old->last_scheduled_insn, old->executing_insns,
+ old->ready_ticks, old->ready_ticks_size,
+ old->sched_next, old->cycle, old->issue_more,
+ old->after_stall_p);
+ }
+ else
+ {
+ _list_add (tailp);
+ FLIST_TAIL_TAILP (new_fences) = &FLIST_NEXT (*tailp);
+ *FLIST_FENCE (*tailp) = *old;
+ init_fence_for_scheduling (FLIST_FENCE (*tailp));
+ }
+ FENCE_INSN (old) = NULL;
+}
+
+/* Add a new fence to NEW_FENCES list and initialize most of its data
+ as a clean one. */
+void
+add_clean_fence_to_fences (flist_tail_t new_fences, insn_t succ, fence_t fence)
+{
+ int ready_ticks_size = get_max_uid () + 1;
+
+ add_to_fences (new_fences,
+ succ, state_create (), create_deps_context (),
+ create_target_context (true),
+ NULL_RTX, NULL,
+ XCNEWVEC (int, ready_ticks_size), ready_ticks_size,
+ NULL_RTX, FENCE_CYCLE (fence) + 1,
+ 0, issue_rate, 1, FENCE_AFTER_STALL_P (fence));
+}
+
+/* Add a new fence to NEW_FENCES list and initialize all of its data
+ from FENCE and SUCC. */
+void
+add_dirty_fence_to_fences (flist_tail_t new_fences, insn_t succ, fence_t fence)
+{
+ int * new_ready_ticks
+ = XNEWVEC (int, FENCE_READY_TICKS_SIZE (fence));
+
+ memcpy (new_ready_ticks, FENCE_READY_TICKS (fence),
+ FENCE_READY_TICKS_SIZE (fence) * sizeof (int));
+ add_to_fences (new_fences,
+ succ, state_create_copy (FENCE_STATE (fence)),
+ create_copy_of_deps_context (FENCE_DC (fence)),
+ create_copy_of_target_context (FENCE_TC (fence)),
+ FENCE_LAST_SCHEDULED_INSN (fence),
+ vec_safe_copy (FENCE_EXECUTING_INSNS (fence)),
+ new_ready_ticks,
+ FENCE_READY_TICKS_SIZE (fence),
+ FENCE_SCHED_NEXT (fence),
+ FENCE_CYCLE (fence),
+ FENCE_ISSUED_INSNS (fence),
+ FENCE_ISSUE_MORE (fence),
+ FENCE_STARTS_CYCLE_P (fence),
+ FENCE_AFTER_STALL_P (fence));
+}
+
+
+/* Functions to work with regset and nop pools. */
+
+/* Returns the new regset from pool. It might have some of the bits set
+ from the previous usage. */
+regset
+get_regset_from_pool (void)
+{
+ regset rs;
+
+ if (regset_pool.n != 0)
+ rs = regset_pool.v[--regset_pool.n];
+ else
+ /* We need to create the regset. */
+ {
+ rs = ALLOC_REG_SET (&reg_obstack);
+
+ if (regset_pool.nn == regset_pool.ss)
+ regset_pool.vv = XRESIZEVEC (regset, regset_pool.vv,
+ (regset_pool.ss = 2 * regset_pool.ss + 1));
+ regset_pool.vv[regset_pool.nn++] = rs;
+ }
+
+ regset_pool.diff++;
+
+ return rs;
+}
+
+/* Same as above, but returns the empty regset. */
+regset
+get_clear_regset_from_pool (void)
+{
+ regset rs = get_regset_from_pool ();
+
+ CLEAR_REG_SET (rs);
+ return rs;
+}
+
+/* Return regset RS to the pool for future use. */
+void
+return_regset_to_pool (regset rs)
+{
+ gcc_assert (rs);
+ regset_pool.diff--;
+
+ if (regset_pool.n == regset_pool.s)
+ regset_pool.v = XRESIZEVEC (regset, regset_pool.v,
+ (regset_pool.s = 2 * regset_pool.s + 1));
+ regset_pool.v[regset_pool.n++] = rs;
+}
+
+#ifdef ENABLE_CHECKING
+/* This is used as a qsort callback for sorting regset pool stacks.
+ X and XX are addresses of two regsets. They are never equal. */
+static int
+cmp_v_in_regset_pool (const void *x, const void *xx)
+{
+ uintptr_t r1 = (uintptr_t) *((const regset *) x);
+ uintptr_t r2 = (uintptr_t) *((const regset *) xx);
+ if (r1 > r2)
+ return 1;
+ else if (r1 < r2)
+ return -1;
+ gcc_unreachable ();
+}
+#endif
+
+/* Free the regset pool possibly checking for memory leaks. */
+void
+free_regset_pool (void)
+{
+#ifdef ENABLE_CHECKING
+ {
+ regset *v = regset_pool.v;
+ int i = 0;
+ int n = regset_pool.n;
+
+ regset *vv = regset_pool.vv;
+ int ii = 0;
+ int nn = regset_pool.nn;
+
+ int diff = 0;
+
+ gcc_assert (n <= nn);
+
+ /* Sort both vectors so it will be possible to compare them. */
+ qsort (v, n, sizeof (*v), cmp_v_in_regset_pool);
+ qsort (vv, nn, sizeof (*vv), cmp_v_in_regset_pool);
+
+ while (ii < nn)
+ {
+ if (v[i] == vv[ii])
+ i++;
+ else
+ /* VV[II] was lost. */
+ diff++;
+
+ ii++;
+ }
+
+ gcc_assert (diff == regset_pool.diff);
+ }
+#endif
+
+ /* If not true - we have a memory leak. */
+ gcc_assert (regset_pool.diff == 0);
+
+ while (regset_pool.n)
+ {
+ --regset_pool.n;
+ FREE_REG_SET (regset_pool.v[regset_pool.n]);
+ }
+
+ free (regset_pool.v);
+ regset_pool.v = NULL;
+ regset_pool.s = 0;
+
+ free (regset_pool.vv);
+ regset_pool.vv = NULL;
+ regset_pool.nn = 0;
+ regset_pool.ss = 0;
+
+ regset_pool.diff = 0;
+}
+
+
+/* Functions to work with nop pools. NOP insns are used as temporary
+ placeholders of the insns being scheduled to allow correct update of
+ the data sets. When update is finished, NOPs are deleted. */
+
+/* A vinsn that is used to represent a nop. This vinsn is shared among all
+ nops sel-sched generates. */
+static vinsn_t nop_vinsn = NULL;
+
+/* Emit a nop before INSN, taking it from pool. */
+insn_t
+get_nop_from_pool (insn_t insn)
+{
+ insn_t nop;
+ bool old_p = nop_pool.n != 0;
+ int flags;
+
+ if (old_p)
+ nop = nop_pool.v[--nop_pool.n];
+ else
+ nop = nop_pattern;
+
+ nop = emit_insn_before (nop, insn);
+
+ if (old_p)
+ flags = INSN_INIT_TODO_SSID;
+ else
+ flags = INSN_INIT_TODO_LUID | INSN_INIT_TODO_SSID;
+
+ set_insn_init (INSN_EXPR (insn), nop_vinsn, INSN_SEQNO (insn));
+ sel_init_new_insn (nop, flags);
+
+ return nop;
+}
+
+/* Remove NOP from the instruction stream and return it to the pool. */
+void
+return_nop_to_pool (insn_t nop, bool full_tidying)
+{
+ gcc_assert (INSN_IN_STREAM_P (nop));
+ sel_remove_insn (nop, false, full_tidying);
+
+ /* We'll recycle this nop. */
+ INSN_DELETED_P (nop) = 0;
+
+ if (nop_pool.n == nop_pool.s)
+ nop_pool.v = XRESIZEVEC (rtx, nop_pool.v,
+ (nop_pool.s = 2 * nop_pool.s + 1));
+ nop_pool.v[nop_pool.n++] = nop;
+}
+
+/* Free the nop pool. */
+void
+free_nop_pool (void)
+{
+ nop_pool.n = 0;
+ nop_pool.s = 0;
+ free (nop_pool.v);
+ nop_pool.v = NULL;
+}
+
+
+/* Skip unspec to support ia64 speculation. Called from rtx_equal_p_cb.
+ The callback is given two rtxes XX and YY and writes the new rtxes
+ to NX and NY in case some needs to be skipped. */
+static int
+skip_unspecs_callback (const_rtx *xx, const_rtx *yy, rtx *nx, rtx* ny)
+{
+ const_rtx x = *xx;
+ const_rtx y = *yy;
+
+ if (GET_CODE (x) == UNSPEC
+ && (targetm.sched.skip_rtx_p == NULL
+ || targetm.sched.skip_rtx_p (x)))
+ {
+ *nx = XVECEXP (x, 0, 0);
+ *ny = CONST_CAST_RTX (y);
+ return 1;
+ }
+
+ if (GET_CODE (y) == UNSPEC
+ && (targetm.sched.skip_rtx_p == NULL
+ || targetm.sched.skip_rtx_p (y)))
+ {
+ *nx = CONST_CAST_RTX (x);
+ *ny = XVECEXP (y, 0, 0);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Callback, called from hash_rtx_cb. Helps to hash UNSPEC rtx X in a correct way
+ to support ia64 speculation. When changes are needed, new rtx X and new mode
+ NMODE are written, and the callback returns true. */
+static int
+hash_with_unspec_callback (const_rtx x, enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx *nx, enum machine_mode* nmode)
+{
+ if (GET_CODE (x) == UNSPEC
+ && targetm.sched.skip_rtx_p
+ && targetm.sched.skip_rtx_p (x))
+ {
+ *nx = XVECEXP (x, 0 ,0);
+ *nmode = VOIDmode;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Returns LHS and RHS are ok to be scheduled separately. */
+static bool
+lhs_and_rhs_separable_p (rtx lhs, rtx rhs)
+{
+ if (lhs == NULL || rhs == NULL)
+ return false;
+
+ /* Do not schedule CONST, CONST_INT and CONST_DOUBLE etc as rhs: no point
+ to use reg, if const can be used. Moreover, scheduling const as rhs may
+ lead to mode mismatch cause consts don't have modes but they could be
+ merged from branches where the same const used in different modes. */
+ if (CONSTANT_P (rhs))
+ return false;
+
+ /* ??? Do not rename predicate registers to avoid ICEs in bundling. */
+ if (COMPARISON_P (rhs))
+ return false;
+
+ /* Do not allow single REG to be an rhs. */
+ if (REG_P (rhs))
+ return false;
+
+ /* See comment at find_used_regs_1 (*1) for explanation of this
+ restriction. */
+ /* FIXME: remove this later. */
+ if (MEM_P (lhs))
+ return false;
+
+ /* This will filter all tricky things like ZERO_EXTRACT etc.
+ For now we don't handle it. */
+ if (!REG_P (lhs) && !MEM_P (lhs))
+ return false;
+
+ return true;
+}
+
+/* Initialize vinsn VI for INSN. Only for use from vinsn_create (). When
+ FORCE_UNIQUE_P is true, the resulting vinsn will not be clonable. This is
+ used e.g. for insns from recovery blocks. */
+static void
+vinsn_init (vinsn_t vi, insn_t insn, bool force_unique_p)
+{
+ hash_rtx_callback_function hrcf;
+ int insn_class;
+
+ VINSN_INSN_RTX (vi) = insn;
+ VINSN_COUNT (vi) = 0;
+ vi->cost = -1;
+
+ if (INSN_NOP_P (insn))
+ return;
+
+ if (DF_INSN_UID_SAFE_GET (INSN_UID (insn)) != NULL)
+ init_id_from_df (VINSN_ID (vi), insn, force_unique_p);
+ else
+ deps_init_id (VINSN_ID (vi), insn, force_unique_p);
+
+ /* Hash vinsn depending on whether it is separable or not. */
+ hrcf = targetm.sched.skip_rtx_p ? hash_with_unspec_callback : NULL;
+ if (VINSN_SEPARABLE_P (vi))
+ {
+ rtx rhs = VINSN_RHS (vi);
+
+ VINSN_HASH (vi) = hash_rtx_cb (rhs, GET_MODE (rhs),
+ NULL, NULL, false, hrcf);
+ VINSN_HASH_RTX (vi) = hash_rtx_cb (VINSN_PATTERN (vi),
+ VOIDmode, NULL, NULL,
+ false, hrcf);
+ }
+ else
+ {
+ VINSN_HASH (vi) = hash_rtx_cb (VINSN_PATTERN (vi), VOIDmode,
+ NULL, NULL, false, hrcf);
+ VINSN_HASH_RTX (vi) = VINSN_HASH (vi);
+ }
+
+ insn_class = haifa_classify_insn (insn);
+ if (insn_class >= 2
+ && (!targetm.sched.get_insn_spec_ds
+ || ((targetm.sched.get_insn_spec_ds (insn) & BEGIN_CONTROL)
+ == 0)))
+ VINSN_MAY_TRAP_P (vi) = true;
+ else
+ VINSN_MAY_TRAP_P (vi) = false;
+}
+
+/* Indicate that VI has become the part of an rtx object. */
+void
+vinsn_attach (vinsn_t vi)
+{
+ /* Assert that VI is not pending for deletion. */
+ gcc_assert (VINSN_INSN_RTX (vi));
+
+ VINSN_COUNT (vi)++;
+}
+
+/* Create and init VI from the INSN. Use UNIQUE_P for determining the correct
+ VINSN_TYPE (VI). */
+static vinsn_t
+vinsn_create (insn_t insn, bool force_unique_p)
+{
+ vinsn_t vi = XCNEW (struct vinsn_def);
+
+ vinsn_init (vi, insn, force_unique_p);
+ return vi;
+}
+
+/* Return a copy of VI. When REATTACH_P is true, detach VI and attach
+ the copy. */
+vinsn_t
+vinsn_copy (vinsn_t vi, bool reattach_p)
+{
+ rtx copy;
+ bool unique = VINSN_UNIQUE_P (vi);
+ vinsn_t new_vi;
+
+ copy = create_copy_of_insn_rtx (VINSN_INSN_RTX (vi));
+ new_vi = create_vinsn_from_insn_rtx (copy, unique);
+ if (reattach_p)
+ {
+ vinsn_detach (vi);
+ vinsn_attach (new_vi);
+ }
+
+ return new_vi;
+}
+
+/* Delete the VI vinsn and free its data. */
+static void
+vinsn_delete (vinsn_t vi)
+{
+ gcc_assert (VINSN_COUNT (vi) == 0);
+
+ if (!INSN_NOP_P (VINSN_INSN_RTX (vi)))
+ {
+ return_regset_to_pool (VINSN_REG_SETS (vi));
+ return_regset_to_pool (VINSN_REG_USES (vi));
+ return_regset_to_pool (VINSN_REG_CLOBBERS (vi));
+ }
+
+ free (vi);
+}
+
+/* Indicate that VI is no longer a part of some rtx object.
+ Remove VI if it is no longer needed. */
+void
+vinsn_detach (vinsn_t vi)
+{
+ gcc_assert (VINSN_COUNT (vi) > 0);
+
+ if (--VINSN_COUNT (vi) == 0)
+ vinsn_delete (vi);
+}
+
+/* Returns TRUE if VI is a branch. */
+bool
+vinsn_cond_branch_p (vinsn_t vi)
+{
+ insn_t insn;
+
+ if (!VINSN_UNIQUE_P (vi))
+ return false;
+
+ insn = VINSN_INSN_RTX (vi);
+ if (BB_END (BLOCK_FOR_INSN (insn)) != insn)
+ return false;
+
+ return control_flow_insn_p (insn);
+}
+
+/* Return latency of INSN. */
+static int
+sel_insn_rtx_cost (rtx insn)
+{
+ int cost;
+
+ /* A USE insn, or something else we don't need to
+ understand. We can't pass these directly to
+ result_ready_cost or insn_default_latency because it will
+ trigger a fatal error for unrecognizable insns. */
+ if (recog_memoized (insn) < 0)
+ cost = 0;
+ else
+ {
+ cost = insn_default_latency (insn);
+
+ if (cost < 0)
+ cost = 0;
+ }
+
+ return cost;
+}
+
+/* Return the cost of the VI.
+ !!! FIXME: Unify with haifa-sched.c: insn_cost (). */
+int
+sel_vinsn_cost (vinsn_t vi)
+{
+ int cost = vi->cost;
+
+ if (cost < 0)
+ {
+ cost = sel_insn_rtx_cost (VINSN_INSN_RTX (vi));
+ vi->cost = cost;
+ }
+
+ return cost;
+}
+
+
+/* Functions for insn emitting. */
+
+/* Emit new insn after AFTER based on PATTERN and initialize its data from
+ EXPR and SEQNO. */
+insn_t
+sel_gen_insn_from_rtx_after (rtx pattern, expr_t expr, int seqno, insn_t after)
+{
+ insn_t new_insn;
+
+ gcc_assert (EXPR_TARGET_AVAILABLE (expr) == true);
+
+ new_insn = emit_insn_after (pattern, after);
+ set_insn_init (expr, NULL, seqno);
+ sel_init_new_insn (new_insn, INSN_INIT_TODO_LUID | INSN_INIT_TODO_SSID);
+
+ return new_insn;
+}
+
+/* Force newly generated vinsns to be unique. */
+static bool init_insn_force_unique_p = false;
+
+/* Emit new speculation recovery insn after AFTER based on PATTERN and
+ initialize its data from EXPR and SEQNO. */
+insn_t
+sel_gen_recovery_insn_from_rtx_after (rtx pattern, expr_t expr, int seqno,
+ insn_t after)
+{
+ insn_t insn;
+
+ gcc_assert (!init_insn_force_unique_p);
+
+ init_insn_force_unique_p = true;
+ insn = sel_gen_insn_from_rtx_after (pattern, expr, seqno, after);
+ CANT_MOVE (insn) = 1;
+ init_insn_force_unique_p = false;
+
+ return insn;
+}
+
+/* Emit new insn after AFTER based on EXPR and SEQNO. If VINSN is not NULL,
+ take it as a new vinsn instead of EXPR's vinsn.
+ We simplify insns later, after scheduling region in
+ simplify_changed_insns. */
+insn_t
+sel_gen_insn_from_expr_after (expr_t expr, vinsn_t vinsn, int seqno,
+ insn_t after)
+{
+ expr_t emit_expr;
+ insn_t insn;
+ int flags;
+
+ emit_expr = set_insn_init (expr, vinsn ? vinsn : EXPR_VINSN (expr),
+ seqno);
+ insn = EXPR_INSN_RTX (emit_expr);
+
+ /* The insn may come from the transformation cache, which may hold already
+ deleted insns, so mark it as not deleted. */
+ INSN_DELETED_P (insn) = 0;
+
+ add_insn_after (insn, after, BLOCK_FOR_INSN (insn));
+
+ flags = INSN_INIT_TODO_SSID;
+ if (INSN_LUID (insn) == 0)
+ flags |= INSN_INIT_TODO_LUID;
+ sel_init_new_insn (insn, flags);
+
+ return insn;
+}
+
+/* Move insn from EXPR after AFTER. */
+insn_t
+sel_move_insn (expr_t expr, int seqno, insn_t after)
+{
+ insn_t insn = EXPR_INSN_RTX (expr);
+ basic_block bb = BLOCK_FOR_INSN (after);
+ insn_t next = NEXT_INSN (after);
+
+ /* Assert that in move_op we disconnected this insn properly. */
+ gcc_assert (EXPR_VINSN (INSN_EXPR (insn)) != NULL);
+ PREV_INSN (insn) = after;
+ NEXT_INSN (insn) = next;
+
+ NEXT_INSN (after) = insn;
+ PREV_INSN (next) = insn;
+
+ /* Update links from insn to bb and vice versa. */
+ df_insn_change_bb (insn, bb);
+ if (BB_END (bb) == after)
+ BB_END (bb) = insn;
+
+ prepare_insn_expr (insn, seqno);
+ return insn;
+}
+
+
+/* Functions to work with right-hand sides. */
+
+/* Search for a hash value determined by UID/NEW_VINSN in a sorted vector
+ VECT and return true when found. Use NEW_VINSN for comparison only when
+ COMPARE_VINSNS is true. Write to INDP the index on which
+ the search has stopped, such that inserting the new element at INDP will
+ retain VECT's sort order. */
+static bool
+find_in_history_vect_1 (vec<expr_history_def> vect,
+ unsigned uid, vinsn_t new_vinsn,
+ bool compare_vinsns, int *indp)
+{
+ expr_history_def *arr;
+ int i, j, len = vect.length ();
+
+ if (len == 0)
+ {
+ *indp = 0;
+ return false;
+ }
+
+ arr = vect.address ();
+ i = 0, j = len - 1;
+
+ while (i <= j)
+ {
+ unsigned auid = arr[i].uid;
+ vinsn_t avinsn = arr[i].new_expr_vinsn;
+
+ if (auid == uid
+ /* When undoing transformation on a bookkeeping copy, the new vinsn
+ may not be exactly equal to the one that is saved in the vector.
+ This is because the insn whose copy we're checking was possibly
+ substituted itself. */
+ && (! compare_vinsns
+ || vinsn_equal_p (avinsn, new_vinsn)))
+ {
+ *indp = i;
+ return true;
+ }
+ else if (auid > uid)
+ break;
+ i++;
+ }
+
+ *indp = i;
+ return false;
+}
+
+/* Search for a uid of INSN and NEW_VINSN in a sorted vector VECT. Return
+ the position found or -1, if no such value is in vector.
+ Search also for UIDs of insn's originators, if ORIGINATORS_P is true. */
+int
+find_in_history_vect (vec<expr_history_def> vect, rtx insn,
+ vinsn_t new_vinsn, bool originators_p)
+{
+ int ind;
+
+ if (find_in_history_vect_1 (vect, INSN_UID (insn), new_vinsn,
+ false, &ind))
+ return ind;
+
+ if (INSN_ORIGINATORS (insn) && originators_p)
+ {
+ unsigned uid;
+ bitmap_iterator bi;
+
+ EXECUTE_IF_SET_IN_BITMAP (INSN_ORIGINATORS (insn), 0, uid, bi)
+ if (find_in_history_vect_1 (vect, uid, new_vinsn, false, &ind))
+ return ind;
+ }
+
+ return -1;
+}
+
+/* Insert new element in a sorted history vector pointed to by PVECT,
+ if it is not there already. The element is searched using
+ UID/NEW_EXPR_VINSN pair. TYPE, OLD_EXPR_VINSN and SPEC_DS save
+ the history of a transformation. */
+void
+insert_in_history_vect (vec<expr_history_def> *pvect,
+ unsigned uid, enum local_trans_type type,
+ vinsn_t old_expr_vinsn, vinsn_t new_expr_vinsn,
+ ds_t spec_ds)
+{
+ vec<expr_history_def> vect = *pvect;
+ expr_history_def temp;
+ bool res;
+ int ind;
+
+ res = find_in_history_vect_1 (vect, uid, new_expr_vinsn, true, &ind);
+
+ if (res)
+ {
+ expr_history_def *phist = &vect[ind];
+
+ /* It is possible that speculation types of expressions that were
+ propagated through different paths will be different here. In this
+ case, merge the status to get the correct check later. */
+ if (phist->spec_ds != spec_ds)
+ phist->spec_ds = ds_max_merge (phist->spec_ds, spec_ds);
+ return;
+ }
+
+ temp.uid = uid;
+ temp.old_expr_vinsn = old_expr_vinsn;
+ temp.new_expr_vinsn = new_expr_vinsn;
+ temp.spec_ds = spec_ds;
+ temp.type = type;
+
+ vinsn_attach (old_expr_vinsn);
+ vinsn_attach (new_expr_vinsn);
+ vect.safe_insert (ind, temp);
+ *pvect = vect;
+}
+
+/* Free history vector PVECT. */
+static void
+free_history_vect (vec<expr_history_def> &pvect)
+{
+ unsigned i;
+ expr_history_def *phist;
+
+ if (! pvect.exists ())
+ return;
+
+ for (i = 0; pvect.iterate (i, &phist); i++)
+ {
+ vinsn_detach (phist->old_expr_vinsn);
+ vinsn_detach (phist->new_expr_vinsn);
+ }
+
+ pvect.release ();
+}
+
+/* Merge vector FROM to PVECT. */
+static void
+merge_history_vect (vec<expr_history_def> *pvect,
+ vec<expr_history_def> from)
+{
+ expr_history_def *phist;
+ int i;
+
+ /* We keep this vector sorted. */
+ for (i = 0; from.iterate (i, &phist); i++)
+ insert_in_history_vect (pvect, phist->uid, phist->type,
+ phist->old_expr_vinsn, phist->new_expr_vinsn,
+ phist->spec_ds);
+}
+
+/* Compare two vinsns as rhses if possible and as vinsns otherwise. */
+bool
+vinsn_equal_p (vinsn_t x, vinsn_t y)
+{
+ rtx_equal_p_callback_function repcf;
+
+ if (x == y)
+ return true;
+
+ if (VINSN_TYPE (x) != VINSN_TYPE (y))
+ return false;
+
+ if (VINSN_HASH (x) != VINSN_HASH (y))
+ return false;
+
+ repcf = targetm.sched.skip_rtx_p ? skip_unspecs_callback : NULL;
+ if (VINSN_SEPARABLE_P (x))
+ {
+ /* Compare RHSes of VINSNs. */
+ gcc_assert (VINSN_RHS (x));
+ gcc_assert (VINSN_RHS (y));
+
+ return rtx_equal_p_cb (VINSN_RHS (x), VINSN_RHS (y), repcf);
+ }
+
+ return rtx_equal_p_cb (VINSN_PATTERN (x), VINSN_PATTERN (y), repcf);
+}
+
+
+/* Functions for working with expressions. */
+
+/* Initialize EXPR. */
+static void
+init_expr (expr_t expr, vinsn_t vi, int spec, int use, int priority,
+ int sched_times, int orig_bb_index, ds_t spec_done_ds,
+ ds_t spec_to_check_ds, int orig_sched_cycle,
+ vec<expr_history_def> history,
+ signed char target_available,
+ bool was_substituted, bool was_renamed, bool needs_spec_check_p,
+ bool cant_move)
+{
+ vinsn_attach (vi);
+
+ EXPR_VINSN (expr) = vi;
+ EXPR_SPEC (expr) = spec;
+ EXPR_USEFULNESS (expr) = use;
+ EXPR_PRIORITY (expr) = priority;
+ EXPR_PRIORITY_ADJ (expr) = 0;
+ EXPR_SCHED_TIMES (expr) = sched_times;
+ EXPR_ORIG_BB_INDEX (expr) = orig_bb_index;
+ EXPR_ORIG_SCHED_CYCLE (expr) = orig_sched_cycle;
+ EXPR_SPEC_DONE_DS (expr) = spec_done_ds;
+ EXPR_SPEC_TO_CHECK_DS (expr) = spec_to_check_ds;
+
+ if (history.exists ())
+ EXPR_HISTORY_OF_CHANGES (expr) = history;
+ else
+ EXPR_HISTORY_OF_CHANGES (expr).create (0);
+
+ EXPR_TARGET_AVAILABLE (expr) = target_available;
+ EXPR_WAS_SUBSTITUTED (expr) = was_substituted;
+ EXPR_WAS_RENAMED (expr) = was_renamed;
+ EXPR_NEEDS_SPEC_CHECK_P (expr) = needs_spec_check_p;
+ EXPR_CANT_MOVE (expr) = cant_move;
+}
+
+/* Make a copy of the expr FROM into the expr TO. */
+void
+copy_expr (expr_t to, expr_t from)
+{
+ vec<expr_history_def> temp = vNULL;
+
+ if (EXPR_HISTORY_OF_CHANGES (from).exists ())
+ {
+ unsigned i;
+ expr_history_def *phist;
+
+ temp = EXPR_HISTORY_OF_CHANGES (from).copy ();
+ for (i = 0;
+ temp.iterate (i, &phist);
+ i++)
+ {
+ vinsn_attach (phist->old_expr_vinsn);
+ vinsn_attach (phist->new_expr_vinsn);
+ }
+ }
+
+ init_expr (to, EXPR_VINSN (from), EXPR_SPEC (from),
+ EXPR_USEFULNESS (from), EXPR_PRIORITY (from),
+ EXPR_SCHED_TIMES (from), EXPR_ORIG_BB_INDEX (from),
+ EXPR_SPEC_DONE_DS (from), EXPR_SPEC_TO_CHECK_DS (from),
+ EXPR_ORIG_SCHED_CYCLE (from), temp,
+ EXPR_TARGET_AVAILABLE (from), EXPR_WAS_SUBSTITUTED (from),
+ EXPR_WAS_RENAMED (from), EXPR_NEEDS_SPEC_CHECK_P (from),
+ EXPR_CANT_MOVE (from));
+}
+
+/* Same, but the final expr will not ever be in av sets, so don't copy
+ "uninteresting" data such as bitmap cache. */
+void
+copy_expr_onside (expr_t to, expr_t from)
+{
+ init_expr (to, EXPR_VINSN (from), EXPR_SPEC (from), EXPR_USEFULNESS (from),
+ EXPR_PRIORITY (from), EXPR_SCHED_TIMES (from), 0,
+ EXPR_SPEC_DONE_DS (from), EXPR_SPEC_TO_CHECK_DS (from), 0,
+ vNULL,
+ EXPR_TARGET_AVAILABLE (from), EXPR_WAS_SUBSTITUTED (from),
+ EXPR_WAS_RENAMED (from), EXPR_NEEDS_SPEC_CHECK_P (from),
+ EXPR_CANT_MOVE (from));
+}
+
+/* Prepare the expr of INSN for scheduling. Used when moving insn and when
+ initializing new insns. */
+static void
+prepare_insn_expr (insn_t insn, int seqno)
+{
+ expr_t expr = INSN_EXPR (insn);
+ ds_t ds;
+
+ INSN_SEQNO (insn) = seqno;
+ EXPR_ORIG_BB_INDEX (expr) = BLOCK_NUM (insn);
+ EXPR_SPEC (expr) = 0;
+ EXPR_ORIG_SCHED_CYCLE (expr) = 0;
+ EXPR_WAS_SUBSTITUTED (expr) = 0;
+ EXPR_WAS_RENAMED (expr) = 0;
+ EXPR_TARGET_AVAILABLE (expr) = 1;
+ INSN_LIVE_VALID_P (insn) = false;
+
+ /* ??? If this expression is speculative, make its dependence
+ as weak as possible. We can filter this expression later
+ in process_spec_exprs, because we do not distinguish
+ between the status we got during compute_av_set and the
+ existing status. To be fixed. */
+ ds = EXPR_SPEC_DONE_DS (expr);
+ if (ds)
+ EXPR_SPEC_DONE_DS (expr) = ds_get_max_dep_weak (ds);
+
+ free_history_vect (EXPR_HISTORY_OF_CHANGES (expr));
+}
+
+/* Update target_available bits when merging exprs TO and FROM. SPLIT_POINT
+ is non-null when expressions are merged from different successors at
+ a split point. */
+static void
+update_target_availability (expr_t to, expr_t from, insn_t split_point)
+{
+ if (EXPR_TARGET_AVAILABLE (to) < 0
+ || EXPR_TARGET_AVAILABLE (from) < 0)
+ EXPR_TARGET_AVAILABLE (to) = -1;
+ else
+ {
+ /* We try to detect the case when one of the expressions
+ can only be reached through another one. In this case,
+ we can do better. */
+ if (split_point == NULL)
+ {
+ int toind, fromind;
+
+ toind = EXPR_ORIG_BB_INDEX (to);
+ fromind = EXPR_ORIG_BB_INDEX (from);
+
+ if (toind && toind == fromind)
+ /* Do nothing -- everything is done in
+ merge_with_other_exprs. */
+ ;
+ else
+ EXPR_TARGET_AVAILABLE (to) = -1;
+ }
+ else if (EXPR_TARGET_AVAILABLE (from) == 0
+ && EXPR_LHS (from)
+ && REG_P (EXPR_LHS (from))
+ && REGNO (EXPR_LHS (to)) != REGNO (EXPR_LHS (from)))
+ EXPR_TARGET_AVAILABLE (to) = -1;
+ else
+ EXPR_TARGET_AVAILABLE (to) &= EXPR_TARGET_AVAILABLE (from);
+ }
+}
+
+/* Update speculation bits when merging exprs TO and FROM. SPLIT_POINT
+ is non-null when expressions are merged from different successors at
+ a split point. */
+static void
+update_speculative_bits (expr_t to, expr_t from, insn_t split_point)
+{
+ ds_t old_to_ds, old_from_ds;
+
+ old_to_ds = EXPR_SPEC_DONE_DS (to);
+ old_from_ds = EXPR_SPEC_DONE_DS (from);
+
+ EXPR_SPEC_DONE_DS (to) = ds_max_merge (old_to_ds, old_from_ds);
+ EXPR_SPEC_TO_CHECK_DS (to) |= EXPR_SPEC_TO_CHECK_DS (from);
+ EXPR_NEEDS_SPEC_CHECK_P (to) |= EXPR_NEEDS_SPEC_CHECK_P (from);
+
+ /* When merging e.g. control & data speculative exprs, or a control
+ speculative with a control&data speculative one, we really have
+ to change vinsn too. Also, when speculative status is changed,
+ we also need to record this as a transformation in expr's history. */
+ if ((old_to_ds & SPECULATIVE) || (old_from_ds & SPECULATIVE))
+ {
+ old_to_ds = ds_get_speculation_types (old_to_ds);
+ old_from_ds = ds_get_speculation_types (old_from_ds);
+
+ if (old_to_ds != old_from_ds)
+ {
+ ds_t record_ds;
+
+ /* When both expressions are speculative, we need to change
+ the vinsn first. */
+ if ((old_to_ds & SPECULATIVE) && (old_from_ds & SPECULATIVE))
+ {
+ int res;
+
+ res = speculate_expr (to, EXPR_SPEC_DONE_DS (to));
+ gcc_assert (res >= 0);
+ }
+
+ if (split_point != NULL)
+ {
+ /* Record the change with proper status. */
+ record_ds = EXPR_SPEC_DONE_DS (to) & SPECULATIVE;
+ record_ds &= ~(old_to_ds & SPECULATIVE);
+ record_ds &= ~(old_from_ds & SPECULATIVE);
+
+ insert_in_history_vect (&EXPR_HISTORY_OF_CHANGES (to),
+ INSN_UID (split_point), TRANS_SPECULATION,
+ EXPR_VINSN (from), EXPR_VINSN (to),
+ record_ds);
+ }
+ }
+ }
+}
+
+
+/* Merge bits of FROM expr to TO expr. When SPLIT_POINT is not NULL,
+ this is done along different paths. */
+void
+merge_expr_data (expr_t to, expr_t from, insn_t split_point)
+{
+ /* Choose the maximum of the specs of merged exprs. This is required
+ for correctness of bookkeeping. */
+ if (EXPR_SPEC (to) < EXPR_SPEC (from))
+ EXPR_SPEC (to) = EXPR_SPEC (from);
+
+ if (split_point)
+ EXPR_USEFULNESS (to) += EXPR_USEFULNESS (from);
+ else
+ EXPR_USEFULNESS (to) = MAX (EXPR_USEFULNESS (to),
+ EXPR_USEFULNESS (from));
+
+ if (EXPR_PRIORITY (to) < EXPR_PRIORITY (from))
+ EXPR_PRIORITY (to) = EXPR_PRIORITY (from);
+
+ if (EXPR_SCHED_TIMES (to) > EXPR_SCHED_TIMES (from))
+ EXPR_SCHED_TIMES (to) = EXPR_SCHED_TIMES (from);
+
+ if (EXPR_ORIG_BB_INDEX (to) != EXPR_ORIG_BB_INDEX (from))
+ EXPR_ORIG_BB_INDEX (to) = 0;
+
+ EXPR_ORIG_SCHED_CYCLE (to) = MIN (EXPR_ORIG_SCHED_CYCLE (to),
+ EXPR_ORIG_SCHED_CYCLE (from));
+
+ EXPR_WAS_SUBSTITUTED (to) |= EXPR_WAS_SUBSTITUTED (from);
+ EXPR_WAS_RENAMED (to) |= EXPR_WAS_RENAMED (from);
+ EXPR_CANT_MOVE (to) |= EXPR_CANT_MOVE (from);
+
+ merge_history_vect (&EXPR_HISTORY_OF_CHANGES (to),
+ EXPR_HISTORY_OF_CHANGES (from));
+ update_target_availability (to, from, split_point);
+ update_speculative_bits (to, from, split_point);
+}
+
+/* Merge bits of FROM expr to TO expr. Vinsns in the exprs should be equal
+ in terms of vinsn_equal_p. SPLIT_POINT is non-null when expressions
+ are merged from different successors at a split point. */
+void
+merge_expr (expr_t to, expr_t from, insn_t split_point)
+{
+ vinsn_t to_vi = EXPR_VINSN (to);
+ vinsn_t from_vi = EXPR_VINSN (from);
+
+ gcc_assert (vinsn_equal_p (to_vi, from_vi));
+
+ /* Make sure that speculative pattern is propagated into exprs that
+ have non-speculative one. This will provide us with consistent
+ speculative bits and speculative patterns inside expr. */
+ if ((EXPR_SPEC_DONE_DS (from) != 0
+ && EXPR_SPEC_DONE_DS (to) == 0)
+ /* Do likewise for volatile insns, so that we always retain
+ the may_trap_p bit on the resulting expression. */
+ || (VINSN_MAY_TRAP_P (EXPR_VINSN (from))
+ && !VINSN_MAY_TRAP_P (EXPR_VINSN (to))))
+ change_vinsn_in_expr (to, EXPR_VINSN (from));
+
+ merge_expr_data (to, from, split_point);
+ gcc_assert (EXPR_USEFULNESS (to) <= REG_BR_PROB_BASE);
+}
+
+/* Clear the information of this EXPR. */
+void
+clear_expr (expr_t expr)
+{
+
+ vinsn_detach (EXPR_VINSN (expr));
+ EXPR_VINSN (expr) = NULL;
+
+ free_history_vect (EXPR_HISTORY_OF_CHANGES (expr));
+}
+
+/* For a given LV_SET, mark EXPR having unavailable target register. */
+static void
+set_unavailable_target_for_expr (expr_t expr, regset lv_set)
+{
+ if (EXPR_SEPARABLE_P (expr))
+ {
+ if (REG_P (EXPR_LHS (expr))
+ && register_unavailable_p (lv_set, EXPR_LHS (expr)))
+ {
+ /* If it's an insn like r1 = use (r1, ...), and it exists in
+ different forms in each of the av_sets being merged, we can't say
+ whether original destination register is available or not.
+ However, this still works if destination register is not used
+ in the original expression: if the branch at which LV_SET we're
+ looking here is not actually 'other branch' in sense that same
+ expression is available through it (but it can't be determined
+ at computation stage because of transformations on one of the
+ branches), it still won't affect the availability.
+ Liveness of a register somewhere on a code motion path means
+ it's either read somewhere on a codemotion path, live on
+ 'other' branch, live at the point immediately following
+ the original operation, or is read by the original operation.
+ The latter case is filtered out in the condition below.
+ It still doesn't cover the case when register is defined and used
+ somewhere within the code motion path, and in this case we could
+ miss a unifying code motion along both branches using a renamed
+ register, but it won't affect a code correctness since upon
+ an actual code motion a bookkeeping code would be generated. */
+ if (register_unavailable_p (VINSN_REG_USES (EXPR_VINSN (expr)),
+ EXPR_LHS (expr)))
+ EXPR_TARGET_AVAILABLE (expr) = -1;
+ else
+ EXPR_TARGET_AVAILABLE (expr) = false;
+ }
+ }
+ else
+ {
+ unsigned regno;
+ reg_set_iterator rsi;
+
+ EXECUTE_IF_SET_IN_REG_SET (VINSN_REG_SETS (EXPR_VINSN (expr)),
+ 0, regno, rsi)
+ if (bitmap_bit_p (lv_set, regno))
+ {
+ EXPR_TARGET_AVAILABLE (expr) = false;
+ break;
+ }
+
+ EXECUTE_IF_SET_IN_REG_SET (VINSN_REG_CLOBBERS (EXPR_VINSN (expr)),
+ 0, regno, rsi)
+ if (bitmap_bit_p (lv_set, regno))
+ {
+ EXPR_TARGET_AVAILABLE (expr) = false;
+ break;
+ }
+ }
+}
+
+/* Try to make EXPR speculative. Return 1 when EXPR's pattern
+ or dependence status have changed, 2 when also the target register
+ became unavailable, 0 if nothing had to be changed. */
+int
+speculate_expr (expr_t expr, ds_t ds)
+{
+ int res;
+ rtx orig_insn_rtx;
+ rtx spec_pat;
+ ds_t target_ds, current_ds;
+
+ /* Obtain the status we need to put on EXPR. */
+ target_ds = (ds & SPECULATIVE);
+ current_ds = EXPR_SPEC_DONE_DS (expr);
+ ds = ds_full_merge (current_ds, target_ds, NULL_RTX, NULL_RTX);
+
+ orig_insn_rtx = EXPR_INSN_RTX (expr);
+
+ res = sched_speculate_insn (orig_insn_rtx, ds, &spec_pat);
+
+ switch (res)
+ {
+ case 0:
+ EXPR_SPEC_DONE_DS (expr) = ds;
+ return current_ds != ds ? 1 : 0;
+
+ case 1:
+ {
+ rtx spec_insn_rtx = create_insn_rtx_from_pattern (spec_pat, NULL_RTX);
+ vinsn_t spec_vinsn = create_vinsn_from_insn_rtx (spec_insn_rtx, false);
+
+ change_vinsn_in_expr (expr, spec_vinsn);
+ EXPR_SPEC_DONE_DS (expr) = ds;
+ EXPR_NEEDS_SPEC_CHECK_P (expr) = true;
+
+ /* Do not allow clobbering the address register of speculative
+ insns. */
+ if (register_unavailable_p (VINSN_REG_USES (EXPR_VINSN (expr)),
+ expr_dest_reg (expr)))
+ {
+ EXPR_TARGET_AVAILABLE (expr) = false;
+ return 2;
+ }
+
+ return 1;
+ }
+
+ case -1:
+ return -1;
+
+ default:
+ gcc_unreachable ();
+ return -1;
+ }
+}
+
+/* Return a destination register, if any, of EXPR. */
+rtx
+expr_dest_reg (expr_t expr)
+{
+ rtx dest = VINSN_LHS (EXPR_VINSN (expr));
+
+ if (dest != NULL_RTX && REG_P (dest))
+ return dest;
+
+ return NULL_RTX;
+}
+
+/* Returns the REGNO of the R's destination. */
+unsigned
+expr_dest_regno (expr_t expr)
+{
+ rtx dest = expr_dest_reg (expr);
+
+ gcc_assert (dest != NULL_RTX);
+ return REGNO (dest);
+}
+
+/* For a given LV_SET, mark all expressions in JOIN_SET, but not present in
+ AV_SET having unavailable target register. */
+void
+mark_unavailable_targets (av_set_t join_set, av_set_t av_set, regset lv_set)
+{
+ expr_t expr;
+ av_set_iterator avi;
+
+ FOR_EACH_EXPR (expr, avi, join_set)
+ if (av_set_lookup (av_set, EXPR_VINSN (expr)) == NULL)
+ set_unavailable_target_for_expr (expr, lv_set);
+}
+
+
+/* Returns true if REG (at least partially) is present in REGS. */
+bool
+register_unavailable_p (regset regs, rtx reg)
+{
+ unsigned regno, end_regno;
+
+ regno = REGNO (reg);
+ if (bitmap_bit_p (regs, regno))
+ return true;
+
+ end_regno = END_REGNO (reg);
+
+ while (++regno < end_regno)
+ if (bitmap_bit_p (regs, regno))
+ return true;
+
+ return false;
+}
+
+/* Av set functions. */
+
+/* Add a new element to av set SETP.
+ Return the element added. */
+static av_set_t
+av_set_add_element (av_set_t *setp)
+{
+ /* Insert at the beginning of the list. */
+ _list_add (setp);
+ return *setp;
+}
+
+/* Add EXPR to SETP. */
+void
+av_set_add (av_set_t *setp, expr_t expr)
+{
+ av_set_t elem;
+
+ gcc_assert (!INSN_NOP_P (EXPR_INSN_RTX (expr)));
+ elem = av_set_add_element (setp);
+ copy_expr (_AV_SET_EXPR (elem), expr);
+}
+
+/* Same, but do not copy EXPR. */
+static void
+av_set_add_nocopy (av_set_t *setp, expr_t expr)
+{
+ av_set_t elem;
+
+ elem = av_set_add_element (setp);
+ *_AV_SET_EXPR (elem) = *expr;
+}
+
+/* Remove expr pointed to by IP from the av_set. */
+void
+av_set_iter_remove (av_set_iterator *ip)
+{
+ clear_expr (_AV_SET_EXPR (*ip->lp));
+ _list_iter_remove (ip);
+}
+
+/* Search for an expr in SET, such that it's equivalent to SOUGHT_VINSN in the
+ sense of vinsn_equal_p function. Return NULL if no such expr is
+ in SET was found. */
+expr_t
+av_set_lookup (av_set_t set, vinsn_t sought_vinsn)
+{
+ expr_t expr;
+ av_set_iterator i;
+
+ FOR_EACH_EXPR (expr, i, set)
+ if (vinsn_equal_p (EXPR_VINSN (expr), sought_vinsn))
+ return expr;
+ return NULL;
+}
+
+/* Same, but also remove the EXPR found. */
+static expr_t
+av_set_lookup_and_remove (av_set_t *setp, vinsn_t sought_vinsn)
+{
+ expr_t expr;
+ av_set_iterator i;
+
+ FOR_EACH_EXPR_1 (expr, i, setp)
+ if (vinsn_equal_p (EXPR_VINSN (expr), sought_vinsn))
+ {
+ _list_iter_remove_nofree (&i);
+ return expr;
+ }
+ return NULL;
+}
+
+/* Search for an expr in SET, such that it's equivalent to EXPR in the
+ sense of vinsn_equal_p function of their vinsns, but not EXPR itself.
+ Returns NULL if no such expr is in SET was found. */
+static expr_t
+av_set_lookup_other_equiv_expr (av_set_t set, expr_t expr)
+{
+ expr_t cur_expr;
+ av_set_iterator i;
+
+ FOR_EACH_EXPR (cur_expr, i, set)
+ {
+ if (cur_expr == expr)
+ continue;
+ if (vinsn_equal_p (EXPR_VINSN (cur_expr), EXPR_VINSN (expr)))
+ return cur_expr;
+ }
+
+ return NULL;
+}
+
+/* If other expression is already in AVP, remove one of them. */
+expr_t
+merge_with_other_exprs (av_set_t *avp, av_set_iterator *ip, expr_t expr)
+{
+ expr_t expr2;
+
+ expr2 = av_set_lookup_other_equiv_expr (*avp, expr);
+ if (expr2 != NULL)
+ {
+ /* Reset target availability on merge, since taking it only from one
+ of the exprs would be controversial for different code. */
+ EXPR_TARGET_AVAILABLE (expr2) = -1;
+ EXPR_USEFULNESS (expr2) = 0;
+
+ merge_expr (expr2, expr, NULL);
+
+ /* Fix usefulness as it should be now REG_BR_PROB_BASE. */
+ EXPR_USEFULNESS (expr2) = REG_BR_PROB_BASE;
+
+ av_set_iter_remove (ip);
+ return expr2;
+ }
+
+ return expr;
+}
+
+/* Return true if there is an expr that correlates to VI in SET. */
+bool
+av_set_is_in_p (av_set_t set, vinsn_t vi)
+{
+ return av_set_lookup (set, vi) != NULL;
+}
+
+/* Return a copy of SET. */
+av_set_t
+av_set_copy (av_set_t set)
+{
+ expr_t expr;
+ av_set_iterator i;
+ av_set_t res = NULL;
+
+ FOR_EACH_EXPR (expr, i, set)
+ av_set_add (&res, expr);
+
+ return res;
+}
+
+/* Join two av sets that do not have common elements by attaching second set
+ (pointed to by FROMP) to the end of first set (TO_TAILP must point to
+ _AV_SET_NEXT of first set's last element). */
+static void
+join_distinct_sets (av_set_t *to_tailp, av_set_t *fromp)
+{
+ gcc_assert (*to_tailp == NULL);
+ *to_tailp = *fromp;
+ *fromp = NULL;
+}
+
+/* Makes set pointed to by TO to be the union of TO and FROM. Clear av_set
+ pointed to by FROMP afterwards. */
+void
+av_set_union_and_clear (av_set_t *top, av_set_t *fromp, insn_t insn)
+{
+ expr_t expr1;
+ av_set_iterator i;
+
+ /* Delete from TOP all exprs, that present in FROMP. */
+ FOR_EACH_EXPR_1 (expr1, i, top)
+ {
+ expr_t expr2 = av_set_lookup (*fromp, EXPR_VINSN (expr1));
+
+ if (expr2)
+ {
+ merge_expr (expr2, expr1, insn);
+ av_set_iter_remove (&i);
+ }
+ }
+
+ join_distinct_sets (i.lp, fromp);
+}
+
+/* Same as above, but also update availability of target register in
+ TOP judging by TO_LV_SET and FROM_LV_SET. */
+void
+av_set_union_and_live (av_set_t *top, av_set_t *fromp, regset to_lv_set,
+ regset from_lv_set, insn_t insn)
+{
+ expr_t expr1;
+ av_set_iterator i;
+ av_set_t *to_tailp, in_both_set = NULL;
+
+ /* Delete from TOP all expres, that present in FROMP. */
+ FOR_EACH_EXPR_1 (expr1, i, top)
+ {
+ expr_t expr2 = av_set_lookup_and_remove (fromp, EXPR_VINSN (expr1));
+
+ if (expr2)
+ {
+ /* It may be that the expressions have different destination
+ registers, in which case we need to check liveness here. */
+ if (EXPR_SEPARABLE_P (expr1))
+ {
+ int regno1 = (REG_P (EXPR_LHS (expr1))
+ ? (int) expr_dest_regno (expr1) : -1);
+ int regno2 = (REG_P (EXPR_LHS (expr2))
+ ? (int) expr_dest_regno (expr2) : -1);
+
+ /* ??? We don't have a way to check restrictions for
+ *other* register on the current path, we did it only
+ for the current target register. Give up. */
+ if (regno1 != regno2)
+ EXPR_TARGET_AVAILABLE (expr2) = -1;
+ }
+ else if (EXPR_INSN_RTX (expr1) != EXPR_INSN_RTX (expr2))
+ EXPR_TARGET_AVAILABLE (expr2) = -1;
+
+ merge_expr (expr2, expr1, insn);
+ av_set_add_nocopy (&in_both_set, expr2);
+ av_set_iter_remove (&i);
+ }
+ else
+ /* EXPR1 is present in TOP, but not in FROMP. Check it on
+ FROM_LV_SET. */
+ set_unavailable_target_for_expr (expr1, from_lv_set);
+ }
+ to_tailp = i.lp;
+
+ /* These expressions are not present in TOP. Check liveness
+ restrictions on TO_LV_SET. */
+ FOR_EACH_EXPR (expr1, i, *fromp)
+ set_unavailable_target_for_expr (expr1, to_lv_set);
+
+ join_distinct_sets (i.lp, &in_both_set);
+ join_distinct_sets (to_tailp, fromp);
+}
+
+/* Clear av_set pointed to by SETP. */
+void
+av_set_clear (av_set_t *setp)
+{
+ expr_t expr;
+ av_set_iterator i;
+
+ FOR_EACH_EXPR_1 (expr, i, setp)
+ av_set_iter_remove (&i);
+
+ gcc_assert (*setp == NULL);
+}
+
+/* Leave only one non-speculative element in the SETP. */
+void
+av_set_leave_one_nonspec (av_set_t *setp)
+{
+ expr_t expr;
+ av_set_iterator i;
+ bool has_one_nonspec = false;
+
+ /* Keep all speculative exprs, and leave one non-speculative
+ (the first one). */
+ FOR_EACH_EXPR_1 (expr, i, setp)
+ {
+ if (!EXPR_SPEC_DONE_DS (expr))
+ {
+ if (has_one_nonspec)
+ av_set_iter_remove (&i);
+ else
+ has_one_nonspec = true;
+ }
+ }
+}
+
+/* Return the N'th element of the SET. */
+expr_t
+av_set_element (av_set_t set, int n)
+{
+ expr_t expr;
+ av_set_iterator i;
+
+ FOR_EACH_EXPR (expr, i, set)
+ if (n-- == 0)
+ return expr;
+
+ gcc_unreachable ();
+ return NULL;
+}
+
+/* Deletes all expressions from AVP that are conditional branches (IFs). */
+void
+av_set_substract_cond_branches (av_set_t *avp)
+{
+ av_set_iterator i;
+ expr_t expr;
+
+ FOR_EACH_EXPR_1 (expr, i, avp)
+ if (vinsn_cond_branch_p (EXPR_VINSN (expr)))
+ av_set_iter_remove (&i);
+}
+
+/* Multiplies usefulness attribute of each member of av-set *AVP by
+ value PROB / ALL_PROB. */
+void
+av_set_split_usefulness (av_set_t av, int prob, int all_prob)
+{
+ av_set_iterator i;
+ expr_t expr;
+
+ FOR_EACH_EXPR (expr, i, av)
+ EXPR_USEFULNESS (expr) = (all_prob
+ ? (EXPR_USEFULNESS (expr) * prob) / all_prob
+ : 0);
+}
+
+/* Leave in AVP only those expressions, which are present in AV,
+ and return it, merging history expressions. */
+void
+av_set_code_motion_filter (av_set_t *avp, av_set_t av)
+{
+ av_set_iterator i;
+ expr_t expr, expr2;
+
+ FOR_EACH_EXPR_1 (expr, i, avp)
+ if ((expr2 = av_set_lookup (av, EXPR_VINSN (expr))) == NULL)
+ av_set_iter_remove (&i);
+ else
+ /* When updating av sets in bookkeeping blocks, we can add more insns
+ there which will be transformed but the upper av sets will not
+ reflect those transformations. We then fail to undo those
+ when searching for such insns. So merge the history saved
+ in the av set of the block we are processing. */
+ merge_history_vect (&EXPR_HISTORY_OF_CHANGES (expr),
+ EXPR_HISTORY_OF_CHANGES (expr2));
+}
+
+
+
+/* Dependence hooks to initialize insn data. */
+
+/* This is used in hooks callable from dependence analysis when initializing
+ instruction's data. */
+static struct
+{
+ /* Where the dependence was found (lhs/rhs). */
+ deps_where_t where;
+
+ /* The actual data object to initialize. */
+ idata_t id;
+
+ /* True when the insn should not be made clonable. */
+ bool force_unique_p;
+
+ /* True when insn should be treated as of type USE, i.e. never renamed. */
+ bool force_use_p;
+} deps_init_id_data;
+
+
+/* Setup ID for INSN. FORCE_UNIQUE_P is true when INSN should not be
+ clonable. */
+static void
+setup_id_for_insn (idata_t id, insn_t insn, bool force_unique_p)
+{
+ int type;
+
+ /* Determine whether INSN could be cloned and return appropriate vinsn type.
+ That clonable insns which can be separated into lhs and rhs have type SET.
+ Other clonable insns have type USE. */
+ type = GET_CODE (insn);
+
+ /* Only regular insns could be cloned. */
+ if (type == INSN && !force_unique_p)
+ type = SET;
+ else if (type == JUMP_INSN && simplejump_p (insn))
+ type = PC;
+ else if (type == DEBUG_INSN)
+ type = !force_unique_p ? USE : INSN;
+
+ IDATA_TYPE (id) = type;
+ IDATA_REG_SETS (id) = get_clear_regset_from_pool ();
+ IDATA_REG_USES (id) = get_clear_regset_from_pool ();
+ IDATA_REG_CLOBBERS (id) = get_clear_regset_from_pool ();
+}
+
+/* Start initializing insn data. */
+static void
+deps_init_id_start_insn (insn_t insn)
+{
+ gcc_assert (deps_init_id_data.where == DEPS_IN_NOWHERE);
+
+ setup_id_for_insn (deps_init_id_data.id, insn,
+ deps_init_id_data.force_unique_p);
+ deps_init_id_data.where = DEPS_IN_INSN;
+}
+
+/* Start initializing lhs data. */
+static void
+deps_init_id_start_lhs (rtx lhs)
+{
+ gcc_assert (deps_init_id_data.where == DEPS_IN_INSN);
+ gcc_assert (IDATA_LHS (deps_init_id_data.id) == NULL);
+
+ if (IDATA_TYPE (deps_init_id_data.id) == SET)
+ {
+ IDATA_LHS (deps_init_id_data.id) = lhs;
+ deps_init_id_data.where = DEPS_IN_LHS;
+ }
+}
+
+/* Finish initializing lhs data. */
+static void
+deps_init_id_finish_lhs (void)
+{
+ deps_init_id_data.where = DEPS_IN_INSN;
+}
+
+/* Note a set of REGNO. */
+static void
+deps_init_id_note_reg_set (int regno)
+{
+ haifa_note_reg_set (regno);
+
+ if (deps_init_id_data.where == DEPS_IN_RHS)
+ deps_init_id_data.force_use_p = true;
+
+ if (IDATA_TYPE (deps_init_id_data.id) != PC)
+ SET_REGNO_REG_SET (IDATA_REG_SETS (deps_init_id_data.id), regno);
+
+#ifdef STACK_REGS
+ /* Make instructions that set stack registers to be ineligible for
+ renaming to avoid issues with find_used_regs. */
+ if (IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG))
+ deps_init_id_data.force_use_p = true;
+#endif
+}
+
+/* Note a clobber of REGNO. */
+static void
+deps_init_id_note_reg_clobber (int regno)
+{
+ haifa_note_reg_clobber (regno);
+
+ if (deps_init_id_data.where == DEPS_IN_RHS)
+ deps_init_id_data.force_use_p = true;
+
+ if (IDATA_TYPE (deps_init_id_data.id) != PC)
+ SET_REGNO_REG_SET (IDATA_REG_CLOBBERS (deps_init_id_data.id), regno);
+}
+
+/* Note a use of REGNO. */
+static void
+deps_init_id_note_reg_use (int regno)
+{
+ haifa_note_reg_use (regno);
+
+ if (IDATA_TYPE (deps_init_id_data.id) != PC)
+ SET_REGNO_REG_SET (IDATA_REG_USES (deps_init_id_data.id), regno);
+}
+
+/* Start initializing rhs data. */
+static void
+deps_init_id_start_rhs (rtx rhs)
+{
+ gcc_assert (deps_init_id_data.where == DEPS_IN_INSN);
+
+ /* And there was no sel_deps_reset_to_insn (). */
+ if (IDATA_LHS (deps_init_id_data.id) != NULL)
+ {
+ IDATA_RHS (deps_init_id_data.id) = rhs;
+ deps_init_id_data.where = DEPS_IN_RHS;
+ }
+}
+
+/* Finish initializing rhs data. */
+static void
+deps_init_id_finish_rhs (void)
+{
+ gcc_assert (deps_init_id_data.where == DEPS_IN_RHS
+ || deps_init_id_data.where == DEPS_IN_INSN);
+ deps_init_id_data.where = DEPS_IN_INSN;
+}
+
+/* Finish initializing insn data. */
+static void
+deps_init_id_finish_insn (void)
+{
+ gcc_assert (deps_init_id_data.where == DEPS_IN_INSN);
+
+ if (IDATA_TYPE (deps_init_id_data.id) == SET)
+ {
+ rtx lhs = IDATA_LHS (deps_init_id_data.id);
+ rtx rhs = IDATA_RHS (deps_init_id_data.id);
+
+ if (lhs == NULL || rhs == NULL || !lhs_and_rhs_separable_p (lhs, rhs)
+ || deps_init_id_data.force_use_p)
+ {
+ /* This should be a USE, as we don't want to schedule its RHS
+ separately. However, we still want to have them recorded
+ for the purposes of substitution. That's why we don't
+ simply call downgrade_to_use () here. */
+ gcc_assert (IDATA_TYPE (deps_init_id_data.id) == SET);
+ gcc_assert (!lhs == !rhs);
+
+ IDATA_TYPE (deps_init_id_data.id) = USE;
+ }
+ }
+
+ deps_init_id_data.where = DEPS_IN_NOWHERE;
+}
+
+/* This is dependence info used for initializing insn's data. */
+static struct sched_deps_info_def deps_init_id_sched_deps_info;
+
+/* This initializes most of the static part of the above structure. */
+static const struct sched_deps_info_def const_deps_init_id_sched_deps_info =
+ {
+ NULL,
+
+ deps_init_id_start_insn,
+ deps_init_id_finish_insn,
+ deps_init_id_start_lhs,
+ deps_init_id_finish_lhs,
+ deps_init_id_start_rhs,
+ deps_init_id_finish_rhs,
+ deps_init_id_note_reg_set,
+ deps_init_id_note_reg_clobber,
+ deps_init_id_note_reg_use,
+ NULL, /* note_mem_dep */
+ NULL, /* note_dep */
+
+ 0, /* use_cselib */
+ 0, /* use_deps_list */
+ 0 /* generate_spec_deps */
+ };
+
+/* Initialize INSN's lhs and rhs in ID. When FORCE_UNIQUE_P is true,
+ we don't actually need information about lhs and rhs. */
+static void
+setup_id_lhs_rhs (idata_t id, insn_t insn, bool force_unique_p)
+{
+ rtx pat = PATTERN (insn);
+
+ if (NONJUMP_INSN_P (insn)
+ && GET_CODE (pat) == SET
+ && !force_unique_p)
+ {
+ IDATA_RHS (id) = SET_SRC (pat);
+ IDATA_LHS (id) = SET_DEST (pat);
+ }
+ else
+ IDATA_LHS (id) = IDATA_RHS (id) = NULL;
+}
+
+/* Possibly downgrade INSN to USE. */
+static void
+maybe_downgrade_id_to_use (idata_t id, insn_t insn)
+{
+ bool must_be_use = false;
+ unsigned uid = INSN_UID (insn);
+ df_ref *rec;
+ rtx lhs = IDATA_LHS (id);
+ rtx rhs = IDATA_RHS (id);
+
+ /* We downgrade only SETs. */
+ if (IDATA_TYPE (id) != SET)
+ return;
+
+ if (!lhs || !lhs_and_rhs_separable_p (lhs, rhs))
+ {
+ IDATA_TYPE (id) = USE;
+ return;
+ }
+
+ for (rec = DF_INSN_UID_DEFS (uid); *rec; rec++)
+ {
+ df_ref def = *rec;
+
+ if (DF_REF_INSN (def)
+ && DF_REF_FLAGS_IS_SET (def, DF_REF_PRE_POST_MODIFY)
+ && loc_mentioned_in_p (DF_REF_LOC (def), IDATA_RHS (id)))
+ {
+ must_be_use = true;
+ break;
+ }
+
+#ifdef STACK_REGS
+ /* Make instructions that set stack registers to be ineligible for
+ renaming to avoid issues with find_used_regs. */
+ if (IN_RANGE (DF_REF_REGNO (def), FIRST_STACK_REG, LAST_STACK_REG))
+ {
+ must_be_use = true;
+ break;
+ }
+#endif
+ }
+
+ if (must_be_use)
+ IDATA_TYPE (id) = USE;
+}
+
+/* Setup register sets describing INSN in ID. */
+static void
+setup_id_reg_sets (idata_t id, insn_t insn)
+{
+ unsigned uid = INSN_UID (insn);
+ df_ref *rec;
+ regset tmp = get_clear_regset_from_pool ();
+
+ for (rec = DF_INSN_UID_DEFS (uid); *rec; rec++)
+ {
+ df_ref def = *rec;
+ unsigned int regno = DF_REF_REGNO (def);
+
+ /* Post modifies are treated like clobbers by sched-deps.c. */
+ if (DF_REF_FLAGS_IS_SET (def, (DF_REF_MUST_CLOBBER
+ | DF_REF_PRE_POST_MODIFY)))
+ SET_REGNO_REG_SET (IDATA_REG_CLOBBERS (id), regno);
+ else if (! DF_REF_FLAGS_IS_SET (def, DF_REF_MAY_CLOBBER))
+ {
+ SET_REGNO_REG_SET (IDATA_REG_SETS (id), regno);
+
+#ifdef STACK_REGS
+ /* For stack registers, treat writes to them as writes
+ to the first one to be consistent with sched-deps.c. */
+ if (IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG))
+ SET_REGNO_REG_SET (IDATA_REG_SETS (id), FIRST_STACK_REG);
+#endif
+ }
+ /* Mark special refs that generate read/write def pair. */
+ if (DF_REF_FLAGS_IS_SET (def, DF_REF_CONDITIONAL)
+ || regno == STACK_POINTER_REGNUM)
+ bitmap_set_bit (tmp, regno);
+ }
+
+ for (rec = DF_INSN_UID_USES (uid); *rec; rec++)
+ {
+ df_ref use = *rec;
+ unsigned int regno = DF_REF_REGNO (use);
+
+ /* When these refs are met for the first time, skip them, as
+ these uses are just counterparts of some defs. */
+ if (bitmap_bit_p (tmp, regno))
+ bitmap_clear_bit (tmp, regno);
+ else if (! DF_REF_FLAGS_IS_SET (use, DF_REF_CALL_STACK_USAGE))
+ {
+ SET_REGNO_REG_SET (IDATA_REG_USES (id), regno);
+
+#ifdef STACK_REGS
+ /* For stack registers, treat reads from them as reads from
+ the first one to be consistent with sched-deps.c. */
+ if (IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG))
+ SET_REGNO_REG_SET (IDATA_REG_USES (id), FIRST_STACK_REG);
+#endif
+ }
+ }
+
+ return_regset_to_pool (tmp);
+}
+
+/* Initialize instruction data for INSN in ID using DF's data. */
+static void
+init_id_from_df (idata_t id, insn_t insn, bool force_unique_p)
+{
+ gcc_assert (DF_INSN_UID_SAFE_GET (INSN_UID (insn)) != NULL);
+
+ setup_id_for_insn (id, insn, force_unique_p);
+ setup_id_lhs_rhs (id, insn, force_unique_p);
+
+ if (INSN_NOP_P (insn))
+ return;
+
+ maybe_downgrade_id_to_use (id, insn);
+ setup_id_reg_sets (id, insn);
+}
+
+/* Initialize instruction data for INSN in ID. */
+static void
+deps_init_id (idata_t id, insn_t insn, bool force_unique_p)
+{
+ struct deps_desc _dc, *dc = &_dc;
+
+ deps_init_id_data.where = DEPS_IN_NOWHERE;
+ deps_init_id_data.id = id;
+ deps_init_id_data.force_unique_p = force_unique_p;
+ deps_init_id_data.force_use_p = false;
+
+ init_deps (dc, false);
+
+ memcpy (&deps_init_id_sched_deps_info,
+ &const_deps_init_id_sched_deps_info,
+ sizeof (deps_init_id_sched_deps_info));
+
+ if (spec_info != NULL)
+ deps_init_id_sched_deps_info.generate_spec_deps = 1;
+
+ sched_deps_info = &deps_init_id_sched_deps_info;
+
+ deps_analyze_insn (dc, insn);
+
+ free_deps (dc);
+
+ deps_init_id_data.id = NULL;
+}
+
+
+struct sched_scan_info_def
+{
+ /* This hook notifies scheduler frontend to extend its internal per basic
+ block data structures. This hook should be called once before a series of
+ calls to bb_init (). */
+ void (*extend_bb) (void);
+
+ /* This hook makes scheduler frontend to initialize its internal data
+ structures for the passed basic block. */
+ void (*init_bb) (basic_block);
+
+ /* This hook notifies scheduler frontend to extend its internal per insn data
+ structures. This hook should be called once before a series of calls to
+ insn_init (). */
+ void (*extend_insn) (void);
+
+ /* This hook makes scheduler frontend to initialize its internal data
+ structures for the passed insn. */
+ void (*init_insn) (rtx);
+};
+
+/* A driver function to add a set of basic blocks (BBS) to the
+ scheduling region. */
+static void
+sched_scan (const struct sched_scan_info_def *ssi, bb_vec_t bbs)
+{
+ unsigned i;
+ basic_block bb;
+
+ if (ssi->extend_bb)
+ ssi->extend_bb ();
+
+ if (ssi->init_bb)
+ FOR_EACH_VEC_ELT (bbs, i, bb)
+ ssi->init_bb (bb);
+
+ if (ssi->extend_insn)
+ ssi->extend_insn ();
+
+ if (ssi->init_insn)
+ FOR_EACH_VEC_ELT (bbs, i, bb)
+ {
+ rtx insn;
+
+ FOR_BB_INSNS (bb, insn)
+ ssi->init_insn (insn);
+ }
+}
+
+/* Implement hooks for collecting fundamental insn properties like if insn is
+ an ASM or is within a SCHED_GROUP. */
+
+/* True when a "one-time init" data for INSN was already inited. */
+static bool
+first_time_insn_init (insn_t insn)
+{
+ return INSN_LIVE (insn) == NULL;
+}
+
+/* Hash an entry in a transformed_insns hashtable. */
+static hashval_t
+hash_transformed_insns (const void *p)
+{
+ return VINSN_HASH_RTX (((const struct transformed_insns *) p)->vinsn_old);
+}
+
+/* Compare the entries in a transformed_insns hashtable. */
+static int
+eq_transformed_insns (const void *p, const void *q)
+{
+ rtx i1 = VINSN_INSN_RTX (((const struct transformed_insns *) p)->vinsn_old);
+ rtx i2 = VINSN_INSN_RTX (((const struct transformed_insns *) q)->vinsn_old);
+
+ if (INSN_UID (i1) == INSN_UID (i2))
+ return 1;
+ return rtx_equal_p (PATTERN (i1), PATTERN (i2));
+}
+
+/* Free an entry in a transformed_insns hashtable. */
+static void
+free_transformed_insns (void *p)
+{
+ struct transformed_insns *pti = (struct transformed_insns *) p;
+
+ vinsn_detach (pti->vinsn_old);
+ vinsn_detach (pti->vinsn_new);
+ free (pti);
+}
+
+/* Init the s_i_d data for INSN which should be inited just once, when
+ we first see the insn. */
+static void
+init_first_time_insn_data (insn_t insn)
+{
+ /* This should not be set if this is the first time we init data for
+ insn. */
+ gcc_assert (first_time_insn_init (insn));
+
+ /* These are needed for nops too. */
+ INSN_LIVE (insn) = get_regset_from_pool ();
+ INSN_LIVE_VALID_P (insn) = false;
+
+ if (!INSN_NOP_P (insn))
+ {
+ INSN_ANALYZED_DEPS (insn) = BITMAP_ALLOC (NULL);
+ INSN_FOUND_DEPS (insn) = BITMAP_ALLOC (NULL);
+ INSN_TRANSFORMED_INSNS (insn)
+ = htab_create (16, hash_transformed_insns,
+ eq_transformed_insns, free_transformed_insns);
+ init_deps (&INSN_DEPS_CONTEXT (insn), true);
+ }
+}
+
+/* Free almost all above data for INSN that is scheduled already.
+ Used for extra-large basic blocks. */
+void
+free_data_for_scheduled_insn (insn_t insn)
+{
+ gcc_assert (! first_time_insn_init (insn));
+
+ if (! INSN_ANALYZED_DEPS (insn))
+ return;
+
+ BITMAP_FREE (INSN_ANALYZED_DEPS (insn));
+ BITMAP_FREE (INSN_FOUND_DEPS (insn));
+ htab_delete (INSN_TRANSFORMED_INSNS (insn));
+
+ /* This is allocated only for bookkeeping insns. */
+ if (INSN_ORIGINATORS (insn))
+ BITMAP_FREE (INSN_ORIGINATORS (insn));
+ free_deps (&INSN_DEPS_CONTEXT (insn));
+
+ INSN_ANALYZED_DEPS (insn) = NULL;
+
+ /* Clear the readonly flag so we would ICE when trying to recalculate
+ the deps context (as we believe that it should not happen). */
+ (&INSN_DEPS_CONTEXT (insn))->readonly = 0;
+}
+
+/* Free the same data as above for INSN. */
+static void
+free_first_time_insn_data (insn_t insn)
+{
+ gcc_assert (! first_time_insn_init (insn));
+
+ free_data_for_scheduled_insn (insn);
+ return_regset_to_pool (INSN_LIVE (insn));
+ INSN_LIVE (insn) = NULL;
+ INSN_LIVE_VALID_P (insn) = false;
+}
+
+/* Initialize region-scope data structures for basic blocks. */
+static void
+init_global_and_expr_for_bb (basic_block bb)
+{
+ if (sel_bb_empty_p (bb))
+ return;
+
+ invalidate_av_set (bb);
+}
+
+/* Data for global dependency analysis (to initialize CANT_MOVE and
+ SCHED_GROUP_P). */
+static struct
+{
+ /* Previous insn. */
+ insn_t prev_insn;
+} init_global_data;
+
+/* Determine if INSN is in the sched_group, is an asm or should not be
+ cloned. After that initialize its expr. */
+static void
+init_global_and_expr_for_insn (insn_t insn)
+{
+ if (LABEL_P (insn))
+ return;
+
+ if (NOTE_INSN_BASIC_BLOCK_P (insn))
+ {
+ init_global_data.prev_insn = NULL_RTX;
+ return;
+ }
+
+ gcc_assert (INSN_P (insn));
+
+ if (SCHED_GROUP_P (insn))
+ /* Setup a sched_group. */
+ {
+ insn_t prev_insn = init_global_data.prev_insn;
+
+ if (prev_insn)
+ INSN_SCHED_NEXT (prev_insn) = insn;
+
+ init_global_data.prev_insn = insn;
+ }
+ else
+ init_global_data.prev_insn = NULL_RTX;
+
+ if (GET_CODE (PATTERN (insn)) == ASM_INPUT
+ || asm_noperands (PATTERN (insn)) >= 0)
+ /* Mark INSN as an asm. */
+ INSN_ASM_P (insn) = true;
+
+ {
+ bool force_unique_p;
+ ds_t spec_done_ds;
+
+ /* Certain instructions cannot be cloned, and frame related insns and
+ the insn adjacent to NOTE_INSN_EPILOGUE_BEG cannot be moved out of
+ their block. */
+ if (prologue_epilogue_contains (insn))
+ {
+ if (RTX_FRAME_RELATED_P (insn))
+ CANT_MOVE (insn) = 1;
+ else
+ {
+ rtx note;
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_SAVE_NOTE
+ && ((enum insn_note) INTVAL (XEXP (note, 0))
+ == NOTE_INSN_EPILOGUE_BEG))
+ {
+ CANT_MOVE (insn) = 1;
+ break;
+ }
+ }
+ force_unique_p = true;
+ }
+ else
+ if (CANT_MOVE (insn)
+ || INSN_ASM_P (insn)
+ || SCHED_GROUP_P (insn)
+ || CALL_P (insn)
+ /* Exception handling insns are always unique. */
+ || (cfun->can_throw_non_call_exceptions && can_throw_internal (insn))
+ /* TRAP_IF though have an INSN code is control_flow_insn_p (). */
+ || control_flow_insn_p (insn)
+ || volatile_insn_p (PATTERN (insn))
+ || (targetm.cannot_copy_insn_p
+ && targetm.cannot_copy_insn_p (insn)))
+ force_unique_p = true;
+ else
+ force_unique_p = false;
+
+ if (targetm.sched.get_insn_spec_ds)
+ {
+ spec_done_ds = targetm.sched.get_insn_spec_ds (insn);
+ spec_done_ds = ds_get_max_dep_weak (spec_done_ds);
+ }
+ else
+ spec_done_ds = 0;
+
+ /* Initialize INSN's expr. */
+ init_expr (INSN_EXPR (insn), vinsn_create (insn, force_unique_p), 0,
+ REG_BR_PROB_BASE, INSN_PRIORITY (insn), 0, BLOCK_NUM (insn),
+ spec_done_ds, 0, 0, vNULL, true,
+ false, false, false, CANT_MOVE (insn));
+ }
+
+ init_first_time_insn_data (insn);
+}
+
+/* Scan the region and initialize instruction data for basic blocks BBS. */
+void
+sel_init_global_and_expr (bb_vec_t bbs)
+{
+ /* ??? It would be nice to implement push / pop scheme for sched_infos. */
+ const struct sched_scan_info_def ssi =
+ {
+ NULL, /* extend_bb */
+ init_global_and_expr_for_bb, /* init_bb */
+ extend_insn_data, /* extend_insn */
+ init_global_and_expr_for_insn /* init_insn */
+ };
+
+ sched_scan (&ssi, bbs);
+}
+
+/* Finalize region-scope data structures for basic blocks. */
+static void
+finish_global_and_expr_for_bb (basic_block bb)
+{
+ av_set_clear (&BB_AV_SET (bb));
+ BB_AV_LEVEL (bb) = 0;
+}
+
+/* Finalize INSN's data. */
+static void
+finish_global_and_expr_insn (insn_t insn)
+{
+ if (LABEL_P (insn) || NOTE_INSN_BASIC_BLOCK_P (insn))
+ return;
+
+ gcc_assert (INSN_P (insn));
+
+ if (INSN_LUID (insn) > 0)
+ {
+ free_first_time_insn_data (insn);
+ INSN_WS_LEVEL (insn) = 0;
+ CANT_MOVE (insn) = 0;
+
+ /* We can no longer assert this, as vinsns of this insn could be
+ easily live in other insn's caches. This should be changed to
+ a counter-like approach among all vinsns. */
+ gcc_assert (true || VINSN_COUNT (INSN_VINSN (insn)) == 1);
+ clear_expr (INSN_EXPR (insn));
+ }
+}
+
+/* Finalize per instruction data for the whole region. */
+void
+sel_finish_global_and_expr (void)
+{
+ {
+ bb_vec_t bbs;
+ int i;
+
+ bbs.create (current_nr_blocks);
+
+ for (i = 0; i < current_nr_blocks; i++)
+ bbs.quick_push (BASIC_BLOCK_FOR_FN (cfun, BB_TO_BLOCK (i)));
+
+ /* Clear AV_SETs and INSN_EXPRs. */
+ {
+ const struct sched_scan_info_def ssi =
+ {
+ NULL, /* extend_bb */
+ finish_global_and_expr_for_bb, /* init_bb */
+ NULL, /* extend_insn */
+ finish_global_and_expr_insn /* init_insn */
+ };
+
+ sched_scan (&ssi, bbs);
+ }
+
+ bbs.release ();
+ }
+
+ finish_insns ();
+}
+
+
+/* In the below hooks, we merely calculate whether or not a dependence
+ exists, and in what part of insn. However, we will need more data
+ when we'll start caching dependence requests. */
+
+/* Container to hold information for dependency analysis. */
+static struct
+{
+ deps_t dc;
+
+ /* A variable to track which part of rtx we are scanning in
+ sched-deps.c: sched_analyze_insn (). */
+ deps_where_t where;
+
+ /* Current producer. */
+ insn_t pro;
+
+ /* Current consumer. */
+ vinsn_t con;
+
+ /* Is SEL_DEPS_HAS_DEP_P[DEPS_IN_X] is true, then X has a dependence.
+ X is from { INSN, LHS, RHS }. */
+ ds_t has_dep_p[DEPS_IN_NOWHERE];
+} has_dependence_data;
+
+/* Start analyzing dependencies of INSN. */
+static void
+has_dependence_start_insn (insn_t insn ATTRIBUTE_UNUSED)
+{
+ gcc_assert (has_dependence_data.where == DEPS_IN_NOWHERE);
+
+ has_dependence_data.where = DEPS_IN_INSN;
+}
+
+/* Finish analyzing dependencies of an insn. */
+static void
+has_dependence_finish_insn (void)
+{
+ gcc_assert (has_dependence_data.where == DEPS_IN_INSN);
+
+ has_dependence_data.where = DEPS_IN_NOWHERE;
+}
+
+/* Start analyzing dependencies of LHS. */
+static void
+has_dependence_start_lhs (rtx lhs ATTRIBUTE_UNUSED)
+{
+ gcc_assert (has_dependence_data.where == DEPS_IN_INSN);
+
+ if (VINSN_LHS (has_dependence_data.con) != NULL)
+ has_dependence_data.where = DEPS_IN_LHS;
+}
+
+/* Finish analyzing dependencies of an lhs. */
+static void
+has_dependence_finish_lhs (void)
+{
+ has_dependence_data.where = DEPS_IN_INSN;
+}
+
+/* Start analyzing dependencies of RHS. */
+static void
+has_dependence_start_rhs (rtx rhs ATTRIBUTE_UNUSED)
+{
+ gcc_assert (has_dependence_data.where == DEPS_IN_INSN);
+
+ if (VINSN_RHS (has_dependence_data.con) != NULL)
+ has_dependence_data.where = DEPS_IN_RHS;
+}
+
+/* Start analyzing dependencies of an rhs. */
+static void
+has_dependence_finish_rhs (void)
+{
+ gcc_assert (has_dependence_data.where == DEPS_IN_RHS
+ || has_dependence_data.where == DEPS_IN_INSN);
+
+ has_dependence_data.where = DEPS_IN_INSN;
+}
+
+/* Note a set of REGNO. */
+static void
+has_dependence_note_reg_set (int regno)
+{
+ struct deps_reg *reg_last = &has_dependence_data.dc->reg_last[regno];
+
+ if (!sched_insns_conditions_mutex_p (has_dependence_data.pro,
+ VINSN_INSN_RTX
+ (has_dependence_data.con)))
+ {
+ ds_t *dsp = &has_dependence_data.has_dep_p[has_dependence_data.where];
+
+ if (reg_last->sets != NULL
+ || reg_last->clobbers != NULL)
+ *dsp = (*dsp & ~SPECULATIVE) | DEP_OUTPUT;
+
+ if (reg_last->uses || reg_last->implicit_sets)
+ *dsp = (*dsp & ~SPECULATIVE) | DEP_ANTI;
+ }
+}
+
+/* Note a clobber of REGNO. */
+static void
+has_dependence_note_reg_clobber (int regno)
+{
+ struct deps_reg *reg_last = &has_dependence_data.dc->reg_last[regno];
+
+ if (!sched_insns_conditions_mutex_p (has_dependence_data.pro,
+ VINSN_INSN_RTX
+ (has_dependence_data.con)))
+ {
+ ds_t *dsp = &has_dependence_data.has_dep_p[has_dependence_data.where];
+
+ if (reg_last->sets)
+ *dsp = (*dsp & ~SPECULATIVE) | DEP_OUTPUT;
+
+ if (reg_last->uses || reg_last->implicit_sets)
+ *dsp = (*dsp & ~SPECULATIVE) | DEP_ANTI;
+ }
+}
+
+/* Note a use of REGNO. */
+static void
+has_dependence_note_reg_use (int regno)
+{
+ struct deps_reg *reg_last = &has_dependence_data.dc->reg_last[regno];
+
+ if (!sched_insns_conditions_mutex_p (has_dependence_data.pro,
+ VINSN_INSN_RTX
+ (has_dependence_data.con)))
+ {
+ ds_t *dsp = &has_dependence_data.has_dep_p[has_dependence_data.where];
+
+ if (reg_last->sets)
+ *dsp = (*dsp & ~SPECULATIVE) | DEP_TRUE;
+
+ if (reg_last->clobbers || reg_last->implicit_sets)
+ *dsp = (*dsp & ~SPECULATIVE) | DEP_ANTI;
+
+ /* Merge BE_IN_SPEC bits into *DSP when the dependency producer
+ is actually a check insn. We need to do this for any register
+ read-read dependency with the check unless we track properly
+ all registers written by BE_IN_SPEC-speculated insns, as
+ we don't have explicit dependence lists. See PR 53975. */
+ if (reg_last->uses)
+ {
+ ds_t pro_spec_checked_ds;
+
+ pro_spec_checked_ds = INSN_SPEC_CHECKED_DS (has_dependence_data.pro);
+ pro_spec_checked_ds = ds_get_max_dep_weak (pro_spec_checked_ds);
+
+ if (pro_spec_checked_ds != 0)
+ *dsp = ds_full_merge (*dsp, pro_spec_checked_ds,
+ NULL_RTX, NULL_RTX);
+ }
+ }
+}
+
+/* Note a memory dependence. */
+static void
+has_dependence_note_mem_dep (rtx mem ATTRIBUTE_UNUSED,
+ rtx pending_mem ATTRIBUTE_UNUSED,
+ insn_t pending_insn ATTRIBUTE_UNUSED,
+ ds_t ds ATTRIBUTE_UNUSED)
+{
+ if (!sched_insns_conditions_mutex_p (has_dependence_data.pro,
+ VINSN_INSN_RTX (has_dependence_data.con)))
+ {
+ ds_t *dsp = &has_dependence_data.has_dep_p[has_dependence_data.where];
+
+ *dsp = ds_full_merge (ds, *dsp, pending_mem, mem);
+ }
+}
+
+/* Note a dependence. */
+static void
+has_dependence_note_dep (insn_t pro ATTRIBUTE_UNUSED,
+ ds_t ds ATTRIBUTE_UNUSED)
+{
+ if (!sched_insns_conditions_mutex_p (has_dependence_data.pro,
+ VINSN_INSN_RTX (has_dependence_data.con)))
+ {
+ ds_t *dsp = &has_dependence_data.has_dep_p[has_dependence_data.where];
+
+ *dsp = ds_full_merge (ds, *dsp, NULL_RTX, NULL_RTX);
+ }
+}
+
+/* Mark the insn as having a hard dependence that prevents speculation. */
+void
+sel_mark_hard_insn (rtx insn)
+{
+ int i;
+
+ /* Only work when we're in has_dependence_p mode.
+ ??? This is a hack, this should actually be a hook. */
+ if (!has_dependence_data.dc || !has_dependence_data.pro)
+ return;
+
+ gcc_assert (insn == VINSN_INSN_RTX (has_dependence_data.con));
+ gcc_assert (has_dependence_data.where == DEPS_IN_INSN);
+
+ for (i = 0; i < DEPS_IN_NOWHERE; i++)
+ has_dependence_data.has_dep_p[i] &= ~SPECULATIVE;
+}
+
+/* This structure holds the hooks for the dependency analysis used when
+ actually processing dependencies in the scheduler. */
+static struct sched_deps_info_def has_dependence_sched_deps_info;
+
+/* This initializes most of the fields of the above structure. */
+static const struct sched_deps_info_def const_has_dependence_sched_deps_info =
+ {
+ NULL,
+
+ has_dependence_start_insn,
+ has_dependence_finish_insn,
+ has_dependence_start_lhs,
+ has_dependence_finish_lhs,
+ has_dependence_start_rhs,
+ has_dependence_finish_rhs,
+ has_dependence_note_reg_set,
+ has_dependence_note_reg_clobber,
+ has_dependence_note_reg_use,
+ has_dependence_note_mem_dep,
+ has_dependence_note_dep,
+
+ 0, /* use_cselib */
+ 0, /* use_deps_list */
+ 0 /* generate_spec_deps */
+ };
+
+/* Initialize has_dependence_sched_deps_info with extra spec field. */
+static void
+setup_has_dependence_sched_deps_info (void)
+{
+ memcpy (&has_dependence_sched_deps_info,
+ &const_has_dependence_sched_deps_info,
+ sizeof (has_dependence_sched_deps_info));
+
+ if (spec_info != NULL)
+ has_dependence_sched_deps_info.generate_spec_deps = 1;
+
+ sched_deps_info = &has_dependence_sched_deps_info;
+}
+
+/* Remove all dependences found and recorded in has_dependence_data array. */
+void
+sel_clear_has_dependence (void)
+{
+ int i;
+
+ for (i = 0; i < DEPS_IN_NOWHERE; i++)
+ has_dependence_data.has_dep_p[i] = 0;
+}
+
+/* Return nonzero if EXPR has is dependent upon PRED. Return the pointer
+ to the dependence information array in HAS_DEP_PP. */
+ds_t
+has_dependence_p (expr_t expr, insn_t pred, ds_t **has_dep_pp)
+{
+ int i;
+ ds_t ds;
+ struct deps_desc *dc;
+
+ if (INSN_SIMPLEJUMP_P (pred))
+ /* Unconditional jump is just a transfer of control flow.
+ Ignore it. */
+ return false;
+
+ dc = &INSN_DEPS_CONTEXT (pred);
+
+ /* We init this field lazily. */
+ if (dc->reg_last == NULL)
+ init_deps_reg_last (dc);
+
+ if (!dc->readonly)
+ {
+ has_dependence_data.pro = NULL;
+ /* Initialize empty dep context with information about PRED. */
+ advance_deps_context (dc, pred);
+ dc->readonly = 1;
+ }
+
+ has_dependence_data.where = DEPS_IN_NOWHERE;
+ has_dependence_data.pro = pred;
+ has_dependence_data.con = EXPR_VINSN (expr);
+ has_dependence_data.dc = dc;
+
+ sel_clear_has_dependence ();
+
+ /* Now catch all dependencies that would be generated between PRED and
+ INSN. */
+ setup_has_dependence_sched_deps_info ();
+ deps_analyze_insn (dc, EXPR_INSN_RTX (expr));
+ has_dependence_data.dc = NULL;
+
+ /* When a barrier was found, set DEPS_IN_INSN bits. */
+ if (dc->last_reg_pending_barrier == TRUE_BARRIER)
+ has_dependence_data.has_dep_p[DEPS_IN_INSN] = DEP_TRUE;
+ else if (dc->last_reg_pending_barrier == MOVE_BARRIER)
+ has_dependence_data.has_dep_p[DEPS_IN_INSN] = DEP_ANTI;
+
+ /* Do not allow stores to memory to move through checks. Currently
+ we don't move this to sched-deps.c as the check doesn't have
+ obvious places to which this dependence can be attached.
+ FIMXE: this should go to a hook. */
+ if (EXPR_LHS (expr)
+ && MEM_P (EXPR_LHS (expr))
+ && sel_insn_is_speculation_check (pred))
+ has_dependence_data.has_dep_p[DEPS_IN_INSN] = DEP_ANTI;
+
+ *has_dep_pp = has_dependence_data.has_dep_p;
+ ds = 0;
+ for (i = 0; i < DEPS_IN_NOWHERE; i++)
+ ds = ds_full_merge (ds, has_dependence_data.has_dep_p[i],
+ NULL_RTX, NULL_RTX);
+
+ return ds;
+}
+
+
+/* Dependence hooks implementation that checks dependence latency constraints
+ on the insns being scheduled. The entry point for these routines is
+ tick_check_p predicate. */
+
+static struct
+{
+ /* An expr we are currently checking. */
+ expr_t expr;
+
+ /* A minimal cycle for its scheduling. */
+ int cycle;
+
+ /* Whether we have seen a true dependence while checking. */
+ bool seen_true_dep_p;
+} tick_check_data;
+
+/* Update minimal scheduling cycle for tick_check_insn given that it depends
+ on PRO with status DS and weight DW. */
+static void
+tick_check_dep_with_dw (insn_t pro_insn, ds_t ds, dw_t dw)
+{
+ expr_t con_expr = tick_check_data.expr;
+ insn_t con_insn = EXPR_INSN_RTX (con_expr);
+
+ if (con_insn != pro_insn)
+ {
+ enum reg_note dt;
+ int tick;
+
+ if (/* PROducer was removed from above due to pipelining. */
+ !INSN_IN_STREAM_P (pro_insn)
+ /* Or PROducer was originally on the next iteration regarding the
+ CONsumer. */
+ || (INSN_SCHED_TIMES (pro_insn)
+ - EXPR_SCHED_TIMES (con_expr)) > 1)
+ /* Don't count this dependence. */
+ return;
+
+ dt = ds_to_dt (ds);
+ if (dt == REG_DEP_TRUE)
+ tick_check_data.seen_true_dep_p = true;
+
+ gcc_assert (INSN_SCHED_CYCLE (pro_insn) > 0);
+
+ {
+ dep_def _dep, *dep = &_dep;
+
+ init_dep (dep, pro_insn, con_insn, dt);
+
+ tick = INSN_SCHED_CYCLE (pro_insn) + dep_cost_1 (dep, dw);
+ }
+
+ /* When there are several kinds of dependencies between pro and con,
+ only REG_DEP_TRUE should be taken into account. */
+ if (tick > tick_check_data.cycle
+ && (dt == REG_DEP_TRUE || !tick_check_data.seen_true_dep_p))
+ tick_check_data.cycle = tick;
+ }
+}
+
+/* An implementation of note_dep hook. */
+static void
+tick_check_note_dep (insn_t pro, ds_t ds)
+{
+ tick_check_dep_with_dw (pro, ds, 0);
+}
+
+/* An implementation of note_mem_dep hook. */
+static void
+tick_check_note_mem_dep (rtx mem1, rtx mem2, insn_t pro, ds_t ds)
+{
+ dw_t dw;
+
+ dw = (ds_to_dt (ds) == REG_DEP_TRUE
+ ? estimate_dep_weak (mem1, mem2)
+ : 0);
+
+ tick_check_dep_with_dw (pro, ds, dw);
+}
+
+/* This structure contains hooks for dependence analysis used when determining
+ whether an insn is ready for scheduling. */
+static struct sched_deps_info_def tick_check_sched_deps_info =
+ {
+ NULL,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ haifa_note_reg_set,
+ haifa_note_reg_clobber,
+ haifa_note_reg_use,
+ tick_check_note_mem_dep,
+ tick_check_note_dep,
+
+ 0, 0, 0
+ };
+
+/* Estimate number of cycles from the current cycle of FENCE until EXPR can be
+ scheduled. Return 0 if all data from producers in DC is ready. */
+int
+tick_check_p (expr_t expr, deps_t dc, fence_t fence)
+{
+ int cycles_left;
+ /* Initialize variables. */
+ tick_check_data.expr = expr;
+ tick_check_data.cycle = 0;
+ tick_check_data.seen_true_dep_p = false;
+ sched_deps_info = &tick_check_sched_deps_info;
+
+ gcc_assert (!dc->readonly);
+ dc->readonly = 1;
+ deps_analyze_insn (dc, EXPR_INSN_RTX (expr));
+ dc->readonly = 0;
+
+ cycles_left = tick_check_data.cycle - FENCE_CYCLE (fence);
+
+ return cycles_left >= 0 ? cycles_left : 0;
+}
+
+
+/* Functions to work with insns. */
+
+/* Returns true if LHS of INSN is the same as DEST of an insn
+ being moved. */
+bool
+lhs_of_insn_equals_to_dest_p (insn_t insn, rtx dest)
+{
+ rtx lhs = INSN_LHS (insn);
+
+ if (lhs == NULL || dest == NULL)
+ return false;
+
+ return rtx_equal_p (lhs, dest);
+}
+
+/* Return s_i_d entry of INSN. Callable from debugger. */
+sel_insn_data_def
+insn_sid (insn_t insn)
+{
+ return *SID (insn);
+}
+
+/* True when INSN is a speculative check. We can tell this by looking
+ at the data structures of the selective scheduler, not by examining
+ the pattern. */
+bool
+sel_insn_is_speculation_check (rtx insn)
+{
+ return s_i_d.exists () && !! INSN_SPEC_CHECKED_DS (insn);
+}
+
+/* Extracts machine mode MODE and destination location DST_LOC
+ for given INSN. */
+void
+get_dest_and_mode (rtx insn, rtx *dst_loc, enum machine_mode *mode)
+{
+ rtx pat = PATTERN (insn);
+
+ gcc_assert (dst_loc);
+ gcc_assert (GET_CODE (pat) == SET);
+
+ *dst_loc = SET_DEST (pat);
+
+ gcc_assert (*dst_loc);
+ gcc_assert (MEM_P (*dst_loc) || REG_P (*dst_loc));
+
+ if (mode)
+ *mode = GET_MODE (*dst_loc);
+}
+
+/* Returns true when moving through JUMP will result in bookkeeping
+ creation. */
+bool
+bookkeeping_can_be_created_if_moved_through_p (insn_t jump)
+{
+ insn_t succ;
+ succ_iterator si;
+
+ FOR_EACH_SUCC (succ, si, jump)
+ if (sel_num_cfg_preds_gt_1 (succ))
+ return true;
+
+ return false;
+}
+
+/* Return 'true' if INSN is the only one in its basic block. */
+static bool
+insn_is_the_only_one_in_bb_p (insn_t insn)
+{
+ return sel_bb_head_p (insn) && sel_bb_end_p (insn);
+}
+
+#ifdef ENABLE_CHECKING
+/* Check that the region we're scheduling still has at most one
+ backedge. */
+static void
+verify_backedges (void)
+{
+ if (pipelining_p)
+ {
+ int i, n = 0;
+ edge e;
+ edge_iterator ei;
+
+ for (i = 0; i < current_nr_blocks; i++)
+ FOR_EACH_EDGE (e, ei, BASIC_BLOCK_FOR_FN (cfun, BB_TO_BLOCK (i))->succs)
+ if (in_current_region_p (e->dest)
+ && BLOCK_TO_BB (e->dest->index) < i)
+ n++;
+
+ gcc_assert (n <= 1);
+ }
+}
+#endif
+
+
+/* Functions to work with control flow. */
+
+/* Recompute BLOCK_TO_BB and BB_FOR_BLOCK for current region so that blocks
+ are sorted in topological order (it might have been invalidated by
+ redirecting an edge). */
+static void
+sel_recompute_toporder (void)
+{
+ int i, n, rgn;
+ int *postorder, n_blocks;
+
+ postorder = XALLOCAVEC (int, n_basic_blocks_for_fn (cfun));
+ n_blocks = post_order_compute (postorder, false, false);
+
+ rgn = CONTAINING_RGN (BB_TO_BLOCK (0));
+ for (n = 0, i = n_blocks - 1; i >= 0; i--)
+ if (CONTAINING_RGN (postorder[i]) == rgn)
+ {
+ BLOCK_TO_BB (postorder[i]) = n;
+ BB_TO_BLOCK (n) = postorder[i];
+ n++;
+ }
+
+ /* Assert that we updated info for all blocks. We may miss some blocks if
+ this function is called when redirecting an edge made a block
+ unreachable, but that block is not deleted yet. */
+ gcc_assert (n == RGN_NR_BLOCKS (rgn));
+}
+
+/* Tidy the possibly empty block BB. */
+static bool
+maybe_tidy_empty_bb (basic_block bb)
+{
+ basic_block succ_bb, pred_bb, note_bb;
+ vec<basic_block> dom_bbs;
+ edge e;
+ edge_iterator ei;
+ bool rescan_p;
+
+ /* Keep empty bb only if this block immediately precedes EXIT and
+ has incoming non-fallthrough edge, or it has no predecessors or
+ successors. Otherwise remove it. */
+ if (!sel_bb_empty_p (bb)
+ || (single_succ_p (bb)
+ && single_succ (bb) == EXIT_BLOCK_PTR_FOR_FN (cfun)
+ && (!single_pred_p (bb)
+ || !(single_pred_edge (bb)->flags & EDGE_FALLTHRU)))
+ || EDGE_COUNT (bb->preds) == 0
+ || EDGE_COUNT (bb->succs) == 0)
+ return false;
+
+ /* Do not attempt to redirect complex edges. */
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (e->flags & EDGE_COMPLEX)
+ return false;
+ else if (e->flags & EDGE_FALLTHRU)
+ {
+ rtx note;
+ /* If prev bb ends with asm goto, see if any of the
+ ASM_OPERANDS_LABELs don't point to the fallthru
+ label. Do not attempt to redirect it in that case. */
+ if (JUMP_P (BB_END (e->src))
+ && (note = extract_asm_operands (PATTERN (BB_END (e->src)))))
+ {
+ int i, n = ASM_OPERANDS_LABEL_LENGTH (note);
+
+ for (i = 0; i < n; ++i)
+ if (XEXP (ASM_OPERANDS_LABEL (note, i), 0) == BB_HEAD (bb))
+ return false;
+ }
+ }
+
+ free_data_sets (bb);
+
+ /* Do not delete BB if it has more than one successor.
+ That can occur when we moving a jump. */
+ if (!single_succ_p (bb))
+ {
+ gcc_assert (can_merge_blocks_p (bb->prev_bb, bb));
+ sel_merge_blocks (bb->prev_bb, bb);
+ return true;
+ }
+
+ succ_bb = single_succ (bb);
+ rescan_p = true;
+ pred_bb = NULL;
+ dom_bbs.create (0);
+
+ /* Save a pred/succ from the current region to attach the notes to. */
+ note_bb = NULL;
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (in_current_region_p (e->src))
+ {
+ note_bb = e->src;
+ break;
+ }
+ if (note_bb == NULL)
+ note_bb = succ_bb;
+
+ /* Redirect all non-fallthru edges to the next bb. */
+ while (rescan_p)
+ {
+ rescan_p = false;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ pred_bb = e->src;
+
+ if (!(e->flags & EDGE_FALLTHRU))
+ {
+ /* We can not invalidate computed topological order by moving
+ the edge destination block (E->SUCC) along a fallthru edge.
+
+ We will update dominators here only when we'll get
+ an unreachable block when redirecting, otherwise
+ sel_redirect_edge_and_branch will take care of it. */
+ if (e->dest != bb
+ && single_pred_p (e->dest))
+ dom_bbs.safe_push (e->dest);
+ sel_redirect_edge_and_branch (e, succ_bb);
+ rescan_p = true;
+ break;
+ }
+ /* If the edge is fallthru, but PRED_BB ends in a conditional jump
+ to BB (so there is no non-fallthru edge from PRED_BB to BB), we
+ still have to adjust it. */
+ else if (single_succ_p (pred_bb) && any_condjump_p (BB_END (pred_bb)))
+ {
+ /* If possible, try to remove the unneeded conditional jump. */
+ if (INSN_SCHED_TIMES (BB_END (pred_bb)) == 0
+ && !IN_CURRENT_FENCE_P (BB_END (pred_bb)))
+ {
+ if (!sel_remove_insn (BB_END (pred_bb), false, false))
+ tidy_fallthru_edge (e);
+ }
+ else
+ sel_redirect_edge_and_branch (e, succ_bb);
+ rescan_p = true;
+ break;
+ }
+ }
+ }
+
+ if (can_merge_blocks_p (bb->prev_bb, bb))
+ sel_merge_blocks (bb->prev_bb, bb);
+ else
+ {
+ /* This is a block without fallthru predecessor. Just delete it. */
+ gcc_assert (note_bb);
+ move_bb_info (note_bb, bb);
+ remove_empty_bb (bb, true);
+ }
+
+ if (!dom_bbs.is_empty ())
+ {
+ dom_bbs.safe_push (succ_bb);
+ iterate_fix_dominators (CDI_DOMINATORS, dom_bbs, false);
+ dom_bbs.release ();
+ }
+
+ return true;
+}
+
+/* Tidy the control flow after we have removed original insn from
+ XBB. Return true if we have removed some blocks. When FULL_TIDYING
+ is true, also try to optimize control flow on non-empty blocks. */
+bool
+tidy_control_flow (basic_block xbb, bool full_tidying)
+{
+ bool changed = true;
+ insn_t first, last;
+
+ /* First check whether XBB is empty. */
+ changed = maybe_tidy_empty_bb (xbb);
+ if (changed || !full_tidying)
+ return changed;
+
+ /* Check if there is a unnecessary jump after insn left. */
+ if (bb_has_removable_jump_to_p (xbb, xbb->next_bb)
+ && INSN_SCHED_TIMES (BB_END (xbb)) == 0
+ && !IN_CURRENT_FENCE_P (BB_END (xbb)))
+ {
+ if (sel_remove_insn (BB_END (xbb), false, false))
+ return true;
+ tidy_fallthru_edge (EDGE_SUCC (xbb, 0));
+ }
+
+ first = sel_bb_head (xbb);
+ last = sel_bb_end (xbb);
+ if (MAY_HAVE_DEBUG_INSNS)
+ {
+ if (first != last && DEBUG_INSN_P (first))
+ do
+ first = NEXT_INSN (first);
+ while (first != last && (DEBUG_INSN_P (first) || NOTE_P (first)));
+
+ if (first != last && DEBUG_INSN_P (last))
+ do
+ last = PREV_INSN (last);
+ while (first != last && (DEBUG_INSN_P (last) || NOTE_P (last)));
+ }
+ /* Check if there is an unnecessary jump in previous basic block leading
+ to next basic block left after removing INSN from stream.
+ If it is so, remove that jump and redirect edge to current
+ basic block (where there was INSN before deletion). This way
+ when NOP will be deleted several instructions later with its
+ basic block we will not get a jump to next instruction, which
+ can be harmful. */
+ if (first == last
+ && !sel_bb_empty_p (xbb)
+ && INSN_NOP_P (last)
+ /* Flow goes fallthru from current block to the next. */
+ && EDGE_COUNT (xbb->succs) == 1
+ && (EDGE_SUCC (xbb, 0)->flags & EDGE_FALLTHRU)
+ /* When successor is an EXIT block, it may not be the next block. */
+ && single_succ (xbb) != EXIT_BLOCK_PTR_FOR_FN (cfun)
+ /* And unconditional jump in previous basic block leads to
+ next basic block of XBB and this jump can be safely removed. */
+ && in_current_region_p (xbb->prev_bb)
+ && bb_has_removable_jump_to_p (xbb->prev_bb, xbb->next_bb)
+ && INSN_SCHED_TIMES (BB_END (xbb->prev_bb)) == 0
+ /* Also this jump is not at the scheduling boundary. */
+ && !IN_CURRENT_FENCE_P (BB_END (xbb->prev_bb)))
+ {
+ bool recompute_toporder_p;
+ /* Clear data structures of jump - jump itself will be removed
+ by sel_redirect_edge_and_branch. */
+ clear_expr (INSN_EXPR (BB_END (xbb->prev_bb)));
+ recompute_toporder_p
+ = sel_redirect_edge_and_branch (EDGE_SUCC (xbb->prev_bb, 0), xbb);
+
+ gcc_assert (EDGE_SUCC (xbb->prev_bb, 0)->flags & EDGE_FALLTHRU);
+
+ /* It can turn out that after removing unused jump, basic block
+ that contained that jump, becomes empty too. In such case
+ remove it too. */
+ if (sel_bb_empty_p (xbb->prev_bb))
+ changed = maybe_tidy_empty_bb (xbb->prev_bb);
+ if (recompute_toporder_p)
+ sel_recompute_toporder ();
+ }
+
+#ifdef ENABLE_CHECKING
+ verify_backedges ();
+ verify_dominators (CDI_DOMINATORS);
+#endif
+
+ return changed;
+}
+
+/* Purge meaningless empty blocks in the middle of a region. */
+void
+purge_empty_blocks (void)
+{
+ int i;
+
+ /* Do not attempt to delete the first basic block in the region. */
+ for (i = 1; i < current_nr_blocks; )
+ {
+ basic_block b = BASIC_BLOCK_FOR_FN (cfun, BB_TO_BLOCK (i));
+
+ if (maybe_tidy_empty_bb (b))
+ continue;
+
+ i++;
+ }
+}
+
+/* Rip-off INSN from the insn stream. When ONLY_DISCONNECT is true,
+ do not delete insn's data, because it will be later re-emitted.
+ Return true if we have removed some blocks afterwards. */
+bool
+sel_remove_insn (insn_t insn, bool only_disconnect, bool full_tidying)
+{
+ basic_block bb = BLOCK_FOR_INSN (insn);
+
+ gcc_assert (INSN_IN_STREAM_P (insn));
+
+ if (DEBUG_INSN_P (insn) && BB_AV_SET_VALID_P (bb))
+ {
+ expr_t expr;
+ av_set_iterator i;
+
+ /* When we remove a debug insn that is head of a BB, it remains
+ in the AV_SET of the block, but it shouldn't. */
+ FOR_EACH_EXPR_1 (expr, i, &BB_AV_SET (bb))
+ if (EXPR_INSN_RTX (expr) == insn)
+ {
+ av_set_iter_remove (&i);
+ break;
+ }
+ }
+
+ if (only_disconnect)
+ remove_insn (insn);
+ else
+ {
+ delete_insn (insn);
+ clear_expr (INSN_EXPR (insn));
+ }
+
+ /* It is necessary to NULL these fields in case we are going to re-insert
+ INSN into the insns stream, as will usually happen in the ONLY_DISCONNECT
+ case, but also for NOPs that we will return to the nop pool. */
+ PREV_INSN (insn) = NULL_RTX;
+ NEXT_INSN (insn) = NULL_RTX;
+ set_block_for_insn (insn, NULL);
+
+ return tidy_control_flow (bb, full_tidying);
+}
+
+/* Estimate number of the insns in BB. */
+static int
+sel_estimate_number_of_insns (basic_block bb)
+{
+ int res = 0;
+ insn_t insn = NEXT_INSN (BB_HEAD (bb)), next_tail = NEXT_INSN (BB_END (bb));
+
+ for (; insn != next_tail; insn = NEXT_INSN (insn))
+ if (NONDEBUG_INSN_P (insn))
+ res++;
+
+ return res;
+}
+
+/* We don't need separate luids for notes or labels. */
+static int
+sel_luid_for_non_insn (rtx x)
+{
+ gcc_assert (NOTE_P (x) || LABEL_P (x));
+
+ return -1;
+}
+
+/* Find the proper seqno for inserting at INSN by successors.
+ Return -1 if no successors with positive seqno exist. */
+static int
+get_seqno_by_succs (rtx insn)
+{
+ basic_block bb = BLOCK_FOR_INSN (insn);
+ rtx tmp = insn, end = BB_END (bb);
+ int seqno;
+ insn_t succ = NULL;
+ succ_iterator si;
+
+ while (tmp != end)
+ {
+ tmp = NEXT_INSN (tmp);
+ if (INSN_P (tmp))
+ return INSN_SEQNO (tmp);
+ }
+
+ seqno = INT_MAX;
+
+ FOR_EACH_SUCC_1 (succ, si, end, SUCCS_NORMAL)
+ if (INSN_SEQNO (succ) > 0)
+ seqno = MIN (seqno, INSN_SEQNO (succ));
+
+ if (seqno == INT_MAX)
+ return -1;
+
+ return seqno;
+}
+
+/* Compute seqno for INSN by its preds or succs. */
+static int
+get_seqno_for_a_jump (insn_t insn)
+{
+ int seqno;
+
+ gcc_assert (INSN_SIMPLEJUMP_P (insn));
+
+ if (!sel_bb_head_p (insn))
+ seqno = INSN_SEQNO (PREV_INSN (insn));
+ else
+ {
+ basic_block bb = BLOCK_FOR_INSN (insn);
+
+ if (single_pred_p (bb)
+ && !in_current_region_p (single_pred (bb)))
+ {
+ /* We can have preds outside a region when splitting edges
+ for pipelining of an outer loop. Use succ instead.
+ There should be only one of them. */
+ insn_t succ = NULL;
+ succ_iterator si;
+ bool first = true;
+
+ gcc_assert (flag_sel_sched_pipelining_outer_loops
+ && current_loop_nest);
+ FOR_EACH_SUCC_1 (succ, si, insn,
+ SUCCS_NORMAL | SUCCS_SKIP_TO_LOOP_EXITS)
+ {
+ gcc_assert (first);
+ first = false;
+ }
+
+ gcc_assert (succ != NULL);
+ seqno = INSN_SEQNO (succ);
+ }
+ else
+ {
+ insn_t *preds;
+ int n;
+
+ cfg_preds (BLOCK_FOR_INSN (insn), &preds, &n);
+
+ gcc_assert (n > 0);
+ /* For one predecessor, use simple method. */
+ if (n == 1)
+ seqno = INSN_SEQNO (preds[0]);
+ else
+ seqno = get_seqno_by_preds (insn);
+
+ free (preds);
+ }
+ }
+
+ /* We were unable to find a good seqno among preds. */
+ if (seqno < 0)
+ seqno = get_seqno_by_succs (insn);
+
+ gcc_assert (seqno >= 0);
+
+ return seqno;
+}
+
+/* Find the proper seqno for inserting at INSN. Returns -1 if no predecessors
+ with positive seqno exist. */
+int
+get_seqno_by_preds (rtx insn)
+{
+ basic_block bb = BLOCK_FOR_INSN (insn);
+ rtx tmp = insn, head = BB_HEAD (bb);
+ insn_t *preds;
+ int n, i, seqno;
+
+ while (tmp != head)
+ {
+ tmp = PREV_INSN (tmp);
+ if (INSN_P (tmp))
+ return INSN_SEQNO (tmp);
+ }
+
+ cfg_preds (bb, &preds, &n);
+ for (i = 0, seqno = -1; i < n; i++)
+ seqno = MAX (seqno, INSN_SEQNO (preds[i]));
+
+ return seqno;
+}
+
+
+
+/* Extend pass-scope data structures for basic blocks. */
+void
+sel_extend_global_bb_info (void)
+{
+ sel_global_bb_info.safe_grow_cleared (last_basic_block_for_fn (cfun));
+}
+
+/* Extend region-scope data structures for basic blocks. */
+static void
+extend_region_bb_info (void)
+{
+ sel_region_bb_info.safe_grow_cleared (last_basic_block_for_fn (cfun));
+}
+
+/* Extend all data structures to fit for all basic blocks. */
+static void
+extend_bb_info (void)
+{
+ sel_extend_global_bb_info ();
+ extend_region_bb_info ();
+}
+
+/* Finalize pass-scope data structures for basic blocks. */
+void
+sel_finish_global_bb_info (void)
+{
+ sel_global_bb_info.release ();
+}
+
+/* Finalize region-scope data structures for basic blocks. */
+static void
+finish_region_bb_info (void)
+{
+ sel_region_bb_info.release ();
+}
+
+
+/* Data for each insn in current region. */
+vec<sel_insn_data_def> s_i_d = vNULL;
+
+/* Extend data structures for insns from current region. */
+static void
+extend_insn_data (void)
+{
+ int reserve;
+
+ sched_extend_target ();
+ sched_deps_init (false);
+
+ /* Extend data structures for insns from current region. */
+ reserve = (sched_max_luid + 1 - s_i_d.length ());
+ if (reserve > 0 && ! s_i_d.space (reserve))
+ {
+ int size;
+
+ if (sched_max_luid / 2 > 1024)
+ size = sched_max_luid + 1024;
+ else
+ size = 3 * sched_max_luid / 2;
+
+
+ s_i_d.safe_grow_cleared (size);
+ }
+}
+
+/* Finalize data structures for insns from current region. */
+static void
+finish_insns (void)
+{
+ unsigned i;
+
+ /* Clear here all dependence contexts that may have left from insns that were
+ removed during the scheduling. */
+ for (i = 0; i < s_i_d.length (); i++)
+ {
+ sel_insn_data_def *sid_entry = &s_i_d[i];
+
+ if (sid_entry->live)
+ return_regset_to_pool (sid_entry->live);
+ if (sid_entry->analyzed_deps)
+ {
+ BITMAP_FREE (sid_entry->analyzed_deps);
+ BITMAP_FREE (sid_entry->found_deps);
+ htab_delete (sid_entry->transformed_insns);
+ free_deps (&sid_entry->deps_context);
+ }
+ if (EXPR_VINSN (&sid_entry->expr))
+ {
+ clear_expr (&sid_entry->expr);
+
+ /* Also, clear CANT_MOVE bit here, because we really don't want it
+ to be passed to the next region. */
+ CANT_MOVE_BY_LUID (i) = 0;
+ }
+ }
+
+ s_i_d.release ();
+}
+
+/* A proxy to pass initialization data to init_insn (). */
+static sel_insn_data_def _insn_init_ssid;
+static sel_insn_data_t insn_init_ssid = &_insn_init_ssid;
+
+/* If true create a new vinsn. Otherwise use the one from EXPR. */
+static bool insn_init_create_new_vinsn_p;
+
+/* Set all necessary data for initialization of the new insn[s]. */
+static expr_t
+set_insn_init (expr_t expr, vinsn_t vi, int seqno)
+{
+ expr_t x = &insn_init_ssid->expr;
+
+ copy_expr_onside (x, expr);
+ if (vi != NULL)
+ {
+ insn_init_create_new_vinsn_p = false;
+ change_vinsn_in_expr (x, vi);
+ }
+ else
+ insn_init_create_new_vinsn_p = true;
+
+ insn_init_ssid->seqno = seqno;
+ return x;
+}
+
+/* Init data for INSN. */
+static void
+init_insn_data (insn_t insn)
+{
+ expr_t expr;
+ sel_insn_data_t ssid = insn_init_ssid;
+
+ /* The fields mentioned below are special and hence are not being
+ propagated to the new insns. */
+ gcc_assert (!ssid->asm_p && ssid->sched_next == NULL
+ && !ssid->after_stall_p && ssid->sched_cycle == 0);
+ gcc_assert (INSN_P (insn) && INSN_LUID (insn) > 0);
+
+ expr = INSN_EXPR (insn);
+ copy_expr (expr, &ssid->expr);
+ prepare_insn_expr (insn, ssid->seqno);
+
+ if (insn_init_create_new_vinsn_p)
+ change_vinsn_in_expr (expr, vinsn_create (insn, init_insn_force_unique_p));
+
+ if (first_time_insn_init (insn))
+ init_first_time_insn_data (insn);
+}
+
+/* This is used to initialize spurious jumps generated by
+ sel_redirect_edge (). */
+static void
+init_simplejump_data (insn_t insn)
+{
+ init_expr (INSN_EXPR (insn), vinsn_create (insn, false), 0,
+ REG_BR_PROB_BASE, 0, 0, 0, 0, 0, 0,
+ vNULL, true, false, false,
+ false, true);
+ INSN_SEQNO (insn) = get_seqno_for_a_jump (insn);
+ init_first_time_insn_data (insn);
+}
+
+/* Perform deferred initialization of insns. This is used to process
+ a new jump that may be created by redirect_edge. */
+void
+sel_init_new_insn (insn_t insn, int flags)
+{
+ /* We create data structures for bb when the first insn is emitted in it. */
+ if (INSN_P (insn)
+ && INSN_IN_STREAM_P (insn)
+ && insn_is_the_only_one_in_bb_p (insn))
+ {
+ extend_bb_info ();
+ create_initial_data_sets (BLOCK_FOR_INSN (insn));
+ }
+
+ if (flags & INSN_INIT_TODO_LUID)
+ {
+ sched_extend_luids ();
+ sched_init_insn_luid (insn);
+ }
+
+ if (flags & INSN_INIT_TODO_SSID)
+ {
+ extend_insn_data ();
+ init_insn_data (insn);
+ clear_expr (&insn_init_ssid->expr);
+ }
+
+ if (flags & INSN_INIT_TODO_SIMPLEJUMP)
+ {
+ extend_insn_data ();
+ init_simplejump_data (insn);
+ }
+
+ gcc_assert (CONTAINING_RGN (BLOCK_NUM (insn))
+ == CONTAINING_RGN (BB_TO_BLOCK (0)));
+}
+
+
+/* Functions to init/finish work with lv sets. */
+
+/* Init BB_LV_SET of BB from DF_LR_IN set of BB. */
+static void
+init_lv_set (basic_block bb)
+{
+ gcc_assert (!BB_LV_SET_VALID_P (bb));
+
+ BB_LV_SET (bb) = get_regset_from_pool ();
+ COPY_REG_SET (BB_LV_SET (bb), DF_LR_IN (bb));
+ BB_LV_SET_VALID_P (bb) = true;
+}
+
+/* Copy liveness information to BB from FROM_BB. */
+static void
+copy_lv_set_from (basic_block bb, basic_block from_bb)
+{
+ gcc_assert (!BB_LV_SET_VALID_P (bb));
+
+ COPY_REG_SET (BB_LV_SET (bb), BB_LV_SET (from_bb));
+ BB_LV_SET_VALID_P (bb) = true;
+}
+
+/* Initialize lv set of all bb headers. */
+void
+init_lv_sets (void)
+{
+ basic_block bb;
+
+ /* Initialize of LV sets. */
+ FOR_EACH_BB_FN (bb, cfun)
+ init_lv_set (bb);
+
+ /* Don't forget EXIT_BLOCK. */
+ init_lv_set (EXIT_BLOCK_PTR_FOR_FN (cfun));
+}
+
+/* Release lv set of HEAD. */
+static void
+free_lv_set (basic_block bb)
+{
+ gcc_assert (BB_LV_SET (bb) != NULL);
+
+ return_regset_to_pool (BB_LV_SET (bb));
+ BB_LV_SET (bb) = NULL;
+ BB_LV_SET_VALID_P (bb) = false;
+}
+
+/* Finalize lv sets of all bb headers. */
+void
+free_lv_sets (void)
+{
+ basic_block bb;
+
+ /* Don't forget EXIT_BLOCK. */
+ free_lv_set (EXIT_BLOCK_PTR_FOR_FN (cfun));
+
+ /* Free LV sets. */
+ FOR_EACH_BB_FN (bb, cfun)
+ if (BB_LV_SET (bb))
+ free_lv_set (bb);
+}
+
+/* Mark AV_SET for BB as invalid, so this set will be updated the next time
+ compute_av() processes BB. This function is called when creating new basic
+ blocks, as well as for blocks (either new or existing) where new jumps are
+ created when the control flow is being updated. */
+static void
+invalidate_av_set (basic_block bb)
+{
+ BB_AV_LEVEL (bb) = -1;
+}
+
+/* Create initial data sets for BB (they will be invalid). */
+static void
+create_initial_data_sets (basic_block bb)
+{
+ if (BB_LV_SET (bb))
+ BB_LV_SET_VALID_P (bb) = false;
+ else
+ BB_LV_SET (bb) = get_regset_from_pool ();
+ invalidate_av_set (bb);
+}
+
+/* Free av set of BB. */
+static void
+free_av_set (basic_block bb)
+{
+ av_set_clear (&BB_AV_SET (bb));
+ BB_AV_LEVEL (bb) = 0;
+}
+
+/* Free data sets of BB. */
+void
+free_data_sets (basic_block bb)
+{
+ free_lv_set (bb);
+ free_av_set (bb);
+}
+
+/* Exchange lv sets of TO and FROM. */
+static void
+exchange_lv_sets (basic_block to, basic_block from)
+{
+ {
+ regset to_lv_set = BB_LV_SET (to);
+
+ BB_LV_SET (to) = BB_LV_SET (from);
+ BB_LV_SET (from) = to_lv_set;
+ }
+
+ {
+ bool to_lv_set_valid_p = BB_LV_SET_VALID_P (to);
+
+ BB_LV_SET_VALID_P (to) = BB_LV_SET_VALID_P (from);
+ BB_LV_SET_VALID_P (from) = to_lv_set_valid_p;
+ }
+}
+
+
+/* Exchange av sets of TO and FROM. */
+static void
+exchange_av_sets (basic_block to, basic_block from)
+{
+ {
+ av_set_t to_av_set = BB_AV_SET (to);
+
+ BB_AV_SET (to) = BB_AV_SET (from);
+ BB_AV_SET (from) = to_av_set;
+ }
+
+ {
+ int to_av_level = BB_AV_LEVEL (to);
+
+ BB_AV_LEVEL (to) = BB_AV_LEVEL (from);
+ BB_AV_LEVEL (from) = to_av_level;
+ }
+}
+
+/* Exchange data sets of TO and FROM. */
+void
+exchange_data_sets (basic_block to, basic_block from)
+{
+ exchange_lv_sets (to, from);
+ exchange_av_sets (to, from);
+}
+
+/* Copy data sets of FROM to TO. */
+void
+copy_data_sets (basic_block to, basic_block from)
+{
+ gcc_assert (!BB_LV_SET_VALID_P (to) && !BB_AV_SET_VALID_P (to));
+ gcc_assert (BB_AV_SET (to) == NULL);
+
+ BB_AV_LEVEL (to) = BB_AV_LEVEL (from);
+ BB_LV_SET_VALID_P (to) = BB_LV_SET_VALID_P (from);
+
+ if (BB_AV_SET_VALID_P (from))
+ {
+ BB_AV_SET (to) = av_set_copy (BB_AV_SET (from));
+ }
+ if (BB_LV_SET_VALID_P (from))
+ {
+ gcc_assert (BB_LV_SET (to) != NULL);
+ COPY_REG_SET (BB_LV_SET (to), BB_LV_SET (from));
+ }
+}
+
+/* Return an av set for INSN, if any. */
+av_set_t
+get_av_set (insn_t insn)
+{
+ av_set_t av_set;
+
+ gcc_assert (AV_SET_VALID_P (insn));
+
+ if (sel_bb_head_p (insn))
+ av_set = BB_AV_SET (BLOCK_FOR_INSN (insn));
+ else
+ av_set = NULL;
+
+ return av_set;
+}
+
+/* Implementation of AV_LEVEL () macro. Return AV_LEVEL () of INSN. */
+int
+get_av_level (insn_t insn)
+{
+ int av_level;
+
+ gcc_assert (INSN_P (insn));
+
+ if (sel_bb_head_p (insn))
+ av_level = BB_AV_LEVEL (BLOCK_FOR_INSN (insn));
+ else
+ av_level = INSN_WS_LEVEL (insn);
+
+ return av_level;
+}
+
+
+
+/* Variables to work with control-flow graph. */
+
+/* The basic block that already has been processed by the sched_data_update (),
+ but hasn't been in sel_add_bb () yet. */
+static vec<basic_block>
+ last_added_blocks = vNULL;
+
+/* A pool for allocating successor infos. */
+static struct
+{
+ /* A stack for saving succs_info structures. */
+ struct succs_info *stack;
+
+ /* Its size. */
+ int size;
+
+ /* Top of the stack. */
+ int top;
+
+ /* Maximal value of the top. */
+ int max_top;
+} succs_info_pool;
+
+/* Functions to work with control-flow graph. */
+
+/* Return basic block note of BB. */
+insn_t
+sel_bb_head (basic_block bb)
+{
+ insn_t head;
+
+ if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
+ {
+ gcc_assert (exit_insn != NULL_RTX);
+ head = exit_insn;
+ }
+ else
+ {
+ insn_t note;
+
+ note = bb_note (bb);
+ head = next_nonnote_insn (note);
+
+ if (head && (BARRIER_P (head) || BLOCK_FOR_INSN (head) != bb))
+ head = NULL_RTX;
+ }
+
+ return head;
+}
+
+/* Return true if INSN is a basic block header. */
+bool
+sel_bb_head_p (insn_t insn)
+{
+ return sel_bb_head (BLOCK_FOR_INSN (insn)) == insn;
+}
+
+/* Return last insn of BB. */
+insn_t
+sel_bb_end (basic_block bb)
+{
+ if (sel_bb_empty_p (bb))
+ return NULL_RTX;
+
+ gcc_assert (bb != EXIT_BLOCK_PTR_FOR_FN (cfun));
+
+ return BB_END (bb);
+}
+
+/* Return true if INSN is the last insn in its basic block. */
+bool
+sel_bb_end_p (insn_t insn)
+{
+ return insn == sel_bb_end (BLOCK_FOR_INSN (insn));
+}
+
+/* Return true if BB consist of single NOTE_INSN_BASIC_BLOCK. */
+bool
+sel_bb_empty_p (basic_block bb)
+{
+ return sel_bb_head (bb) == NULL;
+}
+
+/* True when BB belongs to the current scheduling region. */
+bool
+in_current_region_p (basic_block bb)
+{
+ if (bb->index < NUM_FIXED_BLOCKS)
+ return false;
+
+ return CONTAINING_RGN (bb->index) == CONTAINING_RGN (BB_TO_BLOCK (0));
+}
+
+/* Return the block which is a fallthru bb of a conditional jump JUMP. */
+basic_block
+fallthru_bb_of_jump (rtx jump)
+{
+ if (!JUMP_P (jump))
+ return NULL;
+
+ if (!any_condjump_p (jump))
+ return NULL;
+
+ /* A basic block that ends with a conditional jump may still have one successor
+ (and be followed by a barrier), we are not interested. */
+ if (single_succ_p (BLOCK_FOR_INSN (jump)))
+ return NULL;
+
+ return FALLTHRU_EDGE (BLOCK_FOR_INSN (jump))->dest;
+}
+
+/* Remove all notes from BB. */
+static void
+init_bb (basic_block bb)
+{
+ remove_notes (bb_note (bb), BB_END (bb));
+ BB_NOTE_LIST (bb) = note_list;
+}
+
+void
+sel_init_bbs (bb_vec_t bbs)
+{
+ const struct sched_scan_info_def ssi =
+ {
+ extend_bb_info, /* extend_bb */
+ init_bb, /* init_bb */
+ NULL, /* extend_insn */
+ NULL /* init_insn */
+ };
+
+ sched_scan (&ssi, bbs);
+}
+
+/* Restore notes for the whole region. */
+static void
+sel_restore_notes (void)
+{
+ int bb;
+ insn_t insn;
+
+ for (bb = 0; bb < current_nr_blocks; bb++)
+ {
+ basic_block first, last;
+
+ first = EBB_FIRST_BB (bb);
+ last = EBB_LAST_BB (bb)->next_bb;
+
+ do
+ {
+ note_list = BB_NOTE_LIST (first);
+ restore_other_notes (NULL, first);
+ BB_NOTE_LIST (first) = NULL_RTX;
+
+ FOR_BB_INSNS (first, insn)
+ if (NONDEBUG_INSN_P (insn))
+ reemit_notes (insn);
+
+ first = first->next_bb;
+ }
+ while (first != last);
+ }
+}
+
+/* Free per-bb data structures. */
+void
+sel_finish_bbs (void)
+{
+ sel_restore_notes ();
+
+ /* Remove current loop preheader from this loop. */
+ if (current_loop_nest)
+ sel_remove_loop_preheader ();
+
+ finish_region_bb_info ();
+}
+
+/* Return true if INSN has a single successor of type FLAGS. */
+bool
+sel_insn_has_single_succ_p (insn_t insn, int flags)
+{
+ insn_t succ;
+ succ_iterator si;
+ bool first_p = true;
+
+ FOR_EACH_SUCC_1 (succ, si, insn, flags)
+ {
+ if (first_p)
+ first_p = false;
+ else
+ return false;
+ }
+
+ return true;
+}
+
+/* Allocate successor's info. */
+static struct succs_info *
+alloc_succs_info (void)
+{
+ if (succs_info_pool.top == succs_info_pool.max_top)
+ {
+ int i;
+
+ if (++succs_info_pool.max_top >= succs_info_pool.size)
+ gcc_unreachable ();
+
+ i = ++succs_info_pool.top;
+ succs_info_pool.stack[i].succs_ok.create (10);
+ succs_info_pool.stack[i].succs_other.create (10);
+ succs_info_pool.stack[i].probs_ok.create (10);
+ }
+ else
+ succs_info_pool.top++;
+
+ return &succs_info_pool.stack[succs_info_pool.top];
+}
+
+/* Free successor's info. */
+void
+free_succs_info (struct succs_info * sinfo)
+{
+ gcc_assert (succs_info_pool.top >= 0
+ && &succs_info_pool.stack[succs_info_pool.top] == sinfo);
+ succs_info_pool.top--;
+
+ /* Clear stale info. */
+ sinfo->succs_ok.block_remove (0, sinfo->succs_ok.length ());
+ sinfo->succs_other.block_remove (0, sinfo->succs_other.length ());
+ sinfo->probs_ok.block_remove (0, sinfo->probs_ok.length ());
+ sinfo->all_prob = 0;
+ sinfo->succs_ok_n = 0;
+ sinfo->all_succs_n = 0;
+}
+
+/* Compute successor info for INSN. FLAGS are the flags passed
+ to the FOR_EACH_SUCC_1 iterator. */
+struct succs_info *
+compute_succs_info (insn_t insn, short flags)
+{
+ succ_iterator si;
+ insn_t succ;
+ struct succs_info *sinfo = alloc_succs_info ();
+
+ /* Traverse *all* successors and decide what to do with each. */
+ FOR_EACH_SUCC_1 (succ, si, insn, SUCCS_ALL)
+ {
+ /* FIXME: this doesn't work for skipping to loop exits, as we don't
+ perform code motion through inner loops. */
+ short current_flags = si.current_flags & ~SUCCS_SKIP_TO_LOOP_EXITS;
+
+ if (current_flags & flags)
+ {
+ sinfo->succs_ok.safe_push (succ);
+ sinfo->probs_ok.safe_push (
+ /* FIXME: Improve calculation when skipping
+ inner loop to exits. */
+ si.bb_end ? si.e1->probability : REG_BR_PROB_BASE);
+ sinfo->succs_ok_n++;
+ }
+ else
+ sinfo->succs_other.safe_push (succ);
+
+ /* Compute all_prob. */
+ if (!si.bb_end)
+ sinfo->all_prob = REG_BR_PROB_BASE;
+ else
+ sinfo->all_prob += si.e1->probability;
+
+ sinfo->all_succs_n++;
+ }
+
+ return sinfo;
+}
+
+/* Return the predecessors of BB in PREDS and their number in N.
+ Empty blocks are skipped. SIZE is used to allocate PREDS. */
+static void
+cfg_preds_1 (basic_block bb, insn_t **preds, int *n, int *size)
+{
+ edge e;
+ edge_iterator ei;
+
+ gcc_assert (BLOCK_TO_BB (bb->index) != 0);
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ basic_block pred_bb = e->src;
+ insn_t bb_end = BB_END (pred_bb);
+
+ if (!in_current_region_p (pred_bb))
+ {
+ gcc_assert (flag_sel_sched_pipelining_outer_loops
+ && current_loop_nest);
+ continue;
+ }
+
+ if (sel_bb_empty_p (pred_bb))
+ cfg_preds_1 (pred_bb, preds, n, size);
+ else
+ {
+ if (*n == *size)
+ *preds = XRESIZEVEC (insn_t, *preds,
+ (*size = 2 * *size + 1));
+ (*preds)[(*n)++] = bb_end;
+ }
+ }
+
+ gcc_assert (*n != 0
+ || (flag_sel_sched_pipelining_outer_loops
+ && current_loop_nest));
+}
+
+/* Find all predecessors of BB and record them in PREDS and their number
+ in N. Empty blocks are skipped, and only normal (forward in-region)
+ edges are processed. */
+static void
+cfg_preds (basic_block bb, insn_t **preds, int *n)
+{
+ int size = 0;
+
+ *preds = NULL;
+ *n = 0;
+ cfg_preds_1 (bb, preds, n, &size);
+}
+
+/* Returns true if we are moving INSN through join point. */
+bool
+sel_num_cfg_preds_gt_1 (insn_t insn)
+{
+ basic_block bb;
+
+ if (!sel_bb_head_p (insn) || INSN_BB (insn) == 0)
+ return false;
+
+ bb = BLOCK_FOR_INSN (insn);
+
+ while (1)
+ {
+ if (EDGE_COUNT (bb->preds) > 1)
+ return true;
+
+ gcc_assert (EDGE_PRED (bb, 0)->dest == bb);
+ bb = EDGE_PRED (bb, 0)->src;
+
+ if (!sel_bb_empty_p (bb))
+ break;
+ }
+
+ return false;
+}
+
+/* Returns true when BB should be the end of an ebb. Adapted from the
+ code in sched-ebb.c. */
+bool
+bb_ends_ebb_p (basic_block bb)
+{
+ basic_block next_bb = bb_next_bb (bb);
+ edge e;
+
+ if (next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun)
+ || bitmap_bit_p (forced_ebb_heads, next_bb->index)
+ || (LABEL_P (BB_HEAD (next_bb))
+ /* NB: LABEL_NUSES () is not maintained outside of jump.c.
+ Work around that. */
+ && !single_pred_p (next_bb)))
+ return true;
+
+ if (!in_current_region_p (next_bb))
+ return true;
+
+ e = find_fallthru_edge (bb->succs);
+ if (e)
+ {
+ gcc_assert (e->dest == next_bb);
+
+ return false;
+ }
+
+ return true;
+}
+
+/* Returns true when INSN and SUCC are in the same EBB, given that SUCC is a
+ successor of INSN. */
+bool
+in_same_ebb_p (insn_t insn, insn_t succ)
+{
+ basic_block ptr = BLOCK_FOR_INSN (insn);
+
+ for (;;)
+ {
+ if (ptr == BLOCK_FOR_INSN (succ))
+ return true;
+
+ if (bb_ends_ebb_p (ptr))
+ return false;
+
+ ptr = bb_next_bb (ptr);
+ }
+
+ gcc_unreachable ();
+ return false;
+}
+
+/* Recomputes the reverse topological order for the function and
+ saves it in REV_TOP_ORDER_INDEX. REV_TOP_ORDER_INDEX_LEN is also
+ modified appropriately. */
+static void
+recompute_rev_top_order (void)
+{
+ int *postorder;
+ int n_blocks, i;
+
+ if (!rev_top_order_index
+ || rev_top_order_index_len < last_basic_block_for_fn (cfun))
+ {
+ rev_top_order_index_len = last_basic_block_for_fn (cfun);
+ rev_top_order_index = XRESIZEVEC (int, rev_top_order_index,
+ rev_top_order_index_len);
+ }
+
+ postorder = XNEWVEC (int, n_basic_blocks_for_fn (cfun));
+
+ n_blocks = post_order_compute (postorder, true, false);
+ gcc_assert (n_basic_blocks_for_fn (cfun) == n_blocks);
+
+ /* Build reverse function: for each basic block with BB->INDEX == K
+ rev_top_order_index[K] is it's reverse topological sort number. */
+ for (i = 0; i < n_blocks; i++)
+ {
+ gcc_assert (postorder[i] < rev_top_order_index_len);
+ rev_top_order_index[postorder[i]] = i;
+ }
+
+ free (postorder);
+}
+
+/* Clear all flags from insns in BB that could spoil its rescheduling. */
+void
+clear_outdated_rtx_info (basic_block bb)
+{
+ rtx insn;
+
+ FOR_BB_INSNS (bb, insn)
+ if (INSN_P (insn))
+ {
+ SCHED_GROUP_P (insn) = 0;
+ INSN_AFTER_STALL_P (insn) = 0;
+ INSN_SCHED_TIMES (insn) = 0;
+ EXPR_PRIORITY_ADJ (INSN_EXPR (insn)) = 0;
+
+ /* We cannot use the changed caches, as previously we could ignore
+ the LHS dependence due to enabled renaming and transform
+ the expression, and currently we'll be unable to do this. */
+ htab_empty (INSN_TRANSFORMED_INSNS (insn));
+ }
+}
+
+/* Add BB_NOTE to the pool of available basic block notes. */
+static void
+return_bb_to_pool (basic_block bb)
+{
+ rtx note = bb_note (bb);
+
+ gcc_assert (NOTE_BASIC_BLOCK (note) == bb
+ && bb->aux == NULL);
+
+ /* It turns out that current cfg infrastructure does not support
+ reuse of basic blocks. Don't bother for now. */
+ /*bb_note_pool.safe_push (note);*/
+}
+
+/* Get a bb_note from pool or return NULL_RTX if pool is empty. */
+static rtx
+get_bb_note_from_pool (void)
+{
+ if (bb_note_pool.is_empty ())
+ return NULL_RTX;
+ else
+ {
+ rtx note = bb_note_pool.pop ();
+
+ PREV_INSN (note) = NULL_RTX;
+ NEXT_INSN (note) = NULL_RTX;
+
+ return note;
+ }
+}
+
+/* Free bb_note_pool. */
+void
+free_bb_note_pool (void)
+{
+ bb_note_pool.release ();
+}
+
+/* Setup scheduler pool and successor structure. */
+void
+alloc_sched_pools (void)
+{
+ int succs_size;
+
+ succs_size = MAX_WS + 1;
+ succs_info_pool.stack = XCNEWVEC (struct succs_info, succs_size);
+ succs_info_pool.size = succs_size;
+ succs_info_pool.top = -1;
+ succs_info_pool.max_top = -1;
+
+ sched_lists_pool = create_alloc_pool ("sel-sched-lists",
+ sizeof (struct _list_node), 500);
+}
+
+/* Free the pools. */
+void
+free_sched_pools (void)
+{
+ int i;
+
+ free_alloc_pool (sched_lists_pool);
+ gcc_assert (succs_info_pool.top == -1);
+ for (i = 0; i <= succs_info_pool.max_top; i++)
+ {
+ succs_info_pool.stack[i].succs_ok.release ();
+ succs_info_pool.stack[i].succs_other.release ();
+ succs_info_pool.stack[i].probs_ok.release ();
+ }
+ free (succs_info_pool.stack);
+}
+
+
+/* Returns a position in RGN where BB can be inserted retaining
+ topological order. */
+static int
+find_place_to_insert_bb (basic_block bb, int rgn)
+{
+ bool has_preds_outside_rgn = false;
+ edge e;
+ edge_iterator ei;
+
+ /* Find whether we have preds outside the region. */
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (!in_current_region_p (e->src))
+ {
+ has_preds_outside_rgn = true;
+ break;
+ }
+
+ /* Recompute the top order -- needed when we have > 1 pred
+ and in case we don't have preds outside. */
+ if (flag_sel_sched_pipelining_outer_loops
+ && (has_preds_outside_rgn || EDGE_COUNT (bb->preds) > 1))
+ {
+ int i, bbi = bb->index, cur_bbi;
+
+ recompute_rev_top_order ();
+ for (i = RGN_NR_BLOCKS (rgn) - 1; i >= 0; i--)
+ {
+ cur_bbi = BB_TO_BLOCK (i);
+ if (rev_top_order_index[bbi]
+ < rev_top_order_index[cur_bbi])
+ break;
+ }
+
+ /* We skipped the right block, so we increase i. We accommodate
+ it for increasing by step later, so we decrease i. */
+ return (i + 1) - 1;
+ }
+ else if (has_preds_outside_rgn)
+ {
+ /* This is the case when we generate an extra empty block
+ to serve as region head during pipelining. */
+ e = EDGE_SUCC (bb, 0);
+ gcc_assert (EDGE_COUNT (bb->succs) == 1
+ && in_current_region_p (EDGE_SUCC (bb, 0)->dest)
+ && (BLOCK_TO_BB (e->dest->index) == 0));
+ return -1;
+ }
+
+ /* We don't have preds outside the region. We should have
+ the only pred, because the multiple preds case comes from
+ the pipelining of outer loops, and that is handled above.
+ Just take the bbi of this single pred. */
+ if (EDGE_COUNT (bb->succs) > 0)
+ {
+ int pred_bbi;
+
+ gcc_assert (EDGE_COUNT (bb->preds) == 1);
+
+ pred_bbi = EDGE_PRED (bb, 0)->src->index;
+ return BLOCK_TO_BB (pred_bbi);
+ }
+ else
+ /* BB has no successors. It is safe to put it in the end. */
+ return current_nr_blocks - 1;
+}
+
+/* Deletes an empty basic block freeing its data. */
+static void
+delete_and_free_basic_block (basic_block bb)
+{
+ gcc_assert (sel_bb_empty_p (bb));
+
+ if (BB_LV_SET (bb))
+ free_lv_set (bb);
+
+ bitmap_clear_bit (blocks_to_reschedule, bb->index);
+
+ /* Can't assert av_set properties because we use sel_aremove_bb
+ when removing loop preheader from the region. At the point of
+ removing the preheader we already have deallocated sel_region_bb_info. */
+ gcc_assert (BB_LV_SET (bb) == NULL
+ && !BB_LV_SET_VALID_P (bb)
+ && BB_AV_LEVEL (bb) == 0
+ && BB_AV_SET (bb) == NULL);
+
+ delete_basic_block (bb);
+}
+
+/* Add BB to the current region and update the region data. */
+static void
+add_block_to_current_region (basic_block bb)
+{
+ int i, pos, bbi = -2, rgn;
+
+ rgn = CONTAINING_RGN (BB_TO_BLOCK (0));
+ bbi = find_place_to_insert_bb (bb, rgn);
+ bbi += 1;
+ pos = RGN_BLOCKS (rgn) + bbi;
+
+ gcc_assert (RGN_HAS_REAL_EBB (rgn) == 0
+ && ebb_head[bbi] == pos);
+
+ /* Make a place for the new block. */
+ extend_regions ();
+
+ for (i = RGN_BLOCKS (rgn + 1) - 1; i >= pos; i--)
+ BLOCK_TO_BB (rgn_bb_table[i])++;
+
+ memmove (rgn_bb_table + pos + 1,
+ rgn_bb_table + pos,
+ (RGN_BLOCKS (nr_regions) - pos) * sizeof (*rgn_bb_table));
+
+ /* Initialize data for BB. */
+ rgn_bb_table[pos] = bb->index;
+ BLOCK_TO_BB (bb->index) = bbi;
+ CONTAINING_RGN (bb->index) = rgn;
+
+ RGN_NR_BLOCKS (rgn)++;
+
+ for (i = rgn + 1; i <= nr_regions; i++)
+ RGN_BLOCKS (i)++;
+}
+
+/* Remove BB from the current region and update the region data. */
+static void
+remove_bb_from_region (basic_block bb)
+{
+ int i, pos, bbi = -2, rgn;
+
+ rgn = CONTAINING_RGN (BB_TO_BLOCK (0));
+ bbi = BLOCK_TO_BB (bb->index);
+ pos = RGN_BLOCKS (rgn) + bbi;
+
+ gcc_assert (RGN_HAS_REAL_EBB (rgn) == 0
+ && ebb_head[bbi] == pos);
+
+ for (i = RGN_BLOCKS (rgn + 1) - 1; i >= pos; i--)
+ BLOCK_TO_BB (rgn_bb_table[i])--;
+
+ memmove (rgn_bb_table + pos,
+ rgn_bb_table + pos + 1,
+ (RGN_BLOCKS (nr_regions) - pos) * sizeof (*rgn_bb_table));
+
+ RGN_NR_BLOCKS (rgn)--;
+ for (i = rgn + 1; i <= nr_regions; i++)
+ RGN_BLOCKS (i)--;
+}
+
+/* Add BB to the current region and update all data. If BB is NULL, add all
+ blocks from last_added_blocks vector. */
+static void
+sel_add_bb (basic_block bb)
+{
+ /* Extend luids so that new notes will receive zero luids. */
+ sched_extend_luids ();
+ sched_init_bbs ();
+ sel_init_bbs (last_added_blocks);
+
+ /* When bb is passed explicitly, the vector should contain
+ the only element that equals to bb; otherwise, the vector
+ should not be NULL. */
+ gcc_assert (last_added_blocks.exists ());
+
+ if (bb != NULL)
+ {
+ gcc_assert (last_added_blocks.length () == 1
+ && last_added_blocks[0] == bb);
+ add_block_to_current_region (bb);
+
+ /* We associate creating/deleting data sets with the first insn
+ appearing / disappearing in the bb. */
+ if (!sel_bb_empty_p (bb) && BB_LV_SET (bb) == NULL)
+ create_initial_data_sets (bb);
+
+ last_added_blocks.release ();
+ }
+ else
+ /* BB is NULL - process LAST_ADDED_BLOCKS instead. */
+ {
+ int i;
+ basic_block temp_bb = NULL;
+
+ for (i = 0;
+ last_added_blocks.iterate (i, &bb); i++)
+ {
+ add_block_to_current_region (bb);
+ temp_bb = bb;
+ }
+
+ /* We need to fetch at least one bb so we know the region
+ to update. */
+ gcc_assert (temp_bb != NULL);
+ bb = temp_bb;
+
+ last_added_blocks.release ();
+ }
+
+ rgn_setup_region (CONTAINING_RGN (bb->index));
+}
+
+/* Remove BB from the current region and update all data.
+ If REMOVE_FROM_CFG_PBB is true, also remove the block cfom cfg. */
+static void
+sel_remove_bb (basic_block bb, bool remove_from_cfg_p)
+{
+ unsigned idx = bb->index;
+
+ gcc_assert (bb != NULL && BB_NOTE_LIST (bb) == NULL_RTX);
+
+ remove_bb_from_region (bb);
+ return_bb_to_pool (bb);
+ bitmap_clear_bit (blocks_to_reschedule, idx);
+
+ if (remove_from_cfg_p)
+ {
+ basic_block succ = single_succ (bb);
+ delete_and_free_basic_block (bb);
+ set_immediate_dominator (CDI_DOMINATORS, succ,
+ recompute_dominator (CDI_DOMINATORS, succ));
+ }
+
+ rgn_setup_region (CONTAINING_RGN (idx));
+}
+
+/* Concatenate info of EMPTY_BB to info of MERGE_BB. */
+static void
+move_bb_info (basic_block merge_bb, basic_block empty_bb)
+{
+ if (in_current_region_p (merge_bb))
+ concat_note_lists (BB_NOTE_LIST (empty_bb),
+ &BB_NOTE_LIST (merge_bb));
+ BB_NOTE_LIST (empty_bb) = NULL_RTX;
+
+}
+
+/* Remove EMPTY_BB. If REMOVE_FROM_CFG_P is false, remove EMPTY_BB from
+ region, but keep it in CFG. */
+static void
+remove_empty_bb (basic_block empty_bb, bool remove_from_cfg_p)
+{
+ /* The block should contain just a note or a label.
+ We try to check whether it is unused below. */
+ gcc_assert (BB_HEAD (empty_bb) == BB_END (empty_bb)
+ || LABEL_P (BB_HEAD (empty_bb)));
+
+ /* If basic block has predecessors or successors, redirect them. */
+ if (remove_from_cfg_p
+ && (EDGE_COUNT (empty_bb->preds) > 0
+ || EDGE_COUNT (empty_bb->succs) > 0))
+ {
+ basic_block pred;
+ basic_block succ;
+
+ /* We need to init PRED and SUCC before redirecting edges. */
+ if (EDGE_COUNT (empty_bb->preds) > 0)
+ {
+ edge e;
+
+ gcc_assert (EDGE_COUNT (empty_bb->preds) == 1);
+
+ e = EDGE_PRED (empty_bb, 0);
+ gcc_assert (e->src == empty_bb->prev_bb
+ && (e->flags & EDGE_FALLTHRU));
+
+ pred = empty_bb->prev_bb;
+ }
+ else
+ pred = NULL;
+
+ if (EDGE_COUNT (empty_bb->succs) > 0)
+ {
+ /* We do not check fallthruness here as above, because
+ after removing a jump the edge may actually be not fallthru. */
+ gcc_assert (EDGE_COUNT (empty_bb->succs) == 1);
+ succ = EDGE_SUCC (empty_bb, 0)->dest;
+ }
+ else
+ succ = NULL;
+
+ if (EDGE_COUNT (empty_bb->preds) > 0 && succ != NULL)
+ {
+ edge e = EDGE_PRED (empty_bb, 0);
+
+ if (e->flags & EDGE_FALLTHRU)
+ redirect_edge_succ_nodup (e, succ);
+ else
+ sel_redirect_edge_and_branch (EDGE_PRED (empty_bb, 0), succ);
+ }
+
+ if (EDGE_COUNT (empty_bb->succs) > 0 && pred != NULL)
+ {
+ edge e = EDGE_SUCC (empty_bb, 0);
+
+ if (find_edge (pred, e->dest) == NULL)
+ redirect_edge_pred (e, pred);
+ }
+ }
+
+ /* Finish removing. */
+ sel_remove_bb (empty_bb, remove_from_cfg_p);
+}
+
+/* An implementation of create_basic_block hook, which additionally updates
+ per-bb data structures. */
+static basic_block
+sel_create_basic_block (void *headp, void *endp, basic_block after)
+{
+ basic_block new_bb;
+ insn_t new_bb_note;
+
+ gcc_assert (flag_sel_sched_pipelining_outer_loops
+ || !last_added_blocks.exists ());
+
+ new_bb_note = get_bb_note_from_pool ();
+
+ if (new_bb_note == NULL_RTX)
+ new_bb = orig_cfg_hooks.create_basic_block (headp, endp, after);
+ else
+ {
+ new_bb = create_basic_block_structure ((rtx) headp, (rtx) endp,
+ new_bb_note, after);
+ new_bb->aux = NULL;
+ }
+
+ last_added_blocks.safe_push (new_bb);
+
+ return new_bb;
+}
+
+/* Implement sched_init_only_bb (). */
+static void
+sel_init_only_bb (basic_block bb, basic_block after)
+{
+ gcc_assert (after == NULL);
+
+ extend_regions ();
+ rgn_make_new_region_out_of_new_block (bb);
+}
+
+/* Update the latch when we've splitted or merged it from FROM block to TO.
+ This should be checked for all outer loops, too. */
+static void
+change_loops_latches (basic_block from, basic_block to)
+{
+ gcc_assert (from != to);
+
+ if (current_loop_nest)
+ {
+ struct loop *loop;
+
+ for (loop = current_loop_nest; loop; loop = loop_outer (loop))
+ if (considered_for_pipelining_p (loop) && loop->latch == from)
+ {
+ gcc_assert (loop == current_loop_nest);
+ loop->latch = to;
+ gcc_assert (loop_latch_edge (loop));
+ }
+ }
+}
+
+/* Splits BB on two basic blocks, adding it to the region and extending
+ per-bb data structures. Returns the newly created bb. */
+static basic_block
+sel_split_block (basic_block bb, rtx after)
+{
+ basic_block new_bb;
+ insn_t insn;
+
+ new_bb = sched_split_block_1 (bb, after);
+ sel_add_bb (new_bb);
+
+ /* This should be called after sel_add_bb, because this uses
+ CONTAINING_RGN for the new block, which is not yet initialized.
+ FIXME: this function may be a no-op now. */
+ change_loops_latches (bb, new_bb);
+
+ /* Update ORIG_BB_INDEX for insns moved into the new block. */
+ FOR_BB_INSNS (new_bb, insn)
+ if (INSN_P (insn))
+ EXPR_ORIG_BB_INDEX (INSN_EXPR (insn)) = new_bb->index;
+
+ if (sel_bb_empty_p (bb))
+ {
+ gcc_assert (!sel_bb_empty_p (new_bb));
+
+ /* NEW_BB has data sets that need to be updated and BB holds
+ data sets that should be removed. Exchange these data sets
+ so that we won't lose BB's valid data sets. */
+ exchange_data_sets (new_bb, bb);
+ free_data_sets (bb);
+ }
+
+ if (!sel_bb_empty_p (new_bb)
+ && bitmap_bit_p (blocks_to_reschedule, bb->index))
+ bitmap_set_bit (blocks_to_reschedule, new_bb->index);
+
+ return new_bb;
+}
+
+/* If BB ends with a jump insn whose ID is bigger then PREV_MAX_UID, return it.
+ Otherwise returns NULL. */
+static rtx
+check_for_new_jump (basic_block bb, int prev_max_uid)
+{
+ rtx end;
+
+ end = sel_bb_end (bb);
+ if (end && INSN_UID (end) >= prev_max_uid)
+ return end;
+ return NULL;
+}
+
+/* Look for a new jump either in FROM_BB block or in newly created JUMP_BB block.
+ New means having UID at least equal to PREV_MAX_UID. */
+static rtx
+find_new_jump (basic_block from, basic_block jump_bb, int prev_max_uid)
+{
+ rtx jump;
+
+ /* Return immediately if no new insns were emitted. */
+ if (get_max_uid () == prev_max_uid)
+ return NULL;
+
+ /* Now check both blocks for new jumps. It will ever be only one. */
+ if ((jump = check_for_new_jump (from, prev_max_uid)))
+ return jump;
+
+ if (jump_bb != NULL
+ && (jump = check_for_new_jump (jump_bb, prev_max_uid)))
+ return jump;
+ return NULL;
+}
+
+/* Splits E and adds the newly created basic block to the current region.
+ Returns this basic block. */
+basic_block
+sel_split_edge (edge e)
+{
+ basic_block new_bb, src, other_bb = NULL;
+ int prev_max_uid;
+ rtx jump;
+
+ src = e->src;
+ prev_max_uid = get_max_uid ();
+ new_bb = split_edge (e);
+
+ if (flag_sel_sched_pipelining_outer_loops
+ && current_loop_nest)
+ {
+ int i;
+ basic_block bb;
+
+ /* Some of the basic blocks might not have been added to the loop.
+ Add them here, until this is fixed in force_fallthru. */
+ for (i = 0;
+ last_added_blocks.iterate (i, &bb); i++)
+ if (!bb->loop_father)
+ {
+ add_bb_to_loop (bb, e->dest->loop_father);
+
+ gcc_assert (!other_bb && (new_bb->index != bb->index));
+ other_bb = bb;
+ }
+ }
+
+ /* Add all last_added_blocks to the region. */
+ sel_add_bb (NULL);
+
+ jump = find_new_jump (src, new_bb, prev_max_uid);
+ if (jump)
+ sel_init_new_insn (jump, INSN_INIT_TODO_LUID | INSN_INIT_TODO_SIMPLEJUMP);
+
+ /* Put the correct lv set on this block. */
+ if (other_bb && !sel_bb_empty_p (other_bb))
+ compute_live (sel_bb_head (other_bb));
+
+ return new_bb;
+}
+
+/* Implement sched_create_empty_bb (). */
+static basic_block
+sel_create_empty_bb (basic_block after)
+{
+ basic_block new_bb;
+
+ new_bb = sched_create_empty_bb_1 (after);
+
+ /* We'll explicitly initialize NEW_BB via sel_init_only_bb () a bit
+ later. */
+ gcc_assert (last_added_blocks.length () == 1
+ && last_added_blocks[0] == new_bb);
+
+ last_added_blocks.release ();
+ return new_bb;
+}
+
+/* Implement sched_create_recovery_block. ORIG_INSN is where block
+ will be splitted to insert a check. */
+basic_block
+sel_create_recovery_block (insn_t orig_insn)
+{
+ basic_block first_bb, second_bb, recovery_block;
+ basic_block before_recovery = NULL;
+ rtx jump;
+
+ first_bb = BLOCK_FOR_INSN (orig_insn);
+ if (sel_bb_end_p (orig_insn))
+ {
+ /* Avoid introducing an empty block while splitting. */
+ gcc_assert (single_succ_p (first_bb));
+ second_bb = single_succ (first_bb);
+ }
+ else
+ second_bb = sched_split_block (first_bb, orig_insn);
+
+ recovery_block = sched_create_recovery_block (&before_recovery);
+ if (before_recovery)
+ copy_lv_set_from (before_recovery, EXIT_BLOCK_PTR_FOR_FN (cfun));
+
+ gcc_assert (sel_bb_empty_p (recovery_block));
+ sched_create_recovery_edges (first_bb, recovery_block, second_bb);
+ if (current_loops != NULL)
+ add_bb_to_loop (recovery_block, first_bb->loop_father);
+
+ sel_add_bb (recovery_block);
+
+ jump = BB_END (recovery_block);
+ gcc_assert (sel_bb_head (recovery_block) == jump);
+ sel_init_new_insn (jump, INSN_INIT_TODO_LUID | INSN_INIT_TODO_SIMPLEJUMP);
+
+ return recovery_block;
+}
+
+/* Merge basic block B into basic block A. */
+static void
+sel_merge_blocks (basic_block a, basic_block b)
+{
+ gcc_assert (sel_bb_empty_p (b)
+ && EDGE_COUNT (b->preds) == 1
+ && EDGE_PRED (b, 0)->src == b->prev_bb);
+
+ move_bb_info (b->prev_bb, b);
+ remove_empty_bb (b, false);
+ merge_blocks (a, b);
+ change_loops_latches (b, a);
+}
+
+/* A wrapper for redirect_edge_and_branch_force, which also initializes
+ data structures for possibly created bb and insns. Returns the newly
+ added bb or NULL, when a bb was not needed. */
+void
+sel_redirect_edge_and_branch_force (edge e, basic_block to)
+{
+ basic_block jump_bb, src, orig_dest = e->dest;
+ int prev_max_uid;
+ rtx jump;
+
+ /* This function is now used only for bookkeeping code creation, where
+ we'll never get the single pred of orig_dest block and thus will not
+ hit unreachable blocks when updating dominator info. */
+ gcc_assert (!sel_bb_empty_p (e->src)
+ && !single_pred_p (orig_dest));
+ src = e->src;
+ prev_max_uid = get_max_uid ();
+ jump_bb = redirect_edge_and_branch_force (e, to);
+
+ if (jump_bb != NULL)
+ sel_add_bb (jump_bb);
+
+ /* This function could not be used to spoil the loop structure by now,
+ thus we don't care to update anything. But check it to be sure. */
+ if (current_loop_nest
+ && pipelining_p)
+ gcc_assert (loop_latch_edge (current_loop_nest));
+
+ jump = find_new_jump (src, jump_bb, prev_max_uid);
+ if (jump)
+ sel_init_new_insn (jump, INSN_INIT_TODO_LUID | INSN_INIT_TODO_SIMPLEJUMP);
+ set_immediate_dominator (CDI_DOMINATORS, to,
+ recompute_dominator (CDI_DOMINATORS, to));
+ set_immediate_dominator (CDI_DOMINATORS, orig_dest,
+ recompute_dominator (CDI_DOMINATORS, orig_dest));
+}
+
+/* A wrapper for redirect_edge_and_branch. Return TRUE if blocks connected by
+ redirected edge are in reverse topological order. */
+bool
+sel_redirect_edge_and_branch (edge e, basic_block to)
+{
+ bool latch_edge_p;
+ basic_block src, orig_dest = e->dest;
+ int prev_max_uid;
+ rtx jump;
+ edge redirected;
+ bool recompute_toporder_p = false;
+ bool maybe_unreachable = single_pred_p (orig_dest);
+
+ latch_edge_p = (pipelining_p
+ && current_loop_nest
+ && e == loop_latch_edge (current_loop_nest));
+
+ src = e->src;
+ prev_max_uid = get_max_uid ();
+
+ redirected = redirect_edge_and_branch (e, to);
+
+ gcc_assert (redirected && !last_added_blocks.exists ());
+
+ /* When we've redirected a latch edge, update the header. */
+ if (latch_edge_p)
+ {
+ current_loop_nest->header = to;
+ gcc_assert (loop_latch_edge (current_loop_nest));
+ }
+
+ /* In rare situations, the topological relation between the blocks connected
+ by the redirected edge can change (see PR42245 for an example). Update
+ block_to_bb/bb_to_block. */
+ if (CONTAINING_RGN (e->src->index) == CONTAINING_RGN (to->index)
+ && BLOCK_TO_BB (e->src->index) > BLOCK_TO_BB (to->index))
+ recompute_toporder_p = true;
+
+ jump = find_new_jump (src, NULL, prev_max_uid);
+ if (jump)
+ sel_init_new_insn (jump, INSN_INIT_TODO_LUID | INSN_INIT_TODO_SIMPLEJUMP);
+
+ /* Only update dominator info when we don't have unreachable blocks.
+ Otherwise we'll update in maybe_tidy_empty_bb. */
+ if (!maybe_unreachable)
+ {
+ set_immediate_dominator (CDI_DOMINATORS, to,
+ recompute_dominator (CDI_DOMINATORS, to));
+ set_immediate_dominator (CDI_DOMINATORS, orig_dest,
+ recompute_dominator (CDI_DOMINATORS, orig_dest));
+ }
+ return recompute_toporder_p;
+}
+
+/* This variable holds the cfg hooks used by the selective scheduler. */
+static struct cfg_hooks sel_cfg_hooks;
+
+/* Register sel-sched cfg hooks. */
+void
+sel_register_cfg_hooks (void)
+{
+ sched_split_block = sel_split_block;
+
+ orig_cfg_hooks = get_cfg_hooks ();
+ sel_cfg_hooks = orig_cfg_hooks;
+
+ sel_cfg_hooks.create_basic_block = sel_create_basic_block;
+
+ set_cfg_hooks (sel_cfg_hooks);
+
+ sched_init_only_bb = sel_init_only_bb;
+ sched_split_block = sel_split_block;
+ sched_create_empty_bb = sel_create_empty_bb;
+}
+
+/* Unregister sel-sched cfg hooks. */
+void
+sel_unregister_cfg_hooks (void)
+{
+ sched_create_empty_bb = NULL;
+ sched_split_block = NULL;
+ sched_init_only_bb = NULL;
+
+ set_cfg_hooks (orig_cfg_hooks);
+}
+
+
+/* Emit an insn rtx based on PATTERN. If a jump insn is wanted,
+ LABEL is where this jump should be directed. */
+rtx
+create_insn_rtx_from_pattern (rtx pattern, rtx label)
+{
+ rtx insn_rtx;
+
+ gcc_assert (!INSN_P (pattern));
+
+ start_sequence ();
+
+ if (label == NULL_RTX)
+ insn_rtx = emit_insn (pattern);
+ else if (DEBUG_INSN_P (label))
+ insn_rtx = emit_debug_insn (pattern);
+ else
+ {
+ insn_rtx = emit_jump_insn (pattern);
+ JUMP_LABEL (insn_rtx) = label;
+ ++LABEL_NUSES (label);
+ }
+
+ end_sequence ();
+
+ sched_extend_luids ();
+ sched_extend_target ();
+ sched_deps_init (false);
+
+ /* Initialize INSN_CODE now. */
+ recog_memoized (insn_rtx);
+ return insn_rtx;
+}
+
+/* Create a new vinsn for INSN_RTX. FORCE_UNIQUE_P is true when the vinsn
+ must not be clonable. */
+vinsn_t
+create_vinsn_from_insn_rtx (rtx insn_rtx, bool force_unique_p)
+{
+ gcc_assert (INSN_P (insn_rtx) && !INSN_IN_STREAM_P (insn_rtx));
+
+ /* If VINSN_TYPE is not USE, retain its uniqueness. */
+ return vinsn_create (insn_rtx, force_unique_p);
+}
+
+/* Create a copy of INSN_RTX. */
+rtx
+create_copy_of_insn_rtx (rtx insn_rtx)
+{
+ rtx res, link;
+
+ if (DEBUG_INSN_P (insn_rtx))
+ return create_insn_rtx_from_pattern (copy_rtx (PATTERN (insn_rtx)),
+ insn_rtx);
+
+ gcc_assert (NONJUMP_INSN_P (insn_rtx));
+
+ res = create_insn_rtx_from_pattern (copy_rtx (PATTERN (insn_rtx)),
+ NULL_RTX);
+
+ /* Copy all REG_NOTES except REG_EQUAL/REG_EQUIV and REG_LABEL_OPERAND
+ since mark_jump_label will make them. REG_LABEL_TARGETs are created
+ there too, but are supposed to be sticky, so we copy them. */
+ for (link = REG_NOTES (insn_rtx); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) != REG_LABEL_OPERAND
+ && REG_NOTE_KIND (link) != REG_EQUAL
+ && REG_NOTE_KIND (link) != REG_EQUIV)
+ {
+ if (GET_CODE (link) == EXPR_LIST)
+ add_reg_note (res, REG_NOTE_KIND (link),
+ copy_insn_1 (XEXP (link, 0)));
+ else
+ add_reg_note (res, REG_NOTE_KIND (link), XEXP (link, 0));
+ }
+
+ return res;
+}
+
+/* Change vinsn field of EXPR to hold NEW_VINSN. */
+void
+change_vinsn_in_expr (expr_t expr, vinsn_t new_vinsn)
+{
+ vinsn_detach (EXPR_VINSN (expr));
+
+ EXPR_VINSN (expr) = new_vinsn;
+ vinsn_attach (new_vinsn);
+}
+
+/* Helpers for global init. */
+/* This structure is used to be able to call existing bundling mechanism
+ and calculate insn priorities. */
+static struct haifa_sched_info sched_sel_haifa_sched_info =
+{
+ NULL, /* init_ready_list */
+ NULL, /* can_schedule_ready_p */
+ NULL, /* schedule_more_p */
+ NULL, /* new_ready */
+ NULL, /* rgn_rank */
+ sel_print_insn, /* rgn_print_insn */
+ contributes_to_priority,
+ NULL, /* insn_finishes_block_p */
+
+ NULL, NULL,
+ NULL, NULL,
+ 0, 0,
+
+ NULL, /* add_remove_insn */
+ NULL, /* begin_schedule_ready */
+ NULL, /* begin_move_insn */
+ NULL, /* advance_target_bb */
+
+ NULL,
+ NULL,
+
+ SEL_SCHED | NEW_BBS
+};
+
+/* Setup special insns used in the scheduler. */
+void
+setup_nop_and_exit_insns (void)
+{
+ gcc_assert (nop_pattern == NULL_RTX
+ && exit_insn == NULL_RTX);
+
+ nop_pattern = constm1_rtx;
+
+ start_sequence ();
+ emit_insn (nop_pattern);
+ exit_insn = get_insns ();
+ end_sequence ();
+ set_block_for_insn (exit_insn, EXIT_BLOCK_PTR_FOR_FN (cfun));
+}
+
+/* Free special insns used in the scheduler. */
+void
+free_nop_and_exit_insns (void)
+{
+ exit_insn = NULL_RTX;
+ nop_pattern = NULL_RTX;
+}
+
+/* Setup a special vinsn used in new insns initialization. */
+void
+setup_nop_vinsn (void)
+{
+ nop_vinsn = vinsn_create (exit_insn, false);
+ vinsn_attach (nop_vinsn);
+}
+
+/* Free a special vinsn used in new insns initialization. */
+void
+free_nop_vinsn (void)
+{
+ gcc_assert (VINSN_COUNT (nop_vinsn) == 1);
+ vinsn_detach (nop_vinsn);
+ nop_vinsn = NULL;
+}
+
+/* Call a set_sched_flags hook. */
+void
+sel_set_sched_flags (void)
+{
+ /* ??? This means that set_sched_flags were called, and we decided to
+ support speculation. However, set_sched_flags also modifies flags
+ on current_sched_info, doing this only at global init. And we
+ sometimes change c_s_i later. So put the correct flags again. */
+ if (spec_info && targetm.sched.set_sched_flags)
+ targetm.sched.set_sched_flags (spec_info);
+}
+
+/* Setup pointers to global sched info structures. */
+void
+sel_setup_sched_infos (void)
+{
+ rgn_setup_common_sched_info ();
+
+ memcpy (&sel_common_sched_info, common_sched_info,
+ sizeof (sel_common_sched_info));
+
+ sel_common_sched_info.fix_recovery_cfg = NULL;
+ sel_common_sched_info.add_block = NULL;
+ sel_common_sched_info.estimate_number_of_insns
+ = sel_estimate_number_of_insns;
+ sel_common_sched_info.luid_for_non_insn = sel_luid_for_non_insn;
+ sel_common_sched_info.sched_pass_id = SCHED_SEL_PASS;
+
+ common_sched_info = &sel_common_sched_info;
+
+ current_sched_info = &sched_sel_haifa_sched_info;
+ current_sched_info->sched_max_insns_priority =
+ get_rgn_sched_max_insns_priority ();
+
+ sel_set_sched_flags ();
+}
+
+
+/* Adds basic block BB to region RGN at the position *BB_ORD_INDEX,
+ *BB_ORD_INDEX after that is increased. */
+static void
+sel_add_block_to_region (basic_block bb, int *bb_ord_index, int rgn)
+{
+ RGN_NR_BLOCKS (rgn) += 1;
+ RGN_DONT_CALC_DEPS (rgn) = 0;
+ RGN_HAS_REAL_EBB (rgn) = 0;
+ CONTAINING_RGN (bb->index) = rgn;
+ BLOCK_TO_BB (bb->index) = *bb_ord_index;
+ rgn_bb_table[RGN_BLOCKS (rgn) + *bb_ord_index] = bb->index;
+ (*bb_ord_index)++;
+
+ /* FIXME: it is true only when not scheduling ebbs. */
+ RGN_BLOCKS (rgn + 1) = RGN_BLOCKS (rgn) + RGN_NR_BLOCKS (rgn);
+}
+
+/* Functions to support pipelining of outer loops. */
+
+/* Creates a new empty region and returns it's number. */
+static int
+sel_create_new_region (void)
+{
+ int new_rgn_number = nr_regions;
+
+ RGN_NR_BLOCKS (new_rgn_number) = 0;
+
+ /* FIXME: This will work only when EBBs are not created. */
+ if (new_rgn_number != 0)
+ RGN_BLOCKS (new_rgn_number) = RGN_BLOCKS (new_rgn_number - 1) +
+ RGN_NR_BLOCKS (new_rgn_number - 1);
+ else
+ RGN_BLOCKS (new_rgn_number) = 0;
+
+ /* Set the blocks of the next region so the other functions may
+ calculate the number of blocks in the region. */
+ RGN_BLOCKS (new_rgn_number + 1) = RGN_BLOCKS (new_rgn_number) +
+ RGN_NR_BLOCKS (new_rgn_number);
+
+ nr_regions++;
+
+ return new_rgn_number;
+}
+
+/* If X has a smaller topological sort number than Y, returns -1;
+ if greater, returns 1. */
+static int
+bb_top_order_comparator (const void *x, const void *y)
+{
+ basic_block bb1 = *(const basic_block *) x;
+ basic_block bb2 = *(const basic_block *) y;
+
+ gcc_assert (bb1 == bb2
+ || rev_top_order_index[bb1->index]
+ != rev_top_order_index[bb2->index]);
+
+ /* It's a reverse topological order in REV_TOP_ORDER_INDEX, so
+ bbs with greater number should go earlier. */
+ if (rev_top_order_index[bb1->index] > rev_top_order_index[bb2->index])
+ return -1;
+ else
+ return 1;
+}
+
+/* Create a region for LOOP and return its number. If we don't want
+ to pipeline LOOP, return -1. */
+static int
+make_region_from_loop (struct loop *loop)
+{
+ unsigned int i;
+ int new_rgn_number = -1;
+ struct loop *inner;
+
+ /* Basic block index, to be assigned to BLOCK_TO_BB. */
+ int bb_ord_index = 0;
+ basic_block *loop_blocks;
+ basic_block preheader_block;
+
+ if (loop->num_nodes
+ > (unsigned) PARAM_VALUE (PARAM_MAX_PIPELINE_REGION_BLOCKS))
+ return -1;
+
+ /* Don't pipeline loops whose latch belongs to some of its inner loops. */
+ for (inner = loop->inner; inner; inner = inner->inner)
+ if (flow_bb_inside_loop_p (inner, loop->latch))
+ return -1;
+
+ loop->ninsns = num_loop_insns (loop);
+ if ((int) loop->ninsns > PARAM_VALUE (PARAM_MAX_PIPELINE_REGION_INSNS))
+ return -1;
+
+ loop_blocks = get_loop_body_in_custom_order (loop, bb_top_order_comparator);
+
+ for (i = 0; i < loop->num_nodes; i++)
+ if (loop_blocks[i]->flags & BB_IRREDUCIBLE_LOOP)
+ {
+ free (loop_blocks);
+ return -1;
+ }
+
+ preheader_block = loop_preheader_edge (loop)->src;
+ gcc_assert (preheader_block);
+ gcc_assert (loop_blocks[0] == loop->header);
+
+ new_rgn_number = sel_create_new_region ();
+
+ sel_add_block_to_region (preheader_block, &bb_ord_index, new_rgn_number);
+ bitmap_set_bit (bbs_in_loop_rgns, preheader_block->index);
+
+ for (i = 0; i < loop->num_nodes; i++)
+ {
+ /* Add only those blocks that haven't been scheduled in the inner loop.
+ The exception is the basic blocks with bookkeeping code - they should
+ be added to the region (and they actually don't belong to the loop
+ body, but to the region containing that loop body). */
+
+ gcc_assert (new_rgn_number >= 0);
+
+ if (! bitmap_bit_p (bbs_in_loop_rgns, loop_blocks[i]->index))
+ {
+ sel_add_block_to_region (loop_blocks[i], &bb_ord_index,
+ new_rgn_number);
+ bitmap_set_bit (bbs_in_loop_rgns, loop_blocks[i]->index);
+ }
+ }
+
+ free (loop_blocks);
+ MARK_LOOP_FOR_PIPELINING (loop);
+
+ return new_rgn_number;
+}
+
+/* Create a new region from preheader blocks LOOP_BLOCKS. */
+void
+make_region_from_loop_preheader (vec<basic_block> *&loop_blocks)
+{
+ unsigned int i;
+ int new_rgn_number = -1;
+ basic_block bb;
+
+ /* Basic block index, to be assigned to BLOCK_TO_BB. */
+ int bb_ord_index = 0;
+
+ new_rgn_number = sel_create_new_region ();
+
+ FOR_EACH_VEC_ELT (*loop_blocks, i, bb)
+ {
+ gcc_assert (new_rgn_number >= 0);
+
+ sel_add_block_to_region (bb, &bb_ord_index, new_rgn_number);
+ }
+
+ vec_free (loop_blocks);
+}
+
+
+/* Create region(s) from loop nest LOOP, such that inner loops will be
+ pipelined before outer loops. Returns true when a region for LOOP
+ is created. */
+static bool
+make_regions_from_loop_nest (struct loop *loop)
+{
+ struct loop *cur_loop;
+ int rgn_number;
+
+ /* Traverse all inner nodes of the loop. */
+ for (cur_loop = loop->inner; cur_loop; cur_loop = cur_loop->next)
+ if (! bitmap_bit_p (bbs_in_loop_rgns, cur_loop->header->index))
+ return false;
+
+ /* At this moment all regular inner loops should have been pipelined.
+ Try to create a region from this loop. */
+ rgn_number = make_region_from_loop (loop);
+
+ if (rgn_number < 0)
+ return false;
+
+ loop_nests.safe_push (loop);
+ return true;
+}
+
+/* Initalize data structures needed. */
+void
+sel_init_pipelining (void)
+{
+ /* Collect loop information to be used in outer loops pipelining. */
+ loop_optimizer_init (LOOPS_HAVE_PREHEADERS
+ | LOOPS_HAVE_FALLTHRU_PREHEADERS
+ | LOOPS_HAVE_RECORDED_EXITS
+ | LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS);
+ current_loop_nest = NULL;
+
+ bbs_in_loop_rgns = sbitmap_alloc (last_basic_block_for_fn (cfun));
+ bitmap_clear (bbs_in_loop_rgns);
+
+ recompute_rev_top_order ();
+}
+
+/* Returns a struct loop for region RGN. */
+loop_p
+get_loop_nest_for_rgn (unsigned int rgn)
+{
+ /* Regions created with extend_rgns don't have corresponding loop nests,
+ because they don't represent loops. */
+ if (rgn < loop_nests.length ())
+ return loop_nests[rgn];
+ else
+ return NULL;
+}
+
+/* True when LOOP was included into pipelining regions. */
+bool
+considered_for_pipelining_p (struct loop *loop)
+{
+ if (loop_depth (loop) == 0)
+ return false;
+
+ /* Now, the loop could be too large or irreducible. Check whether its
+ region is in LOOP_NESTS.
+ We determine the region number of LOOP as the region number of its
+ latch. We can't use header here, because this header could be
+ just removed preheader and it will give us the wrong region number.
+ Latch can't be used because it could be in the inner loop too. */
+ if (LOOP_MARKED_FOR_PIPELINING_P (loop))
+ {
+ int rgn = CONTAINING_RGN (loop->latch->index);
+
+ gcc_assert ((unsigned) rgn < loop_nests.length ());
+ return true;
+ }
+
+ return false;
+}
+
+/* Makes regions from the rest of the blocks, after loops are chosen
+ for pipelining. */
+static void
+make_regions_from_the_rest (void)
+{
+ int cur_rgn_blocks;
+ int *loop_hdr;
+ int i;
+
+ basic_block bb;
+ edge e;
+ edge_iterator ei;
+ int *degree;
+
+ /* Index in rgn_bb_table where to start allocating new regions. */
+ cur_rgn_blocks = nr_regions ? RGN_BLOCKS (nr_regions) : 0;
+
+ /* Make regions from all the rest basic blocks - those that don't belong to
+ any loop or belong to irreducible loops. Prepare the data structures
+ for extend_rgns. */
+
+ /* LOOP_HDR[I] == -1 if I-th bb doesn't belong to any loop,
+ LOOP_HDR[I] == LOOP_HDR[J] iff basic blocks I and J reside within the same
+ loop. */
+ loop_hdr = XNEWVEC (int, last_basic_block_for_fn (cfun));
+ degree = XCNEWVEC (int, last_basic_block_for_fn (cfun));
+
+
+ /* For each basic block that belongs to some loop assign the number
+ of innermost loop it belongs to. */
+ for (i = 0; i < last_basic_block_for_fn (cfun); i++)
+ loop_hdr[i] = -1;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ if (bb->loop_father && !bb->loop_father->num == 0
+ && !(bb->flags & BB_IRREDUCIBLE_LOOP))
+ loop_hdr[bb->index] = bb->loop_father->num;
+ }
+
+ /* For each basic block degree is calculated as the number of incoming
+ edges, that are going out of bbs that are not yet scheduled.
+ The basic blocks that are scheduled have degree value of zero. */
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ degree[bb->index] = 0;
+
+ if (!bitmap_bit_p (bbs_in_loop_rgns, bb->index))
+ {
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (!bitmap_bit_p (bbs_in_loop_rgns, e->src->index))
+ degree[bb->index]++;
+ }
+ else
+ degree[bb->index] = -1;
+ }
+
+ extend_rgns (degree, &cur_rgn_blocks, bbs_in_loop_rgns, loop_hdr);
+
+ /* Any block that did not end up in a region is placed into a region
+ by itself. */
+ FOR_EACH_BB_FN (bb, cfun)
+ if (degree[bb->index] >= 0)
+ {
+ rgn_bb_table[cur_rgn_blocks] = bb->index;
+ RGN_NR_BLOCKS (nr_regions) = 1;
+ RGN_BLOCKS (nr_regions) = cur_rgn_blocks++;
+ RGN_DONT_CALC_DEPS (nr_regions) = 0;
+ RGN_HAS_REAL_EBB (nr_regions) = 0;
+ CONTAINING_RGN (bb->index) = nr_regions++;
+ BLOCK_TO_BB (bb->index) = 0;
+ }
+
+ free (degree);
+ free (loop_hdr);
+}
+
+/* Free data structures used in pipelining of loops. */
+void sel_finish_pipelining (void)
+{
+ struct loop *loop;
+
+ /* Release aux fields so we don't free them later by mistake. */
+ FOR_EACH_LOOP (loop, 0)
+ loop->aux = NULL;
+
+ loop_optimizer_finalize ();
+
+ loop_nests.release ();
+
+ free (rev_top_order_index);
+ rev_top_order_index = NULL;
+}
+
+/* This function replaces the find_rgns when
+ FLAG_SEL_SCHED_PIPELINING_OUTER_LOOPS is set. */
+void
+sel_find_rgns (void)
+{
+ sel_init_pipelining ();
+ extend_regions ();
+
+ if (current_loops)
+ {
+ loop_p loop;
+
+ FOR_EACH_LOOP (loop, (flag_sel_sched_pipelining_outer_loops
+ ? LI_FROM_INNERMOST
+ : LI_ONLY_INNERMOST))
+ make_regions_from_loop_nest (loop);
+ }
+
+ /* Make regions from all the rest basic blocks and schedule them.
+ These blocks include blocks that don't belong to any loop or belong
+ to irreducible loops. */
+ make_regions_from_the_rest ();
+
+ /* We don't need bbs_in_loop_rgns anymore. */
+ sbitmap_free (bbs_in_loop_rgns);
+ bbs_in_loop_rgns = NULL;
+}
+
+/* Add the preheader blocks from previous loop to current region taking
+ it from LOOP_PREHEADER_BLOCKS (current_loop_nest) and record them in *BBS.
+ This function is only used with -fsel-sched-pipelining-outer-loops. */
+void
+sel_add_loop_preheaders (bb_vec_t *bbs)
+{
+ int i;
+ basic_block bb;
+ vec<basic_block> *preheader_blocks
+ = LOOP_PREHEADER_BLOCKS (current_loop_nest);
+
+ if (!preheader_blocks)
+ return;
+
+ for (i = 0; preheader_blocks->iterate (i, &bb); i++)
+ {
+ bbs->safe_push (bb);
+ last_added_blocks.safe_push (bb);
+ sel_add_bb (bb);
+ }
+
+ vec_free (preheader_blocks);
+}
+
+/* While pipelining outer loops, returns TRUE if BB is a loop preheader.
+ Please note that the function should also work when pipelining_p is
+ false, because it is used when deciding whether we should or should
+ not reschedule pipelined code. */
+bool
+sel_is_loop_preheader_p (basic_block bb)
+{
+ if (current_loop_nest)
+ {
+ struct loop *outer;
+
+ if (preheader_removed)
+ return false;
+
+ /* Preheader is the first block in the region. */
+ if (BLOCK_TO_BB (bb->index) == 0)
+ return true;
+
+ /* We used to find a preheader with the topological information.
+ Check that the above code is equivalent to what we did before. */
+
+ if (in_current_region_p (current_loop_nest->header))
+ gcc_assert (!(BLOCK_TO_BB (bb->index)
+ < BLOCK_TO_BB (current_loop_nest->header->index)));
+
+ /* Support the situation when the latch block of outer loop
+ could be from here. */
+ for (outer = loop_outer (current_loop_nest);
+ outer;
+ outer = loop_outer (outer))
+ if (considered_for_pipelining_p (outer) && outer->latch == bb)
+ gcc_unreachable ();
+ }
+
+ return false;
+}
+
+/* Check whether JUMP_BB ends with a jump insn that leads only to DEST_BB and
+ can be removed, making the corresponding edge fallthrough (assuming that
+ all basic blocks between JUMP_BB and DEST_BB are empty). */
+static bool
+bb_has_removable_jump_to_p (basic_block jump_bb, basic_block dest_bb)
+{
+ if (!onlyjump_p (BB_END (jump_bb))
+ || tablejump_p (BB_END (jump_bb), NULL, NULL))
+ return false;
+
+ /* Several outgoing edges, abnormal edge or destination of jump is
+ not DEST_BB. */
+ if (EDGE_COUNT (jump_bb->succs) != 1
+ || EDGE_SUCC (jump_bb, 0)->flags & (EDGE_ABNORMAL | EDGE_CROSSING)
+ || EDGE_SUCC (jump_bb, 0)->dest != dest_bb)
+ return false;
+
+ /* If not anything of the upper. */
+ return true;
+}
+
+/* Removes the loop preheader from the current region and saves it in
+ PREHEADER_BLOCKS of the father loop, so they will be added later to
+ region that represents an outer loop. */
+static void
+sel_remove_loop_preheader (void)
+{
+ int i, old_len;
+ int cur_rgn = CONTAINING_RGN (BB_TO_BLOCK (0));
+ basic_block bb;
+ bool all_empty_p = true;
+ vec<basic_block> *preheader_blocks
+ = LOOP_PREHEADER_BLOCKS (loop_outer (current_loop_nest));
+
+ vec_check_alloc (preheader_blocks, 0);
+
+ gcc_assert (current_loop_nest);
+ old_len = preheader_blocks->length ();
+
+ /* Add blocks that aren't within the current loop to PREHEADER_BLOCKS. */
+ for (i = 0; i < RGN_NR_BLOCKS (cur_rgn); i++)
+ {
+ bb = BASIC_BLOCK_FOR_FN (cfun, BB_TO_BLOCK (i));
+
+ /* If the basic block belongs to region, but doesn't belong to
+ corresponding loop, then it should be a preheader. */
+ if (sel_is_loop_preheader_p (bb))
+ {
+ preheader_blocks->safe_push (bb);
+ if (BB_END (bb) != bb_note (bb))
+ all_empty_p = false;
+ }
+ }
+
+ /* Remove these blocks only after iterating over the whole region. */
+ for (i = preheader_blocks->length () - 1; i >= old_len; i--)
+ {
+ bb = (*preheader_blocks)[i];
+ sel_remove_bb (bb, false);
+ }
+
+ if (!considered_for_pipelining_p (loop_outer (current_loop_nest)))
+ {
+ if (!all_empty_p)
+ /* Immediately create new region from preheader. */
+ make_region_from_loop_preheader (preheader_blocks);
+ else
+ {
+ /* If all preheader blocks are empty - dont create new empty region.
+ Instead, remove them completely. */
+ FOR_EACH_VEC_ELT (*preheader_blocks, i, bb)
+ {
+ edge e;
+ edge_iterator ei;
+ basic_block prev_bb = bb->prev_bb, next_bb = bb->next_bb;
+
+ /* Redirect all incoming edges to next basic block. */
+ for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
+ {
+ if (! (e->flags & EDGE_FALLTHRU))
+ redirect_edge_and_branch (e, bb->next_bb);
+ else
+ redirect_edge_succ (e, bb->next_bb);
+ }
+ gcc_assert (BB_NOTE_LIST (bb) == NULL);
+ delete_and_free_basic_block (bb);
+
+ /* Check if after deleting preheader there is a nonconditional
+ jump in PREV_BB that leads to the next basic block NEXT_BB.
+ If it is so - delete this jump and clear data sets of its
+ basic block if it becomes empty. */
+ if (next_bb->prev_bb == prev_bb
+ && prev_bb != ENTRY_BLOCK_PTR_FOR_FN (cfun)
+ && bb_has_removable_jump_to_p (prev_bb, next_bb))
+ {
+ redirect_edge_and_branch (EDGE_SUCC (prev_bb, 0), next_bb);
+ if (BB_END (prev_bb) == bb_note (prev_bb))
+ free_data_sets (prev_bb);
+ }
+
+ set_immediate_dominator (CDI_DOMINATORS, next_bb,
+ recompute_dominator (CDI_DOMINATORS,
+ next_bb));
+ }
+ }
+ vec_free (preheader_blocks);
+ }
+ else
+ /* Store preheader within the father's loop structure. */
+ SET_LOOP_PREHEADER_BLOCKS (loop_outer (current_loop_nest),
+ preheader_blocks);
+}
+#endif