OSDN Git Service

2007-07-06 H.J. Lu <hongjiu.lu@intel.com>
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-ccp.c
index 8da29e4..272a4f8 100644 (file)
@@ -1,5 +1,5 @@
 /* Conditional constant propagation pass for the GNU compiler.
-   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
    Adapted from original RTL SSA-CCP by Daniel Berlin <dberlin@dberlin.org>
    Adapted to GIMPLE trees by Diego Novillo <dnovillo@redhat.com>
@@ -132,13 +132,12 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 
    We should be able to deduce that the predicate 'a.a != B' is always
    false.  To achieve this, we associate constant values to the SSA
-   names in the V_MAY_DEF and V_MUST_DEF operands for each store.
-   Additionally, since we also glob partial loads/stores with the base
-   symbol, we also keep track of the memory reference where the
-   constant value was stored (in the MEM_REF field of PROP_VALUE_T).
-   For instance,
+   names in the VDEF operands for each store.  Additionally,
+   since we also glob partial loads/stores with the base symbol, we
+   also keep track of the memory reference where the constant value
+   was stored (in the MEM_REF field of PROP_VALUE_T).  For instance,
 
-        # a_5 = V_MAY_DEF <a_4>
+        # a_5 = VDEF <a_4>
         a.a = 2;
 
         # VUSE <a_5>
@@ -208,6 +207,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "tree-ssa-propagate.h"
 #include "langhooks.h"
 #include "target.h"
+#include "toplev.h"
 
 
 /* Possible lattice values.  */
@@ -222,9 +222,9 @@ typedef enum
 /* Array of propagated constant values.  After propagation,
    CONST_VAL[I].VALUE holds the constant value for SSA_NAME(I).  If
    the constant is held in an SSA name representing a memory store
-   (i.e., a V_MAY_DEF or V_MUST_DEF), CONST_VAL[I].MEM_REF will
-   contain the actual memory reference used to store (i.e., the LHS of
-   the assignment doing the store).  */
+   (i.e., a VDEF), CONST_VAL[I].MEM_REF will contain the actual
+   memory reference used to store (i.e., the LHS of the assignment
+   doing the store).  */
 static prop_value_t *const_val;
 
 /* True if we are also propagating constants in stores and loads.  */
@@ -375,7 +375,7 @@ get_default_value (tree var)
          else
            val.lattice_val = VARYING;
        }
-      else if (TREE_CODE (stmt) == MODIFY_EXPR
+      else if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
               || TREE_CODE (stmt) == PHI_NODE)
        {
          /* Any other variable defined by an assignment or a PHI node
@@ -543,7 +543,7 @@ likely_value (tree stmt)
 
   /* Anything other than assignments and conditional jumps are not
      interesting for CCP.  */
-  if (TREE_CODE (stmt) != MODIFY_EXPR
+  if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT
       && !(TREE_CODE (stmt) == RETURN_EXPR && get_rhs (stmt) != NULL_TREE)
       && TREE_CODE (stmt) != COND_EXPR
       && TREE_CODE (stmt) != SWITCH_EXPR)
@@ -601,7 +601,7 @@ surely_varying_stmt_p (tree stmt)
 
   /* Anything other than assignments and conditional jumps are not
      interesting for CCP.  */
-  if (TREE_CODE (stmt) != MODIFY_EXPR
+  if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT
       && !(TREE_CODE (stmt) == RETURN_EXPR && get_rhs (stmt) != NULL_TREE)
       && TREE_CODE (stmt) != COND_EXPR
       && TREE_CODE (stmt) != SWITCH_EXPR)
@@ -666,15 +666,18 @@ ccp_initialize (void)
 
 
 /* Do final substitution of propagated values, cleanup the flowgraph and
-   free allocated storage.  */
+   free allocated storage.  
 
-static void
+   Return TRUE when something was optimized.  */
+
+static bool
 ccp_finalize (void)
 {
   /* Perform substitutions based on the known constant values.  */
-  substitute_and_fold (const_val, false);
+  bool something_changed = substitute_and_fold (const_val, false);
 
   free (const_val);
+  return something_changed;;
 }
 
 
@@ -896,8 +899,7 @@ ccp_fold (tree stmt)
        }
 
       if ((code == NOP_EXPR || code == CONVERT_EXPR)
-         && tree_ssa_useless_type_conversion_1 (TREE_TYPE (rhs),
-                                                TREE_TYPE (op0)))
+         && useless_type_conversion_p (TREE_TYPE (rhs), TREE_TYPE (op0)))
        return op0;
       return fold_unary (code, TREE_TYPE (rhs), op0);
     }
