OSDN Git Service

* MAINTAINERS (Reviewers): Keep the list sorted.
[pf3gnuchains/gcc-fork.git] / gcc / modulo-sched.c
index 9e0df22..0ea9a4d 100644 (file)
@@ -124,7 +124,9 @@ typedef struct ps_insn *ps_insn_ptr;
 /* A single instruction in the partial schedule.  */
 struct ps_insn
 {
-  /* The number of the ddg node whose instruction is being scheduled.  */
+  /* Identifies the instruction to be scheduled.  Values smaller than
+     the ddg's num_nodes refer directly to ddg nodes.  A value of
+     X - num_nodes refers to register move X.  */
   int id;
 
   /* The (absolute) cycle in which the PS instruction is scheduled.
@@ -137,6 +139,33 @@ struct ps_insn
 
 };
 
+/* Information about a register move that has been added to a partial
+   schedule.  */
+struct ps_reg_move_info
+{
+  /* The source of the move is defined by the ps_insn with id DEF.
+     The destination is used by the ps_insns with the ids in USES.  */
+  int def;
+  sbitmap uses;
+
+  /* The original form of USES' instructions used OLD_REG, but they
+     should now use NEW_REG.  */
+  rtx old_reg;
+  rtx new_reg;
+
+  /* The number of consecutive stages that the move occupies.  */
+  int num_consecutive_stages;
+
+  /* An instruction that sets NEW_REG to the correct value.  The first
+     move associated with DEF will have an rhs of OLD_REG; later moves
+     use the result of the previous move.  */
+  rtx insn;
+};
+
+typedef struct ps_reg_move_info ps_reg_move_info;
+DEF_VEC_O (ps_reg_move_info);
+DEF_VEC_ALLOC_O (ps_reg_move_info, heap);
+
 /* Holds the partial schedule as an array of II rows.  Each entry of the
    array points to a linked list of PS_INSNs, which represents the
    instructions that are scheduled for that row.  */
@@ -148,6 +177,10 @@ struct partial_schedule
   /* rows[i] points to linked list of insns scheduled in row i (0<=i<ii).  */
   ps_insn_ptr *rows;
 
+  /* All the moves added for this partial schedule.  Index X has
+     a ps_insn id of X + g->num_nodes.  */
+  VEC (ps_reg_move_info, heap) *reg_moves;
+
   /*  rows_length[i] holds the number of instructions in the row.
       It is used only (as an optimization) to back off quickly from
       trying to schedule a node in a full row; that is, to avoid running
@@ -188,8 +221,6 @@ static partial_schedule_ptr sms_schedule_by_order (ddg_ptr, int, int, int *);
 static void permute_partial_schedule (partial_schedule_ptr, rtx);
 static void generate_prolog_epilog (partial_schedule_ptr, struct loop *,
                                     rtx, rtx);
-static void duplicate_insns_of_cycles (partial_schedule_ptr,
-                                      int, int, int, rtx);
 static int calculate_stage_count (partial_schedule_ptr, int);
 static void calculate_must_precede_follow (ddg_node_ptr, int, int,
                                           int, int, sbitmap, sbitmap, sbitmap);
@@ -201,10 +232,8 @@ static void remove_node_from_ps (partial_schedule_ptr, ps_insn_ptr);
 
 #define NODE_ASAP(node) ((node)->aux.count)
 
-#define SCHED_PARAMS(x) (&node_sched_params[x])
+#define SCHED_PARAMS(x) VEC_index (node_sched_params, node_sched_param_vec, x)
 #define SCHED_TIME(x) (SCHED_PARAMS (x)->time)
-#define SCHED_FIRST_REG_MOVE(x) (SCHED_PARAMS (x)->first_reg_move)
-#define SCHED_NREG_MOVES(x) (SCHED_PARAMS (x)->nreg_moves)
 #define SCHED_ROW(x) (SCHED_PARAMS (x)->row)
 #define SCHED_STAGE(x) (SCHED_PARAMS (x)->stage)
 #define SCHED_COLUMN(x) (SCHED_PARAMS (x)->column)
@@ -214,16 +243,6 @@ typedef struct node_sched_params
 {
   int time;    /* The absolute scheduling cycle.  */
 
-  /* The following field (first_reg_move) is a pointer to the first
-     register-move instruction added to handle the modulo-variable-expansion
-     of the register defined by this node.  This register-move copies the
-     original register defined by the node.  */
-  rtx first_reg_move;
-
-  /* The number of register-move instructions added, immediately preceding
-     first_reg_move.  */
-  int nreg_moves;
-
   int row;    /* Holds time % ii.  */
   int stage;  /* Holds time / ii.  */
 
@@ -232,6 +251,9 @@ typedef struct node_sched_params
   int column;
 } *node_sched_params_ptr;
 
