diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 658a3c63aa8d36f91bb2cc31a455ea951c7c3403..569f646f67538249ba99c08c97e77f6a60281153 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,15 @@
+2009-05-19  Eric Botcazou  <ebotcazou@adacore.com>
+
+	* tree-scalar-evolution.c (follow_ssa_edge_expr) <NOP_EXPR>: Turn
+	into CASE_CONVERT.
+	<PLUS_EXPR>: Strip useless type conversions instead of type nops.
+	Propagate the type of the first operand.
+	<ASSERT_EXPR>: Simplify.
+	(follow_ssa_edge_in_rhs): Use gimple_expr_type to get the type.
+	Rewrite using the RHS code as discriminant.
+	<NOP_EXPR>: Turn into CASE_CONVERT.
+	<PLUS_EXPR>: Propagate the type of the first operand.
+
 2009-05-19  Steve Ellcey  <sje@cup.hp.com>
 
 	* config/ia64/ia64-protos.h (ia64_dconst_0_5): New.
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 44019be7e3fb1e6c3de47572fab0166167e4a10a..3457351e5f5496730d485d04e94b84968aa12a80 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2009-05-19  Eric Botcazou  <ebotcazou@adacore.com>
+
+	* gnat.dg/loop_optimization6.ad[sb]: New test.
+
 2009-05-19  Richard Guenther  <rguenther@suse.de>
 
 	* gcc.c-torture/compile/20090519-1.c: New testcase.
