diff gcc/tree-ssa-ccp.c @ 55:77e2b8dfacca gcc-4.4.5

update it from 4.4.3 to 4.5.0
author ryoma <e075725@ie.u-ryukyu.ac.jp>
date Fri, 12 Feb 2010 23:39:51 +0900
parents 3bfb6c00c1e0
children b7f97abdc517
line wrap: on
line diff
--- a/gcc/tree-ssa-ccp.c	Sun Feb 07 18:28:00 2010 +0900
+++ b/gcc/tree-ssa-ccp.c	Fri Feb 12 23:39:51 2010 +0900
@@ -5,17 +5,17 @@
    Adapted to GIMPLE trees by Diego Novillo <dnovillo@redhat.com>
 
 This file is part of GCC.
-   
+
 GCC is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
 Free Software Foundation; either version 3, or (at your option) any
 later version.
-   
+
 GCC is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
-   
+
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
@@ -63,7 +63,7 @@
       mark the outgoing edges as executable or not executable
       depending on the predicate's value.  This is then used when
       visiting PHI nodes to know when a PHI argument can be ignored.
-      
+
 
    2- In ccp_visit_phi_node, if all the PHI arguments evaluate to the
       same constant C, then the LHS of the PHI is set to C.  This
@@ -208,6 +208,7 @@
 #include "langhooks.h"
 #include "target.h"
 #include "toplev.h"
+#include "dbgcnt.h"
 
 
 /* Possible lattice values.  */
@@ -228,6 +229,7 @@
 static prop_value_t *const_val;
 
 static void canonicalize_float_value (prop_value_t *);
+static bool ccp_fold_stmt (gimple_stmt_iterator *);
 
 /* Dump constant propagation value VAL to file OUTF prefixed by PREFIX.  */
 
@@ -275,15 +277,27 @@
 get_symbol_constant_value (tree sym)
 {
   if (TREE_STATIC (sym)
-      && TREE_READONLY (sym)
-      && !MTAG_P (sym))
+      && (TREE_READONLY (sym)
+	  || TREE_CODE (sym) == CONST_DECL))
     {
       tree val = DECL_INITIAL (sym);
       if (val)
 	{
 	  STRIP_USELESS_TYPE_CONVERSION (val);
 	  if (is_gimple_min_invariant (val))
-	    return val;
+	    {
+	      if (TREE_CODE (val) == ADDR_EXPR)
+		{
+		  tree base = get_base_address (TREE_OPERAND (val, 0));
+		  if (base && TREE_CODE (base) == VAR_DECL)
+		    {
+		      TREE_ADDRESSABLE (base) = 1;
+		      if (gimple_referenced_vars (cfun))
+			add_referenced_var (base);
+		    }
+		}
+	      return val;
+	    }
 	}
       /* Variables declared 'const' without an initializer
 	 have zero as the initializer if they may not be
@@ -322,52 +336,45 @@
 {
   tree sym = SSA_NAME_VAR (var);
   prop_value_t val = { UNINITIALIZED, NULL_TREE };
-  tree cst_val;
-  
-  if (!is_gimple_reg (var))
+  gimple stmt;
+
+  stmt = SSA_NAME_DEF_STMT (var);
+
+  if (gimple_nop_p (stmt))
     {
-      /* Short circuit for regular CCP.  We are not interested in any
-	 non-register when DO_STORE_CCP is false.  */
-      val.lattice_val = VARYING;
+      /* Variables defined by an empty statement are those used
+	 before being initialized.  If VAR is a local variable, we
+	 can assume initially that it is UNDEFINED, otherwise we must
+	 consider it VARYING.  */
+      if (is_gimple_reg (sym) && TREE_CODE (sym) != PARM_DECL)
+	val.lattice_val = UNDEFINED;
+      else
+	val.lattice_val = VARYING;
     }
-  else if ((cst_val = get_symbol_constant_value (sym)) != NULL_TREE)
+  else if (is_gimple_assign (stmt)
+	   /* Value-returning GIMPLE_CALL statements assign to
+	      a variable, and are treated similarly to GIMPLE_ASSIGN.  */
+	   || (is_gimple_call (stmt)
+	       && gimple_call_lhs (stmt) != NULL_TREE)
+	   || gimple_code (stmt) == GIMPLE_PHI)
     {
-      /* Globals and static variables declared 'const' take their
-	 initial value.  */
-      val.lattice_val = CONSTANT;
-      val.value = cst_val;
+      tree cst;
+      if (gimple_assign_single_p (stmt)
+	  && DECL_P (gimple_assign_rhs1 (stmt))
+	  && (cst = get_symbol_constant_value (gimple_assign_rhs1 (stmt))))
+	{
+	  val.lattice_val = CONSTANT;
+	  val.value = cst;
+	}
+      else
+	/* Any other variable defined by an assignment or a PHI node
+	   is considered UNDEFINED.  */
+	val.lattice_val = UNDEFINED;
     }
   else
     {
-      gimple stmt = SSA_NAME_DEF_STMT (var);
-
-      if (gimple_nop_p (stmt))
-	{
-	  /* Variables defined by an empty statement are those used
-	     before being initialized.  If VAR is a local variable, we
-	     can assume initially that it is UNDEFINED, otherwise we must
-	     consider it VARYING.  */
-	  if (is_gimple_reg (sym) && TREE_CODE (sym) != PARM_DECL)
-	    val.lattice_val = UNDEFINED;
-	  else
-	    val.lattice_val = VARYING;
-	}
-      else if (is_gimple_assign (stmt)
-               /* Value-returning GIMPLE_CALL statements assign to
-                  a variable, and are treated similarly to GIMPLE_ASSIGN.  */
-               || (is_gimple_call (stmt)
-                   && gimple_call_lhs (stmt) != NULL_TREE)
-	       || gimple_code (stmt) == GIMPLE_PHI)
-        {
-	  /* Any other variable defined by an assignment or a PHI node
-	     is considered UNDEFINED.  */
-	  val.lattice_val = UNDEFINED;
-	}
-      else
-	{
-	  /* Otherwise, VAR will never take on a constant value.  */
-	  val.lattice_val = VARYING;
-	}
+      /* Otherwise, VAR will never take on a constant value.  */
+      val.lattice_val = VARYING;
     }
 
   return val;
@@ -505,6 +512,7 @@
   bool has_constant_operand, has_undefined_operand, all_undefined_operands;
   tree use;
   ssa_op_iter iter;
+  unsigned i;
 
   enum gimple_code code = gimple_code (stmt);
 
@@ -520,33 +528,11 @@
   if (gimple_has_volatile_ops (stmt))
     return VARYING;
 
-  /* If we are not doing store-ccp, statements with loads
-     and/or stores will never fold into a constant.  */
-  if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS))
-    return VARYING;
-
-  /* Note that only a GIMPLE_SINGLE_RHS assignment can satisfy
-     is_gimple_min_invariant, so we do not consider calls or
-     other forms of assignment.  */
-  if (gimple_assign_single_p (stmt)
-      && is_gimple_min_invariant (gimple_assign_rhs1 (stmt)))
-    return CONSTANT;
-
-  if (code == GIMPLE_COND
-      && is_gimple_min_invariant (gimple_cond_lhs (stmt))
-      && is_gimple_min_invariant (gimple_cond_rhs (stmt)))
-    return CONSTANT;
-
-  if (code == GIMPLE_SWITCH
-      && is_gimple_min_invariant (gimple_switch_index (stmt)))
-    return CONSTANT;
-
   /* Arrive here for more complex cases.  */
-
   has_constant_operand = false;
   has_undefined_operand = false;
   all_undefined_operands = true;
-  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE | SSA_OP_VUSE)
+  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
     {
       prop_value_t *val = get_value (use);
 
@@ -559,6 +545,19 @@
 	has_constant_operand = true;
     }
 
+  /* There may be constants in regular rhs operands.  For calls we
+     have to ignore lhs, fndecl and static chain, otherwise only
+     the lhs.  */
+  for (i = (is_gimple_call (stmt) ? 2 : 0) + gimple_has_lhs (stmt);
+       i < gimple_num_ops (stmt); ++i)
+    {
+      tree op = gimple_op (stmt, i);
+      if (!op || TREE_CODE (op) == SSA_NAME)
+	continue;
+      if (is_gimple_min_invariant (op))
+	has_constant_operand = true;
+    }
+
   /* If the operation combines operands like COMPLEX_EXPR make sure to
      not mark the result UNDEFINED if only one part of the result is
      undefined.  */
