The following patch removes folding of bzero, memset, bcopy,
memcpy, mempcpy and memmove from builtins.c and re-implements
them in gimple-fold.c.  This means those foldings are no longer
accessible from GENERIC folding.

Bootstrapped on x86_64-unknown-linux-gnu - testing seems to reveal
some testsuite fallout I still have to look into.

Does this look sensible?  In particular, does any Frontend maintainer
object to no longer getting these simplifications?  Note they
will still apply during gimplification and given they now work
on GENERIC we can simplify them quite a bit (but as followup).

Thanks,
Richard.

2014-08-01  Richard Biener  <rguent...@suse.de>

        * builtins.c (fold_builtin_memory_op, var_decl_component_p,
        fold_builtin_memset): Move to gimple-fold.c.
        (fold_builtin_2): Remove handling of bzero, memset, bcopy,
        memcpy, mempcpy and memmove.
        * gimple-fold.h (gimple_fold_builtin): Adjust signature.
        * gimple-fold.c: Include output.h.
        (readonly_data_expr): New function copied from builtins.c.
        (var_decl_component_p): Moved from builtins.c.
        (gimple_fold_builtin_memory_op): Moved from builtins.c
        fold_builtin_memory_op and rewritten to GIMPLE.
        (gimple_fold_builtin_memset): Likewise.
        (gimple_fold_builtin_1): Split out from gimple_fold_builtin.
        (gimple_fold_builtin): Change signature and handle
        bzero, memset, bcopy, memcpy, mempcpy and memmove folding
        here.
        (gimple_fold_call): Adjust.
        * tree-ssa-ccp.c (pass_fold_builtins::execute): Adjust
        for gimple_fold_builtin change.

