diff --git a/gcc/sel-sched-ir.c b/gcc/sel-sched-ir.c
index 1bbc3ed..1a73308 100644
--- a/gcc/sel-sched-ir.c
+++ b/gcc/sel-sched-ir.c
@@ -149,7 +149,6 @@ 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, heap) **);
 
 static void move_bb_info (basic_block, basic_block);
 static void remove_empty_bb (basic_block, bool);
@@ -1503,13 +1502,13 @@ find_in_history_vect (VEC(expr_history_def, heap) *vect, rtx insn,
 
 /* 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.  */
+   UID/NEW_EXPR_VINSN pair.  TYPE, OLD_EXPR_VINSN, OLD_SPEC_DS and
+   NEW_SPEC_DS save the history of a transformation.  */
 void
 insert_in_history_vect (VEC (expr_history_def, heap) **pvect,
                         unsigned uid, enum local_trans_type type,
                         vinsn_t old_expr_vinsn, vinsn_t new_expr_vinsn,
-                        ds_t spec_ds)
+                        ds_t old_spec_ds, ds_t new_spec_ds)
 {
   VEC(expr_history_def, heap) *vect = *pvect;
   expr_history_def temp;
@@ -1525,15 +1524,16 @@ insert_in_history_vect (VEC (expr_history_def, heap) **pvect,
       /* 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);
+      if (phist->old_spec_ds != old_spec_ds)
+        phist->old_spec_ds = ds_max_merge (phist->old_spec_ds, old_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.old_spec_ds = old_spec_ds;
+  temp.new_spec_ds = new_spec_ds;
   temp.type = type;
 
   vinsn_attach (old_expr_vinsn);
@@ -1543,7 +1543,7 @@ insert_in_history_vect (VEC (expr_history_def, heap) **pvect,
 }
 
 /* Free history vector PVECT.  */
-static void
+void
 free_history_vect (VEC (expr_history_def, heap) **pvect)
 {
   unsigned i;
@@ -1576,7 +1576,7 @@ merge_history_vect (VEC (expr_history_def, heap) **pvect,
   for (i = 0; VEC_iterate (expr_history_def, from, i, phist); i++)
     insert_in_history_vect (pvect, phist->uid, phist->type,
                             phist->old_expr_vinsn, phist->new_expr_vinsn,
-                            phist->spec_ds);
+                            phist->old_spec_ds, phist->new_spec_ds);
 }
 
 /* Compare two vinsns as rhses if possible and as vinsns otherwise.  */
@@ -1632,10 +1632,8 @@ init_expr (expr_t expr, vinsn_t vi, int spec, int use, int priority,
   EXPR_SPEC_DONE_DS (expr) = spec_done_ds;
   EXPR_SPEC_TO_CHECK_DS (expr) = spec_to_check_ds;
 
-  if (history)
-    EXPR_HISTORY_OF_CHANGES (expr) = history;
-  else
-    EXPR_HISTORY_OF_CHANGES (expr) = NULL;
+  gcc_assert (!history);
+  EXPR_HISTORY_OF_CHANGES (expr) = history;
 
   EXPR_TARGET_AVAILABLE (expr) = target_available;
   EXPR_WAS_SUBSTITUTED (expr) = was_substituted;
@@ -1644,35 +1642,39 @@ init_expr (expr_t expr, vinsn_t vi, int spec, int use, int priority,
   EXPR_CANT_MOVE (expr) = cant_move;
 }
 
-/* Make a copy of the expr FROM into the expr TO.  */
+/* Copy history vector of the expr FROM to the expr TO.  */
 void
-copy_expr (expr_t to, expr_t from)
+copy_history_of_changes (expr_t to, expr_t from)
 {
-  VEC(expr_history_def, heap) *temp = NULL;
-
   if (EXPR_HISTORY_OF_CHANGES (from))
     {
       unsigned i;
       expr_history_def *phist;
 
-      temp = VEC_copy (expr_history_def, heap, EXPR_HISTORY_OF_CHANGES (from));
-      for (i = 0;
-           VEC_iterate (expr_history_def, temp, i, phist);
-           i++)
+      EXPR_HISTORY_OF_CHANGES (to)
+        = VEC_copy (expr_history_def, heap, EXPR_HISTORY_OF_CHANGES (from));
+      FOR_EACH_VEC_ELT (expr_history_def, EXPR_HISTORY_OF_CHANGES (to), i,
+			phist)
         {
           vinsn_attach (phist->old_expr_vinsn);
           vinsn_attach (phist->new_expr_vinsn);
         }
     }
+}
 
+/* Make a copy of the expr FROM into the expr TO.  */
+void
+copy_expr (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), EXPR_ORIG_BB_INDEX (from),
 	     EXPR_SPEC_DONE_DS (from), EXPR_SPEC_TO_CHECK_DS (from),
-	     EXPR_ORIG_SCHED_CYCLE (from), temp,
+	     EXPR_ORIG_SCHED_CYCLE (from), NULL,
              EXPR_TARGET_AVAILABLE (from), EXPR_WAS_SUBSTITUTED (from),
              EXPR_WAS_RENAMED (from), EXPR_NEEDS_SPEC_CHECK_P (from),
              EXPR_CANT_MOVE (from));
+  copy_history_of_changes (to, from);
 }
 
 /* Same, but the final expr will not ever be in av sets, so don't copy
@@ -1798,7 +1800,7 @@ update_speculative_bits (expr_t to, expr_t from, insn_t split_point)
               insert_in_history_vect (&EXPR_HISTORY_OF_CHANGES (to),
                                       INSN_UID (split_point), TRANS_SPECULATION,
                                       EXPR_VINSN (from), EXPR_VINSN (to),
-                                      record_ds);
+                                      record_ds, EXPR_SPEC_DONE_DS (to));
             }
         }
     }
diff --git a/gcc/sel-sched-ir.h b/gcc/sel-sched-ir.h
index c8f8be6..bcb47c6 100644
--- a/gcc/sel-sched-ir.h
+++ b/gcc/sel-sched-ir.h
@@ -89,8 +89,11 @@ struct expr_history_def_1
   /* How the expression looks after the transformation.  */
   vinsn_t new_expr_vinsn;
 
-  /* And its speculative status.  */
-  ds_t spec_ds;
+  /* Its previous speculative status.  */
+  ds_t old_spec_ds;
+
+  /* Its new speculative status.  */
+  ds_t new_spec_ds;
 
   /* Type of the transformation.  */
   enum local_trans_type type;
@@ -1537,6 +1540,8 @@ extern bool vinsn_equal_p (vinsn_t, vinsn_t);
 
 /* EXPR functions.  */
 extern void copy_expr (expr_t, expr_t);
+extern void copy_history_of_changes (expr_t, expr_t);
+extern void free_history_vect (VEC (expr_history_def, heap) **);
 extern void copy_expr_onside (expr_t, expr_t);
 extern void merge_expr_data (expr_t, expr_t, insn_t);
 extern void merge_expr (expr_t, expr_t, insn_t);
@@ -1547,7 +1552,7 @@ extern int find_in_history_vect (VEC(expr_history_def, heap) *,
                                  rtx, vinsn_t, bool);
 extern void insert_in_history_vect (VEC(expr_history_def, heap) **,
                                     unsigned, enum local_trans_type,
-                                    vinsn_t, vinsn_t, ds_t);
+                                    vinsn_t, vinsn_t, ds_t, ds_t);
 extern void mark_unavailable_targets (av_set_t, av_set_t, regset);
 extern int speculate_expr (expr_t, ds_t);
 
diff --git a/gcc/sel-sched.c b/gcc/sel-sched.c
index f11faca..91fb0fe 100644
--- a/gcc/sel-sched.c
+++ b/gcc/sel-sched.c
@@ -1965,7 +1965,7 @@ undo_transformations (av_set_t *av_ptr, rtx insn)
                    and in the case of merging different probabilities of the
                    same speculative status along different paths we do not
                    record this in the history vector.  */
-                old_ds = phist->spec_ds;
+                old_ds = phist->old_spec_ds;
                 new_ds = EXPR_SPEC_DONE_DS (expr);
 
                 old_ds &= SPECULATIVE;
@@ -2409,7 +2409,7 @@ try_transformation_cache (expr_t expr, insn_t insn,
       insert_in_history_vect (&EXPR_HISTORY_OF_CHANGES (expr),
                               INSN_UID (insn), pti->type,
                               pti->vinsn_old, pti->vinsn_new,
-                              EXPR_SPEC_DONE_DS (expr));
+                              EXPR_SPEC_DONE_DS (expr), pti->ds);
 
       if (INSN_IN_STREAM_P (VINSN_INSN_RTX (pti->vinsn_new)))
         pti->vinsn_new = vinsn_copy (pti->vinsn_new, true);
@@ -2564,7 +2564,7 @@ moveup_expr_cached (expr_t expr, insn_t insn, bool inside_insn_group)
           insert_in_history_vect (&EXPR_HISTORY_OF_CHANGES (expr),
                                   INSN_UID (insn), trans_type,
                                   expr_old_vinsn, EXPR_VINSN (expr),
-                                  expr_old_spec_ds);
+                                  expr_old_spec_ds, EXPR_SPEC_DONE_DS (expr));
           update_transformation_cache (expr, insn, inside_insn_group,
                                        trans_type, expr_old_vinsn);
           if (sched_verbose >= 6)
