re PR lto/79061 ([LTO][ASAN] LTO plus ASAN fails with "AddressSanitizer: initializati...
[gcc.git] / gcc / builtins.c
index 750488e7f735eb81b5df52266c876ccfc255dc46..bf68e317124f3ae3aa033142d9ae813e78e9ef98 100644 (file)
@@ -1,5 +1,5 @@
 /* Expand builtin functions.
-   Copyright (C) 1988-2015 Free Software Foundation, Inc.
+   Copyright (C) 1988-2017 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -25,52 +25,49 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
-#include "predict.h"
+#include "target.h"
+#include "rtl.h"
 #include "tree.h"
+#include "memmodel.h"
 #include "gimple.h"
-#include "rtl.h"
+#include "predict.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "tree-vrp.h"
+#include "tree-ssanames.h"
+#include "expmed.h"
+#include "optabs.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "diagnostic-core.h"
 #include "alias.h"
 #include "fold-const.h"
-#include "stringpool.h"
+#include "fold-const-call.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
 #include "tree-object-size.h"
 #include "realmpfr.h"
 #include "cfgrtl.h"
-#include "internal-fn.h"
-#include "flags.h"
-#include "regs.h"
 #include "except.h"
-#include "insn-config.h"
-#include "expmed.h"
 #include "dojump.h"
 #include "explow.h"
-#include "emit-rtl.h"
 #include "stmt.h"
 #include "expr.h"
-#include "insn-codes.h"
-#include "optabs.h"
 #include "libfuncs.h"
-#include "recog.h"
 #include "output.h"
 #include "typeclass.h"
-#include "tm_p.h"
-#include "target.h"
 #include "langhooks.h"
-#include "tree-ssanames.h"
-#include "tree-dfa.h"
 #include "value-prof.h"
-#include "diagnostic-core.h"
 #include "builtins.h"
 #include "asan.h"
 #include "cilk.h"
-#include "cgraph.h"
 #include "tree-chkp.h"
 #include "rtl-chkp.h"
-
-
-static tree do_mpc_arg1 (tree, tree, int (*)(mpc_ptr, mpc_srcptr, mpc_rnd_t));
+#include "internal-fn.h"
+#include "case-cfn-macros.h"
+#include "gimple-fold.h"
+#include "intl.h"
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -86,7 +83,6 @@ const char * built_in_names[(int) END_BUILTINS] =
 {
 #include "builtins.def"
 };
-#undef DEF_BUILTIN
 
 /* Setup an array of builtin_info_type, make sure each element decl is
    initialized to NULL_TREE.  */
@@ -108,9 +104,6 @@ static rtx expand_builtin_apply (rtx, rtx, rtx);
 static void expand_builtin_return (rtx);
 static enum type_class type_to_class (tree);
 static rtx expand_builtin_classify_type (tree);
-static void expand_errno_check (tree, rtx);
-static rtx expand_builtin_mathfn (tree, rtx, rtx);
-static rtx expand_builtin_mathfn_2 (tree, rtx, rtx);
 static rtx expand_builtin_mathfn_3 (tree, rtx, rtx);
 static rtx expand_builtin_mathfn_ternary (tree, rtx, rtx);
 static rtx expand_builtin_interclass_mathfn (tree, rtx);
@@ -132,9 +125,11 @@ static rtx expand_builtin_mempcpy (tree, rtx, machine_mode);
 static rtx expand_builtin_mempcpy_with_bounds (tree, rtx, machine_mode);
 static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx,
                                        machine_mode, int, tree);
+static rtx expand_builtin_strcat (tree, rtx);
 static rtx expand_builtin_strcpy (tree, rtx);
 static rtx expand_builtin_strcpy_args (tree, tree, rtx);
 static rtx expand_builtin_stpcpy (tree, rtx, machine_mode);
+static rtx expand_builtin_strncat (tree, rtx);
 static rtx expand_builtin_strncpy (tree, rtx);
 static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, machine_mode);
 static rtx expand_builtin_memset (tree, rtx, machine_mode);
@@ -151,31 +146,11 @@ static tree fold_builtin_constant_p (tree);
 static tree fold_builtin_classify_type (tree);
 static tree fold_builtin_strlen (location_t, tree, tree);
 static tree fold_builtin_inf (location_t, tree, int);
-static tree fold_builtin_nan (tree, tree, int);
 static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 static bool validate_arg (const_tree, enum tree_code code);
-static bool integer_valued_real_p (tree);
-static tree fold_trunc_transparent_mathfn (location_t, tree, tree);
 static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_signbit (tree, rtx);
-static tree fold_builtin_pow (location_t, tree, tree, tree, tree);
-static tree fold_builtin_powi (location_t, tree, tree, tree, tree);
-static tree fold_builtin_cos (location_t, tree, tree, tree);
-static tree fold_builtin_cosh (location_t, tree, tree, tree);
-static tree fold_builtin_tan (tree, tree);
-static tree fold_builtin_trunc (location_t, tree, tree);
-static tree fold_builtin_floor (location_t, tree, tree);
-static tree fold_builtin_ceil (location_t, tree, tree);
-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_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);
-static tree fold_builtin_strcmp (location_t, tree, tree);
-static tree fold_builtin_strncmp (location_t, tree, tree, tree);
-static tree fold_builtin_signbit (location_t, tree, tree);
-static tree fold_builtin_copysign (location_t, tree, tree, tree, tree);
 static tree fold_builtin_isascii (location_t, tree);
 static tree fold_builtin_toascii (location_t, tree);
 static tree fold_builtin_isdigit (location_t, tree);
@@ -190,8 +165,6 @@ static tree fold_builtin_3 (location_t, tree, tree, tree, tree);
 static tree fold_builtin_varargs (location_t, tree, tree*, int);
 
 static tree fold_builtin_strpbrk (location_t, tree, tree, tree);
-static tree fold_builtin_strstr (location_t, tree, tree, tree);
-static tree fold_builtin_strrchr (location_t, tree, tree, tree);
 static tree fold_builtin_strspn (location_t, tree, tree);
 static tree fold_builtin_strcspn (location_t, tree, tree);
 
@@ -210,16 +183,6 @@ static unsigned HOST_WIDE_INT target_s;
 char target_percent_c[3];
 char target_percent_s[3];
 char target_percent_s_newline[4];
-static tree do_mpfr_arg1 (tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mp_rnd_t),
-                         const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *, bool);
-static tree do_mpfr_arg2 (tree, tree, tree,
-                         int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t));
-static tree do_mpfr_arg3 (tree, tree, tree, tree,
-                         int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t));
-static tree do_mpfr_sincos (tree, tree, tree);
-static tree do_mpfr_bessel_n (tree, tree, tree,
-                             int (*)(mpfr_ptr, long, mpfr_srcptr, mp_rnd_t),
-                             const REAL_VALUE_TYPE *, bool);
 static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 static void expand_builtin_sync_synchronize (void);
@@ -255,7 +218,7 @@ is_builtin_fn (tree decl)
    of the optimization level.  This means whenever a function is invoked with
    its "internal" name, which normally contains the prefix "__builtin".  */
 
-static bool
+bool
 called_as_built_in (tree node)
 {
   /* Note that we must use DECL_NAME, not DECL_ASSEMBLER_NAME_SET_P since
@@ -287,14 +250,14 @@ get_object_alignment_2 (tree exp, unsigned int *alignp,
   HOST_WIDE_INT bitsize, bitpos;
   tree offset;
   machine_mode mode;
-  int unsignedp, volatilep;
+  int unsignedp, reversep, volatilep;
   unsigned int align = BITS_PER_UNIT;
   bool known_alignment = false;
 
   /* Get the innermost object and the constant (bitpos) and possibly
      variable (offset) offset of the access.  */
-  exp = get_inner_reference (exp, &bitsize, &bitpos, &offset,
-                            &mode, &unsignedp, &volatilep, true);
+  exp = get_inner_reference (exp, &bitsize, &bitpos, &offset, &mode,
+                            &unsignedp, &reversep, &volatilep);
 
   /* Extract alignment information from the innermost object and
      possibly adjust bitpos and offset.  */
@@ -324,10 +287,6 @@ get_object_alignment_2 (tree exp, unsigned int *alignp,
       align = DECL_ALIGN (exp);
       known_alignment = true;
     }
-  else if (TREE_CODE (exp) == VIEW_CONVERT_EXPR)
-    {
-      align = TYPE_ALIGN (TREE_TYPE (exp));
-    }
   else if (TREE_CODE (exp) == INDIRECT_REF
           || TREE_CODE (exp) == MEM_REF
           || TREE_CODE (exp) == TARGET_MEM_REF)
@@ -343,7 +302,7 @@ get_object_alignment_2 (tree exp, unsigned int *alignp,
        {
          ptr_bitmask = TREE_INT_CST_LOW (TREE_OPERAND (addr, 1));
          ptr_bitmask *= BITS_PER_UNIT;
-         align = ptr_bitmask & -ptr_bitmask;
+         align = least_bit_hwi (ptr_bitmask);
          addr = TREE_OPERAND (addr, 0);
        }
 
@@ -363,7 +322,7 @@ get_object_alignment_2 (tree exp, unsigned int *alignp,
              unsigned HOST_WIDE_INT step = 1;
              if (TMR_STEP (exp))
                step = TREE_INT_CST_LOW (TMR_STEP (exp));
-             align = MIN (align, (step & -step) * BITS_PER_UNIT);
+             align = MIN (align, least_bit_hwi (step) * BITS_PER_UNIT);
            }
          if (TMR_INDEX2 (exp))
            align = BITS_PER_UNIT;
@@ -442,7 +401,7 @@ get_object_alignment (tree exp)
      ptr & (align - 1) == bitpos.  */
 
   if (bitpos != 0)
-    align = (bitpos & -bitpos);
+    align = least_bit_hwi (bitpos);
   return align;
 }
 
@@ -494,8 +453,12 @@ get_pointer_alignment_1 (tree exp, unsigned int *alignp,
        {
          *bitposp = ptr_misalign * BITS_PER_UNIT;
          *alignp = ptr_align * BITS_PER_UNIT;
+         /* Make sure to return a sensible alignment when the multiplication
+            by BITS_PER_UNIT overflowed.  */
+         if (*alignp == 0)
+           *alignp = 1u << (HOST_BITS_PER_INT - 1);
          /* We cannot really tell whether this result is an approximation.  */
-         return true;
+         return false;
        }
       else
        {
@@ -536,14 +499,49 @@ get_pointer_alignment (tree exp)
      ptr & (align - 1) == bitpos.  */
 
   if (bitpos != 0)
-    align = (bitpos & -bitpos);
+    align = least_bit_hwi (bitpos);
 
   return align;
 }
 
-/* Compute the length of a C string.  TREE_STRING_LENGTH is not the right
-   way, because it could contain a zero byte in the middle.
-   TREE_STRING_LENGTH is the size of the character array, not the string.
+/* Return the number of non-zero elements in the sequence
+   [ PTR, PTR + MAXELTS ) where each element's size is ELTSIZE bytes.
+   ELTSIZE must be a power of 2 less than 8.  Used by c_strlen.  */
+
+static unsigned
+string_length (const void *ptr, unsigned eltsize, unsigned maxelts)
+{
+  gcc_checking_assert (eltsize == 1 || eltsize == 2 || eltsize == 4);
+
+  unsigned n;
+
+  if (eltsize == 1)
+    {
+      /* Optimize the common case of plain char.  */
+      for (n = 0; n < maxelts; n++)
+       {
+         const char *elt = (const char*) ptr + n;
+         if (!*elt)
+           break;
+       }
+    }
+  else
+    {
+      for (n = 0; n < maxelts; n++)
+       {
+         const char *elt = (const char*) ptr + n * eltsize;
+         if (!memcmp (elt, "\0\0\0\0", eltsize))
+           break;
+       }
+    }
+  return n;
+}
+
+/* Compute the length of a null-terminated character string or wide
+   character string handling character sizes of 1, 2, and 4 bytes.
+   TREE_STRING_LENGTH is not the right way because it evaluates to
+   the size of the character array in bytes (as opposed to characters)
+   and because it can contain a zero byte in the middle.
 
    ONLY_VALUE should be nonzero if the result is not going to be emitted
    into the instruction stream and zero if it is going to be expanded.
@@ -564,12 +562,6 @@ get_pointer_alignment (tree exp)
 tree
 c_strlen (tree src, int only_value)
 {
-  tree offset_node;
-  HOST_WIDE_INT offset;
-  int max;
-  const char *ptr;
-  location_t loc;
-
   STRIP_NOPS (src);
   if (TREE_CODE (src) == COND_EXPR
       && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
@@ -586,25 +578,36 @@ c_strlen (tree src, int only_value)
       && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
     return c_strlen (TREE_OPERAND (src, 1), only_value);
 
-  loc = EXPR_LOC_OR_LOC (src, input_location);
+  location_t loc = EXPR_LOC_OR_LOC (src, input_location);
 
-  src = string_constant (src, &offset_node);
+  /* Offset from the beginning of the string in bytes.  */
+  tree byteoff;
+  src = string_constant (src, &byteoff);
   if (src == 0)
     return NULL_TREE;
 
-  max = TREE_STRING_LENGTH (src) - 1;
-  ptr = TREE_STRING_POINTER (src);
+  /* Determine the size of the string element.  */
+  unsigned eltsize
+    = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (src))));
+
+  /* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible
+     length of SRC.  */
+  unsigned maxelts = TREE_STRING_LENGTH (src) / eltsize - 1;
 
-  if (offset_node && TREE_CODE (offset_node) != INTEGER_CST)
+  /* PTR can point to the byte representation of any string type, including
+     char* and wchar_t*.  */
+  const char *ptr = TREE_STRING_POINTER (src);
+
+  if (byteoff && TREE_CODE (byteoff) != INTEGER_CST)
     {
       /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't
         compute the offset to the following null if we don't know where to
         start searching for it.  */
-      int i;
-
-      for (i = 0; i < max; i++)
-       if (ptr[i] == 0)
+      if (string_length (ptr, eltsize, maxelts) < maxelts)
+       {
+         /* Return when an embedded null character is found.  */
          return NULL_TREE;
+       }
 
       /* We don't know the starting offset, but we do know that the string
         has no internal zero bytes.  We can assume that the offset falls
@@ -613,27 +616,31 @@ c_strlen (tree src, int only_value)
         and return that.  This would perhaps not be valid if we were dealing
         with named arrays in addition to literal string constants.  */
 
-      return size_diffop_loc (loc, size_int (max), offset_node);
+      return size_diffop_loc (loc, size_int (maxelts * eltsize), byteoff);
     }
 
+  /* Offset from the beginning of the string in elements.  */
+  HOST_WIDE_INT eltoff;
+
   /* We have a known offset into the string.  Start searching there for
      a null character if we can represent it as a single HOST_WIDE_INT.  */
-  if (offset_node == 0)
-    offset = 0;
-  else if (! tree_fits_shwi_p (offset_node))
-    offset = -1;
+  if (byteoff == 0)
+    eltoff = 0;
+  else if (! tree_fits_shwi_p (byteoff))
+    eltoff = -1;
   else
-    offset = tree_to_shwi (offset_node);
+    eltoff = tree_to_shwi (byteoff) / eltsize;
 
   /* If the offset is known to be out of bounds, warn, and call strlen at
      runtime.  */
-  if (offset < 0 || offset > max)
+  if (eltoff < 0 || eltoff > maxelts)
     {
      /* Suppress multiple warnings for propagated constant strings.  */
       if (only_value != 2
          && !TREE_NO_WARNING (src))
         {
-          warning_at (loc, 0, "offset outside bounds of constant string");
+         warning_at (loc, 0, "offset %qwi outside bounds of constant string",
+                     eltoff);
           TREE_NO_WARNING (src) = 1;
         }
       return NULL_TREE;
@@ -643,30 +650,12 @@ c_strlen (tree src, int only_value)
      constructed with build_string will have nulls appended, we win even
      if we get handed something like (char[4])"abcd".
 
-     Since OFFSET is our starting index into the string, no further
+     Since ELTOFF is our starting index into the string, no further
      calculation is needed.  */
-  return ssize_int (strlen (ptr + offset));
-}
-
-/* Return a char pointer for a C string if it is a string constant
-   or sum of string constant and integer constant.  */
-
-const char *
-c_getstr (tree src)
-{
-  tree offset_node;
-
-  src = string_constant (src, &offset_node);
-  if (src == 0)
-    return 0;
-
-  if (offset_node == 0)
-    return TREE_STRING_POINTER (src);
-  else if (!tree_fits_uhwi_p (offset_node)
-          || compare_tree_int (offset_node, TREE_STRING_LENGTH (src) - 1) > 0)
-    return 0;
+  unsigned len = string_length (ptr + eltoff * eltsize, eltsize,
+                               maxelts - eltoff);
 
-  return TREE_STRING_POINTER (src) + tree_to_uhwi (offset_node);
+  return ssize_int (len);
 }
 
 /* Return a constant integer corresponding to target reading
@@ -724,11 +713,11 @@ target_char_cast (tree cst, char *p)
   val = TREE_INT_CST_LOW (cst);
 
   if (CHAR_TYPE_SIZE < HOST_BITS_PER_WIDE_INT)
-    val &= (((unsigned HOST_WIDE_INT) 1) << CHAR_TYPE_SIZE) - 1;
+    val &= (HOST_WIDE_INT_1U << CHAR_TYPE_SIZE) - 1;
 
   hostval = val;
   if (HOST_BITS_PER_CHAR < HOST_BITS_PER_WIDE_INT)
-    hostval &= (((unsigned HOST_WIDE_INT) 1) << HOST_BITS_PER_CHAR) - 1;
+    hostval &= (HOST_WIDE_INT_1U << HOST_BITS_PER_CHAR) - 1;
 
   if (val != hostval)
     return 1;
@@ -747,7 +736,7 @@ builtin_save_expr (tree exp)
   if (TREE_CODE (exp) == SSA_NAME
       || (TREE_ADDRESSABLE (exp) == 0
          && (TREE_CODE (exp) == PARM_DECL
-             || (TREE_CODE (exp) == VAR_DECL && !TREE_STATIC (exp)))))
+             || (VAR_P (exp) && !TREE_STATIC (exp)))))
     return exp;
 
   return save_expr (exp);
@@ -808,11 +797,7 @@ expand_builtin_return_addr (enum built_in_function fndecl_code, int count)
   /* For __builtin_frame_address, return what we've got.  But, on
      the SPARC for example, we may have to add a bias.  */
   if (fndecl_code == BUILT_IN_FRAME_ADDRESS)
-#ifdef FRAME_ADDR_RTX
     return FRAME_ADDR_RTX (tem);
-#else
-    return tem;
-#endif
 
   /* For __builtin_return_address, get the return address from that frame.  */
 #ifdef RETURN_ADDR_RTX
@@ -919,10 +904,8 @@ expand_builtin_setjmp_receiver (rtx receiver_label)
       emit_clobber (hard_frame_pointer_rtx);
     }
 
-#if !HARD_FRAME_POINTER_IS_ARG_POINTER
-  if (fixed_regs[ARG_POINTER_REGNUM])
+  if (!HARD_FRAME_POINTER_IS_ARG_POINTER && fixed_regs[ARG_POINTER_REGNUM])
     {
-#ifdef ELIMINABLE_REGS
       /* If the argument pointer can be eliminated in favor of the
         frame pointer, we don't need to restore it.  We assume here
         that if such an elimination is present, it can always be used.
@@ -937,7 +920,6 @@ expand_builtin_setjmp_receiver (rtx receiver_label)
          break;
 
       if (i == ARRAY_SIZE (elim_regs))
-#endif
        {
          /* Now restore our arg pointer from the address at which it
             was saved in our stack frame.  */
@@ -945,7 +927,6 @@ expand_builtin_setjmp_receiver (rtx receiver_label)
                          copy_to_reg (get_arg_pointer_save_area ()));
        }
     }
-#endif
 
   if (receiver_label != NULL && targetm.have_builtin_setjmp_receiver ())
     emit_insn (targetm.gen_builtin_setjmp_receiver (receiver_label));
@@ -1053,7 +1034,7 @@ more_const_call_expr_args_p (const const_call_expr_arg_iterator *iter)
 
 /* This function validates the types of a function call argument list
    against a specified list of tree_codes.  If the last specifier is a 0,
-   that represents an ellipses, otherwise the last specifier must be a
+   that represents an ellipsis, otherwise the last specifier must be a
    VOID_TYPE.  */
 
 static bool
@@ -1068,9 +1049,14 @@ validate_arglist (const_tree callexpr, ...)
   va_start (ap, callexpr);
   init_const_call_expr_arg_iterator (callexpr, &iter);
 
-  do
+  /* Get a bitmap of pointer argument numbers declared attribute nonnull.  */
+  tree fn = CALL_EXPR_FN (callexpr);
+  bitmap argmap = get_nonnull_args (TREE_TYPE (TREE_TYPE (fn)));
+
+  for (unsigned argno = 1; ; ++argno)
     {
       code = (enum tree_code) va_arg (ap, int);
+
       switch (code)
        {
        case 0:
@@ -1082,6 +1068,19 @@ validate_arglist (const_tree callexpr, ...)
             true, otherwise return false.  */
          res = !more_const_call_expr_args_p (&iter);
          goto end;
+       case POINTER_TYPE:
+         /* The actual argument must be nonnull when either the whole
+            called function has been declared nonnull, or when the formal
+            argument corresponding to the actual argument has been.  */
+         if (argmap
+             && (bitmap_empty_p (argmap) || bitmap_bit_p (argmap, argno)))
+           {
+             arg = next_const_call_expr_arg (&iter);
+             if (!validate_arg (arg, code) || integer_zerop (arg))
+               goto end;
+             break;
+           }
+         /* FALLTHRU */
        default:
          /* If no parameters remain or the parameter's code does not
             match the specified code, return false.  Otherwise continue
@@ -1092,13 +1091,14 @@ validate_arglist (const_tree callexpr, ...)
          break;
        }
     }
-  while (1);
 
   /* We need gotos here since we can only have one VA_CLOSE in a
      function.  */
  end: ;
   va_end (ap);
 
+  BITMAP_FREE (argmap);
+
   return res;
 }
 
@@ -1160,8 +1160,8 @@ expand_builtin_nonlocal_goto (tree exp)
         to targets with a nonlocal_goto pattern; they are free
         to implement it in their own way.  Note also that this is
         a no-op if the GP register is a global invariant.)  */
-      if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
-         && fixed_regs[PIC_OFFSET_TABLE_REGNUM])
+      unsigned regnum = PIC_OFFSET_TABLE_REGNUM;
+      if (regnum != INVALID_REGNUM && fixed_regs[regnum])
        emit_use (pic_offset_table_rtx);
 
       emit_indirect_jump (r_label);