@@ -936,15 +938,13 @@ ccp_fold (tree stmt)
   /* We may be able to fold away calls to builtin functions if their
      arguments are constants.  */
   else if (code == CALL_EXPR
-          && TREE_CODE (TREE_OPERAND (rhs, 0)) == ADDR_EXPR
-          && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs, 0), 0))
-              == FUNCTION_DECL)
-          && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (rhs, 0), 0)))
+          && TREE_CODE (CALL_EXPR_FN (rhs)) == ADDR_EXPR
+          && TREE_CODE (TREE_OPERAND (CALL_EXPR_FN (rhs), 0)) == FUNCTION_DECL
+          && DECL_BUILT_IN (TREE_OPERAND (CALL_EXPR_FN (rhs), 0)))
     {
       if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_USE))
        {
          tree *orig, var;
-         tree fndecl, arglist;
          size_t i = 0;
          ssa_op_iter iter;
          use_operand_p var_p;
@@ -956,9 +956,7 @@ ccp_fold (tree stmt)
 
          /* Substitute operands with their values and try to fold.  */
          replace_uses_in (stmt, NULL, const_val);
-         fndecl = get_callee_fndecl (rhs);
-         arglist = TREE_OPERAND (rhs, 1);
-         retval = fold_builtin (fndecl, arglist, false);
+         retval = fold_call_expr (rhs, false);
 
          /* Restore operands to their original form.  */
          i = 0;
@@ -1130,9 +1128,12 @@ evaluate_stmt (tree stmt)
   prop_value_t val;
   tree simplified = NULL_TREE;
   ccp_lattice_t likelyvalue = likely_value (stmt);
+  bool is_constant;
 
   val.mem_ref = NULL_TREE;
 
+  fold_defer_overflow_warnings ();
+
   /* If the statement is likely to have a CONSTANT result, then try
      to fold the statement to determine the constant value.  */
   if (likelyvalue == CONSTANT)
@@ -1149,7 +1150,11 @@ evaluate_stmt (tree stmt)
   else if (!simplified)
     simplified = fold_const_aggregate_ref (get_rhs (stmt));
 
-  if (simplified && is_gimple_min_invariant (simplified))
+  is_constant = simplified && is_gimple_min_invariant (simplified);
+
+  fold_undefer_overflow_warnings (is_constant, stmt, 0);
+
+  if (is_constant)
     {
       /* The statement produced a constant value.  */
       val.lattice_val = CONSTANT;
@@ -1184,8 +1189,8 @@ visit_assignment (tree stmt, tree *output_p)
   tree lhs, rhs;
   enum ssa_prop_result retval;
 
-  lhs = TREE_OPERAND (stmt, 0);
-  rhs = TREE_OPERAND (stmt, 1);
+  lhs = GIMPLE_STMT_OPERAND (stmt, 0);
+  rhs = GIMPLE_STMT_OPERAND (stmt, 1);
 
   if (TREE_CODE (rhs) == SSA_NAME)
     {
@@ -1219,7 +1224,7 @@ visit_assignment (tree stmt, tree *output_p)
      the constant value into the type of the destination variable.  This
      should not be necessary if GCC represented bitfields properly.  */
   {
-    tree orig_lhs = TREE_OPERAND (stmt, 0);
+    tree orig_lhs = GIMPLE_STMT_OPERAND (stmt, 0);
 
     if (TREE_CODE (orig_lhs) == VIEW_CONVERT_EXPR
        && val.lattice_val == CONSTANT)
@@ -1274,9 +1279,9 @@ visit_assignment (tree stmt, tree *output_p)
     }
   else if (do_store_ccp && stmt_makes_single_store (stmt))
     {
-      /* Otherwise, set the names in V_MAY_DEF/V_MUST_DEF operands
-        to the new constant value and mark the LHS as the memory
-        reference associated with VAL.  */
+      /* Otherwise, set the names in VDEF operands to the new
+        constant value and mark the LHS as the memory reference
+        associated with VAL.  */
       ssa_op_iter i;
       tree vdef;
       bool changed;
@@ -1364,7 +1369,7 @@ ccp_visit_stmt (tree stmt, edge *taken_edge_p, tree *output_p)
       fprintf (dump_file, "\n");
     }
 
-  if (TREE_CODE (stmt) == MODIFY_EXPR)
+  if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
     {
       /* If the statement is an assignment that produces a single
         output value, evaluate its RHS to see if the lattice value of
@@ -1398,21 +1403,23 @@ ccp_visit_stmt (tree stmt, edge *taken_edge_p, tree *output_p)
 
 /* Main entry point for SSA Conditional Constant Propagation.  */
 
-static void
+static unsigned int
 execute_ssa_ccp (bool store_ccp)
 {
   do_store_ccp = store_ccp;
   ccp_initialize ();
   ssa_propagate (ccp_visit_stmt, ccp_visit_phi_node);
-  ccp_finalize ();
+  if (ccp_finalize ())
+    return (TODO_cleanup_cfg | TODO_update_ssa | TODO_remove_unused_locals);
+  else
+    return 0;
 }
 
 
 static unsigned int
 do_ssa_ccp (void)
 {
-  execute_ssa_ccp (false);
-  return 0;
+  return execute_ssa_ccp (false);
 }
 
 
@@ -1432,13 +1439,12 @@ struct tree_opt_pass pass_ccp =
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
   TV_TREE_CCP,                         /* tv_id */
-  PROP_cfg | PROP_ssa | PROP_alias,    /* properties_required */
+  PROP_cfg | PROP_ssa,                 /* properties_required */
   0,                                   /* properties_provided */
-  PROP_smt_usage,                      /* properties_destroyed */
+  0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_cleanup_cfg | TODO_dump_func | TODO_update_ssa
-    | TODO_ggc_collect | TODO_verify_ssa
-    | TODO_verify_stmts | TODO_update_smt_usage, /* todo_flags_finish */
+  TODO_dump_func | TODO_verify_ssa
+  | TODO_verify_stmts | TODO_ggc_collect,/* todo_flags_finish */
   0                                    /* letter */
 };
 
@@ -1447,8 +1453,7 @@ static unsigned int
 do_ssa_store_ccp (void)
 {
   /* If STORE-CCP is not enabled, we just run regular CCP.  */
-  execute_ssa_ccp (flag_tree_store_ccp != 0);
-  return 0;
+  return execute_ssa_ccp (flag_tree_store_ccp != 0);
 }
 
 static bool
@@ -1472,12 +1477,10 @@ struct tree_opt_pass pass_store_ccp =
   TV_TREE_STORE_CCP,                   /* tv_id */
   PROP_cfg | PROP_ssa | PROP_alias,    /* properties_required */
   0,                                   /* properties_provided */
-  PROP_smt_usage,                      /* properties_destroyed */
+  0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func | TODO_update_ssa
-    | TODO_ggc_collect | TODO_verify_ssa
-    | TODO_cleanup_cfg
-    | TODO_verify_stmts | TODO_update_smt_usage, /* todo_flags_finish */
+  TODO_dump_func | TODO_verify_ssa
+  | TODO_verify_stmts | TODO_ggc_collect,/* todo_flags_finish */
   0                                    /* letter */
 };
 
@@ -1546,7 +1549,7 @@ widen_bitfield (tree val, tree field, tree var)
 static tree
 maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type)
 {
-  tree min_idx, idx, elt_offset = integer_zero_node;
+  tree min_idx, idx, idx_type, elt_offset = integer_zero_node;
   tree array_type, elt_type, elt_size;
 
   /* If BASE is an ARRAY_REF, we can pick up another offset (this time
@@ -1572,55 +1575,65 @@ maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type)
   if (TREE_CODE (array_type) != ARRAY_TYPE)
     return NULL_TREE;
   elt_type = TREE_TYPE (array_type);
-  if (!lang_hooks.types_compatible_p (orig_type, elt_type))
+  if (!useless_type_conversion_p (orig_type, elt_type))
     return NULL_TREE;
-       
+
+  /* Use signed size type for intermediate computation on the index.  */
+  idx_type = signed_type_for (size_type_node);
+
   /* If OFFSET and ELT_OFFSET are zero, we don't care about the size of the
      element type (so we can use the alignment if it's not constant).
      Otherwise, compute the offset as an index by using a division.  If the
      division isn't exact, then don't do anything.  */
   elt_size = TYPE_SIZE_UNIT (elt_type);
+  if (!elt_size)
+    return NULL;
   if (integer_zerop (offset))
     {
       if (TREE_CODE (elt_size) != INTEGER_CST)
        elt_size = size_int (TYPE_ALIGN (elt_type));
 
-      idx = integer_zero_node;
+      idx = build_int_cst (idx_type, 0);
     }
   else
     {
       unsigned HOST_WIDE_INT lquo, lrem;
       HOST_WIDE_INT hquo, hrem;
+      double_int soffset;
 
+      /* The final array offset should be signed, so we need
+        to sign-extend the (possibly pointer) offset here
+        and use signed division.  */
+      soffset = double_int_sext (tree_to_double_int (offset),
+                                TYPE_PRECISION (TREE_TYPE (offset)));
       if (TREE_CODE (elt_size) != INTEGER_CST
-         || div_and_round_double (TRUNC_DIV_EXPR, 1,
-                                  TREE_INT_CST_LOW (offset),
-                                  TREE_INT_CST_HIGH (offset),
+         || div_and_round_double (TRUNC_DIV_EXPR, 0,
+                                  soffset.low, soffset.high,
                                   TREE_INT_CST_LOW (elt_size),
                                   TREE_INT_CST_HIGH (elt_size),
                                   &lquo, &hquo, &lrem, &hrem)
          || lrem || hrem)
        return NULL_TREE;
 
-      idx = build_int_cst_wide (NULL_TREE, lquo, hquo);
+      idx = build_int_cst_wide (idx_type, lquo, hquo);
     }
 
   /* Assume the low bound is zero.  If there is a domain type, get the
      low bound, if any, convert the index into that type, and add the
      low bound.  */
-  min_idx = integer_zero_node;
+  min_idx = build_int_cst (idx_type, 0);
   if (TYPE_DOMAIN (array_type))
     {
-      if (TYPE_MIN_VALUE (TYPE_DOMAIN (array_type)))
-       min_idx = TYPE_MIN_VALUE (TYPE_DOMAIN (array_type));
+      idx_type = TYPE_DOMAIN (array_type);
+      if (TYPE_MIN_VALUE (idx_type))
+       min_idx = TYPE_MIN_VALUE (idx_type);
       else
-       min_idx = fold_convert (TYPE_DOMAIN (array_type), min_idx);
+       min_idx = fold_convert (idx_type, min_idx);
 
       if (TREE_CODE (min_idx) != INTEGER_CST)
        return NULL_TREE;
 
-      idx = fold_convert (TYPE_DOMAIN (array_type), idx);
-      elt_offset = fold_convert (TYPE_DOMAIN (array_type), elt_offset);
+      elt_offset = fold_convert (idx_type, elt_offset);
     }
 
   if (!integer_zerop (min_idx))
@@ -1628,22 +1641,24 @@ maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type)
   if (!integer_zerop (elt_offset))
     idx = int_const_binop (PLUS_EXPR, idx, elt_offset, 0);
 
-  return build4 (ARRAY_REF, orig_type, base, idx, min_idx,
-                size_int (tree_low_cst (elt_size, 1)
-                          / (TYPE_ALIGN_UNIT (elt_type))));
+  /* Make sure to possibly truncate late after offsetting.  */
+  idx = fold_convert (idx_type, idx);
+
+  return build4 (ARRAY_REF, orig_type, base, idx, NULL_TREE, NULL_TREE);
 }
 
 
-/* A subroutine of fold_stmt_r.  Attempts to fold *(S+O) to S.X.
+/* 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.  */
-/* ??? This doesn't handle class inheritance.  */
 
 static tree
 maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
                                    tree orig_type, bool base_is_ptr)
 {
   tree f, t, field_type, tail_array_field, field_offset;
+  tree ret;
+  tree new_base;
 
   if (TREE_CODE (record_type) != RECORD_TYPE
       && TREE_CODE (record_type) != UNION_TYPE
@@ -1651,7 +1666,7 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
     return NULL_TREE;
 
   /* Short-circuit silly cases.  */
-  if (lang_hooks.types_compatible_p (record_type, orig_type))
+  if (useless_type_conversion_p (record_type, orig_type))
     return NULL_TREE;
 
   tail_array_field = NULL_TREE;
@@ -1664,6 +1679,8 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
       if (DECL_BIT_FIELD (f))
        continue;
 
+      if (!DECL_FIELD_OFFSET (f))
+       continue;
       field_offset = byte_position (f);
       if (TREE_CODE (field_offset) != INTEGER_CST)
        continue;
@@ -1687,7 +1704,7 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
       /* Here we exactly match the offset being checked.  If the types match,
         then we can return that field.  */
       if (cmp == 0
-         && lang_hooks.types_compatible_p (orig_type, field_type))
+         && useless_type_conversion_p (orig_type, field_type))
        {
          if (base_is_ptr)
            base = build1 (INDIRECT_REF, record_type, base);
@@ -1715,8 +1732,20 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
 
       /* If we matched, then set offset to the displacement into
         this field.  */
-      offset = t;
-      goto found;
+      if (base_is_ptr)
+       new_base = build1 (INDIRECT_REF, record_type, base);
+      else
+       new_base = base;
+      new_base = build3 (COMPONENT_REF, field_type, new_base, f, NULL_TREE);
+
+      /* Recurse to possibly find the match.  */
+      ret = maybe_fold_offset_to_array_ref (new_base, t, orig_type);
+      if (ret)
+       return ret;
+      ret = maybe_fold_offset_to_component_ref (field_type, new_base, t,
+                                               orig_type, false);
+      if (ret)
+       return ret;
     }
 
   if (!tail_array_field)
@@ -1726,7 +1755,6 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
   field_type = TREE_TYPE (f);
   offset = int_const_binop (MINUS_EXPR, offset, byte_position (f), 1);
 
- found:
   /* If we get here, we've got an aggregate field, and a possibly 
      nonzero offset into them.  Recurse and hope for a valid match.  */
   if (base_is_ptr)
@@ -1740,6 +1768,69 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
                                             orig_type, false);
 }
 
