OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / builtins.c
index fe0260f..a086a8c 100644 (file)
@@ -1,7 +1,7 @@
 /* Expand builtin functions.
    Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
 /* Expand builtin functions.
    Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
-   Free Software Foundation, Inc.
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+   2012 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 
 This file is part of GCC.
 
@@ -227,7 +227,7 @@ static void expand_builtin_sync_synchronize (void);
 
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
-bool
+static bool
 is_builtin_name (const char *name)
 {
   if (strncmp (name, "__builtin_", 10) == 0)
 is_builtin_name (const char *name)
 {
   if (strncmp (name, "__builtin_", 10) == 0)
@@ -452,37 +452,84 @@ get_object_alignment (tree exp)
   return align;
 }
 
   return align;
 }
 
-/* Return the alignment in bits of EXP, a pointer valued expression.
-   The alignment returned is, by default, the alignment of the thing that
-   EXP points to.  If it is not a POINTER_TYPE, 0 is returned.
+/* Return the alignment of object EXP, also considering its type when we do
+   not know of explicit misalignment.  Only handle MEM_REF and TARGET_MEM_REF.
 
 
-   Otherwise, look at the expression to see if we can do better, i.e., if the
-   expression is actually pointing at an object whose alignment is tighter.  */
+   ??? Note that, in the general case, the type of an expression is not kept
+   consistent with misalignment information by the front-end, for example when
+   taking the address of a member of a packed structure.  However, in most of
+   the cases, expressions have the alignment of their type so we optimistically
+   fall back to this alignment when we cannot compute a misalignment.  */
 
 unsigned int
 
 unsigned int
-get_pointer_alignment (tree exp)
+get_object_or_type_alignment (tree exp)
+{
+  unsigned HOST_WIDE_INT misalign;
+  unsigned int align = get_object_alignment_1 (exp, &misalign);
+
+  gcc_assert (TREE_CODE (exp) == MEM_REF || TREE_CODE (exp) == TARGET_MEM_REF);
+
+  if (misalign != 0)
+    align = (misalign & -misalign);
+  else
+    align = MAX (TYPE_ALIGN (TREE_TYPE (exp)), align);
+
+  return align;
+}
+
+/* For a pointer valued expression EXP compute values M and N such that
+   M divides (EXP - N) and such that N < M.  Store N in *BITPOSP and return M.
+
+   If EXP is not a pointer, 0 is returned.  */
+
+unsigned int
+get_pointer_alignment_1 (tree exp, unsigned HOST_WIDE_INT *bitposp)
 {
   STRIP_NOPS (exp);
 
   if (TREE_CODE (exp) == ADDR_EXPR)
 {
   STRIP_NOPS (exp);
 
   if (TREE_CODE (exp) == ADDR_EXPR)
-    return get_object_alignment (TREE_OPERAND (exp, 0));
+    return get_object_alignment_1 (TREE_OPERAND (exp, 0), bitposp);
   else if (TREE_CODE (exp) == SSA_NAME
           && POINTER_TYPE_P (TREE_TYPE (exp)))
     {
       struct ptr_info_def *pi = SSA_NAME_PTR_INFO (exp);
   else if (TREE_CODE (exp) == SSA_NAME
           && POINTER_TYPE_P (TREE_TYPE (exp)))
     {
       struct ptr_info_def *pi = SSA_NAME_PTR_INFO (exp);
-      unsigned align;
       if (!pi)
       if (!pi)
-       return BITS_PER_UNIT;
-      if (pi->misalign != 0)
-       align = (pi->misalign & -pi->misalign);
-      else
-       align = pi->align;
-      return align * BITS_PER_UNIT;
+       {
+         *bitposp = 0;
+         return BITS_PER_UNIT;
+       }
+      *bitposp = pi->misalign * BITS_PER_UNIT;
+      return pi->align * BITS_PER_UNIT;
     }
 
     }
 
+  *bitposp = 0;
   return POINTER_TYPE_P (TREE_TYPE (exp)) ? BITS_PER_UNIT : 0;
 }
 
   return POINTER_TYPE_P (TREE_TYPE (exp)) ? BITS_PER_UNIT : 0;
 }
 
