re PR debug/66691 (ICE on valid code at -O3 with -g enabled in simplify_subreg, at...
[gcc.git] / gcc / gimple-fold.c
index 4e8de8235f8ec0437ec086c21e2a5dbb9776717c..ee9abe710cced8bd9252b1f81607da81b817edaf 100644 (file)
@@ -1,5 +1,5 @@
 /* Statement simplification on GIMPLE.
-   Copyright (C) 2010-2014 Free Software Foundation, Inc.
+   Copyright (C) 2010-2015 Free Software Foundation, Inc.
    Split out from tree-ssa-ccp.c.
 
 This file is part of GCC.
@@ -22,21 +22,34 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "alias.h"
+#include "symtab.h"
 #include "tree.h"
+#include "fold-const.h"
 #include "stringpool.h"
-#include "expr.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "rtl.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
 #include "stmt.h"
+#include "expr.h"
 #include "stor-layout.h"
-#include "flags.h"
-#include "function.h"
 #include "dumpfile.h"
 #include "bitmap.h"
+#include "predict.h"
+#include "dominance.h"
 #include "basic-block.h"
 #include "tree-ssa-alias.h"
 #include "internal-fn.h"
 #include "gimple-fold.h"
 #include "gimple-expr.h"
-#include "is-a.h"
 #include "gimple.h"
 #include "gimplify.h"
 #include "gimple-iterator.h"
@@ -47,6 +60,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa.h"
 #include "tree-ssa-propagate.h"
 #include "target.h"
+#include "cgraph.h"
 #include "ipa-utils.h"
 #include "gimple-pretty-print.h"
 #include "tree-ssa-address.h"
@@ -54,6 +68,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify-me.h"
 #include "dbgcnt.h"
 #include "builtins.h"
+#include "output.h"
+#include "tree-eh.h"
+#include "gimple-match.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -84,7 +103,7 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
   struct cgraph_node *node;
   symtab_node *snode;
 
-  if (DECL_ABSTRACT (decl))
+  if (DECL_ABSTRACT_P (decl))
     return false;
 
   /* We are concerned only about static/external vars and functions.  */
@@ -97,7 +116,7 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
     {
       /* Before we start optimizing unreachable code we can be sure all
         static objects are defined.  */
-      if (cgraph_function_flags_ready)
+      if (symtab->function_flags_ready)
        return true;
       snode = symtab_node::get (decl);
       if (!snode || !snode->definition)
@@ -142,7 +161,7 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
      units where they are used and when the other unit was compiled with LTO
      it is possible that vtable was kept public while the function itself
      was privatized. */
-  if (!cgraph_function_flags_ready)
+  if (!symtab->function_flags_ready)
     return true;
 
   snode = symtab_node::get (decl);
@@ -237,8 +256,7 @@ get_symbol_constant_value (tree sym)
         have zero as the initializer if they may not be
         overridden at link or run time.  */
       if (!val
-          && (INTEGRAL_TYPE_P (TREE_TYPE (sym))
-              || SCALAR_FLOAT_TYPE_P (TREE_TYPE (sym))))
+          && is_gimple_reg_type (TREE_TYPE (sym)))
        return build_zero_cst (TREE_TYPE (sym));
     }
 
@@ -255,7 +273,6 @@ get_symbol_constant_value (tree sym)
 static tree
 maybe_fold_reference (tree expr, bool is_lhs)
 {
-  tree *t = &expr;
   tree result;
 
   if ((TREE_CODE (expr) == VIEW_CONVERT_EXPR
@@ -275,71 +292,11 @@ maybe_fold_reference (tree expr, bool is_lhs)
                             TREE_OPERAND (expr, 1),
                             TREE_OPERAND (expr, 2));
 
-  while (handled_component_p (*t))
-    t = &TREE_OPERAND (*t, 0);
-
-  /* Canonicalize MEM_REFs invariant address operand.  Do this first
-     to avoid feeding non-canonical MEM_REFs elsewhere.  */
-  if (TREE_CODE (*t) == MEM_REF
-      && !is_gimple_mem_ref_addr (TREE_OPERAND (*t, 0)))
-    {
-      bool volatile_p = TREE_THIS_VOLATILE (*t);
-      tree tem = fold_binary (MEM_REF, TREE_TYPE (*t),
-                             TREE_OPERAND (*t, 0),
-                             TREE_OPERAND (*t, 1));
-      if (tem)
-       {
-         TREE_THIS_VOLATILE (tem) = volatile_p;
-         *t = tem;
-         tem = maybe_fold_reference (expr, is_lhs);
-         if (tem)
-           return tem;
-         return expr;
-       }
-    }
-
   if (!is_lhs
       && (result = fold_const_aggregate_ref (expr))
       && is_gimple_min_invariant (result))
     return result;
 
-  /* Fold back MEM_REFs to reference trees.  */
-  if (TREE_CODE (*t) == MEM_REF
-      && TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR
-      && integer_zerop (TREE_OPERAND (*t, 1))
-      && (TREE_THIS_VOLATILE (*t)
-         == TREE_THIS_VOLATILE (TREE_OPERAND (TREE_OPERAND (*t, 0), 0)))
-      && !TYPE_REF_CAN_ALIAS_ALL (TREE_TYPE (TREE_OPERAND (*t, 1)))
-      && (TYPE_MAIN_VARIANT (TREE_TYPE (*t))
-         == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_OPERAND (*t, 1)))))
-      /* We have to look out here to not drop a required conversion
-        from the rhs to the lhs if is_lhs, but we don't have the
-        rhs here to verify that.  Thus require strict type
-        compatibility.  */
-      && types_compatible_p (TREE_TYPE (*t),
-                            TREE_TYPE (TREE_OPERAND
-                                       (TREE_OPERAND (*t, 0), 0))))
-    {
-      tree tem;
-      *t = TREE_OPERAND (TREE_OPERAND (*t, 0), 0);
-      tem = maybe_fold_reference (expr, is_lhs);
-      if (tem)
-       return tem;
-      return expr;
-    }
-  else if (TREE_CODE (*t) == TARGET_MEM_REF)
-    {
-      tree tem = maybe_fold_tmr (*t);
-      if (tem)
-       {
-         *t = tem;
-         tem = maybe_fold_reference (expr, is_lhs);
-         if (tem)
-           return tem;
-         return expr;
-       }
-    }
-
   return NULL_TREE;
 }
 
@@ -364,6 +321,9 @@ fold_gimple_assign (gimple_stmt_iterator *si)
       {
         tree rhs = gimple_assign_rhs1 (stmt);
 
+       if (TREE_CLOBBER_P (rhs))
+         return NULL_TREE;
+
        if (REFERENCE_CLASS_P (rhs))
          return maybe_fold_reference (rhs, false);
 
@@ -372,19 +332,13 @@ fold_gimple_assign (gimple_stmt_iterator *si)
            tree val = OBJ_TYPE_REF_EXPR (rhs);
            if (is_gimple_min_invariant (val))
              return val;
-           else if (flag_devirtualize && virtual_method_call_p (val))
+           else if (flag_devirtualize && virtual_method_call_p (rhs))
              {
                bool final;
                vec <cgraph_node *>targets
-                 = possible_polymorphic_call_targets (val, stmt, &final);
+                 = possible_polymorphic_call_targets (rhs, stmt, &final);
                if (final && targets.length () <= 1 && dbg_cnt (devirt))
                  {
-                   tree fndecl;
-
-                   if (targets.length () == 1)
-                     fndecl = targets[0]->decl;
-                   else
-                     fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
                    if (dump_enabled_p ())
                      {
                        location_t loc = gimple_location_safe (stmt);
@@ -393,10 +347,19 @@ fold_gimple_assign (gimple_stmt_iterator *si)
                                         "reference to function %s\n",
                                         targets.length () == 1
                                         ? targets[0]->name ()
-                                        : "__builtin_unreachable");
+                                        : "NULL");
                      }
-                   val = fold_convert (TREE_TYPE (val), fndecl);
-                   STRIP_USELESS_TYPE_CONVERSION (val);
+                   if (targets.length () == 1)
+                     {
+                       val = fold_convert (TREE_TYPE (val),
+                                           build_fold_addr_expr_loc
+                                             (loc, targets[0]->decl));
+                       STRIP_USELESS_TYPE_CONVERSION (val);
+                     }
+                   else
+                     /* We can not use __builtin_unreachable here because it
+                        can not have address taken.  */
+                     val = build_int_cst (TREE_TYPE (val), 0);
                    return val;
                  }
              }
@@ -458,27 +421,6 @@ fold_gimple_assign (gimple_stmt_iterator *si)
       break;
 
     case GIMPLE_UNARY_RHS:
-      {
-       tree rhs = gimple_assign_rhs1 (stmt);
-
-       result = fold_unary_loc (loc, subcode, gimple_expr_type (stmt), rhs);
-       if (result)
-         {
-           /* If the operation was a conversion do _not_ mark a
-              resulting constant with TREE_OVERFLOW if the original
-              constant was not.  These conversions have implementation
-              defined behavior and retaining the TREE_OVERFLOW flag
-              here would confuse later passes such as VRP.  */
-           if (CONVERT_EXPR_CODE_P (subcode)
-               && TREE_CODE (result) == INTEGER_CST
-               && TREE_CODE (rhs) == INTEGER_CST)
-             TREE_OVERFLOW (result) = TREE_OVERFLOW (rhs);
-
-           STRIP_USELESS_TYPE_CONVERSION (result);
-           if (valid_gimple_rhs_p (result))
-             return result;
-         }
-      }
       break;
 
     case GIMPLE_BINARY_RHS:
@@ -604,7 +546,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
    assumed that the operands have been previously folded.  */
 
 static bool
-fold_gimple_cond (gimple stmt)
+fold_gimple_cond (gcond *stmt)
 {
   tree result = fold_binary_loc (gimple_location (stmt),
                             gimple_cond_code (stmt),
@@ -625,6 +567,79 @@ fold_gimple_cond (gimple stmt)
   return false;
 }
 
+
+/* Replace a statement at *SI_P with a sequence of statements in STMTS,
+   adjusting the replacement stmts location and virtual operands.
+   If the statement has a lhs the last stmt in the sequence is expected
+   to assign to that lhs.  */
+
+static void
+gsi_replace_with_seq_vops (gimple_stmt_iterator *si_p, gimple_seq stmts)
+{
+  gimple stmt = gsi_stmt (*si_p);
+
+  if (gimple_has_location (stmt))
+    annotate_all_with_location (stmts, gimple_location (stmt));
+
+  /* First iterate over the replacement statements backward, assigning
+     virtual operands to their defining statements.  */
+  gimple laststore = NULL;
+  for (gimple_stmt_iterator i = gsi_last (stmts);
+       !gsi_end_p (i); gsi_prev (&i))
+    {
+      gimple new_stmt = gsi_stmt (i);
+      if ((gimple_assign_single_p (new_stmt)
+          && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
+         || (is_gimple_call (new_stmt)
+             && (gimple_call_flags (new_stmt)
+                 & (ECF_NOVOPS | ECF_PURE | ECF_CONST | ECF_NORETURN)) == 0))
+       {
+         tree vdef;
+         if (!laststore)
+           vdef = gimple_vdef (stmt);
+         else
+           vdef = make_ssa_name (gimple_vop (cfun), new_stmt);
+         gimple_set_vdef (new_stmt, vdef);
+         if (vdef && TREE_CODE (vdef) == SSA_NAME)
+           SSA_NAME_DEF_STMT (vdef) = new_stmt;
+         laststore = new_stmt;
+       }
+    }
+
+  /* Second iterate over the statements forward, assigning virtual
+     operands to their uses.  */
+  tree reaching_vuse = gimple_vuse (stmt);
+  for (gimple_stmt_iterator i = gsi_start (stmts);
+       !gsi_end_p (i); gsi_next (&i))
+    {
+      gimple new_stmt = gsi_stmt (i);
+      /* If the new statement possibly has a VUSE, update it with exact SSA
+        name we know will reach this one.  */
+      if (gimple_has_mem_ops (new_stmt))
+       gimple_set_vuse (new_stmt, reaching_vuse);
+      gimple_set_modified (new_stmt, true);
+      if (gimple_vdef (new_stmt))
+       reaching_vuse = gimple_vdef (new_stmt);
+    }
+
+  /* If the new sequence does not do a store release the virtual
+     definition of the original statement.  */
+  if (reaching_vuse
+      && reaching_vuse == gimple_vuse (stmt))
+    {
+      tree vdef = gimple_vdef (stmt);
+      if (vdef
+         && TREE_CODE (vdef) == SSA_NAME)
+       {
+         unlink_stmt_vdef (stmt);
+         release_ssa_name (vdef);
+       }
+    }
+
+  /* Finally replace the original statement with the sequence.  */
+  gsi_replace_with_seq (si_p, stmts, false);
+}
+
 /* Convert EXPR into a GIMPLE value suitable for substitution on the
    RHS of an assignment.  Insert the necessary statements before
    iterator *SI_P.  The statement at *SI_P, which must be a GIMPLE_CALL
@@ -642,8 +657,6 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
   gimple stmt, new_stmt;
   gimple_stmt_iterator i;
   gimple_seq stmts = NULL;
-  gimple laststore;
-  tree reaching_vuse;
 
   stmt = gsi_stmt (*si_p);
 
@@ -680,399 +693,2337 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
 
   pop_gimplify_context (NULL);
 
-  if (gimple_has_location (stmt))
-    annotate_all_with_location (stmts, gimple_location (stmt));
+  gsi_replace_with_seq_vops (si_p, stmts);
+}
 
-  /* First iterate over the replacement statements backward, assigning
-     virtual operands to their defining statements.  */
-  laststore = NULL;
-  for (i = gsi_last (stmts); !gsi_end_p (i); gsi_prev (&i))
+
+/* Replace the call at *GSI with the gimple value VAL.  */
+
+static void
+replace_call_with_value (gimple_stmt_iterator *gsi, tree val)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_call_lhs (stmt);
+  gimple repl;
+  if (lhs)
     {
-      new_stmt = gsi_stmt (i);
-      if ((gimple_assign_single_p (new_stmt)
-          && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
-         || (is_gimple_call (new_stmt)
-             && (gimple_call_flags (new_stmt)
-                 & (ECF_NOVOPS | ECF_PURE | ECF_CONST | ECF_NORETURN)) == 0))
-       {
-         tree vdef;
-         if (!laststore)
-           vdef = gimple_vdef (stmt);
-         else
-           vdef = make_ssa_name (gimple_vop (cfun), new_stmt);
-         gimple_set_vdef (new_stmt, vdef);
-         if (vdef && TREE_CODE (vdef) == SSA_NAME)
-           SSA_NAME_DEF_STMT (vdef) = new_stmt;
-         laststore = new_stmt;
-       }
+      if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (val)))
+       val = fold_convert (TREE_TYPE (lhs), val);
+      repl = gimple_build_assign (lhs, val);
     }
-
-  /* Second iterate over the statements forward, assigning virtual
-     operands to their uses.  */
-  reaching_vuse = gimple_vuse (stmt);
-  for (i = gsi_start (stmts); !gsi_end_p (i); gsi_next (&i))
+  else
+    repl = gimple_build_nop ();
+  tree vdef = gimple_vdef (stmt);
+  if (vdef && TREE_CODE (vdef) == SSA_NAME)
     {
-      new_stmt = gsi_stmt (i);
-      /* If the new statement possibly has a VUSE, update it with exact SSA
-        name we know will reach this one.  */
-      if (gimple_has_mem_ops (new_stmt))
-       gimple_set_vuse (new_stmt, reaching_vuse);
-      gimple_set_modified (new_stmt, true);
-      if (gimple_vdef (new_stmt))
-       reaching_vuse = gimple_vdef (new_stmt);
+      unlink_stmt_vdef (stmt);
+      release_ssa_name (vdef);
     }
+  gsi_replace (gsi, repl, true);
+}
 
-  /* If the new sequence does not do a store release the virtual
-     definition of the original statement.  */
-  if (reaching_vuse
-      && reaching_vuse == gimple_vuse (stmt))
+/* Replace the call at *GSI with the new call REPL and fold that
+   again.  */
+
+static void
+replace_call_with_call_and_fold (gimple_stmt_iterator *gsi, gimple repl)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  gimple_call_set_lhs (repl, gimple_call_lhs (stmt));
+  gimple_set_location (repl, gimple_location (stmt));
+  if (gimple_vdef (stmt)
+      && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
     {
-      tree vdef = gimple_vdef (stmt);
-      if (vdef
-         && TREE_CODE (vdef) == SSA_NAME)
-       {
-         unlink_stmt_vdef (stmt);
-         release_ssa_name (vdef);
-       }
+      gimple_set_vdef (repl, gimple_vdef (stmt));
+      gimple_set_vuse (repl, gimple_vuse (stmt));
+      SSA_NAME_DEF_STMT (gimple_vdef (repl)) = repl;
     }
+  gsi_replace (gsi, repl, true);
+  fold_stmt (gsi);
+}
 
-  /* Finally replace the original statement with the sequence.  */
-  gsi_replace_with_seq (si_p, stmts, false);
+/* Return true if VAR is a VAR_DECL or a component thereof.  */
+
+static bool
+var_decl_component_p (tree var)
+{
+  tree inner = var;
+  while (handled_component_p (inner))
+    inner = TREE_OPERAND (inner, 0);
+  return SSA_VAR_P (inner);
 }
 
-/* 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
-   is not NULL and, for TYPE == 0, its value is not equal to the length
-   we determine or if we are unable to determine the length or value,
-   return false.  VISITED is a bitmap of visited variables.
-   TYPE is 0 if string length should be returned, 1 for maximum string
-   length and 2 for maximum value ARG can have.  */
+/* Fold function call to builtin mem{{,p}cpy,move}.  Return
+   false if no simplification can be made.
+   If ENDP is 0, return DEST (like memcpy).
+   If ENDP is 1, return DEST+LEN (like mempcpy).
+   If ENDP is 2, return DEST+LEN-1 (like stpcpy).
+   If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
+   (memmove).   */
 
 static bool