+typedef struct node_sched_params node_sched_params;
+DEF_VEC_O (node_sched_params);
+DEF_VEC_ALLOC_O (node_sched_params, heap);
 \f
 /* The following three functions are copied from the current scheduler
    code in order to use sched_analyze() for computing the dependencies.
@@ -280,23 +302,49 @@ static struct haifa_sched_info sms_sched_info =
   0
 };
 
+/* Partial schedule instruction ID in PS is a register move.  Return
+   information about it.  */
+static struct ps_reg_move_info *
+ps_reg_move (partial_schedule_ptr ps, int id)
+{
+  gcc_checking_assert (id >= ps->g->num_nodes);
+  return VEC_index (ps_reg_move_info, ps->reg_moves, id - ps->g->num_nodes);
+}
+
 /* Return the rtl instruction that is being scheduled by partial schedule
    instruction ID, which belongs to schedule PS.  */
 static rtx
 ps_rtl_insn (partial_schedule_ptr ps, int id)
 {
-  return ps->g->nodes[id].insn;
+  if (id < ps->g->num_nodes)
+    return ps->g->nodes[id].insn;
+  else
+    return ps_reg_move (ps, id)->insn;
 }
 
-/* Return the first instruction in the original (unscheduled) loop that
-   was associated with ps_rtl_insn (PS, ID).  If the instruction had
-   some notes before it, this is the first of those notes.  */
+/* Partial schedule instruction ID, which belongs to PS, occured in
+   the original (unscheduled) loop.  Return the first instruction
+   in the loop that was associated with ps_rtl_insn (PS, ID).
+   If the instruction had some notes before it, this is the first
+   of those notes.  */
 static rtx
 ps_first_note (partial_schedule_ptr ps, int id)
 {
+  gcc_assert (id < ps->g->num_nodes);
   return ps->g->nodes[id].first_note;
 }
 
+/* Return the number of consecutive stages that are occupied by
+   partial schedule instruction ID in PS.  */
+static int
+ps_num_consecutive_stages (partial_schedule_ptr ps, int id)
+{
+  if (id < ps->g->num_nodes)
+    return 1;
+  else
+    return ps_reg_move (ps, id)->num_consecutive_stages;
+}
+
 /* Given HEAD and TAIL which are the first and last insns in a loop;
    return the register which controls the loop.  Return zero if it has
    more than one occurrence in the loop besides the control part or the
@@ -397,18 +445,59 @@ res_MII (ddg_ptr g)
 }
 
 
-/* Points to the array that contains the sched data for each node.  */
-static node_sched_params_ptr node_sched_params;
+/* A vector that contains the sched data for each ps_insn.  */
+static VEC (node_sched_params, heap) *node_sched_param_vec;
 
 /* Allocate sched_params for each node and initialize it.  */
 static void
 set_node_sched_params (ddg_ptr g)
 {
-  node_sched_params = XCNEWVEC (struct node_sched_params, g->num_nodes);
+  VEC_truncate (node_sched_params, node_sched_param_vec, 0);
+  VEC_safe_grow_cleared (node_sched_params, heap,
+                        node_sched_param_vec, g->num_nodes);
 }
 
+/* Make sure that node_sched_param_vec has an entry for every move in PS.  */
+static void
+extend_node_sched_params (partial_schedule_ptr ps)
+{
+  VEC_safe_grow_cleared (node_sched_params, heap, node_sched_param_vec,
+                        ps->g->num_nodes + VEC_length (ps_reg_move_info,
+                                                       ps->reg_moves));
+}
+
+/* Update the sched_params (time, row and stage) for node U using the II,
+   the CYCLE of U and MIN_CYCLE.
+   We're not simply taking the following
+   SCHED_STAGE (u) = CALC_STAGE_COUNT (SCHED_TIME (u), min_cycle, ii);
+   because the stages may not be aligned on cycle 0.  */
 static void