+/* Return the alignment in bits of EXP, a pointer valued expression.
+   The alignment returned is, by default, the alignment of the thing that
+   EXP points to.  If it is not a POINTER_TYPE, 0 is returned.
+
+   Otherwise, look at the expression to see if we can do better, i.e., if the
+   expression is actually pointing at an object whose alignment is tighter.  */
+
+unsigned int
+get_pointer_alignment (tree exp)
+{
+  unsigned HOST_WIDE_INT bitpos = 0;
+  unsigned int align;
+  
+  align = get_pointer_alignment_1 (exp, &bitpos);
+
+  /* align and bitpos now specify known low bits of the pointer.
+     ptr & (align - 1) == bitpos.  */
+
+  if (bitpos != 0)
+    align = (bitpos & -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.
 /* 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.
@@ -2794,10 +2841,7 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target)
   tree fndecl = get_callee_fndecl (exp);
   tree arg;
   enum machine_mode mode;
   tree fndecl = get_callee_fndecl (exp);
   tree arg;
   enum machine_mode mode;
-
-  /* There's no easy way to detect the case we need to set EDOM.  */
-  if (flag_errno_math)
-    return NULL_RTX;
+  enum built_in_function fallback_fn = BUILT_IN_NONE;
 
   if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
      gcc_unreachable ();
 
   if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE))
      gcc_unreachable ();
@@ -2807,46 +2851,78 @@ expand_builtin_int_roundingfn_2 (tree exp, rtx target)
   switch (DECL_FUNCTION_CODE (fndecl))
     {
     CASE_FLT_FN (BUILT_IN_IRINT):
   switch (DECL_FUNCTION_CODE (fndecl))
     {
     CASE_FLT_FN (BUILT_IN_IRINT):
+      fallback_fn = BUILT_IN_LRINT;
+      /* FALLTHRU */
     CASE_FLT_FN (BUILT_IN_LRINT):
     CASE_FLT_FN (BUILT_IN_LLRINT):
     CASE_FLT_FN (BUILT_IN_LRINT):
     CASE_FLT_FN (BUILT_IN_LLRINT):
-      builtin_optab = lrint_optab; break;
+      builtin_optab = lrint_optab;
+      break;
 
     CASE_FLT_FN (BUILT_IN_IROUND):
 
     CASE_FLT_FN (BUILT_IN_IROUND):
+      fallback_fn = BUILT_IN_LROUND;
+      /* FALLTHRU */
     CASE_FLT_FN (BUILT_IN_LROUND):
     CASE_FLT_FN (BUILT_IN_LLROUND):
     CASE_FLT_FN (BUILT_IN_LROUND):
     CASE_FLT_FN (BUILT_IN_LLROUND):
-      builtin_optab = lround_optab; break;
+      builtin_optab = lround_optab;
+      break;
 
     default:
       gcc_unreachable ();
     }
 
 
     default:
       gcc_unreachable ();
     }
 
+  /* There's no easy way to detect the case we need to set EDOM.  */
+  if (flag_errno_math && fallback_fn == BUILT_IN_NONE)
+    return NULL_RTX;
+
   /* Make a suitable register to place result in.  */
   mode = TYPE_MODE (TREE_TYPE (exp));
 
   /* Make a suitable register to place result in.  */
   mode = TYPE_MODE (TREE_TYPE (exp));
 