-get_maxval_strlen (tree arg, tree *length, bitmap visited, int type)
+gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
+                              tree dest, tree src, int endp)
 {
-  tree var, val;
-  gimple def_stmt;
+  gimple stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_call_lhs (stmt);
+  tree len = gimple_call_arg (stmt, 2);
+  tree destvar, srcvar;
+  location_t loc = gimple_location (stmt);
 
-  if (TREE_CODE (arg) != SSA_NAME)
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
     {
-      /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
-      if (TREE_CODE (arg) == ADDR_EXPR
-         && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
-         && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
+      gimple repl;
+      if (gimple_call_lhs (stmt))
+       repl = gimple_build_assign (gimple_call_lhs (stmt), dest);
+      else
+       repl = gimple_build_nop ();
+      tree vdef = gimple_vdef (stmt);
+      if (vdef && TREE_CODE (vdef) == SSA_NAME)
        {
-         tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-         if (TREE_CODE (aop0) == INDIRECT_REF
-             && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
-           return get_maxval_strlen (TREE_OPERAND (aop0, 0),
-                                     length, visited, type);
+         unlink_stmt_vdef (stmt);
+         release_ssa_name (vdef);
        }
+      gsi_replace (gsi, repl, true);
+      return true;
+    }
 
-      if (type == 2)
+  /* If SRC and DEST are the same (and not volatile), return
+     DEST{,+LEN,+LEN-1}.  */
+  if (operand_equal_p (src, dest, 0))
+    {
+      unlink_stmt_vdef (stmt);
+      if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
+       release_ssa_name (gimple_vdef (stmt));
+      if (!lhs)
        {
-         val = arg;
-         if (TREE_CODE (val) != INTEGER_CST
-             || tree_int_cst_sgn (val) < 0)
-           return false;
+         gsi_replace (gsi, gimple_build_nop (), true);
+         return true;
+       }
+      goto done;
+    }
+  else
+    {
+      tree srctype, desttype;
+      unsigned int src_align, dest_align;
+      tree off0;
+
+      /* Build accesses at offset zero with a ref-all character type.  */
+      off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
+                                                        ptr_mode, true), 0);
+
+      /* If we can perform the copy efficiently with first doing all loads
+         and then all stores inline it that way.  Currently efficiently
+        means that we can load all the memory into a single integer
+        register which is what MOVE_MAX gives us.  */
+      src_align = get_pointer_alignment (src);
+      dest_align = get_pointer_alignment (dest);
+      if (tree_fits_uhwi_p (len)
+         && compare_tree_int (len, MOVE_MAX) <= 0
+         /* ???  Don't transform copies from strings with known length this
+            confuses the tree-ssa-strlen.c.  This doesn't handle
+            the case in gcc.dg/strlenopt-8.c which is XFAILed for that
+            reason.  */
+         && !c_strlen (src, 2))
+       {
+         unsigned ilen = tree_to_uhwi (len);
+         if (exact_log2 (ilen) != -1)
+           {
+             tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
+             if (type
+                 && TYPE_MODE (type) != BLKmode
+                 && (GET_MODE_SIZE (TYPE_MODE (type)) * BITS_PER_UNIT
+                     == ilen * 8)
+                 /* If the destination pointer is not aligned we must be able
+                    to emit an unaligned store.  */
+                 && (dest_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type))
+                     || !SLOW_UNALIGNED_ACCESS (TYPE_MODE (type), dest_align)))
+               {
+                 tree srctype = type;
+                 tree desttype = type;
+                 if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+                   srctype = build_aligned_type (type, src_align);
+                 tree srcmem = fold_build2 (MEM_REF, srctype, src, off0);
+                 tree tem = fold_const_aggregate_ref (srcmem);
+                 if (tem)
+                   srcmem = tem;
+                 else if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type))
+                          && SLOW_UNALIGNED_ACCESS (TYPE_MODE (type),
+                                                    src_align))
+                   srcmem = NULL_TREE;
+                 if (srcmem)
+                   {
+                     gimple new_stmt;
+                     if (is_gimple_reg_type (TREE_TYPE (srcmem)))
+                       {
+                         new_stmt = gimple_build_assign (NULL_TREE, srcmem);
+                         if (gimple_in_ssa_p (cfun))
+                           srcmem = make_ssa_name (TREE_TYPE (srcmem),
+                                                   new_stmt);
+                         else
+                           srcmem = create_tmp_reg (TREE_TYPE (srcmem));
+                         gimple_assign_set_lhs (new_stmt, srcmem);
+                         gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+                         gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+                       }
+                     if (dest_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+                       desttype = build_aligned_type (type, dest_align);
+                     new_stmt
+                       = gimple_build_assign (fold_build2 (MEM_REF, desttype,
+                                                           dest, off0),
+                                              srcmem);
+                     gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+                     gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+                     if (gimple_vdef (new_stmt)
+                         && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+                       SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
+                     if (!lhs)
+                       {
+                         gsi_replace (gsi, new_stmt, true);
+                         return true;
+                       }
+                     gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+                     goto done;
+                   }
+               }
+           }
        }
-      else
-       val = c_strlen (arg, 1);
-      if (!val)
-       return false;
 
-      if (*length)
+      if (endp == 3)
        {
-         if (type > 0)
+         /* Both DEST and SRC must be pointer types.
+            ??? This is what old code did.  Is the testing for pointer types
+            really mandatory?
+
+            If either SRC is readonly or length is 1, we can use memcpy.  */
+         if (!dest_align || !src_align)
+           return false;
+         if (readonly_data_expr (src)
+             || (tree_fits_uhwi_p (len)
+                 && (MIN (src_align, dest_align) / BITS_PER_UNIT
+                     >= tree_to_uhwi (len))))
            {
-             if (TREE_CODE (*length) != INTEGER_CST
-                 || TREE_CODE (val) != INTEGER_CST)
+             tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+             if (!fn)
                return false;
-
-             if (tree_int_cst_lt (*length, val))
-               *length = val;
+             gimple_call_set_fndecl (stmt, fn);
+             gimple_call_set_arg (stmt, 0, dest);
+             gimple_call_set_arg (stmt, 1, src);
+             fold_stmt (gsi);
              return true;
            }
-         else if (simple_cst_equal (val, *length) != 1)
-           return false;
-       }
 
-      *length = val;
-      return true;
-    }
+         /* If *src and *dest can't overlap, optimize into memcpy as well.  */
+         if (TREE_CODE (src) == ADDR_EXPR
+             && TREE_CODE (dest) == ADDR_EXPR)
+           {
+             tree src_base, dest_base, fn;
+             HOST_WIDE_INT src_offset = 0, dest_offset = 0;
+             HOST_WIDE_INT size = -1;
+             HOST_WIDE_INT maxsize = -1;
+
+             srcvar = TREE_OPERAND (src, 0);
+             src_base = get_ref_base_and_extent (srcvar, &src_offset,
+                                                 &size, &maxsize);
+             destvar = TREE_OPERAND (dest, 0);
+             dest_base = get_ref_base_and_extent (destvar, &dest_offset,
+                                                  &size, &maxsize);
+             if (tree_fits_uhwi_p (len))
+               maxsize = tree_to_uhwi (len);
+             else
+               maxsize = -1;
+             src_offset /= BITS_PER_UNIT;
+             dest_offset /= BITS_PER_UNIT;
+             if (SSA_VAR_P (src_base)
+                 && SSA_VAR_P (dest_base))
+               {
+                 if (operand_equal_p (src_base, dest_base, 0)
+                     && ranges_overlap_p (src_offset, maxsize,
+                                          dest_offset, maxsize))
+                   return false;
+               }
+             else if (TREE_CODE (src_base) == MEM_REF
+                      && TREE_CODE (dest_base) == MEM_REF)
+               {
+                 if (! operand_equal_p (TREE_OPERAND (src_base, 0),
+                                        TREE_OPERAND (dest_base, 0), 0))
+                   return false;
+                 offset_int off = mem_ref_offset (src_base) + src_offset;
+                 if (!wi::fits_shwi_p (off))
+                   return false;
+                 src_offset = off.to_shwi ();
+
+                 off = mem_ref_offset (dest_base) + dest_offset;
+                 if (!wi::fits_shwi_p (off))
+                   return false;
+                 dest_offset = off.to_shwi ();
+                 if (ranges_overlap_p (src_offset, maxsize,
+                                       dest_offset, maxsize))
+                   return false;
+               }
+             else
+               return false;
 
-  /* If ARG is registered for SSA update we cannot look at its defining
-     statement.  */
-  if (name_registered_for_update_p (arg))
-    return false;
+             fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+             if (!fn)
+               return false;
+             gimple_call_set_fndecl (stmt, fn);
+             gimple_call_set_arg (stmt, 0, dest);
+             gimple_call_set_arg (stmt, 1, src);
+             fold_stmt (gsi);
+             return true;
+           }
 
-  /* If we were already here, break the infinite cycle.  */
-  if (!bitmap_set_bit (visited, SSA_NAME_VERSION (arg)))
-    return true;
+         /* If the destination and source do not alias optimize into
+            memcpy as well.  */
+         if ((is_gimple_min_invariant (dest)
+              || TREE_CODE (dest) == SSA_NAME)
+             && (is_gimple_min_invariant (src)
+                 || TREE_CODE (src) == SSA_NAME))
+           {
+             ao_ref destr, srcr;
+             ao_ref_init_from_ptr_and_size (&destr, dest, len);
+             ao_ref_init_from_ptr_and_size (&srcr, src, len);
+             if (!refs_may_alias_p_1 (&destr, &srcr, false))
+               {
+                 tree fn;
+                 fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+                 if (!fn)
+                   return false;
+                 gimple_call_set_fndecl (stmt, fn);
+                 gimple_call_set_arg (stmt, 0, dest);
+                 gimple_call_set_arg (stmt, 1, src);
+                 fold_stmt (gsi);
+                 return true;
+               }
+           }
 