-print_node_sched_params (FILE *file, int num_nodes, ddg_ptr g)
+update_node_sched_params (int u, int ii, int cycle, int min_cycle)
+{
+  int sc_until_cycle_zero;
+  int stage;
+
+  SCHED_TIME (u) = cycle;
+  SCHED_ROW (u) = SMODULO (cycle, ii);
+
+  /* The calculation of stage count is done adding the number
+     of stages before cycle zero and after cycle zero.  */
+  sc_until_cycle_zero = CALC_STAGE_COUNT (-1, min_cycle, ii);
+
+  if (SCHED_TIME (u) < 0)
+    {
+      stage = CALC_STAGE_COUNT (-1, SCHED_TIME (u), ii);
+      SCHED_STAGE (u) = sc_until_cycle_zero - stage;
+    }
+  else
+    {
+      stage = CALC_STAGE_COUNT (SCHED_TIME (u), 0, ii);
+      SCHED_STAGE (u) = sc_until_cycle_zero + stage - 1;
+    }
+}
+
+static void
+print_node_sched_params (FILE *file, int num_nodes, partial_schedule_ptr ps)
 {
   int i;
 
@@ -417,21 +506,169 @@ print_node_sched_params (FILE *file, int num_nodes, ddg_ptr g)
   for (i = 0; i < num_nodes; i++)
     {
       node_sched_params_ptr nsp = SCHED_PARAMS (i);
-      rtx reg_move = nsp->first_reg_move;
-      int j;
 
       fprintf (file, "Node = %d; INSN = %d\n", i,
-              (INSN_UID (g->nodes[i].insn)));
-      fprintf (file, " asap = %d:\n", NODE_ASAP (&g->nodes[i]));
+              INSN_UID (ps_rtl_insn (ps, i)));
+      fprintf (file, " asap = %d:\n", NODE_ASAP (&ps->g->nodes[i]));
       fprintf (file, " time = %d:\n", nsp->time);
-      fprintf (file, " nreg_moves = %d:\n", nsp->nreg_moves);
-      for (j = 0; j < nsp->nreg_moves; j++)
+      fprintf (file, " stage = %d:\n", nsp->stage);
+    }
+}
+
+/* Set SCHED_COLUMN for each instruction in row ROW of PS.  */
+static void
+set_columns_for_row (partial_schedule_ptr ps, int row)
+{
+  ps_insn_ptr cur_insn;
+  int column;
+
+  column = 0;
+  for (cur_insn = ps->rows[row]; cur_insn; cur_insn = cur_insn->next_in_row)
+    SCHED_COLUMN (cur_insn->id) = column++;
+}
+
+/* Set SCHED_COLUMN for each instruction in PS.  */
+static void
+set_columns_for_ps (partial_schedule_ptr ps)
+{
+  int row;
+
+  for (row = 0; row < ps->ii; row++)
+    set_columns_for_row (ps, row);
+}
+
+/* Try to schedule the move with ps_insn identifier I_REG_MOVE in PS.
+   Its single predecessor has already been scheduled, as has its
+   ddg node successors.  (The move may have also another move as its
+   successor, in which case that successor will be scheduled later.)
+
+   The move is part of a chain that satisfies register dependencies
+   between a producing ddg node and various consuming ddg nodes.
+   If some of these dependencies have a distance of 1 (meaning that
+   the use is upward-exposed) then DISTANCE1_USES is nonnull and
+   contains the set of uses with distance-1 dependencies.
+   DISTANCE1_USES is null otherwise.
+
+   MUST_FOLLOW is a scratch bitmap that is big enough to hold
+   all current ps_insn ids.
+
+   Return true on success.  */
+static bool
+schedule_reg_move (partial_schedule_ptr ps, int i_reg_move,
+                  sbitmap distance1_uses, sbitmap must_follow)
+{
+  unsigned int u;
+  int this_time, this_distance, this_start, this_end, this_latency;
+  int start, end, c, ii;
+  sbitmap_iterator sbi;
+  ps_reg_move_info *move;
+  rtx this_insn;
+  ps_insn_ptr psi;
+
+  move = ps_reg_move (ps, i_reg_move);
+  ii = ps->ii;
+  if (dump_file)
+    {
+      fprintf (dump_file, "Scheduling register move INSN %d; ii = %d"
+              ", min cycle = %d\n\n", INSN_UID (move->insn), ii,
+              PS_MIN_CYCLE (ps));
+      print_rtl_single (dump_file, move->insn);
+      fprintf (dump_file, "\n%11s %11s %5s\n", "start", "end", "time");
+      fprintf (dump_file, "=========== =========== =====\n");
+    }
+
+  start = INT_MIN;
+  end = INT_MAX;
+
+  /* For dependencies of distance 1 between a producer ddg node A
+     and consumer ddg node B, we have a chain of dependencies:
+
+        A --(T,L1,1)--> M1 --(T,L2,0)--> M2 ... --(T,Ln,0)--> B
+
+     where Mi is the ith move.  For dependencies of distance 0 between
+     a producer ddg node A and consumer ddg node C, we have a chain of
+     dependencies:
+
+        A --(T,L1',0)--> M1' --(T,L2',0)--> M2' ... --(T,Ln',0)--> C
+
+     where Mi' occupies the same position as Mi but occurs a stage later.
+     We can only schedule each move once, so if we have both types of
+     chain, we model the second as:
+
+        A --(T,L1',1)--> M1 --(T,L2',0)--> M2 ... --(T,Ln',-1)--> C
+
+     First handle the dependencies between the previously-scheduled
+     predecessor and the move.  */
+  this_insn = ps_rtl_insn (ps, move->def);
+  this_latency = insn_latency (this_insn, move->insn);
+  this_distance = distance1_uses && move->def < ps->g->num_nodes ? 1 : 0;
+  this_time = SCHED_TIME (move->def) - this_distance * ii;
+  this_start = this_time + this_latency;
+  this_end = this_time + ii;
+  if (dump_file)
+    fprintf (dump_file, "%11d %11d %5d %d --(T,%d,%d)--> %d\n",
+            this_start, this_end, SCHED_TIME (move->def),
+            INSN_UID (this_insn), this_latency, this_distance,
+            INSN_UID (move->insn));
+
+  if (start < this_start)
+    start = this_start;
+  if (end > this_end)
+    end = this_end;
+
+  /* Handle the dependencies between the move and previously-scheduled
+     successors.  */
+  EXECUTE_IF_SET_IN_SBITMAP (move->uses, 0, u, sbi)
+    {
+      this_insn = ps_rtl_insn (ps, u);
+      this_latency = insn_latency (move->insn, this_insn);
+      if (distance1_uses && !TEST_BIT (distance1_uses, u))
+       this_distance = -1;
+      else
+       this_distance = 0;
+      this_time = SCHED_TIME (u) + this_distance * ii;
+      this_start = this_time - ii;
+      this_end = this_time - this_latency;
+      if (dump_file)
+       fprintf (dump_file, "%11d %11d %5d %d --(T,%d,%d)--> %d\n",
+                this_start, this_end, SCHED_TIME (u), INSN_UID (move->insn),
+                this_latency, this_distance, INSN_UID (this_insn));
+
+      if (start < this_start)
+       start = this_start;
+      if (end > this_end)
+       end = this_end;
+    }
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "----------- ----------- -----\n");
+      fprintf (dump_file, "%11d %11d %5s %s\n", start, end, "", "(max, min)");
+    }
+
+  sbitmap_zero (must_follow);
+  SET_BIT (must_follow, move->def);
+
+  start = MAX (start, end - (ii - 1));
+  for (c = end; c >= start; c--)
+    {
+      psi = ps_add_node_check_conflicts (ps, i_reg_move, c,
+                                        move->uses, must_follow);
+      if (psi)
        {
-         fprintf (file, " reg_move = ");
-         print_rtl_single (file, reg_move);
-         reg_move = PREV_INSN (reg_move);
+         update_node_sched_params (i_reg_move, ii, c, PS_MIN_CYCLE (ps));
+         if (dump_file)
+           fprintf (dump_file, "\nScheduled register move INSN %d at"
+                    " time %d, row %d\n\n", INSN_UID (move->insn), c,
+                    SCHED_ROW (i_reg_move));
+         return true;
        }
     }