+/* Attempt to express (ORIG_TYPE)BASE+OFFSET as BASE->field_of_orig_type
+   or BASE[index] or by combination of those. 
+
+   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)
+{
+  tree ret;
+  tree type;
+  bool base_is_ptr = true;
+
+  STRIP_NOPS (base);
+  if (TREE_CODE (base) == ADDR_EXPR)
+    {
+      base_is_ptr = false;
+
+      base = TREE_OPERAND (base, 0);
+
+      /* Handle case where existing COMPONENT_REF pick e.g. wrong field of union,
+        so it needs to be removed and new COMPONENT_REF constructed.
+        The wrong COMPONENT_REF are often constructed by folding the
+        (type *)&object within the expression (type *)&object+offset  */
+      if (handled_component_p (base) && 0)
+       {
+          HOST_WIDE_INT sub_offset, size, maxsize;
+         tree newbase;
+         newbase = get_ref_base_and_extent (base, &sub_offset,
+                                            &size, &maxsize);
+         gcc_assert (newbase);
+         gcc_assert (!(sub_offset & (BITS_PER_UNIT - 1)));
+         if (size == maxsize)
+           {
+             base = newbase;
+             if (sub_offset)
+               offset = int_const_binop (PLUS_EXPR, offset,
+                                         build_int_cst (TREE_TYPE (offset),
+                                         sub_offset / BITS_PER_UNIT), 1);
+           }
+       }
+      if (useless_type_conversion_p (orig_type, TREE_TYPE (base))
+         && integer_zerop (offset))
+       return base;
+      type = TREE_TYPE (base);
+    }
+  else
+    {
+      base_is_ptr = true;
+      if (!POINTER_TYPE_P (TREE_TYPE (base)))
+       return NULL_TREE;
+      type = TREE_TYPE (TREE_TYPE (base));
+    }
+  ret = maybe_fold_offset_to_component_ref (type, base, offset,
+                                           orig_type, base_is_ptr);
+  if (!ret)
+    {
+      if (base_is_ptr)
+       base = build1 (INDIRECT_REF, type, base);
+      ret = maybe_fold_offset_to_array_ref (base, offset, orig_type);
+    }
+  return ret;
+}
 
 /* A subroutine of fold_stmt_r.  Attempt to simplify *(BASE+OFFSET).
    Return the simplified expression, or NULL if nothing could be done.  */