Index: gcc/builtins.c
===================================================================
*** gcc/builtins.c.orig 2014-07-31 15:10:04.127659292 +0200
--- gcc/builtins.c      2014-08-01 13:59:34.686001948 +0200
*************** static tree fold_builtin_ceil (location_
*** 164,170 ****
  static tree fold_builtin_round (location_t, tree, tree);
  static tree fold_builtin_int_roundingfn (location_t, tree, tree);
  static tree fold_builtin_bitop (tree, tree);
- static tree fold_builtin_memory_op (location_t, tree, tree, tree, tree, bool, 
int);
  static tree fold_builtin_strchr (location_t, tree, tree, tree);
  static tree fold_builtin_memchr (location_t, tree, tree, tree, tree);
  static tree fold_builtin_memcmp (location_t, tree, tree, tree);
--- 164,169 ----
*************** fold_builtin_exponent (location_t loc, t
*** 8496,8961 ****
    return NULL_TREE;
  }
  
- /* Return true if VAR is a VAR_DECL or a component thereof.  */
- 
- static bool
- var_decl_component_p (tree var)
- {
-   tree inner = var;
-   while (handled_component_p (inner))
-     inner = TREE_OPERAND (inner, 0);
-   return SSA_VAR_P (inner);
- }
- 
- /* Fold function call to builtin memset.  Return
-    NULL_TREE if no simplification can be made.  */
- 
- static tree
- fold_builtin_memset (location_t loc, tree dest, tree c, tree len,
-                    tree type, bool ignore)
- {
-   tree var, ret, etype;
-   unsigned HOST_WIDE_INT length, cval;
- 
-   if (! validate_arg (dest, POINTER_TYPE)
-       || ! validate_arg (c, INTEGER_TYPE)
-       || ! validate_arg (len, INTEGER_TYPE))
-     return NULL_TREE;
- 
-   if (! tree_fits_uhwi_p (len))
-     return NULL_TREE;
- 
-   /* If the LEN parameter is zero, return DEST.  */
-   if (integer_zerop (len))
-     return omit_one_operand_loc (loc, type, dest, c);
- 
-   if (TREE_CODE (c) != INTEGER_CST || TREE_SIDE_EFFECTS (dest))
-     return NULL_TREE;
- 
-   var = dest;
-   STRIP_NOPS (var);
-   if (TREE_CODE (var) != ADDR_EXPR)
-     return NULL_TREE;
- 
-   var = TREE_OPERAND (var, 0);
-   if (TREE_THIS_VOLATILE (var))
-     return NULL_TREE;
- 
-   etype = TREE_TYPE (var);
-   if (TREE_CODE (etype) == ARRAY_TYPE)
-     etype = TREE_TYPE (etype);
- 
-   if (!INTEGRAL_TYPE_P (etype)
-       && !POINTER_TYPE_P (etype))
-     return NULL_TREE;
- 
-   if (! var_decl_component_p (var))
-     return NULL_TREE;
- 
-   length = tree_to_uhwi (len);
-   if (GET_MODE_SIZE (TYPE_MODE (etype)) != length
-       || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
-     return NULL_TREE;
- 
-   if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
-     return NULL_TREE;
- 
-   if (integer_zerop (c))
-     cval = 0;
-   else
-     {
-       if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
-       return NULL_TREE;
- 
-       cval = TREE_INT_CST_LOW (c);
-       cval &= 0xff;
-       cval |= cval << 8;
-       cval |= cval << 16;
-       cval |= (cval << 31) << 1;
-     }
- 
-   ret = build_int_cst_type (etype, cval);
-   var = build_fold_indirect_ref_loc (loc,
-                                fold_convert_loc (loc,
-                                                  build_pointer_type (etype),
-                                                  dest));
-   ret = build2 (MODIFY_EXPR, etype, var, ret);
-   if (ignore)
-     return ret;
- 
-   return omit_one_operand_loc (loc, type, dest, ret);
- }
- 
- /* Fold function call to builtin memset.  Return
-    NULL_TREE if no simplification can be made.  */
- 
- static tree
- fold_builtin_bzero (location_t loc, tree dest, tree size, bool ignore)
- {
-   if (! validate_arg (dest, POINTER_TYPE)
-       || ! validate_arg (size, INTEGER_TYPE))
-     return NULL_TREE;
- 
-   if (!ignore)
-     return NULL_TREE;
- 
-   /* New argument list transforming bzero(ptr x, int y) to
-      memset(ptr x, int 0, size_t y).   This is done this way
-      so that if it isn't expanded inline, we fallback to
-      calling bzero instead of memset.  */
- 
-   return fold_builtin_memset (loc, dest, integer_zero_node,
-                             fold_convert_loc (loc, size_type_node, size),
-                             void_type_node, ignore);
- }
- 
- /* Fold function call to builtin mem{{,p}cpy,move}.  Return
-    NULL_TREE if no simplification can be made.
-    If ENDP is 0, return DEST (like memcpy).
-    If ENDP is 1, return DEST+LEN (like mempcpy).
-    If ENDP is 2, return DEST+LEN-1 (like stpcpy).
-    If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
-    (memmove).   */
- 
- static tree
- fold_builtin_memory_op (location_t loc, tree dest, tree src,
-                       tree len, tree type, bool ignore, int endp)
- {
-   tree destvar, srcvar, expr;
- 
-   if (! validate_arg (dest, POINTER_TYPE)
-       || ! validate_arg (src, POINTER_TYPE)
-       || ! validate_arg (len, INTEGER_TYPE))
-     return NULL_TREE;
- 
-   /* If the LEN parameter is zero, return DEST.  */
-   if (integer_zerop (len))
-     return omit_one_operand_loc (loc, type, dest, src);
- 
-   /* If SRC and DEST are the same (and not volatile), return
-      DEST{,+LEN,+LEN-1}.  */
-   if (operand_equal_p (src, dest, 0))
-     expr = len;
-   else
-     {
-       tree srctype, desttype;
-       unsigned int src_align, dest_align;
-       tree off0;
- 
-       /* Build accesses at offset zero with a ref-all character type.  */
-       off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
-                                                        ptr_mode, true), 0);
- 
-       /* If we can perform the copy efficiently with first doing all loads
-          and then all stores inline it that way.  Currently efficiently
-        means that we can load all the memory into a single integer
-        register which is what MOVE_MAX gives us.  */
-       src_align = get_pointer_alignment (src);
-       dest_align = get_pointer_alignment (dest);
-       if (tree_fits_uhwi_p (len)
-         && compare_tree_int (len, MOVE_MAX) <= 0
-         /* ???  Don't transform copies from strings with known length this
-            confuses the tree-ssa-strlen.c.  This doesn't handle
-            the case in gcc.dg/strlenopt-8.c which is XFAILed for that
-            reason.  */
-         && !c_strlen (src, 2))
-       {
-         unsigned ilen = tree_to_uhwi (len);
-         if (exact_log2 (ilen) != -1)
-           {
-             tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
-             if (type
-                 && TYPE_MODE (type) != BLKmode
-                 && (GET_MODE_SIZE (TYPE_MODE (type)) * BITS_PER_UNIT
-                     == ilen * 8)
-                 /* If the pointers are not aligned we must be able to
-                    emit an unaligned load.  */
-                 && ((src_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type))
-                      && dest_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type)))
-                     || !SLOW_UNALIGNED_ACCESS (TYPE_MODE (type),
-                                                MIN (src_align, dest_align))))
-               {
-                 tree srctype = type;
-                 tree desttype = type;
-                 if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
-                   srctype = build_aligned_type (type, src_align);
-                 if (dest_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
-                   desttype = build_aligned_type (type, dest_align);
-                 if (!ignore)
-                   dest = builtin_save_expr (dest);
-                 expr = build2 (MODIFY_EXPR, type,
-                                fold_build2 (MEM_REF, desttype, dest, off0),
-                                fold_build2 (MEM_REF, srctype, src, off0));
-                 goto done;
-               }
-           }
-       }
- 
-       if (endp == 3)
-       {
-         /* Both DEST and SRC must be pointer types.
-            ??? This is what old code did.  Is the testing for pointer types
-            really mandatory?
- 
-            If either SRC is readonly or length is 1, we can use memcpy.  */
-         if (!dest_align || !src_align)
-           return NULL_TREE;
-         if (readonly_data_expr (src)
-             || (tree_fits_uhwi_p (len)
-                 && (MIN (src_align, dest_align) / BITS_PER_UNIT
-                     >= tree_to_uhwi (len))))
-           {
-             tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
-             if (!fn)
-               return NULL_TREE;
-               return build_call_expr_loc (loc, fn, 3, dest, src, len);
-           }
- 
-         /* If *src and *dest can't overlap, optimize into memcpy as well.  */
-         if (TREE_CODE (src) == ADDR_EXPR
-             && TREE_CODE (dest) == ADDR_EXPR)
-           {
-             tree src_base, dest_base, fn;
-             HOST_WIDE_INT src_offset = 0, dest_offset = 0;
-             HOST_WIDE_INT size = -1;
-             HOST_WIDE_INT maxsize = -1;
- 
-             srcvar = TREE_OPERAND (src, 0);
-             src_base = get_ref_base_and_extent (srcvar, &src_offset,
-                                                 &size, &maxsize);
-             destvar = TREE_OPERAND (dest, 0);
-             dest_base = get_ref_base_and_extent (destvar, &dest_offset,
-                                                  &size, &maxsize);
-             if (tree_fits_uhwi_p (len))
-               maxsize = tree_to_uhwi (len);
-             else
-               maxsize = -1;
-             src_offset /= BITS_PER_UNIT;
-             dest_offset /= BITS_PER_UNIT;
-             if (SSA_VAR_P (src_base)
-                 && SSA_VAR_P (dest_base))
-               {
-                 if (operand_equal_p (src_base, dest_base, 0)
-                     && ranges_overlap_p (src_offset, maxsize,
-                                          dest_offset, maxsize))
-                   return NULL_TREE;
-               }
-             else if (TREE_CODE (src_base) == MEM_REF
-                      && TREE_CODE (dest_base) == MEM_REF)
-               {
-                 if (! operand_equal_p (TREE_OPERAND (src_base, 0),
-                                        TREE_OPERAND (dest_base, 0), 0))
-                   return NULL_TREE;
-                 offset_int off = mem_ref_offset (src_base) + src_offset;
-                 if (!wi::fits_shwi_p (off))
-                   return NULL_TREE;
-                 src_offset = off.to_shwi ();
- 
-                 off = mem_ref_offset (dest_base) + dest_offset;
-                 if (!wi::fits_shwi_p (off))
-                   return NULL_TREE;
-                 dest_offset = off.to_shwi ();
-                 if (ranges_overlap_p (src_offset, maxsize,
-                                       dest_offset, maxsize))
-                   return NULL_TREE;
-               }
-             else
-               return NULL_TREE;
- 
-             fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
-             if (!fn)
-               return NULL_TREE;
-             return build_call_expr_loc (loc, fn, 3, dest, src, len);
-           }
- 
-         /* If the destination and source do not alias optimize into
-            memcpy as well.  */
-         if ((is_gimple_min_invariant (dest)
-              || TREE_CODE (dest) == SSA_NAME)
-             && (is_gimple_min_invariant (src)
-                 || TREE_CODE (src) == SSA_NAME))
-           {
-             ao_ref destr, srcr;
-             ao_ref_init_from_ptr_and_size (&destr, dest, len);
-             ao_ref_init_from_ptr_and_size (&srcr, src, len);
-             if (!refs_may_alias_p_1 (&destr, &srcr, false))
-               {
-                 tree fn;
-                 fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
-                 if (!fn)
-                   return NULL_TREE;
-                 return build_call_expr_loc (loc, fn, 3, dest, src, len);
-               }
-           }
- 
-         return NULL_TREE;
-       }
- 
-       if (!tree_fits_shwi_p (len))
-       return NULL_TREE;
-       /* FIXME:
-          This logic lose for arguments like (type *)malloc (sizeof (type)),
-          since we strip the casts of up to VOID return value from malloc.
-        Perhaps we ought to inherit type from non-VOID argument here?  */
-       STRIP_NOPS (src);
-       STRIP_NOPS (dest);
-       if (!POINTER_TYPE_P (TREE_TYPE (src))
-         || !POINTER_TYPE_P (TREE_TYPE (dest)))
-       return NULL_TREE;
-       /* In the following try to find a type that is most natural to be
-        used for the memcpy source and destination and that allows
-        the most optimization when memcpy is turned into a plain assignment
-        using that type.  In theory we could always use a char[len] type
-        but that only gains us that the destination and source possibly
-        no longer will have their address taken.  */
-       /* As we fold (void *)(p + CST) to (void *)p + CST undo this here.  */
-       if (TREE_CODE (src) == POINTER_PLUS_EXPR)
-       {
-         tree tem = TREE_OPERAND (src, 0);
-         STRIP_NOPS (tem);
-         if (tem != TREE_OPERAND (src, 0))
-           src = build1 (NOP_EXPR, TREE_TYPE (tem), src);
-       }
-       if (TREE_CODE (dest) == POINTER_PLUS_EXPR)
-       {
-         tree tem = TREE_OPERAND (dest, 0);
-         STRIP_NOPS (tem);
-         if (tem != TREE_OPERAND (dest, 0))
-           dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest);
-       }
-       srctype = TREE_TYPE (TREE_TYPE (src));
-       if (TREE_CODE (srctype) == ARRAY_TYPE
-         && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
-       {
-         srctype = TREE_TYPE (srctype);
-         STRIP_NOPS (src);
-         src = build1 (NOP_EXPR, build_pointer_type (srctype), src);
-       }
-       desttype = TREE_TYPE (TREE_TYPE (dest));
-       if (TREE_CODE (desttype) == ARRAY_TYPE
-         && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
-       {
-         desttype = TREE_TYPE (desttype);
-         STRIP_NOPS (dest);
-         dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest);
-       }
-       if (TREE_ADDRESSABLE (srctype)
-         || TREE_ADDRESSABLE (desttype))
-       return NULL_TREE;
- 
-       /* Make sure we are not copying using a floating-point mode or
-          a type whose size possibly does not match its precision.  */
-       if (FLOAT_MODE_P (TYPE_MODE (desttype))
-         || TREE_CODE (desttype) == BOOLEAN_TYPE
-         || TREE_CODE (desttype) == ENUMERAL_TYPE)
-       desttype = bitwise_type_for_mode (TYPE_MODE (desttype));
-       if (FLOAT_MODE_P (TYPE_MODE (srctype))
-         || TREE_CODE (srctype) == BOOLEAN_TYPE
-         || TREE_CODE (srctype) == ENUMERAL_TYPE)
-       srctype = bitwise_type_for_mode (TYPE_MODE (srctype));
-       if (!srctype)
-       srctype = desttype;
-       if (!desttype)
-       desttype = srctype;
-       if (!srctype)
-       return NULL_TREE;
- 
-       src_align = get_pointer_alignment (src);
-       dest_align = get_pointer_alignment (dest);
-       if (dest_align < TYPE_ALIGN (desttype)
-         || src_align < TYPE_ALIGN (srctype))
-       return NULL_TREE;
- 
-       if (!ignore)
-         dest = builtin_save_expr (dest);
- 
-       destvar = dest;
-       STRIP_NOPS (destvar);
-       if (TREE_CODE (destvar) == ADDR_EXPR
-         && var_decl_component_p (TREE_OPERAND (destvar, 0))
-         && tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
-       destvar = fold_build2 (MEM_REF, desttype, destvar, off0);
-       else
-       destvar = NULL_TREE;
- 
-       srcvar = src;
-       STRIP_NOPS (srcvar);
-       if (TREE_CODE (srcvar) == ADDR_EXPR
-         && var_decl_component_p (TREE_OPERAND (srcvar, 0))
-         && tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
-       {
-         if (!destvar
-             || src_align >= TYPE_ALIGN (desttype))
-           srcvar = fold_build2 (MEM_REF, destvar ? desttype : srctype,
-                                 srcvar, off0);
-         else if (!STRICT_ALIGNMENT)
-           {
-             srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
-                                           src_align);
-             srcvar = fold_build2 (MEM_REF, srctype, srcvar, off0);
-           }
-         else
-           srcvar = NULL_TREE;
-       }
-       else
-       srcvar = NULL_TREE;
- 
-       if (srcvar == NULL_TREE && destvar == NULL_TREE)
-       return NULL_TREE;
- 
-       if (srcvar == NULL_TREE)
-       {
-         STRIP_NOPS (src);
-         if (src_align >= TYPE_ALIGN (desttype))
-           srcvar = fold_build2 (MEM_REF, desttype, src, off0);
-         else
-           {
-             if (STRICT_ALIGNMENT)
-               return NULL_TREE;
-             srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
-                                           src_align);
-             srcvar = fold_build2 (MEM_REF, srctype, src, off0);
-           }
-       }
-       else if (destvar == NULL_TREE)
-       {
-         STRIP_NOPS (dest);
-         if (dest_align >= TYPE_ALIGN (srctype))
-           destvar = fold_build2 (MEM_REF, srctype, dest, off0);
-         else
-           {
-             if (STRICT_ALIGNMENT)
-               return NULL_TREE;
-             desttype = build_aligned_type (TYPE_MAIN_VARIANT (srctype),
-                                            dest_align);
-             destvar = fold_build2 (MEM_REF, desttype, dest, off0);
-           }
-       }
- 
-       expr = build2 (MODIFY_EXPR, TREE_TYPE (destvar), destvar, srcvar);
-     }
- 
- done:
-   if (ignore)
-     return expr;
- 
-   if (endp == 0 || endp == 3)
-     return omit_one_operand_loc (loc, type, dest, expr);
- 
-   if (expr == len)
-     expr = NULL_TREE;
- 
-   if (endp == 2)
-     len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len,
-                      ssize_int (1));
- 
-   dest = fold_build_pointer_plus_loc (loc, dest, len);
-   dest = fold_convert_loc (loc, type, dest);
-   if (expr)
-     dest = omit_one_operand_loc (loc, type, dest, expr);
-   return dest;
- }
- 
  /* Fold function call to builtin strcpy with arguments DEST and SRC.
     If LEN is not NULL, it represents the length of the string to be
     copied.  Return NULL_TREE if no simplification can be made.  */