-  var = arg;
-  def_stmt = SSA_NAME_DEF_STMT (var);
+         return false;
+       }
+
+      if (!tree_fits_shwi_p (len))
+       return false;
+      /* FIXME:
+         This logic lose for arguments like (type *)malloc (sizeof (type)),
+         since we strip the casts of up to VOID return value from malloc.
+        Perhaps we ought to inherit type from non-VOID argument here?  */
+      STRIP_NOPS (src);
+      STRIP_NOPS (dest);
+      if (!POINTER_TYPE_P (TREE_TYPE (src))
+         || !POINTER_TYPE_P (TREE_TYPE (dest)))
+       return false;
+      /* In the following try to find a type that is most natural to be
+        used for the memcpy source and destination and that allows
+        the most optimization when memcpy is turned into a plain assignment
+        using that type.  In theory we could always use a char[len] type
+        but that only gains us that the destination and source possibly
+        no longer will have their address taken.  */
+      /* As we fold (void *)(p + CST) to (void *)p + CST undo this here.  */
+      if (TREE_CODE (src) == POINTER_PLUS_EXPR)
+       {
+         tree tem = TREE_OPERAND (src, 0);
+         STRIP_NOPS (tem);
+         if (tem != TREE_OPERAND (src, 0))
+           src = build1 (NOP_EXPR, TREE_TYPE (tem), src);
+       }
+      if (TREE_CODE (dest) == POINTER_PLUS_EXPR)
+       {
+         tree tem = TREE_OPERAND (dest, 0);
+         STRIP_NOPS (tem);
+         if (tem != TREE_OPERAND (dest, 0))
+           dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest);
+       }
+      srctype = TREE_TYPE (TREE_TYPE (src));
+      if (TREE_CODE (srctype) == ARRAY_TYPE
+         && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+       {
+         srctype = TREE_TYPE (srctype);
+         STRIP_NOPS (src);
+         src = build1 (NOP_EXPR, build_pointer_type (srctype), src);
+       }
+      desttype = TREE_TYPE (TREE_TYPE (dest));
+      if (TREE_CODE (desttype) == ARRAY_TYPE
+         && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+       {
+         desttype = TREE_TYPE (desttype);
+         STRIP_NOPS (dest);
+         dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest);
+       }
+      if (TREE_ADDRESSABLE (srctype)
+         || TREE_ADDRESSABLE (desttype))
+       return false;
+
+      /* Make sure we are not copying using a floating-point mode or
+         a type whose size possibly does not match its precision.  */
+      if (FLOAT_MODE_P (TYPE_MODE (desttype))
+         || TREE_CODE (desttype) == BOOLEAN_TYPE
+         || TREE_CODE (desttype) == ENUMERAL_TYPE)
+       desttype = bitwise_type_for_mode (TYPE_MODE (desttype));
+      if (FLOAT_MODE_P (TYPE_MODE (srctype))
+         || TREE_CODE (srctype) == BOOLEAN_TYPE
+         || TREE_CODE (srctype) == ENUMERAL_TYPE)
+       srctype = bitwise_type_for_mode (TYPE_MODE (srctype));
+      if (!srctype)
+       srctype = desttype;
+      if (!desttype)
+       desttype = srctype;
+      if (!srctype)
+       return false;
+
+      src_align = get_pointer_alignment (src);
+      dest_align = get_pointer_alignment (dest);
+      if (dest_align < TYPE_ALIGN (desttype)
+         || src_align < TYPE_ALIGN (srctype))
+       return false;
+
+      destvar = dest;
+      STRIP_NOPS (destvar);
+      if (TREE_CODE (destvar) == ADDR_EXPR
+         && var_decl_component_p (TREE_OPERAND (destvar, 0))
+         && tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+       destvar = fold_build2 (MEM_REF, desttype, destvar, off0);
+      else
+       destvar = NULL_TREE;
+
+      srcvar = src;
+      STRIP_NOPS (srcvar);
+      if (TREE_CODE (srcvar) == ADDR_EXPR
+         && var_decl_component_p (TREE_OPERAND (srcvar, 0))
+         && tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+       {
+         if (!destvar
+             || src_align >= TYPE_ALIGN (desttype))
+           srcvar = fold_build2 (MEM_REF, destvar ? desttype : srctype,
+                                 srcvar, off0);
+         else if (!STRICT_ALIGNMENT)
+           {
+             srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
+                                           src_align);
+             srcvar = fold_build2 (MEM_REF, srctype, srcvar, off0);
+           }
+         else
+           srcvar = NULL_TREE;
+       }
+      else
+       srcvar = NULL_TREE;
+
+      if (srcvar == NULL_TREE && destvar == NULL_TREE)
+       return false;
+
+      if (srcvar == NULL_TREE)
+       {
+         STRIP_NOPS (src);
+         if (src_align >= TYPE_ALIGN (desttype))
+           srcvar = fold_build2 (MEM_REF, desttype, src, off0);
+         else
+           {
+             if (STRICT_ALIGNMENT)
+               return false;
+             srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
+                                           src_align);
+             srcvar = fold_build2 (MEM_REF, srctype, src, off0);
+           }
+       }
+      else if (destvar == NULL_TREE)
+       {
+         STRIP_NOPS (dest);
+         if (dest_align >= TYPE_ALIGN (srctype))
+           destvar = fold_build2 (MEM_REF, srctype, dest, off0);
+         else
+           {
+             if (STRICT_ALIGNMENT)
+               return false;
+             desttype = build_aligned_type (TYPE_MAIN_VARIANT (srctype),
+                                            dest_align);
+             destvar = fold_build2 (MEM_REF, desttype, dest, off0);
+           }
+       }
+
+      gimple new_stmt;
+      if (is_gimple_reg_type (TREE_TYPE (srcvar)))
+       {
+         new_stmt = gimple_build_assign (NULL_TREE, srcvar);
+         if (gimple_in_ssa_p (cfun))
+           srcvar = make_ssa_name (TREE_TYPE (srcvar), new_stmt);
+         else
+           srcvar = create_tmp_reg (TREE_TYPE (srcvar));
+         gimple_assign_set_lhs (new_stmt, srcvar);
+         gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+         gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+       }
+      new_stmt = gimple_build_assign (destvar, srcvar);
+      gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+      gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+      if (gimple_vdef (new_stmt)
+         && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+       SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
+      if (!lhs)
+       {
+         gsi_replace (gsi, new_stmt, true);
+         return true;
+       }
+      gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+    }
+
+done:
+  if (endp == 0 || endp == 3)
+    len = NULL_TREE;
+  else if (endp == 2)
+    len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len,
+                          ssize_int (1));
+  if (endp == 2 || endp == 1)
+    dest = fold_build_pointer_plus_loc (loc, dest, len);
+
+  dest = force_gimple_operand_gsi (gsi, dest, false, NULL_TREE, true,
+                                  GSI_SAME_STMT);
+  gimple repl = gimple_build_assign (lhs, dest);
+  gsi_replace (gsi, repl, true);
+  return true;
+}
+
+/* Fold function call to builtin memset or bzero at *GSI setting the
+   memory of size LEN to VAL.  Return whether a simplification was made.  */
+
+static bool
+gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree etype;
+  unsigned HOST_WIDE_INT length, cval;
+
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
+    {
+      replace_call_with_value (gsi, gimple_call_arg (stmt, 0));
+      return true;
+    }
+
+  if (! tree_fits_uhwi_p (len))
+    return false;
+
+  if (TREE_CODE (c) != INTEGER_CST)
+    return false;
+
+  tree dest = gimple_call_arg (stmt, 0);
+  tree var = dest;
+  if (TREE_CODE (var) != ADDR_EXPR)
+    return false;
+
+  var = TREE_OPERAND (var, 0);
+  if (TREE_THIS_VOLATILE (var))
+    return false;
+
+  etype = TREE_TYPE (var);
+  if (TREE_CODE (etype) == ARRAY_TYPE)
+    etype = TREE_TYPE (etype);
+
+  if (!INTEGRAL_TYPE_P (etype)
+      && !POINTER_TYPE_P (etype))
+    return NULL_TREE;
+
+  if (! var_decl_component_p (var))
+    return NULL_TREE;
+
+  length = tree_to_uhwi (len);
+  if (GET_MODE_SIZE (TYPE_MODE (etype)) != length
+      || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
+    return NULL_TREE;
+
+  if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+    return NULL_TREE;
+
+  if (integer_zerop (c))
+    cval = 0;
+  else
+    {
+      if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
+       return NULL_TREE;
+
+      cval = TREE_INT_CST_LOW (c);
+      cval &= 0xff;
+      cval |= cval << 8;
+      cval |= cval << 16;
+      cval |= (cval << 31) << 1;
+    }
+
+  var = fold_build2 (MEM_REF, etype, dest, build_int_cst (ptr_type_node, 0));
+  gimple store = gimple_build_assign (var, build_int_cst_type (etype, cval));
+  gimple_set_vuse (store, gimple_vuse (stmt));
+  tree vdef = gimple_vdef (stmt);
+  if (vdef && TREE_CODE (vdef) == SSA_NAME)
+    {
+      gimple_set_vdef (store, gimple_vdef (stmt));
+      SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = store;
+    }
+  gsi_insert_before (gsi, store, GSI_SAME_STMT);
+  if (gimple_call_lhs (stmt))
+    {
+      gimple asgn = gimple_build_assign (gimple_call_lhs (stmt), dest);
+      gsi_replace (gsi, asgn, true);
+    }
+  else
+    {
+      gimple_stmt_iterator gsi2 = *gsi;
+      gsi_prev (gsi);
+      gsi_remove (&gsi2, true);
+    }
+
+  return true;
+}
+
+
+/* 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
+   is not NULL and, for TYPE == 0, its value is not equal to the length
+   we determine or if we are unable to determine the length or value,
+   return false.  VISITED is a bitmap of visited variables.
+   TYPE is 0 if string length should be returned, 1 for maximum string
+   length and 2 for maximum value ARG can have.  */
+
+static bool
+get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
+{
+  tree var, val;
+  gimple def_stmt;
+
+  if (TREE_CODE (arg) != SSA_NAME)
+    {
+      /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
+      if (TREE_CODE (arg) == ADDR_EXPR
+         && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
+         && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
+       {
+         tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+         if (TREE_CODE (aop0) == INDIRECT_REF
+             && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
+           return get_maxval_strlen (TREE_OPERAND (aop0, 0),
+                                     length, visited, type);
+       }
+
+      if (type == 2)
+       {
+         val = arg;
+         if (TREE_CODE (val) != INTEGER_CST
+             || tree_int_cst_sgn (val) < 0)
+           return false;
+       }
+      else
+       val = c_strlen (arg, 1);
+      if (!val)
+       return false;
+
+      if (*length)
+       {
+         if (type > 0)
+           {
+             if (TREE_CODE (*length) != INTEGER_CST
+                 || TREE_CODE (val) != INTEGER_CST)
+               return false;
+
+             if (tree_int_cst_lt (*length, val))
+               *length = val;
+             return true;
+           }
+         else if (simple_cst_equal (val, *length) != 1)
+           return false;
+       }
+
+      *length = val;
+      return true;
+    }
+
+  /* If ARG is registered for SSA update we cannot look at its defining
+     statement.  */
+  if (name_registered_for_update_p (arg))
+    return false;
+
+  /* If we were already here, break the infinite cycle.  */
+  if (!*visited)
+    *visited = BITMAP_ALLOC (NULL);
+  if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (arg)))
+    return true;
+
+  var = arg;
+  def_stmt = SSA_NAME_DEF_STMT (var);
+
+  switch (gimple_code (def_stmt))
+    {
+      case GIMPLE_ASSIGN:
+        /* The RHS of the statement defining VAR must either have a
+           constant length or come from another SSA_NAME with a constant
+           length.  */
+        if (gimple_assign_single_p (def_stmt)
+            || gimple_assign_unary_nop_p (def_stmt))
+          {
+            tree rhs = gimple_assign_rhs1 (def_stmt);
+            return get_maxval_strlen (rhs, length, visited, type);
+          }
+       else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
+         {
+           tree op2 = gimple_assign_rhs2 (def_stmt);
+           tree op3 = gimple_assign_rhs3 (def_stmt);
+           return get_maxval_strlen (op2, length, visited, type)
+                  && get_maxval_strlen (op3, length, visited, type);
+          }
+        return false;
+
+      case GIMPLE_PHI:
+       {
+         /* All the arguments of the PHI node must have the same constant
+            length.  */
+         unsigned i;
+
+         for (i = 0; i < gimple_phi_num_args (def_stmt); i++)
+          {
+            tree arg = gimple_phi_arg (def_stmt, i)->def;
+
+            /* If this PHI has itself as an argument, we cannot
+               determine the string length of this argument.  However,
+               if we can find a constant string length for the other
+               PHI args then we can still be sure that this is a
+               constant string length.  So be optimistic and just
+               continue with the next argument.  */
+            if (arg == gimple_phi_result (def_stmt))
+              continue;
+
+            if (!get_maxval_strlen (arg, length, visited, type))
+              return false;
+          }
+        }
+        return true;
+
+      default:
+        return false;
+    }
+}
+
+tree
+get_maxval_strlen (tree arg, int type)
+{
+  bitmap visited = NULL;
+  tree len = NULL_TREE;
+  if (!get_maxval_strlen (arg, &len, &visited, type))
+    len = NULL_TREE;
+  if (visited)
+    BITMAP_FREE (visited);
+
+  return len;
+}
+
+
+/* Fold function call to builtin strcpy with arguments DEST and SRC.
+   If LEN is not NULL, it represents the length of the string to be
+   copied.  Return NULL_TREE if no simplification can be made.  */
+
+static bool
+gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
+                           tree dest, tree src)
+{
+  location_t loc = gimple_location (gsi_stmt (*gsi));
+  tree fn;
+
+  /* If SRC and DEST are the same (and not volatile), return DEST.  */
+  if (operand_equal_p (src, dest, 0))
+    {
+      replace_call_with_value (gsi, dest);
+      return true;
+    }
+
+  if (optimize_function_for_size_p (cfun))
+    return false;
+
+  fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  if (!fn)
+    return false;
+
+  tree len = get_maxval_strlen (src, 0);
+  if (!len)
+    return false;
+
+  len = fold_convert_loc (loc, size_type_node, len);
+  len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1));
+  len = force_gimple_operand_gsi (gsi, len, true,
+                                 NULL_TREE, true, GSI_SAME_STMT);
+  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Fold function call to builtin strncpy with arguments DEST, SRC, and LEN.
+   If SLEN is not NULL, it represents the length of the source string.
+   Return NULL_TREE if no simplification can be made.  */
+
+static bool
+gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
+                            tree dest, tree src, tree len)
+{
+  location_t loc = gimple_location (gsi_stmt (*gsi));
+  tree fn;
+
+  /* If the LEN parameter is zero, return DEST.  */
+  if (integer_zerop (len))
+    {
+      replace_call_with_value (gsi, dest);
+      return true;
+    }
+
+  /* We can't compare slen with len as constants below if len is not a
+     constant.  */
+  if (TREE_CODE (len) != INTEGER_CST)
+    return false;
+
+  /* Now, we must be passed a constant src ptr parameter.  */
+  tree slen = get_maxval_strlen (src, 0);
+  if (!slen || TREE_CODE (slen) != INTEGER_CST)
+    return false;
+
+  slen = size_binop_loc (loc, PLUS_EXPR, slen, ssize_int (1));
+
+  /* We do not support simplification of this case, though we do
+     support it when expanding trees into RTL.  */
+  /* FIXME: generate a call to __builtin_memset.  */
+  if (tree_int_cst_lt (slen, len))
+    return false;
+
+  /* OK transform into builtin memcpy.  */
+  fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  if (!fn)
+    return false;
+
+  len = fold_convert_loc (loc, size_type_node, len);
+  len = force_gimple_operand_gsi (gsi, len, true,
+                                 NULL_TREE, true, GSI_SAME_STMT);
+  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Simplify a call to the strcat builtin.  DST and SRC are the arguments
+   to the call.
+
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.
+
+   The simplified form may be a constant or other expression which
+   computes the same value, but in a more efficient manner (including
+   calls to other builtin functions).
+
+   The call may contain arguments which need to be evaluated, but
+   which are not useful to determine the result of the call.  In
+   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
+   COMPOUND_EXPR will be an argument which must be evaluated.
+   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
+   COMPOUND_EXPR in the chain will contain the tree for the simplified
+   form of the builtin function call.  */
+
+static bool
+gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+
+  const char *p = c_getstr (src);
+
+  /* If the string length is zero, return the dst parameter.  */
+  if (p && *p == '\0')
+    {
+      replace_call_with_value (gsi, dst);
+      return true;
+    }
+
+  if (!optimize_bb_for_speed_p (gimple_bb (stmt)))
+    return false;
+
+  /* See if we can store by pieces into (dst + strlen(dst)).  */
+  tree newdst;
+  tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+  tree memcpy_fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+
+  if (!strlen_fn || !memcpy_fn)
+    return false;
+
+  /* If the length of the source string isn't computable don't
+     split strcat into strlen and memcpy.  */
+  tree len = get_maxval_strlen (src, 0);
+  if (! len)
+    return false;
+
+  /* Create strlen (dst).  */
+  gimple_seq stmts = NULL, stmts2;
+  gimple repl = gimple_build_call (strlen_fn, 1, dst);
+  gimple_set_location (repl, loc);
+  if (gimple_in_ssa_p (cfun))
+    newdst = make_ssa_name (size_type_node);
+  else
+    newdst = create_tmp_reg (size_type_node);
+  gimple_call_set_lhs (repl, newdst);
+  gimple_seq_add_stmt_without_update (&stmts, repl);
+
+  /* Create (dst p+ strlen (dst)).  */
+  newdst = fold_build_pointer_plus_loc (loc, dst, newdst);
+  newdst = force_gimple_operand (newdst, &stmts2, true, NULL_TREE);
+  gimple_seq_add_seq_without_update (&stmts, stmts2);
+
+  len = fold_convert_loc (loc, size_type_node, len);
+  len = size_binop_loc (loc, PLUS_EXPR, len,
+                       build_int_cst (size_type_node, 1));
+  len = force_gimple_operand (len, &stmts2, true, NULL_TREE);
+  gimple_seq_add_seq_without_update (&stmts, stmts2);
+
+  repl = gimple_build_call (memcpy_fn, 3, newdst, src, len);
+  gimple_seq_add_stmt_without_update (&stmts, repl);
+  if (gimple_call_lhs (stmt))
+    {
+      repl = gimple_build_assign (gimple_call_lhs (stmt), dst);
+      gimple_seq_add_stmt_without_update (&stmts, repl);
+      gsi_replace_with_seq_vops (gsi, stmts);
+      /* gsi now points at the assignment to the lhs, get a
+         stmt iterator to the memcpy call.
+        ???  We can't use gsi_for_stmt as that doesn't work when the
+        CFG isn't built yet.  */
+      gimple_stmt_iterator gsi2 = *gsi;
+      gsi_prev (&gsi2);
+      fold_stmt (&gsi2);
+    }
+  else
+    {
+      gsi_replace_with_seq_vops (gsi, stmts);
+      fold_stmt (gsi);
+    }
+  return true;
+}
+
+/* Fold a call to the __strcat_chk builtin FNDECL.  DEST, SRC, and SIZE
+   are the arguments to the call.  */
+
+static bool
+gimple_fold_builtin_strcat_chk (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree dest = gimple_call_arg (stmt, 0);
+  tree src = gimple_call_arg (stmt, 1);
+  tree size = gimple_call_arg (stmt, 2);
+  tree fn;
+  const char *p;
+
+
+  p = c_getstr (src);
+  /* If the SRC parameter is "", return DEST.  */
+  if (p && *p == '\0')
+    {
+      replace_call_with_value (gsi, dest);
+      return true;
+    }
+
+  if (! tree_fits_uhwi_p (size) || ! integer_all_onesp (size))
+    return false;
+
+  /* If __builtin_strcat_chk is used, assume strcat is available.  */
+  fn = builtin_decl_explicit (BUILT_IN_STRCAT);
+  if (!fn)
+    return false;
+
+  gimple repl = gimple_build_call (fn, 2, dest, src);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Simplify a call to the strncat builtin.  */
+
+static bool
+gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree dst = gimple_call_arg (stmt, 0);
+  tree src = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  const char *p = c_getstr (src);
+
+  /* If the requested length is zero, or the src parameter string
+     length is zero, return the dst parameter.  */
+  if (integer_zerop (len) || (p && *p == '\0'))
+    {
+      replace_call_with_value (gsi, dst);
+      return true;
+    }
+
+  /* If the requested len is greater than or equal to the string
+     length, call strcat.  */
+  if (TREE_CODE (len) == INTEGER_CST && p
+      && compare_tree_int (len, strlen (p)) >= 0)
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_STRCAT);
+
+      /* If the replacement _DECL isn't initialized, don't do the
+        transformation.  */
+      if (!fn)
+       return false;
+
+      gcall *repl = gimple_build_call (fn, 2, dst, src);
+      replace_call_with_call_and_fold (gsi, repl);
+      return true;
+    }
+
+  return false;
+}
+
+/* Fold a call to the __strncat_chk builtin with arguments DEST, SRC,
+   LEN, and SIZE.  */
+
+static bool 
+gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree dest = gimple_call_arg (stmt, 0);
+  tree src = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  tree size = gimple_call_arg (stmt, 3);
+  tree fn;
+  const char *p;
+
+  p = c_getstr (src);
+  /* If the SRC parameter is "" or if LEN is 0, return DEST.  */
+  if ((p && *p == '\0')
+      || integer_zerop (len))
+    {
+      replace_call_with_value (gsi, dest);
+      return true;
+    }
+
+  if (! tree_fits_uhwi_p (size))
+    return false;
+
+  if (! integer_all_onesp (size))
+    {
+      tree src_len = c_strlen (src, 1);
+      if (src_len
+         && tree_fits_uhwi_p (src_len)
+         && tree_fits_uhwi_p (len)
+         && ! tree_int_cst_lt (len, src_len))
+       {
+         /* If LEN >= strlen (SRC), optimize into __strcat_chk.  */
+         fn = builtin_decl_explicit (BUILT_IN_STRCAT_CHK);
+         if (!fn)
+           return false;
+
+         gimple repl = gimple_build_call (fn, 3, dest, src, size);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+      return false;
+    }
+
+  /* If __builtin_strncat_chk is used, assume strncat is available.  */
+  fn = builtin_decl_explicit (BUILT_IN_STRNCAT);
+  if (!fn)
+    return false;
+
+  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Fold a call to the fputs builtin.  ARG0 and ARG1 are the arguments
+   to the call.  IGNORE is true if the value returned
+   by the builtin will be ignored.  UNLOCKED is true is true if this
+   actually a call to fputs_unlocked.  If LEN in non-NULL, it represents
+   the known length of the string.  Return NULL_TREE if no simplification
+   was possible.  */
+
+static bool
+gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
+                          tree arg0, tree arg1,
+                          bool unlocked)
+{
+  gimple stmt = gsi_stmt (*gsi);
+
+  /* If we're using an unlocked function, assume the other unlocked
+     functions exist explicitly.  */
+  tree const fn_fputc = (unlocked
+                        ? builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED)
+                        : builtin_decl_implicit (BUILT_IN_FPUTC));
+  tree const fn_fwrite = (unlocked
+                         ? builtin_decl_explicit (BUILT_IN_FWRITE_UNLOCKED)
+                         : builtin_decl_implicit (BUILT_IN_FWRITE));
+
+  /* If the return value is used, don't do the transformation.  */
+  if (gimple_call_lhs (stmt))
+    return false;
+
+  /* Get the length of the string passed to fputs.  If the length
+     can't be determined, punt.  */
+  tree len = get_maxval_strlen (arg0, 0);
+  if (!len
+      || TREE_CODE (len) != INTEGER_CST)
+    return false;
+
+  switch (compare_tree_int (len, 1))
+    {
+    case -1: /* length is 0, delete the call entirely .  */
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+
+    case 0: /* length is 1, call fputc.  */
+      {
+       const char *p = c_getstr (arg0);
+       if (p != NULL)
+         {
+           if (!fn_fputc)
+             return false;
+
+           gimple repl = gimple_build_call (fn_fputc, 2,
+                                            build_int_cst
+                                            (integer_type_node, p[0]), arg1);
+           replace_call_with_call_and_fold (gsi, repl);
+           return true;
+         }
+      }
+      /* FALLTHROUGH */
+    case 1: /* length is greater than 1, call fwrite.  */
+      {
+       /* If optimizing for size keep fputs.  */
+       if (optimize_function_for_size_p (cfun))
+         return false;
+       /* New argument list transforming fputs(string, stream) to
+          fwrite(string, 1, len, stream).  */
+       if (!fn_fwrite)
+         return false;
+
+       gimple repl = gimple_build_call (fn_fwrite, 4, arg0,
+                                        size_one_node, len, arg1);
+       replace_call_with_call_and_fold (gsi, repl);
+       return true;
+      }
+    default:
+      gcc_unreachable ();
+    }
+  return false;
+}
+
+/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+   DEST, SRC, LEN, and SIZE are the arguments to the call.
+   IGNORE is true, if return value can be ignored.  FCODE is the BUILT_IN_*
+   code of the builtin.  If MAXLEN is not NULL, it is maximum length
+   passed as third argument.  */
+
+static bool
+gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
+                               tree dest, tree src, tree len, tree size,
+                               enum built_in_function fcode)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
+  tree fn;
+
+  /* If SRC and DEST are the same (and not volatile), return DEST
+     (resp. DEST+LEN for __mempcpy_chk).  */
+  if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
+    {
+      if (fcode != BUILT_IN_MEMPCPY_CHK)
+       {
+         replace_call_with_value (gsi, dest);
+         return true;
+       }
+      else
+       {
+         tree temp = fold_build_pointer_plus_loc (loc, dest, len);
+         temp = force_gimple_operand_gsi (gsi, temp,
+                                          false, NULL_TREE, true,
+                                          GSI_SAME_STMT);
+         replace_call_with_value (gsi, temp);
+         return true;
+       }
+    }
+
+  if (! tree_fits_uhwi_p (size))
+    return false;
+
+  tree maxlen = get_maxval_strlen (len, 2);
+  if (! integer_all_onesp (size))
+    {
+      if (! tree_fits_uhwi_p (len))
+       {
+         /* If LEN is not constant, try MAXLEN too.
+            For MAXLEN only allow optimizing into non-_ocs function
+            if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+         if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+           {
+             if (fcode == BUILT_IN_MEMPCPY_CHK && ignore)
+               {
+                 /* (void) __mempcpy_chk () can be optimized into
+                    (void) __memcpy_chk ().  */
+                 fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+                 if (!fn)
+                   return false;
+
+                 gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+                 replace_call_with_call_and_fold (gsi, repl);
+                 return true;
+               }
+             return false;
+           }
+       }
+      else
+       maxlen = len;
+
+      if (tree_int_cst_lt (size, maxlen))
+       return false;
+    }
+
+  fn = NULL_TREE;
+  /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
+     mem{cpy,pcpy,move,set} is available.  */
+  switch (fcode)
+    {
+    case BUILT_IN_MEMCPY_CHK:
+      fn = builtin_decl_explicit (BUILT_IN_MEMCPY);
+      break;
+    case BUILT_IN_MEMPCPY_CHK:
+      fn = builtin_decl_explicit (BUILT_IN_MEMPCPY);
+      break;
+    case BUILT_IN_MEMMOVE_CHK:
+      fn = builtin_decl_explicit (BUILT_IN_MEMMOVE);
+      break;
+    case BUILT_IN_MEMSET_CHK:
+      fn = builtin_decl_explicit (BUILT_IN_MEMSET);
+      break;
+    default:
+      break;
+    }
+
+  if (!fn)
+    return false;
+
+  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Fold a call to the __st[rp]cpy_chk builtin.
+   DEST, SRC, and SIZE are the arguments to the call.
+   IGNORE is true if return value can be ignored.  FCODE is the BUILT_IN_*
+   code of the builtin.  If MAXLEN is not NULL, it is maximum length of
+   strings passed as second argument.  */
+
+static bool
+gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
+                               tree dest,
+                               tree src, tree size,
+                               enum built_in_function fcode)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
+  tree len, fn;
+
+  /* If SRC and DEST are the same (and not volatile), return DEST.  */
+  if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
+    {
+      replace_call_with_value (gsi, dest);
+      return true;
+    }
+
+  if (! tree_fits_uhwi_p (size))
+    return false;
+
+  tree maxlen = get_maxval_strlen (src, 1);
+  if (! integer_all_onesp (size))
+    {
+      len = c_strlen (src, 1);
+      if (! len || ! tree_fits_uhwi_p (len))
+       {
+         /* If LEN is not constant, try MAXLEN too.
+            For MAXLEN only allow optimizing into non-_ocs function
+            if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+         if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+           {
+             if (fcode == BUILT_IN_STPCPY_CHK)
+               {
+                 if (! ignore)
+                   return false;
+
+                 /* If return value of __stpcpy_chk is ignored,
+                    optimize into __strcpy_chk.  */
+                 fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK);
+                 if (!fn)
+                   return false;
+
+                 gimple repl = gimple_build_call (fn, 3, dest, src, size);
+                 replace_call_with_call_and_fold (gsi, repl);
+                 return true;
+               }
+
+             if (! len || TREE_SIDE_EFFECTS (len))
+               return false;
+
+             /* If c_strlen returned something, but not a constant,
+                transform __strcpy_chk into __memcpy_chk.  */
+             fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
+             if (!fn)
+               return false;
+
+             len = fold_convert_loc (loc, size_type_node, len);
+             len = size_binop_loc (loc, PLUS_EXPR, len,
+                                   build_int_cst (size_type_node, 1));
+             len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE,
+                                             true, GSI_SAME_STMT);
+             gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+             replace_call_with_call_and_fold (gsi, repl);
+             return true;
+           }
+       }
+      else
+       maxlen = len;
+
+      if (! tree_int_cst_lt (maxlen, size))
+       return false;
+    }
+
+  /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available.  */
+  fn = builtin_decl_explicit (fcode == BUILT_IN_STPCPY_CHK
+                             ? BUILT_IN_STPCPY : BUILT_IN_STRCPY);
+  if (!fn)
+    return false;
+
+  gimple repl = gimple_build_call (fn, 2, dest, src);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Fold a call to the __st{r,p}ncpy_chk builtin.  DEST, SRC, LEN, and SIZE
+   are the arguments to the call.  If MAXLEN is not NULL, it is maximum
+   length passed as third argument. IGNORE is true if return value can be
+   ignored. FCODE is the BUILT_IN_* code of the builtin. */
+
+static bool
+gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
+                                tree dest, tree src,
+                                tree len, tree size,
+                                enum built_in_function fcode)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
+  tree fn;
+
+  if (fcode == BUILT_IN_STPNCPY_CHK && ignore)
+    {
+       /* If return value of __stpncpy_chk is ignored,
+          optimize into __strncpy_chk.  */
+       fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK);
+       if (fn)
+        {
+          gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+          replace_call_with_call_and_fold (gsi, repl);
+          return true;
+        }
+    }
+
+  if (! tree_fits_uhwi_p (size))
+    return false;
+
+  tree maxlen = get_maxval_strlen (len, 2);
+  if (! integer_all_onesp (size))
+    {
+      if (! tree_fits_uhwi_p (len))
+       {
+         /* If LEN is not constant, try MAXLEN too.
+            For MAXLEN only allow optimizing into non-_ocs function
+            if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+         if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+           return false;
+       }
+      else
+       maxlen = len;
+
+      if (tree_int_cst_lt (size, maxlen))
+       return false;
+    }
+
+  /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available.  */
+  fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK
+                             ? BUILT_IN_STPNCPY : BUILT_IN_STRNCPY);
+  if (!fn)
+    return false;
+
+  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  replace_call_with_call_and_fold (gsi, repl);
+  return true;
+}
+
+/* Fold function call to builtin stpcpy with arguments DEST and SRC.
+   Return NULL_TREE if no simplification can be made.  */
+
+static bool
+gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  location_t loc = gimple_location (stmt);
+  tree dest = gimple_call_arg (stmt, 0);
+  tree src = gimple_call_arg (stmt, 1);
+  tree fn, len, lenp1;
+
+  /* If the result is unused, replace stpcpy with strcpy.  */
+  if (gimple_call_lhs (stmt) == NULL_TREE)
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      if (!fn)
+       return false;
+      gimple_call_set_fndecl (stmt, fn);
+      fold_stmt (gsi);
+      return true;
+    }
+
+  len = c_strlen (src, 1);
+  if (!len
+      || TREE_CODE (len) != INTEGER_CST)
+    return false;
+
+  if (optimize_function_for_size_p (cfun)
+      /* If length is zero it's small enough.  */
+      && !integer_zerop (len))
+    return false;
+
+  /* If the source has a known length replace stpcpy with memcpy.  */
+  fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+  if (!fn)
+    return false;
+
+  gimple_seq stmts = NULL;
+  tree tem = gimple_convert (&stmts, loc, size_type_node, len);
+  lenp1 = gimple_build (&stmts, loc, PLUS_EXPR, size_type_node,
+                       tem, build_int_cst (size_type_node, 1));
+  gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+  gcall *repl = gimple_build_call (fn, 3, dest, src, lenp1);
+  gimple_set_vuse (repl, gimple_vuse (stmt));
+  gimple_set_vdef (repl, gimple_vdef (stmt));
+  if (gimple_vdef (repl)
+      && TREE_CODE (gimple_vdef (repl)) == SSA_NAME)
+    SSA_NAME_DEF_STMT (gimple_vdef (repl)) = repl;
+  gsi_insert_before (gsi, repl, GSI_SAME_STMT);
+  /* Replace the result with dest + len.  */
+  stmts = NULL;
+  tem = gimple_convert (&stmts, loc, sizetype, len);
+  gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+  gassign *ret = gimple_build_assign (gimple_call_lhs (stmt),
+                                     POINTER_PLUS_EXPR, dest, tem);
+  gsi_replace (gsi, ret, true);
+  /* Finally fold the memcpy call.  */
+  gimple_stmt_iterator gsi2 = *gsi;
+  gsi_prev (&gsi2);
+  fold_stmt (&gsi2);
+  return true;
+}
+
+/* Fold a call EXP to {,v}snprintf having NARGS passed as ARGS.  Return
+   NULL_TREE if a normal call should be emitted rather than expanding
+   the function inline.  FCODE is either BUILT_IN_SNPRINTF_CHK or
+   BUILT_IN_VSNPRINTF_CHK.  If MAXLEN is not NULL, it is maximum length
+   passed as second argument.  */
+
+static bool
+gimple_fold_builtin_snprintf_chk (gimple_stmt_iterator *gsi,
+                                 enum built_in_function fcode)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree dest, size, len, fn, fmt, flag;
+  const char *fmt_str;
+
+  /* Verify the required arguments in the original call.  */
+  if (gimple_call_num_args (stmt) < 5)
+    return false;
+
+  dest = gimple_call_arg (stmt, 0);
+  len = gimple_call_arg (stmt, 1);
+  flag = gimple_call_arg (stmt, 2);
+  size = gimple_call_arg (stmt, 3);
+  fmt = gimple_call_arg (stmt, 4);
+
+  if (! tree_fits_uhwi_p (size))
+    return false;
+
+  if (! integer_all_onesp (size))
+    {
+      tree maxlen = get_maxval_strlen (len, 2);
+      if (! tree_fits_uhwi_p (len))
+       {
+         /* If LEN is not constant, try MAXLEN too.
+            For MAXLEN only allow optimizing into non-_ocs function
+            if SIZE is >= MAXLEN, never convert to __ocs_fail ().  */
+         if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen))
+           return false;
+       }
+      else
+       maxlen = len;
+
+      if (tree_int_cst_lt (size, maxlen))
+       return false;
+    }
+
+  if (!init_target_chars ())
+    return false;
+
+  /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0
+     or if format doesn't contain % chars or is "%s".  */
+  if (! integer_zerop (flag))
+    {
+      fmt_str = c_getstr (fmt);
+      if (fmt_str == NULL)
+       return false;
+      if (strchr (fmt_str, target_percent) != NULL
+         && strcmp (fmt_str, target_percent_s))
+       return false;
+    }
+
+  /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is
+     available.  */
+  fn = builtin_decl_explicit (fcode == BUILT_IN_VSNPRINTF_CHK
+                             ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF);
+  if (!fn)
+    return false;
+
+  /* Replace the called function and the first 5 argument by 3 retaining
+     trailing varargs.  */
+  gimple_call_set_fndecl (stmt, fn);
+  gimple_call_set_fntype (stmt, TREE_TYPE (fn));
+  gimple_call_set_arg (stmt, 0, dest);
+  gimple_call_set_arg (stmt, 1, len);
+  gimple_call_set_arg (stmt, 2, fmt);
+  for (unsigned i = 3; i < gimple_call_num_args (stmt) - 2; ++i)
+    gimple_call_set_arg (stmt, i, gimple_call_arg (stmt, i + 2));
+  gimple_set_num_ops (stmt, gimple_num_ops (stmt) - 2);
+  fold_stmt (gsi);
+  return true;
+}
+
+/* Fold a call EXP to __{,v}sprintf_chk having NARGS passed as ARGS.
+   Return NULL_TREE if a normal call should be emitted rather than
+   expanding the function inline.  FCODE is either BUILT_IN_SPRINTF_CHK
+   or BUILT_IN_VSPRINTF_CHK.  */
+
+static bool
+gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi,
+                                enum built_in_function fcode)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree dest, size, len, fn, fmt, flag;
+  const char *fmt_str;
+  unsigned nargs = gimple_call_num_args (stmt);
+
+  /* Verify the required arguments in the original call.  */
+  if (nargs < 4)
+    return false;
+  dest = gimple_call_arg (stmt, 0);
+  flag = gimple_call_arg (stmt, 1);
+  size = gimple_call_arg (stmt, 2);
+  fmt = gimple_call_arg (stmt, 3);
+
+  if (! tree_fits_uhwi_p (size))
+    return false;
+
+  len = NULL_TREE;
+
+  if (!init_target_chars ())
+    return false;
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str != NULL)
+    {
+      /* If the format doesn't contain % args or %%, we know the size.  */
+      if (strchr (fmt_str, target_percent) == 0)
+       {
+         if (fcode != BUILT_IN_SPRINTF_CHK || nargs == 4)
+           len = build_int_cstu (size_type_node, strlen (fmt_str));
+       }
+      /* If the format is "%s" and first ... argument is a string literal,
+        we know the size too.  */
+      else if (fcode == BUILT_IN_SPRINTF_CHK
+              && strcmp (fmt_str, target_percent_s) == 0)
+       {
+         tree arg;
+
+         if (nargs == 5)
+           {
+             arg = gimple_call_arg (stmt, 4);
+             if (POINTER_TYPE_P (TREE_TYPE (arg)))
+               {
+                 len = c_strlen (arg, 1);
+                 if (! len || ! tree_fits_uhwi_p (len))
+                   len = NULL_TREE;
+               }
+           }
+       }
+    }
+
+  if (! integer_all_onesp (size))
+    {
+      if (! len || ! tree_int_cst_lt (len, size))
+       return false;
+    }
+
+  /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0
+     or if format doesn't contain % chars or is "%s".  */
+  if (! integer_zerop (flag))
+    {
+      if (fmt_str == NULL)
+       return false;
+      if (strchr (fmt_str, target_percent) != NULL
+         && strcmp (fmt_str, target_percent_s))
+       return false;
+    }
+
+  /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available.  */
+  fn = builtin_decl_explicit (fcode == BUILT_IN_VSPRINTF_CHK
+                             ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF);
+  if (!fn)
+    return false;
+
+  /* Replace the called function and the first 4 argument by 2 retaining
+     trailing varargs.  */
+  gimple_call_set_fndecl (stmt, fn);
+  gimple_call_set_fntype (stmt, TREE_TYPE (fn));
+  gimple_call_set_arg (stmt, 0, dest);
+  gimple_call_set_arg (stmt, 1, fmt);
+  for (unsigned i = 2; i < gimple_call_num_args (stmt) - 2; ++i)
+    gimple_call_set_arg (stmt, i, gimple_call_arg (stmt, i + 2));
+  gimple_set_num_ops (stmt, gimple_num_ops (stmt) - 2);
+  fold_stmt (gsi);
+  return true;
+}
+
+/* Simplify a call to the sprintf builtin with arguments DEST, FMT, and ORIG.
+   ORIG may be null if this is a 2-argument call.  We don't attempt to
+   simplify calls with more than 3 arguments.
+
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.  If IGNORED is true, it means that
+   the caller does not use the returned value of the function.  */
+
+static bool
+gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree dest = gimple_call_arg (stmt, 0);
+  tree fmt = gimple_call_arg (stmt, 1);
+  tree orig = NULL_TREE;
+  const char *fmt_str = NULL;
+
+  /* Verify the required arguments in the original call.  We deal with two
+     types of sprintf() calls: 'sprintf (str, fmt)' and
+     'sprintf (dest, "%s", orig)'.  */
+  if (gimple_call_num_args (stmt) > 3)
+    return false;
+
+  if (gimple_call_num_args (stmt) == 3)
+    orig = gimple_call_arg (stmt, 2);
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return false;
+
+  if (!init_target_chars ())
+    return false;
+
+  /* If the format doesn't contain % args or %%, use strcpy.  */
+  if (strchr (fmt_str, target_percent) == NULL)
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+
+      if (!fn)
+       return false;
+
+      /* Don't optimize sprintf (buf, "abc", ptr++).  */
+      if (orig)
+       return false;
+
+      /* Convert sprintf (str, fmt) into strcpy (str, fmt) when
+        'format' is known to contain no % formats.  */
+      gimple_seq stmts = NULL;
+      gimple repl = gimple_build_call (fn, 2, dest, fmt);
+      gimple_seq_add_stmt_without_update (&stmts, repl);
+      if (gimple_call_lhs (stmt))
+       {
+         repl = gimple_build_assign (gimple_call_lhs (stmt),
+                                     build_int_cst (integer_type_node,
+                                                    strlen (fmt_str)));
+         gimple_seq_add_stmt_without_update (&stmts, repl);
+         gsi_replace_with_seq_vops (gsi, stmts);
+         /* gsi now points at the assignment to the lhs, get a
+            stmt iterator to the memcpy call.
+            ???  We can't use gsi_for_stmt as that doesn't work when the
+            CFG isn't built yet.  */
+         gimple_stmt_iterator gsi2 = *gsi;
+         gsi_prev (&gsi2);
+         fold_stmt (&gsi2);
+       }
+      else
+       {
+         gsi_replace_with_seq_vops (gsi, stmts);
+         fold_stmt (gsi);
+       }
+      return true;
+    }
+
+  /* If the format is "%s", use strcpy if the result isn't used.  */
+  else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0)
+    {
+      tree fn;
+      fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+
+      if (!fn)
+       return false;
+
+      /* Don't crash on sprintf (str1, "%s").  */
+      if (!orig)
+       return false;
+
+      tree orig_len = NULL_TREE;
+      if (gimple_call_lhs (stmt))
+       {
+         orig_len = get_maxval_strlen (orig, 0);
+         if (!orig_len)
+           return false;
+       }
+
+      /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2).  */
+      gimple_seq stmts = NULL;
+      gimple repl = gimple_build_call (fn, 2, dest, orig);
+      gimple_seq_add_stmt_without_update (&stmts, repl);
+      if (gimple_call_lhs (stmt))
+       {
+         if (!useless_type_conversion_p (integer_type_node,
+                                         TREE_TYPE (orig_len)))
+           orig_len = fold_convert (integer_type_node, orig_len);
+         repl = gimple_build_assign (gimple_call_lhs (stmt), orig_len);
+         gimple_seq_add_stmt_without_update (&stmts, repl);
+         gsi_replace_with_seq_vops (gsi, stmts);
+         /* gsi now points at the assignment to the lhs, get a
+            stmt iterator to the memcpy call.
+            ???  We can't use gsi_for_stmt as that doesn't work when the
+            CFG isn't built yet.  */
+         gimple_stmt_iterator gsi2 = *gsi;
+         gsi_prev (&gsi2);
+         fold_stmt (&gsi2);
+       }
+      else
+       {
+         gsi_replace_with_seq_vops (gsi, stmts);
+         fold_stmt (gsi);
+       }
+      return true;
+    }
+  return false;
+}
+
+/* Simplify a call to the snprintf builtin with arguments DEST, DESTSIZE,
+   FMT, and ORIG.  ORIG may be null if this is a 3-argument call.  We don't
+   attempt to simplify calls with more than 4 arguments.
+
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.  If IGNORED is true, it means that
+   the caller does not use the returned value of the function.  */
+
+static bool
+gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree dest = gimple_call_arg (stmt, 0);
+  tree destsize = gimple_call_arg (stmt, 1);
+  tree fmt = gimple_call_arg (stmt, 2);
+  tree orig = NULL_TREE;
+  const char *fmt_str = NULL;
+
+  if (gimple_call_num_args (stmt) > 4)
+    return false;
+
+  if (gimple_call_num_args (stmt) == 4)
+    orig = gimple_call_arg (stmt, 3);
+
+  if (!tree_fits_uhwi_p (destsize))
+    return false;
+  unsigned HOST_WIDE_INT destlen = tree_to_uhwi (destsize);
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return false;
+
+  if (!init_target_chars ())
+    return false;
+
+  /* If the format doesn't contain % args or %%, use strcpy.  */
+  if (strchr (fmt_str, target_percent) == NULL)
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      if (!fn)
+       return false;
+
+      /* Don't optimize snprintf (buf, 4, "abc", ptr++).  */
+      if (orig)
+       return false;
+
+      /* We could expand this as
+        memcpy (str, fmt, cst - 1); str[cst - 1] = '\0';
+        or to
+        memcpy (str, fmt_with_nul_at_cstm1, cst);
+        but in the former case that might increase code size
+        and in the latter case grow .rodata section too much.
+        So punt for now.  */
+      size_t len = strlen (fmt_str);
+      if (len >= destlen)
+       return false;
+
+      gimple_seq stmts = NULL;
+      gimple repl = gimple_build_call (fn, 2, dest, fmt);
+      gimple_seq_add_stmt_without_update (&stmts, repl);
+      if (gimple_call_lhs (stmt))
+       {
+         repl = gimple_build_assign (gimple_call_lhs (stmt),
+                                     build_int_cst (integer_type_node, len));
+         gimple_seq_add_stmt_without_update (&stmts, repl);
+         gsi_replace_with_seq_vops (gsi, stmts);
+         /* gsi now points at the assignment to the lhs, get a
+            stmt iterator to the memcpy call.
+            ???  We can't use gsi_for_stmt as that doesn't work when the
+            CFG isn't built yet.  */
+         gimple_stmt_iterator gsi2 = *gsi;
+         gsi_prev (&gsi2);
+         fold_stmt (&gsi2);
+       }
+      else
+       {
+         gsi_replace_with_seq_vops (gsi, stmts);
+         fold_stmt (gsi);
+       }
+      return true;
+    }
+
+  /* If the format is "%s", use strcpy if the result isn't used.  */
+  else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0)
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_STRCPY);
+      if (!fn)
+       return false;
+
+      /* Don't crash on snprintf (str1, cst, "%s").  */
+      if (!orig)
+       return false;
+
+      tree orig_len = get_maxval_strlen (orig, 0);
+      if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
+       return false;
+
+      /* We could expand this as
+        memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0';
+        or to
+        memcpy (str1, str2_with_nul_at_cstm1, cst);
+        but in the former case that might increase code size
+        and in the latter case grow .rodata section too much.
+        So punt for now.  */
+      if (compare_tree_int (orig_len, destlen) >= 0)
+       return false;
+
+      /* Convert snprintf (str1, cst, "%s", str2) into
+        strcpy (str1, str2) if strlen (str2) < cst.  */
+      gimple_seq stmts = NULL;
+      gimple repl = gimple_build_call (fn, 2, dest, orig);
+      gimple_seq_add_stmt_without_update (&stmts, repl);
+      if (gimple_call_lhs (stmt))
+       {
+         if (!useless_type_conversion_p (integer_type_node,
+                                         TREE_TYPE (orig_len)))
+           orig_len = fold_convert (integer_type_node, orig_len);
+         repl = gimple_build_assign (gimple_call_lhs (stmt), orig_len);
+         gimple_seq_add_stmt_without_update (&stmts, repl);
+         gsi_replace_with_seq_vops (gsi, stmts);
+         /* gsi now points at the assignment to the lhs, get a
+            stmt iterator to the memcpy call.
+            ???  We can't use gsi_for_stmt as that doesn't work when the
+            CFG isn't built yet.  */
+         gimple_stmt_iterator gsi2 = *gsi;
+         gsi_prev (&gsi2);
+         fold_stmt (&gsi2);
+       }
+      else
+       {
+         gsi_replace_with_seq_vops (gsi, stmts);
+         fold_stmt (gsi);
+       }
+      return true;
+    }
+  return false;
+}
+
+/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins.
+   FP, FMT, and ARG are the arguments to the call.  We don't fold calls with
+   more than 3 arguments, and ARG may be null in the 2-argument case.
+
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.  FCODE is the BUILT_IN_*
+   code of the function to be simplified.  */
+
+static bool 
+gimple_fold_builtin_fprintf (gimple_stmt_iterator *gsi,
+                            tree fp, tree fmt, tree arg,
+                            enum built_in_function fcode)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree fn_fputc, fn_fputs;
+  const char *fmt_str = NULL;
+
+  /* If the return value is used, don't do the transformation.  */
+  if (gimple_call_lhs (stmt) != NULL_TREE)
+    return false;
+
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return false;
+
+  if (fcode == BUILT_IN_FPRINTF_UNLOCKED)
+    {
+      /* If we're using an unlocked function, assume the other
+        unlocked functions exist explicitly.  */
+      fn_fputc = builtin_decl_explicit (BUILT_IN_FPUTC_UNLOCKED);
+      fn_fputs = builtin_decl_explicit (BUILT_IN_FPUTS_UNLOCKED);
+    }
+  else
+    {
+      fn_fputc = builtin_decl_implicit (BUILT_IN_FPUTC);
+      fn_fputs = builtin_decl_implicit (BUILT_IN_FPUTS);
+    }
 