@@ -1761,8 +1852,8 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
   if (t)
     return t;
 
-  /* Add in any offset from a PLUS_EXPR.  */
-  if (TREE_CODE (base) == PLUS_EXPR)
+  /* Add in any offset from a POINTER_PLUS_EXPR.  */
+  if (TREE_CODE (base) == POINTER_PLUS_EXPR)
     {
       tree offset2;
 
@@ -1771,11 +1862,14 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
        return NULL_TREE;
       base = TREE_OPERAND (base, 0);
 
-      offset = int_const_binop (PLUS_EXPR, offset, offset2, 1);
+      offset = fold_convert (sizetype,
+                            int_const_binop (PLUS_EXPR, offset, offset2, 1));
     }
 
   if (TREE_CODE (base) == ADDR_EXPR)
     {
+      tree base_addr = base;
+
       /* Strip the ADDR_EXPR.  */
       base = TREE_OPERAND (base, 0);
 
@@ -1784,24 +1878,11 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
          && ccp_decl_initial_min_invariant (DECL_INITIAL (base)))
        return DECL_INITIAL (base);
 
-      /* Try folding *(&B+O) to B[X].  */
-      t = maybe_fold_offset_to_array_ref (base, offset, TREE_TYPE (expr));
-      if (t)
-       return t;
-
       /* Try folding *(&B+O) to B.X.  */