--- 8495,8500 ----
*************** fold_builtin_2 (location_t loc, tree fnd
*** 10707,10715 ****
      CASE_FLT_FN (BUILT_IN_MODF):
        return fold_builtin_modf (loc, arg0, arg1, type);
  
-     case BUILT_IN_BZERO:
-       return fold_builtin_bzero (loc, arg0, arg1, ignore);
- 
      case BUILT_IN_FPUTS:
        return fold_builtin_fputs (loc, arg0, arg1, ignore, false, NULL_TREE);
  
--- 10246,10251 ----
*************** fold_builtin_3 (location_t loc, tree fnd
*** 10866,10890 ****
        return do_mpfr_remquo (arg0, arg1, arg2);
      break;
  
-     case BUILT_IN_MEMSET:
-       return fold_builtin_memset (loc, arg0, arg1, arg2, type, ignore);
- 
-     case BUILT_IN_BCOPY:
-       return fold_builtin_memory_op (loc, arg1, arg0, arg2,
-                                    void_type_node, true, /*endp=*/3);
- 
-     case BUILT_IN_MEMCPY:
-       return fold_builtin_memory_op (loc, arg0, arg1, arg2,
-                                    type, ignore, /*endp=*/0);
- 
-     case BUILT_IN_MEMPCPY:
-       return fold_builtin_memory_op (loc, arg0, arg1, arg2,
-                                    type, ignore, /*endp=*/1);
- 
-     case BUILT_IN_MEMMOVE:
-       return fold_builtin_memory_op (loc, arg0, arg1, arg2,
-                                    type, ignore, /*endp=*/3);
- 
      case BUILT_IN_STRNCAT:
        return fold_builtin_strncat (loc, arg0, arg1, arg2);
  
--- 10402,10407 ----
Index: gcc/gimple-fold.c
===================================================================
*** gcc/gimple-fold.c.orig      2014-07-29 13:05:27.958071088 +0200
--- gcc/gimple-fold.c   2014-08-01 13:42:10.748073822 +0200
*************** along with GCC; see the file COPYING3.
*** 54,59 ****
--- 54,60 ----
  #include "gimplify-me.h"
  #include "dbgcnt.h"
  #include "builtins.h"
+ #include "output.h"
  
  /* Return true when DECL can be referenced from current unit.
     FROM_DECL (if non-null) specify constructor of variable DECL was taken 
from.
*************** gimplify_and_update_call_from_tree (gimp
*** 741,746 ****
--- 742,1310 ----
    gsi_replace_with_seq (si_p, stmts, false);
  }
  
+ 
+ /* Returns true is EXP represents data that would potentially reside
+    in a readonly section.  */
+ 
+ static bool
+ readonly_data_expr (tree exp)
+ {
+   STRIP_NOPS (exp);
+ 
+   if (TREE_CODE (exp) != ADDR_EXPR)
+     return false;
+ 
+   exp = get_base_address (TREE_OPERAND (exp, 0));
+   if (!exp)
+     return false;
+ 
+   /* Make sure we call decl_readonly_section only for trees it
+      can handle (since it returns true for everything it doesn't
+      understand).  */
+   if (TREE_CODE (exp) == STRING_CST
+       || TREE_CODE (exp) == CONSTRUCTOR
+       || (TREE_CODE (exp) == VAR_DECL && TREE_STATIC (exp)))
+     return decl_readonly_section (exp, 0);
+   else
+     return false;
+ }
+ 
+ /* Return true if VAR is a VAR_DECL or a component thereof.  */
+ 
+ static bool
+ var_decl_component_p (tree var)
+ {
+   tree inner = var;
+   while (handled_component_p (inner))
+     inner = TREE_OPERAND (inner, 0);
+   return SSA_VAR_P (inner);
+ }
+ 
+ /* Fold function call to builtin mem{{,p}cpy,move}.  Return
+    NULL_TREE if no simplification can be made.
+    If ENDP is 0, return DEST (like memcpy).
+    If ENDP is 1, return DEST+LEN (like mempcpy).
+    If ENDP is 2, return DEST+LEN-1 (like stpcpy).
+    If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
+    (memmove).   */
+ 
+ static bool 
+ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
+                              tree dest, tree src, int endp)
+ {
+   gimple stmt = gsi_stmt (*gsi);
+   tree lhs = gimple_call_lhs (stmt);
+   tree len = gimple_call_arg (stmt, 2);
+   tree destvar, srcvar;
+   location_t loc = gimple_location (stmt);
+ 
+   /* If the LEN parameter is zero, return DEST.  */
+   if (integer_zerop (len))
+     {
+       gimple repl;
+       if (gimple_call_lhs (stmt))
+       repl = gimple_build_assign (gimple_call_lhs (stmt), dest);
+       else
+       repl = gimple_build_nop ();
+       tree vdef = gimple_vdef (stmt);
+       if (vdef && TREE_CODE (vdef) == SSA_NAME)
+       {
+         unlink_stmt_vdef (stmt);
+         release_ssa_name (vdef);
+       }
+       gsi_replace (gsi, repl, true);
+       return true;
+     }
+ 
+   /* If SRC and DEST are the same (and not volatile), return
+      DEST{,+LEN,+LEN-1}.  */
+   if (operand_equal_p (src, dest, 0))
+     {
+       unlink_stmt_vdef (stmt);
+       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
+       release_ssa_name (gimple_vdef (stmt));
+       goto done;
+     }
+   else
+     {
+       tree srctype, desttype;
+       unsigned int src_align, dest_align;
+       tree off0;
+ 
+       /* Build accesses at offset zero with a ref-all character type.  */
+       off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
+                                                        ptr_mode, true), 0);
+ 
+       /* If we can perform the copy efficiently with first doing all loads
+          and then all stores inline it that way.  Currently efficiently
+        means that we can load all the memory into a single integer
+        register which is what MOVE_MAX gives us.  */
+       src_align = get_pointer_alignment (src);
+       dest_align = get_pointer_alignment (dest);
+       if (tree_fits_uhwi_p (len)
+         && compare_tree_int (len, MOVE_MAX) <= 0
+         /* ???  Don't transform copies from strings with known length this
+            confuses the tree-ssa-strlen.c.  This doesn't handle
+            the case in gcc.dg/strlenopt-8.c which is XFAILed for that
+            reason.  */
+         && !c_strlen (src, 2))
+       {
+         unsigned ilen = tree_to_uhwi (len);
+         if (exact_log2 (ilen) != -1)
+           {
+             tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
+             if (type
+                 && TYPE_MODE (type) != BLKmode
+                 && (GET_MODE_SIZE (TYPE_MODE (type)) * BITS_PER_UNIT
+                     == ilen * 8)
+                 /* If the destination pointer is not aligned we must be able
+                    to emit an unaligned store.  */
+                 && (dest_align >= GET_MODE_ALIGNMENT (TYPE_MODE (type))
+                     || !SLOW_UNALIGNED_ACCESS (TYPE_MODE (type), dest_align)))
+               {
+                 tree srctype = type;
+                 tree desttype = type;
+                 if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+                   srctype = build_aligned_type (type, src_align);
+                 tree srcmem = fold_build2 (MEM_REF, srctype, src, off0);
+                 tree tem = fold_const_aggregate_ref (srcmem);
+                 if (tem)
+                   srcmem = tem;
+                 else if (src_align < GET_MODE_ALIGNMENT (TYPE_MODE (type))
+                          && SLOW_UNALIGNED_ACCESS (TYPE_MODE (type),
+                                                    src_align))
+                   srcmem = NULL_TREE;
+                 if (srcmem)
+                   {
+                     gimple new_stmt;
+                     if (is_gimple_reg_type (TREE_TYPE (srcmem)))
+                       {
+                         new_stmt = gimple_build_assign (NULL_TREE, srcmem);
+                         if (gimple_in_ssa_p (cfun))
+                           srcmem = make_ssa_name (TREE_TYPE (srcmem),
+                                                   new_stmt);
+                         else
+                           srcmem = create_tmp_reg (TREE_TYPE (srcmem),
+                                                    NULL);
+                         gimple_assign_set_lhs (new_stmt, srcmem);
+                         gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+                         gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+                       }
+                     if (dest_align < GET_MODE_ALIGNMENT (TYPE_MODE (type)))
+                       desttype = build_aligned_type (type, dest_align);
+                     new_stmt
+                       = gimple_build_assign (fold_build2 (MEM_REF, desttype,
+                                                           dest, off0),
+                                              srcmem);
+                     gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+                     gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+                     if (gimple_vdef (new_stmt)
+                         && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+                       SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
+                     if (!lhs)
+                       {
+                         gsi_replace (gsi, new_stmt, true);
+                         return true;
+                       }
+                     gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+                     goto done;
+                   }
+               }
+           }
+       }
+ 
+       if (endp == 3)
+       {
+         /* Both DEST and SRC must be pointer types.
+            ??? This is what old code did.  Is the testing for pointer types
+            really mandatory?
+ 
+            If either SRC is readonly or length is 1, we can use memcpy.  */
+         if (!dest_align || !src_align)
+           return false;
+         if (readonly_data_expr (src)
+             || (tree_fits_uhwi_p (len)
+                 && (MIN (src_align, dest_align) / BITS_PER_UNIT
+                     >= tree_to_uhwi (len))))
+           {
+             tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+             if (!fn)
+               return false;
+             gimple_call_set_fndecl (stmt, fn);
+             gimple_call_set_arg (stmt, 0, dest);
+             gimple_call_set_arg (stmt, 1, src);
+             return true;
+           }
+ 
+         /* If *src and *dest can't overlap, optimize into memcpy as well.  */
+         if (TREE_CODE (src) == ADDR_EXPR
+             && TREE_CODE (dest) == ADDR_EXPR)
+           {
+             tree src_base, dest_base, fn;
+             HOST_WIDE_INT src_offset = 0, dest_offset = 0;
+             HOST_WIDE_INT size = -1;
+             HOST_WIDE_INT maxsize = -1;
+ 
+             srcvar = TREE_OPERAND (src, 0);
+             src_base = get_ref_base_and_extent (srcvar, &src_offset,
+                                                 &size, &maxsize);
+             destvar = TREE_OPERAND (dest, 0);
+             dest_base = get_ref_base_and_extent (destvar, &dest_offset,
+                                                  &size, &maxsize);
+             if (tree_fits_uhwi_p (len))
+               maxsize = tree_to_uhwi (len);
+             else
+               maxsize = -1;
+             src_offset /= BITS_PER_UNIT;
+             dest_offset /= BITS_PER_UNIT;
+             if (SSA_VAR_P (src_base)
+                 && SSA_VAR_P (dest_base))
+               {
+                 if (operand_equal_p (src_base, dest_base, 0)
+                     && ranges_overlap_p (src_offset, maxsize,
+                                          dest_offset, maxsize))
+                   return false;
+               }
+             else if (TREE_CODE (src_base) == MEM_REF
+                      && TREE_CODE (dest_base) == MEM_REF)
+               {
+                 if (! operand_equal_p (TREE_OPERAND (src_base, 0),
+                                        TREE_OPERAND (dest_base, 0), 0))
+                   return false;
+                 offset_int off = mem_ref_offset (src_base) + src_offset;
+                 if (!wi::fits_shwi_p (off))
+                   return false;
+                 src_offset = off.to_shwi ();
+ 
+                 off = mem_ref_offset (dest_base) + dest_offset;
+                 if (!wi::fits_shwi_p (off))
+                   return false;
+                 dest_offset = off.to_shwi ();
+                 if (ranges_overlap_p (src_offset, maxsize,
+                                       dest_offset, maxsize))
+                   return false;
+               }
+             else
+               return false;
+ 
+             fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+             if (!fn)
+               return false;
+             gimple_call_set_fndecl (stmt, fn);
+             gimple_call_set_arg (stmt, 0, dest);
+             gimple_call_set_arg (stmt, 1, src);
+             return true;
+           }
+ 
+         /* If the destination and source do not alias optimize into
+            memcpy as well.  */
+         if ((is_gimple_min_invariant (dest)
+              || TREE_CODE (dest) == SSA_NAME)
+             && (is_gimple_min_invariant (src)
+                 || TREE_CODE (src) == SSA_NAME))
+           {
+             ao_ref destr, srcr;
+             ao_ref_init_from_ptr_and_size (&destr, dest, len);
+             ao_ref_init_from_ptr_and_size (&srcr, src, len);
+             if (!refs_may_alias_p_1 (&destr, &srcr, false))
+               {
+                 tree fn;
+                 fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
+                 if (!fn)
+                   return false;
+                 gimple_call_set_fndecl (stmt, fn);
+                 gimple_call_set_arg (stmt, 0, dest);
+                 gimple_call_set_arg (stmt, 1, src);
+                 return true;
+               }
+           }
+ 
+         return false;
+       }
+ 
+       if (!tree_fits_shwi_p (len))
+       return false;
+       /* FIXME:
+          This logic lose for arguments like (type *)malloc (sizeof (type)),
+          since we strip the casts of up to VOID return value from malloc.
+        Perhaps we ought to inherit type from non-VOID argument here?  */
+       STRIP_NOPS (src);
+       STRIP_NOPS (dest);
+       if (!POINTER_TYPE_P (TREE_TYPE (src))
+         || !POINTER_TYPE_P (TREE_TYPE (dest)))
+       return false;
+       /* In the following try to find a type that is most natural to be
+        used for the memcpy source and destination and that allows
+        the most optimization when memcpy is turned into a plain assignment
+        using that type.  In theory we could always use a char[len] type
+        but that only gains us that the destination and source possibly
+        no longer will have their address taken.  */
+       /* As we fold (void *)(p + CST) to (void *)p + CST undo this here.  */
+       if (TREE_CODE (src) == POINTER_PLUS_EXPR)
+       {
+         tree tem = TREE_OPERAND (src, 0);
+         STRIP_NOPS (tem);
+         if (tem != TREE_OPERAND (src, 0))
+           src = build1 (NOP_EXPR, TREE_TYPE (tem), src);
+       }
+       if (TREE_CODE (dest) == POINTER_PLUS_EXPR)
+       {
+         tree tem = TREE_OPERAND (dest, 0);
+         STRIP_NOPS (tem);
+         if (tem != TREE_OPERAND (dest, 0))
+           dest = build1 (NOP_EXPR, TREE_TYPE (tem), dest);
+       }
+       srctype = TREE_TYPE (TREE_TYPE (src));
+       if (TREE_CODE (srctype) == ARRAY_TYPE
+         && !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+       {
+         srctype = TREE_TYPE (srctype);
+         STRIP_NOPS (src);
+         src = build1 (NOP_EXPR, build_pointer_type (srctype), src);
+       }
+       desttype = TREE_TYPE (TREE_TYPE (dest));
+       if (TREE_CODE (desttype) == ARRAY_TYPE
+         && !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+       {
+         desttype = TREE_TYPE (desttype);
+         STRIP_NOPS (dest);
+         dest = build1 (NOP_EXPR, build_pointer_type (desttype), dest);
+       }
+       if (TREE_ADDRESSABLE (srctype)
+         || TREE_ADDRESSABLE (desttype))
+       return false;
+ 
+       /* Make sure we are not copying using a floating-point mode or
+          a type whose size possibly does not match its precision.  */
+       if (FLOAT_MODE_P (TYPE_MODE (desttype))
+         || TREE_CODE (desttype) == BOOLEAN_TYPE
+         || TREE_CODE (desttype) == ENUMERAL_TYPE)
+       desttype = bitwise_type_for_mode (TYPE_MODE (desttype));
+       if (FLOAT_MODE_P (TYPE_MODE (srctype))
+         || TREE_CODE (srctype) == BOOLEAN_TYPE
+         || TREE_CODE (srctype) == ENUMERAL_TYPE)
+       srctype = bitwise_type_for_mode (TYPE_MODE (srctype));
+       if (!srctype)
+       srctype = desttype;
+       if (!desttype)
+       desttype = srctype;
+       if (!srctype)
+       return false;
+ 
+       src_align = get_pointer_alignment (src);
+       dest_align = get_pointer_alignment (dest);
+       if (dest_align < TYPE_ALIGN (desttype)
+         || src_align < TYPE_ALIGN (srctype))
+       return false;
+ 
+       destvar = dest;
+       STRIP_NOPS (destvar);
+       if (TREE_CODE (destvar) == ADDR_EXPR
+         && var_decl_component_p (TREE_OPERAND (destvar, 0))
+         && tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+       destvar = fold_build2 (MEM_REF, desttype, destvar, off0);
+       else
+       destvar = NULL_TREE;
+ 
+       srcvar = src;
+       STRIP_NOPS (srcvar);
+       if (TREE_CODE (srcvar) == ADDR_EXPR
+         && var_decl_component_p (TREE_OPERAND (srcvar, 0))
+         && tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+       {
+         if (!destvar
+             || src_align >= TYPE_ALIGN (desttype))
+           srcvar = fold_build2 (MEM_REF, destvar ? desttype : srctype,
+                                 srcvar, off0);
+         else if (!STRICT_ALIGNMENT)
+           {
+             srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
+                                           src_align);
+             srcvar = fold_build2 (MEM_REF, srctype, srcvar, off0);
+           }
+         else
+           srcvar = NULL_TREE;
+       }
+       else
+       srcvar = NULL_TREE;
+ 
+       if (srcvar == NULL_TREE && destvar == NULL_TREE)
+       return false;
+ 
+       if (srcvar == NULL_TREE)
+       {
+         STRIP_NOPS (src);
+         if (src_align >= TYPE_ALIGN (desttype))
+           srcvar = fold_build2 (MEM_REF, desttype, src, off0);
+         else
+           {
+             if (STRICT_ALIGNMENT)
+               return false;
+             srctype = build_aligned_type (TYPE_MAIN_VARIANT (desttype),
+                                           src_align);
+             srcvar = fold_build2 (MEM_REF, srctype, src, off0);
+           }
+       }
+       else if (destvar == NULL_TREE)
+       {
+         STRIP_NOPS (dest);
+         if (dest_align >= TYPE_ALIGN (srctype))
+           destvar = fold_build2 (MEM_REF, srctype, dest, off0);
+         else
+           {
+             if (STRICT_ALIGNMENT)
+               return false;
+             desttype = build_aligned_type (TYPE_MAIN_VARIANT (srctype),
+                                            dest_align);
+             destvar = fold_build2 (MEM_REF, desttype, dest, off0);
+           }
+       }
+ 
+       gimple new_stmt;
+       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
+       {
+         new_stmt = gimple_build_assign (NULL_TREE, srcvar);
+         if (gimple_in_ssa_p (cfun))
+           srcvar = make_ssa_name (TREE_TYPE (srcvar), new_stmt);
+         else
+           srcvar = create_tmp_reg (TREE_TYPE (srcvar), NULL);
+         gimple_assign_set_lhs (new_stmt, srcvar);
+         gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+         gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+       }
+       new_stmt = gimple_build_assign (destvar, srcvar);
+       gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+       gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+       if (gimple_vdef (new_stmt)
+         && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
+       SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
+       if (!lhs)
+       {
+         gsi_replace (gsi, new_stmt, true);
+         return true;
+       }
+       gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+     }
+ 
+ done:
+   if (!lhs)
+     {
+       gsi_remove (gsi, true);
+       return true;
+     }
+ 
+   if (endp == 0 || endp == 3)
+     len = NULL_TREE;
+   else if (endp == 2)
+     len = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (len), len,
+                          ssize_int (1));
+   if (endp == 2 || endp == 1)
+     dest = fold_build_pointer_plus_loc (loc, dest, len);
+ 
+   dest = force_gimple_operand_gsi (gsi, dest, false, NULL_TREE, true,
+                                  GSI_SAME_STMT);
+   gimple repl = gimple_build_assign (lhs, dest);
+   gsi_replace (gsi, repl, true);
+   return true;
+ }
+ 
+ /* Fold function call to builtin memset or bzero at *GSI setting the
+    memory of size LEN to VAL.  Return whether a simplification was made.  */
+ 
+ static bool
+ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
+ {
+   gimple stmt = gsi_stmt (*gsi);
+   tree etype;
+   unsigned HOST_WIDE_INT length, cval;
+ 
+   /* If the LEN parameter is zero, return DEST.  */
+   if (integer_zerop (len))
+     {
+       gimple repl;
+       if (gimple_call_lhs (stmt))
+       repl = gimple_build_assign (gimple_call_lhs (stmt),
+                                   gimple_call_arg (stmt, 0));
+       else
+       repl = gimple_build_nop ();
+       tree vdef = gimple_vdef (stmt);
+       if (vdef && TREE_CODE (vdef) == SSA_NAME)
+       {
+         unlink_stmt_vdef (stmt);
+         release_ssa_name (vdef);
+       }
+       gsi_replace (gsi, repl, true);
+       return true;
+     }
+ 
+   if (! tree_fits_uhwi_p (len))
+     return false;
+ 
+   if (TREE_CODE (c) != INTEGER_CST)
+     return false;
+ 
+   tree dest = gimple_call_arg (stmt, 0);
+   tree var = dest;
+   if (TREE_CODE (var) != ADDR_EXPR)
+     return false;
+ 
+   var = TREE_OPERAND (var, 0);
+   if (TREE_THIS_VOLATILE (var))
+     return false;
+ 
+   etype = TREE_TYPE (var);
+   if (TREE_CODE (etype) == ARRAY_TYPE)
+     etype = TREE_TYPE (etype);
+ 
+   if (!INTEGRAL_TYPE_P (etype)
+       && !POINTER_TYPE_P (etype))
+     return NULL_TREE;
+ 
+   if (! var_decl_component_p (var))
+     return NULL_TREE;
+ 
+   length = tree_to_uhwi (len);
+   if (GET_MODE_SIZE (TYPE_MODE (etype)) != length
+       || get_pointer_alignment (dest) / BITS_PER_UNIT < length)
+     return NULL_TREE;
+ 
+   if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+     return NULL_TREE;
+ 
+   if (integer_zerop (c))
+     cval = 0;
+   else
+     {
+       if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
+       return NULL_TREE;
+ 
+       cval = TREE_INT_CST_LOW (c);
+       cval &= 0xff;
+       cval |= cval << 8;
+       cval |= cval << 16;
+       cval |= (cval << 31) << 1;
+     }
+ 
+   var = fold_build2 (MEM_REF, etype, dest, build_int_cst (ptr_type_node, 0));
+   gimple store = gimple_build_assign (var, build_int_cst_type (etype, cval));
+   gimple_set_vuse (store, gimple_vuse (stmt));
+   tree vdef = gimple_vdef (stmt);
+   if (vdef && TREE_CODE (vdef) == SSA_NAME)
+     {
+       gimple_set_vdef (store, gimple_vdef (stmt));
+       SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = store;
+     }
+   gsi_replace (gsi, store, true);
+   if (gimple_call_lhs (stmt))
+     {
+       gimple asgn = gimple_build_assign (gimple_call_lhs (stmt), dest);
+       gsi_insert_after (gsi, asgn, GSI_NEW_STMT);
+     }
+ 
+   return true;
+ }
+ 
+ 
  /* Return the string length, maximum string length or maximum value of
     ARG in LENGTH.
     If ARG is an SSA name variable, follow its use-def chains.  If LENGTH
*************** get_maxval_strlen (tree arg, tree *lengt
*** 873,887 ****
     Note that some builtins expand into inline code that may not
     be valid in GIMPLE.  Callers must take care.  */
  
! tree
! gimple_fold_builtin (gimple stmt)
  {
    tree result, val[3];
!   tree callee, a;
    int arg_idx, type;
    bitmap visited;
    bool ignore;
-   int nargs;
    location_t loc = gimple_location (stmt);
  
    ignore = (gimple_call_lhs (stmt) == NULL);
--- 1437,1450 ----
     Note that some builtins expand into inline code that may not
     be valid in GIMPLE.  Callers must take care.  */
  
! static tree
! gimple_fold_builtin_1 (gimple stmt)
  {
    tree result, val[3];
!   tree a;
    int arg_idx, type;
    bitmap visited;
    bool ignore;
    location_t loc = gimple_location (stmt);
  
    ignore = (gimple_call_lhs (stmt) == NULL);
*************** gimple_fold_builtin (gimple stmt)
*** 898,920 ****
        return result;
      }
  
-   /* Ignore MD builtins.  */
-   callee = gimple_call_fndecl (stmt);
-   if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD)
-     return NULL_TREE;
- 
-   /* Give up for always_inline inline builtins until they are
-      inlined.  */
-   if (avoid_folding_inline_builtin (callee))
-     return NULL_TREE;
- 
-   /* If the builtin could not be folded, and it has no argument list,
-      we're done.  */
-   nargs = gimple_call_num_args (stmt);
-   if (nargs == 0)
-     return NULL_TREE;
- 
    /* Limit the work only for builtins we know how to simplify.  */
    switch (DECL_FUNCTION_CODE (callee))
      {
      case BUILT_IN_STRLEN:
--- 1461,1468 ----
        return result;
      }
  
    /* Limit the work only for builtins we know how to simplify.  */
+   tree callee = gimple_call_fndecl (stmt);
    switch (DECL_FUNCTION_CODE (callee))
      {
      case BUILT_IN_STRLEN:
*************** gimple_fold_builtin (gimple stmt)
*** 952,957 ****
--- 1500,1506 ----
        return NULL_TREE;
      }
  
+   int nargs = gimple_call_num_args (stmt);
    if (arg_idx >= nargs)
      return NULL_TREE;
  
*************** gimple_fold_builtin (gimple stmt)
*** 1074,1079 ****
--- 1623,1690 ----
    return result;
  }
  
+ bool
+ gimple_fold_builtin (gimple_stmt_iterator *gsi)
+ {
+   gimple stmt = gsi_stmt (*gsi);
+ 
+   /* Ignore MD builtins.  */
+   tree callee = gimple_call_fndecl (stmt);
+   if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD)
+     return NULL_TREE;
+ 
+   /* Give up for always_inline inline builtins until they are
+      inlined.  */
+   if (avoid_folding_inline_builtin (callee))
+     return NULL_TREE;
+ 
+   switch (DECL_FUNCTION_CODE (callee))
+     {
+     case BUILT_IN_BZERO:
+       if (gimple_fold_builtin_memset (gsi,
+                                     integer_zero_node,
+                                     gimple_call_arg (stmt, 1)))
+       return true;
+       break;
+     case BUILT_IN_MEMSET:
+       if (gimple_fold_builtin_memset (gsi,
+                                     gimple_call_arg (stmt, 1),
+                                     gimple_call_arg (stmt, 2)))
+       return true;
+       break;
+ 
+     case BUILT_IN_BCOPY:
+       if (gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 1),
+                                        gimple_call_arg (stmt, 0), 3))
+       return true;
+       break;
+     case BUILT_IN_MEMCPY:
+       if (gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+                                        gimple_call_arg (stmt, 1), 0))
+       return true;
+     case BUILT_IN_MEMPCPY:
+       if (gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+                                        gimple_call_arg (stmt, 1), 1))
+       return true;
+       break;
+     case BUILT_IN_MEMMOVE:
+       if (gimple_fold_builtin_memory_op (gsi, gimple_call_arg (stmt, 0),
+                                        gimple_call_arg (stmt, 1), 3))
+       return true;
+       break;
+ 
+     default:;
+     }
+ 
+   tree result = gimple_fold_builtin_1 (stmt);
+   if (result)
+     {
+       if (!update_call_from_tree (gsi, result))
+       gimplify_and_update_call_from_tree (gsi, result);
+       return true;
+     }
+   return false;
+ }
  
  /* Attempt to fold a call statement referenced by the statement iterator GSI.
     The statement may be replaced by another statement, e.g., if the call
*************** gimple_fold_call (gimple_stmt_iterator *
*** 1188,1200 ****
       available in the generic fold routines.  */
    if (gimple_call_builtin_p (stmt))
      {
!       tree result = gimple_fold_builtin (stmt);
!       if (result)
!       {
!           if (!update_call_from_tree (gsi, result))
!           gimplify_and_update_call_from_tree (gsi, result);
!         changed = true;
!       }
        else if (gimple_call_builtin_p (stmt, BUILT_IN_MD))
        changed |= targetm.gimple_fold_builtin (gsi);
      }