@@ -589,11 +588,11 @@
   if (has_undefined_operand)
     return VARYING;
 
+  /* We do not consider virtual operands here -- load from read-only
+     memory may have only VARYING virtual operands, but still be
+     constant.  */
   if (has_constant_operand
-      /* We do not consider virtual operands here -- load from read-only
-	 memory may have only VARYING virtual operands, but still be
-	 constant.  */
-      || ZERO_SSA_OPERANDS (stmt, SSA_OP_USE))
+      || gimple_references_memory_p (stmt))
     return CONSTANT;
 
   return VARYING;
@@ -609,9 +608,6 @@
   if (gimple_has_volatile_ops (stmt))
     return true;
 
-  if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_ALL_VIRTUALS))
-    return true;
-
   /* If it is a call and does not return a value or is not a
      builtin and not an indirect call, it is varying.  */
   if (is_gimple_call (stmt))
@@ -623,6 +619,10 @@
 	return true;
     }
 
+  /* Any other store operation is not interesting.  */
+  else if (gimple_vdef (stmt))
+    return true;
+
   /* Anything other than assignments and conditional jumps are not
      interesting for CCP.  */
   if (gimple_code (stmt) != GIMPLE_ASSIGN
@@ -651,7 +651,15 @@
       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
         {
 	  gimple stmt = gsi_stmt (i);
-	  bool is_varying = surely_varying_stmt_p (stmt);
+	  bool is_varying;
+
+	  /* If the statement is a control insn, then we do not
+	     want to avoid simulating the statement once.  Failure
+	     to do so means that those edges will never get added.  */
+	  if (stmt_ends_bb_p (stmt))
+	    is_varying = false;
+	  else
+	    is_varying = surely_varying_stmt_p (stmt);
 
 	  if (is_varying)
 	    {
@@ -661,10 +669,7 @@
 	      /* If the statement will not produce a constant, mark
 		 all its outputs VARYING.  */
 	      FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
-		{
-		  if (is_varying)
-		    set_value_varying (def);
-		}
+		set_value_varying (def);
 	    }
           prop_set_simulate_again (stmt, !is_varying);
 	}
@@ -689,17 +694,38 @@
     }
 }
 
+/* Debug count support. Reset the values of ssa names
+   VARYING when the total number ssa names analyzed is
+   beyond the debug count specified.  */
+
+static void
+do_dbg_cnt (void)
+{
+  unsigned i;
+  for (i = 0; i < num_ssa_names; i++)
+    {
+      if (!dbg_cnt (ccp))
+        {
+          const_val[i].lattice_val = VARYING;
+          const_val[i].value = NULL_TREE;
+        }
+    }
+}
+
 
 /* Do final substitution of propagated values, cleanup the flowgraph and
-   free allocated storage.  
+   free allocated storage.
 
    Return TRUE when something was optimized.  */
 
 static bool
 ccp_finalize (void)
 {
+  bool something_changed;
+
+  do_dbg_cnt ();
   /* Perform substitutions based on the known constant values.  */
-  bool something_changed = substitute_and_fold (const_val, false);
+  something_changed = substitute_and_fold (const_val, ccp_fold_stmt);
 
   free (const_val);
   const_val = NULL;
@@ -856,7 +882,7 @@
     return SSA_PROP_NOT_INTERESTING;
 }
 
-/* Return true if we may propagate the address expression ADDR into the 
+/* Return true if we may propagate the address expression ADDR into the
    dereference DEREF and cancel them.  */
 
 bool
@@ -898,6 +924,7 @@
 static tree
 ccp_fold (gimple stmt)
 {
+  location_t loc = gimple_location (stmt);
   switch (gimple_code (stmt))
     {
     case GIMPLE_ASSIGN:
@@ -946,24 +973,61 @@
 			}
 		    }
 		}
