OSDN Git Service

* de.po, sv.po: Update.
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-forwprop.c
index 3494190..f68395a 100644 (file)
@@ -1,5 +1,6 @@
 /* Forward propagation of expressions for single use variables.
-   Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -25,13 +26,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm_p.h"
 #include "basic-block.h"
 #include "timevar.h"
-#include "diagnostic.h"
+#include "tree-pretty-print.h"
 #include "tree-flow.h"
 #include "tree-pass.h"
 #include "tree-dump.h"
 #include "langhooks.h"
 #include "flags.h"
 #include "gimple.h"
+#include "expr.h"
 
 /* This pass propagates the RHS of assignment statements into use
    sites of the LHS of the assignment.  It's basically a specialized
@@ -339,7 +341,11 @@ rhs_to_tree (tree type, gimple stmt)
 {
   location_t loc = gimple_location (stmt);
   enum tree_code code = gimple_assign_rhs_code (stmt);
-  if (get_gimple_rhs_class (code) == GIMPLE_BINARY_RHS)
+  if (get_gimple_rhs_class (code) == GIMPLE_TERNARY_RHS)
+    return fold_build3_loc (loc, code, type, gimple_assign_rhs1 (stmt),
+                           gimple_assign_rhs2 (stmt),
+                           gimple_assign_rhs3 (stmt));
+  else if (get_gimple_rhs_class (code) == GIMPLE_BINARY_RHS)
     return fold_build2_loc (loc, code, type, gimple_assign_rhs1 (stmt),
                        gimple_assign_rhs2 (stmt));
   else if (get_gimple_rhs_class (code) == GIMPLE_UNARY_RHS)
@@ -627,9 +633,14 @@ forward_propagate_addr_into_variable_array_index (tree offset,
 {
   tree index, tunit;
   gimple offset_def, use_stmt = gsi_stmt (*use_stmt_gsi);
-  tree tmp;
+  tree new_rhs, tmp;
 
-  tunit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (def_rhs)));
+  if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == ARRAY_REF)
+    tunit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (def_rhs)));
+  else if (TREE_CODE (TREE_TYPE (TREE_OPERAND (def_rhs, 0))) == ARRAY_TYPE)
+    tunit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (TREE_TYPE (def_rhs))));
+  else
+    return false;
   if (!host_integerp (tunit, 1))
     return false;
 
@@ -696,10 +707,28 @@ forward_propagate_addr_into_variable_array_index (tree offset,
   /* Replace the pointer addition with array indexing.  */
   index = force_gimple_operand_gsi (use_stmt_gsi, index, true, NULL_TREE,
                                    true, GSI_SAME_STMT);
-  gimple_assign_set_rhs_from_tree (use_stmt_gsi, unshare_expr (def_rhs));
+  if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == ARRAY_REF)
+    {
+      new_rhs = unshare_expr (def_rhs);
+      TREE_OPERAND (TREE_OPERAND (new_rhs, 0), 1) = index;
+    }
+  else
+    {
+      new_rhs = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (TREE_TYPE (def_rhs))),
+                       unshare_expr (TREE_OPERAND (def_rhs, 0)),
+                       index, integer_zero_node, NULL_TREE);
+      new_rhs = build_fold_addr_expr (new_rhs);
+      if (!useless_type_conversion_p (TREE_TYPE (gimple_assign_lhs (use_stmt)),
+                                     TREE_TYPE (new_rhs)))
+       {
+         new_rhs = force_gimple_operand_gsi (use_stmt_gsi, new_rhs, true,
+                                             NULL_TREE, true, GSI_SAME_STMT);
+         new_rhs = fold_convert (TREE_TYPE (gimple_assign_lhs (use_stmt)),
+                                 new_rhs);
+       }
+    }
+  gimple_assign_set_rhs_from_tree (use_stmt_gsi, new_rhs);
   use_stmt = gsi_stmt (*use_stmt_gsi);
-  TREE_OPERAND (TREE_OPERAND (gimple_assign_rhs1 (use_stmt), 0), 1)
-    = index;
 
   /* That should have created gimple, so there is no need to
      record information to undo the propagation.  */
@@ -724,11 +753,9 @@ forward_propagate_addr_expr_1 (tree name, tree def_rhs,
                               bool single_use_p)
 {
   tree lhs, rhs, rhs2, array_ref;
-  tree *rhsp, *lhsp;
   gimple use_stmt = gsi_stmt (*use_stmt_gsi);
   enum tree_code rhs_code;
   bool res = true;
-  bool addr_p = false;
 
   gcc_assert (TREE_CODE (def_rhs) == ADDR_EXPR);
 
@@ -766,31 +793,118 @@ forward_propagate_addr_expr_1 (tree name, tree def_rhs,
       return true;
     }
 
+  /* Propagate through constant pointer adjustments.  */
+  if (TREE_CODE (lhs) == SSA_NAME
+      && rhs_code == POINTER_PLUS_EXPR
+      && rhs == name
+      && TREE_CODE (gimple_assign_rhs2 (use_stmt)) == INTEGER_CST)
+    {
+      tree new_def_rhs;
+      /* As we come here with non-invariant addresses in def_rhs we need
+         to make sure we can build a valid constant offsetted address
+        for further propagation.  Simply rely on fold building that
+        and check after the fact.  */
+      new_def_rhs = fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (rhs)),
+                                def_rhs,
+                                fold_convert (ptr_type_node,
+                                              gimple_assign_rhs2 (use_stmt)));
+      if (TREE_CODE (new_def_rhs) == MEM_REF
+         && !is_gimple_mem_ref_addr (TREE_OPERAND (new_def_rhs, 0)))
+       return false;
+      new_def_rhs = build_fold_addr_expr_with_type (new_def_rhs,
+                                                   TREE_TYPE (rhs));
+
+      /* Recurse.  If we could propagate into all uses of lhs do not
+        bother to replace into the current use but just pretend we did.  */
+      if (TREE_CODE (new_def_rhs) == ADDR_EXPR
+         && forward_propagate_addr_expr (lhs, new_def_rhs))
+       return true;
+
+      if (useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (new_def_rhs)))
+       gimple_assign_set_rhs_with_ops (use_stmt_gsi, TREE_CODE (new_def_rhs),
+                                       new_def_rhs, NULL_TREE);
+      else if (is_gimple_min_invariant (new_def_rhs))
+       gimple_assign_set_rhs_with_ops (use_stmt_gsi, NOP_EXPR,
+                                       new_def_rhs, NULL_TREE);
+      else
+       return false;
+      gcc_assert (gsi_stmt (*use_stmt_gsi) == use_stmt);
+      update_stmt (use_stmt);
+      return true;
+    }
+
   /* Now strip away any outer COMPONENT_REF/ARRAY_REF nodes from the LHS.
      ADDR_EXPR will not appear on the LHS.  */