-  switch (gimple_code (def_stmt))
+  if (!init_target_chars ())
+    return false;
+
+  /* If the format doesn't contain % args or %%, use strcpy.  */
+  if (strchr (fmt_str, target_percent) == NULL)
     {
-      case GIMPLE_ASSIGN:
-        /* The RHS of the statement defining VAR must either have a
-           constant length or come from another SSA_NAME with a constant
-           length.  */
-        if (gimple_assign_single_p (def_stmt)
-            || gimple_assign_unary_nop_p (def_stmt))
-          {
-            tree rhs = gimple_assign_rhs1 (def_stmt);
-            return get_maxval_strlen (rhs, length, visited, type);
-          }
-       else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
-         {
-           tree op2 = gimple_assign_rhs2 (def_stmt);
-           tree op3 = gimple_assign_rhs3 (def_stmt);
-           return get_maxval_strlen (op2, length, visited, type)
-                  && get_maxval_strlen (op3, length, visited, type);
-          }
-        return false;
+      if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK
+         && arg)
+       return false;
 
-      case GIMPLE_PHI:
+      /* If the format specifier was "", fprintf does nothing.  */
+      if (fmt_str[0] == '\0')
        {
-         /* All the arguments of the PHI node must have the same constant
-            length.  */
-         unsigned i;
+         replace_call_with_value (gsi, NULL_TREE);
+         return true;
+       }
 