+	      else if (TREE_CODE (rhs) == CONSTRUCTOR
+		       && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
+		       && (CONSTRUCTOR_NELTS (rhs)
+			   == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
+		{
+		  unsigned i;
+		  tree val, list;
+
+		  list = NULL_TREE;
+		  FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
+		    {
+		      if (TREE_CODE (val) == SSA_NAME
+			  && get_value (val)->lattice_val == CONSTANT)
+			val = get_value (val)->value;
+		      if (TREE_CODE (val) == INTEGER_CST
+			  || TREE_CODE (val) == REAL_CST
+			  || TREE_CODE (val) == FIXED_CST)
+			list = tree_cons (NULL_TREE, val, list);
+		      else
+			return NULL_TREE;
+		    }
+
+		  return build_vector (TREE_TYPE (rhs), nreverse (list));
+		}
 
               if (kind == tcc_reference)
 		{
-		  if (TREE_CODE (rhs) == VIEW_CONVERT_EXPR
+		  if ((TREE_CODE (rhs) == VIEW_CONVERT_EXPR
+		       || TREE_CODE (rhs) == REALPART_EXPR
+		       || TREE_CODE (rhs) == IMAGPART_EXPR)
 		      && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
 		    {
 		      prop_value_t *val = get_value (TREE_OPERAND (rhs, 0));
 		      if (val->lattice_val == CONSTANT)
-			return fold_unary (VIEW_CONVERT_EXPR,
+			return fold_unary_loc (EXPR_LOCATION (rhs),
+					   TREE_CODE (rhs),
 					   TREE_TYPE (rhs), val->value);
 		    }
+		  else if (TREE_CODE (rhs) == INDIRECT_REF
+			   && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
+		    {
+		      prop_value_t *val = get_value (TREE_OPERAND (rhs, 0));
+		      if (val->lattice_val == CONSTANT
+			  && TREE_CODE (val->value) == ADDR_EXPR
+			  && useless_type_conversion_p (TREE_TYPE (rhs),
+							TREE_TYPE (TREE_TYPE (val->value))))
+			rhs = TREE_OPERAND (val->value, 0);
+		    }
 		  return fold_const_aggregate_ref (rhs);
 		}
               else if (kind == tcc_declaration)
                 return get_symbol_constant_value (rhs);
               return rhs;
             }
-            
+
           case GIMPLE_UNARY_RHS:
             {
               /* Handle unary operators that can appear in GIMPLE form.
@@ -1000,14 +1064,16 @@
 		  if (!useless_type_conversion_p (TREE_TYPE (lhs),
 						  TREE_TYPE (op0))
 		      && ((tem = maybe_fold_offset_to_address
-				   (op0, integer_zero_node, TREE_TYPE (lhs)))
+			   (loc,
+			    op0, integer_zero_node, TREE_TYPE (lhs)))
 			  != NULL_TREE))
 		    return tem;
 		  return op0;
 		}
 
-              return fold_unary_ignore_overflow (subcode,
-						 gimple_expr_type (stmt), op0);
+              return
+		fold_unary_ignore_overflow_loc (loc, subcode,
+						gimple_expr_type (stmt), op0);
             }
 
           case GIMPLE_BINARY_RHS:
@@ -1036,14 +1102,14 @@
 		  && TREE_CODE (op0) == ADDR_EXPR
 		  && TREE_CODE (op1) == INTEGER_CST)
 		{
-		  tree lhs = gimple_assign_lhs (stmt);
-		  tree tem = maybe_fold_offset_to_address (op0, op1,
-							   TREE_TYPE (lhs));
+		  tree tem = maybe_fold_offset_to_address
+		    (loc, op0, op1, TREE_TYPE (op0));
 		  if (tem != NULL_TREE)
 		    return tem;
 		}
 
-              return fold_binary (subcode, gimple_expr_type (stmt), op0, op1);
+              return fold_binary_loc (loc, subcode,
+				  gimple_expr_type (stmt), op0, op1);
             }
 
           default:
@@ -1080,9 +1146,10 @@
 		      args[i] = val->value;
 		  }
 	      }
-	    call = build_call_array (gimple_call_return_type (stmt),
-				     fn, gimple_call_num_args (stmt), args);
-	    retval = fold_call_expr (call, false);
+	    call = build_call_array_loc (loc,
+					 gimple_call_return_type (stmt),
+					 fn, gimple_call_num_args (stmt), args);
+	    retval = fold_call_expr (EXPR_LOCATION (call), call, false);
 	    if (retval)
 	      /* fold_call_expr wraps the result inside a NOP_EXPR.  */
 	      STRIP_NOPS (retval);
@@ -1113,7 +1180,7 @@
               op1 = val->value;
           }
 
-        return fold_binary (code, boolean_type_node, op0, op1);
+        return fold_binary_loc (loc, code, boolean_type_node, op0, op1);
       }
 
     case GIMPLE_SWITCH:
@@ -1148,6 +1215,9 @@
   unsigned HOST_WIDE_INT cnt;
   tree cfield, cval;
 
+  if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_declaration)
+    return get_symbol_constant_value (t);
+
   switch (TREE_CODE (t))
     {
     case ARRAY_REF:
@@ -1228,6 +1298,12 @@
 	if (tree_int_cst_equal (cfield, idx))
 	  {
 	    STRIP_USELESS_TYPE_CONVERSION (cval);
+	    if (TREE_CODE (cval) == ADDR_EXPR)
+	      {
+		tree base = get_base_address (TREE_OPERAND (cval, 0));
+		if (base && TREE_CODE (base) == VAR_DECL)
+		  add_referenced_var (base);
+	      }
 	    return cval;
 	  }
       break;
@@ -1271,6 +1347,12 @@
 	    && ! DECL_BIT_FIELD (cfield))
 	  {
 	    STRIP_USELESS_TYPE_CONVERSION (cval);
+	    if (TREE_CODE (cval) == ADDR_EXPR)
+	      {
+		tree base = get_base_address (TREE_OPERAND (cval, 0));
+		if (base && TREE_CODE (base) == VAR_DECL)
+		  add_referenced_var (base);
+	      }
 	    return cval;
 	  }
       break;
@@ -1280,7 +1362,8 @@
       {
 	tree c = fold_const_aggregate_ref (TREE_OPERAND (t, 0));
 	if (c && TREE_CODE (c) == COMPLEX_CST)
-	  return fold_build1 (TREE_CODE (t), TREE_TYPE (t), c);
+	  return fold_build1_loc (EXPR_LOCATION (t),
+			      TREE_CODE (t), TREE_TYPE (t), c);
 	break;
       }
 
@@ -1332,7 +1415,7 @@
       if (code == GIMPLE_ASSIGN)
         {
           enum tree_code subcode = gimple_assign_rhs_code (stmt);
-          
+
           /* Other cases cannot satisfy is_gimple_min_invariant
              without folding.  */
           if (get_gimple_rhs_class (subcode) == GIMPLE_SINGLE_RHS)
@@ -1341,8 +1424,8 @@
       else if (code == GIMPLE_SWITCH)
         simplified = gimple_switch_index (stmt);
       else
-        /* These cannot satisfy is_gimple_min_invariant without folding.  */
-        gcc_assert (code == GIMPLE_CALL || code == GIMPLE_COND);
+	/* These cannot satisfy is_gimple_min_invariant without folding.  */
+	gcc_assert (code == GIMPLE_CALL || code == GIMPLE_COND);
     }
 
   is_constant = simplified && is_gimple_min_invariant (simplified);
@@ -1390,6 +1473,34 @@
   return val;
 }
 
+/* Fold the stmt at *GSI with CCP specific information that propagating
+   and regular folding does not catch.  */
+
+static bool
+ccp_fold_stmt (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  prop_value_t val;
+
+  if (gimple_code (stmt) != GIMPLE_COND)
+    return false;
+
+  /* Statement evaluation will handle type mismatches in constants
+     more gracefully than the final propagation.  This allows us to
+     fold more conditionals here.  */
+  val = evaluate_stmt (stmt);
+  if (val.lattice_val != CONSTANT
+      || TREE_CODE (val.value) != INTEGER_CST)
+    return false;
+
+  if (integer_zerop (val.value))
+    gimple_cond_make_false (stmt);
+  else
+    gimple_cond_make_true (stmt);
+
+  return true;
+}
+
 /* Visit the assignment statement STMT.  Set the value of its LHS to the
    value computed by the RHS and store LHS in *OUTPUT_P.  If STMT
    creates virtual definitions, set the value of each new name to that
@@ -1476,7 +1587,7 @@
    its evaluation changes the lattice value of its output, return
    SSA_PROP_INTERESTING and set *OUTPUT_P to the SSA_NAME holding the
    output value.
-   
+
    If STMT is a conditional branch and we can determine its truth
    value, set *TAKEN_EDGE_P accordingly.  If STMT produces a varying
    value, return SSA_PROP_VARYING.  */
@@ -1558,7 +1669,7 @@
 }
 
 
-struct gimple_opt_pass pass_ccp = 
+struct gimple_opt_pass pass_ccp =
 {
  {
   GIMPLE_PASS,
@@ -1579,12 +1690,15 @@
 };
 
 
-/* A subroutine of fold_stmt_r.  Attempts to fold *(A+O) to A[X].
+/* A subroutine of fold_stmt.  Attempts to fold *(A+O) to A[X].
    BASE is an array type.  OFFSET is a byte displacement.  ORIG_TYPE
-   is the desired result type.  */
+   is the desired result type.
+
+   LOC is the location of the original expression.  */
 
 static tree
-maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type,
+maybe_fold_offset_to_array_ref (location_t loc, tree base, tree offset,
+				tree orig_type,
 				bool allow_negative_idx)
 {
   tree min_idx, idx, idx_type, elt_offset = integer_zero_node;
@@ -1693,7 +1807,7 @@
        (char *)a - 4;
      which should be not folded to &a->d[-8].  */
   if (domain_type
-      && TYPE_MAX_VALUE (domain_type) 
+      && TYPE_MAX_VALUE (domain_type)
       && TREE_CODE (TYPE_MAX_VALUE (domain_type)) == INTEGER_CST)
     {
       tree up_bound = TYPE_MAX_VALUE (domain_type);
@@ -1717,17 +1831,23 @@
 	   && compare_tree_int (idx, 0) < 0)
     return NULL_TREE;
 
-  return build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE);
+  {
+    tree t = build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE);
+    SET_EXPR_LOCATION (t, loc);
+    return t;
+  }
 }
 
 
 /* Attempt to fold *(S+O) to S.X.
    BASE is a record type.  OFFSET is a byte displacement.  ORIG_TYPE
-   is the desired result type.  */
+   is the desired result type.
+
+   LOC is the location of the original expression.  */
 
 static tree
-maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
-				    tree orig_type)
+maybe_fold_offset_to_component_ref (location_t loc, tree record_type,
+				    tree base, tree offset, tree orig_type)
 {
   tree f, t, field_type, tail_array_field, field_offset;
   tree ret;
@@ -1782,7 +1902,7 @@
 	  t = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
 	  return t;
 	}
-      
+
       /* Don't care about offsets into the middle of scalars.  */
       if (!AGGREGATE_TYPE_P (field_type))
 	continue;
@@ -1804,13 +1924,14 @@
       /* If we matched, then set offset to the displacement into
 	 this field.  */
       new_base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
+      SET_EXPR_LOCATION (new_base, loc);
 
       /* Recurse to possibly find the match.  */