@@ -5234,6 +5234,7 @@ move_exprs_to_boundary (bnd_t bnd, expr_t expr_vliw,
 
   b = move_op (BND_TO (bnd), expr_seq, expr_vliw,
                get_dest_from_orig_ops (expr_seq), c_expr, &should_move);
+  free_history_vect (&EXPR_HISTORY_OF_CHANGES (c_expr));
 
   /* We should be able to find the expression we've chosen for
      scheduling.  */
@@ -5999,6 +6000,7 @@ move_op_orig_expr_found (insn_t insn, expr_t expr,
   moveop_static_params_p params = (moveop_static_params_p) static_params;
 
   copy_expr_onside (params->c_expr, INSN_EXPR (insn));
+  copy_history_of_changes (params->c_expr, expr);
   track_scheduled_insns_and_blocks (insn);
   insn_emitted = handle_emitting_transformations (insn, expr, params);
   only_disconnect = (params->uid == INSN_UID (insn)
@@ -6149,19 +6151,78 @@ fur_at_first_insn (insn_t insn,
 	      || AV_LEVEL (insn) == -1);
 }
 
+/* Redo the transformation that moveup_expr performed on EXPR for INSN, as
+   recorded in EXPR's history vector.  */
+static void
+redo_transformations (expr_t expr, insn_t insn)
+{
+  unsigned i, insn_uid = INSN_UID (insn);
+  rtx orig_expr_lhs = EXPR_LHS (expr);
+  expr_history_def *hist;
+  VEC(expr_history_def, heap) *vect = EXPR_HISTORY_OF_CHANGES (expr);
+
+  if (sched_verbose >= 6)
+    {
+      sel_print ("Moving ");
+      dump_expr (expr);
+      sel_print (" through %d: ", INSN_UID (insn));
+    }
+
+  FOR_EACH_VEC_ELT (expr_history_def, vect, i, hist)
+    if (hist->uid == insn_uid)
+      {
+	if (vinsn_equal_p (EXPR_VINSN (expr), hist->old_expr_vinsn))
+	  {
+	    switch (hist->type)
+	      {
+	      case TRANS_SPECULATION:
+		EXPR_SPEC_DONE_DS (expr) = hist->new_spec_ds;
+		/* Fallthru.  */
+	      case TRANS_SUBSTITUTION:
+		change_vinsn_in_expr (expr, hist->new_expr_vinsn);
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+
+	    if (EXPR_LHS (expr) != orig_expr_lhs && expr_dest_reg (expr))
+	      replace_dest_with_reg_in_expr (expr, orig_expr_lhs);
+
+	    if (sched_verbose >= 6)
+	      {
+		sel_print ("changed (redoing): ");
+		dump_expr (expr);
+		sel_print ("\n");
+	      }
+	    return;
+	  }
+	else
+	  {
+	    /* We cannot assert the following; consider:
+	       r1 = r3;
+	       r2 = r3;
+	       r4 = r1;
+	       r5 = r2;
+	       We choose to schedule r4 = r3, find it at r4 = r1, and
+	       attempt to redo transformation on r2 = r3, which was recorded,
+	       because r5 = r2 was moved through it and unified afterwards.  */
+	    /* gcc_assert (vinsn_equal_p (EXPR_VINSN (expr), hist->new_expr_vinsn)); */
+	    break;
+	  }
+      }
+  if (sched_verbose >= 6)
+    sel_print ("unchanged (redoing)\n");
+}
+
 /* Called on the backward stage of recursion to call moveup_expr for insn
    and sparams->c_expr.  */
 static void
 move_op_ascend (insn_t insn, void *static_params)
 {
-  enum MOVEUP_EXPR_CODE res;
   moveop_static_params_p sparams = (moveop_static_params_p) static_params;
 
   if (! INSN_NOP_P (insn))
-    {
-      res = moveup_expr_cached (sparams->c_expr, insn, false);
-      gcc_assert (res != MOVEUP_EXPR_NULL);
-    }
+    redo_transformations (sparams->c_expr, insn);
 
   /* Update liveness for this insn as it was invalidated.  */
   update_liveness_on_insn (insn);