-      t = maybe_fold_offset_to_component_ref (TREE_TYPE (base), base, offset,
-                                             TREE_TYPE (expr), false);
+      t = maybe_fold_offset_to_reference (base_addr, offset,
+                                         TREE_TYPE (expr));
       if (t)
        return t;
-
-      /* Fold *&B to B.  We can only do this if EXPR is the same type
-        as BASE.  We can't do this if EXPR is the element type of an array
-        and BASE is the array.  */
-      if (integer_zerop (offset)
-         && lang_hooks.types_compatible_p (TREE_TYPE (base),
-                                           TREE_TYPE (expr)))
-       return base;
     }
   else
     {
@@ -1830,9 +1911,8 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
       /* Try folding *(B+O) to B->X.  Still an improvement.  */
       if (POINTER_TYPE_P (TREE_TYPE (base)))
        {
-          t = maybe_fold_offset_to_component_ref (TREE_TYPE (TREE_TYPE (base)),
-                                                 base, offset,
-                                                 TREE_TYPE (expr), true);
+          t = maybe_fold_offset_to_reference (base, offset,
+                                             TREE_TYPE (expr));
          if (t)
            return t;
        }
@@ -1843,7 +1923,7 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
 }
 
 
-/* A subroutine of fold_stmt_r.  EXPR is a PLUS_EXPR.
+/* A subroutine of fold_stmt_r.  EXPR is a POINTER_PLUS_EXPR.
 
    A quaint feature extant in our address arithmetic is that there
    can be hidden type changes here.  The type of the result need
@@ -1852,7 +1932,7 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset)
    What we're after here is an expression of the form
        (T *)(&array + const)
    where the cast doesn't actually exist, but is implicit in the
-   type of the PLUS_EXPR.  We'd like to turn this into
+   type of the POINTER_PLUS_EXPR.  We'd like to turn this into
        &array[x]
    which may be able to propagate further.  */
 
@@ -1864,18 +1944,9 @@ maybe_fold_stmt_addition (tree expr)
   tree ptr_type = TREE_TYPE (expr);
   tree ptd_type;
   tree t;
-  bool subtract = (TREE_CODE (expr) == MINUS_EXPR);
 
-  /* We're only interested in pointer arithmetic.  */
-  if (!POINTER_TYPE_P (ptr_type))
-    return NULL_TREE;
-  /* Canonicalize the integral operand to op1.  */
-  if (INTEGRAL_TYPE_P (TREE_TYPE (op0)))
-    {
-      if (subtract)
-       return NULL_TREE;
-      t = op0, op0 = op1, op1 = t;
-    }
+  gcc_assert (TREE_CODE (expr) == POINTER_PLUS_EXPR);
+
   /* It had better be a constant.  */
   if (TREE_CODE (op1) != INTEGER_CST)
     return NULL_TREE;
@@ -1921,32 +1992,11 @@ maybe_fold_stmt_addition (tree expr)
       array_idx = int_const_binop (MULT_EXPR, array_idx, elt_size, 0);
 
       /* Update the operands for the next round, or for folding.  */