-         for (i = 0; i < gimple_phi_num_args (def_stmt); i++)
-          {
-            tree arg = gimple_phi_arg (def_stmt, i)->def;
+      /* When "string" doesn't contain %, replace all cases of
+        fprintf (fp, string) with fputs (string, fp).  The fputs
+        builtin will take care of special cases like length == 1.  */
+      if (fn_fputs)
+       {
+         gcall *repl = gimple_build_call (fn_fputs, 2, fmt, fp);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+    }
 
-            /* If this PHI has itself as an argument, we cannot
-               determine the string length of this argument.  However,
-               if we can find a constant string length for the other
-               PHI args then we can still be sure that this is a
-               constant string length.  So be optimistic and just
-               continue with the next argument.  */
-            if (arg == gimple_phi_result (def_stmt))
-              continue;
+  /* The other optimizations can be done only on the non-va_list variants.  */
+  else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK)
+    return false;
 
-            if (!get_maxval_strlen (arg, length, visited, type))
-              return false;
-          }
-        }
-        return true;
+  /* If the format specifier was "%s", call __builtin_fputs (arg, fp).  */
+  else if (strcmp (fmt_str, target_percent_s) == 0)
+    {
+      if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+       return false;
+      if (fn_fputs)
+       {
+         gcall *repl = gimple_build_call (fn_fputs, 2, arg, fp);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+    }
 
-      default:
-        return false;
+  /* If the format specifier was "%c", call __builtin_fputc (arg, fp).  */
+  else if (strcmp (fmt_str, target_percent_c) == 0)
+    {
+      if (!arg
+         || ! useless_type_conversion_p (integer_type_node, TREE_TYPE (arg)))
+       return false;
+      if (fn_fputc)
+       {
+         gcall *repl = gimple_build_call (fn_fputc, 2, arg, fp);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
     }
+
+  return false;
 }
 
+/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins.
+   FMT and ARG are the arguments to the call; we don't fold cases with
+   more than 2 arguments, and ARG may be null if this is a 1-argument case.
 
-/* Fold builtin call in statement STMT.  Returns a simplified tree.
-   We may return a non-constant expression, including another call
-   to a different function and with different arguments, e.g.,
-   substituting memcpy for strcpy when the string length is known.
-   Note that some builtins expand into inline code that may not
-   be valid in GIMPLE.  Callers must take care.  */
+   Return NULL_TREE if no simplification was possible, otherwise return the
+   simplified form of the call as a tree.  FCODE is the BUILT_IN_*
+   code of the function to be simplified.  */
 
-tree
-gimple_fold_builtin (gimple stmt)
-{
-  tree result, val[3];
-  tree callee, a;
-  int arg_idx, type;
-  bitmap visited;
-  bool ignore;
-  int nargs;
-  location_t loc = gimple_location (stmt);
+static bool
+gimple_fold_builtin_printf (gimple_stmt_iterator *gsi, tree fmt,
+                           tree arg, enum built_in_function fcode)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree fn_putchar, fn_puts, newarg;
+  const char *fmt_str = NULL;
 
-  ignore = (gimple_call_lhs (stmt) == NULL);
+  /* If the return value is used, don't do the transformation.  */
+  if (gimple_call_lhs (stmt) != NULL_TREE)
+    return false;
 
-  /* First try the generic builtin folder.  If that succeeds, return the
-     result directly.  */
-  result = fold_call_stmt (stmt, ignore);
-  if (result)
+  /* Check whether the format is a literal string constant.  */
+  fmt_str = c_getstr (fmt);
+  if (fmt_str == NULL)
+    return false;
+
+  if (fcode == BUILT_IN_PRINTF_UNLOCKED)
     {
-      if (ignore)
-       STRIP_NOPS (result);
-      else
-       result = fold_convert (gimple_call_return_type (stmt), result);
-      return result;
+      /* If we're using an unlocked function, assume the other
+        unlocked functions exist explicitly.  */
+      fn_putchar = builtin_decl_explicit (BUILT_IN_PUTCHAR_UNLOCKED);
+      fn_puts = builtin_decl_explicit (BUILT_IN_PUTS_UNLOCKED);
+    }
+  else
+    {
+      fn_putchar = builtin_decl_implicit (BUILT_IN_PUTCHAR);
+      fn_puts = builtin_decl_implicit (BUILT_IN_PUTS);
     }
 
-  /* Ignore MD builtins.  */
-  callee = gimple_call_fndecl (stmt);
-  if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD)
-    return NULL_TREE;
+  if (!init_target_chars ())
+    return false;
 
-  /* Give up for always_inline inline builtins until they are
-     inlined.  */
-  if (avoid_folding_inline_builtin (callee))
-    return NULL_TREE;
+  if (strcmp (fmt_str, target_percent_s) == 0
+      || strchr (fmt_str, target_percent) == NULL)
+    {
+      const char *str;
 
-  /* If the builtin could not be folded, and it has no argument list,
-     we're done.  */
-  nargs = gimple_call_num_args (stmt);
-  if (nargs == 0)
-    return NULL_TREE;
+      if (strcmp (fmt_str, target_percent_s) == 0)
+       {
+         if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+           return false;
 
-  /* Limit the work only for builtins we know how to simplify.  */
-  switch (DECL_FUNCTION_CODE (callee))
-    {
-    case BUILT_IN_STRLEN:
-    case BUILT_IN_FPUTS:
-    case BUILT_IN_FPUTS_UNLOCKED:
-      arg_idx = 0;
-      type = 0;
-      break;
-    case BUILT_IN_STRCPY:
-    case BUILT_IN_STRNCPY:
-    case BUILT_IN_STRCAT:
-      arg_idx = 1;
-      type = 0;
-      break;
-    case BUILT_IN_MEMCPY_CHK:
-    case BUILT_IN_MEMPCPY_CHK:
-    case BUILT_IN_MEMMOVE_CHK:
-    case BUILT_IN_MEMSET_CHK:
-    case BUILT_IN_STRNCPY_CHK:
-    case BUILT_IN_STPNCPY_CHK:
-      arg_idx = 2;
-      type = 2;
-      break;
-    case BUILT_IN_STRCPY_CHK:
-    case BUILT_IN_STPCPY_CHK:
-      arg_idx = 1;
-      type = 1;
-      break;
-    case BUILT_IN_SNPRINTF_CHK:
-    case BUILT_IN_VSNPRINTF_CHK:
-      arg_idx = 1;
-      type = 2;
-      break;
-    default:
-      return NULL_TREE;
-    }
+         if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+           return false;
 
-  if (arg_idx >= nargs)
-    return NULL_TREE;
+         str = c_getstr (arg);
+         if (str == NULL)
+           return false;
+       }
+      else
+       {
+         /* The format specifier doesn't contain any '%' characters.  */
+         if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK
+             && arg)
+           return false;
+         str = fmt_str;
+       }
 
-  /* Try to use the dataflow information gathered by the CCP process.  */
-  visited = BITMAP_ALLOC (NULL);
-  bitmap_clear (visited);
+      /* If the string was "", printf does nothing.  */
+      if (str[0] == '\0')
+       {
+         replace_call_with_value (gsi, NULL_TREE);
+         return true;
+       }
 
-  memset (val, 0, sizeof (val));
-  a = gimple_call_arg (stmt, arg_idx);
-  if (!get_maxval_strlen (a, &val[arg_idx], visited, type))
-    val[arg_idx] = NULL_TREE;
+      /* If the string has length of 1, call putchar.  */
+      if (str[1] == '\0')
+       {
+         /* Given printf("c"), (where c is any one character,)
+            convert "c"[0] to an int and pass that to the replacement
+            function.  */
+         newarg = build_int_cst (integer_type_node, str[0]);
+         if (fn_putchar)
+           {
+             gcall *repl = gimple_build_call (fn_putchar, 1, newarg);
+             replace_call_with_call_and_fold (gsi, repl);
+             return true;
+           }
+       }
+      else
+       {
+         /* If the string was "string\n", call puts("string").  */
+         size_t len = strlen (str);
+         if ((unsigned char)str[len - 1] == target_newline
+             && (size_t) (int) len == len
+             && (int) len > 0)
+           {
+             char *newstr;
+             tree offset_node, string_cst;
+
+             /* Create a NUL-terminated string that's one char shorter
+                than the original, stripping off the trailing '\n'.  */
+             newarg = build_string_literal (len, str);
+             string_cst = string_constant (newarg, &offset_node);
+             gcc_checking_assert (string_cst
+                                  && (TREE_STRING_LENGTH (string_cst)
+                                      == (int) len)
+                                  && integer_zerop (offset_node)
+                                  && (unsigned char)
+                                     TREE_STRING_POINTER (string_cst)[len - 1]
+                                     == target_newline);
+             /* build_string_literal creates a new STRING_CST,
+                modify it in place to avoid double copying.  */
+             newstr = CONST_CAST (char *, TREE_STRING_POINTER (string_cst));
+             newstr[len - 1] = '\0';
+             if (fn_puts)
+               {
+                 gcall *repl = gimple_build_call (fn_puts, 1, newarg);
+                 replace_call_with_call_and_fold (gsi, repl);
+                 return true;
+               }
+           }
+         else
+           /* We'd like to arrange to call fputs(string,stdout) here,
+              but we need stdout and don't have a way to get it yet.  */
+           return false;
+       }
+    }
 
