re PR middle-end/80222 (may_alias folded away)
[gcc.git] / gcc / gimple-fold.c
index 6c53bac027b82fb7752f1d747fab46b45faceab6..a6a958cefa4c560df9b828d55100e2802c2a1fd1 100644 (file)
@@ -1,5 +1,5 @@
 /* Statement simplification on GIMPLE.
-   Copyright (C) 2010-2015 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.
@@ -22,27 +22,19 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
-#include "predict.h"
+#include "target.h"
+#include "rtl.h"
 #include "tree.h"
 #include "gimple.h"
-#include "rtl.h"
+#include "predict.h"
 #include "ssa.h"
-#include "alias.h"
+#include "cgraph.h"
+#include "gimple-pretty-print.h"
 #include "fold-const.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
-#include "calls.h"
-#include "emit-rtl.h"
-#include "varasm.h"
 #include "stmt.h"
 #include "expr.h"
 #include "stor-layout.h"
 #include "dumpfile.h"
-#include "dominance.h"
-#include "internal-fn.h"
 #include "gimple-fold.h"
 #include "gimplify.h"
 #include "gimple-iterator.h"
@@ -50,18 +42,20 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "tree-ssa.h"
 #include "tree-ssa-propagate.h"
-#include "target.h"
-#include "cgraph.h"
 #include "ipa-utils.h"
-#include "gimple-pretty-print.h"
 #include "tree-ssa-address.h"
 #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 "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.
@@ -97,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.  */
@@ -118,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)
@@ -163,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.  */
@@ -198,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)
        {
@@ -298,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);
 
@@ -352,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);
@@ -368,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),
@@ -391,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;
 
@@ -446,18 +447,18 @@ fold_gimple_assign (gimple_stmt_iterator *si)
 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)
@@ -482,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))
@@ -524,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;
 
@@ -548,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,
@@ -572,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)))
@@ -589,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)
@@ -608,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);
 }
 
@@ -635,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;
@@ -644,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
@@ -655,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;
     }
 
@@ -668,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;
@@ -679,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);
@@ -698,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
@@ -708,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;
@@ -720,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);
@@ -750,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);
@@ -790,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))
                {
@@ -1010,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));
@@ -1030,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;
 }
 
@@ -1058,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;
 
@@ -1118,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)
@@ -1129,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
     {
@@ -1143,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)
     {
@@ -1168,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)
@@ -1181,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;
     }
 
@@ -1228,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;
 
@@ -1258,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;
@@ -1269,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];
 }
 
 
@@ -1316,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;
 }
@@ -1365,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.
 
@@ -1391,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);
@@ -1422,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);
 
@@ -1471,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);
@@ -1495,7 +1752,7 @@ 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;
 }
@@ -1546,7 +1803,7 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 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);
@@ -1579,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;
        }
@@ -1591,11 +1848,254 @@ 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;
 }
 
+/* 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_string_compare (gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree callee = gimple_call_fndecl (stmt);
+  enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
+
+  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;
+
+  /* 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);
+    }
+
+  /* If the LEN parameter is zero, return zero.  */
+  if (length == 0)
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (str1, str2, 0))
+    {
+      replace_call_with_value (gsi, integer_zero_node);
+      return true;
+    }
+
+  const char *p1 = c_getstr (str1);
+  const char *p2 = c_getstr (str2);
+
+  /* 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
@@ -1608,7 +2108,7 @@ gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
                           tree arg0, tree arg1,
                           bool unlocked)
 {
-  gimple stmt = gsi_stmt (*gsi);
+  gimple *stmt = gsi_stmt (*gsi);
 
   /* If we're using an unlocked function, assume the other unlocked
      functions exist explicitly.  */
@@ -1644,7 +2144,7 @@ gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
            if (!fn_fputc)
              return false;
 
-           gimple repl = gimple_build_call (fn_fputc, 2,
+           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);
@@ -1662,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;
@@ -1684,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;
@@ -1700,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;
        }
@@ -1730,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;
                }
@@ -1768,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;
 }
@@ -1785,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;
@@ -1822,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;
                }
@@ -1836,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;
            }
@@ -1859,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;
 }
@@ -1875,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;
 
@@ -1886,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;
         }
@@ -1919,7 +2420,7 @@ 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;
 }
@@ -1980,7 +2481,7 @@ gimple_fold_builtin_stpcpy (gimple_stmt_iterator *gsi)
   gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT);
   gassign *ret = gimple_build_assign (gimple_call_lhs (stmt),
                                      POINTER_PLUS_EXPR, dest, tem);
-  gsi_replace (gsi, ret, true);
+  gsi_replace (gsi, ret, false);
   /* Finally fold the memcpy call.  */
   gimple_stmt_iterator gsi2 = *gsi;
   gsi_prev (&gsi2);
@@ -2176,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;
@@ -2214,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))
        {
@@ -2262,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))
        {
@@ -2349,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))
        {
@@ -2401,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))
        {
@@ -2699,7 +3200,7 @@ gimple_fold_builtin_printf (gimple_stmt_iterator *gsi, tree fmt,
 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;
@@ -2708,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.  */
@@ -2769,6 +3311,21 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
                                         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);
@@ -2848,24 +3405,205 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
                                           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:;
     }
 
-  /* Try the generic builtin folder.  */
-  bool ignore = (gimple_call_lhs (stmt) == NULL);
-  tree result = fold_call_stmt (stmt, ignore);
-  if (result)
-    {
-      if (ignore)
-       STRIP_NOPS (result);
+  /* Try the generic builtin folder.  */
+  bool ignore = (gimple_call_lhs (stmt) == NULL);
+  tree result = fold_call_stmt (stmt, ignore);
+  if (result)
+    {
+      if (ignore)
+       STRIP_NOPS (result);
+      else
+       result = fold_convert (gimple_call_return_type (stmt), result);
+      if (!update_call_from_tree (gsi, result))
+       gimplify_and_update_call_from_tree (gsi, result);
+      return true;
+    }
+
+  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
-       result = fold_convert (gimple_call_return_type (stmt), result);
-      if (!update_call_from_tree (gsi, result))
-       gimplify_and_update_call_from_tree (gsi, result);
-      return true;
+       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);
     }
-
-  return false;
+  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
@@ -2961,16 +3699,29 @@ 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);
@@ -2980,7 +3731,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
              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)
                    {
@@ -3062,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;
@@ -3191,7 +3946,7 @@ 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)
+has_use_on_stmt (tree name, gimple *stmt)
 {
   imm_use_iterator iter;
   use_operand_p use_p;
@@ -3213,7 +3968,7 @@ 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.
@@ -3229,7 +3984,19 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
       || (ops[2]
          && TREE_CODE (ops[2]) == SSA_NAME
          && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2])
-         && !has_use_on_stmt (ops[2], stmt)))
+         && !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))
@@ -3282,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))
            {
@@ -3298,7 +4064,7 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
        }
     }
   else if (rcode.is_fn_code ()
-          && gimple_call_builtin_p (stmt, rcode))
+          && gimple_call_combined_fn (stmt) == rcode)
     {
       unsigned i;
       for (i = 0; i < gimple_call_num_args (stmt); ++i)
@@ -3308,6 +4074,14 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
        }
       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)
@@ -3343,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);
 
@@ -3423,8 +4235,10 @@ 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.
@@ -3457,7 +4271,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
            {
              tree rhs1 = gimple_assign_rhs1 (stmt);
              tree rhs2 = gimple_assign_rhs2 (stmt);
-             if (tree_swap_operands_p (rhs1, rhs2, false))
+             if (tree_swap_operands_p (rhs1, rhs2))
                {
                  gimple_assign_set_rhs1 (stmt, rhs2);
                  gimple_assign_set_rhs2 (stmt, rhs1);
@@ -3523,7 +4337,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
        /* Canonicalize operand order.  */
        tree lhs = gimple_cond_lhs (stmt);
        tree rhs = gimple_cond_rhs (stmt);
-       if (tree_swap_operands_p (lhs, rhs, false))
+       if (tree_swap_operands_p (lhs, rhs))
          {
            gcond *gc = as_a <gcond *> (stmt);
            gimple_cond_set_lhs (gc, rhs);
@@ -3697,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:;
     }
 
@@ -3717,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;
 }
 
@@ -3769,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;
@@ -3830,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
@@ -3917,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,
@@ -3926,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
@@ -3939,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))
@@ -3962,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);
@@ -4010,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);
@@ -4297,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:
@@ -4342,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.  */
@@ -4402,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))
@@ -4425,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);
@@ -4473,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);
@@ -4761,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:
@@ -4806,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.  */
@@ -4867,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;
@@ -4876,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, valueize)
-      && rcode.is_tree_code ()
-      && (TREE_CODE_LENGTH ((tree_code) rcode) == 0
-         || ((tree_code) rcode) == ADDR_EXPR)
-      && is_gimple_val (ops[0]))
-    {
-      tree res = ops[0];
-      if (dump_file && dump_flags & TDF_DETAILS)
+  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);
@@ -5012,10 +5848,8 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
               further propagation.  */
            if (subcode == POINTER_PLUS_EXPR)
              {
-               /* Handle binary operators that can appear in GIMPLE form.  */
                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)
                  {
@@ -5027,6 +5861,30 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree),
                                      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:
@@ -5063,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;
              }
@@ -5136,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))
@@ -5161,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)))
@@ -5178,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
@@ -5199,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;
     }
 }
@@ -5225,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.  */
@@ -5240,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;
 
@@ -5263,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);
@@ -5274,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);
@@ -5347,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);
@@ -5369,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,
@@ -5402,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_USELESS_TYPE_CONVERSION (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
@@ -5416,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)
     {
@@ -5445,6 +6286,7 @@ 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;
@@ -5515,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.  */
@@ -5579,8 +6421,7 @@ 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))
     {
       /* Pass down that we lost track of the target.  */
       if (can_refer)
@@ -5698,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 (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. */
@@ -5844,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)
@@ -5978,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))
     {
@@ -5992,18 +6703,15 @@ 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;
@@ -6034,11 +6742,8 @@ gimple_build (gimple_seq *seq, location_t loc,
   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)
@@ -6063,11 +6768,8 @@ gimple_build (gimple_seq *seq, location_t loc,
   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);
     }
@@ -6087,11 +6789,8 @@ gimple_build (gimple_seq *seq, location_t loc,
                              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));
@@ -6117,13 +6816,10 @@ gimple_build (gimple_seq *seq, location_t loc,
   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);
@@ -6146,13 +6842,10 @@ gimple_build (gimple_seq *seq, location_t loc,
   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);
@@ -6177,13 +6870,10 @@ gimple_build (gimple_seq *seq, location_t loc,
   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);
@@ -6205,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;
+    }
+}