-      /* If we're manipulating unsigned types, then folding into negative
-        values can produce incorrect results.  Particularly if the type
-        is smaller than the width of the pointer.  */
-      if (subtract
-         && TYPE_UNSIGNED (TREE_TYPE (op1))
-         && tree_int_cst_lt (array_idx, op1))
-       return NULL;
-      op1 = int_const_binop (subtract ? MINUS_EXPR : PLUS_EXPR,
+      op1 = int_const_binop (PLUS_EXPR,
                             array_idx, op1, 0);
-      subtract = false;
       op0 = array_obj;
     }
 
-  /* If we weren't able to fold the subtraction into another array reference,
-     canonicalize the integer for passing to the array and component ref
-     simplification functions.  */
-  if (subtract)
-    {
-      if (TYPE_UNSIGNED (TREE_TYPE (op1)))
-       return NULL;
-      op1 = fold_unary (NEGATE_EXPR, TREE_TYPE (op1), op1);
-      /* ??? In theory fold should always produce another integer.  */
-      if (op1 == NULL || TREE_CODE (op1) != INTEGER_CST)
-       return NULL;
-    }
-
   ptd_type = TREE_TYPE (ptr_type);
 
   /* At which point we can try some of the same things as for indirects.  */
@@ -1965,8 +2015,9 @@ maybe_fold_stmt_addition (tree expr)
 
 struct fold_stmt_r_data
 {
-    bool *changed_p;
-    bool *inside_addr_expr_p;
+  tree stmt;
+  bool *changed_p;
+  bool *inside_addr_expr_p;
 };
 
 /* Subroutine of fold_stmt called via walk_tree.  We perform several
@@ -1993,6 +2044,21 @@ fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data)
                                    integer_zero_node);
       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_OPERAND (expr, 0)))
+         && (t = maybe_fold_offset_to_reference
+                     (TREE_OPERAND (expr, 0),
+                      integer_zero_node,
+                      TREE_TYPE (TREE_TYPE (expr)))))
+        t = build_fold_addr_expr_with_type (t, TREE_TYPE (expr));
+      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.
@@ -2020,8 +2086,7 @@ fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data)
         recompute_tree_invariant_for_addr_expr (expr);
       return NULL_TREE;
 
-    case PLUS_EXPR:
-    case MINUS_EXPR:
+    case POINTER_PLUS_EXPR:
       t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL);
       if (t)
        return t;
@@ -2062,10 +2127,16 @@ fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data)
       if (COMPARISON_CLASS_P (TREE_OPERAND (expr, 0)))
         {
          tree op0 = TREE_OPERAND (expr, 0);
-          tree tem = fold_binary (TREE_CODE (op0), TREE_TYPE (op0),
-                                 TREE_OPERAND (op0, 0),
-                                 TREE_OPERAND (op0, 1));
-         if (tem && set_rhs (expr_p, tem))
+          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));
+         set = tem && set_rhs (expr_p, tem);
+         fold_undefer_overflow_warnings (set, fold_stmt_r_data->stmt, 0);
+         if (set)
            {
              t = *expr_p;
              break;
@@ -2103,6 +2174,10 @@ get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
   
   if (TREE_CODE (arg) != SSA_NAME)
     {
+      if (TREE_CODE (arg) == COND_EXPR)
+        return get_maxval_strlen (COND_EXPR_THEN (arg), length, visited, type)
+               && get_maxval_strlen (COND_EXPR_ELSE (arg), length, visited, type);
+
       if (type == 2)
        {
          val = arg;
@@ -2145,14 +2220,14 @@ get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
 
   switch (TREE_CODE (def_stmt))
     {
-      case MODIFY_EXPR:
+      case GIMPLE_MODIFY_STMT:
        {
          tree rhs;
 
          /* The RHS of the statement defining VAR must either have a
             constant length or come from another SSA_NAME with a constant
             length.  */
-         rhs = TREE_OPERAND (def_stmt, 1);
+         rhs = GIMPLE_STMT_OPERAND (def_stmt, 1);
          STRIP_NOPS (rhs);
          return get_maxval_strlen (rhs, length, visited, type);
        }
@@ -2199,18 +2274,18 @@ static tree
 ccp_fold_builtin (tree stmt, tree fn)
 {
   tree result, val[3];
-  tree callee, arglist, a;
+  tree callee, a;
   int arg_mask, i, type;
   bitmap visited;
   bool ignore;
+  call_expr_arg_iterator iter;
+  int nargs;
 
-  ignore = TREE_CODE (stmt) != MODIFY_EXPR;
+  ignore = TREE_CODE (stmt) != GIMPLE_MODIFY_STMT;
 
   /* First try the generic builtin folder.  If that succeeds, return the
      result directly.  */
-  callee = get_callee_fndecl (fn);
-  arglist = TREE_OPERAND (fn, 1);
-  result = fold_builtin (callee, arglist, ignore);
+  result = fold_call_expr (fn, ignore);
   if (result)
     {
       if (ignore)
@@ -2219,12 +2294,14 @@ ccp_fold_builtin (tree stmt, tree fn)
     }
 
   /* Ignore MD builtins.  */
+  callee = get_callee_fndecl (fn);
   if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD)
     return NULL_TREE;
 
   /* If the builtin could not be folded, and it has no argument list,
      we're done.  */