-      ret = maybe_fold_offset_to_array_ref (new_base, t, orig_type,
+      ret = maybe_fold_offset_to_array_ref (loc, new_base, t, orig_type,
 					    f == TYPE_FIELDS (record_type));
       if (ret)
 	return ret;
-      ret = maybe_fold_offset_to_component_ref (field_type, new_base, t,
+      ret = maybe_fold_offset_to_component_ref (loc, field_type, new_base, t,
 						orig_type);
       if (ret)
 	return ret;
@@ -1823,26 +1944,30 @@
   field_type = TREE_TYPE (f);
   offset = int_const_binop (MINUS_EXPR, offset, byte_position (f), 1);
 
-  /* If we get here, we've got an aggregate field, and a possibly 
+  /* If we get here, we've got an aggregate field, and a possibly
      nonzero offset into them.  Recurse and hope for a valid match.  */
   base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
-
-  t = maybe_fold_offset_to_array_ref (base, offset, orig_type,
+  SET_EXPR_LOCATION (base, loc);
+
+  t = maybe_fold_offset_to_array_ref (loc, base, offset, orig_type,
 				      f == TYPE_FIELDS (record_type));
   if (t)
     return t;
-  return maybe_fold_offset_to_component_ref (field_type, base, offset,
+  return maybe_fold_offset_to_component_ref (loc, field_type, base, offset,
 					     orig_type);
 }
 
 /* Attempt to express (ORIG_TYPE)BASE+OFFSET as BASE->field_of_orig_type
-   or BASE[index] or by combination of those. 
+   or BASE[index] or by combination of those.
+
+   LOC is the location of original expression.
 
    Before attempting the conversion strip off existing ADDR_EXPRs and
    handled component refs.  */
 
 tree
-maybe_fold_offset_to_reference (tree base, tree offset, tree orig_type)
+maybe_fold_offset_to_reference (location_t loc, tree base, tree offset,
+				tree orig_type)
 {
   tree ret;
   tree type;
@@ -1872,7 +1997,7 @@
 	  if (sub_offset)
 	    offset = int_const_binop (PLUS_EXPR, offset,
 				      build_int_cst (TREE_TYPE (offset),
-					     sub_offset / BITS_PER_UNIT), 1);
+						     sub_offset / BITS_PER_UNIT), 1);
 	}
     }
   if (useless_type_conversion_p (orig_type, TREE_TYPE (base))
@@ -1880,9 +2005,9 @@
     return base;
   type = TREE_TYPE (base);
 
-  ret = maybe_fold_offset_to_component_ref (type, base, offset, orig_type);
+  ret = maybe_fold_offset_to_component_ref (loc, type, base, offset, orig_type);
   if (!ret)
-    ret = maybe_fold_offset_to_array_ref (base, offset, orig_type, true);
+    ret = maybe_fold_offset_to_array_ref (loc, base, offset, orig_type, true);
 
   return ret;
 }
@@ -1890,17 +2015,21 @@
 /* Attempt to express (ORIG_TYPE)&BASE+OFFSET as &BASE->field_of_orig_type
    or &BASE[index] or by combination of those.
 
+   LOC is the location of the original expression.
+
    Before attempting the conversion strip off existing component refs.  */
 
 tree
-maybe_fold_offset_to_address (tree addr, tree offset, tree orig_type)
+maybe_fold_offset_to_address (location_t loc, tree addr, tree offset,
+			      tree orig_type)
 {
   tree t;
 
   gcc_assert (POINTER_TYPE_P (TREE_TYPE (addr))
 	      && POINTER_TYPE_P (orig_type));
 
-  t = maybe_fold_offset_to_reference (addr, offset, TREE_TYPE (orig_type));
+  t = maybe_fold_offset_to_reference (loc, addr, offset,
+				      TREE_TYPE (orig_type));
   if (t != NULL_TREE)
     {
       tree orig = addr;
@@ -1937,13 +2066,13 @@
       ptr_type = build_pointer_type (TREE_TYPE (t));
       if (!useless_type_conversion_p (orig_type, ptr_type))
 	return NULL_TREE;
-      return build_fold_addr_expr_with_type (t, ptr_type);
+      return build_fold_addr_expr_with_type_loc (loc, t, ptr_type);
     }
 
   return NULL_TREE;
 }
 
-/* A subroutine of fold_stmt_r.  Attempt to simplify *(BASE+OFFSET).
+/* A subroutine of fold_stmt.  Attempt to simplify *(BASE+OFFSET).
    Return the simplified expression, or NULL if nothing could be done.  */
 
 static tree