+
+  if (dump_file)
+    fprintf (dump_file, "\nNo available slot\n\n");
+
+  return false;
 }
 
 /*
@@ -445,8 +682,8 @@ print_node_sched_params (FILE *file, int num_nodes, ddg_ptr g)
    nreg_moves = ----------------------------------- + 1 - {   dependence.
                             ii                          { 1 if not.
 */
-static void
-generate_reg_moves (partial_schedule_ptr ps, bool rescan)
+static bool
+schedule_reg_moves (partial_schedule_ptr ps)
 {
   ddg_ptr g = ps->g;
   int ii = ps->ii;
@@ -457,9 +694,11 @@ generate_reg_moves (partial_schedule_ptr ps, bool rescan)
       ddg_node_ptr u = &g->nodes[i];
       ddg_edge_ptr e;
       int nreg_moves = 0, i_reg_move;
-      sbitmap *uses_of_defs;
-      rtx last_reg_move;
       rtx prev_reg, old_reg;
+      int first_move;
+      int distances[2];
+      sbitmap must_follow;
+      sbitmap distance1_uses;
       rtx set = single_set (u->insn);
       
       /* Skip instructions that do not set a register.  */
@@ -468,6 +707,7 @@ generate_reg_moves (partial_schedule_ptr ps, bool rescan)
  
       /* Compute the number of reg_moves needed for u, by looking at life
         ranges started at u (excluding self-loops).  */
+      distances[0] = distances[1] = false;
       for (e = u->out; e; e = e->next_out)
        if (e->type == TRUE_DEP && e->dest != e->src)
          {
@@ -498,18 +738,48 @@ generate_reg_moves (partial_schedule_ptr ps, bool rescan)
                gcc_assert (!autoinc_var_is_used_p (u->insn, e->dest->insn));
              }
            
+           if (nreg_moves4e)
+             {
+               gcc_assert (e->distance < 2);
+               distances[e->distance] = true;
+             }
            nreg_moves = MAX (nreg_moves, nreg_moves4e);
          }
 
       if (nreg_moves == 0)
        continue;
 