-  if (!arglist)
+  nargs = call_expr_nargs (fn);
+  if (nargs == 0)
     return NULL_TREE;
 
   /* Limit the work only for builtins we know how to simplify.  */
@@ -2267,15 +2344,17 @@ ccp_fold_builtin (tree stmt, tree fn)
   visited = BITMAP_ALLOC (NULL);
 
   memset (val, 0, sizeof (val));
-  for (i = 0, a = arglist;
-       arg_mask;
-       i++, arg_mask >>= 1, a = TREE_CHAIN (a))
-    if (arg_mask & 1)
-      {
-       bitmap_clear (visited);
-       if (!get_maxval_strlen (TREE_VALUE (a), &val[i], visited, type))
-         val[i] = NULL_TREE;
-      }
+  init_call_expr_arg_iterator (fn, &iter);
+  for (i = 0; arg_mask; i++, arg_mask >>= 1)
+    {
+      a = next_call_expr_arg (&iter);
+      if (arg_mask & 1)
+       {
+         bitmap_clear (visited);
+         if (!get_maxval_strlen (a, &val[i], visited, type))
+           val[i] = NULL_TREE;
+       }
+    }
 
   BITMAP_FREE (visited);
 
@@ -2285,36 +2364,45 @@ ccp_fold_builtin (tree stmt, tree fn)
     case BUILT_IN_STRLEN:
       if (val[0])
        {
-         tree new = fold_convert (TREE_TYPE (fn), val[0]);
+         tree new_val = fold_convert (TREE_TYPE (fn), val[0]);
 
          /* If the result is not a valid gimple value, or not a cast
             of a valid gimple value, then we can not use the result.  */
-         if (is_gimple_val (new)
-             || (is_gimple_cast (new)
-                 && is_gimple_val (TREE_OPERAND (new, 0))))
-           return new;
+         if (is_gimple_val (new_val)
+             || (is_gimple_cast (new_val)
+                 && is_gimple_val (TREE_OPERAND (new_val, 0))))
+           return new_val;
        }
       break;
 
     case BUILT_IN_STRCPY:
-      if (val[1] && is_gimple_val (val[1]))
-       result = fold_builtin_strcpy (callee, arglist, val[1]);
+      if (val[1] && is_gimple_val (val[1]) && nargs == 2)
+       result = fold_builtin_strcpy (callee,
+                                     CALL_EXPR_ARG (fn, 0),
+                                     CALL_EXPR_ARG (fn, 1),
+                                     val[1]);
       break;
 
     case BUILT_IN_STRNCPY:
-      if (val[1] && is_gimple_val (val[1]))
-       result = fold_builtin_strncpy (callee, arglist, val[1]);
+      if (val[1] && is_gimple_val (val[1]) && nargs == 3)
+       result = fold_builtin_strncpy (callee,
+                                      CALL_EXPR_ARG (fn, 0),
+                                      CALL_EXPR_ARG (fn, 1),
+                                      CALL_EXPR_ARG (fn, 2),
+                                      val[1]);
       break;
 
     case BUILT_IN_FPUTS:
-      result = fold_builtin_fputs (arglist,
-                                  TREE_CODE (stmt) != MODIFY_EXPR, 0,
+      result = fold_builtin_fputs (CALL_EXPR_ARG (fn, 0),
+                                  CALL_EXPR_ARG (fn, 1),
+                                  TREE_CODE (stmt) != GIMPLE_MODIFY_STMT, 0,
                                   val[0]);
       break;
 
     case BUILT_IN_FPUTS_UNLOCKED:
-      result = fold_builtin_fputs (arglist,
-                                  TREE_CODE (stmt) != MODIFY_EXPR, 1,
+      result = fold_builtin_fputs (CALL_EXPR_ARG (fn, 0),
+                                  CALL_EXPR_ARG (fn, 1),
+                                  TREE_CODE (stmt) != GIMPLE_MODIFY_STMT, 1,
                                   val[0]);
       break;
 
@@ -2323,26 +2411,39 @@ ccp_fold_builtin (tree stmt, tree fn)
     case BUILT_IN_MEMMOVE_CHK:
     case BUILT_IN_MEMSET_CHK:
       if (val[2] && is_gimple_val (val[2]))
-       result = fold_builtin_memory_chk (callee, arglist, val[2], ignore,
+       result = fold_builtin_memory_chk (callee,
+                                         CALL_EXPR_ARG (fn, 0),
+                                         CALL_EXPR_ARG (fn, 1),
+                                         CALL_EXPR_ARG (fn, 2),
+                                         CALL_EXPR_ARG (fn, 3),
+                                         val[2], ignore,
                                          DECL_FUNCTION_CODE (callee));
       break;
 
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
       if (val[1] && is_gimple_val (val[1]))
-       result = fold_builtin_stxcpy_chk (callee, arglist, val[1], ignore,
+       result = fold_builtin_stxcpy_chk (callee,
+                                         CALL_EXPR_ARG (fn, 0),
+                                         CALL_EXPR_ARG (fn, 1),
+                                         CALL_EXPR_ARG (fn, 2),
+                                         val[1], ignore,
                                          DECL_FUNCTION_CODE (callee));
       break;
 
     case BUILT_IN_STRNCPY_CHK:
       if (val[2] && is_gimple_val (val[2]))
-       result = fold_builtin_strncpy_chk (arglist, val[2]);
+       result = fold_builtin_strncpy_chk (CALL_EXPR_ARG (fn, 0),
+                                          CALL_EXPR_ARG (fn, 1),
+                                          CALL_EXPR_ARG (fn, 2),
+                                          CALL_EXPR_ARG (fn, 3),
+                                          val[2]);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
       if (val[1] && is_gimple_val (val[1]))
-       result = fold_builtin_snprintf_chk (arglist, val[1],
+       result = fold_builtin_snprintf_chk (fn, val[1],
                                            DECL_FUNCTION_CODE (callee));
       break;
 
@@ -2368,18 +2469,17 @@ fold_stmt (tree *stmt_p)
   bool changed = false;
   bool inside_addr_expr = false;
 
+  stmt = *stmt_p;
+
+  fold_stmt_r_data.stmt = stmt;
   fold_stmt_r_data.changed_p = &changed;
   fold_stmt_r_data.inside_addr_expr_p = &inside_addr_expr;
 
-  stmt = *stmt_p;
-
   /* If we replaced constants and the statement makes pointer dereferences,
      then we may need to fold instances of *&VAR into VAR, etc.  */
   if (walk_tree (stmt_p, fold_stmt_r, &fold_stmt_r_data, NULL))
     {
-      *stmt_p
-       = build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP],
-                                   NULL);
+      *stmt_p = build_call_expr (implicit_built_in_decls[BUILT_IN_TRAP], 0);
       return true;
     }
 
@@ -2407,8 +2507,8 @@ fold_stmt (tree *stmt_p)
             copying EH region info to the new node.  Easier to just do it
             here where we can just smash the call operand. Also
             CALL_EXPR_RETURN_SLOT_OPT needs to be handled correctly and
-            copied, fold_ternary does not have not information. */
-         callee = TREE_OPERAND (rhs, 0);
+            copied, fold_call_expr does not have not information. */
+         callee = CALL_EXPR_FN (rhs);
          if (TREE_CODE (callee) == OBJ_TYPE_REF
              && lang_hooks.fold_obj_type_ref
              && TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) == ADDR_EXPR
@@ -2426,12 +2526,19 @@ fold_stmt (tree *stmt_p)
              t = lang_hooks.fold_obj_type_ref (callee, t);
              if (t)
                {
-                 TREE_OPERAND (rhs, 0) = t;
+                 CALL_EXPR_FN (rhs) = t;
                  changed = true;
                }
            }
        }
     }
+  else 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));
+    }
 
   /* If we couldn't fold the RHS, hand over to the generic fold routines.  */
   if (result == NULL_TREE)
@@ -2460,6 +2567,7 @@ fold_stmt_inplace (tree stmt)
   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;
 
@@ -2512,7 +2620,7 @@ convert_to_gimple_builtin (block_stmt_iterator *si_p, tree expr, bool ignore)
       tree new_stmt = tsi_stmt (ti);
       find_new_referenced_vars (tsi_stmt_ptr (ti));
       bsi_insert_before (si_p, new_stmt, BSI_NEW_STMT);
-      mark_new_vars_to_rename (bsi_stmt (*si_p));
+      mark_symbols_for_renaming (new_stmt);
       bsi_next (si_p);
     }
 
@@ -2574,19 +2682,22 @@ execute_fold_all_builtins (void)
              print_generic_stmt (dump_file, *stmtp, dump_flags);
            }
 
+         push_stmt_changes (stmtp);
+
          if (!set_rhs (stmtp, result))
            {
              result = convert_to_gimple_builtin (&i, result,
                                                  TREE_CODE (old_stmt)
-                                                 != MODIFY_EXPR);
+                                                 != GIMPLE_MODIFY_STMT);
              if (result)
                {
                  bool ok = set_rhs (stmtp, result);
-                 
                  gcc_assert (ok);
                }
            }
-         mark_new_vars_to_rename (*stmtp);
+
+         pop_stmt_changes (stmtp);
+
          if (maybe_clean_or_replace_eh_stmt (old_stmt, *stmtp)
              && tree_purge_dead_eh_edges (bb))
            cfg_changed = true;
@@ -2615,9 +2726,7 @@ execute_fold_all_builtins (void)
     }
 
   /* Delete unreachable blocks.  */
-  if (cfg_changed)
-    cleanup_tree_cfg ();
-  return 0;
+  return cfg_changed ? TODO_cleanup_cfg : 0;
 }
 
 
@@ -2630,7 +2739,7 @@ struct tree_opt_pass pass_fold_builtins =
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
   0,                                   /* tv_id */
-  PROP_cfg | PROP_ssa | PROP_alias,    /* properties_required */
+  PROP_cfg | PROP_ssa,                 /* properties_required */
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */