OSDN Git Service

2009-04-10 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index 85d61b5..9f0f4ac 100644 (file)
@@ -1,6 +1,6 @@
 /* Expand builtin functions.
    Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -51,6 +51,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "value-prof.h"
 #include "diagnostic.h"
 
+#ifndef SLOW_UNALIGNED_ACCESS
+#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT
+#endif
+
 #ifndef PAD_VARARGS_DOWN
 #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
 #endif
@@ -237,13 +241,9 @@ static tree do_mpfr_bessel_n (tree, tree, tree,
 static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 
-/* Return true if NODE should be considered for inline expansion regardless
-   of the optimization level.  This means whenever a function is invoked with
-   its "internal" name, which normally contains the prefix "__builtin".  */
-
-static bool called_as_built_in (tree node)
+bool
+is_builtin_name (const char *name)
 {
-  const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
   if (strncmp (name, "__builtin_", 10) == 0)
     return true;
   if (strncmp (name, "__sync_", 7) == 0)
@@ -251,6 +251,20 @@ static bool called_as_built_in (tree node)
   return false;
 }
 
+/* Return true if NODE should be considered for inline expansion regardless
+   of the optimization level.  This means whenever a function is invoked with
+   its "internal" name, which normally contains the prefix "__builtin".  */
+
+static bool
+called_as_built_in (tree node)
+{
+  /* Note that we must use DECL_NAME, not DECL_ASSEMBLER_NAME_SET_P since
+     we want the name used to call the function, not the name it
+     will have. */
+  const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
+  return is_builtin_name (name);
+}
+
 /* Return the alignment in bits of EXP, an object.
    Don't return more than MAX_ALIGN no matter what, ALIGN is the inital
    guessed alignment e.g. from type alignment.  */
@@ -465,16 +479,13 @@ c_strlen (tree src, int only_value)
   else
     offset = tree_low_cst (offset_node, 0);
 
-  /* If the offset is known to be out of bounds, warn, and call strlen at
-     runtime.  */
+  /* If the offset is known to be out of bounds, the front-end should
+     have warned already. We call strlen at runtime.  
+
+     ??? Perhaps we should turn this into an assert and force
+     front-ends to define offsets whtin boundaries.  */
   if (offset < 0 || offset > max)
     {
-     /* Suppress multiple warnings for propagated constant strings.  */
-      if (! TREE_NO_WARNING (src))
-        {
-          warning (0, "offset outside bounds of constant string");
-          TREE_NO_WARNING (src) = 1;
-        }
       return NULL_TREE;
     }
 