@@ -1812,127 +1812,142 @@ expand_builtin_classify_type (tree exp)
    determines which among a set of three builtin math functions is
    appropriate for a given type mode.  The `F' and `L' cases are
    automatically generated from the `double' case.  */
-#define CASE_MATHFN(BUILT_IN_MATHFN) \
-  case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \
-  fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \
-  fcodel = BUILT_IN_MATHFN##L ; break;
+#define CASE_MATHFN(MATHFN) \
+  CASE_CFN_##MATHFN: \
+  fcode = BUILT_IN_##MATHFN; fcodef = BUILT_IN_##MATHFN##F ; \
+  fcodel = BUILT_IN_##MATHFN##L ; break;
 /* Similar to above, but appends _R after any F/L suffix.  */
-#define CASE_MATHFN_REENT(BUILT_IN_MATHFN) \
-  case BUILT_IN_MATHFN##_R: case BUILT_IN_MATHFN##F_R: case BUILT_IN_MATHFN##L_R: \
-  fcode = BUILT_IN_MATHFN##_R; fcodef = BUILT_IN_MATHFN##F_R ; \
-  fcodel = BUILT_IN_MATHFN##L_R ; break;
+#define CASE_MATHFN_REENT(MATHFN) \
+  case CFN_BUILT_IN_##MATHFN##_R: \
+  case CFN_BUILT_IN_##MATHFN##F_R: \
+  case CFN_BUILT_IN_##MATHFN##L_R: \
+  fcode = BUILT_IN_##MATHFN##_R; fcodef = BUILT_IN_##MATHFN##F_R ; \
+  fcodel = BUILT_IN_##MATHFN##L_R ; break;
 
-/* Return mathematic function equivalent to FN but operating directly on TYPE,
-   if available.  If IMPLICIT is true use the implicit builtin declaration,
-   otherwise use the explicit declaration.  If we can't do the conversion,
-   return zero.  */
+/* Return a function equivalent to FN but operating on floating-point
+   values of type TYPE, or END_BUILTINS if no such function exists.
+   This is purely an operation on function codes; it does not guarantee
+   that the target actually has an implementation of the function.  */
 
-static tree
-mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit_p)
+static built_in_function
+mathfn_built_in_2 (tree type, combined_fn fn)
 {
-  enum built_in_function fcode, fcodef, fcodel, fcode2;
+  built_in_function fcode, fcodef, fcodel;
 
   switch (fn)
     {
-      CASE_MATHFN (BUILT_IN_ACOS)
-      CASE_MATHFN (BUILT_IN_ACOSH)
-      CASE_MATHFN (BUILT_IN_ASIN)
-      CASE_MATHFN (BUILT_IN_ASINH)
-      CASE_MATHFN (BUILT_IN_ATAN)
-      CASE_MATHFN (BUILT_IN_ATAN2)
-      CASE_MATHFN (BUILT_IN_ATANH)
-      CASE_MATHFN (BUILT_IN_CBRT)
-      CASE_MATHFN (BUILT_IN_CEIL)
-      CASE_MATHFN (BUILT_IN_CEXPI)
-      CASE_MATHFN (BUILT_IN_COPYSIGN)
-      CASE_MATHFN (BUILT_IN_COS)
-      CASE_MATHFN (BUILT_IN_COSH)
-      CASE_MATHFN (BUILT_IN_DREM)
-      CASE_MATHFN (BUILT_IN_ERF)
-      CASE_MATHFN (BUILT_IN_ERFC)
-      CASE_MATHFN (BUILT_IN_EXP)
-      CASE_MATHFN (BUILT_IN_EXP10)
-      CASE_MATHFN (BUILT_IN_EXP2)
-      CASE_MATHFN (BUILT_IN_EXPM1)
-      CASE_MATHFN (BUILT_IN_FABS)
-      CASE_MATHFN (BUILT_IN_FDIM)
-      CASE_MATHFN (BUILT_IN_FLOOR)
-      CASE_MATHFN (BUILT_IN_FMA)
-      CASE_MATHFN (BUILT_IN_FMAX)
-      CASE_MATHFN (BUILT_IN_FMIN)
-      CASE_MATHFN (BUILT_IN_FMOD)
-      CASE_MATHFN (BUILT_IN_FREXP)
-      CASE_MATHFN (BUILT_IN_GAMMA)
-      CASE_MATHFN_REENT (BUILT_IN_GAMMA) /* GAMMA_R */
-      CASE_MATHFN (BUILT_IN_HUGE_VAL)
-      CASE_MATHFN (BUILT_IN_HYPOT)
-      CASE_MATHFN (BUILT_IN_ILOGB)
-      CASE_MATHFN (BUILT_IN_ICEIL)
-      CASE_MATHFN (BUILT_IN_IFLOOR)
-      CASE_MATHFN (BUILT_IN_INF)
-      CASE_MATHFN (BUILT_IN_IRINT)
-      CASE_MATHFN (BUILT_IN_IROUND)
-      CASE_MATHFN (BUILT_IN_ISINF)
-      CASE_MATHFN (BUILT_IN_J0)
-      CASE_MATHFN (BUILT_IN_J1)
-      CASE_MATHFN (BUILT_IN_JN)
-      CASE_MATHFN (BUILT_IN_LCEIL)
-      CASE_MATHFN (BUILT_IN_LDEXP)
-      CASE_MATHFN (BUILT_IN_LFLOOR)
-      CASE_MATHFN (BUILT_IN_LGAMMA)
-      CASE_MATHFN_REENT (BUILT_IN_LGAMMA) /* LGAMMA_R */
-      CASE_MATHFN (BUILT_IN_LLCEIL)
-      CASE_MATHFN (BUILT_IN_LLFLOOR)
-      CASE_MATHFN (BUILT_IN_LLRINT)
-      CASE_MATHFN (BUILT_IN_LLROUND)
-      CASE_MATHFN (BUILT_IN_LOG)
-      CASE_MATHFN (BUILT_IN_LOG10)
-      CASE_MATHFN (BUILT_IN_LOG1P)
-      CASE_MATHFN (BUILT_IN_LOG2)
-      CASE_MATHFN (BUILT_IN_LOGB)
-      CASE_MATHFN (BUILT_IN_LRINT)
-      CASE_MATHFN (BUILT_IN_LROUND)
-      CASE_MATHFN (BUILT_IN_MODF)
-      CASE_MATHFN (BUILT_IN_NAN)
-      CASE_MATHFN (BUILT_IN_NANS)
-      CASE_MATHFN (BUILT_IN_NEARBYINT)
-      CASE_MATHFN (BUILT_IN_NEXTAFTER)
-      CASE_MATHFN (BUILT_IN_NEXTTOWARD)
-      CASE_MATHFN (BUILT_IN_POW)
-      CASE_MATHFN (BUILT_IN_POWI)
-      CASE_MATHFN (BUILT_IN_POW10)
-      CASE_MATHFN (BUILT_IN_REMAINDER)
-      CASE_MATHFN (BUILT_IN_REMQUO)
-      CASE_MATHFN (BUILT_IN_RINT)
-      CASE_MATHFN (BUILT_IN_ROUND)
-      CASE_MATHFN (BUILT_IN_SCALB)
-      CASE_MATHFN (BUILT_IN_SCALBLN)
-      CASE_MATHFN (BUILT_IN_SCALBN)
-      CASE_MATHFN (BUILT_IN_SIGNBIT)
-      CASE_MATHFN (BUILT_IN_SIGNIFICAND)
-      CASE_MATHFN (BUILT_IN_SIN)
-      CASE_MATHFN (BUILT_IN_SINCOS)
-      CASE_MATHFN (BUILT_IN_SINH)
-      CASE_MATHFN (BUILT_IN_SQRT)
-      CASE_MATHFN (BUILT_IN_TAN)
-      CASE_MATHFN (BUILT_IN_TANH)
-      CASE_MATHFN (BUILT_IN_TGAMMA)
-      CASE_MATHFN (BUILT_IN_TRUNC)
-      CASE_MATHFN (BUILT_IN_Y0)
-      CASE_MATHFN (BUILT_IN_Y1)
-      CASE_MATHFN (BUILT_IN_YN)
+    CASE_MATHFN (ACOS)
+    CASE_MATHFN (ACOSH)
+    CASE_MATHFN (ASIN)
+    CASE_MATHFN (ASINH)
+    CASE_MATHFN (ATAN)
+    CASE_MATHFN (ATAN2)
+    CASE_MATHFN (ATANH)
+    CASE_MATHFN (CBRT)
+    CASE_MATHFN (CEIL)
+    CASE_MATHFN (CEXPI)
+    CASE_MATHFN (COPYSIGN)
+    CASE_MATHFN (COS)
+    CASE_MATHFN (COSH)
+    CASE_MATHFN (DREM)
+    CASE_MATHFN (ERF)
+    CASE_MATHFN (ERFC)
+    CASE_MATHFN (EXP)
+    CASE_MATHFN (EXP10)
+    CASE_MATHFN (EXP2)
+    CASE_MATHFN (EXPM1)
+    CASE_MATHFN (FABS)
+    CASE_MATHFN (FDIM)
+    CASE_MATHFN (FLOOR)
+    CASE_MATHFN (FMA)
+    CASE_MATHFN (FMAX)
+    CASE_MATHFN (FMIN)
+    CASE_MATHFN (FMOD)
+    CASE_MATHFN (FREXP)
+    CASE_MATHFN (GAMMA)
+    CASE_MATHFN_REENT (GAMMA) /* GAMMA_R */
+    CASE_MATHFN (HUGE_VAL)
+    CASE_MATHFN (HYPOT)
+    CASE_MATHFN (ILOGB)
+    CASE_MATHFN (ICEIL)
+    CASE_MATHFN (IFLOOR)
+    CASE_MATHFN (INF)
+    CASE_MATHFN (IRINT)
+    CASE_MATHFN (IROUND)
+    CASE_MATHFN (ISINF)
+    CASE_MATHFN (J0)
+    CASE_MATHFN (J1)
+    CASE_MATHFN (JN)
+    CASE_MATHFN (LCEIL)
+    CASE_MATHFN (LDEXP)
+    CASE_MATHFN (LFLOOR)
+    CASE_MATHFN (LGAMMA)
+    CASE_MATHFN_REENT (LGAMMA) /* LGAMMA_R */
+    CASE_MATHFN (LLCEIL)
+    CASE_MATHFN (LLFLOOR)
+    CASE_MATHFN (LLRINT)
+    CASE_MATHFN (LLROUND)
+    CASE_MATHFN (LOG)
+    CASE_MATHFN (LOG10)
+    CASE_MATHFN (LOG1P)
+    CASE_MATHFN (LOG2)
+    CASE_MATHFN (LOGB)
+    CASE_MATHFN (LRINT)
+    CASE_MATHFN (LROUND)
+    CASE_MATHFN (MODF)
+    CASE_MATHFN (NAN)
+    CASE_MATHFN (NANS)
+    CASE_MATHFN (NEARBYINT)
+    CASE_MATHFN (NEXTAFTER)
+    CASE_MATHFN (NEXTTOWARD)
+    CASE_MATHFN (POW)
+    CASE_MATHFN (POWI)
+    CASE_MATHFN (POW10)
+    CASE_MATHFN (REMAINDER)
+    CASE_MATHFN (REMQUO)
+    CASE_MATHFN (RINT)
+    CASE_MATHFN (ROUND)
+    CASE_MATHFN (SCALB)
+    CASE_MATHFN (SCALBLN)
+    CASE_MATHFN (SCALBN)
+    CASE_MATHFN (SIGNBIT)
+    CASE_MATHFN (SIGNIFICAND)
+    CASE_MATHFN (SIN)
+    CASE_MATHFN (SINCOS)
+    CASE_MATHFN (SINH)
+    CASE_MATHFN (SQRT)
+    CASE_MATHFN (TAN)
+    CASE_MATHFN (TANH)
+    CASE_MATHFN (TGAMMA)
+    CASE_MATHFN (TRUNC)
+    CASE_MATHFN (Y0)
+    CASE_MATHFN (Y1)
+    CASE_MATHFN (YN)
 
-      default:
-       return NULL_TREE;
-      }
+    default:
+      return END_BUILTINS;
+    }
 
   if (TYPE_MAIN_VARIANT (type) == double_type_node)
-    fcode2 = fcode;
+    return fcode;
   else if (TYPE_MAIN_VARIANT (type) == float_type_node)
-    fcode2 = fcodef;
+    return fcodef;
   else if (TYPE_MAIN_VARIANT (type) == long_double_type_node)
-    fcode2 = fcodel;
+    return fcodel;
   else
+    return END_BUILTINS;
+}
+
+/* Return mathematic function equivalent to FN but operating directly on TYPE,
+   if available.  If IMPLICIT_P is true use the implicit builtin declaration,
+   otherwise use the explicit declaration.  If we can't do the conversion,
+   return null.  */
+
+static tree
+mathfn_built_in_1 (tree type, combined_fn fn, bool implicit_p)
+{
+  built_in_function fcode2 = mathfn_built_in_2 (type, fn);
+  if (fcode2 == END_BUILTINS)
     return NULL_TREE;
 
   if (implicit_p && !builtin_decl_implicit_p (fcode2))
@@ -1941,292 +1956,81 @@ mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit_p)
   return builtin_decl_explicit (fcode2);
 }
 
-/* Like mathfn_built_in_1(), but always use the implicit array.  */
+/* Like mathfn_built_in_1, but always use the implicit array.  */
 
 tree
-mathfn_built_in (tree type, enum built_in_function fn)
+mathfn_built_in (tree type, combined_fn fn)
 {
   return mathfn_built_in_1 (type, fn, /*implicit=*/ 1);
 }
 
-/* If errno must be maintained, expand the RTL to check if the result,
-   TARGET, of a built-in function call, EXP, is NaN, and if so set
-   errno to EDOM.  */
+/* Like mathfn_built_in_1, but take a built_in_function and
+   always use the implicit array.  */
 
-static void
-expand_errno_check (tree exp, rtx target)
+tree
+mathfn_built_in (tree type, enum built_in_function fn)
 {
-  rtx_code_label *lab = gen_label_rtx ();
-
-  /* Test the result; if it is NaN, set errno=EDOM because
-     the argument was not in the domain.  */
-  do_compare_rtx_and_jump (target, target, EQ, 0, GET_MODE (target),
-                          NULL_RTX, NULL, lab,
-                          /* The jump is very likely.  */
-                          REG_BR_PROB_BASE - (REG_BR_PROB_BASE / 2000 - 1));
-
-#ifdef TARGET_EDOM
-  /* If this built-in doesn't throw an exception, set errno directly.  */
-  if (TREE_NOTHROW (TREE_OPERAND (CALL_EXPR_FN (exp), 0)))
-    {
-#ifdef GEN_ERRNO_RTX
-      rtx errno_rtx = GEN_ERRNO_RTX;
-#else
-      rtx errno_rtx
-         = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno"));
-#endif
-      emit_move_insn (errno_rtx,
-                     gen_int_mode (TARGET_EDOM, GET_MODE (errno_rtx)));
-      emit_label (lab);
-      return;
-    }
-#endif
-
-  /* Make sure the library call isn't expanded as a tail call.  */
-  CALL_EXPR_TAILCALL (exp) = 0;
-
-  /* We can't set errno=EDOM directly; let the library call do it.
-     Pop the arguments right away in case the call gets deleted.  */
-  NO_DEFER_POP;
-  expand_call (exp, target, 0);
-  OK_DEFER_POP;
-  emit_label (lab);
+  return mathfn_built_in_1 (type, as_combined_fn (fn), /*implicit=*/ 1);
 }
 
-/* Expand a call to one of the builtin math functions (sqrt, exp, or log).
-   Return NULL_RTX if a normal call should be emitted rather than expanding
-   the function in-line.  EXP is the expression that is a call to the builtin
-   function; if convenient, the result should be placed in TARGET.
-   SUBTARGET may be used as the target for computing one of EXP's operands.  */
+/* If BUILT_IN_NORMAL function FNDECL has an associated internal function,
+   return its code, otherwise return IFN_LAST.  Note that this function
+   only tests whether the function is defined in internals.def, not whether
+   it is actually available on the target.  */
 
-static rtx
-expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
+internal_fn
+associated_internal_fn (tree fndecl)
 {
-  optab builtin_optab;
-  rtx op0;
-  rtx_insn *insns;
-  tree fndecl = get_callee_fndecl (exp);
-  machine_mode mode;
-  bool errno_set = false;
-  bool try_widening = false;
-  tree arg;
-
-  if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
-    return NULL_RTX;
-
-  arg = CALL_EXPR_ARG (exp, 0);
-
+  gcc_checking_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL);
+  tree return_type = TREE_TYPE (TREE_TYPE (fndecl));
   switch (DECL_FUNCTION_CODE (fndecl))
     {
-    CASE_FLT_FN (BUILT_IN_SQRT):
-      errno_set = ! tree_expr_nonnegative_p (arg);
-      try_widening = true;
-      builtin_optab = sqrt_optab;
-      break;
-    CASE_FLT_FN (BUILT_IN_EXP):
-      errno_set = true; builtin_optab = exp_optab; break;
-    CASE_FLT_FN (BUILT_IN_EXP10):
-    CASE_FLT_FN (BUILT_IN_POW10):
-      errno_set = true; builtin_optab = exp10_optab; break;
-    CASE_FLT_FN (BUILT_IN_EXP2):
-      errno_set = true; builtin_optab = exp2_optab; break;
-    CASE_FLT_FN (BUILT_IN_EXPM1):
-      errno_set = true; builtin_optab = expm1_optab; break;
-    CASE_FLT_FN (BUILT_IN_LOGB):
-      errno_set = true; builtin_optab = logb_optab; break;
-    CASE_FLT_FN (BUILT_IN_LOG):
-      errno_set = true; builtin_optab = log_optab; break;
-    CASE_FLT_FN (BUILT_IN_LOG10):
-      errno_set = true; builtin_optab = log10_optab; break;
-    CASE_FLT_FN (BUILT_IN_LOG2):
-      errno_set = true; builtin_optab = log2_optab; break;
-    CASE_FLT_FN (BUILT_IN_LOG1P):
-      errno_set = true; builtin_optab = log1p_optab; break;
-    CASE_FLT_FN (BUILT_IN_ASIN):
-      builtin_optab = asin_optab; break;
-    CASE_FLT_FN (BUILT_IN_ACOS):
-      builtin_optab = acos_optab; break;
-    CASE_FLT_FN (BUILT_IN_TAN):
-      builtin_optab = tan_optab; break;
-    CASE_FLT_FN (BUILT_IN_ATAN):
-      builtin_optab = atan_optab; break;
-    CASE_FLT_FN (BUILT_IN_FLOOR):
-      builtin_optab = floor_optab; break;
-    CASE_FLT_FN (BUILT_IN_CEIL):
-      builtin_optab = ceil_optab; break;
-    CASE_FLT_FN (BUILT_IN_TRUNC):
-      builtin_optab = btrunc_optab; break;
-    CASE_FLT_FN (BUILT_IN_ROUND):
-      builtin_optab = round_optab; break;
-    CASE_FLT_FN (BUILT_IN_NEARBYINT):
-      builtin_optab = nearbyint_optab;
-      if (flag_trapping_math)
-       break;
-      /* Else fallthrough and expand as rint.  */
-    CASE_FLT_FN (BUILT_IN_RINT):
-      builtin_optab = rint_optab; break;
-    CASE_FLT_FN (BUILT_IN_SIGNIFICAND):
-      builtin_optab = significand_optab; break;
-    default:
-      gcc_unreachable ();
-    }
-
-  /* Make a suitable register to place result in.  */
-  mode = TYPE_MODE (TREE_TYPE (exp));
-
-  if (! flag_errno_math || ! HONOR_NANS (mode))
-    errno_set = false;
-
-  /* Before working hard, check whether the instruction is available, but try
-     to widen the mode for specific operations.  */
-  if ((optab_handler (builtin_optab, mode) != CODE_FOR_nothing
-       || (try_widening && !excess_precision_type (TREE_TYPE (exp))))
-      && (!errno_set || !optimize_insn_for_size_p ()))
-    {
-      rtx result = gen_reg_rtx (mode);
-
-      /* Wrap the computation of the argument in a SAVE_EXPR, as we may
-        need to expand the argument again.  This way, we will not perform
-        side-effects more the once.  */
-      CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);
-
-      op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
-
-      start_sequence ();
-
-      /* Compute into RESULT.
-        Set RESULT to wherever the result comes back.  */
-      result = expand_unop (mode, builtin_optab, op0, result, 0);
-
-      if (result != 0)
-       {
-         if (errno_set)
-           expand_errno_check (exp, result);
-
-         /* Output the entire sequence.  */
-         insns = get_insns ();
-         end_sequence ();
-         emit_insn (insns);
-         return result;
-       }
-
-      /* If we were unable to expand via the builtin, stop the sequence
-        (without outputting the insns) and call to the library function
-        with the stabilized argument list.  */
-      end_sequence ();
-    }
-
-  return expand_call (exp, target, target == const0_rtx);
-}
+#define DEF_INTERNAL_FLT_FN(NAME, FLAGS, OPTAB, TYPE) \
+    CASE_FLT_FN (BUILT_IN_##NAME): return IFN_##NAME;
+#define DEF_INTERNAL_INT_FN(NAME, FLAGS, OPTAB, TYPE) \
+    CASE_INT_FN (BUILT_IN_##NAME): return IFN_##NAME;
+#include "internal-fn.def"
 
-/* Expand a call to the builtin binary math functions (pow and atan2).
-   Return NULL_RTX if a normal call should be emitted rather than expanding the
-   function in-line.  EXP is the expression that is a call to the builtin
-   function; if convenient, the result should be placed in TARGET.
-   SUBTARGET may be used as the target for computing one of EXP's
-   operands.  */
+    CASE_FLT_FN (BUILT_IN_POW10):
+      return IFN_EXP10;
 
-static rtx
-expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
-{
-  optab builtin_optab;
-  rtx op0, op1, result;
-  rtx_insn *insns;
-  int op1_type = REAL_TYPE;
-  tree fndecl = get_callee_fndecl (exp);
-  tree arg0, arg1;
-  machine_mode mode;
-  bool errno_set = true;
+    CASE_FLT_FN (BUILT_IN_DREM):
+      return IFN_REMAINDER;
 
-  switch (DECL_FUNCTION_CODE (fndecl))
-    {
     CASE_FLT_FN (BUILT_IN_SCALBN):
     CASE_FLT_FN (BUILT_IN_SCALBLN):
-    CASE_FLT_FN (BUILT_IN_LDEXP):
-      op1_type = INTEGER_TYPE;
-    default:
-      break;
-    }
-
-  if (!validate_arglist (exp, REAL_TYPE, op1_type, VOID_TYPE))
-    return NULL_RTX;
-
-  arg0 = CALL_EXPR_ARG (exp, 0);
-  arg1 = CALL_EXPR_ARG (exp, 1);
+      if (REAL_MODE_FORMAT (TYPE_MODE (return_type))->b == 2)
+       return IFN_LDEXP;
+      return IFN_LAST;
 
-  switch (DECL_FUNCTION_CODE (fndecl))
-    {
-    CASE_FLT_FN (BUILT_IN_POW):
-      builtin_optab = pow_optab; break;
-    CASE_FLT_FN (BUILT_IN_ATAN2):
-      builtin_optab = atan2_optab; break;
-    CASE_FLT_FN (BUILT_IN_SCALB):
-      if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (exp)))->b != 2)
-       return 0;
-      builtin_optab = scalb_optab; break;
-    CASE_FLT_FN (BUILT_IN_SCALBN):
-    CASE_FLT_FN (BUILT_IN_SCALBLN):
-      if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (exp)))->b != 2)
-       return 0;
-    /* Fall through... */
-    CASE_FLT_FN (BUILT_IN_LDEXP):
-      builtin_optab = ldexp_optab; break;
-    CASE_FLT_FN (BUILT_IN_FMOD):
-      builtin_optab = fmod_optab; break;
-    CASE_FLT_FN (BUILT_IN_REMAINDER):
-    CASE_FLT_FN (BUILT_IN_DREM):
-      builtin_optab = remainder_optab; break;
     default:
-      gcc_unreachable ();
+      return IFN_LAST;
     }
+}
 
-  /* Make a suitable register to place result in.  */
-  mode = TYPE_MODE (TREE_TYPE (exp));
-
-  /* Before working hard, check whether the instruction is available.  */
-  if (optab_handler (builtin_optab, mode) == CODE_FOR_nothing)
-    return NULL_RTX;
-
-  result = gen_reg_rtx (mode);
-
-  if (! flag_errno_math || ! HONOR_NANS (mode))
-    errno_set = false;
-
-  if (errno_set && optimize_insn_for_size_p ())
-    return 0;
-
-  /* Always stabilize the argument list.  */
-  CALL_EXPR_ARG (exp, 0) = arg0 = builtin_save_expr (arg0);
-  CALL_EXPR_ARG (exp, 1) = arg1 = builtin_save_expr (arg1);
-
-  op0 = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL);
-  op1 = expand_normal (arg1);
-
-  start_sequence ();
-
-  /* Compute into RESULT.
-     Set RESULT to wherever the result comes back.  */
-  result = expand_binop (mode, builtin_optab, op0, op1,
-                        result, 0, OPTAB_DIRECT);
+/* If CALL is a call to a BUILT_IN_NORMAL function that could be replaced
+   on the current target by a call to an internal function, return the
+   code of that internal function, otherwise return IFN_LAST.  The caller
+   is responsible for ensuring that any side-effects of the built-in
+   call are dealt with correctly.  E.g. if CALL sets errno, the caller
+   must decide that the errno result isn't needed or make it available
+   in some other way.  */
 
-  /* If we were unable to expand via the builtin, stop the sequence
-     (without outputting the insns) and call to the library function
-     with the stabilized argument list.  */
-  if (result == 0)
+internal_fn
+replacement_internal_fn (gcall *call)
+{
+  if (gimple_call_builtin_p (call, BUILT_IN_NORMAL))
     {
-      end_sequence ();
-      return expand_call (exp, target, target == const0_rtx);
+      internal_fn ifn = associated_internal_fn (gimple_call_fndecl (call));
+      if (ifn != IFN_LAST)
+       {
+         tree_pair types = direct_internal_fn_types (ifn, call);
+         optimization_type opt_type = bb_optimization_type (gimple_bb (call));
+         if (direct_internal_fn_supported_p (ifn, types, opt_type))
+           return ifn;
+       }
     }
-
-  if (errno_set)
-    expand_errno_check (exp, result);
-
-  /* Output the entire sequence.  */
-  insns = get_insns ();
-  end_sequence ();
-  emit_insn (insns);
-
-  return result;
+  return IFN_LAST;
 }
 
 /* Expand a call to the builtin trinary math functions (fma).
@@ -2845,7 +2649,7 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target)
     {
     CASE_FLT_FN (BUILT_IN_IRINT):
       fallback_fn = BUILT_IN_LRINT;
-      /* FALLTHRU */
+      gcc_fallthrough ();
     CASE_FLT_FN (BUILT_IN_LRINT):
     CASE_FLT_FN (BUILT_IN_LLRINT):
       builtin_optab = lrint_optab;
@@ -2853,7 +2657,7 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target)
 
     CASE_FLT_FN (BUILT_IN_IROUND):
       fallback_fn = BUILT_IN_LROUND;
-      /* FALLTHRU */
+      gcc_fallthrough ();
     CASE_FLT_FN (BUILT_IN_LROUND):
     CASE_FLT_FN (BUILT_IN_LLROUND):
       builtin_optab = lround_optab;
@@ -2907,8 +2711,8 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target)
         a call to lround in the hope that the target provides at least some
         C99 functions.  This should result in the best user experience for
         not full C99 targets.  */
-      tree fallback_fndecl = mathfn_built_in_1 (TREE_TYPE (arg),
-                                               fallback_fn, 0);
+      tree fallback_fndecl = mathfn_built_in_1
+       (TREE_TYPE (arg), as_combined_fn (fallback_fn), 0);
 
       exp = build_call_nofold_loc (EXPR_LOCATION (exp),
                                   fallback_fndecl, 1, arg);
@@ -3227,24 +3031,275 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
   return dest_addr;
 }
 
-/* Expand a call EXP to the memcpy builtin.
-   Return NULL_RTX if we failed, the caller should emit a normal call,
-   otherwise try to get the result in TARGET, if convenient (and in
-   mode MODE if that's convenient).  */
+/* Try to verify that the sizes and lengths of the arguments to a string
+   manipulation function given by EXP are within valid bounds and that
+   the operation does not lead to buffer overflow.  Arguments other than
+   EXP may be null.  When non-null, the arguments have the following
+   meaning:
+   SIZE is the user-supplied size argument to the function (such as in
+   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
+   number of bytes to write.
+   MAXLEN is the user-supplied bound on the length of the source sequence
+   (such as in strncat(d, s, N).  It specifies the upper limit on the number
+   of bytes to write.
+   STR is the source string (such as in strcpy(d, s)) when the epxression
+   EXP is a string function call (as opposed to a memory call like memcpy).
+   As an exception, STR can also be an integer denoting the precomputed
+   length of the source string.
+   OBJSIZE is the size of the destination object specified by the last
+   argument to the _chk builtins, typically resulting from the expansion
+   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
+   OBJSIZE).
+
+   When SIZE is null LEN is checked to verify that it doesn't exceed
+   SIZE_MAX.
+
+   If the call is successfully verified as safe from buffer overflow
+   the function returns true, otherwise false..  */
 
-static rtx
-expand_builtin_memcpy (tree exp, rtx target)
+static bool
+check_sizes (int opt, tree exp, tree size, tree maxlen, tree str, tree objsize)
 {
-  if (!validate_arglist (exp,
-                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
-    return NULL_RTX;
-  else
+  /* The size of the largest object is half the address space, or
+     SSIZE_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+
+  tree slen = NULL_TREE;
+
+  /* Set to true when the exact number of bytes written by a string
+     function like strcpy is not known and the only thing that is
+     known is that it must be at least one (for the terminating nul).  */
+  bool at_least_one = false;
+  if (str)
     {
-      tree dest = CALL_EXPR_ARG (exp, 0);
-      tree src = CALL_EXPR_ARG (exp, 1);
-      tree len = CALL_EXPR_ARG (exp, 2);
-      return expand_builtin_memcpy_args (dest, src, len, target, exp);
+      /* STR is normally a pointer to string but as a special case
+        it can be an integer denoting the length of a string.  */
+      if (POINTER_TYPE_P (TREE_TYPE (str)))
+       {
+         /* Try to determine the range of lengths the source string
+            refers to.  If it can be determined add one to it for
+            the terminating nul.  Otherwise, set it to one for
+            the same reason.  */
+         tree lenrange[2];
+         get_range_strlen (str, lenrange);
+         if (lenrange[0])
+           slen = fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+                               size_one_node);
+         else
+           {
+             at_least_one = true;
+             slen = size_one_node;
+           }
+       }
+      else
+       slen = str;
+    }
+
+  if (!size && !maxlen)
+    {
+      /* When the only available piece of data is the object size
+        there is nothing to do.  */
+      if (!slen)
+       return true;
+
+      /* Otherwise, when the length of the source sequence is known
+        (as with with strlen), set SIZE to it.  */
+      size = slen;
+    }
+
+  if (!objsize)
+    objsize = maxobjsize;
+
+  /* The SIZE is exact if it's non-null, constant, and in range of
+     unsigned HOST_WIDE_INT.  */
+  bool exactsize = size && tree_fits_uhwi_p (size);
+
+  tree range[2] = { NULL_TREE, NULL_TREE };
+  if (size)
+    get_size_range (size, range);
+
+  /* First check the number of bytes to be written against the maximum
+     object size.  */
+  if (range[0] && tree_int_cst_lt (maxobjsize, range[0]))
+    {
+      location_t loc = tree_nonartificial_location (exp);
+
+      if (range[0] == range[1])
+       warning_at (loc, opt,
+                   "%K%qD: specified size %wu "
+                   "exceeds maximum object size %wu",
+                   exp, get_callee_fndecl (exp),
+                   tree_to_uhwi (range[0]),
+                   tree_to_uhwi (maxobjsize));
+         else
+           warning_at (loc, opt,
+                       "%K%qD: specified size between %wu and %wu "
+                       "exceeds maximum object size %wu",
+                       exp, get_callee_fndecl (exp),
+                       tree_to_uhwi (range[0]),
+                       tree_to_uhwi (range[1]),
+                       tree_to_uhwi (maxobjsize));
+      return false;
+    }
+
+  /* Next check the number of bytes to be written against the destination
+     object size.  */
+  if (range[0] || !exactsize || integer_all_onesp (size))
+    {
+      if (range[0]
+         && ((tree_fits_uhwi_p (objsize)
+              && tree_int_cst_lt (objsize, range[0]))
+             || (tree_fits_uhwi_p (size)
+                 && tree_int_cst_lt (size, range[0]))))
+       {
+         unsigned HOST_WIDE_INT uwir0 = tree_to_uhwi (range[0]);
+
+         location_t loc = tree_nonartificial_location (exp);
+
+         if (at_least_one)
+           warning_at (loc, opt,
+                       "%K%qD: writing at least %wu byte into a region "
+                       "of size %wu overflows the destination",
+                       exp, get_callee_fndecl (exp), uwir0,
+                       tree_to_uhwi (objsize));
+         else if (range[0] == range[1])
+           warning_at (loc, opt,
+                       (uwir0 == 1
+                        ? G_("%K%qD: writing %wu byte into a region "
+                             "of size %wu overflows the destination")
+                        : G_("%K%qD writing %wu bytes into a region "
+                             "of size %wu overflows the destination")),
+                       exp, get_callee_fndecl (exp), uwir0,
+                       tree_to_uhwi (objsize));
+         else
+           warning_at (loc, opt,
+                       "%K%qD: writing between %wu and %wu bytes "
+                       "into a region of size %wu overflows "
+                       "the destination",
+                       exp, get_callee_fndecl (exp), uwir0,
+                       tree_to_uhwi (range[1]), tree_to_uhwi (objsize));
+
+         /* Return error when an overflow has been detected.  */
+         return false;
+       }
+    }
+
+  /* Check the maximum length of the source sequence against the size
+     of the destination object if known, or against the maximum size
+     of an object.  */
+  if (maxlen)
+    {
+      get_size_range (maxlen, range);
+
+      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+       {
+         location_t loc = tree_nonartificial_location (exp);
+
+         if (tree_int_cst_lt (maxobjsize, range[0]))
+           {
+             /* Warn about crazy big sizes first since that's more
+                likely to be meaningful than saying that the bound
+                is greater than the object size if both are big.  */
+             if (range[0] == range[1])
+               warning_at (loc, opt,
+                           "%K%qD: specified bound %wu "
+                           "exceeds maximum object size %wu",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (maxobjsize));
+             else
+               warning_at (loc, opt,
+                           "%K%qD: specified bound between %wu and %wu "
+                           " exceeds maximum object size %wu",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (range[1]),
+                           tree_to_uhwi (maxobjsize));
+
+             return false;
+           }
+
+         if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+           {
+             if (range[0] == range[1])
+               warning_at (loc, opt,
+                           "%K%qD: specified bound %wu "
+                           "exceeds the size %wu of the destination",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (objsize));
+             else
+               warning_at (loc, opt,
+                           "%K%qD: specified bound between %wu and %wu "
+                           " exceeds the size %wu of the destination",
+                           exp, get_callee_fndecl (exp),
+                           tree_to_uhwi (range[0]),
+                           tree_to_uhwi (range[1]),
+                           tree_to_uhwi (objsize));
+             return false;
+           }
+       }
     }
+
+  return true;
+}
+
+/* Helper to compute the size of the object referenced by the DEST
+   expression which must of of pointer type, using Object Size type
+   OSTYPE (only the least significant 2 bits are used).  Return
+   the size of the object if successful or NULL when the size cannot
+   be determined.  */
+
+static inline tree
+compute_dest_size (tree dest, int ostype)
+{
+  unsigned HOST_WIDE_INT size;
+  if (compute_builtin_object_size (dest, ostype & 3, &size))
+    return build_int_cst (sizetype, size);
+
+  return NULL_TREE;
+}
+
+/* Helper to determine and check the sizes of the source and the destination
+   of calls to __builtin_{bzero,memcpy,memset} calls.  Use Object Size type-0
+   regardless of the OPT_Wstringop_overflow_ setting.  Returns true on success
+   (no overflow or invalid sizes), false otherwise.  */
+
+static bool
+check_memop_sizes (tree exp, tree dest, tree size)
+{
+  if (!warn_stringop_overflow)
+    return true;
+
+  /* For functions like memset and memcpy that operate on raw memory
+     try to determine the size of the largest destination object using
+     type-0 Object Size regardless of the object size type specified
+     by the option.  */
+  tree objsize = compute_dest_size (dest, 0);
+
+  return check_sizes (OPT_Wstringop_overflow_, exp,
+                     size, /*maxlen=*/NULL_TREE, /*str=*/NULL_TREE, objsize);
+}
+
+/* Expand a call EXP to the memcpy builtin.
+   Return NULL_RTX if we failed, the caller should emit a normal call,
+   otherwise try to get the result in TARGET, if convenient (and in
+   mode MODE if that's convenient).  */
+
+static rtx
+expand_builtin_memcpy (tree exp, rtx target)
+{
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  check_memop_sizes (exp, dest, len);
+
+  return expand_builtin_memcpy_args (dest, src, len, target, exp);
 }
 
 /* Expand an instrumented call EXP to the memcpy builtin.
@@ -3292,15 +3347,20 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode)
   if (!validate_arglist (exp,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
-  else
-    {
-      tree dest = CALL_EXPR_ARG (exp, 0);
-      tree src = CALL_EXPR_ARG (exp, 1);
-      tree len = CALL_EXPR_ARG (exp, 2);
-      return expand_builtin_mempcpy_args (dest, src, len,
-                                         target, mode, /*endp=*/ 1,
-                                         exp);
-    }
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  /* Avoid expanding mempcpy into memcpy when the call is determined
+     to overflow the buffer.  This also prevents the same overflow
+     from being diagnosed again when expanding memcpy.  */
+  if (!check_memop_sizes (exp, dest, len))
+    return NULL_RTX;
+
+  return expand_builtin_mempcpy_args (dest, src, len,
+                                     target, mode, /*endp=*/ 1,
+                                     exp);
 }
 
 /* Expand an instrumented call EXP to the mempcpy builtin.
@@ -3472,6 +3532,33 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
   return target;
 }
 
+/* Do some very basic size validation of a call to the strcpy builtin
+   given by EXP.  Return NULL_RTX to have the built-in expand to a call
+   to the library function.  */
+
+static rtx
+expand_builtin_strcat (tree exp, rtx)
+{
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+
+  /* There is no way here to determine the length of the string in
+     the destination to which the SRC string is being appended so
+     just diagnose cases when the souce string is longer than
+     the destination object.  */
+
+  tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+
+  check_sizes (OPT_Wstringop_overflow_,
+              exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+
+  return NULL_RTX;
+}
+
 /* Expand expression EXP, which is a call to the strcpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call, otherwise
    try to get the result in TARGET, if convenient (and in mode MODE if that's
@@ -3480,13 +3567,20 @@ expand_movstr (tree dest, tree src, rtx target, int endp)
 static rtx
 expand_builtin_strcpy (tree exp, rtx target)
 {
-  if (validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
-   {
-     tree dest = CALL_EXPR_ARG (exp, 0);
-     tree src = CALL_EXPR_ARG (exp, 1);
-     return expand_builtin_strcpy_args (dest, src, target);
-   }
-   return NULL_RTX;
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+
+  if (warn_stringop_overflow)
+    {
+      tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+      check_sizes (OPT_Wstringop_overflow_,
+                  exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+    }
+
+  return expand_builtin_strcpy_args (dest, src, target);
 }
 
 /* Helper function to do the actual work for expand_builtin_strcpy.  The
@@ -3594,6 +3688,131 @@ builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
   return c_readstr (str + offset, mode);
 }
 
+/* Helper to check the sizes of sequences and the destination of calls
+   to __builtin_strncat and __builtin___strncat_chk.  Returns true on
+   success (no overflow or invalid sizes), false otherwise.  */
+
+static bool
+check_strncat_sizes (tree exp, tree objsize)
+{
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree maxlen = CALL_EXPR_ARG (exp, 2);
+
+  /* Try to determine the range of lengths that the source expression
+     refers to.  */
+  tree lenrange[2];
+  get_range_strlen (src, lenrange);
+
+  /* Try to verify that the destination is big enough for the shortest
+     string.  */
+
+  if (!objsize && warn_stringop_overflow)
+    {
+      /* If it hasn't been provided by __strncat_chk, try to determine
+        the size of the destination object into which the source is
+        being copied.  */
+      objsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+    }
+
+  /* Add one for the terminating nul.  */
+  tree srclen = (lenrange[0]
+                ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+                               size_one_node)
+                : NULL_TREE);
+
+  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+     nul so the specified upper bound should never be equal to (or greater
+     than) the size of the destination.  */
+  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxlen))
+    {
+      warning_at (EXPR_LOCATION (exp), OPT_Wstringop_overflow_,
+                 "specified bound %wu "
+                 "equals the size of the destination",
+                 tree_to_uhwi (maxlen));
+
+      return false;
+    }
+
+  if (!srclen
+      || (maxlen && tree_fits_uhwi_p (maxlen)
+         && tree_fits_uhwi_p (srclen)
+         && tree_int_cst_lt (maxlen, srclen)))
+    srclen = maxlen;
+
+  /* The number of bytes to write is LEN but check_sizes will also
+     check SRCLEN if LEN's value isn't known.  */
+  return check_sizes (OPT_Wstringop_overflow_,
+                     exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+}
+
+/* Similar to expand_builtin_strcat, do some very basic size validation
+   of a call to the strcpy builtin given by EXP.  Return NULL_RTX to have
+   the built-in expand to a call to the library function.  */
+
+static rtx
+expand_builtin_strncat (tree exp, rtx)
+{
+  if (!validate_arglist (exp,
+                        POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+      || !warn_stringop_overflow)
+    return NULL_RTX;
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  /* The upper bound on the number of bytes to write.  */
+  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  /* The length of the source sequence.  */
+  tree slen = c_strlen (src, 1);
+
+  /* Try to determine the range of lengths that the source expression
+     refers to.  */
+  tree lenrange[2];
+  if (slen)
+    lenrange[0] = lenrange[1] = slen;
+  else
+    get_range_strlen (src, lenrange);
+
+  /* Try to verify that the destination is big enough for the shortest
+     string.  First try to determine the size of the destination object
+     into which the source is being copied.  */
+  tree destsize = compute_dest_size (dest, warn_stringop_overflow - 1);
+
+  /* Add one for the terminating nul.  */
+  tree srclen = (lenrange[0]
+                ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+                               size_one_node)
+                : NULL_TREE);
+
+  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+     nul so the specified upper bound should never be equal to (or greater
+     than) the size of the destination.  */
+  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxlen))
+    {
+      warning_at (EXPR_LOCATION (exp), OPT_Wstringop_overflow_,
+                 "specified bound %wu "
+                 "equals the size of the destination",
+                 tree_to_uhwi (maxlen));
+
+      return NULL_RTX;
+    }
+
+  if (!srclen
+      || (maxlen && tree_fits_uhwi_p (maxlen)
+         && tree_fits_uhwi_p (srclen)
+         && tree_int_cst_lt (maxlen, srclen)))
+    srclen = maxlen;
+
+  /* The number of bytes to write is LEN but check_sizes will also
+     check SRCLEN if LEN's value isn't known.  */
+  check_sizes (OPT_Wstringop_overflow_,
+              exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+
+  return NULL_RTX;
+}
+
 /* Expand expression EXP, which is a call to the strncpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call.  */
 
@@ -3607,9 +3826,33 @@ expand_builtin_strncpy (tree exp, rtx target)
     {
       tree dest = CALL_EXPR_ARG (exp, 0);
       tree src = CALL_EXPR_ARG (exp, 1);
+      /* The number of bytes to write (not the maximum).  */
       tree len = CALL_EXPR_ARG (exp, 2);
+      /* The length of the source sequence.  */
       tree slen = c_strlen (src, 1);
 
+      if (warn_stringop_overflow)
+       {
+         /* Try to determine the range of lengths that the source expression
+            refers to.  */
+         tree lenrange[2];
+         if (slen)
+           lenrange[0] = lenrange[1] = slen;
+         else
+           {
+             get_range_strlen (src, lenrange);
+             slen = lenrange[0];
+           }
+
+         tree destsize = compute_dest_size (dest,
+                                            warn_stringop_overflow - 1);
+
+         /* The number of bytes to write is LEN but check_sizes will also
+            check SLEN if LEN's value isn't known.  */
+         check_sizes (OPT_Wstringop_overflow_,
+                      exp, len, /*maxlen=*/NULL_TREE, slen, destsize);
+       }
+
       /* We must be passed a constant len and src parameter.  */
       if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
        return NULL_RTX;
@@ -3697,13 +3940,14 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   if (!validate_arglist (exp,
                         POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
-  else
-    {
-      tree dest = CALL_EXPR_ARG (exp, 0);
-      tree val = CALL_EXPR_ARG (exp, 1);
-      tree len = CALL_EXPR_ARG (exp, 2);
-      return expand_builtin_memset_args (dest, val, len, target, mode, exp);
-    }
+
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree val = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+
+  check_memop_sizes (exp, dest, len);
+
+  return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
 
 /* Expand expression EXP, which is an instrumented call to the memset builtin.
@@ -3884,20 +4128,21 @@ expand_builtin_memset_args (tree dest, tree val, tree len,
 static rtx
 expand_builtin_bzero (tree exp)
 {
-  tree dest, size;
-  location_t loc = EXPR_LOCATION (exp);
-
   if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  size = CALL_EXPR_ARG (exp, 1);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree size = CALL_EXPR_ARG (exp, 1);
+
+  check_memop_sizes (exp, dest, size);
 
   /* 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.  */
 
+  location_t loc = EXPR_LOCATION (exp);
+
   return expand_builtin_memset_args (dest, integer_zero_node,
                                     fold_convert_loc (loc,
                                                       size_type_node, size),
@@ -3926,53 +4171,24 @@ expand_cmpstr (insn_code icode, rtx target, rtx arg1_rtx, rtx arg2_rtx,
   return NULL_RTX;
 }
 
-/* Try to expand cmpstrn or cmpmem operation ICODE with the given operands.
-   ARG3_TYPE is the type of ARG3_RTX.  Return the result rtx on success,
-   otherwise return null.  */
-
-static rtx
-expand_cmpstrn_or_cmpmem (insn_code icode, rtx target, rtx arg1_rtx,
-                         rtx arg2_rtx, tree arg3_type, rtx arg3_rtx,
-                         HOST_WIDE_INT align)
-{
-  machine_mode insn_mode = insn_data[icode].operand[0].mode;
-
-  if (target && (!REG_P (target) || HARD_REGISTER_P (target)))
-    target = NULL_RTX;
-
-  struct expand_operand ops[5];
-  create_output_operand (&ops[0], target, insn_mode);
-  create_fixed_operand (&ops[1], arg1_rtx);
-  create_fixed_operand (&ops[2], arg2_rtx);
-  create_convert_operand_from (&ops[3], arg3_rtx, TYPE_MODE (arg3_type),
-                              TYPE_UNSIGNED (arg3_type));
-  create_integer_operand (&ops[4], align);
-  if (maybe_expand_insn (icode, 5, ops))
-    return ops[0].value;
-  return NULL_RTX;
-}
-
 /* Expand expression EXP, which is a call to the memcmp built-in function.
    Return NULL_RTX if we failed and the caller should emit a normal call,
-   otherwise try to get the result in TARGET, if convenient.  */
+   otherwise try to get the result in TARGET, if convenient.
+   RESULT_EQ is true if we can relax the returned value to be either zero
+   or nonzero, without caring about the sign.  */
 
 static rtx
-expand_builtin_memcmp (tree exp, rtx target)
+expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
 {
   if (!validate_arglist (exp,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  /* Note: The cmpstrnsi pattern, if it exists, is not suitable for
-     implementing memcmp because it will stop if it encounters two
-     zero bytes.  */
-  insn_code icode = direct_optab_handler (cmpmem_optab, SImode);
-  if (icode == CODE_FOR_nothing)
-    return NULL_RTX;
-
   tree arg1 = CALL_EXPR_ARG (exp, 0);
   tree arg2 = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
+  machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+  location_t loc = EXPR_LOCATION (exp);
 
   unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
   unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
@@ -3981,22 +4197,40 @@ expand_builtin_memcmp (tree exp, rtx target)
   if (arg1_align == 0 || arg2_align == 0)
     return NULL_RTX;
 
-  machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-  location_t loc = EXPR_LOCATION (exp);
   rtx arg1_rtx = get_memory_rtx (arg1, len);
   rtx arg2_rtx = get_memory_rtx (arg2, len);
-  rtx arg3_rtx = expand_normal (fold_convert_loc (loc, sizetype, len));
+  rtx len_rtx = expand_normal (fold_convert_loc (loc, sizetype, len));
 
   /* Set MEM_SIZE as appropriate.  */
-  if (CONST_INT_P (arg3_rtx))
+  if (CONST_INT_P (len_rtx))
+    {
+      set_mem_size (arg1_rtx, INTVAL (len_rtx));
+      set_mem_size (arg2_rtx, INTVAL (len_rtx));
+    }
+
+  by_pieces_constfn constfn = NULL;
+
+  const char *src_str = c_getstr (arg2);
+  if (result_eq && src_str == NULL)
     {
-      set_mem_size (arg1_rtx, INTVAL (arg3_rtx));
-      set_mem_size (arg2_rtx, INTVAL (arg3_rtx));
+      src_str = c_getstr (arg1);
+      if (src_str != NULL)
+       std::swap (arg1_rtx, arg2_rtx);
     }
 
-  rtx result = expand_cmpstrn_or_cmpmem (icode, target, arg1_rtx, arg2_rtx,
-                                        TREE_TYPE (len), arg3_rtx,
-                                        MIN (arg1_align, arg2_align));
+  /* If SRC is a string constant and block move would be done
+     by pieces, we can avoid loading the string from memory
+     and only stored the computed constants.  */
+  if (src_str
+      && CONST_INT_P (len_rtx)
+      && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1)
+    constfn = builtin_memcpy_read_str;
+
+  rtx result = emit_block_cmp_hints (arg1_rtx, arg2_rtx, len_rtx,
+                                    TREE_TYPE (len), target,
+                                    result_eq, constfn,
+                                    CONST_CAST (char *, src_str));
+
   if (result)
     {
       /* Return the value in the proper mode for this function.  */
@@ -4012,20 +4246,7 @@ expand_builtin_memcmp (tree exp, rtx target)
       return convert_to_mode (mode, result, 0);
     }
 
-  result = target;
-  if (! (result != 0
-        && REG_P (result) && GET_MODE (result) == mode
-        && REGNO (result) >= FIRST_PSEUDO_REGISTER))
-    result = gen_reg_rtx (mode);
-
-  emit_library_call_value (memcmp_libfunc, result, LCT_PURE,
-                          TYPE_MODE (integer_type_node), 3,
-                          XEXP (arg1_rtx, 0), Pmode,
-                          XEXP (arg2_rtx, 0), Pmode,
-                          convert_to_mode (TYPE_MODE (sizetype), arg3_rtx,
-                                           TYPE_UNSIGNED (sizetype)),
-                          TYPE_MODE (sizetype));
-  return result;
+  return NULL_RTX;
 }
 
 /* Expand expression EXP, which is a call to the strcmp builtin.  Return NULL_RTX
@@ -4158,7 +4379,7 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
   insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
   if (cmpstrn_icode != CODE_FOR_nothing)
   {
-    tree len, len1, len2;
+    tree len, len1, len2, len3;
     rtx arg1_rtx, arg2_rtx, arg3_rtx;
     rtx result;
     tree fndecl, fn;
@@ -4177,14 +4398,19 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
     if (len2)
       len2 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len2);
 
+    len3 = fold_convert_loc (loc, sizetype, arg3);
+
     /* If we don't have a constant length for the first, use the length
-       of the second, if we know it.  We don't require a constant for
+       of the second, if we know it.  If neither string is constant length,
+       use the given length argument.  We don't require a constant for
        this case; some cost analysis could be done if both are available
        but neither is constant.  For now, assume they're equally cheap,
        unless one has side effects.  If both strings have constant lengths,
        use the smaller.  */
 
-    if (!len1)
+    if (!len1 && !len2)
+      len = len3;
+    else if (!len1)
       len = len2;
     else if (!len2)
       len = len1;
@@ -4201,23 +4427,10 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
     else
       len = len2;
 
-    /* If both arguments have side effects, we cannot optimize.  */
-    if (!len || TREE_SIDE_EFFECTS (len))
-      return NULL_RTX;
-
-    /* The actual new length parameter is MIN(len,arg3).  */
-    len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len,
-                      fold_convert_loc (loc, TREE_TYPE (len), arg3));
-
-    /* If we don't have POINTER_TYPE, call the function.  */
-    if (arg1_align == 0 || arg2_align == 0)
-      return NULL_RTX;
-
-    /* Stabilize the arguments in case gen_cmpstrnsi fails.  */
-    arg1 = builtin_save_expr (arg1);
-    arg2 = builtin_save_expr (arg2);
-    len = builtin_save_expr (len);
-
+    /* If we are not using the given length, we must incorporate it here.
+       The actual new length parameter will be MIN(len,arg3) in this case.  */
+    if (len != len3)
+      len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, len3);
     arg1_rtx = get_memory_rtx (arg1, len);
     arg2_rtx = get_memory_rtx (arg2, len);
     arg3_rtx = expand_normal (len);
@@ -4372,16 +4585,10 @@ std_canonical_va_list_type (tree type)
 {
   tree wtype, htype;
 
-  if (INDIRECT_REF_P (type))
-    type = TREE_TYPE (type);
-  else if (POINTER_TYPE_P (type) && POINTER_TYPE_P (TREE_TYPE (type)))
-    type = TREE_TYPE (type);
   wtype = va_list_type_node;
   htype = type;
-  /* Treat structure va_list types.  */
-  if (TREE_CODE (wtype) == RECORD_TYPE && POINTER_TYPE_P (htype))
-    htype = TREE_TYPE (htype);
-  else if (TREE_CODE (wtype) == ARRAY_TYPE)
+
+  if (TREE_CODE (wtype) == ARRAY_TYPE)
     {
       /* If va_list is an array type, the argument may have decayed
         to a pointer type, e.g. by being passed to another function.
@@ -4573,12 +4780,12 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate)
 {
   rtx op0;
   rtx result;
-  bool valid_arglist;
   unsigned int align;
-  bool alloca_with_align = (DECL_FUNCTION_CODE (get_callee_fndecl (exp))
+  tree fndecl = get_callee_fndecl (exp);
+  bool alloca_with_align = (DECL_FUNCTION_CODE (fndecl)
                            == BUILT_IN_ALLOCA_WITH_ALIGN);
 
-  valid_arglist
+  bool valid_arglist
     = (alloca_with_align
        ? validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)
        : validate_arglist (exp, INTEGER_TYPE, VOID_TYPE));
@@ -4586,6 +4793,18 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate)
   if (!valid_arglist)
     return NULL_RTX;
 
+  if ((alloca_with_align && !warn_vla_limit)
+      || (!alloca_with_align && !warn_alloca_limit))
+    {
+      /* -Walloca-larger-than and -Wvla-larger-than settings override
+        the more general -Walloc-size-larger-than so unless either of
+        the former options is specified check the alloca arguments for
+        overflow.  */
+      tree args[] = { CALL_EXPR_ARG (exp, 0), NULL_TREE };
+      int idx[] = { 0, -1 };
+      maybe_warn_alloc_args_overflow (fndecl, exp, args, idx);
+    }
+
   /* Compute the argument.  */
   op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
 
@@ -4710,7 +4929,12 @@ expand_builtin_trap (void)
        add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta));
     }
   else
-    emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0);
+    {
+      tree fn = builtin_decl_implicit (BUILT_IN_ABORT);
+      tree call_expr = build_call_expr (fn, 0);
+      expand_call (call_expr, NULL_RTX, false);
+    }
+
   emit_barrier ();
 }
 
@@ -4896,8 +5120,9 @@ expand_builtin_init_trampoline (tree exp, bool onstack)
     {
       trampolines_created = 1;
 
-      warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
-                 "trampoline generated for nested function %qD", t_func);
+      if (targetm.calls.custom_function_descriptors != 0)
+       warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
+                   "trampoline generated for nested function %qD", t_func);
     }
 
   return const0_rtx;
@@ -4919,6 +5144,58 @@ expand_builtin_adjust_trampoline (tree exp)
   return tramp;
 }
 
+/* Expand a call to the builtin descriptor initialization routine.
+   A descriptor is made up of a couple of pointers to the static
+   chain and the code entry in this order.  */
+
+static rtx
+expand_builtin_init_descriptor (tree exp)
+{
+  tree t_descr, t_func, t_chain;
+  rtx m_descr, r_descr, r_func, r_chain;
+
+  if (!validate_arglist (exp, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE,
+                        VOID_TYPE))
+    return NULL_RTX;
+
+  t_descr = CALL_EXPR_ARG (exp, 0);
+  t_func = CALL_EXPR_ARG (exp, 1);
+  t_chain = CALL_EXPR_ARG (exp, 2);
+
+  r_descr = expand_normal (t_descr);
+  m_descr = gen_rtx_MEM (BLKmode, r_descr);
+  MEM_NOTRAP_P (m_descr) = 1;
+
+  r_func = expand_normal (t_func);
+  r_chain = expand_normal (t_chain);
+
+  /* Generate insns to initialize the descriptor.  */
+  emit_move_insn (adjust_address_nv (m_descr, ptr_mode, 0), r_chain);
+  emit_move_insn (adjust_address_nv (m_descr, ptr_mode,
+                                    POINTER_SIZE / BITS_PER_UNIT), r_func);
+
+  return const0_rtx;
+}
+
+/* Expand a call to the builtin descriptor adjustment routine.  */
+
+static rtx
+expand_builtin_adjust_descriptor (tree exp)
+{
+  rtx tramp;
+
+  if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  tramp = expand_normal (CALL_EXPR_ARG (exp, 0));
+
+  /* Unalign the descriptor to allow runtime identification.  */
+  tramp = plus_constant (ptr_mode, tramp,
+                        targetm.calls.custom_function_descriptors);
+
+  return force_operand (tramp, NULL_RTX);
+}
+
 /* Expand the call EXP to the built-in signbit, signbitf or signbitl
    function.  The function first checks whether the back end provides
    an insn to implement signbit for the respective mode.  If not, it
@@ -5292,6 +5569,8 @@ get_memmodel (tree exp)
 {
   rtx op;
   unsigned HOST_WIDE_INT val;
+  source_location loc
+    = expansion_point_location_if_in_system_header (input_location);
 
   /* If the parameter is not a constant, it's a run time value so we'll just
      convert it to MEMMODEL_SEQ_CST to avoid annoying runtime checking.  */
@@ -5305,16 +5584,16 @@ get_memmodel (tree exp)
     val = targetm.memmodel_check (val);
   else if (val & ~MEMMODEL_MASK)
     {
-      warning (OPT_Winvalid_memory_model,
-              "Unknown architecture specifier in memory model to builtin.");
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "unknown architecture specifier in memory model to builtin");
       return MEMMODEL_SEQ_CST;
     }
 
   /* Should never see a user explicit SYNC memodel model, so >= LAST works. */
   if (memmodel_base (val) >= MEMMODEL_LAST)
     {
-      warning (OPT_Winvalid_memory_model,
-              "invalid memory model argument to builtin");
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model argument to builtin");
       return MEMMODEL_SEQ_CST;
     }
 
@@ -5366,23 +5645,25 @@ expand_builtin_atomic_compare_exchange (machine_mode mode, tree exp,
   enum memmodel success, failure;
   tree weak;
   bool is_weak;
+  source_location loc
+    = expansion_point_location_if_in_system_header (input_location);
 
   success = get_memmodel (CALL_EXPR_ARG (exp, 4));
   failure = get_memmodel (CALL_EXPR_ARG (exp, 5));
 
   if (failure > success)
     {
-      warning (OPT_Winvalid_memory_model,
-              "failure memory model cannot be stronger than success memory "
-              "model for %<__atomic_compare_exchange%>");
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "failure memory model cannot be stronger than success "
+                 "memory model for %<__atomic_compare_exchange%>");
       success = MEMMODEL_SEQ_CST;
     }
  
   if (is_mm_release (failure) || is_mm_acq_rel (failure))
     {
-      warning (OPT_Winvalid_memory_model,
-              "invalid failure memory model for "
-              "%<__atomic_compare_exchange%>");
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid failure memory model for "
+                 "%<__atomic_compare_exchange%>");
       failure = MEMMODEL_SEQ_CST;
       success = MEMMODEL_SEQ_CST;
     }
@@ -5429,24 +5710,143 @@ expand_builtin_atomic_compare_exchange (machine_mode mode, tree exp,
   return target;
 }
 
-/* Expand the __atomic_load intrinsic:
-       TYPE __atomic_load (TYPE *object, enum memmodel)
-   EXP is the CALL_EXPR.
-   TARGET is an optional place for us to store the results.  */
+/* Helper function for expand_ifn_atomic_compare_exchange - expand
+   internal ATOMIC_COMPARE_EXCHANGE call into __atomic_compare_exchange_N
+   call.  The weak parameter must be dropped to match the expected parameter
+   list and the expected argument changed from value to pointer to memory
+   slot.  */
 
-static rtx
-expand_builtin_atomic_load (machine_mode mode, tree exp, rtx target)
+static void
+expand_ifn_atomic_compare_exchange_into_call (gcall *call, machine_mode mode)
+{
+  unsigned int z;
+  vec<tree, va_gc> *vec;
+
+  vec_alloc (vec, 5);
+  vec->quick_push (gimple_call_arg (call, 0));
+  tree expected = gimple_call_arg (call, 1);
+  rtx x = assign_stack_temp_for_type (mode, GET_MODE_SIZE (mode),
+                                     TREE_TYPE (expected));
+  rtx expd = expand_expr (expected, x, mode, EXPAND_NORMAL);
+  if (expd != x)
+    emit_move_insn (x, expd);
+  tree v = make_tree (TREE_TYPE (expected), x);
+  vec->quick_push (build1 (ADDR_EXPR,
+                          build_pointer_type (TREE_TYPE (expected)), v));
+  vec->quick_push (gimple_call_arg (call, 2));
+  /* Skip the boolean weak parameter.  */
+  for (z = 4; z < 6; z++)
+    vec->quick_push (gimple_call_arg (call, z));
+  built_in_function fncode
+    = (built_in_function) ((int) BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1
+                          + exact_log2 (GET_MODE_SIZE (mode)));
+  tree fndecl = builtin_decl_explicit (fncode);
+  tree fn = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fndecl)),
+                   fndecl);
+  tree exp = build_call_vec (boolean_type_node, fn, vec);
+  tree lhs = gimple_call_lhs (call);
+  rtx boolret = expand_call (exp, NULL_RTX, lhs == NULL_TREE);
+  if (lhs)
+    {
+      rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+      if (GET_MODE (boolret) != mode)
+       boolret = convert_modes (mode, GET_MODE (boolret), boolret, 1);
+      x = force_reg (mode, x);
+      write_complex_part (target, boolret, true);
+      write_complex_part (target, x, false);
+    }
+}
+
+/* Expand IFN_ATOMIC_COMPARE_EXCHANGE internal function.  */
+
+void
+expand_ifn_atomic_compare_exchange (gcall *call)
 {
-  rtx mem;
-  enum memmodel model;
+  int size = tree_to_shwi (gimple_call_arg (call, 3)) & 255;
+  gcc_assert (size == 1 || size == 2 || size == 4 || size == 8 || size == 16);
+  machine_mode mode = mode_for_size (BITS_PER_UNIT * size, MODE_INT, 0);
+  rtx expect, desired, mem, oldval, boolret;
+  enum memmodel success, failure;
+  tree lhs;
+  bool is_weak;
+  source_location loc
+    = expansion_point_location_if_in_system_header (gimple_location (call));
 
-  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
-  if (is_mm_release (model) || is_mm_acq_rel (model))
-    {
-      warning (OPT_Winvalid_memory_model,
-              "invalid memory model for %<__atomic_load%>");
-      model = MEMMODEL_SEQ_CST;
-    }
+  success = get_memmodel (gimple_call_arg (call, 4));
+  failure = get_memmodel (gimple_call_arg (call, 5));
+
+  if (failure > success)
+    {
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "failure memory model cannot be stronger than success "
+                 "memory model for %<__atomic_compare_exchange%>");
+      success = MEMMODEL_SEQ_CST;
+    }
+
+  if (is_mm_release (failure) || is_mm_acq_rel (failure))
+    {
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid failure memory model for "
+                 "%<__atomic_compare_exchange%>");
+      failure = MEMMODEL_SEQ_CST;
+      success = MEMMODEL_SEQ_CST;
+    }
+
+  if (!flag_inline_atomics)
+    {
+      expand_ifn_atomic_compare_exchange_into_call (call, mode);
+      return;
+    }
+
+  /* Expand the operands.  */
+  mem = get_builtin_sync_mem (gimple_call_arg (call, 0), mode);
+
+  expect = expand_expr_force_mode (gimple_call_arg (call, 1), mode);
+  desired = expand_expr_force_mode (gimple_call_arg (call, 2), mode);
+
+  is_weak = (tree_to_shwi (gimple_call_arg (call, 3)) & 256) != 0;
+
+  boolret = NULL;
+  oldval = NULL;
+
+  if (!expand_atomic_compare_and_swap (&boolret, &oldval, mem, expect, desired,
+                                      is_weak, success, failure))
+    {
+      expand_ifn_atomic_compare_exchange_into_call (call, mode);
+      return;
+    }
+
+  lhs = gimple_call_lhs (call);
+  if (lhs)
+    {
+      rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+      if (GET_MODE (boolret) != mode)
+       boolret = convert_modes (mode, GET_MODE (boolret), boolret, 1);
+      write_complex_part (target, boolret, true);
+      write_complex_part (target, oldval, false);
+    }
+}
+
+/* Expand the __atomic_load intrinsic:
+       TYPE __atomic_load (TYPE *object, enum memmodel)
+   EXP is the CALL_EXPR.
+   TARGET is an optional place for us to store the results.  */
+
+static rtx
+expand_builtin_atomic_load (machine_mode mode, tree exp, rtx target)
+{
+  rtx mem;
+  enum memmodel model;
+
+  model = get_memmodel (CALL_EXPR_ARG (exp, 1));
+  if (is_mm_release (model) || is_mm_acq_rel (model))
+    {
+      source_location loc
+       = expansion_point_location_if_in_system_header (input_location);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model for %<__atomic_load%>");
+      model = MEMMODEL_SEQ_CST;
+    }
 
   if (!flag_inline_atomics)
     return NULL_RTX;
@@ -5473,8 +5873,10 @@ expand_builtin_atomic_store (machine_mode mode, tree exp)
   if (!(is_mm_relaxed (model) || is_mm_seq_cst (model)
        || is_mm_release (model)))
     {
-      warning (OPT_Winvalid_memory_model,
-              "invalid memory model for %<__atomic_store%>");
+      source_location loc
+       = expansion_point_location_if_in_system_header (input_location);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model for %<__atomic_store%>");
       model = MEMMODEL_SEQ_CST;
     }
 
@@ -5557,6 +5959,90 @@ expand_builtin_atomic_fetch_op (machine_mode mode, tree exp, rtx target,
   return ret;
 }
 
+/* Expand IFN_ATOMIC_BIT_TEST_AND_* internal function.  */
+
+void
+expand_ifn_atomic_bit_test_and (gcall *call)
+{
+  tree ptr = gimple_call_arg (call, 0);
+  tree bit = gimple_call_arg (call, 1);
+  tree flag = gimple_call_arg (call, 2);
+  tree lhs = gimple_call_lhs (call);
+  enum memmodel model = MEMMODEL_SYNC_SEQ_CST;
+  machine_mode mode = TYPE_MODE (TREE_TYPE (flag));
+  enum rtx_code code;
+  optab optab;
+  struct expand_operand ops[5];
+
+  gcc_assert (flag_inline_atomics);
+
+  if (gimple_call_num_args (call) == 4)
+    model = get_memmodel (gimple_call_arg (call, 3));
+
+  rtx mem = get_builtin_sync_mem (ptr, mode);
+  rtx val = expand_expr_force_mode (bit, mode);
+
+  switch (gimple_call_internal_fn (call))
+    {
+    case IFN_ATOMIC_BIT_TEST_AND_SET:
+      code = IOR;
+      optab = atomic_bit_test_and_set_optab;
+      break;
+    case IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT:
+      code = XOR;
+      optab = atomic_bit_test_and_complement_optab;
+      break;
+    case IFN_ATOMIC_BIT_TEST_AND_RESET:
+      code = AND;
+      optab = atomic_bit_test_and_reset_optab;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  if (lhs == NULL_TREE)
+    {
+      val = expand_simple_binop (mode, ASHIFT, const1_rtx,
+                                val, NULL_RTX, true, OPTAB_DIRECT);
+      if (code == AND)
+       val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
+      expand_atomic_fetch_op (const0_rtx, mem, val, code, model, false);
+      return;
+    }
+
+  rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+  enum insn_code icode = direct_optab_handler (optab, mode);
+  gcc_assert (icode != CODE_FOR_nothing);
+  create_output_operand (&ops[0], target, mode);
+  create_fixed_operand (&ops[1], mem);
+  create_convert_operand_to (&ops[2], val, mode, true);
+  create_integer_operand (&ops[3], model);
+  create_integer_operand (&ops[4], integer_onep (flag));
+  if (maybe_expand_insn (icode, 5, ops))
+    return;
+
+  rtx bitval = val;
+  val = expand_simple_binop (mode, ASHIFT, const1_rtx,
+                            val, NULL_RTX, true, OPTAB_DIRECT);
+  rtx maskval = val;
+  if (code == AND)
+    val = expand_simple_unop (mode, NOT, val, NULL_RTX, true);
+  rtx result = expand_atomic_fetch_op (gen_reg_rtx (mode), mem, val,
+                                      code, model, false);
+  if (integer_onep (flag))
+    {
+      result = expand_simple_binop (mode, ASHIFTRT, result, bitval,
+                                   NULL_RTX, true, OPTAB_DIRECT);
+      result = expand_simple_binop (mode, AND, result, const1_rtx, target,
+                                   true, OPTAB_DIRECT);
+    }
+  else
+    result = expand_simple_binop (mode, AND, result, maskval, target, true,
+                                 OPTAB_DIRECT);
+  if (result != target)
+    emit_move_insn (target, result);
+}
+
 /* Expand an atomic clear operation.
        void _atomic_clear (BOOL *obj, enum memmodel)
    EXP is the call expression.  */
@@ -5574,8 +6060,10 @@ expand_builtin_atomic_clear (tree exp)
 
   if (is_mm_consume (model) || is_mm_acquire (model) || is_mm_acq_rel (model))
     {
-      warning (OPT_Winvalid_memory_model,
-              "invalid memory model for %<__atomic_store%>");
+      source_location loc
+       = expansion_point_location_if_in_system_header (input_location);
+      warning_at (loc, OPT_Winvalid_memory_model,
+                 "invalid memory model for %<__atomic_store%>");
       model = MEMMODEL_SEQ_CST;
     }
 
@@ -5632,7 +6120,7 @@ fold_builtin_atomic_always_lock_free (tree arg0, tree arg1)
 
       /* Either this argument is null, or it's a fake pointer encoding
          the alignment of the object.  */
-      val = val & -val;
+      val = least_bit_hwi (val);
       val *= BITS_PER_UNIT;
 
       if (val == 0 || mode_align < val)
@@ -5648,8 +6136,10 @@ fold_builtin_atomic_always_lock_free (tree arg0, tree arg1)
         end before anything else has a chance to look at it.  The pointer
         parameter at this point is usually cast to a void *, so check for that
         and look past the cast.  */
-      if (CONVERT_EXPR_P (arg1) && POINTER_TYPE_P (ttype)
-         && VOID_TYPE_P (TREE_TYPE (ttype)))
+      if (CONVERT_EXPR_P (arg1)
+         && POINTER_TYPE_P (ttype)
+         && VOID_TYPE_P (TREE_TYPE (ttype))
+         && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (arg1, 0))))
        arg1 = TREE_OPERAND (arg1, 0);
 
       ttype = TREE_TYPE (arg1);
@@ -5942,6 +6432,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
   switch (fcode)
     {
     CASE_FLT_FN (BUILT_IN_FABS):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FABS):
     case BUILT_IN_FABSD32:
     case BUILT_IN_FABSD64:
     case BUILT_IN_FABSD128:
@@ -5951,6 +6442,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
       break;
 
     CASE_FLT_FN (BUILT_IN_COPYSIGN):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_COPYSIGN):
       target = expand_builtin_copysign (exp, target, subtarget);
       if (target)
        return target;
@@ -5961,37 +6453,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
     CASE_FLT_FN (BUILT_IN_CABS):
       break;
 
-    CASE_FLT_FN (BUILT_IN_EXP):
-    CASE_FLT_FN (BUILT_IN_EXP10):
-    CASE_FLT_FN (BUILT_IN_POW10):
-    CASE_FLT_FN (BUILT_IN_EXP2):
-    CASE_FLT_FN (BUILT_IN_EXPM1):
-    CASE_FLT_FN (BUILT_IN_LOGB):
-    CASE_FLT_FN (BUILT_IN_LOG):
-    CASE_FLT_FN (BUILT_IN_LOG10):
-    CASE_FLT_FN (BUILT_IN_LOG2):
-    CASE_FLT_FN (BUILT_IN_LOG1P):
-    CASE_FLT_FN (BUILT_IN_TAN):
-    CASE_FLT_FN (BUILT_IN_ASIN):
-    CASE_FLT_FN (BUILT_IN_ACOS):
-    CASE_FLT_FN (BUILT_IN_ATAN):
-    CASE_FLT_FN (BUILT_IN_SIGNIFICAND):
-      /* Treat these like sqrt only if unsafe math optimizations are allowed,
-        because of possible accuracy problems.  */
-      if (! flag_unsafe_math_optimizations)
-       break;
-    CASE_FLT_FN (BUILT_IN_SQRT):
-    CASE_FLT_FN (BUILT_IN_FLOOR):
-    CASE_FLT_FN (BUILT_IN_CEIL):
-    CASE_FLT_FN (BUILT_IN_TRUNC):
-    CASE_FLT_FN (BUILT_IN_ROUND):
-    CASE_FLT_FN (BUILT_IN_NEARBYINT):
-    CASE_FLT_FN (BUILT_IN_RINT):
-      target = expand_builtin_mathfn (exp, target, subtarget);
-      if (target)
-       return target;
-      break;
-
     CASE_FLT_FN (BUILT_IN_FMA):
       target = expand_builtin_mathfn_ternary (exp, target, subtarget);
       if (target)
@@ -6001,6 +6462,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
     CASE_FLT_FN (BUILT_IN_ILOGB):
       if (! flag_unsafe_math_optimizations)
        break;
+      gcc_fallthrough ();
     CASE_FLT_FN (BUILT_IN_ISINF):
     CASE_FLT_FN (BUILT_IN_FINITE):
     case BUILT_IN_ISFINITE:
@@ -6038,23 +6500,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
        return target;
       break;
 
-    CASE_FLT_FN (BUILT_IN_ATAN2):
-    CASE_FLT_FN (BUILT_IN_LDEXP):
-    CASE_FLT_FN (BUILT_IN_SCALB):
-    CASE_FLT_FN (BUILT_IN_SCALBN):
-    CASE_FLT_FN (BUILT_IN_SCALBLN):
-      if (! flag_unsafe_math_optimizations)
-       break;
-
-    CASE_FLT_FN (BUILT_IN_FMOD):
-    CASE_FLT_FN (BUILT_IN_REMAINDER):
-    CASE_FLT_FN (BUILT_IN_DREM):
-    CASE_FLT_FN (BUILT_IN_POW):
-      target = expand_builtin_mathfn_2 (exp, target, subtarget);
-      if (target)
-       return target;
-      break;
-
     CASE_FLT_FN (BUILT_IN_CEXPI):
       target = expand_builtin_cexpi (exp, target);
       gcc_assert (target);
@@ -6234,12 +6679,24 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
        return target;
       break;
 
+    case BUILT_IN_STRCAT:
+      target = expand_builtin_strcat (exp, target);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_STRCPY:
       target = expand_builtin_strcpy (exp, target);
       if (target)
        return target;
       break;
 
+    case BUILT_IN_STRNCAT:
+      target = expand_builtin_strncat (exp, target);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_STRNCPY:
       target = expand_builtin_strncpy (exp, target);
       if (target)
@@ -6290,9 +6747,15 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
 
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
-      target = expand_builtin_memcmp (exp, target);
+    case BUILT_IN_MEMCMP_EQ:
+      target = expand_builtin_memcmp (exp, target, fcode == BUILT_IN_MEMCMP_EQ);
       if (target)
        return target;
+      if (fcode == BUILT_IN_MEMCMP_EQ)
+       {
+         tree newdecl = builtin_decl_explicit (BUILT_IN_MEMCMP);
+         TREE_OPERAND (exp, 1) = build_fold_addr_expr (newdecl);
+       }
       break;
 
     case BUILT_IN_SETJMP:
@@ -6444,6 +6907,11 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
     case BUILT_IN_ADJUST_TRAMPOLINE:
       return expand_builtin_adjust_trampoline (exp);
 
+    case BUILT_IN_INIT_DESCRIPTOR:
+      return expand_builtin_init_descriptor (exp);
+    case BUILT_IN_ADJUST_DESCRIPTOR:
+      return expand_builtin_adjust_descriptor (exp);
+
     case BUILT_IN_FORK:
     case BUILT_IN_EXECL:
     case BUILT_IN_EXECV:
@@ -7250,9 +7718,7 @@ fold_builtin_expect (location_t loc, tree arg0, tree arg1, tree arg2)
        }
       while (TREE_CODE (inner) == COMPONENT_REF
             || TREE_CODE (inner) == ARRAY_REF);
-      if ((TREE_CODE (inner) == VAR_DECL
-           || TREE_CODE (inner) == FUNCTION_DECL)
-         && DECL_WEAK (inner))
+      if (VAR_OR_FUNCTION_DECL_P (inner) && DECL_WEAK (inner))
        return NULL_TREE;
     }
 
@@ -7310,1662 +7776,192 @@ fold_builtin_inf (location_t loc, tree type, int warn)
   return build_real (type, real);
 }
 
-/* Fold a call to __builtin_nan or __builtin_nans with argument ARG.  */
+/* Fold function call to builtin sincos, sincosf, or sincosl.  Return
+   NULL_TREE if no simplification can be made.  */
 
 static tree
-fold_builtin_nan (tree arg, tree type, int quiet)
+fold_builtin_sincos (location_t loc,
+                    tree arg0, tree arg1, tree arg2)
 {
-  REAL_VALUE_TYPE real;
-  const char *str;
-
-  if (!validate_arg (arg, POINTER_TYPE))
-    return NULL_TREE;
-  str = c_getstr (arg);
-  if (!str)
-    return NULL_TREE;
+  tree type;
+  tree fndecl, call = NULL_TREE;
 
-  if (!real_nan (&real, str, quiet, TYPE_MODE (type)))
+  if (!validate_arg (arg0, REAL_TYPE)
+      || !validate_arg (arg1, POINTER_TYPE)
+      || !validate_arg (arg2, POINTER_TYPE))
     return NULL_TREE;
 
-  return build_real (type, real);
-}
+  type = TREE_TYPE (arg0);
 
-/* Return true if the floating point expression T has an integer value.
-   We also allow +Inf, -Inf and NaN to be considered integer values.  */
+  /* Calculate the result when the argument is a constant.  */
+  built_in_function fn = mathfn_built_in_2 (type, CFN_BUILT_IN_CEXPI);
+  if (fn == END_BUILTINS)
+    return NULL_TREE;
 
-static bool
-integer_valued_real_p (tree t)
-{
-  switch (TREE_CODE (t))
+  /* Canonicalize sincos to cexpi.  */
+  if (TREE_CODE (arg0) == REAL_CST)
     {
-    case FLOAT_EXPR:
-      return true;
+      tree complex_type = build_complex_type (type);
+      call = fold_const_call (as_combined_fn (fn), complex_type, arg0);
+    }
+  if (!call)
+    {
+      if (!targetm.libc_has_function (function_c99_math_complex)
+         || !builtin_decl_implicit_p (fn))
+       return NULL_TREE;
+      fndecl = builtin_decl_explicit (fn);
+      call = build_call_expr_loc (loc, fndecl, 1, arg0);
+      call = builtin_save_expr (call);
+    }
 
-    case ABS_EXPR:
-    case SAVE_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 0));
+  return build2 (COMPOUND_EXPR, void_type_node,
+                build2 (MODIFY_EXPR, void_type_node,
+                        build_fold_indirect_ref_loc (loc, arg1),
+                        fold_build1_loc (loc, IMAGPART_EXPR, type, call)),
+                build2 (MODIFY_EXPR, void_type_node,
+                        build_fold_indirect_ref_loc (loc, arg2),
+                        fold_build1_loc (loc, REALPART_EXPR, type, call)));
+}
 
-    case COMPOUND_EXPR:
-    case MODIFY_EXPR:
-    case BIND_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 1));
+/* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
+   Return NULL_TREE if no simplification can be made.  */
 
-    case PLUS_EXPR:
-    case MINUS_EXPR:
-    case MULT_EXPR:
-    case MIN_EXPR:
-    case MAX_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 0))
-            && integer_valued_real_p (TREE_OPERAND (t, 1));
+static tree
+fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
+{
+  if (!validate_arg (arg1, POINTER_TYPE)
+      || !validate_arg (arg2, POINTER_TYPE)
+      || !validate_arg (len, INTEGER_TYPE))
+    return NULL_TREE;
 
-    case COND_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 1))
-            && integer_valued_real_p (TREE_OPERAND (t, 2));
+  /* If the LEN parameter is zero, return zero.  */
+  if (integer_zerop (len))
+    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
+                             arg1, arg2);
 
-    case REAL_CST:
-      return real_isinteger (TREE_REAL_CST_PTR (t), TYPE_MODE (TREE_TYPE (t)));
+  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
 
-    CASE_CONVERT:
-      {
-       tree type = TREE_TYPE (TREE_OPERAND (t, 0));
-       if (TREE_CODE (type) == INTEGER_TYPE)
-         return true;
-       if (TREE_CODE (type) == REAL_TYPE)
-         return integer_valued_real_p (TREE_OPERAND (t, 0));
-       break;
-      }
+  /* If len parameter is one, return an expression corresponding to
+     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
+  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
+    {
+      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
+      tree cst_uchar_ptr_node
+       = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
 
-    case CALL_EXPR:
-      switch (builtin_mathfn_code (t))
-       {
-       CASE_FLT_FN (BUILT_IN_CEIL):
-       CASE_FLT_FN (BUILT_IN_FLOOR):
-       CASE_FLT_FN (BUILT_IN_NEARBYINT):
-       CASE_FLT_FN (BUILT_IN_RINT):
-       CASE_FLT_FN (BUILT_IN_ROUND):
-       CASE_FLT_FN (BUILT_IN_TRUNC):
-         return true;
+      tree ind1
+       = fold_convert_loc (loc, integer_type_node,
+                           build1 (INDIRECT_REF, cst_uchar_node,
+                                   fold_convert_loc (loc,
+                                                     cst_uchar_ptr_node,
+                                                     arg1)));
+      tree ind2
+       = fold_convert_loc (loc, integer_type_node,
+                           build1 (INDIRECT_REF, cst_uchar_node,
+                                   fold_convert_loc (loc,
+                                                     cst_uchar_ptr_node,
+                                                     arg2)));
+      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
+    }
 
-       CASE_FLT_FN (BUILT_IN_FMIN):
-       CASE_FLT_FN (BUILT_IN_FMAX):
-         return integer_valued_real_p (CALL_EXPR_ARG (t, 0))
-           && integer_valued_real_p (CALL_EXPR_ARG (t, 1));
+  return NULL_TREE;
+}
 
-       default:
-         break;
-       }
-      break;
+/* Fold a call to builtin isascii with argument ARG.  */
 
-    default:
-      break;
+static tree
+fold_builtin_isascii (location_t loc, tree arg)
+{
+  if (!validate_arg (arg, INTEGER_TYPE))
+    return NULL_TREE;
+  else
+    {
+      /* Transform isascii(c) -> ((c & ~0x7f) == 0).  */
+      arg = fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
+                        build_int_cst (integer_type_node,
+                                       ~ (unsigned HOST_WIDE_INT) 0x7f));
+      return fold_build2_loc (loc, EQ_EXPR, integer_type_node,
+                             arg, integer_zero_node);
     }
-  return false;
 }
 
-/* FNDECL is assumed to be a builtin where truncation can be propagated
-   across (for instance floor((double)f) == (double)floorf (f).
-   Do the transformation for a call with argument ARG.  */
+/* Fold a call to builtin toascii with argument ARG.  */
 
 static tree
-fold_trunc_transparent_mathfn (location_t loc, tree fndecl, tree arg)
+fold_builtin_toascii (location_t loc, tree arg)
 {
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  if (!validate_arg (arg, REAL_TYPE))
+  if (!validate_arg (arg, INTEGER_TYPE))
     return NULL_TREE;
 
-  /* Integer rounding functions are idempotent.  */
-  if (fcode == builtin_mathfn_code (arg))
-    return arg;
+  /* Transform toascii(c) -> (c & 0x7f).  */
+  return fold_build2_loc (loc, BIT_AND_EXPR, integer_type_node, arg,
+                         build_int_cst (integer_type_node, 0x7f));
+}
 
-  /* If argument is already integer valued, and we don't need to worry
-     about setting errno, there's no need to perform rounding.  */
-  if (! flag_errno_math && integer_valued_real_p (arg))
-    return arg;
+/* Fold a call to builtin isdigit with argument ARG.  */
 
-  if (optimize)
+static tree
+fold_builtin_isdigit (location_t loc, tree arg)
+{
+  if (!validate_arg (arg, INTEGER_TYPE))
+    return NULL_TREE;
+  else
     {
-      tree arg0 = strip_float_extensions (arg);
-      tree ftype = TREE_TYPE (TREE_TYPE (fndecl));
-      tree newtype = TREE_TYPE (arg0);
-      tree decl;
+      /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9.  */
+      /* According to the C standard, isdigit is unaffected by locale.
+        However, it definitely is affected by the target character set.  */
+      unsigned HOST_WIDE_INT target_digit0
+       = lang_hooks.to_target_charset ('0');
+
+      if (target_digit0 == 0)
+       return NULL_TREE;
 
-      if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
-         && (decl = mathfn_built_in (newtype, fcode)))
-       return fold_convert_loc (loc, ftype,
-                                build_call_expr_loc (loc, decl, 1,
-                                                 fold_convert_loc (loc,
-                                                                   newtype,
-                                                                   arg0)));
+      arg = fold_convert_loc (loc, unsigned_type_node, arg);
+      arg = fold_build2 (MINUS_EXPR, unsigned_type_node, arg,
+                        build_int_cst (unsigned_type_node, target_digit0));
+      return fold_build2_loc (loc, LE_EXPR, integer_type_node, arg,
+                         build_int_cst (unsigned_type_node, 9));
     }
-  return NULL_TREE;
 }
 
-/* FNDECL is assumed to be builtin which can narrow the FP type of
-   the argument, for instance lround((double)f) -> lroundf (f).
-   Do the transformation for a call with argument ARG.  */
+/* Fold a call to fabs, fabsf or fabsl with argument ARG.  */
 
 static tree
-fold_fixed_mathfn (location_t loc, tree fndecl, tree arg)
+fold_builtin_fabs (location_t loc, tree arg, tree type)
 {
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
   if (!validate_arg (arg, REAL_TYPE))
     return NULL_TREE;
 
-  /* If argument is already integer valued, and we don't need to worry
-     about setting errno, there's no need to perform rounding.  */
-  if (! flag_errno_math && integer_valued_real_p (arg))
-    return fold_build1_loc (loc, FIX_TRUNC_EXPR,
-                       TREE_TYPE (TREE_TYPE (fndecl)), arg);
+  arg = fold_convert_loc (loc, type, arg);
+  return fold_build1_loc (loc, ABS_EXPR, type, arg);
+}
 
-  if (optimize)
-    {
-      tree ftype = TREE_TYPE (arg);
-      tree arg0 = strip_float_extensions (arg);
-      tree newtype = TREE_TYPE (arg0);
-      tree decl;
+/* Fold a call to abs, labs, llabs or imaxabs with argument ARG.  */
 
-      if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
-         && (decl = mathfn_built_in (newtype, fcode)))
-       return build_call_expr_loc (loc, decl, 1,
-                               fold_convert_loc (loc, newtype, arg0));
-    }
+static tree
+fold_builtin_abs (location_t loc, tree arg, tree type)
+{
+  if (!validate_arg (arg, INTEGER_TYPE))
+    return NULL_TREE;
 
-  /* Canonicalize iround (x) to lround (x) on ILP32 targets where
-     sizeof (int) == sizeof (long).  */
-  if (TYPE_PRECISION (integer_type_node)
-      == TYPE_PRECISION (long_integer_type_node))
-    {
-      tree newfn = NULL_TREE;
-      switch (fcode)
-       {
-       CASE_FLT_FN (BUILT_IN_ICEIL):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL);
-         break;
+  arg = fold_convert_loc (loc, type, arg);
+  return fold_build1_loc (loc, ABS_EXPR, type, arg);
+}
 
-       CASE_FLT_FN (BUILT_IN_IFLOOR):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR);
-         break;
-
-       CASE_FLT_FN (BUILT_IN_IROUND):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND);
-         break;
-
-       CASE_FLT_FN (BUILT_IN_IRINT):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT);
-         break;
-
-       default:
-         break;
-       }
-
-      if (newfn)
-       {
-         tree newcall = build_call_expr_loc (loc, newfn, 1, arg);
-         return fold_convert_loc (loc,
-                                  TREE_TYPE (TREE_TYPE (fndecl)), newcall);
-       }
-    }
-
-  /* Canonicalize llround (x) to lround (x) on LP64 targets where
-     sizeof (long long) == sizeof (long).  */
-  if (TYPE_PRECISION (long_long_integer_type_node)
-      == TYPE_PRECISION (long_integer_type_node))
-    {
-      tree newfn = NULL_TREE;
-      switch (fcode)
-       {
-       CASE_FLT_FN (BUILT_IN_LLCEIL):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL);
-         break;
-
-       CASE_FLT_FN (BUILT_IN_LLFLOOR):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR);
-         break;
-
-       CASE_FLT_FN (BUILT_IN_LLROUND):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND);
-         break;
-
-       CASE_FLT_FN (BUILT_IN_LLRINT):
-         newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT);
-         break;
-
-       default:
-         break;
-       }
-
-      if (newfn)
-       {
-         tree newcall = build_call_expr_loc (loc, newfn, 1, arg);
-         return fold_convert_loc (loc,
-                                  TREE_TYPE (TREE_TYPE (fndecl)), newcall);
-       }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold call to builtin cabs, cabsf or cabsl with argument ARG.  TYPE is the
-   return type.  Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_cabs (location_t loc, tree arg, tree type, tree fndecl)
-{
-  tree res;
-
-  if (!validate_arg (arg, COMPLEX_TYPE)
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE)
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if (TREE_CODE (arg) == COMPLEX_CST
-      && (res = do_mpfr_arg2 (TREE_REALPART (arg), TREE_IMAGPART (arg),
-                             type, mpfr_hypot)))
-    return res;
-
-  if (TREE_CODE (arg) == COMPLEX_EXPR)
-    {
-      tree real = TREE_OPERAND (arg, 0);
-      tree imag = TREE_OPERAND (arg, 1);
-
-      /* If either part is zero, cabs is fabs of the other.  */
-      if (real_zerop (real))
-       return fold_build1_loc (loc, ABS_EXPR, type, imag);
-      if (real_zerop (imag))
-       return fold_build1_loc (loc, ABS_EXPR, type, real);
-
-      /* cabs(x+xi) -> fabs(x)*sqrt(2).  */
-      if (flag_unsafe_math_optimizations
-         && operand_equal_p (real, imag, OEP_PURE_SAME))
-        {
-         STRIP_NOPS (real);
-         return fold_build2_loc (loc, MULT_EXPR, type,
-                                 fold_build1_loc (loc, ABS_EXPR, type, real),
-                                 build_real_truncate (type, dconst_sqrt2 ()));
-       }
-    }
-
-  /* Optimize cabs(-z) and cabs(conj(z)) as cabs(z).  */
-  if (TREE_CODE (arg) == NEGATE_EXPR
-      || TREE_CODE (arg) == CONJ_EXPR)
-    return build_call_expr_loc (loc, fndecl, 1, TREE_OPERAND (arg, 0));
-
-  /* Don't do this when optimizing for size.  */
-  if (flag_unsafe_math_optimizations
-      && optimize && optimize_function_for_speed_p (cfun))
-    {
-      tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
-
-      if (sqrtfn != NULL_TREE)
-       {
-         tree rpart, ipart, result;
-
-         arg = builtin_save_expr (arg);
-
-         rpart = fold_build1_loc (loc, REALPART_EXPR, type, arg);
-         ipart = fold_build1_loc (loc, IMAGPART_EXPR, type, arg);
-
-         rpart = builtin_save_expr (rpart);
-         ipart = builtin_save_expr (ipart);
-
-         result = fold_build2_loc (loc, PLUS_EXPR, type,
-                               fold_build2_loc (loc, MULT_EXPR, type,
-                                            rpart, rpart),
-                               fold_build2_loc (loc, MULT_EXPR, type,
-                                            ipart, ipart));
-
-         return build_call_expr_loc (loc, sqrtfn, 1, result);
-       }
-    }
-
-  return NULL_TREE;
-}
-
-/* Build a complex (inf +- 0i) for the result of cproj.  TYPE is the
-   complex tree type of the result.  If NEG is true, the imaginary
-   zero is negative.  */
-
-static tree
-build_complex_cproj (tree type, bool neg)
-{
-  REAL_VALUE_TYPE rinf, rzero = dconst0;
-  
-  real_inf (&rinf);
-  rzero.sign = neg;
-  return build_complex (type, build_real (TREE_TYPE (type), rinf),
-                       build_real (TREE_TYPE (type), rzero));
-}
-
-/* Fold call to builtin cproj, cprojf or cprojl with argument ARG.  TYPE is the
-   return type.  Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_cproj (location_t loc, tree arg, tree type)
-{
-  if (!validate_arg (arg, COMPLEX_TYPE)
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE)
-    return NULL_TREE;
-
-  /* If there are no infinities, return arg.  */
-  if (! HONOR_INFINITIES (type))
-    return non_lvalue_loc (loc, arg);
-
-  /* Calculate the result when the argument is a constant.  */
-  if (TREE_CODE (arg) == COMPLEX_CST)
-    {
-      const REAL_VALUE_TYPE *real = TREE_REAL_CST_PTR (TREE_REALPART (arg));
-      const REAL_VALUE_TYPE *imag = TREE_REAL_CST_PTR (TREE_IMAGPART (arg));
-      
-      if (real_isinf (real) || real_isinf (imag))
-       return build_complex_cproj (type, imag->sign);
-      else
-       return arg;
-    }
-  else if (TREE_CODE (arg) == COMPLEX_EXPR)
-    {
-      tree real = TREE_OPERAND (arg, 0);
-      tree imag = TREE_OPERAND (arg, 1);
-
-      STRIP_NOPS (real);
-      STRIP_NOPS (imag);
-      
-      /* If the real part is inf and the imag part is known to be
-        nonnegative, return (inf + 0i).  Remember side-effects are
-        possible in the imag part.  */
-      if (TREE_CODE (real) == REAL_CST
-         && real_isinf (TREE_REAL_CST_PTR (real))
-         && tree_expr_nonnegative_p (imag))
-       return omit_one_operand_loc (loc, type,
-                                    build_complex_cproj (type, false),
-                                    arg);
-      
-      /* If the imag part is inf, return (inf+I*copysign(0,imag)).
-        Remember side-effects are possible in the real part.  */
-      if (TREE_CODE (imag) == REAL_CST
-         && real_isinf (TREE_REAL_CST_PTR (imag)))
-       return
-         omit_one_operand_loc (loc, type,
-                               build_complex_cproj (type, TREE_REAL_CST_PTR
-                                                    (imag)->sign), arg);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin cos, cosf, or cosl with argument ARG.
-   TYPE is the type of the return value.  Return NULL_TREE if no
-   simplification can be made.  */
-
-static tree
-fold_builtin_cos (location_t loc,
-                 tree arg, tree type, tree fndecl)
-{
-  tree res, narg;
-
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpfr_arg1 (arg, type, mpfr_cos, NULL, NULL, 0)))
-    return res;
-
-  /* Optimize cos(-x) into cos (x).  */
-  if ((narg = fold_strip_sign_ops (arg)))
-    return build_call_expr_loc (loc, fndecl, 1, narg);
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin cosh, coshf, or coshl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_cosh (location_t loc, tree arg, tree type, tree fndecl)
-{
-  if (validate_arg (arg, REAL_TYPE))
-    {
-      tree res, narg;
-
-      /* Calculate the result when the argument is a constant.  */
-      if ((res = do_mpfr_arg1 (arg, type, mpfr_cosh, NULL, NULL, 0)))
-       return res;
-
-      /* Optimize cosh(-x) into cosh (x).  */
-      if ((narg = fold_strip_sign_ops (arg)))
-       return build_call_expr_loc (loc, fndecl, 1, narg);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin ccos (or ccosh if HYPER is TRUE) with
-   argument ARG.  TYPE is the type of the return value.  Return
-   NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_ccos (location_t loc, tree arg, tree type, tree fndecl,
-                  bool hyper)
-{
-  if (validate_arg (arg, COMPLEX_TYPE)
-      && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == REAL_TYPE)
-    {
-      tree tmp;
-
-      /* Calculate the result when the argument is a constant.  */
-      if ((tmp = do_mpc_arg1 (arg, type, (hyper ? mpc_cosh : mpc_cos))))
-       return tmp;
-
-      /* Optimize fn(-x) into fn(x).  */
-      if ((tmp = fold_strip_sign_ops (arg)))
-       return build_call_expr_loc (loc, fndecl, 1, tmp);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin tan, tanf, or tanl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_tan (tree arg, tree type)
-{
-  enum built_in_function fcode;
-  tree res;
-
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpfr_arg1 (arg, type, mpfr_tan, NULL, NULL, 0)))
-    return res;
-
-  /* Optimize tan(atan(x)) = x.  */
-  fcode = builtin_mathfn_code (arg);
-  if (flag_unsafe_math_optimizations
-      && (fcode == BUILT_IN_ATAN
-         || fcode == BUILT_IN_ATANF
-         || fcode == BUILT_IN_ATANL))
-    return CALL_EXPR_ARG (arg, 0);
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin sincos, sincosf, or sincosl.  Return
-   NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_sincos (location_t loc,
-                    tree arg0, tree arg1, tree arg2)
-{
-  tree type;
-  tree res, fn, call;
-
-  if (!validate_arg (arg0, REAL_TYPE)
-      || !validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE))
-    return NULL_TREE;
-
-  type = TREE_TYPE (arg0);
-
-  /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpfr_sincos (arg0, arg1, arg2)))
-    return res;
-
-  /* Canonicalize sincos to cexpi.  */
-  if (!targetm.libc_has_function (function_c99_math_complex))
-    return NULL_TREE;
-  fn = mathfn_built_in (type, BUILT_IN_CEXPI);
-  if (!fn)
-    return NULL_TREE;
-
-  call = build_call_expr_loc (loc, fn, 1, arg0);
-  call = builtin_save_expr (call);
-
-  return build2 (COMPOUND_EXPR, void_type_node,
-                build2 (MODIFY_EXPR, void_type_node,
-                        build_fold_indirect_ref_loc (loc, arg1),
-                        build1 (IMAGPART_EXPR, type, call)),
-                build2 (MODIFY_EXPR, void_type_node,
-                        build_fold_indirect_ref_loc (loc, arg2),
-                        build1 (REALPART_EXPR, type, call)));
-}
-
-/* Fold function call to builtin cexp, cexpf, or cexpl.  Return
-   NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_cexp (location_t loc, tree arg0, tree type)
-{
-  tree rtype;
-  tree realp, imagp, ifn;
-  tree res;
-
-  if (!validate_arg (arg0, COMPLEX_TYPE)
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) != REAL_TYPE)
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpc_arg1 (arg0, type, mpc_exp)))
-    return res;
-
-  rtype = TREE_TYPE (TREE_TYPE (arg0));
-
-  /* In case we can figure out the real part of arg0 and it is constant zero
-     fold to cexpi.  */
-  if (!targetm.libc_has_function (function_c99_math_complex))
-    return NULL_TREE;
-  ifn = mathfn_built_in (rtype, BUILT_IN_CEXPI);
-  if (!ifn)
-    return NULL_TREE;
-
-  if ((realp = fold_unary_loc (loc, REALPART_EXPR, rtype, arg0))
-      && real_zerop (realp))
-    {
-      tree narg = fold_build1_loc (loc, IMAGPART_EXPR, rtype, arg0);
-      return build_call_expr_loc (loc, ifn, 1, narg);
-    }
-
-  /* In case we can easily decompose real and imaginary parts split cexp
-     to exp (r) * cexpi (i).  */
-  if (flag_unsafe_math_optimizations
-      && realp)
-    {
-      tree rfn, rcall, icall;
-
-      rfn = mathfn_built_in (rtype, BUILT_IN_EXP);
-      if (!rfn)
-       return NULL_TREE;
-
-      imagp = fold_unary_loc (loc, IMAGPART_EXPR, rtype, arg0);
-      if (!imagp)
-       return NULL_TREE;
-
-      icall = build_call_expr_loc (loc, ifn, 1, imagp);
-      icall = builtin_save_expr (icall);
-      rcall = build_call_expr_loc (loc, rfn, 1, realp);
-      rcall = builtin_save_expr (rcall);
-      return fold_build2_loc (loc, COMPLEX_EXPR, type,
-                         fold_build2_loc (loc, MULT_EXPR, rtype,
-                                      rcall,
-                                      fold_build1_loc (loc, REALPART_EXPR,
-                                                   rtype, icall)),
-                         fold_build2_loc (loc, MULT_EXPR, rtype,
-                                      rcall,
-                                      fold_build1_loc (loc, IMAGPART_EXPR,
-                                                   rtype, icall)));
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin trunc, truncf or truncl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_trunc (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize trunc of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE r, x;
-      tree type = TREE_TYPE (TREE_TYPE (fndecl));
-
-      x = TREE_REAL_CST (arg);
-      real_trunc (&r, TYPE_MODE (type), &x);
-      return build_real (type, r);
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin floor, floorf or floorl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_floor (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize floor of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE x;
-
-      x = TREE_REAL_CST (arg);
-      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
-       {
-         tree type = TREE_TYPE (TREE_TYPE (fndecl));
-         REAL_VALUE_TYPE r;
-
-         real_floor (&r, TYPE_MODE (type), &x);
-         return build_real (type, r);
-       }
-    }
-
-  /* Fold floor (x) where x is nonnegative to trunc (x).  */
-  if (tree_expr_nonnegative_p (arg))
-    {
-      tree truncfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_TRUNC);
-      if (truncfn)
-       return build_call_expr_loc (loc, truncfn, 1, arg);
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin ceil, ceilf or ceill with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_ceil (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize ceil of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE x;
-
-      x = TREE_REAL_CST (arg);
-      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
-       {
-         tree type = TREE_TYPE (TREE_TYPE (fndecl));
-         REAL_VALUE_TYPE r;
-
-         real_ceil (&r, TYPE_MODE (type), &x);
-         return build_real (type, r);
-       }
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin round, roundf or roundl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_round (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize round of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE x;
-
-      x = TREE_REAL_CST (arg);
-      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
-       {
-         tree type = TREE_TYPE (TREE_TYPE (fndecl));
-         REAL_VALUE_TYPE r;
-
-         real_round (&r, TYPE_MODE (type), &x);
-         return build_real (type, r);
-       }
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin lround, lroundf or lroundl (or the
-   corresponding long long versions) and other rounding functions.  ARG
-   is the argument to the call.  Return NULL_TREE if no simplification
-   can be made.  */
-
-static tree
-fold_builtin_int_roundingfn (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize lround of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE x = TREE_REAL_CST (arg);
-
-      if (real_isfinite (&x))
-       {
-         tree itype = TREE_TYPE (TREE_TYPE (fndecl));
-         tree ftype = TREE_TYPE (arg);
-         REAL_VALUE_TYPE r;
-         bool fail = false;
-
-         switch (DECL_FUNCTION_CODE (fndecl))
-           {
-           CASE_FLT_FN (BUILT_IN_IFLOOR):
-           CASE_FLT_FN (BUILT_IN_LFLOOR):
-           CASE_FLT_FN (BUILT_IN_LLFLOOR):
-             real_floor (&r, TYPE_MODE (ftype), &x);
-             break;
-
-           CASE_FLT_FN (BUILT_IN_ICEIL):
-           CASE_FLT_FN (BUILT_IN_LCEIL):
-           CASE_FLT_FN (BUILT_IN_LLCEIL):
-             real_ceil (&r, TYPE_MODE (ftype), &x);
-             break;
-
-           CASE_FLT_FN (BUILT_IN_IROUND):
-           CASE_FLT_FN (BUILT_IN_LROUND):
-           CASE_FLT_FN (BUILT_IN_LLROUND):
-             real_round (&r, TYPE_MODE (ftype), &x);
-             break;
-
-           default:
-             gcc_unreachable ();
-           }
-
-         wide_int val = real_to_integer (&r, &fail, TYPE_PRECISION (itype));
-         if (!fail)
-           return wide_int_to_tree (itype, val);
-       }
-    }
-
-  switch (DECL_FUNCTION_CODE (fndecl))
-    {
-    CASE_FLT_FN (BUILT_IN_LFLOOR):
-    CASE_FLT_FN (BUILT_IN_LLFLOOR):
-      /* Fold lfloor (x) where x is nonnegative to FIX_TRUNC (x).  */
-      if (tree_expr_nonnegative_p (arg))
-       return fold_build1_loc (loc, FIX_TRUNC_EXPR,
-                           TREE_TYPE (TREE_TYPE (fndecl)), arg);
-      break;
-    default:;
-    }
-
-  return fold_fixed_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin ffs, clz, ctz, popcount and parity
-   and their long and long long variants (i.e. ffsl and ffsll).  ARG is
-   the argument to the call.  Return NULL_TREE if no simplification can
-   be made.  */
-
-static tree
-fold_builtin_bitop (tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* Optimize for constant argument.  */
-  if (TREE_CODE (arg) == INTEGER_CST && !TREE_OVERFLOW (arg))
-    {
-      tree type = TREE_TYPE (arg);
-      int result;
-
-      switch (DECL_FUNCTION_CODE (fndecl))
-       {
-       CASE_INT_FN (BUILT_IN_FFS):
-         result = wi::ffs (arg);
-         break;
-
-       CASE_INT_FN (BUILT_IN_CLZ):
-         if (wi::ne_p (arg, 0))
-           result = wi::clz (arg);
-         else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
-           result = TYPE_PRECISION (type);
-         break;
-
-       CASE_INT_FN (BUILT_IN_CTZ):
-         if (wi::ne_p (arg, 0))
-           result = wi::ctz (arg);
-         else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
-           result = TYPE_PRECISION (type);
-         break;
-
-       CASE_INT_FN (BUILT_IN_CLRSB):
-         result = wi::clrsb (arg);
-         break;
-
-       CASE_INT_FN (BUILT_IN_POPCOUNT):
-         result = wi::popcount (arg);
-         break;
-
-       CASE_INT_FN (BUILT_IN_PARITY):
-         result = wi::parity (arg);
-         break;
-
-       default:
-         gcc_unreachable ();
-       }
-
-      return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), result);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin_bswap and the short, long and long long
-   variants.  Return NULL_TREE if no simplification can be made.  */
-static tree
-fold_builtin_bswap (tree fndecl, tree arg)
-{
-  if (! validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* Optimize constant value.  */
-  if (TREE_CODE (arg) == INTEGER_CST && !TREE_OVERFLOW (arg))
-    {
-      tree type = TREE_TYPE (TREE_TYPE (fndecl));
-
-      switch (DECL_FUNCTION_CODE (fndecl))
-       {
-         case BUILT_IN_BSWAP16:
-         case BUILT_IN_BSWAP32:
-         case BUILT_IN_BSWAP64:
-           {
-             signop sgn = TYPE_SIGN (type);
-             tree result =
-               wide_int_to_tree (type,
-                                 wide_int::from (arg, TYPE_PRECISION (type),
-                                                 sgn).bswap ());
-             return result;
-           }
-       default:
-         gcc_unreachable ();
-       }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold a builtin function call to hypot, hypotf, or hypotl.  Return
-   NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_hypot (location_t loc, tree fndecl,
-                   tree arg0, tree arg1, tree type)
-{
-  tree res, narg0, narg1;
-
-  if (!validate_arg (arg0, REAL_TYPE)
-      || !validate_arg (arg1, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpfr_arg2 (arg0, arg1, type, mpfr_hypot)))
-    return res;
-
-  /* If either argument to hypot has a negate or abs, strip that off.
-     E.g. hypot(-x,fabs(y)) -> hypot(x,y).  */
-  narg0 = fold_strip_sign_ops (arg0);
-  narg1 = fold_strip_sign_ops (arg1);
-  if (narg0 || narg1)
-    {
-      return build_call_expr_loc (loc, fndecl, 2, narg0 ? narg0 : arg0,
-                             narg1 ? narg1 : arg1);
-    }
-
-  /* If either argument is zero, hypot is fabs of the other.  */
-  if (real_zerop (arg0))
-    return fold_build1_loc (loc, ABS_EXPR, type, arg1);
-  else if (real_zerop (arg1))
-    return fold_build1_loc (loc, ABS_EXPR, type, arg0);
-
-  /* hypot(x,x) -> fabs(x)*sqrt(2).  */
-  if (flag_unsafe_math_optimizations
-      && operand_equal_p (arg0, arg1, OEP_PURE_SAME))
-    return fold_build2_loc (loc, MULT_EXPR, type,
-                           fold_build1_loc (loc, ABS_EXPR, type, arg0),
-                           build_real_truncate (type, dconst_sqrt2 ()));
-
-  return NULL_TREE;
-}
-
-
-/* Fold a builtin function call to pow, powf, or powl.  Return
-   NULL_TREE if no simplification can be made.  */
-static tree
-fold_builtin_pow (location_t loc, tree fndecl, tree arg0, tree arg1, tree type)
-{
-  tree res;
-
-  if (!validate_arg (arg0, REAL_TYPE)
-       || !validate_arg (arg1, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpfr_arg2 (arg0, arg1, type, mpfr_pow)))
-    return res;
-
-  /* Optimize pow(1.0,y) = 1.0.  */
-  if (real_onep (arg0))
-    return omit_one_operand_loc (loc, type, build_real (type, dconst1), arg1);
-
-  if (TREE_CODE (arg1) == REAL_CST
-      && !TREE_OVERFLOW (arg1))
-    {
-      REAL_VALUE_TYPE cint;
-      REAL_VALUE_TYPE c;
-      HOST_WIDE_INT n;
-
-      c = TREE_REAL_CST (arg1);
-
-      /* Optimize pow(x,0.0) = 1.0.  */
-      if (real_equal (&c, &dconst0))
-       return omit_one_operand_loc (loc, type, build_real (type, dconst1),
-                                arg0);
-
-      /* Optimize pow(x,1.0) = x.  */
-      if (real_equal (&c, &dconst1))
-       return arg0;
-
-      /* Optimize pow(x,-1.0) = 1.0/x.  */
-      if (real_equal (&c, &dconstm1))
-       return fold_build2_loc (loc, RDIV_EXPR, type,
-                           build_real (type, dconst1), arg0);
-
-      /* Optimize pow(x,0.5) = sqrt(x).  */
-      if (flag_unsafe_math_optimizations
-         && real_equal (&c, &dconsthalf))
-       {
-         tree sqrtfn = mathfn_built_in (type, BUILT_IN_SQRT);
-
-         if (sqrtfn != NULL_TREE)
-           return build_call_expr_loc (loc, sqrtfn, 1, arg0);
-       }
-
-      /* Optimize pow(x,1.0/3.0) = cbrt(x).  */
-      if (flag_unsafe_math_optimizations)
-       {
-         const REAL_VALUE_TYPE dconstroot
-           = real_value_truncate (TYPE_MODE (type), dconst_third ());
-
-         if (real_equal (&c, &dconstroot))
-           {
-             tree cbrtfn = mathfn_built_in (type, BUILT_IN_CBRT);
-             if (cbrtfn != NULL_TREE)
-               return build_call_expr_loc (loc, cbrtfn, 1, arg0);
-           }
-       }
-
-      /* Check for an integer exponent.  */
-      n = real_to_integer (&c);
-      real_from_integer (&cint, VOIDmode, n, SIGNED);
-      if (real_identical (&c, &cint))
-       {
-         /* Attempt to evaluate pow at compile-time, unless this should
-            raise an exception.  */
-         if (TREE_CODE (arg0) == REAL_CST
-             && !TREE_OVERFLOW (arg0)
-             && (n > 0
-                 || (!flag_trapping_math && !flag_errno_math)
-                 || !real_equal (&TREE_REAL_CST (arg0), &dconst0)))
-           {
-             REAL_VALUE_TYPE x;
-             bool inexact;
-
-             x = TREE_REAL_CST (arg0);
-             inexact = real_powi (&x, TYPE_MODE (type), &x, n);
-             if (flag_unsafe_math_optimizations || !inexact)
-               return build_real (type, x);
-           }
-
-         /* Strip sign ops from even integer powers.  */
-         if ((n & 1) == 0 && flag_unsafe_math_optimizations)
-           {
-             tree narg0 = fold_strip_sign_ops (arg0);
-             if (narg0)
-               return build_call_expr_loc (loc, fndecl, 2, narg0, arg1);
-           }
-       }
-    }
-
-  if (flag_unsafe_math_optimizations)
-    {
-      const enum built_in_function fcode = builtin_mathfn_code (arg0);
-
-      /* Optimize pow(expN(x),y) = expN(x*y).  */
-      if (BUILTIN_EXPONENT_P (fcode))
-       {
-         tree expfn = TREE_OPERAND (CALL_EXPR_FN (arg0), 0);
-         tree arg = CALL_EXPR_ARG (arg0, 0);
-         arg = fold_build2_loc (loc, MULT_EXPR, type, arg, arg1);
-         return build_call_expr_loc (loc, expfn, 1, arg);
-       }
-
-      /* Optimize pow(sqrt(x),y) = pow(x,y*0.5).  */
-      if (BUILTIN_SQRT_P (fcode))
-       {
-         tree narg0 = CALL_EXPR_ARG (arg0, 0);
-         tree narg1 = fold_build2_loc (loc, MULT_EXPR, type, arg1,
-                                   build_real (type, dconsthalf));
-         return build_call_expr_loc (loc, fndecl, 2, narg0, narg1);
-       }
-
-      /* Optimize pow(cbrt(x),y) = pow(x,y/3) iff x is nonnegative.  */
-      if (BUILTIN_CBRT_P (fcode))
-       {
-         tree arg = CALL_EXPR_ARG (arg0, 0);
-         if (tree_expr_nonnegative_p (arg))
-           {
-             tree c = build_real_truncate (type, dconst_third ());
-             tree narg1 = fold_build2_loc (loc, MULT_EXPR, type, arg1, c);
-             return build_call_expr_loc (loc, fndecl, 2, arg, narg1);
-           }
-       }
-
-      /* Optimize pow(pow(x,y),z) = pow(x,y*z) iff x is nonnegative.  */
-      if (fcode == BUILT_IN_POW
-         || fcode == BUILT_IN_POWF
-         || fcode == BUILT_IN_POWL)
-       {
-         tree arg00 = CALL_EXPR_ARG (arg0, 0);
-         if (tree_expr_nonnegative_p (arg00))
-           {
-             tree arg01 = CALL_EXPR_ARG (arg0, 1);
-             tree narg1 = fold_build2_loc (loc, MULT_EXPR, type, arg01, arg1);
-             return build_call_expr_loc (loc, fndecl, 2, arg00, narg1);
-           }
-       }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold a builtin function call to powi, powif, or powil with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-static tree
-fold_builtin_powi (location_t loc, tree fndecl ATTRIBUTE_UNUSED,
-                  tree arg0, tree arg1, tree type)
-{
-  if (!validate_arg (arg0, REAL_TYPE)
-      || !validate_arg (arg1, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* Optimize pow(1.0,y) = 1.0.  */
-  if (real_onep (arg0))
-    return omit_one_operand_loc (loc, type, build_real (type, dconst1), arg1);
-
-  if (tree_fits_shwi_p (arg1))
-    {
-      HOST_WIDE_INT c = tree_to_shwi (arg1);
-
-      /* Evaluate powi at compile-time.  */
-      if (TREE_CODE (arg0) == REAL_CST
-         && !TREE_OVERFLOW (arg0))
-       {
-         REAL_VALUE_TYPE x;
-         x = TREE_REAL_CST (arg0);
-         real_powi (&x, TYPE_MODE (type), &x, c);
-         return build_real (type, x);
-       }
-
-      /* Optimize pow(x,0) = 1.0.  */
-      if (c == 0)
-       return omit_one_operand_loc (loc, type, build_real (type, dconst1),
-                                arg0);
-
-      /* Optimize pow(x,1) = x.  */
-      if (c == 1)
-       return arg0;
-
-      /* Optimize pow(x,-1) = 1.0/x.  */
-      if (c == -1)
-       return fold_build2_loc (loc, RDIV_EXPR, type,
-                          build_real (type, dconst1), arg0);
-    }
-
-  return NULL_TREE;
-}
-
-/* A subroutine of fold_builtin to fold the various exponent
-   functions.  Return NULL_TREE if no simplification can be made.
-   FUNC is the corresponding MPFR exponent function.  */
-
-static tree
-fold_builtin_exponent (location_t loc, tree fndecl, tree arg,
-                      int (*func)(mpfr_ptr, mpfr_srcptr, mp_rnd_t))
-{
-  if (validate_arg (arg, REAL_TYPE))
-    {
-      tree type = TREE_TYPE (TREE_TYPE (fndecl));
-      tree res;
-
-      /* Calculate the result when the argument is a constant.  */
-      if ((res = do_mpfr_arg1 (arg, type, func, NULL, NULL, 0)))
-       return res;
-
-      /* Optimize expN(logN(x)) = x.  */
-      if (flag_unsafe_math_optimizations)
-       {
-         const enum built_in_function fcode = builtin_mathfn_code (arg);
-
-         if ((func == mpfr_exp
-              && (fcode == BUILT_IN_LOG
-                  || fcode == BUILT_IN_LOGF
-                  || fcode == BUILT_IN_LOGL))
-             || (func == mpfr_exp2
-                 && (fcode == BUILT_IN_LOG2
-                     || fcode == BUILT_IN_LOG2F
-                     || fcode == BUILT_IN_LOG2L))
-             || (func == mpfr_exp10
-                 && (fcode == BUILT_IN_LOG10
-                     || fcode == BUILT_IN_LOG10F
-                     || fcode == BUILT_IN_LOG10L)))
-           return fold_convert_loc (loc, type, CALL_EXPR_ARG (arg, 0));
-       }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin memchr.  ARG1, ARG2 and LEN are the
-   arguments to the call, and TYPE is its return type.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_memchr (location_t loc, tree arg1, tree arg2, tree len, tree type)
-{
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, INTEGER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      const char *p1;
-
-      if (TREE_CODE (arg2) != INTEGER_CST
-         || !tree_fits_uhwi_p (len))
-       return NULL_TREE;
-
-      p1 = c_getstr (arg1);
-      if (p1 && compare_tree_int (len, strlen (p1) + 1) <= 0)
-       {
-         char c;
-         const char *r;
-         tree tem;
-
-         if (target_char_cast (arg2, &c))
-           return NULL_TREE;
-
-         r = (const char *) memchr (p1, c, tree_to_uhwi (len));
-
-         if (r == NULL)
-           return build_int_cst (TREE_TYPE (arg1), 0);
-
-         tem = fold_build_pointer_plus_hwi_loc (loc, arg1, r - p1);
-         return fold_convert_loc (loc, type, tem);
-       }
-      return NULL_TREE;
-    }
-}
-
-/* Fold function call to builtin memcmp with arguments ARG1 and ARG2.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_memcmp (location_t loc, tree arg1, tree arg2, tree len)
-{
-  const char *p1, *p2;
-
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* If the LEN parameter is zero, return zero.  */
-  if (integer_zerop (len))
-    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
-                             arg1, arg2);
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
-
-  p1 = c_getstr (arg1);
-  p2 = c_getstr (arg2);
-
-  /* If all arguments are constant, and the value of len is not greater
-     than the lengths of arg1 and arg2, evaluate at compile-time.  */
-  if (tree_fits_uhwi_p (len) && p1 && p2
-      && compare_tree_int (len, strlen (p1) + 1) <= 0
-      && compare_tree_int (len, strlen (p2) + 1) <= 0)
-    {
-      const int r = memcmp (p1, p2, tree_to_uhwi (len));
-
-      if (r > 0)
-       return integer_one_node;
-      else if (r < 0)
-       return integer_minus_one_node;
-      else
-       return integer_zero_node;
-    }
-
-  /* If len parameter is one, return an expression corresponding to
-     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-       = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree ind1
-       = fold_convert_loc (loc, integer_type_node,
-                           build1 (INDIRECT_REF, cst_uchar_node,
-                                   fold_convert_loc (loc,
-                                                     cst_uchar_ptr_node,
-                                                     arg1)));
-      tree ind2
-       = fold_convert_loc (loc, integer_type_node,
-                           build1 (INDIRECT_REF, cst_uchar_node,
-                                   fold_convert_loc (loc,
-                                                     cst_uchar_ptr_node,
-                                                     arg2)));
-      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin strcmp with arguments ARG1 and ARG2.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strcmp (location_t loc, tree arg1, tree arg2)
-{
-  const char *p1, *p2;
-
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE))
-    return NULL_TREE;
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return integer_zero_node;
-
-  p1 = c_getstr (arg1);
-  p2 = c_getstr (arg2);
-
-  if (p1 && p2)
-    {
-      const int i = strcmp (p1, p2);
-      if (i < 0)
-       return integer_minus_one_node;
-      else if (i > 0)
-       return integer_one_node;
-      else
-       return integer_zero_node;
-    }
-
-  /* If the second arg is "", return *(const unsigned char*)arg1.  */
-  if (p2 && *p2 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-       = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-                              build1 (INDIRECT_REF, cst_uchar_node,
-                                      fold_convert_loc (loc,
-                                                        cst_uchar_ptr_node,
-                                                        arg1)));
-    }
-
-  /* If the first arg is "", return -*(const unsigned char*)arg2.  */
-  if (p1 && *p1 == '\0')
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-       = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp
-       = fold_convert_loc (loc, integer_type_node,
-                           build1 (INDIRECT_REF, cst_uchar_node,
-                                   fold_convert_loc (loc,
-                                                     cst_uchar_ptr_node,
-                                                     arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin strncmp with arguments ARG1, ARG2, and LEN.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_strncmp (location_t loc, tree arg1, tree arg2, tree len)
-{
-  const char *p1, *p2;
-
-  if (!validate_arg (arg1, POINTER_TYPE)
-      || !validate_arg (arg2, POINTER_TYPE)
-      || !validate_arg (len, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* If the LEN parameter is zero, return zero.  */
-  if (integer_zerop (len))
-    return omit_two_operands_loc (loc, integer_type_node, integer_zero_node,
-                             arg1, arg2);
-
-  /* If ARG1 and ARG2 are the same (and not volatile), return zero.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return omit_one_operand_loc (loc, integer_type_node, integer_zero_node, len);
-
-  p1 = c_getstr (arg1);
-  p2 = c_getstr (arg2);
-
-  if (tree_fits_uhwi_p (len) && p1 && p2)
-    {
-      const int i = strncmp (p1, p2, tree_to_uhwi (len));
-      if (i > 0)
-       return integer_one_node;
-      else if (i < 0)
-       return integer_minus_one_node;
-      else
-       return integer_zero_node;
-    }
-
-  /* If the second arg is "", and the length is greater than zero,
-     return *(const unsigned char*)arg1.  */
-  if (p2 && *p2 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-       = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      return fold_convert_loc (loc, integer_type_node,
-                              build1 (INDIRECT_REF, cst_uchar_node,
-                                      fold_convert_loc (loc,
-                                                        cst_uchar_ptr_node,
-                                                        arg1)));
-    }
-
-  /* If the first arg is "", and the length is greater than zero,
-     return -*(const unsigned char*)arg2.  */
-  if (p1 && *p1 == '\0'
-      && TREE_CODE (len) == INTEGER_CST
-      && tree_int_cst_sgn (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-       = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree temp = fold_convert_loc (loc, integer_type_node,
-                                   build1 (INDIRECT_REF, cst_uchar_node,
-                                           fold_convert_loc (loc,
-                                                             cst_uchar_ptr_node,
-                                                             arg2)));
-      return fold_build1_loc (loc, NEGATE_EXPR, integer_type_node, temp);
-    }
-
-  /* If len parameter is one, return an expression corresponding to
-     (*(const unsigned char*)arg1 - (const unsigned char*)arg2).  */
-  if (tree_fits_uhwi_p (len) && tree_to_uhwi (len) == 1)
-    {
-      tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
-      tree cst_uchar_ptr_node
-       = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
-
-      tree ind1 = fold_convert_loc (loc, integer_type_node,
-                                   build1 (INDIRECT_REF, cst_uchar_node,
-                                           fold_convert_loc (loc,
-                                                             cst_uchar_ptr_node,
-                                                             arg1)));
-      tree ind2 = fold_convert_loc (loc, integer_type_node,
-                                   build1 (INDIRECT_REF, cst_uchar_node,
-                                           fold_convert_loc (loc,
-                                                             cst_uchar_ptr_node,
-                                                             arg2)));
-      return fold_build2_loc (loc, MINUS_EXPR, integer_type_node, ind1, ind2);
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin signbit, signbitf or signbitl with argument
-   ARG.  Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_signbit (location_t loc, tree arg, tree type)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* If ARG is a compile-time constant, determine the result.  */
-  if (TREE_CODE (arg) == REAL_CST
-      && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE c;
-
-      c = TREE_REAL_CST (arg);
-      return (REAL_VALUE_NEGATIVE (c)
-             ? build_one_cst (type)
-             : build_zero_cst (type));
-    }
-
-  /* If ARG is non-negative, the result is always zero.  */
-  if (tree_expr_nonnegative_p (arg))
-    return omit_one_operand_loc (loc, type, integer_zero_node, arg);
-
-  /* If ARG's format doesn't have signed zeros, return "arg < 0.0".  */
-  if (!HONOR_SIGNED_ZEROS (arg))
-    return fold_convert (type,
-                        fold_build2_loc (loc, LT_EXPR, boolean_type_node, arg,
-                       build_real (TREE_TYPE (arg), dconst0)));
-
-  return NULL_TREE;
-}
-
-/* Fold function call to builtin copysign, copysignf or copysignl with
-   arguments ARG1 and ARG2.  Return NULL_TREE if no simplification can
-   be made.  */
-
-static tree
-fold_builtin_copysign (location_t loc, tree fndecl,
-                      tree arg1, tree arg2, tree type)
-{
-  tree tem;
-
-  if (!validate_arg (arg1, REAL_TYPE)
-      || !validate_arg (arg2, REAL_TYPE))
-    return NULL_TREE;
-
-  /* copysign(X,X) is X.  */
-  if (operand_equal_p (arg1, arg2, 0))
-    return fold_convert_loc (loc, type, arg1);
-
-  /* If ARG1 and ARG2 are compile-time constants, determine the result.  */
-  if (TREE_CODE (arg1) == REAL_CST
-      && TREE_CODE (arg2) == REAL_CST
-      && !TREE_OVERFLOW (arg1)
-      && !TREE_OVERFLOW (arg2))
-    {
-      REAL_VALUE_TYPE c1, c2;
-
-      c1 = TREE_REAL_CST (arg1);
-      c2 = TREE_REAL_CST (arg2);
-      /* c1.sign := c2.sign.  */
-      real_copysign (&c1, &c2);
-      return build_real (type, c1);
-    }
-
-  /* copysign(X, Y) is fabs(X) when Y is always non-negative.
-     Remember to evaluate Y for side-effects.  */
-  if (tree_expr_nonnegative_p (arg2))
-    return omit_one_operand_loc (loc, type,
-                            fold_build1_loc (loc, ABS_EXPR, type, arg1),
-                            arg2);
-
-  /* Strip sign changing operations for the first argument.  */
-  tem = fold_strip_sign_ops (arg1);
-  if (tem)
-    return build_call_expr_loc (loc, fndecl, 2, tem, arg2);
-
-  return NULL_TREE;
-}
-
-/* Fold a call to builtin isascii with argument ARG.  */
-
-static tree
-fold_builtin_isascii (location_t loc, tree arg)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      /* Transform isascii(c) -> ((c & ~0x7f) == 0).  */
-      arg = fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
-                        build_int_cst (integer_type_node,
-                                       ~ (unsigned HOST_WIDE_INT) 0x7f));
-      return fold_build2_loc (loc, EQ_EXPR, integer_type_node,
-                             arg, integer_zero_node);
-    }
-}
-
-/* Fold a call to builtin toascii with argument ARG.  */
-
-static tree
-fold_builtin_toascii (location_t loc, tree arg)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-
-  /* Transform toascii(c) -> (c & 0x7f).  */
-  return fold_build2_loc (loc, BIT_AND_EXPR, integer_type_node, arg,
-                         build_int_cst (integer_type_node, 0x7f));
-}
-
-/* Fold a call to builtin isdigit with argument ARG.  */
-
-static tree
-fold_builtin_isdigit (location_t loc, tree arg)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      /* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9.  */
-      /* According to the C standard, isdigit is unaffected by locale.
-        However, it definitely is affected by the target character set.  */
-      unsigned HOST_WIDE_INT target_digit0
-       = lang_hooks.to_target_charset ('0');
-
-      if (target_digit0 == 0)
-       return NULL_TREE;
-
-      arg = fold_convert_loc (loc, unsigned_type_node, arg);
-      arg = fold_build2 (MINUS_EXPR, unsigned_type_node, arg,
-                        build_int_cst (unsigned_type_node, target_digit0));
-      return fold_build2_loc (loc, LE_EXPR, integer_type_node, arg,
-                         build_int_cst (unsigned_type_node, 9));
-    }
-}
-
-/* Fold a call to fabs, fabsf or fabsl with argument ARG.  */
-
-static tree
-fold_builtin_fabs (location_t loc, tree arg, tree type)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  arg = fold_convert_loc (loc, type, arg);
-  if (TREE_CODE (arg) == REAL_CST)
-    return fold_abs_const (arg, type);
-  return fold_build1_loc (loc, ABS_EXPR, type, arg);
-}
-
-/* Fold a call to abs, labs, llabs or imaxabs with argument ARG.  */
-
-static tree
-fold_builtin_abs (location_t loc, tree arg, tree type)
-{
-  if (!validate_arg (arg, INTEGER_TYPE))
-    return NULL_TREE;
-
-  arg = fold_convert_loc (loc, type, arg);
-  if (TREE_CODE (arg) == INTEGER_CST)
-    return fold_abs_const (arg, type);
-  return fold_build1_loc (loc, ABS_EXPR, type, arg);
-}
-
-/* Fold a fma operation with arguments ARG[012].  */
-
-tree
-fold_fma (location_t loc ATTRIBUTE_UNUSED,
-         tree type, tree arg0, tree arg1, tree arg2)
-{
-  if (TREE_CODE (arg0) == REAL_CST
-      && TREE_CODE (arg1) == REAL_CST
-      && TREE_CODE (arg2) == REAL_CST)
-    return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma);
-
-  return NULL_TREE;
-}
-
-/* Fold a call to fma, fmaf, or fmal with arguments ARG[012].  */
+/* Fold a call to fma, fmaf, or fmal with arguments ARG[012].  */
 
 static tree
 fold_builtin_fma (location_t loc, tree arg0, tree arg1, tree arg2, tree type)
 {
+  /* ??? Only expand to FMA_EXPR if it's directly supported.  */
   if (validate_arg (arg0, REAL_TYPE)
       && validate_arg (arg1, REAL_TYPE)
-      && validate_arg (arg2, REAL_TYPE))
-    {
-      tree tem = fold_fma (loc, type, arg0, arg1, arg2);
-      if (tem)
-       return tem;
+      && validate_arg (arg2, REAL_TYPE)
+      && optab_handler (fma_optab, TYPE_MODE (type)) != CODE_FOR_nothing)
+    return fold_build3_loc (loc, FMA_EXPR, type, arg0, arg1, arg2);
 
-      /* ??? Only expand to FMA_EXPR if it's directly supported.  */
-      if (optab_handler (fma_optab, TYPE_MODE (type)) != CODE_FOR_nothing)
-        return fold_build3_loc (loc, FMA_EXPR, type, arg0, arg1, arg2);
-    }
-  return NULL_TREE;
-}
-
-/* Fold a call to builtin fmin or fmax.  */
-
-static tree
-fold_builtin_fmin_fmax (location_t loc, tree arg0, tree arg1,
-                       tree type, bool max)
-{
-  if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, REAL_TYPE))
-    {
-      /* Calculate the result when the argument is a constant.  */
-      tree res = do_mpfr_arg2 (arg0, arg1, type, (max ? mpfr_max : mpfr_min));
-
-      if (res)
-       return res;
-
-      /* If either argument is NaN, return the other one.  Avoid the
-        transformation if we get (and honor) a signalling NaN.  Using
-        omit_one_operand() ensures we create a non-lvalue.  */
-      if (TREE_CODE (arg0) == REAL_CST
-         && real_isnan (&TREE_REAL_CST (arg0))
-         && (! HONOR_SNANS (arg0)
-             || ! TREE_REAL_CST (arg0).signalling))
-       return omit_one_operand_loc (loc, type, arg1, arg0);
-      if (TREE_CODE (arg1) == REAL_CST
-         && real_isnan (&TREE_REAL_CST (arg1))
-         && (! HONOR_SNANS (arg1)
-             || ! TREE_REAL_CST (arg1).signalling))
-       return omit_one_operand_loc (loc, type, arg0, arg1);
-
-      /* Transform fmin/fmax(x,x) -> x.  */
-      if (operand_equal_p (arg0, arg1, OEP_PURE_SAME))
-       return omit_one_operand_loc (loc, type, arg0, arg1);
-
-      /* Convert fmin/fmax to MIN_EXPR/MAX_EXPR.  C99 requires these
-        functions to return the numeric arg if the other one is NaN.
-        These tree codes don't honor that, so only transform if
-        -ffinite-math-only is set.  C99 doesn't require -0.0 to be
-        handled, so we don't have to worry about it either.  */
-      if (flag_finite_math_only)
-       return fold_build2_loc (loc, (max ? MAX_EXPR : MIN_EXPR), type,
-                           fold_convert_loc (loc, type, arg0),
-                           fold_convert_loc (loc, type, arg1));
-    }
   return NULL_TREE;
 }
 
@@ -8982,100 +7978,10 @@ fold_builtin_carg (location_t loc, tree arg, tree type)
       if (atan2_fn)
         {
          tree new_arg = builtin_save_expr (arg);
-         tree r_arg = fold_build1_loc (loc, REALPART_EXPR, type, new_arg);
-         tree i_arg = fold_build1_loc (loc, IMAGPART_EXPR, type, new_arg);
-         return build_call_expr_loc (loc, atan2_fn, 2, i_arg, r_arg);
-       }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold a call to builtin logb/ilogb.  */
-
-static tree
-fold_builtin_logb (location_t loc, tree arg, tree rettype)
-{
-  if (! validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  STRIP_NOPS (arg);
-
-  if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
-
-      switch (value->cl)
-      {
-      case rvc_nan:
-      case rvc_inf:
-       /* If arg is Inf or NaN and we're logb, return it.  */
-       if (TREE_CODE (rettype) == REAL_TYPE)
-         {
-           /* For logb(-Inf) we have to return +Inf.  */
-           if (real_isinf (value) && real_isneg (value))
-             {
-               REAL_VALUE_TYPE tem;
-               real_inf (&tem);
-               return build_real (rettype, tem);
-             }
-           return fold_convert_loc (loc, rettype, arg);
-         }
-       /* Fall through... */
-      case rvc_zero:
-       /* Zero may set errno and/or raise an exception for logb, also
-          for ilogb we don't know FP_ILOGB0.  */
-       return NULL_TREE;
-      case rvc_normal:
-       /* For normal numbers, proceed iff radix == 2.  In GCC,
-          normalized significands are in the range [0.5, 1.0).  We
-          want the exponent as if they were [1.0, 2.0) so get the
-          exponent and subtract 1.  */
-       if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
-         return fold_convert_loc (loc, rettype,
-                                  build_int_cst (integer_type_node,
-                                                 REAL_EXP (value)-1));
-       break;
-      }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold a call to builtin significand, if radix == 2.  */
-
-static tree
-fold_builtin_significand (location_t loc, tree arg, tree rettype)
-{
-  if (! validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  STRIP_NOPS (arg);
-
-  if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
-
-      switch (value->cl)
-      {
-      case rvc_zero:
-      case rvc_nan:
-      case rvc_inf:
-       /* If arg is +-0, +-Inf or +-NaN, then return it.  */
-       return fold_convert_loc (loc, rettype, arg);
-      case rvc_normal:
-       /* For normal numbers, proceed iff radix == 2.  */
-       if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
-         {
-           REAL_VALUE_TYPE result = *value;
-           /* In GCC, normalized significands are in the range [0.5,
-              1.0).  We want them to be [1.0, 2.0) so set the
-              exponent to 1.  */
-           SET_REAL_EXP (&result, 1);
-           return build_real (rettype, result);
-         }
-       break;
-      }
+         tree r_arg = fold_build1_loc (loc, REALPART_EXPR, type, new_arg);
+         tree i_arg = fold_build1_loc (loc, IMAGPART_EXPR, type, new_arg);
+         return build_call_expr_loc (loc, atan2_fn, 2, i_arg, r_arg);
+       }
     }
 
   return NULL_TREE;
@@ -9137,67 +8043,6 @@ fold_builtin_frexp (location_t loc, tree arg0, tree arg1, tree rettype)
   return NULL_TREE;
 }
 
-/* Fold a call to builtin ldexp or scalbn/scalbln.  If LDEXP is true
-   then we can assume the base is two.  If it's false, then we have to
-   check the mode of the TYPE parameter in certain cases.  */
-
-static tree
-fold_builtin_load_exponent (location_t loc, tree arg0, tree arg1,
-                           tree type, bool ldexp)
-{
-  if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, INTEGER_TYPE))
-    {
-      STRIP_NOPS (arg0);
-      STRIP_NOPS (arg1);
-
-      /* If arg0 is 0, Inf or NaN, or if arg1 is 0, then return arg0.  */
-      if (real_zerop (arg0) || integer_zerop (arg1)
-         || (TREE_CODE (arg0) == REAL_CST
-             && !real_isfinite (&TREE_REAL_CST (arg0))))
-       return omit_one_operand_loc (loc, type, arg0, arg1);
-
-      /* If both arguments are constant, then try to evaluate it.  */
-      if ((ldexp || REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2)
-         && TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0)
-         && tree_fits_shwi_p (arg1))
-        {
-         /* Bound the maximum adjustment to twice the range of the
-            mode's valid exponents.  Use abs to ensure the range is
-            positive as a sanity check.  */
-         const long max_exp_adj = 2 *
-           labs (REAL_MODE_FORMAT (TYPE_MODE (type))->emax
-                - REAL_MODE_FORMAT (TYPE_MODE (type))->emin);
-
-         /* Get the user-requested adjustment.  */
-         const HOST_WIDE_INT req_exp_adj = tree_to_shwi (arg1);
-
-         /* The requested adjustment must be inside this range.  This
-            is a preliminary cap to avoid things like overflow, we
-            may still fail to compute the result for other reasons.  */
-         if (-max_exp_adj < req_exp_adj && req_exp_adj < max_exp_adj)
-           {
-             REAL_VALUE_TYPE initial_result;
-
-             real_ldexp (&initial_result, &TREE_REAL_CST (arg0), req_exp_adj);
-
-             /* Ensure we didn't overflow.  */
-             if (! real_isinf (&initial_result))
-               {
-                 const REAL_VALUE_TYPE trunc_result
-                   = real_value_truncate (TYPE_MODE (type), initial_result);
-
-                 /* Only proceed if the target mode can hold the
-                    resulting value.  */
-                 if (real_equal (&initial_result, &trunc_result))
-                   return build_real (type, trunc_result);
-               }
-           }
-       }
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold a call to builtin modf.  */
 
 static tree
@@ -9272,6 +8117,8 @@ fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
 
   mode = TYPE_MODE (TREE_TYPE (arg));
 
+  bool is_ibm_extended = MODE_COMPOSITE_P (mode);
+
   /* If there is no optab, try generic code.  */
   switch (DECL_FUNCTION_CODE (fndecl))
     {
@@ -9281,10 +8128,18 @@ fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
       {
        /* isinf(x) -> isgreater(fabs(x),DBL_MAX).  */
        tree const isgr_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
-       tree const type = TREE_TYPE (arg);
+       tree type = TREE_TYPE (arg);
        REAL_VALUE_TYPE r;
        char buf[128];
 
+       if (is_ibm_extended)
+         {
+           /* NaN and Inf are encoded in the high-order double value
+              only.  The low-order value is not significant.  */
+           type = double_type_node;
+           mode = DFmode;
+           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
+         }
        get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
        real_from_string (&r, buf);
        result = build_call_expr (isgr_fn, 2,
@@ -9297,10 +8152,18 @@ fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
       {
        /* isfinite(x) -> islessequal(fabs(x),DBL_MAX).  */
        tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
-       tree const type = TREE_TYPE (arg);
+       tree type = TREE_TYPE (arg);
        REAL_VALUE_TYPE r;
        char buf[128];
 
+       if (is_ibm_extended)
+         {
+           /* NaN and Inf are encoded in the high-order double value
+              only.  The low-order value is not significant.  */
+           type = double_type_node;
+           mode = DFmode;
+           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
+         }
        get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
        real_from_string (&r, buf);
        result = build_call_expr (isle_fn, 2,
@@ -9320,21 +8183,72 @@ fold_builtin_interclass_mathfn (location_t loc, tree fndecl, tree arg)
        /* isnormal(x) -> isgreaterequal(fabs(x),DBL_MIN) &
           islessequal(fabs(x),DBL_MAX).  */
        tree const isle_fn = builtin_decl_explicit (BUILT_IN_ISLESSEQUAL);
-       tree const isge_fn = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
-       tree const type = TREE_TYPE (arg);
+       tree type = TREE_TYPE (arg);
+       tree orig_arg, max_exp, min_exp;
+       machine_mode orig_mode = mode;
        REAL_VALUE_TYPE rmax, rmin;
        char buf[128];
 
+       orig_arg = arg = builtin_save_expr (arg);
+       if (is_ibm_extended)
+         {
+           /* Use double to test the normal range of IBM extended
+              precision.  Emin for IBM extended precision is
+              different to emin for IEEE double, being 53 higher
+              since the low double exponent is at least 53 lower
+              than the high double exponent.  */
+           type = double_type_node;
+           mode = DFmode;
+           arg = fold_build1_loc (loc, NOP_EXPR, type, arg);
+         }
+       arg = fold_build1_loc (loc, ABS_EXPR, type, arg);
+
        get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
        real_from_string (&rmax, buf);
-       sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (mode)->emin - 1);
+       sprintf (buf, "0x1p%d", REAL_MODE_FORMAT (orig_mode)->emin - 1);
        real_from_string (&rmin, buf);
-       arg = builtin_save_expr (fold_build1_loc (loc, ABS_EXPR, type, arg));
-       result = build_call_expr (isle_fn, 2, arg,
-                                 build_real (type, rmax));
-       result = fold_build2 (BIT_AND_EXPR, integer_type_node, result,
-                             build_call_expr (isge_fn, 2, arg,
-                                              build_real (type, rmin)));
+       max_exp = build_real (type, rmax);
+       min_exp = build_real (type, rmin);
+
+       max_exp = build_call_expr (isle_fn, 2, arg, max_exp);
+       if (is_ibm_extended)
+         {
+           /* Testing the high end of the range is done just using
+              the high double, using the same test as isfinite().
+              For the subnormal end of the range we first test the
+              high double, then if its magnitude is equal to the
+              limit of 0x1p-969, we test whether the low double is
+              non-zero and opposite sign to the high double.  */
+           tree const islt_fn = builtin_decl_explicit (BUILT_IN_ISLESS);
+           tree const isgt_fn = builtin_decl_explicit (BUILT_IN_ISGREATER);
+           tree gt_min = build_call_expr (isgt_fn, 2, arg, min_exp);
+           tree eq_min = fold_build2 (EQ_EXPR, integer_type_node,
+                                      arg, min_exp);
+           tree as_complex = build1 (VIEW_CONVERT_EXPR,
+                                     complex_double_type_node, orig_arg);
+           tree hi_dbl = build1 (REALPART_EXPR, type, as_complex);
+           tree lo_dbl = build1 (IMAGPART_EXPR, type, as_complex);
+           tree zero = build_real (type, dconst0);
+           tree hilt = build_call_expr (islt_fn, 2, hi_dbl, zero);
+           tree lolt = build_call_expr (islt_fn, 2, lo_dbl, zero);
+           tree logt = build_call_expr (isgt_fn, 2, lo_dbl, zero);
+           tree ok_lo = fold_build1 (TRUTH_NOT_EXPR, integer_type_node,
+                                     fold_build3 (COND_EXPR,
+                                                  integer_type_node,
+                                                  hilt, logt, lolt));
+           eq_min = fold_build2 (TRUTH_ANDIF_EXPR, integer_type_node,
+                                 eq_min, ok_lo);
+           min_exp = fold_build2 (TRUTH_ORIF_EXPR, integer_type_node,
+                                  gt_min, eq_min);
+         }
+       else
+         {
+           tree const isge_fn
+             = builtin_decl_explicit (BUILT_IN_ISGREATEREQUAL);
+           min_exp = build_call_expr (isge_fn, 2, arg, min_exp);
+         }
+       result = fold_build2 (BIT_AND_EXPR, integer_type_node,
+                             max_exp, min_exp);
        return result;
       }
     default:
@@ -9351,7 +8265,6 @@ static tree
 fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
-  REAL_VALUE_TYPE r;
 
   if (!validate_arg (arg, REAL_TYPE))
     return NULL_TREE;
@@ -9362,16 +8275,6 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
       if (!HONOR_INFINITIES (arg))
        return omit_one_operand_loc (loc, type, integer_zero_node, arg);
 
-      if (TREE_CODE (arg) == REAL_CST)
-       {
-         r = TREE_REAL_CST (arg);
-         if (real_isinf (&r))
-           return real_compare (GT_EXPR, &r, &dconst0)
-                  ? integer_one_node : integer_minus_one_node;
-         else
-           return integer_zero_node;
-       }
-
       return NULL_TREE;
 
     case BUILT_IN_ISINF_SIGN:
@@ -9380,7 +8283,7 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
        /* In a boolean context, GCC will fold the inner COND_EXPR to
           1.  So e.g. "if (isinf_sign(x))" would be folded to just
           "if (isinf(x) ? 1 : 0)" which becomes "if (isinf(x))". */
-       tree signbit_fn = mathfn_built_in_1 (TREE_TYPE (arg), BUILT_IN_SIGNBIT, 0);
+       tree signbit_fn = builtin_decl_explicit (BUILT_IN_SIGNBIT);
        tree isinf_fn = builtin_decl_explicit (BUILT_IN_ISINF);
        tree tmp = NULL_TREE;
 
@@ -9411,24 +8314,21 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
          && !HONOR_INFINITIES (arg))
        return omit_one_operand_loc (loc, type, integer_one_node, arg);
 
-      if (TREE_CODE (arg) == REAL_CST)
-       {
-         r = TREE_REAL_CST (arg);
-         return real_isfinite (&r) ? integer_one_node : integer_zero_node;
-       }
-
       return NULL_TREE;
 
     case BUILT_IN_ISNAN:
       if (!HONOR_NANS (arg))
        return omit_one_operand_loc (loc, type, integer_zero_node, arg);
 
-      if (TREE_CODE (arg) == REAL_CST)
-       {
-         r = TREE_REAL_CST (arg);
-         return real_isnan (&r) ? integer_one_node : integer_zero_node;
-       }
-
+      {
+       bool is_ibm_extended = MODE_COMPOSITE_P (TYPE_MODE (TREE_TYPE (arg)));
+       if (is_ibm_extended)
+         {
+           /* NaN and Inf are encoded in the high-order double value
+              only.  The low-order value is not significant.  */
+           arg = fold_build1_loc (loc, NOP_EXPR, double_type_node, arg);
+         }
+      }
       arg = builtin_save_expr (arg);
       return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
 
@@ -9561,18 +8461,28 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
 /* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
    arithmetics if it can never overflow, or into internal functions that
    return both result of arithmetics and overflowed boolean flag in
-   a complex integer result, or some other check for overflow.  */
+   a complex integer result, or some other check for overflow.
+   Similarly fold __builtin_{add,sub,mul}_overflow_p to just the overflow
+   checking part of that.  */
 
 static tree
 fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
                             tree arg0, tree arg1, tree arg2)
 {
   enum internal_fn ifn = IFN_LAST;
-  tree type = TREE_TYPE (TREE_TYPE (arg2));
-  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
+  /* The code of the expression corresponding to the type-generic
+     built-in, or ERROR_MARK for the type-specific ones.  */
+  enum tree_code opcode = ERROR_MARK;
+  bool ovf_only = false;
+
   switch (fcode)
     {
+    case BUILT_IN_ADD_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_ADD_OVERFLOW:
+      opcode = PLUS_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
@@ -9581,7 +8491,12 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     case BUILT_IN_UADDLL_OVERFLOW:
       ifn = IFN_ADD_OVERFLOW;
       break;
+    case BUILT_IN_SUB_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_SUB_OVERFLOW:
+      opcode = MINUS_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SSUB_OVERFLOW:
     case BUILT_IN_SSUBL_OVERFLOW:
     case BUILT_IN_SSUBLL_OVERFLOW:
@@ -9590,7 +8505,12 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     case BUILT_IN_USUBLL_OVERFLOW:
       ifn = IFN_SUB_OVERFLOW;
       break;
+    case BUILT_IN_MUL_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_MUL_OVERFLOW:
+      opcode = MULT_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SMUL_OVERFLOW:
     case BUILT_IN_SMULL_OVERFLOW:
     case BUILT_IN_SMULLL_OVERFLOW:
@@ -9602,6 +8522,25 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     default:
       gcc_unreachable ();
     }
+
+  /* For the "generic" overloads, the first two arguments can have different
+     types and the last argument determines the target type to use to check
+     for overflow.  The arguments of the other overloads all have the same
+     type.  */
+  tree type = ovf_only ? TREE_TYPE (arg2) : TREE_TYPE (TREE_TYPE (arg2));
+
+  /* For the __builtin_{add,sub,mul}_overflow_p builtins, when the first two
+     arguments are constant, attempt to fold the built-in call into a constant
+     expression indicating whether or not it detected an overflow.  */
+  if (ovf_only
+      && TREE_CODE (arg0) == INTEGER_CST
+      && TREE_CODE (arg1) == INTEGER_CST)
+    /* Perform the computation in the target type and check for overflow.  */
+    return omit_one_operand_loc (loc, boolean_type_node,
+                                arith_overflowed_p (opcode, type, arg0, arg1)
+                                ? boolean_true_node : boolean_false_node,
+                                arg2);
+
   tree ctype = build_complex_type (type);
   tree call = build_call_expr_internal_loc (loc, ifn, ctype,
                                            2, arg0, arg1);
@@ -9609,11 +8548,49 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
   tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
   tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
   ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+
+  if (ovf_only)
+    return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
+
+  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
   tree store
     = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres);
   return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres);
 }
 
+/* Fold a call to __builtin_FILE to a constant string.  */
+
+static inline tree
+fold_builtin_FILE (location_t loc)
+{
+  if (const char *fname = LOCATION_FILE (loc))
+    return build_string_literal (strlen (fname) + 1, fname);
+
+  return build_string_literal (1, "");
+}
+
+/* Fold a call to __builtin_FUNCTION to a constant string.  */
+
+static inline tree
+fold_builtin_FUNCTION ()
+{
+  if (current_function_decl)
+    {
+      const char *name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
+      return build_string_literal (strlen (name) + 1, name);
+    }
+
+  return build_string_literal (1, "");
+}
+
+/* Fold a call to __builtin_LINE to an integer constant.  */
+
+static inline tree
+fold_builtin_LINE (location_t loc, tree type)
+{
+  return build_int_cst (type, LOCATION_LINE (loc));
+}
+
 /* Fold a call to built-in function FNDECL with 0 arguments.
    This function returns NULL_TREE if no simplification was possible.  */
 
@@ -9624,13 +8601,24 @@ fold_builtin_0 (location_t loc, tree fndecl)
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
   switch (fcode)
     {
+    case BUILT_IN_FILE:
+      return fold_builtin_FILE (loc);
+
+    case BUILT_IN_FUNCTION:
+      return fold_builtin_FUNCTION ();
+
+    case BUILT_IN_LINE:
+      return fold_builtin_LINE (loc, type);
+
     CASE_FLT_FN (BUILT_IN_INF):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_INF):
     case BUILT_IN_INFD32:
     case BUILT_IN_INFD64:
     case BUILT_IN_INFD128:
       return fold_builtin_inf (loc, type, true);
 
     CASE_FLT_FN (BUILT_IN_HUGE_VAL):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_HUGE_VAL):
       return fold_builtin_inf (loc, type, false);
 
     case BUILT_IN_CLASSIFY_TYPE:
@@ -9650,6 +8638,13 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+  if (TREE_CODE (arg0) == ERROR_MARK)
+    return NULL_TREE;
+
+  if (tree ret = fold_const_call (as_combined_fn (fcode), type, arg0))
+    return ret;
+
   switch (fcode)
     {
     case BUILT_IN_CONSTANT_P:
@@ -9665,343 +8660,45 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
        return val;
       }
 
-    case BUILT_IN_CLASSIFY_TYPE:
-      return fold_builtin_classify_type (arg0);
-
-    case BUILT_IN_STRLEN:
-      return fold_builtin_strlen (loc, type, arg0);
-
-    CASE_FLT_FN (BUILT_IN_FABS):
-    case BUILT_IN_FABSD32:
-    case BUILT_IN_FABSD64:
-    case BUILT_IN_FABSD128:
-      return fold_builtin_fabs (loc, arg0, type);
-
-    case BUILT_IN_ABS:
-    case BUILT_IN_LABS:
-    case BUILT_IN_LLABS:
-    case BUILT_IN_IMAXABS:
-      return fold_builtin_abs (loc, arg0, type);
-
-    CASE_FLT_FN (BUILT_IN_CONJ):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return fold_build1_loc (loc, CONJ_EXPR, type, arg0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CREAL):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CIMAG):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return non_lvalue_loc (loc, fold_build1_loc (loc, IMAGPART_EXPR, type, arg0));
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CCOS):
-      return fold_builtin_ccos (loc, arg0, type, fndecl, /*hyper=*/ false);
-
-    CASE_FLT_FN (BUILT_IN_CCOSH):
-      return fold_builtin_ccos (loc, arg0, type, fndecl, /*hyper=*/ true);
-
-    CASE_FLT_FN (BUILT_IN_CPROJ):
-      return fold_builtin_cproj (loc, arg0, type);
-
-    CASE_FLT_FN (BUILT_IN_CSIN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_sin);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CSINH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_sinh);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CTAN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_tan);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CTANH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_tanh);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CLOG):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_log);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CSQRT):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_sqrt);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CASIN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_asin);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CACOS):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_acos);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CATAN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_atan);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CASINH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_asinh);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CACOSH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_acosh);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CATANH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_atanh);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_CABS):
-      return fold_builtin_cabs (loc, arg0, type, fndecl);
-
-    CASE_FLT_FN (BUILT_IN_CARG):
-      return fold_builtin_carg (loc, arg0, type);
-
-    CASE_FLT_FN (BUILT_IN_SQRT):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_sqrt, &dconst0, NULL, true);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_CBRT):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_cbrt, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_ASIN):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_asin,
-                            &dconstm1, &dconst1, true);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ACOS):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_acos,
-                            &dconstm1, &dconst1, true);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ATAN):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_atan, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ASINH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_asinh, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ACOSH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_acosh,
-                            &dconst1, NULL, true);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ATANH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_atanh,
-                            &dconstm1, &dconst1, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_SIN):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_sin, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_COS):
-      return fold_builtin_cos (loc, arg0, type, fndecl);
-
-    CASE_FLT_FN (BUILT_IN_TAN):
-      return fold_builtin_tan (arg0, type);
-
-    CASE_FLT_FN (BUILT_IN_CEXP):
-      return fold_builtin_cexp (loc, arg0, type);
-
-    CASE_FLT_FN (BUILT_IN_CEXPI):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_sincos (arg0, NULL_TREE, NULL_TREE);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_SINH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_sinh, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_COSH):
-      return fold_builtin_cosh (loc, arg0, type, fndecl);
-
-    CASE_FLT_FN (BUILT_IN_TANH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_tanh, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ERF):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_erf, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ERFC):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_erfc, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_TGAMMA):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_gamma, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_EXP):
-      return fold_builtin_exponent (loc, fndecl, arg0, mpfr_exp);
-
-    CASE_FLT_FN (BUILT_IN_EXP2):
-      return fold_builtin_exponent (loc, fndecl, arg0, mpfr_exp2);
-
-    CASE_FLT_FN (BUILT_IN_EXP10):
-    CASE_FLT_FN (BUILT_IN_POW10):
-      return fold_builtin_exponent (loc, fndecl, arg0, mpfr_exp10);
-
-    CASE_FLT_FN (BUILT_IN_EXPM1):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_expm1, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG):
-      if (validate_arg (arg0, REAL_TYPE))
-        return do_mpfr_arg1 (arg0, type, mpfr_log, &dconst0, NULL, false);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG2):
-      if (validate_arg (arg0, REAL_TYPE))
-        return do_mpfr_arg1 (arg0, type, mpfr_log2, &dconst0, NULL, false);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG10):
-      if (validate_arg (arg0, REAL_TYPE))
-        return do_mpfr_arg1 (arg0, type, mpfr_log10, &dconst0, NULL, false);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG1P):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_log1p,
-                            &dconstm1, NULL, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_J0):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_j0,
-                            NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_J1):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_j1,
-                            NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_Y0):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_y0,
-                            &dconst0, NULL, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_Y1):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_y1,
-                            &dconst0, NULL, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_NAN):
-    case BUILT_IN_NAND32:
-    case BUILT_IN_NAND64:
-    case BUILT_IN_NAND128:
-      return fold_builtin_nan (arg0, type, true);
-
-    CASE_FLT_FN (BUILT_IN_NANS):
-      return fold_builtin_nan (arg0, type, false);
-
-    CASE_FLT_FN (BUILT_IN_FLOOR):
-      return fold_builtin_floor (loc, fndecl, arg0);
-
-    CASE_FLT_FN (BUILT_IN_CEIL):
-      return fold_builtin_ceil (loc, fndecl, arg0);
-
-    CASE_FLT_FN (BUILT_IN_TRUNC):
-      return fold_builtin_trunc (loc, fndecl, arg0);
-
-    CASE_FLT_FN (BUILT_IN_ROUND):
-      return fold_builtin_round (loc, fndecl, arg0);
-
-    CASE_FLT_FN (BUILT_IN_NEARBYINT):
-    CASE_FLT_FN (BUILT_IN_RINT):
-      return fold_trunc_transparent_mathfn (loc, fndecl, arg0);
-
-    CASE_FLT_FN (BUILT_IN_ICEIL):
-    CASE_FLT_FN (BUILT_IN_LCEIL):
-    CASE_FLT_FN (BUILT_IN_LLCEIL):
-    CASE_FLT_FN (BUILT_IN_LFLOOR):
-    CASE_FLT_FN (BUILT_IN_IFLOOR):
-    CASE_FLT_FN (BUILT_IN_LLFLOOR):
-    CASE_FLT_FN (BUILT_IN_IROUND):
-    CASE_FLT_FN (BUILT_IN_LROUND):
-    CASE_FLT_FN (BUILT_IN_LLROUND):
-      return fold_builtin_int_roundingfn (loc, fndecl, arg0);
+    case BUILT_IN_CLASSIFY_TYPE:
+      return fold_builtin_classify_type (arg0);
 
-    CASE_FLT_FN (BUILT_IN_IRINT):
-    CASE_FLT_FN (BUILT_IN_LRINT):
-    CASE_FLT_FN (BUILT_IN_LLRINT):
-      return fold_fixed_mathfn (loc, fndecl, arg0);
+    case BUILT_IN_STRLEN:
+      return fold_builtin_strlen (loc, type, arg0);
 
-    case BUILT_IN_BSWAP16:
-    case BUILT_IN_BSWAP32:
-    case BUILT_IN_BSWAP64:
-      return fold_builtin_bswap (fndecl, arg0);
+    CASE_FLT_FN (BUILT_IN_FABS):
+    CASE_FLT_FN_FLOATN_NX (BUILT_IN_FABS):
+    case BUILT_IN_FABSD32:
+    case BUILT_IN_FABSD64:
+    case BUILT_IN_FABSD128:
+      return fold_builtin_fabs (loc, arg0, type);
 
-    CASE_INT_FN (BUILT_IN_FFS):
-    CASE_INT_FN (BUILT_IN_CLZ):
-    CASE_INT_FN (BUILT_IN_CTZ):
-    CASE_INT_FN (BUILT_IN_CLRSB):
-    CASE_INT_FN (BUILT_IN_POPCOUNT):
-    CASE_INT_FN (BUILT_IN_PARITY):
-      return fold_builtin_bitop (fndecl, arg0);
+    case BUILT_IN_ABS:
+    case BUILT_IN_LABS:
+    case BUILT_IN_LLABS:
+    case BUILT_IN_IMAXABS:
+      return fold_builtin_abs (loc, arg0, type);
 
-    CASE_FLT_FN (BUILT_IN_SIGNBIT):
-      return fold_builtin_signbit (loc, arg0, type);
+    CASE_FLT_FN (BUILT_IN_CONJ):
+      if (validate_arg (arg0, COMPLEX_TYPE)
+       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
+       return fold_build1_loc (loc, CONJ_EXPR, type, arg0);
+    break;
 
-    CASE_FLT_FN (BUILT_IN_SIGNIFICAND):
-      return fold_builtin_significand (loc, arg0, type);
+    CASE_FLT_FN (BUILT_IN_CREAL):
+      if (validate_arg (arg0, COMPLEX_TYPE)
+       && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
+       return non_lvalue_loc (loc, fold_build1_loc (loc, REALPART_EXPR, type, arg0));
+    break;
 
-    CASE_FLT_FN (BUILT_IN_ILOGB):
-    CASE_FLT_FN (BUILT_IN_LOGB):
-      return fold_builtin_logb (loc, arg0, type);
+    CASE_FLT_FN (BUILT_IN_CIMAG):
+      if (validate_arg (arg0, COMPLEX_TYPE)
+         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
+       return non_lvalue_loc (loc, fold_build1_loc (loc, IMAGPART_EXPR, type, arg0));
+    break;
+
+    CASE_FLT_FN (BUILT_IN_CARG):
+      return fold_builtin_carg (loc, arg0, type);
 
     case BUILT_IN_ISASCII:
       return fold_builtin_isascii (loc, arg0);
@@ -10069,28 +8766,15 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-  switch (fcode)
-    {
-    CASE_FLT_FN (BUILT_IN_JN):
-      if (validate_arg (arg0, INTEGER_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_bessel_n (arg0, arg1, type, mpfr_jn, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_YN):
-      if (validate_arg (arg0, INTEGER_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_bessel_n (arg0, arg1, type, mpfr_yn,
-                                &dconst0, false);
-    break;
+  if (TREE_CODE (arg0) == ERROR_MARK
+      || TREE_CODE (arg1) == ERROR_MARK)
+    return NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_DREM):
-    CASE_FLT_FN (BUILT_IN_REMAINDER):
-      if (validate_arg (arg0, REAL_TYPE)
-          && validate_arg (arg1, REAL_TYPE))
-        return do_mpfr_arg2 (arg0, arg1, type, mpfr_remainder);
-    break;
+  if (tree ret = fold_const_call (as_combined_fn (fcode), type, arg0, arg1))
+    return ret;
 
+  switch (fcode)
+    {
     CASE_FLT_FN_REENT (BUILT_IN_GAMMA): /* GAMMA_R */
     CASE_FLT_FN_REENT (BUILT_IN_LGAMMA): /* LGAMMA_R */
       if (validate_arg (arg0, REAL_TYPE)
@@ -10098,83 +8782,24 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
        return do_mpfr_lgamma_r (arg0, arg1, type);
     break;
 
-    CASE_FLT_FN (BUILT_IN_ATAN2):
-      if (validate_arg (arg0, REAL_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_arg2 (arg0, arg1, type, mpfr_atan2);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_FDIM):
-      if (validate_arg (arg0, REAL_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_arg2 (arg0, arg1, type, mpfr_dim);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_HYPOT):
-      return fold_builtin_hypot (loc, fndecl, arg0, arg1, type);
-
-    CASE_FLT_FN (BUILT_IN_CPOW):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE
-         && validate_arg (arg1, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg1))) == REAL_TYPE)
-       return do_mpc_arg2 (arg0, arg1, type, /*do_nonfinite=*/ 0, mpc_pow);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_LDEXP):
-      return fold_builtin_load_exponent (loc, arg0, arg1, type, /*ldexp=*/true);
-    CASE_FLT_FN (BUILT_IN_SCALBN):
-    CASE_FLT_FN (BUILT_IN_SCALBLN):
-      return fold_builtin_load_exponent (loc, arg0, arg1,
-                                        type, /*ldexp=*/false);
-
     CASE_FLT_FN (BUILT_IN_FREXP):
       return fold_builtin_frexp (loc, arg0, arg1, type);
 
     CASE_FLT_FN (BUILT_IN_MODF):
       return fold_builtin_modf (loc, arg0, arg1, type);
 
-    case BUILT_IN_STRSTR:
-      return fold_builtin_strstr (loc, arg0, arg1, type);
-
     case BUILT_IN_STRSPN:
       return fold_builtin_strspn (loc, arg0, arg1);
 
     case BUILT_IN_STRCSPN:
       return fold_builtin_strcspn (loc, arg0, arg1);
 
-    case BUILT_IN_STRCHR:
-    case BUILT_IN_INDEX:
-      return fold_builtin_strchr (loc, arg0, arg1, type);
-
-    case BUILT_IN_STRRCHR:
-    case BUILT_IN_RINDEX:
-      return fold_builtin_strrchr (loc, arg0, arg1, type);
-
-    case BUILT_IN_STRCMP:
-      return fold_builtin_strcmp (loc, arg0, arg1);
-
     case BUILT_IN_STRPBRK:
       return fold_builtin_strpbrk (loc, arg0, arg1, type);
 
     case BUILT_IN_EXPECT:
       return fold_builtin_expect (loc, arg0, arg1, NULL_TREE);
 
-    CASE_FLT_FN (BUILT_IN_POW):
-      return fold_builtin_pow (loc, fndecl, arg0, arg1, type);
-
-    CASE_FLT_FN (BUILT_IN_POWI):
-      return fold_builtin_powi (loc, fndecl, arg0, arg1, type);
-
-    CASE_FLT_FN (BUILT_IN_COPYSIGN):
-      return fold_builtin_copysign (loc, fndecl, arg0, arg1, type);
-
-    CASE_FLT_FN (BUILT_IN_FMIN):
-      return fold_builtin_fmin_fmax (loc, arg0, arg1, type, /*max=*/false);
-
-    CASE_FLT_FN (BUILT_IN_FMAX):
-      return fold_builtin_fmin_fmax (loc, arg0, arg1, type, /*max=*/true);
-
     case BUILT_IN_ISGREATER:
       return fold_builtin_unordered_cmp (loc, fndecl,
                                         arg0, arg1, UNLE_EXPR, LE_EXPR);
@@ -10224,6 +8849,16 @@ fold_builtin_3 (location_t loc, tree fndecl,
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+  if (TREE_CODE (arg0) == ERROR_MARK
+      || TREE_CODE (arg1) == ERROR_MARK
+      || TREE_CODE (arg2) == ERROR_MARK)
+    return NULL_TREE;
+
+  if (tree ret = fold_const_call (as_combined_fn (fcode), type,
+                                 arg0, arg1, arg2))
+    return ret;
+
   switch (fcode)
     {
 
@@ -10232,7 +8867,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
 
     CASE_FLT_FN (BUILT_IN_FMA):
       return fold_builtin_fma (loc, arg0, arg1, arg2, type);
-    break;
 
     CASE_FLT_FN (BUILT_IN_REMQUO):
       if (validate_arg (arg0, REAL_TYPE)
@@ -10241,12 +8875,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
        return do_mpfr_remquo (arg0, arg1, arg2);
     break;
 
-    case BUILT_IN_STRNCMP:
-      return fold_builtin_strncmp (loc, arg0, arg1, arg2);
-
-    case BUILT_IN_MEMCHR:
-      return fold_builtin_memchr (loc, arg0, arg1, arg2, type);
-
     case BUILT_IN_BCMP:
     case BUILT_IN_MEMCMP:
       return fold_builtin_memcmp (loc, arg0, arg1, arg2);;
@@ -10257,6 +8885,9 @@ fold_builtin_3 (location_t loc, tree fndecl,
     case BUILT_IN_ADD_OVERFLOW:
     case BUILT_IN_SUB_OVERFLOW:
     case BUILT_IN_MUL_OVERFLOW:
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
@@ -10473,7 +9104,7 @@ rewrite_call_expr (location_t loc, tree exp, int skip, tree fndecl, int n, ...)
 }
 
 /* Validate a single argument ARG against a tree code CODE representing
-   a type.  */
+   a type.  Return true when argument is valid.  */
 
 static bool
 validate_arg (const_tree arg, enum tree_code code)
@@ -10549,219 +9180,35 @@ default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
                        rtx target ATTRIBUTE_UNUSED,
                        rtx subtarget ATTRIBUTE_UNUSED,
                        machine_mode mode ATTRIBUTE_UNUSED,
-                       int ignore ATTRIBUTE_UNUSED)
-{
-  return NULL_RTX;
-}
-
-/* Returns true is EXP represents data that would potentially reside
-   in a readonly section.  */
-
-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;
-}
-
-/* Simplify a call to the strstr builtin.  S1 and S2 are the arguments
-   to the call, and TYPE is its return type.
-
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.
-
-   The simplified form may be a constant or other expression which
-   computes the same value, but in a more efficient manner (including
-   calls to other builtin functions).
-
-   The call may contain arguments which need to be evaluated, but
-   which are not useful to determine the result of the call.  In
-   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
-   COMPOUND_EXPR will be an argument which must be evaluated.
-   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
-   COMPOUND_EXPR in the chain will contain the tree for the simplified
-   form of the builtin function call.  */
-
-static tree
-fold_builtin_strstr (location_t loc, tree s1, tree s2, tree type)
-{
-  if (!validate_arg (s1, POINTER_TYPE)
-      || !validate_arg (s2, POINTER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      tree fn;
-      const char *p1, *p2;
-
-      p2 = c_getstr (s2);
-      if (p2 == NULL)
-       return NULL_TREE;
-
-      p1 = c_getstr (s1);
-      if (p1 != NULL)
-       {
-         const char *r = strstr (p1, p2);
-         tree tem;
-
-         if (r == NULL)
-           return build_int_cst (TREE_TYPE (s1), 0);
-
-         /* Return an offset into the constant string argument.  */
-         tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
-         return fold_convert_loc (loc, type, tem);
-       }
-
-      /* The argument is const char *, and the result is char *, so we need
-        a type conversion here to avoid a warning.  */
-      if (p2[0] == '\0')
-       return fold_convert_loc (loc, type, s1);
-
-      if (p2[1] != '\0')
-       return NULL_TREE;
-
-      fn = builtin_decl_implicit (BUILT_IN_STRCHR);
-      if (!fn)
-       return NULL_TREE;
-
-      /* New argument list transforming strstr(s1, s2) to
-        strchr(s1, s2[0]).  */
-      return build_call_expr_loc (loc, fn, 2, s1,
-                                 build_int_cst (integer_type_node, p2[0]));
-    }
-}
-
-/* Simplify a call to the strchr builtin.  S1 and S2 are the arguments to
-   the call, and TYPE is its return type.
-
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.
-
-   The simplified form may be a constant or other expression which
-   computes the same value, but in a more efficient manner (including
-   calls to other builtin functions).
-
-   The call may contain arguments which need to be evaluated, but
-   which are not useful to determine the result of the call.  In
-   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
-   COMPOUND_EXPR will be an argument which must be evaluated.
-   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
-   COMPOUND_EXPR in the chain will contain the tree for the simplified
-   form of the builtin function call.  */
-
-static tree
-fold_builtin_strchr (location_t loc, tree s1, tree s2, tree type)
-{
-  if (!validate_arg (s1, POINTER_TYPE)
-      || !validate_arg (s2, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      const char *p1;
-
-      if (TREE_CODE (s2) != INTEGER_CST)
-       return NULL_TREE;
-
-      p1 = c_getstr (s1);
-      if (p1 != NULL)
-       {
-         char c;
-         const char *r;
-         tree tem;
-
-         if (target_char_cast (s2, &c))
-           return NULL_TREE;
-
-         r = strchr (p1, c);
-
-         if (r == NULL)
-           return build_int_cst (TREE_TYPE (s1), 0);
-
-         /* Return an offset into the constant string argument.  */
-         tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
-         return fold_convert_loc (loc, type, tem);
-       }
-      return NULL_TREE;
-    }
-}
-
-/* Simplify a call to the strrchr builtin.  S1 and S2 are the arguments to
-   the call, and TYPE is its return type.
-
-   Return NULL_TREE if no simplification was possible, otherwise return the
-   simplified form of the call as a tree.
-
-   The simplified form may be a constant or other expression which
-   computes the same value, but in a more efficient manner (including
-   calls to other builtin functions).
-
-   The call may contain arguments which need to be evaluated, but
-   which are not useful to determine the result of the call.  In
-   this case we return a chain of COMPOUND_EXPRs.  The LHS of each
-   COMPOUND_EXPR will be an argument which must be evaluated.
-   COMPOUND_EXPRs are chained through their RHS.  The RHS of the last
-   COMPOUND_EXPR in the chain will contain the tree for the simplified
-   form of the builtin function call.  */
-
-static tree
-fold_builtin_strrchr (location_t loc, tree s1, tree s2, tree type)
-{
-  if (!validate_arg (s1, POINTER_TYPE)
-      || !validate_arg (s2, INTEGER_TYPE))
-    return NULL_TREE;
-  else
-    {
-      tree fn;
-      const char *p1;
-
-      if (TREE_CODE (s2) != INTEGER_CST)
-       return NULL_TREE;
-
-      p1 = c_getstr (s1);
-      if (p1 != NULL)
-       {
-         char c;
-         const char *r;
-         tree tem;
-
-         if (target_char_cast (s2, &c))
-           return NULL_TREE;
-
-         r = strrchr (p1, c);
+                       int ignore ATTRIBUTE_UNUSED)
+{
+  return NULL_RTX;
+}
 
-         if (r == NULL)
-           return build_int_cst (TREE_TYPE (s1), 0);
+/* Returns true is EXP represents data that would potentially reside
+   in a readonly section.  */
 
-         /* Return an offset into the constant string argument.  */
-         tem = fold_build_pointer_plus_hwi_loc (loc, s1, r - p1);
-         return fold_convert_loc (loc, type, tem);
-       }
+bool
+readonly_data_expr (tree exp)
+{
+  STRIP_NOPS (exp);
 
-      if (! integer_zerop (s2))
-       return NULL_TREE;
+  if (TREE_CODE (exp) != ADDR_EXPR)
+    return false;
 
-      fn = builtin_decl_implicit (BUILT_IN_STRCHR);
-      if (!fn)
-       return NULL_TREE;
+  exp = get_base_address (TREE_OPERAND (exp, 0));
+  if (!exp)
+    return false;
 
-      /* Transform strrchr(s1, '\0') to strchr(s1, '\0').  */
-      return build_call_expr_loc (loc, fn, 2, s1, s2);
-    }
+  /* 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
+      || (VAR_P (exp) && TREE_STATIC (exp)))
+    return decl_readonly_section (exp, 0);
+  else
+    return false;
 }
 
 /* Simplify a call to the strpbrk builtin.  S1 and S2 are the arguments
@@ -10858,13 +9305,6 @@ fold_builtin_strspn (location_t loc, tree s1, tree s2)
     {
       const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
 
-      /* If both arguments are constants, evaluate at compile-time.  */
-      if (p1 && p2)
-       {
-         const size_t r = strspn (p1, p2);
-         return build_int_cst (size_type_node, r);
-       }
-
       /* If either argument is "", return NULL_TREE.  */
       if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
        /* Evaluate and ignore both arguments in case either one has
@@ -10901,16 +9341,8 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2)
     return NULL_TREE;
   else
     {
-      const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
-
-      /* If both arguments are constants, evaluate at compile-time.  */
-      if (p1 && p2)
-       {
-         const size_t r = strcspn (p1, p2);
-         return build_int_cst (size_type_node, r);
-       }
-
       /* If the first argument is "", return NULL_TREE.  */
+      const char *p1 = c_getstr (s1);
       if (p1 && *p1 == '\0')
        {
          /* Evaluate and ignore argument s2 in case it has
@@ -10920,6 +9352,7 @@ fold_builtin_strcspn (location_t loc, tree s1, tree s2)
        }
 
       /* If the second argument is "", return __builtin_strlen(s1).  */
+      const char *p2 = c_getstr (s2);
       if (p2 && *p2 == '\0')
        {
          tree fn = builtin_decl_implicit (BUILT_IN_STRLEN);
@@ -11028,7 +9461,7 @@ fold_builtin_next_arg (tree exp, bool va_start_p)
        {
          warning_at (current_location,
                      OPT_Wvarargs,
-                     "undefined behaviour when second parameter of "
+                     "undefined behavior when second parameter of "
                      "%<va_start%> is declared with %<register%> storage");
        }
 
@@ -11105,22 +9538,22 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
   len = CALL_EXPR_ARG (exp, 2);
   size = CALL_EXPR_ARG (exp, 3);
 
-  if (! tree_fits_uhwi_p (size))
+  bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
+                              exp, len, /*maxlen=*/NULL_TREE,
+                              /*str=*/NULL_TREE, size);
+
+  if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
 
   if (tree_fits_uhwi_p (len) || integer_all_onesp (size))
     {
-      tree fn;
-
-      if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
-       {
-         warning_at (tree_nonartificial_location (exp),
-                     0, "%Kcall to %D will always overflow destination buffer",
-                     exp, get_callee_fndecl (exp));
-         return NULL_RTX;
-       }
+      /* Avoid transforming the checking call to an ordinary one when
+        an overflow has been detected or when the call couldn't be
+        validated because the size is not constant.  */
+      if (!sizes_ok && !integer_all_onesp (size) && tree_int_cst_lt (size, len))
+       return NULL_RTX;
 
-      fn = NULL_TREE;
+      tree fn = NULL_TREE;
       /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
         mem{cpy,pcpy,move,set} is available.  */
       switch (fcode)
@@ -11206,68 +9639,68 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 static void
 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);
+  /* The source string.  */
+  tree srcstr = NULL_TREE;
+  /* The size of the destination object.  */
+  tree objsize = NULL_TREE;
+  /* The string that is being concatenated with (as in __strcat_chk)
+     or null if it isn't.  */
+  tree catstr = NULL_TREE;
+  /* The maximum length of the source sequence in a bounded operation
+     (such as __strncat_chk) or null if the operation isn't bounded
+     (such as __strcat_chk).  */
+  tree maxlen = NULL_TREE;
 
   switch (fcode)
     {
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
-    /* For __strcat_chk the warning will be emitted only if overflowing
-       by at least strlen (dest) + 1 bytes.  */
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 2);
+      break;
+
     case BUILT_IN_STRCAT_CHK:
-      len = CALL_EXPR_ARG (exp, 1);
-      size = CALL_EXPR_ARG (exp, 2);
-      is_strlen = 1;
+      /* For __strcat_chk the warning will be emitted only if overflowing
+        by at least strlen (dest) + 1 bytes.  */
+      catstr = CALL_EXPR_ARG (exp, 0);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 2);
       break;
+
     case BUILT_IN_STRNCAT_CHK:
+      catstr = CALL_EXPR_ARG (exp, 0);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      maxlen = CALL_EXPR_ARG (exp, 2);
+      objsize = CALL_EXPR_ARG (exp, 3);
+      break;
+
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
-      len = CALL_EXPR_ARG (exp, 2);
-      size = CALL_EXPR_ARG (exp, 3);
+      srcstr = CALL_EXPR_ARG (exp, 1);
+      maxlen = CALL_EXPR_ARG (exp, 2);
+      objsize = CALL_EXPR_ARG (exp, 3);
       break;
+
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      len = CALL_EXPR_ARG (exp, 1);
-      size = CALL_EXPR_ARG (exp, 3);
+      maxlen = CALL_EXPR_ARG (exp, 1);
+      objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (!len || !size)
-    return;
-
-  if (! tree_fits_uhwi_p (size) || integer_all_onesp (size))
-    return;
-
-  if (is_strlen)
-    {
-      len = c_strlen (len, 1);
-      if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
-       return;
-    }
-  else if (fcode == BUILT_IN_STRNCAT_CHK)
+  if (catstr && maxlen)
     {
-      tree src = CALL_EXPR_ARG (exp, 1);
-      if (! src || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size))
-       return;
-      src = c_strlen (src, 1);
-      if (! src || ! tree_fits_uhwi_p (src))
-       {
-         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))
+      /* Check __strncat_chk.  There is no way to determine the length
+        of the string to which the source string is being appended so
+        just warn when the length of the source string is not known.  */
+      if (!check_strncat_sizes (exp, objsize))
        return;
     }
-  else if (! tree_fits_uhwi_p (len) || ! tree_int_cst_lt (size, len))
-    return;
 
-  warning_at (loc, 0, "%Kcall to %D will always overflow destination buffer",
-             exp, get_callee_fndecl (exp));
+  check_sizes (OPT_Wstringop_overflow_, exp,
+              /*size=*/NULL_TREE, maxlen, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -11321,10 +9754,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
   else
     return;
 
-  if (! tree_int_cst_lt (len, size))
-    warning_at (tree_nonartificial_location (exp),
-               0, "%Kcall to %D will always overflow destination buffer",
-               exp, get_callee_fndecl (exp));
+  /* Add one for the terminating nul.  */
+  len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+  check_sizes (OPT_Wstringop_overflow_,
+              exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -11380,7 +9813,7 @@ fold_builtin_object_size (tree ptr, tree ost)
 
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
-      bytes = compute_builtin_object_size (ptr, object_size_type);
+      compute_builtin_object_size (ptr, object_size_type, &bytes);
       if (wi::fits_to_tree_p (bytes, size_type_node))
        return build_int_cstu (size_type_node, bytes);
     }
@@ -11389,9 +9822,8 @@ fold_builtin_object_size (tree ptr, tree ost)
       /* If object size is not known yet, delay folding until
        later.  Maybe subsequent passes will help determining
        it.  */
-      bytes = compute_builtin_object_size (ptr, object_size_type);
-      if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 ? -1 : 0)
-          && wi::fits_to_tree_p (bytes, size_type_node))
+      if (compute_builtin_object_size (ptr, object_size_type, &bytes)
+         && wi::fits_to_tree_p (bytes, size_type_node))
        return build_int_cstu (size_type_node, bytes);
     }
 
@@ -11547,264 +9979,6 @@ do_mpc_ckconv (mpc_srcptr m, tree type, int inexact, int force_convert)
   return NULL_TREE;
 }
 
-/* If argument ARG is a REAL_CST, call the one-argument mpfr function
-   FUNC on it and return the resulting value as a tree with type TYPE.
-   If MIN and/or MAX are not NULL, then the supplied ARG must be
-   within those bounds.  If INCLUSIVE is true, then MIN/MAX are
-   acceptable values, otherwise they are not.  The mpfr precision is
-   set to the precision of TYPE.  We assume that function FUNC returns
-   zero if the result could be calculated exactly within the requested
-   precision.  */
-
-static tree
-do_mpfr_arg1 (tree arg, tree type, int (*func)(mpfr_ptr, mpfr_srcptr, mp_rnd_t),
-             const REAL_VALUE_TYPE *min, const REAL_VALUE_TYPE *max,
-             bool inclusive)
-{
-  tree result = NULL_TREE;
-
-  STRIP_NOPS (arg);
-
-  /* To proceed, MPFR must exactly represent the target floating point
-     format, which only happens when the target base equals two.  */
-  if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2
-      && TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg);
-
-      if (real_isfinite (ra)
-         && (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min))
-         && (!max || real_compare (inclusive ? LE_EXPR: LT_EXPR , ra, max)))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m;
-
-         mpfr_init2 (m, prec);
-         mpfr_from_real (m, ra, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m, m, rnd);
-         result = do_mpfr_ckconv (m, type, inexact);
-         mpfr_clear (m);
-       }
-    }
-
-  return result;
-}
-
-/* If argument ARG is a REAL_CST, call the two-argument mpfr function
-   FUNC on it and return the resulting value as a tree with type TYPE.
-   The mpfr precision is set to the precision of TYPE.  We assume that
-   function FUNC returns zero if the result could be calculated
-   exactly within the requested precision.  */
-
-static tree
-do_mpfr_arg2 (tree arg1, tree arg2, tree type,
-             int (*func)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t))
-{
-  tree result = NULL_TREE;
-
-  STRIP_NOPS (arg1);
-  STRIP_NOPS (arg2);
-
-  /* To proceed, MPFR must exactly represent the target floating point
-     format, which only happens when the target base equals two.  */
-  if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2
-      && TREE_CODE (arg1) == REAL_CST && !TREE_OVERFLOW (arg1)
-      && TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2))
-    {
-      const REAL_VALUE_TYPE *const ra1 = &TREE_REAL_CST (arg1);
-      const REAL_VALUE_TYPE *const ra2 = &TREE_REAL_CST (arg2);
-
-      if (real_isfinite (ra1) && real_isfinite (ra2))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m1, m2;
-
-         mpfr_inits2 (prec, m1, m2, NULL);
-         mpfr_from_real (m1, ra1, GMP_RNDN);
-         mpfr_from_real (m2, ra2, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m1, m1, m2, rnd);
-         result = do_mpfr_ckconv (m1, type, inexact);
-         mpfr_clears (m1, m2, NULL);
-       }
-    }
-
-  return result;
-}
-
-/* If argument ARG is a REAL_CST, call the three-argument mpfr function
-   FUNC on it and return the resulting value as a tree with type TYPE.
-   The mpfr precision is set to the precision of TYPE.  We assume that
-   function FUNC returns zero if the result could be calculated
-   exactly within the requested precision.  */
-
-static tree
-do_mpfr_arg3 (tree arg1, tree arg2, tree arg3, tree type,
-             int (*func)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t))
-{
-  tree result = NULL_TREE;
-
-  STRIP_NOPS (arg1);
-  STRIP_NOPS (arg2);
-  STRIP_NOPS (arg3);
-
-  /* To proceed, MPFR must exactly represent the target floating point
-     format, which only happens when the target base equals two.  */
-  if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2
-      && TREE_CODE (arg1) == REAL_CST && !TREE_OVERFLOW (arg1)
-      && TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2)
-      && TREE_CODE (arg3) == REAL_CST && !TREE_OVERFLOW (arg3))
-    {
-      const REAL_VALUE_TYPE *const ra1 = &TREE_REAL_CST (arg1);
-      const REAL_VALUE_TYPE *const ra2 = &TREE_REAL_CST (arg2);
-      const REAL_VALUE_TYPE *const ra3 = &TREE_REAL_CST (arg3);
-
-      if (real_isfinite (ra1) && real_isfinite (ra2) && real_isfinite (ra3))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m1, m2, m3;
-
-         mpfr_inits2 (prec, m1, m2, m3, NULL);
-         mpfr_from_real (m1, ra1, GMP_RNDN);
-         mpfr_from_real (m2, ra2, GMP_RNDN);
-         mpfr_from_real (m3, ra3, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m1, m1, m2, m3, rnd);
-         result = do_mpfr_ckconv (m1, type, inexact);
-         mpfr_clears (m1, m2, m3, NULL);
-       }
-    }
-
-  return result;
-}
-
-/* If argument ARG is a REAL_CST, call mpfr_sin_cos() on it and set
-   the pointers *(ARG_SINP) and *(ARG_COSP) to the resulting values.
-   If ARG_SINP and ARG_COSP are NULL then the result is returned
-   as a complex value.
-   The type is taken from the type of ARG and is used for setting the
-   precision of the calculation and results.  */
-
-static tree
-do_mpfr_sincos (tree arg, tree arg_sinp, tree arg_cosp)
-{
-  tree const type = TREE_TYPE (arg);
-  tree result = NULL_TREE;
-
-  STRIP_NOPS (arg);
-
-  /* To proceed, MPFR must exactly represent the target floating point
-     format, which only happens when the target base equals two.  */
-  if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2
-      && TREE_CODE (arg) == REAL_CST
-      && !TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg);
-
-      if (real_isfinite (ra))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         tree result_s, result_c;
-         int inexact;
-         mpfr_t m, ms, mc;
-
-         mpfr_inits2 (prec, m, ms, mc, NULL);
-         mpfr_from_real (m, ra, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = mpfr_sin_cos (ms, mc, m, rnd);
-         result_s = do_mpfr_ckconv (ms, type, inexact);
-         result_c = do_mpfr_ckconv (mc, type, inexact);
-         mpfr_clears (m, ms, mc, NULL);
-         if (result_s && result_c)
-           {
-             /* If we are to return in a complex value do so.  */
-             if (!arg_sinp && !arg_cosp)
-               return build_complex (build_complex_type (type),
-                                     result_c, result_s);
-
-             /* Dereference the sin/cos pointer arguments.  */
-             arg_sinp = build_fold_indirect_ref (arg_sinp);
-             arg_cosp = build_fold_indirect_ref (arg_cosp);
-             /* Proceed if valid pointer type were passed in.  */
-             if (TYPE_MAIN_VARIANT (TREE_TYPE (arg_sinp)) == TYPE_MAIN_VARIANT (type)
-                 && TYPE_MAIN_VARIANT (TREE_TYPE (arg_cosp)) == TYPE_MAIN_VARIANT (type))
-               {
-                 /* Set the values. */
-                 result_s = fold_build2 (MODIFY_EXPR, type, arg_sinp,
-                                         result_s);
-                 TREE_SIDE_EFFECTS (result_s) = 1;
-                 result_c = fold_build2 (MODIFY_EXPR, type, arg_cosp,
-                                         result_c);
-                 TREE_SIDE_EFFECTS (result_c) = 1;
-                 /* Combine the assignments into a compound expr.  */
-                 result = non_lvalue (fold_build2 (COMPOUND_EXPR, type,
-                                                   result_s, result_c));
-               }
-           }
-       }
-    }
-  return result;
-}
-
-/* If argument ARG1 is an INTEGER_CST and ARG2 is a REAL_CST, call the
-   two-argument mpfr order N Bessel function FUNC on them and return
-   the resulting value as a tree with type TYPE.  The mpfr precision
-   is set to the precision of TYPE.  We assume that function FUNC
-   returns zero if the result could be calculated exactly within the
-   requested precision.  */
-static tree
-do_mpfr_bessel_n (tree arg1, tree arg2, tree type,
-                 int (*func)(mpfr_ptr, long, mpfr_srcptr, mp_rnd_t),
-                 const REAL_VALUE_TYPE *min, bool inclusive)
-{
-  tree result = NULL_TREE;
-
-  STRIP_NOPS (arg1);
-  STRIP_NOPS (arg2);
-
-  /* To proceed, MPFR must exactly represent the target floating point
-     format, which only happens when the target base equals two.  */
-  if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2
-      && tree_fits_shwi_p (arg1)
-      && TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2))
-    {
-      const HOST_WIDE_INT n = tree_to_shwi (arg1);
-      const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg2);
-
-      if (n == (long)n
-         && real_isfinite (ra)
-         && (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min)))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m;
-
-         mpfr_init2 (m, prec);
-         mpfr_from_real (m, ra, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m, n, m, rnd);
-         result = do_mpfr_ckconv (m, type, inexact);
-         mpfr_clear (m);
-       }
-    }
-
-  return result;
-}
-
 /* If arguments ARG0 and ARG1 are REAL_CSTs, call mpfr_remquo() to set
    the pointer *(ARG_QUO) and return the result.  The type is taken
    from the type of ARG0 and is used for setting the precision of the
@@ -11944,51 +10118,6 @@ do_mpfr_lgamma_r (tree arg, tree arg_sg, tree type)
   return result;
 }
 
-/* If argument ARG is a COMPLEX_CST, call the one-argument mpc
-   function FUNC on it and return the resulting value as a tree with
-   type TYPE.  The mpfr precision is set to the precision of TYPE.  We
-   assume that function FUNC returns zero if the result could be
-   calculated exactly within the requested precision.  */
-
-static tree
-do_mpc_arg1 (tree arg, tree type, int (*func)(mpc_ptr, mpc_srcptr, mpc_rnd_t))
-{
-  tree result = NULL_TREE;
-
-  STRIP_NOPS (arg);
-
-  /* To proceed, MPFR must exactly represent the target floating point
-     format, which only happens when the target base equals two.  */
-  if (TREE_CODE (arg) == COMPLEX_CST && !TREE_OVERFLOW (arg)
-      && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == REAL_TYPE
-      && REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (TREE_TYPE (arg))))->b == 2)
-    {
-      const REAL_VALUE_TYPE *const re = TREE_REAL_CST_PTR (TREE_REALPART (arg));
-      const REAL_VALUE_TYPE *const im = TREE_REAL_CST_PTR (TREE_IMAGPART (arg));
-
-      if (real_isfinite (re) && real_isfinite (im))
-        {
-         const struct real_format *const fmt =
-           REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (type)));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero ? GMP_RNDZ : GMP_RNDN;
-         const mpc_rnd_t crnd = fmt->round_towards_zero ? MPC_RNDZZ : MPC_RNDNN;
-         int inexact;
-         mpc_t m;
-
-         mpc_init2 (m, prec);
-         mpfr_from_real (mpc_realref (m), re, rnd);
-         mpfr_from_real (mpc_imagref (m), im, rnd);
-         mpfr_clear_flags ();
-         inexact = func (m, m, crnd);
-         result = do_mpc_ckconv (m, type, inexact, /*force_convert=*/ 0);
-         mpc_clear (m);
-       }
-    }
-
-  return result;
-}
-
 /* If arguments ARG0 and ARG1 are a COMPLEX_CST, call the two-argument
    mpc function FUNC on it and return the resulting value as a tree
    with type TYPE.  The mpfr precision is set to the precision of
@@ -12107,42 +10236,19 @@ fold_call_stmt (gcall *stmt, bool ignore)
 void
 set_builtin_user_assembler_name (tree decl, const char *asmspec)
 {
-  tree builtin;
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL
              && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
              && asmspec != 0);
 
-  builtin = builtin_decl_explicit (DECL_FUNCTION_CODE (decl));
+  tree builtin = builtin_decl_explicit (DECL_FUNCTION_CODE (decl));
   set_user_assembler_name (builtin, asmspec);
-  switch (DECL_FUNCTION_CODE (decl))
+
+  if (DECL_FUNCTION_CODE (decl) == BUILT_IN_FFS
+      && INT_TYPE_SIZE < BITS_PER_WORD)
     {
-    case BUILT_IN_MEMCPY:
-      init_block_move_fn (asmspec);
-      memcpy_libfunc = set_user_assembler_libfunc ("memcpy", asmspec);
-      break;
-    case BUILT_IN_MEMSET:
-      init_block_clear_fn (asmspec);
-      memset_libfunc = set_user_assembler_libfunc ("memset", asmspec);
-      break;
-    case BUILT_IN_MEMMOVE:
-      memmove_libfunc = set_user_assembler_libfunc ("memmove", asmspec);
-      break;
-    case BUILT_IN_MEMCMP:
-      memcmp_libfunc = set_user_assembler_libfunc ("memcmp", asmspec);
-      break;
-    case BUILT_IN_ABORT:
-      abort_libfunc = set_user_assembler_libfunc ("abort", asmspec);
-      break;
-    case BUILT_IN_FFS:
-      if (INT_TYPE_SIZE < BITS_PER_WORD)
-       {
-         set_user_assembler_libfunc ("ffs", asmspec);
-         set_optab_libfunc (ffs_optab, mode_for_size (INT_TYPE_SIZE,
-                                                      MODE_INT, 0), "ffs");
-       }
-      break;
-    default:
-      break;
+      set_user_assembler_libfunc ("ffs", asmspec);
+      set_optab_libfunc (ffs_optab, mode_for_size (INT_TYPE_SIZE, MODE_INT, 0),
+                        "ffs");
     }
 }
 
@@ -12268,3 +10374,17 @@ is_inexpensive_builtin (tree decl)
 
   return false;
 }
+
+/* Return true if T is a constant and the value cast to a target char
+   can be represented by a host char.
+   Store the casted char constant in *P if so.  */
+
+bool
+target_char_cst_p (tree t, char *p)
+{
+  if (!tree_fits_uhwi_p (t) || CHAR_TYPE_SIZE != HOST_BITS_PER_CHAR)
+    return false;
+
+  *p = (char)tree_to_uhwi (t);
+  return true;
+}