+      /* Create NREG_MOVES register moves.  */
+      first_move = VEC_length (ps_reg_move_info, ps->reg_moves);
+      VEC_safe_grow_cleared (ps_reg_move_info, heap, ps->reg_moves,
+                            first_move + nreg_moves);
+      extend_node_sched_params (ps);
+
+      /* Record the moves associated with this node.  */
+      first_move += ps->g->num_nodes;
+
+      /* Generate each move.  */
+      old_reg = prev_reg = SET_DEST (single_set (u->insn));
+      for (i_reg_move = 0; i_reg_move < nreg_moves; i_reg_move++)
+       {
+         ps_reg_move_info *move = ps_reg_move (ps, first_move + i_reg_move);
+
+         move->def = i_reg_move > 0 ? first_move + i_reg_move - 1 : i;
+         move->uses = sbitmap_alloc (first_move + nreg_moves);
+         move->old_reg = old_reg;
+         move->new_reg = gen_reg_rtx (GET_MODE (prev_reg));
+         move->num_consecutive_stages = distances[0] && distances[1] ? 2 : 1;
+         move->insn = gen_move_insn (move->new_reg, copy_rtx (prev_reg));
+         sbitmap_zero (move->uses);
+
+         prev_reg = move->new_reg;
+       }
+
+      distance1_uses = distances[1] ? sbitmap_alloc (g->num_nodes) : NULL;
+
       /* Every use of the register defined by node may require a different
         copy of this register, depending on the time the use is scheduled.
-        Set a bitmap vector, telling which nodes use each copy of this
-        register.  */
-      uses_of_defs = sbitmap_vector_alloc (nreg_moves, g->num_nodes);
-      sbitmap_vector_zero (uses_of_defs, nreg_moves);
+        Record which uses require which move results.  */
       for (e = u->out; e; e = e->next_out)
        if (e->type == TRUE_DEP && e->dest != e->src)
          {
@@ -525,69 +795,48 @@ generate_reg_moves (partial_schedule_ptr ps, bool rescan)
              dest_copy--;
 
            if (dest_copy)
-             SET_BIT (uses_of_defs[dest_copy - 1], e->dest->cuid);
-         }
+             {
+               ps_reg_move_info *move;
 
-      /* Now generate the reg_moves, attaching relevant uses to them.  */
-      SCHED_NREG_MOVES (i) = nreg_moves;
-      old_reg = prev_reg = copy_rtx (SET_DEST (single_set (u->insn)));
-      /* Insert the reg-moves right before the notes which precede
-         the insn they relates to.  */
-      last_reg_move = u->first_note;
+               move = ps_reg_move (ps, first_move + dest_copy - 1);
+               SET_BIT (move->uses, e->dest->cuid);
+               if (e->distance == 1)
+                 SET_BIT (distance1_uses, e->dest->cuid);
+             }
+         }
 
+      must_follow = sbitmap_alloc (first_move + nreg_moves);
       for (i_reg_move = 0; i_reg_move < nreg_moves; i_reg_move++)
-       {
-         unsigned int i_use = 0;
-         rtx new_reg = gen_reg_rtx (GET_MODE (prev_reg));
-         rtx reg_move = gen_move_insn (new_reg, prev_reg);
-         sbitmap_iterator sbi;
-
-         add_insn_before (reg_move, last_reg_move, NULL);
-         last_reg_move = reg_move;
-
-         if (!SCHED_FIRST_REG_MOVE (i))
-           SCHED_FIRST_REG_MOVE (i) = reg_move;
-
-         EXECUTE_IF_SET_IN_SBITMAP (uses_of_defs[i_reg_move], 0, i_use, sbi)
-           {
-             replace_rtx (g->nodes[i_use].insn, old_reg, new_reg);
-             if (rescan)
-               df_insn_rescan (g->nodes[i_use].insn);
-           }
-
-         prev_reg = new_reg;
-       }
-      sbitmap_vector_free (uses_of_defs);
+       if (!schedule_reg_move (ps, first_move + i_reg_move,
+                               distance1_uses, must_follow))
+         break;
+      sbitmap_free (must_follow);
+      if (distance1_uses)
+       sbitmap_free (distance1_uses);
+      if (i_reg_move < nreg_moves)
+       return false;
     }
+  return true;
 }
 
-/* Update the sched_params (time, row and stage) for node U using the II,
-   the CYCLE of U and MIN_CYCLE.  
-   We're not simply taking the following
-   SCHED_STAGE (u) = CALC_STAGE_COUNT (SCHED_TIME (u), min_cycle, ii);
-   because the stages may not be aligned on cycle 0.  */
+/* Emit the moves associatied with PS.  Apply the substitutions
+   associated with them.  */
 static void
-update_node_sched_params (int u, int ii, int cycle, int min_cycle)
+apply_reg_moves (partial_schedule_ptr ps)
 {
-  int sc_until_cycle_zero;
-  int stage;
-
-  SCHED_TIME (u) = cycle;
-  SCHED_ROW (u) = SMODULO (cycle, ii);
-
-  /* The calculation of stage count is done adding the number
-     of stages before cycle zero and after cycle zero.  */
-  sc_until_cycle_zero = CALC_STAGE_COUNT (-1, min_cycle, ii);
+  ps_reg_move_info *move;
+  int i;
 
-  if (SCHED_TIME (u) < 0)
-    {
-      stage = CALC_STAGE_COUNT (-1, SCHED_TIME (u), ii);
-      SCHED_STAGE (u) = sc_until_cycle_zero - stage;
-    }
-  else
+  FOR_EACH_VEC_ELT (ps_reg_move_info, ps->reg_moves, i, move)
     {
-      stage = CALC_STAGE_COUNT (SCHED_TIME (u), 0, ii);
-      SCHED_STAGE (u) = sc_until_cycle_zero + stage - 1;
+      unsigned int i_use;
+      sbitmap_iterator sbi;
+
+      EXECUTE_IF_SET_IN_SBITMAP (move->uses, 0, i_use, sbi)
+       {
+         replace_rtx (ps->g->nodes[i_use].insn, move->old_reg, move->new_reg);
+         df_insn_rescan (ps->g->nodes[i_use].insn);
+       }
     }
 }
 
@@ -629,22 +878,6 @@ reset_sched_times (partial_schedule_ptr ps, int amount)
       }
 }
  