@@ -1090,8 +1101,17 @@ expand_builtin_prefetch (tree exp)
 static rtx
 get_memory_rtx (tree exp, tree len)
 {
-  rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
-  rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
+  tree orig_exp = exp;
+  rtx addr, mem;
+  HOST_WIDE_INT off;
+
+  /* When EXP is not resolved SAVE_EXPR, MEM_ATTRS can be still derived
+     from its expression, for expr->a.b only <variable>.a.b is recorded.  */
+  if (TREE_CODE (exp) == SAVE_EXPR && !SAVE_EXPR_RESOLVED_P (exp))
+    exp = TREE_OPERAND (exp, 0);
+
+  addr = expand_expr (orig_exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
+  mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
 
   /* Get an expression we can use to find the attributes to assign to MEM.
      If it is an ADDR_EXPR, use the operand.  Otherwise, dereference it if
@@ -1100,7 +1120,13 @@ get_memory_rtx (tree exp, tree len)
         && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (exp, 0))))
     exp = TREE_OPERAND (exp, 0);
 
-  if (TREE_CODE (exp) == ADDR_EXPR)
+  off = 0;
+  if (TREE_CODE (exp) == POINTER_PLUS_EXPR
+      && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+      && host_integerp (TREE_OPERAND (exp, 1), 0)
+      && (off = tree_low_cst (TREE_OPERAND (exp, 1), 0)) > 0)
+    exp = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  else if (TREE_CODE (exp) == ADDR_EXPR)
     exp = TREE_OPERAND (exp, 0);
   else if (POINTER_TYPE_P (TREE_TYPE (exp)))
     exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
@@ -1114,6 +1140,9 @@ get_memory_rtx (tree exp, tree len)
     {
       set_mem_attributes (mem, exp, 0);
 
+      if (off)
+       mem = adjust_automodify_address_nv (mem, BLKmode, NULL, off);
+
       /* Allow the string and memory builtins to overflow from one
         field into another, see http://gcc.gnu.org/PR23561.
         Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole
@@ -1430,9 +1459,15 @@ expand_builtin_apply_args (void)
     /* Put the insns after the NOTE that starts the function.
        If this is inside a start_sequence, make the outer-level insn
        chain current, so the code is placed at the start of the
-       function.  */
+       function.  If internal_arg_pointer is a non-virtual pseudo,
+       it needs to be placed after the function that initializes
+       that pseudo.  */
     push_topmost_sequence ();
-    emit_insn_before (seq, NEXT_INSN (entry_of_function ()));
+    if (REG_P (crtl->args.internal_arg_pointer)
+       && REGNO (crtl->args.internal_arg_pointer) > LAST_VIRTUAL_REGISTER)
+      emit_insn_before (seq, parm_birth_insn);
+    else
+      emit_insn_before (seq, NEXT_INSN (entry_of_function ()));
     pop_topmost_sequence ();
     return temp;
   }
@@ -3402,7 +3437,7 @@ expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
    stpcpy.  */
 
 static rtx
-expand_builtin_mempcpy(tree exp, rtx target, enum machine_mode mode)
+expand_builtin_mempcpy (tree exp, rtx target, enum machine_mode mode)
 {
   if (!validate_arglist (exp,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
@@ -3429,15 +3464,18 @@ expand_builtin_mempcpy_args (tree dest, tree src, tree len, tree type,
                             rtx target, enum machine_mode mode, int endp)
 {
     /* If return value is ignored, transform mempcpy into memcpy.  */
-  if (target == const0_rtx)
+  if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_MEMCPY])
     {
       tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+      tree result = build_call_expr (fn, 3, dest, src, len);
 
-      if (!fn)
-       return NULL_RTX;
-
-      return expand_expr (build_call_expr (fn, 3, dest, src, len),
-                         target, mode, EXPAND_NORMAL);
+      while (TREE_CODE (result) == COMPOUND_EXPR)
+       {
+         expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+                      EXPAND_NORMAL);
+         result = TREE_OPERAND (result, 1);
+       }
+      return expand_expr (result, target, mode, EXPAND_NORMAL);
     }
   else
     {
@@ -3703,14 +3741,19 @@ expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode)
   src = CALL_EXPR_ARG (exp, 1);
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
-  if (target == const0_rtx)
+  if (target == const0_rtx && implicit_built_in_decls[BUILT_IN_STRCPY])
     {
       tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
-      if (!fn)
-       return NULL_RTX;
+      tree result = build_call_expr (fn, 2, dst, src);
 
-      return expand_expr (build_call_expr (fn, 2, dst, src),
-                         target, mode, EXPAND_NORMAL);
+      STRIP_NOPS (result);
+      while (TREE_CODE (result) == COMPOUND_EXPR)
+       {
+         expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+                      EXPAND_NORMAL);
+         result = TREE_OPERAND (result, 1);
+       }
+      return expand_expr (result, target, mode, EXPAND_NORMAL);
     }
   else
     {
@@ -4963,6 +5006,9 @@ gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
         Call abort to encourage the user to fix the program.  */
       if (warned)
        inform (input_location, "if this code is reached, the program will abort");
+      /* Before the abort, allow the evaluation of the va_list
+        expression to exit or longjmp.  */
+      gimplify_and_add (valist, pre_p);
       t = build_call_expr (implicit_built_in_decls[BUILT_IN_TRAP], 0);
       gimplify_and_add (t, pre_p);
 
@@ -5988,6 +6034,50 @@ expand_builtin_sync_operation (enum machine_mode mode, tree exp,
   rtx val, mem;
   enum machine_mode old_mode;
 
+  if (code == NOT && warn_sync_nand)
+    {
+      tree fndecl = get_callee_fndecl (exp);
+      enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+      static bool warned_f_a_n, warned_n_a_f;
+
+      switch (fcode)
+       {
+       case BUILT_IN_FETCH_AND_NAND_1:
+       case BUILT_IN_FETCH_AND_NAND_2:
+       case BUILT_IN_FETCH_AND_NAND_4:
+       case BUILT_IN_FETCH_AND_NAND_8:
+       case BUILT_IN_FETCH_AND_NAND_16:
+
+         if (warned_f_a_n)
+           break;
+
+         fndecl = implicit_built_in_decls[BUILT_IN_FETCH_AND_NAND_N];
+         inform (input_location,
+                 "%qD changed semantics in GCC 4.4", fndecl);
+         warned_f_a_n = true;
+         break;
+
+       case BUILT_IN_NAND_AND_FETCH_1:
+       case BUILT_IN_NAND_AND_FETCH_2:
+       case BUILT_IN_NAND_AND_FETCH_4:
+       case BUILT_IN_NAND_AND_FETCH_8:
+       case BUILT_IN_NAND_AND_FETCH_16:
+
+         if (warned_n_a_f)
+           break;
+
+         fndecl = implicit_built_in_decls[BUILT_IN_NAND_AND_FETCH_N];
+         inform (input_location,
+                 "%qD changed semantics in GCC 4.4", fndecl);
+         warned_n_a_f = true;
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+    }
+
   /* Expand the operands.  */
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
 
@@ -7244,7 +7334,9 @@ fold_builtin_expect (tree arg0, tree arg1)
        }
       while (TREE_CODE (inner) == COMPONENT_REF
             || TREE_CODE (inner) == ARRAY_REF);
-      if (DECL_P (inner) && DECL_WEAK (inner))
+      if ((TREE_CODE (inner) == VAR_DECL
+           || TREE_CODE (inner) == FUNCTION_DECL)
+         && DECL_WEAK (inner))
        return NULL_TREE;
     }
 
@@ -7619,8 +7711,13 @@ fold_builtin_sqrt (tree arg, tree type)
          tree arg0 = CALL_EXPR_ARG (arg, 0);
          tree tree_root;
          /* The inner root was either sqrt or cbrt.  */
-         REAL_VALUE_TYPE dconstroot =
-           BUILTIN_SQRT_P (fcode) ? dconsthalf : dconst_third ();
+         /* This was a conditional expression but it triggered a bug
+            in Sun C 5.5.  */
+         REAL_VALUE_TYPE dconstroot;
+         if (BUILTIN_SQRT_P (fcode))
+           dconstroot = dconsthalf;
+         else
+           dconstroot = dconst_third ();
 
          /* Adjust for the outer root.  */
          SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
@@ -7843,7 +7940,7 @@ fold_builtin_sincos (tree arg0, tree arg1, tree arg2)
   call = build_call_expr (fn, 1, arg0);
   call = builtin_save_expr (call);
 
-  return build2 (COMPOUND_EXPR, type,
+  return build2 (COMPOUND_EXPR, void_type_node,
                 build2 (MODIFY_EXPR, void_type_node,
                         build_fold_indirect_ref (arg1),
                         build1 (IMAGPART_EXPR, type, call)),
@@ -8244,21 +8341,6 @@ fold_builtin_bswap (tree fndecl, tree arg)
   return NULL_TREE;
 }
 
-/* Return true if EXPR is the real constant contained in VALUE.  */
-
-static bool
-real_dconstp (tree expr, const REAL_VALUE_TYPE *value)
-{
-  STRIP_NOPS (expr);
-
-  return ((TREE_CODE (expr) == REAL_CST
-          && !TREE_OVERFLOW (expr)
-          && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value))
-         || (TREE_CODE (expr) == COMPLEX_CST
-             && real_dconstp (TREE_REALPART (expr), value)
-             && real_zerop (TREE_IMAGPART (expr))));
-}
-
 /* A subroutine of fold_builtin to fold the various logarithmic
    functions.  Return NULL_TREE if no simplification can me made.
    FUNC is the corresponding MPFR logarithm function.  */
@@ -8273,17 +8355,6 @@ fold_builtin_logarithm (tree fndecl, tree arg,
       tree res;
       const enum built_in_function fcode = builtin_mathfn_code (arg);
 
-      /* Optimize log(e) = 1.0.  We're never passed an exact 'e',
-        instead we'll look for 'e' truncated to MODE.  So only do
-        this if flag_unsafe_math_optimizations is set.  */
-      if (flag_unsafe_math_optimizations && func == mpfr_log)
-        {
-         const REAL_VALUE_TYPE e_truncated =
-           real_value_truncate (TYPE_MODE (type), dconst_e ());
-         if (real_dconstp (arg, &e_truncated))
-           return build_real (type, dconst1);
-       }
-
       /* Calculate the result when the argument is a constant.  */
       if ((res = do_mpfr_arg1 (arg, type, func, &dconst0, NULL, false)))
        return res;
@@ -8780,27 +8851,88 @@ fold_builtin_memory_op (tree dest, tree src, tree len, tree type, bool ignore, i
   else
     {
       tree srctype, desttype;
+      int src_align, dest_align;
+
       if (endp == 3)
        {
-          int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
-          int dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+         src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+         dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
 
          /* 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
-             && (readonly_data_expr (src)
-                 || (host_integerp (len, 1)
-                     && (MIN (src_align, dest_align) / BITS_PER_UNIT >=
-                         tree_low_cst (len, 1)))))
+         if (!dest_align || !src_align)
+           return NULL_TREE;
+         if (readonly_data_expr (src)
+             || (host_integerp (len, 1)
+                 && (MIN (src_align, dest_align) / BITS_PER_UNIT
+                     >= tree_low_cst (len, 1))))
            {
              tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
              if (!fn)
                return NULL_TREE;
               return build_call_expr (fn, 3, dest, src, len);
            }
+
+         /* If *src and *dest can't overlap, optimize into memcpy as well.  */
+         srcvar = build_fold_indirect_ref (src);
+         destvar = build_fold_indirect_ref (dest);
+         if (srcvar
+             && !TREE_THIS_VOLATILE (srcvar)
+             && destvar
+             && !TREE_THIS_VOLATILE (destvar))
+           {
+             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;
+
+             src_base = srcvar;
+             if (handled_component_p (src_base))
+               src_base = get_ref_base_and_extent (src_base, &src_offset,
+                                                   &size, &maxsize);
+             dest_base = destvar;
+             if (handled_component_p (dest_base))
+               dest_base = get_ref_base_and_extent (dest_base, &dest_offset,
+                                                    &size, &maxsize);
+             if (host_integerp (len, 1))
+               {
+                 maxsize = tree_low_cst (len, 1);
+                 if (maxsize
+                     > INTTYPE_MAXIMUM (HOST_WIDE_INT) / BITS_PER_UNIT)
+                   maxsize = -1;
+                 else
+                   maxsize *= BITS_PER_UNIT;
+               }
+             else
+               maxsize = -1;
+             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) == INDIRECT_REF
+                      && TREE_CODE (dest_base) == INDIRECT_REF)
+               {
+                 if (! operand_equal_p (TREE_OPERAND (src_base, 0),
+                                        TREE_OPERAND (dest_base, 0), 0)
+                     || ranges_overlap_p (src_offset, maxsize,
+                                          dest_offset, maxsize))
+                   return NULL_TREE;
+               }
+             else
+               return NULL_TREE;
+
+             fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+             if (!fn)
+               return NULL_TREE;
+             return build_call_expr (fn, 3, dest, src, len);
+           }
          return NULL_TREE;
        }
 
@@ -8819,44 +8951,97 @@ fold_builtin_memory_op (tree dest, tree src, tree len, tree type, bool ignore, i
          || !TYPE_SIZE_UNIT (desttype)
          || TREE_CODE (TYPE_SIZE_UNIT (srctype)) != INTEGER_CST
          || TREE_CODE (TYPE_SIZE_UNIT (desttype)) != INTEGER_CST
-         || !tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len)
-         || !tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+         || TYPE_VOLATILE (srctype)
+         || TYPE_VOLATILE (desttype))
        return NULL_TREE;
 
-      if (get_pointer_alignment (dest, BIGGEST_ALIGNMENT) 
-         < (int) TYPE_ALIGN (desttype)
-         || (get_pointer_alignment (src, BIGGEST_ALIGNMENT) 
-             < (int) TYPE_ALIGN (srctype)))
+      src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+      dest_align = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+      if (dest_align < (int) TYPE_ALIGN (desttype)
+         || src_align < (int) TYPE_ALIGN (srctype))
        return NULL_TREE;
 
       if (!ignore)
         dest = builtin_save_expr (dest);
 
-      srcvar = build_fold_indirect_ref (src);
-      if (TREE_THIS_VOLATILE (srcvar))
-       return NULL_TREE;
-      if (!tree_int_cst_equal (lang_hooks.expr_size (srcvar), len))
-       return NULL_TREE;
-      /* With memcpy, it is possible to bypass aliasing rules, so without
-         this check i.e. execute/20060930-2.c would be misoptimized, because
-        it use conflicting alias set to hold argument for the memcpy call.
-        This check is probably unnecessary with -fno-strict-aliasing.
-        Similarly for destvar.  See also PR29286.  */
-      if (!var_decl_component_p (srcvar)
-         /* Accept: memcpy (*char_var, "test", 1); that simplify
-            to char_var='t';  */
-         || is_gimple_min_invariant (srcvar)
-         || readonly_data_expr (src))
-       return NULL_TREE;
+      srcvar = NULL_TREE;
+      if (tree_int_cst_equal (TYPE_SIZE_UNIT (srctype), len))
+       {
+         srcvar = build_fold_indirect_ref (src);
+         if (TREE_THIS_VOLATILE (srcvar))
+           return NULL_TREE;
+         else if (!tree_int_cst_equal (lang_hooks.expr_size (srcvar), len))
+           srcvar = NULL_TREE;
+         /* With memcpy, it is possible to bypass aliasing rules, so without
+            this check i.e. execute/20060930-2.c would be misoptimized,
+            because it use conflicting alias set to hold argument for the
+            memcpy call.  This check is probably unnecessary with
+            -fno-strict-aliasing.  Similarly for destvar.  See also
+            PR29286.  */
+         else if (!var_decl_component_p (srcvar))
+           srcvar = NULL_TREE;
+       }
+
+      destvar = NULL_TREE;
+      if (tree_int_cst_equal (TYPE_SIZE_UNIT (desttype), len))
+       {
+         destvar = build_fold_indirect_ref (dest);
+         if (TREE_THIS_VOLATILE (destvar))
+           return NULL_TREE;
+         else if (!tree_int_cst_equal (lang_hooks.expr_size (destvar), len))
+           destvar = NULL_TREE;
+         else if (!var_decl_component_p (destvar))
+           destvar = NULL_TREE;
+       }
 
-      destvar = build_fold_indirect_ref (dest);
-      if (TREE_THIS_VOLATILE (destvar))
-       return NULL_TREE;
-      if (!tree_int_cst_equal (lang_hooks.expr_size (destvar), len))
-       return NULL_TREE;
-      if (!var_decl_component_p (destvar))
+      if (srcvar == NULL_TREE && destvar == NULL_TREE)
        return NULL_TREE;
 
+      if (srcvar == NULL_TREE)
+       {
+         tree srcptype;
+         if (TREE_ADDRESSABLE (TREE_TYPE (destvar)))
+           return NULL_TREE;
+
+         srctype = build_qualified_type (desttype, 0);
+         if (src_align < (int) TYPE_ALIGN (srctype))
+           {
+             if (AGGREGATE_TYPE_P (srctype)
+                 || SLOW_UNALIGNED_ACCESS (TYPE_MODE (srctype), src_align))
+               return NULL_TREE;
+
+             srctype = build_variant_type_copy (srctype);
+             TYPE_ALIGN (srctype) = src_align;
+             TYPE_USER_ALIGN (srctype) = 1;
+             TYPE_PACKED (srctype) = 1;
+           }
+         srcptype = build_pointer_type_for_mode (srctype, ptr_mode, true);
+         src = fold_convert (srcptype, src);
+         srcvar = build_fold_indirect_ref (src);
+       }
+      else if (destvar == NULL_TREE)
+       {
+         tree destptype;
+         if (TREE_ADDRESSABLE (TREE_TYPE (srcvar)))
+           return NULL_TREE;
+
+         desttype = build_qualified_type (srctype, 0);
+         if (dest_align < (int) TYPE_ALIGN (desttype))
+           {
+             if (AGGREGATE_TYPE_P (desttype)
+                 || SLOW_UNALIGNED_ACCESS (TYPE_MODE (desttype), dest_align))
+               return NULL_TREE;
+
+             desttype = build_variant_type_copy (desttype);
+             TYPE_ALIGN (desttype) = dest_align;
+             TYPE_USER_ALIGN (desttype) = 1;
+             TYPE_PACKED (desttype) = 1;
+           }
+         destptype = build_pointer_type_for_mode (desttype, ptr_mode, true);
+         dest = fold_convert (destptype, dest);
+         destvar = build_fold_indirect_ref (dest);
+       }
+
       if (srctype == desttype
          || (gimple_in_ssa_p (cfun)
              && useless_type_conversion_p (desttype, srctype)))
@@ -8884,6 +9069,7 @@ fold_builtin_memory_op (tree dest, tree src, tree len, tree type, bool ignore, i
     len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len,
                       ssize_int (1));
 
+  len = fold_convert (sizetype, len);
   dest = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (dest), dest, len);
   dest = fold_convert (type, dest);
   if (expr)
@@ -10362,6 +10548,17 @@ fold_builtin_2 (tree fndecl, tree arg0, tree arg1, bool ignore)
     case BUILT_IN_STRCPY:
       return fold_builtin_strcpy (fndecl, arg0, arg1, NULL_TREE);
 
+    case BUILT_IN_STPCPY:
+      if (ignore)
+       {
+         tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+         if (!fn)
+           break;
+
+         return build_call_expr (fn, 2, arg0, arg1);
+       }
+      break;
+
     case BUILT_IN_STRCMP:
       return fold_builtin_strcmp (arg0, arg1);
 
@@ -10665,6 +10862,22 @@ fold_builtin_varargs (tree fndecl, tree exp, bool ignore ATTRIBUTE_UNUSED)
   return NULL_TREE;
 }
 