@@ -1951,6 +2080,7 @@
 {
   tree t;
   bool volatile_p = TREE_THIS_VOLATILE (expr);
+  location_t loc = EXPR_LOCATION (expr);
 
   /* We may well have constructed a double-nested PLUS_EXPR via multiple
      substitutions.  Fold that down to one.  Remove NON_LVALUE_EXPRs that
@@ -1991,7 +2121,7 @@
 	return DECL_INITIAL (base);
 
       /* Try folding *(&B+O) to B.X.  */
-      t = maybe_fold_offset_to_reference (base_addr, offset,
+      t = maybe_fold_offset_to_reference (loc, base_addr, offset,
 					  TREE_TYPE (expr));
       if (t)
 	{
@@ -2005,7 +2135,7 @@
     }
   else
     {
-      /* We can get here for out-of-range string constant accesses, 
+      /* We can get here for out-of-range string constant accesses,
 	 such as "_"[3].  Bail out of the entire substitution search
 	 and arrange for the entire statement to be replaced by a
 	 call to __builtin_trap.  In all likelihood this will all be
@@ -2018,11 +2148,11 @@
 	  && TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST)
 	{
 	  /* FIXME: Except that this causes problems elsewhere with dead
-	     code not being deleted, and we die in the rtl expanders 
+	     code not being deleted, and we die in the rtl expanders
 	     because we failed to remove some ssa_name.  In the meantime,
 	     just return zero.  */
 	  /* FIXME2: This condition should be signaled by
-	     fold_read_from_constant_string directly, rather than 
+	     fold_read_from_constant_string directly, rather than
 	     re-checking for it here.  */
 	  return integer_zero_node;
 	}
@@ -2030,7 +2160,7 @@
       /* Try folding *(B+O) to B->X.  Still an improvement.  */
       if (POINTER_TYPE_P (TREE_TYPE (base)))
 	{
-          t = maybe_fold_offset_to_reference (base, offset,
+          t = maybe_fold_offset_to_reference (loc, base, offset,
 				              TREE_TYPE (expr));
 	  if (t)
 	    return t;
@@ -2055,19 +2185,52 @@
    which may be able to propagate further.  */
 
 tree
-maybe_fold_stmt_addition (tree res_type, tree op0, tree op1)
+maybe_fold_stmt_addition (location_t loc, tree res_type, tree op0, tree op1)
 {
   tree ptd_type;
   tree t;
 
-  /* It had better be a constant.  */
-  if (TREE_CODE (op1) != INTEGER_CST)
-    return NULL_TREE;
   /* The first operand should be an ADDR_EXPR.  */
   if (TREE_CODE (op0) != ADDR_EXPR)
     return NULL_TREE;
   op0 = TREE_OPERAND (op0, 0);
 
+  /* It had better be a constant.  */
+  if (TREE_CODE (op1) != INTEGER_CST)
+    {
+      /* Or op0 should now be A[0] and the non-constant offset defined
+	 via a multiplication by the array element size.  */
+      if (TREE_CODE (op0) == ARRAY_REF
+	  && integer_zerop (TREE_OPERAND (op0, 1))
+	  && TREE_CODE (op1) == SSA_NAME
+	  && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (op0)), 1))
+	{
+	  gimple offset_def = SSA_NAME_DEF_STMT (op1);
+	  if (!is_gimple_assign (offset_def))
+	    return NULL_TREE;
+
+	  if (gimple_assign_rhs_code (offset_def) == MULT_EXPR
+	      && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST
+	      && tree_int_cst_equal (gimple_assign_rhs2 (offset_def),
+				     TYPE_SIZE_UNIT (TREE_TYPE (op0))))
+	    return build_fold_addr_expr
+			  (build4 (ARRAY_REF, TREE_TYPE (op0),
+				   TREE_OPERAND (op0, 0),
+				   gimple_assign_rhs1 (offset_def),
+				   TREE_OPERAND (op0, 2),
+				   TREE_OPERAND (op0, 3)));
+	  else if (integer_onep (TYPE_SIZE_UNIT (TREE_TYPE (op0)))
+		   && gimple_assign_rhs_code (offset_def) != MULT_EXPR)
+	    return build_fold_addr_expr
+			  (build4 (ARRAY_REF, TREE_TYPE (op0),
+				   TREE_OPERAND (op0, 0),
+				   op1,
+				   TREE_OPERAND (op0, 2),
+				   TREE_OPERAND (op0, 3)));
+	}
+      return NULL_TREE;
+    }
+
   /* If the first operand is an ARRAY_REF, expand it so that we can fold
      the offset into it.  */
   while (TREE_CODE (op0) == ARRAY_REF)
@@ -2119,190 +2282,90 @@
     ptd_type = TREE_TYPE (TREE_TYPE (op0));
 
   /* At which point we can try some of the same things as for indirects.  */
-  t = maybe_fold_offset_to_array_ref (op0, op1, ptd_type, true);
+  t = maybe_fold_offset_to_array_ref (loc, op0, op1, ptd_type, true);
   if (!t)
-    t = maybe_fold_offset_to_component_ref (TREE_TYPE (op0), op0, op1,
+    t = maybe_fold_offset_to_component_ref (loc, TREE_TYPE (op0), op0, op1,
 					    ptd_type);
   if (t)
-    t = build1 (ADDR_EXPR, res_type, t);
+    {
+      t = build1 (ADDR_EXPR, res_type, t);
+      SET_EXPR_LOCATION (t, loc);
+    }
 
   return t;
 }
 
-/* For passing state through walk_tree into fold_stmt_r and its
-   children.  */
-
-struct fold_stmt_r_data
-{
-  gimple stmt;
-  bool *changed_p;
-  bool *inside_addr_expr_p;
-};
-
-/* Subroutine of fold_stmt called via walk_tree.  We perform several
-   simplifications of EXPR_P, mostly having to do with pointer arithmetic.  */
+/* Subroutine of fold_stmt.  We perform several simplifications of the
+   memory reference tree EXPR and make sure to re-gimplify them properly
+   after propagation of constant addresses.  IS_LHS is true if the
+   reference is supposed to be an lvalue.  */
 
 static tree
-fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data)
+maybe_fold_reference (tree expr, bool is_lhs)
 {
-  struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
-  struct fold_stmt_r_data *fold_stmt_r_data;
-  bool *inside_addr_expr_p;
-  bool *changed_p;
-  tree expr = *expr_p, t;
-  bool volatile_p = TREE_THIS_VOLATILE (expr);
-
-  fold_stmt_r_data = (struct fold_stmt_r_data *) wi->info;
-  inside_addr_expr_p = fold_stmt_r_data->inside_addr_expr_p;
-  changed_p = fold_stmt_r_data->changed_p;
-
-  /* ??? It'd be nice if walk_tree had a pre-order option.  */
-  switch (TREE_CODE (expr))
+  tree *t = &expr;
+
+  if (TREE_CODE (expr) == ARRAY_REF
+      && !is_lhs)
+    {
+      tree tem = fold_read_from_constant_string (expr);
+      if (tem)
+	return tem;
+    }
+
+  /* ???  We might want to open-code the relevant remaining cases
+     to avoid using the generic fold.  */
+  if (handled_component_p (*t)
+      && CONSTANT_CLASS_P (TREE_OPERAND (*t, 0)))
     {
-    case INDIRECT_REF:
-      t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
-      if (t)
-	return t;
-      *walk_subtrees = 0;
-
-      t = maybe_fold_stmt_indirect (expr, TREE_OPERAND (expr, 0),
-				    integer_zero_node);
+      tree tem = fold (*t);
+      if (tem != *t)
+	return tem;
+    }
+
+  while (handled_component_p (*t))
+    t = &TREE_OPERAND (*t, 0);
+
+  if (TREE_CODE (*t) == INDIRECT_REF)
+    {
+      tree tem = maybe_fold_stmt_indirect (*t, TREE_OPERAND (*t, 0),
+					   integer_zero_node);
       /* Avoid folding *"abc" = 5 into 'a' = 5.  */
-      if (wi->is_lhs && t && TREE_CODE (t) == INTEGER_CST)
-	t = NULL_TREE;
-      if (!t
-	  && TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR)
+      if (is_lhs && tem && CONSTANT_CLASS_P (tem))
+	tem = NULL_TREE;
+      if (!tem
+	  && TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR)
 	/* If we had a good reason for propagating the address here,
 	   make sure we end up with valid gimple.  See PR34989.  */
-	t = TREE_OPERAND (TREE_OPERAND (expr, 0), 0);
-      break;
-
-    case NOP_EXPR:
-      t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
-      if (t)
-	return t;
-      *walk_subtrees = 0;
-
-      if (POINTER_TYPE_P (TREE_TYPE (expr))
-          && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (expr)))
-	  && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0)))
-	  && (t = maybe_fold_offset_to_address (TREE_OPERAND (expr, 0),
-						integer_zero_node,
-						TREE_TYPE (TREE_TYPE (expr)))))
-	return t;
-      break;
-
-      /* ??? Could handle more ARRAY_REFs here, as a variant of INDIRECT_REF.
-	 We'd only want to bother decomposing an existing ARRAY_REF if
-	 the base array is found to have another offset contained within.
-	 Otherwise we'd be wasting time.  */
-    case ARRAY_REF:
-      /* If we are not processing expressions found within an
-	 ADDR_EXPR, then we can fold constant array references.
-	 Don't fold on LHS either, to avoid folding "abc"[0] = 5
-	 into 'a' = 5.  */
-      if (!*inside_addr_expr_p && !wi->is_lhs)
-	t = fold_read_from_constant_string (expr);
-      else
-	t = NULL;
-      break;
-
-    case ADDR_EXPR:
-      *inside_addr_expr_p = true;
-      t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
-      *inside_addr_expr_p = false;
-      if (t)
-	return t;
-      *walk_subtrees = 0;
-
-      /* Make sure the value is properly considered constant, and so gets
-	 propagated as expected.  */
-      if (*changed_p)
-        recompute_tree_invariant_for_addr_expr (expr);
-      return NULL_TREE;
-
-    case COMPONENT_REF:
-      t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
-      if (t)
-        return t;
-      *walk_subtrees = 0;
-
-      /* Make sure the FIELD_DECL is actually a field in the type on the lhs.
-	 We've already checked that the records are compatible, so we should
-	 come up with a set of compatible fields.  */
-      {
-	tree expr_record = TREE_TYPE (TREE_OPERAND (expr, 0));
-	tree expr_field = TREE_OPERAND (expr, 1);
-
-        if (DECL_FIELD_CONTEXT (expr_field) != TYPE_MAIN_VARIANT (expr_record))
-	  {
-	    expr_field = find_compatible_field (expr_record, expr_field);
-	    TREE_OPERAND (expr, 1) = expr_field;
-	  }
-      }
-      break;
-
-    case TARGET_MEM_REF:
-      t = maybe_fold_tmr (expr);
-      break;
-
-    case POINTER_PLUS_EXPR:
-      t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
-      if (t)
-        return t;
-      t = walk_tree (&TREE_OPERAND (expr, 1), fold_stmt_r, data, NULL);
-      if (t)
-        return t;
-      *walk_subtrees = 0;
-
-      t = maybe_fold_stmt_addition (TREE_TYPE (expr),
-                                    TREE_OPERAND (expr, 0),
-                                    TREE_OPERAND (expr, 1));
-      break;
-
-    case COND_EXPR:
-      if (COMPARISON_CLASS_P (TREE_OPERAND (expr, 0)))
-        {
-	  tree op0 = TREE_OPERAND (expr, 0);
-          tree tem;
-	  bool set;
-
-	  fold_defer_overflow_warnings ();
-	  tem = fold_binary (TREE_CODE (op0), TREE_TYPE (op0),
-			     TREE_OPERAND (op0, 0),
-			     TREE_OPERAND (op0, 1));
-          /* This is actually a conditional expression, not a GIMPLE
-             conditional statement, however, the valid_gimple_rhs_p
-             test still applies.  */
-	  set = tem && is_gimple_condexpr (tem) && valid_gimple_rhs_p (tem);
-	  fold_undefer_overflow_warnings (set, fold_stmt_r_data->stmt, 0);
-	  if (set)
-	    {
-              COND_EXPR_COND (expr) = tem;
-	      t = expr;
-	      break;
-	    }
-        }
-      return NULL_TREE;
-
-    default:
-      return NULL_TREE;
+	tem = TREE_OPERAND (TREE_OPERAND (*t, 0), 0);
+
+      if (tem)
+	{
+	  *t = tem;
+	  tem = maybe_fold_reference (expr, is_lhs);
+	  if (tem)
+	    return tem;
+	  return expr;
+	}
     }
-
-  if (t)
+  else if (!is_lhs
+	   && DECL_P (*t))
     {
-      /* Preserve volatileness of the original expression.
-	 We can end up with a plain decl here which is shared
-	 and we shouldn't mess with its flags.  */
-      if (!SSA_VAR_P (t))
-	TREE_THIS_VOLATILE (t) = volatile_p;
-      *expr_p = t;
-      *changed_p = true;
+      tree tem = get_symbol_constant_value (*t);
+      if (tem)
+	{
+	  *t = tem;
+	  tem = maybe_fold_reference (expr, is_lhs);
+	  if (tem)
+	    return tem;
+	  return expr;
+	}
     }
 
   return NULL_TREE;
 }
 
