diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 69f6627cf6844b0a633b82b766586bfde4692692..d1f36dfa6b7f244bbdee803a7fe04ea487d240ee 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,12 @@
+2006-11-14  Paolo Bonzini  <bonzini@gnu.org>
+
+	PR rtl-optimization/29798
+
+	* fwprop.c (use_killed_between): Check that DEF_INSN dominates
+	TARGET_INSN before any other check.
+	(fwprop_init): Always calculate dominators.
+	(fwprop_done): Always free them.
+
 2006-11-14  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
 
 	* fold-const.c (fold_strip_sign_ops): Handle COMPOUND_EXPR and
diff --git a/gcc/fwprop.c b/gcc/fwprop.c
index 1e4f749eb122204c4928a49cd5411df17514913e..fb601e1a4f00367b6fb7e160af35b5c768bbfb30 100644
--- a/gcc/fwprop.c
+++ b/gcc/fwprop.c
@@ -466,10 +466,22 @@ local_ref_killed_between_p (struct df_ref * ref, rtx from, rtx to)
 static bool
 use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn)
 {
-  basic_block def_bb, target_bb;
+  basic_block def_bb = BLOCK_FOR_INSN (def_insn);
+  basic_block target_bb = BLOCK_FOR_INSN (target_insn);
   int regno;
   struct df_ref * def;
 
+  /* In some obscure situations we can have a def reaching a use
+     that is _before_ the def.  In other words the def does not
+     dominate the use even though the use and def are in the same
+     basic block.  This can happen when a register may be used
+     uninitialized in a loop.  In such cases, we must assume that
+     DEF is not available.  */
+  if (def_bb == target_bb
+      ? DF_INSN_LUID (df, def_insn) >= DF_INSN_LUID (df, target_insn)
+      : !dominated_by_p (CDI_DOMINATORS, target_bb, def_bb))
+    return true;
+
   /* Check if the reg in USE has only one definition.  We already
      know that this definition reaches use, or we wouldn't be here.  */
   regno = DF_REF_REGNO (use);
@@ -477,22 +489,9 @@ use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn)
   if (def && (def->next_reg == NULL))
     return false;
 
-  /* Check if we are in the same basic block.  */
-  def_bb = BLOCK_FOR_INSN (def_insn);
-  target_bb = BLOCK_FOR_INSN (target_insn);
+  /* Check locally if we are in the same basic block.  */
   if (def_bb == target_bb)
-    {
-      /* In some obscure situations we can have a def reaching a use
-	 that is _before_ the def.  In other words the def does not
-	 dominate the use even though the use and def are in the same
-	 basic block.  This can happen when a register may be used
-	 uninitialized in a loop.  In such cases, we must assume that
-	 DEF is not available.  */
-      if (DF_INSN_LUID (df, def_insn) >= DF_INSN_LUID (df, target_insn))
-	return true;
-
-      return local_ref_killed_between_p (use, def_insn, target_insn);
-    }
+    return local_ref_killed_between_p (use, def_insn, target_insn);
 
   /* Finally, if DEF_BB is the sole predecessor of TARGET_BB.  */
   if (single_pred_p (target_bb)
@@ -890,16 +889,14 @@ static void
 fwprop_init (void)
 {
   num_changes = 0;
+  calculate_dominance_info (CDI_DOMINATORS);
 
   /* We do not always want to propagate into loops, so we have to find
      loops and be careful about them.  But we have to call flow_loops_find
      before df_analyze, because flow_loops_find may introduce new jump
      insns (sadly) if we are not working in cfglayout mode.  */
   if (flag_rerun_cse_after_loop && (flag_unroll_loops || flag_peel_loops))
-    {
-      calculate_dominance_info (CDI_DOMINATORS);
-      flow_loops_find (&loops);
-    }
+    flow_loops_find (&loops);
 
   /* Now set up the dataflow problem (we only want use-def chains) and
      put the dataflow solver to work.  */
@@ -917,10 +914,10 @@ fwprop_done (void)
   if (flag_rerun_cse_after_loop && (flag_unroll_loops || flag_peel_loops))
     {
       flow_loops_free (&loops);
-      free_dominance_info (CDI_DOMINATORS);
       loops.num = 0;
     }
 
+  free_dominance_info (CDI_DOMINATORS);
   cleanup_cfg (0);
   delete_trivially_dead_insns (get_insns (), max_reg_num ());
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 370620b3cbbb8014a43e14f40a2a39e5ef6670c6..9693e4da1aafa1a7ba333508c751bf6ce83349f0 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2006-11-14  Paolo Bonzini  <bonzini@gnu.org>
+
+	PR rtl-optimization/29798
+
+	* gcc.c-torture/execute/pr29798.c: New.
+
 2006-11-14  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
 
 	* gcc.dg/builtins-20.c: Add more cases.
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr29798.c b/gcc/testsuite/gcc.c-torture/execute/pr29798.c
new file mode 100644
index 0000000000000000000000000000000000000000..f7b90da02000fb7e2201c7b2f7e38dafd32c641b
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr29798.c
@@ -0,0 +1,26 @@
+extern void abort ();
+
+int
+main ()
+{
+  int i;
+  double oldrho;
+  double beta = 0.0;
+  double work = 1.0;
+  for (i = 1; i <= 2; i++)
+    {
+      double rho = work * work;
+      if (i != 1)
+        beta = rho / oldrho;
+      if (beta == 1.0)
+        abort ();
+
+      /* All targets even remotely likely to ever get supported
+	 use at least an even base, so there will never be any
+	 floating-point rounding. All computation in this test
+	 case is exact for even bases.  */
+      work /= 2.0;
+      oldrho = rho;
+    }
+  return 0;
+}