+/* Return true if FNDECL shouldn't be folded right now.
+   If a built-in function has an inline attribute always_inline
+   wrapper, defer folding it after always_inline functions have
+   been inlined, otherwise e.g. -D_FORTIFY_SOURCE checking
+   might not be performed.  */
+
+static bool
+avoid_folding_inline_builtin (tree fndecl)
+{
+  return (DECL_DECLARED_INLINE_P (fndecl)
+         && DECL_DISREGARD_INLINE_LIMITS (fndecl)
+         && cfun
+         && !cfun->always_inline_functions_inlined
+         && lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)));
+}
+
 /* A wrapper function for builtin folding that prevents warnings for
    "statement without effect" and the like, caused by removing the
    call node earlier than the warning is generated.  */
@@ -10697,6 +10910,9 @@ fold_call_expr (tree exp, bool ignore)
            return NULL_TREE;
        }
 
+      if (avoid_folding_inline_builtin (fndecl))
+       return NULL_TREE;
+
       /* FIXME: Don't use a list in this interface.  */
       if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
          return targetm.fold_builtin (fndecl, CALL_EXPR_ARGS (exp), ignore);
@@ -10723,7 +10939,6 @@ fold_call_expr (tree exp, bool ignore)
                  if (CAN_HAVE_LOCATION_P (realret)
                      && !EXPR_HAS_LOCATION (realret))
                    SET_EXPR_LOCATION (realret, EXPR_LOCATION (exp));
