re PR middle-end/80222 (may_alias folded away)
[gcc.git] / gcc / gimple-fold.c
index bdc6088ec0e455e0bae3b4aae3c571811ca2a12f..a6a958cefa4c560df9b828d55100e2802c2a1fd1 100644 (file)
@@ -1,5 +1,5 @@
 /* Statement simplification on GIMPLE.
-   Copyright (C) 2010-2014 Free Software Foundation, Inc.
+   Copyright (C) 2010-2017 Free Software Foundation, Inc.
    Split out from tree-ssa-ccp.c.
 
 This file is part of GCC.
@@ -21,56 +21,41 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
 #include "tree.h"
-#include "stringpool.h"
-#include "expr.h"
+#include "gimple.h"
+#include "predict.h"
+#include "ssa.h"
+#include "cgraph.h"
+#include "gimple-pretty-print.h"
+#include "fold-const.h"
 #include "stmt.h"
+#include "expr.h"
 #include "stor-layout.h"
-#include "flags.h"
-#include "hashtab.h"
-#include "hash-set.h"
-#include "vec.h"
-#include "machmode.h"
-#include "hard-reg-set.h"
-#include "input.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"
-#include "gimple-ssa.h"
-#include "tree-ssanames.h"
 #include "tree-into-ssa.h"
 #include "tree-dfa.h"
 #include "tree-ssa.h"
 #include "tree-ssa-propagate.h"
-#include "target.h"
-#include "hash-map.h"
-#include "plugin-api.h"
-#include "ipa-ref.h"
-#include "cgraph.h"
 #include "ipa-utils.h"
-#include "gimple-pretty-print.h"
 #include "tree-ssa-address.h"
 #include "langhooks.h"
 #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"
+#include "gomp-constants.h"
+#include "optabs-query.h"
+#include "omp-general.h"
+#include "ipa-chkp.h"
+#include "tree-cfg.h"
+#include "fold-const-call.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -106,7 +91,7 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
 
   /* We are concerned only about static/external vars and functions.  */
   if ((!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
-      || (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL))
+      || !VAR_OR_FUNCTION_DECL_P (decl))
     return true;
 
   /* Static objects can be referred only if they was not optimized out yet.  */
@@ -127,7 +112,7 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
      So we are concerned only when DECL comes from initializer of
      external var or var that has been optimized out.  */
   if (!from_decl
-      || TREE_CODE (from_decl) != VAR_DECL
+      || !VAR_P (from_decl)
       || (!DECL_EXTERNAL (from_decl)
          && (vnode = varpool_node::get (from_decl)) != NULL
          && vnode->definition)
@@ -172,6 +157,19 @@ can_refer_decl_in_current_unit_p (tree decl, tree from_decl)
   return !node || !node->global.inlined_to;
 }
 
+/* Create a temporary for TYPE for a statement STMT.  If the current function
+   is in SSA form, a SSA name is created.  Otherwise a temporary register
+   is made.  */
+
+static tree
+create_tmp_reg_or_ssa_name (tree type, gimple *stmt = NULL)
+{
+  if (gimple_in_ssa_p (cfun))
+    return make_ssa_name (type, stmt);
+  else
+    return create_tmp_reg (type);
+}
+
 /* CVAL is value taken from DECL_INITIAL of variable.  Try to transform it into
    acceptable form for is_gimple_min_invariant.
    FROM_DECL (if non-NULL) specify variable whose constructor contains CVAL.  */
@@ -207,11 +205,12 @@ canonicalize_constructor_val (tree cval, tree from_decl)
       if (!base)
        return NULL_TREE;
 
-      if ((TREE_CODE (base) == VAR_DECL
-          || TREE_CODE (base) == FUNCTION_DECL)
+      if (VAR_OR_FUNCTION_DECL_P (base)
          && !can_refer_decl_in_current_unit_p (base, from_decl))
        return NULL_TREE;
-      if (TREE_CODE (base) == VAR_DECL)
+      if (TREE_TYPE (base) == error_mark_node)
+       return NULL_TREE;
+      if (VAR_P (base))
        TREE_ADDRESSABLE (base) = 1;
       else if (TREE_CODE (base) == FUNCTION_DECL)
        {
@@ -307,7 +306,7 @@ maybe_fold_reference (tree expr, bool is_lhs)
 static tree
 fold_gimple_assign (gimple_stmt_iterator *si)
 {
-  gimple stmt = gsi_stmt (*si);
+  gimple *stmt = gsi_stmt (*si);
   enum tree_code subcode = gimple_assign_rhs_code (stmt);
   location_t loc = gimple_location (stmt);
 
@@ -361,8 +360,8 @@ fold_gimple_assign (gimple_stmt_iterator *si)
                    return val;
                  }
              }
-
          }
+
        else if (TREE_CODE (rhs) == ADDR_EXPR)
          {
            tree ref = TREE_OPERAND (rhs, 0);
@@ -377,21 +376,29 @@ fold_gimple_assign (gimple_stmt_iterator *si)
            else if (TREE_CODE (ref) == MEM_REF
                     && integer_zerop (TREE_OPERAND (ref, 1)))
              result = fold_convert (TREE_TYPE (rhs), TREE_OPERAND (ref, 0));
+
+           if (result)
+             {
+               /* Strip away useless type conversions.  Both the
+                  NON_LVALUE_EXPR that may have been added by fold, and
+                  "useless" type conversions that might now be apparent
+                  due to propagation.  */
+               STRIP_USELESS_TYPE_CONVERSION (result);
+
+               if (result != rhs && valid_gimple_rhs_p (result))
+                 return result;
+             }
          }
 
        else if (TREE_CODE (rhs) == CONSTRUCTOR
-                && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
-                && (CONSTRUCTOR_NELTS (rhs)
-                    == TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
+                && TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE)
          {
            /* Fold a constant vector CONSTRUCTOR to VECTOR_CST.  */
            unsigned i;
            tree val;
 
            FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
-             if (TREE_CODE (val) != INTEGER_CST
-                 && TREE_CODE (val) != REAL_CST
-                 && TREE_CODE (val) != FIXED_CST)
+             if (! CONSTANT_CLASS_P (val))
                return NULL_TREE;
 
            return build_vector_from_ctor (TREE_TYPE (rhs),
@@ -400,21 +407,6 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 
        else if (DECL_P (rhs))
          return get_symbol_constant_value (rhs);
-
-        /* If we couldn't fold the RHS, hand over to the generic
-           fold routines.  */
-        if (result == NULL_TREE)
-          result = fold (rhs);
-
-        /* Strip away useless type conversions.  Both the NON_LVALUE_EXPR
-           that may have been added by fold, and "useless" type
-           conversions that might now be apparent due to propagation.  */
-        STRIP_USELESS_TYPE_CONVERSION (result);
-
-        if (result != rhs && valid_gimple_rhs_p (result))
-         return result;
-
-       return NULL_TREE;
       }
       break;
 
@@ -422,106 +414,14 @@ fold_gimple_assign (gimple_stmt_iterator *si)
       break;
 
     case GIMPLE_BINARY_RHS:
-      /* Try to canonicalize for boolean-typed X the comparisons
-        X == 0, X == 1, X != 0, and X != 1.  */
-      if (gimple_assign_rhs_code (stmt) == EQ_EXPR
-         || gimple_assign_rhs_code (stmt) == NE_EXPR)
-        {
-         tree lhs = gimple_assign_lhs (stmt);
-         tree op1 = gimple_assign_rhs1 (stmt);
-         tree op2 = gimple_assign_rhs2 (stmt);
-         tree type = TREE_TYPE (op1);
-
-         /* Check whether the comparison operands are of the same boolean
-            type as the result type is.
-            Check that second operand is an integer-constant with value
-            one or zero.  */
-         if (TREE_CODE (op2) == INTEGER_CST
-             && (integer_zerop (op2) || integer_onep (op2))
-             && useless_type_conversion_p (TREE_TYPE (lhs), type))
-           {
-             enum tree_code cmp_code = gimple_assign_rhs_code (stmt);
-             bool is_logical_not = false;
-
-             /* X == 0 and X != 1 is a logical-not.of X
-                X == 1 and X != 0 is X  */
-             if ((cmp_code == EQ_EXPR && integer_zerop (op2))
-                 || (cmp_code == NE_EXPR && integer_onep (op2)))
-               is_logical_not = true;
-
-             if (is_logical_not == false)
-               result = op1;
-             /* Only for one-bit precision typed X the transformation
-                !X -> ~X is valied.  */
-             else if (TYPE_PRECISION (type) == 1)
-               result = build1_loc (gimple_location (stmt), BIT_NOT_EXPR,
-                                    type, op1);
-             /* Otherwise we use !X -> X ^ 1.  */
-             else
-               result = build2_loc (gimple_location (stmt), BIT_XOR_EXPR,
-                                    type, op1, build_int_cst (type, 1));
-            
-           }
-       }
-
-      if (!result)
-        result = fold_binary_loc (loc, subcode,
-                                 TREE_TYPE (gimple_assign_lhs (stmt)),
-                                 gimple_assign_rhs1 (stmt),
-                                 gimple_assign_rhs2 (stmt));
-
-      if (result)
-        {
-          STRIP_USELESS_TYPE_CONVERSION (result);
-          if (valid_gimple_rhs_p (result))
-           return result;
-        }
       break;
 
     case GIMPLE_TERNARY_RHS:
-      /* Try to fold a conditional expression.  */
-      if (gimple_assign_rhs_code (stmt) == COND_EXPR)
-       {
-         tree op0 = gimple_assign_rhs1 (stmt);
-         tree tem;
-         bool set = false;
-         location_t cond_loc = gimple_location (stmt);
-
-         if (COMPARISON_CLASS_P (op0))
-           {
-             fold_defer_overflow_warnings ();
-             tem = fold_binary_loc (cond_loc,
-                                    TREE_CODE (op0), TREE_TYPE (op0),
-                                    TREE_OPERAND (op0, 0),
-                                    TREE_OPERAND (op0, 1));
-             /* This is actually a conditional expression, not a GIMPLE
-                conditional statement, however, the valid_gimple_rhs_p
-                test still applies.  */
-             set = (tem && is_gimple_condexpr (tem)
-                    && valid_gimple_rhs_p (tem));
-             fold_undefer_overflow_warnings (set, stmt, 0);
-           }
-         else if (is_gimple_min_invariant (op0))
-           {
-             tem = op0;
-             set = true;
-           }
-         else
-           return NULL_TREE;
-
-         if (set)
-           result = fold_build3_loc (cond_loc, COND_EXPR,
-                                     TREE_TYPE (gimple_assign_lhs (stmt)), tem,
-                                     gimple_assign_rhs2 (stmt),
-                                     gimple_assign_rhs3 (stmt));
-       }
-
-      if (!result)
-       result = fold_ternary_loc (loc, subcode,
-                                  TREE_TYPE (gimple_assign_lhs (stmt)),
-                                  gimple_assign_rhs1 (stmt),
-                                  gimple_assign_rhs2 (stmt),
-                                  gimple_assign_rhs3 (stmt));
+      result = fold_ternary_loc (loc, subcode,
+                                TREE_TYPE (gimple_assign_lhs (stmt)),
+                                gimple_assign_rhs1 (stmt),
+                                gimple_assign_rhs2 (stmt),
+                                gimple_assign_rhs3 (stmt));
 
       if (result)
         {
@@ -538,33 +438,6 @@ fold_gimple_assign (gimple_stmt_iterator *si)
   return NULL_TREE;
 }
 
-/* Attempt to fold a conditional statement. Return true if any changes were
-   made. We only attempt to fold the condition expression, and do not perform
-   any transformation that would require alteration of the cfg.  It is
-   assumed that the operands have been previously folded.  */
-
-static bool
-fold_gimple_cond (gcond *stmt)
-{
-  tree result = fold_binary_loc (gimple_location (stmt),
-                            gimple_cond_code (stmt),
-                             boolean_type_node,
-                             gimple_cond_lhs (stmt),
-                             gimple_cond_rhs (stmt));
-
-  if (result)
-    {
-      STRIP_USELESS_TYPE_CONVERSION (result);
-      if (is_gimple_condexpr (result) && valid_gimple_rhs_p (result))
-        {
-          gimple_cond_set_condition_from_tree (stmt, result);
-          return true;
-        }
-    }
-
-  return false;
-}
-
 
 /* Replace a statement at *SI_P with a sequence of statements in STMTS,
    adjusting the replacement stmts location and virtual operands.
@@ -574,18 +447,18 @@ fold_gimple_cond (gcond *stmt)
 static void
 gsi_replace_with_seq_vops (gimple_stmt_iterator *si_p, gimple_seq stmts)
 {
-  gimple stmt = gsi_stmt (*si_p);
+  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;
+  gimple *laststore = NULL;
   for (gimple_stmt_iterator i = gsi_last (stmts);
        !gsi_end_p (i); gsi_prev (&i))
     {
-      gimple new_stmt = gsi_stmt (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)
@@ -610,7 +483,7 @@ gsi_replace_with_seq_vops (gimple_stmt_iterator *si_p, gimple_seq stmts)
   for (gimple_stmt_iterator i = gsi_start (stmts);
        !gsi_end_p (i); gsi_next (&i))
     {
-      gimple new_stmt = gsi_stmt (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))
@@ -652,7 +525,7 @@ void
 gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
 {
   tree lhs;
-  gimple stmt, new_stmt;
+  gimple *stmt, *new_stmt;
   gimple_stmt_iterator i;
   gimple_seq stmts = NULL;
 
@@ -676,13 +549,13 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
              unlink_stmt_vdef (stmt);
              release_defs (stmt);
            }
-         gsi_replace (si_p, gimple_build_nop (), true);
+         gsi_replace (si_p, gimple_build_nop (), false);
          return;
        }
     }
   else
     {
-      tree tmp = get_initialized_tmp_var (expr, &stmts, NULL);
+      tree tmp = force_gimple_operand (expr, &stmts, false, NULL_TREE);
       new_stmt = gimple_build_assign (lhs, tmp);
       i = gsi_last (stmts);
       gsi_insert_after_without_update (&i, new_stmt,
@@ -700,9 +573,9 @@ gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
 static void
 replace_call_with_value (gimple_stmt_iterator *gsi, tree val)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   tree lhs = gimple_call_lhs (stmt);
-  gimple repl;
+  gimple *repl;
   if (lhs)
     {
       if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (val)))
@@ -717,16 +590,16 @@ replace_call_with_value (gimple_stmt_iterator *gsi, tree val)
       unlink_stmt_vdef (stmt);
       release_ssa_name (vdef);
     }
-  gsi_replace (gsi, repl, true);
+  gsi_replace (gsi, repl, false);
 }
 
 /* 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)
+replace_call_with_call_and_fold (gimple_stmt_iterator *gsi, gimple *repl)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  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)
@@ -736,7 +609,7 @@ replace_call_with_call_and_fold (gimple_stmt_iterator *gsi, gimple repl)
       gimple_set_vuse (repl, gimple_vuse (stmt));
       SSA_NAME_DEF_STMT (gimple_vdef (repl)) = repl;
     }
-  gsi_replace (gsi, repl, true);
+  gsi_replace (gsi, repl, false);
   fold_stmt (gsi);
 }
 
@@ -752,7 +625,7 @@ var_decl_component_p (tree var)
 }
 
 /* Fold function call to builtin mem{{,p}cpy,move}.  Return
-   NULL_TREE if no simplification can be made.
+   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).
@@ -763,7 +636,7 @@ static bool
 gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
                               tree dest, tree src, int endp)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   tree lhs = gimple_call_lhs (stmt);
   tree len = gimple_call_arg (stmt, 2);
   tree destvar, srcvar;
@@ -772,7 +645,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
     {
-      gimple repl;
+      gimple *repl;
       if (gimple_call_lhs (stmt))
        repl = gimple_build_assign (gimple_call_lhs (stmt), dest);
       else
@@ -783,7 +656,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
          unlink_stmt_vdef (stmt);
          release_ssa_name (vdef);
        }
-      gsi_replace (gsi, repl, true);
+      gsi_replace (gsi, repl, false);
       return true;
     }
 
@@ -796,7 +669,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
        release_ssa_name (gimple_vdef (stmt));
       if (!lhs)
        {
-         gsi_replace (gsi, gimple_build_nop (), true);
+         gsi_replace (gsi, gimple_build_nop (), false);
          return true;
        }
       goto done;
@@ -807,6 +680,18 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
       unsigned int src_align, dest_align;
       tree off0;
 
+      /* Inlining of memcpy/memmove may cause bounds lost (if we copy
+        pointers as wide integer) and also may result in huge function
+        size because of inlined bounds copy.  Thus don't inline for
+        functions we want to instrument.  */
+      if (flag_check_pointer_bounds
+         && chkp_instrumentable_p (cfun->decl)
+         /* Even if data may contain pointers we can inline if copy
+            less than a pointer size.  */
+         && (!tree_fits_uhwi_p (len)
+             || compare_tree_int (len, POINTER_SIZE_UNITS) >= 0))
+       return false;
+
       /* 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);
@@ -826,7 +711,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
          && !c_strlen (src, 2))
        {
          unsigned ilen = tree_to_uhwi (len);
-         if (exact_log2 (ilen) != -1)
+         if (pow2p_hwi (ilen))
            {
              tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
              if (type
@@ -836,7 +721,9 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
                  /* 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)))
+                     || !SLOW_UNALIGNED_ACCESS (TYPE_MODE (type), dest_align)
+                     || (optab_handler (movmisalign_optab, TYPE_MODE (type))
+                         != CODE_FOR_nothing)))
                {
                  tree srctype = type;
                  tree desttype = type;
@@ -848,19 +735,20 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
                    srcmem = tem;
                  else if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type))
                           && SLOW_UNALIGNED_ACCESS (TYPE_MODE (type),
-                                                    src_align))
+                                                    src_align)
+                          && (optab_handler (movmisalign_optab,
+                                             TYPE_MODE (type))
+                              == CODE_FOR_nothing))
                    srcmem = NULL_TREE;
                  if (srcmem)
                    {
-                     gimple new_stmt;
+                     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));
+                         srcmem
+                           = create_tmp_reg_or_ssa_name (TREE_TYPE (srcmem),
+                                                         new_stmt);
                          gimple_assign_set_lhs (new_stmt, srcmem);
                          gimple_set_vuse (new_stmt, gimple_vuse (stmt));
                          gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
@@ -878,7 +766,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
                        SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
                      if (!lhs)
                        {
-                         gsi_replace (gsi, new_stmt, true);
+                         gsi_replace (gsi, new_stmt, false);
                          return true;
                        }
                      gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
@@ -918,21 +806,21 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
            {
              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;
+             HOST_WIDE_INT maxsize;
 
              srcvar = TREE_OPERAND (src, 0);
-             src_base = get_ref_base_and_extent (srcvar, &src_offset,
-                                                 &size, &maxsize);
+             src_base = get_addr_base_and_unit_offset (srcvar, &src_offset);
+             if (src_base == NULL)
+               src_base = srcvar;
              destvar = TREE_OPERAND (dest, 0);
-             dest_base = get_ref_base_and_extent (destvar, &dest_offset,
-                                                  &size, &maxsize);
+             dest_base = get_addr_base_and_unit_offset (destvar,
+                                                        &dest_offset);
+             if (dest_base == NULL)
+               dest_base = destvar;
              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))
                {
@@ -1138,17 +1026,21 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
            }
        }
 
-      gimple new_stmt;
+      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);
+         tree tem = fold_const_aggregate_ref (srcvar);
+         if (tem)
+           srcvar = tem;
+         if (! is_gimple_min_invariant (srcvar))
+           {
+             new_stmt = gimple_build_assign (NULL_TREE, srcvar);
+             srcvar = create_tmp_reg_or_ssa_name (TREE_TYPE (srcvar),
+                                                  new_stmt);
+             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));
@@ -1158,25 +1050,29 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
        SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
       if (!lhs)
        {
-         gsi_replace (gsi, new_stmt, true);
+         gsi_replace (gsi, new_stmt, false);
          return true;
        }
       gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
     }
 
 done:
+  gimple_seq stmts = NULL;
   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));
+    len = gimple_build (&stmts, loc, MINUS_EXPR, TREE_TYPE (len), len,
+                       ssize_int (1));
   if (endp == 2 || endp == 1)
-    dest = fold_build_pointer_plus_loc (loc, dest, len);
+    {
+      len = gimple_convert_to_ptrofftype (&stmts, loc, len);
+      dest = gimple_build (&stmts, loc, POINTER_PLUS_EXPR,
+                          TREE_TYPE (dest), 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);
+  gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+  gimple *repl = gimple_build_assign (lhs, dest);
+  gsi_replace (gsi, repl, false);
   return true;
 }
 
@@ -1186,7 +1082,7 @@ done:
 static bool
 gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   tree etype;
   unsigned HOST_WIDE_INT length, cval;
 
@@ -1246,7 +1142,7 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
     }
 
   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 *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)
@@ -1257,8 +1153,8 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
   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);
+      gimple *asgn = gimple_build_assign (gimple_call_lhs (stmt), dest);
+      gsi_replace (gsi, asgn, false);
     }
   else
     {
@@ -1271,20 +1167,33 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
 }
 
 
-/* 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.  */
+/* Obtain the minimum and maximum string length or minimum and maximum
+   value of ARG in LENGTH[0] and LENGTH[1], respectively.
+   If ARG is an SSA name variable, follow its use-def chains.  When
+   TYPE == 0, if LENGTH[1] 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 obtained, 1 for maximum string
+   length and 2 for maximum value ARG can have.
+   When FUZZY is set and the length of a string cannot be determined,
+   the function instead considers as the maximum possible length the
+   size of a character array it may refer to.
+   Set *FLEXP to true if the range of the string lengths has been
+   obtained from the upper bound of an array at the end of a struct.
+   Such an array may hold a string that's longer than its upper bound
+   due to it being used as a poor-man's flexible array member.  */
 
 static bool
-get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
+get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
+                 bool fuzzy, bool *flexp)
 {
   tree var, val;
-  gimple def_stmt;
+  gimple *def_stmt;
+
+  /* The minimum and maximum length.  The MAXLEN pointer stays unchanged
+     but MINLEN may be cleared during the execution of the function.  */
+  tree *minlen = length;
+  tree *const maxlen = length + 1;
 
   if (TREE_CODE (arg) != SSA_NAME)
     {
@@ -1296,8 +1205,8 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
          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);
+           return get_range_strlen (TREE_OPERAND (aop0, 0),
+                                    length, visited, type, fuzzy, flexp);
        }
 
       if (type == 2)
@@ -1309,26 +1218,66 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
        }
       else
        val = c_strlen (arg, 1);
+
+      if (!val && fuzzy)
+       {
+         if (TREE_CODE (arg) == ADDR_EXPR)
+           return get_range_strlen (TREE_OPERAND (arg, 0), length,
+                                    visited, type, fuzzy, flexp);
+
+         if (TREE_CODE (arg) == COMPONENT_REF
+             && TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE)
+           {
+             /* Use the type of the member array to determine the upper
+                bound on the length of the array.  This may be overly
+                optimistic if the array itself isn't NUL-terminated and
+                the caller relies on the subsequent member to contain
+                the NUL.
+                Set *FLEXP to true if the array whose bound is being
+                used is at the end of a struct.  */
+             if (array_at_struct_end_p (arg, true))
+               *flexp = true;
+
+             arg = TREE_OPERAND (arg, 1);
+             val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
+             if (!val || integer_zerop (val))
+               return false;
+             val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
+                                integer_one_node);
+             /* Set the minimum size to zero since the string in
+                the array could have zero length.  */
+             *minlen = ssize_int (0);
+           }
+       }
+
       if (!val)
        return false;
 
-      if (*length)
+      if (minlen
+         && (!*minlen
+             || (type > 0
+                 && TREE_CODE (*minlen) == INTEGER_CST
+                 && TREE_CODE (val) == INTEGER_CST
+                 && tree_int_cst_lt (val, *minlen))))
+       *minlen = val;
+
+      if (*maxlen)
        {
          if (type > 0)
            {
-             if (TREE_CODE (*length) != INTEGER_CST
+             if (TREE_CODE (*maxlen) != INTEGER_CST
                  || TREE_CODE (val) != INTEGER_CST)
                return false;
 
-             if (tree_int_cst_lt (*length, val))
-               *length = val;
+             if (tree_int_cst_lt (*maxlen, val))
+               *maxlen = val;
              return true;
            }
-         else if (simple_cst_equal (val, *length) != 1)
+         else if (simple_cst_equal (val, *maxlen) != 1)
            return false;
        }
 
-      *length = val;
+      *maxlen = val;
       return true;
     }
 
@@ -1356,14 +1305,14 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
             || gimple_assign_unary_nop_p (def_stmt))
           {
             tree rhs = gimple_assign_rhs1 (def_stmt);
-            return get_maxval_strlen (rhs, length, visited, type);
+           return get_range_strlen (rhs, length, visited, type, fuzzy, flexp);
           }
        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 get_range_strlen (op2, length, visited, type, fuzzy, flexp)
+             && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
           }
         return false;
 
@@ -1386,8 +1335,13 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
             if (arg == gimple_phi_result (def_stmt))
               continue;
 
-            if (!get_maxval_strlen (arg, length, visited, type))
-              return false;
+           if (!get_range_strlen (arg, length, visited, type, fuzzy, flexp))
+             {
+               if (fuzzy)
+                 *maxlen = build_all_ones_cst (size_type_node);
+               else
+                 return false;
+             }
           }
         }
         return true;
@@ -1397,17 +1351,49 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
     }
 }
 
+/* Determine the minimum and maximum value or string length that ARG
+   refers to and store each in the first two elements of MINMAXLEN.
+   For expressions that point to strings of unknown lengths that are
+   character arrays, use the upper bound of the array as the maximum
+   length.  For example, given an expression like 'x ? array : "xyz"'
+   and array declared as 'char array[8]', MINMAXLEN[0] will be set
+   to 3 and MINMAXLEN[1] to 7, the longest string that could be
+   stored in array.
+   Return true if the range of the string lengths has been obtained
+   from the upper bound of an array at the end of a struct.  Such
+   an array may hold a string that's longer than its upper bound
+   due to it being used as a poor-man's flexible array member.  */
+
+bool
+get_range_strlen (tree arg, tree minmaxlen[2])
+{
+  bitmap visited = NULL;
+
+  minmaxlen[0] = NULL_TREE;
+  minmaxlen[1] = NULL_TREE;
+
+  bool flexarray = false;
+  get_range_strlen (arg, minmaxlen, &visited, 1, true, &flexarray);
+
+  if (visited)
+    BITMAP_FREE (visited);
+
+  return flexarray;
+}
+
 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;
+  tree len[2] = { NULL_TREE, NULL_TREE };
+
+  bool dummy;
+  if (!get_range_strlen (arg, len, &visited, type, false, &dummy))
+    len[1] = NULL_TREE;
   if (visited)
     BITMAP_FREE (visited);
 
-  return len;
+  return len[1];
 }
 
 
@@ -1444,7 +1430,7 @@ gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
   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);
+  gimple *repl = gimple_build_call (fn, 3, dest, src, len);
   replace_call_with_call_and_fold (gsi, repl);
   return true;
 }
@@ -1493,11 +1479,157 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
   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);
+  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 strchr or strrchr.
+   If both arguments are constant, evaluate and fold the result,
+   otherwise simplify str(r)chr (str, 0) into str + strlen (str).
+   In general strlen is significantly faster than strchr
+   due to being a simpler operation.  */
+static bool
+gimple_fold_builtin_strchr (gimple_stmt_iterator *gsi, bool is_strrchr)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree str = gimple_call_arg (stmt, 0);
+  tree c = gimple_call_arg (stmt, 1);
+  location_t loc = gimple_location (stmt);
+  const char *p;
+  char ch;
+
+  if (!gimple_call_lhs (stmt))
+    return false;
+
+  if ((p = c_getstr (str)) && target_char_cst_p (c, &ch))
+    {
+      const char *p1 = is_strrchr ? strrchr (p, ch) : strchr (p, ch);
+
+      if (p1 == NULL)
+       {
+         replace_call_with_value (gsi, integer_zero_node);
+         return true;
+       }
+
+      tree len = build_int_cst (size_type_node, p1 - p);
+      gimple_seq stmts = NULL;
+      gimple *new_stmt = gimple_build_assign (gimple_call_lhs (stmt),
+                                             POINTER_PLUS_EXPR, str, len);
+      gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  if (!integer_zerop (c))
+    return false;
+
+  /* Transform strrchr (s, 0) to strchr (s, 0) when optimizing for size.  */
+  if (is_strrchr && optimize_function_for_size_p (cfun))
+    {
+      tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+
+      if (strchr_fn)
+       {
+         gimple *repl = gimple_build_call (strchr_fn, 2, str, c);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+
+      return false;
+    }
+
+  tree len;
+  tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN);
+
+  if (!strlen_fn)
+    return false;
+
+  /* Create newstr = strlen (str).  */
+  gimple_seq stmts = NULL;
+  gimple *new_stmt = gimple_build_call (strlen_fn, 1, str);
+  gimple_set_location (new_stmt, loc);
+  len = create_tmp_reg_or_ssa_name (size_type_node);
+  gimple_call_set_lhs (new_stmt, len);
+  gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+
+  /* Create (str p+ strlen (str)).  */
+  new_stmt = gimple_build_assign (gimple_call_lhs (stmt),
+                                 POINTER_PLUS_EXPR, str, len);
+  gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+  gsi_replace_with_seq_vops (gsi, stmts);
+  /* gsi now points at the assignment to the lhs, get a
+     stmt iterator to the strlen.
+     ???  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);
+  return true;
+}
+
+/* Fold function call to builtin strstr.
+   If both arguments are constant, evaluate and fold the result,
+   additionally fold strstr (x, "") into x and strstr (x, "c")
+   into strchr (x, 'c').  */
+static bool
+gimple_fold_builtin_strstr (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree haystack = gimple_call_arg (stmt, 0);
+  tree needle = gimple_call_arg (stmt, 1);
+  const char *p, *q;
+
+  if (!gimple_call_lhs (stmt))
+    return false;
+
+  q = c_getstr (needle);
+  if (q == NULL)
+    return false;
+
+  if ((p = c_getstr (haystack)))
+    {
+      const char *r = strstr (p, q);
+
+      if (r == NULL)
+       {
+         replace_call_with_value (gsi, integer_zero_node);
+         return true;
+       }
+
+      tree len = build_int_cst (size_type_node, r - p);
+      gimple_seq stmts = NULL;
+      gimple *new_stmt
+       = gimple_build_assign (gimple_call_lhs (stmt), POINTER_PLUS_EXPR,
+                              haystack, len);
+      gimple_seq_add_stmt_without_update (&stmts, new_stmt);
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* For strstr (x, "") return x.  */
+  if (q[0] == '\0')
+    {
+      replace_call_with_value (gsi, haystack);
+      return true;
+    }
+
+  /* Transform strstr (x, "c") into strchr (x, 'c').  */
+  if (q[1] == '\0')
+    {
+      tree strchr_fn = builtin_decl_implicit (BUILT_IN_STRCHR);
+      if (strchr_fn)
+       {
+         tree c = build_int_cst (integer_type_node, q[0]);
+         gimple *repl = gimple_build_call (strchr_fn, 2, haystack, c);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+    }
+
+  return false;
+}
+
 /* Simplify a call to the strcat builtin.  DST and SRC are the arguments
    to the call.
 
@@ -1519,7 +1651,7 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
 static bool
 gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   location_t loc = gimple_location (stmt);
 
   const char *p = c_getstr (src);
@@ -1550,12 +1682,9 @@ gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
 
   /* Create strlen (dst).  */
   gimple_seq stmts = NULL, stmts2;
-  gimple repl = gimple_build_call (strlen_fn, 1, dst);
+  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);
+  newdst = create_tmp_reg_or_ssa_name (size_type_node);
   gimple_call_set_lhs (repl, newdst);
   gimple_seq_add_stmt_without_update (&stmts, repl);
 
@@ -1599,7 +1728,7 @@ gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
 static bool
 gimple_fold_builtin_strcat_chk (gimple_stmt_iterator *gsi)
 {
-  gimple stmt = gsi_stmt (*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);
@@ -1623,18 +1752,58 @@ gimple_fold_builtin_strcat_chk (gimple_stmt_iterator *gsi)
   if (!fn)
     return false;
 
-  gimple repl = gimple_build_call (fn, 2, dest, src);
+  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);
+  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);
@@ -1667,7 +1836,7 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
          if (!fn)
            return false;
 
-         gimple repl = gimple_build_call (fn, 3, dest, src, size);
+         gimple *repl = gimple_build_call (fn, 3, dest, src, size);
          replace_call_with_call_and_fold (gsi, repl);
          return true;
        }
@@ -1679,60 +1848,303 @@ gimple_fold_builtin_strncat_chk (gimple_stmt_iterator *gsi)
   if (!fn)
     return false;
 
-  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  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.  */
+/* Build and append gimple statements to STMTS that would load a first
+   character of a memory location identified by STR.  LOC is location
+   of the statement.  */
+
+static tree
+gimple_load_first_char (location_t loc, tree str, gimple_seq *stmts)
+{
+  tree var;
+
+  tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+  tree cst_uchar_ptr_node
+    = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+  tree off0 = build_int_cst (cst_uchar_ptr_node, 0);
+
+  tree temp = fold_build2_loc (loc, MEM_REF, cst_uchar_node, str, off0);
+  gassign *stmt = gimple_build_assign (NULL_TREE, temp);
+  var = create_tmp_reg_or_ssa_name (cst_uchar_node, stmt);
+
+  gimple_assign_set_lhs (stmt, var);
+  gimple_seq_add_stmt_without_update (stmts, stmt);
+
+  return var;
+}
+
+/* Fold a call to the str{n}{case}cmp builtin pointed by GSI iterator.
+   FCODE is the name of the builtin.  */
 
 static bool
-gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
-                          tree arg0, tree arg1,
-                          bool unlocked)
+gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
+  tree callee = gimple_call_fndecl (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
 
-  /* 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));
+  tree type = integer_type_node;
+  tree str1 = gimple_call_arg (stmt, 0);
+  tree str2 = gimple_call_arg (stmt, 1);
+  tree lhs = gimple_call_lhs (stmt);
+  HOST_WIDE_INT length = -1;
 
-  /* If the return value is used, don't do the transformation.  */
-  if (gimple_call_lhs (stmt))
-    return false;
+  /* Handle strncmp and strncasecmp functions.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_uhwi_p (len))
+       length = tree_to_uhwi (len);
+    }
 
-  /* 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;
+  /* If the LEN parameter is zero, return zero.  */
+  if (length == 0)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
 
-  switch (compare_tree_int (len, 1))
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (str1, str2, 0))
     {
-    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;
+  const char *p1 = c_getstr (str1);
+  const char *p2 = c_getstr (str2);
 
-           gimple repl = gimple_build_call (fn_fputc, 2,
+  /* For known strings, return an immediate value.  */
+  if (p1 && p2)
+    {
+      int r = 0;
+      bool known_result = false;
+
+      switch (fcode)
+       {
+       case BUILT_IN_STRCMP:
+         {
+           r = strcmp (p1, p2);
+           known_result = true;
+           break;
+         }
+       case BUILT_IN_STRNCMP:
+         {
+           if (length == -1)
+             break;
+           r = strncmp (p1, p2, length);
+           known_result = true;
+           break;
+         }
+       /* Only handleable situation is where the string are equal (result 0),
+          which is already handled by operand_equal_p case.  */
+       case BUILT_IN_STRCASECMP:
+         break;
+       case BUILT_IN_STRNCASECMP:
+         {
+           if (length == -1)
+             break;
+           r = strncmp (p1, p2, length);
+           if (r == 0)
+             known_result = true;
+           break;;
+         }
+       default:
+         gcc_unreachable ();
+       }
+
+      if (known_result)
+       {
+         replace_call_with_value (gsi, build_cmp_result (type, r));
+         return true;
+       }
+    }
+
+  bool nonzero_length = length >= 1
+    || fcode == BUILT_IN_STRCMP
+    || fcode == BUILT_IN_STRCASECMP;
+
+  location_t loc = gimple_location (stmt);
+
+  /* If the second arg is "", return *(const unsigned char*)arg1.  */
+  if (p2 && *p2 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str1, &stmts);
+      if (lhs)
+       {
+         stmt = gimple_build_assign (lhs, NOP_EXPR, var);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+       }
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
+  if (p1 && *p1 == '\0' && nonzero_length)
+    {
+      gimple_seq stmts = NULL;
+      tree var = gimple_load_first_char (loc, str2, &stmts);
+
+      if (lhs)
+       {
+         tree c = create_tmp_reg_or_ssa_name (integer_type_node);
+         stmt = gimple_build_assign (c, NOP_EXPR, var);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+
+         stmt = gimple_build_assign (lhs, NEGATE_EXPR, c);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+       }
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg2 - *(const unsigned char*)arg1).  */
+  if (fcode == BUILT_IN_STRNCMP && length == 1)
+    {
+      gimple_seq stmts = NULL;
+      tree temp1 = gimple_load_first_char (loc, str1, &stmts);
+      tree temp2 = gimple_load_first_char (loc, str2, &stmts);
+
+      if (lhs)
+       {
+         tree c1 = create_tmp_reg_or_ssa_name (integer_type_node);
+         gassign *convert1 = gimple_build_assign (c1, NOP_EXPR, temp1);
+         gimple_seq_add_stmt_without_update (&stmts, convert1);
+
+         tree c2 = create_tmp_reg_or_ssa_name (integer_type_node);
+         gassign *convert2 = gimple_build_assign (c2, NOP_EXPR, temp2);
+         gimple_seq_add_stmt_without_update (&stmts, convert2);
+
+         stmt = gimple_build_assign (lhs, MINUS_EXPR, c1, c2);
+         gimple_seq_add_stmt_without_update (&stmts, stmt);
+       }
+
+      gsi_replace_with_seq_vops (gsi, stmts);
+      return true;
+    }
+
+  return false;
+}
+
+/* Fold a call to the memchr pointed by GSI iterator.  */
+
+static bool
+gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    {
+      replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+      return true;
+    }
+
+  char c;
+  if (TREE_CODE (arg2) != INTEGER_CST
+      || !tree_fits_uhwi_p (len)
+      || !target_char_cst_p (arg2, &c))
+    return false;
+
+  unsigned HOST_WIDE_INT length = tree_to_uhwi (len);
+  unsigned HOST_WIDE_INT string_length;
+  const char *p1 = c_getstr (arg1, &string_length);
+
+  if (p1)
+    {
+      const char *r = (const char *)memchr (p1, c, MIN (length, string_length));
+      if (r == NULL)
+       {
+         if (length <= string_length)
+           {
+             replace_call_with_value (gsi, build_int_cst (ptr_type_node, 0));
+             return true;
+           }
+       }
+      else
+       {
+         unsigned HOST_WIDE_INT offset = r - p1;
+         gimple_seq stmts = NULL;
+         if (lhs != NULL_TREE)
+           {
+             tree offset_cst = build_int_cst (TREE_TYPE (len), offset);
+             gassign *stmt = gimple_build_assign (lhs, POINTER_PLUS_EXPR,
+                                                  arg1, offset_cst);
+             gimple_seq_add_stmt_without_update (&stmts, stmt);
+           }
+         else
+           gimple_seq_add_stmt_without_update (&stmts,
+                                               gimple_build_nop ());
+
+         gsi_replace_with_seq_vops (gsi, stmts);
+         return true;
+       }
+    }
+
+  return false;
+}
+
+/* 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);
@@ -1750,7 +2162,7 @@ gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
        if (!fn_fwrite)
          return false;
 
-       gimple repl = gimple_build_call (fn_fwrite, 4, arg0,
+       gimple *repl = gimple_build_call (fn_fwrite, 4, arg0,
                                         size_one_node, len, arg1);
        replace_call_with_call_and_fold (gsi, repl);
        return true;
@@ -1772,7 +2184,7 @@ 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);
+  gimple *stmt = gsi_stmt (*gsi);
   location_t loc = gimple_location (stmt);
   bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
   tree fn;
@@ -1788,10 +2200,11 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
        }
       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);
+         gimple_seq stmts = NULL;
+         len = gimple_convert_to_ptrofftype (&stmts, loc, len);
+         tree temp = gimple_build (&stmts, loc, POINTER_PLUS_EXPR,
+                                   TREE_TYPE (dest), dest, len);
+         gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
          replace_call_with_value (gsi, temp);
          return true;
        }
@@ -1818,7 +2231,7 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
                  if (!fn)
                    return false;
 
-                 gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+                 gimple *repl = gimple_build_call (fn, 4, dest, src, len, size);
                  replace_call_with_call_and_fold (gsi, repl);
                  return true;
                }
@@ -1856,7 +2269,7 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
   if (!fn)
     return false;
 
-  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  gimple *repl = gimple_build_call (fn, 3, dest, src, len);
   replace_call_with_call_and_fold (gsi, repl);
   return true;
 }
@@ -1873,7 +2286,7 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
                                tree src, tree size,
                                enum built_in_function fcode)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   location_t loc = gimple_location (stmt);
   bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
   tree len, fn;
@@ -1910,7 +2323,7 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
                  if (!fn)
                    return false;
 
-                 gimple repl = gimple_build_call (fn, 3, dest, src, size);
+                 gimple *repl = gimple_build_call (fn, 3, dest, src, size);
                  replace_call_with_call_and_fold (gsi, repl);
                  return true;
                }
@@ -1924,12 +2337,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
              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);
+             gimple_seq stmts = NULL;
+             len = gimple_convert (&stmts, loc, size_type_node, len);
+             len = gimple_build (&stmts, loc, PLUS_EXPR, size_type_node, len,
+                                 build_int_cst (size_type_node, 1));
+             gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
+             gimple *repl = gimple_build_call (fn, 4, dest, src, len, size);
              replace_call_with_call_and_fold (gsi, repl);
              return true;
            }
@@ -1947,7 +2360,7 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   if (!fn)
     return false;
 
-  gimple repl = gimple_build_call (fn, 2, dest, src);
+  gimple *repl = gimple_build_call (fn, 2, dest, src);
   replace_call_with_call_and_fold (gsi, repl);
   return true;
 }
@@ -1963,7 +2376,7 @@ gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
                                 tree len, tree size,
                                 enum built_in_function fcode)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   bool ignore = gimple_call_lhs (stmt) == NULL_TREE;
   tree fn;
 
@@ -1974,7 +2387,7 @@ gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
        fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK);
        if (fn)
         {
-          gimple repl = gimple_build_call (fn, 4, dest, src, len, size);
+          gimple *repl = gimple_build_call (fn, 4, dest, src, len, size);
           replace_call_with_call_and_fold (gsi, repl);
           return true;
         }
@@ -2007,11 +2420,75 @@ gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
   if (!fn)
     return false;
 
-  gimple repl = gimple_build_call (fn, 3, dest, src, len);
+  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, false);
+  /* 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
@@ -2200,7 +2677,7 @@ gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi,
 static bool
 gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   tree dest = gimple_call_arg (stmt, 0);
   tree fmt = gimple_call_arg (stmt, 1);
   tree orig = NULL_TREE;
@@ -2238,7 +2715,7 @@ gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
       /* 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 *repl = gimple_build_call (fn, 2, dest, fmt);
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (gimple_call_lhs (stmt))
        {
@@ -2286,7 +2763,7 @@ gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
 
       /* Convert sprintf (str1, "%s", str2) into strcpy (str1, str2).  */
       gimple_seq stmts = NULL;
-      gimple repl = gimple_build_call (fn, 2, dest, orig);
+      gimple *repl = gimple_build_call (fn, 2, dest, orig);
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (gimple_call_lhs (stmt))
        {
@@ -2373,7 +2850,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
        return false;
 
       gimple_seq stmts = NULL;
-      gimple repl = gimple_build_call (fn, 2, dest, fmt);
+      gimple *repl = gimple_build_call (fn, 2, dest, fmt);
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (gimple_call_lhs (stmt))
        {
@@ -2409,7 +2886,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
        return false;
 
       tree orig_len = get_maxval_strlen (orig, 0);
-      if (!orig_len)
+      if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
        return false;
 
       /* We could expand this as
@@ -2425,7 +2902,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
       /* 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 *repl = gimple_build_call (fn, 2, dest, orig);
       gimple_seq_add_stmt_without_update (&stmts, repl);
       if (gimple_call_lhs (stmt))
        {
@@ -2453,13 +2930,277 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
   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);
+    }
+
+  if (!init_target_chars ())
+    return false;
+
+  /* If the format doesn't contain % args or %%, use strcpy.  */
+  if (strchr (fmt_str, target_percent) == NULL)
+    {
+      if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK
+         && arg)
+       return false;
+
+      /* If the format specifier was "", fprintf does nothing.  */
+      if (fmt_str[0] == '\0')
+       {
+         replace_call_with_value (gsi, NULL_TREE);
+         return true;
+       }
+
+      /* 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;
+       }
+    }
+
+  /* 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 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;
+       }
+    }
+
+  /* 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.
+
+   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_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;
+
+  /* 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_PRINTF_UNLOCKED)
+    {
+      /* 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);
+    }
+
+  if (!init_target_chars ())
+    return false;
+
+  if (strcmp (fmt_str, target_percent_s) == 0
+      || strchr (fmt_str, target_percent) == NULL)
+    {
+      const char *str;
+
+      if (strcmp (fmt_str, target_percent_s) == 0)
+       {
+         if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+           return false;
+
+         if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+           return false;
+
+         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;
+       }
+
+      /* If the string was "", printf does nothing.  */
+      if (str[0] == '\0')
+       {
+         replace_call_with_value (gsi, NULL_TREE);
+         return true;
+       }
+
+      /* 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;
+       }
+    }
+
+  /* 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;
+
+  /* If the format specifier was "%s\n", call __builtin_puts(arg).  */
+  else if (strcmp (fmt_str, target_percent_s_newline) == 0)
+    {
+      if (!arg || ! POINTER_TYPE_P (TREE_TYPE (arg)))
+       return false;
+      if (fn_puts)
+       {
+         gcall *repl = gimple_build_call (fn_puts, 1, arg);
+         replace_call_with_call_and_fold (gsi, repl);
+         return true;
+       }
+    }
+
+  /* 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;
+       }
+    }
+
+  return false;
+}
+
+
 
 /* Fold a call to __builtin_strlen with known length LEN.  */
 
 static bool
 gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   tree len = get_maxval_strlen (gimple_call_arg (stmt, 0), 0);
   if (!len)
     return false;
@@ -2468,6 +3209,47 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
   return true;
 }
 
+/* Fold a call to __builtin_acc_on_device.  */
+
+static bool
+gimple_fold_builtin_acc_on_device (gimple_stmt_iterator *gsi, tree arg0)
+{
+  /* Defer folding until we know which compiler we're in.  */
+  if (symtab->state != EXPANSION)
+    return false;
+
+  unsigned val_host = GOMP_DEVICE_HOST;
+  unsigned val_dev = GOMP_DEVICE_NONE;
+
+#ifdef ACCEL_COMPILER
+  val_host = GOMP_DEVICE_NOT_HOST;
+  val_dev = ACCEL_COMPILER_acc_device;
+#endif
+
+  location_t loc = gimple_location (gsi_stmt (*gsi));
+  
+  tree host_eq = make_ssa_name (boolean_type_node);
+  gimple *host_ass = gimple_build_assign
+    (host_eq, EQ_EXPR, arg0, build_int_cst (TREE_TYPE (arg0), val_host));
+  gimple_set_location (host_ass, loc);
+  gsi_insert_before (gsi, host_ass, GSI_SAME_STMT);
+
+  tree dev_eq = make_ssa_name (boolean_type_node);
+  gimple *dev_ass = gimple_build_assign
+    (dev_eq, EQ_EXPR, arg0, build_int_cst (TREE_TYPE (arg0), val_dev));
+  gimple_set_location (dev_ass, loc);
+  gsi_insert_before (gsi, dev_ass, GSI_SAME_STMT);
+
+  tree result = make_ssa_name (boolean_type_node);
+  gimple *result_ass = gimple_build_assign
+    (result, BIT_IOR_EXPR, host_eq, dev_eq);
+  gimple_set_location (result_ass, loc);
+  gsi_insert_before (gsi, result_ass, GSI_SAME_STMT);
+
+  replace_call_with_value (gsi, result);
+
+  return true;
+}
 
 /* Fold the non-target builtin at *GSI and return whether any simplification
    was made.  */
@@ -2483,7 +3265,9 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
   if (avoid_folding_inline_builtin (callee))
     return false;
 
-  switch (DECL_FUNCTION_CODE (callee))
+  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,
@@ -2506,7 +3290,7 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
                                            gimple_call_arg (stmt, 1), 3);
     case BUILT_IN_SPRINTF_CHK:
     case BUILT_IN_VSPRINTF_CHK:
-      return gimple_fold_builtin_sprintf_chk (gsi, DECL_FUNCTION_CODE (callee));
+      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:
@@ -2525,6 +3309,23 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     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_INDEX:
+    case BUILT_IN_STRCHR:
+      return gimple_fold_builtin_strchr (gsi, false);
+    case BUILT_IN_RINDEX:
+    case BUILT_IN_STRRCHR:
+      return gimple_fold_builtin_strchr (gsi, true);
+    case BUILT_IN_STRSTR:
+      return gimple_fold_builtin_strstr (gsi);
+    case BUILT_IN_STRCMP:
+    case BUILT_IN_STRCASECMP:
+    case BUILT_IN_STRNCMP:
+    case BUILT_IN_STRNCASECMP:
+      return gimple_fold_builtin_string_compare (gsi);
+    case BUILT_IN_MEMCHR:
+      return gimple_fold_builtin_memchr (gsi);
     case BUILT_IN_FPUTS:
       return gimple_fold_builtin_fputs (gsi, gimple_call_arg (stmt, 0),
                                        gimple_call_arg (stmt, 1), false);
@@ -2540,14 +3341,16 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
                                             gimple_call_arg (stmt, 1),
                                             gimple_call_arg (stmt, 2),
                                             gimple_call_arg (stmt, 3),
-                                            DECL_FUNCTION_CODE (callee));
+                                            fcode);
+    case BUILT_IN_STPCPY:
+      return gimple_fold_builtin_stpcpy (gsi);
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
       return gimple_fold_builtin_stxcpy_chk (gsi,
                                             gimple_call_arg (stmt, 0),
                                             gimple_call_arg (stmt, 1),
                                             gimple_call_arg (stmt, 2),
-                                            DECL_FUNCTION_CODE (callee));
+                                            fcode);
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       return gimple_fold_builtin_stxncpy_chk (gsi,
@@ -2555,15 +3358,57 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
                                              gimple_call_arg (stmt, 1),
                                              gimple_call_arg (stmt, 2),
                                              gimple_call_arg (stmt, 3),
-                                             DECL_FUNCTION_CODE (callee));
+                                             fcode);
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      return gimple_fold_builtin_snprintf_chk (gsi,
-                                              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);
+      break;
+    case BUILT_IN_ACC_ON_DEVICE:
+      return gimple_fold_builtin_acc_on_device (gsi,
+                                               gimple_call_arg (stmt, 0));
     default:;
     }
 
@@ -2584,6 +3429,183 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
   return false;
 }
 
+/* Transform IFN_GOACC_DIM_SIZE and IFN_GOACC_DIM_POS internal
+   function calls to constants, where possible.  */
+
+static tree
+fold_internal_goacc_dim (const gimple *call)
+{
+  int axis = oacc_get_ifn_dim_arg (call);
+  int size = oacc_get_fn_dim_size (current_function_decl, axis);
+  bool is_pos = gimple_call_internal_fn (call) == IFN_GOACC_DIM_POS;
+  tree result = NULL_TREE;
+
+  /* If the size is 1, or we only want the size and it is not dynamic,
+     we know the answer.  */
+  if (size == 1 || (!is_pos && size))
+    {
+      tree type = TREE_TYPE (gimple_call_lhs (call));
+      result = build_int_cst (type, size - is_pos);
+    }
+
+  return result;
+}
+
+/* Return true if stmt is __atomic_compare_exchange_N call which is suitable
+   for conversion into ATOMIC_COMPARE_EXCHANGE if the second argument is
+   &var where var is only addressable because of such calls.  */
+
+bool
+optimize_atomic_compare_exchange_p (gimple *stmt)
+{
+  if (gimple_call_num_args (stmt) != 6
+      || !flag_inline_atomics
+      || !optimize
+      || (flag_sanitize & (SANITIZE_THREAD | SANITIZE_ADDRESS)) != 0
+      || !gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+      || !gimple_vdef (stmt)
+      || !gimple_vuse (stmt))
+    return false;
+
+  tree fndecl = gimple_call_fndecl (stmt);
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_2:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8:
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_16:
+      break;
+    default:
+      return false;
+    }
+
+  tree expected = gimple_call_arg (stmt, 1);
+  if (TREE_CODE (expected) != ADDR_EXPR
+      || !SSA_VAR_P (TREE_OPERAND (expected, 0)))
+    return false;
+
+  tree etype = TREE_TYPE (TREE_OPERAND (expected, 0));
+  if (!is_gimple_reg_type (etype)
+      || !auto_var_in_fn_p (TREE_OPERAND (expected, 0), current_function_decl)
+      || TREE_THIS_VOLATILE (etype)
+      || VECTOR_TYPE_P (etype)
+      || TREE_CODE (etype) == COMPLEX_TYPE
+      /* Don't optimize floating point expected vars, VIEW_CONVERT_EXPRs
+        might not preserve all the bits.  See PR71716.  */
+      || SCALAR_FLOAT_TYPE_P (etype)
+      || TYPE_PRECISION (etype) != GET_MODE_BITSIZE (TYPE_MODE (etype)))
+    return false;
+
+  tree weak = gimple_call_arg (stmt, 3);
+  if (!integer_zerop (weak) && !integer_onep (weak))
+    return false;
+
+  tree parmt = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  tree itype = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (parmt)));
+  machine_mode mode = TYPE_MODE (itype);
+
+  if (direct_optab_handler (atomic_compare_and_swap_optab, mode)
+      == CODE_FOR_nothing
+      && optab_handler (sync_compare_and_swap_optab, mode) == CODE_FOR_nothing)
+    return false;
+
+  if (int_size_in_bytes (etype) != GET_MODE_SIZE (mode))
+    return false;
+
+  return true;
+}
+
+/* Fold
+     r = __atomic_compare_exchange_N (p, &e, d, w, s, f);
+   into
+     _Complex uintN_t t = ATOMIC_COMPARE_EXCHANGE (p, e, d, w * 256 + N, s, f);
+     i = IMAGPART_EXPR <t>;
+     r = (_Bool) i;
+     e = REALPART_EXPR <t>;  */
+
+void
+fold_builtin_atomic_compare_exchange (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree fndecl = gimple_call_fndecl (stmt);
+  tree parmt = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  tree itype = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (parmt)));
+  tree ctype = build_complex_type (itype);
+  tree expected = TREE_OPERAND (gimple_call_arg (stmt, 1), 0);
+  bool throws = false;
+  edge e = NULL;
+  gimple *g = gimple_build_assign (make_ssa_name (TREE_TYPE (expected)),
+                                  expected);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+  gimple_stmt_iterator gsiret = gsi_for_stmt (g);
+  if (!useless_type_conversion_p (itype, TREE_TYPE (expected)))
+    {
+      g = gimple_build_assign (make_ssa_name (itype), VIEW_CONVERT_EXPR,
+                              build1 (VIEW_CONVERT_EXPR, itype,
+                                      gimple_assign_lhs (g)));
+      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+    }
+  int flag = (integer_onep (gimple_call_arg (stmt, 3)) ? 256 : 0)
+            + int_size_in_bytes (itype);
+  g = gimple_build_call_internal (IFN_ATOMIC_COMPARE_EXCHANGE, 6,
+                                 gimple_call_arg (stmt, 0),
+                                 gimple_assign_lhs (g),
+                                 gimple_call_arg (stmt, 2),
+                                 build_int_cst (integer_type_node, flag),
+                                 gimple_call_arg (stmt, 4),
+                                 gimple_call_arg (stmt, 5));
+  tree lhs = make_ssa_name (ctype);
+  gimple_call_set_lhs (g, lhs);
+  gimple_set_vdef (g, gimple_vdef (stmt));
+  gimple_set_vuse (g, gimple_vuse (stmt));
+  SSA_NAME_DEF_STMT (gimple_vdef (g)) = g;
+  tree oldlhs = gimple_call_lhs (stmt);
+  if (stmt_can_throw_internal (stmt))
+    {
+      throws = true;
+      e = find_fallthru_edge (gsi_bb (*gsi)->succs);
+    }
+  gimple_call_set_nothrow (as_a <gcall *> (g),
+                          gimple_call_nothrow_p (as_a <gcall *> (stmt)));
+  gimple_call_set_lhs (stmt, NULL_TREE);
+  gsi_replace (gsi, g, true);
+  if (oldlhs)
+    {
+      g = gimple_build_assign (make_ssa_name (itype), IMAGPART_EXPR,
+                              build1 (IMAGPART_EXPR, itype, lhs));
+      if (throws)
+       {
+         gsi_insert_on_edge_immediate (e, g);
+         *gsi = gsi_for_stmt (g);
+       }
+      else
+       gsi_insert_after (gsi, g, GSI_NEW_STMT);
+      g = gimple_build_assign (oldlhs, NOP_EXPR, gimple_assign_lhs (g));
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+  g = gimple_build_assign (make_ssa_name (itype), REALPART_EXPR,
+                          build1 (REALPART_EXPR, itype, lhs));
+  if (throws && oldlhs == NULL_TREE)
+    {
+      gsi_insert_on_edge_immediate (e, g);
+      *gsi = gsi_for_stmt (g);
+    }
+  else
+    gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!useless_type_conversion_p (TREE_TYPE (expected), itype))
+    {
+      g = gimple_build_assign (make_ssa_name (TREE_TYPE (expected)),
+                              VIEW_CONVERT_EXPR,
+                              build1 (VIEW_CONVERT_EXPR, TREE_TYPE (expected),
+                                      gimple_assign_lhs (g)));
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+  g = gimple_build_assign (expected, SSA_NAME, gimple_assign_lhs (g));
+  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  *gsi = gsiret;
+}
+
 /* 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.  */
@@ -2677,25 +3699,39 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                }
              if (targets.length () == 1)
                {
-                 gimple_call_set_fndecl (stmt, targets[0]->decl);
+                 tree fndecl = targets[0]->decl;
+                 gimple_call_set_fndecl (stmt, fndecl);
                  changed = true;
+                 /* If changing the call to __cxa_pure_virtual
+                    or similar noreturn function, adjust gimple_call_fntype
+                    too.  */
+                 if (gimple_call_noreturn_p (stmt)
+                     && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fndecl)))
+                     && TYPE_ARG_TYPES (TREE_TYPE (fndecl))
+                     && (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)))
+                         == void_type_node))
+                   gimple_call_set_fntype (stmt, TREE_TYPE (fndecl));
                  /* If the call becomes noreturn, remove the lhs.  */
-                 if (lhs && (gimple_call_flags (stmt) & ECF_NORETURN))
+                 if (lhs
+                     && gimple_call_noreturn_p (stmt)
+                     && (VOID_TYPE_P (TREE_TYPE (gimple_call_fntype (stmt)))
+                         || should_remove_lhs_p (lhs)))
                    {
                      if (TREE_CODE (lhs) == SSA_NAME)
                        {
                          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);
+                         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
                {
                  tree fndecl = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
-                 gimple new_stmt = gimple_build_call (fndecl, 0);
+                 gimple *new_stmt = gimple_build_call (fndecl, 0);
                  gimple_set_location (new_stmt, gimple_location (stmt));
                  if (lhs && TREE_CODE (lhs) == SSA_NAME)
                    {
@@ -2777,12 +3813,16 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
                  && tree_int_cst_le (gimple_call_arg (stmt, 1),
                                      gimple_call_arg (stmt, 2))))
            {
-             gsi_replace (gsi, gimple_build_nop (), true);
+             gsi_replace (gsi, gimple_build_nop (), false);
              unlink_stmt_vdef (stmt);
              release_defs (stmt);
              return true;
            }
          break;
+       case IFN_GOACC_DIM_SIZE:
+       case IFN_GOACC_DIM_POS:
+         result = fold_internal_goacc_dim (stmt);
+         break;
        case IFN_UBSAN_CHECK_ADD:
          subcode = PLUS_EXPR;
          break;
@@ -2903,6 +3943,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 }
 
 
+/* Return true whether NAME has a use on STMT.  */
+
+static bool
+has_use_on_stmt (tree name, gimple *stmt)
+{
+  imm_use_iterator iter;
+  use_operand_p use_p;
+  FOR_EACH_IMM_USE_FAST (use_p, iter, name)
+    if (USE_STMT (use_p) == stmt)
+      return true;
+  return false;
+}
+
 /* Worker for fold_stmt_1 dispatch to pattern based folding with
    gimple_simplify.
 
@@ -2915,18 +3968,35 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
                                  code_helper rcode, tree *ops,
                                  gimple_seq *seq, bool inplace)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  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.  */
+     newly created statements.  See also maybe_push_res_to_seq.
+     As an exception allow such uses if there was a use of the
+     same SSA name on the old stmt.  */
   if ((TREE_CODE (ops[0]) == SSA_NAME
-       && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[0]))
+       && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[0])
+       && !has_use_on_stmt (ops[0], stmt))
       || (ops[1]
          && TREE_CODE (ops[1]) == SSA_NAME
-         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[1]))
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[1])
+         && !has_use_on_stmt (ops[1], stmt))
       || (ops[2]
          && TREE_CODE (ops[2]) == SSA_NAME
-         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])))
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])
+         && !has_use_on_stmt (ops[2], stmt))
+      || (COMPARISON_CLASS_P (ops[0])
+         && ((TREE_CODE (TREE_OPERAND (ops[0], 0)) == SSA_NAME
+              && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (ops[0], 0))
+              && !has_use_on_stmt (TREE_OPERAND (ops[0], 0), stmt))
+             || (TREE_CODE (TREE_OPERAND (ops[0], 1)) == SSA_NAME
+                 && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (ops[0], 1))
+                 && !has_use_on_stmt (TREE_OPERAND (ops[0], 1), stmt)))))
+    return false;
+
+  /* Don't insert new statements when INPLACE is true, even if we could
+     reuse STMT for the final statement.  */
+  if (inplace && !gimple_seq_empty_p (*seq))
     return false;
 
   if (gcond *cond_stmt = dyn_cast <gcond *> (stmt))
@@ -2979,8 +4049,7 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
          || 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]);
+                                 TREE_TYPE (gimple_assign_lhs (stmt)), ops);
          gimple_assign_set_rhs_with_ops (gsi, rcode, ops[0], ops[1], ops[2]);
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
@@ -2994,13 +4063,35 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
          return true;
        }
     }
+  else if (rcode.is_fn_code ()
+          && gimple_call_combined_fn (stmt) == rcode)
+    {
+      unsigned i;
+      for (i = 0; i < gimple_call_num_args (stmt); ++i)
+       {
+         gcc_assert (ops[i] != NULL_TREE);
+         gimple_call_set_arg (stmt, i, ops[i]);
+       }
+      if (i < 3)
+       gcc_assert (ops[i] == NULL_TREE);
+      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);
-         maybe_push_res_to_seq (rcode, TREE_TYPE (lhs),
-                                ops, seq, lhs);
+         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 ");
@@ -3026,6 +4117,44 @@ maybe_canonicalize_mem_ref_addr (tree *t)
   if (TREE_CODE (*t) == ADDR_EXPR)
     t = &TREE_OPERAND (*t, 0);
 
+  /* The C and C++ frontends use an ARRAY_REF for indexing with their
+     generic vector extension.  The actual vector referenced is
+     view-converted to an array type for this purpose.  If the index
+     is constant the canonical representation in the middle-end is a
+     BIT_FIELD_REF so re-write the former to the latter here.  */
+  if (TREE_CODE (*t) == ARRAY_REF
+      && TREE_CODE (TREE_OPERAND (*t, 0)) == VIEW_CONVERT_EXPR
+      && TREE_CODE (TREE_OPERAND (*t, 1)) == INTEGER_CST
+      && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*t, 0), 0))))
+    {
+      tree vtype = TREE_TYPE (TREE_OPERAND (TREE_OPERAND (*t, 0), 0));
+      if (VECTOR_TYPE_P (vtype))
+       {
+         tree low = array_ref_low_bound (*t);
+         if (TREE_CODE (low) == INTEGER_CST)
+           {
+             if (tree_int_cst_le (low, TREE_OPERAND (*t, 1)))
+               {
+                 widest_int idx = wi::sub (wi::to_widest (TREE_OPERAND (*t, 1)),
+                                           wi::to_widest (low));
+                 idx = wi::mul (idx, wi::to_widest
+                                        (TYPE_SIZE (TREE_TYPE (*t))));
+                 widest_int ext
+                   = wi::add (idx, wi::to_widest (TYPE_SIZE (TREE_TYPE (*t))));
+                 if (wi::les_p (ext, wi::to_widest (TYPE_SIZE (vtype))))
+                   {
+                     *t = build3_loc (EXPR_LOCATION (*t), BIT_FIELD_REF,
+                                      TREE_TYPE (*t),
+                                      TREE_OPERAND (TREE_OPERAND (*t, 0), 0),
+                                      TYPE_SIZE (TREE_TYPE (*t)),
+                                      wide_int_to_tree (sizetype, idx));
+                     res = true;
+                   }
+               }
+           }
+       }
+    }
+
   while (handled_component_p (*t))
     t = &TREE_OPERAND (*t, 0);
 
@@ -3106,14 +4235,17 @@ static bool
 fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
 {
   bool changed = false;
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
+  bool nowarning = gimple_no_warning_p (stmt);
   unsigned i;
+  fold_defer_overflow_warnings ();
 
   /* 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.  */
+     propagated.
+     Also canonicalize operand order.  */
   switch (gimple_code (stmt))
     {
     case GIMPLE_ASSIGN:
@@ -3129,6 +4261,27 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
              && maybe_canonicalize_mem_ref_addr (lhs))
            changed = true;
        }
+      else
+       {
+         /* Canonicalize operand order.  */
+         enum tree_code code = gimple_assign_rhs_code (stmt);
+         if (TREE_CODE_CLASS (code) == tcc_comparison
+             || commutative_tree_code (code)
+             || commutative_ternary_tree_code (code))
+           {
+             tree rhs1 = gimple_assign_rhs1 (stmt);
+             tree rhs2 = gimple_assign_rhs2 (stmt);
+             if (tree_swap_operands_p (rhs1, rhs2))
+               {
+                 gimple_assign_set_rhs1 (stmt, rhs2);
+                 gimple_assign_set_rhs2 (stmt, rhs1);
+                 if (TREE_CODE_CLASS (code) == tcc_comparison)
+                   gimple_assign_set_rhs_code (stmt,
+                                               swap_tree_comparison (code));
+                 changed = true;
+               }
+           }
+       }
       break;
     case GIMPLE_CALL:
       {
@@ -3179,6 +4332,21 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
            changed = true;
        }
       break;
+    case GIMPLE_COND:
+      {
+       /* Canonicalize operand order.  */
+       tree lhs = gimple_cond_lhs (stmt);
+       tree rhs = gimple_cond_rhs (stmt);
+       if (tree_swap_operands_p (lhs, rhs))
+         {
+           gcond *gc = as_a <gcond *> (stmt);
+           gimple_cond_set_lhs (gc, rhs);
+           gimple_cond_set_rhs (gc, lhs);
+           gimple_cond_set_code (gc,
+                                 swap_tree_comparison (gimple_cond_code (gc)));
+           changed = true;
+         }
+      }
     default:;
     }
 
@@ -3190,7 +4358,8 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
       gimple_seq seq = NULL;
       code_helper rcode;
       tree ops[3] = {};
-      if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq, valueize))
+      if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq,
+                          valueize, valueize))
        {
          if (replace_stmt_with_simplification (gsi, rcode, ops, &seq, inplace))
            changed = true;
@@ -3206,23 +4375,51 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
     {
     case GIMPLE_ASSIGN:
       {
-       unsigned old_num_ops = gimple_num_ops (stmt);
-       enum tree_code subcode = gimple_assign_rhs_code (stmt);
-       tree lhs = gimple_assign_lhs (stmt);
-       tree new_rhs;
-       /* First canonicalize operand order.  This avoids building new
-          trees if this is the only thing fold would later do.  */
-       if ((commutative_tree_code (subcode)
-            || commutative_ternary_tree_code (subcode))
-           && tree_swap_operands_p (gimple_assign_rhs1 (stmt),
-                                    gimple_assign_rhs2 (stmt), false))
+       /* Try to canonicalize for boolean-typed X the comparisons
+          X == 0, X == 1, X != 0, and X != 1.  */
+       if (gimple_assign_rhs_code (stmt) == EQ_EXPR
+           || gimple_assign_rhs_code (stmt) == NE_EXPR)
          {
-           tree tem = gimple_assign_rhs1 (stmt);
-           gimple_assign_set_rhs1 (stmt, gimple_assign_rhs2 (stmt));
-           gimple_assign_set_rhs2 (stmt, tem);
-           changed = true;
+           tree lhs = gimple_assign_lhs (stmt);
+           tree op1 = gimple_assign_rhs1 (stmt);
+           tree op2 = gimple_assign_rhs2 (stmt);
+           tree type = TREE_TYPE (op1);
+
+           /* Check whether the comparison operands are of the same boolean
+              type as the result type is.
+              Check that second operand is an integer-constant with value
+              one or zero.  */
+           if (TREE_CODE (op2) == INTEGER_CST
+               && (integer_zerop (op2) || integer_onep (op2))
+               && useless_type_conversion_p (TREE_TYPE (lhs), type))
+             {
+               enum tree_code cmp_code = gimple_assign_rhs_code (stmt);
+               bool is_logical_not = false;
+
+               /* X == 0 and X != 1 is a logical-not.of X
+                  X == 1 and X != 0 is X  */
+               if ((cmp_code == EQ_EXPR && integer_zerop (op2))
+                   || (cmp_code == NE_EXPR && integer_onep (op2)))
+                 is_logical_not = true;
+
+               if (is_logical_not == false)
+                 gimple_assign_set_rhs_with_ops (gsi, TREE_CODE (op1), op1);
+               /* Only for one-bit precision typed X the transformation
+                  !X -> ~X is valied.  */
+               else if (TYPE_PRECISION (type) == 1)
+                 gimple_assign_set_rhs_with_ops (gsi, BIT_NOT_EXPR, op1);
+               /* Otherwise we use !X -> X ^ 1.  */
+               else
+                 gimple_assign_set_rhs_with_ops (gsi, BIT_XOR_EXPR, op1,
+                                                 build_int_cst (type, 1));
+               changed = true;
+               break;
+             }
          }
-       new_rhs = fold_gimple_assign (gsi);
+
+       unsigned old_num_ops = gimple_num_ops (stmt);
+       tree lhs = gimple_assign_lhs (stmt);
+       tree new_rhs = fold_gimple_assign (gsi);
        if (new_rhs
            && !useless_type_conversion_p (TREE_TYPE (lhs),
                                           TREE_TYPE (new_rhs)))
@@ -3237,10 +4434,6 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
        break;
       }
 
-    case GIMPLE_COND:
-      changed |= fold_gimple_cond (as_a <gcond *> (stmt));
-      break;
-
     case GIMPLE_CALL:
       changed |= gimple_fold_call (gsi, inplace);
       break;
@@ -3318,6 +4511,24 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
        }
       break;
 
+    case GIMPLE_RETURN:
+      {
+       greturn *ret_stmt = as_a<greturn *> (stmt);
+       tree ret = gimple_return_retval(ret_stmt);
+
+       if (ret && TREE_CODE (ret) == SSA_NAME && valueize)
+         {
+           tree val = valueize (ret);
+           if (val && val != ret
+               && may_propagate_copy (ret, val))
+             {
+               gimple_return_set_retval (ret_stmt, val);
+               changed = true;
+             }
+         }
+      }
+      break;
+
     default:;
     }
 
@@ -3338,6 +4549,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
        }
     }
 
+  fold_undefer_overflow_warnings (changed && !nowarning, stmt, 0);
   return changed;
 }
 
@@ -3390,7 +4602,7 @@ fold_stmt (gimple_stmt_iterator *gsi, tree (*valueize) (tree))
 bool
 fold_stmt_inplace (gimple_stmt_iterator *gsi)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
   bool changed = fold_stmt_1 (gsi, true, no_follow_ssa_edges);
   gcc_assert (gsi_stmt (*gsi) == stmt);
   return changed;
@@ -3414,7 +4626,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),
@@ -3433,7 +4645,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),
@@ -3451,7 +4663,7 @@ static bool
 same_bool_comparison_p (const_tree expr, enum tree_code code,
                        const_tree op1, const_tree op2)
 {
-  gimple s;
+  gimple *s;
 
   /* The obvious case.  */
   if (TREE_CODE (expr) == code
@@ -3514,12 +4726,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)))
@@ -3538,7 +4750,7 @@ static tree
 and_var_with_comparison (tree var, bool invert,
                         enum tree_code code2, tree op2a, tree op2b);
 static tree
-and_var_with_comparison_1 (gimple stmt, 
+and_var_with_comparison_1 (gimple *stmt,
                           enum tree_code code2, tree op2a, tree op2b);
 static tree
 or_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
@@ -3547,7 +4759,7 @@ static tree
 or_var_with_comparison (tree var, bool invert,
                        enum tree_code code2, tree op2a, tree op2b);
 static tree
-or_var_with_comparison_1 (gimple stmt, 
+or_var_with_comparison_1 (gimple *stmt,
                          enum tree_code code2, tree op2a, tree op2b);
 
 /* Helper function for and_comparisons_1:  try to simplify the AND of the
@@ -3560,7 +4772,7 @@ and_var_with_comparison (tree var, bool invert,
                         enum tree_code code2, tree op2a, tree op2b)
 {
   tree t;
-  gimple stmt = SSA_NAME_DEF_STMT (var);
+  gimple *stmt = SSA_NAME_DEF_STMT (var);
 
   /* We can only deal with variables whose definitions are assignments.  */
   if (!is_gimple_assign (stmt))
@@ -3583,7 +4795,7 @@ and_var_with_comparison (tree var, bool invert,
    Return NULL_EXPR if we can't simplify this to a single expression.  */
 
 static tree
-and_var_with_comparison_1 (gimple stmt,
+and_var_with_comparison_1 (gimple *stmt,
                           enum tree_code code2, tree op2a, tree op2b)
 {
   tree var = gimple_assign_lhs (stmt);
@@ -3631,7 +4843,7 @@ and_var_with_comparison_1 (gimple stmt,
     {
       tree inner1 = gimple_assign_rhs1 (stmt);
       tree inner2 = gimple_assign_rhs2 (stmt);
-      gimple s;
+      gimple *s;
       tree t;
       tree partial = NULL_TREE;
       bool is_and = (innercode == BIT_AND_EXPR);
@@ -3918,7 +5130,7 @@ and_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
     {
       bool invert = ((code1 == EQ_EXPR && integer_zerop (op1b))
                     || (code1 == NE_EXPR && integer_onep (op1b)));
-      gimple stmt = SSA_NAME_DEF_STMT (op1a);
+      gimple *stmt = SSA_NAME_DEF_STMT (op1a);
       switch (gimple_code (stmt))
        {
        case GIMPLE_ASSIGN:
@@ -3963,7 +5175,7 @@ and_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
                           && !SSA_NAME_IS_DEFAULT_DEF (arg))
                    {
                      tree temp;
-                     gimple def_stmt = SSA_NAME_DEF_STMT (arg);
+                     gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
                      /* In simple cases we can look through PHI nodes,
                         but we have to be careful with loops.
                         See PR49073.  */
@@ -4023,7 +5235,7 @@ or_var_with_comparison (tree var, bool invert,
                        enum tree_code code2, tree op2a, tree op2b)
 {
   tree t;
-  gimple stmt = SSA_NAME_DEF_STMT (var);
+  gimple *stmt = SSA_NAME_DEF_STMT (var);
 
   /* We can only deal with variables whose definitions are assignments.  */
   if (!is_gimple_assign (stmt))
@@ -4046,7 +5258,7 @@ or_var_with_comparison (tree var, bool invert,
    Return NULL_EXPR if we can't simplify this to a single expression.  */
 
 static tree
-or_var_with_comparison_1 (gimple stmt,
+or_var_with_comparison_1 (gimple *stmt,
                          enum tree_code code2, tree op2a, tree op2b)
 {
   tree var = gimple_assign_lhs (stmt);
@@ -4094,7 +5306,7 @@ or_var_with_comparison_1 (gimple stmt,
     {
       tree inner1 = gimple_assign_rhs1 (stmt);
       tree inner2 = gimple_assign_rhs2 (stmt);
-      gimple s;
+      gimple *s;
       tree t;
       tree partial = NULL_TREE;
       bool is_or = (innercode == BIT_IOR_EXPR);
@@ -4382,7 +5594,7 @@ or_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
     {
       bool invert = ((code1 == EQ_EXPR && integer_zerop (op1b))
                     || (code1 == NE_EXPR && integer_onep (op1b)));
-      gimple stmt = SSA_NAME_DEF_STMT (op1a);
+      gimple *stmt = SSA_NAME_DEF_STMT (op1a);
       switch (gimple_code (stmt))
        {
        case GIMPLE_ASSIGN:
@@ -4427,7 +5639,7 @@ or_comparisons_1 (enum tree_code code1, tree op1a, tree op1b,
                           && !SSA_NAME_IS_DEFAULT_DEF (arg))
                    {
                      tree temp;
-                     gimple def_stmt = SSA_NAME_DEF_STMT (arg);
+                     gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
                      /* In simple cases we can look through PHI nodes,
                         but we have to be careful with loops.
                         See PR49073.  */
@@ -4488,7 +5700,7 @@ 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;
@@ -4497,22 +5709,25 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
      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)
-      && 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)
+  if (gimple_simplify (stmt, &rcode, ops, NULL, gvalueize, valueize))
+    {
+      tree res = NULL_TREE;
+      if (gimple_simplified_result_is_gimple_val (rcode, ops))
+       res = ops[0];
+      else if (mprts_hook)
+       res = mprts_hook (rcode, gimple_expr_type (stmt), ops);
+      if (res)
        {
-         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");
+         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;
        }
-      return res;
     }
 
   location_t loc = gimple_location (stmt);
@@ -4629,28 +5844,48 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
            return NULL_TREE;
 
           case GIMPLE_BINARY_RHS:
-            {
-              /* Handle binary operators that can appear in GIMPLE form.  */
-              tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
-              tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
-
-             /* Translate &x + CST into an invariant form suitable for
-                further propagation.  */
-             if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
-                 && TREE_CODE (op0) == ADDR_EXPR
-                 && TREE_CODE (op1) == INTEGER_CST)
-               {
-                 tree off = fold_convert (ptr_type_node, op1);
-                 return build_fold_addr_expr_loc
-                          (loc,
-                           fold_build2 (MEM_REF,
-                                        TREE_TYPE (TREE_TYPE (op0)),
-                                        unshare_expr (op0), off));
-               }
-
-              return fold_binary_loc (loc, subcode,
-                                     gimple_expr_type (stmt), op0, op1);
-            }
+           /* Translate &x + CST into an invariant form suitable for
+              further propagation.  */
+           if (subcode == POINTER_PLUS_EXPR)
+             {
+               tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
+               tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
+               if (TREE_CODE (op0) == ADDR_EXPR
+                   && TREE_CODE (op1) == INTEGER_CST)
+                 {
+                   tree off = fold_convert (ptr_type_node, op1);
+                   return build_fold_addr_expr_loc
+                       (loc,
+                        fold_build2 (MEM_REF,
+                                     TREE_TYPE (TREE_TYPE (op0)),
+                                     unshare_expr (op0), off));
+                 }
+             }
+           /* Canonicalize bool != 0 and bool == 0 appearing after
+              valueization.  While gimple_simplify handles this
+              it can get confused by the ~X == 1 -> X == 0 transform
+              which we cant reduce to a SSA name or a constant
+              (and we have no way to tell gimple_simplify to not
+              consider those transforms in the first place).  */
+           else if (subcode == EQ_EXPR
+                    || subcode == NE_EXPR)
+             {
+               tree lhs = gimple_assign_lhs (stmt);
+               tree op0 = gimple_assign_rhs1 (stmt);
+               if (useless_type_conversion_p (TREE_TYPE (lhs),
+                                              TREE_TYPE (op0)))
+                 {
+                   tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
+                   op0 = (*valueize) (op0);
+                   if (TREE_CODE (op0) == INTEGER_CST)
+                     std::swap (op0, op1);
+                   if (TREE_CODE (op1) == INTEGER_CST
+                       && ((subcode == NE_EXPR && integer_zerop (op1))
+                           || (subcode == EQ_EXPR && integer_onep (op1))))
+                     return op0;
+                 }
+             }
+           return NULL_TREE;
 
           case GIMPLE_TERNARY_RHS:
             {
@@ -4658,20 +5893,6 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
               tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
               tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
               tree op2 = (*valueize) (gimple_assign_rhs3 (stmt));
-
-             /* Fold embedded expressions in ternary codes.  */
-             if ((subcode == COND_EXPR
-                  || subcode == VEC_COND_EXPR)
-                 && COMPARISON_CLASS_P (op0))
-               {
-                 tree op00 = (*valueize) (TREE_OPERAND (op0, 0));
-                 tree op01 = (*valueize) (TREE_OPERAND (op0, 1));
-                 tree tem = fold_binary_loc (loc, TREE_CODE (op0),
-                                             TREE_TYPE (op0), op00, op01);
-                 if (tem)
-                   op0 = tem;
-               }
-
               return fold_ternary_loc (loc, subcode,
                                       gimple_expr_type (stmt), op0, op1, op2);
             }
@@ -4700,6 +5921,14 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
              case IFN_UBSAN_CHECK_MUL:
                subcode = MULT_EXPR;
                break;
+             case IFN_BUILTIN_EXPECT:
+                 {
+                   tree arg0 = gimple_call_arg (stmt, 0);
+                   tree op0 = (*valueize) (arg0);
+                   if (TREE_CODE (op0) == INTEGER_CST)
+                     return op0;
+                   return NULL_TREE;
+                 }
              default:
                return NULL_TREE;
              }
@@ -4773,7 +6002,7 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
    returns a constant according to is_gimple_min_invariant.  */
 
 tree
-gimple_fold_stmt_to_constant (gimple stmt, tree (*valueize) (tree))
+gimple_fold_stmt_to_constant (gimple *stmt, tree (*valueize) (tree))
 {
   tree res = gimple_fold_stmt_to_constant_1 (stmt, valueize);
   if (res && is_gimple_min_invariant (res))
@@ -4798,6 +6027,8 @@ get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
                      tree (*valueize)(tree))
 {
   HOST_WIDE_INT bit_offset2, size, max_size;
+  bool reverse;
+
   if (TREE_CODE (base) == MEM_REF)
     {
       if (!integer_zerop (TREE_OPERAND (base, 1)))
@@ -4815,6 +6046,9 @@ get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
         return NULL_TREE;
       base = TREE_OPERAND (base, 0);
     }
+  else if (valueize
+          && TREE_CODE (base) == SSA_NAME)
+    base = valueize (base);
 
   /* Get a CONSTRUCTOR.  If BASE is a VAR_DECL, get its
      DECL_INITIAL.  If BASE is a nested reference into another
@@ -4836,19 +6070,26 @@ get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
        return init;
       }
 
+    case VIEW_CONVERT_EXPR:
+      return get_base_constructor (TREE_OPERAND (base, 0),
+                                  bit_offset, valueize);
+
     case ARRAY_REF:
     case COMPONENT_REF:
-      base = get_ref_base_and_extent (base, &bit_offset2, &size, &max_size);
+      base = get_ref_base_and_extent (base, &bit_offset2, &size, &max_size,
+                                     &reverse);
       if (max_size == -1 || size != max_size)
        return NULL_TREE;
       *bit_offset +=  bit_offset2;
       return get_base_constructor (base, bit_offset, valueize);
 
-    case STRING_CST:
     case CONSTRUCTOR:
       return base;
 
     default:
+      if (CONSTANT_CLASS_P (base))
+       return base;
+
       return NULL_TREE;
     }
 }
@@ -4862,13 +6103,10 @@ fold_array_ctor_reference (tree type, tree ctor,
                           unsigned HOST_WIDE_INT size,
                           tree from_decl)
 {
-  unsigned HOST_WIDE_INT cnt;
-  tree cfield, cval;
   offset_int low_bound;
   offset_int elt_size;
-  offset_int index, max_index;
   offset_int access_index;
-  tree domain_type = NULL_TREE, index_type = NULL_TREE;
+  tree domain_type = NULL_TREE;
   HOST_WIDE_INT inner_offset;
 
   /* Compute low bound and elt size.  */
@@ -4877,22 +6115,22 @@ fold_array_ctor_reference (tree type, tree ctor,
   if (domain_type && TYPE_MIN_VALUE (domain_type))
     {
       /* Static constructors for variably sized objects makes no sense.  */
-      gcc_assert (TREE_CODE (TYPE_MIN_VALUE (domain_type)) == INTEGER_CST);
-      index_type = TREE_TYPE (TYPE_MIN_VALUE (domain_type));
+      if (TREE_CODE (TYPE_MIN_VALUE (domain_type)) != INTEGER_CST)
+       return NULL_TREE;
       low_bound = wi::to_offset (TYPE_MIN_VALUE (domain_type));
     }
   else
     low_bound = 0;
   /* Static constructors for variably sized objects makes no sense.  */
-  gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))))
-             == INTEGER_CST);
+  if (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor)))) != INTEGER_CST)
+    return NULL_TREE;
   elt_size = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))));
 
   /* We can handle only constantly sized accesses that are known to not
      be larger than size of array element.  */
   if (!TYPE_SIZE_UNIT (type)
       || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
-      || wi::lts_p (elt_size, wi::to_offset (TYPE_SIZE_UNIT (type)))
+      || elt_size < wi::to_offset (TYPE_SIZE_UNIT (type))
       || elt_size == 0)
     return NULL_TREE;
 
@@ -4900,9 +6138,6 @@ fold_array_ctor_reference (tree type, tree ctor,
   access_index = wi::udiv_trunc (offset_int (offset / BITS_PER_UNIT),
                                 elt_size);
   access_index += low_bound;
-  if (index_type)
-    access_index = wi::ext (access_index, TYPE_PRECISION (index_type),
-                           TYPE_SIGN (index_type));
 
   /* And offset within the access.  */
   inner_offset = offset % (elt_size.to_uhwi () * BITS_PER_UNIT);
@@ -4911,43 +6146,9 @@ fold_array_ctor_reference (tree type, tree ctor,
      care to fold accesses spanning multiple array indexes.  */
   if (inner_offset + size > elt_size.to_uhwi () * BITS_PER_UNIT)
     return NULL_TREE;
+  if (tree val = get_array_ctor_element_at_index (ctor, access_index))
+    return fold_ctor_reference (type, val, inner_offset, size, from_decl);
 
-  index = low_bound - 1;
-  if (index_type)
-    index = wi::ext (index, TYPE_PRECISION (index_type),
-                    TYPE_SIGN (index_type));
-
-  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval)
-    {
-      /* Array constructor might explicitely set index, or specify range
-        or leave index NULL meaning that it is next index after previous
-        one.  */
-      if (cfield)
-       {
-         if (TREE_CODE (cfield) == INTEGER_CST)
-           max_index = index = wi::to_offset (cfield);
-         else
-           {
-             gcc_assert (TREE_CODE (cfield) == RANGE_EXPR);
-             index = wi::to_offset (TREE_OPERAND (cfield, 0));
-             max_index = wi::to_offset (TREE_OPERAND (cfield, 1));
-           }
-       }
-      else
-       {
-         index += 1;
-         if (index_type)
-           index = wi::ext (index, TYPE_PRECISION (index_type),
-                            TYPE_SIGN (index_type));
-         max_index = index;
-       }
-
-      /* Do we have match?  */
-      if (wi::cmpu (access_index, index) >= 0
-         && wi::cmpu (access_index, max_index) <= 0)
-       return fold_ctor_reference (type, cval, inner_offset, size,
-                                   from_decl);
-    }
   /* When memory is not explicitely mentioned in constructor,
      it is 0 (or out of range).  */
   return build_zero_cst (type);
@@ -4984,8 +6185,7 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
 
       /* Compute bit offset of the field.  */
       bitoffset = (wi::to_offset (field_offset)
-                  + wi::lshift (wi::to_offset (byte_offset),
-                                LOG2_BITS_PER_UNIT));
+                  + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
       /* Compute bit offset where the field ends.  */
       if (field_size != NULL_TREE)
        bitoffset_end = bitoffset + wi::to_offset (field_size);
@@ -5006,7 +6206,7 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
             fields.  */
          if (wi::cmps (access_end, bitoffset_end) > 0)
            return NULL_TREE;
-         if (wi::lts_p (offset, bitoffset))
+         if (offset < bitoffset)
            return NULL_TREE;
          return fold_ctor_reference (type, cval,
                                      inner_offset.to_uhwi (), size,
@@ -5039,9 +6239,12 @@ fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
       && !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);
+       {
+         ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
+         if (ret)
+           STRIP_USELESS_TYPE_CONVERSION (ret);
+       }
       return ret;
     }
   /* For constants and byte-aligned/sized reads try to go through
@@ -5053,9 +6256,10 @@ fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
       && 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);
+      int len = native_encode_expr (ctor, buf, size / BITS_PER_UNIT,
+                                   offset / BITS_PER_UNIT);
+      if (len > 0)
+       return native_interpret_expr (type, buf, len);
     }
   if (TREE_CODE (ctor) == CONSTRUCTOR)
     {
@@ -5082,11 +6286,12 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
   tree ctor, idx, base;
   HOST_WIDE_INT offset, size, max_size;
   tree tem;
+  bool reverse;
 
   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);
@@ -5152,7 +6357,7 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
     case BIT_FIELD_REF:
     case TARGET_MEM_REF:
     case MEM_REF:
-      base = get_ref_base_and_extent (t, &offset, &size, &max_size);
+      base = get_ref_base_and_extent (t, &offset, &size, &max_size, &reverse);
       ctor = get_base_constructor (base, &offset, valueize);
 
       /* Empty constructor.  Always fold to 0.  */
@@ -5216,10 +6421,8 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT token,
     *can_refer = true;
 
   /* First of all double check we have virtual table.  */
-  if (TREE_CODE (v) != VAR_DECL
-      || !DECL_VIRTUAL_P (v))
+  if (!VAR_P (v) || !DECL_VIRTUAL_P (v))
     {
-      gcc_assert (in_lto_p);
       /* Pass down that we lost track of the target.  */
       if (can_refer)
        *can_refer = false;
@@ -5336,139 +6539,8 @@ gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo,
   return gimple_get_virt_method_for_vtable (token, v, offset, can_refer);
 }
 
-/* Return true iff VAL is a gimple expression that is known to be
-   non-negative.  Restricted to floating-point inputs.  */
-
-bool
-gimple_val_nonnegative_real_p (tree val)
-{
-  gimple def_stmt;
-
-  gcc_assert (val && SCALAR_FLOAT_TYPE_P (TREE_TYPE (val)));
-
-  /* Use existing logic for non-gimple trees.  */
-  if (tree_expr_nonnegative_p (val))
-    return true;
-
-  if (TREE_CODE (val) != SSA_NAME)
-    return false;
-
-  /* Currently we look only at the immediately defining statement
-     to make this determination, since recursion on defining 
-     statements of operands can lead to quadratic behavior in the
-     worst case.  This is expected to catch almost all occurrences
-     in practice.  It would be possible to implement limited-depth
-     recursion if important cases are lost.  Alternatively, passes
-     that need this information (such as the pow/powi lowering code
-     in the cse_sincos pass) could be revised to provide it through
-     dataflow propagation.  */
-
-  def_stmt = SSA_NAME_DEF_STMT (val);
-
-  if (is_gimple_assign (def_stmt))
-    {
-      tree op0, op1;
-
-      /* See fold-const.c:tree_expr_nonnegative_p for additional
-        cases that could be handled with recursion.  */
-
-      switch (gimple_assign_rhs_code (def_stmt))
-       {
-       case ABS_EXPR:
-         /* Always true for floating-point operands.  */
-         return true;
-
-       case MULT_EXPR:
-         /* True if the two operands are identical (since we are
-            restricted to floating-point inputs).  */
-         op0 = gimple_assign_rhs1 (def_stmt);
-         op1 = gimple_assign_rhs2 (def_stmt);
-
-         if (op0 == op1
-             || operand_equal_p (op0, op1, 0))
-           return true;
-
-       default:
-         return false;
-       }
-    }
-  else if (is_gimple_call (def_stmt))
-    {
-      tree fndecl = gimple_call_fndecl (def_stmt);
-      if (fndecl
-         && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
-       {
-         tree arg1;
-
-         switch (DECL_FUNCTION_CODE (fndecl))
-           {
-           CASE_FLT_FN (BUILT_IN_ACOS):
-           CASE_FLT_FN (BUILT_IN_ACOSH):
-           CASE_FLT_FN (BUILT_IN_CABS):
-           CASE_FLT_FN (BUILT_IN_COSH):
-           CASE_FLT_FN (BUILT_IN_ERFC):
-           CASE_FLT_FN (BUILT_IN_EXP):
-           CASE_FLT_FN (BUILT_IN_EXP10):
-           CASE_FLT_FN (BUILT_IN_EXP2):
-           CASE_FLT_FN (BUILT_IN_FABS):
-           CASE_FLT_FN (BUILT_IN_FDIM):
-           CASE_FLT_FN (BUILT_IN_HYPOT):
-           CASE_FLT_FN (BUILT_IN_POW10):
-             return true;
-
-           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))))
-               return true;
-
-             break;
-
-           CASE_FLT_FN (BUILT_IN_POWI):
-             /* True if the second argument is an even integer.  */
-             arg1 = gimple_call_arg (def_stmt, 1);
-
-             if (TREE_CODE (arg1) == INTEGER_CST
-                 && (TREE_INT_CST_LOW (arg1) & 1) == 0)
-               return true;
-
-             break;
-             
-           CASE_FLT_FN (BUILT_IN_POW):
-             /* True if the second argument is an even integer-valued
-                real.  */
-             arg1 = gimple_call_arg (def_stmt, 1);
-
-             if (TREE_CODE (arg1) == REAL_CST)
-               {
-                 REAL_VALUE_TYPE c;
-                 HOST_WIDE_INT n;
-
-                 c = TREE_REAL_CST (arg1);
-                 n = real_to_integer (&c);
-
-                 if ((n & 1) == 0)
-                   {
-                     REAL_VALUE_TYPE cint;
-                     real_from_integer (&cint, VOIDmode, n, SIGNED);
-                     if (real_identical (&c, &cint))
-                       return true;
-                   }
-               }
-
-             break;
-
-           default:
-             return false;
-           }
-       }
-    }
-
-  return false;
-}
-
-/* Given a pointer value OP0, return a simplified version of an
-   indirection through OP0, or NULL_TREE if no simplification is
+/* Given a pointer value T, return a simplified version of an
+   indirection through T, or NULL_TREE if no simplification is
    possible.  Note that the resulting type may be different from
    the type pointed to in the sense that it is still compatible
    from the langhooks point of view. */
@@ -5482,7 +6554,8 @@ gimple_fold_indirect_ref (tree t)
 
   STRIP_NOPS (sub);
   subtype = TREE_TYPE (sub);
-  if (!POINTER_TYPE_P (subtype))
+  if (!POINTER_TYPE_P (subtype)
+      || TYPE_REF_CAN_ALIAS_ALL (ptype))
     return NULL_TREE;
 
   if (TREE_CODE (sub) == ADDR_EXPR)
@@ -5616,7 +6689,7 @@ arith_code_with_undefined_signed_overflow (tree_code code)
    a modified form of STMT itself.  */
 
 gimple_seq
-rewrite_to_defined_overflow (gimple stmt)
+rewrite_to_defined_overflow (gimple *stmt)
 {
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
@@ -5630,43 +6703,47 @@ rewrite_to_defined_overflow (gimple stmt)
   gimple_seq stmts = NULL;
   for (unsigned i = 1; i < gimple_num_ops (stmt); ++i)
     {
-      gimple_seq stmts2 = NULL;
-      gimple_set_op (stmt, i,
-                    force_gimple_operand (fold_convert (type,
-                                                        gimple_op (stmt, i)),
-                                          &stmts2, true, NULL_TREE));
-      gimple_seq_add_seq (&stmts, stmts2);
+      tree op = gimple_op (stmt, i);
+      op = gimple_convert (&stmts, type, op);
+      gimple_set_op (stmt, i, op);
     }
   gimple_assign_set_lhs (stmt, make_ssa_name (type, 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 (lhs, NOP_EXPR, gimple_assign_lhs (stmt));
+  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 using VALUEIZE if not NULL.
-   OP0 is expected to be valueized already.  Returns the built
+   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 (*valueize)(tree))
+             enum tree_code code, tree type, tree op0)
 {
-  tree res = gimple_simplify (code, type, op0, seq, valueize);
+  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;
+      res = create_tmp_reg_or_ssa_name (type);
+      gimple *stmt;
       if (code == REALPART_EXPR
          || code == IMAGPART_EXPR
          || code == VIEW_CONVERT_EXPR)
@@ -5680,24 +6757,19 @@ gimple_build (gimple_seq *seq, location_t loc,
 }
 
 /* Build the expression OP0 CODE OP1 of type TYPE with location LOC,
-   simplifying it first if possible using VALUEIZE if not NULL.
-   OP0 and OP1 are expected to be valueized already.  Returns the built
+   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 (*valueize)(tree))
+             enum tree_code code, tree type, tree op0, tree op1)
 {
-  tree res = gimple_simplify (code, type, op0, op1, seq, valueize);
+  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);
+      res = create_tmp_reg_or_ssa_name (type);
+      gimple *stmt = gimple_build_assign (res, code, op0, op1);
       gimple_set_location (stmt, loc);
       gimple_seq_add_stmt_without_update (seq, stmt);
     }
@@ -5705,25 +6777,20 @@ gimple_build (gimple_seq *seq, location_t loc,
 }
 
 /* Build the expression (CODE OP0 OP1 OP2) of type TYPE with location LOC,
-   simplifying it first if possible using VALUEIZE if not NULL.
-   OP0, OP1 and OP2 are expected to be valueized already.  Returns the built
+   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 (*valueize)(tree))
+             enum tree_code code, tree type, tree op0, tree op1, tree op2)
 {
   tree res = gimple_simplify (code, type, op0, op1, op2,
-                             seq, valueize);
+                             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;
+      res = create_tmp_reg_or_ssa_name (type);
+      gimple *stmt;
       if (code == BIT_FIELD_REF)
        stmt = gimple_build_assign (res, code,
                                    build3 (code, type, op0, op1, op2));
@@ -5737,27 +6804,22 @@ gimple_build (gimple_seq *seq, location_t loc,
 
 /* 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 using VALUEIZE if not NULL.
-   ARG0 is expected to be valueized already.  Returns the built
+   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 (*valueize)(tree))
+             enum built_in_function fn, tree type, tree arg0)
 {
-  tree res = gimple_simplify (fn, type, arg0, seq, valueize);
+  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);
+      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);
+         res = create_tmp_reg_or_ssa_name (type);
          gimple_call_set_lhs (stmt, res);
        }
       gimple_set_location (stmt, loc);
@@ -5768,27 +6830,22 @@ gimple_build (gimple_seq *seq, location_t loc,
 
 /* 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 using VALUEIZE if not NULL.
-   ARG0 is expected to be valueized already.  Returns the built
+   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 (*valueize)(tree))
+             enum built_in_function fn, tree type, tree arg0, tree arg1)
 {
-  tree res = gimple_simplify (fn, type, arg0, arg1, seq, valueize);
+  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);
+      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);
+         res = create_tmp_reg_or_ssa_name (type);
          gimple_call_set_lhs (stmt, res);
        }
       gimple_set_location (stmt, loc);
@@ -5799,28 +6856,24 @@ gimple_build (gimple_seq *seq, location_t loc,
 
 /* 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 using VALUEIZE if not NULL.
-   ARG0 is expected to be valueized already.  Returns the built
+   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 (*valueize)(tree))
+             tree arg0, tree arg1, tree arg2)
 {
-  tree res = gimple_simplify (fn, type, arg0, arg1, arg2, seq, valueize);
+  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);
+      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);
+         res = create_tmp_reg_or_ssa_name (type);
          gimple_call_set_lhs (stmt, res);
        }
       gimple_set_location (stmt, loc);
@@ -5842,3 +6895,203 @@ gimple_convert (gimple_seq *seq, location_t loc, tree type, tree op)
     return op;
   return gimple_build (seq, loc, NOP_EXPR, type, op);
 }
+
+/* Build the conversion (ptrofftype) OP with a result of a type
+   compatible with ptrofftype 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_to_ptrofftype (gimple_seq *seq, location_t loc, tree op)
+{
+  if (ptrofftype_p (TREE_TYPE (op)))
+    return op;
+  return gimple_convert (seq, loc, sizetype, op);
+}
+
+/* Return true if the result of assignment STMT is known to be non-negative.
+   If the return value is based on the assumption that signed overflow is
+   undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
+   *STRICT_OVERFLOW_P.  DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_assign_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
+                                  int depth)
+{
+  enum tree_code code = gimple_assign_rhs_code (stmt);
+  switch (get_gimple_rhs_class (code))
+    {
+    case GIMPLE_UNARY_RHS:
+      return tree_unary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt),
+                                            gimple_expr_type (stmt),
+                                            gimple_assign_rhs1 (stmt),
+                                            strict_overflow_p, depth);
+    case GIMPLE_BINARY_RHS:
+      return tree_binary_nonnegative_warnv_p (gimple_assign_rhs_code (stmt),
+                                             gimple_expr_type (stmt),
+                                             gimple_assign_rhs1 (stmt),
+                                             gimple_assign_rhs2 (stmt),
+                                             strict_overflow_p, depth);
+    case GIMPLE_TERNARY_RHS:
+      return false;
+    case GIMPLE_SINGLE_RHS:
+      return tree_single_nonnegative_warnv_p (gimple_assign_rhs1 (stmt),
+                                             strict_overflow_p, depth);
+    case GIMPLE_INVALID_RHS:
+      break;
+    }
+  gcc_unreachable ();
+}
+
+/* Return true if return value of call STMT is known to be non-negative.
+   If the return value is based on the assumption that signed overflow is
+   undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
+   *STRICT_OVERFLOW_P.  DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_call_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
+                                int depth)
+{
+  tree arg0 = gimple_call_num_args (stmt) > 0 ?
+    gimple_call_arg (stmt, 0) : NULL_TREE;
+  tree arg1 = gimple_call_num_args (stmt) > 1 ?
+    gimple_call_arg (stmt, 1) : NULL_TREE;
+
+  return tree_call_nonnegative_warnv_p (gimple_expr_type (stmt),
+                                       gimple_call_combined_fn (stmt),
+                                       arg0,
+                                       arg1,
+                                       strict_overflow_p, depth);
+}
+
+/* Return true if return value of call STMT is known to be non-negative.
+   If the return value is based on the assumption that signed overflow is
+   undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
+   *STRICT_OVERFLOW_P.  DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_phi_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
+                               int depth)
+{
+  for (unsigned i = 0; i < gimple_phi_num_args (stmt); ++i)
+    {
+      tree arg = gimple_phi_arg_def (stmt, i);
+      if (!tree_single_nonnegative_warnv_p (arg, strict_overflow_p, depth + 1))
+       return false;
+    }
+  return true;
+}
+
+/* Return true if STMT is known to compute a non-negative value.
+   If the return value is based on the assumption that signed overflow is
+   undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
+   *STRICT_OVERFLOW_P.  DEPTH is the current nesting depth of the query.  */
+
+bool
+gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
+                                int depth)
+{
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_ASSIGN:
+      return gimple_assign_nonnegative_warnv_p (stmt, strict_overflow_p,
+                                               depth);
+    case GIMPLE_CALL:
+      return gimple_call_nonnegative_warnv_p (stmt, strict_overflow_p,
+                                             depth);
+    case GIMPLE_PHI:
+      return gimple_phi_nonnegative_warnv_p (stmt, strict_overflow_p,
+                                            depth);
+    default:
+      return false;
+    }
+}
+
+/* Return true if the floating-point value computed by assignment STMT
+   is known to have an integer value.  We also allow +Inf, -Inf and NaN
+   to be considered integer values. Return false for signaling NaN.
+
+   DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_assign_integer_valued_real_p (gimple *stmt, int depth)
+{
+  enum tree_code code = gimple_assign_rhs_code (stmt);
+  switch (get_gimple_rhs_class (code))
+    {
+    case GIMPLE_UNARY_RHS:
+      return integer_valued_real_unary_p (gimple_assign_rhs_code (stmt),
+                                         gimple_assign_rhs1 (stmt), depth);
+    case GIMPLE_BINARY_RHS:
+      return integer_valued_real_binary_p (gimple_assign_rhs_code (stmt),
+                                          gimple_assign_rhs1 (stmt),
+                                          gimple_assign_rhs2 (stmt), depth);
+    case GIMPLE_TERNARY_RHS:
+      return false;
+    case GIMPLE_SINGLE_RHS:
+      return integer_valued_real_single_p (gimple_assign_rhs1 (stmt), depth);
+    case GIMPLE_INVALID_RHS:
+      break;
+    }
+  gcc_unreachable ();
+}
+
+/* Return true if the floating-point value computed by call STMT is known
+   to have an integer value.  We also allow +Inf, -Inf and NaN to be
+   considered integer values. Return false for signaling NaN.
+
+   DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_call_integer_valued_real_p (gimple *stmt, int depth)
+{
+  tree arg0 = (gimple_call_num_args (stmt) > 0
+              ? gimple_call_arg (stmt, 0)
+              : NULL_TREE);
+  tree arg1 = (gimple_call_num_args (stmt) > 1
+              ? gimple_call_arg (stmt, 1)
+              : NULL_TREE);
+  return integer_valued_real_call_p (gimple_call_combined_fn (stmt),
+                                    arg0, arg1, depth);
+}
+
+/* Return true if the floating-point result of phi STMT is known to have
+   an integer value.  We also allow +Inf, -Inf and NaN to be considered
+   integer values. Return false for signaling NaN.
+
+   DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_phi_integer_valued_real_p (gimple *stmt, int depth)
+{
+  for (unsigned i = 0; i < gimple_phi_num_args (stmt); ++i)
+    {
+      tree arg = gimple_phi_arg_def (stmt, i);
+      if (!integer_valued_real_single_p (arg, depth + 1))
+       return false;
+    }
+  return true;
+}
+
+/* Return true if the floating-point value computed by STMT is known
+   to have an integer value.  We also allow +Inf, -Inf and NaN to be
+   considered integer values. Return false for signaling NaN.
+
+   DEPTH is the current nesting depth of the query.  */
+
+bool
+gimple_stmt_integer_valued_real_p (gimple *stmt, int depth)
+{
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_ASSIGN:
+      return gimple_assign_integer_valued_real_p (stmt, depth);
+    case GIMPLE_CALL:
+      return gimple_call_integer_valued_real_p (stmt, depth);
+    case GIMPLE_PHI:
+      return gimple_phi_integer_valued_real_p (stmt, depth);
+    default:
+      return false;
+    }
+}