-  BITMAP_FREE (visited);
+  /* The other optimizations can be done only on the non-va_list variants.  */
+  else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+    return false;
 
-  result = NULL_TREE;
-  switch (DECL_FUNCTION_CODE (callee))
+  /* If the format specifier was "%s\n", call __builtin_puts(arg).  */
+  else if (strcmp (fmt_str, target_percent_s_newline) == 0)
     {
-    case BUILT_IN_STRLEN:
-      if (val[0] && nargs == 1)
+      if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+       return false;
+      if (fn_puts)
        {
-         tree new_val =
-              fold_convert (TREE_TYPE (gimple_call_lhs (stmt)), val[0]);
+         gcall *repl = gimple_build_call (fn_puts, 1, arg);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+    }
 
-         /* If the result is not a valid gimple value, or not a cast
-            of a valid gimple value, then we cannot use the result.  */
-         if (is_gimple_val (new_val)
-             || (CONVERT_EXPR_P (new_val)
-                 && is_gimple_val (TREE_OPERAND (new_val, 0))))
-           return new_val;
+  /* If the format specifier was "%c", call __builtin_putchar(arg).  */
+  else if (strcmp (fmt_str, target_percent_c) == 0)
+    {
+      if (!arg || ! useless_type_conversion_p (integer_type_node,
+                                              TREE_TYPE (arg)))
+       return false;
+      if (fn_putchar)
+       {
+         gcall *repl = gimple_build_call (fn_putchar, 1, arg);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
        }
-      break;
+    }
 
-    case BUILT_IN_STRCPY:
-      if (val[1] && is_gimple_val (val[1]) && nargs == 2)
-       result = fold_builtin_strcpy (loc, callee,
-                                      gimple_call_arg (stmt, 0),
-                                      gimple_call_arg (stmt, 1),
-                                     val[1]);
-      break;
+  return false;
+}
 
-    case BUILT_IN_STRNCPY:
-      if (val[1] && is_gimple_val (val[1]) && nargs == 3)
-       result = fold_builtin_strncpy (loc, callee,
-                                       gimple_call_arg (stmt, 0),
-                                       gimple_call_arg (stmt, 1),
-                                       gimple_call_arg (stmt, 2),
-                                      val[1]);
-      break;
 
-    case BUILT_IN_STRCAT:
-      if (val[1] && is_gimple_val (val[1]) && nargs == 2)
-       result = fold_builtin_strcat (loc, gimple_call_arg (stmt, 0),
-                                     gimple_call_arg (stmt, 1),
-                                     val[1]);
-      break;
 
-    case BUILT_IN_FPUTS:
-      if (nargs == 2)
-       result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
-                                    gimple_call_arg (stmt, 1),
-                                    ignore, false, val[0]);
-      break;
+/* Fold a call to __builtin_strlen with known length LEN.  */
 
-    case BUILT_IN_FPUTS_UNLOCKED:
-      if (nargs == 2)
-       result = fold_builtin_fputs (loc, gimple_call_arg (stmt, 0),
-                                    gimple_call_arg (stmt, 1),
-                                    ignore, true, val[0]);
-      break;
+static bool
+gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree len = get_maxval_strlen (gimple_call_arg (stmt, 0), 0);
+  if (!len)
+    return false;
+  len = force_gimple_operand_gsi (gsi, len, true, NULL, true, GSI_SAME_STMT);
+  replace_call_with_value (gsi, len);
+  return true;
+}
+
+
+/* Fold the non-target builtin at *GSI and return whether any simplification
+   was made.  */
 
+static bool
+gimple_fold_builtin (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *>(gsi_stmt (*gsi));
+  tree callee = gimple_call_fndecl (stmt);
+
+  /* Give up for always_inline inline builtins until they are
+     inlined.  */
+  if (avoid_folding_inline_builtin (callee))
+    return false;
+
+  unsigned n = gimple_call_num_args (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+  switch (fcode)
+    {
+    case BUILT_IN_BZERO:
+      return gimple_fold_builtin_memset (gsi, integer_zero_node,
+                                        gimple_call_arg (stmt, 1));
+    case BUILT_IN_MEMSET:
+      return gimple_fold_builtin_memset (gsi,
+                                        gimple_call_arg (stmt, 1),
+                                        gimple_call_arg (stmt, 2));
+    case BUILT_IN_BCOPY:
+      return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 1),
+                                           gimple_call_arg (stmt, 0), 3);
+    case BUILT_IN_MEMCPY:
+      return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+                                           gimple_call_arg (stmt, 1), 0);
+    case BUILT_IN_MEMPCPY:
+      return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+                                           gimple_call_arg (stmt, 1), 1);
+    case BUILT_IN_MEMMOVE:
+      return gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+                                           gimple_call_arg (stmt, 1), 3);
+    case BUILT_IN_SPRINTF_CHK:
+    case BUILT_IN_VSPRINTF_CHK:
+      return gimple_fold_builtin_sprintf_chk (gsi, fcode);
+    case BUILT_IN_STRCAT_CHK:
+      return gimple_fold_builtin_strcat_chk (gsi);
+    case BUILT_IN_STRNCAT_CHK:
+      return gimple_fold_builtin_strncat_chk (gsi);
+    case BUILT_IN_STRLEN:
+      return gimple_fold_builtin_strlen (gsi);
+    case BUILT_IN_STRCPY:
+      return gimple_fold_builtin_strcpy (gsi,
+                                        gimple_call_arg (stmt, 0),
+                                        gimple_call_arg (stmt, 1));
+    case BUILT_IN_STRNCPY:
+      return gimple_fold_builtin_strncpy (gsi,
+                                         gimple_call_arg (stmt, 0),
+                                         gimple_call_arg (stmt, 1),
+                                         gimple_call_arg (stmt, 2));
+    case BUILT_IN_STRCAT:
+      return gimple_fold_builtin_strcat (gsi, gimple_call_arg (stmt, 0),
+                                        gimple_call_arg (stmt, 1));
+    case BUILT_IN_STRNCAT:
+      return gimple_fold_builtin_strncat (gsi);
+    case BUILT_IN_FPUTS:
+      return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
+                                       gimple_call_arg (stmt, 1), false);
+    case BUILT_IN_FPUTS_UNLOCKED:
+      return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
+                                       gimple_call_arg (stmt, 1), true);
     case BUILT_IN_MEMCPY_CHK:
     case BUILT_IN_MEMPCPY_CHK:
     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 (loc, callee,
-                                          gimple_call_arg (stmt, 0),
-                                          gimple_call_arg (stmt, 1),
-                                          gimple_call_arg (stmt, 2),
-                                          gimple_call_arg (stmt, 3),
-                                         val[2], ignore,
-                                         DECL_FUNCTION_CODE (callee));
-      break;
-
+      return gimple_fold_builtin_memory_chk (gsi,
+                                            gimple_call_arg (stmt, 0),
+                                            gimple_call_arg (stmt, 1),
+                                            gimple_call_arg (stmt, 2),
+                                            gimple_call_arg (stmt, 3),
+                                            fcode);
+    case BUILT_IN_STPCPY:
+      return gimple_fold_builtin_stpcpy (gsi);
     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 (loc, callee,
-                                          gimple_call_arg (stmt, 0),
-                                          gimple_call_arg (stmt, 1),
-                                          gimple_call_arg (stmt, 2),
-                                         val[1], ignore,
-                                         DECL_FUNCTION_CODE (callee));
-      break;
-
+      return gimple_fold_builtin_stxcpy_chk (gsi,
+                                            gimple_call_arg (stmt, 0),
+                                            gimple_call_arg (stmt, 1),
+                                            gimple_call_arg (stmt, 2),
+                                            fcode);
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
-      if (val[2] && is_gimple_val (val[2]) && nargs == 4)
-       result = fold_builtin_stxncpy_chk (loc, gimple_call_arg (stmt, 0),
-                                           gimple_call_arg (stmt, 1),
-                                           gimple_call_arg (stmt, 2),
-                                           gimple_call_arg (stmt, 3),
-                                          val[2], ignore,
-                                          DECL_FUNCTION_CODE (callee));
-      break;
-
+      return gimple_fold_builtin_stxncpy_chk (gsi,
+                                             gimple_call_arg (stmt, 0),
+                                             gimple_call_arg (stmt, 1),
+                                             gimple_call_arg (stmt, 2),
+                                             gimple_call_arg (stmt, 3),
+                                             fcode);
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      if (val[1] && is_gimple_val (val[1]))
-       result = gimple_fold_builtin_snprintf_chk (stmt, val[1],
-                                                   DECL_FUNCTION_CODE (callee));
+      return gimple_fold_builtin_snprintf_chk (gsi, fcode);
+    case BUILT_IN_SNPRINTF:
+      return gimple_fold_builtin_snprintf (gsi);
+    case BUILT_IN_SPRINTF:
+      return gimple_fold_builtin_sprintf (gsi);
+    case BUILT_IN_FPRINTF:
+    case BUILT_IN_FPRINTF_UNLOCKED:
+    case BUILT_IN_VFPRINTF:
+      if (n == 2 || n == 3)
+       return gimple_fold_builtin_fprintf (gsi,
+                                           gimple_call_arg (stmt, 0),
+                                           gimple_call_arg (stmt, 1),
+                                           n == 3
+                                           ? gimple_call_arg (stmt, 2)
+                                           : NULL_TREE,
+                                           fcode);
+      break;
+    case BUILT_IN_FPRINTF_CHK:
+    case BUILT_IN_VFPRINTF_CHK:
+      if (n == 3 || n == 4)
+       return gimple_fold_builtin_fprintf (gsi,
+                                           gimple_call_arg (stmt, 0),
+                                           gimple_call_arg (stmt, 2),
+                                           n == 4
+                                           ? gimple_call_arg (stmt, 3)
+                                           : NULL_TREE,
+                                           fcode);
       break;
+    case BUILT_IN_PRINTF:
+    case BUILT_IN_PRINTF_UNLOCKED:
+    case BUILT_IN_VPRINTF:
+      if (n == 1 || n == 2)
+       return gimple_fold_builtin_printf (gsi, gimple_call_arg (stmt, 0),
+                                          n == 2
+                                          ? gimple_call_arg (stmt, 1)
+                                          : NULL_TREE, fcode);
+      break;
+    case BUILT_IN_PRINTF_CHK:
+    case BUILT_IN_VPRINTF_CHK:
+      if (n == 2 || n == 3)
+       return gimple_fold_builtin_printf (gsi, gimple_call_arg (stmt, 1),
+                                          n == 3
+                                          ? gimple_call_arg (stmt, 2)
+                                          : NULL_TREE, fcode);
+    default:;
+    }
 
-    default:
-      gcc_unreachable ();
+  /* Try the generic builtin folder.  */
+  bool ignore = (gimple_call_lhs (stmt) == NULL);
+  tree result = fold_call_stmt (stmt, ignore);
+  if (result)
+    {
+      if (ignore)
+       STRIP_NOPS (result);
+      else
+       result = fold_convert (gimple_call_return_type (stmt), result);
+      if (!update_call_from_tree (gsi, result))
+       gimplify_and_update_call_from_tree (gsi, result);
+      return true;
     }
 
-  if (result && ignore)
-    result = fold_ignored_result (result);
-  return result;
+  return false;
 }
 
+/* Return true if ARG0 CODE ARG1 in infinite signed precision operation
+   doesn't fit into TYPE.  The test for overflow should be regardless of
+   -fwrapv, and even for unsigned types.  */
+
+bool
+arith_overflowed_p (enum tree_code code, const_tree type,
+                   const_tree arg0, const_tree arg1)
+{
+  typedef FIXED_WIDE_INT (WIDE_INT_MAX_PRECISION * 2) widest2_int;
+  typedef generic_wide_int <wi::extended_tree <WIDE_INT_MAX_PRECISION * 2> >
+    widest2_int_cst;
+  widest2_int warg0 = widest2_int_cst (arg0);
+  widest2_int warg1 = widest2_int_cst (arg1);
+  widest2_int wres;
+  switch (code)
+    {
+    case PLUS_EXPR: wres = wi::add (warg0, warg1); break;
+    case MINUS_EXPR: wres = wi::sub (warg0, warg1); break;
+    case MULT_EXPR: wres = wi::mul (warg0, warg1); break;
+    default: gcc_unreachable ();
+    }
+  signop sign = TYPE_SIGN (type);
+  if (sign == UNSIGNED && wi::neg_p (wres))
+    return true;
+  return wi::min_precision (wres, sign) > TYPE_PRECISION (type);
+}
 
 /* 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
@@ -1082,7 +3033,7 @@ gimple_fold_builtin (gimple stmt)
 static bool
 gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
   tree callee;
   bool changed = false;
   unsigned i;
@@ -1107,8 +3058,8 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
        {
           if (dump_file && virtual_method_call_p (callee)
              && !possible_polymorphic_call_target_p
-                   (callee, cgraph_node::get (gimple_call_addr_fndecl
-                                              (OBJ_TYPE_REF_EXPR (callee)))))
+                   (callee, stmt, cgraph_node::get (gimple_call_addr_fndecl
+                                                    (OBJ_TYPE_REF_EXPR (callee)))))
            {
              fprintf (dump_file,
                       "Type inheritance inconsistent devirtualization of ");
@@ -1147,13 +3098,14 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                    {
                      if (TREE_CODE (lhs) == SSA_NAME)
                        {
-                         tree var = create_tmp_var (TREE_TYPE (lhs), NULL);
+                         tree var = create_tmp_var (TREE_TYPE (lhs));
                          tree def = get_or_create_ssa_default_def (cfun, var);
                          gimple new_stmt = gimple_build_assign (lhs, def);
                          gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
                        }
                      gimple_call_set_lhs (stmt, NULL_TREE);
                    }
+                 maybe_remove_unused_call_args (cfun, stmt);
                }
              else
                {
@@ -1162,7 +3114,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                  gimple_set_location (new_stmt, gimple_location (stmt));
                  if (lhs && TREE_CODE (lhs) == SSA_NAME)
                    {
-                     tree var = create_tmp_var (TREE_TYPE (lhs), NULL);
+                     tree var = create_tmp_var (TREE_TYPE (lhs));
                      tree def = get_or_create_ssa_default_def (cfun, var);
 
                      /* To satisfy condition for
@@ -1173,34 +3125,58 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                      gsi_insert_before (gsi, new_stmt, GSI_NEW_STMT);
                    }
                  else
-                   gsi_replace (gsi, new_stmt, true);
+                   {
+                     gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+                     gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+                     gsi_replace (gsi, new_stmt, false);
+                   }
                  return true;
                }
            }
        }
     }
 
+  /* Check for indirect calls that became direct calls, and then
+     no longer require a static chain.  */
+  if (gimple_call_chain (stmt))
+    {
+      tree fn = gimple_call_fndecl (stmt);
+      if (fn && !DECL_STATIC_CHAIN (fn))
+       {
+         gimple_call_set_chain (stmt, NULL);
+         changed = true;
+       }
+      else
+       {
+         tree tmp = maybe_fold_reference (gimple_call_chain (stmt), false);
+         if (tmp)
+           {
+             gimple_call_set_chain (stmt, tmp);
+             changed = true;
+           }
+       }
+    }
+
   if (inplace)
     return changed;
 
   /* Check for builtins that CCP can handle using information not
      available in the generic fold routines.  */
-  if (gimple_call_builtin_p (stmt))
+  if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    {
+      if (gimple_fold_builtin (gsi))
+        changed = true;
+    }
+  else if (gimple_call_builtin_p (stmt, BUILT_IN_MD))
     {
-      tree result = gimple_fold_builtin (stmt);
-      if (result)
-       {
-          if (!update_call_from_tree (gsi, result))
-           gimplify_and_update_call_from_tree (gsi, result);
-         changed = true;
-       }
-      else if (gimple_call_builtin_p (stmt, BUILT_IN_MD))
        changed |= targetm.gimple_fold_builtin (gsi);
     }
   else if (gimple_call_internal_p (stmt))
     {
       enum tree_code subcode = ERROR_MARK;
       tree result = NULL_TREE;
+      bool cplx_result = false;
+      tree overflow = NULL_TREE;
       switch (gimple_call_internal_fn (stmt))
        {
        case IFN_BUILTIN_EXPECT:
@@ -1209,14 +3185,39 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                                        gimple_call_arg (stmt, 1),
                                        gimple_call_arg (stmt, 2));
          break;
-       case IFN_UBSAN_CHECK_ADD:
+       case IFN_UBSAN_OBJECT_SIZE:
+         if (integer_all_onesp (gimple_call_arg (stmt, 2))
+             || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
+                 && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST
+                 && tree_int_cst_le (gimple_call_arg (stmt, 1),
+                                     gimple_call_arg (stmt, 2))))
+           {
+             gsi_replace (gsi, gimple_build_nop (), true);
+             unlink_stmt_vdef (stmt);
+             release_defs (stmt);
+             return true;
+           }
+         break;
+       case IFN_UBSAN_CHECK_ADD:
+         subcode = PLUS_EXPR;
+         break;
+       case IFN_UBSAN_CHECK_SUB:
+         subcode = MINUS_EXPR;
+         break;
+       case IFN_UBSAN_CHECK_MUL:
+         subcode = MULT_EXPR;
+         break;
+       case IFN_ADD_OVERFLOW:
          subcode = PLUS_EXPR;
+         cplx_result = true;
          break;
-       case IFN_UBSAN_CHECK_SUB:
+       case IFN_SUB_OVERFLOW:
          subcode = MINUS_EXPR;