-  lhsp = gimple_assign_lhs_ptr (use_stmt);
-  while (handled_component_p (*lhsp))
-    lhsp = &TREE_OPERAND (*lhsp, 0);
-  lhs = *lhsp;
+  lhs = gimple_assign_lhs (use_stmt);
+  while (handled_component_p (lhs))
+    lhs = TREE_OPERAND (lhs, 0);
 
-  /* Now see if the LHS node is an INDIRECT_REF using NAME.  If so,
+  /* Now see if the LHS node is a MEM_REF using NAME.  If so,
      propagate the ADDR_EXPR into the use of NAME and fold the result.  */
-  if (TREE_CODE (lhs) == INDIRECT_REF
+  if (TREE_CODE (lhs) == MEM_REF
       && TREE_OPERAND (lhs, 0) == name)
     {
-      if (may_propagate_address_into_dereference (def_rhs, lhs)
-         && (lhsp != gimple_assign_lhs_ptr (use_stmt)
-             || useless_type_conversion_p
-                  (TREE_TYPE (TREE_OPERAND (def_rhs, 0)), TREE_TYPE (rhs))))
+      tree def_rhs_base;
+      HOST_WIDE_INT def_rhs_offset;
+      /* If the address is invariant we can always fold it.  */
+      if ((def_rhs_base = get_addr_base_and_unit_offset (TREE_OPERAND (def_rhs, 0),
+                                                        &def_rhs_offset)))
        {
-         *lhsp = unshare_expr (TREE_OPERAND (def_rhs, 0));
-         fold_stmt_inplace (use_stmt);
+         double_int off = mem_ref_offset (lhs);
+         tree new_ptr;
+         off = double_int_add (off,
+                               shwi_to_double_int (def_rhs_offset));
+         if (TREE_CODE (def_rhs_base) == MEM_REF)
+           {
+             off = double_int_add (off, mem_ref_offset (def_rhs_base));
+             new_ptr = TREE_OPERAND (def_rhs_base, 0);
+           }
+         else
+           new_ptr = build_fold_addr_expr (def_rhs_base);
+         TREE_OPERAND (lhs, 0) = new_ptr;
+         TREE_OPERAND (lhs, 1)
+           = double_int_to_tree (TREE_TYPE (TREE_OPERAND (lhs, 1)), off);
          tidy_after_forward_propagate_addr (use_stmt);
-
          /* Continue propagating into the RHS if this was not the only use.  */
          if (single_use_p)
            return true;
        }
+      /* If the LHS is a plain dereference and the value type is the same as
+         that of the pointed-to type of the address we can put the
+        dereferenced address on the LHS preserving the original alias-type.  */
+      else if (gimple_assign_lhs (use_stmt) == lhs
+              && useless_type_conversion_p
+                   (TREE_TYPE (TREE_OPERAND (def_rhs, 0)),
+                    TREE_TYPE (gimple_assign_rhs1 (use_stmt))))
+       {
+         tree *def_rhs_basep = &TREE_OPERAND (def_rhs, 0);
+         tree new_offset, new_base, saved;
+         while (handled_component_p (*def_rhs_basep))
+           def_rhs_basep = &TREE_OPERAND (*def_rhs_basep, 0);
+         saved = *def_rhs_basep;
+         if (TREE_CODE (*def_rhs_basep) == MEM_REF)
+           {
+             new_base = TREE_OPERAND (*def_rhs_basep, 0);
+             new_offset
+               = int_const_binop (PLUS_EXPR, TREE_OPERAND (lhs, 1),
+                                  TREE_OPERAND (*def_rhs_basep, 1), 0);
+           }
+         else
+           {
+             new_base = build_fold_addr_expr (*def_rhs_basep);
+             new_offset = TREE_OPERAND (lhs, 1);
+           }
+         *def_rhs_basep = build2 (MEM_REF, TREE_TYPE (*def_rhs_basep),
+                                  new_base, new_offset);
+         gimple_assign_set_lhs (use_stmt,
+                                unshare_expr (TREE_OPERAND (def_rhs, 0)));
+         *def_rhs_basep = saved;
+         tidy_after_forward_propagate_addr (use_stmt);
+         /* Continue propagating into the RHS if this was not the
+            only use.  */
+         if (single_use_p)
+           return true;
+       }
       else
        /* We can have a struct assignment dereferencing our name twice.
           Note that we didn't propagate into the lhs to not falsely
@@ -800,79 +914,76 @@ forward_propagate_addr_expr_1 (tree name, tree def_rhs,
 
   /* Strip away any outer COMPONENT_REF, ARRAY_REF or ADDR_EXPR
      nodes from the RHS.  */
-  rhsp = gimple_assign_rhs1_ptr (use_stmt);
-  if (TREE_CODE (*rhsp) == ADDR_EXPR)
-    {
-      rhsp = &TREE_OPERAND (*rhsp, 0);
-      addr_p = true;
-    }
-  while (handled_component_p (*rhsp))
-    rhsp = &TREE_OPERAND (*rhsp, 0);
-  rhs = *rhsp;
+  rhs = gimple_assign_rhs1 (use_stmt);
+  if (TREE_CODE (rhs) == ADDR_EXPR)
+    rhs = TREE_OPERAND (rhs, 0);
+  while (handled_component_p (rhs))
+    rhs = TREE_OPERAND (rhs, 0);
 
-  /* Now see if the RHS node is an INDIRECT_REF using NAME.  If so,
+  /* Now see if the RHS node is a MEM_REF using NAME.  If so,
      propagate the ADDR_EXPR into the use of NAME and fold the result.  */
-  if (TREE_CODE (rhs) == INDIRECT_REF
-      && TREE_OPERAND (rhs, 0) == name
-      && may_propagate_address_into_dereference (def_rhs, rhs))
+  if (TREE_CODE (rhs) == MEM_REF
+      && TREE_OPERAND (rhs, 0) == name)
     {
-      *rhsp = unshare_expr (TREE_OPERAND (def_rhs, 0));
-      fold_stmt_inplace (use_stmt);
-      tidy_after_forward_propagate_addr (use_stmt);
-      return res;
+      tree def_rhs_base;
+      HOST_WIDE_INT def_rhs_offset;
+      if ((def_rhs_base = get_addr_base_and_unit_offset (TREE_OPERAND (def_rhs, 0),
+                                                        &def_rhs_offset)))
+       {
+         double_int off = mem_ref_offset (rhs);
+         tree new_ptr;
+         off = double_int_add (off,
+                               shwi_to_double_int (def_rhs_offset));
+         if (TREE_CODE (def_rhs_base) == MEM_REF)
+           {
+             off = double_int_add (off, mem_ref_offset (def_rhs_base));
+             new_ptr = TREE_OPERAND (def_rhs_base, 0);
+           }
+         else
+           new_ptr = build_fold_addr_expr (def_rhs_base);
+         TREE_OPERAND (rhs, 0) = new_ptr;
+         TREE_OPERAND (rhs, 1)
+           = double_int_to_tree (TREE_TYPE (TREE_OPERAND (rhs, 1)), off);
+         fold_stmt_inplace (use_stmt);
+         tidy_after_forward_propagate_addr (use_stmt);
+         return res;
+       }
+      /* If the LHS is a plain dereference and the value type is the same as
+         that of the pointed-to type of the address we can put the
+        dereferenced address on the LHS preserving the original alias-type.  */
+      else if (gimple_assign_rhs1 (use_stmt) == rhs
+              && useless_type_conversion_p
+                   (TREE_TYPE (gimple_assign_lhs (use_stmt)),
+                    TREE_TYPE (TREE_OPERAND (def_rhs, 0))))
+       {
+         tree *def_rhs_basep = &TREE_OPERAND (def_rhs, 0);
+         tree new_offset, new_base, saved;
+         while (handled_component_p (*def_rhs_basep))
+           def_rhs_basep = &TREE_OPERAND (*def_rhs_basep, 0);
+         saved = *def_rhs_basep;
+         if (TREE_CODE (*def_rhs_basep) == MEM_REF)
+           {
+             new_base = TREE_OPERAND (*def_rhs_basep, 0);
+             new_offset
+               = int_const_binop (PLUS_EXPR, TREE_OPERAND (rhs, 1),
+                                  TREE_OPERAND (*def_rhs_basep, 1), 0);
+           }
+         else
+           {
+             new_base = build_fold_addr_expr (*def_rhs_basep);
+             new_offset = TREE_OPERAND (rhs, 1);
+           }
+         *def_rhs_basep = build2 (MEM_REF, TREE_TYPE (*def_rhs_basep),
+                                  new_base, new_offset);
+         gimple_assign_set_rhs1 (use_stmt,
+                                 unshare_expr (TREE_OPERAND (def_rhs, 0)));
+         *def_rhs_basep = saved;
+         fold_stmt_inplace (use_stmt);
+         tidy_after_forward_propagate_addr (use_stmt);
+         return res;
+       }
     }
 
-  /* Now see if the RHS node is an INDIRECT_REF using NAME.  If so,
-     propagate the ADDR_EXPR into the use of NAME and try to
-     create a VCE and fold the result.  */
-  if (TREE_CODE (rhs) == INDIRECT_REF
-      && TREE_OPERAND (rhs, 0) == name
-      && TYPE_SIZE (TREE_TYPE (rhs))
-      && TYPE_SIZE (TREE_TYPE (TREE_OPERAND (def_rhs, 0)))
-      /* Function decls should not be used for VCE either as it could be a
-         function descriptor that we want and not the actual function code.  */
-      && TREE_CODE (TREE_OPERAND (def_rhs, 0)) != FUNCTION_DECL
-      /* We should not convert volatile loads to non volatile loads. */
-      && !TYPE_VOLATILE (TREE_TYPE (rhs))
-      && !TYPE_VOLATILE (TREE_TYPE (TREE_OPERAND (def_rhs, 0)))
-      && operand_equal_p (TYPE_SIZE (TREE_TYPE (rhs)),
-                         TYPE_SIZE (TREE_TYPE (TREE_OPERAND (def_rhs, 0))), 0)
-      /* Make sure we only do TBAA compatible replacements.  */
-      && get_alias_set (TREE_OPERAND (def_rhs, 0)) == get_alias_set (rhs))
-   {
-     tree def_rhs_base, new_rhs = unshare_expr (TREE_OPERAND (def_rhs, 0));
-     new_rhs = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (rhs), new_rhs);
-     if (TREE_CODE (new_rhs) != VIEW_CONVERT_EXPR)
-       {
-        /* If we have folded the VIEW_CONVERT_EXPR then the result is only
-           valid if we can replace the whole rhs of the use statement.  */
-        if (rhs != gimple_assign_rhs1 (use_stmt))
-          return false;
-        new_rhs = force_gimple_operand_gsi (use_stmt_gsi, new_rhs, true, NULL,
-                                            true, GSI_NEW_STMT);
-        gimple_assign_set_rhs1 (use_stmt, new_rhs);
-        tidy_after_forward_propagate_addr (use_stmt);
-        return res;
-       }
-     /* If the defining rhs comes from an indirect reference, then do not
-        convert into a VIEW_CONVERT_EXPR.  Likewise if we'll end up taking
-       the address of a V_C_E of a constant.  */
-     def_rhs_base = TREE_OPERAND (def_rhs, 0);
-     while (handled_component_p (def_rhs_base))
-       def_rhs_base = TREE_OPERAND (def_rhs_base, 0);
-     if (!INDIRECT_REF_P (def_rhs_base)
-        && (!addr_p
-            || !is_gimple_min_invariant (def_rhs)))
-       {
-        /* We may have arbitrary VIEW_CONVERT_EXPRs in a nested component
-           reference.  Place it there and fold the thing.  */
-        *rhsp = new_rhs;
-        fold_stmt_inplace (use_stmt);
-        tidy_after_forward_propagate_addr (use_stmt);
-        return res;
-       }
-   }
-
   /* If the use of the ADDR_EXPR is not a POINTER_PLUS_EXPR, there
      is nothing to do. */
   if (gimple_assign_rhs_code (use_stmt) != POINTER_PLUS_EXPR
@@ -884,9 +995,10 @@ forward_propagate_addr_expr_1 (tree name, tree def_rhs,
      element zero in an array.  If that is not the case then there
      is nothing to do.  */
   array_ref = TREE_OPERAND (def_rhs, 0);
-  if (TREE_CODE (array_ref) != ARRAY_REF
-      || TREE_CODE (TREE_TYPE (TREE_OPERAND (array_ref, 0))) != ARRAY_TYPE
-      || TREE_CODE (TREE_OPERAND (array_ref, 1)) != INTEGER_CST)
+  if ((TREE_CODE (array_ref) != ARRAY_REF
+       || TREE_CODE (TREE_TYPE (TREE_OPERAND (array_ref, 0))) != ARRAY_TYPE
+       || TREE_CODE (TREE_OPERAND (array_ref, 1)) != INTEGER_CST)
+      && TREE_CODE (TREE_TYPE (array_ref)) != ARRAY_TYPE)
     return false;
 
   rhs2 = gimple_assign_rhs2 (use_stmt);
@@ -922,7 +1034,8 @@ forward_propagate_addr_expr_1 (tree name, tree def_rhs,
      array elements, then the result is converted into the proper
      type for the arithmetic.  */
   if (TREE_CODE (rhs2) == SSA_NAME
-      && integer_zerop (TREE_OPERAND (array_ref, 1))
+      && (TREE_CODE (array_ref) != ARRAY_REF
+         || integer_zerop (TREE_OPERAND (array_ref, 1)))
       && useless_type_conversion_p (TREE_TYPE (name), TREE_TYPE (def_rhs))
       /* Avoid problems with IVopts creating PLUS_EXPRs with a
         different type than their operands.  */
@@ -1177,10 +1290,8 @@ simplify_gimple_switch (gimple stmt)
 
              def = gimple_assign_rhs1 (def_stmt);
 
-#ifdef ENABLE_CHECKING
              /* ??? Why was Jeff testing this?  We are gimple...  */
-             gcc_assert (is_gimple_val (def));
-#endif
+             gcc_checking_assert (is_gimple_val (def));
 
              to = TREE_TYPE (cond);
              ti = TREE_TYPE (def);
@@ -1209,6 +1320,302 @@ simplify_gimple_switch (gimple stmt)
     }
 }
 
+/* For pointers p2 and p1 return p2 - p1 if the
+   difference is known and constant, otherwise return NULL.  */
+
+static tree
+constant_pointer_difference (tree p1, tree p2)
+{
+  int i, j;
+#define CPD_ITERATIONS 5
+  tree exps[2][CPD_ITERATIONS];
+  tree offs[2][CPD_ITERATIONS];
+  int cnt[2];
+
+  for (i = 0; i < 2; i++)
+    {
+      tree p = i ? p1 : p2;
+      tree off = size_zero_node;
+      gimple stmt;
+      enum tree_code code;
+
+      /* For each of p1 and p2 we need to iterate at least
+        twice, to handle ADDR_EXPR directly in p1/p2,
+        SSA_NAME with ADDR_EXPR or POINTER_PLUS_EXPR etc.
+        on definition's stmt RHS.  Iterate a few extra times.  */
+      j = 0;
+      do
+       {
+         if (!POINTER_TYPE_P (TREE_TYPE (p)))
+           break;
+         if (TREE_CODE (p) == ADDR_EXPR)
+           {
+             tree q = TREE_OPERAND (p, 0);
+             HOST_WIDE_INT offset;
+             tree base = get_addr_base_and_unit_offset (q, &offset);
+             if (base)
+               {
+                 q = base;
+                 if (offset)
+                   off = size_binop (PLUS_EXPR, off, size_int (offset));
+               }
+             if (TREE_CODE (q) == MEM_REF
+                 && TREE_CODE (TREE_OPERAND (q, 0)) == SSA_NAME)
+               {
+                 p = TREE_OPERAND (q, 0);
+                 off = size_binop (PLUS_EXPR, off,
+                                   double_int_to_tree (sizetype,
+                                                       mem_ref_offset (q)));
+               }
+             else
+               {
+                 exps[i][j] = q;
+                 offs[i][j++] = off;
+                 break;
+               }
+           }
+         if (TREE_CODE (p) != SSA_NAME)
+           break;
+         exps[i][j] = p;
+         offs[i][j++] = off;
+         if (j == CPD_ITERATIONS)
+           break;
+         stmt = SSA_NAME_DEF_STMT (p);
+         if (!is_gimple_assign (stmt) || gimple_assign_lhs (stmt) != p)
+           break;
+         code = gimple_assign_rhs_code (stmt);
+         if (code == POINTER_PLUS_EXPR)
+           {
+             if (TREE_CODE (gimple_assign_rhs2 (stmt)) != INTEGER_CST)
+               break;
+             off = size_binop (PLUS_EXPR, off, gimple_assign_rhs2 (stmt));
+             p = gimple_assign_rhs1 (stmt);
+           }
+         else if (code == ADDR_EXPR || code == NOP_EXPR)
+           p = gimple_assign_rhs1 (stmt);
+         else
+           break;
+       }
+      while (1);
+      cnt[i] = j;
+    }
+
+  for (i = 0; i < cnt[0]; i++)
+    for (j = 0; j < cnt[1]; j++)
+      if (exps[0][i] == exps[1][j])
+       return size_binop (MINUS_EXPR, offs[0][i], offs[1][j]);
+
+  return NULL_TREE;
+}
+
+/* *GSI_P is a GIMPLE_CALL to a builtin function.
+   Optimize
+   memcpy (p, "abcd", 4);
+   memset (p + 4, ' ', 3);
+   into
+   memcpy (p, "abcd   ", 7);
+   call if the latter can be stored by pieces during expansion.  */
+
+static bool
+simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2)
+{
+  gimple stmt1, stmt2 = gsi_stmt (*gsi_p);
+  tree vuse = gimple_vuse (stmt2);
+  if (vuse == NULL)
+    return false;
+  stmt1 = SSA_NAME_DEF_STMT (vuse);
+
+  switch (DECL_FUNCTION_CODE (callee2))
+    {
+    case BUILT_IN_MEMSET:
+      if (gimple_call_num_args (stmt2) != 3
+         || gimple_call_lhs (stmt2)
+         || CHAR_BIT != 8
+         || BITS_PER_UNIT != 8)
+       break;
+      else
+       {
+         tree callee1;
+         tree ptr1, src1, str1, off1, len1, lhs1;
+         tree ptr2 = gimple_call_arg (stmt2, 0);
+         tree val2 = gimple_call_arg (stmt2, 1);
+         tree len2 = gimple_call_arg (stmt2, 2);
+         tree diff, vdef, new_str_cst;
+         gimple use_stmt;
+         unsigned int ptr1_align;
+         unsigned HOST_WIDE_INT src_len;
+         char *src_buf;
+         use_operand_p use_p;
+
+         if (!host_integerp (val2, 0)
+             || !host_integerp (len2, 1))
+           break;
+         if (is_gimple_call (stmt1))
+           {
+             /* If first stmt is a call, it needs to be memcpy
+                or mempcpy, with string literal as second argument and
+                constant length.  */
+             callee1 = gimple_call_fndecl (stmt1);
+             if (callee1 == NULL_TREE
+                 || DECL_BUILT_IN_CLASS (callee1) != BUILT_IN_NORMAL
+                 || gimple_call_num_args (stmt1) != 3)
+               break;
+             if (DECL_FUNCTION_CODE (callee1) != BUILT_IN_MEMCPY
+                 && DECL_FUNCTION_CODE (callee1) != BUILT_IN_MEMPCPY)
+               break;
+             ptr1 = gimple_call_arg (stmt1, 0);
+             src1 = gimple_call_arg (stmt1, 1);
+             len1 = gimple_call_arg (stmt1, 2);
+             lhs1 = gimple_call_lhs (stmt1);
+             if (!host_integerp (len1, 1))
+               break;
+             str1 = string_constant (src1, &off1);
+             if (str1 == NULL_TREE)
+               break;
+             if (!host_integerp (off1, 1)
+                 || compare_tree_int (off1, TREE_STRING_LENGTH (str1) - 1) > 0
+                 || compare_tree_int (len1, TREE_STRING_LENGTH (str1)
+                                            - tree_low_cst (off1, 1)) > 0
+                 || TREE_CODE (TREE_TYPE (str1)) != ARRAY_TYPE
+                 || TYPE_MODE (TREE_TYPE (TREE_TYPE (str1)))
+                    != TYPE_MODE (char_type_node))
+               break;
+           }
+         else if (gimple_assign_single_p (stmt1))
+           {
+             /* Otherwise look for length 1 memcpy optimized into
+                assignment.  */
+             ptr1 = gimple_assign_lhs (stmt1);
+             src1 = gimple_assign_rhs1 (stmt1);
+             if (TREE_CODE (ptr1) != MEM_REF
+                 || TYPE_MODE (TREE_TYPE (ptr1)) != TYPE_MODE (char_type_node)
+                 || !host_integerp (src1, 0))
+               break;
+             ptr1 = build_fold_addr_expr (ptr1);
+             callee1 = NULL_TREE;
+             len1 = size_one_node;
+             lhs1 = NULL_TREE;
+             off1 = size_zero_node;
+             str1 = NULL_TREE;
+           }
+         else
+           break;
+
+         diff = constant_pointer_difference (ptr1, ptr2);
+         if (diff == NULL && lhs1 != NULL)
+           {
+             diff = constant_pointer_difference (lhs1, ptr2);
+             if (DECL_FUNCTION_CODE (callee1) == BUILT_IN_MEMPCPY
+                 && diff != NULL)
+               diff = size_binop (PLUS_EXPR, diff,
+                                  fold_convert (sizetype, len1));
+           }
+         /* If the difference between the second and first destination pointer
+            is not constant, or is bigger than memcpy length, bail out.  */
+         if (diff == NULL
+             || !host_integerp (diff, 1)
+             || tree_int_cst_lt (len1, diff))
+           break;
+
+         /* Use maximum of difference plus memset length and memcpy length
+            as the new memcpy length, if it is too big, bail out.  */
+         src_len = tree_low_cst (diff, 1);
+         src_len += tree_low_cst (len2, 1);
+         if (src_len < (unsigned HOST_WIDE_INT) tree_low_cst (len1, 1))
+           src_len = tree_low_cst (len1, 1);
+         if (src_len > 1024)
+           break;
+
+         /* If mempcpy value is used elsewhere, bail out, as mempcpy
+            with bigger length will return different result.  */
+         if (lhs1 != NULL_TREE
+             && DECL_FUNCTION_CODE (callee1) == BUILT_IN_MEMPCPY
+             && (TREE_CODE (lhs1) != SSA_NAME
+                 || !single_imm_use (lhs1, &use_p, &use_stmt)
+                 || use_stmt != stmt2))
+           break;
+
+         /* If anything reads memory in between memcpy and memset
+            call, the modified memcpy call might change it.  */
+         vdef = gimple_vdef (stmt1);
+         if (vdef != NULL
+             && (!single_imm_use (vdef, &use_p, &use_stmt)
+                 || use_stmt != stmt2))
+           break;
+
+         ptr1_align = get_pointer_alignment (ptr1, BIGGEST_ALIGNMENT);
+         /* Construct the new source string literal.  */
+         src_buf = XALLOCAVEC (char, src_len + 1);
+         if (callee1)
+           memcpy (src_buf,
+                   TREE_STRING_POINTER (str1) + tree_low_cst (off1, 1),
+                   tree_low_cst (len1, 1));
+         else
+           src_buf[0] = tree_low_cst (src1, 0);
+         memset (src_buf + tree_low_cst (diff, 1),
+                 tree_low_cst (val2, 1), tree_low_cst (len2, 1));
+         src_buf[src_len] = '\0';
+         /* Neither builtin_strncpy_read_str nor builtin_memcpy_read_str
+            handle embedded '\0's.  */
+         if (strlen (src_buf) != src_len)
+           break;
+         rtl_profile_for_bb (gimple_bb (stmt2));
+         /* If the new memcpy wouldn't be emitted by storing the literal
+            by pieces, this optimization might enlarge .rodata too much,
+            as commonly used string literals couldn't be shared any
+            longer.  */
+         if (!can_store_by_pieces (src_len,
+                                   builtin_strncpy_read_str,
+                                   src_buf, ptr1_align, false))
+           break;
+
+         new_str_cst = build_string_literal (src_len, src_buf);
+         if (callee1)
+           {
+             /* If STMT1 is a mem{,p}cpy call, adjust it and remove
+                memset call.  */
+             if (lhs1 && DECL_FUNCTION_CODE (callee1) == BUILT_IN_MEMPCPY)
+               gimple_call_set_lhs (stmt1, NULL_TREE);
+             gimple_call_set_arg (stmt1, 1, new_str_cst);
+             gimple_call_set_arg (stmt1, 2,
+                                  build_int_cst (TREE_TYPE (len1), src_len));
+             update_stmt (stmt1);
+             unlink_stmt_vdef (stmt2);
+             gsi_remove (gsi_p, true);
+             release_defs (stmt2);
+             if (lhs1 && DECL_FUNCTION_CODE (callee1) == BUILT_IN_MEMPCPY)
+               release_ssa_name (lhs1);
+             return true;
+           }
+         else
+           {
+             /* Otherwise, if STMT1 is length 1 memcpy optimized into
+                assignment, remove STMT1 and change memset call into
+                memcpy call.  */
+             gimple_stmt_iterator gsi = gsi_for_stmt (stmt1);
+
+             if (!is_gimple_val (ptr1))
+               ptr1 = force_gimple_operand_gsi (gsi_p, ptr1, true, NULL_TREE,
+                                                true, GSI_SAME_STMT);
+             gimple_call_set_fndecl (stmt2, built_in_decls [BUILT_IN_MEMCPY]);
+             gimple_call_set_arg (stmt2, 0, ptr1);
+             gimple_call_set_arg (stmt2, 1, new_str_cst);
+             gimple_call_set_arg (stmt2, 2,
+                                  build_int_cst (TREE_TYPE (len2), src_len));
+             unlink_stmt_vdef (stmt1);
+             gsi_remove (&gsi, true);
+             release_defs (stmt1);
+             update_stmt (stmt2);
+             return false;
+           }
+       }
+      break;
+    default:
+      break;
+    }
+  return false;
+}
+
 /* Run bitwise and assignments throug the folder.  If the first argument is an
    ssa name that is itself a result of a typecast of an ADDR_EXPR to an
    integer, feed the ADDR_EXPR to the folder rather than the ssa name.
@@ -1249,6 +1656,287 @@ simplify_bitwise_and (gimple_stmt_iterator *gsi, gimple stmt)
   return;
 }
 
+
+/* Perform re-associations of the plus or minus statement STMT that are
+   always permitted.  */
+
+static void
+associate_plusminus (gimple stmt)
+{
+  tree rhs1 = gimple_assign_rhs1 (stmt);
+  tree rhs2 = gimple_assign_rhs2 (stmt);
+  enum tree_code code = gimple_assign_rhs_code (stmt);
+  gimple_stmt_iterator gsi;
+  bool changed;
+
+  /* We can't reassociate at all for saturating types.  */
+  if (TYPE_SATURATING (TREE_TYPE (rhs1)))
+    return;
+
+  /* First contract negates.  */
+  do
+    {
+      changed = false;
+
+      /* A +- (-B) -> A -+ B.  */
+      if (TREE_CODE (rhs2) == SSA_NAME)
+       {
+         gimple def_stmt = SSA_NAME_DEF_STMT (rhs2);
+         if (is_gimple_assign (def_stmt)
+             && gimple_assign_rhs_code (def_stmt) == NEGATE_EXPR)
+           {
+             code = (code == MINUS_EXPR) ? PLUS_EXPR : MINUS_EXPR;
+             gimple_assign_set_rhs_code (stmt, code);
+             rhs2 = gimple_assign_rhs1 (def_stmt);
+             gimple_assign_set_rhs2 (stmt, rhs2);
+             gimple_set_modified (stmt, true);
+             changed = true;
+           }
+       }
+
+      /* (-A) + B -> B - A.  */
+      if (TREE_CODE (rhs1) == SSA_NAME
+         && code == PLUS_EXPR)
+       {
+         gimple def_stmt = SSA_NAME_DEF_STMT (rhs1);
+         if (is_gimple_assign (def_stmt)
+             && gimple_assign_rhs_code (def_stmt) == NEGATE_EXPR)
+           {
+             code = MINUS_EXPR;
+             gimple_assign_set_rhs_code (stmt, code);
+             rhs1 = rhs2;
+             gimple_assign_set_rhs1 (stmt, rhs1);
+             rhs2 = gimple_assign_rhs1 (def_stmt);
+             gimple_assign_set_rhs2 (stmt, rhs2);
+             gimple_set_modified (stmt, true);
+             changed = true;
+           }
+       }
+    }
+  while (changed);
+
+  /* We can't reassociate floating-point or fixed-point plus or minus
+     because of saturation to +-Inf.  */
+  if (FLOAT_TYPE_P (TREE_TYPE (rhs1))
+      || FIXED_POINT_TYPE_P (TREE_TYPE (rhs1)))
+    goto out;
+
+  /* Second match patterns that allow contracting a plus-minus pair
+     irrespective of overflow issues.
+
+       (A +- B) - A       ->  +- B
+       (A +- B) -+ B      ->  A
+       (CST +- A) +- CST  ->  CST +- A
+       (A + CST) +- CST   ->  A + CST
+       ~A + A             ->  -1
+       ~A + 1             ->  -A 
+       A - (A +- B)       ->  -+ B
+       A +- (B +- A)      ->  +- B
+       CST +- (CST +- A)  ->  CST +- A
+       CST +- (A +- CST)  ->  CST +- A
+       A + ~A             ->  -1
+
+     via commutating the addition and contracting operations to zero
+     by reassociation.  */
+
+  gsi = gsi_for_stmt (stmt);
+  if (TREE_CODE (rhs1) == SSA_NAME)
+    {
+      gimple def_stmt = SSA_NAME_DEF_STMT (rhs1);
+      if (is_gimple_assign (def_stmt))
+       {
+         enum tree_code def_code = gimple_assign_rhs_code (def_stmt);
+         if (def_code == PLUS_EXPR
+             || def_code == MINUS_EXPR)
+           {
+             tree def_rhs1 = gimple_assign_rhs1 (def_stmt);
+             tree def_rhs2 = gimple_assign_rhs2 (def_stmt);
+             if (operand_equal_p (def_rhs1, rhs2, 0)
+                 && code == MINUS_EXPR)
+               {
+                 /* (A +- B) - A -> +- B.  */
+                 code = ((def_code == PLUS_EXPR)
+                         ? TREE_CODE (def_rhs2) : NEGATE_EXPR);
+                 rhs1 = def_rhs2;
+                 rhs2 = NULL_TREE;
+                 gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
+                 gcc_assert (gsi_stmt (gsi) == stmt);
+                 gimple_set_modified (stmt, true);
+               }
+             else if (operand_equal_p (def_rhs2, rhs2, 0)
+                      && code != def_code)
+               {
+                 /* (A +- B) -+ B -> A.  */
+                 code = TREE_CODE (def_rhs1);
+                 rhs1 = def_rhs1;
+                 rhs2 = NULL_TREE;
+                 gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
+                 gcc_assert (gsi_stmt (gsi) == stmt);
+                 gimple_set_modified (stmt, true);
+               }
+             else if (TREE_CODE (rhs2) == INTEGER_CST
+                      && TREE_CODE (def_rhs1) == INTEGER_CST)
+               {
+                 /* (CST +- A) +- CST -> CST +- A.  */
+                 tree cst = fold_binary (code, TREE_TYPE (rhs1),
+                                         def_rhs1, rhs2);
+                 if (cst && !TREE_OVERFLOW (cst))
+                   {
+                     code = def_code;
+                     gimple_assign_set_rhs_code (stmt, code);
+                     rhs1 = cst;
+                     gimple_assign_set_rhs1 (stmt, rhs1);
+                     rhs2 = def_rhs2;
+                     gimple_assign_set_rhs2 (stmt, rhs2);
+                     gimple_set_modified (stmt, true);
+                   }
+               }
+             else if (TREE_CODE (rhs2) == INTEGER_CST
+                      && TREE_CODE (def_rhs2) == INTEGER_CST
+                      && def_code == PLUS_EXPR)
+               {
+                 /* (A + CST) +- CST -> A + CST.  */
+                 tree cst = fold_binary (code, TREE_TYPE (rhs1),
+                                         def_rhs2, rhs2);
+                 if (cst && !TREE_OVERFLOW (cst))
+                   {
+                     code = PLUS_EXPR;
+                     gimple_assign_set_rhs_code (stmt, code);
+                     rhs1 = def_rhs1;
+                     gimple_assign_set_rhs1 (stmt, rhs1);
+                     rhs2 = cst;
+                     gimple_assign_set_rhs2 (stmt, rhs2);
+                     gimple_set_modified (stmt, true);
+                   }
+               }
+           }
+         else if (def_code == BIT_NOT_EXPR
+                  && INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
+           {
+             tree def_rhs1 = gimple_assign_rhs1 (def_stmt);
+             if (code == PLUS_EXPR
+                 && operand_equal_p (def_rhs1, rhs2, 0))
+               {
+                 /* ~A + A -> -1.  */
+                 code = INTEGER_CST;
+                 rhs1 = build_int_cst (TREE_TYPE (rhs2), -1);
+                 rhs2 = NULL_TREE;
+                 gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
+                 gcc_assert (gsi_stmt (gsi) == stmt);
+                 gimple_set_modified (stmt, true);
+               }
+             else if (code == PLUS_EXPR
+                      && integer_onep (rhs1))
+               {
+                 /* ~A + 1 -> -A.  */
+                 code = NEGATE_EXPR;
+                 rhs1 = def_rhs1;
+                 rhs2 = NULL_TREE;
+                 gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
+                 gcc_assert (gsi_stmt (gsi) == stmt);
+                 gimple_set_modified (stmt, true);
+               }
+           }
+       }
+    }
+
+  if (rhs2 && TREE_CODE (rhs2) == SSA_NAME)
+    {
+      gimple def_stmt = SSA_NAME_DEF_STMT (rhs2);
+      if (is_gimple_assign (def_stmt))
+       {
+         enum tree_code def_code = gimple_assign_rhs_code (def_stmt);
+         if (def_code == PLUS_EXPR
+             || def_code == MINUS_EXPR)
+           {
+             tree def_rhs1 = gimple_assign_rhs1 (def_stmt);
+             tree def_rhs2 = gimple_assign_rhs2 (def_stmt);
+             if (operand_equal_p (def_rhs1, rhs1, 0)
+                 && code == MINUS_EXPR)
+               {
+                 /* A - (A +- B) -> -+ B.  */
+                 code = ((def_code == PLUS_EXPR)
+                         ? NEGATE_EXPR : TREE_CODE (def_rhs2));
+                 rhs1 = def_rhs2;
+                 rhs2 = NULL_TREE;
+                 gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
+                 gcc_assert (gsi_stmt (gsi) == stmt);
+                 gimple_set_modified (stmt, true);
+               }
+             else if (operand_equal_p (def_rhs2, rhs1, 0)
+                      && code != def_code)
+               {
+                 /* A +- (B +- A) -> +- B.  */
+                 code = ((code == PLUS_EXPR)
+                         ? TREE_CODE (def_rhs1) : NEGATE_EXPR);
+                 rhs1 = def_rhs1;
+                 rhs2 = NULL_TREE;
+                 gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
+                 gcc_assert (gsi_stmt (gsi) == stmt);
+                 gimple_set_modified (stmt, true);
+               }
+             else if (TREE_CODE (rhs1) == INTEGER_CST
+                      && TREE_CODE (def_rhs1) == INTEGER_CST)
+               {
+                 /* CST +- (CST +- A) -> CST +- A.  */
+                 tree cst = fold_binary (code, TREE_TYPE (rhs2),
+                                         rhs1, def_rhs1);
+                 if (cst && !TREE_OVERFLOW (cst))
+                   {
+                     code = (code == def_code ? PLUS_EXPR : MINUS_EXPR);
+                     gimple_assign_set_rhs_code (stmt, code);
+                     rhs1 = cst;
+                     gimple_assign_set_rhs1 (stmt, rhs1);
+                     rhs2 = def_rhs2;
+                     gimple_assign_set_rhs2 (stmt, rhs2);
+                     gimple_set_modified (stmt, true);
+                   }
+               }
+             else if (TREE_CODE (rhs1) == INTEGER_CST
+                      && TREE_CODE (def_rhs2) == INTEGER_CST)
+               {
+                 /* CST +- (A +- CST) -> CST +- A.  */
+                 tree cst = fold_binary (def_code == code
+                                         ? PLUS_EXPR : MINUS_EXPR,
+                                         TREE_TYPE (rhs2),
+                                         rhs1, def_rhs2);
+                 if (cst && !TREE_OVERFLOW (cst))
+                   {
+                     rhs1 = cst;
+                     gimple_assign_set_rhs1 (stmt, rhs1);
+                     rhs2 = def_rhs1;
+                     gimple_assign_set_rhs2 (stmt, rhs2);
+                     gimple_set_modified (stmt, true);
+                   }
+               }
+           }
+         else if (def_code == BIT_NOT_EXPR
+                  && INTEGRAL_TYPE_P (TREE_TYPE (rhs2)))
+           {
+             tree def_rhs1 = gimple_assign_rhs1 (def_stmt);
+             if (code == PLUS_EXPR
+                 && operand_equal_p (def_rhs1, rhs1, 0))
+               {
+                 /* A + ~A -> -1.  */
+                 code = INTEGER_CST;
+                 rhs1 = build_int_cst (TREE_TYPE (rhs1), -1);
+                 rhs2 = NULL_TREE;
+                 gimple_assign_set_rhs_with_ops (&gsi, code, rhs1, NULL_TREE);
+                 gcc_assert (gsi_stmt (gsi) == stmt);
+                 gimple_set_modified (stmt, true);
+               }
+           }
+       }
+    }
+
+out:
+  if (gimple_modified_p (stmt))
+    {
+      fold_stmt_inplace (stmt);
+      update_stmt (stmt);
+    }
+}
+
 /* Main entry point for the forward propagation optimizer.  */
 
 static unsigned int
@@ -1288,8 +1976,11 @@ tree_ssa_forward_propagate_single_use_vars (void)
                      && TREE_CODE (rhs) == ADDR_EXPR
                      && POINTER_TYPE_P (TREE_TYPE (lhs))))
                {
-                 STRIP_NOPS (rhs);
-                 if (!stmt_references_abnormal_ssa_name (stmt)
+                 tree base = get_base_address (TREE_OPERAND (rhs, 0));
+                 if ((!base
+                      || !DECL_P (base)
+                      || decl_address_invariant_p (base))
+                     && !stmt_references_abnormal_ssa_name (stmt)
                      && forward_propagate_addr_expr (lhs, rhs))
                    {
                      release_defs (stmt);
@@ -1300,12 +1991,35 @@ tree_ssa_forward_propagate_single_use_vars (void)
                    gsi_next (&gsi);
                }
              else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
-                      && is_gimple_min_invariant (rhs))
+                      && can_propagate_from (stmt))
                {
-                 /* Make sure to fold &a[0] + off_1 here.  */
-                 fold_stmt_inplace (stmt);
-                 update_stmt (stmt);
-                 if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+                 if (TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST
+                     /* ???  Better adjust the interface to that function
+                        instead of building new trees here.  */
+                     && forward_propagate_addr_expr
+                          (lhs,
+                           build1 (ADDR_EXPR,
+                                   TREE_TYPE (rhs),
+                                   fold_build2 (MEM_REF,
+                                                TREE_TYPE (TREE_TYPE (rhs)),
+                                                rhs,
+                                                fold_convert
+                                                  (ptr_type_node,
+                                                   gimple_assign_rhs2 (stmt))))))
+                   {
+                     release_defs (stmt);
+                     todoflags |= TODO_remove_unused_locals;
+                     gsi_remove (&gsi, true);
+                   }
+                 else if (is_gimple_min_invariant (rhs))
+                   {
+                     /* Make sure to fold &a[0] + off_1 here.  */
+                     fold_stmt_inplace (stmt);
+                     update_stmt (stmt);
+                     if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+                       gsi_next (&gsi);
+                   }
+                 else
                    gsi_next (&gsi);
                }
              else if ((gimple_assign_rhs_code (stmt) == BIT_NOT_EXPR
@@ -1345,6 +2059,12 @@ tree_ssa_forward_propagate_single_use_vars (void)
                  simplify_bitwise_and (&gsi, stmt);
                  gsi_next (&gsi);
                }
+             else if (gimple_assign_rhs_code (stmt) == PLUS_EXPR
+                      || gimple_assign_rhs_code (stmt) == MINUS_EXPR)
+               {
+                 associate_plusminus (stmt);
+                 gsi_next (&gsi);
+               }
              else
                gsi_next (&gsi);
            }
@@ -1364,6 +2084,14 @@ tree_ssa_forward_propagate_single_use_vars (void)
                                              WARN_STRICT_OVERFLOW_CONDITIONAL);
              gsi_next (&gsi);
            }
+         else if (is_gimple_call (stmt))
+           {
+             tree callee = gimple_call_fndecl (stmt);
+             if (callee == NULL_TREE
+                 || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
+                 || !simplify_builtin_call (&gsi, callee))
+               gsi_next (&gsi);
+           }
          else
            gsi_next (&gsi);
        }