-                 return realret;
                }
              return ret;
            }
@@ -10799,6 +11014,8 @@ fold_builtin_call_array (tree type,
                && DECL_FUNCTION_CODE (fndecl2) == BUILT_IN_VA_ARG_PACK)
              return build_call_array (type, fn, n, argarray);
          }
+       if (avoid_folding_inline_builtin (fndecl))
+         return build_call_array (type, fn, n, argarray);
         if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
           {
             tree arglist = NULL_TREE;
@@ -10807,6 +11024,7 @@ fold_builtin_call_array (tree type,
             ret = targetm.fold_builtin (fndecl, arglist, false);
             if (ret)
               return ret;
+           return build_call_array (type, fn, n, argarray);
           }
         else if (n <= MAX_ARGS_TO_FOLD_BUILTIN)
           {
@@ -11398,7 +11616,7 @@ fold_builtin_strspn (tree s1, tree s2)
       if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
        /* Evaluate and ignore both arguments in case either one has
           side-effects.  */
-       return omit_two_operands (integer_type_node, integer_zero_node,
+       return omit_two_operands (size_type_node, size_zero_node,
                                  s1, s2);
       return NULL_TREE;
     }
@@ -11444,8 +11662,8 @@ fold_builtin_strcspn (tree s1, tree s2)
        {
          /* Evaluate and ignore argument s2 in case it has
             side-effects.  */
-         return omit_one_operand (integer_type_node,
-                                  integer_zero_node, s2);
+         return omit_one_operand (size_type_node,
+                                  size_zero_node, s2);
        }
 
       /* If the second argument is "", return __builtin_strlen(s1).  */
@@ -11786,8 +12004,9 @@ expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode,
 
       if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
        {
-         warning (0, "%Kcall to %D will always overflow destination buffer",
-                  exp, get_callee_fndecl (exp));
+         warning_at (tree_nonartificial_location (exp),
+                     0, "%Kcall to %D will always overflow destination buffer",
+                     exp, get_callee_fndecl (exp));
          return NULL_RTX;
        }
 
@@ -11894,6 +12113,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
 {
   int is_strlen = 0;
   tree len, size;
+  location_t loc = tree_nonartificial_location (exp);
 
   switch (fcode)
     {
@@ -11940,8 +12160,8 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       src = c_strlen (src, 1);
       if (! src || ! host_integerp (src, 1))
        {
-         warning (0, "%Kcall to %D might overflow destination buffer",
-                  exp, get_callee_fndecl (exp));
+         warning_at (loc, 0, "%Kcall to %D might overflow destination buffer",
+                     exp, get_callee_fndecl (exp));
          return;
        }
       else if (tree_int_cst_lt (src, size))
@@ -11950,8 +12170,8 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len))
     return;
 
-  warning (0, "%Kcall to %D will always overflow destination buffer",
-          exp, get_callee_fndecl (exp));
+  warning_at (loc, 0, "%Kcall to %D will always overflow destination buffer",
+             exp, get_callee_fndecl (exp));
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -12008,10 +12228,9 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
     return;
 
   if (! tree_int_cst_lt (len, size))
-    {
-      warning (0, "%Kcall to %D will always overflow destination buffer",
-              exp, get_callee_fndecl (exp));
-    }
+    warning_at (tree_nonartificial_location (exp),
+               0, "%Kcall to %D will always overflow destination buffer",
+               exp, get_callee_fndecl (exp));
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -12030,9 +12249,11 @@ maybe_emit_free_warning (tree exp)
     return;
 
   if (SSA_VAR_P (arg))
-    warning (0, "%Kattempt to free a non-heap object %qD", exp, arg);
+    warning_at (tree_nonartificial_location (exp),
+               0, "%Kattempt to free a non-heap object %qD", exp, arg);
   else
-    warning (0, "%Kattempt to free a non-heap object", exp);
+    warning_at (tree_nonartificial_location (exp),
+               0, "%Kattempt to free a non-heap object", exp);
 }
 
 /* Fold a call to __builtin_object_size with arguments PTR and OST,
@@ -13515,6 +13736,8 @@ fold_call_stmt (gimple stmt, bool ignore)
     {
       int nargs = gimple_call_num_args (stmt);
 
+      if (avoid_folding_inline_builtin (fndecl))
+       return NULL_TREE;
       /* FIXME: Don't use a list in this interface.  */
       if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
         {