+         cplx_result = true;
          break;
-       case IFN_UBSAN_CHECK_MUL:
+       case IFN_MUL_OVERFLOW:
          subcode = MULT_EXPR;
+         cplx_result = true;
          break;
        default:
          break;
@@ -1225,30 +3226,88 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
        {
          tree arg0 = gimple_call_arg (stmt, 0);
          tree arg1 = gimple_call_arg (stmt, 1);
+         tree type = TREE_TYPE (arg0);
+         if (cplx_result)
+           {
+             tree lhs = gimple_call_lhs (stmt);
+             if (lhs == NULL_TREE)
+               type = NULL_TREE;
+             else
+               type = TREE_TYPE (TREE_TYPE (lhs));
+           }
+         if (type == NULL_TREE)
+           ;
          /* x = y + 0; x = y - 0; x = y * 0; */
-         if (integer_zerop (arg1))
-           result = subcode == MULT_EXPR
-                    ? build_zero_cst (TREE_TYPE (arg0))
-                    : arg0;
+         else if (integer_zerop (arg1))
+           result = subcode == MULT_EXPR ? integer_zero_node : arg0;
          /* x = 0 + y; x = 0 * y; */
          else if (subcode != MINUS_EXPR && integer_zerop (arg0))
-           result = subcode == MULT_EXPR
-                    ? build_zero_cst (TREE_TYPE (arg0))
-                    : arg1;
+           result = subcode == MULT_EXPR ? integer_zero_node : arg1;
          /* x = y - y; */
          else if (subcode == MINUS_EXPR && operand_equal_p (arg0, arg1, 0))
-           result = build_zero_cst (TREE_TYPE (arg0));
+           result = integer_zero_node;
          /* x = y * 1; x = 1 * y; */
-         else if (subcode == MULT_EXPR)
+         else if (subcode == MULT_EXPR && integer_onep (arg1))
+           result = arg0;
+         else if (subcode == MULT_EXPR && integer_onep (arg0))
+           result = arg1;
+         else if (TREE_CODE (arg0) == INTEGER_CST
+                  && TREE_CODE (arg1) == INTEGER_CST)
+           {
+             if (cplx_result)
+               result = int_const_binop (subcode, fold_convert (type, arg0),
+                                         fold_convert (type, arg1));
+             else
+               result = int_const_binop (subcode, arg0, arg1);
+             if (result && arith_overflowed_p (subcode, type, arg0, arg1))
+               {
+                 if (cplx_result)
+                   overflow = build_one_cst (type);
+                 else
+                   result = NULL_TREE;
+               }
+           }
+         if (result)
            {
-             if (integer_onep (arg1))
-               result = arg0;
-             else if (integer_onep (arg0))
-               result = arg1;
+             if (result == integer_zero_node)
+               result = build_zero_cst (type);
+             else if (cplx_result && TREE_TYPE (result) != type)
+               {
+                 if (TREE_CODE (result) == INTEGER_CST)
+                   {
+                     if (arith_overflowed_p (PLUS_EXPR, type, result,
+                                             integer_zero_node))
+                       overflow = build_one_cst (type);
+                   }
+                 else if ((!TYPE_UNSIGNED (TREE_TYPE (result))
+                           && TYPE_UNSIGNED (type))
+                          || (TYPE_PRECISION (type)
+                              < (TYPE_PRECISION (TREE_TYPE (result))
+                                 + (TYPE_UNSIGNED (TREE_TYPE (result))
+                                    && !TYPE_UNSIGNED (type)))))
+                   result = NULL_TREE;
+                 if (result)
+                   result = fold_convert (type, result);
+               }
            }
        }
+
       if (result)
        {
+         if (TREE_CODE (result) == INTEGER_CST && TREE_OVERFLOW (result))
+           result = drop_tree_overflow (result);
+         if (cplx_result)
+           {
+             if (overflow == NULL_TREE)
+               overflow = build_zero_cst (TREE_TYPE (result));
+             tree ctype = build_complex_type (TREE_TYPE (result));
+             if (TREE_CODE (result) == INTEGER_CST
+                 && TREE_CODE (overflow) == INTEGER_CST)
+               result = build_complex (ctype, result, overflow);
+             else
+               result = build2_loc (gimple_location (stmt), COMPLEX_EXPR,
+                                    ctype, result, overflow);
+           }
          if (!update_call_from_tree (gsi, result))
            gimplify_and_update_call_from_tree (gsi, result);
          changed = true;
@@ -1258,16 +3317,307 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
   return changed;
 }
 
+
+/* Worker for fold_stmt_1 dispatch to pattern based folding with
+   gimple_simplify.
+
+   Replaces *GSI with the simplification result in RCODE and OPS
+   and the associated statements in *SEQ.  Does the replacement
+   according to INPLACE and returns true if the operation succeeded.  */
+
+static bool
+replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
+                                 code_helper rcode, tree *ops,
+                                 gimple_seq *seq, bool inplace)
+{
+  gimple stmt = gsi_stmt (*gsi);
+
+  /* Play safe and do not allow abnormals to be mentioned in
+     newly created statements.  See also maybe_push_res_to_seq.  */
+  if ((TREE_CODE (ops[0]) == SSA_NAME
+       && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[0]))
+      || (ops[1]
+         && TREE_CODE (ops[1]) == SSA_NAME
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[1]))
+      || (ops[2]
+         && TREE_CODE (ops[2]) == SSA_NAME
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])))
+    return false;
+
+  if (gcond *cond_stmt = dyn_cast <gcond *> (stmt))
+    {
+      gcc_assert (rcode.is_tree_code ());
+      if (TREE_CODE_CLASS ((enum tree_code)rcode) == tcc_comparison
+         /* GIMPLE_CONDs condition may not throw.  */
+         && (!flag_exceptions
+             || !cfun->can_throw_non_call_exceptions
+             || !operation_could_trap_p (rcode,
+                                         FLOAT_TYPE_P (TREE_TYPE (ops[0])),
+                                         false, NULL_TREE)))
+       gimple_cond_set_condition (cond_stmt, rcode, ops[0], ops[1]);
+      else if (rcode == SSA_NAME)
+       gimple_cond_set_condition (cond_stmt, NE_EXPR, ops[0],
+                                  build_zero_cst (TREE_TYPE (ops[0])));
+      else if (rcode == INTEGER_CST)
+       {
+         if (integer_zerop (ops[0]))
+           gimple_cond_make_false (cond_stmt);
+         else
+           gimple_cond_make_true (cond_stmt);
+       }
+      else if (!inplace)
+       {
+         tree res = maybe_push_res_to_seq (rcode, boolean_type_node,
+                                           ops, seq);
+         if (!res)
+           return false;
+         gimple_cond_set_condition (cond_stmt, NE_EXPR, res,
+                                    build_zero_cst (TREE_TYPE (res)));
+       }
+      else
+       return false;
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "gimple_simplified to ");
+         if (!gimple_seq_empty_p (*seq))
+           print_gimple_seq (dump_file, *seq, 0, TDF_SLIM);
+         print_gimple_stmt (dump_file, gsi_stmt (*gsi),
+                            0, TDF_SLIM);
+       }
+      gsi_insert_seq_before (gsi, *seq, GSI_SAME_STMT);
+      return true;
+    }
+  else if (is_gimple_assign (stmt)
+          && rcode.is_tree_code ())
+    {
+      if (!inplace
+         || gimple_num_ops (stmt) > get_gimple_rhs_num_ops (rcode))
+       {
+         maybe_build_generic_op (rcode,
+                                 TREE_TYPE (gimple_assign_lhs (stmt)),
+                                 &ops[0], ops[1], ops[2]);
+         gimple_assign_set_rhs_with_ops (gsi, rcode, ops[0], ops[1], ops[2]);
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "gimple_simplified to ");
+             if (!gimple_seq_empty_p (*seq))
+               print_gimple_seq (dump_file, *seq, 0, TDF_SLIM);
+             print_gimple_stmt (dump_file, gsi_stmt (*gsi),
+                                0, TDF_SLIM);
+           }
+         gsi_insert_seq_before (gsi, *seq, GSI_SAME_STMT);
+         return true;
+       }
+    }
+  else if (!inplace)
+    {
+      if (gimple_has_lhs (stmt))
+       {
+         tree lhs = gimple_get_lhs (stmt);
+         if (!maybe_push_res_to_seq (rcode, TREE_TYPE (lhs),
+                                     ops, seq, lhs))
+           return false;
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "gimple_simplified to ");
+             print_gimple_seq (dump_file, *seq, 0, TDF_SLIM);
+           }
+         gsi_replace_with_seq_vops (gsi, *seq);
+         return true;
+       }
+      else
+       gcc_unreachable ();
+    }
+
+  return false;
+}
+
+/* Canonicalize MEM_REFs invariant address operand after propagation.  */
+
+static bool
+maybe_canonicalize_mem_ref_addr (tree *t)
+{
+  bool res = false;
+
+  if (TREE_CODE (*t) == ADDR_EXPR)
+    t = &TREE_OPERAND (*t, 0);
+
+  while (handled_component_p (*t))
+    t = &TREE_OPERAND (*t, 0);
+
+  /* Canonicalize MEM [&foo.bar, 0] which appears after propagating
+     of invariant addresses into a SSA name MEM_REF address.  */
+  if (TREE_CODE (*t) == MEM_REF
+      || TREE_CODE (*t) == TARGET_MEM_REF)
+    {
+      tree addr = TREE_OPERAND (*t, 0);
+      if (TREE_CODE (addr) == ADDR_EXPR
+         && (TREE_CODE (TREE_OPERAND (addr, 0)) == MEM_REF
+             || handled_component_p (TREE_OPERAND (addr, 0))))
+       {
+         tree base;
+         HOST_WIDE_INT coffset;
+         base = get_addr_base_and_unit_offset (TREE_OPERAND (addr, 0),
+                                               &coffset);
+         if (!base)
+           gcc_unreachable ();
+
+         TREE_OPERAND (*t, 0) = build_fold_addr_expr (base);
+         TREE_OPERAND (*t, 1) = int_const_binop (PLUS_EXPR,
+                                                 TREE_OPERAND (*t, 1),
+                                                 size_int (coffset));
+         res = true;
+       }
+      gcc_checking_assert (TREE_CODE (TREE_OPERAND (*t, 0)) == DEBUG_EXPR_DECL
+                          || is_gimple_mem_ref_addr (TREE_OPERAND (*t, 0)));
+    }
+
+  /* Canonicalize back MEM_REFs to plain reference trees if the object
+     accessed is a decl that has the same access semantics as the MEM_REF.  */
+  if (TREE_CODE (*t) == MEM_REF
+      && TREE_CODE (TREE_OPERAND (*t, 0)) == ADDR_EXPR
+      && integer_zerop (TREE_OPERAND (*t, 1))
+      && MR_DEPENDENCE_CLIQUE (*t) == 0)
+    {
+      tree decl = TREE_OPERAND (TREE_OPERAND (*t, 0), 0);
+      tree alias_type = TREE_TYPE (TREE_OPERAND (*t, 1));
+      if (/* Same volatile qualification.  */
+         TREE_THIS_VOLATILE (*t) == TREE_THIS_VOLATILE (decl)
+         /* Same TBAA behavior with -fstrict-aliasing.  */
+         && !TYPE_REF_CAN_ALIAS_ALL (alias_type)
+         && (TYPE_MAIN_VARIANT (TREE_TYPE (decl))
+             == TYPE_MAIN_VARIANT (TREE_TYPE (alias_type)))
+         /* Same alignment.  */
+         && TYPE_ALIGN (TREE_TYPE (decl)) == TYPE_ALIGN (TREE_TYPE (*t))
+         /* We have to look out here to not drop a required conversion
+            from the rhs to the lhs if *t appears on the lhs or vice-versa
+            if it appears on the rhs.  Thus require strict type
+            compatibility.  */
+         && types_compatible_p (TREE_TYPE (*t), TREE_TYPE (decl)))
+       {
+         *t = TREE_OPERAND (TREE_OPERAND (*t, 0), 0);
+         res = true;
+       }
+    }
+
+  /* Canonicalize TARGET_MEM_REF in particular with respect to
+     the indexes becoming constant.  */
+  else if (TREE_CODE (*t) == TARGET_MEM_REF)
+    {
+      tree tem = maybe_fold_tmr (*t);
+      if (tem)
+       {
+         *t = tem;
+         res = true;
+       }
+    }
+
+  return res;
+}
+
 /* 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)
+fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
 {
   bool changed = false;
   gimple stmt = gsi_stmt (*gsi);
   unsigned i;
 
+  /* First do required canonicalization of [TARGET_]MEM_REF addresses
+     after propagation.
+     ???  This shouldn't be done in generic folding but in the
+     propagation helpers which also know whether an address was
+     propagated.  */
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_ASSIGN:
+      if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS)
+       {
+         tree *rhs = gimple_assign_rhs1_ptr (stmt);
+         if ((REFERENCE_CLASS_P (*rhs)
+              || TREE_CODE (*rhs) == ADDR_EXPR)
+             && maybe_canonicalize_mem_ref_addr (rhs))
+           changed = true;
+         tree *lhs = gimple_assign_lhs_ptr (stmt);
+         if (REFERENCE_CLASS_P (*lhs)
+             && maybe_canonicalize_mem_ref_addr (lhs))
+           changed = true;
+       }
+      break;
+    case GIMPLE_CALL:
+      {
+       for (i = 0; i < gimple_call_num_args (stmt); ++i)
+         {
+           tree *arg = gimple_call_arg_ptr (stmt, i);
+           if (REFERENCE_CLASS_P (*arg)
+               && maybe_canonicalize_mem_ref_addr (arg))
+             changed = true;
+         }
+       tree *lhs = gimple_call_lhs_ptr (stmt);
+       if (*lhs
+           && REFERENCE_CLASS_P (*lhs)
+           && maybe_canonicalize_mem_ref_addr (lhs))
+         changed = true;
+       break;
+      }
+    case GIMPLE_ASM:
+      {
+       gasm *asm_stmt = as_a <gasm *> (stmt);
+       for (i = 0; i < gimple_asm_noutputs (asm_stmt); ++i)
+         {
+           tree link = gimple_asm_output_op (asm_stmt, i);
+           tree op = TREE_VALUE (link);
+           if (REFERENCE_CLASS_P (op)
+               && maybe_canonicalize_mem_ref_addr (&TREE_VALUE (link)))
+             changed = true;
+         }
+       for (i = 0; i < gimple_asm_ninputs (asm_stmt); ++i)
+         {
+           tree link = gimple_asm_input_op (asm_stmt, i);
+           tree op = TREE_VALUE (link);
+           if ((REFERENCE_CLASS_P (op)
+                || TREE_CODE (op) == ADDR_EXPR)
+               && maybe_canonicalize_mem_ref_addr (&TREE_VALUE (link)))
+             changed = true;
+         }
+      }
+      break;
+    case GIMPLE_DEBUG:
+      if (gimple_debug_bind_p (stmt))
+       {
+         tree *val = gimple_debug_bind_get_value_ptr (stmt);
+         if (*val
+             && (REFERENCE_CLASS_P (*val)
+                 || TREE_CODE (*val) == ADDR_EXPR)
+             && maybe_canonicalize_mem_ref_addr (val))
+           changed = true;
+       }
+      break;
+    default:;
+    }
+
+  /* Dispatch to pattern-based folding.  */
+  if (!inplace
+      || is_gimple_assign (stmt)
+      || gimple_code (stmt) == GIMPLE_COND)
+    {
+      gimple_seq seq = NULL;
+      code_helper rcode;
+      tree ops[3] = {};
+      if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq,
+                          valueize, valueize))
+       {
+         if (replace_stmt_with_simplification (gsi, rcode, ops, &seq, inplace))
+           changed = true;
+         else
+           gimple_seq_discard (seq);
+       }
+    }
+
+  stmt = gsi_stmt (*gsi);
+
   /* Fold the main computation performed by the statement.  */
   switch (gimple_code (stmt))
     {
@@ -1305,7 +3655,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
       }
 
     case GIMPLE_COND:
-      changed |= fold_gimple_cond (stmt);
+      changed |= fold_gimple_cond (as_a <gcond *> (stmt));
       break;
 
     case GIMPLE_CALL:
@@ -1315,17 +3665,18 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
     case GIMPLE_ASM:
       /* Fold *& in asm operands.  */
       {
+       gasm *asm_stmt = as_a <gasm *> (stmt);
        size_t noutputs;
        const char **oconstraints;
        const char *constraint;
        bool allows_mem, allows_reg;
 
-       noutputs = gimple_asm_noutputs (stmt);
+       noutputs = gimple_asm_noutputs (asm_stmt);
        oconstraints = XALLOCAVEC (const char *, noutputs);
 
-       for (i = 0; i < gimple_asm_noutputs (stmt); ++i)
+       for (i = 0; i < gimple_asm_noutputs (asm_stmt); ++i)
          {
-           tree link = gimple_asm_output_op (stmt, i);
+           tree link = gimple_asm_output_op (asm_stmt, i);
            tree op = TREE_VALUE (link);
            oconstraints[i]
              = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
@@ -1336,9 +3687,9 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
                changed = true;
              }
          }
-       for (i = 0; i < gimple_asm_ninputs (stmt); ++i)
+       for (i = 0; i < gimple_asm_ninputs (asm_stmt); ++i)
          {
-           tree link = gimple_asm_input_op (stmt, i);
+           tree link = gimple_asm_input_op (asm_stmt, i);
            tree op = TREE_VALUE (link);
            constraint
              = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
@@ -1407,6 +3758,25 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
   return changed;
 }
 
+/* Valueziation callback that ends up not following SSA edges.  */
+
+tree
+no_follow_ssa_edges (tree)
+{
+  return NULL_TREE;
+}
+
+/* Valueization callback that ends up following single-use SSA edges only.  */
+
+tree
+follow_single_use_edges (tree val)
+{
+  if (TREE_CODE (val) == SSA_NAME
+      && !has_single_use (val))
+    return NULL_TREE;
+  return val;
+}
+
 /* 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.
@@ -1417,7 +3787,13 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace)
 bool
 fold_stmt (gimple_stmt_iterator *gsi)
 {
-  return fold_stmt_1 (gsi, false);
+  return fold_stmt_1 (gsi, false, no_follow_ssa_edges);
+}
+
+bool
+fold_stmt (gimple_stmt_iterator *gsi, tree (*valueize) (tree))
+{
+  return fold_stmt_1 (gsi, false, valueize);
 }
 
 /* Perform the minimal folding on statement *GSI.  Only operations like
@@ -1432,7 +3808,7 @@ bool
 fold_stmt_inplace (gimple_stmt_iterator *gsi)
 {
   gimple stmt = gsi_stmt (*gsi);
-  bool changed = fold_stmt_1 (gsi, true);
+  bool changed = fold_stmt_1 (gsi, true, no_follow_ssa_edges);
   gcc_assert (gsi_stmt (*gsi) == stmt);
   return changed;
 }
@@ -1455,7 +3831,7 @@ canonicalize_bool (tree expr, bool invert)
       else if (TREE_CODE (expr) == SSA_NAME)
        return fold_build2 (EQ_EXPR, boolean_type_node, expr,
                            build_int_cst (TREE_TYPE (expr), 0));
-      else if (TREE_CODE_CLASS (TREE_CODE (expr)) == tcc_comparison)
+      else if (COMPARISON_CLASS_P (expr))
        return fold_build2 (invert_tree_comparison (TREE_CODE (expr), false),
                            boolean_type_node,
                            TREE_OPERAND (expr, 0),
@@ -1474,7 +3850,7 @@ canonicalize_bool (tree expr, bool invert)
       else if (TREE_CODE (expr) == SSA_NAME)
        return fold_build2 (NE_EXPR, boolean_type_node, expr,
                            build_int_cst (TREE_TYPE (expr), 0));
-      else if (TREE_CODE_CLASS (TREE_CODE (expr)) == tcc_comparison)
+      else if (COMPARISON_CLASS_P (expr))
        return fold_build2 (TREE_CODE (expr),
                            boolean_type_node,
                            TREE_OPERAND (expr, 0),
@@ -1555,12 +3931,12 @@ same_bool_result_p (const_tree op1, const_tree op2)
   /* Check the cases where at least one of the operands is a comparison.
      These are a bit smarter than operand_equal_p in that they apply some
      identifies on SSA_NAMEs.  */
-  if (TREE_CODE_CLASS (TREE_CODE (op2)) == tcc_comparison
+  if (COMPARISON_CLASS_P (op2)
       && same_bool_comparison_p (op1, TREE_CODE (op2),
                                 TREE_OPERAND (op2, 0),
                                 TREE_OPERAND (op2, 1)))
     return true;
-  if (TREE_CODE_CLASS (TREE_CODE (op1)) == tcc_comparison
+  if (COMPARISON_CLASS_P (op1)
       && same_bool_comparison_p (op2, TREE_CODE (op1),
                                 TREE_OPERAND (op1, 0),
                                 TREE_OPERAND (op1, 1)))
@@ -2529,8 +4905,33 @@ maybe_fold_or_comparisons (enum tree_code code1, tree op1a, tree op1b,
    to avoid the indirect function call overhead.  */
 
 tree
-gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
+gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
+                               tree (*gvalueize) (tree))
 {
+  code_helper rcode;
+  tree ops[3] = {};
+  /* ???  The SSA propagators do not correctly deal with following SSA use-def
+     edges if there are intermediate VARYING defs.  For this reason
+     do not follow SSA edges here even though SCCVN can technically
+     just deal fine with that.  */
+  if (gimple_simplify (stmt, &rcode, ops, NULL, gvalueize, valueize)
+      && rcode.is_tree_code ()
+      && (TREE_CODE_LENGTH ((tree_code) rcode) == 0
+         || ((tree_code) rcode) == ADDR_EXPR)
+      && is_gimple_val (ops[0]))
+    {
+      tree res = ops[0];
+      if (dump_file && dump_flags & TDF_DETAILS)
+       {
+         fprintf (dump_file, "Match-and-simplified ");
+         print_gimple_expr (dump_file, stmt, 0, TDF_SLIM);
+         fprintf (dump_file, " to ");
+         print_generic_expr (dump_file, res, 0);
+         fprintf (dump_file, "\n");
+       }
+      return res;
+    }
+
   location_t loc = gimple_location (stmt);
   switch (gimple_code (stmt))
     {
@@ -2642,16 +5043,7 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
             }
 
           case GIMPLE_UNARY_RHS:
-            {
-              /* Handle unary operators that can appear in GIMPLE form.
-                 Note that we know the single operand must be a constant,
-                 so this should almost always return a simplified RHS.  */
-              tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
-
-              return
-               fold_unary_ignore_overflow_loc (loc, subcode,
-                                               gimple_expr_type (stmt), op0);
-            }
+           return NULL_TREE;
 
           case GIMPLE_BINARY_RHS:
             {
@@ -2709,6 +5101,7 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
     case GIMPLE_CALL:
       {
        tree fn;
+       gcall *call_stmt = as_a <gcall *> (stmt);
 
        if (gimple_call_internal_p (stmt))
          {
@@ -2768,19 +5161,19 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
                                                       TREE_OPERAND (fn, 0)))
          {
            tree *args = XALLOCAVEC (tree, gimple_call_num_args (stmt));
-           tree call, retval;
+           tree retval;
            unsigned i;
            for (i = 0; i < gimple_call_num_args (stmt); ++i)
              args[i] = (*valueize) (gimple_call_arg (stmt, i));
-           call = build_call_array_loc (loc,
-                                        gimple_call_return_type (stmt),
+           retval = fold_builtin_call_array (loc,
+                                        gimple_call_return_type (call_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);
-               retval = fold_convert (gimple_call_return_type (stmt), retval);
+               retval = fold_convert (gimple_call_return_type (call_stmt),
+                                      retval);
              }
            return retval;
          }
@@ -2809,10 +5202,6 @@ gimple_fold_stmt_to_constant (gimple stmt, tree (*valueize) (tree))
 /* The following set of functions are supposed to fold references using
    their constant initializers.  */
 
-static tree fold_ctor_reference (tree type, tree ctor,
-                                unsigned HOST_WIDE_INT offset,
-                                unsigned HOST_WIDE_INT size, tree);
-
 /* See if we can find constructor defining value of BASE.
    When we know the consructor with constant offset (such as
    base is array[40] and we do know constructor of array), then
@@ -2881,41 +5270,6 @@ get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
     }
 }
 
-/* CTOR is STRING_CST.  Fold reference of type TYPE and size SIZE
-   to the memory at bit OFFSET.
-
-   We do only simple job of folding byte accesses.  */
-
-static tree
-fold_string_cst_ctor_reference (tree type, tree ctor,
-                               unsigned HOST_WIDE_INT offset,
-                               unsigned HOST_WIDE_INT size)
-{
-  if (INTEGRAL_TYPE_P (type)
-      && (TYPE_MODE (type)
-         == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor))))
-      && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor))))
-         == MODE_INT)
-      && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1
-      && size == BITS_PER_UNIT
-      && !(offset % BITS_PER_UNIT))
-    {
-      offset /= BITS_PER_UNIT;
-      if (offset < (unsigned HOST_WIDE_INT) TREE_STRING_LENGTH (ctor))
-       return build_int_cst_type (type, (TREE_STRING_POINTER (ctor)
-                                  [offset]));
-      /* Folding
-        const char a[20]="hello";
-        return a[10];
-
-        might lead to offset greater than string length.  In this case we
-        know value is either initialized to 0 or out of bounds.  Return 0
-        in both cases.  */
-      return build_zero_cst (type);
-    }
-  return NULL_TREE;
-}
-
 /* CTOR is CONSTRUCTOR of an array type.  Fold reference of type TYPE and size
    SIZE to the memory at bit OFFSET.  */
 
@@ -3083,7 +5437,7 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
 /* CTOR is value initializing memory, fold reference of type TYPE and size SIZE
    to the memory at bit OFFSET.  */
 
-static tree
+tree
 fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
                     unsigned HOST_WIDE_INT size, tree from_decl)
 {
@@ -3098,17 +5452,28 @@ fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
      result.  */
   if (!AGGREGATE_TYPE_P (TREE_TYPE (ctor)) && !offset
       /* VIEW_CONVERT_EXPR is defined only for matching sizes.  */
-      && operand_equal_p (TYPE_SIZE (type),
-                         TYPE_SIZE (TREE_TYPE (ctor)), 0))
+      && !compare_tree_int (TYPE_SIZE (type), size)
+      && !compare_tree_int (TYPE_SIZE (TREE_TYPE (ctor)), size))
     {
       ret = canonicalize_constructor_val (unshare_expr (ctor), from_decl);
       ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
       if (ret)
-       STRIP_NOPS (ret);
+       STRIP_USELESS_TYPE_CONVERSION (ret);
       return ret;
     }
-  if (TREE_CODE (ctor) == STRING_CST)
-    return fold_string_cst_ctor_reference (type, ctor, offset, size);
+  /* For constants and byte-aligned/sized reads try to go through
+     native_encode/interpret.  */
+  if (CONSTANT_CLASS_P (ctor)
+      && BITS_PER_UNIT == 8
+      && offset % BITS_PER_UNIT == 0
+      && size % BITS_PER_UNIT == 0
+      && size <= MAX_BITSIZE_MODE_ANY_MODE)
+    {
+      unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
+      if (native_encode_expr (ctor, buf, size / BITS_PER_UNIT,
+                             offset / BITS_PER_UNIT) > 0)
+       return native_interpret_expr (type, buf, size / BITS_PER_UNIT);
+    }
   if (TREE_CODE (ctor) == CONSTRUCTOR)
     {
 
@@ -3138,7 +5503,7 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
   if (TREE_THIS_VOLATILE (t))
     return NULL_TREE;
 
-  if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_declaration)
+  if (DECL_P (t))
     return get_symbol_constant_value (t);
 
   tem = fold_read_from_constant_string (t);
@@ -3271,7 +5636,6 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
   if (TREE_CODE (v) != VAR_DECL
       || !DECL_VIRTUAL_P (v))
     {
-      gcc_assert (in_lto_p);
       /* Pass down that we lost track of the target.  */
       if (can_refer)
        *can_refer = false;
@@ -3471,7 +5835,7 @@ gimple_val_nonnegative_real_p (tree val)
            CASE_FLT_FN (BUILT_IN_SQRT):
              /* sqrt(-0.0) is -0.0, and sqrt is not defined over other
                 nonnegative inputs.  */
-             if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (val))))
+             if (!HONOR_SIGNED_ZEROS (val))
                return true;
 
              break;
@@ -3693,9 +6057,205 @@ rewrite_to_defined_overflow (gimple stmt)
   if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
     gimple_assign_set_rhs_code (stmt, PLUS_EXPR);
   gimple_seq_add_stmt (&stmts, stmt);
-  gimple cvt = gimple_build_assign_with_ops
-      (NOP_EXPR, lhs, gimple_assign_lhs (stmt), NULL_TREE);
+  gimple cvt = gimple_build_assign (lhs, NOP_EXPR, gimple_assign_lhs (stmt));
   gimple_seq_add_stmt (&stmts, cvt);
 
   return stmts;
 }
+
+
+/* The valueization hook we use for the gimple_build API simplification.
+   This makes us match fold_buildN behavior by only combining with
+   statements in the sequence(s) we are currently building.  */
+
+static tree
+gimple_build_valueize (tree op)
+{
+  if (gimple_bb (SSA_NAME_DEF_STMT (op)) == NULL)
+    return op;
+  return NULL_TREE;
+}
+
+/* Build the expression CODE OP0 of type TYPE with location LOC,
+   simplifying it first if possible.  Returns the built
+   expression value and appends statements possibly defining it
+   to SEQ.  */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+             enum tree_code code, tree type, tree op0)
+{
+  tree res = gimple_simplify (code, type, op0, seq, gimple_build_valueize);
+  if (!res)
+    {
+      if (gimple_in_ssa_p (cfun))
+       res = make_ssa_name (type);
+      else
+       res = create_tmp_reg (type);
+      gimple stmt;
+      if (code == REALPART_EXPR
+         || code == IMAGPART_EXPR
+         || code == VIEW_CONVERT_EXPR)
+       stmt = gimple_build_assign (res, code, build1 (code, type, op0));
+      else
+       stmt = gimple_build_assign (res, code, op0);
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (seq, stmt);
+    }
+  return res;
+}
+
+/* Build the expression OP0 CODE OP1 of type TYPE with location LOC,
+   simplifying it first if possible.  Returns the built
+   expression value and appends statements possibly defining it
+   to SEQ.  */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+             enum tree_code code, tree type, tree op0, tree op1)
+{
+  tree res = gimple_simplify (code, type, op0, op1, seq, gimple_build_valueize);
+  if (!res)
+    {
+      if (gimple_in_ssa_p (cfun))
+       res = make_ssa_name (type);
+      else
+       res = create_tmp_reg (type);
+      gimple stmt = gimple_build_assign (res, code, op0, op1);
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (seq, stmt);
+    }
+  return res;
+}
+
+/* Build the expression (CODE OP0 OP1 OP2) of type TYPE with location LOC,
+   simplifying it first if possible.  Returns the built
+   expression value and appends statements possibly defining it
+   to SEQ.  */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+             enum tree_code code, tree type, tree op0, tree op1, tree op2)
+{
+  tree res = gimple_simplify (code, type, op0, op1, op2,
+                             seq, gimple_build_valueize);
+  if (!res)
+    {
+      if (gimple_in_ssa_p (cfun))
+       res = make_ssa_name (type);
+      else
+       res = create_tmp_reg (type);
+      gimple stmt;
+      if (code == BIT_FIELD_REF)
+       stmt = gimple_build_assign (res, code,
+                                   build3 (code, type, op0, op1, op2));
+      else
+       stmt = gimple_build_assign (res, code, op0, op1, op2);
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (seq, stmt);
+    }
+  return res;
+}
+
+/* Build the call FN (ARG0) with a result of type TYPE
+   (or no result if TYPE is void) with location LOC,
+   simplifying it first if possible.  Returns the built
+   expression value (or NULL_TREE if TYPE is void) and appends
+   statements possibly defining it to SEQ.  */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+             enum built_in_function fn, tree type, tree arg0)
+{
+  tree res = gimple_simplify (fn, type, arg0, seq, gimple_build_valueize);
+  if (!res)
+    {
+      tree decl = builtin_decl_implicit (fn);
+      gimple stmt = gimple_build_call (decl, 1, arg0);
+      if (!VOID_TYPE_P (type))
+       {
+         if (gimple_in_ssa_p (cfun))
+           res = make_ssa_name (type);
+         else
+           res = create_tmp_reg (type);
+         gimple_call_set_lhs (stmt, res);
+       }
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (seq, stmt);
+    }
+  return res;
+}
+
+/* Build the call FN (ARG0, ARG1) with a result of type TYPE
+   (or no result if TYPE is void) with location LOC,
+   simplifying it first if possible.  Returns the built
+   expression value (or NULL_TREE if TYPE is void) and appends
+   statements possibly defining it to SEQ.  */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+             enum built_in_function fn, tree type, tree arg0, tree arg1)
+{
+  tree res = gimple_simplify (fn, type, arg0, arg1, seq, gimple_build_valueize);
+  if (!res)
+    {
+      tree decl = builtin_decl_implicit (fn);
+      gimple stmt = gimple_build_call (decl, 2, arg0, arg1);
+      if (!VOID_TYPE_P (type))
+       {
+         if (gimple_in_ssa_p (cfun))
+           res = make_ssa_name (type);
+         else
+           res = create_tmp_reg (type);
+         gimple_call_set_lhs (stmt, res);
+       }
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (seq, stmt);
+    }
+  return res;
+}
+
+/* Build the call FN (ARG0, ARG1, ARG2) with a result of type TYPE
+   (or no result if TYPE is void) with location LOC,
+   simplifying it first if possible.  Returns the built
+   expression value (or NULL_TREE if TYPE is void) and appends
+   statements possibly defining it to SEQ.  */
+
+tree
+gimple_build (gimple_seq *seq, location_t loc,
+             enum built_in_function fn, tree type,
+             tree arg0, tree arg1, tree arg2)
+{
+  tree res = gimple_simplify (fn, type, arg0, arg1, arg2,
+                             seq, gimple_build_valueize);
+  if (!res)
+    {
+      tree decl = builtin_decl_implicit (fn);
+      gimple stmt = gimple_build_call (decl, 3, arg0, arg1, arg2);
+      if (!VOID_TYPE_P (type))
+       {
+         if (gimple_in_ssa_p (cfun))
+           res = make_ssa_name (type);
+         else
+           res = create_tmp_reg (type);
+         gimple_call_set_lhs (stmt, res);
+       }
+      gimple_set_location (stmt, loc);
+      gimple_seq_add_stmt_without_update (seq, stmt);
+    }
+  return res;
+}
+
+/* Build the conversion (TYPE) OP with a result of type TYPE
+   with location LOC if such conversion is neccesary in GIMPLE,
+   simplifying it first.
+   Returns the built expression value and appends
+   statements possibly defining it to SEQ.  */
+
+tree
+gimple_convert (gimple_seq *seq, location_t loc, tree type, tree op)
+{
+  if (useless_type_conversion_p (type, TREE_TYPE (op)))
+    return op;
+  return gimple_build (seq, loc, NOP_EXPR, type, op);
+}