-  target = gen_reg_rtx (mode);
+  /* There's no easy way to detect the case we need to set EDOM.  */
+  if (!flag_errno_math)
+    {
+      target = 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);
+      /* 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, NULL, VOIDmode, EXPAND_NORMAL);
+      op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL);
 
 
-  start_sequence ();
+      start_sequence ();
 
 
-  if (expand_sfix_optab (target, op0, builtin_optab))
-    {
-      /* Output the entire sequence.  */
-      insns = get_insns ();
+      if (expand_sfix_optab (target, op0, builtin_optab))
+       {
+         /* Output the entire sequence.  */
+         insns = get_insns ();
+         end_sequence ();
+         emit_insn (insns);
+         return target;
+       }
+
+      /* 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 ();
       end_sequence ();
-      emit_insn (insns);
-      return target;
     }
 
     }
 
-  /* 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 ();
+  if (fallback_fn != BUILT_IN_NONE)
+    {
+      /* Fall back to rounding to long int.  Use implicit_p 0 - for non-C99
+        targets, (int) round (x) should never be transformed into
+        BUILT_IN_IROUND and if __builtin_iround is called directly, emit
+        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);
+
+      exp = build_call_nofold_loc (EXPR_LOCATION (exp),
+                                  fallback_fndecl, 1, arg);
+
+      target = expand_call (exp, NULL_RTX, target == const0_rtx);
+      return convert_to_mode (mode, target, 0);
+    }
 
   target = expand_call (exp, target, target == const0_rtx);
 
 
   target = expand_call (exp, target, target == const0_rtx);
 
@@ -4523,8 +4599,8 @@ expand_builtin_alloca (tree exp, bool cannot_accumulate)
   bool alloca_with_align = (DECL_FUNCTION_CODE (get_callee_fndecl (exp))
                            == BUILT_IN_ALLOCA_WITH_ALIGN);
 
   bool alloca_with_align = (DECL_FUNCTION_CODE (get_callee_fndecl (exp))
                            == BUILT_IN_ALLOCA_WITH_ALIGN);
 
-  /* Emit normal call if marked not-inlineable.  */
-  if (CALL_CANNOT_INLINE_P (exp))
+  /* Emit normal call if we use mudflap.  */
+  if (flag_mudflap)
     return NULL_RTX;
 
   valid_arglist
     return NULL_RTX;
 
   valid_arglist
@@ -4807,7 +4883,7 @@ round_trampoline_addr (rtx tramp)
 }
 
 static rtx
 }
 
 static rtx
-expand_builtin_init_trampoline (tree exp)
+expand_builtin_init_trampoline (tree exp, bool onstack)
 {
   tree t_tramp, t_func, t_chain;
   rtx m_tramp, r_tramp, r_chain, tmp;
 {
   tree t_tramp, t_func, t_chain;
   rtx m_tramp, r_tramp, r_chain, tmp;
@@ -4824,13 +4900,16 @@ expand_builtin_init_trampoline (tree exp)
   m_tramp = gen_rtx_MEM (BLKmode, r_tramp);
   MEM_NOTRAP_P (m_tramp) = 1;
 
   m_tramp = gen_rtx_MEM (BLKmode, r_tramp);
   MEM_NOTRAP_P (m_tramp) = 1;
 
-  /* The TRAMP argument should be the address of a field within the
-     local function's FRAME decl.  Let's see if we can fill in the
-     to fill in the MEM_ATTRs for this memory.  */
+  /* If ONSTACK, the TRAMP argument should be the address of a field
+     within the local function's FRAME decl.  Either way, let's see if
+     we can fill in the MEM_ATTRs for this memory.  */
   if (TREE_CODE (t_tramp) == ADDR_EXPR)
     set_mem_attributes_minus_bitpos (m_tramp, TREE_OPERAND (t_tramp, 0),
                                     true, 0);
 
   if (TREE_CODE (t_tramp) == ADDR_EXPR)
     set_mem_attributes_minus_bitpos (m_tramp, TREE_OPERAND (t_tramp, 0),
                                     true, 0);
 
+  /* Creator of a heap trampoline is responsible for making sure the
+     address is aligned to at least STACK_BOUNDARY.  Normally malloc
+     will ensure this anyhow.  */
   tmp = round_trampoline_addr (r_tramp);
   if (tmp != r_tramp)
     {
   tmp = round_trampoline_addr (r_tramp);
   if (tmp != r_tramp)
     {
@@ -4850,10 +4929,13 @@ expand_builtin_init_trampoline (tree exp)
   /* Generate insns to initialize the trampoline.  */
   targetm.calls.trampoline_init (m_tramp, t_func, r_chain);
 
   /* Generate insns to initialize the trampoline.  */
   targetm.calls.trampoline_init (m_tramp, t_func, r_chain);
 
-  trampolines_created = 1;
+  if (onstack)
+    {
+      trampolines_created = 1;
 
 
-  warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
-              "trampoline generated for nested function %qD", t_func);
+      warning_at (DECL_SOURCE_LOCATION (t_func), OPT_Wtrampolines,
+                 "trampoline generated for nested function %qD", t_func);
+    }
 
   return const0_rtx;
 }
 
   return const0_rtx;
 }
@@ -5227,7 +5309,7 @@ expand_builtin_sync_lock_test_and_set (enum machine_mode mode, tree exp,
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
   val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
 
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
   val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
 
-  return expand_atomic_exchange (target, mem, val, MEMMODEL_ACQUIRE, true);
+  return expand_sync_lock_test_and_set (target, mem, val);
 }
 
 /* Expand the __sync_lock_release intrinsic.  EXP is the CALL_EXPR.  */
 }
 
 /* Expand the __sync_lock_release intrinsic.  EXP is the CALL_EXPR.  */