diff --git a/gcc/testsuite/gnat.dg/loop_optimization6.adb b/gcc/testsuite/gnat.dg/loop_optimization6.adb
new file mode 100644
index 0000000000000000000000000000000000000000..42f1717f1adeae15aef75764084b0e6b83095b64
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/loop_optimization6.adb
@@ -0,0 +1,25 @@
+-- { dg-do compile }
+-- { dg-options "-O -gnatp -fdump-tree-optimized" }
+
+package body Loop_Optimization6 is
+  procedure Foo is
+  begin
+    for I in 1 .. 1_000_000 loop
+      A := A + 1;
+    end loop;
+  end Foo;
+
+  procedure Bar is
+  begin
+    for J in 1 .. 1_000 loop
+      Foo;
+    end loop;
+  end Bar;
+
+  procedure Main is
+  begin
+    Bar;
+  end;
+end Loop_Optimization6;
+
+-- { dg-final { scan-tree-dump-not "goto" "optimized"} }
diff --git a/gcc/testsuite/gnat.dg/loop_optimization6.ads b/gcc/testsuite/gnat.dg/loop_optimization6.ads
new file mode 100644
index 0000000000000000000000000000000000000000..9b8a2670322b460e254de250b427550c4df3ba9c
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/loop_optimization6.ads
@@ -0,0 +1,4 @@
+package Loop_Optimization6 is
+  A : Integer := 0;
+  procedure Main;
+end Loop_Optimization6;
diff --git a/gcc/tree-scalar-evolution.c b/gcc/tree-scalar-evolution.c
index b3990c6bfe15fb80ef39f0e7671dbaafe0f27939..bac6e594d8f02ba32d961906d48c6bc90a03015c 100644
--- a/gcc/tree-scalar-evolution.c
+++ b/gcc/tree-scalar-evolution.c
@@ -1141,11 +1141,10 @@ static t_bool
 follow_ssa_edge_expr (struct loop *loop, gimple at_stmt, tree expr, 
 		      gimple halting_phi, tree *evolution_of_loop, int limit)
 {
-  t_bool res = t_false;
-  tree rhs0, rhs1;
-  tree type = TREE_TYPE (expr);
-  enum tree_code code;
-  
+  enum tree_code code = TREE_CODE (expr);
+  tree type = TREE_TYPE (expr), rhs0, rhs1;
+  t_bool res;
+
   /* The EXPR is one of the following cases:
      - an SSA_NAME, 
      - an INTEGER_CST,
@@ -1154,10 +1153,10 @@ follow_ssa_edge_expr (struct loop *loop, gimple at_stmt, tree expr,
      - a MINUS_EXPR,
      - an ASSERT_EXPR,
      - other cases are not yet handled.  */
-  code = TREE_CODE (expr);
+
   switch (code)
     {
-    case NOP_EXPR:
+    CASE_CONVERT:
       /* This assignment is under the form "a_1 = (cast) rhs.  */
       res = follow_ssa_edge_expr (loop, at_stmt, TREE_OPERAND (expr, 0),
 				  halting_phi, evolution_of_loop, limit);
@@ -1168,43 +1167,42 @@ follow_ssa_edge_expr (struct loop *loop, gimple at_stmt, tree expr,
       /* This assignment is under the form "a_1 = 7".  */
       res = t_false;
       break;
-      
+
     case SSA_NAME:
       /* This assignment is under the form: "a_1 = b_2".  */
       res = follow_ssa_edge 
 	(loop, SSA_NAME_DEF_STMT (expr), halting_phi, evolution_of_loop, limit);
       break;
-      
+
     case POINTER_PLUS_EXPR:
     case PLUS_EXPR:
     case MINUS_EXPR:
       /* This case is under the form "rhs0 +- rhs1".  */
       rhs0 = TREE_OPERAND (expr, 0);
       rhs1 = TREE_OPERAND (expr, 1);
-      STRIP_TYPE_NOPS (rhs0);
-      STRIP_TYPE_NOPS (rhs1);
-      return follow_ssa_edge_binary (loop, at_stmt, type, rhs0, code, rhs1,
-				     halting_phi, evolution_of_loop, limit);
+      type = TREE_TYPE (rhs0);
+      STRIP_USELESS_TYPE_CONVERSION (rhs0);
+      STRIP_USELESS_TYPE_CONVERSION (rhs1);
+      res = follow_ssa_edge_binary (loop, at_stmt, type, rhs0, code, rhs1,
+				    halting_phi, evolution_of_loop, limit);
+      break;
 
     case ASSERT_EXPR:
-      {
-	/* This assignment is of the form: "a_1 = ASSERT_EXPR <a_2, ...>"
-	   It must be handled as a copy assignment of the form a_1 = a_2.  */
-	tree op0 = ASSERT_EXPR_VAR (expr);
-	if (TREE_CODE (op0) == SSA_NAME)
-	  res = follow_ssa_edge (loop, SSA_NAME_DEF_STMT (op0),
-				 halting_phi, evolution_of_loop, limit);
-	else
-	  res = t_false;
-	break;
-      }
-
+      /* This assignment is of the form: "a_1 = ASSERT_EXPR <a_2, ...>"
+	 It must be handled as a copy assignment of the form a_1 = a_2.  */
+      rhs0 = ASSERT_EXPR_VAR (expr);
+      if (TREE_CODE (rhs0) == SSA_NAME)
+	res = follow_ssa_edge (loop, SSA_NAME_DEF_STMT (rhs0),
+			       halting_phi, evolution_of_loop, limit);
+      else
+	res = t_false;
+      break;
 
     default:
       res = t_false;
       break;
     }
-  
+
   return res;
 }
 
@@ -1215,34 +1213,39 @@ static t_bool
 follow_ssa_edge_in_rhs (struct loop *loop, gimple stmt,
 			gimple halting_phi, tree *evolution_of_loop, int limit)
 {
-  tree type = TREE_TYPE (gimple_assign_lhs (stmt));
   enum tree_code code = gimple_assign_rhs_code (stmt);
+  tree type = gimple_expr_type (stmt), rhs1, rhs2;
+  t_bool res;
 
-  switch (get_gimple_rhs_class (code))
+  switch (code)
     {
-    case GIMPLE_BINARY_RHS:
-      return follow_ssa_edge_binary (loop, stmt, type,
-				     gimple_assign_rhs1 (stmt), code,
-				     gimple_assign_rhs2 (stmt),
-				     halting_phi, evolution_of_loop, limit);
-    case GIMPLE_SINGLE_RHS:
-      return follow_ssa_edge_expr (loop, stmt, gimple_assign_rhs1 (stmt),
-				   halting_phi, evolution_of_loop, limit);
-    case GIMPLE_UNARY_RHS:
-      if (code == NOP_EXPR)
-	{
-	  /* This assignment is under the form "a_1 = (cast) rhs.  */
-	  t_bool res
-	    = follow_ssa_edge_expr (loop, stmt, gimple_assign_rhs1 (stmt),
+    CASE_CONVERT:
+      /* This assignment is under the form "a_1 = (cast) rhs.  */
+      res = follow_ssa_edge_expr (loop, stmt, gimple_assign_rhs1 (stmt),
+				  halting_phi, evolution_of_loop, limit);
+      *evolution_of_loop = chrec_convert (type, *evolution_of_loop, stmt);
+      break;
+
+    case POINTER_PLUS_EXPR:
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      rhs1 = gimple_assign_rhs1 (stmt);
+      rhs2 = gimple_assign_rhs2 (stmt);
+      type = TREE_TYPE (rhs1);
+      res = follow_ssa_edge_binary (loop, stmt, type, rhs1, code, rhs2,
 				    halting_phi, evolution_of_loop, limit);
-	  *evolution_of_loop = chrec_convert (type, *evolution_of_loop, stmt);
-	  return res;
-	}
-      /* FALLTHRU */
+      break;
 
     default:
-      return t_false;
+      if (get_gimple_rhs_class (code) == GIMPLE_SINGLE_RHS)
+	res = follow_ssa_edge_expr (loop, stmt, gimple_assign_rhs1 (stmt),
+				    halting_phi, evolution_of_loop, limit);
+      else
+	res = t_false;
+      break;
     }
+
+  return res;
 }
 
 /* Checks whether the I-th argument of a PHI comes from a backedge.  */