[backport from gcc-4.7/trunk, fixes PR51042 ICE regression from PR42108 fix <mikpe@it.uu.se>]

gcc/

2011-11-10  Richard Guenther  <rguenther@suse.de>

	PR tree-optimization/51042
	* tree-ssa-pre.c (phi_translate_1): Avoid recursing on
	self-referential expressions.  Refactor code to avoid duplication.

gcc/testsuite/
2011-11-10  Richard Guenther  <rguenther@suse.de>

	PR tree-optimization/51042
	* gcc.dg/torture/pr51042.c: New testcase.

--- gcc-4.6.2/gcc/testsuite/gcc.dg/torture/pr51042.c.~1~	1970-01-01 01:00:00.000000000 +0100
+++ gcc-4.6.2/gcc/testsuite/gcc.dg/torture/pr51042.c	2011-11-19 14:27:15.000000000 +0100
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+
+int a, b;
+
+void
+foo (int x)
+{
+  int e[2];
+  int d;
+  while (x)
+    {
+      for (d = 0; d <= 1; d = 1)
+	if (e[a])
+	  break;
+      for (b = 0; b <= 0; b = 1)
+	{
+	  e[a] = a;
+	  if (a)
+	    break;
+	}
+    }
+}
--- gcc-4.6.2/gcc/tree-ssa-pre.c.~1~	2011-07-16 13:33:28.000000000 +0200
+++ gcc-4.6.2/gcc/tree-ssa-pre.c	2011-11-19 14:27:15.000000000 +0100
@@ -1545,7 +1545,7 @@ phi_translate_1 (pre_expr expr, bitmap_s
 	tree newvuse = vuse;
 	VEC (vn_reference_op_s, heap) *newoperands = NULL;
 	bool changed = false, same_valid = true;
-	unsigned int i, j;
+	unsigned int i, j, n;
 	vn_reference_op_t operand;
 	vn_reference_t newref;
 
@@ -1554,100 +1554,83 @@ phi_translate_1 (pre_expr expr, bitmap_s
 	  {
 	    pre_expr opresult;
 	    pre_expr leader;
-	    tree oldop0 = operand->op0;
-	    tree oldop1 = operand->op1;
-	    tree oldop2 = operand->op2;
-	    tree op0 = oldop0;
-	    tree op1 = oldop1;
-	    tree op2 = oldop2;
+	    tree op[3];
 	    tree type = operand->type;
 	    vn_reference_op_s newop = *operand;
-
-	    if (op0 && TREE_CODE (op0) == SSA_NAME)
+	    op[0] = operand->op0;
+	    op[1] = operand->op1;
+	    op[2] = operand->op2;
+	    for (n = 0; n < 3; ++n)
 	      {
-		unsigned int op_val_id = VN_INFO (op0)->value_id;
-		leader = find_leader_in_sets (op_val_id, set1, set2);
-		opresult = phi_translate (leader, set1, set2, pred, phiblock);
-		if (opresult && opresult != leader)
+		unsigned int op_val_id;
+		if (!op[n])
+		  continue;
+		if (TREE_CODE (op[n]) != SSA_NAME)
 		  {
-		    tree name = get_representative_for (opresult);
-		    if (!name)
+		    /* We can't possibly insert these.  */
+		    if (n != 0
+			&& !is_gimple_min_invariant (op[n]))
 		      break;
-		    op0 = name;
+		    continue;
 		  }
-		else if (!opresult)
-		  break;
-	      }
-	    changed |= op0 != oldop0;
-
-	    if (op1 && TREE_CODE (op1) == SSA_NAME)
-	      {
-		unsigned int op_val_id = VN_INFO (op1)->value_id;
+		op_val_id = VN_INFO (op[n])->value_id;
 		leader = find_leader_in_sets (op_val_id, set1, set2);
-		opresult = phi_translate (leader, set1, set2, pred, phiblock);
-		if (opresult && opresult != leader)
+		if (!leader)
+		  break;
+		/* Make sure we do not recursively translate ourselves
+		   like for translating a[n_1] with the leader for
+		   n_1 being a[n_1].  */
+		if (get_expression_id (leader) != get_expression_id (expr))
 		  {
-		    tree name = get_representative_for (opresult);
-		    if (!name)
+		    opresult = phi_translate (leader, set1, set2,
+					      pred, phiblock);
+		    if (!opresult)
 		      break;
-		    op1 = name;
+		    if (opresult != leader)
+		      {
+			tree name = get_representative_for (opresult);
+			if (!name)
+			  break;
+			changed |= name != op[n];
+			op[n] = name;
+		      }
 		  }
-		else if (!opresult)
-		  break;
 	      }
-	    /* We can't possibly insert these.  */
-	    else if (op1 && !is_gimple_min_invariant (op1))
-	      break;
-	    changed |= op1 != oldop1;
-	    if (op2 && TREE_CODE (op2) == SSA_NAME)
+	    if (n != 3)
 	      {
-		unsigned int op_val_id = VN_INFO (op2)->value_id;
-		leader = find_leader_in_sets (op_val_id, set1, set2);
-		opresult = phi_translate (leader, set1, set2, pred, phiblock);
-		if (opresult && opresult != leader)
-		  {
-		    tree name = get_representative_for (opresult);
-		    if (!name)
-		      break;
-		    op2 = name;
-		  }
-		else if (!opresult)
-		  break;
+		if (newoperands)
+		  VEC_free (vn_reference_op_s, heap, newoperands);
+		return NULL;
 	      }
-	    /* We can't possibly insert these.  */
-	    else if (op2 && !is_gimple_min_invariant (op2))
-	      break;
-	    changed |= op2 != oldop2;
-
 	    if (!newoperands)
 	      newoperands = VEC_copy (vn_reference_op_s, heap, operands);
 	    /* We may have changed from an SSA_NAME to a constant */
-	    if (newop.opcode == SSA_NAME && TREE_CODE (op0) != SSA_NAME)
-	      newop.opcode = TREE_CODE (op0);
+	    if (newop.opcode == SSA_NAME && TREE_CODE (op[0]) != SSA_NAME)
+	      newop.opcode = TREE_CODE (op[0]);
 	    newop.type = type;
-	    newop.op0 = op0;
-	    newop.op1 = op1;
-	    newop.op2 = op2;
+	    newop.op0 = op[0];
+	    newop.op1 = op[1];
+	    newop.op2 = op[2];
 	    /* If it transforms a non-constant ARRAY_REF into a constant
 	       one, adjust the constant offset.  */
 	    if (newop.opcode == ARRAY_REF
 		&& newop.off == -1
-		&& TREE_CODE (op0) == INTEGER_CST
-		&& TREE_CODE (op1) == INTEGER_CST
-		&& TREE_CODE (op2) == INTEGER_CST)
+		&& TREE_CODE (op[0]) == INTEGER_CST
+		&& TREE_CODE (op[1]) == INTEGER_CST
+		&& TREE_CODE (op[2]) == INTEGER_CST)
 	      {
-		double_int off = tree_to_double_int (op0);
+		double_int off = tree_to_double_int (op[0]);
 		off = double_int_add (off,
 				      double_int_neg
-				        (tree_to_double_int (op1)));
-		off = double_int_mul (off, tree_to_double_int (op2));
+				        (tree_to_double_int (op[1])));
+		off = double_int_mul (off, tree_to_double_int (op[2]));
 		if (double_int_fits_in_shwi_p (off))
 		  newop.off = off.low;
 	      }
 	    VEC_replace (vn_reference_op_s, newoperands, j, &newop);
 	    /* If it transforms from an SSA_NAME to an address, fold with
 	       a preceding indirect reference.  */
-	    if (j > 0 && op0 && TREE_CODE (op0) == ADDR_EXPR
+	    if (j > 0 && op[0] && TREE_CODE (op[0]) == ADDR_EXPR
 		&& VEC_index (vn_reference_op_s,
 			      newoperands, j - 1)->opcode == MEM_REF)
 	      vn_reference_fold_indirect (&newoperands, &j);