+
 /* Return the string length, maximum string length or maximum value of
    ARG in LENGTH.
    If ARG is an SSA name variable, follow its use-def chains.  If LENGTH
@@ -2317,7 +2380,7 @@
 {
   tree var, val;
   gimple def_stmt;
-  
+
   if (TREE_CODE (arg) != SSA_NAME)
     {
       if (TREE_CODE (arg) == COND_EXPR)
@@ -2412,7 +2475,7 @@
               return false;
           }
         }
-        return true;        
+        return true;
 
       default:
         return false;
@@ -2436,6 +2499,7 @@
   bitmap visited;
   bool ignore;
   int nargs;
+  location_t loc = gimple_location (stmt);
 
   gcc_assert (is_gimple_call (stmt));
 
@@ -2532,7 +2596,7 @@
 
     case BUILT_IN_STRCPY:
       if (val[1] && is_gimple_val (val[1]) && nargs == 2)
-	result = fold_builtin_strcpy (callee,
+	result = fold_builtin_strcpy (loc, callee,
                                       gimple_call_arg (stmt, 0),
                                       gimple_call_arg (stmt, 1),
 				      val[1]);
@@ -2540,7 +2604,7 @@
 
     case BUILT_IN_STRNCPY:
       if (val[1] && is_gimple_val (val[1]) && nargs == 3)
-	result = fold_builtin_strncpy (callee,
+	result = fold_builtin_strncpy (loc, callee,
                                        gimple_call_arg (stmt, 0),
                                        gimple_call_arg (stmt, 1),
                                        gimple_call_arg (stmt, 2),
@@ -2549,14 +2613,14 @@
 
     case BUILT_IN_FPUTS:
       if (nargs == 2)
-	result = fold_builtin_fputs (gimple_call_arg (stmt, 0),
+	result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
 				     gimple_call_arg (stmt, 1),
 				     ignore, false, val[0]);
       break;
 
     case BUILT_IN_FPUTS_UNLOCKED:
       if (nargs == 2)
-	result = fold_builtin_fputs (gimple_call_arg (stmt, 0),
+	result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
 				     gimple_call_arg (stmt, 1),
 				     ignore, true, val[0]);
       break;
@@ -2566,7 +2630,7 @@
     case BUILT_IN_MEMMOVE_CHK:
     case BUILT_IN_MEMSET_CHK:
       if (val[2] && is_gimple_val (val[2]) && nargs == 4)
-	result = fold_builtin_memory_chk (callee,
+	result = fold_builtin_memory_chk (loc, callee,
                                           gimple_call_arg (stmt, 0),
                                           gimple_call_arg (stmt, 1),
                                           gimple_call_arg (stmt, 2),
@@ -2578,7 +2642,7 @@
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
       if (val[1] && is_gimple_val (val[1]) && nargs == 3)
-	result = fold_builtin_stxcpy_chk (callee,
+	result = fold_builtin_stxcpy_chk (loc, callee,
                                           gimple_call_arg (stmt, 0),
                                           gimple_call_arg (stmt, 1),
                                           gimple_call_arg (stmt, 2),
@@ -2588,7 +2652,7 @@
 
     case BUILT_IN_STRNCPY_CHK:
       if (val[2] && is_gimple_val (val[2]) && nargs == 4)
-	result = fold_builtin_strncpy_chk (gimple_call_arg (stmt, 0),
+	result = fold_builtin_strncpy_chk (loc, gimple_call_arg (stmt, 0),
                                            gimple_call_arg (stmt, 1),
                                            gimple_call_arg (stmt, 2),
                                            gimple_call_arg (stmt, 3),
@@ -2621,41 +2685,101 @@
 {
   gimple stmt = gsi_stmt (*si);
   enum tree_code subcode = gimple_assign_rhs_code (stmt);
-
-  tree result = NULL;
+  location_t loc = gimple_location (stmt);
+
+  tree result = NULL_TREE;
 
   switch (get_gimple_rhs_class (subcode))
     {
     case GIMPLE_SINGLE_RHS:
       {
         tree rhs = gimple_assign_rhs1 (stmt);
-        
+
         /* Try to fold a conditional expression.  */
         if (TREE_CODE (rhs) == COND_EXPR)
           {
-            tree temp = fold (COND_EXPR_COND (rhs));
-            if (temp != COND_EXPR_COND (rhs))
-              result = fold_build3 (COND_EXPR, TREE_TYPE (rhs), temp,
-                                    COND_EXPR_THEN (rhs), COND_EXPR_ELSE (rhs));
+	    tree op0 = COND_EXPR_COND (rhs);
+	    tree tem;
+	    bool set = false;
+	    location_t cond_loc = EXPR_LOCATION (rhs);
+
+	    if (COMPARISON_CLASS_P (op0))
+	      {
+		fold_defer_overflow_warnings ();
+		tem = fold_binary_loc (cond_loc,
+				   TREE_CODE (op0), TREE_TYPE (op0),
+				   TREE_OPERAND (op0, 0),
+				   TREE_OPERAND (op0, 1));
+		/* This is actually a conditional expression, not a GIMPLE
+		   conditional statement, however, the valid_gimple_rhs_p
+		   test still applies.  */
+		set = (tem && is_gimple_condexpr (tem)
+		       && valid_gimple_rhs_p (tem));
+		fold_undefer_overflow_warnings (set, stmt, 0);
+	      }
+	    else if (is_gimple_min_invariant (op0))
+	      {
+		tem = op0;
+		set = true;
+	      }
+	    else
+	      return NULL_TREE;
+
+	    if (set)
+	      result = fold_build3_loc (cond_loc, COND_EXPR, TREE_TYPE (rhs), tem,
+				    COND_EXPR_THEN (rhs), COND_EXPR_ELSE (rhs));
           }
 
+	else if (TREE_CODE (rhs) == TARGET_MEM_REF)
+	  return maybe_fold_tmr (rhs);
+
+	else if (REFERENCE_CLASS_P (rhs))
+	  return maybe_fold_reference (rhs, false);
+
+	else if (TREE_CODE (rhs) == ADDR_EXPR)
+	  {
+	    tree tem = maybe_fold_reference (TREE_OPERAND (rhs, 0), true);
+	    if (tem)
+	      result = fold_convert (TREE_TYPE (rhs),
+				     build_fold_addr_expr_loc (loc, tem));
+	  }
+
+	else if (TREE_CODE (rhs) == CONSTRUCTOR
+		 && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
+		 && (CONSTRUCTOR_NELTS (rhs)
+		     == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
+	  {
+	    /* Fold a constant vector CONSTRUCTOR to VECTOR_CST.  */
+	    unsigned i;
+	    tree val;
+
+	    FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
+	      if (TREE_CODE (val) != INTEGER_CST
+		  && TREE_CODE (val) != REAL_CST
+		  && TREE_CODE (val) != FIXED_CST)
+		return NULL_TREE;
+
+	    return build_vector_from_ctor (TREE_TYPE (rhs),
+					   CONSTRUCTOR_ELTS (rhs));
+	  }
+
+	else if (DECL_P (rhs))
+	  return get_symbol_constant_value (rhs);
+
         /* If we couldn't fold the RHS, hand over to the generic
            fold routines.  */
         if (result == NULL_TREE)
           result = fold (rhs);
 
         /* Strip away useless type conversions.  Both the NON_LVALUE_EXPR
-           that may have been added by fold, and "useless" type 
+           that may have been added by fold, and "useless" type
            conversions that might now be apparent due to propagation.  */
         STRIP_USELESS_TYPE_CONVERSION (result);
 
         if (result != rhs && valid_gimple_rhs_p (result))
 	  return result;
-        else
-          /* It is possible that fold_stmt_r simplified the RHS.
-             Make sure that the subcode of this statement still
-             reflects the principal operator of the rhs operand. */
-          return rhs;
+
+	return NULL_TREE;
       }
       break;
 
@@ -2663,7 +2787,7 @@
       {
 	tree rhs = gimple_assign_rhs1 (stmt);
 
-	result = fold_unary (subcode, gimple_expr_type (stmt), rhs);
+	result = fold_unary_loc (loc, subcode, gimple_expr_type (stmt), rhs);
 	if (result)
 	  {
 	    /* If the operation was a conversion do _not_ mark a
@@ -2685,7 +2809,8 @@
 		 && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (stmt))))
 	  {
 	    tree type = gimple_expr_type (stmt);
-	    tree t = maybe_fold_offset_to_address (gimple_assign_rhs1 (stmt),
+	    tree t = maybe_fold_offset_to_address (loc,
+						   gimple_assign_rhs1 (stmt),
 						   integer_zero_node, type);
 	    if (t)
 	      return t;
@@ -2705,13 +2830,14 @@
 		    (TREE_TYPE (gimple_assign_lhs (stmt)), type))
 		type = TREE_TYPE (gimple_assign_rhs1 (stmt));
 	    }
-	  result = maybe_fold_stmt_addition (type,
+	  result = maybe_fold_stmt_addition (gimple_location (stmt),
+					     type,
 					     gimple_assign_rhs1 (stmt),
 					     gimple_assign_rhs2 (stmt));
 	}
 
       if (!result)
-        result = fold_binary (subcode,
+        result = fold_binary_loc (loc, subcode,
                               TREE_TYPE (gimple_assign_lhs (stmt)),
                               gimple_assign_rhs1 (stmt),
                               gimple_assign_rhs2 (stmt));
@@ -2750,7 +2876,8 @@
 static bool
 fold_gimple_cond (gimple stmt)
 {
-  tree result = fold_binary (gimple_cond_code (stmt),
+  tree result = fold_binary_loc (gimple_location (stmt),
+			     gimple_cond_code (stmt),
                              boolean_type_node,
                              gimple_cond_lhs (stmt),
                              gimple_cond_rhs (stmt));
@@ -2768,6 +2895,7 @@
   return false;
 }
 
+static void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
 
 /* Attempt to fold a call statement referenced by the statement iterator GSI.
    The statement may be replaced by another statement, e.g., if the call
@@ -2788,7 +2916,11 @@
       tree result = ccp_fold_builtin (stmt);
 
       if (result)
-        return update_call_from_tree (gsi, result);
+	{
+          if (!update_call_from_tree (gsi, result))
+	    gimplify_and_update_call_from_tree (gsi, result);
+	  return true;
+	}
     }
   else
     {
@@ -2826,125 +2958,130 @@
   return false;
 }
 
-/* Fold the statement pointed to by GSI.  In some cases, this function may
-   replace the whole statement with a new one.  Returns true iff folding
-   makes any changes.  */
-
-bool
-fold_stmt (gimple_stmt_iterator *gsi)
+/* Worker for both fold_stmt and fold_stmt_inplace.  The INPLACE argument
+   distinguishes both cases.  */
+
+static bool
+fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
 {
-  tree res;
-  struct fold_stmt_r_data fold_stmt_r_data;
-  struct walk_stmt_info wi;
-
   bool changed = false;
-  bool inside_addr_expr = false;
-
   gimple stmt = gsi_stmt (*gsi);
-
-  fold_stmt_r_data.stmt = stmt;
-  fold_stmt_r_data.changed_p = &changed;
-  fold_stmt_r_data.inside_addr_expr_p = &inside_addr_expr;
-
-  memset (&wi, 0, sizeof (wi));
-  wi.info = &fold_stmt_r_data;
-
-  /* Fold the individual operands.
-     For example, fold instances of *&VAR into VAR, etc.  */
-  res = walk_gimple_op (stmt, fold_stmt_r, &wi);
-  gcc_assert (!res);
+  unsigned i;
 
   /* Fold the main computation performed by the statement.  */
   switch (gimple_code (stmt))
     {
     case GIMPLE_ASSIGN:
       {
+	unsigned old_num_ops = gimple_num_ops (stmt);
 	tree new_rhs = fold_gimple_assign (gsi);
-	if (new_rhs != NULL_TREE)
+	if (new_rhs != NULL_TREE
+	    && (!inplace
+		|| get_gimple_rhs_num_ops (TREE_CODE (new_rhs)) < old_num_ops))
 	  {
 	    gimple_assign_set_rhs_from_tree (gsi, new_rhs);
 	    changed = true;
 	  }
-	stmt = gsi_stmt (*gsi);
 	break;
       }
+
     case GIMPLE_COND:
       changed |= fold_gimple_cond (stmt);
       break;
+
     case GIMPLE_CALL:
+      /* Fold *& in call arguments.  */
+      for (i = 0; i < gimple_call_num_args (stmt); ++i)
+	if (REFERENCE_CLASS_P (gimple_call_arg (stmt, i)))
+	  {
+	    tree tmp = maybe_fold_reference (gimple_call_arg (stmt, i), false);
+	    if (tmp)
+	      {
+		gimple_call_set_arg (stmt, i, tmp);
+		changed = true;
+	      }
+	  }
       /* The entire statement may be replaced in this case.  */
-      changed |= fold_gimple_call (gsi);
+      if (!inplace)
+	changed |= fold_gimple_call (gsi);
       break;
 
-    default:
-      return changed;
+    case GIMPLE_ASM:
+      /* Fold *& in asm operands.  */
+      for (i = 0; i < gimple_asm_noutputs (stmt); ++i)
+	{
+	  tree link = gimple_asm_output_op (stmt, i);
+	  tree op = TREE_VALUE (link);
+	  if (REFERENCE_CLASS_P (op)
+	      && (op = maybe_fold_reference (op, true)) != NULL_TREE)
+	    {
+	      TREE_VALUE (link) = op;
+	      changed = true;
+	    }
+	}
+      for (i = 0; i < gimple_asm_ninputs (stmt); ++i)
+	{
+	  tree link = gimple_asm_input_op (stmt, i);
+	  tree op = TREE_VALUE (link);
+	  if (REFERENCE_CLASS_P (op)
+	      && (op = maybe_fold_reference (op, false)) != NULL_TREE)
+	    {
+	      TREE_VALUE (link) = op;
+	      changed = true;
+	    }
+	}
       break;
+
+    default:;
+    }
+
+  stmt = gsi_stmt (*gsi);
+
+  /* Fold *& on the lhs.  */
+  if (gimple_has_lhs (stmt))
+    {
+      tree lhs = gimple_get_lhs (stmt);
+      if (lhs && REFERENCE_CLASS_P (lhs))
+	{
+	  tree new_lhs = maybe_fold_reference (lhs, true);
+	  if (new_lhs)
+	    {
+	      gimple_set_lhs (stmt, new_lhs);
+	      changed = true;
+	    }
+	}
     }
 
   return changed;
 }
 
+/* Fold the statement pointed to by GSI.  In some cases, this function may
+   replace the whole statement with a new one.  Returns true iff folding
+   makes any changes.
+   The statement pointed to by GSI should be in valid gimple form but may
+   be in unfolded state as resulting from for example constant propagation
+   which can produce *&x = 0.  */
+
+bool
+fold_stmt (gimple_stmt_iterator *gsi)
+{
+  return fold_stmt_1 (gsi, false);
+}
+
 /* Perform the minimal folding on statement STMT.  Only operations like
    *&x created by constant propagation are handled.  The statement cannot
    be replaced with a new one.  Return true if the statement was
-   changed, false otherwise.  */
+   changed, false otherwise.
+   The statement STMT should be in valid gimple form but may
+   be in unfolded state as resulting from for example constant propagation
+   which can produce *&x = 0.  */
 
 bool
 fold_stmt_inplace (gimple stmt)
 {
-  tree res;
-  struct fold_stmt_r_data fold_stmt_r_data;
-  struct walk_stmt_info wi;
-  gimple_stmt_iterator si;
-
-  bool changed = false;
-  bool inside_addr_expr = false;
-
-  fold_stmt_r_data.stmt = stmt;
-  fold_stmt_r_data.changed_p = &changed;
-  fold_stmt_r_data.inside_addr_expr_p = &inside_addr_expr;
-
-  memset (&wi, 0, sizeof (wi));
-  wi.info = &fold_stmt_r_data;
-
-  /* Fold the individual operands.
-     For example, fold instances of *&VAR into VAR, etc.
-
-     It appears that, at one time, maybe_fold_stmt_indirect
-     would cause the walk to return non-null in order to
-     signal that the entire statement should be replaced with
-     a call to _builtin_trap.  This functionality is currently
-     disabled, as noted in a FIXME, and cannot be supported here.  */
-  res = walk_gimple_op (stmt, fold_stmt_r, &wi);
-  gcc_assert (!res);
-
-  /* Fold the main computation performed by the statement.  */
-  switch (gimple_code (stmt))
-    {
-    case GIMPLE_ASSIGN:
-      {
-	unsigned old_num_ops;
-	tree new_rhs;
-	old_num_ops = gimple_num_ops (stmt);
-	si = gsi_for_stmt (stmt);
-	new_rhs = fold_gimple_assign (&si);
-	if (new_rhs != NULL_TREE
-	    && get_gimple_rhs_num_ops (TREE_CODE (new_rhs)) < old_num_ops)
-	  {
-	    gimple_assign_set_rhs_from_tree (&si, new_rhs);
-	    changed = true;
-	  }
-	gcc_assert (gsi_stmt (si) == stmt);
-	break;
-      }
-    case GIMPLE_COND:
-      changed |= fold_gimple_cond (stmt);
-      break;
-
-    default:
-      break;
-    }
-
+  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+  bool changed = fold_stmt_1 (&gsi, true);
+  gcc_assert (gsi_stmt (gsi) == stmt);
   return changed;
 }
 
@@ -2957,9 +3094,8 @@
 static tree
 optimize_stack_restore (gimple_stmt_iterator i)
 {
-  tree callee, rhs;
-  gimple stmt, stack_save;
-  gimple_stmt_iterator stack_save_gsi;
+  tree callee;
+  gimple stmt;
 
   basic_block bb = gsi_bb (i);
   gimple call = gsi_stmt (i);
@@ -2983,37 +3119,49 @@
 	return NULL_TREE;
 
       if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_RESTORE)
-	break;
+	goto second_stack_restore;
     }
 
-  if (gsi_end_p (i)
-      && (! single_succ_p (bb)
-	  || single_succ_edge (bb)->dest != EXIT_BLOCK_PTR))
-    return NULL_TREE;
-
-  stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
-  if (gimple_code (stack_save) != GIMPLE_CALL
-      || gimple_call_lhs (stack_save) != gimple_call_arg (call, 0)
-      || stmt_could_throw_p (stack_save)
-      || !has_single_use (gimple_call_arg (call, 0)))
+  if (!gsi_end_p (i))
     return NULL_TREE;
 
-  callee = gimple_call_fndecl (stack_save);
-  if (!callee
-      || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
-      || DECL_FUNCTION_CODE (callee) != BUILT_IN_STACK_SAVE
-      || gimple_call_num_args (stack_save) != 0)
-    return NULL_TREE;
-
-  stack_save_gsi = gsi_for_stmt (stack_save);
-  push_stmt_changes (gsi_stmt_ptr (&stack_save_gsi));
-  rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
-  if (!update_call_from_tree (&stack_save_gsi, rhs))
+  /* Allow one successor of the exit block, or zero successors.  */
+  switch (EDGE_COUNT (bb->succs))
     {
-      discard_stmt_changes (gsi_stmt_ptr (&stack_save_gsi));
+    case 0:
+      break;
+    case 1:
+      if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR)
+	return NULL_TREE;
+      break;
+    default:
       return NULL_TREE;
     }
-  pop_stmt_changes (gsi_stmt_ptr (&stack_save_gsi));
+ second_stack_restore:
+
+  /* If there's exactly one use, then zap the call to __builtin_stack_save.
+     If there are multiple uses, then the last one should remove the call.
+     In any case, whether the call to __builtin_stack_save can be removed
+     or not is irrelevant to removing the call to __builtin_stack_restore.  */
+  if (has_single_use (gimple_call_arg (call, 0)))
+    {
+      gimple stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
+      if (is_gimple_call (stack_save))
+	{
+	  callee = gimple_call_fndecl (stack_save);
+	  if (callee
+	      && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
+	      && DECL_FUNCTION_CODE (callee) == BUILT_IN_STACK_SAVE)
+	    {
+	      gimple_stmt_iterator stack_save_gsi;
+	      tree rhs;
+
+	      stack_save_gsi = gsi_for_stmt (stack_save);
+	      rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
+	      update_call_from_tree (&stack_save_gsi, rhs);
+	    }
+	}
+    }
 
   /* No effect, so the statement will be deleted.  */
   return integer_zero_node;
@@ -3029,6 +3177,7 @@
 {
   tree callee, lhs, rhs, cfun_va_list;
   bool va_list_simple_ptr;
+  location_t loc = gimple_location (call);
 
   if (gimple_code (call) != GIMPLE_CALL)
     return NULL_TREE;
@@ -3056,11 +3205,11 @@
 	  || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs)))
 	     != TYPE_MAIN_VARIANT (cfun_va_list))
 	return NULL_TREE;
-      
-      lhs = build_fold_indirect_ref (lhs);
-      rhs = build_call_expr (built_in_decls[BUILT_IN_NEXT_ARG],
+
+      lhs = build_fold_indirect_ref_loc (loc, lhs);
+      rhs = build_call_expr_loc (loc, built_in_decls[BUILT_IN_NEXT_ARG],
                              1, integer_zero_node);
-      rhs = fold_convert (TREE_TYPE (lhs), rhs);
+      rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
       return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
 
     case BUILT_IN_VA_COPY:
@@ -3076,13 +3225,13 @@
 	     != TYPE_MAIN_VARIANT (cfun_va_list))
 	return NULL_TREE;
 
-      lhs = build_fold_indirect_ref (lhs);
+      lhs = build_fold_indirect_ref_loc (loc, lhs);
       rhs = gimple_call_arg (call, 1);
       if (TYPE_MAIN_VARIANT (TREE_TYPE (rhs))
 	  != TYPE_MAIN_VARIANT (cfun_va_list))
 	return NULL_TREE;
 
-      rhs = fold_convert (TREE_TYPE (lhs), rhs);
+      rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
       return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
 
     case BUILT_IN_VA_END:
@@ -3122,7 +3271,7 @@
 
   if (lhs == NULL_TREE)
     gimplify_and_add (expr, &stmts);
-  else 
+  else
     tmp = get_initialized_tmp_var (expr, &stmts, NULL);
 
   pop_gimplify_context (NULL);
@@ -3141,11 +3290,16 @@
   }
 
   if (lhs == NULL_TREE)
-    new_stmt = gimple_build_nop ();
+    {
+      new_stmt = gimple_build_nop ();
+      unlink_stmt_vdef (stmt);
+      release_defs (stmt);
+    }
   else
     {
       new_stmt = gimple_build_assign (lhs, tmp);
-      copy_virtual_operands (new_stmt, stmt);
+      gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+      gimple_set_vdef (new_stmt, gimple_vdef (stmt));
       move_ssa_defining_stmt_for_defs (new_stmt, stmt);
     }
 
@@ -3162,7 +3316,7 @@
   bool cfg_changed = false;
   basic_block bb;
   unsigned int todoflags = 0;
-  
+
   FOR_EACH_BB (bb)
     {
       gimple_stmt_iterator i;
@@ -3230,16 +3384,14 @@
 	    }
 
           old_stmt = stmt;
-	  push_stmt_changes (gsi_stmt_ptr (&i));
-
           if (!update_call_from_tree (&i, result))
-            {
-              gimplify_and_update_call_from_tree (&i, result);
-              todoflags |= TODO_rebuild_alias;
-            }
+	    {
+	      gimplify_and_update_call_from_tree (&i, result);
+	      todoflags |= TODO_update_address_taken;
+	    }
 
 	  stmt = gsi_stmt (i);
-	  pop_stmt_changes (gsi_stmt_ptr (&i));
+	  update_stmt (stmt);
 
 	  if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt)
 	      && gimple_purge_dead_eh_edges (bb))
@@ -3266,16 +3418,16 @@
 	    gsi_next (&i);
 	}
     }
-  
+
   /* Delete unreachable blocks.  */
   if (cfg_changed)
     todoflags |= TODO_cleanup_cfg;
-  
+
   return todoflags;
 }
 
 
-struct gimple_opt_pass pass_fold_builtins = 
+struct gimple_opt_pass pass_fold_builtins =
 {
  {
   GIMPLE_PASS,
@@ -3285,7 +3437,7 @@
   NULL,					/* sub */
   NULL,					/* next */
   0,					/* static_pass_number */
-  0,					/* tv_id */
+  TV_NONE,				/* tv_id */
   PROP_cfg | PROP_ssa,			/* properties_required */
   0,					/* properties_provided */
   0,					/* properties_destroyed */