--- 1799,1806 ----
       available in the generic fold routines.  */
    if (gimple_call_builtin_p (stmt))
      {
!       if (gimple_fold_builtin (gsi))
!         changed = true;
        else if (gimple_call_builtin_p (stmt, BUILT_IN_MD))
        changed |= targetm.gimple_fold_builtin (gsi);
      }
Index: gcc/gimple-fold.h
===================================================================
*** gcc/gimple-fold.h.orig      2014-03-20 12:25:21.499247099 +0100
--- gcc/gimple-fold.h   2014-07-31 15:43:41.739520382 +0200
*************** along with GCC; see the file COPYING3.
*** 25,31 ****
  extern tree canonicalize_constructor_val (tree, tree);
  extern tree get_symbol_constant_value (tree);
  extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
! extern tree gimple_fold_builtin (gimple);
  extern bool fold_stmt (gimple_stmt_iterator *);
  extern bool fold_stmt_inplace (gimple_stmt_iterator *);
  extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, 
--- 25,31 ----
  extern tree canonicalize_constructor_val (tree, tree);
  extern tree get_symbol_constant_value (tree);
  extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
! extern bool gimple_fold_builtin (gimple_stmt_iterator *);
  extern bool fold_stmt (gimple_stmt_iterator *);
  extern bool fold_stmt_inplace (gimple_stmt_iterator *);
  extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, 