-/* Set SCHED_COLUMN of each node according to its position in PS.  */
-static void
-set_columns_for_ps (partial_schedule_ptr ps)
-{
-  int row;
-
-  for (row = 0; row < ps->ii; row++)
-    {
-      ps_insn_ptr cur_insn = ps->rows[row];
-      int column = 0;
-
-      for (; cur_insn; cur_insn = cur_insn->next_in_row)
-       SCHED_COLUMN (cur_insn->id) = column++;
-    }
-}
-
 /* Permute the insns according to their order in PS, from row 0 to
    row ii-1, and position them right before LAST.  This schedules
    the insns of the loop kernel.  */
@@ -661,8 +894,13 @@ permute_partial_schedule (partial_schedule_ptr ps, rtx last)
        rtx insn = ps_rtl_insn (ps, ps_ij->id);
 
        if (PREV_INSN (last) != insn)
-         reorder_insns_nobb (ps_first_note (ps, ps_ij->id), insn,
-                             PREV_INSN (last));
+         {
+           if (ps_ij->id < ps->g->num_nodes)
+             reorder_insns_nobb (ps_first_note (ps, ps_ij->id), insn,
+                                 PREV_INSN (last));
+           else
+             add_insn_before (insn, last, NULL);
+         }
       }
 }
 
@@ -865,7 +1103,7 @@ clear:
 
 static void
 duplicate_insns_of_cycles (partial_schedule_ptr ps, int from_stage,
-                          int to_stage, int for_prolog, rtx count_reg)
+                          int to_stage, rtx count_reg)
 {
   int row;
   ps_insn_ptr ps_ij;
@@ -874,8 +1112,7 @@ duplicate_insns_of_cycles (partial_schedule_ptr ps, int from_stage,
     for (ps_ij = ps->rows[row]; ps_ij; ps_ij = ps_ij->next_in_row)
       {
        int u = ps_ij->id;
-       int j, i_reg_moves;
-       rtx reg_move = NULL_RTX;
+       int first_u, last_u;
        rtx u_insn;
 
         /* Do not duplicate any insn which refers to count_reg as it
@@ -889,48 +1126,15 @@ duplicate_insns_of_cycles (partial_schedule_ptr ps, int from_stage,
             || JUMP_P (u_insn))
           continue;
 
-       if (for_prolog)
-         {
-           /* SCHED_STAGE (u) >= from_stage == 0.  Generate increasing
-              number of reg_moves starting with the second occurrence of
-              u, which is generated if its SCHED_STAGE <= to_stage.  */
-           i_reg_moves = to_stage - SCHED_STAGE (u) + 1;
-           i_reg_moves = MAX (i_reg_moves, 0);
-           i_reg_moves = MIN (i_reg_moves, SCHED_NREG_MOVES (u));
-
-           /* The reg_moves start from the *first* reg_move backwards.  */
-           if (i_reg_moves)
-             {
-               reg_move = SCHED_FIRST_REG_MOVE (u);
-               for (j = 1; j < i_reg_moves; j++)
-                 reg_move = PREV_INSN (reg_move);
-             }
-         }
-       else /* It's for the epilog.  */
+       first_u = SCHED_STAGE (u);
+       last_u = first_u + ps_num_consecutive_stages (ps, u) - 1;
+       if (from_stage <= last_u && to_stage >= first_u)
          {
-           /* SCHED_STAGE (u) <= to_stage.  Generate all reg_moves,
-              starting to decrease one stage after u no longer occurs;
-              that is, generate all reg_moves until
-              SCHED_STAGE (u) == from_stage - 1.  */
-           i_reg_moves = (SCHED_NREG_MOVES (u)
-                          - (from_stage - SCHED_STAGE (u) - 1));
-           i_reg_moves = MAX (i_reg_moves, 0);
-           i_reg_moves = MIN (i_reg_moves, SCHED_NREG_MOVES (u));
-
-           /* The reg_moves start from the *last* reg_move forwards.  */
-           if (i_reg_moves)
-             {
-               reg_move = SCHED_FIRST_REG_MOVE (u);
-               for (j = 1; j < SCHED_NREG_MOVES (u); j++)
-                 reg_move = PREV_INSN (reg_move);
-             }
+           if (u < ps->g->num_nodes)
+             duplicate_insn_chain (ps_first_note (ps, u), u_insn);
+           else
+             emit_insn (copy_rtx (PATTERN (u_insn)));
          }
-
-       for (j = 0; j < i_reg_moves; j++, reg_move = NEXT_INSN (reg_move))
-         emit_insn (copy_rtx (PATTERN (reg_move)));
-       if (SCHED_STAGE (u) >= from_stage
-           && SCHED_STAGE (u) <= to_stage)
-         duplicate_insn_chain (ps_first_note (ps, u), u_insn);
       }
 }
 
@@ -964,11 +1168,13 @@ generate_prolog_epilog (partial_schedule_ptr ps, struct loop *loop,
     }
 
   for (i = 0; i < last_stage; i++)
