Index: cse.c
===================================================================
--- cse.c	(revision 189811)
+++ cse.c	(working copy)
@@ -7548,6 +7548,9 @@ gate_handle_cse_after_global_opts (void)
 }
 
 /* Run second CSE pass after loop optimizations.  */
+
+static unsigned int cse_uncprop (void);
+
 static unsigned int
 rest_of_handle_cse_after_global_opts (void)
 {
@@ -7577,6 +7580,7 @@ rest_of_handle_cse_after_global_opts (vo
     cleanup_cfg (0);
 
   flag_cse_follow_jumps = save_cfj;
+  (void) cse_uncprop ();
   return 0;
 }
 
@@ -7600,3 +7604,235 @@ struct rtl_opt_pass pass_cse_after_globa
   TODO_verify_flow                      /* todo_flags_finish */
  }
 };
+
+#include "alloc-pool.h"
+#include "splay-tree.h"
+#include "vec.h"
+#include "domwalk.h"
+
+/* Find constants that are used more than once, and try to "un-propagate"
+   them if this allows an instruction to be eliminated.  This relies on
+   IRA doing a good job at re-materializing constants if register pressure
+   gets out of hand.  In any case, we attach a REG_EQUAl, to expose the
+   known register value to the rest of the compiler.
+   
+   This pass is almost embarassingly simple: It works only on single-set
+   insns, and only on pseudo-registers that are set once. No attempt is
+   made to make this pass truly global; instead a simple dominator tree
+   traversal is used.  Any opportunities *within* one basic block are
+   ignored, because we trust that CSE has already done the right thing
+   there (using its smart cost calculations and whatnot).  Only CONST_INT
+   is handled, at least for the moment.
+
+   And with all these restrictions, the pass is still enough to fix a
+   wide range of issues, as shown in CSiBE results.  The motivation for
+   the pass is PR44025 but many other cases are handled as well.
+
+   The pass is fully DF-based.  If that is not enough for you, then this
+   pass is probably not the place for whatever you're trying to do.  */
+
+typedef struct const_reg_d
+{
+  /* The register that is known to hold a constant.  */
+  rtx reg;
+  /* The known constant value.  */
+  rtx constval;
+  /* The basic block where reg is set.  */
+  int bb_index;
+} *const_reg_t;
+
+/* ??? This could probably be done more efficiently with an obstack.
+   But I don't feel like using obstacks today, so there you have it...  */
+static alloc_pool const_reg_pool;
+
+/* A VEC of all recorded constant registers.  Only the first occurring reg
+   in the current branch of the dominator tree is recorded.  We keep track
+   of that with a splay tree.  */
+DEF_VEC_P (const_reg_t);
+DEF_VEC_ALLOC_P (const_reg_t, heap);
+static VEC(const_reg_t,heap) *const_regs;
+
+/* A splay tree to create a map of INTVAL to a CONST_REG_T in CONST_REGS,
+   if a constant register for INTVAL was already recorded.  */
+static splay_tree const_to_const_reg_map;
+
+/* We are about to enter BB.  Record interesting regs.  */
+static void
+cse_uncprop_enter_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
+			 basic_block bb)
+{
+  rtx insn;
+
+  /* Push a sentinel NULL onto the stack.  */
+  VEC_safe_push (const_reg_t, heap, const_regs, (const_reg_t) 0);
+
+  /* Walk all insns.  */
+  FOR_BB_INSNS (bb, insn)
+    {
+      rtx set;
+      const_reg_t c;
+      unsigned HOST_WIDE_INT val;
+      splay_tree_node node;
+
+      /* We only deal with single_set insns.  */
+      if (! INSN_P (insn)
+	  || ! (set = single_set (insn)))
+	continue;
+
+      /* SET_DEST must be a pseudo-register that is set only once (that is,
+         in this insn) and SET_SRC must be a CONST_INT.
+         ??? Perhaps handle extends of CONST_INT, too.  */
+      if (! REG_P (SET_DEST (set)) || HARD_REGISTER_P (SET_DEST (set))
+	  || DF_REG_DEF_COUNT (REGNO (SET_DEST (set))) != 1
+	  || ! CONST_INT_P (SET_SRC (set)))
+        continue;
+
+      /* Alright, can't think of any reasons why SET is not
+	 interesting.  Process it.  */
+      val = INTVAL (SET_SRC (set));
+      node = splay_tree_lookup (const_to_const_reg_map, (splay_tree_key) val);
+      if (! node)
+        {
+	  /* This constant hasn't been recorded yet.
+	     What a golden opportunity to add it now!  */
+	  c = (const_reg_t) pool_alloc (const_reg_pool);
+	  c->reg = SET_DEST (set);
+	  c->constval = SET_SRC (set);
+	  c->bb_index = bb->index;
+	  splay_tree_insert (const_to_const_reg_map,
+			     (splay_tree_key) val,
+			     (splay_tree_value) c);
+	  if (dump_file)
+	    fprintf (dump_file,
+		     ";; recorded reg %d with val %ld in bb %d (at uid %d)\n",
+		     REGNO (c->reg), (long) INTVAL (c->constval), c->bb_index,
+		     INSN_UID (insn));
+	  continue;
+	}
+
+      /* We have recorded this constant before.  */
+      c = (const_reg_t) node->value;
+
+      /* Do nothing if the recorded constant comes from this basic block.
+	 CSE is much smarter about cost issues than we are.  */
+      if (bb->index == c->bb_index)
+	{
+	  if (dump_file)
+	    fprintf (dump_file,
+		     ";; not unpropagating local reg %d with val %ld in bb %d "
+		     " into reg %d in bb %d (at uid %d)\n",
+		     REGNO (c->reg), (long) INTVAL (c->constval), c->bb_index,
+		     REGNO (SET_DEST (set)), bb->index, INSN_UID (insn));
+	  continue;
+	}
+
+      /* This is where it gets messy.  We need to decide when and how to do
+	 the unpropagation.  */
+
+      /* We do nothing if the modes of the registers are different.  TODO.  */
+      if (GET_MODE (c->reg) != GET_MODE (SET_DEST (set)))
+	{
+	  if (dump_file)
+	    fprintf (dump_file,
+		     ";; missed opportunity, clash of reg %d "
+		     "with val %ld in bb %d with reg %d in bb %d (at uid %d)\n",
+		     REGNO (c->reg), (long) INTVAL (c->constval), c->bb_index,
+		     REGNO (SET_DEST (set)), bb->index, INSN_UID (insn));
+	  continue;
+	}
+
+      /* Try to unpropagate.  */
+      if (validate_change (insn, &SET_SRC (set), c->reg, 0))
+	{
+	  /* Yay, success!  */
+	  if (dump_file)
+	    fprintf (dump_file,
+		     ";; unpropagated reg %d with val %ld in bb %d "
+		     " into reg %d in bb %d (at uid %d)\n",
+		     REGNO (c->reg), (long) INTVAL (c->constval), c->bb_index,
+		     REGNO (SET_DEST (set)), bb->index, INSN_UID (insn));
+	  /* Remember for later.  */
+	  set_unique_reg_note (insn, REG_EQUAL, c->constval);
+	}
+      else
+	{
+	  /* Ayee, failure!  */
+	  if (dump_file)
+	    fprintf (dump_file,
+		     ";; missed opportunity, cannot unpropagate reg %d "
+		     "with val %ld in bb %d into reg %d in bb %d (at uid %d)\n",
+		     REGNO (c->reg), (long) INTVAL (c->constval), c->bb_index,
+		     REGNO (SET_DEST (set)), bb->index, INSN_UID (insn));
+	}
+    }
+}
+
+/* We have finished processing the dominator children of BB, perform
+   any finalization actions in preparation for leaving this node in
+   the dominator tree.  */
+
+static void
+cse_uncprop_leave_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
+			 basic_block bb)
+{
+  /* Pop everything for this basic block off the stack, using the NULL
+     sentinel to stop in the right place.  */
+  while (true)
+    {
+      unsigned HOST_WIDE_INT val;
+      const_reg_t c = VEC_pop (const_reg_t, const_regs);
+      if (! c)
+	break;
+      gcc_assert (c->bb_index == bb->index);
+      val = INTVAL (c->constval);
+      splay_tree_remove (const_to_const_reg_map, (splay_tree_key) val);
+      pool_free (const_reg_pool, c);
+    }
+}
+
+/* Main driver for CSE un-cprop.  */
+
+static unsigned int
+cse_uncprop (void)
+{
+  struct dom_walk_data walk_data;
+
+  /* Create our global data structures.  */
+  const_reg_pool = create_alloc_pool ("const_reg_t",
+				      sizeof (struct const_reg_d),
+				      1024);
+  const_regs = VEC_alloc (const_reg_t, heap, 1024);
+  const_to_const_reg_map = splay_tree_new (splay_tree_compare_ints, 0, 0);
+
+  /* We're going to do a dominator walk, so ensure that we have
+     dominance information.  */
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  /* Setup callbacks for the generic dominator tree walker.  */
+  walk_data.dom_direction = CDI_DOMINATORS;
+  walk_data.initialize_block_local_data = NULL;
+  walk_data.before_dom_children = cse_uncprop_enter_block;
+  walk_data.after_dom_children = cse_uncprop_leave_block;
+  walk_data.global_data = NULL;
+  walk_data.block_local_data_size = 0;
+
+  /* Now initialize the dominator walker.  */
+  init_walk_dominator_tree (&walk_data);
+
+  /* Recursively walk the dominator tree undoing unprofitable
+     constant/copy propagations.  */
+  walk_dominator_tree (&walk_data, ENTRY_BLOCK_PTR);
+
+  /* Finalize and clean up.  */
+  fini_walk_dominator_tree (&walk_data);
+
+  /* EQUIV_STACK should already be empty at this point, so we just
+     need to empty elements out of the hash table, free EQUIV_STACK,
+     and cleanup the AUX field on the edges.  */
+  gcc_assert (VEC_empty (const_reg_t, const_regs));
+  VEC_free (const_reg_t, heap, const_regs);
+  free_alloc_pool (const_reg_pool);
+  splay_tree_delete (const_to_const_reg_map);
+  return 0;
+}
+