Index: gcc/tree-ssa-ccp.c
===================================================================
*** gcc/tree-ssa-ccp.c.orig     2014-07-25 13:33:36.106749004 +0200
--- gcc/tree-ssa-ccp.c  2014-07-31 16:02:46.847441542 +0200
*************** pass_fold_builtins::execute (function *f
*** 2676,2682 ****
        for (i = gsi_start_bb (bb); !gsi_end_p (i); )
        {
            gimple stmt, old_stmt;
!         tree callee, result;
          enum built_in_function fcode;
  
          stmt = gsi_stmt (i);
--- 2676,2682 ----
        for (i = gsi_start_bb (bb); !gsi_end_p (i); )
        {
            gimple stmt, old_stmt;
!         tree callee;
          enum built_in_function fcode;
  
          stmt = gsi_stmt (i);
*************** pass_fold_builtins::execute (function *f
*** 2709,2762 ****
            }
          fcode = DECL_FUNCTION_CODE (callee);
  
!         result = gimple_fold_builtin (stmt);
! 
!         if (result)
!           gimple_remove_stmt_histograms (fun, stmt);
! 
!         if (!result)
!           switch (DECL_FUNCTION_CODE (callee))
!             {
!             case BUILT_IN_CONSTANT_P:
!               /* Resolve __builtin_constant_p.  If it hasn't been
!                  folded to integer_one_node by now, it's fairly
!                  certain that the value simply isn't constant.  */
!                 result = integer_zero_node;
                break;
  
!             case BUILT_IN_ASSUME_ALIGNED:
!               /* Remove __builtin_assume_aligned.  */
!               result = gimple_call_arg (stmt, 0);
                break;
  
!             case BUILT_IN_STACK_RESTORE:
!               result = optimize_stack_restore (i);
!               if (result)
!                 break;
!               gsi_next (&i);
!               continue;
! 
!             case BUILT_IN_UNREACHABLE:
!               if (optimize_unreachable (i))
!                 cfg_changed = true;
!               break;
  
!             case BUILT_IN_VA_START:
!             case BUILT_IN_VA_END:
!             case BUILT_IN_VA_COPY:
!               /* These shouldn't be folded before pass_stdarg.  */
!               result = optimize_stdarg_builtin (stmt);
!               if (result)
!                 break;
!               /* FALLTHRU */
! 
!             default:
!               gsi_next (&i);
!               continue;
!             }
  
!         if (result == NULL_TREE)
!           break;
  
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
--- 2709,2767 ----
            }
          fcode = DECL_FUNCTION_CODE (callee);
  
!         tree result = NULL_TREE;
!         switch (DECL_FUNCTION_CODE (callee))
!           {
!           case BUILT_IN_CONSTANT_P:
!             /* Resolve __builtin_constant_p.  If it hasn't been
!                folded to integer_one_node by now, it's fairly
!                certain that the value simply isn't constant.  */
!             result = integer_zero_node;
!             break;
! 
!           case BUILT_IN_ASSUME_ALIGNED:
!             /* Remove __builtin_assume_aligned.  */
!             result = gimple_call_arg (stmt, 0);
!             break;
! 
!           case BUILT_IN_STACK_RESTORE:
!             result = optimize_stack_restore (i);
!             if (result)
                break;
+             gsi_next (&i);
+             continue;
  
!           case BUILT_IN_UNREACHABLE:
!             if (optimize_unreachable (i))
!               cfg_changed = true;
!             break;
! 
!           case BUILT_IN_VA_START:
!           case BUILT_IN_VA_END:
!           case BUILT_IN_VA_COPY:
!             /* These shouldn't be folded before pass_stdarg.  */
!             result = optimize_stdarg_builtin (stmt);
!             if (result)
                break;
+             /* FALLTHRU */
  
!           default:;
!           }
  
!         if (result)
!           {
!             if (!update_call_from_tree (&i, result))
!               gimplify_and_update_call_from_tree (&i, result);
!           }
!         else if (gimple_fold_builtin (&i))
!           ;
!         else
!           {
!             gsi_next (&i);
!             continue;
!           }
  
!         todoflags |= TODO_update_address_taken;
  
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
*************** pass_fold_builtins::execute (function *f
*** 2765,2776 ****
            }
  
            old_stmt = stmt;
-           if (!update_call_from_tree (&i, result))
-           {
-             gimplify_and_update_call_from_tree (&i, result);
-             todoflags |= TODO_update_address_taken;
-           }
- 
          stmt = gsi_stmt (i);
          update_stmt (stmt);
  
--- 2770,2775 ----

Reply via email to