-    duplicate_insns_of_cycles (ps, 0, i, 1, count_reg);
+    duplicate_insns_of_cycles (ps, 0, i, count_reg);
 
   /* Put the prolog on the entry edge.  */
   e = loop_preheader_edge (loop);
   split_edge_and_insert (e, get_insns ());
+  if (!flag_resched_modulo_sched)
+    e->dest->flags |= BB_DISABLE_SCHEDULE;
 
   end_sequence ();
 
@@ -976,15 +1182,30 @@ generate_prolog_epilog (partial_schedule_ptr ps, struct loop *loop,
   start_sequence ();
 
   for (i = 0; i < last_stage; i++)
-    duplicate_insns_of_cycles (ps, i + 1, last_stage, 0, count_reg);
+    duplicate_insns_of_cycles (ps, i + 1, last_stage, count_reg);
 
   /* Put the epilogue on the exit edge.  */
   gcc_assert (single_exit (loop));
   e = single_exit (loop);
   split_edge_and_insert (e, get_insns ());
+  if (!flag_resched_modulo_sched)
+    e->dest->flags |= BB_DISABLE_SCHEDULE;
+
   end_sequence ();
 }
 
+/* Mark LOOP as software pipelined so the later
+   scheduling passes don't touch it.  */
+static void
+mark_loop_unsched (struct loop *loop)
+{
+  unsigned i;
+  basic_block *bbs = get_loop_body (loop);
+
+  for (i = 0; i < loop->num_nodes; i++)
+    bbs[i]->flags |= BB_DISABLE_SCHEDULE;
+}
+
 /* Return true if all the BBs of the loop are empty except the
    loop header.  */
 static bool
@@ -1312,10 +1533,9 @@ sms_schedule (void)
     {
       rtx head, tail;
       rtx count_reg, count_init;
-      int mii, rec_mii;
-      unsigned stage_count = 0;
+      int mii, rec_mii, stage_count, min_cycle;
       HOST_WIDEST_INT loop_count = 0;
-      bool opt_sc_p = false;
+      bool opt_sc_p;
 
       if (! (g = g_arr[loop->num]))
         continue;
@@ -1392,54 +1612,59 @@ sms_schedule (void)
        fprintf (dump_file, "SMS iis %d %d %d (rec_mii, mii, maxii)\n",
                 rec_mii, mii, maxii);
 
-      set_node_sched_params (g);
-
-      ps = sms_schedule_by_order (g, mii, maxii, node_order);
-      
-      if (ps)
+      for (;;)
        {
-         /* Try to achieve optimized SC by normalizing the partial
-            schedule (having the cycles start from cycle zero).
-            The branch location must be placed in row ii-1 in the
-            final scheduling.  If failed, shift all instructions to
-            position the branch in row ii-1.  */
-         opt_sc_p = optimize_sc (ps, g);
-         if (opt_sc_p)
-           stage_count = calculate_stage_count (ps, 0);
-         else
+         set_node_sched_params (g);
+
+         stage_count = 0;
+         opt_sc_p = false;
+         ps = sms_schedule_by_order (g, mii, maxii, node_order);
+
+         if (ps)
            {
-             /* Bring the branch to cycle ii-1.  */
-             int amount = SCHED_TIME (g->closing_branch->cuid) - (ps->ii - 1);
-             
-             if (dump_file)
-               fprintf (dump_file, "SMS schedule branch at cycle ii-1\n");
-             
-             stage_count = calculate_stage_count (ps, amount);
+             /* Try to achieve optimized SC by normalizing the partial
+                schedule (having the cycles start from cycle zero).
+                The branch location must be placed in row ii-1 in the
+                final scheduling.      If failed, shift all instructions to
+                position the branch in row ii-1.  */
+             opt_sc_p = optimize_sc (ps, g);
+             if (opt_sc_p)
+               stage_count = calculate_stage_count (ps, 0);
+             else
+               {
+                 /* Bring the branch to cycle ii-1.  */
+                 int amount = (SCHED_TIME (g->closing_branch->cuid)
+                               - (ps->ii - 1));
+
+                 if (dump_file)
+                   fprintf (dump_file, "SMS schedule branch at cycle ii-1\n");
+
+                 stage_count = calculate_stage_count (ps, amount);
+               }
+
+             gcc_assert (stage_count >= 1);
            }
-         
-         gcc_assert (stage_count >= 1);
-         PS_STAGE_COUNT (ps) = stage_count;
-       }
-      
-      /* The default value of PARAM_SMS_MIN_SC is 2 as stage count of
-        1 means that there is no interleaving between iterations thus
-        we let the scheduling passes do the job in this case.  */
-      if (stage_count < (unsigned) PARAM_VALUE (PARAM_SMS_MIN_SC)
-         || (count_init && (loop_count <= stage_count))
-         || (flag_branch_probabilities && (trip_count <= stage_count)))
-       {
-         if (dump_file)
+
+         /* The default value of PARAM_SMS_MIN_SC is 2 as stage count of
+            1 means that there is no interleaving between iterations thus
+            we let the scheduling passes do the job in this case.  */
+         if (stage_count < PARAM_VALUE (PARAM_SMS_MIN_SC)
+             || (count_init && (loop_count <= stage_count))
+             || (flag_branch_probabilities && (trip_count <= stage_count)))
            {
-             fprintf (dump_file, "SMS failed... \n");
-             fprintf (dump_file, "SMS sched-failed (stage-count=%d, loop-count=", stage_count);
-             fprintf (dump_file, HOST_WIDEST_INT_PRINT_DEC, loop_count);
-             fprintf (dump_file, ", trip-count=");
-             fprintf (dump_file, HOST_WIDEST_INT_PRINT_DEC, trip_count);
-             fprintf (dump_file, ")\n");
+             if (dump_file)
+               {
+                 fprintf (dump_file, "SMS failed... \n");
+                 fprintf (dump_file, "SMS sched-failed (stage-count=%d,"
+                          " loop-count=", stage_count);
+                 fprintf (dump_file, HOST_WIDEST_INT_PRINT_DEC, loop_count);
+                 fprintf (dump_file, ", trip-count=");
+                 fprintf (dump_file, HOST_WIDEST_INT_PRINT_DEC, trip_count);
+                 fprintf (dump_file, ")\n");
+               }
+             break;
            }
-       }
-      else
-       {
+
           if (!opt_sc_p)
             {
              /* Rotate the partial schedule to have the branch in row ii-1.  */
@@ -1451,6 +1676,29 @@ sms_schedule (void)
          
          set_columns_for_ps (ps);
 
+         min_cycle = PS_MIN_CYCLE (ps) - SMODULO (PS_MIN_CYCLE (ps), ps->ii);
+         if (!schedule_reg_moves (ps))
+           {
+             mii = ps->ii + 1;
+             free_partial_schedule (ps);
+             continue;
+           }
+
+         /* Moves that handle incoming values might have been added
+            to a new first stage.  Bump the stage count if so.
+
+            ??? Perhaps we could consider rotating the schedule here
+            instead?  */
+         if (PS_MIN_CYCLE (ps) < min_cycle)
+           {
+             reset_sched_times (ps, 0);
+             stage_count++;
+           }
+
+         /* The stage count should now be correct without rotation.  */
+         gcc_checking_assert (stage_count == calculate_stage_count (ps, 0));
+         PS_STAGE_COUNT (ps) = stage_count;
+
          canon_loop (loop);
 
           if (dump_file)
@@ -1483,21 +1731,23 @@ sms_schedule (void)
          permute_partial_schedule (ps, g->closing_branch->first_note);
 
           /* Mark this loop as software pipelined so the later
-            scheduling passes doesn't touch it.  */
+            scheduling passes don't touch it.  */
          if (! flag_resched_modulo_sched)
-           g->bb->flags |= BB_DISABLE_SCHEDULE;
+           mark_loop_unsched (loop);
+         
          /* The life-info is not valid any more.  */
          df_set_bb_dirty (g->bb);
 
-         generate_reg_moves (ps, true);
+         apply_reg_moves (ps);
          if (dump_file)
-           print_node_sched_params (dump_file, g->num_nodes, g);
+           print_node_sched_params (dump_file, g->num_nodes, ps);
          /* Generate prolog and epilog.  */
           generate_prolog_epilog (ps, loop, count_reg, count_init);
+         break;
        }
 
       free_partial_schedule (ps);
-      free (node_sched_params);
+      VEC_free (node_sched_params, heap, node_sched_param_vec);
       free (node_order);
       free_ddg (g);
     }
@@ -1578,7 +1828,11 @@ sms_schedule (void)
    41. endif
    42. compute epilogue & prologue
    43. finish - succeeded to schedule
-*/
+
+   ??? The algorithm restricts the scheduling window to II cycles.
+   In rare cases, it may be better to allow windows of II+1 cycles.
+   The window would then start and end on the same row, but with
+   different "must precede" and "must follow" requirements.  */
 
 /* A limit on the number of cycles that resource conflicts can span.  ??? Should
    be provided by DFA, and be dependent on the type of insn scheduled.  Currently
@@ -2584,6 +2838,7 @@ create_partial_schedule (int ii, ddg_ptr g, int history)
   partial_schedule_ptr ps = XNEW (struct partial_schedule);
   ps->rows = (ps_insn_ptr *) xcalloc (ii, sizeof (ps_insn_ptr));
   ps->rows_length = (int *) xcalloc (ii, sizeof (int));
+  ps->reg_moves = NULL;
   ps->ii = ii;
   ps->history = history;
   ps->min_cycle = INT_MAX;
@@ -2618,8 +2873,16 @@ free_ps_insns (partial_schedule_ptr ps)
 static void
 free_partial_schedule (partial_schedule_ptr ps)
 {
+  ps_reg_move_info *move;
+  unsigned int i;
+
   if (!ps)
     return;
+
+  FOR_EACH_VEC_ELT (ps_reg_move_info, ps->reg_moves, i, move)
+    sbitmap_free (move->uses);
+  VEC_free (ps_reg_move_info, heap, ps->reg_moves);
+
   free_ps_insns (ps);
   free (ps->rows);
   free (ps->rows_length);