@@ -5291,7 +5373,7 @@ expand_builtin_atomic_exchange (enum machine_mode mode, tree exp, rtx target)
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
   val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
 
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
   val = expand_expr_force_mode (CALL_EXPR_ARG (exp, 1), mode);
 
-  return expand_atomic_exchange (target, mem, val, model, false);
+  return expand_atomic_exchange (target, mem, val, model);
 }
 
 /* Expand the __atomic_compare_exchange intrinsic:
 }
 
 /* Expand the __atomic_compare_exchange intrinsic:
@@ -5482,6 +5564,11 @@ expand_builtin_atomic_fetch_op (enum machine_mode mode, tree exp, rtx target,
 }
 
 
 }
 
 
+#ifndef HAVE_atomic_clear
+# define HAVE_atomic_clear 0
+# define gen_atomic_clear(x,y) (gcc_unreachable (), NULL_RTX)
+#endif
+
 /* Expand an atomic clear operation.
        void _atomic_clear (BOOL *obj, enum memmodel)
    EXP is the call expression.  */
 /* Expand an atomic clear operation.
        void _atomic_clear (BOOL *obj, enum memmodel)
    EXP is the call expression.  */
@@ -5503,6 +5590,12 @@ expand_builtin_atomic_clear (tree exp)
       return const0_rtx;
     }
 
       return const0_rtx;
     }
 
+  if (HAVE_atomic_clear)
+    {
+      emit_insn (gen_atomic_clear (mem, model));
+      return const0_rtx;
+    }
+
   /* Try issuing an __atomic_store, and allow fallback to __sync_lock_release.
      Failing that, a store is issued by __atomic_store.  The only way this can
      fail is if the bool type is larger than a word size.  Unlikely, but
   /* Try issuing an __atomic_store, and allow fallback to __sync_lock_release.
      Failing that, a store is issued by __atomic_store.  The only way this can
      fail is if the bool type is larger than a word size.  Unlikely, but
@@ -5519,9 +5612,9 @@ expand_builtin_atomic_clear (tree exp)
    EXP is the call expression.  */
 
 static rtx
    EXP is the call expression.  */
 
 static rtx
-expand_builtin_atomic_test_and_set (tree exp)
+expand_builtin_atomic_test_and_set (tree exp, rtx target)
 {
 {
-  rtx mem, ret;
+  rtx mem;
   enum memmodel model;
   enum machine_mode mode;
 
   enum memmodel model;
   enum machine_mode mode;
 
@@ -5529,20 +5622,7 @@ expand_builtin_atomic_test_and_set (tree exp)
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
   model = get_memmodel (CALL_EXPR_ARG (exp, 1));
 
   mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
   model = get_memmodel (CALL_EXPR_ARG (exp, 1));
 
-  /* Try issuing an exchange.  If it is lock free, or if there is a limited
-     functionality __sync_lock_test_and_set, this will utilize it.  */
-  ret = expand_atomic_exchange (NULL_RTX, mem, const1_rtx, model, true);
-  if (ret)
-    return ret;
-
-  /* Otherwise, there is no lock free support for test and set.  Simply
-     perform a load and a store.  Since this presumes a non-atomic architecture,
-     also assume single threadedness and don't issue barriers either. */
-
-  ret = gen_reg_rtx (mode);
-  emit_move_insn (ret, mem);
-  emit_move_insn (mem, const1_rtx);
-  return ret;
+  return expand_atomic_test_and_set (target, mem, model);
 }
 
 
 }
 
 
@@ -5588,15 +5668,15 @@ fold_builtin_atomic_always_lock_free (tree arg0, tree arg1)
   /* If the object has smaller alignment, the the lock free routines cannot
      be used.  */
   if (type_align < mode_align)
   /* If the object has smaller alignment, the the lock free routines cannot
      be used.  */
   if (type_align < mode_align)
-    return integer_zero_node;
+    return boolean_false_node;
 
   /* Check if a compare_and_swap pattern exists for the mode which represents
      the required size.  The pattern is not allowed to fail, so the existence
      of the pattern indicates support is present.  */
   if (can_compare_and_swap_p (mode, true))
 
   /* Check if a compare_and_swap pattern exists for the mode which represents
      the required size.  The pattern is not allowed to fail, so the existence
      of the pattern indicates support is present.  */
   if (can_compare_and_swap_p (mode, true))
-    return integer_one_node;
+    return boolean_true_node;
   else
   else
-    return integer_zero_node;
+    return boolean_false_node;
 }
 
 /* Return true if the parameters to call EXP represent an object which will
 }
 
 /* Return true if the parameters to call EXP represent an object which will
@@ -5620,7 +5700,7 @@ expand_builtin_atomic_always_lock_free (tree exp)
     }
 
   size = fold_builtin_atomic_always_lock_free (arg0, arg1);
     }
 
   size = fold_builtin_atomic_always_lock_free (arg0, arg1);
-  if (size == integer_one_node)
+  if (size == boolean_true_node)
     return const1_rtx;
   return const0_rtx;
 }
     return const1_rtx;
   return const0_rtx;
 }
@@ -5635,8 +5715,8 @@ fold_builtin_atomic_is_lock_free (tree arg0, tree arg1)
     return NULL_TREE;
   
   /* If it isn't always lock free, don't generate a result.  */
     return NULL_TREE;
   
   /* If it isn't always lock free, don't generate a result.  */
-  if (fold_builtin_atomic_always_lock_free (arg0, arg1) == integer_one_node)
-    return integer_one_node;
+  if (fold_builtin_atomic_always_lock_free (arg0, arg1) == boolean_true_node)
+    return boolean_true_node;
 
   return NULL_TREE;
 }
 
   return NULL_TREE;
 }
@@ -5666,7 +5746,7 @@ expand_builtin_atomic_is_lock_free (tree exp)
 
   /* If the value is known at compile time, return the RTX for it.  */
   size = fold_builtin_atomic_is_lock_free (arg0, arg1);
 
   /* If the value is known at compile time, return the RTX for it.  */
   size = fold_builtin_atomic_is_lock_free (arg0, arg1);
-  if (size == integer_one_node)
+  if (size == boolean_true_node)
     return const1_rtx;
 
   return NULL_RTX;
     return const1_rtx;
 
   return NULL_RTX;
@@ -5725,7 +5805,6 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
      set of builtins.  */
   if (!optimize
       && !called_as_built_in (fndecl)
      set of builtins.  */
   if (!optimize
       && !called_as_built_in (fndecl)
-      && DECL_ASSEMBLER_NAME_SET_P (fndecl)
       && fcode != BUILT_IN_ALLOCA
       && fcode != BUILT_IN_ALLOCA_WITH_ALIGN
       && fcode != BUILT_IN_FREE)
       && fcode != BUILT_IN_ALLOCA
       && fcode != BUILT_IN_ALLOCA_WITH_ALIGN
       && fcode != BUILT_IN_FREE)
@@ -6280,7 +6359,9 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       return const0_rtx;
 
     case BUILT_IN_INIT_TRAMPOLINE:
       return const0_rtx;
 
     case BUILT_IN_INIT_TRAMPOLINE:
-      return expand_builtin_init_trampoline (exp);
+      return expand_builtin_init_trampoline (exp, true);
+    case BUILT_IN_INIT_HEAP_TRAMPOLINE:
+      return expand_builtin_init_trampoline (exp, false);
     case BUILT_IN_ADJUST_TRAMPOLINE:
       return expand_builtin_adjust_trampoline (exp);
 
     case BUILT_IN_ADJUST_TRAMPOLINE:
       return expand_builtin_adjust_trampoline (exp);
 
@@ -6497,12 +6578,28 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
     case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4:
     case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8:
     case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_16:
     case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4:
     case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8:
     case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_16:
-      mode = 
-         get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1);
-      target = expand_builtin_atomic_compare_exchange (mode, exp, target);
-      if (target)
-       return target;
-      break;
+      {
+       unsigned int nargs, z;
+       VEC(tree,gc) *vec;
+
+       mode = 
+           get_builtin_sync_mode (fcode - BUILT_IN_ATOMIC_COMPARE_EXCHANGE_1);
+       target = expand_builtin_atomic_compare_exchange (mode, exp, target);
+       if (target)
+         return target;
+
+       /* If this is turned into an external library call, the weak parameter
+          must be dropped to match the expected parameter list.  */
+       nargs = call_expr_nargs (exp);
+       vec = VEC_alloc (tree, gc, nargs - 1);
+       for (z = 0; z < 3; z++)
+         VEC_quick_push (tree, vec, CALL_EXPR_ARG (exp, z));
+       /* Skip the boolean weak parameter.  */
+       for (z = 4; z < 6; z++)
+         VEC_quick_push (tree, vec, CALL_EXPR_ARG (exp, z));
+       exp = build_call_vec (TREE_TYPE (exp), CALL_EXPR_FN (exp), vec);
+       break;
+      }
 
     case BUILT_IN_ATOMIC_LOAD_1:
     case BUILT_IN_ATOMIC_LOAD_2:
 
     case BUILT_IN_ATOMIC_LOAD_1:
     case BUILT_IN_ATOMIC_LOAD_2:
@@ -6695,7 +6792,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
       break;
 
     case BUILT_IN_ATOMIC_TEST_AND_SET:
       break;
 
     case BUILT_IN_ATOMIC_TEST_AND_SET:
-      return expand_builtin_atomic_test_and_set (exp);
+      return expand_builtin_atomic_test_and_set (exp, target);
 
     case BUILT_IN_ATOMIC_CLEAR:
       return expand_builtin_atomic_clear (exp);
 
     case BUILT_IN_ATOMIC_CLEAR:
       return expand_builtin_atomic_clear (exp);
@@ -6732,6 +6829,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY_CHK:
     case BUILT_IN_STRNCPY_CHK:
+    case BUILT_IN_STPNCPY_CHK:
     case BUILT_IN_STRCAT_CHK:
     case BUILT_IN_STRNCAT_CHK:
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_STRCAT_CHK:
     case BUILT_IN_STRNCAT_CHK:
     case BUILT_IN_SNPRINTF_CHK:
@@ -10896,7 +10994,9 @@ fold_builtin_4 (location_t loc, tree fndecl,
                                      DECL_FUNCTION_CODE (fndecl));
 
     case BUILT_IN_STRNCPY_CHK:
                                      DECL_FUNCTION_CODE (fndecl));
 
     case BUILT_IN_STRNCPY_CHK:
-      return fold_builtin_strncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE);
+    case BUILT_IN_STPNCPY_CHK:
+      return fold_builtin_stxncpy_chk (loc, arg0, arg1, arg2, arg3, NULL_TREE,
+                                       ignore, fcode);
 
     case BUILT_IN_STRNCAT_CHK:
       return fold_builtin_strncat_chk (loc, fndecl, arg0, arg1, arg2, arg3);
 
     case BUILT_IN_STRNCAT_CHK:
       return fold_builtin_strncat_chk (loc, fndecl, arg0, arg1, arg2, arg3);
@@ -12459,6 +12559,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       break;
     case BUILT_IN_STRNCAT_CHK:
     case BUILT_IN_STRNCPY_CHK:
       break;
     case BUILT_IN_STRNCAT_CHK:
     case BUILT_IN_STRNCPY_CHK:
+    case BUILT_IN_STPNCPY_CHK:
       len = CALL_EXPR_ARG (exp, 2);
       size = CALL_EXPR_ARG (exp, 3);
       break;
       len = CALL_EXPR_ARG (exp, 2);
       size = CALL_EXPR_ARG (exp, 3);
       break;
@@ -12813,13 +12914,15 @@ fold_builtin_stxcpy_chk (location_t loc, tree fndecl, tree dest,
   return build_call_expr_loc (loc, fn, 2, dest, src);
 }
 
   return build_call_expr_loc (loc, fn, 2, dest, src);
 }
 
-/* Fold a call to the __strncpy_chk builtin.  DEST, SRC, LEN, and SIZE
+/* Fold a call to the __st{r,p}ncpy_chk builtin.  DEST, SRC, LEN, and SIZE
    are the arguments to the call.  If MAXLEN is not NULL, it is maximum
    are the arguments to the call.  If MAXLEN is not NULL, it is maximum
-   length passed as third argument.  */
+   length passed as third argument. IGNORE is true if return value can be
+   ignored. FCODE is the BUILT_IN_* code of the builtin. */
 
 tree
 
 tree
-fold_builtin_strncpy_chk (location_t loc, tree dest, tree src,
-                         tree len, tree size, tree maxlen)
+fold_builtin_stxncpy_chk (location_t loc, tree dest, tree src,
+                         tree len, tree size, tree maxlen, bool ignore,
+                         enum built_in_function fcode)
 {
   tree fn;
 
 {
   tree fn;
 
@@ -12829,6 +12932,15 @@ fold_builtin_strncpy_chk (location_t loc, tree dest, tree src,
       || !validate_arg (size, INTEGER_TYPE))
     return NULL_TREE;
 
       || !validate_arg (size, INTEGER_TYPE))
     return NULL_TREE;
 
+  if (fcode == BUILT_IN_STPNCPY_CHK && ignore)
+    {
+       /* If return value of __stpncpy_chk is ignored,
+          optimize into __strncpy_chk.  */
+       fn = builtin_decl_explicit (BUILT_IN_STRNCPY_CHK);
+       if (fn)
+         return build_call_expr_loc (loc, fn, 4, dest, src, len, size);
+    }
+
   if (! host_integerp (size, 1))
     return NULL_TREE;
 
   if (! host_integerp (size, 1))
     return NULL_TREE;
 
@@ -12849,8 +12961,9 @@ fold_builtin_strncpy_chk (location_t loc, tree dest, tree src,
        return NULL_TREE;
     }
 
        return NULL_TREE;
     }
 
-  /* If __builtin_strncpy_chk is used, assume strncpy is available.  */
-  fn = builtin_decl_explicit (BUILT_IN_STRNCPY);
+  /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available.  */
+  fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK
+                             ? BUILT_IN_STPNCPY : BUILT_IN_STRNCPY);
   if (!fn)
     return NULL_TREE;
 
   if (!fn)
     return NULL_TREE;